【格院】EP Lab 4(project:智能储物柜监控系统(I2C/TEM102/STM32/MBED OS)

发布于:2025-06-15 ⋅ 阅读:(17) ⋅ 点赞:(0)

一、重要注意事项 ⚠️ (务必优先阅读!)

在开始操作和进一步开发之前,请务必注意以下几点,以确保系统正常工作并避免损坏元器件:

  1. 共地 (Common Ground)

    • 所有模块(NUCLEO L432KC、ADXL345、TMP102、蜂鸣器、LED等)的GND引脚必须连接在一起,形成共同的参考地。

  2. I2C 通信 (TMP102)

    • 上拉电阻:I2C总线的SDA和SCL线通常需要外部上拉电阻(如4.7kΩ)到VCC。

    • TMP102 地址 (ADD0 Pin):代码中地址为0x90 (对应7位地址0x48)。确保TMP102的ADD0引脚接地。

  3. SPI 通信 (ADXL345)

    • 片选 (CS):由微控制器 A3 引脚控制。

  4. ADXL345 传感器

    • 中断引脚 (INT1):连接到微控制器 D9,用于活动检测。

    • 中断清除:代码在处理活动中断后会读取INT_SOURCE (0x30)寄存器以清除硬件中断。

    • 灵敏度与量程:配置为全分辨率,±16g量程。

  5. TMP102 传感器

    • 工作模式:系统自动切换正常与关断模式。

    • 温度阈值:高低温阈值(35°C, 5°C)为例。

  6. 低功耗设计

    • MCU sleep() 功能已集成。代码中已加入进入和退出睡眠的打印信息,便于调试和演示。

  7. 硬件连接

    • 严格按照代码中的Mbed引脚定义连接硬件:

      • TMP102 I2C: SDA - D4, SCL - D5

      • ADXL345 SPI: MOSI - D11, MISO - D12, SCLK - D13 (与板载LD3/LED1共享), CS - A3

      • ADXL345 INT1: D9

      • 按钮1: A0; 按钮2: A1

      • 主警报LED: D2; 状态LED (锁定): D3; 蜂鸣器: D6

二、系统操作说明 🎛️

本智能储物柜系统通过两个按钮(按钮1 - Mbed A0,按钮2 - Mbed A1)进行操作,并通过LED和蜂鸣器提供状态反馈。

  1. 系统启动

    • 连接硬件并上电,系统自动初始化。串口监视器会输出初始化信息。

    • 默认进入 锁定空闲 (LOCKED_IDLE) 状态。status_led_locked (Mbed D3) 点亮。系统开始定期监测温度和倾斜,ADXL345活动检测激活。

  2. 密码输入与解锁/锁定

    • 预设密码:按钮1 -> 按钮2 -> 按钮1。

    • 进入密码输入模式:按任意密码按钮开始。

    • 输入反馈:每成功输入一位,status_led_locked (Mbed D3) 点亮0.5秒。

    • 密码正确:若当前锁定或处于警报状态则解锁 (status_led_locked 灭,监控暂停,警报清除);若当前解锁则锁定 (status_led_locked 亮,监控激活)。错误尝试计数清零。

    • 密码错误:若输入长度足够但不匹配,错误计数增加。

    • 连续输错3次:系统进入篡改警报状态 (LOCKED_TAMPER_ALERT),主警报LED (Mbed D2) 和蜂鸣器 (Mbed D6) 激活。需正确输入密码解除。

    • 密码输入超时 (10秒):恢复到输入前状态,错误计数清零。

  3. 锁定状态下的监控与警报

    • 温度监控 (默认每5秒):超出阈值(35°C高,5°C低)则进入 LOCKED_TEMP_ALERT,激活主警报。如果温度恢复正常,警报会自动清除,状态恢复到LOCKED_IDLE

    • 倾斜监控 (默认每0.5秒):检测到极端倾斜或翻转(XYZ任一轴>0.8g)则进入 LOCKED_TAMPER_ALERT,激活主警报。倾斜警报当前设计为需密码解锁清除。

    • 活动/震动监控 (中断驱动):检测到活动(>0.5g)则进入 LOCKED_TAMPER_ALERT,激活主警报。

  4. 解除警报

    • 在任何警报状态下,正确输入密码即可解除警报并解锁系统。

    • 温度警报在温度恢复正常时也会自动解除。

  5. MCU睡眠

    • LOCKED_IDLEUNLOCKED 状态且无待处理事件时,MCU自动进入低功耗睡眠模式,由中断唤醒。串口会打印进入和退出睡眠的信息。

三、程序重点功能

主要功能如下:

  1. 环境监控 (基于TMP102):通过I2C读取实时温度,具备模式控制和阈值警报功能,使用LowPowerTicker进行低功耗定期检查。警报在条件恢复后可自动清除。

  2. 防篡改检测 (基于ADXL345):通过SPI获取加速度数据,实现极端倾斜/翻转检测(定期检查,包含Z轴)和活动/震动检测(中断驱动)。

  3. 用户认证与交互:双按钮序列密码,带软件去抖、输入反馈LED、错误尝试限制及报警、输入超时处理。

  4. 实时反馈与警报:通过状态LED、主警报LED和蜂鸣器提供清晰的系统状态和警报指示。

  5. 系统状态管理:使用SystemState枚举管理多工作模式。

  6. 低功耗操作:集成MCU sleep(),利用EventFlagsLowPowerTickerLowPowerTimeout等Mbed OS API,并在适当时将传感器置于低功耗模式。包含电源状态日志。

四、核心代码逻辑详解

A. 初始化 (main, initialize_tmp102, initialize_adxl345)
  • main: 启动全局计时器;调用传感器初始化;配置按钮和ADXL345活动中断(ISR设置EventFlags);系统进入enter_locked_idle_state()(此函数启动LowPowerTicker)。

  • initialize_tmp102: 配置TMP102为正常模式。

  • initialize_adxl345: 配置SPI;设置ADXL345数据格式、活动检测参数、中断。

B. 传感器数据处理与业务逻辑 (read_and_check_temperature, check_adxl_tamper)
  • read_and_check_temperature:

    • 延时15ms确保转换。

    • I2C读取,正确进行12位有符号数据转换。

    • 与阈值比较,若异常则触发警报;若之前是温度警报且温度已恢复正常,则清除警报。

  • check_adxl_tamper:

    • SPI读取XYZ轴加速度。

    • 转换数据,比较X、Y、Z轴g值绝对值与EXTREME_TILT_THRESHOLD_G,若超限则触发警报。

C. 中断处理与事件管理 (ISRs, EventFlags, Callbacks)
  • 按钮 ISRs (user_buttonX_isr): 实现去抖,通过event_flags.set()设置标志。

  • ADXL345活动 ISR (adxl_activity_isr): 通过event_flags.set()设置标志。

  • LowPowerTicker 回调 (temp_check_callback, tilt_check_callback): 设置xxx_check_due布尔标志。

  • LowPowerTimeout 回调 (stop_digit_blink): 用于密码输入反馈LED。

D. 主循环与状态机 (mainwhile(1), update_system_state_and_alerts, process_passcode_input, 状态切换函数)
  • mainwhile(1):

    • 使用event_flags.wait_any(..., 0U, false)非阻塞检查事件。

    • 处理按钮事件。

    • 调用update_system_state_and_alerts

    • 根据条件调用sleep()

  • update_system_state_and_alerts:

    • 处理密码超时。

    • 若锁定相关状态:处理ADXL_ACTIVITY_FLAG;处理tilt_check_duetemp_check_due标志,调用相应检查函数。

    • 调用update_status_outputs

  • process_passcode_input: 管理密码输入,包括反馈、验证、错误计数和超时。

  • 状态切换函数 (enter_locked_idle_state, enter_unlocked_state): 设置系统状态,管理计时器和传感器模式。

E. 输出与反馈 (update_status_outputs)
  • 根据系统状态和密码输入反馈标志控制LED和蜂鸣器。

F. 警报处理 (trigger_alert, clear_alert)
  • trigger_alert: 设置警报标志并打印。

  • clear_alert: 清除警报标志,并在特定条件下将状态恢复到LOCKED_IDLE

五、代码示例

#include "mbed.h"       // 引入Mbed OS库,提供嵌入式开发所需的API
#include <cmath>        // 引入C++数学库,用于绝对值计算 (std::abs)
#include <vector>       // 引入C++标准库中的vector容器,用于存储密码序列

// --- TMP102 温度传感器 配置 ---
// 寄存器地址定义 (TMP102)
#define TMP102_REG_TEMP         0x00 // 温度数据寄存器地址
#define TMP102_REG_CONFIG       0x01 // 配置寄存器地址
// 传感器参数 (TMP102)
const int TMP102_I2C_ADDR = 0x90;   // TMP102 的I2C设备地址 (7位地址0x48左移一位得到写地址)
const float TMP102_TEMP_FACTOR = 0.0625f; // 温度转换因子 (每个LSB代表0.0625摄氏度)
// 温度警报阈值
#define TEMP_HIGH_THRESHOLD_C   35.0f // 储物柜过热警报阈值 (示例值)
#define TEMP_LOW_THRESHOLD_C    5.0f  // 储物柜过冷警报阈值 (示例值)

// --- ADXL345 加速度传感器 配置 ---
// 寄存器地址定义 (ADXL345)
#define ADXL345_REG_DEVID           0x00 // 设备ID寄存器地址 (值为0xE5)
#define ADXL345_REG_DATA_FORMAT     0x31 // 数据格式控制寄存器地址
#define ADXL345_REG_POWER_CTL       0x2D // 电源控制寄存器地址
#define ADXL345_REG_THRESH_ACT      0x24 // 活动检测阈值寄存器地址
#define ADXL345_REG_ACT_INACT_CTL   0x27 // 活动/非活动检测控制寄存器地址
#define ADXL345_REG_INT_ENABLE      0x2E // 中断使能控制寄存器地址
#define ADXL345_REG_INT_MAP         0x2F // 中断引脚映射寄存器地址
#define ADXL345_REG_INT_SOURCE      0x30 // 中断源寄存器地址 (只读, 读取此寄存器以清除中断状态)
#define ADXL345_REG_DATAX0          0x32 // X轴加速度数据的低字节(LSB)寄存器地址
// SPI 操作定义 (ADXL345)
#define ADXL345_SPI_READ_BIT        0x80 // SPI读操作时,地址字节的最高位(MSB)需要置1
#define ADXL345_SPI_MB_BIT          0x40 // SPI多字节读写操作时,地址字节的次高位需要置1
// 传感器常量 (ADXL345)
#define ADXL345_SCALE_FACTOR   0.0039f // 加速度转换因子 (单位: g/LSB, 在全分辨率模式下)
// 防篡改检测阈值 (ADXL345)
#define EXTREME_TILT_THRESHOLD_G 0.8f   // 定义为发生显著倾斜的g值阈值 (约等于 arcsin(0.8) ≈ 53度倾斜)
#define ACTIVITY_THRESHOLD_G     0.5f   // 活动检测的g值阈值 (例如0.5g的加速度变化被认为是一次活动)

// === 系统状态定义 ===
enum SystemState {
    LOCKED_IDLE,        // 状态: 储物柜已锁定 - 空闲监控中
    LOCKED_TEMP_ALERT,  // 状态: 储物柜已锁定 - 温度警报触发
    LOCKED_TAMPER_ALERT,// 状态: 储物柜已锁定 - 篡改警报触发
    UNLOCKED,           // 状态: 储物柜已解锁
    PASSCODE_ENTRY      // 状态: 正在输入密码
};
SystemState current_system_state = LOCKED_IDLE; // 系统启动后的初始状态为锁定空闲

// === 硬件接口定义 (使用Mbed OS的抽象引脚名称) ===
// TMP102 温度传感器
I2C tmp102_i2c(D4, D5);      // 初始化I2C接口对象,SDA连接到Mbed引脚D4, SCL连接到Mbed引脚D5

// ADXL345 加速度传感器
SPI adxl345_spi(D11, D12, D13);    // 初始化SPI接口对象: MOSI(D11), MISO(D12), SCLK(D13)
DigitalOut adxl345_cs(A3);      // 初始化ADXL345的片选(CS)控制引脚 (Mbed A3)
InterruptIn adxl_int1(D9, PullDown); // 初始化ADXL345的INT1中断输入引脚 (Mbed D9), 并启用内部下拉电阻

// 用户输入按钮
InterruptIn user_button1(A0, PullDown); // 初始化用户按钮1输入引脚 (Mbed A0), 启用内部下拉电阻
InterruptIn user_button2(A1, PullDown); // 初始化用户按钮2输入引脚 (Mbed A1), 启用内部下拉电阻

// 输出设备
DigitalOut main_alert_led(D2);  // 初始化主警报LED控制引脚 (Mbed D2)
DigitalOut buzzer(D6);          // 初始化蜂鸣器控制引脚 (Mbed D6)
DigitalOut status_led_locked(D3); // 初始化储物柜锁定状态指示LED控制引脚 (Mbed D3) - LED亮表示已锁定

// === 全局变量 ===
// TMP102 相关
char tmp102_config_buf[3];      // 用于TMP102配置命令的缓冲区 (地址+2字节数据)
char tmp102_temp_buf[2];        // 用于存储从TMP102读取的原始温度数据的缓冲区 (2字节)
float current_temperature_c = 25.0f; // 存储当前计算得到的摄氏温度值, 默认初始值为25.0
LowPowerTicker temp_check_ticker; // Mbed OS 低功耗定时器,用于定期触发温度检查
const chrono::milliseconds TEMP_CHECK_INTERVAL = 5000ms; // 定义温度检查的时间间隔 (5000毫秒 = 5秒)
volatile bool temp_check_due = false; // 标志位,表示是否到达了温度检查的时间点 (由temp_check_ticker的回调设置)

// ADXL345 相关
LowPowerTicker adxl_tilt_check_ticker; // Mbed OS 低功耗定时器,用于定期触发倾斜状态检查
const chrono::milliseconds ADXL_TILT_CHECK_INTERVAL = 500ms; // 定义倾斜检查的时间间隔 (500毫秒 = 0.5秒)
volatile bool tilt_check_due = false; // 标志位,表示是否到达了倾斜检查的时间点 (由adxl_tilt_check_ticker的回调设置)

// 密码系统与按键去抖相关
const std::vector<int> PASSCODE = {1, 2, 1}; // 预设的解锁/锁定密码序列 (按钮1 -> 按钮2 -> 按钮1)
std::vector<int> entered_passcode;          // 用于存储用户当前输入的密码数字序列
EventFlags event_flags;                     // Mbed OS 事件标志组,用于在线程间同步事件 (如按钮按下、传感器中断)
const uint32_t BUTTON1_FLAG = (1UL << 0);   // 定义按钮1事件的标志位 (bit 0)
const uint32_t BUTTON2_FLAG = (1UL << 1);   // 定义按钮2事件的标志位 (bit 1)
const uint32_t ADXL_ACTIVITY_FLAG = (1UL << 2); // 定义ADXL345活动中断事件的标志位 (bit 2)

Timer passcode_timeout_timer;               // Mbed OS 普通计时器,用于密码输入过程的超时计时
const chrono::seconds PASSCODE_ENTRY_TIMEOUT = 10s; // 密码输入超时时间 (10秒)
Timer button_debounce_timer;                // Mbed OS 普通计时器,用于按键去抖逻辑中的时间基准
const chrono::milliseconds DEBOUNCE_TIME = 200ms; // 按键去抖的最小间隔时间 (200毫秒)
volatile int64_t last_button1_press_time_ms_val = 0; // 记录按钮1上一次被确认为有效按下的时间戳 (毫秒)
volatile int64_t last_button2_press_time_ms_val = 0; // 记录按钮2上一次被确认为有效按下的时间戳 (毫秒)

int incorrect_passcode_attempts = 0;        // 密码错误尝试次数计数器
const int MAX_PASSCODE_ATTEMPTS = 3;        // 允许的最大密码错误尝试次数

// 密码输入反馈LED控制相关
LowPowerTimeout digit_blink_timeout;      // Mbed OS 低功耗超时对象,用于精确控制LED单次闪烁的持续时间
volatile bool digit_blink_active = false; // 标志位,指示密码输入反馈LED当前是否应处于点亮状态
const chrono::milliseconds DIGIT_BLINK_DURATION = 500ms; // 密码输入反馈LED点亮持续时间 (0.5秒)
void stop_digit_blink();                    // LowPowerTimeout的回调函数声明,用于在延时结束后清除digit_blink_active标志

// 警报状态相关
bool system_in_alert_state = false; // 标志位,指示系统当前是否处于任何类型的警报状态

// === 函数声明 ===
// TMP102 相关函数
void initialize_tmp102();               // 初始化TMP102传感器
void tmp102_config_normal_mode();       // 配置TMP102为正常(连续转换)模式
void tmp102_config_shutdown_mode();     // 配置TMP102为关断(低功耗)模式
void tmp102_set_temp_pointer();         // 设置TMP102的指针寄存器指向温度数据寄存器
bool read_and_check_temperature();      // 读取当前温度并检查是否超出警报阈值

// ADXL345 相关函数
void initialize_adxl345();              // 初始化ADXL345传感器
void adxl_write_reg(char reg_addr, char value); // 通过SPI向ADXL345指定寄存器写入单个字节
char adxl_read_reg(char reg_addr);      // 通过SPI从ADXL345指定寄存器读取单个字节
void adxl_read_multiple_regs(char start_reg_addr, char* buffer, int num_bytes); // 通过SPI从ADXL345指定起始地址连续读取多个字节
bool check_adxl_tamper();               // 检查ADXL345是否检测到篡改行为(当前主要指极端倾斜)

// 中断服务程序 (ISRs) 和回调函数
void user_button1_isr();                // 用户按钮1的上升沿中断服务程序
void user_button2_isr();                // 用户按钮2的上升沿中断服务程序
void adxl_activity_isr();               // ADXL345活动检测中断 (INT1引脚) 的中断服务程序
void temp_check_callback();             // temp_check_ticker (LowPowerTicker) 的回调函数
void tilt_check_callback();             // adxl_tilt_check_ticker (LowPowerTicker) 的回调函数

// 系统核心逻辑函数
void process_passcode_input(int button_id); // 处理密码输入及验证逻辑
void update_system_state_and_alerts();  // 更新系统状态、检查定时事件(温度、倾斜)并处理警报
void enter_locked_idle_state();         // 使系统进入“锁定并空闲监控”状态的逻辑
void enter_unlocked_state();            // 使系统进入“解锁”状态的逻辑
void trigger_alert(const char* reason); // 触发警报(激活声光提示)
void clear_alert();                     // 清除当前的警报状态
void update_status_outputs();           // 根据当前系统状态统一更新所有LED和蜂鸣器的输出


// === TMP102 函数实现 ===
void initialize_tmp102() {
    tmp102_config_normal_mode();
    tmp102_set_temp_pointer();
    printf("TMP102 初始化完成。\n\r");
}
void tmp102_config_normal_mode() {
    tmp102_config_buf[0] = TMP102_REG_CONFIG;
    tmp102_config_buf[1] = 0x60;
    tmp102_config_buf[2] = 0xA0;
    tmp102_i2c.write(TMP102_I2C_ADDR, tmp102_config_buf, 3);
}
void tmp102_config_shutdown_mode() {
    tmp102_config_buf[0] = TMP102_REG_CONFIG;
    tmp102_config_buf[1] = 0x61;
    tmp102_config_buf[2] = 0xA0;
    tmp102_i2c.write(TMP102_I2C_ADDR, tmp102_config_buf, 3);
}
void tmp102_set_temp_pointer() {
    tmp102_config_buf[0] = TMP102_REG_TEMP;
    tmp102_i2c.write(TMP102_I2C_ADDR, tmp102_config_buf, 1);
}
bool read_and_check_temperature() {
    tmp102_set_temp_pointer();
    ThisThread::sleep_for(15ms);
    if (tmp102_i2c.read(TMP102_I2C_ADDR, tmp102_temp_buf, 2) == 0) {
        unsigned short raw_temp_unsigned = ( (unsigned char)tmp102_temp_buf[0] << 4 ) | ( (unsigned char)tmp102_temp_buf[1] >> 4 );
        short final_raw_temp;
        if (raw_temp_unsigned & 0x0800) {
            final_raw_temp = raw_temp_unsigned | 0xF000;
        } else {
            final_raw_temp = raw_temp_unsigned;
        }
        current_temperature_c = final_raw_temp * TMP102_TEMP_FACTOR;

        if (current_temperature_c > TEMP_HIGH_THRESHOLD_C) {
            if (current_system_state != LOCKED_TEMP_ALERT) {
                trigger_alert("温度过高");
                current_system_state = LOCKED_TEMP_ALERT;
            }
            return true;
        } else if (current_temperature_c < TEMP_LOW_THRESHOLD_C) {
            if (current_system_state != LOCKED_TEMP_ALERT) {
                trigger_alert("温度过低");
                current_system_state = LOCKED_TEMP_ALERT;
            }
            return true;
        } else {
            if (current_system_state == LOCKED_TEMP_ALERT) {
                clear_alert();
            }
        }
    } else {
        printf("TMP102 读取失败!\n\r");
    }
    return false;
}

// === ADXL345 函数实现 ===
void initialize_adxl345() {
    adxl345_cs = 1;
    adxl345_spi.format(8, 3);
    adxl345_spi.frequency(2000000);
    adxl_write_reg(ADXL345_REG_DATA_FORMAT, 0x0B);
    float activity_thresh_lsb_float = ACTIVITY_THRESHOLD_G / 0.0625f;
    char thresh_act_val = static_cast<char>(activity_thresh_lsb_float);
    adxl_write_reg(ADXL345_REG_THRESH_ACT, thresh_act_val);
    adxl_write_reg(ADXL345_REG_ACT_INACT_CTL, 0x70);
    adxl_write_reg(ADXL345_REG_INT_ENABLE, 0x10);
    adxl_write_reg(ADXL345_REG_INT_MAP, 0x00);
    adxl_read_reg(ADXL345_REG_INT_SOURCE);
    adxl_write_reg(ADXL345_REG_POWER_CTL, 0x08);
    adxl_int1.rise(&adxl_activity_isr);
    printf("ADXL345 初始化完成。\n\r");
}
void adxl_write_reg(char reg_addr, char value) {
    adxl345_cs = 0;
    adxl345_spi.write(reg_addr & 0x7F);
    adxl345_spi.write(value);
    adxl345_cs = 1;
}
char adxl_read_reg(char reg_addr) {
    char value;
    adxl345_cs = 0;
    adxl345_spi.write(ADXL345_SPI_READ_BIT | (reg_addr & 0x7F));
    value = adxl345_spi.write(0x00);
    adxl345_cs = 1;
    if (reg_addr == ADXL345_REG_DEVID && value != 0xE5) {
        printf("ADXL345 SPI 读取DEVID错误! 期望0xE5, 得到0x%02X\n\r", value);
    }
    return value;
}
void adxl_read_multiple_regs(char start_reg_addr, char* buffer, int num_bytes) {
    adxl345_cs = 0;
    adxl345_spi.write(ADXL345_SPI_READ_BIT | ADXL345_SPI_MB_BIT | (start_reg_addr & 0x3F));
    for (int i = 0; i < num_bytes; i++) {
        buffer[i] = adxl345_spi.write(0x00);
    }
    adxl345_cs = 1;
}
bool check_adxl_tamper() {
    char raw_accel_data[6];
    int16_t accel_counts[3];
    float x_g, y_g, z_g;

    adxl_read_multiple_regs(ADXL345_REG_DATAX0, raw_accel_data, 6);
    accel_counts[0] = (int16_t)((raw_accel_data[1] << 8) | raw_accel_data[0]);
    accel_counts[1] = (int16_t)((raw_accel_data[3] << 8) | raw_accel_data[2]);
    accel_counts[2] = (int16_t)((raw_accel_data[5] << 8) | raw_accel_data[4]);
    x_g = ADXL345_SCALE_FACTOR * accel_counts[0];
    y_g = ADXL345_SCALE_FACTOR * accel_counts[1];
    z_g = ADXL345_SCALE_FACTOR * accel_counts[2];

    if (std::abs(x_g) > EXTREME_TILT_THRESHOLD_G ||
        std::abs(y_g) > EXTREME_TILT_THRESHOLD_G ||
        std::abs(z_g) > EXTREME_TILT_THRESHOLD_G) {
        if (current_system_state != LOCKED_TAMPER_ALERT) {
            trigger_alert("极端倾斜或翻转");
            current_system_state = LOCKED_TAMPER_ALERT;
        }
        return true;
    }
    return false;
}

// === 中断服务程序与回调 ===
void user_button1_isr() {
    chrono::microseconds current_us = button_debounce_timer.elapsed_time();
    chrono::milliseconds current_ms = std::chrono::duration_cast<std::chrono::milliseconds>(current_us);
    chrono::milliseconds last_press_ms(last_button1_press_time_ms_val);

    if ((current_ms - last_press_ms) > DEBOUNCE_TIME) {
        event_flags.set(BUTTON1_FLAG);
        last_button1_press_time_ms_val = current_ms.count();
    }
}
void user_button2_isr() {
    chrono::microseconds current_us = button_debounce_timer.elapsed_time();
    chrono::milliseconds current_ms = std::chrono::duration_cast<std::chrono::milliseconds>(current_us);
    chrono::milliseconds last_press_ms(last_button2_press_time_ms_val);

    if ((current_ms - last_press_ms) > DEBOUNCE_TIME) {
        event_flags.set(BUTTON2_FLAG);
        last_button2_press_time_ms_val = current_ms.count();
    }
}
void adxl_activity_isr() {
    event_flags.set(ADXL_ACTIVITY_FLAG);
}
void temp_check_callback() {
    temp_check_due = true;
}
void tilt_check_callback() {
    tilt_check_due = true;
}
void stop_digit_blink() {
    digit_blink_active = false;
}

// === 系统逻辑函数 ===
void process_passcode_input(int button_id) {
    if (current_system_state != PASSCODE_ENTRY) {
        current_system_state = PASSCODE_ENTRY;
        entered_passcode.clear();
        passcode_timeout_timer.reset();
        passcode_timeout_timer.start();
        printf("进入密码输入模式...\n\r");
    } else {
        passcode_timeout_timer.reset();
    }

    entered_passcode.push_back(button_id);
    printf("按钮 %d 按下, 当前输入: ", button_id);
    for (int digit : entered_passcode) { printf("%d ", digit); }
    printf("\n\r");

    digit_blink_active = true;
    digit_blink_timeout.attach(&stop_digit_blink, DIGIT_BLINK_DURATION);

    if (entered_passcode == PASSCODE) {
        printf("密码正确!\n\r");
        incorrect_passcode_attempts = 0;
        entered_passcode.clear();
        passcode_timeout_timer.stop();
        if (status_led_locked.read() == 1 || system_in_alert_state) {
            enter_unlocked_state();
        } else {
            enter_locked_idle_state();
        }
    } else if (entered_passcode.size() >= PASSCODE.size()) {
        printf("密码错误!\n\r");
        incorrect_passcode_attempts++;
        entered_passcode.clear();
        if (incorrect_passcode_attempts >= MAX_PASSCODE_ATTEMPTS) {
            printf("密码连续错误 %d 次! 系统锁定并报警!\n\r", MAX_PASSCODE_ATTEMPTS);
            trigger_alert("密码错误次数过多");
            current_system_state = LOCKED_TAMPER_ALERT;
            incorrect_passcode_attempts = 0;
            passcode_timeout_timer.stop();
        } else {
            printf("剩余尝试次数: %d\n\r", MAX_PASSCODE_ATTEMPTS - incorrect_passcode_attempts);
        }
    }
}
void update_system_state_and_alerts() {
    if (current_system_state == PASSCODE_ENTRY) {
        if (passcode_timeout_timer.elapsed_time() > PASSCODE_ENTRY_TIMEOUT) {
            printf("密码输入超时。\n\r");
            entered_passcode.clear();
            incorrect_passcode_attempts = 0;
            current_system_state = (status_led_locked.read() == 1) ? LOCKED_IDLE : UNLOCKED;
            passcode_timeout_timer.stop();
        }
    }

    if (current_system_state == LOCKED_IDLE || current_system_state == LOCKED_TEMP_ALERT || current_system_state == LOCKED_TAMPER_ALERT) {
        uint32_t flags = event_flags.get();
        if (flags & ADXL_ACTIVITY_FLAG) {
            event_flags.clear(ADXL_ACTIVITY_FLAG);
            char int_source = adxl_read_reg(ADXL345_REG_INT_SOURCE);
            if (int_source & 0x10) {
                if (current_system_state != LOCKED_TAMPER_ALERT) {
                    trigger_alert("检测到活动/震动");
                    current_system_state = LOCKED_TAMPER_ALERT;
                }
            }
        }

        if (tilt_check_due) {
            tilt_check_due = false;
            check_adxl_tamper();
        }
        
        if (temp_check_due) {
            temp_check_due = false;
            read_and_check_temperature();
        }
    }
    update_status_outputs();
}
void enter_locked_idle_state() {
    printf("系统已锁定。\n\r");
    current_system_state = LOCKED_IDLE;
    clear_alert();
    incorrect_passcode_attempts = 0;
    temp_check_ticker.attach(&temp_check_callback, TEMP_CHECK_INTERVAL);
    adxl_tilt_check_ticker.attach(&tilt_check_callback, ADXL_TILT_CHECK_INTERVAL);
    tmp102_config_normal_mode();
    adxl_write_reg(ADXL345_REG_POWER_CTL, 0x08);
    adxl_write_reg(ADXL345_REG_INT_ENABLE, 0x10);
    adxl_read_reg(ADXL345_REG_INT_SOURCE);
}
void enter_unlocked_state() {
    printf("系统已解锁。\n\r");
    current_system_state = UNLOCKED;
    clear_alert();
    incorrect_passcode_attempts = 0;
    temp_check_ticker.detach();
    adxl_tilt_check_ticker.detach();
    tmp102_config_shutdown_mode();
    adxl_write_reg(ADXL345_REG_POWER_CTL, 0x00);
    adxl_write_reg(ADXL345_REG_INT_ENABLE, 0x00);
}
void trigger_alert(const char* reason) {
    if (!system_in_alert_state) {
        printf("警报触发: %s!\n\r", reason);
    }
    system_in_alert_state = true;
}
void clear_alert() {
    if(system_in_alert_state) {
        printf("警报已清除。\n\r");
    }
    system_in_alert_state = false;
    if((current_system_state == LOCKED_TEMP_ALERT || current_system_state == LOCKED_TAMPER_ALERT) && entered_passcode.empty()) {
        current_system_state = LOCKED_IDLE;
    }
}
void update_status_outputs() {
    bool led_should_be_on_due_to_overall_state = (current_system_state != UNLOCKED);

    if (digit_blink_active) {
        status_led_locked = 1;
    } else {
        status_led_locked = led_should_be_on_due_to_overall_state;
    }

    if (current_system_state == LOCKED_TEMP_ALERT || current_system_state == LOCKED_TAMPER_ALERT) {
        main_alert_led = 1;
        buzzer = 1;
    } else {
        if (!system_in_alert_state) {
             main_alert_led = 0;
             buzzer = 0;
        }
    }
}

int main() {
    printf("智能储物柜系统启动中...\n\r");

    button_debounce_timer.start();

    initialize_tmp102();
    initialize_adxl345();

    user_button1.rise(&user_button1_isr);
    user_button2.rise(&user_button2_isr);
    
    enter_locked_idle_state();

    while (1) {
        uint32_t flags = event_flags.wait_any(BUTTON1_FLAG | BUTTON2_FLAG | ADXL_ACTIVITY_FLAG, 0U, false);

        bool event_processed_this_cycle = false;
        if (flags & BUTTON1_FLAG) {
            event_flags.clear(BUTTON1_FLAG);
            process_passcode_input(1);
            event_processed_this_cycle = true;
        }
        if (flags & BUTTON2_FLAG) {
            event_flags.clear(BUTTON2_FLAG);
            process_passcode_input(2);
            event_processed_this_cycle = true;
        }

        update_system_state_and_alerts();

        if (!event_processed_this_cycle &&
            !(event_flags.get() & (BUTTON1_FLAG | BUTTON2_FLAG | ADXL_ACTIVITY_FLAG)) &&
            !temp_check_due && !tilt_check_due &&
            current_system_state != PASSCODE_ENTRY &&
            current_system_state != LOCKED_TEMP_ALERT &&
            current_system_state != LOCKED_TAMPER_ALERT
            ) {
            printf("MCU 进入睡眠...\n\r");
            sleep();
            printf("MCU 被唤醒!\n\r");
        } else {
            // ThisThread::sleep_for(1ms);
        }
    }
}