本文主要介绍直流减速电机电机驱动接线,与编码器应用:
TB6612FNG直接连接电机线,输出控制电压,具体的引脚说明如下:
VM:接12V电压(电机的额定电压)
VCC:模块内部逻辑供电,3.3V或者5V都可
GND:接地,三个GND有一个接地就行
STBY:置高,模块正常工作,一般3.3V或5V即可
PWMA:单片机输出PWM信号,占空比:0~100,对应控制输出电压,控制电机转速。一般输出10KHz的PWM就行
AIN0/AIN1:连接单片机IO,控制电压方向,从而控制电机转动方向
AO1/AO2:连接电机线,作为TB6612FNG的输出口
编码器是一种将角位移或者角速度转换为一连串电数字脉冲的旋转式传感器,所以可以用来测量转动位置和测量速度。
电机编码器有AB两相,AB相输出两个脉冲,编码器的所有信息都融合在这两个脉冲。
一般编码器都有四根线,VCC、GND、A相、B相。AB相接到单片机IO口捕获脉冲。
理想的AB相波形图如下。
(1)根据A相超前还是滞后B相来判断正反转。
(2)电机每转一圈的脉冲数目是相同的,所以测量的脉冲数目除上一圈的总数目就是电机当前角度了。如果采用四倍频计数,还要再除上4。
(3)如果每隔一段时间读脉冲数目然后把计数器清0,那么所读的数目除上时间就相当于电机转速。
如果使用STM32,可以用定时器输入捕获的编码器模式来计数。直接读取计数器数值就可以了,而且自带滤波。
利用32单片机(以STM32F103VET6为例,用Keil编程)捕获AB相的脉冲有两种方法第一种利用GPIO的外部中断来捕获跳变沿从而可以记录脉冲数。第二种利用定时器的编码器模式来记录脉冲数。
外部中断捕获波形
/**************************************************************************
函数功能:外部中断采集编码器初始化
入口参数:无
返回 值:无
**************************************************************************/
void Encoder_Init_TIM_Exit0(void)//用于检测编码器A相使用PA0引脚
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOB
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//跳变沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
// 配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void Encoder_Init_TIM_Exit1(void)//用于检测编码器B相使用PA1引脚
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOB
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);
EXTI_InitStructure.EXTI_Line=EXTI_Line1;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//跳变沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
//配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
以上代码完成了A相 B相外部中断的初始化当有跳变沿发生时则进入外部中断服务函数,在外部中断服务函数中对脉冲进行计数,如果电机正转则计数的变量Encoder_A_EXTI(在中断服务函数中计数时使用)就加1,反转则减1。使用四倍频计数提高测速精度,所以A相和B相的跳变沿检测到后我们都进行计数。首先看A相
void EXTI0_IRQHandler(void) //外部中断线0服务函数
{
EXTI->PR=1<<0; //清除LINE上的中断标志位
if(PAin(0)==0) //这里判断检测到的是否是下降沿
{
if(PAin(1)==0) Encoder_A_EXTI++;//看B相的电平如果是高电机就是正转则加1,否则就是反转减1
else Encoder_A_EXTI--;
}
else //上升沿
{
if(PAin(1)==0) Encoder_A_EXTI--; //B相低电平为正转,加1,高电平反转减1
else Encoder_A_EXTI++;
}
}
B相同理
void EXTI1_IRQHandler(void)//外部中断线1服务函数
{
EXTI->PR=1<<1; //清除LINE上的中断标志位
if(PAin(1)==1) //这里判断检测到的是否是上升沿
{
if(PAin(0)==0) Encoder_A_EXTI++; //看A相的电平如果是低,电机就是正转则加1,否则就是反转减1
else Encoder_A_EXTI--;
}
else
{
if(PAin(0)==0) Encoder_A_EXTI--;
else Encoder_A_EXTI++;
}
}
通过上数代码我们就可以在Encoder_A_EXTI这个变量中记录A相和B相的脉冲数,然后我们再通过一个函数来读取并清零Encoder_A_EXTI。
int Read_Encoder(u8 TIMX)
{
int Encoder_TIM;
switch(TIMX)
{
case 2: Encoder_TIM=(short)Encoder_A_EXTI; Encoder_A_EXTI=0; break;
default: Encoder_TIM=0;
}
return Encoder_TIM;
}
然后再一个定时中断中使用此函数即可知道电机的转速。
下面是第二种方法的代码
定时器编码器模式
/**************************************************************************
函数功能:把TIM3初始化为编码器接口模式
入口参数:无
返回 值:无
**************************************************************************/
void Encoder_Init_TIM3(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//使能定时器3的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PB端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOB
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 预分频器
TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD-1; //设定计数器自动重装值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频:不分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM向上计数
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ClearFlag(TIM3, TIM_FLAG_Update);//清除TIM的更新标志位
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
//Reset counter
TIM_SetCounter(TIM3,0);
TIM_Cmd(TIM3, ENABLE);
}
初始化完成后我们可以直接在TIM3的CNT计数器中读取到我们捕获的脉冲数,这里要注意一点应为CNT寄存器的最大只能是65536超过这个值该计数器(CNT)就会溢出,所以我们要即使去读取CNT然后把它清空。同样是利用 Read_Encoder()这个函数
int Read_Encoder(u8 TIMX)
{
int Encoder_TIM;
switch(TIMX)
{
case 2: Encoder_TIM=(short)Encoder_A_EXTI; Encoder_A_EXTI=0; break;
case 3: Encoder_TIM= (short)TIM3 -> CNT; TIM3 -> CNT=0;break;
default: Encoder_TIM=0;
}
return Encoder_TIM;
}