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