HAL库(STM32CubeMX)——基本定时器、PWM、输入捕获、输出比较、互补式PWM等综合学习(STM32G431RBT6)

目录

通用定时器中断配置(Basic timers):

通用定时器/高级定时器(General-purpose timers/Advanced-control timers):

PWM配置:

PWM互补输出:

输出比较实现PWM:Out Compare模式

输入捕获模式: 

                        1.普通捕获:

                        2.PWM输入模式(输入捕获的一种特殊形式):


  • 通用定时器中断配置(Basic timers):

可以在库中判断具体挂在哪个总线上:(stm32g4xx_hal_rcc.h)

void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();      //可以根据这个直接go to 定义,就在stm32g4xx_hal_rcc.h中
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();

我认为TIM6是挂在APB1上面的,所以是80MHz。 这里对TIM6进行配置:

 定时器的频率为80M/(79+1)/(999+1)=1KHz,即1ms一次中断。

这里对于这个Trigger Event Selection不懂。

基本定时器的功能函数(轮询、中断、DMA) 

我先学习中断处理:

1.在main函数中开启中断:

  HAL_TIM_Base_Start_IT(&htim6);

2.重新定义回调函数并添加自己想要实现的功能(我只用LED闪烁简单测试):

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
   HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
   HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_8);
}
  •  通用定时器/高级定时器(General-purpose timers/Advanced-control timers):

或者在stm32G431RB数据手册中寻找 block diagram(框图)

TIM15挂在APB2上: 

 同样开启中断、代码相似。自动生成tim.h和tim.c文件:

#include "tim.h"

TIM_HandleTypeDef htim6;
TIM_HandleTypeDef htim15;

在main函数中:

HAL_TIM_Base_Start_IT(&htim6);
HAL_TIM_Base_Start_IT(&htim15);

 自己编写对应的函数中断:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM15)
	{
   HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
   HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_8);
	}
	else if(htim->Instance==TIM6)
	{
	 HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
   HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_9);
		
	}
}

if中也可以使用htim->Instance == htim1.Instance 例如这里:if(htim->Instance==htim15.Instance)

 对于instance在TIM的定义中可以看到:(可以区分进入中断的不同定时器)

void MX_TIM15_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  htim15.Instance = TIM15;                             //不同的定时器的instance不同
  htim15.Init.Prescaler = 7999;
  htim15.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim15.Init.Period = 9999;
  htim15.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim15.Init.RepetitionCounter = 0;
  htim15.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim15) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim15, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim15, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

 PWM配置:

图片来自B站UP:PetraKing

 详细自动重装载见:自动重装载详解

频率的计算是一样的,pwm占空比的设置是Pulse/Counter Period计算的。

 

 初始化只需要在main函数中添加:

HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);

或者:(IT:interruption中断)要对应打开嵌套向量中断控制器

HAL_TIM_PWM_Start_IT(&htim3,TIM_CHANNEL_1);

这里对优先级不配置:有需要的可以配置 。

更改PWM的频率和占空比:

 通过以上可知,PWM的占空比改变的是CCR寄存器的值(不同通道对应不同的CCR),频率的改变对应的是ARR寄存器的值,也就是下图 

 至于CCR寄存器,我们可以看到初始化中,在sConfigOC.Pulse赋值给我们想要的值。

 而在HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1)的定义中,我们能找到 TIM_OC1_SetConfig(htim->Instance, sConfig);类似的函数,而这个函数中的定义中,

 就能找到实际把Pulse的值给了TIM的CCR寄存器。

__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,100);//占空比
__HAL_TIM_SET_AUTORELOAD(&htim3,9999);//周期

 所以就可以通过这两个来改变占空比和周期(频率)。

PWM互补输出:

用不到,先暂时不学: 互补输出配置示例

输出比较实现PWM:Out Compare模式

 图片来自B站UP:PetraKing

 左边是通用定时器,只有一个自动重装载(ARR),但是有多个CCR,所以同一个定时器下面的通道只有一个频率,但是可以有多个占空比。

上图为详细的原理,讲的很清楚,如果看不懂的话建议找一下这个up的视频看一看。

具体配置:

 这里大概说一下自己的看法:中断是肯定要打开的,因为要不断改CCR的值,这里的预分配和你想写的pulse决定了频率,所以这个ARR是没有用的,至于自动重装载还是打开吧,不然会有什么bug吧,我这里预分频后是80M/8000=10Khz,我想配置的500个的高电平和300个低电平,那么总共是800个,实际的频率就是(800/10k)hz。

