STM32串口發送結構體對齊方式


原文鏈接:https://blog.csdn.net/ZL14ZM/article/details/84646011
https://www.cnblogs.com/King-Gentleman/p/5940480.html

 

  stm32做串口或網絡傳輸數據時,經常需要用結構體定義幀格式。如果按照keil默認的對齊方式(4字節對齊),經常會出現結構體中補零的問題,造成幀格式錯誤。所以,在定義結構體類型時,最好把結構體對齊方式改為1字節對齊,防止出錯。也就是說,單片機是32位的,結構體的數據需要對齊,否則就會被補零。

1 #define BYTE_ALIGN   __attribute__ ((packed))  //單字節對齊
2 typedef struct
3 {    
4     u8 node;    
5     u8 cmd;    
6     u8 bytelen;    
7     u16 data;    
8     u16 crc;
9 }BYTE_ALIGN mdb_send_t;

ARM下的對齊處理   
from DUI0067D_ADS1_2_CompLib  


3.13 type  qulifiers  

有部分摘自ARM編譯器文檔對齊部分  
對齊的使用:  
1.__align(num)  
   這個用於修改最高級別對象的字節邊界。在匯編中使用LDRD或者STRD時  
   就要用到此命令__align(8)進行修飾限制,來保證數據對象是相應對齊。  
   這個修飾對象的命令最大是8個字節限制,可以讓2字節的對象進行4字節  
   對齊,但是不能讓4字節的對象2字節對齊。  
   __align是存儲類修改,他只修飾最高級類型對象,不能用於結構或者函數對象。  

   比如:__align(4) u8 mem1base[MEM1_MAX_SIZE];//保證分配的數組空間4字節對齊,同時保證數組首地址可被4整除
     
2.__packed   
  __packed是進行一字節對齊  
  1.不能對packed的對象進行對齊  
  2.所有對象的讀寫訪問都進行非對齊訪問  
  3.float及包含float的結構聯合及未用__packed的對象將不能字節對齊  
  4.__packed對局部整形變量無影響  
  5.強制由unpacked對象向packed對象轉化是未定義,整形指針可以合法定  
  義為packed。  
     __packed int* p;  //__packed int 則沒有意義  
  6.對齊或非對齊讀寫訪問帶來問題  
  __packed struct STRUCT_TEST  
{  
  char a;  
  int b;  
  char c;  
}  ;    //定義如下結構此時b的起始地址一定是不對齊的  
         //在棧中訪問b可能有問題,因為棧上數據肯定是對齊訪問[from CL]  
//將下面變量定義成全局靜態不在棧上   
static char* p;  
static struct STRUCT_TEST a;  
void Main()  
{  
__packed int* q;  //此時定義成__packed來修飾當前q指向為非對齊的數據地址下面的訪問則可以  

p = (char*)&a;            
q = (int*)(p+1);        

*q = 0x87654321;   
/*     
得到賦值的匯編指令很清楚  
ldr      r5,0x20001590 ; = #0x12345678  
[0xe1a00005]   mov      r0,r5  
[0xeb0000b0]   bl       __rt_uwrite4  //在此處調用一個寫4byte的操作函數   
        
[0xe5c10000]   strb     r0,[r1,#0]   //函數進行4次strb操作然后返回保證了數據正確的訪問  
[0xe1a02420]   mov      r2,r0,lsr #8  
[0xe5c12001]   strb     r2,[r1,#1]  
[0xe1a02820]   mov      r2,r0,lsr #16  
[0xe5c12002]   strb     r2,[r1,#2]  
[0xe1a02c20]   mov      r2,r0,lsr #24  
[0xe5c12003]   strb     r2,[r1,#3]  
[0xe1a0f00e]   mov      pc,r14  
*/  

/*  
如果q沒有加__packed修飾則匯編出來指令是這樣直接會導致奇地址處訪問失敗  
[0xe59f2018]   ldr      r2,0x20001594 ; = #0x87654321  
[0xe5812000]   str      r2,[r1,#0]  
*/  

//這樣可以很清楚的看到非對齊訪問是如何產生錯誤的  
//以及如何消除非對齊訪問帶來問題  
//也可以看到非對齊訪問和對齊訪問的指令差異導致效率問題  
}

比如:

typedef __ packed  struct READ_Command
{
    u_char code;
    u_int addr;
    u_char len;
} READ_Command;

typedef   struct READ_Command
{
    u_char code;
    u_int addr;
    u_char len;
} READ_Command;
的區別是什么啊?
回答:沒有__ packed的會出現字對齊等也就是,char型的有可能是占用4個字節的長度的內存空間有__ packed 的就不會,就肯定是1個字節的內存空間,是gcc編譯器的關鍵字。(不止vc下面32位的系統里面的內存數據的存取是32位的,處理的時候都是4個字節為單位,通常也就是int的長度。如果不定義壓縮方式,也就是編譯選項沒有諸如#pragma pack(1)之類的,那么系統會進行4字節對齊)
注意:_ packed只是某種編譯器的格式壓縮,有的是pack呢,對不同的CPU壓縮的對齊方式也不一樣,在使用了該關鍵以后在進行操作時需要格外小心。
聲明結構類型時,可以包含一個保留字packed,用於實現壓縮數據存儲。
      當一個記錄類型在   {$A-}   狀態下聲明或者聲明中包括了保留字   packed   時,記錄中的字段不被調整,而替換為賦予連續的偏移量。這樣一個壓縮記錄的總尺寸就是所有字段的尺寸的和。因為數據調整尺寸可能改變(如不同版本的編譯器對同一種數據類型的調整值可能不同),所以當想要把記錄寫入磁盤時或者在內存中傳遞到另一模塊而該模塊由不同版本的編譯器編譯時,最好還是壓縮所有的記錄。( delphi  borland 中也有該關鍵字)
 
3.在 Cotex-M3 programming manual 中有提到對齊問題
  1.通常編譯器在生成代碼的時候都會進行結構體填充,保證(結構體內部成員)最高性能的對齊方式。
  2.編譯器自動分配出來結構體的內存(比如定義為全局變量或局部變量)肯定是對齊的。
  3.查閱幫助文檔的malloc部分,mdk的標准malloc申請的內存區時8字節對齊的。
  4.若自定義的malloc函數本身沒有對分配的內存實現4字節或以上的對齊操作,分配出來的不對齊的內存,編譯器是不知道的,所以很可能會產生問題。
     此時最好的解決方式在內存池數組前添加__align(4)關鍵字,只需保證自定義malloc分配出來的首地址是4字節對齊。
     比如:__align(4) u8 mem1base[MEM1_MAX_SIZE];
 
相關更多stm32字節對齊問題的討論,請參考正點原子相關帖子http://www.openedv.com/thread-7415-1-1.html。
其中問題的關鍵就在於正點原子自定義的mymalloc函數沒有實現4字節對齊。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM