这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【分享开发笔记,赚取电动螺丝刀】使用STM32F103的hal库,采用PWM+D

共10条 1/1 1 跳转至

【分享开发笔记,赚取电动螺丝刀】使用STM32F103的hal库,采用PWM+DMA发送方式驱动WS2812的RGB彩灯

工程师
2025-04-02 09:06:02     打赏

    简单和大家介绍一下本文章的主要内容:使用STM32F103C8最小系统板,使用STM32 cubeMX 6.14版本生成底层的驱动库、结合定时器的PWM 输出功能、使用DMA发送数据的 方式,驱动WS2812 的RGB三色灯。

    本次小的DIY所需的物料:stm32f103c8系统板、DAP调试器(无线版本)、杜邦线(7根)、WS2812 RGB三色灯、

    备注:为了调试方便,自己绘制了一块系统板的拓展板,引出了所有的GPIO口。

一:RGB三色灯的介绍:

    WS2812俗称:三色 RGB 灯,其中 LED 灯珠内部存在一个芯片控制控制芯片,通讯方式采用单线归零码的方法,使用DIN信号线来发送特定的时序数据,控制灯的工作与否,如果额外增加一路信号线,实现双路信号传输,即使存在某个LED灯异常损坏的时候,也不会影响整体显示效果。

二:WS2812b功能特点:

1.5050 灯珠内部集成高质量外控单线级联恒流 IC和优质 RGB LED 芯片,体积小巧,外围简单。The 5050ball 2.内置 IC恒流精度高,内部 RGB 芯片预先分光处理。发光高度一致,白光效果纯正。3.整形转发强化技术,单线数据传输,可无限级联。4.数据传输频率 800Kbps/秒,可实现画面刷新速率 30 幀/秒时,不小于 1024 点。5.输出端口 PWM 控制能够实现 256 级灰度调节,端口扫描频率 1.5KHz/s。6.采用优化预置 12mA/通道恒流模式,低压驱动级联数量最大化。高恒流精度,片内误差<1.5%,片间误差<3%。 7.内置低压强化模块,VDD在 4.5-5.5V 以上 100%正常工作。8.超强数据整形能力:接受完本单元数据自动将后续数据整形输出。

三:驱动方式:

1.png

驱动的时序图:

2.png

a:LED灯珠主要根据高电平时间判断“0”码和“1”码。高电平时间介于 200ns~410ns,IC判断为“0”码,高电平时间介于 640ns~1000ns,判断为“1”码。 “0”码和“1”码的低电平代表此码结束,准备接收下一数据码。 

b:低电平复位时间最小为100us,为了留有余度,一帧数据传输过程中(包括 24bit 和 24bit 之间、bit和 bit 之间)不要中断超过 35us,否则可能会被 IC 认为是RESET。中断时间在 35us之内,控制器可以进行正常数据传输等其他操作。

这里我使用的方式是:改变PWM脉宽的大小,即采用不同占空比的方式实现数据的输出。

四:PWM输出的方式:

之前在论坛分享了定时器输出PWM的方式,链接如下所示:

NUCLEO-U083RC学习历程5-PWM调试-电子产品世界论坛

https://forum.eepw.com.cn/thread/386882/1

这里不同于之前的帖子,这里使用PWM与DMA的方式发送不同的脉宽;

实现的主要思路如下:

    采用定时器输出不同脉宽的PWM,并且结合DMA技术能够生成数量和占空比可编程控制的脉冲序列,主要利用DMA的自动数据传输功能,不占据CPU的资源,基本上不用担心被其他的任务打断。当定时器使能DMA时,每次计数器达到溢出值后,软件代码自动通过DMA总线获取新的比较值数据。通过动态改变DMA传输的比较值数据,即可实现每个PWM周期占空比的灵活调整。在本次项目中采用预定义数组存储比较值序列,由DMA控制器按需传输数组元素。通过配置数组长度(控制脉冲数量)和元素数值(决定各周期占空比),最终实现脉冲数量和占空比的双重可编程控制。

几个重要和大家分享一下:

工作机制:定时器溢出触发DMA传输,自动更新比较寄存器

动态调节:差异化的DMA传输数据实现占空比动态变化

实现方式:1:比较值序列存储在预定义数组。2:DMA负责数组元素的自动传输

控制难点:1:数组长度决定输出脉冲数量。2:数组元素值决定单个脉冲占空比

