- Ардуино для школьников. Часы - термометр
Ардуино для школьников. Часы - термометр на основе таймера1
Практика для школьников. Мясищев А.А.
Шестой урок: Часы - термометр на Ардуине с микроконтроллером ATmega328p
Рассмотрим часы - термометр с датчиком температуры DS18B20. Часы должны быть запрограммированы с использованием 16-и разрядного счетчика-таймера 1. Результаты должны быть выведены на индикатор LCD 1602.
Таймер-счетчик 1 может работать в нескольких режимах. Здесь рассматривается режим при котором таймер 1 генерирует прерывание, когда обнаруживает совпадение при сравнении содержимого в регистре OCR1A (Output Compare Register A) со значением в регистре, где выполняется счет - регистр TCNT1 (Timer CouNTer 1). Чтобы активировать прерывание таймера 1 необходимо установить бит OCIE1A в регистре TIMSK1.
Регистры TCCR1A и TCCR1B задают режим работы таймера 1(см. рисунок 1)
Режим CTC (MODE 4) - ото режим сброса по совпадению регистров TCNT и OCR.
Рис.1. Необходимые для работы таймера 1 регистры
Например, для того, чтобы активизировать таймер 1 с частотой работы Clock/1024 (для Arduino UNO - 16000000/1024 = 15625Гц) необходимо выполнить следующие установки:
1. Задать режим работы таймера 1. Для этого в регистры TCCR1A, TCCR1B установить биты:
TCCR1A = 0b00000000;
TCCR1B = 0b00001101; - режим (MODE-4), CS12=1, CS10=1 (таймер тактируется частотой Clock/1024).
2. Обнулить старшую и младшую часть регистра счета -
TCNT1H=0b00000000;
TCNT1L=0b00000000.
3. В младшей и старшей части регистра сравнения А установить значение такое, стобы получить 1 секунду, т.е. 15625 (см. выше). Это значение преобразовываем с помощью калькулятора в двоичное или шестнадцатеричное число:
15625 = 0x3D09 или
OCR1AH = 0x3D;
OCR1AL = 0x09.
4. Разрешаем прерывание по сравнению регистров для таймера 1. Для этого устанавливаем бит OCIE1A в состояние 1:
TIMSK1 = 0b00000010.
В программе Arduino IDE описанное выше будет выглядеть так:
setup() {
TCCR1A = 0x00;
TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10);
TCNT1H=0x00;
TCNT1L=0x00;
OCR1AH = 0x3D;
OCR1AL = 0x09;
TIMSK1 = (1 << OCIE1A);
... }
ISR(TIMER1_COMPA_vect) { // Функция обработки прерывания
... }
На рисунке 2 показана схема соединений для реализации часов термометра с кнопками для установки минут и часов
Рис.2. Схема соединений
Программа - выводит температуру и часы на LCD индикатор (Arduino IDE 1.8.6)
#include <avr/interrupt.h>
#include <avr/io.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h> // Добавляем необходимую библиотеку
#define ONE_WIRE_BUS 8 // Подключение датчика температуры к 8 выводу
OneWire oneWire(ONE_WIRE_BUS); // Создание экземпляра класса
DallasTemperature sensors(&oneWire); // Создание экземпляра класса
LiquidCrystal lcd(7, 6, 5, 4, 3, 2); // (RS, E, DB4, DB5, DB6, DB7)
int s=0,m=0,h=0;
float cel=0;
void setup(){
// Устанавливаем регистры для прерывания по таймеру 1 при совпадении по сравнению в регистре А
TCCR1A=0b00000000; // Регистр TCCR1 Задает режим работы Time 1, т.е.
TCCR1B=0b00000000; // Предварительно обнуляем
TCCR1B = (1<<WGM12) | (1<<CS12) | (1<<CS10); //Тактирование таймера 16000000/1024 (16МГц)
TCNT1H=0x00; // В этих регистрах происходит
TCNT1L=0x00; // счет
ICR1H=0x00; // Обнуляем эту старшую часть регистра (можно убрать) - Регистр захвата
ICR1L=0x00; // Обнуляем эту младшую часть регистра (можно убрать)
// 16000000/1024 = 15625 = 0х3D09
OCR1AH=0x3D; // Регистры сравнения А. Старший байт 0x3D. Настройка на 1 секунду
OCR1AL=0x09; // Регистры сравнения. Младший байт 0x09
OCR1BH=0x00; // Регистры сравнения В (можно убрать)
OCR1BL=0x00; // Обнуляем их (можно убрать)
TIMSK1 |= (1 << OCIE1A); // В регистре TIMSK1 устанавливаем бит OCIE1A для прерывания по сравнению
pinMode(9,INPUT); // Установка кнопок на 9
pinMode(10,INPUT); // и 10 пине
digitalWrite(9,HIGH); // Подтягивание кнопок резистором 20ком. к 5Вольт
digitalWrite(10,HIGH);
sensors.begin(); // Вызов метода класса
lcd.begin(16, 2); // Задаем размерность экрана
lcd.setCursor(0, 0); // Устанавливаем курсор в начало 1 строки
lcd.print(" Temp = "); // Выводим текст
sensors.requestTemperatures();
cel=sensors.getTempCByIndex(0); // Определение температуры
lcd.print(cel); // Выводим значение
lcd.print("C");
}
ISR(TIMER1_COMPA_vect) // Функция обработки прерывания для вывода информации на экран и мигания диодами
{
TCNT1H=0; // Каждый раз обнуляем регистры счета
TCNT1L=0;
s++;
// Установка часов
if (digitalRead(9)==0) // Если нажата кнопка m+(подключена к 9 пину)
{ m++; } // к числу минуты прибавляется единица
// (для выполнения установки времени на устройстве локально)
if (digitalRead(10)==0) // Если нажата кнопка h+(подключена к 10 пину)
{ h++;} // к числу часов прибавляется единица
// Условия часов
if(s==60) // Если число секунд = 60
{ m++; s=0;} // к минутам добавляется 1
if(m==60) // Если число минут = 60
{h++; m=0;} // к часам добавляется 1
if (h==24) // Если число часов = 24
{ h=0;m=0;s=0;}
lcd.setCursor(0, 1);
lcd.print("Time = ");
lcd.print(h/10);lcd.print(h%10);lcd.print(":");
lcd.print(m/10);lcd.print(m%10);lcd.print(":");
lcd.print(s/10);lcd.print(s%10);
if( s==0|| s==15 || s==30 || s==45 ){
sensors.requestTemperatures();
cel=sensors.getTempCByIndex(0); // Определение температуры
lcd.setCursor(0, 0);
lcd.print(" Temp = ");
lcd.print(cel); // Выводим значение температуры
lcd.print("C"); }
}
void loop(){
}
Рис.3. Фото часов-термометра
Задание.
1. Изменить программу так, чтобы при нажатии кнопок установки минут и часов их значения менялись не раз в секунду, а 4-е раза в секунду.
2. Подключить к 11 выводу светодиод. Дополнить вышепредставленную программу так, чтобы в 10 часов, 10 минут и 10 секунд включался синий светодиод.
3. Подключить к 12 выводу светодиод. Дополнить вышепредставленную программу так, чтобы при температуре 30 градусов загорался зеленый светодиод.
Программа, выводящая символ градуса, который создается пользователем
#include <avr/interrupt.h>
#include <avr/io.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h> // Добавляем необходимую библиотеку
byte slash[8]= { // Создается массив для своего символа(символ градуса)
B01110, // В массиве может быть 5х8 точек
B10001,
B10001,
B01110,
B00000,
B00000,
B00000,
B00000,
};
#define ONE_WIRE_BUS 8 // Подключение датчика температуры к 8 выводу
OneWire oneWire(ONE_WIRE_BUS); // Создание экземпляра класса
DallasTemperature sensors(&oneWire); // Создание экземпляра класса
LiquidCrystal lcd(7, 6, 5, 4, 3, 2); // (RS, E, DB4, DB5, DB6, DB7)
int s=0,m=0,h=0;
float cel=0;
void setup(){
// Устанавливаем регистры для прерывания по таймеру 1 при совпадении по сравнению в регистре А
TCCR1A=0b00000000; // Регистр TCCR1 Задает режим работы Time 1, т.е.
TCCR1B=0b00000000; // Предварительно обнуляем
TCCR1B = (1<<WGM12) | (1<<CS12) | (1<<CS10); //Тактирование таймера 16000000/1024 (16МГц)
TCNT1H=0x00; // В этих регистрах происходит
TCNT1L=0x00; // счет
//ICR1H=0x00; // Обнуляем эту старшую часть регистра
//ICR1L=0x00; // Обнуляем эту младшую часть регистра
// 16000000/1024 = 15625 = 0х3D09
OCR1AH=0x3D; // Регистры сравнения А. Старший байт 0x3D
OCR1AL=0x09; // Регистры сравнения. Младший байт 0x09
//OCR1BH=0x00; // Регистры сравнения В.
//OCR1BL=0x00; // Обнуляем их
TIMSK1 |= (1 << OCIE1A); // В регистре TIMSK1 устанавливаем бит OCIE1A для прерывания по сравнению
pinMode(9,INPUT);
pinMode(10,INPUT);
digitalWrite(9,HIGH);
digitalWrite(10,HIGH);
lcd.createChar(7, slash); // Создание символа градус под номером 7 (макс. 8 символов) LCD.
sensors.begin(); // Вызов метода класса
lcd.begin(16, 2); // Задаем размерность экрана
lcd.setCursor(0, 0); // Устанавливаем курсор в начало 1 строки
lcd.print(" Temp = "); // Выводим текст
sensors.requestTemperatures();
cel=sensors.getTempCByIndex(0); // Определение температуры
lcd.print(cel); // Выводим значение
lcd.write(7); // Запись символа градус
lcd.print("C");
}
ISR(TIMER1_COMPA_vect) // Функция обработки прерывания для вывода информации на экран и мигания диодами
{
TCNT1H=0; // Каждый раз обнуляем регистры счета
TCNT1L=0;
s++;
// Установка часов
if (digitalRead(9)==0) // Если нажата кнопка m+(подключена к 9 пину)
{ m++; } // к числу минуты прибавляется единица
// (для выполнения установки времени на устройстве локально)
if (digitalRead(10)==0) // Если нажата кнопка h+(подключена к 10 пину)
{ h++;} // к числу часов прибавляется единица
// Условия часов
if(s==60) // Если число секунд = 60
{ m++; s=0;} // к минутам добавляется 1
if(m==60) // Если число минут = 60
{h++; m=0;} // к часам добавляется 1
if (h==24) // Если число часов = 24
{ h=0;m=0;s=0;}
lcd.setCursor(0, 1);
lcd.print("Time = ");
lcd.print(h/10);lcd.print(h%10);lcd.print(":");
lcd.print(m/10);lcd.print(m%10);lcd.print(":");
lcd.print(s/10);lcd.print(s%10);lcd.print(" ");
if( s==0|| s==15 || s==30 || s==45 ){
sensors.requestTemperatures();
cel=sensors.getTempCByIndex(0); // Определение температуры
lcd.setCursor(0, 0);
lcd.print(" Temp = ");
lcd.print(cel); // Выводим значение температуры
lcd.write(7); // Запись символа градус
lcd.print("C "); }
}
void loop(){
}
Программа, устанавливающая часы и минуты за 0.5 секунды и выводящая градусы с одной цифрой после запятой с округлением
#include <avr/interrupt.h>
#include <avr/io.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal.h> // Добавляем необходимую библиотеку
byte slash[8]= { // Создается массив для своего символа(символ градуса)
B01110, // В массиве может быть 5х8 точек
B10001,
B10001,
B01110,
B00000,
B00000,
B00000,
B00000,
};
#define ONE_WIRE_BUS 8 // Подключение датчика температуры к 8 выводу
OneWire oneWire(ONE_WIRE_BUS); // Создание экземпляра класса
DallasTemperature sensors(&oneWire); // Создание экземпляра класса
LiquidCrystal lcd(7, 6, 5, 4, 3, 2); // (RS, E, DB4, DB5, DB6, DB7)
int s=0,m=0,h=0;
float cel=0;
int ss=0;
void setup(){
// Устанавливаем регистры для прерывания по таймеру 1 при совпадении по сравнению в регистре А
TCCR1A=0b00000000; // Регистр TCCR1 Задает режим работы Time 1, т.е.
TCCR1B=0b00000000; // Предварительно обнуляем
TCCR1B = (1<<WGM12) | (1<<CS12)| (1 << CS10) ; //Тактирование таймера 16000000/1024 (16МГц)
TCNT1H=0x00; // В этих регистрах происходит
TCNT1L=0x00; // счет
//ICR1H=0x00; // Обнуляем эту старшую часть регистра
//ICR1L=0x00; // Обнуляем эту младшую часть регистра
// 16000000/1024 = 0х3D09 - прерывание будет происходить за 1.0сек.
// 16000000/2048 = 0х1E84 - прерывание будет происходить за 0.5сек.
OCR1AH=0x1E; // Регистры сравнения А. Старший байт 0x1E
OCR1AL=0x84; // Регистры сравнения. Младший байт 0x84
//OCR1BH=0x00; // Регистры сравнения В.
//OCR1BL=0x00; // Обнуляем их
TIMSK1 |= (1 << OCIE1A); // В регистре TIMSK1 устанавливаем бит OCIE1A для прерывания по сравнению
pinMode(9,INPUT);
pinMode(10,INPUT);
digitalWrite(9,HIGH);
digitalWrite(10,HIGH);
lcd.createChar(7, slash); // Создание символа градус под номером 7 (макс. 8 символов) LCD.
sensors.begin(); // Вызов метода класса
lcd.begin(16, 2); // Задаем размерность экрана
//sensors.requestTemperatures();
//cel=sensors.getTempCByIndex(0); // Определение температуры
}
ISR(TIMER1_COMPA_vect) // Функция обработки прерывания для вывода информации на экран и мигания диодами
{
byte buf[10];
TCNT1H=0; // Каждый раз обнуляем регистры счета
TCNT1L=0;
ss++;
if(ss==2) // При такой подстановке установка часов при нажатии на кнопки будет происходить
// за 1/2 секунды
{ s++; ss=0;}
// Установка часов
if (digitalRead(9)==0) // Если нажата кнопка m+(подключена к 9 пину)
{ m++; } // к числу минуты прибавляется единица
// (для выполнения установки времени на устройстве локально)
if (digitalRead(10)==0) // Если нажата кнопка h+(подключена к 10 пину)
{ h++;} // к числу часов прибавляется единица
// Условия часов
if(s==60) // Если число секунд = 60
{ m++; s=0;} // к минутам добавляется 1
if(m==60) // Если число минут = 60
{h++; m=0;} // к часам добавляется 1
if (h==24) // Если число часов = 24
{ h=0;m=0;s=0;}
lcd.setCursor(0, 1);
lcd.print("Time = ");
lcd.print(h/10);lcd.print(h%10);lcd.print(":");
lcd.print(m/10);lcd.print(m%10);lcd.print(":");
lcd.print(s/10);lcd.print(s%10);lcd.print(" ");
if( s==0|| s==15 || s==30 || s==45 ){
sensors.requestTemperatures();
cel=sensors.getTempCByIndex(0); // Определение температуры
dtostrf(cel,5,1,buf); // Запись температуры в буфер buf всего 5 символов и 1 после точки
// Выполняется также округление. Наприме, если cel=24.87, то в buf будет _24.9(_ это пробел)
lcd.setCursor(0, 0);
lcd.print("Temp = ");
//lcd.write(buf[0]); // Выводим значение температуры
//lcd.write(buf[1]);
//lcd.write(buf[2]);
//lcd.write(buf[3]);
//lcd.write(buf[4]);
for(int k=0;k<5;k++) lcd.write(buf[k]);
lcd.write(7); // Запись символа градус
lcd.print("C "); }
}
void loop(){
}
Часы - термометр, но в случае подключения Arduino UNO к индикатору TM1637
Фото подключения:
Здесь датчик температуры подключен к выводу 10. Кнопки для установки минут и часов, подключены к выводам 8 и 9 соответственно. Схема соединения кнопок, датчика температуры и светодиода показаны на рисунке ниже
Схема соединений
Ниже представлена программа(time_tm1637_1), которая выводит время в часах и минутах на TM1637. Двоеточие между часами и минутами мигает раз в секунду
#include <avr/interrupt.h>
#include <avr/io.h>
#include <TM1637Display.h>
// Подключение TM1637
#define CLK 2
#define DIO 3
TM1637Display display(CLK, DIO);
int s=0,m=0,h=0;
float cel=0;
void setup(){
// Устанавливаем регистры для прерывания по таймеру 1 при совпадении по сравнению в регистре А
TCCR1A=0b00000000; // Регистр TCCR1 Задает режим работы Time 1, т.е.
TCCR1B=0b00000000; // Предварительно обнуляем
TCCR1B = (1<<WGM12) | (1<<CS12) | (1<<CS10); //Тактирование таймера 16000000/1024 (16МГц)
TCNT1H=0x00; // В этих регистрах происходит
TCNT1L=0x00; // счет
ICR1H=0x00; // Обнуляем эту старшую часть регистра (можно убрать)
ICR1L=0x00; // Обнуляем эту младшую часть регистра (можно убрать)
// 16000000/1024 = 15625 = 0х3D09
OCR1AH=0x3D; // Регистры сравнения А. Старший байт 0x3D. Настройка на 1 секунду
OCR1AL=0x09; // Регистры сравнения. Младший байт 0x09
OCR1BH=0x00; // Регистры сравнения В (можно убрать)
OCR1BL=0x00; // Обнуляем их (можно убрать)
TIMSK1 |= (1 << OCIE1A); // В регистре TIMSK1 устанавливаем бит OCIE1A для прерывания по сравнению
pinMode(8,INPUT); // Установка кнопок на 8
pinMode(9,INPUT); // и 9 пине
digitalWrite(8,HIGH); // Подтягивание кнопок резистором 20ком. к 5Вольт
digitalWrite(9,HIGH);
display.clear();
display.setBrightness(7);
}
ISR(TIMER1_COMPA_vect) // Функция обработки прерывания для вывода информации на экран и мигания диодами
{
char data[4];
TCNT1H=0; // Каждый раз обнуляем регистры счета
TCNT1L=0;
s++;
// Установка часов
if (digitalRead(8)==0) // Если нажата кнопка m+(подключена к 8 пину)
{ m++; } // к числу минуты прибавляется единица
// (для выполнения установки времени на устройстве локально)
if (digitalRead(9)==0) // Если нажата кнопка h+(подключена к 9 пину)
{ h++;} // к числу часов прибавляется единица
// Условия часов
if(s==60) // Если число секунд = 60
{ m++; s=0;} // к минутам добавляется 1
if(m==60) // Если число минут = 60
{h++; m=0;} // к часам добавляется 1
if (h==24) // Если число часов = 24
{ h=0;m=0;s=0;}
if(s%2){ // Проверка на нечетность
data[0]=display.encodeDigit(h/10);
data[1]=display.encodeDigit(h%10) | 0b10000000;
data[2]=display.encodeDigit(m/10);
data[3]=display.encodeDigit(m%10);
display.setSegments(data);
}
else {
data[0]=display.encodeDigit(h/10);
data[1]=display.encodeDigit(h%10) ;
data[2]=display.encodeDigit(m/10);
data[3]=display.encodeDigit(m%10);
display.setSegments(data);
}
}
void loop(){
}
Задание
1. Изменить программу так, чтобы первые 10 секунд высвечивалось часы и минуты через двоеточие, следующие 10 секунд - минуты и секунды через двоеточие и так в цикле.
2. Изменить программу так, чтобы первые 20 секунд высвечивалось часы и минуты через двоеточие, следующие 20 секунд - минуты и секунды через двоеточие и следующие 20 секунд - целое значение температуры с значком градуса и символом С(цельсия).
3. Изменить первую программу так, чтобы при нажатии кнопок установки минут и часов их значения менялись не раз в секунду, а 4-е раза в секунду.
4. Подключить к 11 выводу светодиод. Дополнить третью программу так, чтобы при температуре свыше 23 градуса загорался светодиод. При снижении температуры ниже 23 градусов светодиод должен гаснуть. Светодиод должен реагировать на температуру в любом из 3-х состояний отображения информации индикатором.
Подсказка
1.
if((s<10&&s>=0) || (s<30 && s>=20) || (s<50 && s >=40))
{
data[0]=display.encodeDigit(h/10);
data[1]=display.encodeDigit(h%10) | 0b10000000;
data[2]=display.encodeDigit(m/10);
data[3]=display.encodeDigit(m%10);
display.setSegments(data);
}
else {
data[0]=display.encodeDigit(m/10);
data[1]=display.encodeDigit(m%10) | 0b10000000;
data[2]=display.encodeDigit(s/10);
data[3]=display.encodeDigit(s%10);
display.setSegments(data);
}
2.
if(s<20&&s>=0 )
{
data[0]=display.encodeDigit(h/10);
data[1]=display.encodeDigit(h%10) | 0b10000000;
data[2]=display.encodeDigit(m/10);
data[3]=display.encodeDigit(m%10);
display.setSegments(data);
}
if(s<40 && s>=20) {
data[0]=display.encodeDigit(m/10);
data[1]=display.encodeDigit(m%10) | 0b10000000;
data[2]=display.encodeDigit(s/10);
data[3]=display.encodeDigit(s%10);
display.setSegments(data);
}
if(s<60 && s>=40) {
sensors.requestTemperatures();
cel=sensors.getTempCByIndex(0); // Определение температуры
dtostrf(cel,4,1,buf); // Запись числа cel в символьный массив buf
int temH=buf[0]-48; //Преобразование десятичного кода символа в число
int temL=buf[1]-48;
int temLL=buf[3]-48;
// Вывод температуры с двоеточием
data[0]=display.encodeDigit(temH);
data[1]=display.encodeDigit(temL)| 0b10000000; // для получения символа с двоеточием
data[2]=display.encodeDigit(temLL);
data[3]=display.encodeDigit(12);
display.setSegments(data);
}
10.12.2018