- Ардуино для школьников. Часы - термометр

Ардуино для школьников. Часы - термометр на основе таймера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