|
|
E14-140 работа с потоком (builder c+) том 3
хочу продолжить эту тему:
http://lcard.ru/forums/1?action=viewthread&thread=10349
Короче вообще непонятно как работает поток...
Вот считываю. с 14 каналов и вывожу...
http://narod.ru/disk/25081774001/THREAD.zip.html
А также посмотрите фаил
http://narod.ru/disk/25081881001/scre.doc.html
там скрины...
Значит вывожу всего 9 каналов. Сам вывод вот такой:
void __fastcall TGetDataThread::pb() {
Form1->Chart1->Series[0]->Clear();
Form1->Chart2->Series[0]->Clear();
Form1->Chart3->Series[0]->Clear();
Form1->Chart4->Series[0]->Clear();
Form1->Chart5->Series[0]->Clear();
Form1->Chart6->Series[0]->Clear();
Form1->Chart7->Series[0]->Clear();
Form1->Chart8->Series[0]->Clear();
for (int i = 0; i < sizeof(ADC_Buf[0]) / sizeof(ADC_Buf[0][0]); i+=NumCan) {
Form1->Chart1->Series[0]->AddY((double)ADC_Buf[0][i]);
Form1->Chart2->Series[0]->AddY((double)ADC_Buf[0][i+7]);
Form1->Chart3->Series[0]->AddY((double)ADC_Buf[0][i+9]);
Form1->Chart4->Series[0]->AddY((double)ADC_Buf[0][i+11]);
Form1->Chart5->Series[0]->AddY((double)ADC_Buf[0][i+2]);
Form1->Chart6->Series[0]->AddY((double)ADC_Buf[0][i+10]);
Form1->Chart7->Series[0]->AddY((double)ADC_Buf[0][i+9]);
Form1->Chart8->Series[0]->AddY((double)ADC_Buf[0][i+9]);
}
}
я закомментил все AddY... и потом последовательно их расскоменчивал. Посмотрите *.doc фаил там скрины есть и как я последовательно все делаю.
Вообще непонятно че происходит такое...
Ребят, может без извращений всяких есть просто способ сбора данных с вашего АЦП ? Есть примеры может совсем простые? Давайте не будем извращаться со всякими событиями. которые может дают такой результат, а сделаем самым простым вариантом...
Мне вообще потоки нужны только для 1 цели... Я бы таймеры использовал вообще,но если прогу свернуть например там хз че произойти может винда просто тормознет этот компонент и результат просто исказится вычислений...
Ну серьезно ну че я уже не понимаю. отли я тупой совсем в системном программировании, то ли что-то ещё...
Почему у вас статеек нету по этому поводу(по приему данных с ацп)?
просто уже все сделал, только с потоками и приемом корректным данных с лкарда сижу...
|
|
- Сотрудник "Л Кард"
- Здесь с 18.04.2014
- Сообщений: 810
|
Re: E14-140 работа с потоком (builder c+) том 3
1. В программе частота дискретизации 150 кГц, в powergraph на скриншотах - видимо, 1 кГц и еще межкадровая задержка (если я правильно посчитал, то частота кадров около 10 кГц).
Какая частота физически на входах?
2. Очевидно, в TGetDataThread::pb() надо обрабатывать не ADC_Buf[0], а ADC_Buf[idx] (передайте его параметром - TGetDataThread::pb(int idx)
Я думаю, что основная ошибка в этом.
3.
>Давайте не будем извращаться со всякими событиями.
На низких частотах дискретизации в принципе можно пытаться читать, как говорится, тупо:
start - ( read data - process data ) x N - stop
Но это нехорошо (см. http://www.lcard.ru/forumthreads/9977 о черпании двумя ведрами) - пока данные обрабатываются (а сколько времени займет черчение графиков на VCL компоненте - это науке не известно), запущенный поток сбора данных не будет читаться и может произойти переполнение буфера АЦП с потерей куска данных. При гармоническом сигнале Вы это увидите на графике в виде скачков фазы.
Буфер в устройстве маленький, 28 КБайт (порядка 100 мс на 150 кГц). С другой стороны, в Вашей программе и приемный буфер такой же - 2048*14
Но все равно он хотя бы удваивается... Я бы увеличил блок и оставил двойную буферизацию, но никто не мешает Вам сделать по-простому на свой страх и риск.
Кстати, у 140-M в последней версии прошивки есть счетчики переполнений буфера, их можно почитать для проверки.
|
|
|
Re: E14-140 работа с потоком (builder c+) том 3
вот интересная вещь происходит, вернее один касячище... Который я победить не могу... Может вообще проблема не с потоком??? Такое чувство что с ControltTable чето происходит. после отключения потока.
Вот сделал как вы сказали, но idx сделал глоабльным. То есть его не передавал параметром.
http://narod.ru/disk/25741098001/THREAD.zip.html
А вот Скрины работы программы:
http://narod.ru/disk/25741290001/%D0%9A … !.doc.html
Запускаю программу, сразу вывожу данные. потом нажимаю СТОП. Потом нажимаю Старт и такое чувство что в контрольной таблице порядки путаются...
Как так вообще?
|
|
- Сотрудник "Л Кард"
- Здесь с 18.04.2014
- Сообщений: 810
|
Re: E14-140 работа с потоком (builder c+) том 3
Я не понимаю, что происходит с этой программой за моей спиной Почему вызов Synchronize(&pb) переехал с правильного места на неправильное?
По существу: категорически нельзя приостанавливать поток чтения через Suspend. Я это уже говорил в предыдущей ветке (см. 18.07.11 12:30). Вы просто глушите процесс чтения данных в компьютер (причем в неизвестной точке), а АЦП не останавливается. Отсюда сбой в порядке принятых данных, а control table ни при чем. Просто Вы искусственно создали потерю произвольного количества блоков по 64 байта данных, что не кратно кадру, в результате чего каналы съезжают.
Корректно остановить поток можно только через SetEvent(hStopEvent) с ожиданием остановки потока.
Соответственно, для многкратных пусков/стопов надо ЛИБО поток создавать каждый раз заново (переменная thr инициализируется NULL, вместо thr->Resume() thr = new..., в процедуре останова после WaitFor() delete thr; thr = NULL (см. там же - 18.07.11 12:30)), ЛИБО усложнить код потока, чтобы он по hStopEvent не выходил совсем, а ждал новой команды пуска или что-то вроде. Но это на будущее
Грустно, что мы все время ходим кругами по уже обсужденным вопросам. Мои ответы длинные и нудные, это правда, но поверьте - в них нет ни одного лишнего слова 
|
|
|
Re: E14-140 работа с потоком (builder c+) том 3
ну вот таким образом я каждый раз TGetDataThread *thr удаляю и создаю заново... По логике теперь он должен нормально выводится тогда:
тут то что касячно? Суспенды убрал все. Резьюм один оставил в createThread ,так как без него поток просто спит...
delete thr; и thr =NULL равнозначные операции насколько я знаю...
//-- -------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "main.h"
#include "thread.h"
#include "lusbapi.h"
//-- -------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "Chart"
#pragma link "Series"
#pragma link "TeEngine"
#pragma link "TeeProcs"
#pragma resource "*.dfm"
TForm1 *Form1;
const int NumCan=5; //количество каналов
const int BLOCK_SIZE=4096 * NumCan;
HANDLE thread;
DWORD thread_id;
HANDLE hStopEvent; /* Инициализируется в основной программе до потока */
SHORT ADC_Buf[2][BLOCK_SIZE];
ILE140 *m;
ADC_PARS_E140 adc_pars;
MODULE_DESCRIPTION_E140 descr;
SHORT buf[BLOCK_SIZE];
// структура с параметрами запроса на ввод данных
IO_REQUEST_LUSBAPI IoReq;
double ADC_Rate; //Частота опроса канала
int KDecC=64; // Коэффициент децимации
double FC,TC; //Частота и период поступления данных с канала С
HANDLE hDevice;
LPVOID lpBuffer;
DWORD nNumberOfBytesToRead;
LPDWORD lpNumberOfBytesRead;
LPOVERLAPPED lpOverlapped;
TGetDataThread *thr;
//-- -------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
void createThread(){
thr= new TGetDataThread(true);
thr->Priority= tpLower;
// создаем экземпляр интерфейс
m=static_cast<ILE140*>(CreateLInstance("e140"));
hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if(m == NULL);
else{
int i;
for(i=0; i<127; i++) if(m->OpenLDevice(i)) break; // связываем первый попавшийся модуль Е140 с созданным интерфейсом
if(i<127)
{
// попробуем прочитать дескриптор устройства
hDevice = m->GetModuleHandle();
if (hDevice == INVALID_HANDLE_VALUE) {
Application->MessageBox(L"Не могу получить дескриптор устройства!", L"ОШИБКА!!!", MB_OK + MB_ICONINFORMATION);
return;
}
// считываем текущие настройки модуля
if (m->GET_MODULE_DESCRIPTION(&descr))
if (m->GET_ADC_PARS(&adc_pars)){
// ---- задаем параметры работы АЦП
adc_pars.ClkSource=0; //режимов аппаратной синхронизации запуска АЦП = внутренний
adc_pars.EnableClkOutput=0; // трансляции тактовых импульсов запуска АЦП = запретить
adc_pars.InputMode=0; // режим программной синхронизации ввода данных с АЦП = нет
adc_pars.ChannelsQuantity=NumCan; // число активных логических каналов = 4
// управляющая таблица логических каналов
adc_pars.ControlTable[0]=(0)|(false << 0x5)|(0x0);
adc_pars.ControlTable[1]=(9)|(false << 0x5)|(0x0);
adc_pars.ControlTable[2]=(2)|(false << 0x5)|(0x0);
adc_pars.ControlTable[3]=(1)|(false << 0x5)|(0x0);
adc_pars.ControlTable[4]=(5)|(false << 0x5)|(0x0);
adc_pars.AdcRate=150.0; // частоту дискретизации АЦП модуля = 100 кГц
adc_pars.InterKadrDelay=2.0/adc_pars.AdcRate; // межкадровой задержки = 2 периода дискретизации
if(m->SET_ADC_PARS(&adc_pars))
{
m->GET_ADC_PARS(&adc_pars);
ADC_Rate=adc_pars.KadrRate;
FC=ADC_Rate/KDecC; FC=0.0781738586616635;
TC=1/FC;
// создаем поток сбора данных АЦП
thr->Resume();
}
}
}
}
}
//-- -------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
//завершение работы программы
SetEvent(hStopEvent);
thr->WaitFor();
delete thr;
m->ReleaseLInstance();
CloseHandle(hStopEvent);
}
//-- -------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
//завершение работы программы
SetEvent(hStopEvent);
thr->WaitFor();
delete thr;
m->ReleaseLInstance();
CloseHandle(hStopEvent);
}
//-- -------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
//запускаем поток
createThread();
}
//-- -------------------------------------------------------------------------
|
|
- Сотрудник "Л Кард"
- Здесь с 18.04.2014
- Сообщений: 810
|
Re: E14-140 работа с потоком (builder c+) том 3
при объявлении переменную инициализировать TGetDataThread *thr = NULL;
>Резьюм один оставил в createThread ,так как без него поток просто спит...
Тако ж можно в конструктор не передавать suspend = true. Только тогда new обязательно не вверху, а вместо resume. Это также лучше с точки зрения того, что при ошибке (если вышли выше по return) thr не будет создан и останется null.
Кстати, при входе в createThread надо проверить
if (thr) return; //ошибка - поток уже создан
при остановке - аналогично
if (!thr) ничего_не_делать
а после delete обязательно thr = NULL;
По мелочи надо добавить проверки, чтобы при выходе из createThread по ошибке до создания потока освобождались ресурсы (удалялся hStopEvent, если он создан; делался ReleaseLInstance, если был сделан CreateLInstance и т.д.)
Тогда получается, что при выходе из createThread:
thr!=NULL => поток создан, модуль открыт, hStopEvent создан
thr==NULL => поток не создан, модуль не открыт, hStopEvent не создан или уже убит
>delete thr; и thr=NULL равнозначные операции насколько я знаю...
Упаси боже! http://ru.wikipedia.org/wiki/Delete_%28C%2B%2B%29
Кстати, зачем пониженный приоритет потоку чтения? По-моему, лучше как минимум обычный.
P.S. Да, и на вопрос <<что косячно>> без описания аномалий мне трудно ответить, поскольку я не запускаю этот код, а читаю глазами. Мне желательно услышать, ЧТО косячно, и я попробую сказать, ПОЧЕМУ косячно 
|
|
|
Re: E14-140 работа с потоком (builder c+) том 3
Александр Е, внерабочее время отвечаем? ))) ясно.
Завтра проверю... но все равно чувствует мое сердце касяк с функция лкарда(то есть я чето не обнуляю может быть)... Касяк тот же... Скрины я выше скидывал.
|
|
|
Re: E14-140 работа с потоком (builder c+) том 3
>при остановке - аналогично
>if (!thr) ничего_не_делать
как так? может все таки if (!thr) return; ???
вот так:
if (!thr) return;
//завершение работы программы
SetEvent(hStopEvent);
thr->WaitFor();
thr->Terminate();
delete thr;
thr = NULL;
m->ReleaseLInstance();
CloseHandle(hStopEvent);
|
|
|
Re: E14-140 работа с потоком (builder c+) том 3
ну вот переделал... идеальный вариант уже.
http://narod.ru/disk/25828071001/THREAD.zip.html
А касяк такой же
http://narod.ru/disk/25741290001/%D0%9A … !.doc.html
говорю же что проблема не в потоке, а в лкарде... Скорее всего что-то не отрубается или не обнуляется.
|
|
- Сотрудник "Л Кард"
- Здесь с 18.04.2014
- Сообщений: 810
|
Re: E14-140 работа с потоком (builder c+) том 3
Для идеального можно еще сделать освобождение m и hStopEvent при выходе из createThread по ошибке, а также убрать ненужные Terminate; и if (Terminated) return; Но это так, мелочи для порядка.
За исключением этого, если я чего-то не пропустил, как будто бы все на месте.
По существу - руководство по lusbapi советует перед START_ADC() ставить дополнительный STOP_ADC(). В нем подается команда очистки буферов низкого уровня.
Попробуйте.
|
|
|
Re: E14-140 работа с потоком (builder c+) том 3
ничего не изменилось. Сделал так:
void __fastcall TGetDataThread::Execute() {
try {
FreeOnTerminate = false;
HANDLE hEvent[2];
OVERLAPPED ov[2];
DWORD TransferSize;
HANDLE WaitList[2];
hEvent[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
hEvent[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
/* Ставим в очередь первый запрос чтения данных */
ZeroMemory(&ov[0], sizeof(OVERLAPPED));
ov[0].hEvent = hEvent[0];
if (!ReadFile(hDevice, ADC_Buf[0], sizeof(ADC_Buf[0]), NULL,&ov[0]) && (GetLastError() != ERROR_IO_PENDING)) {
/* Ошибка */
return;
}
idx = 1;
m->STOP_ADC();
m->START_ADC();
for (; ; ) {
/* Ставим в очередь следующий буфер */
ZeroMemory(&ov[idx], sizeof(OVERLAPPED));
ov[idx].hEvent = hEvent[idx];
if (!ReadFile(hDevice, ADC_Buf[idx], sizeof(ADC_Buf[idx]), NULL,
&ov[idx]) && (GetLastError() != ERROR_IO_PENDING)) {
/* Ошибка */
break;
}
idx ^= 1;
/* Ждем окончания предыдущего чтения или прихода команды stop */
WaitList[0] = hStopEvent;
WaitList[1] = hEvent[idx];
if (WaitForMultipleObjects(2, WaitList, FALSE,INFINITE) != WAIT_OBJECT_0 + 1) {
/* Пришла команда stop */
break;
}
if (!GetOverlappedResult(hDevice, &ov[idx], &TransferSize, TRUE)) {
/* Ошибка */
break;
}
// if (Terminated) return;
Synchronize(&pb);
Synchronize(&capPlus);
} /* for (;;) */
m->STOP_ADC();
CancelIo(hDevice);
GetOverlappedResult(hDevice, &ov[idx], &TransferSize, TRUE);
GetOverlappedResult(hDevice, &ov[idx ^ 1], &TransferSize, TRUE);
CloseHandle(hEvent[1]);
CloseHandle(hEvent[0]);
if (Terminated)
return;
}
catch(...) {
}
}
а может быть проблема в драйвере? я вроде новый ставил 6.0.1.0
Устройство работает нормально.
При наличии неполадок в работе устройства нажмите кнопку "Диагностика", чтобы запустить мастер диагностики.
|
|
|
Re: E14-140 работа с потоком (builder c+) том 3
adc_pars.AdcRate=150.0; // частоту дискретизации АЦП модуля = 100 кГц
adc_pars.InterKadrDelay=2.0/adc_pars.AdcRate; // межкадровой задержки = 2 периода дискретизации
if(m->SET_ADC_PARS(&adc_pars))
{
m->GET_ADC_PARS(&adc_pars);
ADC_Rate=adc_pars.KadrRate;
FC=ADC_Rate/KDecC; FC=0.0781738586616635;
TC=1/FC;
// создаем поток сбора данных АЦП
thr= new TGetDataThread(false);
thr->Priority= tpNormal;
}
вот эти параметры не могут оказать влияния? Например если АЦП старый и он только на 100 Кгц... Не должно быть глюков?
|
|
- Сотрудник "Л Кард"
- Здесь с 18.04.2014
- Сообщений: 810
|
Re: E14-140 работа с потоком (builder c+) том 3
У E14-140-M АЦП поддерживает 200 кГц, у E14-140 - 100 кГц, но это вообще разные приборы (у них совершенно различные микроконтроллеры)...
То, что Вы описываете, по идее указывает на следующий эффект: при повторном пуске программа получает хвост данных от предыдущего сбора
(mb|Mary had a little lamb|Mary had a little lamb|...). Если этот хвост содержит нецелое число кадров, то очевидно, что произойдет циклическая перестановка каналов.
Добиться такого эффекта можно на разных уровнях - от прикладной программы до прошивки модуля. На низком уровне - это целая история в связи с тем, что данные по USB передаются пакетами по 64 байта, а сбор данных асинхронен по отношению к командам. Есть технологический софт, который это тестирует, вроде бы с ним все хорошо.
...А погодите-погодите, я плохо посоветовал. В этой программе дополнительный STOP надо ставить перед ReadFile. Например, после hEvent[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
Re: E14-140 работа с потоком (builder c+) том 3
хммм. вроде проблема была в STOP... пока нормально работает... блин столько дум уже передумал
|
|
|
Re: E14-140 работа с потоком (builder c+) том 3
А вот если я хочу сделать все таки не статический массив приема данных , а динамический. Так как количество каналов и количество точек приема задается у меня в фаиле и на момент инициализации программы они определяются.
Вот этот кусок можно например заменить
const int NumCan=5; //количество каналов
const int BLOCK_SIZE=4096 * NumCan;
SHORT ADC_Buf[2][BLOCK_SIZE];
на
int NumCan=5; //количество каналов
int BLOCK_SIZE=4096 * NumCan;
SHORT* ADC_Buf = new SHORT[2][BLOCK_SIZE];
....
delete []d;
Это повлечет какие то проблемы с
ReadFile например или ещё какие-нибудь может проблемы породит, которые трудно будет отследить?
|
|
- Сотрудник "Л Кард"
- Здесь с 18.04.2014
- Сообщений: 810
|
Re: E14-140 работа с потоком (builder c+) том 3
Динамический массив ничем не отличается, если логика программы гарантирует отсутствие обращения по этому указателю до выделения и после освобождения памяти. То есть логика та же, что с CreateLInstance/ReleaseLInstance, CreateEvent/CloseHandle.
Также надо исключить попытки создать уже созданный массив или удалить уже удаленный.
Операция выделения динамической памяти имеет право завершиться неуспешно. В C++ оператор new при ошибке может вызывать exception или (в зависимости от компилятора и настроек - например, в MSVC) возвращать NULL, как в C.
http://www.rsdn.ru/forum/cpp/1646298.all.aspx
Если будет exception и нет catch, то программа некрасиво вывалится. Если может вернуться NULL, то совершенно необходимо проверять указатель руками: if (!ADC_Buf) { ошибка и обращаться к массиву нельзя! }
Оператор delete НЕ присваивает указателю NULL, поэтоуму рекомендую пустые указатели делать NULL принудительно.
Вызов delete с аргументом, равным NULL, безопасен и игнорируется.
Прочитайте обязательно:
http://ru.wikipedia.org/wiki/New_%28C%2B%2B%29
http://ru.wikipedia.org/wiki/Delete_%28C%2B%2B%29
http://ru.wikipedia.org/wiki/NULL_%28%D0%A1%D0%B8%29
|