STM32 HAL库高级定时器输入捕获脉宽测量
- 📌相关篇《STM32 HAL库定时器输入捕获SlaveMode脉宽测量》
- ✨相比于上面所使用的高级定时器输入捕获从模式来测量PWM信号,实现方法更为复杂一下,但是还是将实现的方法记录下来。
- 📌本篇实现方法内容参考《定时器TIM的输入捕获——正交编码器模式与PWM输入》
- 🔖想了解详细的有关实现方法可以前往相关篇内容了解。
- 🌿本篇基于STM32f030R8单片机。
📓本工程主要改善了,减少测量误差,将测量换算内容,换到了中断函数中执行,用时间换精度。并且采用多次读取测量结果,取多次连续测量值中,出现频率最高的数值作为输出值,有效减少单次数据异常情况。
🛠STM32CubeMX配置
- 🔧高级定时器1配置:(👉🏻在参数配置上,尽可能的将捕获定时器的自动载计数值设置大一点,减少定时器溢出更新事件)
- 🌿时钟源:外部时钟
- 🌿定时器1分频系数,更具个人使用的芯片型号而定。
- 🌿向上计数方式。
- 🌿不开启自动重装载(auto-reload preload),相对于不使能影子寄存器。
- 其他都是默认选项配置。
-
🔨 配置定时器14作为一个PWM信号输出源:
-
🔰使能定时器捕获、更新中断,以及中断优先级配置:(TIM1更新中断 > TIM1捕获中断)
📑功能代码实现部分
- 📝中断回调函数:
/*** @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); //检测完一个周期的那个上升沿为value3capture_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]){// 如果当前元素与前一个元素相同,则重复次数加1currentCount++;}else{// 如果当前元素与前一个元素不同,则重复次数重置为1currentCount = 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 */
}
- 📜测试调试输出:
- ✨修改PWM相关参数测试了几次,结果也没有发现测量的输出结果异常,符合预期。
📚测试工程
链接:https://pan.baidu.com/s/1pgs9OFyiI291pxBMsuutdQ
提取码:rotp
- 🔖此文章仅作为个人学习探索知识的总结,不作为他人或引用者的理论依据,由于学识所限,难免会出现错误或纰漏,欢迎大家指正。