Перейти к содержанию
    

ADC+DMA AVR ATxmega192

Добрый день!

 

Использую DMA для передачи значений АЦП в буфер (массив из 4652 ячеек). Останов преобразований АЦП происходит в произвольный момент времени. Однако регистр DMA.CH0.TRFCNT всегда считываю равным 4652.

Подскажите, пожалуйста, в чем ошибка?

 

Я пишу в буфер по кругу (т.е. когда дохожу до конца массива перепрыгиваю на начало), поэтому мне необходимо знать номер ячейки массива, где записана последняя точка.

 

Инициализацию DMA и считывание регистра DMA.CH0.TRFCNT следующим образом:

 

void DMAC_init()
{
  DMA.CTRL=0;//DMA отключен
  DMA.CH0.CTRLA|=(1<<7)|(1<<2);//посылка из 1 байта 
                        //разрешение работы 0 канала DMA
                        //singleshot
  DMA.CH0.ADDRCTRL|=(1<<3)|(1<<2);//Начальное значение перезагружается 
                                  // в регистр адреса получателя DMA в конце каждой транзакции 
  DMA.CH0.ADDRCTRL|=(1);//Инкрементное увеличение адреса получателя                                  
  DMA.CH0.TRIGSRC=0x20;//Запуск оn ADCB
  DMA.CH0.TRFCNT=4652;//Количество байт в блоке
  DMA.CH0.SRCADDR0=0x64;//младший байт адреса RESL ADCB.CH0
  DMA.CH0.SRCADDR1=0x02;//старший байт адреса RESL ADCB.CH0
  DMA.CH0.DESTADDR0=((int)&Ch0)&255;
  DMA.CH0.DESTADDR1=((int)&Ch0)>>8;
}

void main
{
DMAC_init();
DMA.CTRL|=(1<<7);//Включили DMA
ADCB_init(); 
ADCB.CTRLA|=1;//Включаем АЦП 
ADCB.CTRLB|=(1<<3); // Старт первого преобразования АЦП

//....  //работа DMA;

ADCB.CTRLA=0;//Отключил АЦП
unsigned int ch_i;
ch_i=DMA.CH0.TRFCNT;
}

 

Заранее спасибо!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Однако регистр DMA.CH0.TRFCNT всегда считываю равным 4652.

Таки всегда?

Может дело в этом:

When TRFCNT

reaches zero, the register is reloaded with the last value written to it.

?

 

DMA.CH0.DESTADDR0=((int)&Ch0)&255;

DMA.CH0.DESTADDR1=((int)&Ch0)>>8;

Думаете DESTADDR2 (и SRCADDR2) инициализировать не надо? Если так, то где об этом написано? Поделитесь...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Артем, спасибо!

Дело в том, что АЦП останавливается в произвольный момент, поэтому меня сильно смущает факт достижения нуля регистром DMA.CH0.TRFCNT именно в тот момент, когда останавливается АЦП. Проверял это несколько раз - всегда одно и тоже.

 

Регистры DESTADDR2 (и SRCADDR2) не инициализировал, т.к. для моих адресов они равны нулю. По правильному, кончено, надо их записать как нули, однако, думаю, что это не влияет на чтение. При проверке подавал сигнал 300 Гц от цифрового осциллографа. Считанный с МК сигнал совпадал по амплитуде и частоте.

 

Все таки здесь что-то еще.. может есть еще предположения?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Дело в том, что АЦП останавливается в произвольный момент, поэтому меня сильно смущает факт достижения нуля регистром DMA.CH0.TRFCNT именно в тот момент, когда останавливается АЦП.

В смысле в произвольный момент? простите не понял...

 

Регистры DESTADDR2 (и SRCADDR2) не инициализировал, т.к. для моих адресов они равны нулю. По правильному, кончено, надо их записать как нули

Попробуйте делать правильно...вам что 3 такта на запись в регистр жалко? Может в этом дело?

