cancel
Showing results for 
Search instead for 
Did you mean: 

CAN Buffer designing RING FIFO engine

JHERI
Senior

Hello,

Ok have a problem that I would like to over come , I am developing with CAN and the Nucleo st32F446re board.

All is working and I can send and and receive packets of CAN data, but..... I would like to implement a more fail safe engine to deal with heavy can data traffic and storing it in memory without loosing data.

So 1st off I was hoping to assign CAN peripheral to the DMA controller, but sadly no joy there and there is no DMA support for the CAN peripheral

So my only other option is to create a FIFO buffer for the storage, but I am having trouble working this out, my c code knowledge is a little green with pointers etc.

So would like some help please

  

I know what I would like to achieve here it is, below .... I have the RING buffer working with a single element, i.e. unit_16, with 32 levels of storage,

but don't know how to add more arrays / variables to the multi depth fifo structure

 this is what i would like

0690X000006CZ8cQAG.jpg

RING BUFFER [32] levels deep (or what ever I require)

--------------------------

members of the structure circular FIFO

uint8_t CAN DATA BYTES[8] ....................................up to 8 bytes of CAN Data (8 bytes wide)

uint32_t Message_ID ................................................stores standard or extended message ID

unit16_t DLC/RTR ......................................................stores 2 8bit values side by side

  

Ok I have the circular FIFO buffer working for a single variable as the code below, but not sure how to make the buffer as I require

I need to be able to write and read the CAN data bytes (0-8) to the ARRAY also need to store and read the Message ID / DLC/RTR values

 My SINGLE FIFO RING BUFFER is as below 

FIFO HEADER FILE 
******************************************************************************/
 
#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);  

top of main.c 
 
assign various fifos, this one for UART
 
// ----- UART RX Data Handle Structure FiFo Variables -------
 
  #define         UART2_BUFFER_SIZE 32                                                                  // define UART2 tx message fifo buffer to 32 byte fifo
  uint16_t        uart2_tx_fifo_buffer[UART2_BUFFER_SIZE];                                              // create BUFFER ARRAY for uart2_tx_fifo with above size
 
  fifo_buffer_t   uart2_tx_fifo;    

main.c (initialise uart FIFO)
 
 FIFO_Init( &uart2_tx_fifo , uart2_tx_fifo_buffer  , sizeof(uart2_tx_fifo_buffer));   

main.c (use uart Fifo)
 
uint8_t ret = 0;
uint8_t a   = 1;
 
    for( a = 1; a < 8; a = a + 1 )
        {
        ret = FIFO_AddItem( &uart2_tx_fifo, a );         // 01 tx uart message to clear uart terminal
        }                                                // 02 tx uart message to display ST32 title
                                                         // 03 tx uart message to display HSE,HSI,LSE,LSI Frequency
                                                         // 04 tx uart message to display SYSCLK
                                                         // 05 tx uart message to display HCLK
                                                         // 06 tx uart message to display PCLK1
                                                         // 07 tx uart message to display PCLK2

Fifo.c Various function relatated to assigned Fifo
 
 
// ************************************************************************
// ********** 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;
}

 Ok so this is my single element circular FIFO working fine for one single variable (16bit wide or what ever bit width I declare)

How can I now re-mould the Fifo buffer code to accept more variables and CAN DATA ARRAY variable,

Please explain simply as I really struggle with pointers and trying to work out what is going on with memory addresses etc.

need to know how to write all variable data to each level of the FIFO and how to extract each variables data from each level of the FIFO

thank you in advance for any help, it is very much appreciated.

5 REPLIES 5

I would probably lean to a FIFO/RING that manages data efficiently at a structure level. ie each allocation unit is a structure containing CAN_RxHeaderTypeDef and Data[8].

You could do this with something containing uint16_t, but that is going to be unnecessarily laboured. You want the delivery into the FIFO/RING with a couple of memcpy(), so the queuing can occur rapidly.

I use byte FIFOs for USART as the delivery unit is small and singular, and have the code to pull stuff out capable of dealing with large blocks up to the total FIFO size, and managing the wrap by decomposing the pull request into one or two memcpy() into the destination buffer.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Jeroen3
Senior

Have you ever heard of memory pools? https://en.wikipedia.org/wiki/Memory_pool

You can use memory pools and put the pointer or index of the memory pool in your fifo. The consumer of the data simply has to get a memory pool pointer/index and cast it back to a can frame structure.

JHERI
Senior

Thank you for the replies and please keep them coming

would love to see some examples too. I am in the process of having a read of memory pools

not quite understanding ???

are you saying the CAN peripheral directly reads and store from the memory pool, and when there is free processor time the FIFO reads and writes data from the memory pool.

this would be instead of the CAN peripheral read/writing directly to the FIFO

Is that correct ???

Thanks once again in advance for any help and info.;)

This is all a little bit new to me, but know what I would like to achieve, it's all about doing it the best way.

Jeroen3
Senior

No, the bxCAN peripheral in ST part is fairly limited. It has a fixed hardware fifo (the mailboxes) even though it utilizes some SRAM for this.

You can easily extend bxCAN in two ways. Software mailboxes or a fifo. Depending on the purpose of the code, you must choose which.

For the fifo implementation you need to write the RX IRQ to do the following:

  1. perform fine ID filtering, if the hardware is block based.
  2. Request a memory pool block.
  3. Copy the message in the memory pool block.
  4. Store the memory pool pointer in the fifo.

The consumer thread then does:

  1. check the fifo for contents.
  2. extracts the pointer from the fifo, and casts it to a can frame structure pointer.
  3. the thing the message is for.
  4. free the memory pool block.

The mailbox approach works with an array of can frame structures for each message (all of them the device needs), when the message arrives, it is put in the corresponding mailbox, and each thread checks this mailbox or gets a signal.

The advantage of this is that you can assign other parameters, like callbacks or timed intervals, to each mailbox for automatic timed transmits.

You can easily make a hybrid version between these two if you are not in a hard real time system. (Put messages in fifo, other thread read them and calls corresponding callback of the mailbox on new data, expired timer or transmit flag)

See if you can find some CAN stacks online, they might explain more with examples.

JHERI
Senior

thank you for the reply, will investigate further

:)