利用现成元器件设计低成本脉搏血氧仪

作者:Stephen Evanczuk

投稿人:DigiKey 北美编辑

脉搏血氧仪可测量外周血氧饱和度 (SpO2),该指标反映了心肺系统向身体提供富氧血液的效率。运动员使用 SpO2 测量值来衡量他们在锻炼中的努力程度,但这种测量在新冠肺炎流行期间变得更加重要。医护人员很关注 SpO2 的下降,因为它是引起新冠肺炎的 SARS-CoV-2 病毒对肺组织造成损伤的预警信号。

对于被要求在家中隔离的症状轻微的感染者来说,如果有一台低成本脉搏血氧仪随时可以使用,那么将有助于判断病情并获得及时就医所需的警告信息。

本文简要讨论了新冠肺炎的症状和监测 SpO2 的需求。然后向开发人员展示如何使用 Microchip Technology 数字信号控制器 (DSC) 和一些附加器件,设计一个低成本的脉搏血氧仪,从而针对新冠肺炎感染后期的症状,为家庭用户提供预警。

新冠肺炎和测量血氧饱和度的需求

由于 SARS-CoV-2 病毒的破坏作用,新冠肺炎会表现出各种症状。对于医护人员来说,一个特别令人担忧的症状与肺组织损伤有关,它将导致呼吸系统受损,摄氧量减少。尽管医生会逐一使用胸部 X 光检查和计算机断层 (CT) 扫描来确认这一阶段的新冠肺炎,但他们往往会使用 SpO2 测量值作为早期指标。

通过由患者动脉抽取样本并分析血气水平,可直接确定动脉血氧饱和度 (SaO2),而 SpO2 测量是一种非侵入式的替代测量方法。虽然某些情况下可能需要直接测量动脉血气,但 SpO2 测量已被证明可以提供可靠的 SaO2 估计值。也许最重要的是,在家即可使用光学脉搏血氧仪进行测量,而测量结果跟临床环境一样可靠。

光学脉搏血氧仪利用脱氧血红蛋白 (Hb) 和氧合血红蛋白 (HbO2) 的光吸收差异来测量 SpO2。在富含氧气的肺内,红细胞中的血红蛋白最多可与四个氧分子迅速形成可逆性结合,从而生成 HbO2,该分子对波长为 940 nm 的光吸收率高于波长为 660 nm 的光(图 1)。

氧合血红蛋白 (HbO2) 与脱氧血红蛋白 (Hb) 的吸收光谱差异图图 1:脉搏血氧仪利用了氧合血红蛋白 (HbO2) 与脱氧血红蛋白 (Hb) 吸收光谱之间的差异。(图片来源:维基百科)

当携带 HbO2 的红细胞通过外周血管时,血氧分压(混合气体中单一气体成分的压力)较低,血红蛋白对氧的亲和力下降,HbO2 开始释放氧分子,最终成为 Hb。在这种脱氧状态下,该分子的吸收光谱发生改变,对波长为 660 nm 的光吸收率高于波长为 940 nm 的光。

因为 HbO2 在血氧分压较低时会变为 Hb,所以 SpO2 可以用简单的公式来确定:

SpO2 = HbO2 / (HbO2 + Hb)

血液中 Hb 和 HbO2 的相对浓度则可通过测量对波长为 660 nm 和 940 nm 的光吸收情况来确定。

脉搏血氧仪利用血氧分压、血红蛋白氧结合状态和光吸收差异之间的关系,可提供可靠的 SpO2 测量。

典型脉搏血氧仪的主要子系统

典型脉搏血氧仪设计包括三个主要子系统:

  • 光传输子系统,包括模拟开关和驱动器,以及可发出红光(波长为 660 nm)和红外线 (IR)(波长为 950 nm)的发光二极管 (LED)。一些系统还包括绿光(波长为 530 nm)光源,可配合光电容积描记法 (PPG) 使用,通过监测皮肤血管容积的变化来确定心率。
  • 光检测子系统,包括光电二极管、信号调节链和模数转换器 (ADC)。
  • DSC 或微控制器,用于协调光传输和光检测子系统,并由测得的数据计算 SpO2

