完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
一、WS2812简介
WS2812使用“单总线”驱动,可以级联驱动n个,当然n是有限制的。时序超级简单,也有一定难度。类似的帖子网上有很多,在此不再赘述。 WS2812驱动方式大概有以下几种: 1.使用GPIO模拟,中间加延时实现“0”、“1”的时序,延时需要借助示波器、逻辑分析仪来调试。 2.使用硬件定时器+PWM实现,当然要控制发送PWM脉冲个数需要严格控制,PWM+DMA的组合程序设计更加简单、更加便捷,但是调试起来要麻烦一些。 3.使用SPI来模拟ws2812的时序,这也是一个很讨巧的办法,当然需要计算SPI的速率、位数、“0”“1”时序要发送的对应的数值。当然SPI+DMA的话,更省cpu。 本帖主要使用硬件定时器PWM+DMA方式实现。 二、CUBEMX初始化代码配置 1.打开cubemx,选择STM32G070RBT6,这是我使用的cpu的型号。 2.时钟树配置,使用内部时钟,sysclk最后为64MHZ, 3.SYS配置,为了调试方便,勾选SW调试接口,tick时钟选择默认systick不变。 4.定时器参数设置,这里使用定时器TIM1的CH3通道 ***注意,定时器的计数器预加载要关闭。 DMA参数设置,选择普通模式,方向从内存到外设,注意数据宽度和字长不同,以及地址是否增加。 5.project manager工程设置 6. 代码生成器设置 7.高级设置 (这里选择LL库) 8.代码生成,打开工程。这里使用keil mdk开发环境。生成的工程的代码结构 9.编辑tim.c 在文件开头包含头文件 #include "bsp_ws2812.h" 在MX_TIM1_Init()函数最后添加如下几行代码 LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_7,LEN);//设置dma数据传输个数/长度 LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_7,(uint32_t)pixelBuffer);//设置内存地址,也就是设置buffer地址 LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_7,(uint32_t)&(TIM1->CCR3));//设置外设地址 LL_DMA_ClearFlag_GI7(DMA1);//清除中断标志 LL_DMA_ClearFlag_TC7(DMA1);//清除中断标志 LL_DMA_EnableIT_TC(DMA1,LL_DMA_CHANNEL_7);//使能传送完成中断 LL_TIM_EnableDMAReq_CC3(TIM1);//使能TIM1的CC3 DMA请求 LL_TIM_EnableAllOutputs(TIM1);//使能TIM的输出 LL_TIM_CC_SetDMAReqTrigger(TIM1,LL_TIM_CCDMAREQUEST_CC);//设置TIM1 DMA请求触发器 LL_TIM_CC_EnableChannel(TIM1,LL_TIM_CHANNEL_CH3);//使能TIM1 的cc通道ch3 这样,每次使用dma的时候,只需要重新设置数据传送长度、使能定时器和DMA通道就可以进行数据传输了。如下: LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_7, LEN); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_7); LL_TIM_EnableCounter(TIM1); 10.编辑stm32g0xx_it.c文件 主要是dma tc中断后,失能dma和tim,并清除中断标志。 void DMA1_Ch4_7_DMAMUX1_OVR_IRQHandler(void) { /* USER CODE BEGIN DMA1_Ch4_7_DMAMUX1_OVR_IRQn 0 */ if(LL_DMA_IsActiveFlag_TC7(DMA1)) { ws2812_xfer_flag=0; LL_DMA_ClearFlag_GI7(DMA1); LL_DMA_ClearFlag_TC7(DMA1); LL_DMA_DisableChannel(DMA1,LL_DMA_CHANNEL_7); LL_TIM_DisableCounter(TIM1); } /* USER CODE END DMA1_Ch4_7_DMAMUX1_OVR_IRQn 0 */ /* USER CODE BEGIN DMA1_Ch4_7_DMAMUX1_OVR_IRQn 1 */ /* USER CODE END DMA1_Ch4_7_DMAMUX1_OVR_IRQn 1 */ } 11.添加bsp_ws2812.c文件和bsp_ws2812.h文件,此ws2812特效显示代码主要参考一位网友。 11.1)bsp_ws2812.h #ifndef __BSP_WS2812_H_ #define __BSP_WS2812_H_ #include "main.h" #define PIXEL_NUM 13 #define GRB 24 //3*8 #define LEN (PIXEL_NUM*GRB) #define WS_LOW 31 #define WS_HIGH 60 extern volatile uint8_t ws2812_xfer_flag; extern uint8_t pixelBuffer[PIXEL_NUM][GRB]; void WS281x_Init(void); void WS281x_CloseAll(void); uint32_t WS281x_Color(uint8_t red, uint8_t green, uint8_t blue); void WS281x_SetPixelColor(uint16_t n, uint32_t GRBColor); void WS281x_SetPixelRGB(uint16_t n ,uint8_t red, uint8_t green, uint8_t blue); void WS281x_Show(void); void WS281x_RainbowCycle(uint8_t wait); void WS281x_TheaterChase(uint32_t c, uint8_t wait); void WS281x_ColorWipe(uint32_t c, uint8_t wait); void WS281x_Rainbow(uint8_t wait); void WS281x_TheaterChaseRainbow(uint8_t wait); #endif /********************************End of File************************************/ 11.2)bsp_ws2812.c文件 #include "bsp_ws2812.h" volatile uint8_t ws2812_xfer_flag = 0; uint8_t pixelBuffer[PIXEL_NUM][GRB] = {0}; /******************************************************************************* ** 函数名称: WS281x_CloseAll ** 功能描述: ********************************************************************************/ void WS281x_CloseAll(void) { uint8_t i; uint8_t j; for(i = 0; i < PIXEL_NUM; ++i) { for(j = 0; j < 24; ++j) { pixelBuffer[j] = WS_LOW; } } WS281x_Show(); } /******************************************************************************* ** 函数名称: WS281x_Color ** 功能描述: ********************************************************************************/ uint32_t WS281x_Color(uint8_t red, uint8_t green, uint8_t blue) { return green << 16 | red << 8 | blue; } /******************************************************************************* ** 函数名称: WS281x_SetPixelColor ** 功能描述: ********************************************************************************/ void WS281x_SetPixelColor(uint16_t n, uint32_t GRBColor) { uint8_t i; if(n < PIXEL_NUM) { for(i = 0; i < GRB; i++) { pixelBuffer[n] = ((GRBColor << i) & 0x800000) ? WS_HIGH : WS_LOW; } } } /******************************************************************************* ** 函数名称: WS281x_SetPixelRGB ** 功能描述: ********************************************************************************/ void WS281x_SetPixelRGB(uint16_t n , uint8_t red, uint8_t green, uint8_t blue) { uint8_t i; if(n < PIXEL_NUM) { for(i = 0; i < GRB; ++i) { pixelBuffer[n] = (((WS281x_Color(red, green, blue) << i) & 0X800000) ? WS_HIGH : WS_LOW); } } } /******************************************************************************* ** 函数名称: WS281x_Show ** 功能描述: ********************************************************************************/ void WS281x_Show(void) { LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_7, LEN); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_7); LL_TIM_EnableCounter(TIM1); while(ws2812_xfer_flag); //dma发送完成后,会清零此flag。 } /******************************************************************************* ** 函数名称: WS281x_Wheel ** 功能描述: Input a value 0 to 255 to get a color value. The colours are a transition r - g - b - back to r. ********************************************************************************/ uint32_t WS281x_Wheel(uint8_t wheelPos) { wheelPos = 255 - wheelPos; if(wheelPos < 85) { return WS281x_Color(255 - wheelPos * 3, 0, wheelPos * 3); } if(wheelPos < 170) { wheelPos -= 85; return WS281x_Color(0, wheelPos * 3, 255 - wheelPos * 3); } wheelPos -= 170; return WS281x_Color(wheelPos * 3, 255 - wheelPos * 3, 0); } /******************************************************************************* ** 函数名称: WS281x_ColorWipe ** 功能描述: Fill the dots one after the other with a color ********************************************************************************/ void WS281x_ColorWipe(uint32_t c, uint8_t wait) { for(uint16_t i = 0; i < PIXEL_NUM; i++) { WS281x_SetPixelColor(i, c); WS281x_Show(); LL_mDelay(wait); } } /******************************************************************************* ** 函数名称: WS281x_Rainbow ** 功能描述: ********************************************************************************/ void WS281x_Rainbow(uint8_t wait) { uint16_t i, j; for(j = 0; j < 256; j++) { for(i = 0; i < PIXEL_NUM; i++) { WS281x_SetPixelColor(i, WS281x_Wheel((i + j) & 255)); } WS281x_Show(); LL_mDelay(wait); } } /******************************************************************************* ** 函数名称: WS281x_RainbowCycle ** 功能描述: Slightly different, this makes the rainbow equally distributed throughout ********************************************************************************/ void WS281x_RainbowCycle(uint8_t wait) { uint16_t i, j; for(j = 0; j < 256 * 5; j++) // 5 cycles of all colors on wheel { for(i = 0; i < PIXEL_NUM; i++) { WS281x_SetPixelColor(i, WS281x_Wheel(((i * 256 / PIXEL_NUM) + j) & 255)); } WS281x_Show(); LL_mDelay(wait); } } /******************************************************************************* ** 函数名称: WS281x_TheaterChase ** 功能描述: Theatre-style crawling lights. ********************************************************************************/ void WS281x_TheaterChase(uint32_t c, uint8_t wait) { for (int j = 0; j < 10; j++) //do 10 cycles of chasing { for (int q = 0; q < 3; q++) { for (uint16_t i = 0; i < PIXEL_NUM; i = i + 3) { WS281x_SetPixelColor(i + q, c); //turn every third pixel on } WS281x_Show(); LL_mDelay(wait); for (uint16_t i = 0; i < PIXEL_NUM; i = i + 3) { WS281x_SetPixelColor(i + q, 0); //turn every third pixel off } } } } /******************************************************************************* ** 函数名称: WS281x_TheaterChaseRainbow ** 功能描述: Theatre-style crawling lights with rainbow effect ********************************************************************************/ void WS281x_TheaterChaseRainbow(uint8_t wait) { for (int j = 0; j < 256; j++) // cycle all 256 colors in the wheel { for (int q = 0; q < 3; q++) { for (uint16_t i = 0; i < PIXEL_NUM; i = i + 3) { WS281x_SetPixelColor(i + q, WS281x_Wheel( (i + j) % 255)); //turn every third pixel on } WS281x_Show(); LL_mDelay(wait); for (uint16_t i = 0; i < PIXEL_NUM; i = i + 3) { WS281x_SetPixelColor(i + q, 0); //turn every third pixel off } } } } /********************************End of File************************************/ 12.main.c中的使用 12.1)包含头文件 #include "bsp_ws2812.h" 12.2)显示 只需要在需要显示的地方调用如下的函数就可以了,当然还有别的特效函数没有在此显示出来。 WS281x_ColorWipe(30, 100); WS281x_ColorWipe(30 << 8, 100); WS281x_ColorWipe(30 << 16, 100); WS281x_TheaterChaseRainbow ( 20 ); 3.附上输出波形 一次PWM波的波形放大 三、后记 之所以使用LL库,是因为在使用HAL库的时候,无法实现功能。封装的太狠了,导致使用的灵活性不够。 调用库函数TIM_PWM_START_DMA(…),只能正确传输一次,之后,DMA无法正确完成,无PWM产生,困了3天,选择用LL库来完成,结果简单很多。 |
|
|
|
只有小组成员才能发言,加入小组>>
3309 浏览 9 评论
2988 浏览 16 评论
3490 浏览 1 评论
9050 浏览 16 评论
4084 浏览 18 评论
1168浏览 3评论
601浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
592浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2329浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1892浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-19 15:30 , Processed in 1.388342 second(s), Total 80, Slave 61 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号