SSブログ

16x8マトリックスLED 第二弾 その4 [Arduino]

Arduino 1.0をそろそろ使ってみようかと思います。

arduino1_0.png

Arduino Wiki
http://www.musashinodenpa.com/wiki/

こちに Arduino 0023 からの変更点がいくつか記載してあります。
シリアル通信の関係他いくつか変更点があるようです。

また電光掲示板をつくってみました。
今回は、半角英数カナにも対応してみたいと思います。

以前つくったEEPROMのアドオンを使います。
eeprom_test_pic.jpg

(参照) 初めての I2C EEPROM
http://hello-world.blog.so-net.ne.jp/2011-05-22

・8x8ドット漢字フォントの恵梨沙フォントは下記のページから
ELISA Font Home Page by YAFO:
http://hp.vector.co.jp/authors/VA002310/

・4x8ドット半角フォントは、、
Mobile Computer Software Gallery
http://oohito.com/mcsg/archives/17
こちらのFontX2形式のファイルをそれぞれ使わせていただきました。

4x8ドット半角フォントには、JPNHN4X.FNT と 4DOT.FNT がありますが、4DOT.FNT がいいと思います。

前回と同じように、Tera Term から送信しますが、Arduino 1.0用のEEPROM書き込みスケッチは次のとおり。
// ★ Arduinoがシリアル受信したデータをI2C EEPROMに書き込むスケッチ
// ・8バイト毎にデータを書き込む (8で割り切れるデータサイズで)
// ・4800 baud まで (これを超えると書き込みが間に合わないと思われる)
// ・Arduino 1.0 用 (Wire.send → Wire.write)
#include <Wire.h> //I2C library

unsigned int addr=0; //first address
byte buf[8];

void setup() 
{
  Wire.begin(); // initialise the connection
  Serial.begin(4800);
  delay(10); //add a small delay
}

void loop() 
{
  if( Serial.available() >= 8 ){
    for(int i=0; i<8; i++) buf[i] = Serial.read();
    i2c_eeprom_write_page( 0x50, addr, buf, 8 );
    delay(10); //add a small delay
    addr += 8; 
  }
}

void i2c_eeprom_write_page( int deviceaddress, unsigned int eeaddresspage, byte* data, byte length ) {
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddresspage >> 8)); // MSB
  Wire.write((int)(eeaddresspage & 0xFF)); // LSB
  for (byte c = 0; c < length; c++)
    Wire.write(data[c]);
    Wire.endTransmission();
}

シリアルポートを 4800 baud で、バイナリーモードで
ELISA100.FNT、4DOT.FNT の順に続けて転送していきます。

スケッチは8byte ごとの書き込みなので、ファイルの最後の部分が書き込めませんが、データも入っていないし、使わないので気にしないことにします。
書き込み終わったら念のためライトプロテクトをしておきます。

続いて、シリアルからのデータを漢字イメージに変換し、マトリックスLEDに表示するスケッチ。
// シリアルからのテキストを電光掲示板
// ・Arduino 1.0用(0023以下では使えません)
// ・EEPROMに "ELISA100.FNT", "4DOT.FNT"
#include <Wire.h> //I2C library

void setup() {
  Wire.begin(); // initialise the connection
  Serial.begin(9600);
}

