陈家乐 发表于 2025-1-12 23:36:18

【SPI操纵WS2812】基于擎天柱的RGB驱动方案,纯走硬件,杜绝软件延时卡顿占用空间

/****************************************************************
本次实验的SPI分组为P14P15P16P17
                                                                       SSMOSI MISOSCK
请将RGB的DIN引脚接到AI8051U的P15引脚进行实验

重头戏在时钟配置函数(Sys.c中)将SPI转到高速SPI达到了刚好的3.2MHZ,
因为高速SPI(HSPI)通过PLL锁相环时钟,输出96MHZ给高速IO时钟,然后高速
SPI的时钟源是高速IO时钟,然后经过15分频后,在SPI配置中又进行了2分频,
所以最后的SPI工作频率是15分频和2分频叠加成了30MHZ,然后就有了SPI时钟
频率为:96/15/2=3.2MHZ,这个是非常合适的时钟频率,接下来就是考虑RGB的
1个时序该多久了,WS2812的时序是800K码率,也就是1个时序需要1250ns的时
间,那么0码是1/4的高电平和3/4的低电平;1码是3/4高电平和1/4低电平.那么
就是总共是1250ns,1/4时长是312.5ns,3/4时长是937.5ns,那我是不是SPI写
1个位就是(1000000/3.2MHZ)=312.5ns,刚好是1/4的占空比,那么我SPI写3个
位就是937.5ns了,通过这个原理,我们就可以通过SPI写4个位来实现0码/1码,
那么SPI一次性写8个位(1个字节)就是可以写2个0码/1码了
那么我们将G、R、B分别提取出来,8位的数据我们1次SPI的操作就可以判断2
位该写0还是写1,这样子1次SPI操作就可以写掉2位的RGB值了,1个颜色用4次
SPI操作来写,那么SPI的字节数总共就是4*3=12了

至于高速SPI,需要将SPI的输出口(MOSI和SCK)引脚设置为高速模式,在本次实
验中,需要将P14 P15 P17加内部上拉电阻,P16加内部下拉电阻,并且设置P15
和P17为推挽输出,P16为高阻输入+下拉电阻,这样MOSI输出完成后才会是低
电平

至此,龙场悟道!!!!

****************************************************************/

DebugLab 发表于 2025-1-13 10:05:23

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

#define                FOSC                        40000000UL                                        //主时钟

unsigned char Poi;

//void delay_ms(unsigned char ms)
//{
//        unsigned int i;
//        do
//        {
//                i=FOSC/6000;
//                while(--i);        //6T per loop
//        }
//        while(--ms);
//}

void SPI_T(unsigned char x)
{
        unsigned char temp;
        temp=(SBUF<<(x*2))&0xC0;
        SPDAT=((temp&0x80)?0xE0:0x80)|((temp&0x40)?0x0E:0x08);
}

