Дисплей пропускает секунды

#define _LCD_TYPE 1  // для работы с I2C дисплеями
#include <microDS3231.h>
#include <LiquidCrystal_I2C.h>
//#include <LCD_1602_RUS_ALL.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
MicroDS3231 rtc;
unsigned long l_time;

void setup() {
  //Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("hi!");
}

void loop() {
  if (millis() - l_time > 1000){
    lcd.setCursor(0, 3);
    lcd.print("                    ");
    if (rtc.getSeconds() % 2 == 0) {
      lcd.setCursor(3, 3);
      lcd.print("Tick");

    } else {
      lcd.setCursor(9, 3);
      lcd.print("Tock");
    }
    выводим дату и время готовыми строками
    lcd.setCursor(3, 1);
    lcd.print(rtc.getTimeString());
    lcd.setCursor(3, 2);
    lcd.print(rtc.getDateString());
    l_time = millis();
  }

}

Проблема в том, что при указаном коде на диспле примерно каждые 13 секунд идет пропуск секунды.

Попробуй 34 строку между 18 и 19 перенести.

2 лайка

Спасибо, вродь помогло. А почему так лучше работает не подскажешь?

Ошибка накапливается. Но это не страшно, можно строку №34 повыше перенести.

Однако, лучше этого не делать, а использовать тот факт, что DS3231 умеет сама выдавать прерывание по смене секунды. Вам надо просто ловить это прерывание и спокойно показывать что Вам нужно. И при этом забыть как о страшном сне и о milllis и о накоплении ошибки.

Так будет концептуально правильно, а сейчас у Вас “через Альпы”.

1 лайк

Ошибка меньше накапливается, но всё равно накапливается и будет глючить, хотя и сильно реже. Как правильно делать я Вам уже сказал.

Это конечно зависит от резонатора, но реже приблизительно в 80000 раз или один раз в 2 часа.

чтобы не накапливалась совсем, строчку 34 надо переписать так:

l_time += 1000;

и тогда ее можно оставить на месте и никуда не переносить.

1 лайк

А если коротко, как это реализовать? Или где про это почитать)

Да это без разницы в данной реализации. Всё равно частота миллис не точна в сравнении с DS3231. Но как вариант решения вполне.

1 лайк

DS3231 ногу INT/SQW запрограммировать на выдачу 1 секндных импульсов. Подключить к ноге с прерыванием МК в режиме input pullup. В подпрограмме прерывания поднимать флаг секунды. В основной программе вместо проверки миллис проверять флаг. Если поднят опустить. Остальное без изменений.

1 лайк

А вот ни фига. Тогда, даже в идеальном случае, “секунда” котроллера будет равна секунде + время на всю байду с 19-ой по 34-ую строки. И это будет накапливаться, т.е. “байда” будет прибавляться каждую секунду. Со временем накопится столько, что показометр просто пропустит секунду и покажет следующую. И так будет повторяться.

Правильно делать через прерывание от RTC.

Ну, Вам уже ответили. Только не забудьте пин SQW притянуть к питанию. И в прерывании сначала разрешите другие прерывания, а потом читайте часы (ну или читайте вне прерывания) иначе у Вас I2C подвиснет.

Это по любому самый верный поход. Например, при таком подходе у Вас секунда будет сменяться на экране тогда же, когда она сменяется в часах А при том подходе, что был у Вас (даже если решить все проблемы с накоплением погрешности) никто её не мешает сменяться посерёдке (т.е. сменяется в часах, через полсекунды - на экране). Вы же там никак к смене секунды в часха не привязываетесь.

А есть это всё оформленное где-то? Как бы вопрос принципиальный - есть модуль часов “точно” дающий данные времени, но вот загвоздка, когда его об этом спрашивать? С выводом SQW он сам командует, но прерывания… без них можно?, аналог - вечная тема кнопок :slight_smile:

видимо да, пробовал перемещать millis, стало лучше, одна ошибка в три минуты, но всё равно бесит) не подскажете, куда копать в смысле настройки SQW ?

Шмякните вместо 1000, 334 к примеру.

сдается мне, вы совершаете ту же ошибку, как в вопросе, нагонит ли таймер 20 секунд.
Обратите внимание, что я предлагаю увеличивать отметку времени от ПРЕЖНЕЙ ровно на 1 сек. Этот отсчет не будет зависеть от длительности кода между 19 и 34 строками (ну по крайней мере до тех пора, пока длительность этого кода меньше секунды).

А что запретили уже алгоритмы типа:

if(millis()-lastTick>=1000){
flag=1;
last tick=millis();
}
if(flag){
//Делаем всю лабуду
flag=0;
}

А RTC можно просто каждый цикл или раз в 1-10мс опрашивать…
???

if (millis() -Y > T){
Y=millis();
}
насколько они альтернативны?, не учитывая переполнение.
if (millis()  > Y){
Y=Y+T;
}

Если не трудно, подскажите, как самым простым способом в изначальный код прикрутить счетчик секунд через SQW… Уже задолбался искать, везде разные методы с кучей библиотек, и пока не заработало. Как понимаете, в тему только вхожу

А вот это из отсюда, нее?

#include "Wire.h"
#include "wiring.c"
void setup() {
Wire.begin();
Wire.beginTransmission(0x68);
Wire.write(0x0E); // Control Register
Wire.write(B01000000); // 1 Hz Out
Wire.endTransmission();
Serial.begin(9600);
attachInterrupt(0, herz, FALLING);
}

void loop() {
Wire.beginTransmission(0x68);
Wire.write(0x00);
Wire.endTransmission();
Wire.requestFrom(0x68,3);
if(Wire.available()) {
  byte secund = Wire.read(); //00h
  byte minut = Wire.read();  //01h
  byte chasov = Wire.read();  //02h
    Serial.print((chasov & B00110000)>>4);
  Serial.print(chasov & B00001111);
  Serial.write(':');
  Serial.print((minut & B11110000)>>4);
  Serial.print(minut & B00001111);
  Serial.write(':');
  Serial.print((secund & B11110000)>>4);
  Serial.print(secund & B00001111);
  Serial.print("  ");
  }
Serial.println (millis() );
}



void herz() {timer0_millis=0; }