Примитивные часы. Два пути

Тренировка началася. Скетч дописал.

//UNO
//max7219

//Загружаем библиотеку max7219
#include "LedControl.h"                 
      //Рацветка шлейфов на индикацию:
      //Vсс -                 красный       
      //GND -                 черный
      //Din (dout) - пин 5 -  желтый
      //CS (load)  - пин 6 -  зеленый  
      //CLK        - пин 7 -  оранжевый
      //создаём объект класса LedControl (din,clk,cs,X)
      //четвертая цифра "X" - количество модулей
LedControl lc = LedControl(10, 8, 9, 0); 
int DesH=0; //Десятки часов
int OneH=0; //Единицы часов
int DesM=0; //Десятки минут
int OneM=0; //Единицы минут 
int DesS=0; //Десятки секунд
int OneS=0; //Единицы секунд 
int Hours=0; //Часы 
int Minutes=0; //Минуты 
int Seconds=0; //Секунды   
int period=1000; //Тик часов 
unsigned long second_previous; //Время последнего переключения секунд 

volatile int encoder_value=0; //Значение энкодера
volatile int button_value=0; //Значение кнопки
volatile boolean encoder_answer; //Ответ второй ножки энкодера  

void setup() 
{
  //Инициализация модуля max7219
  lc.shutdown(0, false); //Выводим из спящего режима
  lc.setIntensity(0,2);  //Яркость дисплея на 2. Всего возможных режимов яркости от 0 до 15
  lc.clearDisplay(0);    //Очистить дисплей   
  
  pinMode(2, INPUT_PULLUP);  //Пин кнопки на прерывании на вход 
  pinMode(3, INPUT_PULLUP);  //Пин энкодера на прерывании на вход 
  pinMode(5, INPUT_PULLUP);  //Пин энкодера на вход для работы
  Serial.begin (115200); //Активация порта
  attachInterrupt(1, ENCODER_TURN,FALLING); //Прерывание энкодера
  attachInterrupt(0, BUTTON_PRESS,FALLING); //Прерывание кнопки
  // Пишем исходное состояние часов
  lc.setDigit(0, 7, DesH, false);
  lc.setDigit(0, 6, OneH, false);
  // Пишем минуты
  lc.setDigit(0, 4, DesM, false);
  lc.setDigit(0, 3, OneM, false);
  // Пишем секунды  
  lc.setDigit(0, 1, DesS, false);
  lc.setDigit(0, 0, OneS, false);
  // Пишем дефисы
  lc.setChar(0, 5, '-', false);
  lc.setChar(0, 2, '-', false);
}
void loop() 
{
  if (button_value==0) // Сброс часов на 0
  {
    lc.setDigit(0, 7, 0, false);
    lc.setDigit(0, 6, 0, false);
    // Пишем минуты
    lc.setDigit(0, 4, 0, false);
    lc.setDigit(0, 3, 0, false);
    // Пишем секунды  
    lc.setDigit(0, 1, 0, false);
    lc.setDigit(0, 0, 0, false);
  }
  if (button_value==1) // Установка часов
  {
    Hours=encoder_value;
    if (Hours>24) {Hours=24;}
    if (Hours<0) {Hours=0;}
    OneH=Hours%10;
    DesH=(Hours/10)%10;
    lc.setDigit(0, 7, DesH, false);
    lc.setDigit(0, 6, OneH, false);
  }
  if (button_value==2) // Установка минут 
  {
    Minutes=encoder_value;
    if (Minutes>59) {Minutes=59;}
    if (Minutes<0) {Minutes=0;}
    OneM=Minutes%10;
    DesM=(Minutes/10)%10;
    lc.setDigit(0, 4, DesM, false);
    lc.setDigit(0, 3, OneM, false);
  }
  if (button_value==3) // Установка счекунд 
  {
    Seconds=encoder_value;
    if (Seconds>59) {Seconds=59;}
    if (Seconds<0) {Seconds=0;}
    OneS=Seconds%10;
    DesS=(Seconds/10)%10;
    lc.setDigit(0, 1, DesS, false);
    lc.setDigit(0, 0, OneS, false);
  }  
  if (button_value==4) // Запуск часов
  {
    if (millis()-second_previous>period)        //По прошествии N миллисек
    {
      Seconds++;
      if (Seconds>59) //Если секунд больше 59
      {
        Seconds=0;
        Minutes++;
      }
      if (Minutes>59) //Если минут больше 59                
      {
        Minutes=0;
        Hours++;
      }
      if (Hours>23) //Если часов больше 23                
      {
        Hours=0;
      }
      // Индикация времени
      //Переводим в единицы/десятки
      OneH=Hours%10;
      DesH=(Hours/10)%10;
      OneM=Minutes%10;
      DesM=(Minutes/10)%10;
      OneS=Seconds%10;
      DesS=(Seconds/10)%10;
      
      //Пишем часы
      lc.setDigit(0, 7, DesH, false);
      lc.setDigit(0, 6, OneH, false);
      // Пишем минуты
      lc.setDigit(0, 4, DesM, false);
      lc.setDigit(0, 3, OneM, false);
      // Пишем секунды  
      lc.setDigit(0, 1, DesS, false); 
      lc.setDigit(0, 0, OneS, false);
      second_previous=millis();     
    }
  }

}
void ENCODER_TURN ()//Обработка вращения энкодера
{
  encoder_answer=digitalRead (5);  
  if (encoder_answer==0)
  {
    encoder_value--;
    if (encoder_value<0) {encoder_value=0;}
    Serial.println (encoder_value);
  }
  else
  {
    encoder_value++;
    if (encoder_value<0) {encoder_value =0;}
    Serial.println (encoder_value);
  }  
}

