cancel
Showing results for 
Search instead for 
Did you mean: 

Perform CAN Transceiver self test on microcontroller boot

Flight64
Associate

I am using CAN on an STM32F042F6P6. I want to detect if an attached CAN transceiver is working or not. While there is no guarantee that there are other CAN nodes present on the bus, a 120 ohm termination resistor is always present.

My original plan was to try transmitting something on the bus and checking for errors. If I only get an acknowledgement error, then the transceiver is probably OK. If I get a bit error, bit stuffing error or a bus off error, something is wrong with the transceiver.

However, when I tried to put this in practice, I found that the STM was never throwing any errors. If I removed my transceiver and reattached it to simulate a failure, I found that although CAN transmission stopped, no errors were triggered and the hcan was stuck in HAL_CAN_STATE_LISTENING state. What could be going wrong here?

 

All parameters except Automatic Retransmission have been disabled in the ioc file. Global interrupt for CAN has been enabled. Attached code for reference.

#include <can.h>

const CAN_TxHeaderTypeDef transmitBuffer = {
    .StdId = 0x00,
    .ExtId = 0x1EFEFEFE,
    .IDE = CAN_ID_EXT,
    .RTR = CAN_RTR_DATA,
    .DLC = 8,
    .TransmitGlobalTime = DISABLE
};

const uint8_t data[8] = {0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88};
uint32_t mailbox = 0;

volatile uint32_t error = 0;

void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) {
    error = hcan->ErrorCode;
    HAL_CAN_ActivateNotification(hcan, CAN_IT_ERROR_PASSIVE | CAN_IT_BUSOFF);
}

// Called once before while loop
void initialiseCAN() {
    if (HAL_CAN_Start(&hcan) != HAL_OK) {
        Error_Handler();
    }

    HAL_CAN_ActivateNotification(&hcan, CAN_IT_ERROR_PASSIVE | CAN_IT_BUSOFF);
}

// Running inside a while loop for now
uint8_t performCANSelfTest() {

    if (HAL_CAN_AddTxMessage(&hcan, &transmitBuffer, data, &mailbox) != HAL_OK) {
        return 255;
    }

    while (HAL_CAN_IsTxMessagePending(&hcan, mailbox)) {
        HAL_Delay(1);
    }
    HAL_Delay(10);

    if (error & HAL_CAN_ERROR_STF || error & HAL_CAN_ERROR_BOF || error & HAL_CAN_ERROR_BR || error & HAL_CAN_ERROR_BD) {
        return 254;
    }
    return 1;

}
7 REPLIES 7
mƎALLEm
ST Employee

Hello @Flight64 and welcome to the ST community,

I don't see the usage of a transceiver detection. Normally you know what hardware is present at your hand: transceivers available or not. Moreover, I'm not sure your test has sense. The normal mode is intended to be used with CAN  bus attachment of the node so you need another node to test and check for ACK.

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

So my use case is not really about transceiver detection, but more about whether or not the attached transceiver is working - but without the guarantee that other CAN nodes are present. It's part of a series of hardware self tests that I am trying to perform such as I2C peripheral response, voltage line response and so on.

More importantly, the bigger issue is the fact that I got no error at all. Not even an ack slot error or form error came when I tried to transmit without a transceiver. I figured that I'd end up in bus off condition instantly from transmit errors.

As mƎALLEm pointed out, CAN is designed as a static bus system. You have to configure and assign addresses / node IDs beforehand.

> I figured that I'd end up in bus off condition instantly from transmit errors.

Each failed transmit attempt counts up the error number by 8 if I remember correctly. When the "error passive" limit is reached transmissions are turned off, and at the "bus off" limit, the node disconnects itself from the bus.

This is standardized CAN behavior.

 


@Flight64 wrote:

So my use case is not really about transceiver detection, but more about whether or not the attached transceiver is working - but without the guarantee that other CAN nodes are present.


This is not what CAN peripheral is intended to be used. During the test you need to connect another CAN node and check if you have ACK error or not.

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Flight64
Associate

Each failed transmit attempt counts up the error number by 8 if I remember correctly. When the "error passive" limit is reached transmissions are turned off, and at the "bus off" limit, the node disconnects itself from the bus.

This is standardized CAN behavior.


That is precisely my doubt. Putting aside my use case, I expected the hcan to go into error state or hcan->ErrorCode to show a non zero value. But neither of these happened - I verified by monitoring the hcan instance in live expressions.

In my code, I have enabled notification for CAN_IT_ERROR_PASSIVE and CAN_IT_BUSOFF, but the HAL_CAN_ErrorCallback never executed - even after I pulled out the transceiver and waited for 5 minutes. At no point, did HAL_CAN_AddTxMessage return anything other than HAL_OK, even though the hcan was clearly not transmitting anything. Of course, transmission starts right back up if I enable automatic bus-off handling during init, but that is really not the point here.

Did I maybe get the callback wrong or have I misunderstood the hcan API?

Screenshot of the hcan state after I pulled out the transceiver for reference.

Flight64_0-1749105199538.png

 

I don't use Cube/HAL code, and don't have a proper STM32-based CAN device or application.
For whatever reasons, my company chose other hardware.
Thus I can't comment on the "hcan", i.e. this CAN handle, and how Cube code handles it.

>... - even after I pulled out the transceiver and waited for 5 minutes. At no point, did HAL_CAN_AddTxMessage return anything other than HAL_OK, even though the hcan was clearly not transmitting anything.

CAN does not embody a master-slave concept for the bus (like e.g. SPI or I2C), it is solely arbitrated by message IDs.
Thus no node can "force" a transmission on the bus, it has to wait until it is free. Which makes the bus somewhat unpredictable, and enforces an asynchronous approach.
This is handled by the relatively complex CAN peripheral IP, which buffers Tx messages in "mailboxes". The message remains in this mbox ready for transmission until the bus is free. Tx completion or failures are usually communicated to the core via interrupts.
Many implementations (supposedly also Cube) add a queue, because the number of physical mboxes is limited. STM32 devices I had looked into had 3 of them.
Thus the Cube code might accept a lot of messages before returning an "overflow" error.


@Flight64 wrote:

Each failed transmit attempt counts up the error number by 8 if I remember correctly. When the "error passive" limit is reached transmissions are turned off, and at the "bus off" limit, the node disconnects itself from the bus.

This is standardized CAN behavior.


That is precisely my doubt. Putting aside my use case, I expected the hcan to go into error state or hcan->ErrorCode to show a non zero value. But neither of these happened - I verified by monitoring the hcan instance in live expressions.

In my code, I have enabled notification for CAN_IT_ERROR_PASSIVE and CAN_IT_BUSOFF, but the HAL_CAN_ErrorCallback never executed - even after I pulled out the transceiver and waited for 5 minutes. At no point, did HAL_CAN_AddTxMessage return anything other than HAL_OK, even though the hcan was clearly not transmitting anything. Of course, transmission starts right back up if I enable automatic bus-off handling during init, but that is really not the point here.


You can't do that for the transceiver self test. Again this is not what CAN interface is intended to be used. You need another CAN node on the bus to make that function consistent (CAN in Normal mode).

If you need to develop a self test on the transceiver may be to apply Low an High on the Tx transceiver pin and use two ADC channels and measure the voltage of recessive and dominant voltage levels on CAN_H and CAN_L pins of the transceiver. Or add a comparator to detect the differential voltage on CAN_H/CAN_L. I don't know if it works but need to test it.

CAN-bus-differential.png

I don't see another method for your scenario. And please discard the usage of the CAN interface for that purpose.

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.