Arduino UNO R4でCAN通信

Featured image of the post

目次

はじめに

📄Arrow icon of a page linkRaspberry Pi PicoとArduino IDEでCAN通信

このまえCANコントローラの無いRaspberry Pi Pico(RP2040)でCAN通信をしました。(CAN通信=ロボマスモーターを回すこと だと思ってる人種)

以前の記事に書いた通り、Arduino UNOはR4になってCANコントローラを内蔵したことは知っていたのですが、実物が手元になかったので試せませんでした。

触る機会がやってきたので回してみました。

苦労した点

以前のコード(RP2040)はArduino CAN互換のCANライブラリ で動いていたので、今回はほとんどそのままで、寧ろ削るくらいで回せました。

一番困ったのはArduino UNO R4 MinimaとWiFiでCANRX,TXのピン配置が異なっていたことです。Minima用のピンで動かないなぁと5分くらいクネクネやってました。パッと検索して出てくるピン配置の図にCANの表示も入れてほしいですね。

UNO R4 Minima / Nano R4 UNO R4 WiFi
D4(CANTX0) D10(CANTX0)
D5(CANRX0) D13(CANRX0)

動いている様子

ついでにDamiaoモーター

コード

以前の記事のコードと違ってフィードバック情報のシリアル出力を一定時間毎にしています。1kHzのままでは暴走したのでやめました。クロック周波数がRaspberry Pi Picoの1/3くらいしかないので単純に処理能力の差なんですかね。

/*
UNO R4 Minima/Nano R4
D4(CANTX0)
D5(CANRX0)

UNO R4 WiFi
D10(CANTX0)
D13(CANRX0)
*/

#include <Arduino_CAN.h>

const float P_GAIN = 100.0f;              // Pゲイン
const float TARGET_RPS = 10.0f;           // 目標回転数
const int16_t CURRENT_LIMIT_MA = 10000;   // 電流指令値の制限 (mA) (-10A to +10A)
const uint32_t MOTOR_COMMAND_ID = 0x200;  // 指令を送信するCAN ID

float fmap(float x, float in_min, float in_max, float out_min, float out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

void setup() {
  Serial.begin(115200);
  CAN.begin(CanBitRate::BR_1000k);
}

void loop() {
  while (CAN.available()) {
    CanMsg rxMsg = CAN.read();

    int16_t rotorPositionRaw = (int16_t)(rxMsg.data[0] << 8 | rxMsg.data[1]);
    int16_t rpm = (int16_t)(rxMsg.data[2] << 8 | rxMsg.data[3]);
    int16_t actualTorqueCurrent = (int16_t)(rxMsg.data[4] << 8 | rxMsg.data[5]);
    //data[6] and data[7] are Null

    float rotorDegree = fmap(rotorPositionRaw, 0, 8192.0f, 0, 360.0f);
    float rps = rpm / 60.0f;

    float error = TARGET_RPS - rps;
    float targetCurrent = P_GAIN * error;
    int16_t commandCurrent = constrain(targetCurrent, -CURRENT_LIMIT_MA, CURRENT_LIMIT_MA);

    CanMsg txMsg = {};
    txMsg.id = MOTOR_COMMAND_ID;
    txMsg.data_length = 8;
    txMsg.data[0] = commandCurrent >> 8;  // モーターid1 電流値上位バイト8ビット
    txMsg.data[1] = commandCurrent;       // モーターid1 電流値下位バイト8ビット
    // txMsg.data[2] = commandCurrent >> 8;  // モーターid2 電流値上位バイト8ビット
    // txMsg.data[3] = commandCurrent;       // モーターid2 電流値下位バイト8ビット
    // txMsg.data[4] = commandCurrent >> 8;  // モーターid3 電流値上位バイト8ビット
    // txMsg.data[5] = commandCurrent;       // モーターid3 電流値下位バイト8ビット
    // txMsg.data[6] = commandCurrent >> 8;  // モーターid4 電流値上位バイト8ビット
    // txMsg.data[7] = commandCurrent;       // モーターid4 電流値下位バイト8ビット
    CAN.write(txMsg);

    static unsigned long lastPrintTime = 0;
    if (millis() - lastPrintTime > 10) {
      lastPrintTime = millis();
      Serial.print("ID: 0x");
      Serial.print(rxMsg.id, HEX);
      Serial.print(", Angle: ");
      Serial.print(rotorDegree);
      Serial.print(" deg, RPS: ");
      Serial.print(rps);
      Serial.print(", Command_mA: ");
      Serial.print(commandCurrent);
      Serial.print(", Actual_mA: ");
      Serial.print(actualTorqueCurrent);
      Serial.println();
    }
  }
  delay(1);
}

感想

ハードウェアに左右されないコードだと移植が楽すぎて気分がいいです。

Arduino IDE環境のマイコンでは、Arduino UNO R4, RP2040, RP2350, ESP32でCAN通信をしてロボマスモーターを回せました。ついでにDamiaoモーターも回せました。

あとはSTM32だけ。