目次

LEDマトリクス

 micro:bitには、5x5のLEDマトリクスがあり
 表示器として、面白い動作をさせられます。

 LEDマトリクスのエミュレーションをしてみたいと
 半田付けました。

top



bottom



 回路図は、以下。



 動かし方を考えます。

 5x5のLEDマトリクスなので、ポートB、ポートCを
 利用して、行の点灯パターンをポートBで扱い、列の
 指定にはポートCを利用。

 OUT_1からOUT_5は、ポートBに接続、OUT_6からOUT_10は
 ポートCへ接続します。
 制御信号は、正論理で利用。

 この仕様で、LEDを扱うコードは、以下にしました。

    /* blank */
    bport = 0x00 ;
    /* display */
    if ( tflag == ON ) { bport = *(xpat+state) ; }
    /* impress */
    PORTB = bport ;
    /* scan */
    PORTC = 1 << state ;
    /* update */
    state++ ;
    state %= 5 ;

 通常は、行に消灯指定のパターンを与えて
 列の制御信号を、そのまま出力。

 常に動いていて、全LEDが消灯しているように
 見えるので、ダイナミックストップと言えます。

 パターンは、5バイトの配列xpatに入れ
 どの行を扱うのかを変数stateで選択可能
 にしておきます。

 フリッカが起きないように、行は5msごとに
 点灯パターンを変えます。この周期の生成
 には、タイマー割込みを活用。

 タイマー割込みのイベント通知を入れると
 次のコードになります。

  if ( eflag == ON ) {
    /* clear flag */
    eflag = OFF ;
    /* blank */
    bport = 0x00 ;
    /* display */
    if ( tflag == ON ) { bport = *(xpat+state) ; }
    /* impress */
    PORTB = bport ;
    /* scan */
    PORTC = 1 << state ;
    /* update */
    state++ ;
    state %= 5 ;
  }

 Arduinoのloopの中に、上のコードを入れると
 ダイナミック点灯による、LEDマトリクス表示
 が完成。

 タイマー割込みは、MsTimer2を利用して
 お手軽に対応します。

 関係する手続きと関数は、以下。

  /* 5ms period */
  MsTimer2::set(5,update_trigger);
  /* enable */ 
  MsTimer2::start();

void update_trigger()
{
  eflag = ON ;
}

 点灯パターンを変更したり、確認したいので
 コマンドインタプリタを入れて対応します。

 コマンドは、以下。

 コマンドは、端末ソフトを利用してPCから
 与えるとして、次の仕様にしておきます。

 データ転送速度 9600bps
 データ長     8ビット
 パリティ    なし
 フロー制御   なし

 通信関係仕様を規定したので、必要な関数を定義。

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

 シリアル処理で、write関数を多用するとコード
 サイズが増えるので、rs_putcharにまとめてます。
 これを使って、文字列表示と改行を定義。

 文字列処理ができると、コマンドのヘルプを実現
 する関数を作成できます。

void show_help()
{
  rs_puts("? help")        ; crlf();
  rs_puts("G enable")      ; crlf();
  rs_puts("g disable")     ; crlf();
  rs_puts("P set pattern") ; crlf();
  rs_puts("p show pattern") ; crlf();
}

 コマンドインタプリタは、受信割込みで
 PCからの送信情報を格納し、受信バッファ
 に、全データが入ったことをイベント通知
 フラグで知らせて貰うようにします。

 割込み処理は、次のように定義。

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

 イベント通知フラグは、uflagとしておきます。
 また、受信バッファは、sbufという名で配列を
 用意。

 ここまで準備したなら、コマンドインタプリタを
 書いていきます。

  if ( uflag == ON ) {
    /* clear flag */
    uflag = OFF ;
    /* new line */
    crlf() ;
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help(); }
    if ( cmd == 'G' ) { tflag = ON ; }
    if ( cmd == 'g' ) { tflag = OFF; }
    if ( cmd == 'P' ) {
      /* get index */
      j = *(sbuf+1) - '0' ;
      /* clear */
      tmp = 0 ;
      /* get pattern */
      for ( i = 0 ; i < 5 ; i++ ) {
        tmp <<= 1 ;
        tmp |= *(sbuf+2+i) - '0' ;
      }
      /* store */
      *(xpat+j) = tmp ;
    }
    if ( cmd == 'p' ) {
      show_byte( *(xpat+0) ); crlf();
      show_byte( *(xpat+1) ); crlf();
      show_byte( *(xpat+2) ); crlf();
      show_byte( *(xpat+3) ); crlf();
      show_byte( *(xpat+4) ); crlf();
    }
  }

 コマンドPでは、行番号を0から4で指定した後に
 点灯パターンを2進数で与えるとしました。

 コマンドpでは、専用関数で1行のパターンを表示。
 このパターン表示は、次のように定義。

