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

文章目录

概要

基于中微CMS32L051驱动WS2812B全彩灯,使用PWM+DMA模式。参照中微的串口uartSendReceive,该例程中有使用DMA进行串口数据的收发。

代码

  • main.c
/***********************************************************************************************************************
* Copyright (C) . All rights reserved.
***********************************************************************************************************************/

/***********************************************************************************************************************
* File Name    : main.c
* Version      :
* Device(s)    : CMS32L051
* Tool-Chain   : MDK(armcc)
* Description  : This file is a template.
* Creation Date: 2022/5/30
***********************************************************************************************************************/

/***********************************************************************************************************************
Macro Definitions
***********************************************************************************************************************/

/***********************************************************************************************************************
Includes
***********************************************************************************************************************/
#include <stdio.h>
#include "uart_demo.h"
#include "tim_demo.h"
#include "delay_demo.h"
#include "dma_demo.h"

#define UART0_DMA_RCV
/***********************************************************************************************************************
Global variables and functions
***********************************************************************************************************************/
#if 0
// g,r,b
uint16_t pwm_buf[24+3] = {
	0, 0,   // DMA发送消抖
	WS2812B_PULSE_LOW, WS2812B_PULSE_LOW, WS2812B_PULSE_LOW, WS2812B_PULSE_LOW, WS2812B_PULSE_LOW, WS2812B_PULSE_LOW, WS2812B_PULSE_LOW, WS2812B_PULSE_LOW,          // G
	WS2812B_PULSE_LOW, WS2812B_PULSE_LOW, WS2812B_PULSE_LOW, WS2812B_PULSE_LOW, WS2812B_PULSE_LOW, WS2812B_PULSE_LOW, WS2812B_PULSE_LOW, WS2812B_PULSE_LOW,          // B
	WS2812B_PULSE_HIGH, WS2812B_PULSE_HIGH, WS2812B_PULSE_HIGH, WS2812B_PULSE_HIGH, WS2812B_PULSE_HIGH, WS2812B_PULSE_HIGH, WS2812B_PULSE_HIGH, WS2812B_PULSE_HIGH,  // R
	0  // PWM关闭
};
#else
/*
 * __attribute__((packed)):
 * 用于告诉编译器取消结构体在编译过程中的优化对齐,
* 按照实际占用字节数进行对齐。这意味着结构体的成员变量将紧密排列,不会插入任何填充字节
*/
struct __attribute__((packed)) PWM
{
	uint16_t g[8], r[8], b[8];
};

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


typedef struct PWM PWM_t;
typedef struct RGB RGB_t;

typedef union
{
	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
}pwm_frame_t;

static pwm_frame_t pwm_frame;


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

void led_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;
	}
}


#endif

int main()
{
    uint32_t msCnt, len, i; // count value of 1ms
    uint8_t rxbuf[64];
	uint32_t u0_Baudchose = 38400;
    //-----------------------------------------------------------------------
    // Systick setting
    //-----------------------------------------------------------------------
    SystemCoreClockUpdate();
    msCnt = SystemCoreClock / 1000;
    SysTick_Config(msCnt);
    delay_init(SystemCoreClock); //延时初始化
		
    Uart0_Init(230400);
	UART_DeInit(UART0);
	Uart0_Init(115200);
#ifdef UART0_DMA_RCV
    /*串口0通过DMA接收不定长数据*/
    DMA_Uart0_Rx(DMA_VECTOR_SR0, DMA_Mode_Repeat, (void *)&UART0_RX, UART0_RX_BUF, UART_MAX_RECV_LEN); //config dma transmission
#endif
	if(u0_Baudchose == 38400)
	{
		UART_DeInit(UART0);
		Uart0_Init(38400);
	}
	else if(u0_Baudchose == 115200)
	{
		UART_DeInit(UART0);
		Uart0_Init(115200);		
	}
	else if(u0_Baudchose == 9600)
	{
		UART_DeInit(UART0);	
		Uart0_Init(9600);
	}
//    Uart1_Init(38400);

//    Uart2_Init(9600);
	
	pwm_init();
	led_Fill_Solid_RGB(RGB(0, 0, 125));


    while (1)
    {
        delayMS(10);

#ifdef UART0_DMA_RCV
        len = Uart0_Dma_Rcv(rxbuf);

        if (len)
        {
            Uart0_Dma_Send(rxbuf, len);
#if 0
			Pwm_Dma_Send(pwm_buf, 27);
#else
			led_Fill_Solid_RGB(RGB(rxbuf[0], rxbuf[1], rxbuf[2]));
			Pwm_Dma_Send(pwm_frame.date, WS2812B_FRAME_SIZE);
#endif
        }

#else

        if (UART0_RX_STA & 0x8000U)
        {
            //          Uart0_IntSend(UART0_RX_BUF,UART0_RX_STA&0x3fff); //中断发送
            //          Uart0_Dma_Send(UART0_RX_BUF,UART0_RX_STA&0x3fff);//DMA 发送
            for (i = 0; i < (UART0_RX_STA & 0x3fff); i++)   //轮询发送
            {
                Uart0_Send(UART0_RX_BUF[i]);
            }

            UART0_RX_STA = 0;
        }

#endif

        if (UART1_RX_STA & 0x8000U)     //如果接收完成
        {
            Uart1_IntSend(UART1_RX_BUF, UART1_RX_STA & 0x3fff);
            UART1_RX_STA = 0;
        }

        if (UART2_RX_STA & 0x8000U)
        {
            Uart2_IntSend(UART2_RX_BUF, UART2_RX_STA & 0x3fff);
            UART2_RX_STA = 0;
        }
    }
}





  • pwm_init函数
void pwm_init(void)
{
    TIM_InitTypeDef TIM_InitStructure = {0};
    GPIO_InitTypeDef GPIO_InitStruct = {0};

#if 0
    GPIO_PinAFConfig(GPIO_PORT6, GPIO_Pin_2, GPIO_P62, GROUP_AF_TO11); // TO11 can be used to any disired pins

    GPIO_InitStruct.GPIO_Pin    = GPIO_Pin_2;
    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_PORT6, &GPIO_InitStruct);
#else
    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);
#endif
		
	//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_CLK0_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);
}
  • Pwm_Dma_Send函数
void Pwm_Dma_Send(uint16_t *tx_buf, uint16_t tx_num)
{
    DMA_Pwm_Tx(DMA_VECTOR_TM41_CH1, DMA_Mode_Normal, (void *)tx_buf, (void *)&TM41->TDR11, tx_num); //config dma transmission
    DMA_Trigger(DMA_VECTOR_TM41_CH1);
}
  • DMA_Pwm_Tx函数
void DMA_Pwm_Tx(DMA_VECTOR_t dma_vector, DMA_Mode_t mode, void *src_adr, void *dst_adr, uint16_t count)
{
    DMA_InitTypeDef  DMA_InitStructure = {0};


    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);
}

小结

  1. DMA启动发送时,前两个占空比有时会丢失,因此在发送WS2812B数据前,发送两个数据0,防止有用数据丢失。
  2. DMA最后发送的数据给0,即关闭占空比,因此控制一个WS2812B灯珠需要发送2(两个0)+24(3*8 rgb数据)+1(stop) = 27个uint16_t的数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值