views:

3168

answers:

7

Hi,

I am attempting to copy the members of a struct containing a mixture of ints, char's and arrays of chars into a byte array to send to a serial line. So far I have

//  struct msg_on_send
//  {
//      char descriptor_msg[5];
//      int  address;
//      char space;
//      char cmdmsg[5];
//      char CR;
//      char LF;
//  };
//  
//  void switch_output_on()
//  {
//      int member;
//      struct msg_on_send SendMsg_on[sizeof member] =
//      {
//            
//      };
//      unsigned char buffer [ sizeof SendMsg_on[0] ];
//      showbytes(buffer, serialize(buffer, SendMsg_on));
//  }

/*************************************************************************** 
*   Function: ArrayBuild               *
*   Purpose: Uses memcopy to transfer the struct members sequentially   *
*      into an array of char            *
*   Arguments:                  *
*   Returns: size_t i = a count of the number of bytes in the array    *
***************************************************************************/    

//  size_t ArrayBuild(unsigned char *dst, const struct msg_on_send *object)
//  {
//      size_t i = 0;
//
//      memcpy(&dst[i], &object->descriptor_msg, sizeof object->descriptor_msg);
//      i += sizeof object->descriptor_msg;
//
//      memcpy(&dst[i], &object->address, sizeof object->address);
//      i += sizeof object->address;
//
//      memcpy(&dst[i], &object->space, sizeof object->space);
//      i += sizeof object->space;
//
//      memcpy(&dst[i], &object->cmdmsg, sizeof object->cmdmsg);
//      i += sizeof object->cmdmsg;
//   
//      memcpy(&dst[i], &object->CR, sizeof object->CR);
//      i += sizeof object->CR;
//   
//      memcpy(&dst[i], &object->LF, sizeof object->LF);
//      i += sizeof object->LF;
//
//      return i;
//  }

/*********************************************************************** 
*   Function: USARTWrite              *
*   Purpose: Writes the array data to the USART data register    *
*   Arguments: void *object = struct member         *
*      size_t size =  size of array remaining       *
*   Returns: None               *
***********************************************************************/

//  void USARTWrite(const void *object, size_t size)  
//  {
//      const unsigned char *byte;
//      for ( byte = object; size--; ++byte )
//      {
//          printf("%02X", *byte);
//      }
//      putchar('\n');
//  }

}

As I obtained this code, I don't fully understand how it works. I can see that the memcpy takes each element of the struct and makes it into a serial stream indexed by the 'i' variable, but I don't know how the USARTWrite function packetises this into a string, or how to load the array with my struct initialisation.

Sorry this post is a bit long, but I'm just starting this programming lark, and trying to get my head around this concept.

Thanks Dave

A: 

Your struct here is just array of bytes, it contains no pointers that point out of it.

Member-to-member copy is most likely performed to cope with alignment, as (char*) &address will likely be greater than ((char*) &descriptor_msg) + 5.

USARTWrite sends HEX codes of the bytes of your struct to stdout, but discards alignment. Compiling this code with different alignment strategies will lead to different outputs.

Enclose your structure declaration into #pragma pack(push, n) and #pragma pack(pop) to force alignment, and just copy your structure byte-to-byte.

Quassnoi
The question does not specify platform or compiler, and neither do the tags. Pragmas are not standard, by definition, and vary between compilers. When suggesting pragmas, please at least qualify by compiler.
David Thornley
A: 

It's fairly straightforward: 1. ArrayBuild takes a pointer to a msg_on_send structure, and for each member in there, uses memcpy to copy the bytes into a char array that was passed in like so -

char byteArray[17]; // This assumes 4-byte ints
                    // be careful though, the length *must* be long enough, or 
                    // Bad Things will happen
size_t msgSize; // Holds the size of the message built by ArrayBuild,
                // passed to USARTWrite
struct msg_on_send myMessage;
// Code to fill up myMessage appropriately

msgSize = ArrayBuild(byteArray, &myMessage); // need the & to pass a pointer as required

