找回密码
 立即注册
查看: 6157|回复: 33

DMA_SPI 驱动 WS2812B 全彩LED 的简单应用 | 梁工是说不用加反向器 ?

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:5
  • 最近打卡:2024-08-17 16:41:35

16

主题

25

回帖

270

积分

中级会员

积分
270
发表于 2023-1-3 00:02:19 | 显示全部楼层 |阅读模式
DMA_SPI, STC8H/STC32G, 驱动WS2812B全彩LED的简单应用
一位新人的工作之余琢磨的,供大家参考
芯片:STC8H4K64TLCD
功能:利用 DMA_SPI 驱动WS2812B(5颗)
限制:10颗以内,主频固定28Mhz
优点:无需反向器
硬件:第一颗WS2812B, DI接MOIS脚,(我接的是P3.4),

          MOSI 接10K下拉电阻
程序:
/*******我也不知道为什么丢这么多头文件*************/
#include<STC8H.H>
#include<absacc.h>
#include<stdio.h>
#include<math.h>
#include <intrins.h>
#include <string.h>

/**********WS2812B数据,0XF0对应1,0X00对应0************************/
unsigned char xdata GRB[96]={ 0xF0,0xF0,0xF0,0xF0, 0xF0,0xF0,0xF0,0xF0,     0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,      0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,     // 绿色  对应WS2812数据:1111 1111 0000 0000 0000 0000
                                               0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,     0xF0,0xF0,0xF0,0xF0, 0xF0,0xF0,0xF0,0xF0,      0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,    // 红色  对应WS2812数据: 0000 0000 1111 1111 0000 0000
                                               0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,     0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,      0xF0,0xF0,0xF0,0xF0, 0xF0,0xF0,0xF0,0xF0,    //蓝色   对应WS2812数据:0000 0000 0000 0000  1111 1111
                                               0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,     0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,      0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,};   //全灭  对应WS2812数据:0000 0000 0000 0000 0000 0000
                                                                                                                                       
unsigned char xdata LED_DATA[120]={0xF0,0xF0,0xF0,0xF0, 0xF0,0xF0,0xF0,0xF0, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,   0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,      //0-23对应第一颗24bit数据,初始绿色
                                                        0xF0,0xF0,0xF0,0xF0, 0xF0,0xF0,0xF0,0xF0, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,   0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,       //24-47对应第二颗24bit数据,初始绿色
                                                        0xF0,0xF0,0xF0,0xF0, 0xF0,0xF0,0xF0,0xF0, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,   0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,        //48-71 对应第三颗24bit数据,初始绿色
                                                        0xF0,0xF0,0xF0,0xF0, 0xF0,0xF0,0xF0,0xF0, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,   0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,        //72-95 对应第四颗24bit数据,初始绿色
                                                        0xF0,0xF0,0xF0,0xF0, 0xF0,0xF0,0xF0,0xF0, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,   0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,};         //96-120 对应第五颗24bit数据,初始绿色


int xdata Di=0;       //定义LED_DATA数据位,0 第一颗LED数据第一位,24第二颗数据第一位,依次按24倍数
int xdata Yrgb=0;    //定义GRB数据位,0 绿色数据第一位 ,24红色数据第一位,48蓝色数据第一位,72关闭LED数据第一位         
int xdata ENSPI=0;    //DMA_SPI中断标志位


/**********延时 80us用于WS2812B置零,实际运用中可考虑其他方式 1000MS方便观察现象************/


void Delay80us()                //@28MHz
{
        unsigned char i, j;

        _nop_();
        i = 3;
        j = 230;
        do
        {
                while (--j);
        } while (--i);
}



void Delay1000ms()                //@28MHz
{
        unsigned char i, j, k;

        _nop_();
        _nop_();
        i = 143;
        j = 12;
        k = 64;
        do
        {
                do
                {
                        while (--k);
                } while (--j);
        } while (--i);
}





/******SW2812B数据*****************/
void LEDRGB(Di,Yrgb)
{
memmove(LED_DATA+Di,GRB+Yrgb,24);       //从GRB第Yrgb+1位复制到LED_DATA的Di+1位,一共复制24个字符
}


