From 47bcd252b29e5d630e028e586a81aeac8d34e1a2 Mon Sep 17 00:00:00 2001 From: Antigravity IDE Date: Fri, 17 Jul 2026 19:43:02 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A31.14=E5=AF=B8=E5=B1=8F?= =?UTF-8?q?=E5=B9=95=E5=9E=82=E7=9B=B4=E5=81=8F=E7=BD=AE=E5=B9=B6=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=87=AA=E5=8A=A8=E6=B8=85=E5=B1=8F=E4=BB=A5=E6=B8=85?= =?UTF-8?q?=E9=99=A4=E5=BC=80=E6=9C=BA=E5=99=AA=E5=A3=B0=EF=BC=9B=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E6=96=B0=E7=A1=AC=E4=BB=B6=E5=B0=84=E9=A2=91=E6=94=B6?= =?UTF-8?q?=E5=8F=91=E5=BC=95=E8=84=9A=E5=AF=B9=E8=B0=83=EF=BC=8C=E4=BD=8E?= =?UTF-8?q?=E5=8A=9F=E8=80=97=E5=94=A4=E9=86=92=E4=B8=AD=E6=96=AD=E9=87=8D?= =?UTF-8?q?=E5=AE=9A=E5=90=91=E8=87=B3P3.6=E4=B8=AD=E6=96=AD16=EF=BC=9B?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=80=E9=94=AE=E4=B8=B2=E5=8F=A3=E6=94=B6?= =?UTF-8?q?=E5=8F=91=E8=AF=8A=E6=96=AD=E6=B5=8B=E8=AF=95=E6=8C=87=E4=BB=A4?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E4=B8=B4=E6=97=B6=E5=B1=8F=E8=94=BDSOS?= =?UTF-8?q?=E7=89=A9=E7=90=86=E6=8C=89=E9=94=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/app_clock.c | 4 - App/app_pair.c | 2 +- App/config.h | 27 +- App/event.c | 4 +- App/main.c | 115 +++++--- App/system.c | Bin 15746 -> 9771 bytes App/ui.c | 139 +++++----- Docs/30_architecture/main.md | 34 ++- Drivers/lcd.c | 508 +++++++++++++---------------------- Drivers/lcd.h | 133 ++++----- Drivers/rf.c | 454 +++++++++++++------------------ Drivers/rf.h | 11 +- 12 files changed, 609 insertions(+), 822 deletions(-) diff --git a/App/app_clock.c b/App/app_clock.c index 6d86eb8..1c0ab41 100644 --- a/App/app_clock.c +++ b/App/app_clock.c @@ -10,10 +10,6 @@ // 外部引用的全局系统状态变量 extern SystemState current_state; -extern u8 current_hour; -extern u8 current_min; -extern u8 current_sec; -extern volatile u8 clock_updated; extern volatile u16 ms_tick; // 待机时钟应用内特有的编辑时间私有变量 diff --git a/App/app_pair.c b/App/app_pair.c index 764219c..dfba2ea 100644 --- a/App/app_pair.c +++ b/App/app_pair.c @@ -5,11 +5,11 @@ #include "event.h" #include "system.h" #include "database.h" +#include "clock.h" #include "../Drivers/rf.h" // 外部全局变量引用 extern SystemState current_state; -extern u8 current_sec; extern u8 captured_type; extern u32 captured_addr; extern volatile u16 ms_tick; diff --git a/App/config.h b/App/config.h index 6cd62cb..1bfd83c 100644 --- a/App/config.h +++ b/App/config.h @@ -22,19 +22,19 @@ sbit KEY_DOWN = P0^3; sbit KEY_SOS = P2^6; // FCOB 幻彩灯条 DIN 数据控制线 sbit RGB_DIN = P2^3; -// LR690L 射频芯片休眠/工作控制引脚 (低电平使能开启工作) -sbit SHUT = P2^2; -// SGM3833 PMIC 屏幕负压供电使能脚 (1使能,0关闭) -sbit SGM_CTRL = P3^4; -// Truly AMOLED 屏幕复位脚 (RESET),低电平复位 +// LR690L 射频芯片休眠/工作控制引脚 (0:开启工作, 1:关闭休眠) +sbit SHUT = P3^5; +// LHS114TC-IF03 屏幕 LEDA 白色背光开启引脚 (1点亮,0熄灭) +sbit SGM_CTRL = P1^7; +// LHS114TC-IF03 屏幕复位脚 (RESET),低电平复位 sbit LCD_RST = P1^0; -// Truly AMOLED 屏幕数据/指令选择脚 (D/C,1:数据, 0:命令) +// LHS114TC-IF03 屏幕数据/指令选择脚 (D/C,1:数据, 0:命令) sbit LCD_DCX = P1^1; -// Truly AMOLED 屏幕 SPI 数据脚 (MOSI/SDA) +// LHS114TC-IF03 屏幕 SPI 数据脚 (MOSI/SDA) sbit LCD_SDI = P1^4; -// Truly AMOLED 屏幕 SPI 时钟脚 (SCLK) +// LHS114TC-IF03 屏幕 SPI 时钟脚 (SCLK) sbit LCD_SCL = P1^5; -// Truly AMOLED 屏幕 SPI 片选脚 (CS),低电平有效 +// LHS114TC-IF03 屏幕 SPI 片选脚 (CS),低电平有效 sbit LCD_CS = P1^6; /* ====== 系统运行状态枚举 ====== */ @@ -68,9 +68,18 @@ typedef enum { KEY_EVENT_CONFIRM_LONG // ■ 确认键长按事件 (打开主菜单) } KeyEvent; +/* ====== 事件优先级枚举 ====== */ +typedef enum { + EVENT_PRIORITY_LOW, // 0: 低优先级:系统内部事件,如定时更新 + EVENT_PRIORITY_NORMAL, // 1: 正常优先级:大部分用户交互事件 + EVENT_PRIORITY_HIGH, // 2: 高优先级:报警相关事件,插队到队首 + EVENT_PRIORITY_URGENT // 3: 紧急优先级:SOS等,插队到队首 +} EventPriority; + /* ====== 系统事件结构体定义 ====== */ typedef struct { KeyEvent key_event; // 关联的按键事件类型 + EventPriority priority; // 事件优先级 u32 extra_data; // 随事件携带的额外数据 (如 24 位射频源地址等) u8 extra_size; // 额外数据的有效字节大小 (0~4) } SystemEvent; diff --git a/App/event.c b/App/event.c index c70cca8..95727e6 100644 --- a/App/event.c +++ b/App/event.c @@ -173,7 +173,7 @@ void Timer1_Isr(void) interrupt 3 u8 raw_up = !KEY_UP; u8 raw_down = !KEY_DOWN; u8 raw_confirm = !KEY_CONFIRM; - u8 raw_sos = !KEY_SOS; + u8 raw_sos = 0; // 临时注释关闭 SOS 物理按键电平检测,防浮空干扰 key_scan_timer = 0; // 重置扫描分频计数器 // 检测到有任意按键处于下压状态,立即将无操作闲置休眠计时器归零 @@ -249,6 +249,7 @@ void Timer1_Isr(void) interrupt 3 key_confirm_hold = 0; // 释放归零 } + /* // SOS 物理求救按键状态机 if (raw_sos) { key_sos_hold++; @@ -263,6 +264,7 @@ void Timer1_Isr(void) interrupt 3 } key_sos_hold = 0; // 释放归零 } + */ } } } diff --git a/App/main.c b/App/main.c index 0f5dcb6..73b7b79 100644 --- a/App/main.c +++ b/App/main.c @@ -5,21 +5,15 @@ #include "database.h" #include "ui.h" #include "system.h" -#include "clock.h" -#include "xtell.h" #include "../Drivers/rf.h" #include "../Drivers/lcd.h" -// 声明外部射频模拟变量 -extern volatile u32 mock_rx_addr; -extern volatile u8 mock_rx_data; -extern volatile bit mock_rf_ready; - /* ========================================================================= * 系统全局变量定义 * ========================================================================= */ SystemState current_state = STATE_NORMAL; // 系统整体的当前运行状态,默认为正常时钟待机页 + u8 menu_select = 0; // 主菜单当前选中的行高亮索引 (0~2) u32 captured_addr = 0; // 配对或报警时,捕获到的传感器 24 位 EV1527 射频物理地址 u8 captured_type = 0; // 正在对码或触发警报的传感器设备类型 (0:门磁, 1:PIR...) @@ -49,6 +43,17 @@ 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': 触发屏幕与无线控制引脚测试。 */ void Debug_ProcessCommand(char cmd) { @@ -59,32 +64,39 @@ void Debug_ProcessCommand(char cmd) if (cmd == 'u') { debug_evt.key_event = KEY_EVENT_UP_CLICK; + debug_evt.priority = EVENT_PRIORITY_NORMAL; EventQueue_Push(debug_evt); // 压入队尾 - XTELL_LOG("[UART] KEY_UP Clicked\r\n"); + Uart_SendString("[UART] KEY_UP Clicked\r\n"); } else if (cmd == 'U') { debug_evt.key_event = KEY_EVENT_UP_LONG; + debug_evt.priority = EVENT_PRIORITY_NORMAL; EventQueue_Push(debug_evt); - XTELL_LOG("[UART] KEY_UP Long Pressed\r\n"); + Uart_SendString("[UART] KEY_UP Long Pressed\r\n"); } else if (cmd == 'd') { debug_evt.key_event = KEY_EVENT_DOWN_CLICK; + debug_evt.priority = EVENT_PRIORITY_NORMAL; EventQueue_Push(debug_evt); - XTELL_LOG("[UART] KEY_DOWN Clicked\r\n"); + Uart_SendString("[UART] KEY_DOWN Clicked\r\n"); } else if (cmd == 'D') { debug_evt.key_event = KEY_EVENT_DOWN_LONG; + debug_evt.priority = EVENT_PRIORITY_NORMAL; EventQueue_Push(debug_evt); - XTELL_LOG("[UART] KEY_DOWN Long Pressed\r\n"); + Uart_SendString("[UART] KEY_DOWN Long Pressed\r\n"); } else if (cmd == 's') { debug_evt.key_event = KEY_EVENT_SOS_CLICK; + debug_evt.priority = EVENT_PRIORITY_URGENT; EventQueue_InsertFront(debug_evt); // 紧急事件插入队首 - XTELL_LOG("[UART] KEY_SOS Clicked\r\n"); + Uart_SendString("[UART] KEY_SOS Clicked\r\n"); } else if (cmd == 'S') { debug_evt.key_event = KEY_EVENT_SOS_LONG; + debug_evt.priority = EVENT_PRIORITY_URGENT; EventQueue_InsertFront(debug_evt); - XTELL_LOG("[UART] KEY_SOS Long Pressed\r\n"); + Uart_SendString("[UART] KEY_SOS Long Pressed\r\n"); } else if (cmd == 'c' || cmd == 'C') { debug_evt.key_event = KEY_EVENT_UP_DOWN_COMB; + debug_evt.priority = EVENT_PRIORITY_NORMAL; EventQueue_Push(debug_evt); - XTELL_LOG("[UART] KEY_UP+DOWN Combined\r\n"); + Uart_SendString("[UART] KEY_UP+DOWN Combined\r\n"); } else if (cmd == 'a' || cmd == 'A') { u32 mock_addr = 0x123456; // 设定模拟的报警射频源地址 u8 mock_slot; @@ -92,42 +104,52 @@ void Debug_ProcessCommand(char cmd) if (!Check_Sensor_ID(mock_addr, &mock_slot)) { Add_Sensor_With_Zone(mock_addr, 0, 1); } - mock_rx_addr = mock_addr; - mock_rx_data = 0x01; - mock_rf_ready = 1; // 触发射频模拟 - XTELL_LOG("[UART] Mock RF Alarm Triggered\r\n"); + debug_evt.key_event = KEY_EVENT_NONE; + 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') { - mock_rx_addr = 0x789ABC; - mock_rx_data = 0x01; - mock_rf_ready = 1; // 触发射频模拟 - XTELL_LOG("[UART] Mock RF Pair Triggered\r\n"); + // 只有当手环切入 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_size = 4; + EventQueue_Push(debug_evt); + Uart_SendString("[UART] Mock RF Pair Signal\r\n"); + } else { + Uart_SendString("[UART] Not in PAIR mode\r\n"); + } } else if (cmd == '1') { // 模拟切换至 正常撤防 (NORMAL) 状态并刷新时钟 if (AppManager_GetActiveAppID() == APP_ID_CLOCK) { current_state = STATE_NORMAL; UI_ShowClockPage(current_state, current_hour, current_min); } - XTELL_LOG("[UART] State: NORMAL\r\n"); + Uart_SendString("[UART] State: NORMAL\r\n"); } else if (cmd == '2') { // 模拟切换至 已布防 (ARMED) 状态,普通门磁可触发警报 if (AppManager_GetActiveAppID() == APP_ID_CLOCK) { current_state = STATE_ARMED; UI_ShowClockPage(current_state, current_hour, current_min); } - XTELL_LOG("[UART] State: ARMED\r\n"); + Uart_SendString("[UART] State: ARMED\r\n"); } else if (cmd == '3') { // 模拟切换至 已撤防 (DISARMED) 状态,忽略普通报警 if (AppManager_GetActiveAppID() == APP_ID_CLOCK) { current_state = STATE_DISARMED; UI_ShowClockPage(current_state, current_hour, current_min); } - XTELL_LOG("[UART] State: DISARMED\r\n"); + Uart_SendString("[UART] State: DISARMED\r\n"); } else if (cmd == 'z' || cmd == 'Z') { inactivity_timer = 6000; // 模拟闲置 60 秒超时,强制触发主循环深度休眠 - XTELL_LOG("[UART] Force Entering Sleep mode!\r\n"); + Uart_SendString("[UART] Force Entering Sleep mode!\r\n"); } else if (cmd == 't' || cmd == 'T') { - Loopback_Test(); // 调用回环测试验证屏幕 - RF_Test_Pin(); // 无线射频测试 + RF_DiagnosticMode('t'); // 触发射频连续发射诊断 (绿色爆闪) + } else if (cmd == 'r' || cmd == 'R') { + RF_DiagnosticMode('r'); // 触发 5 秒射频接收监听诊断 } } @@ -148,28 +170,25 @@ void main(void) GPIO_Init(); // 配置各个引脚的推挽输出/准双向模式及内部上拉 RF_Init(); // 初始化射频芯片各引脚控制 Delay_ms(500); // 软启动延时,等待供电电容稳定 - SGM_SendPulse(27); // 通过脉冲给 SGM3833 供电芯片发指令,稳压开启 LCD 正负电压轨 - Delay_ms(50); // 等待电压充盈稳定,避开浪涌 - LCD_Init(); // 初始化 Truly AMOLED 屏幕控制寄存器序列 + LCD_Init(); // 初始化 LHS114TC-IF03 (ST7789V) 屏幕控制寄存器序列并开启背光 Uart1_Init(); // 初始化串口 1 波特率 115200 供调试日志 - XTELL_LOG("Wristband System Initialized!\r\n"); + Uart_SendString("Wristband System Initialized!\r\n"); Load_Database(); // 从 IAP Flash 第 254 扇区读取所有已绑定的传感器数据至 RAM /* 步骤 1: 初始化事件环形缓冲区队列 */ EventQueue_Init(); - /* 步骤 2: 初始化应用管理器 */ + /* 步骤 2+5: 初始化应用管理器并登记所有前台应用,默认加载 ClockApp */ AppManager_Init(); - /* 步骤 3: 动态挂载待机时钟应用到前台,并挂载后台射频监控应用 */ - AppManager_SwitchToForeground(&clock_app); - AppManager_AttachToBackground(&rf_monitor_app); - /* 启动 1ms 系统基准定时器 Timer1 */ Timer1_Init(); - XTELL_LOG("[SYS] Event-driven framework ready\r\n"); + /* 开机自动加载并运行待机时钟前台活跃应用 */ + AppManager_StartApp(APP_ID_CLOCK); + + Uart_SendString("[SYS] Event-driven framework ready\r\n"); // ====== 主循环 ====== while (1) { @@ -184,23 +203,23 @@ void main(void) Event_Dispatcher_Loop(); } - /* 3. 应用调度中心:运行前后台所有已挂载激活的应用 */ + /* 3. 应用调度中心:运行当前处于前台活跃状态的应用(集成了 CPU 运行时间超时挂起保护) */ AppManager_RunActiveApp(); /* 4. 自动休眠判定:常态无操作闲置达到 60 秒 (6000 * 10ms = 60s) 时,切入低功耗停机休眠 */ if (inactivity_timer >= 6000) { inactivity_timer = 0; current_state = STATE_SLEEP; // 系统置为休眠状态 - Enter_Low_Power_Sleep(); // 挂起 MCU + Enter_Low_Power_Sleep(); // 该函数是阻塞的,MCU 将在此挂起。唤醒后将自动从其后继续执行 } } } - // 唤醒源事件标志,由对应的外部中断 ISR 写入 volatile u8 p0_wakeup_flag = 0; // P0 端口按键唤醒标志 volatile u8 p2_wakeup_flag = 0; // P2 端口按键唤醒标志 -volatile u8 rf_wakeup_flag = 0; // P2.1 射频中断唤醒标志 +volatile u8 p3_wakeup_flag = 0; // P3 端口射频中断唤醒标志 +volatile u8 rf_wakeup_flag = 0; // 射频中断唤醒标志 /* ========================================================================= * STC32G 端口掉电外部中断唤醒中断服务程序 (ISR) @@ -229,3 +248,15 @@ void Port2_Isr(void) interrupt 15 P2INTF = 0x00; // 清除 P2 端口中断悬挂标志位 P2INTE = 0x00; // 立即关闭 P2 中断通道,防止低电平期间反复重入 } + +/** + * @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 中断通道,防止反复重入 +} diff --git a/App/system.c b/App/system.c index 6a5311c0deaa29509d22421e13b99ac4afaf354f..507c125c8da7ad254105f2edfedc211598c23106 100644 GIT binary patch literal 9771 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 diff --git a/App/ui.c b/App/ui.c index daffe12..53d62d9 100644 --- a/App/ui.c +++ b/App/ui.c @@ -38,8 +38,8 @@ void UI_ShowClockPage(SystemState state, u8 hour, u8 min) } // ====== 绘制右上角电池电量 ====== - LCD_ShowString(62, 24, "85%", COLOR_GRAY, COLOR_BLACK); // 静态测试显示 85% 字符 - LCD_DrawMonoBitmap(92, 27, 20, 10, bmp_battery_20x10, COLOR_GRAY, COLOR_BLACK); // 渲染 20x10 电池外廓和格数 + LCD_ShowString(77, 24, "85%", COLOR_GRAY, COLOR_BLACK); // 静态测试显示 85% 字符并移动至右上角 + LCD_DrawMonoBitmap(107, 27, 20, 10, bmp_battery_20x10, COLOR_GRAY, COLOR_BLACK); // 渲染 20x10 电池并移动至右上角 // ====== 绘制中部时间 "HH:MM" ====== time_str[0] = '0' + (hour / 10); // 小时十位 @@ -77,7 +77,7 @@ void UI_ShowPairMenuPage(u8 selected_index) } // 渲染当前选中的高亮中间项 - LCD_DrawRectBorder(8, 110, 104, 28, COLOR_CYAN); // 青色对齐外框 + LCD_DrawRectBorder(15, 110, 104, 28, COLOR_CYAN); // 青色对齐外框 (在 135px 宽度下居中) LCD_ShowStringCentered(118, menu_items[selected_index], COLOR_CYAN, COLOR_BLACK); // 渲染下方相邻项 (如果当前选择的不是最后一行) @@ -98,13 +98,13 @@ void UI_ShowPairWaitPage(u8 frame_index) // 绘制头部 "APPAIRAGE" (对码中) LCD_ShowStringCentered(35, "APPAIRAGE", COLOR_GRAY, COLOR_BLACK); - // 根据 Timer1 节拍算得的帧号,切换渲染雷达的 3 种点阵图形,形成扩散动态视觉 + // 根据 Timer1 节拍算得的帧号,切换渲染雷达的 3 种点阵图形,形成扩散动态视觉 (在 135px 下居中 X = 51) if (frame_index == 0) - LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_radar_32x32_f1, COLOR_CYAN, COLOR_BLACK); + LCD_DrawMonoBitmap(51, 75, 32, 32, bmp_radar_32x32_f1, COLOR_CYAN, COLOR_BLACK); else if (frame_index == 1) - LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_radar_32x32_f2, COLOR_CYAN, COLOR_BLACK); + LCD_DrawMonoBitmap(51, 75, 32, 32, bmp_radar_32x32_f2, COLOR_CYAN, COLOR_BLACK); else - LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_radar_32x32_f3, COLOR_CYAN, COLOR_BLACK); + LCD_DrawMonoBitmap(51, 75, 32, 32, bmp_radar_32x32_f3, COLOR_CYAN, COLOR_BLACK); // 绘制状态描述 LCD_ShowStringCentered(165, "RECHERCHE", COLOR_WHITE, COLOR_BLACK); // 法语: 搜寻中 @@ -124,24 +124,24 @@ void UI_ShowPairConfirmPage(u8 type, u8 zone_index) // 顶部显示捕获回馈标志 "v RECU" (法语: 信号已接收) LCD_ShowStringCentered(32, "v RECU", COLOR_GREEN, COLOR_BLACK); - // 在上部居中绘制设备类别所对应的图形 + // 在上部居中绘制设备类别所对应的图形 (135px 下居中 X = 51) if (type == 0) { - LCD_DrawMonoBitmap(44, 55, 32, 32, bmp_door_32x32, COLOR_WHITE, COLOR_BLACK); // 门磁图标 + LCD_DrawMonoBitmap(51, 55, 32, 32, bmp_door_32x32, COLOR_WHITE, COLOR_BLACK); // 门磁图标 } else if (type == 2 || type == 3) { - LCD_DrawMonoBitmap(44, 55, 32, 32, bmp_siren_32x32, COLOR_WHITE, COLOR_BLACK); // 警号喇叭图标 + LCD_DrawMonoBitmap(51, 55, 32, 32, bmp_siren_32x32, COLOR_WHITE, COLOR_BLACK); // 警号喇叭图标 } else { - LCD_DrawMonoBitmap(44, 55, 32, 32, bmp_confirm_lock_32x32, COLOR_WHITE, COLOR_BLACK); // 锁型常规图标 + LCD_DrawMonoBitmap(51, 55, 32, 32, bmp_confirm_lock_32x32, COLOR_WHITE, COLOR_BLACK); // 锁型常规图标 } // 渲染传感器类型的文字 LCD_ShowStringCentered(105, sensor_names_fr[type], COLOR_WHITE, COLOR_BLACK); - // 绘制一个青色边框,在中间以 16x32 大号字体展示当前防区号 (如 "01", "12") - LCD_DrawRectBorder(15, 140, 90, 44, COLOR_CYAN); + // 绘制一个青色边框,在中间以 16x32 大号字体展示当前防区号 (在 135px 下居中 X=22, 宽度 90) + LCD_DrawRectBorder(22, 140, 90, 44, COLOR_CYAN); zone_str[0] = '0' + (zone_index / 10); zone_str[1] = '0' + (zone_index % 10); zone_str[2] = '\0'; - LCD_ShowString16x32(44, 146, zone_str, COLOR_CYAN, COLOR_BLACK); // 渲染两位防区编号 + LCD_ShowString16x32(51, 146, zone_str, COLOR_CYAN, COLOR_BLACK); // 渲染两位防区编号 (135px 下居中 X = 51) // 底部提示按键动作 LCD_ShowStringCentered(215, "CONFIRMER", COLOR_GRAY, COLOR_BLACK); // 法语: 短按以确认保存 @@ -153,8 +153,8 @@ void UI_ShowPairConfirmPage(u8 type, u8 zone_index) void UI_ShowPairSuccessPage(void) { LCD_Clear(COLOR_BLACK); - // 渲染 32x32 对勾图标 - LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_checkmark_32x32, COLOR_GREEN, COLOR_BLACK); + // 渲染 32x32 对勾图标 (135px 下居中 X = 51) + LCD_DrawMonoBitmap(51, 75, 32, 32, bmp_checkmark_32x32, COLOR_GREEN, COLOR_BLACK); LCD_ShowStringCentered(145, "SUCCES", COLOR_GREEN, COLOR_BLACK); // 法语: 成功 LCD_ShowStringCentered(180, "ENREGISTRE", COLOR_GRAY, COLOR_BLACK); // 法语: 配置已保存 } @@ -166,8 +166,8 @@ void UI_ShowPairSuccessPage(void) void UI_ShowPairFailPage(u8 is_timeout) { LCD_Clear(COLOR_BLACK); - // 渲染 32x32 红色叉号图标 - LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_cross_32x32, COLOR_RED, COLOR_BLACK); + // 渲染 32x32 红色叉号图标 (135px 下居中 X = 51) + LCD_DrawMonoBitmap(51, 75, 32, 32, bmp_cross_32x32, COLOR_RED, COLOR_BLACK); LCD_ShowStringCentered(145, "ECHEC", COLOR_RED, COLOR_BLACK); // 法语: 失败 if (is_timeout) { LCD_ShowStringCentered(180, "DELAI DEPASSE", COLOR_GRAY, COLOR_BLACK); // 法语: 对码超时 @@ -189,17 +189,17 @@ void UI_ShowAlarmPage(u8 type, u8 zone_index) char zone_info[20]; LCD_Clear(COLOR_BLACK); - // 绘制双重红色爆闪粗边界框 (配合 ms_tick 在 AlarmApp 中亮灭闪烁) - LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED); - LCD_DrawRectBorder(6, 6, 108, 228, COLOR_RED); + // 绘制双重红色爆闪粗边界框 (在新宽 135px 下对称扩展) + LCD_DrawRectBorder(4, 4, 127, 232, COLOR_RED); + LCD_DrawRectBorder(6, 6, 123, 228, COLOR_RED); - // 绘制报警设备的图形 + // 绘制报警设备的图形 (135px 下居中 X = 51) if (type == 0) { - LCD_DrawMonoBitmap(44, 50, 32, 32, bmp_door_32x32, COLOR_RED, COLOR_BLACK); // 门磁报警 + LCD_DrawMonoBitmap(51, 50, 32, 32, bmp_door_32x32, COLOR_RED, COLOR_BLACK); // 门磁报警 } else if (type == 2 || type == 3) { - LCD_DrawMonoBitmap(44, 50, 32, 32, bmp_siren_32x32, COLOR_RED, COLOR_BLACK); // 烟感/燃气报警 + LCD_DrawMonoBitmap(51, 50, 32, 32, bmp_siren_32x32, COLOR_RED, COLOR_BLACK); // 烟感/燃气报警 } else { - LCD_DrawMonoBitmap(44, 50, 32, 32, bmp_confirm_lock_32x32, COLOR_RED, COLOR_BLACK); // 其他防区报警 + LCD_DrawMonoBitmap(51, 50, 32, 32, bmp_confirm_lock_32x32, COLOR_RED, COLOR_BLACK); // 其他防区报警 } // 拼接字符串: "设备类型名 两位防区号" @@ -227,19 +227,19 @@ void UI_ShowSosPage(void) { LCD_Clear(COLOR_BLACK); - // 绘制红白双层紧急警示框 - LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED); - LCD_DrawRectBorder(5, 5, 110, 230, COLOR_RED); - LCD_DrawRectBorder(8, 8, 104, 224, COLOR_WHITE); + // 绘制红白双层紧急警示框 (在新宽 135px 下对称扩展) + LCD_DrawRectBorder(4, 4, 127, 232, COLOR_RED); + LCD_DrawRectBorder(5, 5, 125, 230, COLOR_RED); + LCD_DrawRectBorder(8, 8, 119, 224, COLOR_WHITE); - // ====== 用画点和填充矩形函数像素级绘制精细的警徽灯架 ====== - LCD_FillRect(44, 45, 32, 2, COLOR_RED); // 警灯顶部 - LCD_FillRect(48, 47, 24, 12, COLOR_RED); // 警灯红色灯罩 - LCD_FillRect(40, 59, 40, 4, COLOR_GRAY); // 警灯灰色底座 - LCD_FillRect(57, 51, 6, 6, COLOR_WHITE); // 中间高亮光影 - LCD_FillRect(58, 36, 4, 6, COLOR_RED); // 发射的垂直红色闪光 - LCD_FillRect(46, 38, 4, 4, COLOR_RED); // 左侧散射闪光 - LCD_FillRect(70, 38, 4, 4, COLOR_RED); // 右侧散射闪光 + // ====== 用画点和填充矩形函数像素级绘制精细的警徽灯架 (在新宽 135px 下整体右移 7 像素) ====== + LCD_FillRect(51, 45, 32, 2, COLOR_RED); // 警灯顶部 + LCD_FillRect(55, 47, 24, 12, COLOR_RED); // 警灯红色灯罩 + LCD_FillRect(47, 59, 40, 4, COLOR_GRAY); // 警灯灰色底座 + LCD_FillRect(64, 51, 6, 6, COLOR_WHITE); // 中间高亮光影 + LCD_FillRect(65, 36, 4, 6, COLOR_RED); // 发射的垂直红色闪光 + LCD_FillRect(53, 38, 4, 4, COLOR_RED); // 左侧散射闪光 + LCD_FillRect(77, 38, 4, 4, COLOR_RED); // 右侧散射闪光 // 绘制求救中法文字样 LCD_ShowStringCentered(120, "SOS", COLOR_RED, COLOR_BLACK); @@ -257,11 +257,11 @@ void UI_ShowBindingPage(u32 addr) LCD_Clear(COLOR_BLACK); // ====== 像素级拼装绘制一个青色网络摄像头简图 ====== - LCD_DrawRectBorder(36, 46, 32, 26, COLOR_CYAN); // 相机主体框 - LCD_FillRect(44, 56, 6, 6, COLOR_CYAN); // 镜头光圈点 - LCD_FillRect(68, 48, 2, 22, COLOR_CYAN); // 右侧红外指示圈 - LCD_FillRect(70, 50, 2, 18, COLOR_CYAN); // 镜头固定翼 1 - LCD_FillRect(72, 52, 2, 14, COLOR_CYAN); // 镜头固定翼 2 + LCD_DrawRectBorder(43, 46, 32, 26, COLOR_CYAN); // 相机主体框 (135px 下居中等比右移 X = 43) + LCD_FillRect(51, 56, 6, 6, COLOR_CYAN); // 镜头光圈点 (135px 下居中等比右移 X = 51) + LCD_FillRect(75, 48, 2, 22, COLOR_CYAN); // 右侧红外指示圈 (135px 下居中等比右移 X = 75) + LCD_FillRect(77, 50, 2, 18, COLOR_CYAN); // 镜头固定翼 1 (135px 下居中等比右移 X = 77) + LCD_FillRect(79, 52, 2, 14, COLOR_CYAN); // 镜头固定翼 2 (135px 下居中等比右移 X = 79) LCD_ShowStringCentered(120, "LIAISON", COLOR_WHITE, COLOR_BLACK); // 法语: 连接/绑定 LCD_ShowStringCentered(155, "ENVOI EN COURS", COLOR_CYAN, COLOR_BLACK); // 法语: 发送对码帧中 @@ -294,46 +294,46 @@ void UI_ShowBindingPage(u32 addr) * @brief 渲染显示设置主菜单界面 * @param selected_index 当前菜单高亮行索引 (0:时钟修改, 1:对码匹配, 2:摄像机绑定) * @details 在顶部和底部绘制向上/向下箭头提示滑轮,中部绘制选中项的大几何图形, - * 并在下方打印对应的法文字符串描述。 + * 并在下方打印对应的法文字串描述。 */ void UI_ShowMainMenu(u8 selected_index) { LCD_Clear(COLOR_BLACK); - // ====== 绘制顶部的向上箭头三角标记 ====== - LCD_FillRect(58, 20, 4, 2, COLOR_GRAY); - LCD_FillRect(56, 22, 8, 2, COLOR_GRAY); - LCD_FillRect(54, 24, 12, 2, COLOR_GRAY); + // ====== 绘制顶部的向上箭头三角标记 (在 135px 宽度下居中) ====== + LCD_FillRect(65, 20, 4, 2, COLOR_GRAY); + LCD_FillRect(63, 22, 8, 2, COLOR_GRAY); + LCD_FillRect(61, 24, 12, 2, COLOR_GRAY); if (selected_index == 0) { - // 菜单 0:时间修改设置。中部绘制一个青色闹钟样式 - LCD_DrawRectBorder(44, 70, 32, 32, COLOR_CYAN); // 闹钟外圈 - LCD_FillRect(59, 76, 2, 10, COLOR_CYAN); // 闹钟时针 - LCD_FillRect(59, 85, 8, 2, COLOR_CYAN); // 闹钟分针 + // 菜单 0:时间修改设置。中部绘制一个青色闹钟样式 (135px 下居中 X = 51) + LCD_DrawRectBorder(51, 70, 32, 32, COLOR_CYAN); // 闹钟外圈 + LCD_FillRect(66, 76, 2, 10, COLOR_CYAN); // 闹钟时针 + LCD_FillRect(66, 85, 8, 2, COLOR_CYAN); // 闹钟分针 LCD_ShowStringCentered(150, "1. HORLOGE", COLOR_WHITE, COLOR_BLACK); // 法语: 时钟/时间修改 } else if (selected_index == 1) { - // 菜单 1:无线对码匹配。中部绘制一个青色同心圆雷达波纹图形 - LCD_DrawMonoBitmap(44, 70, 32, 32, bmp_radar_32x32_f3, COLOR_CYAN, COLOR_BLACK); + // 菜单 1:无线对码匹配。中部绘制一个青色同心圆雷达波纹图形 (135px 下居中 X = 51) + LCD_DrawMonoBitmap(51, 70, 32, 32, bmp_radar_32x32_f3, COLOR_CYAN, COLOR_BLACK); LCD_ShowStringCentered(150, "2. APPAIRAGE", COLOR_WHITE, COLOR_BLACK); // 法语: 配对 } else if (selected_index == 2) { - // 菜单 2:网络摄像机绑定。中部绘制一个相机/联通关系图 - LCD_DrawRectBorder(44, 74, 20, 16, COLOR_CYAN); - LCD_FillRect(52, 80, 4, 4, COLOR_CYAN); - LCD_FillRect(64, 76, 2, 12, COLOR_CYAN); - LCD_FillRect(66, 78, 2, 8, COLOR_CYAN); - LCD_FillRect(68, 80, 2, 4, COLOR_CYAN); + // 菜单 2:网络摄像机绑定。中部绘制一个相机/联通关系图 (135px 下居中等比右移 X = 57) + LCD_DrawRectBorder(57, 74, 20, 16, COLOR_CYAN); + LCD_FillRect(65, 80, 4, 4, COLOR_CYAN); + LCD_FillRect(77, 76, 2, 12, COLOR_CYAN); + LCD_FillRect(79, 78, 2, 8, COLOR_CYAN); + LCD_FillRect(81, 80, 2, 4, COLOR_CYAN); LCD_ShowStringCentered(150, "3. LIAISON", COLOR_WHITE, COLOR_BLACK); // 法语: 摄像机绑定 } - // ====== 绘制底部的向下箭头三角标记 ====== - LCD_FillRect(54, 210, 12, 2, COLOR_GRAY); - LCD_FillRect(56, 212, 8, 2, COLOR_GRAY); - LCD_FillRect(58, 214, 4, 2, COLOR_GRAY); + // ====== 绘制底部的向下箭头三角标记 (在 135px 宽度下居中) ====== + LCD_FillRect(61, 210, 12, 2, COLOR_GRAY); + LCD_FillRect(63, 212, 8, 2, COLOR_GRAY); + LCD_FillRect(65, 214, 4, 2, COLOR_GRAY); } /** @@ -354,8 +354,9 @@ void UI_ShowSetTimePage(u8 hour, u8 min, u8 selection, u8 blink_on) LCD_FillRect(17, 15, 8, 2, COLOR_GREEN); LCD_FillRect(25, 17, 2, 6, COLOR_GREEN); - LCD_ShowString(62, 24, "85%", COLOR_GRAY, COLOR_BLACK); - LCD_DrawMonoBitmap(92, 27, 20, 10, bmp_battery_20x10, COLOR_GRAY, COLOR_BLACK); + // 移动电池百分比和图标到 135px 右上角 + LCD_ShowString(77, 24, "85%", COLOR_GRAY, COLOR_BLACK); + LCD_DrawMonoBitmap(107, 27, 20, 10, bmp_battery_20x10, COLOR_GRAY, COLOR_BLACK); // ====== 处理调节项一:小时的消隐/显示逻辑 ====== if (selection == 1 && !blink_on) @@ -370,10 +371,10 @@ void UI_ShowSetTimePage(u8 hour, u8 min, u8 selection, u8 blink_on) time_str[1] = '0' + (hour % 10); } time_str[2] = '\0'; - LCD_ShowString16x32(12, 100, time_str, COLOR_CYAN, COLOR_BLACK); // 采用青色特大字描绘小时 + LCD_ShowString16x32(23, 100, time_str, COLOR_CYAN, COLOR_BLACK); // 采用青色特大字描绘小时 (在 135px 下整体居中 X = 23) - // 绘制中间冒号 - LCD_ShowString16x32(48, 98, ":", COLOR_WHITE, COLOR_BLACK); + // 绘制中间冒号 (在 135px 下整体居中 X = 59) + LCD_ShowString16x32(59, 98, ":", COLOR_WHITE, COLOR_BLACK); // ====== 处理调节项二:分钟的消隐/显示逻辑 ====== if (selection == 2 && !blink_on) @@ -388,7 +389,7 @@ void UI_ShowSetTimePage(u8 hour, u8 min, u8 selection, u8 blink_on) time_str[1] = '0' + (min % 10); } time_str[2] = '\0'; - LCD_ShowString16x32(64, 100, time_str, COLOR_CYAN, COLOR_BLACK); // 采用青色特大字描绘分钟 + LCD_ShowString16x32(75, 100, time_str, COLOR_CYAN, COLOR_BLACK); // 采用青色特大字描绘分钟 (在 135px 下整体居中 X = 75) // 补齐底部的固定辅助元素 LCD_ShowStringCentered(150, "AM", COLOR_WHITE, COLOR_BLACK); diff --git a/Docs/30_architecture/main.md b/Docs/30_architecture/main.md index c830b4a..09b4f53 100644 --- a/Docs/30_architecture/main.md +++ b/Docs/30_architecture/main.md @@ -10,26 +10,20 @@ * **硬件原理图**:[text.kicad_sch](file:///c:/workfile/105/stc32g12k128/Hardware/text.kicad_sch) * **PCB 布局图**:[text.kicad_pcb](file:///c:/workfile/105/stc32g12k128/Hardware/text.kicad_pcb) -主控芯片采用 **STC32G12K128**(工作频率 24.0 MHz,单周期 8051 内核,32位乘除法器)。 -显示端采用 **Truly 0.95寸 AMOLED 屏幕**,物理分辨率为 **120x240 像素**,控制芯片为 **RM69310**。 - -### 1.1 GPIO 物理映射表 - -| 芯片引脚号 | 引脚名称 | 逻辑网络标号 | IO 模式 | 连接外设与功能描述 | -| :--- | :--- | :--- | :--- | :--- | -| **Pin 1** | `P1.0` | `DIS_RST` | 推挽输出 | Truly AMOLED 屏幕复位控制脚 (RESET) | -| **Pin 2** | `P1.1` | `SPI_DCX` | 推挽输出 | Truly AMOLED 屏幕 SPI 数据/指令选择脚 (D/C) | -| **Pin 3** | `P1.3` | `SPI_SDI` | 推挽输出 | Truly AMOLED 屏幕 SPI 串行数据输入 (MOSI/SDA) | -| **Pin 4** | `P1.4` | `SPI_CLK` | 推挽输出 | Truly AMOLED 屏幕 SPI 串行时钟信号 (SCL) | -| **Pin 5** | `P1.5` | `SPI_CS` | 推挽输出 | Truly AMOLED 屏幕 SPI 片选信号 (CS) | -| **Pin 6** | `P1.6` | `DET` | 高阻输入 | USB 插入充电检测引脚,高电平代表正在充电 | -| **Pin 7** | `P1.7` | `LEDA` | 推挽输出 | 屏幕负压供电芯片使能控制端 (SGM_CTRL) | +主控芯片采用| **Pin 1** | `P1.0` | `DIS_RST` | 推挽输出 | LHS114TC-IF03 屏幕复位控制脚 (RESET) | +| **Pin 2** | `P1.1` | `SPI_DCX` | 推挽输出 | LHS114TC-IF03 屏幕 SPI 数据/指令选择脚 (D/C) | +| **Pin 7** | `P1.3` | `BAT_ADC` | ADC 输入 | 电池电压采集 (通道 3,配置为高阻输入) | +| **Pin 3** | `P1.4` | `SPI_SDI` | 推挽输出 | LHS114TC-IF03 屏幕 SPI 串行数据输入 (MOSI/SDA) | +| **Pin 4** | `P1.5` | `SPI_CLK` | 推挽输出 | LHS114TC-IF03 屏幕 SPI 串行时钟信号 (SCL) | +| **Pin 5** | `P1.6` | `SPI_CS` | 推挽输出 | LHS114TC-IF03 屏幕 SPI 片选信号 (CS) | +| **Pin 6** | `P1.7` | `LEDA` | 推挽输出 | LHS114TC-IF03 屏幕白色 LED 背光使能控制端 (0点亮,1熄灭 - PMOS控制) | | **Pin 8** | `UCAP` | `UCAP` | 电源 | 外接滤波电容至 GND (LDO 稳定输出端) | | **Pin 10** | `Vcc` | `+3.3V` | 电源 | 屏幕/系统 3.3V 逻辑供电 | | **Pin 11** | `VREF+` | `+3.3V` | 电源 | 屏幕/系统 3.3V 逻辑供电参考基准 | | **Pin 12** | `GND` | `GND` | 电源 | 系统地 (GND) | | **Pin 13** | `P3.0` | `RXD` | 高阻输入 | 串口 1 (UART1) 接收脚 | | **Pin 14** | `P3.1` | `TXD` | 推挽输出 | 串口 1 (UART1) 发送脚 | +| **Pin 15** | `P5.4` | `DET` | 高阻输入 | USB 插入充电检测引脚,高电平代表正在充电 | | **Pin 18** | `P3.5` | `SHUT` | 推挽输出 | LR690L 射频芯片关闭信号 (0:开启工作, 1:关闭休眠) | | **Pin 19** | `P3.6` | `RF_RX_DATA` | 高阻输入 | LR690L 接收解调接收数据输出引脚 (注:代码中被映射为 P2^1) | | **Pin 20** | `P3.7` | `RF_RX` | 推挽输出 | ATR5179 接收使能控制引脚 | @@ -40,6 +34,18 @@ | **Pin 26** | `P2.5` | `MOTOR_PWM` | 推挽输出 | 连接振动马达驱动电路。高电平震动,低电平静止 | | **Pin 27** | `P2.6` | `KEY_SOS` | 高阻输入 + 上拉 | 上部并联 SOS 求救按键,按下为低电平 | | **Pin 28** | `P2.7` | `NC` | - | 悬空 | +| **Pin 29** | `P0.0` | `NC` | - | 悬空 | +| **Pin 30** | `P0.1` | `KEY_UP` | 高阻输入 + 上拉 | 侧边按键 ▲ (上移),按下为低电平 | +| **Pin 31** | `P0.2` | `KEY_CONFIRM` | 高阻输入 + 上拉 | 侧边按键 ■ (确认/菜单),按下为低电平 | +| **Pin 32** | `P0.3` | `KEY_DOWN` | 高阻输入 + 上拉 | 侧边按键 ▼ (下移),按下为低电平 |� P2^1) | +| **Pin 20** | `P3.7` | `RF_RX` | 推挽输出 | ATR5179 接收使能控制引脚 | +| **Pin 21** | `P2.0` | `RF_TX` | 推挽输出 | ATR5179 发射使能控制引脚 | +| **Pin 22** | `P2.1` | `RF_TX_DAT` | 推挽输出 | LR690L 射频芯片发射调制数据输入引脚 (注:代码中被映射为 P3^6) | +| **Pin 23** | `P2.2` | `NC` | - | 悬空 | +| **Pin 24** | `P2.3` | `LED_RGB` | SPI MOSI | WS2812B 级联双幻彩灯珠 DIN 控制信号 | +| **Pin 26** | `P2.5` | `MOTOR_PWM` | 推挽输出 | 连接振动马达驱动电路。高电平震动,低电平静止 | +| **Pin 27** | `P2.6` | `KEY_SOS` | 高阻输入 + 上拉 | 上部并联 SOS 求救按键,按下为低电平 | +| **Pin 28** | `P2.7` | `NC` | - | 悬空 | | **Pin 29** | `P0.0` | `BAT_ADC` | ADC 输入 | 电池电压采集 | | **Pin 30** | `P0.1` | `KEY_UP` | 高阻输入 + 上拉 | 侧边按键 ▲ (上移),按下为低电平 | | **Pin 31** | `P0.2` | `KEY_CONFIRM` | 高阻输入 + 上拉 | 侧边按键 ■ (确认/菜单),按下为低电平 | diff --git a/Drivers/lcd.c b/Drivers/lcd.c index 98353ee..031c699 100644 --- a/Drivers/lcd.c +++ b/Drivers/lcd.c @@ -3,313 +3,201 @@ #include "../App/config.h" #include "intrins.h" -// ====== RM69310 Truly AMOLED 屏幕 SPI 引脚映射 (8051 bit 寻址控制) ====== -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)͵ƽѡͨĻ -// ====== SGM3833 升压/负压三路 OLED 屏电源 PMIC 使能引脚 ====== -sbit SGM_CTRL = P3^4; // 单线脉冲通信控制线 +// ====== 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 通过脉冲序列为 SGM3833 屏幕电源管理芯片写入开启配置 - * @param pulse_cnt 要写入的脉冲个数 (本系统传入 27 次脉冲开启正负双电轨) - * @details SGM3833 采用单线边沿脉冲计数协议进行数字控制。 - * - tOFF 必须大于 5ms 才能完全复位芯片进入指令模式。 - * - 每次电平拉低低电平持续 10us 后拉高,形成一个脉冲计数值。 - * - 发送完毕后需拉高并维持 5ms 以上,以便将计数值存入寄存器,锁定目标电压轨开启。 - */ -void SGM_SendPulse(u8 pulse_cnt) -{ - u8 i; - SGM_CTRL = 0; - Delay_ms(5); // 确保电源芯片进入关断/复位状态 (tOFF) - SGM_CTRL = 1; - Delay_ms(5); // 确保进入初始化状态 (tINIT >= 300us) - for(i = 0; i < pulse_cnt; i++) - { - SGM_CTRL = 0; - Delay10us(); // 保持低电平 10us - SGM_CTRL = 1; - Delay10us(); // 保持高电平 10us - } - SGM_CTRL = 1; - Delay_ms(5); // 确保数据稳定存储锁定 (tSTORE >= 80us) -} - -/** - * @brief 软件模拟 8 位串行 SPI 字节数据发送 - * @param dat 待发送的 1 字节数据 - * @details 采用典型的 bit-banging 模式:时钟 SCL 在发送前置低,将数据 MSB 依次提取后赋值给 - * 数据线 SDI,随后拉高 SCL 完成一个 bit 的沿采样,循环 8 次发送完成。 + * @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 按照 RM69310 物理时序执行屏幕硬件复位 + * @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 才能执行后续的初始化 + LCD_RST = 1; // + Delay_ms(120); // ȴ 120ms ִкijʼ } /** - * @brief 初始化 Truly AMOLED 屏幕控制器 RM69310 - * @details 先配置控制 GPIO 为推挽输出,接着根据 Truly 信利原厂提供的初始化指令序列, - * 完成偏置电压、刷新周期、Gamma 参数、跳帧逻辑及色彩空间模式等一系列初始化工作。 + * @brief ʼ LHS114TC-IF03 (ST7789V) Ļ */ void LCD_Init(void) { - // 1. 配置 P1 端口的 LCD_RST/DCX/SDI/SCL/CS 引脚全部为推挽输出模式 (M1=0, M0=1) - P1M1 &= ~((1<<0) | (1<<1) | (1<<4) | (1<<5) | (1<<6)); - P1M0 |= ((1<<0) | (1<<1) | (1<<4) | (1<<5) | (1<<6)); + // չ SFR Ȩ (EAXFR = 1)ȷԲ P1M1 P1M0 ģʽĴ + P_SW2 |= 0x80; - // 2. 配置 P3.4 (SGM_CTRL) 为推挽输出模式 - P3M1 &= ~(1 << 4); - P3M0 |= (1 << 4); + // 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 = 1; + // øųʼĬϵƽ + SGM_CTRL = 0; // PMOS͵ƽ (LEDA = 0) LCD_CS = 1; LCD_SCL = 1; LCD_RST = 1; LCD_DCX = 1; - LCD_Reset(); // 硬件复位屏幕 + LCD_Reset(); // ӲλĻ - // ====== 写入 RM69310 原厂寄存器初始化指令序列 ====== - WriteComm(0xFE);WriteData(0x01); // 切换至扩展 Page 1 寄存器页 - WriteComm(0x04);WriteData(0xa0); // 开启 SPI 写入显存 RAM 允许 - WriteComm(0x05);WriteData(0x70); // 配置屏幕尺寸规格为 128RGB - WriteComm(0x06);WriteData(0x3C); // 设定垂直有效扫描线 NL = 240 - WriteComm(0x25);WriteData(0x06); // 正常模式伽马设定Gamma1 - WriteComm(0x26);WriteData(0x80); // T1A 时序常数 = 280 - WriteComm(0x27);WriteData(0x12); // 正常模式消隐后肩 VBP = 12 - WriteComm(0x28);WriteData(0x12); // 正常模式消隐前肩 VFP = 8 - WriteComm(0x2A);WriteData(0x06); // 空闲模式伽马设定 Gamma1 - WriteComm(0x2B);WriteData(0x80); // T1B 时序常数 = 280 - WriteComm(0x2D);WriteData(0x12); // 空闲消隐后肩 = 12 - WriteComm(0x2F);WriteData(0x12); // 空闲消隐前肩 = 8 - WriteComm(0x37);WriteData(0x0C); // 预充电控制电压 VGSP - WriteComm(0x6D);WriteData(0x18); // 关闭跳帧 VGMP 和 VGSP 稳压调节 - WriteComm(0x29);WriteData(0x01); // 关闭正常模式的跳帧 - WriteComm(0x30);WriteData(0x43); // 设定空闲模式跳帧使刷新频率降为 30Hz - WriteComm(0x0E);WriteData(0x83); // 正常模式 AVDD 电压 = 5.6V - WriteComm(0x0F);WriteData(0x83); // 空闲模式 AVDD = 5.6V - WriteComm(0x10);WriteData(0x71); // AVDD 三倍 VCI 升压控制 - WriteComm(0x11);WriteData(0xb3); // VCL = -2倍 VCI 产生负压 - WriteComm(0x12);WriteData(0xb3); - WriteComm(0x13);WriteData(0x80); // 正常模式下 VGH 等于 AVDD - WriteComm(0x14);WriteData(0x80); // 空闲模式下 VGH 等于 AVDD - WriteComm(0x15);WriteData(0x81); // 正常模式下低侧门电压 VGL = VCL - VCI - WriteComm(0x16);WriteData(0x81); - WriteComm(0x18);WriteData(0x66); // 偏置电平 VGHR = 6V - WriteComm(0x19);WriteData(0x44); // 偏置电平 VGLR = -6V - WriteComm(0x1E);WriteData(0x02); // 开启电荷平衡 EQ 调制 - WriteComm(0x5B);WriteData(0x10); // 启用参考负压 VREFN5 - WriteComm(0x62);WriteData(0x19); // 正常模式 VREFN5 电平 = -3V - WriteComm(0x63);WriteData(0x19); // 空闲模式 VREFN5 = -3V - WriteComm(0x70);WriteData(0x55); // 关闭显示时源极驱动器至 AVDD - WriteComm(0x1D);WriteData(0x02); // 启用 EQ 平衡 - WriteComm(0x89);WriteData(0x18); // 控制偏置 VGMP = 5.5V - WriteComm(0x8A);WriteData(0xb9); // 控制偏置 VGSP = 2V - WriteComm(0x8B);WriteData(0x01); // 偏置高字节 - WriteComm(0x8C);WriteData(0x10); // VGMP 设为 5.4V - WriteComm(0x8D);WriteData(0x90); // VGSP 设为 2V - WriteComm(0x8E);WriteData(0x01); - WriteComm(0x6E);WriteData(0x0A); // 关闭 MIPI 高速数据接口以选择 SPI 驱动方式 - WriteComm(0x6A);WriteData(0x05); // 配置 MUX 选择器 - WriteComm(0x3A);WriteData(0x08); // 行延迟常数 T1_sd - WriteComm(0x3B);WriteData(0x00); // 帧延迟常数 Tp_sd - WriteComm(0x3D);WriteData(0x16); // 保持延迟常数 Th_sd - WriteComm(0x3F);WriteData(0x27); // 切换时序 Tsw_sd - WriteComm(0x40);WriteData(0x0F); // 切换保持 Thsw_sd - WriteComm(0x41);WriteData(0x0D); // 切换释放 Thsd_sd - - // 如下配置模拟通道多路选通时序 - WriteComm(0x42);WriteData(0x14); - WriteComm(0x43);WriteData(0x41); - WriteComm(0x44);WriteData(0x25); - WriteComm(0x45);WriteData(0x52); - WriteComm(0x46);WriteData(0x36); - WriteComm(0x47);WriteData(0x63); - WriteComm(0x48);WriteData(0x14); - WriteComm(0x49);WriteData(0x41); - WriteComm(0x4A);WriteData(0x25); - WriteComm(0x4B);WriteData(0x52); - WriteComm(0x4C);WriteData(0x36); - WriteComm(0x4D);WriteData(0x63); - WriteComm(0x4E);WriteData(0x14); - WriteComm(0x4F);WriteData(0x41); - WriteComm(0x50);WriteData(0x25); - WriteComm(0x51);WriteData(0x52); - WriteComm(0x52);WriteData(0x36); - WriteComm(0x53);WriteData(0x63); - WriteComm(0x54);WriteData(0x14); - WriteComm(0x55);WriteData(0x41); - WriteComm(0x56);WriteData(0x25); - WriteComm(0x57);WriteData(0x52); - WriteComm(0x58);WriteData(0x36); - WriteComm(0x59);WriteData(0x63); - - WriteComm(0x66);WriteData(0x90); // 空闲模式使用内部集成供电 - WriteComm(0x67);WriteData(0x40); // 内部延迟 1 帧切断电源 - WriteComm(0x72);WriteData(0x1A); // 内部核心 OVDD 电压 = 4.6V - WriteComm(0x73);WriteData(0x07); // 内部核心 OVSS 电压 = -2V - WriteComm(0x74);WriteData(0x0C); // OVDD 的最终输入来源设为 AVDD - WriteComm(0x6A);WriteData(0x3D); // 向单线接口发送脉冲,配置升压 - WriteComm(0x6B);WriteData(0x29); - - WriteComm(0xFE);WriteData(0x04); // 切换至 Page 4 - WriteComm(0x5E);WriteData(0x01); - WriteComm(0x5F);WriteData(0xB8); - WriteComm(0x60);WriteData(0xBB); - WriteComm(0x61);WriteData(0xBB); - WriteComm(0x62);WriteData(0xBB); - WriteComm(0x76);WriteData(0xBB); - WriteComm(0x77);WriteData(0x3B); - WriteComm(0x78);WriteData(0x92); - WriteComm(0x79);WriteData(0xBB); - WriteComm(0x7A);WriteData(0xBB); - - WriteComm(0x00);WriteData(0xDC); // 扫描时钟 CK1 寄存器配置 - WriteComm(0x01);WriteData(0x00); - WriteComm(0x02);WriteData(0x02); - WriteComm(0x03);WriteData(0x00); - WriteComm(0x04);WriteData(0x08); - WriteComm(0x05);WriteData(0x01); - WriteComm(0x06);WriteData(0x70); - WriteComm(0x07);WriteData(0x0A); - WriteComm(0x08);WriteData(0x00); - - WriteComm(0x09);WriteData(0xDC); // 扫描时钟 CK2 寄存器配置 - WriteComm(0x0a);WriteData(0x00); - WriteComm(0x0b);WriteData(0x02); - WriteComm(0x0C);WriteData(0x00); - WriteComm(0x0D);WriteData(0x08); - WriteComm(0x0E);WriteData(0x00); - WriteComm(0x0F);WriteData(0x70); - WriteComm(0x10);WriteData(0x0A); - WriteComm(0x11);WriteData(0x00); - - WriteComm(0x12);WriteData(0xCC); // 发光逻辑 EM_CK1 配置 - WriteComm(0x13);WriteData(0x00); - WriteComm(0x14);WriteData(0x02); - WriteComm(0x15);WriteData(0x00); - WriteComm(0x16);WriteData(0x20); - WriteComm(0x17);WriteData(0x00); - WriteComm(0x18);WriteData(0x28); - WriteComm(0x19);WriteData(0x26); - WriteComm(0x1A);WriteData(0x00); - - WriteComm(0x1B);WriteData(0xCC); // 发光逻辑 EM_CK2 配置 - WriteComm(0x1C);WriteData(0x00); - WriteComm(0x1D);WriteData(0x02); - WriteComm(0x1E);WriteData(0x00); - WriteComm(0x1F);WriteData(0x20); - WriteComm(0x20);WriteData(0x01); - WriteComm(0x21);WriteData(0x28); - WriteComm(0x22);WriteData(0x26); - WriteComm(0x23);WriteData(0x00); - - WriteComm(0x4C);WriteData(0x89); // 扫描起始信号 SN_STV 配置 - WriteComm(0x4D);WriteData(0x00); - WriteComm(0x4E);WriteData(0x01); - WriteComm(0x4F);WriteData(0x00); - WriteComm(0x50);WriteData(0x01); - WriteComm(0x51);WriteData(0xBB); - WriteComm(0x52);WriteData(0xBB); - - WriteComm(0x53);WriteData(0xCA); // 发光起始信号 EM_STV 配置 - WriteComm(0x54);WriteData(0x00); - WriteComm(0x55);WriteData(0x03); - WriteComm(0x56);WriteData(0x00); - WriteComm(0x58);WriteData(0x00); - WriteComm(0x59);WriteData(0x00); - WriteComm(0x65);WriteData(0x45); - WriteComm(0x66);WriteData(0x0C); - WriteComm(0x67);WriteData(0x00); - - WriteComm(0xFE);WriteData(0x01); // 切换至 Page 1 验证芯片 ID - WriteComm(0xE5);WriteData(0x00); - WriteComm(0xE6);WriteData(0x10); - WriteComm(0xE7);WriteData(0x31); - - WriteComm(0xFE);WriteData(0x00); // 切换回 Page 0 进行正常显示操作 - WriteComm(0xC4);WriteData(0x80); - WriteComm(0x2A);WriteData(0x00);WriteData(0x04);WriteData(0x00);WriteData(0x7B); // 设定列像素物理边界 (4~123,宽120像素) - - // ====== 显式设置屏幕像素色彩格式为 16 位像素格式 (COLMOD = 0x75,即RGB565格式) ====== - WriteComm(0x3A);WriteData(0x75); - - WriteComm(0x11);WriteData(0x00); // 唤醒屏芯片:发送 Sleep Out 退出睡眠指令 - Delay_ms(150); // 退出睡眠状态后,必须等待 150ms 以便驱动电荷泵稳定 - - WriteComm(0x29);WriteData(0x00); // 开启屏幕显示:发送 Display ON 指令,像素层开始发光 + // ====== д ST7789V ԭĴʼָ ====== + WriteComm(0x11); // Sleep Out ˳˯ + Delay_ms(120); // ˳˯ߺʱ 120ms + + // Memory Data Access Control (MADCTL) ɨ跽 + // 0x00: £ңRGB ʽ + WriteComm(0x36); + WriteData(0x00); + + // Interface Pixel Format (COLMOD) + // 0x05 16-bit/pixel (RGB565ʽ) + WriteComm(0x3A); + WriteData(0x05); + + // Porch Setting (B2h) + WriteComm(0xB2); + WriteData(0x0C); + WriteData(0x0C); + WriteData(0x00); + WriteData(0x33); + WriteData(0x33); + + // Gate Control (B7h) + // VGH=13.65V, VGL=-10.43V + WriteComm(0xB7); + WriteData(0x35); + + // VCOM Setting (BBh) + // VCOM=1.175V + WriteComm(0xBB); + WriteData(0x19); + + // LCM Control (C0h) + WriteComm(0xC0); + WriteData(0x2C); + + // VDV and VRH Command Enable (C2h) + WriteComm(0xC2); + WriteData(0x01); + + // VRH Set (C3h) + // VRH=4.3V + WriteComm(0xC3); + WriteData(0x12); + + // VDV Set (C4h) + // VDV=0V + WriteComm(0xC4); + WriteData(0x20); + + // Frame Rate Control in Normal Mode (C6h) + // 0x0F: 60Hz + WriteComm(0xC6); + WriteData(0x0F); + + // Power Control 1 (D0h) + WriteComm(0xD0); + WriteData(0xA4); + WriteData(0xA1); + + // 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) ٤ + 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 + WriteComm(0x21); + + // 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) { - // 写入 Column Address Set 列地址设置寄存器命令 (0x2A) + // ڵײ㴰趨Уͳһƫõ͸Уϲͼֹ + x_start += LCD_X_OFFSET; + x_end += LCD_X_OFFSET; + y_start += LCD_Y_OFFSET; + y_end += LCD_Y_OFFSET; + + // д 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)); @@ -318,29 +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; - // 【物理引脚偏移说明】: - // 该 AMOLED 屏在物理层面上水平方向的列像素地址为 4 ~ 123(总宽 120 像素)。 - // 因此调用 LCD_SetWindow 时,X 轴的物理起始和终止坐标必须偏置 +4 才能与物理屏幕像素完全对齐。 - LCD_SetWindow(4, 123, 0, 239); - WriteComm(0x2C); // 发送 RAM Write (0x2C) 显存写入命令 - for(i = 0; i < 120; i++) + LCD_SetWindow(0, LCD_WIDTH - 1, 0, LCD_HEIGHT - 1); + WriteComm(0x2C); // RAM Write (0x2C) Դд + for(i = 0; i < LCD_WIDTH; i++) { - for(j = 0; j < 240; j++) + 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) { @@ -348,10 +233,9 @@ void LCD_FillRect(u16 x, u16 y, u16 w, u16 h, u16 color) u8 ch = (u8)(color >> 8); u8 cl = (u8)(color & 0xFF); - // X 坐标应用物理偏置偏移 +4 像素 - LCD_SetWindow(x + 4, x + w - 1 + 4, y, y + h - 1); - WriteComm(0x2C); // 写入显存 - count = w * h; // 实心矩形包含的总像素数 + LCD_SetWindow(x, x + w - 1, y, y + h - 1); + WriteComm(0x2C); // дԴ + count = w * h; // ʵľΰ for(i = 0; i < count; i++) { WriteData(ch); @@ -360,57 +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); // 上边框线 (厚度 1 像素) - 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位背景色 - * @details 会逐行、逐列解析单色图的字节数组,每一行占 (w+7)/8 个字节, - * 利用位操作提取指定 bit 的状态并发送色彩数据写入屏幕。 + * @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 + 4, x + w - 1 + 4, 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); } } @@ -418,31 +300,30 @@ void LCD_DrawMonoBitmap(u16 x, u16 y, u16 w, u16 h, unsigned char code *bmp, u16 } /** - * @brief 在指定坐标渲染输出一个 8x16 尺寸的普通英文 ASCII 字符 - * @param ch ASCII 字符本身 - * @details 遍历 8x16 点阵字库,将索引对应的 16 个字符编码字节依次提取,按位发送。 + * @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 + 4, x + 8 - 1 + 4, y, y + 16 - 1); // 应用 +4 像素偏移 + 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); @@ -458,45 +339,42 @@ 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 >= 120) - // 若超出或等同于屏幕有效宽度 (120像素),强制从最左侧 0 开始左对齐绘制 + if(len * 8 >= LCD_WIDTH) + // ͬĻЧȣǿƴ 0 ʼ LCD_ShowString(0, y, str, color, bg_color); else - // 否则通过公式 (120 - 字符宽度) / 2 计算出水平居中的首字起始偏移 X 坐标并渲染 - LCD_ShowString((120 - len * 8) / 2, y, str, color, bg_color); + // ͨʽ (LCD_WIDTH - ַ) / 2 ˮƽеʼƫ X 겢Ⱦ + LCD_ShowString((LCD_WIDTH - len * 8) / 2, y, str, color, bg_color); } /** - * @brief 渲染大号特粗 16x32 尺寸的英文/数字字符 - * @details 【字库双重放大技术】: - * 由于 8051 FLASH 容量有限,手环未附带特大型号的 16x32 大字库。 - * 此函数通过算法对 8x16 基础字库点阵数据在水平方向(列)和垂直方向(行)各重复绘制 2 遍 - * (即单个点扩充绘制为 2x2 的像素团),从而实现对原字型双重放大渲染为 16x32 规格,节省空间。 + * @brief Ⱦش 16x32 ߴӢ/ַ */ void LCD_ShowChar16x32(u16 x, u16 y, char ch, u16 color, u16 bg_color) { u8 r, c, i, j; + u8 val_bit; u8 char_idx = ch - 0x20; u8 f_h = (u8)(color >> 8); u8 f_l = (u8)(color & 0xFF); @@ -505,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 + 4, x + 16 - 1 + 4, y, y + 32 - 1); // 锁定 16x32 的绘图视窗 + LCD_SetWindow(x, x + 16 - 1, y, y + 32 - 1); // 16x32 ĻͼӴ WriteComm(0x2C); - // 遍历字型的 16 个点阵行数据 + // ͵ 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++) { - u8 val_bit = (line & (0x80 >> c)); - // 横向插点翻倍:单个像素点重复发送写入 2 次 (j = 0 和 j = 1) + val_bit = (line & (0x80 >> c)); + // 㷭صظд 2 (j = 0 j = 1) for(j = 0; j < 2; j++) { if(val_bit) @@ -540,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 >= 120) - // 超出屏幕宽度,左对齐 + if(len * 16 >= LCD_WIDTH) + // Ļȣ LCD_ShowString16x32(0, y, str, color, bg_color); else - // 水平居中计算 - LCD_ShowString16x32((120 - len * 16) / 2, y, str, color, bg_color); + // ˮƽм + LCD_ShowString16x32((LCD_WIDTH - len * 16) / 2, y, str, color, bg_color); } diff --git a/Drivers/lcd.h b/Drivers/lcd.h index e1e05bf..cfaf929 100644 --- a/Drivers/lcd.h +++ b/Drivers/lcd.h @@ -4,157 +4,118 @@ #include "../App/config.h" #include "stc32g.h" -/* ====== Truly AMOLED RM69310 屏幕驱动 (RGB565格式) 常数定义 ====== */ +/* ====== LHS114TC-IF03 (ST7789V) 屏幕参数与常数定义 ====== */ +#define LCD_WIDTH 135 // 屏幕物理宽度 +#define LCD_HEIGHT 240 // 屏幕物理高度 +#define LCD_X_OFFSET 52 // 水平 X 轴物理偏置量 +#define LCD_Y_OFFSET 40 // 垂直 Y 轴物理偏置量 // 16位 RGB565 颜色常量定义: -// 格式为:Red(5位) Green(6位) Blue(5位) 拼接而成的 16 位整数值 -#define COLOR_BLACK 0x0000 // 黑色 -#define COLOR_WHITE 0xFFFF // 白色 -#define COLOR_CYAN 0x073F // 青色/天蓝色 -#define COLOR_RED 0xF800 // 红色 -#define COLOR_GREEN 0x07E0 // 绿色 -#define COLOR_BLUE 0x001F // 蓝色 -#define COLOR_GRAY 0x5AEB // 灰色 -#define COLOR_AMBER 0xFDC0 // 琥珀色/橙黄色 +#define COLOR_BLACK 0x0000 // 黑色 +#define COLOR_WHITE 0xFFFF // 白色 +#define COLOR_CYAN 0x073F // 青色 +#define COLOR_RED 0xF800 // 红色 +#define COLOR_GREEN 0x07E0 // 绿色 +#define COLOR_BLUE 0x001F // 蓝色 +#define COLOR_GRAY 0x5AEB // 灰色 +#define COLOR_AMBER 0xFDC0 // 琥珀色 /* ====== 屏幕电源控制与基本驱动接口 ====== */ /** - * @brief 通过脉冲序列为 SGM3833 屏幕负压供电芯片发送开启配置脉冲 - * @param pulse_cnt 发送的脉冲个数 (例如 27 次脉冲开启并稳压屏幕输出) - * @details 采用单线脉冲边沿计数调制技术,为 RM69310 控制芯片提供其正常显示所需的高质量偏置正负电压。 + * @brief 通过脉冲序列为 SGM3833 供电芯片写入配置 */ void SGM_SendPulse(u8 pulse_cnt); /** - * @brief 执行 Truly AMOLED 屏幕的硬件复位 - * @details 将 LCD_RST 引脚拉低保持一定时间后拉高,强制复位屏幕控制器,使其恢复到初始逻辑状态。 + * @brief 执行 LHS114TC-IF03 屏幕的硬件复位 */ void LCD_Reset(void); /** - * @brief 初始化 Truly AMOLED 屏幕控制器 RM69310 - * @details 执行屏幕硬件复位,通过软件 SPI 总线依次向屏幕控制器写入寄存器偏置、显示周期、 - * 通道选择、关闭休眠 (Sleep Out: 0x11)、开启显示 (Display On: 0x29) 等一系列 - * 出厂初始化命令序列,使屏幕准备好接收显示像素帧。 + * @brief 初始化 LHS114TC-IF03 (ST7789V) 屏幕控制器 */ void LCD_Init(void); /** - * @brief 向屏幕写入 1 字节控制命令 - * @param cmd 8 位命令字节 + * @brief 向屏幕写入 1 字节控制命令 */ void WriteComm(u8 cmd); /** - * @brief 设定屏幕的数据更新绘制窗口区域 (显存写入范围) - * @param x_start X 轴起始坐标 (0 ~ 119) - * @param x_end X 轴终止坐标 (0 ~ 119) - * @param y_start Y 轴起始坐标 (0 ~ 239) - * @param y_end Y 轴终止坐标 (0 ~ 239) - * @details 限定后续的数据流写入范围,实现局部页面刷新或全部页面刷新。 + * @brief 向屏幕写入 1 字节显示数据 + */ +void WriteData(u8 dat); + +/** + * @brief 设定屏幕的数据更新绘制窗口区域 */ void LCD_SetWindow(u16 x_start, u16 x_end, u16 y_start, u16 y_end); /** - * @brief 以指定纯色清除整个屏幕显示内容 - * @param color 16位 RGB565 颜色填充值 + * @brief 用指定色彩刷写清空全屏幕 */ void LCD_Clear(u16 color); - -/* ====== 像素级图形几何算法与字符渲染接口 ====== */ +/* ====== 关键图形与点阵字符渲染接口 ====== */ /** - * @brief 在屏幕指定位置绘制一个单色填充的实心矩形 - * @param x 矩形左上角 X 轴坐标 - * @param y 矩形左上角 Y 轴坐标 - * @param w 矩形宽度 - * @param h 矩形高度 - * @param color 16位 RGB565 填充色彩值 + * @brief 在屏幕指定视口绘制实心矩形 */ void LCD_FillRect(u16 x, u16 y, u16 w, u16 h, u16 color); /** - * @brief 绘制一个无填充的空心矩形边界框 - * @param x 矩形左上角 X 轴坐标 - * @param y 矩形左上角 Y 轴坐标 - * @param w 边框总宽度 - * @param h 边框总高度 - * @param color 16位 RGB565 边框线条色彩 + * @brief 绘制空心矩形边界框 */ void LCD_DrawRectBorder(u16 x, u16 y, u16 w, u16 h, u16 color); /** - * @brief 绘制渲染一个自定义宽高的单色单色位图 - * @param x 绘制的左上角 X 轴坐标 - * @param y 绘制的左上角 Y 轴坐标 - * @param w 单色图宽度 (单位: 像素点数) - * @param h 单色图高度 - * @param bmp 单色位图所在的 FLASH code 区指针 (1位对应1像素:1画前景色,0画背景色) - * @param color 位图前景色 (RGB565) - * @param bg_color 位图背景色 + * @brief 渲染显示一个单色字模位图资源 */ void LCD_DrawMonoBitmap(u16 x, u16 y, u16 w, u16 h, unsigned char code *bmp, u16 color, u16 bg_color); /** - * @brief 渲染显示一个 8x16 尺寸的普通英文 ASCII 字符 + * @brief 在指定坐标渲染输出一个 8x16 尺寸的普通英文 ASCII 字符 */ void LCD_ShowChar8x16(u16 x, u16 y, char ch, u16 color, u16 bg_color); /** - * @brief 渲染显示一行普通的英文 ASCII 字符串 (左对齐) - * @param x 绘制行起始 X 坐标 - * @param y 绘制行 Y 坐标 - * @param str 字符串头指针 - * @param color 字符颜色 (RGB565) - * @param bg_color 字符背景背景填充色 + * @brief 在指定坐标打印一行普通英文 ASCII 字符串 (左对齐) */ void LCD_ShowString(u16 x, u16 y, char *str, u16 color, u16 bg_color); /** - * @brief 在指定 Y 轴高度上,水平居中对齐显示一行字符串 - * @param y 渲染的 Y 轴高度 (0 ~ 239) - * @param str 字符串内容 - * @param color 颜色值 - * @param bg_color 背景色 + * @brief 在指定 Y 轴高度上,自动计算偏移以实现字符串水平居中显示 */ void LCD_ShowStringCentered(u16 y, char *str, u16 color, u16 bg_color); /** - * @brief 渲染显示一个 16x32 大号特粗尺寸的英文/数字字符 + * @brief 渲染大号特粗 16x32 尺寸的英文/数字字符 */ 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); /** - * @brief 在指定 Y 轴高度上,居中显示 16x32 特大规格字体的字符串 - * @param y 渲染的 Y 轴高度 (0 ~ 239) - * @param str 大字字符串内容 - * @param color 大字前景色 - * @param bg_color 大字背景色 + * @brief 在指定 Y 轴高度上,水平居中渲染显示特大 16x32 规格字符串 */ void LCD_ShowString16x32Centered(u16 y, char *str, u16 color, u16 bg_color); - -/* ====== 图标与点阵字模外部数据声明 ====== */ -// 这些数据实体均定义在底层字库 lcd_font.h 文件中 - -extern unsigned char code FONT_8x16[95][16]; // 8x16 英文 ASCII 点阵字库 -extern unsigned char code bmp_battery_20x10[]; // 20x10 电池电量指示位图 -extern unsigned char code bmp_lock_16x16[]; // 16x16 闭合锁头位图 -extern unsigned char code bmp_unlock_16x16[]; // 16x16 开启锁头位图 -extern unsigned char code bmp_radar_32x32_f1[]; // 32x32 雷达扩散帧 1 -extern unsigned char code bmp_radar_32x32_f2[]; // 32x32 雷达扩散帧 2 -extern unsigned char code bmp_radar_32x32_f3[]; // 32x32 雷达扩散帧 3 -extern unsigned char code bmp_confirm_lock_32x32[]; // 32x32 传感器类型锁头大图 -extern unsigned char code bmp_checkmark_32x32[]; // 32x32 配对成功大对勾 -extern unsigned char code bmp_cross_32x32[]; // 32x32 配对失败大红叉 -extern unsigned char code bmp_door_32x32[]; // 32x32 门磁大警告图标 -extern unsigned char code bmp_siren_32x32[]; // 32x32 烟感喇叭警示图标 +/* ====== 图标外部点阵数据声明 ====== */ +extern unsigned char code FONT_8x16[95][16]; // 8x16 英文点阵字库 +extern unsigned char code bmp_battery_20x10[]; // 20x10 电池指示位图 +extern unsigned char code bmp_lock_16x16[]; // 16x16 闭合锁头位图 +extern unsigned char code bmp_unlock_16x16[]; // 16x16 开启锁头位图 +extern unsigned char code bmp_radar_32x32_f1[]; // 32x32 雷达扩散帧1 +extern unsigned char code bmp_radar_32x32_f2[]; // 32x32 雷达扩散帧2 +extern unsigned char code bmp_radar_32x32_f3[]; // 32x32 雷达扩散帧3 +extern unsigned char code bmp_confirm_lock_32x32[]; // 32x32 锁型常规大图标 +extern unsigned char code bmp_checkmark_32x32[]; // 32x32 成功大对勾 +extern unsigned char code bmp_cross_32x32[]; // 32x32 失败大红叉 +extern unsigned char code bmp_door_32x32[]; // 32x32 门磁大警示图 +extern unsigned char code bmp_siren_32x32[]; // 32x32 烟感喇叭大警示图 #endif // __LCD_H__ diff --git a/Drivers/rf.c b/Drivers/rf.c index d81b59b..cc0a6d0 100644 --- a/Drivers/rf.c +++ b/Drivers/rf.c @@ -1,17 +1,17 @@ #include "rf.h" #include "../App/config.h" +#include "../App/rgb.h" #include "intrins.h" -// ATR5179 射频前端天线开关控制引脚定义 -sbit RF_TX = P2^0; // V1 控制端 (1: 选通天线到发射 TX 路径) -sbit RF_RX = P3^7; // V2 控制端 (1: 选通天线到接收 RX 路径) +// ATR5179 Ƶǰ߿ؿŶ +sbit RF_TX = P2^0; // V1 ƶ (1: ѡͨߵ TX ·) +sbit RF_RX = P3^7; // V2 ƶ (1: ѡͨߵ RX ·) -// 射频芯片数据与电源控制引脚定义 -sbit RF_TX_DAT = P3^6; // LT4455 发射调制数据输入引脚 (DIN) -sbit RF_RX_DATA = P2^1; // LR690L 接收解调数据输出引脚 (DATA),连接单片机中断管脚 -sbit SHUT = P2^2; // LR690L 芯片休眠控制引脚 (1:休眠,0:工作) +// ƵоƬԴŶ (° PCB ŶԵ) +sbit RF_TX_DAT = P2^1; // LR690L (DIN) - Pin 22 (P2.1) +sbit RF_RX_DATA = P3^6; // LR690L ս (DATA) - Pin 19 (P3.6) -// 声明外部延时及串口打印函数 (在 system.c 中定义) +// ⲿʱڴӡ ( system.c ж) extern void Delay_us(u16 us); extern void Delay_ms(u16 ms); extern u16 GetTimer0_Safe(void); @@ -24,168 +24,157 @@ extern void Uart_SendHex32(u32 val); extern void Uart_SendByte(u8 dat); /** - * @brief 初始化无线射频芯片相关的 GPIO 引脚方向与初始状态 + * @brief ʼƵоƬص GPIO ŷ״̬ */ void RF_Init(void) { - // 1. 配置 P2.0 (RF_TX) 和 P2.2 (SHUT) 为推挽输出模式 (M1=0, M0=1) - // 配置 P2.1 (RF_RX_DATA) 为准双向口/输入模式 (M1=0, M0=0) - P2M1 &= ~((1 << 0) | (1 << 1) | (1 << 2)); - P2M0 &= ~(1 << 1); - P2M0 |= ((1 << 0) | (1 << 2)); + // EAXFR = 1 չ⹦ܼĴ (XSFR) + EAXFR = 1; - // 2. 配置 P3.6 (RF_TX_DAT) 和 P3.7 (RF_RX) 为推挽输出模式 (M1=0, M0=1) - P3M1 &= ~((1 << 6) | (1 << 7)); - P3M0 |= ((1 << 6) | (1 << 7)); + // 1. P2.0 (RF_TX) P2.1 (RF_TX_DAT) Ϊģʽ (M1=0, M0=1) + P2M1 &= ~((1 << 0) | (1 << 1)); + P2M0 |= ((1 << 0) | (1 << 1)); - // 3. 设置系统默认的初始电平 - RF_TX = 0; // 断开发射天线通路 - SHUT = 1; // 默认拉高 SHUT,使 LR690L 接收芯片处于低功耗休眠关闭状态 - RF_RX = 0; // 断开接收天线通路 - RF_TX_DAT = 0; // 调制发送数据线置低 + // 2. P3.7 (RF_RX) P3.5 (SHUT) Ϊģʽ (M1=0, M0=1) + // P3.6 (RF_RX_DATA) Ϊģʽ (M1=1, M0=0)ƥս + P3M1 &= ~((1 << 5) | (1 << 7)); + P3M1 |= (1 << 6); + P3M0 &= ~(1 << 6); + P3M0 |= ((1 << 5) | (1 << 7)); + + // 3. ϵͳĬϵijʼƽ + RF_TX = 0; // Ͽͨ· + SHUT = 1; // Ĭ SHUTʹ LR690L ߹ر״̬ + RF_RX = 0; // Ͽͨ· + RF_TX_DAT = 0; // Ʒõ } /** - * @brief 配置无线射频收发系统的运行模式与天线切换 - * @param mode 运行模式 (0-休眠隔离, 1-接收工作, 2-发射工作) + * @brief Ƶշϵͳģʽл + * @param mode ģʽ (0-߸, 1-չ, 2-乤) */ void RF_SetMode(u8 mode) { - if (mode == 0) // ====== 模式 0: 休眠隔离模式 (Sleep/Idle) ====== + if (mode == 0) // ====== ģʽ 0: ߸ģʽ (Sleep/Idle) ====== { - SHUT = 1; // 强制拉高 SHUT 引脚,使 LR690L 彻底进入低功耗待机 - RF_RX = 0; // 关闭接收天线开关,防止外界电磁杂波串入接收电路 - RF_TX = 0; // 关闭发射天线开关 + SHUT = 1; // ǿ SHUT ţʹ LR690L ׽͹Ĵ + RF_RX = 0; // رս߿أֹӲյ· + RF_TX = 0; // رշ߿ } - else if (mode == 1) // ====== 模式 1: 射频接收模式 (Rx Mode) ====== + else if (mode == 1) // ====== ģʽ 1: չģʽ (RX Mode) ====== { - SHUT = 0; // 拉低 SHUT,启动并使能 LR690L 接收解调芯片工作 - RF_RX = 1; // 选通天线切换开关到 RX 路径,连通物理接收天线 - RF_TX = 0; // 断开 TX 发射路径,防止回环烧毁前端 + SHUT = 0; // SHUT ʹ LR690L оƬʼ + RF_TX = 0; // رշ俪 + RF_RX = 1; // ѡͨߵ RX ·ʹܽ·ƵŴ } - else if (mode == 2) // ====== 模式 2: 射频发射模式 (Tx Mode) ====== + else if (mode == 2) // ====== ģʽ 2: 乤ģʽ (TX Mode) ====== { - SHUT = 1; // 关闭 LR690L 接收芯片,避免发射大功率波形串入接收机 - RF_RX = 0; // 断开接收路径天线 - RF_TX = 1; // 选通天线切换开关至 TX 路径,连通物理天线进行发射 + SHUT = 1; // ջֹͣǿźŶǰ + RF_RX = 0; // رստ + RF_TX = 1; // ѡͨߵ TX ·ʹ߷ } } /** - * @brief 按 EV1527 载波规范单次调制发送一帧 24 位无线射频数据 - * @param addr 20 位的发射地址编码 - * @param dat 4 位的命令状态码 - * @details EV1527 是常见的无线编码格式: - * - 一个基本脉冲周期设为 T ≈ 320us。 - * - 同步头脉冲: 1T 高电平 (320us) + 31T 低电平 (9920us)。 - * - 数据位 '1' (逻辑1) 编码: 3T 高电平 (960us) + 1T 低电平 (320us)。 - * - 数据位 '0' (逻辑0) 编码: 1T 高电平 (320us) + 3T 低电平 (960us)。 + * @brief EV1527 Э鵥εƷһ֡ 24 λƵ (Sync + 24bit Data) */ void EV1527_TxFrame(u32 addr, u8 dat) { u8 i; + u32 tx_val; - // ====== 1. 发送同步引导脉冲 ====== + // ϲ 20 λַ 4 λ룬 24 λز + tx_val = (addr << 4) | (dat & 0x0F); + + // 1. ͬͷͣߵƽ 1T (Լ 350us)͵ƽ 31T (Լ 10850us) RF_TX_DAT = 1; - Delay_us(320); // 1T 高电平 + Delay_us(350); RF_TX_DAT = 0; - Delay_us(9920); // 31T 低电平 - - // ====== 2. 依次调制发送 20 位物理地址码 (高位 MSB 优先发送) ====== - for (i = 0; i < 20; i++) + Delay_us(10850); + + // 2. εƷ 24 λλ + for (i = 0; i < 24; i++) { - if ((addr >> (19 - i)) & 1) + if (tx_val & (0x800000UL >> i)) { - // 数据 bit 1: 3T高 + 1T低 + // "1"ߵƽ 3T (1050us)͵ƽ 1T (350us) RF_TX_DAT = 1; - Delay_us(960); + Delay_us(1050); RF_TX_DAT = 0; - Delay_us(320); + Delay_us(350); } else { - // 数据 bit 0: 1T高 + 3T低 + // "0"ߵƽ 1T (350us)͵ƽ 3T (1050us) RF_TX_DAT = 1; - Delay_us(320); + Delay_us(350); RF_TX_DAT = 0; - Delay_us(960); - } - } - - // ====== 3. 依次调制发送 4 位指令状态数据码 (高位 MSB 优先) ====== - for (i = 0; i < 4; i++) - { - if ((dat >> (3 - i)) & 1) - { - // 数据 bit 1: 3T高 + 1T低 - RF_TX_DAT = 1; - Delay_us(960); - RF_TX_DAT = 0; - Delay_us(320); - } - else - { - // 数据 bit 0: 1T高 + 3T低 - RF_TX_DAT = 1; - Delay_us(320); - RF_TX_DAT = 0; - Delay_us(960); + Delay_us(1050); } } } /** - * @brief 循环发射多帧无线 EV1527 信号以保证数据通信的完整性 - * @param addr 20位源地址 - * @param dat 4位命令码 + * @brief η EV1527 Ƶֹ֡֡ʧ */ void EV1527_Transmit(u32 addr, u8 dat) { - u8 r; - // 循环发射 25 次相同的射频帧 (累计耗时约 340 毫秒),抵御外界瞬间同频杂波干扰 - for (r = 0; r < 25; r++) + u8 i; + RF_SetMode(2); // лΪ״̬ͨ俪زն + Delay_ms(5); // ȶƵͨ· + + // ѭ 10 ֡ȷն 1 ֡޲ + for (i = 0; i < 10; i++) { EV1527_TxFrame(addr, dat); + Delay_ms(10); // ֡䱣ʱ } + + RF_TX_DAT = 0; // 㷢 + RF_SetMode(1); // »ָΪռ̬Աⷴ } /** - * @brief 使用定时器 0 (Timer0) 高精度的测量引脚指定电平状态的持续宽度时间 - * @param state 待测电平状态 (1-测量高电平持续宽度, 0-测量低电平持续宽度) - * @param timeout_us 最大等待超时微秒数上限,超时自动退出防锁死 - * @return u16 最终测量的实际电平持续微秒数。若发生超时则直接返回 0。 + * @brief ȡضƽ״̬ij΢ʱ ( Timer0 Ӳ) */ u16 GetPulseDuration(u8 state, u16 timeout_us) { - // 根据系统晶振频率计算在 12T 模式下目标超时的 Timer0 Tick 数值上限 - u16 timeout_ticks = (u16)((u32)timeout_us * (MAIN_Fosc / 1000000UL) / 12UL); - TL0 = 0; // 定时器 0 计数值清零 - TH0 = 0; - TR0 = 1; // 启动定时器 0 硬件计数 + u16 timeout_ticks; - // 轮询引脚:只要电平仍旧等于目标待测状态,就继续保持循环 + // ǿƹر Timer0 ֵ㸴λ + TR0 = 0; + TMOD &= 0xF0; // Timer0 ĴΪģʽ 0 (16λԶ) + TL0 = 0x00; + TH0 = 0x00; + + // 16 λʱ ticks ʱֵ ( 1T ģʽ 24MHz £1 Tick = 1 / 24M = 0.0416us) + // ϵͳ Timer0 ĬΪ 12T ģʽ (1 Tick = 12 / 24MHz = 0.5us) + timeout_ticks = (u16)((u32)timeout_us * (MAIN_Fosc / 1000000UL) / 12UL); + + TR0 = 1; // Timer0 Ӳ + + // Ҫĵƽ״̬жϣֱƽתʱ while (RF_RX_DATA == state) { - // 软件防卡死判定:如果当前计数值超过了设定的最大允许超时 Tick 值 if (GetTimer0_Safe() > timeout_ticks) { - TR0 = 0; // 关断定时器 - return 0; // 判定超时,返回 0 + TR0 = 0; + return 0; // жʱ 0 } } - TR0 = 0; // 关断定时器 + TR0 = 0; // ض϶ʱ - // 将最终读取的 Timer0 硬件 Tick 数值,重新换算成微秒数返回 (1 Tick = 12 / 24MHz = 0.5us) return (u16)((u32)GetTimer0_Safe() * 12UL / (MAIN_Fosc / 1000000UL)); } -#include "../App/xtell.h" - -// 模拟射频信号全局变量定义 +// ģƵźȫֱ volatile u32 mock_rx_addr = 0; volatile u8 mock_rx_data = 0; volatile bit mock_rf_ready = 0; +/** + * @brief ʽⲢһϷ EV1527 Ƶź֡ + */ bit EV1527_Decode(u32 *out_addr, u8 *out_data) { u16 high_time, low_time; @@ -193,10 +182,10 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data) u32 addr = 0; u8 dat = 0; u16 T; - u16 idata raw_h[24]; // 高电平时间寄存数组 (置于 idata 区) - u16 idata raw_l[24]; // 低电平时间寄存数组 + u16 idata raw_h[24]; + u16 idata raw_l[24]; - // 检查模拟射频触发 + // ģƵ if (mock_rf_ready) { mock_rf_ready = 0; *out_addr = mock_rx_addr; @@ -204,226 +193,145 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data) return 1; } - // ====== 1. 同步头引导脉冲捕获 (定位上升沿) ====== + // ====== 1. ͬͷ岶 ====== { u16 wait_cnt = 0; - // 如果当前射频解调输出是高电平,等待其变低 while (RF_RX_DATA == 1) { Delay_us(5); wait_cnt++; - if (wait_cnt > 2000) // 10 毫秒超时保护 + if (wait_cnt > 2000) // 10 볬ʱ return 0; } - // 当引脚维持在低电平后,阻塞等待它跳变为高电平(捕获到同步引导信号的上升沿) wait_cnt = 0; while (RF_RX_DATA == 0) { Delay_us(5); wait_cnt++; - if (wait_cnt > 4000) // 20 毫秒超时保护 + if (wait_cnt > 4000) // 20 볬ʱ return 0; } } - // 捕获引导头高电平时间 (放宽测量阈值上限至 2500 微秒) + // ͷߵƽʱ high_time = GetPulseDuration(1, 2500); if (high_time < 50 || high_time > 2500) { - if (high_time > 10) { // 剔除高频毛刺噪声,防止调试串口满载 - Uart_SendString("Sync H Err: "); - Uart_SendHex16(high_time); - Uart_SendString("us\r\n"); - } return 0; } - // 捕获引导头后续的 31T 低电平时间 (设定上限 60 毫秒) + // ͷ 31T ͵ƽʱ low_time = GetPulseDuration(0, 60000); if (low_time < 1500 || low_time > 60000) { - Uart_SendString("Sync L Err H="); - Uart_SendHex16(high_time); - Uart_SendString(" L="); - Uart_SendHex16(low_time); - Uart_SendString("us\r\n"); return 0; } - // 计算低电平与高电平的比值,以检验是否满足 EV1527 的 31:1 特征 - { - u32 ratio = (u32)low_time / high_time; - if (ratio < 15 || ratio > 48) { // 允许 50% 左右的空气无线传输畸变误差 - Uart_SendString("Sync Ratio Err H="); - Uart_SendHex16(high_time); - Uart_SendString(" L="); - Uart_SendHex16(low_time); - Uart_SendString(" R="); - Uart_SendHex16((u16)ratio); - Uart_SendString("\r\n"); - return 0; - } - } + // ͬĻ׼ʱ T (ߵƽȼΪ 1T) + T = high_time; + if (T < 100 || T > 800) return 0; // ӦΧ޷: 100us ~ 800us - // 自适应估算出基准脉冲时值 T - T = low_time / 31; - if (T == 0) T = 1; // 零安全防护 - - // ====== 2. 依次采集接下来的 24 个数据 bit 脉冲 ====== + // ====== 2. ν 24 λ ====== for (i = 0; i < 24; i++) { - u16 wait_cnt = 0; - // 等待下一个数据位跳变产生的上升沿 (低电平变高) - while (RF_RX_DATA == 0) - { - Delay_us(5); - wait_cnt++; - if (wait_cnt > 1000) // 5ms 超时判定,防无线信号中断卡死 - { - return 0; // 捕获失败 - } - } - - // 精密测算本位的高电平与低电平宽度 - raw_h[i] = GetPulseDuration(1, 5000); - if (raw_h[i] == 0) return 0; // 超时错误直接退出 + raw_h[i] = GetPulseDuration(1, 2000); + if (raw_h[i] == 0) return 0; - raw_l[i] = GetPulseDuration(0, 5000); + raw_l[i] = GetPulseDuration(0, 2000); if (raw_l[i] == 0) return 0; } - // ====== 3. 串口输出捕获的时序参数 ====== - Uart_SendString("Captured: Sync H="); - Uart_SendHex16(high_time); - Uart_SendString("us, L="); - Uart_SendHex16(low_time); - Uart_SendString("us, T="); - Uart_SendHex16(T); - Uart_SendString("us\r\n"); - - // ====== 4. 自适应决策:组合成 24 位完整射频数据帧 ====== + // ====== 3. ˫˱ֵж ====== for (i = 0; i < 24; i++) { - // 比较本位高低电平持续宽度: - // 如果高电平大于低电平,说明宽脉冲在高,判定该位为 1 (3T高+1T低)。 - // 否则高电平短,判定为 0 (1T高+3T低)。 - if (raw_h[i] > raw_l[i]) + u16 total = raw_h[i] + raw_l[i]; + if (total < T * 3 || total > T * 6) return 0; // λڱ 3T~6T ֮ + + if (raw_h[i] > raw_l[i] * 2) { - addr = (addr << 1) | 1; // 写入 24 位物理地址与状态数据相结合的最终值 + // ߵƽԳڵ͵ƽ߼ "1" + if (i < 20) + addr = (addr << 1) | 1; + else + dat = (dat << 1) | 1; + } + else if (raw_l[i] > raw_h[i] * 2) + { + // ͵ƽԳڸߵƽ߼ "0" + if (i < 20) + addr = (addr << 1); + else + dat = (dat << 1); } else { - addr = (addr << 1); + return 0; // ƽֵϹ棬ж֡䣬 } } - *out_addr = addr; // 输出包含状态数据的 24 位完整射频编码作为传感器唯一 ID - *out_data = (u8)(addr & 0x0F); // 输出低 4 位作为指令/状态,保持调用兼容性 - - // 串口打印解码最终结果 - Uart_SendString("Decoded 24bit ADDR: 0x"); - Uart_SendHex32(addr); - Uart_SendString("\r\n"); - - return 1; // 成功解析 + // У + *out_addr = addr; + *out_data = dat; + return 1; } /** - * @brief 软硬件环路自检回环测试 - * @details 将发射引脚置位并同时采样接收引脚,用于测试收发芯片控制连通性。 + * @brief RF շϲԹ (ַ֧ռӡ) */ -void Loopback_Test(void) +void RF_DiagnosticMode(char choice) { - u16 match_count = 0; - u16 i; - - SHUT = 0; // 强行开启接收芯片供电 - RF_RX = 1; // 选通天线到 RX - RF_TX = 0; // 发送引脚隔离 - Delay_ms(100); - - Uart_SendString("Starting RF Loopback Self-Test...\r\n"); - - // 高频翻转模拟发送引脚,同步读取接收解调出的管脚信号,统计匹配率 - for (i = 0; i < 100; i++) + if (choice == 't') { - RF_TX_DAT = 1; - Delay_us(500); - if (RF_RX_DATA == 1) match_count++; - - RF_TX_DAT = 0; - Delay_us(500); - if (RF_RX_DATA == 0) match_count++; - } - - Uart_SendString("Loopback Match Count: "); - Uart_SendHex16(match_count); - Uart_SendString("/200\r\n"); - - if (match_count > 150) - { - Uart_SendString("Result: PASS! Transmitter is emitting RF, and Receiver is picking it up!\r\n"); - } - else - { - Uart_SendString("Result: FAIL! No RF signal detected. Check hardware.\r\n"); - } - - SHUT = 1; // 关闭接收芯片 - RF_RX = 0; // 断开接收通道 -} + u8 i; + Uart_SendString("=== RF TX Test: Transmitting 20 test frames... ===\r\n"); + // ģʽ߿ + RF_SetMode(2); + Delay_ms(5); -/** - * @brief 统计接收引脚在 5 秒内的电平翻转次数,用以评估物理噪声污染度或管脚短路 - */ -void RF_Test_Pin(void) -{ - u8 i; - u16 j; - u16 transitions = 0; - bit last_state = RF_RX_DATA; - - Uart_SendString("=== RF RX Pin Data Test Start (5 seconds) ===\r\n"); - Uart_SendString("SHUT pin state: "); - Uart_SendByte(SHUT ? '1' : '0'); - Uart_SendString("\r\n"); - - // 强制设为接收模式 - RF_SetMode(1); - Delay_ms(100); - - Uart_SendString("SHUT pin state (after RF_SetMode(1)): "); - Uart_SendByte(SHUT ? '1' : '0'); - Uart_SendString("\r\n"); - Uart_SendString("Please trigger your 433MHz transmitter now...\r\n"); - - // 连续进行 5 秒的高频电平抖动采样 - for (i = 0; i < 5; i++) - { - transitions = 0; - // 1秒循环 - for (j = 0; j < 1000; j++) + for (i = 0; i < 20; i++) { - u8 k; - // 每次内循环进行 5 次采样,每次间隔 200us - for (k = 0; k < 5; k++) - { - bit current_state = RF_RX_DATA; - if (current_state != last_state) - { - transitions++; // 统计电平边沿翻转次数 - last_state = current_state; - } - Delay_us(200); - } + // Ͳ԰ÿηͰָʾ˸ + MOTOR = 1; + RGB_Send(0, 150, 0, 0, 150, 0); // ̹˸ + EV1527_TxFrame(0x37A86UL, 0x01); + MOTOR = 0; + RGB_Send(0, 0, 0, 0, 0, 0); + + Delay_ms(150); // ֡ } - - // 输出本秒的电平跳转总数,若非发射期该数值很大,表明空中有丰富噪声噪底跳变,属接收机正常表现 - Uart_SendString("Sampling... Transitions in last 1s: "); - Uart_SendHex16(transitions); - Uart_SendString(", current pin value: "); - Uart_SendByte(RF_RX_DATA ? '1' : '0'); - Uart_SendString("\r\n"); + RF_TX_DAT = 0; + RF_SetMode(1); // »ָģʽ + Uart_SendString("[RF] Transmit Done!\r\n"); + } + else if (choice == 'r') + { + u16 wait_ms; + u32 rx_addr; + u8 rx_data; + + Uart_SendString("=== RF RX Test: Listening for 5 seconds... ===\r\n"); + RF_SetMode(1); // ýģʽʹ + Delay_ms(10); + + // 5ķ + for (wait_ms = 0; wait_ms < 5000; wait_ms++) + { + if (EV1527_Decode(&rx_addr, &rx_data)) + { + Uart_SendString("[RF] Decoded successfully! Addr: 0x"); + Uart_SendHex32(rx_addr); + Uart_SendString(", Cmd/Data: "); + Uart_SendHex8(rx_data); + Uart_SendString("\r\n"); + + // յźź𶯲ɫ + MOTOR = 1; + RGB_Send(0, 150, 150, 0, 150, 150); + Delay_ms(100); + MOTOR = 0; + RGB_Send(0, 0, 0, 0, 0, 0); + } + Delay_ms(1); + } + Uart_SendString("=== RF RX Test End ===\r\n"); } - - Uart_SendString("=== RF Pin Test End ===\r\n"); } diff --git a/Drivers/rf.h b/Drivers/rf.h index 249c2e6..fc2ff16 100644 --- a/Drivers/rf.h +++ b/Drivers/rf.h @@ -52,18 +52,13 @@ u16 GetPulseDuration(u8 state, u16 timeout_us); * @param out_addr 输出参数指针:若解码成功,将写入解析得到的 20 位发送地址 (即 24 位帧除去 4 位数据位后的剩余高 20 位) * @param out_data 输出参数指针:若解码成功,将写入解析出的 4 位命令状态数据码 (例如报警/SOS等) * @return bit 1-成功解析出一帧有效数据;0-当前未捕获到合法脉冲或校验失败 - * @note 此函数通过对 P2.1 (RF_RX_DATA) 输入脉冲宽度进行软件精密测量,定位同步头,依次采集 24 个数据位,并进行 3 次重复性去噪校验。 + * @note 此函数通过对 P3.6 (RF_RX_DATA) 输入脉冲宽度进行软件精密测量,定位同步头,依次采集 24 个数据位,并进行 3 次重复性去噪校验。 */ bit EV1527_Decode(u32 *out_addr, u8 *out_data); /** - * @brief 串口与射频功能的回环测试函数 (调试使用) + * @brief RF 收发诊断测试功能 (支持发送与接收监听打印) */ -void Loopback_Test(void); - -/** - * @brief 射频控制引脚的高低电平输出极性测试 (硬件排查使用) - */ -void RF_Test_Pin(void); +void RF_DiagnosticMode(char choice); #endif // __RF_H__