Итак, после небольшого технического перерыва, продолжаем наше знакомство с семейством МК ARDUINO. В этом уроке мы попробуем сделать часы работающие от внутреннего генератора МК (с внешним генератором будет один из следующих уроков) и выводящего информацию на ЖК индикатор типа 1602 (что означает 16 символов в 2 строки, есть еще тип 1604- 16 символов в 4 строки, вы уже поняли что первые 2 цифры указывают на количество символов индикатора а вторые- на количество строк). Не будем затягивать вступление, переходим к работе.
Для проекта нам понадобится:
- Arduino Uno
- ЖК индикатор 1602
- Макетная плата
- Провода
- Подстроечный резистор на 10 кОм
Для особо ленивых советую опустится в низ страницы и скачать готовый скетч, для тех кто хочет научится делать скетчи самостоятельно опишу более подробно все шаги проекта. Для правильной и быстрой работы над проектом необходим алгоритм работы. Практически любой проект лучше накидать на бумаге и потом следовать по алгоритму шаг за шагом. Мы поступим абсолютно так же. Итак составляем алгоритм. У нас есть несколько условий, выпишем их в порядке возрастания:
- Секунды, работают в пределе от 0 до 59 по циклу с секундным интервалом (это понятно).
- Минуты, работают в пределе от 0 до 59 по циклу, переключение происходит при достижении значения секундами значения 0.
- Часы, работают в пределе от 0 до 24 (здесь вы можете выбрать отображение как в зарубежном стиле от 0 до 12 со значениями AM и PM, это как вам больше нравится) по циклу, переключение происходит по достижении значения минутами 0.
- Вывести всю необходимую информацию на дисплей (например вы можете решить не выводить секунды а сделать просто мигающую точку между часами и минутами)
Собираем наши часы по вот такой схеме:
Советы по сборке. Индикатор 1602 обычно приходит из Китая в «голом» виде, т.е. никаких выводов не подпаяно, советую для этих целей использовать двухрядные компьютерные гнезда от материнских плат, один вывод гнезда вставляется в 1602, второй вывод оказывается за краем платы, запаиваете оба вывода на один контакт- так повышается механическая и электрическая прочность. На данной схеме не указана схема подключения подсветки, это следующие 2 вывода справа от D7. Вы можете их подключить к питанию 3,3В на ARDUINO, можете сделать плавное загорание/затухание если подключите плюсовой вывод (он подписан как А- анод) к ШИМ выходу ARDUINO и будете управлять питание через этот вывод, это уже второстепенная задача, пока просто подключите вывод А на 1602 к 3,3V на ARDUINO, а вывод К 1602 к GND ARDUINO.
Теперь приступаем собственно к разработке часов. Запускаем оболочку ARDUINO на компьютере. Попробуем поиграться с 1602 для проверки правильности соединений схемы. Заходим Файл-Примеры-LiqidCrystal и выбираем любой из файлов. Заливаем скетч в ARDUINO и наблюдаем что происходит. Если вместо символов вы видите черные квадратики- подкрутите подстроечный резистор, это регулятор контрастности (так же поступите если вообще ничего не отображается). Информация должна отображаться корректно и никаких «кракозябров» быть просто не должно. Если они появились- проверьте схему соединений, где то собрали неправильно. Можете сразу посмотреть в скетчах как обращаться к ЖК- индикатору и поразится простоте работы с ним! Если все у вас заработало правильно переходим непосредственно к программированию.
Определимся что таймер у нас будет работать без оператора delay как написано здесь. Поэтому вводим такой код:
int h,m,s; // переменные для часов, минут, секунд
boolean z; // переменная для отображения точки
// подключаем библиотеку индикатора
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// Variables will change :
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was updated
const long interval = 1000; // interval at which to blink (milliseconds)
void setup() {
lcd.begin(16, 2);
}
void loop()
{
unsigned long currentMillis = millis();
if(currentMillis — previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
s++; // добавляем единицу, равносильно записи s=s+1;
}
}
Данный код уже будет работать но ничего отображать не будет. К переменной s каждую секунду будет добавляться 1. Т.е. мы уже получили точный интервал в 1 секунду! Теперь, следуя алгоритму, нам необходим предел переменной между 0 и 59. Делаем.
if (s>59) // если значение s больше 59
{
s=0; // присваиваем значение 0 переменной s
m++; // добавляем 1 к переменной m отвечающей за минуты
}
Добавляем этот код к программе. По описанию все понятно- если значение s больше 59 то присваиваем ей 0 и прибавляем 1 минуту в переменной m. На данный момент имеем полностью работающий секундный таймер и бесконечный (до 32768- максимальное значение переменной типа integer) счетчик минут. Теперь нужно таким же образом рассчитать минуты. Пишем следующее:
if (m>59) // если значения m больше 59
{
m=0; // присваиваем значение 0 переменной m
h++; // добавляем 1 к переменной h отвечающей за часы
}
Добавляем строки к программе. Она уже должна выглядеть так:
int h,m,s; // переменные для часов, минут, секунд
boolean z; // переменная для отображения точки
// подключаем библиотеку индикатора
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// Variables will change :
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was updated
const long interval = 1000; // interval at which to blink (milliseconds)
void setup() {
lcd.begin(16, 2);
}
void loop()
{
unsigned long currentMillis = millis();
if(currentMillis — previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
s++; // добавляем единицу, равносильно записи s=s+1;
// секция подсчета секунд
if (s>59) // если значение s больше 59
{
s=0; // присваиваем значение 0 переменной s
m++; // добавляем 1 к переменной m отвечающей за минуты
}
// секция подсчета минут
if (m>59) // если значение m больше 59
{
m=0; // присваиваем значение 0 переменной m
h++; // добавляем 1 к переменной h отвечающей за часы
}
}
}
В принципе все понятно. Осталось сделать обработку часов. Делаем. Дописываем после секции подсчета минут:
if (h>23) // если значение h больше 23
{
h=0; // присваиваем значение 0 переменной h
}
Все, часы готовы! Заливаем скетч и часы будут ходить как надо! Хочу обратить ваше внимание что считать они будут в 24- часовом формате. Попробуйте сами сделать 12- часовой формат. Осталось вывести информацию на ЖК- индикатор. Существует 2 пути по написания кода на вывод информации.
- Посчитать одни данные и сразу вывести
- Посчитать все данные и вывести все сразу.
Тут уж вы сами определитесь по какому пути вы пойдете. Если пойдете по первому пути то писать отображение информации надо сразу в секциях подсчета, если по второму- пишется блок после всех вычислений. Давайте пойдем по второму пути т.к. он более предпочтителен и более логичен (хотя, если честно сказать, мой тестовый скетч написан по первому пути). Итак, для передачи данных на индикатор 1602 применяются всего 2 команды:
lcd.setCursor(3, 0); // устанавливает курсор на 3 символ 0 строки (счет строк и символов идет от 0)
lcd.print(0); // печатаем (print- печать, учите аглицкий) 0
Есть еще команда lcd.clear; означающая очистку экрана но здесь мы ее можем не использовать.
Начинаем выводить информацию. Начнем с секунд (можете начать с любого значения, делайте как вам будет удобно). Устанавливаем курсор на 0 строку в 6 позицию и выводим значение секунд. Почему в 6 позицию спросите вы? Давайте представим следующее: формат отображения часов- 2 символа(часы), разделитель (допустим :), 2 символа (минуты), разделитель (:) и, наконец, секунды. Считаем с нулевой позиции: 0+2+1+2+1=6. Так как счет начинается с 0 то вычитаем из данных единицу (ноль тоже является числом), выходит 6-1=5. Столько занимает отображение часов и минут с разделителями, следующая позиция- секундная и она равна 5+1=6. Немного запутано но напишу следующее hh:mm:ss и посчитаем координаты сначала начиная от 0. Вот так и высчитываются координаты на индикаторах семейства 16хх. При данных условиях часы будут отображаться в верхнем левом углу, вы можете сменить расположение как вам удобно, можете даже ввести переменную и подбирая её подбирать нужное вам положение индикации. Ладно, пишем такие строки:
lcd.setCursor(6, 0); // устанавливает курсор на 6 символ 0 строки (счет строк идет от 0)
lcd.print(s); // печатаем данные из переменной s
Программа будет выглядеть так:
int h,m,s; // переменные для часов, минут, секунд
boolean z; // переменная для отображения точки
// подключаем библиотеку индикатора
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// Variables will change :
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was updated
const long interval = 1000; // interval at which to blink (milliseconds)
void setup() {
lcd.begin(16, 2);
}
void loop()
{
unsigned long currentMillis = millis();
if(currentMillis — previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
s++; // добавляем единицу, равносильно записи s=s+1;
// секция подсчета секунд
if (s>59) // если значение s больше 59
{
s=0; // присваиваем значение 0 переменной s
m++; // добавляем 1 к переменной m отвечающей за минуты
}
// секция подсчета минут
if (m>59) // если значение m больше 59
{
m=0; // присваиваем значение 0 переменной m
h++; // добавляем 1 к переменной h отвечающей за часы
}
// секция подсчета часов
if (h>23) // если значение h больше 23
{
h=0; // присваиваем значение 0 переменной h
}
// секция вывода информации
lcd.setCursor(6, 0); // устанавливает курсор на 7 символ 0 строки (счет строк идет от 0)
lcd.print(s); // печатаем данные из переменной s
}
}
Заливаем скетч и…. секунды начали отображаться!!! Только обратите внимание, при счете от 0 до 59- все нормально, но как только начинается следующая минута- начинают меняться десятки секунд вместо единиц секунд, т.е. время отображается некорректно. Давайте разберемся с этим. Мы указали программе жестко позицию 6,0, и она выводит данные точно в этой позиции не затирая то что находится после этой позиции. Выхода 2. Применить lcd.clear или описать отображение корректно, тем более при первом варианте будет довольно трудно привыкнуть к прыгающим разрядам секунд (далее минут и часов). Напишем обработчик корректного отображения. Какие условия будут в этой обработке? Давайте подумаем. Если секунд меньше 10 то пишем их значение в 7 позиции (6+1=7) и в 6 позиции пишем 0, если больше или равно 10- пишем в 6 позиции. Все довольно просто. Условие будет иметь следующий вид:
if (s<10) //если секунд меньше 10
{
lcd.setCursor(6, 0); // курсор в 6 позицию 0 строки
lcd.print(0); //печатаем 0
lcd.setCursor(7, 0); //курсор в 7 позицию 0 строки
lcd.print(s); //печатаем значение переменной s
}
else
{
lcd.setCursor(6, 0); //иначе курсор в 6 позицию 0 строки
lcd.print(s); // печатаем значение переменной s
}
lcd.setCursor(5, 0); // курсор в 5 позицию 0 строки
lcd.print(‘:’); // печатаем разделитель между секундами и минутами
Вставляем данный код вместо
lcd.setCursor(6, 0); // устанавливает курсор на 7 символ 0 строки (счет строк идет от 0)
lcd.print(s); // печатаем данные из переменной s
и радуемся уже полученному результату! Все отображается корректно! Кроме того перед секундами появился разделитель «:»! Таким же образом пишем обработчик для минут и часов с соответствующими координатами курсора. Это может выглядеть так для минут:
if (m<10)
{
lcd.setCursor(3, 0);
lcd.print(0);
lcd.setCursor(4, 0);
lcd.print(m);
}
else
{
lcd.setCursor(3, 0);
lcd.print(m);
}
lcd.setCursor(2, 0); // курсор в 2 позицию 0 строки
lcd.print(‘:’); // печатаем разделитель между минутами и часами
и так для часов:
if (h<10)
{
lcd.setCursor(0, 0);
lcd.print(0);
lcd.setCursor(1, 0);
lcd.print(h);
}
else
{
lcd.setCursor(0, 0);
lcd.print(h);
}
Наша программа примет следующий вид:
int h,m,s; // переменные для часов, минут, секунд
boolean z; // переменная для отображения точки
// подключаем библиотеку индикатора
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// Variables will change :
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was updated
const long interval = 1000; // interval at which to blink (milliseconds)
void setup() {
lcd.begin(16, 2);
}
void loop()
{
unsigned long currentMillis = millis();
if(currentMillis — previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
s++; // добавляем единицу, равносильно записи s=s+1;
// секция подсчета секунд
if (s>59) // если значение s больше 59
{
s=0; // присваиваем значение 0 переменной s
m++; // добавляем 1 к переменной m отвечающей за минуты
}
// секция подсчета минут
if (m>59) // если значение m больше 59
{
m=0; // присваиваем значение 0 переменной m
h++; // добавляем 1 к переменной h отвечающей за часы
}
// секция подсчета часов
if (h>23) // если значение h больше 23
{
h=0; // присваиваем значение 0 переменной h
}
// секция вывода информации
// вывод секунд
if (s<10) //если секунд меньше 10
{
lcd.setCursor(6, 0); // курсор в 6 позицию 0 строки
lcd.print(0); //печатаем 0
lcd.setCursor(7, 0); //курсор в 7 позицию 0 строки
lcd.print(s); //печатаем значение переменной s
}
else
{
lcd.setCursor(6, 0); //иначе курсор в 6 позицию 0 строки
lcd.print(s); // печатаем значение переменной s
}
lcd.setCursor(5, 0); // курсор в 5 позицию 0 строки
lcd.print(‘:’); // печатаем разделитель между секундами и минутами
// вывод минут
if (m<10)
{
lcd.setCursor(3, 0);
lcd.print(0);
lcd.setCursor(4, 0);
lcd.print(m);
}
else
{
lcd.setCursor(3, 0);
lcd.print(m);
}
lcd.setCursor(2, 0); // курсор в 2 позицию 0 строки
lcd.print(‘:’); // печатаем разделитель между минутами и часами
// вывод часов
if (h<10)
{
lcd.setCursor(0, 0);
lcd.print(0);
lcd.setCursor(1, 0);
lcd.print(h);
}
else
{
lcd.setCursor(0, 0);
lcd.print(h);
}
}
}
Весь код уместился в каких то 3 с небольшим килоБайта! Из них бОльшую часть съела библиотека для ЖК индикатора. Сразу хочу заметить что этот код- только тело программы, нужно еще дописать функцию установки времени. Кроме того можно добавить фоторезистор и управлять яркостью подсветки дисплея. Можно дописать функцию ввода будильника и работать со звуком. Так же можно выводить температуру в помещении, дату и т.д… Вобщем данный индикатор с соответствующими датчиками может превратить данные часы в уникальное устройство! Единственный минус данного аппарата- при отключении электричества настраивать часы придется заново. Поэтому в ближайшее время опишу работу с модулем RTC, при работе с ним, даже если электричество отключится, при подаче напряжения часы будут работать как будто ничего и не произошло. Для более дешевой версии часов можно использовать arduino pro mini, это такой же контроллер только не имеет USB разъема но стоит в несколько раз дешевле и имеет очень маленькие размеры. Можно также применить и arduino nano, тот же про но с USB разъемом. До следующего урока. Всем спасибо за внимание.
PS*. Кстати процедуру отображения значений можно написать в виде отдельной функции и передавать необходимые значения в нее. Попробуйте так сделать и сравните объем занимаемой памяти. Всегда стремитесь к наименьшему объему.