I2CシリアルEEPROM 24LC64

投稿日 2014/10/08

microchip社のI2CシリアルEEPROM 24L64 8Kbyte(64Kbit)をPICにつないでみました。

 

EEPROMは書き換え可能な不揮発性メモリで、I2CシリアルEEPROMは、I2Cで制御でき取扱いが簡単です。

 

PICのMCUにもEEPROMは内蔵されているものがありますが、256byteや1024byteなど、容量が限られています。このため電源オン時の前回設定の復帰など、用途は限られます。

18f4553_eeprom_6.jpg

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を確認して終了です。最後のストップコンディションの前に複数のデータを流せば連続で書き込めます。(ページモード)

18f4553_eeprom_3.jpg

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にしてメモリアドレスを指定を省略すると、現在のメモリアドレス(最後に指定したメモリアドレスの次)から読み出しが行えます。

18f4553_eeprom_5.jpg

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番目のフレームは右に隠れている(下図参照)

18f4553_eeprom_4.jpg

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)