フォト
2017年5月
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
無料ブログはココログ

AVR

2016年9月 2日 (金)

DigisparkでUSB経由のシリアル通信が出来ない

以前動作確認しただけで使っていなかったDigisparkを使おう(赤外線送信したい)と思い、
Arduino IDE 1.6.11上に開発環境を構築しました。

Dscn5684

インストール等は公式サイトの下記のドキュメントに従って行いました。
  Connecting and Programming Your Digispark

私の環境ではCRCエラーが発生してインストール失敗しました。
インストール先の\staging\packages\の次の2つのファイルを削除するとうまくいきました。
  digistump-avr-1.6.7.zip
  micronucleus-2.0a4-win.zip

  01

  インストール後、取りあえずLチカは問題なく動作しました。
  一見、インストールは問題ないようです。

  ところが一番やりたかった、USB経由のシリアル通信が出来ませんでした。

   03

   ドライバーが正常の動かないようです。
   ドラーバーの状態を見ると、一部のドライバがインストールに失敗していました。
 
   "ドライバーの署名なし"に引っかかっているのかと思い、Windows 10を署名無し
   でインストールする状態にしてインストールしてみたのですが、今度はDigisparkを
   接続する度にブルー画面でOSが落ちていまうという悲惨な状況になってしましました。

    Dscn5686

   ドライバーの作りが悪いんでしょうねぇ。普通ここまでの状況にはならないと思うけど..

   Digisparkのファームウェアとドライバー類が古い可能性もあるので、
   下記のサイトから一式ダウンロードしてファームウェアの書き換えと、ドライバーの
   インストールを行いました。
     ・micronucleus/micronucleus  https://github.com/micronucleus/micronucleus

    ファームウェアの更新はちょっと面倒でした。
    avrdudeコマンドを使って、USBtinyによる書き込みを実施
   
     ピン接続
        No.1ピン : MOSI
        No.2ピン : MISO       
        No.3ピン : CLK
        No.6ピン : RESET 
      
 
      コマンド引数
         avrdude -c usbtiny -p t85 -U flash:w:t85_default.hex
       
     Dscn5688
    
    ファームウェアは問題なく動作している(書き込み、Lチカ問題なく動作)ものの、
    シリアル通信はやっぱりダメでした。

    書き込みは出来ているのでUSB機能のうちHIDは問題ないようです。
   
    Digisparkのフォーラムを見てもこの問題は多数発生しておりかつ解決策も
    無いようです。動いている人もいるみだいですが..   
    元々、V-USB を使った強引なUSB実装なので、これ以上の深追いは止めて
    諦めることにします。

    HIDデバイスを作る際には利用することにしましょう。
    (例:マウス、キーボード、ジョイスティック的なもの)

    価格も大きさも2倍($3.2くらい)になりますが、
    Digisparkを使うよりも Arduino Microのクローン製品を使った方が良さそうです。
    Arduino IDEも標準サポートしていますし..


    左: Digispark 、右:Arduino Microクローン製品(ただし、リセットボタン、ICSP端子なし)

     Dscn5689

2016年4月29日 (金)

Arduino(あちゃんでいいの)によるI2C EEPROMのエミュレーション

Arduino(あちゃんでいいの)によるI2C EEPROM エミュレーションをやってみました。

目的はIchigoJamとの連携です。
IchigoJamはプログラム保存デバイスとしてI2C EEPROMをサポートしています。
そのインタフェースを使って「通信をしてしまおう!」という目論見です。

05

IchigoJamからは容量 1kバイトの24系のI2C EEPROM として利用出来ます。
(実際はSRAM上に保存のため ROMではないですが.. 最終的にはSDカードに保存したい)

Arduino の I2Cライブラリ(Wire) のスレーブ実装機能が今一問題ありで苦労しましたが、
何とか出来ました。

接続はこんな感じ。ちなみにArduino として aitendo「あちゃんでいいの」を使っているのは、
3.3V 16MHz稼働で利用出来るからです。

Dscn5124

IchigoJamからはプログラム保存番号100として利用出来ます。
実際に動かしてみるとこんな感じです。

02

起動後、LOAD 0 命令で本体保存プログラムをロードし、SAVE 100 でエミュレーションした
プログラム保存番号100 に保存します。 認識されてちゃん動作しています。

次に FILES 100 命令でファイルリストの表示。100番もちゃんと表示出来ました。
ここでプログラムを消して、プログラム保存番号100 をロードします。

03

ちゃんとロードしました。内容も確認。

04

問題無くロード出来ています。

Arduino側でも保存状態を確認出来ます。

06

メニュー選択でプログラムを表示すると、同じものが保存されているのが分かります。

IchigoJam固有フォントで表示出来ない文字はダンプ表示で確認します。

07

容量1kバイトでは全然実用でないですが、SDカードに保存なんか出来るように
したいと思います。

Arduioのスケッチについて

プログラム的にはArduinoをI2CスレーブにしてEEPROMをエミュレーションしています。
利用しているI2Cライブラリ(Wire)をI2Cスレーブとして使う場合、微妙に機能不足で
若干問題ありです。かなりハマりました。

1)受信バッファの問題
まず、受信バッファが32バイトしかなくEEPROMのような一回のトランザクションで
大量のデータ通信を行うスレーブの実装は無理です。

IchigoJamでは一回のトランザクションで34バイトの通信を行うためギリギリでOUTでした。
仕方がなく、ライブラリのバッファサイズ指定部を32にから64に変更しました。
(2ヶ所修正、詳細はソースに記載)

汎用的なEEPROM機能をエミュレーション出来ず、1回あたり64バイトまでの転送という
制約が付きます。

2)スレーブ時のwrite()関数のマスター応答が取得できない問題
また、EEPROMでは可変長データリードをサポートするのですが、この機能を
Arduinoで実装するにあたり、マスターにデータを渡すのにwrite()関数を使うのですが、
この関数は渡した応答(ACK/NACK)を取得することが出来ません。
ちょっと困った仕様です。

下図はそのパケットの仕様でマスターが次のデータの受信が不要の場合、NACKで応答します。

08

この対応としては、write()関数で多めにデータを送信することで対処出来ることが分かりました。
ライブラリ内部ではマスターからNACKを受け取ると送信はそこで終了するようです。
(ロジックアナライザで確認にて確認)

09

プログラムではwrite()関数で常に64バイトを送信していますが、実際にはマスターが
NACKを返した時点で送信は終了します。ちょっと美しくないですね。