具体程序:

	HAL_TIM_OC_Start_IT(&htim16,TIM_CHANNEL_1);

__HAL_TIM_GET_COUNTER也是一个宏定义。 
在stm32f4xx_hal_tim.h文件中可以找到。其作用是获取定时器某一通道的捕获/比较寄存器值

等价于 :  HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_1);

void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
   if(htim->Instance==htim16.Instance)
	 {
		 if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)
		 {
		   if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12)==GPIO_PIN_RESET)
			 {
				 
				 __HAL_TIM_SET_COMPARE(&htim16,TIM_CHANNEL_1,__HAL_TIM_GET_COUNTER(&htim16)+300);
			 }
			 else
			 {
			 	 __HAL_TIM_SET_COMPARE(&htim16,TIM_CHANNEL_1,__HAL_TIM_GET_COUNTER(&htim16)+500);
			 }	 
		 }	 
	 }
}

 如果不清楚这if怎么写,可以去定义里面找:

去找TIM_HandleTypeDef的定义:

再去找HAL_TIM_ActiveChannel的定义:(就找到了)(枚举)

没有示波器,只用万用表简单测试了一下,感觉电压差不多,然后再去找示波器看一下吧,主要是STM32G431RB好像KEIL不支持它的虚拟示波器,我也没逻辑测试仪,然后看玩再放图吧。


更新:2022.4.7

使用示波器看过了,程序没有问题,注意使用的是__HAL_TIM_GET_COUNTER,不是__HAL_TIM_GET_COMPARE。

同时如果只是简单的配置不同频率的50%pwm,不需要判断高低电平了,这杨相当各200的高电平和低电平

void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
	  if(htim->Instance==TIM4)
  {
				if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
			{			
			  __HAL_TIM_SET_COMPARE(htim,TIM_CHANNEL_1,(__HAL_TIM_GetCounter(htim)+200));//5Khz
					
	}	
}

 输入捕获模式: 

输入捕获可以对输入的信号的上升沿,下降沿或者双边沿进行捕获,常用的有测量输入信号的脉
宽和测量 PWM 输入信号的频率和占空比这两种。

1.普通捕获:

 main函数中:

  HAL_TIM_IC_Start_IT(&htim4,TIM_CHANNEL_1);

输入捕获的中断回调函数: 

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{

	if(htim->Instance==htim4.Instance)
	 {
		 if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)
		 {
			 PWM_count=__HAL_TIM_GET_COUNTER(&htim4);
			 __HAL_TIM_SET_COUNTER(&htim4,0);
			 frequency=1000000/PWM_count;
		 }	 
	 }
}

 读出当前计数值,清空,频率就是之前配置的80M/(79+1)/__HAL_TIM_GET_COUNTER()

2.PWM输入模式(输入捕获的一种特殊形式):

 在main函数中:

HAL_TIM_IC_Start_IT( &htim2, TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT( &htim2, TIM_CHANNEL_2);

 在中断回调函数中:

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{

  if(htim->Instance==htim2.Instance)
	 {
	 		 if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)
		 {
				PWM_count1=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1)+1;
		 }	 
		 else if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_2)
		 {
		    PWM_count2=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_2)+1;
		 }
	 
	 }
}

由此读出来PWM_count1是总周期数,PWM_count2是低电平数 。

 我认为input capture direct mode是读取整个周期的,把他设置为上升沿就会在高电平来到时触发,input capture indirect mode则也同时开始,然后再下降沿到来时停止计数,而前一个则一直计算到下一个上升沿。(最后结果都需要加1).

剩下还有很多不会的不理解的,以后慢慢加深学习吧。


对输入捕获的理解,当我们配置了某个定时器的某个通道为输入捕获时,对一个未知的pwm进行采集,比如设置在高电平时触发的话,在高电平来到后,记录此时该定时器的CNT寄存器,即该定时器在预分频下采集到的计数值,然后记得每次采集完清空该CNT,否则就会累加。

例如:预分频设置为79,而该时钟是80M的,比如CNT的值是1000,那么这个未知的pwm的频率就是80M/(79+1)/1000

  • 27
    点赞
  • 93
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值