找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 24|回复: 0
打印 上一主题 下一主题
收起左侧

STC8G/H通用 SPI驱动WS2812 SPI模拟单总线

[复制链接]
跳转到指定楼层
楼主
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();
    }
}

IMG_20260223_195306.jpg (2.86 MB, 下载次数: 0)

IMG_20260223_195306.jpg

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表