【ALIENTEK 战舰STM32开发板例程系列连载+教学】第三十四章 ADXL345三轴加速度传感器实验

2019-07-20 23:07发布

 

第三十四章 三轴加速度传感器实验

  自从有了Iphone,各种新技术的普及程度越来越快,人们喜欢的不再是摔不坏的诺基亚,而是用户体验极佳的Iphone 本章,我们介绍一种当今智能手机普遍具有的传感器:加速度传感器。在手机上,这个功能可以用来:自动切换横竖屏、玩游戏和切歌等。ALIENTEK战舰STM32开发板自带了加速度传感器:ADXL345。本章我们将使用STM32来驱动ADXL345,读取3个方向的重力加速度值,并转换为角度,显示在TFTLCD模块上。本章分为如下几个部分: 34.1 ADXL345简介 34.2 硬件设计 34.3 软件设计 34.4 下载验证

34.1 ADXL345简介

ADXL345ADI公司的一款3轴、数字输出的加速度传感器。ADXL345ADI公司推出的基于iMEMS技术的3轴、数字输出加速度传感器。该加速度传感器的特点有: l  分辨率高。最高13位分辨率。 l  量程可变。具有+/-2g+/-4g+/-8g+/-16g可变的测量范围。 l  灵敏度高。最高达3.9mg/LSB,能测量不到1.0°的倾斜角度变化。 l  功耗低。40~145uA的超低功耗,待机模式只有0.1uA l  尺寸小。整个IC尺寸只有3mm*5mm*1mmLGA封装。 ADXL支持标准的I2CSPI数字接口,自带32FIFO存储,并且内部有多种运动状态检测和灵活的中断方式等特性。ADXL345传感器的检测轴如图34.1.1所示:
34.1.1 ADXL345的三个检测轴 ADXL345沿检测轴正向加速时,它对正加速度进行检测。在检测重力时用户需要注意,当检测轴的方向与重力的方向相反时检测到的是正加速度。图33.1.2所示为输出对重力的响应。
34.1.2 ADXL345输出对重力的响应        34.1.2列出了ADXL345在不同摆放方式时的输出,以便后续分析。接下来我们看看ADXL345的引脚图,如图34.1.3所示:
34.1.3 ADXL345引脚图        ADXL345支持SPIIIC两种通信方式,为了节省IO口,战舰STM32开发板采用的是IIC方式连接,官方推荐的IIC连接电路如图34.1.4所示:
34.1.4 ADXL345 IIC模式连接电路        从上图可看出,ADXL345的连接十分简单,外围需要的器件也极少(就2个电容),如上连接(SDO/ALT ADDRESS接地),则ADXL345的地址为0X53(不含最低位),如果SDO/ALT ADDRESS接高,那么ADXL345的地址将变为0X1D(不含最低位)。IIC通信的时序我们在之前已经介绍过(第二十七章,IIC实验),这里就不再细说了。        最后,我们介绍一下ADXL345的初始化步骤。ADXL345的初始化步骤如下: 1) 上电 2) 等待1.1ms 3) 初始化命令序列 4) 结束 其中上电这个动作发生在开发板第一次上电的时候,在上电之后,等待1.1ms左右,就可以开始发送初始化序列了,初始化序列一结束,ADXL345就开始正常工作了。这里的初始化序列,最简单的只需要配置3个寄存器,如表34.1.1所示:   步骤 寄存器地址 寄存器名字 寄存器值 功能描述 1 0X31 DATA_FORMAT 0X0B ±16g13位模式 2 0X2D POWER_CTL 0X08 测量模式 3 0X2E INT_ENABLE 0X80 使能DATA_READY中断 34.1.1 ADXL345最简单的初始化命令序列        发送以上序列给ADXL345以后,ADXL345即开始正常工作。        ADXL345我们就介绍到这里,详细的介绍,请参考ADXL345的数据手册。

34.2 硬件设计

本实验采用STM323个普通IO连接ADXL345本章实验功能简介:主函数不停的查询ADXL345的转换结果,得到xyz三个方向的加速度值(读数值),然后将其转换为与自然系坐标的角度,并将结果在LCD模块上显示出来。DS0来指示程序正在运行,通过按下WK_UP按键,可以进行ADXL345的自动校准(DS1用于提示正在校准)。 所要用到的硬件资源如下: 1)  指示灯DS0DS1 2)  WK_UP按键 3) TFTLCD模块 4)  ADXL345     3个,在之前的实例已经介绍过了,这里我们仅介绍ADXL345与战舰STM32开发板的连接。该接口与MCU的连接原理图如34.2.1所示:
                 
