Get Ready

anything to declare?

Now that we have the idea let's write the code for it. Although the code is straightforward enough, it is a bit hard to start from nothing and write the final thing. When you have an idea and you try to write something that works start by defining the elements of your idea, the declarations. By the way, this is the profound difference between engineers and marketing people. They are happy just with the idea.

So, one buffer is not enough, as long as communication is full-duplex it is better to utilize two buffers, the incoming buffer (Rx) and the outgoing buffer (Tx). The buffers do not need to have the same length. What else? We need two pointers for each buffer, the Read and Write pointer. In terms of functions, we need an initialization routine, a Transmit and a Receive routine that will be used within our main loop and of course we will write the ISRs for the USART interrupts, the Rx Complete and the Data Register Empty interrupt. The former triggers when a byte has been received whereas the latter, when a byte has been sent.


/// Includes //////////
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>

/// Typedefs //////////
typedef unsigned char u8;
typedef unsigned int u16;

/// Defines ///////////
#define TxbufLength 32
#define TxbufMask TxbufLength - 1
#define RxbufLength 128
#define RxbufMask RxbufLength - 1

/// Prototypes ////////
void InitUART(u16 baud);
void TransmitByte(u8 data);
u8 ReceiveByte(void);

/// Globals ///////////
static u8 TxBuf[TxbufLength];
volatile static u8 TxWR, TxRD, numTx;
static u8 RxBuf[RxbufLength];
volatile static u8 RxWR, RxRD, numRx;
 

This is what we are going to use. A 32 bytes long outgoing buffer and an 128 bytes long incoming buffer. We need the incoming buffer to be fairly long, the longer it is the less likely to experience an overflow. On the other hand, we expect to transmit when we have a result, another task is not expected to run simultaneously so, we keep the buffer short. Do we need to keep the number of bytes in each buffer? No, numTx and numRx are redundant since they are equal with the respective pointer differences but, I like them and I am going to use them. What about the Mask definitions? Well, they just appeared to be needful, you 'll see why.

Note that since our variables are altered within interrupt service routines, they should be declared volatile. This tells the processor that their values should not be taken for granted. They may change out of the normal program flow and under any optimization they should not be left out. Onto the initialization routine, have a look at the datasheet to verify our settings.


void InitUSART(u16 baud)
{
   // set baud rate
   UBRRH = (u8)(baud>>8);
   UBRRL = (u8)baud;
   
   // enable Rx, Tx interrupts, 0 parity, 1 stop bit
   UCSRB = (1<<RXCIE)|(1<<RXEN)|(1<<TXEN)
   UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
   
   // initialize Tx, Rx buffer pointers
   TxWR = 0; TxRD = 0; numTx = 0
   RxWR = 0; RxRD = 0; numRx = 0;
}
 
 
 
Visit my other site!