ここでwrite()関数が実際に送信したバイト数を返してくれればよいのですが64が帰って
きます。関数のソースを見ると引数の送信バイト数をそのまま復帰値として返すと
いう手抜きをやってくれていました。

何処にもそんな仕様が書かれていないようですし.. まあ、この対処で良しとしましょう。

スケッチ ダウンロード Ichigoepprom.zip (2.0K)

//
// I2C EEPROMエミュレーション調査
// 2016/04/26,4/30 Tamakichi
//
// 注意:このスケッチの動作にはWireライブラリの修正が必要
//  修正箇所 
//   \libraries\Wire\utility\twi.h #define TWI_BUFFER_LENGTH 32 => #define TWI_BUFFER_LENGTH 64
//   \libraries\Wire\Wire.h        #define BUFFER_LENGTH 32 => #define BUFFER_LENGTH 64
//

#include <Wire.h>

#define DEVICE_ADDRESS 0x50  // AT24C1024B I2Cデバイスアドレス 
#define MYDEBUG 0            // 0:デバッグ表示on(=1)/off(=0)
 
 uint8_t buf[1088]; // プログラム保存領域 
 uint16_t adr;      // EEPROM アクセスアドレス

// I2Cマスタからのデータ受信ハンドラ
void receiveEvent(int len) {
  uint16_t d1,d2;
  int n = 0;
  
  // アドレス情報取得
  d1 = Wire.read();
  d2 = Wire.read(); 
  adr = (d1<<8) + d2;
  adr &= 0x3ff;

#if MYDEBUG == 1
  Serial.print("adr=");
  Serial.print(adr,HEX);
  Serial.write(' ');
  Serial.println(len,DEC);
#endif

  // 送信されたデータを受信(データがある場合) 
  while (Wire.available()){
    buf[adr++] = Wire.read();
    if (adr >= 1024) {
        adr= 0;
    }
  }
}

// I2Cマスタからの要求の処理
void requestEvent() {
  uint16_t n;
  uint8_t  sts;
  uint8_t  i;
  
#if MYDEBUG  == 1
  Serial.print("req ");
  Serial.println(adr,HEX);
#endif
  Wire.write(&buf[adr], 64);
}

//
// 保存プログラムのダンプ表示
//
void disp_prog() {
  uint16_t ptr = 0;  // 参照位置
  uint16_t nm,n1,n2; // 行番号
  uint8_t sz;        // 行サイズ
   Serial.println("Ichigojam program list");
  while (1) {
    n1 = buf[ptr++];
    n2 = buf[ptr++];
    nm = n1 + (n2<<8);
    if (!nm)
      break;
    sz = buf[ptr]; ptr++;
    Serial.print(nm,DEC); Serial.write(' ');
    for (uint8_t i= 0; i < sz; i++) 
      Serial.write(buf[ptr++]);
    Serial.println();
    ptr++;  
  }  
}

//
// 書込みデータのダンプリスト
// dt(in) : データ格納先頭アドレス
// n(in)  : 表示データ数
void dump(byte *dt, int n) {
  unsigned long sz;
  char buf[64];
  int clm = 0;
  byte data;
  byte sum;
  byte vsum[16];
  byte total =0;
  int saddr =0;
  int eaddr =n-1;
  sz = eaddr - saddr;
  
  Serial.println(F("----------------------------------------------------------"));
  for (int i=0;i<16;i++) vsum[i]=0;  
  for (unsigned long addr = saddr; addr <= eaddr; addr++) {
    data = dt[addr];
    if (clm == 0) {
      sum =0;
      sprintf(buf,"%05lx: ",addr);
      Serial.print(buf);
    }

    sum+=data;
    vsum[addr % 16]+=data;  
    sprintf(buf,"%02x ",data);
    Serial.print(buf);
    clm++;
    if (clm == 16) {
      sprintf(buf,"|%02x ",sum);
      Serial.print(buf);      
      Serial.println("");
      clm = 0;
    }    
  }
  Serial.println(F("----------------------------------------------------------"));
  Serial.print("       ");
  for (int i=0; i<16;i++) {
    total+=vsum[i];
    sprintf(buf,"%02x ",vsum[i]);
    Serial.print(buf);
  }
  sprintf(buf,"|%02x ",total);
  Serial.print(buf);      
  Serial.println("");
  Serial.println("");
}

// セットアップ
void setup() {
    adr = 0;
    Serial.begin(115200);
    Wire.begin(DEVICE_ADDRESS) ;        // I2Cの初期化、自アドレスをA0とする
    Wire.onRequest(requestEvent) ;    // I2Cコマンド要求割込み関数の登録
    Wire.onReceive(receiveEvent) ;      // I2Cデータ受信割込み関数の登録
    Serial.println("start");
}

// メイン処理
void loop() {
  byte c;
  Serial.print(F("Menu(1:disp 2:dump 3:init) ="));
  while ( !Serial.available() );
   c = Serial.read();
   if (c == '1') {
     Serial.println();
     disp_prog();
   } else if (c == '2') {
     Serial.println();
     dump(buf, 1024);
   } else if (c == '3') {
     Serial.println();
     adr = 0;
     for (uint16_t i = 0; i < 1088; i++)
       buf[i] =0;
   } else {
     Serial.println();
   } 
}

2015年3月29日 (日)

ATtiny13AでUART-I2Cブリッジを試作したが微妙

前回の続きです。
取りあえず、ATtiny13AにUART-I2Cブリッジを実装しました。

I2C接続LCDキャラクタ表示器にTeraTermでI2CコマンドをATtiny13Aに送ると

01

一応、ちゃんとコマンドを認識して文字が表示出来ました。

Dscn3699

ただし、非常に微妙です。

問題点等
・シリアル送信時に1バイト毎に2msecウェイトをかけないと
  受信データを取りこぼしてしまいます。
  メモリが足らないのでバッファリングやエラー処理が実装できません。
  2つの通信を同時に扱うのにSRAM 64バイトはちょっと辛かったです。

・I2Cがちょっと遅い。シリアルで115200出ない。
  なんか異常に遅いです。

まあ、今回は命令文を構想が練れたので良いとしましょう。
コマンドの形式前回からちょっと変更しました。

シリアル通信からI2C制御コマンドは次の通りです。
@a[C]    I2Cアドレス送信(スタートビットも送信,スレーブからack読み取りも行う)
@w[C]   1バイト送信(スレーブからack読み取りも行う)
@r[A]    1バイト受信(スレーブにack送信も行う)
@e        通信終了(ストップビット送信)

