2018-12-02 10:55 AM
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
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.
2018-12-02 12:04 PM
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.
2018-12-02 12:07 PM
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.
2018-12-02 01:01 PM
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.
2018-12-02 02:03 PM
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:
The consumer thread then does:
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.
2018-12-02 02:31 PM
thank you for the reply, will investigate further
:)