Часы на больших семисегментных индикаторах

Novosdima

✩✩✩✩✩✩✩
14 Фев 2021
8
2
Всем привет. Нашел у себя в запасах большие семисегментные индикаторы с общим анодом, и пришла мне в голову мысль собрать на них часы, заодно подучится писать скетчи для Ардуино. Схему управления индикатором организовал на сдвиговом регистре 74HC595 для катодов, и усилил выход сборкой Дарлингтона ULN2003, Аноды усилил транзисторными ключами. Пока вроде все хорошо, но вот столкнулся с проблемой, при считывании показаний с датчика температуры DS18B20 индикаторы начинают дико моргать? Проблему пока не могу локализировать. Может что-то не так с динамической индикацией, так как пробовал писать её сам. За помощь и наставления на путь истинный буду премного благодарен. Прикрепляю к посту схему устройства и код. Заранее спасибо.

Schematic_arduino wach 75HC595_2021-03-02.pngphoto_2021-03-02_15-05-55.jpgphoto_2021-03-02_15-05-56.jpg

75ch595:
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>

#define latchPin  3  //Пин 8 подключен к ST_CP вход 74HC595
#define clockPin 4 //Пин 9 подключен к SH_CP входу 74HC595
#define dataPin  2  //Пин 11 подключен к DS входу 74HC595
#define pwmPin  9
#define SET_BUTTON A2
#define DOWN_BUTTON A1
#define UP_BUTTON A0
#define DS3231_I2C_ADDRESS 0x68
#define ONE_WIRE_BUS 12

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
byte hour_1, hour_2, minute_1, minute_2, seconds_1, seconds_2, temp_1, temp_2;
byte a;
byte bright;
unsigned long timer = 0;

const byte digitSegment[] = {
  0b01101111, 0b00000011, 0b01011101, 0b01010111, 0b00110011,
  0b01110110,  0b01111110, 0b01000011, 0b01111111, 0b01110111
};

byte decToBcd(byte val) {
  return ( (val / 10 * 16) + (val % 10) );
}
byte bcdToDec(byte val) {
  return ( (val / 16 * 10) + (val % 16) );
}
void setDateDs3231(byte second, // 0-59
                   byte minute, // 0-59
                   byte hour, // 0-23
                   byte dayOfWeek, // 1-7
                   byte dayOfMonth, // 1-28/29/30/31
                   byte month, // 1-12
                   byte year) // 0-99
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0);
  Wire.write(decToBcd(second));
  Wire.write(decToBcd(minute));
  Wire.write(decToBcd(hour));
  Wire.write(decToBcd(dayOfWeek));
  Wire.write(decToBcd(dayOfMonth));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.endTransmission();
}
void getDateDs3231(byte *second,
                   byte *minute,
                   byte *hour,
                   byte *dayOfWeek,
                   byte *dayOfMonth,
                   byte *month,
                   byte *year)
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0);
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
  *second = bcdToDec(Wire.read() & 0x7f);
  *minute = bcdToDec(Wire.read());
  *hour = bcdToDec(Wire.read() & 0x3f);
  *dayOfWeek = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month = bcdToDec(Wire.read());
  *year = bcdToDec(Wire.read());
}

