RPi CANBus

From eLinux.org
Jump to: navigation, search

CAN bus is a communication protocol used mainly in cars and some industrial products.

The Raspberry Pi doesn't have CAN bus built in, but it can be added through USB or SPI converters.

This document presents how to enable CAN bus support in the kernel, using a SPI-to-CAN bus converter (MCP2515). The same can be done for other SPI converters, or for PeakCAN USB.

The Raspbian kernel doesn't come with modules needed, so the kernel must be compiled from source. Refer to http://elinux.org/RPi_Kernel_Compilation for more information on this. The following instructions present all steps requiered to build a kernel with the correct modules, and some useful commands to use it.

Please note:
The best way to compile a kernel is to do it on a desktop PC and not on a Raspberry Pi.
It takes about 2 or 3 hours on desktop PCs, but much more (about 12 hours) on a Raspberry Pi.

Prerequisite

At least, a proper GCC installation is needed, and ncurses development package are used by kernel menuconfig.

 sudo apt-get install gcc ncurses-dev

If you are using Ubuntu or Kubuntu, please use this command instead:

 sudo apt-get install gcc-arm-linux-gnueabi make ncurses-dev

Kernel configuration and compilation

For this example, everything will be done in a directory "/opt/raspberrypi/". So first create it, and then checkout the last version of the kernel for the raspberrypi in the linux subdirectory.

 cd /opt
 sudo mkdir raspberrypi
 cd raspberrypi 
 sudo chmod og+w .
 git clone --depth 1 https://github.com/raspberrypi/linux/
 cd linux

At the time of writing, the current kernel trunk version is 3.2. To use the 3.6 kernel, an additional branch has to be fetch.

 git fetch --depth=1  git://github.com/raspberrypi/linux.git rpi-3.6.y:refs/remotes/origin/rpi-3.6.y
 git checkout rpi-3.6.y

Then copy the default cutdown .config file, and run the "oldconfig" make to make it up to date.

 
 cp arch/arm/configs/bcmrpi_cutdown_defconfig .config
 make ARCH=arm CROSS_COMPILE=arm-rpi-linux-gnueabi- oldconfig -j 3

For Ubuntu or Kubuntu users, please use this command instead:

 
 cp arch/arm/configs/bcmrpi_cutdown_defconfig .config
 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- oldconfig -j 3

From the default file, I only changed the two following lines (first is for cross-compiling the kernel), and pressed "enter" for all other (there are a lot...).

Cross-compiler tool prefix (CROSS_COMPILE) [] (NEW) arm-rpi-linux-gnueabi-
Default hostname (DEFAULT_HOSTNAME) [(none)] (NEW) raspberrypi

