LPC1343 I2C EEPROM通信 write

出来た事

1.I2Cを使ったEEPROMのWrite

(LPC1343がマスタ : エラー処理なしの単純処理)
開発Listへ戻る

I2Cにチャレンジです!

Philipsが開発した、世の中で長年使われている通信仕様 I2C
代表的な使用例はEEPROMですね。

それ以外でいうと、温度センサーやADコンバータなどの
インターフェースでも使われています。


手始めにEEPROMの 1byte write/readをやろうと思います。今回はWriteです。

EEPROMは容量によってコントロール方法が少し違います。
具体的に言うと 256byte-16k byteと32k-512k byteに分かれます。

アドレスを指定して書き込み・読み出しを行う際に
16kまでは8bitでアドレス指定が出来るのに対し、32k-512kのアドレス指定はそれ以上必要になります。
I2Cは8bit単位でデータを通信するのでちょっと変わってきます。

以下はMicrochipの仕様書から(DS21930A_JP - Page 16引用)
16kまではAddress byteが1ですが、32-512kはLow/High 2つのAddress byteが必要です。


このエントリでは4k byteのEEPROMを使います。(上の図の6.1です)

(秋月で買うEEPROMは60円とかですが64k byte以上のようです。(図6.2)

接続図!
プルアップ抵抗を忘れずに!

I2C通信ではICがHigh,Lowを出力すのではなく、
ICはLowを出力 or High-Z状態を作ります。
全てのICがHigh-Z状態であればプルアップ抵抗があるためにHighになります。
このためIC同士のコンフリクトがありません。賢いですね。
(あるICがHighを出力し、あるICがLowを出力するとショートする
この状態をコンフリクトといいってます)



I2C write動作解説

先ほどの図6.1まさにこいつをそのまま行います。
(DS21930A_JP - Page 16引用)

I2CはSDA,SCLの2本の線で複数のICをコントロールすることが出来ます。
したがって、複数のICに対した想定が必要です。
ちなみにコントロールする側(今回の場合のLPC1343)をマスタ
コントロールされる側をスレーブといいます。

Start:
マスタからの信号です。
今からスタートするぞー準備しろという指令がスタート信号です。

Control byte :
これはマスタが点呼を取っているようなイメージです。
今から○○ICをコントロールするけど居ます??みたいな感じで訊いています。

上の図の場合は1010というデバイス=EEPROMで***というアドレスのICに
Writeしたいんだけど居る??と訊いているのです。

Control byteの7:4bitはデバイスを示していてEEPROMの場合は1010です。
バイスごとに違うので他のデバイスを扱う場合はDatasheetで確認する必要があります。

3:1bitはたとえばEEPROMを2つ以上コントロールする場合にどのICか指定する場合に使います。
つまり、ICのユーザ側の設定です。
今回は回路図の接続を見てもわかるようにA2-A0をGNDしていますので "000"です。

0 bitはWrite or Readです 0がWrite 1がReadです。

ACK:
ICのお返事です。アクノリッジ、アックといいます。

1010というデバイス=EEPROMで***というアドレスのICに
Writeしたいんだけど居る?? と訊かれて

ハイハイ私です!と返事をするという事です。したがって、この場合はEEPROMからの信号です。
(ReadのときなどマスタからACKを返さなければならないこともあります)
ACKは0です。

Address byte:
マスタ側からの信号で、
じゃあ△△アドレスに書き込みたいんだけど良い?と訊いています。

その次のACKはEEPROMから返事の信号で
ACKがある=0ということは良いよ!! といっています。

Data byte:      
マスタからの信号です。
このデータ書き込んで!という指令です。

その次のACKはEEPROMから返事の信号で
ACKがある=0ということは書き込んだよ!!ということです。

Stop:
終了!!

です。

LPC1343でのI2C通信(Write)
ユーザマニュアルUM10375 Rev. 3 ― 14 June 2011 
ページ 228 of 368が動作の該当ページです。

赤の部分だけやります。

青矢印の部分はSLA(スレーブアドレス)= 上の図でいうControl byteを送信したのに
ACKが返ってこなかった場合です。

製品設計などになると、そのような意図しないところにも対応しなければならないでしょうが
今回は面倒なので割愛。アクシデントは考えません。

赤枠部分の説明

Sおよび下の丸の中の08H:
Sがスタート信号送信で○はステータスレジスタの値です。
ステータスが08Hになります。

08を確認した後でSLA+Write = 1010 000 0
(1010=EEPROM 000=回路上のA0-A2の設定(今回接続は3つともGNDなので000) 0=Write)
を送信します。

ACKを確認するとステータスレジスタは18Hになります。

18Hを確認すると今度は図6-1でいうAddress byteを送信です。
0Hだとちゃんと制御できているかあいまいなので今回は12Hにしてみましょう。

ACKを確認するとステータスレジスタは28Hになります。

28Hを確認すると今度は書き込むデータを送信します。
今回は適当に19Hにします。

ACKを確認するとまたステータスレジスタは28Hになります。

2回目の28Hを確認したら終了のSTOP信号を入力します。

code

#ifdef __USE_CMSIS
#include "LPC13xx.h"
#endif

#include
#include

__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;

int main(void) {
LPC_IOCON->PIO0_4 = 0x01; // PIO0_4 as SCL
LPC_IOCON->PIO0_5 = 0x01; // PIO0_5 as SDA
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<5);// Enable for I2C clock
LPC_SYSCON->PRESETCTRL |= 0x02; // De-asset I2C reset

LPC_I2C->SCLL = 350; // SCL Low
LPC_I2C->SCLH = 350; // SCL High
// Duty=50% about 100kHz@70MHz clock

NVIC_EnableIRQ(I2C_IRQn); // Enable I2C interrupt

LPC_I2C->CONCLR = 0x6c; // I2C flagClear
LPC_I2C->CONSET = 0x40; // I2C enable

LPC_I2C->CONSET = 0x20; // assert Start
// Enter an infinite loop, just incrementing a counter
volatile static int i = 0 ;
while(1) {i++ ;}
return 0 ;
}

