一、知识点
1. 编码器
两相编码器(正交编码器):两相编码器由 A 相和 B 相组成,相位差为 90 度。当旋转方向为顺时针时,A 相先变化,然后 B 相变化;当旋转方向为逆时针时,B 相先变化,然后 A 相变化。通过检测相位差变化,可以确定旋转的方向。STM32 单片机可以使用定时器的正交编码器模式(Encoder Mode)来实现两相编码器的接口。
2. 编码器接口
编码器的两个输入引脚,就是每个定时器的CH1和CH2引脚,CH3和CH4不能接编码器。
最终的实验现象,编码器有两个输出,一个是A相,一个是B相,然后接入到STM32,定时器的编码器接口,编码器的接口自动控制定时器时基单元中的CNT计数器,进行自增或自减。比如初始化之后,CNT初始值为0,然后编码器右转,CNT就++,右转产生一个脉冲,CNT就加一次,比如右转产生10个脉冲后,停下来,那么这个过程CNT就由0自增到10,停下来,编码器左转,CNT就–,左转产生一个脉冲,CNT就自减一次, 比如编码器再左转产生5个脉冲,那CNT就在原来10的基础上自减5,停下来。
编码器接口,其实就相当于是一个带有方向控制的外部时钟,同时控制着CNT的计数时钟和计数方向,这样CNT的值就表示了编码器的位置。如果我们每隔一段时间取一次编码器的值,再把CNT清零,那么每次取出来的值就表示了编码器的速度。
编码器测速实际上就是测频法测正交脉冲的频率,CNT计次,然后每隔一段时间取一次计次,这就是测频法的思路。编码器计次能根据旋转方向,不仅能自增计次还能自减计次,是一个带方向的测速。
每个高级定时器和通用定时器都拥有1个编码器接口。
二、框图
三、编码器计数操作
解释:当Tl1先产生脉冲,Tl2后产生脉冲。(A相先变化,B相后变化)时电机正转,计数器开始计数CNT++。当产生毛刺时,CNT不计数。Tl2先产生脉冲,Tl1后产生脉冲。(B相先变化,A相后变化)时电机反转,计数器CNT- -。当产生毛刺时不计数。
解释:在TL1和TL2上计数时:当Tl1FP1信号为上升沿,且相对信号(Tl2)为高电平状态,则为向下计数,即CNT- -。若Tl2FP2信号在上升沿,且相对信号(TL1)为高电平,则为向上计数,即CNT++。各类信号状态与计数关系请查看上表。
四、相关寄存器
1. 第①部分对应寄存器
2. 第②部分对应寄存器
五、代码编写
1. 库函数编写
void Encoder_Init_TIM4(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义一个引脚初始化的结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//定义一个定时器初始化的结构体
TIM_ICInitTypeDef TIM_ICInitStructure; //定义一个定时器编码器模式初始化的结构体
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //使能TIM4时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能CPIOB时钟
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 = 0xffff; //设定计数器自动重装值
TIM_TimeBaseStructure.TIM_Prescaler = 0; // 预分频器
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
}
int16_t Encoder_Get(void)
{
int16_t Temp;
Temp = TIM_GetCounter(TIM4); //获取编码器当前值
TIM_SetCounter(TIM4, 0); //将编码器计数器清0
return Temp;
}
六、电机测速原理
1. 基本信息
- 需要知道电机的一些基本信息,如减速比、多少线的编码器(这些参数需要问电机的的商家,或者查看手册获得)。
- 知道在代码中使用的是编码器模式几。如编码器模式1、2、3。使用编码器模式1和2则为2倍频,使用编码器模式3则为4倍频。频数高的精度会更高。
2. 计算电机一圈的脉冲数
假设电机减速比为30,500线的编码器,使用编码器模式3。
电机一圈脉冲数:30×500×4=60000
3. 计算电机的速度
这是我们需要再使用一个定时器用于定时,在定时器中断中获取编码器的脉冲。如:我们将定时器2设置为10ms触发一次中断,则每过10ms我们获取一次脉冲,假设10ms测得脉冲数为600。则1ms的脉冲数为60(600/10),1s的脉冲数为60000(60*1000)。又因为电机一圈脉冲为60000,所以电机的速度为:60000/60000=1 圈/s 。代码如下所示:
float cnt=0;
float speed=0;
void Time2_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = arr; //电机PWM频率要和定时器采样频率一致
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE);
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
cnt=Encoder_Get();
speed=cnt/10*1000/60000;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
//------------------------mian.c------------
int main()
{
Time2_Init(7199,99); //定时时间为7200*100/72 = 10000us =10ms
while(1){}
}