Go Down

Topic: CAN options (Read 868 times) previous topic - next topic

weird_dave

I'm toying with the idea of trying some CAN on the due. There appears to be 2 main options:
1) use the MCP2515/2551 combo. The 2515 looks 3v3 compatible but the 2551 doesn't.
2) use the CAN controller in the Due.

(1) might not be possible, has anyone tried it? I would have though this would be the easier method if it works.

I would want to be able to read various CAN messages, some at 10ms intervals, others slower. I would also want to send some at regular intervals (probably 10ms for the fastest). Is this feasible with either option? (I'm open to other options too).

Please assume I have zero CAN experience :)

AdderD

You might as well use the CAN controller built into the Due. You will need to provide a transceiver still. The 2551 transceiver is 5V and isn't likely to work properly with a Due. It might fry the Due if you provide it was 5V as it'll try to use 5V signalling back to the Due. Your better bet is the SN65HVD230 series transceivers. There are several choices - SN65HVD234 is a good choice.

Then, I maintain a CAN bus library for the Due: https://github.com/collin80/due_can

With that you should be on your way. You must keep in mind that CAN buses need to be terminated on both sides with 120 ohms of resistance. If your bus does not measure 60 ohms of resistance (or close) then you don't have it right yet. Obviously do this check with all the connected nodes turned off.

weird_dave

So it's not worth offloading the bitstuffing etc...? I've been using the wiznet 5500 for Ethernet and have got used to how easy going it is on Due resources :)
I have a small engine I want to play with, a mate is looking at using some off the shelf CAN kit but I'm not convinced it will be flexible enough, so the Due is my backup plan :D

AdderD

The SAM3X processor in the Due has hardware CAN so really bitstuffing and all that are handled for you and basically for free. There's dedicated hardware specifically to implement CAN. The due_can library is interrupt driven and buffered. You won't use hardly any resources to do it on-chip and you get the benefit that you're extraordinarily unlikely to miss any frames because they're coming right into the chip and being decoded and stored by dedicated on-chip hardware. It's usually a lot harder to get good performance out of an off-chip solution - it adds overhead to have to talk to the external chip over SPI.

There are lots of off the shelf CAN kits that are decent. There are lots that aren't. You could be OK using something purpose built for what you want to do. Sometimes nothing quite does what you want and you end up rolling your own. The Due is a decent base for such work.

weird_dave

OK, I'm convinced the built-in port is the way forward with regards to the Due, looks like I'll be ordering a PHY IC tomorow :)

weird_dave

I decided to take a peek at some of the CAN examples and I think I want to setup 4 filters, one for each of the CAN messages I'm interested in. However, the example usage of setRXFilter in CAN_SnooperCallback.ino has me a little confused, line 73:
Can0.setRXFilter(0, 0x2FF00, 0x1FF2FF00, true);
What is the result of the mask, how was it constructed? I took a look at CANRaw::mailbox_set_accept_mask, but I couldn't work out what this meant.

AdderD

I decided to take a peek at some of the CAN examples and I think I want to setup 4 filters, one for each of the CAN messages I'm interested in. However, the example usage of setRXFilter in CAN_SnooperCallback.ino has me a little confused, line 73:
Can0.setRXFilter(0, 0x2FF00, 0x1FF2FF00, true);
What is the result of the mask, how was it constructed? I took a look at CANRaw::mailbox_set_accept_mask, but I couldn't work out what this meant.
When a message comes in it goes through all of the filter/mask combos to see if any match. First it does a boolean AND with the mask. Then it compares that to the filter ID value. If it matches then the frame is accepted. If not the rest of the filters are checked. If none match then the frame is not accepted.

So, if you set an RX filter it's first AND with mask then compare to ID.

Thus, take the example above. Lets say a frame with ID 0x2FF4D comes in. You AND that with 0x1FF2FF00 and get 0x2FF00. You compare this to 0x2FF00 and see that it matches. The frame is accepted. Now, a frame with ID 0x12FF00. AND with 0x1FF2FF00 and you get 0x12FF00. This does not equal 0x2FF00 and so is not accepted.

