一、AS5600介绍
AS5600是一个易于编程的磁性旋转位置传感器,具有高分辨率的12位模拟或PWM输出。这种非接触式系统测量一个直径磁化的轴上磁铁的绝对角度。 引脚如下图 他有两种供电模式:5V和3.3V 我们为了和stm32F103C8T6单片机的电压一致,也使用3.3V供电,然后开始画PCB。
二、pcb设计
使用嘉立创EDA画图
三、实物照片
已经把as5600贴到了电机后面,暂时没有稍微长一点的螺丝,不然用四个螺丝固定更好。
四、程序代码
代码实现的功能: 1、可以实时显示当前绝对位置的角度(0~360) 2、可以实时显示增量角度,比如正转了2转,显示为720度,又在此基础上反转了3转,显示为-360度。 3、一上电当前位置即可自动设置为初始零位。 3、也可以测实时的速度(暂时没有写这部分功能,实现也简单,两次位置差除以时间即可)
难点:as5600接在了电机尾部,步进电机的轴转5.18圈,减速器输出的轴转1圈,as5600直接读的是步进电机轴的位置(我这个步进电机带了减速器,减速比为5.18)所以还需要一些转换。
程序大概思路: 设置3个变量temp0(记录一上电之后电机轴的初始位置,在程序中只需运行一次即可),temp1(用于记录本次电机轴的位置),temp2(用于记录下一次电机轴的位置)。temp1和temp2主要用于过360度判断,即如果temp-temp2>180度(在很短的时间内),那么肯定是过了360度了,即下一转开始了,因为电机不可能在很短的时间转过这么多度。而且从程序初始化以后就要一直拿比较temp1和temp2,每次都不能少,比较完了就把temp2赋值给temp1,然后下次再获取最新的temp2以后,继续和temp1比较。就是要一直和上一次的数据比较,中间不能又一次断开,这样可以保证不漏过那个变化的点,每次过360度的时候,进行一个计数(程序中用过零点标记sign_angle计数),然后只要知道初始角度(temp0)、过零点的次数(sign_angle),以及当前点的角度(temp2),就可以算出角度增量。 程序代码如下:
下面是as5600.h文件
#ifndef __AS5600__
#define __AS5600__
#include "sys.h"
#include "stm32f10x.h"
#define Slave_Addr 0x36
#define Write_Bit 0
#define Read_Bit 1
#define Angle_Hight_Register_Addr 0x0C
#define Angle_Low_Register_Addr 0x0D
#define Jian_Su_Bi 5.18
void AS5600_Init ( void );
u16 AS5600_Read_Len ( u8 addr, u8 reg, u8 len, u8 *buf );
void Get_Ini_Val(void);
void Get_Temp_Add(void);
void Change_angle(void);
void Get_Num_sign(void);
下面是as5600.c文件
#include "as5600.h"
#include "i2c.h"
#include "delay.h"
u32 angle_ini = 0;
u32 temp0 = 0;
u32 temp1 = 0;
u32 temp2 = 0;
u32 temp_add = 0;
u8 buf[2] = {0};
int sign_angle = 0;
double True_Angle = 0.0;
int dir = 0;
double Current_Angle = 0;
void AS5600_Init ( void ) {
IIC_Init();
}
u16 AS5600_Read_Len ( u8 addr, u8 reg, u8 len, u8 *buf ) {
IIC_Start();
IIC_Send_Byte ( ( addr << 1 ) | Write_Bit );
if ( IIC_Wait_Ack() ) {
IIC_Stop();
return 1;
}
IIC_Send_Byte ( reg );
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte ( ( addr << 1 ) | Read_Bit );
IIC_Wait_Ack();
while ( len ) {
if ( len == 1 ) {
*buf = IIC_Read_Byte ( 0 );
} else {
*buf = IIC_Read_Byte ( 1 ) & 0x000f;
}
len--;
buf++;
}
IIC_Stop();
return 0;
}
void Get_Ini_Val(void)
{
u8 i = 0;
u32 transfer = 0;
for ( i = 0; i < 20; i++ ) {
AS5600_Read_Len ( Slave_Addr, Angle_Hight_Register_Addr, 2, buf );
delay_ms ( 5 );
}
for ( i = 0; i < 20; i++ ) {
AS5600_Read_Len ( Slave_Addr, Angle_Hight_Register_Addr, 2, buf );
transfer += ( ( buf[0] << 8 ) | buf[1] );
delay_ms ( 5 );
}
temp0 = transfer / 20;
temp2 = temp0;
temp1 = temp0;
}
void Get_Temp_Add(void)
{
if(sign_angle == 0)
{
if(temp2 >= temp0)
{ temp_add = temp2 - temp0;
dir = 0;}
else
{ temp_add = temp0 - temp2;
dir = 1;}
}
else if(sign_angle > 0)
{
temp_add = 4096 + temp2 - temp0 + ( sign_angle - 1)*4096;
dir = 0;
}
else
{
temp_add =4096 + temp0 - 4096*(sign_angle+1) - temp2;
dir = 1;
}
}
void Change_angle(void)
{
True_Angle = (temp_add/(Jian_Su_Bi*4096))*360;
}
void Get_Current_angle(void)
{
u32 x0 = 0;
x0 = (temp_add*100)%((u32)(Jian_Su_Bi*100)*4096);
Current_Angle = (x0/(Jian_Su_Bi*4096))*360/100;
}
void Get_Num_sign(void)
{
u32 x;
AS5600_Read_Len ( Slave_Addr, Angle_Hight_Register_Addr, 2, buf );
temp2 = ( ( buf[0] << 8 ) | buf[1] );
if(temp1 >= temp2)
{
x = temp1 - temp2;
if(x>2048)
{
sign_angle++;
}
}
else
{
x = temp2 -temp1;
if(x>2048)
{
sign_angle--;
}
}
temp1 = temp2;
}
下面是main函数
#include "stm32f10x.h"
#include "oled.h"
#include "delay.h"
#include "timer.h"
#include "key.h"
#include "exti.h"
#include "as5600.h"
#include "stdio.h"
#include "sys.h"
#include "key.h"
#include "mtor.h"
#include "exti.h"
#include "oledfont.h"
extern u8 TIM2_Pulse_TIM3_Counter_OK;
uint16_t pwm = 7199;
u32 pulsecnt = 200;
extern double Current_Angle;
extern double True_Angle;
extern int dir;
extern u32 temp_add;
extern u32 temp2;
extern u32 temp0;
uint8_t sign = 0;
extern int sign_angle;
int main(void)
{
char strff[21];
char strff2[21];
static uint8_t x = 1;
delay_init();
OLED_Init();
led_Init();
KEY_Init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
TIM3_PWM_Init(pwm,19);
TIM_SetCompare2(TIM3,10);
motor_init();
TIM2_Init(pulsecnt);
EXTIX_Init();
AS5600_Init();
memset(strff,0,sizeof(strff));
memset(strff2,0,sizeof(strff2));
Get_Ini_Val();
while(1)
{
Get_Temp_Add();
Change_angle();
Get_Current_angle();
Get_Num_sign();
sprintf(strff,"%.2f",True_Angle);
sprintf(strff2,"%.2f",Current_Angle);
if(dir == 0)
{
OLED_ShowChar(0,0,'+');
OLED_ShowChar(0,4,'+');
}
else
{
OLED_ShowChar(0,0,'-');
OLED_ShowChar(0,4,'-');
}
OLED_ShowNum(6,0,True_Angle,5,12);
OLED_ShowString(0,2,strff);
OLED_ShowNum(6,4,Current_Angle,5,12);
OLED_ShowString(0,6,strff2);
Get_Num_sign();
switch(sign)
{
case 1:
if(x==1)
{
setDir();
x++;
}
else if(x==2)
{
TIM_Cmd(TIM3,ENABLE);
led_On_Off();
x++;
}
else if(x==3)
{ TIM_Cmd(TIM3,DISABLE);
led_On_Off();
x=1;}
sign = 0;
break;
case 2:
if(pwm>0)
{
pwm-=100;
}else
{
pwm=7199;
}
sign = 0;
TIM_SetAutoreload(TIM3,pwm);
break;
case 3:
if(pwm<15000)
{
pwm+=100;
}else
{
pwm=7199;
}
sign = 0;
TIM_SetAutoreload(TIM3,pwm);
break;
case 4:
Pulse_output(1036);
sign = 0;
break;
default:
break;
}
if(TIM2_Pulse_TIM3_Counter_OK==1)
{
TIM2_Pulse_TIM3_Counter_OK=0;
}
}
}
注意switch后面的语句是用来按钮控制步进电机运动的,和as5600本身无关。
五、实物演示
图中一个是基于STM32F103C8T6的控制器,一个是TB6600步进电机驱动板。 实测,代码正常运行,功能都可以实现,连续正转10圈误差在3.6度以内,再反转10圈回零点误差在0.15度以内,数据也基本稳定。 测试数据记录 测试的时候每次给半圈的脉冲,所以一转是两个数据,0->180.21->0.103->180.63->0.239…依次测量得到。
最后,附上程序代码以及AS5600的berger制板文件的下载地址 链接:https://pan.baidu.com/s/1zxl2O3oEvmDIDNtbDGoTPg 提取码:m5k5 演示效果观看地址: https://www.bilibili.com/video/BV1hP411N7PJ/?vd_source=81fb9332eb85b94d92d4e3884ff48c6a
|