Статьи и ноу хау

Статьи на компьтерные темы: Как самому сделать СНПЧ, Как сделать портейбл программу, Как собрать простой ламповый усилитель, Как работать в Windows 7 и др.

четверг, 3 апреля 2025 г.

Цифровой будильник на Arduino без каких-либо библиотек

Описание кода цифровых часов-будильника для Arduino


Этот код предназначен для создания простых цифровых часов с функцией будильника на платформе Arduino. Он использует 4-разрядный 7-сегментный индикатор для отображения времени и кнопки для управления настройками и функцией будильника. Динамическая индикация (мультиплексирование) реализована непосредственно в коде без использования внешних библиотек.

Рабочий эмулятор этого кода можно посмотреть на https://wokwi.com/projects/427022146745829377

#include <Arduino.h>

/**
   Arduino Digital Alarm Clock

This code is designed to create a simple digital clock with an alarm function on the Arduino platform. 
Dynamic display is used with a minimum of code and without any libraries.

*/
const int COLON_PIN = 13;
const int SPEAKER_PIN = A3;
const int dpins[] = {2, 3, 4, 5};
const int spins[] = {6, 7, 8, 9, 10, 11, 12};
const byte buttonModePin = A0;
const byte buttonIncPin = A1;
const byte setAlarmPin = A2;
const byte dpPin = A4;
unsigned long timer = 0;
int h = 0, m = 0, s = 0, ss = 0; // --- Time Variables ---
// --- Alarm Variables ---
int alarmH = 7, alarmM = 0;
bool alarmSet = false;
bool alarmEnabled = false;
bool alarmSounding = false;

const byte digitPatterns[10] = {
  B01000000, // 0
  B01111001, // 1
  B00100100, // 2
  B00110000, // 3
  B00011001, // 4
  B00010010, // 5
  B00000010, // 6
  B01111000, // 7
  B00000000, // 8
  B00010000  // 9
};
const byte dash = B00111111;

// --- State Machine for Setting Time/Alarm ---
enum Mode { DISPLAY_TIME, SET_HOUR, SET_MINUTE, SET_ALARM_HOUR, SET_ALARM_MINUTE, TOGGLE_ALARM };
Mode currentMode = DISPLAY_TIME;

// --- Multiplexing & Timing Variables ---
unsigned long lastMuxTime = 0;
const int muxDelay = 2; // Milliseconds per digit (adjust for brightness/flicker)
byte currentDigit = 0; // Which digit (0-3) is currently active

unsigned long lastSecondTime = 0;

void setup() {
for (int i = 0; i < 4; i++) pinMode(dpins[i], OUTPUT);
for (int i = 0; i < 7; i++) pinMode(spins[i], OUTPUT);
timer = millis();
pinMode(buttonModePin, INPUT_PULLUP);
pinMode(buttonIncPin, INPUT_PULLUP);
pinMode(setAlarmPin, INPUT_PULLUP);
pinMode(snoozePin, INPUT);
pinMode(COLON_PIN, OUTPUT);
pinMode(SPEAKER_PIN, OUTPUT);
pinMode(dpPin, OUTPUT);
digitalWrite(dpPin, HIGH);
}

void loop() {
  handleTime();
  handleButtons();
  if (!alarmSet) { displayTime(h, m);} else {displayAlarmTime(alarmH, alarmM);}
 // checkAlarm();
 // displayTime();
}

