C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

стр.: (2) < [1] 2 >
Access violation, при работе с укзателем на данные
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)


Я правильно понимаю?
Если я говорю
CODE

int a = &tmp[i]

то я присваиваю переменной a АДРЕС указанной ячейки.
Как еще можно попробовать получить те даннные, которые надо — я не представляю...
Георгий
Отправлено: 08.03.2005, 00:35


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



ты с WORD и int поосторожнее:
WORD по определению на всех машинах 2 байта
int = системно зависимая вешь и на 32х разрядных машинах она 4 байта.
точно нигде не делал предположения, что WORD = int ?

с данными работаешь в || потоках? синхронизацию чтения/записи общих переменных через семафоры делал?

пробовал закоментировать всё, что коментируется?

на крайний случай переменные которые || читаются/пишутся volatile объявил? а то вдруг в ходе оптимизаций что-то легло в регистры или вообще было сочтено константой smile.gif

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. А может попытаться как-то поменять местами байты в слове?

User Attached Image Скачать файл
LView.exe


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;

или как-то по-другому?

User Attached Image Скачать файл
Data.exe


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

User Attached Image Скачать файл
_07_ADC_2005.03.12_00_40.rar


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-битные числа вручную.

User Attached Image Скачать файл
_07_ADC_2005.03.12_18_39.rar


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
стр.: (2) < [1] 2 >
Вернуться в Вопросы программирования в C++Builder