T4.1+CAN to robot actuator - need some perspective/advice

Status
Not open for further replies.

bschena

Active member
First, I'm new to CAN and am in the middle of trying to figure out the basics of how to use mailboxes, FIFOs and callbacks.

I wrote a long detailed post, but figured no one would read or respond, so here's the short(er) version.

I have the SKPang "Triple CAN" T4.1 board talking to a CAN2.0A/B robot actuator using FlexCAN. It is just those two things on a dedicated CAN bus. The message ID is always the same for all frames traded between the two nodes. My current setup works with the 1Mbps CAN baudrate.

Here's how I'm setting things up right now (though I still don't really understand the purpose of distribute() or mailboxStatus() )

Code:
void startCAN1( void ){
  can1.begin();
  can1.setBaudRate(1000000);   // 1Mbits/sec (1000000) works now with termination resistor
  can1.setMBFilter(ACCEPT_ALL);
  can1.distribute();
  can1.mailboxStatus();
  return;
}

For technical performance reasons, I need to be able to throw the frames at the actuator at at least 1000 packets/sec, ideally 4000-5000 packets/sec.

My current implementation simply uses can1.write(msgCMD); and can1.read(msgcan1); in a polling loop running flat out (un-timed) on the T4.1.

The most obvious problem is that I'm missing Reply frames from the actuator - I get many of one type, but miss about 99% of the others. I don't really understand why, though I believe that the way I am currently polling (e.g. code architecture) is likely my problem.

Here's what I think I know - please correct me if any of this is wrong:
1) I don't think that using Masking or Filtering would make any difference, since this is a dedicated bus and that all MessageIDs are always the same.
2) I think that I probably need to be using (at least) an RX Callback to grab the frame asynchronously, but I've not found an example yet that makes sense to me. TX callbacks conceptually make zero sense to me at the moment.
3) I think I conceptually understand Mailboxes, but I still don't really understand the purpose of the CAN FIFO, or if that is something I should be using, or even if the polling approach I am using actually using the FIFO, just without my knowledge?
4) My understanding is that a Mailbox can only contain one message? Does that message get overwritten when the next arrives? Is there a buffer/queue behind the scenes, stacking frames up? (Doesn't seem so as I'm losing frames.)
5) Can you use multiple mailboxes to create a sequential message queue of sorts? Is that a dumb way to think about/use Mailboxes, if that is even possible?
6) Can anyone point to any "CAN Mailboxes & Callbacks for Dummies" examples to look at?

I've read as much as I can find on this site, read lots of Readmes and examples, and watched a lot of YouTube videos. Most of what I've found are either very basic (e.g. CAN frame format) or very automotive-related (not relevant it seems).

I feel like I'm kind of stuck in a place where I'm struggling to learn more but not finding the right material, advice, or examples.

Happy to provide more details if anyone is interested. I'd be grateful for any thoughts/observations/pointers/suggestions/examples.

Thanks!

Bruce
 
there is a FIFO interrupt example that just works on the repo, just make sure you select the right bus in the constructor (CAN1,CAN2,CAN3)

1) masking not needed
2) need interrupt and callback
3) fifo has ordered receptions, using either in your case doesnt matter
4) only the last mailbox gets overridden on flexcan hardware, polling is guarenteed to lose frames
5) FIFO is sequential reception, for ordered transmits, use msg.seq = 1 before writing
6) fifo interrupt example on github repo
 
there is a FIFO interrupt example that just works on the repo, just make sure you select the right bus in the constructor (CAN1,CAN2,CAN3)

1) masking not needed
2) need interrupt and callback
3) fifo has ordered receptions, using either in your case doesnt matter
4) only the last mailbox gets overridden on flexcan hardware, polling is guarenteed to lose frames
5) FIFO is sequential reception, for ordered transmits, use msg.seq = 1 before writing
6) fifo interrupt example on github repo

Thanks tonton1, great answers as usual.

I just looked at your "CAN2.0_example_FIFO_with_interrupts" and made a simple version of my code to see what happens with that scheme.

The short version is that I'm still not getting all the frames.

Here are the important bits:

Code:
// =================================================================================================
// SETUP ===========================================================================================
// =================================================================================================
void setup(void) {
  Serial.begin(115200); delay(1000);
  startCAN1();
}

// =================================================================================================
// START CAN1 =======================================================================================
// =================================================================================================
void startCAN1( void ) {
  can1.begin();
  can1.setBaudRate(1000000);   
  can1.enableFIFO();
  can1.enableFIFOInterrupt();
  can1.onReceive(canSniff);
  can1.mailboxStatus();
  return;
}

