Перейти к содержанию
    

Собираю терморегулятор. Готова динамическая индикация (спасибо форумчанам). Теперь проблема с АЦП.

/*****************************************************

Chip type : ATmega16

Clock frequency : 1,000000 MHz

*****************************************************/

#include <mega16.h>

char Dig[10]; // Массив, с кодами чисел для индикатора с общим анодом

char i=0; // счетчик для переключения анодов

char e=0; // единицы установленной температуры

char d=0; // десятки

char s=0; // сотни

char s2=0; // единицы измеренной температуры

char e2=0; // десятки

char d2=0; // сотни

char res=0; // в эту переменную поочереди записываются e,d,s или e2,d2,s2

char keys_prev=0; // две переменной для правильной работы кнопок

char keys_now=0; //

unsigned int current_temp=0; // измеренная температура

short temp2=0; // временная переменная

short set_temp=0; // установленная температура

 

 

char num (void) { // функция, для опроса клавиатуры, и вывода на катоды кода текущего разряда

if (PINB.4!=0){ // если кнопка "нагрев" не нажата, то

if ((keys_prev!=keys_now)&(PINB.0==0)) e++; // если нажата кнопка "единицы", то прибавить 1 к нулевому разряду (единицы)

if (e==10) e=0; // если результат прибавления перевалил за 9, то сбросить его в нуль

if ((keys_prev!=keys_now)&(PINB.1==0)) d++; // если нажата кнопка "десятки", то прибавить 1 к первому разряду (десятки)

if (d==10) d=0; // если результат прибавления перевалил за 9, то сбросить его в нуль

if ((keys_prev!=keys_now)&(PINB.2==0)) s++; // если нажата кнопка "сотни", то прибавить 1 к второму разряду (сотни)

if (s==3) s=0; // если результат прибавления перевалил за 9, то сбросить его в нуль

if (PINB.3==0) { // если нажата кнопка "сброс", то сбросить все разряды в нуль

e=0;

d=0;

s=0;}

switch (i) { // в зависимости от переменной i выбираем что показываем в текущий момент: единицы, десятки, или сотни

case 0:

res=e;

break;

case 1:

res=d;

break;

case 2:

res=s;

break; }

return res;}

else { // если нажата кнопка "нагрев", то

 

current_temp=ADCL; // в переменную записываем значение из регистра ADCL

current_temp+=((int)ADCH << 8); // прибавляем к переменной значение из регистра ADCH со сдвигом влево на 8 разрядов

current_temp=current_temp/2.5; // преобразовываем (ADCH ADCL) в температуру. Т.к. используется внутренний ИОН 2560 мВ, то 2560/1024=2.5

e2=current_temp%10; // получаем единицы измеренной температуры

temp2=current_temp/10; // промежуточная операция, для понижения степени

d2=temp2%10; // получаем десятки измеренной температуры

s2=temp2/10; // получаем сотни измеренной температуры

// в зависимости от переменной i выбираем что показываем в текущий момент времени

switch (i) {

case 0:

res=e2;

break;

case 1:

res=d2;

break;

case 2:

res=s2;

break; }

return res;

 

 

;}}

 

// Timer 0 output compare interrupt service routine

interrupt [TIM0_COMP] void timer0_comp_isr(void) //обработка прерывания таймера по совпадению с OCR0

{

keys_prev=keys_now; //для клавиш

keys_now=PINB; //читаем что нажато

if (i==3) i=0; //если показатель текущего разряда перевалил за 2, то зажигаем нулевой разряд

PORTC=Dig[num()]; //выставляем на катодах число, которое получаем из функции num()

switch (i) { //в зависимости от переменной i поочередно подаем питание на аноды

case 0: //если i=0, то включаем разряд для единиц

PORTD.1=1; //гасим ненужные разряды

PORTD.2=1;

PORTD.0=0; //включаем нулевой разряд, активный уровень - 0,т.к. используются p-n-p

break;

case 1: // и т.д.

PORTD.0=1;

PORTD.2=1;

PORTD.1=0;

break;

case 2:

PORTD.0=1;

PORTD.1=1;

PORTD.2=0;

break;}

i++;

 

}

 

// Declare your global variables here

 

void main(void)

