ADXL345-数字加速度计

790 阅读5分钟

一、方向判断

注意看管脚1,x,y,z三轴如图所示,当重力加速度方向与坐标轴方向相反时为正,重力加速度方向与坐标轴相同时为负。

二、驱动

采用了IIC接口,实际上也支持SPI接口。

ADXL345.h:

#ifndef     _ADXL345_H
#define     _ADXL345_H
typedef     unsigned   char   u8;
//0x0B To 0x1F  Factory Reserved
#define    DEVICE_ID        0x00    //器件ID,0xE5
#define    THRESH_TAP       0x1D    //敲击阈值
#define    OFSX             0x1E    //X轴偏移
#define    OFSY             0x1F    //Y轴偏移
#define    OFSZ             0x20    //Z轴偏移
#define    DUR              0x21    //敲击持续时间
#define    Latent           0x22    //敲击延时
#define    Window           0x23    //敲击窗口
#define    THRESH_ACT       0x24    //活动阈值
#define    THRESH_INACT     0x25    //静止阈值
#define    TIME_INACT       0x26    //静止时间
#define    ACT_INACT_CTL    0x27    //轴使能控制活动和静止检测
#define    THRESH_FF        0x28    //自由落体阈值
#define    TIME_FF          0x29    //自由落体时间
#define    TAP_AXES         0x2A    //单击/双击轴控制
#define    ACT_TAP_STATUS   0x2B    //单击/双击源
#define    BW_RATE          0x2C    //数据速率及功率模式控制
#define    POWER_CTL        0x2D    //省电特性控制
#define    INT_ENABLE       0x2E    //中断使能控制
#define    INT_MAP          0x2F    //中断映射控制
#define    INT_SOURCE       0x30    //中断源
#define    DATA_FORMAT      0x31    //数据格式控制
#define    DATAX0           0x32    //X轴数据0
#define    DATAX1           0x33    //X轴数据1
#define    DATAY0           0x34    //Y轴数据0
#define    DATAY1           0x35    //Y轴数据1
#define    DATAZ0           0x36    //Z轴数据0
#define    DATAZ1           0x37    //Z轴数据1
#define    FIFO_CTL         0x38    //FIFO控制
#define    FIFO_STATUS      0x39    //FIFO状态

//如果ALT ADDRESS脚(12脚)接地,ADXL 7位地址为0x53,第8位为R/W,0xA7为读,0xA6为写
//如果ALT ADDRESS脚(12脚)接3.3V,ADXL 7位地址为0x1D,第8位为R/W,0x3B为读,0x3A为写
#define     ADXL_READ       0x3B
#define     ADXL_WRITE      0x3A

u8    ADXL345_Init(void);//初始化ADXL345
void  ADXL345_WR_Reg(u8 addr, u8 val);//写ADXL345寄存器
u8    ADXL345_RD_Reg(u8 addr);        //读ADXL345寄存器
void  ADXL345_RD_XYZ(short *x, short *y, short *z);//读取一个值
void  ADXL345_RD_Avval(short *x, short *y, short *z);//读取平均值
void  ADXL345_AUTO_Adjust(char *xval,char *yval, char *zval);//自动校准
void  ADXL345_Read_Average(short *x, short *y, short *z, u8 times);//连续读取times次,取平均
void  ADXL345_Get_Angle(float x, float y, float z, u8 dir);

#endif

ADXL345.c:

#include    "ADXL345.h"
#include    "math.h"

//初始化ADXL345,返回值:0-初始化成功, 1-初始化失败
u8  ADXL345_Init(void)
{
    IIC_Init();
    if(ADXL345_RD_Reg(DEVICE_ID)==0xE5)//固定为0xE5
    {
        ADXL345_WR_Reg(DATA_FORMAT,0x2B);//低电平中断输出,13位全分辨率,输出数据右对齐,±16g量程
        ADXL345_WR_Reg(BW_RATE,0x0A);//数据输出速度为100Hz,正常操作(非低功耗)
        ADXL345_WR_Reg(POWER_CTL,0x28);//链接使能,测量模式
        ADXL345_WR_Reg(INT_ENABLE,0x00);//不使用中断
        //X轴,Y轴,Z轴偏移设置为0
        ADXL345_WR_Reg(OFSX,0x00);
        ADXL345_WR_Reg(OFSY,0x00);
        ADXL345_WR_Reg(OFSZ,0x00);
        return  0;
    }
    return  1;
}

//写ADXL345寄存器,addr寄存器地址,val:要写入的值,返回值:读到的值
void    ADXL345_WR_Reg(u8   addr,  u8  val)
{
    IIC_Start();
    IIC_Send_Byte(ADXL_WRITE);//发送写器件指令
    IIC_Wait_Ack();
    IIC_Send_Byte(addr);//发送寄存器地址
    IIC_Wait_Ack();
    IIC_Send_Byte(val);//发送值
    IIC_Wait_Ack();
    IIC_Stop();//产生一个停止条件
}

//读ADXL345寄存器,addr:寄存器地址,返回值:读到的值
u8  ADXL345_RD_Reg(u8   addr)
{
    u8  temp=0;
    IIC_Start();
    IIC_Send_Byte(ADXL_WRITE);//发送写器件指令
    temp=IIC_Wait_Ack();
    IIC_Send_Byte(addr);//发送寄存器地址
    temp=IIC_Wait_Ack();
    IIC_Start();
    IIC_Send_Byte(ADXL_READ);//发送读器件指令
    temp=IIC_Wait_Ack();
    temp=IIC_Read_Byte(0);//读取一个字节,不继续再读,发送NAK
    IIC_Stop();           //产生一个停止条件
    return      temp;     //返回读到的值
}

