专家
公告
财富商城
电子网
旗下网站
首页
问题库
专栏
标签库
话题
专家
NEW
门户
发布
提问题
发文章
STM32
【ALIENTEK 战舰STM32开发板例程系列连载+教学】第三十四章 ADXL345三轴加速度传感器实验
2019-07-20 23:07
发布
×
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
站内问答
/
STM32/STM8
7862
49
1241
第三十四章
三轴加速度传感器实验
自从有了
Iphone
,各种新技术的普及程度越来越快,人们喜欢的不再是摔不坏的诺基亚,而是用户体验极佳的
Iphone
。
本章,我们介绍一种当今智能手机普遍具有的传感器:加速度传感器。在手机上,这个功能可以用来:自动切换横竖屏、玩游戏和切歌等。
ALIENTEK
战舰
STM32
开发板自带了加速度传感器:
ADXL345
。本章我们将使用
STM32
来驱动
ADXL345
,读取
3
个方向的重力加速度值,并转换为角度,显示在
TFTLCD
模块上。本章分为如下几个部分:
34.1 ADXL345
简介
34.2
硬件设计
34.3
软件设计
34.4
下载验证
34.1 ADXL345
简介
ADXL345
是
ADI
公司的一款
3
轴、数字输出的加速度传感器。
ADXL345
是
ADI
公司推出的基于
iMEMS
技术的
3
轴、数字输出加速度传感器。该加速度传感器的特点有:
l
分辨率高。最高
13
位分辨率。
l
量程可变。具有
+/-2g
,
+/-4g
,
+/-8g
,
+/-16g
可变的测量范围。
l
灵敏度高。最高达
3.9mg/LSB
,能测量不到
1.0
°的倾斜角度变化。
l
功耗低。
40~145uA
的超低功耗,待机模式只有
0.1uA
。
l
尺寸小。整个
IC
尺寸只有
3mm*5mm*1mm
,
LGA
封装。
ADXL
支持标准的
I2C
或
SPI
数字接口,自带
32
级
FIFO
存储,并且内部有多种运动状态检测和灵活的中断方式等特性。
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
支持
SPI
和
IIC
两种通信方式,为了节省
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
±
16g
,
13
位模式
2
0X2D
POWER_CTL
0X08
测量模式
3
0X2E
INT_ENABLE
0X80
使能
DATA_READY
中断
表
34.1.1 ADXL345
最简单的初始化命令序列
发送以上序列给
ADXL345
以后,
ADXL345
即开始正常工作。
ADXL345
我们就介绍到这里,详细的介绍,请参考
ADXL345
的数据手册。
34.2
硬件设计
本实验采用
STM32
的
3
个普通
IO
连接
ADXL345
,
本章实验功能简介:主函数不停的查询
ADXL345
的转换结果,得到
x
、
y
和
z
三个方向的加速度值(读数值),然后将其转换为与自然系坐标的角度,并将结果在
LCD
模块上显示出来。
DS0
来指示程序正在运行,通过按下
WK_UP
按键,可以进行
ADXL345
的自动校准(
DS1
用于提示正在校准)。
所要用到的硬件资源如下:
1)
指示灯
DS0
、
DS1
2)
WK_UP
按键
3)
TFTLCD
模块
4)
ADXL345
前
3
个,在之前的实例已经介绍过了,这里我们仅介绍
ADXL345
与战舰
STM32
开发板的连接。该接口与
MCU
的连接原理图如
34.2.1
所示:
图
34.2.1 ADXL345
与
STM32
的连接电路图
从上图可以看出,
ADXL345
通过三根线与
STM32
开发板连接,其中
IIC
总线时和
24C02
以及
RDA5820
共用,接在
PB10
和
PB11
上面。
ADXL345
的两个中断输出,这里我们只用了一个,连接在
STM32
的
PF11
脚,另外这里的地址线是接
3.3V
,所以
ADXL345
的地址是
0X1D
,转换为
0X3A
写入,
0X3B
读取。
34.3
软件设计
打开上一章的工程,首先在
HARDWARE
文件夹下新建一个
ADXL345
的文件夹。然后新建一个
adxl345.c
和
adxl345.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
的读值,转换为与自然坐标系的角度。计算公式如下:
其中
Ax
,
Ay
,
Az
分别代表从
ADXL345
读到的
X
,
Y
,
Z
方向的加速度值。通过该函数,我们只需要知道三个方向的加速度值,就可以将其转换为对应的弧度值,再通过弧度角度转换,就可以得到角度值了。
其他函数,我们就不介绍了,也比较简单。保存
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,
所以转为读写地址后
,
为
0X3B
和
0X3A(
如果接
GND,
则为
0XA7
和
0XA6)
#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条回答
songdaxuan
2019-07-27 13:09
精彩回答 2 元偷偷看……
0人看过
加载中...
查看其它49个回答
一周热门
更多
>
相关问题
STM32F4上I2C(在PROTEUS中模拟)调试不通的问题
6 个回答
芯片供应紧张,准备换个MCU,MM32L系列替换STM32L系列的怎么样?
7 个回答
STM32同时使用两个串口进行数据收发时数据丢包的问题
5 个回答
STM32F103串口通信死机问题
4 个回答
STM32WLE5CC连接SX1268在LoRa模式下能与 SX1278互通吗?
2 个回答
相关文章
ST公司第一款无线低功耗单片机模块有效提高物联网设计生产效率
0个评论
如何实现对单片机寄存器的访问
0个评论
通过USB用STM32片内自带Bootloader下载程序及注意事项
0个评论
欲练此功必先自宫之STM32汇编启动,放慢是为了更好的前行
0个评论
×
关闭
采纳回答
向帮助了您的知道网友说句感谢的话吧!
非常感谢!
确 认
×
关闭
编辑标签
最多设置5个标签!
STM32
保存
关闭
×
关闭
举报内容
检举类型
检举内容
检举用户
检举原因
广告推广
恶意灌水
回答内容与提问无关
抄袭答案
其他
检举说明(必填)
提交
关闭
×
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
×
付费偷看金额在0.1-10元之间
确定
×
关闭
您已邀请
0
人回答
查看邀请
擅长该话题的人
回答过该话题的人
我关注的人
一周热门 更多>