Home > Software design >  Circular Buffers embedded C but with Dynamic Struct
Circular Buffers embedded C but with Dynamic Struct

Time:08-10

I don't really understand pointers, find them so difficult to understand its unreal. I don't think I will ever ever grasp POINTERs when sending to functions * & omg ...

I am using arm nucleo board to transfer CAN data

I have created a Struct for CAN Bus 1 & Can Bus 2 data which takes care of the CAN incoming messages

  1. CAN type of packet
  2. CAN HEADER ID
  3. CAN DATA
typedef struct can_packet_s
    {
        uint8_t             RX_CAN_Message_Type;                            
        uint32_t            RX_CAN_ID;                                      
        uint8_t             RX_CAN_DATA[8];                                         
    }can_packet_t;

once the interrupt executes for incoming CAN data I store it in the above struct variables

but I want to go one stage further I want to add this data to a circular buffer

I have a circular buffer buffer working, I don't really understand how it all works with all the * / pointers calling various functions, its a total maze of trying to under stand?

but this circular buffer only works with a variable when initialized?

How can I get rid of the variable and transpose it to the struct? I basically want to have a dynamic struct of can data not a single variable.

here is the circular buffer (though if some one can explain)

The *data I want to get rid of and replace with my dynamic can struct of how ever deep I want to make the fifo, how can this be done. I have spent days trying?

fifo.h

#pragma once
 
// types
typedef struct fifo_buffer_s
{
    uint16_t *data;                 // pointer to external buffer
    uint16_t  size;                 // size of external buffer
    uint16_t  head;                 // index of free space in external buffer
    uint16_t  tail;                 // index of last item to be read
    uint16_t  count;                // hold number of items stored but not read
} fifo_buffer_t;
 
// interface
void      FIFO_Init   (fifo_buffer_t * const buff_state, uint16_t* in_buffer, uint16_t buffer_size_in_bytes);
void      FIFO_Reset  (fifo_buffer_t * const buff_state);                                               // note: does not clear external buffer
uint8_t   FIFO_IsEmpty(fifo_buffer_t * const buff_state);                                               // return non-zero if buffer is empty
uint8_t   FIFO_IsFull (fifo_buffer_t * const buff_state);                                               // return non-zero if buffer is full
uint8_t   FIFO_AddItem(fifo_buffer_t * const buff_state, uint16_t  new_item);                           // return non-zero if buffer is full
uint8_t   FIFO_GetItem(fifo_buffer_t * const buff_state, uint16_t* const out_item);                     // return non-zero if buffer is empty

fifo.c

#include "main.h"


// Simple prove-of-concept fifo buffer
// Author: Adam Orcholski, www.tath.eu
 
// #include <fifobuffer.h>
#include <assert.h>
 
// ************************************************************************
// ********** FIFO INIT ROUTINE ASSIGNS NEW DATA TO STRUCTURE   ***********
// ************************************************************************     

void FIFO_Init(fifo_buffer_t * const buff_state, uint16_t* in_buffer, uint16_t buffer_size_in_bytes)
{
    assert(0 != buff_state);
    assert(0 != in_buffer);
    assert(buffer_size_in_bytes > 0);
 
    buff_state->data = in_buffer;
    buff_state->size = buffer_size_in_bytes;
    buff_state->head =  0;
    buff_state->tail =  0;
    buff_state->count = 0;
}
 
// ************************************************************************
// ********** FIFO RESET ROUTINE RESETS DATA IN STRUCTURE to 0   **********
// ************************************************************************ 

void FIFO_Reset(fifo_buffer_t * const buff_state)
{
    assert(0 != buff_state);
 
    buff_state->head = 0;
    buff_state->tail = 0;
    buff_state->count = 0;
}

// ************************************************************************
// ********** FIFO RESET ROUTINE RESETS DATA IN STRUCTURE to 0   **********
// ************************************************************************ 
 
