SPIでEEPROM(AT93C86)と接続して読み書きを行って見ます

〔Arduinoの動かせ方入門に戻る〕


 EEPROMで93C86シリーズと言うのが有り各社から販売されています、
これは3線式シリアル(Microwire:マイクロワイヤ)で接続するEEPROMで、 MicrowireはSPIと似ていますが少し異なるインターフェイスです。
なのでぇ、例えばATMELが出しているAT93C86はMicrowireだけどぉ、 AT25シリーズでSPIを販売している所からも見てもぉ、やっぱりぃ別物みたいなぁ。
だが、少し工夫をすればSPIでも接続が可能です、今回はその紹介です。 今回は上手く接続が出来ましたが、場合によってはダメな場合が有るかもね。

MicrowireとSPIの違いは?
SPIはモトローラが提唱した規格でMicrowireはナショナル・セミコンダクタが提唱した規格です。
SPIはSS信号を通常HIGHで出力し、接続したいスレーブにはLOWを出力して通信を行います。
 Microwireは逆です、また、SS信号の立ち上がりでデータを読み取り、立下りで読み取り終了に使用
 します。だからMicrowireはSS信号をコマンドを送受信する度にHigh/Low操作する必要が有ります。
SPIはデータの送信が8バイト単位で、Microwireは決まっていませんコマンドにより可変します。
 なので、Microwire方式をSPIで送受信するならデータを8バイトの単位で区切って数回に渡り送受信
 する必要が有ります。
SPIはデータの送信と受信が同時に発生しますが、Microwireは送信を行った後受信に移行します。
 (8バイトに区切るのでダミービットを作ったり、受信する為にダミーデータを送信する必要が有ります)

 この実験ではATMELのAT93C86を使用し、x8bitモードで行っています。
また、他社の93C86シリーズも利用出来ます、 それと、93C76シリーズも利用可能ですが93C76は93C86(16KBit:2048Byte)の半分の容量しかないのでアドレス指定時の上位1ビット使いません。
尚、AT93C86は秋月電子のこちらから購入しています。
また、AT93C86のマニュアルはこちらですが、 こちらのSTMicroelectronics社M93C86のマニュアルの
方が判りやすいかもね。 *3)

あと、SPIの基本概要等はこちらの記事を参考にして下さい。

[配線図]
配線図

プルダウン抵抗について
STMicroelectronics社M93C86のマニュアルを見ると抵抗が付いています、 *3)
「SS(CS)ラインが何処にも無接続状態になった場合にEEPROMが選択されていない状態(GND接地)になるからいいよ」
みたいなぁ、事が書いて在ります、なので適当に付けといた方が良いかも(無くても動作はします)。

あとぉ、自分で基板を起こす場合はAT93C86の電源ピン8番と5番の間に0.1uFコンデンサ(バイパスコンデンサ)を取り付けた方が良いです。
「安定した電源電圧を確保する必要が有る為、VCC/VSSパッケージのピンの近くに適当なコンデンサ(通常100nF〜10nF)でVCCラインを分離することをお勧めします」
Google 翻訳の直訳です、みたいなぁ事が書いてあるようなぁ。

ORGピンについて
AT93C86の6番ピンは読み書きするデータのサイズを変更する場合に使用します。
+5V(VDD)に配線すると16bitモードです、2バイト(WORD)単位でのアクセスでアドレスは0-1023です。
GND(VSS)に配線すると8bitモードです、1バイト単位でのアクセスでアドレスも0-2047になります。
今回のライブラリは8bitモードのみ対応です。

SS(CS)ピンについて
通常SPIは、スレーブデバイスがAとBの2個が接続されているとするとSS信号線が各A/Bに1本ずつ
配線されます、でぇ、通信したいデバイスのSS信号をLOWにします、MicrowireはHIGHです。
ただ、MicrowireはこのSS信号のHIGH立上りが通信開始のタイミングでLOWで通信終了なので、
送受信する度にSS信号をHIGH/LOW頻繁に切り替える必要が有ります。
尚、ArduinoのSPIライブラリを使用していますが、 このライブラリはSS信号の10番ピンを初期化でHIGHにしているだけです。

《ダウンロードスケッチについて》

↓ここからArduino用サンプルスケッチファイルをダウンロードして下さい。
sk93C86.lzh(Ver1.00:2012/01/30)
sk93C86.zip pde->ino *4)

解凍すると下の様に展開されます。
[sk93C86]─┬─[examples]───[AT93C86]--- AT93C86.ino
      ├ sk93C86.cpp
      ├ sk93C86.h
      └ keywords.txt
