本实验使用到硬件资源如下:
(1)D1 指示灯
(2)K_UP 按键
(3)串口 1
(4)TFTLCD 模块
(5)ADXL345
D1指示灯、K_UP 按键、串口 1、TFTLCD 模块电路在前面章节都介绍过,这里就不多说,下面我们介绍下 ADXL345 与 STM32F1 的连接电路,开发板连接图如下:
从电路图中可以看到,ADXL345 芯片与 STM32F1 芯片的连接只有 3 根线,其中两根是 IIC 总线,AT24C02 芯片也使用了这两根线,因此不能同时使用,但可以分时复用。ADXL345 的中断输出线(3D_INT)连接在 PA8 引脚上,另外我们将芯片的 ADDR地址线 接在 3.3V上,所以 ADXL345 的器件地址是:0X1D(不包含最低位),因此写入为 0X3A,读取为 0X3B。
D1指示灯用来提示系统运行状态,K_UP 按键用来自动校准,TFTLCD模块用来显示传感器检测的三个方向加速度值及角度值。
所要实现的功能是:使用 STM32F1 来驱动 ADXL345,读取传感器三个方
向的加速度值和转换后的角度值,通过 TFTLCD 模块显示,同时可通过 K_UP键进行校准。程序框架如下:
(1)初始化 ADXL345
(2)读取 ADXL345三个方向的加速度值
(3)转换与自然系坐标的角度值
(4)编写主函数
这些步骤前面我们都已介绍。下面我们打开“ADXL345 传感器实验” 工程, 在 APP 工程组中添加adxl345.c 文件 (里面包含了 ADXL345 的驱动程序),在 StdPeriph_Driver 工程组中并未添加新的库文件,仍然采用的是一个工程的模板。记住添加原文件时,还要包含对应的头文件路径。
这里我们分析几个重要函数。
ADXL345 初始化函数
要使用 ADXL345 首先就要对他进行初始化, 初始化步骤在前面 ADXL345的初始化步骤已介绍,具体代码如下:
//初始化 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); //数据输出速度为100HzADXL345_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 三个方向的加速度值函数
ADXL345 初始化成功后接下来就可以读取传感器加速度数据。具体代码如
下:
//读取 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 次{ADXL345_RD_XYZ(&tx,&ty,&tz);*x+=tx;*y+=ty;*z+=tz;delay_ms(5);}*x/=times;*y/=times;*z/=times;}}//读取 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); //发送寄存器地址(数据缓存的起始地址为 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);//读取一个字节,不继续再读,发送 NACKelse 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]);}
ADXL345_Read_Average 函数用于读取 X、Y、Z 轴的加速度值,函数的入口还有一个参数 times,用于设置读取的次数,以提高数据的准确性。
获取自然坐标系角度函数
前面我们已经读取了ADXL345 传感器 3 个方向的加速度值, 如果要转换成与自然坐标系的角度还需要进行处理,其角度转换计算公式如下:
其中 Ax, Ay, Az 分别代表从 ADXL345 读到的 X, Y, Z 方向的加速度值。通过该函数,我们只需要知道三个方向的加速度值,就可以将其转换为对应的弧度值, 再通过弧度角度转换, 就可以得到角度值了。这里我们看下主要代码,如下:
//得到角度//x,y,z:x,y,z 方向的重力加速度分量(不需要单位,直接数值即可)//dir:要获得的角度.0,与Z 轴的角度;1,与 X 轴的角度;2,与Y轴的角度.//返回值:角度值.单位0.1°.//res 得到的是弧度值,需要将其转换为角度值也就是*180/3.14short 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;}
自动校准函数
具体代码如下:
//自动校准//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); //数据输出速度为100HzADXL345_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);}
该函数用于 ADXL345 的校准, ADXL345 有偏移校准的功能,该功能的详细介绍请参考 ADXL345 数据手册的第 29 页偏移校准部分。这里我们就不细说了,如果不进行校准的话, ADXL345 的读数可能会有些偏差,通过校准,我们可以讲这个偏差减少甚至消除。
主函数
编写好 ADXL345 初始化、读取三个方向的加速度值函数、角度转换函数和自动校准函数后,接下来就可以编写主函数了,代码如下:
//x,y:开始显示的坐标位置//num:要显示的数据//mode:0,显示加速度值;1,显示角度值;void ADXL_Show_num(u16 x,u16 y,short num,u8 mode) //ADXL345显示{u8 valbuf[3];if(mode==0) //显示加速度值{if(num<0){num=-num;LCD_ShowString(x,y,tftlcd_data.width,tftlcd_data.height,16,"-");}else{LCD_ShowString(x,y,tftlcd_data.width,tftlcd_data.height,16," ");}valbuf[0]=num/100+0x30;valbuf[1]=num%100/10+0x30;valbuf[2]=num%100%10+0x30;LCD_ShowString(x+10,y,tftlcd_data.width,tftlcd_data.height,16,valbuf);}else //显示角度值{if(num<0){num=-num;LCD_ShowString(x,y,tftlcd_data.width,tftlcd_data.height,16,"-");}else{LCD_ShowString(x,y,tftlcd_data.width,tftlcd_data.height,16," ");}valbuf[0]=num/10+0x30;valbuf[1]='.';valbuf[2]=num%10+0x30;LCD_ShowString(x+10,y,tftlcd_data.width,tftlcd_data.height,16,valbuf);}}void data_pros() //数据处理函数{short x,y,z;short xang,yang,zang;u8 key;ADXL345_Read_Average(&x,&y,&z,10); //读取 x,y,z 3 个方向的加速度值 总共 10 次ADXL_Show_num(60,120,x,0);ADXL_Show_num(60,140,y,0);ADXL_Show_num(60,160,z,0);xang=ADXL345_Get_Angle(x,y,z,1);yang=ADXL345_Get_Angle(x,y,z,2);zang=ADXL345_Get_Angle(x,y,z,0);ADXL_Show_num(60,180,xang,1);ADXL_Show_num(60,200,yang,1);ADXL_Show_num(60,220,zang,1);key=KEY_Scan(0);if(key==KEY_UP) //按下 K_UP 校准{led2=0; //LED2 亮表示正在校准ADXL345_AUTO_Adjust((char*)&x,(char*)&y,(char*)&z);led2=1; //LED2 灭表示校准完成}}int main(){u8 i=0;SysTick_Init(72);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2 组LED_Init();USART1_Init(9600);TFTLCD_Init(); //LCD 初始化KEY_Init();FRONT_COLOR=BLACK;LCD_ShowString(10,10,tftlcd_data.width,tftlcd_data.height,16,"PRECHIN STM32F1");LCD_ShowString(10,30,tftlcd_data.width,tftlcd_data.height,16,"www.prechin.net");LCD_ShowString(10,50,tftlcd_data.width,tftlcd_data.height,16,"ADXL345 Test");LCD_ShowString(10,90,tftlcd_data.width,tftlcd_data.height,16,"K_UP:ADXL345 Adjust");LCD_ShowString(10,120,tftlcd_data.width,tftlcd_data.height,16,"X Val:");LCD_ShowString(10,140,tftlcd_data.width,tftlcd_data.height,16,"Y Val:");LCD_ShowString(10,160,tftlcd_data.width,tftlcd_data.height,16,"Z Val:");LCD_ShowString(10,180,tftlcd_data.width,tftlcd_data.height,16,"X Ang:");LCD_ShowString(10,200,tftlcd_data.width,tftlcd_data.height,16,"Y Ang:");LCD_ShowString(10,220,tftlcd_data.width,tftlcd_data.height,16,"Z Ang:");FRONT_COLOR=RED;while(ADXL345_Init()){printf("ADXL345 Error!\r\n");LCD_ShowString(130,50,tftlcd_data.width,tftlcd_data.height,16,"Error ");delay_ms(200);}printf("ADXL345 OK!\r\n");LCD_ShowString(130,50,tftlcd_data.width,tftlcd_data.height,16,"Success");while(1){i++;if(i%20==0){led1=!led1;data_pros();}delay_ms(10);}}
主函数实现的功能很简单,首先调用之前编写好的硬件初始化函数,包括
SysTick 系统时钟,中断分组,LED 初始化等。然后调用我们前面编写的
ADXL345_Init函数,并且不断循环检测初始化是否成功,如果失败,TFTLCD 会显示“ADXL345 Error!”,如果成功就会接着往下执行,TFTLCD显示"ADXL345 OK!"。
然后进入 while 循环,调用 data_pros 函数读取加速度值和转换后的角度,通过K_UP 键可以进行自动校准,读取的加速度值和角度值显示在 TFTLCD 上。并且D1指示灯不断闪烁,提示系统正常运行。
实验现象
将工程程序编译后下载到开发板内,可以看到 D1 指示灯不断闪烁,表示程序正常运行。同时在 TFTLCD 模块上可以看到读取的三个轴的加速度值及角度值。
实验说明:屏幕显示了 ADXL345 传感器检测的三个方向的加速度值和角度值。我们可以晃动开发板,看看各角度的变化。如果需要自动校准,可以通过K_UP完成。