1
0

ジンバルモーター、SimpleFOCで動かす(9) - 磁気センサ2個接続

Last updated at Posted at 2024-08-04

はじめに

前回のの続きです。

Arduino Uno R4にBLDCモーターを2個接続しました。当然、磁気センサも2個になります。Arduino Uno R4とのつなぎ方とソースコードについて考えてみました。

I2Cマルチプレクサ

前回の図を再掲します。

BLDC_SimpleFOC_BLDCx2.png

AS5600とはI2C接続しています。I2Cはチップ毎にユニークな7bitのアドレスを持っていて通信相手を決めています。AS5600のアドレスは固定のため、2個つなぐとおかしなことになります。

対策方法を考えてみました。

  1. マイコン側で複数のI2Cマスタを使えるの能力があるなら、それぞれに接続する。
  2. I2Cマルチプレクサで切り替え
  3. I2Cアドレストランスレーターでアドレスを書き換え

1が手っ取り早いです。Arduino Uno R4はピンヘッダのA5とA6を、2つ目のI2Cとして使えます。私はこのGPIOを別の用途に使いたかったので却下しました。

SCLとSDAは、A4とA5共用でした。Arduino Uno R4 Wi-Fiの場合はQwiicコネクタに2つ目のWire1が出ているそうです。ただしQwiicの電圧は3.3Vであることに注意が必要です。Arduino Uno R4 Wi-Fiは所有していないため却下となります。

あとは2と3ですが、I2Cデバイスが2個なら3が楽かなと思います。しかし私は2のマルチプレクサを使いました。単純に先に手元に届いたからです。

図の右上あたりにある TCA9548A が使用したICです。TCA9548A自体もI2Cのアドレスを持っていて、ICにぶら下げた磁気センサへのチャンネルを切り替えてくれます。8chも切り替えられますが、TIのラインナップを見ると2ch品・4ch品もあるみたいですね。アマゾンで5個セット¥749で入手できました。

TCA9548A

TCA9548A_1.png

TCA9548Aモジュールにはプルアップ抵抗がついていました。よってマスター側のSCLとSDAへ直接つないで問題ないです。RESETは今回使いません。A0・A1・A2はアドレス指定になります。アドレスは表のようになります。どこにも接続しないでみたらデフォルトの70hでした。IC内部でプルダウンしているのかもしれませんがドキュメントに記載が見つからなかったので、なるべくGNDかVCCにつないだほうがよさそうです。

TCA9548A_3.png

磁気センサはひとつめをSC0・SD0へ接続、ふたつめはSC1・SD1に接続しました。

ソースコード

どの磁気センサと通信するかを切り替える方法は、TCA9548AにI2Cで書き込みをおこないます。下表のように8bitのそれぞれのビットがチャンネルに対応していて、ビットを立てる(1にする)と通信できます。ちなみにch0とch1を同時オンにすることも可能だそうです。書き込みをするときはいいですが、読み込みのときは最後に受信したバイトがTCA9548Aに保存されマスターがそれを読み込みますので、ちょっと使い方としては難しそうです。今回はこのような使い方はしません。

TCA9548A_2.png

Arduinoのコードはこんな感じで切り替えます。説明するほどのこともないですが一応。

Wire.begin();
Wire.setClock(400000);

byte ch = 0;

Wire.beginTransmission(0x70);
Wire.write(1 << ch);
Wire.endTransmission();

SimpleFOCで使いやすいようにコーディングスタイルを合わせたclassを作っておきます。

SelectableI2CTCA9548A.h
#ifndef TCA9548A_LIB_H
#define TCA9548A_LIB_H
#include "Arduino.h"
#include <Wire.h>

class SelectableI2CTCA9548A {
  public:
    static constexpr uint8_t TCA9548A_I2C_ADDR_BASE = 0x70;
    static constexpr uint8_t TCA9548A_I2C_ADDR = TCA9548A_I2C_ADDR_BASE + 0;

    SelectableI2CTCA9548A(uint8_t _tca_addr = TCA9548A_I2C_ADDR)
      : tca_addr(_tca_addr) {
    }

    void init(TwoWire* _wire = &Wire) {
      wire = _wire;
    }

    void setTcaChannel(uint8_t _ch);

  protected:
    uint8_t tca_addr;
    TwoWire* wire;
};

#endif
SelectableI2CTCA9548A.cpp
#include "SelectableI2CTCA9548A.h"

void SelectableI2CTCA9548A::setTcaChannel(uint8_t _ch) {
  if (_ch > 7) return;
  wire->beginTransmission(tca_addr);
  wire->write(1 << _ch);
  wire->endTransmission();
}

