目次
前
次
SPIスレーブ処理
センサーや他のマイコンとのインタフェースに
SPIを利用することがあります。
SPIマスターとして動作は、簡単ですがスレーブの
場合には、待ち受け処理が必要なので、少し面倒
になります。
次のようなシステムで、SPIマスターから送信される
コマンドとパラメータを使っての動作を確認しました。
SPIインタフェースは、マスターとスレーブに各々が
用意したシフトレジスタの間で、データ交換すると
いうハードウエアのイメージを理解すると、コード
作成は難しくないと思います。
SPIインタフェースのデータ交換イメージは、以下。
信号は、データ交換に使う3信号の他に、どのスレーブ
とのデータ交換するかの指定が必要。
マスターは、どのスレーブとデータ交換したのかを
SS(Slave Select)を使って、明示します。
ワンチップマイコンを使っていると、イメージを
掴みにくいですが、CPUとROM、RAM、ペリフェラル
をバスにて接続する場合、CS(Chip Select)または
CE(Chip Enable)を利用して、チップを特定する
のと同じカラクリを採用しています。
SPIスレーブは、受信割込みを使い、データを
受信バッファに格納して、取りこぼしがない
ように、コードを作成。
SPIの受信割込みの処理は、次のように記述できます。
ISR(SPI_STC_vect)
{
char ch;
/* echo back */
ch = SPDR;
SPDR = ch;
crlf();
rs_puts("ch:");
show_byte( ch ) ;
rs_puts("SPDR:");
show_byte( SPDR ) ;
/* store */
*(spibuf + sindex) = ch;
/* update */
sindex++;
/* */
if ( ch == '\r' ) {
eflag = ON ;
sindex = 0;
}
/* show index of recieve buffer */
rs_puts("sindex");
rs_puchar( sindex + '0' );
crlf();
}
SPIのマスターとスレーブでは、シフトレジスタを
利用して、データ交換するので、エコーバックでの
マスターがスレーブへの送信データの確認ができる
ようにしておきます。
マスターとスレーブで、通信プロトコルを規定して
1レコードを送信する仕様で、割込み処理を記述。
このとき、受信バッファに1レコード分のデータを
放り込んでおいて、レコード全部を受信し終わった
ことを、フラグで通知しておきます。
動作イメージは、以下。
イベント発生の通知を受けたなら、関数loop()の中に
処理ルーチンを用意して、コマンドインタプリタを
動かします。
/* SPI command interpreter (begin) */
if ( eflag == ON ) {
/* clear flag */
eflag = OFF ;
/* command */
cmd = *(spibuf + 0);
/* */
rs_puts("cmd:");
rs_putchar( cmd );
/* parameter */
xx = *(spibuf + 1);
/* command 'R' */
if (cmd == 'R') {
rs_puts("xrf:");
show_byte( xx );
}
/* command 'L' */
if (cmd == 'L') {
rs_puts("xlf:");
show_byte( xx );
}
/* command 'S' */
if ( cmd == 'S' ) {
rs_puts("action:");
show_byte( xx );
}
SPI関係の割込みがあったときに、どんなコマンドと
パラメータが与えられたのかを、シリアルでモニタ
すると、わかりやすいので、文字列処理関数を用意
しています。
文字列処理に関係する関数の内容は、次のように定義。
void rs_putchar(char x)
{
Serial.write(x);
}
void rs_puts(char *ptr)
{
while ( *ptr ) {
rs_putchar( *ptr );
ptr++;
}
}
void crlf()
{
rs_putchar('\r');
rs_putchar('\n');
}
SPIでは、8ビット(1バイト)ごとのデータ転送が
使われるので、1バイトの内容を16進数表示する関数
を用意して、使い勝手をよくします。
void show_byte(byte x)
{
char dh ;
char dl ;
/* separate */
dh = (x >> 4) & 15 ;
dl = x & 15 ;
/* show */
rs_putchar( get_asc( dh ) );
rs_putchar( get_asc( dl ) );
/* new line */
crlf();
}
char get_asc(byte x)
{
char result ;
/* default */
result = '0' ;
/* judge */
if ( 0 <= x && x <= 9 ) { result = x + '0' ; }
if ( x > 9 ) { result = x - 10 + 'A' ;}
return result ;
}
SPIマスターとしてRaspberryPiを用意して、Pythonの
コードで動作確認すると、次のようになりました。
基本動作を確認できたので、実際に利用したスケッチを
見てみます。
#include <TinyGPS++.h> /* GPS */
#include <SoftwareSerial.h> /* GPS */
#include <SPI.h> /* RPI */
#include <Wire.h> /* HMC5883L */
/* IIC devece address */
#define address 0x0E /* HMC5883L */
/* SPI receive buffer size */
#define SPIBUFSIZE 8
/* logical string character */
#define OFF 0
#define ON OFF+1
/* software serial */
static const int RXD = 4 ;
static const int TXD = 3 ;
/* GPS baud rate */
static const uint32_t GPSBaud = 4800;
/* SPI chip select */
const int SSPin = 10 ;
/* 3D compass */
int x ;
int y ;
int z ;
/* GPS location */
double GPSlat = 0.15624894 ;
double GPSlng = 0.65726525 ;
double HMC ;
/* command interpreter variables */
boolean eflag;
byte spibuf[8];
byte sindex;
/* GPS class instance */
TinyGPSPlus gps;
/* serial class instance */
SoftwareSerial ss(RXD, TXD);
/* function prototype */
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
char get_asc(byte x);
void show_byte(byte x);
int HMC_Data();
void setup()
{
/* serial Interface */
Serial.begin(9600);
/* GPS */
ss.begin(GPSBaud);
/* SPI */
pinMode(MISO, OUTPUT); /* MISOを出力 */
SPI.setBitOrder(MSBFIRST); /* 最上位ビット(MSB)から送信 */
SPI.setClockDivider(SPI_CLOCK_DIV4); /* 通信速度をデフォルト */
SPI.setDataMode(SPI_MODE2); /* アイドル5Vで0V→5Vの変化で送信する */
SPCR |= _BV(SPE);
SPI.attachInterrupt();
/* HMC */
Wire.begin();
Wire.beginTransmission(address); /* open communication with HMC5883 */
Wire.write(0x02); /* select mode register */
Wire.write(0x00); /* continuous measurement mode */
}
void loop()
{
byte cmd ;
byte xx ;
/* GPS_program */
if (gps.location.isValid()) {
GPSlat = (gps.location.lat(), 6);
GPSlng = (gps.location.lng(), 6);
rs_puts("\n<PS_Data>\n");//以下デバック用
rs_puts("GPSlat=");
Serial.print(GPSlat);
rs_puts("\nGPSlng=");
Serial.print(GPSlng);
} else {
rs_puts("INVALID\n");
}
/* SPI command interpreter (begin) */
if ( eflag == ON ) {
/* clear flag */
eflag = OFF ;
/* command */
cmd = *(spibuf + 0);
/* */
rs_puts("cmd:");
rs_putchar( cmd );
/* parameter */
xx = *(spibuf + 1);
/* command 'R' */
if (cmd == 'R') {
rs_puts("xrf:");
show_byte( xx );
}
/* command 'L' */
if (cmd == 'L') {
rs_puts("xlf:");
show_byte( xx );
}
/* command 'S' */
if ( cmd == 'S' ) {
rs_puts("action:");
show_byte( xx );
}
}
/* SPI command interpreter (end) */
Wire.endTransmission();
delay(3000);
}
void rs_putchar(char x)
{
Serial.write(x);
}
void rs_puts(char *ptr)
{
while ( *ptr ) {
rs_putchar( *ptr );
ptr++;
}
}
void crlf()
{
rs_putchar('\r');
rs_putchar('\n');
}
char get_asc(byte x)
{
char result ;
/* default */
result = '0' ;
/* judge */
if ( 0 <= x && x <= 9 ) { result = x + '0' ; }
if ( x > 9 ) { result = x - 10 + 'A' ;}
return result ;
}
void show_byte(byte x)
{
char dh ;
char dl ;
/* separate */
dh = (x >> 4) & 15 ;
dl = x & 15 ;
/* show */
rs_putchar( get_asc( dh ) );
rs_putchar( get_asc( dl ) );
/* new line */
crlf();
}
int HMC_Data()
{
int hmcx;
/* HMC_program */
Wire.beginTransmission(address);
Wire.write(0x03); //select register 3, X MSB register
Wire.endTransmission();
/* get data from GPS */
Wire.requestFrom(address, 6);
if ( Wire.available() >= 6 ) {
/* x axis */
x = Wire.read() << 8; /* X msb */
x |= Wire.read(); /* X lsb */
/* z axis */
z = Wire.read() << 8; /* Z msb */
z |= Wire.read(); /* Z lsb */
/* y axis */
y = Wire.read() << 8; /* Y msb */
y |= Wire.read(); /* Y lsb */
}
/* デバック用 */
rs_puts(" \nHMC_Data_INPUT"); crlf();
hmcx = 360; /* atan2((x + 20) , (y + 20)*(-1)) * RAD_TO_DEG + 180; */
/* 40と20は補正値 */
return hmcx ;
}
ISR(SPI_STC_vect)
{
char ch;
/* echo back */
ch = SPDR;
SPDR = ch;
crlf();
rs_puts("ch:");
show_byte( ch ) ;
rs_puts("SPDR:");
show_byte( SPDR ) ;
/* store */
*(spibuf + sindex) = ch;
/* update */
sindex++;
/* */
if ( ch == '\r' ) {
eflag = ON ;
sindex = 0;
}
/* show index of recieve buffer */
rs_puts("sindex");
rs_puchar( sindex + '0' );
crlf();
}
setupの中に、SPIに関係する設定をまとめています。
pinMode(MISO, OUTPUT); /* MISOを出力 */
SPI.setBitOrder(MSBFIRST); /* 最上位ビット(MSB)から送信 */
SPI.setClockDivider(SPI_CLOCK_DIV4); /* 通信速度をデフォルト */
SPI.setDataMode(SPI_MODE2); /* アイドル5Vで0V→5Vの変化で送信する */
SPCR |= _BV(SPE);
SPI.attachInterrupt();
SPIマスターとスレーブで、データ通信するためには
プロトコルが必要なので、モードに関係する設定を
どうしているかのコメントを残しておきます。
マスターとスレーブの情報交換で、自分が最初に
書いたスケッチを載せておきます。
マスター
#include <MsTimer2.h>
#include <SPI.h>
#define OFF 0
#define ON OFF +1
boolean uflag ;
boolean tflag ;
boolean eflag ;
char sbuf[8];
byte sindex ;
char cmd ;
byte xcnt ;
void update_trigger();
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void show_help();
void spi_send(byte x);
void send_code(byte x);
void setup()
{
/* serial handling */
Serial.begin(9600);
show_help();
crlf();
/* SPI parameters */
pinMode(MISO, INPUT); /* MISO : input */
pinMode(MOSI,OUTPUT);
pinMode(SS,OUTPUT);
pinMode(SCK,OUTPUT);
SPI.setBitOrder(MSBFIRST); /* 最上位ビット(MSB)から送信 */
SPI.setClockDivider(SPI_CLOCK_DIV4); /* 通信速度をデフォルト */
SPI.setDataMode(SPI_MODE2); /* アイドル5Vで0V→5Vの変化で送信する */
SPI.begin();
/* set flags */
uflag = OFF ;
eflag = OFF ;
tflag = OFF ;
/* set parameters */
sindex = 0 ;
xcnt = 0 ;
/* 2000ms period */
MsTimer2::set(2000,update_trigger);
/* enable */
MsTimer2::start();
}
void loop()
{
/* command interpreter */
if ( uflag == ON ) {
/* clear */
uflag = OFF ;
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help(); }
/* enable */
if ( cmd == 'G' ) { eflag = ON ; }
/* disable */
if ( cmd == 'g' ) { eflag = OFF ; send_code( OFF ); }
}
/* timer handling */
if ( tflag == ON ) {
/* clear */
tflag = OFF ;
/* check */
if ( eflag == ON ) {
/* default */
if ( xcnt & ON ) { send_code( ON ); }
else { send_code( OFF ); }
/* update */
xcnt++ ;
}
}
}
void update_trigger()
{
/* set flag */
tflag = ON ;
}
void rs_putchar(char x)
{
Serial.write(x);
}
void rs_puts(char *ptr)
{
while ( *ptr ) {
rs_putchar( *ptr );
ptr++;
}
}
void crlf()
{
rs_putchar('\r');
rs_putchar('\n');
}
void show_help()
{
rs_puts("? help") ; crlf();
rs_puts("G enable") ; crlf();
rs_puts("g disable") ; crlf();
}
void spi_send(byte x)
{
/* select device */
digitalWrite(SS, LOW);
/* send 1 byte */
SPI.transfer( x );
/* relese device */
digitalWrite(SS, HIGH);
}
void send_code(byte x)
{
if ( x ) {
spi_send( 'L' );
spi_send( 1 ) ;
} else {
spi_send( 'l' );
spi_send( 0 ) ;
}
/* delimiter */
spi_send( '\r' );
}
/* receive interrupt */
void serialEvent()
{
char ch;
if ( Serial.available() > 0 ) {
/* get 1 character */
ch = Serial.read();
/* store */
*(sbuf+sindex) = ch ;
/* increment */
sindex++ ;
/* judge */
if ( ch == '\r' ) {
sindex = 0 ;
uflag = ON ;
}
}
}
スレーブ
#include <SPI.h>
/* SPI receive buffer size */
#define SPIBUFSIZE 8
/* logical string character */
#define OFF 0
#define ON OFF+1
#define LED_BIT 0
/* SPI chip select */
const int SSPin = 10 ;
/* command interpreter variables */
boolean eflag;
byte spibuf[SPIBUFSIZE];
byte sindex;
/* function prototype */
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
char get_asc(byte x);
void show_byte(byte x);
void setup()
{
/* serial Interface */
Serial.begin(9600);
rs_puts("Hello !");
crlf();
/* set LED bit */
pinMode(8,OUTPUT);
digitalWrite(8,HIGH);
/* SPI */
pinMode(MISO,OUTPUT); /* MISOを出力 */
pinMode(MOSI,INPUT);
pinMode(SSPin,INPUT);
pinMode(SCK,INPUT);
SPI.setBitOrder(MSBFIRST); /* 最上位ビット(MSB)から送信 */
SPI.setClockDivider(SPI_CLOCK_DIV4); /* 通信速度をデフォルト */
SPI.setDataMode(SPI_MODE2); /* アイドル5Vで0V→5Vの変化で送信する */
SPCR |= _BV(SPE);
SPI.attachInterrupt();
}
void loop()
{
byte cmd ;
byte xx ;
/* SPI command interpreter (begin) */
if ( eflag == ON ) {
/* clear flag */
eflag = OFF ;
/* command */
cmd = *(spibuf + 0);
/* parameter */
xx = *(spibuf + 1);
/* turn on LED */
if ( cmd == 'L' ) { digitalWrite(8,HIGH) ; }
/* turn off LED */
if ( cmd == 'l' ) { digitalWrite(8,LOW); }
}
/* SPI command interpreter (end) */
}
void rs_putchar(char x)
{
Serial.write(x);
}
void rs_puts(char *ptr)
{
while ( *ptr ) {
rs_putchar( *ptr );
ptr++;
}
}
void crlf()
{
rs_putchar('\r');
rs_putchar('\n');
}
char get_asc(byte x)
{
char result ;
/* default */
result = '0' ;
/* judge */
if ( 0 <= x && x <= 9 ) { result = x + '0' ; }
if ( x > 9 ) { result = x - 10 + 'A' ;}
return result ;
}
void show_byte(byte x)
{
char dh ;
char dl ;
/* separate */
dh = (x >> 4) & 15 ;
dl = x & 15 ;
/* show */
rs_putchar( get_asc( dh ) );
rs_putchar( get_asc( dl ) );
/* new line */
crlf();
}
ISR(SPI_STC_vect)
{
char ch;
/* echo back */
ch = SPDR;
SPDR = ch;
/* store */
*(spibuf + sindex) = ch;
/* update */
sindex++;
/* debug */
show_byte( ch ) ;
show_byte( sindex );
/* */
if ( ch == '\r' ) {
eflag = ON ;
sindex = 0;
}
}
目次
前
次