Air001 advanced timer input capture function measures pulse width and frequency
Air001 advanced timer input capture function measures pulse width and frequency
- ?Air001 only has one 16-bit advanced timer. After actual testing, it was found that the input capture function of channel 1 failed. I was not sure whether it was a problem with the IO pin or a hardware bug. After struggling for a long time, I finally switched to channel 2 for use. It can be captured. The remaining channels 3 and 4 have not been tested for validity.
- For ARM 32-bit M0+ core microcontrollers, the use of timers is the same as that of STM32 microcontrollers with the same core architecture, and the code can basically be copied.
- This article mainly transplants the STM32 code into the chip project to verify the function.
- Tested using advanced timer channel 1 and channel 2, reset mode, and measured the pulse width signal. It was found that it did not work. It was normal when used on STM32f030. “STM32 HAL library timer input capture SlaveMode pulse width measurement”
- Air001 is relatively cheap, but there are still many hidden pits or design flaws in the hardware. When choosing a migration and switching solution, it is best to verify the function in advance.
- Timer input capture configuration function:
void MX_TIM1_Init(void) {<!-- --> /* USER CODE BEGIN TIM1_Init 0 */ /* USER CODE END TIM1_Init 0 */ TIM_ClockConfigTypeDef sClockSourceConfig = {<!-- -->0}; TIM_MasterConfigTypeDef sMasterConfig = {<!-- -->0}; TIM_IC_InitTypeDef sConfigIC = {<!-- -->0}; /* USER CODE BEGIN TIM1_Init 1 */ /* USER CODE END TIM1_Init 1 */ htim1.Instance = TIM1; htim1.Init.Prescaler = 23;//1MHz htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 65535; htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter = 0; htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init( & amp;htim1) != HAL_OK) {<!-- --> Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource( & amp;htim1, & amp;sClockSourceConfig) != HAL_OK) {<!-- --> Error_Handler(); } if (HAL_TIM_IC_Init( & amp;htim1) != HAL_OK) {<!-- --> Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization( & amp;htim1, & amp;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( & amp;htim1, & amp;sConfigIC, TIM_CHANNEL_2) != HAL_OK)//Channel 2 {<!-- --> Error_Handler(); } /* TIM1 enables startup and interrupts */ if (HAL_TIM_Base_Start_IT( & amp;htim1) != HAL_OK) {<!-- --> Error_Handler(); } }
- Timer input capture pin configuration and interrupt priority configuration
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle) {<!-- --> GPIO_InitTypeDef GPIO_InitStruct = {<!-- -->0}; if(tim_baseHandle->Instance==TIM1) {<!-- --> /* USER CODE BEGIN TIM1_MspInit 0 */ /* USER CODE END TIM1_MspInit 0 */ /* TIM1 clock enable */ __HAL_RCC_TIM1_CLK_ENABLE(); // __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /**TIM1 GPIO Configuration PA3 GPIO_AF13_TIM1 ------> TIM1_CH1 PB3 GPIO_AF1_TIM1 ------> TIM1_CH2 \t */ GPIO_InitStruct.Pin = GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF1_TIM1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* TIM1 interrupt Init */ \t\t HAL_NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 2, 0); HAL_NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn); HAL_NVIC_SetPriority(TIM1_CC_IRQn, 1, 0); //Input capture priority > update overflow interrupt HAL_NVIC_EnableIRQ(TIM1_CC_IRQn); /* USER CODE BEGIN TIM1_MspInit 1 */ /* USER CODE END TIM1_MspInit 1 */ } if(tim_baseHandle->Instance==TIM16)//Used to generate PWM signal {<!-- --> /* USER CODE BEGIN TIM16_MspInit 0 */ /* USER CODE END TIM16_MspInit 0 */ /* TIM16 clock enable */ __HAL_RCC_TIM16_CLK_ENABLE(); /* USER CODE BEGIN TIM16_MspInit 1 */ /* USER CODE END TIM16_MspInit 1 */ } }
- System clock frequency configuration (this routine test is used for internal clock, frequency 24MHz)
/** * @brief configure system clock * @param HSICLKSource_SET: select HSI clock frequency * @arg @ref RCC_HSICALIBRATION_8MHz: 8M clock * @arg @ref RCC_HSICALIBRATION_16MHz: 16M clock * @arg @ref RCC_HSICALIBRATION_22p12MHz: 22.12M clock * @arg @ref RCC_HSICALIBRATION_24MHz: 24M clock * @retval None */ void SystemClock_Config(uint32_t HSICLKSource_SET) {<!-- --> RCC_OscInitTypeDef RCC_OscInitStruct = {<!-- -->0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {<!-- -->0}; // RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; /* Configure HSI clock */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; /* Enable HSI */ RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1; /* HSI prescaler */ RCC_OscInitStruct.HSICalibrationValue = HSICLKSource_SET; /* Setting the HSI output clock library will set the calibration value */ if(HAL_RCC_OscConfig( & amp;RCC_OscInitStruct) != HAL_OK) /* Configure clock */ {<!-- --> Error_Handler(); } /* Initialize AHB, APB bus clock */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; /* Configure AHB clock source */ RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; /* Set AHB prescaler */ RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; /* Set APB1 prescaler */ if(HAL_RCC_ClockConfig( & amp;RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) /* Configuration bus */ {<!-- --> Error_Handler(); } }
- Timer interrupt callback function
/** * @brief input capture callback function * @retval None */ void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef* htim) {<!-- --> static uint8_t RisingEdge_count = 0; if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2) {<!-- --> __HAL_TIM_CLEAR_IT( & amp;htim1, TIM_IT_CC2);//Clear the interrupt flag bit if(capture_flag & amp; 0x40) //0X40 is 0100 0000, the falling edge is captured during the high level {<!-- --> capture_flag &= 0x3F; //0X3F is 0011 1111, clear the flag bit that captures the rising edge and the flag bit that captures the completion value2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2); //Get the current captured value __HAL_TIM_DISABLE(htim); //Close the timer __HAL_TIM_SET_COUNTER(htim, value2); //Recount based on value2 TIM_RESET_CAPTUREPOLARITY(htim, TIM_CHANNEL_2); //Reset polarity selection is required for downlink configuration /*Reconfiguration and opening of the input capture function, hardware startup will cause a delay of several clocks*/ TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_2, TIM_ICPOLARITY_RISING); //Next rising edge capture __HAL_TIM_ENABLE(htim); //Restart the timer } else //Capture the rising edge {<!-- --> capture_flag |= 0x40; //0X40 is 0100 0000, the mark captures a rising edge RisingEdge_count + + ; if((RisingEdge_count % 2 == 0)) //Every time the same transition edge is captured twice, it means that one PWM cycle has passed {<!-- --> value3 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2); //The rising edge after detecting a cycle is value3 capture_flag |= 0x80; //Mark capture completes a cycle Pulse_Width = (value2 + OverflowCount_high * TIM1_Period_Value - value1 + 7)/2; //7 clock compensation PWM_Period_ARR[PWM_Period_CNT + + ] = value3 + OverflowCount_high * TIM1_Period_Value + OverflowCount_low * TIM1_Period_Value - value1 - 3; if(PWM_Period_CNT == 5)//Read 5 times, get the value with the highest frequency {<!-- --> PWM_Period = findMostFrequentNum(PWM_Period_ARR, 5)/2;//2MHZ count PWM_Period_CNT = 0; } OverflowCount_high = OverflowCount_low = 0;//Clear the overflow count } else //Detecting the first rising edge of the PWM signal means capturing the falling edge next time {<!-- --> capture_flag &= 0x7F; //0X7F is 0111 1111, clear the PWM capture completion flag, and start a new round of PWM cycle capture value1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2); //The first rising edge is value1 } __HAL_TIM_DISABLE(htim); __HAL_TIM_SET_COUNTER(htim, value1); //Recount based on value1 TIM_RESET_CAPTUREPOLARITY(htim, TIM_CHANNEL_2); //Reset polarity selection is required for downlink configuration TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_2, TIM_ICPOLARITY_FALLING); //Next falling edge capture __HAL_TIM_ENABLE(htim); //Restart the timer } } } /** * @brief update overflow callback function * @retval None */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) {<!-- --> __HAL_TIM_CLEAR_IT( & amp;htim1, TIM_IT_UPDATE);//Clear the interrupt flag bit if((capture_flag & amp; 0X80) == 0) //One cycle of PWM has not been detected yet {<!-- --> if(capture_flag & amp; 0x40) //Overflow M times during high level {<!-- --> OverflowCount_high + + ; } else //Overflow N times during low level {<!-- --> OverflowCount_low + + ; } } else //One cycle of PWM detection is completed {<!-- --> OverflowCount_high = 0; OverflowCount_low = 0; } }
- main function
volatile uint32_t ARR_Value, PWM_f; volatile char capture_flag = 0; //Capture status flag variable, the highest bit of 0x80 marks the completion of a cycle of capture, 0x40 indicates that the rising edge is captured volatile uint8_t OverflowCount_high = 0; //Number of overflows during high level volatile uint8_t OverflowCount_low = 0; //Number of overflows during low level volatile uint32_t value1, value2, value3; //Values in three edges volatile uint32_t Pulse_Width = 0; //Pulse width volatile uint32_t PWM_Period = 0; //Period uint32_t PWM_Period_ARR[5] = {<!-- -->0}; uint8_t PWM_Period_CNT = 0; int main(void) {<!-- --> uint32_t TimerUART; uint8_t USART_TX_Buff[40] = {<!-- -->0}; /* Initialize all peripherals, Flash interface, SysTick */ HAL_Init(); SystemClock_Config(RCC_HSICALIBRATION_24MHz); APP_GpioConfig(); HAL_TIM_Base_MspInit( & amp;htim1); MX_TIM1_Init(); MX_TIM16_Init(); MX_USARTx_UART_Init(); // __HAL_TIM_SET_AUTORELOAD( & amp;htim16, plusewidth - 1); //Adjust frequency division coefficient, TIM16->ARR // __HAL_TIM_SET_COMPARE( & amp;htim16, TIM_CHANNEL_2, plusedelay); //PWM pulse width, TIM16->CCR1 modifies the duty cycle comparison value HAL_TIM_PWM_Start( & amp;htim16, TIM_CHANNEL_1); //Enable PWM output channel: PA6 HAL_TIM_Base_Start_IT( & amp;htim1); // __HAL_TIM_ENABLE_IT( & amp;htim1, TIM_IT_UPDATE); // Start update interrupt __HAL_TIM_URS_ENABLE( & amp;htim1); //Update interrupt is only triggered when overflow occurs /* Clear the interrupt flag bit */ __HAL_TIM_CLEAR_IT( & amp;htim1, TIM_IT_UPDATE); /* Enable timer update event interrupt */ __HAL_TIM_ENABLE_IT( & amp;htim1, TIM_IT_UPDATE);//Enable update interrupt /* Enable input capture */ HAL_TIM_IC_Start_IT( & amp;htim1, TIM_CHANNEL_2); TimerUART = HAL_GetTick(); printf("Air001 SysClockFreq:%d \r\ ", HAL_RCC_GetSysClockFreq()); while(1) {<!-- --> // HAL_Delay(1000); if((HAL_GetTick() - TimerUART) > 1000) {<!-- --> /* LED flip */ HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1); PWM_f = 1000000l / PWM_Period; sprintf((char*)USART_TX_Buff, "Pulse_Width:%dus,PWM_Period:%d,PWM_f:%dHz", Pulse_Width, PWM_Period, PWM_f); printf("%s \ ", USART_TX_Buff); memset((char*)USART_TX_Buff, '\0', strlen((char*)USART_TX_Buff)); //Clear the array TimerUART = HAL_GetTick(); } } }
- When the frequency of the measured signal is higher, the error is greater.
- When the frequency of the measured signal is relatively low, the measurement results:
Test project
Link: https://pan.baidu.com/s/1OIf_XiWwRuBVPgidumX_SQ Extraction code: 93ev