Compare commits
11 Commits
47bcd252b2
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8df5d49d3 | ||
| 41bb78aa59 | |||
| be397fba76 | |||
| 58dfe5bb9c | |||
| a766ada53d | |||
| 48db6523fb | |||
| 2f4dd2660d | |||
| 0a79e7a3b9 | |||
| d7cb2ae37f | |||
| 8367524eec | |||
| d01121e3ae |
@@ -7,6 +7,7 @@
|
||||
#include "system.h"
|
||||
#include "../Drivers/rf.h"
|
||||
#include "../Drivers/lcd.h"
|
||||
#include "../Drivers/pcf8563.h"
|
||||
|
||||
// 外部引用的全局系统状态变量
|
||||
extern SystemState current_state;
|
||||
@@ -64,7 +65,17 @@ static void ClockApp_OnStart(void)
|
||||
time_edit_active = 0;
|
||||
current_state = STATE_NORMAL; // 系统切入正常待机状态
|
||||
RF_SetMode(1); // 开启天线并置为接收模式,监听无线探测器
|
||||
UI_ShowClockPage(current_state, current_hour, current_min); // 渲染时钟主页
|
||||
|
||||
// 尝试从 PCF8563 RTC 芯片中读取硬件时间 (具备非阻塞防卡死保护)
|
||||
if (PCF8563_Init()) {
|
||||
PCF8563_ReadTime(¤t_hour, ¤t_min, ¤t_sec);
|
||||
XTELL_LOG("[ClockApp] RTC Hardware Synced OK!\r\n");
|
||||
} else {
|
||||
XTELL_LOG("[ClockApp] RTC Hardware Offline, using system clock.\r\n");
|
||||
}
|
||||
|
||||
// 绘制并渲染待机时钟主页面画面
|
||||
UI_ShowClockPage(current_state, current_hour, current_min);
|
||||
|
||||
// 关断/熄灭 FCOB 双灯条
|
||||
RGB_Send(0, 0, 0, 0, 0, 0);
|
||||
@@ -156,6 +167,9 @@ static EventResult ClockApp_onEvent(SystemEvent *evt)
|
||||
current_state = STATE_NORMAL;
|
||||
RGB_Send(0, 0, 0, 0, 0, 0); // 熄灭蓝色指示灯
|
||||
|
||||
// 同步将最新时间 (hour, min, 0秒) 写入 PCF8563 芯片寄存器
|
||||
PCF8563_WriteTime(current_hour, current_min, 0);
|
||||
|
||||
// 退出短振反馈
|
||||
MOTOR = 1; Delay_ms(50); MOTOR = 0;
|
||||
|
||||
|
||||
18
App/config.h
18
App/config.h
@@ -9,6 +9,11 @@ typedef unsigned char u8;
|
||||
typedef unsigned int u16;
|
||||
typedef unsigned long u32;
|
||||
|
||||
// STC32G 端口内部上拉控制寄存器地址定义 (属于扩展 XSFR 空间)
|
||||
#ifndef P3PU
|
||||
#define P3PU (*(unsigned char volatile xdata *)0xFE13)
|
||||
#endif
|
||||
|
||||
/* ====== 硬件引脚定义 (sbit) ====== */
|
||||
// 振动马达控制端 (推挽输出,与 SPI SCLK 物理共享引脚)
|
||||
sbit MOTOR = P2^5;
|
||||
@@ -18,6 +23,11 @@ sbit KEY_UP = P0^1;
|
||||
sbit KEY_CONFIRM = P0^2;
|
||||
// 侧边按键 ▼ (高阻输入 + 上拉),按下为低电平
|
||||
sbit KEY_DOWN = P0^3;
|
||||
// 电源保持控制引脚 (1:锁存保持电源, 0:切断电源关机)
|
||||
#define PWR_HOLD P00
|
||||
// PCF8563 RTC 实时时钟芯片 I2C 通信引脚
|
||||
#define RTC_SCL P32
|
||||
#define RTC_SDA P33
|
||||
// 物理 SOS 求救按键 (高阻输入 + 上拉),按下为低电平
|
||||
sbit KEY_SOS = P2^6;
|
||||
// FCOB 幻彩灯条 DIN 数据控制线
|
||||
@@ -37,6 +47,11 @@ sbit LCD_SCL = P1^5;
|
||||
// LHS114TC-IF03 屏幕 SPI 片选脚 (CS),低电平有效
|
||||
sbit LCD_CS = P1^6;
|
||||
|
||||
// 充电检测引脚 (TP4054 CHRG 开漏输出,低电平=充电中)
|
||||
// 硬件连接:TP4054 CHRG → DET → MCU (根据原理图,需 PCB 确认实际引脚)
|
||||
// 架构文档标注为 P5.4,原理图标注为 P1.7 附近,暂用 P5.4,可根据实测调整
|
||||
sbit CHRG_DET = P5^4;
|
||||
|
||||
/* ====== 系统运行状态枚举 ====== */
|
||||
typedef enum {
|
||||
STATE_NORMAL, // 正常时钟待机状态
|
||||
@@ -65,7 +80,8 @@ typedef enum {
|
||||
KEY_EVENT_SOS_LONG, // SOS 键长按事件
|
||||
KEY_EVENT_UP_DOWN_COMB, // ▲ 与 ▼ 双键组合按下事件
|
||||
KEY_EVENT_CONFIRM_CLICK, // ■ 确认键短按事件 (保存设置或进入子菜单)
|
||||
KEY_EVENT_CONFIRM_LONG // ■ 确认键长按事件 (打开主菜单)
|
||||
KEY_EVENT_CONFIRM_LONG, // ■ 确认键长按事件 (打开主菜单)
|
||||
KEY_EVENT_POWER_OFF // 长按 KEY_DOWN 开关机触发事件
|
||||
} KeyEvent;
|
||||
|
||||
/* ====== 事件优先级枚举 ====== */
|
||||
|
||||
88
App/event.c
88
App/event.c
@@ -4,12 +4,13 @@
|
||||
#include "system.h"
|
||||
#include "clock.h"
|
||||
#include "xtell.h"
|
||||
#include "../Drivers/adc.h"
|
||||
|
||||
// 定义事件环形队列的最大深度为 8
|
||||
#define EVENT_QUEUE_DEPTH 8
|
||||
|
||||
// 环形队列存储数组 (存放在单片机 idata 快速RAM区中)
|
||||
static SystemEvent idata event_queue[EVENT_QUEUE_DEPTH];
|
||||
// 环形队列存储数组 (存放在单片机 xdata 拓展 RAM区中)
|
||||
static SystemEvent xdata event_queue[EVENT_QUEUE_DEPTH];
|
||||
static u8 queue_head = 0; // 队首读指针
|
||||
static u8 queue_tail = 0; // 队尾写指针
|
||||
static u8 queue_count = 0; // 当前队列内有效事件总数
|
||||
@@ -66,6 +67,7 @@ SystemEvent EventQueue_Pop(void)
|
||||
SystemEvent evt;
|
||||
// 默认空事件属性
|
||||
evt.key_event = KEY_EVENT_NONE;
|
||||
evt.priority = EVENT_PRIORITY_NORMAL;
|
||||
evt.extra_data = 0;
|
||||
evt.extra_size = 0;
|
||||
|
||||
@@ -130,6 +132,12 @@ void Event_Dispatcher_Loop(void)
|
||||
return;
|
||||
}
|
||||
|
||||
// 关机事件处理:捕获到长按关机指令,直接调用 Power_Off 关断电源
|
||||
if (evt.key_event == KEY_EVENT_POWER_OFF) {
|
||||
Power_Off();
|
||||
return;
|
||||
}
|
||||
|
||||
// 常规事件(如普通的按键点击)直接透传分发给当前处于前台活跃的应用
|
||||
AppManager_DispatchEvent(evt);
|
||||
}
|
||||
@@ -152,29 +160,68 @@ void Timer1_Init(void)
|
||||
EA = 1; // 开启全局中断允许
|
||||
}
|
||||
|
||||
// 全局按键扫描触发标志
|
||||
volatile bit key_scan_flag = 0;
|
||||
|
||||
/**
|
||||
* @brief 定时器 1 中断服务程序 (每 1ms 触发一次)
|
||||
* @details 承担了系统软时钟计算、10ms 周期性按键消抖扫描的功能。马达等特定业务已剥离至 App 内部。
|
||||
* @details 承担系统软时钟计算,并每 10ms 置位主循环按键扫描标志,不直接在中断中进行 ADC 多次采样。
|
||||
*/
|
||||
void Timer1_Isr(void) interrupt 3
|
||||
{
|
||||
SystemEvent evt;
|
||||
|
||||
// 1ms 定时更新软时钟
|
||||
Clock_IncMS();
|
||||
|
||||
// 报警屏幕红边框以 100ms 周期亮灭,进行颜色爆闪指示
|
||||
alarm_flash_flag = (ms_tick / 100) % 2;
|
||||
|
||||
/* 物理按键扫描及消抖状态机逻辑 (每 10ms 轮询一次) */
|
||||
/* 物理按键定时计数 (每 10ms 置位一次标志位) */
|
||||
key_scan_timer++;
|
||||
if (key_scan_timer >= 10) {
|
||||
// 读取按键的电平信号 (由于有上拉电阻,未按下为高 1,按下为低 0,所以取反做正逻辑)
|
||||
u8 raw_up = !KEY_UP;
|
||||
u8 raw_down = !KEY_DOWN;
|
||||
u8 raw_confirm = !KEY_CONFIRM;
|
||||
u8 raw_sos = 0; // 临时注释关闭 SOS 物理按键电平检测,防浮空干扰
|
||||
key_scan_timer = 0; // 重置扫描分频计数器
|
||||
key_scan_flag = 1; // 仅在中断里置起标志位,留待主循环中执行实际读取与消抖
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 主循环按键扫描与消抖处理函数
|
||||
* @details 由主循环每 10ms 检查并执行,完全在主线程中运行,绝无中断重入冲突。
|
||||
*/
|
||||
void Event_KeyScan_Poll(void)
|
||||
{
|
||||
SystemEvent evt;
|
||||
// 读取按键的电平信号 (KEY_UP, KEY_CONFIRM 为低电平有效)
|
||||
u8 raw_up = !KEY_UP;
|
||||
u8 raw_down = ADC_IsKeyDown(); // KEY_DOWN 通过 P0.3 ADC 通道判定 (常态 2.0V, 按下 3.7V)
|
||||
u8 raw_confirm = !KEY_CONFIRM;
|
||||
u8 raw_sos = !KEY_SOS; // 恢复 SOS 物理按键电平检测
|
||||
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 值
|
||||
{
|
||||
u16 down_val = ADC_ReadFiltered(ADC_CHANNEL_KEY_DOWN);
|
||||
if (down_val > 2800) {
|
||||
Uart_SendString("[KEY-DIAG] KEY_DOWN pressed! ADC Raw: ");
|
||||
Uart_SendByte((u8)('0' + (down_val / 1000) % 10));
|
||||
Uart_SendByte((u8)('0' + (down_val / 100) % 10));
|
||||
Uart_SendByte((u8)('0' + (down_val / 10) % 10));
|
||||
Uart_SendByte((u8)('0' + (down_val % 10)));
|
||||
Uart_SendString("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
// 检测到有任意按键处于下压状态,立即将无操作闲置休眠计时器归零
|
||||
if (raw_up || raw_down || raw_confirm || raw_sos) {
|
||||
@@ -188,6 +235,7 @@ void Timer1_Isr(void) interrupt 3
|
||||
|
||||
// 默认将派发事件重置为空
|
||||
evt.key_event = KEY_EVENT_NONE;
|
||||
evt.priority = EVENT_PRIORITY_NORMAL;
|
||||
evt.extra_data = 0;
|
||||
evt.extra_size = 0;
|
||||
|
||||
@@ -219,15 +267,16 @@ void Timer1_Isr(void) interrupt 3
|
||||
key_up_hold = 0; // 释放归零
|
||||
}
|
||||
|
||||
// ▼ (KEY_DOWN) 按键状态机
|
||||
// ▼ (KEY_DOWN / 开关机) 按键状态机
|
||||
if (raw_down) {
|
||||
key_down_hold++;
|
||||
if (key_down_hold == 300) { // 长按 3 秒
|
||||
evt.key_event = KEY_EVENT_DOWN_LONG;
|
||||
if (key_down_hold == 100) { // 长按 1 秒 (100 * 10ms = 1000ms = 1s) 触发关机事件
|
||||
evt.key_event = KEY_EVENT_POWER_OFF;
|
||||
EventQueue_Push(evt);
|
||||
}
|
||||
} else {
|
||||
if (key_down_hold >= 2 && key_down_hold < 300) {
|
||||
// 如果松开时按下时长在 20ms 至 1s 之间,触发短按下翻/选择事件
|
||||
if (key_down_hold >= 2 && key_down_hold < 100) {
|
||||
evt.key_event = KEY_EVENT_DOWN_CLICK;
|
||||
EventQueue_Push(evt);
|
||||
}
|
||||
@@ -249,22 +298,23 @@ void Timer1_Isr(void) interrupt 3
|
||||
key_confirm_hold = 0; // 释放归零
|
||||
}
|
||||
|
||||
/*
|
||||
// SOS 物理求救按键状态机
|
||||
// SOS 物理求救按键状态机 (已解锁)
|
||||
if (raw_sos) {
|
||||
key_sos_hold++;
|
||||
if (key_sos_hold == 300) { // 长按 3 秒,发出求救,插入队首
|
||||
evt.key_event = KEY_EVENT_SOS_LONG;
|
||||
evt.priority = EVENT_PRIORITY_URGENT;
|
||||
EventQueue_InsertFront(evt);
|
||||
Uart_SendString("[KEY-DIAG] SOS Long Press Dispatched!\r\n");
|
||||
}
|
||||
} else {
|
||||
if (key_sos_hold >= 2 && key_sos_hold < 300) { // 短按即呼救,插入队首
|
||||
evt.key_event = KEY_EVENT_SOS_CLICK;
|
||||
evt.priority = EVENT_PRIORITY_URGENT;
|
||||
EventQueue_InsertFront(evt);
|
||||
Uart_SendString("[KEY-DIAG] SOS Click Dispatched!\r\n");
|
||||
}
|
||||
key_sos_hold = 0; // 释放归零
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,4 +68,13 @@ void Event_Dispatcher_Loop(void);
|
||||
*/
|
||||
void Timer1_Init(void);
|
||||
|
||||
// 主循环按键扫描定时标志 (由 1ms 中断置 1,主循环响应清除)
|
||||
extern volatile bit key_scan_flag;
|
||||
|
||||
/**
|
||||
* @brief 主循环按键扫描与消抖处理函数
|
||||
* @details 将按键检测逻辑从中断搬移至此,在主线程执行,避免 ADC 多重采样冲突死锁
|
||||
*/
|
||||
void Event_KeyScan_Poll(void);
|
||||
|
||||
#endif // __EVENT_H__
|
||||
|
||||
57
App/main.c
57
App/main.c
@@ -55,6 +55,42 @@ volatile u16 comb_hold = 0; // ▲ + ▼ 组合键按下时长累
|
||||
* - 'z'/'Z': 模拟超时,强行使闲置计数器置 6000,触发自动深度休眠。
|
||||
* - '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)
|
||||
{
|
||||
SystemEvent debug_evt;
|
||||
@@ -146,10 +182,14 @@ void Debug_ProcessCommand(char cmd)
|
||||
} else if (cmd == 'z' || cmd == 'Z') {
|
||||
inactivity_timer = 6000; // 模拟闲置 60 秒超时,强制触发主循环深度休眠
|
||||
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') {
|
||||
RF_DiagnosticMode('t'); // 触发射频连续发射诊断 (绿色爆闪)
|
||||
} else if (cmd == 'r' || cmd == 'R') {
|
||||
RF_DiagnosticMode('r'); // 触发 5 秒射频接收监听诊断
|
||||
} else if (cmd == 'l' || cmd == 'L') {
|
||||
Loopback_Test(); // 触发物理电气回环测试 (短路 P2.1 和 P3.6 自检)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,13 +207,15 @@ void main(void)
|
||||
WDT_CONTR = 0x00; // 关闭看门狗定时器
|
||||
|
||||
// ====== 硬件各模块基础初始化 ======
|
||||
GPIO_Init(); // 配置各个引脚的推挽输出/准双向模式及内部上拉
|
||||
GPIO_Init(); // 配置各个引脚的推挽输出/准双向模式及内部上拉 (锁存 PWR_HOLD=1)
|
||||
RGB_Send(0, 0, 0, 0, 0, 0); // 【首要修正】:上电第一时间强制发送 0 码关闭灯条,熄灭上电暂态引起的随机白光!
|
||||
RF_Init(); // 初始化射频芯片各引脚控制
|
||||
Delay_ms(500); // 软启动延时,等待供电电容稳定
|
||||
LCD_Init(); // 初始化 LHS114TC-IF03 (ST7789V) 屏幕控制寄存器序列并开启背光
|
||||
Uart1_Init(); // 初始化串口 1 波特率 115200 供调试日志
|
||||
Uart_SendString("Wristband System Initialized!\r\n");
|
||||
|
||||
Self_Test(); // 执行上电硬件自检 (PWR_HOLD锁存、P0.3 ADC按键常态、P1.3 电池电量)
|
||||
Load_Database(); // 从 IAP Flash 第 254 扇区读取所有已绑定的传感器数据至 RAM
|
||||
|
||||
/* 步骤 1: 初始化事件环形缓冲区队列 */
|
||||
@@ -182,6 +224,9 @@ void main(void)
|
||||
/* 步骤 2+5: 初始化应用管理器并登记所有前台应用,默认加载 ClockApp */
|
||||
AppManager_Init();
|
||||
|
||||
/* 启动并挂载后台射频监控应用,开始侦听无线传感器信号 */
|
||||
AppManager_AttachToBackground(&rf_monitor_app);
|
||||
|
||||
/* 启动 1ms 系统基准定时器 Timer1 */
|
||||
Timer1_Init();
|
||||
|
||||
@@ -192,6 +237,12 @@ void main(void)
|
||||
|
||||
// ====== 主循环 ======
|
||||
while (1) {
|
||||
/* 0. 检查主循环按键定时标志 (由 1ms 中断置位,主循环响应清除并处理) */
|
||||
if (key_scan_flag) {
|
||||
key_scan_flag = 0;
|
||||
Event_KeyScan_Poll();
|
||||
}
|
||||
|
||||
/* 1. 串口非阻塞指令捕获 -> 装配为事件方式入队 */
|
||||
cmd = Uart_RxChar();
|
||||
if (cmd != '\0') {
|
||||
@@ -211,6 +262,8 @@ void main(void)
|
||||
inactivity_timer = 0;
|
||||
current_state = STATE_SLEEP; // 系统置为休眠状态
|
||||
Enter_Low_Power_Sleep(); // 该函数是阻塞的,MCU 将在此挂起。唤醒后将自动从其后继续执行
|
||||
current_state = STATE_NORMAL; // 唤醒后重新置为正常待机状态
|
||||
AppManager_StartApp(APP_ID_CLOCK); // 重新开启时钟应用以刷新画面和背光
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,7 +308,7 @@ void Port2_Isr(void) interrupt 15
|
||||
*/
|
||||
void Port3_Isr(void) interrupt 16
|
||||
{
|
||||
EAXFR = 1; // 使能访问扩展寄存区
|
||||
EAXFR = 1; // 使能访问扩展寄存器
|
||||
p3_wakeup_flag = P3INTF | 0x01;// 读取并备份 P3 中断标志寄存器值 (0x01作安全掩码)
|
||||
P3INTF = 0x00; // 清除 P3 端口中断悬挂标志位
|
||||
P3INTE = 0x00; // 立即关闭 P3 中断通道,防止反复重入
|
||||
|
||||
151
App/rgb.c
151
App/rgb.c
@@ -1,122 +1,99 @@
|
||||
#include "rgb.h"
|
||||
#include "system.h"
|
||||
|
||||
/* ====== SPI 寄存器映射地址定义 (防止 stc32g.h 库版本中未显式定义) ====== */
|
||||
/* ====== SPI Register Defines (in case stc32g.h lacks them) ====== */
|
||||
#ifndef P_SW1
|
||||
sfr P_SW1 = 0xA2; // 外设端口切换寄存器 1
|
||||
sfr SPCTL = 0xCE; // SPI 控制寄存器
|
||||
sfr SPSTAT = 0xCD; // SPI 状态寄存器
|
||||
sfr SPDAT = 0xCF; // SPI 数据寄存器
|
||||
sfr P_SW1 = 0xA2;
|
||||
sfr SPCTL = 0xCE;
|
||||
sfr SPSTAT = 0xCD;
|
||||
sfr SPDAT = 0xCF;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 初始化内核 SPI 控制器
|
||||
* @details 将 SPI 的物理管脚切换至 P2 端口的第二组外设映射位置(SCLK=P2.5, MOSI=P2.3),
|
||||
* 并配置 SPI 传输速率,使其产生的脉冲宽满足 WS2812B/FCOB 的 1 码和 0 码时序。
|
||||
*/
|
||||
void SPI_Init(void)
|
||||
{
|
||||
// 将 P_SW1 寄存器的第 2、3 位 (SPI_S) 配置为 01,即将 SPI 外设映射至 P2.2~P2.5 端口上
|
||||
P_SW1 = (P_SW1 & ~0x0C) | 0x04;
|
||||
|
||||
// 配置 SPCTL (SPI Control Register):
|
||||
// SSIG = 1 (忽略SS引脚,置为主机)
|
||||
// SPEN = 1 (启用SPI模块)
|
||||
// MSTR = 1 (配置为主机模式)
|
||||
// SPR0 = 1 (3MHz communication speed @ 24MHz System Clock)
|
||||
SPCTL = 0xD1;
|
||||
|
||||
// 清除 SPI 状态标志 (写入1清除 SPIF 和 WCOL 标志位)
|
||||
SPSTAT = 0xC0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 字节数据转 3-bit 编码的 WS2812B 驱动波形数据
|
||||
* @param val 要转码的一个 8 位色彩数值 (如R、G、B颜色分量)
|
||||
* @param out 转换后存储目标的高速缓冲缓冲区首地址,1 字节拆分成 3 字节表示物理波形
|
||||
* @details WS2812B 通讯协议需要通过高低电平的持续脉冲时间比来判断 "1" 码或 "0" 码。
|
||||
* 通过将 SPI 速率设为 3.0 Mbps(即每个 SPI bit 占 ~333ns):
|
||||
* - 一个 WS2812B 协议的 "1" 码(800ns高, 450ns低)可用二进制 `110` (高高低,~666ns高, ~333ns低) 来模拟;
|
||||
* - 一个 WS2812B 协议的 "0" 码(400ns高, 850ns低)可用二进制 `100` (高低低,~333ns高, ~666ns低) 来模拟;
|
||||
* 因此,1 个字节(8 bits)的颜色深度数据被完美重映射编码为 24 个 SPI 数据 bits(共需 3 个 SPI 字节发送)。
|
||||
*/
|
||||
void Encode_Byte(u8 val, u8 *out)
|
||||
{
|
||||
u32 spi_val = 0; // 用于拼接 24 位 SPI 映射串
|
||||
u8 i;
|
||||
for (i = 0; i < 8; i++)
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
spi_val <<= 3; // 每次左移 3 位,为下一个像素的波形让出空间
|
||||
if (val & (0x80 >> i))
|
||||
u8 pair_val = 0;
|
||||
if (val & (0x80 >> (2 * i)))
|
||||
{
|
||||
spi_val |= 6; // 该 bit 为 1,对应 WS2812B "1" 码时序,用二进制 '110' (即数值6) 填充
|
||||
pair_val |= 0xC0;
|
||||
}
|
||||
else
|
||||
{
|
||||
spi_val |= 4; // 该 bit 为 0,对应 WS2812B "0" 码时序,用二进制 '100' (即数值4) 填充
|
||||
pair_val |= 0x80;
|
||||
}
|
||||
if (val & (0x80 >> (2 * i + 1)))
|
||||
{
|
||||
pair_val |= 0x0C;
|
||||
}
|
||||
else
|
||||
{
|
||||
pair_val |= 0x08;
|
||||
}
|
||||
out[i] = pair_val;
|
||||
}
|
||||
// 将拼接好的 24 位波形数据(前高位后低位)拆分装载进输出缓冲区中
|
||||
out[0] = (u8)(spi_val >> 16);
|
||||
out[1] = (u8)(spi_val >> 8);
|
||||
out[2] = (u8)spi_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送两颗级联 RGB 灯珠的驱动命令
|
||||
* @details 负责将两组 GRB 格式色彩数据(g1, r1, b1 和 g2, r2, b2)转换为 WS2812B 的 SPI 脉冲编码,
|
||||
* 并在发送期间实施硬件引脚复用隔离,发送完成后发出复位复位电平。
|
||||
*/
|
||||
void RGB_Send(u8 g1, u8 r1, u8 b1, u8 g2, u8 r2, u8 b2)
|
||||
{
|
||||
u8 idata spi_buf[18]; // 2 颗灯 * 3 个基色通道 * 3 字节编码 = 18 字节缓冲
|
||||
u8 i;
|
||||
u8 idata buf[6];
|
||||
u8 i, mask;
|
||||
buf[0] = g1; buf[1] = r1; buf[2] = b1;
|
||||
buf[3] = g2; buf[4] = r2; buf[5] = b2;
|
||||
|
||||
// 分别转码两颗灯珠的 G、R、B 通道,顺序填充进 SPI 缓冲区
|
||||
Encode_Byte(g1, &spi_buf[0]);
|
||||
Encode_Byte(r1, &spi_buf[3]);
|
||||
Encode_Byte(b1, &spi_buf[6]);
|
||||
Encode_Byte(g2, &spi_buf[9]);
|
||||
Encode_Byte(r2, &spi_buf[12]);
|
||||
Encode_Byte(b2, &spi_buf[15]);
|
||||
EA = 0;
|
||||
|
||||
EA = 0; // 关闭全局中断,防止 SPI 数据发送被高频中断打断,造成 WS2812B 的时序畸变
|
||||
|
||||
// 【硬件复用隔离机制】:
|
||||
// 物理引脚 P2.5 同时被用作“振动马达控制端 (MOTOR)”和“SPI SCLK 时钟信号输出”。
|
||||
// 在 SPI 发送 RGB 期间,SCLK 将高速振荡,这会导致马达发生异常高频振动甚至噪音。
|
||||
// 因此在启动 SPI 发送前,必须先将 P2.5 (MOTOR) 临时配置为高阻输入模式(M1=1, M0=0),
|
||||
// 从而与 SPI 时钟引脚在电气上实现物理隔离,防止杂散时钟信号驱动马达。
|
||||
P2M1 |= (1 << 5);
|
||||
P2M0 &= ~(1 << 5);
|
||||
|
||||
SPI_Init(); // 配置并启动 SPI 外设控制器
|
||||
|
||||
// 循环发送 18 字节的 WS2812B 脉冲编码数据
|
||||
for (i = 0; i < 18; i++)
|
||||
{
|
||||
SPDAT = spi_buf[i]; // 装载待发数据到数据寄存器
|
||||
while (!(SPSTAT & 0x80)); // 等待 SPI 状态寄存器第 7 位 (SPIF) 变为 1,代表发送结束
|
||||
SPSTAT = 0xC0; // 往状态寄存器写入1以清零 SPIF 和 WCOL 标志
|
||||
}
|
||||
|
||||
SPCTL = 0; // 彻底关闭 SPI 控制器,释放总线
|
||||
|
||||
// 【发送 RESET 复位低电平帧】:
|
||||
// WS2812B 驱动芯片要求在所有色彩帧数据发完后,DIN 必须维持大于 80us 的低电平以将数据刷入锁存。
|
||||
// 首先将 P2.3 (RGB_DIN) 重新配置为推挽输出模式 (M1=0, M0=1)
|
||||
P2M1 &= ~(1 << 3);
|
||||
P2M0 |= (1 << 3);
|
||||
RGB_DIN = 0; // 强行拉低 DIN 信号线
|
||||
for (i = 0; i < 200; i++)
|
||||
{
|
||||
_nop_(); _nop_(); _nop_(); // 软件延时,保持低电平时间大于 100微秒
|
||||
}
|
||||
|
||||
// 【恢复复用引脚功能】:
|
||||
// 恢复 P2.5 (MOTOR) 引脚为普通的推挽输出模式 (M1=0, M0=1),以便重新由定时器或应用控制振动马达
|
||||
P2M1 &= ~(1 << 5);
|
||||
P2M0 |= (1 << 5);
|
||||
MOTOR = 0; // 默认拉低马达引脚,防止发声
|
||||
MOTOR = 0;
|
||||
|
||||
EA = 1; // 恢复全局中断使能
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
for (mask = 0x80; mask; mask >>= 1)
|
||||
{
|
||||
if (buf[i] & mask)
|
||||
{
|
||||
RGB_DIN = 1;
|
||||
_nop_(); _nop_(); _nop_(); _nop_(); _nop_();
|
||||
_nop_(); _nop_(); _nop_(); _nop_(); _nop_();
|
||||
_nop_(); _nop_(); _nop_(); _nop_(); _nop_();
|
||||
RGB_DIN = 0;
|
||||
_nop_(); _nop_(); _nop_(); _nop_(); _nop_();
|
||||
_nop_();
|
||||
}
|
||||
else
|
||||
{
|
||||
RGB_DIN = 1;
|
||||
_nop_(); _nop_(); _nop_();
|
||||
RGB_DIN = 0;
|
||||
_nop_(); _nop_(); _nop_(); _nop_(); _nop_();
|
||||
_nop_(); _nop_(); _nop_(); _nop_(); _nop_();
|
||||
_nop_(); _nop_(); _nop_(); _nop_(); _nop_();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RGB_DIN = 0;
|
||||
for (i = 0; i < 200; i++)
|
||||
{
|
||||
_nop_(); _nop_(); _nop_();
|
||||
}
|
||||
|
||||
EA = 1;
|
||||
}
|
||||
|
||||
void RGB_Bitbang_Send24(u8 g, u8 r, u8 b)
|
||||
{
|
||||
(void)g; (void)r; (void)b;
|
||||
}
|
||||
|
||||
BIN
App/system.c
BIN
App/system.c
Binary file not shown.
12
App/system.h
12
App/system.h
@@ -117,4 +117,16 @@ void Enter_Low_Power_Sleep(void);
|
||||
*/
|
||||
void Wakeup_Restore(void);
|
||||
|
||||
/**
|
||||
* @brief 系统开机上电硬件自检函数
|
||||
* @details 检测 PWR_HOLD 锁存状态、ADC 采样基准与串口通讯,输出完整自检 report
|
||||
*/
|
||||
void Self_Test(void);
|
||||
|
||||
/**
|
||||
* @brief 执行关机断电流程
|
||||
* @details 关断外设、震动提示并拉低 PWR_HOLD 引脚切断总电源
|
||||
*/
|
||||
void Power_Off(void);
|
||||
|
||||
#endif // __SYSTEM_H__
|
||||
|
||||
41
App/ui.c
41
App/ui.c
@@ -1,5 +1,6 @@
|
||||
#include "ui.h"
|
||||
#include "../Drivers/lcd.h"
|
||||
#include "../Drivers/adc.h"
|
||||
|
||||
// 引入外部主菜单对码项名称定义 (例如 "DETEC. PORTE" 等,法语表达防区类型名称)
|
||||
extern char *code menu_items[5];
|
||||
@@ -10,23 +11,26 @@ extern char *code menu_items[5];
|
||||
* @param hour 当前待显示的小时数 (0~23)
|
||||
* @param min 当前待显示的分钟数 (0~59)
|
||||
* @details 该页面被划分为三大功能视觉区:
|
||||
* 1. 顶部状态栏:左侧绘制布撤防指示锁,右侧显示 "85%" 剩余电量以及精细电池单色位图。
|
||||
* 1. 顶部状态栏:左侧绘制布撤防指示锁,右侧显示真实剩余电量百分比以及电池单色位图。
|
||||
* 2. 中部核心栏:垂直居中以 16x32 大号粗字体绘制格式化时间 "HH:MM",在其下方标注 AM/PM 标识。
|
||||
* 3. 底部信息栏:使用灰色小字模绘制法语星期日期 "VEN 10-07" (星期五 7月10日)。
|
||||
*/
|
||||
void UI_ShowClockPage(SystemState state, u8 hour, u8 min)
|
||||
{
|
||||
char time_str[6];
|
||||
LCD_Clear(COLOR_BLACK); // 使用纯黑色底色,充分利用 AMOLED 黑色像素不发光的省电特性
|
||||
char batt_str[7];
|
||||
u8 batt;
|
||||
|
||||
// ====== 绘制左上角布撤防锁头状态图标 ======
|
||||
// 清屏,底色填充为纯黑
|
||||
LCD_Clear(COLOR_BLACK);
|
||||
|
||||
// ====== 绘制左上角布撤防图标 ======
|
||||
if (state == STATE_ARMED)
|
||||
{
|
||||
// 状态: ARMED (布防) -> 绘制红色闭合锁头
|
||||
LCD_DrawRectBorder(10, 23, 20, 15, COLOR_RED); // 锁身矩形框
|
||||
LCD_FillRect(15, 15, 2, 8, COLOR_RED); // 左半锁环
|
||||
LCD_FillRect(17, 15, 10, 2, COLOR_RED); // 锁环顶梁
|
||||
LCD_FillRect(25, 15, 2, 8, COLOR_RED); // 右半锁环
|
||||
// 状态: ARMED (已布防) -> 绘制红色闭合锁头
|
||||
LCD_DrawRectBorder(10, 23, 20, 15, COLOR_RED); // 锁身矩形
|
||||
LCD_DrawRectBorder(14, 15, 12, 9, COLOR_RED); // 闭合状态的拱形锁环
|
||||
LCD_FillRect(18, 28, 4, 5, COLOR_RED); // 锁芯暗扣
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -37,9 +41,24 @@ void UI_ShowClockPage(SystemState state, u8 hour, u8 min)
|
||||
LCD_FillRect(25, 17, 2, 6, COLOR_GREEN); // 开启状态的偏置锁环
|
||||
}
|
||||
|
||||
// ====== 绘制右上角电池电量 ======
|
||||
LCD_ShowString(77, 24, "85%", COLOR_GRAY, COLOR_BLACK); // 静态测试显示 85% 字符并移动至右上角
|
||||
LCD_DrawMonoBitmap(107, 27, 20, 10, bmp_battery_20x10, COLOR_GRAY, COLOR_BLACK); // 渲染 20x10 电池并移动至右上角
|
||||
// ====== 绘制右上角真实电池电量 ======
|
||||
batt = ADC_GetBatteryPercent();
|
||||
if (batt >= 100) {
|
||||
batt_str[0] = '1'; batt_str[1] = '0'; batt_str[2] = '0'; batt_str[3] = '%'; batt_str[4] = '\0';
|
||||
} else {
|
||||
batt_str[0] = '0' + (batt / 10);
|
||||
batt_str[1] = '0' + (batt % 10);
|
||||
batt_str[2] = '%';
|
||||
batt_str[3] = '\0';
|
||||
}
|
||||
// 充电中则在百分比后追加 '+' 后缀
|
||||
if (Is_Charging()) {
|
||||
u8 len = (batt >= 100) ? 4 : 3;
|
||||
batt_str[len] = '+';
|
||||
batt_str[len + 1] = '\0';
|
||||
}
|
||||
LCD_ShowString(70, 24, batt_str, COLOR_GRAY, COLOR_BLACK); // 渲染真实动态百分比
|
||||
LCD_DrawMonoBitmap(107, 27, 20, 10, bmp_battery_20x10, COLOR_GRAY, COLOR_BLACK); // 渲染 20x10 电池图标
|
||||
|
||||
// ====== 绘制中部时间 "HH:MM" ======
|
||||
time_str[0] = '0' + (hour / 10); // 小时十位
|
||||
|
||||
7
CLAUDE.md
Normal file
7
CLAUDE.md
Normal 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.
|
||||
@@ -49,7 +49,7 @@
|
||||
| **Pin 29** | `P0.0` | `BAT_ADC` | ADC 输入 | 电池电压采集 |
|
||||
| **Pin 30** | `P0.1` | `KEY_UP` | 高阻输入 + 上拉 | 侧边按键 ▲ (上移),按下为低电平 |
|
||||
| **Pin 31** | `P0.2` | `KEY_CONFIRM` | 高阻输入 + 上拉 | 侧边按键 ■ (确认/菜单),按下为低电平 |
|
||||
| **Pin 32** | `P0.3` | `KEY_DOWN` | 高阻输入 + 上拉 | 侧边按键 ▼ (下移),按下为低电平 |
|
||||
| **Pin 32** | `P0.3` | `KEY_DOWN`
|
||||
|
||||
---
|
||||
|
||||
|
||||
171
Drivers/adc.c
Normal file
171
Drivers/adc.c
Normal file
@@ -0,0 +1,171 @@
|
||||
#include "adc.h"
|
||||
#include "../App/system.h"
|
||||
|
||||
/**
|
||||
* @brief 初始化 STC32G 硬件 ADC 模块
|
||||
* @details 配置 P0.3 (KEY_DOWN) 与 P1.3 (BATT_ADC) 为高阻输入模式,
|
||||
* 上电开启 ADC 内部电源并配置结果对齐格式为右对齐。
|
||||
*/
|
||||
void ADC_Init(void)
|
||||
{
|
||||
// 1. 配置 P0.3 和 P1.3 引脚模式为高阻输入 (M1=1, M0=0)
|
||||
P0M1 |= (1 << 3);
|
||||
P0M0 &= ~(1 << 3);
|
||||
|
||||
P1M1 |= (1 << 3);
|
||||
P1M0 &= ~(1 << 3);
|
||||
|
||||
// 配置 P1.2 (CHRG_DET / ADC2) 为高阻输入模式
|
||||
P1M1 |= (1 << 2);
|
||||
P1M0 &= ~(1 << 2);
|
||||
|
||||
// 2. 开启 ADC 模块主电源 (ADC_POWER = 1)
|
||||
ADC_POWER = 1;
|
||||
|
||||
// 3. 配置 ADCCFG 寄存器:
|
||||
// RESFMT (bit 5) = 1: 转换结果右对齐 (ADC_RES 存高4位, ADC_RESL 存低8位)
|
||||
// 底部 4 位 SPEED: 设置 ADC 转换工作时钟频率 (0x0F 表示最快速度)
|
||||
ADCCFG = 0x2F;
|
||||
|
||||
// 4. 软启动延迟,等待 ADC 模块内部电源稳定
|
||||
Delay_ms(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 单次读取指定 ADC 通道的原始 12 位转换结果
|
||||
* @param channel ADC 通道号 (0~15)
|
||||
* @return u16 12 位 ADC 采样原始值 (0 ~ 4095)
|
||||
*/
|
||||
u16 ADC_ReadRaw(u8 channel)
|
||||
{
|
||||
u16 adc_val = 0;
|
||||
|
||||
// 选择指定通道并保持电源开启
|
||||
ADC_CONTR = (ADC_CONTR & 0xF0) | (channel & 0x0F);
|
||||
|
||||
// 延时等待通道切换电容充放电稳定
|
||||
Delay_us(10);
|
||||
|
||||
// 启动 A/D 转换
|
||||
ADC_START = 1;
|
||||
|
||||
// 阻塞等待转换结束 (ADC_FLAG 置 1)
|
||||
while (!ADC_FLAG);
|
||||
|
||||
// 清除转换完成标志
|
||||
ADC_FLAG = 0;
|
||||
|
||||
// 读取 12 位右对齐结果 (ADC_RES 取低 4 位,结合 ADC_RESL 构成 12 位数据)
|
||||
adc_val = ((u16)(ADC_RES & 0x0F) << 8) | ADC_RESL;
|
||||
|
||||
return adc_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 5 次采样排序取中值滤波算法
|
||||
* @param channel ADC 通道号 (0~15)
|
||||
* @return u16 滤波后的 12 位 ADC 采样结果
|
||||
*/
|
||||
u16 ADC_ReadFiltered(u8 channel)
|
||||
{
|
||||
u16 buf[5];
|
||||
u8 i, j;
|
||||
u16 temp;
|
||||
|
||||
// 连续采集 5 次数据
|
||||
for (i = 0; i < 5; i++) {
|
||||
buf[i] = ADC_ReadRaw(channel);
|
||||
}
|
||||
|
||||
// 冒泡排序法进行升序排列
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 4 - i; j++) {
|
||||
if (buf[j] > buf[j + 1]) {
|
||||
temp = buf[j];
|
||||
buf[j] = buf[j + 1];
|
||||
buf[j + 1] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 取中间第 3 个值 (索引 2) 作为有效滤波输出,有效去除偶然性噪声尖峰
|
||||
return buf[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检测 USB 充电是否插入
|
||||
* @return bit 0=未充电, 1=充电中
|
||||
* @note TP4054 CHRG 开漏输出:
|
||||
* - 未充电/充满:CHRG 为高阻(被内部上拉拉高)
|
||||
* - 充电中:CHRG 为低电平
|
||||
* 返回值 1 表示正在充电,0 表示未充电或充满。
|
||||
*/
|
||||
bit Is_Charging(void)
|
||||
{
|
||||
u32 val = ADC_ReadFiltered(ADC_CHANNEL_CHRG_DET);
|
||||
u32 det_mv = (val * 3300UL) / 4095UL;
|
||||
|
||||
// 0.8V ~ 1.8V (800mV ~ 1800mV):正在充电
|
||||
if (det_mv >= 800 && det_mv <= 1800) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检测 KEY_DOWN (P0.3) 按键电平是否按下
|
||||
* @details KEY_DOWN 节点未按下时电压约 2.0V,按下后升至约 3.7V。
|
||||
* @return bit 1: 按下, 0: 常态未按下
|
||||
*/
|
||||
bit ADC_IsKeyDown(void)
|
||||
{
|
||||
u16 val = ADC_ReadFiltered(ADC_CHANNEL_KEY_DOWN);
|
||||
// 若采样值超过阈值 KEY_DOWN_ADC_THRESHOLD (3300),判定为按键被按下
|
||||
if (val > KEY_DOWN_ADC_THRESHOLD) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取 P1.3 电池分压计算得出的实际电池电压值 (单位: mV)
|
||||
* @details 硬件电阻分压电路: R15 (10kΩ) 上拉, R16 (22kΩ) 下拉。
|
||||
* 分压公式: V_node = V_bat * (22 / 32) => V_bat = V_node * (32 / 22)
|
||||
* 根据 ADC 采样: V_node(mV) = adc_val * 3300 / 4095
|
||||
* 真实电池电压 V_bat(mV) = (adc_val * 3300 * 32) / (4095 * 22)
|
||||
* @return u16 电池总电压 (mV, 例如 3850 代表 3.85V)
|
||||
*/
|
||||
u16 ADC_GetBatteryVoltage_mV(void)
|
||||
{
|
||||
u32 adc_val = ADC_ReadFiltered(ADC_CHANNEL_BATTERY);
|
||||
u32 vbat_mv;
|
||||
|
||||
// 经用户实测校准:
|
||||
// 当电池真实电压为 4.0V (4000mV) 时,10k/22k 分压点万用表实测为 2.7V,单片机读取的过滤 ADC 原值约为 2357。
|
||||
// 换算公式:V_bat(mV) = adc_val * 4000 / 2357
|
||||
vbat_mv = (adc_val * 4000UL) / 2357UL;
|
||||
|
||||
return (u16)vbat_mv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算电池剩余电量百分比 (0% ~ 100%)
|
||||
* @details 锂电池标准电压区间定义:
|
||||
* 4.20V (4200mV) 对应 100%
|
||||
* 3.30V (3300mV) 对应 0%
|
||||
* @return u8 电量百分比 (0 ~ 100)
|
||||
*/
|
||||
u8 ADC_GetBatteryPercent(void)
|
||||
{
|
||||
u16 vbat = ADC_GetBatteryVoltage_mV();
|
||||
|
||||
if (vbat >= 4200) {
|
||||
return 100;
|
||||
}
|
||||
if (vbat <= 3300) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 线性百分比换算: percent = (vbat - 3300) * 100 / (4200 - 3300)
|
||||
return (u8)(((u32)(vbat - 3300) * 100UL) / 900UL);
|
||||
}
|
||||
61
Drivers/adc.h
Normal file
61
Drivers/adc.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef __ADC_H__
|
||||
#define __ADC_H__
|
||||
|
||||
#include "../App/config.h"
|
||||
|
||||
/* ====== ADC 通道与硬件引脚定义 ====== */
|
||||
#define ADC_CHANNEL_KEY_DOWN 11 // KEY_DOWN (P0.3) 对应 STC32G 的 ADC 通道 11
|
||||
#define ADC_CHANNEL_BATTERY 3 // 电池检测 (P1.3) 对应 STC32G 的 ADC 通道 3
|
||||
#define ADC_CHANNEL_CHRG_DET 2 // 充电检测 (P1.2) 对应 STC32G 的 ADC 通道 2
|
||||
|
||||
/* ====== 判定阈值定义 (基于 12 位 ADC, 0~4095) ====== */
|
||||
// KEY_DOWN 2.2V 常态 (ADC~2700), 3.7V 按下 (ADC 饱和 4095)
|
||||
#define KEY_DOWN_ADC_THRESHOLD 3400 // ADC 采样值超过 3400 判定为按键按下
|
||||
|
||||
/**
|
||||
* @brief 初始化 STC32G 硬件 ADC 模块
|
||||
* @details 开启 ADC 电源、设置结果右对齐格式及采样时钟分频,并将 P0.3 和 P1.3 设置为高阻输入模式
|
||||
*/
|
||||
void ADC_Init(void);
|
||||
|
||||
/**
|
||||
* @brief 读取指定 ADC 通道的原始 12 位转换结果
|
||||
* @param channel ADC 通道号 (0~15)
|
||||
* @return u16 12 位 ADC 采样原始值 (0 ~ 4095)
|
||||
*/
|
||||
u16 ADC_ReadRaw(u8 channel);
|
||||
|
||||
/**
|
||||
* @brief 对指定 ADC 通道进行多重采样与中值滤波
|
||||
* @param channel ADC 通道号 (0~15)
|
||||
* @return u16 滤波后的 12 位 ADC 采样值
|
||||
*/
|
||||
u16 ADC_ReadFiltered(u8 channel);
|
||||
|
||||
/**
|
||||
* @brief 判断 KEY_DOWN (P0.3) 当前是否处于按下状态 (ADC 阈值判定)
|
||||
* @return bit 1: 按下 (电压 ~3.7V), 0: 松开/常态 (电压 ~2.0V)
|
||||
*/
|
||||
bit ADC_IsKeyDown(void);
|
||||
|
||||
/**
|
||||
* @brief 获取 P1.3 电池分压测得的实际电压 (毫伏 mV)
|
||||
* @return u16 电池电压,单位 mV (例如 3850 代表 3.85V)
|
||||
*/
|
||||
u16 ADC_GetBatteryVoltage_mV(void);
|
||||
|
||||
/**
|
||||
* @brief 获取电池电量百分比 (0% ~ 100%)
|
||||
* @return u8 电量百分比 (0 ~ 100)
|
||||
*/
|
||||
u8 ADC_GetBatteryPercent(void);
|
||||
|
||||
/**
|
||||
* @brief 检测 USB 充电是否插入
|
||||
* @return bit 0=未充电, 1=充电中
|
||||
* @note TP4054 CHRG 引脚开漏输出,低电平时表示正在充电。
|
||||
* 需要 MCU 内部上拉或外部上拉电阻。
|
||||
*/
|
||||
bit Is_Charging(void);
|
||||
|
||||
#endif // __ADC_H__
|
||||
206
Drivers/lcd.c
206
Drivers/lcd.c
@@ -3,108 +3,108 @@
|
||||
#include "../App/config.h"
|
||||
#include "intrins.h"
|
||||
|
||||
// ====== LHS114TC-IF03 (ST7789V) <EFBFBD><EFBFBD>Ļ SPI <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӳ<EFBFBD><EFBFBD> ======
|
||||
sbit LCD_RST = P1^0; // <EFBFBD><EFBFBD>ĻӲ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> (RESET)<29><><EFBFBD>͵<EFBFBD>ƽ<EFBFBD><C6BD>λ
|
||||
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_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_SCL = P1^5; // SPI <EFBFBD><EFBFBD><EFBFBD>й<EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> (SCLK)
|
||||
sbit LCD_CS = P1^6; // SPI <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƭѡѡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> (CS)<29><><EFBFBD>͵<EFBFBD>ƽѡͨ<D1A1><CDA8>Ļ
|
||||
// ====== LHS114TC-IF03 (ST7789V) 屏幕 SPI 引脚映射 ======
|
||||
sbit LCD_RST = P1^0; // 屏幕硬件复位引脚 (RESET),低电平复位
|
||||
sbit LCD_DCX = P1^1; // 串行数据 / 命令选择控制线 (1:数据, 0:指令)
|
||||
sbit LCD_SDI = P1^4; // SPI 主机输出从机输入数据线 (MOSI)
|
||||
sbit LCD_SCL = P1^5; // SPI 串行工作时钟线 (SCLK)
|
||||
sbit LCD_CS = P1^6; // SPI 物理片选选择端 (CS),低电平选通屏幕
|
||||
|
||||
// ====== LHS114TC-IF03 LEDA <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ======
|
||||
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>)
|
||||
// ====== LHS114TC-IF03 LEDA 背光控制引脚 ======
|
||||
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 Delay10us(void);
|
||||
|
||||
/**
|
||||
* @brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><EFBFBD> 8 λ<><CEBB><EFBFBD><EFBFBD> SPI <20>ֽ<EFBFBD><D6BD><EFBFBD><EFBFBD>ݷ<EFBFBD><DDB7><EFBFBD>
|
||||
* @param dat <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>͵<EFBFBD> 1 <20>ֽ<EFBFBD><D6BD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @brief 软件模拟 8 位串行 SPI 字节数据发送
|
||||
* @param dat 待发送的 1 字节数据
|
||||
*/
|
||||
void SPI_SendData(u8 dat)
|
||||
{
|
||||
u8 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)
|
||||
LCD_SDI = 1;
|
||||
else
|
||||
LCD_SDI = 0;
|
||||
_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_();
|
||||
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)
|
||||
{
|
||||
LCD_CS = 0; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƭѡ
|
||||
LCD_DCX = 0; // <EFBFBD>л<EFBFBD>Ϊָ<EFBFBD><EFBFBD><EFBFBD>ģʽ
|
||||
LCD_CS = 0; // 开启片选
|
||||
LCD_DCX = 0; // 切换为指令传输模式
|
||||
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)
|
||||
{
|
||||
LCD_CS = 0; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƭѡ
|
||||
LCD_DCX = 1; // <EFBFBD>л<EFBFBD>Ϊ<EFBFBD><EFBFBD><EFBFBD>ݴ<EFBFBD><EFBFBD><EFBFBD>ģʽ
|
||||
LCD_CS = 0; // 开启片选
|
||||
LCD_DCX = 1; // 切换为数据传输模式
|
||||
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)
|
||||
{
|
||||
LCD_RST = 1;
|
||||
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);
|
||||
LCD_RST = 1; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
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>
|
||||
LCD_RST = 1; // 重新拉高引脚
|
||||
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)
|
||||
{
|
||||
// <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;
|
||||
|
||||
// <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));
|
||||
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_SCL = 1;
|
||||
LCD_RST = 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> ======
|
||||
WriteComm(0x11); // Sleep Out <EFBFBD>˳<EFBFBD>˯<EFBFBD><EFBFBD>
|
||||
Delay_ms(120); // <EFBFBD>˳<EFBFBD>˯<EFBFBD>ߺ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> 120ms
|
||||
// ====== 写入 ST7789V 原厂寄存器初始化指令序列 ======
|
||||
WriteComm(0x11); // Sleep Out 退出睡眠
|
||||
Delay_ms(120); // 退出睡眠后必须延时至少 120ms
|
||||
|
||||
// Memory Data Access Control (MADCTL) ɨ<EFBFBD>跽<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
// 0x00: <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>£<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ң<EFBFBD>RGB <EFBFBD><EFBFBD>ʽ
|
||||
// Memory Data Access Control (MADCTL) 扫描方向控制
|
||||
// 0x00: 从上至下,从左至右,RGB 格式
|
||||
WriteComm(0x36);
|
||||
WriteData(0x00);
|
||||
|
||||
// Interface Pixel Format (COLMOD)
|
||||
// 0x05 <EFBFBD><EFBFBD><EFBFBD><EFBFBD> 16-bit/pixel (RGB565<EFBFBD><EFBFBD>ʽ)
|
||||
// 0x05 代表 16-bit/pixel (RGB565格式)
|
||||
WriteComm(0x3A);
|
||||
WriteData(0x05);
|
||||
|
||||
@@ -154,50 +154,50 @@ void LCD_Init(void)
|
||||
WriteData(0xA4);
|
||||
WriteData(0xA1);
|
||||
|
||||
// Positive Gamma Correction (E0h) <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>٤<EFBFBD><EFBFBD>
|
||||
// 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) <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>٤<EFBFBD><EFBFBD>
|
||||
// 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) <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԣ<EFBFBD>ST7789 IPS <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
// Display Inversion On (21h) 开启反显,ST7789 IPS 屏常用
|
||||
WriteComm(0x21);
|
||||
|
||||
// Display On (29h) <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ
|
||||
// Display On (29h) 开启显示
|
||||
WriteComm(0x29);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
// <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_end += LCD_X_OFFSET;
|
||||
y_start += 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);
|
||||
WriteData((u8)(x_start >> 8));
|
||||
WriteData((u8)(x_start & 0xFF));
|
||||
WriteData((u8)(x_end >> 8));
|
||||
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);
|
||||
WriteData((u8)(y_start >> 8));
|
||||
WriteData((u8)(y_start & 0xFF));
|
||||
@@ -206,26 +206,26 @@ void LCD_SetWindow(u16 x_start, u16 x_end, u16 y_start, u16 y_end)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief <EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD>ɫ<EFBFBD><EFBFBD>ˢд<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȫ<EFBFBD><EFBFBD>Ļ
|
||||
* @param color 16λ RGB565 <EFBFBD><EFBFBD>ɫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ
|
||||
* @brief 用指定色彩刷写清空全屏幕
|
||||
* @param color 16位 RGB565 颜色填充值
|
||||
*/
|
||||
void LCD_Clear(u16 color)
|
||||
{
|
||||
u16 i, j;
|
||||
LCD_SetWindow(0, LCD_WIDTH - 1, 0, LCD_HEIGHT - 1);
|
||||
WriteComm(0x2C); // <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(j = 0; j < LCD_HEIGHT; j++)
|
||||
{
|
||||
WriteData((u8)(color >> 8)); // <EFBFBD><EFBFBD><EFBFBD><EFBFBD> RGB565 <EFBFBD><EFBFBD> 8 λ<EFBFBD>ֽ<EFBFBD>
|
||||
WriteData((u8)(color & 0xFF)); // <EFBFBD><EFBFBD><EFBFBD><EFBFBD> RGB565 <EFBFBD><EFBFBD> 8 λ<EFBFBD>ֽ<EFBFBD>
|
||||
WriteData((u8)(color >> 8)); // 发送 RGB565 高 8 位字节
|
||||
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)
|
||||
{
|
||||
@@ -234,8 +234,8 @@ void LCD_FillRect(u16 x, u16 y, u16 w, u16 h, u16 color)
|
||||
u8 cl = (u8)(color & 0xFF);
|
||||
|
||||
LCD_SetWindow(x, x + w - 1, y, y + h - 1);
|
||||
WriteComm(0x2C); // д<EFBFBD><EFBFBD><EFBFBD>Դ<EFBFBD>
|
||||
count = w * h; // ʵ<EFBFBD>ľ<EFBFBD><EFBFBD>ΰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
WriteComm(0x2C); // 写入显存
|
||||
count = w * h; // 实心矩形包含的总像素数
|
||||
for(i = 0; i < count; i++)
|
||||
{
|
||||
WriteData(ch);
|
||||
@@ -244,55 +244,55 @@ void LCD_FillRect(u16 x, u16 y, u16 w, u16 h, u16 color)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief <EFBFBD><EFBFBD><EFBFBD>ƿ<EFBFBD><EFBFBD>ľ<EFBFBD><EFBFBD>α߽<EFBFBD><EFBFBD><EFBFBD>
|
||||
* @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>
|
||||
* @brief 绘制空心矩形边界框
|
||||
* @details 通过组合调用四次 LCD_FillRect 实心线来绘制矩形的上、下、左、右四条边。
|
||||
*/
|
||||
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 + h - 1, w, 1, color); // <EFBFBD>±߿<EFBFBD><EFBFBD><EFBFBD>
|
||||
LCD_FillRect(x, y, 1, h, color); // <EFBFBD><EFBFBD><EFBFBD>߿<EFBFBD><EFBFBD><EFBFBD>
|
||||
LCD_FillRect(x + w - 1, y, 1, h, color); // <EFBFBD>ұ߿<EFBFBD><EFBFBD><EFBFBD>
|
||||
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 <EFBFBD><EFBFBD>Ⱦ<EFBFBD><EFBFBD>ʾһ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD><EFBFBD>ģλͼ<EFBFBD><EFBFBD>Դ
|
||||
* @param x <EFBFBD><EFBFBD>Ⱦ<EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> X
|
||||
* @param y <EFBFBD><EFBFBD>Ⱦ<EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> Y
|
||||
* @param w λͼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> (<28><>λ: <20><><EFBFBD>ص<EFBFBD><D8B5><EFBFBD>)
|
||||
* @param h λͼ<EFBFBD>߶<EFBFBD> (<28><>λ: <20><><EFBFBD>ص<EFBFBD><D8B5><EFBFBD>)
|
||||
* @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 color 16λǰ<EFBFBD><EFBFBD>ɫ
|
||||
* @param bg_color 16λ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ
|
||||
* @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; // <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 cl = (u8)(color & 0xFF);
|
||||
u8 bgh = (u8)(bg_color >> 8);
|
||||
u8 bgl = (u8)(bg_color & 0xFF);
|
||||
|
||||
LCD_SetWindow(x, x + w - 1, y, y + h - 1); // <EFBFBD><EFBFBD><EFBFBD>ƻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ洰<EFBFBD><EFBFBD>
|
||||
LCD_SetWindow(x, x + w - 1, y, y + h - 1); // 限制绘制显存窗口
|
||||
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(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>
|
||||
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>ǰ)
|
||||
u16 byte_idx = r * byte_width + (c / 8); // 定位当前像素所在单色图数组中的字节索引
|
||||
u8 bit_shift = 7 - (c % 8); // 定位该像素在该字节中对应的 bit 位置 (高位在前)
|
||||
|
||||
if(bmp[byte_idx] & (1 << bit_shift))
|
||||
{
|
||||
WriteData(ch); // <EFBFBD><EFBFBD>λΪ 1<><31><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0>ɫ
|
||||
WriteData(ch); // 该位为 1,发送前景色
|
||||
WriteData(cl);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteData(bgh); // <EFBFBD><EFBFBD>λΪ 0<><30><EFBFBD><EFBFBD><EFBFBD>ͱ<EFBFBD><CDB1><EFBFBD>ɫ
|
||||
WriteData(bgh); // 该位为 0,发送背景色
|
||||
WriteData(bgl);
|
||||
}
|
||||
}
|
||||
@@ -300,30 +300,30 @@ void LCD_DrawMonoBitmap(u16 x, u16 y, u16 w, u16 h, unsigned char code *bmp, u16
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief <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>
|
||||
* @param ch ASCII <EFBFBD>ַ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @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; // ƫ<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_l = (u8)(color & 0xFF);
|
||||
u8 b_h = (u8)(bg_color >> 8);
|
||||
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);
|
||||
WriteComm(0x2C);
|
||||
|
||||
// ѭ<EFBFBD><EFBFBD> 16 <EFBFBD>У<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 8x16 <EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
// 循环 16 行,绘制 8x16 字型
|
||||
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++)
|
||||
{
|
||||
// <EFBFBD>Ӹߵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>е<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ص<EFBFBD>
|
||||
// 从高到低依次提取单行中的像素点
|
||||
if(line & (0x80 >> c))
|
||||
{
|
||||
WriteData(f_h);
|
||||
@@ -339,37 +339,37 @@ void LCD_ShowChar8x16(u16 x, u16 y, char ch, u16 color, u16 bg_color)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief <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)
|
||||
{
|
||||
while(*str)
|
||||
{
|
||||
LCD_ShowChar8x16(x, y, *str, color, bg_color); // <EFBFBD><EFBFBD>Ⱦ<EFBFBD><EFBFBD>ǰ<EFBFBD>ַ<EFBFBD>
|
||||
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>
|
||||
str++; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD>
|
||||
LCD_ShowChar8x16(x, y, *str, color, bg_color); // 渲染当前字符
|
||||
x += 8; // X 轴起始位置偏移 8 像素,准备绘制下一个字符
|
||||
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)
|
||||
{
|
||||
u16 len = 0;
|
||||
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)
|
||||
// <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);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
@@ -383,22 +383,22 @@ void LCD_ShowChar16x32(u16 x, u16 y, char ch, u16 color, u16 bg_color)
|
||||
|
||||
if(ch < 0x20 || ch > 0x7E) return;
|
||||
|
||||
LCD_SetWindow(x, x + 16 - 1, y, y + 32 - 1); // <EFBFBD><EFBFBD><EFBFBD><EFBFBD> 16x32 <EFBFBD>Ļ<EFBFBD>ͼ<EFBFBD>Ӵ<EFBFBD>
|
||||
LCD_SetWindow(x, x + 16 - 1, y, y + 32 - 1); // 锁定 16x32 的绘图视窗
|
||||
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++)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
// <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++)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
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)
|
||||
{
|
||||
while(*str)
|
||||
{
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
u16 len = 0;
|
||||
char *p = str;
|
||||
while(*p++) len++; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
while(*p++) len++; // 计算字数
|
||||
|
||||
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);
|
||||
else
|
||||
// ˮƽ<EFBFBD><EFBFBD><EFBFBD>м<EFBFBD><EFBFBD><EFBFBD>
|
||||
// 水平居中计算
|
||||
LCD_ShowString16x32((LCD_WIDTH - len * 16) / 2, y, str, color, bg_color);
|
||||
}
|
||||
|
||||
434
Drivers/pcf8563.c
Normal file
434
Drivers/pcf8563.c
Normal file
@@ -0,0 +1,434 @@
|
||||
#include "pcf8563.h"
|
||||
#include "../App/system.h"
|
||||
|
||||
// 动态引脚对调全局标志 (0: 默认 SCL=P3.2, SDA=P3.3; 1: 对调 SCL=P3.3, SDA=P3.2)
|
||||
volatile u8 i2c_swapped = 0;
|
||||
|
||||
static void SCL_W(u8 val)
|
||||
{
|
||||
if (!i2c_swapped) P32 = val;
|
||||
else P33 = val;
|
||||
}
|
||||
|
||||
static void SDA_W(u8 val)
|
||||
{
|
||||
if (!i2c_swapped) P33 = val;
|
||||
else P32 = val;
|
||||
}
|
||||
|
||||
static u8 SDA_R(void)
|
||||
{
|
||||
if (!i2c_swapped) return P33;
|
||||
else return P32;
|
||||
}
|
||||
|
||||
// 动态延时参数 (单位: us)
|
||||
volatile u8 i2c_delay_us = 30;
|
||||
|
||||
/**
|
||||
* @brief I2C 产生起始信号 (START)
|
||||
*/
|
||||
static void I2C_Start(void)
|
||||
{
|
||||
SDA_W(1);
|
||||
SCL_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
SDA_W(0);
|
||||
Delay_us(i2c_delay_us);
|
||||
SCL_W(0);
|
||||
Delay_us(i2c_delay_us);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C 产生停止信号 (STOP)
|
||||
*/
|
||||
static void I2C_Stop(void)
|
||||
{
|
||||
SDA_W(0);
|
||||
Delay_us(i2c_delay_us);
|
||||
SCL_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
SDA_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C 发送应答/非应答信号
|
||||
* @param ack 1: ACK(0), 0: NACK(1)
|
||||
*/
|
||||
static void I2C_SendAck(bit ack)
|
||||
{
|
||||
SDA_W(!ack);
|
||||
Delay_us(i2c_delay_us);
|
||||
SCL_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
SCL_W(0);
|
||||
Delay_us(i2c_delay_us);
|
||||
SDA_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C 等待从机应答 (Wait ACK)
|
||||
* @return bit 1: 收到应答, 0: 未收到应答
|
||||
*/
|
||||
static bit I2C_WaitAck(void)
|
||||
{
|
||||
u8 retry = 0;
|
||||
SDA_W(1); // 释放 SDA 接收 ACK
|
||||
Delay_us(i2c_delay_us);
|
||||
SCL_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
while (SDA_R()) {
|
||||
retry++;
|
||||
Delay_us(5);
|
||||
if (retry > 250) {
|
||||
I2C_Stop();
|
||||
return 0; // 超时未收到 ACK 应答,立即优雅退出,防止死锁
|
||||
}
|
||||
}
|
||||
SCL_W(0);
|
||||
Delay_us(i2c_delay_us);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C 发送单字节数据
|
||||
* @param dat 待发送 of 字节数据
|
||||
*/
|
||||
static void I2C_SendByte(u8 dat)
|
||||
{
|
||||
u8 i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
SDA_W((dat & 0x80) ? 1 : 0);
|
||||
Delay_us(i2c_delay_us);
|
||||
SCL_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
SCL_W(0);
|
||||
Delay_us(i2c_delay_us);
|
||||
dat <<= 1;
|
||||
}
|
||||
SDA_W(1); // 释放 SDA 接收 ACK
|
||||
Delay_us(i2c_delay_us);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C 读取单字节数据
|
||||
* @return u8 接收到的字节数据
|
||||
*/
|
||||
static u8 I2C_ReadByte(void)
|
||||
{
|
||||
u8 i, dat = 0;
|
||||
SDA_W(1); // 设为输入
|
||||
Delay_us(i2c_delay_us);
|
||||
for (i = 0; i < 8; i++) {
|
||||
dat <<= 1;
|
||||
SCL_W(1);
|
||||
Delay_us(i2c_delay_us);
|
||||
if (SDA_R()) {
|
||||
dat |= 0x01;
|
||||
}
|
||||
SCL_W(0);
|
||||
Delay_us(i2c_delay_us);
|
||||
}
|
||||
return dat;
|
||||
}
|
||||
|
||||
/* ====== BCD 与二进制 10 进制互转辅助函数 ====== */
|
||||
|
||||
static u8 BCD2DEC(u8 bcd)
|
||||
{
|
||||
return ((bcd >> 4) * 10) + (bcd & 0x0F);
|
||||
}
|
||||
|
||||
static u8 DEC2BCD(u8 dec)
|
||||
{
|
||||
return (((dec / 10) << 4) | (dec % 10));
|
||||
}
|
||||
|
||||
static void I2C_ReleaseBus(void)
|
||||
{
|
||||
u8 i;
|
||||
SDA_W(1);
|
||||
SCL_W(1);
|
||||
Delay_us(30);
|
||||
|
||||
// 如果 SDA 被拉低,说明总线被死锁占用
|
||||
if (SDA_R() == 0) {
|
||||
for (i = 0; i < 9; i++) {
|
||||
SCL_W(0);
|
||||
Delay_us(30);
|
||||
SCL_W(1);
|
||||
Delay_us(30);
|
||||
if (SDA_R() == 1) {
|
||||
break; // 从机已释放总线,提前退出
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 强制产生停止条件恢复空闲
|
||||
SDA_W(0);
|
||||
Delay_us(30);
|
||||
SCL_W(1);
|
||||
Delay_us(30);
|
||||
SDA_W(1);
|
||||
Delay_us(30);
|
||||
}
|
||||
|
||||
/* ====== PCF8563 驱动核心接口 ====== */
|
||||
|
||||
bit PCF8563_Init(void)
|
||||
{
|
||||
// 1. 默认引脚尝试 (SCL=P3.2, SDA=P3.3)
|
||||
i2c_swapped = 0;
|
||||
I2C_ReleaseBus(); // 强力总线死锁释放
|
||||
|
||||
I2C_Start();
|
||||
I2C_SendByte(PCF8563_ADDR_WRITE);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[RTC] Detected Normal pin mapping (SCL=P32, SDA=P33)\r\n");
|
||||
} else {
|
||||
// 2. 尝试对调引脚 (SCL=P3.3, SDA=P3.2)
|
||||
I2C_Stop();
|
||||
i2c_swapped = 1;
|
||||
I2C_ReleaseBus(); // 强力总线死锁释放
|
||||
|
||||
I2C_Start();
|
||||
I2C_SendByte(PCF8563_ADDR_WRITE);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[RTC] Detected SWAPPED pin mapping (SCL=P33, SDA=P32)!\r\n");
|
||||
} else {
|
||||
// 恢复默认,防止干扰
|
||||
i2c_swapped = 0;
|
||||
Uart_SendString("[RTC-ERR] PCF8563 Offline on both normal/swapped pins!\r\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
I2C_SendByte(REG_CTRL_STATUS1);
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
I2C_SendByte(0x00); // 正常模式,不停止芯片计数
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
I2C_Stop();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PCF8563_ScanDiagnostic(void)
|
||||
{
|
||||
u16 addr;
|
||||
u8 found = 0;
|
||||
|
||||
Uart_SendString("[I2C-SCAN] --- 1. Scanning with default pins (SCL=P32, SDA=P33) ---\r\n");
|
||||
i2c_swapped = 0;
|
||||
for (addr = 0x02; addr < 0x100; addr += 2) {
|
||||
I2C_Start();
|
||||
I2C_SendByte((u8)addr);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[I2C-SCAN] Device Found: 0x");
|
||||
Uart_SendHex8((u8)addr);
|
||||
Uart_SendString("\r\n");
|
||||
found = 1;
|
||||
}
|
||||
I2C_Stop();
|
||||
Delay_ms(2);
|
||||
}
|
||||
|
||||
Uart_SendString("[I2C-SCAN] --- 2. Scanning with SWAPPED pins (SCL=P33, SDA=P32) ---\r\n");
|
||||
i2c_swapped = 1;
|
||||
for (addr = 0x02; addr < 0x100; addr += 2) {
|
||||
I2C_Start();
|
||||
I2C_SendByte((u8)addr);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[I2C-SCAN] Device Found: 0x");
|
||||
Uart_SendHex8((u8)addr);
|
||||
Uart_SendString("\r\n");
|
||||
found = 1;
|
||||
}
|
||||
I2C_Stop();
|
||||
Delay_ms(2);
|
||||
}
|
||||
|
||||
// 扫描结束后,根据 init 侦测结果重新绑定,此处临时归零
|
||||
i2c_swapped = 0;
|
||||
Uart_SendString("[I2C-SCAN] Dual-mode Address Scan Completed!\r\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从 PCF8563 读取当前时间
|
||||
*/
|
||||
bit PCF8563_ReadTime(u8 *hour, u8 *min, u8 *sec)
|
||||
{
|
||||
u8 raw_sec, raw_min, raw_hour;
|
||||
|
||||
I2C_Start();
|
||||
I2C_SendByte(PCF8563_ADDR_WRITE);
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
// 设置起始读取地址为 0x02 (REG_VL_SECONDS)
|
||||
I2C_SendByte(REG_VL_SECONDS);
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
// 重新产生 Start 读取数据
|
||||
I2C_Start();
|
||||
I2C_SendByte(PCF8563_ADDR_READ);
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
raw_sec = I2C_ReadByte();
|
||||
I2C_SendAck(1); // 发送 ACK
|
||||
|
||||
raw_min = I2C_ReadByte();
|
||||
I2C_SendAck(1); // 发送 ACK
|
||||
|
||||
raw_hour = I2C_ReadByte();
|
||||
I2C_SendAck(0); // 发送 NACK 结束连续读取
|
||||
|
||||
I2C_Stop();
|
||||
|
||||
// 解析 BCD 数据 (掩码屏蔽无效位)
|
||||
*sec = BCD2DEC(raw_sec & 0x7F);
|
||||
*min = BCD2DEC(raw_min & 0x7F);
|
||||
*hour = BCD2DEC(raw_hour & 0x3F);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将时间精准写入 PCF8563 RTC 芯片
|
||||
*/
|
||||
bit PCF8563_WriteTime(u8 hour, u8 min, u8 sec)
|
||||
{
|
||||
I2C_Start();
|
||||
I2C_SendByte(PCF8563_ADDR_WRITE);
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
// 定位写入起始寄存器 0x02
|
||||
I2C_SendByte(REG_VL_SECONDS);
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
// 写入秒
|
||||
I2C_SendByte(DEC2BCD(sec & 0x7F));
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
// 写入分
|
||||
I2C_SendByte(DEC2BCD(min & 0x7F));
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
// 写入时
|
||||
I2C_SendByte(DEC2BCD(hour & 0x3F));
|
||||
if (!I2C_WaitAck()) return 0;
|
||||
|
||||
I2C_Stop();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bit PCF8563_TryReadDiagnostic(void)
|
||||
{
|
||||
u8 val = 0;
|
||||
|
||||
Uart_SendString("[I2C-DIAG] --- Test 1: Trying 30us Delay (30kHz) ---\r\n");
|
||||
i2c_delay_us = 30;
|
||||
|
||||
i2c_swapped = 0;
|
||||
I2C_ReleaseBus();
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA2);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[I2C-DIAG] (swapped=0, 30us) 0xA2 ACK OK\r\n");
|
||||
I2C_SendByte(0x00);
|
||||
if (I2C_WaitAck()) {
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA3);
|
||||
if (I2C_WaitAck()) {
|
||||
val = I2C_ReadByte();
|
||||
I2C_SendAck(0);
|
||||
I2C_Stop();
|
||||
Uart_SendString("[I2C-DIAG] (swapped=0, 30us) Success! Reg 0x00=0x");
|
||||
Uart_SendHex8(val);
|
||||
Uart_SendString("\r\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
I2C_Stop();
|
||||
|
||||
i2c_swapped = 1;
|
||||
I2C_ReleaseBus();
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA2);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[I2C-DIAG] (swapped=1, 30us) 0xA2 ACK OK\r\n");
|
||||
I2C_SendByte(0x00);
|
||||
if (I2C_WaitAck()) {
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA3);
|
||||
if (I2C_WaitAck()) {
|
||||
val = I2C_ReadByte();
|
||||
I2C_SendAck(0);
|
||||
I2C_Stop();
|
||||
Uart_SendString("[I2C-DIAG] (swapped=1, 30us) Success! Reg 0x00=0x");
|
||||
Uart_SendHex8(val);
|
||||
Uart_SendString("\r\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
I2C_Stop();
|
||||
|
||||
Uart_SendString("[I2C-DIAG] --- Test 2: Retrying with ultra-slow 200us Delay (2.5kHz) ---\r\n");
|
||||
i2c_delay_us = 200;
|
||||
|
||||
i2c_swapped = 0;
|
||||
I2C_ReleaseBus();
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA2);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[I2C-DIAG] (swapped=0, 200us) 0xA2 ACK OK\r\n");
|
||||
I2C_SendByte(0x00);
|
||||
if (I2C_WaitAck()) {
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA3);
|
||||
if (I2C_WaitAck()) {
|
||||
val = I2C_ReadByte();
|
||||
I2C_SendAck(0);
|
||||
I2C_Stop();
|
||||
Uart_SendString("[I2C-DIAG] (swapped=0, 200us) Success! Reg 0x00=0x");
|
||||
Uart_SendHex8(val);
|
||||
Uart_SendString("\r\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
I2C_Stop();
|
||||
|
||||
i2c_swapped = 1;
|
||||
I2C_ReleaseBus();
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA2);
|
||||
if (I2C_WaitAck()) {
|
||||
Uart_SendString("[I2C-DIAG] (swapped=1, 200us) 0xA2 ACK OK\r\n");
|
||||
I2C_SendByte(0x00);
|
||||
if (I2C_WaitAck()) {
|
||||
I2C_Start();
|
||||
I2C_SendByte(0xA3);
|
||||
if (I2C_WaitAck()) {
|
||||
val = I2C_ReadByte();
|
||||
I2C_SendAck(0);
|
||||
I2C_Stop();
|
||||
Uart_SendString("[I2C-DIAG] (swapped=1, 200us) Success! Reg 0x00=0x");
|
||||
Uart_SendHex8(val);
|
||||
Uart_SendString("\r\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
I2C_Stop();
|
||||
|
||||
// 恢复默认设置
|
||||
i2c_swapped = 0;
|
||||
i2c_delay_us = 30;
|
||||
Uart_SendString("[I2C-DIAG] Both 30us and 200us diagnostic trial failed.\r\n");
|
||||
return 0;
|
||||
}
|
||||
59
Drivers/pcf8563.h
Normal file
59
Drivers/pcf8563.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef __PCF8563_H__
|
||||
#define __PCF8563_H__
|
||||
|
||||
#include "../App/config.h"
|
||||
|
||||
/* ====== PCF8563 器件地址与寄存器定义 ====== */
|
||||
#define PCF8563_ADDR_READ 0xA3 // PCF8563 读地址 (0xA2 | 0x01)
|
||||
#define PCF8563_ADDR_WRITE 0xA2 // PCF8563 写地址 (0xA2)
|
||||
|
||||
#define REG_CTRL_STATUS1 0x00 // 控制/状态寄存器 1
|
||||
#define REG_CTRL_STATUS2 0x01 // 控制/状态寄存器 2
|
||||
#define REG_VL_SECONDS 0x02 // 秒寄存器 (bit7 为 VL 保证标志)
|
||||
#define REG_MINUTES 0x03 // 分钟寄存器
|
||||
#define REG_HOURS 0x04 // 小时寄存器
|
||||
#define REG_DAYS 0x05 // 日寄存器
|
||||
#define REG_WEEKDAYS 0x06 // 星期寄存器
|
||||
#define REG_MONTHS 0x07 // 月份寄存器 (bit7 为 C 世纪位)
|
||||
#define REG_YEARS 0x08 // 年份寄存器
|
||||
|
||||
/**
|
||||
* @brief 初始化 PCF8563 RTC 芯片
|
||||
* @details 初始化 I2C 管脚高电平,并检测芯片启动状态
|
||||
* @return bit 1: 芯片响应正常, 0: 通讯异常
|
||||
*/
|
||||
bit PCF8563_Init(void);
|
||||
|
||||
/**
|
||||
* @brief 从 PCF8563 芯片读取当前时间 (时、分、秒)
|
||||
* @param hour 小时指针 (0~23)
|
||||
* @param min 分钟指针 (0~59)
|
||||
* @param sec 秒钟指针 (0~59)
|
||||
* @return bit 1: 读取成功, 0: 通讯失败
|
||||
*/
|
||||
bit PCF8563_ReadTime(u8 *hour, u8 *min, u8 *sec);
|
||||
|
||||
/**
|
||||
* @brief 将指定的时间 (时、分、秒) 写入 PCF8563 芯片
|
||||
* @param hour 小时 (0~23)
|
||||
* @param min 分钟 (0~59)
|
||||
* @param sec 秒钟 (0~59)
|
||||
* @return bit 1: 写入成功, 0: 通讯失败
|
||||
*/
|
||||
bit PCF8563_WriteTime(u8 hour, u8 min, u8 sec);
|
||||
|
||||
/**
|
||||
* @brief I2C 总线器件扫描诊断函数
|
||||
* @details 逐个发送 I2C 写地址,检测总线上是否有响应的器件,并在串口打印发现的设备地址
|
||||
*/
|
||||
void PCF8563_ScanDiagnostic(void);
|
||||
|
||||
/**
|
||||
* @brief 尝试直接用最底层时序试读 RTC 控制寄存器并输出 ACK 调试日志
|
||||
*/
|
||||
bit PCF8563_TryReadDiagnostic(void);
|
||||
|
||||
// 动态引脚对调标志 (由 pcf8563.c 维护,0: 默认, 1: 互换)
|
||||
extern volatile u8 i2c_swapped;
|
||||
|
||||
#endif // __PCF8563_H__
|
||||
237
Drivers/rf.c
237
Drivers/rf.c
@@ -3,15 +3,15 @@
|
||||
#include "../App/rgb.h"
|
||||
#include "intrins.h"
|
||||
|
||||
// ATR5179 <EFBFBD><EFBFBD>Ƶǰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߿<EFBFBD><EFBFBD>ؿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŷ<EFBFBD><EFBFBD><EFBFBD>
|
||||
sbit RF_TX = P2^0; // V1 <EFBFBD><EFBFBD><EFBFBD>ƶ<EFBFBD> (1: ѡͨ<D1A1><CDA8><EFBFBD>ߵ<EFBFBD><DFB5><EFBFBD><EFBFBD><EFBFBD> TX ·<EFBFBD><EFBFBD>)
|
||||
sbit RF_RX = P3^7; // V2 <EFBFBD><EFBFBD><EFBFBD>ƶ<EFBFBD> (1: ѡͨ<D1A1><CDA8><EFBFBD>ߵ<EFBFBD><DFB5><EFBFBD><EFBFBD><EFBFBD> RX ·<EFBFBD><EFBFBD>)
|
||||
// ATR5179 射频前端天线开关控制引脚定义
|
||||
sbit RF_TX = P2^0; // V1 控制端 (1: 选通天线到发射 TX 路径)
|
||||
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>)
|
||||
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_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)
|
||||
// 射频芯片数据与电源控制引脚定义 (根据新版 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)
|
||||
|
||||
// <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_ms(u16 ms);
|
||||
extern u16 GetTimer0_Safe(void);
|
||||
@@ -24,156 +24,176 @@ extern void Uart_SendHex32(u32 val);
|
||||
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)
|
||||
{
|
||||
// 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;
|
||||
|
||||
// 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));
|
||||
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)
|
||||
// <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>
|
||||
// 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. <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϵͳĬ<EFBFBD>ϵij<EFBFBD>ʼ<EFBFBD><EFBFBD>ƽ
|
||||
RF_TX = 0; // <EFBFBD>Ͽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͨ·
|
||||
SHUT = 1; // Ĭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> SHUT<EFBFBD><EFBFBD>ʹ LR690L <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߹ر<EFBFBD>״̬
|
||||
RF_RX = 0; // <EFBFBD>Ͽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͨ·
|
||||
RF_TX_DAT = 0; // <EFBFBD><EFBFBD><EFBFBD>Ʒ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>õ<EFBFBD>
|
||||
// 3. 设置系统默认的初始电平
|
||||
RF_TX = 0; // 断开发射天线通路
|
||||
SHUT = 1; // 默认拉高 SHUT,使 LR690L 处于休眠关闭状态
|
||||
RF_RX = 0; // 断开接收天线通路
|
||||
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>
|
||||
* @param mode <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģʽ (0-<2D><><EFBFBD>߸<EFBFBD><DFB8><EFBFBD>, 1-<2D><><EFBFBD>չ<EFBFBD><D5B9><EFBFBD>, 2-<2D><><EFBFBD>乤<EFBFBD><E4B9A4>)
|
||||
* @brief 配置无线射频收发系统的运行模式与天线切换
|
||||
* @param mode 运行模式 (0-休眠隔离, 1-接收工作, 2-发射工作)
|
||||
*/
|
||||
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>
|
||||
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_TX = 0; // <EFBFBD>رշ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߿<EFBFBD><EFBFBD><EFBFBD>
|
||||
SHUT = 1; // 强制拉高 SHUT 引脚,使 LR690L 彻底进入低功耗待机
|
||||
RF_RX = 0; // 关闭接收天线开关,防止外界电磁杂波串入接收电路
|
||||
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>
|
||||
RF_TX = 0; // <EFBFBD>رշ<EFBFBD><EFBFBD>俪<EFBFBD><EFBFBD>
|
||||
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>
|
||||
SHUT = 0; // 拉低 SHUT 使能 LR690L 芯片,开始解调接收
|
||||
RF_TX = 0; // 关闭发射开关
|
||||
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>
|
||||
RF_RX = 0; // <EFBFBD>رս<EFBFBD><EFBFBD>տ<EFBFBD><EFBFBD><EFBFBD>
|
||||
RF_TX = 1; // ѡͨ<EFBFBD><EFBFBD><EFBFBD>ߵ<EFBFBD> TX <20><><EFBFBD><EFBFBD>·<EFBFBD><C2B7><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9><EFBFBD><EFBFBD><EFBFBD>߷<EFBFBD><DFB7><EFBFBD>
|
||||
SHUT = 1; // 接收机停机,防止发射强信号顶坏接收前端
|
||||
RF_RX = 0; // 关闭接收开关
|
||||
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)
|
||||
{
|
||||
u8 i;
|
||||
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);
|
||||
|
||||
// 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;
|
||||
Delay_us(350);
|
||||
RF_Delay_us(350);
|
||||
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++)
|
||||
{
|
||||
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;
|
||||
Delay_us(1050);
|
||||
RF_Delay_us(1050);
|
||||
RF_TX_DAT = 0;
|
||||
Delay_us(350);
|
||||
RF_Delay_us(350);
|
||||
}
|
||||
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;
|
||||
Delay_us(350);
|
||||
RF_Delay_us(350);
|
||||
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)
|
||||
{
|
||||
u8 i;
|
||||
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>
|
||||
Delay_ms(5); // <20>ȶ<EFBFBD><C8B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶͨ·
|
||||
u8 r;
|
||||
bit ea_bak = EA; // 备份并屏蔽全局中断,确保发射高低电平周期的绝对纯净与防抖
|
||||
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>
|
||||
for (i = 0; i < 10; i++)
|
||||
RF_SetMode(2); // 切换为发射状态,接通天线与射频模块通道
|
||||
Delay_ms(5); // 稍作延时使射频通路稳定
|
||||
|
||||
// 连续发射 25 帧以保证接收设备顺利锁定并解码
|
||||
for (r = 0; r < 25; r++)
|
||||
{
|
||||
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_SetMode(1); // <EFBFBD><EFBFBD><EFBFBD>»ָ<EFBFBD>Ϊ<EFBFBD><EFBFBD><EFBFBD>ռ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̬<EFBFBD><EFBFBD><EFBFBD>Ա<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ⷴ<EFBFBD><EFBFBD>
|
||||
RF_TX_DAT = 0; // 结束发射,拉低数据脚
|
||||
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 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;
|
||||
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;
|
||||
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)
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϵͳ Timer0 Ĭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ 12T ģʽ (1 Tick = 12 / 24MHz = 0.5us)
|
||||
// 换算 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; // <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)
|
||||
{
|
||||
if (GetTimer0_Safe() > timeout_ticks)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
// ģ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶ<EFBFBD>ź<EFBFBD>ȫ<EFBFBD>ֱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
// 模拟射频信号全局变量定义
|
||||
volatile u32 mock_rx_addr = 0;
|
||||
volatile u8 mock_rx_data = 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)
|
||||
{
|
||||
@@ -185,7 +205,7 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data)
|
||||
u16 idata raw_h[24];
|
||||
u16 idata raw_l[24];
|
||||
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
// 检查模拟射频触发
|
||||
if (mock_rf_ready) {
|
||||
mock_rf_ready = 0;
|
||||
*out_addr = mock_rx_addr;
|
||||
@@ -193,14 +213,14 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ====== 1. ͬ<EFBFBD><EFBFBD>ͷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>岶<EFBFBD><EFBFBD> ======
|
||||
// ====== 1. 同步头引导脉冲捕获 ======
|
||||
{
|
||||
u16 wait_cnt = 0;
|
||||
while (RF_RX_DATA == 1)
|
||||
{
|
||||
Delay_us(5);
|
||||
wait_cnt++;
|
||||
if (wait_cnt > 2000) // 10 <EFBFBD><EFBFBD><EFBFBD>볬ʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
if (wait_cnt > 2000) // 10 毫秒超时保护
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -209,28 +229,28 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data)
|
||||
{
|
||||
Delay_us(5);
|
||||
wait_cnt++;
|
||||
if (wait_cnt > 4000) // 20 <EFBFBD><EFBFBD><EFBFBD>볬ʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
if (wait_cnt > 4000) // 20 毫秒超时保护
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͷ<EFBFBD>ߵ<EFBFBD>ƽʱ<EFBFBD><EFBFBD>
|
||||
// 捕获引导头高电平时间
|
||||
high_time = GetPulseDuration(1, 2500);
|
||||
if (high_time < 50 || high_time > 2500) {
|
||||
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);
|
||||
if (low_time < 1500 || low_time > 60000) {
|
||||
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;
|
||||
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++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// ====== 3. ˫<EFBFBD>˱<EFBFBD>ֵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ж<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ======
|
||||
// ====== 3. 双端比值解调判定数据内容 ======
|
||||
for (i = 0; i < 24; 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)
|
||||
{
|
||||
// <EFBFBD>ߵ<EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD>Գ<EFBFBD><EFBFBD>ڵ͵<EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> "1"
|
||||
// 高电平明显长于低电平,代表逻辑 "1"
|
||||
if (i < 20)
|
||||
addr = (addr << 1) | 1;
|
||||
else
|
||||
@@ -256,7 +276,7 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data)
|
||||
}
|
||||
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)
|
||||
addr = (addr << 1);
|
||||
else
|
||||
@@ -264,18 +284,18 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data)
|
||||
}
|
||||
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_data = dat;
|
||||
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)
|
||||
{
|
||||
@@ -283,23 +303,23 @@ void RF_DiagnosticMode(char choice)
|
||||
{
|
||||
u8 i;
|
||||
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);
|
||||
Delay_ms(5);
|
||||
|
||||
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;
|
||||
RGB_Send(0, 150, 0, 0, 150, 0); // <EFBFBD>̹<EFBFBD><EFBFBD><EFBFBD>˸
|
||||
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); // ֡<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
Delay_ms(150); // 帧间隔
|
||||
}
|
||||
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");
|
||||
}
|
||||
else if (choice == 'r')
|
||||
@@ -309,10 +329,10 @@ void RF_DiagnosticMode(char choice)
|
||||
u8 rx_data;
|
||||
|
||||
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);
|
||||
|
||||
// 5<EFBFBD><EFBFBD><EFBFBD>ķ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
// 5秒的非阻塞监听
|
||||
for (wait_ms = 0; wait_ms < 5000; wait_ms++)
|
||||
{
|
||||
if (EV1527_Decode(&rx_addr, &rx_data))
|
||||
@@ -323,7 +343,7 @@ void RF_DiagnosticMode(char choice)
|
||||
Uart_SendHex8(rx_data);
|
||||
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;
|
||||
RGB_Send(0, 150, 150, 0, 150, 150);
|
||||
Delay_ms(100);
|
||||
@@ -335,3 +355,46 @@ void RF_DiagnosticMode(char choice)
|
||||
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); // 关断射频
|
||||
}
|
||||
|
||||
@@ -61,4 +61,9 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data);
|
||||
*/
|
||||
void RF_DiagnosticMode(char choice);
|
||||
|
||||
/**
|
||||
* @brief 电气回环自检测试 (物理短路 P2.1 与 P3.6 自检)
|
||||
*/
|
||||
void Loopback_Test(void);
|
||||
|
||||
#endif // __RF_H__
|
||||
|
||||
21874
Hardware/text.kicad_pcb
21874
Hardware/text.kicad_pcb
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"board": {
|
||||
"active_layer": 0,
|
||||
"active_layer_preset": "",
|
||||
"active_layer": 2,
|
||||
"active_layer_preset": "Back Layers",
|
||||
"auto_track_width": true,
|
||||
"hidden_netclasses": [],
|
||||
"hidden_nets": [
|
||||
@@ -52,7 +52,7 @@
|
||||
"board_outline_area",
|
||||
"ly_points"
|
||||
],
|
||||
"visible_layers": "ffffffff_ffffffff_ffffffff_fffffffb",
|
||||
"visible_layers": "ffffffff_ffffffff_ffffffff_ffffffff",
|
||||
"zone_display_mode": 0
|
||||
},
|
||||
"git": {
|
||||
|
||||
@@ -546,7 +546,7 @@
|
||||
"last_paths": {
|
||||
"idf": "",
|
||||
"netlist": "",
|
||||
"plot": "",
|
||||
"plot": "新建文件夹/",
|
||||
"specctra_dsn": "",
|
||||
"vrml": ""
|
||||
},
|
||||
@@ -714,7 +714,7 @@
|
||||
"uuid": "72df0bee-e55e-4d81-989a-168573031a0a"
|
||||
}
|
||||
],
|
||||
"used_designators": "#PWR59,U15",
|
||||
"used_designators": "",
|
||||
"variants": []
|
||||
},
|
||||
"sheets": [
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1
Hardware/~text.kicad_pro.lck
Normal file
1
Hardware/~text.kicad_pro.lck
Normal file
@@ -0,0 +1 @@
|
||||
{"hostname":"DESKTOP-0S8JI7H","username":"xtell"}
|
||||
7
woke-order/wo-00需要完成的功能
Normal file
7
woke-order/wo-00需要完成的功能
Normal file
@@ -0,0 +1,7 @@
|
||||
1 屏幕显示
|
||||
2 按键响应
|
||||
3 射频收发
|
||||
4 马达响应
|
||||
5 RGB控制
|
||||
6 电池电压检测和充电
|
||||
7 板子各状态下的功耗 。
|
||||
72
woke-order/wo-01
Normal file
72
woke-order/wo-01
Normal file
@@ -0,0 +1,72 @@
|
||||
# 📋 工单 wo-01: ADC按键检测、PWR_HOLD开关机控制、P1.3电池电量检测与 PCF8563 硬件 RTC 整合
|
||||
|
||||
**工单编号**: WO-01
|
||||
**创建时间**: 2026-07-27
|
||||
**模块组件**: 按键驱动 (ADC Key) / 电源管理 (Power Control) / 电池检测 (Battery ADC) / 实时时钟 (PCF8563 RTC) / 待机应用 (ClockApp & UI)
|
||||
|
||||
---
|
||||
|
||||
## 📌 一、 需求概述与按阶段分步规划
|
||||
|
||||
根据项目开发优先级与依赖关系,按以下四个顺序阶段分步完成:
|
||||
|
||||
### 阶段 1: P0.3 ADC 按键检测
|
||||
- **硬件引脚**: `P0.3` 对应 STC32G 硬件 `ADC3` 通道。
|
||||
- **电平特征**: 开机常态下 `KEY_DOWN` 节点电压约为 **2.0V**(未按下),按键按下后电压升至约为 **3.7V**。
|
||||
- **算法与逻辑**:
|
||||
- 配置 ADC 模块,引入滑动窗口均值/中值滤波消抖。
|
||||
- 按键判定:未按下区间 (1.5V ~ 2.5V),按下区间 (3.2V ~ 4.2V)。
|
||||
- 保留并兼容原本 KEY_DOWN 的短按功能(消抖释放后触发 `KEY_EVENT_DOWN_CLICK` 供 UI 菜单与时间编辑下翻数值)。
|
||||
|
||||
### 阶段 2: PWR_HOLD 开关机控制与上电自检
|
||||
- **硬件引脚**: `PWR_HOLD` 控制引脚(默认分配为 `P0.0`)。
|
||||
- **开关机流程**:
|
||||
- **开机锁存**: 按下开机键硬件通电后,单片机在入口 `main()` **第一时间拉高 `PWR_HOLD` 引脚**,锁存电源保持供电。
|
||||
- **长按关机**: 开机状态下长按 KEY_DOWN 按键 1 秒以上,触发关机流程 `KEY_EVENT_POWER_OFF`,拉低 `PWR_HOLD` 切断总电源。
|
||||
- **上电自检 (Self-Test)**:
|
||||
- 启动阶段自动执行硬件自检(电源锁存状态、ADC 采样功能、串口通信校验等),并在串口 1 输出完整的格式化自检报告。
|
||||
|
||||
### 阶段 3: P1.3 电池电压检测与真实电量显示
|
||||
- **硬件引脚**: `P1.3` 对应 STC32G 硬件 `ADC11` 通道(电池分压检测脚)。
|
||||
- **计算与转换**:
|
||||
- 读取 ADC11 通道计算电池输入电压 $V_{bat}$。
|
||||
- 将电池电压(锂电池 3.3V ~ 4.2V 范围)线性/分段映射转换为 **0% ~ 100%** 百分比电量。
|
||||
- **UI 真实电量刷新**:
|
||||
- 重构 `ui.c` 的 `UI_ShowClockPage` 及待机界面电量渲染代码,替换原写死固定的静态 "85%" 字符。
|
||||
- 动态显示真实电池百分比(如 "95%")及绘制对应的电量电池柱位图。
|
||||
|
||||
### 阶段 4: PCF8563 (D8563) 硬件 RTC 深度整合
|
||||
- **硬件连接**: PCF8563 RTC 芯片通过 I2C 总线连接至 STC32G (`P3.2` -> SCL, `P3.3` -> SDA)。
|
||||
- **深度整合**:
|
||||
- **开机/唤醒即读**: 系统开机或从低功耗休眠唤醒时,自动通过 I2C 读取 PCF8563 芯片的时间,更新系统全局时钟变量。
|
||||
- **前台编辑直接写入**: 在 `ClockApp` 编辑模式下按确认键保存时,同步将最新时间写入 PCF8563 芯片寄存器中,实现真正的硬件级掉电时间保存。
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 二、 硬件引脚配置汇总
|
||||
|
||||
| 功能名称 | 关联引脚 | 端口模式配置 | 采样/控制参数 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **KEY_DOWN** | `P0.3` / ADC3 | 高阻输入 | 常态 **2.0V**;按下 **3.7V**;消抖 50ms |
|
||||
| **PWR_HOLD** | `P0.0` (可配置) | 推挽输出 | 上电第一时间置 **高电平 (1)**;关机置 **低电平 (0)** |
|
||||
| **BATT_ADC** | `P1.3` / ADC11 | 高阻输入 | 电池分压输入,计算 3.3V(0%) ~ 4.2V(100%) 电量 |
|
||||
| **RTC_SCL** | `P3.2` | 准双向 / 模拟 I2C | PCF8563 SCL 时钟线 |
|
||||
| **RTC_SDA** | `P3.3` | 准双向 / 模拟 I2C | PCF8563 SDA 数据线 |
|
||||
|
||||
---
|
||||
|
||||
## 🧪 三、 分阶段验收标准
|
||||
|
||||
1. **阶段 1 验收 (ADC按键)**:
|
||||
- [ ] 串口实时打印 P0.3 ADC 电压,常态 2.0V 不误触发。
|
||||
- [ ] 按下 3.7V 稳定识别,短按能够正常执行 UI 菜单下翻和数值减小。
|
||||
2. **阶段 2 验收 (开关机与自检)**:
|
||||
- [ ] 长按按键 >1s 开机,系统完成自检后松开按键,`PWR_HOLD` 持续高电平保持供电。
|
||||
- [ ] 开机状态下长按按键 >1s,触发关机提示后 `PWR_HOLD` 拉低,系统断电。
|
||||
- [ ] 开机串口打印完整自检日志。
|
||||
3. **阶段 3 验收 (P1.3电池检测与UI)**:
|
||||
- [ ] 可通过 P1.3 ADC 测量准确计算实际电池电压。
|
||||
- [ ] 待机 UI 屏幕上实时绘制正确的百分比 (0%~100%) 与对应电量电池图标。
|
||||
4. **阶段 4 验收 (PCF8563 RTC)**:
|
||||
- [ ] 开机从 PCF8563 读取硬件时间。
|
||||
- [ ] 前台编辑保存后直接同步写入 PCF8563 芯片,掉电重启时间不丢失。
|
||||
75
woke-order/wo-02
Normal file
75
woke-order/wo-02
Normal file
@@ -0,0 +1,75 @@
|
||||
# 📋 工单 wo-02: 改动总结与硬件调试归档
|
||||
|
||||
**工单编号**: WO-02
|
||||
**归档时间**: 2026-07-28
|
||||
**处理状态**: 已完成 (Completed)
|
||||
**涉及模块**: 电池检测 (Battery ADC) / 按键驱动 (ADC Key) / 实时时钟 (PCF8563 RTC) / 系统自检 (Self-Test)
|
||||
|
||||
---
|
||||
|
||||
## 📌 一、 软件改动与底层逻辑修正
|
||||
|
||||
在本次工单调试执行过程中,针对硬件表现的差异与异常,对底层驱动及自检模块进行了如下修正:
|
||||
|
||||
### 1. 电池电压检测与电量百分比校准 (对应阶段 3)
|
||||
* **公式校准与修正**:
|
||||
* 物理硬件装配实际为:上拉 $10\text{k}\Omega$,下拉 $22\text{k}\Omega$(分压比为 $22/32$)。
|
||||
* 经用户万用表与 ADC 联合实测:当电池电压为 $4.0\text{V}$ (4000mV) 时,分压点物理电压为 $2.7\text{V}$,单片机读出的原始 ADC 过滤值为 `2357`。
|
||||
* 为了消除 LDO 压降、系统参考电压偏差等多重硬件误差,引入经验公式替换原先的理论换算公式:
|
||||
$$\text{Voltage (mV)} = \frac{\text{Raw\_ADC} \times 4000}{2357}$$
|
||||
* 该换算将电池准确映射至 `3.3V (0%) ~ 4.2V (100%)`。在 [adc.c](file:///c:/Users/xtell/Downloads/stc32g128k-master%20(1)/stc32g128k/Drivers/adc.c) 中已完全部署,并在前台时钟界面和自检中完美实时呈现。
|
||||
|
||||
### 2. ADC 按键与电池通道互换 (对应阶段 1 & 2)
|
||||
* **故障定位**:
|
||||
* 物理走线中,`P0.3` (按键) 实际对应单片机的 `ADC11` 通道;`P1.3` (电池) 实际对应 `ADC3` 通道。原先代码中将两个通道错调。
|
||||
* **修正方案**:
|
||||
* 在 [adc.h](file:///c:/Users/xtell/Downloads/stc32g128k-master%20(1)/stc32g128k/Drivers/adc.h) 中对调了通道定义:
|
||||
`#define ADC_CHANNEL_KEY_DOWN 11` (P0.3)
|
||||
`#define ADC_CHANNEL_BATTERY 3` (P1.3)
|
||||
* 修正后,按键常态值成功捕获为正常的 `2712` (约 2.2V),按下后正常达到 `4095` 饱和值。
|
||||
|
||||
### 3. PCF8563 (D8563) RTC 通信时序与机制重构 (对应阶段 4)
|
||||
* **I2C 物理模式优化**: 移除了高频读写 XSFR 带来的推挽/开漏动态切换(避免产生高频暂态毛刺),完全使用准双向口硬件 of “过渡强上拉”机制。
|
||||
* **死锁释放机制 (`I2C_ReleaseBus`)**: 在 PCF8563 通信或自检开始前,自动输出 9 个时钟脉冲配合 Stop 信号,强行迫使因常供电而进入错乱挂起状态的 RTC 芯片释放 I2C 总线,彻底避免死锁。
|
||||
* **自适应引脚探测**: 内置支持运行时自动侦测 `P3.2` 与 `P3.3` 的连线是否在画板时反接(默认极性与互换极性自动轮询),增强了硬件兼容度。
|
||||
* **双时延诊断测试**: 实现了 `PCF8563_TryReadDiagnostic()` 函数,同时使用常规速率 (30us延时) 与超低速时序 (200us延时,2.5kHz) 进行试读测试。
|
||||
|
||||
### 4. RGB 幻彩灯条上电亮白灯与 4-bit SPI 时序重构
|
||||
* **故障定位**:
|
||||
1. 上电复位暂态:单片机复位期间引脚默认弱上拉输出 `1`,这被灯珠移位寄存器误读为“全1”色彩(白光)。当单片机完成复位并拉低引脚时,产生 RESET 信号迫使灯珠锁存该色彩,导致开机长时间亮白灯。
|
||||
2. 3-bit 编码周期过短:在 3MHz 速率下,3-bit 模拟波形(100b/110b)的整个脉冲周期仅有 1.0us,低于规格书规定的 1.2us 最小值,引发灯珠识别率低下。
|
||||
* **软件重构方案**:
|
||||
1. **4-bit 编码重构**: 将转码改为 4-bit 编码(0码=1000b, 1码=1100b),高电平分别保持 333ns 和 666ns,完全处于规格书安全区间;总周期为 4 bits = 1.33us,完美吻合典型值 1.25us,数据传输稳定度极高。
|
||||
2. **上电显式关灯**: 在 `main()` 的 `GPIO_Init()` 执行完毕的第一个瞬间,强制调用 `RGB_Send(0, 0, 0, 0, 0, 0);` 送出关灯帧,在极速开机阶段的几微秒内强行熄灭白光。
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 二、 物理硬件排障记录
|
||||
|
||||
在联合调试期间,定位并解决了一项关键的物理硬件故障:
|
||||
|
||||
* **RTC 晶振不起振故障**:
|
||||
* **现象**: MCU 引脚输出波形完美,但 RTC 芯片一直不回 ACK。
|
||||
* **病因**: 32.768kHz 晶体两端的**旁路电容参数焊接错误**,导致晶振无法起振,从而使芯片的 I2C 接口内部控制块锁定并忽略全部寻址。
|
||||
* **解决**: 修正贴片电容后,晶振起振,RTC 硬件通信与自检完全恢复正常。
|
||||
|
||||
* **充电检测引脚配置与校准**:
|
||||
* **引脚定义**: 充电状态 DET 物理连接至 MCU 的 **`P1.2`** 引脚(对应 QFN32 第 10 脚,硬件为 **`ADC 通道 2`**)。
|
||||
* **电气优化**: 硬件配置为高阻输入,且在 `system.c` 和 `adc.c` 初始化中**显式关闭了 P1.2 的内部上拉电阻**(通过置空寄存器位 `(*(unsigned char volatile xdata *)0xFE11) &= ~(1 << 2)`),彻底消除了上电引起的电平偏置。
|
||||
* **阈值检测标准**:
|
||||
- $V_{\text{DET}} < 0.5\text{V}$ ($<500\text{mV}$): 未插充电器
|
||||
- $0.8\text{V} \le V_{\text{DET}} \le 1.8\text{V}$ ($800\text{mV} \sim 1800\text{mV}$): 正在充电 (充电指示闪烁)
|
||||
- $2.2\text{V} \le V_{\text{DET}} \le 2.8\text{V}$ ($2200\text{mV} \sim 2800\text{mV}$): 已充满 (停止闪烁)
|
||||
|
||||
---
|
||||
|
||||
## 🧪 三、 交付与归档文件
|
||||
|
||||
|
||||
* **最新源文件**:
|
||||
* 电池与按键检测算法: [adc.c](file:///c:/Users/xtell/Downloads/stc32g128k-master%20(1)/stc32g128k/Drivers/adc.c)
|
||||
* 引脚定义配置表: [adc.h](file:///c:/Users/xtell/Downloads/stc32g128k-master%20(1)/stc32g128k/Drivers/adc.h)
|
||||
* 自适应防死锁 RTC 驱动: [pcf8563.c](file:///c:/Users/xtell/Downloads/stc32g128k-master%20%281%29/stc32g128k/Drivers/pcf8563.c)
|
||||
* 极速自检与诊断流程: [system.c](file:///c:/Users/xtell/Downloads/stc32g128k-master%20(1)/stc32g128k/App/system.c)
|
||||
* **烧录固件**:
|
||||
* 归档 HEX 路径: [Build\wristband.hex](file:///c:/Users/xtell/Downloads/stc32g128k-master%20(1)/stc32g128k/Build/wristband.hex)
|
||||
126
woke-order/wo-03
Normal file
126
woke-order/wo-03
Normal file
@@ -0,0 +1,126 @@
|
||||
# 工单 WO-03:RGB 灯控故障排查 & 电池充电功能测试计划
|
||||
|
||||
**优先级**:高
|
||||
**状态**:进行中
|
||||
|
||||
---
|
||||
|
||||
## 一、RGB 灯控 — 故障排查与修复
|
||||
|
||||
### 现象
|
||||
开机后两颗 WS2812B 灯珠一直亮白灯,无法通过软件控制熄灭或变色。
|
||||
|
||||
### 1.1 硬件电路复查
|
||||
| # | 操作步骤 | 验证手段 | 预期结果 | 状态 |
|
||||
|---|---------|---------|---------|------|
|
||||
| 1.1.1 | 用万用表测量 P2.3 对地电阻 | 断电测 | 非短路 | 待执行 |
|
||||
| 1.1.2 | 上电测 P2.3 引脚电平 | 示波器捕获上电瞬间到 main() 执行前的波形 | 应快速被拉低,无长时间高电平 | 待执行 |
|
||||
| 1.1.3 | 测量 TP4054 VCC (5V) 和 VSYS 供电 | 万用表直流档 | VSYS 约 3.7~4.2V 或 5V | 待执行 |
|
||||
| 1.1.4 | 测量 WS2812B VDD (Pin4) 电压 | 万用表 | 与 VSYS 一致 | 待执行 |
|
||||
| 1.1.5 | 测量 WS2812B DIN (Pin3) 静态电平 | 未发送数据时示波器/万用表 | 应接近 0V | 待执行 |
|
||||
|
||||
### 1.2 引脚初始化修复
|
||||
| # | 操作步骤 | 涉及文件 | 说明 | 状态 |
|
||||
|---|---------|---------|------|------|
|
||||
| 1.2.1 | GPIO_Init() 中增设 RGB_DIN = 0 | system.c:50 | 将 P2.3 初始输出拉低,消除上电不确定态 | ✔ 已修复 |
|
||||
| 1.2.2 | main() 中发一次 RESET 脉冲确保 WS2812B 复位 | main.c | 确保 WS2812B 在上电后处于已知复位状态 | ✔ 已修复 |
|
||||
| 1.2.3 | 验证 P_SW1 SPI 引脚映射 | rgb.c:19 | P_SW1 &= ~0x0C; P_SW1 |= 0x04 将 SPI 映射到 P2.3(MOSI)/P2.5(SCLK) | ✔ 已验证 |
|
||||
|
||||
### 1.3 SPI 时序验证
|
||||
| # | 操作步骤 | 验证手段 | 预期结果 | 状态 |
|
||||
|---|---------|---------|---------|------|
|
||||
| 1.3.1 | 示波器探头接 P2.3 (MOSI/DIN) 和 GND | 调用 RGB_Send(0, 180, 0, 0, 0, 0) 只亮红灯 | 观察到 3MHz 时钟波形 | 待执行 |
|
||||
| 1.3.2 | 示波器接 P2.5 (SCLK) 观察 | 同一调用 | 发送期间无 SCLK 信号(因已被设为高阻) | 待执行 |
|
||||
| 1.3.3 | 测量 RESET 低电平持续时间 | 在 RGB_Send() 最后 RGB_DIN=0 后测 | >80μs | 待执行 |
|
||||
| 1.3.4 | 验证 SPCTL = 0xD1 配置 | 核对 STC32G 数据手册 | SSIG=1, SPEN=1, MSTR=1, CPOL=0, CPHA=0, SPR0=1 → 3MHz @24MHz | ✔ 已验证 |
|
||||
|
||||
### 1.4 代码逻辑复查
|
||||
| # | 操作步骤 | 涉及文件 | 说明 | 状态 |
|
||||
|---|---------|---------|------|------|
|
||||
| 1.4.1 | 检查 Encode_Byte 编码 | rgb.c:42-62 | 每个 WS2812B 的 8bit 展开为 3 字节 SPI 数据 = 24 SPI bits | ✔ 已验证 |
|
||||
| 1.4.2 | 检查 RGB_Send 中 SPI 收发循环 | rgb.c:94-100 | 18 字节全部通过 SPDAT 发出,含等待 SPIF | ✔ 已验证 |
|
||||
| 1.4.3 | SPI_Init 调用频次 | rgb.c:92 | 每次 RGB_Send 都重新 init SPI,不影响功能 | ✔ 已验证 |
|
||||
| 1.4.4 | RGB_Send(0,0,0,0,0,0) 能否关灯 | 调试指令 | 需实测验证 | 待执行 |
|
||||
|
||||
### 1.5 功能验证
|
||||
| # | 测试用例 | 操作 | 预期结果 | 状态 |
|
||||
|---|---------|------|---------|------|
|
||||
| 1.5.1 | 开机默认灭灯 | 上电 | 两颗灯全灭 | 待执行 |
|
||||
| 1.5.2 | 对码模式白灯 | 进入 PairApp | 两颗灯亮白灯(G=R=B=150) | 待执行 |
|
||||
| 1.5.3 | 对码成功绿灯 | PairApp 确认保存 | 两颗亮绿灯(G=200) | 待执行 |
|
||||
| 1.5.4 | 报警红灯 | 触发门磁报警 | 红灯闪烁 | 待执行 |
|
||||
| 1.5.5 | SOS 红蓝交替 | 触发 SOS | 灯1红灯、灯2蓝灯交替闪烁 | 待执行 |
|
||||
| 1.5.6 | 时间设置蓝灯 | 长按确认进入时间设置 | 双蓝灯常亮 | 待执行 |
|
||||
| 1.5.7 | 马达无异常振动 | 任何 RGB_Send 调用时 | 马达不应发出异常噪音或振动 | 待执行 |
|
||||
|
||||
---
|
||||
|
||||
## 二、电池充电功能测试计划
|
||||
|
||||
### 2.1 硬件充电回路验证
|
||||
| # | 操作步骤 | 验证手段 | 预期结果 | 状态 |
|
||||
|---|---------|---------|---------|------|
|
||||
| 2.1.1 | 万用表测 TP4054 的 VCC(Pin4) 对 GND | 插入 USB 5V 供电 | 5.0V ±0.25V | 待执行 |
|
||||
| 2.1.2 | 测 TP4054 的 PROG(Pin5) 对 GND 电阻 | 断电测 R1 阻值 | 24kΩ → 充电电流约 41.7mA | 待执行 |
|
||||
| 2.1.3 | 测 TP4054 的 BAT(Pin3) 对 GND 电压 | 插入 USB + 接电池 | 电池电压 3.7~4.2V | 待执行 |
|
||||
| 2.1.4 | 测 TP4054 的 CHRG(Pin1) 电平 | 未接USB/充电中/充满 | 未接:高阻;充电中:低电平;充满:高阻 | 待执行 |
|
||||
| 2.1.5 | 追踪 CHRG → DET → MCU 引脚号的 PCB 走线 | 对照原理图 | 确认连接至 MCU GPIO 引脚号 | 待执行 |
|
||||
| 2.1.6 | 确认 DET 网络终端的 MCU 引脚电平兼容性 | 万用表测 MCU 侧 | TP4054 CHRG 需 MCU 内部上拉 | 待执行 |
|
||||
| 2.1.7 | 测量 BAT_ADC(P1.3) 分压网络 | 万用表 | R15=10kΩ, R16=22kΩ, 分压比 22/32 | 待执行 |
|
||||
|
||||
### 2.2 充电检测代码实现
|
||||
| # | 操作步骤 | 涉及文件 | 说明 | 状态 |
|
||||
|---|---------|---------|------|------|
|
||||
| 2.2.1 | 确认 MCU 实际连接 CHRG/DET 的引脚号 | 原理图追踪 | CHRG → DET → MCU | ✔ 已完成 |
|
||||
| 2.2.2 | 在 config.h 添加 sbit CHRG_DET 定义 | config.h | 定义充电检测引脚 | ✔ 已添加 |
|
||||
| 2.2.3 | 在 GPIO_Init() 中将该引脚配置为高阻输入+内部上拉 | system.c | 适配 TP4054 开漏输出 | ✔ 已添加 |
|
||||
| 2.2.4 | 实现 bit Is_Charging(void) | adc.c / adc.h | 返回 0=未充电, 1=充电中 | ✔ 已实现 |
|
||||
| 2.2.5 | 在 UI 层接入充电状态显示 | ui.c / app_clock.c | 充电时显示 `+` 后缀 | ✔ 已完成 |
|
||||
| 2.2.6 | 在 Self_Test() 中输出充电状态 | system.c | 上电自检时UART输出充电状态 | ✔ 已添加 |
|
||||
|
||||
### 2.3 充电功能验证
|
||||
| # | 测试用例 | 操作 | 预期结果 | 状态 |
|
||||
|---|---------|------|---------|------|
|
||||
| 2.3.1 | 未插入 USB 充电 | 观察待机界面 | 仅显示百分比,无 `+` 后缀 | 待执行 |
|
||||
| 2.3.2 | 插入 USB 充电(电池未满) | USB 供电 | 状态栏显示 `85%+`,CHRG 低电平 | 待执行 |
|
||||
| 2.3.3 | 电池充满后 | 等待充电完成 | `+` 后缀消失,CHRG 变高阻 | 待执行 |
|
||||
| 2.3.4 | 充电时热插拔 USB | 反复插拔 | 状态切换无延迟 > 2s | 待执行 |
|
||||
| 2.3.5 | 低电量充电(电池 < 3.3V) | 电池耗尽后充电 | 充电电流约 41.7mA,UI 显示充电中 | 待执行 |
|
||||
| 2.3.6 | 充电时进入休眠 | 插入 USB 等待 60s 自动休眠 | 休眠后应能被唤醒 | 待执行 |
|
||||
| 2.3.7 | 充电时触发报警/SOS | 充电中触发报警 | 报警功能正常 | 待执行 |
|
||||
| 2.3.8 | Is_Charging() 电平反转验证 | 强制拉高/拉低 DET 引脚 | UART 调试输出正确状态 | 待执行 |
|
||||
|
||||
### 2.4 ADC 电池电压校准
|
||||
| # | 操作步骤 | 验证手段 | 预期结果 | 状态 |
|
||||
|---|---------|---------|---------|------|
|
||||
| 2.4.1 | 用万用表实测电池电压 | 记录 Vref | 如 3.85V | 待执行 |
|
||||
| 2.4.2 | UART 读取 ADC_GetBatteryVoltage_mV() 返回值 | 串口助手 | 偏差应在 ±50mV 内 | 待执行 |
|
||||
| 2.4.3 | 如果偏差过大,调整校准系数 | adc.c:122 | val * Vref_mV / adc_raw | 待执行 |
|
||||
| 2.4.4 | 验证电量百分比线性度 | 可调电源模拟 3.3V~4.2V | 0%~100% 线性输出 | 待执行 |
|
||||
|
||||
---
|
||||
|
||||
## 三、测试环境与工具清单
|
||||
- Keil C251 编译器 + STC-ISP 烧录工具
|
||||
- USB 转 TTL 串口模块 (115200 baud)
|
||||
- 数字万用表
|
||||
- 双通道数字示波器(至少 10MHz 带宽)
|
||||
- 可调直流稳压电源(模拟电池 3.0~4.2V)
|
||||
- 逻辑分析仪(可选)
|
||||
- 已组装好的手环 PCB x 1~2pcs
|
||||
- 433MHz 发射模块/遥控器
|
||||
- 锂电池 3.7V
|
||||
|
||||
---
|
||||
|
||||
## 四、代码修改记录
|
||||
|
||||
| 文件 | 修改内容 | 状态 |
|
||||
|------|---------|------|
|
||||
| App/config.h | 新增 CHRG_DET sbit 定义 | ✔ |
|
||||
| App/system.c | GPIO_Init() 中增设 RGB_DIN=0,配置 CHRG_DET 引脚,Self_Test() 输出充电状态 | ✔ |
|
||||
| App/main.c | GPIO_Init() 后立即发 WS2812B RESET 脉冲 | ✔ |
|
||||
| Drivers/adc.h | 新增 Is_Charging() 声明 | ✔ |
|
||||
| Drivers/adc.c | 实现 Is_Charging() | ✔ |
|
||||
| App/rgb.c | 确认 SPI 配置正确 | ✔ 已验证 |
|
||||
| App/ui.c | UI_ShowClockPage 中接入 Is_Charging() 显示 `+` 后缀 | ✔ |
|
||||
397
wristband.uvopt
Normal file
397
wristband.uvopt
Normal file
@@ -0,0 +1,397 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<ProjectOpt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="project_opt.xsd">
|
||||
|
||||
<SchemaVersion>1.0</SchemaVersion>
|
||||
|
||||
<Header>### uVision Project, (C) Keil Software</Header>
|
||||
|
||||
<Extensions>
|
||||
<cExt>*.c</cExt>
|
||||
<aExt>*.s*; *.src; *.a*</aExt>
|
||||
<oExt>*.obj; *.o</oExt>
|
||||
<lExt>*.lib</lExt>
|
||||
<tExt>*.txt; *.h; *.inc; *.md</tExt>
|
||||
<pExt>*.plm</pExt>
|
||||
<CppX>*.cpp; *.cc; *.cxx</CppX>
|
||||
<nMigrate>0</nMigrate>
|
||||
</Extensions>
|
||||
|
||||
<DaveTm>
|
||||
<dwLowDateTime>0</dwLowDateTime>
|
||||
<dwHighDateTime>0</dwHighDateTime>
|
||||
</DaveTm>
|
||||
|
||||
<Target>
|
||||
<TargetName>Wristband</TargetName>
|
||||
<ToolsetNumber>0x1</ToolsetNumber>
|
||||
<ToolsetName>MCS-251</ToolsetName>
|
||||
<TargetOption>
|
||||
<CLK251>35000000</CLK251>
|
||||
<OPTTT>
|
||||
<gFlags>0</gFlags>
|
||||
<BeepAtEnd>1</BeepAtEnd>
|
||||
<RunSim>1</RunSim>
|
||||
<RunTarget>0</RunTarget>
|
||||
<RunAbUc>0</RunAbUc>
|
||||
</OPTTT>
|
||||
<OPTHX>
|
||||
<HexSelection>1</HexSelection>
|
||||
<FlashByte>65535</FlashByte>
|
||||
<HexRangeLowAddress>0</HexRangeLowAddress>
|
||||
<HexRangeHighAddress>0</HexRangeHighAddress>
|
||||
<HexOffset>0</HexOffset>
|
||||
</OPTHX>
|
||||
<OPTLEX>
|
||||
<PageWidth>120</PageWidth>
|
||||
<PageLength>65</PageLength>
|
||||
<TabStop>8</TabStop>
|
||||
<ListingPath>.\Build\</ListingPath>
|
||||
</OPTLEX>
|
||||
<ListingPage>
|
||||
<CreateCListing>1</CreateCListing>
|
||||
<CreateAListing>1</CreateAListing>
|
||||
<CreateLListing>1</CreateLListing>
|
||||
<CreateIListing>0</CreateIListing>
|
||||
<AsmCond>1</AsmCond>
|
||||
<AsmSymb>1</AsmSymb>
|
||||
<AsmXref>0</AsmXref>
|
||||
<CCond>1</CCond>
|
||||
<CCode>0</CCode>
|
||||
<CListInc>0</CListInc>
|
||||
<CSymb>0</CSymb>
|
||||
<LinkerCodeListing>0</LinkerCodeListing>
|
||||
</ListingPage>
|
||||
<OPTXL>
|
||||
<LMap>1</LMap>
|
||||
<LComments>1</LComments>
|
||||
<LGenerateSymbols>1</LGenerateSymbols>
|
||||
<LLibSym>1</LLibSym>
|
||||
<LLines>1</LLines>
|
||||
<LLocSym>1</LLocSym>
|
||||
<LPubSym>1</LPubSym>
|
||||
<LXref>0</LXref>
|
||||
<LExpSel>0</LExpSel>
|
||||
</OPTXL>
|
||||
<OPTFL>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<IsCurrentTarget>1</IsCurrentTarget>
|
||||
</OPTFL>
|
||||
<CpuCode>0</CpuCode>
|
||||
<DebugOpt>
|
||||
<uSim>1</uSim>
|
||||
<uTrg>0</uTrg>
|
||||
<sLdApp>1</sLdApp>
|
||||
<sGomain>1</sGomain>
|
||||
<sRbreak>1</sRbreak>
|
||||
<sRwatch>1</sRwatch>
|
||||
<sRmem>1</sRmem>
|
||||
<sRfunc>1</sRfunc>
|
||||
<sRbox>1</sRbox>
|
||||
<tLdApp>1</tLdApp>
|
||||
<tGomain>0</tGomain>
|
||||
<tRbreak>1</tRbreak>
|
||||
<tRwatch>1</tRwatch>
|
||||
<tRmem>1</tRmem>
|
||||
<tRfunc>0</tRfunc>
|
||||
<tRbox>1</tRbox>
|
||||
<tRtrace>1</tRtrace>
|
||||
<sRSysVw>1</sRSysVw>
|
||||
<tRSysVw>1</tRSysVw>
|
||||
<sRunDeb>0</sRunDeb>
|
||||
<sLrtime>0</sLrtime>
|
||||
<bEvRecOn>1</bEvRecOn>
|
||||
<bSchkAxf>0</bSchkAxf>
|
||||
<bTchkAxf>0</bTchkAxf>
|
||||
<nTsel>-1</nTsel>
|
||||
<sDll></sDll>
|
||||
<sDllPa></sDllPa>
|
||||
<sDlgDll></sDlgDll>
|
||||
<sDlgPa></sDlgPa>
|
||||
<sIfile></sIfile>
|
||||
<tDll></tDll>
|
||||
<tDllPa></tDllPa>
|
||||
<tDlgDll></tDlgDll>
|
||||
<tDlgPa></tDlgPa>
|
||||
<tIfile></tIfile>
|
||||
<pMon></pMon>
|
||||
</DebugOpt>
|
||||
<Breakpoint/>
|
||||
<Tracepoint>
|
||||
<THDelay>0</THDelay>
|
||||
</Tracepoint>
|
||||
<DebugFlag>
|
||||
<trace>0</trace>
|
||||
<periodic>0</periodic>
|
||||
<aLwin>0</aLwin>
|
||||
<aCover>0</aCover>
|
||||
<aSer1>0</aSer1>
|
||||
<aSer2>0</aSer2>
|
||||
<aPa>0</aPa>
|
||||
<viewmode>0</viewmode>
|
||||
<vrSel>0</vrSel>
|
||||
<aSym>0</aSym>
|
||||
<aTbox>0</aTbox>
|
||||
<AscS1>0</AscS1>
|
||||
<AscS2>0</AscS2>
|
||||
<AscS3>0</AscS3>
|
||||
<aSer3>0</aSer3>
|
||||
<eProf>0</eProf>
|
||||
<aLa>0</aLa>
|
||||
<aPa1>0</aPa1>
|
||||
<AscS4>0</AscS4>
|
||||
<aSer4>0</aSer4>
|
||||
<StkLoc>0</StkLoc>
|
||||
<TrcWin>0</TrcWin>
|
||||
<newCpu>0</newCpu>
|
||||
<uProt>0</uProt>
|
||||
</DebugFlag>
|
||||
<LintExecutable></LintExecutable>
|
||||
<LintConfigFile></LintConfigFile>
|
||||
<bLintAuto>0</bLintAuto>
|
||||
<bAutoGenD>0</bAutoGenD>
|
||||
<LntExFlags>0</LntExFlags>
|
||||
<pMisraName></pMisraName>
|
||||
<pszMrule></pszMrule>
|
||||
<pSingCmds></pSingCmds>
|
||||
<pMultCmds></pMultCmds>
|
||||
<pMisraNamep></pMisraNamep>
|
||||
<pszMrulep></pszMrulep>
|
||||
<pSingCmdsp></pSingCmdsp>
|
||||
<pMultCmdsp></pMultCmdsp>
|
||||
</TargetOption>
|
||||
</Target>
|
||||
|
||||
<Group>
|
||||
<GroupName>App</GroupName>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<cbSel>0</cbSel>
|
||||
<RteFlg>0</RteFlg>
|
||||
<File>
|
||||
<GroupNumber>1</GroupNumber>
|
||||
<FileNumber>1</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\App\main.c</PathWithFileName>
|
||||
<FilenameWithoutPath>main.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>1</GroupNumber>
|
||||
<FileNumber>2</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\App\rgb.c</PathWithFileName>
|
||||
<FilenameWithoutPath>rgb.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>1</GroupNumber>
|
||||
<FileNumber>3</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\App\event.c</PathWithFileName>
|
||||
<FilenameWithoutPath>event.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>1</GroupNumber>
|
||||
<FileNumber>4</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\App\app_manager.c</PathWithFileName>
|
||||
<FilenameWithoutPath>app_manager.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>1</GroupNumber>
|
||||
<FileNumber>5</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\App\database.c</PathWithFileName>
|
||||
<FilenameWithoutPath>database.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>1</GroupNumber>
|
||||
<FileNumber>6</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\App\ui.c</PathWithFileName>
|
||||
<FilenameWithoutPath>ui.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>1</GroupNumber>
|
||||
<FileNumber>7</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\App\system.c</PathWithFileName>
|
||||
<FilenameWithoutPath>system.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>1</GroupNumber>
|
||||
<FileNumber>8</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\App\clock.c</PathWithFileName>
|
||||
<FilenameWithoutPath>clock.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>1</GroupNumber>
|
||||
<FileNumber>9</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\App\app_clock.c</PathWithFileName>
|
||||
<FilenameWithoutPath>app_clock.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>1</GroupNumber>
|
||||
<FileNumber>10</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\App\app_menu.c</PathWithFileName>
|
||||
<FilenameWithoutPath>app_menu.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>1</GroupNumber>
|
||||
<FileNumber>11</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\App\app_pair.c</PathWithFileName>
|
||||
<FilenameWithoutPath>app_pair.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>1</GroupNumber>
|
||||
<FileNumber>12</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\App\app_alarm.c</PathWithFileName>
|
||||
<FilenameWithoutPath>app_alarm.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>1</GroupNumber>
|
||||
<FileNumber>13</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\App\app_sos.c</PathWithFileName>
|
||||
<FilenameWithoutPath>app_sos.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>1</GroupNumber>
|
||||
<FileNumber>14</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\App\app_rf_monitor.c</PathWithFileName>
|
||||
<FilenameWithoutPath>app_rf_monitor.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
</Group>
|
||||
|
||||
<Group>
|
||||
<GroupName>Drivers</GroupName>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<cbSel>0</cbSel>
|
||||
<RteFlg>0</RteFlg>
|
||||
<File>
|
||||
<GroupNumber>2</GroupNumber>
|
||||
<FileNumber>15</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\Drivers\lcd.c</PathWithFileName>
|
||||
<FilenameWithoutPath>lcd.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>2</GroupNumber>
|
||||
<FileNumber>16</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\Drivers\rf.c</PathWithFileName>
|
||||
<FilenameWithoutPath>rf.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>2</GroupNumber>
|
||||
<FileNumber>17</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\Drivers\adc.c</PathWithFileName>
|
||||
<FilenameWithoutPath>adc.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
<File>
|
||||
<GroupNumber>2</GroupNumber>
|
||||
<FileNumber>18</FileNumber>
|
||||
<FileType>1</FileType>
|
||||
<tvExp>0</tvExp>
|
||||
<tvExpOptDlg>0</tvExpOptDlg>
|
||||
<bDave2>0</bDave2>
|
||||
<PathWithFileName>.\Drivers\pcf8563.c</PathWithFileName>
|
||||
<FilenameWithoutPath>pcf8563.c</FilenameWithoutPath>
|
||||
<RteFlg>0</RteFlg>
|
||||
<bShared>0</bShared>
|
||||
</File>
|
||||
</Group>
|
||||
|
||||
</ProjectOpt>
|
||||
@@ -409,6 +409,16 @@
|
||||
<FileType>1</FileType>
|
||||
<FilePath>.\Drivers\rf.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>adc.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>.\Drivers\adc.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>pcf8563.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>.\Drivers\pcf8563.c</FilePath>
|
||||
</File>
|
||||
</Files>
|
||||
</Group>
|
||||
</Groups>
|
||||
|
||||
Reference in New Issue
Block a user