STM32使用LL库PWM的DMA模式驱动ws2812

STM32使用LL库PWM的DMA模式驱动ws2812

    • ~~文末有源代码链接。~~
  • 一、WS2812简介
  • 二、CUBEMX初始化代码配置

文末有源代码链接。

一、WS2812简介

给个链接吧,ws2812时序简介

WS2812使用“单总线”驱动,可以级联驱动n个,当然n是有限制的。时序超级简单,也有一定难度。类似的帖子网上有很多,在此不再赘述。

WS2812驱动方式大概有以下几种:
1.使用GPIO模拟,中间加延时实现“0”、“1”的时序,延时需要借助示波器、逻辑分析仪来调试。
2.使用硬件定时器+PWM实现,当然要控制发送PWM脉冲个数需要严格控制,PWM+DMA的组合程序设计更加简单、更加便捷,但是调试起来要麻烦一些。
3.使用SPI来模拟ws2812的时序,这也是一个很讨巧的办法,当然需要计算SPI的速率、位数、“0”“1”时序要发送的对应的数值。当然SPI+DMA的话,更省cpu。
本帖主要使用硬件定时器PWM+DMA方式实现。

二、CUBEMX初始化代码配置

1.打开cubemx,选择STM32G070RBT6,这是我使用的cpu的型号。
2.时钟树配置,使用内部时钟,sysclk最后为64MHZ,
STM32使用LL库PWM的DMA模式驱动ws2812_第1张图片
3.SYS配置,为了调试方便,勾选SW调试接口,tick时钟选择默认systick不变。
STM32使用LL库PWM的DMA模式驱动ws2812_第2张图片
4.定时器参数设置,这里使用定时器TIM1的CH3通道
***注意,定时器的计数器预加载要关闭。
STM32使用LL库PWM的DMA模式驱动ws2812_第3张图片
DMA参数设置,选择普通模式,方向从内存到外设,注意数据宽度和字长不同,以及地址是否增加。
STM32使用LL库PWM的DMA模式驱动ws2812_第4张图片
5.project manager工程设置
STM32使用LL库PWM的DMA模式驱动ws2812_第5张图片
6. 代码生成器设置
STM32使用LL库PWM的DMA模式驱动ws2812_第6张图片
7.高级设置 (这里选择LL库)
STM32使用LL库PWM的DMA模式驱动ws2812_第7张图片
8.代码生成,打开工程。这里使用keil mdk开发环境。生成的工程的代码结构
STM32使用LL库PWM的DMA模式驱动ws2812_第8张图片
9.编辑tim.c
在文件开头包含头文件

	#include "bsp_ws2812.h"

在MX_TIM1_Init()函数最后添加如下几行代码

  	LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_7,LEN);//设置dma数据传输个数/长度
	LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_7,(uint32_t)pixelBuffer);//设置内存地址,也就是设置buffer地址
	LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_7,(uint32_t)&(TIM1->CCR3));//设置外设地址
	LL_DMA_ClearFlag_GI7(DMA1);//清除中断标志
	LL_DMA_ClearFlag_TC7(DMA1);//清除中断标志
	LL_DMA_EnableIT_TC(DMA1,LL_DMA_CHANNEL_7);//使能传送完成中断
	LL_TIM_EnableDMAReq_CC3(TIM1);//使能TIM1的CC3 DMA请求
	LL_TIM_EnableAllOutputs(TIM1);//使能TIM的输出
	LL_TIM_CC_SetDMAReqTrigger(TIM1,LL_TIM_CCDMAREQUEST_CC);//设置TIM1 DMA请求触发器	
	LL_TIM_CC_EnableChannel(TIM1,LL_TIM_CHANNEL_CH3);//使能TIM1 的cc通道ch3	

这样,每次使用dma的时候,只需要重新设置数据传送长度、使能定时器和DMA通道就可以进行数据传输了。如下:

    LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_7, LEN);
    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_7);
    LL_TIM_EnableCounter(TIM1);

10.编辑stm32g0xx_it.c文件
主要是dma tc中断后,失能dma和tim,并清除中断标志。

	void DMA1_Ch4_7_DMAMUX1_OVR_IRQHandler(void)
	{
	  /* USER CODE BEGIN DMA1_Ch4_7_DMAMUX1_OVR_IRQn 0 */
	  if(LL_DMA_IsActiveFlag_TC7(DMA1))
		{
		  	 ws2812_xfer_flag=0;
			 LL_DMA_ClearFlag_GI7(DMA1);
			 LL_DMA_ClearFlag_TC7(DMA1);
			 LL_DMA_DisableChannel(DMA1,LL_DMA_CHANNEL_7);
			 LL_TIM_DisableCounter(TIM1);	  
		}
	  /* USER CODE END DMA1_Ch4_7_DMAMUX1_OVR_IRQn 0 */
	  
	  /* USER CODE BEGIN DMA1_Ch4_7_DMAMUX1_OVR_IRQn 1 */
	
	  /* USER CODE END DMA1_Ch4_7_DMAMUX1_OVR_IRQn 1 */
	}

