24LC64 DIP8
microchip社 24LCファミリEEPROMデータシートより抜粋
SDカードのような記憶媒体のようにはいきませんが、外付けのEEPROMを使用すれば、簡単なデータのロギングなどの用途が広がります。
整数型(例えば12bitや16bit)のデータ(つまり2byteのデータ)を、10分間隔でロギングするとして、8Kbyte(8192byte) / 2 = 4096回分 4096 x 10 = 40,960分 = 682時間 = 28日。容量をすべてデータに使うとすれば、28日分のデータが記録できます。これは容量8Kbyteの24LC64の場合の話で、1Mbitの24LC1024では444日分のデータが記録できます。
ピン配置と接続(DIP/SOIC/TSSOP/MSOPの場合)
Pin1 A0 Vssにつなぐと0 Vccにつなぐと1
Pin2 A1 Vssにつなぐと0 Vccにつなぐと1
Pin3 A2 Vssにつなぐと0 Vccにつなぐと1
Pin4 Vss GND
Pin5 SDA プルアップ要
Pin6 SCL プルアップ要
Pin7 WP Vssにつなぐと書き込み可 Vccにつなぐと書き込み不可
Pin8 Vcc 2.5 - 5.5V
I2Cのスレーブアドレスは、1 0 1 0 A2 A1 A0 です。
A2, A1, A0は上記のように接続によって設定できます。000から111まで8種類のアドレスが選べます。今回はA2, A1はGND、A0をVccにつないで、スレーブアドレスを0xA2としました。(I2Cの場合アドレスは7bitなのでこのようになります。0xA1ではありません)。実際にはLSBにR/W指定ビットが1ビット加わって8bitになるので、WRITE時0xA2、READ時0xA3となります。
WPは書き込みができるようにVss(GND)につないでおきます。
書き込み/読み出し
EEPROMはメモリなのでアドレスを指定して読み書きします。
24LC64は8192byteなので 0から8191までのアドレスを持っています。これには2byte必要です。
メモリアドレスに続けて1byteのデータを送ります。(複数バイトを連続で送ることも可能)
I2Cのデータフォーマットと書き込み、読み出しの概略手順は以下のようになります。(詳細はデータシートを参照してください)
SCL 9クロックが1フレームです。
書き込み(WRITE)時 4フレームから成る
SCL 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
SDA S 1 0 1 0 0 0 1 W As Address HIGH As Address LOW As Data As P
読み出し(READ)時 5フレームから成る
SCL 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 +
SDA S 1 0 1 0 0 0 1 W As Address HIGH As Address LOW As +
SCL 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
SDA S 1 0 1 0 0 0 1 R As Data Nm P
S:スタートコンディション
1010001: EEPROMのスレーブアドレス(0xA2)
W:Write (0)
R:READ(1)
As: スレーブからのアクノーリッジ(0)
Nm:マスターからのノーアクノリッジ(1)
Address HIGH: メモリアドレスの上位バイト
Addres LOW: メモリアドレスの下位バイト
Data: 書き込む/読みだしたデータ
P: ストップコンディション
()内はSDAの論理(正論理)
書き込みの手順
まずマスター(PIC)がスタートコンディションを発行し、続けてスレーブアドレス(0xA2)を流します。クロック(SCL)の8番目でWRITEを指示します。
I2Cバスにスレーブアドレス0xA2のデバイスがつながれていれば(今回はEEPROM)、9番目のクロックでスレーブがSDAをLOWに引っ張りアクノーリッジ(Ack)を送ります。マスターはAckが来たことを確認して、Address HIGHを送り、スレーブからのAckを確認します。Address LOWも同じです。最後に8bitのデータを送って、最後のスレーブからのAckを確認して終了です。最後のストップコンディションの前に複数のデータを流せば連続で書き込めます。(ページモード)
I2C 書き込みタイミング
CH1: SCL, CH2: SDA, CH3:トリガ(T)用の信号
クロック(SCL)の周期は10uS(f = 100KHz)
トリガがかかった直後がスタートコンディション
SCLは1フレーム 9クロック 計4フレーム
最初のフレームはスレーブアドレス(0xA2)+W(0)+Ack(0)
2番目のフレームは、Address HIGH+Ack(0)
3番目のフレームはAddress LOW+Ack(0)
4番目のフレームは書き込むデータ(0xAA)+Ack(0)
トリガのフォールエッジの直前がストップコンディション
読み出しの手順
Address LOWを送るまでは書き込みと同じです。
Address LOWに続き再びスタートコンディションを発行し、再度スレーブアドレスを送ります。このとき8ビット目をR(READ)にして読み込みを指示します。これにスレーブからのAckが返ったら、データが送られてくるので取り込んで、マスターからノーアックノリッジを送ります(Ackを送らない)。ノーアックノリッジはこれで読み込み終了を意味します。このときAckを送ると次のメモリアドレスから連続して読み込みが行えます。(ページモード) 最初のスレーブアドレスで8ビット目をRにしてメモリアドレスを指定を省略すると、現在のメモリアドレス(最後に指定したメモリアドレスの次)から読み出しが行えます。
I2C 読み出しタイミング(1-4フレーム)
Ch1: SCL, Ch2: SDA, Ch3:トリガ(T)用の信号
クロック(SCL)の周期は10uS(f = 100KHz)
トリガ信号のライズエッジでトリガ
トリガがかかった直後がスタートコンディション
SCLは1フレーム 9クロック 計5フレーム
最初のフレームはスレーブアドレス(0xA2)+W(0)+Ack(0)
2番目のフレームは、Address HIGH+Ack(0)
3番目のフレームはAddress LOW+Ack(0)
4番目のフレームの直前に2回目のスタートコンディション
4番目のフレームは再度スレーブアドレス(0xA2+R(1)+Ack(0)
5番目のフレームは右に隠れている(下図参照)
I2C 読み出しタイミング(2-5フレーム)
CH1: SCL, CH2: SDA, CH3:トリガ(T)用の信号
トリガ信号のフォールエッジでトリガ
5番目のフレーム(右端)は読みだされたデータ(0xAA)+Nack(1)
最期がNackだとEEPROMは終了と判断
最も右端にストップコンディション
テストパターンを書いてみました
すべてのアドレス(0 - 8191番地)に0xAAを書き込み、その後読み出すという単純なものです。上記波形を観測したプログラムです。
ソースコード
PIC 18F4553
MPLAB X IDE, XC8
main.c
#include <xc.h>
#include <p18f4553.h>
#include <stdio.h>
#include <math.h>
#pragma config CPUDIV = OSC2_PLL3 //[Primary Oscillator Src: /2][96 MHz PLL Src: /3]
#pragma config PLLDIV = 5 //Divide by 5 (20 MHz oscillator input)
#pragma config USBDIV = 2 //USB clock source comes from the 96 MHz PLL divided by 2
#pragma config FCMEN = OFF //Fail-Safe Clock Monitor disabled
#pragma config IESO = OFF //Oscillator Switchover mode disabled
#pragma config FOSC = HS //HS oscillator (HS)
#pragma config PWRT = OFF //PWRT disabled
#pragma config VREGEN = OFF //USB voltage regulator disabled
#pragma config BORV = 3 //Minimum setting
#pragma config BOR = ON //Brown-out Reset enabled in hardware only (SBOREN is disabled)
#pragma config WDTPS = 32768 //1:32768
#pragma config WDT = OFF //WDT disabled (control is placed on the SWDTEN bit)
#pragma config CCP2MX = OFF //CCP2 input/output is multiplexed with RB3
#pragma config PBADEN = OFF //PORTB<4:0> pins are configured as digital I/O on Reset
#pragma config MCLRE = ON //MCLR pin enabled; RE3 input pin disabled
#pragma config LPT1OSC = OFF //Timer1 configured for higher power operation
#pragma config STVREN = ON //Stack full/underflow will cause Reset
#pragma config DEBUG = OFF //Background debugger disabled, RB6 and RB7 configured as general purpose I/O pins
#pragma config ICPRT = OFF //ICPORT disabled
#pragma config LVP = OFF //Single-Supply ICSP disabled
#pragma config XINST = OFF //Instruction set extension and Indexed Addressing mode disabled (Legacy mode)
#pragma config CP0 = OFF //Block 0 (000800-001FFFh) is not code-protected
#pragma config CP1 = OFF //Block 1 (002000-003FFFh) is not code-protected
#pragma config CP2 = OFF //Block 2 (004000-005FFFh) is not code-protected
#pragma config CP3 = OFF //Block 3 (006000-007FFFh) is not code-protected
#pragma config CPB = OFF //Boot block (000000-0007FFh) is not code-protected
#pragma config CPD = OFF //Data EEPROM is not code-protected
#pragma config WRT0 = OFF //Block 0 (000800-001FFFh) is not write-protected
#pragma config WRT1 = OFF //Block 1 (002000-003FFFh) is not write-protected
#pragma config WRT2 = OFF //Block 2 (004000-005FFFh) is not write-protected
#pragma config WRT3 = OFF //Block 3 (006000-007FFFh) is not write-protected
#pragma config WRTB = OFF //Boot block (000000-0007FFh) is not write-protected
#pragma config WRTC = OFF //Configuration registers (300000-3000FFh) are not write-protected
#pragma config WRTD = OFF //Data EEPROM is not write-protected
#pragma config EBTR0 = OFF //Block 0 (000800-001FFFh) is not protected from table reads executed in other blocks
#pragma config EBTR1 = OFF //Block 1 (002000-003FFFh) is not protected from table reads executed in other blocks
#pragma config EBTR2 = OFF //Block 2 (004000-005FFFh) is not protected from table reads executed in other blocks
#pragma config EBTR3 = OFF //Block 3 (006000-007FFFh) is not protected from table reads executed in other blocks
#pragma config EBTRB = OFF //Boot block (000000-0007FFh) is not protected from table reads executed in other blocks
#define LED PORTEbits.RE2 //Sens Time
void i2c_init(void);
void i2c_enable(void);
void lcd_init(void);
void lcd_clear(void);
void lcd_write(unsigned char, unsigned char, const unsigned char*);
void EEPROM_24lc64_write(unsigned int, unsigned char);
unsigned char EEPROM_24lc64_read(unsigned int);
#define _XTAL_FREQ 10000000 //__delay_ms
void delay_us(int time){
int i;
for(i = 0; i < time; i++) __delay_us(1);
}
void delay_ms(int time){
int i;
for(i = 0; i < time; i++) __delay_ms(1);
}
void init(void){
OSCCONbits.SCS = 0; //Primary OSC 10MHz
ADCON1 = 0b00001111; // All Digital
CMCON = 0b00000111; // No Comparator
TRISA = 0b00000000;
TRISB = 0b00000000;
TRISC = 0b00000000;
TRISD = 0b00000000;
TRISE = 0b00000011;
LATA = 0b00000000;
LATB = 0b00000000;
LATC = 0b00000000;
LATD = 0b00000000;
LATE = 0b00000000;
}
void main(void){
unsigned int eeprom_address;
unsigned char get_val;
char string[10];
init();
i2c_init();
i2c_enable();
lcd_init();
lcd_clear();
while(1){
LED = 1;
for(int i = 0; i < 8191; i++){
sprintf(string, "ADR:%4d", i);
lcd_write(0, 0, string);
eeprom_address = i;
EEPROM_24lc64_write(eeprom_address, 0xAA);
lcd_write(0, 1, "WDT:0xAA");
delay_ms(10);
}
for(int i = 0; i < 8191; i++){
sprintf(string, "ADR:%4d", i);
lcd_write(0, 0, string);
eeprom_address = i;
get_val = EEPROM_24lc64_read(eeprom_address);
sprintf(string, "RDT:0x%2X", get_val);
lcd_write(0, 1, string);
delay_ms(10);
}
lcd_clear();
LED = 0;
delay_ms(3000);
} //while
} //main
i2c_24lc64.c
#include <xc.h>
#include <p18f4553.h>
#define EEPROM_24lc64_I2C_ADDR 0xA2
#define WRITE_MODE 0x00
#define READ_MODE 0x01
#define PROVE PORTCbits.RC0 //Prove
void delay_us(int);
void delay_ms(int);
void EEPROM_24lc64_write(unsigned int, unsigned char);
unsigned char EEPROM_24lc64_read(unsigned int);
void EEPROM_24lc64_write(unsigned int address, unsigned char data){
PROVE = 1;
SSPCON2bits.SEN = 1;
while(SSPCON2bits.SEN == 1);
SSPBUF = EEPROM_24lc64_I2C_ADDR | WRITE_MODE;
while(SSPSTATbits.BF);
delay_us(1);
if (SSPCON2bits.ACKSTAT){
SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN);
return;
}
SSPBUF = address >> 8;
while(SSPSTATbits.BF);
delay_us(1);
if (SSPCON2bits.ACKSTAT){
SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN);
return;
}
SSPBUF = address & 0x00FF;
while(SSPSTATbits.BF);
delay_us(1);
if (SSPCON2bits.ACKSTAT){
SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN);
return;
}
SSPBUF = data;
while(SSPSTATbits.BF);
delay_us(1);
if (SSPCON2bits.ACKSTAT){
SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN);
return;
}
delay_us(1);
SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN);
PROVE = 0;
return;
}
unsigned char EEPROM_24lc64_read(unsigned int address){
unsigned char data;
PROVE = 1; //オシロスコープのトリガ用
SSPCON2bits.SEN = 1;
while(SSPCON2bits.SEN == 1);
SSPBUF = EEPROM_24lc64_I2C_ADDR | WRITE_MODE;
while(SSPSTATbits.BF);
delay_us(1);
if (SSPCON2bits.ACKSTAT){
SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN);
return;
}
PROVE = 0;
SSPBUF = address >> 8;
while(SSPSTATbits.BF);
delay_us(1);
if (SSPCON2bits.ACKSTAT){
SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN);
return;
}
SSPBUF = address & 0x00FF;
while(SSPSTATbits.BF);
delay_us(1);
if (SSPCON2bits.ACKSTAT){
SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN);
return;
}
SSPCON2bits.SEN = 1;
while(SSPCON2bits.SEN == 1);
SSPBUF = EEPROM_24lc64_I2C_ADDR | READ_MODE;
while(SSPSTATbits.BF);
delay_us(1);
if (SSPCON2bits.ACKSTAT){
SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN);
return;
}
delay_us(1);
SSPCON2bits.RCEN = 1;
while(SSPCON2bits.RCEN);
while(SSPSTATbits.BF == 0);
data = SSPBUF;
SSPCON2bits.ACKDT = 1; //Nack
SSPCON2bits.ACKEN = 1; //マスタからNackを送る
PROVE = 0;
delay_us(1);
SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN);
//PROVE = 0;
return(data);
}
(JF1VRR)