尽管任何脉搏血氧仪中都存在这些基本子系统,但它们的实现方式可能千差万别。对于透射式脉搏血氧仪,光电二极管与 LED 分别放置在用户手指或耳垂的两侧。常见的指夹装置一侧装有红光、红外光 和绿光(可选)LED,另一侧则是光电二极管。对于反射式脉搏血氧仪,光电二极管和 LED 均放置在皮肤的同一侧,两者之间需设置光学屏障以减少伪影。例如,OSRAM 的 SFH7060 是一款即用型反射式测量器件,在单个封装中包含 LED 和光电二极管,尺寸仅为 7.2 x 2.5 x 0.9 mm。

无论是将这些采用光学封装结构的器件用于透射式测量,还是反射式测量,设计人员都只需要添加相对较少的元器件就可实现低成本的脉搏血氧仪设计,并且能够为家庭用户提供相关信息,针对是否需要专业医护人员进一步评估提出建议。在基于 Microchip Technology 的 DSPIC33FJ128GP802 DSC 构建的设计示例中,使用微控制器的集成外设来控制红光和红外光 LED 对皮肤的照射,然后将调节后的光电二极管输出信号数字化(图 2)。

Microchip 的典型脉搏血氧仪设计示意图图 2:在典型脉搏血氧仪设计中,往往集成了 LED 照射子系统和光电二极管信号处理子系统,以及控制照射时序和数据采集的微控制器。(图片来源:Microchip Technology)

无论光源类型如何,脉搏血氧仪设计通常依靠具有宽光谱响应范围的单个光电二极管,以捕获透射或反射信号。为了确保接收到的信号仅对应于红光或红外光波长,硬件或软件控制逻辑在给定时间内仅开启红光或红外光源,两个光源交替发光以完成测量序列。

实现低成本脉搏血氧仪硬件设计

在此设计中,DSC 使用外部 Microchip Technology 的 MCP4728 数模转换器 (DAC) 将单独的 MBT2222 晶体管置于所需电平,从而以所需的亮度驱动各个 LED。为了对每个 LED 的“开启”序列进行精确定时,DSC 使用其中两个脉冲宽度调制 (PWM) 输出来控制 Analog DevicesADG884 模拟开关(图 3)。

模拟开关产生红光和红外光 LED 的驱动电流示意图图 3:模拟开关由数字控制器红光和红外光通道的交替信号驱动,用于产生红光和红外光 LED 的驱动电流。(图片来源:Microchip Technology)

为了处理光电二极管输出,单个 Microchip Technology 的 MCP6002 器件可提供一对运算放大器,借以实现基本两级信号调节链。在这里,第一级使用 MCP6002 的一个运算放大器,将其配置为跨阻放大器,以便将光电二极管的电流输出转换为电压信号。在经过高通滤波器以降低噪声后,MCP6002 中的第二个运算放大器提供了增益和直流偏移调整,以在 DSC 集成的 ADC 全范围内优化调节后信号的摆动(图 4)。

两级信号链调节光电二极管输出进行信号传送示意图图 4:两级信号链调节光电二极管输出,以实现到数字控制器的集成 ADC 的信号传送。(图片来源:Microchip Technology)

工作时,DSC 使用其 PWM 输出和 ADC 输入,对调节后光电二极管输出信号的 LED 照射和 ADC 数字化进行同步。这样,每个交替的红光和红外光照射周期都会配合信号采集和转换。在两个 LED 都关闭时,还需采集一个 ADC 样本以测量环境光,以便用于优化 LED 亮度和 SpO2 测量。最后得到的是一个精确控制的事件序列,协调 LED 照射与 ADC 数字化,以捕获表示 Hb 的红光波长结果,再捕获环境光,最终捕获表示 HbO2 的红外光波长结果(图 5)。

