Porting HardwareSerial to C

Refer to
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1248217939

for the reasons as to why I am doing this.

/*
 * hserial.h
 *
 *  Created on: Jul 25, 2009
 *      Author: Orlando Arias
 *     License: GPLv3
 *
 *   Based off: HardwareSerial.h
 */

#ifndef HSERIAL_H_
#define HSERIAL_H_

#include <inttypes.h>
#include "wiring.h"
#include "wiring_private.h"

#define RX_BUFFER_SIZE 128

typedef struct _ring_buffer {
      unsigned char buffer[RX_BUFFER_SIZE];
      int head;
      int tail;
} ring_buffer;

typedef struct _hserial {
      ring_buffer *rx_buffer;
    volatile uint8_t *ubrrh;
    volatile uint8_t *ubrrl;
    volatile uint8_t *ucsra;
    volatile uint8_t *ucsrb;
    volatile uint8_t *udr;
    uint8_t rxen;
    uint8_t txen;
    uint8_t rxcie;
    uint8_t udre;

} hserial;

#if defined(__AVR_ATmega1280__)
      hserial Serial[4];
#else
      hserial Serial[1];
#endif

void hs_init();                                                       // implemented
void hs_start(const int port, unsigned long baud);       // implemented
uint8_t hs_available(const int port);                        // implemented
void hs_flush(const int port);                                     // implemented
int hs_getChar(const int port);                                    // implemented
void hs_writeChar(const int port, uint8_t c);            // implemented
void hs_writeStr(const int port, const char str[]); // implemented

#endif /* HSERIAL_H_ */
/*
 * hserial.c
 *
 *  Created on: Jul 25, 2009
 *      Author: Orlando Arias
 *     License: GPLv3
 *
 *   Based off: HardwareSerial.cpp
 */
#include <inttypes.h>
#include <stdio.h>
#include "wiring.h"
#include "wiring_private.h"

#include "hserial.h"

// function prototypes
void hs_parseStr(const int port, const char *str);
inline void store_char(unsigned char c, ring_buffer *rx_buffer);

// Initialize rx_buffer arrays accordingly.
#if defined(__AVR_ATmega1280__)
      ring_buffer rx_buffer[4] = { { { 0 }, 0, 0 },
                                                 { { 0 }, 0, 0 }
                                                 { { 0 }, 0, 0 }
                                                 { { 0 }, 0, 0 } };
#else
      ring_buffer rx_buffer[1] =  { { { 0 }, 0, 0 } };
#endif

// serial macros
#if defined(__AVR_ATmega1280__)

SIGNAL(SIG_USART0_RECV)
{
      unsigned char c = UDR0;
  store_char(c, &rx_buffer[0]);
}

SIGNAL(SIG_USART1_RECV)
{
      unsigned char c = UDR1;
  store_char(c, &rx_buffer[1]);
}

SIGNAL(SIG_USART2_RECV)
{
      unsigned char c = UDR2;
  store_char(c, &rx_buffer[2]);
}

SIGNAL(SIG_USART3_RECV)
{
      unsigned char c = UDR3;
  store_char(c, &rx_buffer[3]);
}

#else

#if defined(__AVR_ATmega8__)
SIGNAL(SIG_UART_RECV)
#else
SIGNAL(USART_RX_vect)
#endif
{
#if defined(__AVR_ATmega8__)
      unsigned char c = UDR;
#else
      unsigned char c = UDR0;
#endif
  store_char(c, &rx_buffer[0]);
}
#endif

