CMS32L051驱动WS2812B全彩灯-SPI+DMA模式

文章目录

概要

基于中微CMS32L051驱动WS2812B全彩灯,使用SPI+DMA模式。SPI时钟设置为6MHz,发送一个字节是8/6000000=1.333us。ws2812b设置一个灯珠的颜色需要3个字节(r, g, b),3*8=24位。使SPI的字节正好等于ws2812b的一个位。因此控制一个ws2812b需要SPI发送24byte,在头部加入两个字节0,在尾部加入一个字节0进行消抖,总共需要24+3=27byte

代码

bsp_ws2812b.c

#include "UserConfigure.h"

typedef union
{
#if (WS2812B_PWM_MODE)
	struct {
		uint16_t head1;                        // 0,DMA发送消抖
		uint16_t head2;                        // 0,DMA发送消抖
		
		PWM_t DMABuffer[WS2812B_BUFFER_SIZE];  // 实际ws2812b数据
		
		uint16_t stop;                         // 0,停止PWM
	};
	
	uint16_t date[WS2812B_FRAME_SIZE]; // 2 head + 24 * WS2812B_BUFFER_SIZE + 1 stop
#elif (WS2812B_SPI_MODE)
	struct {
		uint8_t head1;                        // 0,DMA发送消抖
		uint8_t head2;                        // 0,DMA发送消抖
		
		PWM_t DMABuffer[WS2812B_BUFFER_SIZE];  // 实际ws2812b数据
		
		uint8_t stop;                         // 0,停止PWM
	};
	
	uint8_t date[WS2812B_FRAME_SIZE]; // 2 head + 24 * WS2812B_BUFFER_SIZE + 1 stop
#endif
}pwm_frame_t;

static pwm_frame_t pwm_frame;

/***********************************************************************************************************************
* Function Name: Spi0_Init
* @brief simple spi init
* @param
* @return  status
***********************************************************************************************************************/
void NSS_set()
{
//    GPIO_SetBits(GPIO_PORT6, GPIO_Pin_2);
}
void NSS_clr()
{
//    GPIO_ResetBits(GPIO_PORT6, GPIO_Pin_2);
}

RGB_t bsp_ws2812b_set_rgb(uint8_t r, uint8_t g, uint8_t b)
{
	RGB_t tmp = { r, g, b };
	return tmp;
}

void bsp_ws2812b_fill_solid_rgb(RGB_t color)
{
	uint8_t r = color.r;
	uint8_t g = color.g;
	uint8_t b = color.b;
	uint8_t mask = 0x80;
	
	pwm_frame.head1 = 0;
	pwm_frame.head2 = 0;
	pwm_frame.stop = 0;
	
	int i;
	for (i = 0; i < 8; i++)
	{
		pwm_frame.DMABuffer->r[i] = r & mask ? WS2812B_PULSE_HIGH : WS2812B_PULSE_LOW;
		pwm_frame.DMABuffer->g[i] = g & mask ? WS2812B_PULSE_HIGH : WS2812B_PULSE_LOW;
		pwm_frame.DMABuffer->b[i] = b & mask ? WS2812B_PULSE_HIGH : WS2812B_PULSE_LOW;
		
		mask >>= 1;
	}
}

#if (WS2812B_PWM_MODE)
void bsp_dma_pwm_tx(DMA_VECTOR_t dma_vector, DMA_Mode_t mode, void *src_adr, void *dst_adr, uint16_t count)
{
	unsigned char CTRL_DATA_PWM = 10;
    DMA_InitTypeDef  DMA_InitStructure = {};

    DMA_InitStructure.DMA_Vector = dma_vector;    //根据功能选择不同的dma向量区
    DMA_InitStructure.DMA_CtrlId = CTRL_DATA_PWM;     //选择控制数据区
    DMA_InitStructure.DMA_SrcAddr = (uint32_t)src_adr;  //配置dma源地址
    DMA_InitStructure.DMA_DstAddr = (uint32_t)dst_adr;  //配置dma目标地址
    DMA_InitStructure.DMA_BufferSize = count;
    DMA_InitStructure.DMA_SrcInc = DMA_SrcInc_Enable;//源地址增量模式
    DMA_InitStructure.DMA_DstInc = DMA_DstInc_Disable;//目标地址固定
    DMA_InitStructure.DMA_DataSize = DMA_DataSize_HalfWord;//传输数据长度选择
    DMA_InitStructure.DMA_Mode = mode;//普通模式
	DMA_Init(&DMA_InitStructure);

	DMA_Start(DMA_InitStructure.DMA_Vector);
}

