|
- // Copyright 2010 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 <http://www.gnu.org/licenses/>.
- //
- // -----------------------------------------------------------------------------
- //
- // Implementation of the I2C protocol (Master mode only for now).
- //
- // Note that this file is not in the hal directory directly because I don't want
- // the interrupt handler code for TWI to be linked with every project.
-
- #ifndef AVRLIB_I2C_I2C_H_
- #define AVRLIB_I2C_I2C_H_
-
- #include <util/twi.h>
-
- #include "avrlib/gpio.h"
- #include "avrlib/avrlib.h"
- #include "avrlib/ring_buffer.h"
-
- namespace avrlib {
-
- IORegister(TWCR);
- IORegister(TWSR);
- typedef BitInRegister<TWSRRegister, TWPS0> Prescaler0;
- typedef BitInRegister<TWSRRegister, TWPS1> Prescaler1;
- typedef BitInRegister<TWCRRegister, TWEN> I2cEnable;
- typedef BitInRegister<TWCRRegister, TWIE> I2cInterrupt;
- typedef BitInRegister<TWCRRegister, TWEA> I2cAck;
- typedef BitInRegister<TWCRRegister, TWSTA> I2cStart;
- typedef BitInRegister<TWCRRegister, TWSTO> I2cStop;
-
- enum I2cState {
- I2C_STATE_READY,
- I2C_STATE_TRANSMITTING,
- I2C_STATE_RECEIVING,
- };
-
- enum I2cError {
- I2C_ERROR_NONE = 0xff,
- I2C_ERROR_NO_ACK_FOR_ADDRESS = 0x01,
- I2C_ERROR_NO_ACK_FOR_DATA = TW_MT_SLA_NACK,
- I2C_ERROR_ARBITRATION_LOST = TW_MT_DATA_NACK,
- I2C_ERROR_BUS_ERROR = 0xfe,
- I2C_ERROR_TIMEOUT = 0x02,
- };
-
- template<uint8_t output_buffer_size = 4>
- class I2cOutput {
- public:
- I2cOutput() { }
- enum {
- buffer_size = output_buffer_size,
- data_size = 8
- };
- typedef typename DataTypeForSize<data_size>::Type Value;
- };
-
- template<uint8_t input_buffer_size = 4>
- class I2cInput {
- public:
- I2cInput() { }
- enum {
- buffer_size = input_buffer_size,
- data_size = 8
- };
- typedef typename DataTypeForSize<data_size>::Type Value;
- };
-
- // I2C Handler.
- extern void (*i2c_handler_)();
-
- template<uint8_t input_buffer_size = 16,
- uint8_t output_buffer_size = 16,
- uint32_t frequency = 100000 /* Hz */>
- class I2cMaster {
- public:
- I2cMaster() { }
-
- typedef typename DataTypeForSize<I2cInput<0>::data_size>::Type Value;
-
- static void Init() {
- // Prescaler is set to a factor of 1.
- Prescaler0::clear();
- Prescaler1::clear();
- TWBR = (F_CPU / frequency - 16) / 2;
-
- I2cEnable::set();
- I2cInterrupt::set();
- I2cAck::set();
-
- state_ = I2C_STATE_READY;
- i2c_handler_ = &Handler;
- }
-
- static void Done() {
- I2cInterrupt::clear();
- I2cEnable::clear();
- I2cAck::clear();
- i2c_handler_ = NULL;
- }
-
- static uint8_t Wait() {
- while (state_ != I2C_STATE_READY) { }
- return error_;
- }
-
- static uint8_t Wait(uint16_t num_cycles) {
- while (state_ != I2C_STATE_READY && num_cycles) {
- --num_cycles;
- }
- if (!num_cycles) {
- error_ = I2C_ERROR_TIMEOUT;
- }
- return error_;
- }
-
- static uint8_t Send(uint8_t address) {
- // The output buffer is empty, no need to do anything.
- if (!Output::readable()) {
- return 0;
- }
-
- // Sorry, data can be sent only when the line is not busy.
- if (state_ != I2C_STATE_READY) {
- return 0;
- }
-
- error_ = I2C_ERROR_NONE;
- state_ = I2C_STATE_RECEIVING;
- slarw_ = (address << 1) | TW_WRITE;
-
- uint8_t size = Output::readable();
- I2cStart::set();
-
- return size;
- }
-
- 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()) {
- requested = Input::writable() - 1;
- }
- // Sorry, data can be requested only when the line is not busy.
- if (state_ != I2C_STATE_READY) {
- return 0;
- }
-
- error_ = I2C_ERROR_NONE;
- state_ = I2C_STATE_RECEIVING;
- slarw_ = (address << 1) | TW_READ;
- received_ = 0;
- requested_ = requested;
- I2cStart::set();
-
- return requested;
- }
-
- // All the read/write operations are done on the buffer, so they do not
- // block.
- static inline void Write(Value v) { Output::Write(v); }
- static inline uint8_t writable() { return Output::writable(); }
- static inline uint8_t NonBlockingWrite(Value v) {
- return Output::NonBlockingWrite(v);
- }
- static inline void Overwrite(Value v) { Output::Overwrite(v); }
- static inline Value Read() { return Input::Read(); }
- static inline uint8_t readable() { return Input::readable(); }
- static inline int16_t NonBlockingRead() { return Input::NonBlockingRead(); }
- static inline Value ImmediateRead() { return Input::ImmediateRead(); }
-
- static inline void FlushInputBuffer() { Input::Flush(); }
- static inline void FlushOutputBuffer() { Output::Flush(); }
-
- private:
- static inline void Continue(uint8_t ack) {
- if (ack) {
- TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
- } else {
- TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
- }
- }
-
- static inline void Stop() {
- I2cStop::set();
- while (I2cStop::value()) { }
- state_ = I2C_STATE_READY;
- }
-
- static inline void Abort() {
- TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
- state_ = I2C_STATE_READY;
- }
-
- static void Handler() {
- switch (TW_STATUS) {
- case TW_START:
- case TW_REP_START:
- TWDR = slarw_;
- Continue(1);
- break;
-
- case TW_MT_DATA_ACK:
- case TW_MT_SLA_ACK:
- if (Output::readable()) {
- TWDR = Output::ImmediateRead();
- Continue(1);
- } else {
- Stop();
- }
- break;
-
- case TW_MT_SLA_NACK:
- case TW_MT_DATA_NACK:
- error_ = TW_STATUS;
- Stop();
- break;
-
- case TW_MT_ARB_LOST:
- error_ = I2C_ERROR_ARBITRATION_LOST;
- Abort();
- break;
-
- case TW_MR_DATA_ACK:
- Input::Overwrite(TWDR);
- ++received_;
- case TW_MR_SLA_ACK:
- if (received_ < requested_) {
- Continue(1);
- } else {
- Continue(0);
- }
- break;
-
- case TW_MR_DATA_NACK:
- Input::Overwrite(TWDR);
- ++received_;
- case TW_MR_SLA_NACK:
- Stop();
- break;
-
- case TW_NO_INFO:
- break;
-
- case TW_BUS_ERROR:
- error_ = I2C_ERROR_BUS_ERROR;
- Stop();
- break;
- }
- }
-
- public:
- typedef RingBuffer<I2cInput<input_buffer_size> > Input;
- typedef RingBuffer<I2cOutput<output_buffer_size> > 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(I2cMaster);
- };
-
- /* static */
- template<uint8_t input_buffer_size, uint8_t output_buffer_size,
- uint32_t frequency>
- volatile uint8_t I2cMaster<input_buffer_size, output_buffer_size,
- frequency>::state_;
-
- /* static */
- template<uint8_t input_buffer_size, uint8_t output_buffer_size,
- uint32_t frequency>
- volatile uint8_t I2cMaster<input_buffer_size, output_buffer_size,
- frequency>::error_;
-
- /* static */
- template<uint8_t input_buffer_size, uint8_t output_buffer_size,
- uint32_t frequency>
- volatile uint8_t I2cMaster<input_buffer_size, output_buffer_size,
- frequency>::slarw_;
-
- /* static */
- template<uint8_t input_buffer_size, uint8_t output_buffer_size,
- uint32_t frequency>
- volatile uint8_t I2cMaster<input_buffer_size, output_buffer_size, frequency>::received_;
-
- /* static */
- template<uint8_t input_buffer_size, uint8_t output_buffer_size,
- uint32_t frequency>
- uint8_t I2cMaster<input_buffer_size, output_buffer_size, frequency>::requested_;
-
- } // namespace avrlib
-
- #endif // AVRLIB_I2C_I2C_H_
|