Страница 1 из 1
АЦП на Atmega 8
Добавлено: Вт апр 08, 2014 00:00:28
I.Eisenstadt
Господа, доброго времени суток. Пишу диплом, по причинам, не зависящим от меня, тему выдали поздно, и в результате штудировать учебники времени нет. А с Си и AVR я знаком до ужасного плохо. В чем соль. Нужно последовательно считывать данные с трех каналов АЦП и передавать их в UART. Но пока что мне не удается даже циклически отправлять значения с одного канала. Перепробовал кучу вариантов, приведу здесь тот, который мне кажется наиболее логичным. И насколько мне известно, механизм срабатывания прерываний такой же. Возникает прерывание - считыванием данные - отправляем - запускаем новое преобразование - ждем прерывания. Но у меня срабатывает только один раз. Предположение номер один - прерывания от UART и АЦП мешают друг другу. Предположение номер два - функция uart_putf() слишком большая для того, чтобы ее тыкать в прерывание. И в силу своей необразованности не могу ничего сделать. Пишу в AVR Studio. Может, поможете чем-нибудь?
Код: Выделить всё
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/signal.h>
#include "uart.h"
float ADCData;
#define F_CPU 8000000UL
#define UART_BAUD_RATE 9600
ISR(SIG_ADC)
{
ADCData = ADCW; //чтение показаний АЦП
uart_putf(ADCData); //отправка по UART
ADCSR = (1<<ADSC); //запуск нового преобразования
}
int main(void)
{
uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
ADMUX = 0; //выбор канала ADC0
ADCSR = 0xCE; //настройка АЦП, запуск преобразования
sei();
while(1)
{
}
}
Re: АЦП на Atmega 8
Добавлено: Вт апр 08, 2014 08:22:59
ARV
не надо мудрить: алгоритм решения вашей задачи до смешного прямолинеен: в цикле считываем канал АЦП и отправляем результат - никаких прерываний. вот канва кода:
Код: Выделить всё
static uint16_t get_adc(uint8_t chanel); // в этой функции ведется включение канала, запуск АЦП, ожидание конца замера и возврат результата
int main(void){
uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
while(1){
for(uint8_t i=0; i<3; i++){
uart_putf(get_adc(i));
}
}
}
Re: АЦП на Atmega 8
Добавлено: Вт апр 08, 2014 08:32:34
ИС-пытатель
Можете задействовать только одно прерывание - от АЦП. И по нему считывать результат и отправлять в UART. Единственное, нужно настроить UART на такую скорость, чтобы он успевал отправлять результаты между измерениями.
P.S. пипец у Вас темы для дипломов... На уровне детского сада... Где учитесь, если не секрет? Сделаю себе пометку, чтобы не отдавать сына в это учебное заведение.

Re: АЦП на Atmega 8
Добавлено: Вт апр 08, 2014 11:44:46
I.Eisenstadt
Кажется, я выспался и разобрался. Проблема была действительно в том. что обработчик прерывания для UART (а он используется в функции uart_putf() ), вложенный в обработчик прерывания АЦП - плохая идея. В общем, функцию-то написал, а как это должно работать, не продумал. Если в основной программе отправлять данные, прочитанные в переменные, то, вроде бы, все нормально.
ARV, так-то да, и у меня что-то подобное по смыслу даже вышло поначалу. Но хотелось же на прерываниях сделать.
ИС-пытатель, в харьковском авиационном. А я разве называл тему диплома и полную задачу?:)
Код: Выделить всё
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/signal.h>
#include <avr/delay.h>
#include "uart.h"
#define F_CPU 8000000UL
#define UART_BAUD_RATE 9600
float ADCDataX;
float ADCDataY;
int i;
ISR(SIG_ADC)
{
switch(ADMUX)
{
case 0:
ADCDataX = ADCW; //чтение показаний АЦП
ADMUX = 1; //переключение канала АЦП
ADCSR = ADCSR| (1<<ADSC); //запуск нового преобразования
break;
case 1:
ADCDataY = ADCW; //чтение показаний АЦП
ADMUX = 0; переключение канала АЦП
}
}
int main(void)
{
uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
ADMUX = 0; //выбор канала ADC0
ADCSR = 0xCE; //настройка АЦП, запуск преобразования
sei();
while(1)
{
uart_puts("X: ");
uart_putf(ADCDataX);
uart_puts(" Y: ");
uart_putf(ADCDataY);
uart_puts(" \n"); //Отправка данных
ADCSR = ADCSR| (1<<ADSC); //запуск нового преобразования
};
}
Re: АЦП на Atmega 8
Добавлено: Вт апр 08, 2014 12:03:49
ARV
сдается мне, что работает у вас исключительно по недоразумению

