一、重要注意事项 ⚠️ (务必优先阅读!)
在开始操作和进一步开发之前,请务必注意以下几点,以确保系统正常工作并避免损坏元器件:
共地 (Common Ground):
所有模块(NUCLEO L432KC、ADXL345、TMP102、蜂鸣器、LED等)的GND引脚必须连接在一起,形成共同的参考地。
I2C 通信 (TMP102):
上拉电阻:I2C总线的SDA和SCL线通常需要外部上拉电阻(如4.7kΩ)到VCC。
TMP102 地址 (ADD0 Pin):代码中地址为
0x90
(对应7位地址0x48)。确保TMP102的ADD0引脚接地。
SPI 通信 (ADXL345):
片选 (CS):由微控制器
A3
引脚控制。
ADXL345 传感器:
中断引脚 (INT1):连接到微控制器
D9
,用于活动检测。中断清除:代码在处理活动中断后会读取
INT_SOURCE
(0x30)寄存器以清除硬件中断。灵敏度与量程:配置为全分辨率,±16g量程。
TMP102 传感器:
工作模式:系统自动切换正常与关断模式。
温度阈值:高低温阈值(35°C, 5°C)为例。
低功耗设计:
MCU
sleep()
功能已集成。代码中已加入进入和退出睡眠的打印信息,便于调试和演示。
硬件连接:
严格按照代码中的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和蜂鸣器提供状态反馈。
系统启动:
连接硬件并上电,系统自动初始化。串口监视器会输出初始化信息。
默认进入 锁定空闲 (LOCKED_IDLE) 状态。
status_led_locked
(MbedD3
) 点亮。系统开始定期监测温度和倾斜,ADXL345活动检测激活。
密码输入与解锁/锁定:
预设密码:按钮1 -> 按钮2 -> 按钮1。
进入密码输入模式:按任意密码按钮开始。
输入反馈:每成功输入一位,
status_led_locked
(MbedD3
) 点亮0.5秒。密码正确:若当前锁定或处于警报状态则解锁 (
status_led_locked
灭,监控暂停,警报清除);若当前解锁则锁定 (status_led_locked
亮,监控激活)。错误尝试计数清零。密码错误:若输入长度足够但不匹配,错误计数增加。
连续输错3次:系统进入篡改警报状态 (
LOCKED_TAMPER_ALERT
),主警报LED (MbedD2
) 和蜂鸣器 (MbedD6
) 激活。需正确输入密码解除。密码输入超时 (10秒):恢复到输入前状态,错误计数清零。
锁定状态下的监控与警报:
温度监控 (默认每5秒):超出阈值(35°C高,5°C低)则进入
LOCKED_TEMP_ALERT
,激活主警报。如果温度恢复正常,警报会自动清除,状态恢复到LOCKED_IDLE
。倾斜监控 (默认每0.5秒):检测到极端倾斜或翻转(XYZ任一轴>0.8g)则进入
LOCKED_TAMPER_ALERT
,激活主警报。倾斜警报当前设计为需密码解锁清除。活动/震动监控 (中断驱动):检测到活动(>0.5g)则进入
LOCKED_TAMPER_ALERT
,激活主警报。
解除警报:
在任何警报状态下,正确输入密码即可解除警报并解锁系统。
温度警报在温度恢复正常时也会自动解除。
MCU睡眠:
在
LOCKED_IDLE
或UNLOCKED
状态且无待处理事件时,MCU自动进入低功耗睡眠模式,由中断唤醒。串口会打印进入和退出睡眠的信息。
三、程序重点功能
主要功能如下:
环境监控 (基于TMP102):通过I2C读取实时温度,具备模式控制和阈值警报功能,使用
LowPowerTicker
进行低功耗定期检查。警报在条件恢复后可自动清除。防篡改检测 (基于ADXL345):通过SPI获取加速度数据,实现极端倾斜/翻转检测(定期检查,包含Z轴)和活动/震动检测(中断驱动)。
用户认证与交互:双按钮序列密码,带软件去抖、输入反馈LED、错误尝试限制及报警、输入超时处理。
实时反馈与警报:通过状态LED、主警报LED和蜂鸣器提供清晰的系统状态和警报指示。
系统状态管理:使用
SystemState
枚举管理多工作模式。低功耗操作:集成MCU
sleep()
,利用EventFlags
、LowPowerTicker
、LowPowerTimeout
等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. 主循环与状态机 (main
的while(1)
, update_system_state_and_alerts
, process_passcode_input
, 状态切换函数)
main
的while(1)
:使用
event_flags.wait_any(..., 0U, false)
非阻塞检查事件。处理按钮事件。
调用
update_system_state_and_alerts
。根据条件调用
sleep()
。
update_system_state_and_alerts
:处理密码超时。
若锁定相关状态:处理
ADXL_ACTIVITY_FLAG
;处理tilt_check_due
和temp_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);
}
}
}