Without doubt, the presence of the FIFO buffer helps (as we have seen before). The pieces of software that are needed to handle the FIFO are:
- the producer: Rx ISR (the receive interrupt service routine)
- the consumer: GetChar() function itself.
Assuming the USART is identified by the name USART1 and the implementation does not involve an RTOS yet, their code will look as follows:
Producer code (Rx ISR)
void Com_ISR (void) // USART Rx handler (ISR) {///////////////////////////////////////////////////////////////////////////////////////////// COM_RX_BUFFER *pcom_buf; // pointer to COM Rx buffer U8 b_char; // byte holder . . . . . . // identify the interrupt pcom_buf = Com_GetBufPtr (USART1); // obtain the pointer to the circular buffer . . . . . . // do other things as required b_char = USART_ReceiveData (USART1); // get one byte from USART1 Com_WriteRxBuf (pcom_buf, b_char); // write the byte into the circular buffer . . . . . . // do other things as required } ////////////////////////////////////////////////////////////////////////////////////////////
Consumer code (GetChar):
S32 ComGetChar (U32 timeout) // the "GetChar()" function {///////////////////////////////////////////////////////////////////////////////////////////// COM_RX_BUFFER *pcom_buf; // pointer to COM buffer U8 b_val; // byte holder pcom_buf = Com_GetBufPtr (USART1); // obtain the pointer to the circular buffer . . . . . . // do other things as required b_val = Com_ReadRxByte (pcom_buf); // extract the character from the circular buffer return (S32)b_val & 0xFF; // return the character as a 32-bit signed int } ////////////////////////////////////////////////////////////////////////////////////////////
Image may be NSFW.
Clik here to view.Simple, isn’t it? Well, not that simple. A quick look will find two functions that are not implemented yet (Com_WriteRxBuf and Com_ReadRxByte) and one parameter (timeout) that is not used in ComGetChar(). I will ignore the parameter for now and concentrate on the two functions just mentioned. Their role is to be the interface between the code above and the FIFO buffer we intend to use.
At a second glance the embedded developer will realize that the producer and the consumer run in two different contexts independent to each other: the ISR and the main thread where the function ComGetChar() is invoked sharing the FIFO buffer (common resource). As consequence, the access to the buffer needs to disciplined ensuring that only one process — producer or consumer — can access the buffer at one given time, i.e. mutual exclusion policy.
Let’s examine the FIFO buffer implementation. The following picture shows one of the most used methods of designing a FIFO in software. With a buffer of a given size and two pointers (named here Head and Tail) the FIFO can be described as a data structure and, eventually, as a data type — COM_RX_BUFFER:
typedef struct // Rx Circular Bufer structure { U8 volatile *rx_buf; // pointer to (volatile) Rx buffer U32 rx_buf_size; // Rx buffer size U32 volatile rx_head; // Rx head index U32 volatile rx_tail; // Rx tail index U32 volatile rx_count; // Rx byte counter } COM_RX_BUFFER;
As C programmers know, the relationship between pointers and arrays allow us to interchange subscripting with C pointers — I will prefer the first for clarity. I added a counter to keep track of how many bytes we have in the FIFO without the need of subtracting the head and tail pointers. The idea is simple: whenever an item is inserted I will use the tail pointer (one end of the buffer) and whenever an item is extracted I will use the head pointer (the other end of the buffer).
U8 Com_ReadRxByte (COM_RX_BUFFER *pcom_buf) {///////////////////////////////////////////////////////////////////////////////////////////// U8 b_val; // byte holder b_val = pcom_buf->rx_buf[pcom_buf->rx_head]; // extract one byte and pcom_buf->rx_head = (pcom_buf->rx_head + 1) % // advance the head index and wrap pcom_buf->rx_buf_size; // around if past end of buffer pcom_buf->rx_count--; // decrement the byte counter return b_val; // return the extracted byte } //////////////////////////////////////////////////////////////////////////////////////////// void Com_WriteRxBuf (COM_RX_BUFFER *pcom_buf, U8 b_char) {///////////////////////////////////////////////////////////////////////////////////////////// pcom_buf->rx_buf [pcom_buf->rx_tail] = b_char; // insert one byte pcom_buf->rx_tail = (pcom_buf->rx_tail + 1) % // advance the tail index and wrap pcom_buf->rx_buf_size; // around if past end of buffer pcom_buf->rx_count++; // increment the byte counter } ////////////////////////////////////////////////////////////////////////////////////////////
Look at the code: now you’ve got the idea. But don’t rush to use these functions yet. They may hide some flaws or omissions at least. Can you identify them? Please send me your comments. In the next posting I will examine in detail the problem of mutual exclusion and a way to solve it.