曾有STM32用戶(hù)反饋,他發(fā)現(xiàn)同樣代碼在STM32F1系列芯片上運(yùn)行好好的,而且代碼跟STM32外設(shè)關(guān)聯(lián)性也不大。而當(dāng)代碼運(yùn)行在stm32L071VB單片機(jī)時(shí),在做數(shù)據(jù)的內(nèi)存拷貝時(shí)會(huì)進(jìn)入硬件錯(cuò)誤【Hard Fault】,覺(jué)得不可理解。
它定義了類(lèi)似下面的數(shù)據(jù)結(jié)構(gòu),并用到預(yù)編譯命令安排結(jié)構(gòu)體數(shù)據(jù)成員的存放對(duì)齊原則:
#pragma pack? (1)
Struct Comm_Frame {
uint8_t Head;
uint16_t Data[3];
uint8_t Class;
uint16_t Tail [2];
} Stream;
#pragma pack ()
他使用到基于上面結(jié)構(gòu)體定義的數(shù)據(jù)變量進(jìn)行數(shù)據(jù)通信,為了讓數(shù)據(jù)成員在內(nèi)存中緊湊連續(xù)存放,將數(shù)據(jù)結(jié)構(gòu)體的地址對(duì)齊規(guī)則指定為字節(jié)對(duì)齊,即使用#pragma pack (1)。數(shù)據(jù)在內(nèi)存中像下面樣子擺放:

他這樣設(shè)計(jì)的話,數(shù)據(jù)結(jié)構(gòu)體中的Data[]和Tail[]雙字節(jié)數(shù)據(jù)會(huì)出現(xiàn)在奇數(shù)地址的地方。那么,當(dāng)將上述Stream.Data[]數(shù)據(jù)拷貝出去的時(shí)候,在基于雙字節(jié)數(shù)據(jù)類(lèi)型的指針尋址訪問(wèn)時(shí),會(huì)出現(xiàn)被訪問(wèn)數(shù)據(jù)的地址不遵循2倍數(shù)的原則,即出現(xiàn)訪問(wèn)地址不對(duì)齊的問(wèn)題,可能導(dǎo)致運(yùn)行出錯(cuò)。一般來(lái)講,對(duì)于ARM內(nèi)核的芯片,基于雙字節(jié)數(shù)據(jù)寬度的尋址訪問(wèn)時(shí),被訪問(wèn)數(shù)據(jù)的地址要求是2的倍數(shù);基于4字節(jié)數(shù)據(jù)寬度的尋址訪問(wèn)時(shí),地址要求是4的倍數(shù)。


比如:這里定義了一個(gè)數(shù)組uint16_t forcomp[3]和下面兩個(gè)指針:
uint16_t *pointer1 = &forcomp[0];
uint16_t *pointer2 = &Stream.Data[0];
現(xiàn)將上面結(jié)構(gòu)體成員Stream.Data[]的內(nèi)容通過(guò)指針尋址按如下方式拷貝進(jìn)?forcomp[]?。

上面的代碼如果運(yùn)行在基于M0或M0+內(nèi)核的STM32芯片的話,就會(huì)出現(xiàn)Hard Fault錯(cuò)誤. 客戶(hù)使用的芯片stm32L071VBT6正是基于M0+內(nèi)核的STM32芯片。

為什么會(huì)這樣呢?這可以從Cortex M0/M0+的內(nèi)核技術(shù)手冊(cè)上看到相關(guān)描述:

顯然,基于M0、M0+內(nèi)核的芯片,它是不支持非對(duì)齊尋址訪問(wèn)的。
客戶(hù)又說(shuō)過(guò),相同代碼在STM32F1芯片上運(yùn)行又沒(méi)有問(wèn)題,那怎么解釋呢?
STM32F1系列MCU是基于ARMCortex M3內(nèi)核的芯片,關(guān)于地址對(duì)齊方面跟M0/M0+有所不同。M3內(nèi)核支持部分指令的非對(duì)齊地址訪問(wèn),相關(guān)描述如下:

