HAL_CAN_Transmit_IT и HAL_CAN_Receive_IT одновременно используют проблему

Описание: Библиотека STM32 HAL функционирует HAL_CAN_Transmit_ITи HAL_CAN_Receive_ITне может эффективно использоваться одновременно без риска потери данных.

Подробности:

Когда вы строите цикл приема/передачи, как показано ниже (упрощенно)

main() {
  HAL_CAN_Receive_IT();
  HAL_CAN_Transmit_IT();
}

HAL_CAN_RxCpltCallback() {
  HAL_CAN_Receive_IT(); // Rearm receive
}

HAL_CAN_TxCpltCallback() {
  HAL_CAN_Transmit_IT(); // Rearm transmit
}

В некоторых ситуациях HAL_CAN_Receive_IT/ HAL_CAN_Transmit_ITпадает с состоянием занятости. Это происходит из-за того, что и для передачи, и для приема используется блокировка через __HAL_LOCK(hcan).

Когда вы звоните HAL_CAN_Transmit_ITи HAL_CAN_RxCpltCallbackпроисходит прерывание, состояние блокируется HAL_CAN_Transmit_IT, и перезапуск rx завершается неудачно.

Каково решение, чтобы решить эту проблему?

Я не могу найти легкий путь сейчас. На мой взгляд, общей ошибкой является унифицированное HAL_CAN_StateTypeDef Stateиспользование трех независимых флагов — общего состояния CAN, состояния rx и состояния tx.

Я думаю, что решение состоит в том, чтобы разделить состояние для {State, rxState и txState} и никогда не блокировать одно и то же в обоих приемах/передачах.

Например, существующая структура,

typedef enum
{
  HAL_CAN_STATE_RESET             = 0x00,  /*!< CAN not yet initialized or disabled */
  HAL_CAN_STATE_READY             = 0x01,  /*!< CAN initialized and ready for use   */
  HAL_CAN_STATE_BUSY              = 0x02,  /*!< CAN process is ongoing              */
  HAL_CAN_STATE_BUSY_TX           = 0x12,  /*!< CAN process is ongoing              */
  HAL_CAN_STATE_BUSY_RX           = 0x22,  /*!< CAN process is ongoing              */
  HAL_CAN_STATE_BUSY_TX_RX        = 0x32,  /*!< CAN process is ongoing              */
  HAL_CAN_STATE_TIMEOUT           = 0x03,  /*!< CAN in Timeout state                */
  HAL_CAN_STATE_ERROR             = 0x04   /*!< CAN error state                     */
}HAL_CAN_StateTypeDef;

typedef struct
{
  ...
  __IO HAL_CAN_StateTypeDef   State;      /*!< CAN communication state        */
  ...
}CAN_HandleTypeDef;

разделить на

typedef enum
{
  HAL_CAN_STATE_RESET             = 0x00,  /*!< CAN not yet initialized or disabled */
  HAL_CAN_STATE_READY             = 0x01,  /*!< CAN initialized and ready for use   */
  HAL_CAN_STATE_BUSY              = 0x02,  /*!< CAN process is ongoing              */


}HAL_CAN_StateTypeDef;

typedef enum
{
  HAL_CAN_TXRX_STATE_READY             = 0x01,
  HAL_CAN_TXRX_STATE_BUSY              = 0x02,
  HAL_CAN_TXRX_STATE_TIMEOUT           = 0x03,
  HAL_CAN_TXRX_STATE_ERROR             = 0x04
}HAL_CAN_TxRxStateTypeDef;

typedef struct
{
  ...
  __IO HAL_CAN_StateTypeDef     State;      /*!< CAN communication state        */
  __IO HAL_CAN_TxRxStateTypeDef RxState;    /*!< CAN RX communication state        */
  __IO HAL_CAN_TxRxStateTypeDef TxState;    /*!< CAN TX communication state        */
  ...
}CAN_HandleTypeDef;

Но это потрясающая модификация библиотеки. Может быть, существует лучшее решение? Та же проблема затрагивает и библиотеку USART, я думаю.

Ответы (3)

Для повторного приема RX вы можете использовать __HAL_CAN_ENABLE_IT(&hcan, CAN_IT_FMP0); // установить флаг прерывания для RX FIFO0.

