ESP8266 Developer Zone The Official ESP8266 Forum 2018-07-22T10:54:43+08:00 https://bbs.espressif.com:443/feed.php?f=49&t=2092 2018-07-22T10:54:43+08:00 2018-07-22T10:54:43+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=22173#p22173 <![CDATA[I2C Slave Mode]]>
Has anyone built code to use a a pic in i2c slave mode that they can share?

Thanks

Peter

Statistics: Posted by MelaniaZew — Sun Jul 22, 2018 10:54 am


]]>
2018-07-05T15:55:28+08:00 2018-07-05T15:55:28+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=20996#p20996 <![CDATA[I2C Slave Mode]]> Statistics: Posted by RobertDot — Thu Jul 05, 2018 3:55 pm


]]>
2018-03-09T08:10:59+08:00 2018-03-09T08:10:59+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=19556#p19556 <![CDATA[Re: I2C Slave Mode]]>
i already read the whole thread and i am on my way to write my own I2C Slave library, mine is interrupt driven,too. I can reach up to 100 kHz, but i want to reach Fast Mode (400 kHz). My biggest problem is, that my Interrupts get triggeres waaayy too slow. I am also reaching 2 µs (with 160 MHz CPU Speed) until i am able to write a GPIO, which is too slow to repsond fast enough and i would prefer running the ESP8266 at 80 MHz again.

So my question is: Is there meanwhile any faster way to run my ISR?

On Github i found the very intersting part from CNLohr, where he uses ASM instructions, but i am not able to understand and adopt these to my needs. I would basically like to use my current ISR and simply jump there from the ASM instructions, that runs as soon as the Interrupt flag is set.
https://github.com/cnlohr/espusb/blob/master/user/usb_asm_1bit.S

I hope my english is understandable, since i am from germany ;)

Statistics: Posted by Driftmonster — Fri Mar 09, 2018 8:10 am


]]>
2017-05-20T22:01:54+08:00 2017-05-20T22:01:54+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=13288#p13288 <![CDATA[Re: I2C Slave Mode]]> Arduino Pro Mini (3.3V, Atmega328 8Mhz) to ESP8266 (nodemcu) using the code by Divingshrek but i can't get any data transfer.

What's wrong?

connections:
ESP8266 <--> GPIO 4 (D2) SCL <==> SCL (A4) <--> PRO-MINI
ESP8266 <--> GPIO 5 (D1) SDA <==> SDA (A5) <--> PRO-MINI
pull-up 5K to VCC


MASTER (Arduino Pro Mini)

Code:

#include <Wire.h>

int x = 0;
void setup() {

 Serial.begin(9600);
  Wire.begin(8); 
}

void loop()
{
     
  Wire.beginTransmission(8);
  Wire.write(x);
  Wire.endTransmission();
  Serial.println(x);
  x++;
  delay(100);
 
}



SLAVE (ESP8266)

Code:


... code above...

unsigned int SCL_pin=4;       // SCL pin GPIO
unsigned int SDA_pin=5;       // SCL pin GPIO


int address = 8;

void receiveEvent(int howMany)
{
  Serial.println("receiveEvent()");
  while (1 < ESP_I2CSlaveavailable())
  {
    char c = ESP_I2CSlaveread();
    Serial.print(c);       
  }
}

void requestEvent()
{
  Serial.println("requestEvent()");
}


void setup()
{
    ESP_I2CSlavebegin(SCL_pin,SDA_pin,address);
    ESP_I2CSlaveonRequest(requestEvent);
    ESP_I2CSlaveonReceive(receiveEvent);
    Serial.begin(9600);
}

void loop()
{
  delay(100);
}

Statistics: Posted by biccius — Sat May 20, 2017 10:01 pm


]]>
2017-02-13T14:33:26+08:00 2017-02-13T14:33:26+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=11308#p11308 <![CDATA[Re: I2C Slave Mode]]> http://www.esp8266.com/viewtopic.php?f= ... d30b106aae

Statistics: Posted by Guest — Mon Feb 13, 2017 2:33 pm


]]>
2017-02-06T09:21:15+08:00 2017-02-06T09:21:15+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=11239#p11239 <![CDATA[Re: I2C Slave Mode]]> Still have a few bits and pieces to test so consider it a work in progress. My intention was to turn it into a library at some point hence the long public method names beginning with "ESP_I2CSlave". Looking at this forum it would appear I'll need to change the GPIO reads/writes into ASM for speed...but the code is prob too long for an ISR anyway.

Any constructive comments welcome...and any hints how to join the forum too :D

Cheers
Ian


Code:

/* I2C Slave
 * 

 */

// States
// ======
#define IDL 0x01        // IDL - Idle state
#define SLA 0x02        // SLA - Receiving SLA from master
#define ACK 0x04        // ACK - Sending ACK tomaster
#define NAK 0x08        // NAK - Sending NACK to master
#define RAK 0x10        // RAK - Receiving ACK/NACK from master
#define RCD 0x20        // RCD - Receiving data from master
#define SND 0x40        // SND - Sending data to master

// Flags
// =====
#define STOP 0x01       // STOP - STOP signal detected
#define STRT 0x02       // STRT - START signal detected
#define SLAW 0x04       // SLAW - SLA+W matched our slave address
#define SLAR 0x08       // SLAR - SLA+R matched our slave address
#define BFUL 0x10       // BFUL - I2C buffer full
#define BEMP 0x20       // BEMP - I2C buffer empty

// Pin Defs
unsigned int SCL_pin=4;       // SCL pin GPIO
unsigned int SDA_pin=5;       // SCL pin GPIO

// macros to avoid using digital write and read (speed up as everything is interrupt driven)
#define SCL_LOW GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS,1<< SCL_pin)
#define SCL_HIGH GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS,1<< SCL_pin)
#define SDA_LOW GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS,1<< SDA_pin)
#define SDA_HIGH GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS,1<< SDA_pin)
#define READ_SCL GPIO_REG_READ(GPIO_IN_ADDRESS)&(1<<SCL_pin)
#define READ_SDA GPIO_REG_READ(GPIO_IN_ADDRESS)&(1<<SDA_pin)

// buffer defs
#define BUFF_ERR 0xffffffff       // error code to return if trying to read from an empty buffer or write to full buffer
#define I2CBUFFLEN 32            // buffer size
#define BUFF_READ 0x01            // read buffer item
#define BUFF_WRITE 0x02           // write to buffer
#define BUFF_AVAIL 0x03           // how many items in the buffer
#define BUFF_FLUSH 0x04           // flush the buffer

// misc defs
#define FUNC_RST 1                // used to reset static vars in send/receive functions
#define FUNC_NOT_RST 0            // opposite of FUNC_RST

// global vars (native 32 bit and unsigned for speed)
volatile unsigned int Flags=STOP, State=IDL;
void (*user_onRequest)(void);
void (*user_onReceive)(int);
volatile unsigned int _I2CAddress=0x3f;


void setup() {
}

void loop() {
}

////////////////////////////////////////////////////////////////////////
//
// PRIVATE METHODS
//
////////////////////////////////////////////////////////////////////////

unsigned int PRIV_I2CBuffer(unsigned int _action, unsigned int &_val)
{
  volatile static unsigned int _BuffHead=0, _BuffTail=0, _Buffer[I2CBUFFLEN];

  switch (_action) {
    case BUFF_READ:
      if (Flags & BEMP) return false;
      _val = _Buffer[_BuffTail];
      _BuffTail++;
      if (_BuffTail >= I2CBUFFLEN) _BuffTail = 0;
      if (_BuffTail == _BuffHead) Flags |= BEMP;
      else Flags &= ~BEMP;
      Flags &= ~BFUL;
      break;
    case BUFF_WRITE:
      if (Flags & BFUL) return false;
      _Buffer[_BuffHead] = _val;
      _BuffHead++;
      if (_BuffHead >= I2CBUFFLEN) _BuffHead = 0;
      if (_BuffHead == _BuffTail) Flags |= BFUL;
      else Flags &= ~BFUL;
      Flags &= ~BEMP;
      break;
    case BUFF_AVAIL:
       if (Flags & BFUL) _val = I2CBUFFLEN;
       else _val = (_BuffHead >= _BuffTail)?_BuffHead - _BuffTail:I2CBUFFLEN - _BuffTail + _BuffHead;
      break; 
    case BUFF_FLUSH:
      _BuffHead = 0;
      _BuffTail = 0;
      Flags |= BEMP;
      Flags &= ~BFUL;
      break;
  }
  return true;
}

void onReceiveService(int numBytes)
{
  // don't bother if user hasn't registered a callback
  if(!user_onReceive) return;
  // alert user program
  user_onReceive(numBytes);
}

void onRequestService(void){
  // // don't bother if user hasn't registered a callback
  if(!user_onRequest) return;
  // alert user program
  user_onRequest();
}

void PRIV_RAKhandler()
{
  // nice and easy if we get ACK send more data otherwise stop
  if (READ_SDA) PRIV_STOPhandler();
  else State = SND;
}

void PRIV_NAKhandler()
{
  // nice and simple if we NACK the next response should
  // be stop or start both of which will reset the state
  SDA_HIGH;
}

void PRIV_ACKhandler()
{
  volatile static unsigned int _firstpass = 1;
  if (_firstpass) {
    SDA_LOW;
    _firstpass = 0;
  }
  else {
    SDA_HIGH;
    _firstpass = 1;
    if (Flags & SLAW) State = RCD;
    else {
      State = SND;
      PRIV_SNDhandler(FUNC_NOT_RST);
    }
  }
}

void PRIV_RCDhandler(unsigned int _reset)
{
  volatile static unsigned int _bitcount=0, _i2cbyte=0, _firstpass=1;
  unsigned int _val;
  if (_reset) {
    _firstpass = 1;
    _bitcount = 0;
    _i2cbyte = 0;
    return;
  }
  if (_firstpass) {
    _firstpass = 0;
    _bitcount = 0;
    _i2cbyte = 0;
  }
  _i2cbyte <<= 1;
  if (READ_SDA) _i2cbyte |= 1;
  _bitcount++;
  if (_bitcount > 7) {
    _firstpass = 1;
    _val = _i2cbyte;
    if (!PRIV_I2CBuffer(BUFF_WRITE, _val)) State = NAK;
    else {
      if (Flags & BFUL) State = NAK;
      else State = ACK;
    }
  }
}

void PRIV_SLAhandler(unsigned int _reset)
{
  volatile static unsigned int _bitcount=0, _i2cbyte, _firstpass = 1;
    if (_reset) {
    _firstpass = 1;
    _bitcount = 0;
    _i2cbyte = 0;
    return;
  }
  if (_firstpass) {
    _firstpass =0;
    _bitcount = 0;
    _i2cbyte = 0;
  }
  _i2cbyte <<= 1;
  if (READ_SDA) _i2cbyte |= 1;
  _bitcount++;
  if (_bitcount > 7) {
    _firstpass = 1;
     if ((_i2cbyte >> 1) == _I2CAddress) {
      if (_i2cbyte & 0x01) {
        Flags |= SLAR;
        onRequestService();
        if (Flags & BEMP) State = NAK;
        else State = ACK;
      }
      else Flags|= SLAW;
      if (Flags & BFUL) State = NAK;
      else State = ACK;
    }
  }
}

