誰不想擁有炫酷的小彩燈呢?WS2812B可以給你機會......
博主使用STM32驅(qū)動WS2812B主要參考了這位佬的文章,因為需求問題,采用了Cortex-M0的stm32f030f4p6(16k的flash,4k的sram)來驅(qū)動,原文中寫的是stm32f103c8t6,個人認為其實區(qū)別并不是很大,需要修改部分參數(shù)即可移植(cv戰(zhàn)士申請出戰(zhàn))。
上圖是我的一圈燈,一共8個,第一個LED的數(shù)據(jù)輸入端接的是定時器1的通道2,想看底層原理可以去看佬的文章,本文只介紹如何移植。
CUBEMX配置
首先選擇外部石英晶振,我用的是外部12M晶振,然后配置時鐘樹:
然后打開串行調(diào)試:
?找到使用的定時器(輸出PWM的),并打開定時器時鐘,選定輸出PWM的通道以及模式:
重點來了:
佬的文章中說的0碼PWM占空比(CCR值)算到約為28,1碼PWM占空比(CCR值)算到約為58;但人家用的是f1啊,72M時鐘頻率的,咱的是f0,自然低人一等,只有48M時鐘頻率。所以在滿足燈帶頻率固定為800KHz的前提下,略微修改即可,設(shè)置參數(shù)如下:
下面來簡單說一下如何修改
因為PWM頻率F需要為800KHz,而時鐘頻率固定為48MHz,根據(jù)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中斷:
?配置好生成路徑后直接生成代碼即可
代碼部分:
主函數(shù)頭文件加個#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(); //循環(huán)顯示
}
/* USER CODE END 3 */
}
?ws2812b.h文件如下:
#ifndef __WS2812B_H__
#define __WS2812B_H__
#include "main.h"
/*這里是上文計算所得CCR的宏定義*/
#define CODE_1 (38) //1碼定時器計數(shù)次數(shù)
#define CODE_0 (19) //0碼定時器計數(shù)次數(shù)
/*建立一個定義單個LED三原色值大小的結(jié)構(gòu)體*/
typedef struct
{
uint8_t R;
uint8_t G;
uint8_t B;
}RGB_Color_TypeDef;
#define Pixel_NUM 8 //LED數(shù)量宏定義,這里我使用一個LED,(單詞pixel為像素的意思)
void RGB_SetColor(uint8_t LedId,RGB_Color_TypeDef Color);//給一個LED裝載24個顏色數(shù)據(jù)碼(0碼和1碼)
void Reset_Load(void); //該函數(shù)用于將數(shù)組最后24個數(shù)據(jù)變?yōu)?,代表RESET_code
void RGB_SendArray(void); //發(fā)送最終數(shù)組
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數(shù)據(jù)
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};
/*二維數(shù)組存放最終PWM輸出數(shù)組,每一行24個
數(shù)據(jù)代表一個LED,最后一行24個0代表RESET碼*/
uint32_t Pixel_Buf[Pixel_NUM+1][24];
/*
功能:設(shè)定單個RGB LED的顏色,把結(jié)構(gòu)體中RGB的24BIT轉(zhuǎn)換為0碼和1碼
參數(shù):LedId為LED序號,Color:定義的顏色結(jié)構(gòu)體
*/
void RGB_SetColor(uint8_t LedId,RGB_Color_TypeDef Color)
{
uint8_t i;
if(LedId > Pixel_NUM)return; //avoid overflow 防止寫入ID大于LED總數(shù)
for(i=0;i<8;i++) Pixel_Buf[LedId][i] = ( (Color.G & (1 << (7 -i)))? (CODE_1):CODE_0 );//數(shù)組某一行0~7轉(zhuǎn)化存放G
for(i=8;i<16;i++) Pixel_Buf[LedId][i] = ( (Color.R & (1 << (15-i)))? (CODE_1):CODE_0 );//數(shù)組某一行8~15轉(zhuǎn)化存放R
for(i=16;i<24;i++) Pixel_Buf[LedId][i] = ( (Color.B & (1 << (23-i)))? (CODE_1):CODE_0 );//數(shù)組某一行16~23轉(zhuǎn)化存放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;
}
}
/*
功能:發(fā)送數(shù)組
參數(shù):(&htim1)定時器1,(TIM_CHANNEL_2)通道2,((uint32_t *)Pixel_Buf)待發(fā)送數(shù)組,
(Pixel_NUM+1)*24)發(fā)送個數(shù),數(shù)組行列相乘
*/
void RGB_SendArray(void)
{
HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_2, (uint32_t *)Pixel_Buf,(Pixel_NUM+1)*24);
}
/*
功能:顯示紅色
參數(shù):Pixel_Len為顯示LED個數(shù)
*/
void RGB_RED(uint16_t Pixel_Len)
{
uint16_t i;
for(i=0;i<Pixel_Len;i++)//給對應(yīng)個數(shù)LED寫入紅色
{
RGB_SetColor(i,RED);
}
Reset_Load();
RGB_SendArray();
}
/*
功能:顯示綠色
參數(shù):Pixel_Len為顯示LED個數(shù)
*/
void RGB_GREEN(uint16_t Pixel_Len)
{
uint16_t i;
for(i=0;i<Pixel_Len;i++)//給對應(yīng)個數(shù)LED寫入綠色
{
RGB_SetColor(i,GREEN);
}
Reset_Load();
RGB_SendArray();
}
/*
功能:顯示藍色
參數(shù):Pixel_Len為顯示LED個數(shù)
*/
void RGB_BLUE(uint16_t Pixel_Len)
{
uint16_t i;
for(i=0;i<Pixel_Len;i++)//給對應(yīng)個數(shù)LED寫入藍色
{
RGB_SetColor(i,BLUE);
}
Reset_Load();
RGB_SendArray();
}
/*
功能:顯示白色
參數(shù):Pixel_Len為顯示LED個數(shù)
*/
void RGB_WHITE(uint16_t Pixel_Len)
{
uint16_t i;
for(i=0;i<Pixel_Len;i++)//給對應(yīng)個數(shù)LED寫入白色
{
RGB_SetColor(i,WHITE);
}
Reset_Load();
RGB_SendArray();
}
//也可以繼續(xù)添加其他顏色,和顏色變化函數(shù)等
/*******************************************************************************/
/* 添加部分 */
//顯示指定顏色
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();
}
//顏色循環(huán)轉(zhuǎn)換
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();
}
//彩虹燈旋轉(zhuǎn)
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; //首次調(diào)用初始化
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中斷回調(diào)函數(shù)
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文章來源:http://www.zghlxwxcb.cn/news/detail-462253.html
The END文章來源地址http://www.zghlxwxcb.cn/news/detail-462253.html
到了這里,關(guān)于STM32 HAL庫 PWM+DMA 驅(qū)動WS2812B彩燈(STM32F030F4P6)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!