MagneticSensorI2C classを継承する

MagneticSensorI2C classを拡張し、TCA9548Aでチャンネルを切り替えて通信できるようにします。

もともと基底クラスである Sensor で純粋仮想関数が定義されているため拡張可能です。MagneticSensorI2C のinit関数にvirtualキーワードがないことに注意が必要です。親クラスにキャストすると、子クラスのオーバーライドしたメソッドはコールできなくなります。できればSimpleFOCライブラリでvirtualをつけるように変更して欲しいと思っています。

作成したクラスはこちらです。前出の SelectableI2CTCA9548AMagneticSensorI2C を多重継承しています。SelectableI2CTCA9548A はメンバ変数にしてもいいので迷いました。

MagneticSensorSelectableI2C.h
#ifndef MAGNETICSENSORSELECTABLEI2C_LIB_H
#define MAGNETICSENSORSELECTABLEI2C_LIB_H
#include "Arduino.h"
#include <Wire.h>
#include "sensors/MagneticSensorI2C.h"
#include "SelectableI2CTCA9548A.h"

class MagneticSensorSelectableI2C: public SelectableI2CTCA9548A, public MagneticSensorI2C {
  public:
    MagneticSensorSelectableI2C(uint8_t _chip_address, int _bit_resolution, uint8_t _angle_register_msb, int _msb_bits_used, uint8_t _ch = 0, uint8_t _tca_addr = TCA9548A_I2C_ADDR);

    MagneticSensorSelectableI2C(MagneticSensorI2CConfig_s config, uint8_t _ch = 0, uint8_t _tca_addr = TCA9548A_I2C_ADDR);

    void init(TwoWire* _wire = &Wire);

    float getSensorAngle() override;

  protected:
    uint8_t ch;
};

#endif
MagneticSensorSelectableI2C.cpp
#include "MagneticSensorSelectableI2C.h"

MagneticSensorSelectableI2C::MagneticSensorSelectableI2C(uint8_t _chip_address, int _bit_resolution, uint8_t _angle_register_msb, int _msb_bits_used, uint8_t _ch, uint8_t _tca_addr)
  : SelectableI2CTCA9548A(_tca_addr),
  MagneticSensorI2C(_chip_address, _bit_resolution, _angle_register_msb, _msb_bits_used) {
  ch = _ch < 8 ? _ch : 0;
}

MagneticSensorSelectableI2C::MagneticSensorSelectableI2C(MagneticSensorI2CConfig_s config, uint8_t _ch, uint8_t _tca_addr)
  : SelectableI2CTCA9548A(_tca_addr), MagneticSensorI2C(config) {
  ch = _ch < 8 ? _ch : 0;
}

/** sensor initialise pins */
void MagneticSensorSelectableI2C::init(TwoWire* _wire) {
  this->SelectableI2CTCA9548A::init(_wire);
  this->MagneticSensorI2C::init(_wire);
}

// implementation of abstract functions of the Sensor class
/** get current angle (rad) */
float MagneticSensorSelectableI2C::getSensorAngle() {
  setTcaChannel(ch);
  return this->MagneticSensorI2C::getSensorAngle();
}

使い方のショートコードです。

#include <Arduino.h>
#include <SimpleFOC.h>
#include "MagneticSensorSelectableI2C.h"

MagneticSensorSelectableI2C sensor1{AS5600_I2C, 0}; // channel 0
MagneticSensorSelectableI2C sensor2{AS5600_I2C, 1}; // channel 1

void setup() {
  Serial.begin(115200);
  Wire.begin();
  Wire.setClock(400000);

  sensor1.init();
  sensor2.init();
}

void loop() {
  Serial.print(sensor1.getAngle());
  Serial.print("\t");
  Serial.println(sensor2.getAngle());
  delay(100);
}

子クラス内でTCA9548Aの切り替え処理を隠蔽しているので、通常の MagneticSensorI2C と同じように扱うことができました。MotorとMotorDriverへの登録は、以前の記事で書いたモーター1個の場合を、2個目のモーターでも繰り返すだけなので割愛します。

さいごに

twe_bldc.gif

2個のBLDCモーターを動かしている様子です。2軸カメラジンバルを作れますね。カメラジンバルは磁気センサではなくIMUを使いますけど笑

記事で書いたセンサーやモーターとは別の品を入手して実験を続けてますが、記録していくほどの話題はないようです。というわけで今回で区切りをつけます。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0