void PRIV_SNDhandler(unsigned int _reset)
{
  volatile static unsigned int _bitcount=0, _i2cbyte, _releaseSDA=0, _firstpass = 1;
  unsigned int _val;
    if (_reset) {
    _firstpass = 1;
    _bitcount = 0;
    return;
  }
  if (_releaseSDA) {
    SDA_HIGH;
    State = RAK;
    _releaseSDA = 0;
    _firstpass = 1;
    return;
  }
  if (!_firstpass) {
    PRIV_I2CBuffer(BUFF_READ, _val);
    _i2cbyte = _val;
    _bitcount = 0;
    _firstpass = 0;
  }
  if (_i2cbyte & 1) SDA_HIGH;
  else SDA_LOW;
  _i2cbyte >>= 1;
  _bitcount++;
  if (_bitcount > 7) {
     _releaseSDA = 1;
  }
}

void PRIV_STRThandler()
{
  unsigned int _retval, _val;
  if (Flags & SLAW) {
    PRIV_I2CBuffer(BUFF_AVAIL, _val);
    if (_val) onReceiveService(_val);
  }
  if (Flags & SLAR) PRIV_I2CBuffer(BUFF_FLUSH, _val);
  Flags &= 0xfffffff0;
  Flags |= STRT;
  State = SLA;
}

void PRIV_STOPhandler()
{
  unsigned int _retval, _val;
  if (Flags & SLAW) {
    PRIV_I2CBuffer(BUFF_AVAIL, _val);
    if (_val) onReceiveService(_val);
    PRIV_RCDhandler(FUNC_RST);
    PRIV_SNDhandler(FUNC_RST);
    PRIV_SLAhandler(FUNC_RST);
  }
  if (Flags & SLAR) PRIV_I2CBuffer(BUFF_FLUSH, _val);
  Flags &= 0xfffffff0;
  Flags |= STOP;
  State = IDL;
}

void PRIV_SCLChangeISR()
{
  if (State == IDL) return;

  if (READ_SCL) {            //read on rising edge
    switch (State) {
      case RAK:
        // receiving ACK from master
        PRIV_RAKhandler();
        break;
      case RCD:
        // receiving data from master
        PRIV_RCDhandler(FUNC_NOT_RST);
        break;
      case SLA:
        // receiving SLA+R/W from master
        PRIV_SLAhandler(FUNC_NOT_RST);
        break;
    }
  }
  else {                    // Sending data to master
    SCL_LOW;                // Apply Clock Stretch
    switch (State) {
      case ACK:
        // send ack to master
        PRIV_ACKhandler();
        break;
      case NAK:
        // send nack to master
        PRIV_NAKhandler();
        break;
      case SND:
        // send data to master
        PRIV_SNDhandler(FUNC_NOT_RST);
        break;
    }
    SCL_HIGH;              // Release Clock Stretch
  }
}

void PRIV_SDAChangeISR()
{
  if (READ_SCL) {
    if (READ_SDA) PRIV_STOPhandler();
    else PRIV_STRThandler();
  }
}

////////////////////////////////////////////////////////////////////////////////
//
//   PUBLIC METHODS
//
///////////////////////////////////////////////////////////////////////////////

unsigned int ESP_I2CSlavewrite(unsigned int _val)
{
  unsigned int _retval;

  if (Flags & BFUL) return BUFF_ERR;
  _retval = PRIV_I2CBuffer(BUFF_WRITE, _val);
  return (_retval)?0:BUFF_ERR;
}

unsigned int ESP_I2CSlaveread()
{
  unsigned int _retval, _val;
  if (Flags & BEMP) return BUFF_ERR;
  _retval = PRIV_I2CBuffer(BUFF_READ, _val);
  return (_retval)?_val:BUFF_ERR;
}

void ESP_I2CSlaveflush()
{
  unsigned int _retval,  _val;
  PRIV_I2CBuffer(BUFF_FLUSH, _val);
}

unsigned int ESP_I2CSlaveavailable()
{
  unsigned int _retval, _val;
  _retval = PRIV_I2CBuffer(BUFF_AVAIL, _val);
  return _val;
}

void ESP_I2CSlaveonReceive( void (*function)(int) ){
  user_onReceive = function;
}

void ESP_I2CSlaveonRequest( void (*function)(void) ){
  user_onRequest = function;
}

void ESP_I2CSlavebegin(int scl_pin, int sda_pin, byte address)
{
  _I2CAddress = address & 0x7f;
  SCL_pin = scl_pin & 0xf;
  SDA_pin = sda_pin & 0xf;
  pinMode(SCL_pin, OUTPUT_OPEN_DRAIN);
  pinMode(SDA_pin, OUTPUT_OPEN_DRAIN);
  SCL_HIGH;
  SDA_HIGH;
  attachInterrupt(digitalPinToInterrupt(SCL_pin), PRIV_SCLChangeISR, CHANGE);
  attachInterrupt(digitalPinToInterrupt(SDA_pin), PRIV_SDAChangeISR, CHANGE);
}

Statistics: Posted by Guest — Mon Feb 06, 2017 9:21 am


]]>
2017-01-05T16:36:26+08:00 2017-01-05T16:36:26+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=11031#p11031 <![CDATA[Re: I2C Slave Mode]]>
yes that's true. But this is depending on the used SDK if this IRAM_ATTR is necessary. As far as I know the IRAM_ATTR is the default for NONOS SDK. if you use RTOS SDK it must be added.

Statistics: Posted by dkaufmann — Thu Jan 05, 2017 4:36 pm


]]>
2016-11-30T19:54:46+08:00 2016-11-30T19:54:46+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10734#p10734 <![CDATA[Re: I2C Slave Mode]]>
What I noticed about your code is you were using a full blown C function call to request the pin value instead of the ASM call that the examples were using; that "CALL" and "RTN" will add time.

I'm not sure it will help, but I've read that also using the IRAM_ATTR flag is typically considered good practice for fast/short ISRs.

Statistics: Posted by Guest — Wed Nov 30, 2016 7:54 pm


]]>
2016-11-29T20:54:09+08:00 2016-11-29T20:54:09+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10728#p10728 <![CDATA[Re: I2C Slave Mode]]>
okay. I still have no idea how to enter the interrupt routine faster. If I enter the ISR() on falling edge SDA and measure the SCL line, SCL has already changed to the next state. It is just not a real "interrupt" for me if it takes that long. DMA is no option for a simple I2C slave.

best regards
Dominik

Statistics: Posted by dkaufmann — Tue Nov 29, 2016 8:54 pm


]]>
2016-11-26T05:31:50+08:00 2016-11-26T05:31:50+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10674#p10674 <![CDATA[Re: I2C Slave Mode]]>
dkaufmann wrote:
Hi Rudi,

i am very interested in these ASM instruction to run the I2C faster. I'd like to run in high speed mode or at least >100kHz.

is this line to read the inputs? how exactly?
_l8ui a0, a11, GPIO_OFFSET_INPUT


hi dominik

ah you use the code from CNLohr :D
yes, asked you, do you know about this in prev postings..


the thing is not bad, but the 17 cycles is too much too.
CNLohr on esp8266.com wrote:
I could respond to interrupt requests in .28 us!


then you know about my state to this too

rudi on esp8266.com wrote:
with 80MHz it is 12.5 ns
with 160 MHz it is 6.5 ns ( had measuring on time this by 6.2 ns - see picture )

fastest:
instructions: 4

80 MHz : 12.5ns * 4 = 50 ns
160 MHz : 6.5 ns * 4 = 26 ns ( 6.25 * 4 = 25 ns )

optimated
instructions: 8

80 MHz : 12.5ns * 8 = 100 ns
160 MHz : 6.5 ns * 8 = 52 ns ( 6.25 * 8 = 50 ns )

and does a little be more better as your cool thing with .28 us ( 280 ns )

..

state to high speed usb:
-------------------------------

without DMA and Core 160MHz
without drops - tricks
and with asm blocks

custom Instructions: 6.25ns
bit flow for 1 bit data read:
---------------------------

ISR Fired:
fired 6.25
nop 6.25
clear 6.25
nop 6.25
attach 6.25
nop 6.25


Clock:
low 6.25
nop 6.25
high 6.25
nop 6.25



Data read:
get pin 6.25
nop 6.25
shift pin 6.25
nop 6.25

-----------------------------

14 * 6.5 87.5 ns ( full clock )

87.5 ns * 8 = 700 ns

8 Bit = 0.700 us

1 ms = 1000 * 8 / 0.700 = 11428 Bit / ms

1 s 11428 * 1000 = 11 428 000 Bit/s

11 428 000 Bit/s = 11.428 MBit/s




example:
you can work helped with
"for while (..)"

Code:

static inline unsigned get_ccount(void)
{
        unsigned r;
        asm volatile ("rsr %0, ccount" : "=r"(r));
        return r;
}




backgrounds:

-> http://naberius.de/2015/05/14/esp8266-g ... rformance/
-> viewtopic.php?t=979


the interrupt delay or the GPIO read is too slow. even without debug prints. how do you read/set the GPIO in assembler? in your code I see you use the "normal" GPIO_INPUT_GET function. this is much too slow even with 160MHz. Do you agree?


Yes - there are to much instructions


Can you show me how you do it faster?


using DMA and ticks on 6.5 ns

a good example is done by CNLohr with Broadcast TV and driving WS2812

i am just in time on esp32 CAN theme
perhabs will come back again to this i2c slave theme..

best wishes
rudi ;-)

Statistics: Posted by rudi — Sat Nov 26, 2016 5:31 am


]]>
2016-11-22T15:44:28+08:00 2016-11-22T15:44:28+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10610#p10610 <![CDATA[Re: I2C Slave Mode]]>
i am very interested in these ASM instruction to run the I2C faster. I'd like to run in high speed mode or at least >100kHz.

is this line to read the inputs? how exactly?
_l8ui a0, a11, GPIO_OFFSET_INPUT

"if i delete the debug prints i can go on to 400 KHz with 160 MHz CPU":
the interrupt delay or the GPIO read is too slow. even without debug prints. how do you read/set the GPIO in assembler? in your code I see you use the "normal" GPIO_INPUT_GET function. this is much too slow even with 160MHz. Do you agree? Can you show me how you do it faster?

Thank you

Dominik

Statistics: Posted by dkaufmann — Tue Nov 22, 2016 3:44 pm


]]>
2016-11-21T16:47:12+08:00 2016-11-21T16:47:12+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10598#p10598 <![CDATA[Re: I2C Slave Mode]]>
I also shared some code here about the I2C slave code. But I still have some troubles with interrupt latencies. Sometimes I detect wrong START messages because the delay of measuring the inputs is too bad. Any ideas how to avoid this problem? By the way, I measure the inputs right at the beginning of the ISR.

any help is appreciated. here is my code of the ISR:

Code:

