打印
[应用相关]

SPI+DMA驱动WS2812B

[复制链接]
1103|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wiba|  楼主 | 2023-8-15 08:36 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本次主要参考:
https://blog.51cto.com/xfxuezhang/5873175

MCU:STM32F411CEU6,主频96M
外设:SPI2(引脚为PB12、PB13、PB14、PB15,波特率为3M),DMA1(数据流4,通道0)
WS2812B:接收波特率为750Kbps

说明:如果SPI2上挂有多个设备,需要用CS信号控制MOSI的锁存电路。DMA是防止发送相邻两个Byte时中间间隔过大。SPI的MOSI向WS2812B发送数据,每4个SPI的bit表示一个WS2812B的bit码。因为WS2812B要求先传输高位,SPI配置为MSB模式,于是有0b’1100表示WS2812B的1码;0b’1000表示0码。(如果所用主控支持,将SPI的波特率设置在3M~3.2M之间都可以)。每个WS2812B灯珠需要SPI发送12个字节。

相关代码大致如下(已全部测试通过):

1、使能相关时钟

        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);  


2、初始化SPI2和DMA1,执行SPI2_DMA_Init(),即可完成初始化

#define SENDBUFF_SIZE (1024*2)                        // 缓存长度  
static uint8_t TX_Buff[SENDBUFF_SIZE];        // 发送缓存

static void SPI2_PortInit(void){
        GPIO_InitTypeDef GPIO_InitStructure;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 |  GPIO_Pin_14 | GPIO_Pin_15;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
        GPIO_Init(GPIOB, &GPIO_InitStructure);
       
        GPIO_PinAFConfig(GPIOB, 13, GPIO_AF_SPI2);
        GPIO_PinAFConfig(GPIOB, 14, GPIO_AF_SPI2);
        GPIO_PinAFConfig(GPIOB, 15, GPIO_AF_SPI2);
       
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 ;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
        GPIO_Init(GPIOB, &GPIO_InitStructure);
       
        SPI2_BusEnd();
}

static void SPI2_TX_DMA_Init(void)
{
    /* 通道0,数据流4 */  
    // DMA结构体
    DMA_InitTypeDef DMA_InitStructure;
    /* 使能DMA时钟 */
    /* 复位初始化DMA数据流 */
    DMA_DeInit(DMA1_Stream4);
    /* 确保DMA数据流复位完成 */
    while (DMA_GetCmdStatus(DMA1_Stream4) != DISABLE);

    /* 配置 DMA Stream */  
    DMA_InitStructure.DMA_Channel = DMA_Channel_0;
    /* 外设地址 */  
    DMA_InitStructure.DMA_PeripheralBaseAddr =  (uint32_t)(&(SPI2->DR));   
    /* 内存地址(要传输的变量的指针) ,DMA存储器0地址*/
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)TX_Buff;
    /* 方向:存储器到外设 */
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    /* 数据传输量 ,可设置为0, 实际发送时会重新设置*/      
    DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
    /* 外设非增量模式 */
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    /* 存储器增量模式 */
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    /* 外设数据长度:8位 */
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    /* 内存数据长度:8位 */
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    /* DMA模式:正常模式 */
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    /* 优先级:高 */
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    /* 禁用FIFO */
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
    /* 外设突发单次传输 */
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    /* 存储器突发单次传输 */
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

    /* 初始化DMA Stream */     
    DMA_Init(DMA1_Stream4, &DMA_InitStructure);
       
    /* 开启传输完成中断  */     
    DMA_ITConfig(DMA1_Stream4, DMA_IT_TC, ENABLE);
       
        DMA_Cmd(DMA1_Stream4, ENABLE);
}

static void SPI2_TX_DMA_NVICInit(void){
    // 中断初始化
    // 中断结构体
    NVIC_InitTypeDef NVIC_InitStructure;
    /* DMA发送中断源 */  
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream4_IRQn;
    /* 抢断优先级 */  
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    /* 响应优先级 */  
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;              
    /* 使能外部中断通道 */
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                     
    /* 配置NVIC */        
    NVIC_Init(&NVIC_InitStructure);
}

