Splitting out a frame of bytes

Hi there!

I hope this is a simple case of just finding out the right operator...

I have a 64 bit frame of data incoming as a CAN Bus message that looks like this:

0011100110001111000000000111100000001010001110011000101100100000

it comes in as an unsigned long long int called "CAN_Incoming"

I would like to split this out into data packets, and to do that, I know that I need to split off chunks of bits from the front of the frame. For example, I know that the first 6 digits is one piece of data, so I need to break off "001110". The second piece might be the next 3, so "011".

What operator do I need to use to perform this function?

Thanks!

Nick

Two, actually... '&' and '>>'

Bit shifting and masking (see operators posted by @anon57585045) should do the trick. Although you would need to check that your micro/ complier supports shifting of unsigned long long ints.

So I would write for example:

int data = CAN_Incoming << 6;

This should pull the 6 bits from the left of the frame which would equal "001110" = 14.

I'm not sure about the masking operator - why is this needed?

Does this alter CAN_Incoming such that when I do the next bit shift, the 6 bits I just removed are gone from CAN_Incoming?

Assuming that the "001110" are the first 6 bits received and you get them into the unsigned long long int by shifting them in one at a time into the LSB, then your 6 bits are up at the top of of the uint64. Doing a << (aka shift left) will cause them to fall out of the top of the uint64. You need to do a >> (aka shift right) as @anon57585045 said.

something like: (CAN_Incoming >>58) & 0x3F

Ok I've written a little code and have discovered that these frames are actually only half length (32 bits). So unsigned long int only.

My first data packet is "01111111000000001010000100000100"

The first two pieces of data I want to take out of this are "WiperState_M1" which is the first 3 bits, then "HW_Error_M1" which is the next 1 bit.

I have tried writing the following code to test out the basics but I just get 0 in the serial monitor...

      M1_Status = 01111111000000001010000100000100;
      int x = 31;
      int y;
      y = x - 3;
      WiperState_M1 = (M1_Status >> y) & x;
      Serial.println(WiperState_M1,BIN);
      y = y - 1;
      HW_Error_M1 = (M1_Status >> y) & x;;

Any ideas??

Off the top of my head:

      M1_Status = 0b01111111000000001010000100000100;
      
      WiperState_M1 = ( M1_Status >> 29 )  & 0x07;
      Serial.println(WiperState_M1,BIN);
      HW_Error_M1 = ( M1_Status >>  28) & 0x01;

Usually magic numbers like 29, 28, 0x07 and 0x01 are setup as constants at the top of your code with descriptive names like:

#define WiperState_M1_mask 0x07
#define WiperState_MS_pos 29
#define HW_Error_M1_mask 0x01
#define HW_Error_M1_pos 28

So it makes it a lot easier to debug/revisit the code later.

Ok, I mostly have it working except for one part. here's the code I have running right now:


unsigned long int M1_Status;
unsigned long int M2_Status;

#define WiperState_M1_mask 0x03
#define WiperState_M1_pos 29
#define HW_Error_M1_mask 0x01
#define HW_Error_M1_pos 28
#define UnderOverVolt_M1_mask 0x01
#define UnderOverVolt_M1_pos 27
#define OverTemp_M1_mask 0x01
#define OverTemp_M1_pos 26
#define Blocked_M1_mask 0x01
#define Blocked_M1_pos 25
#define Overload_M1_mask 0x01
#define Overload_M1_pos 24
#define ConfigStat_M1_mask 0x02
#define ConfigStat_M1_pos 22
#define WsmStat_Res1_mask 0x01
#define WsmStat_Res1_pos 21
#define WiperPos_M1_mask 0xA
#define WiperPos_M1_pos 11
#define TeachInStatus_M1_mask 0x02
#define TeachInStatus_M1_pos 9
#define LifeTimeEnd_M1_mask 0x01
#define LifeTimeEnd_M1_pos 8
#define MotSynchTime_M1_mask 0x06
#define MotSynchTime_M1_pos 2
#define MsgCntWsmStat_M1_mask 0x02
#define MsgCntWsmStat_M1_pos 0

int WiperState_M1;
int HW_Error_M1;
int UnderOverVolt_M1;
int OverTemp_M1;
int Blocked_M1;
int Overload_M1;
int ConfigStat_M1;
int WsmStat_Res1;
int WiperPos_M1;
int TeachInStatus_M1;
int LifeTimeEnd_M1;
int MotSynchTime_M1;
int MsgCntWsmStat_M1;

void setup() {
  Serial.begin(9600);
}

void loop() {
  Motor_Status();
  delay(1000);
}

