STM32 HAL库 Timer(定时器)+DMA输出PWM底层配置过程学习

这篇具有很好参考价值的文章主要介绍了STM32 HAL库 Timer(定时器)+DMA输出PWM底层配置过程学习。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

本文使用的芯片型号是STM32G030,写本文的目前是想记录学习下Timer借助DMA生成可变占空比PWM时的底层配置过程。

一、CUBEMUX生成项目

1. Timer配置

使用TIM1,配置就只改了图上的配置,系统时钟用的16M,分频选择15(16-1),自动重装载寄存器ARR选择999(1000-1),那么生成的就是1kHz的PWM,这里为什么要减1,因为这俩是从0开始计数,想知道公式计算的可以去搜一下,介绍的有很多。Clock Source(时钟源)选择内部时钟,也就是系统时钟。通道1选择PWM。
hal_tim_pwm_start_dma,stm32,学习

这里再解释下通道选择PWM模式有几个,CH1、CH1N还有CH1和CH1N的组合。对应到下边一张图的右边,通道指Capture/Compare x register,而每个通道有两个输出口,OC1和OC1N,这俩是相反的波形,就是对应选择的选项了CH1、CH1N,选择他俩就是同时输出了。
hal_tim_pwm_start_dma,stm32,学习
hal_tim_pwm_start_dma,stm32,学习

2. DMA配置

这是DMA的配置参数,这里选择循环发送,如果只发送一次,就看不出来PWM变化了,所有我改成了循环发送。这里的数据宽度为什么要选择半字是根据TIM1的CCR寄存器定的,看下边一张图,CCR寄存器就是控制PWM占空比的,他的大小就是16位的。
hal_tim_pwm_start_dma,stm32,学习
hal_tim_pwm_start_dma,stm32,学习

二、代码流程

1.代码

代码如下:

int main(void)
{
  /* USER CODE BEGIN 1 */
  uint16_t aDutyCycleArray[9] = {100, 200, 300, 400, 600, 700, 800, 900};
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();
  
  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */
  HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t*)aDutyCycleArray, 9);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
    /* USER CODE END 3 */
}

2.流程

MX_GPIO_Init()MX_DMA_Init() 这两个函数就不讲了。

1) MX_TIM1_Init()

该函数大部分都和不使用DMA一样,主要分析下添加的部分,这个全局变量是添加DMA需要的。

DMA_HandleTypeDef hdma_tim1_ch1;
    /* TIM1 DMA Init */
    /* TIM1_CH1 Init */
    hdma_tim1_ch1.Instance = DMA1_Channel1;
    hdma_tim1_ch1.Init.Request = DMA_REQUEST_TIM1_CH1;
    hdma_tim1_ch1.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_tim1_ch1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_tim1_ch1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_tim1_ch1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_tim1_ch1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_tim1_ch1.Init.Mode = DMA_CIRCULAR;
    hdma_tim1_ch1.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_tim1_ch1) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(tim_baseHandle,hdma[TIM_DMA_ID_CC1],hdma_tim1_ch1);

进入HAL_DMA_Init 函数,

1、hdma->ChannelIndex = (((uint32_t)hdma->Instance - (uint32_t)DMA1_Channel1) / ((uint32_t)DMA1_Channel2 - (uint32_t)DMA1_Channel1)) << 2U;
/* Clear PL, MSIZE, PSIZE, MINC, PINC, CIRC, DIR and MEM2MEM bits */
2CLEAR_BIT(hdma->Instance->CCR, (DMA_CCR_PL    | DMA_CCR_MSIZE  | DMA_CCR_PSIZE  | \
                                  DMA_CCR_MINC  | DMA_CCR_PINC   | DMA_CCR_CIRC   | \
                                  DMA_CCR_DIR   | DMA_CCR_MEM2MEM));
/* Set the DMA Channel configuration */
3SET_BIT(hdma->Instance->CCR, (hdma->Init.Direction           |                               \
                                hdma->Init.PeriphInc           | hdma->Init.MemInc           | \
                                hdma->Init.PeriphDataAlignment | hdma->Init.MemDataAlignment | \
                                hdma->Init.Mode                | hdma->Init.Priority));
/* Initialize parameters for DMAMUX channel :
     DMAmuxChannel, DMAmuxChannelStatus and DMAmuxChannelStatusMask
  */
