STM32 SPI+DMA驱动WS2812

【举报再看养成习惯,噢 不对,点赞再看养成习惯。感谢支持】

开头不多叨叨,直接进入主题:

WS2812的驱动原理:

首先明白高低电平的表示方法:

低电平(0 code): 0.35us的高电平+0.8us的低电平

高电平(1 code):0.7us的高电平+0.6us的低电平

之前有一期使用PWM+DMA 使用Dshot协议驱动电调的文章,里面表示高低电平也是这种方式。

==================================================================

然后看一下数据包怎么发送到每个灯珠:D1、D2、D3、D4

        1、红框左侧是第一包数据,我们先看D1拿到了一包3*24的数据,然后自己留下first 24bit。

然后将剩下的2*24bit传给D2D2留下second 24bit,将最后24bit传给D3D4没有数据可拿,就不会亮。

        2、然后中间间隔>=50us后,认为是第二包数据。

        3、所以原理类似于:从第一排往后传卷子,一人留一张。【来自上学时的恐惧】

==========================分割线========================

所以我们就需要将每颗灯珠需要的24bit,按照它要求的高低电平的表示方式传输即可。如果你看过我之前写的Dshot驱动电调的文章,那么可以利用PWM+DMA组成数据包发送。今天我选择使用另一种方式:SPI+DMA模拟灯珠需要的信号。

首先解决SPI发送要求的高低电平的问题:

 我们只需要让SPI_MOSI发送引脚输出一个0xE0,就可以模拟0 code。

发送0xF8模拟 1 code。

举个例子:如果要发送R:0x80    G:0x08     B:0x11.  即发送

1000 0000           0000 1000         0001 0001

0xE0代替00xF8代替1,那么实际输出:(低位往后放、高位往前放)

 0xE0 0xE0 0xE0 0xE0      0xE0 0xE0 0xE0 0xF8 

0xE0 0xE0 0xE0 0xF8      0xE0 0xE0 0xE0  0xE0

 0xF8  0xE0 0xE0  0xE0    0xF8  0xE0 0xE0  0xE0

 ============================================================

再来解决发送频率的问题:

从上面我们可以知道: 最短需要0.85us发出一个 0 Code(低电平)。最长可以1.45us

SPI模拟一个0 code要发出0xE0,即8个bit。

拿低电平来计算:  

        最快:单个bit耗时:0.85/8    频率:1/0.85*89.41MHz

        最慢:单个bit耗时:1.45/8    频率:1/1.45*8≈5.52MHz

所以我们的SPI通信频率只要保持在这个区间内,应该就没问题。

===========================================================

上CubeMx配置图:

因为我们只需要使用SPI的发送引脚,所以选择只发送模式。

 打开DMA模式。配置好之后就可以生成代码了。

==============================================================

核心代码:

// GRB格式

