【格院】EP Lab4 project 加入OLED/LED/BUZZER版本的智能密码柜(TMP102/ADXL345/I2C/SPI)

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

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

  1. 共地 (Common Ground)

    • 所有模块必须共地:NUCLEO L432KC 微控制器、ADXL345 加速度传感器、TMP102 温度传感器、OLED显示屏、蜂鸣器、LED等所有外部组件的GND(地)引脚必须连接在一起。

  2. I2C 通信

    • TMP102 (I2C on D4, D5):

      • SDA: Mbed D4

      • SCL: Mbed D5

      • 需要外部上拉电阻 (例如4.7kΩ) 连接到VCC。

      • TMP102的ADD0引脚需接地 (代码中I2C地址 0x90)。

      • 读取温度前有适当延时以确保转换完成。

    • OLED (I2C on D12, A6):

      • SDA: Mbed D12

      • SCL: Mbed A6

      • 同样需要外部上拉电阻连接到OLED的VCC。

      • OLED I2C地址在代码中设为 0x78

  3. SPI 通信 (ADXL345 on D2, D10, A4, A3)

    • MOSI: Mbed D2

    • MISO: Mbed D10

    • SCLK: Mbed A4 (此引脚与NUCLEO板载绿色LD3/程序内称LED1共享, SPI通信时此LED会闪烁)

    • CS: Mbed A3

    • SPI模式3正确。代码中包含对ADXL345 DEVID寄存器的读取验证。

  4. ADXL345 传感器

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

    • 中断清除adxl_activity_isr 中使用 adxl_read_reg_safe 直接读取并清除硬件中断。

    • 配置:全分辨率,±16g量程,活动检测阈值0.5g,倾斜检测考虑XYZ轴(阈值0.8g)。

  5. TMP102 传感器

    • 工作模式在锁定/解锁状态下自动切换。

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

  6. 硬件连接

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

    • 蜂鸣器引脚:Mbed A2

    • 主警报LED:Mbed A7

    • 储物柜锁定状态LED: Mbed D3

    • 用户按钮1: Mbed A0

    • 用户按钮2: Mbed A1

  7. 按键去抖

    • 已为用户按钮实现基于时间的软件去抖,DEBOUNCE_TIME (200ms)。时间戳更新使用了原子操作。

二、系统操作说明 🎛️

本智能储物柜系统通过两个按钮(按钮1 - Mbed A0,按钮2 - Mbed A1)进行操作。状态和信息将通过 OLED显示屏、LED指示灯和蜂鸣器提供反馈。

  1. 系统启动

    • 连接硬件并上电,系统进入 SYSTEM_INIT 状态并开始初始化各组件(TMP102, ADXL345, OLED)。

    • OLED显示:“INITIALIZING... Please wait”。

    • 串口监视器会输出详细的初始化和调试信息。

    • 初始化完成后:

      • 如果所有关键组件均健康,系统进入 锁定空闲 (LOCKED_IDLE) 状态。status_led_locked (Mbed D3) 点亮。OLED显示 "LOCKED", 当前温度, 和 "Monitoring..."。温度、倾斜和活动监控激活。

      • 如果任一关键组件初始化失败,系统会进入 LOCKED_TAMPER_ALERT 状态,OLED显示“系统初始化失败”(如果OLED本身正常),并通过主警报LED和蜂鸣器报警。

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

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

    • 进入密码输入模式:按任意密码按钮开始。OLED显示 "Enter Passcode:" 和已输入的星号(*)。

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

    • 密码正确:OLED显示 "Passcode OK!"。若当前锁定/警报,则解锁(OLED显示 "UNLOCKED", status_led_locked 灭,监控暂停,警报清除);若当前解锁,则锁定(OLED显示 "LOCKED", status_led_locked 亮,监控激活)。错误尝试计数清零。

    • 密码错误:OLED可能显示 "Passcode Fail!" 或剩余尝试次数。错误计数增加。

    • 密码连续输错3次:OLED显示 "TAMPER ALERT!" 和 "Reason: Max Attempts"。主警报LED (Mbed A7) 和蜂鸣器 (Mbed A2) 激活。需正确输入密码解除。

    • 密码输入超时 (10秒):OLED可能显示 "Input Timeout"。系统恢复到输入前状态。错误计数清零。

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

    • 温度监控 (默认每5秒):OLED在 LOCKED_IDLE 状态下显示当前温度(若TMP102健康,否则显示N/A或ERROR)。若超阈值,进入 LOCKED_TEMP_ALERT,OLED显示 "TEMP ALERT!", 温度, 及 "TOO HOT"/"TOO COLD"。主警报激活。温度恢复正常则警报自动清除。

    • 倾斜监控 (默认每0.5秒):若检测到极端倾斜/翻转(XYZ任一轴>0.8g),进入 LOCKED_TAMPER_ALERT,OLED显示 "TAMPER ALERT!" 和 "Reason: Tilt"。主警报激活。

    • 活动/震动监控 (中断驱动):若检测到活动(>0.5g),进入 LOCKED_TAMPER_ALERT,OLED显示 "TAMPER ALERT!" 和 "Reason: Activity"。主警报激活。

  4. 解除警报

    • 在任何警报状态下,正确输入密码即可解除警报并解锁系统。OLED会相应更新显示。

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

  5. MCU睡眠与健康检查

    • LOCKED_IDLEUNLOCKED 状态且无待处理事件时,MCU自动使用 __WFE() 指令进入低功耗睡眠模式。串口会打印进入和退出睡眠的信息(如果DEBUG_ENABLED为1)。

    • 系统会定期(例如每30秒)执行一次 system_health_check(),并将健康状况打印到串口(如果DEBUG_ENABLED为1)。

