cancel
Showing results for 
Search instead for 
Did you mean: 

I2C start condition not generating and unknown error interrupts

Dioswilson
Associate III

I am trying to use stm32F446 as a master I2C device with LL library and I have 2 main issues.

1. After sending a couple of messages(always 4 at most) over I2C I get no start condition when setting CR1->START, if I pause the program a couple of seconds later and I inspect the register it is still at 1 and never goes back to 0.

2.I randomly get interrupts on both (Don't know if both at the same time) EVT and ERR with none of the EVT or ERR flags set.

Here is my code if needed:

typedef enum {
        TWI_IDLE,
        TWI_SENDING,
        TWI_RECEIVING,
        TWI_BERR,
        TWI_AF,
        TWI_ARLO,
        TWI_OVR,
        TWI_UNKNOWN_EVENT,
        TWI_UNKNOWN_ERROR,
} TWI_state;

typedef enum {
        TWI_SUCCESS,
        TWI_FAILURE,
        TWI_BUSY,
} TWI_status;

typedef struct {
        I2C_TypeDef* I2CxReg;
        TWI_state state;
        uint8_t totalBytes;
        uint8_t bytesDone;
        uint8_t* data;
        uint8_t sendAddress;
} TWI_interface;

static void resetTWI(TWI_interface* iface);

TWI_status TWIMasterTransmit(TWI_interface* iface, uint8_t address, uint8_t* data, uint8_t length) {
    TWI_status status = TWI_SUCCESS;
    if (iface->I2CxReg == NULL) {
        status = TWI_FAILURE;
    }
    else if (iface->state == TWI_IDLE) {
        iface->totalBytes = length;
        iface->bytesDone = 0;
        iface->data = data;
        iface->sendAddress = address << 1; //addr+w
        LL_I2C_GenerateStartCondition(iface->I2CxReg);
        iface->state = TWI_SENDING;
    }
    return status;
}
void TWIEventHandler(TWI_interface* iface) {

    //Master transmit
    if (LL_I2C_IsActiveFlag_SB(iface->I2CxReg)) {
        LL_I2C_TransmitData8(iface->I2CxReg, iface->sendAddress);
    }
    else if (LL_I2C_IsActiveFlag_ADDR(iface->I2CxReg) || LL_I2C_IsActiveFlag_ADD10(iface->I2CxReg)) {
        LL_I2C_ClearFlag_ADDR(iface->I2CxReg);
        if (iface->bytesDone == 0) {
            LL_I2C_TransmitData8(iface->I2CxReg, iface->data[iface->bytesDone]); //bytesDone=0
            iface->bytesDone++;
        }
    }
    else if (LL_I2C_IsActiveFlag_TXE(iface->I2CxReg) || LL_I2C_IsActiveFlag_BTF(iface->I2CxReg)) {
        if (iface->bytesDone < iface->totalBytes) {
            // Transmit data
            LL_I2C_TransmitData8(iface->I2CxReg, iface->data[iface->bytesDone]);
            iface->bytesDone++;
        }
        else if (LL_I2C_IsActiveFlag_BTF(iface->I2CxReg)) {
            LL_I2C_GenerateStopCondition(iface->I2CxReg);

            // Sometimes it waits forever, so we time out.
            uint16_t timeout = 1000;
            while (timeout > 0 && (iface->I2CxReg->CR1 & I2C_CR1_STOP)) {
                timeout--;
            }
            if (timeout == 0) {
                resetTWI(iface);
            }
        }
    }
    else {
        iface->state = TWI_UNKNOWN_EVENT;
    }
}


void TWIErrorHandler(TWI_interface* iface) {
    //Note: State should be reseted to TWI_IDLE manually
    //  Maybe do fix the i2c issue here
    if (LL_I2C_IsActiveFlag_BERR(iface->I2CxReg)) {
        iface->state = TWI_BERR;
        LL_I2C_ClearFlag_BERR(iface->I2CxReg);
    }
    else if (LL_I2C_IsActiveFlag_AF(iface->I2CxReg)) {
        iface->state = TWI_AF;
        LL_I2C_ClearFlag_AF(iface->I2CxReg);
    }
    else if (LL_I2C_IsActiveFlag_ARLO(iface->I2CxReg)) {
        iface->state = TWI_ARLO;
        LL_I2C_ClearFlag_ARLO(iface->I2CxReg);
    }
    else if (LL_I2C_IsActiveFlag_OVR(iface->I2CxReg)) {
        iface->state = TWI_OVR;
        LL_I2C_ClearFlag_OVR(iface->I2CxReg);
    }
    else {
        iface->state = TWI_UNKNOWN_ERROR;
    }
}

static void resetTWI(TWI_interface* iface) {
    iface->state = TWI_IDLE;
    iface->totalBytes = 0;
    iface->bytesDone = 0;
    iface->data = NULL;
    iface->sendAddress = 0;
    LL_I2C_EnableReset(iface->I2CxReg);
    LL_I2C_DisableReset(iface->I2CxReg);
}

16 REPLIES 16

Is the BUSY bit set in the status register? If so you will have to clear it before it will send out a start signal.

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

No, here are the registers AFTER the start bit wasn't generated:

Dioswilson_0-1735669735854.png

Dioswilson_1-1735669747857.png

 

> What should I send through UART? I2C registers?

No, or at least not primarily. My idea is, that you trace the progression of code through the I2C state machine, including the error handling; i.e. at each key point where something is written to the I2C registers, I would output some distinctive mark, which could be observed together with the actual behaviour of the I2C bus (there are quite a few of those changes, that's why I suggested UART).

In other words, you would want to know, what happened a couple of steps before the machine stuck.

> here are the registers AFTER the start bit wasn't generated:

But that's after the timeout, isn't it. If there was some error/status flag, it would have been already cleared by the error interrupt, wouldn't it.

At this point, I would also ask, what exactly is connected and how, and whether signal integrity of the bus is perfect or not that much so (that involves track length, capacitance, pullups, neighbouring tracks with potential crosstalk, and all aspects of ground arrangement).

In my experience, the v1 I2C in 'F4 has a flaw (unacknowledged by ST, so take this with a huge grain of salt) in that due to unexpected interference on the bus it can get stuck amidst communication without any indication (my guess is, that this is due to flawed multimaster-related logic (I've seen that on non-ST chips in the past, unacknowledged by that manufacturer either), but I have no proof and I don't have insider information on the I2C internal machine to draw definitive conclusions). So, if the answer of the above question is "no, I am not 100% sure about signal integrity, as my tracks are not very short, pullups are weak, ground is not that great, and there are sources of interference nearby", you definitively need the timeout to bail out, but you should primarily focus on improving that signal integrity.

JW

 

TDK
Guru

OAR1 = 0x4018 is interesting. Since it's a master, why is this register modified? The highest bit set is in a "reserved" section of the register. Not sure if it's relevant.

Putting a logic analyzer on the SDA/SCL lines would provide some insight.

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

But that's after the timeout, isn't it. If there was some error/status flag, it would have been already cleared by the error interrupt, wouldn't it.
While it is true that status registers' were cleared if an error occured, this is not the case, since in case of errors I change the state variable in my struct, and that didn't happen. It has the value it takes when START bit is set on CR1

> Signal integrity is not perfect. It is a breadboard, so tracks are short. But I did check and GNDs are 0.3v apart.
Reducing pullup resistors also semmed to help. Will take this in consideration for the final product but still check for errors just in case and reset the interface I guess

I did check SDA/SCL lines. All messages are sent correctly until this error happens (And start bit isn't sent).
OAR1 is modified because it has a random slave address I set on CubeMX

> . It is a breadboard

If solderless, I recommend you to throw it to garbage bin today and never look back.

Soldered breadboards may be OK for prototyping, but these days when PCBs are manufactured cheaply and quickly on demand, there's little reason for not to make prototypes closely resembling the final product.

> still check for errors just in case and reset the interface I guess

I strongly recommend to do so.

JW