USARTWrite(myMessage, msgSize);

USARTWrite is just given a char array and a size - it grabs each char in turn and prints it to the screen as a hex value with printf().

The 'magic' is in the ArrayBuild - memcpy does a literal copy of bytes from source to destination, with no translation. Assuming 4-byte ints, the array built by the function will look like so:

                     1 1 1 1 1 1 1 
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
|   A     |   B   |C|    D    |E|F|

A = descriptor_msg (char[5])
B = address (int)
C = space (char)
D = cmdmsg (char[5])
E = CR (char)
F = LF (char)

I'd assume that in the 'real' application, the printf() call would be replaced by a call to a serial port write.

Harper Shelby
+4  A: 

You don't have to actually copy the struct into an array of bytes. You could optionally do this:

struct msg_on_send myMessage;

// code to set myMessage to whatever values...

// get a byte pointer that points to the beginning of the struct    
uint8_t *bytePtr = (uint8_t*)&myMessage;

// pass that into the write function, and it will write the amount of bytes passed in
USARTWrite(bytePtr, sizeof(myMessage));

The power of pointers! :)

Steve Lazaridis
This is quite common and I see it used all the time to zero out large window api structs. I wouldn't make the temp variable bytePtr though.
gradbot
why not make this temp variable?
droseman
You **must** have __attribute__ ((packed)) for the struct in order to guarantee that it will always work as expected!
David Holm
I'm sorry, but I don't really understand what you mean, would you be able to explain in a simpler way - I am just starting in programming.
droseman
to slaz: would it be too much to ask for a more complete worked example, as I have just spend 6 hours trying to figure out how to implement this. Several of these concepts are new to me.If thats too cheeky, then sorry!Dave
droseman
A: 

wow, many good answers quickly - thanks guys.

slaz: That seems logical to me, I hadn't really thought about that approach as I haven't really got my head around pointers yet, but I am beginning to see that they are an essential part of C, so I duly will have a look.

  • This line of code sends the data to my UART, what would I replace the array containing the message contents with? It seems like I am missing a logical step here where I have a variable telling me where my structure starts and how big it is, but no array to send

    USART_SendData(USART1, message_on_contents[array_count]);
    

Harper Shelby: Thank you for that description, it makes it much clearer in my mind.

rgds

Dave

droseman
+1  A: 

Hey droseman:
Sorry, I didn't see your comment until just now. The code below compiles on Linux just fine, so I hope it works for you.
printf() is printing in hex, you will get 2 characters for each byte.

#include <stdio.h>

struct msg_on_send
{
char descriptor_msg[5];
int  address;
char space;
char cmdmsg[5];
char CR; 
char LF; 
};

void USARTWrite(const void *object, size_t size)    
{
    const unsigned char *byte;
      for ( byte = object; size--; ++byte )                                     
      {   
          printf("%02X", *byte);
      }   
      putchar('\n');
}

int main (int argc, char**argv)
{
    struct msg_on_send myMsg;
    unsigned char* ptr= (unsigned char*)&myMsg;

    USARTWrite(ptr, sizeof(myMsg));

    return 0;
}

I hope this helps.

~
~

Steve Lazaridis
since USARTWrite takes a const void *, there should be a need for the ptr variable in main. You could just do: USARTWrite(
Evan Teran
A: 

Thank you very much for your help, that was exactly the solution I was looking for. I was surprised how simple it was when I read through the whole code listing.

One other question - does the send function send 1 byte at a time, or would it send an int as all 4 bytes at once? Ultimately, this is not going to printf, but as a serial data stream 1 byte at a time.

--dave

droseman
A: 

If I want to treat a structure as an array of bytes I typically use a union to combined the structure with a byte array. For example:

typedef union
{
    struct
    { 
        char descriptor_msg[5]; 
        int  address; 
        char space; 
        char cmdmsg[5]; 
        char CR; 
        char LF; 
    };
    BYTE bytes[];
} msg_on_send;
mjh2007