// ---------------- Interrupt Service Routine ISR -----------
void gpio_intrHandlerCB (void)
{
   // variables
   uint8 temp_SDA = GPIO_INPUT_GET(I2C_SDA_PIN);
   uint8 temp_SCK = GPIO_INPUT_GET(I2C_SCK_PIN);

   i2c_state_t i2c_state = REC_ADDR;
   uint8 bit_cntr = 0;   // counts clock edges to read data
   uint8 byte_cntr = 0;   // counts databytes
   uint8 i2c_address = 0;      // 8 Bit without R/W
   uint8 i2c_rec_data = 0;      // 8 Bit receive data
   uint8 i2c_rwn = 0;         // 0: write, 1: read
   uint8 i2c_stop = 0;         // stop signal
   uint16 i2c_TO_cntr = 0;

   // read GPIO status register to check which line had an interrupt
   uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);

   // handle SDA Pin Interrupt
   //-----------------------------------------------------------------------
   if ( (gpio_status & BIT(I2C_SDA_PIN)) && (temp_SCK))   // if SCL = high : START
   {
      //GPIO_OUTPUT_SET(DEBUG_PIN4, 1);
      //GPIO_OUTPUT_SET(DEBUG_PIN4, 0);

      // disable interrupt on SDA pin only when START or STOP received
      gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SDA_PIN), GPIO_PIN_INTR_DISABLE);
      if (temp_SDA)   //    if SDA rising: STOP
      {
         i2c_stop = 1;
         //enable START interrupt again
         gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SDA_PIN), GPIO_PIN_INTR_ANYEDGE);
      }
      else    // START
      {
         while (!i2c_stop)
         {
            switch (i2c_state)
            {
               case REC_ADDR:
                  //on next pos edge on SCL: read addr
                  for (bit_cntr = 0; bit_cntr<7; bit_cntr++)
                  {
                     // read 7 bit address
                     // wait for falling edge
                     i2c_TO_cntr = 0;
                     while (GPIO_INPUT_GET(I2C_SCK_PIN) || i2c_TO_cntr >= I2C_TIMEOUT) {
                        i2c_TO_cntr++;
                        os_delay_us(1);
                     }
                     // wait for rising edge
                     i2c_TO_cntr = 0;
                     while (!GPIO_INPUT_GET(I2C_SCK_PIN) || i2c_TO_cntr >= I2C_TIMEOUT)   {
                        i2c_TO_cntr++;
                        os_delay_us(1);
                     }

                     i2c_address |= (((uint8) (GPIO_INPUT_GET(I2C_SDA_PIN))) << (6-bit_cntr));
                  }
                  if (i2c_address == I2C_SLAVEADDR)
                  {
                     i2c_state = READ_RW;
                  }
                  else
                  {
                     i2c_state = REC_ADDR;   // init start state
                     i2c_stop = 1;         // leave ISR
                     //enable START interrupt again
                     gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SDA_PIN), GPIO_PIN_INTR_ANYEDGE);
                  };
                  break;

               case READ_RW:                        // read the rwn bit (0 = MASTER write, 1 = MASTER read)
                  // wait for falling edge
                  i2c_TO_cntr = 0;
                  while (GPIO_INPUT_GET(I2C_SCK_PIN) || i2c_TO_cntr >= I2C_TIMEOUT) {
                     i2c_TO_cntr++;
                     os_delay_us(1);
                  }
                  // wait for rising edge
                  i2c_TO_cntr = 0;
                  while (!GPIO_INPUT_GET(I2C_SCK_PIN) || i2c_TO_cntr >= I2C_TIMEOUT)   {
                     i2c_TO_cntr++;
                     os_delay_us(1);
                  }

                  i2c_rwn =  GPIO_INPUT_GET(I2C_SDA_PIN);   // read databit
                  byte_cntr=0;                     // reset databyte counter
                  i2c_state = SEND_ACK;
                  break;

               case SEND_ACK:
                  // wait for falling edge
                  i2c_TO_cntr = 0;
                  while (GPIO_INPUT_GET(I2C_SCK_PIN) || i2c_TO_cntr >= I2C_TIMEOUT) {
                     i2c_TO_cntr++;
                     os_delay_us(1);
                  }
                  GPIO_OUTPUT_SET(I2C_SDA_PIN, 0);   // ACK = SDA low
                  GPIO_OUTPUT_SET(I2C_INT_PIN, 1);   // release interrupt line after receiving correct address

                  if (i2c_rwn)                   // SEND_DATA?
                  {
                     i2c_state = SEND_DATA;         // = MASTER READ DATA
                  }
                  else                        // Receive data
                  {
                     i2c_state = REC_DATA;         // = MASTER WRITE DATA
                  }
                  break;

               case SEND_DATA:   // C5
                  // Slave sends Data to master: MSB first
                  for (bit_cntr = 0; bit_cntr < 8; bit_cntr++)
                  {
                     // wait for rising edge
                     i2c_TO_cntr = 0;
                     while (!GPIO_INPUT_GET(I2C_SCK_PIN) || i2c_TO_cntr >= I2C_TIMEOUT)   {
                        i2c_TO_cntr++;
                        os_delay_us(1);
                     }

                     // wait for falling edge
                     i2c_TO_cntr = 0;
                     while (GPIO_INPUT_GET(I2C_SCK_PIN) || i2c_TO_cntr >= I2C_TIMEOUT) {
                        i2c_TO_cntr++;
                        os_delay_us(1);
                     }
                     // write data on falling edge
                     GPIO_OUTPUT_SET(I2C_SDA_PIN, (i2c_TxBuf[byte_cntr]&(0x80 >> bit_cntr)?1:0)); // shift out data: if true = 1 else = 0
                  }

                  // check ACK
                  // wait for rising edge
                  i2c_TO_cntr = 0;
                  while (!GPIO_INPUT_GET(I2C_SCK_PIN) || i2c_TO_cntr >= I2C_TIMEOUT)   {
                     i2c_TO_cntr++;
                     os_delay_us(1);
                  }

                  // wait for falling edge
                  i2c_TO_cntr = 0;
                  while (GPIO_INPUT_GET(I2C_SCK_PIN) || i2c_TO_cntr >= I2C_TIMEOUT) {
                     i2c_TO_cntr++;
                     os_delay_us(1);
                  }
                  GPIO_OUTPUT_SET(I2C_SDA_PIN,1);         // release SDA Pin
                  // wait for rising edge
                  i2c_TO_cntr = 0;
                  while (!GPIO_INPUT_GET(I2C_SCK_PIN) || i2c_TO_cntr >= I2C_TIMEOUT)   {
                     i2c_TO_cntr++;
                     os_delay_us(1);
                  }

                  if (!GPIO_INPUT_GET(I2C_SDA_PIN))      // ACK:
                  {
                     byte_cntr++;
                  }
                  else                           // NACK: END SEND DATA
                  {
                     byte_cntr=0;                  // reset byte counter
                     i2c_stop = 1;
                     //enable START interrupt again to read master's answer
                     gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SDA_PIN), GPIO_PIN_INTR_ANYEDGE);
                  };
                  break;

               case REC_DATA:   // C4 -> MASTER WRITE
                  //Slave reads 8 databit from master
                  // wait for rising edge
                  i2c_TO_cntr = 0;
                  while (!GPIO_INPUT_GET(I2C_SCK_PIN) || i2c_TO_cntr >= I2C_TIMEOUT)   {
                     i2c_TO_cntr++;
                     os_delay_us(1);
                  }

                  for (bit_cntr = 0; bit_cntr < 8; bit_cntr++)
                  {
                     temp_SDA = GPIO_INPUT_GET(I2C_SDA_PIN);
                     i2c_TO_cntr = 0;
                     while (GPIO_INPUT_GET(I2C_SCK_PIN) || i2c_TO_cntr >= I2C_TIMEOUT)   // wait for falling edge (end ack read)
                     {
                        i2c_TO_cntr++;   // increase time out counter
                        os_delay_us(1);
                        // during high: check if data did not change -> STOP
                        if (temp_SDA != GPIO_INPUT_GET(I2C_SDA_PIN)){
                           i2c_stop = 1;
                           i2c_RxCount = byte_cntr+1;
                           i2c_rec_done = 1;
                           break;
                        }

                     }
                     if (i2c_stop)   // if STOP occured, leave loop
                        break;

                     // release SDA pin on falling SCL edge:
                     GPIO_OUTPUT_SET(I2C_SDA_PIN, 1);      // release I2C_SDA_PIN
                     // wait for rising edge
                     i2c_TO_cntr = 0;
                     while (!GPIO_INPUT_GET(I2C_SCK_PIN) || i2c_TO_cntr >= I2C_TIMEOUT)   {
                        i2c_TO_cntr++;
                        os_delay_us(1);
                     }

                     i2c_rec_data |= (((uint8) (GPIO_INPUT_GET(I2C_SDA_PIN))) << (7-bit_cntr));
                  }
                  i2c_RxBuf[byte_cntr]=i2c_rec_data;   // store received databyte
                  byte_cntr++;                  // increase databyte counter
                  i2c_rec_data = 0;                // reset received databyte
                  i2c_state = SEND_ACK;
                  break;

               default:
                  i2c_state = REC_ADDR;
            }   // end switch
         }   // end while
         // state machine end: stop occured:
         i2c_stop = 0;         // reset stop flag
         i2c_state = REC_ADDR;   // init state
         i2c_rec_data = 0;
         i2c_address = 0;
      };
      //clear interrupt status for GPIOx (SDA)
      GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & BIT(I2C_SDA_PIN));
   }

Statistics: Posted by dkaufmann — Mon Nov 21, 2016 4:47 pm


]]>
2016-11-05T05:06:59+08:00 2016-11-05T05:06:59+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10410#p10410 <![CDATA[Re: I2C Slave Mode]]> Statistics: Posted by martinayotte — Sat Nov 05, 2016 5:06 am


]]>
2016-11-03T16:58:25+08:00 2016-11-03T16:58:25+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10401#p10401 <![CDATA[Re: I2C Slave Mode]]>
martinayotte wrote:
I don't understand, I see an email from you on Oct 19, but it has been flag "deleted by you", so I can't read it.
You've said that you will share your code, but had never shared it.
Now, you are saying "i do nobody hims homework" ... :-(
With such attitude, probably yourself won't get any help from other when you will need it ...


I have to say Martin, I don't think rudi will ever need much help from others - he is a legend, and a prolific poster here and in the other ESP forums. He has my respect big time!!

I took the info here and was able to get code running pretty quickly - I don't think there's anything missing from the explanations that I have found so far.

Big thanks rudi!!

Statistics: Posted by Guest — Thu Nov 03, 2016 4:58 pm


]]>
2016-11-01T01:58:10+08:00 2016-11-01T01:58:10+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10367#p10367 <![CDATA[Re: I2C Slave Mode]]> You've said that you will share your code, but had never shared it.
Now, you are saying "i do nobody hims homework" ... :-(
With such attitude, probably yourself won't get any help from other when you will need it ...

Statistics: Posted by martinayotte — Tue Nov 01, 2016 1:58 am


]]>
2016-10-28T22:24:05+08:00 2016-10-28T22:24:05+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10334#p10334 <![CDATA[Re: I2C Slave Mode]]>
martinayotte wrote:
@Rudi, Few weeks ago, you were saying that you will share this I2C Slave code, and said that it should be after Chinese holiday ...

edit:
yes i did this say.

read the code here - this is my share -

but me was say too, that i get devKitC,
but i have not get. so my thinked demo project i can not build.
this would be a gift on second birthday to bbs

i had pm you the things what happens, but you have not read more weeks.

at just in time - i get no devkitc from adafruit, olimex or espressif.
so - i cant build my thinked demo with all espressif boards
and i do not start again theme in esp8266 that was done in last year.. sry

and btw:
here now a part of detailed things in the thread that you not read on technicals docu.
- pin setup
- opendrain
- isr theme
- start, stop, addr, data

where is the problem now to build an own project with this as base?

if there is no understand for the code here,
there cant be an understand of the demo project too,
so please.