If you want to accept only a single ID then the mask is either 0x7FF (for standard frames) or 0x1FFFFFFF for extended frames. Then set the ID to the one you want to let through. If you want to accept multiple frames then you need to figure out an AND mask that yields the frames you want while excluding as many as possible that you don't want. This is something harder to explain how to do.

weird_dave

OK, that makes sense. I think I was confused because the mask is strange, if a frame with ID 0x0003FF00 came, it would match, as the masking result would be 0x0002FF00, which might be intentional, but an odd example. I would have used a mask of 0x1FFFFF00, giving a range 0x0002FF00-FF. The example mask gives a horrible set of disjointed ranges, the '2' in the mask allows '2', '3', '6', '7', 'A', 'B', E' and 'F'.

AdderD

You're right, the mask would allow through a wide range of values that perhaps people would not expect. It should probably be changed to be more restrictive. But, since you caught that it certainly appears that you understand how filtering works. So, that's a bonus.

weird_dave

Well, I was starting to think there was some new fangled masking technique that I didn't understand, so I appreciate the clarity.

srolf

#10
Apr 26, 2017, 07:04 am Last Edit: Apr 26, 2017, 07:12 am by srolf
The library provided by Collin80 is very good.

I'm using it for an example which uses both CAN controllers combined with transceivers of the type SN65HVD230. With that plus the Ethernet Shield V2 I created a box which acts as router between both CAN segments and addtionally one segment uses the ethernet connection as a kind of gateway for MQTT messages.

If you are doing someting like this, be little careful with the interrupt load and consider to use interrupt masking by noInterrupts() ... interrupts() in case you are using shared variables.

weird_dave

I am doing something similar, 2 SAM3X, one for CAN and one for Ethernet, FPGA (Spartan 6LX25) sits between them doing all the decision making (there's a lot of other kit connected to it). SPI transfer under control of the SAM3X swaps data to/from the FPGA.
I could put the Ethernet and CAN on one SAM3X, but why bother when you can have 2 :D (ease of making the software behave trumps cost in my case, so separating the 2 protocols works for me)

weird_dave

Well, I've finally got around to having a play with CAN but I seem to be having a little trouble setting myFrame.data:

Code: [Select]
error: no match for 'operator=' (operand types are 'BytesUnion' and 'CAN_Data')

    myFrame.data = From_FPGA_Data.KSM1;


I have some structs setup, which may be the cause of my issues (I've never used unions before)
Code: [Select]
struct CAN_Data
{
  uint8_t data[8];
};

struct SPI_Data
{
  char data[34];
};


union FPGA_Rx_struct
{
  struct
  {
    CAN_Data KSM1;
    CAN_Data Time;
    char FPGA_SPI_Tx_NULL[18]; //padding not really needed in this union
  };
  struct //FPGA_SPI_Bulk_Tx
  {
    SPI_Data data;
  };
};


FPGA_Rx_struct From_FPGA_Data;


I have figured out that I can use BytesUnion instead of CAN_Data (well, it compiles anyway), is that the only option? (#define CAN_Data BytesUnion perhaps? Seems to compile too).

AdderD

Yeah the bytes union is there just to provide a big range of ways to access the bytes. You can do things like

frame.data.byte[0] = 0;
frame.data.s0 = 0x2353; //set 16 bit value directly into bytes 0 and 1
frame.data.value = 0x3445839538435375ull; //set all 8 bytes at once

So, there's options. But, internally it's really an array of 8 bytes so if you cast it as such it would still work.

weird_dave

So why can't a transfer all 8 bytes in manner in which I have tried in the posted code (the first bit with the error)?
I also tried to compile with:
Code: [Select]
myFrame.data.bytes = From_FPGA_Data.KSM1.data;
but that fails too:
 error: invalid array assignment

Obviously i could use a for loop, but I think there is a neater way.
Code: [Select]
#define CAN_Data BytesUnion
works fine in place of my CAN_Data struct but I don't think this is good coding practise. This really should be easy, it's just a bunch of bytes :(

Go Up