11.添加bsp_ws2812.c文件和bsp_ws2812.h文件,此ws2812特效显示代码主要参考一位网友。
链接如下:参考代码网址,在此表示感谢。
11.1)bsp_ws2812.h

#ifndef __BSP_WS2812_H_
#define __BSP_WS2812_H_
#include "main.h"

#define PIXEL_NUM  13
#define GRB  24   //3*8
#define  LEN        (PIXEL_NUM*GRB)
#define WS_LOW  31
#define WS_HIGH 60

extern volatile uint8_t  ws2812_xfer_flag;
extern           uint8_t  pixelBuffer[PIXEL_NUM][GRB];

void WS281x_Init(void);
void WS281x_CloseAll(void);
uint32_t WS281x_Color(uint8_t red, uint8_t green, uint8_t blue);
void WS281x_SetPixelColor(uint16_t n, uint32_t GRBColor);
void WS281x_SetPixelRGB(uint16_t n ,uint8_t red, uint8_t green, uint8_t blue);
void WS281x_Show(void);

void WS281x_RainbowCycle(uint8_t wait);
void WS281x_TheaterChase(uint32_t c, uint8_t wait);
void WS281x_ColorWipe(uint32_t c, uint8_t wait);
void WS281x_Rainbow(uint8_t wait);
void WS281x_TheaterChaseRainbow(uint8_t wait);

#endif

/********************************End of File************************************/

11.2)bsp_ws2812.c文件

#include "bsp_ws2812.h"

volatile uint8_t  ws2812_xfer_flag = 0;
uint8_t pixelBuffer[PIXEL_NUM][GRB] = {0};

/*******************************************************************************
** 函数名称: WS281x_CloseAll
** 功能描述:
********************************************************************************/
void WS281x_CloseAll(void)
{
    uint8_t i;
    uint8_t j;

    for(i = 0; i < PIXEL_NUM; ++i)
    {
        for(j = 0; j < 24; ++j)
        {
            pixelBuffer[i][j] = WS_LOW;
        }
    }
    WS281x_Show();
}

/*******************************************************************************
** 函数名称: WS281x_Color
** 功能描述:
********************************************************************************/
uint32_t WS281x_Color(uint8_t red, uint8_t green, uint8_t blue)
{
    return green << 16 | red << 8 | blue;
}

/*******************************************************************************
** 函数名称: WS281x_SetPixelColor
** 功能描述: 
********************************************************************************/
void WS281x_SetPixelColor(uint16_t n, uint32_t GRBColor)
{
    uint8_t i;
    if(n < PIXEL_NUM)
    {
        for(i = 0; i < GRB; i++)
        {
            pixelBuffer[n][i] = ((GRBColor << i) & 0x800000) ? WS_HIGH : WS_LOW;
        }
    }
}

/*******************************************************************************
** 函数名称: WS281x_SetPixelRGB
** 功能描述:
********************************************************************************/
void WS281x_SetPixelRGB(uint16_t n , uint8_t red, uint8_t green, uint8_t blue)
{
    uint8_t i;

    if(n < PIXEL_NUM)
    {
        for(i = 0; i < GRB; ++i)
        {
            pixelBuffer[n][i] = (((WS281x_Color(red, green, blue) << i) & 0X800000) ? WS_HIGH : WS_LOW);
        }
    }
}


/*******************************************************************************
** 函数名称: WS281x_Show
** 功能描述:
********************************************************************************/
void WS281x_Show(void)
{
    LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_7, LEN);
    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_7);
    LL_TIM_EnableCounter(TIM1);
    while(ws2812_xfer_flag); //dma发送完成后,会清零此flag。
}

/*******************************************************************************
** 函数名称: WS281x_Wheel
** 功能描述: Input a value 0 to 255 to get a color value. The colours are a transition r - g - b - back to r.
********************************************************************************/
uint32_t WS281x_Wheel(uint8_t wheelPos)
{
    wheelPos = 255 - wheelPos;
    if(wheelPos < 85)
    {
        return WS281x_Color(255 - wheelPos * 3, 0, wheelPos * 3);
    }
    if(wheelPos < 170)
    {
        wheelPos -= 85;
        return WS281x_Color(0, wheelPos * 3, 255 - wheelPos * 3);
    }
    wheelPos -= 170;
    return WS281x_Color(wheelPos * 3, 255 - wheelPos * 3, 0);
}


