如果说我们要判断一个旋钮的位置,或者是判断一个电机转了多少圈,这个电机当前的角度是什么,那么我们就会需要使用到编码器。
传统的光栅方式的编码器多是增量式,输出数字编码数值,它有两个输出
连接的处理器通过判断这两个输出用来判断当前所旋转的一个圈数角度等等
当然了除了这一种现在还有另一种适用于小型,低转速电机上的编码器——磁编码器,通过判断磁铁的磁轴方向,从而达到判断角度的效果,这一个东西的典型应用就是最近很火的步进电机闭环控制电路。
而磁铁和磁编码器是不用接触的,换句话说相对于增量式编码器,像是AS5600这类廉价的磁编码器便宜,耐用,没有物理接触磨损也没有LED灯,甚至可以没有阻力。
不过这个真的好用吗?
要使用磁编码器,那就需要安装它以及把配套磁铁给贴在电机轴的位置,这也就很大程度的限制了可以安装的编码器的电机类型。
可以安装的电机,一定要背部轴是沉入外壳的,并且最好是有大于磁铁大小的中轴,当然使用强力胶贴着也可以。为了用上它,我逆向了要安装的电机
并且为它设计了一个夹具,以保证可以稳定的让AS5600磁编码器固定在电机上面。
然后用三维打印的方式制作出来
最后加上了MOS管控制电机通断,用PWM方式来控制转速。加上了一个高C电池,这样试验的平台就可以用了。
需要注意的是,磁铁和磁编码器的最大距离不应超过1mm,超过就无法检测出,所以,如果是用这个芯片,对于装配和设计也有点小的要求。
如果说要测试,那么使用例子程序可以很好的去配置几个功能,不过这边的话我还是想单独的测试一下对于角度检测这个功能。
因为是通过I2C,所以AS5600必须主动的去读取,用上的地址有三个,数据高位低位和I2C地址。
int _raw_ang_hi = 0x0c;
int _raw_ang_lo = 0x0d;
int _ams5600_Address = 0x36;
在接线上没有什么很需要注意的,除了电源供电,A4接SDA,A5接SCL,然后初始化串口和I2C
void setup() {
Serial.begin(115200);
Wire.begin();
}
主要用的是读取字节的的函数,用于读取AS5600的角度数据
int readOneByte(int in_adr)
{
int retVal = -1;
Wire.beginTransmission(_ams5600_Address);//开始传输
Wire.write(in_adr);//需要读的地址
Wire.endTransmission();
Wire.requestFrom(_ams5600_Address, 1);//从5600处获取数据
while(Wire.available() == 0);//等待I2C总线停止占用(传输结束)
retVal = Wire.read();//读到的数据
return retVal;
}
因为角度数据是10bit的,所以要读高位地址和低位地址,再把读取到的数据组合起来,因此有读两个数值的函数
word readTwoBytes(int in_adr_hi, int in_adr_lo)
{
word retVal = -1;
/* 先读取低位 */
Wire.beginTransmission(_ams5600_Address);
Wire.write(in_adr_lo);
Wire.endTransmission();
Wire.requestFrom(_ams5600_Address, 1);
while(Wire.available() == 0);
int low = Wire.read();
/* 再读取高位 */
Wire.beginTransmission(_ams5600_Address);
Wire.write(in_adr_hi);
Wire.endTransmission();
Wire.requestFrom(_ams5600_Address, 1);
while(Wire.available() == 0);
word high = Wire.read();
high = high << 8;//高位数据左移
retVal = high | low; //高位数据左移动8位之后,和低位数据或
//或计算就是尽量等于1,任何一边有1就是1
return retVal;
}
读取两个数值组合后就是一个磁编码器数据,10bit,从0-4096的那种,根据角度划分,360/4096=0.087度,精度达到0.1度。
最后在主函数中,我用串口打印的方式来输出数据
int val = getRawAngle()*0.087;//获取10bit数值,乘以角度后转成实际角度
Serial.println(val);
//delay(10);
digitalWrite(OUTPORT,HIGH);
delayMicroseconds(400);
digitalWrite(OUTPORT,LOW);
delay(3);
整个AS5600 的程序如下,我在D8这边接了MOS管用来控制电机的转动。但是这个程序里面没有控制的部分因为我发现最后还是应该要用能控制正反转的芯片来操作电机。
#include "Wire.h"
#define OUTPORT 8 //ver2
int _raw_ang_hi = 0x0c;
int _raw_ang_lo = 0x0d;
int _ams5600_Address = 0x36;
void setup() {
Serial.begin(115200);
Wire.begin();
pinMode(OUTPORT,OUTPUT);
}
void loop() {
//Serial.println(String(convertRawAngleToDegrees(getRawAngle()),DEC));
int val = getRawAngle()*0.087;
Serial.println(val);
//delay(10);
digitalWrite(OUTPORT,HIGH);
delayMicroseconds(400);
digitalWrite(OUTPORT,LOW);
delay(3);
}
float convertRawAngleToDegrees(word newAngle)
{
/* Raw data reports 0 - 4095 segments, which is 0.087 of a degree */
float retVal = newAngle * 0.087;
return retVal;
}
word getRawAngle()
{
return readTwoBytes(_raw_ang_hi, _raw_ang_lo);
}
int readOneByte(int in_adr)
{
int retVal = -1;
Wire.beginTransmission(_ams5600_Address);
Wire.write(in_adr);
Wire.endTransmission();
Wire.requestFrom(_ams5600_Address, 1);
while(Wire.available() == 0);
retVal = Wire.read();
return retVal;
}
word readTwoBytes(int in_adr_hi, int in_adr_lo)
{
word retVal = -1;
/* Read Low Byte */
Wire.beginTransmission(_ams5600_Address);
Wire.write(in_adr_lo);
Wire.endTransmission();
Wire.requestFrom(_ams5600_Address, 1);
while(Wire.available() == 0);
int low = Wire.read();
/* Read High Byte */
Wire.beginTransmission(_ams5600_Address);
Wire.write(in_adr_hi);
Wire.endTransmission();
Wire.requestFrom(_ams5600_Address, 1);
while(Wire.available() == 0);
word high = Wire.read();
high = high << 8;
retVal = high | low;
return retVal;
}
最后的测试效果
这一个编码器的使用很简单,实际上如果你不要做什么处理,那就只要读取和校正就可以了,程序难度小。
在电机控制上,最好还是用步进电机这种能精确角度快速刹停的,如果是一般有刷电机那么在操作难度上都会大不少。
另外为了能很好的去固定这个模块,你需要设计一个装具来夹紧电机。相比较传统的光电编码器,AS5600这个芯片使用难度低下,轻便,但是有安装和电机类型的要求。