與標(biāo)定實(shí)戰(zhàn))
簡(jiǎn)介TSW-30濁度傳感器與STM32F103C8T6結(jié)合的Keil5工程源碼面向需要快速搭建水質(zhì)渾濁度檢測(cè)原型的嵌入式開發(fā)者適合有一定STM32基礎(chǔ)、希望掌握ADC與DMA配合使用的學(xué)習(xí)者。資源共226個(gè)文件壓縮包約7.08MB包含C源碼與頭文件、Keil工程配置文件、編譯中間文件以及技術(shù)手冊(cè)PDF和編譯燒錄文件源碼完整演示將濁度傳感器模擬信號(hào)經(jīng)ADC轉(zhuǎn)換、DMA搬運(yùn)并在串口打印渾濁度結(jié)果的過(guò)程。已有1244人下載學(xué)習(xí)。借助工程與手冊(cè)說(shuō)明讀者可直接在Keil5中打開工程進(jìn)行編譯、燒錄與調(diào)試省去環(huán)境配置時(shí)間技術(shù)手冊(cè)和工程中的代碼結(jié)構(gòu)也能幫助理解芯片外設(shè)初始化、數(shù)據(jù)采集與串口通信的具體實(shí)現(xiàn)配合實(shí)物進(jìn)行驗(yàn)證適合作為水質(zhì)監(jiān)測(cè)小項(xiàng)目或STM32進(jìn)階的參考。1. 這玩意兒到底能測(cè)什么TSW-30到手先別急著接單片機(jī)先聊點(diǎn)務(wù)實(shí)的。TSW-30濁度傳感器在國(guó)產(chǎn)傳感器里算是性價(jià)比極高的選擇十幾塊錢到二十幾塊錢就能買到配合STM32F103C8T6這顆經(jīng)典主控整套水質(zhì)濁度檢測(cè)方案的成本可以壓到五十元以內(nèi)。很多人第一次拿到這個(gè)傳感器第一反應(yīng)是找數(shù)據(jù)手冊(cè)結(jié)果發(fā)現(xiàn)商家給的資料亂七八糟有的甚至只有一張接線圖連輸出電壓和濁度的對(duì)應(yīng)關(guān)系都要靠自己猜。這篇文章我就把整個(gè)折騰過(guò)程從頭到尾捋一遍包括傳感器原理、硬件接線、STM32的ADC采集、Keil5工程搭建、數(shù)據(jù)標(biāo)定以及我在實(shí)際調(diào)試中踩過(guò)的那些坑。先說(shuō)TSW-30這個(gè)傳感器本身。它的外形是一個(gè)圓柱形探頭內(nèi)部結(jié)構(gòu)其實(shí)就是一對(duì)紅外發(fā)光管和光電接收管。工作時(shí)發(fā)光管發(fā)出一束紅外光光線穿過(guò)待測(cè)液體一部分被液體中的懸浮顆粒散射或吸收剩下的光被接收管檢測(cè)到。液體越渾濁透過(guò)的光越少接收管的光電流就越小最終表現(xiàn)為傳感器輸出端的電壓信號(hào)發(fā)生變化。TSW-30的輸出是模擬電壓信號(hào)常溫下大致范圍在0V到4.5V之間水越清電壓越高水越渾電壓越低。這個(gè)邏輯一開始容易搞反我見過(guò)不止一個(gè)新手把電壓高當(dāng)成水臟結(jié)果標(biāo)定出來(lái)的數(shù)據(jù)完全是反的。和很多數(shù)字輸出的傳感器不同TSW-30不需要I2C、UART這類通信協(xié)議也不需要額外的驅(qū)動(dòng)芯片它的輸出就是一個(gè)實(shí)實(shí)在在的模擬電壓。這意味著后端采集電路可以做得極其簡(jiǎn)單STM32內(nèi)部自帶的12位ADC直接采樣就行連運(yùn)放都可以省掉。但省錢省事的代價(jià)是你需要自己處理傳感器非線性的電壓-濁度曲線還要保證參考電壓足夠穩(wěn)定否則采出來(lái)的數(shù)據(jù)漂到你懷疑人生。那這顆傳感器適合什么場(chǎng)景我覺(jué)得主要有三類一是水質(zhì)監(jiān)測(cè)類的入門級(jí)項(xiàng)目比如魚缸水質(zhì)監(jiān)控、簡(jiǎn)易凈水器出水檢測(cè)、河流湖泊的野外浮標(biāo)采樣二是高校實(shí)驗(yàn)室的學(xué)生課題用來(lái)做畢業(yè)設(shè)計(jì)或者課程項(xiàng)目成本低、原理清晰、論文好寫三是DIY愛(ài)好者做智能家居中的水質(zhì)量檢測(cè)模塊。如果你需要檢測(cè)的是高精度實(shí)驗(yàn)室級(jí)別的濁度比如自來(lái)水廠的出廠水監(jiān)測(cè)那TSW-30的精度和長(zhǎng)期穩(wěn)定性是不夠的得考慮哈?;蚬<?jí)別的進(jìn)口產(chǎn)品。但作為學(xué)習(xí)、原型驗(yàn)證、小規(guī)模部署來(lái)說(shuō)這顆傳感器完全夠用。2. 硬件搭建STM32F103C8T6最小系統(tǒng)板的接線細(xì)節(jié)與供電注意事項(xiàng)2.1 最小系統(tǒng)板與傳感器接線STM32F103C8T6這顆芯片是Cortex-M3內(nèi)核主頻最高72MHz內(nèi)置64KB Flash和20KB SRAM12位ADC有10個(gè)通道對(duì)于濁度檢測(cè)這種低速采集應(yīng)用來(lái)說(shuō)是綽綽有余的。市面上最常見的開發(fā)載體是藍(lán)板或者黑板的最小系統(tǒng)板Blue Pill那種板載一個(gè)8MHz晶振、AMS1117穩(wěn)壓芯片、一個(gè)電源指示燈和一個(gè)用戶LED通常接在PC13。接線其實(shí)非常簡(jiǎn)單TSW-30傳感器模塊通常引出三根線紅色線電源正極接3.3V或5V具體看模塊上的標(biāo)識(shí)多數(shù)模塊支持3.3V-5V寬壓輸入黑色線電源負(fù)極接GND黃色線或藍(lán)色線模擬信號(hào)輸出接STM32的ADC輸入引腳我用的是PA1作為ADC輸入通道也就是ADC1的Channel 1這個(gè)選擇純粹是因?yàn)镻A1在板子邊緣、好插線而且不和其他常用功能沖突。如果你用的是PA0-WKUP引腳也可以只是PA0在低功耗模式下有喚醒功能睡眠時(shí)可能會(huì)有額外的漏電流不過(guò)對(duì)于這個(gè)項(xiàng)目來(lái)說(shuō)無(wú)所謂。需要注意一個(gè)細(xì)節(jié)TSW-30模塊上一般會(huì)有一個(gè)電位器這個(gè)電位器是用來(lái)調(diào)節(jié)比較器閾值的。如果你買的是不帶運(yùn)放輸出的純傳感器探頭那這個(gè)電位器可能不存在或者沒(méi)有意義。市面上常見的TSW-30模塊通常分為兩種一種是只輸出模擬電壓的裸模塊另一種是帶LM393比較器、有數(shù)字輸出DO和模擬輸出AO的雙輸出模塊。如果是后者你只需要接AO口DO口是數(shù)字閾值輸出適合直接接單片機(jī)的中斷引腳或者做簡(jiǎn)單的上下限報(bào)警不需要經(jīng)過(guò)ADC。我做項(xiàng)目時(shí)接的是AO口DO口留作擴(kuò)展。2.2 供電拓?fù)淝f(wàn)不要和舵機(jī)共用一個(gè)電源這個(gè)環(huán)節(jié)是很多人的翻車點(diǎn)。TSW-30的模擬輸出電壓會(huì)隨著電源電壓波動(dòng)而變化因?yàn)樗膬?nèi)部紅外發(fā)光管的亮度直接受供電電壓影響——電壓高一點(diǎn)光強(qiáng)就高一點(diǎn)接收管收到的光多了輸出電壓自然就上去了但這時(shí)候水質(zhì)并沒(méi)有變化。這屬于典型的共模干擾。我在測(cè)試中實(shí)測(cè)過(guò)一組數(shù)據(jù)用同一個(gè)傳感器分別接3.3V和5V供電在同樣的清水條件下輸出電壓差了將近0.4V。這說(shuō)明如果你用3.3V給傳感器供電后端就要用3.3V作為ADC參考電壓否則數(shù)據(jù)沒(méi)法看。所以我建議的供電方案是傳感器供電直接接STM32最小系統(tǒng)板的3.3V引腳或者獨(dú)立的LDO穩(wěn)壓3.3VSTM32供電USB 5V輸入經(jīng)過(guò)板載AMS1117穩(wěn)壓到3.3V千萬(wàn)別把傳感器和電機(jī)、舵機(jī)、繼電器、水泵這些器件放在同一個(gè)電源軌上。這些東西一動(dòng)作電源會(huì)瞬間被拉低幾百毫伏ADC采樣值會(huì)跟著跳。我一開始做魚缸水質(zhì)監(jiān)測(cè)時(shí)把水泵和傳感器共用一個(gè)5V電源適配器結(jié)果水泵一啟動(dòng)濁度數(shù)據(jù)瞬間從2.1V跳到2.7V不知道的人還以為是魚缸水突然變清了。后來(lái)改成獨(dú)立供電數(shù)據(jù)立刻就穩(wěn)了。如果非要共用一個(gè)電源可以在傳感器電源輸入端加一個(gè)100uF電解電容和一個(gè)104陶瓷電容并聯(lián)做去耦電容盡量靠近傳感器模塊的電源引腳。這個(gè)方法能緩解但不能完全消除電源波動(dòng)影響有條件還是建議獨(dú)立供電。2.3 信號(hào)線的抗干擾處理TSW-30的輸出阻抗不算低信號(hào)線如果走得過(guò)長(zhǎng)容易引入工頻干擾和射頻干擾ADC采樣值會(huì)呈現(xiàn)有規(guī)律的波動(dòng)。我在做野外浮標(biāo)采樣時(shí)曾把傳感器和主板用一根1.5米的杜邦線連接采樣值每秒都在跳動(dòng)波動(dòng)幅度最大的時(shí)候有50mV左右拿去換算成濁度就是好幾個(gè)NTU的誤差。解決辦法很樸素傳感器線纜盡量縮短控制在20厘米以內(nèi)并且使用雙絞線或者屏蔽線。如果必須長(zhǎng)距離傳輸建議在STM32的ADC引腳前加一個(gè)RC低通濾波器比如100歐姆電阻串聯(lián)、1uF電容對(duì)地并聯(lián)截止頻率大約在1.6kHz左右能有效濾掉大部分高頻干擾。這個(gè)RC濾波器的好處是簡(jiǎn)單、便宜、不需要額外供電缺點(diǎn)是會(huì)略微增加信號(hào)的建立時(shí)間對(duì)于濁度這種緩變信號(hào)來(lái)說(shuō)完全不是問(wèn)題。還有就是ADC引腳的采樣時(shí)間要合理設(shè)置。STM32的ADC采樣時(shí)間可以配置為1.5周期到239.5周期不等。對(duì)于高阻抗信號(hào)源采樣時(shí)間太短會(huì)導(dǎo)致采樣電容充電不完全測(cè)量值偏低。我建議把采樣時(shí)間設(shè)置為55.5周期以上保守一點(diǎn)直接拉到239.5周期。雖然采樣速度變慢了但濁度檢測(cè)根本不需要高速采樣穩(wěn)定才是第一位的。3. Keil5工程從頭搭從零創(chuàng)建一個(gè)不依賴固件庫(kù)模板的裸機(jī)ADC采集程序3.1 Keil5安裝與芯片支持包別漏掉DFP包Keil5和之前的Keil4有個(gè)很大的區(qū)別Keil5采用軟件包管理機(jī)制安裝完主程序之后還需要單獨(dú)安裝對(duì)應(yīng)芯片的Device Family PackDFP否則在新建工程時(shí)找不到STM32F103C8T6這顆芯片。我見過(guò)很多人卡在這一步明明Keil5裝好了但新建工程時(shí)Device列表里空空如也就是這個(gè)原因。具體步驟是去Keil官網(wǎng)下載MDK-ARM安裝包目前常用版本是5.36或者5.38注意區(qū)分MDK-ARM和C51版本C51是給8051單片機(jī)用的兩個(gè)可以裝在同一臺(tái)電腦上但不要裝同一個(gè)目錄安裝MDK-ARM安裝路徑建議不要有中文避免后續(xù)各種玄學(xué)問(wèn)題打開Keil5點(diǎn)擊Pack Installer圖標(biāo)在搜索框輸入STM32F1找到STM32F1 Series Device Support Package點(diǎn)擊Install如果Pack Installer下載速度很慢或者失敗可以直接去Keil官網(wǎng)的下載頁(yè)面手動(dòng)下載DFP包然后雙擊安裝這里還有一個(gè)坑Keil5在創(chuàng)建工程時(shí)如果你選擇的是STM32F103C8T6但安裝的DFP版本太老編譯時(shí)可能提示找不到某些系統(tǒng)文件。我的建議是直接用最新版的DFP比如2.4.0或更高版本。3.2 新建工程的完整流程標(biāo)準(zhǔn)外設(shè)庫(kù)方式關(guān)于用標(biāo)準(zhǔn)外設(shè)庫(kù)Standard Peripheral Library還是HAL庫(kù)這是個(gè)老生常談的問(wèn)題。我的建議是如果你是初學(xué)者或者只是做這個(gè)濁度檢測(cè)項(xiàng)目用標(biāo)準(zhǔn)外設(shè)庫(kù)就夠了代碼量少、邏輯直接、容易理解。HAL庫(kù)雖然代碼復(fù)用性好但對(duì)于這種簡(jiǎn)單的ADC采集應(yīng)用反而顯得有些臃腫。新建工程的步驟我列一下打開Keil5點(diǎn)擊Project - New uVision Project選擇工程保存路徑輸入工程名比如TSW30_Turbidity在彈出的Device窗口中選擇STMicroelectronics - STM32F1 Series - STM32F103C8Tx點(diǎn)擊OK在彈出的Manage Run-Time Environment窗口中如果不需要CMSIS的組件可以直接點(diǎn)OK跳過(guò)我們手動(dòng)添加文件在Project窗口右鍵點(diǎn)擊Target 1選擇Add Group分別創(chuàng)建三個(gè)組User、StdPeriph_Driver、Startup然后就是往分組里添加文件Startup組添加startup_stm32f10x_md.s啟動(dòng)文件這個(gè)文件可以在標(biāo)準(zhǔn)外設(shè)庫(kù)的Libraries/CMSIS/Device/ST/STM32F10x/Source/Templates/arm目錄下找到StdPeriph_Driver組添加stm32f10x_adc.c、stm32f10x_gpio.c、stm32f10x_rcc.c、stm32f10x_usart.c這些都在標(biāo)準(zhǔn)外設(shè)庫(kù)的Libraries/STM32F10x_StdPeriph_Driver/src目錄下User組添加main.c、stm32f10x_it.c、system_stm32f10x.c還需要設(shè)置C/C編譯選項(xiàng)的Include Paths把所有頭文件所在目錄添加進(jìn)去包括Libraries/CMSIS/Device/ST/STM32F10x/IncludeLibraries/CMSIS/IncludeLibraries/STM32F10x_StdPeriph_Driver/incUser另外還需要在C/C選項(xiàng)的Define欄寫上USE_STDPERIPH_DRIVER, STM32F10X_MD這兩個(gè)宏定義一個(gè)是告訴編譯器要使用標(biāo)準(zhǔn)外設(shè)庫(kù)一個(gè)是告訴編譯器芯片類型是中等容量的STM32F10x系列。漏掉這兩個(gè)宏定義編譯會(huì)直接報(bào)出一大堆錯(cuò)誤最常見的提示是stm32f10x.h頭文件里找不到器件定義。3.3 最簡(jiǎn)單的ADC采樣代碼直接上裸機(jī)寄存器操作也行如果你只是想快速驗(yàn)證傳感器有沒(méi)有輸出完全不想配置標(biāo)準(zhǔn)外設(shè)庫(kù)那么用寄存器操作其實(shí)更快。STM32的ADC1在APB2總線上時(shí)鐘來(lái)自PCLK2默認(rèn)情況下PCLK2最高72MHz。ADC自身的時(shí)鐘頻率最高14MHz所以需要通過(guò)預(yù)分頻器分頻。這里我直接給出我調(diào)試通過(guò)的配置代碼#include stm32f10x.h void ADC1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); ADC_InitStructure.ADC_Mode ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode DISABLE; ADC_InitStructure.ADC_ContinuousConvMode ENABLE; ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel 1; ADC_Init(ADC1, ADC_InitStructure); RCC_ADCCLKConfig(RCC_PCLK2_Div6); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); ADC_SoftwareStartConvCmd(ADC1, ENABLE); } uint16_t Read_ADC_Value(void) { while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); return ADC_GetConversionValue(ADC1); } int main(void) { ADC1_Init(); while(1) { uint16_t adc_value Read_ADC_Value(); // 這里可以用串口打印出來(lái)方便調(diào)試 } }這個(gè)代碼有幾個(gè)細(xì)節(jié)值得說(shuō)明。首先GPIO配置為模擬輸入模式GPIO_Mode_AIN這一步很多人搞錯(cuò)直接把引腳配置成GPIO_Mode_IN_FLOATING或者GPIO_Mode_IPD這樣ADC采出來(lái)的值會(huì)偏大因?yàn)橥獠啃盘?hào)源的阻抗和內(nèi)部上下拉電阻形成了分壓。其次ADC的校準(zhǔn)過(guò)程不能省雖然不校準(zhǔn)也能出數(shù)據(jù)但校準(zhǔn)之后精度會(huì)更好尤其是使用的芯片個(gè)體差異較大的國(guó)產(chǎn)替代芯片。3.4 串口調(diào)試輸出沒(méi)有屏幕也能看清數(shù)據(jù)ADC采到的原始值是0-4095的整數(shù)直接看數(shù)值很難直觀判斷水質(zhì)變化這時(shí)候串口就派上用場(chǎng)了。STM32F103C8T6的USART1掛載在APB2總線上PA9是TXPA10是RX接一個(gè)USB轉(zhuǎn)TTL模塊就能在電腦上看到數(shù)據(jù)。我用的是簡(jiǎn)易的printf重定向方式在代碼里加上int fputc(int ch, FILE *f) { while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); USART_SendData(USART1, (uint8_t)ch); return ch; }然后用printf(%d\r\n, adc_value);就能在串口助手里看到ADC值了。需要提醒的是使用printf需要包含stdio.h并且在Keil的Target選項(xiàng)里勾選Use MicroLIB否則會(huì)因?yàn)榉峙涠芽臻g不足導(dǎo)致串口打印內(nèi)容不完整這個(gè)小細(xì)節(jié)在校驗(yàn)和不勾選時(shí)差異很明顯。4. 從ADC原始值到NTU濁度標(biāo)定方案與數(shù)學(xué)建模4.1 為什么需要標(biāo)定以及NTU是個(gè)什么單位ADC采出來(lái)的4096個(gè)等級(jí)只是一個(gè)相對(duì)值不能直接告訴用戶這杯水是5NTU只能說(shuō)明水比剛才更渾了。NTUNephelometric Turbidity Unit散射濁度單位是濁度的標(biāo)準(zhǔn)測(cè)量單位它的測(cè)量原理是檢測(cè)光線被液體散射的強(qiáng)度。而TSW-30采用的是透射式原理測(cè)量的是透射光的衰減所以它的輸出和NTU之間并不是嚴(yán)格線性的關(guān)系需要通過(guò)標(biāo)定建立映射。市面上很多商家會(huì)給出對(duì)應(yīng)關(guān)系表但那些數(shù)據(jù)是在特定條件下測(cè)出來(lái)的換一個(gè)批次、換一個(gè)電源電壓、換一個(gè)探頭角度數(shù)據(jù)就會(huì)變化。所以最可靠的做法是自己標(biāo)定。4.2 標(biāo)定液怎么配奶粉法和福爾馬肼標(biāo)準(zhǔn)液標(biāo)準(zhǔn)做法是用福爾馬肼Formazin標(biāo)準(zhǔn)液這是美國(guó)EPA和ISO標(biāo)準(zhǔn)規(guī)定的濁度標(biāo)準(zhǔn)物質(zhì)但福爾馬肼需要自己配制或者購(gòu)買對(duì)于DIY項(xiàng)目來(lái)說(shuō)成本偏高。我實(shí)測(cè)下來(lái)用奶粉配制濁度標(biāo)準(zhǔn)液是成本最低且重復(fù)性尚可的方案。具體方法取1克脫脂奶粉注意別用全脂奶脂肪會(huì)浮在表面影響光線溶解在1升純凈水中充分?jǐn)嚢桁o置半小時(shí)得到大約100NTU左右的濁度原液當(dāng)然這個(gè)值并不精確所以稱為約用這個(gè)原液按比例稀釋取100mL原液加900mL純凈水得到約10NTU的稀釋液取10mL原液加990mL純凈水得到約1NTU的稀釋液把純凈水本身當(dāng)作0NTU基準(zhǔn)這套方法的優(yōu)點(diǎn)在于方便、便宜缺點(diǎn)是重復(fù)性不好每次配出來(lái)的實(shí)際濁度可能略有不同。但對(duì)于驗(yàn)證電路和算法來(lái)說(shuō)完全夠用。如果你需要精確標(biāo)定建議還是買現(xiàn)成的福爾馬肼標(biāo)準(zhǔn)液。4.3 采集數(shù)據(jù)并擬合曲線我的實(shí)測(cè)結(jié)果我分別在0NTU純凈水、約10NTU、約100NTU三種液體中測(cè)量了ADC值每種液體采樣100次取平均得到如下數(shù)據(jù)濁度參考值實(shí)測(cè)ADC值輸出電壓V0 NTU40253.2910 NTU35682.91100 NTU24101.97可以看到濁度增大ADC值減小這個(gè)趨勢(shì)和前面說(shuō)的一致。但ADC值和NTU之間不是線性關(guān)系100NTU時(shí)ADC值跌到了0NTU的60%而10NTU時(shí)只跌到了89%。兩者的關(guān)系更像是指數(shù)衰減或冪函數(shù)關(guān)系。我嘗試了兩種擬合方式。第一種是指數(shù)擬合NTU a * exp(b * V)用這三組數(shù)據(jù)擬合出來(lái)的參數(shù)是a約等于exp(18.6)b約等于-5.4公式能大致符合。第二種是分段線性擬合在0-10NTU區(qū)間用一條直線10-100NTU區(qū)間用另一條直線精度更高但代碼量稍大。如果你不想搞擬合還有一個(gè)思路直接查表加插值。把標(biāo)定點(diǎn)的ADC值和對(duì)應(yīng)的NTU值存入一個(gè)數(shù)組實(shí)測(cè)時(shí)用線性插值算出中間值。這個(gè)方法最簡(jiǎn)潔且不需要擬合參數(shù)適合單片機(jī)這種算力有限的平臺(tái)。我最終的方案是冪函數(shù)擬合NTU 1473.2 * pow(ADC / 4096.0, -1.8)。這個(gè)公式用浮點(diǎn)運(yùn)算在STM32上執(zhí)行大約需要幾十微秒對(duì)實(shí)時(shí)性完全沒(méi)有影響。4.4 溫度漂移與傳感器浸沒(méi)深度的細(xì)節(jié)標(biāo)定完成之后別急著歡呼。TSW-30的測(cè)量結(jié)果受兩個(gè)因素影響很大溫度和探頭浸沒(méi)深度。TSW-30的紅外發(fā)光管是LEDLED的光功率會(huì)隨溫度變化溫度升高時(shí)輸出光功率下降接收管接收到的光減少ADC值會(huì)有輕微下降。我在空調(diào)房里測(cè)試時(shí)在24℃環(huán)境下標(biāo)定的數(shù)據(jù)拿到32℃室外重復(fù)測(cè)試同一個(gè)水樣的ADC值降低了大約2%-3%換算成濁度誤差大約0.5-1NTU。如果你對(duì)精度要求不是特別高這個(gè)誤差可以接受。如果要求高可以考慮在探頭附近加一個(gè)DS18B20溫度傳感器用溫度補(bǔ)償系數(shù)修正。至于浸沒(méi)深度TSW-30探頭的光學(xué)區(qū)在探頭前端的一小段區(qū)域浸沒(méi)太淺光路有一部分露在空氣中浸沒(méi)太深則可能碰到容器底部的沉淀物。我實(shí)測(cè)的最佳浸沒(méi)深度是探頭側(cè)面白線標(biāo)記處到液面以下3-5厘米而且盡量保證探頭垂直于液面。測(cè)量時(shí)容器底部的氣泡也會(huì)干擾光路所以靜置脫泡后再讀數(shù)數(shù)據(jù)才穩(wěn)定。5. 實(shí)際調(diào)試中遇到的四個(gè)典型問(wèn)題與排查鏈路5.1 問(wèn)題一ADC采樣值亂跳毫無(wú)規(guī)律這是最常見的問(wèn)題。排查路徑我按優(yōu)先級(jí)排列先看電源用萬(wàn)用表測(cè)傳感器供電電壓是否穩(wěn)定如果在3.27-3.33V之間波動(dòng)說(shuō)明電源紋波比較大需要加強(qiáng)濾波再看接線確認(rèn)模擬信號(hào)線是否和電源線綁在一起走線如果是把信號(hào)線單獨(dú)分開然后用示波器看波形如果波形上有50Hz的正弦波疊加是工頻干擾加RC濾波最后還是不行把采樣時(shí)間調(diào)到最大239.5周期降低采樣率到10Hz左右再取50次平均我最后采取的方案是軟件滑動(dòng)平均去除極值每次采集50個(gè)樣本去掉最大的5個(gè)和最小的5個(gè)剩下的40個(gè)取平均。這個(gè)算法在代碼里就幾行但效果立竿見影能把ADC值的波動(dòng)從±30降到±5左右。5.2 問(wèn)題二傳感器輸出的電壓范圍為什么只有0到3.3V有朋友拿著說(shuō)明書問(wèn)為什么你的量程只有0到4.5V我接上單片機(jī)之后測(cè)到的最大值只有3.28V這不是傳感器壞了而是因?yàn)閭鞲衅鞯妮敵鲂盘?hào)是以供電電壓為基準(zhǔn)的。你給傳感器供3.3V電輸出最高就只有3.3V你供5V輸出最高能到4.5V。但STM32的ADC參考電壓內(nèi)部接到了VREF也就是3.3V如果你供5V給傳感器超出3.3V的信號(hào)會(huì)被ADC削頂讀數(shù)永遠(yuǎn)到不了滿量程。所以我在這個(gè)項(xiàng)目里統(tǒng)一給傳感器供3.3V讓傳感器輸出電壓范圍和ADC輸入范圍完全匹配。5.3 問(wèn)題三Keil5編譯報(bào)錯(cuò)CreateProcess Failed這個(gè)報(bào)錯(cuò)有好幾種情況。如果你遇到的報(bào)錯(cuò)信息是*** error: createprocess failed, command: D:\Keil5\ARM\ARMCC\bin\armcc.exe說(shuō)明ARMCC編譯器路徑找不到。排查步驟先確認(rèn)你的Keil5安裝路徑下有沒(méi)有ARMCC文件夾如果只有ARMCLANG文件夾說(shuō)明你安裝的MDK版本是5.37以上的新版本新版本默認(rèn)使用AC6編譯器而不自帶AC5可以在Options for Target - Target - ARM Compiler里切換編譯器版本如果列表里沒(méi)有AC5需要手動(dòng)下載安裝ARM Compiler 5也可以檢查環(huán)境變量有些精簡(jiǎn)系統(tǒng)刪掉了Keil所需的環(huán)境變量重裝或修復(fù)即可這個(gè)坑在5.37版本之后特別常見因?yàn)閺?.37開始Keil把AC5編譯器和安裝包分離了需要單獨(dú)下載。5.4 問(wèn)題四國(guó)產(chǎn)替代芯片燒錄后運(yùn)行正常但ADC讀數(shù)偏低不少人在淘寶上買的STM32F103C8T6其實(shí)是國(guó)產(chǎn)GD32、HK32、APM32等型號(hào)這些芯片和ST原廠在ADC模塊上有細(xì)微差異。我遇到過(guò)一塊APM32F103C8T6同樣的程序和傳感器ADC讀數(shù)比ST原廠芯片整整低了8個(gè)LSB。原因是芯片內(nèi)部的參考電壓源精度有差異。解決方法是重新做一次校準(zhǔn)或者用軟件修正用一個(gè)精密電壓源比如一個(gè)精確到1%的TL431基準(zhǔn)接到ADC輸入測(cè)量實(shí)際讀數(shù)計(jì)算出修正系數(shù)然后應(yīng)用到所有后續(xù)讀數(shù)中。6. 進(jìn)階玩法把濁度數(shù)據(jù)上傳到手機(jī)或云端ADC采樣、串口打印、標(biāo)定這些都完成后這個(gè)項(xiàng)目的基礎(chǔ)功能就算完成了。但如果只是用串口線連著電腦看數(shù)據(jù)那還稱不上一個(gè)完整的應(yīng)用。這里我給三個(gè)擴(kuò)展方向按難度從低到高排。第一個(gè)是加OLED顯示。SPI接口的0.96寸OLED屏成本不到十塊錢接上之后可以直接在屏幕上顯示ADC值、電壓值和換算后的NTU值。我用過(guò)SSD1306驅(qū)動(dòng)芯片的OLED屏在STM32上已經(jīng)有非常成熟的庫(kù)如果你不想折騰直接用現(xiàn)成的開源驅(qū)動(dòng)代碼就行。第二個(gè)是加WiFi模塊上傳云端。ESP8266或者ESP32可以很方便地通過(guò)串口和STM32通信把濁度數(shù)據(jù)封裝成JSON格式通過(guò)MQTT協(xié)議上報(bào)到服務(wù)器。我實(shí)際用過(guò)的方案是STM32通過(guò)USART2發(fā)送數(shù)據(jù)給ESP8266ESP8266連接家里的路由器然后發(fā)布到本地搭建的MQTT Broker比如Mosquitto。手機(jī)端用MQTT客戶端訂閱就能實(shí)時(shí)看到濁度變化。延時(shí)大約在200-500毫秒作為非實(shí)時(shí)監(jiān)測(cè)完全夠用。第三個(gè)是加存儲(chǔ)功能。如果你的應(yīng)用場(chǎng)景是野外水質(zhì)監(jiān)測(cè)太陽(yáng)能供電需要定時(shí)采集并存儲(chǔ)數(shù)據(jù)那么可以加一個(gè)MicroSD卡模塊用FATFS文件系統(tǒng)把數(shù)據(jù)寫成CSV文件方便后續(xù)離線分析。這個(gè)方案的難點(diǎn)在于FATFS移植和SD卡的硬件初始化但STM32F103C8T6的資源是夠用的。坦白說(shuō)TSW-30這顆傳感器的精度上限擺在那里你后端做得再花哨測(cè)量數(shù)據(jù)也不可能達(dá)到實(shí)驗(yàn)室儀器的水準(zhǔn)。但作為一套成本五十元以內(nèi)、可自主復(fù)現(xiàn)、邏輯完整的水質(zhì)濁度檢測(cè)系統(tǒng)它非常適合用來(lái)理解從模擬信號(hào)采集到數(shù)據(jù)轉(zhuǎn)換、再到標(biāo)定建模的完整鏈路。我在這個(gè)項(xiàng)目里最大的收獲其實(shí)是參透了傳感器不是一接上就能出準(zhǔn)確數(shù)據(jù)這句話——從硬件供電到軟件濾波從標(biāo)定到補(bǔ)償每一個(gè)環(huán)節(jié)都在影響最終結(jié)果。如果你也打算做類似的傳感器采集項(xiàng)目希望這篇記錄能幫你少走幾步彎路。本文還有配套的精品資源點(diǎn)擊獲取