目次

SPI関係スケッチ

 SPIインタフェースは、AVRのフラッシュROMに
 プログラムコードを転送するときに使われる
 ことが多く、ライターで利用されています。



 RaspberryPiとArduinoを接続する前に、2つのArduino
 マスターとスレーブの役割分担をし、接続しました。
 1台のPCにUSB/シリアル変換器を2つ接続し、2つの
 Arduinoと2つの仮想COMポートを使い、動作確認です。



 SPIマスター側は、Personal Computer上の
 端末ソフトで与えた文字列を受取り、SPI
 インタフェースでスレーブ側に転送します。

 端末ソフトとしてTeraTermを利用します。
 コマンドは、2つだけにしました。

 コマンドSでは、Sに続けて最大8文字まで
 入力できるようにします。

 仕様を固めたので、スケッチにします。

#include <SPI.h>
#include <MsTimer2.h>

#define OFF 0
#define ON  OFF+1

#define LED_BIT 7

#define nSS_BIT 2

volatile byte sindex ;
volatile char sbuf[16] ;
volatile byte xspi;
volatile byte xcnt;
volatile byte uflag;
volatile byte eflag;

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  send_led(byte x)
{
  if ( x ) { PORTD |=  (1 << LED_BIT); }
  else     { PORTD &= ~(1 << LED_BIT); }
}

void  update_trigger(void)
{
  eflag = ON ;
}

void show_help()
{
  rs_puts("? help");          crlf();
  rs_puts("S send SPI data"); crlf();
}

void setup (void)
{
  /* initialilze serial port */
  Serial.begin(9600);
  sindex = 0 ;
  /* set I/O values */
  PORTB = 0x00 ;
  PORTC = 0x00 ;
  PORTD = 0x01 ;
  /* set port directions */
  DDRB = 0x2f ;
  DDRC = 0xff ; 
  DDRD = 0xfe ;
  /* initialize SPI mode */
  {
    SPI.begin();
    SPI.setBitOrder(MSBFIRST);
    SPI.setDataMode(SPI_MODE0);
    SPI.setClockDivider(SPI_CLOCK_DIV8);
  }
  /* clear */
  xcnt = 0 ;
  /* clear flags */
  uflag = OFF ;
  eflag = OFF ;
  /* LED handling */
  MsTimer2::set(200,update_trigger);
  /* enable */ 
  MsTimer2::start();
}

void loop(void)
{
  char cmd ;
  byte i ;
  char msg[8] ;
  /* UART handling */
  if ( uflag == ON ) {
    /* clear flag */
    uflag = OFF ;
    /* new line */
    crlf() ;
    /* command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help() ; }
    /* send data to SPI */
    if ( cmd == 'S' ) {
      /* clear buffer */
      for ( i = 0 ; i < 8 ; i++ ) { *(msg+i) = 0 ; }
      /* get data */
      for ( i = 0 ; i < 8 ; i++ ) {
        /* judge */
        if ( *(sbuf+i+1) == '\r' ) break ;
        /* store */
        *(msg+i) = *(sbuf+i+1) ;
        /* enable nSS */
        PORTB &= ~(1 << nSS_BIT) ;
        /* transfer */
        SPI.transfer( *(msg+i) );
        /* disable nSS */ 
        PORTB |= (1 << nSS_BIT) ;
        /* delay */
        delay(1);
      }
    }
  }
  /* LED handling */
  if ( eflag == ON ) {
    /* clear flag */
    eflag = OFF ;
    /* impress */
    send_led( xcnt & ON ) ;
    /* update counter */
    xcnt++ ;
  }
}

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

 TeraTermでは、次のように操作します。



 スケッチの中では、setupの中に次の
 パラメータを設定しています。

 マスター側は、SPI.hの中に定義されている
 メソッドを使うだけなので、転送するときに
 SSをイネーブルにすることに気をつけます。

 スレーブ側は、SPIインタフェースで転送された
 文字を2進数に変換後、接続PCに転送します。

 接続PCでは、端末ソフトを利用しスレーブ側の
 ArduinoがSPIインタフェースで受信したデータ
 を確認します。

 SPIスレーブとして利用するため、チップ内部に
 あるレジスタでモードをスレーブにします。

 setupの中に、次のコードを入れます。

    SPCR |= (1 << SPE);

 1バイトのデータ受信は、ハードウエアに任せ
 受信終了を割込みによりloopに通知します。

ISR(SPI_STC_vect)
{
  /* get 1 byte from SPI Data Register */
  xspi = SPDR ;
  /* set trigger flag */
  sflag = ON ;
}

 SPI.hの内容を見ると、attachInterruptがある
 ので、外部割込みと等価な受信割込みを実現
 できるようになっていました。

 スレーブ側のスケッチは、以下。

#include <SPI.h>
#include <MsTimer2.h>

#define OFF 0
#define ON  OFF+1

volatile byte xspi;
volatile byte sflag;

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 setup (void)
{
  /* initialilze serial port */
  Serial.begin(9600);
  rs_puts("Hello!");
  crlf();
  /* set I/O values */
  PORTD = 0x01 ;
  PORTB = 0xef ;
  PORTC = 0x00 ;
  /* set port directions */  
  DDRD = 0xfe ;
  DDRB = 0x10 ;
  DDRC = 0xff ; 
  /* initialize SPI mode */
  {
    /* set SPI as slave */
    SPCR |= (1 << SPE);
    /* mode */
    SPI.setBitOrder(MSBFIRST);
    SPI.setDataMode(SPI_MODE0);
    SPI.setClockDivider(SPI_CLOCK_DIV8);
    /* enable SPI interrupt */
    SPI.attachInterrupt();
  }
  /* clear flags */
  sflag = OFF ;
}

/* SPI interrupt routine */
ISR(SPI_STC_vect)
{
  /* get 1 byte from SPI Data Register */
  xspi = SPDR ;
  /* set trigger flag */
  sflag = ON ;
}

void loop (void)
{
  byte i ;
  byte tmp ;
  char msg[9] ;
  /* flag handling */
  if ( sflag == ON ) {
    /* clear flag */
    sflag = OFF ;
    /* */
    tmp = xspi ;
    rs_putchar( tmp ) ;
    rs_putchar( '\t' ) ;
    /* clear */
    for ( i = 0 ; i < 9 ; i++ ) { *(msg+i) = '\0' ; }
    /* separate */
    for ( i = 0 ; i < 8 ; i++ ) {
      /* get bit code */
      *(msg+i) = (tmp >> (7-i)) & ON ;
      /* convert ASCII */
      *(msg+i) += 0x30 ;
    }
    /* show */
    rs_puts( msg ) ;
    crlf() ;
  }
}

 スレーブ側Arduinoに接続したPCのTeraTermの
 画面は、以下のようになりました。



 文字をそのままASCIIコードで表示し、2進数表示に
 なっているので、間違いなくSPIによるデータ転送で
 あるとわかります。

 マスターからスレーブにデータ転送するだけで充分な時も
 ありますが、スレーブからデータを返すことが必要な場面
 もあります。その場合の処理は、別ページで扱います。


目次

inserted by FC2 system