日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

stm32【RGB_LED_WS2812灯珠】

發(fā)布時(shí)間:2023/12/15 编程问答 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 stm32【RGB_LED_WS2812灯珠】 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

RGB_LED 內(nèi)置驅(qū)動(dòng)IC:WS2812

測(cè)試平臺(tái):stm32f103c8t6
庫(kù)版本:官方標(biāo)準(zhǔn)庫(kù)3.5.0版本

LED規(guī)格:RGB-5050
內(nèi)置IC:ws2812
驅(qū)動(dòng)方式:歸零碼

相比普通的LED燈珠,內(nèi)置驅(qū)動(dòng)芯片的燈珠在實(shí)用性,整體體積,電路設(shè)計(jì),程序設(shè)計(jì)上都有很大的優(yōu)勢(shì),本文所寫(xiě)內(nèi)置驅(qū)動(dòng)IC的WS2812的燈珠是比較常見(jiàn)的一種彩色LED燈珠,通過(guò)級(jí)聯(lián)方式連接每顆LED,電路設(shè)計(jì)簡(jiǎn)單,采用歸零碼輸入彩色數(shù)據(jù)。
萬(wàn)能的某寶有很多這類內(nèi)置驅(qū)動(dòng)的燈珠,購(gòu)買(mǎi)前記得詢問(wèn)是否由數(shù)據(jù)手冊(cè)或者規(guī)格書(shū)等,這對(duì)電路設(shè)計(jì)和程序設(shè)計(jì)有很大幫助。

注: 文中首次出現(xiàn)的代碼塊會(huì)標(biāo)注[xxx.c]或[xxx.h],表明該代碼是屬于對(duì)應(yīng)的文件,未標(biāo)注的即為重復(fù)出現(xiàn)的

目錄

  • RGB_LED 內(nèi)置驅(qū)動(dòng)IC:WS2812
    • 1、電路設(shè)計(jì)
    • 2、程序設(shè)計(jì)
      • 2.1 驅(qū)動(dòng)方式
      • 2.2 定時(shí)器配置
      • 2.3 數(shù)據(jù)處理與發(fā)送
      • 2.4 定時(shí)器中斷服務(wù)函數(shù)
    • 3、總結(jié)

1、電路設(shè)計(jì)

由于是級(jí)聯(lián)方式連接相鄰的燈珠,因此電路很簡(jiǎn)單,
燈珠管腳▼

規(guī)格書(shū)描述▼

只要將相鄰燈珠的DOUT和DIN連接在一起即可,這里的104電容并不是必須的

電路原理圖▼

LED1~LED10一路
LED11~LED20一路
這里是設(shè)計(jì)需求,兩路并聯(lián)在一起,同步控制

PCB圖▼

這是一個(gè)燈板的PCB,注意左右兩邊接口表明的順序,左邊的DI對(duì)應(yīng)右邊的DO,可以通過(guò)左右接口將多個(gè)燈板并聯(lián)起來(lái)控制。

2、程序設(shè)計(jì)

歸零碼通訊是單線通訊,對(duì)時(shí)序要求比較嚴(yán)格,通過(guò)單周期內(nèi)的高低電平持續(xù)時(shí)間來(lái)判斷數(shù)據(jù)bit為0或1
規(guī)格書(shū)描述▼




信息1: 單顆燈珠可接收3Byte(24bit)數(shù)據(jù)
信息2: 單顆燈珠數(shù)據(jù)結(jié)構(gòu)G-R-B,高位先發(fā)
信息3: 碼元周期,上邊兩張圖都是這款燈珠規(guī)格書(shū)里的截圖,碼元周期T有兩種描述,分別是2us(500kHz)和1.25us(800kHz),1.25us是網(wǎng)上關(guān)于WS2812教程里出現(xiàn)比較多的典型碼元周期,2us似乎是該燈珠特有的,在實(shí)際的測(cè)試中,兩種周期均可以實(shí)現(xiàn)驅(qū)動(dòng),因此程序以1.25us進(jìn)行編寫(xiě)
信息4: 0碼和1碼占空比