copy & paste is not good for teach.
so my steps are teached bit by bit with each instruction to do INTR on SDA, SCL
in v 0.1 for Start, Stop, Read cmd , Write cmd, SDA, SCL, Addr Match, ISR

your steps now: Data ( its the same principal ) and a idea what you do with I2C.
example send cmd by I2C Master to I2C Slave for AT Commands based on I2C.

i do nobody hims homework.
nobody pay me the time or gives me DevKitC to do the things...
and i am not work at espressif - i am customer like you.

sry

i do not more coment this theme here martin.
there stands all info to create a own I2C Slave.c and Header and demo project.

rudi

Statistics: Posted by rudi — Fri Oct 28, 2016 10:24 pm


]]>
2016-10-28T22:05:08+08:00 2016-10-28T22:05:08+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10332#p10332 <![CDATA[Re: I2C Slave Mode]]> Statistics: Posted by martinayotte — Fri Oct 28, 2016 10:05 pm


]]>
2016-10-28T00:58:57+08:00 2016-10-28T00:58:57+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10310#p10310 <![CDATA[Re: I2C Slave Mode]]>
martinayotte wrote:
rudi wrote:
martinayotte wrote:Rudi, did you got chance to upload your I2CSlave code somewhere ?


edit:

the missunderstand was cleaned now.
lets see what happens.

rudi


I don't understand your above comment ...
My question was : do you have any github where we can look at your ESP8266 I2C Slave code ?


;-)
this was other theme - i have wait for the devkitc from john and want make a esp family i2c example,
but had not get .. this theme is cleared - not to do with you, martin, sry for the
confusion.

need we the github code any more?
we have here opendrain setup, ack , start, stop, isr and other as example.

best wishes
rudi ;-)

Statistics: Posted by rudi — Fri Oct 28, 2016 12:58 am


]]>
2016-10-28T00:21:18+08:00 2016-10-28T00:21:18+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10309#p10309 <![CDATA[Re: I2C Slave Mode]]>
rudi wrote:
martinayotte wrote:Rudi, did you got chance to upload your I2CSlave code somewhere ?


edit:

the missunderstand was cleaned now.
lets see what happens.

rudi


I don't understand your above comment ...
My question was : do you have any github where we can look at your ESP8266 I2C Slave code ?

Statistics: Posted by martinayotte — Fri Oct 28, 2016 12:21 am


]]>
2016-10-27T01:22:19+08:00 2016-10-27T01:22:19+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10281#p10281 <![CDATA[Re: I2C Slave Mode]]>
dkaufmann wrote:
Hi Rudi,

okay I see. thanks for this one. Gonna try it out.

best regards
D.


one hint i get perhabs for you important too :)

- CCOUNT SR it's 32 bits
- overflow of CCOUNT detectable? -- no, there's nothing that can tell you about an overflow. No interrupt, no overflow bit.
- is there an interrupt for CCOUNT? Yes, there is a number of CCOMPARE SRs. You can write a value there and then an IRQ will be raised once CCOUNT reaches that value.

For more details please see xtensa ISA book (e.g. http://0x04.net/~mwk/doc/xtensa.pdf ), 4.4.6 Timer interrupt option.

have phun
best wishes

rudi ;-)

Statistics: Posted by rudi — Thu Oct 27, 2016 1:22 am


]]>
2016-10-26T22:46:14+08:00 2016-10-26T22:46:14+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10280#p10280 <![CDATA[Re: I2C Slave Mode]]>
okay I see. thanks for this one. Gonna try it out.

best regards
D.

Statistics: Posted by dkaufmann — Wed Oct 26, 2016 10:46 pm


]]>
2016-10-26T22:01:04+08:00 2016-10-26T22:01:04+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10279#p10279 <![CDATA[Re: I2C Slave Mode]]>
dkaufmann wrote:
Hi Rudi,

I'm surprised that you manage to reach 400kHz with your method. When I implemented the interrupt driven version (with SCL and SDA interrupt), it already stopped to work with 100kHz. The interrupt latency was too high. From 100kHz I started to miss the next edge (from rising to the next falling edge on SCL). Do you have an idea why? (my ESP8266 runs with 80MHz which should be fast enough).

Thanks for the help.

best regards
D.


Yes you are right!
my fastest with this v 0.1 methode is 120..KHz
in the others i use asm instruction because the gpio switch is very slow, and ( i think it was the ESP-04 for best result ) if you take difference ESP modules there can be time difference in switching ( capazitors on gpio=bad idea )

do you know

Code:


_l8ui a0, a11, GPIO_OFFSET_INPUT



or
example precise timing..

Code:


static inline unsigned get_ccount(void)
{
        unsigned r;
        asm volatile ("rsr %0, ccount" : "=r"(r));
        return r;
}



with 80MHz it is 12.5 ns
with 160 MHz it is 6.5 ns ( had measuring on time this by 6.2 ns - see picture in this thread i started long time ago)
just in time i maked a pause on this, in the past last year in spring, i wrote things here:
http://www.mikrocontroller.net/topic/358654

fastest:
instructions: 4

80 MHz : 12.5ns * 4 = 50 ns
160 MHz : 6.5 ns * 4 = 26 ns ( 6.25 * 4 = 25 ns )

optimated
instructions: 8

80 MHz : 12.5ns * 8 = 100 ns
160 MHz : 6.5 ns * 8 = 52 ns ( 6.25 * 8 = 50 ns )

and does a little be more better as usually doings on int ( 280 ns )

perhabs go on again on this , there are many question open to this theme,
i had paused because there was no (germany) community interrest on this last year -
my favority doing in this was usb host :)

Charles works on USB with asm instruction too. here is my statement to his doing.
http://www.esp8266.com/viewtopic.php?f= ... 275#p52980

the theme is DMA, too. but this is an other theme.

with the "trick" you can do a timing on this:

Code:

..
t1 = system_get_time();     // API get time
// start
tick1 = get_ccount();       // Spezial Register CCOUNT Abruf und Übertrag / call and get
while (mtick < 100 )
{
// empty
mtick++;
}

t2 = system_get_time();     // API get time
tickdiff = tick2 - tick1;   // Wieviel Ticks verbraucht / how many ticks used..
..


and then you can handle fine.
..but you must work with 160 MHz..

best wishes
rudi ;-)

Statistics: Posted by rudi — Wed Oct 26, 2016 10:01 pm


]]>
2016-10-26T19:55:50+08:00 2016-10-26T19:55:50+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10278#p10278 <![CDATA[Re: I2C Slave Mode]]>
I'm surprised that you manage to reach 400kHz with your method. When I implemented the interrupt driven version (with SCL and SDA interrupt), it already stopped to work with 100kHz. The interrupt latency was too high. From 100kHz I started to miss the next edge (from rising to the next falling edge on SCL). Do you have an idea why? (my ESP8266 runs with 80MHz which should be fast enough).

Thanks for the help.

best regards
D.

Statistics: Posted by dkaufmann — Wed Oct 26, 2016 7:55 pm


]]>
2016-10-26T17:55:33+08:00 2016-10-26T17:55:33+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10277#p10277 <![CDATA[Re: I2C Slave Mode]]>
dkaufmann wrote:
Hi Rudi,

a lot of comments :). Thanks for your review. In which case it happens, that a stop comes from the master after sending the address? I will add a general interrupt on the STOP signal, too. So I can stop the whole communication independent of the state.
Thus, I also had a version with SCL and SDA interrupt (without while loops). The problem was, that it was too slow for 100 kHz. This is why I changed my code to only Start interrupt and then stay in the ISR.
Sorry, implementation of a master is not planned at the moment. (not needed in my project, and no time at the moment.) If I do later, I'll post it.

have a nice one!


;-)
if the master break example the communication - you must "agieren" and "handle" this.
so my "teaching steps" was bit for bit in cases...

yes you right, your doing is faster in this part, my doing with int on sda + scl makes the slave slower but exactly.
and the prints do make isr slower too. so this is going well do 25 Khz with debug prints..
if i delete the debug prints i can go on to 400 KHz with 160 MHz CPU

this was the v 0.1 ;-) so never push your last version you work on :)
this only for teach / follow steps for understanding... ;-) ( Opendrain, ACK, Start, Stop ..)

the homework must be done by the user.
i like your version.
i like the mine too :)

best wishes
rudi ;-)

Statistics: Posted by rudi — Wed Oct 26, 2016 5:55 pm


]]>
2016-10-26T17:15:12+08:00 2016-10-26T17:15:12+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10276#p10276 <![CDATA[Re: I2C Slave Mode]]>
a lot of comments :). Thanks for your review. In which case it happens, that a stop comes from the master after sending the address? I will add a general interrupt on the STOP signal, too. So I can stop the whole communication independent of the state.
Thus, I also had a version with SCL and SDA interrupt (without while loops). The problem was, that it was too slow for 100 kHz. This is why I changed my code to only Start interrupt and then stay in the ISR.
Sorry, implementation of a master is not planned at the moment. (not needed in my project, and no time at the moment.) If I do later, I'll post it.

have a nice one!

Statistics: Posted by dkaufmann — Wed Oct 26, 2016 5:15 pm


]]>
2016-10-26T05:05:30+08:00 2016-10-26T05:05:30+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10268#p10268 <![CDATA[Re: I2C Slave Mode]]>
dkaufmann wrote:
plus open drain config of the SDA/SCL pins
..



yep da kemma zam :) geht doch.
why ? ( ~PERIPHS_IO_MUX_MTMS_U and PERIPHS_IO_MUX_MTDI_U ./. PERIPHS_IO_MUX_MTCK_U )

Code:

// PIN SETUP..
// (c) dkaufmann, 25.10.2016, Zürich, Switzerland

//SDA on GPIO12
#define I2C_SDA_PIN 12
#define I2C_SDA_MUX PERIPHS_IO_MUX_MTDI_U
#define I2C_SDA_FUNC FUNC_GPIO12

//SCK on GPIO14
#define I2C_SCK_PIN 14
#define I2C_SCK_MUX PERIPHS_IO_MUX_MTMS_U
#define I2C_SCK_FUNC FUNC_GPIO14



Code:

// rudi
// header
#define I2C_SLAVE_SDA_MUX PERIPHS_IO_MUX_MTMS_U  //CONFIG THE GPIO MUX HERE
#define I2C_SLAVE_SDL_MUX PERIPHS_IO_MUX_MTCK_U  //CONFIG THE GPIO MUX HERE
#define I2C_SLAVE_SDA_GPIO 12 //CONFIG THE GPIO NUMBER HERE
#define I2C_SLAVE_SDL_GPIO 14 //CONFIG THE GPIO NUMBER HERE


// c.file
//SET_PERI_REG_MASK(I2C_SLAVE_SDA_MUX, 0x30);
WRITE_PERI_REG(I2C_SLAVE_SDA_MUX, (READ_PERI_REG(I2C_SLAVE_SDA_MUX)&0xfffffe0f)|0x30);

//SET_PERI_REG_MASK(I2C_SLAVE_SDL_MUX, 0x30);
WRITE_PERI_REG(I2C_SLAVE_SDL_MUX, (READ_PERI_REG(I2C_SLAVE_SDL_MUX)&0xfffffe0f)|0x30);




OpenDrain


Code:


