谁不想拥有炫酷的小彩灯呢?WS2812B可以给你机会......
博主使用STM32驱动WS2812B主要参考了这位佬的文章,因为需求问题,采用了Cortex-M0的stm32f030f4p6(16k的flash,4k的sram)来驱动,原文中写的是stm32f103c8t6,个人认为其实区别并不是很大,需要修改部分参数即可移植(cv战士申请出战)。
上图是我的一圈灯,一共8个,第一个LED的数据输入端接的是定时器1的通道2,想看底层原理可以去看佬的文章,本文只介绍如何移植。
CUBEMX配置
首先选择外部石英晶振,我用的是外部12M晶振,然后配置时钟树:
然后打开串行调试:
找到使用的定时器(输出PWM的),并打开定时器时钟,选定输出PWM的通道以及模式:
重点来了:
佬的文章中说的0码PWM占空比(CCR值)算到约为28,1码PWM占空比(CCR值)算到约为58;但人家用的是f1啊,72M时钟频率的,咱的是f0,自然低人一等,只有48M时钟频率。所以在满足灯带频率固定为800KHz的前提下,略微修改即可,设置参数如下:
下面来简单说一下如何修改
因为PWM频率F需要为800KHz,而时钟频率固定为48MHz,根据Fpwm=Fclk / (arr+1)(psc+1),我取psc为0,取arr=59,恰好可以得到 800KHz的频率,所以arr取值为59。因此,计算出PWM0码的CCR值为:0.32 × (59+1)=19.2 ≈ 19;PWM1码的CCR值为:0.64 × (59+1)=38.4 ≈ 38;
接下来配置DMA:
再由于我后面切换显示需要用到定时器中断,所以用了一个定时器3的1ms中断:
配置好生成路径后直接生成代码即可
代码部分:
主函数头文件加个#include "ws2812b.h"即可
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_TIM1_Init();
MX_TIM3_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
led_loop(); //循环显示
}
/* USER CODE END 3 */
}
ws2812b.h文件如下:
#ifndef __WS2812B_H__
#define __WS2812B_H__
#include "main.h"
/*这里是上文计算所得CCR的宏定义*/
#define CODE_1 (38) //1码定时器计数次数
#define CODE_0 (19) //0码定时器计数次数
/*建立一个定义单个LED三原色值大小的结构体*/
typedef struct
{
uint8_t R;
uint8_t G;
uint8_t B;
}RGB_Color_TypeDef;
#define Pixel_NUM 8 //LED数量宏定义,这里我使用一个LED,(单词pixel为像素的意思)
void RGB_SetColor(uint8_t LedId,RGB_Color_TypeDef Color);//给一个LED装载24个颜色数据码(0码和1码)
void Reset_Load(void); //该函数用于将数组最后24个数据变为0,代表RESET_code
void RGB_SendArray(void); //发送最终数组
void RGB_RED(uint16_t Pixel_Len); //显示红灯
void RGB_GREEN(uint16_t Pixel_Len);//显示绿灯
void RGB_BLUE(uint16_t Pixel_Len); //显示蓝灯
void RGB_WHITE(uint16_t Pixel_Len);//显示白灯
void led_loop(void);
#endif
ws2812b.c文件如下:
#include "ws2812b.h"
#include "tim.h"
/*Some Static Colors------------------------------*/
const RGB_Color_TypeDef RED = {255,0,0}; //显示红色RGB数据
const RGB_Color_TypeDef ORANGE = {127,106,0};
const RGB_Color_TypeDef YELLOW = {127,216,0};
const RGB_Color_TypeDef GREEN = {0,255,0};
const RGB_Color_TypeDef CYAN = {0,255,255};
const RGB_Color_TypeDef BLUE = {0,0,255};
const RGB_Color_TypeDef PURPLE = {238,130,238};
const RGB_Color_TypeDef BLACK = {0,0,0};
const RGB_Color_TypeDef WHITE = {255,255,255};
const RGB_Color_TypeDef MAGENTA = {255,0,220};
/*二维数组存放最终PWM输出数组,每一行24个
数据代表一个LED,最后一行24个0代表RESET码*/
uint32_t Pixel_Buf[Pixel_NUM+1][24];
/*
功能:设定单个RGB LED的颜色,把结构体中RGB的24BIT转换为0码和1码
参数:LedId为LED序号,Color:定义的颜色结构体
*/
void RGB_SetColor(uint8_t LedId,RGB_Color_TypeDef Color)
{
uint8_t i;
if(LedId > Pixel_NUM)return; //avoid overflow 防止写入ID大于LED总数
for(i=0;i<8;i++) Pixel_Buf[LedId][i] = ( (Color.G & (1 << (7 -i)))? (CODE_1):CODE_0 );//数组某一行0~7转化存放G
for(i=8;i<16;i++) Pixel_Buf[LedId][i] = ( (Color.R & (1 << (15-i)))? (CODE_1):CODE_0 );//数组某一行8~15转化存放R
for(i=16;i<24;i++) Pixel_Buf[LedId][i] = ( (Color.B & (1 << (23-i)))? (CODE_1):CODE_0 );//数组某一行16~23转化存放B
}
/*
功能:最后一行装在24个0,输出24个周期占空比为0的PWM波,作为最后reset延时,这里总时长为24*1.2=30us > 24us(要求大于24us)
*/
void Reset_Load(void)
{
uint8_t i;
for(i=0;i<24;i++)
{
Pixel_Buf[Pixel_NUM][i] = 0;
}
}
/*
功能:发送数组
参数:(&htim1)定时器1,(TIM_CHANNEL_2)通道2,((uint32_t *)Pixel_Buf)待发送数组,
(Pixel_NUM+1)*24)发送个数,数组行列相乘
*/
void RGB_SendArray(void)
{
HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_2, (uint32_t *)Pixel_Buf,(Pixel_NUM+1)*24);
}
/*
功能:显示红色
参数:Pixel_Len为显示LED个数
*/
void RGB_RED(uint16_t Pixel_Len)
{
uint16_t i;
for(i=0;i<Pixel_Len;i++)//给对应个数LED写入红色
{
RGB_SetColor(i,RED);
}
Reset_Load();
RGB_SendArray();
}
/*
功能:显示绿色
参数:Pixel_Len为显示LED个数
*/
void RGB_GREEN(uint16_t Pixel_Len)
{
uint16_t i;
for(i=0;i<Pixel_Len;i++)//给对应个数LED写入绿色
{
RGB_SetColor(i,GREEN);
}
Reset_Load();
RGB_SendArray();
}
/*
功能:显示蓝色
参数:Pixel_Len为显示LED个数
*/
void RGB_BLUE(uint16_t Pixel_Len)
{
uint16_t i;
for(i=0;i<Pixel_Len;i++)//给对应个数LED写入蓝色
{
RGB_SetColor(i,BLUE);
}
Reset_Load();
RGB_SendArray();
}
/*
功能:显示白色
参数:Pixel_Len为显示LED个数
*/
void RGB_WHITE(uint16_t Pixel_Len)
{
uint16_t i;
for(i=0;i<Pixel_Len;i++)//给对应个数LED写入白色
{
RGB_SetColor(i,WHITE);
}
Reset_Load();
RGB_SendArray();
}
//也可以继续添加其他颜色,和颜色变化函数等
/*******************************************************************************/
/* 添加部分 */
//显示指定颜色
static void rgb_show(uint32_t Pixel_Len, RGB_Color_TypeDef rgb)
{
uint16_t i;
for(i=0;i<Pixel_Len;i++)
{
RGB_SetColor(i,rgb);
}
Reset_Load();
RGB_SendArray();
}
//颜色循环转换
static RGB_Color_TypeDef Wheel(uint8_t WheelPos)
{
RGB_Color_TypeDef rgb;
WheelPos = 255 - WheelPos;
if (WheelPos < 85)
{
rgb.R = 255 - WheelPos * 3;
rgb.G = 0;
rgb.B = WheelPos * 3;
return rgb;
}
if (WheelPos < 170)
{
WheelPos -= 85;
rgb.R = 0;
rgb.G = WheelPos * 3;
rgb.B = 255 - WheelPos * 3;
return rgb;
}
WheelPos -= 170;
rgb.R = WheelPos * 3;
rgb.G = 255 - WheelPos * 3;
rgb.B = 0;
return rgb;
}
//彩虹呼吸灯
static void rainbow(uint8_t wait)
{
uint32_t timestamp = HAL_GetTick();
uint16_t i;
static uint8_t j;
static uint32_t next_time = 0;
uint32_t flag = 0;
if (next_time < wait)
{
if ((uint64_t)timestamp + wait - next_time > 0)
flag = 1;
}
else if (timestamp > next_time)
{
flag = 1;
}
if (flag) // && (timestamp - next_time < wait*5))
{
j++;
next_time = timestamp + wait;
for (i = 0; i < Pixel_NUM; i++)
{
RGB_SetColor(i, Wheel((i + j) & 255));
}
}
RGB_SendArray();
}
//彩虹灯旋转
static void rainbowCycle(uint8_t wait)
{
uint32_t timestamp = HAL_GetTick();
uint16_t i;
static uint8_t j;
static uint32_t next_time = 0;
static uint8_t loop = 0;
if (loop == 0)
next_time = timestamp;
loop = 1; //首次调用初始化
if ((timestamp > next_time)) // && (timestamp - next_time < wait*5))
{
j++;
next_time = timestamp + wait;
for (i = 0; i < Pixel_NUM; i++)
{
RGB_SetColor(i, Wheel(((i * 256 / (Pixel_NUM)) + j) & 255));
}
}
RGB_SendArray();
}
static uint8_t rainbow_change_flag = 0;
void led_loop(void)
{
int i;
rgb_show(8, BLACK); HAL_Delay(300);
for(i = 1; i <= 8 ;i++) { //红
rgb_show(i, RED);
HAL_Delay(50);
}
for(i = 1; i <= 8 ;i++) { //橙
rgb_show(i, ORANGE);
HAL_Delay(50);
}
for(i = 1; i <= 8 ;i++) { //黄
rgb_show(i, YELLOW);
HAL_Delay(50);
}
for(i = 1; i <= 8 ;i++) { //绿
rgb_show(i, GREEN);
HAL_Delay(50);
}
for(i = 1; i <= 8 ;i++) { //青
rgb_show(i, CYAN);
HAL_Delay(50);
}
for(i = 1; i <= 8 ;i++) { //蓝
rgb_show(i, BLUE);
HAL_Delay(50);
}
for(i = 1; i <= 8 ;i++) { //紫
rgb_show(i, PURPLE);
HAL_Delay(50);
}
HAL_TIM_Base_Start_IT(&htim3); //使能定时器中断->时间:1ms
while(1) {
if(!rainbow_change_flag)
rainbow(5);
else
rainbowCycle(2);
}
}
//定时器3中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(htim);
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_TIM_PeriodElapsedCallback could be implemented in the user file
*/
static uint32_t time_counter = 0;
if(htim->Instance == TIM3) {
if(++time_counter == 10000) { //10s后切换
rainbow_change_flag = ~rainbow_change_flag;
time_counter = 0;
}
}
}
工程链接:https://pan.baidu.com/s/1QXB_ALAOFp-71YSm94Po-g
提取码:0xFF文章来源:https://www.toymoban.com/news/detail-462253.html
The END文章来源地址https://www.toymoban.com/news/detail-462253.html
到了这里,关于STM32 HAL库 PWM+DMA 驱动WS2812B彩灯(STM32F030F4P6)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!