控制系統(tǒng):原理圖+代碼+仿真開源實(shí)戰(zhàn))
1. 從“監(jiān)護(hù)”到“調(diào)控”這個(gè)升級(jí)版到底改了什么先說說我為什么對(duì)這個(gè)項(xiàng)目特別上心。兩年多以前我做過一版智能輸液監(jiān)護(hù)系統(tǒng)說白了就是一個(gè)能實(shí)時(shí)顯示滴速、液位低了會(huì)報(bào)警的“電子觀察員”。當(dāng)時(shí)做完發(fā)給幾個(gè)做醫(yī)療器械的朋友看反饋很一致能看、能報(bào)但不能做任何事。護(hù)士趕過來之前管路照樣可能回血?dú)馀菡諛涌赡苓M(jìn)血管。報(bào)警只能減少發(fā)現(xiàn)時(shí)間不能消除風(fēng)險(xiǎn)。所以這版“升級(jí)版”的核心邏輯變了我要做的不是一個(gè)更聰明的報(bào)警器而是一個(gè)能閉環(huán)干預(yù)的調(diào)控系統(tǒng)。所謂閉環(huán)就是傳感器采集數(shù)據(jù)主控芯片做決策執(zhí)行機(jī)構(gòu)立刻動(dòng)作動(dòng)作結(jié)果再被傳感器感知形成一個(gè)完整的控制回路。落在輸液場(chǎng)景里就是三件事實(shí)時(shí)測(cè)滴速紅外對(duì)射傳感器檢測(cè)液滴滴落動(dòng)態(tài)調(diào)滴速步進(jìn)電機(jī)驅(qū)動(dòng)蠕動(dòng)泵轉(zhuǎn)速不夠就加壓轉(zhuǎn)速太快就減壓異常自動(dòng)處理氣泡、堵管、液位低時(shí)自動(dòng)夾斷管路并報(bào)警這個(gè)定位決定了整個(gè)項(xiàng)目的架構(gòu)。它不再是“傳感器單片機(jī)buzzer”那種課設(shè)級(jí)別的東西而是包含了感知層、決策層、執(zhí)行層、人機(jī)交互層和通信層的完整嵌入式系統(tǒng)。標(biāo)題里寫的“代碼原理圖仿真”三件套其實(shí)就是把這一整套東西從硬件到軟件到驗(yàn)證手段全部開源出來。順便說一下適用人群。這個(gè)項(xiàng)目特別適合下面幾類人正在做電子設(shè)計(jì)競(jìng)賽、課程設(shè)計(jì)但不想做“萬年溫度計(jì)”的學(xué)生想了解醫(yī)療電子基礎(chǔ)設(shè)計(jì)流程的嵌入式工程師還有想學(xué)習(xí)“傳感器電機(jī)控制PID調(diào)節(jié)”這套完整鏈路怎么落地的愛好者。如果你只想跑個(gè)流水燈那這篇文對(duì)你來說過度了但如果你想認(rèn)真啃下一個(gè)有點(diǎn)分量的STM32項(xiàng)目這篇內(nèi)容應(yīng)該能幫你在思路上省不少時(shí)間。2. 硬件設(shè)計(jì)思路原理圖里的每一個(gè)器件都不是隨便放的2.1 傳感器選型怎么用紅外對(duì)射管測(cè)出一滴液體的掉落滴速檢測(cè)是整個(gè)項(xiàng)目里最容易被低估的部分。很多人第一反應(yīng)是“光電對(duì)射管一擋不就檢測(cè)到了嗎”實(shí)際做起來根本不是這么回事。我用的方案是紅外對(duì)射傳感器槽型光耦或分離式對(duì)射管卡在滴壺兩側(cè)。液滴從滴壺中落下時(shí)會(huì)短暫遮擋紅外光線接收端輸出電平就會(huì)產(chǎn)生一次跳變。單片機(jī)的定時(shí)器輸入捕獲引腳捕獲到這個(gè)跳變就可以計(jì)算相鄰兩次滴落的時(shí)間間隔換算成滴速滴速滴/分 60000 / 滴落間隔毫秒。但這里有幾個(gè)很坑的細(xì)節(jié)。第一滴壺是透明的但不是絕對(duì)透明的紅外光穿過時(shí)本身就有衰減。第二液滴不是一個(gè)完美的圓球下落過程中可能會(huì)有拖尾一次滴落可能產(chǎn)生多次遮擋。第三環(huán)境光里的紅外分量會(huì)干擾接收管導(dǎo)致誤觸發(fā)。原理圖里我為這個(gè)傳感器專門加了一個(gè)電壓比較器整形電路。傳感器輸出先經(jīng)過一個(gè)運(yùn)放或比較器LM393做閾值判斷把模擬信號(hào)整形成干凈的數(shù)字方波再進(jìn)STM32的GPIO。閾值電壓用可調(diào)電阻設(shè)置這樣可以適應(yīng)不同滴壺的透光率。這個(gè)電路看起來多花了幾個(gè)器件實(shí)際上省掉了后面軟件濾波的無數(shù)麻煩。對(duì)射管輸出那種毛刺信號(hào)直接進(jìn)單片機(jī)的話中斷會(huì)被觸發(fā)到懷疑人生。2.2 執(zhí)行機(jī)構(gòu)蠕動(dòng)泵和步進(jìn)電機(jī)是怎么配合的滴速調(diào)控靠什么靠改變輸液管路的壓力差。常見做法是蠕動(dòng)泵——步進(jìn)電機(jī)帶動(dòng)滾輪周期性擠壓輸液管把管中的液體往前推。電機(jī)轉(zhuǎn)速越快單位時(shí)間推過去的液體越多滴速也就越快。原理圖里我選的是一體化的步進(jìn)電機(jī)驅(qū)動(dòng)方案電機(jī)驅(qū)動(dòng)芯片用的DRV8825也可以換A4988引腳兼容。為什么用這兩顆而不是直接用L298N因?yàn)槿鋭?dòng)泵需要的扭矩不大但需要細(xì)分控制來降低振動(dòng)和噪音。DRV8825最高支持32細(xì)分能讓步進(jìn)電機(jī)走得非常平滑。L298N那種大電流H橋驅(qū)動(dòng)在這個(gè)場(chǎng)景里純屬殺雞用牛刀而且還發(fā)熱。注意一個(gè)重要問題步進(jìn)電機(jī)是感性負(fù)載啟停瞬間會(huì)產(chǎn)生反向電動(dòng)勢(shì)。DRV8825雖然內(nèi)部有續(xù)流二極管但電源端我還是加了大容量電解電容100μF以上和TVS管。不加的話電機(jī)急停時(shí)電源電壓可能被拉到一個(gè)離譜的值輕則復(fù)位重則打壞主控。這個(gè)在原理圖里是很小一筆但實(shí)際項(xiàng)目里我吃過虧后面實(shí)測(cè)部分會(huì)細(xì)說。2.3 安全冗余電磁閥、液位傳感器和氣泡檢測(cè)的接線邏輯醫(yī)療設(shè)備最講究的就是“萬一”。萬一主控死機(jī)了怎么辦萬一電機(jī)失控一直擠管子怎么辦所以這版設(shè)計(jì)里我加了三重保護(hù)電磁閥串接在輸液管路上正常輸液時(shí)通電打開一旦檢測(cè)到異常立即斷電靠彈簧力夾斷管路。液位傳感器貼在滴壺外壁檢測(cè)液位低于閾值時(shí)給出信號(hào)。這里用的是電容式非接觸液位傳感器不用接觸液體避免污染。氣泡檢測(cè)用另一組紅外對(duì)射管檢測(cè)管路中是否有連續(xù)的氣泡通過。氣泡和液滴的光學(xué)特征不同氣泡會(huì)導(dǎo)致接收信號(hào)出現(xiàn)一個(gè)持續(xù)時(shí)間更長(zhǎng)、幅度更大的遮擋波形通過脈沖寬度來區(qū)分。接線邏輯上電磁閥和蜂鳴器驅(qū)動(dòng)必須用三極管或MOS管原因是STM32的GPIO輸出能力很弱灌電流也就幾毫安直接推不動(dòng)電磁閥線圈和蜂鳴器。原理圖里我用的是ULN2003達(dá)林頓管陣列一顆芯片搞定多個(gè)執(zhí)行器驅(qū)動(dòng)內(nèi)部自帶續(xù)流二極管可以直接驅(qū)動(dòng)繼電器和電磁閥。這個(gè)細(xì)節(jié)看圖的時(shí)候可以留意一下這是很多新手抄原理圖最容易抄錯(cuò)的地方——拿著單片機(jī)的IO口直接去接繼電器然后發(fā)現(xiàn)單片機(jī)復(fù)位、發(fā)熱、甚至燒毀。2.4 供電樹和通信接口為什么需要一個(gè)像樣的電源設(shè)計(jì)整個(gè)系統(tǒng)的供電情況是這樣的步進(jìn)電機(jī)和電磁閥需要12V單片機(jī)、傳感器、OLED屏幕需要3.3V或5V。如果用一個(gè)12V適配器供電板上必須有可靠的降壓鏈路。我用的是MP1584降壓模塊做12V轉(zhuǎn)5V再用AMS1117-3.3做5V轉(zhuǎn)3.3V。12V直接進(jìn)電機(jī)驅(qū)動(dòng)5V給傳感器和邏輯電路3.3V給主控。三級(jí)供電結(jié)構(gòu)清晰每個(gè)節(jié)點(diǎn)的電流余量都留了50%以上。通信接口方面除了板載的串口用于調(diào)試還預(yù)留了一個(gè)RS485接口和WiFi模塊插座。RS485用在病房組網(wǎng)場(chǎng)景多個(gè)輸液泵通過一條雙絞線掛到護(hù)士站W(wǎng)iFi模塊ESP8266或ESP01S則用于數(shù)據(jù)上云。這兩個(gè)接口在原理圖上都是可選的不焊模塊系統(tǒng)照常運(yùn)行焊上模塊就能擴(kuò)展聯(lián)網(wǎng)功能。這就叫“為升級(jí)留接口”比把所有功能堆在一塊板子上要靈活得多。3. 軟件架構(gòu)拆解狀態(tài)機(jī)思維是這類項(xiàng)目的靈魂3.1 主程序框架不能是裸奔的while循環(huán)我在很多開源項(xiàng)目里看到過這樣的代碼一個(gè)大while循環(huán)里面順序跑LCD刷新、傳感器讀取、按鍵掃描最后delay一下。這在小項(xiàng)目里沒啥問題但在這個(gè)系統(tǒng)里絕對(duì)不行。為什么因?yàn)榈嗡贉y(cè)量需要精確的定時(shí)器捕獲控制算法需要周期性執(zhí)行按鍵需要響應(yīng)顯示需要刷新報(bào)警需要優(yōu)先處理——這些任務(wù)對(duì)實(shí)時(shí)性的要求完全不同。這版代碼我采用的是前后臺(tái)架構(gòu) 定時(shí)器中斷驅(qū)動(dòng)。主循環(huán)后臺(tái)負(fù)責(zé)跑狀態(tài)機(jī)和處理低速任務(wù)顯示刷新、按鍵掃描、通信處理。定時(shí)器中斷和外部中斷前臺(tái)負(fù)責(zé)處理高實(shí)時(shí)性任務(wù)滴速捕獲、滴速計(jì)算、控制周期執(zhí)行、報(bào)警觸發(fā)。舉例來說我用TIM2做輸入捕獲捕獲紅外傳感器的滴落邊沿信號(hào)。每次捕獲中斷里更新時(shí)間戳計(jì)算滴落間隔然后更新一個(gè)全局變量current_drip_interval。主循環(huán)里做滴速控制時(shí)直接讀這個(gè)變量。TIM3則產(chǎn)生1ms的時(shí)基中斷用來做控制算法的周期調(diào)度。這樣設(shè)計(jì)的好處是無論主循環(huán)里顯示刷新多慢滴速測(cè)量都不會(huì)丟信號(hào)。3.2 主狀態(tài)機(jī)待機(jī)、校準(zhǔn)、輸液、異常、完成的流轉(zhuǎn)條件系統(tǒng)的核心邏輯是一個(gè)狀態(tài)機(jī)每個(gè)狀態(tài)都有明確的進(jìn)入條件和退出條件。這是嵌入式軟件里最值得學(xué)習(xí)的部分比任何炫技的算法都重要。我定義的狀態(tài)如下狀態(tài)含義進(jìn)入條件退出條件IDLE待機(jī)上電按下啟動(dòng)鍵CALIB校準(zhǔn)啟動(dòng)完成滴速校準(zhǔn)INFUSING輸液運(yùn)行校準(zhǔn)完成達(dá)到目標(biāo)量/按暫停PAUSE暫停按暫停鍵按繼續(xù)鍵ALARM異常報(bào)警檢測(cè)到任一異常人工復(fù)位DONE輸液完成達(dá)到目標(biāo)滴數(shù)人工復(fù)位為什么需要狀態(tài)機(jī)因?yàn)樵O(shè)備在不同狀態(tài)下對(duì)同一個(gè)輸入的處理方式完全不同。比如滴速傳感器在CALIB狀態(tài)下出現(xiàn)的脈沖是校準(zhǔn)參考在ALARM狀態(tài)下出現(xiàn)的脈沖可能就沒人關(guān)心再比如按鍵的短按在IDLE狀態(tài)下是啟動(dòng)在INFUSING狀態(tài)下可能就是暫停。如果用一堆if else去管理這些邏輯代碼會(huì)變成一團(tuán)亂麻而且很容易出現(xiàn)“按這個(gè)鍵沒反應(yīng)”的詭異bug。狀態(tài)機(jī)把系統(tǒng)的行為約束得很清晰每個(gè)狀態(tài)只處理自己關(guān)心的事件這是醫(yī)療設(shè)備軟件里非常核心的設(shè)計(jì)思路。3.3 核心算法滴速PID調(diào)節(jié)與異常判定閾值滴速調(diào)節(jié)我最初用的是增量式PID后來實(shí)際測(cè)試發(fā)現(xiàn)光PID不夠還需要加一個(gè)前饋控制。原因是蠕動(dòng)泵的流量和轉(zhuǎn)速之間雖然不是線性關(guān)系但在正常工作區(qū)間內(nèi)近似線性。如果目標(biāo)滴速?gòu)?0滴/分突然跳到60滴/分單純靠PID慢慢調(diào)節(jié)要花很長(zhǎng)時(shí)間才能穩(wěn)定但如果先根據(jù)“目標(biāo)滴速-當(dāng)前滴速”查表給出一個(gè)初始轉(zhuǎn)速再讓PID去修正誤差收斂速度會(huì)快很多。PID參數(shù)我是這樣調(diào)的先把積分項(xiàng)去掉只留比例項(xiàng)從小到大調(diào)Kp直到系統(tǒng)出現(xiàn)等幅振蕩記錄此時(shí)的臨界增益和振蕩周期然后根據(jù)Ziegler-Nichols經(jīng)驗(yàn)公式算出Ki和Kd。在這個(gè)系統(tǒng)里最終用的參數(shù)大約是Kp8.5、Ki0.4、Kd12。但要注意這只是針對(duì)我這種蠕動(dòng)泵和管路的參數(shù)換一套機(jī)械結(jié)構(gòu)就必須重新整定。異常判定閾值方面我設(shè)定了幾組經(jīng)驗(yàn)值滴速偏差超過目標(biāo)值±15%持續(xù)5秒認(rèn)為堵管或失控滴落間隔2000ms即滴速低于30滴/分認(rèn)為管路堵塞或滴壺液位過低氣泡檢測(cè)脈沖寬度超過500ms認(rèn)為有連續(xù)氣泡液位傳感器輸出低電平認(rèn)為液位過低理論上這些閾值應(yīng)該做成可配置參數(shù)存在Flash里方便護(hù)士根據(jù)不同藥液調(diào)整這也是醫(yī)療設(shè)備的基本要求。我在代碼里預(yù)留了參數(shù)存儲(chǔ)區(qū)考究一點(diǎn)的還會(huì)加上參數(shù)校驗(yàn)防止Flash數(shù)據(jù)損壞導(dǎo)致設(shè)備亂跑。4. 仿真與調(diào)試沒有硬件也能跑通90%的邏輯4.1 用Proteus搭建仿真工程傳感器怎么模擬這個(gè)項(xiàng)目另外一個(gè)重點(diǎn)就是仿真。很多人對(duì)Proteus仿真的理解還停留在“畫個(gè)電路放個(gè)單片機(jī)點(diǎn)運(yùn)行看流水燈”的階段。實(shí)際上Proteus足夠模擬這個(gè)輸液系統(tǒng)的絕大部分行為關(guān)鍵是傳感器怎么在仿真里模擬。紅外對(duì)射傳感器在Proteus里沒法用真實(shí)器件搭我的辦法是用信號(hào)發(fā)生器或脈沖源代替。滴速檢測(cè)傳感器的輸出是一系列脈沖脈沖間隔對(duì)應(yīng)滴落間隔。比如模擬60滴/分時(shí)就用一個(gè)頻率為1Hz的方波信號(hào)源接到STM32的輸入捕獲引腳。模擬滴速變化時(shí)用兩個(gè)信號(hào)源通過模擬開關(guān)切換或者直接寫一個(gè)簡(jiǎn)單的微控制器仿真模型讓它按預(yù)設(shè)曲線輸出變化的頻率信號(hào)。步進(jìn)電機(jī)在Proteus里也有現(xiàn)成的模型但要注意普通的步進(jìn)電機(jī)模型看不到轉(zhuǎn)速和扭矩。我做仿真時(shí)用的是虛擬終端邏輯分析儀來驗(yàn)證電機(jī)控制邏輯是否對(duì)觀察PWM波形的頻率和占空比是否符合預(yù)期。這樣雖然不能“看到”實(shí)際的蠕動(dòng)泵擠壓效果但能證明單片機(jī)輸出的控制信號(hào)是對(duì)的。4.2 仿真能驗(yàn)證什么、不能驗(yàn)證什么仿真最大的價(jià)值是驗(yàn)證軟件邏輯最大的坑是讓人誤以為硬件可以直接照搬。我的建議是用仿真驗(yàn)證以下三部分第一驗(yàn)證狀態(tài)機(jī)邏輯。按鍵輸入、狀態(tài)跳轉(zhuǎn)、報(bào)警觸發(fā)條件這些純邏輯的東西仿真和實(shí)物應(yīng)該完全一致。第二驗(yàn)證PID算法。把滴速傳感器用脈沖源模擬通過示波器觀察控制周期里PWM占空比的變化曲線看是否按照預(yù)期邏輯調(diào)節(jié)。第三驗(yàn)證顯示和通信。OLED在Proteus里有仿真模型串口通信也可以直接連虛擬終端看輸出。但仿真驗(yàn)證不了的是真實(shí)的信號(hào)質(zhì)量、電源紋波和電磁兼容。比如滴速傳感器的信號(hào)整形電路在實(shí)物中要處理的噪聲問題在仿真里完全體現(xiàn)不出來步進(jìn)電機(jī)啟停瞬間的電源跌落仿真里也沒有紅外對(duì)射管在不同光照下的行為差異仿真更是無從談起。所以正確的開發(fā)流程是仿真驗(yàn)證邏輯實(shí)物驗(yàn)證電路兩條線并行推進(jìn)而不是互相替代。4.3 配合Keil和STM32CubeMX的開發(fā)效率提升代碼部分我用了STM32CubeMX做外設(shè)初始化然后用Keil MDK寫業(yè)務(wù)邏輯。這樣分工的原因是CubeMX生成的初始化代碼非常規(guī)范尤其時(shí)鐘樹、GPIO復(fù)用、定時(shí)器配置這些手寫很容易漏配置某一路時(shí)鐘導(dǎo)致“明明代碼沒錯(cuò)但外設(shè)不工作”的怪現(xiàn)象。而業(yè)務(wù)層代碼自己寫邏輯控制更清晰。具體流程是在CubeMX里勾選需要的引腳和外設(shè)配置好定時(shí)器輸入捕獲、PWM輸出、UART、I2COLED生成MDK工程然后在這個(gè)框架下添加自己的業(yè)務(wù)代碼。要注意CubeMX生成的main函數(shù)里有一個(gè)while(1)業(yè)務(wù)代碼不是堆在里面而是拆成模塊文件比如drip_detect.c、motor_control.c、state_machine.c、alarm.c、display.cmain里只做初始化和狀態(tài)機(jī)調(diào)度。這樣每個(gè)模塊可以獨(dú)立測(cè)試也方便別人直接看代碼結(jié)構(gòu)。另外提一句日常開發(fā)環(huán)境的小事。如果是用ST-Link調(diào)試在Keil里容易遇到“no stm32 target found”的報(bào)錯(cuò)尤其是“if your product embeds debug authentication”這種提示。多數(shù)情況不是芯片燒了而是SWD引腳被代碼復(fù)用、接線松動(dòng)、或目標(biāo)板供電不穩(wěn)。我遇到過的場(chǎng)景是板子外接了12V電機(jī)后ST-Link的3.3V參考電壓被拉低導(dǎo)致調(diào)試器找不到芯片。解決辦法很簡(jiǎn)單調(diào)試的時(shí)候用獨(dú)立USB供電或者把ST-Link改成外接供電模式。這種小問題在開發(fā)中很常見但很煩人先排除電源問題再動(dòng)代碼。5. 實(shí)測(cè)踩坑記錄幾個(gè)只有在實(shí)物上才會(huì)暴露的問題5.1 滴速傳感器誤觸發(fā)從“瘋狂中斷”到濾波方案實(shí)物調(diào)試第一天傳感器輸出的信號(hào)就把我整懵了。滴壺里明明沒液體在滴單片機(jī)的中斷計(jì)數(shù)器卻一直在跳。用示波器一看輸出波形上疊了一堆毛刺頻率還不低。這個(gè)問題的根源是環(huán)境光。我一開始把對(duì)射管裝在滴壺上之后就直接接比較器閾值設(shè)在了中間值。但實(shí)際場(chǎng)景中燈光、窗戶反光都會(huì)引起接收管輸出波動(dòng)當(dāng)這些波動(dòng)跨過閾值時(shí)就產(chǎn)生了誤觸發(fā)。解決思路分兩步硬件上我在比較器正輸入端加了一個(gè)遲滯電阻讓比較器變成一個(gè)施密特觸發(fā)器避免信號(hào)在閾值附近反復(fù)跳變軟件上我加了一道去抖濾波——捕獲中斷里記錄這次脈沖的時(shí)間戳只有當(dāng)前后兩次脈沖間隔大于3ms才認(rèn)為是一次有效滴落。小于3ms的脈沖一律視為抖動(dòng)忽略。這個(gè)“硬件整形軟件濾波”雙重方案實(shí)測(cè)效果非常好誤觸發(fā)基本清零。這個(gè)經(jīng)驗(yàn)放到其他傳感器設(shè)計(jì)里其實(shí)同樣適用傳感器信號(hào)處理不能只靠器件選型也不能只靠軟件算法兩條腿走路才是工程做法。5.2 步進(jìn)電機(jī)一啟動(dòng)單片機(jī)就復(fù)位這是我在做實(shí)物聯(lián)動(dòng)時(shí)遇到的第二個(gè)大問題。單獨(dú)測(cè)步進(jìn)電機(jī)時(shí)一切正常單獨(dú)測(cè)主控時(shí)也一切正常兩個(gè)一連起來只要電機(jī)一轉(zhuǎn)OLED屏幕閃一下復(fù)位或者蜂鳴器“嘀”一聲重新上電。排查過程我做了好一陣。先用示波器量STM32的VDD電機(jī)啟動(dòng)瞬間電壓直接跌到了2.8V復(fù)位閾值3.0V以下單片機(jī)自然就重置了。再看12V電源紋波電機(jī)啟動(dòng)瞬間有將近1V的跌落。原因很清晰電源的瞬態(tài)響應(yīng)能力不足步進(jìn)電機(jī)啟動(dòng)時(shí)的電流沖擊直接把電壓拉垮了。解決方案是在電機(jī)驅(qū)動(dòng)板的電源輸入端并聯(lián)了大電容470μF/25V電解電容 100nF陶瓷電容同時(shí)在12V到5V的降壓模塊前后也各加了一級(jí)濾波電容。電容的存在相當(dāng)于一個(gè)蓄水池電機(jī)啟動(dòng)時(shí)先消耗電容里存儲(chǔ)的能量給電源模塊爭(zhēng)取響應(yīng)時(shí)間。這個(gè)問題之后我再也不省電源濾波電容了這在原理圖設(shè)計(jì)里看起來是“保守”實(shí)際上是必要的穩(wěn)妥。5.3 電磁閥關(guān)不斷不是電機(jī)問題是驅(qū)動(dòng)邏輯接反了電磁閥的測(cè)試也出過烏龍。我用ULN2003驅(qū)動(dòng)一個(gè)12V常開電磁閥代碼邏輯是“異常時(shí)輸出高電平讓ULN2003導(dǎo)通電磁閥得電夾斷”。結(jié)果實(shí)測(cè)是正常輸液的時(shí)候電磁閥莫名其妙發(fā)熱異常時(shí)反而沒反應(yīng)。查了半天發(fā)現(xiàn)我把電磁閥的接線接到ULN2003的公共端COM和輸出端之間而COM接的是12V輸出端導(dǎo)通時(shí)電磁閥兩端電壓差為0反而是不工作的。正確的接法是電磁閥一端接12V另一端接ULN2003的輸出端輸出端低電平時(shí)電磁閥得電動(dòng)作。這個(gè)錯(cuò)誤讓我意識(shí)到一個(gè)很重要的問題達(dá)林頓管陣列和繼電器一樣是低電平驅(qū)動(dòng)還是高電平驅(qū)動(dòng)完全取決于外部接法不要想當(dāng)然。模電基礎(chǔ)不牢的話非常容易在這里翻車。后來我在原理圖里專門標(biāo)注了驅(qū)動(dòng)方向還在代碼里留了注釋防止自己以后再看的時(shí)候犯同樣的錯(cuò)。5.4 氣泡檢測(cè)的分辨難題液體和氣泡在光學(xué)上的區(qū)分氣泡檢測(cè)模塊我一開始直接用紅外對(duì)射管放在滴壺后面的透明管段上。結(jié)果發(fā)現(xiàn)氣泡和液滴的光學(xué)特征差異不夠明顯——細(xì)小的氣泡和液滴經(jīng)過檢測(cè)區(qū)時(shí)接收信號(hào)的下降幅度幾乎一樣沒法可靠區(qū)分。后面我找了半天資料發(fā)現(xiàn)商用輸液泵的氣泡檢測(cè)大多是超聲氣泡檢測(cè)——利用超聲波在液體和氣體中傳播衰減差異巨大的特性來分辨管路中是否有氣體段。超聲波方案確實(shí)可靠但電路復(fù)雜度和成本都上去了。在不增加太多硬件成本的前提下我改用了一種折中方案把紅外對(duì)射管沿著管路徑向放置并加了一個(gè)窄縫遮光罩只允許管路中心一小條區(qū)域的光通過。液體通過時(shí)由于折射和散射接收管信號(hào)的變化幅度與氣體通過時(shí)不同。配合軟件里對(duì)脈沖寬度的判斷氣泡造成的遮擋持續(xù)時(shí)間和液滴不同總算實(shí)現(xiàn)了基本可用的氣泡分辨。說實(shí)話這個(gè)方案離商用設(shè)備的可靠性還有差距但它作為一個(gè)開源方案已經(jīng)能把“連續(xù)氣泡”這種危險(xiǎn)情況檢測(cè)出來了。6. 代碼結(jié)構(gòu)怎么安排才算“開源友好”項(xiàng)目開源大家最關(guān)心的肯定還是代碼。我見過不少“開源項(xiàng)目”解壓下來一個(gè)文件夾全是散落的.c/.h沒有任何層次新手打開直接勸退。這個(gè)項(xiàng)目的代碼結(jié)構(gòu)我花了心思整理目的是讓一個(gè)只學(xué)過單片機(jī)基礎(chǔ)的人也能順著目錄找到自己想看的部分。├── Core/ │ ├── Inc/ // 頭文件 │ └── Src/ // 系統(tǒng)初始化、中斷處理 ├── Drivers/ │ ├── STM32F1xx_HAL_Driver/ // HAL庫(kù) │ └── BSP/ // 板級(jí)支持包LED、按鍵、蜂鳴器 ├── App/ │ ├── state_machine.c/h // 主狀態(tài)機(jī) │ ├── drip_detect.c/h // 滴速檢測(cè)與計(jì)算 │ ├── motor_control.c/h // 步進(jìn)電機(jī)與蠕動(dòng)泵控制 │ ├── pid.c/h // PID算法 │ ├── alarm.c/h // 異常報(bào)警與處理 │ ├── display.c/h // OLED顯示 │ └── comm.c/h // 串口通信與協(xié)議解析 ├── Simulink/Proteus/ // 仿真工程文件 ├── Hardware/ // 原理圖、PCB │ ├── Schematic/ // 原理圖工程文件 │ └── Datasheet/ // 關(guān)鍵器件數(shù)據(jù)手冊(cè) └── Doc/ // 設(shè)計(jì)文檔、說明這個(gè)分層邏輯是Drivers層不寫業(yè)務(wù)代碼只做硬件抽象App層只調(diào)Drivers的接口不直接操作寄存器。好處是以后換個(gè)顯示屏幕、換個(gè)電機(jī)驅(qū)動(dòng)芯片只需要改BSP層App層邏輯幾乎不用動(dòng)。這也是一個(gè)合格嵌入式工程的基本素養(yǎng)。在模塊接口設(shè)計(jì)上我盡量做到“函數(shù)名見名知意”。比如DripSpeed_GetCurrent(void)、Motor_SetSpeed(uint16_t speed)、Alarm_Trigger(AlarmType_t type)。這樣即使不看實(shí)現(xiàn)細(xì)節(jié)讀代碼的人也能猜到這個(gè)模塊是干嘛的。每個(gè)模塊的頭部都有注釋說明負(fù)責(zé)什么、依賴什么、關(guān)鍵參數(shù)含義。注釋是給下一個(gè)維護(hù)者看的這個(gè)維護(hù)者很可能就是幾個(gè)月后的自己所以我都盡量寫得人性化一點(diǎn)。7. 如果你打算復(fù)現(xiàn)這個(gè)項(xiàng)目我建議按這個(gè)順序來從零開始復(fù)現(xiàn)一個(gè)這樣的項(xiàng)目如果上來就直接焊板子大概率會(huì)翻車。我的建議是按“仿真先行、模塊化驗(yàn)證、系統(tǒng)聯(lián)調(diào)”三個(gè)步驟走。第一步先把Proteus仿真工程跑起來。不要去管硬件先把代碼燒進(jìn)仿真里的STM32用信號(hào)源模擬滴速傳感器驗(yàn)證狀態(tài)機(jī)、顯示、報(bào)警這些邏輯能不能跑對(duì)。這個(gè)階段你會(huì)發(fā)現(xiàn)代碼里的很多邏輯問題改起來成本最低。第二步按模塊搭硬件。先搭最小系統(tǒng)板OLED按鍵驗(yàn)證顯示和交互正常再搭滴速檢測(cè)模塊驗(yàn)證傳感器信號(hào)整形和中斷捕獲再搭電機(jī)驅(qū)動(dòng)用開環(huán)PWM控制電機(jī)轉(zhuǎn)速確認(rèn)方向可調(diào)最后把PID控制閉環(huán)調(diào)通。每一個(gè)模塊單獨(dú)調(diào)試的時(shí)候問題都很好定位。全部模塊都通了之后再整合到一起。第三步系統(tǒng)聯(lián)調(diào)。聯(lián)調(diào)階段重點(diǎn)是看模塊間的相互影響比如電機(jī)啟動(dòng)時(shí)會(huì)不會(huì)干擾滴速檢測(cè)電源負(fù)載變化時(shí)會(huì)不會(huì)引起傳感器漂移報(bào)警時(shí)電磁閥動(dòng)作會(huì)不會(huì)導(dǎo)致主控復(fù)位。這些問題只有在全系統(tǒng)運(yùn)行時(shí)才會(huì)暴露也是最考驗(yàn)排查能力的時(shí)候。最后實(shí)物和仿真之間有差異是正常的仿真跑通了不代表實(shí)物能直接工作實(shí)物出了問題也不用推翻仿真結(jié)論——兩者驗(yàn)證的層面不同把它們當(dāng)成互補(bǔ)工具就好。這個(gè)項(xiàng)目對(duì)我來說收獲最大的地方并不在于某一個(gè)具體的功能模塊而在于它逼著我把“從傳感器到執(zhí)行機(jī)構(gòu)再到算法控制”的整個(gè)鏈路走通了一遍。醫(yī)療電子確實(shí)是容錯(cuò)率很低的領(lǐng)域但作為學(xué)習(xí)項(xiàng)目它涵蓋的知識(shí)面足夠廣難度又不會(huì)讓人徹底放棄卡在中間進(jìn)度時(shí)還能隨時(shí)用仿真回頭檢查邏輯。我把自己踩過的坑、試過的方法都攤開寫在這了如果你決定復(fù)現(xiàn)它希望這些記錄能幫你少走幾個(gè)彎路。