cancel
Showing results for 
Search instead for 
Did you mean: 

HAL_CAN_Transmit_IT & HAL_CAN_Receive_IT can't be used simultaneously

dmitry1
Associate II
Posted on November 10, 2015 at 11:08

Synopsis: HAL_CAN_Transmit_IT & HAL_CAN_Receive_IT can't be used simultaneously effective without data loss risks.

Details:

When you build receive/transmit cycle like below (simplified)

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

}

In some situations HAL_CAN_Receive_IT/HAL_CAN_Transmit_IT drops with busy state.

This occurs because both of Transmit & Receive uses lock via __HAL_LOCK(hcan) 

When you call HAL_CAN_Transmit_IT and interrupt HAL_CAN_RxCpltCallback occurs, state is locked by HAL_CAN_Transmit_IT and rearm rx failed.

What solution to solve?

I can't find easy way now. In my opinion general mistake is unified ''HAL_CAN_StateTypeDef State'' used for three indenpended flags - General can state, rx state & tx state.

I think solution is split State for {State, rxState & txState} and never lock same thing in both of Receive/Transmit.

For example current structure

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;

split to

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;

But that is awesome library modification. Maybe you know any better solution?

Same problem affects USART library, i think
2 REPLIES 2
markb
Associate II
Posted on November 10, 2015 at 12:15 Hi, Yes, this has been mentioned a few times before (the same bug also exists in the CAN code). ST appear not to be interested in fixing this so workarounds are required. What I do is have my own function that is the guts of the HAL function which I call when I want to receive another character:

void UART_RxAgain(UART_HandleTypeDef *huart) {
// use the code from HAL_UART_Receive_IT() to repost interrupt
huart->pRxBuffPtr = &wifiState.rxChar;
huart->RxXferSize = 1;
huart->RxXferCount = 1;
huart->ErrorCode = HAL_UART_ERROR_NONE;
/* Check if a transmit process is ongoing or not */
if(huart->State == HAL_UART_STATE_BUSY_TX)
{
huart->State = HAL_UART_STATE_BUSY_TX_RX;
}
else
{
huart->State = HAL_UART_STATE_BUSY_RX;
}
/* Enable the UART Parity Error Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_PE);
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
__HAL_UART_ENABLE_IT(huart, UART_IT_ERR);
/* Process Unlocked */
//__HAL_UNLOCK(huart);
/* Enable the UART Data Register not empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
}

I should say that I am only using polling for UART tx and so this may not be suitable if you want to use interrupts for tx. Cheers, Mark
dmitry1
Associate II
Posted on November 10, 2015 at 12:36

That solution contains previously fixed by HAL_LOCK problem with State.

If that code interrupt another call of Transmit_IT in 

if(huart->State == HAL_UART_STATE_BUSY_RX)

section, State can be overriden after return from interrupt.

UPD: Oops, didn't see your last message. Confirmed