第五节:STM32输入捕获(用CubeMX学习STM32)

发布者:闪耀之星最新更新时间:2020-05-17 来源: eefocus关键字:STM32  输入捕获  CubeMX 手机看文章 扫描二维码
随时随地手机看文章

前言: STM32定时器输入捕获简介

STM32的输入捕获可以用于捕获脉宽, 测量时间 . 例如超声波测距模块就是需要用输入捕获功能, 通过测量输入脉冲的高电平脉宽 , 从而计算出测量物体的距离 ;

定时器PWM工作模式上篇博客讲过了, 上篇是输出PWM, 本篇是要输入, 即外面的信号送给单片机的引脚, 然后单片机测量出脉宽 ;

注: 下面根据正点原子的标准库函数教程分析, 并用CubeMX完成配置以及HAL库函数编程

在这里插入图片描述

如图所示 : 以测量高电平脉宽为例, 我们先设置定时器通道为上升沿捕获, 到1的时候触发定时器计数, 然后立刻设置为下降沿捕获, 到2的时候就捕获到下降沿, 再记录输入捕获寄存器的值, 两个时间差就是高电平时长tH;

需要注意的是, 在tH这段高电平时间内, 是由很多个向上计数的脉冲来计数的。在这里面计数可能溢出N多次; 下面是原子的库函数指南pdf里面讲解的图

在这里插入图片描述

在tH这段高电平里面, 可能有多个向上计数的脉冲, 而那个三角向上计数脉冲也可能溢出多次。就是利用这N多个向上计数的脉冲来计算tH的值的。  ARR的值是我们自己设定的,所以可以知道溢出一次是多长时间, 每溢出一次, 都给溢出次数加一。 溢出次数以及检测高低电平的数据记录在自己设定的一个变量里面


N*ARR + CCRx2即为CNT计数次数, 从而就可以算出计数时间, 算出高电平时长


N: 溢出次数  ARR: 溢出一次的时间   在一个tH内,溢出的次数不一定正好是整数, 所以用记录下CCRx2的值, 用以补充, 这样tH的值就更精确了

在这里插入图片描述

这是一个八位的变量,可以将其看做8位寄存器,不同的位储存不同的数据


5.1 操作简介

   通过信号发生器给单片机对应引脚输入一个给定频率和占空比的矩形波信号, 单片机通过输入捕获测量出高电平时长; 通过串口发送至PC端的串口调试助手查看测量的脉宽是否准确


5.2 STM32CubeMX配置初始化+IAR编程

Step1 : Cube配置

(1) 新建工程

  RCC和SYS配置, 时钟树配置都同前面一样; 还要用串口打印数据, 测试用。

RCC和SYS配置

在这里插入图片描述

USART1串口1配置(按照串口那一篇配置串口即可–>串口通信 )

在这里插入图片描述

注: 详细解释转至串口通信


时钟树配置

在这里插入图片描述

(2)TIM5参数配置

使用TIM5的通道一(TIM5_CH1)接收外部输入的信号。配置如下

在这里插入图片描述

注: 上一篇介绍了如何计算定时器溢出时间,这里溢出时间为1us  点击查看—>定时器中断及定时器产生PWM

使能TIM5中断(要在中断里面计数高电平脉宽)

NVIC设置(同样可以查看上一篇看详细讲解NVIC配置以及中断分组详解)

在这里插入图片描述

(3) 工程配置(Project Manager)

在这里插入图片描述

注 : 高级设置默认即可


(4) 生成代码(Generate Code)

Step2 : Keil/IAR编程

(1)重定向printf函数(重定向之后我们才可以使用printf函数将调试信息打印到串口调试助手) 下面是串口通信那一篇博客写的话,直接搬到这里:

在学习C语言的时候, 大家肯定都用过printf这个函数, printf可以将指定字符打印到电脑的显示器上;

但是, 单片机要使用这个就要把他打印的方向改一下, 不是打印在电脑的命令行中, 而是打印到串口里面,传输到串口调试助手. 因此我们需要重定向printf函数;

重定向后我们要将调试信息打印到USART1中, 需要对printf所依赖的打印函数fputc()重定向 .

在usart.c里面添加重定向代码

在这里插入图片描述

以后这段代码直接抄就好了, copy下来用


/* USER CODE BEGIN 0 */

#include "stdio.h"


#ifdef __GNUC__

    #define PUTCHAR_PROTOTYPE int _io_putchar(int ch)

#else 

    #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)

#endif /* __GNUC__*/

    

// 重定向C语言中的printf函数 

PUTCHAR_PROTOTYPE