三、程序重点功能

  1. 环境监控 (基于TMP102):通过I2C读取实时温度,具备模式控制和阈值警报功能,使用LowPowerTicker进行低功耗定期检查。温度警报在条件恢复后可自动清除。初始化时会检查传感器响应。I2C访问受互斥锁保护。

  2. 防篡改检测 (基于ADXL345):通过SPI获取加速度数据,实现极端倾斜/翻转检测(定期检查,包含Z轴)和活动/震动检测(中断驱动,ISR内直接清除硬件中断)。初始化时会检查设备ID。SPI访问受互斥锁保护。

  3. 用户认证与交互:双按钮序列密码,带软件去抖(时间戳使用原子操作更新)、输入反馈LED(LowPowerTimeout管理延时,主线程附加)、错误尝试限制(3次)及报警、输入超时处理。

  4. 实时反馈与警报

    • OLED显示:显示系统状态、密码提示、温度(或传感器错误提示)、警报类型和原因。OLED的I2C操作受互斥锁保护。

    • LED和蜂鸣器:作为辅助和即时警报。

  5. 系统状态管理:新增 SYSTEM_INIT 状态。使用 safe_state_transition 函数(受互斥锁保护)记录状态变化。

  6. 低功耗操作:使用 __WFE() / __SEV() 指令进行MCU睡眠,利用EventFlags进行事件管理,LowPowerTicker执行定期后台任务,并在适当时将传感器置于低功耗模式。

  7. 系统健康与调试

    • DEBUG_PRINT 宏方便调试信息输出。

    • SystemHealth 结构体追踪关键组件的健康状态和错误计数(错误计数使用原子操作递增)。

    • 初始化函数返回 bool 值,并在 main() 中检查,若关键组件初始化失败则系统进入警报状态。

    • log_error 函数用于记录组件错误。

    • 定期执行 system_health_check()

四、核心代码逻辑详解

A. 初始化 (main, initialize_tmp102, initialize_adxl345, oled_init)
  • main:

    • 设置初始状态为 SYSTEM_INIT

    • 启动 button_debounce_timerhealth_check_main_timer

    • 调用各组件的初始化函数,并根据返回的布尔值更新 system_health 结构体。初始化函数内部的I2C/SPI操作会使用各自的互斥锁。

    • 如果所有关键组件初始化成功,则进入 enter_locked_idle_state()。否则,触发系统初始化失败警报,OLED显示错误,系统进入 LOCKED_TAMPER_ALERT 状态。

  • initialize_tmp102: 返回 bool。设置I2C频率,配置传感器为正常模式,尝试读取一次数据以验证通信,并更新 system_health.tmp102_healthy。所有I2C操作使用 tmp102_mutex

  • initialize_adxl345: 返回 bool。配置SPI,读取设备ID (ADXL345_REG_DEVID) 进行验证,然后配置数据格式、活动检测参数和中断。更新 system_health.adxl345_healthy。所有SPI操作使用 adxl345_mutex

  • oled_init: 返回 bool。设置I2C频率,发送一系列SSD1306初始化命令,并在每个命令发送后检查I2C写入结果。最后清屏。更新 system_health.oled_healthy。所有I2C操作使用 oled_mutex

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

    • 增加了在函数开始时检查 system_health.tmp102_healthy 的逻辑。

    • I2C读取失败时会调用 log_error 并更新健康状态。

    • 温度恢复正常时,若当前是温度警报,则调用 clear_alert() 并通过 safe_state_transition 恢复到 LOCKED_IDLE

    • current_system_state 的读取和潜在修改通过 state_mutexsafe_state_transition 进行保护。

  • check_adxl_tamper:

    • 增加了在函数开始时检查 system_health.adxl345_healthy 的逻辑。

    • 倾斜/翻转检测包含Z轴。

    • current_system_state 的读取和潜在修改通过 state_mutexsafe_state_transition 进行保护。

