STM32G474之TIM1捕获1模式

news2024/9/27 11:27:22

STM32G474采用TIM8产生方波信号,使用TIM1工作于捕获1模式,并计算方波频率。捕获方波周期,在有些开发中,还是能用到。建议开发时使用HAL库自带的库函数。使用寄存器方法也可以实现,但是后期修改不太方便。

测试时,将PA8引脚复用为TIM1_CH1,LED灯引脚为PC13

TIM1_CH1重映射,见下面的表格:

 1、测试程序

#include "Timer1.h"
#include "LED.h"
#include "stm32g4xx_hal.h"
#include "stdio.h"  //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()

//STM32G474采用TIM8产生方波信号,使用TIM1工作于捕获1模式,并计算方波频率。
//捕获方波周期,在有些开发中,还是能用到。建议开发时使用HAL库自带的库函数。
//使用寄存器方法也可以实现,但是后期修改不太方便.

//将PA8引脚复用为TIM1_CH1
//LED灯引脚为PC13

TIM_HandleTypeDef htim1;  //TIM1句柄
double uwTimclock1;
uint32_t uwPrescalerValue1;

uint32_t uwIC2Value1 = 0; //用来保存第1次捕获到的值
uint32_t uwIC2Value2 = 0; //用来保存第2次捕获到的值
uint32_t uwDiffCapture = 0; //两次捕获到的差值
uint16_t uhCaptureIndex = 0; //用来表示捕获下标

float uwFrequency = 0;//用来保存计算到的频率

uint16_t LED_cnt;

void Timer1_Init(void);
void TIM8_Init(void);
void Print_Period(void);

//使用TIM1捕获
void Timer1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_IC_InitTypeDef sConfigIC = {0};
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};

  __HAL_RCC_TIM1_CLK_ENABLE();  //使能“定时器1”的时钟,Enable TIM1 clock
  __HAL_RCC_GPIOA_CLK_ENABLE(); //GPIOA时钟使能

    GPIO_InitStruct.Pin = GPIO_PIN_8;                  //选择引脚编号8
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;            //复用功能推挽模式
    GPIO_InitStruct.Pull = GPIO_NOPULL;                //引脚上拉和下拉都没有被激活
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; //引脚的输出速度为120MHz
    GPIO_InitStruct.Alternate = GPIO_AF6_TIM1;         //将PA8引脚复用为TIM1_CH1
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    //根据GPIO_InitStruct结构变量指定的参数初始化GPIOA的外设寄存器

  uwTimclock1 = HAL_RCC_GetPCLK2Freq();
    //读取PCLK2的时钟频率,Return the PCLK2 frequency
    //若PCLK2的分频器值为1,则和SystemCoreClock的值相等

    //Frequency computation: for this example TIMx (TIM1) is clocked by APB2Clk
    uwPrescalerValue1 = (uint32_t) ((uwTimclock1 / 1000000U) - 1U);
    //uwPrescalerValue1=170

  htim1.Instance = TIM1;
    htim1.Init.Period = 0xFFFF;//在配置为捕获时,定时器周期必须设置为0xFFFF
  htim1.Init.Prescaler = uwPrescalerValue1;
    //设置TIM1预分频器为uwPrescalerValue1
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    //设置时钟分频系数,TIM1_CR1中的CKD[9:8]=00b,tDTS=ttim_ker_ck;
    //溢出时间为(FFFF+1)*1*170/170000000/1=65.535ms
    //最大误差为170/170000000=1us,则允许采集的最大周期为65.535毫妙,最小周期为1微妙

    htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.RepetitionCounter = 0;//重复计数(1-0)次,产生1次中断,比较重要
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    //因为该定时器为捕获,因此不使能“自动重装载”
    HAL_TIM_IC_Init(&htim1);//TIM1输入捕获初始化

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;//TIMx_EGR.UG位用作产生TIM1_TRGO触发信号
  sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;//TIMx_EGR.UG位用作产生TIM1_TRGO2触发信号
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;//Master/slave mode is selected
  HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);
    //Configures the TIM in master mode.

  sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;//上升沿捕获
  sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
    //将“TIM输入1,TIM输入2,TIM输入3和TIM输入4”,分别和“IC1、IC2、IC3和IC4”一一对应连接起来 
  sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
  sConfigIC.ICFilter = 0;
  HAL_TIM_IC_ConfigChannel(&htim1, &sConfigIC, TIM_CHANNEL_1);
    //因为捕获引脚为TIM1_CH1,所以要配置TIM1捕获1通道
    //该函数调用后,会记录htim->Channel=HAL_TIM_ACTIVE_CHANNEL_1
    //所以htim要被申请为全局变量;
    //TIM_CHANNEL_1表示通道1

  sBreakDeadTimeConfig.BreakAFMode = TIM_BREAK_AFMODE_INPUT;//刹车输入引脚BRK处于输入模式
  sBreakDeadTimeConfig.Break2AFMode = TIM_BREAK_AFMODE_INPUT;
  HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig);

    HAL_NVIC_SetPriority(TIM1_CC_IRQn, 0, 0);
    //设置NVIC中断分组4:4位抢占优先级,0位响应优先级
    //选择中断优先级组4,即抢占优先级为4位,取值为0~15,响应优先级组为0位,取值为0
    //因为捕获脉宽时间,不能受到其它中断影响产生误差,所以这里设置TIM1中断优先级为0

    HAL_NVIC_EnableIRQ(TIM1_CC_IRQn);//使能TIM1产生捕获中断

  HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1);
    //Start the Input Capture in interrupt mode
    //TIM_CHANNEL_1表示通道1

    TIM8_Init();
}