// return non-zero value if buffer is empty
uint8_t FIFO_IsEmpty(fifo_buffer_t * const buff_state)
{
    uint8_t fBufferIsEmpty = 0;
 
    assert(0 != buff_state);
 
    if (0 == buff_state->count)
    {
        fBufferIsEmpty = 1;
    }
 
    return fBufferIsEmpty;
}

// ************************************************************************
// ********** FIFO RESET ROUTINE RESETS DATA IN STRUCTURE to 0   **********
// ************************************************************************ 
 
// return non-zero value if buffer is full
uint8_t FIFO_IsFull(fifo_buffer_t * const buff_state)
{
    uint8_t fBufferIsFull = 0;
 
    assert(0 != buff_state);
 
    if (buff_state->count >= (buff_state->size / sizeof(buff_state->size)))
    {
        fBufferIsFull = 1;
    }
 
    return fBufferIsFull;
}

// ************************************************************************
// ********** FIFO ADD ITEM ROUTINE Returns Non 0 If FIFO Full  ***********
// ************************************************************************ 

// return non-zero value if buffer is full
uint8_t  FIFO_AddItem(fifo_buffer_t * const buff_state, uint16_t new_item)
{
    uint8_t fBufferIsFull = 0;
 
    assert(0 != buff_state);
 
    fBufferIsFull = FIFO_IsFull(buff_state);
 
    if (!fBufferIsFull)
    {
        buff_state->data[buff_state->head] = new_item;
        buff_state->head  ;
        buff_state->count  ;
 
        if (buff_state->head >= (buff_state->size / sizeof(buff_state->size)))
        {
            buff_state->head = 0;
        }
    }
 
 
    return fBufferIsFull;
}
 
// ************************************************************************
// ********** FIFO GET ITEM ROUTINE Returns Non 0 If FIFO Empty  **********
// ************************************************************************ 

// return non-zero value if buffer is empty
uint8_t  FIFO_GetItem(fifo_buffer_t * const buff_state, uint16_t* const out_item)
{
    uint8_t fBufferIsEmpty = 0;
 
    assert(0 != buff_state);
    assert(0 != out_item);
 
    fBufferIsEmpty = FIFO_IsEmpty(buff_state);
 
    if (!fBufferIsEmpty)
    {
        *out_item = buff_state->data[buff_state->tail];
        buff_state->tail  ;
        buff_state->count--;
 
        if (buff_state->tail >= (buff_state->size / sizeof(buff_state->size)))
        {
            buff_state->tail = 0;
        }
    }
 
    return fBufferIsEmpty;
}

To set it up in main.c

// - CAN RX FIFO Data Handle Structure FiFo Variables -

  #define         CAN_RX_BUFFER_SIZE 32                                                                // define can rx message fifo buffer to 32 byte fifo
  uint16_t        can_rx_fifo_buffer[CAN_RX_BUFFER_SIZE];                                              // create BUFFER ARRAY for can_rx_fifo with above size
 
  fifo_buffer_t   can_rx_fifo;   


 FIFO_Init( &can_rx_fifo   , can_rx_fifo_buffer    , sizeof(can_rx_fifo_buffer  ));                   // INIT FIFO FOR CAN INCOMING MESSAGES



then to add data

  ret = FIFO_AddItem( &can_rx_fifo, RxHeader.StdId    );  

but this is no good for what I want, this fifo simply uses one 16bit variable with a depth of sizeof(can_rx_fifo_buffer )

how can I replace the *data with my CAN struct

{
        uint8_t             RX_CAN_Message_Type;                            
        uint32_t            RX_CAN_ID;                                      
        uint8_t             RX_CAN_DATA[8];                                         
    }can_packet_t;

so the depth of this fifo is multiple structs in memory

this code below inits the fifo and I think makes the fifo struct element *data which is 16bits wide a type of array? pointer array? by can_rx_fifo_buffer[CAN_RX_BUFFER_SIZE]

