首页 > 其他分享 >CMS32L051驱动WS2812B全彩灯-PWM+DMA模式

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

时间:2024-09-18 10:20:43浏览次数:14  
标签:DMA PWM PULSE TIM InitStructure GPIO CMS32L051 WS2812B

文章目录

概要

基于中微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的数据。

标签:DMA,PWM,PULSE,TIM,InitStructure,GPIO,CMS32L051,WS2812B
From: https://blog.csdn.net/wwfsdffinf/article/details/142173778

相关文章

  • CMS32L051使用旋转编码器
    文章目录概要代码小结概要CMS32L051使用外部中断的方式识别旋转编码器的方向。选取其中一个信号A进行外部中断触发,由于信号A空闲时处于高电平,因此初始化时外部中断使用下降沿触发;触发第一个下降沿后,判断当前是否已经触发了上升沿,如果已经触发上升沿,则需要判断当前......
  • CMS32L051定时器时钟选择
    文章目录概要代码小结概要CMS32L051的定时器搭载两个通用定时器单元,每个单元含有4个通道。每个通用定时器单元有4个16位定时器。各16位定时器称为“通道”,既能分别用作独立的定时器,也能组合多个通道用作高级的定时器功能。定时器时钟选择寄存器m(TPSm)是16位寄存器,选......
  • 【Proteus51单片机仿真】PWM直流电机调速
    目录一、主要功能二、硬件资源三、程序编程四、实现现象一、主要功能**        基于AT89C51,L298N驱动两个电机,因为是平台,最后用两个电机驱动,然后第一个按键控制所有电机停止,第二个按键按下,电机顺时针转动,第三个按键按下,电机逆时针转动。然后加入pwm调速,分为......
  • 1030-基于51单片机的SPWM波(数码管)原理图、流程图、物料清单、仿真图、源代码
    1030-基于51单片机的SPWM波(数码管)原理图、流程图、物料清单、仿真图、源代码功能介绍:要求能够输出SPWM并且测量输入正弦波的频率并显示。直流电压通过DC-AC电路转为方波,搭建检测电路进行滤波和调节,得到正弦波,单片机采集该正弦波的频率,并显示。有哪些资料:1、仿真工程文......
  • ARM SMMU原理与IOMMU技术(“VT-d” DMA、I/O虚拟化、内存虚拟化)
    名词缩写ASID:AddressSpaceID地址空间标识符CD:ContextDescriptor;上下文描述符;CTP:Context-tablepointer上下文表指针EPT:ExtendedPageTable扩展页表GPA:GuestPhyicalAddress客人的实际地址GVA:GuestVirtualAddress访客虚拟地址HPA:HostPhyicalAddress......
  • 通信工程学习:什么是FDMA频分多址
    FDMA:频分多址        FDMA(FrequencyDivisionMultipleAccess,频分多址)是一种在无线通信领域广泛应用的多址技术。该技术通过将可用的频谱资源按频率划分,把传输频带划分为若干较窄且互不重叠的子频带(或称信道),每个用户分配到一个固定子频带,按频带区分用户,从而实现多个......
  • 通信工程学习:什么是CDMA码分多址
    CDMA:码分多址        CDMA(CodeDivisionMultipleAccess,码分多址)是一种先进的无线通信技术,它属于扩频通信技术的范畴,是数字通信技术的重要分支。以下是CDMA码分多址的详细解释:一、CDMA码分多址的定义与原理定义:        CDMA是一种通过编码区分不同用户......
  • AndroidManifest.xml文件的重要信息
    AndroidManifest.xml文件详解一、概述AndroidManifest.xml文件是Android应用的核心配置文件,它位于应用程序的根目录下(通常在app/src/main/文件夹中)。这个文件对于Android系统来说至关重要,因为它提供了关于应用程序的所有必要信息,包括应用程序的组件、权限要求、应用程序的配......
  • ADC_DMA_双buffer传输
    ADC_DMA_双buffer传输线程A切换buffer地址开启ADC转换,并使用DMA传输等待获取DMA中断的信号量,获取到信号量,表示上一次DMA传输已完成将地址通过消息队列传输给线程Buint32_t*adc_value=NULL;/*USERCODEENDHeader_adc_dma_task_function*/voidadc_dma_task_func......
  • 实战中学习:CMA和DMA_CMA导致安装大游戏失败的问题
     在实际遇到的问题中,遇到Android虚机中安装超过1.8G以上的大游戏APK文件时,就会出现安装失败的现象,通过分析,发现在/data/目录下进行大文件拷贝时,就会出现数据不一致,发现时内核打开了CMA和DMA_CMA,解决方法目前主要回退,将CMA和DMA_CMA的配置关闭现象: 拷贝5次:数据不一致/data/app......