cancel
Showing results for 
Search instead for 
Did you mean: 

Manually sending an I2C stop command.

Jcoll.1
Associate III

In the manual it says that a stop command will be sent with the next byte if the CR2 stop flag is high.  Is there a way to send a stop command immediately rather than with the next byte?  I'm asking because I have a section of code where I send a start command (with the slave address) and then I repeatedly send data.  I would like to be able to terminate this data loop whenever I need to, rather that needing to write out another byte of data.

 

Jcoll1_0-1707080808253.png

 

6 REPLIES 6
TDK
Guru

It says after the current byte, not the next byte. If you're not currently sending anything (and clock is stretched), it should be immediate. Are you seeing something different?

Can't send a stop command mid-byte.

If you feel a post has answered your question, please click "Accept as Solution".
Jcoll.1
Associate III

I write a byte like this, I observe on a scope that the byte is written and then SCL is held low:

`hi2c->Instance->TXDR = *hi2c->pBuffPtr;`

 

I then try and issue a stop like this, and nothing happens:

hi2c->Instance->CR2 |= I2C_CR2_STOP;

 

I then write another byte, which I see on the score, and again set stop bit, and on the SECOND time I set the stop bit I will see the stop flag.  Note that this is during single-step debugging.  So I could post 3 separate scope images if you wanted to see that.

 

My end-goal is to basically have separate commands to send the start command, an unknown amount of data in a loop, and then a stop command.  Since I haven't yet gotten away from HAL I was trying to do this by making a modified version of HAL_I2C_Master_Transmit which takes an additional argument for which output type you want (start, data, stop).  Because of this I'm using I2C_TransferConfig, and have been playing around with the AUTOEND vs RELOAD modes trying to find a config that worked.  Since I haven't been able to find a mode that works as I liked I was thinking about just manually setting this stop flag.

 

 

 

Jcoll.1
Associate III

For reference, this is what I have. It works if I immediatly stop the transfer, otherwise it will eventually stop sending data.  I assume this could be becuase of some kind of bus error which I'm not checking for yet.  And yeah, it does appear to behave differently, in terms of the stop flag, than my example above.  The problem with this code, in addition to it eventually crashing, is that it requires that I send some data to get a stop byte.

 

    my_transfer (dac.hi2c, dac._i2cAddress,  NULL, 0, 1000);
    my_transfer (dac.hi2c, dac._i2cAddress, buffer, 2, 1000);
    my_transfer (dac.hi2c, dac._i2cAddress, buffer, -2, 1000);

 

 

 

 

#define MAX_NBYTE_SIZE 255
int my_transfer (I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData,
                 int16_t Size, uint32_t Timeout)
{

  uint32_t tickstart = HAL_GetTick ();

  if (hi2c->State != HAL_I2C_STATE_READY)
  {
    //return HAL_ERROR;
  }

  /* Process Locked */
  //__HAL_LOCK(hi2c);
  hi2c->State = HAL_I2C_STATE_BUSY_TX;
  hi2c->Mode = HAL_I2C_MODE_MASTER;
  hi2c->ErrorCode = HAL_I2C_ERROR_NONE;
  hi2c->pBuffPtr = pData;
  hi2c->XferCount = abs (Size);
  hi2c->XferISR = NULL;

  // normally you check if the transfer is larger than 255, we're going to skip that for now and assume <255
  if (hi2c->XferCount > MAX_NBYTE_SIZE)
    return 1;

  if (Size == 0)
  {
    /* Send Slave Address */
    I2C_TransferConfig (hi2c, DevAddress,
                        (uint8_t) MAX_NBYTE_SIZE,
                        I2C_SOFTEND_MODE, //I2C_SOFTEND_MODE,	// DONT send a stop
                        I2C_GENERATE_START_WRITE);

  }
  else if (Size < 0)
  {
    I2C_TransferConfig (hi2c, DevAddress, hi2c->XferCount,
                        I2C_SOFTEND_MODE,
                        I2C_NO_STARTSTOP);

    while (hi2c->XferCount > 0U)
    {
      /* Write data to TXDR */
      hi2c->Instance->TXDR = *hi2c->pBuffPtr;

      /* Increment Buffer pointer */
      hi2c->pBuffPtr++;

      hi2c->XferCount--;

      /* Wait until TXIS flag is set */
      if (I2C_WaitOnTXISFlagUntilTimeout (hi2c, Timeout, tickstart) != HAL_OK)
      {
        return HAL_ERROR;
      }
    }

    hi2c->Instance->CR2 |= I2C_CR2_STOP;

    if (I2C_WaitOnSTOPFlagUntilTimeout (hi2c, Timeout, tickstart) != HAL_OK)
    {
      return HAL_ERROR;
    }

    /* Clear STOP Flag */
    __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF);

    /* Clear Configuration Register 2 */
    I2C_RESET_CR2(hi2c);

    hi2c->State = HAL_I2C_STATE_READY;
    hi2c->Mode = HAL_I2C_MODE_NONE;
  }
  else if (Size != 0)
  {
    I2C_TransferConfig (hi2c, DevAddress, hi2c->XferCount,
                        I2C_SOFTEND_MODE,
                        I2C_NO_STARTSTOP);

    while (hi2c->XferCount > 0U)
    {
      /* Write data to TXDR */
      hi2c->Instance->TXDR = *hi2c->pBuffPtr;

      /* Increment Buffer pointer */
      hi2c->pBuffPtr++;

      hi2c->XferCount--;

      /* Wait until TXIS flag is set */
      if (I2C_WaitOnTXISFlagUntilTimeout (hi2c, Timeout, tickstart) != HAL_OK)
      {
        return HAL_ERROR;
      }

    }
    return HAL_OK;
  }

  /* Process Unlocked */
  // __HAL_UNLOCK(hi2c);
  return HAL_OK;
}

 

 

 

TDK
Guru

Just went back to look at my code. Setting STOP immediately sends a stop condition for me. No need to send a new byte.

I2C_TransferConfig isn't meant to be called directly and does a lot of other things other than just setting STOP.

If you feel a post has answered your question, please click "Accept as Solution".
Jcoll.1
Associate III

Thanks for looking, and for confirming that the stop flag is supposed to work like I thought it should.  So my issue must be related to some other configuration.

I noticed that my code has a few things in common with the HAL code for interrupt driven transmit.  And I've also noticed that I have a problem using that command where it stops sending data and keeps SCL low.  Its the exact same problem I had with previous versions of my function above (not only did it not send a stop, but sometimes I would be unable to write data to the bus at all).  I posted about my issue with the IT functions here: https://community.st.com/t5/stm32-mcus-wireless/i2c-it-rx-fails-with-a-stopped-scl-but-the-rx-interrupt-still/td-p/635437 .  I wonder if these might be related issues.  I'll probably try debugging the HAL callback to see when this error occurs.

TDK
Guru

If the interrupt keeps happening, look at the flags and determine why. This should be straightforward.

If you feel a post has answered your question, please click "Accept as Solution".