FIFO_Init( &can_rx_fifo , can_rx_fifo_buffer , sizeof(can_rx_fifo_buffer ));

I know what I want to do, how ones does it even if it can be done I have not a clue?

simply want to modify this working circular fifo code to create multiple CAN structs in memory and I set how deep the fifo struct are

  1. so we must first start by creating the struct which is already done...
typedef struct can_packet_s
    {
        uint8_t             RX_CAN_Message_Type;                            
        uint32_t            RX_CAN_ID;                                      
        uint8_t             RX_CAN_DATA[8];                                         
                            // could always add more variables here ?
   }can_packet_t;
  1. we can now workout the size of the struct with all its elements
int size=0;
size = sizeof(can_packet_t);

ok so we now know the size of memory to store one complete CAN struct in memory

  1. I now need to allocate a section of memory to store the desired number of strucs in my fifo

how do I do this?

is something like this

fifo.h

// types
typedef struct fifo_buffer_s
{
    // uint16_t *data;                 // get rid of INT 16 data pointer to external buffer
  
// now add the struct to replace variable data ?
// do i make this a pointer struct ??? don't understand pointer to struct or varaibles ????


 struct can_pack_s
    {
        uint8_t             *RX_CAN_Message_Type;                           
        uint32_t            *RX_CAN_ID;                                     
        uint8_t             *RX_CAN_DATA[8];                                            
                            // could always add more variables here?
   }*can_pack_t;
                           // possibly right? haven't a clue?


    uint16_t  size;                 // size of external buffer
    uint16_t  head;                 // index of free space in external buffer
    uint16_t  tail;                 // index of last item to be read
    uint16_t  count;                // hold number of items stored but not read
} fifo_buffer_t;

Then I can send use

uint8_t FIFO_AddItem(fifo_buffer_t * const buff_state, uint16_t new_item);

but not send a 16bit variable new_item I want to send my CAN struct and write all elements to my fifo struct?

please reply and hemp in very basic terms, I want to understand how to do this, I think I'm on the right course, but need very basic stage by stage answers

thank you in advance

CodePudding user response:

Two salient points in your question.

"Want to store 'n' buffers." You've made a valiant effort with a "circular buffer" using an array. Sadly, without far more complex code, you may encounter "buffer full" conditions and be unable to store important data. This may be unavoidable in an embedded system. Dynamic memory allocation is usually forbidden in that space.

"Step by step". There is lots of information available. Find a source that makes sense to you.

I've taken snippets of your code and stripped them down to a reasonable minimum in the hope that you can follow what is happening. It's best to NOT try to write code without understanding what it is supposed to do. You wind up with a 'blob' and can't begin to diagnose misbehaviours. Work incrementally and make 'checkpoint' copies of files that have be tested and shown to work.

Good luck!

// Your basic "message" block
typedef struct {
    uint8_t     RX_CAN_Message_Type;
    uint32_t    RX_CAN_ID;
    uint8_t     RX_CAN_DATA[8];
                // Yes! can add more variables here!
} can_packet_t;

// Temporarily reduce the size for this example
//#define   CAN_RX_BUFFER_SIZE 32
#define CAN_RX_BUFFER_SIZE 3

// An array to hold upto 'n' "messages" compiled into the wrapping structure
typedef struct {
    can_packet_t data[ CAN_RX_BUFFER_SIZE ];
    uint16_t head;      // index of next spot to write
    uint16_t tail;      // index of next spot to read
    uint16_t count;     // number of items stored
} fifo_buffer_t;


// unnecessary duplication of "reset()"
// void      FIFO_Init(); 


// Simply zero-out the FIFO buffer
void FIFO_Reset( fifo_buffer_t *p ) {

    // Check, as far as can be done, address is not bad
    // Omitted check in other example functions for brevity
    if( p == NULL )
        exit( 1 );

    memset( p, 0, sizeof *p );
}


// fairly obvious
bool FIFO_IsEmpty( fifo_buffer_t *p ) {
    return p->count == 0;
}

