Мерцание (переключение) 7 сегментных индикаторов

PavelLevin
Offline
Зарегистрирован: 15.12.2019

Привет всем, задавал этот вопрос в песочнице, но не был компетентен вообще, закидали меня и нечего не понял.. Сейчас рядом друг который больше меня шарит, но в моём вопросе помочь не смог, по-этому надеюсь очень на великие Ваши умы и отзывчивость.

Итак предистория: Пришла в голову идея на подарок жене - рамка которая показывает сколько лет, месяцев и дней мы вместе, и на этой же плате второй счётчик который показывает сколько дней в сумме вместе. По идее всё просто, часики на DS1307 и разницу в датах вывести на дисплей 7-ми сегментный.

Ссылки на видео работы готового устройства: 

1) https://www.youtube.com/watch?v=mg2ACzUWGNM

2) https://www.youtube.com/watch?v=eJ8YNu5g6b8

Заказал платы, напечатал корпус, всё огонь и дальше слабое место = прошивка. Тут я копипастил всё с любых примеров, пытался разобраться и объединить всё в одно, и как не странно с моими знаниями которые были равны нулю я реализовал задуманное, но с одной проблемой - это видимое переключение между индикаторами, то есть меняя значения delay в конце кода я наглядно вижу как быстрее или медленнее они переключаются. Если я ставлю значение уже в delayMicroseconds начинает тускнеть дисплей и ничего не разобрать и горит одна цифра отчетливо. 

Собирал всё в протеусе, там так же в симуляции тестил.

Получается индикаторы с общим анодом, всё на транзисторах.

Плата получилась такая:

Вот готовая с jlcpcb, качество отличное!

Вот и сам мой несчастный код:

#include <Wire.h>    //Libraries to communicate with RTC
#include "RTClib.h"

RTC_DS1307 rtc;      //create rtc object

int segs[] = {0, 1, 2, 3, 4, 5, 6, 7}; //abcdefg. segments
int digits[] = {12, 13, 10, 11, 8, 9}; //number of digits

byte num[]  = { B11000000, B11111001, B10100100, B10110000, B10011001, B10010010, B10000010, B11111000, B10000000, B10010000 };
int t, dm, Y, M, D;

DateTime dob = DateTime(2010, 6, 27, 0, 0, 0);
int t0 = dob.year() * 12 + dob.month() - 1;

void setup()
{
  rtc.begin(); //begin rtc communication
  //rtc.adjust(DateTime(2019, 12, 23, 20, 04, 0));

  for (int i = 0; i < 8; i++)
  {
    pinMode(segs[i], OUTPUT);//set segment pins output
  }
  for (int i = 0; i < 6; i++)
  {
    pinMode(digits[i], OUTPUT);//set digts as outputs
  }
}

void loop()
{
  DateTime now = rtc.now();
  t = now.year() * 12 + now.month() - 1;
  dm = t - t0;

  if (now.day() >= dob.day()) {
    Y = floor(dm / 12);
    M = dm % 12;
    D = now.day() - dob.day();
  } else {
    dm--;
    t--;
    Y = floor(dm / 12);
    M = dm % 12;
    DateTime tmp = DateTime(floor(t / 12), (t % 12) + 1, dob.day(), 0, 0, 0);
    D = (now.unixtime() - tmp.unixtime()) / 60 / 60 / 24;
  }

  printTime(D, M, Y);
}
void printNum(int number) //function to print number
{
  byte data = num[number];
  for (int i = 0; i < 8; i++)
  {
    if (data & 0x01)
      digitalWrite(segs[i], HIGH);
    else
      digitalWrite(segs[i], LOW);
    data >>= 1;
  }
}

void printTime(int D, int M, int Y)
{
  int d[] = { floor(D / 10), D - 10 * floor(D / 10), floor(M / 10), M - 10 * floor(M / 10), floor(Y / 10), Y - 10 * floor(Y / 10) };

  for (int i = 0; i < 6; i++)
  {
    if (i == 0) digitalWrite(digits[5], LOW);
    if (i != 0) digitalWrite(digits[i - 1], LOW);
    digitalWrite(digits[i], HIGH);
    printNum(d[i]);
    delayMicroseconds(200);
  }
}

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

1. вызовы floor - выкинуть, они бесполезны, т.к. используется целочисленная арифметика;

2. вместо digitalWrite - прямая работа с портами;

3. смысла дёргать часы чаще, чем раз в секунду - нету.

Это так, навскидку - предложения по первичной оптимизации. 

bwn
Offline
Зарегистрирован: 25.08.2014

Ну и динамическая индикация на форах, не самое умное. Таймеры. ИМХО.

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

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

Вы принципиально неверно походите к динамической индикации. При динамической индикации обновления сегментов должны происходить через равные промежутки времени такие, что полное обновление экрана происходило бы за 20 мс. Тогда экран у Вас будет обновляться с частотой 50Гц и никакого мерцания не будет. Хотите обновлять с частотой 100Гц - никто не запрещает, замените 20 на 10 в вышесказанном, но в любом случае "обновления сегментов должны происходить через равные промежутки времени".

Вы же за временем вообще не следите, а обновляете "когда получится".

ELITE
ELITE аватар
Offline
Зарегистрирован: 11.01.2018

а почему вы не хотите использовать для работы с индикаторами специально сделанные для этого драйвера?? например тот же МАХ7219 / МАХ7221 ???... работает по SPI... легко и без мерцаний будет обновлять все ваши экраны... (2 микрухи придется заюзать разве что....) 

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