多分、これでI2C接続デバイスのの多くが制御ができると思います。
マクロ命令(上記の命令を組み合わせている)
@c[addr][cmd]   コマンド送信
@d[addr][data]   1バイトデータ送信 

さて、どうしましょうかね。
ATtiny13Aでこれ以上頑張っても、ちょっと無理そうです。
次はLPC810で実装しようと思います。

関連記事
LPC810を使ったUART-I2Cブリッジの改良版が出来ました (16/07/21)
LPC810を使ったUART-I2Cブリッジの機能拡張の検討中 (16/07/13)
UART-I2Cブリッジの使い方 - リアルタイムクロックの調査 (15/05/17)
Wiiヌンチャクの動作確認しました (15/05/17)
LPC810を使ったUART-I2Cブリッジがやっと出来ました (15/04/15)
ATtiny13AでUART-I2Cブリッジを試作したが微妙 (15/03/29) [この記事です]
ATtiny13AでUART-I2Cブリッジの製作 - まずその準備3 (15/03/26)
ATtiny13AでUART-I2Cブリッジの製作 - まずその準備2 (15/03/25)
ATtiny13AでUART-I2Cブリッジの製作 - まずその準備 (15/03/24)

公開・ダウンロードサイト
GitHub - LPC810 UART-I2Cブリッジモジュール

2015年3月26日 (木)

ATtiny13AでUART-I2Cブリッジの製作 - まずその準備3

前回の続きです。早速、arduno Uno上でプログラムを実装してみました。

Dscn3698

取りあえず、シリアル通信経由でI2Cのコマンドを投げて表示することが出来ました。
写真の"Hello World"を表示するのに次のコマンドを送信しています。

@s@wA0@i0@w80@i0@w48@i0@e
@s@wA0@i0@w80@i0@w65@i0@e
@s@wA0@i0@w80@i0@w6C@i0@e
@s@wA0@i0@w80@i0@w6C@i0@e
@s@wA0@i0@w80@i0@w6F@i0@e
@s@wA0@i0@w80@i0@w20@i0@e
@s@wA0@i0@w80@i0@w57@i0@e
@s@wA0@i0@w80@i0@w6F@i0@e
@s@wA0@i0@w80@i0@w72@i0@e
@s@wA0@i0@w80@i0@w6C@i0@e
@s@wA0@i0@w80@i0@w64@i0@e

一応、表示は出来たのですが、ATtiny13A用に作ったシリアル通信ライブリが
バッファリングしない仕様のため、TeraTermでデータを流した場合、
1文字毎に1msecのディレイを入れないとデータ抜けが発生しました。
IchigoJamで使うにはちょっと厳しいですね。

I2Cのプロトコルをここまで低レベルに分解する必要も無さそうなので
コマンド送信、データ送信、データ受信の基本パターンを用意して、
コマンド長を短くしてバッファリングするようにしてみます。
ただ、ATtiny13AのRAMが64バイトしかないんですよねぇ

関連記事
LPC810を使ったUART-I2Cブリッジの改良版が出来ました (16/07/21)
LPC810を使ったUART-I2Cブリッジの機能拡張の検討中 (16/07/13)
UART-I2Cブリッジの使い方 - リアルタイムクロックの調査 (15/05/17)
Wiiヌンチャクの動作確認しました (15/05/17)
LPC810を使ったUART-I2Cブリッジがやっと出来ました (15/04/15)
ATtiny13AでUART-I2Cブリッジを試作したが微妙 (15/03/29)
ATtiny13AでUART-I2Cブリッジの製作 - まずその準備3 (15/03/26) [この記事です]
ATtiny13AでUART-I2Cブリッジの製作 - まずその準備2 (15/03/25)
ATtiny13AでUART-I2Cブリッジの製作 - まずその準備 (15/03/24)

公開・ダウンロードサイト
GitHub - LPC810 UART-I2Cブリッジモジュール

2015年3月25日 (水)

ATtiny13AでUART-I2Cブリッジの製作 - まずその準備2

前回と続き。まだまだ構想レベルです。

まずは、自分がI2Cマスターで、I2Cバスに接続しているスレーブデバイスに
対してのコマンドを実装するものとします。

実際のシリアル通信によるコマンドは下記のものとします。
テキストコマンドとなります。

1)I2C初期化             @@
2)1ビット送信           @o[B]
3)1ビット受信           @i[M]
4)スタートビット送信    @s
5)ストップビット送信    @e
6)1バイト送信           @w[C]
7)1バイト受信           @r[A]
[B]は1ビット '0'or'1'
[M]はモード指定 '0':戻り値不要 '1':戻り値要求
[C]は16進数 '00' 〜 'FF'
[A]は1バイト受信時のデバイスへのAck送信の値

一方、私が使っている秋月電子で購入した、
I2C接続キャラクタLCDモジュール(16x2行)のI2Cのコマンドは次のような
フォーマットとなります。

これを定義したシリアル通信のコマンドに置き換えると、先頭から
@s                     :スタートビット送信
@wA0                 :アドレス'A0' 1バイト送信
@i0                    :Ackビット 1ビット受信(応答受信不要)
@w00 or  w80      :コントロールバイト1バイト送信 '00':コマンド送信  '80' データ送信
@i0                    :Ackビット 1ビット受信(応答受信不要)
@wNN                :コマンドまたはデータとして 1バイトデータ送信
@i0                    :Ackビット 1ビット受信(応答受信不要)
@e                     :ストップビット送信

となります。単純に"A"という文字を、液晶パネルに表示する場合は、
  @s@wA0@i0@w80@i0@w41@i0@e
となります。

1バイト送るのにこの長さはちょっと効率悪いですね。
本来8ビット送信毎にスレーブからちゃんと送れたのか応答Ackビットの値を受け取って
エラー対応を行う必要があるのですが、当面は未実装で行きます。
さて、I2Cアドレス+1バイト送信は良く使うので、
@dA041

とか短縮コマンドも用意したいと思います。
また、直前のI2Cアドレスに送信といった具合に
  @D41
短縮コマンドも用意したいと思います。

シリアル接続の相手がOFFになったりと異常対応も必要かもしれません。
取りあえず、ウオッチドックタイマを使ってセルフチェックして自己復旧(リセット)できる
ようにはしたいです。

今日はここまでとします。

