Compare commits

...

10 Commits

Author SHA1 Message Date
edisondeng
d8df5d49d3 fix: 修复 main.c/event.c/lcd.c 的中文注释乱码,统一为 UTF-8 编码
lcd.c 原本整体是 GBK 编码,直接转码即可。main.c 与 event.c 曾被
Keil 环境以错误编码保存导致部分注释损坏,本次以历史提交中未损坏的
版本为基底恢复正确注释文本,并将其后新增的真实代码改动(RGB 三色
诊断指令、SOS 按键状态机解锁)对应的注释一并还原,不涉及任何代码逻辑变更。
2026-07-31 09:54:59 +08:00
41bb78aa59 feat: 增加调试串口指令 'q'/'Q',支持自动轮流点亮红、绿、蓝三色以校准物理灯珠通道顺序 2026-07-30 13:46:44 +08:00
be397fba76 feat: 按照要求调整 SOS 期间 RGB 闪烁顺序,并延长闪烁间隔为 300ms 方便人眼分辨 2026-07-30 13:44:09 +08:00
58dfe5bb9c debug: 增加 Loopback_Test 硬件电气回环自检及对应的调试串口指令 'l' 2026-07-29 10:16:05 +08:00
a766ada53d fix: 彻底解开 SOS 求救按键状态机被注释的屏蔽,使按键正常产生求救事件并分发 2026-07-29 10:09:19 +08:00
48db6523fb debug: 增加 KEY_SOS 物理按键开机自检与运行期电平跳变串口调试打印 2026-07-29 10:04:00 +08:00
2f4dd2660d fix: 解锁恢复 SOS 物理按键电平读取逻辑,并添加 GPIO_Init 中对应 P2.6 的上拉配置 2026-07-29 09:57:36 +08:00
0a79e7a3b9 fix: 重构 EV1527 发射时序,使用硬件 Timer0 保证 100% 精准码宽延迟以成功触发接收机 2026-07-29 09:40:54 +08:00
d7cb2ae37f fix: 开机默认挂载后台射频监控应用,修复唤醒/复位后无法识别传感器的问题 2026-07-28 18:34:28 +08:00
8367524eec fix: 完美复原低功耗停机休眠与外部唤醒控制寄存器,修复唤醒死机 2026-07-28 18:22:04 +08:00
7 changed files with 467 additions and 209 deletions

View File

@@ -67,11 +67,12 @@ SystemEvent EventQueue_Pop(void)
SystemEvent evt; SystemEvent evt;
// 默认空事件属性 // 默认空事件属性
evt.key_event = KEY_EVENT_NONE; evt.key_event = KEY_EVENT_NONE;
evt.priority = EVENT_PRIORITY_NORMAL;
evt.extra_data = 0; evt.extra_data = 0;
evt.extra_size = 0; evt.extra_size = 0;
if (queue_count == 0) return evt; // 队空,直接返回空闲事件 if (queue_count == 0) return evt; // 队空,直接返回空闲事件
EA = 0; // 屏蔽中断 EA = 0; // 屏蔽中断
evt = event_queue[queue_head]; // 读取队首数据 evt = event_queue[queue_head]; // 读取队首数据
queue_head = (queue_head + 1) % EVENT_QUEUE_DEPTH; // 头指针向后移动,回环 queue_head = (queue_head + 1) % EVENT_QUEUE_DEPTH; // 头指针向后移动,回环
@@ -111,14 +112,14 @@ u8 EventQueue_GetCount(void)
void Event_Dispatcher_Loop(void) void Event_Dispatcher_Loop(void)
{ {
SystemEvent evt = EventQueue_Pop(); // 弹出一个事件 SystemEvent evt = EventQueue_Pop(); // 弹出一个事件
// 如果是无事发生的空事件,直接退出 // 如果是无事发生的空事件,直接退出
if (evt.key_event == KEY_EVENT_NONE && evt.extra_size == 0) { if (evt.key_event == KEY_EVENT_NONE && evt.extra_size == 0) {
return; return;
} }
inactivity_timer = 0; // 捕获到有效事件,清零休眠计时器,重置闲置超时 inactivity_timer = 0; // 捕获到有效事件,清零休眠计时器,重置闲置超时
// 串口打印调试信息 // 串口打印调试信息
XTELL_LOG("[EVENT] key="); XTELL_LOG("[EVENT] key=");
XTELL_LOG_HEX8((u8)evt.key_event); XTELL_LOG_HEX8((u8)evt.key_event);
@@ -193,9 +194,22 @@ void Event_KeyScan_Poll(void)
u8 raw_up = !KEY_UP; 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_confirm = !KEY_CONFIRM;
u8 raw_sos = 0; // 临时注释关闭 SOS 物理按键电平检测,防浮空干扰 u8 raw_sos = !KEY_SOS; // 恢复 SOS 物理按键电平检测
key_scan_timer = 0; // 重置扫描分频计数器 key_scan_timer = 0; // 重置扫描分频计数器
// 【SOS 按键电平实时诊断】
{
static u8 last_raw_sos = 99;
if (raw_sos != last_raw_sos) {
last_raw_sos = raw_sos;
Uart_SendString("[KEY-DIAG] KEY_SOS (P2.6) logic raw: ");
Uart_SendByte(raw_sos ? '1' : '0');
Uart_SendString(", Physical GPIO Pin: ");
Uart_SendByte(KEY_SOS ? '1' : '0');
Uart_SendString("\r\n");
}
}
// 按键调试诊断:若按键 ADC 值有变化或处于高电平,串口输出真实 ADC 值 // 按键调试诊断:若按键 ADC 值有变化或处于高电平,串口输出真实 ADC 值
{ {
u16 down_val = ADC_ReadFiltered(ADC_CHANNEL_KEY_DOWN); u16 down_val = ADC_ReadFiltered(ADC_CHANNEL_KEY_DOWN);
@@ -221,6 +235,7 @@ void Event_KeyScan_Poll(void)
// 默认将派发事件重置为空 // 默认将派发事件重置为空
evt.key_event = KEY_EVENT_NONE; evt.key_event = KEY_EVENT_NONE;
evt.priority = EVENT_PRIORITY_NORMAL;
evt.extra_data = 0; evt.extra_data = 0;
evt.extra_size = 0; evt.extra_size = 0;
@@ -283,21 +298,23 @@ void Event_KeyScan_Poll(void)
key_confirm_hold = 0; // 释放归零 key_confirm_hold = 0; // 释放归零
} }
/* // SOS 物理求救按键状态机 (已解锁)
// SOS 物理求救按键状态机
if (raw_sos) { if (raw_sos) {
key_sos_hold++; key_sos_hold++;
if (key_sos_hold == 300) { // 长按 3 秒,发出求救,插入队首 if (key_sos_hold == 300) { // 长按 3 秒,发出求救,插入队首
evt.key_event = KEY_EVENT_SOS_LONG; evt.key_event = KEY_EVENT_SOS_LONG;
evt.priority = EVENT_PRIORITY_URGENT;
EventQueue_InsertFront(evt); EventQueue_InsertFront(evt);
Uart_SendString("[KEY-DIAG] SOS Long Press Dispatched!\r\n");
} }
} else { } 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.key_event = KEY_EVENT_SOS_CLICK;
evt.priority = EVENT_PRIORITY_URGENT;
EventQueue_InsertFront(evt); EventQueue_InsertFront(evt);
Uart_SendString("[KEY-DIAG] SOS Click Dispatched!\r\n");
} }
key_sos_hold = 0; // 释放归零 key_sos_hold = 0; // 释放归零
} }
*/
} }
} }

View File