void handleTime() {
  if(millis() - timer >= 1000) {
    timer += 1000;
    s++;
    if(s >= 60) { s = 0; m++; }
    if(m >= 60) { m = 0; h++; }
    if(h >= 24) h = 0;
  }
}
  void handleButtons() {
   // Check alarm button (toggle alarm mode)
  static bool lastAlarmButtonState = HIGH;
  bool currentAlarmButtonState = digitalRead(setAlarmPin);
  if (lastAlarmButtonState == HIGH && currentAlarmButtonState == LOW) {
    delay(200); // debounce
  currentMode = (Mode)((currentMode + 1) % 6); 
    if (currentMode == SET_ALARM_HOUR || currentMode == SET_ALARM_MINUTE || currentMode == TOGGLE_ALARM) {
    alarmSet = true;} // ss = s + 10; setNewAlarm();
  }
  if (currentMode == TOGGLE_ALARM) { // Auto-toggle and move on
alarmEnabled = !alarmEnabled;
currentMode = DISPLAY_TIME; alarmSet = false;// Go back to display after toggle
}
  lastAlarmButtonState = currentAlarmButtonState;

  // Check alarm
  if (h == alarmH && m == alarmM && s < 10) {
  soundAlarm(); }

 if(digitalRead(buttonModePin) == LOW) { 
switch (currentMode) {
case SET_HOUR:
h = (h + 1) % 24;
delay(200);
break;
case SET_MINUTE:
m = (m + 1) % 60;
delay(200);
s = 0; // Reset seconds when setting minutes
break;
case SET_ALARM_HOUR:
alarmH = (alarmH + 1) % 24;
delay(200);
break;
case SET_ALARM_MINUTE:
alarmM = (alarmM + 1) % 60;
delay(200);
break;
case DISPLAY_TIME:
// Allow Inc button press in display mode to stop alarm if needed
// (already handled by the alarmSounding check above)
break;
// TOGGLE_ALARM mode is handled automatically by Mode button
}
}
}

  void displayTime(int h, int m) {
  bool blinkState = (millis() / 500) % 2; // On for 500ms, off for 500ms 
  byte digits[] = {digitPatterns[h/10], digitPatterns[h%10], digitPatterns[m/10], digitPatterns[m%10]};
  // Flash colon at 1Hz
 digitalWrite(COLON_PIN, s % 2); // Blink colon
  // digitalWrite(colonPin, (millis() % 1000) < 500);
  if (currentMode ==  SET_HOUR) {
  if(blinkState) {digits[0] = digitPatterns[h/10]; digits[1] = digitPatterns[h%10];} else {digits[0] = dash; digits[1] = dash;}
} else { digits[0] = digitPatterns[h/10]; digits[1] = digitPatterns[h%10];}
  if (currentMode ==  SET_MINUTE) {
  if(blinkState) {digits[2] = digitPatterns[m/10]; digits[3] = digitPatterns[m%10];} else {digits[2] = dash; digits[3] = dash;}
} else { digits[2] = digitPatterns[m/10]; digits[3] = digitPatterns[m%10];}
  // Display each digit in sequence with multiplexing
  for (int i = 0; i < 4; i++) { displayDigit(i, digits[i]);
    }
}

void displayDigit(int digit, byte pattern) {
  // Turn off all digits first
  for (int i = 0; i < 4; i++) { digitalWrite(dpins[i], LOW);
       for (byte j = 0; j < 7; j++) { digitalWrite(spins[j], HIGH);}
     }
  if (alarmEnabled) {   
  if (digit == 3) {digitalWrite(dpPin, LOW);} }  
  // Set segments according to digit pattern
  // byte pattern = digitPatterns[number];
  for (int i = 0; i < 7; i++) {
    digitalWrite(spins[i], bitRead(pattern, i));
  }
  // Turn on the current digit
    digitalWrite(dpins[digit], HIGH);
    digitalWrite(dpPin, HIGH);  
    delay(5); 
}

void displayAlarmTime(int alarmH, int alarmM) {
  bool blinkState = (millis() / 500) % 2; // On for 500ms, off for 500ms
  byte digits[] = {digitPatterns[alarmH/10], digitPatterns[alarmH%10], digitPatterns[alarmM/10], digitPatterns[alarmM%10]};
  if (currentMode ==  SET_ALARM_HOUR) {
  if(blinkState) {digits[0] = digitPatterns[alarmH/10]; digits[1] = digitPatterns[alarmH%10];} else {digits[0] = dash; digits[1] = dash;}
} else { digits[0] = digitPatterns[alarmH/10]; digits[1] = digitPatterns[alarmH%10];}
  if (currentMode ==  SET_ALARM_MINUTE) {
  if(blinkState) {digits[2] = digitPatterns[alarmM/10]; digits[3] = digitPatterns[alarmM%10];} else {digits[2] = dash; digits[3] = dash;}
} else { digits[2] = digitPatterns[alarmM/10]; digits[3] = digitPatterns[alarmM%10];}
  for (int i = 0; i < 4; i++) { displayDigit(i, digits[i]);}
  // {displayAlarmTime(dash, alarmM);} else {displayAlarmTime(alarmH, alarmM);}
  //  if (s == ss) {alarmSet = false;}
//}
}

