2015-05-30 82 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上优化这一点。

后者,你可以简单地分配给每个位字段。如果幸运的话,编译器知道这种模式并创建一个简单的赋值(或者至少只是一个带有两条指令的位域传输)。