MAX3010X初始化部分
byte ledBrightness = 60; //Options: 0=Off to 255=50mA
byte sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32
byte ledMode = 2; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
byte sampleRate = 100; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
int pulseWidth = 411; //Options: 69, 118, 215, 411
int adcRange = 4096; //Options: 2048, 4096, 8192, 16384
particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings
这里开始设置MAX3010X的各种参数,对其进行初始化。直接看函数原型吧
MAX30105::setup
在H文件中的定义如下
void setup(byte powerLevel = 0x1F, byte sampleAverage = 4, byte ledMode = 3, int sampleRate = 400, int pulseWidth = 411, int adcRange = 4096);
也都是可选参数
CPP文件中的实现如下
void MAX30105::setup(byte powerLevel, byte sampleAverage, byte ledMode, int sampleRate, int pulseWidth, int adcRange) {
softReset(); //Reset all configuration, threshold, and data registers to POR values
//FIFO Configuration
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//The chip will average multiple samples of same type together if you wish
if (sampleAverage == 1) setFIFOAverage(MAX30105_SAMPLEAVG_1); //No averaging per FIFO record
else if (sampleAverage == 2) setFIFOAverage(MAX30105_SAMPLEAVG_2);
else if (sampleAverage == 4) setFIFOAverage(MAX30105_SAMPLEAVG_4);
else if (sampleAverage == 8) setFIFOAverage(MAX30105_SAMPLEAVG_8);
else if (sampleAverage == 16) setFIFOAverage(MAX30105_SAMPLEAVG_16);
else if (sampleAverage == 32) setFIFOAverage(MAX30105_SAMPLEAVG_32);
else setFIFOAverage(MAX30105_SAMPLEAVG_4);
//setFIFOAlmostFull(2); //Set to 30 samples to trigger an 'Almost Full' interrupt
enableFIFORollover(); //Allow FIFO to wrap/roll over
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//Mode Configuration
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (ledMode == 3) setLEDMode(MAX30105_MODE_MULTILED); //Watch all three LED channels
else if (ledMode == 2) setLEDMode(MAX30105_MODE_REDIRONLY); //Red and IR
else setLEDMode(MAX30105_MODE_REDONLY); //Red only
activeLEDs = ledMode; //Used to control how many bytes to read from FIFO buffer
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//Particle Sensing Configuration
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if(adcRange < 4096) setADCRange(MAX30105_ADCRANGE_2048); //7.81pA per LSB
else if(adcRange < 8192) setADCRange(MAX30105_ADCRANGE_4096); //15.63pA per LSB
else if(adcRange < 16384) setADCRange(MAX30105_ADCRANGE_8192); //31.25pA per LSB
else if(adcRange == 16384) setADCRange(MAX30105_ADCRANGE_16384); //62.5pA per LSB
else setADCRange(MAX30105_ADCRANGE_2048);
if (sampleRate < 100) setSampleRate(MAX30105_SAMPLERATE_50); //Take 50 samples per second
else if (sampleRate < 200) setSampleRate(MAX30105_SAMPLERATE_100);
else if (sampleRate < 400) setSampleRate(MAX30105_SAMPLERATE_200);
else if (sampleRate < 800) setSampleRate(MAX30105_SAMPLERATE_400);
else if (sampleRate < 1000) setSampleRate(MAX30105_SAMPLERATE_800);
else if (sampleRate < 1600) setSampleRate(MAX30105_SAMPLERATE_1000);
else if (sampleRate < 3200) setSampleRate(MAX30105_SAMPLERATE_1600);
else if (sampleRate == 3200) setSampleRate(MAX30105_SAMPLERATE_3200);
else setSampleRate(MAX30105_SAMPLERATE_50);
//The longer the pulse width the longer range of detection you'll have
//At 69us and 0.4mA it's about 2 inches
//At 411us and 0.4mA it's about 6 inches
if (pulseWidth < 118) setPulseWidth(MAX30105_PULSEWIDTH_69); //Page 26, Gets us 15 bit resolution
else if (pulseWidth < 215) setPulseWidth(MAX30105_PULSEWIDTH_118); //16 bit resolution
else if (pulseWidth < 411) setPulseWidth(MAX30105_PULSEWIDTH_215); //17 bit resolution
else if (pulseWidth == 411) setPulseWidth(MAX30105_PULSEWIDTH_411); //18 bit resolution
else setPulseWidth(MAX30105_PULSEWIDTH_69);
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//LED Pulse Amplitude Configuration
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//Default is 0x1F which gets us 6.4mA
//powerLevel = 0x02, 0.4mA - Presence detection of ~4 inch
//powerLevel = 0x1F, 6.4mA - Presence detection of ~8 inch
//powerLevel = 0x7F, 25.4mA - Presence detection of ~8 inch
//powerLevel = 0xFF, 50.0mA - Presence detection of ~12 inch
setPulseAmplitudeRed(powerLevel);
setPulseAmplitudeIR(powerLevel);
setPulseAmplitudeGreen(powerLevel);
setPulseAmplitudeProximity(powerLevel);
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//Multi-LED Mode Configuration, Enable the reading of the three LEDs
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
enableSlot(1, SLOT_RED_LED);
if (ledMode > 1) enableSlot(2, SLOT_IR_LED);
if (ledMode > 2) enableSlot(3, SLOT_GREEN_LED);
//enableSlot(1, SLOT_RED_PILOT);
//enableSlot(2, SLOT_IR_PILOT);
//enableSlot(3, SLOT_GREEN_PILOT);
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
clearFIFO(); //Reset the FIFO before we begin checking the sensor
}
看起来挺长的,但是都是类似与枚举的if-else,不是很复杂。
复位操作
softReset(); //Reset all configuration, threshold, and data registers to POR values
函数代码如下
void MAX30105::softReset(void) {
bitMask(MAX30105_MODECONFIG, MAX30105_RESET_MASK, MAX30105_RESET);
// Poll for bit to clear, reset is then complete
// Timeout after 100ms
unsigned long startTime = millis();
while (millis() - startTime < 100)
{
uint8_t response = readRegister8(_i2caddr, MAX30105_MODECONFIG);
if ((response & MAX30105_RESET) == 0) break; //We're done!
delay(1); //Let's not over burden the I2C bus
}
}
这个bitMask
函数还是挺有意思的,之前做这类操作的时候没有想过用这种方法。
后面的部分就是读取了esp32启动以来的毫秒数,然后做循环,判断这个位是不是设置成功了。
就是等待100毫秒,看复位成功了没有。
话说这个操作方式确实比较精准,误差不会太大。
void MAX30105::bitMask
这个函数是一个位操作的函数,控制一位或者几位的bit的赋值。
代码如下:
void MAX30105::bitMask(uint8_t reg, uint8_t mask, uint8_t thing)
{
// Grab current register context
uint8_t originalContents = readRegister8(_i2caddr, reg);
// Zero-out the portions of the register we're interested in
originalContents = originalContents & mask;
// Change contents
writeRegister8(_i2caddr, reg, originalContents | thing);
}
操作流程如下:
- 读取该地址的值
- 把读取到的数据和mask做与操作
- 写入第二步得到的数值与thing的或运算的值
这个操作一开始看,感觉有点傻,为啥不直接传输thing,然后在里面进行取反操作,何必多次一举,看了其他调用这个函数的代码,我大概搞清楚了。
这个操作不是单纯的对一位bit进行操作,而是对多位进行操作,打个比方
现在有第0位到第2位的bit是代表一个模式设置,这个模式有101,110,111三种。那么就可以传输一个二进制是1111 1000
的mask,那与读取出来的值进行与操作之后,就把第0位到第2位的数据清零了,这时thing再传递三种模式的其中一种。这样就可以做到任意位数的赋值,这种方式还挺巧妙的。
但是也能感觉到作者在写mark的数据定义的时候挺烦的,一会2进制赋值,一会16进制赋值。
FIFO配置
FIFO就可以想象成一个队列,先进先出,用于缓存数据的。
代码如下
if (sampleAverage == 1) setFIFOAverage(MAX30105_SAMPLEAVG_1); //No averaging per FIFO record
else if (sampleAverage == 2) setFIFOAverage(MAX30105_SAMPLEAVG_2);
else if (sampleAverage == 4) setFIFOAverage(MAX30105_SAMPLEAVG_4);
else if (sampleAverage == 8) setFIFOAverage(MAX30105_SAMPLEAVG_8);
else if (sampleAverage == 16) setFIFOAverage(MAX30105_SAMPLEAVG_16);
else if (sampleAverage == 32) setFIFOAverage(MAX30105_SAMPLEAVG_32);
else setFIFOAverage(MAX30105_SAMPLEAVG_4);
setFIFOAverage
函数中只有bitMask(MAX30105_FIFOCONFIG, MAX30105_SAMPLEAVG_MASK, numberOfSamples);
这一段代码
定义的代码如下
static const uint8_t MAX30105_FIFOCONFIG = 0x08;
static const uint8_t MAX30105_SAMPLEAVG_MASK = (byte)~0b11100000;
static const uint8_t MAX30105_SAMPLEAVG_1 = 0x00;
static const uint8_t MAX30105_SAMPLEAVG_2 = 0x20;
static const uint8_t MAX30105_SAMPLEAVG_4 = 0x40;
static const uint8_t MAX30105_SAMPLEAVG_8 = 0x60;
static const uint8_t MAX30105_SAMPLEAVG_16 = 0x80;
static const uint8_t MAX30105_SAMPLEAVG_32 = 0xA0;
具体表达了什么意思可以看前言里我写的文章里的FIFO配置
章节,我在这里也做了部分引用
地址 功能 B7 B6 B5 B4 B3 B2 B1 B0 R/W 0x08 FIFO配置 SMP_AVE[2] SMP_AVE[1] SMP_AVE[0] FIFO_ROL LOVER_EN FIFO_A_FULL[3] FIFO_A_FULL[2] FIFO_A_FULL[1] FIFO_A_FULL[0] RW SMP_AVE:平均值,为了减少数据吞吐量,通过设置这个寄存器,相邻的样本(在每个单独的通道中)可以在芯片上进行平均和抽取。
SMP_AVE 平均量 000 1(不平均) 001 2 010 4 011 8 100 16 101 32 110 32 111 32 FIFO_ROL LOVER_EN:FIFO被填满之后的控制。如果是0,在你读取之前都不会更新,如果是1,会更新覆盖之前的数据
更新使能
这其实也是FIFO的设置,当设置为1时如果FIFO中的数据满了,那么就会覆盖老的数据,设置为0则不会覆盖。
enableFIFORollover(); //Allow FIFO to wrap/roll over
内部也就是调用了bitMask,代码如下
void MAX30105::enableFIFORollover(void) {
bitMask(MAX30105_FIFOCONFIG, MAX30105_ROLLOVER_MASK, MAX30105_ROLLOVER_ENABLE);
}
同上,可以看前言里的MAX30102分析。
可以看出来,设置的过程是按照功能划分的,更新使能和FIFO配置都是一个寄存器里的内容,却分成了两个部分来写。可读性比较好,但是执行效率就不怎么高了。
LED设置
设置红光和红外光,三种模式,同上,可以看前言里的MAX30102分析。
if (ledMode == 3) setLEDMode(MAX30105_MODE_MULTILED); //Watch all three LED channels
else if (ledMode == 2) setLEDMode(MAX30105_MODE_REDIRONLY); //Red and IR
else setLEDMode(MAX30105_MODE_REDONLY); //Red only
activeLEDs = ledMode; //Used to control how many bytes to read from FIFO buffer
ADC检测设置
设置ADC的采样范围,具体参数,可以看前言里的MAX30102分析。
if(adcRange < 4096) setADCRange(MAX30105_ADCRANGE_2048); //7.81pA per LSB
else if(adcRange < 8192) setADCRange(MAX30105_ADCRANGE_4096); //15.63pA per LSB
else if(adcRange < 16384) setADCRange(MAX30105_ADCRANGE_8192); //31.25pA per LSB
else if(adcRange == 16384) setADCRange(MAX30105_ADCRANGE_16384); //62.5pA per LSB
else setADCRange(MAX30105_ADCRANGE_2048);
SpO2采样率控制
采样率和脉冲宽度是相关的,因为采样率设置了脉冲宽度时间的上限。如果用户选择的采样率对于所选LED_PW设置来说太高,则将尽可能高的采样率编程到寄存器中。具体参数,可以看前言里的MAX30102分析。
if (sampleRate < 100) setSampleRate(MAX30105_SAMPLERATE_50); //Take 50 samples per second
else if (sampleRate < 200) setSampleRate(MAX30105_SAMPLERATE_100);
else if (sampleRate < 400) setSampleRate(MAX30105_SAMPLERATE_200);
else if (sampleRate < 800) setSampleRate(MAX30105_SAMPLERATE_400);
else if (sampleRate < 1000) setSampleRate(MAX30105_SAMPLERATE_800);
else if (sampleRate < 1600) setSampleRate(MAX30105_SAMPLERATE_1000);
else if (sampleRate < 3200) setSampleRate(MAX30105_SAMPLERATE_1600);
else if (sampleRate == 3200) setSampleRate(MAX30105_SAMPLERATE_3200);
else setSampleRate(MAX30105_SAMPLERATE_50);
设置LED脉宽控制和ADC分辨率
这些位设置LED脉冲宽度(IR和Red具有相同的脉冲宽度),因此间接设置每个样本中ADC的积分时间。ADC分辨率与积分时间直接相关。具体参数,可以看前言里的MAX30102分析。
if (pulseWidth < 118) setPulseWidth(MAX30105_PULSEWIDTH_69); //Page 26, Gets us 15 bit resolution
else if (pulseWidth < 215) setPulseWidth(MAX30105_PULSEWIDTH_118); //16 bit resolution
else if (pulseWidth < 411) setPulseWidth(MAX30105_PULSEWIDTH_215); //17 bit resolution
else if (pulseWidth == 411) setPulseWidth(MAX30105_PULSEWIDTH_411); //18 bit resolution
else setPulseWidth(MAX30105_PULSEWIDTH_69);
LED脉冲宽度设置
设置脉冲宽度,具体参数,可以看前言里的MAX30102分析。
setPulseAmplitudeRed(powerLevel);
setPulseAmplitudeIR(powerLevel);
非MAX30102有效寄存器
这两个设置在MAX30102中是无效的,因为数据手册中这个地址的寄存器并没有分配功能,但是因为MAX30105是向下兼容的,所以MAX30102使用也不会出问题。
setPulseAmplitudeGreen(powerLevel);
setPulseAmplitudeProximity(powerLevel);
多LED模式控制
enableSlot(1, SLOT_RED_LED);
if (ledMode > 1) enableSlot(2, SLOT_IR_LED);
if (ledMode > 2) enableSlot(3, SLOT_GREEN_LED);
void MAX30105::enableSlot(uint8_t slotNumber, uint8_t device) {
uint8_t originalContents;
switch (slotNumber) {
case (1):
bitMask(MAX30105_MULTILEDCONFIG1, MAX30105_SLOT1_MASK, device);
break;
case (2):
bitMask(MAX30105_MULTILEDCONFIG1, MAX30105_SLOT2_MASK, device << 4);
break;
case (3):
bitMask(MAX30105_MULTILEDCONFIG2, MAX30105_SLOT3_MASK, device);
break;
case (4):
bitMask(MAX30105_MULTILEDCONFIG2, MAX30105_SLOT4_MASK, device << 4);
break;
default:
//Shouldn't be here!
break;
}
}
如果是MAX30102最大只可以设置到2。
清除FIFO
具体参数,可以看前言里的MAX30102分析。
clearFIFO(); //Reset the FIFO before we begin checking the sensor