/**********DMA_SPI**************************/

void SPI()
{
  ENSPI=DMA_SPI_CR; //读DMA_SPI控制寄存器
  ENSPI&=0xC0;          //取第7.6位   
if(ENSPI==0)              //0,说明DMA_SPI未使用,
  {
        P34=0;                                //MOSI低电平        
        Delay80us();                          //延时WS2812B置零,开始接收数据,                        
        DMA_SPI_AMT=119;            //传输120字节。24*5=120字节
        DMA_SPI_TXA=LED_DATA;   //数据起始位。
        SPCTL=0xD3;                       //1101 0011  6.使能SPI  5主机模式,4空闲时钟线低电平,3前沿写 2后沿读 1:0主时钟2分频 14Mhz”                        
        DMA_SPI_CR=0xC0;             //11000000 开始DMA_SPI
  }        
}

//DM_SPI中断
void DMA_SPI_Routine(void)  interrupt 49
{
SPCTL=0x00;
P34=0;                         //WS2812B数据空闲低电平
DMA_SPI_CR=0x00;    //关闭DMA_SPI
DMA_SPI_STA=0x00;  //中断标志位清零        
}



void main()
{
CLKSEL=0;  //内部高速时钟 下载设置25.6MHZ
EA=1; //打开总中断
//------------------I/O设置,MOIS设置为推挽输出,设置为准双向口时,实测高电平为一斜线----------
P0M0 = 0x00; P0M1 = 0x00;
P1M0 = 0x00; P1M1 = 0x00;
P2M0 = 0x00; P2M1 = 0x7f;
P3M0 = 0x10; P3M1 = 0x00;
P34=0;        
P4M0 = 0x00; P4M1 = 0x00;
P5M0 = 0x00; P5M1 = 0x00;
//---------------------SPI设置-----------------------------------------
P_SW2|=128;         //1000 0000 允许访问片内SFR  0FA00H~0FFFFH  FB50-FB6F 触摸阈值寄存器        
P_SW1=12;             //00-- **0- MOSI输出设置P3.4 **=11
AUXR=0;            //允许访问内部扩展RAM        
DMA_SPI_CFG2=4;    //00000100 不使用SS脚 SS脚P1.2”
DMA_SPI_CFG=0xCF;    //1100 1111,允许DMA_SPI中断,允许发送禁止接收数据,中断优先最高 访问级最大        

while(1)
{
//-----------全部显示绿色-----------
Delay1000ms();
LEDRGB(0,0);     //从GRB第1个字符开始,依次复制24个字符复制到LED_DATA的第1-24字符,第一颗LED数据
LEDRGB(24,0);   //从GRB第1个字符开始,依次复制24个字符复制到LED_DATA的第25-48字符,第二颗LED数据
LEDRGB(48,0);   //从GRB第1个字符开始,依次复制24个字符复制到LED_DATA的第49-72字符,第三颗LED数据               
LEDRGB(72,0);   //从GRB第1个字符开始,依次复制24个字符复制到LED_DATA的第73-96字符,第三颗LED数据        
LEDRGB(96,0);   //从GRB第1个字符开始,依次复制24个字符复制到LED_DATA的第97-120字符,第三颗LED数据        
SPI();
//-----------全部显示红色-----------

Delay1000ms();
LEDRGB(0,24);   
LEDRGB(24,24);  
LEDRGB(48,24);                           
LEDRGB(72,24);
LEDRGB(96,24);
SPI();

//-----------全部显示蓝色-----------
Delay1000ms();
LEDRGB(0,48);   
LEDRGB(24,48);  
LEDRGB(48,48);                           
LEDRGB(72,48);
LEDRGB(96,48);        
SPI();
//-----------全部关闭-----------
Delay1000ms();
LEDRGB(0,72);   
LEDRGB(24,72);  
LEDRGB(48,72);                           
LEDRGB(72,72);
LEDRGB(96,72);        
SPI();        

}
}


