STM32: I2C slave without enabled clock stretching feature

admin

Administrator
Staff member
I have to implement on a STM32L053 an I2C slave to read/write some arbitrary bytes of memory on the slave uC and the requirement is, that it should also work for I2C masters, which are not support clock stretching (
Code:
NOSTRETCH=1
).

I found a good example at:

<ul>
<li><a href="https://drolliblog.wordpress.com/2017/11/04/stm32l031x6-slave-transmitter-and-receiver/" rel="nofollow noreferrer">https://drolliblog.wordpress.com/2017/11/04/stm32l031x6-slave-transmitter-and-receiver/</a></li>
</ul>

And here is the implementation of this article:

<ul>
<li><a href="https://github.com/olikraus/u8g2/tree/master/sys/arm/stm32l031x6/i2c_test" rel="nofollow noreferrer">https://github.com/olikraus/u8g2/tree/master/sys/arm/stm32l031x6/i2c_test</a></li>
</ul>

This implementation works for me without problems for the read operation @100KHz data rate, but the write operations from the master are just working properly about 50%. Sometimes the written bytes are correct and sometimes not...

The difference between this example and my environment is, despite the uC are a bit different but still STM32L0 deviates, I have to use a quite lower sysclock due to the fact, I also have to fulfill another requirement for low power consumption. My uC is running at 4Mhz and the one in the example is running at 32Mhz.
Therefore, I am afraid, that I am due to my slow sysclock unable to read the incoming write data from the master fast enough within the ISR, before the next incoming data is received...

Can someone please give me a hint if it is possible to implement such a No-Clock-Stretching-I2C-Slave with my 4Mhz environment at all, and if yes, what can I do to get the write operations also working reliable?

Here is the description of the reference manual for the
Code:
NOSTRETCH=1
setting:

<ul>
<li><a href="https://www.st.com/content/ccc/reso...df/jcr:content/translations/en.DM00095744.pdf" rel="nofollow noreferrer">https://www.st.com/content/ccc/reso...df/jcr:content/translations/en.DM00095744.pdf</a></li>
</ul>

<a href=" " rel="nofollow noreferrer"><img src=" " alt="enter image description here"></a>

<a href=" " rel="nofollow noreferrer"><img src=" " alt="enter image description here"></a>

<a href=" " rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/rlonb.png" alt="enter image description here"></a>

Here is the implementation of the read/write functions and the I2C ISR:

Code:
volatile unsigned char i2c_mem[256];     /* contains data, which read or written */
volatile unsigned char i2c_idx;          /* the current index into i2c_mem */
volatile unsigned char i2c_is_write_idx; /* write state */

volatile uint16_t i2c_total_irq_cnt;
volatile uint16_t i2c_TXIS_cnt;
volatile uint16_t i2c_RXNE_cnt;


void i2c_mem_reset_write(void)
{
  i2c_is_write_idx = 1;  
}

void i2c_mem_init(void)
{
  i2c_idx = 0;
  i2c_mem_reset_write();
}

void i2c_mem_set_index(unsigned char value)
{
  i2c_idx = value;
  i2c_is_write_idx = 0;
}

void i2c_mem_write_via_index(unsigned char value)
{
  i2c_mem[i2c_idx++] = value;
}

unsigned char i2c_mem_read(void)
{
  i2c_mem_reset_write();
  i2c_idx++;
  return i2c_mem[i2c_idx];
}

void i2c_mem_write(unsigned char value)
{
  if ( i2c_is_write_idx != 0 )
  {
    i2c_mem_set_index(value);
  }
  else
  {
    i2c_is_write_idx = 0;
    i2c_mem_write_via_index(value);
  }
}

void __attribute__ ((interrupt, used)) I2C1_IRQHandler(void)
{
  unsigned long isr = I2C1-&gt;ISR;

  i2c_total_irq_cnt ++;

  if ( isr &amp; I2C_ISR_TXIS )
  {
    i2c_TXIS_cnt++;
    I2C1-&gt;TXDR = i2c_mem_read();
  }
  else if ( isr &amp; I2C_ISR_RXNE )
  {
    i2c_RXNE_cnt++;
    i2c_mem_write(I2C1-&gt;RXDR);
    I2C1-&gt;ISR |= I2C_ISR_TXE;           // allow overwriting the TCDR with new data
    I2C1-&gt;TXDR = i2c_mem[i2c_idx];
  }
  else if ( isr &amp; I2C_ISR_STOPF )
  {
    I2C1-&gt;ICR = I2C_ICR_STOPCF;
    I2C1-&gt;ISR |= I2C_ISR_TXE;           // allow overwriting the TCDR with new data
    I2C1-&gt;TXDR = i2c_mem[i2c_idx];
    i2c_mem_reset_write();
  }
  else if ( isr &amp; I2C_ISR_NACKF )
  {
    I2C1-&gt;ICR = I2C_ICR_NACKCF;
    I2C1-&gt;ISR |= I2C_ISR_TXE;           // allow overwriting the TCDR with new data
    I2C1-&gt;TXDR = i2c_mem[i2c_idx];
    i2c_mem_reset_write();
  }
  else if ( isr &amp; I2C_ISR_ADDR )
  {
    /* not required, the addr match interrupt is not enabled */
    I2C1-&gt;ICR = I2C_ICR_ADDRCF;
    I2C1-&gt;ISR |= I2C_ISR_TXE;           // allow overwriting the TCDR with new data
    I2C1-&gt;TXDR = i2c_mem[i2c_idx];
    i2c_mem_reset_write();
  }

  /* if at any time the addr match is set, clear the flag */
  /* not sure, whether this is required */
  if ( isr &amp; I2C_ISR_ADDR )
  {
    I2C1-&gt;ICR = I2C_ICR_ADDRCF;
  }

}