Reading MSB and LSB if data is left-justified - c

While going through my coursework, I came across a function that reads temperature from the TMP102 sensor (only required to measure positive temperatures).
The function first reads the MSB and LSB using I2C. Since the temperature data is 12-bit and left-justified, the function proceeds as follows:
temp = ( (MSB << 8) | LSB) >> 4
I do not understand why this is done. Could someone please help me explain how the above line of code is related to the data being 12-bit and left-justified?

Let v be a bit of the temperature value and p be a padding bit on the right, then you have
MSB = vvvvvvvv
LSB = vvvvpppp
---
MSB << 8 = vvvvvvvv 00000000
(MSB << 8) | LSB = vvvvvvvv vvvvpppp
((MSB << 8) | LSB) >> 4 = 0000vvvv vvvvvvvv
In the last line, you see the correct representation as a 16bit value (with the upper 4 bits always 0).

Related

ADC raw data forming

I would like to ask you for an explanation about this part of my code. I am not sure what it really does. This is example code and I would like to understand it. The purpose of the original code should be acquiring the data from ADC in the streaming mode. This should be about forming the raw data. Thank you.
#define CH_DATA_SIZE 6
uint8_t read_buf[CH_DATA_SIZE];
uint32_t adc_data;
TI_ADS1293_SPIStreamReadReg(read_buf, count);
adc_data = ((uint32_t) read_buf[0] << 16) | ((uint16_t) read_buf[1] << 8)
| read_buf[2];
I will skip the variable declaration, because I will refer to it in the rest of the description.
The code begins at this line:
TI_ADS1293_SPIStreamReadReg(read_buf, count);
From a Google search, I assume you have this function from this file. If it is this function, it will read three register from this module (see 8.6 Register Maps, the data registers DATA_CHx_ECG are three bytes long, which is what should be in the count variable).
Once this function executed, you have the ECG data in the first three bytes of the read_buf variable, but you want a 24-bit value since the quantified value is a 24-bit value.
Since we don't have uint24_t in C (and no other language I know of), we take the next possible size, which is uint32_t to declare the adc_data variable.
Now the following code does rebuild a single 24-bit value from the 3 bytes we read from the ADC:
adc_data = ((uint32_t) read_buf[0] << 16) | ((uint16_t) read_buf[1] << 8)
| read_buf[2];
From the datasheet and the TI_ADS1293_SPIStreamReadReg, we know that the function does read the values in the order their addresses come, in this case high-byte, middle-byte and low-byte in this order (respectively in read_but[0], read_buf[1] and read_buf[2]).
To rebuild the 24-bit value, the code shifts the value with the appropriate offset: read_buf[0] goes from bits 23 to 16 thus shifted 16 bits, read_buf[1] from bits 15 to 8 thus shifted 8 bits and read_buf[2] from 7 to 0 thus shifted 0 bits (this shift is not represented). We will represent them as such (the 0xAA, 0xBB and 0xCC are example values to show what happens):
read_buf[0] = 0xAA => read_buf[0] << 16 = 0xAA0000
read_buf[1] = 0xBB => read_buf[0] << 8 = 0x00BB00
read_buf[2] = 0xCC => read_buf[0] << 0 = 0x0000CC
To combine the three shifted values, the code uses a bitwise-or | which results in this:
0xAA0000 | 0x00BB00 | 0x0000CC = 0xAABBCC
And you now have a 24-bit value of you ADC reading.

Can I add the bits of two ports to make a new bit sequence?