void SPI2_DMA_Init(void){
        SPI_InitTypeDef spi_init_structure;
       
        SPI2_PortInit();
       
        spi_init_structure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    spi_init_structure.SPI_Mode = SPI_Mode_Master;
    spi_init_structure.SPI_DataSize = SPI_DataSize_8b;
    spi_init_structure.SPI_CPOL = SPI_CPOL_High;
    spi_init_structure.SPI_CPHA = SPI_CPHA_2Edge;
    spi_init_structure.SPI_NSS = SPI_NSS_Soft;
    spi_init_structure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
    spi_init_structure.SPI_FirstBit = SPI_FirstBit_MSB;
    spi_init_structure.SPI_CRCPolynomial = 7;
    SPI_Init(SPI2, &spi_init_structure);

    SPI_Cmd(SPI2, ENABLE);
       
        SPI2_TX_DMA_Init();
        SPI2_TX_DMA_NVICInit();
}

void SPI2_BusStart(void){
        GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}

void SPI2_BusEnd(void){
        GPIO_SetBits(GPIOB, GPIO_Pin_12);
}

void SPI2_DMA_SendBuf(uint32_t SizeLen)
{   
        //SPI向DMA发出请求,DMA会通过仲裁器自动回应请求
        SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);
    // 关闭发送 DMA     
    DMA_Cmd(DMA1_Stream4, DISABLE);
    // 设置发送的数据量   
    DMA_SetCurrDataCounter(DMA1_Stream4, SizeLen);
    // 清空数据
    SPI_I2S_ReceiveData(SPI2);      
    // 擦除DMA标志位
    DMA_ClearFlag(DMA1_Stream4, DMA_IT_TCIF4);
    // 片选拉低,接收数据
    SPI2_BusStart();
    // 开启发送 DMA
    DMA_Cmd(DMA1_Stream4, ENABLE);  
}

uint8_t *Get_SPI2DMATxBuff(void){
        return TX_Buff;
}

void DMA1_Stream4_IRQHandler(void)
{
    // DMA 发送完成
    if(DMA_GetITStatus(DMA1_Stream4, DMA_IT_TCIF4))
    {
        // 清除DMA发送完成标志
        DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TCIF4);  
        // 片选拉高,数据发送完毕
                SPI2_BusEnd();
        }
}


3、WS2812B处理,执行WS2812_Init()完成初始化,WS2812_CloseAll()设置所有WS2812B熄灭,Set_WS2812RGB(uint16_t Num, uint32_t RGB_Data)设置对应灯珠的颜色(也就是第Num灯珠的RGB,Num是从0开始)。

#define SINGLEWS2812_BYTELEN 12//单个灯珠需要12个字节,勿动

#define WS2812_NUM 64//总共有多少灯珠

#define BIT_BLOCK        0x88//灯珠熄灭bit值
#define BIT_RESET        0x00//复位bit值
#define BIT_TEST        0x55

typedef union {
        uint32_t RGB;
        struct{
                uint8_t B;
                uint8_t G;
                uint8_t R;
                uint8_t Empty;
        };
}Color_T;

const uint8_t reset_bytelen = 120;
static uint16_t Send_AllByteLen = 0;
static uint8_t *tx_data;
static uint8_t *reset_data;
static uint8_t *rgb_data;