思路:
DMA_SPI在进行数据传输时,2个字符直接有间隙,间隙期间MOSI为高电平,
而WS2812B判断1与0是依据高电平时间,间隙时长与主频有关,40Mhz时候,
间隙时长约280nS,28Mhz间隙时长约400nS.
         

如果增加反相器,可以无需考虑这段时长,因为我的PCB已经成型不方便增加反相器
所以利用2个字符的间隙高电平时间,让WS2812B判断为0,通过字符高位设1延长
高电平时间让WS2812B判断为1。

最后,强烈建议使用DMA_SPI驱动WS2812B时候,MOSI脚增加反相器。
小白一枚,如果有什么错误,欢迎指出,谢谢


DMA_SPI 字符 间隙

波形.jpg

主频28MHz MOSI 波形
波形1.jpg

1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:5
  • 最近打卡:2024-08-17 16:41:35

16

主题

25

回帖

270

积分

中级会员

积分
270
发表于 2023-1-3 00:04:48 | 显示全部楼层

上面测试,没有增加反向器
另外,无论采取什么方式,DMA_SPI数据传输完成后,MOSI要有大于50uS的低电平时间。
  • 打卡等级:偶尔看看I
  • 打卡总天数:12
  • 最近打卡:2025-04-27 09:44:17

25

主题

252

回帖

2089

积分

超级版主

积分
2089
发表于 2023-1-5 10:45:27 | 显示全部楼层
感谢应用的分享   
QQ:3398500488
微信号:18106296592(小刘)
  • 打卡等级:偶尔看看III
  • 打卡总天数:50
  • 最近打卡:2025-04-30 22:59:03

73

主题

5882

回帖

1万

积分

超级版主

积分
12073
发表于 2023-1-5 11:21:29 | 显示全部楼层
WS2812S的标准时序如下:
TH+TL = 1.25us±150ns, RES>50us
T0H = 0.25us±150ns = 0.10us - 0.40us
T0L = 1.00us±150ns = 0.85us - 1.15us
T1H = 1.00us±150ns = 0.85us - 1.15us
T1L = 0.25us±150ns = 0.10us - 0.40us
两个位数据之间的间隔要小于RES的50us.

SPI 传输, 速度3~3.3Mbps,

每个字节高4位低4位分别对应一个位数据,
1000为数据0, 1110为数据1, 使用 DMA-SPI 传输.

  • 打卡等级:初来乍到
  • 打卡总天数:5
  • 最近打卡:2024-08-17 16:41:35

16

主题

25

回帖

270

积分

中级会员

积分
270
发表于 2023-1-5 15:30:16 | 显示全部楼层
多次提升,反向还是增加外部反向芯片比较稳定,调整如下
频率32Mhz  SPI 4分频
/*******我也不知道为什么丢这么多头文件*************/

#include<STC8H.H>
#include<absacc.h>
#include<stdio.h>
#include<math.h>
#include <intrins.h>
#include <string.h>


/**********WS2812B数据,0XC0对应1,0XFC对应0************************/


unsigned char xdata GRB[72]={ 0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,    0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,    0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,      
                                              0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,    0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,    0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,      
                                              0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,    0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,    0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,};
                                                                                                                                       
unsigned char xdata LED_DATA[120]={0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,    0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,     0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,   
                                                         0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,    0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,     0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,   
                                                         0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,    0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,     0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,  
                                                         0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,    0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,     0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,  
                                                         0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,    0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,     0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,  };        



int xdata Di=0;       //定义LED_DATA数据位,0 第一颗LED数据第一位,24第二颗数据第一位,依次按24倍数
int xdata Yrgb=0;    //定义GRB数据位,0 绿色数据第一位 ,24红色数据第一位,48蓝色数据第一位,72关闭LED数据第一位        
int xdata SPIIF=0;    //DMA_SPI中断标志位,采用查询方式


/**********延时 80us用于WS2812B置零,实际运用中可考虑其他方式,500MS方便观察现象************/

void Delay80us()                //@32MHz
{
        unsigned char i, j;

        _nop_();
        i = 4;
        j = 80;
        do
        {
                while (--j);
        } while (--i);
}