void soundAlarm() {
  alarmSounding = true;
  tone(SPEAKER_PIN, 1000, 500);
  delay(500);
  tone(SPEAKER_PIN, 1000, 500);
  delay(500);
}
  /*  digitalWrite(SPEAKER_PIN, HIGH);
      delay(50);
      digitalWrite(SPEAKER_PIN, LOW);
      delay(50);*/

Ключевые особенности и компоненты:

Дисплей:

  • *   Используется 4-разрядный 7-сегментный индикатор. Судя по логике кода (`digitalWrite(dpins[i], HIGH)` для включения разряда и `digitalWrite(spins[i], bitRead(pattern, i))`, вероятно, управляющий сегментами через LOW для включения), это, скорее всего, индикатор с **общим анодом (ОА)**.
  • *   Реализована **динамическая индикация (мультиплексирование)**: Цифры на индикаторе загораются поочередно с высокой скоростью, создавая иллюзию непрерывного свечения. Это позволяет управлять дисплеем с меньшим количеством выводов Arduino по сравнению со статическим управлением.
  • *   Код написан **без использования внешних библиотек** для управления дисплеем.
  • *   Пины `dpins` (2, 3, 4, 5) управляют выбором активного разряда (подключение к общим анодам).
  • *   Пины `spins` (6-12) управляют отдельными сегментами (a-g) активного разряда.
  • *   Пин `COLON_PIN` (13) управляет светодиодом(ами) двоеточия между часами и минутами.
  • *   Пин `dpPin` (A4) управляет десятичной точкой, используемой здесь для индикации включенного будильника.

Время:

  • *   Переменные `h`, `m` и `s` хранят текущие часы, минуты и секунды.
  • *   Функция `handleTime()` использует `millis()` для неблокирующего отсчета времени и инкрементирует секунды, минуты и часы, обрабатывая переходы через 60 секунд/минут и 24 часа.

Будильник:

  • *   Переменные `alarmH` и `alarmM` хранят установленное время будильника (часы и минуты).
  • *   Флаг `alarmEnabled` (тип `bool`) указывает, активна ли функция будильника.
  • *   Флаг `alarmSet` (тип `bool`) используется внутри программы для временного переключения дисплея на отображение времени будильника во время его установки.
  • *   Флаг `alarmSounding` (тип `bool`) указывает, звучит ли будильник в данный момент (используется в закомментированной логике snooze).
  • *   Пин `SPEAKER_PIN` (A3) используется с функцией `tone()` для генерации звука будильника.
  • *   Функция `soundAlarm()` генерирует звуковые сигналы, когда текущее время (`h`, `m`) совпадает с временем будильника (`alarmH`, `alarmM`), будильник включен (`alarmEnabled`), и `s < 10` (звучит в течение первых 10 секунд целевой минуты).

 Управление (Кнопки):

  • * `buttonModePin` (A0): **Кнопка "+" (Инкремент)**. В режимах настройки (`SET_HOUR`, `SET_MINUTE`, `SET_ALARM_HOUR`, `SET_ALARM_MINUTE`), нажатие этой кнопки увеличивает значение мигающего параметра. * `buttonIncPin` (A1): **Объявлен, но не используется** в логике функции `handleButtons`. * `setAlarmPin` (A2): **Кнопка "Режим"**. Нажатие этой кнопки последовательно переключает режимы работы, определенные машиной состояний (`enum Mode`): * `DISPLAY_TIME`: Показывает текущее время. * `SET_HOUR`: Позволяет установить текущий час (цифры часов мигают). * `SET_MINUTE`: Позволяет установить текущую минуту (цифры минут мигают), сбрасывает секунды в 0. * `SET_ALARM_HOUR`: Позволяет установить час будильника (цифры часов будильника мигают). * `SET_ALARM_MINUTE`: Позволяет установить минуту будильника (цифры минут будильника мигают). * `TOGGLE_ALARM`: Переключает состояние будильника (`alarmEnabled`, вкл/выкл). Это происходит автоматически при входе в этот режим, и система немедленно возвращается в режим `DISPLAY_TIME`. * `snoozePin` (A3): **Объявлен, но его использование закомментировано**. Предназначался для функции "отложить будильник". **ВНИМАНИЕ:** Это назначение пина конфликтует с `SPEAKER_PIN`, также назначенным на A3. * Для кнопок "Режим" и "+" включены подтягивающие резисторы (`INPUT_PULLUP`), что означает, что кнопки должны быть подключены между пином Arduino и землей (GND). Нажатие кнопки переводит пин в состояние `LOW`. * Используется простое программное подавление дребезга контактов с помощью `delay(200)` после обнаружения нажатия кнопки.

