STM32CubeMx驅(qū)動(dòng)WS2812B實(shí)現(xiàn)幻彩(超詳)
1.創(chuàng)建基于STM32F03C8T6工程
1.1配置時(shí)鐘
- 選擇外部高速時(shí)鐘源HSE
在這里插入圖片描述
1.2配置系統(tǒng)時(shí)鐘樹(shù)使其達(dá)到最大時(shí)鐘72MHz(最大系統(tǒng)時(shí)鐘)
在這里插入圖片描述
由時(shí)鐘樹(shù)可以知道APB1上定時(shí)器時(shí)鐘頻率是72MHz,實(shí)驗(yàn)使用的硬件接的是PA2,用的定時(shí)器TIM2_CH3, 查閱數(shù)據(jù)手冊(cè)可知
TIM2外設(shè)在APB1上所以TIM2的定時(shí)器頻率是72MHz。
在這里插入圖片描述
在這里插入圖片描述
3.理論分析
之前寫(xiě)過(guò)一篇文章,文章鏈接在此:創(chuàng)客實(shí)驗(yàn)第一彈之驅(qū)動(dòng)WS2812B彩燈;不了解的可以直接跳轉(zhuǎn),今天主要講解如何使用STM32CubeMx驅(qū)動(dòng)WS2812B實(shí)現(xiàn)幻彩,這里做部分內(nèi)容截取,完整可以跳轉(zhuǎn)文章閱讀。
3.1WS2812B的邏輯“1”和邏輯“0”
圖片
由上圖可知“0”碼和“1”碼都是既有高電平又有低電平不過(guò)高電平和低電平的比例不同,這點(diǎn)很好理解,重點(diǎn)是分析一下它的特點(diǎn),首先直觀的特點(diǎn)就是編碼“0”的電平高電平時(shí)間短一些低電平時(shí)間長(zhǎng)一些,這也恰好符合我們的邏輯畢竟它還是低電平多一些的,編碼“1”的電平高電平時(shí)間就長(zhǎng)一些,而低電平就短一些。
但是不管是高電平還是低電平他們占用整個(gè)時(shí)間長(zhǎng)度是一樣的,這里還有一個(gè)很長(zhǎng)的低電平這個(gè)代表復(fù)位信號(hào)。
3.2WS2812B控制波形的精準(zhǔn)描述如何?
這里涉及到嚴(yán)格的數(shù)學(xué)描述了,長(zhǎng)一點(diǎn)是多長(zhǎng)?短一點(diǎn)是多短?這個(gè)肯定是有標(biāo)準(zhǔn)或者是約束的
圖片
理論上來(lái)說(shuō),高電平時(shí)長(zhǎng)和低電平時(shí)長(zhǎng)加起來(lái)應(yīng)該是0.4us+0.85us或者0.85us+0.4us也就是說(shuō)總共要占用1.25us的時(shí)間才可以編碼出來(lái)一個(gè)“0”或者“1”出來(lái)。復(fù)位是要求50us以上,顯然是要比編碼的0或者1占用的時(shí)間要多的。
當(dāng)然既然是電路的高低電平時(shí)長(zhǎng)就會(huì)引入誤差這個(gè)在誤差允許的范圍內(nèi)我們可以接受,這個(gè)范圍就是上下不超過(guò)150ns這里是ns比us還要小的時(shí)間,這個(gè)其實(shí)時(shí)間要求還是很?chē)?yán)格的。
3.3分析結(jié)果
定時(shí)器主頻是72MHz 要想實(shí)現(xiàn)1.25us 周期的波形也就是頻率是800KHz波形我們可以采用分頻為1分頻,計(jì)數(shù)值為90來(lái)實(shí)現(xiàn)具體原因如下
- PWM頻率計(jì)算公式:
Fpwm =Tclk / ((arr+1)*(psc+1))(單位:Hz)
上面提到數(shù)據(jù)傳送頻率為800KHz,TCLK為72Mhz,我們這里設(shè)置psc = 0,arr= 89,得到頻率剛好為800KHz,也就是計(jì)數(shù)器計(jì)數(shù)90個(gè)數(shù),每個(gè)數(shù)計(jì)數(shù)時(shí)間是1/72us,1/72us*90 = 1.25us;
通過(guò)控制PWM占空比發(fā)送0碼和1碼,額定周期為1.25us,則頻率為800Khz
- 0碼占空比計(jì)算
(0碼高電平時(shí)間)/(周期)---> 0.4us / 1.25 us = 0.32
用占空比乘以定時(shí)器重裝值加一就是0碼的CCR值(代表PWM高電平計(jì)數(shù)個(gè)數(shù))--->
0.32 * (90) = 28.8(取28,網(wǎng)上網(wǎng)友實(shí)測(cè)不可以高于28,但23到28都可以,我這里選28)
- 1碼占空比計(jì)算
同理計(jì)算:(1碼高電平時(shí)間)/ (周期)---> 0.8 / 1.25 = 0.64
(占空比)*(90)= CCR ---> 0.64 * 90 = 57.6(取57左右即可這里取58)
4.配置定時(shí)器的參數(shù)
在這里插入圖片描述
5.WS2812B燈帶的數(shù)據(jù)是什么樣的呢?
圖片
在說(shuō)數(shù)據(jù)格式之前先來(lái)補(bǔ)充一下關(guān)于色彩的知識(shí)點(diǎn),就是三原色,紅綠藍(lán),也就是我們常說(shuō)的RGB,R就是RED,G就是GREEN,B就是BLUE,一個(gè)彩色可以用這三種顏色的比例來(lái)混合出來(lái)。
每一個(gè)LED的R、G、B分別由八位數(shù)據(jù)控制顏色濃度,(每種顏色濃度有0~255檔,理論上RGB就可以組成256的3次方中顏色組合)即每個(gè)LED需要24BIT數(shù)據(jù),那么需要發(fā)送數(shù)據(jù)的總長(zhǎng)度則為(要控制LED數(shù)量 n)*(24),每個(gè)LED保存24BIT將剩余位傳給后面LED。全部數(shù)據(jù)發(fā)送完成后要繼續(xù)發(fā)送大于24us的低電平作為RESET_CODE等才可以點(diǎn)亮。
既然是24bit數(shù)據(jù)代表三種顏色,我們就要首先知道一個(gè)bit的意義是什么,我們傳統(tǒng)意義上來(lái)說(shuō)1個(gè)bit代表一個(gè)數(shù)據(jù)位,但是對(duì)于數(shù)據(jù)位bit的理解好像就是“1”或者“0”在數(shù)電里我們很容易把高低電平跟邏輯1和邏輯0對(duì)應(yīng)起來(lái),但是表示燈珠的邏輯電平不是簡(jiǎn)單的高低電平。在數(shù)值上0xFFFFFF就是24bit的1,0x000000就是24bit的0.這里有8個(gè)bit代表顏色G分量,G7G6G5G4G3G2G1G0,有8個(gè)bit代表R分量R7R6R5R4R3R2R1R0,有8個(gè)bit代表B分量B7B6B5B4B3B2B1B0,當(dāng)不同分量組合時(shí)候就會(huì)有不同的數(shù)據(jù)產(chǎn)生,這個(gè)數(shù)據(jù)背后其實(shí)是邏輯電平,這里要說(shuō)明的是彩燈的邏輯“1”并不是簡(jiǎn)簡(jiǎn)單單的高電平,彩燈的邏輯“0”也不是簡(jiǎn)簡(jiǎn)單單的低電平。而是上面分析的占空比不同的PWM波形。
4.配置定時(shí)器的DMA參數(shù)
這里解釋下為什么要就一顆燈也要用DMA,這里僅僅是拿一個(gè)燈做,其實(shí)多個(gè)燈串起來(lái)數(shù)據(jù)量還是很大的使用DMA才可以充分釋放其性能優(yōu)勢(shì)。
- 內(nèi)存向外設(shè)傳輸
- 內(nèi)存遞增遞增
- DMA普通模式
- 32為字長(zhǎng)傳輸
在這里插入圖片描述
4.生產(chǎn)代碼修改完善
生成工程配置可參考上篇博客
4.1添加自定義ws2812b.c文件跟ws2812b.h文件
在這里插入圖片描述
在這里插入圖片描述
4.2修改自定義ws2812b.c文件跟ws2812b.h文件
- ws2812.c文件內(nèi)容
#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個(gè)
數(shù)據(jù)代表一個(gè)LED,最后一行24個(gè)0代表RESET碼*/
uint32_t Pixel_Buf[LED_NUM+1][24];
/*
功能:設(shè)定單個(gè)RGB LED的顏色,把結(jié)構(gòu)體中RGB的24BIT轉(zhuǎn)換為0碼和1碼
參數(shù):LedId為L(zhǎng)ED序號(hào),Color:定義的顏色結(jié)構(gòu)體
*/
void RGB_SetColor(uint8_t LedId,RGB_Color_TypeDef Color)
{
uint8_t i;
if(LedId > LED_NUM)return; //avoid overflow 防止寫(xiě)入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個(gè)0,輸出24個(gè)周期占空比為0的PWM波,作為最后reset延時(shí),這里總時(shí)長(zhǎng)為24*1.2=30us > 24us(要求大于24us)
*/
void Reset_Load(void)
{
uint8_t i;
for(i=0;i< 24;i++)
{
Pixel_Buf[LED_NUM][i] = 0;
}
}
/*
功能:發(fā)送數(shù)組
參數(shù):(&htim1)定時(shí)器1,(TIM_CHANNEL_2)通道2,((uint32_t *)Pixel_Buf)待發(fā)送數(shù)組,
(Pixel_NUM+1)*24)發(fā)送個(gè)數(shù),數(shù)組行列相乘
*/
void RGB_SendArray(void)
{
HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_3, (uint32_t *)Pixel_Buf,(LED_NUM+1)*24);
}
/*
功能:顯示紅色
參數(shù):Pixel_Len為顯示LED個(gè)數(shù)
*/
void RGB_RED(uint16_t Pixel_Len)
{
uint16_t i;
for(i=0;i< Pixel_Len;i++)//給對(duì)應(yīng)個(gè)數(shù)LED寫(xiě)入紅色
{
RGB_SetColor(i,RED);
}
Reset_Load();
RGB_SendArray();
}
/*
功能:顯示綠色
參數(shù):Pixel_Len為顯示LED個(gè)數(shù)
*/
void RGB_GREEN(uint16_t Pixel_Len)
{
uint16_t i;
for(i=0;i< Pixel_Len;i++)//給對(duì)應(yīng)個(gè)數(shù)LED寫(xiě)入綠色
{
RGB_SetColor(i,GREEN);
}
Reset_Load();
RGB_SendArray();
}
/*
功能:顯示藍(lán)色
參數(shù):Pixel_Len為顯示LED個(gè)數(shù)
*/
void RGB_BLUE(uint16_t Pixel_Len)
{
uint16_t i;
for(i=0;i< Pixel_Len;i++)//給對(duì)應(yīng)個(gè)數(shù)LED寫(xiě)入藍(lán)色
{
RGB_SetColor(i,BLUE);
}
Reset_Load();
RGB_SendArray();
}
/*
功能:顯示白色
參數(shù):Pixel_Len為顯示LED個(gè)數(shù)
*/
void RGB_WHITE(uint16_t Pixel_Len)
{
uint16_t i;
for(i=0;i< Pixel_Len;i++)//給對(duì)應(yīng)個(gè)數(shù)LED寫(xiě)入白色
{
RGB_SetColor(i,WHITE);
}
Reset_Load();
RGB_SendArray();
}
//用戶(hù)自定義API接口可根據(jù)實(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;
staticuint8_t j;
staticuint32_t next_time = 0;
uint32_t flag = 0;
if (next_time < wait)
{
if ((uint64_t)timestamp + wait - next_time > 0)
flag = 1;
}
elseif (timestamp > next_time)
{
flag = 1;
}
if (flag) // && (timestamp - next_time < wait*5))
{
j++;
next_time = timestamp + wait;
for (i = 0; i < LED_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;
staticuint8_t j;
staticuint32_t next_time = 0;
staticuint8_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 < LED_NUM; i++)
{
RGB_SetColor(i, Wheel(((i * 256 / (LED_NUM)) + j) & 255));
}
}
RGB_SendArray();
}
- ws2812.h文件內(nèi)容
#ifndef _WS2812B_H_
#define _WS2812B_H_
#include "main.h"
/*這里是上文計(jì)算所得CCR的宏定義*/
#define CODE_1 (58) //1碼定時(shí)器計(jì)數(shù)次數(shù)
#define CODE_0 (28) //0碼定時(shí)器計(jì)數(shù)次數(shù)
/*建立一個(gè)定義單個(gè)LED三原色值大小的結(jié)構(gòu)體*/
typedefstruct
{
uint8_t R;
uint8_t G;
uint8_t B;
}RGB_Color_TypeDef;
#define LED_NUM 1 //LED數(shù)量宏定義,這里我使用一個(gè)LED
void RGB_SetColor(uint8_t LedId,RGB_Color_TypeDef Color);//給一個(gè)LED裝載24個(gè)顏色數(shù)據(jù)碼(0碼和1碼)
void Reset_Load(void); //該函數(shù)用于將數(shù)組最后24個(gè)數(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); //顯示藍(lán)燈
void RGB_WHITE(uint16_t Pixel_Len);//顯示白燈
#endif
4.3修改主函數(shù)實(shí)現(xiàn)測(cè)試功能
/* USER CODE BEGIN Includes */
#include "ws2812b.h"
/* USER CODE END Includes */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
RGB_RED(1);
HAL_Delay(1000);
RGB_GREEN(1);
HAL_Delay(1000);
RGB_BLUE(1);
HAL_Delay(1000);
RGB_WHITE(1);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
5.下載驗(yàn)證
顏色好像有點(diǎn)色差,基本沒(méi)啥問(wèn)題。OK 到這如何使用STM32CubeMx驅(qū)動(dòng)WS2812B實(shí)現(xiàn)幻彩基本搞定,后面有機(jī)會(huì)再更新幻彩燈的實(shí)現(xiàn)部分。
-
PWM
+關(guān)注
關(guān)注
116文章
5364瀏覽量
217910 -
定時(shí)器
+關(guān)注
關(guān)注
23文章
3287瀏覽量
117121 -
時(shí)鐘樹(shù)
+關(guān)注
關(guān)注
0文章
56瀏覽量
10960 -
stm32cubemx
+關(guān)注
關(guān)注
5文章
286瀏覽量
15965 -
WS2812B
+關(guān)注
關(guān)注
2文章
39瀏覽量
2517
發(fā)布評(píng)論請(qǐng)先 登錄
什么是WS2812B?WS2812B燈帶是怎么連接的呢?

哪位大神有完整的STM32F205驅(qū)動(dòng)WS2812B燈帶的程序
使用ESP8266驅(qū)動(dòng)WS2812B型RGB LED智能燈制作分享
WS2812B RGB全彩LED燈珠
怎樣給WS2812B發(fā)送數(shù)據(jù)呢
怎樣使用PWM+DMA的方式去驅(qū)動(dòng)WS2812B呢
WS2812B RGB 彩燈 51單片機(jī) STC15W 驅(qū)動(dòng)代碼
基于GD32F427開(kāi)發(fā)板點(diǎn)亮WS2812B炫彩燈環(huán)的設(shè)計(jì)實(shí)現(xiàn)
使用ESP8266驅(qū)動(dòng)WS2812B型RGB LED簡(jiǎn)單3步打造智能燈
【有趣的全彩LED | 編程】用STM32 HAL庫(kù)讓WS2812B為你所動(dòng)

STM32F1 DMA+PWM 控制WS2812B LED燈

WS2812B遠(yuǎn)程區(qū)域面板控制器

ESP32 WS2812b分線板開(kāi)源分享

評(píng)論