Не могу насторить АЦП в ATmega

Вопросы настройки, программирования, прошивки микроконтроллеров и микросхем программируемой логики
Кот-тоК
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт окт 28, 2010 19:33:35

Не могу насторить АЦП в ATmega

Сообщение Кот-тоК »

Всем, привет! :))

Я в Proteus моделирую следующую простейшую задачку:
Микроконтроллер atmega16 принимает "данные" с аналогового датчика температуры, АЦП преобразовывает эти данные и они выводятся на дисплей. Но у меня естественно ничего не выводится, хотя с дисплеем я уже разобрался и он функционирует.

Изображение

Питание МК и на входе делителя +5В внешнее.
Произвел калибровку датчика там же в Proteus:

T, град. U, В (на выходе делителя)
0 ----- 3.67
25 ----- 3.92
50 ----- 4.17
75 ----- 4.42
100 ----- 4.67

Вот код, написанный мной. Там инициализация АЦП и прерывание по преобразованию данных.
Пишу в Code::Blocks, компилятор WinAVR.
Прошу прощения если там полный бред написан :oops:

Изображение

Конечно сперва, хотелось бы верно произвести инициализацию. :))
Вот, что написано в книге Шпака:

Изображение


Изображение

Как настраивал ADCSRA:
Изображение

Я взял коэффициент деления 64 поэтому ADPS0 = 0, ADPS1 = 1, ADPS2 = 1.
Частота которая стоит у проца сейчас 8Мгц, в реальном устройстве 11Мгц будет от внешнего кварцевого резонатора.
ADIE = 1.
У Шпака следующий бит называется ADFR, но в документации ADATE.
В документации написано, что 1 в этом бите включает какой-то триггер и преобразование числа в АЦП начинается по нарастающему фронту, чтобы его включить также надо еще что-то где-то выставить в 1....короче, здесь я поставил ADATE = 0.
ADSC = 1.
ADEN = 1.

В итоге ADCSRA = 0b11001110

Как настраивал регистр ADMUX:
Изображение

Датчик висит на ноге PA0 МК, поэтому MUX0..4 = 00000.
Внешний источник питания REFS1 = 0, REFS2 = 1.
Левое выравнивание преобразованных значений ADLAR = 0.

т.е ADMUX = 0b01000000

Так теперь о получении данных, они, как я понял, хранятся в регистрах ADCH и ADCL.

Все свои переменные я определил как unsigned char. Вычисления я провожу прямо в прерывании, это ,вроде бы, не очень хорошо??
Программе приходится ждать пока все вычислится, а так она могла дальше что-то делать.

Вычисления провожу по формуле:

Vin=(ADCH*256+ADCL)*Vref/1024

T=Vin*0,01

Vin - Напряжение на входе АЦП
Vref - Напряжение на входе делителя (напряжение питания)
T - температура
0,01 я получил из калибровки датчика (4.67 - 3.67)/100 т.е повышение температуры на 1 градус соответствует повышению напряжения на 0.01 В.

Не работает!!!!!! Что делать??? Может преобразование типов переменных не верное?
И еще как запустить преобразование в АЦП повторно? сбрасывать ADIF в ноль??
Помогите!!!!!! :)) :)) :))
Кот-тоК
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт окт 28, 2010 19:33:35

Re: Не могу насторить АЦП в ATmega

Сообщение Кот-тоК »

Не научился я отлаживать программы ни в Code::Blocks ни в Protus.
Повыводил значения регистров ADCH и ADCL на светодиоды:
ADCH = 0x00
ADCL = 0x01

Меняю температуру значения не меняются. Не удивительно, что на экран ничего не выводится, по таким адресам там нет символов.
:dont_know: :dont_know: :dont_know: :dont_know: :dont_know: :dont_know:

Прерывания выполняются, тоже по светодиодам проверял.

К стати, узнал что для начала повторного преобразования значений нужно ADSC установить в 1 :)
Vov123
Опытный кот
Сообщения: 804
Зарегистрирован: Чт мар 12, 2009 16:31:05

Re: Не могу насторить АЦП в ATmega

Сообщение Vov123 »

Вместо подтягивающего резистора на 10К,установите PULLUP.
avrman
Первый раз сказал Мяу!
Сообщения: 24
Зарегистрирован: Пт окт 01, 2010 20:17:58
Контактная информация:

