曾有STM32用戶反饋,他發現同樣代碼在STM32F1系列芯片上運行好好的,而且代碼跟STM32外設關聯性也不大。而當代碼運行在stm32L071VB單片機時,在做數據的內存拷貝時會進入硬件錯誤【Hard Fault】,覺得不可理解。
它定義了類似下面的數據結構,并用到預編譯命令安排結構體數據成員的存放對齊原則:
#pragma pack? (1)
Struct Comm_Frame {
uint8_t Head;
uint16_t Data[3];
uint8_t Class;
uint16_t Tail [2];
} Stream;
#pragma pack ()
他使用到基于上面結構體定義的數據變量進行數據通信,為了讓數據成員在內存中緊湊連續存放,將數據結構體的地址對齊規則指定為字節對齊,即使用#pragma pack (1)。數據在內存中像下面樣子擺放:
他這樣設計的話,數據結構體中的Data[]和Tail[]雙字節數據會出現在奇數地址的地方。那么,當將上述Stream.Data[]數據拷貝出去的時候,在基于雙字節數據類型的指針尋址訪問時,會出現被訪問數據的地址不遵循2倍數的原則,即出現訪問地址不對齊的問題,可能導致運行出錯。一般來講,對于ARM內核的芯片,基于雙字節數據寬度的尋址訪問時,被訪問數據的地址要求是2的倍數;基于4字節數據寬度的尋址訪問時,地址要求是4的倍數。
比如:這里定義了一個數組uint16_t forcomp[3]和下面兩個指針:
uint16_t *pointer1 = &forcomp[0];
uint16_t *pointer2 = &Stream.Data[0];
現將上面結構體成員Stream.Data[]的內容通過指針尋址按如下方式拷貝進?forcomp[]?。
上面的代碼如果運行在基于M0或M0+內核的STM32芯片的話,就會出現Hard Fault錯誤. 客戶使用的芯片stm32L071VBT6正是基于M0+內核的STM32芯片。
為什么會這樣呢?這可以從Cortex M0/M0+的內核技術手冊上看到相關描述:
顯然,基于M0、M0+內核的芯片,它是不支持非對齊尋址訪問的。
客戶又說過,相同代碼在STM32F1芯片上運行又沒有問題,那怎么解釋呢?
STM32F1系列MCU是基于ARMCortex M3內核的芯片,關于地址對齊方面跟M0/M0+有所不同。M3內核支持部分指令的非對齊地址訪問,相關描述如下:
也就是說,基于CortexM3內核的芯片,它支持部分指令的非對齊訪問,但非對齊訪問要慢于對齊訪問。即非對齊訪問是需要代價的,訪問效率會受到影響。所以,我們在應用中要盡量遵循地址對齊的尋址訪問方式。關于地址對齊話題,在各個ARM內核技術參考手冊里略有介紹。
結合本案的實際情況,碰巧用戶代碼先是可以正常運行于基于M3內核的STM32F1芯片,而在基于M0+內核的芯片上出現了異常。導致他覺得不好理解。
這里,指針所指數據類型為雙字節類型,為了避免在M0/M0+內核芯片里尋址訪問時發生非對齊而導致的異常,可以將結構體變量的內存地址對齊方式改為雙字節對齊,即使用#pragma pack (2)。數據在內存中像下面這樣擺放。
這樣修改后,經過測試的確沒有問題。結合到客戶的具體情況,客戶希望數據連續、緊湊存放,不希望數據間有空隙,即結構體數據成員的內存地址對齊規則不變,仍然采用pack(1)。那么,數據拷貝操作時可以將雙字節數據類型的指針強轉為單字節數據類型的指針,將雙字節數據按字節對齊尋址方式分作兩次連續讀取完成。此時,用戶只需將應用程序稍作調整即可。
所以,在STM32開發過程中,有些代碼或許跟MCU外設沒什么關系,但可能跟內核有關。STM32系列眾多,涉及多個ARM內核,不同的內核在諸多方面存在些差異,這點需要注意。其實,從MCU軟件開發層面來看,地址對齊問題、中斷優先級安排問題、堆棧安排問題,都是些比較隱蔽的問題,出錯了后果往往也很嚴重,我們平時可以多留意下。
-
STM32
+關注
關注
2270文章
10900瀏覽量
356053 -
代碼
+關注
關注
30文章
4788瀏覽量
68617 -
數據結構
+關注
關注
3文章
573瀏覽量
40132
原文標題:一個跟地址對齊有關的應用異常案例
文章出處:【微信號:stmcu832,微信公眾號:茶話MCU】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論