void Motor_Status() {

      M1_Status = 0b01111111000000001010000100000100;
      WiperState_M1 = ( M1_Status >> WiperState_M1_pos )  & WiperState_M1_mask;
      HW_Error_M1 = ( M1_Status >>  HW_Error_M1_pos) & HW_Error_M1_mask;
      UnderOverVolt_M1 = ( M1_Status >>  UnderOverVolt_M1_pos) & UnderOverVolt_M1_mask;
      OverTemp_M1 = ( M1_Status >>  OverTemp_M1_pos) & OverTemp_M1_mask;
      Blocked_M1 = ( M1_Status >>  Blocked_M1_pos) & Blocked_M1_mask;
      Overload_M1 = ( M1_Status >>  Overload_M1_pos) & Overload_M1_mask;
      ConfigStat_M1 = ( M1_Status >>  ConfigStat_M1_pos) & ConfigStat_M1_mask;
      WsmStat_Res1 = ( M1_Status >>  WsmStat_Res1_pos) & WsmStat_Res1_mask;
      WiperPos_M1 = ( M1_Status >>  WiperPos_M1_pos) & WiperPos_M1_mask;
      TeachInStatus_M1 = ( M1_Status >>  TeachInStatus_M1_pos) & TeachInStatus_M1_mask;
      LifeTimeEnd_M1 = ( M1_Status >>  LifeTimeEnd_M1_pos) & LifeTimeEnd_M1_mask;   
      MotSynchTime_M1 = ( M1_Status >>  MotSynchTime_M1_pos) & MotSynchTime_M1_mask; 
      MsgCntWsmStat_M1 = ( M1_Status >>  MsgCntWsmStat_M1_pos) & MsgCntWsmStat_M1_mask; 

      Serial.print(WiperState_M1,DEC);
      Serial.print(" / ");
      Serial.print(HW_Error_M1,DEC);
      Serial.print(" / ");
      Serial.print(UnderOverVolt_M1,DEC);
      Serial.print(" / ");
      Serial.print(OverTemp_M1,DEC);
      Serial.print(" / ");
      Serial.print(Blocked_M1,DEC);
      Serial.print(" / ");
      Serial.print(Overload_M1,DEC);
      Serial.print(" / ");
      Serial.print(ConfigStat_M1,DEC);
      Serial.print(" / ");
      Serial.print(WsmStat_Res1,DEC);
      Serial.print(" / ");
      Serial.print(WiperPos_M1,DEC);
      Serial.print(" / ");
      Serial.print(TeachInStatus_M1,DEC);
      Serial.print(" / ");
      Serial.print(LifeTimeEnd_M1,DEC);
      Serial.print(" / ");
      Serial.print(MotSynchTime_M1,DEC);
      Serial.print(" / ");
      Serial.println(MsgCntWsmStat_M1,DEC);
}

The output I'm expecting in the serial monitor is on top vs what I'm getting is on the bottom:
3 / 1 / 1 / 1 / 1 / 1 / 0 / 0 / 20 / 0 / 1 / 1 / 0
3 / 1 / 1 / 1 / 1 / 1 / 0 / 0 / 0 / 0 / 1 / 0 / 0

This means that it seems to work great up to WiperPos_M1, which is a 10 bit variable. I'm currently using 0xA for a mask - is there some reason why that wouldn't work? There should be plenty of room for it in the int. It should be reading 20 but I'm getting 0.

Ok, first thing to fix is the mask values. Masks work by hiding all the bits you don't want by doing a logical AND (i.e. '&').

In this case you are wanting to extract sequential groups of bits, so the masks go like this:

1 Bit Mask = 0x01 => 0000 0001
2 Bit Mask = 0x03 => 0000 0011
3 Bit mask = 0x07 => 0000 0111
4 Bit mask = 0x0F => 0000 1111
5 Bit mask = 0x1F => 0001 1111

In simple terms the mask value is 2^n - 1, so to extract the lower 6 bits, it's 2^6 - 1 = 63 = 0x3F => 0011 1111.

So WiperPos_M1_mask should be 2^10-1 = 1023 = 0x03FF => 0011 1111 1111.

MotSynchTime_M1_mask should be 0x3F as it's 6 bits.

As above, masks can be synthesized like this. Suppose you want to mask the lower 3 bits. The modulus is 8, 2^3. To make a mask, just subtract 1. 2^3-1 = 7. The mask for any modulus n that is a power of 2 (thus a series of consecutive bits) is 2^n - 1.

The significance is - in this way, shifts and masks can be based on the same defined constants, without having to puzzle them out and hard code them.

It is sometimes easier to work this kind of problems out on paper by aligning 1s and 0s. When you have the binary mask you want, any calculator with "programmer's" functionality will convert it to decimal or hex or octal to suit your tastes.

But that obfuscates it in the source code.

Which is hardly an issue, in my opinion, as long as the masks are working and as long as you define them with meaningful names in your code.

The meaningful names - that's the really important part. The technique in reply #9 wasn't fully explicated with a complete example. Let's try. Suppose I have an unsigned long, and I want to extract bit 9 to bit 13 inclusive. In this scenario, bit 14 might be the start of another bit field, or simply unused data. Often there are multiple fields so let's run with the former.

const int FIELD1_OFFSET = 9;
const int FIELD2_OFFSET = 14;

I need two numbers, the shift amount and the mask.

const int FIELD1_SIZE = FIELD2_OFFSET - FIELD1_OFFSET;  // 5 in this case
const int FIELD1_MASK = (1 <<  FIELD1_SIZE) - 1 ;  // 31 in this case

I think I'm ready!

unsigned long data = 0xDEADBEEF;
unsigned long result = ( data >> FIELD1_OFFSET ) & FIELD1_MASK;

This is probably an error, the two bits of the mask are not adjacent.

Perfect illustration of my point.

Would a struct with bit fields not work for a fixed length encoded value?

Yes, but you would need a union, no? I know someone who will come in here and give you a sound thrashing for that. :wink:

The union would not help, the standard prohibits using it for conversions,
pulling different union members after storing one.

I would just cast its address into a target structs pointer, I know the content of the data,
so the operation is valid.

Thanks everyone for your excellent input! I think I understand how it works now and have been able to get it to work correctly. Working with binary is definitely hard to get your head around, thanks so much for enlightening me!