/*
* Лабораторна робота №1, варіант 11
* Спецефект на LD2 (PA6), зміна порядку кнопкою B1 (PA8)
*
* Прямий режим:
* 6 Гц - 2 с -> 9 Гц - 4 с -> 20 Гц - 10 с -> світить
* Зворотний режим:
* 20 Гц - 10 с -> 9 Гц - 4 с -> 6 Гц - 2 с -> вимкнений
*
* Кожне натискання B1 змінює режим на протилежний
* і запускає ефект спочатку в новому режимі.
*/
#include <Arduino.h>
// Піни
#define LED_LD2_PIN PA6 // Світлодіод LD2 на PA6
#define BUTTON_B1_PIN PA8 // Кнопка B1 на PA8
// Константи
#define DEBOUNCE_MS 30 // Антидребезг кнопки (мс)
#define STAGES_COUNT 3 // Кількість етапів блимання
// Опис одного етапу ефекту
typedef struct
{
uint8_t freq_hz; // Частота блимання (Гц)
uint32_t duration_ms; // Тривалість етапу (мс)
} Stage_t;
// Послідовності етапів для обох режимів
static const Stage_t STAGES_FORWARD[STAGES_COUNT] =
{ {6, 2000}, {9, 4000}, {20, 10000} };
static const Stage_t STAGES_REVERSE[STAGES_COUNT] =
{ {20, 10000}, {9, 4000}, {6, 2000} };
// Стан автомата
static bool reverse_mode = false; // true - зворотний
static bool effect_finished = false; // усі етапи пройдено
static uint8_t stage_idx = 0; // Поточний етап
static uint32_t stage_start_ms = 0; // Початок етапу
// Прототипи функцій
void StartEffect(void);
void UpdateEffect(void);
void PrintStage(void);
bool ButtonPressed(void);
void setup()
{
Serial.begin(115200);
Serial.println("=== Спецефект на світлодіоді LD2 ===");
Serial.println("Сергієнко Ю. С. КІ-231, 11-й варіант");
Serial.println();
pinMode(LED_LD2_PIN, OUTPUT); // PA6 - вихід
pinMode(BUTTON_B1_PIN, INPUT_PULLUP); // PA8 - вхід, pull-up
digitalWrite(LED_LD2_PIN, LOW);
StartEffect(); // Спочатку - прямий
}
void loop()
{
// Кнопка натиснута - змінити порядок ефекту
if (ButtonPressed())
{
reverse_mode = !reverse_mode;
StartEffect();
}
// Оновити стан світлодіода за поточним етапом
UpdateEffect();
}
/**
* Запуск ефекту з першого етапу в поточному режимі
*/
void StartEffect(void)
{
Serial.print("[");
Serial.print(millis());
Serial.print(" мс] Режим: ");
Serial.println(reverse_mode ?
"ЗВОРОТНИЙ (20 -> 9 -> 6 Гц, в кінці LED вимкнено)" :
"ПРЯМИЙ (6 -> 9 -> 20 Гц, в кінці LED світиться)");
stage_idx = 0;
stage_start_ms = millis();
effect_finished = false;
PrintStage();
}
/**
* Обчислення стану світлодіода та перехід між етапами
*/
void UpdateEffect(void)
{
if (effect_finished) return; // LED уже в кінцевому стані
const Stage_t *stages =
reverse_mode ? STAGES_REVERSE : STAGES_FORWARD;
uint32_t elapsed = millis() - stage_start_ms;
// Етап завершено - перейти до наступного
if (elapsed >= stages[stage_idx].duration_ms)
{
// Без накопичення похибки часу
stage_start_ms += stages[stage_idx].duration_ms;
stage_idx++;
if (stage_idx >= STAGES_COUNT)
{
effect_finished = true;
digitalWrite(LED_LD2_PIN, reverse_mode ? LOW : HIGH);
Serial.print("[");
Serial.print(millis());
Serial.println(reverse_mode ?
" мс] Ефект завершено: LED вимкнено" :
" мс] Ефект завершено: LED світиться постійно");
return;
}
PrintStage();
elapsed = millis() - stage_start_ms;
}
// Число півперіодів: парне - LED увімкнено, непарне - ні
uint32_t half_periods =
(elapsed * 2UL * stages[stage_idx].freq_hz) / 1000UL;
digitalWrite(LED_LD2_PIN, (half_periods % 2 == 0) ? HIGH : LOW);
}
/**
* Вивід інформації про поточний етап у Serial Monitor
*/
void PrintStage(void)
{
const Stage_t *stages =
reverse_mode ? STAGES_REVERSE : STAGES_FORWARD;
Serial.print("[");
Serial.print(millis());
Serial.print(" мс] Етап ");
Serial.print(stage_idx + 1);
Serial.print("/");
Serial.print(STAGES_COUNT);
Serial.print(": ");
Serial.print(stages[stage_idx].freq_hz);
Serial.print(" Гц, ");
Serial.print(stages[stage_idx].duration_ms / 1000);
Serial.println(" с");
}
/**
* Неблокуюча перевірка кнопки з антидребезгом
* @return true - нове натискання (спадний фронт)
*/
bool ButtonPressed(void)
{
static bool last_raw = HIGH;
static bool stable = HIGH;
static uint32_t last_change = 0;
bool raw = digitalRead(BUTTON_B1_PIN);
if (raw != last_raw)
{
last_raw = raw;
last_change = millis();
}
if ((millis() - last_change) >= DEBOUNCE_MS &&
raw != stable)
{
stable = raw;
if (stable == LOW) return true;
}
return false;
}