I2C Slave Mode

User avatar
rudi
Posts: 197
Joined: Fri Oct 24, 2014 7:55 pm

Re: I2C Slave Mode

Postby rudi » Tue Oct 25, 2016 3:59 am

i2c_slave.h

we use

GPIO12 for SDA
GPIO14 for SCL

ok?

now show me your gpio setup

-------------------------------------
love it, change it or leave it.
-------------------------------------
問候飛出去的朋友遍全球魯迪

apollo0226
Posts: 3
Joined: Mon Oct 24, 2016 4:22 pm

Re: I2C Slave Mode

Postby apollo0226 » Tue Oct 25, 2016 11:01 am

I am using NodeMCU. I am using Arduino IDE. Here is my the first setting.
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.

dkaufmann
Posts: 11
Joined: Mon Sep 26, 2016 10:02 pm

Re: I2C Slave Mode

Postby dkaufmann » Tue Oct 25, 2016 3:58 pm

Hi all

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: Select all

    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: Select all


// (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.

dkaufmann
Posts: 11
Joined: Mon Sep 26, 2016 10:02 pm

Re: I2C Slave Mode

Postby dkaufmann » Tue Oct 25, 2016 4:07 pm

I forgot the pin definition:

Code: Select all

//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

dkaufmann
Posts: 11
Joined: Mon Sep 26, 2016 10:02 pm

Re: I2C Slave Mode

Postby dkaufmann » Tue Oct 25, 2016 5:04 pm

plus open drain config of the SDA/SCL pins

Code: Select all

//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);
   

User avatar
rudi
Posts: 197
Joined: Fri Oct 24, 2014 7:55 pm

Re: I2C Slave Mode

Postby rudi » Wed Oct 26, 2016 5:05 am

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: Select all

// 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: Select all

// 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: Select all


// (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: Select all

   
   // 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: Select all

// 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: Select all


// **************************************************
// (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: Select all


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: Select all

 ...
 ...
 
 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: Select all


// 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 ;-)

-------------------------------------
love it, change it or leave it.
-------------------------------------
問候飛出去的朋友遍全球魯迪

dkaufmann
Posts: 11
Joined: Mon Sep 26, 2016 10:02 pm

Re: I2C Slave Mode

Postby dkaufmann » Wed Oct 26, 2016 5:15 pm

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!

User avatar
rudi
Posts: 197
Joined: Fri Oct 24, 2014 7:55 pm

Re: I2C Slave Mode

Postby rudi » Wed Oct 26, 2016 5:55 pm

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 ;-)

-------------------------------------
love it, change it or leave it.
-------------------------------------
問候飛出去的朋友遍全球魯迪

dkaufmann
Posts: 11
Joined: Mon Sep 26, 2016 10:02 pm

Re: I2C Slave Mode

Postby dkaufmann » Wed Oct 26, 2016 7:55 pm

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.

User avatar
rudi
Posts: 197
Joined: Fri Oct 24, 2014 7:55 pm

Re: I2C Slave Mode

Postby rudi » Wed Oct 26, 2016 10:01 pm

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: Select all


_l8ui a0, a11, GPIO_OFFSET_INPUT



or
example precise timing..

Code: Select all


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: Select all

..
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 ;-)

-------------------------------------
love it, change it or leave it.
-------------------------------------
問候飛出去的朋友遍全球魯迪

Who is online

Users browsing this forum: No registered users and 1 guest