// Initialize serial objects according to processor type.
void hs_init(){
      #if defined(__AVR_ATmega8__)
            Serial[0].rx_buffer = &rx_buffer[0];
            Serial[0].ubrrh = &UBRRH;
            Serial[0].ubrrl = &UBRRL;
            Serial[0].ucsra = &UCSRA;
            Serial[0].ucsrb = &UCSRB;
            Serial[0].udr = &UDR;
            Serial[0].rxen = RXEN;
            Serial[0].txen = TXEN;
            Serial[0].rxcie = RXCIE;
            Serial[0].udre = UDRE;
      #else
            Serial[0].rx_buffer = &rx_buffer[0];
            Serial[0].ubrrh = &UBRR0H;
            Serial[0].ubrrl = &UBRR0L;
            Serial[0].ucsra = &UCSR0A;
            Serial[0].ucsrb = &UCSR0B;
            Serial[0].udr = &UDR0;
            Serial[0].rxen = RXEN0;
            Serial[0].txen = TXEN0;
            Serial[0].rxcie = RXCIE0;
            Serial[0].udre = UDRE0;
      #endif

      #if defined(__AVR_ATmega1280__)
            Serial[1].rx_buffer = &rx_buffer[1];
            Serial[1].ubrrh = &UBRR1H;
            Serial[1].ubrrl = &UBRR1L;
            Serial[1].ucsra = &UCSR1A;
            Serial[1].ucsrb = &UCSR1B;
            Serial[1].udr = &UDR1;
            Serial[1].rxen = RXEN1;
            Serial[1].txen = TXEN1;
            Serial[1].rxcie = RXCIE1;
            Serial[1].udre = UDRE1;

            Serial[2].rx_buffer = &rx_buffer[2];
            Serial[2].ubrrh = &UBRR2H;
            Serial[2].ubrrl = &UBRR2L;
            Serial[2].ucsra = &UCSR2A;
            Serial[2].ucsrb = &UCSR2B;
            Serial[2].udr = &UDR2;
            Serial[2].rxen = RXEN2;
            Serial[2].txen = TXEN2;
            Serial[2].rxcie = RXCIE2;
            Serial[2].udre = UDRE2;

            Serial[3].rx_buffer = &rx_buffer[3];
            Serial[3].ubrrh = &UBRR3H;
            Serial[3].ubrrl = &UBRR3L;
            Serial[3].ucsra = &UCSR3A;
            Serial[3].ucsrb = &UCSR3B;
            Serial[3].udr = &UDR3;
            Serial[3].rxen = RXEN3;
            Serial[3].txen = TXEN3;
            Serial[3].rxcie = RXCIE3;
            Serial[3].udre = UDRE3;
      #endif
}

void hs_start(const int port, unsigned long baud){
      *Serial[port].ubrrh = ((F_CPU / 16 + baud / 2) / baud - 1) >> 8;
      *Serial[port].ubrrl = ((F_CPU / 16 + baud / 2) / baud - 1);
      sbi(*Serial[port].ucsrb, Serial[port].rxen);
      sbi(*Serial[port].ucsrb, Serial[port].txen);
      sbi(*Serial[port].ucsrb, Serial[port].rxcie);
}

uint8_t hs_available(const int port){
      return (RX_BUFFER_SIZE + Serial[port].rx_buffer->head -
                  Serial[port].rx_buffer->tail) % RX_BUFFER_SIZE;
}

int hs_getChar(const int port){
      /*
       * If the head isn't ahead of the tail,
       * we don't have any characters
       */
      if (Serial[port].rx_buffer->head == Serial[port].rx_buffer->tail) {
            return -1;
      } else {
            unsigned char c =
                  Serial[port].rx_buffer->buffer[Serial[port].rx_buffer->tail];
            Serial[port].rx_buffer->tail =
                  (Serial[port].rx_buffer->tail + 1) % RX_BUFFER_SIZE;
            return c;
      }
}

void hs_flush(const int port){
      Serial[port].rx_buffer->head =
                  Serial[port].rx_buffer->tail;
}

void hs_writeChar(const int port, uint8_t c){
      while (!((*Serial[port].ucsra) & (1 << Serial[port].udre)));
      Serial[port].udr = &c;
}

void hs_writeStr(const int port, const char str[]){
      hs_parseStr(port, str);
}

void hs_parseStr(const int port, const char *str){
        while (*str)
          hs_writeChar(port, *str++);
}

inline void store_char(unsigned char c, ring_buffer *rx_buffer){
      int i = (rx_buffer->head + 1) % RX_BUFFER_SIZE;
      // if we should be storing the received character into the location
      // just before the tail (meaning that the head would advance to the
      // current location of the tail), we're about to overflow the buffer
      // and so we don't write the character or advance the head.
      if (i != rx_buffer->tail) {
            rx_buffer->buffer[rx_buffer->head] = c;
            rx_buffer->head = i;
      }
}

Test code:

/*
 * core.c
 *
 *  Created on: Jul 26, 2009
 *      Author: tron
 */

#include "WProgram.h"
#include "HD44780.h"
#include "hserial.h"

