// Copyright 2012 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 . #ifndef AVRLIBX_IO_TWI_H_ #define AVRLIBX_IO_TWI_H_ #include #include "avrlibx/avrlibx.h" #include "avrlibx/io/gpio.h" #include "avrlibx/io/ring_buffer.h" namespace avrlibx { template struct TwiWrapper { }; #define WRAP_TWI(port) \ template<> \ struct TwiWrapper { \ static inline TWI_t& twi() { \ return TWI ## port; \ } \ static inline TWI_MASTER_t& master() { \ return twi().MASTER; \ } \ static inline TWI_SLAVE_t& slave() { \ return twi().SLAVE; \ } \ static inline void Handler() { \ if (handler_) { \ (*handler_)(); \ } \ } \ static inline void set_interrupt_handler(void (*handler)()) { \ handler_ = handler; \ } \ static void (*handler_)(); \ }; enum TWIState { TWI_STATE_READY, TWI_STATE_TRANSMITTING, TWI_STATE_RECEIVING, }; enum TWIError { TWI_ERROR_NONE = 0, TWI_ERROR_NO_ACK = 1, TWI_ERROR_ARBITRATION_LOST = 2, TWI_ERROR_BUS_ERROR = 3, TWI_ERROR_UNEXPECTED_STATE = 4 }; WRAP_TWI(C) WRAP_TWI(E) template class TwiOutputBufferSpecs { public: TwiOutputBufferSpecs() { } enum { buffer_size = size, data_size = 8 }; typedef uint8_t Value; }; template class TwiInputBufferSpecs { public: TwiInputBufferSpecs() { } enum { buffer_size = size, data_size = 8 }; typedef uint8_t Value; }; template< typename Port, uint32_t frequency = 100000, /* Hz */ uint8_t input_buffer_size = 16, uint8_t output_buffer_size = 16, uint8_t int_level = 1> struct TwiMaster { typedef TwiWrapper Twi; static inline void Init() { Twi::set_interrupt_handler(&InterruptHandler); Twi::twi().CTRL = 0; Twi::master().BAUD = F_CPU / (2 * frequency) - 5; Twi::master().CTRLA = (int_level << 6) \ | TWI_MASTER_RIEN_bm \ | TWI_MASTER_WIEN_bm \ | TWI_MASTER_ENABLE_bm; Twi::master().STATUS = TWI_MASTER_BUSSTATE_IDLE_gc; state_ = TWI_STATE_READY; } static uint8_t Wait() { while (state_ != TWI_STATE_READY) { } return error_; } static void Done() { Twi::master().CTRLA = 0; Twi::set_interrupt_handler(NULL); } static uint8_t Request(uint8_t address, uint8_t requested) { // Make sure that we don't request more than the buffer can hold. if (requested >= Input::writable()) { return 0; } if (state_ != TWI_STATE_READY) { return 0; } error_ = TWI_ERROR_NONE; state_ = TWI_STATE_RECEIVING; slarw_ = (address << 1) | 1; received_ = 0; requested_ = requested; Twi::master().ADDR = slarw_; return requested; } static uint8_t Send(uint8_t address) { if (!Output::readable()) { return 0; } if (state_ != TWI_STATE_READY) { return 0; } error_ = TWI_ERROR_NONE; state_ = TWI_STATE_TRANSMITTING; slarw_ = (address << 1) | 0; uint8_t sent = Output::readable(); Twi::master().ADDR = slarw_; return sent; } // All the read/write operations are done on the buffer, so they do not // block. static inline void Write(uint8_t v) { Output::Write(v); } static inline uint8_t writable() { return Output::writable(); } static inline uint8_t NonBlockingWrite(uint8_t v) { return Output::NonBlockingWrite(v); } static inline void Overwrite(uint8_t v) { Output::Overwrite(v); } static inline uint8_t Read() { return Input::Read(); } static inline uint8_t readable() { return Input::readable(); } static inline int16_t NonBlockingRead() { return Input::NonBlockingRead(); } static inline uint8_t ImmediateRead() { return Input::ImmediateRead(); } static inline void FlushInputBuffer() { Input::Flush(); } static inline void FlushOutputBuffer() { Output::Flush(); } private: static void InterruptHandler() { uint8_t current_status = Twi::master().STATUS; if ((current_status & TWI_MASTER_ARBLOST_bm) || (current_status & TWI_MASTER_BUSERR_bm)) { // Arbitration lost or bus error. error_ = current_status & TWI_MASTER_ARBLOST_bm ? TWI_ERROR_ARBITRATION_LOST : TWI_ERROR_BUS_ERROR; state_ = TWI_STATE_READY; } else if (current_status & TWI_MASTER_WIF_bm) { // Ready for write. // Check if slave has NACK'ed. if (current_status & TWI_MASTER_RXACK_bm) { error_ = TWI_ERROR_NO_ACK; state_ = TWI_STATE_READY; Twi::master().CTRLC = TWI_MASTER_CMD_STOP_gc; } else if (Output::readable()) { Twi::master().DATA = Output::ImmediateRead(); } else if (received_ < requested_) { Twi::master().ADDR = slarw_; } else { Twi::master().CTRLC = TWI_MASTER_CMD_STOP_gc; state_ = TWI_STATE_READY; } } else if (current_status & TWI_MASTER_RIF_bm) { Input::Overwrite(Twi::master().DATA); ++received_; if (received_ < requested_) { Twi::master().CTRLC = TWI_MASTER_CMD_RECVTRANS_gc; } else { Twi::master().CTRLC = TWI_MASTER_ACKACT_bm | TWI_MASTER_CMD_STOP_gc; state_ = TWI_STATE_READY; } } else { error_ = TWI_ERROR_UNEXPECTED_STATE; state_ = TWI_STATE_READY; } } public: typedef RingBuffer > Input; typedef RingBuffer > Output; private: static volatile uint8_t state_; static volatile uint8_t error_; static volatile uint8_t slarw_; static volatile uint8_t received_; static uint8_t requested_; DISALLOW_COPY_AND_ASSIGN(TwiMaster); }; /* static */ template volatile uint8_t TwiMaster::state_; /* static */ template volatile uint8_t TwiMaster::error_; /* static */ template volatile uint8_t TwiMaster::slarw_; /* static */ template volatile uint8_t TwiMaster::received_; /* static */ template uint8_t TwiMaster::requested_; } // namespace avrlibx #endif // AVRLIBX_IO_TWI_H_