void BUTTON_PRESS () //Обработка нажатия кнопки
{
  button_value++;
  if (button_value>4)  
  {
    button_value=0;  
  }
}

Пока для “тренировки” так. Сейчас просто установил показатели и в нужный момент дал старт. Посмотрим “точность”. Но есть вопрос: поставил “тик” часов (добавка секунд) как “int period=1000;”. Ну, дальше - как обычно: “if (millis()-second_previous>period) { }”
То ли мне кажется, то ли после пивка не соображаю. Может, надо было “int period>999;”?
Посмотрю, если будут отставать, то второе выражение верное…

137 строка не там - пока до неё дойдёт - уже много чего натикает !!!

“точность” в кавычках - это прикол или серьёзно?

Поясни. Часы работают, вроде (пока) :slight_smile:

  1. Что там может много чего натикать?
  2. Куды совать ее надо? Выше? Смысл?

Не прикол и не кавычки. Пока не разобрался, когда добавлять секунду: когда больше 1000 или когда больше 999?

Между 103 и 104,а еще лучше засечь текущий миллис до сравнения с периодом и период >999 или >=1000 …

Ну смотри, у тебя ж логика присутствует? При чем здесь точность к индикации? Точность - сама по себе, индикация - сама. Согласись?

Спойлер
//UNO
//max7219

//Загружаем библиотеку max7219
#include "LedControl.h"                 
      //Рацветка шлейфов на индикацию:
      //Vсс -                 красный       
      //GND -                 черный
      //Din (dout) - пин 5 -  желтый
      //CS (load)  - пин 6 -  зеленый  
      //CLK        - пин 7 -  оранжевый
      //создаём объект класса LedControl (din,clk,cs,X)
      //четвертая цифра "X" - количество модулей
LedControl lc = LedControl(10, 8, 9, 0); 
int DesH=0; //Десятки часов
int OneH=0; //Единицы часов
int DesM=0; //Десятки минут
int OneM=0; //Единицы минут 
int DesS=0; //Десятки секунд
int OneS=0; //Единицы секунд 
int Hours=0; //Часы 
int Minutes=0; //Минуты 
int Seconds=0; //Секунды   
int period=1000; //Тик часов 
unsigned long second_previous; //Время последнего переключения секунд 

volatile int encoder_value=0; //Значение энкодера
volatile int button_value=0; //Значение кнопки
volatile boolean encoder_answer; //Ответ второй ножки энкодера  

