// 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_