//HAL_TIM_IRQHandler()会调用这个函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
  if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
  {
    if(uhCaptureIndex == 0)
    {
      uwIC2Value1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
            //保存读到的第1个捕获数据
            //Get the 1st Input Capture value
            //TIM_CHANNEL_1表示通道1

      uhCaptureIndex = 1;
    }
    else if(uhCaptureIndex == 1)
    {
      uwIC2Value2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
      //保存读到的第2个捕获数据
            //Get the 2nd Input Capture value
            //TIM_CHANNEL_1表示通道1

      if (uwIC2Value2 >= uwIC2Value1)
      {
        uwDiffCapture = (uwIC2Value2 - uwIC2Value1);//计算差值 
      }
      else if (uwIC2Value2 < uwIC2Value1)
      {
        uwDiffCapture = ( (0xFFFF - uwIC2Value1) + uwIC2Value2 ) + 1;
                //计算差值
                //0xFFFF is max TIM1_CCRx value

      }
     
      uwFrequency = uwTimclock1 /uwPrescalerValue1 / uwDiffCapture;
      uhCaptureIndex = 0;//允许再次捕获
    }
  }
}

//定时器1捕获中断
void TIM1_CC_IRQHandler(void)
{
  HAL_TIM_IRQHandler(&htim1);
}

//用来产生方波信号///
void TIM8_Init(void)
{
    TIM_HandleTypeDef htim8;  //TIM8句柄
    RCC_ClkInitTypeDef    clkconfig;
    uint32_t              uwTimclock8 = 0;
    uint32_t              pFLatency;
    uint32_t              uwPrescalerValue8 = 0;
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};

  __HAL_RCC_TIM8_CLK_ENABLE();//使能“定时器8”的时钟,Enable TIM8 clock
  HAL_RCC_GetClockConfig(&clkconfig, &pFLatency);//Get clock configuration
  uwTimclock8 = HAL_RCC_GetPCLK2Freq();
    //读取PCLK2的时钟频率,Return the PCLK2 frequency
    //若PCLK2的分频器值为1,则和SystemCoreClock的值相等

    uwPrescalerValue8 = (uint32_t) ((uwTimclock8 / 1000000U) - 1U);
    //uwPrescalerValue8=170

  htim8.Instance = TIM8;
  htim8.Init.Period = (1000000U / 1000U) - 1U;
    //定时器周期999
  htim8.Init.Prescaler = uwPrescalerValue8;
    //设置TIM8预分频器为uwPrescalerValue8
  htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    //设置时钟分频系数,TIM8_CR1中的CKD[9:8]=00b,tDTS=ttim_ker_ck;
    //溢出时间为(999+1)*1*170/170000000/1=1毫秒

  htim8.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim8.Init.RepetitionCounter = 0;//重复计数(1-0),产生一次中断
  htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;//TIM_AUTORELOAD_PRELOAD_DISABLE;
  HAL_TIM_Base_Init(&htim8);

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
    //TIM8_TRGO是adc_ext_trg9,用来触发ADC1/2/3/4/5
//  sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
    //TIM8_TRGO2是adc_ext_trg10,用来触发ADC1/2/3/4/5

  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig);
    //Configures the TIM in master mode.

   HAL_TIM_Base_Start_IT(&htim8);
   HAL_NVIC_EnableIRQ(TIM8_UP_IRQn);//使能TIM8产生中断
   HAL_NVIC_SetPriority(TIM8_UP_IRQn, 5, 0U);
     //设置NVIC中断分组4:4位抢占优先级,0位响应优先级
     //选择中断优先级组4,即抢占优先级为4位,取值为0~15,响应优先级组为0位,取值为0
    //这里设置TIM8中断优先级为5

    LED_cnt=0;
}

