第29章 通用定时器-测量脉冲宽度
第二十九章 通用定时器-测量脉冲宽度
1. 硬件设计
无需硬件设计
2. 软件设计
- 定时器相关参数宏定义
// 当使用不同的定时器的时候,对应的GPIO是不一样的,这点要注意
// 我们这里默认使用TIM5
#define GENERAL_TIM TIM5 // 通用定时器选择-TIM5
#define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd // 使能时钟
#define GENERAL_TIM_CLK RCC_APB1Periph_TIM5 // 时钟选择
#define GENERAL_TIM_PERIOD 0XFFFF // 自动重装载值
#define GENERAL_TIM_PSC (72-1) // 时钟预分频系数
// TIM 输入捕获通道GPIO相关宏定义
#define GENERAL_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA // 通道1 GPIO时钟
#define GENERAL_TIM_CH1_PORT GPIOA // 通道1 GPIO端口
#define GENERAL_TIM_CH1_PIN GPIO_Pin_0 // 通道1 GPIO引脚
#define GENERAL_TIM_CHANNEL_x TIM_Channel_1 // 通道选择
// 中断相关宏定义
#define GENERAL_TIM_IT_CCx TIM_IT_CC1 // 输入捕获中断
#define GENERAL_TIM_IRQ TIM5_IRQn // 通用定时器中断
#define GENERAL_TIM_INT_FUN TIM5_IRQHandler // 通用定时器中断服务函数
// 获取捕获寄存器值函数宏定义
#define GENERAL_TIM_GetCapturex_FUN TIM_GetCapture1
// 捕获信号极性函数宏定义
#define GENERAL_TIM_OCxPolarityConfig_FUN TIM_OC1PolarityConfig
// 测量的起始边沿
#define GENERAL_TIM_STRAT_ICPolarity TIM_ICPolarity_Rising
// 测量的结束边沿
#define GENERAL_TIM_END_ICPolarity TIM_ICPolarity_Falling
有四个宏定义,我们是第一次见,需要简单解释一下:
-
GENERAL_TIM_GetCapturex_FUN
:指定用于获取捕获寄存器值的定时器函数。例如,TIM_GetCapture1
用于读取定时器 TIM 的捕获寄存器 1 的值。这个宏定义可以根据不同的定时器配置,方便地替换不同的函数调用。 -
GENERAL_TIM_OCxPolarityConfig_FUN
:指定用于配置输出比较通道极性的函数。例如,TIM_OC1PolarityConfig
用于设置定时器 TIM 的通道 1 的输出比较极性。这个宏定义使得在不同的定时器和通道配置下,可以统一修改极性配置的函数。 -
GENERAL_TIM_STRAT_ICPolarity
:定义测量信号的起始边沿。TIM_ICPolarity_Rising
表示测量开始于上升沿。这个宏用于配置输入捕获信号的初始边沿,以进行测量或时间间隔计算。 -
GENERAL_TIM_END_ICPolarity
:定义测量信号的结束边沿。TIM_ICPolarity_Falling
表示测量结束于下降沿。这个宏用于配置输入捕获信号的结束边沿。
- 中断优先级配置
// 中断优先级配置
static void GENERAL_TIM_NVIC_Config(void)
{
// 1.定义一个NVIC_InitTypeDef类型的结构体变量,用于配置NVIC
NVIC_InitTypeDef NVIC_InitStructure;
// 2.设置中断组为0
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
// 3.设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIM_IRQ ;
// 4.设置主优先级为 0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
// 5.设置抢占优先级为3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
// 6.使能中断
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure); // 7.根据配置结构体初始化NVIC
}
- 输入捕获通道 GPIO配置
// 输入捕获通道 GPIO 配置
static void GENERAL_TIM_GPIO_Config(void)
{
// 输入捕获通道 GPIO 端口初始化
GPIO_InitTypeDef GPIO_InitStructure;
// 输入捕获通道 GPIO 初始化
RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE); // 使能GPIO时钟
GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH1_PIN; // 选择GPIO引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 设置为浮空输入模式
GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure); // 初始化GPIO
}
- 定时器模式配置
// 定时器模式配置
static void GENERAL_TIM_Mode_Config(void)
{
// 开启定时器时钟,即内部时钟CK_INT=72M
GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);
/*--------------------时基结构体初始化-------------------------*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Period = GENERAL_TIM_PERIOD;
// 驱动CNT计数器的时钟 = Fck_int/(psc+1)
TIM_TimeBaseStructure.TIM_Prescaler = GENERAL_TIM_PSC;
// 时钟分频因子 ,配置死区时间时需要用到
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
// 计数器计数模式,设置为向上计数
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
// 重复计数器的值,没用到不用管
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
// 初始化定时器
TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);
/*--------------------输入捕获结构体初始化-------------------*/
TIM_ICInitTypeDef TIM_ICInitStructure;
// 配置输入捕获的通道,需要根据具体的GPIO来配置
TIM_ICInitStructure.TIM_Channel = GENERAL_TIM_CHANNEL_x; // 这里我们宏定义的是通道1
// 输入捕获信号的极性配置
TIM_ICInitStructure.TIM_ICPolarity = GENERAL_TIM_STRAT_ICPolarity; // 这里我们宏定义的是输出比较极性
// 输入通道和捕获通道的映射关系,有直连和非直连两种
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // 这里我们选择直连
// 输入的需要被捕获的信号的分频系数
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
// 输入的需要被捕获的信号的滤波系数
TIM_ICInitStructure.TIM_ICFilter = 0;
// 定时器输入捕获初始化
TIM_ICInit(GENERAL_TIM, &TIM_ICInitStructure);
// 清除更新和捕获中断标志位
TIM_ClearFlag(GENERAL_TIM, TIM_FLAG_Update|GENERAL_TIM_IT_CCx);
// 开启更新和捕获中断
TIM_ITConfig(GENERAL_TIM, TIM_IT_Update | GENERAL_TIM_IT_CCx, ENABLE);
// 使能计数器
TIM_Cmd(GENERAL_TIM, ENABLE);
}
- 主函数
int main(void)
{
uint32_t time; // 存储高电平脉宽时间
// TIM 计数器的驱动时钟
uint32_t TIM_PscCLK = 72000000 / (GENERAL_TIM_PSC+1);
// 串口初始化
USART_Config();
// 定时器初始化
GENERAL_TIM_Init();
printf ( "\r\n野火 STM32 输入捕获实验\r\n" );
printf ( "\r\n按下K1,测试K1按下的时间\r\n" );
while(1)
{
if(TIM_ICUserValueStructure.Capture_FinishFlag == 1)
{
// 计算高电平时间的计数器的值
time = TIM_ICUserValueStructure.Capture_Period * (GENERAL_TIM_PERIOD+1) +
(TIM_ICUserValueStructure.Capture_CcrValue+1);
// 打印高电平脉宽时间
printf ("\r\n测得高电平脉宽时间:%d.%d s\r\n",time/TIM_PscCLK,time%TIM_PscCLK );
TIM_ICUserValueStructure.Capture_FinishFlag = 0;
}
}
}
这个实验实际上就是输入捕获实验,使用定时器来测量输入信号的高电平脉宽,并通过串口输出结果,我们重点分析一下主循环:
while(1)
{
if(TIM_ICUserValueStructure.Capture_FinishFlag == 1)
{
// 计算高电平时间的计数器的值
time = TIM_ICUserValueStructure.Capture_Period * (GENERAL_TIM_PERIOD+1) +
(TIM_ICUserValueStructure.Capture_CcrValue+1);
// 打印高电平脉宽时间
printf ("\r\n测得高电平脉宽时间:%d.%d s\r\n",time/TIM_PscCLK, time%TIM_PscCLK);
TIM_ICUserValueStructure.Capture_FinishFlag = 0;
}
}
- 在主循环中,程序检查
TIM_ICUserValueStructure.Capture_FinishFlag
标志位,判断是否捕获完成。 - 如果捕获完成,计算高电平脉宽时间。
TIM_ICUserValueStructure.Capture_Period
是捕获周期值,GENERAL_TIM_PERIOD
是定时器的计数周期。TIM_ICUserValueStructure.Capture_CcrValue
是捕获值。 - 计算的时间值
time
是通过周期和捕获值结合起来得出的高电平脉宽时间。 - 将计算出的时间以秒为单位打印出来,格式为
秒.微秒
。 - 重新设置
Capture_FinishFlag
为 0,以便等待下一个捕获事件。
3. 小结
本章实质就是实验通用定时器的输入捕获功能,我们就如何使用输入捕获的基本流程展开讨论:
1. 硬件连接
假设我们将输入信号连接到TIM2的通道1(即TIM2_CH1
),通常这个通道是通过PA0引脚连接的。
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 第一次修订,后期不再维护