5

Given the following minimal working example. I tested this initially with uint16_t and it worked with no warnings. After that, I adjusted everything to work with uint32_t.

#include <stdint.h> /* needed for the uint16_t type*/
#include <inttypes.h> /* needed for sscanf uint16_t format placeholder  */
#include "printf.h"

#define SET_CMD_ID(ID)    ( (ID) << 27)
#define SET_CMD_CMD(CMD)  ( (CMD)  << 25)
#define SET_CMD_TYPE(TYPE)  ( (TYPE) << 19)
#define SET_CMD_PKT(PKT)  ( (PKT)    )


void setup() {
  Serial.begin(115200);
  printf_begin();
  Serial.print(F("\n\rExample command\n\r: "));


}

void loop() {
  uint32_t payload = 0;

  uint8_t id = 3;   // 00000 - 5
  uint8_t cmd = 2;  // 00    - 2
  uint8_t type = 1; // 000000 - 6 
  uint32_t pkt = 0; // 0000000000000000000 - 19

  payload =  
        SET_CMD_ID(id)   |
        SET_CMD_CMD(cmd)  |
        SET_CMD_TYPE(type) |
        SET_CMD_PKT(pkt);
  Serial.print(F("Now sending "));
  Serial.println(payload);
  delay(2000);        // Try again soon
}

Verifying this sketch outputs:

/path/to/ino/Arduino32bitTest.ino: In function 'void loop()':
/path/to/ino/Arduino32bitTest.ino:5:37: warning: left shift count >= width of type [enabled by default]
 #define SET_CMD_ID(ID)    ( (ID) << 27)
                                     ^
/path/to/ino/Arduino32bitTest.ino:28:9: note: in expansion of macro 'SET_CMD_ID'
         SET_CMD_ID(id)   |
         ^
/path/to/ino/Arduino32bitTest.ino:6:39: warning: left shift count >= width of type [enabled by default]
 #define SET_CMD_CMD(CMD)  ( (CMD)  << 25)
                                       ^
/path/to/ino/Arduino32bitTest.ino:29:9: note: in expansion of macro 'SET_CMD_CMD'
         SET_CMD_CMD(cmd)  |
         ^
/path/to/ino/Arduino32bitTest.ino:7:41: warning: left shift count >= width of type [enabled by default]
 #define SET_CMD_TYPE(TYPE)  ( (TYPE) << 19)
                                         ^
/path/to/ino/Arduino32bitTest.ino:30:9: note: in expansion of macro 'SET_CMD_TYPE'
         SET_CMD_TYPE(type) |
         ^

Sketch uses 3,174 bytes (9%) of program storage space. Maximum is 32,256 bytes.
Global variables use 216 bytes (10%) of dynamic memory, leaving 1,832 bytes for local variables. Maximum is 2,048 bytes.

Executing the sketch on an Arduino Uno results in:

Example command

: Now sending 0
Now sending 0
Now sending 0
Now sending 0

I googled the message of the warning and found out that it could mean that I'm trying to shift 32bit which is undefined, but I'm not sure on that. So my question is: What am I doing wrong here? How can I get this to work?

2
  • 2
    It is basically duplicate of Problem with bitwise OR
    – KIIV
    Jul 13, 2016 at 10:35
  • @KIIV : Thanks, the thread pointed me in the right direction
    – JustCoding
    Jul 13, 2016 at 11:11

2 Answers 2

5

It's basically an inability of the compiler to auto-promote the source variables to a size big enough to fit the shifted version.

The default type for all operations, unless otherwise specified, is 16-bit. By shifting more than 16 bits you are overflowing that default type.

To get around it you have to manually tell the compiler that you want to work with 32-bits:

#define SET_CMD_ID(ID)      ( ((uint32_t)ID) << 27)
#define SET_CMD_CMD(CMD)    ( ((uint32_t)CMD)  << 25)
#define SET_CMD_TYPE(TYPE)  ( ((uint32_t)TYPE) << 19)
#define SET_CMD_PKT(PKT)    ( ((uint32_t)PKT)    )
0
2

Another solution would be to not shift values at all. It seems to be very common in Arduino and other micro-controller projects to use antique ANSI C. But ALL major compiles support "bit fields". The language removes your headache of packing bits into and unpacking bits from a larger type.

It goes like this:

struct {
    uint32_t pkt  : 19;
    uint32_t type :  6;
    uint32_t cmd  :  2;
    uint32_t id   :  5;
} payload;

As long ans 19 + 6 + 2 + 5 does not exceed 32, the struct will still take up only 32 bits.

Setting values works like this:

payload.id = 3;
payload.cmd = 2;
payload.type = 1;
payload.pkt = 0;

Reading values works the same way. No need to shift mask or do any other operations by hand. The compiler does everything for you without making any mistakes or without forgetting about to mask when reading...

The only thing you have to remember is that bitfields are packed from least significant bit to the most significant bit. So a struct like this:

struct {
    uint8_t r :  2;
    uint8_t g :  3;
    uint8_t b :  2;
    uint8_t a :  1;
} someType;

Will result it a byte where the values are layout like this:

abbgggrr

This also works for signed values. But you have to make sure they fit and be aware of the twos complement representation.

Hopefully more Arduino and micro controller developer will adopt this and take advantage of the compiler.

2
  • Note: depending on the compiler and version you may need to manually pack the struct by adding __attribute__((packed)) to the end of it. I do that always by default to ensure proper packing regardless of compiler version or target chip.
    – Majenko
    Jul 13, 2016 at 12:34
  • The order of the fields in the word, and whether a field can straddle a byte boundary, is implementation defined. Thus your usage of bit fields is not portable. Jul 13, 2016 at 12:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.