使用現成元件設計低成本脈搏血氧儀

作者:Stephen Evanczuk

資料提供者:DigiKey 北美編輯群

脈搏血氧儀可測量末稍血氧飽和度 (SpO2),能反映出心肺系統提供含氧血液到身體的有效性。運動員會使用 SpO2 測量值來衡量運動時的賣力程度,而在 COVID-19 疫情期間,這些測量數據的重要性日益增加。醫護人員會觀察 SpO2 是否降低。這是肺部組織遭受 SARS-CoV-2 病毒入侵受損而引發 COVID-19 的早期徵兆。

若感染者症狀輕微而必須居家隔離,取得低成本的脈搏血氧儀將有助於記錄感染過程,並可預警是否需要進一步的醫療照護。

本文將簡要探討 COVID-19 的症狀,以及 SpO2 監測的需求;然後向開發人員說明如何使用 Microchip Technology 的數位訊號控制器 (DSC) 以及一些額外元件來設計低成本的脈搏血氧儀,以便在出現符合 COVID-19 早期症狀的徵象時向居家使用者發出預警。

COVID-19 和測量血氧飽和度的需求

由於 SARS-CoV-2 病毒的破壞作用,COVID-19 出現的症狀相當多種。對醫護人員來說,令其特別擔憂的症狀與肺部組織受損有關,因為這會導致呼吸系統受損以及攝氧量降低。雖然醫師可使用胸部 X 光和電腦斷層 (CT) 掃描來確認 COVID-19 此階段的發展,但通常也會使用 SpO2 測量值作為預警指標。

對病患動脈抽出的血液檢體進行血液氣體濃度分析,即可直接判定動脈血氧飽和度 (SaO2),但 SpO2 測量可提供非侵入式的替代方法。雖然有些情況可能需要直接測量動脈的血氣濃度,但目前已發現 SpO2 能提供可靠的 SaO2 估計值。或許最重要的是,在家中使用光學脈搏血氧儀就可像在臨床環境一樣可靠地進行測量。

光學脈搏血氧儀會利用去氧血紅素 (Hb) 和氧合血紅素 (HbO2) 在光吸收上的差異來測量 SpO2。血紅素由紅血球攜帶,可在富含氧的肺部與多達四個氧分子快速形成可逆鍵結。在此狀態下會生成 HbO2,而此分子對 940 nm 波長光線的吸收量,比 660 nm 波長光線更多 (圖 1)。

氧合 (HbO2) 和去氧 (Hb) 血紅素吸收光譜的差異圖圖 1:脈搏血氧儀利用氧合 (HbO2) 和去氧 (Hb) 血紅素吸收光譜的差異進行測量。(圖片來源:Wikipedia)

當攜帶 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 的第二個運算放大器提供必要的增益和 DC 偏移調整,以在 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:此處所示的 Timer3 ISR 隨附於 Microchip Technology 範例程式碼套件內,用於採集紅光 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 的相關差異。

例如,DSPIC33CK64MP102 DSC 提供一組多用途計時器模組,而非 DSPIC33FJ128GP802 DSC 中的 Timer2/Timer3 功能,因此開發人員需要對本文清單中所述的某些功能提供自己的解決方案。即便如此,操作原理都是一樣的,開發人員至少可以使用 Microchip Technology 範例軟體套裝所示的設計模式,引導建立客製化軟體設計。

結論

血氧飽和度測量值是呼吸功能的重要指標,更在 COVID-19 疫情期間,成為重要的健康管理工具。脈搏血氧儀利用直覺的光學方法提供可靠的末稍血氧飽和度 (SpO2) 估計值,可在疫情期間滿足市場對經濟實惠型健康監測解決方案的特定需求。

如本文所述,DSC 搭配一些基本元件後即可提供有效的硬體基礎,以實作低成本的脈搏血氧儀,如此一來,就能可靠地提供 SpO2 測量值,而使用者就能以此判斷是否要在 COVID-19 感染期間尋求進一步的醫療協助。

DigiKey logo

聲明:各作者及/或論壇參與者於本網站所發表之意見、理念和觀點,概不反映 DigiKey 的意見、理念和觀點,亦非 DigiKey 的正式原則。

關於作者

Image of Stephen Evanczuk

Stephen Evanczuk

Stephen Evanczuk 撰寫電子產業的相關資訊已有超過二十年的經驗,涉及的主題多元,涵蓋硬體、軟體、系統以及包含 IoT 在內的應用。他以神經元網路為研究主題,取得神經科學博士學位,並且在航太產業,針對廣泛運用的安全系統和演算法加速方法進行研究。目前,在撰寫科技和工程文章之餘,他投入辨識和推薦系統的深度學習應用。

關於出版者

DigiKey 北美編輯群