// and obvious here, too
bool FIFO_IsFull( fifo_buffer_t *p ) {
    return p->count == CAN_RX_BUFFER_SIZE;
}


// adapted your code to serve 'struct' instead of 'int'
bool FIFO_AddItem( fifo_buffer_t *p, can_packet_t *item ) {
    bool isFull = FIFO_IsFull( p );

    if( !isFull ) {
        memcpy( &p->data[ p->head ], item, sizeof p->data[0] );
        p->count  ;
        p->head  ;
        if( p->head >= CAN_RX_BUFFER_SIZE ) // wrap around
            p->head = 0;
    }

    return isFull;
}

// Same, but now "read" instead of "write" the item
bool FIFO_GetItem( fifo_buffer_t *p, can_packet_t *item ) {
    // functions receive address(es) and use those as pointers
    bool isEmpty = FIFO_IsEmpty( p);

    if( !isEmpty ) {
        memcpy( item, &p->data[ p->tail ], sizeof *item );
        p->count--;
        p->tail  ;
        if( p->tail >= CAN_RX_BUFFER_SIZE )
            p->tail = 0;
    }
 
    return isEmpty;
}


int main() {
    fifo_buffer_t can_rx_fifo; // One instance, local to main()

    FIFO_Reset( &can_rx_fifo );

    // simulating 4 distinct packets to be saved in FIFO order
    can_packet_t  packs[] = {
        { 27, 100000003, 32, 32, 32 }, 
        { 27, 100000004, 32, 32, 32 }, 
        { 27, 100000005, 32, 32, 32 }, 
        { 27, 100000006, 32, 32, 32 }, 
    };

    // Attempt to save 4 packets when only room for 3 max...
    // main() passes the addresses of structs to the functions
    for( int i = 0; i < sizeof packs/sizeof packs[0]; i   ) {
        bool rVal = FIFO_AddItem( &can_rx_fifo, &packs[ i ] );

        printf( "#%d - %s\n", i, rVal ? "Failure" : "Success" );
    }


    can_packet_t item; // location to retrieve packet if available
    while( FIFO_GetItem( &can_rx_fifo, &item ) != true ) { // Strange, but as per description
        printf( "%d\n", item.RX_CAN_ID );
    }

    return 0;
}

CodePudding user response:

It's really not easy to understand pointers, but you don't need a book for that. Understand the pointer as a variable type that contains some memory address. Let's see an example:

int num = 10;
int *pointer = &num; // & returns with the address of the num variable
printf("Memory address of num:%p\n", pointer); // output: the memory address of the data
printf("The data stored at the memory address:%d\n", *pointer); // output: the stored variable, 10

I hope this short code can help you, but you should try it yourself, practice it. Let's see your questions:

  1. "How can I get rid of the variable and transpose it to the struct?" A: Simple, you should ovewrite it with your struct.

    typedef struct fifo_buffer_s { can_pack_t* data; uint16_t size;
    uint16_t head;
    uint16_t tail;
    uint16_t count;
    } fifo_buffer_t;

  2. "*data wich is 16bits wide a type of array? pointer array? by can_rx_fifo_buffer[CAN_RX_BUFFER_SIZE]" A: *data is a memory address, a pointer type of variable. It stores a memory address. When you init the fifo structure, you give the start of memory address of your array.

Your fifo implementation is ok, but it is not circular buffer. If buffer is full and you get a new data, then you have to overwrite the first data received with it.

  • you replace uint16_t with can_pack_t like this: uint8_t FIFO_AddItem(fifo_buffer_t * const buff_state, can_pack_t new_item)

Your array will be:

#define         CAN_RX_BUFFER_SIZE 32                                                           
can_pack_t can_rx_fifo_buffer[CAN_RX_BUFFER_SIZE];                   
fifo_buffer_t    can_rx_fifo;

Good luck with trying to code this!

  • Related