// =================================================================================================
// CAN SNIFFER (unchanged from tonton1 original) ==================================================================
// =================================================================================================
void canSniff(const CAN_message_t &msg) {
  Serial.print("MB "); Serial.print(msg.mb);
  Serial.print("  OVERRUN: "); Serial.print(msg.flags.overrun);
  Serial.print("  LEN: "); Serial.print(msg.len);
  Serial.print(" EXT: "); Serial.print(msg.flags.extended);
  Serial.print(" TS: "); Serial.print(msg.timestamp);
  Serial.print(" ID: "); Serial.print(msg.id, HEX);
  Serial.print(" Buffer: ");
  for ( uint8_t i = 0; i < msg.len; i++ ) {
    Serial.print(msg.buf[i], HEX); Serial.print(" ");
  } Serial.println();
}

// =================================================================================================
// MAIN LOOP =======================================================================================
// =================================================================================================
void loop() {

  Serial.println("\n========================");
//  can1.events();

  Serial.println("Sending Status 1 (0x9A) request");
  sendStatus1Request();

  Serial.println("Sending Status 2 (0x9C) request");
  sendStatus2Request();

  Serial.println("Sending Status 3 (0x9D) request");
  sendStatus3Request();

  while (1){
  }

}

So, I fire off 3 different requests to the drive. Each is supposed to return a different status frame.

However, here's what I'm getting back:

Code:
FIFO Enabled --> Interrupt Enabled

	FIFO Filters in use: 8

	Remaining Mailboxes: 8

		MB8 code: TX_INACTIVE

		MB9 code: TX_INACTIVE

		MB10 code: TX_INACTIVE

		MB11 code: TX_INACTIVE

		MB12 code: TX_INACTIVE

		MB13 code: TX_INACTIVE

		MB14 code: TX_INACTIVE

		MB15 code: TX_INACTIVE


========================

Sending Status 1 (0x9A) request

Sending Status 2 (0x9C) request

Sending Status 3 (0x[B]9D[/B]) request

MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 524 ID: 141 Buffer: [B]9D[/B] 1B 0 0 0 0 0 0

So, I'm quite sure the packets are going out (I tested separately), and the callback to canSniff() seems to be working, but I'm still only getting the last "0x9D" packet response. I don't know who ate the first two....

Also, is the mailbox 99 (MB 99) an expected response?

I tried sprinkling some "can1.events();" here, there, and everywhere, but their presence/absence did nothing at all.

See anything dumb?
 
MB99 is virtual, it's actually FIFO, check if your bus is properly terminated?

i dont see your sendStatusxRequest functions so I dont know for that part
 
MB99 is virtual, it's actually FIFO, check if your bus is properly terminated?

i dont see your sendStatusxRequest functions so I dont know for that part

Yes, bus is terminated at both ends with 120ohms.

Example code for the status requests (all 3 functions are essentially identical except for the hex code that is passed)
Code:
// =================================================================================================
// SEND REQUEST FOR STATUS FRAME #3 ================================================================
// =================================================================================================
void sendStatus3Request( void ) {
  RMD_send1ByteCommand( MOTOR_1, RMD_READ_MOTOR_STATUS_3 );
}

which uses:

Code:
// =================================================================================================
// SEND 1 BYTE COMMAND (NO PASSED PARAMETERS) ======================================================
// =================================================================================================

void RMD_send1ByteCommand( int motorNum, int hex_cmd_code ) {
  CAN_message_t msgCMD;

  msgCMD.id = 0x140 + motorNum;  // add the target node number to 0x140
  msgCMD.buf[0] = hex_cmd_code;  // 0x9A, 9C, or 9D

  for ( uint8_t i = 1; i < msgcan1.len ; i++ ) {
    msgCMD.buf[i] = 0;
  }
  can1.write(msgCMD);
}


EDIT:

And I just commented out the other two command requests (0x9A and 0x9C) just to prove to myself that the drive is responding to them with a reply frame - and it is:

Code:
FIFO Enabled --> Interrupt Enabled

	FIFO Filters in use: 8

	Remaining Mailboxes: 8

		MB8 code: TX_INACTIVE

		MB9 code: TX_INACTIVE

		MB10 code: TX_INACTIVE

		MB11 code: TX_INACTIVE

		MB12 code: TX_INACTIVE

		MB13 code: TX_INACTIVE

		MB14 code: TX_INACTIVE

		MB15 code: TX_INACTIVE


========================

Sending Status 1 (0x[B]9A[/B]) request

MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 259 ID: 141 Buffer: [B]9A[/B] 1C 0 DF 1 0 0 0

and

Code:
FIFO Enabled --> Interrupt Enabled

	FIFO Filters in use: 8

	Remaining Mailboxes: 8

		MB8 code: TX_INACTIVE

		MB9 code: TX_INACTIVE

		MB10 code: TX_INACTIVE

		MB11 code: TX_INACTIVE

		MB12 code: TX_INACTIVE

		MB13 code: TX_INACTIVE

		MB14 code: TX_INACTIVE

		MB15 code: TX_INACTIVE


========================

Sending Status 2 (0x[B]9C[/B]) request

MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 262 ID: 141 Buffer: [B]9C[/B] 1C 0 0 0 0 F3 C6

