Building a CAN API for Arduino DUE

I think the Atmel designers have made the choice for me. I was leaning towards the 234 anyway because of the EN function that allows me to save power, then I remembered that CANRX1 shares a pin with DAC0 so it's important to be able to tri-state CANRX1 if you are using the DAC.

So it's the 234 for me, no choice unless I add a FET switch or something.

If the library doesn't support controlling the EN pin (it should, we all want to save power :)) then I'll cross that bridge when I come to it.


Rob

Graynomad:
If the library doesn't support controlling the EN pin (it should, we all want to save power :)) then I'll cross that bridge when I come to it.

It does use the enable pin. But, right now as soon as CAN and CAN2 are instantiated they immediately enable the transceiver and leave it enabled. So, the library basically assumes you're using a 234 chip. This has been accurate for the most part. Since they're instantiated automatically for you this means that the current library forces the relevant pins to be CAN and enables the transceiver forever. Of course, not everyone wants this behavior so in the future it should be possible to turn off the canbus hardware if required. This is not hard to do at all. I think that the default behavior of automatically turning everything on is still the way to go. If you don't want that you can not include the library in which case it won't do that. :wink: But, yes, on the to-do list is being able to disable and re-enable the hardware.

In the github CAN samples 1 & 2, I used four pins to control the two transceivers as follows:

CAN0 Transceiver

  • pin A7 (D61) to enable/disable the lowpower mode.
  • pin A8 (D62) to enable/disable the transceiver.

CAN1 Transceiver

  • pin A9 (D63) to enable/disable the lowpower mode.
  • pin A10 (D64) to enable/disable the transceiver.

But you can choose the pins that best suits your needs. For example, let's say you want to use pin 46 (D46) to enable the lowpower mode in the CAN1 transceiver, you have to use the following code:

SN65HVD234_SetRs(&can1_transceiver, 46);
SN65HVD234_EnableLowPower(&can1_transceiver);

Or, if you want to use pin A4 (D58) to restore normal lowpower mode (disable) in the CAN0 transceiver, you have to use the following code:

SN65HVD234_SetRs(&can0_transceiver, 58);
SN65HVD234_DisableLowPower(&can0_transceiver);

Similar case to enable or disable the transceivers. For example, you want to use pin 14 (D14) to disable the CAN0 transceiver, you have to use the following code:

SN65HVD234_SetEN(&can0_transceiver, 14);
SN65HVD234_Disable(&can0_transceiver);

I hope that the foregoing clarify a bit more the use of the control pins for the CAN transceivers.

EDIT: Of course, the four pins you choose and add to your sketch shall be connected to the transceivers pins EN ans Rs accordingly.

Sounds like it's covered. You didn't use a resistor on RS in the post #33 schems, I think I'll add one, value TBD.


Rob

Graynomad:
Sounds like it's covered. You didn't use a resistor on RS in the post #33 schems, I think I'll add one, value TBD.


Rob

Hello Rob,

At this time, this is a very good point. Thanks for mention it because it allows me to clarify something:

Knowing that the most important thing in the first stages of this project was to establish a good communication between nodes, I forced a 'direct mode' in the transceivers connecting pins 5(EN) to 3V3 and pins 8(Rs) to GND but now that some users out there have achieved this, their next step should be to deal with the connection/control of those pins if they are interested to exploit the low power mode an other features of these transceivers, thus, a 10K pullup resistor must be connected on EN and a 10K pulldown resistor on Rs according to Atmel's original design (doc11156).

Collin80:
It seems to me that the only thing you're really lacking is that you forgot to pull the trigger at the end. You have to tell it to send those mailboxes once you load them. You can do this one at a time, stagger them, or however you wish. If you want to try to tell it to send all of the mailboxes as fast as it can then do this:

  CAN.global_send_transfer_cmd(CAN_TCR_MB0 | CAN_TCR_MB1 | CAN_TCR_MB2 | CAN_TCR_MB3 | CAN_TCR_MB4);  //send frame in mailbox 0-4

I should probably abstract that a bit. It looks kind of ugly as-is.

I made the change - here is my code again. I still am only getting out on the first TEST1_CAN_TRANSFER_ID :frowning:

Thank you!