C. 中断处理与事件管理 (ISRs, EventFlags, Callbacks)
  • 按钮 ISRs (user_buttonX_isr):

    • 获取当前时间,与上次该按钮有效按下的时间(原子读取 last_buttonX_press_time_ms_val)进行比较。

    • 如果时间间隔大于 DEBOUNCE_TIME,则通过 event_flags.set() 设置相应的按钮标志,并原子更新上次按键时间 (core_util_atomic_store_u32)。

  • ADXL345活动 ISR (adxl_activity_isr):

    • 关键改进:直接在ISR中使用 adxl_read_reg_safe()(一个不使用互斥锁的SPI读取版本)读取并清除ADXL345的 INT_SOURCE 寄存器。

    • 如果确认是活动中断,则设置 adxl_interrupt_pending = true; 并通过 event_flags.set(ADXL_ACTIVITY_FLAG); 通知主循环。

  • 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):

    • 新增了对 DIGIT_BLINK_FLAG 的处理,如果该标志被设置(由 process_passcode_input 请求),则在主线程中安全地附加 digit_blink_timeout

    • 定期调用 system_health_check()

    • MCU睡眠决策:使用 __WFE() / __SEV()。在进入睡眠前,会通过 state_mutex 安全地获取 current_system_state 的快照进行判断。

  • update_system_state_and_alerts:

    • 在处理ADXL345活动中断、倾斜检查和温度检查时,会先判断对应传感器是否健康。

    • current_system_state 的读取和修改通过 state_mutexsafe_state_transition 保护。

  • process_passcode_input(int button_id):

    • 对共享变量如 entered_passcodeincorrect_passcode_attempts 的访问通过 system_mutex 保护。

    • 状态转换使用 safe_state_transition

  • 状态切换函数 (enter_locked_idle_state, enter_unlocked_state):

    • 在启动 LowPowerTicker 或配置传感器时,也考虑对应传感器的健康状态。

    • 状态转换使用 safe_state_transition

  • safe_state_transition(SystemState new_state): 新增函数,使用 state_mutex 保护对 current_system_stateprevious_system_state 的修改。

E. 输出与反馈 (update_status_outputs, update_oled_display)
  • update_oled_display:

    • 在函数开始时检查 system_health.oled_healthy

    • 在显示温度时,如果 system_health.tmp102_healthy 为false,则显示 "Temp: N/A" 或 "Temp: ERROR"。

    • current_system_state 的读取通过 state_mutex 保护。

    • OLED的I2C操作 (oled_clear, oled_puts 内部的 oled_cmd, oled_data) 现在受 oled_mutex 保护。

  • update_status_outputs:

    • current_system_statesystem_in_alert_state 的读取通过 state_mutex 保护。

    • 在调用 update_oled_display() 前,会检查 system_health.oled_healthy

F. 警报处理 (trigger_alert, clear_alert)
  • 对共享变量 system_in_alert_statelast_alert_reason 的访问通过 system_mutex 保护。

  • 状态转换使用 safe_state_transition

G. 系统管理函数 (system_health_check, safe_state_transition, log_error)
  • log_error: 对 system_health.error_count 的递增使用 core_util_atomic_incr_u32

#include "mbed.h"
#include <cmath>
#include <vector>
#include <string>
#include <cstring>
#include <atomic>

// --- TMP102 配置 ---
#define TMP102_REG_TEMP         0x00
#define TMP102_REG_CONFIG       0x01
const int TMP102_I2C_ADDR = 0x90;
const float TMP102_TEMP_FACTOR = 0.0625f;
#define TEMP_HIGH_THRESHOLD_C   35.0f
#define TEMP_LOW_THRESHOLD_C    5.0f

// --- ADXL345 配置 ---
#define ADXL345_REG_DEVID           0x00
#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
#define ADXL345_SPI_READ_BIT        0x80
#define ADXL345_SPI_MB_BIT          0x40
#define ADXL345_SCALE_FACTOR        0.0039f
#define EXTREME_TILT_THRESHOLD_G    0.8f
#define ACTIVITY_THRESHOLD_G        0.5f

// --- OLED 配置 ---
const int OLED_I2C_FREQUENCY = 400000;
const uint8_t OLED_I2C_ADDRESS = 0x78;
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_PAGES (SCREEN_HEIGHT / 8)
#define FONT_WIDTH 5
#define FONT_HEIGHT 8
#define FONT_FIRST_CHAR 32
#define FONT_LAST_CHAR 126
#define FONT_CHAR_COUNT (FONT_LAST_CHAR - FONT_FIRST_CHAR + 1)

// === 系统状态定义 ===
enum SystemState {
    LOCKED_IDLE,
    LOCKED_TEMP_ALERT,
    LOCKED_TAMPER_ALERT,
    UNLOCKED,
    PASSCODE_ENTRY
};

// Use atomic for thread-safe state access
std::atomic<SystemState> current_system_state(LOCKED_IDLE);

// === 硬件接口定义 ===
// TMP102 温度传感器
I2C tmp102_i2c(D4, D5);

// ADXL345 加速度传感器
SPI adxl345_spi(D2, D10, A4);
DigitalOut adxl345_cs(A3);
InterruptIn adxl_int1(D9, PullDown);

// OLED 显示屏
I2C oled_i2c(D12, A6);

// 用户输入按钮
InterruptIn user_button1(A0, PullDown);
InterruptIn user_button2(A1, PullDown);

// 输出设备
DigitalOut main_alert_led(A7);
DigitalOut buzzer(D6);
DigitalOut status_led_locked(D3);

// === 全局变量 ===
// 使用Mutex保护共享资源
Mutex system_mutex;
Mutex oled_mutex;

// TMP102 相关
char tmp102_config_buf[3];
char tmp102_temp_buf[2];
float current_temperature_c = 25.0f;
LowPowerTicker temp_check_ticker;
const float TEMP_CHECK_INTERVAL_SEC = 5.0f;  // Changed to float seconds
volatile bool temp_check_due = false;