{

PORTA=0x00;

DDRA=0x00;

PORTB=0xFF;

DDRB=0x00;

PORTC=0x00;

DDRC=0xFF;

PORTD=0x00;

DDRD=0xFF;

 

// Timer/Counter 0 initialization

// Clock source: System Clock

// Clock value: 0,977 kHz

// Mode: Normal top=FFh

// OC0 output: Disconnected

TCCR0=0x0D;

TCNT0=0x00;

OCR0=0x03; //задаем частоту развертки. подбирал наглаз.

 

// Timer/Counter 1 initialization

TCCR1A=0x00;

TCCR1B=0x00;

TCNT1H=0x00;

TCNT1L=0x00;

ICR1H=0x00;

ICR1L=0x00;

OCR1AH=0x00;

OCR1AL=0x00;

OCR1BH=0x00;

OCR1BL=0x00;

 

// Timer/Counter 2 initialization

ASSR=0x00;

TCCR2=0x00;

TCNT2=0x00;

OCR2=0x00;

 

// External Interrupt(s) initialization

MCUCR=0x00;

MCUCSR=0x00;

 

// Timer(s)/Counter(s) Interrupt(s) initialization

TIMSK=0x02;

 

// Analog Comparator initialization

// Analog Comparator: Off

// Analog Comparator Input Capture by Timer/Counter 1: Off

ACSR=0x80;

SFIOR=0x00; // free running режим АЦП

ADMUX=0xC0; // Внутренний источник опорного напряжения 2.56В REFS1..0=1; правое выравнивание ADLAR=0,вход ADC0

ADCSRA=0xE4; //ADEN=1(включили АЦП),ADSC=1(начали преобразование),ADATE:1(авто триггер включен),ADIE=0(прерывания от АЦП нам не нужны), делитель на 16

 

// заполняем массив комбинациями нулей/единиц для катодов

Dig[0] = 0xC0;

Dig[1] = 0xF9;

Dig[2] = 0xA4;

Dig[3] = 0xB0;

Dig[4] = 0x99;

Dig[5] = 0x92;

Dig[6] = 0x82;

Dig[7] = 0xF8;

Dig[8] = 0x80;

Dig[9] = 0x90;

 

#asm("sei") // включаем прерывания

while (1)

{

if (PINB.4==0) { // если нажата кнопка "нагрев", то

set_temp=e+d*10+s*100; // вычисляем установленную температуру, исходя из того, что в переменных e,d,s

if (current_temp<(set_temp-1)) {PORTD.3=1;}; //если текущая температура ниже установленной на 1 градус, то включаем релюшку

if (current_temp>=set_temp) {PORTD.3=0;};} //если текущая температура больше или равна установленной, то выключаем релюшку

else {PORTD.3=0;}; }; //если кнопка "нагрев" не нажата, то реле должно быть выключенным.

} // game over

 

почему-то в Proteus переменные e2,s2,d2 неправильно считаются. Залил в мегу - на индикаторе 409. Когда использовал прерывание АЦП, то у меня все работало. Но хочется упростить немного код (пусть хоть потребляет больше)

Схемку прилагаю.

 

Подскажите что не так, я уже не знаю чего делать.

post-37091-1217419101_thumb.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

почему-то в Proteus переменные e2,s2,d2 неправильно считаются. Залил в мегу - на индикаторе 409.

А сколько должно быть?

 

Смущает вот что:

 

current_temp=current_temp/2.5; // преобразовываем (ADCH ADCL) в температуру. Т.к. используется внутренний ИОН 2560 мВ, то 2560/1024=2.5

 

Во-первых, current_temp у вас unsigned int, то есть это эквивалентно

current_temp=current_temp/2.

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

 

ЗЫ. Для форматирования кода - кнопочка "код", а не "цитата":)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

current_temp=current_temp/2.5; // преобразовываем (ADCH ADCL) в температуру. Т.к. используется внутренний ИОН 2560 мВ, то 2560/1024=2.5

то есть 1 дискрет АЦП = 2,5 мВ.

Причём тогда тут деление?

Наверно, умножить надо? Получим Uadc в милливольтах

ещё такая штучка:

x*2.5=(x<<1)+(x>>1)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Протеус 7.2 .2 с авр работает глючно!!!

Мегу 16 не проверял, а 8535 и тиньки - глюк, особенно что касаемо АЦП.

По заверениям разработчиков в версии 7.3 глюки обещали исправить.

Ждем-с.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Подскажите что не так, я уже не знаю чего делать

У вас на самом деле последовательно с транзисторами стоят сопротивления 1 Ом? Не боитесь попалить МК и индикатор?

 

Последовательно с диодом D1 надо бы включить сопротивление, а то и здесь всё попалите.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

У вас на самом деле последовательно с транзисторами стоят сопротивления 1 Ом? Не боитесь попалить МК и индикатор?

 

Последовательно с диодом D1 надо бы включить сопротивление, а то и здесь всё попалите.

 

 

нет, номиналы там от балды. Не от балды - катушка, емкости, и резистор к питанию АЦП

 

Протеус 7.2 .2 с авр работает глючно!!!