void setup() 
{
  //Инициализация модуля max7219
  lc.shutdown(0, false); //Выводим из спящего режима
  lc.setIntensity(0,2);  //Яркость дисплея на 2. Всего возможных режимов яркости от 0 до 15
  lc.clearDisplay(0);    //Очистить дисплей   
  
  pinMode(2, INPUT_PULLUP);  //Пин кнопки на прерывании на вход 
  pinMode(3, INPUT_PULLUP);  //Пин энкодера на прерывании на вход 
  pinMode(5, INPUT_PULLUP);  //Пин энкодера на вход для работы
  Serial.begin (115200); //Активация порта
  attachInterrupt(1, ENCODER_TURN,FALLING); //Прерывание энкодера
  attachInterrupt(0, BUTTON_PRESS,FALLING); //Прерывание кнопки
  // Пишем исходное состояние часов
  lc.setDigit(0, 7, DesH, false);
  lc.setDigit(0, 6, OneH, false);
  // Пишем минуты
  lc.setDigit(0, 4, DesM, false);
  lc.setDigit(0, 3, OneM, false);
  // Пишем секунды  
  lc.setDigit(0, 1, DesS, false);
  lc.setDigit(0, 0, OneS, false);
  // Пишем дефисы
  lc.setChar(0, 5, '-', false);
  lc.setChar(0, 2, '-', false);
}
void loop() 
{
  if (button_value==0) // Сброс часов на 0
  {
    lc.setDigit(0, 7, 0, false);
    lc.setDigit(0, 6, 0, false);
    // Пишем минуты
    lc.setDigit(0, 4, 0, false);
    lc.setDigit(0, 3, 0, false);
    // Пишем секунды  
    lc.setDigit(0, 1, 0, false);
    lc.setDigit(0, 0, 0, false);
  }
  if (button_value==1) // Установка часов
  {
    Hours=encoder_value;
    if (Hours>24) {Hours=24;}
    if (Hours<0) {Hours=0;}
    OneH=Hours%10;
    DesH=(Hours/10)%10;
    lc.setDigit(0, 7, DesH, false);
    lc.setDigit(0, 6, OneH, false);
  }
  if (button_value==2) // Установка минут 
  {
    Minutes=encoder_value;
    if (Minutes>59) {Minutes=59;}
    if (Minutes<0) {Minutes=0;}
    OneM=Minutes%10;
    DesM=(Minutes/10)%10;
    lc.setDigit(0, 4, DesM, false);
    lc.setDigit(0, 3, OneM, false);
  }
  if (button_value==3) // Установка счекунд 
  {
    Seconds=encoder_value;
    if (Seconds>59) {Seconds=59;}
    if (Seconds<0) {Seconds=0;}
    OneS=Seconds%10;
    DesS=(Seconds/10)%10;
    lc.setDigit(0, 1, DesS, false);
    lc.setDigit(0, 0, OneS, false);
  }  
  if (button_value==4) // Запуск часов
  {
    unsigned long cm=millis();
    if (cm-second_previous>=period)        //По прошествии N миллисек
    {
      Seconds++;
      if (Seconds>59) //Если секунд больше 59
      {
        Seconds=0;
        Minutes++;
      }
      if (Minutes>59) //Если минут больше 59                
      {
        Minutes=0;
        Hours++;
      }
      if (Hours>23) //Если часов больше 23                
      {
        Hours=0;
      }
      // Индикация времени
      //Переводим в единицы/десятки
      OneH=Hours%10;
      DesH=(Hours/10)%10;
      OneM=Minutes%10;
      DesM=(Minutes/10)%10;
      OneS=Seconds%10;
      DesS=(Seconds/10)%10;
      
      //Пишем часы
      lc.setDigit(0, 7, DesH, false);
      lc.setDigit(0, 6, OneH, false);
      // Пишем минуты
      lc.setDigit(0, 4, DesM, false);
      lc.setDigit(0, 3, OneM, false);
      // Пишем секунды  
      lc.setDigit(0, 1, DesS, false); 
      lc.setDigit(0, 0, OneS, false);
      second_previous=cm;     
    }
  }

}
void ENCODER_TURN ()//Обработка вращения энкодера
{
  encoder_answer=digitalRead (5);  
  if (encoder_answer==0)
  {
    encoder_value--;
    if (encoder_value<0) {encoder_value=0;}
    Serial.println (encoder_value);
  }
  else
  {
    encoder_value++;
    if (encoder_value<0) {encoder_value =0;}
    Serial.println (encoder_value);
  }  
}

void BUTTON_PRESS () //Обработка нажатия кнопки
{
  button_value++;
  if (button_value>4)  
  {
    button_value=0;  
  }
}

В вашем же варианте вы в 102 строке сравниваете с одним значением миллис, а в 137 берете уже другое значение миллис, которое может сильно отличаться от того что было в 102 строке пока вы там высчитываете секунды минуты часы и отображаете это !!! И еще эта разница у вас разная в зависимости от того натикала целая минута иди нет и натикал целый час или нет …

