How to use the CAN bus of a Beagle Bone Black from Erlang
May 2015.
Let's try using the CAN bus device of a Beagle Bone Black from Erlang.
Here's some information about the system present on the board:
# uname -a
Linux beaglebone 3.14.40-ti-r62 #1 SMP PREEMPT Thu Apr 30 18:28:06 UTC 2015 armv7l GNU/Linux
# cat /etc/issue
Debian GNU/Linux 8 \n \l
BeagleBoard.org Debian Image 2015-05-01
Testing the CAN driver
Let's create a virtual CAN device:
# modprobe vcan
# ip link add dev vcan0 type vcan
# ifconfig vcan0 up
# ip -details -statistics link show vcan0
5: vcan0:mtu 16 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/can promiscuity 0
vcan
RX: bytes packets errors dropped overrun mcast
0 0 0 0 0 0
TX: bytes packets errors dropped carrier collsns
0 0 0 0 0 0
Let's try sending/receiving data on that virtual device:
# cansend vcan0 442#DEADBABE
On another terminal:
# candump vcan0
vcan0 442 [4] DE AD BA BE
Using the CAN driver from Erlang
I'll be using the excellent CAN module from Tony Rogvall: https://github.com/tonyrog/can.
Let's create a project from the following
rebar.config
:
{lib_dirs,["deps"]}.
{sub_dirs, ["rel"]}.
{deps, [
{lager, ".*", {
git, "git://github.com/basho/lager.git", "master"}
},
{can, ".*", {
git, "git://github.com/tonyrog/can", "master"}
}
]}.
{erl_opts, [{parse_transform, lager_transform}]}.
Compile everything and start a shell:
# rebar compile
# erl -pa ebin/ -pa deps/*/ebin
1> lager:start().
12:53:30.268 [info] Application lager started on node nonode@nohost
ok
2> lager:set_loglevel(lager_console_backend, debug).
ok
3> can:start().
12:53:39.905 [info] Application uart started on node nonode@nohost
ok
4> can_sock:start(0, [{device,"vcan0"}]).
12:53:39.963 [debug] Supervisor can_sup started can_router:start_link([]) at pid <0.58.0>
12:53:39.965 [debug] Supervisor can_sup started can_if_sup:start_link() at pid <0.59.0>
12:53:39.966 [info] Application can started on node nonode@nohost
12:53:41.213 [debug] can_router: process <0.61.0>, param {can_sock,"vcan0",0} joined.
12:53:41.215 [debug] Supervisor can_sup started can_sock:start_link(0, [{device,"vcan0"}]) at pid <0.61.0>
{ok,<0.61.0>}
Driver and device are loaded. Let's try sending something:
5> can:send(42, <<"ABCD">>).
ok
6> 12:54:42.626 [debug] can_router: broadcast: [ID: 02A LEN:4 DATA:41424344]
12:54:42.627 [debug] broadcast: frame={can_frame,42,4,<<"ABCD">>,0,0}, send=1
On the dump:
# candump vcan0
vcan0 02A [4] 41 42 43 44
Sending data works. Let's try receiving data.
Let's attach the shell pid to the router.
6> can_router:attach().
12:58:06.992 [debug] can_router: process <0.33.0> attached.
ok
Information about the module can be queried:
can_router:i().
Interfaces
1: {can_sock,"vcan0",0}
input_frames: 3
output_frames: 2
Applications
<0.60.0>: interface=0
ok
Send something from the terminal:
# cansend vcan0 442#44434241
Do we have a new message?
7> receive V -> V end.
12:58:31.517 [debug] can_router: broadcast: [ID: 442 LEN:4 INTF:1 DATA:44434241]
12:58:31.517 [debug] broadcast: frame={can_frame,1090,4,<<"DCBA">>,1,-1}, send=1
{can_frame,1090,4,<<"DCBA">>,1,-1}
Success!
Using the real CAN
Plug your CAN transceiver the following way:
Pin | Description |
---|---|
24 | CAN RX |
26 | CAN TX |
Make sure the dtb is loaded in
/boot/uEnv.txt
:
dtb=am335x-boneblack-can1.dtb
If not, edit the file and reboot the board.
You can check that the board is aware of its CAN interface:
# cat /sys/kernel/debug/pinctrl/44e10800.pinmux/pinmux-pins | grep -i can
pin 96 (44e10980.0): P9_26_pinmux.53 (GPIO UNCLAIMED) function pinmux_P9_26_can_pin group pinmux_P9_26_can_pin
pin 97 (44e10984.0): P9_24_pinmux.51 (GPIO UNCLAIMED) function pinmux_P9_24_can_pin group pinmux_P9_24_can_pin
Stop the driver, configure the link and start the interface again:
# ifconfig can0 down
# ip link set can0 up type can bitrate 1000000 loopback off triple-sampling on
# if config can0 up
If everything goes right, the link should be working and you can query information about it:
# ip -details -statistics link show can0
3: can0:mtu 16 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
link/can promiscuity 0
canstate ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
bitrate 1000000 sample-point 0.750
tq 83 prop-seg 4 phase-seg1 4 phase-seg2 3 sjw 1
c_can: tseg1 2..16 tseg2 1..8 sjw 1..4 brp 1..1024 brp-inc 1
clock 24000000
re-started bus-errors arbit-lost error-warn error-pass bus-off
0 0 0 1 1 0
RX: bytes packets errors dropped overrun mcast
14058 2425 0 89 0 0
TX: bytes packets errors dropped carrier collsns
591 591 0 0 0 0
For information, here's how my other device is configured:
//CLOCK CONFIG:
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
__SYSCFG_CLK_ENABLE();
//CAN CONFIG:
hcan.Instance = CAN;
hcan.Init.Prescaler = 2;
hcan.Init.Mode = CAN_MODE_NORMAL;
hcan.Init.SJW = CAN_SJW_1TQ;
hcan.Init.BS1 = CAN_BS1_9TQ;
hcan.Init.BS2 = CAN_BS2_8TQ;
hcan.Init.TTCM = DISABLE;
hcan.Init.ABOM = DISABLE;
hcan.Init.AWUM = DISABLE;
hcan.Init.NART = DISABLE;
hcan.Init.RFLM = DISABLE;
hcan.Init.TXFP = DISABLE;
HAL_CAN_Init(&hcan);
(it's a STM32F302).