Microchip 的低成本脉搏血氧仪功能示意图图 5:低成本脉搏血氧仪的功能依赖于数字信号控制器的管理能力,控制器需要对照射时序和数据捕获进行精确定时,才能收集测量值以确定 SpO2。(图片来源:Microchip Technology)

实现中断驱动软件解决方案

Microchip 提供的脉搏血氧仪固件包带有一个样例程序,演示了如何使用 DSC 执行这些照射控制和数据转换序列。在这里,该程序使用一对 DSC 定时器(Timer2 和 Timer3)来实现中断驱动方法,这两个定时器分别用于红外光 LED 和红光 LED 各自的“开启”序列。这样,每个定时器又为 DSC 的两个输出比较 (OC) 模块(OC1 和 OC2)提供时基,这两个模块分别用于控制红外光LED 和红光 LED 的模拟开关。

如清单 1 所示,该软件首先初始化 Timer2 和 Timer3,以设置所需的照射周期时长并启用中断。作为其初始化序列的一部分,OC1 和 OC2 模块使用 DSC 的可重映射引脚 (RP) 功能连接到各自的输出引脚。然后,初始化序列设置照射占空比,并选择关联定时器用作时基。

副本
    //*********************************************************************************************************
    // Initialize Timer 2 - IR light
    //*********************************************************************************************************
    T2CON = 0x0020;                    // Stop 16-bit Timer2, 1:64(40MhzFosc) Prescale, Internal clock (Fosc/2)
    TMR2 = 0x00;                    // Clear timer register
    PR2 = 1250;                        // Load the period value, OCxRS <= PRx, 4ms period = (1/(Fosc/2))*1000*64*PR2 = (1/(40000000/2))*1000*64*1250
    IPC1bits.T2IP = 2;                // Set Timer2 Interrupt Priority Level
    IFS0bits.T2IF = 0;                // Clear Timer2 Interrupt Flag
    IEC0bits.T2IE = 1;                // Enable Timer2 Interrupt
 
    //*********************************************************************************************************
    // Initialize Timer 3 - Red light
    //*********************************************************************************************************
    T3CON = 0x0020;                    // Stop 16-bit Timer3, 1:64(40MhzFosc) Prescale, Internal clock (Fosc/2)
    TMR3 = 0x00;                    // Clear timer register
    PR3 = 1250;                        // Load the period value, OCxRS <= PRx, 4ms period = (1/(Fosc/2))*1000*64*PR2 = (1/(40000000/2))*1000*64*1250
    IPC2bits.T3IP = 2;                // Set Timer3 Interrupt Priority Level
    IFS0bits.T3IF = 0;                // Clear Timer3 Interrupt Flag
    IEC0bits.T3IE = 1;                // Enable Timer3 Interrupt
 
    //*********************************************************************************************************
    // Initialize Output Compare 1 module in Continuous Pulse mode, OC1 controls IR LED switch
    //*********************************************************************************************************
    RPOR6bits.RP13R = 0b10010;        // RP13/RB13 tied to OC1 (IR)
    OC1CONbits.OCM = 0b000;         // Disable Output Compare 1 Module
    OC1R = 0;                         // Write the duty cycle for the first PWM pulse, 24=8MHzFosc(50us), 30=40MHzFosc(50us), 600=40MHzFosc(1ms)
    OC1RS = duty_cycle;             // Write the duty cycle for the second PWM pulse, OCxRS <= PRx, 499=8MHzFosc(1ms), 623=40MHzFosc(1ms), 1246=40MHzFoc,2msPeriod, 4984=40MHzFoc,8msPeriod, 280=450us D/C@40MHzFoc,2msPeriod,switch
    OC1CONbits.OCTSEL = 0;             // Select Timer 2 as output compare time base
 
    //*********************************************************************************************************
    // Initialize Output Compare 2 module in Continuous Pulse mode, OC2 controls Red LED switch
    //*********************************************************************************************************
    RPOR6bits.RP12R = 0b10011;        // RP12/RB12 tied to OC2 (Red)
    OC2CONbits.OCM = 0b000;         // Disable Output Compare 2 Module
    OC2R = 0;                         // Write the duty cycle for the first PWM pulse, 24=8MHzFosc, 30=40MHzFosc, 600=40MHzFosc(1ms)
    OC2RS = duty_cycle;             // Write the duty cycle for the second PWM pulse, OCxRS <= PRx, 499=8MHzFosc(1ms), 623=40MHzFosc(1ms), 1246=40MHzFoc,2msPeriod, 4984=40MHzFoc,8msPeriod, 280=450us D/C@40MHzFoc,2msPeriod,switch
    OC2CONbits.OCTSEL = 1;             // Select Timer 3 as output compare time base

