※当サイトではアフィリエイト広告を利用しています

電子工作

CANを使ってArduino2台で複数データの送受信を行う方法

  • Arduinoを使用したCAN通信で複数データの送受信はどうやって行うのだろう?
  • 一度に複数のデータが送られてきたときに、受信側はどうやって受け取る?
  • 単発データならまだしも、複数データだと取りこぼしが発生してしまうのでは?

 今回はそんな疑問にお答えします。

 CAN通信で単発のデータの送受信ができるようになっても複数データとなるとどうやって取得したらいいか分からなかったりしますよね。送信側によるデータ送信周期が遅ければなんとなくデータの取りこぼしも発生しないような気がしますが、同時多発的に送られてきたときにすべてのデータを漏らすことなく受信する方法が分からない。

 答えは"割り込み"といって、CANを受信したら都度データを取得する関数を発動させる仕組みを使って取りこぼしを防止します。この記事ではCANのハード的なお話から割り込みの仕組みまで丁寧に解説していきます。

前回記事にてArduinoとMCP2515との接続方法や、動作に必要なライブラリのインストール方法を掲載しておりますので、必ず参照をお願いします。

前回記事
MCP2515
CANを使ってArduino2台で簡単にデータ送受信を行う方法

Arduinoを2台使ってデータ通信を行いたいけど、どうすればいいんだろ? マイコンの通信方法はI2CやSPIがあるけど、やり方がよく分からないし・・・ 大容量のデータ通信じゃなくてとりあえず1バイト ...

続きを見る

まずはCANのハード的な解説

arduino-can-multidata_01

 ArduinoでCAN通信を行うためにはMCP2515という外部モジュールが必要になります。

 ArduinoからMCP2515に対してフォーマットに従ったデータ(電気信号)を送れば、あとはMCP2515側が勝手に送信をしてくれます。そして相手側からデータが来た場合も自動的に受信処理を行ってくれます。

 CAN通信は設定次第では送信も受信も同時に行うことが可能ですが、今回の図では便宜的に左側を送信専用、右側を受信専用としました。

arduino-can-multidata_02

 MCP2515にはRX0とRX1という2つの8Byteバッファがありまして、CANでデータを受信した場合そこに自動的にデータを格納します。ArduinoからCAN.readMsgBufという関数を使用するとMCP2515に格納されているデータを取得可能になり、またバッファをクリアしてあげることができます。

 そうすることでMCP2515はまた新たなデータをバッファに格納できるのですが、RX0/RX1両方埋まった状態で新たなデータが来た場合はオーバーフローとなり、それ以上はデータの受信ができなくなってしまいます。

 なのでArduino側はCANでデータを受信するたびに割り込みを発生させて、都度CAN.readMsgBufを行いバッファのデータ取得&クリアを行います。Arduinoに限らずどのマイコンにも割り込みコントローラーというものがあり、設定次第では特定のピンに電気信号が送られたらそれをCPUに通知する機能があります。

 ArduinoのCAN通信では2番ピンが割り込みコントローラーに接続されているので、MCP2515からはデータを受信したら2番ピンに電気信号を送らせ、割り込みが発生したらバッファを読み込みに行く処理にします。

 

サンプルコード

 今回は3種類のCAN IDのデータを数秒おきに送り、受信側はそれをシリアルモニタに表示するだけの単純なプログラムを作成したいと思います。

CAN ID(16進数) データ
0x100 int intData = 1234
0x101 float floatData = 56.78
0x102 char charData = 'A'

送信側

 

受信側

解説

 送信側は特に問題ないと思いますので、受信側を解説します。

 まずはCAN受信の仕組みを理解するためにものすごく単純なプログラムを作成してみました。

 CAN受信が発生(2番ピンに電気信号が送られる)したら割り込み関数を発動してhandleCANInterrupt関数が呼ばれるように設定します。そしてhandleCANInterrupt関数の中でcanInterrupt(割り込みフラグ)をONにします。

 void loop()の中でcanInterrupt(割り込みフラグ)を判定し、フラグがONであればCAN.readMsgBufでバッファのデータを取得します。

CAN.readMsgBufの関数だけでなく

  • CAN0.readMsgBuf
  • CAN1.readMsgBuf

という関数もあります。こちらを使うとRX0/RX1を指定してバッファからの読み出しが可能になります。
とはいえCAN.readMsgBufはバッファが埋まっている方を読み出しに行くので、基本的にはこの関数を使えばよいでしょう

このプログラムの問題点

 割り込みによって「割り込みフラグ」をONにしにいって、loopの中で実際にバッファデータの取得を行うわけですが、割り込みが行われてloop関数が発動するまでに連続でCANデータが送られた場合、取りこぼしが発生してしまいます。

 なのでこの問題を解決するために以下の方法を採りたいと思います。

  • Arduino側でCANデータを格納するためのバッファを用意しておく
  • 構造体配列でCANID、データ長、データが保存できるようにする
  • データはとりあえず8Byte分配列用意しておいて、そこにデータを入れておく
  • データは使う際に、必要な部分だけ取り出す
  • 割り込みが発生するたびにMCP2515バッファ→Arduinoバッファにデータを退避させる
  • void loop()の方で、退避させたArduinoバッファデータを使ってシリアルモニタ表示を行う
  • データの格納/取り出し方法は以前このブログでも取り上げたFIFO方式とする

改良版プログラム

解説

 構造体配列RecBufを用意して、割り込み関数handleCANInterruptの中でFIFO方式で都度データを入れる方式とします。

 そしてvoid loop()の中でRecBufからデータを取り出し、IDに応じてシリアルモニタで表示する方式とします。

 バッファのデータ(rxBuf)は8Byte分確保してしまっているので、memcpyを使用してデータ型に応じて必要な部分だけ取り出します。

 

備考:バッファ構造体のイメージ図

arduino-can-multidata_03

 

 以上がCANを使ってArduino2台で複数データの送受信を行う方法でした。

 誰かに解説してもらえれば簡単に頭に入りますが、巷にはこういった解説が出ていないのが困りどころですよね。みなさんもこの記事を読んで、ぜひCANで複数データの送受信を行ってみましょう!



-電子工作
-

© 2025 名古屋とエンジニアリング