関連記事
LPC810を使ったUART-I2Cブリッジの改良版が出来ました (16/07/21)
LPC810を使ったUART-I2Cブリッジの機能拡張の検討中 (16/07/13)
UART-I2Cブリッジの使い方 - リアルタイムクロックの調査 (15/05/17)
Wiiヌンチャクの動作確認しました (15/05/17)
LPC810を使ったUART-I2Cブリッジがやっと出来ました (15/04/15)
ATtiny13AでUART-I2Cブリッジを試作したが微妙 (15/03/29)
ATtiny13AでUART-I2Cブリッジの製作 - まずその準備3 (15/03/26)
ATtiny13AでUART-I2Cブリッジの製作 - まずその準備2 (15/03/25) [この記事です]
ATtiny13AでUART-I2Cブリッジの製作 - まずその準備 (15/03/24)

公開・ダウンロードサイト
GitHub - LPC810 UART-I2Cブリッジモジュール

2015年2月22日 (日)

arduinoで安価にテレビに映像を出力するその2

ビデオ出力ネタです。

以前arduinoで、arduino-tvoutという映像出力を試してみたのですが、
今回はNigel Battenさんがホームページ「Batsocks」にて公開している、
TellyMateを試してみました。

(注意: TellyMakeの商標は作成者Nigel Battenさんにあります。回路図やソースは
  公開されており、自由に利用できますが、TellyMakeを名乗るのは控えましょう)

01
(紹介ページ: http://www.batsocks.co.uk/products/Other/TellyMate.htm )

TellyMateを簡単に説明すると、Atmega8/88/168/328p を使った
ビデオ出力モジュール(ターミナル出力モジュール)です。
arduinoとシリアル通信でデータをやり取りして、キャラクタ表示をテレビに出力
できます。

02
   TellyMate TTL Kit購入ページ   http://www.batsocks.co.uk/products/Other/TellyMate%20TTL%20Kit.htm

Atmega8/88/168/328p系列のAVRマイコンに少々の部品で作成できます。
回路図およびソース、サンプルプログラムが公開されています。
更にキットの販売も行っています(上記の写真のリンクで購入できます)。



んで、早速ブレッドボード上に作成してみました。
水晶発振子、三端子レギュレータ等、部品揃えるのよりは
arduino pro mini(中華製) 1個使った方が速そうなので、arduinoのブートローダ部は
潰してUSBbaspでプログラムを書込みました。

追加パーツは、
  秋月電子RCAジャックDIP化キット 100円
  ダイオード 1N4148  2個 4円
で合計300円くらいで実装出来ます(ブレッドボード、ワイヤー類は除く)。 

書込みのためのバッチファイル、イメージファイルが用意されていて
回路ができれば、コマンドを一回実行すれば焼き上がります。

実際に組みあがった雰囲気はこんな感じです。
ビデオコンポーネント出力を小型液晶モニタに接続して映しています。

arduinoとモジュールをシリアル接続して、arduinoから制御して画面を表示します。
ダム端末ってやつですよね、これって。

Dscn3629

フォントが自由に定義出来るっぽいので、試しに美咲フォントを流用して
半角カタカタを流し込んでみました。ちゃんと表示できましたね。
(フォントデータの書込みはarduino側からできます)。
表示には倍角表示機能があり、大きく表示することもできます。

Dscn3630

シリアル接続なので、arduinoではなく、パソコンに繋げてTera Termで通信すれば
打った文字がそのまま表示するこもできます。
通常のコンソール画面的な制御(制御文字、エスケープ文字)で文字の表示制御
ができます。

表示はNTSCとPALに対応しています。接続してるモニタはNTSCよりもPALの方が
綺麗に映りました。テレビにつなげるとPALでは映らず、NTSCでのみ映りました。
スケッチはこんな感じです。ただ単に、シリアル接続して出力しているだけです。

/* Example Sketch for a TellyMate Shield */
/* Hello World */
#define CHAR_ESC '\x1B'
#define CHAR_DLE '\x10'
void setup()
{
    Serial.begin( 57600 ) ;
    Serial.write( CHAR_ESC ) ;
    Serial.write( 'E' ) ; // clear screen    
    
    for ( byte row = 0 ; row < 25 ; row++ ){
        tm_cursor_move( row , 0 ) ; // move the cursor the required row (any column would do)
        Serial.write( CHAR_ESC ) ;
        Serial.write( '_' ) ;
        Serial.write( ':') ; // use the specified fontbank
    }
    tm_cursor_move( 0 , 0 );

    Serial.println( "Hello World!" ) ; 
    Serial.println("\n");    
    Serial.write( CHAR_ESC ) ;
    Serial.write( '_' ) ;
    Serial.write( '4') ; // use the specified fontbank
    Serial.println("\xc8\xba\x20\xc6\x20\xba\xa5\xdd\xa5\xca\xde\xa5\xdd\xa5\xdc");   
    Serial.write( CHAR_ESC ) ;
    Serial.write( '_' ) ;
    Serial.write( '5') ; // use the specified fontbank
    Serial.println("\xc8\xba\x20\xc6\x20\xba\xa5\xdd\xa5\xca\xde\xa5\xdd\xa5\xdc");   
    Serial.write( CHAR_ESC ) ;
    Serial.write( '_' ) ;
    Serial.write( '0') ; // use the specified fontbank
    for (uint8_t i=32; i < 255;i++) {
      Serial.write( i ) ; 
    }
    Serial.println("\n");    
    Serial.println( "\xca\xdd\xb6\xb8\xb6\xc0\xb6\xc5") ; 
    Serial.println("\n");    

    //Serial.write( CHAR_ESC ) ;
    //Serial.write( 'Q' ) ;
}


void tm_cursor_move( uint8_t row , uint8_t col )
{ // <ESC>Yrc
  Serial.write( CHAR_ESC ) ;
  Serial.write( 'Y' ) ;
  Serial.write( 32 + row ) ;
  Serial.write( 32 + col ) ;
}
void loop()
{
    // do nothing!
}

意外といい感じに使えます。キャラクタ表示のみですが、
キャラクタを使って擬似的な76x75のグラフィック表示が可能です。

2015年2月 3日 (火)

arduinoで利用可能な漢字フォントROMの製作(2)

「arduinoで利用可能な漢字フォントROMの製作」の続きです。

SPI接続のフラッシュメモリ W25Q64BV に漢字を含む14種類のフォントデータを載せて
arduinoで汎用的に利用できる 漢字フォントROMを作成しました。
その作成手順とテストをまとめました。

まずは14種類のフォントファイルから、漢字フォントROM用データの作成です。
ツールを作成しました。需要は無いと思われますが公開します。

1.ツールによる漢字フォントROM用データの作成
    作成するデータは、14種類のフォント(全角、半角)を格納したバイナリファイルです。
    arduinoが採用している文字コード系 UTF-8に親和性のあるUTF-16のインデックスを
    付加したデータとなります。

    作成はすべてWinddows 8.1上で行いますが、perl(ActivePerl)が必要です。

    手順
    1)ツールをここ(fonts.zip)からダウンロードして下さい。
       解凍すると、\fontsフォルダ下に次のファイルが入っています。

      01

   2)この中にフォントデータは含まれていません。次のサイトから入手して、
     上記フォルダbdf内にファイルを配置して下さい。
     フォルダbdfには全部で14個のファイルを配置します。
    (1)8×8 ドット日本語フォント「美咲フォント」 X11 BDF 形式
      ファイル: misaki_bdf_2012-06-03.tar.gz

      解凍したファイルから、次のファイルを .\bdf フォルダに配置して下さい。
         misaki_4x8_iso8859.bdf
         misaki_gothic.bdf

    (2)/efont/ 日本語 ビットマップフォント集
       ファイル: japanese-bitmap-fonts-0.4.5.tar.bz2

       解凍したファイルから、次のファイルを .\bdf フォルダに配置して下さい。
         .\kappa20\10x20a.bdf
         .\kappa20\k20.bdf
         .\naga10\5x10a.bdf
         .\naga10\knj10.bdf
         .\shinonome12\shnm6x12a.bdf
         .\shinonome12\shnmk12.bdf
         .\shinonome14\shnm7x14a.bdf
         .\shinonome14\shnmk14.bdf
         .\shinonome16\shnm8x16a.bdf
         .\shinonome16\shnmk16.bdf
         .\x11\12x24a.bdf
         .\x11\jiskan24.bdf

  3)
