Страница 1 из 4
Несколько каналов АЦП Atmega 88
Добавлено: Ср авг 09, 2017 08:08:18
Jman
Добрый день, подскажите пожалуйста, есть ряд непонятных вопросов по АЦП данного МК. Мне необходимо раз в 200 мкс опрашивать один из входов, пусть их будет 3 (2-1-0) и усреднять это значение по 3 выборкам.
Допустим три канала измеряют напряжение от 0 до 2,5В, подключен от внешний ИОН на 2,5В
Мое понимание:
Мультиплексором переключать каждые 200 мс по прерыванию таймера входа?
И как усреднять по выборкам?
Буду благодарен любой помощи.
Re: Несколько каналов АЦП Atmega 88
Добавлено: Ср авг 09, 2017 09:18:19
Ivanoff-iv
используй массивы (для настройки ацп одномерный (или один из столбцов двумерного) а для усреднения - двумерный(один столбец - один канал)), только таймер на 200/3 мс (к нужному каналу в срок вернётся).
Re: Несколько каналов АЦП Atmega 88
Добавлено: Ср авг 09, 2017 09:32:50
Jman
По поводу усреднения, читаю например 3 канала дергая мультиплексор таймером по переполнению со значением 200мкс, суммирую, а потом делю на 3? Так нужно сделать?
Re: Несколько каналов АЦП Atmega 88
Добавлено: Ср авг 09, 2017 10:29:19
Ivanoff-iv
делаешь массив 3х4 (3 — количество каналов, 4 = 1 3 — 1 настройка (то что в мух вписать после замера для включения след. канала) и 3 — количество замеров усреднения).
i и j счетчики, по первому переключаешь каналы, а по второму ячейки масива усреднения.
Re: Несколько каналов АЦП Atmega 88
Добавлено: Ср авг 09, 2017 12:54:16
Jman
А можете пример привести кода? Чет не пойму...
Добавлено after 2 hours 16 minutes 36 seconds:
Пытаюсь пока просто переключать каналы. Но что-то совсем не работает... Где я споткнулся?
Код: Выделить всё
volatile uint8_t adc_result[8];
volatile uint8_t input = 0;
void ADC_init(void);
int main(void) {
DDRB = 0xFF;
ADC_init();
sei();
ADCSRA |= (1 << ADSC);
while (1) {
if (adc_result[0] > 150) {
PORTB = 0x01;
} else if (adc_result[0] < 150) {
PORTB = 0x00;
}
if (adc_result[1] > 150) {
PORTB = 0x02;
} else if (adc_result[1] < 150) {
PORTB = 0x00;
}
}
return 0;
}
ISR( ADC_vect ) {
adc_result[input] = ADCH;
if (input < 8)
input++;
else
input = 0;
ADMUX |= input;
ADCSRA |= ( 1 << ADSC );
}
void ADC_init() {
ADMUX |= (1 << ADLAR) | (1 << REFS0);
ADCSRA = (1 << ADEN) | (1 << ADIE) | (1 << ADPS2);
}
Re: Несколько каналов АЦП Atmega 88
Добавлено: Ср авг 09, 2017 13:03:14
ozonn
если под усреднением вы понимаете нахождение среднего арифметического, то никакие массивы не нужны. Просто суммируете все замеры и потом делите на количество замеров
Re: Несколько каналов АЦП Atmega 88
Добавлено: Ср авг 09, 2017 13:05:02
Jman
[uquote="ozonn",url="/forum/viewtopic.php?p=3158920#p3158920"]если под усреднением вы понимаете нахождение среднего арифметического, то никакие массивы не нужны. Просто суммируете все замеры и потом делите на количество замеров[/uquote]
и усреднять это значение по 3 выборкам.
Re: Несколько каналов АЦП Atmega 88
Добавлено: Ср авг 09, 2017 13:09:55
ozonn
делаете три замера, каждый последующий замер прибавляете к предыдущему, потом получившуюся сумму делите на три
.
.
buff = buff+ADCH;
.
.
.
res = buff/3;
.
.
.
Re: Несколько каналов АЦП Atmega 88
Добавлено: Ср авг 09, 2017 13:11:52
Jman
Помогите с настройкой АЦП пожалуйста. Выше код привел. Почему-то не работает.
Re: Несколько каналов АЦП Atmega 88
Добавлено: Ср авг 09, 2017 13:41:08
ozonn
[uquote="Jman",url="/forum/viewtopic.php?p=3158927#p3158927"]Помогите с настройкой АЦП пожалуйста. Выше код привел. Почему-то не работает.[/uquote]
вот тут собака порылась:
ADMUX |= input;
потому как:
7 | 0 = 7
Re: Несколько каналов АЦП Atmega 88
Добавлено: Ср авг 09, 2017 13:44:01
Jman
ADMUX нужно сбросить? А как?
Re: Несколько каналов АЦП Atmega 88
Добавлено: Ср авг 09, 2017 13:52:00
ozonn
скажу честно, с 88-й дела не имел. Почитал бегло даташит и сходу не понял, как выбрать Single Conversion. В 8-й есть вит ADFR, который включает и отключает Free Running. В 88-й такого бита не нашел
Добавлено after 2 minutes 56 seconds:
[uquote="Jman",url="/forum/viewtopic.php?p=3158945#p3158945"]ADMUX нужно сбросить? А как?[/uquote]
записать в него нули, потом снова инкрементировать, как вы делаете с input.
просто вместо input инкрементируйте ADMUX, если он больше 7-ми, обнуляйте
Re: Несколько каналов АЦП Atmega 88
Добавлено: Ср авг 09, 2017 13:53:46
Ivanoff-iv
можно и копить, но тогда результат заранее заказывать, а потом ждать придётся.
с кодом смогу помочь только завтра...
Re: Несколько каналов АЦП Atmega 88
Добавлено: Ср авг 09, 2017 16:23:26
roman.com
а что даёт усреднение по трём выборкам АЦП?
Re: Несколько каналов АЦП Atmega 88
Добавлено: Чт авг 10, 2017 07:39:57
Ivanoff-iv
некоторое снижение случайной ошибки, вот например возьмём 10бит шим (младший разряд шумящий) сложим 2 замера и получим 11бит сигнал, младший шумящий - отсекаем его (/2) и получаем обратно 10бит сигнал, но уже стабильней.
Добавлено after 2 hours 59 minutes 28 seconds:
посмотрел настройки этой микры в адмух можно просто номер вода (от 0 по 7й) вписать (поэтому массив сдесь не нужен).
начальные настройки:
Код: Выделить всё
#include <mega88.h>
#include <delay.h>
#define ADC_VREF_TYPE 0xC0
volatile unsigned int A_adc[9];
...
// ADC initialization
// ADC Clock frequency: 250,000 kHz
// ADC Voltage Reference: Int., cap. on AREF
// ADC Auto Trigger Source: Free Running
// Digital input buffers on ADC0: Off, ADC1: Off, ADC2: Off, ADC3: On
// ADC4: On, ADC5: On
DIDR0=0x07;
ADMUX=ADC_VREF_TYPE & 0xff;
ADCSRA=0xA1;
ADCSRB&=0xF8;
.6;.
main{...}
этот код вызывать каждые 66мс (3 раза за 200мс)
он заполняет массив
Код: Выделить всё
{
static unsigned char n_zam=8;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
A_adc[n_zam]=ADCW;
//next conversion
n_imp++;
if (n_imp>8) n_imp=0;
ADMUX=(n_imp%3) | (ADC_VREF_TYPE & 0xff);
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
}
эта функция выдает среднее по 3м последним замерам по заданному входу
Код: Выделить всё
unsigned int read_3_adc(unsigned char adc_input)
{
if (adc_input<3)
{
adc_input*=3;
return ((A_adc[adc_input]+A_adc[adc_input+1]+A_adc[adc_input+2])/3);
}else{return 0;};
}
основано на сгенерированном CVAVR коде, регистры и работа с ними подсмотрены в AB.
Re: Несколько каналов АЦП Atmega 88
Добавлено: Чт авг 10, 2017 08:13:20
Jman
этот код вызывать каждые 66мс (3 раза за 200мс)
он заполняет массив
Этот код например можно подсунуть в ISR(TIMER0_OVF_Vect) ?
Тут правда 100мкс из кодвижна взял
Код: Выделить всё
ISR( TIMER0_OVF_vect )
{
// 66 us oveflow
TCNT0=BE;
}
Re: Несколько каналов АЦП Atmega 88
Добавлено: Чт авг 10, 2017 08:18:44
Ivanoff-iv
можно в любое место подсунуть, главное, чтобы гарантированно выполнялось.
если не каждые 66мс, а с другой частотой вызывать, то, просто частота замеров изменится.
Добавлено after 1 minute 25 seconds:
одно условие - не чаще чем время выполнения преобразования (кстати можно и в его вектор засунуть, просто я не хотел плодить прерывания)
Re: Несколько каналов АЦП Atmega 88
Добавлено: Чт авг 10, 2017 08:21:42
Jman
Рабочее прерывание по завершению измерения с автосканированием 4х входов. Как это прикрутить к прерыванию по времени?
Я склеил, но это не правильно, чувствую.
Код: Выделить всё
ISR( TIMER0_OVF_vect )
{
// 66 us oveflow
TCNT0=BE;
static unsigned char n_zam=8;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
A_adc[n_zam]=ADCW;
//next conversion
n_imp++;
if (n_imp>8) n_imp=0;
ADMUX=(n_imp%3) | (ADC_VREF_TYPE & 0xff);
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
}
// ADC interrupt service routine
ISR (ADC_vect)
{
static unsigned char input_index=0;
// Read the AD conversion result
adc_data[input_index]=ADCW;
// Select next ADC input
if (++input_index > (LAST_ADC_INPUT-FIRST_ADC_INPUT))
input_index=0;
ADMUX=(FIRST_ADC_INPUT | ADC_VREF_TYPE)+input_index;
// Delay needed for the stabilization of the ADC input voltage
_delay_us(100);
ADCSRA|=(1<<ADSC); //ADCSRA|=0x40
}
Re: Несколько каналов АЦП Atmega 88
Добавлено: Чт авг 10, 2017 08:43:19
Ivanoff-iv
во первых настраивай таймер не 66
us, а 66
ms overflow.
чтобы настраивать частоту прерывания не так
TCNT0=BE; используй
СТС режим
про склейку не понял - два разных кода почти одно и тоже делают - ("мучают" АДС)

