> 文章列表 > 平衡小车之编码器的使用(深夜学习——单片机)

平衡小车之编码器的使用(深夜学习——单片机)

平衡小车之编码器的使用(深夜学习——单片机)

一、平衡小车要编码器干什么?

采集电机的转速,并转化成脉冲信号并发送给单片机,最后计算出小车的速度。

二、编码器原理:(以常用的增量式为例)

  1. 霍尔编码器:

(1)组成:

平衡小车之编码器的使用(深夜学习——单片机)

霍尔编码器主要包括两个部分:霍尔码盘,霍尔元件组成。

霍尔码盘:由多个NS极间隔的圆形磁体,其中你买电机看到xx线,就是指有多少组NS极

霍尔元件:一种基于霍尔效应,能检测磁场的元件(如果不懂霍尔效应,建议先去了解一下),当N极靠近就产生高电平;当S极靠近就产生低电平

(3)工作原理:

假设初始位置如上图所示,则A、B输出的电压如图:

平衡小车之编码器的使用(深夜学习——单片机)

当主轴顺时针旋转时,输出脉冲A通道信号位于B通道之前;

当主轴逆时针旋转时,输出脉冲A通道信号位于B通道之后。

(4)由输出信号得出转速:

假设编码器为13线,也就是每转一圈产生13个脉冲信号,我们只需要在单位时间内对产生的脉冲信号进行计数,再将总数除以13和计数时间,就可以得出转速。

(5)由AB确定转动方向:

通过观察上图可以知道,顺时针转动时,A相会先变化,导致A相上升沿,B相处于高电平;A相下降沿,B相处于低电平。逆时针时,B相会先变化,导致A相上升沿,B相处于低电平,A相下降沿,B相处于低电平。

三、计算方法:

  1. 倍频:

(1)LSB最低有效位:

类似于一个长10cm的尺子,假设平均分为100格,那么最低有效位就是0.1cm

(2)编码器的LSB:

编码器的LSB主要由线数决定,我们知道每经过一组NS极,都会产生一个脉冲信号,而上升沿和下降沿都可以触发单片机的中断,我们只需要在此时计数,就可以将转一圈分为13份(假设为13线)

(3)编码器的倍频:

不知道你们有没有用很多个点拟合一个正弦函数,我们会发现缩小了看,可以看到十分平滑曲线,但是逐渐放大看局部,就会发现崎岖无比的曲线。

推广到编码器的计数,如果我们可以尽可能将转动一圈分为更多份,那么局部的有害干扰就对整体的结果产生的影响就显得微乎其微了

(4)二倍频:

我们知道每经过NS的边界,霍尔传感器就会上升沿和下降沿,而这个变化之间的距离又是相同的,所以我们让单片机对A或B相的上升沿和下降沿都计数,也可以看成输出频率加倍了,也就是二倍频。

(5)四倍频:

如果将一个NS极的角度叫做一个周期的角度,而A、B相之间的角度被设计为相差四分之一个周期的角度。综合起来看,也就是A,B相的上升沿和下降沿之前相差的距离是相同的,如果对A、B相的上升沿和下降沿都进行计数,就能实现四倍频

  1. 电机转速计算:

(1)“13线”:码盘中缺口的个数,每经过一个缺口就会产生一个脉冲信号,也就是说每产生13个脉冲信号相当于码盘转一圈

(2)”减速比1:30“:轮子转一圈,码盘转三十圈

(3)轮子转速 =码盘转速/减速比

码盘转速 = 一定时间内计数值/转一圈的计数值/计数时间

四、参考代码:(以二倍频,STC15系列为例)

  1. 主函数:

#ifndef PUBLIC_H
#define PUBLIC_H#include <STC15F2K60S2.H>
#define u8 unsigned char
#define u16 unsigned int
#endif
#include "public.h"
#include "uart.h"
#include "stdio.h"
#include "encoder.h"
#include "intrins.h"
#include "timer.h"
#include "pwm.h"u8 sign_5ms;
float rpm;//单位:转每分钟
int count_enco,cacl_temp;void main()
{UartInit();E_IT1_Init();Timer0_Init();//驱动电机转动PMW_Init();        AIN1 = 1;AIN2 = 0;BIN1 = 1;BIN2 = 0; while(1){//每5ms计算一次if(sign_5ms == 1){printf("cacl_temp:%d\\r\\n",cacl_temp);//轮子转速,单位:转/分钟rpm =  cacl_temp/26.0/5*1000*60/30;printf("rpm:%f\\r\\n",rpm);sign_5ms = 0;}}
} void E_IT1() interrupt 2
{//相异为正,相同为负if(Encoder1_A ^ Encoder1_B)count_enco++;elsecount_enco--;//指示灯,检测是否正常工作P00 ^= 1;
}
void Timer0_Isr(void) interrupt 1
{sign_5ms++;cacl_temp = count_enco;count_enco = 0;
}
  1. 编码器初始化:

#ifndef ENCODER_H
#define ENCODER_H#include "public.h"sbit Encoder1_A = P3^3;
sbit Encoder1_B = P2^5; 
void E_IT1_Init();#endif
#include "encoder.h"/*外部中断0初始化
*/
void E_IT1_Init()
{//跳变沿触发IT1 = 0;//开启中断EX1 = 1;EA = 1;
}
  1. PWM初始化:

#ifndef PWM_H
#define PWM_H#include "public.h"sbit pwm_A = P3^6;
sbit AIN1 = P2^0;
sbit AIN2 = P2^1;sbit pwm_B = P3^7;
sbit BIN1 = P2^2;
sbit BIN2 = P2^3;void PMW_Init(void);
#endif
#include "pwm.h"void PMW_Init(void)        
{//CCP1:P36 CCP2:P37P_SW1 = P_SW1 & 0xcf | (0x02<<4);CL = CH = 0;CMOD = 0x08;//CCP1:     占空比:70%PCA_PWM1 = 0x00;CCAP1H = CCAP1L = 0;CCAPM1 = 0X42;//CCP2:     占空比:70%PCA_PWM2 = 0x00;CCAP2H = CCAP2L = 0;CCAPM2 = 0X42;//CCON = 0X40;
}
  1. 定时器初始化:

#ifndef     TIMER_H
#define     TIMER_H#include "public.h"void Timer0_Init(void);#endif
#include "timer.h"void Timer0_Init(void)        //5毫秒@12MHz
{AUXR |= 0x80;            //定时器时钟1T模式TMOD &= 0xF0;            //设置定时器模式TL0 = 0xA0;                //设置定时初始值TH0 = 0x15;                //设置定时初始值TF0 = 0;                //清除TF0标志TR0 = 1;                //定时器0开始计时ET0 = 1;                //使能定时器0中断EA = 1;
}
  1. 串口通信:

#ifndef UART_H
#define UART_H#include "public.h"void UartInit(void);#endif
#include "uart.h"
#include <stdio.h> 
void UartInit(void)        //9600bps@12MHz
{SCON = 0x50;        //8位数据,可变波特率AUXR |= 0x40;        //定时器时钟1T模式AUXR &= 0xFE;        //串口1选择定时器1为波特率发生器TMOD &= 0x0F;        //设置定时器模式TL1 = 0xC7;            //设置定时初始值TH1 = 0xFE;            //设置定时初始值ET1 = 0;            //禁止定时器中断TR1 = 1;            //定时器1开始计时
}/*重定义putchar函数
*/
char putchar (char c)
{SBUF = c;while(!TI);TI = 0;return c;
}