34.2.1 ADXL345STM32的连接电路图 从上图可以看出,ADXL345通过三根线与STM32开发板连接,其中IIC总线时和24C02以及RDA5820共用,接在PB10PB11上面。ADXL345的两个中断输出,这里我们只用了一个,连接在STM32PF11脚,另外这里的地址线是接3.3V,所以ADXL345的地址是0X1D,转换为0X3A写入,0X3B读取。   

34.3 软件设计

打开上一章的工程,首先在HARDWARE文件夹下新建一个ADXL345的文件夹。然后新建一个adxl345.cadxl345.h的文件保存在JOYPAD文件夹下,并将这个文件夹加入头文件包含路径。 打开adxl345.c文件,输入如下代码: #include "adxl345.h" #include "sys.h" #include "delay.h" #include "math.h"   //初始化ADXL345. //返回值:0,初始化成功;1,初始化失败. u8 ADXL345_Init(void) {                                   IIC_Init();                                                       //初始化IIC总线         if(ADXL345_RD_Reg(DEVICE_ID)==0XE5)    //读取器件ID        {                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);        //不使用中断                      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;                         //返回读到的值 //读取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);        //不使用中断                                     `             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;        *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);      } //读取3个轴的数据 //x,y,z:读取到的数据 void ADXL345_RD_XYZ(short *x,short *y,short *z) {        u8 buf[6],i;        IIC_Start();                                 IIC_Send_Byte(ADXL_WRITE);  //发送写器件指令         IIC_Wait_Ack();           IIC_Send_Byte(0x32);               //发送寄存器地址(数据缓存的起始地址为0X32)        IIC_Wait_Ack();                  IIC_Start();                           //重新启动        IIC_Send_Byte(ADXL_READ);   //发送读器件指令        IIC_Wait_Ack();        for(i=0;i<6;i++)        {               if(i==5)buf=IIC_Read_Byte(0);       //读取一个字节,不继续再读,发送NACK                else buf=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]);            } //读取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++)//连续读取times               {                      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°. short ADXL345_Get_Angle(float x,float y,float z,u8 dir) {        float temp,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*1800/3.14; } 该部分代码总共有8个函数,这里我们仅介绍其中4个。首先是ADXL345_Init函数,该函数用来初始化ADXL345,和前面我们提到的步骤差不多,不过本章我们而是采用查询的方式来读取数据的,所以在这里并没有开启中断。另外3个偏移寄存器,都默认设置为0 其次,我们介绍ADXL345_RD_XYZ函数,该函数用于从ADXL345读取数据,通过该函数可以读取ADXL345的转换结果,得到三个轴的加速度值(仅是数值,并没有转换单位)。 接着,我们介绍ADXL345_AUTO_Adjust函数,该函数用于ADXL345的校准,ADXL345有偏移校准的功能,该功能的详细介绍请参考ADXL345数据手册的第29页,偏移校准部分。这里我们就不细说了,如果不进行校准的话,ADXL345的读数可能会有些偏差,通过校准,我们可以讲这个偏差减少甚至消除。 最后,我们看看ADXL345_Get_Angle函数,该函数根据ADXL345的读值,转换为与自然坐标系的角度。计算公式如下:
其中AxAyAz分别代表从ADXL345读到的XYZ方向的加速度值。通过该函数,我们只需要知道三个方向的加速度值,就可以将其转换为对应的弧度值,再通过弧度角度转换,就可以得到角度值了。 其他函数,我们就不介绍了,也比较简单。保存adxl345.c,然后把该文件加入HARDWARE组下。接下来打开adxl345.h在该文件里面加入如下代码: #ifndef __ADXL345_H #define __ADXL345_H #include "myiic.h"   #define DEVICE_ID            0X00      //器件ID,0XE5 #define THRESH_TAP         0X1D          //敲击阀值 ……省略部分寄存器定义 #define FIFO_STATUS         0X39   //0X0B TO OX1F Factory Reserved     //如果ALT ADDRESS(12)接地,IIC地址为0X53(不包含最低位). //如果接V3.3,IIC地址为0X1D(不包含最低位). //开发板接V3.3,所以转为读写地址后,0X3B0X3A(如果接GND,则为0XA70XA6)  #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,取平均 short ADXL345_Get_Angle(float x,float y,float z,u8 dir); #endif 上面的代码省略了部分寄存器的定义,其他部分比较简单,我们不作介绍。保存adxl345.h,然后在test.c里面修改代码如下: //x,y:开始显示的坐标位置 //num:要显示的数据 //mode:0,显示加速度值;1,显示角度值; void Adxl_Show_Num(u16 x,u16 y,short num,u8 mode) {        if(mode==0)   //显示加速度值        {               if(num<0)               {                      LCD_ShowChar(x,y,'-',16,0);               //显示负号                      num=-num;                                       //转为正数               }else LCD_ShowChar(x,y,' ',16,0);                     //去掉负号               LCD_ShowNum(x+8,y,num,4,16);                     //显示值                   }else             //显示角度值        {               if(num<0)               {                      LCD_ShowChar(x,y,'-',16,0);               //显示负号                      num=-num;                                       //转为正数               }else LCD_ShowChar(x,y,' ',16,0);                     //去掉负号              LCD_ShowNum(x+8,y,num/10,2,16);         //显示整数部分                              LCD_ShowChar(x+24,y,'.',16,0);                //显示小数点               LCD_ShowNum(x+32,y,num%10,1,16);     //显示小数部分                       } }                                                           int main(void) {                   u8 key;        u8 t=0;           short x,y,z;                short angx,angy,angz;             Stm32_Clock_Init(9);           //系统时钟设置        uart_init(72,9600);             //串口初始化为9600        delay_init(72);                         //延时初始化        LED_Init();                        //初始化与LED连接的硬件接口        LCD_Init();                       //初始化LCD        usmart_dev.init(72);             //初始化USMART              KEY_Init();                         //按键初始化        POINT_COLOR=RED;//设置字体为红 {MOD}        LCD_ShowString(60,50,200,16,16,"WarShip STM32");           LCD_ShowString(60,70,200,16,16,"3D TEST");        LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");        LCD_ShowString(60,110,200,16,16,"2012/9/12");       LCD_ShowString(60,130,200,16,16,"KEY0:Auto Adjust");       while(ADXL345_Init()) //3D加速度传感器初始化           {               LCD_ShowString(60,150,200,16,16,"ADXL345 Error");               delay_ms(200);               LCD_Fill(60,150,239,150+16,WHITE);              delay_ms(200);        }                                                                LCD_ShowString(60,150,200,16,16,"ADXL345 OK");       LCD_ShowString(60,170,200,16,16,"X VAL:");        LCD_ShowString(60,190,200,16,16,"Y VAL:");        LCD_ShowString(60,210,200,16,16,"Z VAL:");       LCD_ShowString(60,230,200,16,16,"X ANG:");        LCD_ShowString(60,250,200,16,16,"Y ANG:");        LCD_ShowString(60,270,200,16,16,"Z ANG:");        POINT_COLOR=BLUE;//
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
49条回答
正点原子
1楼-- · 2019-07-23 15:57
 精彩回答 2  元偷偷看……
sword
2楼-- · 2019-07-23 16:30
原子哥  我想把adxl345移植到k60中去   但是移植以后发现初始化都无法成功  我觉得是硬件连接的问题 vcc+5V  GND  SCL SDA +3.3 VS .CS .SD0 .INT1 INT2  这些脚哪些是要用到的 我是按照你的例程移植的 IIC通讯的 能解释一下吗@正点原子
sword
3楼-- · 2019-07-23 19:33
 精彩回答 2  元偷偷看……
正点原子
4楼-- · 2019-07-23 22:43
 精彩回答 2  元偷偷看……
jidian0177
5楼-- · 2019-07-24 01:31
原子哥,我把你的程序写成自己的模块了,还是战舰的板子,感觉频率快了好多,为什么呢?
正点原子
6楼-- · 2019-07-24 05:18
回复【24楼】jidian0177:
---------------------------------
频率快好多是指?

一周热门 更多>