ADXL345驱动程序设计与实现过程(SPI通信)

一、ADXL345编程概述

概述

ADXL345是三轴加速度计,分辨率高(13位),测量范围达±16g。数字输出数据为16位二进制补码格式,可通过SPI(3线或4线)或I2C访问。

物理安装与方向

1. 通信接口

在本文中,主机通过SPI与ADXL345进行通信。通过在DATA_FORMAT寄存器(地址0x31)中清除D6位(SPI位),选择4线模式,并且配置时钟极性(CPOL)=1和时钟相位(CPHA)=1。

2. 初始化

ADXL345的初始化步骤如下:

1) 上电

2) 等待1.1ms

3) 初始化命令序列

4)初始化结束

这里的初始化命令序列,至少需要配置3个寄存器,如表1。详细的配置参考ADXL345数据手册。

表1 ADXL345 最简单的初始化命令序列
步骤寄存器地址寄存器名字寄存器值功能描述
10x31DATA_FORMAT0x0B±16g,13位模式
20x2DPOWER_CTL0x08测量模式
30x2EINT_ENABLE0x80使能DATA_READY中断

3. 读取输出数据

DATA_READY中断信号表示数据寄存器中的三轴加速度数据已更新。当新数据就绪时它被置为高电平(通过DATA_FORMAT寄存器,低电平转高电平触发中断服务)。可从DATA0、DATAX1、DATAY0、DATAY1、DATAZ0、DATAZ1寄存器中读取数据。推荐多字节读取数据。下图为4线式SPI读写序列实例。

指令格式
最高位次高位A5A4A3A2A1A0
功能描述读/写单字节/多字节寄存器地址

0-写

1-读

0-单字节

1-多字节

//////

4. 数据格式

 ADXL345是16位数据格式。从数据寄存器中获取加速度数据后要进行处理。DATAX0是X轴加速度低字节寄存器,DATAX1是高字节寄存器。13位模式下,高4位是符号位。参照ADXL345数据手册通过DATA_FORMAT寄存器设置更多数据格式。

ADXL345使用二进制补码数据格式。在13位模式下,1LSB代表3.9mg。

4. 使用偏移寄存器

ADXL345有偏移寄存器可进行偏移校准。偏移寄存器的数据格式是8位有符号数、二进制补码。偏移寄存器分辨率为15.6mg/LSB。将偏移值的相反数写入偏移寄存器来校准。如偏移为+156mg,就向偏移寄存器写入-156mg。

偏移校准序列

二、运行环境

1. 环境设置:①开发环境:安装stm32CubeMx和IDE(keil);

                       ② 硬件连接:
                           PA5  ------> SPI1_SCK
                           PA6 ------> SPI1_MISO
                           PA7   ------> SPI1_MOSI
2. 创建项目,并启用必要的外设(如SPI1、USART1等),并生成初始化代码。

三、STM32CubeMX配置

1. 创建 STM32CubeMX 项目

  • 打开 STM32CubeMX,创建一个新项目。

  • 在芯片选择器中搜索并选择 STM32L051C8Tx(或其他具体型号)。

  • 点击 "Start Project" 创建项目。

2. 配置时钟树

  • 在 "Clock Configuration" 选项卡中,配置系统时钟源和频率。

  • 如果使用外部晶振(HSE),确保启用 HSE 并配置 PLL 以生成所需的系统时钟频率(例如 32 MHz)。

  • 如果使用内部晶振(HSI),直接使用 HSI 作为系统时钟源。

3. 配置 SPI 接口

  • ADXL345 通过 SPI 接口与 STM32 通信。

  • 在 "Pinout & Configuration" 选项卡中,找到 SPI 外设(例如 SPI1)。

  • 配置 SPI  Mode为 Full-Duplex Master 模式。

  • 配置 SPI 的引脚:①SCK:时钟信号;②MISO:主设备输入,从设备输出;③MOSI:主设备输出,从设备输入;④NSS:片选信号(可选,如果使用软件控制片选,可以忽略)。

  • 配置 SPI 的参数(Parameter Settings):

    • Baud Rate:设置 SPI 通信速率(例如62.5KBit/s)。

    • Data Size:8 Bits。

    • Clock Polarity (CPOL):High。

    • Clock Phase (CPHA):2 Edge。

NSS Signal Type:如果使用软件控制片选,选择 "Software NSS Management"


4. 配置 GPIO 引脚

  • ADXL345 的片选信号(CS)通常通过 GPIO 控制。

  • 在 "Pinout & Configuration" 选项卡中,选择一个 GPIO 引脚(例如 PB0)作为片选信号。