Ну так это индикация, установка. У ТС ведь дилема выбора как считать время. Установки - это потом.
Ой, увидел. Ну да ладно. Через часы нужно пройти каждому.)

Такая у меня мысль появилась, может админ или кто-нибудь из старожилов напишет на форуме Wiki статью с рекомендованными задачками для начинающих? Ну если им это интересно будет.

кому, админам или начинающим?

После написания прочитал и хотел исправить, я имел ввиду админам и старожилам будет ли интересно такую статью написать.

Судя по прошлому опыту, старожилам может и интересно - да только новичкам оно даром не надо, обычно.

Новичков обычно не интересует ничего, кроме их гениальной идеи ))

1 лайк

Лично мне было бы интересно потренироваться на этих задачах. (Да что ж я с начала пишу, а потом думаю что написал ) )
Как говориться, кто со мной согласен, ставьте лайки. )

1 лайк

Т.е. выбора нет.

2 лайка

Ну дизлайков здесь нет. )

Ну и мой вариант решения задачи.

Эмуляция в Wokwi - ссылка.

Текст скетча.
Комментарии лень было писать, извиняюсь.

Спойлер
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

constexpr uint8_t startSecond = 0;
constexpr uint8_t startMinute = 17;
constexpr uint8_t startHour = 18;

LiquidCrystal_I2C lcd(0x27,16,2);

void goSecond(uint8_t mySecond) {
   if ((mySecond < 10) || (mySecond == 0)) {
      lcd.setCursor(6,0);
      lcd.print("0");
      lcd.setCursor(7,0);
      lcd.print(mySecond);
   }
   else {
     lcd.setCursor(6,0);
     lcd.print(mySecond);
   }
}

void goMinute(uint8_t myMinute) {
  if ((myMinute < 10) || (myMinute == 0)) {
      lcd.setCursor(3,0);
      lcd.print("0");
      lcd.setCursor(4,0);
      lcd.print(myMinute);
   }
   else {
     lcd.setCursor(3,0);
     lcd.print(myMinute);
   }
}

void goHour(uint8_t myHour) {
  if ((myHour < 10) || (myHour == 0)) {
      lcd.setCursor(0,0);
      lcd.print("0");
      lcd.setCursor(1,0);
      lcd.print(myHour);
   }
   else {
     lcd.setCursor(0,0);
     lcd.print(myHour);
   }
}

void setup() {
  lcd.init();                        
  lcd.backlight();
  
  goSecond(startSecond);
  goMinute(startMinute);
  goHour(startHour);
}

void loop() {
  uint32_t currentMillis = millis();
  static uint32_t interval = 1000, previousMillis = 0;
  static uint8_t mySecond = 0;
  static uint8_t myMinute = 0;
  static uint8_t myHour = 0;
  static byte flgColons = 0;

  if(currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    flgColons = !flgColons;
    if (flgColons) {
      lcd.setCursor(2,0);
      lcd.print(":");
      lcd.setCursor(5,0);
      lcd.print(":");  
    }
    else {
      lcd.setCursor(2,0);
      lcd.print(" ");
      lcd.setCursor(5,0);
      lcd.print(" ");  
    } 
    if (mySecond < 59) {
       mySecond++;
       goSecond(mySecond);
    }
    else {
      mySecond = 0;
      goSecond(mySecond);
      if (myMinute < 59) {
          myMinute++;
          goMinute(myMinute);
      }
      else {
        myMinute = 0;
        goMinute(myMinute);
        if (myHour < 23) {
          myHour++;
          goHour(myHour);
        }
        else {
          myHour = 0;
          goHour(myHour);
        }
      }   
    }
  }
}

Как же всё же люди не ценят своё драгоценное время. У меня тоже есть 2 пути, - это поставить ds3231 за 100 рублей, и считывать от них время, или просто читать время с NTP сервера коих полно в инете. На сегодня - это самые приметивные часы, для начинающего IMHO

1 лайк

А я ставлю такой вариант как дублирующий, если RTC отвалиться и сеть.

Блин. Ну да, понятно. А других вариантов нет что ли? Например, нет 3231 за 100 рублей, и нет NTP сервера? И как жить дальше? Без "“приметивных” часов для начинающего?
А как дети 10 лет в школе учатся? Они тоже не ценят своё время?