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

news2025/1/12 19:00:19

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); //检测完一个周期的那个上升沿为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 */
}
  • 📜测试调试输出:
    在这里插入图片描述
    在这里插入图片描述
  • ✨修改PWM相关参数测试了几次,结果也没有发现测量的输出结果异常,符合预期。

📚测试工程

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1099672.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV16-图像连通域分析

OpenCV16-图像连通域分析 1.图像连通域分析2.connectedComponents3.connectedComponentsWithStatus 1.图像连通域分析 连通域是指图像中具有相同像素值并且位置相邻的像素组成的区域。连通域分析是指在图像中寻找彼此互相独立的连通域并将其标记出来。 4邻域与8邻域的概念&am…

TatukGIS Developer Kernel使用教程:如何为FMX创建第一个应用程序

概述&#xff1a;TatukGIS Developer Kernel&#xff08;DK&#xff09;是一个用于开发自定义地理信息系统&#xff08;GIS&#xff09;应用程序以及解决方案的综合性软件开发工具包&#xff08;SDK&#xff09;。本篇文章主要介绍用DK11为FMX创建一个应用程序&#xff0c;现在…

solidworks 2024新功能之--保存为低版本 硕迪科技

大家期盼已久的SOLIDWORKS保存低版本文件功能来了&#xff0c;从SOLIDWORKS 2024 开始&#xff0c;您可以将在最新版本的SOLIDWORKS 中创建的SOLIDWORKS零件、装配体和工程图另存为SOLIDWORKS 早期版本的全功能文档&#xff08;完成的特征树与相关参数&#xff09;。 将文件另…

Spring Boot项目中使用 TrueLicense 生成和验证License

1、Linux 在客户linux上新建layman目录&#xff0c;导入license.sh文件&#xff0c; [rootlocalhost layman]# mkdir -p /laymanlicense.sh文件内容&#xff1a; #!/bin/bash # 1.获取要监控的本地服务器IP地址 IPifconfig | grep inet | grep -vE inet6|127.0.0.1 | awk {p…

Kubernetes简略架构

kubectl kubectl是Kubernetes的命令行工具。它的全称为Kubernetes Command Line Tool&#xff0c;是用于管理Kubernetes集群的工具。它可以用来创建、更新、删除资源对象、查看日志等操作 Node: Kubernetes 通过将容器放入在节点&#xff08;Node&#xff09;上运行的Pod 中…

5、使用 pgAdmin4 图形化创建和管理 PostgreSQL 数据库

通过上几篇文章我们讲解了如何安装 PostgreSQL 数据库软件和 pgAdmin4 图形化管理工具。 今天我们继续学习如何通过 pgAdmin4 管理工具图形化创建和管理 PostgreSQL 数据库。 一、PostgreSQL的基本工作方式 在学习如何使用PostgreSQL创建数据库之前&#xff0c;我们需要了解一…

C++学习: 文件I/O

作者: 苏丙榅 原文链接: https://subingwen.cn/c/file/ 文章目录 1. 文件概述1.1 什么是文件I/O1.2 磁盘文件分类 2. 文件的打开和关闭2.1 文件指针2.2 打开文件 2.3 关闭文件3. 文件的读写3.1 按照字符读写文件3.1.1 写文件3.1.2 读文件3.1.3 EOF 3.2 按照行读写文件3.2.1 写文…

华为交换机S5700系列产品命名规则

华为交换机的全系列产品命名规则如下&#xff1a; S系列&#xff1a;代表固定端口交换机。例如&#xff0c;S5720系列、S6720系列。CE系列&#xff1a;代表企业级交换机。例如&#xff0c;CE5800系列、CE6800系列。CloudEngine系列&#xff1a;代表华为云引擎交换机&#xff0c…

数据库系列之MySQL中Join语句优化问题

最近使用MySQL 8.0.25版本时候遇到一个SQL问题&#xff0c;两张表做等值Join操作执行很慢&#xff0c;当对Join连接字段添加索引优化后&#xff0c;执行效率反而变得更差&#xff0c;其中的原因值得分析。因此本文介绍下MySQL中常见的Join算法&#xff0c;并对比使用不同Join算…