void Init(void)
{
        WTST=0;                                        //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
        EAXFR=1;                                //扩展寄存器(XFR)访问打开
        CKCON=0;                                //提高访问XRAM速度
        IAP_TPS=40;
        IRCDB=128;
       
        P0M1=0x00;        P0M0=0x00;        //设置为准双向口
        P1M1=0x00;        P1M0=0x00;        //设置为准双向口
        P2M1=0x00;        P2M0=0x00;        //设置为准双向口
        P3M1=0x00;        P3M0=0x00;        //设置为准双向口
        P4M1=0x00;        P4M0=0x00;        //设置为准双向口
        P5M1=0x00;        P5M0=0x00;        //设置为准双向口
        P6M1=0x00;        P6M0=0x00;        //设置为准双向口
        P7M1=0x00;        P7M0=0x00;        //设置为准双向口
       
        S1_S1=0;                //UART1_2
        S1_S0=1;                //UART1_2
        S1BRT=0;                //选择TMR1作为UART1波特率发生器
        T1x12=1;                //设置TMR1为1T模式
        TH1=0xFF;                //设置UART1波特率(250Kbps@40MHz)
        TL1=0xD8;                //设置UART1波特率(250Kbps@40MHz)
        SMOD0=1;                //打开帧错误检测
        SM1=1;                        //设置UART1模式为9位数据可变波特率
        REN=1;                        //允许UART1接收数据
        TR1=1;                        //打开定时器1
        ES=1;                        //打开UART1中断
       
        SPI_S1=1;                //SPI_3
        SPI_S0=0;                //SPI_3
        SPI_CLKDIV=3;        //SPI时钟3分频
        SPR1=1;                        //SPI输入时钟/2
        SPR0=1;                        //SPI输入时钟/2
        SSIG=1;                        //忽略SS引脚功能,使用MSTR确定器件是主机还是从机
        DORD=0;                        //先发送/接收数据的高位(MSB)
        MSTR=1;                        //设置主机模式
        CPOL=0;                        //SCLK空闲时为低电平,SCLK的前时钟沿为上升沿,后时钟沿为下降沿
        CPHA=1;                        //数据在SCLK的前时钟沿驱动,后时钟沿采样
        SPSTAT=0xC0;        //清除SPI中断标志、清除SPI写冲突标志
        SPEN=1;                        //打开SPI功能
        ESPI=1;                        //打开SPI中断
       
        EA=1;
}

void main(void)
{
        Init();
        while(1)
        {
               
        }
}

void UART1_Isr(void) interrupt UART1_VECTOR
{
        bit sync;
        if(RI)
        {
                RI=0;
                if(SM0)
                {
                        SM0=0;
                        sync=1;
                }
                else
                {
                        if(sync)
                        {
                                sync=0;
                        }
                        else
                        {
                                Poi=0;
                                SPI_T(Poi);
                        }
                }
        }
        if(TI)
        {
                TI=0;
        }
}

void SPI_Isr(void) interrupt SPI_VECTOR
{
        SPSTAT|=0x80;
        if(Poi<3)
        {
                SPI_T(++Poi);
        }
}

DebugLab 发表于 2025-1-13 10:07:34

DebugLab 发表于 2025-1-13 10:05


我直接用40M分频
40/12=3.3333333

陈家乐 发表于 2025-1-13 13:50:59

DebugLab 发表于 2025-1-13 08:07
我直接用40M分频
40/12=3.3333333

欧欧欧大佬还是牛,我不确定是不是SPI_CLKDIV可以控制普通SPI,因为那里写的是高速SPI分频,所以我还得启用高速SPI

陈家乐 发表于 2025-1-13 13:51:41

DebugLab 发表于 2025-1-13 08:07
我直接用40M分频
40/12=3.3333333

就是这玩意吧挺严格的,分频不好分,要不就是直接,48/15=3.2MHZ

陈家乐 发表于 2025-1-13 13:52:21

DebugLab 发表于 2025-1-13 08:07
我直接用40M分频
40/12=3.3333333

但是主频无法跟STC32F比,那个主频比较低,这个分频也是不好分,STC单片机也没有倍频器,所以很难搞

DebugLab 发表于 2025-1-13 14:13:13

陈家乐 发表于 2025-1-13 13:51
就是这玩意吧挺严格的,分频不好分,要不就是直接,48/15=3.2MHZ

8051U跑不了48M

DebugLab 发表于 2025-1-13 14:15:11

陈家乐 发表于 2025-1-13 13:50
欧欧欧大佬还是牛,我不确定是不是SPI_CLKDIV可以控制普通SPI,因为那里写的是高速SPI分频,所以我还得启 ...


陈家乐 发表于 2025-1-13 14:44:43

DebugLab 发表于 2025-1-13 12:13
8051U跑不了48M

最高42MHZ,有时候限制了发挥,还是得高主频才不用掉头发

陈家乐 发表于 2025-1-13 14:45:14

DebugLab 发表于 2025-1-13 12:15


以前的SPI我拿来就用,现在折腾了这个时钟,理解更深刻了
页: [1] 2
查看完整版本: 【SPI操纵WS2812】基于擎天柱的RGB驱动方案,纯走硬件,杜绝软件延时卡顿占用空间