目录
一、介绍
ADXL345是一款由Analog Devices(ADI)公司生产的三轴数字加速度传感器。它常用于消费电子、工业自动化、运动检测、游戏设备和医疗仪器等应用领域。可以同时测量X、Y和Z三个轴的加速度和倾斜角度,支持自由空间运动和倾斜角度检测,支持I²C和SPI数字接口,方便与微控制器和其他数字设备进行通信。
以下是ADXL345加速度传感器的参数:
供电电压 | DC:3~5V |
使用芯片 | ADXL345 |
通信方式 | IIC/SPI通信协议 |
测量范围 | ±16g |
分辨率 | 13位 |
哔哩哔哩视频链接:
ADXL345加速度传感器(IIC协议 STM32)
(资料分享见文末)
二、传感器原理
1.原理图
2.引脚描述
引脚名称 | 描述 |
VCC | 供给电压5V |
GND | 地线 |
SCL | 时钟线 |
SDA | 数据线 |
INT1 | 中断1输出 |
INT2 | 中断2输出 |
3.工作原理:常用寄存器
下面介绍一些该传感器的控制指令常用寄存器:
4.工作原理:角度计算
加速度传感器Z轴与自然坐标系Z轴的夹角:
加速度传感器X轴与自然坐标系X轴的夹角:
加速度传感器Y轴与自然坐标系Y轴的夹角:
三、程序设计
1.使用STM32F103C8T6读取ADXL345加速度传感器采集的数据,通过串口发送至电脑
2.将读取得到的温湿度数据同时在OLED上显示
ADXL345_SCL | PB7 |
ADXL345_SDA | PB6 |
OLED_SCL | PB11 |
OLED_SDA | PB10 |
串口 | 串口1 |
main.c文件
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "OLED.h"
#include "string.h"
#include "adxl345.h"
/*****************辰哥单片机设计******************
STM32
* 项目 : ADXL345加速度传感器实验
* 版本 : V1.0
* 日期 : 2024.8.15
* MCU : STM32F103C8T6
* 接口 : 见adxl345.h
* BILIBILI : 辰哥单片机设计
* CSDN : 辰哥单片机设计
* 作者 : 辰哥
**********************BEGIN***********************/
float x_angle,y_angle,z_angle;
int main(void)
{
unsigned char p[16]=" ";
short temperature = 0; //温度值
delay_init(72);
LED_Init(); //初始化与控制设备连接的硬件接口
OLED_Init(); //OLED初始化
delay_ms(50);
OLED_Clear(); //清屏
//显示“角度:”
OLED_ShowChinese(0,0,0,16,1);
OLED_ShowChinese(16,0,1,16,1);
OLED_ShowChar(40,0,':',16,1);
OLED_ShowChar(25,20,'X',16,1);
OLED_ShowChar(62,20,'Y',16,1);
OLED_ShowChar(100,20,'Z',16,1);
ADXL345_Init();
delay_ms(1000);
USART1_Config();//串口初始化
while(1)
{
get_angle(&x_angle,&y_angle,&z_angle);
printf(" %2.1f %2.1f %2.1f \n ",x_angle,y_angle,z_angle);
sprintf((char*)p,"%4.1f ",(float)x_angle);
OLED_ShowString(0,40,p ,16,1);
sprintf((char*)p,"%4.1f ",(float)y_angle);
OLED_ShowString(40,40,p ,16,1);
sprintf((char*)p,"%4.1f ",(float)z_angle);
OLED_ShowString(90,40,p ,16,1);
delay_ms(100);
}
}
adxl345.h文件
#ifndef __ADXL345_H
#define __ADXL345_H
#include "sys.h"
/*****************辰哥单片机设计******************
STM32
* 文件 : ADXL345传感器h文件
* 版本 : V1.0
* 日期 : 2024.8.15
* MCU : STM32F103C8T6
* 接口 : 见代码
* BILIBILI : 辰哥单片机设计
* CSDN : 辰哥单片机设计
* 作者 : 辰哥
**********************BEGIN***********************/
/***************根据自己需求更改****************/
//ADXL345引脚宏定义
#define ADXL345_SCL_PORT GPIOB
#define ADXL345_SCL_PIN GPIO_Pin_7
#define ADXL345_SCL_GPIO_CLK RCC_APB2Periph_GPIOB
#define ADXL345_SDA_PORT GPIOB
#define ADXL345_SDA_PIN GPIO_Pin_6
#define ADXL345_SDA_GPIO_CLK RCC_APB2Periph_GPIOB
#define ADXL345_IIC_SCL PBout(7) //SCL
#define ADXL345_IIC_SDA PBout(6) //SDA
#define ADXL345_READ_SDA PBin(6) //输入SDA
/*********************END**********************/
#define X_AXLE 0 //x轴
#define Y_AXLE 1 //y轴
#define Z_AXLE 2 //z轴
#define slaveaddress 0xA6 //定义器件在IIC总线中的从地址,根据ALT ADDRESS地址引脚不同修改
// ALT ADDRESS引脚接地时地址为0xA6,接电源时地址为0x3A
#define THRESH_TAP 0X1D //敲击中断阈值(用于正常敲击检测)
// 16g模式中,62.5mg/Bit
#define OFSX 0X1E //X轴偏移寄存器 15.6mg/Bit 0xff = 4g
#define OFSY 0X1F //Y轴偏移寄存器 15.6mg/Bit 0xff = 4g
#define OFSZ 0X20 //Z轴偏移寄存器 15.6mg/Bit 0xff = 4g
#define DUR 0x21 //敲击阈值时间 625uS/Bit
#define LATENT 0X22 //敲击事件到时间窗口的延迟时间,在此期间
// 可检测第二次敲击时间 1.25mg/Bit
#define WINDOW 0X23 //敲击窗口,延迟时间满后的时间量,在此期间
// 能开始进行第二次有效敲击 1.25mg/Bit
#define THRESH_ACT 0X24 //检测活动的阈值,活动事件的幅度与该寄存器
// 的值进行比较 62.5mg/Bit
#define THRESH_INACT 0X25 //检测静止的阈值,静止事件的幅度与该寄存器
// 的值进行比较 62.5mg/Bit
#define TIME_INACT 0X26 //加速度时间量小于该寄存器的值表示静止 1S/Bit
#define ACT_INACT_CTL 0X27
//Bit 7,3 ACT交流/直流[INACT交流/直流]:0选择直流耦合;1使能交流耦合 直流时将
// 当前加速度值直接与THRESH_ACT和THRESH_INACT
// 进行比较,确定检测到的是活动还是静止
// 6,2 ACT_X使能[INACT_X使能]:设置为1,使能X轴参与检测活动或静止;活动检测时,
// 所有轴为逻辑"或",有任意轴超过阈值时,活动功能触发
// 禁止检测时,所有轴为逻辑"与",只有当所有轴低于阈值
// 时,静止功能触发
// 5,1 ACT_Y使能[INACT_Y使能]:与X轴类似
// 4,0 ACT_Z使能[INACT_Z使能]:与X轴类似
#define THRESH_FF 0X28 //阈值,用于自由落体检测,所有轴加速度与该寄存器值比较
// ,以确定是否发生自由落体 62.5mg/Bit,建议300mg与600mg之间
#define TIME_FF 0X29 //维持THRESH_FF阈值的最小时间,以生成自由落体中断 5mS/Bit
#define TAP_AXES 0X2A
// Bit 7:4 0
// 3 抑制 两次敲击之间出现大于THRESH_TAP值得加速度,设置抑制会抑制双击检测
// 2 TAP_X使能 设置为1时,使能X轴进行敲击检测,0时排除该轴的敲击检测
// 1 TAP_Y使能 设置为1时,使能Y轴进行敲击检测,0时排除该轴的敲击检测
// 0 TAP_Z使能 设置为1时,使能Z轴进行敲击检测,0时排除该轴的敲击检测
#define ACT_TAP_STATUS 0X2B /*只读寄存器*/
// Bit 7 0
// 6,2 ACT_X来源,TAP_X来源:表示涉及敲击或活动事件的第一轴,设置为1时,对应事件参与
// 设置为0时,对应未参与.不会自动清零,新数据覆盖,中断清零前
// 应读取该寄存器
// 5,1 ACT_Y来源,TAP_Y来源:与X相似
// 4,0 ACT_Z来源,TAP_Z来源:与X相似
// 3 休眠设置为1时,器件进入休眠状态
#define BW_RATE 0X2C
// Bit 7:5 0
// 4 LOW_POWER 低功耗位,0选择正常模式,1进入低功耗模式
// 3:0 速率位
#define POWER_CTL 0X2D
// Bit 7,6 0
// 5 链接 设置1时,延迟活动开始,直到检测到静止.检测到活动后,禁止检测开始,活动
// 检测停止,设置时动态链接活动和静止交替检测;设置0时静止与活动同时检测
// 4 AUTO_SLEEP 设置1时自动休眠,检测出静止后,进行休眠模式,活动使能后被唤醒
// 3 测量 0待机 1测量模式
// 2 休眠 0普通 1休眠
// 1,0 唤醒(休眠模式下的读取频率) "00":8HZ "01":4HZ "10":2HZ "11":1HZ
#define INT_ENABLE 0X2E //中断使能配置
// Bit 7 DATA_READY
// 6 SINGLE_TAP
// 5 DOUBLE_TAP
// 4 Activity
// 3 Inactivity
// 2 FREE_FALL 自由落体中断
// 1 Watermark
// 0 Overrun
#define INT_MAP 0X2F //中断映射 自读寄存器
//位与INT_ENABLE对应,,设置为0,该中断映射到INT1引脚;设置为1,该中断映射到INT2引脚
#define INT_SOURCE 0X30 //中断来源
//位与INT_ENABLE对应,1表示该功能触发
#define DATA_FORMAT 0X31
// Bit 7 SELF_TEST 设置1,自测力应用至传感器,造成输出数据转换;0时禁用自测力
// 6 SPI 1设置为3线SPI模式,0时设置4线SPI模式
// 5 INT_INVERT 0时中断高电平有效,1时低电平有效
// 4 0
// 3 FULL_RES 1时设置全分辨率模式,输出以4mg/Bit增加;0时为10位模式
// 2 Justify 1为左对齐模式;8为右对齐模式,并带有符号扩展
// 1:0 范围位 "00"±2g "01"±4g "10"±8g "11"±16g
#define DATAX0 0X32
#define DATAX1 0X33 //与DATAX0组成x轴输出数据(二进制补码),DATAX1为高位,4mg/Bit
#define DATAY0 0X34
#define DATAY1 0X35 //与DATAY0组成Y轴输出数据(二进制补码),DATAY1为高位,4mg/Bit
#define DATAZ0 0X36
#define DATAZ1 0X37 //与DATAZ0组成Z轴输出数据(二进制补码),DATAZ1为高位,4mg/Bit
#define FIFO_CTL 0X38
// Bit 7,6 FIFO_MODE "00" 旁路模式
// "01" FIFO模式 可收集最多32个值,然后停止收集数据
// "10" 流模式 FIFO保存最后32个数据值,FIFO满时,新数据覆盖最早数据(先进先出)
// "11" 触发器 通过触发位触发,FIFO在触发事件前保存最后的数据样本,然后
// 继续收集数据直到填满;填满后,不再收集新数据
// 5 触发位 0链接触发器模式下的触发事件至INT1,1链接至INT2
// 4:0 样本 功能取决于FIFO模式:FIFO模式时,指定触发水印中断需要的FIFO条目数
// 流模式时,指定触发水印中断需要的FIFO条目数
// 触发器模式:指定触发事件之前在FIFO缓冲区要保留的FIFO样本数
// 样本位设置为0时,不管哪种FIFO模式,立即在INT_SOURCE寄存器设置水印状态位
#define FIFO_STATUS 0X39 /*只读寄存器*/
// Bit 7 FIFO_TRIG FIFO_TRIG为1时表示有触发事件发生
// 6 0
// 5:0 条目位 报告FIFO存储的数据值的数量
//#define slaveaddress 0XA6//write
#define regaddress 0XA7//read
#define DEVICE_ID 0X00
void ADXL345_IIC_SDA_Mode(u8 mode);//ADXL345引脚输出模式控制
void ADXL345_IIC_SDA_OUT(void);
void ADXL345_IIC_SDA_IN(void);
void ADXL345_IIC_Init(void);
void ADXL345_IIC_Start(void);
void ADXL345_IIC_Stop(void);
void ADXL345_IIC_Ack(void);
void ADXL345_IIC_NAck(void);
u8 ADXL345_IIC_Wait_Ack(void);
void ADXL345_IIC_Send_Byte(u8 txd);
u8 ADXL345_IIC_Read_Byte(u8 ack);
void ADXL345_Init(void);
u8 adxl345_read_reg(u8 addr);
void adxl345_write_reg(u8 addr,u8 val);
void adxl345_read_data(short *x,short *y,short *z);
void adxl345_read_average(float *x,float *y,float *z,u8 times);
void ADXL345_Start(void);
void ADXL345_Stop(void);
void get_angle(float *x_angle,float *y_angle,float *z_angle);
#endif
adxl345.c文件
#include "adxl345.h"
#include "delay.h"
#include "math.h"
/*****************辰哥单片机设计******************
STM32
* 文件 : ADXL345加速度传感器c文件
* 版本 : V1.0
* 日期 : 2024.8.15
* MCU : STM32F103C8T6
* 接口 : 见ADXL345.h文件
* BILIBILI : 辰哥单片机设计
* CSDN : 辰哥单片机设计
* 作者 : 辰哥
**********************BEGIN***********************/
//ADXL345引脚输出模式控制
void ADXL345_IIC_SDA_OUT(void)//SDA输出方向配置
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=ADXL345_SDA_PIN;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//SDA推挽输出
GPIO_Init(ADXL345_SDA_PORT,&GPIO_InitStructure);
}
void ADXL345_IIC_SDA_IN(void)//SDA输入方向配置
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=ADXL345_SDA_PIN;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//SCL上拉输入
GPIO_Init(ADXL345_SDA_PORT,&GPIO_InitStructure);
}
//以下为模拟IIC总线函数
void ADXL345_IIC_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(ADXL345_SCL_GPIO_CLK, ENABLE); //使能SCL端口时钟
GPIO_InitStructure.GPIO_Pin = ADXL345_SCL_PIN; //配置为推挽输出,SCL
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(ADXL345_SCL_PORT, &GPIO_InitStructure);
GPIO_SetBits(ADXL345_SCL_PORT,ADXL345_SCL_PIN);
RCC_APB2PeriphClockCmd(ADXL345_SDA_GPIO_CLK, ENABLE); //使能SDA端口时钟
GPIO_InitStructure.GPIO_Pin = ADXL345_SDA_PIN; //配置为推挽输出,SDA
GPIO_SetBits(ADXL345_SDA_PORT,ADXL345_SDA_PIN);
// I2C_SCL_H;
// I2C_SDA_H;//均拉高
}
void ADXL345_IIC_Start()
{
ADXL345_IIC_SDA_OUT();
ADXL345_IIC_SDA=1;
ADXL345_IIC_SCL=1;
delay_us(5);
ADXL345_IIC_SDA=0;
delay_us(5);
ADXL345_IIC_SCL=0;
}
void ADXL345_IIC_Stop()
{
ADXL345_IIC_SDA_OUT();
ADXL345_IIC_SCL=0;
ADXL345_IIC_SDA=0;
delay_us(5);
ADXL345_IIC_SCL=1;
ADXL345_IIC_SDA=1;
delay_us(5);
}
//主机产生一个应答信号
void ADXL345_IIC_Ack()
{
ADXL345_IIC_SCL=0;
ADXL345_IIC_SDA_OUT();
ADXL345_IIC_SDA=0;
delay_us(2);
ADXL345_IIC_SCL=1;
delay_us(5);
ADXL345_IIC_SCL=0;
}
//主机不产生应答信号
void ADXL345_IIC_NAck()
{
ADXL345_IIC_SCL=0;
ADXL345_IIC_SDA_OUT();
ADXL345_IIC_SDA=1;
delay_us(2);
ADXL345_IIC_SCL=1;
delay_us(2);
ADXL345_IIC_SCL=0;
}
//等待从机应答信号
//返回值:1 接收应答失败
// 0 接收应答成功
u8 ADXL345_IIC_Wait_Ack()
{
u8 tempTime=0;
ADXL345_IIC_SDA_IN();
ADXL345_IIC_SDA=1;
delay_us(1);
ADXL345_IIC_SCL=1;
delay_us(1);
while(ADXL345_READ_SDA)
{
tempTime++;
if(tempTime>250)
{
ADXL345_IIC_Stop();
return 1;
}
}
ADXL345_IIC_SCL=0;
return 0;
}
void ADXL345_IIC_Send_Byte(u8 txd)
{
u8 i=0;
ADXL345_IIC_SDA_OUT();
ADXL345_IIC_SCL=0;;//拉低时钟开始数据传输
for(i=0;i<8;i++)
{
ADXL345_IIC_SDA=(txd&0x80)>>7;//读取字节
txd<<=1;
ADXL345_IIC_SCL=1;
delay_us(2); //发送数据
ADXL345_IIC_SCL=0;
delay_us(2);
}
}
//读取一个字节
u8 ADXL345_IIC_Read_Byte(u8 ack)
{
u8 i=0,receive=0;
ADXL345_IIC_SDA_IN();
for(i=0;i<8;i++)
{
ADXL345_IIC_SCL=0;
delay_us(2);
ADXL345_IIC_SCL=1;
receive<<=1;//左移
if(ADXL345_READ_SDA)
receive++;//连续读取八位
delay_us(1);
}
if(!ack)
ADXL345_IIC_NAck();
else
ADXL345_IIC_Ack();
return receive;//返回读取到的字节
}
//传感器初始化
void ADXL345_Init()
{
ADXL345_IIC_Init();
adxl345_write_reg(0X31,0X0B); //低电平中断输出,13位全分辨率,输出数据右对齐,16g量程
adxl345_write_reg(0x2C,0x0B); //数据输出速度为100Hz
adxl345_write_reg(0x2D,0x08); //链接使能,测量模式,省电特性
adxl345_write_reg(0X2E,0x80); //不使用中断
adxl345_write_reg(0X1E,0x00);
adxl345_write_reg(0X1F,0x00);
adxl345_write_reg(0X20,0x05);
}
//写寄存器函数
void adxl345_write_reg(u8 addr,u8 val)
{
ADXL345_IIC_Start();
ADXL345_IIC_Send_Byte(slaveaddress); //发送写器件指令
ADXL345_IIC_Wait_Ack();
ADXL345_IIC_Send_Byte(addr); //发送寄存器地址
ADXL345_IIC_Wait_Ack();
ADXL345_IIC_Send_Byte(val); //发送值
ADXL345_IIC_Wait_Ack();
ADXL345_IIC_Stop(); //产生一个停止条件
}
//读寄存器函数
u8 adxl345_read_reg(u8 addr)
{
u8 temp=0;
ADXL345_IIC_Start();
ADXL345_IIC_Send_Byte(slaveaddress); //发送写器件指令
temp=ADXL345_IIC_Wait_Ack();
ADXL345_IIC_Send_Byte(addr); //发送寄存器地址
temp=ADXL345_IIC_Wait_Ack();
ADXL345_IIC_Start(); //重新启动
ADXL345_IIC_Send_Byte(regaddress); //发送读器件指令
temp=ADXL345_IIC_Wait_Ack();
temp=ADXL345_IIC_Read_Byte(0); //读取一个字节,不继续再读,发送NAK
ADXL345_IIC_Stop(); //产生一个停止条件
return temp;
}
//读取数据函数
void adxl345_read_data(short *x,short *y,short *z)
{
u8 buf[6];
u8 i;
ADXL345_IIC_Start();
ADXL345_IIC_Send_Byte(slaveaddress); //发送写器件指令
ADXL345_IIC_Wait_Ack();
ADXL345_IIC_Send_Byte(0x32); //发送寄存器地址(数据缓存的起始地址为0X32)
ADXL345_IIC_Wait_Ack();
ADXL345_IIC_Start(); //重新启动
ADXL345_IIC_Send_Byte(regaddress); //发送读器件指令
ADXL345_IIC_Wait_Ack();
for(i=0;i<6;i++)
{
if(i==5)buf[i]=ADXL345_IIC_Read_Byte(0);//读取一个字节,不继续再读,发送NACK
else buf[i]=ADXL345_IIC_Read_Byte(1); //读取一个字节,继续读,发送ACK
}
ADXL345_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]);
}
//连读读取几次取平均值函数
//times 取平均值的次数
void adxl345_read_average(float *x,float *y,float *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_read_data(&tx,&ty,&tz);
*x+=tx;
*y+=ty;
*z+=tz;
delay_ms(5);
}
*x/=times;
*y/=times;
*z/=times;
}
}
void get_angle(float *x_angle,float *y_angle,float *z_angle)
{
float ax,ay,az;
adxl345_read_average(&ax,&ay,&az,10);
*x_angle=atan(ax/sqrt((az*az+ay*ay)))*180/3.14;
*y_angle=atan(ay/sqrt((ax*ax+az*az)))*180/3.14;
*z_angle=atan(sqrt((ax*ax+ay*ay)/az))*180/3.14;
//return x_angle;
}