第29章 通用定时器-测量脉冲宽度
第二十九章 通用定时器-测量脉冲宽度
1. 硬件设计
无需硬件设计
2. 软件设计
2.1 编程大纲
-
TIM GPIO及捕获通道参数宏定义
-
GPIO、NVIC、Mode配置
-
捕获中断服务函数
-
主函数测试
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_StartFlag
和Capture_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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具