/*******************************************************************************
** 函数名称: WS281x_ColorWipe
** 功能描述: Fill the dots one after the other with a color
********************************************************************************/
void WS281x_ColorWipe(uint32_t c, uint8_t wait)
{
    for(uint16_t i = 0; i < PIXEL_NUM; i++)
    {
        WS281x_SetPixelColor(i, c);
        WS281x_Show();
        LL_mDelay(wait);
    }
}

/*******************************************************************************
** 函数名称: WS281x_Rainbow
** 功能描述:
********************************************************************************/
void WS281x_Rainbow(uint8_t wait)
{
    uint16_t i, j;

    for(j = 0; j < 256; j++)
    {
        for(i = 0; i < PIXEL_NUM; i++)
        {
            WS281x_SetPixelColor(i, WS281x_Wheel((i + j) & 255));
        }
        WS281x_Show();
        LL_mDelay(wait);
    }
}


/*******************************************************************************
** 函数名称: WS281x_RainbowCycle
** 功能描述: Slightly different, this makes the rainbow equally distributed throughout
********************************************************************************/
void WS281x_RainbowCycle(uint8_t wait)
{
    uint16_t i, j;

    for(j = 0; j < 256 * 5; j++) // 5 cycles of all colors on wheel
    {
        for(i = 0; i < PIXEL_NUM; i++)
        {
            WS281x_SetPixelColor(i, WS281x_Wheel(((i * 256 / PIXEL_NUM) + j) & 255));
        }
        WS281x_Show();
        LL_mDelay(wait);
    }
}


/*******************************************************************************
** 函数名称: WS281x_TheaterChase
** 功能描述: Theatre-style crawling lights.
********************************************************************************/
void WS281x_TheaterChase(uint32_t c, uint8_t wait)
{
    for (int j = 0; j < 10; j++) //do 10 cycles of chasing
    {
        for (int q = 0; q < 3; q++)
        {
            for (uint16_t i = 0; i < PIXEL_NUM; i = i + 3)
            {
                WS281x_SetPixelColor(i + q, c);  //turn every third pixel on
            }
            WS281x_Show();

            LL_mDelay(wait);

            for (uint16_t i = 0; i < PIXEL_NUM; i = i + 3)
            {
                WS281x_SetPixelColor(i + q, 0);      //turn every third pixel off
            }
        }
    }
}

/*******************************************************************************
** 函数名称: WS281x_TheaterChaseRainbow
** 功能描述: Theatre-style crawling lights with rainbow effect
********************************************************************************/
void WS281x_TheaterChaseRainbow(uint8_t wait)
{
    for (int j = 0; j < 256; j++)     // cycle all 256 colors in the wheel
    {
        for (int q = 0; q < 3; q++)
        {
            for (uint16_t i = 0; i < PIXEL_NUM; i = i + 3)
            {
                WS281x_SetPixelColor(i + q, WS281x_Wheel( (i + j) % 255)); //turn every third pixel on
            }
            WS281x_Show();

            LL_mDelay(wait);

            for (uint16_t i = 0; i < PIXEL_NUM; i = i + 3)
            {
                WS281x_SetPixelColor(i + q, 0);      //turn every third pixel off
            }
        }
    }
}

/********************************End of File************************************/

12.main.c中的使用
12.1)包含头文件

	#include "bsp_ws2812.h"

12.2)显示
只需要在需要显示的地方调用如下的函数就可以了,当然还有别的特效函数没有在此显示出来。

        WS281x_ColorWipe(30, 100);
        WS281x_ColorWipe(30 << 8, 100);
        WS281x_ColorWipe(30 << 16, 100);
        WS281x_TheaterChaseRainbow ( 20 );

13.附上输出波形
STM32使用LL库PWM的DMA模式驱动ws2812_第9张图片
一次PWM波的波形放大
STM32使用LL库PWM的DMA模式驱动ws2812_第10张图片
三、后记
之所以使用LL库,是因为在使用HAL库的时候,无法实现功能。封装的太狠了,导致使用的灵活性不够。
调用库函数TIM_PWM_START_DMA(…),只能正确传输一次,之后,DMA无法正确完成,无PWM产生,困了3天,选择用LL库来完成,结果简单很多。
github源代码仓库地址:github
视频链接:

stm32 LL库使用定时器pwm dma驱动ws2812

你可能感兴趣的:(STM32)