本帖最后由 blust5 于 2023-5-10 16:25 編輯
#申請?jiān)瓌?chuàng)# @21小跑堂
前幾天寫了GD32開發(fā)板硬件I2C讀寫方面遇到的問題以及解決辦法。
https://bbs.21ic.com/icview-3300590-1-1.html?fromuser=blust5
https://bbs.21ic.com/icview-3300846-1-1.html?fromuser=blust5
使用GD32303C-EVAL開發(fā)板和MPL3115A2模塊測量氣壓或高度數(shù)據(jù),兩者間使用硬件I2C進(jìn)行通訊。
一開始發(fā)現(xiàn)I2C讀寫異常,EXMC模塊時鐘使能就會導(dǎo)致I2C讀取失敗。通過逐步排查與調(diào)試驗(yàn)證,最終確認(rèn)問題為PB7引腳使用沖突,EXMC模塊與I2C均使用了開發(fā)板的PB7引腳。通過對I2C0功能的引腳remap配置,已經(jīng)將問題解決。
那么下面就進(jìn)入到了MPL3115A2模塊的配置和使用了。
下面是焊接前后的模塊實(shí)物圖。
51789645b4eec15584.png (237.15 KB )
下載附件
2023-5-10 15:59 上傳
51923645b4ef616932.png (241.13 KB )
下載附件
2023-5-10 15:59 上傳
下面是所使用的GD32303C-EVAL開發(fā)板實(shí)物圖。
47994645b4f06a9777.png (567.54 KB )
下載附件
2023-5-10 16:00 上傳
由于前面已經(jīng)調(diào)通了I2C硬件驅(qū)動,這里開始編寫MPL3115A2模塊初始化程序。
首先查看MPL3115A2的芯片手冊,確認(rèn)使用過程。
找到MPL3115A2的內(nèi)部寄存器列表,查看需要使用的內(nèi)容。
21035645b4f140a714.png (92.9 KB )
下載附件
2023-5-10 16:00 上傳
這里可以看到,0x01-0x05寄存器是保存測量結(jié)果的,其中0x01-0x03是保存氣壓值或高度值的,0x04和0x05是保存溫度值的。
MPL3115有兩種工作模式,氣壓計(jì)模式和高度計(jì)模式,在不同的工作模式下,0x01-0x03寄存器中保存的值是不同的,分別跟對應(yīng)的工作模式匹配。就是說無論工作在什么模式,讀取結(jié)果都是同樣的寄存器地址。
0x00寄存器是狀態(tài)寄存器。這個地址有點(diǎn)特別,在不同的數(shù)據(jù)模式下指向不同的寄存器地址。
MPL3115除了具有單次采樣結(jié)果存儲的模式之外,還具有FIFO功能,在開啟FIFO數(shù)據(jù)模式的情況下,芯片可以保存最多32組數(shù)據(jù),每組數(shù)據(jù)都包括氣壓值/高度值和溫度值(5字節(jié)數(shù)據(jù))。
在這個數(shù)據(jù)模式下,0x00地址指向的是0x0D寄存器,即I2C讀取0x00地址的值,實(shí)際上讀取的是0x0D寄存器(當(dāng)然,直接讀取0x0D寄存器也是可以的),這個寄存器是FIFO系統(tǒng)狀態(tài)寄存器,可以查詢有沒有發(fā)生FIFO數(shù)據(jù)溢出事件以及FIFO數(shù)據(jù)有沒有存儲到設(shè)定數(shù)量。
在這個模式下,讀取數(shù)據(jù)也不是0x01-0x05寄存器讀取了。為了方便循環(huán)讀取,可以始終讀取0x01寄存器的值,讀取到的值會輪流按照“氣壓值/高度值高字節(jié)→中字節(jié)→低字節(jié)→溫度高字節(jié)→低字節(jié)→下一組值的氣壓/高度值高字節(jié)”的形式一直循環(huán)讀取,直到讀取完畢所有FIFO數(shù)據(jù)的值。而且按照I2C通訊連續(xù)讀取時寄存器地址自增模式進(jìn)行匹配,0x01寄存器地址在這個模式下的自增地址還是0x01,即按照I2C總線通訊模式進(jìn)行連續(xù)讀取,寄存器地址選擇為0x01,一直讀取,會一直讀到0x01地址的值,直到讀取完所有數(shù)據(jù)。
而如果沒有開啟FIFO數(shù)據(jù)模式,0x00寄存器地址指向的是0x06寄存器,這個寄存器也是狀態(tài)寄存器,這個寄存器可以查看有沒有出現(xiàn)數(shù)據(jù)覆蓋以及當(dāng)前數(shù)據(jù)有沒有準(zhǔn)備好。
由于沒有啟用FIFO數(shù)據(jù)模式,這個模式下只能保存一組數(shù)據(jù)(氣壓值/高度值和溫度值,共5字節(jié)),分別存放在0x01-0x05寄存器里,讀取時直接連續(xù)讀取5字節(jié)即可。如果一直循環(huán)讀取,讀完0x05寄存器之后寄存器指針會自動再次定位到0x00寄存器,再次進(jìn)行一輪讀取,但是由于這時候下一組采樣數(shù)據(jù)還沒有準(zhǔn)備好,因此讀出來的值無效(應(yīng)該為0,不過沒有驗(yàn)證)。
工作模式大概說了一下,繼續(xù)查看如何配置工作模式。首先使用最簡單的模式:不啟用FIFO數(shù)據(jù)的連續(xù)采樣模式。
78943645b4f24cb6d1.png (45.67 KB )
下載附件
2023-5-10 16:00 上傳
可以看到,控制寄存器有5個,分別查看每個寄存器的介紹,發(fā)現(xiàn)只要控制第一個控制寄存器0x26即可完成初步使用。
76454645b4f30d6db8.png (153.24 KB )
下載附件
2023-5-10 16:00 上傳
14777645b4f36c9f07.png (44.06 KB )
下載附件
2023-5-10 16:00 上傳
這個寄存器可以配置芯片工作在氣壓計(jì)模式或高度計(jì)模式,可以配置過采樣次數(shù),可以配置芯片的待機(jī)模式和激活模式。
通過描述來看,直接將SBYB位置1即可使芯片進(jìn)入激活模式,開始進(jìn)行采樣。
于是初始化函數(shù)如下(氣壓計(jì)模式,過采樣設(shè)置為最大128,增加數(shù)據(jù)穩(wěn)定性):
- init8_t MPL_Init(void)
- {
- uint8_t reg_data, get_data;
- reg_data = 0x39;
- eeprom_byte_write(& reg_data, CTRL_REG1_ADDR);
- eeprom_buffer_read(&get_data, CTRL_REG1_ADDR, 1);
- if(get_data==reg_data)
- {
- return 0;
- }
- else
- {
- return -1;
- }
- }
復(fù)制代碼
其中CTRL_REG1_ADDR為0x26。
同時編寫結(jié)果讀取函數(shù)(剛開始只是把結(jié)果讀取出來,至于數(shù)值轉(zhuǎn)換等讀取成功之后再處理):
- int8_t MPL_Result_Read(void)
- {
- uint8_t status;
- uint8_t data_buf[5];
-
- eeprom_buffer_read(&status, DATA_STATUS_ADDR, 1);
- if((status & 0x08) == 0)
- {
- return -1;
- }
-
- eeprom_buffer_read(data_buf, DATA_START_ADDR, 5);
-
- return 0;
- }
復(fù)制代碼
其中DATA_STATUS_ADDR為0x00,而DATA_START_ADDR為0x01。
編譯、燒錄、運(yùn)行,結(jié)果發(fā)現(xiàn)讀取DATA_STATUS_ADDR始終為0。好吧,繼續(xù)找問題。
單步調(diào)試運(yùn)行,發(fā)現(xiàn)控制寄存器寫入成功了,但是讀取狀態(tài)寄存器確實(shí)為0,那么久確認(rèn)問題不是出在I2C讀寫上。
繼續(xù)看手冊,同時網(wǎng)上找一下例程(例程放到附件里)。
通過對比網(wǎng)上找的例程,發(fā)現(xiàn)多了一個寄存器的操作:0x13寄存器。查看手冊,發(fā)現(xiàn)這個是數(shù)據(jù)更新狀態(tài)置位允許寄存器,就是說這個寄存器可以配置允許哪些情況產(chǎn)生事件更新標(biāo)志。
75119645b4f63d6fe0.png (96.49 KB )
下載附件
2023-5-10 16:01 上傳
原來問題在這里。這個寄存器配置了允許,數(shù)據(jù)采樣完成后才會在狀態(tài)寄存器里產(chǎn)生標(biāo)志位。
OK!了解了。然后按照例程里的模式,對控制寄存器進(jìn)行寫入,并增加了讀取驗(yàn)證過程,同時增加了氣壓計(jì)/高度計(jì)模式選擇控制,避免來回切換工作模式都要改動初始化函數(shù)。并在運(yùn)行完成之后顯示初始化結(jié)果。
- void MPL_Init(uint8_t mode)
- {
- uint8_t i, mode_data,reg_data, get_data=0;
-
- if(mode==0)
- {
- mode_data = 0x38;
- }
- else
- {
- mode_data = 0xB8;
- }
- reg_data = mode_data;
- eeprom_byte_write(& reg_data, CTRL_REG1_ADDR);
- eeprom_buffer_read(&get_data, CTRL_REG1_ADDR, 1);
- if(get_data!=reg_data)
- {
- MPL_ERR_Display();
- return;
- }
- reg_data = 0x07;
- eeprom_byte_write(& reg_data, PT_DATA_CFG_ADDR);
- eeprom_buffer_read(&get_data, PT_DATA_CFG_ADDR, 1);
- if(get_data!=reg_data)
- {
- MPL_ERR_Display();
- return;
- }
- reg_data = mode_data + 1;
- eeprom_byte_write(& reg_data, CTRL_REG1_ADDR);
- eeprom_buffer_read(&get_data, CTRL_REG1_ADDR, 1);
- if(get_data!=reg_data)
- {
- MPL_ERR_Display();
- return;
- }
- MPL_OK_Display();
- }
復(fù)制代碼
再次編譯、燒錄、運(yùn)行,這次果然讀取到狀態(tài)和數(shù)據(jù)了。
下面就是數(shù)據(jù)格式轉(zhuǎn)換了,這個相對簡單,按照手冊上的說明對數(shù)據(jù)進(jìn)行位移和拼接處理,即可得到正確的結(jié)果。這里為了顯示方便,直接將整數(shù)部分和小數(shù)部分分別存儲,然后拼接顯示在TFT屏幕上的。
- int8_t MPL_Result_Read(void)
- {
- uint8_t status;
- uint8_t data_buf[5];
-
- eeprom_buffer_read(&status, DATA_STATUS_ADDR, 1);
- if((status & 0x08) == 0)
- {
- return -1;
- }
-
- eeprom_buffer_read(data_buf, DATA_START_ADDR, 5);
- height_integer = (uint32_t)data_buf[0]*0x100 + (uint32_t)data_buf[1];
- height_decimal = (data_buf[2]>>4) * 625;
- temp_integer = data_buf[3];
- temp_decimal = (data_buf[4]>>4) * 625;
- pressure_value = (uint32_t)data_buf[0]*0x400 + (uint32_t)data_buf[1]*0x04 + (data_buf[2]>>6);
-
- return 0;
- }
復(fù)制代碼
這里將高度計(jì)模式和氣壓計(jì)模式兩種模式下的轉(zhuǎn)換關(guān)系都保存了下來,當(dāng)然,同一模式下肯定有一組轉(zhuǎn)換結(jié)果是錯誤的,但是只使用正確的結(jié)果就可以了,防止每次切換模式都去修改代碼,畢竟調(diào)試階段經(jīng)常變換模式是很正常的。同時這里將氣壓計(jì)模式的小數(shù)位直接丟棄了(精度不夠)。
下面是高度計(jì)模式的運(yùn)行結(jié)果示意,分別顯示高度值和溫度值。
44330645b4fa967c33.png (492.17 KB )
下載附件
2023-5-10 16:02 上傳
然后開始運(yùn)行,同時查看測量結(jié)果的變化是否符合預(yù)期。
初步觀察,發(fā)現(xiàn)測量結(jié)果會有波動,但是波動值范圍在±0.3m范圍內(nèi),符合預(yù)期情況。
由于我是在4樓辦公,于是準(zhǔn)備逐層下到1樓,再上到4樓,來查看測量結(jié)果的變化。
實(shí)驗(yàn)結(jié)果如下:一開始在4樓時高度約16m,下到3樓高度約12.5m,下到2樓高度約7m,下到1樓高度約2.5m,再上到2樓高度約7m,上到3樓高度約12m,上到4樓高度約15.5m。
通過初步測試,剔除掉絕對誤差后,發(fā)現(xiàn)基本可以測量出樓層高度。
但是在辦公室一直運(yùn)行時發(fā)現(xiàn)一個問題,測量值一直在漂移,一兩個小時測量結(jié)果就能漂移10m左右,而溫度的變化很小,不應(yīng)該引起這么大的漂移。以為是沒有配置好芯片工作模式,于是繼續(xù)查資料,找例程。同時對接淘寶賣家,問下有沒有技術(shù)支持或者例程代碼之類的。
最終賣家發(fā)過來一個例程代碼包,C++語言編寫的,不過大致看一下配置過程還是OK的。(例程見附件)
多方對比之下,發(fā)現(xiàn)我的配置流程和讀取流程沒有問題,就是這么用的。
那么是什么原因?qū)е碌臏y量結(jié)果漂移呢?
為了確認(rèn)漂移的情況,我對程序增加了結(jié)果上傳功能,通過串口實(shí)時將測量結(jié)果上傳到電腦上,通過串口助手接收保存數(shù)據(jù)。
60416645b4fbf8e0f0.png (198.69 KB )
下載附件
2023-5-10 16:03 上傳
然后進(jìn)行長時間的采樣、數(shù)據(jù)保存,并對采集到的數(shù)據(jù)按照時間進(jìn)行圖表繪制,確認(rèn)其變化趨勢。
446645b4fcab1eb4.png (76.95 KB )
下載附件
2023-5-10 16:03 上傳
可以看出,溫度的變化(右側(cè)的附坐標(biāo)軸)很小,基本上在25.5-27.5度之間變化,而高度值在整個過程中(約13小時)從23m左右最終下降到0m附近?梢钥闯銎渌矔r值還是相對比較準(zhǔn)的(趨勢線不是特別粗),那是什么原因?qū)е铝碎L時間的測量值漂移這么大呢?
這時候我想到了一個問題,一天之內(nèi)大氣壓是穩(wěn)定的么?畢竟大氣是氣體,整體包裹在地球外部,并沒有一個特定的容器去限制它,而且天氣、風(fēng)、云等等也可能影響到大氣壓。于是我去搜了一下,大氣壓在一天之內(nèi)有沒有變化。
結(jié)果搜到一個“氣壓日變化”的詞條,里面說了大氣在一天之內(nèi)的變化規(guī)律。
氣壓日變化
氣壓日變化的特點(diǎn)是在一天中有一個最高值和一個次高值,一個最低值和一個次低值。最高值出現(xiàn)在9 ~10時,次高值出現(xiàn)在21~22時;最低值出現(xiàn)在15~16時,次低值出現(xiàn)在3~4時。
氣壓的日變化在低緯度地區(qū)比較明顯。氣壓日振幅(一日中最高值與最低值之差,又稱為日較差)隨緯度的增高而減小。在低緯地區(qū),平均日振幅可達(dá)3~4百帕,到緯度50度附近日振幅不足1 百帕了。不同緯度上氣壓日變化的情況,在我國中緯度地區(qū)氣壓日振幅為1~2.5百帕,在低緯地區(qū)為2.5~4百帕,而在西藏高原東部邊緣的山谷中氣壓的日振幅有時可達(dá)6. 5百帕。
對照這個說明,可以看到大氣壓在一天之內(nèi)變化幾百帕是正常的,換算成高度的話差不多有幾十米,因此測到的漂移應(yīng)該是正常的。
為了驗(yàn)證這個結(jié)果,我繼續(xù)進(jìn)行長時間連續(xù)數(shù)據(jù)采集,采集夠24小時的測量結(jié)果(早上上班前約7點(diǎn)50分測量到第二天早上上班前),得到的趨勢圖如下:
53052645b4fdbb53be.png (76.21 KB )
下載附件
2023-5-10 16:03 上傳
可以從趨勢圖中看出,高度值在上午9時-10時左右最低,在下午16時左右最高,這剛好對應(yīng)了上面詞條里說的氣壓最高值出現(xiàn)在9-10時,最低值出現(xiàn)在15-16時(氣壓越高海拔越低)。而較小的波峰波谷也基本上能對應(yīng)詞條里的次低值和次高值時段。
因此基本確認(rèn)了長時間的測量結(jié)果漂移實(shí)際上是大氣壓的真實(shí)變化,而不是芯片測量不準(zhǔn)確。
至此,問題基本解決,測量結(jié)果也符合預(yù)期,后期加上數(shù)據(jù)修正和基準(zhǔn)補(bǔ)償之后,可以相對準(zhǔn)確的獲取當(dāng)前的高度值。
注:代碼里配置寄存器時eeprom_byte_write(& reg_data, CTRL_REG1_ADDR);這里&和reg_data中間不應(yīng)該有空格的,但是不加空格的話論壇會顯示_data。這個只能在這里做一個說明。
附件如下
網(wǎng)上查找的MPL3115例程.rar (17.99 KB)
2023-5-10 16:06 上傳
點(diǎn)擊文件名下載附件
賣家給的MPL3115模塊資料.zip (126.13 KB)
2023-5-10 16:06 上傳
點(diǎn)擊文件名下載附件