Electronic – STM32 HAL_CAN_Transmit always returns TIMEOUT (HAL_CAN_STATE_TIMEOUT)

canstm32

Setup

I am using an STM32F103C8T6 (aka Blue Pill).
With the STM32Cube I set CAN_RX to PB8 and CAN_TX9 to PB9 (these are defaults/nonchangeable).

Circuit

schematic

simulate this circuit – Schematic created using CircuitLab

Components in above circuit:

  • STM #1: STM32F103C8T6
  • STM #2: STM32F103C8T6
  • Transceiver #1: TJA1050 based transceiver (see TJA 1050)
  • Transceiver #2: TJA1050 based transceiver (see TJA 1050)

I found out the TJA1050 works on 5V and the output VCCs from STM32 are 3.3V, so I used a breadboard power supply to give 5V to Transceiver 1 and 2 VCC. I assume the USB GNDs are coupled to the GNDs of the STM32s (probably internally since I didn't do any specific wiring), same as the USB +5V is coupled to the VCC of the STMs.

The transceivers already contain 120 ohm resistors so I assume I don't need any additional.
The current distance between CANL and CANH of transceiver #1 and #2 is about 10 cm / 4" (simple wire). In my real application it will be about 2 meters.

Also I assume that the CAN TX needs to be connected to the Tranceiver's TX (and RX to RX).

Can Settings

The generated CAN settings are below. This executes ok.

/* CAN init function */
static void MX_CAN_Init(void)
{

  static CanRxMsgTypeDef CanRX;
  static CanTxMsgTypeDef CanTX;
  CAN_FilterConfTypeDef sFilterConfig;

  hcan.Instance = CAN1;

  hcan.pRxMsg = &CanRX;
  hcan.pTxMsg = &CanTX;

  hcan.Init.Prescaler = 128;
  hcan.Init.Mode = CAN_MODE_NORMAL;
  hcan.Init.SJW = CAN_SJW_1TQ;
  hcan.Init.BS1 = CAN_BS1_12TQ;
  hcan.Init.BS2 = CAN_BS2_5TQ;
  hcan.Init.TTCM = DISABLE;
  hcan.Init.ABOM = DISABLE;
  hcan.Init.AWUM = DISABLE;
  hcan.Init.NART = DISABLE;
  hcan.Init.RFLM = DISABLE;
  hcan.Init.TXFP = DISABLE;
  if (HAL_CAN_Init(&hcan) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  sFilterConfig.FilterNumber = 0;
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
  sFilterConfig.FilterIdHigh = 0x0000;
  sFilterConfig.FilterIdLow = 0x0000;
  sFilterConfig.FilterMaskIdHigh = 0x0000;
  sFilterConfig.FilterMaskIdLow = 0x0000;
  sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
  sFilterConfig.FilterActivation = ENABLE;
  sFilterConfig.BankNumber = 14;

  if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
  {
      Error_Handler();
  }
}

Program

(removed STM generated comments blocks)

Transmitter:

int main(void)
{
  ..
  /* USER CODE BEGIN 2 */
  hcan.pTxMsg->StdId = 0x100;
  hcan.pTxMsg->ExtId = 0x01;
  hcan.pTxMsg->RTR   = CAN_RTR_DATA;
  hcan.pTxMsg->IDE   = CAN_ID_STD;
  hcan.pTxMsg->DLC   = 2;

  while (1)
  {
  hcan.pTxMsg->Data[0] = 0x10;
  hcan.pTxMsg->Data[1] = 0x1;

  if (HAL_CAN_Transmit(&hcan, CAN_FIFO0) != HAL_OK)
  {
      Error_Handler();
  }
  HAL_Delay(1000);
  }
}

Receiver (interrupt code is never called):

void RxIntEnable(CAN_HandleTypeDef *CanHandle)

{
    if (CanHandle->State == HAL_CAN_STATE_BUSY_TX)
    {
        CanHandle->State = HAL_CAN_STATE_BUSY_TX_RX0;
    }
    else
    {
        CanHandle->ErrorCode = HAL_CAN_ERROR_NONE;
        __HAL_CAN_ENABLE_IT(CanHandle, CAN_IT_EWG); // Error warning interrupt
        __HAL_CAN_ENABLE_IT(CanHandle, CAN_IT_EPV); // Error passive interrupt
        __HAL_CAN_ENABLE_IT(CanHandle, CAN_IT_BOF); // Bus-off interrupt
        __HAL_CAN_ENABLE_IT(CanHandle, CAN_IT_LEC); // Last error code interrupt
        __HAL_CAN_ENABLE_IT(CanHandle, CAN_IT_ERR); // Error interrupt
    }

    __HAL_CAN_ENABLE_IT(CanHandle, CAN_IT_FMP0); // FIFO0 message pending interrupt
}

void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* CanHandle)
{
    if ((CanHandle->pRxMsg->StdId == 0x100) &&
        (CanHandle->pRxMsg->IDE   == CAN_ID_STD) &&
        (CanHandle->pRxMsg->DLC   == 2))
    {
        printf("1");
    }

    RxIntEnable(CanHandle);
}

within main:

if (HAL_CAN_Receive_IT(&hcan, CAN_FIFO0) != HAL_OK)
{
    Error_Handler();
}

Loopback mode

When I use loopback mode:

hcan.Init.Mode = CAN_MODE_LOOPBACK 

instead of Normal mode, I can transmit and receive messages (and the hcan shows the correct data in the received message).

Problem

However, in Normal mode (as shown in the code fragment above) I always get a timeout in the next command:

 if (HAL_CAN_Transmit(&hcan, 10) != HAL_OK)

The function returns: HAL_CAN_STATE_TIMEOUT within this fragment (default HAL code):

/* Check End of transmission flag */
while(!(__HAL_CAN_TRANSMIT_STATUS(hcan, transmitmailbox)))
{
  /* Check for the Timeout */
  if(Timeout != HAL_MAX_DELAY)
  {
    if((Timeout == 0U) || ((HAL_GetTick()-tickstart) > Timeout))
    {
      hcan->State = HAL_CAN_STATE_TIMEOUT;

      /* Cancel transmission */
      __HAL_CAN_CANCEL_TRANSMIT(hcan, transmitmailbox);

      /* Process unlocked */
      __HAL_UNLOCK(hcan);
      return HAL_TIMEOUT;
    }
  }
}

All initialization seems to be ok (all functions return HAL_OK).

Analysis

What I tried/checked was:

  • Using another STM32: no difference
  • Using another transceiver: no difference
  • Played a bit with the SJW/BS1/BS2 time quantities: no difference
  • Making sure the time quantities were equal
  • Playing with different data values and filters: no difference
  • Checked the output of PB9 (CAN transmit): it seems not to change at all (so this is a problem): no difference
  • Removing the wire from GPIO PB9 (CAN Tx) to the TX of my CAN transceiver : no difference.
  • Checking the Transmit is cancelled (which is needed and was an old bug, but has already been fixed by the HAL library I'm using).
  • Checking the resistance between CANL and CANH which moves between 63 and 70 ohms.
  • Checking the voltage between CANL and CANH (while not sending after the error). 0 Voltage; I wouldn't expect this.

Questions

  1. Why do I get a timeout? I'm having a hard time trying to find more to check what I already did.

Related question:

Update

This is an old question, but trying again to get CAN working with same bad result (timeout). I changed some settings and updated the information above accordingly.

Best Answer

I got the new version and what a difference there are quite a couple of changes to keep in mind so here are some snippets:


// To send a message
// Old version:
HAL_CAN_Transmit(&hcan1, 10);

//new version:
HAL_CAN_AddTxMessage(&hcan, &TxMessage, TxData, &TxMailbox); 

//To receive a message by polling
        Receive_CAN_Message_Polling(RxMessage, RxData);     

// here is the Polling Function 

void Receive_CAN_Message_Polling(CAN_RxHeaderTypeDef RxMessage_, uint8_t* RxData_){
    uint8_t messages = HAL_CAN_GetRxFifoFillLevel(&hcan, CAN_RX_FIFO0);
        if(messages > 0){
            if(HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &RxMessage_, RxData_) == HAL_OK){
            Indicate();             
        }
    }
}   

much different to the old Library but it works very well I have not yet looked at the Interrupts and receiving with them but I am sure that they also work fine in the new version of the HAL_CAN Driver

Related Topic