// declares a global LCD struct
LCD myLCD;

void setup(){
      // set up pinout
      myLCD.bitmode = BIT_MODE_4;
      myLCD.enable_pin = 10;
      myLCD.rs_pin = 12;
      myLCD.rw_pin = 11;

      for (int i=0;i<4;i++)
            myLCD.datapin[i] = 5-i;

      // initialize LCD
      initLCD(&myLCD);

      hs_init();
      hs_start(0, 9600);

      clearLCD(&myLCD);
      writeStr(&myLCD, "Program Initialized.");
      setCursor(&myLCD, 1,0);
      writeStr(&myLCD, "Awaiting input...");
}

void loop(){
      int incByte = 0;
      if (hs_available(0) > 0){
            clearLCD(&myLCD);
            incByte = hs_getChar(0);
            writeStr(&myLCD, "Received char: ");
            writeChar(&myLCD, incByte);
      }
}

I tried sending data away from the Arduino as well, reading the serial port using Minicom. Can't get that working either. No data seems to come out. I can read the output of the samples included with the Arduino IDE, but I can't send any data to the arduino.

Minicom settings:

A -    Serial Device      : /dev/ttyUSB0                              
B - Lockfile Location     : /var/lock                                  
C -   Callin Program      :                                           
D -  Callout Program      :                                           
E -    Bps/Par/Bits       : 9600 8N1                                  
F - Hardware Flow Control : Yes                                       
G - Software Flow Control : No

If I missed something, or something is wrong, please let me know. Thanks.

Switch hardware flow control from Yes to No.

Ok, by disabling hardware flow control, sending data to the board works. Thanks. However, sending data from the board to the PC still does not work:

      for (int i = 65; i<123; i++){
            clearLCD(&myLCD);
            writeStr(&myLCD, "Sending char: ");
            writeChar(&myLCD, i);
            hs_writeChar(0, (uint8_t)(i));
            delay(500);
      }

Any help is appreciated.

At about line 173 of hserial.c you should change:

      Serial[port].udr = &c;

to

      *(Serial[port].udr) = c;

And this is what I meant when I said I am not good with pointers. Thank you very much.

adds RuggedCircuits' contribution and gives credit

/*
 * hserial.h
 *
 *  Created on: Jul 25, 2009
 *      Author: Orlando Arias
 *     License: GPLv3
 *
 *   Based off: HardwareSerial.h
 */

#ifndef HSERIAL_H_
#define HSERIAL_H_

#include <inttypes.h>
#include "wiring.h"
#include "wiring_private.h"

#define RX_BUFFER_SIZE 128

typedef struct _ring_buffer {
      unsigned char buffer[RX_BUFFER_SIZE];
      int head;
      int tail;
} ring_buffer;

typedef struct _hserial {
      ring_buffer *rx_buffer;
    volatile uint8_t *ubrrh;
    volatile uint8_t *ubrrl;
    volatile uint8_t *ucsra;
    volatile uint8_t *ucsrb;
    volatile uint8_t *udr;
    uint8_t rxen;
    uint8_t txen;
    uint8_t rxcie;
    uint8_t udre;
} hserial;

/* The ATmega1280 has 4 serial ports available
 * thus we check whether we are cross compiling to
 * this CPU and create as many serial ports as needed.
 */
#if defined(__AVR_ATmega1280__)
      hserial Serial[4];
#else
      hserial Serial[1];
#endif

void hs_init();                                                       // Initializes all available serial ports.
void hs_start(const int port, unsigned long baud);       // Sets up a serial port.
uint8_t hs_available(const int port);                        // Checks whether data is available on the port
void hs_flush(const int port);                                     // Flushes the contents on the serial port.
int hs_getChar(const int port);                                    // Gets next character (rx) on serial port.
void hs_writeChar(const int port, uint8_t c);            // Write a character (tx) on serial port.
void hs_writeStr(const int port, const char str[]); // Write a string to serial port (tx).

#endif /* HSERIAL_H_ */
/*
 * hserial.c
 *
 *  Created on: Jul 25, 2009
 *      Author: Orlando Arias
 *     License: GPLv3
 *
 *
 *   Based off: HardwareSerial.cpp
 *
 *   Special thanks to RuggedCircuits for fixing bug on hs_writeChar() function.
 *               http://www.ruggedcircuits.com/
 *
 */
