yoshiyuki's blog

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

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

前回で端子設定まで完了したので、いよいよ LPI2C1モジュールの中身を設定していきます。
繰り返しますが、I2C通信をやりたいのであれば素直に Wireライブラリを使う方が簡単、確実です。

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

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

Clock の設定

LPI2C1 モジュールの前に Clock の設定を忘れていました。以前の記事でも紹介しましたが、Clock が入ってこないと LPI2C1 モジュールのレジスタ値を変更することすらできません。
LPI2C の Clock を制御するレジスタは CCM_CSCDR2 と CCM_CCGR2 の二つです。

CCM_CSCDR2 は LPI2C用 Clock の元となる Clock の選択と、その Clock の分周比を設定します。ここではそれらを以下のコードで設定します。

  CCM_CSCDR2 = (CCM_CSCDR2 & 0xFE03FFFF) | 0x00040000;

CCM_CSCDR2 には LPI2C の Clock 以外の設定も含まれており、それらの設定を触りたくないので、CCM_CSCDR2 の値そのもののうち LPI2C の Clock の設定に割り当てられた 24-18 bit の値を 0 にしてから、そこに必要な値を書き込むという式にしています。
18-bit 目は元となる Clock の選択です。IMXRT1062 は複数の Clock を持っていて、各モジュールはそれら複数の Clock から自分の Clock の元となるものを選択できるようになっています。LPI2C の場合は OSC (24 MHz)、または、PLL3 を 8分周したもの (60 MHz) のどちらかをここで選択できます。I2C の周波数は 400 kHz までなので Clock周波数も低い方で十分だろうということで、ここでは OSC を選択するために値を 1 にしています。
24-19 bit では、先に選択した OSC = 24 MHz を何分周するかを設定します。例えば 2分周に設定すると、LPI2Cの Clock は 24 MHz / 2 = 12 MHz となります。ここでは値を 0 、つまり、分周無しとしていますので、LPI2C の Clock はそのまま 24 MHz となります。

CCM_CCGR2 は以前の記事で紹介した通り、Clock を LPI2C モジュールに送るかどうかを決定する Clock Gating の設定です。以下のコードで、今回、使用する LPI2C モジュールへの Clock Gating に対応する 7-6 bit の値に 11 を書込み、Clock を常に ON に設定します。ちなみに、値の書き込みの際には他の bit の値を変更しないよう、元の値に対して OR で書き込んでいます。

  CCM_CCGR2 |= 0x000000C0; // CG3 = 11, enable LPI2C1 clock

LPI2C1 の設定

今度こそ LPI2C1 モジュールの中に進みます。レジスタマップから LPI2C1 モジュールの設定に必要そうなレジスタを順に確認していきます。

MCR (Master Control Register)

0-bit が Master Enable (MEN) となっていて、この値を 1 にすると I2C Master として動作するようになります。

IMXRT_LPI2C_t *port_i2c;

void setup() {
  port_i2c = &IMXRT_LPI2C1; //0x403F0000

  port_i2c->MCR = 0x00000001;
}

MCFGR1 (Master Configuration Register 1)

2-0 bit で LPI2C Master Logic を動作させる Clock の分周比を設定します。LPI2C の Clock はすでに CCM_x レジスタで設定していますが、ここではそこからさらに分周する場合の設定となります。ここでは 001 (2分周) に設定したので、LPI2C の Clock は 24 MHz / 2 = 12 MHz となります。

IMXRT_LPI2C_t *port_i2c;

void setup() {
  port_i2c = &IMXRT_LPI2C1; //0x403F0000

  port_i2c->MCFGR1 = 0x00000001;
}

MCFGR2 (Master Configuration Register 2)

27-24 bit, 19-16 bit がそれぞれ SDA, SCL の Glitch Filster の設定となっていて、これらの値 x LPI2C Clock (MCFGR1 の分周前) より短い SDA, SCL のパルスは Glitch としてフィルタされます。つまり、無視されます。ここでフィルタ時間を決定する際の Clock は MCFGR1 で設定した分周を行う前の Clock であることに注意してください。下記のコードでは SDA/SCL Glitch Filter の値をどちらも 5 にしていますが、時間としては 5 x (24 MHz) = 0.2 us となります。
11-0 bit は Bus Idle Timeout の設定で、値 x LPI2C Clock の時間だけ SDA/SCL = High が維持されると IMXRT1062 は現在、誰も I2C 通信をしていないから自分が通信を開始しても良いと判断します。下記のコードでは値を 0xBB8 = 3000 としており、時間としては 3000 x (12 MHz) = 250 us となります。ややこしいですが、ここでは Clock は MCFGR1 で設定した分周の後のものを使用します。

IMXRT_LPI2C_t *port_i2c;

void setup() {
  port_i2c = &IMXRT_LPI2C1; //0x403F0000

  port_i2c->MCFGR2 = 0x05050BB8; // FILTSDA = 5 (0.2 us), FILTSCL = 5 (0.2 us), BUSIDLE = 3000 (250 us)
}

MCCR0 (Master Clock Configuration Register 0)

このレジスタでは SCL のパルス幅や速さ他、I2C の通信速度に関わる設定を行います。通信速度なんて 100 kHz とか 400 kHz とかで設定したいところですが IMXRT1062 の場合、それでは許してくれません。
29-24 bit は SDA の Hold time, 21-16 bit は Start/Stop Condition の Setup/Hold time, 13-8 bit は SCL の High幅, 5-0 bit は SCL の Low幅の設定で、それぞれ (値 + 1) x CLK (12 MHz) の時間に設定されます。下記コードは 100 kHz 用の設定ですが、白状すると、面倒だったので Wire ライブラリの値を丸写しです。

IMXRT_LPI2C_t *port_i2c;

void setup() {
  port_i2c = &IMXRT_LPI2C1; //0x403F0000

  port_i2c->MCCR0 = 0x1928373B; // DATAVD = 25 (2.2 us), SETHOLD = 40 (3.4 us), CLKHI = 55 (4.7 us), CLKLO = 59 (5 us)
}

以上で設定が完了したので、次は実際に通信を行います。