ArduinoIDE0022がインストールされているフォルダー、インストール先を変更していないなら
[C:\Program Files\arduino-0022\libraries]です。
この場所に上記解凍ファイルを[sk93C86]ディレクトリ丸ごと移動します。
また、こちらの[ライブラリ登録]を参考すればより理解が深まるかもぉ?いや、こんがらがるかもぉ。

AT93C86.ino  ・・・・・本体のサンプルスケッチ
sk93C86.cpp ・・・・・93C86(16Kbite) EEPROM 関数ライブラリソース
sk93C86.h ・・・・・・ヘッダファイル
keywords.txt ・・・・・キーワードファイル

注意)
ArduinoIDE 1.0.1以降からArduinoライブラリ用インクルードファイル名が変更されています、
なのでこれ以降を利用している人は、sk93C86.cpp と sk93C86.h に
#include "WProgram.h" と記述しています、これを #include "arduino.h" に変更願います。 *2)
PS. スケッチは変更しました。 *4)

AT93C86.ino

AT93C86.inoを開くには、
IDEを起動して、メニューバーの「File」→「Examples」→「sk93C86」→[AT93C86]
をクリック操作すればファイルが開かれます。
次に、IDEツールバーの「Upload」ボタンをクリックしてコンパイルとarduinoボードに書込みを行います。

IDEのシリアルモニターCOM画面を表示させましょう。
起動が成功すると画面には数値が"10 20 30"と表示されます、表示が異なる場合は配線等を見直して下さい。
EEPROMの0番地から3バイト[10][20][30]を書き込み、その後に0番地から3バイト読み込んで表示しているだけです。
AT93C86は16Kbit(+8bitモードで2048Kbyte)なので0番地から2047番地までアクセス出来ます。

skMC24xxx.h

93C86(16Kbite) EEPROM 関数のインクルードファイルです。
このライブラリを利用する場合は、メニューバーの「Sketch」→「ImportLibray...」→「sk93C86」を
クリック操作すれば、"#include <sk93C86.h>"がスケッチに追加されます。
まぁ、手動でキー入力しても良いんですけどね。

skMC24xxx.cpp

このライブラリはAT93C86とデータのやり取りを行う為の関数集です。

まず利用する場合は下記2行を記述します。
#include <SPI.h>
#include <sk93C86.h>

93C86(16Kbite) EEPROM 関数ライブラリの使い方を説明します。

sk93C86
 EEPROM関数ライブラリを使用する為に必要な宣言(初期化)を行います。
 sk93C86 MEM(protect) ;
  protect : 0=EEPROMに書込みを禁止する(読み込みは可能) 1=EEPROMに書込みを許可する
 MEMの名前は任意に変更可能です。

 AT93C86のシリアルクロックレートは最大2MHzですので、FOSC(16MHz)/8で初期化されています。

 sk93C86 MEM(1) ; 以下はこの宣言例で記述します。

MEM.WEN()
 EEPROM書き込みプロテクトです、書き込みを許可する処理です。

MEM.WDS()
 EEPROM書き込みプロテクトです、書き込みを禁止する処理です(読み込みは可能)。

ans = MEM.Read(adrs,*data,num)
 EEPROMの指定番地から指定した個数だけデータを読み出す処理です。
  adrs  :読み出し開始のデータアドレス位置を指定します。(AT93C86なら0〜2047番地)
  *data:読み出したデータを保存する為の配列変数を指定します。
  num  :読み出すデータの個数を指定します。(この個数分だけ配列変数は確保します)
  ans   :他のEEPROMライブラリと互換性を確保する為に単純に0を返すだけです
 例)0番地から2バイト読み込み、シリアルモニターに表示する
     char dt[4] ;

     MEM.Read(0x00,dt,2) ;
     Serial.print(dt[0],HEX) ;
     Serial.print(' ') ;
     Serial.print(dt[1],HEX) ;
 ※ 2046番地から4バイト読み出した場合は、[2046][2047][0][1]番地と言った感じに0番地に
   戻って読み出されます。

ans = MEM.Write(adrs,*data,num)
 EEPROMの指定番地へ指定した個数だけデータを書き込む処理です。
  adrs  :書き込み開始のデータアドレス位置を指定します。(AT93C86なら0〜2047番地)
  *data:書き込むデータを保存した配列変数を指定します。
  num  :書き込むデータの個数を指定します。
  ans   :他のEEPROMライブラリと互換性を確保する為に単純に0を返すだけです
 例)0番地から2バイト書き込む
     char dt[4] ;

     dt[0] = 1 ;
     dt[1] = 2 ;
     MEM.Write(0x00,dt,2) ;
 ※ 2047番地を超えた場合は、0番地から上書きされます注意して下さい。
 ※ 書き込み禁止状態で書き込んだ場合、メモリには書き込まれませんが、
   書き込み通信は正常に終了するので注意が必要かもね。

 注意 ArduinoのWireライブラリの送受信バッファが32バイトしか有りません、
     だからEEPROMに読み書きするサイズは最大で32バイトです。