// ADXL345 相关
LowPowerTicker adxl_tilt_check_ticker;
const float ADXL_TILT_CHECK_INTERVAL_SEC = 0.5f;  // Changed to float seconds
volatile bool tilt_check_due = false;

// 密码系统与按键去抖相关
const std::vector<int> PASSCODE = {1, 2, 1};
std::vector<int> entered_passcode;
EventFlags event_flags;
const uint32_t BUTTON1_FLAG = (1UL << 0);
const uint32_t BUTTON2_FLAG = (1UL << 1);
const uint32_t ADXL_ACTIVITY_FLAG = (1UL << 2);

Timer passcode_timeout_timer;
const uint32_t PASSCODE_ENTRY_TIMEOUT_MS = 10000;  // 10 seconds in milliseconds

// 为每个按钮使用独立的去抖定时器
Timer button1_debounce_timer;
Timer button2_debounce_timer;
const uint32_t DEBOUNCE_TIME_MS = 200;  // 200 milliseconds

int incorrect_passcode_attempts = 0;
const int MAX_PASSCODE_ATTEMPTS = 3;

LowPowerTimeout digit_blink_timeout;
std::atomic<bool> digit_blink_active(false);
const float DIGIT_BLINK_DURATION_SEC = 0.5f;  // 0.5 seconds

bool system_in_alert_state = false;
char last_alert_reason[64] = "";

// OLED 字体数据
const unsigned char basicFont5x8[FONT_CHAR_COUNT][FONT_WIDTH] = {
    {0x00,0x00,0x00,0x00,0x00}, /* SPACE */ {0x00,0x00,0x5F,0x00,0x00}, /* ! */ {0x00,0x07,0x00,0x07,0x00}, /* " */
    {0x14,0x7F,0x14,0x7F,0x14}, /* # */ {0x24,0x2A,0x7F,0x2A,0x12}, /* $ */ {0x23,0x13,0x08,0x64,0x62}, /* % */
    {0x36,0x49,0x55,0x22,0x50}, /* & */ {0x00,0x05,0x03,0x00,0x00}, /* ' */ {0x00,0x1C,0x22,0x41,0x00}, /* ( */
    {0x00,0x41,0x22,0x1C,0x00}, /* ) */ {0x14,0x08,0x3E,0x08,0x14}, /* * */ {0x08,0x08,0x3E,0x08,0x08}, /* + */
    {0x00,0x50,0x30,0x00,0x00}, /* , */ {0x08,0x08,0x08,0x08,0x08}, /* - */ {0x00,0x60,0x60,0x00,0x00}, /* . */
    {0x20,0x10,0x08,0x04,0x02}, /* / */ {0x3E,0x51,0x49,0x45,0x3E}, /* 0 */ {0x00,0x42,0x7F,0x40,0x00}, /* 1 */
    {0x42,0x61,0x51,0x49,0x46}, /* 2 */ {0x21,0x41,0x45,0x4B,0x31}, /* 3 */ {0x18,0x14,0x12,0x7F,0x10}, /* 4 */
    {0x27,0x45,0x45,0x45,0x39}, /* 5 */ {0x3C,0x4A,0x49,0x49,0x30}, /* 6 */ {0x01,0x71,0x09,0x05,0x03}, /* 7 */
    {0x36,0x49,0x49,0x49,0x36}, /* 8 */ {0x06,0x49,0x49,0x29,0x1E}, /* 9 */ {0x00,0x36,0x36,0x00,0x00}, /* : */
    {0x00,0x56,0x36,0x00,0x00}, /* ; */ {0x08,0x14,0x22,0x41,0x00}, /* < */ {0x14,0x14,0x14,0x14,0x14}, /* = */
    {0x00,0x41,0x22,0x14,0x08}, /* > */ {0x02,0x01,0x51,0x09,0x06}, /* ? */ {0x32,0x49,0x79,0x41,0x3E}, /* @ */
    {0x7E,0x11,0x11,0x11,0x7E}, /* A */ {0x7F,0x49,0x49,0x49,0x36}, /* B */ {0x3E,0x41,0x41,0x41,0x22}, /* C */
    {0x7F,0x41,0x41,0x22,0x1C}, /* D */ {0x7F,0x49,0x49,0x49,0x41}, /* E */ {0x7F,0x09,0x09,0x09,0x01}, /* F */
    {0x3E,0x41,0x49,0x49,0x7A}, /* G */ {0x7F,0x08,0x08,0x08,0x7F}, /* H */ {0x00,0x41,0x7F,0x41,0x00}, /* I */
    {0x20,0x40,0x41,0x3F,0x01}, /* J */ {0x7F,0x08,0x14,0x22,0x41}, /* K */ {0x7F,0x40,0x40,0x40,0x40}, /* L */
    {0x7F,0x02,0x0C,0x02,0x7F}, /* M */ {0x7F,0x04,0x08,0x10,0x7F}, /* N */ {0x3E,0x41,0x41,0x41,0x3E}, /* O */
    {0x7F,0x09,0x09,0x09,0x06}, /* P */ {0x3E,0x41,0x51,0x21,0x5E}, /* Q */ {0x7F,0x09,0x19,0x29,0x46}, /* R */
    {0x46,0x49,0x49,0x49,0x31}, /* S */ {0x01,0x01,0x7F,0x01,0x01}, /* T */ {0x3F,0x40,0x40,0x40,0x3F}, /* U */
    {0x1F,0x20,0x40,0x20,0x1F}, /* V */ {0x3F,0x40,0x38,0x40,0x3F}, /* W */ {0x63,0x14,0x08,0x14,0x63}, /* X */
    {0x07,0x08,0x70,0x08,0x07}, /* Y */ {0x61,0x51,0x49,0x45,0x43}, /* Z */ {0x00,0x7F,0x41,0x41,0x00}, /* [ */
    {0x02,0x04,0x08,0x10,0x20}, /* \ */{0x00,0x41,0x41,0x7F,0x00}, /* ] */ {0x04,0x02,0x01,0x02,0x04}, /* ^ */
    {0x40,0x40,0x40,0x40,0x40}, /* _ */ {0x00,0x01,0x02,0x04,0x00}, /* ` */ {0x20,0x54,0x54,0x54,0x78}, /* a */
    {0x7F,0x48,0x44,0x44,0x38}, /* b */ {0x38,0x44,0x44,0x44,0x20}, /* c */ {0x38,0x44,0x44,0x48,0x7F}, /* d */
    {0x38,0x54,0x54,0x54,0x18}, /* e */ {0x08,0x7E,0x09,0x01,0x02}, /* f */ {0x0C,0x52,0x52,0x52,0x3E}, /* g */
    {0x7F,0x08,0x04,0x04,0x78}, /* h */ {0x00,0x44,0x7D,0x40,0x00}, /* i */ {0x20,0x40,0x44,0x3D,0x00}, /* j */
    {0x7F,0x10,0x28,0x44,0x00}, /* k */ {0x00,0x41,0x7F,0x40,0x00}, /* l */ {0x7C,0x04,0x18,0x04,0x78}, /* m */
    {0x7C,0x08,0x04,0x04,0x78}, /* n */ {0x38,0x44,0x44,0x44,0x38}, /* o */ {0x7C,0x14,0x14,0x14,0x08}, /* p */
    {0x08,0x14,0x14,0x18,0x7C}, /* q */ {0x7C,0x08,0x04,0x04,0x08}, /* r */ {0x48,0x54,0x54,0x54,0x20}, /* s */
    {0x04,0x3F,0x44,0x40,0x20}, /* t */ {0x3C,0x40,0x40,0x20,0x7C}, /* u */ {0x1C,0x20,0x40,0x20,0x1C}, /* v */
    {0x3C,0x40,0x30,0x40,0x3C}, /* w */ {0x44,0x28,0x10,0x28,0x44}, /* x */ {0x0C,0x50,0x50,0x50,0x3C}, /* y */
    {0x44,0x64,0x54,0x4C,0x44}, /* z */ {0x00,0x08,0x36,0x41,0x00}, /* { */ {0x00,0x00,0x7F,0x00,0x00}, /* | */
    {0x00,0x41,0x36,0x08,0x00}, /* } */ {0x02,0x01,0x02,0x04,0x02}  /* ~ */
};