#include <Wire.h>
#include "variant.h"
#include <due_can.h>

#define TEST1_CAN_COMM_MB_IDX 0
#define TEST1_CAN_TRANSFER_ID 0x07
#define TEST2_CAN_TRANSFER_ID 0x08
#define TEST3_CAN_TRANSFER_ID 0x08
#define TEST4_CAN_TRANSFER_ID 0x09
#define TEST5_CAN_TRANSFER_ID 0x0A
#define TEST1_CAN0_TX_PRIO 15
#define CAN_MSG_DUMMY_DATA 0x55AAAA55

// CAN frame max data length
#define MAX_CAN_FRAME_DATA_LEN 8

// Message variable to be send
uint32_t CAN_MSG_1 = 0;
uint32_t CAN_MSG_2 = 0;
uint32_t CAN_MSG_3 = 0;
uint32_t CAN_MSG_4 = 0;
uint32_t CAN_MSG_5 = 0;
uint32_t CAN_MSG_6 = 0;
uint32_t CAN_MSG_7 = 0;
uint32_t CAN_MSG_8 = 0;
uint32_t CAN_MSG_9 = 0;
uint32_t CAN_MSG_10 = 0;
uint32_t CAN_MSG_11 = 0;
uint32_t CAN_MSG_12 = 0;

// Initialize CAN1 mailbox 0 as receiver, frame ID is 0x07
CAN.init(SystemCoreClock, CAN_BPS_250K);
CAN.reset_all_mailbox();
// Initialize CAN0 mailbox 0 as transmitter, transmit priority is 15
CAN.mailbox_init(0);
CAN.mailbox_init(1);
CAN.mailbox_init(2);
CAN.mailbox_init(3);
CAN.mailbox_init(4);
CAN.mailbox_set_mode(0, CAN_MB_TX_MODE);
CAN.mailbox_set_mode(1, CAN_MB_TX_MODE);
CAN.mailbox_set_mode(2, CAN_MB_TX_MODE);
CAN.mailbox_set_mode(3, CAN_MB_TX_MODE);
CAN.mailbox_set_mode(4, CAN_MB_TX_MODE);
CAN.mailbox_set_priority(0, TEST1_CAN0_TX_PRIO);
CAN.mailbox_set_priority(1, TEST1_CAN0_TX_PRIO);
CAN.mailbox_set_priority(2, TEST1_CAN0_TX_PRIO);
CAN.mailbox_set_priority(3, TEST1_CAN0_TX_PRIO);
CAN.mailbox_set_priority(4, TEST1_CAN0_TX_PRIO);

CAN.mailbox_set_accept_mask(0, 0, false);
// Prepare transmit ID, data and data length in CAN0 mailbox 0
CAN.mailbox_set_id(0, TEST1_CAN_TRANSFER_ID, false);
CAN.mailbox_set_datal(0, CAN_MSG_1);
CAN.mailbox_set_datah(0, CAN_MSG_2);
CAN.mailbox_set_datalen(0, MAX_CAN_FRAME_DATA_LEN);

CAN.mailbox_set_id(1, TEST2_CAN_TRANSFER_ID, false);
CAN.mailbox_set_datal(1, CAN_MSG_3);
CAN.mailbox_set_datah(1, CAN_MSG_DUMMY_DATA);
CAN.mailbox_set_datalen(1, MAX_CAN_FRAME_DATA_LEN);

CAN.mailbox_set_id(2, TEST3_CAN_TRANSFER_ID, false);
CAN.mailbox_set_datal(2, CAN_MSG_5);
CAN.mailbox_set_datah(2, CAN_MSG_6);
CAN.mailbox_set_datalen(2, MAX_CAN_FRAME_DATA_LEN);

CAN.mailbox_set_id(3, TEST4_CAN_TRANSFER_ID, false);
CAN.mailbox_set_datal(3, CAN_MSG_7);
CAN.mailbox_set_datah(3, CAN_MSG_8);
CAN.mailbox_set_datalen(3, MAX_CAN_FRAME_DATA_LEN);

CAN.mailbox_set_id(4, TEST5_CAN_TRANSFER_ID, false);
CAN.mailbox_set_datal(4, CAN_MSG_9);
CAN.mailbox_set_datah(4, CAN_MSG_DUMMY_DATA);
CAN.mailbox_set_datalen(4, MAX_CAN_FRAME_DATA_LEN);

