2026年8月17日月曜日

stm32h753ziをArduinoでCANFDを使う


背景

マイコンstm32h753ziはCAN FDの回路を積んでいます。
使い方が気になったのでArduino環境で呼び出して使ってみました。
備忘録として対応内容を記事に残します。

使ったもの

  • nucleo-h753zi
    今回利用するマイコンであるstm32h753ziの開発ボードです。 
  • MCP2562FD
    CANのトランシーバーです。
  • ブレッドボード
    部品の信号線を引き出します。
  • ジャンパワイヤ
    ブレッドボード上は硬いもの、ブレッドボード間は柔らかいものを利用しました。
  • 120Ω抵抗
    CAN busの終端抵抗に利用します。
  • microB-USBケーブル
    nucleoへのプログラム書き込みに利用します。
  • 通信相手 teensy4.1とCAN FD用の回路
    前回取り組んだteensy4.1でCAN FDを使えるようにした構成との通信を行います。
    通信速度1Mbpsではstm32h753ziと同期できなかったので、基本速度を500kbps、FD部分を2Mbpsにして通信しました。
  • プログラム書き込み環境 PlatformIO
    ubuntu24.04 にVSCodeとPlatformIOをインストールした環境を使いました

回路

今回はCAN1の回路を使います。
stm32h753ziの説明書でCAN1と検索すると、それ用のピンが分かります。
今回はnucleoで分かりやすく引き出されているPD0とPD1を使います。


nucleo-h753ziのピン配列を示す説明書でPD0とPD1の場所を探すとCN9端子にあると分かります。

実機だとここです。
CN9端子のCANと書かれた部分のGNDに近い側がPD0でCAN RX、GNDから遠い側がPD1でCAN TXです。


上記のPD1とPD2に加えて5V、3.3V、GNDをCANのトランシーバーに繋ぎます。


繋ぐとこうなりました。


この回路を後で使います。

ライブラリ準備 h753zi対応版acanfd-stm32

h743ziに対応しているライブラリacanfd-stm32h753ziでも使えるようにした変更したライブラリを使います。
変更内容のpull requestをadanfd-stm32に対して出していますが、1年ほど動きが無いライブラリなので、取り込まれる可能性が低そうです。
そのため、自分が変更を加えたライブラリをplatformioで取り込んで使います。

platformio.iniのlib_depsにgithubのブランチを指定すれば使えます。
lib_deps =
https://github.com/asukiaaa/acanfd-stm32#feature/support-h753zi

acanfd-stm32を利用するCAN FD通信プログラム

下記の処理を行うプログラムです。
acanfd-stm32のサンプルプログラムH743ZI2-LoopBackDemoを書き換えて作成しました。
  • PD0をCAN RX、PD1をCAN TXに利用 
  • 基本通信速度 500kbps
  • FD部分の通信速度 2Mbps
  • 2秒毎にCAN FDの情報配信
  • 8bytesと64bytesの配信を交互に実施
  • 情報を受信するたびにLEDのON OFFを切り替え
  • 受信内容をシリアル通信で配信

#include <Arduino.h>

static const uint32_t FDCAN1_MESSAGE_RAM_WORD_SIZE = 2000;
static const uint32_t FDCAN2_MESSAGE_RAM_WORD_SIZE = 0;

#include <ACANFD_STM32.h>

//-----------------------------------------------------------------

void setup() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
Serial.begin(115200);

ACANFD_STM32_Settings settings(500 * 1000, DataBitRateFactor::x4);