// === 函数声明 ===
void initialize_tmp102();
void tmp102_config_normal_mode();
void tmp102_config_shutdown_mode();
void tmp102_set_temp_pointer();
bool read_and_check_temperature();
void initialize_adxl345();
void adxl_write_reg(char reg_addr, char value);
char adxl_read_reg(char reg_addr);
void adxl_read_multiple_regs(char start_reg_addr, char* buffer, int num_bytes);
bool check_adxl_tamper();
void user_button1_isr();
void user_button2_isr();
void adxl_activity_isr();
void temp_check_callback();
void tilt_check_callback();
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();
void stop_digit_blink();
// OLED 函数声明
void oled_cmd(char cmd);
void oled_data(char data_byte);
void oled_init();
void oled_clear();
void oled_putc(char c, int x_char_col, int y_char_row);
void oled_puts(const char *str, int x_char_col, int y_char_row);
void update_oled_display();

// === OLED 函数实现 ===
void oled_cmd(char cmd) {
    oled_mutex.lock();
    char i2c_payload[2] = {0x00, cmd};
    int result = oled_i2c.write(OLED_I2C_ADDRESS, i2c_payload, 2);
    oled_mutex.unlock();
    if (result != 0) {
        printf("OLED命令写入失败: %d\n\r", result);
    }
}

void oled_data(char data_byte) {
    oled_mutex.lock();
    char i2c_payload[2] = {0x40, data_byte};
    int result = oled_i2c.write(OLED_I2C_ADDRESS, i2c_payload, 2);
    oled_mutex.unlock();
    if (result != 0) {
        printf("OLED数据写入失败: %d\n\r", result);
    }
}

