STM32 HAL库高级定时器输入捕获脉宽测量

STM32 HAL库高级定时器输入捕获脉宽测量


  • 相关篇《STM32 HAL库定时器输入捕获SlaveMode脉宽测量》
  • ✨相比于上面所使用的高级定时器输入捕获从模式来测量PWM信号,实现方法更为复杂一下,但是还是将实现的方法记录下来。
  • 本篇实现方法内容参考《定时器TIM的输入捕获——正交编码器模式与PWM输入》
  • 想了解详细的有关实现方法可以前往相关篇内容了解。
  • 本篇基于STM32f030R8单片机。
本工程主要改善了,减少测量误差,将测量换算内容,换到了中断函数中执行,用时间换精度。并且采用多次读取测量结果,取多次连续测量值中,出现频率最高的数值作为输出值,有效减少单次数据异常情况。

STM32CubeMX配置

  • 高级定时器1配置:(在参数配置上,尽可能的将捕获定时器的自动载计数值设置大一点,减少定时器溢出更新事件)
    STM32 HAL库高级定时器输入捕获脉宽测量_第1张图片
  • 时钟源:外部时钟
  • 定时器1分频系数,更具个人使用的芯片型号而定。
  • 向上计数方式。
  • 不开启自动重装载(auto-reload preload),相对于不使能影子寄存器。
  • 其他都是默认选项配置。
  • 配置定时器14作为一个PWM信号输出源:
    STM32 HAL库高级定时器输入捕获脉宽测量_第2张图片

  • 使能定时器捕获、更新中断,以及中断优先级配置:(TIM1更新中断 > TIM1捕获中断)
    STM32 HAL库高级定时器输入捕获脉宽测量_第3张图片

功能代码实现部分

  • 中断回调函数:
/**
  * @brief 输入捕获回调函数
  * @retval None
  */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef* htim)
{
    static uint8_t RisingEdge_count = 0;
    if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
    {
        if(capture_flag & 0x40)										//0X40是0100 0000,高电平期间捕获到下降沿
        {
            capture_flag &= 0x3F;										//0X3F是0011 1111,清除捕获到上升沿的标记位和捕获完成的标记位
            value2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);	//获取当前的捕获值

            __HAL_TIM_DISABLE(htim);        						//关闭定时器
            __HAL_TIM_SET_COUNTER(htim, value2);						//以value2为基准重新计数
            TIM_RESET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1);      	//复位极性选择才能进行下行配置
					/*输入捕获功能的重配与开启,硬件启动会产生几个时钟的延迟*/
            TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING);	//下次上升沿捕获
					 
            __HAL_TIM_ENABLE(htim);									//重开定时器
        }
        else	            								 		//捕获到上升沿
        {
            capture_flag |= 0x40;										//0X40是0100 0000,标记捕捉到了一次上升沿

            RisingEdge_count++;
            if((RisingEdge_count % 2 == 0))								//每捕获两次相同跳变沿表示过了一个PWM周期
            {
                value3 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); //检测完一个周期的那个上升沿为value3
                capture_flag |= 0x80;								 //标记捕获完了一个周期
								Pulse_Width = value2 + OverflowCount_high * TIM1_Period_Value - value1 + 4;	//经验值:再进行4个时钟的补偿
                PWM_Period_ARR[PWM_Period_CNT++] = value3 + OverflowCount_high * TIM1_Period_Value + OverflowCount_low * TIM1_Period_Value - value1 + 7;
                if(PWM_Period_CNT == 5)
                {
                    PWM_Period = findMostFrequentNum(PWM_Period_ARR, 5);
                    PWM_Period_CNT = 0;
                }
                OverflowCount_high = OverflowCount_low = 0;//清空溢出计数
                __HAL_TIM_CLEAR_IT(&htim1, TIM_IT_UPDATE);//清零中断标志位
            }
            else				 								   //正在检测PWM信号的第一个上升沿,意味着下次捕获下降沿
            {
                capture_flag &= 0x7F;								 //0X7F是0111 1111,清除PWM捕获完成标志,开始新一轮PWM周期捕获
                value1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); //第一个上升沿是value1
            }
            __HAL_TIM_DISABLE(htim);
            __HAL_TIM_SET_COUNTER(htim, value1);					//以value1为基准重新计数
            TIM_RESET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1);  	//复位极性选择才能进行下行配置
            TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING);	//下次下降沿捕获
            __HAL_TIM_ENABLE(htim);								//重开定时器
        }
    }
}


/**
  * @brief (高级定时器TIM特有)更新溢出中断函数
  */