Serial.print("Bit Rate prescaler: ");
Serial.println(settings.mBitRatePrescaler);
Serial.print("Arbitration Phase segment 1: ");
Serial.println(settings.mArbitrationPhaseSegment1);
Serial.print("Arbitration Phase segment 2: ");
Serial.println(settings.mArbitrationPhaseSegment2);
Serial.print("Arbitration SJW: ");
Serial.println(settings.mArbitrationSJW);
Serial.print("Actual Arbitration Bit Rate: ");
Serial.print(settings.actualArbitrationBitRate());
Serial.println(" bit/s");
Serial.print("Arbitration sample point: ");
Serial.print(settings.arbitrationSamplePointFromBitStart());
Serial.println("%");
Serial.print("Exact Arbitration Bit Rate: ");
Serial.println(settings.exactArbitrationBitRate() ? "yes" : "no");
Serial.print("Data Phase segment 1: ");
Serial.println(settings.mDataPhaseSegment1);
Serial.print("Data Phase segment 2: ");
Serial.println(settings.mDataPhaseSegment2);
Serial.print("Data SJW: ");
Serial.println(settings.mDataSJW);
Serial.print("Actual Data Bit Rate: ");
Serial.print(settings.actualDataBitRate());
Serial.println(" bit/s");
Serial.print("Data sample point: ");
Serial.print(settings.dataSamplePointFromBitStart());
Serial.println("%");
Serial.print("Exact Data Bit Rate: ");
Serial.println(settings.exactDataBitRate() ? "yes" : "no");

// settings.mModuleMode = ACANFD_STM32_Settings::EXTERNAL_LOOP_BACK;

// settings.mTxPin = PB_9;
// settings.mRxPin = PB_8;
settings.mTxPin = PD_1;
settings.mRxPin = PD_0;
uint32_t errorCode = fdcan1.beginFD(settings);
Serial.print("FDCAN1 Message RAM required minimum size: ");
Serial.print(fdcan1.messageRamRequiredMinimumSize());
Serial.println(" words");
if (0 == errorCode) {
Serial.println("fdcan1 ok");
} else {
Serial.print("Error can: 0x");
Serial.println(errorCode, HEX);
}
}

uint32_t can1SentAt = 0;
bool flagSend64bytes = false;
auto sendIntervalMs = 2000UL;

void loop() {
if (millis() - can1SentAt >= sendIntervalMs) {
can1SentAt = millis();
CANFDMessage message;
message.id = 0x7FF;
message.len = flagSend64bytes ? 64 : 8;
flagSend64bytes = !flagSend64bytes;
for (int i = 0; i < message.len; ++i) {
message.data[i] = i + 1;
}
message.data[0] = can1SentAt / 1000;
const uint32_t sendStatus = fdcan1.tryToSendReturnStatusFD(message);
Serial.println("FDCAN1 send " + String(message.len) + " bytes status " +
String(sendStatus) + " at " + String(millis()));
}
CANFDMessage messageFD;
if (fdcan1.receiveFD0(messageFD)) {
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
Serial.println("FDCAN1 received at " + String(millis()));
Serial.print("id: ");
Serial.println(messageFD.id);
Serial.print("data:");
for (uint8_t i = 0; i < messageFD.len; i++) {
Serial.print(" ");
Serial.print(messageFD.data[i]);
}
Serial.println();
}
}

下記の記述が基本通信速度500kbps、FD部分の通信速度2Mbpsを意味します。
500kの4倍だから2Mbpsということです。
  ACANFD_STM32_Settings settings(500 * 1000, DataBitRateFactor::x4);

動作確認

Teensy4.1と通信させます。
繰り返しになりますが、Teensy4.1も基本部分500kbps、FD部分2Mbpsで通信させます。
基本部分1Mbpsだと同期が取れず通信できなかったので、500kbpsにしました。

繋いで通信させます。

h753ziのログです。
期待通りに8bytesと64bytesの情報を受信しました。
FDCAN1 send 64 bytes status 0 at 16000
FDCAN1 received at 17411
id: 294
data: 18 2 3 4 5 6 7 8
FDCAN1 send 8 bytes status 0 at 18000
FDCAN1 received at 19449
id: 1617
data: 20 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

Teensy4.1のログです。
こちらも期待通りに受信しました。
sent 64bytes at 16008
received 64bytes at 16623
rx.id: 2047
rx.buf 16 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
sent 8bytes at 18009
received 8bytes at 18587
rx.id: 2047
rx.buf 18 2 3 4 5 6 7 8

しかしながら、たまに同期外れが発生して以後通信不能になるので、まだ把握できていないCANの再起動処理が必要そうです。

おわり

stm32h753ziでCAN FD通信ができました。
気になっていた機能の使い方が分かって良かったです。

参考

acanfd-stm32
h753ziでも使えるようにした変更

0 件のコメント :