void oled_init() {
    oled_i2c.frequency(OLED_I2C_FREQUENCY);
    
    oled_cmd(0xAE); // Display OFF
    ThisThread::sleep_for(10);
    oled_cmd(0xD5); oled_cmd(0x80); // Display Clock
    oled_cmd(0xA8); oled_cmd(0x3F); // Multiplex Ratio
    oled_cmd(0xD3); oled_cmd(0x00); // Display Offset
    oled_cmd(0x40); // Start Line
    oled_cmd(0x8D); oled_cmd(0x14); // Charge Pump
    oled_cmd(0x20); oled_cmd(0x02); // Page Addressing Mode
    oled_cmd(0xA1); // Segment Re-map
    oled_cmd(0xC8); // COM Output Scan Direction
    oled_cmd(0xDA); oled_cmd(0x12); // COM Pins Configuration
    oled_cmd(0x81); oled_cmd(0xCF); // Contrast
    oled_cmd(0xD9); oled_cmd(0xF1); // Pre-charge Period
    oled_cmd(0xDB); oled_cmd(0x40); // VCOMH Deselect Level
    oled_cmd(0xA4); // Entire Display ON
    oled_cmd(0xA6); // Normal Display
    oled_cmd(0xAF); // Display ON
    ThisThread::sleep_for(100);
    oled_clear();
    printf("OLED初始化完成。\n\r");
}

void oled_clear() {
    oled_mutex.lock();
    for (int page = 0; page < OLED_PAGES; page++) {
        oled_cmd(0xB0 + page);
        oled_cmd(0x00);
        oled_cmd(0x10);
        for (int col = 0; col < SCREEN_WIDTH; col++) {
            oled_data(0x00);
        }
    }
    oled_mutex.unlock();
}

void oled_putc(char c, int x_char_col, int y_char_row) {
    if (x_char_col * (FONT_WIDTH + 1) >= SCREEN_WIDTH || y_char_row >= (SCREEN_HEIGHT / FONT_HEIGHT)) return;

    int actual_y_pixel = y_char_row * FONT_HEIGHT;
    int page = actual_y_pixel / 8;
    
    oled_mutex.lock();
    oled_cmd(0xB0 + page);
    int actual_x_pixel = x_char_col * (FONT_WIDTH + 1);
    oled_cmd(0x00 + (actual_x_pixel & 0x0F));
    oled_cmd(0x10 + ((actual_x_pixel >> 4) & 0x0F));

    if (c >= FONT_FIRST_CHAR && c <= FONT_LAST_CHAR) {
        int char_index = c - FONT_FIRST_CHAR;
        for (int i = 0; i < FONT_WIDTH; i++) {
            if ((actual_x_pixel + i) < SCREEN_WIDTH) {
                oled_data(basicFont5x8[char_index][i]);
            }
        }
        if ((actual_x_pixel + FONT_WIDTH) < SCREEN_WIDTH) {
            oled_data(0x00);
        }
    } else {
        for (int i = 0; i < FONT_WIDTH + 1; i++) {
            if ((actual_x_pixel + i) < SCREEN_WIDTH) {
                oled_data(0x00);
            }
        }
    }
    oled_mutex.unlock();
}

void oled_puts(const char *str, int x_char_col, int y_char_row) {
    if (!str) return;
    int current_x_char_col = x_char_col;
    while (*str != '\0' && current_x_char_col < 21) {
        oled_putc(*str, current_x_char_col, y_char_row);
        str++;
        current_x_char_col++;
    }
}

void update_oled_display() {
    system_mutex.lock();
    SystemState state = current_system_state.load();
    float temp = current_temperature_c;
    size_t passcode_len = entered_passcode.size();
    int attempts = incorrect_passcode_attempts;
    char alert_reason[64];
    strncpy(alert_reason, last_alert_reason, sizeof(alert_reason) - 1);
    alert_reason[sizeof(alert_reason) - 1] = '\0';
    system_mutex.unlock();

    oled_clear();
    char line1[32], line2[32], line3[32], line4[32];
    memset(line1, 0, sizeof(line1));
    memset(line2, 0, sizeof(line2));
    memset(line3, 0, sizeof(line3));
    memset(line4, 0, sizeof(line4));

    switch(state) {
        case LOCKED_IDLE:
            snprintf(line1, sizeof(line1), "LOCKED");
            snprintf(line2, sizeof(line2), "Temp: %.1fC", temp);
            snprintf(line3, sizeof(line3), "Monitoring...");
            break;
        case UNLOCKED:
            snprintf(line1, sizeof(line1), "UNLOCKED");
            snprintf(line2, sizeof(line2), "Safe to open.");
            break;
        case PASSCODE_ENTRY:
            snprintf(line1, sizeof(line1), "Enter Passcode:");
            for(size_t i = 0; i < passcode_len && i < 10; ++i) {
                line2[i] = '*'; 
            }
            line2[passcode_len] = '\0';
            if(attempts > 0) {
                snprintf(line3, sizeof(line3), "Attempts: %d/%d", attempts, MAX_PASSCODE_ATTEMPTS);
            }
            break;
        case LOCKED_TEMP_ALERT:
            snprintf(line1, sizeof(line1), "**TEMP ALERT!**");
            snprintf(line2, sizeof(line2), "Temp: %.1fC", temp);
            if (temp > TEMP_HIGH_THRESHOLD_C) 
                snprintf(line3, sizeof(line3), "Status: TOO HOT");
            else 
                snprintf(line3, sizeof(line3), "Status: TOO COLD");
            snprintf(line4, sizeof(line4), "Enter Code");
            break;
        case LOCKED_TAMPER_ALERT:
            snprintf(line1, sizeof(line1), "**TAMPER ALERT!**");
            snprintf(line2, sizeof(line2), "%.20s", alert_reason);
            snprintf(line3, sizeof(line3), "Enter Code");
            break;
        default:
            snprintf(line1, sizeof(line1), "System Error!");
            snprintf(line2, sizeof(line2), "State: %d", (int)state);
            break;
    }
    
    oled_puts(line1, 0, 0);
    oled_puts(line2, 0, 1);
    oled_puts(line3, 0, 2);
    oled_puts(line4, 0, 3);
}