バイナリフォントデータの作成
     コマンドプロンプトを開き、\fonts のディレクトリに移動します。
     makeall.bat を実行します。
     これにより、バイナリフォントデータを作成します。
     ほかに、幾つかのファイルを生成しますが、利用するファイルは次の2つです。
       .\utf_fontbin\u_font.bin  … 全種半角全角結合フォント
       .\u_table.h                   … Arduinoライブラリ exfont用フォントテーブル構造体ソース

     その他の生成されたファイルについては、添付のREADME.txtに説明を記述しています。

     これで、フォントデータの作成は終了です。
     一部のフォントでUTF16コード変換でエラーが発生しますが、無視して結構です。

  < 2016/04/18 追記、2016/04/19 修正

   バイナリファイル版を用意しました。

     下記からダウンロードして解凍してご利用下さい。
     ココログの制約から2つに分割しています。
       ダウンロード exfontbin.z01 (800.0K)
       ダウンロード exfontbin.zip (510.4K)

     解凍はwinrarを使い、上記2つのファイルを同じフォルダに入れて exfontbin.zipの方を
     解凍ツールで解凍すれば、解凍出来ます。   

        ライブラリではフォルダ内のu_font.bin、u_table.h を利用します。
     本ファイルを利用するのであれば ステップ1はスキップして下さい。
     ライセンスに関しては、添付のREADME.txtを参照して下さい。
     フォントに関してはフォント製作者に著作権があります。各フォントのライセンスに関する
     記載内容を確認の上、個人・非商用利用に限り配布可能と判断いたしました。
     本配布について問題があるようでしたらご指摘下さい。
   
2. フラッシュメモリへの書込み
   手持ちの CH341A Programmer を使って書込みます。
   前回は「Serprog/Arduino flasher」で行いましたが、これを使うと非常に楽ちんです。

   国内だとaitendoさんで購入出来ます: フラッシュ/SPIプログラマ [PRG341A]
   
     04

  Aliexpressebayなんかだと、400円くらいから購入出来きます。
   
    03

   操作は、利用するフラッシュメモリの種類を設定して、書き込むデータとして
   作成した u_font.bin を指定して読み込みます。 
   あとは、[auto]ボタンを押して、 書込みを開始します。

  Photo

  数分で書込みが完了します。
  CH341A programmer については、「CH341A programmerを試してみました」にも
  使い方等を書いています。
 
3. arduinoでの漢字ROMの利用
   作成した漢字ROMを使うには、次の物が必要です。
    ・arduinoで 漢字ROM (W25Q64BV) を利用する回路を作成する。
    ・漢字ROM を利用するスケッチ (プログラム)を作成する。

1)arduinoで 漢字ROM (W25Q64BV) を利用する回路の作成
  対象は、arduino Unoで利用する場合とします。
  W25Q64BVは、3.3V駆動のため、CD4050でレベル調整しています。
  回路図
01_2
  実際の結線接続イメージ

02Dscn3574

3.3Vで駆動できるArduinoでは直接、接続して利用します。



2)スケッチ(プログラム)の作成
  ライブラリを作成しました。ダウンロードして各自のarduino IDEに
  ライブラリとして登録して下さい(スケッチ保存場所ディレクトリのlibrariesに
  exfontsと、W25Q64を入れる)。

  ダウンロード: libraries.zip
  解凍すると、2つのフォルダ
   ・exfonts : 漢字利用ライブラリ :
   ・W25Q64: SPI接続 フラッシュメモリ W25Q64 利用ライブラリ
  が得られます。

  一点、修正作業があります。 
  「1.3)バイナリフォントデータの作成」で作成したu_table.hを、
  exfontsのフォルダに入れて下さい。既に同名のファイルが入っていますが、
  オリジナルのフォントデータの修正等により、内容が異なる場合があります。

3)サンプルプログラムの実行
    arduino IEDを起動して、
       メニュー [ファイル] - [スケッチの例] - [exfonts] - [exfonts_sample]
    を選択します。

   04 
 

#include <SPI.h>
#include <W25Q64.h>
#include <exfonts.h>

exfonts ft;  // フォント管理オブジェクト

// ビットパターン表示
// d: 8ビットパターンデータ
//
void bitdisp(uint8_t d) {
  for (byte i=0; i<8;i++) {
    if (d & 0x80>>i) 
      Serial.print("#");
    else
      Serial.print(".");
  }
}

// フォントデータの表示
// c(in) : フォントコード(UTF16コード)
//
void fontdisp(uint16_t c) {
  byte buf[MAXFONTLEN]; 
  ft.getFontData(buf,c);        // フォントデータの取得
  byte bn= ft.getRowLength();   // 1行当たりのバイト数取得
  
  Serial.print(ft.getWidth(),DEC);
  Serial.print("x");      
  Serial.print(ft.getHeight(),DEC);      
  Serial.print(" ");      
  Serial.println((unsigned short)c, HEX);  // UTF16コード表示

  for (byte i = 0; i < ft.getLength(); i += bn ) {
      for (byte j = 0; j < bn; j++) {
        bitdisp(buf[i+j]);
      }
      Serial.println("");
  }
  Serial.println("");
} 

