STM32G474之TIM1捕获1模式

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.rhkb.cn/news/412959.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利用 Web 浏览器构建 Java Media Player

如果您需要在 Java 桌面应用程序中嵌入媒体播放器&#xff0c;有几种方法可供选择&#xff1a; 您可以使用 JavaFX Media API 来实现所有必需的媒体播放器功能。虽然稍显过时但仍然可用的 Java Media Framework 也可以作为一种解决方案。您可以集成像 VLCJ 这样的第三方 Java …

如何选择适合企业的财税自动化解决方案

财税自动化解决方案是现代企业提升财务管理效率、降低运营成本的关键工具。然而&#xff0c;市场上的财税自动化产品琳琅满目&#xff0c;功能各异&#xff0c;企业在选择时常常感到困惑。本文金智维将从中小型的需求出发&#xff0c;帮助企业了解如何选择适合自身的财税自动化…

QT实战项目之音乐播放器

项目效果演示 myMusicShow 项目概述 在本QT音乐播放器实战项目中,开发环境使用的是QT Creator5.14版本。该项目实现了音乐播放器的基本功能,例如开始播放、停止播放、下一首播放、上一首播放、调节音量、调节倍速、设置音乐播放模式等。同时还具备搜索功能,通过搜索歌曲名字…

另一种关于类的小例

前言 我们还是以一段关于构造函数的代码作为开端&#xff0c;我们以之前银行家的小项目为背景 class Account {constructor(owner, currency, pin) {this.owner owner;this.currency currency;this.pin pin;} }const ITshare new Account(ITshare, EUR, 21211); console.…

零基础入门天池镜像提交--windows场景VirtualBox虚拟机安装linux系统并ssh远程登录,直至镜像的制作及提交

背景&#xff1a;由于本人只有一台windows,天池上的比赛需要提交镜像&#xff0c;自己试了好多方法给windows安装linux&#xff0c;但是始终没安装成功。最终采用在利用VirtualBox安装linux虚拟机&#xff0c;使用MobaXterm进行ssh登陆linux&#xff0c;镜像的制作、push、提交…

Java+Swing实现学生选课管理系统

JavaSwing实现学生选课管理系统 一、系统介绍二、系统展示1.课程查询2.课程添加3.退课 三、系统实现四、其他1.其它系统2.获取源码 一、系统介绍 本系统实现了学生登录和管理员登录&#xff0c;学生实现选课&#xff0c;查看已选课程&#xff0c;修改密码&#xff0c;查看学生…

Java | Leetcode Java题解之第386题字典序排数

题目&#xff1a; 题解&#xff1a; class Solution {public List<Integer> lexicalOrder(int n) {List<Integer> ret new ArrayList<Integer>();int number 1;for (int i 0; i < n; i) {ret.add(number);if (number * 10 < n) {number * 10;} els…

顺序表

目录 1. 数据结构 2. 顺序表 1&#xff09;线性表 2&#xff09;顺序表分类 3、动态顺序表的实现 1. 数据结构 数据&#xff1a;常见的数值1、2、3、4.....、教务系统里保存的用户信息&#xff08;姓名、性别、年龄、学历等 等&#xff09;、网页里肉眼可以看到的信息&…

vTESTstudio系列12--vTESTstudio中的动态函数库介绍2

在上期的文章&#xff08;vTESTstudio系列11--vTESTstudio中的动态函数库介绍1&#xff09;中&#xff0c;我们详细介绍了osek_tp.dll的接口&#xff0c;本章开始给大家介绍如何通过osek_tp.dll的接口去发送诊断指令&#xff0c;Let‘s Go!!! 目录 1. CanTp发送数据的函数&am…

图表操作——图表保存为图片+多个图表批量保存为压缩包——js技能提升

使用场景&#xff1a; echarts图表&#xff1a;生成的柱状图/折线图/饼图等可以实现图表的导出&#xff0c;导出格式为一个图片。也可以支持多个图表同时导出为图片&#xff0c;以压缩包的形式下载下来。 下面介绍单个导出批量导出的具体用法&#xff1a; 1.单个导出功能——…

