一、脈搏波簡介
脈搏一般情況下指的都是動脈脈搏。每分鍾的脈搏次數稱為脈率,正常情況下與心率是一致的。心臟的一次收縮和舒張成為一個心動周期。在每個心動周期內,心室的收縮和舒張會引起脈內壓力的周期性波動,使動脈擴張和回縮,從而使得動脈血管發生有規律的搏動,稱為脈搏。當脈搏在血管中向前傳遞的時候,是采用波浪式向前,所以稱為脈搏波。脈搏波是一種波的形式,當心臟規律性的進行收縮和舒張運動的時候,血液注入到主動脈流經身體其他部位時產生的一種波。心室進行收縮運動的時候,主動脈瓣會呈現一種被張開的狀態,血液在這種情況下會注入主動脈,但是在血管中存在着阻撓血液流動的力,這樣血液會有一部分不能夠立即注入到靜脈中,造成血液會暫時存在主動脈的近端,擴張了主動脈,而導致主動脈血液容量變大,血壓升高等變化。而當心室舒張時,主動脈瓣會呈現關閉狀態,停止向動脈注血,主動脈在血管的彈性影響下而回到最初狀態。脈搏波是心臟的收縮和舒張沿動脈向四周傳遞而形成的,其傳播特點與心臟射血能力、血管粗細、血管壁彈性、血液粘稠度等有關。因此,在脈搏波中包含着大量的人體心臟和血管等人體系統的病理和生理信息。典型的脈搏波如圖所示。
圖中的 1 表示脈搏波的上升支,代表心室的快速射血周期,表現為上升快速且平滑,上升的速度與心室的射血能力以及動脈血管的阻力和血管壁的彈性有關,占整個脈搏波波動周期的時間比較短,圖中 a 點所處的位置為心室射血結束,緊接着主動脈關閉,進入下降支。2 表示脈搏波的下降支,是心室射血到下一次射血開始前的一段時期,占整個脈搏波動周期的時間比較長,a 點之后,主動脈內血流向主動脈瓣方向反向流沖擊主動脈瓣,因瓣膜關閉不能進入心室,因而又退回到主動脈,過程類似潮汐,因此稱為潮波,又稱為重波前波,如圖 中 b 所示。c 點為重波波谷,又被稱之為降中峽,它的形成是因為心室收縮完畢、舒張開始,此時主動脈內血流趨向回流,即血流向主動脈瓣方向反向流動,因此在急速降低的脈搏波的降支形成一個切跡。d 點為重搏波峰。在心室射血期緩慢下來后,心室慢慢舒張,室內的壓力很快降低到明顯低於主動脈的脈壓,主動脈內的血流開始向心室方向反向流動,因主動脈瓣關閉,血流逆流沖擊主動脈瓣,但因瓣膜關閉血流不能流入心室,只能向主動脈退去,因此造成動脈內的壓強在突然降下后,很快的上升,動脈管壁亦隨之稍有擴張,因此,在下降支的終端形成一個小波。
二、系統組成
該系統由Pluse Sensor光電反射式傳感器模塊、基於IIC的OLED顯示模塊和STM32F103ZET6最小系統三個模塊組成。Pluse Sensor脈搏波形模塊采集原始模擬信號,通過導線傳輸給STM32芯片經內部ADC轉換為數字信號,再通過心率脈搏算法得到心率BPM,然后由畫點函數將數據以波形和數據的方式在OLED屏幕上實時顯示,其系統框圖如下圖所示。
根據圖所示的系統框圖,本設計以STM32F103為核心,簡單的外圍電路為輔助,主要實現實時采集人體的脈搏以及心電信號,采集到的脈搏波形在顯示屏上實時顯示,並計算出每分鍾心跳次數的功能。
三、系統模塊選擇
3.1傳感器選擇
PulseSensor 是一款用於脈搏心率測量的光電反射式模擬傳感器。將其佩戴於手指、耳垂等處,利用人體組織在血管搏動時造成透光率不同來進行脈搏測量。傳感器對光電信號進行濾波、放大,最終輸出模擬電壓值。單片機通過將采集到的模擬信號值轉換為數字信號,再通過計算就可以得到心率數值。
3.2主控芯片的選擇
本設計的單片機采用意法半導體的STM32F103ZET6。此芯片是基於高性能的ARM®Cortex™-M3的32位RISC內核,工作頻率最高可達72 MHz。Cortex-M3是一個32位處理器內核。內部的數據路徑是32位的,寄存器是32位的,存儲器接口也是32位的。CM3采用了哈佛結構,擁有獨立的指令總線和數據總線,可以讓取指與數據訪問並行不悖。這樣一來數據訪問不再占用指令總線,從而提升了性能。為實現這個特性,CM3內部含有好幾條總線接口,每條都為自己的應用場合優化過,並且它們可以並行工作。此外此款單片機還有豐富的外設比較低的功耗,因此可以用在醫療設備上。
3.3顯示設備及數據傳輸
我們采用了四引腳的IIC模式OLED屏幕,處理好的數據將在SDA數據線和SCL時鍾的共同控制下通過單片機的IIC接口實時發送到OLED顯示屏中顯示出波形。同時也會將計算好的心率BPM顯示。
四、檢測原理
4.1傳感器
傳感器只有三個引腳,分別為信號輸出 S 腳 、電源正極 VCC 以及電源負極 GND,供電電壓為 3.3V到5V,可通過杜邦線與開發板連接。
上電后,傳感器會不斷從 S 腳輸出采集到的電壓模擬值。需要注意的是,印有心形的一面才是與手指接觸面,在測量時要避免接觸布滿元件的另一面,否則會影響信號准確性。模塊圖如圖。
4.2心跳節拍采集與計算處理
總體上就是設定一些參數,判斷心跳節拍是否產生(也就是有沒有把手穩定的放到傳感器上)。如果有,那么找到該節拍的峰值和波谷,計算公式如下:
IBI = sampleCounter - lastBeatTime
測量示意如圖所示。
4.3心率計算
通過以下公式即可算得心率。
4.4 OLED顯示
脈搏波形可以在OLED屏幕上實時顯示,但由於IIC接口是並行傳輸,屏幕的刷新速度可能會比較慢
五、電路設計
5.1 STM32F103控制模塊
STM32F103單片機,具有512kB Flash,64kB SRAM,系統時鍾72MHz,內核為ARM32位的Cortex-M3 CPU。 該控制器含有112個多功能雙向I/O口,所有I/O口可以映像到16個外部中斷;同時,還包括多達4個16位定時器,每個定時器有多達4個用於輸入捕獲、輸出比較、PWM或脈沖計數的通道,串口通信接口可用於單片機與上位機或串口顯示屏的通信。STM32片上資源豐富,功能強大,結合中斷、定時器等其他外設實現便攜式心電監測功能。其硬件電路如圖所示,串口屏的RXD和TXD分別連接單片機上的PA9(TXD)和PA10(RXD),進行人機數據交換。
5.2 Pulse Sensor心電監測模塊
Pulse Sensor是一款用於脈搏心率測量、脈搏波形測量和HRV分析的光電反射式模擬傳感器。將其佩戴於手指、耳垂等處,通過導線連接可將采集到的的模擬信號轉變為數字信號,再通過單片機的簡單計算后就可以得到心率數值。本傳感器采用了峰值波長為515mm的綠光LED,光接收器采用了APDS-9008,這是一款環境光感受器,感受峰值波長為565mm,兩者峰值波長相近,靈敏度較高。此外,由於脈搏信號容易受到各種信號干擾,在傳感器后面使用了低通濾波器和由運放MCP6001構成的放大器,將信號放大了330倍,同時采用分壓電阻設置直流偏置電壓為電源電壓的1/2,使放大后的芯片可以很好的被單片機的AD采集到的。
5.3 OLED(I2C)顯示模塊
本設計采用主控為SSD1306的0.96寸I2C OLED屏幕來顯示得到的數據並繪制波形。其數據傳輸原理是使用STM32F103ZET6自帶的IIC接口(或使用GPIO模擬IIC也行),通過SDA數據線和SCL時鍾線來與OLED屏幕通信,進而接收或發送處理好的數據。OLED屏幕顯示圖像和字符銳利清晰,並且體積小,功耗低,作為長時間顯示的屏幕十分合適,其實物圖如圖所示。
六、軟件設計
6.1程序總體設計說明
采集到的模擬量經過STM32F103ZET6中自帶的AD轉換模塊轉化為數字量,並以3.3V作為參考電壓將數字量歸化為電壓。
由心率采集與計算處理模塊計算得出心率BPM和心跳節拍IBI;
由位於定時器中的波形處理模塊繪制波形;
由按鍵控制模塊實現通過KEY1切換顯示界面的功能;
6.2 主程序main
本設計的系統軟件主程序流程圖如圖9所示,基於STM32F103ZET6,先初始化各項外設,然后判斷KEY1是否被按下,並且通過按下的次數切換不同的功能模塊進行顯示 。這種設計可以更充分地利用OLED屏幕顯示更多的信息。
6.3按鍵處理函數KEY_deal
在該函數中,需要通過key1_state標志位處理KEY1按鍵。KEY1就是用於決定display_flag值的。key1_state在等於1時代表某按鍵被按下。當判斷KEY1按下后,清零標志位key1_state,並通過OLED_Clear()進行初始清屏操作,然后開始對display_flag進行賦值:若當下display_flag等於0(也就是KEY1只按下了一次,display_flag此時還為初始化時賦的0值),則使display_flag等於1;若當下display_flag已經不為0,則判斷其為1(KEY1按下1次)還是2(KEY1按下2次),如果是1則使display_flag等於2,如果是2則清零display_flag。
6.4 數值顯示函數OLED_Value_display
該函數中有兩個主要變量:temp(u8無符號字符型,用於保存采集到的心率的原始ADC值的整數部分)和xiao(浮點型,保存小數部分)。
取整數和取小數的邏輯:取整,用原始ADC值(adcx)與1做除法運算(“/”)即可去除小數部分,然后把值保存在temp;取小數,把原始ADC值與取整后的值相減並存到xiao即可(因為小數可能很小,所以放大1000倍便於顯示)。然后調用OLED_ShowString()、OLED_ShowHz()以及OLED_ShowNum()等函數顯示。
6.5 波形顯示OLED_Waveform_display函數
waveform_flag為波形采樣時間計數標志位,當采樣128次之后才一次性顯示出來,即waveform_flag變為1時(該標志位在波形處理函數中,由一個采樣延時變量控制),說明已經采樣了128次,此時OLED_Waveform_display()函數按水平方向為Y軸(長度為128),垂直方向為X軸(長度為64)的規定開始繪制波形。值得注意的是,由於OLED屏幕的顯示方式,我們繪制時是按“頁”繪制,一“頁”為OLED上一塊81的區域,一共要繪制8“頁”。這也是為什么要在設計坐標系時將x、y軸對調的原因。當繪制完畢,我們將所有數據更新到GRAM即可顯示一幀波形曲線,若刷新率足夠快,那么我們就能得到連續的波形。*
6.6 自帶庫中的關鍵函數
1、OLED_DrawPoint()畫點函數
該函數有 3 個輸入參數,前兩個是要畫點的坐標,第三個 t 為要寫入 1 還是 0。該函數實現了我們在 OLED模塊上任意位置畫點的功能。
自定義OLED 坐標系如下: 【構建OLED 直角坐標系, x,y軸反置 方便函數運算】
↑x
|
127--------
| |
| |
| |
| |
| |
| |
(0,0)----------→y
63
void OLED_DrawPoint(u8 x,u8 y,u8 t)
{
u8 pos,bx,temp=0;
if(x>127||y>63)return;//超出范圍了.
pos=7-y/8;/*輸入的Y坐標要除以8,以定位到要改變的那個點對應的“頁”(page),減7是為了轉化為1到8下的位置(因為底層是用0到7表示的) */
bx=y%8;//該點在該頁中的位置
temp=1<<(7-bx);//把該點寫入temp
if(t)OLED_GRAM[x][pos]|=temp; //如果t為1就把該點寫1
else OLED_GRAM[x][pos]&=~temp; //如果t為0就把該點寫0
}
OLED畫圖顯示字符時,水平方向為Y軸;垂直方向才是X軸;
如何點亮一個點(假設為x,y)?因為我們是縱向取模,所以我們只要清楚y坐標在哪就行了。首先我們要確定y在哪一“頁”(page)--> pos=y/8;,因為每8行為一頁,而縱向取模是按頁來完成的。其次就要知道y在這一頁里面的哪里,這就要用到求余運算-->bx=y%8。
例如: Y = 25
pos = 25 /8 = 3,即該點處於第4頁(底層驅動是從0開始計數的,代碼中再用7減3可得到1到8下對應頁數);
bx = 25 % 8= 1, 即該點處於第4頁第2行(底層驅動是從0開始計數的);
2、OLED_Refresh_Gram()gram更新函數
由於我們使用的是正點原子系列開發板,其在 STM32 內部定義了一個塊 GRAM:u8 OLED_GRAM[128][8];此部分 GRAM 對應 OLED 模塊上的 GRAM。在操作的時候,我們只要修改 STM32 內部的 GRAM 就可以了,然后通過 OLED_Refresh_Gram 函數把 GRAM 一次刷新到 OLED 的 GRAM 上。OLED_Refresh_Gram 函數先設置頁地址,然后寫入列地址(也就是縱坐標),然后從 0 開始寫入 128 個字節,寫滿該頁,最后循環把 8 頁的內容都寫入,就實現了整個從 STM32 顯存到 OLED 顯存的拷貝。
6.7 ADC處理函數
此處定義一個temp(u16,無符號字符型),調用Get_Adc()獲得ADC原始值,此時該原始值只是ADC輸出的一個數字量,不帶單位,由於ADC是12位的(最大的數字量是4096),所以這個原始值最大只能為4096。因為它不帶單位,我們想要用單位表示它就必須有一個參考,於是就用電壓作為這個參考。參考電壓是3.3v,所以用原始值*3.3/4096就可以把ADC采集到的數字量轉化為電壓值,保存到adcx即可。Signal = temp>>2用於處理傳感器的值,向右位移2位。
6.8 心率采集與計算處理函數(代碼詳見工程)
設定一些參數,判斷心跳節拍是否產生(也就是有沒有把手穩定的放到傳感器上)。如果有,那么找到該節拍的峰值和波谷,用sampleCounter與lastBeatTime做差來得到節拍持續的時間,差值為IBI。其中,sampleCounter是位於通用定時器中的一個自增量,自增值為2ms,用於記錄CPU運行時間;lastBeatTime始終跟隨上一次sampleCounter的值,目的是為了記錄下一次節拍的時間,它的初值為0。
例如從CPU開始運行開始記錄時間到第100ms的時候有了一個脈沖,此時IBI = sampleCounter – lastBeatTime,舍棄此時的IBI,顯然波形沒有持續100ms,此時使sampleCounter = lastBeatTime,當下一次波形到來時(相隔時間很短,因為人的心跳是連續的,可視為脈沖是連續的一個一個到來),此時再用IBI = sampleCounter – lastBeatTime便可以得到單獨一個脈沖的持續時間。將IBI存入一個數組中,用保留的最后十個數據,取一個平均值保存為runningTotal,再把runningTotal平均到一分鍾里面就得到心率BPM,計算公式如下:
IBI = sampleCounter - lastBeatTime
6.9 波形處理函數Waveform_deal(代碼詳見工程)
該函數中包含兩個主要變量,waveSample_times和xiao,前者為采樣延時變量,用於保證每采樣128次記錄一次waveform_flag標志位,然后使波形顯示函數繪制波形;xiao則是用來控制轉換並放大后的小數的長度以便於oled顯示的,一般我們設置為45到80。最終,xiao經過處理后的值在0到60間,用來控制波形的寬度。(因為我們規定x軸的長度為64,超過的話顯示會不完整)
6.10 通用定時器TIM3
TIM3中斷,執行時間由TIMER控制,每2ms執行一次。該定時器內部的中斷函數為標志量sampleCounter的自增語句、Waveform_deal()波形數據處理函數和HeartRate_deal()心率采集與計算處理函數,作用就是每2ms執行對應函數或語句一次以達到之前介紹的一些功能。
6.11主界面顯示函數OLED_Main_display
該函數會調用一個庫函數OLED_ShowHz()去顯示作者和標題信息。
七、實物圖
放幾張效果圖(由於時間限制,就直接用現成的開發板了)
工程地址:
https://gitee.com/daycen/STM32-BPM
通過Keil uVision5打開即可使用