cancel
Showing results for 
Search instead for 
Did you mean: 

I am working on STM32H743 micro operating as a slave I2C device. I am able to write to the slave device by a master I2C device but I can't seem to read.

onio
Senior

I have done some investigation and arrived that the problem is with the "DIR" bit in 

the Interrupt Status register not firing to indicate whether to do a Read.

uint32_t isr_trace[64];
int isr_count = 0;
 
void I2C2_EV_IRQHandler(void)
{
    isr_trace[isr_count++] = I2C2->ISR;  /* debug to check ISR status on entry*/
    if (!(I2C2->ISR & I2C_ISR_DIR_Msk)) /* Master is writing memory address.*/
    {
        while (!(I2C2->ISR & I2C_ISR_RXNE_Msk)) {}
        RX_buffer[RX_count++] = I2C2->RXDR;
    }
    else                          /*  Master is requesting data from memory.  */
    {
        for (int i = 0; i < 4; i++)
        {
            while (!(I2C2->ISR & I2C_ISR_TXE_Msk)) {}
            I2C2->TXDR = TX_buffer[i];
        }
    }
    I2C2->ICR |= I2C_ICR_STOPCF;
}

However if I then set a software break point before reading the I2C2->ISR register

as shown below here.

void I2C2_EV_IRQHandler(void)
{
   __BKPT(0);
    isr_trace[isr_count++] = I2C2->ISR;  /* debug to check ISR status on entry*/
   ...
}

The I2C2->ISR is updated to show the "DIR" bit correctly. Many thanks in advance.

5 REPLIES 5
T J
Lead

Working on H743 today, IIC as Master

Try a stronger pullup, I used a 4k7 pair and still have trouble.

had to drop the speed to 40KHz, still not perfect.

It was suggested by @Community member​  to drop the pullup further, maybe 2k7.

onio
Senior

Hi TJ thanks for your response. I have tried reducing the speed to 10KHz just to prove things but did not make a difference. I am unable to change the Pullup on the I2C master as this is a third party device that we are using to talk to other I2C devices and works fine.

One more thing I forgot to add to my initial post is that the Master I2C device does not support clock stretching.

You can still add a pullup on your end, without issue,

I would just try a 1K as an extreme then you will know if it helps.

if 10KHz does not work then add extra pullups are unlikely to fix it, I would test it. its the SDA pin that needs it.

otherwise , looks like a coding issue.

you could try using the DMA transfer, slightly different coding/delays, may give a different result.

onio
Senior

Hi TJ, Thank for your response once again. I am not sure that this is a data transfer issue due to the I2C bus. It might be a code issue like you suggested. As I mentioned in my first email I can perform a write from the I2C master (Electric Imp 005) and read all the data sent correctly by the I2C Slave (Stm32H743) .

The problem I am experiencing is related to when I perform an I2C read from the master. There is meant to be a flag in the I2C ISR register known as the "DIR" bit.

This flag should be set on entry to the I2C2_EV_IRQHandler(void) but what I observed that this bit is never set unless a breakpoint is SET in the ISR.

onio
Senior

Hi All,

Just in case someone has a similar issue, please find below a solution that I derive. Note that the RX_buffer, TX_buffer and their respective counters are defined as follows.

//  ------------------- main.c ----------------------------
#define MAX_BUF 128
 
volatile uint8_t RX_buffer[MAX_BUF];
volatile uint8_t TX_buffer[MAX_BUF];
volatile int RX_count = 0;
volatile int TX_count = 0;
 
void main()
{
    ...
    MX_I2C2_Init();
    ...
    HAL_I2C_EnableListen_IT(&hi2c2);
 
    while(1)
    {
    }
}

// ---------------------------------stm32h7xx_it.c ----------------------------------
 
extern uint8_t TX_buffer[MAX_BUF];
extern uint8_t RX_buffer[MAX_BUF];
extern uint8_t RX_count;
extern uint8_t TX_count;
 
void I2C2_EV_IRQHandler(void)
{
    /* USER CODE BEGIN I2C2_EV_IRQn 0 */
    uint32_t isr = I2C2->ISR;
 
    if (( isr & I2C_ISR_TXE) == I2C_ISR_TXE )
    {
        I2C2->TXDR = TX_buffer[TX_count++];                 
    }
    if (( isr & I2C_ISR_RXNE) == I2C_ISR_RXNE )
    {
        RX_buffer[RX_count++] = I2C2->RXDR;
    }
    if (( isr & I2C_ISR_NACKF) == I2C_ISR_NACKF )
    {
        TX_count = RX_count = 0;
        I2C2->ICR |= I2C_ICR_ADDRCF; // clear flag only once after receiving nack.
        I2C2->ISR |=  I2C_ISR_TXE; // flush txdr to avoid residue in buffer
        I2C2->ICR |= I2C_ICR_NACKCF;                
    }
    if (( isr & I2C_ISR_STOPF) == I2C_ISR_STOPF )
    {
        I2C2->ICR |= I2C_ICR_STOPCF;
    }
    if (( isr & I2C_ISR_OVR) == I2C_ISR_OVR )
    {
        I2C2->ICR |= I2C_ICR_OVRCF;
    }            
    /* USER CODE END I2C2_EV_IRQn 0 */
}