void setup() {
  Serial.begin(9600);
  Wire.begin();
  sensors.begin();

  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(pwmPin, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode (A0, INPUT);
  pinMode (A1, INPUT);
  pinMode (A2, INPUT);
  pinMode (A3, INPUT);

  // Шим на 9 ногу 62,5 кГц
  TCCR1A = TCCR1A & 0xe0 | 1;
  TCCR1B = TCCR1B & 0xe0 | 0x09;
}

void loop() {
  autoBright ();
  showTemp ();
  // Динамическая индикация
  for (a = 5; a <= 8; a++) {
    digitalWrite(latchPin, LOW); // закрываем на "защелку" 74HC595
    showTime();
    digitalWrite(latchPin, HIGH); //открываем "защелку" 74HC595
    digitalWrite (a, HIGH);
    delay (1);
    digitalWrite (a, LOW);
  }
}

// показ времени
void showTime() {
  getDateDs3231(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  hour_1 = hour / 10;
  hour_2 = hour % 10;
  minute_1 = minute / 10;
  minute_2 = minute % 10;
  seconds_1 = second / 10;
  seconds_2 = second % 10;

  switch (a) {
    case 5: {
        shiftOut(dataPin, clockPin, MSBFIRST, digitSegment[hour_1]); // передаем байт в 74HC595
        break;
      }
    case 6: {
        shiftOut(dataPin, clockPin, MSBFIRST, digitSegment[hour_2]); // передаем байт в 74HC595
        break;
      }
    case 7: {
        shiftOut(dataPin, clockPin, MSBFIRST, digitSegment[minute_1]); // передаем байт в 74HC595
        break;
      }
    case 8: {
        shiftOut(dataPin, clockPin, MSBFIRST, digitSegment[minute_2]); // передаем байт в 74HC595
        break;
      }
  }

  // моргание двоиточием

  if (second % 2 != 0) {
    analogWrite(10, 1);
  } else
  {
    digitalWrite (10, LOW);
    //      analogWrite(10, 250);
  }

  //Обработка кнопок
  if (millis() - timer > 200) {
    if (!digitalRead(UP_BUTTON)) {
      second = 0;
      minute++;
      if (minute > 59) minute = 0;
      setDateDs3231(second, minute, hour, dayOfWeek, dayOfMonth, month, year);
      timer = millis();
    }

    if (!digitalRead(DOWN_BUTTON)) {
      int mins = minute;
      second = 0;
      mins--;
      if (mins < 0) mins = 59;
      minute = mins;
      setDateDs3231(second, minute, hour, dayOfWeek, dayOfMonth, month, year);
      timer = millis();
    }
    if (!digitalRead(SET_BUTTON)) {
      second = 0;
      hour++;
      if (hour > 23) hour = 0;
      setDateDs3231(second, minute, hour, dayOfWeek, dayOfMonth, month, year);
      timer = millis();
    }
  }
}


//Автояркость дисплея
void autoBright () {
  if (millis() - timer > 1000) {
    int val = map (analogRead(A3), 0, 1023, 0, 5);

    if (val <= 1) {
      bright = 250;
    }

    if (val > 1 && val <= 2) {
      bright = 180;
    }
    if (val > 2 && val <= 4) {
      bright = 70;
    }

    if (val >= 4 && val <= 5) {
      bright = 13;
    }
  }
  analogWrite(pwmPin, bright); //шим сигнал на вывод ОЕ 74HC595
}

//Считывание температуры
void showTemp () {
  float tempC = sensors.getTempCByIndex(0);
  Serial.println(tempC);
}
 

Вложения

PiratFox

★★★★★✩✩
13 Фев 2020
1,706
474
@Novosdima, навскидку, слишком часто опрашивается датчик температуры, что приводит к приостановке индикации. Попробуйте опрашивать хотя бы не чаще 1-го раза в 3 - 5 секунд. А вообще для таких индикаторов лучше юзать чип ТМ1637. Библиотеки под него есть, да и код проще получается.
 
Изменено:

Novosdima

✩✩✩✩✩✩✩
14 Фев 2021
8
2
@Novosdima, навскидку, слишком часто опрашивается датчик температуры, что приводит к приостановке индикации. Попробуйте опрашивать хотя бы не чаще 1-го раза в 1 - 3 секунды.
даже если опрашиваю раз в секунду, или реже, все равно моргает
 

bort707

★★★★★★✩
21 Сен 2020
3,057
910
Да и время нет никакого смысла обновлять в каждом проходе LOOP. Если индикатор часов 4х значный - значит обновлять время чаще 1 раза в минуту бессмысленно.
Что касается температуры - по-моему вы неправильно работаете с библиотекой. Прежде чем запрашивать температуру командой getTemp(), необходимо запустить конверсию методом RequestTemperatures(), а такой строчки я в вашем скетче вообще не нашел.
Смотрите примеры к библиотеке
 

PiratFox

★★★★★✩✩
13 Фев 2020
1,706
474
@Novosdima, вот Вам пример функции опроса датчика DS18b20 без задействования библиотеки Dallas.

C++:
//                    !!!   ОПРОС ДАТЧИКА ТЕМПЕРАТУРЫ DS18B20    !!!

  float getTempSensor()
  {
    byte reg[2];      // Резервировать 2 байта для считывания регистров ds18b20

    ds.reset();       // Сбросить сенсор DS18b20
    ds.write(0xCC);   // Отправить DS18b20 команду пропустить поиск по адресу, т.к. имеется только одно устройство
    ds.write(0x44);   // Отправить DS18b20 команду измерить температуру.

    delay(1000);      // Пауза для измерения

    ds.reset();          // Снова сбросить сенсор
    ds.write(0xCC);
    ds.write(0xBE);      // Отправить запрос на получение данных регистров со значением температуры
    reg[0] = ds.read();  // Получить младший байт значения температуры
    reg[1] = ds.read();  // Получить старший байт значения температуры
    
    float tDS =  ((reg[1] << 8) | reg[0]) * 0.0625; // Сформировать итоговое значение: сперва "склеить" данные регистров,
    //    затем умножить полученное на коэффициент, соответсвующий разрешающей способности (для 12 бит по умолчанию - 0,0625).
    return tDS;
 
Изменено:

Novosdima

✩✩✩✩✩✩✩
14 Фев 2021
8
2
Да и время нет никакого смысла обновлять в каждом проходе LOOP. Если индикатор часов 4х значный - значит обновлять время чаще 1 раза в минуту бессмысленно.
Что касается температуры - по-моему вы неправильно работаете с библиотекой. Прежде чем запрашивать температуру командой getTemp(), необходимо запустить конверсию методом RequestTemperatures(), а такой строчки я в вашем скетче вообще не нашел.
Смотрите примеры к библиотеке
Для опроса раз в 5 секунд добавил код, но в момент считывания данных индикатор на секунду гаснет

C++:
  if (millis() - timer > 5000) {
    sensors.requestTemperatures();
    float tempC = sensors.getTempCByIndex(0);
    Serial.println(tempC);
    timer = millis();
  }
 

bort707

★★★★★★✩
21 Сен 2020
3,057
910
Для опроса раз в 5 секунд добавил код, но в момент считывания данных индикатор на секунду гаснет

C++:
  if (millis() - timer > 5000) {
    sensors.requestTemperatures();
    float tempC = sensors.getTempCByIndex(0);
    Serial.println(tempC);
    timer = millis();
  }
меэжду строчками 2 и 3 нужно делать выдержку на 700мс. Если этого не делать - то библиотека, насколько я помню, вставляет эту задержку за вас и программа просто подвисает.
Что у вас и наблюдается.
 

Novosdima

✩✩✩✩✩✩✩
14 Фев 2021
8
2
меэжду строчками 2 и 3 нужно делать выдержку на 700мс. Если этого не делать - то библиотека, насколько я помню, вставляет эту задержку за вас и программа просто подвисает.
Что у вас и наблюдается.
Время на которое тухнет дисплей увеличилось на время делея

Всё правильно, опрос длится около 1 сек. Лучше юзать TM1637. В нём динамическая индикация реализована аппаратно.
TM1637 у меня не получилось усилить транзисторами для большого индикатора
photo_2021-03-02_15-51-35.jpg
 

bort707

★★★★★★✩
21 Сен 2020
3,057
910
Всё правильно, опрос длится около 1 сек. Лучше юзать TM1637. В нём динамическая индикация реализована аппаратно.
в данном случае достаточно выполнять запрос температуры в два этапа - сначала делаем requestTemperatures(); и сразу выходим. Мигаем индикаторами секунду, снова заходим в фунцию температуры и уже выполняем getTemp()
Время на которое тухнет дисплей увеличилось на время делея
Конечно :) Делеи тут не покатят, задержку надо делать на миллис и во время задержки продолжать обновлять динамическую индикацию, иначе дисплей потухнет
 

Novosdima

✩✩✩✩✩✩✩
14 Фев 2021
8
2
рт
Судя по фото, Ваш индикатор можно подключать без транзисторов.
для работы ему необходимо минимум 9 вольт

Судя по фото, Ваш индикатор можно подключать без транзисторов.
TM1637 не потянет такой индикатор

IMG_20210302_160455.jpg
 

poty

★★★★★★✩
19 Фев 2020
3,227
939
Динамическую индикацию стоит делать либо на millis(), либо на прерываниях.
Опрос термодатчика нужно разделять на как можно большее количество частей и опрашивать раз в 5-10 секунд.
Опрос DS3231 - тоже затратная штука, её желательно делать раз в 15-20 минут и разделять запросы (дату и прочие параметры можно получать гораздо реже), между этими запросами отображать время из внутреннего таймера.
Убрать все выводы в серийный порт или повысить его скорость, если они нужны.
 

Александр Симонов

★★★★✩✩✩
2 Авг 2018
727
208

Nikanor

★★✩✩✩✩✩
1 Окт 2020
178
51
а кто мешает вообще опрашивать даллас без библиотек)))))
в ванвае есть примеры работы.
делаете без делеев и радуетесь жизни.
 

