STM32直流电机驱动与编码器测速

1.什么是编码器

​ 编码器是一种将角位移或者角速度转换成一连串电数字脉冲的旋转式传感器,我们可以通过编码器测量到底位移或者速度信息。编码器从输出数据类型上分,可以分为增量式编码器和绝对式编码器。
​ 从编码器检测原理上来分,还可以分为光学式、磁式、感应式、电容式。常见的是光电编码器(光学式)和霍尔编码器(磁式)。

​ 这里使用的编码器就是霍尔编码器

2.编码器原理

​ 光电编码器是一种通过光电转换将输出轴上的机械几何位移量转换成脉冲或数字量的传感器。光电编码器是由光码盘和光电检测装置组成。光码盘是在一定直径的圆板上等分地开通若干个长方形孔。由于光电码盘与电动机同轴,电动机旋转时,检测装置检测输出若干脉冲信号,为判断转向,一般输出两组存在一定相位差的方波信号。

​ 霍尔编码器是一种通过磁电转换将输出轴上的机械几何位移量转换成脉冲或数字量的传感器。霍尔编码器是由霍尔码盘和霍尔元件组成。霍尔码盘是在一定直径的圆板上等分地布置有不同的磁极。霍尔码盘与电动机同轴,电动机旋转时,霍尔元件检测输出若干脉冲信号,为判断转向,一般输出两组存在一定相位差的方波信号。

​ 可以看到两种原理的编码器目的都是获取AB相输出的方波信号,其使用方法也是一样,下面是一个简单的示意图。

20200725161723303.png

3.编码器接线说明

20200725161759680.png

​ 这是一款增量式输出的霍尔编码器。编码器有AB相输出,所以不仅可以测速,还可以辨别转向。根据上图的接线说明可以看到,只需给编码器电源5V供电,在电机转动的时候即可通过AB相输出方波信号。编码器自带了上拉电阻,所以无需外部上拉,可以直接连接到单片机IO读取。

4.编码器的四倍频计数

​ 这是一项实用的技术,可以真正地把编码器的精度提升4倍。作用可类比于单反相机的光学变焦,而并非牺牲清晰度来放大图像的数码变焦。

20200725161906256.png

​ 常规的方法,我们只测量A相(或B相)的上升沿或者下降沿,也就是上图中对应的数字1234中的某一个,这样就只能计数3次。而四倍频的方法是测量A相和B相编码器的上升沿和下降沿。这样在同样的时间内,可以计数12次(3个1234的循环)。这就是四倍频的原理。

​ 这里的话不建议采用利用IO口中断的方式来采集脉冲,这样会过于占用单片机资源

​ STM32正好就带有编码器接口,这样操作起来会方便很多

5.STM32 TIM 编码器模式配置

/**************************************************************************
函数功能:把TIM2初始化为编码器接口模式
入口参数:无
返回  值:无
**************************************************************************/
void Encoder_Init_TIM2(void)
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  
  TIM_ICInitTypeDef TIM_ICInitStructure;  
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);        //使能定时器2的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);        //使能PA端口时钟

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;        //端口配置
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;     //浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);                              //根据设定参数初始化GPIOA

  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  TIM_TimeBaseStructure.TIM_Prescaler = 0x0;                             // 预分频器 
  TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD;     //设定计数器自动重装值
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;    //选择时钟分频:不分频
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//TIM向上计数  
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
  TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3

  TIM_ICStructInit(&TIM_ICInitStructure);
  TIM_ICInitStructure.TIM_ICFilter = 10;
  TIM_ICInit(TIM2, &TIM_ICInitStructure);

  TIM_ClearFlag(TIM2, TIM_FLAG_Update);                                        //清除TIM的更新标志位
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);                            //使能TIM2的中断,防止计数溢出

  TIM_SetCounter(TIM2,0);
  TIM_Cmd(TIM2, ENABLE); 
}

/**************************************************************************
函数功能:单位时间读取编码器计数
入口参数:定时器
返回  值:速度值
**************************************************************************/
int Read_Encoder(u8 TIMX)
{
    int Encoder_TIM;    
    switch(TIMX)
    {
        case 2:  Encoder_TIM= (short)TIM2 -> CNT;  TIM2 -> CNT=0;break;
        case 3:  Encoder_TIM= (short)TIM3 -> CNT;  TIM3 -> CNT=0;break;    
        case 4:  Encoder_TIM= (short)TIM4 -> CNT;  TIM4 -> CNT=0;break;    
        default: Encoder_TIM=0;
    }
    return Encoder_TIM;
}

/**************************************************************************
函数功能:TIM2中断服务函数
入口参数:无
返回  值:无
**************************************************************************/
void TIM2_IRQHandler(void)
{                                       
    if(TIM2->SR&0X0001)//溢出中断
    {
        TIM2->SR&=~(1<<0);//清除中断标志位 
    &#125;                            
&#125;

6.速度计算方法

这里计算的是真实的电机轮子的物理转速

电机转动一圈的脉冲数:num1 单位:个

单位时间:t 单位:秒

单位时间内捕获的脉冲变化数:num2 单位:个 (反应电机正反转)

电机轮子半径:r 单位:m

速度:speed 单位: mm/s
$$
Speed = 1000num2(2pir/num1)/t
$$

7.PWM驱动直流电机及1S中断配置

void TIM3_Int_Init(u16 arr,u16 psc)
&#123;
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能

    TIM_TimeBaseStructure.TIM_Period = arr;             //设置在下一个更新事件装入活动的自动重装载寄存器周期的值     计数到5000为500ms
    TIM_TimeBaseStructure.TIM_Prescaler =psc;         //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;     //不分频
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

    TIM_Cmd(TIM3, ENABLE);  //使能TIM3外设

&#125;


void TIM4_Int_Init(u16 arr,u16 psc)
&#123;
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能

    TIM_TimeBaseStructure.TIM_Period = arr;     //arr为999 psc为71 就是1ms的中断溢出
    TIM_TimeBaseStructure.TIM_Prescaler =psc;  
    TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); 

    TIM_ITConfig(TIM4,TIM_IT_Update ,ENABLE ); //开启TIM4中断

    TIM_Cmd(TIM4, ENABLE);  //使能TIM4外设

&#125;

void TIM4_IRQHandler(void)                                                       //TIM4中断回调函数
&#123;
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)     //检查指定的TIM中断发生与否
        &#123;
            time++;
            if(time == 1000)
            &#123;
                time = 0;
                Encoder = Read_Encoder(2);
            &#125;
            TIM_ClearITPendingBit(TIM4, TIM_IT_Update);  //清除TIMx的中断待处理位        
        &#125;
&#125;

void TIM3_PWM_Init(u16 arr,u16 psc)
&#123;  
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;


    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);        //使能定时器3时钟
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);      //使能GPIO外设时钟

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;                             //CH1
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                  //复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);                                    //初始化GPIO

  //初始化TIM3
    TIM_TimeBaseStructure.TIM_Period = arr; 
    TIM_TimeBaseStructure.TIM_Prescaler =psc;  
    TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    //初始化TIM3 CHl PWM模式     
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 
     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;         //输出极性:TIM输出比较极性高
    TIM_OC1Init(TIM3, &TIM_OCInitStructure);                                          //根据T指定的参数初始化外设TIM3 OC1

    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR1上的预装载寄存器

    TIM_Cmd(TIM3, ENABLE);  //使能TIM3
&#125;

实际效果

1610648473170.gif 1610829367881.gif