也就是說(shuō),基于CortexM3內(nèi)核的芯片,它支持部分指令的非對(duì)齊訪問(wèn),但非對(duì)齊訪問(wèn)要慢于對(duì)齊訪問(wèn)。即非對(duì)齊訪問(wèn)是需要代價(jià)的,訪問(wèn)效率會(huì)受到影響。所以,我們?cè)趹?yīng)用中要盡量遵循地址對(duì)齊的尋址訪問(wèn)方式。關(guān)于地址對(duì)齊話題,在各個(gè)ARM內(nèi)核技術(shù)參考手冊(cè)里略有介紹。
結(jié)合本案的實(shí)際情況,碰巧用戶(hù)代碼先是可以正常運(yùn)行于基于M3內(nèi)核的STM32F1芯片,而在基于M0+內(nèi)核的芯片上出現(xiàn)了異常。導(dǎo)致他覺(jué)得不好理解。
這里,指針?biāo)笖?shù)據(jù)類(lèi)型為雙字節(jié)類(lèi)型,為了避免在M0/M0+內(nèi)核芯片里尋址訪問(wèn)時(shí)發(fā)生非對(duì)齊而導(dǎo)致的異常,可以將結(jié)構(gòu)體變量的內(nèi)存地址對(duì)齊方式改為雙字節(jié)對(duì)齊,即使用#pragma pack (2)。數(shù)據(jù)在內(nèi)存中像下面這樣擺放。

這樣修改后,經(jīng)過(guò)測(cè)試的確沒(méi)有問(wèn)題。結(jié)合到客戶(hù)的具體情況,客戶(hù)希望數(shù)據(jù)連續(xù)、緊湊存放,不希望數(shù)據(jù)間有空隙,即結(jié)構(gòu)體數(shù)據(jù)成員的內(nèi)存地址對(duì)齊規(guī)則不變,仍然采用pack(1)。那么,數(shù)據(jù)拷貝操作時(shí)可以將雙字節(jié)數(shù)據(jù)類(lèi)型的指針強(qiáng)轉(zhuǎn)為單字節(jié)數(shù)據(jù)類(lèi)型的指針,將雙字節(jié)數(shù)據(jù)按字節(jié)對(duì)齊尋址方式分作兩次連續(xù)讀取完成。此時(shí),用戶(hù)只需將應(yīng)用程序稍作調(diào)整即可。
所以,在STM32開(kāi)發(fā)過(guò)程中,有些代碼或許跟MCU外設(shè)沒(méi)什么關(guān)系,但可能跟內(nèi)核有關(guān)。STM32系列眾多,涉及多個(gè)ARM內(nèi)核,不同的內(nèi)核在諸多方面存在些差異,這點(diǎn)需要注意。其實(shí),從MCU軟件開(kāi)發(fā)層面來(lái)看,地址對(duì)齊問(wèn)題、中斷優(yōu)先級(jí)安排問(wèn)題、堆棧安排問(wèn)題,都是些比較隱蔽的問(wèn)題,出錯(cuò)了后果往往也很?chē)?yán)重,我們平時(shí)可以多留意下。
-
STM32
+關(guān)注
關(guān)注
2309文章
11162瀏覽量
373415 -
代碼
+關(guān)注
關(guān)注
30文章
4967瀏覽量
73960 -
數(shù)據(jù)結(jié)構(gòu)
+關(guān)注
關(guān)注
3文章
573瀏覽量
41584
原文標(biāo)題:一個(gè)跟地址對(duì)齊有關(guān)的應(yīng)用異常案例
文章出處:【微信號(hào):stmcu832,微信公眾號(hào):茶話MCU】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
對(duì) M7 內(nèi)核上的 memcpy 速度的一個(gè)疑問(wèn)求解
Cortex-M3工作模式及異常
LAT1185+一個(gè)地址未對(duì)齊引起的 HardFault 異常應(yīng)用筆記
開(kāi)關(guān)電源 變壓器初級(jí)電流異常
CW32操作FLASH地址對(duì)齊的要求
摩爾線程新一代大語(yǔ)言模型對(duì)齊框架URPO入選AAAI 2026
Hardfult對(duì)齊異常怎么解決?
連得上熱點(diǎn),但是ping baidu.com出現(xiàn)timeout,請(qǐng)問(wèn)跟什么有關(guān)?
芯知識(shí)|WT2003H語(yǔ)音芯片音頻地址詳解:一線/UART模式差異及靜音地址實(shí)踐
CYPD5235的CC Pin功能異常,還可能會(huì)跟什么有關(guān)?
Allegro Skill布局功能--器件絲印過(guò)孔對(duì)齊介紹與演示
PCB布局太亂? Altium Designer這個(gè)快捷鍵幫你一秒對(duì)齊全場(chǎng)
一個(gè)跟地址對(duì)齊有關(guān)的應(yīng)用異常案例
評(píng)論