4DMA_CalcDMAMUXChannelBaseAndMask(hdma);
/* Set peripheral request  to DMAMUX channel */
5、hdma->DMAmuxChannel->CCR = (hdma->Init.Request & DMAMUX_CxCR_DMAREQ_ID);
/* Clear the DMAMUX synchro overrun flag */
6、hdma->DMAmuxChannelStatus->CFR = hdma->DMAmuxChannelStatusMask;
  1. 绑定当前hdma_tim1_ch1变量用的是通道几,在配置阶段我们配置的是通道1,这款芯片总共有5个DMA通道。
  2. 第2点和第3点一起讲,就是DMA的CCR寄存器先清空再根据我们上一张图设置的参数来设置该寄存器。
  3. 总结到第2点。
  4. DMA_CalcDMAMUXChannelBaseAndMask 主要用于建立DMA和DMAMUX通道的连接,DMAMUX相当于DMA和Timer之间加的一个请求器,DMA可以帮很多模块搬东西,但怎么去与不同的模块连接就靠DMAMUX这个中间件来完成。
  5. 建立DMAMUX和Timer的连接,这就相当于,上一步把DMAMUX的一头连接到了DMA,这一步把DMAMUX的另一头连接到了Timer。
  6. 清除标志位,这个同步标志位我也不懂,如果有看懂的也可以留言教教我,哈哈哈。

再回头看看这行代码

__HAL_LINKDMA(tim_baseHandle,hdma[TIM_DMA_ID_CC1],hdma_tim1_ch1);

tim_baseHandle就是传入的TIM1 ,看下TIM1 的结构体定义,hdma 是个DMA_HandleTypeDef类型的指针数组。
hal_tim_pwm_start_dma,stm32,学习
他的宏是这样的,

#define __HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD__, __DMA_HANDLE__)               \
                        do{                                                      \
                              (__HANDLE__)->__PPP_DMA_FIELD__ = &(__DMA_HANDLE__); \
                              (__DMA_HANDLE__).Parent = (__HANDLE__);             \
                          } while(0U)

TIM_DMA_ID_CC1这个的值是1,带入参数展开后就是,就是这个指针数组的1号位置 = hdma_tim1_ch1变量的地址。

TIM1->hdma[TIM_DMA_ID_CC1] = &hdma_tim1_ch1;
hdma_tim1_ch1.Parent = TIM1;

2)HAL_TIM_PWM_Start_DMA()

HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t*)aDutyCycleArray, 9);

这里有个疑点就是我们前边设置的搬运Memory的宽度是16位,为什么这里要转成32位呢,其实就是这个函数要求要传32位的,但搬运的时候是按照16位来搬,除非代码里用到地址自增自减,否则是不会有影响的。
进入到函数,跳过不重要的部分。

  • 这里的hdma[TIM_DMA_ID_CC1] 就等于hdma_tim1_ch1。先是设置了DMA传输的回调函数,当DMA传输到相应状态时就会触发中断,从而调用这里设置的回调函数。然后开启了DMA通道HAL_DMA_Start_IT,最后打开Timer的DMA请求使能 __HAL_TIM_ENABLE_DMA, 当Timer开启后就会通知DMA搬运数据过来。
  ...
  switch (Channel)
  {
    case TIM_CHANNEL_1:
    {
      /* Set the DMA compare callbacks */
      htim->hdma[TIM_DMA_ID_CC1]->XferCpltCallback = TIM_DMADelayPulseCplt;
      htim->hdma[TIM_DMA_ID_CC1]->XferHalfCpltCallback = TIM_DMADelayPulseHalfCplt;

      /* Set the DMA error callback */
      htim->hdma[TIM_DMA_ID_CC1]->XferErrorCallback = TIM_DMAError ;

      /* Enable the DMA channel */
      if (HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC1], (uint32_t)pData, (uint32_t)&htim->Instance->CCR1, Length) != HAL_OK)
      {
        /* Return error status */
        return HAL_ERROR;
      }

      /* Enable the TIM Capture/Compare 1 DMA request */
      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC1);
      break;
    }
    ...

进入到HAL_DMA_Start_IT,省略了一些过程,就是开启DMA连接Memory和Timer通道以及DMA中断。

/* Disable the peripheral */
    __HAL_DMA_DISABLE(hdma);
/* Configure the source, destination address and the data length & clear flags*/
    DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength);
	
	__HAL_DMA_DISABLE_IT(hdma, DMA_IT_HT);
    __HAL_DMA_ENABLE_IT(hdma, (DMA_IT_TC | DMA_IT_TE));

/* Enable the Peripheral */
    __HAL_DMA_ENABLE(hdma);
  • 返回到上一层函数,接着往Timer的DMA请求使能往后讲,开启比较通道寄存器,开启输出,最后开启Timer,开始生成PWM,并从DMA出传入数据到CCR寄存器。
/* Enable the Capture compare channel */
  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
/* Enable the main output */
  __HAL_TIM_MOE_ENABLE(htim);
    
  __HAL_TIM_ENABLE(htim);

三、结果展示

最后给张结果展示图,我设置的CCR的值从100到900不断循环,ARR的值又为1000,所以占空比就是10%~90%不断变化。
hal_tim_pwm_start_dma,stm32,学习文章来源地址https://www.toymoban.com/news/detail-845640.html

