yoshiyuki's blog

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

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

作った I2C関数の使い方です。
実際に呼び出して使うのは以下の 2個です。

// Master Write
byte i2c_write(byte slave_adr, byte *data, int data_length)
// Master Read
byte i2c_read(byte slave_adr, byte *reg_adr, int reg_adr_length, byte *read_data, int read_data_length)

タイムアウトの設定

const int CONST_TIMEOUT = 100;  //Timeout for I2C functions [ms]

I2C 制御処理のタイムアウト時間を設定します。(単位: ms)
この時間を超えて処理が完了しない場合、実行中の処理を中断して SCL/SDA を開放します。

SCL/SDA 内蔵 Pull-up の設定

  pinMode(SDA, INPUT);  //disable internal pull-up
  pinMode(SCL, INPUT);  //disable internal pull-up
  //pinMode(SDA, INPUT_PULLUP);  //enable internal pull-up
  //pinMode(SCL, INPUT_PULLUP);  //enable internal pull-up

pinMode の設定により SCL/SDA の内蔵 Pull-up を有効/無効にできます。
Pull-up 電圧は Arduino の電源電圧となります。

SCL 周波数の設定

  TWSR &= ~_BV(TWPS1)&~_BV(TWPS0);  //I2C SCL setting
  TWBR = 72;                        //SCL=100kHz @16MHz

SCL 周波数は TWBR の値から、下記の式で決定されます。
SCL 周波数を変更する場合は TWBR に代入する値を変更してください。

SCL周波数 = (Arduino のクロック周波数) / ( 16 + 2 * TWBR )

ちなみに、詳細は省きますが TWSR の値も SCL 周波数に影響し、今の設定から変更することで SCL周波数をより遅くすることが可能です。

Master Write

byte i2c_write(byte slave_adr, byte *data, int data_length)

slave_adr:
Byte型
書き込む相手の 7-bit Slave Address

*data:
Byte型配列のポインタ
書き込むデータ

data_length:
Int型
書き込むデータの長さ (単位: Byte)

戻り値:
Byte型
Master Write の実行結果が、次のいずれかで返ってきます。
SC_SUCCESS (= 0xFF): 書き込み成功
SC_NACK (= 0x00): NACK が返ってきた (書き込み失敗)
SC_TIMEOUT (=0x80): タイムアウト で処理が完了しなかった (書き込み失敗)


Slave Address は、データシートによっては 8-bit の値が記載されているものもありますが、ここでは 7-bit の値を使用してください。

I2C では Slave Address は「7-bit Slave Address + 1-bit R/W command」の合わせて 8-bit で取り扱われます。
例えば 7-bit Slave Address が 0x24 = B010_0100 の場合、実際に取り扱われるのは最下位 (8-bit目) に 1-bit コマンドを足した 0x48 = B0100_1000 (Write) または 0x49 = B0100_1001 (Read) となります。
逆に、データシートにSlave Address が 0x86 = B1000_0110 (Write) /0x87 = B1000_0111 (Read) のような形で表記されている場合、7-bit Slave Address は上位7-bit の 0x43 = B100_0011 となります。

書き込むデータはByte型配列で用意し、関数にはそのポインタを渡してください。
例えば、書き込むデータの入れ物として byteWRITE[4] を用意した場合、 byteWRITE がこの配列のポインタとなります。

data_length には、実際に書き込むデータの長さを指定してください。
書き込むデータを入れた Byte型配列の長さではない点に注意してください。
例えば、byteWRITE[4] という配列に対して実際に書き込むデータが byteWRITE[0] と byteWRITE[1] の 2-byte だけの場合、ここで指定する長さは 2 になります。 (5 ではありません)

エラー処理は作っていないので、例えば data_length に配列の大きさよりも大きな値を与えても無理矢理動作します。
ご注意ください。

使用例です。

byte byteSC;
byte byteADR = 0x24;
byte byteWRITE[] = {0x01, 0x08, 0x10};
int intWRITE = 3;

byteSC = i2c_write(byteADR, byteWRITE, intWRITE);

ACK を返す相手がいれば下図のような通信が行われます。

f:id:ysin1128:20200529184022p:plain
i2c_write_example

Master Read

byte i2c_read(byte slave_adr, byte *reg_adr, int reg_adr_length, byte *read_data, int read_data_length)

slave_adr:
Byte型
読み出す相手の 7-bit Slave Address

*reg_adr:
Byte型配列のポインタ
読み出す Register の Address

reg_adr_length:
Int型
reg_adr の長さ (単位: Byte)

*read_data
Byte型配列のポインタ
読み出したデータの格納先

read_data_length:
Int型
読み出すデータの長さ (単位: Byte)

戻り値:
Byte型
Master Write の実行結果が、次のいずれかで返ってきます。
0xFF = SC_SUCCESS: 読み出し成功。read_data には読み出したデータが格納されています。
0x00 = SC_NACK: NACK が返ってきた (読出し失敗)。 read_data の中身は更新されていません。
0x80 = SC_TIMEOUT: タイムアウト で処理が完了しなかった (読出し失敗)。 read_data の中身は更新されていません。


7-bit Slave Address, 配列のポインタについては Master Write の項目をご参照ください。

読み出す Register Address の指定が必要無い場合も *reg_adr には何でも良いので配列を与え、 reg_adr_length に 0 を与えてください。reg_adr_length = 0 であれば *reg_adr で与えた配列にはアクセスしません。
read_data の配列長は、必ず read_data_length で与える値よりも長くしてください。エラー処理を作っていないため、read_data_length の方が長い場合、読み出したデータは read_data で与えた配列を超えて、別の変数に割り当てられているかもしれないメモリにまで書き込まれます。

使用例です。

byte byteSC;
byte byteADR = 0x24;
byte byteREG[1] = {0x00};
int intREG = 1;
byte byteREAD[4];
int intREAD = 4

byteSC = i2c_read(byteADR, byteREG, intREG, byteREAD, intREAD);

ACK を返す相手がいれば下図のような通信が行われ、成功すれば byteREAD に読み出した値が格納されます。
f:id:ysin1128:20200612175219p:plain