6; у меня в коде не знаю кто приписал...
я стараюсь разграничивать код - по ТЗ замер входа каждые 200 мс, 3 входа, а про использование результатов замера ни слова, вот я и оформил отдельно в мелкую функцию, которую когда угодно вызвать можно.
ещё я накосячил - переменная
n_zam это переименованная
n_imp, только я её не везде переименовал.

Re: Несколько каналов АЦП Atmega 88
Добавлено: Чт авг 10, 2017 09:32:04
Jman
По сути вот эта строчка заменят прерывание ISR (ADC_vect)
И мне можно прерывание выбросить.
Со временем так и не понял как сделать по прерыванию таймера, куда вставляется ваш код... Нужен пример... Можете помочь?
Добавлено after 45 minutes 4 seconds:
Если я сделаю
ADC Auto Trigger Source: Timer0 Overflow
Настрою прерывание по таймеру:
ISR( TIMER0_OVF_vect )
{
// 100 us oveflow
// Reinitialize Timer 0 value
TCNT0=0x9C;
}
Это даст мне гарантию на переключение каналов по необходимому времени и по прерыванию завершения измерения?
Код: Выделить всё
ISR (ADC_vect)
{
static unsigned char input_index=0;
// Read the AD conversion result
adc_data[input_index]=ADCW;
// Select next ADC input
if (++input_index > (LAST_ADC_INPUT-FIRST_ADC_INPUT))
input_index=0;
ADMUX=(FIRST_ADC_INPUT | ADC_VREF_TYPE)+input_index;
// Delay needed for the stabilization of the ADC input voltage
_delay_us(100);
ADCSRA|=(1<<ADSC); //ADCSRA|=0x40
}