//TIM8更新中断,每1ms中断一次
//为了节省内存,该中断处理程序使用“寄存器”处理,该死的HAL库,就是这么屌;

void TIM8_UP_IRQHandler(void)
{
    if( (TIM8->SR & TIM_FLAG_UPDATE) == TIM_FLAG_UPDATE)
  {//读取TIM8状态寄存器TIMx_SR的bit0(UIF),UIF=1表示产生了“TIM8更新事件”
        if( (TIM8->DIER & TIM_IT_UPDATE)  == TIM_IT_UPDATE )
    {//读取TIM8中断使能寄存器TIMx_DIER的bot0(UIE),查看UIE=1?
      TIM8->SR = ~(TIM_IT_UPDATE);
        LED_cnt++;
        if(LED_cnt>25)//每25毫秒,LED灯闪烁一次
        {
            LED_cnt=0;
              LED1_Toggle(); //LED1引脚输出电平翻转
        }
    }
  }
}

void Print_Period(void)
{
    float f;

    printf("uwDiffCapture=%u\r\n",uwDiffCapture);
    f=uwFrequency;
  printf("uwFrequency=%0.2fHz\r\n",f);
    f=1000/f;
    printf("Period=%0.2fms\r\n",f);
}

2、测试结果

3、误差分析: 

测试有点误差,LED闪烁为25*2=50ms,实际测试时间偏大,是由于TIM8的中断优先级不是最高级导致的,但测试原理是正确的 。

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

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

相关文章

springboot学习(2)

1、springboot入门 需求&#xff1a;使用 Springboot 开发一个 web 应用&#xff0c;浏览器发起请求 /hello 后&#xff0c;给浏览器返回字符串 "hello world!" 2、基本尝试步骤 创建Maven工程导入 spring-boot-starter-web (起步依赖)编写Controller提供启动类 &…

无人机 PX4 飞控 | ROS应用层开发:offboard 模式切换详细总结

无人机 PX4 飞控 | ROS应用层开发&#xff1a;offboard 模式切换详细总结 PX4 offboard 模式介绍通过mavros 进行offboard模式切换代码测试 通过地面站进行offboard模式切换通过遥控器拨杆切换offboard模式 PX4 offboard 模式介绍 PX4 是一个广为熟知的开源飞控软件&#xff0…

Yolo环境搭建(深度学习基础环境)

需要安装的东西 CUDAcuDnn魔法 一、CUDA安装(Windows10环境) 第一&#xff1a;下载驱动 第二&#xff1a;查看显卡支持的最高CUDA的版本&#xff0c;以便下载对应的CUDA安装包 第三&#xff1a;确定CUDA版本对应的cuDNN版本&#xff0c;这个其实不用太关注&#xff0c;因为…

宾得相机sd卡格式化了怎么办?分享应对之策

对于摄影爱好者而言&#xff0c;‌相机不仅是捕捉美好瞬间的设备&#xff0c;‌更是珍藏回忆的宝库。‌然而&#xff0c;‌在使用宾得相机的过程中&#xff0c;‌可能会遇到SD卡意外格式化的情况&#xff0c;‌这无疑给许多摄影爱好者带来了不小的困扰。‌SD卡格式化后&#xf…

Shell脚本入门:多命令处理

我的后端学习大纲 我的Linux学习大纲 1.什么是多命令处理 1.多命令处理就是在Shell脚本文件中编写多个Shell命令 2.入门案例&#xff1a; 2.1.需求介绍&#xff1a; 1.在已知目录/root/itheima目录&#xff0c;执行batch.sh脚本&#xff0c;实现在/root/itheima/目录下创建一…

PDF文本指令解析与文本水印去除

上次我在《PDF批量加水印 与 去除水印实践》一文中完成了对图片水印和文字水印的去除。 链接&#xff1a;https://xxmdmst.blog.csdn.net/article/details/139483535 但是对于页面对象的内容对象是单层&#xff0c;不是数组的情况&#xff0c;无法去除水印。今天我们专门研究…

Error running tomcat: Can‘t find catalina.jar

一、错误描述&#xff1a; 在运行 java-web项目时出现报错&#xff1a;Error running tomcat: Can‘t find catalina.jar 二、错误原因&#xff1a; tomcat的路径错误&#xff0c;在idea中配置正确的tomcat路径 三、解决方法&#xff1a; 1.点击EditConfigurations 2.点…

如何通过WinRAR软件有效禁止RAR压缩包内文件的修改