{

    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);

    return ch;

}


/* USER CODE END 0 */


(2) 查看一下定时器相关代码学习

打开之后跟正点原子的标准库函数写的代码对比一下, 又利于自己理解CubeMX配置的机理, 以后会更得心应手, 慢慢的自己就可以一直标准库函数用HAL库写了。

在这里插入图片描述

这张图左边是CubeMX配置后自动生成的代码, 蓝色框框里面就是对应的CubeMX里面的配置; 右侧是原子的标准库例程代码, 可以对比一下, 增强理解


tips:CSDN只能上传不超过5M的图片, 所以这个图片经过了压缩 , 放大看可以看清晰一点。


(3) 编写中断部分函数

因为要在中断中捕获上升沿和下降沿, 所以主要代码写在中断服务函数里面

下图是计数中断

在这里插入图片描述

TIM5CH1_CAPTURE_STA虽然是我们定义的一个变量,但可以把它看做是一个8位的寄存器

在这里插入图片描述

下图是捕获中断

在这里插入图片描述

在HAL_TIM_PeriodElapsedCallback()回调函数中用以处理计数次数和时间;   在HAL_TIM_IC_CaptureCallback()回调函数负责处理捕获到的上升沿和下降沿,

并随着捕获到上升沿而更改为下降沿捕获,   随着捕获到下降沿而更改定时器为上升沿捕获.

下面是完整代码:


/* USER CODE BEGIN 1 */

/* bit7 捕获完成标识 bit6 捕获到高电平标识 bit5~0 捕获高电平后定时器溢出的次数 */

uint8_t TIM5CH1_CAPTURE_STA = 0; // 输入捕获状态

uint32_t TIM5CH1_CAPTURE_VAL; // 输入捕获值(TIM2/TIM5是32位的定时器所以这里定义为uint32_t)

// 中断服务函数里面会自动调用这个回调函数  这个是定时器更新中断中处理的函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

{

  if (htim->Instance == TIM5) // 判断是定时器5发生中断

  {

    if ((TIM5CH1_CAPTURE_STA & 0x80) == 0) // 还未成功捕获

    {

      if (TIM5CH1_CAPTURE_STA & 0x40)    // 捕获到高电平

      {

        if ( (TIM5CH1_CAPTURE_STA & 0x3f) == 0x3f ) // 如果高电平太长  做溢出处理

        {

          TIM5CH1_CAPTURE_STA |= 0x80; // 标记成功捕获了一次

          TIM5CH1_CAPTURE_VAL = 0xffffffff;

        }

        else

        {

          TIM5CH1_CAPTURE_STA++; // 若没有溢出, 就只让TIM5CH1_CAPTURE_STA自加就ok

        }

      }

    }

  }

}


// 定时器输入捕获中断处理回调函数,该函数在 HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim) 中会被调用 

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)

{

  if ( (TIM5CH1_CAPTURE_STA & 0x80) == 0 ) // 还未成功捕获

  {

    if (TIM5CH1_CAPTURE_STA & 0x40) // 捕获到一个下降沿

    {

      TIM5CH1_CAPTURE_STA |= 0x80; // 标记成功捕获到一次高电平脉宽

      TIM5CH1_CAPTURE_VAL = HAL_TIM_ReadCapturedValue(&htim5, TIM_CHANNEL_1); // 获取当前的捕获值. 即CCRx2

      TIM_RESET_CAPTUREPOLARITY(&htim5, TIM_CHANNEL_1); // 清除原来的设置

      TIM_SET_CAPTUREPOLARITY(&htim5, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING); // 配置TIM5通道1上升沿捕获

      

    }

    else

    {

      TIM5CH1_CAPTURE_STA = 0; // 清空自定义的状态寄存器

      TIM5CH1_CAPTURE_VAL = 0; // 清空捕获值

      TIM5CH1_CAPTURE_STA |= 0x40;// 标记捕获到了上升沿

      __HAL_TIM_DISABLE(&htim5); //关闭定时器5

      __HAL_TIM_SET_COUNTER(&htim5,0);

      TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1);   //一定要先清除原来的设置!!

      TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);//定时器5通道1设置为下降沿捕获

      __HAL_TIM_ENABLE(&htim5);//使能定时器5

    }

  }

}


/* USER CODE END 1 */

=

tips:每句话都有注释, 不要一看到密密麻麻代码就不看了, 看一下并不是很难理解。 也不要因为看到全是大写字母的函数或者变量而犯怵, 静下心来用两分钟看一看很容易看懂

在这里插入图片描述