In my lab I'm supposed to test a 9 bit value but each port only contains 8 bits. According to the instructions, I use all bits on PORTD including PB0 to make this 9 bit value, but I have no plan on how I can do that and that's essentially the whole challenge of this problem. The rest of it should be easy, I only need to store a 9 bit value "borrowing" a bit from another port.
Microcontroller: ATmega1284
Problem:
(Challenge): A car's passenger-seat weight sensor outputs a 9-bit value (ranging from 0 to 511) and connects to input PD7..PD0PB0 on the microcontroller. If the weight is equal to or above 70 pounds, the airbag should be enabled by setting PB1 to 1. If the weight is above 5 but below 70, the airbag should be disabled and an "Airbag disabled" icon should light by setting PB2 to 1. (Neither B1 nor B2 should be set if the weight is 5 or less, as there is no passenger).
Bits from separate registers can be combined in any way necessary using the bit-wise operators:
& – Bitwise AND
| – Bitwise OR
~ – Bitwise NOT
^ – Bitwise XOR
<< – Left Shift
>> – Right Shift
The somewhat vague notation "PD7..PD0PB0" suggests that PB0 is the LSB or the weight value. In that case, given:
PORTD DDDDDDDD
PORTB xxxxxxxB
then:
uint16_t weight = ((uint16_t)PORTD << 1u) | (PORTB & 0x01) ;
will result in a value for weight composed of bits:
0000000DDDDDDDDB
The sub-expression ((uint16_t)PORTD << 1u) shifts the value of PORTD left by 1 bit:
0000000DDDDDDDD0
(PORTB & 0x01) zeros all but bit-0, leaving bit-0 unchanged:
0000000B
The two sub-expressions are then bit-wise OR'ed:
0000000DDDDDDDD0
OR 0000000B
----------------
= 0000000DDDDDDDDB
Note that it might be possible (safety considering) to simplify the task by using just the 8 bit value in PORTD and halving the limit values. Currently you have: "above 5 but below 70", if you just read PORTD, the limits would be 3 >= PORTD < 35. This may in fact be safer - when reading a single value from two separate registers you need to be certain the values you have are for the same sample, and that the value in the first register read did not change before you read the second. With only the one LSB in the PORTB register this will have little impact, but also makes it of limited value unless the data is somehow latched while it is read.
It depends on if PB0 is the MSB or LSB of register B. But what you cand do is using uint16_t variable and some masks to get the value you need. For example, you could do one of the following things.
PB0 is MSB
uint16_t sensor;
sensor = (PORTD << 1) | (PORTB >> 7);
PB0 is LSB
uint16_t sensor;
sensor = (PORTD << 1 ) | (PORTB & 0x01);
It can be done in many ways, and it is just one of them. In both solutions, all bits of register D are the most significant bits, and LSB, in the first one, would be the MSB of register B, and in the second one, would be the LSB of register B.
If you want your code is MISRA-C compliant you should initialize sensor variable.

How does this reverseBytes method work?

I was looking at this function online and am wondering how it works:
/*
* reverseBytes - reverse bytes
* Example: reverseBytes(0x12345678) = 0x78563412
* Legal ops: ! ~ & ^ | + << >>
*/
int reverseBytes(int x)
{
int newbyte0 = (x >> 24) & 0xff;
int newbyte1 = (x >> 8) & 0xff00;
int newbyte2 = (x << 8) & 0xff0000;
int newbyte3 = x << 24;
return newbyte0 | newbyte1 | newbyte2 | newbyte3;
}
Here's what I think I understand:
0xff, 0xff00, and 0xff0000 in binary are 1111 1111, 1111 1111 0000 0000, and 1111 1111 0000 0000 0000 0000 respectively
The method creates four new bytes with masks (0xff, etc), and then adds their values together using the | operator
I really don't get how this reverses the bytes though. I would appreciate a detailed explanation. Thanks!
The code assumes a 32 bit integer and 8 bit bytes. A 32 bit integer is made up of 4 bytes:
Let's say these 4 bytes are laid out in memory like so:
+---------------------------------+
|Byte 4 | Byte 3 | Byte 2 | Byte 1|
+---------------------------------+
This could relate to the Endianess of a given CPU type. When interpreting an integer that is made up of several bytes, some CPU families will treat the leftmost byte, the one with a lower memory address as the most significant byte of the integer - such CPUs are called big endian. Other CPUs will do the reverse, they will treat the rightmost byte within an integer , the byte with the largest memory address as the most significant byte - little endian CPUs. So your functions convert an integer from one endian to another.
int newbyte0 = (x >> 24) & 0xff;
This takes the integer (the 4 bytes) depicted above, shifts it 24 bits to the right, and masks off everything but the lower 8 bits, newbyte0 looks like this now, where Byte 4 is the original Byte 4 of x and the other 3 bytes have all bits set to zero.
+---------------------------------+
| 0 | 0 | 0 | Byte 4 |
+---------------------------------+
Similarely
int newbyte1 = (x >> 8) & 0xff00;
Shifts the bits 8 bits to the right, and masks off everything but the 8 bits in the 2. byte from the left. The result looks like this with, with only Byte 3 remaining of the original value of x
+---------------------------------+
| 0 | 0 | Byte 3 | 0 |
+---------------------------------+
The 2 leftmost bytes are handled similarly, just x is shifted left to accomplish the same thing.
Finally you have
newbyte0 | newbyte1 | newbyte2 | newbyte3;
Which combines all the integers you created above, each with only 8 bits remaining from the original x. Do a bitwise or of them, and you end up with
+---------------------------------+
|Byte 1 | Byte 2 | Byte 3 | Byte 4|
+---------------------------------+
int newbyte0 = (x >> 24) & 0xff;
Shifts the number 24 bits to the right, so that the left-most byte will now be the right-most byte. It then uses a mask (0xff) to zero out the rest of the bytes, which is redundant as the shift will have zeroed them anyways, so the mask can be omitted.
int newbyte1 = (x >> 8) & 0xff00;
Shifts the number 8 bits to the right, so that the second byte from the left is now the second byte from the right, and the rest of the bytes are zeroed out with a mask.
int newbyte2 = (x << 8) & 0xff0000;
Shifts the number 8 bits to the left this time - essentially the same thing as the last line, only now the second byte from the right becomes the second byte from the left.
int newbyte3 = x << 24;
The same as the first line (this time the redundant mask really is omitted) - the right-most byte becomes the left-most byte.
return newbyte0 | newbyte1 | newbyte2 | newbyte3;
And finally you just OR all the bytes to finish the reversal.
You can actually follow this process step-by-step in code by using printf("%x", newbyte) to print each of the bytes - the %x format allows you to print in hexadecimal.
Lets assume for 32 bit system you have passed 0x12345678 to the function.
int newbyte0 = (x >> 24) & 0xff; //will be 0x00000012
int newbyte1 = (x >> 8) & 0xff00; //will be 0x00003400
int newbyte2 = (x << 8) & 0xff0000; //will be 0x00560000
int newbyte3 = x << 24; //will be 0x78000000
return newbyte0 | newbyte1 | newbyte2 | newbyte3; will be 0x78563412
This function just shift byte to the right position in an integer and than OR all of them together.
For example x is 0xAABBCCDD:
For the first byte we shift all byte to the right, so we have 0x00000000AA & 0xFF which is 0xAA.
For the second byte we have 0x00AABBCC & 0xFF00 which is 0x0000BB00
And so on.
We just shift bits to the right position and erase all other bits.
Yes, your understands the code correctly, but of course it assumes int as 32 bits value.
int newbyte0 = (x >> 24) & 0xff; // Shift the bits 24~31 to 0~7
int newbyte1 = (x >> 8) & 0xff00; // Shift the bits 16~23 to 8~15
int newbyte2 = (x << 8) & 0xff0000; // Shifts bit bits 8~15 to 16~23
int newbyte3 = x << 24; // Shift bits 0~7 to 24~31
return newbyte0 | newbyte1 | newbyte2 | newbyte3; // Join all the bits

