HAL_CAN_Transmit_IT y HAL_CAN_Receive_IT problema de uso simultáneo

Sinopsis: STM32 HAL Library funciona HAL_CAN_Transmit_ITy HAL_CAN_Receive_ITno se puede usar simultáneamente de manera efectiva sin riesgos de pérdida de datos.

Detalles:

Cuando crea un ciclo de recepción/transmisión como el siguiente (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_ITgotas con estado ocupado. Esto ocurre porque tanto Transmit & Receive usa un bloqueo a través de __HAL_LOCK(hcan).

Cuando llama HAL_CAN_Transmit_ITy HAL_CAN_RxCpltCallbackse produce una interrupción, el estado se bloquea HAL_CAN_Transmit_ITy el rearme rx falla.

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

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

Creo que la solución es dividir State para {State, rxState & txState} y nunca bloquear lo mismo en ambos Receive/Transmit.

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 existe una solución mejor? El mismo problema afecta a la biblioteca USART, creo.

Respuestas (3)

Para rearmar RX puedes usar __HAL_CAN_ENABLE_IT(&hcan, CAN_IT_FMP0); // establece 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;
}

Como esclavo, usaría esto como un oyente que responde. Como maestro, lo usaría como transmisor-oyente.

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

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

Mi aplicación es "adaptador". Debe transferir cualquier cosa sin ninguna limitación.
Medio dúplex significa que puede hablar o puede recibir. Esa es una limitación física de CAN Bus.\

HAL_CAN_Transmit_ITy HAL_CAN_Receive_ITtrabajar con interrupciones. Le sugiero que encuentre el que sea más importante para usted y use la interrupción en eso. Por ejemplo, si prefiere recibir que transmitir, use HAL_CAN_Receive_ITy HAL_CAN_Transmitpara la parte de transmisión.

Con la forma en que funciona un BUS CAN, puede recibir un mensaje en cualquier momento y, por lo tanto, su interrupción de recepción estará muy ocupada.

En mi propia implementación, me gusta usar las funciones HAL_CAN_Transmity HAL_CAN_Receivelas sin interrupciones, porque mis implementaciones no usan marcos de solicitud remota y, hasta ahora, no tenía la necesidad de usar una interrupción para recibir o transmitir mensajes CAN.