АЦП на Atmega 8

Обсуждаем контроллеры компании Atmel.
Ответить
Родился
Сообщения: 5
Зарегистрирован: Чт фев 23, 2012 16:48:02

Сообщение I.Eisenstadt »

Господа, доброго времени суток. Пишу диплом, по причинам, не зависящим от меня, тему выдали поздно, и в результате штудировать учебники времени нет. А с Си и AVR я знаком до ужасного плохо. В чем соль. Нужно последовательно считывать данные с трех каналов АЦП и передавать их в UART. Но пока что мне не удается даже циклически отправлять значения с одного канала. Перепробовал кучу вариантов, приведу здесь тот, который мне кажется наиболее логичным. И насколько мне известно, механизм срабатывания прерываний такой же. Возникает прерывание - считыванием данные - отправляем - запускаем новое преобразование - ждем прерывания. Но у меня срабатывает только один раз. Предположение номер один - прерывания от UART и АЦП мешают друг другу. Предположение номер два - функция uart_putf() слишком большая для того, чтобы ее тыкать в прерывание. И в силу своей необразованности не могу ничего сделать. Пишу в AVR Studio. Может, поможете чем-нибудь? :dont_know:

Код: Выделить всё

#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)
    {
        
    }
}
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение 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));
        }
    }
}
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Вымогатель припоя
Аватара пользователя
Сообщения: 577
Зарегистрирован: Ср июн 19, 2013 08:10:48
Откуда: Москва, СПб, Липецк, Рязань

Сообщение ИС-пытатель »

Можете задействовать только одно прерывание - от АЦП. И по нему считывать результат и отправлять в UART. Единственное, нужно настроить UART на такую скорость, чтобы он успевал отправлять результаты между измерениями.

P.S. пипец у Вас темы для дипломов... На уровне детского сада... Где учитесь, если не секрет? Сделаю себе пометку, чтобы не отдавать сына в это учебное заведение. ;)
Родился
Сообщения: 5
Зарегистрирован: Чт фев 23, 2012 16:48:02

Сообщение 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);	//запуск нового преобразования
	};
}
Реклама
Эиком - электронные компоненты и радиодетали
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18785
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение 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-ом как каналом вывода, рекомендую использовать мои наработки :) все будет еще проще
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Родился
Сообщения: 5
Зарегистрирован: Чт фев 23, 2012 16:48:02

Сообщение I.Eisenstadt »

Насчет недоразумений - я более, чем уверен, что это так :))) Каждый учился по-разному, нам в универе на ассемблере MC 51 вбили, что прерывания - это хорошо и ими нужно разгружать процессор. У Шпака в "Программировании на языке С.." тоже натолкнулся на такие рекомендации. Поэтому и уперся. Более того, в ближайшей перспективе есть желание проверить режим подавления помех АЦП и что он дает. А насколько я понял из даташита, работает он по прерываниям, т.к. основной процессор уходит в сон. Зачем? Ну, чем точнее данные об угловом положении беспилотника (да, полная тема диплома - автомат стабилизации положения), тем лучше. По крайней мере, проверить точно стоит. Не станет лучше - сделаю по-Вашему, оно действительно проще и понятней. За UART спасибо, попробую у себя реализовать.
Реклама
Вымогатель припоя
Аватара пользователя
Сообщения: 577
Зарегистрирован: Ср июн 19, 2013 08:10:48
Откуда: Москва, СПб, Липецк, Рязань

Сообщение ИС-пытатель »

Прерывания не разгружают процессор. Они все лишь упрощают написание программы. Но не везде их использование целесообразно.

Измерения в спящем режиме предназначены для того, чтобы уменьшить влияние частоты ядра процессора на измерения. Считается, что уменьшает шумы. Хотя, если хорошо спроектировать плату и развязать аналоговую и цифровую часть - то в этом надобности практически никакой - проверено опытным путем. Разобраться с даташитом если с английским не айс - сложно. Скачайте себе Евстифеева. Справочник по Мега и Тини. Тот же даташит, только обобщенный и на русском.

В любом случае при измерении у Вас как минимум 1 мл. значащий разряд будет "плавать". Чтобы убрать это можно либо брать усреднение по нескольким измерениям (т.е. увеличить частоту выборок и усреднить), либо, если это невозможно по техническим причинам - сделать то же усреднение, только методом скользящего окна - когда у Вас берется среднее между текущей выборкой и несколькими прошлыми.
Информации в инете полно. Думаю, разберетесь.

А вообще, Вам еще надо будет про ПИД-регуляторы почитать - тоже пригодится для стабилизации.
Последний раз редактировалось ИС-пытатель Вт апр 08, 2014 13:12:31, всего редактировалось 1 раз.
Друг Кота
Аватара пользователя
Сообщения: 6339
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

Сообщение Jack_A »

ИС-пытатель писал(а):Прерывания не разгружают процессор. Они все лишь упрощают написание программы.
Ну я бы не так сформулировал. Прерывания позволяют распараллелить задачу, совместив работу периферии с вычислениями вместо тупого ожидания готовности или по задержкам. Конечно, при условии, что для вычисления не понадобятся результаты текущего замера - например, занимаемся обработкой предыдущего.
Ответить

Вернуться в «AVR»