void I2C_IRQHandler(void)
{
uint8_t StatValue;
int adrs=0x13;
int dat =0x2C;
static int t=1;

StatValue= LPC_I2C->STAT;
switch ( StatValue )
{
case 0x08: //Now Status is Start
LPC_I2C->DAT =0xa0; //Device address 1010 000 + Write
LPC_I2C->CONCLR = 0x28; //clear interrupt flag + set next action
break;
case 0x18: //Now Status is SLA+W and Receive ACK
LPC_I2C->DAT =adrs; //Address
LPC_I2C->CONCLR = 0x08; //clear interrupt flag
break;
case 0x28: //Now Status is sending Data byte and Receive ACK
if(t==1){ // 1st : Data byte is address
LPC_I2C->DAT = dat; // write data
LPC_I2C->CONCLR = 0x08;
}else{ // 2nd : Data byte is write data
LPC_I2C->CONSET = 0x10; //send stop command
LPC_I2C->CONCLR = 0x08; //Clear interrupt
while(LPC_I2C->CONSET & 0x00000010); //waiting STOP flag
LPC_I2C->CONCLR = 0x48; //Clear interrupt & Disable I2c }
++t;
break;
default:
break;
}
}

解説です。

まずはIOPINの設定とI2Cへのクロック供給、リセット解除です。
ユーザマニュアルUM10375 Rev. 3 ― 14 June 2011 
ページ 207 of 368 の13.2 Basic configurationをやっているだけです。

LPC_IOCON->PIO0_4 = 0x01; // PIO0_4 as SCL
LPC_IOCON->PIO0_5 = 0x01; // PIO0_5 as SDA
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<5);// Enable for I2C clock
LPC_SYSCON->PRESETCTRL |= 0x02; // De-asset I2C reset

次はクロック周波数,DUTYの設定です。
ユーザマニュアル ページ 213 of 368
13.8.5.1 Selecting the appropriate I2C data rate and duty cycleのTable223をみて
100kHz程度になるようにしています。
(LCP1343のSYSTEM clockは72MHzです)

LPC_I2C->SCLL = 350; // SCL Low
LPC_I2C->SCLH = 350; // SCL High
// Duty=50% about 100kHz@70MHz clock