void Delay500ms()                //@32MHz
{
        unsigned char i, j, k;

        _nop_();
        i = 82;
        j = 43;
        k = 255;
        do
        {
                do
                {
                        while (--k);
                } while (--j);
        } while (--i);
}





/******SW2812B数据*****************/
void LEDRGB(Di,Yrgb)
{
memmove(LED_DATA+Di,GRB+Yrgb,24);       //从GRB第Yrgb+1位复制到LED_DATA的Di+1位,一共复制24个字符
}



void SPI()
{

  Delay80us();                 //延时WS2812B置零,开始接收数据               
        DMA_SPI_AMT=119;   //传输120字节。24*5=120字节
        DMA_SPI_TXA=LED_DATA;  //数据起始位。               
        DMA_SPI_CR=0xC0;    //11000000 开始DMA_SPI
        while(SPIIF==0){SPIIF=DMA_SPI_STA;SPIIF&=0x01;}   //采用查询DMA_SPI中断标志位的方式
        SPIIF=0;
        DMA_SPI_CR=0x00;
       DMA_SPI_STA=0x00;  //中断标志位清零        

}
/***********显示不同颜色************/
void LED1_G(){LEDRGB(0, 0);}
void LED1_R(){LEDRGB(0, 28);}
void LED1_B(){LEDRGB(0, 48);}
void LED2_G(){LEDRGB(24,0);}
void LED2_R(){LEDRGB(24,24);}
void LED2_B(){LEDRGB(24,48);}
void LED3_G(){LEDRGB(48,0);}
void LED3_R(){LEDRGB(48,24);}
void LED3_B(){LEDRGB(48,48);}
void LED4_G(){LEDRGB(72,0);}
void LED4_R(){LEDRGB(72,24);}
void LED4_B(){LEDRGB(72,48);}
void LED5_G(){LEDRGB(96,0);}
void LED5_R(){LEDRGB(96,24);}
void LED5_B(){LEDRGB(96,48);}

/************主函数**************/
void main()
{

CLKSEL=0;  //内部高速时钟
P0M0 = 0x00; P0M1 = 0x00;
P1M0 = 0x00; P1M1 = 0x00;
P2M0 = 0x00; P2M1 = 0x00;
P3M0 = 0x10; P3M1 = 0x00;  //MOSI端口推挽输出
P4M0 = 0x00; P4M1 = 0x00;
P5M0 = 0x00; P5M1 = 0x00;
//---------------------SPI设置-----------------------------------------
P_SW2|=128;              //1000 0000 允许访问片内SFR  0FA00H~0FFFFH  FB50-FB6F 触摸阈值寄存器        
P_SW1=12;                 //00-- **0- MOSI输出设置P3.4 **=11
AUXR=0;                     //允许访问内部扩展RAM        
DMA_SPI_CFG2=4;       //00000100 不使用SS脚 SS脚P1.2”
DMA_SPI_CFG=0xCF;    //1100 1111,允许DMA_SPI中断,允许发送禁止接收数据,中断优先最高 访问级最大        
SPCTL=0xD0;               //1101 0000  6.使能SPI  5主机模式,4空闲时钟线低电平,3前沿写 2后沿读 1:0主时钟4分频 8Mhz”               

while(1)
{
LED1_R();        SPI();Delay500ms();
LED1_G();        SPI();Delay500ms();
LED1_B();        SPI();Delay500ms();
LED2_R();        SPI();Delay500ms();
LED2_G();        SPI();Delay500ms();
LED2_B();        SPI();Delay500ms();
LED3_R();        SPI();Delay500ms();
LED3_G();        SPI();Delay500ms();
LED3_B();        SPI();Delay500ms();
LED4_R();        SPI();Delay500ms();
LED4_G();        SPI();Delay500ms();
LED4_B();        SPI();Delay500ms();
LED5_R();        SPI();Delay500ms();
LED5_G();        SPI();Delay500ms();
LED5_B();        SPI();Delay500ms();
}
}


效果(因未设置LED熄灭数据,所以未操作LED最终是蓝色):
波形图:周期约1.35us
反向波形.jpg

