[sape_tizer]
Ср. Май 31st, 2023

Что и как строить на земельном участке или обо всем понемногу

Все о инструментах, строительстве, работах, АРДУИНО, электрика, электроника и многое другое

Урок 6. Часы на ARDUINO (основная часть)

Итак, после небольшого технического перерыва, продолжаем наше знакомство с семейством МК ARDUINO. В этом уроке мы попробуем сделать часы работающие от внутреннего генератора МК (с внешним генератором будет один из следующих уроков) и выводящего информацию на ЖК индикатор типа 1602 (что означает 16 символов в 2 строки, есть еще тип 1604- 16 символов в 4 строки, вы уже поняли что первые 2 цифры указывают на количество символов индикатора а вторые- на количество строк). Не будем затягивать вступление, переходим к работе.

Для проекта нам понадобится:

  1. Arduino Uno
  2. ЖК индикатор 1602
  3. Макетная плата
  4. Провода
  5. Подстроечный резистор на 10 кОм

Для особо ленивых советую опустится в низ страницы и скачать готовый скетч, для тех кто хочет научится делать скетчи самостоятельно опишу более подробно все шаги проекта. Для правильной и быстрой работы над проектом необходим алгоритм работы. Практически любой проект лучше накидать на бумаге и потом следовать по алгоритму шаг за шагом. Мы поступим абсолютно так же. Итак составляем алгоритм. У нас есть несколько условий, выпишем их в порядке возрастания:

  1. Секунды, работают в пределе от 0 до 59 по циклу с секундным интервалом (это понятно).
  2. Минуты, работают в пределе от 0 до 59 по циклу, переключение происходит при достижении значения секундами значения 0.
  3. Часы, работают в пределе от 0 до 24 (здесь вы можете выбрать отображение как в зарубежном стиле от 0 до 12 со значениями AM и PM, это как вам больше нравится) по циклу, переключение происходит по достижении значения минутами 0.
  4. Вывести всю необходимую информацию на дисплей (например вы можете решить не выводить секунды а сделать просто мигающую точку между часами и минутами)

Собираем наши часы по вот такой схеме:

1602 и ARDUINO
Подключение ЖК индикатора 1602 к ARDUINO

Советы по сборке. Индикатор 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 пути по написания кода на вывод информации.

  1. Посчитать одни данные и сразу вывести
  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*. Кстати процедуру отображения значений можно написать в виде отдельной функции и передавать необходимые значения в нее. Попробуйте так сделать и сравните объем занимаемой памяти. Всегда стремитесь к наименьшему объему.

PPS*. В продолжении темы по часам, как и обещал- подробное продолжение темы «Как сделать часы на Ардуино с кнопками с нуля» смотрите ДАЛЕЕ>>>

Добавить комментарий