#include <inttypes.h>
#include <stdio.h>
#include "wiring.h"
#include "wiring_private.h"

#include "hserial.h"

// function prototypes
void hs_parseStr(const int port, const char *str);
inline void store_char(unsigned char c, ring_buffer *rx_buffer);

// Initialize rx_buffer arrays accordingly.
#if defined(__AVR_ATmega1280__)
      ring_buffer rx_buffer[4] = { { { 0 }, 0, 0 },
                                                 { { 0 }, 0, 0 }
                                                 { { 0 }, 0, 0 }
                                                 { { 0 }, 0, 0 } };
#else
      ring_buffer rx_buffer[1] =  { { { 0 }, 0, 0 } };
#endif

// serial macros
#if defined(__AVR_ATmega1280__)

SIGNAL(SIG_USART0_RECV)
{
      unsigned char c = UDR0;
  store_char(c, &rx_buffer[0]);
}

SIGNAL(SIG_USART1_RECV)
{
      unsigned char c = UDR1;
  store_char(c, &rx_buffer[1]);
}

SIGNAL(SIG_USART2_RECV)
{
      unsigned char c = UDR2;
  store_char(c, &rx_buffer[2]);
}

SIGNAL(SIG_USART3_RECV)
{
      unsigned char c = UDR3;
  store_char(c, &rx_buffer[3]);
}

#else

#if defined(__AVR_ATmega8__)
SIGNAL(SIG_UART_RECV)
#else
SIGNAL(USART_RX_vect)
#endif
{
#if defined(__AVR_ATmega8__)
      unsigned char c = UDR;
#else
      unsigned char c = UDR0;
#endif
  store_char(c, &rx_buffer[0]);
}
#endif

// Initialize serial objects according to processor type.
void hs_init(){
      #if defined(__AVR_ATmega8__)
            Serial[0].rx_buffer = &rx_buffer[0];
            Serial[0].ubrrh = &UBRRH;
            Serial[0].ubrrl = &UBRRL;
            Serial[0].ucsra = &UCSRA;
            Serial[0].ucsrb = &UCSRB;
            Serial[0].udr = &UDR;
            Serial[0].rxen = RXEN;
            Serial[0].txen = TXEN;
            Serial[0].rxcie = RXCIE;
            Serial[0].udre = UDRE;
      #else
            Serial[0].rx_buffer = &rx_buffer[0];
            Serial[0].ubrrh = &UBRR0H;
            Serial[0].ubrrl = &UBRR0L;
            Serial[0].ucsra = &UCSR0A;
            Serial[0].ucsrb = &UCSR0B;
            Serial[0].udr = &UDR0;
            Serial[0].rxen = RXEN0;
            Serial[0].txen = TXEN0;
            Serial[0].rxcie = RXCIE0;
            Serial[0].udre = UDRE0;
      #endif

      #if defined(__AVR_ATmega1280__)
            Serial[1].rx_buffer = &rx_buffer[1];
            Serial[1].ubrrh = &UBRR1H;
            Serial[1].ubrrl = &UBRR1L;
            Serial[1].ucsra = &UCSR1A;
            Serial[1].ucsrb = &UCSR1B;
            Serial[1].udr = &UDR1;
            Serial[1].rxen = RXEN1;
            Serial[1].txen = TXEN1;
            Serial[1].rxcie = RXCIE1;
            Serial[1].udre = UDRE1;

            Serial[2].rx_buffer = &rx_buffer[2];
            Serial[2].ubrrh = &UBRR2H;
            Serial[2].ubrrl = &UBRR2L;
            Serial[2].ucsra = &UCSR2A;
            Serial[2].ucsrb = &UCSR2B;
            Serial[2].udr = &UDR2;
            Serial[2].rxen = RXEN2;
            Serial[2].txen = TXEN2;
            Serial[2].rxcie = RXCIE2;
            Serial[2].udre = UDRE2;

            Serial[3].rx_buffer = &rx_buffer[3];
            Serial[3].ubrrh = &UBRR3H;
            Serial[3].ubrrl = &UBRR3L;
            Serial[3].ucsra = &UCSR3A;
            Serial[3].ucsrb = &UCSR3B;
            Serial[3].udr = &UDR3;
            Serial[3].rxen = RXEN3;
            Serial[3].txen = TXEN3;
            Serial[3].rxcie = RXCIE3;
            Serial[3].udre = UDRE3;
      #endif
}

