Canctl
Note: | The documentation below has been deprecated for new products using tsctl. For the updated canctl documentation please click here. |
Overview
TS provides the utility "canctl" to both serve as sample C source code on how to send/receive CAN packets and as a simple utility to perform useful and common functions with the CAN bus. This utility, like xuartctl, also provides a simple network service with the --server option that will listen on TCP port 7552 and allow local or remote applications to send and receive CAN packets.
The routines can_peek8(), can_poke8(), can_rxpacket(), and can_loadtx() use the SBUS peek/poke routines to access SJA1000C registers through the memwindow core at SBUS address 0x50. Please see the canctl.c source code at https://files.embeddedTS.com/ts-arm-sbc/ts-7500-linux/samples/samples-aug052010/canctl/canctl.c for their definition. As with all TS released sample code, the restrictions of the common open source "GPL" license does not apply and customers may use, modify, or copy it in any form and have the freedom to keep modifications private and proprietary i.e. you will not be forced to open source derivative works.
Usage
To start the CAN network service, invoke canctl with the --server option:
canctl --server
The canctl application itself can be a client of any canctl server running on the network. When compiling canctl for a platform which lacks the TS-CAN hardware, use the -DREMOTE_ONLY flag to the compiler to indicate that the executable is to be compiled as a client only. The supplied Makefile defines this flag when the environment variable CLIENT is defined. This has been tested on an X86 Linux box - the following command builds canctl for client use:
CLIENT=1 make
To invoke canctl as a client, specify the --port option. This option requires an argument which is the IP name or address of the server. (The canctl server uses TCP/IP port 7552.) All subsequent command-line arguments will communicate with the specified canctl server using the CAN network service.
Examples
Send a CAN packet with the data 01:02:03:04:05:06 (it is assumed that the canctl server is running on this board) with IP address 192.168.1.100:
canctl --port=192.168.1.100 --txdat=01:02:03:04:05:06
Programming with CAN
CAN from userspace
Talking straight to the CAN hardware registers to implement CAN is also not that complicated and fully supported by userspace application code. The CAN controller used in the FPGA is an improved SJA1000C-compatible FPGA opencore available from the open source hardware site at opencores.org. TS has made several bug fixes as well as extended the depth of the receive FIFO to 2kbytes, but is still compatible with all the register level documentation one can find on nxp.com regarding the discrete SJA1000C chip that has been available and has been the reference CAN controller for many years. For example, initializing the CAN controller from userspace:
sbuslock();
can_poke8(0, 0x1); /* Enter reset mode */
can_poke8(31, 0x80); /* Enable PeliCAN mode */
can_poke8(4, 0xff); /* All interrupts enabled */
can_poke8(6, 0); /* BTR0 for 500kbps */
can_poke8(7, 0x7e); /* BTR1 for 500kbps */
can_poke8(13, 96); /* Error warning limit */
can_poke8(14, 0); /* Reset RX error counter to 0 */
can_poke8(15, 0); /* Reset TX error counter to 0 */
can_poke8(20, 0xff); /* Acceptance msk disabled (all packets rx'ed) */
can_poke8(21, 0xff);
can_poke8(22, 0xff);
can_poke8(23, 0xff);
can_peek8(3); /* Clear pending interrupts */
can_poke8(0, 0); /* Enter operational mode */
sbusunlock();
Sample C code to transmit a CAN packet with CAN id in an int variable 'txid' and 0-8 bytes of data in a char array 'txdat' of length 'txdat_len'. 'txrtr' is set if the CAN packet is to be a CAN RTR packet instead of a data packet:
sbuslock();
can_loadtx(txid, txdat, txrtr ? CAN_RTR : txdat_len);
can_poke8(1, 0x1); /* TX command */
do {
sbusunlock();
can_irqwait(); /* pauses until IRQ trig'd */
sbuslock();
j = can_peek8(2); /* Poll status */
can_peek8(3); /* Clear pending interrupts */
} while ((j & 0x44) == 0);
sbusunlock();
Sample C source code to receive a CAN packet in a 16-byte char array 'tmp':
can_irqwait(); /* block until IRQ trig'd */
sbuslock();
j = can_peek8(3); /* read IRQ */
if (j & 0x1) { /* RX packet IRQ? */
can_rxpacket(tmp);
can_poke8(1, 0x4); /* Release receive buffer */
}
if (j & 0x8) can_poke8(1, 0x8); /* clear possible data overrun */
sbusunlock();
At the end of the above RX packet routine, 'tmp' is a char buffer that has been filled to contain the CAN packet in the buffer format described on page 39 of the SJA1000 datasheet [here].
The routines above make use of userspace IRQ support. This is not standard in mainstream Linux 2.6 kernel sources and requires a patch. This patch is available for download at https://files.embeddedTS.com/ts-arm-sbc/ts-7500-linux/sources/cavium-userspace-irq.patch but should already be present in default TS kernel binaries. The functionality this provides is a mechanism for a userspace program to block until an IRQ triggers by simply trying to 'read' from a file /proc/irq/IRQNUM/irq. The CAN IRQ is IRQ number 30. The following is the implementation of can_irqwait():
static void can_irqwait(void) {
static int can_irqfd;
static int initialized;
int dummy;
if (!initialized) {
can_irqfd = open("/proc/irq/30/irq", O_RDONLY);
assert(can_irqfd != -1);
initialized = 1;
}
read(can_irqfd, &dummy, sizeof(dummy));
}
TCP Format
The canctl application provides a CAN network service. Any application on the network can make use of this service to send or receive CAN packets using the API defined by canctl. Thus, it is possible to develop code written in other languages (java, python, etc.) and/or to run this code under other operating systems.
The canctl application implements network CAN functionality using the can_rx_remote() and can_tx_remote() functions. These are very simple functions which read and write one fixed-size packet of struct canmsg to a TCP socket descriptor. Writing your own canctl client in the language of your choice is as simple as doing the same thing. The format of the each CAN packet sent or received via the network interface is described below. The terms "Rx" and "Tx" are relative to the client, so "Rx" would describe packets read from CAN over the network and "Tx" would describe packets written to CAN over the network.
UINT32 flags: bit 7 - set on Tx if packet is a control packet control packets are intercepted by the canctl server to allow control functionality. bit 6 - set if message originates locally (unused) bit 5 - set if CAN message has extended ID bit 4 - set if remote transmission request (RTR) bit 3 - set on Rx if CAN error warning condition occurred bit 2 - set on Rx if CAN bus had a data overrun bit 1 - set on Rx if CAN bus went error passive bit 0 - set on Rx if a CAN bus error occurred Error conditions are reported for informational purposes. The server normally handles these errors and recovers from them. control information present (reserved for future use) message originates from this node (unused) UINT32 CAN id UINT32 timestamp_seconds UINT32 timestamp_microseconds UINT32 bytes of CAN data which are valid if bit 7 of flags is set, this byte is instead interpreted as a command number: 0 = set acceptance filter if the acceptance filter has been set, then only CAN packets which pass the filter will be received. to pass the filter, all bits in the acceptance filter which are to be checked (specified by a 1 in the corresponding bit of the mask) are compared (filter id compared to corresponding bit in received id). only if all bits to be checked do match will the packet be received. UINT8[8] CAN data if bit 7 of flags is set, this byte is instead interpreted as follows: cmd 0: UINT32 acceptance filter id UINT32 acceptance filter mask
UINT32 values are sent in little-endian format.
So for example, to send a standard CAN packet of length 6 with contents 01:02:03:04:05:06 and CAN id 55 it would be necessary to open a TCP connection to port 7552 on the device with the canctl server running, and the write the following packet to the socket:
00 00 00 00 55 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 01 02 03 04 05 06 00 00