насколько я могу полагаться на собственное знание avr-gcc, у вас отключена оптимизация - и только по этой причине код работоспособен

и даже то, как он работоспосбен, будет для вас источником граблей в будущем - хотя бы по тому, что запуск преообразования вы делаете и из прерываний, и из основного цикла.
если у вас нет особых причин использовать прерывания, я продолжу рекомендовать мой подход

Код: Выделить всё
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
//#include <avr/signal.h> не надо этого!!!!
#include <avr/delay.h>
#include "uart.h"
#define F_CPU 8000000UL /* ЭТО ДОЛЖНО ДЕЛАТЬСЯ НЕ ЗДЕСЬ, А В ГЛОБАЛЬНЫХ СВОЙСТВАХ ПРОЕКТА */
#define UART_BAUD_RATE 9600
float ADCDataX;
float ADCDataY;
int i;
static float get_adc(uint8_t chanel){
ADMUX = (ADMUX & 0xF8) | (chanel & 0x07);
ADCSRA |= _BV(ADSC);
while(bit_is_set(ADCSRA, ADSC));
return ADC;
}
int main(void)
{
uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
ADMUX = 0; //выбор канала ADC0
ADCSR = 0xCE; //настройка АЦП, запуск преобразования
while(1){
uart_puts("X: ");
ADCDataX = get_adc(0);
uart_putf(ADCDataX);
uart_puts(" Y: ");
ADCDataY = get_adc(1);
uart_putf(ADCDataY);
uart_puts(" \n");
}
}
по-моему, так существенно понятнее и безграбельнее
и вообще, если вы работаете с UART-ом как каналом вывода, рекомендую
использовать мои наработки 
все будет еще проще
Re: АЦП на Atmega 8
Добавлено: Вт апр 08, 2014 12:26:11
I.Eisenstadt
Насчет недоразумений - я более, чем уверен, что это так

Каждый учился по-разному, нам в универе на ассемблере MC 51 вбили, что прерывания - это хорошо и ими нужно разгружать процессор. У Шпака в "Программировании на языке С.." тоже натолкнулся на такие рекомендации. Поэтому и уперся. Более того, в ближайшей перспективе есть желание проверить режим подавления помех АЦП и что он дает. А насколько я понял из даташита, работает он по прерываниям, т.к. основной процессор уходит в сон. Зачем? Ну, чем точнее данные об угловом положении беспилотника (да, полная тема диплома - автомат стабилизации положения), тем лучше. По крайней мере, проверить точно стоит. Не станет лучше - сделаю по-Вашему, оно действительно проще и понятней. За UART спасибо, попробую у себя реализовать.
Re: АЦП на Atmega 8
Добавлено: Вт апр 08, 2014 13:06:44
ИС-пытатель
Прерывания не разгружают процессор. Они все лишь упрощают написание программы. Но не везде их использование целесообразно.
Измерения в спящем режиме предназначены для того, чтобы уменьшить влияние частоты ядра процессора на измерения. Считается, что уменьшает шумы. Хотя, если хорошо спроектировать плату и развязать аналоговую и цифровую часть - то в этом надобности практически никакой - проверено опытным путем. Разобраться с даташитом если с английским не айс - сложно. Скачайте себе Евстифеева. Справочник по Мега и Тини. Тот же даташит, только обобщенный и на русском.
В любом случае при измерении у Вас как минимум 1 мл. значащий разряд будет "плавать". Чтобы убрать это можно либо брать усреднение по нескольким измерениям (т.е. увеличить частоту выборок и усреднить), либо, если это невозможно по техническим причинам - сделать то же усреднение, только методом скользящего окна - когда у Вас берется среднее между текущей выборкой и несколькими прошлыми.
Информации в инете полно. Думаю, разберетесь.
А вообще, Вам еще надо будет про ПИД-регуляторы почитать - тоже пригодится для стабилизации.
Re: АЦП на Atmega 8
Добавлено: Вт апр 08, 2014 13:12:06
Jack_A
ИС-пытатель писал(а):Прерывания не разгружают процессор. Они все лишь упрощают написание программы.
Ну я бы не так сформулировал. Прерывания позволяют распараллелить задачу, совместив работу периферии с вычислениями вместо тупого ожидания готовности или по задержкам. Конечно, при условии, что для вычисления не понадобятся результаты текущего замера - например, занимаемся обработкой предыдущего.