/*************************************************************************** * Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016 by Terraneo Federico * * * * 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 2 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. * * * * As a special exception, if other files instantiate templates or use * * macros or inline functions from this file, or you compile this file * * and link it with other works to produce a work based on this file, * * this file does not by itself cause the resulting work to be covered * * by the GNU General Public License. However the source code for this * * file must still be made available in accordance with the GNU General * * Public License. This exception does not invalidate any other reasons * * why a work based on this file might be covered by the GNU General * * Public License. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see * ***************************************************************************/ #ifndef SOFTWARE_I2C_H #define SOFTWARE_I2C_H #include "interfaces/gpio.h" #include "interfaces/delays.h" namespace miosix { /** * Software I2C class. * \param SDA SDA gpio pin. Pass a Gpio class * \param SCL SCL gpio pin. Pass a Gpio class * \param timeout for clock stretching, in milliseconds * \param fast false=~100KHz true=~400KHz */ template class SoftwareI2C { public: /** * Initializes the SPI software peripheral */ static void init(); /** * Send a start condition. */ static void sendStart(); /** * Send a repeated start. */ static void sendRepeatedStart(); /** * Send a stop condition */ static void sendStop(); /** * Send a byte to a device. * \param data byte to send * \return true if the device acknowledged the byte */ static bool send(unsigned char data); /** * Receive a byte from a device. Always acknowledges back. * \return the received byte */ static unsigned char recvWithAck(); /** * Receive a byte from a device. Never acknowledges back. * \return the received byte */ static unsigned char recvWithNack(); private: SoftwareI2C();//Disallow creating instances, class is used via typedefs /** * Wait if the slave asserts SCL low. This is called clock stretching. * \return true on success, false on timeout */ static bool waitForClockStretching(); }; template void SoftwareI2C::init() { SDA::high(); SCL::high(); SDA::mode(Mode::OPEN_DRAIN); SCL::mode(Mode::OPEN_DRAIN); } template void SoftwareI2C::sendStart() { SDA::low(); delayUs(fast ? 1 : 3); SCL::low(); delayUs(fast ? 1 : 3); } template void SoftwareI2C::sendRepeatedStart() { SDA::high(); delayUs(fast ? 1 : 3); SCL::high(); delayUs(fast ? 1 : 3); waitForClockStretching(); sendStart(); } template void SoftwareI2C::sendStop() { SDA::low(); delayUs(fast ? 1 : 3); SCL::high(); delayUs(fast ? 1 : 3); waitForClockStretching(); SDA::high(); delayUs(fast ? 1 : 3); } template bool SoftwareI2C::send(unsigned char data) { for(int i=0;i<8;i++) { if(data & 0x80) SDA::high(); else SDA::low(); delayUs(fast ? 1 : 3); SCL::high(); data<<=1; delayUs(fast ? 2 : 5);//Double delay waitForClockStretching(); SCL::low(); delayUs(fast ? 1 : 3); } SDA::high(); delayUs(fast ? 1 : 3); SCL::high(); delayUs(fast ? 1 : 3); bool timeout=waitForClockStretching(); bool result=(SDA::value()==0); delayUs(fast ? 1 : 3); SCL::low(); delayUs(fast ? 1 : 3); return result && timeout; } template unsigned char SoftwareI2C::recvWithAck() { SDA::high(); unsigned char result=0; delayUs(fast ? 1 : 3); for(int i=0;i<8;i++) { result<<=1; if(SDA::value()) result |= 1; delayUs(fast ? 1 : 3); SCL::high(); delayUs(fast ? 2 : 5);//Double delay waitForClockStretching(); SCL::low(); delayUs(fast ? 1 : 3); } SDA::low(); delayUs(fast ? 1 : 3); SCL::high(); delayUs(fast ? 2 : 5);//Double delay waitForClockStretching(); SCL::low(); delayUs(fast ? 1 : 3); return result; } template unsigned char SoftwareI2C::recvWithNack() { SDA::high(); unsigned char result=0; delayUs(fast ? 1 : 3); for(int i=0;i<8;i++) { result<<=1; if(SDA::value()) result |= 1; delayUs(fast ? 1 : 3); SCL::high(); delayUs(fast ? 2 : 5);//Double delay waitForClockStretching(); SCL::low(); delayUs(fast ? 1 : 3); } delayUs(fast ? 1 : 3); SCL::high(); delayUs(fast ? 2 : 5);//Double delay waitForClockStretching(); SCL::low(); delayUs(fast ? 1 : 3); return result; } template bool SoftwareI2C::waitForClockStretching() { if(SCL::value()==1) return true; for(unsigned int i=0;i