配置该引脚为 Output Push-Pull 模式,初始状态为高电平(CS 高电平表示 SPI 从设备未选中)。

5. 配置RCC

6. 配置 USART(可选)

  • 如果需要通过串口调试输出数据,可以配置 USART。

  • 在 "Pinout & Configuration" 选项卡中,找到 USART 外设(例如 USART1)。

  • 配置 USART 为 Asynchronous 模式。

  • 配置 USART 的引脚:

    • TX:发送数据。

    • RX:接收数据。

  • 配置 USART 的参数:

    • Baud Rate:例如 115200。

    • Word Length:8 bits。

    • Parity:None。

    • Stop Bits:1。


7. 生成代码

  • 在 "Project Manager" 选项卡中,配置项目名称、工具链(例如 MDK-ARM、STM32CubeIDE 等)和代码生成选项。

  • 点击 "Generate Code" 生成代码。

四、编写代码(主要部分)

1. ADXL345.C

头文件和宏定义

#include "stm32l0xx_hal.h"
#include "adxl345.h"
#include "spi.h"
#include "usart.h"
// 其他必要的头文件

#define SET_SPI_CS_L()  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET)
#define SET_SPI_CS_H()  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET)
#define ADXL345_SCALE_FACTOR  0.0039f         // ±16g 范围的灵敏度为 3.9 mg/LSB
#define M_PI    3.14159265358979323846

ADXL345寄存器读写操作

寄存器地址格式

  • 最高位(MSB, 第7位):为1,读操作。为0,写操作
  • 次高位(第6位):为1,启用多字节模式,连续读取多个寄存器的数据。
//PB0作为CS引脚,低电平有效
#define SET_SPI_CS_L()  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET)
#define SET_SPI_CS_H()  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET)


//读取ADXL345寄存器地址,从指定寄存器读取nReadbyte个字节
static void ADXL345_ReadRegister(uint8_t regAddress, uint8_t *buf, uint8_t nReadbyte)
{
  if ( 1 == nReadbyte)
    regAddress = (regAddress & 0x3F) | 0x80;   //读单个字节
  else 
    regAddress = (regAddress & 0x3F) | 0xC0;   //读多个字节, 多字节读取时寄存器的自动递增
  
  uint8_t txBuf[1+ nReadbyte]; memset(txBuf+1, 0, nReadbyte); //填充后续字节为0以生成时钟
  uint8_t rxBuf[1+ nReadbyte]; memset(rxBuf, 0, sizeof(rxBuf)); //接收初始化为0
  txBuf[0] = regAddress;
 
  SET_SPI_CS_L();
  HAL_SPI_TransmitReceive(&hspi1, txBuf, rxBuf, 1+nReadbyte, 100)
  SET_SPI_CS_H();
  
  memcpy(buf, rxBuf+1, nReadbyte);  //有效数据从rxBuf[1]开始
}

//写寄存器
static void ADXL345_WriteRegister(uint8_t regAddress, uint8_t command)
{
  regAddress &= 0x3F; 
  uint8_t data[2] = {regAddress, command};
  
  SET_SPI_CS_L();
  HAL_SPI_Transmit(&hspi1, data, 2, 10)
  SET_SPI_CS_H();
}

 ADXL345初始化

static uint8_t ADXL345_GetID(void)
{
  uint8_t id = 0x00;
  ADXL345_ReadRegister(DEVICE_ID, &id, 1);
  return id;
}

void ADXL345_Init(void)
{
  uint8_t deviceID = ADXL345_GetID(); //读取设备ID,确认与正确的ADXL345传感器通信
  while (deviceID != 0xE5)
  {
      HAL_Delay(1000);
      deviceID = ADXL345_GetID();
   }
   
  //进入待机模式,便于进行配置
   ADXL345_WriteRegister(POWER_CTL, 0x00); 
  //设置数据格式
   ADXL345_WriteRegister(DATA_FORMAT, 0x0B); //全分辨率,±16g范围和小端字节序并带符号扩展
   //设置输出数据速率(带宽率)
   ADXL345_WriteRegister(BW_RATE,0x0A);      //100HZ
   //进入测量模式
   ADXL345_WriteRegister(POWER_CTL,0x08); 
   
   //初始化偏移寄存器
   ADXL345_Calibrate();
   
   //打印初始化成功信息
}

1. SPI通信速率(波特率或时钟频率)和输出数据速率(ODR)要匹配,避免数据丢失或产生噪声。如SPI通信速率2MHz或更高,ODR设置为3200Hz或1600Hz。如果SPI通信速率400kHz,ODR设置为800Hz。

