一、用途与工作原理
用途:用于测量信号的参数,比如周期和频率。
工作原理:在输入捕获模式下,当捕获单元捕捉到外部信号的有效边沿(上升沿/下降 沿/双边沿)时,将计数器的当前值锁存到捕获/比较寄存器TIMx_CCR, 供用户读取。
二、输入捕获框图理解
当产生捕获信号时,将计数器当前的值存到捕获/比较影子寄存器中,但是影子寄存器不能直接访问,所以捕获转移信号(capture_transfer)产生时,将影子寄存器的值存到预装载寄存器中。这是程序员就可以读取预装载寄存器的值。
简单理解:先设置上升沿捕获,当上升沿来到后,读取当前计数器的值CNT1。再设置下降沿捕获,当下降沿到来后,读取当前计数器的值为CNT2。高电平持续时间=CNT2-CNT1。
三、脉宽测量原理
前提:计数器为递增计数模式。
目的:计算高电平持续时间。
实现过程:
- 先设置定时器为上升沿捕获。在 t1 时刻检测到上升沿,记录当前的计数值为CCRx1,并将计数器的值清0。(开始计算计数器溢出次数)
- 计数器清0后,设置下降沿捕获。在 t2 时刻检测到下降沿,记录当前计时器的值为CCRx2。在下降沿捕获前可能会有 N次溢出,则总时间= N*( ARR+1) +CCRx2−CCRx1。其中N 为溢出次数,ARR 为自动重装载值,CCRx2 为下降沿捕获的计数值,CCRx1 为上升沿捕获的计数值。
四、输入捕获配置流程
- 设置滤波器
- 选择触发边沿
- 选择输入通道映射
- 设置事件分频器-不分频
- 使能输入通道
五、实验
实验内容:使用TIM5的通道1来捕获按键高电平脉宽时间totalTime 。( 按键按下为高电平 )
1. 寄存器版本
(1) 初始化PA0。
a. 打开GPIOA时钟
b. 配置为复用功能
c. 映射AF2
//1.初始化PA0引脚
RCC->AHB1ENR |=(0x01 <<0); //开启GPIOA的时钟
GPIOA->MODER &=~(0X03 <<0);
GPIOA->MODER |=(0X02 <<0); //配置为复用功能
GPIOA->AFR[0] &=(0XFu <<0);
GPIOA->AFR[0] |=(0X02 <<0);//映射AF2
(2) 初始化基本定时配置。
a.打开TIM5时钟
b.选择内部时钟源
c.配置 CR1 、CKD、URS
d.设置重载值
e.设置分频值
f.产生更新事件
//2.初始化基本定时器配置
RCC->APB1ENR |= 1 << 3; //打开TIM5时钟
TIM5->SMCR &=~(0x07 << 0); //选择内部时钟源
TIM5->CR1 |= 0x01 <<2; //计数器上溢产生更新事件
TIM5->CR1 =0; //整体清0
TIM5->CR1 |=(0x01 <<7); //有影子,缓冲。
TIM5->ARR = 99;//设定计数器主动重装值(决定PWM的频率)
TIM5->PSC = 7199;//预分频器 0为不分频
TIM5->EGR |=(0x01 <<0); //产生更新事件,将上面配置更新到影子寄存器
(3) 初始化输入通道。
a.设置滤波器
b.选择触发边沿
c. 选择输入通道映射
d.设置事件分频器-不分频
e.使能输入通道
//3. 初始化输入通道
TIM5->CCMR1 |= (0xF << 4);//设置滤波器
TIM5->CCER |=(0x5<<1);//选择极性,输入使能
TIM5->CCMR1 &= ~(0x3 << 0);
TIM5->CCMR1 |=(0x1<<0);//选择输入通道映射,映射到Tl1上
TIM5->CCMR1 &= ~ (0x3 << 2);//设置事件分频器-不分频
TIM5->CCER |= (0x1 << 0);//使能捕获输入通道
(4) 设置更新中断和捕获中断。
a.设置优先级
b.使能NVIC响应
c.使能更新中断和捕获中断- DIER
d.使能定时器5
NVIC_SetPriority (TIM5_ IRQn, NVIC_ EncodePriority(7-2, 1, 2));//设置优先级
NVIC_EnableIRQ (TIM5_ IRQn); // 使能NVIC响应
TIM5->DIER |= (0x1 << 0) ;//使能更新中断
TIM5->DIER |= (0x1 << 1) ;//使能捕获中断
TIM5->CR1 |= (0x1 << 0) ;//使能计数器
(5) 编写TIM5 _lRQHandler中断服务函数
中断服务函数
{
if(更新中断)
{
}
else(捕获中断)
{
}
}
void TIM5_IRQHandler (void)
{
static u16 updateNum = 0; //进入更新中断的次数
static u8 updateFlag = 0; //更新标志
u32 totalTime = 0 ; //总时间
u16 curTime = 0; //记录当前时间
if (TIM5->SR & (0x1 << 0)) //更新中断
{
TIM5->SR &= ~(0x1 << 0); //清除更新中断标志
if(updateFlag ==1) updateNum ++; //记录N个溢出时间次数
}
else if (TIM5->SR & (0x1 << 1)) //捕获中断
{
TIM5->SR &= ~(0x1 << 1); //清除捕获中断标志
if(KEY1 == 1) // 上升沿-按键按下
{ TIM5->CNT = 0;
updateFlag = 1; //开始记录更新事件的次数
}
else //按键松开
{
curTime = TIM5->CNT; //按键 松开的时间节点
totalTime =updateNum *100 +curTime; //得到总时间
updateFlag = 0; //清除记录更新事件标志
updateNum = 0; //更新事件次数清零方便下一次记录
}
}
}
2. 库函数版本
#include "stm32f10x.h"
volatile uint32_t t1_capture = 0; // 用于存储上升沿时间戳
volatile uint32_t t2_capture = 0; // 用于存储下降沿时间戳
volatile uint32_t totalTime = 0; // 按键高电平脉宽时间(单位:计时器计数值)
volatile uint32_t overflow_count = 0; // 定时器溢出次数
// GPIO 初始化函数
void GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// 使能 GPIOC 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
// 配置 PC6 为输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
// 定时器初始化函数
void TIM5_Init(void) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
// 使能 TIM5 时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
// 配置定时器基本参数
TIM_TimeBaseStructure.TIM_Period = 0xFFFF; // 最大计数值
TIM_TimeBaseStructure.TIM_Prescaler = 7199; // 预分频,1kHz 定时器时钟
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_UP;
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
// 配置输入捕获模式
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; // 通道1
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿捕获
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // 直接连接到 TI1
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 不分频
TIM_ICInitStructure.TIM_ICFilter = 0x0F; // 输入滤波器
TIM_ICInit(TIM5, &TIM_ICInitStructure);
// 使能输入捕获中断
TIM_ITConfig(TIM5, TIM_IT_CC1, ENABLE);
// 使能 TIM5
TIM_Cmd(TIM5, ENABLE);
// 配置中断
NVIC_EnableIRQ(TIM5_IRQn);
}
// 定时器中断服务函数
void TIM5_IRQHandler(void) {
// 检查是否为捕获中断
if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET) {
// 上升沿捕获
if (TIM_GetCapturePolarity(TIM5, TIM_Channel_1) == TIM_ICPolarity_Rising) {
t1_capture = TIM_GetCapture1(TIM5); // 记录上升沿时间
TIM_SetCapturePolarity(TIM5, TIM_Channel_1, TIM_ICPolarity_Falling); // 设置为下降沿捕获
}
// 下降沿捕获
else {
t2_capture = TIM_GetCapture1(TIM5); // 记录下降沿时间
// 计算高电平脉宽时间,考虑溢出次数
if (t2_capture >= t1_capture) {
totalTime = t2_capture - t1_capture + overflow_count * (TIM5->ARR + 1);
} else {
totalTime = (TIM5->ARR + 1) - t1_capture + t2_capture + (overflow_count - 1) * (TIM5->ARR + 1);
}
overflow_count = 0; // 清除溢出计数器
// 切换回上升沿捕获
TIM_SetCapturePolarity(TIM5, TIM_Channel_1, TIM_ICPolarity_Rising);
}
// 清除捕获中断标志
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1);
}
// 溢出中断处理
if (TIM_GetFlagStatus(TIM5, TIM_FLAG_Update) != RESET) {
overflow_count++; // 溢出次数增加
TIM_ClearFlag(TIM5, TIM_FLAG_Update); // 清除溢出标志
}
}
int main(void) {
// 初始化 GPIO 和 TIM5
GPIO_Init();
TIM5_Init();
while (1) {
// 可以在主循环中根据 totalTime 来进行其他操作
// 例如输出按键按下的高电平脉宽时间
if (totalTime > 0) {
// 这里可以加入其他逻辑处理,比如输出 totalTime
}
}
}