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的数据。
### CMS32L051 DMA Configuration and Usage For configuring Direct Memory Access (DMA) on the CMS32L051 microcontroller, several key steps must be followed to ensure proper setup of peripherals that require high-speed data transfers without CPU intervention. The process involves enabling specific channels within the DMA controller, configuring them according to requirements, and linking these configurations with peripheral requests such as those from timers or ADCs. To enable a DMA channel specifically for input capture using Timer 2's CC2 event: ```c // Enable the DMA1 Channel 7 by setting its EN bit in CCR register. DMA1_Channel7->CCR |= DMA_CCR_EN; // Allow TIM2 Capture/Compare 2 events to trigger DMA requests via DIER register. TIM2->DIER |= TIM_DMA_CC2; ``` After preparing the DMA channel and associating it with an interrupt source like timer captures, one should also configure the corresponding timer channel for operation mode settings including direction control through `CCER` registers: ```c // Activate Capture Compare Channel 2 inside TIM2 module. TIM2->CCER |= TIM_CCER_CC2E; ``` Finally, starting both the configured timer instance along with waiting until all requested operations are completed ensures synchronization between hardware components involved during execution time: ```c // Start TIM2 counter after completing previous setups. TIM2->CR1 |= TIM_CR1_CEN; // Wait loop checking status flag TCIF7 indicating end-of-transfer condition met. while ((DMA1->ISR & DMA_ISR_TCIF7) == RESET) {} ``` The above code snippets provide insight into how DMA can be initialized alongside other modules present within CMS32L051 devices when implementing features requiring efficient memory handling mechanisms[^1].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值