宣武網(wǎng)站建設(shè)低價刷贊網(wǎng)站推廣
一、項目簡介
????????采用STM32f103C8t6單片機(jī),使用HAL庫編寫。項目中針對初學(xué)者驅(qū)動WS2812時會遇到的一些問題,給出了解決方案。
二、ws2812驅(qū)動原理
? ? ? ? WS2812采用單線歸零碼的通訊方式,即利用高低電平的持續(xù)時間來確定0和1。這種通信方式優(yōu)點是只需要一根通信線,缺點是對通信的時序要求較高。
? ? ? ? 以官方數(shù)據(jù)手冊的時序圖為例,通信速率為800kbit/s,也就是PWM波的速率為800Kbit/s,每個PWM的周期為1.25微妙。PWM的一個周期即為一個數(shù)據(jù)幀,每個數(shù)據(jù)幀由一段高電平和一段低電平組成。下圖為官方規(guī)定的數(shù)據(jù)傳輸時間:
T0H | 0碼,高電平時間 | 0.22 us~0.35 us |
T0L | 0碼,低電平時間 | 0.58 us~1.0 us |
T1H | 1碼,高電平時間 | 0.58 us~1.0 us |
T1L | 1碼,低電平時間 | 0.22 us~0.42 us |
RES | 幀間隔,低電平時間 | 50us以上 |
? ? ? ? 也就是說:一個1碼,由2/3左右的高電平 和 1/3左右的低電平組成。
? ? ? ? ? ? ? ? ? ? ? ? ? ?一個0碼,由1/3左右的高電平 和 2/3左右的低電平組成。
? ? ? ? 若定時器的時鐘頻率為72MHz,那么預(yù)分頻值設(shè)置為0,比較值設(shè)置為89,這樣產(chǎn)生的PWM波的頻率就為800KHz,周期為1.25us。要發(fā)送1碼時,設(shè)置占空比為60。要發(fā)送0碼時,設(shè)置占空比為29。
????????每個WS2812需要用24bit的數(shù)據(jù)來控制,當(dāng)n個ws2812進(jìn)行級聯(lián)的時候,第一個燈會將第一個24bit的數(shù)據(jù)攔截,將后面的數(shù)據(jù)進(jìn)行轉(zhuǎn)發(fā)。第二個燈又會攔截第二個24bit的數(shù)據(jù),將后面的數(shù)據(jù)進(jìn)行轉(zhuǎn)發(fā)。后面的邏輯也是一樣,數(shù)據(jù)每經(jīng)過一個燈,數(shù)據(jù)的前24bit就會被攔截下來,作為這個燈的顯示內(nèi)容。數(shù)據(jù)傳輸方法如下圖所示:
? ? ? ? 代碼編寫邏輯:初始化的時候,需生成一個顯存數(shù)組,由DMA將數(shù)組中的內(nèi)容實時搬運到定時器的比較寄存器中,DMA要開啟循環(huán)模式。之后我們只需要更新顯存數(shù)組中的數(shù)據(jù),WS2812的顯示內(nèi)容就會被實時更新。
三、Cube MX 生成底層代碼
1、配置Debug的模式
2、配置外部晶振
3、配置時鐘
4、配置定時器
5、配置定時器的DMA
6、生成代碼
四、代碼編寫
1、下面ws2812.c的代碼
#include "ws2812.h"//顯存數(shù)組,長度為 燈的數(shù)量*24+復(fù)位周期
uint16_t WS2812_RGB_Buff[LED_NUM*DATA_LEN+WS2812_RST_NUM] = {0}; /*** 函數(shù):WS2812單燈設(shè)置函數(shù)* 參數(shù):num:燈的位置,R、G、B分別為三個顏色通道的亮度,最大值為255* 作用:單獨設(shè)置每一個WS2812的顏色
***/
void WS2812_Set(uint16_t num,uint8_t R,uint8_t G,uint8_t B)
{uint32_t indexx=(num*(3*8));for (uint8_t i = 0;i < 8;i++){//填充數(shù)組WS2812_RGB_Buff[indexx+i] = (G << i) & (0x80)?WS_H:WS_L;WS2812_RGB_Buff[indexx+i + 8] = (R << i) & (0x80)?WS_H:WS_L;WS2812_RGB_Buff[indexx+i + 16] = (B << i) & (0x80)?WS_H:WS_L;}
}//WS2812初始化函數(shù)
void WS2812_Init()
{//設(shè)置關(guān)閉所有燈for(int i=0;i<8;i++){WS2812_Set(i,0,20,0);}//作用:調(diào)用DMA將顯存中的內(nèi)容實時搬運至定時器的比較寄存器HAL_TIM_PWM_Start_DMA(&htim2,TIM_CHANNEL_1,(uint32_t *)WS2812_RGB_Buff,sizeof(WS2812_RGB_Buff)/sizeof(uint16_t));
}
?2、下面為ws2812.h的代碼
#include "main.h"
#include "tim.h"#define WS_H 60 // 1 碼相對計數(shù)值
#define WS_L 29 // 0 碼相對計數(shù)值
#define WS_REST 40 // 復(fù)位信號脈沖數(shù)量
#define LED_NUM 8 // WS2812燈個數(shù)
#define DATA_LEN 24 // WS2812數(shù)據(jù)長度,單個需要24個字節(jié)
#define WS2812_RST_NUM 50 // 官方復(fù)位時間為50us(40個周期),保險起見使用50個周期void WS2812_Init(void);
void WS2812_Set(uint16_t num,uint8_t R,uint8_t G,uint8_t B);
3.下面為main.c中調(diào)用的代碼,效果為流水燈
?
/*** @brief The application entry point.* @retval int*/
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_TIM2_Init();/* USER CODE BEGIN 2 */WS2812_Init();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){//效果一,流水燈for(int i=0;i<8;i++){HAL_Delay(100);WS2812_Set(i,2*(i+1),4*(i+1),10*(i+1));}HAL_Delay(300);for(int i=0;i<8;i++){WS2812_Set(i,0,0,0);}HAL_Delay(100);//效果二,跑馬燈
// for(int i=0;i<8;i++)
// {
// HAL_Delay(100);
// WS2812_Set(i,0,20,0);
// if(i==0) WS2812_Set(7,0,0,0);
// else WS2812_Set(i-1,0,0,0);
// }/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
五、燒錄效果
六、注意事項
1、DMA的搬運方向為 內(nèi)存(Memory) 到 外設(shè)(Peripheral)
2、DMA的模式為循環(huán)模式
3、DMA設(shè)置內(nèi)存地址自增