yoshiyuki's blog

Arduino/Teensy/Raspberry pi pico を利用した I2C, SPI 通信アプリを紹介します

Teensy 4.0 / I2C ができるまで (2)

前回レジスタの読み書きが出来るようになったので I2C 通信を行うための準備をしていきます。
前回に続いて繰り返しますが、I2C通信をやりたいだけであれば Wireライブラリを使う方が簡単、確実です。

最終的には I2C 関数の自作に至ります。
ysin1128.hatenablog.com

さらに、I2C に加えて SPI と GPIO も制御する Sketch とツールも作成しました。
ysin1128.hatenablog.com

端子設定

SCL, SDAを端子に割り当てます。Teesy 4.0 は複数の I2Cモジュールを持っていて、どの端子を SCL, SDA として使うかはモジュールによって異なります。また、1つのモジュールが SCL, SDA として使う端子を複数の候補から選べたりもします。
どの端子でもいいやと思っていたので自由度の高さに逆に困ったのですが、とりあえず Teesy 4.0 の端子表によると #18 が SDA0, #19 が SCL0 とあるので、これで設定してみることにしました。

f:id:ysin1128:20210208114524p:plain

端子の選択

まず Teesy 4.0 の回路図から #18, #19 が搭載マイコン IMXRT1062 のどの端子に繋がっているか確認します。回路図によると #18 は AD_B1_01, #19 は AD_B1_00 に繋がっているようです。

f:id:ysin1128:20210208114640p:plain

IMXRT1062 の Reference Manual によるとこれら端子を使うのは LPI2C1 モジュールとのことです (ちなみに、LPI2Cモジュールは 1 から 4 まであります)。さらに調べたところ、LPI2C1モジュールがSCL, SDAとして使用できる端子は他にもあり、実際にどの端子を使うかをレジスタで設定する必要があるようです。LPI2C1 の場合は下記端子から選択できます。

LPI2C1_SCL: AD_B1_00, SD_B1_04
LPI2C1_SDA: AC_B1_01, SD_B1_05

ここでは SCL, SDA ともに AD_B1_xx の方を使いたいので以下のコードで設定します。

IMXRT_REGISTER32_t *port_iomuxc_b;

void setup() {
  port_iomuxc_b = &IMXRT_IOMUXC_b;  //0x401F8400

  // #19 = SCL, GPIO_AD_B1_00
  port_iomuxc_b->offset0CC = 0x00000001; // DAISY = 1

  // #18 = SDA, GPIO_AD_B1_01
  port_iomuxc_b->offset0D0 = 0x00000001; // DAISY = 1
}

それぞれ、該当するレジスタの 0-bit の値を 1 にすることで設定できます。ちなみにこの値を 0 にすると SD_B1_xx の方が選択されます。
レジスタ指定の記述については前回説明した通りです。IOMUXC_LPI2C1_SCL_SELECT_INPUT = 0x00000001 と記述することもできます。

Multiplexer

上の記述で LPI2C1モジュールから端子までの接続を設定しましたが、実は、各端子には他にも色々なモジュールから同じように接続が設定されていたりします。例えば AC_B1_00 端子には、I2C の他にも USB, UART, PGIO, SDHC, 他のモジュールが使用する端子として設定できるようになっています。そのため、今度は端子側で、どのモジュールと接続するかを設定する必要があります。

IMXRT_REGISTER32_t *port_iomuxc; // added
IMXRT_REGISTER32_t *port_iomuxc_b;

void setup() {
  port_iomuxc = &IMXRT_IOMUXC;  //0x401F8000 // added
  port_iomuxc_b = &IMXRT_IOMUXC_b;  //0x401F8400

  // #19 = SCL, GPIO_AD_B1_00
  port_iomuxc->offset0FC = 0x00000013; // SION = 1, MUX_MODE = 011(LPI2C1_SCL) // added
  port_iomuxc_b->offset0CC = 0x00000001; // DAISY = 1

  // #18 = SDA, GPIO_AD_B1_01
  port_iomuxc->offset100 = 0x00000013; // SION = 1, MUX_MODE = 011(LPI2C1_SDA) // added
  port_iomuxc_b->offset0D0 = 0x00000001; // DAISY = 1
}

"added" が端子側の設定のために追加したコードです。2-0 bit の値 (MUX_MODE) でどのモジュールと接続するかを選択します。どちらも 011b で LPI2C1_SCL/SDA を選択することになります。さらに 4-bit の値 (SION) を 1 にすることでこの選択が有効になります。

アナログ設定

最後に端子のアナログ設定を行います。端子の動作速度やオン抵抗なども設定できるのですが、I2C で使用する場合に重要なのは Open Drain に設定することと、内蔵 Pull-up の有無です。

IMXRT_REGISTER32_t *port_iomuxc;
IMXRT_REGISTER32_t *port_iomuxc_b;

void setup() {
  port_iomuxc = &IMXRT_IOMUXC;  //0x401F8000
  port_iomuxc_b = &IMXRT_IOMUXC_b;  //0x401F8400

  // #19 = SCL, GPIO_AD_B1_00
  port_iomuxc->offset0FC = 0x00000013; // SION = 1, MUX_MODE = 011(LPI2C1_SCL)
  port_iomuxc->offset2EC = 0x0000E861; // Pull-up = none, PUS = 11 (22kohm), PUE = 1, PKE = 0, ODE = 1, SPEED = 01, DSE = 100, SRE = 1 // added
  port_iomuxc_b->offset0CC = 0x00000001; // DAISY = 1

  // #18 = SDA, GPIO_AD_B1_01
  port_iomuxc->offset100 = 0x00000013; // SION = 1, MUX_MODE = 011(LPI2C1_SDA)
  port_iomuxc->offset2F0 = 0x0000E861; // Pull-up = none // added
  port_iomuxc_b->offset0D0 = 0x00000001; // DAISY = 1
}

"added" がアナログ設定のために追加したコードです。11-bit の値 (ODE) を 1 にすると端子は Open Drain で動作します。 I2C で使用する場合は必須です。
内蔵 Pull-up の大きさは 15-14 bit の値 (PUS) で設定でき、ここでは 11 = 22 kohm に設定しています。ちなみに 01 = 47 kohm, 10 = 100 kohm となり、00 にすると 100 kohm の Pull-down になります。
内蔵 Pull-up を使用する場合は、さらに 13-bit の値 (PUE) を 1 に設定する必要があります。この bit は Pull-up/down と Keeper のどちらを使用するかを設定するもので、1 にすると PUS で設定した Pull-up/down を使用します。
そして 12-bit の値 (PKE) が Pull-up/down の Enable で、この値を 1 にして初めて 15-13 bit の設定が有効になります。上記コードではこの bit の値を 0 にしているため、実は内蔵 Pull-up が無効になっています。ただし、15-13 bit は設定してあるので、必要な場合は 12-bit の値を 1 にする (上記のコードの場合は書き込む値を 0x0000E861 から 0x0000F861 に変更する) だけで 22 kohm の内蔵 Pull-up が有効になります。
7-0 bit は動作速度他の設定となりますが、説明は省きます。

次は LPI2C1 モジュールの中の設定を行います。