PiratFox

★★★★★✩✩
13 Фев 2020
1,706
474
@Nikanor, вот именно. Например так: сначала вызвать функцию измерения,
Измерение температуры:
 void measureTemp()
  {
    ds.reset();       // Сбросить сенсор DS18b20
    ds.write(0xCC);   // Отправить DS18b20 команду пропустить поиск по адресу, т.к. имеется только одно устройство
    ds.write(0x44);   // Отправить DS18b20 команду измерить температуру.
    return;
  }
а по прошествии 700 - 1000 mS считать данные из регистров сенсора.
Получение данных:
 float getTempSensor()
  {
    byte reg[2];      // Резервировать 2 байта для считывания регистров ds18b20

    ds.reset();          // Сбросить сенсор
    ds.write(0xCC);
    ds.write(0xBE);      // Отправить запрос на получение данных регистров со значением температуры
    reg[0] = ds.read();  // Получить младший байт значения температуры
    reg[1] = ds.read();  // Получить старший байт значения температуры
      
    float tDS =  ((reg[1] << 8) | reg[0]) * 0.0625; // Сформировать итоговое значение: сперва "склеить" данные регистров,
    //    затем умножить полученное на коэффициент, соответствующий разрешающей способности (для 12 бит по умолчанию - 0,0625).
    return tDS;
  }
 