そしてI2Cの割り込みを有効にします。
ステータスが変わると割り込みが入ります。

NVIC_EnableIRQ(I2C_IRQn); // Enable I2C interrupt


一応フラッグを全てクリアーにしてからI2Cを有効にします。

LPC_I2C->CONCLR = 0x6c; // I2C flagClear
LPC_I2C->CONSET = 0x40; // I2C enable


そしてI2C動作スタート!
スタートコマンド送信!

LPC_I2C->CONSET = 0x20; // assert Start

スタートコマンドを送信するとステータスが0x08=スタートコマンドを送信したよ!
になります。そして割り込みが入るわけです。

switch文でStatus毎に動作を割り振っていますが、上から順に実行されるようにしてます。
スタートコマンドを送信した=0x08割り込みが入った後は
バイスアドレスとWriteコマンドを送信しなければなりませんので
LPC_I2C->Datに 0xa0を入れておきます。
その後でスタートフラグと割り込みフラグを解除します。
割り込みを解除すると次の動作に行きます。
バイスアドレス+write=SLA+Writeを送信します。

StatValue= LPC_I2C->STAT;
switch ( StatValue )
{
case 0x08: //Now Status is Start
LPC_I2C->DAT =0xa0; //Device address 1010 000 + Write
LPC_I2C->CONCLR = 0x28; //clear interrupt flag + set next action
break;

SLA+WriteをEEPROMが受けてACKを出したことをLPC1343が検出するとステータスが0x18になります。
そこで割り込みがまた発生します。
次は書き込むアドレスを送信します。
アドレスは int adrs=0x13; で指定しました。(意味なし気分で決めた値)
DATレジスタにアドレスを入れて、割り込みフラグを解除でアドレスデータを送信します。

case 0x18: //Now Status is SLA+W and Receive ACK
LPC_I2C->DAT =adrs; //Address
LPC_I2C->CONCLR = 0x08; //clear interrupt flag
break;

アドレスデータをEEPROMが受けてACKを出したことをLPC1343が検出するとステータスが0x28になります。
(Data byte in I2DAT has been transmitted ACK has been received.)
次の書き込みデータを送信する場合もステータスは0x28なので
カウンタで最初の0x28と次の0x28を区別できるようにしました。
最初の0x28で発生した割り込みでは
次にwriteデータを送信します。
writeデータは int dat =0x2C; で指定しました。(意味なし気分で決めた値)
DATレジスタにデータを入れて、割り込みフラグを解除でwriteデータを送信です。

case 0x28: //Now Status is sending Data byte and Receive ACK
if(t==1){ // 1st : Data byte is address
LPC_I2C->DAT = dat; // write data
LPC_I2C->CONCLR = 0x08;

最後。
writeデータをEEPROMが受けてACKを出したことをLPC1343が検出するとステータスがまた0x28になります。
2回目の0x28です。
これで終わりなのでStopコマンドを送信して
ストップフラグが立つのをまって
I2Cをdisableします。この部分はNXPのExample codeからもってきました。
(2012.3.4修正)


}else{ // 2nd : Data byte is write data
LPC_I2C->CONSET = 0x10; //send stop command
LPC_I2C->CONCLR = 0x08; //Clear interrupt
while( LPC_I2C->CONSET & 0x00000010); //waiting STOP flag
LPC_I2C->CONCLR = 0x48; //Clear interrupt & Disable I2c }


ちなみに次の送信データセットと割り込みフラグ解除の書く順番ですが
データセット→フラグ解除にすべきです。
逆にしても通常で流すと結果は同じになりました。
しかし、デバッグモードで1Step毎に進むと、逆に書いた場合は結果が期待値と異なります。

期待値が出る書き順
LPC_I2C->DAT =0xa0; //Device address 1010 000 + Write
LPC_I2C->CONCLR = 0x28; //clear interrupt flag + set next action

デバッグモードでは期待値が出ない書き順
LPC_I2C->CONCLR = 0x28; //clear interrupt flag + set next action
LPC_I2C->DAT =0xa0; //Device address 1010 000 + Write