docs: add detailed Chinese comments for code files and correct GPIO mapping table in main.md
This commit is contained in:
92
App/rgb.c
92
App/rgb.c
@@ -1,47 +1,77 @@
|
||||
#include "rgb.h"
|
||||
|
||||
/* ====== SPI 寄存器定义 (如果 stc32g.h 没定义) ====== */
|
||||
/* ====== SPI 寄存器映射地址定义 (防止 stc32g.h 库版本中未显式定义) ====== */
|
||||
#ifndef P_SW1
|
||||
sfr P_SW1 = 0xA2;
|
||||
sfr SPCTL = 0xCE;
|
||||
sfr SPSTAT = 0xCD;
|
||||
sfr SPDAT = 0xCF;
|
||||
sfr P_SW1 = 0xA2; // 外设端口切换寄存器 1
|
||||
sfr SPCTL = 0xCE; // SPI 控制寄存器
|
||||
sfr SPSTAT = 0xCD; // SPI 状态寄存器
|
||||
sfr SPDAT = 0xCF; // SPI 数据寄存器
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 初始化内核 SPI 控制器
|
||||
* @details 将 SPI 的物理管脚切换至 P2 端口的第二组外设映射位置(SCLK=P2.5, MOSI=P2.3),
|
||||
* 并配置 SPI 传输速率,使其产生的脉冲宽满足 WS2812B/FCOB 的 1 码和 0 码时序。
|
||||
*/
|
||||
void SPI_Init(void)
|
||||
{
|
||||
P_SW1 = (P_SW1 & ~0x0C) | 0x04; // 映射 SPI 至第二组 (P2.2~P2.5)
|
||||
SPCTL = 0xD1; // SSIG=1, SPEN=1, MSTR=1, SPR0=1 (3.0 MHz)
|
||||
SPSTAT = 0xC0; // 清除标志
|
||||
// 将 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;
|
||||
u32 spi_val = 0; // 用于拼接 24 位 SPI 映射串
|
||||
u8 i;
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
spi_val <<= 3;
|
||||
spi_val <<= 3; // 每次左移 3 位,为下一个像素的波形让出空间
|
||||
if (val & (0x80 >> i))
|
||||
{
|
||||
spi_val |= 6; // 1码 -> 110
|
||||
spi_val |= 6; // 该 bit 为 1,对应 WS2812B "1" 码时序,用二进制 '110' (即数值6) 填充
|
||||
}
|
||||
else
|
||||
{
|
||||
spi_val |= 4; // 0码 -> 100
|
||||
spi_val |= 4; // 该 bit 为 0,对应 WS2812B "0" 码时序,用二进制 '100' (即数值4) 填充
|
||||
}
|
||||
}
|
||||
// 将拼接好的 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];
|
||||
u8 idata spi_buf[18]; // 2 颗灯 * 3 个基色通道 * 3 字节编码 = 18 字节缓冲
|
||||
u8 i;
|
||||
|
||||
// 转码 3-bit 编码
|
||||
// 分别转码两颗灯珠的 G、R、B 通道,顺序填充进 SPI 缓冲区
|
||||
Encode_Byte(g1, &spi_buf[0]);
|
||||
Encode_Byte(r1, &spi_buf[3]);
|
||||
Encode_Byte(b1, &spi_buf[6]);
|
||||
@@ -49,36 +79,44 @@ void RGB_Send(u8 g1, u8 r1, u8 b1, u8 g2, u8 r2, u8 b2)
|
||||
Encode_Byte(r2, &spi_buf[12]);
|
||||
Encode_Byte(b2, &spi_buf[15]);
|
||||
|
||||
EA = 0; // 关中断
|
||||
EA = 0; // 关闭全局中断,防止 SPI 数据发送被高频中断打断,造成 WS2812B 的时序畸变
|
||||
|
||||
// 临时将 P2.5 (MOTOR) 配置为高阻输入以隔离时钟信号
|
||||
// 【硬件复用隔离机制】:
|
||||
// 物理引脚 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
|
||||
SPI_Init(); // 配置并启动 SPI 外设控制器
|
||||
|
||||
// 循环发送 18 字节的 WS2812B 脉冲编码数据
|
||||
for (i = 0; i < 18; i++)
|
||||
{
|
||||
SPDAT = spi_buf[i];
|
||||
while (!(SPSTAT & 0x80)); // 等待发送完毕
|
||||
SPSTAT = 0xC0;
|
||||
SPDAT = spi_buf[i]; // 装载待发数据到数据寄存器
|
||||
while (!(SPSTAT & 0x80)); // 等待 SPI 状态寄存器第 7 位 (SPIF) 变为 1,代表发送结束
|
||||
SPSTAT = 0xC0; // 往状态寄存器写入1以清零 SPIF 和 WCOL 标志
|
||||
}
|
||||
|
||||
SPCTL = 0; // 关闭 SPI
|
||||
SPCTL = 0; // 彻底关闭 SPI 控制器,释放总线
|
||||
|
||||
// 配置 P2.3 (RGB_DIN) 为推挽输出并拉低 100us 做 Reset
|
||||
// 【发送 RESET 复位低电平帧】:
|
||||
// WS2812B 驱动芯片要求在所有色彩帧数据发完后,DIN 必须维持大于 80us 的低电平以将数据刷入锁存。
|
||||
// 首先将 P2.3 (RGB_DIN) 重新配置为推挽输出模式 (M1=0, M0=1)
|
||||
P2M1 &= ~(1 << 3);
|
||||
P2M0 |= (1 << 3);
|
||||
RGB_DIN = 0;
|
||||
RGB_DIN = 0; // 强行拉低 DIN 信号线
|
||||
for (i = 0; i < 200; i++)
|
||||
{
|
||||
_nop_(); _nop_(); _nop_();
|
||||
_nop_(); _nop_(); _nop_(); // 软件延时,保持低电平时间大于 100微秒
|
||||
}
|
||||
|
||||
// 恢复 P2.5 (MOTOR) 为推挽输出以进行振动控制
|
||||
// 【恢复复用引脚功能】:
|
||||
// 恢复 P2.5 (MOTOR) 引脚为普通的推挽输出模式 (M1=0, M0=1),以便重新由定时器或应用控制振动马达
|
||||
P2M1 &= ~(1 << 5);
|
||||
P2M0 |= (1 << 5);
|
||||
MOTOR = 0;
|
||||
MOTOR = 0; // 默认拉低马达引脚,防止发声
|
||||
|
||||
EA = 1; // 开中断
|
||||
EA = 1; // 恢复全局中断使能
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user