Skip to content

STM32F446RE 複数マイコン連携技術資料 - CAN通信によるセンサ・モータ制御

STM32F446REマイコンを用いて、CAN通信で複数マイコンを連携させる実装方法を解説する。

目標

  • STM32F446REを使って CAN2通信(B13, B12) を設定する
  • マイコン1:リミットスイッチの状態を定期的に送信する
  • マイコン2:受信した情報に応じてモータ制御・エンコーダPID制御を行う

必要なもの

種類 内容
MCU STM32F446RE(NUCLEO-F446RE) × 2台
ライブラリ sken_library(CAN Legacy対応版)
接続 CAN通信線(TX: B13, RX: B12)
その他 リミットスイッチ、モータ、モータドライバ、エンコーダなど

STEP 1: 開発環境セットアップ

HALとプロジェクト設定

  1. SW4STM32でC++プロジェクトを作成
  2. ボードは NUCLEO-F446RE を選択
  3. HAL_CAN_MODULE_ENABLED を無効化し、HAL_CAN_LEGACY_MODULE_ENABLED を有効化

sken_library導入

  • #include "sken_library/include.h"
  • sken_system.init();main() 冒頭に記述

(詳しくはこちら参照)

STEP 2: 配線図

[マイコン1] ----------- CAN2 (B13, B12) ----------- [マイコン2]
  | A6,A7,A8 : リミットスイッチ入力                  | A0,A1 : エンコーダ入力
  | C11      : スイッチ入力                         | B14,B15 : MD1(PWM)
                                                  | A8,A11 : MD2(PID制御)

STEP 3: マイコン1 - スイッチ送信処理

概要

  • 3つのリミットスイッチと1つのユーザースイッチ状態を読み取り、CANで送信

コード

#include "stm32f4xx.h"
#include "sken_library/include.h"

Gpio limit1, limit2, limit3, button;
uint8_t can_data[4];

int main() {
    sken_system.init();
    limit1.init(A6, INPUT_PULLUP);
    limit2.init(A7, INPUT_PULLUP);
    limit3.init(A8, INPUT_PULLUP);
    button.init(C11, INPUT_PULLUP);
    sken_system.startCanCommunicate(B13, B12, CAN_2);

    while (1) {
        can_data[0] = limit1.read();
        can_data[1] = limit2.read();
        can_data[2] = limit3.read();
        can_data[3] = button.read();

        sken_system.canTrancemit(CAN_2, 0x101, can_data, 4);
        sken_system.delayMillis(10);
    }
}

解説

  • INPUT_PULLUP:スイッチ未押下でHIGHになるようにする
  • canTrancemit:最大8バイト送信可能(ここでは4バイト使用)

STEP 4: マイコン2 - モータとPID制御

制御仕様

  • C11スイッチが偶数回押されたら制御が有効に
  • A6:ONになるまでMD1順転
  • A7:ONになるまでMD1逆転
  • A8スイッチがON:エンコーダを使いMD2を90度へPID制御(OFF時は0度)

コード

#include "stm32f4xx.h"
#include "sken_library/include.h"

Motor md1, md2;
Encoder enc;
Encoder_data e_data;
Pid pid;
Can_data can_rx;

int c11_count = 0;
bool last_c11 = 1;
double target_angle = 0;
double output = 0;

void control_loop() {
    enc.interrupt(&e_data);
    output = pid.control(target_angle, e_data.deg, 1);
}

int main() {
    sken_system.init();

    enc.init(A0, A1, TIMER5);
    md1.init(Apin, B14, TIMER12, CH1);
    md1.init(Bpin, B15, TIMER12, CH2);
    md2.init(Apin, A8, TIMER1, CH1);
    md2.init(Bpin, A11, TIMER1, CH4);
    pid.setGain(1.0, 0.0, 0.1, 10);
    sken_system.addTimerInterruptFunc(control_loop, 0, 1);

    sken_system.startCanCommunicate(B13, B12, CAN_2);
    sken_system.addCanRceiveInterruptFunc(CAN_2, &can_rx);

    while (1) {
        if (can_rx.rx_stdid == 0x101) {
            bool a6 = can_rx.rx_data[0] == 0;
            bool a7 = can_rx.rx_data[1] == 0;
            bool a8 = can_rx.rx_data[2] == 0;
            bool c11 = can_rx.rx_data[3] == 0;

            if (last_c11 == 1 && c11 == 0) c11_count++;
            last_c11 = c11;

            if (c11_count % 2 == 0) {
                if (!a6) md1.write(50);
                else if (!a7) md1.write(-50);
                else md1.stop();
            } else {
                md1.stop();
            }

            target_angle = a8 ? 90.0 : 0.0;
            md2.write(output);
        }
    }
}

トラブル対処

CAN通信が動かない

  • HAL_CAN_LEGACY_MODULE_ENABLED を有効にしているか?
  • 配線(B13, B12)が逆ではないか?
  • ターミネータ抵抗(120Ω)が両端にあるか?

スイッチが効かない

  • INPUT_PULLUPの設定漏れ
  • GNDとの接続確認
Note

著者:Shion Noguchi