Российский производитель и разработчик сертифицированного измерительного оборудования с 1987 года


E14-140 работа с потоком (builder c+) том 3

Вы не вошли.

 Поиск | Регистрация | Вход 

МОТ
14.09.2011 10:37:32
#1

Гость

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 цели... Я бы таймеры использовал вообще,но если прогу свернуть например там хз че произойти может винда  просто тормознет этот компонент и результат просто исказится вычислений...
Ну серьезно ну че я уже не понимаю. отли я тупой совсем в системном программировании, то ли что-то ещё...

Почему у вас статеек нету по этому поводу(по приему данных с ацп)?

просто уже все сделал, только с потоками и приемом корректным данных с лкарда сижу...

15.09.2011 11:08:36
#2

Сотрудник "Л Кард"
Здесь с 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 smile
Но все равно он хотя бы удваивается... Я бы увеличил блок и оставил двойную буферизацию, но никто не мешает Вам сделать по-простому на свой страх и риск.
Кстати, у 140-M в последней версии прошивки есть счетчики переполнений буфера, их можно почитать для проверки.

МОТ
20.09.2011 14:19:43
#3

Гость

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

Запускаю программу, сразу вывожу данные. потом нажимаю СТОП. Потом нажимаю Старт и такое чувство что в контрольной таблице порядки путаются...

Как так вообще?

20.09.2011 14:57:01
#4

Сотрудник "Л Кард"
Здесь с 18.04.2014
Сообщений: 810

Re: E14-140 работа с потоком (builder c+) том 3

Я не понимаю, что происходит с этой программой за моей спиной smile Почему вызов 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 не выходил совсем, а ждал новой команды пуска или что-то вроде. Но это на будущее wink

Грустно, что мы все время ходим кругами по уже обсужденным вопросам. Мои ответы длинные и нудные, это правда, но поверьте - в них нет ни одного лишнего слова smile

МОТ
20.09.2011 15:42:18
#5

Гость

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();
}
//-- -------------------------------------------------------------------------

20.09.2011 19:17:19
#6

Сотрудник "Л Кард"
Здесь с 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. Да, и на вопрос <<что косячно>> без описания аномалий мне трудно ответить, поскольку я не запускаю этот код, а читаю глазами. Мне желательно услышать, ЧТО косячно, и я попробую сказать, ПОЧЕМУ косячно smile

MOT
20.09.2011 19:57:45
#7

Гость

Re: E14-140 работа с потоком (builder c+) том 3

Александр Е, внерабочее время отвечаем? ))) ясно.
Завтра проверю... но все равно чувствует мое сердце касяк с функция лкарда(то есть я чето не обнуляю может быть)... Касяк тот же... Скрины я выше скидывал.

МОТ
21.09.2011 08:26:19
#8

Гость

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);

МОТ
21.09.2011 08:36:00
#9

Гость

Re: E14-140 работа с потоком (builder c+) том 3

ну вот переделал... идеальный вариант уже.
http://narod.ru/disk/25828071001/THREAD.zip.html
А касяк такой же
http://narod.ru/disk/25741290001/%D0%9A … !.doc.html

говорю же что проблема не в потоке, а в лкарде... Скорее всего что-то не отрубается или не обнуляется.

21.09.2011 11:00:12
#10

Сотрудник "Л Кард"
Здесь с 18.04.2014
Сообщений: 810

Re: E14-140 работа с потоком (builder c+) том 3

Для идеального можно еще сделать освобождение m и hStopEvent при выходе из createThread по ошибке, а также убрать ненужные Terminate; и if (Terminated) return; Но это так, мелочи для порядка.
За исключением этого, если я чего-то не пропустил, как будто бы все на месте.

По существу - руководство по lusbapi советует перед START_ADC() ставить дополнительный STOP_ADC(). В нем подается команда очистки буферов низкого уровня.
Попробуйте.

МОТ
21.09.2011 11:22:38
#11

Гость

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

Устройство работает нормально.

При наличии неполадок в работе устройства нажмите кнопку "Диагностика", чтобы запустить мастер диагностики.

МОТ
21.09.2011 11:34:04
#12

Гость

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 Кгц... Не должно быть глюков?

21.09.2011 12:15:05
#13

Сотрудник "Л Кард"
Здесь с 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);

МОТ
21.09.2011 15:13:49
#14

Гость

Re: E14-140 работа с потоком (builder c+) том 3

хммм. вроде проблема была в STOP... пока нормально работает... блин столько дум уже передумал

MOT
24.09.2011 17:24:00
#15

Гость

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 например или ещё какие-нибудь может проблемы породит, которые трудно будет отследить?

03.10.2011 12:45:03
#16

Сотрудник "Л Кард"
Здесь с 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