реклама на сайте
подробности

 
 
> ADC+DMA AVR ATxmega192
KIG
сообщение Jan 13 2014, 07:12
Сообщение #1


Частый гость
**

Группа: Участник
Сообщений: 115
Регистрация: 25-12-06
Пользователь №: 23 884



Добрый день!

Использую 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;
}


Заранее спасибо!
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов
KIG
сообщение Jan 21 2014, 12:49
Сообщение #2


Частый гость
**

Группа: Участник
Сообщений: 115
Регистрация: 25-12-06
Пользователь №: 23 884



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

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


Задача МК записывать осциллограмму в массив и выполнять цифровую фильтрацию измеренного сигнала. Сначала идея была в использовании прерываний МК для цифровой фильтрации, которая заключается в нахождении разницы между текущим отсчетом АЦП и предыдущим 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 - Jan 21 2014, 12:52
Go to the top of the page
 
+Quote Post



Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 22nd July 2025 - 18:06
Рейтинг@Mail.ru


Страница сгенерированна за 0.01393 секунд с 7
ELECTRONIX ©2004-2016