清单 1:在 Microchip Technology 提供的示例代码包中,主例程使用一个简短的初始化序列来设置数字信号控制器的定时器和输出比较模块,这是这种低成本脉搏血氧仪解决方案的核心。(代码来源:Microchip Technology)

这种方法利用了 DSC 架构将每个定时器中断与特定的中断服务例程 (ISR) 入口点相关联的特性。例如,当红光 LED 通道的 Timer3 中断发生时,DSC 在 _T3Interrupt 入口点执行相应功能。因此,当红光 LED 的 Timer3 到期时,会发生以下两个相应的硬件和软件事件:

  • OC2 生成一个连续脉冲并发送至模拟开关,开启红光 LED
  • DSC 开始执行 _T3Interrupt ISR(清单 2)
副本
void __attribute__((__interrupt__, no_auto_psv)) _T3Interrupt(void)        //Read Red DC & AC signals from AN0 & AN1
{
    int delay;
    unsigned char i;
 
    Read_ADC_Red = 1;
    CH0_ADRES_Red_sum = 0;
    CH1_ADRES_Red_sum = 0;
 
    for (delay=0; delay<200; delay++);    //2000=delayed 256us before read ADC
 
//    LATBbits.LATB14 = 1;            // for debugging
 
    for (i=0; i<oversampling_number; i++)
    {
        //Acquires Red-DC from Channel0 (AN0)
        AD1CHS0bits.CH0SA = 0x00;        // Select AN0
        AD1CON1bits.SAMP = 1;            // Begin sampling
        while(!AD1CON1bits.DONE);        // Waiting for ADC completed
        AD1CON1bits.DONE = 0;            // Clear conversion done status bit
        CH0_ADRES_Red_sum = CH0_ADRES_Red_sum + ADC1BUF0;    // Read ADC result
 
        //Acquires Red-AC from Channel1 (AN1)
        AD1CHS0bits.CH0SA = 0x01;        // Select AN1
        AD1CON1bits.SAMP = 1;            // Begin sampling
        while(!AD1CON1bits.DONE);        // Waiting for ADC completed
        AD1CON1bits.DONE = 0;            // Clear conversion done status bit
        CH1_ADRES_Red_sum = CH1_ADRES_Red_sum + ADC1BUF0;    // Read ADC result
    }
 
    CH0_ADRES_Red = CH0_ADRES_Red_sum / oversampling_number;
    FIR_input_Red[0] = CH1_ADRES_Red_sum / oversampling_number;
 
#ifdef Sleep_Enabled
    if (CH0_ADRES_Red<=74 && CH1_ADRES_Red>=4000)    //if spo2 probe is not connected, 74=60mV, 4000=3.2V
    {
        goto_sleep = 1;
    }
    else if (CH0_ADRES_Red > Finger_Present_Threshold)    //if no finger present then goto sleep
    {
        goto_sleep = 1;
    }
    else
#endif
    {
//        LATBbits.LATB14 = 0;            // for debugging
        for (delay=0; delay<500; delay++);    //1000=delayed 256us before read ADC
//        LATBbits.LATB14 = 1;            // for debugging
 
        //Acquires Red-DC baseline from Channel0 (AN0)
        AD1CHS0bits.CH0SA = 0x00;        // Select AN0
        AD1CON1bits.SAMP = 1;            // Begin sampling
        while(!AD1CON1bits.DONE);        // Waiting for ADC completed
        AD1CON1bits.DONE = 0;            // Clear conversion done status bit
        Baseline_ambient = ADC1BUF0;
 
        Baseline_Upper_Limit = Baseline_ambient + DCVppHigh;
        Baseline_Lower_Limit = Baseline_ambient + DCVppLow;
 
        Meter_State = Calibrate_Red();
    }
 
//    LATBbits.LATB14 = 0;            // for debugging
 
    OC2RS = duty_cycle;                // Write Duty Cycle value for next PWM cycle
    IFS0bits.T3IF = 0;                // Clear Timer3 Interrupt Flag
}

