介绍
炫彩灯珠内置 IC,可以显示 $256\times256\times256$ 种颜色,仅凭一根信号线即可实现多种多样的效果
硬件电路
根据 SK6812 的电气参数和手册上的典型应用电路来设计其硬件电路
参数 | 符号 | 范围 | 单位 |
---|---|---|---|
电压 | VDD | +3.7~+5.5 | V |
逻辑输入电压 | VI | -0.5~VDD+0.5 | V |
工作温度 | Topt | -40~+85 | ℃ |
储存温度 | Tstg | -40~+85 | ℃ |
ESD耐压(设备模式) | VESD | 200 | V |
ESD耐压(人体模式) | VESD | 2K | V |
控制方式
SK6812 的数据传输方式比起一般的外设来说,有点特别,每个位都是一个码元,然后通过控制码元的高低电平时间来控制该位的高低
时序表名称 | min | typic | max | 单位 |
---|---|---|---|---|
T | 1.20 | - | - | us |
T0H | 0.2 | 0.3 | 0.4 | us |
T0L | 0.8 | - | - | us |
T1H | 0.6 | 0.67 | 1.0 | us |
T1L | 0.2 | - | - | us |
Trst | $\geq80$ | - | - | us |
协议中每个码元都必须有低电平,每个码元起始为高电平,高电平时间宽度决定了该码元是 0 码还是 1 码,每个码元周期最小为 1.2us,具体的传输方式如下图所示
GPIO 翻转
GPIO 可以通过计算系统主频来计算出单条指令的时间,从而实现纳秒级硬延时。例如对于 AT32F403,设置单片机运行主频为 240MHz,所以它单条指令所需要的时间为 $\frac{1}{240M}=4.167ns$ 。根据上述分析,设置码元时间为 $1.2us$ ,则 0 码的高电平持续时间为 $0.3us$ ,低电平持续时间为 $0.9us$ ,而 1 码的高电平持续时间为 $0.6us$ ,低电平持续时间为 $0.6us$
一般可以利用 __nop()
指令,单条 __nop()
指令所需要的时间是 $4.167ns$ ,所以可以利用 $72$ 条指令来模拟延时 $0.3us$ ,对应的代码如下
1 | // clang-format off |
PWM+DMA
根据上述中每个码元的时间为 $1.2us$ ,因此可以设置 PWM 的周期为 $1.25us$ 即频率为 $800KHz$,通知通过控制重装载值即可控制高低电平的时间,从而实现码元输出。由于 AT32F403 时钟的主频为 $240MHz$ ,因此可以设置预分频值为 2
,设置周期值为 99
即可。由于 PWM 的输出,当计数值小于比较值时,输出低电平,由于码元需要是高电平在前,所以设置 PWM 计数方式为向下计数
设置 0 码元和 1 码元的高低电平占比
- 0 码元: $0.3us$ 高电平, $0.95us$ 低电平,设置比较值为 76
- 1 码元: $0.65us$ 高电平, $0.6us$ 低电平,设置比较值为 50
另外打开该通道的 DMA,传输方向设置为从内存到外设,代码实现如下
1 |
|
SPI+DMA
由上述所述,可知每个码元的持续时间大概在 $1.2\sim\infty us$ 之间, 为了传输的效率,此处要选择尽量小的周期。使用 SPI 控制时,可以通过发送 8 bits 数据来控制,可以利用单个 8 bit 数据所形成的波形来分别表示 0 码和 1 码
- 0 码:前 2 个比特为高电平,后 6 个比特为低电平,因此发送的数据应当是
0b11000000
- 1 码:前 5 个比特为高电平,后 3 个比特为低电平,因此发送的数据为
0b11111000
假设 SPI 的频率为 $x MHz$ (也就是发送一个比特位的频率),则可以调整 $x$ 的大小,使得 SPI 能在 $1.2us\sim\infty$ 之间发送一个字节,计算可得 $x\leq6.67MHz$ ,所以在设置 SPI 时就不能设置的主频过高,而且需要注意设置 SPI 的帧位个数为 8 位,且高位先传输(MSB First)。对应的代码如下
1 |
|
进阶控制方式——呼吸灯
SK6812 可以通过控制 RGB 三色值来控制彩灯的颜色,当然还可以通过控制颜色空间 HSV,再转换为 RGB 来控制颜色空间
RGB 转 HSV
代码实现
1 | static void rgb_to_hsv(float* h, float* s, float* v, uint8_t r, uint8_t g, uint8_t b) { |
HSV 转 RGB
代码实现
1 | static void hsv_to_rgb(float h, float s, float v, uint8_t* r, uint8_t* g, uint8_t* b) { |
一个示例代码
1 | void func() { |