#elif (WS2812B_SPI_MODE)
/***********************************************************************************************************************
* Function Name: DMA_Sspi_Write
* @brief  config DMA for spi write
* @param  src_adr: dma source address
* @param  dst_adr: dma destine address
* @param  count: dma transform num
* @return None
***********************************************************************************************************************/
void DMA_Sspi_Write(DMA_VECTOR_t dma_vector, uint8_t ctrl_data, DMA_Mode_t mode, void *src_adr, void *dst_adr, uint16_t count)
{
    DMA_InitTypeDef  DMA_InitStructure = {};

    DMA_InitStructure.DMA_Vector = dma_vector;    //根据功能选择不同的dma向量区
    DMA_InitStructure.DMA_CtrlId = ctrl_data;     //选择控制数据区
    DMA_InitStructure.DMA_SrcAddr = (uint32_t)src_adr;  //配置dma源地址
    DMA_InitStructure.DMA_DstAddr = (uint32_t)dst_adr;  //配置dma目标地址
    DMA_InitStructure.DMA_BufferSize = count;
    DMA_InitStructure.DMA_SrcInc = DMA_SrcInc_Enable;//源地址增量模式
    DMA_InitStructure.DMA_DstInc = DMA_DstInc_Disable;//目标地址固定
    DMA_InitStructure.DMA_DataSize = DMA_DataSize_Byte;//传输数据长度选择
    DMA_InitStructure.DMA_Mode = mode;//普通模式

	DMA_Init(&DMA_InitStructure);

	DMA_Start(DMA_InitStructure.DMA_Vector);

}

/***********************************************************************************************************************
* Function Name: Spi_Dma_Write
* @brief  spi write data by dma
* @param  None
* @return None
***********************************************************************************************************************/
void Spi20_Dma_Write(uint8_t *tx_buf, uint16_t tx_num)
{
	unsigned int CTRL_DATA_SPI20 = 7;
	
    SSPI_Set_TransmitMode(SSPI20, SSPI_TransmitMode_Send);
    SSPI_Start(SSPI20);
    INTC_EnableIRQ(SPI20_IRQn);
	
    DMA_Sspi_Write(DMA_VECTOR_SPI20, CTRL_DATA_SPI20, DMA_Mode_Normal, tx_buf, (void *)&SSPI20_SDR, tx_num); //config dma transmission

    pData.flag = INT_DMA;   //set data direction
    DMA_Trigger(DMA_VECTOR_SPI20);
}
#endif

/****************************************************************************
* Function Name: uart0_dma_send
* @brief  UART0 Send interrupt service routine
* @param  None
* @return None
*****************************************************************************/
void bsp_ws2812b_send(void)
{
#if (WS2812B_PWM_MODE)
    bsp_dma_pwm_tx(DMA_VECTOR_TM41_CH1, DMA_Mode_Normal, pwm_frame.date, (void *)&TM41->TDR11, WS2812B_FRAME_SIZE); //config dma transmission
    DMA_Trigger(DMA_VECTOR_TM41_CH1);
#elif (WS2812B_SPI_MODE)
	Spi20_Dma_Write(pwm_frame.date, sizeof(pwm_frame.date));
#endif
}

