# Getting an ACK failure right away after writing slave address in interrupt mode for I2C [STM32F4]

I see that once slave address is written in the interrupt handler, I get an ACK Failure right away, but when I do the same thing with polling approach, I do get an ACK and goes on to read off the values from the device.

With interrupts, I see ISR gets triggered only once (in which it writes slave address) and never gets called again. My i2c configurations are fine, but there's something with interrupts that I seem to be missing out on.

Polling approach

HAL_StatusTypeDef HAL_I2C_Master_Transmit (I2C *hi2c, uint8_t *data, uint8_t size)
{
GenerateStartCondition();

// validate the completion of start condition
while (!GetFlagStatus(hi2c->Instance, I2C_SR1_SB) && HAL_Timeout(5));

// write slave address along with write bit

// wait for address to be sent

// write data to DR ...
}


With interrupts:

void HAL_StartI2CInterrupts() {
GenerateStartCondition();

// setting control bits
hi2c->Instance->CR2 |= I2C_CR2_ITBUFEN;
hi2c->Instance->CR2 |= I2C_CR2_ITEVTEN;
hi2c->Instance->CR2 |= I2C_CR2_ITERREN;

void I2C1_EV_IRQHandler ()
{

uint8_t event_interrupt = (hi2c->Instance->CR2 & I2C_CR2_ITEVTEN) >> I2C_CR2_ITEVTEN_Pos;

uint8_t buffer_interrupt = (hi2c->Instance->CR2 & I2C_CR2_ITBUFEN) >> I2C_CR2_ITBUFEN_Pos;

uint8_t var;

if (event_interrupt)
{
//  validate the completion of START condition
var =  (hi2c->Instance->SR1 & I2C_SR1_SB) >> I2C_SR1_SB_Pos;
if (var)
{
if (hi2c->I2C_State == I2C_TX_BUSY)
{
}
}
}

}


• I'm not sure if it's your problem, but as a general rule, avoid doing too much in an interrupt handler. It is a bit suspicious that you are calling I2C_WriteSlaveAddress inside the handler, unless that is how it is intended to be used. Mar 31, 2020 at 15:09
• I need to service the i2c interrupt though. After generating the start condition, I have to send the slave address for me to talk to it, no? @Justin
– xyf
Mar 31, 2020 at 18:28
• Yes, looking at other examples online, it looks like it is fine. Can you look at SDA/SCL on a scope and see if the slave device is actually not sending an ACK? Mar 31, 2020 at 18:34
• Yes, it does not. Do you know if there's any particular way of using interrupts? the only thing I can think of is using incorrect slave address which clearly isn't the case cause it does work with polling method. @Justin
– xyf
Mar 31, 2020 at 19:15
• also, does it make sense to call HAL_StartI2CInterrupts() repeatedly till TXing is done/stop-condition is generated? to me it doesn't make sense but I saw an example online that does that. @Justin
– xyf
Mar 31, 2020 at 19:31

ST's HAL could be generating a racing condition when called inside an ISR. As mentioned by @Justin, you don't want to occupy the CPU for too long when in an ISR. For your NACK when writing inside an ISR I see two solutions:

1. Use a flag. Set it on the ISR, on your main loop check for the raised flag, if so send the data through I2C;

2. Use DMA. HAL DMA calls are not blocking and the driver will deal with writing on the peripheral themselves.

Solution 1 is simpler but less deterministic. 1 would look something like:

static volatile uint8_t i2cSendFlag = 0;

void I2C1_EV_IRQHandler(void){
i2cSendFlag = 1;
I2C_ClearFlag(<the active ISR on your code>);
}

static I2CDoWork(void){
if(i2cSendFlag){

// Clear the send flag
i2cSendFlag = 0;
}
}

int main(void){
I2CConfig(I2C1);
for(;;){
I2CDoWork();