yoshiyuki's blog

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

Arduino / I2C 関数をイチから作る (4)

ここからは関数の中身について説明します。

肝はレジスタの直叩きです。

Arduinoマイコンの使い方のひとつの形態であり、開発環境も含めてマイコンの機能を簡単に利用できるよう整えられた環境です。つまり、実際に各種機能を実現しているのはマイコンです。そして、マイコンを操作する方法というのがマイコンレジスタへの値の書き込みです。例えば Arduino IDE で使う digitalWrite という関数も、その中ではマイコンレジスタの読み書きで所望の機能を実現しています。

今回は Wireライブラリ、つまり、既存の関数に頼らずに I2C を制御したいので、マイコンレジスタに読み書きする関数を自分で作成します。このようにマイコンレジスタに直接アクセスする行為をレジスタ直叩きと呼びます。

続いて、マイコンレジスタがどんな感じか説明します。

実際のところはマイコンによって様々なのですが、例えば 32 Byte のレジスタを持つ場合、1ページ当たり 1 Byte のレジスタ x 32ページという形で管理されます。そして、例えば 5ページの 値が D0 - D7 の出力状態 (High or Low) を制御する、というようなイメージです。
この例では、ページ 0x0B に値として B1010_1000 = 0xA8 を書き込むと、対応する Bit に 1 が書き込まれたことで D7, 5, 3 が High, 0 が書き込まれたことで D6, 4, 2, 1, 0 が Low を出力します。
f:id:ysin1128:20200531161458p:plain

これらレジスタArduino IDE で操作する場合は、レジスタのページをページ番号の代わりに各ページに割り当てられた名前で指定することも可能です。
また、レジスタの各Bit にも名前が割り当てられており、_BV マクロを使うことで Bit の位置を意識せずに指定することができます。
では、また例として、さっきのページ 0x0B にこんな名前が割り当てられているとします。
f:id:ysin1128:20200531161542p:plain

ここで D7, D6 を High, 他を Low にしたい場合は次のように記述します。

PORTD = _BV(PORTD7) | _BV(PORTD6);

PORTD = _BV(PORTD7) は PORTD という名前のページに対して PORTD7 に対応した Bit だけが 1、それ以外が 0 の 8 bit の値 B1000_0000 を書き込むことを意味します。同様に PORTD = _BV(PORTD6) は B0100_0000 を、PORTD = _BV(PORTD7)|_BV(PORTD6) は B1100_0000 を PORTD に書き込むことを意味します。

応用として、例えば D7, D6 を High にして他の状態を変更したくない場合は次のように記述します。

PORTD = PORTD & ~_BV(PORTD7) & ~_BV(PORTD6);

PORTD = ~_BV(PORTD7) = ~(B1000_0000) = B0111_1111 となり、PORTD = ~_BV(PORTD7) & ~_BV(PORTD6) = B0011_1111 となります。これと元の PORTD との & を取ると Bit 7, 6 は元の PORTD の状態に関わらず 0 になり B5 以下は元の PORTD の状態を維持します。

以上の要領でレジスタの直叩きをやっていきます。