// (c) dkaufmann, 25.10.2016, Zürich, Switzerland
//Set SDA as open drain
    GPIO_REG_WRITE(
        GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_SDA_PIN)),
        GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_SDA_PIN))) |
        GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE)
    );
    GPIO_REG_WRITE(GPIO_ENABLE_ADDRESS, GPIO_REG_READ(GPIO_ENABLE_ADDRESS) | (1 << I2C_SDA_PIN));
    GPIO_OUTPUT_SET(I2C_SDA_PIN, 1);

    //Set SCK as open drain
    GPIO_REG_WRITE(
        GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_SCK_PIN)),
        GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_SCK_PIN))) |
        GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE)
    );
    GPIO_REG_WRITE(GPIO_ENABLE_ADDRESS, GPIO_REG_READ(GPIO_ENABLE_ADDRESS) | (1 << I2C_SCK_PIN));
    GPIO_OUTPUT_SET(I2C_SCK_PIN, 1);



Code:

   
   // rudi
   GPIO_REG_WRITE(
      GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO)),
      GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO)))|
      GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE));//open drain;
   
   GPIO_REG_WRITE(GPIO_ENABLE_ADDRESS,GPIO_REG_READ(GPIO_ENABLE_ADDRESS)|(1<<I2C_SLAVE_SDA_GPIO) );
    // set..step later
   
   
    GPIO_REG_WRITE(
      GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO)),
      GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO)))|
      GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE));//open drain;
   
   GPIO_REG_WRITE(GPIO_ENABLE_ADDRESS,GPIO_REG_READ(GPIO_ENABLE_ADDRESS)|(1<<I2C_SLAVE_SDL_GPIO) );
   // set..step later
   
   
   
   // set pins high
   // header
   #define I2C_SLAVE_GPIO_SET(pin)  \
        gpio_output_set(1<<pin,0,1<<pin,0)

    #define I2C_SLAVE_GPIO_CLR(pin) \
        gpio_output_set(0,1<<pin,1<<pin,0)
      
   #define I2C_SLAVE_GPIO_OUT(pin,val) \
        if(val) I2C_SLAVE_GPIO_SET(pin);\
        else I2C_SLAVE_GPIO_CLR(pin)
   
   
   // c.file
   I2C_SLAVE_GPIO_SET(I2C_SLAVE_SDA_GPIO);
    I2C_SLAVE_GPIO_SET(I2C_SLAVE_SDL_GPIO);




tests..

Code:

// rudi

//for test
void read_gpio_sta()
{

    ets_printf("gpio in reg   :  0x%08x\n",GPIO_REG_READ(GPIO_IN_ADDRESS));
    ets_printf("gpio out reg  :  0x%08x\n\n",GPIO_REG_READ(GPIO_OUT_ADDRESS));

    ets_printf("sda reg set   :  0x%08x \n",GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO))) );
    ets_printf("sdl reg set   :  0x%08x \n",GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO))) );

}




the isr comes asap i must clean my code,
it is longer :) your ISR looks not bad - but i think you get a problem, if the master send the addr and then a stop,
you have no routine for this; i will check your isr ,, have seen the reply and have done reply fast.


@apollo0226
where is your code?
work in this and i am sure you will do it too,
the important things is, opendrain and ack doing.
now you have this in two difference ways.
because i do step by step ( bit by bit ) in the debug code..
i allways check in this debug version 0.1 each int fire for start, stop..
and i do this difference to switzerland.
switzerland use a "while on the scl"
i use interrupt on SDA and SCL
this is the difference.

now go on guy!


edit ..isr...

Code:


// **************************************************
// (c) eMbeddedHome rudi ;-)  07.May 2016
// **************************************************
//**    v.0.1
//**    ISR for SDA and SCL
//**    ===================
//**    simple protokol
//**   Master send Start
//**    Master send addr - Slave check it with ACK/NACK
//**   if a write command
//**    Master send data - Slave receive it
//**   Master send Stop
// **************************************************


void i2c_slave_isr() {
   
   u8 scl=0;
   u8 sda=0;
   u32 gpio_status = 0;

   // welchen read/write Status?
   static u8 write_read=0;

   // LAST
   // 0 = mehr daten
   // 1 = waren letze daten
   static nLast = 1; // vorgabe mal explizit

   gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
   ETS_GPIO_INTR_DISABLE() ;
   
   scl=GPIO_INPUT_GET(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO));
   sda=GPIO_INPUT_GET(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO));
   
   // ets_printf("ISR fired..\n\0");
   // ets_printf("%lu fired\n\0", gpio_status);


if(( (gpio_status>>I2C_SLAVE_SDA_GPIO)& BIT0)&&scl) //SDA trigger and sdl high   
{
           if( sda )// sda posedge , stop
            {
            ets_printf("stop..\n\0");
         //    if (startphase == 0) {
         //*   I2C_SLAVE_STATE = I2C_SLAVE_STATE_IDLE;
            
                I2C_SLAVE_NEXTSTEP = nsWAITING_IDLE;
                gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO),GPIO_PIN_INTR_NEGEDGE);
                gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO),GPIO_PIN_INTR_DISABLE);
            ; // return;
         }
 
   
         else //sda negedge,start
   
         {
            ets_printf("start..\n\0");
             // if (!startphase) { startphase = 1;
             // I2C_SLAVE_STATE =  I2C_SLAVE_STATE_RX_ADDR;
            // allways at start:
            I2C_SLAVE_NEXTSTEP = nsADDR1;
            I2C_SLAVE_PING = ADI;

                gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO),GPIO_PIN_INTR_DISABLE);
                gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO),GPIO_PIN_INTR_POSEDGE);
            ; // return;
         }

     
}   

     else if ( (gpio_status>>I2C_SLAVE_SDL_GPIO)& BIT0) //SDL trigger
            
         {
       // TO DO:
       // nur antworten wenn ein START vorhanden war
       // ansonsten der SLAVE alles mitprotokolliert
      
   // .... see next part...
   // switch (I2C_SLAVE_NEXTSTEP) {
   




Code:


switch (I2C_SLAVE_NEXTSTEP) {

   case nsADDR1   :    rx_addr = 0;
                  startphase = 3;
                  rx_addr = (rx_addr << 1 ) | sda ;
                  I2C_SLAVE_NEXTSTEP = nsADDR2;
                  gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO),GPIO_PIN_INTR_DISABLE);
                  gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO),GPIO_PIN_INTR_POSEDGE);
                  break; // return; 

   case nsADDR2   :    rx_addr = (rx_addr << 1 ) | sda ;
                  I2C_SLAVE_NEXTSTEP = nsADDR3;
                  gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO),GPIO_PIN_INTR_DISABLE);
                  gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO),GPIO_PIN_INTR_POSEDGE);
                  break; // return; 
   
   case nsADDR3   :    rx_addr = (rx_addr << 1 ) | sda ;
                  I2C_SLAVE_NEXTSTEP = nsADDR4;
                  gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO),GPIO_PIN_INTR_DISABLE);
                  gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO),GPIO_PIN_INTR_POSEDGE);
                  break; // return;
   
   case nsADDR4   :    rx_addr = (rx_addr << 1 ) | sda ;
                  I2C_SLAVE_NEXTSTEP = nsADDR5;
                  gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO),GPIO_PIN_INTR_DISABLE);
                  gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO),GPIO_PIN_INTR_POSEDGE);
                  break; // return;
   
   case nsADDR5   :    rx_addr = (rx_addr << 1 ) | sda ;
                  I2C_SLAVE_NEXTSTEP = nsADDR6;
                  gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO),GPIO_PIN_INTR_DISABLE);
                  gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO),GPIO_PIN_INTR_POSEDGE);
                  break; // return;
   
   case nsADDR6   :    rx_addr = (rx_addr << 1 ) | sda ;
                  I2C_SLAVE_NEXTSTEP = nsADDR7;
                  gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO),GPIO_PIN_INTR_DISABLE);
                  gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO),GPIO_PIN_INTR_POSEDGE);
                  break; // return;
   
   case nsADDR7   :    rx_addr = (rx_addr << 1 ) | sda ;
                  I2C_SLAVE_NEXTSTEP = nsADDR8;
                  gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO),GPIO_PIN_INTR_DISABLE);
                  gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO),GPIO_PIN_INTR_POSEDGE);
                  break; // return;
   
   case nsADDR8   :    rx_addr = (rx_addr << 1 ) | sda ;
                  // addr match
                  if ( !1 || ((rx_daten & 0xfe) == I2C_SLAVE_ADDRESS)) // write & read adress is checked here
                  
                  { // addr missmatch
                     ets_printf("rx_addr 0x%2x not match \n\0", rx_addr);
                     gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO),GPIO_PIN_INTR_NEGEDGE); // important thing
                     gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO),GPIO_PIN_INTR_DISABLE);
                     goto missmatch;
                     break;
                     }
                  else
                  {
                     ets_printf("wAdmatch:0x%2x\n\0", rx_addr);
                     I2C_SLAVE_GPIO_OUT(12,0); // send ACK low
                      rx_addr = 0;
                     // Neue VAR write_read
                     // I2C_SLAVE_COMMAND = WRITE;
                     // write_read = 1;
                     I2C_SLAVE_NEXTSTEP = nsADDR9;
                     CALLED = ad8;
                     gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO),GPIO_PIN_INTR_DISABLE);
                     gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO),GPIO_PIN_INTR_NEGEDGE); // important thing
                     }
                     break; // return;
   
   case nsADDR9   :   if(!CALLED==ad8) ets_printf("false route from %d \n\0", CALLED);
                  // GO I2C_SLAVE_GPIO_OUT(12,0);
                  
                   // check read or write
                   if ( 1 & rx_addr) // its a read from Slave by the Master
                   {
                     I2C_SLAVE_COMMAND = READ;
                     I2C_SLAVE_NEXTSTEP = nsADDR1; // bzw IDLE
                     gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO),GPIO_PIN_INTR_DISABLE);
                     gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO),GPIO_PIN_INTR_POSEDGE);
                  
                  }
                   else // its a write from Master to Slave
                   {
                     ACK1;
                     I2C_SLAVE_COMMAND = WRITE;
                     I2C_SLAVE_NEXTSTEP = nsDATA1;
                     gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO),GPIO_PIN_INTR_ANYEDGE); // important
                     gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO),GPIO_PIN_INTR_POSEDGE); // important
                  }
                  break;
                  
   ...
   ...
   





Code:

 ...
 ...
 
 case nsIDLE      :   // DO NOTHING
                  ets_printf("nsIDLE\n\0");
                  break;
   
   case nsWAITING_IDLE : // nothing sollte stop werden
                  ets_printf("%d: nsWaiting_IDLE \n\0", CALLED);
                     break;
   
   
   default         :   ets_printf("default\n\0");


     }

   }

 
missmatch:

     GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status);
     ETS_GPIO_INTR_ENABLE() ;


}

// *******************************************
// last edit 7 May 2016
//
// *******************************************







i use example this in my i2c_slave.h file:
so you know my next cases what follows


Code:


// rudi
typedef enum{
   nsSTART,
   nsSTOP,
   nsADDR1,
   nsADDR2,
   nsADDR3,
   nsADDR4,
   nsADDR5,
   nsADDR6,
   nsADDR7,
   nsADDR8,
   nsADDR9,
   nsADDR9A,
   nsADDR10,
   nsDATA1,
   nsDATA2,
   nsDATA3,
   nsDATA4,
   nsDATA5,
   nsDATA6,
   nsDATA7,
   nsDATA8,
   nsDATA9,
   nsDATA9A,
   nsDATA10,
   nsWDATA1,
   nsWDATA2,
   nsWDATA3,
   nsWDATA4,
   nsWDATA5,
   nsWDATA6,
   nsWDATA7,
   nsWDATA8,
   nsWDATA9,
   nsWDATA10,
   nsACK_A,
   nsACK_B,
   nsNACK_A,
   nsNACK_B,
   nsLAST_ADDR,
   nsLAST_DATA,
   nsFALSE_TRIGGER,
   nsWAITING_IDLE,
   nsCHECK_ADDRESS_MATCH,
   nsIDLE
}I2C_SLAVE_STEPS;

typedef enum {
   WRITE,
   READ,
   IDLE
} I2C_SLAVE_COMMANDS;

// rudi
// simple
#define I2C_START_DEDECTED 1
#define I2C_STOP_DEDECTED  2
#define I2C_ADDR_MISMATCH 3
#define I2C_SLAVE_ADDRESS  0x30
#define SINGLE 1 // in this code, one Byte transfer -> means SendStart SendAddr, SendByte, SendStop





@dkaufmann
do you have a master code too?
can you create and select for master 10Khz example? ( not faster because debug prints in the isr )
and for address example 0x30
master read one byte ( Last = 1 )
i build the slave and i give you then the value of read counter back
this means that we do the things like this:

while( ( counter <=255) ) {
// addr.
master -> uart("count: %d\n", (uint16*) counter )
master -> start
master -> send adr + read
slave <- ack
master -> uart("addr match\n")
master -> stop
master -> delay(10µs)
// data.
master -> start
master -> dummy byte
slave <- counter_byte
master -> Last=1
slave <- counter++
master -> stop
}


@apollo0226
now you have all.
- opendrain
- ack
- steps in a switch like you thinked
- check for start
- check for stop
- debug prints
- INTR on SDA and SCL
- INTR only SDA with a while on SCL
..

now your homework will be do weekend ( or later )
an i2c slave demo ;-)



i am sure you will do it..
and make a video! :)

go on :)

@martin
Have kept my word
hope this helps
and named me!
"rudi ;-)" is enough
fun

happy second birthday bbs.espressif.com


best wishes
rudi ;-)

Statistics: Posted by rudi — Wed Oct 26, 2016 5:05 am


]]>
2016-10-25T17:04:13+08:00 2016-10-25T17:04:13+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10264#p10264 <![CDATA[Re: I2C Slave Mode]]>

Code:

//Set SDA as open drain
    GPIO_REG_WRITE(
        GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_SDA_PIN)),
        GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_SDA_PIN))) |
        GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE)
    );
    GPIO_REG_WRITE(GPIO_ENABLE_ADDRESS, GPIO_REG_READ(GPIO_ENABLE_ADDRESS) | (1 << I2C_SDA_PIN));
    GPIO_OUTPUT_SET(I2C_SDA_PIN, 1);

    //Set SCK as open drain
    GPIO_REG_WRITE(
        GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_SCK_PIN)),
        GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(I2C_SCK_PIN))) |
        GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE)
    );
    GPIO_REG_WRITE(GPIO_ENABLE_ADDRESS, GPIO_REG_READ(GPIO_ENABLE_ADDRESS) | (1 << I2C_SCK_PIN));
    GPIO_OUTPUT_SET(I2C_SCK_PIN, 1);
   

Statistics: Posted by dkaufmann — Tue Oct 25, 2016 5:04 pm


]]>
2016-10-25T16:07:09+08:00 2016-10-25T16:07:09+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10263#p10263 <![CDATA[Re: I2C Slave Mode]]>

Code:

//SDA on GPIO12
#define I2C_SDA_PIN 12
#define I2C_SDA_MUX PERIPHS_IO_MUX_MTDI_U
#define I2C_SDA_FUNC FUNC_GPIO12

//SCK on GPIO14
#define I2C_SCK_PIN 14
#define I2C_SCK_MUX PERIPHS_IO_MUX_MTMS_U
#define I2C_SCK_FUNC FUNC_GPIO14

Statistics: Posted by dkaufmann — Tue Oct 25, 2016 4:07 pm


]]>
2016-10-25T15:58:41+08:00 2016-10-25T15:58:41+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10262#p10262 <![CDATA[Re: I2C Slave Mode]]>
I managed to run the I2C Slave but it is not finished yet (not safe). I need to add timeouts and comm error exceptions and stuff like this.

here is my code to init the interrupt

Code:

    ETS_GPIO_INTR_ATTACH(gpio_intrHandlerCB, NULL);                            //Register the interrupt function
    ETS_GPIO_INTR_ENABLE() ;                                                    //Enable the GPIO interrupt in general
    // enable interrupt on SDA negative edge to detect start
    gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SDA_PIN), GPIO_PIN_INTR_NEGEDGE);


here is my ISR:

Code:


// (c) dkaufmann, 25.10.2016, Zürich, Switzerland

// ---------------- Interrupt Service Routine ISR -----------
void gpio_intrHandlerCB (void)
{
   // variables
   // define I2C-SLAVE states
   typedef enum {    REC_ADDR,
               READ_RW,
               SEND_ACK,
               REC_DATA,
               SEND_DATA
   } i2c_state_t;

   i2c_state_t i2c_state = REC_ADDR;
   uint8 bit_cntr = 0;   // counts clock edges to read data
   uint8 byte_cntr = 0;   // counts databytes
   uint8 i2c_address = 0;      // 8 Bit without R/W
   uint8 i2c_rec_data = 0;      // 8 Bit receive data
   uint8 i2c_rwn = 0;         // 0: write, 1: read
   uint8 i2c_stop = 0;         // stop signal

   uint8 temp = 0;

   // read GPIO status register to check which line had an interrupt
   uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);

   // handle SDA Pin Interrupt
   //-----------------------------------------------------------------------
   if (gpio_status & BIT(I2C_SDA_PIN))   // if (neg) edge SDA interrupt
   {
      if (GPIO_INPUT_GET(I2C_SCK_PIN))   //    if SCK high: start
      {
         // disable interrupt on SDA pin only when START received
         gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SDA_PIN), GPIO_PIN_INTR_DISABLE);

         while (!i2c_stop)
         {
            switch (i2c_state)
            {
               case REC_ADDR:
                  //on next pos edge on SCL: read addr
                  for (bit_cntr = 0; bit_cntr<7; bit_cntr++)
                  {
                     //read 7 bit address
                     while (GPIO_INPUT_GET(I2C_SCK_PIN));   // wait for falling edge
                     while (!GPIO_INPUT_GET(I2C_SCK_PIN));   // wait for rising edge
                     i2c_address |= (((uint8) (GPIO_INPUT_GET(I2C_SDA_PIN))) << (6-bit_cntr));
                  }
                  if (i2c_address == I2C_SLAVEADDR)
                  {
                     i2c_state = READ_RW;
                  }
                  else
                  {
                     i2c_state = REC_ADDR;   // init start state
                     i2c_stop = 1;         // leave ISR
                     //enable START interrupt again
                     gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SDA_PIN), GPIO_PIN_INTR_NEGEDGE);
                  };
                  break;

               case READ_RW:                        // read the rwn bit (0 = MASTER write, 1 = MASTER read)
                  while (GPIO_INPUT_GET(I2C_SCK_PIN));   // wait for falling edge
                  while (!GPIO_INPUT_GET(I2C_SCK_PIN));   // wait for rising edge
                  i2c_rwn =  GPIO_INPUT_GET(I2C_SDA_PIN);   // read databit
                  byte_cntr=0;                     // reset databyte counter
                  i2c_state = SEND_ACK;
                  break;

               case SEND_ACK:
                  while (GPIO_INPUT_GET(I2C_SCK_PIN));// wait for falling edge
                  GPIO_OUTPUT_SET(I2C_SDA_PIN, 0);   // ACK = SDA low

                  if (i2c_rwn)                   // SEND_DATA?
                  {
                     i2c_state = SEND_DATA;         // = MASTER READ DATA
                  }
                  else                        // Receive data
                  {
                     i2c_state = REC_DATA;         // = MASTER WRITE DATA
                  }
                  break;

               case SEND_DATA:   // C5
                  // Slave sends Data to master: MSB first
                  for (bit_cntr = 0; bit_cntr < 8; bit_cntr++)
                  {
                     while (!GPIO_INPUT_GET(I2C_SCK_PIN));   // wait for rising edge
                     while (GPIO_INPUT_GET(I2C_SCK_PIN));   // wait for falling edge
                     // write data on falling edge
                     GPIO_OUTPUT_SET(I2C_SDA_PIN, (i2c_TxBuf[byte_cntr]&(0x80 >> bit_cntr)?1:0)); // shift out data: if true = 1 else = 0
                  }

                  // check ACK
                  while (!GPIO_INPUT_GET(I2C_SCK_PIN));   // wait for rising edge   - Master reads
                  while (GPIO_INPUT_GET(I2C_SCK_PIN));   // wait for falling edge - Master ends read
                  GPIO_OUTPUT_SET(I2C_SDA_PIN,1);         // release SDA Pin
                  while (!GPIO_INPUT_GET(I2C_SCK_PIN));   // wait for rising edge
                  if (!GPIO_INPUT_GET(I2C_SDA_PIN))      // ACK:
                  {
                     byte_cntr++;
                  }
                  else                           // NACK: END SEND DATA
                  {
                     byte_cntr=0;                  // reset byte counter
                     i2c_stop = 1;
                     //enable START interrupt again to read master's answer
                     gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SDA_PIN), GPIO_PIN_INTR_NEGEDGE);
                  };
                  break;

               case REC_DATA:   // C4 -> MASTER WRITE
                  //Slave reads 8 databit from master
                  while (!GPIO_INPUT_GET(I2C_SCK_PIN));   // wait for rising edge (ACK read)

                  for (bit_cntr = 0; bit_cntr < 8; bit_cntr++)
                  {
                     temp = GPIO_INPUT_GET(I2C_SDA_PIN);
                     while (GPIO_INPUT_GET(I2C_SCK_PIN))   // wait for falling edge (end ack read)
                     {
                        // during high: check if data did not change -> STOP
                        if (temp != GPIO_INPUT_GET(I2C_SDA_PIN)){
                           i2c_stop = 1;
                           i2c_RxCount = byte_cntr+1;
                           i2c_rec_done = 1;
                           break;
                        }

                     }
                     if (i2c_stop)   // if STOP occured, leave loop
                        break;

                     // release SDA pin on falling SCL edge:
                     GPIO_OUTPUT_SET(I2C_SDA_PIN, 1);      // release I2C_SDA_PIN
                     while (!GPIO_INPUT_GET(I2C_SCK_PIN));   // wait for rising edge
                     i2c_rec_data |= (((uint8) (GPIO_INPUT_GET(I2C_SDA_PIN))) << (7-bit_cntr));
                  }
                  i2c_RxBuf[byte_cntr]=i2c_rec_data;   // store received databyte
                  byte_cntr++;                  // increase databyte counter
                  i2c_rec_data = 0;                // reset received databyte
                  i2c_state = SEND_ACK;
                  break;

               default:
                  i2c_state = REC_ADDR;
            }   // end switch
         }   // end while
         // state machine end: stop occured:
         i2c_stop = 0;         // reset stop flag
         i2c_state = REC_ADDR;   // init state
         i2c_rec_data = 0;
         i2c_address = 0;
      };
        //clear interrupt status for GPIOx (SDA)
        GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & BIT(I2C_SDA_PIN));
   }

}