@@ -55,6 +55,42 @@ volatile u16 comb_hold = 0; // ▲ + ▼ 组合键按下时长累
* - 'z'/'Z': 模拟超时,强行使闲置计数器置 6000触发自动深度休眠。 * - 'z'/'Z': 模拟超时,强行使闲置计数器置 6000触发自动深度休眠。
* - 't'/'T': 触发屏幕与无线控制引脚测试。 * - 't'/'T': 触发屏幕与无线控制引脚测试。
*/ */
/**
* @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)
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)
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)
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");
}
void Debug_ProcessCommand(char cmd) void Debug_ProcessCommand(char cmd)
{ {
SystemEvent debug_evt; SystemEvent debug_evt;
@@ -146,10 +182,14 @@ void Debug_ProcessCommand(char cmd)
} else if (cmd == 'z' || cmd == 'Z') { } else if (cmd == 'z' || cmd == 'Z') {
inactivity_timer = 6000; // 模拟闲置 60 秒超时,强制触发主循环深度休眠 inactivity_timer = 6000; // 模拟闲置 60 秒超时,强制触发主循环深度休眠
Uart_SendString("[UART] Force Entering Sleep mode!\r\n"); Uart_SendString("[UART] Force Entering Sleep mode!\r\n");
} else if (cmd == 'q' || cmd == 'Q') {
RGB_Diagnostic_Test(); // 启动 RGB 物理颜色通道排查测试
} else if (cmd == 't' || cmd == 'T') { } else if (cmd == 't' || cmd == 'T') {
RF_DiagnosticMode('t'); // 触发射频连续发射诊断 (绿色爆闪) RF_DiagnosticMode('t'); // 触发射频连续发射诊断 (绿色爆闪)
} else if (cmd == 'r' || cmd == 'R') { } 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 自检)
} }
} }
@@ -174,7 +214,7 @@ void main(void)
LCD_Init(); // 初始化 LHS114TC-IF03 (ST7789V) 屏幕控制寄存器序列并开启背光 LCD_Init(); // 初始化 LHS114TC-IF03 (ST7789V) 屏幕控制寄存器序列并开启背光
Uart1_Init(); // 初始化串口 1 波特率 115200 供调试日志 Uart1_Init(); // 初始化串口 1 波特率 115200 供调试日志
Uart_SendString("Wristband System Initialized!\r\n"); Uart_SendString("Wristband System Initialized!\r\n");
Self_Test(); // 执行上电硬件自检 (PWR_HOLD锁存、P0.3 ADC按键常态、P1.3 电池电量) Self_Test(); // 执行上电硬件自检 (PWR_HOLD锁存、P0.3 ADC按键常态、P1.3 电池电量)
Load_Database(); // 从 IAP Flash 第 254 扇区读取所有已绑定的传感器数据至 RAM Load_Database(); // 从 IAP Flash 第 254 扇区读取所有已绑定的传感器数据至 RAM
@@ -184,6 +224,9 @@ void main(void)
/* 步骤 2+5: 初始化应用管理器并登记所有前台应用,默认加载 ClockApp */ /* 步骤 2+5: 初始化应用管理器并登记所有前台应用,默认加载 ClockApp */
AppManager_Init(); AppManager_Init();
/* 启动并挂载后台射频监控应用,开始侦听无线传感器信号 */
AppManager_AttachToBackground(&rf_monitor_app);
/* 启动 1ms 系统基准定时器 Timer1 */ /* 启动 1ms 系统基准定时器 Timer1 */
Timer1_Init(); Timer1_Init();
@@ -219,6 +262,8 @@ void main(void)
inactivity_timer = 0; inactivity_timer = 0;
current_state = STATE_SLEEP; // 系统置为休眠状态 current_state = STATE_SLEEP; // 系统置为休眠状态
Enter_Low_Power_Sleep(); // 该函数是阻塞的MCU 将在此挂起。唤醒后将自动从其后继续执行 Enter_Low_Power_Sleep(); // 该函数是阻塞的MCU 将在此挂起。唤醒后将自动从其后继续执行
current_state = STATE_NORMAL; // 唤醒后重新置为正常待机状态
AppManager_StartApp(APP_ID_CLOCK); // 重新开启时钟应用以刷新画面和背光
} }
} }
} }
@@ -263,7 +308,7 @@ void Port2_Isr(void) interrupt 15
*/ */
void Port3_Isr(void) interrupt 16 void Port3_Isr(void) interrupt 16
{ {
EAXFR = 1; // 使能访问扩展寄存 EAXFR = 1; // 使能访问扩展寄存
p3_wakeup_flag = P3INTF | 0x01;// 读取并备份 P3 中断标志寄存器值 (0x01作安全掩码) p3_wakeup_flag = P3INTF | 0x01;// 读取并备份 P3 中断标志寄存器值 (0x01作安全掩码)
P3INTF = 0x00; // 清除 P3 端口中断悬挂标志位 P3INTF = 0x00; // 清除 P3 端口中断悬挂标志位
P3INTE = 0x00; // 立即关闭 P3 中断通道,防止反复重入 P3INTE = 0x00; // 立即关闭 P3 中断通道,防止反复重入

View File

@@ -51,6 +51,12 @@ void GPIO_Init(void)
MOTOR = 0; MOTOR = 0;
RGB_DIN = 0; // P2.3 初始拉低,防止上电期间 WS2812B 误触发 RGB_DIN = 0; // P2.3 初始拉低,防止上电期间 WS2812B 误触发
// 配置 KEY_SOS (P2.6) 为带上拉准双向口模式以确保输入稳定
P2M1 &= ~(1 << 6);
P2M0 &= ~(1 << 6);
KEY_SOS = 1;
(*(unsigned char volatile xdata *)0xFE12) |= (1 << 6); // 使能 P2.6 的强内部上拉电阻 (P2PU = 0xFE12)
// SGM_CTRL (P1.7), LCD 控制管脚 (P1.0, P1.1, P1.4, P1.5, P1.6) 置为推挽输出 // SGM_CTRL (P1.7), LCD 控制管脚 (P1.0, P1.1, P1.4, P1.5, P1.6) 置为推挽输出
P1M1 &= ~((1 << 7) | (1 << 0) | (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6)); P1M1 &= ~((1 << 7) | (1 << 0) | (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6));
P1M0 |= ((1 << 7) | (1 << 0) | (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6)); P1M0 |= ((1 << 7) | (1 << 0) | (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6));
@@ -206,6 +212,11 @@ void Self_Test(void)
Uart_SendByte((u8)('0' + (key_down_adc % 10))); Uart_SendByte((u8)('0' + (key_down_adc % 10)));
Uart_SendString(" [OK]\r\n"); Uart_SendString(" [OK]\r\n");
// 2.2 检测 KEY_SOS (P2.6) 物理电平常态
Uart_SendString("[SELF-TEST] KEY_SOS (P2.6) Pin Level: ");
Uart_SendByte((u8)(KEY_SOS ? '1' : '0'));
Uart_SendString("\r\n");
// 3. 检查 P1.3 电池电压检测与百分比 // 3. 检查 P1.3 电池电压检测与百分比
vbat_mv = ADC_GetBatteryVoltage_mV(); vbat_mv = ADC_GetBatteryVoltage_mV();
percent = ADC_GetBatteryPercent(); percent = ADC_GetBatteryPercent();
@@ -271,7 +282,7 @@ void Power_Off(void)
MOTOR = 0; MOTOR = 0;
// 关断屏幕背光与 RGB 灯光 // 关断屏幕背光与 RGB 灯光
SGM_CTRL = 0; SGM_CTRL = 1; // 高电平断电,关闭背光灭屏
RGB_Send(0, 0, 0, 0, 0, 0); RGB_Send(0, 0, 0, 0, 0, 0);
// 拉低 PWR_HOLD 切断电源锁存使总电源关闭 // 拉低 PWR_HOLD 切断电源锁存使总电源关闭
@@ -288,15 +299,81 @@ void Power_Off(void)
*/ */
void Enter_Low_Power_Sleep(void) void Enter_Low_Power_Sleep(void)
{ {
Uart_SendString("[SYS] Entering Sleep mode...\r\n"); Uart_SendString("[SYS] Entering low power sleep...\r\n");
SGM_CTRL = 0; // 熄灭背光
// 1. 关闭/熄灭 RGB 灯
RGB_Send(0, 0, 0, 0, 0, 0); RGB_Send(0, 0, 0, 0, 0, 0);
// 配置 PCON 挂起单片机 // 2. 向 LCD 芯片写入 Display OFF 指令 (0x28) 关显示
PCON |= 0x02; // PD = 1 掉电停机模式 WriteComm(0x28);
Delay_ms(20);
// 3. 向 LCD 芯片写入 Sleep In 指令 (0x10) 关内部电荷泵
WriteComm(0x10);
Delay_ms(20);
// 4. 关闭背光(高电平断电,关闭背光灭屏)
SGM_CTRL = 1;
// 5. 屏幕接口防漏电处理
LCD_CS = 1;
LCD_RST = 1;
LCD_DCX = 0;
LCD_SCL = 0;
LCD_SDI = 0;
// 6. 射频设置为关闭接收模式低功耗SHUT=0, RF_SetMode(0)
RF_SetMode(0);
// 使能访问扩展特殊功能寄存器
P_SW2 |= 0x80;
// 7. 配置 Port0 中断唤醒源 (KEY_UP=P0.1, KEY_CONFIRM=P0.2, KEY_DOWN=P0.3)
P0IM1 |= 0x0E;
P0IM0 &= ~0x0E; // 下降沿触发
P0INTF = 0x00; // 清除挂起的中断标志
P0INTE |= 0x0E; // 开启 P0.1, P0.2, P0.3 中断允许
P0WKUE |= 0x0E; // 使能 P0.1, P0.2, P0.3 掉电唤醒
// 8. 配置 Port2 中断唤醒源 (SOS=P2.6)
P2IM1 |= 0x40;
P2IM0 &= ~0x40; // 下降沿触发
P2INTF = 0x00; // 清除挂起标志
P2INTE |= 0x40; // 开启 P2.6 中断允许
P2WKUE |= 0x40; // 使能 P2.6 掉电唤醒
// 9. 配置 Port3 中断唤醒源 (RF_RX_DATA=P3.6)
P3IM1 |= 0x40;
P3IM0 |= 0x40; // 双边沿触发
P3INTF = 0x00; // 清除挂起标志
P3INTE |= 0x40; // 开启 P3.6 中断允许
P3WKUE |= 0x40; // 使能 P3.6 掉电唤醒
// 确保引脚上拉和输入使能,防止因浮空产生的功耗泄漏
P2PU |= 0x40;
P2IE |= 0x40;
P3PU &= ~0x40;
P3IE |= 0x40;
// 清零各个唤醒检测标志位
P0INTF = 0x00;
P2INTF = 0x00;
P3INTF = 0x00;
p0_wakeup_flag = 0;
p2_wakeup_flag = 0;
p3_wakeup_flag = 0;
rf_wakeup_flag = 0;
// 10. 关闭系统滴答定时器 Timer1 中断,防止休眠时被定时器误唤醒
ET1 = 0;
EA = 1; // 开启全局中断
// 配置 PCON 挂起单片机,切入停机 (Power Down) 模式
PCON |= 0x02; // PD = 1
_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_();
// 被唤醒后继续执行 // ====== 被外部中断唤醒后,在此处继续运行 ======
Wakeup_Restore(); Wakeup_Restore();
} }
@@ -305,8 +382,52 @@ void Enter_Low_Power_Sleep(void)
*/ */
void Wakeup_Restore(void) void Wakeup_Restore(void)
{ {
SGM_CTRL = 1; // 恢复背光 EAXFR = 1;
Uart_SendString("[SYS] System Woken Up!\r\n"); // 1. 立即关闭所有掉电外部中断允许与唤醒允许,防止重入
P0INTE = 0x00;
P2INTE = 0x00;
P3INTE = 0x00;
P0WKUE = 0x00;
P2WKUE = 0x00;
P3WKUE = 0x00;
P0INTF = 0x00;
P2INTF = 0x00;
P3INTF = 0x00;
// 2. 清零掉电唤醒定时器
WKTCH = 0x00;
WKTCL = 0x00;
// 3. 开启全局中断并重启系统滴答定时器
EA = 1;
ET1 = 1;
// 4. 恢复射频芯片工作模式
RF_SetMode(1);
// 打印唤醒调试日志
if ((p0_wakeup_flag & 0x0E) || (p2_wakeup_flag & 0x40)) {
Uart_SendString("[WR] Woken by KEY (P0=");
Uart_SendHex8(p0_wakeup_flag);
Uart_SendString(", P2=");
Uart_SendHex8(p2_wakeup_flag);
Uart_SendString(")\r\n");
} else if ((p3_wakeup_flag & 0x40) || rf_wakeup_flag) {
rf_wakeup_flag = 1;
Uart_SendString("[WR] Woken by RF (P3.6)!\r\n");
} else {
Uart_SendString("[WR] Woken by TIMER/Other\r\n");
}
// 5. 恢复屏幕供电与背光(低电平上电,恢复背光亮屏)
SGM_CTRL = 0;
// 6. 延时 200ms 等待液晶稳压板上电稳定后,重新对 LCD 寄存器初始化
Delay_ms(200);
LCD_Init();
// 7. 充零闲置计时器
inactivity_timer = 0;
} }
/** /**

7
CLAUDE.md Normal file
View File

@@ -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.

View File

@@ -3,108 +3,108 @@
#include "../App/config.h" #include "../App/config.h"
#include "intrins.h" #include "intrins.h"
// ====== LHS114TC-IF03 (ST7789V) <EFBFBD><EFBFBD>Ļ SPI <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӳ<EFBFBD><EFBFBD> ====== // ====== LHS114TC-IF03 (ST7789V) 屏幕 SPI 引脚映射 ======
sbit LCD_RST = P1^0; // <EFBFBD><EFBFBD>ĻӲ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> (RESET)<29><><EFBFBD>͵<EFBFBD>ƽ<EFBFBD><C6BD>λ sbit LCD_RST = P1^0; // 屏幕硬件复位引脚 (RESET),低电平复位
sbit LCD_DCX = P1^1; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> / <20><><EFBFBD><EFBFBD>ѡ<EFBFBD><D1A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (1:<3A><><EFBFBD><EFBFBD>, 0:ָ<EFBFBD><EFBFBD>) sbit LCD_DCX = P1^1; // 串行数据 / 命令选择控制线 (1:数据, 0:指令)
sbit LCD_SDI = P1^4; // SPI <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (MOSI) sbit LCD_SDI = P1^4; // SPI 主机输出从机输入数据线 (MOSI)
sbit LCD_SCL = P1^5; // SPI <EFBFBD><EFBFBD><EFBFBD>й<EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> (SCLK) sbit LCD_SCL = P1^5; // SPI 串行工作时钟线 (SCLK)
sbit LCD_CS = P1^6; // SPI <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƭѡѡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> (CS)<29><><EFBFBD>͵<EFBFBD>ƽѡͨ<D1A1><CDA8>Ļ sbit LCD_CS = P1^6; // SPI 物理片选选择端 (CS),低电平选通屏幕
// ====== LHS114TC-IF03 LEDA <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ====== // ====== LHS114TC-IF03 LEDA 背光控制引脚 ======
sbit SGM_CTRL = P1^7; // LEDA <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (0:<3A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, 1:<3A>ر<EFBFBD>Ϩ<EFBFBD><CFA8> - PMOS<EFBFBD><EFBFBD><EFBFBD><EFBFBD>) sbit SGM_CTRL = P1^7; // LEDA 开启引脚 (0:开启背光, 1:关闭熄灭 - PMOS控制)
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ⲿ<EFBFBD><EFBFBD>ʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> system.c <EFBFBD><EFBFBD>) // 引用外部延时函数 (定义在 system.c )
extern void Delay_ms(u16 ms); extern void Delay_ms(u16 ms);
extern void Delay10us(void); extern void Delay10us(void);
/** /**
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><EFBFBD> 8 λ<><CEBB><EFBFBD><EFBFBD> SPI <20>ֽ<EFBFBD><D6BD><EFBFBD><EFBFBD>ݷ<EFBFBD><DDB7><EFBFBD> * @brief 软件模拟 8 位串行 SPI 字节数据发送
* @param dat <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>͵<EFBFBD> 1 <20>ֽ<EFBFBD><D6BD><EFBFBD><EFBFBD><EFBFBD> * @param dat 待发送的 1 字节数据
*/ */
void SPI_SendData(u8 dat) void SPI_SendData(u8 dat)
{ {
u8 i; u8 i;
for(i = 0; i < 8; i++) for(i = 0; i < 8; i++)
{ {
LCD_SCL = 0; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD>ӣ<EFBFBD>׼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> LCD_SCL = 0; // 拉低时钟,准备更新数据
if(dat & 0x80) if(dat & 0x80)
LCD_SDI = 1; LCD_SDI = 1;
else else
LCD_SDI = 0; LCD_SDI = 0;
_nop_(); _nop_();
LCD_SCL = 1; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD>ӣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ز<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ LCD_SCL = 1; // 拉高时钟,触发屏幕在上升沿采样数据位
_nop_(); _nop_();
dat <<= 1; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD>һλ<EFBFBD><EFBFBD>׼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һλ dat <<= 1; // 左移一位,准备发送下一位
} }
} }
/** /**
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>д<EFBFBD><EFBFBD>ֽڿ<EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD> (DCX=0) * @brief 向屏幕控制器写入单字节控制指令 (DCX=0)
*/ */
void WriteComm(u8 cmd) void WriteComm(u8 cmd)
{ {
LCD_CS = 0; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƭѡ LCD_CS = 0; // 开启片选
LCD_DCX = 0; // <EFBFBD>л<EFBFBD>Ϊָ<EFBFBD><EFBFBD><EFBFBD>ģʽ LCD_DCX = 0; // 切换为指令传输模式
SPI_SendData(cmd); SPI_SendData(cmd);
LCD_CS = 1; // <EFBFBD>ر<EFBFBD>Ƭѡ LCD_CS = 1; // 关闭片选
} }
/** /**
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>д<EFBFBD><EFBFBD>ֽ<EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> (DCX=1) * @brief 向屏幕控制器写入单字节显示数据 (DCX=1)
*/ */
void WriteData(u8 dat) void WriteData(u8 dat)
{ {
LCD_CS = 0; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƭѡ LCD_CS = 0; // 开启片选
LCD_DCX = 1; // <EFBFBD>л<EFBFBD>Ϊ<EFBFBD><EFBFBD><EFBFBD>ݴ<EFBFBD><EFBFBD><EFBFBD>ģʽ LCD_DCX = 1; // 切换为数据传输模式
SPI_SendData(dat); SPI_SendData(dat);
LCD_CS = 1; // <EFBFBD>ر<EFBFBD>Ƭѡ LCD_CS = 1; // 关闭片选
} }
/** /**
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><EFBFBD>ִ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĻӲ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ * @brief 按照物理时序执行屏幕硬件复位
*/ */
void LCD_Reset(void) void LCD_Reset(void)
{ {
LCD_RST = 1; LCD_RST = 1;
Delay_ms(10); Delay_ms(10);
LCD_RST = 0; // <EFBFBD><EFBFBD><EFBFBD>͸<EFBFBD>λ<EFBFBD><EFBFBD><EFBFBD>Ž<EFBFBD><EFBFBD><EFBFBD>Ӳ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ״̬ LCD_RST = 0; // 拉低复位引脚进入硬件复位状态
Delay_ms(20); Delay_ms(20);
LCD_RST = 1; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> LCD_RST = 1; // 重新拉高引脚
Delay_ms(120); // <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 120ms <20><><EFBFBD><EFBFBD>ִ<EFBFBD>к<EFBFBD><D0BA><EFBFBD><EFBFBD>ij<EFBFBD>ʼ<EFBFBD><CABC> Delay_ms(120); // 必须等待至少 120ms 才能执行后续的初始化
} }
/** /**
* @brief <EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD> LHS114TC-IF03 (ST7789V) <EFBFBD><EFBFBD>Ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> * @brief 初始化 LHS114TC-IF03 (ST7789V) 屏幕控制器
*/ */
void LCD_Init(void) void LCD_Init(void)
{ {
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>չ SFR <20><><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8> (EAXFR = 1)<29><>ȷ<EFBFBD><C8B7><EFBFBD><EFBFBD><EFBFBD>Բ<EFBFBD><D4B2><EFBFBD> P1M1 <EFBFBD><EFBFBD> P1M0 ģʽ<EFBFBD>Ĵ<EFBFBD><EFBFBD><EFBFBD> // 开启扩展 SFR 访问权限 (EAXFR = 1),确保可以操作 P1M1 P1M0 模式寄存器
P_SW2 |= 0x80; P_SW2 |= 0x80;
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> P1 <20>˿ڵ<CBBF> LCD_RST(P1.0)/DCX(P1.1)/SDI(P1.4)/SCL(P1.5)/CS(P1.6)/LEDA(P1.7) Ϊ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģʽ (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)); 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)); P1M0 |= ((1<<0) | (1<<1) | (1<<4) | (1<<5) | (1<<6) | (1<<7));
// <EFBFBD><EFBFBD><EFBFBD>ø<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ų<EFBFBD>ʼĬ<EFBFBD>ϵ<EFBFBD>ƽ // 设置各控制引脚初始默认电平
SGM_CTRL = 0; // PMOS<EFBFBD>͵<EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (LEDA = 0) SGM_CTRL = 0; // PMOS低电平点亮背光 (LEDA = 0)
LCD_CS = 1; LCD_CS = 1;
LCD_SCL = 1; LCD_SCL = 1;
LCD_RST = 1; LCD_RST = 1;
LCD_DCX = 1; LCD_DCX = 1;
LCD_Reset(); // Ӳ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><EFBFBD>Ļ LCD_Reset(); // 硬件复位屏幕
// ====== д<EFBFBD><EFBFBD> ST7789V ԭ<EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ====== // ====== 写入 ST7789V 原厂寄存器初始化指令序列 ======
WriteComm(0x11); // Sleep Out <EFBFBD>˳<EFBFBD>˯<EFBFBD><EFBFBD> WriteComm(0x11); // Sleep Out 退出睡眠
Delay_ms(120); // <EFBFBD>˳<EFBFBD>˯<EFBFBD>ߺ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> 120ms Delay_ms(120); // 退出睡眠后必须延时至少 120ms
// Memory Data Access Control (MADCTL) ɨ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // Memory Data Access Control (MADCTL) 扫描方向控制
// 0x00: <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>£<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ң<EFBFBD>RGB <EFBFBD><EFBFBD>ʽ // 0x00: 从上至下,从左至右,RGB 格式
WriteComm(0x36); WriteComm(0x36);
WriteData(0x00); WriteData(0x00);
// Interface Pixel Format (COLMOD) // Interface Pixel Format (COLMOD)
// 0x05 <EFBFBD><EFBFBD><EFBFBD><EFBFBD> 16-bit/pixel (RGB565<EFBFBD><EFBFBD>ʽ) // 0x05 代表 16-bit/pixel (RGB565格式)
WriteComm(0x3A); WriteComm(0x3A);
WriteData(0x05); WriteData(0x05);
@@ -154,50 +154,50 @@ void LCD_Init(void)
WriteData(0xA4); WriteData(0xA4);
WriteData(0xA1); WriteData(0xA1);
// Positive Gamma Correction (E0h) <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>٤<EFBFBD><EFBFBD> // Positive Gamma Correction (E0h) 正极性伽马
WriteComm(0xE0); WriteComm(0xE0);
WriteData(0xD0); WriteData(0x04); WriteData(0x0D); WriteData(0x11); WriteData(0xD0); WriteData(0x04); WriteData(0x0D); WriteData(0x11);
WriteData(0x13); WriteData(0x2B); WriteData(0x3F); WriteData(0x54); WriteData(0x13); WriteData(0x2B); WriteData(0x3F); WriteData(0x54);
WriteData(0x4C); WriteData(0x18); WriteData(0x0D); WriteData(0x0B); WriteData(0x4C); WriteData(0x18); WriteData(0x0D); WriteData(0x0B);
WriteData(0x1F); WriteData(0x23); WriteData(0x1F); WriteData(0x23);
// Negative Gamma Correction (E1h) <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>٤<EFBFBD><EFBFBD> // Negative Gamma Correction (E1h) 负极性伽马
WriteComm(0xE1); WriteComm(0xE1);
WriteData(0xD0); WriteData(0x04); WriteData(0x0C); WriteData(0x11); WriteData(0xD0); WriteData(0x04); WriteData(0x0C); WriteData(0x11);
WriteData(0x13); WriteData(0x2C); WriteData(0x3F); WriteData(0x44); WriteData(0x13); WriteData(0x2C); WriteData(0x3F); WriteData(0x44);
WriteData(0x51); WriteData(0x2F); WriteData(0x1F); WriteData(0x1F); WriteData(0x51); WriteData(0x2F); WriteData(0x1F); WriteData(0x1F);
WriteData(0x20); WriteData(0x23); WriteData(0x20); WriteData(0x23);
// Display Inversion On (21h) <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԣ<EFBFBD>ST7789 IPS <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // Display Inversion On (21h) 开启反显,ST7789 IPS 屏常用
WriteComm(0x21); WriteComm(0x21);
// Display On (29h) <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ // Display On (29h) 开启显示
WriteComm(0x29); WriteComm(0x29);
Delay_ms(20); Delay_ms(20);
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֹ<EFBFBD><EFBFBD>Ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>˲<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 开启显示后立即进行全屏清屏,防止屏幕亮起瞬间呈现花屏随机噪点
LCD_Clear(COLOR_BLACK); LCD_Clear(COLOR_BLACK);
} }
/** /**
* @brief <EFBFBD><EFBFBD><EFBFBD>Ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݸ<EFBFBD><EFBFBD>»<EFBFBD><EFBFBD>ƴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> * @brief 设定屏幕的数据更新绘制窗口区域
*/ */
void LCD_SetWindow(u16 x_start, u16 x_end, u16 y_start, u16 y_end) void LCD_SetWindow(u16 x_start, u16 x_end, u16 y_start, u16 y_end)
{ {
// <EFBFBD>ڵײ㴰<EFBFBD><EFBFBD><EFBFBD><EFBFBD>У<EFBFBD>ͳһ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƫ<EFBFBD>õ<EFBFBD>͸<EFBFBD><EFBFBD>У<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϲ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 在底层窗口设定中,统一进行物理偏置的透明校正,免除上层各个绘图函数的手工计算
x_start += LCD_X_OFFSET; x_start += LCD_X_OFFSET;
x_end += LCD_X_OFFSET; x_end += LCD_X_OFFSET;
y_start += LCD_Y_OFFSET; y_start += LCD_Y_OFFSET;
y_end += LCD_Y_OFFSET; y_end += LCD_Y_OFFSET;
// д<EFBFBD><EFBFBD> Column Address Set <EFBFBD>е<EFBFBD>ַ<EFBFBD><EFBFBD><EFBFBD>üĴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (0x2A) // 写入 Column Address Set 列地址设置寄存器命令 (0x2A)
WriteComm(0x2A); WriteComm(0x2A);
WriteData((u8)(x_start >> 8)); WriteData((u8)(x_start >> 8));
WriteData((u8)(x_start & 0xFF)); WriteData((u8)(x_start & 0xFF));
WriteData((u8)(x_end >> 8)); WriteData((u8)(x_end >> 8));
WriteData((u8)(x_end & 0xFF)); WriteData((u8)(x_end & 0xFF));
// д<EFBFBD><EFBFBD> Row Address Set <EFBFBD>е<EFBFBD>ַ<EFBFBD><EFBFBD><EFBFBD>üĴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (0x2B) // 写入 Row Address Set 行地址设置寄存器命令 (0x2B)
WriteComm(0x2B); WriteComm(0x2B);
WriteData((u8)(y_start >> 8)); WriteData((u8)(y_start >> 8));
WriteData((u8)(y_start & 0xFF)); 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 <EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD>ɫ<EFBFBD><EFBFBD>ˢд<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȫ<EFBFBD><EFBFBD>Ļ * @brief 用指定色彩刷写清空全屏幕
* @param color 16λ RGB565 <EFBFBD><EFBFBD>ɫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ * @param color 16 RGB565 颜色填充值
*/ */
void LCD_Clear(u16 color) void LCD_Clear(u16 color)
{ {
u16 i, j; u16 i, j;
LCD_SetWindow(0, LCD_WIDTH - 1, 0, LCD_HEIGHT - 1); LCD_SetWindow(0, LCD_WIDTH - 1, 0, LCD_HEIGHT - 1);
WriteComm(0x2C); // <EFBFBD><EFBFBD><EFBFBD><EFBFBD> RAM Write (0x2C) <EFBFBD>Դ<EFBFBD>д<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> WriteComm(0x2C); // 发送 RAM Write (0x2C) 显存写入命令
for(i = 0; i < LCD_WIDTH; i++) for(i = 0; i < LCD_WIDTH; i++)
{ {
for(j = 0; j < LCD_HEIGHT; j++) for(j = 0; j < LCD_HEIGHT; j++)
{ {
WriteData((u8)(color >> 8)); // <EFBFBD><EFBFBD><EFBFBD><EFBFBD> RGB565 <EFBFBD><EFBFBD> 8 λ<EFBFBD>ֽ<EFBFBD> WriteData((u8)(color >> 8)); // 发送 RGB565 8 位字节
WriteData((u8)(color & 0xFF)); // <EFBFBD><EFBFBD><EFBFBD><EFBFBD> RGB565 <EFBFBD><EFBFBD> 8 λ<EFBFBD>ֽ<EFBFBD> WriteData((u8)(color & 0xFF)); // 发送 RGB565 8 位字节
} }
} }
} }
/** /**
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ļָ<EFBFBD><EFBFBD><EFBFBD>ӿڻ<EFBFBD><EFBFBD><EFBFBD>ʵ<EFBFBD>ľ<EFBFBD><EFBFBD><EFBFBD> * @brief 在屏幕指定视口绘制实心矩形
*/ */
void LCD_FillRect(u16 x, u16 y, u16 w, u16 h, u16 color) 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); u8 cl = (u8)(color & 0xFF);
LCD_SetWindow(x, x + w - 1, y, y + h - 1); LCD_SetWindow(x, x + w - 1, y, y + h - 1);
WriteComm(0x2C); // д<EFBFBD><EFBFBD><EFBFBD>Դ<EFBFBD> WriteComm(0x2C); // 写入显存
count = w * h; // ʵ<EFBFBD>ľ<EFBFBD><EFBFBD>ΰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> count = w * h; // 实心矩形包含的总像素数
for(i = 0; i < count; i++) for(i = 0; i < count; i++)
{ {
WriteData(ch); WriteData(ch);
@@ -244,55 +244,55 @@ void LCD_FillRect(u16 x, u16 y, u16 w, u16 h, u16 color)
} }
/** /**
* @brief <EFBFBD><EFBFBD><EFBFBD>ƿ<EFBFBD><EFBFBD>ľ<EFBFBD><EFBFBD>α߽<EFBFBD><EFBFBD><EFBFBD> * @brief 绘制空心矩形边界框
* @details ͨ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD> LCD_FillRect ʵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƾ<EFBFBD><EFBFBD>ε<EFBFBD><EFBFBD>ϡ<EFBFBD><EFBFBD>¡<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ߡ<EFBFBD> * @details 通过组合调用四次 LCD_FillRect 实心线来绘制矩形的上、下、左、右四条边。
*/ */
void LCD_DrawRectBorder(u16 x, u16 y, u16 w, u16 h, u16 color) void LCD_DrawRectBorder(u16 x, u16 y, u16 w, u16 h, u16 color)
{ {
LCD_FillRect(x, y, w, 1, color); // <EFBFBD>ϱ߿<EFBFBD><EFBFBD><EFBFBD> LCD_FillRect(x, y, w, 1, color); // 上边框线
LCD_FillRect(x, y + h - 1, w, 1, color); // <EFBFBD>±߿<EFBFBD><EFBFBD><EFBFBD> LCD_FillRect(x, y + h - 1, w, 1, color); // 下边框线
LCD_FillRect(x, y, 1, h, color); // <EFBFBD><EFBFBD><EFBFBD>߿<EFBFBD><EFBFBD><EFBFBD> LCD_FillRect(x, y, 1, h, color); // 左边框线
LCD_FillRect(x + w - 1, y, 1, h, color); // <EFBFBD>ұ߿<EFBFBD><EFBFBD><EFBFBD> LCD_FillRect(x + w - 1, y, 1, h, color); // 右边框线
} }
/** /**
* @brief <EFBFBD><EFBFBD>Ⱦ<EFBFBD><EFBFBD>ʾһ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD><EFBFBD>ģλͼ<EFBFBD><EFBFBD>Դ * @brief 渲染显示一个单色字模位图资源
* @param x <EFBFBD><EFBFBD>Ⱦ<EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> X * @param x 渲染起始坐标 X
* @param y <EFBFBD><EFBFBD>Ⱦ<EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> Y * @param y 渲染起始坐标 Y
* @param w λͼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> (<28><>λ: <20><><EFBFBD>ص<EFBFBD><D8B5><EFBFBD>) * @param w 位图宽度 (单位: 像素点数)
* @param h λͼ<EFBFBD>߶<EFBFBD> (<28><>λ: <20><><EFBFBD>ص<EFBFBD><D8B5><EFBFBD>) * @param h 位图高度 (单位: 像素点数)
* @param bmp FLASH <EFBFBD>еĵ<EFBFBD>ɫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD> (1<><31>ʾ<EFBFBD><CABE>ǰ<EFBFBD><C7B0>ɫ<EFBFBD><C9AB>0<EFBFBD><30>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ) * @param bmp FLASH 中的单色像素数组指针 (1表示画前景色0表示画背景色)
* @param color 16λǰ<EFBFBD><EFBFBD>ɫ * @param color 16位前景色
* @param bg_color 16λ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ * @param bg_color 16位背景色
*/ */
void LCD_DrawMonoBitmap(u16 x, u16 y, u16 w, u16 h, unsigned char code *bmp, u16 color, u16 bg_color) void LCD_DrawMonoBitmap(u16 x, u16 y, u16 w, u16 h, unsigned char code *bmp, u16 color, u16 bg_color)
{ {
u16 r, c; u16 r, c;
u16 byte_width = (w + 7) / 8; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڵ<EFBFBD>ɫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>У<EFBFBD>һ<EFBFBD><EFBFBD>ͼ<EFBFBD><EFBFBD>ռ<EFBFBD>õ<EFBFBD>ʵ<EFBFBD><EFBFBD><EFBFBD>ֽ<EFBFBD><EFBFBD><EFBFBD> u16 byte_width = (w + 7) / 8; // 计算在单色编码中,一行图像占用的实际字节数
u8 ch = (u8)(color >> 8); u8 ch = (u8)(color >> 8);
u8 cl = (u8)(color & 0xFF); u8 cl = (u8)(color & 0xFF);
u8 bgh = (u8)(bg_color >> 8); u8 bgh = (u8)(bg_color >> 8);
u8 bgl = (u8)(bg_color & 0xFF); u8 bgl = (u8)(bg_color & 0xFF);
LCD_SetWindow(x, x + w - 1, y, y + h - 1); // <EFBFBD><EFBFBD><EFBFBD>ƻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ洰<EFBFBD><EFBFBD> LCD_SetWindow(x, x + w - 1, y, y + h - 1); // 限制绘制显存窗口
WriteComm(0x2C); WriteComm(0x2C);
// Ƕ<EFBFBD><EFBFBD>ѭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӵ<EFBFBD> 0 <20>е<EFBFBD> 0 <20>п<EFBFBD>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>ÿ<EFBFBD><C3BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><CEBB> // 嵌套循环,从第 0 行第 0 列开始遍历每个像素位置
for(r = 0; r < h; r++) for(r = 0; r < h; r++)
{ {
for(c = 0; c < w; c++) for(c = 0; c < w; c++)
{ {
u16 byte_idx = r * byte_width + (c / 8); // <EFBFBD><EFBFBD>λ<EFBFBD><EFBFBD>ǰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڵ<EFBFBD>ɫͼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>е<EFBFBD><EFBFBD>ֽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> u16 byte_idx = r * byte_width + (c / 8); // 定位当前像素所在单色图数组中的字节索引
u8 bit_shift = 7 - (c % 8); // <EFBFBD><EFBFBD>λ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڸ<EFBFBD><EFBFBD>ֽ<EFBFBD><EFBFBD>ж<EFBFBD>Ӧ<EFBFBD><EFBFBD> bit λ<><CEBB> (<28><>λ<EFBFBD><CEBB>ǰ) u8 bit_shift = 7 - (c % 8); // 定位该像素在该字节中对应的 bit 位置 (高位在前)
if(bmp[byte_idx] & (1 << bit_shift)) if(bmp[byte_idx] & (1 << bit_shift))
{ {
WriteData(ch); // <EFBFBD><EFBFBD>λΪ 1<><31><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0>ɫ WriteData(ch); // 该位为 1发送前景色
WriteData(cl); WriteData(cl);
} }
else else
{ {
WriteData(bgh); // <EFBFBD><EFBFBD>λΪ 0<><30><EFBFBD><EFBFBD><EFBFBD>ͱ<EFBFBD><CDB1><EFBFBD>ɫ WriteData(bgh); // 该位为 0发送背景色
WriteData(bgl); WriteData(bgl);
} }
} }
@@ -300,30 +300,30 @@ void LCD_DrawMonoBitmap(u16 x, u16 y, u16 w, u16 h, unsigned char code *bmp, u16
} }
/** /**
* @brief <EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ⱦ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD> 8x16 <20>ߴ<EFBFBD><DFB4><EFBFBD><EFBFBD><EFBFBD>ͨӢ<CDA8><D3A2> ASCII <EFBFBD>ַ<EFBFBD> * @brief 在指定坐标渲染输出一个 8x16 尺寸的普通英文 ASCII 字符
* @param ch ASCII <EFBFBD>ַ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> * @param ch ASCII 字符本身
*/ */
void LCD_ShowChar8x16(u16 x, u16 y, char ch, u16 color, u16 bg_color) void LCD_ShowChar8x16(u16 x, u16 y, char ch, u16 color, u16 bg_color)
{ {
u8 r, c; u8 r, c;
u8 char_idx = ch - 0x20; // ƫ<EFBFBD>Ƽ<EFBFBD><EFBFBD>㣺ASCII <20>ɴ<EFBFBD>ӡ<EFBFBD>ַ<EFBFBD><D6B7><EFBFBD> 0x20 (<28>ո<EFBFBD>) <20><>ʼ u8 char_idx = ch - 0x20; // 偏移计算ASCII 可打印字符从 0x20 (空格) 开始
u8 f_h = (u8)(color >> 8); u8 f_h = (u8)(color >> 8);
u8 f_l = (u8)(color & 0xFF); u8 f_l = (u8)(color & 0xFF);
u8 b_h = (u8)(bg_color >> 8); u8 b_h = (u8)(bg_color >> 8);
u8 b_l = (u8)(bg_color & 0xFF); u8 b_l = (u8)(bg_color & 0xFF);
if(ch < 0x20 || ch > 0x7E) return; // <EFBFBD><EFBFBD><EFBFBD>˲<EFBFBD><EFBFBD>ɼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD> if(ch < 0x20 || ch > 0x7E) return; // 过滤不可见控制字符
LCD_SetWindow(x, x + 8 - 1, y, y + 16 - 1); LCD_SetWindow(x, x + 8 - 1, y, y + 16 - 1);
WriteComm(0x2C); WriteComm(0x2C);
// ѭ<EFBFBD><EFBFBD> 16 <EFBFBD>У<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 8x16 <EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 循环 16 行,绘制 8x16 字型
for(r = 0; r < 16; r++) for(r = 0; r < 16; r++)
{ {
u8 line = FONT_8x16[char_idx][r]; // <EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD>ǰ<EFBFBD>ж<EFBFBD>Ӧ<EFBFBD><EFBFBD> 8 λ<><CEBB>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ u8 line = FONT_8x16[char_idx][r]; // 提取当前行对应的 8 位单色像素信息
for(c = 0; c < 8; c++) for(c = 0; c < 8; c++)
{ {
// <EFBFBD>Ӹߵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>е<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ص<EFBFBD> // 从高到低依次提取单行中的像素点
if(line & (0x80 >> c)) if(line & (0x80 >> c))
{ {
WriteData(f_h); WriteData(f_h);
@@ -339,37 +339,37 @@ void LCD_ShowChar8x16(u16 x, u16 y, char ch, u16 color, u16 bg_color)
} }
/** /**
* @brief <EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӡһ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͨӢ<EFBFBD><EFBFBD> ASCII <20>ַ<EFBFBD><D6B7><EFBFBD> (<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>) * @brief 在指定坐标打印一行普通英文 ASCII 字符串 (左对齐)
*/ */
void LCD_ShowString(u16 x, u16 y, char *str, u16 color, u16 bg_color) void LCD_ShowString(u16 x, u16 y, char *str, u16 color, u16 bg_color)
{ {
while(*str) while(*str)
{ {
LCD_ShowChar8x16(x, y, *str, color, bg_color); // <EFBFBD><EFBFBD>Ⱦ<EFBFBD><EFBFBD>ǰ<EFBFBD>ַ<EFBFBD> LCD_ShowChar8x16(x, y, *str, color, bg_color); // 渲染当前字符
x += 8; // X <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼλ<EFBFBD><EFBFBD>ƫ<EFBFBD><EFBFBD> 8 <20><><EFBFBD>أ<EFBFBD>׼<EFBFBD><D7BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>ַ<EFBFBD> x += 8; // X 轴起始位置偏移 8 像素,准备绘制下一个字符
str++; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD> str++; // 移向下一个字符
} }
} }
/** /**
* @brief <EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD> Y <20><><EFBFBD>߶<EFBFBD><DFB6>ϣ<EFBFBD><CFA3>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD>ƫ<EFBFBD><C6AB><EFBFBD><EFBFBD>ʵ<EFBFBD><CAB5><EFBFBD>ַ<EFBFBD><D6B7><EFBFBD>ˮƽ<CBAE><C6BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ * @brief 在指定 Y 轴高度上,自动计算偏移以实现字符串水平居中显示
*/ */
void LCD_ShowStringCentered(u16 y, char *str, u16 color, u16 bg_color) void LCD_ShowStringCentered(u16 y, char *str, u16 color, u16 bg_color)
{ {
u16 len = 0; u16 len = 0;
char *p = str; char *p = str;
while(*p++) len++; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> while(*p++) len++; // 计算字符串长度
if(len * 8 >= LCD_WIDTH) if(len * 8 >= LCD_WIDTH)
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ļ<EFBFBD><EFBFBD>Ч<EFBFBD><EFBFBD><EFBFBD>ȣ<EFBFBD>ǿ<EFBFBD>ƴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 0 <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 若超出或等同于屏幕有效宽度,强制从最左侧 0 开始左对齐绘制
LCD_ShowString(0, y, str, color, bg_color); LCD_ShowString(0, y, str, color, bg_color);
else else
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͨ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ (LCD_WIDTH - <EFBFBD>ַ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>) / 2 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˮƽ<CBAE><C6BD><EFBFBD>е<EFBFBD><D0B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼƫ<CABC><C6AB> X <20><><EFBFBD><EFBFBD><EAB2A2>Ⱦ // 否则通过公式 (LCD_WIDTH - 字符宽度) / 2 计算出水平居中的首字起始偏移 X 坐标并渲染
LCD_ShowString((LCD_WIDTH - len * 8) / 2, y, str, color, bg_color); LCD_ShowString((LCD_WIDTH - len * 8) / 2, y, str, color, bg_color);
} }
/** /**
* @brief <EFBFBD><EFBFBD>Ⱦ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ش<EFBFBD> 16x32 <20>ߴ<EFBFBD><DFB4><EFBFBD>Ӣ<EFBFBD><D3A2>/<2F><><EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD> * @brief 渲染大号特粗 16x32 尺寸的英文/数字字符
*/ */
void LCD_ShowChar16x32(u16 x, u16 y, char ch, u16 color, u16 bg_color) 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; if(ch < 0x20 || ch > 0x7E) return;
LCD_SetWindow(x, x + 16 - 1, y, y + 32 - 1); // <EFBFBD><EFBFBD><EFBFBD><EFBFBD> 16x32 <EFBFBD>Ļ<EFBFBD>ͼ<EFBFBD>Ӵ<EFBFBD> LCD_SetWindow(x, x + 16 - 1, y, y + 32 - 1); // 锁定 16x32 的绘图视窗
WriteComm(0x2C); WriteComm(0x2C);
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>͵<EFBFBD> 16 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> data // 遍历字型的 16 个点阵行 data
for(r = 0; r < 16; r++) for(r = 0; r < 16; r++)
{ {
u8 line = FONT_8x16[char_idx][r]; u8 line = FONT_8x16[char_idx][r];
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><EFBFBD><EFBFBD>ظ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (i = 0 <EFBFBD><EFBFBD> i = 1) // 纵向插点翻倍:将当前行重复绘制两次 (i = 0 i = 1)
for(i = 0; i < 2; i++) for(i = 0; i < 2; i++)
{ {
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڵ<EFBFBD> 8 <EFBFBD><EFBFBD> bit <EFBFBD><EFBFBD><EFBFBD>ص<EFBFBD> // 遍历单行内的 8 bit 像素点
for(c = 0; c < 8; c++) for(c = 0; c < 8; c++)
{ {
val_bit = (line & (0x80 >> c)); val_bit = (line & (0x80 >> c));
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ص<EFBFBD><EFBFBD>ظ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>д<EFBFBD><EFBFBD> 2 <EFBFBD><EFBFBD> (j = 0 <EFBFBD><EFBFBD> j = 1) // 横向插点翻倍:单个像素点重复发送写入 2 (j = 0 j = 1)
for(j = 0; j < 2; j++) for(j = 0; j < 2; j++)
{ {
if(val_bit) if(val_bit)
@@ -418,31 +418,31 @@ void LCD_ShowChar16x32(u16 x, u16 y, char ch, u16 color, u16 bg_color)
} }
/** /**
* @brief <EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ش<EFBFBD> 16x32 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><><D3A2><EFBFBD>ִ<EFBFBD> * @brief 在指定坐标绘制特大 16x32 规格的数字/英文字串
*/ */
void LCD_ShowString16x32(u16 x, u16 y, char *str, u16 color, u16 bg_color) void LCD_ShowString16x32(u16 x, u16 y, char *str, u16 color, u16 bg_color)
{ {
while(*str) while(*str)
{ {
LCD_ShowChar16x32(x, y, *str, color, bg_color); LCD_ShowChar16x32(x, y, *str, color, bg_color);
x += 16; // ÿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƫ<EFBFBD><EFBFBD> 16 <20><><EFBFBD><EFBFBD> x += 16; // 每次起始坐标偏移 16 像素
str++; str++;
} }
} }
/** /**
* @brief <EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD> Y <20><><EFBFBD>߶<EFBFBD><DFB6>ϣ<EFBFBD>ˮƽ<CBAE><C6BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ⱦ<EFBFBD><C8BE>ʾ<EFBFBD>ش<EFBFBD> 16x32 <20><><EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD><D6B7><EFBFBD> * @brief 在指定 Y 轴高度上,水平居中渲染显示特大 16x32 规格字符串
*/ */
void LCD_ShowString16x32Centered(u16 y, char *str, u16 color, u16 bg_color) void LCD_ShowString16x32Centered(u16 y, char *str, u16 color, u16 bg_color)
{ {
u16 len = 0; u16 len = 0;
char *p = str; char *p = str;
while(*p++) len++; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> while(*p++) len++; // 计算字数
if(len * 16 >= LCD_WIDTH) if(len * 16 >= LCD_WIDTH)
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ļ<EFBFBD><EFBFBD><EFBFBD>ȣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 超出屏幕宽度,左对齐
LCD_ShowString16x32(0, y, str, color, bg_color); LCD_ShowString16x32(0, y, str, color, bg_color);
else else
// ˮƽ<EFBFBD><EFBFBD><EFBFBD>м<EFBFBD><EFBFBD><EFBFBD> // 水平居中计算
LCD_ShowString16x32((LCD_WIDTH - len * 16) / 2, y, str, color, bg_color); LCD_ShowString16x32((LCD_WIDTH - len * 16) / 2, y, str, color, bg_color);
} }

View File

@@ -3,15 +3,15 @@
#include "../App/rgb.h" #include "../App/rgb.h"
#include "intrins.h" #include "intrins.h"
// ATR5179 <EFBFBD><EFBFBD>Ƶǰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߿<EFBFBD><EFBFBD>ؿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŷ<EFBFBD><EFBFBD><EFBFBD> // ATR5179 射频前端天线开关控制引脚定义
sbit RF_TX = P2^0; // V1 <EFBFBD><EFBFBD><EFBFBD>ƶ<EFBFBD> (1: ѡͨ<D1A1><CDA8><EFBFBD>ߵ<EFBFBD><DFB5><EFBFBD><EFBFBD><EFBFBD> TX ·<EFBFBD><EFBFBD>) sbit RF_TX = P2^0; // V1 控制端 (1: 选通天线到发射 TX 路径)
sbit RF_RX = P3^7; // V2 <EFBFBD><EFBFBD><EFBFBD>ƶ<EFBFBD> (1: ѡͨ<D1A1><CDA8><EFBFBD>ߵ<EFBFBD><DFB5><EFBFBD><EFBFBD><EFBFBD> RX ·<EFBFBD><EFBFBD>) sbit RF_RX = P3^7; // V2 控制端 (1: 选通天线到接收 RX 路径)
// <EFBFBD><EFBFBD>ƵоƬ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŷ<EFBFBD><EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD><EFBFBD>°<EFBFBD> PCB <20><><EFBFBD>ŶԵ<C5B6><D4B5><EFBFBD><EFBFBD><EFBFBD>) // 射频芯片数据与电源控制引脚定义 (根据新版 PCB 引脚对调修正)
sbit RF_TX_DAT = P2^1; // LR690L <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (DIN) - <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Pin 22 (P2.1) sbit RF_TX_DAT = P2^1; // LR690L 发射调制数据输入引脚 (DIN) - 物理连接 Pin 22 (P2.1)
sbit RF_RX_DATA = P3^6; // LR690L <EFBFBD><EFBFBD><EFBFBD>ս<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (DATA) - <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Pin 19 (P3.6) sbit RF_RX_DATA = P3^6; // LR690L 接收解调数据输出引脚 (DATA) - 物理连接 Pin 19 (P3.6)
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ⲿ<EFBFBD><EFBFBD>ʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD>ӡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> (<28><> system.c <EFBFBD>ж<EFBFBD><EFBFBD><EFBFBD>) // 声明外部延时及串口打印函数 (在 system.c 中定义)
extern void Delay_us(u16 us); extern void Delay_us(u16 us);
extern void Delay_ms(u16 ms); extern void Delay_ms(u16 ms);
extern u16 GetTimer0_Safe(void); extern u16 GetTimer0_Safe(void);
@@ -24,156 +24,176 @@ extern void Uart_SendHex32(u32 val);
extern void Uart_SendByte(u8 dat); extern void Uart_SendByte(u8 dat);
/** /**
* @brief <EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƵоƬ<EFBFBD><EFBFBD><EFBFBD>ص<EFBFBD> GPIO <20><><EFBFBD>ŷ<EFBFBD><C5B7><EFBFBD><EFBFBD><EFBFBD>״̬ * @brief 初始化无线射频芯片相关的 GPIO 引脚方向与状态
*/ */
void RF_Init(void) void RF_Init(void)
{ {
// EAXFR = 1 <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>չ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܼĴ<EFBFBD><EFBFBD><EFBFBD> (XSFR) // EAXFR = 1 允许访问扩展特殊功能寄存器 (XSFR)
EAXFR = 1; EAXFR = 1;
// 1. <EFBFBD><EFBFBD><EFBFBD><EFBFBD> P2.0 (RF_TX) <EFBFBD><EFBFBD> P2.1 (RF_TX_DAT) Ϊ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģʽ (M1=0, M0=1) // 1. 配置 P2.0 (RF_TX) P2.1 (RF_TX_DAT) 为推挽输出模式 (M1=0, M0=1)
P2M1 &= ~((1 << 0) | (1 << 1)); P2M1 &= ~((1 << 0) | (1 << 1));
P2M0 |= ((1 << 0) | (1 << 1)); P2M0 |= ((1 << 0) | (1 << 1));
// 2. <EFBFBD><EFBFBD><EFBFBD><EFBFBD> P3.7 (RF_RX) <EFBFBD><EFBFBD> P3.5 (SHUT) Ϊ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģʽ (M1=0, M0=1) // 2. 配置 P3.7 (RF_RX) P3.5 (SHUT) 为推挽输出模式 (M1=0, M0=1)
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> P3.6 (RF_RX_DATA) Ϊ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģʽ (M1=1, M0=0)<EFBFBD><EFBFBD>ƥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ս<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 配置 P3.6 (RF_RX_DATA) 为高阻输入模式 (M1=1, M0=0)以匹配接收解调输入
P3M1 &= ~((1 << 5) | (1 << 7)); P3M1 &= ~((1 << 5) | (1 << 7));
P3M1 |= (1 << 6); P3M1 |= (1 << 6);
P3M0 &= ~(1 << 6); P3M0 &= ~(1 << 6);
P3M0 |= ((1 << 5) | (1 << 7)); P3M0 |= ((1 << 5) | (1 << 7));
// 3. <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϵͳĬ<EFBFBD>ϵij<EFBFBD>ʼ<EFBFBD><EFBFBD>ƽ // 3. 设置系统默认的初始电平
RF_TX = 0; // <EFBFBD>Ͽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͨ· RF_TX = 0; // 断开发射天线通路
SHUT = 1; // Ĭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> SHUT<EFBFBD><EFBFBD>ʹ LR690L <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߹ر<EFBFBD>״̬ SHUT = 1; // 默认拉高 SHUT,使 LR690L 处于休眠关闭状态
RF_RX = 0; // <EFBFBD>Ͽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͨ· RF_RX = 0; // 断开接收天线通路
RF_TX_DAT = 0; // <EFBFBD><EFBFBD><EFBFBD>Ʒ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>õ<EFBFBD> RF_TX_DAT = 0; // 调制发送数据线置低
} }
/** /**
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶ<EFBFBD>շ<EFBFBD>ϵͳ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģʽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>л<EFBFBD> * @brief 配置无线射频收发系统的运行模式与天线切换
* @param mode <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģʽ (0-<2D><><EFBFBD>߸<EFBFBD><DFB8><EFBFBD>, 1-<2D><><EFBFBD>չ<EFBFBD><D5B9><EFBFBD>, 2-<2D><><EFBFBD><EFBFBD><E4B9A4>) * @param mode 运行模式 (0-休眠隔离, 1-接收工作, 2-发射工作)
*/ */
void RF_SetMode(u8 mode) void RF_SetMode(u8 mode)
{ {
if (mode == 0) // ====== ģʽ 0: <EFBFBD><EFBFBD><EFBFBD>߸<EFBFBD><EFBFBD><EFBFBD>ģʽ (Sleep/Idle) ====== if (mode == 0) // ====== 模式 0: 休眠隔离模式 (Sleep/Idle) ======
{ {
SHUT = 1; // ǿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> SHUT <20><><EFBFBD>ţ<EFBFBD>ʹ LR690L <20><><EFBFBD>׽<EFBFBD><D7BD><EFBFBD><EFBFBD>͹<EFBFBD><CDB9>Ĵ<EFBFBD><C4B4><EFBFBD> SHUT = 1; // 强制拉高 SHUT 引脚,使 LR690L 彻底进入低功耗待机
RF_RX = 0; // <EFBFBD>رս<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߿<EFBFBD><EFBFBD>أ<EFBFBD><EFBFBD><EFBFBD>ֹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӳ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>յ<EFBFBD>· RF_RX = 0; // 关闭接收天线开关,防止外界电磁杂波串入接收电路
RF_TX = 0; // <EFBFBD>رշ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߿<EFBFBD><EFBFBD><EFBFBD> RF_TX = 0; // 关闭发射天线开关
} }
else if (mode == 1) // ====== ģʽ 1: <EFBFBD><EFBFBD><EFBFBD>չ<EFBFBD><EFBFBD><EFBFBD>ģʽ (RX Mode) ====== else if (mode == 1) // ====== 模式 1: 接收工作模式 (RX Mode) ======
{ {
SHUT = 0; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD> SHUT ʹ<EFBFBD><EFBFBD> LR690L оƬ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> SHUT = 0; // 拉低 SHUT 使能 LR690L 芯片,开始解调接收
RF_TX = 0; // <EFBFBD>رշ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> RF_TX = 0; // 关闭发射开关
RF_RX = 1; // ѡͨ<EFBFBD><EFBFBD><EFBFBD>ߵ<EFBFBD> RX <20><><EFBFBD><EFBFBD>·<EFBFBD><C2B7><EFBFBD><EFBFBD>ʹ<EFBFBD>ܽ<EFBFBD><DCBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>Ƶ<EFBFBD>Ŵ<EFBFBD> RF_RX = 1; // 选通天线到 RX 接收路径,使能接收链路高频放大
} }
else if (mode == 2) // ====== ģʽ 2: <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģʽ (TX Mode) ====== else if (mode == 2) // ====== 模式 2: 发发射工作模式 (TX Mode) ======
{ {
SHUT = 1; // <EFBFBD><EFBFBD><EFBFBD>ջ<EFBFBD>ͣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǿ<EFBFBD>źŶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><EFBFBD> SHUT = 1; // 接收机停机,防止发射强信号顶坏接收前端
RF_RX = 0; // <EFBFBD>رս<EFBFBD><EFBFBD>տ<EFBFBD><EFBFBD><EFBFBD> RF_RX = 0; // 关闭接收开关
RF_TX = 1; // ѡͨ<EFBFBD><EFBFBD><EFBFBD>ߵ<EFBFBD> TX <20><><EFBFBD><EFBFBD>·<EFBFBD><C2B7><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9><EFBFBD><EFBFBD><EFBFBD>߷<EFBFBD><DFB7><EFBFBD> RF_TX = 1; // 选通天线到 TX 发射路径,使能天线发射
} }
} }
/** /**
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD> EV1527 Э<><EFBFBD>ε<EFBFBD><CEB5>Ʒ<EFBFBD><C6B7><EFBFBD>һ֡ 24 λ<><CEBB>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD> (Sync + 24bit Data) * @brief 使用单片机硬件 Timer0 实现高精度发射电平时延控制
* @param us 延时微秒数
* @note STC32G 在 24MHz 晶振 12T 模式下Timer0 的 1 tick 正好等于 0.5us (2MHz)
*/
void RF_Delay_us(u16 us)
{
u16 ticks = us * 2;
TL0 = 0;
TH0 = 0;
TR0 = 1; // 启动定时器 0
while (GetTimer0_Safe() < ticks);
TR0 = 0; // 停止定时器 0
}
/**
* @brief 按照 EV1527 协议单次调制发送一帧 24 位射频数据 (Sync + 24bit Data)
*/ */
void EV1527_TxFrame(u32 addr, u8 dat) void EV1527_TxFrame(u32 addr, u8 dat)
{ {
u8 i; u8 i;
u32 tx_val; u32 tx_val;
// <EFBFBD>ϲ<EFBFBD> 20 λ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD><EFBFBD> 4 λ<><CEBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EBA3AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 24 λ<>ز<EFBFBD><D8B2><EFBFBD> // 合并 20 位地址与 4 位数据,成为 24 位完整 EV1527 帧
tx_val = (addr << 4) | (dat & 0x0F); tx_val = (addr << 4) | (dat & 0x0F);
// 1. ͬ<EFBFBD><EFBFBD>ͷ<EFBFBD><EFBFBD><EFBFBD>ͣ<EFBFBD><EFBFBD>ߵ<EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1T (Լ 350us)<EFBFBD><EFBFBD><EFBFBD>͵<EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> 31T (Լ 10850us) // 1. 同步头脉冲: 高电平 1T (350us) + 低电平 31T (10850us)
RF_TX_DAT = 1; RF_TX_DAT = 1;
Delay_us(350); RF_Delay_us(350);
RF_TX_DAT = 0; RF_TX_DAT = 0;
Delay_us(10850); RF_Delay_us(10850);
// 2. <EFBFBD><EFBFBD><EFBFBD>ε<EFBFBD><EFBFBD>Ʒ<EFBFBD><EFBFBD><EFBFBD> 24 λ<><CEBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ // 2. 依次发送 24 个数据位 (MSB First)
for (i = 0; i < 24; i++) for (i = 0; i < 24; i++)
{ {
if (tx_val & (0x800000UL >> i)) if (tx_val & (0x800000UL >> i))
{ {
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> "1"<22><><EFBFBD>ߵ<EFBFBD>ƽ<EFBFBD><C6BD><EFBFBD><EFBFBD> 3T (1050us)<EFBFBD><EFBFBD><EFBFBD>͵<EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1T (350us) // 逻辑 "1": 高电平 3T (1050us) + 低电平 1T (350us)
RF_TX_DAT = 1; RF_TX_DAT = 1;
Delay_us(1050); RF_Delay_us(1050);
RF_TX_DAT = 0; RF_TX_DAT = 0;
Delay_us(350); RF_Delay_us(350);
} }
else else
{ {
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> "0"<22><><EFBFBD>ߵ<EFBFBD>ƽ<EFBFBD><C6BD><EFBFBD><EFBFBD> 1T (350us)<EFBFBD><EFBFBD><EFBFBD>͵<EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> 3T (1050us) // 逻辑 "0": 高电平 1T (350us) + 低电平 3T (1050us)
RF_TX_DAT = 1; RF_TX_DAT = 1;
Delay_us(350); RF_Delay_us(350);
RF_TX_DAT = 0; RF_TX_DAT = 0;
Delay_us(1050); RF_Delay_us(1050);
} }
} }
} }
/** /**
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>η<EFBFBD><EFBFBD><EFBFBD> EV1527 <20><>Ƶ֡<C6B5><D6A1><EFBFBD><EFBFBD>ֹ<EFBFBD><D6B9>֡<EFBFBD><D6A1>ʧ * @brief 调制发射 25 帧 EV1527 射频同步电平信号以确保接收侧可靠触发
*/ */
void EV1527_Transmit(u32 addr, u8 dat) void EV1527_Transmit(u32 addr, u8 dat)
{ {
u8 i; u8 r;
RF_SetMode(2); // <EFBFBD>л<EFBFBD>Ϊ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>״̬<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͨ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ز<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ն<EFBFBD> bit ea_bak = EA; // 备份并屏蔽全局中断,确保发射高低电平周期的绝对纯净与防抖
Delay_ms(5); // <20>ȶ<EFBFBD><C8B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶͨ· EA = 0;
// ѭ<><D1AD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 10 ֡<><D6A1>ȷ<EFBFBD><C8B7><EFBFBD><EFBFBD><EFBFBD>ն<EFBFBD><D5B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1 ֡<><D6A1><EFBFBD>޲<EFBFBD><DEB2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> RF_SetMode(2); // 切换为发射状态,接通天线与射频模块通道
for (i = 0; i < 10; i++) Delay_ms(5); // 稍作延时使射频通路稳定
// 连续发射 25 帧以保证接收设备顺利锁定并解码
for (r = 0; r < 25; r++)
{ {
EV1527_TxFrame(addr, dat); EV1527_TxFrame(addr, dat);
Delay_ms(10); // ֡<><EFBFBD><E4B1A3><EFBFBD><EFBFBD>ʱ // 去除帧与帧之间可能导致波形不连续的 Delay_ms
} }
RF_TX_DAT = 0; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> RF_TX_DAT = 0; // 结束发射,拉低数据脚
RF_SetMode(1); // <EFBFBD><EFBFBD><EFBFBD>»ָ<EFBFBD>Ϊ<EFBFBD><EFBFBD><EFBFBD>ռ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̬<EFBFBD><EFBFBD><EFBFBD>Ա<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> RF_SetMode(1); // 恢复为常态后台接收模式
EA = ea_bak; // 还原全局中断开关状态
} }
/** /**
* @brief <EFBFBD><EFBFBD>ȡ<EFBFBD>ض<EFBFBD><EFBFBD><EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>״̬<EFBFBD>ij<EFBFBD><EFBFBD><EFBFBD>΢<EFBFBD><EFBFBD>ʱ<EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD> Timer0 Ӳ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>) * @brief 获取特定电平引脚状态的持续微秒时间 (利用 Timer0 硬件测量)
*/ */
u16 GetPulseDuration(u8 state, u16 timeout_us) u16 GetPulseDuration(u8 state, u16 timeout_us)
{ {
u16 timeout_ticks; u16 timeout_ticks;
// ǿ<EFBFBD>ƹر<EFBFBD> Timer0 <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ<EFBFBD><EFBFBD><EFBFBD>㸴λ // 强制关闭 Timer0 计数器,并将其计数值清零复位
TR0 = 0; TR0 = 0;
TMOD &= 0xF0; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD> Timer0 <EFBFBD>Ĵ<EFBFBD><EFBFBD><EFBFBD>Ϊģʽ 0 (16λ<36>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD>) TMOD &= 0xF0; // 设置 Timer0 寄存器为模式 0 (16位自动重载)
TL0 = 0x00; TL0 = 0x00;
TH0 = 0x00; TH0 = 0x00;
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> 16 λ<><CEBB>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ticks <20><>ʱֵ (<28><> 1T ģʽ 24MHz <EFBFBD>£<EFBFBD>1 Tick = 1 / 24M = 0.0416us) // 换算 16 位定时器所需的最大 ticks 超时值 (在 1T 模式 24MHz 下,1 Tick = 1 / 24M = 0.0416us)
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϵͳ Timer0 Ĭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ 12T ģʽ (1 Tick = 12 / 24MHz = 0.5us) // 但此系统 Timer0 默认配置为 12T 模式 (1 Tick = 12 / 24MHz = 0.5us)
timeout_ticks = (u16)((u32)timeout_us * (MAIN_Fosc / 1000000UL) / 12UL); timeout_ticks = (u16)((u32)timeout_us * (MAIN_Fosc / 1000000UL) / 12UL);
TR0 = 1; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD> Timer0 Ӳ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> TR0 = 1; // 启动 Timer0 硬件计数器
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD>ƽ״̬<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>жϣ<EFBFBD>ֱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ת<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ // 根据要捕获的电平状态进行阻塞判断,直到电平发生翻转或计数超时
while (RF_RX_DATA == state) while (RF_RX_DATA == state)
{ {
if (GetTimer0_Safe() > timeout_ticks) if (GetTimer0_Safe() > timeout_ticks)
{ {
TR0 = 0; TR0 = 0;
return 0; // <EFBFBD>ж<EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 0 return 0; // 判定超时,返回 0
} }
} }
TR0 = 0; // <EFBFBD>ض϶<EFBFBD>ʱ<EFBFBD><EFBFBD> TR0 = 0; // 关断定时器
return (u16)((u32)GetTimer0_Safe() * 12UL / (MAIN_Fosc / 1000000UL)); return (u16)((u32)GetTimer0_Safe() * 12UL / (MAIN_Fosc / 1000000UL));
} }
// ģ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶ<EFBFBD>ź<EFBFBD>ȫ<EFBFBD>ֱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 模拟射频信号全局变量定义
volatile u32 mock_rx_addr = 0; volatile u32 mock_rx_addr = 0;
volatile u8 mock_rx_data = 0; volatile u8 mock_rx_data = 0;
volatile bit mock_rf_ready = 0; volatile bit mock_rf_ready = 0;
/** /**
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD><EFBFBD>Ϸ<EFBFBD><EFBFBD><EFBFBD> EV1527 <20><>Ƶ<EFBFBD>ź<EFBFBD><C5BA><EFBFBD><EFBFBD><EFBFBD>֡ * @brief 阻塞式检测并解码一个合法的 EV1527 射频信号数据帧
*/ */
bit EV1527_Decode(u32 *out_addr, u8 *out_data) bit EV1527_Decode(u32 *out_addr, u8 *out_data)
{ {
@@ -185,7 +205,7 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data)
u16 idata raw_h[24]; u16 idata raw_h[24];
u16 idata raw_l[24]; u16 idata raw_l[24];
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 检查模拟射频触发
if (mock_rf_ready) { if (mock_rf_ready) {
mock_rf_ready = 0; mock_rf_ready = 0;
*out_addr = mock_rx_addr; *out_addr = mock_rx_addr;
@@ -193,14 +213,14 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data)
return 1; return 1;
} }
// ====== 1. ͬ<EFBFBD><EFBFBD>ͷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ====== // ====== 1. 同步头引导脉冲捕获 ======
{ {
u16 wait_cnt = 0; u16 wait_cnt = 0;
while (RF_RX_DATA == 1) while (RF_RX_DATA == 1)
{ {
Delay_us(5); Delay_us(5);
wait_cnt++; wait_cnt++;
if (wait_cnt > 2000) // 10 <EFBFBD><EFBFBD><EFBFBD>볬ʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> if (wait_cnt > 2000) // 10 毫秒超时保护
return 0; return 0;
} }
@@ -209,28 +229,28 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data)
{ {
Delay_us(5); Delay_us(5);
wait_cnt++; wait_cnt++;
if (wait_cnt > 4000) // 20 <EFBFBD><EFBFBD><EFBFBD>볬ʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> if (wait_cnt > 4000) // 20 毫秒超时保护
return 0; return 0;
} }
} }
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͷ<EFBFBD>ߵ<EFBFBD>ƽʱ<EFBFBD><EFBFBD> // 捕获引导头高电平时间
high_time = GetPulseDuration(1, 2500); high_time = GetPulseDuration(1, 2500);
if (high_time < 50 || high_time > 2500) { if (high_time < 50 || high_time > 2500) {
return 0; return 0;
} }
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 31T <20>͵<EFBFBD>ƽʱ<C6BD><CAB1> // 捕获引导头后续的 31T 低电平时间
low_time = GetPulseDuration(0, 60000); low_time = GetPulseDuration(0, 60000);
if (low_time < 1500 || low_time > 60000) { if (low_time < 1500 || low_time > 60000) {
return 0; return 0;
} }
// ͬ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ļ<EFBFBD>׼ʱ<EFBFBD><EFBFBD> T <20><><EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ߵ<EFBFBD>ƽ<EFBFBD><C6BD><EFBFBD>ȼ<EFBFBD>Ϊ 1T) // 同步引导脉冲的基准时间 T 计算 (引导脉冲高电平宽度即为 1T)
T = high_time; T = high_time;
if (T < 100 || T > 800) return 0; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD>Χ<EFBFBD>޷<EFBFBD>: 100us ~ 800us if (T < 100 || T > 800) return 0; // 自适应范围限幅: 100us ~ 800us
// ====== 2. <EFBFBD><EFBFBD><EFBFBD>ν<EFBFBD><EFBFBD><EFBFBD> 24 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><CEBB><EFBFBD><EFBFBD> ====== // ====== 2. 依次解析 24 个数据位脉宽 ======
for (i = 0; i < 24; i++) for (i = 0; i < 24; i++)
{ {
raw_h[i] = GetPulseDuration(1, 2000); raw_h[i] = GetPulseDuration(1, 2000);
@@ -240,15 +260,15 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data)
if (raw_l[i] == 0) return 0; if (raw_l[i] == 0) return 0;
} }
// ====== 3. ˫<EFBFBD>˱<EFBFBD>ֵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ж<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ====== // ====== 3. 双端比值解调判定数据内容 ======
for (i = 0; i < 24; i++) for (i = 0; i < 24; i++)
{ {
u16 total = raw_h[i] + raw_l[i]; u16 total = raw_h[i] + raw_l[i];
if (total < T * 3 || total > T * 6) return 0; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><EFBFBD><EFBFBD>ڱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 3T~6T ֮<EFBFBD><EFBFBD> if (total < T * 3 || total > T * 6) return 0; // 单个数据位周期必须在 3T~6T 之间
if (raw_h[i] > raw_l[i] * 2) if (raw_h[i] > raw_l[i] * 2)
{ {
// <EFBFBD>ߵ<EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD>Գ<EFBFBD><EFBFBD>ڵ͵<EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߼<EFBFBD> "1" // 高电平明显长于低电平,代表逻辑 "1"
if (i < 20) if (i < 20)
addr = (addr << 1) | 1; addr = (addr << 1) | 1;
else else
@@ -256,7 +276,7 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data)
} }
else if (raw_l[i] > raw_h[i] * 2) else if (raw_l[i] > raw_h[i] * 2)
{ {
// <EFBFBD>͵<EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD>Գ<EFBFBD><EFBFBD>ڸߵ<EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߼<EFBFBD> "0" // 低电平明显长于高电平,代表逻辑 "0"
if (i < 20) if (i < 20)
addr = (addr << 1); addr = (addr << 1);
else else
@@ -264,18 +284,18 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data)
} }
else else
{ {
return 0; // <EFBFBD><EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ<EFBFBD><EFBFBD><EFBFBD>Ϲ<EFBFBD>ж<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֡<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> return 0; // 电平脉冲比值不合规,判定数据帧畸变,解码作废
} }
} }
// У<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 校验无误,输出解调数据
*out_addr = addr; *out_addr = addr;
*out_data = dat; *out_data = dat;
return 1; return 1;
} }
/** /**
* @brief RF <EFBFBD>շ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϲ<EFBFBD><EFBFBD>Թ<EFBFBD><EFBFBD><EFBFBD><>ַ<EFBFBD><D6B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ռ<EFBFBD><D5BC><EFBFBD><EFBFBD><EFBFBD>ӡ) * @brief RF 收发诊断测试功能 (支持发送与接收监听打印)
*/ */
void RF_DiagnosticMode(char choice) void RF_DiagnosticMode(char choice)
{ {
@@ -283,23 +303,23 @@ void RF_DiagnosticMode(char choice)
{ {
u8 i; u8 i;
Uart_SendString("=== RF TX Test: Transmitting 20 test frames... ===\r\n"); Uart_SendString("=== RF TX Test: Transmitting 20 test frames... ===\r\n");
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģʽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߿<EFBFBD><EFBFBD><EFBFBD> // 开启发射模式并配置天线开关
RF_SetMode(2); RF_SetMode(2);
Delay_ms(5); Delay_ms(5);
for (i = 0; i < 20; i++) for (i = 0; i < 20; i++)
{ {
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ͳ<EFBFBD><EFBFBD>԰<EFBFBD><EFBFBD><EFBFBD>ÿ<EFBFBD>η<EFBFBD><EFBFBD>Ͱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ָʾ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>˸ // 连续发送测试包,每次发送伴随马达短震及指示灯闪烁
MOTOR = 1; MOTOR = 1;
RGB_Send(0, 150, 0, 0, 150, 0); // <EFBFBD>̹<EFBFBD><EFBFBD><EFBFBD>˸ RGB_Send(0, 150, 0, 0, 150, 0); // 绿光闪烁
EV1527_TxFrame(0x37A86UL, 0x01); EV1527_TxFrame(0x37A86UL, 0x01);
MOTOR = 0; MOTOR = 0;
RGB_Send(0, 0, 0, 0, 0, 0); RGB_Send(0, 0, 0, 0, 0, 0);
Delay_ms(150); // ֡<EFBFBD><EFBFBD><EFBFBD><EFBFBD> Delay_ms(150); // 帧间隔
} }
RF_TX_DAT = 0; RF_TX_DAT = 0;
RF_SetMode(1); // <EFBFBD><EFBFBD><EFBFBD>»ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģʽ RF_SetMode(1); // 重新恢复接收模式
Uart_SendString("[RF] Transmit Done!\r\n"); Uart_SendString("[RF] Transmit Done!\r\n");
} }
else if (choice == 'r') else if (choice == 'r')
@@ -309,10 +329,10 @@ void RF_DiagnosticMode(char choice)
u8 rx_data; u8 rx_data;
Uart_SendString("=== RF RX Test: Listening for 5 seconds... ===\r\n"); Uart_SendString("=== RF RX Test: Listening for 5 seconds... ===\r\n");
RF_SetMode(1); // <EFBFBD><EFBFBD><EFBFBD>ý<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģʽ<EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> RF_SetMode(1); // 配置进入接收模式并使能天线
Delay_ms(10); Delay_ms(10);
// 5<EFBFBD><EFBFBD><EFBFBD>ķ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 5秒的非阻塞监听
for (wait_ms = 0; wait_ms < 5000; wait_ms++) for (wait_ms = 0; wait_ms < 5000; wait_ms++)
{ {
if (EV1527_Decode(&rx_addr, &rx_data)) if (EV1527_Decode(&rx_addr, &rx_data))
@@ -323,7 +343,7 @@ void RF_DiagnosticMode(char choice)
Uart_SendHex8(rx_data); Uart_SendHex8(rx_data);
Uart_SendString("\r\n"); Uart_SendString("\r\n");
// <EFBFBD>յ<EFBFBD><EFBFBD>źź<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>𶯲<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> // 收到信号后马达震动并亮青色灯提醒
MOTOR = 1; MOTOR = 1;
RGB_Send(0, 150, 150, 0, 150, 150); RGB_Send(0, 150, 150, 0, 150, 150);
Delay_ms(100); Delay_ms(100);
@@ -335,3 +355,46 @@ void RF_DiagnosticMode(char choice)
Uart_SendString("=== RF RX Test End ===\r\n"); Uart_SendString("=== RF RX Test End ===\r\n");
} }
} }
/**
* @brief 电气回环自检测试 (物理短路 P2.1 与 P3.6 自检)
* @details 用户通过短路 P2.1 和 P3.6 来实现 GPIO 的物理连通性自检
*/
void Loopback_Test(void)
{
u16 match_count = 0;
u16 i;
EAXFR = 1;
RF_SetMode(1); // 开启接收芯片
Delay_ms(100);
Uart_SendString("Starting RF Hardware Loopback Test (P2.1 -> P3.6)...\r\n");
Uart_SendString("Please use a metal tweezers to short-circuit P2.1 (Pin 22) and P3.6 (Pin 19)!\r\n");
for (i = 0; i < 100; i++)
{
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! Pin P2.1 and P3.6 are electrical connected successfully!\r\n");
}
else
{
Uart_SendString("Result: FAIL! GPIO coupling check failed. Short-circuit the pins and test again.\r\n");
}
RF_SetMode(0); // 关断射频
}

View File

@@ -61,4 +61,9 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data);
*/ */
void RF_DiagnosticMode(char choice); void RF_DiagnosticMode(char choice);
/**
* @brief 电气回环自检测试 (物理短路 P2.1 与 P3.6 自检)
*/
void Loopback_Test(void);
#endif // __RF_H__ #endif // __RF_H__