文章目录
- 一、STM32F407 定时器编码器功能
- 1.1 STM32定时器简介
- 1.2 STM32定时器编码器功能
- 二、带编码器的直流电机
- 三、代码与验证
- 3.1 初始化代码
- 3.2 验证
一、STM32F407 定时器编码器功能
1.1 STM32定时器简介
STM32的定时器功能非常强大,根据官方手册,定时器的功能如下
高级定时器
TIM1 和 TIM8 主要特性
TIM1 和 TIM8 定时器具有以下特性:
● 16 位递增、递减、递增/递减自动重载计数器。
● 16 位可编程预分频器,用于对计数器时钟频率进行分频(即运行时修改),分频系数
介于 1 到 65536 之间。
● 多达 4 个独立通道,可用于:
— 输入捕获
— 输出比较
— PWM 生成(边沿和中心对齐模式)
— 单脉冲模式输出
● 带可编程死区的互补输出。
● 使用外部信号控制定时器且可实现多个定时器互连的同步电路。
● 重复计数器,用于仅在给定数目的计数器周期后更新定时器寄存器。
● 用于将定时器的输出信号置于复位状态或已知状态的断路输入。
● 发生如下事件时生成中断/DMA 请求:
— 更新:计数器上溢/下溢、计数器初始化(通过软件或内部/外部触发)
— 触发事件(计数器启动、停止、初始化或通过内部/外部触发计数)
— 输入捕获
— 输出比较
— 断路输入
● 支持定位用增量(正交)编码器和霍尔传感器电路。
● 外部时钟触发输入或逐周期电流管理。
通用定时器
TIM2 到 TIM5 主要特性
通用 TIMx 定时器具有以下特性:
● 16 位(TIM3 和 TIM4)或 32 位(TIM2 和 TIM5) 递增、递减和递增/递减自动重载计
数器。
● 16 位可编程预分频器,用于对计数器时钟频率进行分频 (即运行时修改),分频系数介
于 1 到 65536 之间。
● 多达 4 个独立通道,可用于:
— 输入捕获
— 输出比较
— PWM 生成(边沿和中心对齐模式)
— 单脉冲模式输出
● 使用外部信号控制定时器且可实现多个定时器互连的同步电路。
● 发生如下事件时生成中断/DMA 请求:
— 更新:计数器上溢/下溢、计数器初始化(通过软件或内部/外部触发)
— 触发事件(计数器启动、停止、初始化或通过内部/外部触发计数)
— 输入捕获
— 输出比较
● 支持定位用增量(正交)编码器和霍尔传感器电路
● 外部时钟触发输入或逐周期电流管理
1.2 STM32定时器编码器功能
根据下面的图可以看出通用定时器也是有编码器输入功能。
在编码输入时是有上升沿与下降沿的,我们可以利用外部中断分别捕获编码器A、B项边沿,手写逻辑消除毛刺并解析编码器数据,但这是比较复杂的。其实这里的脉冲输入是一种特殊的输入捕获情况,因此stm32专门在定时器中提供了编码器模式,可大大简化解析过程。
选择编码器接口模式时,如果计数器仅在 TI2 边沿处计数,在 TIMx_SMCR 寄存器中写入
SMS=001;如果计数器仅在 TI1 边沿处计数,写入 SMS=010;如果计数器在 TI1 和 TI2 边 沿处均计数,则写入 SMS=011。
通过编程 TIMx_CCER 寄存器的 CC1P 和 CC2P 位,选择 TI1 和 TI2 极性。如果需要,还可对输入滤波器进行编程TI1 和 TI2 两个输入用于连接增量编码器。请参见表 75。如果使能计数器(在 TIMx_CR1 寄 存器的 CEN 位中写入“1”),则计数器的时钟由 TI1FP1 或 TI2FP2 上的每次有效信号转 换提供。TI1FP1 和 TI2FP2 是进行输入滤波器和极性选择后 TI1 和 TI2 的信号,如果不进行 滤波和反相,则 TI1FP1=TI1,TI2FP2=TI2。将根据两个输入的信号转换序列,产生计数脉 冲和方向信号。根据该信号转换序列,计数器相应递增或递减计数,同时硬件对 TIMx_CR1 寄存器的 DIR 位进行相应修改。任何输入(TI1 或 TI2)发生信号转换时,都会计算 DIR 位,无论计数器是仅在 TI1 或 TI2 边沿处计数,还是同时在 TI1 和 TI2 处计数。
编码器接口模式就相当于带有方向选择的外部时钟。这意味着,计数器仅在 0 到 TIMx_ARR 寄存器中的自动重载值之间进行连续计数(根据具体方向,从 0 递增计数到 ARR,或从 ARR 递减计数到 0)。因此,在启动前必须先配置 TIMx_ARR。同样,捕获、比较、预分频 器、触发输出功能继续正常工作。
在此模式下,计数器会根据增量编码器的速度和方向自动进行修改,因此,其内容始终表示编码器的位置。计数方向对应于所连传感器的旋转方向。下表汇总了可能的组合(假设 TI1 和 TI2 不同时切换)。
下图为双项模式下计数效果,可见在A、B中仅一项有**抖动(**就是编译器无效的脉冲,需要过滤)时,计数值加减后保持不变,实现了抖动补偿
从图上可以看出,定时器是采集A、B两相的脉冲,当编码器输出一个周期时,定时器计数值为4,这是要必须记住的哈。
二、带编码器的直流电机
我手上刚好有一个带编码器的直流电机,电机参数如下,520电机包含编码器款该电机采用全金属齿轮,具备功率大、抗干扰好、精度高、寿命长的特点。
在这里需要说明一下,如果电机转一圈,那么黄线输出的脉冲数量为:1130 = 330个, 那么绿线输出的脉冲数量为:1130 = 330个。根据上面分析的定时器编码器模式,电机转一圈,定时器计数脉冲的个数为:330*4 = 1320个。
三、代码与验证
电机实物图
3.1 初始化代码
/****************************************
引脚说明
A相连接PB6
B相连接PB7
PB6 -- TIM4_CH1
PB7 -- TIM4_CH2
****************************************/
void TIM4_Int_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);//开启GPIOC的时钟
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_TIM4);//将引脚6映像到TIM8
GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_TIM4);//将引脚7映像到TIM8
//定时器设置-------------------------------------------------------------
TIM_TimeBaseInitStructure.TIM_Period = 330*4; //重装载值 这是两相脉冲总数量
TIM_TimeBaseInitStructure.TIM_Prescaler=0x0; //预分频
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //时钟分割
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);//初始化TIM3
//编码器模式设置--------------------------------------------------------------
TIM_EncoderInterfaceConfig(TIM4,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//计数模式3
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10; //滤波器值
TIM_ICInit(TIM4, &TIM_ICInitStructure);
//溢出中断设置--------------------------------------------------------------
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //允许TIM4溢出中断
NVIC_InitStructure.NVIC_IRQChannel =TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0x01;
NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;
NVIC_Init(&NVIC_InitStructure);
//Reset counter-----------------------------------------------
TIM_SetCounter(TIM4,0); //TIM3->CNT=0
TIM_Cmd(TIM4, ENABLE);
}
主函数
int Encoder_Timer_Overflow; //编码器溢出次数
u16 Previous_Count;
u32 speed=0;
int circle_count = 0; //记录电机正反转
//void TIM8_CC_IRQHandler(void)
//{
// if(TIM_GetITStatus(TIM8,TIM_IT_Update)==SET)
// {
// Encoder_Timer_Overflow++;
// }
// TIM_ClearITPendingBit(TIM8,TIM_IT_Update);
//}
//电机转动一圈产生中断
void TIM4_IRQHandler(void)
{
if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET)
{
Encoder_Timer_Overflow++;
if((TIM4->CR1>>4 & 0x01)==0) //DIR==0 通过寄存器TIMx_CR1第四位判断 0:电机正转
circle_count++;
else if((TIM4->CR1>>4 & 0x01)==1)//DIR==1 通过寄存器TIMx_CR1第四位判断 1:电机正转
circle_count--;
}
TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
}
void Read_Encoder(void)
{
u16 Current_Count; //一段时间的脉冲数
u16 Enc_Timer_Overflow_one; //当前脉冲数
Enc_Timer_Overflow_one=Encoder_Timer_Overflow; //获取中断溢出次数
Current_Count = TIM_GetCounter(TIM4); //得到脉冲数
Encoder_Timer_Overflow=0; //清0方便下次计算
//speed = (u32)(Enc_Timer_Overflow_one*车轮周长+Current_Count/(330*4.0)*车轮周长); //进行平均测速
}
int main(void)
{
u16 m = 0,n = 0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
usart1_init(115200);
//TIM8_Int_Init();
TIM4_Int_Init();
while(1)
{
delay_ms(500);
delay_ms(500);
//CNT的值,能够表示当前获取了多少个脉冲
printf("获取定时器中的CNT值:%d\r\n",TIM4->CNT);
//值为正,表示正转圈数比反转圈数多,反之亦然;值递增,表示正在正转,值递减,表示正在反转。
printf("目前轮子正或反的圈数:%d\r\n",circle_count);
}
return 0;
}
3.2 验证
总结一下,通过对这些的理解,那么可以计算速度、角度、加速度等。
最后代码链接:https://download.csdn.net/download/wwwqqq2014/87124313?spm=1001.2014.3001.5503