feat: 校准电池电压与ADC2充电检测,重构RGB为Bitbang翻转驱动,并修复RTC晶振起振问题

This commit is contained in:
2026-07-28 18:03:20 +08:00
parent 47bcd252b2
commit d01121e3ae
25 changed files with 15134 additions and 11463 deletions

159
App/rgb.c
View File

@@ -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;
}