From cb05b86d49717ce155db618b9cb9e9cc18ced92f Mon Sep 17 00:00:00 2001 From: Matej Sychra Date: Fri, 26 Oct 2018 17:04:16 +0200 Subject: [PATCH] I2C slave support (originally by bjoham) (#5226) * I2C slave support; resolving conflicts against current master * removed unused argument, updateded to hopefully pass Travis * cleaning up commit as requested by https://github.com/esp8266/Arduino/pull/5162#pullrequestreview-162242359 * cleaning up commit as requested by https://github.com/esp8266/Arduino/pull/5162#pullrequestreview-162242359 * type fix --- cores/esp8266/core_esp8266_si2c.c | 644 +++++++++++++++++++++++++++--- cores/esp8266/twi.h | 21 +- cores/esp8266/twi_util.h | 243 +++++++++++ libraries/Wire/Wire.cpp | 76 ++-- 4 files changed, 894 insertions(+), 90 deletions(-) create mode 100644 cores/esp8266/twi_util.h diff --git a/cores/esp8266/core_esp8266_si2c.c b/cores/esp8266/core_esp8266_si2c.c index 784a546be..72f5ae3ef 100644 --- a/cores/esp8266/core_esp8266_si2c.c +++ b/cores/esp8266/core_esp8266_si2c.c @@ -17,15 +17,89 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified January 2017 by Bjorn Hammarberg (bjoham@esp8266.com) - i2c slave support */ #include "twi.h" #include "pins_arduino.h" #include "wiring_private.h" unsigned int preferred_si2c_clock = 100000; +#include "twi_util.h" + +#include "ets_sys.h" + unsigned char twi_dcount = 18; static unsigned char twi_sda, twi_scl; static uint32_t twi_clockStretchLimit; +static unsigned char twi_addr = 0; + +// modes (private) +#define TWIPM_UNKNOWN 0 +#define TWIPM_IDLE 1 +#define TWIPM_ADDRESSED 2 +#define TWIPM_WAIT 3 + +// states (private) +#define TWIP_UNKNOWN 0 +#define TWIP_IDLE 1 +#define TWIP_START 2 +#define TWIP_SEND_ACK 3 +#define TWIP_WAIT_ACK 4 +#define TWIP_WAIT_STOP 5 +#define TWIP_SLA_W 6 +#define TWIP_SLA_R 7 +#define TWIP_REP_START 8 +#define TWIP_READ 9 +#define TWIP_STOP 10 +#define TWIP_REC_ACK 11 +#define TWIP_READ_ACK 12 +#define TWIP_RWAIT_ACK 13 +#define TWIP_WRITE 14 +#define TWIP_BUS_ERR 15 + +static volatile uint8_t twip_mode = TWIPM_IDLE; +static volatile uint8_t twip_state = TWIP_IDLE; +static volatile uint8_t twip_status = TW_NO_INFO; +static volatile uint8_t bitCount = 0; + +#define TWDR twi_data +static volatile uint8_t twi_data = 0x00; +static volatile uint8_t twi_ack = 0; +static volatile uint8_t twi_ack_rec = 0; +static volatile int twi_timeout_ms = 10; + +#define TWI_READY 0 +#define TWI_MRX 1 +#define TWI_MTX 2 +#define TWI_SRX 3 +#define TWI_STX 4 +static volatile uint8_t twi_state = TWI_READY; +static volatile uint8_t twi_error = 0xFF; + +static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH]; +static volatile uint8_t twi_txBufferIndex; +static volatile uint8_t twi_txBufferLength; + +static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH]; +static volatile uint8_t twi_rxBufferIndex; + +static void (*twi_onSlaveTransmit)(void); +static void (*twi_onSlaveReceive)(uint8_t*, size_t); + +static void onSclChange(void); +static void onSdaChange(void); + +#define EVENTTASK_QUEUE_SIZE 1 +#define EVENTTASK_QUEUE_PRIO 2 + +#define TWI_SIG_RANGE 0x00000100 +#define TWI_SIG_RX (TWI_SIG_RANGE + 0x01) +#define TWI_SIG_TX (TWI_SIG_RANGE + 0x02) + +static ETSEvent eventTaskQueue[EVENTTASK_QUEUE_SIZE]; +static void eventTask(ETSEvent *e); +static ETSTimer timer; +static void onTimer(); #define SDA_LOW() (GPES = (1 << twi_sda)) //Enable SDA (becomes output and since GPO is 0 for the pin, it will pull the line low) #define SDA_HIGH() (GPEC = (1 << twi_sda)) //Disable SDA (becomes input and since it has pullup it will go high) @@ -69,36 +143,51 @@ void twi_setClockStretchLimit(uint32_t limit){ twi_clockStretchLimit = limit * TWI_CLOCK_STRETCH_MULTIPLIER; } -void twi_init(unsigned char sda, unsigned char scl){ +void twi_init(unsigned char sda, unsigned char scl) +{ + // set timer function + ets_timer_setfn(&timer, onTimer, NULL); + + // create event task + ets_task(eventTask, EVENTTASK_QUEUE_PRIO, eventTaskQueue, EVENTTASK_QUEUE_SIZE); + twi_sda = sda; twi_scl = scl; pinMode(twi_sda, INPUT_PULLUP); pinMode(twi_scl, INPUT_PULLUP); twi_setClock(preferred_si2c_clock); twi_setClockStretchLimit(230); // default value is 230 uS + + if (twi_addr != 0) + { + attachInterrupt(scl, onSclChange, CHANGE); + attachInterrupt(sda, onSdaChange, CHANGE); + } } - -void twi_stop(void){ - pinMode(twi_sda, INPUT); - pinMode(twi_scl, INPUT); +void twi_setAddress(uint8_t address) +{ + // set twi slave address (skip over R/W bit) + twi_addr = address << 1; } -static void twi_delay(unsigned char v){ +static void ICACHE_RAM_ATTR twi_delay(unsigned char v){ unsigned int i; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-but-set-variable" unsigned int reg; - for(i=0;i0) { //if SDA low, read the bits slaves have to sent to a max - --clockCount; - twi_read_bit(); - if (SCL_READ()==0) - return I2C_SCL_HELD_LOW_AFTER_READ; //I2C bus error. SCL held low beyond slave clock stretch time +uint8_t twi_status() { + if (SCL_READ()==0) { + return I2C_SCL_HELD_LOW; // SCL held low by another device, no procedure available to recover + } + int clockCount = 20; + while (SDA_READ()==0 && clockCount>0) { // if SDA low, read the bits slaves have to sent to a max + twi_read_bit(); + if (SCL_READ()==0) { + return I2C_SCL_HELD_LOW_AFTER_READ; // I2C bus error. SCL held low beyond slave clock stretch time + } + } + if (SDA_READ()==0) { + return I2C_SDA_HELD_LOW; // I2C bus error. SDA line held low by slave/another_master after n bits. + } + if (!twi_write_start()) { + return I2C_SDA_HELD_LOW_AFTER_INIT; // line busy. SDA again held low by another device. 2nd master? + } else { + return I2C_OK; } - - if (SDA_READ()==0) - return I2C_SDA_HELD_LOW; //I2C bus error. SDA line held low by slave/another_master after n bits. - - if(!twi_write_start()) - return I2C_SDA_HELD_LOW_AFTER_INIT; //line busy. SDA again held low by another device. 2nd master? - - return I2C_OK; //all ok - +} + +uint8_t twi_transmit(const uint8_t* data, uint8_t length) +{ + uint8_t i; + + // ensure data will fit into buffer + if (length > TWI_BUFFER_LENGTH) { + return 1; + } + + // ensure we are currently a slave transmitter + if (twi_state != TWI_STX) { + return 2; + } + + // set length and copy data into tx buffer + twi_txBufferLength = length; + for (i = 0; i < length; ++i) { + twi_txBuffer[i] = data[i]; + } + + return 0; +} + +void twi_attachSlaveRxEvent( void (*function)(uint8_t*, size_t) ) +{ + twi_onSlaveReceive = function; +} + +void twi_attachSlaveTxEvent( void (*function)(void) ) +{ + twi_onSlaveTransmit = function; +} + +void ICACHE_RAM_ATTR twi_reply(uint8_t ack) +{ + // transmit master read ready signal, with or without ack + if (ack) { + //TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); + SCL_HIGH(); // _BV(TWINT) + twi_ack = 1; // _BV(TWEA) + } else { + //TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); + SCL_HIGH(); // _BV(TWINT) + twi_ack = 0; // ~_BV(TWEA) + } +} + +void ICACHE_RAM_ATTR twi_stop(void) +{ + // send stop condition + //TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO); + SCL_HIGH(); // _BV(TWINT) + twi_ack = 1; // _BV(TWEA) + twi_delay(5); // Maybe this should be here + SDA_HIGH(); // _BV(TWSTO) + // update twi state + twi_state = TWI_READY; +} + +void ICACHE_RAM_ATTR twi_releaseBus(void) +{ + // release bus + //TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT); + SCL_HIGH(); // _BV(TWINT) + twi_ack = 1; // _BV(TWEA) + SDA_HIGH(); + + // update twi state + twi_state = TWI_READY; +} + + +void ICACHE_RAM_ATTR twi_onTwipEvent(uint8_t status) +{ + switch(status) { + // Slave Receiver + case TW_SR_SLA_ACK: // addressed, returned ack + case TW_SR_GCALL_ACK: // addressed generally, returned ack + case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack + case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack + // enter slave receiver mode + twi_state = TWI_SRX; + // indicate that rx buffer can be overwritten and ack + twi_rxBufferIndex = 0; + twi_reply(1); + break; + case TW_SR_DATA_ACK: // data received, returned ack + case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack + // if there is still room in the rx buffer + if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){ + // put byte in buffer and ack + twi_rxBuffer[twi_rxBufferIndex++] = TWDR; + twi_reply(1); + }else{ + // otherwise nack + twi_reply(0); + } + break; + case TW_SR_STOP: // stop or repeated start condition received + // put a null char after data if there's room + if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){ + twi_rxBuffer[twi_rxBufferIndex] = '\0'; + } + // callback to user-defined callback over event task to allow for non-RAM-residing code + //twi_rxBufferLock = true; // This may be necessary + ets_post(EVENTTASK_QUEUE_PRIO, TWI_SIG_RX, twi_rxBufferIndex); + + // since we submit rx buffer to "wire" library, we can reset it + twi_rxBufferIndex = 0; + break; + + case TW_SR_DATA_NACK: // data received, returned nack + case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack + // nack back at master + twi_reply(0); + break; + + // Slave Transmitter + case TW_ST_SLA_ACK: // addressed, returned ack + case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack + // enter slave transmitter mode + twi_state = TWI_STX; + // ready the tx buffer index for iteration + twi_txBufferIndex = 0; + // set tx buffer length to be zero, to verify if user changes it + twi_txBufferLength = 0; + // callback to user-defined callback over event task to allow for non-RAM-residing code + // request for txBuffer to be filled and length to be set + // note: user must call twi_transmit(bytes, length) to do this + ets_post(EVENTTASK_QUEUE_PRIO, TWI_SIG_TX, 0); + break; + + case TW_ST_DATA_ACK: // byte sent, ack returned + // copy data to output register + TWDR = twi_txBuffer[twi_txBufferIndex++]; + + bitCount = 8; + bitCount--; + (twi_data & 0x80) ? SDA_HIGH() : SDA_LOW(); + twi_data <<= 1; + + // if there is more to send, ack, otherwise nack + if(twi_txBufferIndex < twi_txBufferLength){ + twi_reply(1); + }else{ + twi_reply(0); + } + break; + case TW_ST_DATA_NACK: // received nack, we are done + case TW_ST_LAST_DATA: // received ack, but we are done already! + // leave slave receiver state + twi_releaseBus(); + break; + + // All + case TW_NO_INFO: // no state information + break; + case TW_BUS_ERROR: // bus error, illegal stop/start + twi_error = TW_BUS_ERROR; + twi_stop(); + break; + } +} + +void ICACHE_RAM_ATTR onTimer() +{ + twi_releaseBus(); + twip_status = TW_BUS_ERROR; + twi_onTwipEvent(twip_status); + twip_mode = TWIPM_WAIT; + twip_state = TWIP_BUS_ERR; +} + +static void eventTask(ETSEvent *e) +{ + + if (e == NULL) { + return; + } + + switch (e->sig) + { + case TWI_SIG_TX: + twi_onSlaveTransmit(); + + // if they didn't change buffer & length, initialize it + if (twi_txBufferLength == 0) { + twi_txBufferLength = 1; + twi_txBuffer[0] = 0x00; + } + + // Initiate transmission + twi_onTwipEvent(TW_ST_DATA_ACK); + + break; + + case TWI_SIG_RX: + // ack future responses and leave slave receiver state + twi_releaseBus(); + twi_onSlaveReceive(twi_rxBuffer, e->par); + break; + } +} + +void ICACHE_RAM_ATTR onSclChange(void) +{ + static uint8_t sda; + static uint8_t scl; + + sda = SDA_READ(); + scl = SCL_READ(); + + twip_status = 0xF8; // reset TWI status + + switch (twip_state) + { + case TWIP_IDLE: + case TWIP_WAIT_STOP: + case TWIP_BUS_ERR: + // ignore + break; + + case TWIP_START: + case TWIP_REP_START: + case TWIP_SLA_W: + case TWIP_READ: + if (!scl) { + // ignore + } else { + bitCount--; + twi_data <<= 1; + twi_data |= sda; + + if (bitCount != 0) { + // continue + } else { + twip_state = TWIP_SEND_ACK; + } + } + break; + + case TWIP_SEND_ACK: + if (scl) { + // ignore + } else { + if (twip_mode == TWIPM_IDLE) { + if ((twi_data & 0xFE) != twi_addr) { + // ignore + } else { + SDA_LOW(); + } + } else { + if (!twi_ack) { + // ignore + } else { + SDA_LOW(); + } + } + twip_state = TWIP_WAIT_ACK; + } + break; + + case TWIP_WAIT_ACK: + if (scl) { + // ignore + } else { + if (twip_mode == TWIPM_IDLE) { + if ((twi_data & 0xFE) != twi_addr) { + SDA_HIGH(); + twip_state = TWIP_WAIT_STOP; + } else { + SCL_LOW(); // clock stretching + SDA_HIGH(); + twip_mode = TWIPM_ADDRESSED; + if (!(twi_data & 0x01)) { + twip_status = TW_SR_SLA_ACK; + twi_onTwipEvent(twip_status); + bitCount = 8; + twip_state = TWIP_SLA_W; + } else { + twip_status = TW_ST_SLA_ACK; + twi_onTwipEvent(twip_status); + twip_state = TWIP_SLA_R; + } + } + } else { + SCL_LOW(); // clock stretching + SDA_HIGH(); + if (!twi_ack) { + twip_status = TW_SR_DATA_NACK; + twi_onTwipEvent(twip_status); + twip_mode = TWIPM_WAIT; + twip_state = TWIP_WAIT_STOP; + } else { + twip_status = TW_SR_DATA_ACK; + twi_onTwipEvent(twip_status); + bitCount = 8; + twip_state = TWIP_READ; + } + } + } + break; + + case TWIP_SLA_R: + case TWIP_WRITE: + if (scl) { + // ignore + } else { + bitCount--; + (twi_data & 0x80) ? SDA_HIGH() : SDA_LOW(); + twi_data <<= 1; + + if (bitCount != 0) { + // continue + } else { + twip_state = TWIP_REC_ACK; + } + } + break; + + case TWIP_REC_ACK: + if (scl) { + // ignore + } else { + SDA_HIGH(); + twip_state = TWIP_READ_ACK; + } + break; + + case TWIP_READ_ACK: + if (!scl) { + // ignore + } else { + twi_ack_rec = !sda; + twip_state = TWIP_RWAIT_ACK; + } + break; + + case TWIP_RWAIT_ACK: + if (scl) { + // ignore + } else { + SCL_LOW(); // clock stretching + if (twi_ack && twi_ack_rec) { + twip_status = TW_ST_DATA_ACK; + twi_onTwipEvent(twip_status); + twip_state = TWIP_WRITE; + } else { + // we have no more data to send and/or the master doesn't want anymore + twip_status = twi_ack_rec ? TW_ST_LAST_DATA : TW_ST_DATA_NACK; + twi_onTwipEvent(twip_status); + twip_mode = TWIPM_WAIT; + twip_state = TWIP_WAIT_STOP; + } + } + break; + + default: + break; + } +} + +void ICACHE_RAM_ATTR onSdaChange(void) +{ + static uint8_t sda; + static uint8_t scl; + sda = SDA_READ(); + scl = SCL_READ(); + + switch (twip_state) + { + case TWIP_IDLE: + if (!scl) { + // DATA - ignore + } else if (sda) { + // STOP - ignore + } else { + // START + bitCount = 8; + twip_state = TWIP_START; + ets_timer_arm_new(&timer, twi_timeout_ms, false, true); // Once, ms + } + break; + + case TWIP_START: + case TWIP_REP_START: + case TWIP_SEND_ACK: + case TWIP_WAIT_ACK: + case TWIP_SLA_R: + case TWIP_REC_ACK: + case TWIP_READ_ACK: + case TWIP_RWAIT_ACK: + case TWIP_WRITE: + if (!scl) { + // DATA - ignore + } else { + // START or STOP + SDA_HIGH(); // Should not be necessary + twip_status = TW_BUS_ERROR; + twi_onTwipEvent(twip_status); + twip_mode = TWIPM_WAIT; + twip_state = TWIP_BUS_ERR; + } + break; + + case TWIP_WAIT_STOP: + case TWIP_BUS_ERR: + if (!scl) { + // DATA - ignore + } else { + if (sda) { + // STOP + SCL_LOW(); // clock stretching + ets_timer_disarm(&timer); + twip_state = TWIP_IDLE; + twip_mode = TWIPM_IDLE; + SCL_HIGH(); + } else { + // START + if (twip_state == TWIP_BUS_ERR) { + // ignore + } else { + bitCount = 8; + twip_state = TWIP_REP_START; + ets_timer_arm_new(&timer, twi_timeout_ms, false, true); // Once, ms + } + } + } + break; + + case TWIP_SLA_W: + case TWIP_READ: + if (!scl) { + // DATA - ignore + } else { + // START or STOP + if (bitCount != 7) { + // inside byte transfer - error + twip_status = TW_BUS_ERROR; + twi_onTwipEvent(twip_status); + twip_mode = TWIPM_WAIT; + twip_state = TWIP_BUS_ERR; + } else { + // during first bit in byte transfer - ok + SCL_LOW(); // clock stretching + twip_status = TW_SR_STOP; + twi_onTwipEvent(twip_status); + if (sda) { + // STOP + ets_timer_disarm(&timer); + twip_state = TWIP_IDLE; + twip_mode = TWIPM_IDLE; + } else { + // START + bitCount = 8; + ets_timer_arm_new(&timer, twi_timeout_ms, false, true); // Once, ms + twip_state = TWIP_REP_START; + twip_mode = TWIPM_IDLE; + } + } + } + break; + + default: + break; + } } diff --git a/cores/esp8266/twi.h b/cores/esp8266/twi.h index 75ac52d56..685221820 100644 --- a/cores/esp8266/twi.h +++ b/cores/esp8266/twi.h @@ -1,9 +1,9 @@ -/* +/* twi.h - Software I2C library for esp8266 Copyright (c) 2015 Hristo Gochkov. All rights reserved. This file is part of the esp8266 core for Arduino environment. - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -17,6 +17,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified January 2017 by Bjorn Hammarberg (bjoham@esp8266.com) - i2c slave support */ #ifndef SI2C_h #define SI2C_h @@ -32,7 +33,12 @@ extern "C" { #define I2C_SDA_HELD_LOW 3 #define I2C_SDA_HELD_LOW_AFTER_INIT 4 +#ifndef TWI_BUFFER_LENGTH +#define TWI_BUFFER_LENGTH 32 +#endif + void twi_init(unsigned char sda, unsigned char scl); +void twi_setAddress(uint8_t); void twi_stop(void); void twi_setClock(unsigned int freq); void twi_setClockStretchLimit(uint32_t limit); @@ -40,8 +46,17 @@ uint8_t twi_writeTo(unsigned char address, unsigned char * buf, unsigned int len uint8_t twi_readFrom(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop); uint8_t twi_status(); +uint8_t twi_transmit(const uint8_t*, uint8_t); + +void twi_attachSlaveRxEvent( void (*)(uint8_t*, size_t) ); +void twi_attachSlaveTxEvent( void (*)(void) ); +void twi_reply(uint8_t); +//void twi_stop(void); +void twi_releaseBus(void); + + #ifdef __cplusplus } #endif -#endif \ No newline at end of file +#endif diff --git a/cores/esp8266/twi_util.h b/cores/esp8266/twi_util.h new file mode 100644 index 000000000..60a92fc96 --- /dev/null +++ b/cores/esp8266/twi_util.h @@ -0,0 +1,243 @@ +/* Copyright (c) 2002, Marek Michalkiewicz + Copyright (c) 2005, 2007 Joerg Wunsch + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Modified January 2017 by Bjorn Hammarberg (bjoham@esp8266.com) - i2c slave support +*/ + +/* $Id$ */ +/* copied from: Id: avr/twi.h,v 1.4 2004/11/01 21:19:54 arcanum Exp */ + +#ifndef _UTIL_TWI_H_ +#define _UTIL_TWI_H_ 1 + +//#include + +/** \file */ +/** \defgroup util_twi : TWI bit mask definitions + \code #include \endcode + + This header file contains bit mask definitions for use with + the AVR TWI interface. +*/ +/** \name TWSR values + + Mnemonics: +
TW_MT_xxx - master transmitter +
TW_MR_xxx - master receiver +
TW_ST_xxx - slave transmitter +
TW_SR_xxx - slave receiver + */ + +/*@{*/ +/* Master */ +/** \ingroup util_twi + \def TW_START + start condition transmitted */ +#define TW_START 0x08 + +/** \ingroup util_twi + \def TW_REP_START + repeated start condition transmitted */ +#define TW_REP_START 0x10 + +/* Master Transmitter */ +/** \ingroup util_twi + \def TW_MT_SLA_ACK + SLA+W transmitted, ACK received */ +#define TW_MT_SLA_ACK 0x18 + +/** \ingroup util_twi + \def TW_MT_SLA_NACK + SLA+W transmitted, NACK received */ +#define TW_MT_SLA_NACK 0x20 + +/** \ingroup util_twi + \def TW_MT_DATA_ACK + data transmitted, ACK received */ +#define TW_MT_DATA_ACK 0x28 + +/** \ingroup util_twi + \def TW_MT_DATA_NACK + data transmitted, NACK received */ +#define TW_MT_DATA_NACK 0x30 + +/** \ingroup util_twi + \def TW_MT_ARB_LOST + arbitration lost in SLA+W or data */ +#define TW_MT_ARB_LOST 0x38 + +/* Master Receiver */ +/** \ingroup util_twi + \def TW_MR_ARB_LOST + arbitration lost in SLA+R or NACK */ +#define TW_MR_ARB_LOST 0x38 + +/** \ingroup util_twi + \def TW_MR_SLA_ACK + SLA+R transmitted, ACK received */ +#define TW_MR_SLA_ACK 0x40 + +/** \ingroup util_twi + \def TW_MR_SLA_NACK + SLA+R transmitted, NACK received */ +#define TW_MR_SLA_NACK 0x48 + +/** \ingroup util_twi + \def TW_MR_DATA_ACK + data received, ACK returned */ +#define TW_MR_DATA_ACK 0x50 + +/** \ingroup util_twi + \def TW_MR_DATA_NACK + data received, NACK returned */ +#define TW_MR_DATA_NACK 0x58 + +/* Slave Transmitter */ +/** \ingroup util_twi + \def TW_ST_SLA_ACK + SLA+R received, ACK returned */ +#define TW_ST_SLA_ACK 0xA8 + +/** \ingroup util_twi + \def TW_ST_ARB_LOST_SLA_ACK + arbitration lost in SLA+RW, SLA+R received, ACK returned */ +#define TW_ST_ARB_LOST_SLA_ACK 0xB0 + +/** \ingroup util_twi + \def TW_ST_DATA_ACK + data transmitted, ACK received */ +#define TW_ST_DATA_ACK 0xB8 + +/** \ingroup util_twi + \def TW_ST_DATA_NACK + data transmitted, NACK received */ +#define TW_ST_DATA_NACK 0xC0 + +/** \ingroup util_twi + \def TW_ST_LAST_DATA + last data byte transmitted, ACK received */ +#define TW_ST_LAST_DATA 0xC8 + +/* Slave Receiver */ +/** \ingroup util_twi + \def TW_SR_SLA_ACK + SLA+W received, ACK returned */ +#define TW_SR_SLA_ACK 0x60 + +/** \ingroup util_twi + \def TW_SR_ARB_LOST_SLA_ACK + arbitration lost in SLA+RW, SLA+W received, ACK returned */ +#define TW_SR_ARB_LOST_SLA_ACK 0x68 + +/** \ingroup util_twi + \def TW_SR_GCALL_ACK + general call received, ACK returned */ +#define TW_SR_GCALL_ACK 0x70 + +/** \ingroup util_twi + \def TW_SR_ARB_LOST_GCALL_ACK + arbitration lost in SLA+RW, general call received, ACK returned */ +#define TW_SR_ARB_LOST_GCALL_ACK 0x78 + +/** \ingroup util_twi + \def TW_SR_DATA_ACK + data received, ACK returned */ +#define TW_SR_DATA_ACK 0x80 + +/** \ingroup util_twi + \def TW_SR_DATA_NACK + data received, NACK returned */ +#define TW_SR_DATA_NACK 0x88 + +/** \ingroup util_twi + \def TW_SR_GCALL_DATA_ACK + general call data received, ACK returned */ +#define TW_SR_GCALL_DATA_ACK 0x90 + +/** \ingroup util_twi + \def TW_SR_GCALL_DATA_NACK + general call data received, NACK returned */ +#define TW_SR_GCALL_DATA_NACK 0x98 + +/** \ingroup util_twi + \def TW_SR_STOP + stop or repeated start condition received while selected */ +#define TW_SR_STOP 0xA0 + +/* Misc */ +/** \ingroup util_twi + \def TW_NO_INFO + no state information available */ +#define TW_NO_INFO 0xF8 + +/** \ingroup util_twi + \def TW_BUS_ERROR + illegal start or stop condition */ +#define TW_BUS_ERROR 0x00 + +#if 0 + +/** + * \ingroup util_twi + * \def TW_STATUS_MASK + * The lower 3 bits of TWSR are reserved on the ATmega163. + * The 2 LSB carry the prescaler bits on the newer ATmegas. + */ +#define TW_STATUS_MASK (_BV(TWS7)|_BV(TWS6)|_BV(TWS5)|_BV(TWS4)|\ + _BV(TWS3)) +/** + * \ingroup util_twi + * \def TW_STATUS + * + * TWSR, masked by TW_STATUS_MASK + */ +#define TW_STATUS (TWSR & TW_STATUS_MASK) +/*@}*/ +#endif + + +/** + * \name R/~W bit in SLA+R/W address field. + */ + +/*@{*/ +/** \ingroup util_twi + \def TW_READ + SLA+R address */ +#define TW_READ 1 + +/** \ingroup util_twi + \def TW_WRITE + SLA+W address */ +#define TW_WRITE 0 +/*@}*/ + +#endif /* _UTIL_TWI_H_ */ diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index b83072914..906b22eed 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -19,6 +19,7 @@ Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts Modified December 2014 by Ivan Grokhotkov (ivan@esp8266.com) - esp8266 support Modified April 2015 by Hrsto Gochkov (ficeto@ficeto.com) - alternative esp8266 support + Modified January 2017 by Bjorn Hammarberg (bjoham@esp8266.com) - i2c slave support */ extern "C" { @@ -78,10 +79,9 @@ void TwoWire::begin(void){ } void TwoWire::begin(uint8_t address){ - (void)address; - // twi_setAddress(address); - // twi_attachSlaveTxEvent(onRequestService); - // twi_attachSlaveRxEvent(onReceiveService); + twi_setAddress(address); + twi_attachSlaveTxEvent(onRequestService); + twi_attachSlaveRxEvent(onReceiveService); begin(); } @@ -160,7 +160,7 @@ size_t TwoWire::write(uint8_t data){ ++txBufferIndex; txBufferLength = txBufferIndex; } else { - // i2c_slave_transmit(&data, 1); + twi_transmit(&data, 1); } return 1; } @@ -171,7 +171,7 @@ size_t TwoWire::write(const uint8_t *data, size_t quantity){ if(!write(data[i])) return i; } }else{ - // i2c_slave_transmit(data, quantity); + twi_transmit(data, quantity); } return quantity; } @@ -214,51 +214,53 @@ void TwoWire::flush(void){ void TwoWire::onReceiveService(uint8_t* inBytes, int numBytes) { - (void)inBytes; - (void)numBytes; // don't bother if user hasn't registered a callback - // if(!user_onReceive){ - // return; - // } + if (!user_onReceive) { + return; + } // // don't bother if rx buffer is in use by a master requestFrom() op // // i know this drops data, but it allows for slight stupidity // // meaning, they may not have read all the master requestFrom() data yet // if(rxBufferIndex < rxBufferLength){ // return; // } - // // copy twi rx buffer into local read buffer - // // this enables new reads to happen in parallel - // for(uint8_t i = 0; i < numBytes; ++i){ - // rxBuffer[i] = inBytes[i]; - // } - // // set rx iterator vars - // rxBufferIndex = 0; - // rxBufferLength = numBytes; - // // alert user program - // user_onReceive(numBytes); + + // copy twi rx buffer into local read buffer + // this enables new reads to happen in parallel + for (uint8_t i = 0; i < numBytes; ++i) { + rxBuffer[i] = inBytes[i]; + } + + // set rx iterator vars + rxBufferIndex = 0; + rxBufferLength = numBytes; + + // alert user program + user_onReceive(numBytes); } -void TwoWire::onRequestService(void){ - // // don't bother if user hasn't registered a callback - // if(!user_onRequest){ - // return; - // } - // // reset tx buffer iterator vars - // // !!! this will kill any pending pre-master sendTo() activity - // txBufferIndex = 0; - // txBufferLength = 0; - // // alert user program - // user_onRequest(); +void TwoWire::onRequestService(void) +{ + // don't bother if user hasn't registered a callback + if (!user_onRequest) { + return; + } + + // reset tx buffer iterator vars + // !!! this will kill any pending pre-master sendTo() activity + txBufferIndex = 0; + txBufferLength = 0; + + // alert user program + user_onRequest(); } -void TwoWire::onReceive( void (*function)(int) ){ - (void)function; - //user_onReceive = function; +void TwoWire::onReceive( void (*function)(int) ) { + user_onReceive = function; } void TwoWire::onRequest( void (*function)(void) ){ - (void)function; - //user_onRequest = function; + user_onRequest = function; } // Preinstantiate Objects //////////////////////////////////////////////////////