文章目录
概要
基于中微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);
}
小结
- DMA启动发送时,前两个占空比有时会丢失,因此在发送WS2812B数据前,发送两个数据0,防止有用数据丢失。
- DMA最后发送的数据给0,即关闭占空比,因此控制一个WS2812B灯珠需要发送2(两个0)+24(3*8 rgb数据)+1(stop) = 27个uint16_t的数据。