STM32CubeMX learning (5) input capture experiment

personal learning record

1. New construction

insert image description here

Second, select the chip model

The development board I use is the Punctual Atomic STM32F103ZET6 core board

insert image description here

3. Configure the clock

The development board is soldered with an external crystal, so I selected Crystal/Ceramic Resonator (quartz/ceramic resonator) for the RCC (Reset and Cock Control) configuration. After the configuration is complete, the relevant pins in the Pinout view on the right will be marked green.

insert image description here

After the external high-speed clock configuration is completed, enter the Clock Configuration option, according to the actual situation, configure the system clock to 72 MHz, the configuration steps are as follows, and finally press Enter, the software will automatically adjust the frequency division and frequency multiplication parameters.

insert image description here

Fourth, configure the debug mode

ST-Link is Serial Wire debug mode, be sure to set it! ! !
Before using the M0 chip, there was no problem without configuring this mode, but now this model, if the Serial Wire mode is not configured, once the program is programmed into the chip through ST-Link, the chip can no longer be recognized by ST-Link. (Later I returned to normal after burning the program/erasing through the STMISP tool)

insert image description here

5. Timer (input capture) parameter configuration

I use channel 1 of TIM2 as the input capture test channel, and STM32CubeMX will configure PA0 as the IO port for input capture by default (PA0 has this multiplexing function and does not need to be remapped, so PA0 is automatically set as the GPIO of TIM_CH1), timer The parameter settings are shown in the following figure (the configuration of input capture can be left unchanged, and the rising edge is captured by default):

insert image description here

The frequency division factor is 72-1, which means 72 frequency division (0 means 1 frequency division, 1 means 2 frequency division, and so on), the clock frequency of TIM2 is 72 MHz (in the figure below, the clock frequency of APB1 Timer clocks is 72MHz, TIM2 is mounted on APB1). After dividing it by 72, the frequency becomes 1MHz, which is 1,000,000 counts per second. The period is set to 1000-1 (it is decremented by one here, it should be because the minimum count value is 0), which means that a complete timing cycle is 1000 counts. Combined with the timer counting frequency, the time required for the timer to overflow once is 1ms. [The frequency determines the capture period of the input capture, and the timing value is set to 1000 just for the convenience of calculation]

insert image description here

Input capture needs to open the interrupt of the timer, whether it is time-out or input capture, the interrupt needs to be used.

insert image description here

6. Generate Keil project

Set IDE and project directory and name:

insert image description here

Store the code of each peripheral in a different .c /.h file for easy management (otherwise it will be placed in main.c).

insert image description here

The following is the code for the initialization of TIM2 (input capture) in the Keil project:

/* TIM2 init function */
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_IC_InitTypeDef sConfigIC = {
    
    0};

  /* USER CODE BEGIN TIM2_Init 1 */

  /* USER CODE END TIM2_Init 1 */
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 72 - 1;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 1000 - 1;
  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_IC_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();
  }
  sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
  sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
  sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
  sConfigIC.ICFilter = 0;
  if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_Init 2 */

}

7. Where is the interrupt function written?

When using the standard library, we write the interrupt handling in the bottom-level interrupt handling function, for example EXTI0_IRQHandler(), but the Hal library adds a callback function to "hide" some necessary operations at the bottom of the interrupt (such as clearing the interrupt).

The calling sequence of interrupts is (taking EXTI0 as an example): EXTI0_IRQHandler()—> HAL_GPIO_EXTI_IRQHandler()—> HAL_GPIO_EXTI_Callback().

The interrupt service function of TIM2 has stm32f1xx_it.cbeen defined in (automatically generated by STM32CubeMX)

/**
  * @brief This function handles TIM2 global interrupt.
  */
void TIM2_IRQHandler(void)
{
    
    
  /* USER CODE BEGIN TIM2_IRQn 0 */

  /* USER CODE END TIM2_IRQn 0 */
  HAL_TIM_IRQHandler(&htim2);
  /* USER CODE BEGIN TIM2_IRQn 1 */

  /* USER CODE END TIM2_IRQn 1 */
}

HAL_TIM_IRQHandler()It is the total interrupt of the timer of the HAL library. There is a lot of code in it. It is not shown here. We only need to know one thing - when the TIM2 count value overflows or other events occur (such as capturing rising/falling edge signals), the system will execute a series of The interrupt callback function, which includes the count overflow callback function HAL_TIM_PeriodElapsedCallback()and the input capture callback function that we will use HAL_TIM_IC_CaptureCallback().

Eight, test example

The serial port is used in the experiment, which is not mentioned in the configuration above. The serial port configuration can refer to STM32CubeMx learning (2) USART serial port experiment

