HAL_CAN_Transmit_IT und HAL_CAN_Receive_IT verwenden gleichzeitig Problem

Synopsis: STM32 HAL Library funktioniert HAL_CAN_Transmit_ITund HAL_CAN_Receive_ITkann nicht gleichzeitig effektiv ohne Datenverlustrisiko verwendet werden.

Einzelheiten:

Wenn Sie einen Empfangs- / Sendezyklus wie unten erstellen (vereinfacht)

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 einigen Situationen HAL_CAN_Receive_IT/ HAL_CAN_Transmit_ITDrops mit einem besetzten Zustand. Dies tritt auf, weil sowohl Senden als auch Empfangen eine Sperre über verwenden __HAL_LOCK(hcan).

Wenn Sie anrufen HAL_CAN_Transmit_ITund eine Unterbrechung HAL_CAN_RxCpltCallbackauftritt, wird der Zustand durch gesperrt HAL_CAN_Transmit_ITund das Rearm-Rx schlägt fehl.

Was ist die Lösung, um dies zu lösen?

Ich finde jetzt keinen einfachen Weg. Meiner Meinung nach ist ein allgemeiner Fehler die einheitliche HAL_CAN_StateTypeDef StateVerwendung von drei unabhängigen Flags - Allgemeiner CAN-Zustand, RX-Zustand und Tx-Zustand.

Ich denke, die Lösung besteht darin, State für {State, rxState & txState} aufzuteilen und niemals dasselbe in Receive/Transmit zu sperren.

Zum Beispiel die aktuelle Struktur,

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;

aufgeteilt zu

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;

Aber das ist eine großartige Bibliotheksmodifikation. Vielleicht gibt es eine bessere Lösung? Das gleiche Problem betrifft die USART-Bibliothek, denke ich.

Antworten (3)

Für Rearm RX können Sie __HAL_CAN_ENABLE_IT(&hcan, CAN_IT_FMP0) verwenden; // Interrupt-Flag für RX FIFO0 setzen.

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

}

oder

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

verursachen, zum Beispiel 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;
}

Als Slave würden Sie dies als Listener-Responder verwenden. Als Master würden Sie es als Sender-Hörer verwenden.

Der Punkt ist, dass Sie in jedem Moment wissen, was Sie tun sollten, entweder zuhören oder senden.

Ich fand die ST HAL großartig, um schnell loszulegen, aber wenn man von ein paar spezifischen Anwendungsfällen abweicht, wird es extrem haarig. Ich bin mir jedoch nicht sicher, wie es in diesem Fall ist, da CAN halbduplex ist.

Meine Anwendung ist "Adapter". Muss alles ohne Einschränkungen übertragen.
Halbduplex bedeutet, dass Sie sprechen oder empfangen können. Das ist eine physikalische Einschränkung des CAN-Busses.\

HAL_CAN_Transmit_ITund HAL_CAN_Receive_ITmit Interrupts arbeiten. Ich würde vorschlagen, dass Sie denjenigen finden, der für Sie wichtiger ist, und den Interrupt dafür verwenden. Wenn Sie beispielsweise lieber empfangen als senden möchten, verwenden Sie HAL_CAN_Receive_ITund HAL_CAN_Transmitfür den Sendeteil.

Mit der Art und Weise, wie ein CAN-BUS funktioniert, können Sie jederzeit eine Nachricht erhalten, und daher wird Ihr Empfangs-Interrupt sehr beschäftigt sein.

In meiner eigenen Implementierung verwende ich gerne die HAL_CAN_Transmitund die HAL_CAN_ReceiveFunktionen ohne die Interrupts, da meine Implementierungen keine Remote-Request-Frames verwenden und ich bisher keinen Bedarf hatte, tatsächlich einen Interrupt zu verwenden, um CAN-Nachrichten zu empfangen oder zu senden.