4 years, 6 months ago.

Facing issues in establishing CAN communication between two nodes

I am trying to establish CAN communication between two nodes using NUCLEO-F103RB board and two CAN transceivers SN65HVD230DR. I used 120ohm termination resistance and remaining connections also seem right(i.e I connected transceivers RX pin to NUCLEO TD pin & transceivers TX pin to NUCLEO RD pin). I used the below attached code.

Output: I was getting 0 rderrors and 248 tderrors irrespective of whether I connect CAN transceivers to NUCLEO board or not. can1 is able to send data for the first three loop iterations and after that can1 is also failing to send the message. can2 is not able to receive data from the first loop iteration itself.

Anyone, please help me in establishing communication.

#include "mbed.h"
#if defined (DEVICE_CAN) || defined(DOXYGEN_ONLY)

Ticker ticker;
DigitalOut led1(LED1);
DigitalOut led2(LED2);

CAN can1(PB_8,PB_9);
CAN can2(PA_11,PA_12);
char counter = 0;

void send() {

    CANMessage msg1(887, &counter, 1);                    // create and initialize msg to being the message to send.
    if(can1.write(msg1)) {                                                   // sends the message
        counter++;
        printf("MESSAGE SENT: %d, %d\n", counter, msg1.data[0]);
        led1 = !led1;
    } 
    else{
        printf("Failed to send the message\n");
        }
    printf("CAN1 rderror: %d, tderror: %d\r\n\n", can1.rderror(), can1.tderror() );
    
}


int main() {
    printf("main()\n");
    
    can2.frequency(250000);
    can1.frequency(250000);
    ticker.attach(&send, 2);
    CANMessage msg;
    
    while(1) {
        if(can2.read(msg)) {
            printf("Message received: %d\n", msg.data[0]);
            led2 = !led2;
        } 
        else{
            printf("message not received\n");
        }
        printf("CAN2 rderror: %d, tderror: %d\r\n\n", can2.rderror(), can2.tderror() );
        wait(1);
    }
    
}

#else
  #error CAN NOT SUPPORTED
  
#endif

/media/uploads/DheerajEtta/output1.jpg

Hello Dheeraj,

Try to click on the Edit button located in the top-right corner and then enclose your source code with <<code>> and <</code>> tags, each on separate line, like:

<<code>>
Your source
code.
<</code>>

Then after clicking on the Save Question button it will be displayed as formatted code which can be copy & pasted into the online editor for testing. A picture (png, bmp, jpg) doesn't allow that.

posted by Zoltan Hudak 10 Oct 2019

2 Answers

4 years, 6 months ago.

Hello Dheeraj,

When using Mbed OS 2 try this example.

Because Mbed OS 5 doesn't allow to call CAN's read method in ISR, replace main.cpp with the one below when building with Mbed OS 5:

main.cpp

/*
 * An example showing how to use the mbed CAN API:
 *
 * Two affordable (about $2 on ebay) STM32F103C8T6 boards (20kB SRAM, 64kB Flash),
 * (see [https://developer.mbed.org/users/hudakz/code/STM32F103C8T6_Hello/] for more details)
 * are connected to the same CAN bus via transceivers (MCP2551 or TJA1040, or etc.).
 * CAN transceivers are not part of NUCLEO boards, therefore must be added by you.
 * Remember also that CAN bus (even a short one) must be terminated with 120 Ohm resitors at both ends.
 *
 *
 * The same code is used for both mbed boards, but:
 *      For board #1 compile the example without any change.
 *      For board #2 comment out line 21 before compiling
 *
 * Once the binaries have been downloaded to the boards reset both boards at the same time.
 *
 */
//#define TARGET_STM32F103C8T6    1       // uncomment this line to use STM32F103C8T6 boards
//#define BOARD1  1                       // comment out this line when compiling for board #2
//
#if defined(TARGET_STM32F103C8T6)
#define LED_PIN PC_13
const int           OFF = 1;
const int           ON = 0;
#else
#define LED_PIN LED1
const int           OFF = 0;
const int           ON = 1;
#endif
#if defined(BOARD1)
const unsigned int  RX_ID = 0x100;
const unsigned int  TX_ID = 0x101;
#else
const unsigned int  RX_ID = 0x101;
const unsigned int  TX_ID = 0x100;
#endif
#include "mbed.h"
#include "CANMsg.h"

Serial              pc(USBTX, USBRX);
CAN                 can(PB_8, PB_9);    // CAN Rx pin name, CAN Tx pin name
//CAN                 can(p30, p29);  // CAN Rx pin name, CAN Tx pin name
CANMsg              rxMsg;
CANMsg              txMsg;
DigitalOut          led(LED_PIN);
Timer               timer;
uint8_t             counter = 0;
AnalogIn            analogIn(A0);
float               voltage;