Хотя кто его знает...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

АЦП останавливается по прерыванию от часов реального времени..

 

Разобрался почему регистр читается как 4652. Дело в том, что после того DMA.CH0.TRFCNT доходит до нуля, он (регистр) снова становится равным предыдущему значению (т.е. 4652), но при этом происходит сброс бита разрешения канала DMA и DMA далее не работает. Что это глюк МК или где-то есть ошибка?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Разобрался почему регистр читается как 4652. Дело в том, что после того DMA.CH0.TRFCNT доходит до нуля, он (регистр) снова становится равным предыдущему значению (т.е. 4652)

В мануале так и написано...

 

но при этом происходит сброс бита разрешения канала DMA и DMA далее не работает. Что это глюк МК или где-то есть ошибка?

Так и должно быть - смотрите в мануале описание регистра CTRLA. Там будет таблица (Table 5-4. Summary of triggers, transaction complete flag and channel disable according to DMA channel configuration.), где расписано когда dma останавливается и тп.

Чтобы dma работал без остановки нужно настроить регистрs канала так: REPEAT=1, SINGLE=1, REPCNT=0. По другому никак. Подойдёт ли вам такая настройка смотрите сами...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Артем, еще раз спасибо.

 

Скачал новый даташит на Xmega192A3, там действительно это есть (в моем даташите не было). Буду пробовать!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Добрый день!

 

В продолжении темы...

 

Чтобы dma работал без остановки нужно настроить регистрs канала так: REPEAT=1, SINGLE=1, REPCNT=0. По другому никак.

При вышеуказанных настройках и включенном прерывании от АЦП все работает как и требовалось. Однако, в связи с тем, что переход в функцию обработки прерывания занимает достаточно длительное время (примерно 1,7 мкс при тактовой частоте 29,4912 МГц и частоте дискретизации АЦП 1,08 МГц, т.е. 1 мкс) пришлось отказаться от прерываний. В замен контролирую флаг СН0IF. Если он установлен преобразование завершено, можно сбрасывать флаг и считывать значение регистра результата АЦП.

 

При этом DMA стал работать следующим образом (наблюдаю по отладчику в AVR Studio 5): запускается раньше, чем устанавливается флаг СH0IF (при этом значение регистра результата преобразования АЦП заполняется значением раньше, чем устанавливается флаг CH0IF) и продолжает работать независимо от состояния флага CH0IF, записывая в массив текущее значение регистра результата АЦП.

 

Подскажите, пожалуйста, где я ошибся? Заранее спасибо.

 

Код проверки состояния флага следующий:

asm("loopInt1:");
asm("wdr");
asm("LDS r18,0x0240");//ADCB.CTRLA - Bit 1 - Enable ADCB
asm("TST r18");//if (!(ADCB.CTRLA&1)) goto loopEx1;
asm("BRNE loopInt4");
asm("rjmp loopEx1");
asm("loopInt4:");
asm("LDS r18,0x0263");//INTFLAGS
asm("CPI r18,1");
asm("BRNE loopInt1");
asm("STS 0x0263,1");//Сбрасываем INTFLAG0
asm("LDS r18, 0264");
asm("INC r2");
asm("CP r2,r3");
asm("BRNE loopInt1");
asm("MOV r5,r18");//t1=ADCB.CH0.RES
asm("CLR r2");//t_i=0;
asm("CLR r4");//FLAG=0;

Изменено пользователем KIG

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Однако, в связи с тем, что переход в функцию обработки прерывания занимает достаточно длительное время (примерно 1,7 мкс при тактовой частоте 29,4912 МГц и частоте дискретизации АЦП 1,08 МГц, т.е. 1 мкс)

Что у вас за обработчик такой? Что вы в нём делаете? Вызываете функции из других файлов? Почему вход такой долгий? Приведите код.

 

