📌

ArduinoとAtom matrix間でCAN通信

2021/11/10に公開

標記の内容をやってみたのでメモします。

1.接続図


[送信側]
・Arduino Uno
・MCP2515 CANバスモジュール
https://www.amazon.co.jp/gp/product/B07F68GHBJ/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1

[受信側]
・Atom matrix
・M5Stack用CAN-BUSユニット

2.動かしてる様子

見た通りですが、ブレッドボードのボタンを押すことでRGBの色情報を送信しています。
同時押しすることで色の合成ができます。

3.プログラム解説

[送信側]
ボタンを接続するGPIO端子を全て内部プルアップの設定にしています。

pinMode(red_button, INPUT_PULLUP);
pinMode(green_button, INPUT_PULLUP);
pinMode(blue_button, INPUT_PULLUP);

ブレッドボードの配線がシンプルになります。
ポートがLOWに落ちたことをトリガにして状態を変えています。

byte rgb[] = {0x00,0x00,0x00}
この配列が左からR,G,Bの並びになっていて、
赤ボタンが押されたら{0xFF,0x00,0x00}
緑ボタンが押されたら{0x00,0xFF,0x00}
青ボタンが押されたら{0x00,0x00,0xFF}
になります。
赤と緑同時押しで{0xFF,0xFF,0x00}で黄色になります。

そういえば、MCP2515を使うにあたって、まずこちらの記事を参考にしてライブラリを入れました。
https://qiita.com/covao/items/d30fa5e36470bbee3be7
今回の作例もこちらのコードを改造しています。

一度この記事のコードをそのまま動かして、オシロで波形をモニタしたのが以下の図。

CH1がCAN_H、CH2がCAN_L、赤はCH1とCH2の差分です。
実際にはこの赤の波形で信号を認識することになります。
ちゃんとノイズがキャンセルされています。

この波形は、MCP2515モジュールのジャンパをショートして、120Ωの終端抵抗を有効化しています。
受信側のCAN-BUSユニットにも終端抵抗をつけたほうがいい気がしますが、つけると電圧が半分になったので、つけませんでした。それで通信は成功しました。
電圧レベルとしてどの辺が適当か、調べ切れてないです…

[受信側]
M5.dis.drawpix(j,CRGB(rx_frame.data.u8[0],rx_frame.data.u8[1],rx_frame.data.u8[2]))
この部分でLEDを表示させていて、CRGB(X,X,X)の部分に左からR,G,Bの値が入ります。
その前の for(int j=0; j<25; j++){ で、0から24番目のピクセルまで順番に表示させることになります。

CAN-ID:100を受け取ったらLEDを点灯させますが、受け取っていない時はdisp_clear()関数で消灯させています。

このコードは、M5Atomのサンプルコードを改造しています。
Arduino IDEで ファイル→スケッチ例→M5Atom→Unit→CAN で表示できるコードです。

こちらの記事も参考にしました。
https://knt60345blog.com/can-bus/

CANの通信速度は送信側・受信側で合わせておく必要があります。

4.コード

送信側
#include <mcp_can.h> 
#include <SPI.h>

unsigned long rxId;
byte len;
byte rgb[] = {0x00,0x00,0x00};

MCP_CAN CAN0(10);// CAN0 CS: pin 10

int red_button = 7;
int green_button = 6;
int blue_button = 5;
int red_buttonState = 0;
int green_buttonState = 0;
int blue_buttonState = 0;

void setup()
{
  Serial.begin(115200);

  // init CAN0 bus, baudrate: 500kbps@8MHz
  if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ) == CAN_OK){
    Serial.println("CAN0: Init OK!");
    CAN0.setMode(MCP_NORMAL);
  } else{ 
    Serial.println("CAN0: Init Fail!");
  }

  //pinMode(ledPin, OUTPUT);      
  pinMode(red_button, INPUT_PULLUP);
  pinMode(green_button, INPUT_PULLUP);
  pinMode(blue_button, INPUT_PULLUP);
}

void loop(){
  
  red_buttonState = digitalRead(red_button);
  green_buttonState = digitalRead(green_button);
  blue_buttonState = digitalRead(blue_button);
  
  if (red_buttonState == LOW) {
    rgb[0]={0xFF};
  } 
  else{
    rgb[0]={0x00};
  }

    if (green_buttonState == LOW) {
    rgb[1]={0xFF};
  } 
  else{
    rgb[1]={0x00};
  }

    if (blue_buttonState == LOW) {
    rgb[2]={0xFF};
  } 
  else{
    rgb[2]={0x00};
  }

  CAN0.sendMsgBuf(0x100, 0, 8, rgb);
  Serial.print(rgb[0],HEX);
  Serial.print(rgb[1],HEX);
  Serial.print(rgb[2],HEX);
  Serial.println();
    
  delay(200); //ここの時間変更でボタン押下のチャタリング対策できる
}
受信側
#include <M5Atom.h>
#include "ESP32CAN.h"
#include "CAN_config.h"

#define TX GPIO_NUM_26
#define RX GPIO_NUM_32

CAN_device_t CAN_cfg;

int i,j = 0;

void disp_clear(){
  for(int j=0; j<25; j++){
    M5.dis.drawpix(j, 0x000000); //black
  }
}
void setup() {
  M5.begin(true, false, true);
  Serial.println("CAN Unit Send&Received");

  CAN_cfg.speed = CAN_SPEED_500KBPS;  //Set the Can speed
  CAN_cfg.tx_pin_id = TX; //Set the Pin foot
  CAN_cfg.rx_pin_id = RX;

  CAN_cfg.rx_queue = xQueueCreate(10,sizeof(CAN_frame_t));
  ESP32Can.CANInit(); // Init CAN Module
}

void loop() {
  CAN_frame_t rx_frame;

  if(xQueueReceive(CAN_cfg.rx_queue,&rx_frame, 3*portTICK_PERIOD_MS)==pdTRUE){

    Serial.print(millis());   // 受信時間
    Serial.print(",");
    Serial.print(rx_frame.MsgID,HEX);   // Msg ID
    Serial.print(",");
    Serial.print(rx_frame.FIR.B.DLC);   // DLC
    Serial.println(",");

    for(int i = 0; i < 3; i++){
      Serial.print(rx_frame.data.u8[i],HEX);   // CANデータ
    }
    Serial.println();
    
    if(rx_frame.MsgID==256){ //CAN-ID:0x100が10進で256なので。IDは何でもいい。
      Serial.println("received");
      for(int j=0; j<25; j++){
        M5.dis.drawpix(j,CRGB(rx_frame.data.u8[0],rx_frame.data.u8[1],rx_frame.data.u8[2]));
      }
    }
  }
  else {
    disp_clear();
  }

  M5.update();
  delay(200);

}

以上です。

Discussion