数据读取与处理

根据adxl345手册page.26,建议所有寄存器执行多字节读取,以防止相继寄存器读取之间的数据变化。

//读原始x,y,z轴数据(LSB)
void ADXL345_ReadXYZ(short *x, short *y, short *z)
{
  uint8_t data[6]= {0};
      
  //读取X、Y、Z轴原始数据
  ADXL345_ReadRegister(DATAX0 , data, 6);  
  
  //数据处理,将两个8位字节合并成一个16位的有符号整数,高位在前低位在后
  *x = (int16_t)((int8_t)data[1]<<8)|data[0];    
  *y = (int16_t)((int8_t)data[3]<<8)|data[2];
  *z = (int16_t)((int8_t)data[5]<<8)|data[4];
}

//ADXL345取平均值,x,y,z读取times次后取平均值,并转成实际的加速度值
void ADXL345_ReadAverage(float *x, float *y, float *z, uint8_t times)
{ 
  int32_t x_num = 0, y_num = 0, z_num = 0;

  for (uint8_t i = 0; i < times; i++)
  {
    short tx=0, ty=0, tz=0;
    ADXL345_ReadXYZ(&tx, &ty, &tz);
    x_num += (int32_t)tx;  // 原始LSB累加
    y_num += (int32_t)ty;
    z_num += (int32_t)tz;
    HAL_Delay(5);
  }  
  float x_avg = (float)x_num / times;  //平均值计算时先转成float,避免整数除法的精度丢失
  float y_avg = (float)y_num / times;
  float z_avg = (float)z_num / times;

  //乘灵敏度系数以转换成实际的加速度值(单位:g)
  *x = x_avg * ADXL345_SCALE_FACTOR;
  *y = y_avg * ADXL345_SCALE_FACTOR;
  *z = z_avg * ADXL345_SCALE_FACTOR; 
}

//角度计算:计算相对于自然坐标系各轴的角度
void ADXL345_GetAngle(float *x_angle, float *y_angle)
{
  float x = 0, y = 0, z = 0;
  ADXL345_ReadAverage(&x, &y, &z, 10);

  float x_denom = sqrt(y*y + z*z);
  float y_denom = sqrt(x*x + z*z);
  //添加除以零保护
  if (fabs(x_denom) < 1e-6f)  x_denom = 1e-6f;
  if (fabs(y_denom) < 1e-6f)  y_denom = 1e-6f;

  *x_angle = atan(x/x_denom)*180/M_PI;  //#define M_PI 3.14159265358979323846 
  *y_angle = atan(y/y_denom)*180/M_PI;
}

1. 加速度计算公式

将原始数据(Raw Data)转换为实际加速度(g)的公式为:

                              Acceleration (g)=Raw Data×LSB Sensitivity

其中:

  • Raw Data 是从传感器读取的 16 位有符号整数(范围:-32768 到 +32767)。

    • LSB Sensitivity 是根据量程确定的灵敏度,如下表。

量程和对应的LSB灵敏度
量程(Range)LSB 灵敏度(Sensitivity)
所有g范围,全分辨率(13位)3.9 mg/LSB (0.0039 g/LSB)
±2g,10位分辨率3.9 mg/LSB (0.0039 g/LSB)
±4g,10位分辨率7.8 mg/LSB (0.0078 g/LSB)
±8g,10位分辨率15.6 mg/LSB (0.0156 g/LSB)
±16g,10位分辨率31.2 mg/LSB (0.0312 g/LSB)

示例:

假设:

  • 量程设置为 ±2g,LSB 灵敏度为 0.0039 g/LSB

  • 读取的原始数据为 1000

则加速度为:

                              Acceleration (g)=1000×0.0039=3.9 g

2. 加速度值转换为角度

对于每个轴,计算其相对于地面的倾斜角度,可用公式:

  • X轴的倾斜角(Roll):\theta_{x} =\arctan\left ( \frac{A_{x}}{\sqrt{A_{y}^2+A_{z}^2}} \right )
  • Y轴的倾斜角(Pitch):\theta_{y} =\arctan\left ( \frac{A_{y}}{\sqrt{A_{x}^2+A_{z}^2}} \right )
  • Z轴通常用于表示偏航角Yaw,但在纯静态情况下无法直接由加速度计获得,因为没有外力作用于Z轴方向(除了重力,但它不影响绕Z轴的旋转)。偏航角一般需要结合陀螺仪或者磁力计的数据进行计算。
  • 其中,Ax​,Ay​,Az​ 分别是X、Y、Z轴上的加速度读数(已转换为g单位)

数据显示

