I'm trying out basic CAN communication on a Hercules microcontroller. This second test is sending data between two controllers. I'm using interrupts for reading and writing this time. It's not yet a complete CAN implementation. The physical CAN BUS components are missing.
|
Simplified CAN driver
CAN communication requires line drivers. The microcontrollers that I'm using have CAN peripherals but their pins are generic IO that operate on the logic 3.3V level.
I'm using a poor man's CAN driver that requires one resistor and two diodes. Check my 2014 blog TI Hercules LaunchPad - test the CAN with a poor man's CAN driver.
In the next blog I'll start to design the physical layer. I have 2 driver ICs at hand, but no schematic or PCB yet.
I'm using two Hercules LaunchPads. The smallest of the family, a TMS570LS04 and the biggest, the TMS570LC43.
The code for both is the same. Both send packages of data to each other and write what they receive to memory.
I'm using CAN peripheral 1 on both, mailbox 1 for sending, 2 for receiving.
The pinouts to get at those signals is shown in the images below.
Firmware
I've set a baud rate of 500 kBit/s.
The message boxes, different than in the previous blog, use unique IDs.
The small LaunchPad sends with ID 2 and listens to ID 1.
The big LaunchPad the other way around.
Because they are on the same bus, they should each read each others data and ignore their own messages.
The high priority interrupt for CAN1 is enabled on both.
The C code is straightforward. A few buffers and assorted other things are declared.
#define D_COUNT 8 uint32 cnt=0, error =0, tx_done =0; uint8 tx_data[D_COUNT][8] = {0}; uint8 rx_data[D_COUNT][8] = {0}; uint8 *tx_ptr = &tx_data[0][0]; uint8 *rx_ptr = &rx_data[0][0]; uint8 *dptr=0;
The main() function initialises CAN, enables interrupts and error handling.
/* enable irq interrupt in Cortex R4 */ _enable_interrupt_(); /** - writing a random data in RAM - to transmit */ dumpSomeData(); /** - configuring CAN1 */ canInit(); /** - enabling error interrupts */ canEnableErrorNotification(canREG1);
Then it goes on to write some random data. Reading doesn't happen in main(). It's done in the interrupt service routine.
the tx_done flag is set in the ISR too, when the controller calls the interrupt after transmission is completed.
/** - 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 ...*/ }
Here is that service routine.
#pragma WEAK(canMessageNotification) 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 */ rx_ptr +=8; } }
The first part handles the write interrupt. The full reading functionality is performed in the second part.
This simple program does nothing more than sending a few chunks of random data to each other, but that's ok for now.
It's a proof that two separate CANs can talk over a wire. A good start for the design with real physical CAN bus drivers.
Here's a capture of traffic generated by this setup:
Top Comments