esp32驱动max30102的iic初始化篇一
前言
主要介绍的是arduino中SparkFun_MAX3010x_Sensor_Library这个库。
SparkFun_MAX3010x_Sensor_Library链接地址
这个库可以在arduino中直接搜索下载。
主要分析的是SpO2这个部分。examples中是示例,src中是源码。
如果对max30102的初始化过程不清楚,可以看下面这篇文章。
实例代码分析
引用部分
头文件的引用
#include <Wire.h>
#include "MAX30105.h"
#include "spo2_algorithm.h"
虽然这里标注的是#include "MAX30105.h"
,但是MAX30102也可以使用。
#include <Wire.h>
其实是不用引用的,因为在#include "MAX30105.h"
中已经引用过了,这里引用可能是为了可读性。
创建类
MAX30105 particleSensor;
这里没什么好说的,就是创建了一个MAX30105的对象。
iic初始化部分
if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
{
Serial.println(F("MAX30105 was not found. Please check wiring/power."));
while (1);
}
代码的核心部分是particleSensor.begin(Wire, I2C_SPEED_FAST)
。
I2C_SPEED_FAST
:
代表的是iic的速度,下面是它的定义。
#define I2C_SPEED_STANDARD 100000
#define I2C_SPEED_FAST 400000
因为现在购买的一般是黑色的MAX30102的模块,这种模块是MAX30102和电容电阻都在一个面,而且面积小,手指在按上去的时候很容易接触到信号线的触点,所以会干扰到iic信号。一般的解决方法是做绝缘,或者把信号线速率降低。
所以这里更建议设置为I2C_SPEED_STANDARD
。
Wire
:
这里其实就是传递一个TowWire的引用。
MAX30105::begin
再看一下这个函数的原型。
在H文件中的引用是这样的
boolean begin(TwoWire &wirePort = Wire, uint32_t i2cSpeed = I2C_SPEED_STANDARD, uint8_t i2caddr = MAX30105_ADDRESS);
这样写是代表这三个选项都是可选的,也就是说,你不传递任何值,也是可以正确初始化的。
看一下三个参数
- wirePort就是接受了一个Wire类
- i2cSpeed默认是100kHz的速率
- i2caddr是MAX30102的地址,默认是0x57
也就是说如果什么都不传递,i2cSpeed的速率默认是低速的,所以在初始化时,传递了Wire和I2C_SPEED_FAST,把速度设置为高速。
为啥不直接传I2C_SPEED_FAST,还要传个Wire呢?因为i2cSpeed是第二的参数,所以想要赋值第二个参数,你先得赋值第一个参数。
所以如果想让iic运行在低速时,begin是不用传递参数的。
这里的TwoWire只是Wire类的别名,功能上是完全等价的,为什么要用TwoWire呢,其实就是告诉你,如果你的开发板上有两个iic,而你恰好想用第二条,你就可以传递一个Wire1。
begin在C文件中的实现如下
boolean MAX30105::begin(TwoWire &wirePort, uint32_t i2cSpeed, uint8_t i2caddr) {
_i2cPort = &wirePort; //Grab which port the user wants us to use
_i2cPort->begin();
_i2cPort->setClock(i2cSpeed);
_i2caddr = i2caddr;
// Step 1: Initial Communication and Verification
// Check that a MAX30105 is connected
if (readPartID() != MAX_30105_EXPECTEDPARTID) {
// Error -- Part ID read from MAX30105 does not match expected part ID.
// This may mean there is a physical connectivity problem (broken wire, unpowered, etc).
return false;
}
// Populate revision ID
readRevisionID();
return true;
}
前四行代码功能就是开启iic总线
_i2cPort = &wirePort;
在赋值TowWire类_i2cPort->begin();
在初始化iic总线_i2cPort->setClock(i2cSpeed);
在设置iic速率_i2caddr = i2caddr;
在设置iic地址。
进一步深入发现readPartID()
和readRevisionID
其核心是调用了readRegister8
,而读取和写入一般都是成对出现的,写入的函数是writeRegister8
uint8_t MAX30105::readPartID() {
return readRegister8(_i2caddr, MAX30105_PARTID);
}
MAX30105_PARTID:值为0xFF。作用是读取部件id。部件id固定是0x15,所以可以推出来MAX_30105_EXPECTEDPARTID的值是0x15。
void MAX30105::readRevisionID() {
revisionID = readRegister8(_i2caddr, MAX30105_REVISIONID);
}
MAX30105_REVISIONID:值为0xFE。作用是读取版本号。
MAX30105::readRegister8
来看看读写函数
这是一个用iic总线读取8位数据的函数。
看一下函数原型
uint8_t MAX30105::readRegister8(uint8_t address, uint8_t reg) {
_i2cPort->beginTransmission(address);
_i2cPort->write(reg);
_i2cPort->endTransmission(false);
_i2cPort->requestFrom((uint8_t)address, (uint8_t)1); // Request 1 byte
if (_i2cPort->available())
{
return(_i2cPort->read());
}
return (0); //Fail
}
_i2cPort->beginTransmission(address);
的作用是设置传输设备的地址
_i2cPort->write(reg);
的作用是设置要传输的数据,这里代表的就是寄存器地址
_i2cPort->endTransmission(false);
的作用是结束iic传输,并重新发送一个开始信号,释放资源,如果是传输true的话,会返回一个状态码,指示是否传输成功
状态码 | 表示 |
---|---|
0 | 表示传输成功 |
1 | 表示数据量超过了传送缓存的容纳限制 |
2 | 表示在传送地址时收到了NACK(非确认信号) |
3 | 表示在传送数据时收到了NACK |
4 | 表示其他错误 |
_i2cPort->requestFrom((uint8_t)address, (uint8_t)1);
的作用是读取一个字节
_i2cPort->available()
的作用是判断是否有可用的数据供读取
_i2cPort->read()
的作用是把这个数据读取出来
如果读取失败了,就返回0,这也是在使用这个库时最好iic用低速率的原因。当iic总线收到干扰时(没有做绝缘,手指触到到之后干扰到iic总线信号传输,因为人体相当于一个几十到几百uF的电容)就会直接返回0,这时并不能确定时总线受到干扰,信号没回来,还是传输回来的就是0。
这时候看到的数据就是不稳定的。
MAX30105::writeRegister8
介绍完了读函数,写函数就没什么好说的了。看一下源码。
void MAX30105::writeRegister8(uint8_t address, uint8_t reg, uint8_t value) {
_i2cPort->beginTransmission(address);
_i2cPort->write(reg);
_i2cPort->write(value);
_i2cPort->endTransmission();
}
加了一个value,就是需要在reg这个地址的寄存内写入的数据。
不同的是_i2cPort->endTransmission();
里面没有传递参数,这代表着,发送停止信号,结束iic传输。在读函数中,有参数是因为还要执行一遍读取操作,而写函数不需要再读取了。
用户操作部分
Serial.println(F("Attach sensor to finger with rubber band. Press any key to start conversion"));
while (Serial.available() == 0) ; //wait until user presses a key
Serial.read();
这一段没什么好说的,就是初始化成功了,然后让你随便输入个东西,然后继续执行后面的内容
实际开发中用不到这一块代码