最近正在研製一種經過測量人體導納,估算體內血液變化率,進而評估心血管系統泵血功能的醫療儀器。爲測量人體導納,咱們設計了一套巧妙的激勵信號幅度反饋電路,該電路因爲涉及商業機密就不在這裏討論了。這裏主要分享一下我本身設計的,用於對導納測量電路進行調試和幅度定標的重要工具——導納信號發生器的設計。git
如下原創內容歡迎網友轉載,但請註明出處:http://blog.163.com/helesheng算法
1、自制導納信號發生器的緣由緩存
在研製人體導納測試儀器的過程當中,我發現很難對儀器進行調試和定標:因爲沒法買到商品化的導納信號發生器,只能直接進行人體試驗。一方面,每次調試電路都很麻煩;另外一方面,沒法對電路增益進行幅度和頻率定標。所以自制一種阻值接近人體,變化頻率可調的導納信號發生器就勢在必行了。網絡
導納(admittance)的定義是阻抗的倒數,標準量綱爲西門子S,1S即1Ω的阻抗對應的導納.ide
「導納信號發生器」從本質上講也是阻抗信號發生器,但「導納信號發生器」的靜態部分應該能夠直接設置導納值(而非阻值),而動態部分則應該是導納隨時間成正弦變化的。函數
先來看看人體導納的基本狀況:在50KHz交流信號激勵下,人體胸腔靜態阻抗約30Ω(33mS,33毫西門子),因爲血流變化引發的阻抗約爲5KΩ(0.2mS)。二者相差較大,爲方便調試,我用固定電阻實現導納信號發生器的靜態部分,血流變化引發的動態導納變化則用數字電位器(Digital Potentiometer)來模擬。「動態」和「靜態」兩部分電路則採用並聯形式。工具
2、靜態部分的電路測試
我設計的導納信號發生器的「靜態部分」,是指導納值能夠手動調節,但不會自動變化的部分。其電路以下。ui
圖1 導納信號發生器的靜態部分電路spa
其中R1~R10都是阻值爲100Ω,精度爲1%的電阻,OPT1-OPT10則是導通電阻僅爲1Ω的固態繼電器KAQY212。導納信號發生器能夠在使用者的控制下,使OPT1~OPT10導通和關閉,每多打開一個固態繼電器則測試端Ts1和Ts2之間的導納就增長10mS(100Ω的倒數)。設上述電路在Ts1和Ts2之間產生的靜態導納爲
其中K爲打開的固態繼電器數量, 是100Ω電阻對應的導納數值。例如,當打開三個固態繼電器後, 爲30mS —— 與人體靜態導納至關。
3、動態部分的電路
我設計的導納信號發生器的「動態部分」,是導納值自動呈正弦性週期變化的部分,它與靜態部分並聯,以模擬人體的動態導納變化。我用最大阻值爲10KΩ的數字電位器MCP41010來實現動態部分。
圖2 與靜態部分並聯的動態部分電路
MCP41010是SPI接口的器件,使用者設置完變化頻率後,導納信號發生器中的MCU經過SPI口定製改變MCP41010的阻值,以使導納值根據設定的頻率成正弦變化。其中,MCP41010的阻值分辨率爲最大阻值(10KΩ)的256分之一。
設MCP41010在Ts1和Ts2之間產生的動態導納爲 ,而從外部觀測整個導納信號發生器,其整體導納能夠表示爲下式。
4、控制和人機交互電路
個人導納信號發生器可以設置靜態導納值和動態導納變化的頻率,所以還必須有顯示和按鍵等人機交互設備。爲了省事,我使用了一個具備Arduino接口的STM32F103開發板做爲個人主控板。擴展Arduino盾板除了有上述的靜態導納和動態導納電路以外,還有一隻四位數碼管和四隻按鍵。四位數碼管中兩位用於顯示靜態導納,兩位用於顯示動態導納變化的頻率;四隻按鍵兩隻用於調整靜態導納,兩隻用於調整動態導納變化的頻率。
標準Arduino接口的I/O數量很少,不足以控制這麼多外設,所以使用了三隻74HC595來擴展I/O口。具體電路以下圖所示。
圖3 控制和人機交互電路
上面電路右上角爲標準Arduino擴展接口,左邊順序串接的三隻74HC595擴展產生:數碼管的段碼驅動D0-D七、數碼管的位選通驅動DIG0-DIG3以及靜態導納電路中固態繼電器的開關信號SWs1- SWs10。
動態導納產生電路中的數字電位器MCP41010則由Arduino接口中的SPI接口控制。
由上述電路配置可知,Arduino控制板中的MCU除了要定時顯示數碼管的各個位以外,還要定時刷新MCP41010的阻值,以及掃描按鍵。我在實時操做系統uC/OS-II下來開發導納信號發生器的軟件。
5、動態正弦導納信號的產生算法
如下是本文的核心內容。
1)正弦導納表格的產生
既然是「導納信號發生器」,就應該使測試端子Ts+和Ts-之間的動態導納成正弦變化。但做爲一款「數字電位器」,MCP41010是將本身的總阻值(10KΩ±2KΩ)均分爲256份。MCU經過指令給MCP41010的數值每增長「1」,其抽頭和某一端的阻值就增長約40Ω,即每一個LSB對應的阻值相等,而非導納相等。
顯然當阻值較小時,數字電位器的每一個LSB變化所引發的導納變化較大。所以在數字電位阻值處,導納分辨率也較低。經過一段Matlab代碼來計算產生正弦導納所需的數字電位數值。
% 本腳本用於產生導納模擬器數字電位器所需的數值 %數字電位MCP41010的阻值爲1-10k歐姆,取中間阻值做爲導納正弦變化的0值 MAX_R = 10*10^3; MID_R = MAX_R*0.5;%MID_R決定了計算產生的導納以哪一個值爲中心 MID_ADMIT = 1/MID_R;%導納的平均值(中間值) MIN_ADMIT = 1/MAX_R;%導納的最小值等於最大的阻值的倒數 %AMP_ADMIT = (MID_ADMIT - MIN_ADMIT)*0.98;%導納的變化幅度爲最大幅度的98% AMP_ADMIT = (MID_ADMIT - MIN_ADMIT)*0.8;%只使用了導納的變化幅度的80% N = 64;%表格中的數據點數 i = 1 : N; sig_admit = AMP_ADMIT*sin(2*pi*i/N) + MID_ADMIT; R_data8_admin = round(sig_admit.^(-1));%計算產生正弦變化的導納所需的阻值 plot(R_data8_admin,'*-r');grid on; title('導納正弦變化所需的阻值(單位歐姆)'); %將上述阻值折算爲MCP41010所需的0~255的設定值 R_CODE_MCP4XXXX = round((R_data8_admin/MAX_R)*256); %反過來計算這些數值所對應的導納值 admit_true = (R_CODE_MCP4XXXX/256 * MAX_R).^(-1); figure(); plot(admit_true,'*-b');grid on; title('MCP41010變化引發的實際導納變化(單位西門子)');
上面的代碼取MCP41010阻值範圍的一個值MID_R對應的導納MID_ADMIT做爲要產生的正弦導納值的平均值。因爲正弦導納信號中高於平均值的部分的導納值和低於平均值的部分的導納值是對應相等的,但數字電位器阻值較小的部分所對應的導納範圍顯然較寬,所以用正半周內最大導納值MAX_ADMIT減去平均值MID_ADMIT獲得的導納變化幅度的一部分做爲正弦導納信號的幅度AMP_ADMIT。
AMP_ADMIT = (MID_ADMIT - MIN_ADMIT)*0.8;%使用了導納的變化幅度的80%
取MID_ADMIT爲MCP41010的中間阻值所對應的導納,而AMP_ADMIT爲可能的最大振幅的80%。
上面代碼產生的「導納正弦變化所需的阻值(單位歐姆)」以下圖。
圖4 導納正弦變化所需的阻值
從圖中可知,在阻值的較小的一半(上圖下半截),較小的阻值變化就能引發和阻值較大的一半(上圖上半截)相同的導納變化。所以爲產生上下對稱的導納值,上圖的下半截被壓縮得「較窄」。如前所述,這一現象將致使在阻值較小的下半段,數字電位的分辨率不足。上述Matlab代碼對這一現象進行了仿真,獲得了下圖所示的「MCP41010變化引發的實際導納變化(單位西門子)」。從圖中可知導納發生器的產生的正弦變化幅度爲(2.0±0.5)×10-4S,簡單表示爲:
圖5 MCP41010變化引發的實際導納變化
能夠看到在阻值較小的上半部分,導納變化的正弦曲線存在明顯失真。若感受這種程度的失真沒法達到設計要求,則只能更換分辨率更高的數字電位器了。
2)程控導納變化頻率的實現
人體導納變化主要由心臟搏動引發,所以變化頻率在10Hz之內。爲了對不一樣頻率的導納變化進行頻率定標,須要導納信號發生器可以產生頻率穩定且可調的導納變化信號。提到頻率可調,天然想到DDS算法。DDS算法能夠描述爲下列公式。
其中,fout是算法輸出的信號頻率,fclk是算法刷新的速度,2N是DDS算法查找表(LUT)的長度,而delta則是算法在查找表中每次跳過的點數。程序只須要修改M就能夠產生和delta成正比的輸出頻率fout。每次在爲方便uC/OS-II下的程序設計,我將uC/OS-II的系統時鐘設爲1KHz,並把刷新MCP41010的阻值的操做放在鉤子函數OSTimeTickHook();中,DDS的時鐘fclk也就是1KHz。取N爲16,即查找表的長度爲2N =65536。根據公式(5),fout的分辨率約爲0.015Hz——遠高於頻率定標所需的頻率精度。鉤子函數中DDS算法的實現代碼以下。
1 void OSTimeTickHook (void) 2 { 3 unsigned char MCP41XXX_1st_byte,MCP41XXX_2nd_byte;//須要經過SPI口發送給MCP41XXXX的兩個字節 4 MCP41XXX_1st_byte = 0X11;//對電位器1執行寫數據操做 5 MCP41XXX_2nd_byte = 0x00;//第二個字節是須要寫入的電阻數值 6 /////如下DDS算法產生頻率可調節的正弦導納值//////////// 7 unsigned short delta;//DDS算法的地址表增量 8 delta = admit_frq*65536/1000;//根據DDS算法,地址表增量等於目標頻率admit_frq乘以表格長度L,再除以採樣率fs 9 dds_adder = dds_adder + delta;//累加器增長並天然溢出 10 MCP41XXX_2nd_byte = 256 - sin_admit_tbl[dds_adder>>10];//數值表格只有64個數,也就是6位地址能夠覆蓋 11 //最後用256減去查表的值是由於電路圖畫的有問題:輸入MCP41XXX的數值是PW和PB之間的值, 12 //但電路圖將MCP41XXX鏈接成變阻器的方法是將PW和PB短路,從而外部獲得的是PW和PA的值,因此將數值反轉後才能獲得正確的值 13 CS_RP = 0; 14 SPIx_ReadWriteByte(MCP41XXX_1st_byte); 15 SPIx_ReadWriteByte(MCP41XXX_2nd_byte); 16 CS_RP = 1; 17 }
sin_admit_tbl[]是上面的Matlab代碼產生的長度爲64個點的MCP41010數值表,它們對應的導納值是一個正弦變化的週期。其索引dds_adder長度是16位的,但sin_admit_tbl[]的地址只有6位(64個點),所以將dds_adder右移10位做爲sin_admit_tbl[]的地址。admit_frq是DDS算法但願產生的信號頻率,至關於(5)式中的fout。admit_frq做爲全局變量,其值是在鍵盤任務中修改的。
6、uC/OS-II下代碼的實現
因爲刷新數字電位的任務在鉤子函數中完成,uC/OS-II中只須要兩個任務:1)「鍵盤任務」TaskKEY();負責掃描四個按鍵,並根據輸入刷新靜態導納r_sw_num和動態導納頻率admit_frq這兩個全局變量。2)「刷新顯示和輸出任務」TaskFLASH_DIS();負責定時地、逐位地刷新數碼管上顯示的內容,以及靜態導納電路中須要打開的固態繼電器。
鍵盤任務代碼以下所示。
1 void TaskKEY(void *pdata) 2 { 3 while(1) 4 { 5 OSTimeDly(20); 6 if(KEY1 == 0) 7 { 8 OSTimeDly(20);//消除按鍵抖動 9 if(KEY1 == 0) 10 { 11 if(admit_frq < MAX_FRQ) 12 admit_frq++; 13 while(KEY1 == 0)OSTimeDly(10);//一直等待到按鍵被釋放,最後的延時還能消抖動 14 } 15 } 16 if(KEY2 == 0) 17 { 18 OSTimeDly(20);//消除按鍵抖動 19 if(KEY2 == 0) 20 { 21 if(admit_frq > MIN_FRQ) 22 admit_frq--; 23 while(KEY2 == 0)OSTimeDly(10);//一直等待到按鍵被釋放,最後的延時還能消抖動 24 } 25 } 26 if(KEY3 == 0) 27 { 28 OSTimeDly(20);//消除按鍵抖動 29 if(KEY3 == 0) 30 { 31 if(r_sw_num < MAX_SW) 32 r_sw_num++; 33 while(KEY3 == 0)OSTimeDly(10);//一直等待到按鍵被釋放,最後的延時還能消抖動 34 } 35 } 36 if(KEY4 == 0) 37 { 38 OSTimeDly(20);//消除按鍵抖動 39 if(KEY4 == 0) 40 { 41 if(r_sw_num > 0) 42 r_sw_num--; 43 while(KEY4 == 0)OSTimeDly(10);//一直等待到按鍵被釋放,最後的延時還能消抖動 44 } 45 } 46 } 47 }
刷新顯示和輸出任務代碼以下所示。其中顯示緩存dis_buff[]中對應的是須要顯示的每一個數碼管位的內容。dis_buff[]的內容須要不斷計算、刷新,以防鍵盤任務在用戶操做時修改須要顯示的值。變量first_byte,second_byte,third_byte中的值則是須要經過串行口下載到三隻74HC595中的。其中既包括當前須要顯示的數碼管位的字形碼D0-D7,也包括顯示的位置選通訊號DIG0-DIG3和固態繼電器的開關信號SWs1- SWs10,其對應關係請參見圖3中電路網絡標號。
1 void TaskFLASH_DIS(void *pdata) 2 { 3 unsigned char dis_buff[4],i; 4 unsigned short sw_ctl_bits=0;//低10位對應100歐姆導納電阻的開關狀態 5 unsigned char dis_index = 0;//刷新數碼管到第幾位的計數器 6 unsigned char bit_sel = 1;//決定哪位被選中顯示,必須有一個位爲1 7 unsigned char first_byte,second_byte,third_byte; 8 while(1) 9 { 10 //////////先刷新須要595輸出的全部東西的數值/////////// 11 dis_buff[3] = r_sw_num/10;//數碼管的右半邊兩個顯示打開的100歐姆電阻數目 12 dis_buff[2] = r_sw_num%10; 13 if(dis_buff[3] == 0)//處理十位的消隱問題 14 dis_buff[3] = LED_PATT[10];//消隱顯示 15 else 16 dis_buff[3] = LED_PATT[dis_buff[3]];//查字型表 17 dis_buff[2] = LED_PATT[dis_buff[2]];//查字型表 18 dis_buff[1] = admit_frq/10;//數碼管的左半邊兩個顯示導納變化的頻率 19 dis_buff[0] = admit_frq%10; 20 if(dis_buff[1] == 0)//處理十位的消隱問題 21 dis_buff[1] = LED_PATT[10];//消隱顯示 22 else 23 dis_buff[1] = LED_PATT[dis_buff[1]];//查字型表 24 dis_buff[0] = LED_PATT[dis_buff[0]];//查字型表 25 //由打開的100歐姆電阻數r_sw_num,決定打開的位 26 sw_ctl_bits = 0; 27 if(r_sw_num != 0) 28 { 29 for(i = 0;i < r_sw_num;i++) 30 { 31 sw_ctl_bits = sw_ctl_bits<<1; 32 sw_ctl_bits++;//將最低位置一,並左移一位 33 } 34 } 35 bit_sel = 0x01 << dis_index;//刷新到哪一位,就讓對應的位選信號爲1 36 //////////刷新595輸出,每次讓一個數碼管亮/////////// 37 third_byte = dis_buff[dis_index];//第三個送出的字節是字形碼,但具體是那個位的,要由當前掃描到的位置決定 38 ///////第二個字節的最高兩個位控制100歐姆導納電阻的最低兩個開關,第四位是數碼管的位選通訊號 39 second_byte = (sw_ctl_bits & 0x0003);//第二個字節中只去最低兩個位 40 second_byte = second_byte << 6;//將最低兩個位放到最高兩個位置 41 second_byte = second_byte + bit_sel;//第四位是位選通訊號 42 first_byte = (sw_ctl_bits >> 2) & 0x00ff;//取開關控制位的3-10位 43 send_to_tri_74595(first_byte,second_byte,third_byte);//從模擬SPI口送出三個準備好的字節 44 if(dis_index < 3)//更新下一個須要刷新的位置 45 dis_index++; 46 else 47 dis_index = 0; 48 OSTimeDly(5);//刷新速度是5個時鐘週期也就是200Hz 49 } 50 }
七、驗證電路
常見的萬用表、示波器等儀表一般能夠直接測量阻值和電壓值,不能直接測量導納和電流。所以須要一個電路來驗證導納信號發生器產生的信號是否符合設計要求。採用下列由運算放大器爲主構成的電路來將導納值變換成電壓值,來驗證上述設計的正確性。
圖6 將導納轉換爲方便測量和觀察的電壓信號的電路
其中,RL是導納信號發生器。VREF_-0.1V是由電壓基準芯片分壓後,再由跟隨器產生的標準-0.1V電壓。使用時尤爲要注意,導納信號發生器的GND和這裏的GND不是同一GND,導納信號發生器和上圖必定要分開供電(好比,上圖電路採用電池供電)不然必定會形成短路和工做不正常。
根據虛短原理,運算放大器OPAB的反向輸入端被鉗置在0電平。則由左至右流過RL的電流等於0.1/RL,改寫爲導納YL後有:
又由虛短原理,流過電阻R1後,運算放大器OPAB的輸出爲:
也就是使運算放大器OPAB的輸出電壓正比於導納信號發生器輸出的阻抗。將(3)、(2)和(4)式代入上式獲得下式。
上式中K是打開的固態繼電器的數量,ω是正弦變化導納的角速度,單位爲V(伏特)。第一項表明靜態導納,第二項表明動態導納。與人體導納類似,動態導納約爲靜態導納的百分之一。此時經過萬用表測量OPAB的輸出,能夠發現每當導納信號發生器在鍵盤控制下多打開一個固態繼電器開關,輸出的直流電壓就增長0.5V。
爲了進一步驗證動態導納的正確性,須要將上式信號中的第二項(交流部分)放大100倍左右。運算放大器OPAA就是這個交流放大器,Cac是隔直電容,二極管D一、D2起到儘快穩定交流放大器工做點的做用,OPAA接成同相放大形式,以提升輸入阻抗。下圖是從示波器上觀測到的OPAA的輸出。
圖7 用示波器觀測動態導納變化
與預想的相同,OPAA的輸出是一個稍有噪聲的正弦信號,該正弦信號頻率隨導納信號發生器的設置的改變而改變。圖6中的OPAB被鏈接成了反相放大形式,圖7中的負半周對應圖5中理想正弦波形的正半周。能夠看到圖7的實測波形的負半周確實分辨率較正半周低。另外,因爲Matlab生成阻值代碼表格中的點數只有64點,實測波形的連續性也不十分理想。但對於人體導納測試應用這已經足夠了,如有更高需求,能夠適當增長阻值表格的長度。