还有一个问题:就是这里为什么用HAL_TIM_PeriodElapsedCallback而不是其他的callback呢? 原因在IRQ_Handler函数里面。

在这里插入图片描述

(4) 主函数程序(main.c)

首先使能定时器中断、同时定义一个变量备用:

在这里插入图片描述

/* USER CODE BEGIN 2 */

HAL_TIM_IC_Start_IT(&htim5, TIM_CHANNEL_1); // 开启输入捕获中断

__HAL_TIM_ENABLE_IT(&htim5,TIM_IT_UPDATE); //使能更新中断

long long temp = 0; // 定义一个变量用以存储捕获到的时间 long long型是为了防止数据溢出

/* USER CODE END 2 */


在while(1)循环测量数据并打印

在这里插入图片描述

while (1)

{

  /* USER CODE END WHILE */


  /* USER CODE BEGIN 3 */

  HAL_Delay(10);


  // 信号发生器输入信号 串口打印高电平时长  ms

  if (TIM5CH1_CAPTURE_STA & 0x80)   // 如果捕获完成

  {

      temp = TIM5CH1_CAPTURE_STA & 0x3f;

      temp *= 0xffffffff; // Total Overflow Time(总的溢出时间)

      temp += TIM5CH1_CAPTURE_VAL;    // Get Total High Level Time(获取总的高电平时长)

      printf("HIGH: %lld msrn", temp/1000); // Print Total High Level Time(打印总的高电平时长)

      TIM5CH1_CAPTURE_STA = 0;     // Clear Capture State , Open The Next Capture(清除捕获状态,打开下一次捕获)

  }

}

/* USER CODE END 3 */


(5) 至此程序就完成了.

2020年4月10日2020年4月10日20:53:30

== 补充说明:==


输入捕获这一节 有个学弟反应有点问题,如果用检测按键输入,按下时间太久了就会显示4194ms ; 类比到我这里的操作就是,信号发生器产生的波形频率很低的时候,就达到了测量上限,高电平最多4194ms;


这是因为原子的例程有一个时间上限 在于0x3f那个地方,0x3F就限制了时间的上限;下面这个是他给我的截图;


后面返校之后我会修改代码,完善这个地方

在这里插入图片描述

在主函数里面, TIM5CH1_CAPTURE_STA & 0x80的意思是判断有没有捕获到高电平 用TIM5CH1_CAPTURE_STA和0x80相与, 从而判断TIM5CH1_CAPTURE_STA的6位是否为1, 进而判断出是否捕获到高电平; 下面的一些涉及到相与的操作也都类似, 把一个变量看做一个寄存器, 把0x80、 0xffffffff等转换为二进制就好判断了, 在演草纸上画一下就很清楚

在这里插入图片描述

(6) 编译下载

上述代码都是之前经过测试的,但是当前由于疫情,没有条件展现结果, 如果有人用了这些代码并测试,有什么问题的话可以下面评论告知,感激不尽。效果展示会在后期补上

关键字:STM32  输入捕获  CubeMX 引用地址:第五节:STM32输入捕获(用CubeMX学习STM32)

上一篇:STM32CubeMX 配置STM32F407 实现HAL库延时微妙方案
下一篇:第六节:STM32基于HAL库的IIC通信

推荐阅读最新更新时间:2024-11-17 04:27

STM32一键连接JQ8400-FLJQ8900语音模块程序分析
首先是JQ8900.C #include JQ8900.h #include delay.h //初始化PB5使能端口的时钟 //SDA IO初始化 void JQ8900_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOE, ENABLE); //使能PB,PE端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //LED0-- PB.5
[单片机]
STM32五个串口同时收发函数配置
#include user_usart.h #include stm32f10x_usart.h /*使用microLib的方法*/ /* int fputc(int ch, FILE *f) { USART_SendData(USART1, (uint8_t) ch); while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {} return ch; } int GetKey (void) { while (!(USART1- SR & USART_FLAG_RXNE)); return ((int)(USART1-
[单片机]
基于STM32的低功耗温湿度采集器的设计与实现
STM32嵌入式系统在许多控制领域有着广泛的应用。STM32是一款基于Cortex-M3内核的微控器,该控制器在性能和成本以及低功率操作和硬实时控制方面设定了新的标准。 尽管市场上已有多种温湿度采集系统,但具有低功耗且自带数据记录功能的采集器较少,并且费用较高。本文中采用STM32F103RET6设计了温湿度采集系统。该系统工作时间长约30天,低功耗模式采用了停止模式实现,温湿度传感器使用SHTIO温湿度传感器。结合实际使用环境,采集周期为5分钟。采用18650锂电池供电,具有SD卡存储功能,且能实现USB全速通信和串口通信。 1、温湿度采集器设计 1.1、硬件设计 采集器的结构框架如图l所示,主要有供电模块,USB全速通
[单片机]
基于<font color='red'>STM32</font>的低功耗温湿度采集器的设计与实现
STM32单片机中使用SPI通信的方法
  在本教程中,我们将使用 STM32F103C8 的 Blue Pill 板替换一个 Arduino 板,并将使用 SPI 总线与 Arduino 板进行通信。在这个STM32 SPI 示例中,我们将使用Arduino UNO作为 Slave,STM32F103C8 作为 Master,两个16X2 LCD 显示器分别连接在一起。两个电位器还与STM32(PA0)和Arduino(A0)相连,通过改变电位器来确定主机到从机和从机到主机的发送值(0到255)。   STM32F103C8中的SPI   比较 Arduino 和 STM32F103C8 Blue Pill 板中的 SPI 总线,STM32 有2 条 SPI 总线
