Archives

Page Ranking

A Matter of Interrupts and Mutual Exclusion - Part 3

Studying the implementation of the FIFO buffer from my previous posting some may have realized already that the code does not address few important things:

  • The example does not give the reader any indication where the buffer is actually allocated and how and when this allocation is done.
  • There is no check if the FIFO buffer is empty (in ComGetChar) or full (in Com_ISR) – the rx_counter is not used anywhere to detect such conditions despite of the declared intention of making it the holder of the actual number of bytes in the FIFO.
  • There is no mutual exclusion mechanism in place to prevent concurrent access to either the buffer itself or to the rx_counter that is, you heard it right, just another shared resource requiring protection.

Judging by the way the FIFO data structure was declared, the buffer itself is not allocated statically when a variable of COM_RX_BUFFER type is defined. We need either a pre-allocated buffer of a given size or a dynamically allocated piece of memory to initialize the rx_buf pointer. I think the best idea is to let the caller perform these operations, offering just the right means to do it.

The easiest way is to declare some storage area, statically allocated, and have a function to perform the initialization. The user must write something like:

#define RX_BUFFER_SIZE       128

U8      MyRxStorage[RX_BUFFER_SIZE];

void MyFunction  (void)                     // user function
{////////////////////////////////////////////////////////////////////////////////////////
    // . . . . . .                          // code that does not require serial I/O
    ComRxBufInit (MyRxStorage,              // initialize the Rx FIFO before
                  RX_BUFFER_SIZE);          // using it later in the function
    // . . . . . .
    UserChar = ComGetChar (1000);           // now you can read one byte
    // . . . . . .                          // do other things as required
} ///////////////////////////////////////////////////////////////////////////////////////

In the case of dynamically allocated memory, you will have to obtain the buffer by calling:

MyRxStorage = malloc (RX_BUFFER_SIZE);

and declaring MyRxStorage as a pointer not as an array. But I’m against using dynamic allocation in embedded programming. Keep an eye on my blog and… one day I will tell you why.

The function ComRxBufInit() will look like the one below:

void  ComRxBufInit  (U8 *pbuff, U32 size)   // Initialize the Rx FIFO structure
{///|///////////////////////////////////////|////////////////////////////////////////////
    COM_RX_BUFFER *pcom_buf;                // pointer to COM buffer
 
    pcom_buf = Com_GetBufPtr (USART1);      // obtain the pointer to the circular buffer
    pcom_buf->rx_buf = pbuff;               // store the FIFO buffer pointer
    pcom_buf->rx_buf_size = size;           // and its size
    pcom_buf->rx_count = 0;                 // reset the byte counter
    pcom_buf->rx_head = 0;                  // and the head
    pcom_buf->rx_tail = 0;                  // and tail pointers
    // . . . . . .                          // do other initialization things
 } ///////////////////////////////////////////////////////////////////////////////////////

As a side note, the function Com_GetBufPtr() was implemented to help the programmer to expand the code for more than one serial interface. In the end of my series of posts dedicated to this subject I will offer a fully functional code that will be able to handle any number of serial interfaces – the only part the programmer should be providing would be the lower layer that give access to the USART registers and the hardware initialization required by the MCU (I/O pins, baud rate, etc.)

I know this looks to many like a never ending story. Believe me, it has an ending or, even better, a happy ending. Stay tuned.

Comments are closed.