[其他ST产品] STM32定时器输入捕获

[复制链接]
1602|35
 楼主| qn7a12 发表于 2023-8-27 11:51 | 显示全部楼层 |阅读模式
STM32定时器输入捕获

STM32F429做定时器捕获PWM波形,测出波形的周期、频率以及占空比、正向脉宽。

基本原理

定时器的输入捕获主要是为了测量输入信号的频率,脉宽,占空比等信息。

需要理解stm32定时器的基本结构

5835864eac836be626.png


 楼主| qn7a12 发表于 2023-8-27 11:51 | 显示全部楼层
主要理解这些框起来的是重点,都是本人自己的理解,才疏学浅,万一有理解错的还望指正。

至于上半部分的时钟没有太难理解的。下面的通道理解上才比较复杂。
 楼主| qn7a12 发表于 2023-8-27 11:51 | 显示全部楼层
首先一个通用定时器有4个输入通道4个通道,这些通道可以空着也可以复用到对应的GPIO上去,
 楼主| qn7a12 发表于 2023-8-27 11:51 | 显示全部楼层
  1. /*
  2.         可以输出到GPIO的TIM通道:

  3.         TIM1_CH1, PA8,        PE9,
  4.         TIM1_CH2, PA9,        PE11
  5.         TIM1_CH3, PA10,        PE13
  6.         TIM1_CH4, PA11,        PE14

  7.         TIM2_CH1, PA15 (仅限429,439) 407没有此脚
  8.         TIM2_CH2, PA1,        PB3
  9.         TIM2_CH3, PA2,        PB10
  10.         TIM2_CH4, PA3,        PB11

  11.         TIM3_CH1, PA6,  PB4, PC6
  12.         TIM3_CH2, PA7,        PB5, PC7
  13.         TIM3_CH3, PB0,        PC8
  14.         TIM3_CH4, PB1,        PC9

  15.         TIM4_CH1, PB6,  PD12
  16.         TIM4_CH2, PB7,        PD13
  17.         TIM4_CH3, PB8,        PD14
  18.         TIM4_CH4, PB9,        PD15

  19.         TIM5_CH1, PA0,  PH10
  20.         TIM5_CH2, PA1,        PH11
  21.         TIM5_CH3, PA2,        PH12
  22.         TIM5_CH4, PA3,        PI10

  23.         TIM8_CH1, PC6,  PI5
  24.         TIM8_CH2, PC7,        PI6
  25.         TIM8_CH3, PC8,        PI7
  26.         TIM8_CH4, PC9,        PI2

  27.         TIM9_CH1, PA2,  PE5
  28.         TIM9_CH2, PA3,        PE6

  29.         TIM10_CH1, PB8,  PF6

  30.         TIM11_CH1, PB9,  PF7

  31.         TIM12_CH1, PB14,  PH6
  32.         TIM12_CH2, PB15,  PH9

  33.         TIM13_CH1, PA6,  PF8
  34.         TIM14_CH1, PA7,  PF9
 楼主| qn7a12 发表于 2023-8-27 11:52 | 显示全部楼层
但是一个通道只能配置成一种模式,比如配置成输出模式,就不能配置成输入模式,虽然一个通道有好几个GPIO可以设置,但是设置完一个其他就不能在设置在这个通道了。
 楼主| qn7a12 发表于 2023-8-27 11:52 | 显示全部楼层
因为要捕获输入的信号,所以设置为输入模式,关键看上图左侧的通道关系

TI1到TI4表示4个输入通道,但是输入通道并不是和定时器的通道直接连起来的,经过了输入滤波器和边沿检测器之后,一个输入通道就分成了两个,可以分别送到两个定时器通道去,比如TI1FP1,TI1FP2,这都是第一个输入通道分出来的,可以分别送给IC1和IC2,IC的意思就是输入通道,这样在后边定时器的捕获通道就可以一个捕获上升沿,一个捕获下降沿,这样就可以把一路PWM的频率和占空比都测出来了。
 楼主| qn7a12 发表于 2023-8-27 11:52 | 显示全部楼层
再接下来还有重要的问题是每个定时器的时钟速度。
907164eac8767fdc6.png
 楼主| qn7a12 发表于 2023-8-27 11:54 | 显示全部楼层
可以在时钟树上看到APB1和APB2上的定时器的时钟速度是不一样的。

  1.         APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14
  2.         APB2 定时器有 TIM1, TIM8 ,TIM9, TIM10, TIM11
  3.        

  4.         APB1 定时器的输入时钟 TIMxCLK = SystemCoreClock / 2; 90M
  5.         APB2 定时器的输入时钟 TIMxCLK = SystemCoreClock; 180M
 楼主| qn7a12 发表于 2023-8-27 11:54 | 显示全部楼层
因此在配置定时器的分频系数,和最后计算信号频率时要注意使用到的定时器的频率。

输入捕获计算信号的频率和占空比的原理是:

假设计数器的计数方向是向上,定时器会按照分频之后的速度,一直向上累加,当遇到设置好的边沿时,就会把当前计数的值存下来,通过读取这个值就可以知道在什么时刻检测到了边沿,通过不同边沿时刻的差值,就可以计算出频率等信息。
 楼主| qn7a12 发表于 2023-8-27 12:01 | 显示全部楼层
代码
使用Cubemx生成代码

361764eaca7a3a9b0.png
 楼主| qn7a12 发表于 2023-8-27 12:01 | 显示全部楼层
这个定时器配置来检测PWM信号。

前两个选项slave mode用来配置TI1FP1触发,而且触发之后的结果是reset,为什么要这么设置,关键是后边代码实现的原理,这么设置的意思是,当TI1FP1触发之后,整个计数器重置,从0开始数,如果前边不设置这么滤波器、分频器的话,TI1FP1就是第一路的输入信号,比如设置为搞电平触发之后,一旦来了高电平,之前全部复位开始计时,这就好比是PWM信号的高电平来了,全部重置,然后第1个定时器通道开始计时,直到遇到下一个上升沿的时候才会记录下数值,而第二路定时器通道也同时开始计时,直到遇到下降沿才记录数值。不难发现,第一路记录的数值就是整个周期的数值,而第二个通道记录的数值是上升沿到下降沿之间的数值,就是PWM正向脉宽的数值,这样就得出了想要的结果,所以要设置成这种模式。
———————————————
 楼主| qn7a12 发表于 2023-8-27 12:01 | 显示全部楼层
下面两个选项是定时器的两个通道的选择,回到结构框图去看,通道一的输入可以有两个选择,TI1FP1和TI2FP1,那选择第一个就是直连,选择第二个就是非直连,第二路的选择也是同样的道理。硬件配置为通道1的GPIO,所以直接选择通道1直连,通道2非直连。
 楼主| qn7a12 发表于 2023-8-27 12:01 | 显示全部楼层
接下来的配置都比较明白,在下方的参数选择中,一个上升沿触发,一个下降沿触发。

下面主要涉及到3个函数:
 楼主| qn7a12 发表于 2023-8-27 12:02 | 显示全部楼层
  1. TIM_HandleTypeDef htim3;
  2. __IO uint16_t IC2Value = 0;
  3. __IO uint16_t IC1Value = 0;
  4. __IO float DutyCycle = 0;
  5. __IO float Frequency = 0;



  6. void bsp_SetPWMCapture()
  7. {
  8.           /* USER CODE BEGIN TIM3_Init 0 */

  9.   /* USER CODE END TIM3_Init 0 */

  10.   TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  11.   TIM_SlaveConfigTypeDef sSlaveConfig = {0};
  12.   TIM_MasterConfigTypeDef sMasterConfig = {0};
  13.   TIM_IC_InitTypeDef sConfigIC = {0};
  14. /*
  15. 上面是3个句柄结构体

  16. */
  17.   /* USER CODE BEGIN TIM3_Init 1 */

  18.   /* USER CODE END TIM3_Init 1 */
  19.   htim3.Instance = TIM3;
  20.   htim3.Init.Prescaler = 90-1;
  21.   htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  22.   htim3.Init.Period = 65535;
  23.   htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  24.   htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  25.   /*
  26. 上面主要设置了一些分频系数

  27. */
  28.   if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  29.   {
  30.     Error_Handler(__FILE__, __LINE__);
  31.   }
  32.   sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  33.   if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  34.   {
  35.     Error_Handler(__FILE__, __LINE__);
  36.   }
  37.   if (HAL_TIM_IC_Init(&htim3) != HAL_OK)
  38.   {
  39.     Error_Handler(__FILE__, __LINE__);
  40.   }
  41.   sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;
  42.   sSlaveConfig.InputTrigger = TIM_TS_TI1FP1;
  43.   sSlaveConfig.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
  44.   sSlaveConfig.TriggerFilter = 0;
  45.   /*
  46. 上面设置的是从模式,比如设置为复位模式,TI1FP1上升沿触发

  47. */
  48.   if (HAL_TIM_SlaveConfigSynchro(&htim3, &sSlaveConfig) != HAL_OK)
  49.   {
  50.     Error_Handler(__FILE__, __LINE__);
  51.   }
  52.   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  53.   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  54. /*
  55. 上面这里对于输入捕获来说没有用到

  56. */
  57.   if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  58.   {
  59.     Error_Handler(__FILE__, __LINE__);
  60.   }
  61.   sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
  62.   sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
  63.   sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
  64.   sConfigIC.ICFilter = 0;
  65.   /*
  66. 上面是通道1的配置参数

  67. */
  68.   if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
  69.   {
  70.     Error_Handler(__FILE__, __LINE__);
  71.   }
  72.   sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
  73.   sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI;
  74. /*
  75. 上面是通道2的配置参数

  76. */
  77.   if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_2) != HAL_OK)
  78.   {
  79.     Error_Handler(__FILE__, __LINE__);
  80.   }
  81.   /* USER CODE BEGIN TIM3_Init 2 */
  82.         HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);
  83.         HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_2);
  84.         /*
  85. 上面是使能两个通道的输入捕获中断,cubemx是不会自动生成这两句的,需要自己手动添加

  86. */
  87.   /* USER CODE END TIM3_Init 2 */

  88. }
  89. /*
  90. HAL_TIM_Base_MspInit是HAL库的一个弱定义函数,在这里定义之后就会按照这个定义为准
  91. HAL_TIM_Base_Init会调用这个函数,用来初始化GPIO和中断的参数。
  92. */
  93. void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
  94. {

  95.   GPIO_InitTypeDef GPIO_InitStruct = {0};
  96.   if(tim_baseHandle->Instance==TIM1)
  97.   {
  98.   
  99.   }
  100.   else if(tim_baseHandle->Instance==TIM2)
  101.   {

  102.   }
  103.   else if(tim_baseHandle->Instance==TIM3)
  104.   {
  105.   /* USER CODE BEGIN TIM3_MspInit 0 */

  106.   /* USER CODE END TIM3_MspInit 0 */
  107.     /* TIM3 clock enable */
  108.     __HAL_RCC_TIM3_CLK_ENABLE();

  109.     __HAL_RCC_GPIOC_CLK_ENABLE();
  110.     /**TIM3 GPIO Configuration
  111.     PC6     ------> TIM3_CH1
  112.     */
  113.     GPIO_InitStruct.Pin = GPIO_PIN_6;
  114.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  115.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  116.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  117.     GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
  118.     HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  119.     /* TIM3 interrupt Init */
  120.     HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
  121.     HAL_NVIC_EnableIRQ(TIM3_IRQn);
  122.   /* USER CODE BEGIN TIM3_MspInit 1 */

  123.   /* USER CODE END TIM3_MspInit 1 */
  124.   }

  125. }
  126. /*
  127. //__HAL_TIM_SET_CAPTUREPOLARITY(__HANDLE__, __CHANNEL__, __POLARITY__)可以设置指定定时器和通道的输入触发沿
  128. 接下来就是中断服务函数,如果产生了输入捕获中断就会在HAL库的中断服务函数中调用这个回调函数,可以在下面实现自己的代码
  129. */

  130. void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
  131. {
  132.         if(TIM3 == htim->Instance)
  133.         {
  134.                 if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
  135.                 {
  136.                         /* 获取输入捕获值 */
  137.                         IC1Value = HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_1);
  138.                         IC2Value = HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_2);       
  139.                         if (IC1Value != 0)
  140.                         {
  141.                                 /* 占空比计算 */
  142.                                 DutyCycle = (float)((IC2Value+1) * 100) / (IC1Value+1);

  143.                                 /* 频率计算 */
  144.                                 Frequency = 90000000/90/(float)(IC1Value+1);
  145.                                
  146.                         }
  147.                         else
  148.                         {
  149.                                 DutyCycle = 0;
  150.                                 Frequency = 0;
  151.                         }

  152.                 }
  153.         }
  154. }

 楼主| qn7a12 发表于 2023-8-27 12:02 | 显示全部楼层