Логика отображения:

  • *   Функция `displayTime()` показывает текущее время в формате ЧЧ:ММ. Двоеточие мигает каждую секунду (`s % 2`). Если активен режим `SET_HOUR` или `SET_MINUTE`, соответствующие цифры мигают (чередуются между числом и тире).
  • *   Функция `displayAlarmTime()` показывает установленное время будильника ЧЧ:ММ (без двоеточия) во время настройки будильника. Если активен режим `SET_ALARM_HOUR` или `SET_ALARM_MINUTE`, соответствующие цифры мигают.
  • *   Функция `displayDigit()` обрабатывает низкоуровневое мультиплексирование: она выключает все разряды, устанавливает паттерн сегментов для запрошенного `digit` на основе `pattern`, включает пин общего анода нужного разряда (`dpins[digit]`) и добавляет короткую задержку (`delay(5)`). Она также включает десятичную точку (`dpPin`) на последнем разряде (`digit == 3`), если будильник включен (`alarmEnabled`).
  • *   Массив `digitPatterns` хранит байтовые паттерны (битовые маски) для отображения цифр 0-9 на 7-сегментном индикаторе (вероятно, для общего анода, где 0 в бите означает, что сегмент ВКЛЮЧЕН).

Структура кода:


  • *   **Константы:** Определяют номера пинов и паттерны сегментов.
  • *   **Глобальные переменные:** Хранят время, состояние будильника, текущий режим и переменные тайминга.
  • *   **`setup()`:** Инициализирует режимы пинов и таймер.
  • *   **`loop()`:** Основной цикл программы вызывает функции для обработки обновлений времени (`handleTime`), входов кнопок (`handleButtons`) и обновлений дисплея (`displayTime` или `displayAlarmTime`).
  • *   **Вспомогательные функции:** `handleTime`, `handleButtons`, `displayTime`, `displayDigit`, `displayAlarmTime`, `soundAlarm`.

  • В целом, это рабочий пример цифровых часов с будильником, демонстрирующий прямое мультиплексирование 7-сегментного дисплея, обработку кнопок и отсчет времени на Arduino без внешних библиотек. Однако он имеет существенный конфликт пинов на A3.

