From d8df5d49d3ccf9199cdf27ce4af9a6fdcbeec6cb Mon Sep 17 00:00:00 2001 From: edisondeng Date: Fri, 31 Jul 2026 09:22:07 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20main.c/event.c/lcd.?= =?UTF-8?q?c=20=E7=9A=84=E4=B8=AD=E6=96=87=E6=B3=A8=E9=87=8A=E4=B9=B1?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E7=BB=9F=E4=B8=80=E4=B8=BA=20UTF-8=20?= =?UTF-8?q?=E7=BC=96=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lcd.c 原本整体是 GBK 编码,直接转码即可。main.c 与 event.c 曾被 Keil 环境以错误编码保存导致部分注释损坏,本次以历史提交中未损坏的 版本为基底恢复正确注释文本,并将其后新增的真实代码改动(RGB 三色 诊断指令、SOS 按键状态机解锁)对应的注释一并还原,不涉及任何代码逻辑变更。 --- App/event.c | 178 ++++++++++++++++++++--------------------- App/main.c | 218 +++++++++++++++++++++++++------------------------- CLAUDE.md | 7 ++ Drivers/lcd.c | 206 +++++++++++++++++++++++------------------------ 4 files changed, 308 insertions(+), 301 deletions(-) create mode 100644 CLAUDE.md diff --git a/App/event.c b/App/event.c index 7f6edd0..216a8a4 100644 --- a/App/event.c +++ b/App/event.c @@ -6,17 +6,17 @@ #include "xtell.h" #include "../Drivers/adc.h" -// 定义事件形队列的大深度为 8 +// 定义事件环形队列的最大深度为 8 #define EVENT_QUEUE_DEPTH 8 -// 形队列存储数 (存放在单片机 xdata 拓展 RAM区中) +// 环形队列存储数组 (存放在单片机 xdata 拓展 RAM区中) static SystemEvent xdata event_queue[EVENT_QUEUE_DEPTH]; -static u8 queue_head = 0; // 队指 -static u8 queue_tail = 0; // 队尾写指 -static u8 queue_count = 0; // 当前队列内有效事件数 +static u8 queue_head = 0; // 队首读指针 +static u8 queue_tail = 0; // 队尾写指针 +static u8 queue_count = 0; // 当前队列内有效事件总数 /** - * @brief 初化/清空事件队列 + * @brief 初始化/清空事件队列 */ void EventQueue_Init(void) { @@ -26,63 +26,63 @@ void EventQueue_Init(void) } /** - * @brief 将普通事件压入队 - * @param evt 输入的系统事 - * @return bit 1-成功0-失败 + * @brief 将普通事件压入队尾 + * @param evt 输入的系统事件 + * @return bit 1-成功,0-失败 */ bit EventQueue_Push(SystemEvent evt) { if (queue_count >= EVENT_QUEUE_DEPTH) return 0; // 队列满,丢弃事件 - EA = 0; // 关闭全局,保护临界区操作的原子 + EA = 0; // 关闭全局中断,保护临界区操作的原子性 event_queue[queue_tail] = evt; // 存入队尾 - queue_tail = (queue_tail + 1) % EVENT_QUEUE_DEPTH; // 尾指针向后移,回 - queue_count++; // 有效计数 1 - EA = 1; // 恢全 + queue_tail = (queue_tail + 1) % EVENT_QUEUE_DEPTH; // 尾指针向后移动,回环 + queue_count++; // 有效计数加 1 + EA = 1; // 恢复全局中断 return 1; } /** - * @brief 将高优先/紧事件强行插到队 - * @param evt 输入的系统事 - * @return bit 1-成功0-失败 + * @brief 将高优先级/紧急事件强行插到队首 + * @param evt 输入的系统事件 + * @return bit 1-成功,0-失败 */ bit EventQueue_InsertFront(SystemEvent evt) { if (queue_count >= EVENT_QUEUE_DEPTH) return 0; // 队列满,插队失败 - EA = 0; // 屏蔽 - // 头指针向前移动一位(带回防溢出) + EA = 0; // 屏蔽中断 + // 头指针向前移动一位(带回环防溢出) queue_head = (queue_head - 1 + EVENT_QUEUE_DEPTH) % EVENT_QUEUE_DEPTH; - event_queue[queue_head] = evt; // 存入腾出的队首位 - queue_count++; // 计数 1 - EA = 1; // 恢中 + event_queue[queue_head] = evt; // 存入腾出的队首位置 + queue_count++; // 计数加 1 + EA = 1; // 恢复中断 return 1; } /** - * @brief 从队首弹出一缓冲事件 - * @return SystemEvent 返回弹出的事件若空返回空闲事 + * @brief 从队首弹出一个缓冲事件 + * @return SystemEvent 返回弹出的事件。若空返回空闲事件 */ SystemEvent EventQueue_Pop(void) { SystemEvent evt; - // 默空事件属 + // 默认空事件属性 evt.key_event = KEY_EVENT_NONE; evt.priority = EVENT_PRIORITY_NORMAL; evt.extra_data = 0; evt.extra_size = 0; - - if (queue_count == 0) return evt; // 队空,直接返回空闲事 - - EA = 0; // 屏蔽 - evt = event_queue[queue_head]; // 读取队数 - queue_head = (queue_head + 1) % EVENT_QUEUE_DEPTH; // 头指针向后移,回 - queue_count--; // 有效计数 1 - EA = 1; // 恢中 + + if (queue_count == 0) return evt; // 队空,直接返回空闲事件 + + EA = 0; // 屏蔽中断 + evt = event_queue[queue_head]; // 读取队首数据 + queue_head = (queue_head + 1) % EVENT_QUEUE_DEPTH; // 头指针向后移动,回环 + queue_count--; // 有效计数减 1 + EA = 1; // 恢复中断 return evt; } /** - * @brief 判断队列否为 + * @brief 判断队列是否为空 */ bit EventQueue_IsEmpty(void) { @@ -90,7 +90,7 @@ bit EventQueue_IsEmpty(void) } /** - * @brief 判断队列否已 + * @brief 判断队列是否已满 */ bit EventQueue_IsFull(void) { @@ -98,7 +98,7 @@ bit EventQueue_IsFull(void) } /** - * @brief 获取当前队列的事件数 + * @brief 获取当前队列中的事件总数 */ u8 EventQueue_GetCount(void) { @@ -106,98 +106,98 @@ u8 EventQueue_GetCount(void) } /** - * @brief 事件分发器核心轮询辑 - * @details 逐一处理队列的事件,并根系统当前的运行模式进行拦、跳或下发派发 + * @brief 事件分发器核心轮询逻辑 + * @details 逐一处理队列中的事件,并根据系统当前的运行模式进行拦截、跳转或下发派发。 */ void Event_Dispatcher_Loop(void) { - SystemEvent evt = EventQueue_Pop(); // 弹出事件 - - // 如果无事发生的空事件,直接 + SystemEvent evt = EventQueue_Pop(); // 弹出一个事件 + + // 如果是无事发生的空事件,直接退出 if (evt.key_event == KEY_EVENT_NONE && evt.extra_size == 0) { return; } - - inactivity_timer = 0; // 捕获到有效事件,清零休眠计时,重闲置超时 - + + inactivity_timer = 0; // 捕获到有效事件,清零休眠计时器,重置闲置超时 + // 串口打印调试信息 XTELL_LOG("[EVENT] key="); XTELL_LOG_HEX8((u8)evt.key_event); XTELL_LOG("\r\n"); - // SOS 按键紧理:若当前前台不为 SOS 应用,接收到 SOS 动作,立即强行切入到 SOS_APP 并执行求 + // SOS 按键紧急处理:若当前前台不为 SOS 应用,接收到 SOS 键动作,立即强行切入到 SOS_APP 并执行求救 if ((evt.key_event == KEY_EVENT_SOS_CLICK || evt.key_event == KEY_EVENT_SOS_LONG) && AppManager_GetActiveAppID() != APP_ID_SOS) { AppManager_StartApp(APP_ID_SOS); return; } - // 关机事件处理:捕获到长按关机指令,直接调 Power_Off 关断电源 + // 关机事件处理:捕获到长按关机指令,直接调用 Power_Off 关断电源 if (evt.key_event == KEY_EVENT_POWER_OFF) { Power_Off(); return; } - // 常事件(如普通的按键点击)直接传分发给当前于前台活跃的应 + // 常规事件(如普通的按键点击)直接透传分发给当前处于前台活跃的应用 AppManager_DispatchEvent(evt); } /** - * @brief 定时 1 初化,定 1ms 周期 + * @brief 定时器 1 初始化,设定 1ms 中断周期 */ void Timer1_Init(void) { - AUXR |= 0x40; // 设定定时 1 1T 模式 (无分频,主直接输) - TMOD &= 0x0F; // 设置定时 1 模式寄存,保留低四位 (定时0配置) + AUXR |= 0x40; // 设定定时器 1 为 1T 模式 (无分频,主频直接输入) + TMOD &= 0x0F; // 设置定时器 1 模式寄存器,保留低四位 (定时器0配置) { - // 按照系统主 (MAIN_Fosc) 计算 1ms 对应的重装载 + // 按照系统主频 (MAIN_Fosc) 计算 1ms 对应的重装载值 u16 reload = (u16)(65536UL - (MAIN_Fosc / 1000UL)); - TL1 = (u8)reload; // 载入 8 - TH1 = (u8)(reload >> 8);// 载入 8 + TL1 = (u8)reload; // 载入低 8 位 + TH1 = (u8)(reload >> 8);// 载入高 8 位 } - ET1 = 1; // 定时 1 允 - TR1 = 1; // 动定时器 1 计数 - EA = 1; // 全局允 + ET1 = 1; // 开启定时器 1 中断允许 + TR1 = 1; // 启动定时器 1 计数 + EA = 1; // 开启全局中断允许 } -// 全局按键描触发标 +// 全局按键扫描触发标志 volatile bit key_scan_flag = 0; /** - * @brief 定时 1 服务程序 ( 1ms 触发) - * @details 承担系统时钟计算,并 10ms 位主按键描标志,不直接在进 ADC 多采样 + * @brief 定时器 1 中断服务程序 (每 1ms 触发一次) + * @details 承担系统软时钟计算,并每 10ms 置位主循环按键扫描标志,不直接在中断中进行 ADC 多次采样。 */ void Timer1_Isr(void) interrupt 3 { - // 1ms 定时更新时钟 + // 1ms 定时更新软时钟 Clock_IncMS(); - // 报屏幕红边以 100ms 周期,进行色爆闪指示 + // 报警屏幕红边框以 100ms 周期亮灭,进行颜色爆闪指示 alarm_flash_flag = (ms_tick / 100) % 2; - /* 物理按键定时计数 ( 10ms 位一次标志位) */ + /* 物理按键定时计数 (每 10ms 置位一次标志位) */ key_scan_timer++; if (key_scan_timer >= 10) { - key_scan_timer = 0; // 重置描分频数 - key_scan_flag = 1; // 仅在里置起标志位,留待主执实际取与消 + key_scan_timer = 0; // 重置扫描分频计数器 + key_scan_flag = 1; // 仅在中断里置起标志位,留待主循环中执行实际读取与消抖 } } /** - * @brief 主循按键描与消抖处理函数 - * @details 由主 10ms 查并执,完全在主线程运,绝无重入冲突 + * @brief 主循环按键扫描与消抖处理函数 + * @details 由主循环每 10ms 检查并执行,完全在主线程中运行,绝无中断重入冲突。 */ void Event_KeyScan_Poll(void) { SystemEvent evt; - // 读取按键的电平信 (KEY_UP, KEY_CONFIRM 为低电平有效) + // 读取按键的电平信号 (KEY_UP, KEY_CONFIRM 为低电平有效) u8 raw_up = !KEY_UP; - u8 raw_down = ADC_IsKeyDown(); // KEY_DOWN 通过 P0.3 ADC 通道判定 (常 2.0V, 按下 3.7V) + u8 raw_down = ADC_IsKeyDown(); // KEY_DOWN 通过 P0.3 ADC 通道判定 (常态 2.0V, 按下 3.7V) u8 raw_confirm = !KEY_CONFIRM; - u8 raw_sos = !KEY_SOS; // 恢 SOS 物理按键电平 - key_scan_timer = 0; // 重置描分频数 + u8 raw_sos = !KEY_SOS; // 恢复 SOS 物理按键电平检测 + key_scan_timer = 0; // 重置扫描分频计数器 - // 【SOS 按键电平实时诊断 + // 【SOS 按键电平实时诊断】 { static u8 last_raw_sos = 99; if (raw_sos != last_raw_sos) { @@ -210,7 +210,7 @@ void Event_KeyScan_Poll(void) } } - // 按键调试诊断:若按键 ADC 值有变化或于高电平,串口输出真实 ADC + // 按键调试诊断:若按键 ADC 值有变化或处于高电平,串口输出真实 ADC 值 { u16 down_val = ADC_ReadFiltered(ADC_CHANNEL_KEY_DOWN); if (down_val > 2800) { @@ -223,35 +223,35 @@ void Event_KeyScan_Poll(void) } } - // 测到有任意按处于下压状,立即将无操作闲置休眠计时器归 + // 检测到有任意按键处于下压状态,立即将无操作闲置休眠计时器归零 if (raw_up || raw_down || raw_confirm || raw_sos) { inactivity_timer = 0; } else { - // 如果在常态下(非休眠、非报非紧求救),累加闲时间 + // 如果在常态下(非休眠、非报警、非紧急求救),累加闲置时间 if (current_state != STATE_SLEEP && current_state != STATE_ALARM && current_state != STATE_SOS_EMITTED) { inactivity_timer++; } } - // 默将派发事件重置为空 + // 默认将派发事件重置为空 evt.key_event = KEY_EVENT_NONE; evt.priority = EVENT_PRIORITY_NORMAL; evt.extra_data = 0; evt.extra_size = 0; - // 双键组合判定:▲ and 同时按下的消抖与生成 + // 双键组合判定:▲ 键 and ▼ 键同时按下的消抖与生成 if (raw_up && raw_down) { comb_hold++; - if (comb_hold == 300) { // 持续按下 3 (300 * 10ms = 3s) + if (comb_hold == 300) { // 持续按下达 3 秒 (300 * 10ms = 3s) evt.key_event = KEY_EVENT_UP_DOWN_COMB; EventQueue_Push(evt); // 压入队列 } - key_up_hold = 0; // 清零单个按键计数,防止双击事件干 + key_up_hold = 0; // 清零单个按键计数,防止双击事件干扰 key_down_hold = 0; } else { - comb_hold = 0; // 组合松开归零 + comb_hold = 0; // 组合键松开归零 - // (KEY_UP) 按键状机 + // ▲ (KEY_UP) 按键状态机 if (raw_up) { key_up_hold++; if (key_up_hold == 300) { // 达到 3 秒判定为长按 @@ -259,7 +259,7 @@ void Event_KeyScan_Poll(void) EventQueue_Push(evt); } } else { - // 如果松开时按下数在消抖阈值(20ms)至长按阈(3s)之间,则判定为次短 + // 如果松开时按下计数在消抖阈值(20ms)至长按阈值(3s)之间,则判定为一次短按 if (key_up_hold >= 2 && key_up_hold < 300) { evt.key_event = KEY_EVENT_UP_CLICK; EventQueue_Push(evt); @@ -267,15 +267,15 @@ void Event_KeyScan_Poll(void) key_up_hold = 0; // 释放归零 } - // (KEY_DOWN / 关机) 按键状机 + // ▼ (KEY_DOWN / 开关机) 按键状态机 if (raw_down) { key_down_hold++; - if (key_down_hold == 100) { // 长按 1 (100 * 10ms = 1000ms = 1s) 触发关机事件 + if (key_down_hold == 100) { // 长按 1 秒 (100 * 10ms = 1000ms = 1s) 触发关机事件 evt.key_event = KEY_EVENT_POWER_OFF; EventQueue_Push(evt); } } else { - // 如果松开时按下时长在 20ms 1s 之间,触发短按下/选择事件 + // 如果松开时按下时长在 20ms 至 1s 之间,触发短按下翻/选择事件 if (key_down_hold >= 2 && key_down_hold < 100) { evt.key_event = KEY_EVENT_DOWN_CLICK; EventQueue_Push(evt); @@ -283,10 +283,10 @@ void Event_KeyScan_Poll(void) key_down_hold = 0; // 释放归零 } - // (KEY_CONFIRM) 认按状机 + // ■ (KEY_CONFIRM) 确认按键状态机 if (raw_confirm) { key_confirm_hold++; - if (key_confirm_hold == 300) { // 长按 3 秒打主菜 + if (key_confirm_hold == 300) { // 长按 3 秒打开主菜单 evt.key_event = KEY_EVENT_CONFIRM_LONG; EventQueue_Push(evt); } @@ -298,23 +298,23 @@ void Event_KeyScan_Poll(void) key_confirm_hold = 0; // 释放归零 } - // SOS Ȱ״̬ (ѽ) + // SOS 物理求救按键状态机 (已解锁) if (raw_sos) { key_sos_hold++; - if (key_sos_hold == 300) { // 3 룬ȣ + if (key_sos_hold == 300) { // 长按 3 秒,发出求救,插入队首 evt.key_event = KEY_EVENT_SOS_LONG; evt.priority = EVENT_PRIORITY_URGENT; EventQueue_InsertFront(evt); Uart_SendString("[KEY-DIAG] SOS Long Press Dispatched!\r\n"); } } else { - if (key_sos_hold >= 2 && key_sos_hold < 300) { // ̰ȣ + if (key_sos_hold >= 2 && key_sos_hold < 300) { // 短按即呼救,插入队首 evt.key_event = KEY_EVENT_SOS_CLICK; evt.priority = EVENT_PRIORITY_URGENT; EventQueue_InsertFront(evt); Uart_SendString("[KEY-DIAG] SOS Click Dispatched!\r\n"); } - key_sos_hold = 0; // ͷŹ + key_sos_hold = 0; // 释放归零 } } } diff --git a/App/main.c b/App/main.c index 8ee0ba9..18cc371 100644 --- a/App/main.c +++ b/App/main.c @@ -11,82 +11,82 @@ /* ========================================================================= * 系统全局变量定义 * ========================================================================= */ -SystemState current_state = STATE_NORMAL; // 系统整体的当前运行状态,默为正常时钟待机 +SystemState current_state = STATE_NORMAL; // 系统整体的当前运行状态,默认为正常时钟待机页 -u8 menu_select = 0; // 主菜单当前中的高索引 (0~2) -u32 captured_addr = 0; // 配或报时,捕获到的传感器 24 EV1527 射物理地 -u8 captured_type = 0; // 正在对码或触发报的传感器设类 (0:门, 1:PIR...) -u8 alarm_sensor_slot = 0; // 触发警报的传感器 sensor_list 数组的索引槽位号 +u8 menu_select = 0; // 主菜单当前选中的行高亮索引 (0~2) +u32 captured_addr = 0; // 配对或报警时,捕获到的传感器 24 位 EV1527 射频物理地址 +u8 captured_type = 0; // 正在对码或触发警报的传感器设备类型 (0:门磁, 1:PIR...) +u8 alarm_sensor_slot = 0; // 触发警报的传感器在 sensor_list 数组中的索引槽位号 -// 传感器存储列表数库,位于单片机部 XDATA 存储空间,共 16 槽位 +// 传感器存储列表数据库,位于单片机外部 XDATA 存储空间,总共 16 个槽位 Sensor_Slot xdata sensor_list[16]; -// 全局滴答与定时中计数变量,位 volatile 防编译器优 -volatile u16 ms_tick = 0; // 定时 1ms 基准滴答加器 -volatile u16 inactivity_timer = 0; // 用户闲置计时,用于无操作判定(单: 10ms -volatile u32 pair_timeout_ms = 0; // 传感器配对码超时秒时 (上限 30000ms 30s) -volatile u16 alarm_timer_ms = 0; // 入侵报马达震动时序数 -volatile u16 motor_timer_ms = 0; // 立马达震动时长数 -volatile bit alarm_flash_flag = 0; // 报时红周期闪烁亮标志 +// 全局滴答与定时中断计数变量,位于 volatile 防止被编译器优化 +volatile u16 ms_tick = 0; // 定时中断 1ms 基准滴答累加器 +volatile u16 inactivity_timer = 0; // 用户闲置计时器,用于无操作判定(单位: 10ms) +volatile u32 pair_timeout_ms = 0; // 传感器配对对码超时毫秒计时器 (上限 30000ms 即 30s) +volatile u16 alarm_timer_ms = 0; // 入侵报警马达震动时序计数器 +volatile u16 motor_timer_ms = 0; // 独立马达震动时长计数器 +volatile bit alarm_flash_flag = 0; // 报警时红框周期性闪烁亮灭标志位 -// 物理按键描与消抖长按计数 (10ms 描一) -volatile u16 key_scan_timer = 0; // 10ms 描周期分频器 -volatile u16 key_up_hold = 0; // (KEY_UP) 按下时长 -volatile u16 key_down_hold = 0; // (KEY_DOWN) 按下时长 -volatile u16 key_confirm_hold = 0; // (KEY_CONFIRM) 按下时长 -volatile u16 key_sos_hold = 0; // SOS 按下时长 -volatile u16 comb_hold = 0; // + 组合按下时长 +// 物理按键扫描与消抖长按计数器 (10ms 扫描一次) +volatile u16 key_scan_timer = 0; // 10ms 扫描周期分频器 +volatile u16 key_up_hold = 0; // ▲ (KEY_UP) 按下时长累加 +volatile u16 key_down_hold = 0; // ▼ (KEY_DOWN) 按下时长累加 +volatile u16 key_confirm_hold = 0; // ■ (KEY_CONFIRM) 按下时长累加 +volatile u16 key_sos_hold = 0; // SOS 按下时长累加 +volatile u16 comb_hold = 0; // ▲ + ▼ 组合键按下时长累加 /* ========================================================================= - * 串口调试指令解析与事件派发封 + * 串口调试指令解析与事件派发封装 * ========================================================================= */ /** - * @brief 解析通过串口 1 发来的键盘及测试模拟指令 - * @param cmd 接收到的 ASCII 字命 - * @details 负责将字符指令转化应的系统按或射频事件,并推入事件队列以测试件架构: - * - 'u'/'U': 模拟/长按 - * - 'd'/'D': 模拟/长按 - * - 's'/'S': 模拟/长按 SOS 物理(优先插入队首) - * - 'c'/'C': 模拟组合触发 - * - 'a'/'A': 模拟无线传感器入侵触发报信号,地固定 0x123456,zone=1 - * - 'p'/'P': 模拟对码期间捕获 24 位无线码信号,地固定 0x789ABC - * - '1'/'2'/'3': 模拟系统的常、已布防、已撤防状切 - * - 'z'/'Z': 模拟超时,强行使闲置计数器置 6000,触发自动深度休眠 - * - 't'/'T': 触发屏幕与无线控制引脚测试 + * @brief 解析通过串口 1 发送来的键盘及测试模拟指令 + * @param cmd 接收到的 ASCII 字符命令 + * @details 负责将字符指令转化对应的系统按键或射频事件,并推入事件队列以测试软件架构: + * - 'u'/'U': 模拟短按/长按 ▲ 键。 + * - 'd'/'D': 模拟短按/长按 ▼ 键。 + * - 's'/'S': 模拟短按/长按 SOS 物理键(优先插入队首)。 + * - 'c'/'C': 模拟组合键触发。 + * - 'a'/'A': 模拟无线传感器入侵触发警报信号,地址固定为 0x123456,zone=1。 + * - 'p'/'P': 模拟对码期间捕获的 24 位无线对码信号,地址固定为 0x789ABC。 + * - '1'/'2'/'3': 模拟系统的正常、已布防、已撤防状态切换。 + * - 'z'/'Z': 模拟超时,强行使闲置计数器置 6000,触发自动深度休眠。 + * - 't'/'T': 触发屏幕与无线控制引脚测试。 */ /** - * @brief ڵרRGB ɫͨŲ - * @details ηʹ졢̡ÿɫ 2 룬Уʵɫӳϵ + * @brief 串口调试专属:RGB 物理颜色通道排查测试 + * @details 依次发送纯红、纯绿、纯蓝的数据流,每个颜色亮起 2 秒,帮助肉眼校对物理灯珠的实际颜色映射关系 */ void RGB_Diagnostic_Test(void) { Uart_SendString("\r\n=== RGB LED Color Channel Test Starting ===\r\n"); - // 1. Էͺɫ (G=0, R=150, B=0) + // 1. 尝试发送红色控制数据 (G=0, R=150, B=0) Uart_SendString("[RGB-TEST] 1. Sending RED command -> G=0, R=150, B=0\r\n"); RGB_Send(0, 150, 0, 0, 150, 0); Delay_ms(2000); - // Ϩ + // 熄灭 RGB_Send(0, 0, 0, 0, 0, 0); Delay_ms(300); - // 2. Էɫ (G=150, R=0, B=0) + // 2. 尝试发送绿色控制数据 (G=150, R=0, B=0) Uart_SendString("[RGB-TEST] 2. Sending GREEN command -> G=150, R=0, B=0\r\n"); RGB_Send(150, 0, 0, 150, 0, 0); Delay_ms(2000); - // Ϩ + // 熄灭 RGB_Send(0, 0, 0, 0, 0, 0); Delay_ms(300); - // 3. Էɫ (G=0, R=0, B=150) + // 3. 尝试发送蓝色控制数据 (G=0, R=0, B=150) Uart_SendString("[RGB-TEST] 3. Sending BLUE command -> G=0, R=0, B=150\r\n"); RGB_Send(0, 0, 150, 0, 0, 150); Delay_ms(2000); - // Ϩ + // 彻底熄灭 RGB_Send(0, 0, 0, 0, 0, 0); Uart_SendString("=== RGB LED Color Channel Test Ended ===\r\n"); } @@ -94,7 +94,7 @@ void RGB_Diagnostic_Test(void) void Debug_ProcessCommand(char cmd) { SystemEvent debug_evt; - inactivity_timer = 0; // 收到指令,立即重用户的无操作闲置计时 + inactivity_timer = 0; // 收到指令,立即重置用户的无操作闲置计时器 debug_evt.extra_data = 0; debug_evt.extra_size = 0; @@ -121,7 +121,7 @@ void Debug_ProcessCommand(char cmd) } else if (cmd == 's') { debug_evt.key_event = KEY_EVENT_SOS_CLICK; debug_evt.priority = EVENT_PRIORITY_URGENT; - EventQueue_InsertFront(debug_evt); // 紧事件插入队 + EventQueue_InsertFront(debug_evt); // 紧急事件插入队首 Uart_SendString("[UART] KEY_SOS Clicked\r\n"); } else if (cmd == 'S') { debug_evt.key_event = KEY_EVENT_SOS_LONG; @@ -136,22 +136,22 @@ void Debug_ProcessCommand(char cmd) } else if (cmd == 'a' || cmd == 'A') { u32 mock_addr = 0x123456; // 设定模拟的报警射频源地址 u8 mock_slot; - // 如果地数库中没有匹配该射频,则强制登记进 1 号防 + // 如果本地数据库中没有匹配该射频,则强制登记进 1 号防区 if (!Check_Sensor_ID(mock_addr, &mock_slot)) { Add_Sensor_With_Zone(mock_addr, 0, 1); } debug_evt.key_event = KEY_EVENT_NONE; - debug_evt.priority = EVENT_PRIORITY_HIGH; // 入侵报为 HIGH 优先 + debug_evt.priority = EVENT_PRIORITY_HIGH; // 入侵报警设为 HIGH 优先级 debug_evt.extra_data = mock_addr; debug_evt.extra_size = 4; EventQueue_InsertFront(debug_evt); // 高优先级插队 Uart_SendString("[UART] Mock RF Alarm\r\n"); } else if (cmd == 'p' || cmd == 'P') { - // 有当手环切入 Pair 模式时,模拟对码信号才有 + // 只有当手环切入 Pair 模式时,模拟对码信号才有效 if (AppManager_GetActiveAppID() == APP_ID_PAIR) { debug_evt.key_event = KEY_EVENT_NONE; debug_evt.priority = EVENT_PRIORITY_NORMAL; - debug_evt.extra_data = 0x789ABC; // 设定模拟对码件地 + debug_evt.extra_data = 0x789ABC; // 设定模拟对码硬件地址 debug_evt.extra_size = 4; EventQueue_Push(debug_evt); Uart_SendString("[UART] Mock RF Pair Signal\r\n"); @@ -159,157 +159,157 @@ void Debug_ProcessCommand(char cmd) Uart_SendString("[UART] Not in PAIR mode\r\n"); } } else if (cmd == '1') { - // 模拟切换 正常撤防 (NORMAL) 状并刷新时钟 + // 模拟切换至 正常撤防 (NORMAL) 状态并刷新时钟 if (AppManager_GetActiveAppID() == APP_ID_CLOCK) { current_state = STATE_NORMAL; UI_ShowClockPage(current_state, current_hour, current_min); } Uart_SendString("[UART] State: NORMAL\r\n"); } else if (cmd == '2') { - // 模拟切换 已布 (ARMED) 状,通门磁可触发警报 + // 模拟切换至 已布防 (ARMED) 状态,普通门磁可触发警报 if (AppManager_GetActiveAppID() == APP_ID_CLOCK) { current_state = STATE_ARMED; UI_ShowClockPage(current_state, current_hour, current_min); } Uart_SendString("[UART] State: ARMED\r\n"); } else if (cmd == '3') { - // 模拟切换 已撤 (DISARMED) 状,忽略通报 + // 模拟切换至 已撤防 (DISARMED) 状态,忽略普通报警 if (AppManager_GetActiveAppID() == APP_ID_CLOCK) { current_state = STATE_DISARMED; UI_ShowClockPage(current_state, current_hour, current_min); } Uart_SendString("[UART] State: DISARMED\r\n"); } else if (cmd == 'z' || cmd == 'Z') { - inactivity_timer = 6000; // 模拟闲置 60 秒超时,强制触发主循深度休眠 + inactivity_timer = 6000; // 模拟闲置 60 秒超时,强制触发主循环深度休眠 Uart_SendString("[UART] Force Entering Sleep mode!\r\n"); } else if (cmd == 'q' || cmd == 'Q') { - RGB_Diagnostic_Test(); // RGB ɫͨŲ + RGB_Diagnostic_Test(); // 启动 RGB 物理颜色通道排查测试 } else if (cmd == 't' || cmd == 'T') { - RF_DiagnosticMode('t'); // 触发射连发射诊断 (绿色爆闪) + RF_DiagnosticMode('t'); // 触发射频连续发射诊断 (绿色爆闪) } else if (cmd == 'r' || cmd == 'R') { - RF_DiagnosticMode('r'); // 触发 5 秒射频接收监诊断 + RF_DiagnosticMode('r'); // 触发 5 秒射频接收监听诊断 } else if (cmd == 'l' || cmd == 'L') { - Loopback_Test(); // 触发物理电气回环测试 ( P2.1 P3.6 ) + Loopback_Test(); // 触发物理电气回环测试 (短路 P2.1 和 P3.6 自检) } } /* ========================================================================= - * 主函 (入口) + * 主函数 (入口) * ========================================================================= */ void main(void) { char cmd; - // ====== STC32G 特殊功能寄存器内核基初化 ====== - WTST = 0; // 设置程序 Flash 访问等待时间 0 (高运) - EAXFR = 1; // 允问扩展特殊功能寄存 (XSFR) - CKCON = 0; // 外部总线时钟设定为最 + // ====== STC32G 特殊功能寄存器内核基础初始化 ====== + WTST = 0; // 设置程序 Flash 访问等待时间为 0 (最高速运行) + EAXFR = 1; // 允许访问扩展特殊功能寄存器 (XSFR) + CKCON = 0; // 外部总线时钟设定为最快 WDT_CONTR = 0x00; // 关闭看门狗定时器 - // ====== 件各模块基初化 ====== - GPIO_Init(); // 配置各个引脚的推挽输/准双向模式及内部上拉 (锁存 PWR_HOLD=1) - RGB_Send(0, 0, 0, 0, 0, 0); // 【修正:上电时间强制发 0 码关条,熄灭上电暂引起的随机白光 - RF_Init(); // 初化射芯片各引脚控制 - Delay_ms(500); // 动延时,等待供电电稳 - LCD_Init(); // 初化 LHS114TC-IF03 (ST7789V) 屏幕控制寄存器序列并背光 - Uart1_Init(); // 初化串口 1 波特 115200 供调试日 + // ====== 硬件各模块基础初始化 ====== + GPIO_Init(); // 配置各个引脚的推挽输出/准双向模式及内部上拉 (锁存 PWR_HOLD=1) + RGB_Send(0, 0, 0, 0, 0, 0); // 【首要修正】:上电第一时间强制发送 0 码关闭灯条,熄灭上电暂态引起的随机白光! + RF_Init(); // 初始化射频芯片各引脚控制 + Delay_ms(500); // 软启动延时,等待供电电容稳定 + LCD_Init(); // 初始化 LHS114TC-IF03 (ST7789V) 屏幕控制寄存器序列并开启背光 + Uart1_Init(); // 初始化串口 1 波特率 115200 供调试日志 Uart_SendString("Wristband System Initialized!\r\n"); - - Self_Test(); // 执上电硬件自 (PWR_HOLD锁存、P0.3 ADC按键常P1.3 电池电量) - Load_Database(); // IAP Flash 254 扇区读取有已绑定的传感器数据 RAM - /* 步 1: 初化事件形缓冲区队列 */ + Self_Test(); // 执行上电硬件自检 (PWR_HOLD锁存、P0.3 ADC按键常态、P1.3 电池电量) + Load_Database(); // 从 IAP Flash 第 254 扇区读取所有已绑定的传感器数据至 RAM + + /* 步骤 1: 初始化事件环形缓冲区队列 */ EventQueue_Init(); - /* 步 2+5: 初化应用管理器并登所有前台应,默认加 ClockApp */ + /* 步骤 2+5: 初始化应用管理器并登记所有前台应用,默认加载 ClockApp */ AppManager_Init(); - /* 动并挂载后台射监控应,开始侦无线传感器信 */ + /* 启动并挂载后台射频监控应用,开始侦听无线传感器信号 */ AppManager_AttachToBackground(&rf_monitor_app); - /* 1ms 系统基准定时 Timer1 */ + /* 启动 1ms 系统基准定时器 Timer1 */ Timer1_Init(); - /* 机自动加载并运待机时钟前台活跃应 */ + /* 开机自动加载并运行待机时钟前台活跃应用 */ AppManager_StartApp(APP_ID_CLOCK); Uart_SendString("[SYS] Event-driven framework ready\r\n"); - // ====== 主循 ====== + // ====== 主循环 ====== while (1) { - /* 0. 查主按键定时标志 ( 1ms 位,主循响应清除并理) */ + /* 0. 检查主循环按键定时标志 (由 1ms 中断置位,主循环响应清除并处理) */ if (key_scan_flag) { key_scan_flag = 0; Event_KeyScan_Poll(); } - /* 1. 串口非阻塞指令捕 -> 装配为事件方式入 */ + /* 1. 串口非阻塞指令捕获 -> 装配为事件方式入队 */ cmd = Uart_RxChar(); if (cmd != '\0') { Debug_ProcessCommand(cmd); } - /* 2. 事件分发总线:循询并处理队列的所有有效事 */ + /* 2. 事件分发总线:循环轮询并处理队列中的所有有效事件 */ while (!EventQueue_IsEmpty()) { Event_Dispatcher_Loop(); } - /* 3. 应用调度心:运当前于前台活跃状的应用(集成了 CPU 运时间超时挂起保护) */ + /* 3. 应用调度中心:运行当前处于前台活跃状态的应用(集成了 CPU 运行时间超时挂起保护) */ AppManager_RunActiveApp(); - /* 4. 动休眠判定:常无操作闲置达到 60 (6000 * 10ms = 60s) 时,切入低功耗停机休 */ + /* 4. 自动休眠判定:常态无操作闲置达到 60 秒 (6000 * 10ms = 60s) 时,切入低功耗停机休眠 */ if (inactivity_timer >= 6000) { inactivity_timer = 0; - current_state = STATE_SLEEP; // 系统为休眠状 - Enter_Low_Power_Sleep(); // 该函数是阻的,MCU 将在此挂起唤醒后将自动从其后继续执 - current_state = STATE_NORMAL; // 唤醒后重新置为常待机状 - AppManager_StartApp(APP_ID_CLOCK); // 重新时钟应用以刷新画面和背光 + current_state = STATE_SLEEP; // 系统置为休眠状态 + Enter_Low_Power_Sleep(); // 该函数是阻塞的,MCU 将在此挂起。唤醒后将自动从其后继续执行 + current_state = STATE_NORMAL; // 唤醒后重新置为正常待机状态 + AppManager_StartApp(APP_ID_CLOCK); // 重新开启时钟应用以刷新画面和背光 } } } -// 唤醒源事件标志,由应的部 ISR 写入 -volatile u8 p0_wakeup_flag = 0; // P0 口按唤醒标志 -volatile u8 p2_wakeup_flag = 0; // P2 口按唤醒标志 -volatile u8 p3_wakeup_flag = 0; // P3 口射频中唤醒标志 -volatile u8 rf_wakeup_flag = 0; // 射中唤醒标志 +// 唤醒源事件标志,由对应的外部中断 ISR 写入 +volatile u8 p0_wakeup_flag = 0; // P0 端口按键唤醒标志 +volatile u8 p2_wakeup_flag = 0; // P2 端口按键唤醒标志 +volatile u8 p3_wakeup_flag = 0; // P3 端口射频中断唤醒标志 +volatile u8 rf_wakeup_flag = 0; // 射频中断唤醒标志 /* ========================================================================= - * STC32G 口掉电部唤醒服务程序 (ISR) + * STC32G 端口掉电外部中断唤醒中断服务程序 (ISR) * ========================================================================= */ /** - * @brief Port0 口掉电中服务函数 ( isr_jump.asm 引定位至) - * @details 负责处理 P0.1, P0.2, P0.3 按键在休眠模式下的物理触发,并立刻用防反复执行 + * @brief Port0 端口掉电中断服务函数 (由 isr_jump.asm 引导定位至此) + * @details 负责处理 P0.1, P0.2, P0.3 按键在休眠模式下的物理触发,并立刻禁用中断防止反复执行。 */ void Port0_Isr(void) interrupt 13 { - EAXFR = 1; // 使能访问扩展寄存 - p0_wakeup_flag = P0INTF | 0x01;// 读取并份 P0 标志寄存器 (0x01作安全掩) - P0INTF = 0x00; // 清除 P0 口中挂标志位 - P0INTE = 0x00; // 【关保护】:立即关闭 P0 允,防按处于低电平期间反复触 ISR 导致堆栈溢出 + EAXFR = 1; // 使能访问扩展寄存区 + p0_wakeup_flag = P0INTF | 0x01;// 读取并备份 P0 中断标志寄存器值 (0x01作安全掩码) + P0INTF = 0x00; // 清除 P0 端口中断悬挂标志位 + P0INTE = 0x00; // 【关键保护】:立即关闭 P0 中断允许,防止按键处于低电平期间反复触发 ISR 导致堆栈溢出 } /** - * @brief Port2 口掉电中服务函数 ( isr_jump.asm 引定位至) - * @details 负责处理 P2.6 (SOS按键) P2.1 (射数) 在休眠模式下的物理跳变唤醒 + * @brief Port2 端口掉电中断服务函数 (由 isr_jump.asm 引导定位至此) + * @details 负责处理 P2.6 (SOS按键) 与 P2.1 (射频数据脚) 在休眠模式下的物理跳变唤醒。 */ void Port2_Isr(void) interrupt 15 { - EAXFR = 1; // 使能扩展寄存 - p2_wakeup_flag = P2INTF | 0x01;// 读取并份 P2 标志寄存区的 - P2INTF = 0x00; // 清除 P2 口中挂标志位 - P2INTE = 0x00; // 立即关闭 P2 通道,防低电平期间反复重 + EAXFR = 1; // 使能扩展寄存区 + p2_wakeup_flag = P2INTF | 0x01;// 读取并备份 P2 中断标志寄存区的值 + P2INTF = 0x00; // 清除 P2 端口中断悬挂标志位 + P2INTE = 0x00; // 立即关闭 P2 中断通道,防止低电平期间反复重入 } /** - * @brief Port3 口掉电中服务函数 ( isr_jump.asm 引定位至) - * @details 负责处理 P3.6 (新射频数) 在休眠模式下的物理跳变唤醒 + * @brief Port3 端口掉电中断服务函数 (由 isr_jump.asm 引导定位至此) + * @details 负责处理 P3.6 (新射频数据脚) 在休眠模式下的物理跳变唤醒。 */ void Port3_Isr(void) interrupt 16 { - EAXFR = 1; // 使能访问扩展寄存 - p3_wakeup_flag = P3INTF | 0x01;// 读取并份 P3 标志寄存器 (0x01作安全掩) - P3INTF = 0x00; // 清除 P3 口中挂标志位 - P3INTE = 0x00; // 立即关闭 P3 通道,防止反复重 + EAXFR = 1; // 使能访问扩展寄存器 + p3_wakeup_flag = P3INTF | 0x01;// 读取并备份 P3 中断标志寄存器值 (0x01作安全掩码) + P3INTF = 0x00; // 清除 P3 端口中断悬挂标志位 + P3INTE = 0x00; // 立即关闭 P3 中断通道,防止反复重入 } diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..a7ef4d1 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,7 @@ +## Git Workflow Requirements +- You must make a separate Git commit immediately after completing every individual, logical task (e.g., a bug fix, feature addition, or test case addition). +- Never accumulate multiple independent features or fixes into a single chunk of work. +- Automatically stage the files and generate a commit message matching Conventional Commits standards (e.g., `feat(auth): add login validation`). +- Do not ask for user confirmation before running `git add` and `git commit` locally. +- each `git commit` message must not more 100 words +- always increase APP_VERSION after each change. \ No newline at end of file diff --git a/Drivers/lcd.c b/Drivers/lcd.c index 031c699..a18dba7 100644 --- a/Drivers/lcd.c +++ b/Drivers/lcd.c @@ -3,108 +3,108 @@ #include "../App/config.h" #include "intrins.h" -// ====== LHS114TC-IF03 (ST7789V) Ļ SPI ӳ ====== -sbit LCD_RST = P1^0; // ĻӲλ (RESET)͵ƽλ -sbit LCD_DCX = P1^1; // / ѡ (1:, 0:ָ) -sbit LCD_SDI = P1^4; // SPI ӻ (MOSI) -sbit LCD_SCL = P1^5; // SPI йʱ (SCLK) -sbit LCD_CS = P1^6; // SPI Ƭѡѡ (CS)͵ƽѡͨĻ +// ====== LHS114TC-IF03 (ST7789V) 屏幕 SPI 引脚映射 ====== +sbit LCD_RST = P1^0; // 屏幕硬件复位引脚 (RESET),低电平复位 +sbit LCD_DCX = P1^1; // 串行数据 / 命令选择控制线 (1:数据, 0:指令) +sbit LCD_SDI = P1^4; // SPI 主机输出从机输入数据线 (MOSI) +sbit LCD_SCL = P1^5; // SPI 串行工作时钟线 (SCLK) +sbit LCD_CS = P1^6; // SPI 物理片选选择端 (CS),低电平选通屏幕 -// ====== LHS114TC-IF03 LEDA ====== -sbit SGM_CTRL = P1^7; // LEDA (0:, 1:رϨ - PMOS) +// ====== LHS114TC-IF03 LEDA 背光控制引脚 ====== +sbit SGM_CTRL = P1^7; // LEDA 开启引脚 (0:开启背光, 1:关闭熄灭 - PMOS控制) -// ⲿʱ ( system.c ) +// 引用外部延时函数 (定义在 system.c 中) extern void Delay_ms(u16 ms); extern void Delay10us(void); /** - * @brief ģ 8 λ SPI ֽݷ - * @param dat ͵ 1 ֽ + * @brief 软件模拟 8 位串行 SPI 字节数据发送 + * @param dat 待发送的 1 字节数据 */ void SPI_SendData(u8 dat) { u8 i; for(i = 0; i < 8; i++) { - LCD_SCL = 0; // ʱӣ׼ + LCD_SCL = 0; // 拉低时钟,准备更新数据 if(dat & 0x80) LCD_SDI = 1; else LCD_SDI = 0; _nop_(); - LCD_SCL = 1; // ʱӣĻزλ + LCD_SCL = 1; // 拉高时钟,触发屏幕在上升沿采样数据位 _nop_(); - dat <<= 1; // һλ׼һλ + dat <<= 1; // 左移一位,准备发送下一位 } } /** - * @brief Ļд뵥ֽڿָ (DCX=0) + * @brief 向屏幕控制器写入单字节控制指令 (DCX=0) */ void WriteComm(u8 cmd) { - LCD_CS = 0; // Ƭѡ - LCD_DCX = 0; // лΪָģʽ + LCD_CS = 0; // 开启片选 + LCD_DCX = 0; // 切换为指令传输模式 SPI_SendData(cmd); - LCD_CS = 1; // رƬѡ + LCD_CS = 1; // 关闭片选 } /** - * @brief Ļд뵥ֽʾ (DCX=1) + * @brief 向屏幕控制器写入单字节显示数据 (DCX=1) */ void WriteData(u8 dat) { - LCD_CS = 0; // Ƭѡ - LCD_DCX = 1; // лΪݴģʽ + LCD_CS = 0; // 开启片选 + LCD_DCX = 1; // 切换为数据传输模式 SPI_SendData(dat); - LCD_CS = 1; // رƬѡ + LCD_CS = 1; // 关闭片选 } /** - * @brief ʱִĻӲλ + * @brief 按照物理时序执行屏幕硬件复位 */ void LCD_Reset(void) { LCD_RST = 1; Delay_ms(10); - LCD_RST = 0; // ͸λŽӲλ״̬ + LCD_RST = 0; // 拉低复位引脚进入硬件复位状态 Delay_ms(20); - LCD_RST = 1; // - Delay_ms(120); // ȴ 120ms ִкijʼ + LCD_RST = 1; // 重新拉高引脚 + Delay_ms(120); // 必须等待至少 120ms 才能执行后续的初始化 } /** - * @brief ʼ LHS114TC-IF03 (ST7789V) Ļ + * @brief 初始化 LHS114TC-IF03 (ST7789V) 屏幕控制器 */ void LCD_Init(void) { - // չ SFR Ȩ (EAXFR = 1)ȷԲ P1M1 P1M0 ģʽĴ + // 开启扩展 SFR 访问权限 (EAXFR = 1),确保可以操作 P1M1 和 P1M0 模式寄存器 P_SW2 |= 0x80; - // P1 ˿ڵ LCD_RST(P1.0)/DCX(P1.1)/SDI(P1.4)/SCL(P1.5)/CS(P1.6)/LEDA(P1.7) Ϊģʽ (M1=0, M0=1) + // 配置 P1 端口的 LCD_RST(P1.0)/DCX(P1.1)/SDI(P1.4)/SCL(P1.5)/CS(P1.6)/LEDA(P1.7) 为推挽输出模式 (M1=0, M0=1) P1M1 &= ~((1<<0) | (1<<1) | (1<<4) | (1<<5) | (1<<6) | (1<<7)); P1M0 |= ((1<<0) | (1<<1) | (1<<4) | (1<<5) | (1<<6) | (1<<7)); - // øųʼĬϵƽ - SGM_CTRL = 0; // PMOS͵ƽ (LEDA = 0) + // 设置各控制引脚初始默认电平 + SGM_CTRL = 0; // PMOS低电平点亮背光 (LEDA = 0) LCD_CS = 1; LCD_SCL = 1; LCD_RST = 1; LCD_DCX = 1; - LCD_Reset(); // ӲλĻ + LCD_Reset(); // 硬件复位屏幕 - // ====== д ST7789V ԭĴʼָ ====== - WriteComm(0x11); // Sleep Out ˳˯ - Delay_ms(120); // ˳˯ߺʱ 120ms + // ====== 写入 ST7789V 原厂寄存器初始化指令序列 ====== + WriteComm(0x11); // Sleep Out 退出睡眠 + Delay_ms(120); // 退出睡眠后必须延时至少 120ms - // Memory Data Access Control (MADCTL) ɨ跽 - // 0x00: £ңRGB ʽ + // Memory Data Access Control (MADCTL) 扫描方向控制 + // 0x00: 从上至下,从左至右,RGB 格式 WriteComm(0x36); WriteData(0x00); // Interface Pixel Format (COLMOD) - // 0x05 16-bit/pixel (RGB565ʽ) + // 0x05 代表 16-bit/pixel (RGB565格式) WriteComm(0x3A); WriteData(0x05); @@ -154,50 +154,50 @@ void LCD_Init(void) WriteData(0xA4); WriteData(0xA1); - // Positive Gamma Correction (E0h) ٤ + // Positive Gamma Correction (E0h) 正极性伽马 WriteComm(0xE0); WriteData(0xD0); WriteData(0x04); WriteData(0x0D); WriteData(0x11); WriteData(0x13); WriteData(0x2B); WriteData(0x3F); WriteData(0x54); WriteData(0x4C); WriteData(0x18); WriteData(0x0D); WriteData(0x0B); WriteData(0x1F); WriteData(0x23); - // Negative Gamma Correction (E1h) ٤ + // Negative Gamma Correction (E1h) 负极性伽马 WriteComm(0xE1); WriteData(0xD0); WriteData(0x04); WriteData(0x0C); WriteData(0x11); WriteData(0x13); WriteData(0x2C); WriteData(0x3F); WriteData(0x44); WriteData(0x51); WriteData(0x2F); WriteData(0x1F); WriteData(0x1F); WriteData(0x20); WriteData(0x23); - // Display Inversion On (21h) ԣST7789 IPS + // Display Inversion On (21h) 开启反显,ST7789 IPS 屏常用 WriteComm(0x21); - // Display On (29h) ʾ + // Display On (29h) 开启显示 WriteComm(0x29); Delay_ms(20); - // ʾȫֹĻ˲ֻ + // 开启显示后立即进行全屏清屏,防止屏幕亮起瞬间呈现花屏随机噪点 LCD_Clear(COLOR_BLACK); } /** - * @brief 趨Ļݸ»ƴ + * @brief 设定屏幕的数据更新绘制窗口区域 */ void LCD_SetWindow(u16 x_start, u16 x_end, u16 y_start, u16 y_end) { - // ڵײ㴰趨Уͳһƫõ͸Уϲͼֹ + // 在底层窗口设定中,统一进行物理偏置的透明校正,免除上层各个绘图函数的手工计算 x_start += LCD_X_OFFSET; x_end += LCD_X_OFFSET; y_start += LCD_Y_OFFSET; y_end += LCD_Y_OFFSET; - // д Column Address Set еַüĴ (0x2A) + // 写入 Column Address Set 列地址设置寄存器命令 (0x2A) WriteComm(0x2A); WriteData((u8)(x_start >> 8)); WriteData((u8)(x_start & 0xFF)); WriteData((u8)(x_end >> 8)); WriteData((u8)(x_end & 0xFF)); - // д Row Address Set еַüĴ (0x2B) + // 写入 Row Address Set 行地址设置寄存器命令 (0x2B) WriteComm(0x2B); WriteData((u8)(y_start >> 8)); WriteData((u8)(y_start & 0xFF)); @@ -206,26 +206,26 @@ void LCD_SetWindow(u16 x_start, u16 x_end, u16 y_start, u16 y_end) } /** - * @brief ָɫˢдȫĻ - * @param color 16λ RGB565 ɫֵ + * @brief 用指定色彩刷写清空全屏幕 + * @param color 16位 RGB565 颜色填充值 */ void LCD_Clear(u16 color) { u16 i, j; LCD_SetWindow(0, LCD_WIDTH - 1, 0, LCD_HEIGHT - 1); - WriteComm(0x2C); // RAM Write (0x2C) Դд + WriteComm(0x2C); // 发送 RAM Write (0x2C) 显存写入命令 for(i = 0; i < LCD_WIDTH; i++) { for(j = 0; j < LCD_HEIGHT; j++) { - WriteData((u8)(color >> 8)); // RGB565 8 λֽ - WriteData((u8)(color & 0xFF)); // RGB565 8 λֽ + WriteData((u8)(color >> 8)); // 发送 RGB565 高 8 位字节 + WriteData((u8)(color & 0xFF)); // 发送 RGB565 低 8 位字节 } } } /** - * @brief Ļָӿڻʵľ + * @brief 在屏幕指定视口绘制实心矩形 */ void LCD_FillRect(u16 x, u16 y, u16 w, u16 h, u16 color) { @@ -234,8 +234,8 @@ void LCD_FillRect(u16 x, u16 y, u16 w, u16 h, u16 color) u8 cl = (u8)(color & 0xFF); LCD_SetWindow(x, x + w - 1, y, y + h - 1); - WriteComm(0x2C); // дԴ - count = w * h; // ʵľΰ + WriteComm(0x2C); // 写入显存 + count = w * h; // 实心矩形包含的总像素数 for(i = 0; i < count; i++) { WriteData(ch); @@ -244,55 +244,55 @@ void LCD_FillRect(u16 x, u16 y, u16 w, u16 h, u16 color) } /** - * @brief ƿľα߽ - * @details ͨϵĴ LCD_FillRect ʵƾεϡ¡ߡ + * @brief 绘制空心矩形边界框 + * @details 通过组合调用四次 LCD_FillRect 实心线来绘制矩形的上、下、左、右四条边。 */ void LCD_DrawRectBorder(u16 x, u16 y, u16 w, u16 h, u16 color) { - LCD_FillRect(x, y, w, 1, color); // ϱ߿ - LCD_FillRect(x, y + h - 1, w, 1, color); // ±߿ - LCD_FillRect(x, y, 1, h, color); // ߿ - LCD_FillRect(x + w - 1, y, 1, h, color); // ұ߿ + LCD_FillRect(x, y, w, 1, color); // 上边框线 + LCD_FillRect(x, y + h - 1, w, 1, color); // 下边框线 + LCD_FillRect(x, y, 1, h, color); // 左边框线 + LCD_FillRect(x + w - 1, y, 1, h, color); // 右边框线 } /** - * @brief ȾʾһɫģλͼԴ - * @param x Ⱦʼ X - * @param y Ⱦʼ Y - * @param w λͼ (λ: ص) - * @param h λͼ߶ (λ: ص) - * @param bmp FLASH еĵɫָ (1ʾǰɫ0ʾɫ) - * @param color 16λǰɫ - * @param bg_color 16λɫ + * @brief 渲染显示一个单色字模位图资源 + * @param x 渲染起始坐标 X + * @param y 渲染起始坐标 Y + * @param w 位图宽度 (单位: 像素点数) + * @param h 位图高度 (单位: 像素点数) + * @param bmp FLASH 中的单色像素数组指针 (1表示画前景色,0表示画背景色) + * @param color 16位前景色 + * @param bg_color 16位背景色 */ void LCD_DrawMonoBitmap(u16 x, u16 y, u16 w, u16 h, unsigned char code *bmp, u16 color, u16 bg_color) { u16 r, c; - u16 byte_width = (w + 7) / 8; // ڵɫУһͼռõʵֽ + u16 byte_width = (w + 7) / 8; // 计算在单色编码中,一行图像占用的实际字节数 u8 ch = (u8)(color >> 8); u8 cl = (u8)(color & 0xFF); u8 bgh = (u8)(bg_color >> 8); u8 bgl = (u8)(bg_color & 0xFF); - LCD_SetWindow(x, x + w - 1, y, y + h - 1); // ƻԴ洰 + LCD_SetWindow(x, x + w - 1, y, y + h - 1); // 限制绘制显存窗口 WriteComm(0x2C); - // Ƕѭӵ 0 е 0 пʼÿλ + // 嵌套循环,从第 0 行第 0 列开始遍历每个像素位置 for(r = 0; r < h; r++) { for(c = 0; c < w; c++) { - u16 byte_idx = r * byte_width + (c / 8); // λǰڵɫͼеֽ - u8 bit_shift = 7 - (c % 8); // λڸֽжӦ bit λ (λǰ) + u16 byte_idx = r * byte_width + (c / 8); // 定位当前像素所在单色图数组中的字节索引 + u8 bit_shift = 7 - (c % 8); // 定位该像素在该字节中对应的 bit 位置 (高位在前) if(bmp[byte_idx] & (1 << bit_shift)) { - WriteData(ch); // λΪ 1ǰɫ + WriteData(ch); // 该位为 1,发送前景色 WriteData(cl); } else { - WriteData(bgh); // λΪ 0ͱɫ + WriteData(bgh); // 该位为 0,发送背景色 WriteData(bgl); } } @@ -300,30 +300,30 @@ void LCD_DrawMonoBitmap(u16 x, u16 y, u16 w, u16 h, unsigned char code *bmp, u16 } /** - * @brief ָȾһ 8x16 ߴͨӢ ASCII ַ - * @param ch ASCII ַ + * @brief 在指定坐标渲染输出一个 8x16 尺寸的普通英文 ASCII 字符 + * @param ch ASCII 字符本身 */ void LCD_ShowChar8x16(u16 x, u16 y, char ch, u16 color, u16 bg_color) { u8 r, c; - u8 char_idx = ch - 0x20; // ƫƼ㣺ASCII ɴӡַ 0x20 (ո) ʼ + u8 char_idx = ch - 0x20; // 偏移计算:ASCII 可打印字符从 0x20 (空格) 开始 u8 f_h = (u8)(color >> 8); u8 f_l = (u8)(color & 0xFF); u8 b_h = (u8)(bg_color >> 8); u8 b_l = (u8)(bg_color & 0xFF); - if(ch < 0x20 || ch > 0x7E) return; // ˲ɼַ + if(ch < 0x20 || ch > 0x7E) return; // 过滤不可见控制字符 LCD_SetWindow(x, x + 8 - 1, y, y + 16 - 1); WriteComm(0x2C); - // ѭ 16 У 8x16 + // 循环 16 行,绘制 8x16 字型 for(r = 0; r < 16; r++) { - u8 line = FONT_8x16[char_idx][r]; // ȡǰжӦ 8 λɫϢ + u8 line = FONT_8x16[char_idx][r]; // 提取当前行对应的 8 位单色像素信息 for(c = 0; c < 8; c++) { - // Ӹߵȡеص + // 从高到低依次提取单行中的像素点 if(line & (0x80 >> c)) { WriteData(f_h); @@ -339,37 +339,37 @@ void LCD_ShowChar8x16(u16 x, u16 y, char ch, u16 color, u16 bg_color) } /** - * @brief ָӡһͨӢ ASCII ַ () + * @brief 在指定坐标打印一行普通英文 ASCII 字符串 (左对齐) */ void LCD_ShowString(u16 x, u16 y, char *str, u16 color, u16 bg_color) { while(*str) { - LCD_ShowChar8x16(x, y, *str, color, bg_color); // Ⱦǰַ - x += 8; // X ʼλƫ 8 أ׼һַ - str++; // һַ + LCD_ShowChar8x16(x, y, *str, color, bg_color); // 渲染当前字符 + x += 8; // X 轴起始位置偏移 8 像素,准备绘制下一个字符 + str++; // 移向下一个字符 } } /** - * @brief ָ Y ߶ϣԶƫʵַˮƽʾ + * @brief 在指定 Y 轴高度上,自动计算偏移以实现字符串水平居中显示 */ void LCD_ShowStringCentered(u16 y, char *str, u16 color, u16 bg_color) { u16 len = 0; char *p = str; - while(*p++) len++; // ַ + while(*p++) len++; // 计算字符串长度 if(len * 8 >= LCD_WIDTH) - // ͬĻЧȣǿƴ 0 ʼ + // 若超出或等同于屏幕有效宽度,强制从最左侧 0 开始左对齐绘制 LCD_ShowString(0, y, str, color, bg_color); else - // ͨʽ (LCD_WIDTH - ַ) / 2 ˮƽеʼƫ X 겢Ⱦ + // 否则通过公式 (LCD_WIDTH - 字符宽度) / 2 计算出水平居中的首字起始偏移 X 坐标并渲染 LCD_ShowString((LCD_WIDTH - len * 8) / 2, y, str, color, bg_color); } /** - * @brief Ⱦش 16x32 ߴӢ/ַ + * @brief 渲染大号特粗 16x32 尺寸的英文/数字字符 */ void LCD_ShowChar16x32(u16 x, u16 y, char ch, u16 color, u16 bg_color) { @@ -383,22 +383,22 @@ void LCD_ShowChar16x32(u16 x, u16 y, char ch, u16 color, u16 bg_color) if(ch < 0x20 || ch > 0x7E) return; - LCD_SetWindow(x, x + 16 - 1, y, y + 32 - 1); // 16x32 ĻͼӴ + LCD_SetWindow(x, x + 16 - 1, y, y + 32 - 1); // 锁定 16x32 的绘图视窗 WriteComm(0x2C); - // ͵ 16 data + // 遍历字型的 16 个点阵行 data for(r = 0; r < 16; r++) { u8 line = FONT_8x16[char_idx][r]; - // 㷭ǰظ (i = 0 i = 1) + // 纵向插点翻倍:将当前行重复绘制两次 (i = 0 和 i = 1) for(i = 0; i < 2; i++) { - // ڵ 8 bit ص + // 遍历单行内的 8 个 bit 像素点 for(c = 0; c < 8; c++) { val_bit = (line & (0x80 >> c)); - // 㷭صظд 2 (j = 0 j = 1) + // 横向插点翻倍:单个像素点重复发送写入 2 次 (j = 0 和 j = 1) for(j = 0; j < 2; j++) { if(val_bit) @@ -418,31 +418,31 @@ void LCD_ShowChar16x32(u16 x, u16 y, char ch, u16 color, u16 bg_color) } /** - * @brief ָش 16x32 /Ӣִ + * @brief 在指定坐标绘制特大 16x32 规格的数字/英文字串 */ void LCD_ShowString16x32(u16 x, u16 y, char *str, u16 color, u16 bg_color) { while(*str) { LCD_ShowChar16x32(x, y, *str, color, bg_color); - x += 16; // ÿʼƫ 16 + x += 16; // 每次起始坐标偏移 16 像素 str++; } } /** - * @brief ָ Y ߶ϣˮƽȾʾش 16x32 ַ + * @brief 在指定 Y 轴高度上,水平居中渲染显示特大 16x32 规格字符串 */ void LCD_ShowString16x32Centered(u16 y, char *str, u16 color, u16 bg_color) { u16 len = 0; char *p = str; - while(*p++) len++; // + while(*p++) len++; // 计算字数 if(len * 16 >= LCD_WIDTH) - // Ļȣ + // 超出屏幕宽度,左对齐 LCD_ShowString16x32(0, y, str, color, bg_color); else - // ˮƽм + // 水平居中计算 LCD_ShowString16x32((LCD_WIDTH - len * 16) / 2, y, str, color, bg_color); }