HAL_CAN_Transmit_IT y HAL_CAN_Receive_IT usan simultáneamente el problema

2

Sinopsis: las funciones de la biblioteca STM32 HAL HAL_CAN_Transmit_IT y HAL_CAN_Receive_IT no pueden usarse simultáneamente de manera efectiva sin riesgos de pérdida de datos.

Detalles:

Cuando creas un ciclo de recepción / transmisión como el de abajo (simplificado)

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
}

En algunas situaciones, HAL_CAN_Receive_IT / HAL_CAN_Transmit_IT cae con un estado ocupado. Esto ocurre porque tanto de Transmisión como de Amp; Receive utiliza un bloqueo a través de __HAL_LOCK(hcan) .

Cuando se llama a HAL_CAN_Transmit_IT y se produce la interrupción HAL_CAN_RxCpltCallback , el estado está bloqueado por HAL_CAN_Transmit_IT y falla rearm rx.

¿Cuál es la solución para resolver esto?

No puedo encontrar una manera fácil ahora. En mi opinión, un error general es el HAL_CAN_StateTypeDef State unificado utilizado para tres indicadores independientes: estado CAN general, estado rx y estado tx.

Creo que la solución es dividir State para {State, rxState & txState} y nunca bloquee lo mismo en Recibir / Transmitir.

Por ejemplo, la estructura actual,

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;

dividir a

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;

Pero esa es una modificación impresionante de la biblioteca. Tal vez exista una mejor solución? El mismo problema afecta a la biblioteca USART, creo.

    
pregunta Dmitry

2 respuestas

1

Como esclavo, usarías esto como un oyente-respondedor. Como maestro, lo utilizarías como un transmisor-oyente.

El punto es que, en cualquier momento, sabes lo que debes hacer, ya sea escuchando o transmitiendo.

Descubrí que el ST HAL es excelente para comenzar a correr, pero si te apartas de algunos casos de uso específicos, se vuelve extremadamente peludo. Sin embargo, no estoy seguro de qué importa en este caso, porque CAN es semidúplex.

    
respondido por el Daniel
1

Para rearm RX puede usar __HAL_CAN_ENABLE_IT (& hcan, CAN_IT_FMP0); // establecer el indicador de interrupción para RX FIFO0.

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

}

o

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
  }
}

causa, por ejemplo, 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;
}
    
respondido por el Andrew

Lea otras preguntas en las etiquetas