SPI通信   

 SPI通信の学習記録です。


 3 maximRTC(DS3234S)の読み書き例 tiny2313  
 2 4つの動作モードについて  
 1 SPI通信概要  

















 3 maximRTC(DS3234S)の読み書き例 tiny2313 

tiny2313はSPI通信のハードウエアを持っていません。したがってソフトウエアSPIとしました。
このRTCは動作モード1ですからそれに対応しています。
/***************************************************************************************
AVRの接続:
   6 PD2/INT0   ←RTC #INT/SQW // (1Hz利用のため)
  12 BP0        →RTC SPI CS
  13 BP1        →RTC SPI CLK
  14 BP2        ←RTC SPI DO
  15 BP3        →RTC SPI DI
  
***************************************************************************************/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <string.h>                  // string
#include <util/delay.h>
#include <avr/pgmspace.h>

 /* SPI設定 */
#define CS_H()    PORTB |= 0x01  /* set rtc CS "high" */
#define CS_L()    PORTB &= 0xFE  /* set rtc CS "low" */
#define CK_H()    PORTB |= 0x02  /* set rtc SCLK "high" */
#define CK_L()    PORTB &= 0xFD  /* set rtc SCLK "low" */
#define DO_H()    PORTB |= 0x04  /* set rtc DI "high" */
#define DO_L()    PORTB &= 0xFB  /* set rtc DI "low" */
#define DI       (PINB &  0x08)  /* get rtc DO value (high:true, low:false) */

  /* プロトタイプ宣言 */
  uint8_t xmit(uint8_t);   

//----------------------------------------------------------------------------------------------------------
int main(void)      /* ***************************  main  ************************************************* */
{
  uint32_t fq,seisuu,amari; //測定値,整数部分,余り部分
  char s[20];
  char msg[30];
  uint8_t ss,mm,hh;
  uint8_t i,menu_n;
  
  DDRB=0b00010111;
  PORTB=0xff;                                /*  */
   /* SWQに1Hz出力設定  8Eh番地に 0x00(RS2=0 RS1=0 INTCH=0)を書く */

  CS_L();
  xmit(0x8e);  // Eh write mode
  xmit(0x00);  // write 00h
  CS_H();

  while(1){
    if(menu_n==1){                            //  menu 1  周波数カウンタ  1111111111111111111111
                                                   
        CS_L();
        xmit(0x00);     // set read
        ss=xmit(0x00);  // read 00h
        mm=xmit(0x00);  // read 01h
        hh=xmit(0x00);  // read 02h
        CS_H();
      }//if flg
    }//11111111111111111111

    if(menu_n==2){                            //  menu 2  時刻合わせ  222222222222222222222222222222222222 
      strcpy_P(msg,PSTR("24時制で hhmmss を入力してください。")); string_out(msg);
      while(1){
        if(rflg==1){
          uint8_t i,h,m,ss;
          rflg=0;
          for(i=0;i<20;i++){s[i]=recbuf[i];}    //        strcpy(s,recbuf);  //
          if(s[0]<0x33){
            h=(s[0]-'0')*16+(s[1]-'0');
            m=(s[2]-'0')*16+(s[3]-'0');
            ss=(s[4]-'0')*16+(s[5]-'0');
            
            CS_L();
            xmit(0x80);     // set write mode
            xmit(ss);       // write 00h
            xmit(m);        // write 01h
            xmit(h);        // write 02h
            CS_H();
          }// if s[0]

        }//end if rflg
      }//end while
    }//22222222222222222222
  }//while
}//------------メインルーチンの終わり------------------------------------------------------------


 /* ----------------------  サブルーチン  ------------------------------------------------------ */
 /* ******************  SPI 1byte通信ルーチン ************************************ */