清单 2:Microchip Technology 的示例代码包含有此处所示的 Timer3 ISR,这段代码用于收集红光 LED 照射的测量值和环境光测量值,而 Timer2 ISR 只需要收集红外光 LED 照射的测量值。(代码来源:Microchip Technology)

如清单 2 所示,_T3Interrupt ISR 从 ADC 通道 0 (AN0) 读取红光基线水平 (Red-DC),并从 ADC 通道 1 (AN1) 读取红光动态水平 (Red-AC)。如果开发人员选择将 Sleep_Enabled 的定义纳入应用中,则编译的 ISR 代码会在捕获数据后检查处理器是否应进入休眠状态。Microchip 软件包的默认配置包括 Sleep_Enabled 的 #define,因此如果未连接光学探头或者未检测到用户的手指,则将设置变量 goto_sleep。

检查了此探头状态之后,ISR 会对环境光水平进行采样,并使用此更新值相应地调整基线窗口限值。利用这些调整后的限值,函数 Calibrate_Red() 可增加或减少连接红光 LED 驱动器的 DAC 输出,使亮度保持在 Baseline_Lower_Limit 和 Baseline_Upper_Limit 之间。

T2 定时器中断服务例程使用相同的基本设计模式,但不包括对 sleep_enabled 和环境光水平测量值的检查。

配置了定时器、输出比较和 ISR 后,该示例软件的主例程将执行一个简短的初始化序列,并启动 Timer2 和 Timer3。此时,代码进入主循环,等待接收 ISR 处理的数据。待获得红光和红外光数据后,这些值将由一个数字有限脉冲响应 (FIR) 滤波器进行处理,最后调用例程来计算 SpO2 和心率(清单 3)。