// Send out the information in the mailbox
CAN.global_send_transfer_cmd(CAN_TCR_MB0);
CAN.global_send_transfer_cmd(CAN_TCR_MB1);
CAN.global_send_transfer_cmd(CAN_TCR_MB2);
CAN.global_send_transfer_cmd(CAN_TCR_MB3);
CAN.global_send_transfer_cmd(CAN_TCR_MB4);

CAN.disable();

zabaat:
I made the change - here is my code again. I still am only getting out on the first TEST1_CAN_TRANSFER_ID :frowning:

Thank you!

// Send out the information in the mailbox
CAN.global_send_transfer_cmd(CAN_TCR_MB0);
CAN.global_send_transfer_cmd(CAN_TCR_MB1);
CAN.global_send_transfer_cmd(CAN_TCR_MB2);
CAN.global_send_transfer_cmd(CAN_TCR_MB3);
CAN.global_send_transfer_cmd(CAN_TCR_MB4);

CAN.disable();

Why are you disabling the canbus device as soon as you try to send? Those sending commands are just flags that tell the canbus hardware to send out that mailbox when it gets a chance. Likely what is happening is that you send the commands, the bus is free so it starts sending the first frame. Then you disable the hardware so the other four never get a chance to go out. You've got to give it some time to send the frames before you disable the canbus hardware. Figure that a frame could be about 118 bits and then use your baud rate to see how long the minimum time to wait is. At 250k baud this is about .5ms per frame.

I've updated the Arduino "can" branch with https://github.com/collin80/due_can
PM me if there are other changes needed.

Got my prototype pcb's in and built one up.....seems to have passed ping-pong and skew testing (thanks for writing those tests btw) although i was using a short piece of cable and doing nothing else on the board. I used 233's with jumpers for 120ohm terminations, RS pin (10k on board), and loopback. I like the 233's w/loopback an example use would be if you are doing a project in which you have 2 CAN ports operating at two different baud rates, loopback lets you do some code testing...just my preference. Looking to do some OBD testing next, Feedback appreciated.

Found a painful issue putting the SPI stacking header on....because the DUE has a male header on it the SPI sits higher when stacked than the rest of the headers. This caused some pins to only have partial...of course because i was doing a CAN project in this occurred on the CAN pins! Not sure what i'm going to do about that, maybe for SPI use i have to recommend removal and swapping the DUE to a female stacking header as well. Feedback appreciated.

Hi all, CAN and arduino noob here...
Fisrt, thank you for the great job done, it always makes me happy seeing so much people involved in open projects :slight_smile:
I try to make up my mind for a project that uses CAN to talk to pioneer IP-Bus.
Am I restricted to SN65HVD234 transceivers in order to use this library? Or can I use a MCP2551-I/P (to be able to use a breadboard and eventually simple soldering)? Should I write a driver for it?
Thanks for the attention.

sanzoghenzo:
Am I restricted to SN65HVD234 transceivers in order to use this library? Or can I use a MCP2551-I/P (to be able to use a breadboard and eventually simple soldering)? Should I write a driver for it?
Thanks for the attention.

You can use a tuning fork, a ball of twine, and a pair of jumper cables if you want. The transceiver takes a TTL signal and turns it into a differential signal for canbus. Use whichever transceiver you want. The reason there is a driver file for the 234 transceiver is that it supports being turned on/off under MCU control. Otherwise the canbus code doesn't need to worry about what the transceiver does at all.

Having said that, the MCP2551 is a 5V device. The Due is a 3.3V device. This is not a match made in Heaven. The MCP2551 will probably accept the TX signal coming from the Due as it will be close to 3.3V and that should be enough to trigger the MCP chip. However, the MCP will be sending back a 5V RX signal and that's bad for your Due's health. You can fix that with a voltage divider but it's extra parts. So, yes, you can use it if you know what you're doing.

The 2551 can be put to sleep from the MCU (pin 8 = RS pulled HIGH), but I think you would need voltage shifting to make it work reliably with the due. Ciao, Lenny

thank you so much for the information. better stick with the SN65HVD234 then!