void bsp_ws2812b_init(void)
{
#if (WS2812B_PWM_MODE)
    TIM_InitTypeDef TIM_InitStructure = {};
    GPIO_InitTypeDef GPIO_InitStruct = {};

    GPIO_PinAFConfig(GPIO_PORT1, GPIO_Pin_4, GPIO_P14, GROUP_AF_TO11); // TO11 can be used to any disired pins

    GPIO_InitStruct.GPIO_Pin    = GPIO_Pin_4;
    GPIO_InitStruct.GPIO_Mode   = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_Level  = GPIO_Level_LOW;
	GPIO_InitStruct.GPIO_OType  = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Ctrl   = GPIO_Control_DIG;
    GPIO_Init(GPIO_PORT1, &GPIO_InitStruct);
		
	//TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);
    // 1 / 32000000 * 40 = 1.25us  = 0.85us + 0.4us
    // WS2812B_FREQUENCY  32000000
    TIM_InitStructure.TIM = TIM41;
    TIM_InitStructure.TIM_Selection_Master = TTM_Channel_0;  // when multi-tim combination,it can generate pwm wave
    TIM_InitStructure.TIM_Channel = TTM_Channel_1;           // |TTM_Channel_2|TTM_Channel_3
    TIM_InitStructure.TIM_ClkDivision = TIM_CLK1_Div2;       // specify the operation clk of tim
    TIM_InitStructure.TIM_Period[0] = WS2812B_PERIOD;        // specify the number of count clock
    TIM_InitStructure.TIM_Period[1] = 0;                     // specify duty

    TIM_InitStructure.TIM_Trigger = TIM_Trigger_Software;    // specify the software trigger
    TIM_InitStructure.TIM_Mode = TIM_Mode_PWM_Master;        // PWM_Master mode
    TIM_InitStructure.TIM_StartInt = TIM_StartInt_Enable;    // the relationship between startCount and interrupt setting
    TIM_Init(&TIM_InitStructure);

#elif (WS2812B_SPI_MODE)
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    SPI_InitTypeDef  SPI_InitStructure;
	
	GPIO_PinAFConfig(GPIO_PORT1, GPIO_Pin_4, GPIO_P14, GROUP_AF_SDO20); // MOSI(SDO20)  can be disired to any pins with PxxCFG register
    /*SDO(MOSI) GPIO CONFIG*/
    GPIO_InitStruct.GPIO_Pin     = GPIO_Pin_4;
    GPIO_InitStruct.GPIO_Mode    = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_Ctrl    = GPIO_Control_DIG;
    GPIO_InitStruct.GPIO_OType   = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Level   = GPIO_Level_HIGH;
    GPIO_Init(GPIO_PORT1, &GPIO_InitStruct);

    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                  //设置SPI工作模式:设置为主SPI 从模式SLAVE
    SPI_InitStructure.SPI_DataSize = SPI_Data_Bits_8;             //设置SPI的数据大小:SPI发送接收8位帧结构
    SPI_InitStructure.SPI_Phase_Mode = SPI_Phase_Mode0;          //选择使用spi的工作模式
    SPI_InitStructure.SPI_ClockSpeed = 6000000;                   //选择spi运行时钟
    SPI_InitStructure.SPI_Bitorder = SPI_Bit_Msb;                //选择bit高先传

    SSPI_Init(SSPI20, &SPI_InitStructure);

	ISR_Register(SPI20_IRQn, spi20_interrupt);       //中断服务路径注册
    NSS.Active = NSS_clr;                            //NSS片选初始化
    NSS.Inactive = NSS_set;
    NSS.Inactive();                                  //NSS初始化空闲状态 MODE0 情况下低电平为空闲,在clk第一个边沿采样

    Spi20_Dma_Write(pwm_frame.date, sizeof(pwm_frame.date));  // 拉低MOSI,否则在高电平状态下发送rgb数据识别错误

#endif
}

/***********************************************************************************************************************
* Function Name: spi_callback_error
* @brief  UART0 Receive interrupt service routine
* @param  None
* @return None
***********************************************************************************************************************/
void spi_callback_error()
{
    //user edit here when appear error
}