到了这里,关于STM32 HAL库 Timer(定时器)+DMA输出PWM底层配置过程学习的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 定时器详解 -- 定时器中断、PWM输出 --stm32

    STM32F103系列芯片拥有多种定时器,包括基本定时器、通用定时器和高级定时器,每种定时器都具有一些特定的功能。 向上计数:计数器从0计数到自动重装载值(ARR),然后重新从0开始计数并且产生一个计数器溢出事件。 向下计数:计数器从自动重装载值(ARR)开始向下计数

    2024年02月11日
    浏览(26)
  • STM32定时器输出PWM脉冲

    一、什么是PWM脉冲? PWM (Pulse Width Modulation) 脉冲宽度调制,通常应用于惯性系统中,( 不是不能即停的设备,因为如果设备不具有关心,那么运动是断断续续的,不具有连续性 ),通过脉宽调节输出不同的波形作用域受控对象。 二、 PWM中三个重要的参数 1.频率=1/Ts ;这个很

    2024年02月15日
    浏览(25)
  • STM32 HAL库定时器触发DMA并口数据传输

    STM32与FPGA通讯,通过8位并口线进行通讯,16byte的数据在10us之内通过8位并口数据线传给FPGA,FPGA读取该数据。 时钟采用80MHz,由于16byte的数据要在10us之内传完,那么10/(16*2)=0.3125us/次,也就是传输频率得≥3.2MHz。 定时器设置:为了方便起见,先选用了4MHz的传输频率。80MHz

    2024年02月02日
    浏览(27)
  • STM32F1定时器-PWM输出

    STM32 PWM工作过程 ARR寄存器决定PWM周期,CCR寄存器决定占空比 通道1为例 的PWM输出电路图 CCR1:捕获比较(值)寄存器(x =1,2,3,4):设置比较值。 CCMR1:OC1M[2:0]位:对于PWM方式下,用于设置PWM模式1【110】 或者PWM模式2【111】 CCER:CC1P位:输入/捕获1输出极性。0:高电平有效,1:低电平

    2024年02月09日
    浏览(33)
  • STM32TIM定时器PWM输出比较(适用于通用,高级定时器)

    在定时器中我们最常用的功能就是输出PWM,大多是用在电机控制方面,目前网络上相关资料也有很多,但是,很多不利于我们“现搜现用”我这里不是说我写的有多好,而是你搜索到此类文章时大部分是急于解决目前的问题,一段相关代码和讲解就行,当然不是学习背后的原

    2024年01月25日
    浏览(25)
  • STM32(7)-定时器输出PWM的原理分析

    概念+代码 OC(Output Compare)输出比较 输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形 每个高级定时器和通用定时器都拥有4个输出比较通道 高级定时器的前3个通道额外拥有死区生成和互补输出的功

    2024年02月04日
    浏览(27)
  • STM32使用高级定时器输出互补pwm波

    最近做的一个项目用到stm32,网上查了很多资料,也踩了很多坑,这里记录一下配置的步骤和说明 硬件使用的是stm32h750vbt6; 软件用到了stm32cubemx和keil5; 打开Debug模式方便调试,可以忽略,不影响代码运行 在用cube配置时钟源时,有下面三个选项 Disable(禁用) BYPASS Clock Sou

    2024年02月16日
    浏览(20)
  • STM32使用CubeMx配置定时器输出PWM

            项目中经常使用到STM32来输出PWM,每次配置过后过不了多久就会忘记,稍微需要对配置做出修改时都要翻很久的手册,所以决定结合实例把PWM配置的详细步骤记录下来,这样在下次配置时可以很快的捡起来。         本文档的行文结构如下,首先,说明实际需

    2024年02月03日
    浏览(32)
  • stm32定时器输出pwm&IO口模拟pwm——呼吸灯

    什么是pwm波?pwm就是脉冲宽度调制,就是占空比可变的脉冲波形,通过改变占空比,输出不同的pwm波,就能实现许多有趣的功能,比如说我们生活中常见的呼吸灯就是通过这样实现的。接下来我们就以呼吸灯为例,学习stm32通过定时器输出pwm波和io口模拟输出pwm波。 控制方式就

    2024年01月18日
    浏览(27)
  • STM32笔记——定时器输出比较功能(产生PWM波)

    目录 一、概述 二、PWM简单介绍  三、通用定时器输出比较 3.1 输出比较简介 3.2 输出比较通道 3.3 产生PWM的过程 四、实验硬件介绍及PWM模块程序 4.1 舵机简介 4.2 直流电机及驱动芯片TB6612  4.3 PWM模块驱动程序         主要介绍通用定时器输出比较功能,在GPIO口输出PWM来控

    2024年02月13日
    浏览(14)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包