I'm doing a small embedded project where I have 40 bits transferred through a SPI type interface. I pull these bits off of a 32 bit bus and place the upper 32 bits into a uint32_t variable and the lower 8 bits into a uint8_t variable. I'm trying to combine them into a single uint64_t. However when I shift by 8, it drops the top 8 bits. Here is my code.
uint64_t getError()
{
uint32_t * disp_addr = (uint32_t*)(MYDISPLAY);
uint64_t error_upper;
uint8_t error_lower;
uint64_t error= 0;
error_lower = *(disp_addr+1);
error_upper = *(disp_addr+0);
error = ((uint64_t) error_upper) <<8 | error_lower;
return error;
}
This code is working except for the fact that it's dropping my top 8 bits.
Any thoughts or hints would be greatly appreciated. Thanks.
edit
uint64_t getError()
{
uint32_t * disp_addr = (uint32_t*)(MYDISPLAY);
uint64_t error_upper;
uint8_t error_lower;
uint64_t error= 0;
error_lower = 0x34;
error_upper = 0xABCDEF12;
error = ((uint64_t) error_upper) <<8 | error_lower;
printf("%010x", error);
//return error;
}
Results:
00cdef1234
The printf format specifier is incorrect.
#include <stdio.h>
typedef unsigned __int64 uint64_t;
typedef unsigned char uint8_t;
int main(void) {
uint64_t error_upper, error;
uint8_t error_lower;
error_lower = 0x34;
error_upper = 0xABCEDF12;
error = (error_upper << 8) | error_lower;
printf("%x\n", error);
printf("%llx\n", error);
return 0;
}
Program output:
cedf1234
abcedf1234
Why are you saying that the upper byte is cut?
If I use your code and print the result is ok:
uint32_t temp = 0x01020304;
uint32_t *disp_addr = &temp;
uint64_t error_upper;
uint8_t error_lower;
uint64_t error= 0;
error_lower = *(disp_addr+1);
error_upper = *(disp_addr+0);
error = (error_upper<<8) | error_lower;
printf("\n%08X%08X\n", (uint32_t)(error>>32), error);
Output is
0000000102030401
Are you using %d to printout the value?
Look carefully at
uint32_t *disp_addr = &temp;
...
error_lower = *(disp_addr+0);
Because disp_addr is a ptr to uint32_t you are storing a 32bit value in a 8bit variable. Depending on your machine endiannes and how you load disp_addr you may be loading the wrong data.
You probably wanted to do:
uint32_t *disp_addr = &temp;
...
error_lower = *(uint8_t *)(disp_addr+0);
which is not the same.
Related
I've been searching for a while what are for square-brackets in an addressed-of pointer, but I continue without understanding it. Here are the lines of the function, where "id" variable is an uint32_t pointer that has been previously passed as an argument.
#define LIST_STARTED (0x0001) /*defined out of the function*/
#define LIST_FIRST (0x0002) /*defined out of the function*/
uint32_t *pointer = id;
uint16_t *flags = &((uint16_t *)pointer)[0];
uint16_t *index = &((uint16_t *)pointer)[1];
bool started = *flags & LIST_STARTED;
bool first = *flags & LIST_FIRST;
if (!started){
/* does something */
*flags = MSEC_PRM_MGMT_LIST_STARTED;
return true;
}
if (!first){
/* does something */
*flags |= MSEC_PRM_MGMT_LIST_FIRST;
*index = 1;
return true;
}
if (*index == final_index)
/* does something */
return false;
*index += 1;
I understand what the logic of the function is, but I don't understand what do the following lines. I put "all" the code above in case it helps you.
uint16_t *flags = &((uint16_t *)pointer)[0];
uint16_t *index = &((uint16_t *)pointer)[1];
I would appreciate if someone could help me!
Thank you!
I edit to say that this C code works fine in an Embedded System, I'm not modifying it, I was just watching its behaviour.
The following code tries to read a uint32_t object as an array of two uint16_t objects:
uint32_t *id = ...;
uint32_t *pointer = id;
uint16_t *flags = &((uint16_t *)pointer)[0];
uint16_t *index = &((uint16_t *)pointer)[1];
and that is undefined behaviour to read a uint32_t object as 2 uint16_t objects through flags and index pointers because that breaks strict aliasing rules.
The correct way is:
uint16_t flags = *id; // The lowest order bits of id.
uint16_t index = *id >> 16; // The highest order bits of id.
In the above assignments of uint32_t to uint16_t it truncates the highest order bits of id.
uint32_t *pointer = id;
uint16_t *flags = &((uint16_t *)pointer)[0];
it is an equivalent of.
uint32_t *pointer = id;
uint16_t *flags = (uint16_t *)pointer;
The definition:
uint16_t *index = &((uint16_t *)pointer)[1];
Is an equivalent of:
uint16_t *temp = (uint16_t *)pointer;
uint16_t *index = temp + 1;
//or
uint16_t *index = &temp[1];
This is called: pointer punning and it is considered dangerous and not portable.
You can use unions for safe punning (at least when using gcc or its derivatives)
typedef union
{
uint64_t u64;
uint32_t u32[2];
uint16_t u16[4];
uint8_t u8[8];
struct
{
uint8_t n1: 4;
uint8_t n2: 4;
}u4[8];
}union_pune_t;
uint16_t foo16(uint32_t *p32)
{
union_pune_t *d64 = (void *)p32;
return d64 -> u16[1];
}
uint8_t foo8(uint32_t *p32)
{
union_pune_t *d64 = (void *)p32;
return d64 -> u8[5];
}
uint8_t foon4(uint32_t *p32)
{
union_pune_t *d64 = (void *)p32;
return d64 -> u4[9].n2;
}
Why do I get this error on sum = line ?
Not sure what is wrong...
mem_ptr is declared as pointer...
uint32_t MCU_ReadMemTest(uint32_t start_addr, uint32_t end_addr)
{
uint32_t mem_ptr = (uint32_t*)start_addr;
uint32_t stop_ptr = (uint32_t*)end_addr;
while (mem_ptr != stop_ptr)
{
sum = (*mem_ptr);
mem_ptr++;
cnt++;
}
return retval;
}
Thanks,
R.
Thanks for help,
have changed the code and now it is ok...
uint32_t MCU_ReadMemTest(uint32_t start_addr, uint32_t end_addr)
{
uint32_t retval, startt, endt, cnt=0,sum=0;
uint32_t *mem_ptr = start_addr;
uint32_t *stop_ptr = end_addr;
...
Thanks,
R.
I have multiple questions.
Is the size of char always 1 (Byte) on every system ?
Is the size of uint32_t always 4 (Byte) on every system ?
I wrote two little functions to serialize my struct, but I can't tell if this is a good way to do it.
struct udtpackage{
char version;
bool eof;
uint32_t data_size;
uint32_t encrypted_size;
char data[BUFFERSIZE+16];
char hmac[64];
};
void serialize (udtpackage package, unsigned char* buffer){
uint32_t tmp;
memcpy(&buffer[0], &package.version, 1);
(package.eof) ? (buffer[1] = 0xFF) : (buffer[1] = 0x00);
tmp = htonl(package.data_size);
memcpy(&buffer[2], &tmp, 4);
tmp = htonl(package.encrypted_size);
memcpy(&buffer[6], &tmp, 4);
memcpy(&buffer[10], &package.data[0], BUFFERSIZE+16);
memcpy(&buffer[10+BUFFERSIZE+16], &package.hmac[0], 64);
}
void deserialize (udtpackage* package, unsigned char* buffer){
uint32_t tmp;
memcpy(&package->version, &buffer[0], 1);
(buffer[1] & 0xFF) ? (package->eof = true) : (package->eof = false);
memcpy(&tmp, &buffer[2], 4);
package->data_size = ntohl(tmp);
memcpy(&tmp, &buffer[6], 4);
package->encrypted_size = ntohl(tmp);
memcpy(&package->data[0], &buffer[10], BUFFERSIZE+16);
memcpy(&package->hmac[0], &buffer[10+BUFFERSIZE+16], 64);
}
Yes. The size of char is always 1. That doesn't mean that there are 8 bits per byte though.
No. For example, the implementation may define 32 bits per byte,
then the size of uint32_t will be 1. If this is the case, some of the fixed width types will not be defined.
Here is a potential problem:
memcpy(&buffer[2], &tmp, 4);
^
As I mentioned in the second point, the code should be:
memcpy(&buffer[2], &tmp, sizeof(tmp));
^
Going further the buffer offsets should be fixed as well, otherwise you will potentially waste memory:
memcpy(&buffer[6], &tmp, 4);
^
I am trying to convert char variables into Unsigned int. my code is
char spi(char data)
{
//Start transmision
SPDR = data;
//Wait for transmision complete
while(!(SPSR & 0x80));
return SPDR;
}
unsigned int ReadAd(void)
{
unsigned int data;
ChipSelectAd(1);
//Read data
CheckStatus();
spi(0x58);
data = (spi(0xFF)<< 8);
data |= spi(0xFF);
return data;
}
Actually my problem is The spi function return an 8bit char so the above code shifts left 8bits the char variable and then assigns it to a 16bit variable, the result will always be 0.
In order to actually shift the data to the left I need to typecast them first to a 16bit type variable. I have tried like this
char spi(char data)
{
//Start transmision
SPDR = data;
//Wait for transmision complete
while(!(SPSR & 0x80));
return SPDR;
}
unsigned int ReadAd(void)
{
unsigned int data;
ChipSelectAd(1);
//Read data
CheckStatus();
spi(0x58);
data = (unsigned int)((unsigned char)spi(0xFF)<< 8);
data |= (unsigned int)((unsigned char)spi(0xFF));
return data;
}
void CheckStatus(void)
{
//char adcStatus;
adcStatus = 0xFF;
//Read status
while(!(adcStatus & 0x80))
{
spi(0x40);
adcStatus = spi(0xFF);
}
}
void ChipSelectAd(char s)
{
if(s == 1){
PORTB.3 = 0; //Switch on ADC
//while(PINB.3); //Wait for chip select pin
}
else
PORTB.3 = 1; //Switch off ADC
}
its not working. please suggest me which function i have to use.
Thanks in advance.
Your problem is not the casts. It works the same without them, due to the integral promotion rules.
#include <stdio.h>
char spi ( char data )
{
char SPDR = data;
return SPDR;
}
unsigned int ReadAd ( void )
{
unsigned int data;
data = spi ( 0x81 ) << 8;
data |= spi ( 0x42 );
return data;
}
int main ( void )
{
printf ( "Result %x\n", ReadAd() );
return 0;
}
This outputs Result ffff8142 on a system where char is a signed type. To get to the real problem, try assinging the values of spi() calls to variables and then print their values. Please also show us the declaration/definition of SPDR.
1) in embedded systems: get rid of the standard integer types, the default char type in particular. If you have a modern compiler, use uint8_t, uint16_t etc from stdint.h. If you have an ancient compiler, then typedef unsigned char uint8_t and so on. If you aren't using unsigned types of known size in embedded systems, you are asking for bugs bugs bugs.
2) Learn and understand integer promotion rules. It is frightening how many programmers there are that don't know about them or understand them.
Once you have the two above fundamentals sorted out, your code should look something like this code:
(I took the liberty to fix various suspicious, possible bugs and the crappy, inconsistent indention. Plus some style nitpicks.)
#include <stdint.h>
uint8_t spi (uint8_t data)
{
SPDR = data; //Start transmision
while((SPSR & 0x80) > 0) //Wait for transmision complete
;
return SPDR;
}
uint16_t ReadAd(void)
{
uint16_t data;
ChipSelectAd(true);
CheckStatus(); //Read data
(void) spi(0x58);
data = ((uint16_t)spi(0xFF)) << 8;
data |= (uint16_t)spi(0xFF);
return data;
}
void CheckStatus(void)
{
uint8_t adcStatus;
do
{
(void) spi(0x40);
adcStatus = spi(0xFF);
} while((adcStatus & 0x80) > 0);
}
void ChipSelectAd(bool on)
{
if(on)
{
PORTB.3 = 0; //Switch on ADC
}
else
{
PORTB.3 = 1; //Switch off ADC
}
}
How in C can we read and make DWORD variables with a low and high word and low and high byte?
WinAPI provides macros for the manipulations of these types, such as:
HIWORD
LOWORD
MAKELPARAM
In Win32 a DWORD is a 32 bit unsigned integer. In other contexts it could possibly mean something else.
Assuminng the Win32 definition (and other Win32 typedefs):
BYTE lsb = 0x11 :
BYTE next_lsb = 0x22 :
BYTE next_msb = 0x33 :
BYTE msb = 0x44 :
DWORD dword_from_bytes = (msb << 24) | (next_msb << 16) | (next_lsb << 8) | lsb ;
dword_from_bytes will have the value 0x44332211.
Similarly:
WORD lsw = 0x1111 :
WORD msw = 0x2222 :
DWORD dword_from_words = (msw << 16) | lsw ;
dword_from_words will have the value 0x22221111.
To extract say the third byte from dword_from_bytes for example:
next_msb = (dword_from_bytes >> 16) & 0xff ;
although the & 0xff is not strictly necessary in this case given the type of next_msb, but if the type of the receiver were larger than 8 bits, it will mask off the msb bits.
#include <stdint.h>
#include <stdio.h>
typedef union _little_endian{
struct _word{
union _msw{
struct _msw_byte{
uint8_t MSB;
uint8_t LSB;
} __attribute__((__packed__)) MSW_BYTE;
uint16_t WORD;
} MSW;
union _lsw{
struct _lsw_byte{
uint8_t MSB;
uint8_t LSB;
} __attribute__((__packed__)) LSW_BYTE;
uint16_t WORD;
} LSW;
} __attribute__((__packed__)) WORD;
uint32_t DWORD;
} DWORD;
int main(int argc, char *argv[]){
DWORD test1;
test1.WORD.MSW.MSW_BYTE.MSB = 1;
test1.WORD.MSW.MSW_BYTE.LSB = 2;
test1.WORD.LSW.LSW_BYTE.MSB = 3;
test1.WORD.LSW.LSW_BYTE.LSB = 4;
printf("test1: hex=%x uint=%u\n", test1.DWORD, test1.DWORD);
DWORD test2;
test2.DWORD = 0x08080404;
printf("test2: hex=%x uint=%u\n", test2.DWORD, test2.DWORD);
printf("test2.WORD.MSW.MSW_BYTE.MSB: uint=%u\n", test2.WORD.MSW.MSW_BYTE.MSB);
printf("test2.WORD.MSW.MSW_BYTE.LSB: uint=%u\n", test2.WORD.MSW.MSW_BYTE.LSB);
printf("test2.WORD.LSW.LSW_BYTE.MSB: uint=%u\n", test2.WORD.LSW.LSW_BYTE.MSB);
printf("test2.WORD.LSW.LSW_BYTE.LSB: uint=%u\n", test2.WORD.LSW.LSW_BYTE.LSB);
return 0;
}
I prefer to use a combination of structs and unions.
Output:
test1: hex=4030201 uint=67305985
test2: hex=8080404 uint=134743044
test2.WORD.MSW.MSW_BYTE.MSB: uint=4
test2.WORD.MSW.MSW_BYTE.LSB: uint=4
test2.WORD.LSW.LSW_BYTE.MSB: uint=8
test2.WORD.LSW.LSW_BYTE.LSB: uint=8