I need to write data into a structure where the length of the data depends on the command I want to send to a device. For that I have defined the following structure:
typedef struct {
uint8 len; // Command length (cmd ... crc)
uint8 cmd; // Command code
uint8 data_length; // Data length
uint8 data[12]; // Data: max 12 Byte
uint8 crc_h; // CRC value MSB
uint8 crc_l; // CRC value LSB
}CMD_TYPE;
Note: the members cmd, *data_length* and crc that are always present, instead member data can be empty or contains up to 12 Bytes.
I have created a function that returns a initialized command according to the parameters passed to the function:
CMD_TYPE Device::get_cmd(uint8 cmd, uint8 data_len, uint8 *data)
{
CMD_TYPE cmd;
cmd.len = (4 + data_len) * sizeof(uint8);
cmd.cmd = cmd;
cmd.data_length = data_len;
cmd.data = (uint8 *)realloc(cmd.data, data_len*sizeof(uint8));
if(data_len > 0) memcpy(cmd.data, data, data_len);
add_crc16((uint8*)&cmd);
return cmd;
}
The function get_cmd() is used like this:
uint8 cmd_code = 0x01;
uint8 data[2] = {0xAB, 0xCD};
CMD_TYPE cmd = local_device->get_cmd(cmd_code, 2, data);
retVal = local_device->send(cmd);
When I try to compile this code I get an error from the compiler for that line:
cmd.data = (uint8 *)realloc(cmd.data, data_len*sizeof(uint8));
and the compiler error is:
error: lvalue required as left operand of assignment
The aim of using realloc() is to re-size the array data or to remove it at all from my new command structure. What is wrong in my code? Is that the right way to initialize structures with dynamic memory allocation?
What you want is the infamous struct hack:
typedef struct
{
uint8 len; // Command length (cmd ... crc)
uint8 cmd; // Command code
uint8 data_length; // Data length
uint8 crc_h; // CRC value MSB
uint8 crc_l; // CRC value LSB
uint8 data[1]; // Data: max 12 Byte
} CMD_TYPE;
The trick is to allocate enough room for all of the members of the struct up to data[], then add enough bytes for the data[] member:
CMD_TYPE * allocCmd(int dataSize)
{
int len;
CMD_TYPE * p;
len = sizeof(CMD_TYPE) + (dataSize-1)*sizeof(uint8);
p = (CMD_TYPE *) malloc(len);
memset(p, 0, len);
p->data_length = dataSize;
return p;
}
Here, len is calculated to be the size of the struct, minus the size of the empty data member, plus however many elements dataSize specifies for the data array.
The catch is that you have to be careful never to access any elements of p->data[] beyond what is actually allocated in it (inside the struct).
Your CMD_TYPE.data is an array, not a pointer. Since you want it to track dynamically allocated memory, it has to be a pointer:
uint8_t * data;
Just don't forget to initialize it with malloc() (or by setting it to zero before realloc()) and to clean up after yourself
By the way, do not cast the result of malloc() and co.
array defined as a[..] are immutable, you can't assign anything to them. Instead you should use pointers.
Related
I am currently trying to find the simplest way to convert a struct which contains a char * to a char array for transmitting a serial data frame.
The struct stores the frame headers, checksum, msg size etc along with the data to be transmit. eg
struct {
unsigned char header;
unsigned char msgSizeL;
unsigned char msgSizeH;
unsigned char *data;
unsigned char checksum;
....
....
....
....
unsinged char endFrame;
} Frame;
The data is a pointer as the amount of data can change with each frame transmit in the range from 1 to 16 bytes.
I am trying to find the simplest way to convert the struct to an array of unsigned chars without copying out each entry in the struct to the array but don't seem to be able to find a way.
Alternately I don't have to use a struct, as i am generating the frame contents excluding the data contents from scratch, I just want to avoid a case of -
unsigned char frame[1000];
frame[0] = 0x01; // header
frame[1] = msgSizeL; // msg size low byte
frame[2] = msgSizeH; // msg size high byte
frame[3] = data[0]; // data contents 0 Over simplification
frame[4] = data[1]; // data contents 1 for data copying -
frame[5] = data[2]; // data contents 2 data length is variable.
frame[6] = data[3]; // data contents 3
frame[7] = data[4]; // data contents 4
frame[8] = data[5]; // data contents 5
frame[9] = data[6]; // data contents 6
frame[10] = data[7]; // data contents 7
frame[3 + msgSize]; = getChecksum(data);
....
....
....
....
frame [3 + msgSize + 20] = 0xFF; // end frame
where i write data to what reads as a random array index rather than something meaningful like frame.checksum = getChecksum(data);.
Open to suggestions and a little bit of flaming for asking a general question rather than a specific one :-)
I am trying to find the simplest way to convert the struct to an array of unsigned chars without copying out each entry in the struct to the array but don't seem to be able to find a way.
If data would contain up to 16 bytes, I would just declare it as 16-char array, like so:
#define MAX_DATA_LEN 16
struct {
unsigned char header;
unsigned char msgSizeL;
unsigned char msgSizeH;
unsigned char data[MAX_DATA_LEN]; // 16-char array instead of pointer
unsigned char checksum;
....
....
....
....
unsinged char endFrame;
} Frame;
If this is done, then all your data inside this struct has a pre-defined size (i.e. number of unsigned char * sizeof(unsigned char), and so on with other data types...) and you can use simple memcpy to copy the struct to an array:
size_t struct_len = /* computing the length of the data */ 100;
memcpy(frame, struct Frame, struct_len);
You have two options:
1. dynamic frame length
struct {
unsigned char header;
unsigned char msgSizeL; // this will be dynamic from 0x06 (empty) to 0x16 (full)
unsigned char msgSizeH; // this will be always 0x00
unsigned char dataSize; // specify the data size here
unsigned char data[16]; // use a static memory area for the package
unsigned char checksum;
unsigned char endFrame;
} Frame;
then you can do this:
Frame xFrame;
unsigned char ucaData[17] = {0}; // your data
unsigned int uiDataLength = 6; // for example
memset(&xFrame, 0, sizeof(Frame)); // just to be sure
xFrame.header = 0x01;
xFrame.msgSizeL = uiDataLength;
xFrame.msgSizeH = 0;
xFrame.dataSize = uiDataLength;
memcpy(xFrame.data, ucaData, uiDataLength);
xFrame.checksum = CRC8(&xFrame, uiDataLength + 4); // crc for filled area
xFrame.endFrame = 0xFF;
then when building the package:
unsigned char xDataFrame[1000];
memcpy(xDataFrame, xFrame, 4 + uiDataLength);
memcpy(xDataFrame + 4 + uiDataLength, &xFrame + 20, 2);
transmit(xDataFrame, 6 + uiDataLength);
This way, you will discard empty bytes from sending and from calculating checksum for.
2. static frame length
Then you can use a struct like this:
struct {
unsigned char header;
unsigned char msgSizeL; // this will be always 0x16 (22)
unsigned char msgSizeH; // this will be always 0x00
unsigned char dataSize; // specify the data size here
unsigned char data[16]; // use a static memory area for the package
unsigned char checksum;
unsigned char endFrame;
} Frame;
This way you can calculate the checksum for the first 20 bytes of this structure, but be aware of that this structure may contain padding areas which may move your data around the defined and padded memory area. sizeof(Frame) may be calculated by hand as 22 bytes but the compiler may decide that it should use 32 bytes or 24 bytes of memory related with the pack setting. You may use
#pragma pack(1) // disable padding
[structure code]
#pragma pack() // back to the original setting
More info about packing:
#pragma pack effect
How can i extract bytes from offset offset of tvb with length length length? type of tvb is :
uint8_t *tvb;
uint8_t *extractBytes(uint8_t *tvb, guint8 offset, guint8 length)
{
// do ...
// extract bytes and return
}
I don't know how can I do this ?
Thanks in advance.
You will need to allocate memory for the extracted bytes. Then it's a simple matter of copying the correct bytes:
uint8_t *extractBytes(uint8_t *tvb, guint8 offset, guint8 length)
{
uint8_t *new = malloc (length);
if (new) {
mempcy (new, tvb+offset, length);
}
return new; /* Returns NULL on allocation failure */
}
Don't forget to free() the allocated memory once you are done using it.
Note that the malloc() call above allocates length number of bytes. If you want to allocate memory for elements that are larger than uint8_t, you will have to multiply the amount by the size of the element. To be on the safe side, and to guard against future changes, you can allocate the memory as follows:
new = malloc (length * sizeof *new);
Now, sizeof *new will always be the correct element size.
If you don't need to change the extracted bytes, and the original buffer (tvb) doesn't change while you're using the bytes, you can access them directly using something like:
int i;
for (i = 0; i < length; i++) {
do_something (tvb[offset+i]);
}
Objective: Writing to an internal buffer from the values of members of a structure.
I have a structure that contains members of type Uint16 (unsigned int); here is a small portion of it:
typedef unsigned int Uint16;
typedef struct
{
Uint16 ee_Speed_Control_Mode;
Uint16 ee_Motor_Type;
Uint16 ee_Max_Electrical_Speed;
Uint16 ee_Carrier_Frequency;
Uint16 ee_Rated_Motor_Frequency;
Uint16 ee_Rated_Motor_Current;
Uint16 ee_Rs; // extern
Uint16 ee_Rr; // extern
Uint16 ee_L_lkg; // extern
Uint16 ee_Lm; // extern
Uint16 ee_No_Load_Current;
Uint16 ee_Phase_Reversal;
.....
.....
} EEPROM_PARAMETERS;
EEPROM_PARAMETERS eepromParameters;
My attempt:
Here is a function that is intended to write to eeprom: (Most of it is not shown for simplicity; the focus is occurring in the 'for' loop
void eeprom_write(Uint16 address, Uint32 *data, Int16 len)
{
Uint16 i;
// Multiple bytes will be written
// Page Write operation will be used
// Page Write bits to be sent:
// bit 0: Start condition, a high-to-low transition of SDA with SCL high
startCondition();
// bits 1-8: Device address
I2caRegs.I2CDXR = DEVICE_ADDRESS_WRITE;
// bit 9: EEPROM outputs 0 as ACK bit
// Check for ACK bit
while (I2caRegs.I2CDRR != 0)
{
wait();
}
// bits 10-17, bit 18 (ACK) and bits 19-26: two 8-bit word addresses
I2caRegs.I2CDXR = address;
// After setting the address, page write is capable of writing 64 bytes without stopping
// The EEPROM will respond with a zero after each data word has been received
// The data word address lower 6 bits are internally incremented following the receipt of each data word
// If more than 64 data words are written, data word addresses will "roll over" and previous data will be overwritten
for (i = 0; i < len; i++)
{
// How to increment address in data structure?
I2caRegs.I2CDXR = *data++;
}
// After page write operation is complete, execute stop condition
stopCondition();
}
When I try to call this function with my parameters..
eeprom_write(0, &eepromParameters, sizeof(eepromParameters) );
I get a incompatible type error:
error #169: argument of type "EEPROM_PARAMETERS *" is incompatible with parameter of type "Uint16 *"
My next thought would be that I need a middle man to bring them together and make it a compatible match. Any tips please on what I can try? Thanks
The problem is the declaration and usage of data. If you declare it as
void eeprom_write(Uint16 address, EEPROM_PARAMETERS* data, Int16 len);
and call it as
eeprom_write(0, &eepromParameters, sizeof(eepromParameters));
It will fall over in
*data++
since it will increment by the size of EEPROM_PARAMTERS. If the prototype is declared as
void eeprom_write(Uint16 address, UInt16* data, Int16 len);
It needs to be called as
eeprom_write(0, &eepromParameters.ee_Speed_Control_Mode, sizeof(eepromParameters) / sizeof(Uint16));
This assumes that everything in EEPROM_PARAMETERS is Uint16. Another way of doing this is to use enums.
enum EEOffsets
{
ee_Speed_Control_Mode,
ee_Motor_Type,
ee_Max_Electrical_Speed,
ee_Carrier_Frequency,
...
ee_Max
};
// Initialize the parameters
Uint16 eepromParameters[ee_Max] = { ... };
// If you need to assign
eepromParameters[ee_Carrier_Frequency] = 85;
...
eeprom_write(0, eepromParameters, ee_Max);
I am trying to copy a byte array to my struct, then serialize my struct to a byte array again.
But, after I serialize my struct array, I cant get my data value (0x12, 0x34, 0x56) again, instead i get some rubbish data.
What is wrong here?
#pragma pack(push, 1)
typedef struct {
uint8_t length;
uint8_t *data;
} Tx_Packet;
#pragma pack(pop)
static void create_tx_packet(uint8_t *packet, uint8_t *src, int length);
int main(void)
{
uint8_t packet[32];
uint8_t data[] = { 0x12, 0x34, 0x56 };
create_tx_packet(packet, data, 3);
//i check using debugger, i cant get the data value correctly
//but i could get length value correctly
return 0;
}
static void create_tx_packet(uint8_t *packet, uint8_t *src, int length)
{
Tx_Packet *tx_packet = malloc(sizeof(*tx_packet ));
tx_packet->length = length;
tx_packet->data = (uint8_t *)malloc(length);
memcpy(tx_packet->data, src, length);
memcpy(packet, tx_packet, sizeof(*tx_packet));
}
Right now, your create_tx_packet() function copies a Tx_Packet struct created in the function to a uint8_t array. That struct contains the length and a pointer to the data, but not the data itself. It's actually not necessary to use the struct as an intermediate step at all, particularly for such a simple packet, so you could instead do:
static void create_tx_packet(uint8_t *packet, uint8_t *src, int length)
{
*packet = length; /* set (first) uint8_t pointed to by packet to the
length */
memcpy(packet + 1, src, length); /* copy length bytes from src to
the 2nd and subsequent bytes of
packet */
}
You still need to make sure packet points to enough space (at least length + 1 bytes) for everything (which it does). Since the version above doesn't dynamically allocate anything, it also fixes the memory leaks in your original (which should have freed tx_packet->data and tx_packet before exiting).
--
If you do want to use a struct, you can (since the data is at the end) change your struct to use an array instead of a pointer for data -- then extra space past the size of the struct can be used for the data, and accessed through the data array in the struct. The struct might be:
typedef struct {
uint8_t length;
uint8_t data[];
} Tx_Packet;
and the function becomes (if a temporary struct is used):
static void create_tx_packet(uint8_t *packet, uint8_t *src, int length)
{
/* allocate the temporary struct, with extra space at the end for the
data */
Tx_Packet *tx_packet = malloc(sizeof(Tx_Packet)+length);
/* fill the struct (set length, copy data from src) */
tx_packet->length = length;
memcpy(tx_packet->data, src, length);
/* copy the struct and following data to the output array */
memcpy(packet, tx_packet, sizeof(Tx_Packet) + length);
/* and remember to free our temporary struct/data */
free(tx_packet);
}
Rather than allocate a temporary struct, though, you could also use struct pointer to access the byte array in packet directly and avoid the extra memory allocation:
static void create_tx_packet(uint8_t *packet, uint8_t *src, int length)
{
/* Set a Tx_Packet pointer to point at the output array */
Tx_Packet *tx_packet = (Tx_Packet *)packet;
/* Fill out the struct as before, but this time directly into the
output array so we don't need to allocate and copy so much */
tx_packet->length = length;
memcpy(tx_packet->data, src, length);
}
If you use memcpy(packet, tx_packet, sizeof(*tx_packet)); you are copying the memory representation of tx_Packet into packet, starting with tx_packet->length.
Additionally when mallocating tx_packet that size should be sizeof(*packet)+sizeof(uint8_t) (length of packet plus length field)
And again when copying the tx_packet back to packet you are writing out of the boundaries of packet.
EDIT:
I forgot to mention that depending on your compiler memory alignment parameter you could get any length for the fields (including tx_packet->length) to accelerate memory operation. On 32bits machine it could be 4 and padded with rubbish.
When you serialize your struct with
memcpy(packet, tx_packet, sizeof(*tx_packet));
you're copying the length and the pointer to the data, but not the data itself. You'll probably need two memcpy calls: one of sizeof(uint8_t) to copy the length field, and one of length to copy the data.
This line:
Tx_Packet *tx_packet = malloc(sizeof(*packet));
only allocates one byte for the packet header, which you then immediately write off the end of, causing undefined behavior. You probably meant
Tx_Packet *tx_packet = malloc(sizeof(*tx_packet));
Compiler reports error:
"argument of type "uint8_t" is incompatible with parameter of type "void *" (see code below)
Declarations of the two functions are:
// param[in] addr - First Radio register address
// param[out] buffer - Buffer where to copy the registers data
// param[in] size - Number of registers to be read
void SX1276ReadBuffer( uint8_t addr, uint8_t *buffer, uint8_t size );
and
// param[in] handle - Pointer to a SPI driver handle
// param[in] txBuffer - Transmit data buffer
// param[out] rxBuffer - Receive data buffer
// param[in] count - Number of bytes in transfer
Ecode_t SPIDRV_MTransferB( SPIDRV_Handle_t handle, const void *txBuffer, void *rxBuffer, int count )
The first function is platform independent, while the second one is not. The definition of the first function (which is called by the higher layers of the chip driver, written by manufacturer) is:
void SX1276ReadBuffer( uint8_t addr, uint8_t *buffer, uint8_t size )
{
uint8_t i;
uint8_t *rxBuffer;
uint8_t startAddr = addr & 0x7F;
SPIDRV_MTransferB(handle, &startAddr, rxBuffer, 1);
for( i = 0; i < size; i++ )
{
SPIDRV_MTransferB(handle, 0, buffer[i], 1); ////// THIS LINE REPORTS ERROR
}
}
Any ideas?
txBuffer and rxBuffer must be valid addresses of memory chunks. So your call is probably false for two reasons :
It is highly not probable that the address of a uint8_t could be
used as a good transmit buffer.
For rxBuffer the problem is that it does not point to any memory
(the pointer is not initialized).
So you must define where the data will be read/write from/to. I did not know how the function SPIDRV_MTransferB really work. count is not clearly specified, which length it refers to ?
Your call should probably look like :
txBuffer = malloc(some_size); // or derive an address from addr param ?
rxBuffer = malloc(some_size); // or ?
SPIDRV_MTransferB(handle, &startAddr, rxBuffer, some_size);
here is the function prototype:
Ecode_t SPIDRV_MTransferB(
SPIDRV_Handle_t handle,
const void *txBuffer,
void *rxBuffer,
int count )
here is the call to that function:
SPIDRV_MTransferB(
handle, // code not posted to verify this parameter
0, // this, at best, defines a pointer to addr 0
buffer[i], // this is the contents of buffer[i], not a pointer
1); // this parameter is ok.
suggest:
SPIDRV_MTransferB(handle, NULL, &(buffer[i]), 1);
to be of any further assistance, we need a description
of exactly what the SPIDRV_MTransferB()
is expected to perform when any pointer passed to it is NULL.