STM32 HAL I2C возвращает ошибку OVR (overrun / under-run) при использовании подхода, управляемого прерываниями

Я пытаюсь реализовать ведомое устройство I2C, управляемое прерываниями, которое постоянно готово к приему (кроме передачи) на STM32L051. Проблема в том, что я продолжаю получать ошибку HAL_I2C_ERROR_OVR в HAL_I2C_ErrorCallback. Приведенный ниже код является всего лишь примером и настроен на получение 3-байтового сообщения и ответ с однобайтовым статусом. Запуск этого кода вызывает ошибку OVR, как только мастер передает, а затем, кажется, постоянно запускает I2C1_IRQHandler, связывая другие операции. Подчиненное устройство не может использовать растяжение, поэтому я отключил его, как показано в приведенном ниже коде. Я новичок в HAL и в основном использую серию STM32Fxx с периферийными библиотеками std. Я просмотрел опубликованные примеры, даже скопировал некоторые из них в свой более сложный код, только чтобы получить ту же ошибку OVR. Есть ли у кого-нибудь из вас какие-либо предложения / советы / и т. Д. что могло подтолкнуть меня к рабочему решению?

Системная тактовая частота: 16 МГц

Скорость шины I2C: 10 кГц

Спасибо.

//**** Slave Rx/Tx ****//
int main(void)
{
  /* USER CODE BEGIN 1 */
    int responseSize = 1;
    int bytesToReceive = 3; 
    bool error = false; 
  /* USER CODE END 1 */
  /* MCU Configuration----------------------------------------------------------*/
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */

  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();


  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_I2C1_Init();

  /* Initialize interrupts */
  MX_NVIC_Init();

  i2c_response[0] = 0x01;
  responseSize = 1;

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
        //**** Slave Rx/Tx Interrupt based ****//
        if(slaveRxCallbackTriggered)
        {   
            slaveRxCallbackTriggered = false;

            if(responseSize > 0)
            {
                if(!IOModule_TransmitToMaster(i2c_response,responseSize))
                {
                    error = true;
                }
            }           

            IOModule_ReceiveFromMaster(bytesToReceive); 
        }
        else
            IOModule_ReceiveFromMaster(bytesToReceive);

  /* USER CODE END WHILE */
  /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/*------------------------------------------------------------------------------
    I2C Receive data from master
 *------------------------------------------------------------------------------*/
bool IOModule_ReceiveFromMaster(int numberOfBytes)
{
    bool result = false;

    if(HAL_I2C_GetState(&hi2c1) == HAL_I2C_STATE_READY)
    {       
        HAL_StatusTypeDef status = HAL_I2C_Slave_Receive_IT(&hi2c1, (uint8_t *)i2c_rx_array, numberOfBytes);

        if(status == HAL_OK)
        {
            result = true;
        }
    }


    return result;
}

/*------------------------------------------------------------------------------
    I2C Transmit data to master
 *------------------------------------------------------------------------------*/
bool IOModule_TransmitToMaster(uint8_t txBuffer[], int bufferSize)
{
    bool result = false;    

    while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
  {
  } 

    HAL_StatusTypeDef status = HAL_I2C_Slave_Transmit(&hi2c1, (uint8_t*)txBuffer, bufferSize, 10000);

    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_11,GPIO_PIN_RESET);

    if(status == HAL_OK)
    {   
        result = true;
    }

    return result;
}

/*------------------------------------------------------------------------------
    I2C Slave Rx Complete Callback
 *------------------------------------------------------------------------------*/
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *I2CxHandle)
{   
    slaveRxCallbackTriggered = true;
}

/*------------------------------------------------------------------------------
    I2C Error Callback
 *------------------------------------------------------------------------------*/
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)
{
    lastErrorCode = HAL_I2C_GetError(&hi2c1);
}

//I2C1 init
void MX_I2C1_Init(void)
{
  hi2c1.Instance = I2C1;
  hi2c1.Init.Timing = 0x000006C5;
  hi2c1.Init.OwnAddress1 = 0x24;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_ENABLE;
  HAL_I2C_Init(&hi2c1);

    /**Configure Analogue filter 
    */
  HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE);
}