T0碼高電平占比1碼高電平占比
2us14%45%
1.25us25.6%51.2%

2.1 驅(qū)動(dòng)方式

【程序以T = 1.25us為準(zhǔn)】
要想驅(qū)動(dòng)該燈珠,實(shí)際上是要實(shí)現(xiàn)0碼和1碼的發(fā)送時(shí)序,能夠產(chǎn)生這樣的時(shí)序信號(hào)的方式大致有三種:
第一種是使用延時(shí)函數(shù)在特定延時(shí)時(shí)間內(nèi)對(duì)輸出管腳進(jìn)行翻轉(zhuǎn)操作,這種方式非常占用單片機(jī)資源,而且實(shí)現(xiàn)1.25us延時(shí)的準(zhǔn)確度不高

第二種是使用定時(shí)器進(jìn)行PWM輸出,輸出頻率設(shè)置為800kHz即可實(shí)現(xiàn)1.25us周期循環(huán),通過(guò)改變周期內(nèi)占空比來(lái)實(shí)現(xiàn)0碼和1碼輸出

第三種是使用SPI,這種方式比較巧妙,使用SPI的clk線和mosi線,通過(guò)8分頻可以設(shè)置clk時(shí)鐘線的輸出頻率(主頻72Mhz),然后采用16byte數(shù)據(jù)模擬0碼和1碼,這樣輸出頻率為562.5kHz,理論上也是可以驅(qū)動(dòng)的,本人沒(méi)有實(shí)踐過(guò),該up主的文章【SPI驅(qū)動(dòng)ws2812】有介紹這種方式,感興趣的可以嘗試下。

第二種使用定時(shí)器的方式,精度高,只有在更新中斷服務(wù)程序才占用系統(tǒng)資源,是比較理想的驅(qū)動(dòng)方式,考慮到STM32單片機(jī)的定時(shí)器較多,硬件SPI有限(其他拓展功能有可能會(huì)使用到SPI),因此最終方案選擇使用定時(shí)器產(chǎn)生PWM來(lái)驅(qū)動(dòng)燈珠

2.2 定時(shí)器配置

避開(kāi)有特殊外設(shè)功能的IO口,這里選擇PB0管腳進(jìn)行PWM輸出,其對(duì)應(yīng)的定時(shí)器是TIM3,輸出通道是CH3
IO口配置為復(fù)用推挽輸出,啟動(dòng)復(fù)用時(shí)鐘▼

[ws2812.c] /********************************************************************************* @brief TIM對(duì)應(yīng)的GPIO配置* @param none* @retval none* @retval none*******************************************************************************/ static void TIM_GPIO_Config(void) {GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復(fù)用推挽輸出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); }

TIM3基礎(chǔ)以及PWM模式配置▼

[ws2812.c] /********************************************************************************* @brief TIM基礎(chǔ)配置以及PWM配置* @param none* @retval none* @retval none*******************************************************************************/ static void TIM_PWM_Config(void) {TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);/* Time base configuration */TIM_TimeBaseStructure.TIM_Period = 90-1; // (144 500KHz 2us) (90 800KHz 1.25us)TIM_TimeBaseStructure.TIM_Prescaler = 0;TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);/* PWM1 Mode configuration */TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_Pulse = 0;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OC3Init(TIM3, &TIM_OCInitStructure);TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIM_ARR重載,即周期重載TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM_CRR重載,即占空比重載TIM_ITConfig(TIM3,TIM_IT_Update, ENABLE); //使能TIM更新中斷 // TIM_Cmd(TIM3, ENABLE); //使能TIM}

注意:周期是從0開(kāi)始算的,預(yù)分頻為0,即不分頻,頻率為72MHz,周期為72MHz / 90 = 800kHz
使能定時(shí)器更新中斷,這樣可以在中斷服務(wù)函數(shù)里更改占空比
配置完成先不使能TIM3,等需要發(fā)送數(shù)據(jù)時(shí)再使能定時(shí)器

