|
|
WS2812的單總線是根據高電平時間決定是邏輯1還是邏輯0的,而SPI恰巧是不歸零碼,只有當數據比特改變時,才會改變MOSI的電平。
比如輸出11110111的時候,前四個1MOSI都是高電平,不會回到低電平,因此可以使用SPI模擬2812單總線,時序很容易控制,不需要軟件TOGGLE。
STC8G的spi算是比較簡陋的類型,功能不是很多,但是只是驅動ws2812彩燈的話,是完全足夠的,簡陋反而是優點,不需要寫太多的寄存器,這個功能我一下午就寫出來了。
tips:STC的spi,MOSI在空閑時會輸出MISO采集到的電平,但是一些情況下,比如使用8腳單片機,MOSI引腳又不能閑置,可以關閉MISO引腳的數字輸入功能,這樣子,軟件在讀取MISO引腳電平的時候不論外部電平,始終返回0。這樣MISO引腳就可以用作他用了。
同理SCLK,SPI時鐘引腳,只要設置為高阻模式,就可以不受SPI控制,并且可以自由讀取外部電平,用作按鍵輸入使用。
下面是一套驅動庫與測試范例代碼,這是一個底層接口,你可以自己封裝為頭文件,也可以基于
WS2812_Send_Data(unsigned char WS2812_Num,unsigned char Red,unsigned char Green,unsigned char Blue)
的底層接口,實現流水燈,RGB等等效,范例中給出的是實現RGB顏色平滑變化的代碼。
注意:主頻一定要設置為24MHZ,否則可能導致高低電平時間不準,無法點亮ws2812
理論上,STC8單片機工作在從機模式下,是可以從MISO輸出一樣的信息的,但是我這里試了兩三次沒有成功,就沒有繼續做,沒必要了,已經足夠。
STC8H系列的SPI雖然比STC8G系列多了一些功能,但是基本的SPI寄存器是一樣的,理論上STC8H系列也可以使用,我就懶得測試了,網友們可以測試一次。本庫函數目前只在STC8G1K08A上使用過。
下面的代碼已經實現了基本的Init,Pinset等arduino風格接口,你可以很輕松的封裝為頭文件,當然也可以不封裝,直接作為C文件調用。
#include "STC8G.H"
#include "intrins.h"
//下面的代碼,要求使用SPI實現驅動WS2812彩燈
//6MHZ時鐘下,SPI傳輸一位需要166.7ns
//使用本驅動庫,必須設置系統時鐘為24Mhz,否則你需要手動修改一碼零碼高低電平時間
#define High_Code 0xf8//11111000
#define Zero_Code 0xC0//11000000
//開始發送時,SPIF = 0,結束發送后SPIF = 1;
void SPI_Send_1_Byte(unsigned char SPI_Data){
SPDAT = SPI_Data;//寫入后,硬件置SPIF = 0;
while(!(SPSTAT & 0x80));//SPI控制器發送完數據后,退出while循環
SPSTAT = 0xC0;//清空中斷標志
}
void WS2812_Lightup(){
//調用SPI_Send_1_Byte,發送80us的0x00,使燈珠亮起
//80us/0.1667*8 = 60,循環發送40個0
unsigned char i;
for(i = 0;i < 40;i++){//循環40次
SPI_Send_1_Byte(0x00);}
}
void WS2812_PinSet(unsigned char Pin){
//傳入引腳,配置引腳為推挽
if(Pin == 34){//STC8 MOSI
P_SW1 |= 0X0C;//B3B2 = 1;
SPCTL |= 0X10;//B4 MSTR = 1;
P3IE &= 0XF7; //P33IE = 0;
P3M0 |= 0x10; P3M1 &= ~0x10;}//P34推挽
else if(Pin == 40){//MOSI
P_SW1 |= 0X08;//B3 = 1;
SPCTL |= 0X10;//MSTR = 1;
P4IE &= 0XFD;//P41IE = 0;
P4M0 |= 0x01; P4M1 &= ~0x01;}//P40推挽
else if(Pin == 23){//MOSI
P_SW1 |= 0X04;//B2 = 1;
SPCTL |= 0X10;//MSTR = 1;
P2IE &= 0XEF;//P24IE = 0;
P2M0 |= 0x08; P2M1 &= ~0x08;}//P23推挽
else if(Pin == 13){//MOSI
P_SW1 &= 0XF3;//B3B2 = 0;
SPCTL |= 0X10;//MSTR = 1;
P1IE &= 0XEF;//B4 P14IE = 0
P1M0 |= 0x08; P1M1 &= ~0x08;}//P13推挽
else if(Pin == 54){//STC8G1K08A 8PIN MOSI
P_SW1 &= 0XF3;//B3B2=0
SPCTL |= 0X10;//B4 MSTR = 1,主機
P3IE &= 0xF7;//關閉P33 MISO數字輸入
P5M0 |= 0x10; P5M1 &= ~0x10;}//P54推挽
}
void WS2812_Init(unsigned char Pin_Sel){//對于STC8G1K08A 8Pin需要執行不同的引腳初始化
//初始化,并決定是否為主機從機
P_SW1 &= 0XF3;//B3B2 = 0;
P_SW2 |= 0X80;//允許訪問擴展寄存器
SPSTAT = 0XC0;//清零中斷標志
SPCTL = 0XD4;//SPEN = 1;SSIG = 1;先發高位;主機模式;時鐘空閑低電平;后時鐘沿采樣;SPI CLK為sysclk/4;
WS2812_PinSet(Pin_Sel);
}
void WS2812_Deinit(){//關閉SPI,但引腳解綁需要根據需求手動配置
SPSTAT = 0XC0;
SPCTL &= 0XBF;//SPEN = 0;
}
void WS2812_Send_Data(unsigned char WS2812_Num,unsigned char Red,unsigned char Green,unsigned char Blue){
//發送數據順序為GRB
unsigned char i,j;
unsigned char dat;
for(j = 0;j < WS2812_Num;j++){
//Green
dat = Green;
for(i = 0; i < 8; i++){
if(dat & 0x80) SPI_Send_1_Byte(High_Code); // 提取最高位,判斷發1碼還是0碼
else SPI_Send_1_Byte(Zero_Code);
dat <<= 1; // 左移一位,準備發送下一位
}
//Red
dat = Red;
for(i = 0; i < 8; i++){
if(dat & 0x80) SPI_Send_1_Byte(High_Code);
else SPI_Send_1_Byte(Zero_Code);
dat <<= 1;
}
//Blue
dat = Blue;
for(i = 0; i < 8; i++){
if(dat & 0x80) SPI_Send_1_Byte(High_Code);
else SPI_Send_1_Byte(Zero_Code);
dat <<= 1;
}
}
WS2812_Lightup();
}
void WS2812_Color_Encoder(unsigned char Red,unsigned char Green,unsigned char Blue){
}
void WS2812_Test(){
static unsigned char phase = 0;
unsigned int i = 65535;
static unsigned char r,g,b;
while(i--);
if (phase < 85) {
// 紅 -> 綠
r = 255 - (phase * 3); // 255 → 0
g = phase * 3; // 0 → 255
b = 0;
} else if (phase < 170) {
// 綠 -> 藍
unsigned char t = phase - 85;
r = 0;
g = 255 - (t * 3); // 255 → 0
b = t * 3; // 0 → 255
} else {
// 藍 -> 紅
unsigned char t = phase - 170;
r = t * 3; // 0 → 255
g = 0;
b = 255 - (t * 3); // 255 → 0
}
phase++;
WS2812_Send_Data(10,r,g,b);
}
void main(){
WS2812_Init(54);
while(1){
//WS2812_Send_Data(10,44,10,96);//RGB
WS2812_Test();
}
}
|
評分
-
查看全部評分
|