整数型(int)で読み書きする技
 上のMEM.Read/MEM.Writeはバイト単位(char)で読み書きを行いますが、
 この関数で整数型(int)でも読み書き出来ます。
下の例はint型で書き込んでint型で読み込んでいます、int型は2バイトですね、だから

     int dt ;

     dt = 123 ;
     MEM.Write(0x00,&dt,2) ;   // 書き込み
     MEM.Read(0x00,&dt,2) ;    // 読み込み

ってやればOKです、当然int型で書き込んだらint型で読み込まないとだめですよ。
93C46/93C56/93C66の場合は?
 +8bitモードで使用するなら"sk93C86.c"ライブラリのオペコード部分とアドレスの長さを変更すれば
 利用可能ですが.....
 下記の様に変更すればOKと思いますが、このシリーズ持っていないのでデバッグ出来ていません。
93C46シリーズ
MEM_WEN()
     SPI.transfer(0b00001001) ;     // オペコード"10011"
     SPI.transfer(0b10000000) ;
MEM_WDS()
     SPI.transfer(0b00001000) ;     // オペコード"10000"
     SPI.transfer(0b00000000) ;
MEM_Read()
     // 送信データ(オペコード"110"とアドレス"7bit")の作成
     dt1 = 0b00000011 ;
     dt2 = 0b00000000 | (adrs & 0b0000000001111111) ;
MEM_Write()
          // 送信データ(オペコード"101"とアドレス"7bit")の作成
          dt1 = 0b00000010 ;
          dt2 = 0b10000000 | (adrs & 0b0000000001111111) ;

93C56/93C66シリーズ
MEM_WEN()
     SPI.transfer(0b00100110) ;     // オペコード"10011"
     SPI.transfer(0b00000000) ;
MEM_WDS()
     SPI.transfer(0b00100000) ;     // オペコード"10000"
     SPI.transfer(0b00000000) ;
MEM_Read()
     // 送信データ(オペコード"110"とアドレス"9bit")の作成
     dt1 = 0b00001100 | ((adrs & 0b0000000100000000) >> 8) ;
     dt2 = (adrs & 0b0000000011111111) ;
MEM_Write()
          // 送信データ(オペコード"101"とアドレス"9bit")の作成
          dt1 = 0b00001010 | ((adrs & 0b0000000100000000) >> 8) ;
          dt2 = (adrs & 0b0000000011111111) ;

《え、3線式シリアル接続ぅ??》

SPIは[SS][SDO][SDI][SCK]で接続なら4線じゃねぇ、
だが、マスターとスレーブ1対1ならSSいらないよねでぇ3線式?
でもねぇ、スレーブ2個接続するならSSを2本配線しないといけないからなぁ
それってぇ、なんだかなぁ....

Microwireもぉ、[CS][DO][DI][SK]接続しないといけないからやっぱぁ4線じゃねぇ、
配線図2 だが、Microwireはデータを送信した後、データを受信するので左図の様に配線するとぉ3線式?。 (この配線で実験はしていません)
(SPIは送信と受信が同時に起きるのでこんな配線をしてはだめですよ)
でもねぇ、スレーブ2個接続するならやっぱりぃCSを2本配線しないといけないからなぁ
これもぉ、なんだかなぁ....
いんちきっぽ〜い、やっぱりI2C接続かぁ?スレーブ何個接続しようと2本だしねぇって感じぃ。


・ EEPROM(AT93C86)とPICを接続した記事はこちらを参照下さい。
・ EEPROM(24LC256/1024)とArduinoを接続した記事はこちらを参照下さい。
・ EEPROM(24LC256/1024)とPICを接続した記事はこちらを参照下さい。

《読者様からの情報》 *1)

★ UNOさんからの情報です。
  「93C46/93C56/93C66の場合は?」の記述に加えて、次の変更が必要でした。
  AT93C56は、シーケンシャルリードに対応していません。
  なので、AT93C56Aにするか、シーケンシャルリードを諦めるかのどちらかです。
  私の場合は、後者の変更で、無事に読み書きできるようになりました。

  と言う事らしいです、でぇ、AT93C56のデータシートを改めてよくみてみたらAT93C56/66は
  AT93C56A/66Aを使う様に推奨されています。*1)



スケッチの拡張子を"pde"から"ino"に変更(*4) 2020/04/07
リンク切れ見直し(*3) 2017/01/10
追記(*2) 2012/09/14
追記(*1) 2012/07/12


【きむ茶工房ガレージハウス】
Copyright (C) 2006-2020 Shigehiro Kimura All Rights Reserved.