/***********************************************************************************************************************
* Function Name: spi11_interrupt
* @brief  spi write/read interrupt service routine
* @param  None
* @return None
***********************************************************************************************************************/
void spi20_interrupt(void *msg)
{
    volatile uint8_t err_type;
    volatile uint8_t sio_dummy;
    ATE_FRAME_t *pFrame = (ATE_FRAME_t *)msg;
    SCIAFSelect_TypeDef func = SSPI20;

    err_type = SSPI_GetErrStaus(SSPI20, SSPI_FLAG_OVERRUN);

    INTC_ClearPendingIRQ(SPI20_IRQn);

    if (1U == err_type)
    {
        spi_callback_error();//error condition
    }
    else
    {
        if (pFrame->flag == INT_TX)
        {
            if (pFrame->len > 0)
            {
                if (0U != pFrame->data)
                {
                    SSPI_SendByte(SSPI20, *pFrame->data);
                    pFrame->data++;
                    pFrame->len--;
                }
            }
            else
            {
                if (0U != pFrame->data)
                {
                    if ((SSPI_Get_MasterMode(SSPI20) != SPI_Mode_Master) && (SSPI_Get_TransmitMode(SSPI20) != SSPI_TransmitMode_Recv))//slave mode :sclk is inputed by master clk
                    {
                        ;//slave mode;
                    }
                    else
                    {
                        while (SSPI_GetErrStaus(SSPI20, SCI_UNDER_EXECUTE)); //waitting conmunction end, if register ST turns 1,or SS turns 1
                    }
                }

                SSPI_Stop(func);
                g_spi_tx_end = 1;
            }
        }

        if (pFrame->flag == INT_RX)
        {
            if (pFrame->len > 1)
            {
                if (0U != pFrame->data)
                {
                    *pFrame->data = SSPI_ReceiveByte(SSPI20);
                    pFrame->data++;
                    pFrame->len--;
                }
                else
                {
                    sio_dummy = SSPI_ReceiveByte(SSPI20);
                }

                if (SSPI_Get_MasterMode(SSPI20) == SPI_Mode_Master)
                {
                    SSPI_SendByte(SSPI20, 0xFFU);  // dummy write in master reception mode
                }
            }
            else if (pFrame->len == 1)
            {
                while (SSPI_GetErrStaus(SSPI20, SCI_VALID_STORED) == 0); // wait last data stored in register SDR

                if (0U != pFrame->data)
                {
                    *pFrame->data = SSPI_ReceiveByte(SSPI20);
                    pFrame->len--;
                }
                else
                {
                    sio_dummy = SSPI_ReceiveByte(SSPI20);
                }

                SSPI_Stop(func);
                g_spi_rx_end = 1;
            }
        }

        if (pFrame->flag == INT_DMA)
        {
//			while (SSPI_GetErrStaus(SSPI20, SCI_VALID_STORED) == 0); // wait last data stored in register SDR
			*pFrame->data = SSPI_ReceiveByte(SSPI20);
			
            SSPI_Stop(func);
            g_spi_tx_end = 1;
            g_spi_rx_end = 1;
        }
    }
}



/***********************END OF FILE***********************/

bsp_ws2812b.h

#ifndef _BSP_WS2812B_H_
#define _BSP_WS2812B_H_

#include "UserConfigure.h"

#define WS2812B_SPI_MODE             1
#define WS2812B_PWM_MODE             0

#define WS2812B_FREQUENCY            (32000000)
#define WS2812B_PERIOD               (40)       // 1 / 32000000 * 40 = 1.25us

#if (WS2812B_PWM_MODE)
#define WS2812B_PULSE_HIGH           (27)       // 1 / 32000000 * 27 = 0.85us
#define WS2812B_PULSE_LOW            (13)       // 1 / 32000000 * 13 = 0.40us
#elif (WS2812B_SPI_MODE)
#define WS2812B_PULSE_HIGH           (0xfc)     // 6*150=900us => 1111 1100b = 0xfc
#define WS2812B_PULSE_LOW            (0xe0)     // 3*150=450us => 1110 0000b = 0xe0
#endif

#define WS2812B_BUFFER_SIZE          (1)
#define WS2812B_FRAME_SIZE           (2+WS2812B_BUFFER_SIZE*24+1)
#define WS2812B_START_SIZE           (0)
#define NUM_LEDS                     (1)

/*
 * __attribute__((packed)):
 * 用于告诉编译器取消结构体在编译过程中的优化对齐,
* 按照实际占用字节数进行对齐。这意味着结构体的成员变量将紧密排列,不会插入任何填充字节
*/
struct __attribute__((packed)) PWM
{
#if (WS2812B_PWM_MODE)
    uint16_t g[8], r[8], b[8];
#elif (WS2812B_SPI_MODE)
    uint8_t g[8], r[8], b[8];
#endif
};

struct RGB
{
  uint8_t r, g, b;  // r, g, b 取值1-255
};

typedef struct PWM PWM_t;
typedef struct RGB RGB_t;

void spi20_interrupt(void *msg);

void bsp_ws2812b_init(void);
void bsp_ws2812b_send(void);
void bsp_ws2812b_fill_solid_rgb(RGB_t color);
RGB_t bsp_ws2812b_set_rgb(uint8_t r, uint8_t g, uint8_t b);


#endif


小结

  1. 使用SPI+DMA的方式发送,字节与字节之间也存在一定延迟时间,需要实际测量波形,查看是否满足WS2812B高低位定义的时间。
  2. 程序里设计ws2812b的‘1’是发送一个spi数据0xfc,6x150=900us => 1111 1100b = 0xfc;ws2812b的‘0’是发送一个spi数据0xe0,3x150=450us => 1110 0000b = 0xe0。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值