最終更新日: 2010年6月22日

3-wire EEPROMを使う

シリアル接続のEEPROMは大きく分けてI2C, SPI, そして3-wireとがあるようです。この中で結構入手が簡単なのがi2Cと3Wire。そこで3Wire方式のEEPROMをArduinoに接続してみました。使用したのはAtmelの93C86です。

ポイント

3-wireはSPIと良く似た規格ですが、大きな点として以下が異なってます。
・CS(SPIではSS)の極性。3WireではアクティブH。
・コマンドのビット数。SPIは8bit単位ですが、3-wireは半端。
・書き込み後のBusyステータス。3-wireはDO端子のレベルでチェック。
ArduinoのPlaygroundページにはSPIインターフェースのEEPROMを使った解説が出てますが3-wireは見当たりません。もちろんSPI用のプログラムをそのまま使うことはできませんが、若干の工夫で3-wire方式のEEPROMと接続できました。

接続

Arduinoとは、その名のとおり3本の信号線と、CS信号、そして電源を接続します。93C86はデータ長が8bitと16bitをORG端子で選択できますが、今回は8bit(ORG端子をLレベル)にしました。

pin 機能 Arduino端子
1 CS (Chip Select) Digital 10
2 SK (Serial Data Clock) Digital 13
3 DI (Serial Data Input) Digital 11
4 DO (Serial Data Output) Ditigal 12
5 GND GND
6 ORG GND(8bit mode)
7 DC (Don't Connect) -
8 Vcc 5V

 

まず初期設定

まずは使用する端子のI/O方向を設定します。さらにデータ入力端子は書き込み後のReady状態をチェックできるようにするためにプルアップしておきます(Hを書き込んでおく)。
SPCR(SPI Control Register)を設定する際、CPHAを1にセットする必要があります。これをしないと書き込んだデータを読み出す時にビットがずれます。
これで初期設定は完了。書き込みができるよう、EWENコマンドを送信しておきます。

cs_en(), cs_de()関数は、CS端子をH,Lにセットするだけです。

sketch
#define DATAOUT 11 //MOSI
#define DATAIN  12 //MISO 
#define SPICLOCK 13 //sck
#define SLAVESELECT 10 //ss

#define CMD_READ 0x02
#define CMD_WEN 0x00
#define CMD_WRITE 0x01

void setup()
{
  byte clr;
  
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(SLAVESELECT,OUTPUT);
  digitalWrite(SLAVESELECT,LOW); //disable device 
  digitalWrite(DATAIN,HIGH);

  SPCR = (1<<SPE)|(1<<MSTR)|(1<<CPHA);
  clr=SPSR;
  clr=SPDR;
  
  cs_en();
  sendCommand(CMD_WEN,0x600);
  cs_de();
   
}

void cs_en()
{
  digitalWrite(SLAVESELECT,HIGH);
}

void cs_de()
{
  digitalWrite(SLAVESELECT,LOW);
}

コマンドの送信

この93C86のコマンドは、スタートビット1bit, オペコード2bit、アドレス11bit(8bitモードの場合)、合計14bitです。ArduinoのSPIインターフェースは8bit単位でしか送受信できませんが、3WireはCSがアクティブになった後の最初の“1”入力をスタートビットの見なし、その前のゼロがあれば無視されます。したがってこの14bitの前にゼロを追加して16bitとすればSPIインターフェースを使って簡単にコマンドが送信できます。
オペコードとアドレスを受け取り、コマンドとして組み立てて送信するサブルーチンを作成しました。8bitモードを想定しているのでスタートビットは12ピット目(13ビットシフト)、オペコードは11ビットシフトとなります。オペコードが00の時の使用法がお洒落じゃありませんが、まぁ、妥協。そうして組み立てた16bitのデータを上位8bit、下位8bitの順に送信します。
spi_transfer()関数はPlaygroundページのものと同一、送信するとともに受信します。

Sketch
void sendCommand(byte cmd, word address) 
{
  word sendcmd;
  
  sendcmd = (1 << 13) | (cmd << 11) | address;

  spi_transfer((char)(sendcmd >> 8));
  spi_transfer((char)(sendcmd & 0xff));
}

char spi_transfer(volatile char data)
{
  
  SPDR = data;                    // Start the transmission
  while (!(SPSR & (1 << SPIF)))     // Wait for the end of the transmission
  {
  };

  return SPDR;                    // return the received byte
}
        

データの書き込み

書き込み時には、書き込む前にBusy/Readyをチェックするようにします。それがcheckReady()です。CSをイネーブルにした後、DI端子がLの場合はBusyなのでこれがHになるまで待ちます。Busyだった場合は一旦CSをLに落としてからHに戻す必要があります。ifとwhileが冗長ですが、仮に最初からBusyじゃなかった場合でもCSを一旦Lに落とすってのは美しくないので、「H(最初からReady)だったらそのまま、L(Busy)だったらHになるまで待ち、 Readyになったら後にCSを一旦Lに落としてHに戻す」という処理をしています。
Readyであれば、書き込みのコマンド(オペーコードとアドレス)、そして書き込むデータを送信するだけです。書き込みは1バイト(または1ワード)ずつアドレスを指定する必要があるようで、大きなデータを連続して書き込む事はできないようです。

Sketch
void checkReady()
{
  if(digitalRead(DATAIN) == LOW) {
    // busy
     while(digitalRead(DATAIN) == LOW) {
      ; 
     }
     cs_de();
     cs_en();
  }
}

void writeData(word address,byte data)
{
  cs_en();
  checkReady();
  sendCommand(CMD_WRITE,address);
  spi_transfer(data);
  cs_de();
}
    

データの読み出し

データの読み出し時、書き込んだ直後のデータを読み出す可能性を考慮し、書き込み時同様にBusy/Readyをチェックするようにしました。
あとは読み出しのオペコードとアドレスを送信し、ダミーのデータ(この例では0xff)を送信することで読み出したデータを受信します。連続するアドレスを読み出す事が可能です。その場合はCS端子をHに保ったまま、連続して次のデータを読み出すだけです(下記のルーチンにはその処理は含まれてません)。

Sketch
byte readData(word address) 
{
  byte data;
  
  cs_en();
  checkReady();
  sendCommand(CMD_READ,address);
  data = spi_transfer(0xff);
  cs_de();
  return(data);
}

あとはこれらを組み合わせるだけです。
3-wireは高速なアクセスが可能だと思いますが、書き込み時の連続書き込みをサポートしていないからでしょうか、大容量のものが無いですね。93C86は16kbit、秋月で80円でした。Arduino用として考えると、ATMega328で1kByteのEEPROM内蔵なので、ちょっと微妙かな。

 



home | Elec top

inserted by FC2 system