// Copyright 2009 Olivier Gillet.
//
// Author: Olivier Gillet (ol.gillet@gmail.com)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
// -----------------------------------------------------------------------------
//
// Fast serial (for the onboard UART), using compile time optimizations.
//
// Can be used in buffered mode, or in polled mode (to save space or avoid
// interruptions if there is an even higher priority task than the serial I/O).
//
// Usage:
//
// Set-up:
// typedef Serial Serial;
// then, in the setup() hook: Serial::Init()
//
// Write:
// Serial::Write(40) // Will block until data is written.
// write_has_succeeded = Serial::NonBlockingWrite(40) // Will not block.
//
// Buffer manipulation (for buffered I/O):
// Serial::available() // Number of bytes ready to be read. For polled read too
// my_value = Serial::Read() // Will wait until data arrives.
// my_value = Serial::NonBlockingRead() // Will return -1 if no data is there.
// my_value = Serial::ImmediateRead() // Assumes you are sure about what you
// are doing and you know that data is here.
//
// Flushing a buffer:
// Serial::InputBuffer::Flush()
//
// TODO(pichenettes): Buffered writes not supported for now (should look up
// the right interrupt handler).
#ifndef AVRLIB_SERIAL_H_
#define AVRLIB_SERIAL_H_
#include "avrlib/avrlib.h"
#include "avrlib/gpio.h"
#include "avrlib/ring_buffer.h"
namespace avrlib {
const uint8_t kSerialOutputBufferSize = 32;
const uint8_t kSerialInputBufferSize = 32;
// Low-level interface to the low-level UART registers. Several specializations
// may be declared for each serial port. This class could theoretically be used
// for non-blocking write or polling reads.
template
struct SerialPort {
typedef TxEnableBit Tx;
typedef RxEnableBit Rx;
typedef RxInterruptBit RxInterrupt;
typedef TurboBit Turbo;
enum {
input_buffer_size = input_buffer_size_,
output_buffer_size = output_buffer_size_
};
static inline void set_prescaler(uint16_t value) {
*PrescalerRegisterH::ptr() = value >> 8;
*PrescalerRegisterL::ptr() = value;
}
static inline uint8_t tx_ready() { return TxReadyBit::value(); }
static inline uint8_t rx_ready() { return RxReadyBit::value(); }
static inline uint8_t data() { return *DataRegister::ptr(); }
static inline void set_data(uint8_t value) { *DataRegister::ptr() = value; }
};
template
struct SerialInput : public Input {
enum {
buffer_size = SerialPort::input_buffer_size,
data_size = 8
};
typedef uint8_t Value;
// Blocking!
static inline Value Read() { while (!readable()); return ImmediateRead(); }
// Number of bytes available for read.
static inline uint8_t readable() { return SerialPort::rx_ready(); }
// A byte, or -1 if reading failed.
static inline int16_t NonBlockingRead() { return readable() ? Read() : -1; }
// No check for ready state.
static inline Value ImmediateRead() { return SerialPort::data(); }
// Called in data reception interrupt.
static inline void Received() {
if (!readable()) {
return;
}
// This will discard data if the buffer is full.
RingBuffer >::NonBlockingWrite(ImmediateRead());
}
};
template
struct SerialOutput : public Output {
enum {
buffer_size = SerialPort::output_buffer_size,
data_size = 8
};
typedef uint8_t Value;
// Blocking!
static inline void Write(Value v) { while (!writable()); Overwrite(v); }
// Number of bytes that can be fed.
static inline uint8_t writable() { return SerialPort::tx_ready(); }
// 1 if success.
static inline uint8_t NonBlockingWrite(Value v) {
if (!writable()) {
return 0;
}
Overwrite(v);
return 1;
}
// No check for ready state.
static inline void Overwrite(Value v) { SerialPort::set_data(v); }
// Called in data emission interrupt.
static inline Value Requested() {
Value v = RingBuffer >::NonBlockingRead();
if (v >= 0) {
Overwrite(v);
}
}
};
template
struct SerialImplementation { };
template
struct SerialImplementation {
typedef InputOutput IO;
};
template
struct SerialImplementation {
typedef InputOutput > IO;
};
template
struct SerialImplementation {
typedef RingBuffer > OutputBuffer;
typedef InputOutput IO;
};
template
struct SerialImplementation {
typedef InputOutput, DisabledOutput> IO;
};
template
struct SerialImplementation {
typedef InputOutput, SerialOutput > IO;
};
template
struct SerialImplementation {
typedef RingBuffer > OutputBuffer;
typedef InputOutput, OutputBuffer> IO;
};
template
struct SerialImplementation {
typedef RingBuffer > InputBuffer;
typedef InputOutput IO;
};
template
struct SerialImplementation {
typedef RingBuffer > InputBuffer;
typedef InputOutput > IO;
};
template
struct SerialImplementation {
typedef RingBuffer > InputBuffer;
typedef RingBuffer > OutputBuffer;
typedef InputOutput IO;
};
template
struct Serial {
typedef SerialImplementation Impl;
typedef uint8_t Value;
typedef typename Impl::IO::Input Input;
typedef typename Impl::IO::Output Output;
static inline void Init() {
Init();
}
template
static inline void Init() {
if (turbo) {
SerialPort::Turbo::set();
uint16_t prescaler = F_CPU / (8L * baud_rate) - 1;
SerialPort::set_prescaler(prescaler);
} else {
SerialPort::Turbo::clear();
uint16_t prescaler = F_CPU / (16 * baud_rate) - 1;
SerialPort::set_prescaler(prescaler);
}
if (output != DISABLED) {
SerialPort::Tx::set();
}
if (input != DISABLED) {
SerialPort::Rx::set();
}
if (input == BUFFERED) {
SerialPort::RxInterrupt::set();
}
}
static inline void Disable() {
SerialPort::Tx::clear();
SerialPort::Rx::clear();
SerialPort::RxInterrupt::clear();
}
static inline void Write(Value v) { Impl::IO::Write(v); }
static inline uint8_t writable() { return Impl::IO::writable(); }
static inline uint8_t NonBlockingWrite(Value v) {
return Impl::IO::NonBlockingWrite(v);
}
static inline void Overwrite(Value v) { Impl::IO::Overwrite(v); }
static inline Value Read() { return Impl::IO::Read(); }
static inline uint8_t readable() { return Impl::IO::readable(); }
static inline int16_t NonBlockingRead() {
return Impl::IO::NonBlockingRead();
}
static inline Value ImmediateRead() { return Impl::IO::ImmediateRead(); }
};
#ifdef HAS_USART0
IORegister(UBRR0H);
IORegister(UBRR0L);
IORegister16(UBRR0);
IORegister(UCSR0A);
IORegister(UCSR0B);
IORegister(UCSR0C);
IORegister(UDR0);
typedef SerialPort<
BitInRegister,
BitInRegister,
BitInRegister,
BitInRegister,
BitInRegister,
BitInRegister,
UBRR0HRegister,
UBRR0LRegister,
UDR0Register,
kSerialOutputBufferSize,
kSerialInputBufferSize> SerialPort0;
#endif // #ifdef HAS_USART0
#ifdef HAS_USART1
IORegister(UBRR1H);
IORegister(UBRR1L);
IORegister16(UBRR1);
IORegister(UCSR1A);
IORegister(UCSR1B);
IORegister(UCSR1C);
IORegister(UDR1);
typedef SerialPort<
BitInRegister,
BitInRegister,
BitInRegister,
BitInRegister,
BitInRegister,
BitInRegister,
UBRR1HRegister,
UBRR1LRegister,
UDR1Register,
kSerialOutputBufferSize,
kSerialInputBufferSize> SerialPort1;
#endif // #ifdef HAS_USART1
#ifdef HAS_USART2
IORegister(UBRR2H);
IORegister(UBRR2L);
IORegister16(UBRR2);
IORegister(UCSR2A);
IORegister(UCSR2B);
IORegister(UCSR2C);
IORegister(UDR2);
typedef SerialPort<
BitInRegister,
BitInRegister,
BitInRegister,
BitInRegister,
BitInRegister,
BitInRegister,
UBRR2HRegister,
UBRR2LRegister,
UDR2Register,
kSerialOutputBufferSize,
kSerialInputBufferSize> SerialPort2;
#endif // #ifdef HAS_USART2
#ifdef HAS_USART3
IORegister(UBRR3H);
IORegister(UBRR3L);
IORegister16(UBRR3);
IORegister(UCSR3A);
IORegister(UCSR3B);
IORegister(UCSR3C);
IORegister(UDR3);
typedef SerialPort<
BitInRegister,
BitInRegister,
BitInRegister,
BitInRegister,
BitInRegister,
BitInRegister,
UBRR3HRegister,
UBRR3LRegister,
UDR3Register,
kSerialOutputBufferSize,
kSerialInputBufferSize> SerialPort3;
#endif // #ifdef HAS_USART3
} // namespace avrlib
#endif // AVRLIB_SERIAL_H_