【MATLAB源码-第47期】基于matlab的GMSK调制解调仿真,输出误码率曲线,采用相干解调。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 GMSK&#xff08;高斯最小移相键控&#xff09;是数字调制技术的一种。下面是关于GMSK调制解调、应用场景以及其优缺点的详细描述&#xff1a; 1. 调制解调&#xff1a; - 调制&#xff1a;GMSK是一种连续相位调制技术&am…

03【彻底掌握Git的底层对象】

上一篇&#xff1a;02【Git的基本使用-快速上手Git】 下一篇&#xff1a;04【彻底掌握Git的底层对象】 目录&#xff1a;【Git系列教程-目录大纲】 文章目录 三、Git底层对象3.1 Blob对象3.1.1 Blob对象简介3.1.2 Blob对象的使用1&#xff09;写入数据2&#xff09;读取数据3…

Excel大量表格选择,快速定位表格

excel有大量表格&#xff0c;快速定位表格方法。 在这个区域电机鼠标右键 出现表格选择。&#xff08;此处方便查看15个表格&#xff09;&#xff0c;如果超过15个表格可以选择其他工资表。 选择其他工作表会弹出列表框如下图 特此记录 anlog 2023年10月12日

红队专题-从零开始VC++远程控制软件RAT-C/S-[4]客户端与服务端连接

红队专题 招募六边形战士队员服务端编写新建工程server函数创建主线程类获取配置信息command 命令startsocket 开始监听win32 类库/头文件 招募六边形战士队员 一起学习 代码审计、安全开发、web攻防、逆向等。。。 私信联系 服务端编写 新建工程 server函数 // FackExec_…

Windows10用Navicat 定时备份报错80070057

直接按照网上的教程配置定时任务发现报错&#xff0c;提示参数非法之类的&#xff0c;80070057。 搜索加自己测试发现是用户权限问题。 设置任务计划的时候&#xff0c;我用了用户组&#xff0c;选了administors&#xff0c;在勾选上run with hightest privileges。 查找用户…

JVM第七讲:JVM 基础 - Java 内存模型详解

JVM 基础 - Java 内存模型详解 本文是JVM第七讲&#xff0c;JVM 基础 - Java 内存模型详解。主要转载自 Info 上深入理解Java内存模型, 作者程晓明。这篇文章对JMM讲的很清楚了&#xff0c;大致分三部分&#xff1a;1、重排序与顺序一致性&#xff1b;2、三个同步原语&#xff…

数据结构复盘——第二章:线性表

文章目录 第一部分:顺序表1、顺序表的定义2、顺序表的操作3、顺序表的优缺点第一部分习题第二部分:单链表1、单链表的定义2、单链表的结点知识3、单链表的操作4、单链表的优缺点第二部分习题第三部分:双链表1、双链表的结构2、双链表的操作第三部分习题第四部分:静态链表1、…

使用 Tkinter Canvas 小部件添加放大镜功能?

一、说明 据我所知&#xff0c;内置的 Tkinter Canvas 类比例不会自动缩放图像。如果您无法使用自定义小部件&#xff0c;则可以缩放原始图像并在调用缩放函数时将其替换在画布上。 二、实现图像放大镜技术细节 我如何将放大和缩小添加到以下脚本中&#xff0c;我想将其绑定到…

OpenCV 笔记(1):图像的读取、显示、创建

Part11. 数字图像的含义 OpenCV 中的图像&#xff0c;其实指的是数字图像。在介绍图像这个概念之前&#xff0c;先介绍几个基础的概念&#xff1a; 像素(Pixel)是图像的基本单元或者基本元素&#xff0c;亦或者是图像最小的单位。图像中的像素点包含不同的像素值。对于灰白图像…

Flume 整合 Kafka

1.背景 先说一下&#xff0c;为什么要使用 Flume Kafka&#xff1f; 以实时流处理项目为例&#xff0c;由于采集的数据量可能存在峰值和峰谷&#xff0c;假设是一个电商项目&#xff0c;那么峰值通常出现在秒杀时&#xff0c;这时如果直接将 Flume 聚合后的数据输入到 Storm 等…

Vue-props配置功能

Vue-props配置功能 props概述 功能&#xff1a;接收从其他组件传过来的数据&#xff0c;将数据从静态转为动态注意&#xff1a; 同一层组件不能使用props&#xff0c;必须是父组件传子组件的形式。父组件传数据&#xff0c;子组件接收数据。不能什么数据都接收&#xff0c;可…