疫情的时候买的血氧传感器MAX30102,一直吃灰没整起来, 某次整理元件发现,不想浪费了,就找了找库存元件,打了个板子 原理介绍MAX30102 脉搏血氧计和心率传感器如何工作?MAX30102 或任何光学脉搏血氧计和心率传感器由一对高强度 LED(红色和红外,波长不同)和一个光电探测器组成。这些 LED 的波长分别为 660nm 和 880nm。 MAX30102 的工作原理是将两束光照射到手指或耳垂(或者基本上是皮肤不太厚的任何地方,因此两束光可以轻松穿透组织),并使用光电探测器测量反射光量。这种通过光检测脉搏的方法称为光电体积描记法。 MAX30102的工作可以分为两部分:心率测量和脉搏血氧饱和度(测量血液中的含氧量)。 心率测量动脉血中的氧合血红蛋白(HbO2)具有吸收红外光的特性。血液越红(血红蛋白越高),吸收的红外光就越多。当血液随着每次心跳泵入手指时,反射光的量会发生变化,从而在光电探测器的输出端产生变化的波形。当您继续照射光线并获取光电探测器读数时,您很快就会开始获得心率 (HR) 脉搏读数。 脉搏血氧仪脉搏血氧测定法的原理是,吸收的红光和红外光的量根据血液中的氧含量而变化。下图是含氧血红蛋白(HbO2)和脱氧血红蛋白(Hb)的吸收光谱。 从图中可以看出,脱氧血液吸收更多红光 (660nm),而含氧血液吸收更多红外光 (880nm)。通过测量光电探测器接收到的红外光和红光的比率,可以计算出血液中的氧含量 (SpO2)。
简单来说就是血液中的血红蛋白回吸收光线,影响光线反射.光线照射手指,手指中血管随着心跳泵入血量不同导致反射回传感器的光量变化,产生跟随心率一块变化的波形,分析波形就能得到心率,手机摄像头可以通过软件测心率也是同样的原理. MAX30102集成红光+红外光LED+光线采样传感器,玻璃密封,同系列还有MAX30100和MAX30105,30100没有玻璃盖密封,更容易被污染,30105增加绿光LED,使用范围更广,也更贵,综合来说30102更具有性价比. 硬件原理 硬件整体没啥复杂的,Typec输入,TP4054锂电充电,XC6206分别提供3.3V和1.8V工作电压,开关电路保证关机下的低功耗(约为0),ADC采集电池电压显示电量 ,传感器I2C接口通信采用轮询采数,通过库计算心率和血样.
这里介绍下开关机电路,这个电路相对拨动开关更复杂,需要浪费一个IO,但是可以复用开关机和正常按键操作,而且不像拨动开关一样易坏,可以实现自动关机,关机后0功耗,核心就是Q1 PMOS,关机状态下Q2基极低电平,截止,无功耗,Q1 G极高电平,截止,整体电路无功耗.开机时按下SW1,Q1 G极拉低,导通,后续电路供电正常工作,单片机IO拉高Q2基极,Q2导通,持续拉低Q1 G极,保证SW1松手后Q1继续导通,此时SW1又可作为正常按键工作,通过D3检测SW1按下状态,实现单击/双击/长按等状态检测实现功能切换开关机.
整机原理图
打板原理图分开画,最后拼版打样,此处说个小技巧,拼版时可以打排孔,PCB厚度选1.2或者更薄更方便分割,排孔间隙铺铜或者走线抱持电气连接,更容易过检,这里只针对这种结构复杂体积小的电路,单独打板要好几单太麻烦,对于单纯为了拼版而拼版的强烈反对. 不建议拼版 不建议拼版 不建议拼版
分割,本来设计的时1.2的板厚,结果没券了,只剩一张彩券,只能打1.6了,随便划拉两刀掰开打磨一下,顶板开槽,可以分割后作为屏幕保护框 测试阶段,使用了排针和排母连接,方便拆卸,右侧传感器*2,还有一个绿色PCB,代表曾经失败过一版... 失败原因比较逗逼,下载正常,无限重启,不开启I2C就正常,开启I2C就无限重启,但是下载后又能直接启动,只要重新上电就无限重启 重新打板还是无限重启,最后发现是因为没有接电池,4054供电不足,接上电池就正常工作了
然后又遇到取数异常,心率计算结果不准确,算不出血样,最后替换传感器正常,淘宝买的二手传感器坑壁 传感器焊接,为了降低整体厚度直接把传感器焊接到侧面了,学习苹果开创胶粘后盖,主打一个难为人
测试供电,骚紫色充电指示灯
成品展示整体体积较小,和可乐瓶盖直径差不多,厚度微厚,主要是手里的铜柱就这么高,懒得打磨,不然可以再薄2mm 传感器使用705侧面密封,防止沾水腐蚀 测试效果,没啥UI,随便画了一下,心型本来是想按实际心率闪烁,最终落地显示手指效果,手指放上就显示,手指移开就不显示,HR是心率,O2是血氧 双击按钮切换显示内容,默认血氧数据,切换后显示心率波形,主要是开发阶段看数据状态的,长按关机
软件篇整体使用arduino开发,偷懒直接用现成库,省的搞HAL来回折腾还得一直库 代码配合Baidu Comate开发,实际主要用了自动注释(公司要求保持AI辅助开发日用量...) 画电池 - /**
- * [url=home.php?mod=space&uid=650075]@brief[/url] 初始化电池显示
- *
- * 该函数用于初始化电池显示的界面。
- * 它清除指定的矩形区域,并绘制一个矩形框和一个矩形外框。
- */
- void initBat()
- {
- clearBox(58, 8, 6, 24);
- u8g2.drawBox(59, 8, 4, 2);
- u8g2.drawFrame(58, 10, 6, 22);
- }
- /**
- * @brief 刷新电池状态
- *
- */
- void refreshBat(bool forceRefresh)
- {
- static uint8_t old = 101;
- if(forceRefresh){
- old = 101;
- }
- // 电量检查
- //(4.2/2)/(3.3/1024)=652
- // 既adc=652时4.2v满电
- uint32_t vBat, vMax, vMin, vSum;
- vSum = vBat = vMax = vMin = analogRead(PB2);
- for (int i = 0; i < 9; i++)
- {
- vBat = analogRead(PB2);
- vSum += vBat;
- if (vBat > vMax)
- vMax = vBat;
- if (vBat < vMin)
- vMin = vBat;
- }
- vBat = ((vSum - vMax - vMin) >> 3) * 100 / 652;
- if (vBat > 100)
- {
- vBat = 100;
- }
- // float f = 20.0 / 100 * vBat;
- // vBat = (uint8_t)(ceil(f));
- vBat = vBat / 5 + (vBat % 5 > 0 ? 1 : 0);
- if (vBat != old)
- {
- old = vBat;
- clearBox(59, 11, 4, 20 - vBat);
- // 高度 20
- u8g2.drawBox(59, 11 + 20 - vBat, 4, vBat);
- u8g2.sendBuffer();
- }
- }
复制代码
画心型
没有使用图片取模,直接线条画的 - /**
- * @brief 刷新心形图案
- *
- * 根据给定的显示标志,刷新心形图案的显示或清除。
- *
- * @param show 是否显示心形图案,true 为显示,false 为清除
- */
- void refreshHeart(bool show)
- {
- static bool old = false;
- if (show != old)
- {
- old = show;
- if (show)
- {
- // 58 59 60 61 62 63
- u8g2.drawPixel(59, 0);
- u8g2.drawPixel(62, 0);
- u8g2.drawLine(58, 1, 63, 1);
- u8g2.drawLine(58, 2, 63, 2);
- u8g2.drawLine(59, 3, 62, 3);
- u8g2.drawLine(60, 4, 61, 4);
- u8g2.sendBuffer();
- }
- else
- {
- clearBox(58, 0, 6, 5);
- }
- }
- }
复制代码
画波形
64像素的屏幕直接从缓冲区取64个值,高度压缩到屏幕高度 - void drawChart()
- {
- u8g2.clearBuffer();
- uint32_t max, min;
- max = min = irBuffer[0];
- for (int i = 1; i < 64; i++)
- {
- if (irBuffer[i] > max)
- max = irBuffer[i];
- if (irBuffer[i] < min)
- min = irBuffer[i];
- }
- uint8_t step = (max - min) / 32;
- for (int i = 0; i < 64; i++)
- {
- u8g2.drawPixel(i, (irBuffer[i] - min) / step);
- }
- u8g2.sendBuffer();
- }
复制代码
启用传感器
传感器设置较多,没有仔细研究,主要就是LED亮度,采样频率,LED模式(红/红外/绿--max30105支持绿光)等 - byte ledBrightness = 150; //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 = 200; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
- int pulseWidth = 411; //Options: 69, 118, 215, 411
- int adcRange = 16384; //Options: 2048, 4096, 8192, 16384
- particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); // Configure sensor with these settings
复制代码
从传感器采集到红光和红外光的强度数值(传感器自动过滤环境光等干扰因素,但是无法完全屏蔽,所以使用时应尽量保持稳定,实际手握持强度无法保持高度稳定,会影响测量效果,这也是成品都用夹子外形的原因) - isPress = true;
- // take 25 sets of samples before calculating the heart rate.
- // for (uint16_t i = BUFFER_SIZE - FreqS * 2; i < BUFFER_SIZE; i++)
- for (uint16_t i = 0; i < BUFFER_SIZE; i++)
- {
- while (particleSensor.available() == false) // do we have new data?
- particleSensor.check(); // Check the sensor for new data
- redBuffer[i] = particleSensor.getFIFORed();
- irBuffer[i] = particleSensor.getFIFOIR();
- particleSensor.nextSample(); // We're finished with this sample so move to next sample
- button.tick();
- if (irBuffer[BUFFER_SIZE - 1] < 25000)
- isPress = false;
- }
- time = millis() - time;
- #ifdef ENABLE_OLED
- refreshHeart(isPress);
- #endif
- for (uint16_t i = 0; i < BUFFER_SIZE; i++)
- // for (uint16_t i = BUFFER_SIZE - FreqS * 2; i < BUFFER_SIZE; i++)
- {
- // send samples and calculation result to terminal program through UART
- log_debug("red:%d,ir:%d", redBuffer[i], irBuffer[i]);
- }
- if (isPress)
- maxim_heart_rate_and_oxygen_saturation(irBuffer, BUFFER_SIZE, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
复制代码
实际上在研究的时候看到gitee上有个开源的Python上位机,也测试了一下,直接串口输出采样数据 - void loop()
- {
- button.tick();
- while (particleSensor.available() == false) //do we have new data?
- particleSensor.check(); //Check the sensor for new data
- particleSensor.nextSample(); //We're finished with this sample so move to next sample
- log_debug("[DATA] R:%d,IR:%d,time:%d",particleSensor.getRed(),particleSensor.getIR(),millis());
- }
复制代码
上位机实时分析
相关文件立创eda开源还得审核,也得编辑一波,懒得整了,直接把PCB文件上传gitee了,有需要的导入eda或者直接用geber上传打样就行
|