Green
Offline
Зарегистрирован: 01.10.2015

ЕвгенийП пишет:

Вы принципиально неверно походите к динамической индикации. При динамической индикации обновления сегментов должны происходить через равные промежутки времени такие, что полное обновление экрана происходило бы за 20 мс. Тогда экран у Вас будет обновляться с частотой 50Гц и никакого мерцания не будет. Хотите обновлять с частотой 100Гц - никто не запрещает, замените 20 на 10 в вышесказанном, но в любом случае "обновления сегментов должны происходить через равные промежутки времени"

20 мс мало, лучше 10, в крайнем случае 12. Иначе будет мельтешение.

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Green пишет:
20 мс мало, лучше 10, в крайнем случае 12. Иначе будет мельтешение.
При 50fps? Да ладно Вам, ещё не так давно у половины мониторов обновление 50Гц было. Нормально там всё.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

ЕвгенийП пишет:

Green пишет:
20 мс мало, лучше 10, в крайнем случае 12. Иначе будет мельтешение.
При 50fps? Да ладно Вам, ещё не так давно у половины мониторов обновление 50Гц было. Нормально там всё.

Не было.

Практически у всех было 60 Гц. (кроме случаев когда вместо мониторов использовались отечественные телевизоры и рассчитанные на работу именно с ними компьютеры)

Потом требования к комфортной частоте обновления экрана увеличились до 72-85 Гц. (были и маркетинговые 100+ Гц)

А потом появились ЖК и частота обновления вернулась к 60 Гц.

Но, строго говоря, важна не сама частота обновления, а длительность паузы. В CRT мониторах длительность паузы практически равнялась периоду кадровой частоты. При 60 Гц она примерно равна 17 мс. Но если у нас, скажем 4-разрядный дисплей с динамической индикацией, то при 50 Гц (20 мс период) на длительность гашения каждого разряда будет приходиться по 15 мс (а 5 мс - на время горения). Т.е. реально уровень мерцания такого индикатора примерно такой же, как и у CRT дисплея с частотой обновления 67 Гц.

Для 8-разрядного, естественно, хуже: 17.5 мс и эффективные 57 Гц.

В общем, для прибора, на который не планируется смотреть постоянно, 50 Гц, конечно, достаточно, хотя никто не мешает сделать и чуть-чуть побольше.

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

andriano пишет:

В общем, для прибора, на который не планируется смотреть постоянно, 50 Гц, конечно, достаточно, хотя никто не мешает сделать и чуть-чуть побольше.

О чём я и написал ТС в #3

SegaKHV
Offline
Зарегистрирован: 13.03.2018

Давайте попробуем реже вызывать часы для начала

#include <Wire.h>    //Libraries to communicate with RTC
#include "RTClib.h"

RTC_DS1307 rtc;      //create rtc object

int segs[] = {0, 1, 2, 3, 4, 5, 6, 7}; //abcdefg. segments
int digits[] = {12, 13, 10, 11, 8, 9}; //number of digits
unsigned long next_blink;

byte num[]  = { B11000000, B11111001, B10100100, B10110000, B10011001, B10010010, B10000010, B11111000, B10000000, B10010000 };
int t, dm, Y, M, D;

DateTime dob = DateTime(2010, 6, 27, 0, 0, 0);
int t0 = dob.year() * 12 + dob.month() - 1;

void setup()
{
	rtc.begin(); //begin rtc communication
	//rtc.adjust(DateTime(2019, 12, 23, 20, 04, 0));
	
	for (int i = 0; i < 8; i++)
	{
		pinMode(segs[i], OUTPUT);//set segment pins output
	}
	for (int i = 0; i < 6; i++)
	{
		pinMode(digits[i], OUTPUT);//set digts as outputs
	}
}

void loop()
{
	if (((signed long)(millis() - next_blink)) > 0)
	{
		next_blink = millis() + 60000;	//	вызов раз в минуту
		
		DateTime now = rtc.now();
		t = now.year() * 12 + now.month() - 1;
		dm = t - t0;
		
		if (now.day() >= dob.day()) {
			Y = floor(dm / 12);
			M = dm % 12;
			D = now.day() - dob.day();
			} else {
			dm--;
			t--;
			Y = floor(dm / 12);
			M = dm % 12;
			DateTime tmp = DateTime(floor(t / 12), (t % 12) + 1, dob.day(), 0, 0, 0);
			D = (now.unixtime() - tmp.unixtime()) / 60 / 60 / 24;
		}
	}
	printTime(D, M, Y);
}
void printNum(int number) //function to print number
{
	byte data = num[number];
	for (int i = 0; i < 8; i++)
	{
		if (data & 0x01)
		digitalWrite(segs[i], HIGH);
		else
		digitalWrite(segs[i], LOW);
		data >>= 1;
	}
}

void printTime(int D, int M, int Y)
{
	int d[] = { floor(D / 10), D - 10 * floor(D / 10), floor(M / 10), M - 10 * floor(M / 10), floor(Y / 10), Y - 10 * floor(Y / 10) };
	
	for (int i = 0; i < 6; i++)
	{
		if (i == 0) digitalWrite(digits[5], LOW);
		if (i != 0) digitalWrite(digits[i - 1], LOW);
		digitalWrite(digits[i], HIGH);
		printNum(d[i]);
	//	delayMicroseconds(200);
	}
}

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

SegaKHV пишет:

Давайте попробуем реже вызывать часы для начала

И в строке 33 - опять. Интересно, когда-нибудь это закончится?