副本
    //********** Enable OC1 & OC2 ouputs for IR & Red LED's on/off switch **********
    OC2CONbits.OCM = 0b101;                // Select the Output Compare 2 mode, Turn on Red LED
    T3CONbits.TON = 1;                    // Start Timer3
 
    for (delay=0; delay<2200; delay++);
 
    OC1CONbits.OCM = 0b101;                // Select the Output Compare 1 mode, Turn on IR LED
    T2CONbits.TON = 1;                    // Start Timer2
 
    goto_sleep = 0;
    first_reading = 0;
    
 
    while (1)
    {
        if (goto_sleep)
        {
 
[lines clipped]
 
                Sleep();                    // Put MCU into sleep
                Nop();
            }
        }
 
        //--------- Main State Machine starts here ---------
        if (RedReady && IRReady)
        {
            RedReady = 0;
            IRReady = 0;
 
//            LATBbits.LATB14 = 1;            //for debugging
 
            FIR(1, &FIR_output_IR[0], &FIR_input_IR[0], &BandpassIRFilter);
            FIR(1, &FIR_output_Red[0], &FIR_input_Red[0], &BandpassRedFilter);
 
            CH1_ADRES_IR = FIR_output_IR[0];
            CH1_ADRES_Red = FIR_output_Red[0];
 
[lines clipped]
 
            if (Detection_Done)
            {
                //Max & Min are all found. Calculate SpO2 & Pulse Rate
                SpO2_Calculation();                //calculate SpO2
                Pulse_Rate_Calculation();        //calculate pulse rate
 
[lines clipped]
 
    }
/*****************************************************************************
 * Function Name: SpO2_Calculation()
 * Specification: Calculate the %SpO2
 *****************************************************************************/
void SpO2_Calculation (void)
{
    double Ratio_temp;
 
    IR_Vpp1 = fabs(IR_Max - IR_Min);
    Red_Vpp1 = fabs(Red_Max - Red_Min);
    IR_Vpp2 = fabs(IR_Max2 - IR_Min2);
    Red_Vpp2 = fabs(Red_Max2 - Red_Min2);
 
    IR_Vpp = (IR_Vpp1 + IR_Vpp2) / 2;
    Red_Vpp = (Red_Vpp1 + Red_Vpp2) / 2;
 
    IR_Vrms = IR_Vpp / sqrt(8);
    Red_Vrms = Red_Vpp / sqrt(8);
 
//    SpO2 = log10(Red_Vrms) / log10(IR_Vrms) * 100;
//    if (SpO2 > 100)
//    {
//        SpO2 = 100;
//    }
 
    // Using lookup table to calculate SpO2
    Ratio = (Red_Vrms/CH0_ADRES_Red) / (IR_Vrms/CH0_ADRES_IR);

清单 3:在 Microchip Technology 示例代码包中,主例程的这段代码展示了该代码如何初始化定时器和输出比较模块并进入无限循环,从而在获得测量值后计算 SpO2 和心率,或在未连接传感器时将处理器置于低功耗休眠模式。(代码来源:Microchip Technology)

对于 SpO2,函数 SpO2_Calculation() 将红光和红外光信号的脉冲幅度 (Vpp) 转换为 Vrms 值。该函数使用这些值生成一个比值,并使用查找表(清单 3 中未显示)将该比值转换为特定的 SpO2 值。通常,该查找表由多次测量经验总结得出。Pulse_Rate_Calculation() 使用测量的峰间计时来确定心率。

SpO2 设计优化选项

虽然本文介绍的设计为低成本脉搏血氧仪提供了一个有效的解决方案,但其他器件可能会提供进一步的优化。例如,开发人员可以使用 Microchip Technology 的 DSPIC33CK64MP102 DSC 集成的运算放大器,则无需添加外部 MCP6002 双运算放大器器件。

然而,在实现这种经过修改的脉搏血氧仪设计时,由于 DSC 有所不同,开发人员需要重写以上软件包的一些关键部分。

例如,不同于 DSPIC33FJ128GP802 DSC 中的 Timer2/Timer3 功能,DSPIC33CK64MP102 DSC 提供了一组多用途定时器模块,因此需要开发人员提供自己的解决方案,以实现本文清单中所述的某些功能。即便如此,工作原理依然不变,开发人员至少可以使用 Microchip Technology 示例软件包所示的设计模式来指导自己的定制软件设计。

结语

血氧饱和度测量可提供重要的呼吸功能指标,在新冠肺炎疫情期间已成为重要的健康管理工具。使用简单的光学方法,脉搏血氧仪提供了可靠的外周血氧饱和度 (SpO2) 估计值,可满足疫情期间对平价健康监测解决方案的特别需求。

如上所述,DSC 与一些基本元器件相结合,为实现低成本的脉搏血氧仪提供了有效的硬件基础,能够提供可靠的 SpO2 测量值,从而针对是否需要寻求进一步医疗护理以诊治不断加重的新冠肺炎感染,为用户提供必要信息。

免责声明:各个作者和/或论坛参与者在本网站发表的观点、看法和意见不代表 DigiKey 的观点、看法和意见,也不代表 DigiKey 官方政策。

关于此作者

Stephen Evanczuk

Stephen Evanczuk 拥有 20 多年的电子行业编辑经验,主题涉猎广泛,涵盖硬件、软件、系统以及包括物联网在内的各种应用。他拥有神经网络领域的神经科学博士学位,曾经从事航空航天领域大规模分布式安全系统和算法加速方法方面的工作。目前,他不是在撰写技术和工程文章,就是在研究深度学习在识别和推荐系统方面的应用。

关于此出版商

DigiKey 北美编辑