之前發(fā)了一個(gè)基于涂鴉平臺(tái)的寵物自動(dòng)喂食器點(diǎn)此跳轉(zhuǎn),已經(jīng)有小夜燈功能了,但是躁動(dòng)的心豈能因此平靜,功能以及花里胡哨,那就讓他華麗起來(lái),除了畫手們的涂裝風(fēng)格,作為電子工程師,得用我們自己的方式美化。正好手里有個(gè)ws2812的燈環(huán),全彩模式開始。
WS2812特點(diǎn):
1.WS2812燈珠內(nèi)置控制電路與RGB芯片,集成在一個(gè)5050封裝的元器件中,構(gòu)成一個(gè)完整的外控像素點(diǎn)。
2.每個(gè)像素點(diǎn)的三基色可實(shí)現(xiàn)256級(jí)亮度顯示,完成16777216種顏色的全真色彩顯示,掃描頻率不低于400Hz/s。
3.串行級(jí)聯(lián)接口,能通過(guò)一根信號(hào)線完成數(shù)據(jù)的接收與解碼。
4.當(dāng)刷新速率30幀/秒時(shí),低速模式級(jí)聯(lián)數(shù)不小于512點(diǎn),高速模式不小于1024點(diǎn)。
5.數(shù)據(jù)發(fā)送速度可達(dá)800Kbps。
數(shù)據(jù)傳輸:
ws2812的每顆燈珠的控制需要24位數(shù)據(jù),分為8位綠色+8位紅色+8位藍(lán)色,每個(gè)像素點(diǎn)的三基色顏色可實(shí)現(xiàn)256級(jí)亮度顯示,完成16777216種顏色的全真色彩顯示。燈珠之間采用串行級(jí)聯(lián),在上電復(fù)位以后,控制器可向燈帶發(fā)送一串24bit的數(shù)據(jù),比如需要點(diǎn)亮10顆燈,我們發(fā)送10個(gè)24bit的數(shù)據(jù)到第一個(gè)燈,第一個(gè)燈的DIN端接受控制器發(fā)過(guò)來(lái)的24bit數(shù)據(jù),第一個(gè)燈珠會(huì)提取第一個(gè)24bit數(shù)據(jù)后會(huì)將該數(shù)據(jù)送進(jìn)數(shù)據(jù)鎖存器,剩余的數(shù)據(jù)經(jīng)過(guò)內(nèi)部整形電路向下傳送,直到所有的燈都獲取一個(gè)24bit數(shù)據(jù)。這10個(gè)24bit數(shù)據(jù)的發(fā)送間隔不能超過(guò)50us,否則會(huì)導(dǎo)致下一次的顏色數(shù)據(jù)被第一個(gè)重新鎖存,就無(wú)法完成完整的點(diǎn)亮。( f# T% }0 [3 q
這里需要著重點(diǎn)一下時(shí)序相關(guān)的知識(shí),先看時(shí)序波形圖和數(shù)據(jù)傳輸時(shí)間表:
根據(jù)上面兩個(gè)圖可以看到ws2812的電平反轉(zhuǎn)是納秒級(jí)別的,所以在使用單片機(jī)外設(shè)時(shí)我們需要對(duì)單片機(jī)的外設(shè)的傳輸速度進(jìn)行控制,速度慢了根本無(wú)法點(diǎn)亮。; S( G) r; U: ]/ l" e: d( y) W
操作開始:在驅(qū)動(dòng)ws2812的時(shí)候一般采用PWM或者SPI的方式,這兩個(gè)速度較快,比直接使用IO口進(jìn)行電平反轉(zhuǎn)要方便,且控制效果更好。這里我們采用SPI的方式,因?yàn)樵邳c(diǎn)燈是的數(shù)據(jù)發(fā)送間隔時(shí)間的約束,如果我們使用SPI發(fā)送的數(shù)據(jù)較多,中途遇到中斷可能會(huì)打斷我們的點(diǎn)燈,所以我這里使用SPI的DMA進(jìn)行控制,不怕被其他影響。
$ K& Q* H) y8 A3 N
通過(guò)硬件SPI我們模擬WS2812的通信時(shí)序。單片機(jī)選用STM32F103,主頻72M,SPI1分頻設(shè)置為8,這樣SPI1的通信頻率為9M。時(shí)間很充足,如果使用SPI2,則需要減小分頻系數(shù),否則無(wú)法點(diǎn)亮。我這里采用的便是SPI2,4分頻,因?yàn)镾PI1被我的屏幕占用了。1 h7 F1 f/ g& Q2 l3 H
.h文件只要定義了燈珠的個(gè)數(shù)和0碼1碼。不同的燈珠數(shù)量只需修改PIXEL_NUM 的值
#ifndef __WS2812_H#define __WS2812_H
#include "stm32f10x.h"
#define PIXEL_NUM 24
//硬件spi模擬ws2811時(shí)序(用spi的8位數(shù)據(jù)模擬ws281x的一位數(shù)據(jù))//要將系統(tǒng)時(shí)鐘設(shè)置為56M,分頻數(shù)設(shè)置為8,則SPI的通信頻率為7M,傳輸一位數(shù)據(jù)的時(shí)間約為143納秒(ns)//3*143 = 429ns 5*143 = 715ns 符合WS281X芯片的通信時(shí)序。// _____ // | |___| 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時(shí)鐘使能 RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); //SPI1時(shí)鐘使能 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復(fù)用推挽輸出 SPI GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOA
SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; //設(shè)置SPI單向或者雙向的數(shù)據(jù)模式:SPI設(shè)置為雙線雙向全雙工 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //設(shè)置SPI工作模式:設(shè)置為主SPI SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //設(shè)置SPI的數(shù)據(jù)大小:SPI發(fā)送接收8位幀結(jié)構(gòu) SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //串行同步時(shí)鐘的空閑狀態(tài)為低電平 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步時(shí)鐘的第2個(gè)跳變沿(上升或下降)數(shù)據(jù)被采樣 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信號(hào)由硬件(NSS管腳)還是軟件(使用SSI位)管理:內(nèi)部NSS信號(hào)有SSI位控制 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //定義波特率預(yù)分頻的值:波特率預(yù)分頻值為16 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定數(shù)據(jù)傳輸從MSB位還是LSB位開始:數(shù)據(jù)傳輸從MSB位開始 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值計(jì)算的多項(xiàng)式 SPI_Init(SPI2, &SPI_InitStructure); //根據(jù)SPI_InitStruct中指定的參數(shù)初始化外設(shè)SPIx寄存器
SPI_Cmd(SPI2, ENABLE); //使能SPI外設(shè) SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE); DMA_DeInit(DMA1_Channel5); //將DMA的通道1寄存器重設(shè)為缺省值 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(SPI2 -> DR); //cpar; //DMA外設(shè)ADC基地址 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)pixelBuffer; //cmar; //DMA內(nèi)存基地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //數(shù)據(jù)傳輸方向,從內(nèi)存讀取發(fā)送到外設(shè) DMA_InitStructure.DMA_BufferSize = PIXEL_NUM * 24; //cndtr; //DMA通道的DMA緩存的大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設(shè)地址寄存器不變 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //內(nèi)存地址寄存器遞增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //數(shù)據(jù)寬度為8位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //數(shù)據(jù)寬度為8位 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常緩存模式 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x擁有中優(yōu)先級(jí) DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x沒有設(shè)置為內(nèi)存到內(nèi)存?zhèn)鬏?DMA_Init(DMA1_Channel5, &DMA_InitStructure); //根據(jù)DMA_InitStruct中指定的參數(shù)初始化DMA的通道USART1_Tx_DMA_Channel所標(biāo)識(shí)的寄存器 ws281x_closeAll(); //關(guān)閉全部的燈 delay_ms(100); //關(guān)閉全部的燈需要一定的時(shí)間 }
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 ); //關(guān)閉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ù)的部分函數(shù),用以實(shí)現(xiàn)炫酷的顯示效果。; k h. }7 o9 N
都是基礎(chǔ)的SPI,不說(shuō)廢話,移植就能用。展示一下效果吧!# U8 ]7 i! d2 f# n1 v2 g. z
『本文轉(zhuǎn)載自網(wǎng)絡(luò),版權(quán)歸原作者所有,如有侵權(quán)請(qǐng)聯(lián)系刪除』
|