目录
1.IIC总线
1.1 IIC概述
IIC总线是一个多向控制总线,多个器件(从机)可以同时挂载到一个主机控制的一条总线上,每个连接在总线上的设备都是通过唯一的地址和其他器件通信。
与串口的一对一通信方式不同,总线通信通常有主机(Master)和从机(Slave)之分。通信时,主机负责启动和终止数据传送,同时还要输出时钟信号;从机会被主机寻址,并且响应主机的通信请求。
主机就是负责整个系统的任务协调与分配,从机一般是通过接收主机的指令从而完成某些特定的任务,主机和从机之间通过总线连接,进行数据通讯。
串口通信双方需要事先约定同样的波特率才能正常进行通信。而在IIC通信中,通信速率的控制由主机完成,主机会通过SCL引脚输出时钟信号供总线上的所有从机使用。 同时,IIC是一种半双工通信方式,即总线上的设备通过SDA引脚传输通信数据,数据的发送和接收由主机控制,切换进行。
IIC上的所有通信都是由主机发起的,总线上的设备都应该有各自的地址。主机可以通过这些地址向总线上的任一设备发起连接,从机响应请求并建立连接后,便可进行数据传输。IIC总线上的主设备与从设备之间以字节(8位)为单位进行双向的数据传输。
- 软件IIC:软件IIC通信指的是用单片机的两个I/O端口模拟出来的IIC,用软件控制管脚状态以模拟IIC通信波形,软件模拟寄存器的工作方式。
- 硬件IIC:一块硬件电路,硬件IIC对应芯片上的IIC外设,有相应IIC驱动电路,其所使用的IIC管脚也是专用的,硬件(固件)IIC是直接调用内部寄存器进行配置。
硬件IIC的效率要远高于软件的,而软件IIC由于不受管脚限制,接口比较灵活。
1.2 IIC通信协议
IIC总线是由数据线SDA和时钟线SCL以及GND构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbs以上。
- 时钟线SCL:在通信过程起到控制作用。
- 数据线SDA:用来一位一位的传送数据。
通信原理是通过对SCL和SDA线高低电平时序的控制,来产生IIC总线协议所需要的信号,从而进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。
2.Wire类库
对于IIC总线的使用,Arduino IDE自带了一个第三方类库Wire。
2.1 常用函数
2.1.1 begin()
begin()函数有4个重载:
// 如果i2c总线初始化成功,返回true
bool begin(int sda, int scl, uint32_t frequency=0);
bool begin(uint8_t slaveAddr, int sda, int scl, uint32_t frequency);
// Arduino主流API的显式重载兼容性
inline bool begin()
{
return begin(-1, -1, static_cast<uint32_t>(0));
}
inline bool begin(uint8_t addr)
{
return begin(addr, -1, -1, 0);
}
inline bool begin(int addr)
{
return begin(static_cast<uint8_t>(addr), -1, -1, 0);
}
- 功能:初始化IIC连接,并作为主机或者从机设备加入IIC总线。
- 参数:address 一个7位的从机地址。不填,设备将以主机形式加入IIC总线。
- 参数:sda 数据线对应引脚,不填则默认为8。
- 参数:scl 时钟线对应引脚,不填则默认为9。
- 参数:frequency 频率,不填则默认为100,000。
- 返回值:是否初始化成功。
2.1.2 requestFrom( )
requestFrom()函数有8个重载:
size_t requestFrom(uint16_t address, size_t size, bool sendStop);
uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop);
uint8_t requestFrom(uint16_t address, uint8_t size, uint8_t sendStop);
size_t requestFrom(uint8_t address, size_t len, bool stopBit);
uint8_t requestFrom(uint16_t address, uint8_t size);
uint8_t requestFrom(uint8_t address, uint8_t size, uint8_t sendStop);
uint8_t requestFrom(uint8_t address, uint8_t size);
uint8_t requestFrom(int address, int size, int sendStop);
uint8_t requestFrom(int address, int size);
- 功能:主机向从机发送数据请求信号。使用
requestFrom()
后,从机端可以使用onRequest()
注册一个事件用以响应主机的请求;主机可以通过available()
和read()
函数读取这些数据。 - 参数:address 从机地址。
- 参数:size 读取数据字节大小。
- 参数:sendStop 当其值为true时,将发送一个停止信息,释放IIC总线;当为false时,将发送一个重新开始信息,并继续保持IIC总线的有效连接。
- 返回值:出问题返回0。
2.1.3 beginTransmission( )
void beginTransmission(uint16_t address);
- 功能:设定传输数据到指定地址的从机设备。随后可以使用
write()
函数发送数据,并搭配endTransmission()函数结束数据传输。 - 参数:address,要发送的从机的7位地址。
- 返回值:无。
2.1.4 endTransmission( )
uint8_t endTransmission(bool sendStop);
uint8_t endTransmission(void);
- 功能:结束数据传输。
- 参数:stop, boolean型值,当其值为true时将发送一一个停止信息,释放IIC总线,当没有填写stop参数时,等效使用true;当为false时,将发送一个重新开始信息,并继续保持IIC总线的有效连接。
- 返回值:byte型值,表示本次传输的状态,取值为:0,成功;1,数据过长,超出发送缓冲区;2,在地址发送时接收到NACK信号,3,在数据发送时接收到NACK信号;4,其他错误。
2.1.5 write( )
write()有6个重载:
size_t TwoWire::write(uint8_t data);
size_t TwoWire::write(const uint8_t *data, size_t quantity);
inline size_t write(const char * s)
{
return write((uint8_t*) s, strlen(s));
}
inline size_t write(unsigned long n)
{
return write((uint8_t)n);
}
inline size_t write(long n)
{
return write((uint8_t)n);
}
inline size_t write(unsigned int n)
{
eturn write((uint8_t)n);
}
inline size_t write(int n)
{
return write((uint8_t)n);
}
- 功能:当为主机状态时,主机将要发送的数据加入发送队列;当为从机状态时,从机发送数据至发起请求的主机
- 参数:data 要发送的数据。
- 参数:length 传输的字节数。
- 返回值:byte型值,返回输入的字节数。
2.1.6 read()
int read(void);
- 功能:读取1字节的数据。在主机中,当使用
requestFrom()
函数发送数据请求信号后,需要使用read()
函数来获取数据;在从机中需要使用该函数读取主机发送来的数据。 - 参数:无。
- 返回值:读到的字节数据。
3.ADXL345
3.1 数据手册下载
3.2 模块引脚定义
- GND:电源地(供电负极)
- VCC:供电正极
- CS:片选
- INT1:中断1
- INT2:中断2
- SDO:SPI数据输出/IIC地址设定
- SDA:SPI数据I/O、IIC数据线(兼容5V电平)
- SCL:SPI时钟线、IIC时钟线(兼容5V电平)
接线使用GND、VCC、SDA、SCL即可。
3.3 示例代码
初始化后调用读取XYZ轴数据的函数即可。
3.3.1 写寄存器
/**
* @brief ADXL345写寄存器
* @param addr 寄存器地址
* @param val 要写的值
* @retval None
*/
void ADXL345_write_reg(uint8_t addr, uint8_t val)
{
Wire.beginTransmission(ADXL345_DEFAULT_ADDRESS);
Wire.write(addr);
Wire.write(val);
Wire.endTransmission(true);
}
3.3.2 读寄存器
/**
* @brief ADXL345读寄存器
* @param addr 寄存器地址
* @retval 读取到的寄存器值
*/
uint8_t ADXL345_read_reg(uint8_t addr)
{
uint8_t temp = 0;
Wire.beginTransmission(ADXL345_DEFAULT_ADDRESS);
Wire.write(addr);
Wire.endTransmission(true);
Wire.requestFrom(ADXL345_DEFAULT_ADDRESS, 1);
temp = Wire.read();
return temp;
}
3.3.3 模块初始化
/**
* @brief ADXL345初始化
* @param None
* @retval ID正确返回0,错误返回1
*/
uint8_t adxl345_init(void)
{
Wire.begin(SDA, SCL);
// Serial.printf("%x",ADXL345_read_reg(ADXL345_REG_DEVID));
if (ADXL345_read_reg(ADXL345_REG_DEVID) == 0xE5) // 检测设备ID
{
Serial.println("检测到ADXL345芯片!");
// 低电平中断输出,13位全分辨率,输出数据右对齐,16g量程
ADXL345_write_reg(ADXL345_REG_DATA_FORMAT, ADXL345_RANGE_16_G);
// 数据输出速度为100Hz
ADXL345_write_reg(ADXL345_REG_BW_RATE, ADXL345_DATARATE_100_HZ);
// 链接使能,测量模式,省电特性
ADXL345_write_reg(ADXL345_REG_POWER_CTL, 0x08);
// 不使用中断
ADXL345_write_reg(ADXL345_REG_INT_ENABLE, 0x80);
ADXL345_write_reg(ADXL345_REG_OFSX, 0x00);
ADXL345_write_reg(ADXL345_REG_OFSY, 0x00);
ADXL345_write_reg(ADXL345_REG_OFSZ, 0x05);
return 0;
}
return 1;
}
3.3.4 读取X、Y、Z轴数据
/**
* @brief ADXL345读取数据
* @param None
* @retval None
*/
void ADXL345_read_data(short *x, short *y, short *z)
{
uint8_t buf[6];
uint8_t i;
Wire.beginTransmission(ADXL345_DEFAULT_ADDRESS);
Wire.write(ADXL345_REG_DATAX0);
Wire.endTransmission(true);
Wire.requestFrom(ADXL345_DEFAULT_ADDRESS, 6);
for (i = 0; i < 6; i++)
{
buf[i] = Wire.read(); // 读取一个字节,不继续再读,发送NACK
}
*x = (short)(((uint16_t)buf[1] << 8) + buf[0]); // 合成数据
*y = (short)(((uint16_t)buf[3] << 8) + buf[2]);
*z = (short)(((uint16_t)buf[5] << 8) + buf[4]);
}
3.3.5 寄存器宏定义
/*=========================================================================
I2C ADDRESS/BITS
-----------------------------------------------------------------------*/
#define ADXL345_DEFAULT_ADDRESS (0x53) ///< Assumes ALT address pin low
/*=========================================================================*/
/*=========================================================================
REGISTERS
-----------------------------------------------------------------------*/
#define ADXL345_REG_DEVID (0x00) ///< Device ID
#define ADXL345_REG_THRESH_TAP (0x1D) ///< Tap threshold
#define ADXL345_REG_OFSX (0x1E) ///< X-axis offset
#define ADXL345_REG_OFSY (0x1F) ///< Y-axis offset
#define ADXL345_REG_OFSZ (0x20) ///< Z-axis offset
#define ADXL345_REG_DUR (0x21) ///< Tap duration
#define ADXL345_REG_LATENT (0x22) ///< Tap latency
#define ADXL345_REG_WINDOW (0x23) ///< Tap window
#define ADXL345_REG_THRESH_ACT (0x24) ///< Activity threshold
#define ADXL345_REG_THRESH_INACT (0x25) ///< Inactivity threshold
#define ADXL345_REG_TIME_INACT (0x26) ///< Inactivity time
#define ADXL345_REG_ACT_INACT_CTL \
(0x27) ///< Axis enable control for activity and inactivity detection
#define ADXL345_REG_THRESH_FF (0x28) ///< Free-fall threshold
#define ADXL345_REG_TIME_FF (0x29) ///< Free-fall time
#define ADXL345_REG_TAP_AXES (0x2A) ///< Axis control for single/double tap
#define ADXL345_REG_ACT_TAP_STATUS (0x2B) ///< Source for single/double tap
#define ADXL345_REG_BW_RATE (0x2C) ///< Data rate and power mode control
#define ADXL345_REG_POWER_CTL (0x2D) ///< Power-saving features control
#define ADXL345_REG_INT_ENABLE (0x2E) ///< Interrupt enable control
#define ADXL345_REG_INT_MAP (0x2F) ///< Interrupt mapping control
#define ADXL345_REG_INT_SOURCE (0x30) ///< Source of interrupts
#define ADXL345_REG_DATA_FORMAT (0x31) ///< Data format control
#define ADXL345_REG_DATAX0 (0x32) ///< X-axis data 0
#define ADXL345_REG_DATAX1 (0x33) ///< X-axis data 1
#define ADXL345_REG_DATAY0 (0x34) ///< Y-axis data 0
#define ADXL345_REG_DATAY1 (0x35) ///< Y-axis data 1
#define ADXL345_REG_DATAZ0 (0x36) ///< Z-axis data 0
#define ADXL345_REG_DATAZ1 (0x37) ///< Z-axis data 1
#define ADXL345_REG_FIFO_CTL (0x38) ///< FIFO control
#define ADXL345_REG_FIFO_STATUS (0x39) ///< FIFO status
/*=========================================================================*/
3.3.6 频率及量程枚举
/**
* @brief Used with register 0x2C (ADXL345_REG_BW_RATE) to set bandwidth
*/
typedef enum {
ADXL345_DATARATE_3200_HZ = 0b1111, ///< 1600Hz Bandwidth 140uA IDD
ADXL345_DATARATE_1600_HZ = 0b1110, ///< 800Hz Bandwidth 90uA IDD
ADXL345_DATARATE_800_HZ = 0b1101, ///< 400Hz Bandwidth 140uA IDD
ADXL345_DATARATE_400_HZ = 0b1100, ///< 200Hz Bandwidth 140uA IDD
ADXL345_DATARATE_200_HZ = 0b1011, ///< 100Hz Bandwidth 140uA IDD
ADXL345_DATARATE_100_HZ = 0b1010, ///< 50Hz Bandwidth 140uA IDD
ADXL345_DATARATE_50_HZ = 0b1001, ///< 25Hz Bandwidth 90uA IDD
ADXL345_DATARATE_25_HZ = 0b1000, ///< 12.5Hz Bandwidth 60uA IDD
ADXL345_DATARATE_12_5_HZ = 0b0111, ///< 6.25Hz Bandwidth 50uA IDD
ADXL345_DATARATE_6_25HZ = 0b0110, ///< 3.13Hz Bandwidth 45uA IDD
ADXL345_DATARATE_3_13_HZ = 0b0101, ///< 1.56Hz Bandwidth 40uA IDD
ADXL345_DATARATE_1_56_HZ = 0b0100, ///< 0.78Hz Bandwidth 34uA IDD
ADXL345_DATARATE_0_78_HZ = 0b0011, ///< 0.39Hz Bandwidth 23uA IDD
ADXL345_DATARATE_0_39_HZ = 0b0010, ///< 0.20Hz Bandwidth 23uA IDD
ADXL345_DATARATE_0_20_HZ = 0b0001, ///< 0.10Hz Bandwidth 23uA IDD
ADXL345_DATARATE_0_10_HZ =
0b0000 ///< 0.05Hz Bandwidth 23uA IDD (default value)
} dataRate_t;
/**
* @brief Used with register 0x31 (ADXL345_REG_DATA_FORMAT) to set g range
*
*/
typedef enum {
ADXL345_RANGE_16_G = 0b11, ///< +/- 16g
ADXL345_RANGE_8_G = 0b10, ///< +/- 8g
ADXL345_RANGE_4_G = 0b01, ///< +/- 4g
ADXL345_RANGE_2_G = 0b00 ///< +/- 2g (default value)
} range_t;
3.3.7 读多次取平均值
/**
* @brief ADXL345连读读取几次取平均值
* @param None
* @retval None
*/
void ADXL345_read_average(short *x, short *y, short *z, uint8_t times)
{
uint8_t 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(5);
}
*x /= times;
*y /= times;
*z /= times;
}
}
3.3.8 计算角度
/**
* @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;
}
4.Adafruit_ADXL345驱动库
本文用于学习如何使用Wire库驱动ADXL345模块。
这是一个专为Arduino设计的库,用于与 ADXL345 三轴加速度传感器进行通信。通过使用这款库,您可以轻松地获取传感器的数据,并在您的项目中实现各种有趣的功能。
Adafruit_ADXL345 库提供了易于使用的 API,可以让您在 Arduino 中快速地集成 ADXL345 传感器并处理数据。这个库支持 I2C 和 SPI 两种通信协议,可以根据您的硬件需求选择合适的接口。
4.1 特点
Adafruit_ADXL345 库具有以下特点:
- 支持 I2C 和 SPI 通信协议
- 高度封装的 API,简化了与 ADXL345 的交互
- 可配置传感器的工作模式和范围,满足不同应用场景的需求
- 提供丰富的示例代码,帮助您快速上手
4.2 开始使用
要开始使用 Adafruit_ADXL345 库,请按照以下步骤操作:
- 下载并安装 Arduino IDE(vscode platformIO同理)。
- 在 Arduino IDE 的 "Sketch" 菜单中选择 "Include Library" > "Manage Libraries...",然后搜索 "Adafruit_ADXL345" 并安装。
- platformIO需搜索安装 "Adafruit Unified Sensor"及"Adafruit Buslo" Library,这是 Adafruit_ADXL345 库的基础框架。Arduino IDE会提醒安装依赖库。
- 将 ADXL345 传感器连接到 Arduino 板子,并根据所选通信协议接通 I2C 或 SPI 引脚。
- 打开 Arduino IDE 中的一个示例程序(例如 "BasicRead"),修改其中的相关参数并上传至 Arduino 板子。
现在,您已经成功集成了 ADXL345 传感器并开始读取数据!