// === TMP102 函数实现 ===
void initialize_tmp102() {
    tmp102_i2c.frequency(100000);
    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; 
    int result = tmp102_i2c.write(TMP102_I2C_ADDR, tmp102_config_buf, 3);
    if (result != 0) {
        printf("TMP102配置失败: %d\n\r", result);
    }
}

void tmp102_config_shutdown_mode() {
    tmp102_config_buf[0] = TMP102_REG_CONFIG; 
    tmp102_config_buf[1] = 0x61; 
    tmp102_config_buf[2] = 0xA0; 
    int result = tmp102_i2c.write(TMP102_I2C_ADDR, tmp102_config_buf, 3);
    if (result != 0) {
        printf("TMP102关闭模式配置失败: %d\n\r", result);
    }
}

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(15); 
    
    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; 
        }
        
        system_mutex.lock();
        current_temperature_c = final_raw_temp * TMP102_TEMP_FACTOR;
        float temp = current_temperature_c;
        system_mutex.unlock();

        SystemState state = current_system_state.load();
        
        if (temp > TEMP_HIGH_THRESHOLD_C) {
            if (state != LOCKED_TEMP_ALERT) { 
                trigger_alert("温度过高");
                current_system_state = LOCKED_TEMP_ALERT;
            }
            return true; 
        } else if (temp < TEMP_LOW_THRESHOLD_C) {
            if (state != LOCKED_TEMP_ALERT) { 
                trigger_alert("温度过低");
                current_system_state = LOCKED_TEMP_ALERT;
            }
            return true; 
        } else {
            if (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); 
    
    // 验证设备ID
    char devid = adxl_read_reg(ADXL345_REG_DEVID);
    if (devid != 0xE5) {
        printf("ADXL345设备ID错误: 0x%02X\n\r", devid);
    }
    
    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; 
    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]; 

    SystemState state = current_system_state.load();
    
    if (std::abs(x_g) > EXTREME_TILT_THRESHOLD_G || 
        std::abs(y_g) > EXTREME_TILT_THRESHOLD_G || 
        std::abs(z_g) < 0.3f) { // Z轴检测翻转
        if (state != LOCKED_TAMPER_ALERT) { 
            trigger_alert("极端倾斜或翻转");
            current_system_state = LOCKED_TAMPER_ALERT;
        }
        return true; 
    }
    return false; 
}

// === 中断服务程序与回调 ===
void user_button1_isr() {
    if (button1_debounce_timer.read_ms() > DEBOUNCE_TIME_MS) { 
        event_flags.set(BUTTON1_FLAG); 
        button1_debounce_timer.reset();
    }
}

void user_button2_isr() {
    if (button2_debounce_timer.read_ms() > DEBOUNCE_TIME_MS) { 
        event_flags.set(BUTTON2_FLAG); 
        button2_debounce_timer.reset();
    }
}

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) {
    SystemState state = current_system_state.load();
    
    if (state != PASSCODE_ENTRY) {
        current_system_state = PASSCODE_ENTRY;
        system_mutex.lock();
        entered_passcode.clear(); 
        system_mutex.unlock();
        passcode_timeout_timer.reset(); 
        passcode_timeout_timer.start(); 
        printf("进入密码输入模式...\n\r");
    } else { 
        passcode_timeout_timer.reset();
    }

    system_mutex.lock();
    entered_passcode.push_back(button_id);
    size_t current_size = entered_passcode.size();
    std::vector<int> current_passcode = entered_passcode;
    system_mutex.unlock();
    
    printf("按钮 %d 按下, 当前输入长度: %d\n\r", button_id, current_size);

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

    if (current_passcode == PASSCODE) {
        printf("密码正确!\n\r");
        system_mutex.lock();
        incorrect_passcode_attempts = 0; 
        entered_passcode.clear();        
        system_mutex.unlock();
        passcode_timeout_timer.stop();   
        
        if (status_led_locked.read() == 1 || system_in_alert_state) { 
            enter_unlocked_state(); 
        } else {
            enter_locked_idle_state(); 
        }
    } else if (current_size >= PASSCODE.size()) { 
        printf("密码错误!\n\r");
        system_mutex.lock();
        incorrect_passcode_attempts++; 
        int attempts = incorrect_passcode_attempts;
        entered_passcode.clear();      
        system_mutex.unlock();
        
        if (attempts >= MAX_PASSCODE_ATTEMPTS) { 
            printf("密码连续错误 %d 次! 系统锁定并报警!\n\r", MAX_PASSCODE_ATTEMPTS);
            trigger_alert("密码错误次数过多");
            current_system_state = LOCKED_TAMPER_ALERT; 
            system_mutex.lock();
            incorrect_passcode_attempts = 0; 
            system_mutex.unlock();
            passcode_timeout_timer.stop();   
        } else {
            printf("剩余尝试次数: %d\n\r", MAX_PASSCODE_ATTEMPTS - attempts);
        }
    }
    
    update_oled_display();
}

