Wednesday, December 2, 2015

AVR 328p minimalist serial read/write source code

This is a C source code I wrote to do serial read/write on an Atmel AVR 328p microcontroller.
This is the same processor as the Arduino Uno for example. It is part of a growing collection of AVR routines.

And since you can set your own limits, you can use this code in place of the usual Serial class, as the default buffers are quite small and they may overflow if you are not processing them fast enough, or if you are sending too much data at once (eg. file copy/paste). Note that some terminals let you set add a small delay automatically after each line is sent, which is the way to go so as to leave enough time for your program to process the incoming flow.




''
#include <avr/io.h>
#include <stdbool.h>
#include <avr/interrupt.h>

#define BAUDS_RATE 115200

uint8_t tx_buffer[64]; // set your size (uses RAM)
uint8_t tx_head = 0;
volatile uint8_t tx_tail = 0;

uint8_t rx_buffer[128]; // set your size (uses RAM)
uint8_t rx_head = 0;
volatile uint8_t rx_tail = 0;

void serial_write(uint8_t data);
uint8_t serial_read();

int main()
{
uint16_t UBRR0_value = ((F_CPU / (4L * BAUDS_RATE)) - 1)/2;
UCSR0A |= (1 << U2X0);  // baud doubler on for high baud rates, i.e. 115200
UBRR0H = UBRR0_value >> 8;
UBRR0L = UBRR0_value;

// enable rx and tx
UCSR0B |= 1<<RXEN0;
UCSR0B |= 1<<TXEN0;

// enable interrupt when a byte was received
UCSR0B |= 1<<RXCIE0;

sei(); // Enable timers

while(1)
{
char c= serial_read(); // expect a character
if(c) serial_write(c+1); // send the following one back
}

return 0;
}

ISR(TIMER1_COMPA_vect)
{
return;
}

// return the first byte of the serial read buffer, or zero when empty
uint8_t serial_read()
{
if (rx_head == rx_tail)
return 0;
uint8_t data = rx_buffer[rx_tail];
if (++rx_tail == sizeof(rx_buffer))
rx_tail = 0;
return data;
}

// serial read interrupt handler
ISR(SERIAL_RX)
{
  uint8_t data = UDR0;
  uint8_t next_head;

  next_head = rx_head + 1;
  if (next_head == sizeof(rx_buffer))
 next_head = 0;

  // Write data to buffer unless it is full.
  if (next_head != rx_tail)
  {
 rx_buffer[rx_head] = data;
 rx_head = next_head;
  }
}

void serial_write(uint8_t data)
{
// move to next slot
uint8_t next_head = tx_head + 1;
if (next_head == sizeof(tx_buffer))
next_head = 0;

// store character and new position
tx_buffer[tx_head] = data;
tx_head = next_head;

// (possible re-)enable interrupt to send this piece of data
UCSR0B |=  (1 << UDRIE0);
}

// serial send interrupt handler
ISR(USART_UDRE_vect)
{
// Send a byte from the buffer
UDR0 = tx_buffer[tx_tail];

// Update tail position
if (++tx_tail == sizeof(tx_buffer))
tx_tail = 0;

// switch serial interrupts off when no data remains
if (tx_tail == tx_head)
UCSR0B &= ~(1 << UDRIE0);
}
''