|
Xmega АЦП |
|
|
|
Mar 11 2012, 07:26
|
Частый гость
 
Группа: Участник
Сообщений: 140
Регистрация: 21-04-11
Пользователь №: 64 524

|
Сначала приведу код , а потом расскажу в чём проблема: Хэдер для АЦП: Код #define ADC_BUFFER_SIZE 256
/* AdcConvertionMode - режим преобразования, знаковый или без знаковый (см. в даташите на странице ) == 0 - беззнаковый режим == 1 - знаковый режим Resolution - разрядность == 00 - 12 бит выравнивание справа == 01 - зарезервированно == 10 - 8 бит выравневание справа == 11 - 12 бит выравнивание слева Reference - опорное напряжения == 00 - внутреннее 1.00V == 01 - внешнее VCC/1.6 == 10 - внешнее на порту A. == 11 - внешнее на порту B. Prescaler - делитель частоты АЦП == 000 - 4 == 001 - 8 == 010 - 16 == 011 - 32 == 100 - 64 == 101 - 128 == 110 - 256 == 111 - 512 AdcPin - Вход АЦП == 000 - ADC0 pin == 001 - ADC1 pin == 010 - ADC2 pin == 011 - ADC3 pin == 100 - ADC4 pin == 101 - ADC5 pin == 110 - ADC6 pin == 111 - ADC7 pin Values[ADC_BUFFER_SIZE] - буфер в котором хранятся преобразованные значения CurVal - текущее количество значений в буфере ConversionCnt - количество измерений которые надо сделать Flags - текущее состояние АЦП */ struct ADC_cfg { unsigned char AdcConvertionMode; unsigned char Resolution; unsigned char Reference; unsigned char Prescaler; unsigned char AdcPin; unsigned short Values[ADC_BUFFER_SIZE]; unsigned short CurVal; unsigned short ConversionCnt; unsigned char Flags; } ADC_config;
#define ADC_STOPED 0 #define ADC_STARTED 1 #define ADC_CONVERSION_COMPLETE 2
void ADC_init(); void ADC_deinit(); void ADC_flush(); void ADC_start(); void ADC_wait(); Соурс для АЦП:Код void ADC_init() { // нужную ногу настраиваем на вход PORTA.DIRCLR = 1 << ADC_config.AdcPin; // CTRLA - ADC Control Register A (см. в даташите на странице 302) // здесь просто включаем АЦП выставляя нуливой бит ADCA.CTRLA = 1; // CTRLB - ADC Control Register B // здесь в зависимосте от настроек выставляем знак, режим, разрядность ADCA.CTRLB = ((ADC_config.AdcConvertionMode << ADC_CONMODE_bp) | (ADC_config.Resolution << 1)); // REFCTRL - ADC Reference Control register // выставляем опорное напряжение ADCA.REFCTRL = (ADC_config.Reference << 4) | 2; // делитель частоты АЦП, по даташиту максимальная частота 2 МГц ADCA.PRESCALER = ADC_config.Prescaler; // Настраиваем мультиплексор ADCA.CH0.CTRL = 1; ADCA.CH0.MUXCTRL = ADC_config.AdcPin << 3; ADCA.CH0.INTCTRL = 3; ADC_config.CurVal = 0; ADC_config.Flags = ADC_STOPED; }
void ADC_deinit() { // отключаем АЦП ADCA.CTRLA = 0; ADC_config.Flags = ADC_STOPED; }
void ADC_flush() { // очистить АЦП ADCA.CTRLA |= 2; ADC_config.CurVal = 0; ADC_config.Flags = ADC_STOPED; }
void ADC_start() { // начать преобразование //ADCA.CH0.CTRL |= 128; ADCA.CTRLA |= 4; // start channel 0 ADC_config.Flags = ADC_STARTED; }
void ADC_wait() { do {} while(ADC_config.Flags != ADC_CONVERSION_COMPLETE); }
ISR(ADCA_CH0_vect) { ADC_config.Values[ADC_config.CurVal] = ADCA.CH0.RES; //ADCA.CH0.INTFLAGS = ADC_CH_CHIF_bm; ADC_config.CurVal++; ADC_config.ConversionCnt--; if(ADC_config.ConversionCnt != 0) { ADC_start(); } else { ADC_config.Flags = ADC_CONVERSION_COMPLETE; } } И как пользуюсь: Код // заполняем конфигурационную структуру ADC_config.AdcConvertionMode = 0; // без знаковый ADC_config.Resolution = 0; // 12 бит, выравневание справа ADC_config.Reference = 0; // внутрений источник опорного напряжения 1 В ADC_config.Prescaler = 2; // делитель частоты 64 (частота АЦП получается 2 МГц при тактовой частоте камня 500 КГц) ADC_config.AdcPin = 0; // выбираем ногу ADC0 // инициализируем АЦП ADC_init(); // устанавляваем количество преобразований ADC_config.ConversionCnt = ((unsigned short) ParseBuf[1]) << 8; ADC_config.ConversionCnt |= (unsigned short) ParseBuf[0]; // запускаем цикл измерений ADC_flush(); ADC_start();
// ожидаем завершения цикла измерений [color="#FF0000"]_delay_us(50); //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!![/color] ADC_wait(); // отключаем АЦП ADC_deinit(); Проблема в следующем: если есть строка _delay_us(50); //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! то всё работает норм, делаются нужное количество преобразований и отсылаются мне на комп... А вот если я убираю данную строку то программа зависает в функции ADC_wait(); (там цикл выполняется пока все преобразования не выполнятся). Флаг выставляется в прерывании АЦП (строчка ADC_config.Flags = ADC_CONVERSION_COMPLETE;)... Без вот этой задержки почему то не каких преобразований не делается, может конечно они делаются но прерывание не срабатывает!!! Люди, может я что то упускаю из особенностей работы с данным АЦП? Вот если нужно проект для AVR Studio 5:
ADC.rar ( 290.39 килобайт )
Кол-во скачиваний: 106
|
|
|
|
|
Mar 11 2012, 08:34
|
Частый гость
 
Группа: Участник
Сообщений: 140
Регистрация: 21-04-11
Пользователь №: 64 524

|
Цитата(Палыч @ Mar 11 2012, 10:40)  Вероятно, не хватает volatile при описании Flags А зачем? это же просто переменная в которой я храню текущее состояние АЦП и все...
|
|
|
|
|
Mar 11 2012, 09:24
|
Частый гость
 
Группа: Участник
Сообщений: 140
Регистрация: 21-04-11
Пользователь №: 64 524

|
Цитата(Палыч @ Mar 11 2012, 11:54)  Поскольку значение этого поля структуры может измениться "вдруг" при наступлении прерывания А можно по подробней... просто я считал что volatile используется для гарантирования, что что-то запишется во флэш (например volatile asm("cli"))!!!!! Поставил volatile помогло БОЛЬШУЩЕЕ СПАСИБО Палыч!!!!!!!!
|
|
|
|
|
Mar 11 2012, 09:42
|

Гуру
     
Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954

|
Цитата(Xeon @ Mar 11 2012, 13:24)  А можно по подробней... Выполнение цикла в ADC_wait() выполняется следующим образом: 1. Взять значение Flags на регистр Temp 2. Сравнить значение регистра Temp со значением ADC_CONVERSION_COMPLETE 3. Если условие не выполняется то выход из цикла и функции 4. В противном случае - переход: а) при отсутствии volatile - значение Flags не изменилось в теле цикла и находится в регистре Temp, то переход на п.2 (впадаем в бесконечный цикл) б) с volatile - значение Flags могло быть изменено где-то (не важно где), то на п.1
|
|
|
|
|
Mar 11 2012, 12:58
|
Частый гость
 
Группа: Участник
Сообщений: 140
Регистрация: 21-04-11
Пользователь №: 64 524

|
Цитата(Палыч @ Mar 11 2012, 13:42)  Выполнение цикла в ADC_wait() выполняется следующим образом: 1. Взять значение Flags на регистр Temp 2. Сравнить значение регистра Temp со значением ADC_CONVERSION_COMPLETE 3. Если условие не выполняется то выход из цикла и функции 4. В противном случае - переход: а) при отсутствии volatile - значение Flags не изменилось в теле цикла и находится в регистре Temp, то переход на п.2 (впадаем в бесконечный цикл) б) с volatile - значение Flags могло быть изменено где-то (не важно где), то на п.1 Если я правельно понил: получается, что без volatile зайдя в цикл копируется Flags в temp регистр один раз и потом проверяется при этом нового копирования не происходит...а в случае присутствия volatile значение Flags копируется в temp на каждой итерации?????
|
|
|
|
|
Mar 11 2012, 16:53
|
Частый гость
 
Группа: Участник
Сообщений: 140
Регистрация: 21-04-11
Пользователь №: 64 524

|
Цитата(Палыч @ Mar 11 2012, 19:37)  Да, это - "гримаса" оптимизатора. Спецификатор volatile "подсказывает" оптимизатору, что значение Flags может изменяться в любой момент времени, и заставляет генерить код, который "достаёт" значение переменной из памяти всякий раз, когда её значение используется в неком выражении. Помимо того, что volatile используется для того что бы гарантировать, что какая либо команда будет точно вставленна компилятором и "доставать значение всякий раз когда пытаемся изменить его, для чего ещё может быть использованна volatile?"
|
|
|
|
|
Mar 28 2012, 10:39
|
Частый гость
 
Группа: Участник
Сообщений: 140
Регистрация: 21-04-11
Пользователь №: 64 524

|
И снова Всем привет! Ситуация следующая нужно использовать АЦП вместе с ДМА... Есть Буфер в который надо положить 256 значений. Код инициализации: CODE void ADC_init() { // нужную ногу настраиваем на вход PORTA.DIRCLR = 1 << /*ADC_config.*/AdcPin; // CTRLA - ADC Control Register A (см. в даташите на странице 302) // здесь просто включаем АЦП выставляя нулeвой бит ADCA.CTRLA = ADC_ENABLE_bm /*| (1 << 7) | (1 << 6)*/; // CTRLB - ADC Control Register B // здесь в зависимосте от настроек выставляем знак, режим, разрядность ADCA.CTRLB = ((/*ADC_config.*/AdcConvertionMode << ADC_CONMODE_bp) | (/*ADC_config.*/Resolution << 1)) | 0x08; // REFCTRL - ADC Reference Control register // выставляем опорное напряжение ADCA.REFCTRL = (/*ADC_config.*/Reference << 4) | 2; // делитель частоты АЦП, по даташиту максимальная частота 2 МГц ADCA.PRESCALER = /*ADC_config.*/Prescaler; // Настраиваем мультиплексор ADCA.CH0.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc; ADCA.CH0.MUXCTRL = /*ADC_config.*/AdcPin << 3; //ADCA.CH0.INTCTRL = 3; /*ADC_config.*/CurVal = 0; /*ADC_config.*/Flags = ADC_STOPED; DMA DMA.CTRL = DMA_ENABLE_bm; DMA.CH0.CTRLA = /*DMA_CH_SINGLE_bm |*/ DMA_CH_REPEAT_bm; DMA.CH0.CTRLB = 3; DMA.CH0.ADDRCTRL = DMA_CH_SRCRELOAD_gm | DMA_CH_SRCDIR0_bm | DMA_CH_DESTRELOAD_gm/* | DMA_CH_DESTDIR0_bm*/; DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_ADCA_CH0_gc; DMA.CH0.TRFCNT = 10; DMA.CH0.SRCADDR0 = /*(uint16_t)&inval;*/(uint16_t)&ADCA.CH0.RES; DMA.CH0.SRCADDR1 = /*(uint16_t)&inval>>8;*/(uint16_t)&ADCA.CH0.RES>>8; DMA.CH0.SRCADDR2 = 0; DMA.CH0.DESTADDR0 = /*(uint16_t)&outval;*/(uint16_t)&Values; DMA.CH0.DESTADDR1 = /*(uint16_t)&outval>>8;*/(uint16_t)&Values>>8; DMA.CH0.DESTADDR2 = 0; DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm; }
Есть ещё функция которая начинает цикл измерений: CODE void ADC_start() { // начать преобразование ADCA.CH0.CTRL |= 128; ADCA.CTRLA |= 4; // start channel 0 }
И прерывание ДМА: CODE ISR(DMA_CH0_vect) { _DebugMes(USARTE0, "DMA interrupt\r\n", 19); ADCA.CTRLA = 0; Flags = ADC_CONVERSION_COMPLETE; DMA.CH0.CTRLB |= (1 << 4) | (1 << 5); }
В итоге получается что измерения не происходит...в прерывание не когда не входим...и...если вывожу значения из буфера, то там нули. В чём моя ошибка? +Вопрос: ставлю на АЦП частоту в 250кГЦ, знаковый режим, 8 бит... по даташиту получаю задержку распространения в 20мкс что соответствует реально 50000 выборок в секунду, так же в даташите сказано при 8 разрядах минимальная задержка распространения 2,5 мкс что чоответствует 400000 выборок... и там же говорят что максимальная частота преобразований 2 мегавыборки...Врут?...противоречат сами себе? Или используя ДМА эта задержка убирается? И в добавок: какую максимальную частоту можно подавать на АЦП? Например, когда я подавал 500КГц и мерил синусоиду к 63,5КГц, всё было ок... крачивая такая синусойда на графике))), а вот когда увеличивал частоту до 1МГц, то получал полную белиберду!!!!
|
|
|
|
|
Apr 27 2013, 04:13
|
Группа: Новичок
Сообщений: 2
Регистрация: 27-04-13
Пользователь №: 76 649

|
Цитата(ASDFG123 @ Apr 5 2013, 11:11)  почитал отзывы по ней, и ацп в пред. ревизиях не работал с заявленными хар-ками.
Нужно сделать вольтметр который сможет померить напряжения импульсов длительностью 6 мкс
даташита кстати у нее нет, есть только подобии в виде manual Сделал подобный вольтметр на мега8. Принцип очень простой. На микроконтроллере формируется экспонентно спадающая пила с периодом 200 -300 мс. Входные импульсы длит ~ 1мкс. подаются на вход встроенного компаратора. На другой вход пила. В момент совпадения пила останавливается и ее напряжение оцифровывается встроенным АЦП. Затем цикл повторяется. На зтом принципе сделал измеритель импульсной мощности на AD8313. Точность измерений 0.5дБ. Динамический диапазон 60дБ. Входные радиочастотные импульсы длит от 0.8мкс до бесконечности.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|