// ======================================================
// Слово состояния программы
volatile unsigned int SYS_status;
#define SYS_KEYPRESS 0x0001 // бит нажатой кнопки
#define SYS_KEYRDY 0x0002 // бит готовности кнопки
#define SYS_LEDON 0x0004 // бит включенной подсветки
#define SYS_IN1 0x0008 // флаг включения входа 1
// ======================================================
// входы/выходы контроллера для работы с кнопками
// и коммутатором
// ------------------------------------------------------
// PD3 (D3) - вход прерывания кнопок
//
// PD4 (D4) - выход 0
// PD5 (D5) - выход 1
// PD6 (D6) - выход 2
// PD7 (D7) - выход 3
// -------------------------------------------------
// ячейки обработки кнопок с подавлением дребезга
// -------------------------------------------------
volatile int KEY_value; // состояние кнопки
int KEY_delay = 250; // задержка на дребезг
#define KEY_1 0x08 // маска для кнопки
#define OUT_LED 0x04 // маска подсветки
#define OUT_1 0x10 // маска аналогового входа 1
#define OUT_2 0x20 // маска аналогового входа 2
#define OUT_3 0x40 // маска аналогового входа 3
#define OUT_4 0x80 // маска аналогового входа 4
#define TIM2_DIVIDER 0x07 // предделитель таймера 2 (1024)
#define TIM2_MASK 0x02 // маска прерывания таймера 2
volatile int LED_count; // счетчик таймера подсветки
#define LED_MAXON 20 // время включения подсветки
#define SET_TIME 8 // таймаут на включение режима установки
// =================================================
// -------------------------------------------------------------------------
void setup() {
// запрещаем прерывания перед настройкой
cli();
// сбрасываем слово состояния программы
SYS_status = 0;
// устанавливаем режим "выход" линий 4-7 порта D
DDRD = (DDRD | 0xf0);
// устанавливаем режим "выход" линии 2 порта B (подсветка)
DDRB = (DDRB | 0x04);
// сбрасываем счетчик таймера подсветки
LED_count = 0;
// +++++++++++++++++++++++++++++++++
// настройка режимов прерывания INT1
// +++++++++++++++++++++++++++++++++
// срабатывание по изменению уровня на входе INT1 (PD3)
EICRA = (0 << ISC11) | (1 << ISC10);
EIMSK |= (1 << INT1); // разрешаем прерывания INT1
// конец настройки прерывания INT1
// +++++++++++++++++++++++++++
// настройки режимов таймера 2
// +++++++++++++++++++++++++++
TCCR2A = 0; // установить регистры в 0
TCCR2B = 0; // таймер остановлен
// устанавливаем режим CTC (сравнение с регистром OCR2A)
TCCR2A |= (0 << WGM20) | (1 << WGM21);
// установка регистра совпадения таймера OCR2A
OCR2A = KEY_delay;
// включение прерываний по сравнению (CTC)
TIMSK2 = TIM2_MASK;
// конец настройки прерывания таймера 2
// +++++++++++++++++++++++++++++++++++++
// Настройка сторожевого таймера 0.5 sec
// +++++++++++++++++++++++++++++++++++++
// необходимо для изменения делителя таймера
WDTCSR = (1 << WDCE) | (1 << WDE);
// установка делителя 0,5 сек.
WDTCSR = (0 << WDP3) | (1 << WDP2) | (0 << WDP1) | (1 << WDP0);
// разрешение прерываний таймера
WDTCSR |= (1 << WDIE);
// конец настройки прерывания сторожевого таймера
// +++++++++++++++++++++++++++++++++++++
// включаем по умолчанию аналоговый вход 1
PORTD |= OUT_1;
SYS_status |= SYS_IN1;
// разрешаем прерывания после настройки
sei();
}
// -------------------------------------------------------------------------
void loop() {
// проверка на срабатывание кнопки
// -------------------------------
if ((SYS_status & SYS_KEYRDY) != 0)
{
// сбрасываем готовность кнопок
SYS_status &= ~SYS_KEYRDY;
// если кнопка нажата, включаем таймер подсветки
if ((KEY_value & KEY_1) == 0)
{
// включаем подсветку
PORTB |= OUT_LED;
// устанавливаем бит включенной подсветки
SYS_status |= SYS_LEDON;
// устанавливаем бит нажатой клавиши
SYS_status |= SYS_KEYPRESS;
LED_count = 0;
}
else
// сбрасываем бит нажатой клавиши
SYS_status &= ~SYS_KEYPRESS;
}
// проверяем включена ли подсветка
// -------------------------------
if ((SYS_status & SYS_LEDON) != 0)
{
if (LED_count >= LED_MAXON)
{
PORTB &= ~OUT_LED;
SYS_status &= ~SYS_LEDON;
}
}
// переключение входа при удержании кнопки SET_TIME/2 сек
// ------------------------------------------------------
if (((SYS_status & SYS_KEYPRESS) != 0) && (LED_count == SET_TIME) && ((SYS_status & SYS_LEDON) != 0))
{
// сначала гасим подсветку
PORTB &= ~OUT_LED;
SYS_status &= ~SYS_LEDON;
// если включен Вход 1
if ((SYS_status & SYS_IN1) != 0)
{
// отключаем вход 1
PORTD &= ~OUT_1;
// включаем вход 2
PORTD |= OUT_2;
SYS_status &= ~SYS_IN1;
}
else
{
// отключаем вход 2
PORTD &= ~OUT_2;
// включаем вход 1
PORTD |= OUT_1;
SYS_status |= SYS_IN1;
}
}
}
// ===================================
// Программа обработки прерывания INT1
// ===================================
ISR(INT1_vect) {
KEY_value = PIND; // сохраняем состояние кнопки
TCNT2 = 0; // обнуляем счетчик таймера 2
TCCR2B = TIM2_DIVIDER; // запускаем таймер с делителем 1024
}
// ============================================
// Программа прерывания таймера 2 по сравнению
// ============================================
ISR(TIMER2_COMPA_vect) {
TCCR2B = 0; // останавливаем таймер
SYS_status |= SYS_KEYRDY; // фиксируем состояние кнопки
}
// ==================================================
// Программа обработки прерывания сторожевого таймера
// ==================================================
ISR(WDT_vect) {
LED_count++;
}