五:STM32 cube MX 软件配置如下:

对于基本的STM32 cube的配置,请大家移步到之前的帖子,这里i就不做介绍:

【DIY手势翻页笔】手势翻页笔-过程贴:使用cubx生成代码点亮板载的LED灯-电子产品世界论坛

https://forum.eepw.com.cn/thread/383040/1

大概的配置过程,如上所示,只是软件程序版本不一致,使用的主控不一致,

这里仅仅说明此处定时器输出PWM的配置图:

3.png

4.png

5.2  代码编写

在生成的代码中添加对 RGB灯的驱动过程

定时器2的初始化

static void MX_TIM2_Init(void)
{

  /* USER CODE BEGIN TIM2_Init 0 */

  /* USER CODE END TIM2_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  /* USER CODE BEGIN TIM2_Init 1 */

  /* USER CODE END TIM2_Init 1 */
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 89;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_Init 2 */
  HAL_TIM_MspPostInit(&htim2);

}

定时器的DMA配置初始化:

    __HAL_RCC_TIM2_CLK_ENABLE();

    /* TIM2 DMA Init */
    /* TIM2_CH1 Init */
    hdma_tim2_ch1.Instance = DMA1_Channel5;
    hdma_tim2_ch1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_tim2_ch1.Init.PeriphInc = DMA_PINC_ENABLE;
    hdma_tim2_ch1.Init.MemInc = DMA_MINC_DISABLE;
    hdma_tim2_ch1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_tim2_ch1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_tim2_ch1.Init.Mode = DMA_CIRCULAR;
    hdma_tim2_ch1.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_tim2_ch1) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(tim_pwmHandle,hdma[TIM_DMA_ID_CC1],hdma_tim2_ch1);

WB2812的初始化:

void ws2812_init(uint8_t data)
{
	uint16_t num;
	num = 60 + data * 24;
	for(uint8_t i = 0; i < data; i++)
	{
		ws2812_set_RGB(0x00, 0x00, 0x00, i);
	}
	 HAL_TIM_PWM_Start_DMA(&htim2,TIM_CHANNEL_1,(uint32_t *)RGB_buffur,(num)); 
}

WB2812的实现逐渐变亮驱动函数

void ws2812_breath(uint8_t color,uint16_t data,uint16_t run_time)
{
	
	if(rgb_run_time>=run_time)
	{		
		if(level<=breath_level)//渐亮
		{
			switch (color)
			{
				case red:   ws2812_red(breath_R+=breath_change_sum,data) ;break;
				case green: ws2812_green(breath_G+=breath_change_sum,data) ;break;
				case blue:  ws2812_blue(breath_B+=breath_change_sum,data) ;break;
				case yellow:ws2812_yellow(breath_B+=breath_change_sum,data) ;break;
			}
		}
	}
}

在主程序中调用该函数即可

HAL_Delay(20);
ws2812_breath(green,2,500);

图片测试如下、

5.png

5.png

GIF图 如下所示:

WeChat_20250402092325 00_00_00-00_00_30.gif



院士
2025-04-02 09:30:33     打赏
2楼

占空比通过DMA来设置,这个方法真棒啊

给楼主赞一下



高工
2025-04-02 09:43:12     打赏
3楼

意思是,定时器更新PWM值?然后PWM周期与定时器触发时长一致?但有一点不明白,这种情况下,DMA存在的意义在哪?


工程师
2025-04-02 15:17:11     打赏
4楼

硬件链接:使用PA15引脚,连接到WS2812的 DIN 引脚:可执行文件如下:

RGB.zip



高工
2025-04-02 19:29:04     打赏
5楼

DMA+PWM 的配置之前确实没使用过,学习一波


工程师
2025-04-03 16:41:50     打赏
6楼

今天整理了一下,代码 感兴趣的可以下载看下

RGB.zip


工程师
2025-04-04 09:02:16     打赏
7楼

大佬,DMA传输的事件是如何处理的,比如结束、半传输、出错等?


专家
2025-04-07 14:21:25     打赏
8楼

思路不错,学习了!


专家
2025-04-08 12:11:55     打赏
9楼

嵌入式开发就是这样,大家集思广益,相互提高,跟着楼主学习了一招。


助工
2025-04-08 14:35:56     打赏
10楼

文章结构化, 驱动时序清晰. 效果展示直观清晰


共10条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]