I'm trying out CAN communication. I have a mix of can devices and monitors available now. In this post, I'm building a device that keeps the CAN bus traffic high at all times, and can listen for particular traffic on that bus. Whatever anyone else is doing. |
I've been looking into microcontroller and Linux supported CAN devices for some time now. I can say I know enough to build a meaningful conversation between devices.
It's time to ramp up, and test traffic on a buzzy bus.
I used the Hercules CAN example to generate loads of traffic, and to be able to detect a particular message on that buzzy bus.
Nothing complex. It's actually a simplification of one of TI's examples for that controller.
What does the example do:
- it continuously puts a sequence of 4 packets of 8 bits on the bus. As fast as possible.
- it listens for a particular message id all the time
Very simple, but valuable in its simplicity.
It entertains the bus at all time. With four known datagrams.
When you are just interested in checking traffic in a highly loaded bus, that's all you need.
You can try and see if you can fish out all test packages. If you can ignore all or some. If you can deal with other traffic in that highly loaded bus.
You can use the Hercules as listener to see if you are successful in posting a message on the buzzy bus.
You can try and have a conversation with another device on the bus while the Hercules is spamming it.
Firmware
I used the simplest Hercules. TMS570LS04. It's not super fast and doesn't support DMA. But in this situation, it can act as a mean devil non-the-less.
TI released an interrupt based example for that controller. It uses one CAN peripheral for sending, a second one for listening.
A very good example that allows you to validate the data integrity on a bus. It's worth trying and learning from it.
I simplified the example (because I want a different, simpler but noisier, scenario):
- use the same CAN peripheral for sending and receiving. This saves one can tranceiver.
- keep on sending 4 known datagrams on the bus. Forever.
- listen to one particular message ID. Forever.
The program sets up CAN1 for interrupt driven communication, both ways.
box 1 will be used for sending messages with ID 1. We listen to box 2 for messages with ID 2.
For this exercise, I gave incoming traffic notification a higher priority than notification when a message is sent.
For showing that it can be done. Not really necessary here.
4 datagrams are prepared. They will be sent in a continuous loop.
int main(void) { /* enable irq interrupt in Cortex R4 */ _enable_interrupt_(); dumpSomeData(); // prepare data to load on the bus. 4 datagrams /** - configuring CAN1 MB1,Msg ID-1 to transmit and CAN1 MB2 to receive Msg ID-2 */ canInit(); /** - enabling error interrupts */ canEnableErrorNotification(canREG1); while(1){ tx_ptr = &tx_data[0][0]; /** - starting transmission */ for(cnt=0;cnt<D_COUNT;cnt++) { canTransmit(canREG1, canMESSAGE_BOX1, tx_ptr); /* transmitting 8 different chunks 1 by 1 */ while(tx_done == 0){}; /* ... wait until transmit request is through */ tx_done=0; tx_ptr +=8; /* next chunk ...*/ for (y=0; y<100; y++); // give some time to others/// } }; return 0; } /* writing some data to ram */ void dumpSomeData() { uint32 tmp = 0x11; cnt = (D_COUNT*8)-1; dptr = &tx_data[0][0]; *dptr = tmp; while(cnt--) { tmp = *dptr++; *dptr = tmp + 0x11; } }
The interrupt system will deal with a complete send or receive:
Because I used high and low priority interrupts in the CAN setup, I need to enable handlers for both.
In the end they will call the same code, shown below:
void canMessageNotification(canBASE_t *node, uint32 messageBox) { if((node==canREG1)&& (messageBox == canMESSAGE_BOX1)) // transmit box { tx_done=1; /* confirm transfer request */ } if((node==canREG1)&& (messageBox == canMESSAGE_BOX2)) // receive box { while(!canIsRxMessageArrived(canREG1, canMESSAGE_BOX2)); canGetData(canREG1, canMESSAGE_BOX2, rx_ptr); /* copy to RAM */ } }
The first if() flags that a transmit is complete. Firmare can start sending the next datagram. This entertains the bus continuously.
The second if()is to detect if we received a particular datagram (we are looking for one with ID 8, 8 bytes long).
I did not put any logic in there. I put a breakpoint so I can see if it fished a datagram with ID-2 out of the traffic.
You could blink a led, flip a GPIO, or do anything else you like to check that a particular message made it through the high traffic....
I prefer to keep it simple - only extend if I need to.
The program manages to spawn messages with less than 90 µs gaps (if you remove the for(y) loop) when it's alone on the bus.
The best I do can with the TMS570LS04 is to put a loop of y<100. That gives a 100 µs between two messages, and gives the controller enough time to deal with interrupts.
Any less, and the controller will not have time to deal with incoming traffic interrupts.
The Hercules controllers that support DMA don't have that issue, because they have plenty clock pulses free when communicating....
When there's other traffic, it will behave and deal with the bus arbitration. This design never shouts over another message.
How to Use?
If you just want traffic on your CAN bus, attach the tranceiver to CAN peripheral 1 pins, and connect to the bus.
It will generate traffic as soon as it's powered on.
If you want to see if your package made it through the buzzy bus, run the firmware with a debugger and put a breakpoint in the CAN interrupt (in the if() loop of message box 2).
If your message passed safely through the bus, you can then see it and inspect the data:
I use my Linux device here to generate a message with ID-2:
The firmware will break when the receive trigger fires. An API function retrieves the data from the CAN peripheral:
You can see that the data sent matches the data received....
The CCS project is attached, with HALCoGen configuration.
Top Comments