void update_system_state_and_alerts() {
    SystemState state = current_system_state.load();
    
    if (state == PASSCODE_ENTRY) {
        if (passcode_timeout_timer.read_ms() > PASSCODE_ENTRY_TIMEOUT_MS) {
            printf("密码输入超时。\n\r");
            system_mutex.lock();
            entered_passcode.clear();
            incorrect_passcode_attempts = 0; 
            system_mutex.unlock();
            current_system_state = (status_led_locked.read() == 1) ? LOCKED_IDLE : UNLOCKED;
            passcode_timeout_timer.stop();
        }
    }

    if (state == LOCKED_IDLE || state == LOCKED_TEMP_ALERT || 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 (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(); 
    system_mutex.lock();
    incorrect_passcode_attempts = 0; 
    system_mutex.unlock();
    temp_check_ticker.attach(&temp_check_callback, TEMP_CHECK_INTERVAL_SEC); 
    adxl_tilt_check_ticker.attach(&tilt_check_callback, ADXL_TILT_CHECK_INTERVAL_SEC); 
    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(); 
    system_mutex.lock();
    incorrect_passcode_attempts = 0; 
    system_mutex.unlock();
    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) {
    system_mutex.lock();
    if (!system_in_alert_state) { 
        printf("警报触发: %s!\n\r", reason);
    }
    system_in_alert_state = true; 
    strncpy(last_alert_reason, reason, sizeof(last_alert_reason) - 1);
    last_alert_reason[sizeof(last_alert_reason) - 1] = '\0';
    system_mutex.unlock();
}

void clear_alert() {
    system_mutex.lock();
    if(system_in_alert_state) { 
        printf("警报已清除。\n\r");
    }
    system_in_alert_state = false; 
    strcpy(last_alert_reason, "");
    bool passcode_empty = entered_passcode.empty();
    system_mutex.unlock();
    
    SystemState state = current_system_state.load();
    if((state == LOCKED_TEMP_ALERT || state == LOCKED_TAMPER_ALERT) && passcode_empty) {
        current_system_state = LOCKED_IDLE;
    }
}

void update_status_outputs() {
    SystemState state = current_system_state.load();
    bool led_should_be_on = (state != UNLOCKED);
    bool digit_blink = digit_blink_active.load();

    if (digit_blink) { 
        status_led_locked = 1;
    } else {
        status_led_locked = led_should_be_on;
    }

    if (state == LOCKED_TEMP_ALERT || state == LOCKED_TAMPER_ALERT) {
        main_alert_led = 1; 
        buzzer = 1;         
    } else {
        system_mutex.lock();
        bool alert_state = system_in_alert_state;
        system_mutex.unlock();
        
        if (!alert_state) { 
            main_alert_led = 0; 
            buzzer = 0;         
        }
    }
    update_oled_display();
}

// === Main ===
int main() {
    printf("\n\r智能储物柜系统启动中...\n\r");
    printf("固件版本: 2.0 (调试改进版)\n\r");

    // 启动按钮去抖定时器
    button1_debounce_timer.start();
    button2_debounce_timer.start();

    // 初始化传感器和显示
    initialize_tmp102();
    initialize_adxl345();
    oled_init();

    // 配置中断
    user_button1.rise(&user_button1_isr); 
    user_button2.rise(&user_button2_isr); 
    
    // 进入初始锁定状态
    enter_locked_idle_state(); 

    printf("系统初始化完成。\n\r");

    while (1) { 
        uint32_t flags = event_flags.wait_any(
            BUTTON1_FLAG | BUTTON2_FLAG | ADXL_ACTIVITY_FLAG, 
            10,  // Changed from 10ms to 10
            false
        ); 

        if (flags & BUTTON1_FLAG) {
            event_flags.clear(BUTTON1_FLAG); 
            process_passcode_input(1);    
        }
        if (flags & BUTTON2_FLAG) {
            event_flags.clear(BUTTON2_FLAG); 
            process_passcode_input(2);    
        }

        update_system_state_and_alerts(); 

        // 改进的睡眠条件
        SystemState state = current_system_state.load();
        bool can_sleep = (flags == 0) &&  // 没有事件
                        !(event_flags.get() & (BUTTON1_FLAG | BUTTON2_FLAG | ADXL_ACTIVITY_FLAG)) && 
                        !temp_check_due && 
                        !tilt_check_due && 
                        (state == LOCKED_IDLE || state == UNLOCKED);
                        
        if (can_sleep) {
            // 使用低功耗等待而不是sleep()
            ThisThread::sleep_for(50);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值