it works for me for 100 kHz I2C. Please let me know if you are happy with this solution.

Statistics: Posted by dkaufmann — Tue Oct 25, 2016 3:58 pm


]]>
2016-10-25T11:01:38+08:00 2016-10-25T11:01:38+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10258#p10258 <![CDATA[Re: I2C Slave Mode]]> SCL -> D1 -> GPIO5
SDA -> D2 -> GPIO4

twi_sda = sda;
twi_scl = scl;
pinMode(twi_sda, INPUT_PULLUP);
pinMode(twi_scl, INPUT_PULLUP);
twi_setClock(100000);
twi_setClockStretchLimit(230); // default value is 230 uS

This seems not correct. I will try to change them to

twi_sda = sda;
twi_scl = scl;
pinMode(twi_sda, OUTPUT_OPEN_DRAIN);
pinMode(twi_scl, OUTPUT_OPEN_DRAIN);
twi_setClock(100000);
twi_setClockStretchLimit(230); // default value is 230 uS

And try next steps.

Statistics: Posted by apollo0226 — Tue Oct 25, 2016 11:01 am


]]>
2016-10-25T03:59:35+08:00 2016-10-25T03:59:35+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10254#p10254 <![CDATA[Re: I2C Slave Mode]]>
we use

GPIO12 for SDA
GPIO14 for SCL

ok?

now show me your gpio setup

Statistics: Posted by rudi — Tue Oct 25, 2016 3:59 am


]]>
2016-10-25T03:29:44+08:00 2016-10-25T03:29:44+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10252#p10252 <![CDATA[Re: I2C Slave Mode]]>
apollo0226 wrote:
I know how to set the GPIO and ISR.
Now I set the SDA a low active Interrupt.


sry - you did it not know :)

Code:


if(( (gpio_status>>I2C_SLAVE_SDA_GPIO)& BIT0)&&scl) //SDA trigger and scl high   


you must set to anyedge

Code:


 if( sda )// sda posedge , stop
            {
            ets_printf("stop..\n\0");



and any edge you must handle

Code:


 gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDA_GPIO),GPIO_PIN_INTR_NEGEDGE);
 gpio_pin_intr_state_set(GPIO_ID_PIN(I2C_SLAVE_SDL_GPIO),GPIO_PIN_INTR_DISABLE);


and on each handle
you must know, when it is a start and when it is a stop

Code:


else //sda negedge,start
   
         {
            ets_printf("start..\n\0");


you must each edge handle and compare

because you know what is a start
and because you know what is a stop
you can make the procedure like you need.

apollo0226 wrote:
So when the Interrupt first trigger , it must be start condition, because SCL is high, SDA is low.


sry no.
it triggers allways, must allways tigger
in the isr then you check
is it a start
if is a start, then check if addr match
if addr match
then check again is it not a stop
then check addr match
if addr match then again
then check again is it not a stop
if you have checked addr
then check it is a write or a read cmd
..

apollo0226 wrote:
So I get the 8 bit and set ACK, this is slave address and R/W bit.


no sry.
you must allway first check is it no stop
you know what stop is
so check
get the pin valu of scl and check against sda,
if sda is high, then it is a stop
apollo0226 wrote:
I compare it , is this my slave address? if no ,
set the NACK and exit, If yes, go to check R/W bit to Read or Write.
If Read , get more data.


sry no :)
you only make ack,
no NACK
nack is allways, when you do no ACK.
ok?
you do not a NACK

NACK is given by the OpenDrain Config
thats the "trick"
you only config the Gpio to OpenDrain
and set the value for it to high ( NACK )
if you not change the pin valu for a ACK
then automatically the master read a NACK
you have not to set the PIN Value for the GPIO to High
its configured high as OpenDrain.
you only change the config for the GPIO if you give a ACK
and this can read the master because it is set as Opendrain too.
ok?

apollo0226 wrote:
a) My question is when do I stop ?
b) Checking SCL and SDA both high (STOP condition) in the SDA ISR ?
c) Do the SCL need to set to Interrupt ? if yes , active high or low? What do it do in SCL ISR?

d) If you give us the sample code , it will help a lot.


a) as master? or as slave?
b) no, first goes sda high, and scl is low at this time, this is a stop, and yes in the ISR.
c) yes - if you do by HW yes, you have to check SDA AND SCL,
you can do a small only by SDA, this was my start too. and make a software ISR - this is the simplest way until you not go faster

but if you go faster, then your software "time stretch" is not detailed enough to handle SDA on right timming.

d) no sir ;-)
you have to learn first how the I2C protokoll works, then come back and we start again step by step.
i know, you can do it. your thinking is not to much wrong on the start and stop. be sure this is in your head on right function,
then come back and we work together i am sure on end of week the base stand here.

come on guy!
you need my example not - i am sure.

;-)

best wishes
rudi ;-)

step 1)
post your code how you do setup opendrain the gpio for I2C slave
this is the first step.
lets go - show me..

Statistics: Posted by rudi — Tue Oct 25, 2016 3:29 am


]]>
2016-10-24T22:54:29+08:00 2016-10-24T22:54:29+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10250#p10250 <![CDATA[Re: I2C Slave Mode]]> Now I set the SDA a low active Interrupt.
So when the Interrupt first trigger , it must be start condition, because SCL is high, SDA is low.
So I get the 8 bit and set ACK, this is slave address and R/W bit.
I compare it , is this my slave address? if no , set the NACK and exit, If yes, go to check R/W bit to Read or Write. If Read , get more data.

My question is when do I stop ? Checking SCL and SDA both high (STOP condition) in the SDA ISR ?
Do the SCL need to set to Interrupt ? if yes , active high or low? What do it do in SCL ISR?

If you give us the sample code , it will help a lot.

Statistics: Posted by apollo0226 — Mon Oct 24, 2016 10:54 pm


]]>
2016-10-24T20:20:21+08:00 2016-10-24T20:20:21+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10249#p10249 <![CDATA[Re: I2C Slave Mode]]>
apollo0226 wrote:
Do we get any official support from espressif ?
Or any sample code?
I do need this.


next time i will post the doing with a smaller base.
yes you will get a sample code here next time
so keep smile - but you have to studdy i2c protokoll first.
there is nothing mysteriose on the code.
and there comes no end solution for your homework.
you have to optimate the code then by your self and needs.

what is need/comes:

GPIO setup for openDrain
if you not know about this, you must studdy first the difference to openDrain

GPIO set valu
Gpio set ACK, NACK
if you not know about this, you must studdy first, how you can give an Low Value to an OpenDrainPin and a high.
to give a LOW by set the gpio to low is not enough!

ISR / Interrupting and comparing
Start, Stop, 7bit, 10bit... Adress, Data
if you not know about this, you must studdy first Interrupting with GPIO
and how you can
- set
- compare
- clear


best wishes
rudi ;-)

Statistics: Posted by rudi — Mon Oct 24, 2016 8:20 pm


]]>
2016-10-24T16:41:40+08:00 2016-10-24T16:41:40+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10243#p10243 <![CDATA[Re: I2C Slave Mode]]> Or any sample code?
I do need this.

Statistics: Posted by apollo0226 — Mon Oct 24, 2016 4:41 pm


]]>
2016-10-25T04:13:26+08:00 2016-10-23T16:38:07+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10237#p10237 <![CDATA[Re: I2C Slave Mode]]>
martinayotte wrote:
Rudi, did you got chance to upload your I2CSlave code somewhere ?


edit:

the missunderstand was cleaned now.
lets see what happens.

rudi

Statistics: Posted by rudi — Sun Oct 23, 2016 4:38 pm


]]>
2016-10-23T05:44:13+08:00 2016-10-23T05:44:13+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10232#p10232 <![CDATA[Re: I2C Slave Mode]]>
DeCoco wrote:
..here is my idea:

- interrupt on rising edge SCL and SDA