//使用串口打印加速度值或角度信息
void ADXL345_PrintAccelerometerValues(void)
{
  char buffer1[50] = {0};
  char buffer2[50] = {0};
  
  float x_value = 0, y_value = 0, z_value = 0;
  float x_angle=0, y_angle=0;
  ADXL345_ReadAverage(&x_value, &y_value, &z_value, 10);

  ADXL345_GetAngle(&x_angle, &y_angle);
  snprintf(buffer1, sizeof(buffer1), "x_value:%.2f, y_value:%.2f, z_value:%.2f\r\n", x_value, y_value, z_value);
  snprintf(buffer2, sizeof(buffer2), "x_angle:%.2f, y_angle:%.2f\r\n", x_angle, y_angle);
  HAL_UART_Transmit(&huart1, (uint8_t*)buffer1, strlen(buffer1), 10);
  HAL_UART_Transmit(&huart1, (uint8_t*)buffer2, strlen(buffer2), 10);
  HAL_Delay(2000);
}

误差校正(静态校准)

静止状态下,多次采样求平均值

//x,y,z静态校准
void ADXL345_Calibrate(void)
{
  long x_temp = 0, y_temp = 0, z_temp = 0;
  const uint8_t num_samples = 100;  //采样次数

  //读取多次数据算平均值
  for(uint8_t i = 0; i < num_samples; i++)
  {
    short x, y, z;
    ADXL345_ReadXYZ(&x, &y, &z);
    x_temp += x;
    y_temp += y;
    z_temp += z;
    HAL_Delay(10);  //确保每次读取之间有足够时间间隔,避免数据读取过快导致不准确
  }
  
  //计算平均值
  int16_t x_avg = x_temp/num_samples;
  int16_t y_avg = y_temp/num_samples;
  int16_t z_avg = z_temp/num_samples;
  
  //计算偏移值(单位:LSB)
  int8_t offsetx = (int8_t)roundf((-x_avg) / 4.0);   //roundf返回四舍五入后的整数值
  int8_t offsety = (int8_t)roundf((-y_avg) / 4.0);
//  //Z轴期望值通常为256LSB(1g)
  int8_t offsetz = (int8_t)roundf((256-z_avg) / 4.0);
  
  //写入偏移寄存器,ADXL345 会自动补偿零偏误差
  ADXL345_WriteRegister(OFSX, (uint8_t)offsetx);
  ADXL345_WriteRegister(OFSY, (uint8_t)offsety);
  ADXL345_WriteRegister(OFSZ, (uint8_t)offsetz);
}

 1. 偏移量寄存器

    用于存储每个轴的偏移校准值

  • X 轴偏移寄存器OFSX:地址0x1E

  • Y 轴偏移寄存器OFSY:地址0x1F

  • Z 轴偏移寄存器OFSZ:地址0x20

    每个寄存器的值是一个 8 位有符号整数,范围为 -128 到 +127

2. 偏移量的计算

    步骤 1:静止状态下读取原始数据

    将 ADXL345 放置在静止状态(理论上加速度为 0g),读取 X、Y、Z 轴的原始数据。

    步骤 2:计算偏移量

    偏移量的计算公式为:

                             \text{offset} =(Expected - Measured) / 4

    

    其中:

     Expected为期望值(通常X、Y为0g(0 LSB),Z为1g(256 LSB))

     Measured为实测值(LSB)。

    步骤 3:写入偏移量寄存器

    将计算得到的偏移量(单位LSB)写入对应的偏移量寄存器(0x1E、0x1F、0x20)。

2. ADXL345.h

#ifndef _ADXL345_H
#define _ADXL345_H

#include "spi.h"
#include "stdint.h"
#include "stm32l0xx_hal.h"

/********ADXL345寄存器命令定义********/
/********  #define 名称 寄存器地址 ********/
#define DEVICE_ID           0x00

#define OFSX                0x1E  //x轴偏移
#define OFSY                0x1F  //y轴偏移
#define OFSZ                0x20  //Z轴偏移

#define BW_RATE             0x2C  //数据速率及功率模式控制
#define POWER_CTL           0x2D


#define DATA_FORMAT         0x31  //数据格式控制
#define DATAX0              0x32  //X轴数据0
#define DATAX1              0x33  //X轴数据1
#define DATAY0              0x34  //Y轴数据0
#define DATAY1              0x35  //Y轴数据1
#define DATAZ0              0x36  //Z轴数据0
#define DATAZ1              0x37  //Z轴数据1

