IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32 SPI+DMA驱动WS2812 -> 正文阅读

[嵌入式]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[i] =  (G & 0x01) ? WS_BIT_1 : WS_BIT_0; 
        G = G >> 1;
    }
    for (uint8_t i=0;i<8;i++){
        temp[i+8] =  (R & 0x01) ? WS_BIT_1 : WS_BIT_0; 
        R = R >> 1;
    }
    for (uint8_t i=0;i<8;i++){
        temp[i+16] =  (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[i] =  (G & 0x01) ? WS_BIT_1 : WS_BIT_0; 
        G = G >> 1;
    }
    for (uint8_t i=0;i<8;i++){
        temp[i+8] =  (R & 0x01) ? WS_BIT_1 : WS_BIT_0; 
        R = R >> 1;
    }
    for (uint8_t i=0;i<8;i++){
        temp[i+16] =  (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函数成功点亮!

上图:

?

?

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-09-08 10:53:52  更:2021-09-08 10:55:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 0:28:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码