2015-05-30 43 views
0

我有以下轉換位和背部

typedef struct fpButtons { 

    /**  BYTE 0   **/   
    uint8_t  button_1:1;  
    uint8_t  button_2:1; 
    uint8_t  button_3:1; 
    uint8_t  button_4:1; 
    uint8_t  button_5:1; 
    uint8_t  button_6:1; 
    uint8_t  button_7:1; 
    uint8_t  button_8:1; 

    /** And BYTE 2 which I didn't paste to save SO's space lol **/ 
    // button_9:1 to button_16:1 
} FP_BUTTONS; 

的結構而這個功能,讓一個無符號整數,它的位應該建立上述

void FP_UpdateData(FP_BUTTONS *data, uint8_t b1, uint8_t b2) 
{ 
    data->button_1 = (b1 & (1 << 1)) >> 1; 
    data->button_2 = (b1 & (1 << 2)) >> 2; 
    data->button_3 = (b1 & (1 << 3)) >> 3; 
    data->button_4 = (b1 & (1 << 4)) >> 4; 
    data->button_5 = (b1 & (1 << 5)) >> 5; 
    data->button_6 = (b1 & (1 << 6)) >> 6; 
    data->button_7 = (b1 & (1 << 7)) >> 7; 
    data->button_8 = (b1 & (1 << 8)) >> 8; 

    //Do the same thing for button 9 to 16 

} 

的結構現在存儲後,我必須把它發送到其他地方:

void APP_Send(){ 

    uint8_t packet[2]; 

    packet[0] = *((uint8_t*) &FP_BUTTONS_Data); 
    packet[1] = *(((uint8_t*) &FP_BUTTONS_Data)+1);  

    //Send stuff away..... 
} 

儘管所有的努力,沒有上述代碼似乎窩RK。我在一個嵌入式處理器上做這件事,它很難調試。我想知道一些C-Guru傢伙是否可以告訴我這段代碼有什麼問題?

+2

您的第一班不應該是'1 << 0'嗎? (不是一個解決方案,因爲它仍然應該做*某事* - 錯誤的,但仍然)。 – usr2564301

+1

當涉及到可移植性或甚至在具有字節順序的單個平臺內時,位域顯然可能成爲主要悲傷的根源。值得避免他們贊成只是按照自己的邏輯進行按位邏輯。也就是說,如果他們工作的很好,而且你還可以,那麼我真的推薦一個帶有'fpButtons'和'uint8_t'的工會。那麼你不必麻煩抽取/包裝位。否則,無論如何,當你只是要進行所有這種按位提取時,使用位域似乎毫無意義。 –

+0

@Ike我真的不能使用聯合解決方案,因爲它是項目的其他部分所必需的。 –

回答

4

位是0索引的,但是您將這些位移編碼爲位而不是1位索引。您必須使用07作爲uint8_t的班次。

此外,可以刪除右移。

試試這個:

void FP_UpdateData(FP_BUTTONS *data, uint8_t b1, uint8_t b2) 
{ 
    data->button_1 = (b1 & (1 << 0)) != 0; 
    data->button_2 = (b1 & (1 << 1)) != 0; 
    data->button_3 = (b1 & (1 << 2)) != 0; 
    data->button_4 = (b1 & (1 << 3)) != 0; 
    data->button_5 = (b1 & (1 << 4)) != 0; 
    data->button_6 = (b1 & (1 << 5)) != 0; 
    data->button_7 = (b1 & (1 << 6)) != 0; 
    data->button_8 = (b1 & (1 << 7)) != 0; 

    //Do the same thing for button 9 to 16 
} 

void APP_Send() 
{ 
    uint8_t packet[2]; 

    uint8_t *data = (uint8_t*) &FP_BUTTONS_Data; 
    packet[0] = data[0]; 
    packet[1] = data[1]; 

    //Send stuff away..... 
} 

隨着中說,如果你包裹在union S中的結構數據這一切的手動換擋的是不必要的:

typedef struct fpButtons { 

    /**  BYTE 0   **/   
    union { 
    struct { 
     uint8_t  button_1:1;  
     uint8_t  button_2:1; 
     uint8_t  button_3:1; 
     uint8_t  button_4:1; 
     uint8_t  button_5:1; 
     uint8_t  button_6:1; 
     uint8_t  button_7:1; 
     uint8_t  button_8:1; 
    }; 
    uint8_t   rawbuttons_1; 
    }; 

    /**  BYTE 1   **/   
    union { 
    struct { 
     uint8_t  button_9:1;  
     uint8_t  button_10:1; 
     uint8_t  button_11:1; 
     uint8_t  button_12:1; 
     uint8_t  button_13:1; 
     uint8_t  button_14:1; 
     uint8_t  button_15:1; 
     uint8_t  button_16:1; 
    }; 
    uint8_t   rawbuttons_2; 
    }; 

} FP_BUTTONS; 