пришлось отказаться от прерываний.В замен контролирую флаг СН0IF. Если он установлен преобразование завершено, можно сбрасывать флаг и считывать значение регистра результата АЦП.
СН0IF - это флаг статуса АЦП. Так ведь? От ДМА отказались? ДМА само результат должно прочитать и флаг СН0IF сбросить. Или вы что -то другое придумали?

 

При этом DMA стал работать следующим образом (наблюдаю по отладчику в AVR Studio 5): запускается раньше, чем устанавливается флаг СH0IF (при этом значение регистра результата преобразования АЦП заполняется значением раньше, чем устанавливается флаг CH0IF)
Что-то странное...Код приведите...
Подскажите, пожалуйста, где я ошибся? Заранее спасибо.

 

Код проверки состояния флага следующий:

asm("loopInt1:");
asm("wdr");
asm("LDS r18,0x0240");//ADCB.CTRLA - Bit 1 - Enable ADCB

Нужен ли ассемблер тут? Вопрос...

 

Вы не пробовали использовать ДМА в ping-ping режиме(DMA_DBUFMODE_CH01_gc или DMA_DBUFMODE_CH23_gc и тп)? Если каналы есть, то попробуйте.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Артем, спасибо за помощь. Попытаюсь объяснить подробнее.

 

Что у вас за обработчик такой? Что вы в нём делаете? Вызываете функции из других файлов? Почему вход такой долгий? Приведите код.

 

Задача МК записывать осциллограмму в массив и выполнять цифровую фильтрацию измеренного сигнала. Сначала идея была в использовании прерываний МК для цифровой фильтрации, которая заключается в нахождении разницы между текущим отсчетом АЦП и предыдущим 40 отсчетом АЦП. DMAС предполагалось использовать для записи осциллограммы. Однако, измеряя в дебаггере AVR Studio время перехода в функцию прерывания, решил внести изменения в алгоритм.

 

Теперь контролирую флаг завершения преобразования АЦП (CH0IF - флаг статуса, который говорит о завершенном преобразовании АЦП по даташиту от 11.2012). Как только вижу что флаг установился, сбрасываю его и считываю регистр результата преобразования АЦП. Выполняю преобразования над новым отсчетом и снова ожидаю установки флага завершения преобразования АЦП. При этом запись осциллограммы также возлагается на DMAC. DMAC фиксирует установку флага CH0IF (ранее я думал, что DMAC будет так делать) и копирует значение регистра результата преобразования АЦП в массив. Однако, не тут то было...

 

Ассемблер решил использовать для ускорения работы программы, т.к. надо укладываться в 1 мкс (т.е. успевать обрабатывать отсчет пока АЦП преобразует новый). В программе также используется прерывание от Компаратора, который контролирует напряжение питания. Если срабатывает компаратор, то происходит останов АЦП. Для этого контролирую 1 бит ADCB.CTRLA.

 

Код программы без использования прерываний следующий:

 

unsigned char EEMEM C0_ctrl_E =3|(1<<2);//диф. режим АЦП с усилением 2x
unsigned char EEMEM C0_MUXpos_E =0; //PIN0
unsigned char EEMEM C0_MUXneg_E =0; //PIN4
unsigned char EEMEM ADC_ref_E =(1<<4);//опорное напряжение Vcc/1.6V
unsigned char EEMEM ADC_prescaler_E =(1<<1)|1;

register unsigned char t_i asm("r2");
register unsigned char t_eq asm("r3");
register unsigned char FLAG asm("r4");
register unsigned char t1 asm("r5");
register unsigned char Ustavka asm("r6");
register unsigned char ch0_iL asm("r7");
register unsigned char ch0_iH asm("r8");
register unsigned char ValueOfFlag asm("r9");
register unsigned char One asm("r10");
register unsigned char TempR asm("r11");


void ADC_B_init()
{
if (!RESET) asm("wdr");

unsigned char ADC_C0_ctrl =eeprom_read_byte((uint8_t *)0x02);
unsigned char C0_MUXpos =eeprom_read_byte((uint8_t *)0x03);
unsigned char C0_MUXneg =eeprom_read_byte((uint8_t *)0x04);
unsigned char ADC_ref =eeprom_read_byte((uint8_t *)0x05);
unsigned char ADC_prescaler=eeprom_read_byte((uint8_t *)0x06);

if (!RESET) asm("wdr");

ADCB.CTRLA=2;// очищаем настройки АЦП
ADCB.REFCTRL=ADC_ref;
ADCB.PRESCALER=ADC_prescaler; 

ADCB.EVCTRL=0;//используем нулевой канал

ADCB.CH0.CTRL=ADC_C0_ctrl;//дифференциальный входной 
//сигнал c усилением 1x ADCB.CH0.CTRL=3;
ADCB.CH0.MUXCTRL=(C0_MUXpos<<3)|(C0_MUXneg);
ADCB.CH0.INTCTRL=0;//MAX Level is OFF
ADCB.CH0.INTFLAGS=0x01; // сбрасываем флаги прерываний канала

ADCB.CALL=Production_Signature_Row_Read(0x24); // ADCBCAL0 = 0x44
ADCB.CALH=Production_Signature_Row_Read(0x25); // ADCBCAL1 = 0x04

PORTB.PIN0CTRL=7;
PORTB.PIN1CTRL=7;
PORTB.PIN2CTRL=7;
PORTB.PIN4CTRL=7;
PORTB.PIN5CTRL=7;
PORTB.PIN6CTRL=7;
}

void DMAC_init()
{
DMA.CTRL=0;
DMA.CH0.CTRLB=(1<<4)|(1<<5);//Отключаем прерывания от DMA и сбрасываем флаги TRNIF и ERRIF
DMA.CH0.ADDRCTRL|=(1<<3)|(1<<2);//Начальное значение перезагружается 
// в регистр адреса получателя DMA в конце каждой транзакции 
DMA.CH0.ADDRCTRL|=(1);//Инкрементное увеличение адреса получателя 
DMA.CH0.TRIGSRC=0x20;//0x20;//Запуск оn ADCB
DMA.CH0.TRFCNT=10;//Количество байт в блоке
DMA.CH0.REPCNT=0;
DMA.CH0.SRCADDR0=0x64;//младший байт адреса RESL ADCB.CH0
DMA.CH0.SRCADDR1=0x02;//старший байт адреса RESL ADCB.CH0
DMA.CH0.SRCADDR2=0;
DMA.CH0.DESTADDR0=((int)&Ch0)&255;
DMA.CH0.DESTADDR1=((int)&Ch0)>>8;
DMA.CH0.DESTADDR2=0;

DMA.CH0.CTRLA|=(1<<7)|(1<<5)|(1<<2);//посылка из 1 байта
//REPEAT 
//разрешение работы 0 канала DMA
//singleshot
}
void main ()
{
ADC_B_init();
DMAC_init();


DMA.CTRL|=(1<<7);
ADCB.CTRLB=(1<<4)|(1<<2);//знаковый режим и 8 битное разрешение АЦП 
ADCB.CTRLA|=1;//Включаем АЦП 
ADCB.CTRLB|=(1<<3); // Старт первого преобразования АЦП

[b]//-------Здесь идет запись сороковой точки в регистр r5, из которого далее будут вычитаться новый текущий отсчет (этот код не привожу т.к. он однотипный коду далее)
//При выполнения кода ниже DMA сюдя по дебаггеру ведет себя не так как задумано[/b]
asm("loopInt1:");
asm("wdr");
asm("LDS r18,0x0240");//ADCB.CTRLA - Bit 1 - Enable ADCB
asm("TST r18");//if (!(ADCB.CTRLA&1)) goto loopEx1;
asm("BRNE loopInt4");
asm("rjmp loopEx1");
asm("loopInt4:");
asm("LDS r18,0x0246");//INTFLAGS
asm("CPI r18,1");
asm("BRNE loopInt1");
asm("STS 0x0246,1");//Сбрасываем INTFLAG0
asm("LDS r18, 0264");
asm("INC r2");
asm("CP r2,r3");
asm("BRNE loopInt1");
asm("MOV r5,r18");//t1=ADCB.CH0.RES
asm("CLR r2");//t_i=0;
asm("CLR r4");//FLAG=0;

}

 