Re: Не могу насторить АЦП в ATmega

Сообщение avrman »

подозреваю что это глюк самого протеуса, так как тоже пробовал выводить значения ацп меги32 на жки, только сигнал заводил с резистивного делителя. Ничего кроме нуля протеус на дисплей не выводил, собрал в железе, прекрасно работает.
Vov123
Опытный кот
Сообщения: 804
Зарегистрирован: Чт мар 12, 2009 16:31:05

Re: Не могу насторить АЦП в ATmega

Сообщение Vov123 »

Посмотрите пожалуйста,сейчас не глючит?И если не трудно сообщите.
Вложения
2.rar
(29.98 КБ) 246 скачиваний
Кот-тоК
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт окт 28, 2010 19:33:35

Re: Не могу насторить АЦП в ATmega

Сообщение Кот-тоК »

Всем, доброе утро!!! :) :) :)
Спасибо за ответы!

Vov123 писал(а):Посмотрите пожалуйста,сейчас не глючит?И если не трудно сообщите.


Запустил Ваш проект и..... всё работает как надо :) :) :) Меняешь температуру меняются значения на дисплее (конечно они пока не откалиброваны). Так что это проблема в неправильно написанной мной программе :dont_know: :dont_know: :dont_know:

Расскажите пожалуйста про PULLUP резистор. И может ещё есть какие-то секреты настройки АЦП. :write:
Vov123
Опытный кот
Сообщения: 804
Зарегистрирован: Чт мар 12, 2009 16:31:05

Re: Не могу насторить АЦП в ATmega

Сообщение Vov123 »

PULLUP для симуляции в Проте,а в живую нужно подбирать самому.Возможно ваш R10K будет вполне пригоден.Вот вам мой проектик,переделанный под ваши нужды,я думаю разберётесь сами.
Вложения
Потенциометр и АЦП.rar
(85.22 КБ) 261 скачивание
Кот-тоК
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт окт 28, 2010 19:33:35

Re: Не могу насторить АЦП в ATmega

Сообщение Кот-тоК »

Спасибо, спасибо, спасибо! :))
Очень хочется прямо сейчас глянуть, но надо по делам уходить. Вернусь обязательно проверю и сообщу результат. :beer:
stas00n
Вымогатель припоя
Сообщения: 557
Зарегистрирован: Пн мар 23, 2009 04:03:45

Re: Не могу насторить АЦП в ATmega

Сообщение stas00n »

Кот-тоК писал(а):Все свои переменные я определил как unsigned char...
...
Не работает!!!!!! Что делать??? Может преобразование типов переменных не верное?


Ваш код по идее не должен работать правильно; T будет всегда 0; кстати компилятор не ругается на значение 0,00005 ?

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

unsigned char a,b,T;
a = ADCH;
b = ADCL;
T = (a*256+b)*0,00005;

Кроме того, желательно считывать сначала ADCL, а потом ADCH.

Кот-тоК писал(а):Вычисления я провожу прямо в прерывании, это ,вроде бы, не очень хорошо??
Программе приходится ждать пока все вычислится, а так она могла дальше что-то делать.

В прерывании надо только сохранить результат преобразования, а вычисления и вывод на дисплей делать в основном цикле
Кот-тоК писал(а):И еще как запустить преобразование в АЦП повторно? сбрасывать ADIF в ноль??
Помогите!!!!!! :)) :)) :))

Считать ADCL, ADCH; сбросить ADIF; установить ADSC.
Кот-тоК писал(а):Вычисления провожу по формуле:

Vin=(ADCH*256+ADCL)*Vref/1024

T=Vin*0,01

Vin - Напряжение на входе АЦП
Vref - Напряжение на входе делителя (напряжение питания)
T - температура
0,01 я получил из калибровки датчика (4.67 - 3.67)/100 т.е повышение температуры на 1 градус соответствует повышению напряжения на 0.01 В.

Немного странный подход. Для линейной зависимости я обычно составляю примерно такую табличку:

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

Температура|Отсчет АЦП =1024*Vin/Vref
----------
       0          |                  751
     100        |                  956

Для вычисления температуры составляем линейное уравнение:

t = a * ADC + b

подставляем данные из таблицы и решаем простейшую систему

