ws2812做为RGB灯,在很多场合都是非常有用的,我准备使用STM32H755的M4内核驱动时,遇到了一些问题,因此先用STM32F103来验证一下,找到库存的STM32103的开发板,来实现驱动,特记录如下:
1、使用STM32CubeMX新建基于STM32F103C8Tx的工程:
2、打开外部时钟,因为内部时钟最高只能跑到64MHz。
3、在时钟配置界面,配置72MHz的总线时钟。
从上面我们得到Timer的时钟总线为72MHz。
4、打开TIM1的配置界面配置如下:
这里我们需要将GPIO的输出速度选择为高速模式。
5、配置pwm
在定时器配置中,我们根据WS2812的最大传输速率800kbps,设置定时器不分频和计数周期为89+1,这样下来波形的频率为 72M /(89+1) = 800K ,并且一个波形的周期为 1 / 800 = 1.25us
6、配置DMA
我们打开DMA的界面,添加一个DMA,选择方向为从内存到外设,内存为递增,数据宽度为半字节,即16bit。
到此界面配置结束,生成工程后,使用mdk打开工程。
【代码添加】
1、新建RGB.c添加代码如下:
#include "RGB.h" #include "main.h" #include "tim.h" uint16_t RGB_buffur[Reste_Data + WS2812_Data_Len] = { 0 }; //数据缓存数组 void WS2812_Display_1(uint32_t Color, uint16_t num) { //指针偏移:需要跳过复位信号的N个0 uint16_t* p = (RGB_buffur + Reste_Data) + (num * Led_Data_Len); for (uint8_t i = 0; i < 8; ++i) p[i+8]= (((Color << i) & 0X800000) ? Hight_Data :Low_Data); for (uint8_t i = 8; i < 16; ++i) p[i-8]= (((Color << i) & 0X800000) ? Hight_Data :Low_Data); for (uint8_t i = 16; i < 24; ++i) p[i]= (((Color << i) & 0X800000) ? Hight_Data :Low_Data); } void WS2812_Display_2( uint8_t red, uint8_t green, uint8_t blue,uint16_t num) { uint8_t i; uint32_t Color=(green << 16 | red << 8 | blue);//将2个8位数据合并转化为32位数据类型 //指针偏移:需要跳过复位信号的N个0 uint16_t* p = (RGB_buffur + Reste_Data) + (num * Led_Data_Len); for (i = 0; i < 24; ++i) //对数组进行编辑 p[i]= (((Color << i) & 0X800000) ? Hight_Data : Low_Data); } void WS2812_Number_4(uint32_t Color1,uint32_t Color2,uint32_t Color3,uint32_t Color4) { uint16_t RGB_Buff_4[Reste_Data + 4 * WS2812_Data_Len] = { 0 }; uint16_t* p; uint32_t Color; for( uint8_t k=0;k<4;k++) { switch (k) //进行指针偏移 { case 0: p= (RGB_Buff_4 + Reste_Data) + (0 * Led_Data_Len),Color=Color1;break; case 1: p= (RGB_Buff_4 + Reste_Data) + (1 * Led_Data_Len),Color=Color2;break; case 2: p= (RGB_Buff_4 + Reste_Data) + (2 * Led_Data_Len),Color=Color3;break; case 3: p= (RGB_Buff_4 + Reste_Data) + (3 * Led_Data_Len),Color=Color4;break; default : ;break; } for (uint8_t i = 0; i < 8; ++i) //对数组进行编辑 { for (uint8_t i = 0; i < 8; ++i) p[i+8]= (((Color << i) & 0X800000) ? Hight_Data :Low_Data); for (uint8_t i = 8; i < 16; ++i) p[i-8]= (((Color << i) & 0X800000) ? Hight_Data :Low_Data); for (uint8_t i = 16; i < 24; ++i) p[i]= (((Color << i) & 0X800000) ? Hight_Data :Low_Data); } } HAL_TIM_PWM_Start_DMA(&htim1,TIM_CHANNEL_1,(uint32_t *)RGB_Buff_4,(176));//启动DMA传输 } // DMA 传输完成回调函数 void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) { HAL_TIM_PWM_Stop_DMA(&htim1,TIM_CHANNEL_1); }
2、添加RGB.h代码如下:
#ifndef __RGB_H #define __RGB_H #include "main.h" #define Hight_Data ( 64 ) //1 码相对计数值 #define Low_Data ( 36 ) //0 码相对计数值 #define Reste_Data ( 80 ) //80 复位电平相对计数值 #define Led_Num ( 4 ) //WS2812灯个数 #define Led_Data_Len ( 24 ) //WS2812数据长度,单个需要24个字节 #define WS2812_Data_Len (Led_Num * Led_Data_Len) //ws2812级联后需要的数组长度 //uint16_t RGB_buffur[Reste_Data + WS2812_Data_Len] = { 0 }; //数据缓存数组 void WS2812_Display_1(uint32_t Color, uint16_t num); void WS2812_Display_2( uint8_t red, uint8_t green, uint8_t blue,uint16_t num); void WS2812_Number_4(uint32_t Color1,uint32_t Color2,uint32_t Color3,uint32_t Color4);//封装好的四个灯函数,只需要分别输入四个灯的颜色即可 #endif
3、Main
首先引RGB.h头文件
再使用extern一下数组
extern uint16_t RGB_buffur[Reste_Data + WS2812_Data_Len];
在while中添加周期的亮灯:
WS2812_Number_4(0x180000,0x001800,0x000018,0); HAL_Delay(500); WS2812_Number_4(0,0x180000,0x001800,0x000018); HAL_Delay(500); WS2812_Number_4(0x000018,0,0x180000,0x001800); HAL_Delay(500); WS2812_Number_4(0x001800,0x000018,0,0x180000); HAL_Delay(500);
【验证】
将ws2812的DI引脚接到PA8,VCC与GND接到开发板的电源上,将程序下载到开发板后,可以看到如期点亮了WB2812