Difference between revisions of "EMAC CAN Interface"
m (Fixed two comma splices. Fixed a few grammatical issues. Noted missing keywords for SEO and an oddity in one code example. Marked buggy.) |
Kyoungmeyer (talk | contribs) |
||
Line 1: | Line 1: | ||
− | {{todo| Review (01.18.2016-10:45->KY+);(01.20.2016-13:50->MD-)|Klint Youngmeyer| project=OE 5.0,KY,MG,MD, | + | {{todo| Review (01.18.2016-10:45->KY+);(01.20.2016-13:50->MD-)(01.20.2016-14:23->KY+)|Klint Youngmeyer| project=OE 5.0,KY,MG,MD,Review }} |
{{#seo: | {{#seo: | ||
|title=EMAC CAN Interface | |title=EMAC CAN Interface | ||
|titlemode=append | |titlemode=append | ||
− | |keywords= | + | |keywords=can,socketcan,canbus,cansend,candump,linux |
|description=This page describes how to use the CAN interface on EMAC boards. | |description=This page describes how to use the CAN interface on EMAC boards. | ||
}} | }} | ||
− | |||
− | |||
− | |||
<!-- /*********************************************************************************************************/ --> | <!-- /*********************************************************************************************************/ --> | ||
<!-- /**************************************** Page Description Text ****************************************/ --> | <!-- /**************************************** Page Description Text ****************************************/ --> | ||
Line 126: | Line 123: | ||
frame.can_id = id; | frame.can_id = id; | ||
for(j = 0; j < dlc; j++){ | for(j = 0; j < dlc; j++){ | ||
− | frame.data[j] = | + | frame.data[j] = rand() % 0x100; // Assign 8 random hex bytes to the payload |
} | } | ||
frame.can_dlc = dlc; | frame.can_dlc = dlc; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
====CAN Write==== | ====CAN Write==== | ||
Line 253: | Line 230: | ||
frame.can_id = id; | frame.can_id = id; | ||
for(j = 0; j < dlc; j++){ | for(j = 0; j < dlc; j++){ | ||
− | frame.data[j] = | + | frame.data[j] = rand() % 0x100; // Assign 8 random hex bytes to the payload |
} | } | ||
frame.can_dlc = dlc; | frame.can_dlc = dlc; |
Latest revision as of 14:25, 20 January 2016
The CAN interface is a vehicle bus standard that allows communication between a controller and various sensors. EMAC produces several boards capable of communicating on a CAN bus. For more information about which products support the CAN bus, see the hardware page.
Background
CAN (controller area network) is a multi-master serial bus for communicating between "nodes" over a two-wire interface. The wires are generally a 120 ohm twisted pair. Each CAN node has the ability to communicate with every other node, one at a time.
EMAC CAN Interface
The CAN bus is able to be accessed from the command line, or using a C library on EMAC products (with CAN support).
CAN Bus Setup
Before using the CAN interface on an EMAC device, two commands must be run. These commands could be scripted to be run at startup; see this page for details.
NOTE |
The settings in the first command may need to be changed according to the application in question. |
root
@
ipac9x25
:
~
#
ip link set can0 type can bitrate 125000 triple-sampling on
root
@
ipac9x25
:
~
#
ip link set can0 up
Command Line CAN
The command line tools included with EMAC OE 5 are part of the canutils
package. The tools included in this package are:
canconfig
candump
canecho
cansend
cansequence
Of these five, the most easily used are cansend
and candump
.
cansend
cansend
, as the name implies, is used for sending CAN messages over the bus. Up to eight bytes at a time can be sent using cansend
, these bytes are formatted at a space separated list of hexadecimal octets e.g. "0xBE 0x42 0xEF 0x9A
." The command for sending the previously mentioned hexadecimal digits on the can0
device is:
root
@
ipac9x25
:
~
#
cansend can0 0xBE 0x42 0xEF 0x9A
The received message, on a board running candump
, will look like the following:
<0x001> [4] be 42 ef 9a
cansend
uses, by default, an identifier of 0x01
. If an identifier other than default is desired, the -i 0x<IDENT>
option must be passed on the command line. In the following example, the identity 0x89
is used.
root
@
ipac9x25
:
~
#
cansend can0 -i 0x89 0xBE 0x42 0xEF 0x9A
The received message, on a board running candump
, will look like the following:
<0x089> [4] be 42 ef 9a
Using the standard identity, the length of the identity is limited to a maximum hexadecimal value of 0x7FF
. If a larger identity is needed, the -e
option can be passed, which allows an extended CAN frame with a maximum hexadecimal value of 0x1FFFFFFF
. The following example shows a message with an extended frame.
root
@
ipac9x25
:
~
#
cansend can0 -e -i 0x8008 0xBE 0x42 0xEF 0x9A
The received message, on a board running candump
, will look like the following:
<0x00008008> [4] be 42 ef 9a
candump
candump
is used to receive messages over CAN. These messages can be output to stdout
or directly to a file. candump
will run until it is killed, either with Ctrl-C
or a kill
command. The following command shows how to receive any message over CAN and output it to stdout
. A command of cansend can0 0xBE 0x42 0xEF 0x9A
was given on a connected board.
root
@
ipac9x25
:
~
#
candump can0
interface = can0, family = 29, type = 3, proto = 1
<0x001> [4] be 42 ef 9a
To receive any message over CAN and output it to a file, the following command is used. A command of cansend can0 0xBE 0x42 0xEF 0x9A
was given on a connected board.
root
@
ipac9x25
:
~
#
candump can0 -o /tmp/candump.txt
interface = can0, family = 29, type = 3, proto = 1
Since candump
will continue running, a Ctrl-C
should be given before viewing the contents of the file. To view the file, cat
can be used.
root
@
ipac9x25
:
~
#
cat /tmp/candump.txt
<0x001> [4] be 42 ef 9a
candump
can also filter its output according to two values: filter and mask. The filter [can_id]:[can_mask]
matches when [received_can_id] & [can_mask] == [can_id] & [mask]
. For example, to only receive messages from the identity 0x088
, use the following command.
root
@
ipac9x25
:
~
#
candump can0 --filter 0x088:0x7FF
C Programming CAN
Using CAN with C programming on an EMAC Linux board involves using the SocketCAN implementation.
CAN Sockets
To open a CAN connection, first a socket must be opened. CAN sockets can be either raw
or broadcast manager
; for this document, raw
will be used. After creation of a raw
CAN socket, it must be bound to an address with the bind()
system call. This address is specified by a sockaddr_can
struct cast to a sockaddr
struct. The following code snippet demonstrates how to open a CAN socket on can0
with no error checking.
int s;
struct sockaddr_can addr;
struct ifreq ifr;
s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
strcpy(ifr.ifr_name, "can0" );
ioctl(s, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
bind(s, (struct sockaddr *)&addr, sizeof(addr));
CAN Frames
In order to transmit data over this CAN socket, the data must be in a "CAN frame." This frame struct has three important member variables: can_id
, can_dlc
, and data[8]
. All of these variables are of type __u8
, which is a Linux unsigned 8-bit integer. can_id
contains the identifier for the message sender, can_dlc
contains the frame payload in bytes, and data[8]
is an array that holds up to the 8 bytes of payload. The following snippet of code shows how to create a frame with 8 random hex values as its payload without error checking.
struct can_frame frame;
int j;
int id = 0x1;
int dlc = 8; // Frame payload length in bytes
srand(time(NULL)); // Randomize seed
frame.can_id = id;
for(j = 0; j < dlc; j++){
frame.data[j] = rand() % 0x100; // Assign 8 random hex bytes to the payload
}
frame.can_dlc = dlc;
CAN Write
Sending a CAN frame is accomplished by simply using the write()
system call. write()
takes 3 arguments: a file descriptor, a buffer(data) to write, and the size of data to write. In this case, the file descriptor is the CAN socket that was opened previously, the buffer will will be the address to the frame struct, and the size of data is the size of the CAN frame struct. The return value of write()
is the number of bytes written and is valuable for error checking. The following code snippet illustrates how to write a frame to the socket that was set up in the previous sections.
int wbytes;
wbytes = write(s, &frame, sizeof(struct can_frame));
CAN Read
Reading from a CAN frame is very similar to writing to one. Reading is accomplished with the read()
system call. read()
takes 3 arguments: a file descriptor, a buffer(data) to put the read data in, and the size of data to read. In this case, the file descriptor is the CAN socket that was opened previously, the buffer will will be the address to a new frame struct, and the size of data is the size of the CAN frame struct. The return value of read()
is the number of bytes read and is valuable for error checking. When write is called, it will block the program from continuing until data is read. The following code snippet illustrates how to read from the CAN bus to a frame and print that frame to stdout
in a similar format to candump
without error checking.
struct can_frame frame2;
int rbytes, k;
rbytes = read(s, &frame2, sizeof(struct can_frame));
printf("<0x%03x> [%d] ", frame2.can_id, frame2.can_dlc);
for(k = 0; k < frame2.can_dlc; k++){
printf("%02x ", frame2.data[k]);
}
printf("\n");
CAN Example Program
Using all of the information in the above sections, a small program can be made to emulate some of the features of cansend
and candump
. To build this example, see this section of the getting started guide. To run this program, enter the name of the program followed by either "send" or "receive." Send will send 8 bytes of random hex over can0
, while receive will read one message from can0
and print it out.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <linux/can.h>
#include <net/if.h>
#include <linux/can/raw.h>
#include <time.h>
int can_write(int socket);
int can_read(int socket);
int main(int argc, char* argv[])
{
if(argc != 2){
printf("%s requires either the argument \"send\" or \"receive\"\n", argv[0]);
exit(EXIT_FAILURE);
}
// Create and Bind Socket
int s, ret;
struct sockaddr_can addr;
struct ifreq ifr;
if(s = socket(PF_CAN, SOCK_RAW, CAN_RAW) < 0){
perror("socket");
exit(EXIT_FAILURE);
}
strcpy(ifr.ifr_name, "can0" );
ioctl(s, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0){
perror("bind");
exit(EXIT_FAILURE);
}
////
if(strcmp(argv[1], "send") == 0){
ret = can_write(s);
}else if(strcmp(argv[1], "receive") == 0){
ret = can_read(s);
}else{
printf("%s requires either the argument \"send\" or \"receive\"\n", argv[0]);
exit(EXIT_FAILURE);
}
if(ret == 0) {
exit(EXIT_SUCCESS);
}else {
exit(EXIT_FAILURE);
}
}
int can_write(int socket)
{
// Build frame from random digits
struct can_frame frame;
int j;
int id = 0x1;
int dlc = 8; // Frame payload length in bytes
srand(time(NULL)); // Randomize seed
frame.can_id = id;
for(j = 0; j < dlc; j++){
frame.data[j] = rand() % 0x100; // Assign 8 random hex bytes to the payload
}
frame.can_dlc = dlc;
////
// Write frame to CAN socket
int wbytes;
wbytes = write(socket, &frame, sizeof(struct can_frame));
if(wbytes < 0) {
perror("can raw socket write");
return 1;
}
////
return 0;
}
int can_read(int socket){
// Read from CAN socket and print message
struct can_frame frame2;
int rbytes, k;
rbytes = read(socket, &frame2, sizeof(struct can_frame));
if(rbytes < 0) {
perror("can raw socket read");
return 1;
}
printf("<0x%03x> [%d] ", frame2.can_id, frame2.can_dlc);
for(k = 0; k < frame2.can_dlc; k++){
printf("%02x ", frame2.data[k]);
}
printf("\n");
////
return 0;
}
This information can be found in much grater detail, along with much more, in the Linux Kernel Documentation.
Conclusion
CAN is a useful protocol for communicating quickly with sensors and other controllers. EMAC boards with CAN should be able to quickly integrate with existing CAN networks with little trouble.