751a + b = 0;
956a + b = 100;
получаем a = 0,488; b = -366.

Чтобы в программе не работать с плавающей математикой, коэффициент а лучше представить в виде двоичной дроби:
0,488 = (125/256);

Я бы сделал примерно так:

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

#define TEMP_MULTIPLIER  125   //MULTIPLIER*256
#define TEMP_OFFSET      -366


volatile unsigned int adcResult     = 0;
static unsigned char temperatura    = 0;

void main (void){
unsigned long t;            //Вспомогательная переменная
//Other code here
ADCSRA &= ~(1<<ADIE);       //Запрет прерывания от АЦП
                            //для правильного вычисления
t = ((adcResult * TEMP_MULTIPLIER)>>8) + TEMP_OFFSET;
ADCSRA |= (1<<ADIE);        //Разрешить прерывания
temperatura = (unsigned char) t;
//Other code here
}


void interrupt ADC_Interrupt (void){
adcResult   = (ADCH<<8) + ADCL;
ADCSRA      &= ~(1<<ADIF);
ADCSRA      |= (1<<ADSC);
}


За работоспособность не ручаюсь, но принцип примерно такой.
Кот-тоК
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт окт 28, 2010 19:33:35

Re: Не могу насторить АЦП в ATmega

Сообщение Кот-тоК »

:idea:
Последний раз редактировалось Кот-тоК Вс дек 12, 2010 01:42:48, всего редактировалось 3 раза.
Кот-тоК
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт окт 28, 2010 19:33:35

Re: Не могу насторить АЦП в ATmega

Сообщение Кот-тоК »

Ну что же, результат на данный момент следующий:

регистры ADCL заполняется правильно :)
Их значения передаю на мониторчик, там загорается символ который в пересчете правильно определяет температуру.
Т.е. если у меня температура 52 то на экране загорается #. Напряжение на входе при этом 3,92.
По формуле ADC=Vin*1024/Vref получаю что ADC = 803 в бинарном виде 00100011, что соответствует в таблице символов экрана символу #.
Надеюсь, что ADCH тоже верно заполняется, не проверял. :)

Заработало после правки кода по примерам и обязательной замены обычного резистора на PULLUP резистор. :)

Но вот с переводом того, что лежит в этих регистрах в градусы Цельсия проблемы. Пытался сам, что-либо выдумать, но без деления не получается.

stas00n писал(а):Для вычисления температуры составляем линейное уравнение:

t = a * ADC + b


Никак не могу понять откуда линейные уравнения и как вам удалось деления избежать.
Извиняюсь за своё скудоумие :)))
Кот-тоК
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт окт 28, 2010 19:33:35

Re: Не могу насторить АЦП в ATmega

Сообщение Кот-тоК »

Кот-тоК писал(а):stas00n писал(а):
Для вычисления температуры составляем линейное уравнение:

t = a * ADC + b


Никак не могу понять откуда линейные уравнения ....


Вот же я выдал :)) :)) :))

Изображение

Из калибровки оно и есть :)) :)) :))

ААААааа!!!Всё, понял!!! как надо переводить, сейчас попробую :idea:
stas00n
Вымогатель припоя
Сообщения: 557
Зарегистрирован: Пн мар 23, 2009 04:03:45

Re: Не могу насторить АЦП в ATmega

Сообщение stas00n »

Кот-тоК писал(а):Никак не могу понять откуда линейные уравнения и как вам удалось деления избежать.
Извиняюсь за своё скудоумие :)))

Линейная функция - из школьного курса алгебры - функция вида y = ax + b :)
в нашем случае -
t = a * ADC + b;
Подставив известные значения вместо t и ADC для двух калибровочных точек получим систему из двух линейных уравнений:

751a + b = 0;
956a + b = 100;

выразим b через a из первого уравнения:

b = -751a

и подставим во второе:

956a - 751a = 100;
205a = 100;
a = 100/205 = 0,488;

потом находим b

b = -751a;
b = -751 * 0,488 = -366.


Получаем формулу для расчета температуры:

t = 0,488 * ADC - 366;

:))) :)))

Чтобы легче было помножить на 0,488, делаем фокус - умножаем например, на 256/256:

