feat: 校准电池电压与ADC2充电检测,重构RGB为Bitbang翻转驱动,并修复RTC晶振起振问题
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
20
App/config.h
20
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 数据控制线
|
||||
@@ -35,7 +45,12 @@ sbit LCD_SDI = P1^4;
|
||||
// LHS114TC-IF03 屏幕 SPI 时钟脚 (SCLK)
|
||||
sbit LCD_SCL = P1^5;
|
||||
// LHS114TC-IF03 屏幕 SPI 片选脚 (CS),低电平有效
|
||||
sbit LCD_CS = P1^6;
|
||||
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 {
|
||||
@@ -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;
|
||||
|
||||
/* ====== 事件优先级枚举 ====== */
|
||||
|
||||
67
App/event.c
67
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; // 当前队列内有效事件总数
|
||||
@@ -130,6 +131,12 @@ void Event_Dispatcher_Loop(void)
|
||||
return;
|
||||
}
|
||||
|
||||
// 关机事件处理:捕获到长按关机指令,直接调用 Power_Off 关断电源
|
||||
if (evt.key_event == KEY_EVENT_POWER_OFF) {
|
||||
Power_Off();
|
||||
return;
|
||||
}
|
||||
|
||||
// 常规事件(如普通的按键点击)直接透传分发给当前处于前台活跃的应用
|
||||
AppManager_DispatchEvent(evt);
|
||||
}
|
||||
@@ -152,31 +159,57 @@ 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 = 0; // 临时注释关闭 SOS 物理按键电平检测,防浮空干扰
|
||||
key_scan_timer = 0; // 重置扫描分频计数器
|
||||
|
||||
// 按键调试诊断:若按键 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) {
|
||||
inactivity_timer = 0;
|
||||
} else {
|
||||
@@ -219,15 +252,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);
|
||||
}
|
||||
@@ -266,5 +300,4 @@ void Timer1_Isr(void) interrupt 3
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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__
|
||||
|
||||
10
App/main.c
10
App/main.c
@@ -167,13 +167,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: 初始化事件环形缓冲区队列 */
|
||||
@@ -192,6 +194,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') {
|
||||
|
||||
159
App/rgb.c
159
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;
|
||||
|
||||
// 分别转码两颗灯珠的 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; // 关闭全局中断,防止 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)
|
||||
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;
|
||||
|
||||
EA = 0;
|
||||
|
||||
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; // 默认拉低马达引脚,防止发声
|
||||
|
||||
EA = 1; // 恢复全局中断使能
|
||||
MOTOR = 0;
|
||||
|
||||
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); // 小时十位
|
||||
|
||||
Reference in New Issue
Block a user