Guest |
Отправлено: 25.02.2005, 21:53 |
|
Не зарегистрирован
|
Проблема такая: работаю с железякой, сливаю с нее данные. Под данные в ОЗУ выделен (драйверами железки) буфер заданного мной размера. Пока заполняется одна половина, читаю и строю данные из другой, уже заполненной. При чтении первой половины буфера — никаких проблем, а вот со второй никак не могу справится. Все время вылетает EAccessViolation error at address.... При этом в файл данные пишутся без проблем, обе половины буфера. Умучался уже!
Строю так:
[CODE]
for (i=halfbuffer*flag; i<((halfbuffer*(flag+1))-(ChanNum-1));i+=ChanNum)
{
for (j=0; j
{
frm[j]->XYLine->QuickAddXY(x, (short)tmp[i+j]);
}
x++;
}
WORD *tmp — указатель на начало данных
flag=(0,1) — флаг готовности превой или второй половины буфера
[CODE]
Ошибка как раз в этой единственной строке...
Раньше подобное возникало если пытался читать записываемую половину буфера, но сейчас точно такого нет. Значение i, при котором выскакивает ошибка лежит как раз во второй половине массива, а в это время совершенно точно пишется первая — проверено. Причем, ошибка выскакивает примерно при одном и том же i — +_150...
Может я где-то ошибаюсь при работе с указателем?
Спасибо, что дочитали! |
|
Bond |
Отправлено: 28.02.2005, 11:23 |
|
Станционный диспетчер
Группа: Участник
Сообщений: 142
|
Трудно разобраться с кодом, в котором куча переменных и только одна описана
|
|
Konstantine |
Отправлено: 28.02.2005, 11:23 |
|
Мастер участка
Группа: Модератор
Сообщений: 545
|
во-первых — исправте второй [CОDE] на [/CОDE]
а во-вторых — как получаете указатель на буфер halfbuffer? и как рарешаете запись и чтение программе?
|
|
Bond |
Отправлено: 28.02.2005, 11:38 |
|
Станционный диспетчер
Группа: Участник
Сообщений: 142
|
Как ты обрабатываешь флаг?
|
|
Slader |
Отправлено: 28.02.2005, 16:30 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 27
|
Забыл в первый раз войти...
Так вот. Опишу технологию подробнее. Есть PCI АЦП, вместе с которым поставляются API-драйвера. Функции, входящие в этот набор, позволяют задавать параметры работы АЦП (частота дискретизации, число опрашиваемых каналов, размер кольцевого буфера в ОЗУ, размер FIFO-буфера в самом АЦП), запускать и останавливать сбор данных. Технология работы такая: АЦП собирает отсчеты сначала в свой кольцевой буфер, а затем выкидывает их в большой кольцевой. Процесс перекидывания из маленького в большой происходит внутри самого драйвера. Задача программиста — собрать данные из большого буфера. Для этих целей в драйвере существует переменная синхронизации — она увеличивается на величину малого буфера каждый раз при перекидывании данных. Изменяется она от 0 до величины, равной размеру большого буфера с шагом равным размеру FIFO АЦП. Совсем запутал наверно...
Теперь переменные:
FIFO — это половина кольцевого буфера внутри АЦП.
pages — число страниц в большом кольцевом буфере в ОЗУ. Одна страница равна FIFO.
halfbuffer=FIFO*pages/2 — половина большого кольцевого буфера
*sync — переменная синхронизации (бегает по кругу от 0 до halfbuffer*2 с шагом FIFO)
WORD *data — указатель на большой кольцевой буфер (определяется драйвером)
WORD *tmp — указатель либо на первую, либо на вторую половину буфера в зависимости от флага (см. ниже)
Теперь по программе:
CODE |
halfbuffer = FIFO*pages/2; // Собираем половинками большого кольцевого буфера
fl1 = fl2 = (*sync<=halfbuffer)? 0:1; // Настроили флаги (заполняется либо первая, либо вторая половина буфера)
while(fl2==fl1) fl2=(*sync<=halfbuffer)? 0:1; // Ждем завершения заполнения половинки буфера
tmp = data+halfbuffer*fl1; // Настраиваем указатель в кольцевом буфере
//Строим график
for (i=halfbuffer*fl1; i<halfbuffer*(fl1+1);i++)
{
XYLine->QuickAddXY(x, (short)tmp[i]);
}
fl1=(*sync<=halfbuffer)? 0:1; // Обновляем флаг
Все по новой — программа зациклена
|
Строит он намного быстрее, чем заполняется половина буфера, так что в чем проблема — я не знаю. |
|
Slader |
Отправлено: 28.02.2005, 16:34 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 27
|
Вернее, зациклена программа начиная с while:
[CODE]
while(!Terminated)
{
while(fl2==fl1) fl2=(*sync<=halfbuffer)? 0:1; // Ждем завершения заполнения половинки буфера
tmp = data+halfbuffer*fl1; // Настраиваем указатель в кольцевом буфере
//Строим график
for (i=halfbuffer*fl1; i
{
XYLine->QuickAddXY(x, (short)tmp[i]);
}
fl1=(*sync<=halfbuffer)? 0:1; // Обновляем флаг
Sleep(0);
} |
|
Slader |
Отправлено: 01.03.2005, 20:03 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 27
|
В общем, почему-то указатель вылетает далеко за пределы выделенного буфера. То есть, максимальный адрес, на который должен указывать указатель, равен
data+4*halfbuffer,
так как формат данных, на которые он указывает — WORD.
В момент ошибки индекс i в цикле построения почему-то равен 0 (хотя читается ВТОРАЯ половина буфера), а предел переменной (второе условие цикла) — не опрделен, во всяком случае так сказал WatchList. Ошибка выскакивает, когда указатель превышает верхнюю границу буфера на одно и то же число — 0х4000... Пробовал уменьшать на эту величину предел изменения переменной i — ноль эмоций, все равно Acess Violation...
В чем здесь может быть проблема? Просмотрел все объявления — все указатели имеют тип данных WORD... |
|
Георгий |
Отправлено: 01.03.2005, 22:11 |
|
Почетный железнодорожник
Группа: Модератор
Сообщений: 874
|
похоже где то промах по памяти — какой то указатель указывает непонятно куда, по нему производится запись, что приводит к затиранию облисти памяти, где лежали какие то переменные и, в свою очередь, использование этих забитых не понятно чем переменных в качестве индекса массива приводит в это раз уже к критическому промаху — за пределы памяти задачи.
я бы вывел на печать (в файл) значения всех подозреваемых переменных а потом посмотрел бы как они меняются в процессе работы — наверняка в какой то момент какой-нибудь указатель или индекс принимают неожиданное значение |
|
** Slader |
Отправлено: 07.03.2005, 23:47 |
|
Не зарегистрирован
|
Никак не пойму в чем дело. Один и тот же указатель в файл пишет нормальные данные, а строит какую-то белиберду, на данные совсем не похожую. Может я просто не умею работать с указателем?
Итак, если *tmp — указатель на WORD (причем он указывает на начало массива данных), то чтобы добраться до содержимого ячеек области памяти через указатель нужно делать так:
CODE |
int a=tmp[i];
или же
int a=*(tmp+i)
|
Я правильно понимаю?
Если я говорю
то я присваиваю переменной a АДРЕС указанной ячейки.
Как еще можно попробовать получить те даннные, которые надо — я не представляю... |
|
Георгий |
Отправлено: 08.03.2005, 00:35 |
|
Почетный железнодорожник
Группа: Модератор
Сообщений: 874
|
ты с WORD и int поосторожнее:
WORD по определению на всех машинах 2 байта
int = системно зависимая вешь и на 32х разрядных машинах она 4 байта.
точно нигде не делал предположения, что WORD = int ?
с данными работаешь в || потоках? синхронизацию чтения/записи общих переменных через семафоры делал?
пробовал закоментировать всё, что коментируется?
на крайний случай переменные которые || читаются/пишутся volatile объявил? а то вдруг в ходе оптимизаций что-то легло в регистры или вообще было сочтено константой
PS. кстати время уже позднее, а ты работаешь — отдыхать пробовал? а то вдруг банальный ляп не видишь..
Отредактировано Георгий — 08/03/2005, 00:36 |
|
** Slader |
Отправлено: 09.03.2005, 19:20 |
|
Не зарегистрирован
|
Спасибо, Георгий!
С Access Violation разобрался — действительно был глупейший ляп.
Однако данные все равно совершенно не те строятся...
Ты случаем не знаешь, как преобразовать WORD к 12-битному формату? А то данные с АЦП принимать нужно как 12 бит...
11 значащих + знаковый бит.
Может в этом проблема, хотя не должно быть...
В общем, я уже начинаю медленно звереть — вместо сигнала постоянного уровня строится сигнал в виде треугольников, причем всегда один и тот же! Одна и та же амплитуда и период 4 точки...
А если записывать в файл, а потом читать и строить — все ровно, все как надо... |
|
Rius |
Отправлено: 09.03.2005, 19:34 |
|
Мастер участка
Группа: Участник
Сообщений: 321
|
Попробуй к типу signed short int (16 битному)
CODE | #define fromadc -2000 // данные принятые с АЦП
int data = (fromadc << 4);
Caption = data / 16;//использование результата
|
Может перевести АЦП в униполярный режим? Тогда никакого знака не надо учитывать (всё> 0).
Треугольники смахивают на колебания младших разрядов АЦП, хотя такой плате это не положено. С другой стороны — всего 12 бит, а у нас АЦП в 24 бита.
Можешь привести весь текст программы (присоединённым к посту файлом)? Может смогу чем помочь.
|
|
** Slader |
Отправлено: 09.03.2005, 20:53 |
|
Не зарегистрирован
|
2 Rius:
Спасибо большое, но думаю что в моей программе ты просто потеряешься... Она сейчас достатолчно здоровая — представляет из себя небольшое приложение, так что в ней ногу сломаешь разбираться.
У меня беда с указателем. Никак не получается через него получить данные... Треугольники идут с размахом 500-4000 (вместо -2000...+2000), то есть чушь какая-то в принципе.
В униполярном режиме это АЦП работать не будет.
Читал я файл блоками по 2 байта — все прекрасно. Ради эксперимента прочитал куском в 16 байт через указатель — опять фигня строится, только уже не треугольники.
Поправьте, если не прав:
CODE |
WORD *data;
data = (WORD *)malloc(16);
ReadFile(hDatFile, data, 16, 0, 0);
for (int i=0; i<8; i++)
{
XYLine->AddXY(x++, (short)data[i]);
}
| |
|
Rius |
Отправлено: 09.03.2005, 21:21 |
|
Мастер участка
Группа: Участник
Сообщений: 321
|
Сколько КБ весят cpp файлы?
Я ещё не совсем въехал в задачу, поэтому навскидку могу предложить:
CODE | BYTE *data;
union _cdat{
BYTE Bytes[2];
short int Integer;
WORD Slovo;
} cdat;
data = new BYTE[16];
// ReadFile(hDatFile, data, 16, 0, 0);
for (int i=0; i<8; i++)
{
cdat.Bytes[0] = data[i*2];
cdat.Bytes[1] = data[i*2 + 1];
cdat.Slovo <<= 4;//= (cdat.Slovo << 4);
cdat.Integer /= 16;
// XYLine->AddXY(x++, cdat.Integer);
}
delete data; |
WORD не подходит для хранения знаковых чисел, так как по определению беззнаковый. Диапазон 500-4000 указывает как раз на это. Для достоверности посмотри на считанные ReadFile данные в отладчике в HEX виде.
Для выделения памяти в C++ используется new, а не malloc.
|
|
Slader |
Отправлено: 09.03.2005, 22:58 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 27
|
Спасибо за помощь.
Пробовал просто делать приведение к (signed short) — не вышло. Изменилась форма треугольников и их расположение на координатной сетке, но на данные с АЦП осталось все равно не похоже. Завтра попробую сделать через union. А может попытаться как-то поменять местами байты в слове?
|
|
Bond |
Отправлено: 10.03.2005, 10:33 |
|
Станционный диспетчер
Группа: Участник
Сообщений: 142
|
Может проблема все же в том, что данные 12-разрядные?
Попробуй так (в 12-разрядном дополнительном коде 0x0FFF = -1)
CODE | short tmp = 0x0FFF;
if( tmp & 0x0800 ) tmp |= 0xF000;
|
|
|
Bond |
Отправлено: 10.03.2005, 10:41 |
|
Станционный диспетчер
Группа: Участник
Сообщений: 142
|
Посмотри в режиме отладки в каком виде данные хранятся в буфере. Простое приведение типов здесь не поможет, потому что оно не оперирует 12-разрядными данными. Можешь попробовать еще юзать структуру типа этой:CODE | typedef struct
{
short val:12;
short sufix:4;
} val12_t;
short tmp = 0x0FFF;
val12 = *((val12_t*)&tmp);
|
|
|
** Slader |
Отправлено: 10.03.2005, 22:12 |
|
Не зарегистрирован
|
2 Rius:
С union тоже не заработало. Хотя вид данных опять изменился...
2 Bond:
Интересная мысль, но до конца не разобрался.
Что значит объявление?
CODE |
short val:12;
short sufix:4
|
Как я понял, число — это количество бит?
А как воспользоваться данными?
Простое преобразование типа val12_t -> double не проходит. Как посторить полученное занчение? Там требуется тип double. |
|
Rius |
Отправлено: 10.03.2005, 22:34 |
|
Мастер участка
Группа: Участник
Сообщений: 321
|
Несколько замечаний:
void __fastcall TMain::BOpenClick(TObject *Sender)
Возможно здесь перепутаны имена (глобальная переменная есть, но я точно не скажу что должно быть).
CODE | [b]OFileName[/b]=OFileName.SubString(1, i-1)+".par";
Blocks->Lines->Add([b]FileName[/b]); |
Это лучше делать в обработчике OnResize
CODE | void __fastcall TMain::FormPaint(TObject *Sender)
{
ProgressBar1->Width=StatusBar->Width-4;
ProgressBar1->Top=StatusBar->Top+2;
ProgressBar1->Left=StatusBar->Left+2;
} |
, или как у меня (еще лучше):
CODE | ProgrBar->Parent = StatusBar;
//ProgrBar->Align = alClient;
ProgrBar->Left = StatusBar->Panels->Items[0]->Width;
ProgrBar->Width = StatusBar->Width — ProgrBar->Left;
ProgrBar->Top = 0;
ProgrBar->Height = StatusBar->Height;
ProgrBar->Anchors = TAnchors() << akLeft << akTop << akRight << akBottom; |
Для массовой де/активации можно разместить группу контролов на одном оконном компоненте (TForm, TPanel, ...) и менять его свойство Enable.
Посмотри еще компонент TActionList, скорее всего пригодится (для упрощения управления менюшками).
Как я понял, данные после считывания с АЦП сразу скидываются в файл (void __fastcall Thrd::Write()), а также строится график.
Можешь дать такой файлик этак на 1000 чисел? Попробую сам график построить и исходные данные посмотреть. Формат — каждые два байта есть один код с АЦП?
QUOTE | Как я понял, число — это количество бит? | Правильно. Это структура с битовыми полями. Даёт возможность доступа сразу к нужным битам данных.
Отредактировано Rius — 11/03/2005, 00:35
|
|
Slader |
Отправлено: 11.03.2005, 15:16 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 27
|
Спасибо за замечания.
Вот файл на 100000 отсчетов (dat). Второй файл (par) — файл параметров для отбражения. Там записана одна структура. Если понадобится, приведу ее содержание.
Да, формат данных 2 байта — один отсчет.
QUOTE | Даёт возможность доступа сразу к нужным битам данных.
|
А как получить этот доступ-то? Я не понял...
Если идет объявление структуры
CODE |
typedef struct
{
short val:12;
short sufix:4;
} val12_t;
|
то к отдельной переменной структуры нужно обращаться как
CODE |
signed short int Num1, Num2;
Num1 = val12_t.val;
Num2 = val12_t.sufix;
|
или как-то по-другому?
|
|
Bond |
Отправлено: 11.03.2005, 15:36 |
|
Станционный диспетчер
Группа: Участник
Сообщений: 142
|
val12_t — это тип данных, размер которого 12+4=16 разрядов. можешь объявить указатель этого типа и натравить его на буфер:CODE | val12_t *val12Ptr = (val12_t*)dataBuffer;
for( int i = 0; i < BufferLength; i++ )
{
double dblValue = (double)val12Ptr[i].val12;
// trali-vale
...
} |
|
|
** Slader |
Отправлено: 11.03.2005, 19:21 |
|
Не зарегистрирован
|
Все равно нет...(((
Не знаю уже, что делать и где еще можно искать ошибку...
Пробовал переставить местами байты (через union) — тоже не то.
Если бы указатель указывал не на ту область памяти, наверняка вылезла бы ошибка, потому что буфер достаточно большой. Может, конечно, где-то глупость сделал, но где — не вижу. Все указатели вроде бы туда куда надо указывают, да и файл правильно пишется...
Читается с указателем только вот неправильно...
А какая разница — читать в двухбайтовую переменную или 16-байтный массив? По-моему особо никакой. Однако в первом случае все хорошо, во втором — ничего хорошего. Разве что при считывании в перемнную идет присвоение вида
CODE |
WORD y;
ReadFile(hDatFile, &y, sizeof(WORD), 0, 0);
short DataTemp=(short)y;
|
а при считывании в массив
CODE |
WORD *data;
data = new WORD[8];
ReadFile(hDatFile, data, 16, &read, 0);
short DataTemp=(short)data[j];
|
|
|
Rius |
Отправлено: 11.03.2005, 22:45 |
|
Мастер участка
Группа: Участник
Сообщений: 321
|
Slader
Вместо выделения имени файлаCODE | int i=OFileName.Pos(".dat");
OFileName=OFileName.SubString(1, i-1)+".par"; | можно использовать функцию ChangeFileExt
CODE | AnsiString filename = "0803_1041.par";
filename = ChangeFileExt(filename, ".dat");
if ((file=CreateFile(filename.c_str(), GENERIC_READ, 0, 0,
... |
Нужно узнать коэффициент усиления АЦП к этому файлу данных, и что такое деление на 2000:
CODE | frm[param.AdcChannelArray[j]]->XYLine->QuickAddXY(x, ((double)((short)data[i+n])*Gain[j]/2000)); |
а то у меня все считанные числа в диапазоне 0x232-0x247. По такому графику можно сказать, что АЦП плохое, либо на входе много помех.
Да, и какое там АЦП стоит? Сколько бит/отсчет (чтобы перевести в вольты).
Отредактировано Rius — 12/03/2005, 00:51
|
|
Slader |
Отправлено: 12.03.2005, 14:06 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 27
|
Спасибо за подсказку с расширением )
Что именно за АЦП (микросхема), я не знаю. Само устройство — PCI плата L783 российской фирмы L-Card, высокочастотное (до 3МГц) АЦП с цифровым сигнальным процессором ADSP2186.
Максимальное значение кода при включенной автоматической корректировке данных лежит в пределе -2000...+2000 (именно поэтому я и делю на 2000, производя таким образом нормировку к 1). В записанном файле коэффициент усиления 1. Это соответствует максимальному напряжению +-5В на входе (Gain[j] = 5).
Возможны также коэффициенты усиления 2, 4, 8. Им соответствует максимальное напряжение 2.5, 1.25, 0.625 на входе АЦП. Однако уже при усилении 2 сигнал приобретает шумоподобный характер. С чем это связано — не знаю. На сайте тех. поддержки мне так и не ответили на этот вопрос.
В файле записан постоянный сигнал с уровнем ~1.43В. Уровень прыгает из-за помех — я не применял никаких средств шумоподавления, — но помехи четко видны и носят импульсный характер. Скорее всего это наводка с цифровых линий материнской платы.
Спасибо за помощь! |
|
Rius |
Отправлено: 12.03.2005, 16:42 |
|
Мастер участка
Группа: Участник
Сообщений: 321
|
Вот вроде бы и все. Когда отладишь программу, перепиши её начисто, будет гораздо понятнее.
Данные в начале файла заменены на отрицательные 12-битные числа вручную.
|
|
Slader |
Отправлено: 12.03.2005, 20:55 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 27
|
Большое спасибо всем, кто помогал! Особенно Rius-у.
Вроде бы со всем разобрался. Остался только один непонятным момент.
Почему если строить график в потоке, параллельном ожиданию заполнения буфера (где происходит определение значения указателя) и запускаемом из этого потока ожидания (методом Resume()), строится чушь, а если строить в самом потоке ождидания — все нормально. Во всех потоках используется метод Synchronize. Глюки... |
|
Георгий |
Отправлено: 12.03.2005, 23:06 |
|
Почетный железнодорожник
Группа: Модератор
Сообщений: 874
|
QUOTE (Slader @ 12/03/2005, 20:55) | Почему если строить график в потоке, параллельном ожиданию заполнения буфера (где происходит определение значения указателя) и запускаемом из этого потока ожидания (методом Resume()), строится чушь, а если строить в самом потоке ождидания — все нормально. Во всех потоках используется метод Synchronize. Глюки... |
Хз, почему — по идее Synchronize как раз и приводит фактически к последовалельной работе — как только основной поток переходит в режим ожидания, так начинает работать метод вызванный через Synchronize и только после его завершения. основной поток снова начинает работать (обрабатывать сообщения).
если реактивность пользовательского интерфейса (задержка между действием оператора и его визуальным подтверждением, например между нажатием на левую кнопку мыши и визуальным вдавливанием кнопки) в пределах допустимого, то можно полностью отказаться от || потоков построения и чтения из устройства. в || оставить только запись на диск — она, как я понял, работает без сбоев. |
|
Slader |
Отправлено: 13.03.2005, 12:44 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 27
|
Спасибо, так и сделал. |
|
** Slader |
Отправлено: 13.03.2005, 18:43 |
|
Не зарегистрирован
|
В общем, фишка такая: если одновременно и строить и писать в файл (последовательность не важна) — все ОК. Если же только строить — фигня. Прям мистика какая-то... |
|
Rius |
Отправлено: 13.03.2005, 18:59 |
|
Мастер участка
Группа: Участник
Сообщений: 321
|
Это ещё ничего, вот у меня ответ от устройства приходил на 200 мс раньше, чем уходил запрос... Потоки вещь хитрая...
Ошибка вероятнее всего в них, где-то указатели перекрываются, где-то обращение к массиву их разных потоков, просто странный код, например
CODE | void __fastcall GraphData::Execute()
{
while (!Terminated)
{
Synchronize(Draw);
}
}
void __fastcall GraphData::Draw()
{
...
while(!this->Suspended) this->Suspend();
} |
вроде делается вот так:
CODE | void __fastcall GraphData::Execute()
{
while (!Terminated)
{
Synchronize(Draw);
Suspend();
}
}
void __fastcall GraphData::Draw()
{
...
} |
Продумай алгоритм заново, мне помогло.
Отредактировано Rius — 13/03/2005, 21:00
|
|