Сделал программу.
Установил таймер который тикает 1000 раз в секунду.
Использую три прерывания.
2 прерывания на таймер первое вызывается при совпадении второе при переполнении. Третье вызывается когда АЦП выдает результат.
в теле прерываний от таймера достаточно много кода. В теле прерывания АЦП мало кода. Ну и тело программы тоже небольшой код.
Контроллер работает на частоте 8Мгц. Получается у меня есть 8000 тактов на один отрезок времени в который выполняются 3 прерывания и кусочек основной программы. Основная программа не критична к времени.
Самое критично это исполнение кода в теле прерываний от таймера.
1. Вопрос как мне определить сколько тактов съедает каждый кусок кода, чтоб правильно все сбалансировать и избежать непредвиденных ошибок.
На АЦП я измеряю 3 параметра. 1 параметр очень критичный и я его измеряю при каждом такте в 1 мс таймера. Два других параметра измеряю 20 раз в секунду, то есть раз в 50мс.
Ясное дело что у меги 48 один АЦП, поэтому я 48 раз из 50 измеряю первый параметр и 1 раз второй и 1 раз третий.
После выполнения измерения в теле прерывания АЦП если нужно поменять канал я меняю его. Таким образом при следующем измерении уже заранее будет выбран нужный канал. Ясное дело что от выбора другого канала до измерения меньше 1 мс.
2. Вопрос - после смены канала требуется время чтоб провести новое измерение или нет. И если да какое время требуется?
1. Вопрос как мне определить сколько тактов съедает каждый кусок кода, чтоб правильно все сбалансировать и избежать непредвиденных ошибок.
1. открываете ассемблерный листинг и считаете ручками.
2. Если не ошибаюсь, IAR умеет считать циклы и такты.
3. В AVR studio в эмуляторе тоже вроде такая возможность была.
2. Вопрос - после смены канала требуется время чтоб провести новое измерение или нет. И если да какое время требуется?
Судя из описаний, дополнительного времени не требуется. Время на выбор канала вроде как входит во время измерения и занимает, если не изменяет память, первые 1,5 такта.
1) можно открыть cof файл, сгенерированный Codevision в Atmel AVR Studio и в режиме симуляции произвести замеры времени выполнения отдельных функций
2)задержка нужна, генератор кода так рекомендует
ADMUX = adc_input | ADC_VREF_TYPE;
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA |= 0x40;
while ((ADCSRA & 0x10) == 0); // Wait for the AD conversion to complete
ADCSRA |= 0x10;
return ADCW;
Время также зависит от выходного сопротивления источника, который подключен ко входу АЦП
.lss Codevision не делает - есть файлы с расширениями .asm и .lst
При включении устройства, на микроконтроллере питание не появляется мгновенно: http://thebard.narod.ru/EPUS/bileti/16perexproc.htm
Если не включена система контроля питания (BOD) в микроконтроллере, то микроконтроллер может за время переходного процесса питания запустится несколько раз, что будет с регистрами предсказать невозможно. Поэтому делал всегда перед настройками периферии и заданием переменных задержку и обязательное обнуление и выключение не задействованной периферии.
Вообще-то для этого выставляется фьюзами время запуска МК. И обычно оно составлет 65 мсек, что в значительной мере превышает большинство всех переходных процессов. т.е. тактирование начнется после 65 мс устойчивой работы.
Всем доброго времени суток.
Есть вопросик в программном исполнении АЦП на АТмега8.
С регистрами разобрался, примеры программ смотрел. Но что-то не на 100% работает.
Программа простая: на ножке ADC0 смотрим напряжение, передаём значения ADC на терминал по UART. Проще некуда, но ...
Удачно вывести значение получается только из старшего байта ADCH ... Когда ставлю хочу вывести значения 2-х байтового регистра ADCW, то вижу всевозможный "спам" вместо значений. Даташит перечитал, аж в глазах рябит ... Помогите, где ошибся?
Pink-Pank писал(а):ADMUX = 0x60; //0b01100000 - AVCC , ACD0
У Вас выравнивание по левому краю стоит. Бит ADLAR уберите - и выводите два регистра.
Убрал бит ADLAR ... Всё равно из ADCW сыпятся непонятные, всё время разные значения...
PS. нашёл ошибку. Дело в оглашении переменной функии unsigned char ADC_result(void) ... Тут как ни крути только 255 значений. Поменял на int - работает! Спасибо!
Только те, кто предпринимают абсурдные попытки, смогут достичь невозможного.
Продолжу задавать глупые вопросы
Пошёл дальше и решил сделать, чтобы результаты расчёта АЦП выводились в UART только по нажатии на кнопку (по прерыванию INT0). Мат часть изучил, код написал - не работает. Пляски с бубном пока не помогают. Прошу помощи у добрых котов...
//наши переменные
unsigned int res;
unsigned int volt;
unsigned char i;
// // Функция внешнего прерывания по INT0
interrupt [EXT_INT0] void ext_int0_isr(void)
{
i=1;
}
//Функция измерения
unsigned int ADC_result(void)
{
ADCSRA = 0x8E; //0b10001110 - предделитель на 64, прерывания разрешены, ADC включён
ADMUX = 0x40; //0b01000000 - AVCC , ACD0, ADLAR off
delay_us(30); //задержка для стабилизации
ADCSRA |= 0x40; //начинаем измерение
while((ADCSRA & 0x10)==0); //Ждём флаг окончания измерения
ADCSRA |=0x10;
return ADCW; //Возвращаем функции значение регистра ADCW
}
void main(void)
{
PORTB=0x00;
DDRB=0x00;
PORTC=0x00;
DDRC=0x00;
PORTD=0x04; // устанавливаем подтягивающий резистор на ногу PD2 (INT0)
DDRD=0x00;
// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud Rate: 9600 (при 8 МГц)
UCSRA=0x00;
UCSRB=0x18;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x33;
// Настройка прерываний:
GICR |= (1<<INT0); // разрешение внешнего прерывания по INT0
MCUCR |= (1<<ISC01); // прерывание по заднему фронту
#asm("sei")
while (1)
{
if (i)
{
res=ADC_result(); //значение измерения присваеваем переменной RES
volt=(float)res*0.0048828125*100; // 5 Вольт = 0,0048.. мВ/ед. Умножаем на 100 ддля точности до сотых.
printf("V=%u.%u ",volt/100,volt%100); // выводим значение в UART
delay_ms(50);
i=0;
};
}
}
Только те, кто предпринимают абсурдные попытки, смогут достичь невозможного.
Pink-Pank, огромное спасибо за внимание к моей "проблеме". Да, за volatile знаю, спасибо. Попробовал - увы, косяк где-то в другом... Возможно в инициализации прерывания, проверю.
Попутно другой вопрос (извините за мою любопытность) :
Шаг за шагом вдумчиво изучаю программирование и дописываю/изменяю программу, исследую новые возможности.
Теперь решил выводить значения ADC (АЦП) или же напряжения на светодиодный 7-сегментный индикатор.
Задачу поставил себе так, чтобы не было "мерцания" из-за меняющегося значения ADC.
Сначала думал делать замер нескольких значений и выводить среднее значение, но как-то не сраслось. Не понял как, знаний мало.
Выход из ситуации нашёл таким, что расчёт значений АЦП идёт по таймеру0 на частоте 8 кГц, а после идёт обработчик прерывания по таймеру2 на 100 кГц ... Результат устроил, в принципе. Мерцания нет.
Вопрос в том: как правильно делать? Хотелось, чтобы АЦП вычислялось раз в 0,2 сек (например) и затем выводились на индикатор. Как это сделать? В основном цикле, почему-то, вызывать функцию обработки АЦП и делать задержку не получается... Индикатор несёт ахинею...
DataLife писал(а):Попробовал - увы, косяк где-то в другом... Возможно в инициализации прерывания, проверю.
Инициализацию я смотрел - вроде все норм. Без кнопки-то пробовали? Выводит значения?
DataLife писал(а):Вопрос в том: как правильно делать? Хотелось, чтобы АЦП вычислялось раз в 0,2 сек (например) и затем выводились на индикатор.
Сделать можно по разному. Можно через таймеры, можно через прерывания от АЦП, а можно вообще без прерываний. У Вас проблема была скорее всего в объемности расчетов из-за которых МК не успевал вывести значение на экран. Попробуйте оптимизировать код перевода 2->10. Вы несколько раз делите на кратные числа. Можно, например, ввести промежуточную переменную и остаток скажем, от деления на 1000 потом использовать для вычисления остатка от деления на 100, поделив его еще на 10 и т.д. Плавающую точку тоже не желательно использовать - много ресурсов ядра ест.
Усреднение АЦП можно делать "скользящим окном" с хранением последней суммы и . Тогда для вычисления следующей суммы нужно всего лишь вычесть первый элемент и добавить вновь полученный. количество элементов лучше брать кратным 2, чтобы операцию деления потом заменить операцией сдвига. Например, 8 элементов, заместо деления суммы пишем S=S>>3;
P.S. Правильный тот вариант - который работает так, как задумано, а не тот, который более красиво написан.