本帖最后由 6552918 于 2022-3-4 22:43 编辑
#申请原创#
@21小跑堂
这次的N32优质帖征集活动不错,可以更好的体验国民技术的通用MCU的特点,能够让大家更深入的体验国民的通用MCU,也能让大家看到更多的干货,在此我来抛个砖!!! 这次分享的是使用PWM+DMA驱动WS2812B(兼容WS2811)的方式和源码,源码内支持查询驱动方式和中断驱动方式,是一款可以产品级应用的方式,在此源码基础上可以根据自己的需求修改即可。 下面我们先看看RGB彩灯WS2812B和WS2811的简单介绍 WS2812B和WS2811在供电;驱动数量;24bit数据结构和0 1码高低电平定义上有差别,但大致是兼容的,在代码里我通过宏定义进行了兼容。 接下来我介绍一下具体源码 源码由5个文件组成 delay.c -> 官方BSP内的延时函数 支持微妙 毫秒延时 delay.h -> 官方BSP内的延时函数头文件 n32g43x_it.c -> 中断函数文件 WS281X.c -> WS2812B和WS2811的驱动文件 WS281X.h -> WS2812B和WS2811的驱动文件头文件
delay.c和delay.h是国民官方BSP内的延时函数文件,文件内实现的us和ms延时函数,可以方便的使用,在使用相关延时函数前需要调用void delay_init(void)对系统时钟进行初始化。 我重点介绍一下n32g43x_it.c ;WS281X.c ;WS281X.h这3个文件
WS281X.h内定义了和灯珠驱动的相关宏定义 //#define WS2811 -> 芯片为WS2811开启此宏 两宏互斥 #define WS2812B -> 芯片为WS2812B开启此宏 两宏互斥 #define PWM_DMA_IRQ -> 使用PWM DMA中断开启此宏
#define PIXEL_NUM1 4 //灯带1灯数(组数) #define PIXEL_NUM2 3 //灯带2灯数(组数) #define PIXEL_NUM3 2 //灯带3灯数(组数) #define PIXEL_NUM4 1 //灯带4灯数(组数) #define PIXEL_NUM5 1 //灯带5灯数(组数) #define PIXEL_NUM (PIXEL_NUM1+PIXEL_NUM2+PIXEL_NUM3+PIXEL_NUM4+PIXEL_NUM5) 定义了驱动的灯数(组数);
定义了WS2811和WS2812B的0 1码PWM占空比定义,这里WS_HIGH和WS_LOW的值会再后面初始化函数时介绍。
接下来我来介绍一下WS281X.c这个文件 函数void WS281x_Init(void) 对PWM输出用到的GPIO进行了初始化;对PWM输出相关的定时器进行了初始化,对PWM输出相关的DMA进行了初始化; 对PWM输出相关的中断进行了初始化;在相关外设初始化前要打开相关外设的时钟。 PWM输出使用GPIOA的6脚(TIM3的通道1),开启复用模式
这里DMA使用的是通道2,在开启DMA模式时通过宏定义PWM_DMA_IRQ开启了DMA的NVIC相关操作。 这里重点介绍一下N32G435的DMA,尽管通道不多,但真的十分灵活,所有请求都可以映射到任意8个通道上,这让使用变得非常自由。DMA_RequestRemap(DMA_REMAP_TIM3_UP, DMA, DMA_CH2, ENABLE);这个函数实现了请求与通道的映射。
DMA_InitStructure.BufSize = 0; //传输次数-》为0不发送 当DMA传输次数为0时,即使有相关请求也不会进行传输操作!!! 在这里使用更新事件触发DMA请求。 在定时器相关初始化过程中,涉及到PWM的周期和占空比问题,在用户手册中相关描述如下: 在函数中相关参数为 TIM_TimeBaseInitStruct.Period= 68 - 1; /* 54M / 68 = 794.12K (小于800K) */ (TIM3的时钟总线最大频率为54M,而WS2812B的数据发送速度最大为800K,所以将TIM3_AR寄存器值设定为68(794.12K约等于1.26us),以便将输出频率控制在最接近800K的速率以内。) TIM_OCInitStruct.Pulse= 0; (对应的寄存器为TIM3_CDDATx的值,此值由DMA进行传输赋值,以便控制占空比。) 在开启DMA模式时通过宏定义PWM_DMA_IRQ开启了TIM3的NVIC相关操作。 外设初始化完成后,还要对灯珠进行初始化,就行将所要控制的灯珠,都设置为熄灭状态。 void WS281x_CloseAll(void) 函数将显示buffer全部填充为“0”(0码对应的占空比数值),再通过WS281x_Show();控制PWM输出开始。
WS281x_Show()内,先检查DMA传输次数是否为0,不为0说明传输未完成,需要等待完成,再将TIM3_CDDATx的值清0,然后在关闭DMA通道情况下对传输次数赋值,然后开启通道,最后使能TIM3就开始PWM的输出了。在使用查询方式输出情况下,使用PWM_DMA_IRQ宏控制的部分是查询传输是否完成的相关操作。这部分涉及到2块内容,一块是DMA传输是否完成,领一部分是PWM输出是否完成,因为DMA传输完成是先于PWM输出完成的,为保证WS2812B的控制信号准确,要在PWM传输完成后马上关闭PWM输出才行。 本文件的其他函数是和显示效果相关的,可以根据自己的需求选择,再此就不详细介绍了。
当使用PWM+DMA中断传输方式,就涉及到n32g43x_it.c文件的内容。 由于开启PWM+DMA中断方式后,DMA传输完中断先产生,会先进void DMA_Channel2_IRQHandler(void)函数,在函数内确认是传输完中断后进入相关操作,先清除传输完成标志,关闭通道,然后再开启TIM的更新中断,在PWM完成最后一个周期的输出后进入TIM3的更新中断,再更新中断内要先清除TIM3_CDDAT1为0(以便保证输出为低电平,在下图中显示,如果TIM3_CDDAT1不为0,有可能会使输出为高电平,这样是不满足WS2812B的时序要求的),然后清除更新中断标志,关闭TIM3,禁用TIM3的更新中断。 至此,在使用PWM+DMA驱动WS2812B(兼容WS2811)的整个过程就介绍完成,相关源码一并提供,希望对有需求的朋友有所帮助。 源码在附件中,欢迎大家下载。
|