t = 0,488 * 256 * ADC / 256 - 366 = 125 * ADC / 256 - 366
Деление на 256 в контроллере суть логический сдвиг вправо на 8 разрядов, то бишь помножив отсчет АЦП на 125 и отбросив младший байт результата получим 0,488 * ADC; дальше банально вычитаем 366 - получаем искомую температуру в градусах.
При таком вычислении нужно учесть - промежуточный результат 125 * 1023 = 127875 > 65535, т.е. в переменную типа unsigned int уже не влезет, - нужна переменная long int или unsigned long int
Кот-тоК
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт окт 28, 2010 19:33:35

Re: Не могу насторить АЦП в ATmega

Сообщение Кот-тоК »

Да, я ступил! :)) :)) :))
Бывает!!!

Значит так:
Из-за вот этой строчки в прерывании

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

adcResult   = (ADCH<<8) + ADCL;


Proteus сильно тупил, и писал что в регистры ADC не успевает ничего записываться.
Перенес её в main - заработало. :))

Осталось, надеюсь, последнее препятствие: перевести полученное значение температуры из unsigned char в строку.
Функцию sprintf никак не могу подрубить :dont_know:

Кстати, вот эта фишка очень крута оказалась.

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

t = ((adcResult * TEMP_MULTIPLIER)>>8) + TEMP_OFFSET;


Я имею ввиду a>>8 вместо деления на 256. Очень понравилось, не знал такого, теперь запомню. :))
stas00n
Вымогатель припоя
Сообщения: 557
Зарегистрирован: Пн мар 23, 2009 04:03:45

Re: Не могу насторить АЦП в ATmega

Сообщение stas00n »

Кот-тоК писал(а):Да, я ступил! :)) :)) :))
Бывает!!!

Значит так:
Из-за вот этой строчки в прерывании

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

adcResult   = (ADCH<<8) + ADCL;


Proteus сильно тупил, и писал что в регистры ADC не успевает ничего записываться.
Перенес её в main - заработало. :))

Дык нельзя ее в main переносить! На то она и в прерывании, чтобы сохранить результат, освободить ацп, а дальше в мэйне делать с этим результатом что заблагорассудится. Лучше вынесите повторный старт преобразования из прерывания в основной цикл, после вывода на дисплей, - зачем ацп лишние телодвижения пока предыдущий результат не обработан. Кстати, CVAVR генерит на эту строчку просто адский код - реально в цикле восьмикратно прокручивает ADCH! :shock: вместо того, чтобы тупо сохранить. Надо как-то по другому делать или в компиляторе что-то настроить.
Кот-тоК писал(а):
Кстати, вот эта фишка очень крута оказалась.

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

t = ((adcResult * TEMP_MULTIPLIER)>>8) + TEMP_OFFSET;


Я имею ввиду a>>8 вместо деления на 256. Очень понравилось, не знал такого, теперь запомню. :))

Умножение/деление на 2^n делается сдвигом на n бит влево/вправо соответственно.
Кот-тоК
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт окт 28, 2010 19:33:35

Re: Не могу насторить АЦП в ATmega

Сообщение Кот-тоК »

stas00n писал(а):Дык нельзя ее в main переносить! На то она и в прерывании, чтобы сохранить результат, освободить ацп, а дальше в мэйне делать с этим результатом что заблагорассудится. Лучше вынесите повторный старт преобразования из прерывания в основной цикл, после вывода на дисплей, - зачем ацп лишние телодвижения пока предыдущий результат не обработан. Кстати, CVAVR генерит на эту строчку просто адский код - реально в цикле восьмикратно прокручивает ADCH! :shock: вместо того, чтобы тупо сохранить. Надо как-то по другому делать или в компиляторе что-то настроить.


Да, да! Я понимаю, что её просто так нельзя переносить. Я немного не точно выразился.
Вот код который написал, он во многом сходен с примером, но куда деваться :)) Главное всё понять.

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

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "LCM.h"
#include "ADC.h"

#define k 202
#define b -580

unsigned char ADCresult1 = 0, ADCresult2 = 0;
unsigned char temperatura = 0;

void main()
{

  unsigned long t;

  DDRC = 0xFF;
  DDRD = 0xFF;
  PORTC = 0;
  PORTD = 0;

  LCMinit();

  ADCinit();

  SREG |= (1 << 7);

  while(1)
  {
    ADCSRA |= (0 << 3);
   
    t = ((((ADCresult2 << 8) + ADCresult1) * k) >> 8) + b;
   
    temperatura = (unsigned char) t;

    SetLCMPosition(1,0);
    ShowChar(temperatura);

    ADCSRA |= (1 << 3);
  }


}