[单片机]
在<font color='red'>STM32</font>单片机中使用SPI通信的方法
STM32的ADC用法你都知道吗?
AD采样在电路中是一种比较常见的功能,可以用于电池电压检测、传感器值读取、信号采集等。STM32的ADC,由于引入了DMA,以及多种触发源,功能自然强大,用法也多种多样。这里简单说下单通道情况下,AD采样的几种用法。 1、AD单次转换+软件启动 最基本的用法,通过程序启动AD,AD采集一次,我们就去读一次。这种情况,建议开启AD转换完成中断,在中断中读出AD值并做处理。 这种方式的优点是配置简单,缺点么,太T么简单~ 初始化的时候,启动一次。然后在主循环里,每隔一秒启动一次。 在中断回调函数里,进行相关处理: 电脑输出如下: 2、连续转换+软件启动 在方法1的基础上做调整,从单次转换,变成连续转换。也就是说,只需要
[单片机]
<font color='red'>STM32</font>的ADC用法你都知道吗?
STM32的条状指纹采集与拼接系统
引言 指纹识别作为生物认证中可靠性较高、方便性较好、性价比较高的一种技术,已经得到了全面的应用。指纹采集是指纹识别的重要组成,其核心硬件就是指纹传感器。随着移动嵌入式设备自身体积越来越小,以及对成本和功耗的更高要求,指纹传感器也向着小型化方向发展。其中条状指纹传感器(也称为刮擦式传感器)具有体积小、价格低、无指纹残留等优点,在移动嵌入式领域得到越来越多的应用。条状指纹传感器是一种窄条形半导体传感器,无法一次性采集到完整的指纹图像,要求连续采集划过传感器表面的手指指纹,并对采集到的窄条状指纹图像序列进行拼接,从而形成完整的指纹图像。 本系统采用STM32F103RD处理器作为主控器件,采用AES1711条状指纹传感器作为采集
[单片机]
<font color='red'>STM32</font>的条状指纹采集与拼接系统
STM32之SPI主机例程
#include stm32f10x.h /* RCC时钟配置 */ void RCC_config(void) { ErrorStatus HSEStartUpStatus; /* RCC寄存器设置为默认配置 */ RCC_DeInit(); /* 打开外部高速时钟 */ RCC_HSEConfig(RCC_HSE_ON); /* 等待外部高速时钟稳定 */ HSEStartUpStatus = RCC_WaitForHSEStartUp(); if(HSEStartUpStatus == SUCCESS) { /* 设置HCLK = SYSCLK */ RCC_HCLKConfig(RCC_SYSCLK_D
[单片机]
STM32直接驱动RGB接口的TFT数字彩屏设计
引言     随着工业技术的不断发展,人机界面的开发及应用空前火热,为了具有比较友好的人机界面,TFT数字彩屏被广泛的应用,但是TFT彩屏通常都不带有控制器,所以现在驱动彩屏的方案大致有2种:     ①采用ARM9或者更高级别的平台,芯片上带有TFT控制器,可以直接挂接TFT数字屏。     ②采用低端CPU处理器平台,外加TFT控制器模块,再挂接TFT数字屏。     对于方案①来说,系统的复杂度会莫名地增加,再加上该类的平台中主MCU多为BGA封装,对于需求很多小量多样化产品的客户来说,较难以接受这样的方案;而 ②方案平白无故添加了一个LCD控制器。这两种方案无论哪一种都增加了硬件成本,本文提出了一种由STM32的FSMC总线
[嵌入式]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved