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

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


 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のマニュアルの
方が判りやすいかもね。 *4)

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

[ピンの配置図]

配置図1  配置図2
今回使用するPIC16F1827のピンは、電源ピンの14番(VDD:+5V)と5番(VSS:GND)、
SPIピンの10番(RB4:SCK1)と7番(RB1:SDI1)と8番(RB2:SDO1)に11番(RB5:SS1)です。
それとEEPROMから読み出したデータ表示用として18番(RA1)から、
別途作成した自作のLCDモニターに出力しています。
また、AT93C86の電源ピンは8番(VCC:+5V)と5番(VSS:GND)です。

[配線図]

配線図1

SDI1ピンについて
SDI1(RB1)の7番ピンは必ずデジタル入力に設定を行います。

プルダウン抵抗について
STMicroelectronics社M93C86のマニュアルを見ると抵抗が付いています、 *4)
「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頻繁に切り替える必要が有ります。
また、PICがスレーブモードでSS信号を使用する場合は11番ピン(SS1:RB5)に接続しないとだめですが、 マスターモードでSS信号を使用する場合は何処のデジタルピンでも構いませんが今回は11番ピンを使っています。

《ダウンロードプログラムについて》

↓ここからサンプルプログラムソースファイルをダウンロードして下さい。
AT93C86.lzh(Ver2.00:2014/02/02) *2)
AT93C86.lzh(2015/10/10) *3)

プログラムソースをダウンロードしたら、MPLAB Xにてプロジェクトを作成します。
以下のファイルをプロジェクトディレクトリにコピーしてプロジェクトに取込んで下さい。
次にコンパイルPIC書き込みを実行して下さい。 *3)
MPLAB(R) XC8 C Compiler Version 1.32コンパイラを使用しています。

ダウンロードしたら解凍して下さい、以下のファイル構成です。
 AT93C86.c ・・・・・本体のソースプログラム *3)
 sk93C86.c ・・・・・・93C86(16Kbite) EEPROM 関数を記述したライブラリソースプログラム
 sk93C86.h ・・・・・・ヘッダファイル

このプログラムにはデバッグモニタープログラム「skMonitorLCD.c」「skMonitorLCD.h」が必要です。
デバッグLCDモニターについてはこちらを参照して下さい。
「skMonitorLCD.c」は送信データをTimer2のタイミングで送る様に変更しました、
その為にAT93C86.cプログラムを変更しています。
 *2)

また、LCDモニターの出力はRA1から行っているので「skMonitorLCD.h」を下記の様に変更します。
#define _XTAL_FREQ 8000000  // 使用するPIC等により動作周波数値を設定する
#define MONITOR_PIN RA1      // モニタ出力するピンの番号を設定する

AT93C86.c

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

sk93C86.h

93C86(16Kbite) EEPROM 関数ライブラリ用インクルードファイルです。
"sk93C86.c"を利用する場合に
#include "sk93C86.h" をプログラムの先頭で記述しないとだめです。

また、SS(CS)信号を出力するピンを変える場合は下記の行を変更して下さい。
#define SS RB5 // SS(CS)のチップセレクト端子を指定します

sk93C86.c

このライブラリはAT93C86とデータのやり取りを行う為の関数集です。
尚、"sk93C86.c"はPIC16F1827だけでなく、12F1822やおそらく他のPICでF1シリーズは使用できると思います、 たぶん....、他のPIC持ってないのでぇにゃんともですがぁ、試してみてみてぇ。

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

Inter93C86()
 EEPROM(93C86)関連の割り込み処理です。
 この関数はメインプログラムの割込み関数で必ず呼びます。
 例)
     void interrupt InterFunction( void )
     {
          Inter93C86() ;
     }

MEM_Init(protect)
 EEPROM 関数ライブラリを使用する為の初期化を行う処理です。
  protect : 0=EEPROMに書込みを禁止する(読み込みは可能) 1=EEPROMに書込みを許可する

 SDO1ピンをRB2に割り当てています、RA6(15)ピンにしたい人は"SDO1SEL=1"に変更して下さい。
 AT93C86のシリアルクロックレートは最大2MHzですので、FOSC(8MHz)/4で初期化されています。
 CPUクロック(FOSC)を変える人は下記の赤い数字部分を変更して下さい。
 SSP1CON1 = 0b00100000 ;
  0000 : FOSC/4 (SPIのマスターモードで設定)
  0001 : FOSC/16 (SPIのマスターモードで設定)
  0010 : FOSC/64 (SPIのマスターモードで設定)
  0011 : TMR2 output/2 (タイマー2の出力を使用する設定)
  1010 : FOSC/(4 * (SSP1ADD+1)) (SSP1ADDに設定が必要)

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

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

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

     MEM_Read(0x00,dt,2) ;
     MonitorPutc(0x11) ;
     MonitorPuth(dt[0]) ;
     MonitorPutc(' ') ;
     MonitorPuth(dt[1]) ;
 ※ 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バイト書き込む
     int ans ;
     char dt[4] ;

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

整数型(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()
     snd_rcv(0b00001001) ;     // オペコード"10011"
     snd_rcv(0b10000000) ;
MEM_WDS()
     snd_rcv(0b00001000) ;     // オペコード"10000"
     snd_rcv(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()
     snd_rcv(0b00100110) ;     // オペコード"10011"
     snd_rcv(0b00000000) ;
MEM_WDS()
     snd_rcv(0b00100000) ;     // オペコード"10000"
     snd_rcv(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)とArduinoを接続した記事はこちらを参照下さい。
・ EEPROM(24LC256/1024)とPICを接続した記事はこちらを参照下さい。
・ EEPROM(24LC256/1024)とArduinoを接続した記事はこちらを参照下さい。

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

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

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



リンク切れ見直し(*4) 2017/01/11
MPLAB X用に記事変更(*3) 2015/10/10
"skMonitorLCD.c"変更により"AT93C86.c"のプログラム変更(*2) 2014/02/02
追記(*1) 2012/07/12


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