從題目中可以看出該課題來源于2020年省電賽A題的無線運動傳感器節(jié)點的設(shè)計,該作品得過湖北省電賽二等獎,同時也是我本科畢業(yè)設(shè)計,這里我把自己做的關(guān)于心電部分的工作進(jìn)行一次總結(jié),也對我的大學(xué)四年進(jìn)行一次總結(jié)。
一、硬件設(shè)計
- 處理器板子的選擇
本研究的處理器模塊選擇正點原子公司的STM32F4最小系統(tǒng)板子,如圖1所示,該最小系統(tǒng)板子搭載STM32F407ZGT6芯片,并具有 192KB的SRAM、1024KB的FLASH、豐富的定時器資源(12個16位定時器,2個32位定時器)、112個通用I/O口、2個DMA控制器以及1個FSMC接口,其中通過FSMC接口可以使得刷屏的速度可達(dá)3300W像素/秒,另外該板子還外擴(kuò)了1M字節(jié)的SRAM芯片,更加有利于該處理器驅(qū)動4.3寸的LCD,這樣極大加快心電監(jiān)測儀的刷屏速度,而且STM32F407ZGT6這款芯片還集成FPU和DSP指令,可以加快數(shù)字濾波器的處理速度,而且該最小系統(tǒng)板子還將FSMC接口和其他IO口一并引出。
- 心電采集板—ADS1292R模塊的介紹
本研究最重要的地方便是心電采集板,關(guān)于心電信號的采集板的芯片選擇TI公司的ADS1292R,外圍電路參考TI公司所給的原理圖和建議繪制,如圖所示。
關(guān)于ADS1292R的外圍電路的介紹和使用,這里推薦這篇博文,ADS1292R的使用
- 溫度模塊----LMT70
溫度檢測模塊采用LMT70溫度傳感器。其優(yōu)點是:超小型、高精度、低功耗的模擬溫度傳感器。而缺點是:接觸式的溫度傳感器,測體表溫度存在一定誤差。但是考慮到溫度測量的精度以及測量方便,最終選擇LMT70作為測溫傳感器,同時選擇采用ADS1118具有PGA、電壓基準(zhǔn)、16位的高精度ADC對LMT70數(shù)據(jù)的溫度模擬量進(jìn)行采集。
- 系統(tǒng)不帶屏幕的外觀
二、GUI的設(shè)計
本系統(tǒng)為了更好的人機(jī)交互,采用4.3寸觸摸屏搭載開源圖形庫LVGL,一方面將顯示波形和數(shù)據(jù)與心電信號的采集和處理隔離開,另一方面是為了交互的方便和美觀,系統(tǒng)運行界面如下圖所示。整體界面主要有菜單、返回、圖表、數(shù)據(jù)欄、導(dǎo)聯(lián)狀態(tài)燈以及開啟心電采集按鈕。
系統(tǒng)的菜單是通過LVGL的roller控件繪制的,roller里面選項的事件則是通過回調(diào)函數(shù)的形式調(diào)用。由于選中roller的選項,LVGL就會返回選項的值,因此我自己設(shè)計了函數(shù)指針數(shù)組來注冊回調(diào)函數(shù),并且將這個選中的序號通過數(shù)組方式調(diào)用函數(shù),代碼如下所示:
void (*oper_fuc[4])();//函數(shù)指針數(shù)組
void Menuitem_Init(void)
{
oper_fuc[0]=send_type_server;
oper_fuc[1]=Set_chart_div_line;
oper_fuc[2]=clear_step;
oper_fuc[3]=smooth_filter;
}
static void roller_event_handler(lv_obj_t * obj, lv_event_t event)
{
static unsigned char count=0;
if(event==LV_EVENT_VALUE_CHANGED)
{
count=lv_roller_get_selected(obj);
}
if(event==LV_EVENT_CLICKED)
{
oper_fuc[count]();
}
}
roller_event_handler是選中roller中的事件函數(shù),在事件函數(shù)里面來回調(diào)選項的處理函數(shù)。roller中總共寫了4個選項,分別為
send_type選擇發(fā)送類型(支持發(fā)送到本地顯示或者串口發(fā)送給上位機(jī))、set_div_line是否設(shè)置圖表的等分線、
clear_step清除界面上的數(shù)據(jù)、
smooth_filter是否進(jìn)行平滑濾波
本系統(tǒng)還設(shè)計導(dǎo)聯(lián)狀態(tài)指示燈,前面討論過ADS1292R可以檢測電聯(lián)的脫落狀態(tài),因而這里用LVGL的led控件作為導(dǎo)聯(lián)的狀態(tài)指示,當(dāng)檢測到導(dǎo)聯(lián)接入人體,led控件就會點亮。設(shè)計了紅心周期性跳動,當(dāng)檢測到導(dǎo)聯(lián)接入人體后,紅心就會周期性跳動,當(dāng)心電數(shù)據(jù)采樣開始后,紅心隨著心率值的改變而跳動著。同時還設(shè)計了采樣開始/停止按鈕,可以隨時暫停和開始采樣心電信號。
除了以上看得見的設(shè)計之外,還創(chuàng)建了四個周期性的任務(wù),任務(wù)優(yōu)先級從高到低分別為:更新數(shù)據(jù)欄里的數(shù)據(jù)、更新導(dǎo)聯(lián)狀態(tài)、檢查心電信號的縱軸范圍、系統(tǒng)狀態(tài)的檢查。
三、導(dǎo)聯(lián)體系的選擇
心電信號本質(zhì)上是測量人體體表的電信號,將電極通過一定的導(dǎo)聯(lián)體系就可以記錄到心電圖,因而選擇合適的電極是觀察心電圖至關(guān)重要的選擇。在醫(yī)學(xué)上常見的導(dǎo)聯(lián)體系分別為標(biāo)準(zhǔn)12導(dǎo)聯(lián)體系、Lewis導(dǎo)聯(lián)、Fontaine導(dǎo)聯(lián)、Cabrera導(dǎo)聯(lián)、Nehb導(dǎo)聯(lián)、frank導(dǎo)聯(lián)、Mason-Likar導(dǎo)聯(lián)等。標(biāo)準(zhǔn)12導(dǎo)聯(lián)體系是醫(yī)院所使用的,它由3個雙極肢體導(dǎo)聯(lián)、6個單極胸前導(dǎo)聯(lián)、3個單極加壓肢體導(dǎo)聯(lián)所組成。
該系統(tǒng)的主要目的是實時檢測心率和QRS寬度,因此選擇的導(dǎo)聯(lián)應(yīng)該基于能觀察心電中R波較大的原則。因而選擇標(biāo)準(zhǔn)12導(dǎo)聯(lián)中標(biāo)準(zhǔn)肢體導(dǎo)聯(lián)I(見圖左),或者M(jìn)ason-Likar導(dǎo)聯(lián)(見圖右)。
四、心電電極選擇
人體的內(nèi)阻很高,因而心電信號是一個高內(nèi)阻且幅度很低的信號,如果處理不好就會造成心電信號的衰減,因此就需要從兩方面解決:
(1)降低與電極的接觸阻抗(2)提高采集電路的輸入阻抗。
目前,市面上有三種電極,分別為濕電極、干電極和非接觸式電極,這三種電極中濕電極的接觸電阻最小,因而對于模擬前端的輸入電阻不需要太大。濕電極主要由電極片、Ag/AgCl 涂層、導(dǎo)電膠等物質(zhì)組成。 醫(yī)學(xué)電極貼片與身體接觸的是水凝膠(親水化合物),“黑色”部分為Ag/AgCl,使用導(dǎo)電金屬和導(dǎo)線與儀器連接,實物如圖所示。
五、心電信號時域和頻域特征
人體的心電信號是一種非平穩(wěn)、非線性、隨機(jī)性比較強(qiáng)的微弱生理信號,幅值約為毫伏(mV)級,頻率在0.05-100Hz之間。心電信號的每一個心跳循環(huán)由一系列有規(guī)律的波形組成,它們分別是P波、QRS復(fù)合波和T波,而這些波形的起點、終點、波峰、波谷、以及間期分別記錄著心臟活動狀態(tài)的詳細(xì)信息
心電信號各個波段的詳細(xì)說明如下:
心電各個波段的功率譜如下:
心電信號的噪聲分析如下:
讀者想對心電信號進(jìn)一步了解可以參考如下鏈接:http://www.mythbird.com/ecgxin-hao-te-zheng/。
六、軟件設(shè)計
6.1、系統(tǒng)總體設(shè)計
系統(tǒng)先從硬件初始化開始,其中包括串口初始化、觸摸屏初始化、外部SRAM初始化、ADS1292R初始化、LMT70初始化、LVGL心跳定時器初始化。
其次就是LVGL初始化,主要是一些主題和變量的初始化。然后創(chuàng)建系統(tǒng)的UI界面和一些定時的任務(wù)。
最后初始化心電數(shù)據(jù)緩存、 數(shù)字低通濾波器初始化、心率數(shù)據(jù)緩存初始化。
完成以上的初始化,系統(tǒng)便進(jìn)入主循環(huán),等待心電數(shù)據(jù)輸入緩存中出現(xiàn)數(shù)據(jù),隨后開始濾波,將濾波之后的數(shù)據(jù)寫入心電輸出緩存中,然后輪詢LVGL的任務(wù)和觸摸屏掃描。就這樣不停地循環(huán)。其中心電輸入緩存中的數(shù)據(jù)是通過中斷從ADS1292R的輸出引腳中讀取,而心電輸出緩存則是原始數(shù)據(jù)經(jīng)過低通處理后的數(shù)據(jù),等待LVGL顯示任務(wù)的到來并顯示在觸摸屏上。系統(tǒng)總體框圖和軟件框圖如下所示
6.2、系統(tǒng)總體設(shè)計
在前面討論過心電信號頻譜和噪聲,因而要對心電信號進(jìn)行濾波,為了同時實現(xiàn)心電信號的實時濾波和心電波形實時顯示,所以有必要設(shè)計一個緩存區(qū)來解決這個難題。這里我打算用我自己設(shè)計的兩個循環(huán)隊列解決這個難題。
為了使得在濾波的時候,心電數(shù)據(jù)依然能夠采集,設(shè)計兩個循環(huán)隊列,如上圖所示,其中IN_Buffer和OUT_Buffer的每個矩形框表示25x4個字節(jié)的空間,這取決一次需要多少字節(jié)的數(shù)據(jù)濾波。這里一次濾波需要25個int型的數(shù)據(jù),因而每個緩存需要25x4字節(jié)。圖中的藍(lán)色填充表示緩存區(qū)中填滿了數(shù)據(jù),每次讀完數(shù)據(jù)之后都需要切換緩存區(qū),且IN_Buffer和OUT_Buffer的讀寫操作相反,即IN_Buffer的讀操作是OUT_Buffer的寫操作,程序框圖如下圖所示。
圖上所示的三個程序均是并行處理的,
程序1是通過外部中斷的服務(wù)函數(shù)調(diào)用的,
程序2則是在UI畫圖程序里面通過定時器周期性的調(diào)用,
程序3則是在主程序中的濾波函數(shù)里面調(diào)用
程序1代碼如下(ADS1292R采用中斷方式讀取數(shù)據(jù)):
void EXTI9_5_IRQHandler(void)
{
if(EXTI->IMR&EXTI_Line5 && ADS_DRDY==0)//數(shù)據(jù)接收中斷
{
ADS1292_Read_Data(ads1292_Cache);//數(shù)據(jù)存到9字節(jié)緩沖區(qū)
Update_ECG_Data(ads1292_Cache);
Cheack_lead_stata(ads1292_Cache);
if(state_pcb.SampleStartFlag==true)
WriteAdsInBuffer(ecg_info.ecg_data);//數(shù)據(jù)寫入緩存區(qū)
}
EXTI_ClearITPendingBit(EXTI_Line5);
}
程序2代碼如下(LVGL的心跳在定時器中周期調(diào)用,同時程序2也在其中運行,主要從濾波后的數(shù)據(jù)緩存中取出數(shù)據(jù)進(jìn)行波形顯示):
void Wave_show(void)
{
int value=0;
if(ReadEcgOutBuffer(&value)!=0) {
if(ecg_graph.send_type==GRAPH) {
ecg_graph.y_pose=Transf_EcgData_To_Vert(value,ecg_graph.sacle);
chart_add_data(ecg_graph.y_pose);
set_data_into_heart_buff(ecg_graph.y_pose);
} else if(ecg_graph.send_type==USART) {
//EcgSendByUart(value);
printf("%d\r\n",(int)alg(value/200));
}
}
}
//定時器3中斷服務(wù)程序
void TIM3_IRQHandler(void)
{ static u8 show_cnt=0;
if(TIM3->SR&TIM_IT_Update)//溢出中斷
{ show_cnt++;
lv_tick_inc(1);//lvgl的1ms心跳
if(show_cnt==3){
show_cnt=0;
Wave_show();
}
}
TIM3->SR = (uint16_t)~TIM_IT_Update;
}
程序3代碼如下(在濾波函數(shù)中調(diào)用,用于承上啟下,即從IN緩存中取出數(shù)據(jù),濾波之后寫入OUT緩存中):
void arm_fir_f32_lp(void)
{
float32_t *inputf32, *outputf32;
if(ReadAdsInBuffer() && WriterEcgOutBuffer()){//指針定位成功
/* 初始化輸入輸出緩存指針 */
inputf32 = (float32_t *)InFifoDev.rp;
outputf32 =(float32_t *)OutFifoDev.wp;
/* 實現(xiàn)FIR濾波 */
arm_fir_f32(&S, inputf32, outputf32, BLOCK_SIZE);
//my_memcpy(OutFifoDev.wp,InFifoDev.rp,BLOCK_SIZE*4);
InFifoDev.state[InFifoDev.read_front]=Empty;
InFifoDev.read_front=(InFifoDev.read_front+1)%PACK_NUM;//切換讀緩存塊
OutFifoDev.state[OutFifoDev.writer_rear]=Full;
OutFifoDev.writer_rear=(OutFifoDev.writer_rear+1)%PACK_NUM;//切換寫緩存塊
}
}
關(guān)于緩存切換代碼如下:
static void WriteAdsInBuffer(int date)
{
static u8 cnt=0;
if(InFifoDev.state[InFifoDev.writer_rear]==Empty){//緩存塊可寫
InFifoDev.wp=&AdsInBuffer[InFifoDev.writer_rear*(BLOCK_SIZE)];//將寫指針定位寫緩存塊
InFifoDev.wp[cnt++]=date;
if(cnt==BLOCK_SIZE){
cnt=0;
InFifoDev.state[InFifoDev.writer_rear]=Full;
InFifoDev.writer_rear=(InFifoDev.writer_rear+1)%PACK_NUM;//切換寫緩存塊
}
}
}
//定位讀指針
//成功則返回1,不成功則返回0
u8 ReadAdsInBuffer(void)
{
if(InFifoDev.state[InFifoDev.read_front]==Full){//緩存塊可讀
InFifoDev.rp=&AdsInBuffer[InFifoDev.read_front*(BLOCK_SIZE)];//將讀指針定位讀緩存塊
return 1;
}
return 0;
}
//定位讀指針
u8 WriterEcgOutBuffer(void)
{
if(OutFifoDev.state[OutFifoDev.writer_rear]==Empty){//緩存塊可寫
OutFifoDev.wp=&EcgOutBuffer[OutFifoDev.writer_rear*(BLOCK_SIZE)];//將讀指針定位讀緩存塊
return 1;
}
return 0;
}
//成功則返回1,不成功則返回0
u8 ReadEcgOutBuffer(int32_t *p)
{
static u8 cnt=0;
if(OutFifoDev.state[OutFifoDev.read_front]==Full){//緩存塊可讀
OutFifoDev.rp=&EcgOutBuffer[OutFifoDev.read_front*(BLOCK_SIZE)];//將寫指針定位讀緩存塊
*p=OutFifoDev.rp[cnt++];
if(cnt==BLOCK_SIZE){
cnt=0;
OutFifoDev.state[OutFifoDev.read_front]=Empty;
OutFifoDev.read_front=(OutFifoDev.read_front+1)%PACK_NUM;//切換寫讀緩存塊
}
return 1;
}
return 0;
}
6.3、心電信號濾波
- 工頻噪聲濾除
濾除工頻噪聲的數(shù)字濾波算法主要有經(jīng)典濾波器、小波變換、自適應(yīng)濾波。小波變換能將心電信號進(jìn)行多層分解,可以使得心電信號與工頻噪聲分離,但是計算量大,所占用的中間變量也比較多,對于單片機(jī)來說,處理的速度也不夠快,因而對于系統(tǒng)的實時性這一指標(biāo)很難實現(xiàn)。自適應(yīng)濾波能夠自動跟蹤工頻噪聲的改變,但是需要增加一個輸入信號作為參考,因而增加了系統(tǒng)的復(fù)雜性。在前面也討論過心電信號95%的能量都是集中在0~40Hz,而工頻噪聲則在50Hz左右,過渡帶比較寬,因而可以選擇截止頻率為40Hz的低通濾波器。
該低通濾波器利用MATLAB的FDATOOL生成,只需要選擇低通濾波器是FIR結(jié)構(gòu),選擇Blackman-Harris窗函數(shù),濾波器的階數(shù)定為50,選擇采樣頻率為250Hz,截止頻率為40Hz,參數(shù)如下圖所示:
然后利用FDATOOL生成的沖激響應(yīng)的數(shù)組,選擇ARM官方的DSP庫,調(diào)用arm_fir_f32函數(shù),既可以完成一次濾波。但是在這之前,需要調(diào)用arm_fir_init_f32進(jìn)行初始化。
濾波器系數(shù)如下:
const float32_t fir32LP[NUM_TAPS] = {
-7.484454468902e-22,-3.269336712398e-06,-1.365915864079e-05,-5.014073980636e-06,
6.804735231975e-05,0.0001662336497003,7.965197426322e-05,-0.0003784662837741,
-0.0008928563387901,-0.0005280588787408, 0.001284875839485, 0.003225662215767,
0.0022425431358,-0.003157084585057,-0.009028737319977,-0.007219934929014,
0.006057868257093, 0.02144319498633, 0.01971312591228,-0.009448071870685,
-0.04806332586811, -0.05291973061693, 0.01224382260678, 0.1388254178822,
0.2663085232723, 0.3199984843521, 0.2663085232723, 0.1388254178822,
0.01224382260678, -0.05291973061693, -0.04806332586811,-0.009448071870685,
0.01971312591228, 0.02144319498633, 0.006057868257093,-0.007219934929014,
-0.009028737319977,-0.003157084585057, 0.0022425431358, 0.003225662215767,
0.001284875839485,-0.0005280588787408,-0.0008928563387901,-0.0003784662837741,
7.965197426322e-05,0.0001662336497003,6.804735231975e-05,-5.014073980636e-06,
-1.365915864079e-05,-3.269336712399e-06,-7.484454468902e-22
};
static float32_t firStateF32[BLOCK_SIZE + NUM_TAPS - 1];
arm_fir_instance_f32 S;
void arm_fir_Init(void)
{
arm_fir_init_f32(&S, NUM_TAPS, (float32_t *)&fir32LP[0], &firStateF32[0], BLOCK_SIZE);
}
濾波函數(shù)見程序3(往上找)
- 基線漂移
基線漂移與工頻噪聲不同,它是由于呼吸和電極滑動變化所異致的,其頻率一般低于1Hz左右。常見對于基線漂移濾除的數(shù)字算法有高通濾波器、中值濾波、小波變換、形態(tài)學(xué)濾波、曲線擬合等,其中高通濾波器可能會對心電信號的ST波段產(chǎn)生影響,畢竟基線漂移的頻率也在ST波段里面。曲線擬合對較大的基線漂移處理能力較弱,處理的效果與處理數(shù)據(jù)的長度成正相關(guān),因而不適用實時處理的系統(tǒng)。小波變換計算量大,也不適用實時處理的系統(tǒng)。相比之下,形態(tài)學(xué)濾波對心電信號的基線漂移濾除效果更好,計算量也比中值濾波小。但是形態(tài)學(xué)濾波要求數(shù)據(jù)長度足夠長,因而會改變前面的緩存結(jié)構(gòu),并且在本系統(tǒng)中并未太嚴(yán)重的基線漂移,系統(tǒng)的任務(wù)也比較多,多方面權(quán)衡之下,選擇不處理基線漂移。
- 肌電噪聲的抑制
肌電噪聲主要是由于人體肌肉顫抖導(dǎo)致體表的電位發(fā)生變化,這種噪聲通過電極貼傳導(dǎo)至心電模擬前端,并且這種噪聲持續(xù)時間較短,使得ECG信號波形產(chǎn)生細(xì)小的波紋,這種噪聲頻率分布比較廣,前面已經(jīng)將心電信號通過截止頻率為40Hz的低通濾波器,因而需要5點平滑濾波將細(xì)小的波紋濾除,為了不影響心電信號的實時處理,因而改進(jìn)版的平滑濾波器代碼如下:
/*
* 滑動平均值濾波。
* 每調(diào)用一次,就加入一個新數(shù)據(jù),并得到當(dāng)前的濾波值。
*/
float alg(float new_val)
{
/* 用一個減法,就做了"丟棄最舊的數(shù)據(jù),加入最新的數(shù)據(jù)"這一操作 */
sum += (new_val - buf[pos]);
buf[pos] = new_val;
pos = (pos + 1) % MAX_COUNT;
/* 個數(shù)不足時,cnt是實際個數(shù),個數(shù)足夠時,cnt最多也只是MAX_COUNT */
pcnt += (pcnt < MAX_COUNT);
return sum / MAX_COUNT;
}
6.4、心率和QRS寬度檢測
心率和QRS寬度檢測作為本系統(tǒng)的算法核心,有了心率值和QRS寬度值才能進(jìn)一步判斷常見的心律失常。心率基本上都是檢測兩個R波之間的時隙來計算的,常見檢測R的算法主要有閾值法、模板法和語句描述法。
而本系統(tǒng)的心率和QRS寬度檢測算法是在一起檢測的,所采用的算法是幅度閾值檢測和差分檢測相結(jié)合,因為觀察心電信號的R波,發(fā)現(xiàn)R波是具有窄的脈沖,且脈沖的幅度是心電信號最高的,因而采用幅度和一階差分共同約束找到R波,同時在找R波的同時還可以估計出QRS的寬度,算法的框圖如圖
心率檢測和QRS寬度檢測算法是采用狀態(tài)機(jī)的編程思想,通過R波幅度大且從Q到R一直遞增,并且R波到S波的一階差分值很大,從而將R波定位出來,檢測兩個R波之前的時間,然后通過如下公式就可以計算出心率:
H
R
=
(
60
?
S
a
m
p
l
e
R
a
t
e
)
/
c
o
u
n
t
HR=(60*SampleRate) /count
HR=(60?SampleRate)/count
而QRS寬度則是由
Q
R
S
=
Q
R
S
c
n
t
?
2.2
?
1000
/
(
S
a
m
p
l
e
R
a
t
e
)
QRS=QRScnt*2.2* 1000/(SampleRate)
QRS=QRScnt?2.2?1000/(SampleRate)
上式中的2.2是估計值,因為QRS_cnt是在檢測到R波之后才開始計數(shù),并且未到S波谷停止計數(shù),觀察QRS波,發(fā)現(xiàn)Q到R與R到S近似對稱,因而采用2.2這個估計值,這也是實時檢測的缺陷,檢測的樣本不多。
心率算法和QRS寬度檢測代碼如下:
/**
* @Brief 測量心率
* @Call
* @Param
* @Note
* @Retval
*/
void ecg_heart_rate(int data)
{
int Signal=data;
if(Signal>hr.vmax)
hr.vmax=Signal;
if(Signal<hr.vmin)
hr.vmin=Signal;
thresh=hr.vmax-(hr.vmax-hr.vmin)/5;
for( uint16_t i = 0; i <= DATA_NUM_CAL_HR - 2; i++ )
{
DataArrayCalHR[i] = DataArrayCalHR[i + 1];
}
DataArrayCalHR[DATA_NUM_CAL_HR - 1] = Signal;
Diff_Arrray( DiffDataArrayCalHR, DataArrayCalHR, DATA_NUM_CAL_HR ); //差分
if(hr.flag==StartDetected){
uint8_t FlagAllDiffRise = true;
for( uint16_t i = 0; i <= DATA_NUM_CAL_HR - 2; i++ ) //判斷波形是否一直上升
{
if( DiffDataArrayCalHR[i] <= 0 )
{
FlagAllDiffRise = false;
break;
}
}
if(FlagAllDiffRise==true){
hr.flag=QWave;
}
}
else if(hr.flag==QWave)//已經(jīng)找Q波
{
if(DataArrayCalHR[DATA_NUM_CAL_HR-1]>thresh){
if(hr.count>125){
if( hr.firstBeat==true )//如果已經(jīng)找到 過R波
{
hr.rate=(float)60*SAMPLE_RATE/(hr.count);
hr.count=0;//清除計數(shù)
hr.flag=RWave;
QRScntflag=true;
} else if(hr.firstBeat==false) {
hr.firstBeat=true;
hr.count=0;//清除計數(shù)
hr.flag=RWave;
QRScntflag=true;
}
}
}
}
else if(hr.flag==RWave ){
if(DiffDataArrayCalHR[0]<-(hr.vmax-hr.vmin)/5){
hr.flag=SWave;
}
}
else if(hr.flag==SWave){
if(hr.QRS_cnt<15){
hr.flag=StartDetected;
hr.QRS=hr.QRS_cnt*22*100/SAMPLE_RATE;
hr.QRS_cnt=0;
QRScntflag=false;
}else {
hr.flag=StartDetected;
hr.QRS=0;
hr.QRS_cnt=0;
QRScntflag=false;
}
}
if(hr.count>420){
hr.firstBeat=false;
hr.flag=StartDetected;
hr.vmax=Heart_MIN;
hr.vmin=Heart_MAX;
hr.rate=0;
thresh=0;
hr.count=0;//清除計數(shù)
}
}
七、實機(jī)演示
實機(jī)演示
八、總結(jié)與展望
本系統(tǒng)因為沒有加入操作系統(tǒng)的管理,造成實現(xiàn)的功能較為少,并且數(shù)據(jù)分析功能因為缺乏強(qiáng)大處理器造成數(shù)據(jù)分析功能所需要的指標(biāo)太少,要想對心電信號實現(xiàn)自動化分析,必須對心電信號更多的信息進(jìn)行提前,而且由于處理器的限制使得一些強(qiáng)大的數(shù)字算法是用不了,而且將采集—濾波—顯示集成一體化本身就顯得笨重,會讓每個處理單元相互牽制,嚴(yán)重的會影響系統(tǒng)的采樣率,造成一些不必要的誤差。所以日后會有針對選擇更為強(qiáng)大的處理器,會將采集、濾波、顯示分開來。同時為了減少外界噪聲,應(yīng)該選擇更為干凈的電源和屏蔽外殼,系統(tǒng)也不能以這種模塊化的方式拼接在一起,日后會選擇畫PCB,將所有模塊集成在PCB上,在套上屏蔽殼,這樣能夠最大程度減少外界噪聲干擾。并且無線通信模塊保留著,但是上位機(jī)并未實現(xiàn),因而日后需要增加這一項功能??偨Y(jié)以上的不足如下:文章來源:http://www.zghlxwxcb.cn/news/detail-437962.html
- 使用更為強(qiáng)大的處理器,將采集、濾波、顯示分開來。
- 所有模塊應(yīng)該集成在PCB上,在增加屏蔽殼。
- 開發(fā)上位機(jī)軟件,實現(xiàn)心電數(shù)據(jù)的無線通信的功能。
代碼我會放在github上,鏈接如下:
https://github.com/lvzhe-speed/STM32_ECG
后面我去考研了,希望能夠考上,以后會每一年至少寫一篇技術(shù)博客,謝謝各位大佬前來斧正,歡迎探討,一起技術(shù)進(jìn)步。其實我也想借此說一下,不能因為一次失敗就否定自己之前的努力,100-1=0這也許是別人對你的評價,但自己不能認(rèn)同這個錯誤的式子,人生不僅有加法,也有減法。也希望對別人多一點諒解,每個人都不容易,也許今日之菜鳥,明日之大鵬,總會翱翔九天。文章來源地址http://www.zghlxwxcb.cn/news/detail-437962.html
到了這里,關(guān)于基于STM32F4的心電監(jiān)護(hù)儀的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!