void loop() { 
  int elisa, i, j;
  unsigned int fontadrs;
  byte sjisH, scrollText[256], buf[8], txdata, chrwidth, col, row;
    // ■ LEDをクリアー ■
  for( i = 0; i < 16; i++ ) Serial.write( (byte) 0 );
    // ■ シリアルからテキスト取得 ■
  while( !Serial.available() );            // テキストが届くまで待機
  i = 0;
  do {
    if( i < sizeof(scrollText) - 5 )        // 配列に入りきるなら、、
      scrollText[i++] = Serial.read();     //   データを格納
    else  Serial.read();                   //   オーバーするなら破棄 (Arduino1.0前は .flush()でも )
    if( Serial.available() ) continue;     // データがあれば繰り返す
    delay(10);                              // 10msec待っても、
  } while( Serial.available() );           // データがなければ終了
  for( j = i; j < i+4; j++ ) scrollText[j] = ' ';      // スペース4個と
  scrollText[j] = '\0';                                // 終了コード追加
    // ■ 文字をイメージに変換しながらシリアル送信 ■
  for( i = 0; !Serial.available(); ) {     // シリアルから入力があるまで繰り返す
    sjisH = scrollText[ i++ ];              // 1byte 読み込む
    if((sjisH>=0x81 && sjisH<=0x9f) || (sjisH>=0xe0 && sjisH<=0xef)) {  // S-JISの上位1byteなら漢字
      elisa = sjisToElisa( ((unsigned int)sjisH << 8) + scrollText[ i++ ] );
      if( elisa == -1 ) elisa = 0;          // 対応コードがなければ全角スペース
      fontadrs = elisa * 8;                 // 1文字8byteなので、文字順に8をかける
      chrwidth = 8;                         // 全角は幅8ドット
    } else {		                    // 半角ANKなら、
      fontadrs = 55016 + 17 + sjisH * 8;    // Elisaサイズ+FontX2のヘッダ
      chrwidth = 4;                         // 半角は幅4ドット
    }
    i2c_eeprom_read_buffer( 0x50, fontadrs, buf, 8 );    // フォントデータ取得
    for( col = 0; col < chrwidth; col++ ) { // フォントを列ごとに取得
      for( row = 0; row < 8; row++ ) bitWrite( txdata, row , bitRead( buf[ row ], 7 - col ) );
      Serial.write( txdata );              // 文字パターンを1列ごと送信
      delay( 100 );                         // スクロールのスピード調節
    }
    Serial.write( (byte) 0 );              // 1文字毎に1列あける
    delay( 100 );                           // スクロールのスピード調節
    if( !scrollText[i] ) i = 0;             // 終わりまできたら始めに戻る
  }
}

void i2c_eeprom_read_buffer( int deviceaddress, unsigned int eeaddress, byte *buffer, int length ) {
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8)); // MSB       (Arduino1.0前は .send() )
  Wire.write((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();
  Wire.requestFrom(deviceaddress,length);
  for (int c = 0; c < length; c++ ) if (Wire.available()) buffer[c] = Wire.read();
}

signed int sjisToElisa(unsigned int sjiscode) {    // シフトJISからELISAフォント上の位置に
  byte jisH, jisL;
  int sp, jiscode;
    // まず、シフトJISをJISに変換
  jisH = sjiscode / 256;    // 上位バイト
  jisL = sjiscode % 256;    // 下位バイト
  jisH = (jisH - ((jisH <= 0x9F) ? 0x71:0xB1)) * 2 + 1;
  if( jisL > 0x7F ) jisL--;
  if( jisL >= 0x9E ) jisH++;
  jisL -= ((jisL >= 0x9E) ? 0x7D:0x1F);
  jiscode = (jisH << 8) + jisL;
    // JISから ELISAフォントでの文字位置を計算
  if     (jiscode>=0x2121 && jiscode<=0x222e) sp =   0;  // 一般記号
  else if(jiscode>=0x223a && jiscode<=0x2241) sp =  11;  // 集合記号
  else if(jiscode>=0x224a && jiscode<=0x2250) sp =  19;  // 論理記号
  else if(jiscode>=0x225c && jiscode<=0x226a) sp =  30;  // 幾何記号
  else if(jiscode>=0x2272 && jiscode<=0x2279) sp =  37;  // 音楽記号
  else if(jiscode==0x227e)                    sp =  41;  // 丸記号
  else if(jiscode>=0x2330 && jiscode<=0x2339) sp =  56;  // 数字
  else if(jiscode>=0x2341 && jiscode<=0x235a) sp =  63;  // 英大文字
  else if(jiscode>=0x2361 && jiscode<=0x237a) sp =  69;  // 英小文字 
  else if(jiscode>=0x2421 && jiscode<=0x2473) sp =  73;  // ひらがな
  else if(jiscode>=0x2521 && jiscode<=0x2576) sp =  84;  // カタカナ
  else if(jiscode>=0x2621 && jiscode<=0x2638) sp =  92;  // ギリシャ大文字
  else if(jiscode>=0x2641 && jiscode<=0x2658) sp = 100;  // ギリシャ小文字
  else if(jiscode>=0x2721 && jiscode<=0x2741) sp = 138;  // キリル大文字
  else if(jiscode>=0x2751 && jiscode<=0x2771) sp = 153;  // キリル小文字
  else if(jiscode>=0x2821 && jiscode<=0x2840) sp = 166;  // 罫線
  else if(jiscode>=0x3021 && jiscode<=0x4f53) sp = 886;  // 第一水準漢字
  else if(jiscode>=0x5021 && jiscode<=0x7426) sp = 929;  // 第二水準漢字
  else return -1;              // 対応文字コードがなければ -1
  return (jiscode / 256 - 0x21) * 94 + (jiscode % 256) - 0x21 - sp;
}

テキストデータは随時イメージに変換することにして、RAMをあんまり使わないようにしてみました。

eleboard2.jpg
ジャンパピンは、Arduinoからのシリアル出力(TX)を、マトリックスLEDのTiny2313の入力側につなぐようにします。
EEPROMのアドオンが載っていると不格好ですが、、。

シリアルモニターを9600baudにして、文字列を入力すると、LEDマトリックスに電光掲示板のように表示されます。

半角英数カナと全角漢字も全部OK!

16x8マトリックスLED 第二弾 その3 [Arduino]

前回はシリアルモニターからのデータ送信だったので、テキストの送信しかできず、思い通りのイメージをマトリックスLEDに送信することができませんでした。
パソコンからバイナリーデータを送るプログラムが必要になります。
どんな言語でつくろうか考えてみたのですが、 Arduino と相性がよさそうな Processing で作ってみることにしました。

processing.png

Processing.org
http://processing.org/

Arduino が基本C言語なのに対して、Java ベースです。
初めての Processing でしたが、なんとか形にはなるくらいのものは作れました。

import processing.serial.*;
Serial myPort;       
int[] matrixImage=new int[16];

void setup() {
  println(Serial.list());                // 表示されたポートの中から、
  String portName = Serial.list()[0];    // 適切なポートを指定する
  myPort = new Serial(this, portName, 9600);
  size(255, 159);
  textSize(12);
}

void draw() {
  int x, y;
  if(mousePressed && mouseX >= 0 && mouseX < 256 && mouseY >= 0 && mouseY < 128) {
     if(mouseButton == LEFT)  matrixImage[mouseX / 16] |=   1 << (mouseY / 16) ;   // bit set
     if(mouseButton == RIGHT) matrixImage[mouseX / 16] &= ~(1 << (mouseY / 16));   // bit clear
  }
  for(y=0; y < 8; y++) {
    for(x=0; x < 16; x++) {
      fill( (matrixImage[x] >> y) % 2 == 1 ? 204 : 32 );
      rect(x * 16, y * 16, 15, 15);
    }
  }
  fill( 32 );
  rect(0, 128, 255, 15);
  rect(0, 144, 255, 15);
  fill( 204 );
  text("1 line", 16, 141);
  text("16 lines", 16, 157);
}

void mouseClicked() {
  int i, x, len;
  if      (mouseY / 16 == 8) len =  1;
  else if (mouseY / 16 == 9) len = 16;
  else return;
  for(i=0; i < len; i++) {               // lenの分だけスライド
    myPort.write( matrixImage[0] );                          // データ送信
    for(x=1; x < 16; x++) matrixImage[x-1] = matrixImage[x];  // 一列スライド
    matrixImage[15] = 0;
  }
}

これを「Run」して、、

proc_matrix.png

こんな感じでお絵かきして、1ラインずつあるいは16ラインごと転送すると、、

proc_image.jpg

表示できました。

次は、Arduino からデータを送ってみたいと思います。


Processingをはじめよう (Make: PROJECTS)

Processingをはじめよう (Make: PROJECTS)

  • 作者: Casey Reas
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2011/10/22
  • メディア: 単行本(ソフトカバー)


16x8マトリックスLED 第二弾 その2 [Arduino]

ATtiny2313でシリアル入力をして、それをマトリックスLEDに表示していくスケッチです。
よく分からないながらも、いろんなサイトを参考にして、何とか形になりました。

new_matrix_on.jpg

こんな感じでワイヤーを4本つなぎ、シリアル入力選択用のジャンパを外しておきます。
そしてATtiny2313 (Int. 8MHz) に、次のスケッチを書き込みます。

// 電光掲示板(シリアル受信, ATtiny2313):シリアルでデータを受信し、受信されるごとにスライドしていく
#define  BAUD  9600    // ボーレート
  // ATtiny2313 のピンとマトリックスLEDの対応配列(ポート PD0 なら 0xD0 ということにした)
const byte pin_matrixCol[8] = { 0xB4, 0xA1, 0xA0, 0xB1, 0xD3, 0xB2, 0xB6, 0xB7};  // Matrxの列のポート
const byte pin_matrixRow[8] = { 0xB0, 0xB5, 0xD5, 0xB3, 0xD6, 0xD4, 0xD1, 0xD2};  // Matrxの行のポート
byte matrixImage[16];     // イメージの配列
byte matrixIndex = 0;     // イメージの表示開始位置 

void setup() {
  UBRRH = (F_CPU / 16 / BAUD - 1) >> 8;
  UBRRL = (F_CPU / 16 / BAUD - 1);
  UCSRB = (1 << RXEN) | (0 << TXEN) | (1 << RXCIE);    // 受信許可,送信不可,割り込み許可
  UCSRC = (0 << USBS) | (3 << UCSZ0);                  // stopbit 1bit , 8bit送信
  sei();
}

void loop() {
  byte x, y;
  for( x = 0; x < 8; x++ ) {
    for( y = 0; y < 8; y++ ) {
      if ( matrixImage[ ( x     + matrixIndex ) % 16 ] & (1 << y) ) setPixelTn2313( pin_matrixCol[x], pin_matrixRow[y]);
      else setMatrixAllOffTn2313();
      if ( matrixImage[ ( x + 8 + matrixIndex ) % 16 ] & (1 << y) ) setPixelTn2313( pin_matrixRow[y], pin_matrixCol[x]);
      else setMatrixAllOffTn2313();
    }
  }
}

ISR(USART_RX_vect) {     // 割り込みによる受信
  matrixImage[matrixIndex++] = UDR;
  matrixIndex %= 16;
}

void setMatrixAllOffTn2313() {    // Tiny2313をすべて入力に(ハイ・インピーダンスに)
  DDRA = B00000000;  PORTA  = B00000000;
  DDRB = B00000000;  PORTB  = B00000000;
  DDRD = B00000000;  PORTD &= B00000001;  // PD0 は RXD (シリアル受信用なので触らず)
}

void setPixelTn2313(byte pin_H, byte pin_L) {
  setMatrixAllOffTn2313();
  switch (pin_H & 0xF0) {
    case 0xA0: DDRA  = 1 << (pin_H & 0x0F);  PORTA  = 1 << (pin_H & 0x0F);  break;
    case 0xB0: DDRB  = 1 << (pin_H & 0x0F);  PORTB  = 1 << (pin_H & 0x0F);  break;
    case 0xD0: DDRD  = 1 << (pin_H & 0x0F);  PORTD |= 1 << (pin_H & 0x0F);  break;
  }
  switch (pin_L & 0xF0) {
    case 0xA0: DDRA |= 1 << (pin_L & 0x0F);  break;
    case 0xB0: DDRB |= 1 << (pin_L & 0x0F);  break;
    case 0xD0: DDRD |= 1 << (pin_L & 0x0F);  break;
  }  
}

ATtiny2313 に書き込みが終わったら、ワイヤーをはずして、ジャンパーピンをArduinoの0番ピンからの入力にします(FT232RLからのデータを直接受け取る)。

そしたら、シリアルモニターでボーレートを合わせ(とりあえず9600にしてみた)、なにか適当に入力するとその文字が2進数表記で表示されるようになりました。

16x8マトリックスLED 第二弾 その1 [Arduino]

あけましておめでとうございます。

昨年末には、Arduino 1.0 が登場したようですが、、
http://arduino.cc/en/Main/Software

ATtinyを使った開発を続けたいので、いまだに Arduino 0021 を使っています。

今回もATtiny2313を使います。
以前に 8x8 マトリックスLEDを2個使った電光掲示板を作成しましたが、これをATtiny2313でやってみました。

前回苦労した配線にすこしアイデアを加えてみました。

aitendo@shopping~楽しい電子工作を提案する
http://www.aitendo.co.jp/

こちらでは、同じサイズ、色でアノードコモンとカソードコモンをペアでそろえることができるタイプがあります。
今回は一番お値打ちな、0.8インチ角、赤でそろえてみました。
ペアで使用すると、それぞれのLEDの同じピン同士を接続するだけでよく、配線が見やすくなるという算段です。

ATtiny2313は足の配置がよく、マトリックスLEDを使うのに非常に適した感じです。I/Oピンは17本あり残りの1本はデータの入力に使います。残念なことにシリアル入力ピンを残したかったので、すこしだけ配線が遠回りになりました。
電源の確保や、ATtiny2313自体のプログラミング、今後のシリアル通信のことも考え、シールド仕立てにしました。

new_matrix_fig.png
new_matrix_front.jpg
new_matrix_back.jpg

LEDマトリックスのソケットは、ピンソケットではゆるゆるになるので、IC用の丸ピンソケットにしました。
またATtiny2313は、高さや、取り外しの問題もあるので直付けにしました。
今後のシリアル入力については、ArduinoについているFT232RLからの出力をATtiny2313に入力するか、ATmega328からの出力をATtiny2313に入力するかの選択用のジャンパピンもつけました。
Arduinoのデジタル10~13の真横にATtiny2313のプログラミングのためのソケットをつけました。プログラミング時には、まず、ArduinoにArduinoISPのスケッチを描き込み、ライターにします。続いて、シールドのデジタル10~13ピンをそれぞれ真横のソケットにワイヤーで接続します。

new_matrix_on.jpg
いつもの、、

Areduino IDEで ATtiny/ATmega 開発環境を作る
http://www.geocities.jp/arduino_diecimila/use/arduino_dev.html

の通りに、ATtiny2313に書き込みを行います。
サンプルスケッチは次の通りです。
// 電光掲示板(心電図っぽいもの)
  // ATtiny2313 のピンとマトリックスLEDの対応配列(0xD0 なら ポートはPD0ということにした)
    // ATtiny2313の足の配置からは、PD0をマトリックスLEDの1番ピンにするといいけど
    // PD0 をシリアル受信用に使う予定のため、PD6をマトリックスLEDの1番ピンにしてみた。
const byte pin_matrixCol[8] = { 0xB4, 0xA1, 0xA0, 0xB1, 0xD3, 0xB2, 0xB6, 0xB7};  // Matrxの列のポート
const byte pin_matrixRow[8] = { 0xB0, 0xB5, 0xD5, 0xB3, 0xD6, 0xD4, 0xD1, 0xD2};  // Matrxの行のポート
byte matrixImage[16] = {        // イメージの配列(心電図)
  0b00100000,  0b00010000,  0b00100000,  0b01000000,  0b00111100,  0b00001111,  0b01110000,  0b10000000,
  0b01100000,  0b00100000,  0b00010000,  0b00100000,  0b00100000,  0b00100000,  0b00100000,  0b00100000};
byte matrixIndex;               // イメージの表示開始位置
byte pulseRate = 60;            // 脈拍数
long previousMillis = 0;

void setup() {
}

void loop() {
  byte x, y;
  for( x = 0; x < 8; x++ ) {
    for( y = 0; y < 8; y++ ) {
      if ( matrixImage[ ( x     + matrixIndex ) % 16 ] & (1 << y) ) setPixelTn2313( pin_matrixCol[x], pin_matrixRow[y]);
      else setMatrixAllOffTn2313();
      if ( matrixImage[ ( x + 8 + matrixIndex ) % 16 ] & (1 << y) ) setPixelTn2313( pin_matrixRow[y], pin_matrixCol[x]);
      else setMatrixAllOffTn2313();
    }
  }
  setMatrixAllOffTn2313();
  unsigned long currentMillis = millis();
  if(currentMillis - previousMillis > 60000 / pulseRate / 16) {
    previousMillis = currentMillis;
    matrixIndex++;
    matrixIndex %= 16;
  }
}

void setMatrixAllOffTn2313() {    // Tiny2313をすべて入力に(ハイ・インピーダンスに)
  DDRA = B00000000;  PORTA  = B00000000;
  DDRB = B00000000;  PORTB  = B00000000;
  DDRD = B00000000;  PORTD &= B00000001;  // PD0 は RXD (シリアル受信用なので触らず)
}

void setPixelTn2313(byte pin_H, byte pin_L) {  // High と Low のピンを指定することで1ドット点灯
  setMatrixAllOffTn2313();
  switch (pin_H & 0xF0) {
    case 0xA0: DDRA  = 1 << (pin_H & 0x0F);  PORTA  = 1 << (pin_H & 0x0F);  break;
    case 0xB0: DDRB  = 1 << (pin_H & 0x0F);  PORTB  = 1 << (pin_H & 0x0F);  break;
    case 0xD0: DDRD  = 1 << (pin_H & 0x0F);  PORTD |= 1 << (pin_H & 0x0F);  break;
  }
  switch (pin_L & 0xF0) {
    case 0xA0: DDRA |= 1 << (pin_L & 0x0F);  break;
    case 0xB0: DDRB |= 1 << (pin_L & 0x0F);  break;
    case 0xD0: DDRD |= 1 << (pin_L & 0x0F);  break;
  }  
}

高速化のためポート直接操作をしています。また、プログラムの簡便化+明るさの均一化のため、1ドットずつのダイナミック点灯としているのでやや暗めになります。暗くなるので、電流制限抵抗も不要(というか、これ以上暗くなると困る、、)。


しかし、配線をわかりやすくしたとはいえ、1.27mmピッチ基板の配線は2.54mmのものと比べてとっても大変でした。

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。