另外还需要定义一个大的中断服务函数。
 楼主| qn7a12 发表于 2023-8-27 12:02 | 显示全部楼层
  1. void TIM3_IRQHandler(void)
  2. {
  3.   /* USER CODE BEGIN TIM3_IRQn 0 */

  4.   /* USER CODE END TIM3_IRQn 0 */
  5.   HAL_TIM_IRQHandler(&htim3);//在这个函数中会回调上面自定义的服务函数
  6.   /* USER CODE BEGIN TIM3_IRQn 1 */

  7.   /* USER CODE END TIM3_IRQn 1 */
  8. }
 楼主| qn7a12 发表于 2023-8-27 12:02 | 显示全部楼层
至此配置完成,在主函数中做好初始化,在生成PWM波之后,把输出引脚和输入引脚相连,即可得到波形的数据。

首先设置了一个2k频率,占空比30的PWM波,以下为实验结果:
 楼主| qn7a12 发表于 2023-8-27 12:02 | 显示全部楼层
 楼主| qn7a12 发表于 2023-8-27 12:02 | 显示全部楼层
 楼主| qn7a12 发表于 2023-8-27 12:03 | 显示全部楼层
和示波器测得的数据完全一致。

本文如有错误或者不足,恳请指正。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

40

主题

542

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部