Мегу 16 не проверял, а 8535 и тиньки - глюк, особенно что касаемо АЦП.

По заверениям разработчиков в версии 7.3 глюки обещали исправить.

Ждем-с.

 

АЦП в протеусе у меня работает, по крайней мере так говорит ADCL регистр. Но были замеченны другие глюки, например глюкаво работает динамическая индикация.

 

current_temp=current_temp/2.5; // преобразовываем (ADCH ADCL) в температуру. Т.к. используется внутренний ИОН 2560 мВ, то 2560/1024=2.5

то есть 1 дискрет АЦП = 2,5 мВ.

Причём тогда тут деление?

Наверно, умножить надо? Получим Uadc в милливольтах

просто я это так назвал "температурой", ну то, что пропорционально.

Насчет деления ты прав. Это я что-то ступил, но сути все равно не изменит

 

А сколько должно быть?

 

число от 0 до ...

Т.е. кручу переменник - меняется на нем падение напряжения, которое приложенно ко входу АЦП.

Заметил такую вещь: подал на вход АЦП напряжение (1;2;3 вольта) от блока питания - показания изменились. При напряжении 3 вольта опять выдало 409. По сути это 2,56 вольта. Поставил добавочное сопротивление ко входу АЦП, опять все вернул на места (по схеме) - ничего не изменилось, все равно 409. Как ни крути переменник. Померил напряжометром - напряжение действительно >2.56В, амперметр включенный между переменником и входом АЦП вообще ничего не показал. Чудеса.

 

Смущает вот что:

 

current_temp=current_temp/2.5; // преобразовываем (ADCH ADCL) в температуру. Т.к. используется внутренний ИОН 2560 мВ, то 2560/1024=2.5

 

 

Во-первых, current_temp у вас unsigned int, то есть это эквивалентно

current_temp=current_temp/2.

да и там вместо деления умножение должно быть.

просто на скорую руку переписал код - сделал без прерывания, и влезли некоторые оплошности. Надо будет погрешность пересчитать теперь...

 

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

Ну, блин! :biggrin: Мне для теста хотя бы напряжение показать, а коэффициенты пересчета для температуры потом введу

ЗЫ. Для форматирования кода - кнопочка "код", а не "цитата":)

писал на работе, юзая gprs internet Megafon. В целях экономии траффика отключил все картинки, и почему-то вместо (code) вставилось (quote). Сообщение не перечитывал, т.к. рабочий день подходил к концу. sorry :05:

Изменено пользователем Lost_Viking

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Заметил такую вещь: подал на вход АЦП напряжение (1;2;3 вольта) от блока питания - показания изменились. При напряжении 3 вольта опять выдало 409. По сути это 2,56 вольта. Поставил добавочное сопротивление ко входу АЦП, опять все вернул на места (по схеме) - ничего не изменилось, все равно 409. Как ни крути переменник. Померил напряжометром - напряжение действительно >2.56В, амперметр включенный между переменником и входом АЦП вообще ничего не показал. Чудеса.

 

А как соотносятся номиналы R1 и переменника? Может переменник просто большой и не до конца выкручивается? :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

нет, номиналы там от балды.

Уберите их вообще, вместо них поставьте резисторы 390-510om между пинами МК и сегментами индикатора (ABC..GH). Чтобы яркость цифр не менялась взависимости от заполнения. Транзисторы должны быть расчитаны на ток не менее 100ma.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А как соотносятся номиналы R1 и переменника? Может переменник просто большой и не до конца выкручивается? :)

 

на работе у меня целая гора переменников разных номиналов. все перепробовал - ну ни как! а вот с блока питания напрямую напряжение на вход АЦП - все работает. чудеса. завтра поколдую.

 

Уберите их вообще, вместо них поставьте резисторы 390-510om между пинами МК и сегментами индикатора (ABC..GH). Чтобы яркость цифр не менялась взависимости от заполнения. Транзисторы должны быть расчитаны на ток не менее 100ma.

 

упс, извинясь. просто эта схема взята из Proteus'а, и я туда специально воткнул эти резисторы, т.к. без них в протеусе транзисторы не закрывались (чудеса протеуса!). в реале их у меня нет.

 

Люди, скажите как вычислить абсолютную погрешность, если учесть, что два младших разряда АЦП по даташиту грешат,плюс округление с real до integer?

 

Т.е. имеем результат измерения 0010101100, младшие 2 разряда могут быть любыми, т.к. по даташиту абсолютная погрещность АЦП +/- 2 младших разряда. Далее, этот результат (dec(172)) умножаем на коэф.пересчета напряжения в температуру, например 2.6, и заносим в переменную unsigned int, соответственно все, что после точки отбрасывается.

 

 