Как пользоваться часами

  1. 1.  **Просмотр времени:** По умолчанию часы показывают текущее время (ЧЧ:ММ) с мигающим двоеточием. Если будильник включен, будет гореть десятичная точка на последнем разряде.
  2. 2.  **Вход в настройки / Смена режима:** Нажимайте кнопку **"Режим"** (подключенную к пину `A2`) несколько раз, чтобы циклически переключаться между режимами:
  3.     *   Отображение времени -> Установка часа -> Установка минуты -> Установка часа будильника -> Установка минуты будильника -> Вкл/Выкл будильника -> Отображение времени...
  4. 3.  **Установка значений:** Когда вы находитесь в режиме настройки (`Установка часа`, `Установка минуты`, `Установка часа будильника`, `Установка минуты будильника`), редактируемые цифры будут мигать. Нажимайте кнопку **"+" (Инкремент)** (подключенную к пину `A0`), чтобы увеличить мигающее значение (часы переключаются 0-23, минуты 0-59). При установке минут (`Установка минуты`) секунды сбрасываются в 0.
  5. 4.  **Включение/Выключение будильника:** Когда вы переходите в режим "Вкл/Выкл будильника", статус будильника (`alarmEnabled`) инвертируется (вкл->выкл или выкл->вкл), и часы немедленно возвращаются в режим "Отображение времени". Проверьте десятичную точку на последнем разряде, чтобы подтвердить статус будильника (горит = включен).
  6. 5.  **Звучание будильника:** Когда текущее время совпадает с включенным временем будильника, динамик на A3 будет издавать звуковые сигналы в течение первых 10 секунд.

Аппаратное обеспечение


ItemQuantityNotes
Arduino Uno R3 (or similar)1
4-Digit 7-Segment Display1Common Anode type is assumed based on code logic.
220Ω Resistor7Current limiting for segment pins (spins 6-12). Value may need adjustment.
1kΩ Resistor (approx)4Current limiting for common anode digit pins (dpins 2-5). Optional but recommended.
Piezo Speaker/Buzzer1For alarm sound (connect to A3 and GND).
Tactile Push button2 (or 3)Mode (A2), Increment (A0). Snooze button (A3) is optional/conflicting.
Jumper WiresVariousFor connections.

Important Considerations:

  • Common Cathode: If you use a Common Cathode display, you will need to significantly modify the code logic in displayDigit (invert dpins and spins logic) and potentially the digitPatterns.
  • Direct Drive Current: Directly driving segments and digits from Arduino pins can draw significant current, potentially exceeding pin or total Arduino limits, especially if multiple segments are lit. Using resistors on both segment and digit lines is crucial. For safer operation, consider using transistors (like PNPs for common anode) to drive the digits.
  • Pin Usage: This direct drive method uses many pins (7 segments + 4 digits + 1 colon + 1 decimal point = 13 pins just for the display).
  • Alternatives for Fewer Pins: To use fewer Arduino pins, consider using:
    • 74HC595 shift register (reduces segment pins).
    • A dedicated display driver IC like MAX7219 or TM1637 (often requires only 2-3 pins).
    • These alternatives would require different code and libraries.

Pin Connections

(Based on the code definitions. Verify with your specific 7-segment display datasheet. )

Arduino Uno PinDeviceDevice Pin / FunctionNotes
27-Segment Common AnodeDigit 1 Commondpins[0]
37-Segment Common AnodeDigit 2 Commondpins[1]
47-Segment Common AnodeDigit 3 Commondpins[2]
57-Segment Common AnodeDigit 4 Commondpins[3]
67-Segment CathodeSegment Aspins[0]
77-Segment CathodeSegment Bspins[1]
87-Segment CathodeSegment Cspins[2]
97-Segment CathodeSegment Dspins[3]
107-Segment CathodeSegment Espins[4]
117-Segment CathodeSegment Fspins[5]
127-Segment CathodeSegment Gspins[6]
137-Segment CathodeColonCOLON_PIN
A0Increment ButtonTo GNDbuttonModePin (Increments value), INPUT_PULLUP
A1Unused Button InputTo GNDbuttonIncPin (Declared but not read), INPUT_PULLUP
A2Mode ButtonTo GNDsetAlarmPin (Changes mode), INPUT_PULLUP
A3Speaker (+) SPEAKER_PIN (Output) 
A47-Segment CathodeDecimal Point (DP)dpPin (Indicates Alarm Enabled)
GNDButtons, Speaker (-), DisplayCommon Ground
5VDisplay Power (if needed)

  • Datasheet: The pin numbers (like Dig 1, Segment A, etc.) on your specific 7-segment display model will likely differ. Always consult the datasheet for your component to make the correct connections.