I have made this function which unpacks received I2C messages and puts the individual values in a uint32_t array. It works when I use a fixed macro function for a fixed number of 2 bytes into a uint16_t , but I attempted to use a for loop to append any number of bytes to my values, since different i2c packets might have different value types. My problem lays in the way I do the bit operations, my bitwise knowledge seems too limited. What is wrong with option 2 here?
#define BytesToU16LE(B) ( \
(uint16_t)((B)[1]) << 8 \
| (uint16_t)((B)[0]) \
)
uint8_t ezi2cBuffer[100];
void unpack_i2c_packet(uint8_t nb_values_in_packet, uint8_t bytes_per_value, uint32_t * values, uint8_t buffer_head_size)
{
uint8_t current_value_bytes[bytes_per_value];
uint16_t payload_size = bytes_per_value * nb_values_in_packet;
uint8_t current_value_index = 0;
if(!nb_values_in_packet | !bytes_per_value)
return;
for(int i = 0; i < payload_size; i++)
{
current_value_bytes[(i % bytes_per_value) + buffer_head_size] = ezi2cBuffer[i + buffer_head_size]; // message head does not contain values, separate from payload
if((i % bytes_per_value) == (bytes_per_value - 1))
{
/* OPTION 1 WITH MACRO WORKS */
values[current_value_index] = BytesToU16LE(current_value_bytes);
/* OPTION 2 FOR LOOP PUTS RANDOM DATA IN MY VAR ARRAY! */
for(int bi = 0; bi < bytes_per_value; bi ++)
{
values[current_value_index] |= ((uint32_t)(current_value_bytes[bi]) << (bi * 8));
}
current_value_index++;
}
}
}
values[current_value_index] is not initialized in OPTION 2. You should initialize it to zero or the value after loop will depend on the value before the loop.
/* OPTION 2 FOR LOOP PUTS RANDOM DATA IN MY VAR ARRAY! */
values[current_value_index] = 0; /* add this */
for(int bi = 0; bi < bytes_per_value; bi ++)
{
values[current_value_index] |= ((uint32_t)(current_value_bytes[bi]) << (bi * 8));
}
Related
I want to convert two times the Signal[8] values into a uint16_t word, so I can send it via the SPI port.(shift register)?
I tried the following, but it doesn't work:
the code was like that, my you can compile it.
void senddata(void){
uint8_t NZero = 0;
uint16_t timeout;
uint8_t value ;
volatile uint8_t Signal[8]={RGB_NC_0, RGB_1, RGB_2, RGB_3, RGB_4, RGB_5, RGB_6, RGB_NC_7}; // to be set by the state machine
volatile uint8_t SPIData[16]={0};
for(int i=0;i<8;i++){
nonZero|= Signal[i];
}
int i , j;
//Set LATCH low
GPIO_WriteBit(LED_LATCH_PORT, LED_LATCH, Bit_RESET);
//Set blank high
GPIO_WriteBit(LED_BLANK_PORT, LED_BLANK, Bit_SET);
//Enable SPI
SPI_Cmd(LED_SPI, ENABLE);
//iterate through the registers
for(i = 2 - 1; i >= 0; i--){
//iterate through the bits in each registers
for(j = 8 - 1; j >= 0; j--){
valr = Signal[i] & (1 << j);
SPI_I2S_SendData(LED_SPI, value);
while(SPI_I2S_GetFlagStatus(LED_SPI, SPI_I2S_FLAG_TXE) == 0 && timeout < 0xFFFF) //Odota että TXE=1
{ timeout++; }
if(timeout == 0xFFFF){break;}
}
}
SPI_Cmd(LED_SPI, DISABLE); /*!< SPI disable */
GPIO_WriteBit(LED_LATCH_PORT, LED_LATCH, Bit_SET);//Set LATCH high
if(NZero){
GPIO_WriteBit(LED_BLANK_PORT, LED_BLANK, Bit_RESET);//Set BLANK low
}
else{
GPIO_WriteBit(LED_BLANK_PORT, LED_BLANK, Bit_SET);//Set BLANK high
}
}
You can combine each subsequent two bytes into the SPI port register as follows:
for(size_t i = 0; i < sizeof(signal/sizeof(*signal); i += 2)
{
spiPortRegister = (uint16_t)signal[i + 0] << 0
| (uint16_t)signal[i + 1] << 8;
// send via SPI here!
}
// a *totally* generic implementation might add special handling for
// odd arrays, in your specific case you can omit...
Analogously you split back on receiver side:
for(size_t i = 0; i < sizeof(signal/sizeof(*signal); i += 2)
{
// receive via SPI here
signal[i + 0] = (uint8_t) spiPortRegister >> 0;
signal[i + 1] = (uint8_t) spiPortRegister >> 8;
}
Note: Additions or shifts by 0 are unnecessary and only added for code consistency; they will be optimised away by compiler anyway, but you can omit, if you prefer. Similarly the casts in second case, but these in addition silent the compiler from warning about precision loss.
Note, though, that even though promotion to int occurs in first case int might only be of size of 16 bits – and as you apparently operate on a MCU chances for rise – in which case the shift could provoke overflow, thus undefined behaviour, thus the cast should be applied in any case!
Endianness independent
uint16_t get16(volatile uint8_t *table)
{
return *table | ((uint16_t)*(table + 1) << 8);
}
or depending on endianess
uint16_t get16(volatile uint8_t *table)
{
uint16_t result;
memcpy(&result, table, sizeof(result));
return result;
}
I'm reading the values from a SD card in an ARM micro:
Res = f_read(&fil, (void*)buf, 6, &NumBytesRead);
where fil is a pointer, buf is a buffer where the data is stored.
And that's the problem: it's an array but I'd like to have the contents of that array in a single variable.
To give an actual example: the 6 bytes read from the file are:
buf[0] = 0x1B
buf[1] = 0x26
buf[2] = 0xB3
buf[3] = 0x54
buf[4] = 0xA1
buf[5] = 0xCF
And I'd like to have: uint64_t data be equal to 0x1B26B354A1CF. That is, all the elements of the array "concatenated" in one single 64 bit integer.
Without type punning you can do as below.
uint64_t data = 0;
for (int i=0; i<6; i++)
{
data <<= 8;
data |= (uint64_t) buf[i];
}
Use union but remember about the endianes.
union
{
uint8_t u8[8];
uint64_t u64;
}u64;
typedef union
{
uint8_t u8[8];
uint64_t u64;
}u64;
typedef enum
{
LITTLE_E,
BIG_E,
}ENDIANESS;
ENDIANESS checkEndianess(void)
{
ENDIANESS result = BIG_E;
u64 d64 = {.u64 = 0xff};
if(d64.u8[0]) result = LITTLE_E;
return result;
}
uint64_t arrayToU64(uint8_t *array, ENDIANESS e) // for the array BE
{
u64 d64;
if(e == LITTLE_E)
{
memmove(&d64, array, sizeof(d64.u64));
}
else
{
for(int index = sizeof(d64.u64) - 1; index >= 0; index--)
{
d64.u8[sizeof(d64.u64) - index - 1] = array[index];
}
}
return d64.u64;
}
int main()
{
uint8_t BIG_E_Array[] = {0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80};
ENDIANESS e;
printf("This system endianess: %s\n", (e = checkEndianess()) == BIG_E ? "BIG":"LITTLE");
printf("Punned uint64_t for our system 0x%lx\n", arrayToU64(BIG_E_Array, e));
printf("Punned uint64_t for the opposite endianess system 0x%lx\n", arrayToU64(BIG_E_Array, e == BIG_E ? LITTLE_E : BIG_E));
return 0;
}
To things to take care of here:
have the bytes be ordered correctly
read the six bytes into one 64bit integer
Issue 1 can be taken care of by storing the byte coming in in network byte order (Big Endian) into the 64 bit integer in host byte order by for example using the two marcos below:
/* below defines of htonll() and ntohll() are taken from this answer:
https://stackoverflow.com/a/28592202/694576
*/
#if __BIG_ENDIAN__
# define htonll(x) (x)
# define ntohll(x) (x)
#else
# define htonll(x) ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
# define ntohll(x) ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
#endif
Issue 2 can be solved in multiple ways:
Extending your approach
#define BUFFER_SIZE (6)
...
assert(BUFFER_SIZE <= sizeof (uint64_t));
uint8_t buffer[BUFFER_SIZE];
FILE * pf = ...; /* open file here */
/* test if file has been opened successfully here */
... result = f_read(pf, buffer, BUFFER_SIZE, ...);
/* test result for success */
uint64_t number = 0;
memset(&number, buffer, BUFFER_SIZE)
number = ntohll(number);
Use "Type Punning" by using a union
union buffer_wrapper
{
uint8_t u8[sizeof (uint64_t)];
uint64_t u64;
}
Instead of
uint8_t buffer[BUFFER_SIZE];
use
union buffer_wrapper buffer;
and instead of
memcpy(&number, buffer, BUFFER_SIZE)
number = ntohll(number)
use
number = ntohll(buffer.u64)
I am new to c language and having some trouble in a program.I have 3 arrays
size = 8;
u8 a [size]; // a = 0x0D
u8 b [size]; // b= 0xDE
u16 new_buffer[size]; // i want to see as 0xDE0D on the terminal
I need to move the contents of a as Lower order byte and contents of b as higher order byte in the new_buffer and later right shift the the new_buffer by three bits. I read about it and was suggested to use two for loops. I tried it but not getting appropriate output.
please help me for this..
u8 SendBuffer[BUFFER_SIZE];
u8 RecvBuffer[BUFFER_SIZE];
u16 Buffer[BUFFER_SIZE];
int main(void) {
RecvBuffer[0] = 0x00;
RecvBuffer[1] = 0x0D;
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
printf("Sensor data is = 0x%x \n\r", RecvBuffer[0]);
printf("---Data received successfully---\n\r");
return 0;
}
Thanks
Two versions here, depending on the endian-ness
for (i=0; i<BUFFER_SIZE; i++) {
// little endian
new_buffer[i] = (u16)a[i] << 8 | (u16)b[i]);
// big endian
new_buffer[i] = (u16)b[i] << 8 | (u16)a[i]);
// bit shift
new_buffer[i] >>= 3;
}
Or, to rotate the buffer 3 bits to the right
u16 bits, prev = 0;
for (i=0; i<BUFFER_SIZE; i++) {
bits = new_buffer[i];
new_buffer[i] = bits >> 3 | prev;
prev = bits << (sizeof(u16)*8 - 3);
}
Note that u8 is not part of the standard, you mean uintX_t, and these values are not arrays but variables:
uint8_t a = 0X0D;
uint8_t b = 0XDE;
uint16_t new_buffer = ((uint16_t)b << 8) | a;
As pointed out by #WeatherVane, you need to reverse those values if you are under big endian:
uint16_t new_buffer = ((uint16_t)a << 8) | b;
Lets say I have this byte
uint8_t k[8]= {0,0,0,1,1,1,0,0};
Is there a way to get this to become a single integer or hex?
If k represents 8 bytes of the 64-bit integer, go through the array of 8-bit integers, and shift them into the result left-to-right:
uint64_t res = 0;
for (int i = 0 ; i != 8 ; i++) {
res <<= 8;
res |= k[i];
}
The direction of the loop depends on the order in which the bytes of the original int are stored in the k array. The above snippet shows the MSB-to-LSB order; if the array is LSB-to-MSB, start the loop at 7, and go down to zero.
If the bytes represent individual bits, shift by one rather than eight.
This should do the trick:
int convertToInt(uint8_t k[8], bool leastSignificantFirst) {
int res = 0;
for (int i = 0; i < 8; ++i) {
if (leastSignificantFirst) {
res |= (k[i] & 1) << (7 - i);
} else {
res |= (k[i] & 1) << i;
}
}
return res;
}
I have got an array of chars that I'm trying to bitwise shift right >>, then & with another array. I think I have got the wrong idea of how to do this.
I thought, even though it was an array of chars just stating my_array >>= 1 would shift everything but I am getting an error: "error: invalid operands to binary >> (have ‘char[8]’ and ‘int’)"
The bitwise comparision I am trying to do is with a similar size array initiated to all "0's"...for that I'm getting: "error: invalid operands to binary & (have ‘char *’ and ‘char *’)"
Do I need to convert these array's into something else before I can shift and compare?
Sorry, I was not super clear... All great advice up to this point and I think I am realizing more that there is no super easy way to do this. More specifically, what I am trying to do is shift the bits of the WHOLE char array right 1, adding the bit shifted off the right back to the left most side of the array, do the bitwise compare with another array of same size.
Technically the compare doesn't have to be array with array... I just need the bits. Would it be easier to convert the array's to something else before trying to do the shifts/comparisons?
You have to shift and compare elementwise.
for(i = 0; i < len; ++i)
array[i] >>= 3;
for example. If you want to move the bits shifted out of one element to the next, it's more complicated, say you're shifting right, then
unsigned char bits1 = 0, bits2 = 0;
for(i = len-1; i >= 0; --i) {
bits2 = array[i] & 0x07;
array[i] >>= 3;
array[i] |= bits1 << 5;
bits1 = bits2;
}
traversing the array in the other direction because you need the bits from the next higher slot.
You'll have to shift the entries in the array one by one. (And if you want to compare two of these, you'll need to do it element by element.)
If you were hoping that bits shifted off each char would get shifted into the next one, you'll need to take care of that manually too.
If you are wanting that shift-into-the-next-byte behaviour, and don't mind making your code nasty and nonportable and bug-prone, you might be able to take a pointer to the array, cast it to something like unsigned long long *, dereference it and shift the resulting integer, and store it back again.
BUT if that's the behaviour you want then you should be using an integer instead of a char[8] to begin with.
(If you could say more about what you're actually aiming to achieve, then more helpful answers may be possible.)
If you want to perform operations such as shifting / OR / XOR / AND / etc.. on arrays, you should perform it in a loop, you cannot perform it directly on the array.
/** Shift an array right.
* #param ar The array to shift.
* #param size The number of array elements.
* #param shift The number of bits to shift.
*/
void shift_right(unsigned char *ar, int size, int shift)
{
int carry = 0; // Clear the initial carry bit.
while (shift--) { // For each bit to shift ...
for (int i = size - 1; i >= 0; --i) { // For each element of the array from high to low ...
int next = (ar[i] & 1) ? 0x80 : 0; // ... if the low bit is set, set the carry bit.
ar[i] = carry | (ar[i] >> 1); // Shift the element one bit left and addthe old carry.
carry = next; // Remember the old carry for next time.
}
}
}
You can shift only members of that arrays, a char (or an int). You can't shift an entire array. Shifting my_array tries to perform a shift operation on an array type (or a pointer to char) which is impossible. Do this instead:
for (i = 0; i < size; i++) {
my_array[i] >>= 1;
}
Also you must be careful with chars because they are usually signed, and a char containing a negative value will bring '1' from the left instead of zeros. So you better use unsigned chars.
EDIT:
The code above is simplistic. If you intended to shift right the array as a whole, not just each byte on its own, then you need to "manually" copy each LSB to the MSB of the byte to its right. Take a loop at the answer of Richard Pennington.
/**
* shift a number of bits to the right
*
* #param SRC the array to shift
* #param len the length of the array
* #param shift the number of consecutive bits to shift
*
*/
static void shift_bits_right(uint8_t SRC[], uint16_t len, uint32_t shift) {
uint32_t i = 0;
uint8_t start = shift / 8;
uint8_t rest = shift % 8;
uint8_t previous = 0;
for(i = 0; i < len; i++) {
if(start <= i) {
previous = SRC[i - start];
}
uint8_t value = (previous << (8 - rest)) | SRC[i + start] >> rest;
SRC[i + start] = value;
}
}
I know this is old topic but i was not satisfied with the answers available, here is something i wrote recently which allows you to specify the amount of bits you can shift by and also there is simple XOR encryption in it.
//https://github.com/ashvin-bhuttoo/CryptoTest/blob/master/CryptoTest/Crypto.cpp
//CRYPTO CONFIGURATION PARAMETERS
#define BIT_SHIFT 3
#define XOR_KEY 0x3C
#define ENABLE_XOR_VARIANCE true
////////////////////////////////
int get_rs_mask(int shift)
{
switch (shift)
{
case 0:
return 0x00;
case 1:
return 0x01;
case 2:
return 0x03;
case 3:
return 0x07;
case 4:
return 0x0F;
case 5:
return 0x1F;
case 6:
return 0x3F;
case 7:
return 0x7F;
default:
throw "get_rs_mask -> Error, shift argument outside legal range 0-7";
}
}
void shift_right(char* buf, int msg_len, int shift)
{
unsigned char tmp = 0x00, tmp2 = 0x00;
for (int k = 0; k <= msg_len; k++)
{
if (k == 0)
{
tmp = buf[k];
buf[k] >>= shift;
}
else
{
tmp2 = buf[k];
buf[k] >>= shift;
buf[k] |= ((tmp & get_rs_mask(shift)) << (8 - shift));
if (k != msg_len)
tmp = tmp2;
}
}
}
int get_ls_mask(int shift)
{
switch (shift)
{
case 0:
return 0x00;
case 1:
return 0x80;
case 2:
return 0xC0;
case 3:
return 0xE0;
case 4:
return 0xF0;
case 5:
return 0xF8;
case 6:
return 0xFC;
case 7:
return 0xFE;
default:
throw "get_ls_mask -> Error, shift argument outside legal range 0-7";
}
}
void shift_left(char* buf, int msg_len, int shift)
{
char tmp = 0x00, tmp2 = 0x00;
for (int k = msg_len; k >= 0; k--)
{
if (k == msg_len)
{
tmp = buf[k];
buf[k] <<= shift;
}
else
{
tmp2 = buf[k];
buf[k] <<= shift;
buf[k] |= ((tmp & get_ls_mask(shift)) >> (8 - shift));
tmp = tmp2;
}
}
}
void crypt(char* buf, int msg_len, bool decrypt = false)
{
if (!decrypt)
{
shift_right(buf, msg_len, BIT_SHIFT);
for (int k = 0; k < msg_len; k++)
{
buf[k] = buf[k] ^ XOR_KEY ^ k * (ENABLE_XOR_VARIANCE ? 2 : 0);
}
buf[msg_len] = '\0';
}
else
{
for (int k = 0; k < msg_len; k++)
{
buf[k] = buf[k] ^ XOR_KEY ^ k * (ENABLE_XOR_VARIANCE ? 2 : 0);
}
shift_left(buf, (msg_len)-1, BIT_SHIFT);
}
}
/**
* Shift a number of bits to the right
*
* #param array The array to shift
* #param len The length of the array
* #param shift The number of consecutive bits to shift. To the right if shift is positif.
*
*/
static void shift_bits_right(uint8_t *array, int len, int shift) {
uint8_t macro_shift = shift / 8;
shift = shift % 8;
uint8_t array_out[len];
memset(array_out, 0, len);
for(int i = 0; i < len; i++) {
if(i+macro_shift < len)
array_out[i+macro_shift] += array[i]>>shift;
if(i+macro_shift+1 < len)
array_out[i+macro_shift+1] += array[i]<<(8-shift);
}
memcpy(array, array_out, len);
}
For everyone who is looking for a code snippet to (logically) shift right a byte array that actually works:
template<size_t N> void shift_right(array<uint8_t, N>& arr, uint64_t bits)
{
int64_t num_bytes = bits / 8;
int64_t num_bits = bits % 8;
for(int64_t i = N-1; i >= 0; i--)
{
int64_t i_from = i - num_bytes;
int64_t i_from_minus_one = i - num_bytes - 1;
uint8_t v_from = i_from < 0 ? 0 : arr[i_from];
uint8_t v_from_minus_one = i_from_minus_one < 0 ? 0 : arr[i_from_minus_one];
arr[i] = v_from >> num_bits | v_from_minus_one << (8 - num_bits);
}
}