void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef *CanHandle)
{
   __HAL_CAN_ENABLE_IT(CanHandle, CAN_IT_FMP0);

}

или

void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef *CanHandle)
{

  if (HAL_CAN_Receive_IT(CanHandle, CAN_FIFO0) != HAL_OK)
  {
    __HAL_CAN_ENABLE_IT(CanHandle, CAN_IT_FMP0);  // set interrupt flag for RX FIFO0 if CAN locked
  }
}

вызвать, например, stm32f1xx_hal_can.c:

HAL_StatusTypeDef HAL_CAN_Receive_IT(CAN_HandleTypeDef* hcan, uint8_t FIFONumber)
{
  /* Check the parameters */
  assert_param(IS_CAN_FIFO(FIFONumber));

  if((hcan->State == HAL_CAN_STATE_READY) || (hcan->State == HAL_CAN_STATE_BUSY_TX))
  {
    /* Process locked */
    __HAL_LOCK(hcan);                 // <<----see define of __HAL_LOCK, this contains return command, wtf????????


    if(hcan->State == HAL_CAN_STATE_BUSY_TX) 
    {
      /* Change CAN state */
      hcan->State = HAL_CAN_STATE_BUSY_TX_RX;
    }
    else
    {
      /* Change CAN state */
      hcan->State = HAL_CAN_STATE_BUSY_RX;
    }

    /* Set CAN error code to none */
    hcan->ErrorCode = HAL_CAN_ERROR_NONE;

    /* Enable interrupts: */
    /*  - Enable Error warning Interrupt */
    /*  - Enable Error passive Interrupt */
    /*  - Enable Bus-off Interrupt */
    /*  - Enable Last error code Interrupt */
    /*  - Enable Error Interrupt */
    /*  - Enable Transmit mailbox empty Interrupt */
    __HAL_CAN_ENABLE_IT(hcan, CAN_IT_EWG |
                              CAN_IT_EPV |
                              CAN_IT_BOF |
                              CAN_IT_LEC |
                              CAN_IT_ERR |
                              CAN_IT_TME  );   
    /* Process unlocked */
    __HAL_UNLOCK(hcan);

    if(FIFONumber == CAN_FIFO0)
    {
      /* Enable FIFO 0 message pending Interrupt */   
      __HAL_CAN_ENABLE_IT(hcan, CAN_IT_FMP0);         // <<---- here the rearm interrupt flag for FIFO0, if can is locked, function exits on  __HAL_LOCK and newer comes here!
    }
    else
    {
      /* Enable FIFO 1 message pending Interrupt */
      __HAL_CAN_ENABLE_IT(hcan, CAN_IT_FMP1);
    }

  }
  else
  {
    return HAL_BUSY;
  }

  /* Return function status */
  return HAL_OK;
}

Как раб, вы бы использовали это как слушатель-ответчик. Как мастер, вы бы использовали его в качестве передатчика-слушателя.

Суть в том, что в любой момент вы знаете, что вам следует делать, либо слушать, либо передавать.

Я обнаружил, что ST HAL отлично подходит для быстрого старта, но если вы отклоняетесь от нескольких конкретных вариантов использования, он становится очень сложным. Я не уверен, какое это имеет значение в данном случае, потому что CAN является полудуплексным.

Мое приложение "адаптер". Должен передавать что угодно без каких-либо ограничений.
Полудуплекс означает, что вы можете говорить или принимать. Это физическое ограничение шины CAN.\

HAL_CAN_Transmit_ITи HAL_CAN_Receive_ITработать с прерываниями. Я бы посоветовал вам найти тот, который более важен для вас, и использовать прерывание на нем. Например, если вы предпочитаете получать, а не передавать, используйте HAL_CAN_Receive_ITи HAL_CAN_Transmitдля части передачи.

Благодаря тому, как работает CAN BUS, вы можете получить сообщение в любой момент времени, и поэтому ваше прерывание приема будет очень занятым.

В моей собственной реализации мне нравится использовать функции HAL_CAN_Transmitи HAL_CAN_Receiveбез прерываний, потому что мои реализации не используют кадры удаленных запросов, и до сих пор у меня не было необходимости фактически использовать прерывание для приема или передачи сообщений CAN.