constructing 32 bit unsigned int in c

I am trying to construct a 32 bit binary value using an unsigned int. This is how
the 32 bit is split into parts
part 1 = 4 bit
part 2 = 2 bit
part 3 = 12 bit
part 4 = 2 bit
part 5 = 12 bit
How do i use the parts to construct 32 bit binary value (unsigned int) using
bit shifting can someone please help
(cant just shift bits... what if i want to update certain part?)
This is typical bit-shifting/bit-masking stuff. Here's how you set the initial value:
uint32_t value = ((part1 & 0xF) << 28)
| ((part2 & 0x3) << 26)
| ((part3 & 0xFFF) << 14)
| ((part4 & 0x3) << 12)
| (part5 & 0xFFF);
Each line uses a bitwise-AND (&) to clear the upper bits of each part so it doesn't overflow its allocated bit-width within the final value. For example, if part4 was 0xFF and you forgot the & 0x3 then the upper 6 bits of part4 (0xFC) would spill into the region for part3. Then the part is shifted (<<) to its final location and bitwise-OR'd (|) with the rest of the parts.
Some developers accomplish the same thing via bitfields but I don't recommend that approach due to potential portability issues.
Most (all?) of the other answers here so far have forgotten the bitwise-AND portion of the solution. Their answers will result in bugs if the part values ever exceed the specified bit width.
If you want to update a particular portion of the value, you'll need some more bit-masking via bitwise-AND and bitwise-OR. For example, to update part4 you'd do this:
value &= ~(0x3 << 12); /* Clear the part4 region */
value |= (part4 & 0x3) << 12; /* Set the part4 region to the new value */
That first line is a little tricky if you're new to bitwork in C. It says take 0x3 and shift it by 12 (result = 0x00003000), perform a bitwise-complement (result = 0xFFFFCFFF), and set value equal to itself bitwise-AND'd with that result. That's how you clear the part4 region of the value... because you're bitwise-AND'ing that region with zero the result is that region is now zero.
The second line sets the zeroed part4 region to the new value, just like we did above when setting the initial value.
You just use shifting, as you suggested:
uint32_t x = (part1 << 28)
| (part2 << 26)
| (part3 << 14)
| (part4 << 12)
| (part5);
Looks like you want this value:
part5 + (part4 << 12) + (part3 << 14) + (part2 << 26) + (part1 << 28)
(Make sure all five variables are of type uint32_t or some such.)

Take the last 2 bits of a nibble in C

I have this byte: 10111011 and i want to split into 2 nibble (msb and lsb).After that i want to take the last 2 bits from the lsb (so i want 11 from 1011).
I know that:
With 10011011 >> 4 i get the msb (1001)
With 10011011 & 0xf i get the lsb (1011)
Now what can i do to take the 11 from lsb 1011?
Just the same: bits = lsb & 0x03
The bitmask for the first two bits is 3, so simply use:
int val = x & 3;
Since the bits are already in the proper position you don't need some shift operator.
For the above value it would be.
val = (x >> 4) & 3;
You'd do:
foo & 0x03
Where foo is the bit-pattern you want masked.

Resources