基于STM32的ADXL345传感器应用(IIC)
·
若该文为原创文章,转载请注明原文出处。
一、介绍
ADXL345是三轴加速度传感器,根据官方网站以及官方手册(https://www.analog.com/cn/products/adxl345.html#product-overview)上的描述,它具有以下特性:
1、超低功耗:VS = 2.5 V时(典型值),测量模式下低至23μA,待机模式下为0.1 μA
2、功耗随带宽自动按比例变化
3、用户可选的分辨率:10位固定分辨率/全分辨率
分辨率随g范围提高而提高,±16 g时达到最高分辨率13位(在所有g范围内保持4 mg/LSB的比例系数)

二、通讯方式
根据官方手册可知,通信方式可参考下表:


说明:CS接入高电平则选择IIC通信,反之则SPI通信。SDO(地址引脚)接入高电平,根据手册器件的7位I2C地址是0x1D,后面跟上读取/写入位(R/W),则写寄存器为0x3A,读寄存器为0x3B;接入低电平,则7位I2C地址是0x53,同理,跟上读写标志位后写寄存器为0xA6,读寄存器为0xA7;
三、模块原理图

四、和STM32连接方式
这里测试使用的是I2C方式

SDA -> PB0
SCL -> PB1
3脚为CS脚,CS接入高电平则选择IIC通信, SDO(地址引脚)接入低电平,读写标志位后写寄存器为0xA6,读寄存器为0xA7
五、STM32驱动使用步骤
1、模拟I2C
#define IIC_SDA_PORT_adxl345 GPIOB
#define IIC_SDA_CLK_adxl345 (RCC_APB2Periph_GPIOB)
#define IIC_SDA_PIN_adxl345 GPIO_Pin_0
#define IIC_SCL_PORT_adxl345 GPIOB
#define IIC_SCL_CLK_adxl345 (RCC_APB2Periph_GPIOB)
#define IIC_SCL_PIN_adxl345 GPIO_Pin_1
#define IIC_SCL_OUT_1_adxl345 IIC_SCL_PORT_adxl345->BSRR = (uint32_t)IIC_SCL_PIN_adxl345 // 置1
#define IIC_SCL_OUT_0_adxl345 IIC_SCL_PORT_adxl345->BRR = (uint32_t)IIC_SCL_PIN_adxl345
#define IIC_SDA_OUT_1_adxl345 IIC_SDA_PORT_adxl345->BSRR = (uint32_t)IIC_SDA_PIN_adxl345 // 置1
#define IIC_SDA_OUT_0_adxl345 IIC_SDA_PORT_adxl345->BRR = (uint32_t)IIC_SDA_PIN_adxl345
#define IIC_SDA_IN_adxl345() ((IIC_SDA_PORT_adxl345->IDR & IIC_SDA_PIN_adxl345) != 0)
/**
* @brief IIC延时
* @param None
* @retval None
*/
static void IIC_Delay_adxl345(void)
{
uint8_t i;
/*
下面的时间是通过逻辑分析仪测试得到的。
工作条件:CPU主频72MHz ,MDK编译环境,1级优化
循环次数为10时,SCL频率 = 205KHz
循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us
循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us
*/
for (i = 0; i < 10; i++);
}
/**
* @brief SDA输出方向配置
* @param None
* @retval None
*/
void Set_IIC_SDA_OUT_adxl345(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(IIC_SDA_CLK_adxl345, ENABLE);
GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN_adxl345;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;
GPIO_Init(IIC_SDA_PORT_adxl345,&GPIO_InitStructure);
}
/**
* @brief SDA输入方向配置
* @param None
* @retval None
*/
void Set_IIC_SDA_IN_adxl345(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(IIC_SDA_CLK_adxl345, ENABLE);
GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN_adxl345;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(IIC_SDA_PORT_adxl345,&GPIO_InitStructure);
}
/**
* @brief 模拟IIC初始化
* @param None
* @retval None
*/
void IIC_init_adxl345()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(IIC_SDA_CLK_adxl345 | IIC_SCL_CLK_adxl345, ENABLE);
GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN_adxl345;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(IIC_SDA_PORT_adxl345, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = IIC_SCL_PIN_adxl345;
GPIO_Init(IIC_SCL_PORT_adxl345, &GPIO_InitStructure);
IIC_stop_adxl345();
}
/**
* @brief 模拟IIC起始信号
* @param None
* @retval None
*/
void IIC_start_adxl345()
{
Set_IIC_SDA_OUT_adxl345();
IIC_SCL_OUT_1_adxl345;
IIC_SDA_OUT_1_adxl345;
IIC_Delay_adxl345();
IIC_SDA_OUT_0_adxl345;
IIC_Delay_adxl345();
IIC_SCL_OUT_0_adxl345;
IIC_Delay_adxl345();
}
/**
* @brief 模拟IIC停止信号
* @param None
* @retval None
*/
void IIC_stop_adxl345()
{
Set_IIC_SDA_OUT_adxl345();
IIC_SCL_OUT_1_adxl345;
IIC_SDA_OUT_0_adxl345;
IIC_Delay_adxl345();
IIC_SDA_OUT_1_adxl345;
}
/**
* @brief 模拟IIC主机应答
* @param None
* @retval None
*/
void IIC_ack_adxl345()
{
Set_IIC_SDA_OUT_adxl345();
IIC_SDA_OUT_0_adxl345;
IIC_Delay_adxl345();
IIC_SCL_OUT_1_adxl345;
IIC_Delay_adxl345();
IIC_SCL_OUT_0_adxl345;
IIC_Delay_adxl345();
IIC_SDA_OUT_1_adxl345;
}
/**
* @brief 模拟IIC主机不应答
* @param None
* @retval None
*/
void IIC_noack_adxl345()
{
Set_IIC_SDA_OUT_adxl345();
IIC_SDA_OUT_1_adxl345;
IIC_Delay_adxl345();
IIC_SCL_OUT_1_adxl345;
IIC_Delay_adxl345();
IIC_SCL_OUT_0_adxl345;
IIC_Delay_adxl345();
}
/**
* @brief 模拟IIC等待从机应答
* @param None
* @retval 1: 接收应答失败 0: 接收应答成功
*/
uint8_t IIC_wait_ack_adxl345()
{
uint8_t rec = 0;
Set_IIC_SDA_OUT_adxl345();
IIC_SDA_OUT_1_adxl345;
IIC_Delay_adxl345();
Set_IIC_SDA_IN_adxl345();
IIC_SCL_OUT_1_adxl345;
IIC_Delay_adxl345();
rec = IIC_SDA_IN_adxl345();
IIC_SCL_OUT_0_adxl345;
IIC_Delay_adxl345();
return rec;
}
/**
* @brief 模拟IIC发送一个字节
* @param None
* @retval None
*/
void IIC_send_byte_adxl345(uint8_t txd)
{
uint8_t i=0;
Set_IIC_SDA_OUT_adxl345();
for(i=0;i<8;i++)
{
if(txd&0x80) IIC_SDA_OUT_1_adxl345;
else IIC_SDA_OUT_0_adxl345;
IIC_Delay_adxl345();
IIC_SCL_OUT_1_adxl345;
IIC_Delay_adxl345(); // 发送数据
IIC_SCL_OUT_0_adxl345;
if(i == 7) IIC_SDA_OUT_1_adxl345; // 最后一位数据发送完要释放SDA总线
txd <<= 1;
IIC_Delay_adxl345();
}
}
/**
* @brief 模拟IIC读取一个字节
* @param ack: 0,读完不产生应答 1,读完产生应答
* @retval 返回读取到的字节
*/
uint8_t IIC_read_byte_adxl345(uint8_t ack)
{
uint8_t i,receive=0;
Set_IIC_SDA_IN_adxl345();
for(i=0;i<8;i++)
{
receive <<= 1;
IIC_SCL_OUT_1_adxl345;
IIC_Delay_adxl345();
if(IIC_SDA_IN_adxl345()) receive++; // 连续读取八位
IIC_SCL_OUT_0_adxl345;
IIC_Delay_adxl345();
}
if(!ack) IIC_noack_adxl345();
else IIC_ack_adxl345();
return receive; // 返回读取到的字节
}
2、寄存器初始化配置
/**
* @brief ADXL345初始化
* @param None
* @retval None
*/
uint8_t ADXL345_init(void)
{
IIC_init_adxl345();
if(ADXL345_read_reg(DEVICE_ID) == 0xE5)
{
ADXL345_write_reg(DATA_FORMAT,0X0B); // 低电平中断输出,13位全分辨率,输出数据右对齐,16g量程
ADXL345_write_reg(BW_RATE,0x0E); // 数据输出速度为100Hz
ADXL345_write_reg(POWER_CTL,0x08); // 链接使能,测量模式,省电特性
ADXL345_write_reg(INT_ENABLE,0x80); // 不使用中断
ADXL345_write_reg(OFSX,0x00);
ADXL345_write_reg(OFSY,0x00);
ADXL345_write_reg(OFSZ,0x05);
return 0;
}
return 1;
}
3、读入数据
/**
* @brief ADXL345计算角度
* @param None
* @retval None
*/
void get_angle(float *x_angle,float *y_angle,float *z_angle)
{
short 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;
}
六、源码
/*
ADXL345三轴倾斜度模块
*/
#include "ADXL345.h"
#include "delay.h"
#include "math.h"
#define IIC_SDA_PORT_adxl345 GPIOB
#define IIC_SDA_CLK_adxl345 (RCC_APB2Periph_GPIOB)
#define IIC_SDA_PIN_adxl345 GPIO_Pin_0
#define IIC_SCL_PORT_adxl345 GPIOB
#define IIC_SCL_CLK_adxl345 (RCC_APB2Periph_GPIOB)
#define IIC_SCL_PIN_adxl345 GPIO_Pin_1
#define IIC_SCL_OUT_1_adxl345 IIC_SCL_PORT_adxl345->BSRR = (uint32_t)IIC_SCL_PIN_adxl345 // 置1
#define IIC_SCL_OUT_0_adxl345 IIC_SCL_PORT_adxl345->BRR = (uint32_t)IIC_SCL_PIN_adxl345
#define IIC_SDA_OUT_1_adxl345 IIC_SDA_PORT_adxl345->BSRR = (uint32_t)IIC_SDA_PIN_adxl345 // 置1
#define IIC_SDA_OUT_0_adxl345 IIC_SDA_PORT_adxl345->BRR = (uint32_t)IIC_SDA_PIN_adxl345
#define IIC_SDA_IN_adxl345() ((IIC_SDA_PORT_adxl345->IDR & IIC_SDA_PIN_adxl345) != 0)
/**
* @brief IIC延时
* @param None
* @retval None
*/
static void IIC_Delay_adxl345(void)
{
uint8_t i;
/*
下面的时间是通过逻辑分析仪测试得到的。
工作条件:CPU主频72MHz ,MDK编译环境,1级优化
循环次数为10时,SCL频率 = 205KHz
循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us
循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us
*/
for (i = 0; i < 10; i++);
}
/**
* @brief SDA输出方向配置
* @param None
* @retval None
*/
void Set_IIC_SDA_OUT_adxl345(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(IIC_SDA_CLK_adxl345, ENABLE);
GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN_adxl345;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;
GPIO_Init(IIC_SDA_PORT_adxl345,&GPIO_InitStructure);
}
/**
* @brief SDA输入方向配置
* @param None
* @retval None
*/
void Set_IIC_SDA_IN_adxl345(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(IIC_SDA_CLK_adxl345, ENABLE);
GPIO_InitStructure.GPIO_Pin=IIC_SDA_PIN_adxl345;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(IIC_SDA_PORT_adxl345,&GPIO_InitStructure);
}
/**
* @brief 模拟IIC初始化
* @param None
* @retval None
*/
void IIC_init_adxl345()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(IIC_SDA_CLK_adxl345 | IIC_SCL_CLK_adxl345, ENABLE);
GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN_adxl345;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(IIC_SDA_PORT_adxl345, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = IIC_SCL_PIN_adxl345;
GPIO_Init(IIC_SCL_PORT_adxl345, &GPIO_InitStructure);
IIC_stop_adxl345();
}
/**
* @brief 模拟IIC起始信号
* @param None
* @retval None
*/
void IIC_start_adxl345()
{
Set_IIC_SDA_OUT_adxl345();
IIC_SCL_OUT_1_adxl345;
IIC_SDA_OUT_1_adxl345;
IIC_Delay_adxl345();
IIC_SDA_OUT_0_adxl345;
IIC_Delay_adxl345();
IIC_SCL_OUT_0_adxl345;
IIC_Delay_adxl345();
}
/**
* @brief 模拟IIC停止信号
* @param None
* @retval None
*/
void IIC_stop_adxl345()
{
Set_IIC_SDA_OUT_adxl345();
IIC_SCL_OUT_1_adxl345;
IIC_SDA_OUT_0_adxl345;
IIC_Delay_adxl345();
IIC_SDA_OUT_1_adxl345;
}
/**
* @brief 模拟IIC主机应答
* @param None
* @retval None
*/
void IIC_ack_adxl345()
{
Set_IIC_SDA_OUT_adxl345();
IIC_SDA_OUT_0_adxl345;
IIC_Delay_adxl345();
IIC_SCL_OUT_1_adxl345;
IIC_Delay_adxl345();
IIC_SCL_OUT_0_adxl345;
IIC_Delay_adxl345();
IIC_SDA_OUT_1_adxl345;
}
/**
* @brief 模拟IIC主机不应答
* @param None
* @retval None
*/
void IIC_noack_adxl345()
{
Set_IIC_SDA_OUT_adxl345();
IIC_SDA_OUT_1_adxl345;
IIC_Delay_adxl345();
IIC_SCL_OUT_1_adxl345;
IIC_Delay_adxl345();
IIC_SCL_OUT_0_adxl345;
IIC_Delay_adxl345();
}
/**
* @brief 模拟IIC等待从机应答
* @param None
* @retval 1: 接收应答失败 0: 接收应答成功
*/
uint8_t IIC_wait_ack_adxl345()
{
uint8_t rec = 0;
Set_IIC_SDA_OUT_adxl345();
IIC_SDA_OUT_1_adxl345;
IIC_Delay_adxl345();
Set_IIC_SDA_IN_adxl345();
IIC_SCL_OUT_1_adxl345;
IIC_Delay_adxl345();
rec = IIC_SDA_IN_adxl345();
IIC_SCL_OUT_0_adxl345;
IIC_Delay_adxl345();
return rec;
}
/**
* @brief 模拟IIC发送一个字节
* @param None
* @retval None
*/
void IIC_send_byte_adxl345(uint8_t txd)
{
uint8_t i=0;
Set_IIC_SDA_OUT_adxl345();
for(i=0;i<8;i++)
{
if(txd&0x80) IIC_SDA_OUT_1_adxl345;
else IIC_SDA_OUT_0_adxl345;
IIC_Delay_adxl345();
IIC_SCL_OUT_1_adxl345;
IIC_Delay_adxl345(); // 发送数据
IIC_SCL_OUT_0_adxl345;
if(i == 7) IIC_SDA_OUT_1_adxl345; // 最后一位数据发送完要释放SDA总线
txd <<= 1;
IIC_Delay_adxl345();
}
}
/**
* @brief 模拟IIC读取一个字节
* @param ack: 0,读完不产生应答 1,读完产生应答
* @retval 返回读取到的字节
*/
uint8_t IIC_read_byte_adxl345(uint8_t ack)
{
uint8_t i,receive=0;
Set_IIC_SDA_IN_adxl345();
for(i=0;i<8;i++)
{
receive <<= 1;
IIC_SCL_OUT_1_adxl345;
IIC_Delay_adxl345();
if(IIC_SDA_IN_adxl345()) receive++; // 连续读取八位
IIC_SCL_OUT_0_adxl345;
IIC_Delay_adxl345();
}
if(!ack) IIC_noack_adxl345();
else IIC_ack_adxl345();
return receive; // 返回读取到的字节
}
/**
* @brief ADXL345初始化
* @param None
* @retval None
*/
uint8_t ADXL345_init(void)
{
IIC_init_adxl345();
if(ADXL345_read_reg(DEVICE_ID) == 0xE5)
{
ADXL345_write_reg(DATA_FORMAT,0X0B); // 低电平中断输出,13位全分辨率,输出数据右对齐,16g量程
ADXL345_write_reg(BW_RATE,0x0E); // 数据输出速度为100Hz
ADXL345_write_reg(POWER_CTL,0x08); // 链接使能,测量模式,省电特性
ADXL345_write_reg(INT_ENABLE,0x80); // 不使用中断
ADXL345_write_reg(OFSX,0x00);
ADXL345_write_reg(OFSY,0x00);
ADXL345_write_reg(OFSZ,0x05);
return 0;
}
return 1;
}
/**
* @brief ADXL345写寄存器
* @param None
* @retval None
*/
uint8_t ADXL345_write_reg(u8 addr,u8 val)
{
IIC_start_adxl345();
IIC_send_byte_adxl345(slaveaddress); // 发送写器件指令
if(IIC_wait_ack_adxl345())
{
return 1;
}
IIC_send_byte_adxl345(addr); // 发送寄存器地址
if(IIC_wait_ack_adxl345())
{
return 2;
}
IIC_send_byte_adxl345(val); // 发送值
if(IIC_wait_ack_adxl345())
{
return 3;
}
IIC_stop_adxl345(); // 产生一个停止条件
return 0;
}
/**
* @brief ADXL345读寄存器
* @param None
* @retval None
*/
u8 ADXL345_read_reg(u8 addr)
{
u8 temp=0;
IIC_start_adxl345();
IIC_send_byte_adxl345(slaveaddress); // 发送写器件指令
if(IIC_wait_ack_adxl345())
{
return 1;
}
IIC_send_byte_adxl345(addr); // 发送寄存器地址
if(IIC_wait_ack_adxl345())
{
return 2;
}
IIC_start_adxl345(); // 重新启动
IIC_send_byte_adxl345(regaddress); // 发送读器件指令
if(IIC_wait_ack_adxl345())
{
return 3;
}
temp=IIC_read_byte_adxl345(0); // 读取一个字节,不继续再读,发送NAK
IIC_stop_adxl345(); // 产生一个停止条件
return temp;
}
/**
* @brief ADXL345读取数据
* @param None
* @retval None
*/
void ADXL345_read_data(short *x,short *y,short *z)
{
u8 buf[6];
u8 i;
IIC_start_adxl345();
IIC_send_byte_adxl345(slaveaddress); // 发送写器件指令
IIC_wait_ack_adxl345();
IIC_send_byte_adxl345(0x32); // 发送寄存器地址(数据缓存的起始地址为0X32)
IIC_wait_ack_adxl345();
IIC_start_adxl345(); // 重新启动
IIC_send_byte_adxl345(regaddress); // 发送读器件指令
IIC_wait_ack_adxl345();
for(i=0;i<6;i++)
{
if(i==5)buf[i]=IIC_read_byte_adxl345(0); // 读取一个字节,不继续再读,发送NACK
else buf[i]=IIC_read_byte_adxl345(1); // 读取一个字节,继续读,发送ACK
}
IIC_stop_adxl345(); // 产生一个停止条件
*x=(short)(((u16)buf[1]<<8)+buf[0]); // 合成数据
*y=(short)(((u16)buf[3]<<8)+buf[2]);
*z=(short)(((u16)buf[5]<<8)+buf[4]);
}
/**
* @brief ADXL345连读读取几次取平均值
* @param None
* @retval None
*/
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_read_data(&tx,&ty,&tz);
*x+=tx;
*y+=ty;
*z+=tz;
delay_ms(5);
}
*x/=times;
*y/=times;
*z/=times;
}
}
/**
* @brief ADXL345计算角度
* @param None
* @retval None
*/
void get_angle(float *x_angle,float *y_angle,float *z_angle)
{
short 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;
}
测试数据正常,直接通过仿真查看数据。

通过更改ADXL345模块方位,摇晃等,可以明显看出xyz发送了改变,说明我们已经驱动ADXL345模块,同时能获取相关数据。可后期对该数据进行采集,平均值,卡尔曼滤波等处理,使得更加稳定。
如有侵权,或需要完整代码,请及时联系博主。
更多推荐
所有评论(0)