//读取3个轴的数据,x,y,z:读取到的数据
void    ADXL345_RD_XYZ(short *x, short *y, short *z)
{
    u8  buf[6];
    u8  i;
    IIC_Start();
    IIC_Send_Byte(ADXL_WRITE);//发送写器件指令
    IIC_Wait_Ack();
    IIC_Send_Byte(0x32);//发送寄存器地址
    IIC_Wait_Ack();

    IIC_Start();//重新启动
    IIC_Send_Byte(ADXL_READ);//发送读器件指令
    IIC_Wait_Ack();

    for(i=0;i<6;i++)
    {
        if(i==5)    buf[i]=IIC_Read_Byte(0);//读取一个字节,不继续再读,发送NACK
        else        buf[i]=IIC_Read_Byte(1);//读取一个字节,继续读,发送ACK
    }

    IIC_Stop();//产生一个停止条件

    *x=(short)(((u16)buf[1]<<8)+buf[0]);
    *y=(short)(((u16)buf[3]<<8)+buf[2]);
    *z=(short)(((u16)buf[5]<<8)+buf[4]);
}

//读取ADXL的平均值,x,y,z:读取10次后取平均值
void    ADXL345_RD_Avval(short *x, short *y, short *z)
{
    short   tx=0,   ty=0,   tz=0;
    u8      i;
    for(i=0;i<10;i++)
    {
        ADXL345_RD_XYZ(x,y,z);
        delay_ms(10);
        tx+=(short)*x;
        ty+=(short)*y;
        tz+=(short)*z;
    }
    *x=tx/10;
    *y=ty/10;
    *z=tz/10;
}

//自动校准,xval,yval,zval:x,y,z轴的校准值
void    ADXL345_AUTO_Adjust(char* xval, char* yval, char* zval)
{
    short   tx,ty,tz;
    u8      i;
    short   offx=0, offy=0, offz=0;
    ADXL345_WR_Reg(POWER_CTL,0x00);//先进入休眠模式
    delay_ms(100);
    ADXL345_WR_Reg(DATA_FORMAT,0x2B);//低电平中断输出,13位全分辨率,输出数据右对齐,±16g量程
    ADXL345_WR_Reg(BW_RATE,0x0A);//数据输出速度为100Hz,正常操作(非低功耗)
    ADXL345_WR_Reg(POWER_CTL,0x28);//链接使能,测量模式
    ADXL345_WR_Reg(INT_ENABLE,0x00);//不使用中断

    //X轴,Y轴,Z轴偏移设置为0
    ADXL345_WR_Reg(OFSX,0x00);
    ADXL345_WR_Reg(OFSY,0x00);
    ADXL345_WR_Reg(OFSZ,0x00); 
    delay_ms(12);
    for(i=0;i<10;i++)
    {
        ADXL345_RD_Avval(&tx,&ty,&tz);
        offx+=tx;
        offy+=ty;
        offz+=tz;
    }
    offx/=10;
    offy/=10;
    offz/=10;
    //灵敏度为256LSB/g,OFSX,OFSY,OFSZ的寄存器比例因子为15.6mg/LSB  
    //15.6/(1000/256)=3.9936,近似为4,又因为校准置于z轴1g的重力场下,Zog=Z+1g-Sz  Sz为灵敏度256
    *xval=-offx/4;
    *yval=-offy/4;
    *zval=-(offz-256)/4;
    ADXL345_WR_Reg(OFSX,*xval);
    ADXL345_WR_Reg(OFSY,*yval);
    ADXL345_WR_Reg(OFSZ,*zval);    
}

//读取ADXL345的数据times次,再取平均,x,y,z:读到的数据,times读取多少次
void    ADXL345_Read_Average(short  *x, short *y, short *z, u8 times)
{
    u8  i;
    short   tx,ty,tz;
    *x=0;
    *y=0;
    *z=0;
    if(times)//读取次数不为0
    {
        for(i=0;i<times;i++)
        {
            ADXL345_RD_XYZ(&tx,&ty,&tz);
            *x+=tx;
            *y+=ty;
            *z+=tz;
            delay_ms(5);
        }
        *x/=times;
        *y/=times;
        *z/=times;
    }
}

//得到角度,x,y,z:x,y,z方向的重力加速度分量(不需要单位,直接数值即可)
//dir:要获得的角度,0-与Z轴的角度;1-与X轴的角度;2-与Y轴的角度
//返回值:角度值,单位0.1°,与自然坐标系轴的角度
//res得到的是弧度值,需要将其转换成角度值也就是*180/3.14
short   ADXL345_Get_Angle(float x, float y, float z, u8 dir)
{
    float   temp;
    float   res=0;
    switch(dir)
    {
        case    0://与自然Z轴的角度
                temp=sqrt((x*x+y*y))/z;
                res=atan(temp);//反正切
                break;
        case    1://与自然X轴的角度
                temp=x/sqrt((y*y+z*z));
                res=atan(temp);
                break;
        case    2://与自然Y轴的角度
                temp=y/sqrt((x*x+z*z));
                res=atan(temp);
                break;
    }
    return      res*180/3.14;
}