NVIC配置,這個(gè)隨意▼

[ws2812.c] /********************************************************************************* @brief 配置嵌套向量中斷控制器NVIC* @param none* @retval none*******************************************************************************/ static void NVIC_TIM3_Config(void) {NVIC_InitTypeDef NVIC_InitStructure;/* Configure one bit for preemption priority */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);/* 配置TIM3_IRQ中斷為中斷源 */NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure); }

到此定時(shí)器配置完成

2.3 數(shù)據(jù)處理與發(fā)送

一顆燈珠接收3byte(24bit)數(shù)據(jù),當(dāng)接收完3byte(24bit)數(shù)據(jù)后,自動(dòng)整型轉(zhuǎn)發(fā)接下來(lái)的數(shù)據(jù),高位先發(fā)
采用PWM來(lái)產(chǎn)生驅(qū)動(dòng)時(shí)序,一個(gè)周期代表1bit,這1bit代表0或者1則是由周期內(nèi)的高電平占空比來(lái)決定

在定時(shí)器配置中,周期設(shè)置TIM_ARR = 90
那么0碼占空比設(shè)置為20(22%),1碼占空比設(shè)置為50(55%)即可
實(shí)際測(cè)試0碼小于30,1碼大于35都是可以驅(qū)動(dòng)的

首先點(diǎn)亮一個(gè)燈珠,使其發(fā)出綠光,按數(shù)據(jù)格式G-R-B,需要發(fā)送3byte數(shù)據(jù)【0xFF0000】,總共24bit的數(shù)據(jù),數(shù)據(jù)處理就是根據(jù)24bit數(shù)據(jù)轉(zhuǎn)換成占空比數(shù)組,然后在中斷服務(wù)函數(shù)里更新TIM_CCR的值

數(shù)據(jù)處理和發(fā)送函數(shù)▼

[ws2812.c]#define DATA_SIZE 0x800 //2k #define DATA_BUF_Address 0x20004800 u8 DATA_BUF[DATA_SIZE] __attribute__ ((at(DATA_BUF_Address)));#define DATA_BIT_0 20 //20 #define DATA_BIT_1 50 //75 /********************************************************************************* @brief 發(fā)送數(shù)據(jù)* @param *buff 緩存基地址* @retval len 發(fā)送數(shù)據(jù)量(n byte)* @retval none*******************************************************************************/ void DATA_Send(u8 *buff, u32 len) {u8 i = 0;u32 j = 0;u32 counter = 0;u8 data;for(j = 0; j < len; j++){data = buff[j];//1byte數(shù)據(jù)分解成8bytefor(i = 0; i < 8; i++){DATA_BUF[counter] = ((data & 0x80) ? DATA_BIT_1 : DATA_BIT_0);data = data << 1;counter++;}}//初始化結(jié)構(gòu)體ws2812.data_cnt = len*8;ws2812.send_cnt = 0;ws2812.flag = 0;//使能TIM,開(kāi)始發(fā)送數(shù)據(jù)TIM3->CR1 |= TIM_CR1_CEN; }

【DATA_BUF[DATA_SIZE]】數(shù)組是一個(gè)足夠大的數(shù)組,最終的TIM_CCR值緩存數(shù)組

直接看發(fā)送函數(shù)有點(diǎn)難解釋,先看這個(gè)函數(shù)是如何調(diào)用的▼

[ws2812.c] u8 data[3]; /********************************************************************************* @brief 打開(kāi)文件并讀取文件基礎(chǔ)信息* @param name 文件名稱** @retval 1 模式1* @retval 2 模式2*******************************************************************************/ void test(void) {data[0] = 0xff;data[1] = 0x00;data[2] = 0x00;DATA_Send(data,3); }

使用一個(gè)u8類型的數(shù)組【data[3]】存儲(chǔ)綠燈數(shù)據(jù)【0xFF0000】
然后把數(shù)組地址和數(shù)據(jù)個(gè)數(shù)【3】傳遞到發(fā)送函數(shù)【DATA_Send】