Is it possible to try to whack/overwrite earlier messages in the TX queue if they get thrown at the FlexCAN_t4 before it has a chance to send the frame?
 
Last edited:
not really, does the motor perhaps requires a delay between requests (like car's do?) try to put a small delay between each request. If you wamna know for sure if it's a transmit issue, have another node on the network log the frames, if all 3 frames are there, then the motor needs a time between frames

oh and please try the updated library on github
 
have another node on the network log the frames

Hi Bruce, you may consider buying a cheap CANbus analyzer like this one. This device saved me a lot of time while implementing & debugging a wireless CAN bus.
Here is a thread with some screenshots of the analyzer software in action.

Regards,
Paul
 
Interesting.

I added a progressively shorter delay in between the sends and I can see it starting to choke:

Code:
void loop() {
  int delayTime = 100;

  Serial.println("\n========================");

  Serial.println("Sending Status 1 (0x9A) request");
  sendStatus1Request();
  delayMicroseconds(delayTime);

  Serial.println("Sending Status 2 (0x9C) request");
  sendStatus2Request();
  delayMicroseconds(delayTime);
  
  Serial.println("Sending Status 3 (0x9D) request");
  sendStatus3Request();
  delayMicroseconds(delayTime);
}
at 100microseconds I get this (no responses):
Code:
========================

Sending Status 1 (0x9A) request

Sending Status 2 (0x9C) request

Sending Status 3 (0x9D) request

at 200microsconds I get this (2 out of 3 per loop):

Code:
Sending Status 1 (0x9A) request

Sending Status 2 (0x9C) request

MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 154 ID: 141 Buffer: 9A 1C 0 DF 1 0 0 0 

Sending Status 3 (0x9D) request

MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 406 ID: 141 Buffer: 9C 1C 0 0 0 0 F3 C6

I start to get all 3 responses back at 350uS, though not always in the same spot relative to the loop() execution.

Code:
========================

Sending Status 1 (0x9A) request

MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 167 ID: 141 Buffer: 9A 1C 0 DF 1 0 0 0 

Sending Status 2 (0x9C) request

MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 542 ID: 141 Buffer: 9C 1C 0 0 0 0 F3 C6 

Sending Status 3 (0x9D) request

MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 917 ID: 141 Buffer: 9D 1C 0 0 0 0 0 0 


========================

Sending Status 1 (0x9A) request

MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 1291 ID: 141 Buffer: 9A 1C 0 DF 1 0 0 0 

Sending Status 2 (0x9C) request

Sending Status 3 (0x9D) request

MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 1666 ID: 141 Buffer: 9C 1C 0 0 0 0 F3 C6 

MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 1916 ID: 141 Buffer: 9D 1C 0 0 0 0 0 0
 
2 sends before response, i think the motor needs a time gap between commands, not sure. I know for cars if you write too fast they also stop responding for a bit, and car's gap is usually 10ms (not uS) :)

the fact you got 2 responses after 2 commmands at 350uS, means the motor didnt respond right away, due to processing the movement first i would imagine

maybe its mentioned the minimum request time in the datasheet of the motor

the other thing is maybe the motor needs to reposition before responding

(1 second) / (350 microseconds) = 2 857.14286

thats over a thousand per sec, almost 3k :)
 
2 sends before response, i think the motor needs a time gap between commands, not sure. I know for cars if you write too fast they also stop responding for a bit, and car's gap is usually 10ms (not uS) :)

Right, makes sense. I just sent an email to the motor vendor asking about the maximum rate I can send updates to it.

Is there an expected (or tested) speed limit of FlexCAN before Bad Things start to happen, like losing frames? I assume that there is some fundamental limit of the CAN Silicon.

I have not tried to change the CAN clock rate (setClock()?) on the T4.1 - would you expect that to change anything?
 
it wont change the bus activity, but it will probably make controller more responsive, you probably wouldn't notice. It's also used for different baudrates that don't reliably work at X MHz since bitrate is calculated off it to. You can flood the bus with frames if you wanted, it should be fine, I use mine as a gateway CAN1 to CAN3 and CAN1 and CAN2 are main busses, all 3 on interrupt mode, on same board as you, but 4.0 version of it
 
thanks tonton81.

quick question, where is the right place to call setClock()?

will this sequence work, or should I be calling setClock() before begin() - or someplace else?

Code:
  can1.begin();
  can1.setBaudRate(1000000);   // start with 500kbits/sec (500000), 1Mbits/sec (1000000) works now with termination resistor
  can1.setClock(CLK_60MHz);
  can1.enableFIFO();
  can1.enableFIFOInterrupt();
  can1.onReceive(canSniff);
  can1.mailboxStatus();
  return;
}
 
Thanks Paul, I'll look into that CANbus analyzer....that would probably explain a *lot*.

EDIT: It's on it's way!
 
Status
Not open for further replies.
Back
Top