Изменено:

Старик Похабыч

★★★★★★★
14 Авг 2019
4,262
1,300
Москва
@PiratFox, Правильное решение. Но делать в ракообразном порядке.
Надо при старте сразу запросить новые данные. Выждать секунду.
А в цикле уже будут готовые данные, их считать и тут же послать новый запрос.
Следующее чтение не менее чем через секунду. Но это если читать данные раз в секунду.
А если надо сильно реже, то надо отправлять запрос, запоминать время и уже считывать по готовности.
Есть еще вариант. Если не нужны такие точности, то снизить ее, эту точность. Там все 95 мс при для целых градусов всего будет, против 760 самых точных. Но все равно будет видно моргание.
 

PiratFox

★★★★★✩✩
13 Фев 2020
1,706
474
Но делать в ракообразном порядке.
Полностью согласен.
А в цикле уже будут готовые данные, их считать и тут же послать новый запрос.
Ну или перед выходом из функции getTempSensor() вызывать measureTemp()
Там все 95 мс при для целых градусов всего будет, против 760 самых точных.
Дык это уже пусть делают кому как понравится.😁
 
Изменено:

Эдуард Анисимов

★★★★★★✩
23 Сен 2019
2,407
973
58
Марий-Эл
DS3231 имеет выход который можно настроить.
Я настраивал на 1 секунду и вызывал им прерывание.
  • дальше считывание температуры.
  • запуск измерения температуры
  • вкл/выкл точек.
  • считывание времени и вывод, если необходимо.
 

Novosdima

✩✩✩✩✩✩✩
14 Фев 2021
8
2
Еще вопрос по поводу организации динамической индикации в моём коде, если какие-то замечания и возможные улучшения?
 

hindenburg

✩✩✩✩✩✩✩
25 Сен 2023
1
0
добрый день, а можете выложить конечный вариант кода, а если еще и файл печатной платы вообще будет замечательно