CAN通信

Raspberry PiでCANを使う


Raspberry PiでCAN通信を行う場合、CAN Controller と CAN Transceiver の2つのチップが必要になります。
MCP2515(Stand-Alone CAN Controller with SPI Interface)は2.7Vから5.5Vに対応しているので3.3V駆動、
MCP2551(High-Speed CAN Transceiver)は5V単一電源なので少し面倒です。

Raspberry PiでCAN通信を行う方法は色々なところで紹介されていますが、回路を組むにあたり一番参考にしたのはこ ちらのページです。
結線図が公開されていますが、この通りに結線すると安定して動きません。
色々調べてみると、MCP2551(RXD)→MCP2515(RxCan)のライン(下の図の緑のライン)は、
レベルシフト(5V→3.3V)してやる必要が有ることが分かりました。

MCP2551のRXD(Pin#4)と、MCP2515のRxCan(Pin#2)を直結すると、 MCP2551(RXD)→MCP2515(RxCan)のレベルが5Vとなり、
それがMCP2515の電源供給ラインに回り込んでしまいます。
下の回路の様に、MCP2515への電源供給をやめて、MCP2515の電源電圧を計ってみると、3.8V以上あります。




MCP2551のRXD(Pin#4)と、MCP2515のRxCan(Pin#2)をレベルシフトして、MCP2515の電源電圧を計ってみる と、2.0V程度に落ち着きます。




その後、調べてみたらこ ちらに同じことが書かれていました。
MCP2515の以下のピンもPullUpする必要が有ります。
・Pin#12(INT)
・Pin#16(CS)
・Pin#17(RESET)

ブレッドボードは5V単一電源にしたかったので、5V→3.3Vのレギュレータを追加して以下の回路に決定しました。
CAN-BUSの終端には150Ωの抵抗を付けています。
使用した発振器は16Mhzのセラロックです。
これで安定して動くようになります。



3.3V駆動のCAN Transceiverを使うと、レベルシフトやレギュレータは不要になり、3.3V単一電源で扱うことができます。




これ以外の設定はこ ちらこ ちらのページとほぼ同じですが、一応書いておきます。
まず、/boot/config.txt に以下を追加して再起動します。
oscillatorには使用する発信機の周波数を指定し、interruptにはintピンに繋がっているGPIO#を指定します。
dtparam=spi=on
dtoverlay=mcp2515-can0,oscillator=16000000,interrupt=25
dtoverlay=spi-bcm2835-overlay

再起動後にデバイスができていることを確認し、ipコマンドで転送スピードを指定します。
指定することができる値はこ ちらなどに公開されていますが、他のノードと合わせる必要が有ります。
多量のデータを送信する場合は、txqueuelenを増やしておかないと、エラーになります。
$ dmesg | grep mcp251x
[   19.992025] mcp251x spi0.0 can0: MCP2515 successfully initialized.

$ ls /sys/bus/spi/devices/spi0.0
driver  modalias  net  of_node  power  statistics  subsystem  uevent

$ ls /sys/bus/spi/devices/spi0.0/net
can0

$ sudo /sbin/ip link set can0 up type can bitrate 1000000

$ dmesg | grep can0
[   19.992025] mcp251x spi0.0 can0: MCP2515 successfully initialized.
[ 1309.525795] IPv6: ADDRCONF(NETDEV_CHANGE): can0: link becomes ready

$ sudo ifconfig can0
can0: flags=193<UP,RUNNING,NOARP>  mtu 16
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 10  (UNSPEC)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

$ sudo ifconfig can0 txqueuelen 1000

引き続きcan-utilをインストールします。
$ sudo apt install can-utils

#受信
$ candump can0

#送信
$ cansend can0 123#11223344AABBCCDD

これで送受信ができるようになります。
candumpを実行すると、受信したCANフレームを表示します。




/boot/config.txtを以下の様に変更し、セラロックを20Mhzの物に変えてみました。
dtparam=spi=on
dtoverlay=mcp2515-can0,oscillator=20000000,interrupt=25
dtoverlay=spi-bcm2835-overlay

20Mhzのセラロックは以下のエラーとなります。
$ dmesg | grep mcp251x
[   25.134938] mcp251x spi0.0: Cannot initialize MCP2515. Wrong wiring?
[   25.135005] mcp251x spi0.0: Probe failed, err=19



/boot/config.txtを以下の様に変更し、セラロックを8Mhzの物に変えてみました。
dtparam=spi=on
dtoverlay=mcp2515-can0,oscillator=8000000,interrupt=25
dtoverlay=spi-bcm2835-overlay

1Mbpsの転送速度を指定すると以下のエラーとなります。
$ sudo /sbin/ip link set can0 up type can bitrate 1000000
RTNETLINK answers: Numerical argument out of domain

転送速度を500Kbpsに落とすと問題なく通信できます。
8Mhzの発振器では500Kbpsが限界です。



以下のシェルでデータを(大体)0.2秒間隔で連続して送信することができます。
#!/bin/bash
val=0
id=100
while true; do
  data=`printf '%.16x\n' ${val}`
  #echo $data
  echo $id"#"$data
  cansend can0 $id"#"$data
  #echo $((val++))
  val=$(( $val+1 ))
  sleep 0.2
done



送信側を2台にして受信してみました。
ノード1とノード2のデータを交互に受信します。




mcp2515のデー タシートを確認するとCAN 2.0B(CAN拡張フレーム)に対応しています。
そこで、can-utilsを使ってCAN 2.0B(CAN拡張フレーム)が送受信できるかどうか確認してみました。
CAN標準フレームでのCAN-IDは11Bit(0から7FF)ですが、CAN拡張フレームではCAN-IDが29Bit(0から 1FFFFFFF)に拡張されています。
cansendで標準フレーム/拡張フレームどちらも送信できることを確認しました。


candumpでは以下の様に表示されます。


続く...