|
|
  |
терморегулятор, помогите |
|
|
|
Jul 30 2008, 12:01
|
Частый гость
 
Группа: Участник
Сообщений: 168
Регистрация: 25-04-08
Пользователь №: 37 091

|
Собираю терморегулятор. Готова динамическая индикация (спасибо форумчанам). Теперь проблема с АЦП. Цитата /***************************************************** 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. Когда использовал прерывание АЦП, то у меня все работало. Но хочется упростить немного код (пусть хоть потребляет больше) Схемку прилагаю. Подскажите что не так, я уже не знаю чего делать.
Эскизы прикрепленных изображений
|
|
|
|
|
Jul 30 2008, 12:31
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(Lost_Viking @ Jul 30 2008, 18:01)  почему-то в 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. Во-вторых, не вижу здесь вычисления значения температуры, вижу только вычисление напряжения. ЗЫ. Для форматирования кода - кнопочка "код", а не "цитата":)
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jul 30 2008, 12:40
|

Беспросветный оптимист
     
Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646

|
Код current_temp=current_temp/2.5; // преобразовываем (ADCH ADCL) в температуру. Т.к. используется внутренний ИОН 2560 мВ, то 2560/1024=2.5 то есть 1 дискрет АЦП = 2,5 мВ. Причём тогда тут деление? Наверно, умножить надо? Получим Uadc в милливольтах ещё такая штучка: x*2.5=(x<<1)+(x>>1)
--------------------
Программирование делится на системное и бессистемное. ©Моё :) — а для кого-то БГ — это Bill Gilbert =)
|
|
|
|
|
Jul 30 2008, 17:04
|
Частый гость
 
Группа: Участник
Сообщений: 168
Регистрация: 25-04-08
Пользователь №: 37 091

|
Цитата(=GM= @ Jul 30 2008, 19:50)  У вас на самом деле последовательно с транзисторами стоят сопротивления 1 Ом? Не боитесь попалить МК и индикатор?
Последовательно с диодом D1 надо бы включить сопротивление, а то и здесь всё попалите. нет, номиналы там от балды. Не от балды - катушка, емкости, и резистор к питанию АЦП Цитата(domowoj @ Jul 30 2008, 19:26)  Протеус 7.2 .2 с авр работает глючно!!! Мегу 16 не проверял, а 8535 и тиньки - глюк, особенно что касаемо АЦП. По заверениям разработчиков в версии 7.3 глюки обещали исправить. Ждем-с. АЦП в протеусе у меня работает, по крайней мере так говорит ADCL регистр. Но были замеченны другие глюки, например глюкаво работает динамическая индикация. Цитата(MrYuran @ Jul 30 2008, 16:40)  Код current_temp=current_temp/2.5; // преобразовываем (ADCH ADCL) в температуру. Т.к. используется внутренний ИОН 2560 мВ, то 2560/1024=2.5 то есть 1 дискрет АЦП = 2,5 мВ. Причём тогда тут деление? Наверно, умножить надо? Получим Uadc в милливольтах просто я это так назвал "температурой", ну то, что пропорционально. Насчет деления ты прав. Это я что-то ступил, но сути все равно не изменит Цитата(AHTOXA @ Jul 30 2008, 16:31)  А сколько должно быть? число от 0 до ... Т.е. кручу переменник - меняется на нем падение напряжения, которое приложенно ко входу АЦП. Заметил такую вещь: подал на вход АЦП напряжение (1;2;3 вольта) от блока питания - показания изменились. При напряжении 3 вольта опять выдало 409. По сути это 2,56 вольта. Поставил добавочное сопротивление ко входу АЦП, опять все вернул на места (по схеме) - ничего не изменилось, все равно 409. Как ни крути переменник. Померил напряжометром - напряжение действительно >2.56В, амперметр включенный между переменником и входом АЦП вообще ничего не показал. Чудеса. Цитата(AHTOXA @ Jul 30 2008, 16:31)  Смущает вот что: Код current_temp=current_temp/2.5; // преобразовываем (ADCH ADCL) в температуру. Т.к. используется внутренний ИОН 2560 мВ, то 2560/1024=2.5 Во-первых, current_temp у вас unsigned int, то есть это эквивалентно current_temp=current_temp/2. да и там вместо деления умножение должно быть. просто на скорую руку переписал код - сделал без прерывания, и влезли некоторые оплошности. Надо будет погрешность пересчитать теперь... Цитата(AHTOXA @ Jul 30 2008, 16:31)  Во-вторых, не вижу здесь вычисления значения температуры, вижу только вычисление напряжения. Ну, блин!  Мне для теста хотя бы напряжение показать, а коэффициенты пересчета для температуры потом введу Цитата(AHTOXA @ Jul 30 2008, 16:31)  ЗЫ. Для форматирования кода - кнопочка "код", а не "цитата":) писал на работе, юзая gprs internet Megafon. В целях экономии траффика отключил все картинки, и почему-то вместо (code) вставилось (quote). Сообщение не перечитывал, т.к. рабочий день подходил к концу. sorry
Сообщение отредактировал Lost_Viking - Jul 30 2008, 17:10
|
|
|
|
|
Jul 30 2008, 18:56
|
Частый гость
 
Группа: Участник
Сообщений: 168
Регистрация: 25-04-08
Пользователь №: 37 091

|
Цитата(AHTOXA @ Jul 30 2008, 21:31)  А как соотносятся номиналы R1 и переменника? Может переменник просто большой и не до конца выкручивается?  на работе у меня целая гора переменников разных номиналов. все перепробовал - ну ни как! а вот с блока питания напрямую напряжение на вход АЦП - все работает. чудеса. завтра поколдую. Цитата(defunct @ Jul 30 2008, 21:45)  Уберите их вообще, вместо них поставьте резисторы 390-510om между пинами МК и сегментами индикатора (ABC..GH). Чтобы яркость цифр не менялась взависимости от заполнения. Транзисторы должны быть расчитаны на ток не менее 100ma. упс, извинясь. просто эта схема взята из Proteus'а, и я туда специально воткнул эти резисторы, т.к. без них в протеусе транзисторы не закрывались (чудеса протеуса!). в реале их у меня нет. Люди, скажите как вычислить абсолютную погрешность, если учесть, что два младших разряда АЦП по даташиту грешат,плюс округление с real до integer? Т.е. имеем результат измерения 0010101100, младшие 2 разряда могут быть любыми, т.к. по даташиту абсолютная погрещность АЦП +/- 2 младших разряда. Далее, этот результат (dec(172)) умножаем на коэф.пересчета напряжения в температуру, например 2.6, и заносим в переменную unsigned int, соответственно все, что после точки отбрасывается. Пока попробую в Excel'e покумекать на катодах у меня по 100 Ом в реале стоят (вроде бы). Завтра гляну. Транзюки нормальные стоят. Короче, индикация работает на ура! А АЦП что-то мне в морду плюет
Сообщение отредактировал Lost_Viking - Jul 30 2008, 19:04
|
|
|
|
|
Jul 31 2008, 06:26
|
Группа: Новичок
Сообщений: 11
Регистрация: 8-02-08
Пользователь №: 34 857

|
Цитата(Lost_Viking @ Jul 30 2008, 21:56)  Короче, индикация работает на ура! А АЦП что-то мне в морду плюет Если Вы пробовали подавать разное напряжение на вход АЦП, и на индикаторе отбражалость новое число, значит ваш девайс работает правильно.. Попробуйте замерять мультиметром напряжения на входе АЦП во время подключения к нему переменного резистора, если крутить ручку, и мультиметр будет показывать разные напряжения, то и девас покажет новое значение. Если нет, проверьте номинал переменного резистора он не должен быть слышком большим.. У меня для таких целей 5,1-33кОм прекрасно работают.. Странно, если вы убрали прерывания от АЦП, то где вы считываете текущее значение с регистра данных АЦП? Я бы поставил его в main. т.е. как я понял у вас данные считываються с АЦП только один раз при включении.
|
|
|
|
|
Jul 31 2008, 06:42
|

Беспросветный оптимист
     
Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646

|
Цитата(Lost_Viking @ Jul 30 2008, 22:56)  Люди, скажите как вычислить абсолютную погрешность, если учесть, что два младших разряда АЦП по даташиту грешат Я что-то вообще не понял, что за датчик, схема какая-то трудночитабельная. Вообще, имхо, на АВР очень хитро вывернувшись можно, наверно, получить погрешность в пределах +/- 1 градуса... Это при условии калибровки. Про нестабильность внутренней опоры тоже надо подумать...
--------------------
Программирование делится на системное и бессистемное. ©Моё :) — а для кого-то БГ — это Bill Gilbert =)
|
|
|
|
|
Jul 31 2008, 10:08
|
Частый гость
 
Группа: Участник
Сообщений: 168
Регистрация: 25-04-08
Пользователь №: 37 091

|
Цитата(AVRdeveloper @ Jul 31 2008, 10:26)  Если Вы пробовали подавать разное напряжение на вход АЦП, и на индикаторе отбражалость новое число, значит ваш девайс работает правильно..
Попробуйте замерять мультиметром напряжения на входе АЦП во время подключения к нему переменного резистора, если крутить ручку, и мультиметр будет показывать разные напряжения, то и девас покажет новое значение. Если нет, проверьте номинал переменного резистора он не должен быть слышком большим.. У меня для таких целей 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 десятичных разряда. Вобщем, я понял в чем ошибка. Смотрите схему. Слева - как было, справа - как стало. Т.е. теперь снимаю напряжение фактически с делителя. Может кто-нибудь объяснить почему при включении, что слева, АЦП не понимает в чем дело? Я не совсем понимаю.
Сообщение отредактировал Lost_Viking - Jul 31 2008, 10:26
Эскизы прикрепленных изображений
|
|
|
|
|
Jul 31 2008, 10:55
|
Частый гость
 
Группа: Участник
Сообщений: 168
Регистрация: 25-04-08
Пользователь №: 37 091

|
Цитата(MrYuran @ Jul 31 2008, 10:42)  Я что-то вообще не понял, что за датчик, схема какая-то трудночитабельная. Вообще, имхо, на АВР очень хитро вывернувшись можно, наверно, получить погрешность в пределах +/- 1 градуса... Это при условии калибровки. Про нестабильность внутренней опоры тоже надо подумать... датчиком будет какой-нибудь терморезистор, или транзистор. А пока для опытов вместо датчика использую обычный переменник на 2.4 кОм. А такую погрешность можно получить и не выворачиваясь. Самое главное питание стабилизировать. Насчет внутренней опоры я не подумал. Насколько она нестабильна?
|
|
|
|
|
Jul 31 2008, 11:05
|

Беспросветный оптимист
     
Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646

|
Цитата(Lost_Viking @ Jul 31 2008, 14:55)  датчиком будет какой-нибудь терморезистор, или транзистор. Какой именно? Платиновый точно не подойдёт, только термистор. У него характеристика покруче, но нелинейная. Цитата Насчет внутренней опоры я не подумал. Насколько она нестабильна? по даташиту +/- 0,1В, то есть почти +/-5%
--------------------
Программирование делится на системное и бессистемное. ©Моё :) — а для кого-то БГ — это Bill Gilbert =)
|
|
|
|
|
  |
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|