#include "main.h"
#include "tm1637.h"
#include "keyboard.h"
volatile uint32_t tickCount;
uint32_t last_display_update;
uint16_t counter;
char lastKey;
uint32_t lastScanTime;
// ====== PIN & FSM ======
typedef enum {
ST_IDLE = 0, // ожидание
ST_INPUT_PIN, // ввод текущего пина
ST_VERIFY_PIN, // проверка
ST_GRANTED, // доступ разрешён
ST_DENIED, // доступ запрещён
ST_SETTINGS_WAIT2, // увидели первый '*', ждём второй
ST_SETTINGS_NEW, // ввод нового PIN
ST_SETTINGS_CONFIRM, // повтор нового PIN
ST_SETTINGS_SAVED // сохранено
} State;
// LED helpers (static inline + определения ДО первого использования)
static inline void leds_all_off(void) {
LED_OFF(LED_GREEN_PORT, LED_GREEN_PIN);
LED_OFF(LED_RED_PORT, LED_RED_PIN);
LED_OFF(LED_BLUE_PORT, LED_BLUE_PIN);
}
static inline void led_green_on(void){ LED_ON(LED_GREEN_PORT, LED_GREEN_PIN); }
static inline void led_green_off(void){ LED_OFF(LED_GREEN_PORT, LED_GREEN_PIN); }
static inline void led_red_on(void){ LED_ON(LED_RED_PORT, LED_RED_PIN); }
static inline void led_red_off(void){ LED_OFF(LED_RED_PORT, LED_RED_PIN); }
static inline void led_blue_on(void){ LED_ON(LED_BLUE_PORT, LED_BLUE_PIN); }
static inline void led_blue_off(void){ LED_OFF(LED_BLUE_PORT, LED_BLUE_PIN); }
static State state;
static char storedPIN[4] = {'1','2','3','4'}; // начальный PIN
static char inputBuf[4];
static uint8_t inputLen;
static char newPinBuf[4];
static uint8_t newPinLen;
static uint32_t lastActivityMs; // для таймаута
static const uint32_t TIMEOUT_MS = 10000; // 10 секунд бездействия
// утилиты
static inline void reset_input(void) { inputLen = 0; memset(inputBuf, 0, sizeof(inputBuf)); }
static inline void reset_newpin(void){ newPinLen = 0; memset(newPinBuf, 0, sizeof(newPinBuf)); }
static inline int pins_equal(const char a[4], const char b[4]) {
for (int i=0;i<4;i++) if (a[i]!=b[i]) return 0;
return 1;
}
static void show_input_stars(uint8_t n) { tm1637_display_asterisks(n); }
static void set_activity_now(void){ lastActivityMs = tickCount; }
static void go_idle(void) {
state = ST_IDLE;
reset_input();
reset_newpin();
leds_all_off();
tm1637_clear();
}
static void handle_key(char k) {
set_activity_now();
switch (state) {
case ST_IDLE:
if (k == '*') {
state = ST_SETTINGS_WAIT2; // ждём вторую '*'
led_blue_on();
} else if (k >= '0' && k <= '9') {
state = ST_INPUT_PIN;
reset_input();
inputBuf[inputLen++] = k;
show_input_stars(inputLen);
}
break;
case ST_SETTINGS_WAIT2:
if (k == '*') {
state = ST_SETTINGS_NEW;
reset_newpin();
tm1637_clear();
show_input_stars(0);
led_blue_on();
} else {
// не вторая '*': трактуем как начало обычного ввода PIN
state = ST_INPUT_PIN;
reset_input();
if (k >= '0' && k <= '9') {
inputBuf[inputLen++] = k;
show_input_stars(inputLen);
}
led_blue_off();
}
break;
case ST_INPUT_PIN:
if (k >= '0' && k <= '9') {
if (inputLen < 4) {
inputBuf[inputLen++] = k;
show_input_stars(inputLen);
}
} else if (k == '*') {
if (inputLen > 0) {
inputLen--;
inputBuf[inputLen] = 0;
show_input_stars(inputLen);
}
} else if (k == '#') {
if (inputLen == 4) {
state = ST_VERIFY_PIN;
if (pins_equal(inputBuf, storedPIN)) {
state = ST_GRANTED;
leds_all_off();
led_green_on();
} else {
state = ST_DENIED;
leds_all_off();
led_red_on();
}
}
}
break;
case ST_GRANTED:
case ST_DENIED:
// Любая клавиша — в idle
go_idle();
break;
case ST_SETTINGS_NEW:
// ввод нового PIN (4 цифры), '*' — backspace, '#' — продолжить к подтверждению
if (k >= '0' && k <= '9') {
if (newPinLen < 4) {
newPinBuf[newPinLen++] = k;
show_input_stars(newPinLen);
}
} else if (k == '*') {
if (newPinLen > 0) {
newPinLen--;
newPinBuf[newPinLen] = 0;
show_input_stars(newPinLen);
}
} else if (k == '#') {
if (newPinLen == 4) {
state = ST_SETTINGS_CONFIRM;
reset_input();
show_input_stars(0);
}
}
break;
case ST_SETTINGS_CONFIRM:
// повторяем ввод (в inputBuf), '*' — backspace, '#' — сохранить
if (k >= '0' && k <= '9') {
if (inputLen < 4) {
inputBuf[inputLen++] = k;
show_input_stars(inputLen);
}
} else if (k == '*') {
if (inputLen > 0) {
inputLen--;
inputBuf[inputLen] = 0;
show_input_stars(inputLen);
}
} else if (k == '#') {
if (inputLen == 4) {
if (pins_equal(newPinBuf, inputBuf)) {
memcpy(storedPIN, newPinBuf, 4);
state = ST_SETTINGS_SAVED;
leds_all_off();
led_green_on(); // подтверждение сохранения
} else {
state = ST_DENIED; // отказ (несовпадение)
leds_all_off();
led_red_on();
led_blue_off();
}
}
}
break;
case ST_SETTINGS_SAVED:
// Любая клавиша — выход в idle
led_blue_off();
go_idle();
break;
}
}
static void fsm_update(void) {
// Таймаут бездействия
if ((tickCount - lastActivityMs) > TIMEOUT_MS) {
go_idle();
return;
}
// Постобработка состояний
switch (state) {
case ST_GRANTED:
if ((tickCount - lastActivityMs) > 2000) {
go_idle();
}
break;
case ST_DENIED:
if ((tickCount - lastActivityMs) > 2000) {
go_idle();
}
break;
case ST_SETTINGS_SAVED:
if ((tickCount - lastActivityMs) > 1500) {
led_blue_off();
go_idle();
}
break;
default: break;
}
}
void osSystickHandler(void) {
tickCount++;
}
void initGPIO() {
// Включаем тактирование GPIOA и GPIOB
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN;
// PA5 (зелёный) как push-pull output
GPIOA->MODER = (GPIOA->MODER & ~(3U << (LED_GREEN_PIN * 2))) | (1U << (LED_GREEN_PIN * 2));
GPIOA->OTYPER &= ~(1U << LED_GREEN_PIN);
GPIOA->OSPEEDR |= (1U << (LED_GREEN_PIN * 2));
// PA9 (красный), PA8 (синий) как push-pull output
GPIOA->MODER = (GPIOA->MODER & ~(3U << (LED_RED_PIN * 2))) | (1U << (LED_RED_PIN * 2));
GPIOA->MODER = (GPIOA->MODER & ~(3U << (LED_BLUE_PIN * 2))) | (1U << (LED_BLUE_PIN * 2));
GPIOA->OTYPER &= ~((1U << LED_RED_PIN) | (1U << LED_BLUE_PIN));
GPIOA->OSPEEDR |= (1U << (LED_RED_PIN * 2)) | (1U << (LED_BLUE_PIN * 2));
// Гасим всё на старте
leds_all_off();
}
void initUSART2() {
// Включаем тактирование USART2
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
// Настраиваем PA2 (TX), PA3 (RX) в альтернативный режим AF1
GPIOA->MODER = (GPIOA->MODER & ~(0xF << 4)) | (0xA << 4);
GPIOA->AFR[0] = (GPIOA->AFR[0] & ~(0xFF << 8)) | (1 << 8) | (1 << 12);
// Настройка USART2: 115200@48MHz
USART2->BRR = 417; // 48e6 / 115200 ≈ 416.7
USART2->CR1 = USART_CR1_TE | USART_CR1_UE;
}
void initSysTick() {
SysTick->LOAD = 47999; // 1ms при 48MHz
SysTick->VAL = 0;
SysTick->CTRL = (1 << 2) | (1 << 1) | (1 << 0);
}
int _write(int file, uint8_t *ptr, int len) {
for (int i = 0; i < len; i++) {
while (!(USART2->ISR & USART_ISR_TXE));
USART2->TDR = ptr[i];
}
return len;
}
int main(void) {
initGPIO();
initUSART2();
initSysTick();
initKeyboard();
tm1637_init();
state = ST_IDLE;
lastActivityMs = tickCount;
tm1637_clear();
printf("Hello, %s!\n", "Wokwi Simulation");
while (1) {
tm1637_update(); // счётчик/лог — дисплей не перетирает
scanKeyboard(); // обновляет lastKey раз в 100 мс
// Обработка клавиши по фронту
static char prev = '\0';
if (lastKey != '\0' && lastKey != prev) {
handle_key(lastKey);
}
prev = lastKey;
fsm_update();
}
return 0;
}