void hs_start(const int port, unsigned long baud){
      *Serial[port].ubrrh = ((F_CPU / 16 + baud / 2) / baud - 1) >> 8;
      *Serial[port].ubrrl = ((F_CPU / 16 + baud / 2) / baud - 1);
      sbi(*Serial[port].ucsrb, Serial[port].rxen);
      sbi(*Serial[port].ucsrb, Serial[port].txen);
      sbi(*Serial[port].ucsrb, Serial[port].rxcie);
}

uint8_t hs_available(const int port){
      return (RX_BUFFER_SIZE + Serial[port].rx_buffer->head -
                  Serial[port].rx_buffer->tail) % RX_BUFFER_SIZE;
}

int hs_getChar(const int port){
      /*
       * If the head isn't ahead of the tail,
       * we don't have any characters
       */
      if (Serial[port].rx_buffer->head == Serial[port].rx_buffer->tail) {
            return -1;
      } else {
            unsigned char c =
                  Serial[port].rx_buffer->buffer[Serial[port].rx_buffer->tail];
            Serial[port].rx_buffer->tail =
                  (Serial[port].rx_buffer->tail + 1) % RX_BUFFER_SIZE;
            return c;
      }
}

void hs_flush(const int port){
      Serial[port].rx_buffer->head =
                  Serial[port].rx_buffer->tail;
}

void hs_writeChar(const int port, uint8_t c){
      while (!((*Serial[port].ucsra) & (1 << Serial[port].udre)));
      *(Serial[port].udr) = c;
}

void hs_writeStr(const int port, const char str[]){
      hs_parseStr(port, str);
}

void hs_parseStr(const int port, const char *str){
        while (*str)
          hs_writeChar(port, *str++);
}

inline void store_char(unsigned char c, ring_buffer *rx_buffer){
      int i = (rx_buffer->head + 1) % RX_BUFFER_SIZE;
      // if we should be storing the received character into the location
      // just before the tail (meaning that the head would advance to the
      // current location of the tail), we're about to overflow the buffer
      // and so we don't write the character or advance the head.
      if (i != rx_buffer->tail) {
            rx_buffer->buffer[rx_buffer->head] = c;
            rx_buffer->head = i;
      }
}

To use:

/*
 * core.c
 *
 *  Created on: Jul 26, 2009
 *      Author: Orlando Arias
 *     License: GPLv3
 *
 *     Requires HD44780.h or equivalent for HD44780 LCD manipulation.
 */

#include "WProgram.h"
#include "HD44780.h"
#include "hserial.h"
#include <inttypes.h>

// declares a global LCD struct
LCD myLCD;

void setup(){
      // set up pinout
      myLCD.bitmode = BIT_MODE_4;
      myLCD.enable_pin = 10;
      myLCD.rs_pin = 12;
      myLCD.rw_pin = 11;

      for (int i=0;i<4;i++)
            myLCD.datapin[i] = 5-i;

      // initialize LCD
      initLCD(&myLCD);

      hs_init();
      hs_start(0, 9600);

      clearLCD(&myLCD);
      writeStr(&myLCD, "Program Initialized.");

      // swap comments to change functionality
      delay(1000);
      //setCursor(&myLCD, 1,0);
      //writeStr(&myLCD, "Awaiting input...");
}

void loop(){

      // Use this code to type stuff on the LCD.

      /*int incByte = 0;
      static int chrCount = 0;
      if (hs_available(0) > 0){
            if (chrCount == 0){
                  clearLCD(&myLCD);
            }
            chrCount++;
            if (chrCount == 21) {
                  setCursor(&myLCD, 1,0);
            } else if (chrCount == 41){
                  clearLCD(&myLCD);
                  chrCount = 1;
            }
            incByte = hs_getChar(0);
            //writeStr(&myLCD, "Received char: ");
            writeChar(&myLCD, incByte);
      }*/

      // use this code to send stuff to computer

      for (int i = 65; i<123; i++){
            clearLCD(&myLCD);
            writeStr(&myLCD, "Sending char: ");
            writeChar(&myLCD, i);
            hs_writeChar(0, (uint8_t)(i));
            delay(500);
      }
}

Thank you so very much, and happy hacking.