反相器电路图
从坏液晶电路板上找了个三态输出缓冲器,在PCB上搭了个反相器。(丝印C255)



反向电路.png



1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2024-01-23 19:43:10

0

主题

17

回帖

134

积分

注册会员

积分
134
发表于 2023-8-12 17:39:30 | 显示全部楼层
您好,能给一份硬件的原理图嘛
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:5
  • 最近打卡:2024-08-17 16:41:35

16

主题

25

回帖

270

积分

中级会员

积分
270
发表于 2023-8-30 14:19:47 | 显示全部楼层
Hs*** 发表于 2023-8-12 17:39
您好,能给一份硬件的原理图嘛

就是芯片的随便一个MOSI 脚接SN74LVC1G125的OE脚,然后程序中定义该MOSI脚为推挽输出。
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:2
  • 最近打卡:2023-12-06 15:33:02

20

主题

110

回帖

771

积分

荣誉版主

积分
771
发表于 2023-9-27 15:23:48 | 显示全部楼层
梁*** 发表于 2023-1-5 11:21
WS2812S的标准时序如下:
TH+TL = 1.25us±150ns, RES>50us
T0H = 0.25us±150ns = 0.10us - 0.40us

我用stc32f也遇到同样问题,关键是stc的spi_dma不支持连续模式,mosi管脚在两个数据之间是好几百nm的高电平,这就导致时序不能满足。想要解决如楼主所说只能反相了。或者说梁工能有更简便的方法

点评

没有问题,用双缓冲,一个缓冲正在传输,另一个缓冲准备数据,传输一个缓冲完成,DMA中断,切换一个缓冲,几个us就好了。  详情 回复 发表于 2023-9-27 17:54
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:50
  • 最近打卡:2025-04-30 22:59:03

73

主题

5882

回帖

1万

积分

超级版主

积分
12073
发表于 2023-9-27 17:54:11 | 显示全部楼层
ch*** 发表于 2023-9-27 15:23
我用stc32f也遇到同样问题,关键是stc的spi_dma不支持连续模式,mosi管脚在两个数据之间是好几百nm的高电 ...

没有问题,用双缓冲,一个缓冲正在传输,另一个缓冲准备数据,传输一个缓冲完成,
DMA中断,切换一个缓冲,几个uS就好了。
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:2
  • 最近打卡:2023-12-06 15:33:02

20

主题

110

回帖

771

积分

荣誉版主

积分
771
发表于 2023-9-27 20:27:35 | 显示全部楼层
梁*** 发表于 2023-9-27 17:54
没有问题,用双缓冲,一个缓冲正在传输,另一个缓冲准备数据,传输一个缓冲完成,DMA中断,切换一个缓冲 ...

梁工你误解我的意思了,这个不是spi传输快慢的问题,dma+spi速度确实很快,但是要驱动WS2812不是光有速度就行,要让时序满足。stc的spi不支持连续模式就导致两个字节之间有几百纳秒的间隔,spi常规应用这不到0.5us的间隔无所谓,但是用spi驱动2812这种时序敏感的非spi器件就有问题了。
今天我试出MOSI脚位初始化成0的时候,间隔的时候MOSI脚为低电平。具体等明天我驱动一下看有没有问题。

题外话,WS2812这种全彩灯珠用的还是非常多的,STC是不是哪天把这个驱动集成到里面

WS2812这种nm级别的时序,简单点就是看着示波器加nop,这种方式如果只驱动灯的话没问题,实际上一言难尽。基本上现在都是靠DMA+SPI取巧或者DMA+PWM。STC貌似做不到DMA+PWM,想要不占CPU资源貌似只能通过SPI了。

点评

我不知道你是否实际驱动过2812. 2812传输信号时,会检测高电平的时间,T1H = 1.00us±150ns,T0H = 0.25us±150ns。 低电平时间小于50us就不会复位,正常传输。 我不知道你担心的是什么。  详情 回复 发表于 2023-9-28 00:54
回复 支持 反对

使用道具 举报 送花

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

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-2 02:36 , Processed in 0.239665 second(s), 119 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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