2@TOC
(1)前言
做灯带ws2812其实有一段时间了,中间遇到很多问题,从开始的学习,到后来慢慢熟悉,再到后来尝试点很多灯带,做过非常多的实验了,自己新建工程,几乎尝试过很多条道路,并且每个选择都有尝试,这篇是一个汇总篇,将为给各位讲述移植时注意的函数,共享这些知识为其绕过一些坑。
写在前面,其实自己之前写过很多文章了,里面有很多“细节点”,对应新手而言,非常建议注意下这些点。
本次实验非常重要的两个表,你要是做相关实验,可能会反复看这两张表。
—DMA1请求映射表
—DMA2请求映射表
(2)环境
- 软件环境:STM32cubeIDE 1.8.0
- 硬件芯片:STM32F407ZGT6
- HAL库版本:stm32cube_fw_f4_v1262
- 灯带型号:WS2812
(3)综合汇总
(1)作为最基础的,这其实是我最早调试成功的,也是最先使用双缓存的,并且是最开始发现函数顺序的重要性的点,算是开始吧。
【关于STM32F4xx使用DMA+TIM3_PWM调试灯带WS2812过程记录】
(2)作为基础的,这篇讲述了使用TIM2点亮灯带,这里同时遇到最早的问题,32位寄存器与TIM3的16位寄存器。
【软件STM32cubeIDE下STM32F4xx使用32位定时器2(TIM2)用DMA+PWM点亮灯带WS2812-基础样例
(3)如果你已经会了基本的点灯带,那么当你想让你的灯带颜色更漂亮些,那么可以推荐看下列文章。
【关于STM32F4xx使用DMA+PWM调试灯带WS2812过程记录+进阶】
(4)后来我做个其它尝试,希望可以复用同一数据流,其实这种方式不是很推荐,请教大佬后,才实现,但需要在回调函数不断初始化。
【关于STM32F4 使用DMA数据流-冲突下-复用输出PWM操作问题的记录以及解决办法】
(5)再后来,因为需求变更,自己也想知道一些边界的存在,所有尝试所有数据流点灯的。
【软件STM32cubeIDE下STM32F4xx使用DMA+定时器+推PWM+点亮灯带WS2812相关-进阶(全数据流版)】
(4)移植相关讲解
写在前面,我们看一些基础案例,最终希望能运用在我们自己的项目上的,而不是说以基础样例为基础,这就会涉及,弄明白了样例后,将相应代码添加到自己的工程里,同时不与现有自己项目工程冲突,而且移植时,一定是你自己基础样例能跑起来了,不要没跑起来,就移植,这样你都不知道基础样例问题还是移植中产生问题。
本次实验我将选取我以前一个样例,同时也会上传其代码,以此代码为样例,来说明要移植哪些代码。
(1)第一步:MX_GPIO_Init()与 MX_DMA_Init();
这两函数里面内容其实挺少的的,但是稍微不注意,灯带就可能跑不起来。
1)MX_GPIO_Init()内容如下,主要是RCC的使能。
2)MX_DMA_Init()内容如下,主要是数据流优先级以及中断使能。
(2)第二步:定时器组初始化函数,例如void MX_TIM2_Init(void);
(1)无论STM32cubeIDE软件还是STM32cubeMX软件,通过配置,软件都会为你自动生成好这些函数,只要你设置没错,但是移植时要注意有时候,我们不只是使用一组定时器,这里也用通道分配,只要你确定选择使用一下通道了,就不要随意更改,移植时,主要将里面内容复制。
- 注意函数顺序
- 移植目标要是有系统时,注意是否自动初始化,这样会打乱原本函数顺序
- 注意里面内容
如下图所示:
(3)第三步:在stm32f4xx_hal_msp.c文件下,一些函数以及结构体声明;
(1)主要是这几个函数,圈出来这几个,为啥不说是三个呢,因为我见过有些编译器生成出来的,不叫这个名字的,类似名字,但带base这样。
(2)加入结构体声明
(4)第四步:在stm32f4xx_it.c文件下,数据流函数加入中断函数以及结构体声明;
(1)有时候可能其他,比如串口,SPI占用了这个数据流,是不能共存的,我们要去掉相应使能,并且更换我们需要的。
(2)结构体声明
(5)第五步:灯带驱动编写以及移植。
(1)这块弄过很多次了,主要是功能是往数组里赛灯带数据的功能和一些声明
uint32_t tim2_ccr1_value[]={10,20,30,40,50,60,70,80,90};
uint16_t tim3_ccr1_value[]={10,20,30,40,50,60,70,80,90};
#define ONE_PULSE (59) //1
#define ZERO_PULSE (29) //0
#define RESET_PULSE (48) //80 ¸
#define LED_NUMS (22) //led ¸
#define LED_DATA_LEN (24) //led
#define WS2812_DATA_LEN (LED_NUMS*LED_DATA_LEN) //ws2812
uint16_t RGB_buffur[RESET_PULSE + WS2812_DATA_LEN] = { 0 };
uint32_t RGB_buffur1[RESET_PULSE + WS2812_DATA_LEN] = { 0 };
void ws2812_set_RGB(uint8_t R, uint8_t G, uint8_t B, uint16_t num)
{
uint16_t * p = (RGB_buffur + RESET_PULSE) + (num * LED_DATA_LEN);
for (uint16_t i = 0;i < 8;i++)
{
p[i] = (G << i) & (0x80)?ONE_PULSE:ZERO_PULSE;
p[i + 8] = (R << i) & (0x80)?ONE_PULSE:ZERO_PULSE;
p[i + 16] = (B << i) & (0x80)?ONE_PULSE:ZERO_PULSE;
}
}
void ws2812_set_RGB1(uint8_t R, uint8_t G, uint8_t B, uint16_t num)
{
uint32_t * p = (RGB_buffur1 + RESET_PULSE) + (num * LED_DATA_LEN);
for (uint16_t i = 0;i < 8;i++)
{
p[i] = (G << i) & (0x80)?ONE_PULSE:ZERO_PULSE;
p[i + 8] = (R << i) & (0x80)?ONE_PULSE:ZERO_PULSE;
p[i + 16] = (B << i) & (0x80)?ONE_PULSE:ZERO_PULSE;
}
}
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
//if(htim)
__HAL_TIM_SetCompare(htim, TIM_CHANNEL_1,0); //占空比清0,若不清会导致灯珠颜色不�???
HAL_TIM_PWM_Stop_DMA(htim,TIM_CHANNEL_1);
__HAL_TIM_SetCompare(htim, TIM_CHANNEL_2,0); //占空比清0,若不清会导致灯珠颜色不�???
HAL_TIM_PWM_Stop_DMA(htim,TIM_CHANNEL_2);
__HAL_TIM_SetCompare(htim, TIM_CHANNEL_3,0); //占空比清0,若不清会导致灯珠颜色不�???
HAL_TIM_PWM_Stop_DMA(htim,TIM_CHANNEL_3);
__HAL_TIM_SetCompare(htim, TIM_CHANNEL_4,0); //占空比清0,若不清会导致灯珠颜色不�???
HAL_TIM_PWM_Stop_DMA(htim,TIM_CHANNEL_4);
}
(6)第六步:生成想要的灯带颜色。
(1)这部分,因为之前说过,如何生成好看的彩灯,这一步其实是可以单独列出一个部分
我这部分是找到网上资料,然后自己工程调优过的,细节也说了如下文章。
如果你已经会了基本的点灯带,那么当你想让你的灯带颜色更漂亮些,那么可以推荐看下列文章。
【关于STM32F4xx使用DMA+PWM调试灯带WS2812过程记录+进阶】
(7)第七步:函数输出。
(1)这块也比较明确,就一个函数,直接DMA推送,优点是不耗费CPU资源。
(6)实验代码
实验代码连接:https://download.csdn.net/download/qq_22146161/87383562
(7)细节点
细节点还是零零散散将一些的,这里有可能有之前说过的。
(1)移植目标为带有系统时,要注意函数初始化顺序。
个人案例真实写照,当时移植是RT-thread,虽然系统为我们带来方便,但是有时候调试是真摸不到头脑。
这块因为系统原因,定时器先初始化了,结果就是跑不起来,样例一上就行,一移植就废。
(2)一些容易误导名字,无法使用数据流-边界问题
这属于边界问题,当时为了探究F4边界,想把所有数据流都点灯带,结果16个通道,只是点了14个,想TIM_TRIG和TIM1_UP这种,是不能用的。另外TIM1_CH1N也尝试过,不能用。
(9)总结
更多的测试,有些我可能无法传递,也许是我忘了,也许是文字表达有限,我这些文章是你的辅助,一定要动手自己尝试下,同时也非常欢迎讨论。