void show_byte(char x)
{
  char xtmp[6] ;
  *(xtmp+5) = '\0' ;
  *(xtmp+4) = (x & 1) + '0' ; x >>= 1 ;
  *(xtmp+3) = (x & 1) + '0' ; x >>= 1 ;
  *(xtmp+2) = (x & 1) + '0' ; x >>= 1 ;
  *(xtmp+1) = (x & 1) + '0' ; x >>= 1 ;
  *(xtmp+0) = (x & 1) + '0' ;
  rs_puts( xtmp );
}

 ここまでに定義した内容をまとめると、以下。

/*
  apro.ino

  Pin assignment

  PORTB
    PB5 (output) LED
    PB4 (output) pattern4
    PB3 (output) pattern3
    PB2 (output) pattern2
    PB1 (output) pattern1
    PB0 (output) pattern0

  PORTC
    PC5 (output)
    PC4 (output) select4
    PC3 (output) select3
    PC2 (output) select2
    PC1 (output) select1
    PC0 (output) select0

  PORTD
    PD7 (output)
    PD6 (output)
    PD5 (output)
    PD4 (output)
    PD3 (output)
    PD2 (output)
    PD1 (output)TxD
    PD0 (input) RxD

*/
#include <MsTimer2.h>

#define OFF 0
#define ON  OFF+1

#define MASK0F 0x0f
#define MASKF0 0xf0
#define MASKFF 0xff
#define MASK7F 0x7f

/* function prototype */
void update_trigger();
void show_help();
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void show_byte(char x);

/* variables */
boolean tflag ;
boolean eflag ;
boolean uflag ;

byte xpat[5] ;
byte state ;
byte sbuf[16] ;
byte sindex ;

byte cmd ;
byte i ;
byte j ;
byte tmp ;
byte bport ;

void setup()
{
  /* initialize serial */
  Serial.begin(9600);
  sindex = 0 ;
  rs_puts("Hello !");
  crlf();
  /* clear flags */
  tflag = OFF ;
  eflag = OFF ;
  uflag = OFF ;
  /* initialize port values */
  PORTB = 0x00 ;
  PORTC = 0x00 ;
  PORTD = 0x00 ;
  /* initialize port direction */
  DDRB = 0xff ;
  DDRC = 0xff ;
  DDRD = 0xfe ;
  /* variables */
  state = 0 ;
  *(xpat+0) = 16 ;
  *(xpat+1) = 1 ;
  *(xpat+2) = 2 ;
  *(xpat+3) = 4 ;
  *(xpat+4) = 8 ;
  /* 5ms period */
  MsTimer2::set(5,update_trigger);
  /* enable */ 
  MsTimer2::start();
}

void loop()
{
  /* command interpreter */
  if ( uflag == ON ) {
    /* clear flag */
    uflag = OFF ;
    /* new line */
    crlf() ;
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help(); }
    if ( cmd == 'G' ) { tflag = ON ; }
    if ( cmd == 'g' ) { tflag = OFF; }
    if ( cmd == 'P' ) {
      /* get index */
      j = *(sbuf+1) - '0' ;
      /* clear */
      tmp = 0 ;
      /* get pattern */
      for ( i = 0 ; i < 5 ; i++ ) {
        tmp <<= 1 ;
        tmp |= *(sbuf+2+i) - '0' ;
      }
      /* store */
      *(xpat+j) = tmp ;
    }
    if ( cmd == 'p' ) {
      show_byte( *(xpat+0) ); crlf();
      show_byte( *(xpat+1) ); crlf();
      show_byte( *(xpat+2) ); crlf();
      show_byte( *(xpat+3) ); crlf();
      show_byte( *(xpat+4) ); crlf();
    }
  }
  /* handling */
  if ( eflag == ON ) {
    /* clear flag */
    eflag = OFF ;
    /* blank */
    bport = 0x00 ;
    /* display */
    if ( tflag == ON ) { bport = *(xpat+state) ; }
    /* impress */
    PORTB = bport ;
    /* scan */
    PORTC = 1 << state ;
    /* update */
    state++ ;
    state %= 5 ;
  }
}

void update_trigger()
{
  eflag = ON ;
}

void show_help()
{
  rs_puts("? help")        ; crlf();
  rs_puts("G enable")      ; crlf();
  rs_puts("g disable")     ; crlf();
  rs_puts("P set pattern") ; crlf();
  rs_puts("p show pattern") ; crlf();
}

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_byte(char x)
{
  char xtmp[6] ;
  *(xtmp+5) = '\0' ;
  *(xtmp+4) = (x & 1) + '0' ; x >>= 1 ;
  *(xtmp+3) = (x & 1) + '0' ; x >>= 1 ;
  *(xtmp+2) = (x & 1) + '0' ; x >>= 1 ;
  *(xtmp+1) = (x & 1) + '0' ; x >>= 1 ;
  *(xtmp+0) = (x & 1) + '0' ;
  rs_puts( xtmp );
}

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

 Arduino基板とLEDマトリクス基板を接続すると
 次のようになります。



 TeraTermを使って、コマンドインタプリタのチェックを
 すると、次のようになります。



 コマンドG、gを利用して、ダイナミック点灯で
 表示されるのを確認できました。



 7と表示されているのが、お分かり頂けるだろうか。


目次

inserted by FC2 system