ラズパイでCANを使いたい

環境

  • ラズパイ4B一式
  • OS:Ubuntu18またはラズパイOS
  • MCP2515基板(Amazonで500円くらいで売ってるやつ)
  • ジャンパワイヤ-メスメス

以下はあれば嬉しい

  • 16Mhz水晶(あれば)
  • はんだごて&はんだ
  • ハンダシュッ太郎

参考

https://qiita.com/h-kiyo/items/d8583af13768ad67bcd0

前置き

これまでArduinoボードにSPIからMCP2515を接続して,モーターを駆動していた.
このボードには,他にも色々機能をつけていたので,使用していた.
一方,CANしか使用しない場合,いちいちArduinoのシリアルを通してモータ制御するのはめんどくさいので,ラズパイのSPIに直接繋いじゃおうという話.

接続

Raspberry-pi 配線 MCP2515
02 5V <-> VCC 5V電源
06 GND <-> GND
24 GPIO8 <-> CS
21 GPIO9 <-> SO
19 GPIO10 <-> SI
23 GPIO11 <-> SCK
22 GPIO25 <-> INT

configの書き換え

ubuntuの場合

sudo nano /boot/firmware/config.txt

raspiOSの場合

sudo nano /boot/config.txt

書き込み内容

dtparam=spi=on
dtoverlay=mcp2515-can0,oscillator=8000000,interrupt=25

interruptはGPIO25だけど繋いだところに合わせれば良い.
oscillatorは水晶の周波数を記述する.
標準では8Mhz水晶がついているが,これだと500kbpsは対応できても1Mbpsの通信が微妙なので,16Mhzの水晶に付け替える.
ハンダシュッ太郎を使用すると簡単に交換できるので,常備しておくことをオススメする.

設定

認識されているかどうか

下記コマンドを使用する.
何か表示されればok.
表示されなければ認識されてない.

sudo modprobe mcp251x
dmesg | grep mcp251x

can立ち上げ(リンクアップ)

500kbpsの場合

sudo ip link set can0 up type can bitrate 500000

1Mbpsの場合

sudo ip link set can0 up type can bitrate 1000000

正常な場合下記のように表示される.

$ ip link show can0

3: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10
    link/can

下記コマンドでcan0が追加されていればok

ifconfig

ループバックテスト

can-toolの追加

sudo apt install can-utils

ループバックモードで立ち上げ直す.

sudo ip link set can0 down
sudo ip link set can0 type can bitrate 500000 loopback on
sudo ip link set can0 up

下記コマンドを入力して待機

candump can0

別のターミナルを開いて,下記を入力

cansend can0 123#DEADBEEF

candumpコマンドを実行したターミナルで文字が表示されたら成功.

自動起動

今回はserviceファイルに定義する.

cd /etc/systemd/system
sudo nano can.service
[Unit]
Description=Setup CAN interface can0
After=network.target

[Service]
Type=oneshot
ExecStart=/sbin/ip link set can0 up type can bitrate 1000000
ExecStop=/sbin/ip link set can0 down
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
sudo systemctl enable can.service
sudo systemctl start can.service

RoboMasterモータM3508P19を接続

HとLを接続するだけ.
複数個使う場合は,終端抵抗をつける場所について考える.

受信データの表示.ID:201が送られてくる.

candump can0

必要パッケージのインストール

環境はひとまずpython2.7

sudo apt install python-pip
pip install --upgrade setuptools wheel
pip install wrapt==1.10.11

コード

motor_output_current_A に電流値を入れるとモーターが回る.
なんか,時間が経つと,送信バッファがオーバフローしてエラーが出るので,対策する必要がありあそう.

# -*- coding: utf-8 -*-

import can
import time
import struct

# fmap関数と同様の処理
def fmap(x, in_min, in_max, out_min, out_max):
    return int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)

# CANインターフェースを初期化
can_interface = 'can0'
bus = can.interface.Bus(can_interface, bustype='socketcan')

# 初期化変数
tx_id = 0x200
rx_id = 0x201
prev_time = time.time()

while True:
    try:
        # 送信データ作成
        motor_output_current_A = 0.0
        motor_output_current_byte = fmap(motor_output_current_A, 0, 20, 0, 16384)

        tx_data = [
            (motor_output_current_byte >> 8) & 0xFF,  # 上位バイト
            motor_output_current_byte & 0xFF,         # 下位バイト
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00        # その他のデータ
        ]

        # 20msごとにデータ送信
        if time.time() - prev_time > 0.02:
            msg = can.Message(arbitration_id=tx_id, data=tx_data, extended_id=False)
            bus.send(msg)
            print("Sent:", tx_data)
            prev_time = time.time()

        # CANデータ受信
        rx_msg = bus.recv(timeout=0.01)  # 10ms待機
        if rx_msg is not None and rx_msg.arbitration_id == rx_id:
            # データを解析
            rx_data = rx_msg.data
            angle = struct.unpack('>h', rx_data[0:2])[0]
            rpm = struct.unpack('>h', rx_data[2:4])[0]
            amp = struct.unpack('>h', rx_data[4:6])[0]
            temp = struct.unpack('b', rx_data[6:7])[0]

            print("Received ID:", hex(rx_msg.arbitration_id))
            print("Angle:", angle, "RPM:", rpm, "Amp:", amp, "Temp:", temp)

    except KeyboardInterrupt:
        print("Stopping...")
        break