/*************************************************************
** Function name:       CreatData
** Descriptions:        组合数据 并拷贝到BUFF中对应的位置
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
static void CreatData(PWS2812_Struct pWs2812, uint8_t index, uint8_t R,uint8_t G,uint8_t B){
    // 先组合成8*3个字节的数据
    uint8_t temp[24] = {0};
    for (uint8_t i=0;i<8;i++){
        temp[7-i] =  (G & 0x01) ? WS_BIT_1 : WS_BIT_0; 
        G = G >> 1;
    }
    for (uint8_t i=0;i<8;i++){
        temp[15-i] =  (R & 0x01) ? WS_BIT_1 : WS_BIT_0; 
        R = R >> 1;
    }
    for (uint8_t i=0;i<8;i++){
        temp[23-i] =  (B & 0x01) ? WS_BIT_1 : WS_BIT_0; 
        B = B >> 1;
    }
    // 拷贝到对应的Buff中
    memcpy(&pWs2812->sendBuff[index*24], temp, 24);
}

 我们需要填入RGB的参数来输出对应的数据包,利用位运算即可。

完整代码:

【.c文件】

#include "ws2812Frame.h"

uint32_t gWS2812_TimeCNT;

// GRB格式

/*************************************************************
** Function name:       WS2812SendMassge
** Descriptions:        通过SPI发送WS2812的数据
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void WS2812SendMassge(uint8_t *sendBuff, uint16_t sendSize){

    HAL_SPI_Transmit_DMA(&hspi1, sendBuff, sendSize);

}


/*************************************************************
** Function name:       CreatData
** Descriptions:        组合数据 并拷贝到BUFF中对应的位置
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
static void CreatData(PWS2812_Struct pWs2812, uint8_t index, uint8_t R,uint8_t G,uint8_t B){
    // 先组合成8*3个字节的数据
    uint8_t temp[24] = {0};
    for (uint8_t i=0;i<8;i++){
        temp[7-i] =  (G & 0x01) ? WS_BIT_1 : WS_BIT_0; 
        G = G >> 1;
    }
    for (uint8_t i=0;i<8;i++){
        temp[15-i] =  (R & 0x01) ? WS_BIT_1 : WS_BIT_0; 
        R = R >> 1;
    }
    for (uint8_t i=0;i<8;i++){
        temp[23-i] =  (B & 0x01) ? WS_BIT_1 : WS_BIT_0; 
        B = B >> 1;
    }
    // 拷贝到对应的Buff中
    memcpy(&pWs2812->sendBuff[index*24], temp, 24);
}

/*************************************************************
** Function name:       SetWSColor
** Descriptions:        设置WS2812颜色
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void SetWSColor(PWS2812_Struct pWs2812, uint8_t index, uint8_t R,uint8_t G,uint8_t B){
    CreatData(pWs2812,index,R,G,B);
}


/*************************************************************
** Function name:       ClearAllColor
** Descriptions:        清除所有颜色(关闭灯光)
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void ClearAllColor(PWS2812_Struct pWs2812){
    for (uint8_t i=0;i<pWs2812->num;i++){
        CreatData(pWs2812,i,0,0,0);
    }
}


/*************************************************************
** Function name:       ClearIndexColor
** Descriptions:        清除指定WS2812的颜色
** Input parameters:    index : 灯珠的序号  从0开始
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void ClearIndexColor(PWS2812_Struct pWs2812,uint8_t index){
    if (index >= pWs2812->num){
        return ;
    }
    CreatData(pWs2812,index,0,0,0);
}


/*************************************************************
** Function name:       WS2812SendDataLoop
** Descriptions:        WS2812数据发送函数,需要放到循环中
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
** Remarks:             None
*************************************************************/
void WS2812SendDataLoop(PWS2812_Struct ws2812){
    static uint32_t startTime = 0;
    if  ( WS2812_TIMEOUT(10,startTime) ){
        // 这里会指向SPI的发送函数  最上面的第一个函数
        ws2812->WS2812DataTransmit(ws2812->sendBuff, ws2812->num * WS_DATALENGTH);
        startTime = WS2812_GETTIME();
    }
}

【.h文件】


#ifndef ws2812Frame_h
#define ws2812Frame_h

#include "main.h"
#include "stdint.h"
#include "string.h"

#define WS_BIT_1            0xF8
#define WS_BIT_0            0xE0
#define WS_RESET            0x00

#define WS_DATALENGTH          24

struct SWS2812_Struct {
    void (*WS2812DataTransmit)(uint8_t *pData, uint16_t dataSize);
    uint8_t num;
    uint8_t index;
    uint8_t *sendBuff;
};

typedef struct SWS2812_Struct WS2812_Struct;
typedef WS2812_Struct *PWS2812_Struct;

#define WS2812_INIT(name,xNum,xSendBuff,xWS2812DataTransmit)      \
WS2812_Struct name = {                                      \
    .num = xNum,                                            \
    .index = 0,                                             \
    .sendBuff = xSendBuff,                                   \
    .WS2812DataTransmit = xWS2812DataTransmit,                  \
};

/*************************************************************
** Function name:       WS2812_TIMEBASE
** Descriptions:        时基,放在周期为1ms的函数里面执行
** Input parameters:    None
** Output parameters:   None
** Returned value:      None
*************************************************************/
#define WS2812_TIMEBASE(ms)                \
        gWS2812_TimeCNT+= ms

/*************************************************************
** Function name:       WS2812_GETTIME
** Descriptions:        获取起始时间
** Input parameters:    None
** Output parameters:   None
** Returned value:      (uint32_t)起始时间
*************************************************************/
#define WS2812_GETTIME(void)                        \
    gWS2812_TimeCNT

/*************************************************************
** Function name:       WS2812_TIMEOUT
** Descriptions:        检查超时
** Input parameters:    timeOut:(uint32_t)超时时间
**                      startTime:(uint32_t)开始的时间
** Output parameters:   None
** Returned value:      false,未超时,true,超时
*************************************************************/
#define WS2812_TIMEOUT(timeOut,startTime)                \
    ((gWS2812_TimeCNT - startTime) >= timeOut ? 1 : 0)


void SetWSColor(PWS2812_Struct pWs2812, uint8_t index, uint8_t R,uint8_t G,uint8_t B);
void ClearAllColor(PWS2812_Struct pWs2812);
void ClearIndexColor(PWS2812_Struct pWs2812,uint8_t index);
void WS2812SendDataLoop(PWS2812_Struct ws2812);

extern uint32_t gWS2812_TimeCNT;


#endif /* ws2812Frame_h */

最后使用SetWSColor函数成功点亮!

上图:

  • 36
    点赞
  • 128
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值