STM32CubeMx驅動WS2812B實現幻彩(超詳)
1.創建基于STM32F03C8T6工程
1.1配置時鐘
- 選擇外部高速時鐘源HSE
在這里插入圖片描述
1.2配置系統時鐘樹使其達到最大時鐘72MHz(最大系統時鐘)
在這里插入圖片描述
由時鐘樹可以知道APB1上定時器時鐘頻率是72MHz,實驗使用的硬件接的是PA2,用的定時器TIM2_CH3, 查閱數據手冊可知
TIM2外設在APB1上所以TIM2的定時器頻率是72MHz。
在這里插入圖片描述
在這里插入圖片描述
3.理論分析
之前寫過一篇文章,文章鏈接在此:創客實驗第一彈之驅動WS2812B彩燈;不了解的可以直接跳轉,今天主要講解如何使用STM32CubeMx驅動WS2812B實現幻彩,這里做部分內容截取,完整可以跳轉文章閱讀。
3.1WS2812B的邏輯“1”和邏輯“0”
圖片
由上圖可知“0”碼和“1”碼都是既有高電平又有低電平不過高電平和低電平的比例不同,這點很好理解,重點是分析一下它的特點,首先直觀的特點就是編碼“0”的電平高電平時間短一些低電平時間長一些,這也恰好符合我們的邏輯畢竟它還是低電平多一些的,編碼“1”的電平高電平時間就長一些,而低電平就短一些。
但是不管是高電平還是低電平他們占用整個時間長度是一樣的,這里還有一個很長的低電平這個代表復位信號。
3.2WS2812B控制波形的精準描述如何?
這里涉及到嚴格的數學描述了,長一點是多長?短一點是多短?這個肯定是有標準或者是約束的
圖片
理論上來說,高電平時長和低電平時長加起來應該是0.4us+0.85us或者0.85us+0.4us也就是說總共要占用1.25us的時間才可以編碼出來一個“0”或者“1”出來。復位是要求50us以上,顯然是要比編碼的0或者1占用的時間要多的。
當然既然是電路的高低電平時長就會引入誤差這個在誤差允許的范圍內我們可以接受,這個范圍就是上下不超過150ns這里是ns比us還要小的時間,這個其實時間要求還是很嚴格的。
3.3分析結果
定時器主頻是72MHz 要想實現1.25us 周期的波形也就是頻率是800KHz波形我們可以采用分頻為1分頻,計數值為90來實現具體原因如下
- PWM頻率計算公式:
Fpwm =Tclk / ((arr+1)*(psc+1))(單位:Hz)
上面提到數據傳送頻率為800KHz,TCLK為72Mhz,我們這里設置psc = 0,arr= 89,得到頻率剛好為800KHz,也就是計數器計數90個數,每個數計數時間是1/72us,1/72us*90 = 1.25us;
通過控制PWM占空比發送0碼和1碼,額定周期為1.25us,則頻率為800Khz
- 0碼占空比計算
(0碼高電平時間)/(周期)---> 0.4us / 1.25 us = 0.32
用占空比乘以定時器重裝值加一就是0碼的CCR值(代表PWM高電平計數個數)--->
0.32 * (90) = 28.8(取28,網上網友實測不可以高于28,但23到28都可以,我這里選28)
- 1碼占空比計算
同理計算:(1碼高電平時間)/ (周期)---> 0.8 / 1.25 = 0.64
(占空比)*(90)= CCR ---> 0.64 * 90 = 57.6(取57左右即可這里取58)
4.配置定時器的參數
在這里插入圖片描述
5.WS2812B燈帶的數據是什么樣的呢?
圖片
在說數據格式之前先來補充一下關于色彩的知識點,就是三原色,紅綠藍,也就是我們常說的RGB,R就是RED,G就是GREEN,B就是BLUE,一個彩色可以用這三種顏色的比例來混合出來。
每一個LED的R、G、B分別由八位數據控制顏色濃度,(每種顏色濃度有0~255檔,理論上RGB就可以組成256的3次方中顏色組合)即每個LED需要24BIT數據,那么需要發送數據的總長度則為(要控制LED數量 n)*(24),每個LED保存24BIT將剩余位傳給后面LED。全部數據發送完成后要繼續發送大于24us的低電平作為RESET_CODE等才可以點亮。
既然是24bit數據代表三種顏色,我們就要首先知道一個bit的意義是什么,我們傳統意義上來說1個bit代表一個數據位,但是對于數據位bit的理解好像就是“1”或者“0”在數電里我們很容易把高低電平跟邏輯1和邏輯0對應起來,但是表示燈珠的邏輯電平不是簡單的高低電平。在數值上0xFFFFFF就是24bit的1,0x000000就是24bit的0.這里有8個bit代表顏色G分量,G7G6G5G4G3G2G1G0,有8個bit代表R分量R7R6R5R4R3R2R1R0,有8個bit代表B分量B7B6B5B4B3B2B1B0,當不同分量組合時候就會有不同的數據產生,這個數據背后其實是邏輯電平,這里要說明的是彩燈的邏輯“1”并不是簡簡單單的高電平,彩燈的邏輯“0”也不是簡簡單單的低電平。而是上面分析的占空比不同的PWM波形。
4.配置定時器的DMA參數
這里解釋下為什么要就一顆燈也要用DMA,這里僅僅是拿一個燈做,其實多個燈串起來數據量還是很大的使用DMA才可以充分釋放其性能優勢。
- 內存向外設傳輸
- 內存遞增遞增
- DMA普通模式
- 32為字長傳輸
在這里插入圖片描述
4.生產代碼修改完善
生成工程配置可參考上篇博客
4.1添加自定義ws2812b.c文件跟ws2812b.h文件
在這里插入圖片描述
在這里插入圖片描述
4.2修改自定義ws2812b.c文件跟ws2812b.h文件
- ws2812.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[LED_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 > LED_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[LED_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(&htim2, TIM_CHANNEL_3, (uint32_t *)Pixel_Buf,(LED_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();
}
//用戶自定義API接口可根據實際拓展
/*******************************************************************************/
/* 添加部分 */
//顯示指定顏色
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;
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();
}
//彩虹燈旋轉
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; //首次調用初始化
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文件內容
#ifndef _WS2812B_H_
#define _WS2812B_H_
#include "main.h"
/*這里是上文計算所得CCR的宏定義*/
#define CODE_1 (58) //1碼定時器計數次數
#define CODE_0 (28) //0碼定時器計數次數
/*建立一個定義單個LED三原色值大小的結構體*/
typedefstruct
{
uint8_t R;
uint8_t G;
uint8_t B;
}RGB_Color_TypeDef;
#define LED_NUM 1 //LED數量宏定義,這里我使用一個LED
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);//顯示白燈
#endif
4.3修改主函數實現測試功能
/* 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.下載驗證
顏色好像有點色差,基本沒啥問題。OK 到這如何使用STM32CubeMx驅動WS2812B實現幻彩基本搞定,后面有機會再更新幻彩燈的實現部分。
-
PWM
+關注
關注
114文章
5186瀏覽量
213937 -
定時器
+關注
關注
23文章
3248瀏覽量
114800 -
時鐘樹
+關注
關注
0文章
54瀏覽量
10750 -
stm32cubemx
+關注
關注
5文章
283瀏覽量
14809 -
WS2812B
+關注
關注
1文章
38瀏覽量
2182
發布評論請先 登錄
相關推薦
評論