void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{
  GPIO_InitTypeDef GPIO_InitStruct;
  if(hi2c->Instance==I2C1)
  {  
    /**I2C1 GPIO Configuration    
    PB6     ------> I2C1_SCL
    PB7     ------> I2C1_SDA 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF1_I2C1;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Peripheral clock enable */
    __HAL_RCC_I2C1_CLK_ENABLE();
  }
}

Мастер Tx / Rx

int main(void)
{
    //**** Master Tx/Rx ****//
    ioModule_response[0] = 0xA0;
    ioModule_response[1] = 0x01;
    ioModule_response[2] = 0xE3;

    while(1)
    {
        if(HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)0x24, (uint8_t*)ioModule_response, 3, 10000)== HAL_OK)
        {
            if(HAL_I2C_Master_Receive(&hi2c1, (uint16_t)0x24, (uint8_t *)i2c_rx_array, 1, 10000) != HAL_OK)
            {
                            //Blink LED and Pause
                LED_Show_StatusCode(2,false);
                HAL_Delay(500);
            }
            else if(i2c_rx_array[0] != 1)
            {
                            //Blink LED and Pause
                LED_Show_StatusCode(3,false);
                HAL_Delay(500);
            }
        }
    }
}

Это обработчик IRQ, сгенерированный STM32Cube MX.

void I2C1_IRQHandler(void)
{
  /* USER CODE BEGIN I2C1_IRQn 0 */

  /* USER CODE END I2C1_IRQn 0 */
  if (hi2c1.Instance->ISR & (I2C_FLAG_BERR | I2C_FLAG_ARLO | I2C_FLAG_OVR)) {
    HAL_I2C_ER_IRQHandler(&hi2c1);
  } else {
    HAL_I2C_EV_IRQHandler(&hi2c1);
  }
  /* USER CODE BEGIN I2C1_IRQn 1 */

  /* USER CODE END I2C1_IRQn 1 */
}

ОБНОВЛЕНИЕ: в настоящее время я изучаю LL API, поскольку он больше похож на стандартные периферийные библиотеки, которые я использовал в прошлом.


person Devin Parker    schedule 14.07.2016    source источник
comment
(1) HAL реализует множество проверок, поэтому IRQ_Handlers работает слишком медленно. Вы видели I2C_Slave_ISR_IT код обработчика? (2) Если у вас есть осциллограф - вы можете переключить тестовый вывод в реальном обработчике IRQ, который помещен в stm32f4xx_it.c файл, для просмотра его с помощью выводов I2C SCL, SDA. (3) Если вы используете HAL только для переносимости, попробуйте   -  person imbearr    schedule 14.07.2016
comment
Спасибо за ответ. (1) Я просмотрел ISR, реализованную в HAL. I2C_SlaveReceive_ISR сам по себе кажется довольно простым, но весь код обработчика прерывания действительно перескакивает с I2C1_IRQHandler на HAL_I2C_EV_IRQHandler, а затем, наконец, на I2C_SlaveReceive_ISR, так что я согласен, что в целом это кажется слишком сложным. (2) Я попытаюсь выполнить захват области с помощью отладочного вывода, установленного во время обработки IRQ, и дам вам знать, что я обнаружил. (3) Я посмотрю.   -  person Devin Parker    schedule 14.07.2016
comment
Сброшен ли флаг I2C_IT_EVT в фактическом обработчике ISR? Не могли бы вы выложить обработчик IRQ?   -  person Koorosh Hajiani    schedule 15.07.2016
comment
Фактический обработчик @KooroshHajiani является обработчиком HAL по умолчанию. HAL можно загрузить бесплатно. Что нового вы увидите, когда op опубликует его?   -  person imbearr    schedule 15.07.2016


Ответы (1)


Решение для меня заключалось в том, чтобы полностью отказаться от HAL и перейти на LL API, как это было предложено imbearr. Как уже говорилось, я использовал стандартные периферийные библиотеки STM32 в прошлом, и LL во многих отношениях очень похож.

Спасибо imbearr за помощь.

person Devin Parker    schedule 25.07.2016