void test(char* pUTF8) {
  int n;
  uint16_t pUTF16[100];
  n = exfonts::Utf8ToUtf16(pUTF16, pUTF8);  // UTF8からUTF16に変換する

  // 7種類フォントサイズで表示
  for (byte i=0; i < MAXSIZETYPE; i++) {
    ft.setFontSize(i);      // フォントサイズの設定
    for (byte j=0; j < n; j++) {
      fontdisp(pUTF16[j]);  // フォントパターンの表示
    }
  }
}

void test02() {
  
}

void setup() {
  Serial.begin(115200);
  ft.init();  // フォント管理の初期化
  Serial.println("exfont liblary");
  char *pUTF8 = "AAアイウエオ12A\¢£§¨¬°±´¶×÷埼玉さいたま";
  //char *pUTF8 = "1aA!#/";
  //char *pUTF8 = "あ";
  test(pUTF8);
}

void loop() {
}

サンプルでは、test()関数にて利用するフォントサイズを
  ft.setFontSize(i)
で指定し、パラメタで指定されたpUTF8に格納されている文字列を
シリアルモニタに表示しています。

実際にフォントデータの取得と表示を行うのは fontdisp()関数です。
  ft.getFontData(buf,c)
にて、指定した文字(UTF16)に該当するデータを取得します。

サンプルを実行すると、7種類の異なるフォントでシリアルモニタ上に
フォントパターンを文字で表示します。

05


作成したフォントの問題点
・半角カタカタは使えません。半角カタカナを取得しようとすると全角カタカナを返します。
・①②などの一部の特殊文字は使えません。

フォントROMのデータ配置について
u_table.h がフォントROM内の構造を示しています。

static FontInfo _finfo[EXFONTNUM] = {
	{ 0x000000, 0x00017E,  191,  8,  4,  8 }, // 0:u_4x8a.hex
	{ 0x000776, 0x000976,  256, 10,  5, 10 }, // 1:u_5x10a.hex
	{ 0x001376, 0x001576,  256, 12,  6, 12 }, // 2:u_6x12a.hex
	{ 0x002176, 0x002330,  221, 14,  7, 14 }, // 3:u_7x14a.hex
	{ 0x002F46, 0x003100,  221, 16,  8, 16 }, // 4:u_8x16a.hex
	{ 0x003ED0, 0x00404C,  190, 40, 10, 20 }, // 5:u_10x20a.hex
	{ 0x005DFC, 0x005FB6,  221, 48, 12, 24 }, // 6:u_12x24a.hex
	{ 0x008926, 0x00BEE4, 6879,  8,  8,  8 }, // 7:u_8x8.hex
	{ 0x0195DC, 0x01CB96, 6877, 20, 10, 10 }, // 8:u_10x10.hex
	{ 0x03E4DA, 0x041A98, 6879, 24, 12, 12 }, // 9:u_12x12.hex
	{ 0x069F80, 0x06D53E, 6879, 28, 14, 14 }, // 10:u_14x14.hex
	{ 0x09C5A2, 0x09FB60, 6879, 32, 16, 16 }, // 11:u_16x16.hex
	{ 0x0D5740, 0x0D8CFE, 6879, 60, 20, 20 }, // 12:u_20x20.hex
	{ 0x13D942, 0x140EFC, 6877, 72, 24, 24 }, // 13:u_24x24.hex
};

この構造体の定義はexfonts.hで行っています。
// フォント管理テーブル
typedef struct FontInfo {
   uint32_t    idx_addr;  // インデックス格納先頭アドレス
   uint32_t    dat_addr;  // フォントデータ格納先頭アドレス
   uint16_t    f_num;     // フォント格納数
   uint8_t     b_num;     // フォントあたりのバイト数
   uint8_t     w;         // 文字幅
   uint8_t     h;         // 文字高さ
} FontInfo;

2015年1月31日 (土)

Linuxで直接シリアルデバイスを利用する際の注意点等

Linuxにて、直接 /dev/ttyUSB0、/dev/ttyACM0 なんかのデバイスを使って
arduinoやichigoJamなんかとちょっとした通信を行う場合のメモです。
まあ、自分がハマって苦労した点です。

具体的には、
#  cat /dev/ttyUSB0
#  echo "ABCDE" > /dev/ttyUSB0

とかやって、データをやり取りする場合に発生する問題点に対応する方法のメモです。

現在の通信速度や通信条件の確認

# stty -a < /dev/ttyUSB0 speed 115200 baud; rows 0; columns 0; line = 0; intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0; -parenb -parodd cs8 -hupcl -cstopb cread clocal -crtscts -cdtrdsr -ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel -iutf8 -opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 -isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke

または、

# stty -g < /dev/ttyUSB0
500:0:18b2:0:3:1c:7f:15:4:0:1:0:11:13:1a:0:12:f:17:16:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0

通信条件の設定

arduinoやichigoJamなんかと、通信したい場合の設定

#stty -F /dev/ttyUSB0 115200 \
-iuclc -ixany -imaxbel -iutf8 -opost -olcuc -ocrnl -onlcr \
-onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 -isig \
-icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase \
-tostop -echoprt -echoctl -echoke

空白文字(スペース)を使う場合の注意事項

コマンドライン、シェルプログラムでは空白文字(スペース)は区切り文字
として機能します。

#!/bin/bash
line="    AAA BBB   CCC DDD"
echo ${line}
echo -n "["
echo " 123 4 5" | while read -rN 1 c
do
     echo -n "$c"
done
echo "]"

この実行結果は、

AAA BBB CCC DDD
[12345]

となります。
空白文字(スペース)は1文字にまとめられたり、削除されてりしています。
(逆に単語(ワード)部分の取り出しは楽になります)

コマンドラインでの利用では問題ないのですが、
echo ${line}  > /dev/ttyUSB0
echo -n "$c" > /dev/ttyUSB0

とかやって、通信データとし空白文字(スペース)もちゃんと送信したい場合は
当然問題となります。

この勝手に空白文字(スペース)をいじられないよにするには、
環境変数IFS(フィールドセパレータ)に空白文字(スペース)を入れないようにします。
デフォルトの設定の確認
  # set | grep IFS
  IFS=$' \t\n'

これを、シェルプログラム内でのみ、IFS=$'\n' として、セパレータは改行文字のみにします。

