目录
基本定时器结构框图
通用定时器结构框图
高级定时器结构框图
编辑TIMx时基单元
定时工作原理
影子寄存器
编辑
定时器中断基本结构
定时器计时中断
定时器外部中断
输出比较 OC
输出比较模式
PWM基本结构
输出比较常用函数
使用PWM来驱动舵机
输入捕获 IC
测量频率
输入捕获通道
主从触发模式
编辑输入捕获基本结构
PWMI基本结构
PWM输入模式时序
编码器接口
正交编码器
编码器接口基本结构
编辑
工作模式
- 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
- 16位计数器、预分频器、自动重装寄存器的时基单元,在72Mhz计数时钟下可以实现最大65535*65535/72M = 59.65s的定时
- 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
- 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
类型 | 总线 | 功能 |
---|---|---|
高级定时器 | APB2 | 拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能 |
通用定时器 | APB1 | 拥有基本定时器全部功能、并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能 |
基本定时器 | APB1 | 拥有定时器中断、主模式触发DAC的功能 |
- 基本定时器:TIM6、TIM7
- 通用定时器:TIM2~TIM5
- 高级定时器:TIM1、TIM8
STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4
基本定时器结构框图
通用定时器结构框图
高级定时器结构框图
TIMx时基单元
- 计数器寄存器(TIMx_CNT)
- 预分频器寄存器(TIMx_PSC)
- 自动装载寄存器 (TIMx_ARR)
定时工作原理
首先,定时器时钟信号送入16位可编程预分频器寄存器(TIMx_PSC),该预分频器系数为0~65535之间的任意数值,TIMx_PSC溢出后,会向16位的计数器寄存器(TIMx_CNT)发出一个脉冲信号(CK_CNT),每来一个CK_CNT脉冲,TIMx_CNT计数器值+1,当TIMx_CNT的值与自动装载寄存器 (TIMx_ARR)的设定值相等后就自动生成更新事件(也可产生DMA请求,产生中断信号或触发ADC同步电路),并且TIMx_CNT计数器值自动清零,然后重新开始计数。
影子寄存器
比如预分频器(或者自动装载寄存器 ),这个控制寄存器带有缓冲器(影子寄存器) ,能够在工作时被改变,新的预分频器的参数在下一次更新事件到来时被采用。
(引入这类寄存器的目的实际上是为了同步,让值的变化和更新事件同步发生,防止在运行途中更改造成错误,比如自动装载寄存器一开始的值为F5,后面更新为36,此时计数器寄存器已经到F1了,如果没有影子寄存器,F5--->36立即生效,此时F1已经超过36了,F1就只能增加,但他的目标却是36,那就只能一直加到FFFF再回到0,再加到36,才能产生更新)
预分频器时序
计数器计数频率:CK_CNT = CK_PSC/ (TIMx_PSC + 1)
计数器时序
计数器溢出频率:CK_CNT_OV = CK_PSC/(TIMx_PSC + 1)/(TIMx_ARR)
计数器无预装时序
计数器有预装时序
定时器中断基本结构
计数器时钟选择
- 内部时钟(CK_INT)
- 外部时钟模式1:外部输入脚(TIx)
- 外部时钟模式2:外部触发输入(ETR)
- 内部触发输入:使用一个定时器作为另一个定时器的预分频器
时基单元的时钟选择函数
一些基本函数 (stm32f10x_tim.h)
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
// 时基单元初始化函数
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
// 使能计数器(运行控制)
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
// 使能中断输出信号
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
// 单独用来配置ETR引脚的预分频器,极性,滤波器这些参数
......
定时器计时中断
void Timer_Init(void) {
// 1.RCC开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
// 2.选择时基单元的时钟源
TIM_InternalClockConfig(TIM4);
// 3.配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; // 自动重装寄存器 溢出就会产生中断
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; // 预分频器 1ms
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStructure);
// 因为TIM_TimeBaseInit()函数最后 有TIMx->EGR = TIM_PSCReloadMode_Immediate;
// 所以要先清除事件
TIM_ClearFlag(TIM4, TIM_FLAG_Update);
// 4.配置中断输出控制,允许更新终端输出到NVIC
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
// 5.配置NVIC,并分配优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
// 6.运行控制,使能计数器
TIM_Cmd(TIM4, ENABLE);
}
中断处理函数
在启动文件中找中断处理函数
void TIM4_IRQHandler(void) {
if(TIM_GetITStatus(TIM4, TIM_IT_Update) == SET) {
// 中断要执行的代码
Count++;
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
}
}
定时器外部中断
void Timer_Init(void) {
// 1.RCC开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置GPIO,选择模式和引脚
GPIO_InitTypeDef GPIO_InitStruture;
GPIO_InitStruture.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruture.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruture.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruture);
// 2.选择时基单元的时钟源
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0f);
// 3.配置时基单元
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; // 自动重装寄存器 溢出就会产生中断
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; // 预分频器
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
// 因为TIM_TimeBaseInit()函数最后 有TIMx->EGR = TIM_PSCReloadMode_Immediate;
// 所以要先清除事件
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
// 4.配置中断输出控制,允许更新终端输出到NVIC
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
// 5.配置NVIC,并分配优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);
// 6.运行控制,使能计数器
TIM_Cmd(TIM2, ENABLE);
}
void TIM2_IRQHandler(void) {
if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) {
Num++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
uint16_t Timer_GetCounter(void) {
return TIM_GetCounter(TIM2);
}
(来一个外部事件比如遮挡一下对射式传感器,计数器值++,直到它的值等于自动重装寄存器的值时会产生溢出,计数器值清零,重新计数)
输出比较 OC
- 通过比较CNT与CCR寄存器值的关系,对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形
- 每个高级定时器和通用定时器都拥有4个输出比较通道
- 高级定时器的前3个通道额外拥有死区生成和互补输出的功能
输出比较通道(通用)
输出比较模式
模式 | 描述 |
---|---|
冻结 | CNT=CCR时,REF保持为原状态 |
匹配时置有效电平 | CNT=CCR时,REF置有效电平 |
匹配时置无效电平 | CNT=CCR时,REF置无效电平 |
匹配时电平翻转 | CNT=CCR时,REF电平翻转(输出方波) |
强制为无效电平 | CNT与CCR无效,REF强制为无效电平 |
强制为有效电平 | CNT与CCR无效,REF强制为有效电平 |
PWM模式1 | 向上计数:CNT<CCR时,REF置有效电平,CNT≥CCR时,REF置无效电平 向下计数:CNT>CCR时,REF置无效电平,CNT≤CCR时,REF置有效电平 |
PWM模式2 | 向上计数:CNT<CCR时,REF置无效电平,CNT≥CCR时,REF置有效电平 向下计数:CNT>CCR时,REF置有效电平,CNT≤CCR时,REF置无效电平 |
PWM基本结构
PWM频率:Freq = CK_PSC/(TIMx_PSC+1)/(TIMx_ARR+1)
PWM占空比:Duty = CCR / (TIMx_ARR+1)
PWM分辨率: Reso = 1 / (TIMx_ARR+1)
输出比较常用函数
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
// 输出比较控制
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
// 给输出比较结构体赋一个默认值
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
// 单独更改CCR寄存器的值
使用PWM来驱动舵机
舵机是一种根据输入PWM信号占空比来控制输出角度的装置,输入PWM信号要求:周期为20ms,高电平宽度为0.5-2.5ms
(这里的PWM波形其实是当作一个通信协议来使用的,把PWM当作一个通信协议是一个比较常见的应用)
代码:
void PWM_Init(void) {
// 开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// 选择内部时钟
TIM_InternalClockConfig(TIM2);
// 配置时基单元
TIM_TimeBaseInitTypeDef TimeBase_Initstructure;
TimeBase_Initstructure.TIM_ClockDivision = TIM_CKD_DIV1;
TimeBase_Initstructure.TIM_CounterMode = TIM_CounterMode_Up;
TimeBase_Initstructure.TIM_Period = 20000 - 1;
TimeBase_Initstructure.TIM_Prescaler = 72 - 1;
TimeBase_Initstructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TimeBase_Initstructure);
// 配置输出比较单元
TIM_OCInitTypeDef TIM_OCInitstructure;
TIM_OCStructInit(&TIM_OCInitstructure);
TIM_OCInitstructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitstructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitstructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitstructure.TIM_Pulse = 0;
TIM_OC2Init(TIM2,&TIM_OCInitstructure);
// 配置GPIO,把PWM对应的GPIO口初始化为复用推挽输出
GPIO_InitTypeDef GPIO_Initstructure;
GPIO_Initstructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Initstructure.GPIO_Pin = GPIO_Pin_1;
GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Initstructure);
// 运行控制
TIM_Cmd(TIM2,ENABLE);
}
// 设置占空比
void Set_Compare2(uint16_t compare) {
TIM_SetCompare2(TIM2,compare);
}
// 设置角度
void Set_Angle(float angle) {
Set_Compare2(angle/180*2000+500);
}
输入捕获 IC
- 当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
- 每个高级定时器和通用定时器都同时拥有4个输入捕获通道
- 可配置为PWMI(PWM的输入模式)模式,同时测量频率和占空比
- 可配合主从触发模式,实现硬件全自动测量
测量频率
- 测频法(高频信号):在闸门时间T内,对上升沿/下降沿计次,得到N,则频率 = N / T
- 测周法(低频信号):两个上升沿/下降沿内,以标准频率计次,得到N,则频率 = / N
- 中界频率: 测频法与测周法误差相等的频率点 =
输入捕获通道
主从触发模式
(实现这三块,对应库函数中三个函数,调用函数,给个参数就行了)
手册中主模式的解释(控制寄存器2)
从模式触发源的可选信号(从模式控制寄存器)
从模式选择(从模式控制寄存器)
输入捕获基本结构
这里使用了一个通道,只能测量频率
(注:CNT的值是有上限的,ARR一般设置最大65535,CNT最大也只能计65535个数,如果信号频率太低,CNT计数值可能会溢出;从模式的触发源选择,只有TI1FP1和TI2FP2,没有TI3和TI4的信号,所以如果想使用从模式自动清零CNT,就只能用通道1和通道2,对于通道3和通道4,就只能开启捕获中断,在中断里手动清零)
PWMI基本结构
使用了两个通道,可同时测量周期和占空比 ,CCR1是一整个周期的计数值,CCR2是高电平期间的计数值,CCR2/CCR1就是占空比了
PWM输入模式时序
这里使用PA0引脚产生PWM(输出),再用PA6引脚进行输入捕获,所以用线连到PA0(TIM2)(输出,产生波形)和PA6(TIM3)(输入,测量波形频率或占空比)
产生波形代码:
void PWM_Init(void) {
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; // 1kHZ
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0; // 占空比
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE);
}
void SetTIM2_Compare1(uint16_t Compare) { // 设置占空比
TIM_SetCompare1(TIM2, Compare);
}
void SetTIM2_Prescaler(uint16_t Prescaler) { // 改变频率
TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Update);
}
输入捕获代码:
/* 输入捕获,捕获PA0的TIM2的通道1的频率和占空比*/
void IC_Init(void) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM3);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; // 尽量设置大一些,防止计数溢出
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;
// 测频率的范围,给的标准频率是1Mhz,计数器最大只能计到65535
// 所以所测量的最低频率是1M/65535 15HZ,如果频率再低,计数器就要溢出了
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
// TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
TIM_Cmd(TIM3, ENABLE);
}
uint32_t IC_GetFreq(void) {
return 72000000 / (TIM_GetPrescaler(TIM3) + 1) / (TIM_GetCapture1(TIM3) + 1);
}
uint32_t IC_FetDuty(void) {
return (TIM_GetCapture2(TIM3) + 1)*100 / (TIM_GetCapture1(TIM3) + 1);
}
编码器接口
对应手册14.3.12 编码器接口模式
- 编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生正交信号脉冲,自动控制CNT自增自减,从而指示编码器的位置、旋转方向和旋转速度
- 每个高级定时器和通用定时器都拥有1个编码器接口
- 两个输入引脚借用了输入捕获的通道1和通道2
使用编码器接口的好处就是节约软件资源,如果使用外部中断来计次,当电机高速旋转时,编码器每秒产生成千上万个脉冲,程序就得频繁进中断,进中断之后完成的任务只是简单的加1减1,软件资源就被这种简单又低级的工作给占用了,所以对于这种任务可以设计一个硬件电路模块来自动完成
正交编码器
当编码器的旋转轴转起来时,A相和B相就会输出这样的方波信号,转的越快这个方波的频率就越高,所以方波的频率就代表了速度,取出任意一相的信号来测量频率,就能知道旋转速度了,但是只有一相的信号。就无法测量旋转方向,因为无论是正转还是反转,都是这样的方波,所以要测量方向,还必须要有另一根线的辅助。
编码器的设计逻辑:首先把A相和B相的所有边沿作为计数器的计数时钟,出现边沿信号时,就计数自增或者自减,是增是减,由另一相的状态来确定,当出现某个边沿时,判断另一相的高低电平。(比如初始化后,CNT的初始值为0,编码器右转,CNT++,右转产生一个脉冲,CNT的值就加一次,比如右转后产生10个脉冲后停下来,那么这个过程CNT就由0自增到10停下来;编码器左转,CNT自减,左转产生一个脉冲,CNT的值就减一次,比如编码器再左转产生5个脉冲,那CNT就在原来10的基础上自减5停下来,所以编码器接口模式基本上相当于使用了一个带有方向选择的外部时钟,他同时控制着CNT的计数时钟和计数方向,这样,CNT的值就代表了编码器的位置,如果每隔一段时间取一次CNT的值,再把CNT清零,那每次去取出来的值就表示了编码器的速度,(测频法测量正交脉冲的频率))
编码器接口在定时器的位置
编码器接口基本结构
工作模式
代码:
// TIM3 CH1 CH2
void Encoder_Init(void) {
// 开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置引脚
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置时基单元,预分频器不分频,只需要CNT执行计数即可
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 65535;
TIM_TimeBaseInitStructure.TIM_Prescaler = 0;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
// 配置输入捕获单元
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter = 0xf;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICFilter = 0xf;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
// 配置编码器接口模式
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
// 启动定时器
TIM_Cmd(TIM3, ENABLE);
}
uint16_t Encoder_Get(void) {
int16_t temp = 0;
temp = TIM_GetCounter(TIM3);
TIM_SetCounter(TIM3, 0);
return temp;
}
初始化完成后,CNT就会随着编码器旋转而自增自减,如果需要测量编码器的位置,直接读出CNT的值就行,如果需要测量编码器的速度和方向,则需每隔一端固定的闸门时间取出CNT的值,再把CNT的值清零。
void TIM2_IRQHandler(void) {
if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) {
// 1s取一次CNT的值
Speed = Encoder_Get();
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
(引脚上拉下拉的选择:看接在这个引脚的外部模块输出的默认电平,外部模块空闲默认输出高电平,选择上拉,默认输出高电平;外部模块空闲默认输出低电平,选择下拉,默认输出低电平;和外部模块保持默认状态一致,防止默认电平打架,一般来说,默认高电平,这是一个习惯的状态,上拉输入用的比较多)
(说明:学习记录)