|
We start always at the low level and we go up. Let's think
what should happen when a new byte is received. In the ISR, the new position
of the Write pointer is calculated and the new byte is stored, simple
enough. So, is it a simple advance for RxRW fine? No, it has to wrap around
and not to fall off the buffer edge. This is what masking does, grab a
piece of paper and do the maths. By the way, why the buffer length should be
a power of 2 bytes long?
SIGNAL(SIG_UART_RECV)
{
// advance RxWR
RxWR++;
// mask it
RxWR &= RxbufMask;
// store received byte in buffer
RxBuf[RxWR] = UDR;
// data added
numRx++;
}
The same concept applies for our ReceiveByte routine that
is used within our main program. Here, if there are bytes in the incoming
buffer we point to the oldest of them and we return it.
u8 ReceiveByte(void)
{
// advance RxRD
RxRD++;
// mask it
RxRD &= RxbufMask;
// data consumed
numRx--;
// return data
return Rxbuf[RxRD];
}
Regarding transmission, the Data Register Empty interrupt
is triggered every time a byte is transmitted and the data register gets
empty. Therefore, as long as we have bytes to send, we load them and we
enable the interrupt. If there are no more bytes to send, the interrupt
should be disabled. Have you noticed that for transmission the roles are
different? Here the consumer is not the actual TransmitByte routine but
the ISR, the TransmitByte routine generates data. Thus, within the ISR
we have to read data from the buffer. Another interesting thing here is
the fact that upon loading the Data Register, transmission starts.
SIGNAL(SIG_UART_DATA)
{
// check if more bytes are available
if (numTx>0)
{
// advance TxRD
TxRD++;
// mask it
TxRD &= TxbufMask;
// start transmission by loading UDR
UDR = TxBuf[TxRD];
// data sent
numTx--;
}
else
// Disable UDRE interrupt (no more bytes)
UCSRB &= ~(1<<UDRIE);
}
Now the TransmitByte routine should look very reasonable,
TxWR is advanced, a byte is stored in the outgoing buffer and the interrupt
is enabled for the ISR to be executed.
void TransmitByte(u8 data)
{
// advance TxWR
TxWR++;
// mask it
TxWR &= TxbufMask;
// store data in buffer
TxBuf[TxWR] = data;
// data added
numTx++;
// enable UDRE interrupt
UCSRB |= 1<<UDRIE;
}
That's all folks! Time to test it, here is the loopback
code you may use to test your routines.
while (1)
{
if (numRx>0)
{
TransmitByte(ReceiveByte()+1);
}
}
|