void FP_UpdateData(FP_BUTTONS *data, uint8_t b1, uint8_t b2) 
{ 
    data->rawbuttons_1 = b1; 
    data->rawbuttons_2 = b2; 
} 

void APP_Send() 
{ 
    uint8_t packet[2]; 

    packet[0] = FP_BUTTONS_Data.rawbuttons_1; 
    packet[1] = FP_BUTTONS_Data.rawbuttons_2; 

    //Send stuff away..... 
} 
+0

它的工作很好。謝謝。 –

+0

只是一個簡單的問題,因爲替代方案更好:在頂層函數中使用'data-> button_1 =(b1 >> 0)&1;'(等等)是否更有意義,因爲它交易了類似'用'shr' +'和'測試'+'setnz'?看到這一點,我肯定已經使用過這樣的構造了數百次 - 但是我認爲移位更昂貴。 – usr2564301

+0

指定'rawbuttons_1'將同時設置所有8位,不需要位操作,只需一個「MOV」指定。分配給'button_1'只會設置一位而不設置其他7位,所以使用位操作會更加昂貴。 –

2

你可能會考慮,如果你改變你的方法實質上是將兩個八位字節保存在定製的位填充結構中,以便將這兩個八位字節返回給其他地方。

假定所有的位字段的1個比特寬的(如在您的示例的情況下),可以使用比特移位來訪問的值(其中,編譯器會爲你無論如何做)像這樣:

/* Button shift values */ 
typedef enum 
{ 
    BUTTON_1 = 0, 
    BUTTON_2 = 1, 
    ... 
    BUTTON_16 = 15, 
} button_t; 

void FP_UpdateData(uint16_t *buttons, uint8_t b1, uint8_t b2) 
{ 
    /* You could also just return the value rather than passing a pointer 
    * around like this. Even better, skip this function call and do the 
    * math. */ 
    *buttons = (uint16_t)b1; 
    *buttons |= (uint16_t)(b2 << 8); 
} 

bool get_button_state(uint16_t *buttons, button_t button) 
{ 
    return *buttons & (1 << button); 
} 

void APP_Send() 
{ 
    uint8_t packet[2]; 

    packet[0] = (uint8_t)(buttons & 0xff); 
    packet[1] = (uint8_t)((buttons >> 8) & 0xff); 
} 

如果你使用GCC,你可以做以下,使壓縮結構:

typedef struct __atrribute__((packed)) { 

    /**  BYTE 0   **/   
    unsigned  button_1:1;  
    unsigned  button_2:1; 
    unsigned  button_3:1; 
    unsigned  button_4:1; 
    unsigned  button_5:1; 
    unsigned  button_6:1; 
    unsigned  button_7:1; 

    /** And BYTE 2 which I didn't paste to save SO's space lol **/ 
    // button_9:1 to button_16:1 
} FP_BUTTONS; 
2

的代碼有一些缺陷:

  • 您可以編寫C,但對APP_Send()使用C++標記和C++簽名(C需要void作爲空參數列表)。
  • 位域有一些問題,主要是在這裏,因爲它們的排序沒有定義(字段可能是自上而下或自下而上的 - 所有已經看過)。
  • (b1 & (1 << 1)) >> 1將計算爲int。對於簽名類型,右移是實現定義的。雖然它可能有效,但它肯定不是「防禦性編程」;-)
  • 位從0開始,而不是從1開始。你的錯誤被稱爲「一個」,非常常見(它甚至可能是最頻繁的編程錯誤)。
  • 此代碼可能非常昂貴,因爲編譯器可能無法將其優化爲基本指令。

因此,在嵌入式系統中,通常只使用uint8_t作爲位圖並避免位域結構。這是因爲不能保證這些寄存器與外設寄存器的佈局相匹配,例如移動等等,這非常煩人,並且很可能會影響你的定時(例如中斷處理程序)。

此外,使用蒙版允許一次很容易地操縱多個位。

enum { 
    BIT0 = (1U<<0), 
    BIT1 = (1U<<1), 
    ... 
}; 
uint8_t buttons; 

... in a function: 
buttons |= BIT1 | BIT2;  // set the corresponding bits 
buttons &= ~(BIT5 | BIT7); // clear those bits 

if (buttons & BIT4) 
    ... 

GPIOB->OUTR = BIT4 | BIT6; // set the output bits on STM32F port B 

這就是嵌入式編程的實際情況。請注意,STM32F具有16位GPIO端口,而不是8位。

哦,得到的單個比特作爲0/1值:

int single_bit = !!(buttons & BIT3) 

這將創建與所述第一否定,這是通過標準的0或1的布爾值結果。第二個否定將再次形成正面的邏輯。然而,這很少使用,上面的if ...是最多的時間。 但是,一個好的編譯器(如gcc)應該在Cortex-M上優化這一點。

後者,你可以簡單地分配給每個位字段。如果幸運的話,編譯器知道這種模式並創建一個簡單的賦值(或者至少只是一個帶有兩條指令的位域傳輸)。