static const uint32_t DataTable[] = {
                0x88888888, 0x8c888888, 0xc8888888, 0xcc888888, 0x888c8888, 0x8c8c8888, 0xc88c8888, 0xcc8c8888,
                0x88c88888, 0x8cc88888, 0xc8c88888, 0xccc88888, 0x88cc8888, 0x8ccc8888, 0xc8cc8888, 0xcccc8888,
                0x88888c88, 0x8c888c88, 0xc8888c88, 0xcc888c88, 0x888c8c88, 0x8c8c8c88, 0xc88c8c88, 0xcc8c8c88,
                0x88c88c88, 0x8cc88c88, 0xc8c88c88, 0xccc88c88, 0x88cc8c88, 0x8ccc8c88, 0xc8cc8c88, 0xcccc8c88,
                0x8888c888, 0x8c88c888, 0xc888c888, 0xcc88c888, 0x888cc888, 0x8c8cc888, 0xc88cc888, 0xcc8cc888,
                0x88c8c888, 0x8cc8c888, 0xc8c8c888, 0xccc8c888, 0x88ccc888, 0x8cccc888, 0xc8ccc888, 0xccccc888,
                0x8888cc88, 0x8c88cc88, 0xc888cc88, 0xcc88cc88, 0x888ccc88, 0x8c8ccc88, 0xc88ccc88, 0xcc8ccc88,
                0x88c8cc88, 0x8cc8cc88, 0xc8c8cc88, 0xccc8cc88, 0x88cccc88, 0x8ccccc88, 0xc8cccc88, 0xcccccc88,
                0x8888888c, 0x8c88888c, 0xc888888c, 0xcc88888c, 0x888c888c, 0x8c8c888c, 0xc88c888c, 0xcc8c888c,
                0x88c8888c, 0x8cc8888c, 0xc8c8888c, 0xccc8888c, 0x88cc888c, 0x8ccc888c, 0xc8cc888c, 0xcccc888c,
                0x88888c8c, 0x8c888c8c, 0xc8888c8c, 0xcc888c8c, 0x888c8c8c, 0x8c8c8c8c, 0xc88c8c8c, 0xcc8c8c8c,
                0x88c88c8c, 0x8cc88c8c, 0xc8c88c8c, 0xccc88c8c, 0x88cc8c8c, 0x8ccc8c8c, 0xc8cc8c8c, 0xcccc8c8c,
                0x8888c88c, 0x8c88c88c, 0xc888c88c, 0xcc88c88c, 0x888cc88c, 0x8c8cc88c, 0xc88cc88c, 0xcc8cc88c,
                0x88c8c88c, 0x8cc8c88c, 0xc8c8c88c, 0xccc8c88c, 0x88ccc88c, 0x8cccc88c, 0xc8ccc88c, 0xccccc88c,
                0x8888cc8c, 0x8c88cc8c, 0xc888cc8c, 0xcc88cc8c, 0x888ccc8c, 0x8c8ccc8c, 0xc88ccc8c, 0xcc8ccc8c,
                0x88c8cc8c, 0x8cc8cc8c, 0xc8c8cc8c, 0xccc8cc8c, 0x88cccc8c, 0x8ccccc8c, 0xc8cccc8c, 0xcccccc8c,
                0x888888c8, 0x8c8888c8, 0xc88888c8, 0xcc8888c8, 0x888c88c8, 0x8c8c88c8, 0xc88c88c8, 0xcc8c88c8,
                0x88c888c8, 0x8cc888c8, 0xc8c888c8, 0xccc888c8, 0x88cc88c8, 0x8ccc88c8, 0xc8cc88c8, 0xcccc88c8,
                0x88888cc8, 0x8c888cc8, 0xc8888cc8, 0xcc888cc8, 0x888c8cc8, 0x8c8c8cc8, 0xc88c8cc8, 0xcc8c8cc8,
                0x88c88cc8, 0x8cc88cc8, 0xc8c88cc8, 0xccc88cc8, 0x88cc8cc8, 0x8ccc8cc8, 0xc8cc8cc8, 0xcccc8cc8,
                0x8888c8c8, 0x8c88c8c8, 0xc888c8c8, 0xcc88c8c8, 0x888cc8c8, 0x8c8cc8c8, 0xc88cc8c8, 0xcc8cc8c8,
                0x88c8c8c8, 0x8cc8c8c8, 0xc8c8c8c8, 0xccc8c8c8, 0x88ccc8c8, 0x8cccc8c8, 0xc8ccc8c8, 0xccccc8c8,
                0x8888ccc8, 0x8c88ccc8, 0xc888ccc8, 0xcc88ccc8, 0x888cccc8, 0x8c8cccc8, 0xc88cccc8, 0xcc8cccc8,
                0x88c8ccc8, 0x8cc8ccc8, 0xc8c8ccc8, 0xccc8ccc8, 0x88ccccc8, 0x8cccccc8, 0xc8ccccc8, 0xccccccc8,
                0x888888cc, 0x8c8888cc, 0xc88888cc, 0xcc8888cc, 0x888c88cc, 0x8c8c88cc, 0xc88c88cc, 0xcc8c88cc,
                0x88c888cc, 0x8cc888cc, 0xc8c888cc, 0xccc888cc, 0x88cc88cc, 0x8ccc88cc, 0xc8cc88cc, 0xcccc88cc,
                0x88888ccc, 0x8c888ccc, 0xc8888ccc, 0xcc888ccc, 0x888c8ccc, 0x8c8c8ccc, 0xc88c8ccc, 0xcc8c8ccc,
                0x88c88ccc, 0x8cc88ccc, 0xc8c88ccc, 0xccc88ccc, 0x88cc8ccc, 0x8ccc8ccc, 0xc8cc8ccc, 0xcccc8ccc,
                0x8888c8cc, 0x8c88c8cc, 0xc888c8cc, 0xcc88c8cc, 0x888cc8cc, 0x8c8cc8cc, 0xc88cc8cc, 0xcc8cc8cc,
                0x88c8c8cc, 0x8cc8c8cc, 0xc8c8c8cc, 0xccc8c8cc, 0x88ccc8cc, 0x8cccc8cc, 0xc8ccc8cc, 0xccccc8cc,
                0x8888cccc, 0x8c88cccc, 0xc888cccc, 0xcc88cccc, 0x888ccccc, 0x8c8ccccc, 0xc88ccccc, 0xcc8ccccc,
                0x88c8cccc, 0x8cc8cccc, 0xc8c8cccc, 0xccc8cccc, 0x88cccccc, 0x8ccccccc, 0xc8cccccc, 0xcccccccc,
        };

