人若无名 便可潜心练剑.|

hazy1k

园龄:7个月粉丝:14关注:0

2024-09-13 15:17阅读: 30评论: 0推荐: 0

第29章 通用定时器-测量脉冲宽度

第二十九章 通用定时器-测量脉冲宽度

1. 硬件设计

无需硬件设计

2. 软件设计

2.1 编程大纲

  1. TIM GPIO及捕获通道参数宏定义

  2. GPIO、NVIC、Mode配置

  3. 捕获中断服务函数

  4. 主函数测试

2.2 代码分析

2.2.1 TIM相关参数宏定义

/* TIM相关参数宏定义 */
#define TIMx TIM5
#define TIM_APBxClock RCC_APB1PeriphClockCmd
#define TIMx_CLK RCC_APB1Periph_TIM5
#define TIMx_Period 0xFFFF
#define TIMx_Prescaler 71
/* 输入捕获通道 */
#define TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define TIM_CH1_GPIO_PORT GPIOA
#define TIM_CH1_GPIO_PIN GPIO_Pin_0
#define TIM_CHx TIM_Channel_1
/* 中断配置 */
#define TIM_IT_CCx TIM_IT_CC1
#define TIM_IC_IRQ TIM5_IRQn
#define TIM_IC_IRQHandler TIM5_IRQHandler

2.2.2 TIM输入捕获配置

#include "GeneralTim.h"

static void TIM_NVIC_Init(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
	NVIC_InitStructure.NVIC_IRQChannel = TIM_IC_IRQ;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

static void TIM_GPIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(TIM_CH1_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin = TIM_CH1_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 输入浮空模式
	GPIO_Init(TIM_CH1_GPIO_PORT, &GPIO_InitStructure);
}

static void TIM_Mode_Config(void)
{
	TIM_APBxClock(TIMx_CLK, ENABLE);
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	TIM_TimeBaseStructure.TIM_Period = TIMx_Period; // 0xFFFF
	TIM_TimeBaseStructure.TIM_Prescaler = TIMx_Prescaler; // 71
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);
	// 输入捕获配置
	TIM_ICInitTypeDef TIM_ICInitStructure;
	TIM_ICInitStructure.TIM_Channel = TIM_CHx; // TIM5_CH1
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿捕获
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
	TIM_ICInitStructure.TIM_ICFilter = 0x0; // 不滤波
	TIM_ICInit(TIMx, &TIM_ICInitStructure);
	
	TIM_ClearFlag(TIMx, TIM_FLAG_Update|TIM_IT_CCx); // 清除中断标志位
	TIM_ITConfig(TIMx, TIM_IT_Update|TIM_IT_CCx, ENABLE); // 使能更新中断和通道捕获中断
	TIM_Cmd(TIMx, ENABLE);
}

void GTim_Init(void)
{
	TIM_NVIC_Init();
	TIM_GPIO_Init();
	TIM_Mode_Config();
}

2.2.3 TIM捕获中断服务函数

void TIM_IC_IRQHandler(void)
{
  // 如果捕获信号时间大于定时器最大周期,会产生定时器溢出,称之为更新中断
  // 我们同样需要把这个时间加入到捕获信号的周期中
  if(TIM_GetITStatus(TIMx, TIM_IT_Update) != RESET) // 更新中断
  {
    Capture_Period++;
    TIM_ClearITPendingBit(TIMx, TIM_IT_Update);
  }
  if(TIM_GetITStatus(TIMx, TIM_IT_CC1) != RESET) // 上升沿捕获中断
  {
    if(Capture_startflag == 0)
    {
      TIM_SetCounter(TIMx, 0); // 重置定时器
      Capture_Period = 0;
      Caputure_Value = 0; // 存储捕获比较值
      // 捕获到上升沿后,TIM开始捕获下降沿,从而得到高电平的时间
      TIM_OC1PolarityConfig(TIMx, TIM_ICPolarity_Falling);
      Capture_startflag = 1;
    }
    else // 捕获到下降沿
    {
      Caputure_Value = TIM_GetCapture1(TIMx); // 存储捕获比较值
      TIM_OC1PolarityConfig(TIMx, TIM_ICPolarity_Rising); // 复位为上升沿捕获
      Capture_startflag = 0;
      Capture_endflag = 1;
    }
    TIM_ClearITPendingBit(TIMx, TIM_IT_CCx); // 清除中断标志位
  }
}
  • 定时器溢出:当定时器溢出时,更新捕获周期Capture_Period,避免捕获信号周期过长时丢失数据。
  • 上升沿捕获:第一次捕获时,将定时器清零,开始捕获信号,并设置捕获极性为下降沿。
  • 下降沿捕获:第二次捕获时,记录捕获到的高电平时间,准备重新开始新的周期,并设置捕获极性为上升沿。
  • 标志位管理:通过Capture_StartFlagCapture_FinishFlag管理捕获的状态,确保捕获过程是有序的。

