Canctl

From embeddedTS Manuals
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