//void TIM1_UP_IRQHandler(void)
//{
//    HAL_TIM_IRQHandler(&htim1);
//}
/**
  * @brief 更新溢出回调函数
  * @retval None
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)
{
    if((capture_flag & 0X80) == 0)			//PWM的一个周期没检测完
    {
        if(capture_flag & 0x40)				//在高电平期间溢出M次
        {
            OverflowCount_high++;
        }
        else								//在低电平期间溢出N次
        {
            OverflowCount_low++;
        }
    }
    else								   //PWM的一个周期检测完了
    {

        OverflowCount_high = 0;
        OverflowCount_low = 0;
    }
}

  • ⚡连续读取的数值中获取出现频率最高的数值实现方法:
//比较函数,用于排序
int compare(const void* a, const void* b)
{
    return (*(int*)a - * (int*)b);
}

uint32_t findMostFrequentNum(uint32_t arr[], int size)
{
    // 对数组进行排序
    qsort(arr, size, sizeof(int), compare);//需要包含stdlib.h头文件

    int maxCount = 1; // 最大重复次数
    int currentCount = 1; // 当前元素的重复次数
    uint32_t mostFrequentNumber = arr[0]; // 最频繁出现的数

    for(int i = 1; i < size; i++)
    {
        if(arr[i] == arr[i - 1])
        {
            // 如果当前元素与前一个元素相同,则重复次数加1
            currentCount++;
        }
        else
        {
            // 如果当前元素与前一个元素不同,则重复次数重置为1
            currentCount = 1;
        }

        if(currentCount > maxCount)
        {
            // 如果当前元素的重复次数超过最大重复次数,则更新最大重复次数和最频繁出现的数
            maxCount = currentCount;
            mostFrequentNumber = arr[i];
        }
    }

    return mostFrequentNumber;
}
  • main函数代码以及相关变量定义:
/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
volatile char capture_flag = 0;				//捕获状态标记变量,0x80最高位标记捕获完一个周期,0x40表示捕获到了上升沿
volatile uint8_t OverflowCount_high = 0;		//高电平期间溢出次数
volatile uint8_t OverflowCount_low = 0;		//低电平期间溢出次数
volatile uint32_t value1, value2, value3;	  //下图中三个边沿中的值
volatile uint32_t Pulse_Width = 0;		  //脉宽
volatile uint32_t PWM_Period = 0;				//周期
uint32_t PWM_Period_ARR[5] = {0};
uint8_t PWM_Period_CNT = 0;
/* USER CODE END PV */

int main(void)
{
  /* USER CODE BEGIN 1 */
    uint32_t TimerUART;
//    uint16_t plusewidth = 500;//脉冲宽度;f=1000 000/500=2000Hz
//    uint16_t plusedelay = 20;//脉宽40us
  /* 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_TIM1_Init();
  MX_TIM14_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
    uint32_t sysclock = 0;
//sysclock =RCC_GetHSIFreq();
    sysclock = HAL_RCC_GetSysClockFreq();//RCC_GetHSIFreq()
    TimerUART = HAL_GetTick();
    printf("STM32F030 SysClockFreq:%d \r\n", sysclock);
//		__HAL_TIM_SET_AUTORELOAD(&htim14, plusewidth - 1); //调整分频系数,可以改变arr以改变频率
//    __HAL_TIM_SET_COMPARE(&htim14, TIM_CHANNEL_1, plusedelay); //PWM脉冲宽度,修改占空比比较值
    /* 使能PWM输出 */
    HAL_TIM_PWM_Start(&htim14, TIM_CHANNEL_1);
    /* 清零中断标志位 */
    __HAL_TIM_CLEAR_IT(&htim1, TIM_IT_UPDATE);
    /* 使能定时器的更新事件中断 */
    __HAL_TIM_ENABLE_IT(&htim1, TIM_IT_UPDATE);
    /* 使能输入捕获 */
    HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    while(1)
    {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
        if((HAL_GetTick() - TimerUART) > 1000)
        {
          
            printf("/********************/\r\n");
            printf("脉宽为: %d us\r\n", Pulse_Width);
            printf("周期为:	%d us\r\n", PWM_Period);
            printf("/********************/\r\n");
            TimerUART = HAL_GetTick();
        }
    }
  /* USER CODE END 3 */
}
  • 测试调试输出:
    STM32 HAL库高级定时器输入捕获脉宽测量_第4张图片
    STM32 HAL库高级定时器输入捕获脉宽测量_第5张图片
  • ✨修改PWM相关参数测试了几次,结果也没有发现测量的输出结果异常,符合预期。

测试工程

链接:https://pan.baidu.com/s/1pgs9OFyiI291pxBMsuutdQ 
提取码:rotp

  • 此文章仅作为个人学习探索知识的总结,不作为他人或引用者的理论依据,由于学识所限,难免会出现错误或纰漏,欢迎大家指正。

你可能感兴趣的:(stm32,脉宽测量,输入捕获,定时器)