可信捐赠系统的设计与开发论文

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统可信捐赠系统信息管理难度大&#xff0c;容错率低&#x…

选对文档版本管理软件:10款工具详解

本篇文章中提到的工具包括&#xff1a;1.PingCode&#xff1b;2.Worktile&#xff1b;3.联想Filez&#xff1b;4.蓝凌云&#xff1b;5.阿里云盘&#xff1b;6.360亿方云&#xff1b;7.无忧企业文档&#xff1b;8.DocStar ECM&#xff1b;9.Dropbox Business&#xff1b;10.Shar…

数据库之心:MySQL 探索(一)mysql的安装和基本介绍

欢迎来到我们的MySQL博客&#xff01;在这里&#xff0c;我们将深入探讨MySQL数据库系统的各个方面&#xff0c;包括基础知识、优化技巧、实践案例以及最新的行业趋势。 目录 前言 什么是数据库&#xff1f; 数据库产品 MySQL安装 解压 配置 添加环境变量 初始化MySQL …

Linux云计算 |【第二阶段】SHELL-DAY4

主要内容&#xff1a; grep、egrep的使用&#xff0c;基本/扩展正则、sed的使用&#xff08;非交互、逐行处理、常用命令与选项&#xff09; 补充&#xff1a;grep [-E]选项&#xff0c;表示允许使用扩展的正则表达式&#xff08;同等于egrep&#xff09; 一、grep 与 egrep 的…

基于深度学习的单目标跟踪系统

基于深度学习的单目标跟踪&#xff0c;效果吊打传统算法&#xff0c;3060显卡上达到实时&#xff0c;代码python和c两个版本都有。 基于深度学习的单目标跟踪系统是一种先进的计算机视觉技术&#xff0c;它可以实现实时的、高精度的目标跟踪。与传统的基于特征匹配或模板匹配的…

LED的使用寿命评估

一&#xff0e;前言 LED光源随着时间的推移&#xff0c;输出光功率会逐渐衰减。在某一时间点&#xff0c;当LED发出的光衰减到一定水平&#xff0c;就无法满足应用要求。因此&#xff0c;常遇见客户会索取产品中LED光源的使用寿命数据。那么怎样确认LED的使用寿命呢&#xff1…

MySQL5.6迁移到DM8

注意&#xff1a; MySQL 5.7 与 MySQL 8.0 的语法有所区别&#xff0c;本文档是将MySQL5.6迁移到DM8。 迁移前准备 源库 数据库信息 统计源端业务库要迁移的数据量、字符编码、归档保留等信息。 内容 说明 备注 数据库架构 单机 节点数 1 数据库版本 MySQL 5.6…

UE5 摄像机图像采集到材质 映射到 UI 和 物体表面

一.创建SceneCapture2D的组件 二.创建用于 映射的 贴图 三.将RenderTarget贴图放到SceneCapture2D的摄像机上Scene Capture的TextureTarget 四.这个时候的映射贴图&#xff0c;产生的材质可以直接。放到Plane上。 五&#xff0c;但是如果要用于UI,还需要更改SceneCapture2D的摄…

C语言基础(二十七)

1、位字段&#xff08;Bit-fields&#xff09;也是一种数据结构&#xff0c;允许在结构体&#xff08;struct&#xff09;或联合体&#xff08;union&#xff09;中定义其成员占用特定的位数。对于需要精确控制内存布局或处理硬件寄存器映射等场景非常有用。位字段使得开发者能…

WebRTC协议下的视频汇聚融合技术:EasyCVR视频技术构建高效视频交互体验

视频汇聚融合技术是指将来自不同源、不同格式、不同网络环境的视频流进行集中处理、整合和展示的技术。随着视频监控、远程会议、在线教育、直播娱乐等领域的快速发展&#xff0c;视频数据的规模急剧增长&#xff0c;对视频处理能力和效率提出了更高要求。视频汇聚融合技术通过…