// Инициализация АЦП
void ADCinit()
{

    ADMUX |= (1 << 6);  //АЦП работает от Vcc, прием данных по A0
    ADCSRA |= (1 << 7) | (1 << 6) | (1 << 3) |  (1 << 2) | (1 << 1);

}

ISR (SIG_ADC)
{
    ADCresult1 = ADCL;
    ADCresult2 = ADCH;


    ADCSRA |= (0 << 4);
    ADCSRA |= (1 << 6);
}


Я пытался переносить

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

    
    ADCSRA |= (0 << 4);
    ADCSRA |= (1 << 6);


в main, не фурычит. Он продолжает штамповать прерывания без остановки. Как так?? :dont_know: :dont_know: :dont_know: :dont_know:
stas00n
Вымогатель припоя
Сообщения: 557
Зарегистрирован: Пн мар 23, 2009 04:03:45

Re: Не могу насторить АЦП в ATmega

Сообщение stas00n »

Кот-тоК писал(а):Proteus сильно тупил, и писал что в регистры ADC не успевает ничего записываться.

Проверьте, какой код в прерывании получается при компиляции? Возможно тоже имеет место быть попытка повторного чтения регистров результата АЦП, а на это контроллер будет ругаться аппаратно, т.к. последний результат уже считан, а новый еще не получен. Тогда наверное имеет смысл добавить временные переменные:

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

interrupt [ADC_INT] void Adc_Interrupt_Handler(void){
unsigned char a, b;
a = ADCL;
b = ADCH;
adcResult   = (b<<8) + a;
//TODO: Other code here
}
stas00n
Вымогатель припоя
Сообщения: 557
Зарегистрирован: Пн мар 23, 2009 04:03:45

Re: Не могу насторить АЦП в ATmega

Сообщение stas00n »

Кот-тоК писал(а):
Я пытался переносить

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

    
 ADCSRA |= (0 << 4);                   //Этот код ничего не делает!!!
    ADCSRA |= (1 << 6);


в main, не фурычит. Он продолжает штамповать прерывания без остановки. Как так?? :dont_know: :dont_know: :dont_know: :dont_know:


Запись ADCSRA |= (0 << 4) делает побитовое "ИЛИ" с нулем! То есть ничего! Флаг прерывания не сбрасывается! Чтобы сбросить бит нужна операция "И":

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

ADCSRA &= ~(1 << 4)

А вместо цифр нужно писать название битов (они ведь продефайнены в h-файле):

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

ADCSRA &= ~(1 << ADIF)
Кот-тоК
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт окт 28, 2010 19:33:35

Re: Не могу насторить АЦП в ATmega

Сообщение Кот-тоК »

Пока программа которую я приводил верно симулируется в Proteus. Конечно не всё так радужно, он пишет, что не может в режиме реального времени симулировать, т.к. проц перегружен. Хотя если использовать тот проект который мне дал Vov123, то у него всё слету нормально работает.
У меня же всё как обычно не просто :?

Когда вот так вот записал

stas00n писал(а):Код:
interrupt [ADC_INT] void Adc_Interrupt_Handler(void){
unsigned char a, b;
a = ADCL;
b = ADCH;
adcResult = (b<<8) + a;
//TODO: Other code here
}


стало нормально симулироваться, только Proteus все-равно пишет, что в реальном времени не может симулировать т.к. проц перегружен.

И проблема с переводом числа в строку тоже не решена...
Кот-тоК
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт окт 28, 2010 19:33:35

Re: Не могу насторить АЦП в ATmega

Сообщение Кот-тоК »

stas00n писал(а):Запись ADCSRA |= (0 << 4) делает побитовое "ИЛИ" с нулем! То есть ничего! Флаг прерывания не сбрасывается! Чтобы сбросить бит нужна операция "И":
Код:
ADCSRA &= ~(1 << 4)


Вот я программист-то :)) :)) :)) Действительно, ваша правда, сейчас поправлю.
Закрыто

Вернуться в «Микроконтроллеры и ПЛИС»