I am trying to port a generic C driver available for a IMU to my embedded platform based on a nordic module. the most optimal way would be to correctly modify the interface functions to adapt it to my system. So the driver available on github here, has this interface for write/read register:
typedef int32_t (*lsm6dso_write_ptr)(void *, uint8_t, uint8_t*, uint16_t);
typedef int32_t (*lsm6dso_read_ptr) (void *, uint8_t, uint8_t*, uint16_t);
typedef struct {
/** Component mandatory fields **/
lsm6dso_write_ptr write_reg;
lsm6dso_read_ptr read_reg;
/** Customizable optional pointer **/
void *handle;
} lsm6dso_ctx_t;
My read/write register functions are:
void write_i2c_data(nrf_drv_twi_t const *m_twi, uint8_t reg, uint8_t val)
{
uint8_t cmd[2] = {0, 0};
cmd[0] = reg;
cmd[1] = val;
nrf_drv_twi_tx(m_twi, ADDR, cmd, 2, true);
nrf_delay_ms(1);
}
void read_i2c_data(nrf_drv_twi_t const *m_twi, uint8_t reg, uint8_t *val)
{
nrf_drv_twi_tx(m_twi, ADDR, ®, 1, true);
nrf_delay_ms(1);
nrf_drv_twi_rx(m_twi, ADDR, val, 1);
nrf_delay_ms(1);
}
Questions -
1 - I am not sure how to pass along the m_twi driver instance function along to the lsm6dso_ctx_t struct. It says the struct is customizable, but I am not sure how to augment it.
2 - The function pointer kind of got me too - how can I point my function to the lsm6dso_write_ptr pointer. I know will need to modify my function to provide for multiple byte read/write, which I think is doable.
You should implement two functions:
static int32_t your_callback_lsm6dso_read_reg(void *ctx, uint8_t reg, uint8_t* data,
uint16_t len) {
// read from register ret
// len length of data to data pointer
// return 0 on success
// something like: (I have no idea about nrf_* interface)
nrf_drv_twi_t const *m_twi = ctx;
nrf_drv_twi_rx(m_twi, reg, data, len);
return 0;
}
static int32_t your_callback_lsm6dso_write_reg(void *ctx, uint8_t reg, uint8_t* data,
uint16_t len)
// write to register ret len length od data from data pointer
// return 0 on success
// something like: (I have no idea about nrf* interface)
nrf_drv_twi_t const *m_twi = ctx;
nrf_drv_twi_tx(m_twi, reg, data, len);
return 0;
}
Then instantiate the structure:
lsm6dso_ctx_t lsm6dso_ctx = { your_callback_lsm6dso_write_reg, your_callback_lsm6dso_read_reg, m_twi };
and use it like:
lsm6dso_some_function_from_the_library(&lsm6dso_ctx, ...)
The function from the library will call the function pointers from lsm6dso_ctx with the first argument as the void* pointer from the structure. The void* pointer from the structure is used to pass your custom data along. You can then cast the handle from void* pointer into a custom pointer and call the appropriate functions.
how can I point my function to the lsm6dso_write_ptr pointer.
I think your confusion comes from it, that's it's the other way round. The function pointers inside lsm6dso_ctx_t should point to your functions.
Then you have just an instance of lsm6dso_ctx_t structure you use with all functions from the driver. The driver has some logic and it calls your functions as passed with the structure to do input/output operations.
Related
I am working with the Renesas RA2A1 using their Flexible software package, trying to send data over a uart.
I am sending ints and floats over the uart, so I created a union of a float and a 4 byte uint8_t array, same for ints.
I put a few of these in a struct, and then put that in a union with an array that is the size of all the data contained in the struct.
I can't get it to work by passing the array in the struct to the function.. If I create an array of uint8_t, that passes in and works OK... I'm not sure what's wrong with trying to pass the array as I am.
It is failing an assert in R_SCI_UART_WRITE that checks the size, which is failing because it is 0.
typedef union{
float num_float;
uint32_t num_uint32;
int32_t num_int32;
uint8_t num_array[4];
} comms_data_t;
typedef struct{
comms_data_t a;
comms_data_t b;
comms_data_t c;
comms_data_t d;
comms_data_t e;
uint8_t lr[2];
} packet_data_t;
typedef union{
packet_data_t msg_packet_data;
uint8_t packet_array[22];
}msg_data_t;
/* Works */
uint8_t myData[10] = "Hi Dave!\r\n";
uart_print_main_processor_msg(myData);
/* Doesn't work */
msg_data_t msg_data;
/* code removed that puts data into msg_data,ex below */
msg_data.msg_packet_data.a.num_float = 1.2f;
uart_print_main_processor_msg(msg_data.packet_array);
// Functions below
/****************************************************************************************************************/
fsp_err_t uart_print_main_processor_msg(uint8_t *p_msg)
{
fsp_err_t err = FSP_SUCCESS;
uint8_t msg_len = RESET_VALUE;
uint32_t local_timeout = (DATA_LENGTH * UINT16_MAX);
char *p_temp_ptr = (char *)p_msg;
/* Calculate length of message received */
msg_len = ((uint8_t)(strlen(p_temp_ptr)));
/* Reset callback capture variable */
g_uart_event = RESET_VALUE;
/* Writing to terminal */
err = R_SCI_UART_Write (&g_uartMainProcessor_ctrl, p_msg, msg_len);
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT ("\r\n** R_SCI_UART_Write API Failed **\r\n");
return err;
}
/* Check for event transfer complete */
while ((UART_EVENT_TX_COMPLETE != g_uart_event) && (--local_timeout))
{
/* Check if any error event occurred */
if (UART_ERROR_EVENTS == g_uart_event)
{
APP_ERR_PRINT ("\r\n** UART Error Event Received **\r\n");
return FSP_ERR_TRANSFER_ABORTED;
}
}
if(RESET_VALUE == local_timeout)
{
err = FSP_ERR_TIMEOUT;
}
return err;
}
fsp_err_t R_SCI_UART_Write (uart_ctrl_t * const p_api_ctrl, uint8_t const * const p_src, uint32_t const bytes)
{
#if (SCI_UART_CFG_TX_ENABLE)
sci_uart_instance_ctrl_t * p_ctrl = (sci_uart_instance_ctrl_t *) p_api_ctrl;
#if SCI_UART_CFG_PARAM_CHECKING_ENABLE || SCI_UART_CFG_DTC_SUPPORTED
fsp_err_t err = FSP_SUCCESS;
#endif
#if (SCI_UART_CFG_PARAM_CHECKING_ENABLE)
err = r_sci_read_write_param_check(p_ctrl, p_src, bytes);
FSP_ERROR_RETURN(FSP_SUCCESS == err, err);
FSP_ERROR_RETURN(0U == p_ctrl->tx_src_bytes, FSP_ERR_IN_USE);
#endif
/* Transmit interrupts must be disabled to start with. */
p_ctrl->p_reg->SCR &= (uint8_t) ~(SCI_SCR_TIE_MASK | SCI_SCR_TEIE_MASK);
/* If the fifo is not used the first write will be done from this function. Subsequent writes will be done
* from txi_isr. */
#if SCI_UART_CFG_FIFO_SUPPORT
if (p_ctrl->fifo_depth > 0U)
{
p_ctrl->tx_src_bytes = bytes;
p_ctrl->p_tx_src = p_src;
}
else
#endif
{
p_ctrl->tx_src_bytes = bytes - p_ctrl->data_bytes;
p_ctrl->p_tx_src = p_src + p_ctrl->data_bytes;
}
#if SCI_UART_CFG_DTC_SUPPORTED
/* If a transfer instance is used for transmission, reset the transfer instance to transmit the requested
* data. */
if ((NULL != p_ctrl->p_cfg->p_transfer_tx) && p_ctrl->tx_src_bytes)
{
uint32_t data_bytes = p_ctrl->data_bytes;
uint32_t num_transfers = p_ctrl->tx_src_bytes >> (data_bytes - 1);
p_ctrl->tx_src_bytes = 0U;
#if (SCI_UART_CFG_PARAM_CHECKING_ENABLE)
/* Check that the number of transfers is within the 16-bit limit. */
FSP_ASSERT(num_transfers <= SCI_UART_DTC_MAX_TRANSFER);
#endif
err = p_ctrl->p_cfg->p_transfer_tx->p_api->reset(p_ctrl->p_cfg->p_transfer_tx->p_ctrl,
(void const *) p_ctrl->p_tx_src,
NULL,
(uint16_t) num_transfers);
FSP_ERROR_RETURN(FSP_SUCCESS == err, err);
}
#endif
#if SCI_UART_CFG_FLOW_CONTROL_SUPPORT
if ((((sci_uart_extended_cfg_t *) p_ctrl->p_cfg->p_extend)->uart_mode == UART_MODE_RS485_HD) &&
(p_ctrl->flow_pin != SCI_UART_INVALID_16BIT_PARAM))
{
R_BSP_PinAccessEnable();
R_BSP_PinWrite(p_ctrl->flow_pin, BSP_IO_LEVEL_HIGH);
R_BSP_PinAccessDisable();
}
#endif
/* Trigger a TXI interrupt. This triggers the transfer instance or a TXI interrupt if the transfer instance is
* not used. */
p_ctrl->p_reg->SCR |= SCI_SCR_TIE_MASK;
#if SCI_UART_CFG_FIFO_SUPPORT
if (p_ctrl->fifo_depth == 0U)
#endif
{
/* On channels with no FIFO, the first byte is sent from this function to trigger the first TXI event. This
* method is used instead of setting TE and TIE at the same time as recommended in the hardware manual to avoid
* the one frame delay that occurs when the TE bit is set. */
if (2U == p_ctrl->data_bytes)
{
p_ctrl->p_reg->FTDRHL = *((uint16_t *) (p_src)) | (uint16_t) ~(SCI_UART_FIFO_DAT_MASK);
}
else
{
p_ctrl->p_reg->TDR = *(p_src);
}
}
return FSP_SUCCESS;
#else
FSP_PARAMETER_NOT_USED(p_api_ctrl);
FSP_PARAMETER_NOT_USED(p_src);
FSP_PARAMETER_NOT_USED(bytes);
return FSP_ERR_UNSUPPORTED;
#endif
}
There are several issues with this program. A large part of this code relies on undefined behavior. Unions are also UB if used for aliasing, even if pretty much all C compilers tend to allow it, but if you are using a union I would still prefer using a char[] for the array used for aliasing. As mentioned in the comments, "Hi Dave!\r\n"; actually takes up 11 bytes with the null-character. It's safer to use uint8_t myData[] = "Hi Dave!\r\n"; or const * uint8_t = "Hi Dave!\r\n"; and spare yourself the trouble.
Second problem is that strlen cannot work correctly for binary data. strlen works by searching for the first occurrence of the null-character in the string, so it's not applicable for binary data. If you pass a floating point value which has a single zero byte in its IEEE 754 representation, it will mark the end of this "string".
Plain and simple, your function should be declared as fsp_err_t uart_write(const char * msg, size_t msg_len); and be called using uart_write(data_array, sizeof data_array);. If you want to transmit messages of variable size over the UART, you will also have to define a certain communication protocol, i.e. create a message that can be unambiguously parsed. This will likely mean: 1) some cookie at the beginning, 2) length of the transmitted data, 3) actual data, 4) crc -- but this is outside the scope of this question.
So, strlen won't tell you the length of the data, you will pass it to the function yourself, and you don't need unions at all. If you choose not to properly serialize the data (e.g. using protobuf or some other protocol), you can simply pass the pointer to the struct to the function, i.e. call the above mentioned uart_write((char*)&some_struct, sizeof some_struct); and it will work as if you passed an array.
Note that char in this case doesn't mean "ascii character", or "character in a string". The point with using the char* is that it's the only pointer which is legally allowed to alias other pointers. So, you acquire a pointer to your struct (&str), cast it to a char*, and pass it to a function which can then read its representation in memory. I am aware that R_SCI_UART_Write is likely generated by your IDE, and unfortunately these blocks often use uint8_t* instead of char*, so you will probably have to cast to uint8_t* at some point.
I am writing a program to read a bmp header. I've written some code that was working when it was all in main. How to implement this code as a function of its own and then implementing it onto main?
Here is the whole code :
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdin.h>
struct bmp_header {
uint16_t type;
uint32_t size;
uint16_t reserved1;
uint16_t reserved2;
uint32_t offset;
uint32_t dib_size;
uint32_t width;
uint32_t height;
uint16_t planes;
uint16_t bpp;
uint32_t compression;
uint32_t image_size;
uint32_t x_ppm;
uint32_t y_ppm;
uint32_t num_colors;
uint32_t important_colors;
};
void read_bmp(FILE *BMPFile,struct bmp_header* Header) {
fread(&(Header->type), 2, 1, BMPFile);
fread(&(Header->size),4,1,BMPFile);
fread(&(Header->reserved1),2,1,BMPFile);
fread(&(Header->reserverd2),2,1,BMPFile);
fread(&(Header->offset),4,1,BMPFile);
fread(&(Header->dib_size),4,1,BMPFile);
fread(&(Header->width),4,1,BMPFile);
fread(&(Header->height),4,1,BMPFile);
fread(&(Header->planes),2,1,BMPFile);
fread(&(Header->bpp),2,1,BMPFile);
fread(&(Header->compression),4,1,BMPFile);
fread(&(Header->image_size),4,1,BMPFile);
fread(&(Header->x_ppm),4,1,BMPFile);
fread(&(Header->y_pp),4,1,BMPFile);
fread(&(Header->num_colors),4,1,BMPFile);
fread(&(Header->important_colors),4,1,BMPFile);
}
int main() {
FILE *BMPFile = fopen("image.bmp","rb");
if(BMPFile == NULL)
{
return;
}
struct bmp_header* Header;
read_bmp(BMPFile,Header);
fclose(BMPFile);
return 0;
}
The relevant parts of the version of the program with all reading action in main, that worked as expected, is reported below
int main( void )
{
FILE *BMPFile = fopen ("lenna.bmp", "rb");
if (BMPFile == NULL)
{
return 0;
}
struct bmp_header Header;
memset(&Header, 0, sizeof(Header));
fread(&Header.type, 2, 1, BMPFile);
fread(&Header.size),4,1,BMPFile);
fread(&Header.reserved1),2,1,BMPFile);
fread(&Header.reserverd2),2,1,BMPFile);
fread(&Header.offset),4,1,BMPFile);
fread(&Header.dib_size),4,1,BMPFile);
fread(&Header.width),4,1,BMPFile);
fread(&Header.height),4,1,BMPFile);
fread(&Header.planes),2,1,BMPFile);
fread(&Header.bpp),2,1,BMPFile);
fread(&Header.compression),4,1,BMPFile);
fread(&Header.image_size),4,1,BMPFile);
fread(&Header.x_ppm),4,1,BMPFile);
fread(&Header.y_pp),4,1,BMPFile);
fread(&Header.num_colors),4,1,BMPFile);
fread(&Header.important_colors),4,1,BMPFile);
/* Header fields print section */
/* ... */
}
Whenever a working code stops working, it is useful to focus on the changes between the two versions of the code. So, why does your original code work correctly? It looks like this:
int main( void )
{
FILE *BMPFile = fopen ("lenna.bmp", "rb");
if (BMPFile == NULL)
{
return 0;
}
struct bmp_header Header;
memset(&Header, 0, sizeof(Header));
fread(&Header.type, 2, 1, BMPFile);
...
}
You declare Header, of type struct bmp_header, in main's stack (as local variable). In this way the structure will be for sure allocated during all program's lifetime.
You memset it to 0
You pass Header's fields addresses directly to fread
In the new version of the program you have a function defined as
void read_bmp(FILE *BMPFile,struct bmp_header* Header);
so you need a pointer to struct bmp_header to be passed to it. For this reason you declare
struct bmp_header* Header;
and call read_bmp(BMPFile,Header);.
What is the different with the working version? Well, the pointer! Declaring a pointer you say to the compiler that it contains an address, in this case the address of the structure required by read_bmp().
But you never say to the compiler what the address is, so that freads within read_bmp() will write to random location causing a segmentation fault.
What to do
You need to pass to read_bmp() a valid struct bmp_header address, and you have two options.
You can allocate Header in the stack like you did before, and pass its address to read_bmp(), through & operator. It should have been your first attempt, since it was really similar to your working solution.
struct bmp_header Header;
read_bmp(BMPFile, &Header);
You can declare Header as a pointer, but you will need to dynamically allocate its memory through malloc:
struct bmp_header * Header = malloc(sizeof(struct bmp_header));
read_bmp(BMPFile, Header);
You have to create struct bmp_header* type function. Next create struct bmp_header pointer in your function & allocate memory (header size == 54B) for returned reference.
I have been trying to port a driver from 2.6 to 4.X without support from the original board manufacturer (and very limited Linux experience).
The original driver uses init_timer() and passes in a pointer to the timer_list structure. That timer_list structure's data element was set to a pointer to another memory structure and the function element set to the callback. Inside the callback function the data element was used to access other bits of stuff.
The current timer init-method uses:
timer_setup( timer_list *, callback, (unsigned int) flags);
and the timer_list structure was changed to eliminate the data field.
I'm not sure what is the best/proper way to inform the callback function of the equivalent data element. Can anyone provide some guidance?
Here is a snippet of the old driver:
myDevice * dev;
dev->getIntrTimer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
init_timer(dev->getIntrTimer);
dev->getIntrTimer->data = (unsigned long) dev;
dev->getIntrTimer->function = GetIntrTimerCallback;
The callback function starts off like this:
void GetIntrTimerCallback(unsigned long devAddr)
{
myDevice *dev = (myDevice *) devAddr;
dev->blahBlah++; // etc.
So the old code gets passed the pointer to myDevice so inside the callback that structure can be accessed.
But with the new timer method only has available an int that is 4 bytes but a pointer is 8 (or whatever).
What I'd like to do is this:
dev->getIntrTimer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
timer_setup(dev->getIntrTimer, GetIntrTimerCallback, dev);
but of course that generates compile errors because dev is a pointer to type myDevice, which does not fit in an int.
The timer_setup() with three args is present since 4.14 Linux kernel (FYI there was setup_timer() in slightly earlier versions). If you maintain some code which should be relevant up to recent kernels - you have to change it in appropriate way every time the API changes. Now you can access your data through the special function from_timer() based on container_of().
timer_list is normally used not as pointer inside struct, so the example implies normal usage and could be something like:
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
init_timer(&dev->getIntrTimer);
dev->getIntrTimer.data = (unsigned long) dev;
dev->getIntrTimer.function = GetIntrTimerCallback;
/* ... */
add_timer(&dev->getIntrTimer);
#else
timer_setup(&dev->getIntrTimer, GetIntrTimerCallback, 0);
/* the third argument may include TIMER_* flags */
/* ... */
#endif
The callback function:
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
void GetIntrTimerCallback(unsigned long devAddr)
{
myDevice *dev = (myDevice *) devAddr;
#else
void GetIntrTimerCallback(struct timer_list *t)
{
myDevice *dev = from_timer(dev, t, getIntrTimer);
#endif
/* Do something with "dev" */
Read also:
Linux kernel timers new API
Example
Linux kernel versions
I am trying to design a data structure (I have made it much shorter to save space here but I think you get the idea) to be used for byte level communication:
/* PACKET.H */
#define CM_HEADER_SIZE 3
#define CM_DATA_SIZE 16
#define CM_FOOTER_SIZE 3
#define CM_PACKET_SIZE (CM_HEADER_SIZE + CM_DATA_SIZE + CM_FOOTER_SIZE)
// + some other definitions
typedef struct cm_header{
uint8_t PacketStart; //Start Indicator 0x5B [
uint8_t DeviceId; //ID Of the device which is sending
uint8_t PacketType;
} CM_Header;
typedef struct cm_footer {
uint16_t DataCrc; //CRC of the 'Data' part of CM_Packet
uint8_t PacketEnd; //should be 0X5D or ]
} CM_Footer;
//Here I am trying to conver a few u8[4] tp u32 (4*u32 = 16 byte, hence data size)
typedef struct cm_data {
union {
struct{
uint8_t Value_0_0:2;
uint8_t Value_0_1:2;
uint8_t Value_0_2:2;
uint8_t Value_0_3:2;
};
uint32_t Value_0;
};
//same thing for Value_1, 2 and 3
} CM_Data;
typedef struct cm_packet {
CM_Header Header;
CM_Data Data;
CM_Footer Footer;
} CM_Packet;
typedef struct cm_inittypedef{
uint8_t DeviceId;
CM_Packet Packet;
} CM_InitTypeDef;
typedef struct cm_appendresult{
uint8_t Result;
uint8_t Reason;
} CM_AppendResult;
extern CM_InitTypeDef cmHandler;
The goal here is to make reliable structure for transmitting data over USB interface. At the end the CM_Packet should be converted to an uint8_t array and be given to data transmit register of an mcu (stm32).
In the main.c file I try to init the structure as well as some other stuff related to this packet:
/* MAIN.C */
uint8_t packet[CM_PACKET_SIZE];
int main(void) {
//use the extern defined in packet.h to init the struct
cmHandler.DeviceId = 0x01; //assign device id
CM_Init(&cmHandler); //construct the handler
//rest of stuff
while(1) {
CM_GetPacket(&cmHandler, (uint8_t*)packet);
CDC_Transmit_FS(&packet, CM_PACKET_SIZE);
}
}
And here is the implementation of packet.h which screws up everything so bad. I added the packet[CM_PACKET_SIZE] to watch but it is like it is just being generated randomly. Sometimes by pure luck I can see in this array some of the values that I am interested in! but it is like 1% of the time!
/* PACKET.C */
CM_InitTypeDef cmHandler;
void CM_Init(CM_InitTypeDef *cm_initer) {
cmHandler.DeviceId = cm_initer->DeviceId;
static CM_Packet cmPacket;
cmPacket.Header.DeviceId = cm_initer->DeviceId;
cmPacket.Header.PacketStart = CM_START;
cmPacket.Footer.PacketEnd = CM_END;
cm_initer->Packet = cmPacket;
}
CM_AppendResult CM_AppendData(CM_InitTypeDef *handler, uint8_t identifier,
uint8_t *data){
CM_AppendResult result;
switch(identifier){
case CM_VALUE_0:
handler->Packet.Data.Value_0_0 = data[0];
handler->Packet.Data.Value_0_1 = data[1];
handler->Packet.Data.Value_0_2 = data[2];
handler->Packet.Data.Value_0_3 = data[3];
break;
//Also cases for CM_VALUE_0, 1 , 2
//to build up the CM_Data sturct of CM_Packet
default:
result.Result = CM_APPEND_FAILURE;
result.Reason = CM_APPEND_CASE_ERROR;
return result;
break;
}
result.Result = CM_APPEND_SUCCESS;
result.Reason = 0x00;
return result;
}
void CM_GetPacket(CM_InitTypeDef *handler, uint8_t *packet){
//copy the whole struct in the given buffer and later send it to USB host
memcpy(packet, &handler->Packet, sizeof(CM_PACKET_SIZE));
}
So, the problem is this code gives me 99% of the time random stuff. It never has the CM_START which is the start indicator of packet to the value I want to. But most of the time it has the CM_END byte correctly! I got really confused and cant find out the reason. Being working on an embedded platform which is hard to debugg I am kind of lost here...
If you transfer data to another (different) architecture, do not just pass a structure as a blob. That is the way to hell: endianess, alignment, padding bytes, etc. all can (and likely will) cause trouble.
Better serialize the struct in a conforming way, possily using some interpreted control stream so you do not have to write every field out manually. (But still use standard functions to generate that stream).
Some areas of potential or likely trouble:
CM_Footer: The second field might very well start at a 32 or 64 bit boundary, so the preceeding field will be followed by padding. Also, the end of that struct is very likely to be padded by at least 1 bytes on a 32 bit architecture to allow for proper alignment if used in an array (the compiler does not care you if you actually need this). It might even be 8 byte aligned.
CM_Header: Here you likely (not guaranteed) get one uint8_t with 4*2 bits with the ordering not standardized. The field my be followed by 3 unused bytes which are required for the uint32_t interprettion of the union.
How do you guarantee the same endianess (for >uint8_t: high byte first or low byte first?) for host and target?
In general, the structs/unions need not have the same layout for host and target. Even if the same compiler is used, their ABIs may differ, etc. Even if it is the same CPU, there might be other system constraints. Also, for some CPUs, different ABIs (application binary interface) exist.
I am working on an embedded platform which does not have debugging features. So it is hard to say what is the error source.
I have defined in header file:
typedef struct cm_packet {
CM_Header Header; //header of packet 3 bytes
uint8_t *Data; //packet data 64 bytes
CM_Footer Footer; //footer of packet 3 bytes
} CM_Packet;
typedef struct cm_inittypedef{
uint8_t DeviceId;
CM_Packet Packet;
} CM_InitTypeDef;
extern CM_InitTypeDef cmHandler;
void CM_Init(CM_InitTypeDef *handler);
CM_AppendResult CM_AppendData(CM_InitTypeDef *handler, uint8_t identifier
, uint8_t *data, uint8_t length);
And somewhere in implementation I have:
uint8_t bufferIndex = 0;
void CM_Init(CM_InitTypeDef *cm_initer) { //init a handler
cmHandler.DeviceId = cm_initer->DeviceId;
CM_Packet cmPacket;
cmPacket.Header.DeviceId = cm_initer->DeviceId;
cmPacket.Header.PacketStart = CM_START;
cmPacket.Footer.PacketEnd = CM_END;
//initialize data array
uint8_t emptyBuffer[CM_MAX_DATA_SIZE] = {0x00};
cmPacket.Data = emptyBuffer;
cm_initer->Packet = cmPacket;
}
CM_AppendResult CM_AppendData(CM_InitTypeDef *handler, uint8_t identifier
, uint8_t *data, uint8_t length){
//some check to see if new data does not make Data overflow
uint8_t i;
/*** ERROR HAPPENS HERE!!!! ***/
handler->Packet.Data[bufferIndex++] = identifier;
//now add the data itself
for(i = 0; i < length; i++) {
handler->Packet.Data[bufferIndex++] = data[i];
}
//reset indexer
if(bufferIndex > 64) {
PacketReady(); //mark packet as ready
bufferIndex = 0
};
//return result
}
The idea is to update the Packet.Data from some other source codes which have access to the handler. For example some other sources can call that Append function to change Packet.Data. But as you see in the code, I have commented the place which causes the micro-controller to go in hard fault mode. I am not sure what is happening here. All I know is exactly at that line micro goes into hard fault mode and never recovers!
This might be a race condition but before anything else I want to know I have written correct c !!! code then I try to rule out other problems.
In function CM_Init, you are setting cmPacket.Data to point to a local array:
uint8_t emptyBuffer[CM_MAX_DATA_SIZE] = {0x00};
cmPacket.Data = emptyBuffer;
Accessing this memory address outside the scope of the function yields undefined behavior.
As #barak manos mentioned, the buffer supplied to Data is allocated on the stack.
When you get to CM_AppendData, you are writing over memory that is no longer dedicated to the buffer.
You may want to use malloc so that the buffer is allocated on the heap instead of on the stack. Just remember to call free so that you are not leaking memory.
If you can't use dynamic allocation, it's possible to dedicate some scratch memory for all the Data uses. It just needs to be static.
Hope that helps :)