The core part of my experimental code is the interrupt callback function:

// 定时器计数溢出中断处理回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    
    
	if(IC_DONE_FLAG == 0)  // 未完成捕获
	{
    
    
		if(IC_START_FLAG == 1)  // 已经捕获到了高电平
		{
    
    
			IC_TIMES++;  // 捕获次数加一
		}
	}
}

//定时器输入捕获中断处理回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)// 捕获中断发生时执行
{
    
    
	if(IC_DONE_FLAG == 0)  // 未完成捕获
	{
    
    
		if(IC_START_FLAG == 1)  // 原来是高电平,现在捕获到一个下降沿
		{
    
    
			IC_VALUE = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);  // 获取捕获值
			TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1);  // 先清除原来的设置
			TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);// 配置为上升沿捕获
			IC_START_FLAG = 0;  // 标志复位
			IC_DONE_FLAG = 1;  // 完成一次高电平捕获
		}
		else  // 捕获还未开始,第一次捕获到上升沿
		{
    
    
			IC_TIMES = 0;  // 捕获次数清零
			IC_VALUE = 0;  // 捕获值清零
			IC_START_FLAG = 1;  // 设置捕获到了上边沿的标志
			TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1);  // 先清除原来的设置
			TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);// 配置为下降沿捕获
		}
		__HAL_TIM_SET_COUNTER(htim,0);  // 定时器计数值清零
	}
}

complete main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include <stdio.h>

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */


uint32_t IC_TIMES;  // 捕获次数,单位1ms
uint8_t IC_START_FLAG;  // 捕获开始标志,1:已捕获到高电平;0:还没有捕获到高电平
uint8_t IC_DONE_FLAG;  // 捕获完成标志,1:已完成一次高电平捕获
uint16_t IC_VALUE;  // 输入捕获的捕获值

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
    
    
  /* USER CODE BEGIN 1 */
	
	uint32_t time = 0;
  /* 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_TIM2_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);  //开启TIM2的捕获通道1
  __HAL_TIM_ENABLE_IT(&htim2,TIM_IT_UPDATE);  //使能更新中断
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    
    
		HAL_Delay(10);
		
		if(IC_DONE_FLAG == 1)  // 如果完成一次高电平捕获
		{
    
    
			IC_DONE_FLAG = 0;  // 标志清零
			time = IC_TIMES * 1000;  // 脉冲时间为捕获次数 * 1000us
			time += IC_VALUE;  // 加上捕获时间(小于1ms的部分)
			printf("High level: %d us\n", time);
		}
		
    /* USER CODE END WHILE */

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

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
    
    
  RCC_OscInitTypeDef RCC_OscInitStruct = {
    
    0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {
    
    0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    
    
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */


// 定时器计数溢出中断处理回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    
    
	if(IC_DONE_FLAG == 0)  // 未完成捕获
	{
    
    
		if(IC_START_FLAG == 1)  // 已经捕获到了高电平
		{
    
    
			IC_TIMES++;  // 捕获次数加一
		}
	}
}

//定时器输入捕获中断处理回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)// 捕获中断发生时执行
{
    
    
	if(IC_DONE_FLAG == 0)  // 未完成捕获
	{
    
    
		if(IC_START_FLAG == 1)  // 原来是高电平,现在捕获到一个下降沿
		{
    
    
			IC_VALUE = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);  // 获取捕获值
			TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1);  // 先清除原来的设置
			TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);// 配置为上升沿捕获
			IC_START_FLAG = 0;  // 标志复位
			IC_DONE_FLAG = 1;  // 完成一次高电平捕获
		}
		else  // 捕获还未开始,第一次捕获到上升沿
		{
    
    
			IC_TIMES = 0;  // 捕获次数清零
			IC_VALUE = 0;  // 捕获值清零
			IC_START_FLAG = 1;  // 设置捕获到了上边沿的标志
			TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1);  // 先清除原来的设置
			TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);// 配置为下降沿捕获
		}
		__HAL_TIM_SET_COUNTER(htim,0);  // 定时器计数值清零
	}
}
/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
    
    
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
    
    
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
    
    
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */


Experimental effect:

PA0 corresponds to a button on my development board. When the button is tapped (not pressed), the serial port will keep printing some useless high-level durations. The durations of these useless pulses are very close, about 10ms. It shows that the duration of the jitter level of the key is about 10ms.

When the button is pressed for a long time and then released, the time of pressing the button will be printed. For example, the data in the two red circles in the following figure, the duration of the first high level is 4.35s, and the second high level The duration is 1.59s.

insert image description here

Guess you like

Origin blog.csdn.net/weixin_43772810/article/details/125393640