回到【DATA_Send】函數(shù),for(i = 0; i < 8; i++)循環(huán)將1byte數(shù)據(jù)轉(zhuǎn)換成8byte數(shù)據(jù),是占空比的值,存儲(chǔ)在【DATA_BUF[ ]】數(shù)組里邊,那么經(jīng)過(guò)三次循環(huán),將【data[3]】分解成【DATA_BUF[24]】個(gè)數(shù)據(jù)
此時(shí)【DATA_BUF[24]】各值為

DATA_BUF[0]DATA_BUF[1]DATA_BUF[2]DATA_BUF[3]DATA_BUF[4]DATA_BUF[5]DATA_BUF[6]DATA_BUF[7]
5050505050505050
DATA_BUF[8]DATA_BUF[9]DATA_BUF[10]DATA_BUF[11]DATA_BUF[12]DATA_BUF[13]DATA_BUF[14]DATA_BUF[15]
2020202020202020
DATA_BUF[16]DATA_BUF[17]DATA_BUF[18]DATA_BUF[19]DATA_BUF[20]DATA_BUF[21]DATA_BUF[22]DATA_BUF[23]
2020202020202020

轉(zhuǎn)換完數(shù)據(jù)后先初始化一個(gè)結(jié)構(gòu)體,然后啟動(dòng)定時(shí)器TIM3
結(jié)構(gòu)體定義▼

[ws2812.h] typedef struct {u32 data_cnt; //需發(fā)送的數(shù)據(jù)個(gè)數(shù)u32 send_cnt; //已發(fā)送的數(shù)據(jù)個(gè)數(shù)u8 flag; //發(fā)送完成標(biāo)志 }WS2812_TypeDef;

2.4 定時(shí)器中斷服務(wù)函數(shù)

在數(shù)據(jù)發(fā)送函數(shù)最后的操作是啟動(dòng)定時(shí)器TIM3,具體的占空比更改是在中斷服務(wù)程序里實(shí)現(xiàn)的▼

[ws2812.c] /********************************************************************************* @brief TIM中斷服務(wù)程序* @param none* @retval none*******************************************************************************/ void TIM3_IRQHandler(void) {TIM3->SR = (uint16_t)~TIM_IT_Update; //清除中斷TIM3->CCR3 = DATA_BUF[ws2812.send_cnt]; //更新占空比ws2812.send_cnt++;if(ws2812.send_cnt > ws2812.data_cnt){ //當(dāng)發(fā)送完所需數(shù)據(jù)之后關(guān)閉TIMTIM3->CR1 &= ~((uint16_t)TIM_CR1_CEN); //關(guān)閉TIM} }

定時(shí)器每產(chǎn)生一次中斷,可以理解為發(fā)送了1byte數(shù)據(jù),因此在中斷函數(shù)里更新下一個(gè)TIM_CCR值,當(dāng)發(fā)送完24byte數(shù)據(jù)后,就關(guān)閉定時(shí)器TIM3,

3、總結(jié)

在主函數(shù)里調(diào)用TIM配置函數(shù)后,直接調(diào)用測(cè)試函數(shù)【void test(void)】即可驅(qū)動(dòng)一顆燈珠發(fā)綠光▼

(由于左右兩路是并聯(lián)的,因此兩個(gè)燈珠同步點(diǎn)亮)
改下函數(shù)即可控制其他燈珠▼

void test(void) {data[0] = 0x0f;data[1] = 0x00;data[2] = 0x00;data[3] = 0x00;data[4] = 0x0f;data[5] = 0x00;data[6] = 0x00;data[7] = 0x00;data[8] = 0x0f;DATA_Send(data,9);}

RGB效果(拍照色差比較嚴(yán)重)▼

內(nèi)置ws2812的彩色LED燈珠的驅(qū)動(dòng)就這么些,具體怎么使用還得看應(yīng)用場(chǎng)景

總結(jié)

以上是生活随笔為你收集整理的stm32【RGB_LED_WS2812灯珠】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。