RAR压缩包作为一种广泛使用的文件格式&#xff0c;凭借其高压缩比和强大的功能&#xff0c;成为了许多用户保存和传输文件的首选。然而&#xff0c;在某些情况下&#xff0c;我们可能希望确保RAR压缩包内的文件不被随意修改或删除&#xff0c;以维护文件的安全性和完整性。本文…

CANoe入门(四) :全真实节点阶段和真实节点和部分仿真节点共存阶段,读取和模拟数据

1. 前言 前篇文章我们在CANoe全仿真阶段&#xff0c;模拟数据和信号。这篇文章&#xff0c;我们来看下全真实节点阶段和真实节点和部分仿真节点共存阶段&#xff0c;怎么读取数据信号&#xff0c;和模拟发送数据信号。 2. 全真实节点阶段 全真实节点阶段&#xff0c;所有的 …

LeetCode_sql_day18(1841.联赛信息统计)

描述 表: Teams ------------------------- | Column Name | Type | ------------------------- | team_id | int | | team_name | varchar | ------------------------- team_id 是该表主键. 每一行都包含了一个参加联赛的队伍信息.表: Matches -------…

StarShip v0.5版本更新

CodeSouler更新 IDE插件&#xff08;CodeSouler&#xff09; 01 代码补全优化 &#x1f680; 解决了Tab操作与IDE自带补全的冲突。 优化代码补全机制&#xff0c;调整触发逻辑并改进防抖算法&#xff0c;减少编码干扰。 修复了JetBrains插件中的多余 ) 和 } 符号问题。 02 代…

【GPT】Coze使用开放平台接口-【4】创建机器人

在前面三篇&#xff0c;我们分别创建了插件&#xff0c;插件里面添加了多个工具。接着&#xff0c;我们把插件添加到工作流内&#xff0c;成为一个开放平台API的调用节点&#xff0c;从而创建出一条业务流。分别是&#xff0c;语音伪造检测工作流&#xff0c;以及通话语音内容分…

day14JS-正则表达式

1. 什么是正则表达式 正则表达式(Regular Expression)是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到 z 之间的字母&#xff09;和特殊字符&#xff08;称为"元字符"&#xff09;&#xff0c;可以用来描述和匹配字符串的特定模式。正则表…

SRA ToolKit(v 3.1.1)安装和使用(Bioinformatics tools-032)

01 检索数据 run就是数据&#xff0c;如SRR26717485 SRA 档案数据通过 SRA 加载过程进行标准化&#xff0c;并由 SRA 工具包用于读取和生成如 FASTQ、SAM 等格式。默认的工具包配置使其能够通过登录号查找和检索 SRA 运行数据。 现在&#xff0c;公共 SRA 文件可以通过 GCP 和…

[WCT系列(四):BLASTSyncEngine

WCT系列&#xff08;一&#xff09;&#xff1a;WindowContainerTransaction类详解 WCT系列&#xff08;二&#xff09;&#xff1a;SyncTransactionQueue类详解 WCT系列&#xff08;三&#xff09;&#xff1a;WindowOrganizerController WCT系列&#xff08;四&#xff09;&a…

分治,CF 768B. Code For 1

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 https://codeforces.com/problemset/problem/768/B 二、解题报告 1、思路…

python-读取word中的内容

doc Document(rD:\xxxx\xxxx\xxx.docx) #读取word中所有内容 for p in doc.paragraphs print(p,p.text) #读取指定段落中的所有run(文字块) for run in doc.paragraphs[1].runs: print(run,run.text) #读取word中所有表格内容 for 表格 in doc.tables: print(表格) for 行 in …

什么是家庭全光组网和企业全光组网,两者有什么区别?

家庭全光组网和企业全光组网虽然都是基于光纤技术来实现高速网络连接&#xff0c;但它们在应用场景、规模、需求和技术细节上存在一些差异。 家庭全光组网 目标用户&#xff1a;面向个人家庭用户。 规模&#xff1a;一般为单个住宅内的网络覆盖&#xff0c;或者小范围内的多个房…

零基础泛微二开指南

前言 在泛微系统上开发一个自定义post接口 准备 首先准备工作要做好&#xff0c;安装一个泛微&#xff0c;之后所有的操作要在泛微的安装目录操作 参考官网安装&#xff0c;挺麻烦的&#xff1b; IDEA 1、直接新建项目 new ->Project from Existing Sources.直接打开泛…

uniapp微信小程序page-container导致滚动失效/向下偏移,返回上一页/左滑取消返回上一页

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 前提&#xff1a; 使用uniapp来做的微信小程序 有两级tab页面 要求手机的两边往中间滑时 要求&#xff08;调用手机的物理返回按钮--有震动感&#xff09; 返回上一页。具体如下图箭头所示&#xf…