Теперь приведу код с обработчиком прерывания. Настройка параметров АЦП такая же как указано выше, только ADCB.CH0.INTCTRL=3;//MAX Level is on

Настройка DMA такая же как выше. Переход из основной функции main () в обработчик прерывания занимает много времени. Думаю это связано с тем, что компилятор преобразуя код си добавляет команды сохранения в стек адреса с которого произошло прерывание.

 

ISR(ADCB_CH0_vect) [b]//Общее время выполнения команд в прерывании занимает не более 0,8 мкс[/b]
{
asm("wdr"); 
asm("LDS R18,0x0264");//0x0264 - адрес ADCB.CH0.RESL 

asm("TST r4");//if (FLAG!=) тогда переход
asm("BRNE loop1");//Метка Аварии

//t_i - номер текущей точки - r2
//t_e - номер разностной точки - для частоты 1 МГц равна 40, для частоты 0,5 Мгц равна 20 - r3
asm("CP r2,r3");
asm("BRNE loop2");//Выход1
  
asm("SUB R18,r5");//t3=t2-t1; (разницы нет t1-t2 или t2-t1)
   
asm("TST R5");//if (t3>0)
asm("BRGE loop3");//Если R5 больше нуля или равно нулю, тогда переход
asm("NEG R5");//Если R5 меньше, тогда находим модуль

asm("loop3:");
asm("CP R5,R6");//if (t3>Ustavka)
asm("BRLO loop6");
asm("INC R4");
asm("STS 0x0689, R1");//PORTE.INTCTRL=0; Отключаем прерывание от часов
asm("RJMP loop5");
   
asm ("loop6:");
asm ("MOV r5,r18");//t1=r18
asm ("CLR r2");//t_i=0;
asm ("RJMP loop5");

asm("loop1:");
asm("CP R4,r9");
asm("BREQ loop5");   
asm("ADD r7,1");
asm("ADC r8,0");
asm("MOV R19, R7");
asm("LDI R20, hi8(LEN)");
asm("CPI r19, lo8(LEN)");
asm("CPC r8,r20");
asm("BRNE loop5");
asm("STS 0x0241, R1");
asm("STS 0x0240, R1");
asm("LDI R18, 152");
asm("STS 0x0664, R18");//PORTD.OUT|=(1<<4);

asm ("loop2:");
asm("INC R2");//t_i++

asm("loop5:");
}

 

Вы не пробовали использовать ДМА в ping-ping режиме(DMA_DBUFMODE_CH01_gc или DMA_DBUFMODE_CH23_gc и тп)? Если каналы есть, то попробуйте.

 

Каналы есть. Расскажите, пожалуйста, подробнее как могу использовать DMA в ping-ping режиме. (DMA_DBUFMODE_CH01_gc - если правильно понял - это ведь создание буфера DMA )

Изменено пользователем KIG

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

DMAС предполагалось использовать для записи осциллограммы.
Осцилограмма - это значения АЦП без фильтрации?

 

При этом запись осциллограммы также возлагается на DMAC. DMAC фиксирует установку флага CH0IF (ранее я думал, что DMAC будет так делать) и копирует значение регистра результата преобразования АЦП в массив. Однако, не тут то было...

ДМА так и делает, но при этом сбрасывает флаг CH0IF.

 