how looks your code for this?
how you do interrupt on sda + scl`?

Code:






DeCoco wrote:
- if rising edge SDA and SCL = 1 : START condition
- if rising edge SCL: count a counter with each rising edge:


what you do if after a start comes a stop?

and

what you do after a start, send address, send byte
comes a start again and no stop..

you do not know, when a start or a stop comes.
you must be flexible.

please register on forum.
anonyom is not good .
i talk then with a wall ;-)

Statistics: Posted by rudi — Sun Oct 23, 2016 5:44 am


]]>
2016-10-23T01:28:57+08:00 2016-10-23T01:28:57+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10230#p10230 <![CDATA[Re: I2C Slave Mode]]>

Statistics: Posted by Guest — Sun Oct 23, 2016 1:28 am


]]>
2016-10-14T18:45:01+08:00 2016-10-14T18:45:01+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10166#p10166 <![CDATA[Re: I2C Slave Mode]]>
I also read this thread and it would be very intersting how you solved the bitbanged I2C slave. I also try it, doing by myself. I just started, without much code yet, but here is my idea:

- interrupt on rising edge SCL and SDA
- if rising edge SDA and SCL = 1 : START condition
- if rising edge SCL: count a counter with each rising edge:

Code:

      switch(rec_cntr) {
         case 0 ... 7:   // read address
            os_printf("read i2c_address, counter = %d\n", rec_cntr);
            break;
         case 8:         // check address, send ack if ok (0) or not (1)
            os_printf("check i2c_address, counter = %d\n", rec_cntr);
            break;
         case 9 ... 16:    // write databyte
            os_printf("write data, counter = %d\n", rec_cntr);
            break;
         case 17:       // read ACK (must be 0)
            os_printf("read ack, counter = %d\n", rec_cntr);
            break;
         case 18:       // read stop
            os_printf("read stop, counter = %d\n", rec_cntr);
            rec_cntr = 0;   // reset rec_countr
            break;
         default: rec_cntr = 0;   // reset rec_cntr
      }
      rec_cntr++;   // increase counter


what do you think?

thanks for your feedback, if I have I'll share it.

Statistics: Posted by Guest — Fri Oct 14, 2016 6:45 pm


]]>
2016-10-13T03:22:16+08:00 2016-10-13T03:22:16+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10134#p10134 <![CDATA[Re: I2C Slave Mode]]> Statistics: Posted by martinayotte — Thu Oct 13, 2016 3:22 am


]]>
2016-10-11T09:45:01+08:00 2016-10-11T09:45:01+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=10112#p10112 <![CDATA[Re: I2C Slave Mode]]> Statistics: Posted by Seriga — Tue Oct 11, 2016 9:45 am


]]>
2016-09-27T18:27:39+08:00 2016-09-27T18:27:39+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=9958#p9958 <![CDATA[Re: I2C Slave Mode]]> ESP32 standalone TCP done
https://twitter.com/eMbeddedHome/status ... 2203046912

next step:
combine with esp8266 as I2C Slave,
read ADC, post on ESP32 WebContent on Client Request

the same is later done with BLE and connected devices.
but this is an other theme and for http://ESP32.com


best wishes
rudi ;-)

btw:
Yes, for makers on ESP8266
there is then the same Standalone TCP for an ESP8266
in combine a ESP8266 as I2C Slave.
Or we build a Network..lets see whats happend next time.

Statistics: Posted by rudi — Tue Sep 27, 2016 6:27 pm


]]>
2016-09-21T08:54:01+08:00 2016-09-21T08:54:01+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=9897#p9897 <![CDATA[Re: I2C Slave Mode]]>
- now i try part 1 of 3 to combine with esp32..
got v2 today, v3 is at custom office for clearing, think i get next few days
then we build the I2C "ESP-family" project ;-)

arrived2_txs.jpg


best wishes
rudi ;-)

Statistics: Posted by rudi — Wed Sep 21, 2016 8:54 am


]]>
2016-09-13T23:05:22+08:00 2016-09-13T23:05:22+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=9815#p9815 <![CDATA[Re: I2C Slave Mode]]>
best wishes
rudi ;-)



edit (14 Sept 01:30 PM ) :
@martin i must edit this a little, sorry:
thinked, that after our holiday there is an ESP32 ( ESP32-D0WDQ6, ESP3212, ESP32 Developer V2, ESP32 Developer version: John Lee ) parcel here, and because i combine this I2C doing with a esp32 fun project ( audio, video sdhc ) - and just in time ( looked in my email office ) there is no parcel with esp32 on the express way - i think it can be a small extra duration perhabs a few days in next week too because in china is mid autumn festival too and there are holidays. not sure they send parcel now before the weekend comes.

But will make it so now, if no parcel comes before next national holiday then i post a small example with i2c_slave.c and i2c_slave.h files, that you can use in yours. hope this helps in the meantime - because there are holdiays do 17 september i will contact on (earliest) 18. Sept (latest + one week ) Stanza with the pdf and source code like i promissed, and we have then docu to the i2c too in the docu thread/forum too.

edit ( 14 Sept 01:40 PM ) :
@martin - i will combine the small example with two ESP8266 Modules.
so if there is no esp32 next week here - we can use this example with esp's what we have.
and can test the i2c slave doing well.
so i make it with one ESP8266 as Master and the other ESP8266 as Slave.

My runing example was with ESP31 ( ESP32 beta ) and ESP8266.

Info:
chin. holidays:
15.09.-17.09.2016 Mid-Autumn Festival
and the next are on:
01.10.-07.10.2016 National

best wishes
rudi ;-)

Statistics: Posted by rudi — Tue Sep 13, 2016 11:05 pm


]]>
2016-09-13T06:09:21+08:00 2016-09-13T06:09:21+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=9803#p9803 <![CDATA[Re: I2C Slave Mode]]> Statistics: Posted by martinayotte — Tue Sep 13, 2016 6:09 am


]]>
2016-08-22T19:30:02+08:00 2016-08-22T19:30:02+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=9567#p9567 <![CDATA[I2C Slave Mode]]>
Anyone knows where can I get the lyrics for "Seven Days Saturday night" by Slave to the Square Wave?

Thanks,

Statistics: Posted by Dustupendr — Mon Aug 22, 2016 7:30 pm


]]>
2016-08-01T20:01:24+08:00 2016-08-01T20:01:24+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=8224#p8224 <![CDATA[Re: I2C Slave Mode]]>
scargill wrote:
..
Surprised Espressif have not followed up on this and included in the SDK...


pete, i think - because it is a really simple protocoll (i2c) - we have docu about gpio, interrupts and we have the i2c_master, so we can build simple the i2c_slave by using the standard i2c RFC ( Slave )

but i agree with you, it really would be nice to have an i2c slave.c and .h file as we do for the masters -
but think over, the "player on i2c_slave" must study the i2c protocoll too, because there are steps need to hold on the protocoll rules.

the slave is only a talk member to the master, and does only, when ( clock ) and what the master request. the begin for the slave is, to understand how to handle the master - so the slave can follow the instruction.

there must be a base understand for start and stop sequenze, ( SDA, SCL ) what happend on this and why.
what happend when master : i2c_master_start()
what happend when master : i2c_master_stop()

so we can setup the interrupt ( combine compare with SCL ) on SDA Pin for a START and other interrupt ( combine compare with SCL ) on SDA Pin for the STOP. this is a base understand.

the next interrupt is on the 8 bits DATA on SDA whilest not Stop, this is done 8 times, on 9 bit time slot the master read ACK/LAST example, this is base too. and vola - you have a i2c_slave.c and i2c_slave.h

;-)

the kitchen demo here i posted is only a user doing - when the i2c_slave is used.

the i2c base must be clear, how i2c works, then take the i2c docu, interrupt docu, gpio docu, i am sure - you finnish before i post the demo next time by self. its really easy.

i start with interrupts only on SDA first time, and make a while on SCL,
then i got the info between the reading lines , we must do on SDA + SCL

but i promissed, will post my i2c_slave.c and .h and the demo project next time.


edit: 4 August 2016
preinfo - what comes after the holdiay: DevF

example, here comes a master example flowchart, done with the freeware GUI IDE DevF

I2C_Master_Example_with_DevF.jpg


simple generated code :

Code:


// we include Headers here
   #include <osapi.h>
   #include <c_types.h>
   #include <i2c_master.h>

// global vars
   uint8 addr = 0x54;
   uint8 cmdWrite = 0;
   uint8 cmdRead = 1;
   uint8 ACK = 0;
   uint8 sendByte = 0;


uint32 counter()
{
  static uint32 count = 0;

  count++;
  sendByte = count;
  return count;
}


void i2c_talk()
{

  i2c_master_start();
  i2c_master_writeByte(addr+cmdWrite);
  ACK = i2c_master_checkAck();
  if (ACK)
  {
    os_printf("ACK ADDR True\n");
    i2c_master_writeByte(sendByte);
  }
  else
  {
    os_printf("addr does not match\n");
  }
  i2c_master_stop();
}


void i2c_demo_init()
{

  i2c_master_gpio_init();
  os_delay_us(30 * 1000 );
  i2c_master_init();
  os_delay_us(30 * 1000 );
}


void user_init()
{

  i2c_demo_init();
  while (1)
  {
    os_printf("count: %d\n", counter() );
    i2c_talk(sendByte);
    system_soft_wdt_feed();
    os_delay_us(1 * 1000 * 1000);
  }
}



for this, we need in project folder driver files from sdk:
-> driver / i2c_master.c
-> driver / include / i2c_master.h

and we can share the "sketch" simply with one project file ( likewise arduino )
we can export parts, we can import parts, we can share parts, we can work together on a project...

DevF Install is easy:
download the release project from the leader of the freeware at github to your desktop and expand.
you only must call one exe file for the ide - and make your setups/configs. it comes black to white.
so you can change all color/colour by your self like you like, this setups saved in the registry.

after this, download the language definition file for the esp serie from my github later:
there are then

- esp8266/esp8285
- esp31b
- esp32

and copy the text file to the language folder.

the freeware support just in time:
- chinese
- english
- espanol
- francais
- polish

and later ( i am at work to translate )
- german

links, esp repo, i2c slave and a small how to for start after the holiday.
we start step by step in this from scratch together.
please stand still patient :) found my master just in time :)
and the stuff is great and freeware

( the same is build for the esp32 )

Statistics: Posted by rudi — Mon Aug 01, 2016 8:01 pm


]]>
2016-08-01T07:01:27+08:00 2016-08-01T07:01:27+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=8215#p8215 <![CDATA[Re: I2C Slave Mode]]>
code explorer errors, warnings:
code_explorer_warnings.jpg



btw:
and yes
we have support for

-> static locals
-> volatile
-> and more


static-locals.jpg



best wishes
rudi ;-)

Statistics: Posted by rudi — Mon Aug 01, 2016 7:01 am


]]>
2016-08-01T07:06:19+08:00 2016-08-01T06:44:04+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=8214#p8214 <![CDATA[Re: I2C Slave Mode]]>
scargill wrote:
Did this go anywhere in terms of code for I2c slave? I'm not that hot on interrupts - it really would be nice to have an i2c slave.c and .h file as we do for the masters. Surprised Espressif have not followed up on this and included in the SDK.


hi pete
yes, after our holidays, i start a fine simple demo and give it to espressif ( stanza ) with documented doings ( pdf )
and there will be then a demo i2c_slave.c and i2c_slave.h. i promissed martin this too.

have a look to a nice user GUI..

Test Demo I2C Master Code:
Master call a counter func,
count is local variable and returned
sendByte is the Byte, that Master send to the Slave ( Address 0x54, WriteCommand )

settings vars:

vars.jpg


settings extern from i2c_master.c:

externs.jpg



user_init()
and
counter func

i2c_master_demo_1.jpg


i2c doings
i2c_master_demo_2.jpg


code explorer errors, warnings:

( see the next post, cause only 5 files are allowed )


and the flowchart generate by click

Code:


/* i2c_master_demo */

#include <osapi.h>
#include <c_types.h>
#include <i2c_master.h>


uint8 addr = 0x54;
uint8 cmdWrite = 0;
uint8 cmdRead = 1;
uint8 ACK = 0;
uint8 sendByte = 0;

uint32 counter()

{
  static uint32 count = 0;

  count++;
  sendByte = count;
  return count;
}

void i2c_talk()

{

  i2c_master_start();
  i2c_master_writeByte(addr+cmdWrite);
  ACK = i2c_master_checkAck();
  if (ACK)
  {
    i2c_master_writeByte(sendByte);
    os_printf("ACK ADDR True\n");
  }
  else
  {
    os_printf("addr does not match\n");
  }
  i2c_master_stop();
}

void i2c_demo_init()

{

  /* FYI this function are extern from driver pack
  void i2c_master_gpio_init(void);
  void i2c_master_init(void);
  #define i2c_master_wait    os_delay_us
  void i2c_master_stop(void);
  void i2c_master_start(void);
  void i2c_master_setAck(uint8 level);
  uint8 i2c_master_getAck(void);
  uint8 i2c_master_readByte(void);
  void i2c_master_writeByte(uint8 wrdata);
  bool i2c_master_checkAck(void);
  void i2c_master_send_ack(void);
  void i2c_master_send_nack(void); */
 
 
  i2c_master_gpio_init();
  os_delay_us(30 * 1000 );
  i2c_master_init();
  os_delay_us(30 * 1000 );
}

void user_rf_pre_init()

{

}

void user_init()

{

  i2c_demo_init();
  while (1)
  {
    os_printf("count: %d\n", counter() );
    i2c_talk(sendByte);
    // os_printf("hello world\n");;
    system_soft_wdt_feed();;
    // os_delay_us(1 * 1000 * 1000);;
    os_delay_us(20 * 1000);;
  }
}




and do the compile + flash if you press on click more..

compile.jpg




the I2C Slave on same way, .. i post the complet here later after the holidays.. and:

and FYI:

this is freeware!

i will post later more details ( gui src, repo, leader, democode, chippack and more.. ) ,
if the esp32 is out this weeks, we are back from holdiays too, so we start the doings

-> ESP8266
-> ESP8285
-> ESP32
-> each next esp that comes out

it is an amazing gui ( freeware! ) - will later write more about this - and steps -
but for this summer pause - you must wait for it a little time.
the preview chip packs was done - for a release the chip packs need a small test process time
and i search beta user for the freeware gui with esp chip packs.

best wishes
rudi ;-)

Statistics: Posted by rudi — Mon Aug 01, 2016 6:44 am


]]>
2016-07-31T13:22:55+08:00 2016-07-31T13:22:55+08:00 https://bbs.espressif.com:443/viewtopic.php?t=2092&p=8210#p8210 <![CDATA[Re: I2C Slave Mode]]> Statistics: Posted by scargill — Sun Jul 31, 2016 1:22 pm


]]>