新聞詳情

幻彩燈珠--STM32F103 SPI驅動WS2812

 二維碼
發表時間:2022-03-30 09:59
文章附圖

  之前發了一個基于涂鴉平臺的寵物自動喂食器點此跳轉,已經有小夜燈功能了,但是躁動的心豈能因此平靜,功能以及花里胡哨,那就讓他華麗起來,除了畫手們的涂裝風格,作為電子工程師,得用我們自己的方式美化。正好手里有個ws2812的燈環,全彩模式開始。

  WS2812特點:

  1.WS2812燈珠內置控制電路與RGB芯片,集成在一個5050封裝的元器件中,構成一個完整的外控像素點。

  2.每個像素點的三基色可實現256級亮度顯示,完成16777216種顏色的全真色彩顯示,掃描頻率不低于400Hz/s。

  3.串行級聯接口,能通過一根信號線完成數據的接收與解碼。

  4.當刷新速率30幀/秒時,低速模式級聯數不小于512點,高速模式不小于1024點。

  5.數據發送速度可達800Kbps。

  數據傳輸:

  ws2812的每顆燈珠的控制需要24位數據,分為8位綠色+8位紅色+8位藍色,每個像素點的三基色顏色可實現256級亮度顯示,完成16777216種顏色的全真色彩顯示。燈珠之間采用串行級聯,在上電復位以后,控制器可向燈帶發送一串24bit的數據,比如需要點亮10顆燈,我們發送10個24bit的數據到第一個燈,第一個燈的DIN端接受控制器發過來的24bit數據,第一個燈珠會提取第一個24bit數據后會將該數據送進數據鎖存器,剩余的數據經過內部整形電路向下傳送,直到所有的燈都獲取一個24bit數據。這10個24bit數據的發送間隔不能超過50us,否則會導致下一次的顏色數據被第一個重新鎖存,就無法完成完整的點亮。( f# T% }0 [3 q

  這里需要著重點一下時序相關的知識,先看時序波形圖和數據傳輸時間表:





  根據上面兩個圖可以看到ws2812的電平反轉是納秒級別的,所以在使用單片機外設時我們需要對單片機的外設的傳輸速度進行控制,速度慢了根本無法點亮。; S( G) r; U: ]/ l" e: d( y) W

  操作開始:在驅動ws2812的時候一般采用PWM或者SPI的方式,這兩個速度較快,比直接使用IO口進行電平反轉要方便,且控制效果更好。這里我們采用SPI的方式,因為在點燈是的數據發送間隔時間的約束,如果我們使用SPI發送的數據較多,中途遇到中斷可能會打斷我們的點燈,所以我這里使用SPI的DMA進行控制,不怕被其他影響。

  $ K& Q* H) y8 A3 N

  通過硬件SPI我們模擬WS2812的通信時序。單片機選用STM32F103,主頻72M,SPI1分頻設置為8,這樣SPI1的通信頻率為9M。時間很充足,如果使用SPI2,則需要減小分頻系數,否則無法點亮。我這里采用的便是SPI2,4分頻,因為SPI1被我的屏幕占用了。1 h7 F1 f/ g& Q2 l3 H

  .h文件只要定義了燈珠的個數和0碼1碼。不同的燈珠數量只需修改PIXEL_NUM 的值

  #ifndef __WS2812_H#define __WS2812_H

  #include "stm32f10x.h"

  #define PIXEL_NUM 24

  //硬件spi模擬ws2811時序(用spi的8位數據模擬ws281x的一位數據)//要將系統時鐘設置為56M,分頻數設置為8,則SPI的通信頻率為7M,傳輸一位數據的時間約為143納秒(ns)//3*143 = 429ns 5*143 = 715ns 符合WS281X芯片的通信時序。// _____ // | |___| 11111000 high level// ___ // | |_____| 11100000 low level" b0 v* d3 B$ G5 m

  #define WS_HIGH 0XF8#define WS_LOW 0XE0;

  void ws281x_init(void);void ws281x_closeAll(void);void ws281x_rainbowCycle(uint8_t wait);uint32_t ws281x_color(uint8_t red, uint8_t green, uint8_t blue);void ws281x_setPixelColor(uint16_t n ,uint32_t GRBcolor);void ws281x_show(void);

  void ws281x_theaterChase(uint32_t c, uint8_t wait);void ws281x_colorWipe(uint32_t c, uint8_t wait);void ws281x_rainbow(uint8_t wait);void ws281x_theaterChaseRainbow(uint8_t wait);

  #endif /* __WS2812_H */

  #include "../BOARD/ws2812/ws2812.h"#include "usart.h"#include "delay.h"

  uint8_t pixelBuffer[PIXEL_NUM][24] ;" k* H) D$ J, e; F W; H

  7 Z0 x! U" e; j% G" m

  void ws281x_init(void){ GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; DMA_InitTypeDef DMA_InitStructure;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //PORTA時鐘使能 RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); //SPI1時鐘使能 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA傳輸5 m/ ^; {1 S- T5 l) c

  /* PA7 SPI1_MOSI */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PA7復用推挽輸出 SPI GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOA

  SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; //設置SPI單向或者雙向的數據模式:SPI設置為雙線雙向全雙工 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //設置SPI工作模式:設置為主SPI SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //設置SPI的數據大小:SPI發送接收8位幀結構 SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //串行同步時鐘的空閑狀態為低電平 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步時鐘的第2個跳變沿(上升或下降)數據被采樣 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信號由硬件(NSS管腳)還是軟件(使用SSI位)管理:內部NSS信號有SSI位控制 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //定義波特率預分頻的值:波特率預分頻值為16 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定數據傳輸從MSB位還是LSB位開始:數據傳輸從MSB位開始 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值計算的多項式 SPI_Init(SPI2, &SPI_InitStructure); //根據SPI_InitStruct中指定的參數初始化外設SPIx寄存器

  SPI_Cmd(SPI2, ENABLE); //使能SPI外設 SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE); DMA_DeInit(DMA1_Channel5); //將DMA的通道1寄存器重設為缺省值 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(SPI2 -> DR); //cpar; //DMA外設ADC基地址 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)pixelBuffer; //cmar; //DMA內存基地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //數據傳輸方向,從內存讀取發送到外設 DMA_InitStructure.DMA_BufferSize = PIXEL_NUM * 24; //cndtr; //DMA通道的DMA緩存的大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設地址寄存器不變 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //內存地址寄存器遞增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //數據寬度為8位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //數據寬度為8位 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常緩存模式 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x擁有中優先級 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x沒有設置為內存到內存傳輸 DMA_Init(DMA1_Channel5, &DMA_InitStructure); //根據DMA_InitStruct中指定的參數初始化DMA的通道USART1_Tx_DMA_Channel所標識的寄存器 ws281x_closeAll(); //關閉全部的燈 delay_ms(100); //關閉全部的燈需要一定的時間 }

  void ws281x_closeAll(void){ uint16_t i; uint8_t j; for(i = 0; i < PIXEL_NUM; ++i) { for(j = 0; j < 24; ++j) { pixelBuffer[j] = WS_LOW; } } ws281x_show();}

  uint32_t ws281x_color(uint8_t red, uint8_t green, uint8_t blue){ return green << 16 | red << 8 | blue;}4 @, B7 p) q) {, `, I" X) s

  void ws281x_setPixelColor(uint16_t n ,uint32_t GRBcolor){ uint8_t i; if(n < PIXEL_NUM) { for(i = 0; i < 24; ++i) { pixelBuffer[n] = (((GRBcolor << i) & 0X800000) ? WS_HIGH : WS_LOW); } }}$ s) V [/ M! [& k: l2 S; F

  void ws281x_setPixelRGB(uint16_t n ,uint8_t red, uint8_t green, uint8_t blue){ uint8_t i; if(n < PIXEL_NUM) { for(i = 0; i < 24; ++i) { pixelBuffer[n] = (((ws281x_color(red,green,blue) << i) & 0X800000) ? WS_HIGH : WS_LOW); } }} S8 t. {2 h0 ~+ d$ f1 f

  void ws281x_show(void){ DMA_Cmd(DMA1_Channel5, DISABLE ); //關閉USART1 TX DMA1 所指示的通道 DMA_CleaRFlag(DMA1_FLAG_TC5); DMA_SetCurrDataCounter(DMA1_Channel5,24 * PIXEL_NUM );//DMA通道的DMA緩存的大小 DMA_Cmd(DMA1_Channel5, ENABLE); //使能USART1 TX DMA1 所指示的通道}( u f4 G3 L2 H# {6 x- J) Q

  / ], A6 r3 L9 b+ c# f9 ~) R

  // Input a value 0 to 255 to get a color value.// The colours are a transition r - g - b - back to r.uint32_t ws281x_wheel(uint8_t wheelPos) { wheelPos = 255 - wheelPos; if(wheelPos < 85) { return ws281x_color(255 - wheelPos * 3, 0, wheelPos * 3); } if(wheelPos < 170) { wheelPos -= 85; return ws281x_color(0, wheelPos * 3, 255 - wheelPos * 3); } wheelPos -= 170; return ws281x_color(wheelPos * 3, 255 - wheelPos * 3, 0);}0 {# {; H7 z! J0 T$ |# Z

  // Fill the dots one after the other with a colorvoid ws281x_colorWipe(uint32_t c, uint8_t wait) { for(uint16_t i=0; i

  void ws281x_rainbow(uint8_t wait) { uint16_t i, j;

  for(j=0; j<256; j++) { for(i=0; i

  // Slightly different, this makes the rainbow equally distributed throughoutvoid ws281x_rainbowCycle(uint8_t wait) { uint16_t i, j;( m/ S/ q* U' D, I4 ]5 B

  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel for(i=0; i< PIXEL_NUM; i++) { ws281x_setPixelColor(i,ws281x_wheel(((i * 256 / PIXEL_NUM) + j) & 255)); } ws281x_show(); delay_ms(wait); }}# J( `" j3 |5 l7 j2 G g

  //Theatre-style crawling lights.void ws281x_theaterChase(uint32_t c, uint8_t wait) { for (int j=0; j<10; j++) { //do 10 cycles of chasing for (int q=0; q < 3; q++) { for (uint16_t i=0; i < PIXEL_NUM; i=i+3) { ws281x_setPixelColor(i+q, c); //turn every third pixel on } ws281x_show();% `& ^* R: S7 R

  delay_ms(wait);

  for (uint16_t i=0; i < PIXEL_NUM; i=i+3) { ws281x_setPixelColor(i+q, 0); //turn every third pixel off } } }}2 u% i! Q$ [9 I& R6 l8 R

  //Theatre-style crawling lights with rainbow effectvoid ws281x_theaterChaseRainbow(uint8_t wait) { for (int j=0; j < 256; j++) { // cycle all 256 colors in the wheel for (int q=0; q < 3; q++) { for (uint16_t i=0; i < PIXEL_NUM; i=i+3) { ws281x_setPixelColor(i+q, ws281x_wheel( (i+j) % 255)); //turn every third pixel on } ws281x_show();. T4 W/ b( e% o, E3 n

  delay_ms(wait);

  for (uint16_t i=0; i < PIXEL_NUM; i=i+3) { ws281x_setPixelColor(i+q, 0); //turn every third pixel off } } }}

  ( S" o5 I1 l0 m* `1 r( l- P

  ) M0 f9 L# m" x8 {

  1 Q1 n! D3 w( g9 E, S

  const char s[5];int8_t i;; R* I Y$ m( @* q' S" K

  int main(void){// usart1_init(115200); delay_init();% R( y0 V" N, i$ y) [% I

  ws281x_init();

  while(1) { // Some example procedures showing how to display to the pixels: ws281x_colorWipe(ws281x_color(255, 0, 0), 50); // Red ws281x_colorWipe(ws281x_color(0, 255, 0), 50); // Green ws281x_colorWipe(ws281x_color(0, 0, 255), 50); // Blue//colorWipe(strip.Color(0, 0, 0, 255), 50); // White RGBW // Send a theater pixel chase in... ws281x_theaterChase(ws281x_color(127, 127, 127), 50); // White ws281x_theaterChase(ws281x_color(127, 0, 0), 50); // Red ws281x_theaterChase(ws281x_color(0, 0, 127), 50); // Blue. K/ h) C4 i7 a. t- D) b

  //ws281x_rainbow(20); ws281x_rainbowCycle(20); ws281x_theaterChaseRainbow(200); for(i = 0; i < PIXEL_NUM; ++i) { ws281x_setPixelColor(i, ws281x_color(0,250,0)); ws281x_show(); delay_ms(500); } }}4 K1 G9 z9 ^3 Z- g/ {+ n" f

  在ws2812.c移植了Adafruit_NeoPixel庫的部分函數,用以實現炫酷的顯示效果。; k h. }7 o9 N

  都是基礎的SPI,不說廢話,移植就能用。展示一下效果吧!# U8 ]7 i! d2 f# n1 v2 g. z




  『本文轉載自網絡,版權歸原作者所有,如有侵權請聯系刪除』

最新文章
2025-03-19
2025-03-19
2025-03-19
2025-03-18
2025-04-25
2025-04-25
2025-04-25
2025-04-25
2025-04-25
2025-04-25
隨機文章
LED光源的種類很多,不同的LED燈,內部結構所用的燈珠也會有細微差別。今天,小編為大家全面、系統地科普一下LED燈珠的常見類型,供大家參考使用。LED光源可以分為兩大類:芯片類光源和替換類光源。芯片類光源1引腳插入型(DIP)這種LED燈珠是結構最簡單的發光二極管,因為燈珠下面有兩根形似“腳”的細絲,可以直接穿接在電路板上,所以稱之為引腳插入式的燈珠。使用特點:它的安全性好、性能穩定,在低...
日前,富采投控旗下晶電與錼創科技全資子公司錼創顯示宣布合作,將由錼創顯示協助其建置6英寸Micro LED生產線。目前Micro LED大型顯示器是前驅產品,盡管仍有技術與成本問題待克服,不過TrendForce集邦咨詢同步看好Micro LED運用于頭戴式的AR穿透式智慧眼鏡、穿戴式的智慧手表以及車用的智慧駕駛座艙及透明顯示產品的發展,有望刺激Micro LED高階應用產品的誕生,預估至2...
  LED電子顯示屏很重要的組成部分就有LED單元板,如果單元板有問題,會直影響LED顯示屏的整體質量!所以,如何辨別LED單元板的好壞是LED顯示屏商家關心的問題,下面整理了一些檢測LED單元板的常見方法。  一、材質  1. LED單元板板材  有些LED單元板廠家為了低價競爭,采用廉價的阻燃紙質板或者是單面纖維板作為LED燈的PCB線路板。因為全玻纖PCB板很貴。剛開始看不出差別,一般...
  8月17日,福建泉州市2022年重大項目視頻連線開工活動在晉江舉行。據悉,晉江市此次共22個項目集中開工,總投資965.5億元,涵蓋光電顯示等多個領域。  此次集中開工活動中,位于高端制造產業園的MiniLED封裝產業項目作為安溪縣的代表性重大項目,向全市展示開工場面。    圖片來源:安溪縣融媒體中心  據悉,安溪縣MiniLED封裝產業項目總投資11.67億元,年度計劃投資1.5億元...
  1:幻彩燈珠簡介  1.1 幻彩內置IC燈珠的特點  幻彩內置IC燈泡主要有兩個部分:一個是LED,另一個是IC晶體管。LED部分負責產生光,而IC晶體管則負責控制LED的亮度和顏色。通常情況下,一個幻彩內置IC燈泡包含三個不同顏色的LED:紅、 綠、 和 藍 (RGB) 。 控制晶體 管開關的電流大小,來調整 LED的亮度。而通過不同比例地控制三個 LED的亮度,可以得到不同顏色的光。...
  led燈在我們的生活中使用極廣,同時在使用led燈時還會出現一些故障,如只發弱光,甚至是不亮了。面對這些情況應該怎么辦?  一、led燈只發弱光怎么回事  電源參數主要注意兩個,一個是瓦數,一個是電源輸出電壓(也就是燈板上的輸入DC電壓)。  也可能是燈的驅動電源損壞了,若是獨立電源,可買個相同規格的更換,若是一體的,建議整燈更換。  還有可能是led燈泡的問題,也可以再買個燈泡看看。 ...
  led燈是現代生活中再常見不過的燈具,人們選擇led燈時會考慮其使用壽命,更需要注意保養。究竟led燈怎么保養?  一、led燈的使用壽命多少  正常來說,led燈的使用壽命差不多都是能夠達到4萬小時到5萬小時的,這個跟led燈的品牌和性能是有很大的關系的,像大品牌的led燈各方面還是比較有保障的,使用壽命久,而且質量也是非常不錯的,不容易出現問題。  現在led燈的應用還是非常廣泛的,...
  led燈不亮怎么回事  led燈是我們生活中再常見不過的燈具,led燈在使用過程中容易出現不亮的情況,究竟是怎么回事?不亮的話怎么修理呢  一、led燈不亮怎么回事  1、led燈條的焊接點有虛焊現象,運輸過程中的震動造成焊點脫落而導致燈帶不亮。  2、焊錫質量不好,led燈條在彎折的過程中焊點容易產生脆裂、脫落現象。  3、安裝時彎折角度過大,造成led燈條焊點與銅箔分離而導致不亮。 ...
  對于led燈人們都并不陌生,甚至我們生活中大部分燈具都是led燈,led燈在使用過程中可能會出現一些問題,如不亮或者變弱的問題   一、led燈不亮怎么辦  1、關掉開關,在折疊梯子的幫助下,拆掉led吸頂燈的外殼,并檢查燈管是否出現發黑發暗的地方。如果有,很有可能是燈管已經損壞了。必須更換了,這種LED燈管在五金商店里面可以買到,建議選擇知名品牌。  2、如果燈管還是完好的,那么應該試...

關于天成    產品中心    新聞資訊    技術支持    人力資源    聯系方式

聯系我們

----------------------------------------------

銷售熱線:181 2996 9297

電話:4008-320-328 /   0755-29573979   

傳真:0755-29573533

郵件:tczm@tczmled.com   郵編:518000

地址::深圳市光明區鳳凰街道長鳳路263號天成高新園

郵箱:jiangeng@tczmled.com   (投訴與合作)


(關注我們)
_____________________________________________________________________________________________________________________________________________________________________________________________________________________________
_____________________________________________________________________________________________________________________________________________________________________________________________________________________________
登錄
登錄
其他賬號登錄:
我的資料
留言
回到頂部
主站蜘蛛池模板: 甘谷县| 甘泉县| 长兴县| 隆昌县| 新干县| 张家口市| 绥江县| 新民市| 高阳县| 嘉禾县| 邯郸县| 延寿县| 城固县| 庐江县| 临高县| 平陆县| 隆回县| 广灵县| 大埔县| 牙克石市| 长海县| 百色市| 明水县| 桑日县| 安义县| 新和县| 弋阳县| 漾濞| 荣成市| 崇礼县| 桑植县| 德钦县| 滁州市| 琼海市| 六盘水市| 通河县| 凤阳县| 新郑市| 宜都市| 德令哈市| 建德市|