3 votos

Obtener un fallo ACK inmediatamente después de escribir la dirección de esclavo en modo de interrupción para I2C [STM32F4]

Veo que una vez que se escribe la dirección del esclavo en el manejador de interrupción, obtengo un Fallo ACK de inmediato, pero cuando hago lo mismo con el enfoque de sondeo, sí obtengo un ACK y pasa a leer los valores del dispositivo.

Con las interrupciones, veo que el ISR se activa sólo una vez (en la que escribe la dirección del esclavo) y nunca es llamado de nuevo. Mis configuraciones i2c están bien, pero hay algo con las interrupciones que parece que me estoy perdiendo.

Enfoque de sondeo

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
            I2C_WriteSlaveAddress(hi2c, WRITE);

            // wait for address to be sent
            while (!GetFlagStatus(hi2c->Instance, I2C_SR1_ADDR) && HAL_Timeout(5));

            // clear address flag
            I2C_ClearADDRFlag(hi2c->Instance);

            // write data to DR ...
}

Con interrupciones:

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)
            {               
                I2C_WriteSlaveAddress(hi2c, WRITE);     
            }
        }
         // check ADDR bit ...
     }

}

1voto

whatUwant Puntos 131

El HAL de ST podría estar generando una condición de carrera cuando se llama dentro de un ISR. Como menciona @Justin, no querrás ocupar la CPU durante demasiado tiempo cuando estés en un ISR. Para tu NACK al escribir dentro de un ISR veo dos soluciones:

  1. Usa una bandera. Establecerlo en el ISR, en su bucle principal de verificación de la bandera levantada, si es así enviar los datos a través de I2C;

  2. Utiliza DMA. Las llamadas DMA de HAL no son bloqueantes y el controlador se encargará de escribir en el periférico por sí mismo.

Solución 1 es más simple pero menos determinista. 1 sería algo así:

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){
        // Send your data

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

int main(void){
    I2CConfig(I2C1);
    for(;;){
        I2CDoWork();
        // Rest of your code
    }
}

El ISR que se llama sólo una vez parece que las banderas de los periféricos no se han borrado. De 1 cada evento ISR activo se borra mediante un método diferente. En su paso de configuración HAL, ¿qué banderas de evento están activas? Consulta la siguiente tabla para ver cómo borrar el evento correspondiente en la función ISR.

I2C IRQ requests

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X