/**
 * @brief   Prints CAN message to PC's serial terminal
 * @note
 * @param   CANMessage to print
 * @retval
 */
void printMsg(CANMessage& msg)
{
    pc.printf("  ID      = 0x%.3x\r\n", msg.id);
    pc.printf("  Type    = %d\r\n", msg.type);
    pc.printf("  Format  = %d\r\n", msg.format);
    pc.printf("  Length  = %d\r\n", msg.len);
    pc.printf("  Data    =");
    for (int i = 0; i < msg.len; i++)
        pc.printf(" 0x%.2X", msg.data[i]);
    pc.printf("\r\n");
}

/**
 * @brief   Main
 * @note
 * @param
 * @retval
 */
int main(void)
{
    pc.baud(9600);                              // set serial speed
#if defined(BOARD1)
    led = ON;                                   // turn the LED on
    timer.start();                              // start timer
    pc.printf("CAN_Hello board #1\r\n");
#else
    led = OFF;                                  // turn LED off
    pc.printf("CAN_Hello board #2\r\n");
#endif
    can.frequency(1000000);                     // set CAN bit rate to 1Mbps
    can.filter(RX_ID, 0xFFF, CANStandard, 0);   // set filter #0 to accept only standard messages with ID == RX_ID
    while (1) {
        if (timer.read_ms() >= 2000) {
            // check for timeout
            timer.stop();                       // stop timer
            timer.reset();                      // reset timer
            counter++;                          // increment counter
            voltage = analogIn * 3.3f;          // read the small drift voltage from analog input
            txMsg.clear();                      // clear Tx message storage
            txMsg.id = TX_ID;                   // set ID
            // append data (total data length must not exceed 8 bytes!)
            txMsg << counter;                   // one byte
            txMsg << voltage;                   // four bytes
            if (can.write(txMsg)) {
                // transmit message
                led = OFF;                      // turn the LED off
                pc.printf("-------------------------------------\r\n");
                pc.printf("-------------------------------------\r\n");
                pc.printf("CAN message sent\r\n");
                printMsg(txMsg);
                pc.printf("  counter = %d\r\n", counter);
                pc.printf("  voltage = %e V\r\n", voltage);
            }
            else
                pc.printf("Transmission error\r\n");
        }

        if (can.read(rxMsg)) {
            pc.printf("-------------------------------------\r\n");
            pc.printf("CAN message received\r\n");
            printMsg(rxMsg);
            led = ON;                           // turn the LED on
            if (rxMsg.id == RX_ID) {
                // extract data from the received CAN message
                // in the same order as it was added on the transmitter side
                rxMsg >> counter;
                rxMsg >> voltage;
                pc.printf("  counter = %d\r\n", counter);
                pc.printf("  voltage = %e V\r\n", voltage);
            }

            timer.start();                      // to transmit next message
        }
    }
}

Edit:

  • When you create two CAN objects by using different pin names also make sure they are associated with different CAN controllers! Your code:

CAN  can1(PB_8, PB_9); 
CAN  can2(PA_11, PA_12);

creates two CAN objects, however, they are associated with the same CAN controller (they both represent the CAN1 controller). The NUCLEO-F103RB board is equipped with only one CAN peripheral but fortunately there are available many other Mbed borads equipped with two or more CAN controllers to chose from.

  • As Jan says, also check your wiring - compare with the schematics available here.

Hello Zoltan, Thanks for the reply. But, it's not working. My board1 was able to send the first data after 2000ms then after that board2 was not able to receive data as a result of which no further connection is established between the boards. I measured the voltages of CAN_H and CAN_L lines, both were at 2.4V. I connected transceivers RX pin to NUCLEO RD pin & transceivers TX pin to NUCLEO TD pin. And remaining wiring also seems fine. What could be the reason for board2 not receiving data?

posted by Dheeraj Etta 11 Oct 2019
  • To avoid problems resulting from incompatible mbed library version, please do not update the mbed library when importing the example program into the online compiler.
  • For board #1 compile without any modification to main.cpp
  • For board #2 exclude (comment out) the line as below before compilation:

//#define BOARD1   1 
  • To start testing the communication try to reset both boards at the same time.
posted by Zoltan Hudak 12 Oct 2019
4 years, 6 months ago.

Hi there,

The MbedOS5 with bare metal profile is also usable for CAN's read method in the interrupt context.

However if I correctly understand what you wrote, you had crossed wires. The CAN is not like an UART, the TX pin from the Nucleo must be connected to the TX pin on the transceiver and same for the RX.

Best regards, Jan

You beat me to it. Check the datasheet for the CAN transceiver used but almost certainly the issue is a wiring problem. Tx on the CAN transceiver normally means data to transmit on to the CAN bus which means it's an input and should be connected to the Tx output from the processor.

posted by Andy A 11 Oct 2019