sending data to UART in C - Page 1
Forum section: electronics / general

author: Tony R
author_rank: Regular Contributor
timestamp: November 16, 2011, 04:13:19 pm
content: ok, I never had to do this before and i want to know what the best way to do this is.i have a struct likeCode:struct status{uint16_t heading;uint8_t speed;uint8_t battery;uint8_t event;uint8_t headlights;};what I want to do is use a UART connection to send this data. first two bytes heading and the remaining bytes speed battery event and headlights in that order.The function prototype for the UART is this:Code:void UARTSend( uint32_t portNum, uint8_t *BufferPtr, uint32_t Length );now I would like a simple way of just sending the struct, i know i will have to use pointers but i am not sure how to use them in this case.instead i have done the following.Code:void transmit(struct status stat){uint8_t data;data = stat.heading & 0x00FF;data = (stat.heading & 0xFF00)>>8;data = stat.speed;data = stat.battery;data = stat.event;data = stat.headlights;UARTSend( 0, data, sizeof(data));}Does anyone know if a cleaner way to do this?

author: enz
author_rank: Regular Contributor
timestamp: November 16, 2011, 04:31:53 pm
content: Hello Tony,just skip the skip the unnecessary copy action and add a cast to a pointer to uint8_t for the address of the the struct:void transmit(struct status stat){UARTSend( 0, (uint8_t *) stat, sizeof(struct status));}Regards, Martin

author: Tony R
author_rank: Regular Contributor
timestamp: November 16, 2011, 04:40:11 pm
content: Quote from: enz on November 16, 2011, 04:31:53 pmTried that but it says "invalid type conversion" error number 171I am using the Keil IDE for the ARM M3 LPC1768

author: Tony R
author_rank: Regular Contributor
timestamp: November 16, 2011, 04:42:02 pm
content: Quote from: Tony R on November 16, 2011, 04:40:11 pmbut this seems to work:Code:void transmit(struct status *stat){UARTSend( 0, (uint8_t *) stat, sizeof(struct status));}Thanks for your help!

author: baljemmett
author_rank: Unknown
timestamp: November 16, 2011, 04:44:33 pm
content: Quote from: Tony R on November 16, 2011, 04:40:11 pmYou need to take a pointer to the structure and cast that; you can't cast the struct itself:Code:void transmit(struct status stat){UARTSend( 0, (uint8_t *) &stat, sizeof(stat));}Although do be aware that this doesn't guarantee the data format is the same as the routine you had originally. The two main things to look out are endianness (your two-byte heading field might get transmitted high-byte-first or low-byte-first depending on architecture) and structure padding (junk bytes between structure elements used to keep things in alignment, although I believe in this case you'll be OK since the natural alignment of the elements shouldn't require any padding).In general it could be considered better practice to serialise the data into a known format for transmission (as you are currently doing), so that you don't need to worry about the wire protocol being tied to the struct's exact memory layout. At least, that's the case in the world of full-blown computers and software; in the embedded space it's probably less of a worry, but I'm a bit of a neophyte there so could be wrong.

author: Unknown
author_rank: Unknown
timestamp: November 16, 2011, 04:56:32 pm
content: I believe that C only guarantees the order of the struct fields in memory, i.e. &stat.heading < &stat.speed. Alignment is not guaranteed. Endianness is indeed another issue. If you rely on compiler/platform-specific or even undocumented behavior, be sure to make this very clear in the documentation, so the poor soul who happens to port your code to another compiler/platform will not bang his head against the desk trying to debug this. For example, if you ever change headlights to uint16_t, you may suddenly find a padding byte inserted in front of headlights.Sending structs is only acceptable if both sides are under your control and use the same platform and compiler for both. Even then it's an issue if you ever change one of them, eg. from PIC to PIC32 so you can use a flashy touch screen.

author: IanB
author_rank: Super Contributor
timestamp: November 16, 2011, 06:38:23 pm
content: Quote from: Tony R on November 16, 2011, 04:13:19 pmAs others have said, unpacking the data from the structure like this gives you better guarantees about portability and robustness.However, you can avoid unnecessary copying of data by passing in a pointer to the struct, rather than the whole struct itself:Code:void transmit(struct status *stat){uint8_t data;data = stat->heading & 0x00FF;data = (stat->heading & 0xFF00)>>8;data = stat->speed;data = stat->battery;data = stat->event;data = stat->headlights;UARTSend( 0, data, sizeof(data));}You would not be able to measure the difference in performance on a desktop computer, but on a micro it might make a difference. And coding for best performance is a good habit to get into.(But if you pass a pointer to the struct, you have to make sure the struct can't be modified while you are accessing it. This could be an issue with parallel threads of execution, e.g. interrupt service routines that trigger and update the data while you are trying to send it.)