相关链接
硬件介绍(PCB设计方案)
模拟时序发送
协议
WS2812是一种异步串行通信,它每一位数据时间是ns级别的
默认是高电平状态
0码:220-380ns高电平+580-1600ns低电平
1码:580-1600ns高电平+220-380ns低电平
复位码:>280us低电平
24Bit数据来代表GRB的亮度值
从高位到低位发送,分别按照G->R->B的顺序发送
先发送第一个灯的数据(离Dat输入直连的那个灯)
例子:
分别发送红绿蓝三色的数据给3个ws2812
外设设置
硬件PWM
分析
STM32F4系列的系统频率为84MHz
定时器的最高频率(不改动时钟设置的情况下)为84MHz,也就是11ns左右,
完全可足以驱动WS2812(几百ns级的)
设系统频率为 f 单位是MHz
则0码.1码的高电平占空比 n0 n1(0码高电平取370ns左右,1码高电平取850ns左右)
重载值 n 分别为
n 0 = 0.37 f , n 1 = 0.85 f , n = 1.25 f n_{0}=0.37f,n_{1}=0.85f,n=1.25f n0=0.37f,n1=0.85f,n=1.25f
四舍五入一下,取n=105,n0=32,n1=71
HAL设置
PWM输出,不分频,重载值设为105
其他不需要改动
PWM的输出GPIO设置为开漏浮空输出(外接5V上拉)
DMA
分析
DMA是外设直接访问内存的一个手段,可以快速的传递数据
我们在这个例子里用到的是
将内存里的关于占空比的数据发送给定时器的比较值
让其发送具有编码含义的脉冲
PWM+DMA的一些问题:
使用HAL库DMA给定时器发送比较值时,会一次性发送多个(数组地址和长度)
定时器每当发送一个脉冲后,更改为下一个比较值,直到数组被发送完成,触发一次DMA中断
这样我们就可以利用这个特点发送WS2812的数据
HAL设置
设置PWM的输出通道为响应通道
从内存到外设模式(Direction的设置)
普通模式(Mode)
数据宽度(Data Width)为字(32bit)
方案设计
设置两个缓冲区,每个长度为25个uint32(其中前24个是发送的数据码,第25个是保持占空比为0)
缓冲区的第25个数据为0是确保DMA中断响应时是低电平(码字的低电平时间稍长,3-4us无所谓,但是不能出现而外的高电平)
发送复位码时则使用单独划分好的,在Flash里的静态数组
在DMA中断响应时,计算下次发送的数据给到缓冲区
这样只需要3个变量数组(2个缓冲区,1个存放灯带颜色数据),1个常量数组(复位码)
操作流程:
- 更改颜色信息给颜色数组
- 发送复位码,开启PWM+DMA
- 解码第一位颜色数据给到缓冲区0
- (DMA中断)发送缓冲区0给定时器
- (DMA中断)计算第二位颜色数据给缓冲区1
- (下一个)(DMA中断)发送缓冲区1给定时器
- (DMA中断)计算第三位颜色数据给缓冲区0
以此类推,直到发送完成
代码实现
解码函数
/**
* @brief 将uint32转为发送的数据
* @param Data:颜色数据
* @param Ret:解码后的数据(PWM占空比)
* @return
* @author HZ12138
* @date 2022-10-03 18:03:17
*/
void WS2812_uint32ToData(uint32_t Data, uint32_t *Ret)
{
uint32_t zj = Data;
uint8_t *p = (uint8_t *)&zj;
uint8_t R = 0, G = 0, B = 0;
B = *(p); // B
G = *(p + 1); // G
R = *(p + 2); // R
zj = (G << 16) | (R << 8) | B;
for (int i = 0; i < 24; i++)
{
if (zj & (1 << 23))
Ret[i] = WS2812_Code_1;
else
Ret[i] = WS2812_Code_0;
zj <<= 1;
}
Ret[24] = 0;
}
用一个指针p,将数据中的RGB数据解码出来
然后使用一个循环,将数据存入缓冲区中
开始发送和复位
const uint32_t WS2812_Rst[240] = {0}; //复位码缓冲区
/**
* @brief 开始发送颜色数据
* @param 无
* @return 无
* @author HZ12138
* @date 2022-10-03 18:05:13
*/
void WS2812_Start(void)
{
HAL_TIM_PWM_Start_DMA(&WS2812_TIM, WS2812_TIM_Channel, (uint32_t *)WS2812_Rst, 240);
WS2812_uint32ToData(WS2812_Data[0], WS2812_SendBuf0);
WS2812_En = 1;
}
/**
* @brief 发送复位码
* @param 无
* @return 无
* @author HZ12138
* @date 2022-10-03 18:05:33
*/
void WS2812_Code_Reast(void)
{
HAL_TIM_PWM_Start_DMA(&WS2812_TIM, WS2812_TIM_Channel, (uint32_t *)WS2812_Rst, 240);
WS2812_En = 0;
}
复位码缓冲区是常量数组,开始发送和单独复位码的区别WS2812_En的标志
当WS2812_En为1时才执行交替发送缓冲区0和1
发送函数
/**
* @brief 发送函数(DMA中断调用)
* @param 无
* @return 无
* @author HZ12138
* @date 2022-10-03 18:04:50
*/
void WS2812_Send(void)
{
static uint32_t j = 0;
static uint32_t ins = 0;
if (WS2812_En == 1)
{
if (j == WS2812_Num)
{
j = 0;
HAL_TIM_PWM_Stop_DMA(&WS2812_TIM, WS2812_TIM_Channel);
WS2812_En = 0;
return;
}
j++;
if (ins == 0)
{
HAL_TIM_PWM_Start_DMA(&WS2812_TIM, WS2812_TIM_Channel, WS2812_SendBuf0, 25);
WS2812_uint32ToData(WS2812_Data[j], WS2812_SendBuf1);
ins = 1;
}
else
{
HAL_TIM_PWM_Start_DMA(&WS2812_TIM, WS2812_TIM_Channel, WS2812_SendBuf1, 25);
WS2812_uint32ToData(WS2812_Data[j], WS2812_SendBuf0);
ins = 0;
}
}
}
这个函数是在DMA中断中调用
思路如同前面所说
波形
整个波形全貌↑
两次发送的间隔(DMA中断中导致的)↑
1码↑
0码↑
成品
GitHub文章来源:https://www.toymoban.com/news/detail-612332.html
百度网盘文章来源地址https://www.toymoban.com/news/detail-612332.html
到了这里,关于【STM32F4系列】【HAL库】【自制库】WS2812(软件部分)(PWM+DMA)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!