Hi folks,

Great work and thanks a lot to all involved! Just wanted to let you know that I finished my build on a proto-board and (surprise !) it worked from the beginning (at least the local loop-back tests).

Here's a pic of my version. It's going to be used in a car conversion (see http://s80ev.blogspot.com)

Note: Although it says 5V on the print, I'm running it on 3.3V only. :slight_smile:

So far so good :slight_smile:

Thanks for the tip about being able to do this Adder
CAN.global_send_transfer_cmd(CAN_TCR_MB0 | CAN_TCR_MB1 | CAN_TCR_MB2 | CAN_TCR_MB3 | CAN_TCR_MB4);

Worked perfectly!

Zabaat,

What are those nice robust-looking connectors on your last photo?

Ciao,
Lenny

Hi guys, thanks for all the effort in making this work!
I have been playing with CAN on the Due and have the basics figured out. I am struggling with one thing though... I can send and receive just fine but I need to receive a CAN message and assign its value (in HEX obviously) to a variable. I have been able to use incoming.data[] with some success but assume there is a better way to do this. I would like to assign all 8 bytes to a single variable but understand some limitations to that idea. Sorry for asking a noob question here but its only been a few weeks since I saw the arduino for the first time so I'm in a bit over my head :~ Just for fun I attached a pic of my breadboard setup and am also working toward manufacturing shields for this...
Thanks in advance for any help!

Hi there,

I did some research for CANopen OpenSource libraries and found the canfestival.org. That seams to have a very active community. canfestival also supports already AVR chips as noted on their "supported devices" page under CanFestival documentation.

I saw all the work you have already done for supporting CAN on the due. Great work, amazing.

Do you think it is feasible to integrate the canfestival into the Arduino IDE for Arduino Due and possibly also in Arduino Uno with the seeedstudio or sparkfun can shield?

Thx

Zabaat - is that a deutsch dtm series pcb mount connectors for use with the oem sealed enclosure they offer?

maxwest:
Do you think it is feasible to integrate the canfestival into the Arduino IDE for Arduino Due and possibly also in Arduino Uno with the seeedstudio or sparkfun can shield?

Hello maxwest,

As an integrator I could say Yes. Integrate canfestival (or any other third-party CANopen protocol like CanOpenNode, etc.) to Due or UNO should be feasible. The problem lies that between CAN bus and CANopen there is an in-construction bridge of missing layers to be built. So far, I'm not aware of any software (code) implementation that fills the gap. Again, it can be done and collin (reply #152) and me have made comments about this matter but let me explain in a dossier this thing.

CAN bus and CANopen (an other CANs) are OSI bus standards to communicate embedded control systems like MCUs between them without an intermediate host.

CAN bus is the lowest level link in the chain and it comprehends basically three layers or profiles.

  1. Physical layer. Implemented with a transceiver (SN65HVD234 in my design)
  2. Data Link layer. Implemented with a controller (inside SAM3X8E)
  3. Application Layer. Implemented with software (our CAN library)

Layers 1 and 2 are known as CAN itself.

CAN open is a higher level protocol. It comprehends the first two layers of CAN bus plus other layers above, as follows:

  1. Application Layer (non existent for DUE) - communication - I/O - drives -motion control - programmable devices
  2. Transport Layer (software-reliability, segmentation/desegmentation, error control).

Thus, it all about to generate the layers 3 and 4.

Having said this, now you know, broadly speaking, what it is required to complete the bridge and let the train goes.

There is another thing: Time. How much time can you afford to reconcile canfestival and Due? Right now I am trying to make our new Arduino CAN and EMAC libraries to run in a third-party board based on a SAM3X8C. This MCU has only 100 pins unlike the MCU of the Due SAM3X8E with 144 pins and even though they belong to the same family "X", I have to so some tweak on the current variant files (.cpp and .h) and pin_arduino.h. See that my case is your opposite case, I am integrating a third-party board with CAN library (you want integrate a third-party library with DUE or UNO). My integration is not a big deal given that the differences between both MCUs is minimal. In your case, I would recommend to visit the canfestival community and ask questions. I would recommend also that you start with UNO (AVR) and a CAN shield. I hope this helps you. Good luck!