|
|
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();
}
}
|
评分
-
查看全部评分
|