#!/bin/bash
PRE_IFS=$IFS
IFS=$'\n'
line="    AAA BBB   CCC DDD"
echo ${line}
echo -n "["
echo " 123 4 5" | while read -rN 1 c
do
     echo -n "$c"
done
echo "]"
IFS=$PRE_IFS

この実行結果は、

    AAA BBB   CCC DDD
[ 123 4 5]

無加工の出力となります。

デバイスがブロックされる場合の対応

通信条件を設定した後、
  # cat /dev/ttyUSB01

とすると、シリアル通信データを簡単に表示出来ます。
ただし、受信待ちとなって終了しない場合があります。

調べたのですが、シェルプログラムでのタイムアウトの設定が分かりませんでした。
stty当たりの設定で何とかならないか調べたのですが分かりませんでした。

そこでtimeoutコマンドを使って、タイムアウトを実現しました。
私の使っているCentOSでは標準で用意されているコマンドのようです。
# timeout 2 cat /dev/ttyUSB0

とすると、2秒待ちのタイムアウトとなります。
特定のコマンドをarduinoに送って、その結果を幾つか受信するような場合は、

#!/bin/bash
PRE_IFS=$IFS
IFS=$'\n'
tty="/dev/ttyUSB0"
stty -F ${tty} 115200 \
-parenb -parodd cs8 -hupcl -cstopb cread clocal -crtscts \
-iuclc -ixany -imaxbel -iutf8 -opost -olcuc -ocrnl -onlcr \
-onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 -isig \
-icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase \
-tostop -echoprt -echoctl -echoke

echo  -n "$1" > ${tty}
echo  -e "\n" > ${tty}
timeout 2 cat ${tty} | while read line
do
  echo "${line}"
  usleep 1000
done
echo "" > ${tty}
IFS=$PRE_IFS

こんな感じで、対応しています。
例では、コマンドラインの文字列を送信して、その結果として
複数のデータを表示するとうものです。

シリアル通信デバイスの利用権限 (2016/01/19 追記)
USB-UARTモジュールをLinuxマシンに挿入して利用権限を調べると次のように
なっています(CentOS 6.4 、Raspbian Jessieで確認)
$ ls -l /dev/ttyU*
crw-rw---- 1 root dialout 188, 0  1月 19 00:11 /dev/ttyUSB0

グループ dialoutに利用権限があります。
そこで一般ユーザがシリアル通信デバイスを気楽に利用できるようにするには
グループ dialoutに登録すればよいです。

#sudo gpasswd -a USER dialout
または(ubuntu、Raspbian Jessieの場合)
#sudo adduser USER dialout

これでUSERがdialoutの一員になり以降、/dev/ttyUSBxxxや/dev/ttyACMxxが
使えると思います。

関連記事
  ・IchigoJam用の支援ツールLinux版を作成しました ・・・ シェルプログラムでシリアル通信
  ・Raspberry Pi 2(Raspbian Jessie) のGPIOのシリアルポートを使う
  ・Raspbian Jessie のGPIOまわりの利用権限
 

2015年1月26日 (月)

Arduinoで16x16 LEDドットマトリックスを利用する(8)

ちょっと間が空きましたが、前回の続きです。

漢字用フラッシュメモリを基板上に載せて、前回よりも更にシンプルになりました。
更に、漢字フォント周りのプログラムをライブラリ化して使いやすくしました。
(現在、ライブラリのテスト中です)

Dscn3561

実際の動作



デモでは、ランダムにフォントサイズを変えて「いろは歌」をスクロール表示しています。
漢字フォントは8、10、12、14、16、20、24ドットの7種類の漢字が同時に使えます。

16x16の表示領域しかなくても、24x24ドットの漢字が意外と読めますね。
ちょっとビックリです。

このデモの作成において、
ビットマップ操作(表示パターンの加工、切り貼り)が無茶苦茶苦労しました。
任意サイズのビットマップ上の任意の位置に任意サイズのビットマップを書く等、
意外と面倒ですね。

実装面のお話
当初、漢字用フォント格納用のフラッシュメモリとして W25Q64VBを使っていましたが、
安い4MバイトのW25Q32BVSIGに変更しました。10個で$3.8なので1個40円くらいです。

Dscn3562

Dscn3563

フォントデータの書込みは、以前購入したCH341A programmerを利用しました。
SOPパッケージのため下駄を履かせて焼きました。

Dscn3569

焼いたフラッシュメモリは三端子レギュレータ、レベルコンバーターを使って
3.3V <=> 5Vの変換を行った回路を付加して、強引に載せます。

Dscn3557

う〜ん、なんて汚い基板でしょう。これで動いているのが不思議です。

W25Q64VBからW25Q32BVSIGからの変更しても、
プログラムの修正は無しで利用出来ました。

表示部のドットマトリックスの制御は、この前調査したHT16K33に変更する予定です。
現在制御にICをTB62706中心として5つ使っていますが2個で済みます。

このバージョンでは、汎用的なライブラリの実装に重点を置いて完成させます。

高々、16x16の表示なのに意外と面倒です。

2015年1月18日 (日)

HT16K33を2つ使ってLEDドットマトリックスを制御してみた

前回の続きです。

HT16K33を2つ使用して4つの8x8LEDドットマトリックスの表示を試してみました。

Dscn3524

配線がちょと大変でしたが、結論としては、全く問題無しです。
表示遅れやパターンの波うち等は無いです。
I2C接続なのでArduinoとの接続は4線のみです。
MAX7219よりも使いやすいです。

さらに、データーシートでは5V駆動なのですが、3.3Vでも全く問題無く動きました。
輝度の指定はコマンドで設定するのですが、最低にしても明るいです。

複数利用について
HT16K33(28 SOP-A版)は、個々にI2Cアドレスの設定が出来ます。
下図のように抵抗とダイオードでアドレス設定します。
ダイオードは手持ちの1N4148を利用しました。抵抗は39KΩを持っていなかったので
51KΩを利用しましたが、問題なく動作しました。

01
3ビット分のアドレス領域があるので同時に8個の利用が可能です。
今回使った2つのうち、1つはアドレス指定なし、もう一つはA0に抵抗を接続して
異なるアドレスとしました。

動作の様子



スケッチ

#include <Wire.h>

// アドレス
#define HT_I2C_ADDRESS 0x70

// コマンド
#define HT_CMS_DATA       0x00
#define HT_CMD_SYSSET     0x20
#define HT_CMD_BLINK      0x80
#define HT_CMD_BRIGHTNESS 0xE0

// System Setup レジスタ設定値
#define HT_SYSSET_OSC_ON  1