uint8_t xmit(uint8_t d){    /* 引数=送信データ  戻り値=受信データ */
  uint8_t e=0,i;
//  uint8_t b[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
  uint8_t b=0x80;
  
  CK_L();    /* アイドル時のクロックはL */
  for(i=0;i<8;i++){ // 8回繰り返しで1バイト送受信する
    if(d & b) DO_H(); else DO_L();    // (MSB側の)bitが1ならportにHiを設定する
    CK_H();                             // クロックをH にしてポート準備完了を伝える
    CK_L();                             // クロックを立ち下げてデータ取り込み処理を促す
    e<<=1;                              //  前回の取り込みデータをLSBから1bit左シフトする
    if(DI) e++;                         // ポートがHiならLSBに1を書き込む 
    b>>1;                               //  次の送受信のために送り出しビットを右に指定し直す
  }
  return e;
}

1バイト送受信ルーチンは次の書き方の方が理解しやすい場合があります。
  CK_H(); if(d & 0x80) DO_H(); else DO_L(); CK_L(); e=0;   if(DI) e++; /* bit7 */
  CK_H(); if(d & 0x40) DO_H(); else DO_L(); CK_L(); e<<=1; if(DI) e++; /* bit6 */
  CK_H(); if(d & 0x20) DO_H(); else DO_L(); CK_L(); e<<=1; if(DI) e++; /* bit5 */
  CK_H(); if(d & 0x10) DO_H(); else DO_L(); CK_L(); e<<=1; if(DI) e++; /* bit4 */
  CK_H(); if(d & 0x08) DO_H(); else DO_L(); CK_L(); e<<=1; if(DI) e++; /* bit3 */
  CK_H(); if(d & 0x04) DO_H(); else DO_L(); CK_L(); e<<=1; if(DI) e++; /* bit2 */
  CK_H(); if(d & 0x02) DO_H(); else DO_L(); CK_L(); e<<=1; if(DI) e++; /* bit1 */
  CK_H(); if(d & 0x01) DO_H(); else DO_L(); CK_L(); e<<=1; if(DI) e++; /* bit0 */
送信データ変数dのMSBから順にMOSI端子に送り出し、MISO端子からのビットを受信データ変数eのLSBに取り込んで左へシフトしています。






2 4つの動作モードについて

SPIはクロックのエッジでデータ交換を行い(シフト動作)、他のエッジでそれぞれがデータを取り込み(ラッチ動作)ます。その組み合わせで4種類の動作モードが考えられます。
動作モードアイドルsckアイドル時のポート状態SCKエッジとデータ状態SCKエッジとデータ状態
 0 Loデータ準備立ち上がりでラッチ立ち下がりでシフト(データ準備)
 1 Lo-立ち上がりでシフト(データ準備)立ち下がりでラッチ
 2 Hiデータ準備立ち下がりでラッチ立ち上がりでシフト(データ準備)
 3 Hi-立ち下がりでシフト(データ準備)立ち上がりでラッチ
この関係はChaNさんの伝送タイミングの図に詳しく書かれています。

ソフトウエアSPIのときは、ラッチの前にポートにデータを準備して、シフトで準備完了の合図を送り、ラッチでポート状態を所定のレジスタ(変数に)取り込みます。
スレーブデバイス(温度センサ、RTC、EEPROMなど)によって動作モードが決まっているのでマスタはそれに合わせる必要があります。





 1 SPI通信概要 

SPI(Serial Peripheral Interface)はボード間またはモジュール間の通信に使う方法でマスタから起動してスレーブとデータ交換をします。
マスタ1個に対してスレーブは複数が可能です。通信線は複数のスレーブに共通な(並列接続する)データ送信MOSI、データ受信MISO、クロックSCKの3本と通信するスレーブを選択するスレーブ選択SS線です。SS線はスレーブの数だけ必要です。

通信するスレーブはSSをLowにすることで通知します。スレーブはSSポートを調べて(通常は割り込みで)Lowであれば受信の準備をします。スレーブのデータ送出端子DOはマスタのMISOを繋がりますが選択されていないときはハイインピーダンスの状態で他への影響は与えない状態にしておきます。
8ビット通信の場合はマスタ、スレーブともに8ビットのシフトレジスタを持ちクロックに同期してMSBから順に相手のLSBへデータを送ります。8クロックでマスタとスレーブのデータが交換されることになります。



  1 SPI通信概要 おわり























工事中 inserted by FC2 system