先导知识
1.定时器的输入捕获模式
本实验以 STM32F103C8T6 捕获 PB0(TIM3_CH3
)引脚 高电平时间长度为例讲解定时器输入捕获。
按键一端接 PB0引脚,按键另一端接 3.3V 引脚,按键按下时 PB0引脚就会接通高电平。
定时器输入捕获实验
代码实现
TIMER.h
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
#include "stm32f10x_tim.h"
void TIM3_Cap_Init(u16 arr,u16 psc);
#endif
TIMER.c
#include "TIMER.h"
#include "usart.h"
//定时器3通道3输入捕获配置初始化函数
void TIM3_Cap_Init(u16 arr,u16 psc)
{
//定义相关结构体
GPIO_InitTypeDef GPIO_InitStructure; //定义一个GPIO初始化的结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //定义一个定时器初始化的结构体
TIM_ICInitTypeDef TIM3_ICInitStructure; //定义一个定时器捕获输入初始化的结构体
NVIC_InitTypeDef NVIC_InitStructure; //定义一个中断优先级初始化的结构体
//使能相关时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能TIM3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能GPIOB时钟
//初始化 GPIOB0
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PB0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉输入,默认低电平,可以被外部电压拉高为高电平
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据GPIO_InitStructure的参数初始化GPIOB0
GPIO_ResetBits(GPIOB,GPIO_Pin_0); //初始化PB0为低电平
//初始化定时器TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //预分频器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct的参数初始化定时器TIM3
//初始化TIM3输入捕获参数
TIM3_ICInitStructure.TIM_Channel = TIM_Channel_3; //输入捕获通道3
TIM3_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM3_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI3上
TIM3_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频为不分频,分频决定几个捕获事件触发中断服务函数
TIM3_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器为不滤波
TIM_ICInit(TIM3, &TIM3_ICInitStructure); //根TIM3_ICInitStructure参数初始化定时器TIM3输入捕获
//中断分组初始化
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //先占优先级3级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //中断使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_ITConfig(TIM3,TIM_IT_Update|TIM_IT_CC3,ENABLE);//允许TIM3更新中断|允许TIM3通道3捕获中断
TIM_Cmd(TIM3, ENABLE ); //使能定时器2
}
u8 falling_flag=0; //下降沿捕获标志
u16 TIM3CH3_CAPTURE_VAL; //输入捕获计数值
//定时器3中断服务程序
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_CC3)&&falling_flag==0) //发生捕获事件&&捕获事件为上升沿(按键按下,B0接通高电平)
{
TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Falling); //设置为下降沿捕获
falling_flag=1; //更新标志
TIM_SetCounter(TIM3,0); //清除计数
//TIM3->CNT=0; //清除计数
}
else if(TIM_GetITStatus(TIM3, TIM_IT_CC3)&&falling_flag==1) //发生捕获事件&&捕获事件为下降沿
{
TIM3CH3_CAPTURE_VAL=TIM_GetCapture3(TIM3)/10; //统计上升沿事件后的高电平持续时间(按键长按时间),
//计数器频率p=主频时钟(72M)/预分频系数(psc)=72*10^6/7200=10Khz,即0.0001s=0.1ms一次计数
//计数值超过arr(本程序设置为65535)将重新计数
printf("TIM3CH3_CAPTURE_VAL:%d ms\r\n",TIM3CH3_CAPTURE_VAL); //向串口调试助手发送统计时间,注意时间上限为6.5535秒,超过6.5535秒会重新计数
TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Rising); //设置为上升沿捕获
falling_flag=0; //更新标志
TIM_SetCounter(TIM3,0); //清除计数
//TIM3->CNT=0; //清除计数
}
TIM_ClearITPendingBit(TIM3, TIM_IT_CC3|TIM_IT_Update);//每次进入中断都要清空中断标志,否则主函数将无法正常执行
}
main.c
#include "TIMER.h"
#include "usart.h"
#include "delay.h"
int main(void)
{
// static u32 i = 0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
TIM3_Cap_Init(0xffff,7200); //定时器计数上限为0xffff,计数频率为72M/7200
uart_init(9600);
while(1)
{
// //本次例程无主函数,用户可自定义
// i++;
// delay_ms(200);
}
}
程序说明
实现思路
- 编写定时器输入捕获初始化函数。
初始化 GPIO、定时器时钟→初始化 GPIO 引脚→初始化定时器→初始化定时器输入捕获→初始化中断分组→使能定时中断、输入捕获中断→使能定时器。
-
编写定时器中断服务函数。
按键按下产生上升沿,激活定时器输入捕获中断,进入中断服务函数,定时器重新计数,设置为下降沿捕获;
按键松开产生下降沿,进入中断服务函数,读取计数值,设置为上升沿捕获。 -
在主函数中定义相关初始化函数。
实现效果
- 工具 USB - TTL
- STM32F103C6T6 通过USB - TTL与串口监视助手通信,接线如下:
USB - TTL | STM32F103C6T6 |
---|---|
RX | TX(PA9) |
TX | RX(PA10) |
本实验以 STM32F103C8T6 捕获 PB0(TIM3_CH3
)引脚 高电平时间长度为例讲解定时器输入捕获。
按键一端接 PB0引脚,按键另一端接 3.3V 引脚,按键按下时 PB0引脚就会接通高电平。
知识要点
1.定时器输入捕获参数的初始化:选择输入捕获通道 3→选择捕获模式→选择映射方式→配置预分频→配置滤波。
2.重新设置捕获模式函数、获取计数值函数和清除计数值函数的使用。
2.定时器的编码器模式
在 STM32F103C8T6 中,编码器使用的是定时器接口,定时器 1,2,3,4 有编码器的功能。同时只有 CH1 和 CH2可以进行编码器模式。
本实验以 B6(TIM4CH1)、B7(TIM4CH2为例。
定时器编码器实验
代码实现
ENCODER.c
#include "ENCODER.h"
/*TIM4初始化为编码器接口*/
void Encoder_Init_TIM4(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义一个引脚初始化的结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//定义一个定时器初始化的结构体
TIM_ICInitTypeDef TIM_ICInitStructure; //定义一个定时器编码器模式初始化的结构体
// 使能 GPIO、定时器时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //使能TIM4时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能CPIOB时钟
//初始化 GPIO引脚
// B6(TIM4CH1) B7(TIM4CH2)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //PB6、PB7
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据GPIO_InitStructure的参数初始化GPIOB0
//初始化定时器
TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值
TIM_TimeBaseStructure.TIM_Prescaler = psc; // 预分频器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //选择时钟分频:不分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct的参数初始化定时器TIM4
//初始化定时器编码器
TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3:CH1、CH2同时计数,四分频
TIM_ICStructInit(&TIM_ICInitStructure); //把TIM_ICInitStruct 中的每一个参数按缺省值填入
TIM_ICInitStructure.TIM_ICFilter = 10; //设置滤波器长度
TIM_ICInit(TIM4, &TIM_ICInitStructure); //根TIM_ICInitStructure参数初始化定时器TIM4编码器模式
//使能定时器
TIM_Cmd(TIM4, ENABLE); //使能定时器4
}
//读取编码器计数
int Read_Encoder_TIM4(void)
{
int Encoder_TIM;
Encoder_TIM=TIM4->CNT; //读取计数
if(Encoder_TIM>0xefff)Encoder_TIM=Encoder_TIM-0xffff; //转化计数值为有方向的值,大于0正转,小于0反转。
//TIM4->CNT范围为0-0xffff,初值为0。
TIM4->CNT=0; //读取完后计数清零
return Encoder_TIM; //返回值
}
ENCODER.h
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
#include "stm32f10x_tim.h"
void Encoder_Init_TIM4(u16 arr,u16 psc);
int Read_Encoder_TIM4(void);
#endif
mai.c
#include "ENCODER.h"
#include "usart.h"
#include "delay.h"
int main(void)
{
delay_init();
uart_init(9600);
Encoder_Init_TIM4(0xffff,0);
while(1)
{
delay_ms(200); //每隔200ms读取一次编码器计数,即速度。
//可以使用定时中断实现更精准的速度计算,用户可自定义
printf("Encoder=%d\r\n", Read_Encoder_TIM4());
}
}
实验思路
-
编写编码器初始化函数。
使能 GPIO、定时器时钟→初始化 GPIO(两个)引脚→初始化定时器→初始化定时器编码器→使能定时器。 -
编写读取编码器计数值函数。
读取编码器计数并判断方向正反,读取完后将计数值清零。 -
编写主函数。
调用相关初始化函数,在 while 循环中以一定周期读取并向串口调试助手发送编码器计数值(即电机速度),每次读取编码器计数会在读取函数中将编码器计数清零。 -
打开串口调试助手,调波特率为 9600 (
uart_init(9600);
),对应主程序的函数调用打开串口。
STM32F103的编码器模式读取不了计数是怎么回事?
知识要点
- 定时器编码器模式的初始化:选择编码器模式→编码器模式结构体默认初始化→设置滤波器长度→初始化。
- 编码器计数的读取与置 0。