//为了提高执行效率用查表实现
static uint8_t CheckTableAnalyticDataBits(uint8_t Data, uint8_t *DataList_4S){
        *(uint32_t *)DataList_4S = DataTable[Data];
        return 0;
}

static uint8_t AnalyticRGBData(uint32_t RGB_Data, uint8_t *DataList_12S){//
        Color_T a;
        a.RGB = RGB_Data;
        CheckTableAnalyticDataBits(a.G, DataList_12S);
        CheckTableAnalyticDataBits(a.R, DataList_12S+4);
        CheckTableAnalyticDataBits(a.B, DataList_12S+8);
        return 0;
}

uint8_t Set_WS2812RGB(uint16_t Num, uint32_t RGB_Data){
        AnalyticRGBData(RGB_Data, &rgb_data[Num * SINGLEWS2812_BYTELEN]);
        return 0;
}

void WS2812_Init(void){
        tx_data = Get_SPI2DMATxBuff();
        reset_data = tx_data;
        rgb_data = &tx_data[reset_bytelen];
        if(reset_bytelen > 0){//设置复位,也就是全部发送0。每次发数据之前都发送。
                memset(reset_data, BIT_RESET, reset_bytelen);
        }
        Send_AllByteLen = reset_bytelen + WS2812_NUM * SINGLEWS2812_BYTELEN;
}

//设置所有WS2812B熄灭
void WS2812_CloseAll(void){
        memset(rgb_data, BIT_BLOCK, WS2812_NUM * SINGLEWS2812_BYTELEN);
}



4、全部设置完相应位灯珠的颜色之后,执行以下语句,即可点亮所有设置的灯珠

SPI2_DMA_SendBuf(Send_AllByteLen);
————————————————
版权声明:本文为CSDN博主「GJF712」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/GJF712/article/details/131989518

使用特权

评论回复
沙发
Henryko| | 2023-8-19 22:37 | 只看该作者
数据表的数据咋看着都是重复的

使用特权

评论回复
板凳
帛灿灿| | 2024-2-21 07:20 | 只看该作者

它是由两个尺寸相同、匝数相同的线圈对称地绕制在同一个铁氧体环形磁芯

使用特权

评论回复
地板
Pulitzer| | 2024-2-21 08:23 | 只看该作者

镀半孔或c形孔是在板的边缘上镀半个半孔的一半。

使用特权

评论回复
5
周半梅| | 2024-2-21 10:19 | 只看该作者

这种电路结构的特点是:由四只相同的开关管接成电桥结构驱动脉冲变压器原边。

使用特权

评论回复
6
Pulitzer| | 2024-2-21 11:22 | 只看该作者

这种技术称为板对板焊接

使用特权

评论回复
7
童雨竹| | 2024-2-21 13:18 | 只看该作者

模块电源灌封操作之所以重要,主要是由于其涉及到模块电源的防护及热设计

使用特权

评论回复
8
Wordsworth| | 2024-2-21 14:21 | 只看该作者

得到不同测试条件下的输出电流和电压值,分析数据并进行比较

使用特权

评论回复
9
Clyde011| | 2024-2-21 15:24 | 只看该作者

这样可以获得更光滑的表面。

使用特权

评论回复
10
公羊子丹| | 2024-2-21 16:17 | 只看该作者

根据电荷守恒:Qinitial=Qfinal

使用特权

评论回复
11
万图| | 2024-2-21 17:20 | 只看该作者

是因为它作用是起到抑制,多应用于开关电源电路中

使用特权

评论回复
12
Uriah| | 2024-2-21 18:23 | 只看该作者

对于标准PCB设计,c形孔的最小直径为0.5mm,

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

77

主题

3312

帖子

3

粉丝