// Display setup 設定値
#define HT_BLINK_DISPLAY_ON B00000001
#define HT_BLINK_OFF        B00000000
#define HT_BLINK_2HZ        B00000010
#define HT_BLINK_1HZ        B00000100
#define HT_BLINK_05HZ       B00000110

// 表示パターン用バッファ(R16xC8)
uint32_t buff[8];  
uint8_t raw[32];

// テスト用パターン('さ','い','た','ま')
uint8_t font[4][8] = {
  {0x08,0x08,0x7E,0x04,0x24,0x40,0x3C,0x00},  // さ
  {0x00,0x88,0x84,0x82,0x82,0x50,0x20,0x00},  // い
  {0x20,0xF0,0x2E,0x40,0x48,0x50,0x8E,0x00},  // た
  {0x08,0x7E,0x08,0x7E,0x08,0x7C,0x7A,0x00}   // ま
};

// HT16K33の初期化
void ht_init() {
  Wire.begin();

  // #1
  ht_system_Seup(HT_I2C_ADDRESS, HT_SYSSET_OSC_ON);  // システムオシレータをONにする
  ht_display_setup(HT_I2C_ADDRESS,HT_BLINK_OFF) ;    // 点滅表示周期の設定
  ht_setBrightness(HT_I2C_ADDRESS,1);                // 明るさの設定

  // #2
  ht_system_Seup(HT_I2C_ADDRESS+1, HT_SYSSET_OSC_ON);  // システムオシレータをONにする
  ht_display_setup(HT_I2C_ADDRESS+1,HT_BLINK_OFF) ;    // 点滅表示周期の設定
  ht_setBrightness(HT_I2C_ADDRESS+1,1);                // 明るさの設定
}

// SystemSetup Registerの設定
// (システムオシレータのモード設定)
void ht_system_Seup(uint8_t addr, uint8_t p) {
  Wire.beginTransmission(addr);
  Wire.write(HT_CMD_SYSSET | p);
  Wire.endTransmission(); 
} 

// 明るさの設定
void  ht_setBrightness(uint8_t addr, uint8_t p) {
  if (p > 15) p = 15;
  Wire.beginTransmission(addr);
  Wire.write(HT_CMD_BRIGHTNESS | p);
  Wire.endTransmission(); 
}

// Display setup(点滅周期の設定)
void ht_display_setup(uint8_t addr, uint8_t p) {
  if (p > 3) p = 0;
  Wire.beginTransmission(addr);  
  Wire.write(HT_CMD_BLINK | HT_BLINK_DISPLAY_ON | p); 
  Wire.endTransmission();
}

// 表示パターンの送信
void ht_write(void) {
  uint8_t d;
  uint32_t msk;
  uint8_t t;

  // バッファデータを表示用生データに変換する
  msk = 0x00000001;
  t = 0;  
  for (uint8_t x=0; x <32; x++) {
   d = 0;
    for (uint8_t y=0; y < 8; y++) {
      if (msk & buff[y]) {
        d |= _BV(y);
      }
    }
    raw[t] = d;
    msk <<=1;
    t++;
  } 
  
  // HT16K33 #1 にデータ転送
  Wire.beginTransmission(HT_I2C_ADDRESS);
  Wire.write(HT_CMS_DATA);
  for (uint8_t i=0; i<8; i++) {
    Wire.write(raw[i+24]);    
    Wire.write(raw[i+16]);    
  }
  Wire.endTransmission();  

  // HT16K33 #2 にデータ転送
  Wire.beginTransmission(HT_I2C_ADDRESS+1);
  Wire.write(HT_CMS_DATA);
  for (uint8_t i=0; i<8; i++) {
    Wire.write(raw[i+8]);    
    Wire.write(raw[i]);    
  }
  Wire.endTransmission();  
}

// バッファのクリア
void ht_clear_buffer() {
    for (uint8_t i=0; i <8; i++) 
      buff[i] = 0;
}

// 表示のクリア
void ht_clear() {
  ht_clear_buffer();
  ht_write();
}

// バッファの指定座標のON/OFF
void ht_set_dot_buffer(uint8_t x, uint8_t y, uint8_t d) {
  if (d) {
    buff[y] |= 0x80000000 >> x;
  } else {
    buff[y] &= ~(0x80000000 >>x);
  }   
}

// バッファ上の指定座標に8x8パターンをセット
void write_bufat(uint8_t* fptr, uint8_t x, uint8_t y) {
  if (x>32 || y >7)
    return ;     
  for (byte j=y,i=0; i < 8; j++,i++)
    buff[j] |= (((uint32_t)(fptr[i]))<<24) >>x;
}

// バッファ上の指定座標に8x8パターンをセット(負座標指定可能)
void write_bufat2(uint8_t* fptr, int8_t x, int8_t y) {
  if (x>31 || y >7 || x < -7 || y <-7)
    return ;
  for (int8_t i=0,j=y; i < 8; i++,j++) {
    if (j>=0) {
      if (x>=0)
        buff[j] |= (((uint32_t)(fptr[i]))<<24) >>x;
      else 
        buff[j] |= (((uint32_t)(fptr[i]))<<24) <<(-x);      
    }
  }
}

// バッファ左スクロール
void scroll() {
   for (uint8_t i=0; i < 8; i++) {
      buff[i]<<=1;  // 各行を左にシフトする
   }
}

void setup() {
   ht_init();
   ht_clear();
}

void demo_test() {
  ht_clear();
  
  // バッファに"さいたま"のパターンを書き込む
  for (uint8_t i=0; i <4; i++) {
    write_bufat(font[i],i<<3, 0);
  }
    
  ht_write(); // バッファの内容を表示
  delay(500);
  
  // スクロール
  for (uint8_t i=0; i < 32; i++) {
    scroll();    // バッファ内を1ドット左にシフト
    ht_write();  // バッファの内容を表示
    delay(100);
  }  
}

// 指定座標へのパターン書込み
void demo_test2() {
  for (int8_t y=-7; y<8; y++) {
    for (int8_t x=-7; x<32; x++) {
     ht_clear_buffer();
     write_bufat2(font[0],x, y);
     ht_write();  // バッファの内容を表示
     delay(100);
    }
  }  
}  
  
void loop() {
 demo_test();
 delay(1000);  
 demo_test2();
 delay(1000);  
}

回路図は、データーシートのApplication Circuitのまんまなので作ってません。
8x8LEDマトリックス(HSN-0788UR)とHT16K33との配線接続表を作成して
配線しました。

01_2

色々と使えそうです。表示器をモジュール化してみようと思います。

より以前の記事一覧