Then the CAN bus support must be added using "menuconfig" (see. http://www.raspberrypi.org/phpBB3/viewtopic.php?f=44&t=7027&start=50)

 cd  /opt/raspberrypi/linux
 make ARCH=arm CROSS_COMPILE=arm-rpi-linux-gnueabi- menuconfig

For Ubuntu or Kubuntu users, please use this command instead:

 cd  /opt/raspberrypi/linux
 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig

Notice: Please make the changes first in the "Device Drivers ---> SPI support --->". After that you can make the changes in "Networking support --->", because only now you can see the entry "<M> Microchip MCP251x SPI CAN controllers".

Activate the following lines:

[*] Networking support --->
....<M> CAN bus subsystem support --->
........<M> Raw CAN Protocol (raw access with CAN-ID filtering)
........<M> Broadcast Manager CAN Protocol (with content filtering)
............CAN Device Drivers --->
................<M> Platform CAN drivers with Netlink support
................[*] CAN bit-timing calculation
................<M> Microchip MCP251x SPI CAN controllers
................[*] CAN devices debugging messages

....Device Drivers --->
........[*] SPI support --->
............<M> BCM2798 SPI controller driver (SPI0)
............<M> User mode SPI driver support
.......-*- GPIO Support --->
............[*] /sys/class/gpio/... (sysfs interface)

If another driver is used, activate it in place of the "MCP251x SPI CAN controllers". If using a USB controller, then SPI support is not needed, but USB must be correctly set up.

Then edit the board definition, to add the informations about the SPI bus, and to configure the interrupt pin of the MCP2515.

 vi arch/arm/mach-bcm2708/bcm2708.c
 #apply patch

Note that the "IRQF_ONESHOT" flag is only required for kernel 3.6. For kernel 3.2 you should remove it. Adjust the GPIO pin used for interrupt (here 25), the SPI frequency (here 10MHz) and MCP2515 oscillator frequency (here 20 MHz) to your setup.

diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c
index 838e0f2..10026ec 100644
--- a/arch/arm/mach-bcm2708/bcm2708.c
+++ b/arch/arm/mach-bcm2708/bcm2708.c
@@ -54,6 +54,12 @@
 #include <mach/vcio.h>
 #include <mach/system.h>
 
+#include <linux/can/platform/mcp251x.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+
+#define MCP2515_CAN_INT_GPIO_PIN 25
+
 #include <linux/delay.h>
 
 #include "bcm2708.h"
@@ -586,11 +592,21 @@ static struct platform_device bcm2708_spi_device = {
 	.resource = bcm2708_spi_resources,
 };
 
+static struct mcp251x_platform_data mcp251x_info = {
+   .oscillator_frequency   = 20000000,
+   .board_specific_setup   = NULL,
+   .irq_flags              = IRQF_TRIGGER_FALLING|IRQF_ONESHOT,
+   .power_enable           = NULL,
+   .transceiver_enable     = NULL,
+};
+
 #ifdef CONFIG_SPI
 static struct spi_board_info bcm2708_spi_devices[] = {
 	{
-		.modalias = "spidev",
-		.max_speed_hz = 500000,
+		.modalias = "mcp2515",
+		.max_speed_hz = 10000000,
+		.platform_data = &mcp251x_info,
+		/* .irq = unknown , defined later thru bcm2708_mcp251x_init */
 		.bus_num = 0,
 		.chip_select = 0,
 		.mode = SPI_MODE_0,
@@ -602,6 +618,13 @@ static struct spi_board_info bcm2708_spi_devices[] = {
 		.mode = SPI_MODE_0,
 	}
 };
+
+static void __init bcm2708_mcp251x_init(void) {
+   bcm2708_spi_devices[0].irq = gpio_to_irq(MCP2515_CAN_INT_GPIO_PIN);
+   printk(KERN_INFO " BCM2708 mcp251x_init:  got IRQ %d for MCP2515\n", bcm2708_spi_devices[0].irq);
+   return;
+};
+
 #endif
 
 static struct resource bcm2708_bsc0_resources[] = {
@@ -749,6 +772,7 @@ void __init bcm2708_init(void)
 	system_serial_low = serial;
 
 #ifdef CONFIG_SPI
+	bcm2708_mcp251x_init();
 	spi_register_board_info(bcm2708_spi_devices,
 			ARRAY_SIZE(bcm2708_spi_devices));
 #endif

Then compile the kernel. The example below is for cross compilation, using a two-core x86 machine. For compiling on the Raspberry Pi, just type "make".

 cd /opt/raspberrypi/linux
 make ARCH=arm CROSS_COMPILE=arm-rpi-linux-gnueabi- -j3

For Ubuntu or Kubuntu users, please use this command instead:

 cd /opt/raspberrypi/linux
 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j 3

Then install the tools from Git.

 cd /opt/raspberrypi/
 git clone --depth 1 git://github.com/raspberrypi/tools.git

Use the tools to generate an image from the build kernel, and copy that to a new "build" directory.

 cd tools/mkimage
 ./imagetool-uncompressed.py ../../linux/arch/arm/boot/zImage
 mkdir -p /opt/raspberrypi/build/boot
 mv kernel.img /opt/raspberrypi/build/boot

Then return to the linux directory, to compile the kernel modules, and copy them to the build directory.

 cd ../../linux/
 make ARCH=arm CROSS_COMPILE=arm-rpi-linux-gnueabi- modules_install INSTALL_MOD_PATH=/opt/raspberrypi/build/ -j3
 cp .config ../build/boot/

For Ubuntu or Kubuntu users, please use this command instead.

 cd ../../linux/
 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- modules_install INSTALL_MOD_PATH=/opt/raspberrypi/build/ -j3
 cp .config ../build/boot/

Again from Git, get the last firmware for the Raspberry Pi, and copy it to the build directory.

 cd ..
 git clone --depth 1 git://github.com/raspberrypi/firmware.git
 cd firmware
 git fetch  --depth 1 git://github.com/raspberrypi/firmware.git next:refs/remotes/origin/next
 git checkout next
 
 cp boot/bootcode.bin /opt/raspberrypi/build/boot
 cp boot/fixup.dat /opt/raspberrypi/build/boot
 cp boot/start.elf /opt/raspberrypi/build/boot
 
 mkdir -p /opt/raspberrypi/build/opt
 cp -r hardfp/opt/vc /opt/raspberrypi/build/opt

You should now have the complete new kernel/modules/firmware in the "/opt/raspberrypi/build" directory. If you want to use it, simply puts its contents to the root directory. It is possible to do it directly without using a temporary build directory, but this method has the advantage of being possible on a remote machine, and to allow easier save of the binary generated (simply archive this directory if you need to give it to someone else).

MCP2515 Asynchronous Driver

The standard MCP251x has some drawbacks with kernel 3.6. It happens that it hangs and stop receiving frames. It has something to do with the "IRQF_ONESHOT" flag. Refer to the Raspberry Pi forum, for latest discussions on this point: http://www.raspberrypi.org/phpBB3/viewtopic.php?f=44&t=7027.

However, another MCP2515 driver exists, and it has been successfully tested with kernel 3.6. Please not that it only allows to use a MCP2515 and no MCP2510 like the MCP251x driver.

You can download it and activate it in your kernel like follows.

 cd /opt/raspberrypi/linux
 wget http://clientes.netvisao.pt/anbadeol/linux-mcp2515-20101018.patch.gz
 gunzip linux-mcp2515-20101018.patch.gz
 patch -p1 < linux-mcp2515-20101018.patch
 
 make ARCH=arm CROSS_COMPILE=arm-rpi-linux-gnueabi- menuconfig

For Ubuntu or Kubuntu users, please use this command instead.

 cd /opt/raspberrypi/linux
 wget http://clientes.netvisao.pt/anbadeol/linux-mcp2515-20101018.patch.gz
 gunzip linux-mcp2515-20101018.patch.gz
 patch -p1 < linux-mcp2515-20101018.patch
 
 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig

Activate the following lines.

[*] Networking support --->
....<M> CAN bus subsystem support --->
........<M> Raw CAN Protocol (raw access with CAN-ID filtering)
........<M> Broadcast Manager CAN Protocol (with content filtering)
............CAN Device Drivers --->
................<M> Platform CAN drivers with Netlink support
................[*] CAN bit-timing calculation
................<M>  Microchip MCP2515 SPI CAN controller
................[*] CAN devices debugging messages

Then go again through the kernel compilation (see above if some line is missing).

 make ARCH=arm CROSS_COMPILE=arm-rpi-linux-gnueabi- -j3
 make ARCH=arm CROSS_COMPILE=arm-rpi-linux-gnueabi- modules_install INSTALL_MOD_PATH=/opt/raspberrypi/build/modules -j3
 cd ../tools/mkimage
 ./imagetool-uncompressed.py ../../linux/arch/arm/boot/zImage
 mv kernel.img /opt/raspberrypi/build/boot
 cd ../../linux

For Ubuntu or Kubuntu users, please use this command instead.

 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j3
 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- modules_install INSTALL_MOD_PATH=/opt/raspberrypi/build/modules -j3
 cd ../tools/mkimage
 ./imagetool-uncompressed.py ../../linux/arch/arm/boot/zImage
 mv kernel.img /opt/raspberrypi/build/boot
 cd ../../linux

The build directory is updated with the kernel and module with MCP2515 driver support.

SPI low latency patch

Finally, the SPI adds a lot of latency, that can results in frame lost at high CAN bus rates, due to the small receive buffer of the MCP2515 controller.

This can be reduced using the following patch. Refer to the discussion on the forum for more details: http://www.raspberrypi.org/phpBB3/viewtopic.php?f=44&t=19489.

Warning: following the commit of 22 January 2013 (https://github.com/raspberrypi/linux/commit/91a3be5b2b783b930b2d7cdbf38283b613bce7d4) the patch fails to apply. A new patch is in process, see the forum above to check the last patch.


These instructions download it from the forum and apply it.

 cd /opt/raspberrypi/linux
 wget http://www.raspberrypi.org/phpBB3/download/file.php?id=1492 -O spi-latency-branch3.6.y.patch.bz2
 bunzip2 spi-latency-branch3.6.y.patch.bz2 
 patch -p1 < spi-latency-branch3.6.y.patch

Then a new compilation of the kernel is done.

 make ARCH=arm CROSS_COMPILE=arm-rpi-linux-gnueabi- -j3
 make ARCH=arm CROSS_COMPILE=arm-rpi-linux-gnueabi- modules_install INSTALL_MOD_PATH=/opt/raspberrypi/build/modules -j3
 cd ../tools/mkimage
 ./imagetool-uncompressed.py ../../linux/arch/arm/boot/zImage
 mv kernel.img /opt/raspberrypi/build/boot
 cd ../../linux

For Ubuntu or Kubuntu users, please use this command instead.

 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j3
 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- modules_install INSTALL_MOD_PATH=/opt/raspberrypi/build/modules -j3
 cd ../tools/mkimage
 ./imagetool-uncompressed.py ../../linux/arch/arm/boot/zImage
 mv kernel.img /opt/raspberrypi/build/boot
 cd ../../linux

The build directory is updated with the kernel patched SPI driver with low latency.