void ADXL345_Init(void);
extern void ADXL345_Calibrate(void);
void ADXL345_ReadXYZ(short *x, short *y, short *z);
void ADXL345_ReadAverage(float *x, float *y, float *z, uint8_t times);
void ADXL345_GetAngle(float *x_angle, float *y_angle);

extern void ADXL345_PrintAccelerometerValues(void);

#endif

3. main.c

int main(void)
{
    HAL_Init();
    SystemClock_Config(); 
    MX_GPIO_Init();
    MX_SPI1_Init();
    MX_USART1_UART_Init();

    ADXL345_Init();

    while (1)
    {
        ADXL345_PrintAccelerometerValues();
    }
}

六、运行结果

七、资源

#include #include //Keil library #include //Keil library #include #define uchar unsigned char #define uint unsigned int #define DataPort P0 //LCD1602数据端口 sbit SCL=P1^0; //IIC时钟引脚定义 sbit SDA=P1^1; //IIC数据引脚定义 sbit LCM_RS=P2^0; //LCD1602命令端口 sbit LCM_RW=P2^1; //LCD1602命令端口 sbit LCM_EN=P2^2; //LCD1602命令端口 #define SlaveAddress 0xA6 //定义器件在IIC总线中的从地址,根据ALT ADDRESS地址引脚不同修改 //ALT ADDRESS引脚接地时地址为0xA6,接电源时地址为0x3A typedef unsigned char BYTE; typedef unsigned short WORD; BYTE BUF[8]; //接收数据缓存区 uchar ge,shi,bai,qian,wan; //显示变量 int dis_data; //变量 void delay(unsigned int k); void InitLcd(); //初始化lcd1602 void Init_ADXL345(void); //初始化ADXL345 void WriteDataLCM(uchar dataW); void WriteCommandLCM(uchar CMD,uchar Attribc); void DisplayOneChar(uchar X,uchar Y,uchar DData); void conversion(uint temp_data); void Single_Write_ADXL345(uchar REG_Address,uchar REG_data); //单个写入数据 uchar Single_Read_ADXL345(uchar REG_Address); //单个读取内部寄存器数据 void Multiple_Read_ADXL345(); //连续的读取内部寄存器数据 //------------------------------------ void Delay5us(); void Delay5ms(); void ADXL345_Start(); void ADXL345_Stop(); void ADXL345_SendACK(bit ack); bit ADXL345_RecvACK(); void ADXL345_SendByte(BYTE dat); BYTE ADXL345_RecvByte(); void ADXL345_ReadPage(); void ADXL345_WritePage(); //-----------------------------------
### 使用PHPStudy搭建JavaSEC开发环境 #### 1. 准备工作 为了在 PHPStudy 中创建 JavaSEC 开发环境,需先安装并配置好 PHPStudy 平台。确保已下载最新版的 PHPStudy 软件包,并按照官方指南完成基本设置[^1]。 #### 2. 安装 JDK (Java Development Kit) 由于 Java 应用程序依赖于特定版本的 JVM 来运行,在 PHPStudy 的基础上构建 JavaSEC 环境前,必须单独安装适合目标系统的 JDK 版本。可以从 Oracle 或者 OpenJDK 获取稳定发行版,并遵循其文档中的说明来执行本地部署过程。 #### 3. 配置环境变量 成功安装 JDK 后,需要更新操作系统的 PATH 和 JAVA_HOME 变量指向新安装路径下的 bin 文件夹位置。这一步骤对于命令行工具能够识别 java 命令至关重要。 #### 4. 下载 Apache Tomcat Web Server Apache Tomcat 是广泛使用的 Servlet/JSP 容器之一,非常适合用来承载基于 Spring Framework 构建的应用服务端逻辑部分。前往官方网站获取适用于当前平台架构类型的二进制分发压缩文件,并将其解压到指定目录下保存备用。 #### 5. 设置项目结构导入依赖库 通过 IDE 如 IntelliJ IDEA 或 Eclipse 新建 Maven 工程作为起点;接着定义 pom.xml 描述符内必要的 GroupId, ArtifactId 字段以及引入 spring-boot-starter-parent POM 继承关系。随后添加 web starter module 支持 RESTful API 设计模式的同时也方便后续集成安全组件实现防护措施。 ```xml <dependencies> <!-- Other dependencies --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Security related modules can be added here --> </dependencies> ``` #### 6. 将应用部署至Tomcat服务器 当所有准备工作完成后,可以考虑把打包好的 WAR 归档上传给 Tomcat 进行在线测试验证功能完整性。通常情况下只需简单复制粘贴 war 到 webapps 子文件夹即可触发自动展开机制启动应用程序实例。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gouxf_0219

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值