Пока попробую в Excel'e покумекать

 

на катодах у меня по 100 Ом в реале стоят (вроде бы). Завтра гляну. Транзюки нормальные стоят.

 

Короче, индикация работает на ура! А АЦП что-то мне в морду плюет

Изменено пользователем Lost_Viking

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Короче, индикация работает на ура! А АЦП что-то мне в морду плюет

 

Если Вы пробовали подавать разное напряжение на вход АЦП, и на индикаторе отбражалость новое число, значит ваш девайс работает правильно..

 

Попробуйте замерять мультиметром напряжения на входе АЦП во время подключения к нему переменного резистора, если крутить ручку, и мультиметр будет показывать разные напряжения, то и девас покажет новое значение. Если нет, проверьте номинал переменного резистора он не должен быть слышком большим.. У меня для таких целей 5,1-33кОм прекрасно работают..

 

 

Странно, если вы убрали прерывания от АЦП, то где вы считываете текущее значение с регистра данных АЦП? Я бы поставил его в main. т.е. как я понял у вас данные считываються с АЦП только один раз при включении.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Люди, скажите как вычислить абсолютную погрешность, если учесть, что два младших разряда АЦП по даташиту грешат

Я что-то вообще не понял, что за датчик, схема какая-то трудночитабельная. Вообще, имхо, на АВР очень хитро вывернувшись можно, наверно, получить погрешность в пределах +/- 1 градуса... Это при условии калибровки. Про нестабильность внутренней опоры тоже надо подумать...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если Вы пробовали подавать разное напряжение на вход АЦП, и на индикаторе отбражалость новое число, значит ваш девайс работает правильно..

 

Попробуйте замерять мультиметром напряжения на входе АЦП во время подключения к нему переменного резистора, если крутить ручку, и мультиметр будет показывать разные напряжения, то и девас покажет новое значение. Если нет, проверьте номинал переменного резистора он не должен быть слышком большим.. У меня для таких целей 5,1-33кОм прекрасно работают..

Странно, если вы убрали прерывания от АЦП, то где вы считываете текущее значение с регистра данных АЦП? Я бы поставил его в main. т.е. как я понял у вас данные считываються с АЦП только один раз при включении.

 

Там сделано гораздо проще: АЦП преобразовывает все время, т.к. находится в режиме free run, и его не нужно все время запускать. Ну, а значения берутся из АЦП с частотой, равной частоте развертки, т.е. регулируется регистром OCR0. Там при каждом совпадении таймера с OCR0 вызывается фнукция обработчика прерывания:

interrupt [TIM0_COMP] void timer0_comp_isr(void)

в ней есть комманда:

PORTC=Dig[num()]

где num() - это функция, где при нажатой кнопке "нагрев" значения из ADCL и ADCH (ну или в CodevisionAvr - ADCW) помещаются в переменную current_temp. Далее это значение разбивается на 3 десятичных разряда.

 

Вобщем, я понял в чем ошибка. Смотрите схему. Слева - как было, справа - как стало. Т.е. теперь снимаю напряжение фактически с делителя. Может кто-нибудь объяснить почему при включении, что слева, АЦП не понимает в чем дело? Я не совсем понимаю.

post-37091-1217499924_thumb.jpg

Изменено пользователем Lost_Viking

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вобщем, я понял в чем ошибка. Смотрите схему. Слева - как было, справа - как стало. Т.е. теперь снимаю напряжение фактически с делителя. Может кто-нибудь объяснить почему при включении, что слева, АЦП не понимает в чем дело? Я не совсем понимаю.

 

А в исходной схеме нарисовано как справа:)

В левой схеме тоже получается делитель, но его нижнее сопротивление равно бесконечности. Потому на выходе получается всё время 5в.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я что-то вообще не понял, что за датчик, схема какая-то трудночитабельная. Вообще, имхо, на АВР очень хитро вывернувшись можно, наверно, получить погрешность в пределах +/- 1 градуса... Это при условии калибровки. Про нестабильность внутренней опоры тоже надо подумать...

 

датчиком будет какой-нибудь терморезистор, или транзистор. А пока для опытов вместо датчика использую обычный переменник на 2.4 кОм.

А такую погрешность можно получить и не выворачиваясь. Самое главное питание стабилизировать. Насчет внутренней опоры я не подумал. Насколько она нестабильна?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

датчиком будет какой-нибудь терморезистор, или транзистор.
Какой именно? Платиновый точно не подойдёт, только термистор. У него характеристика покруче, но нелинейная.

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

по даташиту +/- 0,1В, то есть почти +/-5%

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...