цифровая электроника
вычислительная техника
встраиваемые системы

 

ATmega и встроенный датчик температуры

Автор: Mike(admin) от 13-02-2014, 07:45

Интересной особенностью микроконтроллеров серии ATmega является встроенный датчик температуры, показания которого можно прочитать, используя внутренний АЦП. На текущий момент среди МК ATmega насчитывается 18 представителей с датчиком температуры: AT90PWM161, AT90PWM81, ATmega168A, ATmega168P, ATmega168PA, ATmega16M1, ATmega16U4, ATmega328, ATmega328P, ATmega32M1, ATmega32U4, ATmega48A, ATmega48P, ATmega48PA, ATmega64M1, ATmega88A, ATmega88P и ATmega88PA.


Проблема в том, что этот датчик измеряет температуру кристалла и для повседневных нужд, вроде измерения окружающей температуры, изначально он не пригоден. Но, используя известную разницу между температурой окружающего воздуха и температурой кристалла, можно найти эту температуру. В Arduino Leonardo и Arduino Pro Micro применяется ATMEGA32u4, поэтому для них нижеприведенный код подойдет без изменений, для других микроконтроллеров нужно уточнять регистры.


Первым делом нужно настроить АЦП на считывание показаний внутреннего датчика температуры. Сначала следует в качестве источника опорного напряжения установить внутренний источник 2.56 В, настроить мультиплексор на датчик температуры и в итоге включить АЦП. Код этих действий:



void setupADC(){

//Мультиплексирование АЦП
ADMUX = 0;
ADMUX |= (1 << REFS1); //Внутр. источник опорного напр-я 2.56 В с внешним конденсатором на выводе AREF
ADMUX |= (1 << REFS0); //Внутр. источник опорного напр-я 2.56 В с внешним конденсатором на выводе AREF
ADMUX |= (0 << MUX4); //Датчик температуры - 100111
ADMUX |= (0 << MUX3); //Датчик температуры - 100111
ADMUX |= (1 << MUX2); //Датчик температуры - 100111
ADMUX |= (1 << MUX1); //Датчик температуры - 100111
ADMUX |= (1 << MUX0); //Датчик температуры - 100111

//Регистр А статуса и управления АЦП
ADCSRA = 0;
ADCSRA |= (1 << ADEN); //Включение АЦП
ADCSRA |= (1 << ADPS2); //Предделитель АЦП 16 (1из 16 в 1 МГц)

//Регистр B статуса и управления АЦП
ADCSRB = 0;
ADCSRB |= (1 << MUX5); //Датчик температуры - 100111

Ниже приводится код для проверки значения датчика. Он стартует преобразование АЦП, ждет его завершения и затем возвращает температуру в Цельсиях. Изначально датчик выдает температуру в Кельвинах, поэтому впоследствии ее нужно скорректировать на 273.



int getTemp(){
ADCSRA |= (1 << ADSC); //Начало преобразования
while (bit_is_set(ADCSRA, ADSC)); //Ожидание завершения преобразования
byte low = ADCL;
byte high = ADCH;
int temperature = (high << 8) | low; //Результат в Кельвинах
return temperature - 273;
}

Вот код самой программы с вызовом приведенных выше функций:



void setup(){
//Вкл. последовательный порт
Serial.begin(9600);

setupADC();
}

void loop(){
Serial.print("Time: ");
Serial.print(millis());
Serial.print(" Core Temperature: ");
Serial.print(getTemp());
Serial.println(" C");

delay(1000);
}

Этот код позволит вам считывать показания датчика и выводить их в Цельсиях на последовательный порт. Но в данном случае наш датчик не откалиброван, и согласно документации заводская калибровка имеет точность +- 10C. Для калибровки температуры можно, например, поднести к ATMEGA32u4 кубик льда и ждать, пока температура не стабилизируется примерно на 7C. Поскольку вода меняет свое состояние из жидкого в твердое при 0C, то температуру кубика льда можно считать 0C. Если предположить, что в данном случае обеспечивается достаточная теплопроводность, то температура кристалла также должна быть близка к 0C. Исходя из этого, мы теперь знаем, что показание 7C слишком велико для датчика, поэтому к формуле вычисления температуры добавим смещение, как показано в коде ниже:



#define TEMP_OFFSET -7
int getTemp(){
ADCSRA |= (1 << ADSC);
while (bit_is_set(ADCSRA, ADSC));
byte low = ADCL;
byte high = ADCH;
int temperature = (high << 8) | low;
return temperature - 273 + TEMP_OFFSET;
}

Но помните, что для каждого конкретного микроконтроллера смещение придется определять индивидуально. Ниже приведена вся программа для ATMEGA32u4 (другие МК будут иметь другие регистры):



#define TEMP_OFFSET -7

void setupADC(){
cli(); //Отключаем глобальные прерывания

ADMUX = 0;
ADMUX |= (1 << REFS1);
ADMUX |= (1 << REFS0);
ADMUX |= (0 << MUX4);
ADMUX |= (0 << MUX3);
ADMUX |= (1 << MUX2);
ADMUX |= (1 << MUX1);
ADMUX |= (1 << MUX0);

ADCSRA = 0;
ADCSRA |= (1 << ADEN);
ADCSRA |= (1 << ADPS2);

ADCSRB = 0;
ADCSRB |= (1 << MUX5);

sei(); //Разрешаем глобальные прерывания
}

int getTemp(){
ADCSRA |= (1 << ADSC);
while (bit_is_set(ADCSRA, ADSC));
byte low = ADCL;
byte high = ADCH;
int temperature = (high << 8) | low;
return temperature - 273 + TEMP_OFFSET;
}

void setup(){

Serial.begin(9600);

setupADC();
}

void loop(){
Serial.print("Time: ");
Serial.print(millis());
Serial.print(" Core Temperature: ");
Serial.print(getTemp());
Serial.println(" C");

delay(1000);
}

После регистрации температуры примерно в течение 5 минут и последующей фильтрации можно получить следующий график:


ATmega и встроенный датчик температуры

Начав работу примерно при 20C (температура помещения), микроконтроллер быстро начал нагреваться, дойдя до теплового равновесия в 24C за 1 минуту. Таким образом, используя ATMEGA32u4, мы можем узнать температуру окружающей среды, вычитая 4C от текущих показаний.




Перевод © digitrode.ru


<Источник>


Теги: AVR




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

Комментарии:

Оставить комментарий