2.2.4 主函数测试

#include "stm32f10x.h"
#include "led.h"
#include "usart.h"
#include "GeneralTim.h"  

extern volatile uint32_t Capture_Period;
extern volatile uint32_t Caputure_Value;
extern volatile uint8_t Capture_startflag;
extern volatile uint8_t Capture_endflag;

int main(void)
{
	uint32_t time;
	uint32_t TIM_CNT_CLK = 72000000/(TIMx_Prescaler+1); // 计数器时钟频率1Mhz,预分频系数为71
	LED_Init();
	USART_Config();
	GTim_Init();
	while(1)
	{
		if(Capture_endflag == 1)
		{
			time = Capture_Period*(TIMx_Period+1)+(Caputure_Value+1);
			printf("高电平时间:%d.%d s\r\n", time/TIM_CNT_CLK, time%TIM_CNT_CLK);
			Capture_endflag = 0;
		}
	}
}

3. 小结

本章实质就是实验通用定时器的输入捕获功能,我们就如何使用输入捕获的基本流程展开讨论:

3.1 硬件连接

假设我们将输入信号连接到TIM2的通道1(即TIM2_CH1),通常这个通道是通过PA0引脚连接的。

3.2 配置代码

下面是配置TIM2为输入捕获模式的完整代码示例,分为两个主要部分:初始化代码和输入捕获中断处理函数。

#include "stm32f10x.h"

// TIM2 输入捕获初始化函数
void TIM2_InputCapture_Init(void)
{
    TIM_ICInitTypeDef TIM_ICInitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    // 使能GPIOA和TIM2的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  // 使能GPIOA的时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);    // 使能TIM2的时钟

    // 配置PA0为输入捕获功能
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;                // 选择PA0引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;     // 设置为浮空输入模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;         // 设置GPIO速度为50MHz
    GPIO_Init(GPIOA, &GPIO_InitStructure);                    // 初始化GPIOA引脚

    // 配置TIM2的基本参数
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_TimeBaseStructure.TIM_Period = 0xFFFF;                // 设置计数器周期为0xFFFF
    TIM_TimeBaseStructure.TIM_Prescaler = 0;                   // 不使用预分频器
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;    // 时钟分频因子为1
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数器向上计数
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);            // 初始化TIM2

    // 配置TIM2输入捕获
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;            // 选择通道1
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;  // 选择上升沿触发
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // 选择直接输入模式
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;        // 输入分频器设置为1
    TIM_ICInitStructure.TIM_ICFilter = 0;                        // 输入滤波器设置为0
    TIM_ICInit(TIM2, &TIM_ICInitStructure);                      // 初始化TIM2输入捕获

    TIM_Cmd(TIM2, ENABLE); // 启动TIM2计数器

    // 配置TIM2输入捕获中断
    TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE); // 使能通道1捕获中断

    // 配置NVIC中断
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;             // 选择TIM2中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;   // 设置抢占优先级为1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;          // 设置子优先级为1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;              // 使能TIM2中断
    NVIC_Init(&NVIC_InitStructure);                              // 初始化NVIC中断设置
}

// TIM2中断处理函数
void TIM2_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET) // 检查是否是通道1捕获中断
    {
        TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); // 清除通道1捕获中断挂起位

        // 获取捕获值
        uint32_t capture = TIM_GetCapture1(TIM2); // 从通道1读取捕获值

        // 处理捕获值
        // printf("Captured Value: %lu\n", capture); // 打印捕获值(需要有相应的串口配置)
    }
}

2024.9.13 第一次修订,后期不再维护

2025.1.26 去除一版的自定义结构体,简化了代码

本文作者:hazy1k

本文链接:https://www.cnblogs.com/hazy1k/p/18412240

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   hazy1k  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起