目次

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;
  }
}


目次

inserted by FC2 system