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通信概要 おわり
工事中