Ассемблер решил использовать для ускорения работы программы, т.к. надо укладываться в 1 мкс (т.е. успевать обрабатывать отсчет пока АЦП преобразует новый).
Надо ли?

 

Переход из основной функции main () в обработчик прерывания занимает много времени.
10 тактов не больше. Оптимизация включена?

 

Код программы без использования прерываний следующий:
DMA.CH0.TRIGSRC=0x20;//0x20;//Запуск оn ADCB Переход из основной функции main () в обработчик прерывания занимает много времени. Думаю это связано с тем, что компилятор преобразуя код си добавляет команды сохранения в стек адреса с которого произошло прерывание.[/quote] То есть у вас триггером для ДМА является флаг завершения преобразования? Тогда прерывание от АЦП использовать невозможно - они оба флаг сбрасывают. Кто первый успеет тот и сбросит...

[quote name='KIG' date='Jan 21 2014, 14:49' post='1228343']
[code]ISR(ADCB_CH0_vect) [b]//Общее время выполнения команд в прерывании занимает не более 0,8 мкс[/b]
{
asm("wdr"); 
asm("LDS R18,0x0264");//0x0264 - адрес ADCB.CH0.RESL 

asm("TST r4");//if (FLAG!=) тогда переход
asm("BRNE loop1");//Метка Аварии

//t_i - номер текущей точки - r2
//t_e - номер разностной точки - для частоты 1 МГц равна 40, для частоты 0,5 Мгц равна 20 - r3
asm("CP r2,r3");
  
}

Не надо столько асма...

 

Каналы есть. Расскажите, пожалуйста, подробнее как могу использовать DMA в ping-ping режиме.

Два канала работают по очереди - сначала один копирует результаты в ОЗУ, потом - второй. Когда один из каналов скопировал весь блок запускается обработка этого блока (фильтрация). В это время другой канал ДМА копирует результаты АЦП в другое место ОЗУ. И так далее. То есть цикл выглядит так:

----канал 0 ДМА сохраняет данные в массив 0, программа обрабатывает данные из массива 1 --- канал 1 ДМА сохраняет данные в массив 1, программа обрабатывает данные из массива 0 --- и так далее, пока компаратор всё это не прекратит.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Артем, большое спасибо! Идея c ping-ping режимом DMA очень интересная. Буду реализовывать.

 

Осциллограмма - это значения АЦП без фильтрации?

 

Все верно!

 

ДМА так и делает, но при этом сбрасывает флаг CH0IF.

 

CH0IF принадлежит модулю АЦП. Правильно ли я понимаю, что ДМА сбрасывает флаг модуля АЦП? Ранее думал, что этот флаг автоматически сбрасывается при переходе на вектор прерывания или путем записи в него 1.

 

10 тактов не больше. Оптимизация включена?

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

 

Еще вопрос:

правильно ли я понимаю, что DMA может работать, записывая в память отсчеты АЦП, при тех же настройках (что и ранее), если я не буду использовать прерывания АЦП (т.е. ADCB.CH0.INTCTRL=0;//MAX Level is OFF)??

Изменено пользователем KIG

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

CH0IF принадлежит модулю АЦП. Правильно ли я понимаю, что ДМА сбрасывает флаг модуля АЦП?
Сбрасывает - в мануале искать не буду, но иначе ДМА бы без остановки читал результат. Написал и засомневался, но проверить не на чём (скорее некогда). Посмотрите что симулятор показывает - это он отрабатывает правильно.

Ранее думал, что этот флаг автоматически сбрасывается при переходе на вектор прерывания или путем записи в него 1.

Это само собой.

 

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

Дело в программе. 100 %.

 

правильно ли я понимаю, что DMA может работать, записывая в память отсчеты АЦП, при тех же настройках (что и ранее), если я не буду использовать прерывания АЦП (т.е. ADCB.CH0.INTCTRL=0;//MAX Level is OFF)??
По идее верно - прерывания и ДМА вместе не удасться использовать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...