フォト
2017年10月
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        
無料ブログはココログ

« aitendo 16x16LEDマトリックスの制御 (4) | トップページ | Arduino用ビットマップファイルロードライブラリを作りました »

2016年6月19日 (日)

aitendo 16x16LEDマトリックスの制御 (5)

前回からの続きです。

モノクロ(2値)のWindowsビットマップファイルを汎用的に利用したいなぁと思い、
ライブラリ化を目指して機能を追加しています。

取りあえず、手持ちのアナログジョイスティックを繋げて、ビットマップ画像を
「ぐりぐり」と任意にスクロール表示出来るよう、機能追加しました。

実装したものの、想像していたよりは面白くなかったです。 Otz..
(一度に16x16ドットしか表示出来ないのが敗因ですね)

まあ、ビットマップファイル操作機能は今後フラフィック液晶なんかにも使おうと思います。

接続の様子

Dscn5322

動作の様子その1

下記のモノクロビットマップ画像658x431(36kバイト)から、任意の座標から16x16ドットを
切り出して表示をしています。青空文庫の「芥川龍之介 蜘蛛の糸」をキャプチャしたものです。

01

36kバイトの画像データの全てをArduinoにロードすることは出来ませんが、
ビットマップ画像から部分切り出しでデータをロードすることで表示可能にしています。


動作の様子その2

グラフィック画像っぽいののも試してみました。画像サイズは96x69です。

02

思っていたよりも面白くなかったですが、ビットマップ画像操作ライブラリの動作確認と
割り切ることにします^^


スケッチ ダウンロード sample6.zip (19.8K)   (2016/06/21 バグがあり修正しました)

ビットマップ画像の操作については、下記のソースを参考にさせて頂きました。
   Adafruit-SSD1331-OLED-Driver-Library-for-Arduino/examples/bmp/bmp.pde

ソース自体は全面的に作り直しですが、ヘッダー部と、画像の取り出し部分が大変参考になりました。

スケッチは3つのソースファイルに分かれています。
   sample6.ino   ・・・  メイン
   sdbitmap.h    ・・・  sdbitmapクラス定義ヘッダファイル
   sdbitmap.cpp ・・・  sdbitmapクラス定義本体

sample6.ino

// sample6.ino
// aitendo 16x16LEDドットマトリックスの制御サンプル ビットマップファイル表示
// 2016/06/19 たま吉さん
//

#include <arduino.h>
#include <MsTimer2.h>
#include <string.h>
#include "sdbitmap.h"

#define DATAPIN	  (7)	  // TB62706のSERIAL-INへ
#define LATCHPIN  (9)	  // TB62706のLATCHへ
#define CLOCKPIN  (8)	  // TB62706のCLOCKへ
#define ENABLEPIN (6)     // TB62706のENABLEへ
#define CS_SD     (10)    // SDカードモジュールCSへ

// 表示用バッファ
uint8_t fbuf[32];        // 表示パターンバッファデータ(16x16)

//
// shiftOut高速化版
//
void new_shiftOut(uint8_t dataPin,uint8_t clockPin,uint8_t bitOrder,byte val) {
  uint8_t i;
  uint8_t bit_data = digitalPinToBitMask(dataPin);
  uint8_t bit_clock = digitalPinToBitMask(clockPin);
  volatile uint8_t *out_data = portOutputRegister(digitalPinToPort(dataPin));
  volatile uint8_t *out_clock = portOutputRegister(digitalPinToPort(clockPin));

  for (i = 0; i < 8; i++)  {
    if (bitOrder == LSBFIRST) {
      if(val & (1 << i)) {
        *out_data |= bit_data;
      } else {
        *out_data &= ~bit_data;
      }
    } else {
      if(val & (1 << (7 - i))) {
        *out_data |= bit_data;
      } else {
        *out_data &= ~bit_data;
      }
    }
    *out_clock |= bit_clock;
    *out_clock &= ~bit_clock;
  }
}

//
// digitalWrite高速化版
//
inline void new_digitalWrite(uint8_t pin, uint8_t val) {
  uint8_t bit = digitalPinToBitMask(pin);
  volatile uint8_t *out = portOutputRegister(digitalPinToPort(pin));
  if (val == LOW)
    *out &= ~bit;
  else
    *out |= bit;  
}

//
// バッファ内データを表示する(1ライン分割表示)
//
void fontout() {
  static uint8_t _line = 0;  
  // 点灯LEDが移動するパターン
  new_digitalWrite(ENABLEPIN,LOW);   // OUTを有効にする
  new_digitalWrite(LATCHPIN, LOW);   //送信開始        
 
  // 行選択
  uint16_t val = _BV(15-_line);
  new_shiftOut(DATAPIN, CLOCKPIN, LSBFIRST, val);
  new_shiftOut(DATAPIN, CLOCKPIN, LSBFIRST, val>>8);       
     
  // 行フォントパターンの送信
  new_shiftOut(DATAPIN, CLOCKPIN, LSBFIRST, fbuf[(_line<<1)+1]);
  new_shiftOut(DATAPIN, CLOCKPIN, LSBFIRST, fbuf[_line<<1]);

  new_digitalWrite(LATCHPIN, HIGH);  //送信終了
  _line++;
  if (_line == 16)
    _line = 0;
}

//
// バッファクリア
//
void clearBuf() {
   memset(fbuf,0,32);
}

void test_bitmapEx2() {
  uint8_t tmpbuf[32];        // 表示パターンバッファデータ
  uint16_t w,h,x=0,y=0;
  int16_t  px,py;
  int16_t dx,dy;
  uint16_t tm;
  
  memset(tmpbuf,0,32);
  sdbitmap bitmap;
  bitmap.setFilename("IMG3.BMP");

  // ビットマップデータオープン
  bitmap.open();
  w  = bitmap.getWidth();
  h  = bitmap.getHeight();
  x = 0; y= 0;

  bitmap.getBitmapEx(tmpbuf, x, y, 16, 16, 1);
  memcpy(fbuf,tmpbuf,32);

  while(1) {
    dx =  analogRead(A1);  
    dy =  analogRead(A0); 
    px = x;
    py = y;
    
    if (x>0 && dx > 520)
      x--;
    else if (x < w-16 && dx < 490) 
      x++;
    if (y>0 && dy < 490)
      y--;
    else if (y < h-16 && dy > 520) 
      y++;

    if (px!=x || py!=y) {
      dx = 40-abs(dx-512)/16;
      dy = 40-abs(dy-512)/16;
      tm = min(dx,dy);
      bitmap.getBitmapEx(tmpbuf, x, y, 16, 16, 1);
      memcpy(fbuf,tmpbuf,32);
    } else
      tm = 30;
    delay(tm);
  }
  delay(2000);
  bitmap.close();
}

void setup() {
  //Serial.begin(115200);
  
  // LEDマトリックス用制御ピンのモード設定
  pinMode(ENABLEPIN,OUTPUT);
  pinMode(DATAPIN, OUTPUT);
  pinMode(LATCHPIN, OUTPUT);
  pinMode(CLOCKPIN, OUTPUT);

  // 制御ピンの初期化
  digitalWrite(ENABLEPIN,HIGH);
  digitalWrite(CLOCKPIN, LOW);
  digitalWrite(LATCHPIN, HIGH);

  // SDカード利用開始
  SD.begin(CS_SD);     // フォント管理の初期化
  
  // 割り込み開始
  MsTimer2::set(1, fontout); //  1ライン描画/1回 版
  MsTimer2::start();

  // デモ
  test_bitmapEx2();  // ビットマップデータロードテスト
}

void loop(){
} 

sdbitmap.h

// 
// sdbitmap.h
// ビットマップファイル操作クラス
// 2016/06/18 たま吉さん
//

#ifndef __SDBITMAP_H__
#define __SDBITMAP_H__

#include <arduino.h>
#include <SD.h>
#include <SPI.h>

// クラス定義
class sdbitmap {
 
 // メンバ変数の定義
 private:
   char*    _filename;         // ビットマップファイル名
   File	    _bmpfile;          // ファイルオブジェクト
   uint8_t  _sts;              // ファイルアクセス状態(0:初期 1:オープン中1)
   int16_t  _bmpWidth;         // 画像幅
   int16_t  _bmpHeight;        // 画像高さ
   uint32_t _bmpImageoffset;   // Start of image data in file
   uint32_t _rowSize;          // 1ラインのバイトサイズ
   boolean  _flip;             // 画像格納方向
	 
 // メンバ関数の定義
 public:
   sdbitmap();		           // コンストラクタ
   void	    init();	           // 初期化
   void	    setFilename(char* str);// ファイルの設定
   uint8_t  open();                // ファイルのオープン
   void	    close();               // ファイルのクローズ
   int16_t  getWidth();            // 画像幅の取得
   int16_t  getHeight();           // 画像高さの取得	 

   uint8_t getByte(uint16_t x, uint16_t y); // 指定位置から8ドット(バイトデータ)取り出し
   uint8_t getBitmap(uint8_t*bmp,           // ビットマップデータの切り出し取得(高速版)
   uint16_t x, uint16_t y, 
	uint8_t w, uint8_t h, uint8_t mode);

   uint8_t getBitmapEx(uint8_t*bmp,         // ビットマップデータの切り出し取得
   uint16_t x, uint16_t y, 
   uint8_t w, uint8_t h, uint8_t mode);
   uint8_t getBitmap(uint8_t*bmp, uint8_t mode); // ビットマップデータの取得  

 private:
   uint16_t read16();		// ワードデータ読み込み
   uint32_t read32();		// ロングデータ読み込み
};

#endif

sdbitmap.cpp

//
// sdbitmap.cpp
// ビットマップファイル操作クラス
// 2016/06/21 たま吉さん
//

#include "sdbitmap.h"
#define DEBUG_BMPLOAD 0

//
// コンストラクタ
//

sdbitmap::sdbitmap() {
	init();
}

//
// 初期化
//
void sdbitmap::init() {
	_filename = NULL;
	_sts = 0;
}

//
// ファイル名設定
//
void sdbitmap::setFilename(char* str) {
	_filename = str;	
}

//
// ワードデータ読み込み
//
uint16_t sdbitmap::read16() {
  uint16_t result;
  ((uint8_t *)&result)[0] = _bmpfile.read(); // LSB
  ((uint8_t *)&result)[1] = _bmpfile.read(); // MSB
  return result;
}

//
// ロングデータ読み込み
//
uint32_t sdbitmap::read32() {
  uint32_t result;
  ((uint8_t *)&result)[0] = _bmpfile.read(); // LSB
  ((uint8_t *)&result)[1] = _bmpfile.read();
  ((uint8_t *)&result)[2] = _bmpfile.read();
  ((uint8_t *)&result)[3] = _bmpfile.read(); // MSB
  return result;
}


//
// ビットマップファイルのオープン 
// 引数なし
// 戻り値	0:正常終了
//			1:オープン失敗
//			2:フォーマットエラー
// 
uint8_t	sdbitmap::open() {
  uint8_t  bmpDepth;              // Bit depth (currently must be 24)
  uint32_t tmp_val;

  _flip    = true;      		  // BMP is stored bottom-to-top

  // ファイルオープン

#if DEBUG_BMPLOAD == 1
  Serial.println();
  Serial.print("Loading image '"); Serial.print(_filename);  Serial.println('\'');
#endif
 
  // Open requested file on SD card
  if ((_bmpfile = SD.open(_filename)) == NULL) {

#if DEBUG_BMPLOAD == 1
    Serial.print("File not found");
#endif

    return 1;	// オープン失敗
  }

  // [ファイルヘッダ] (14バイト)
  //   アドレス (サイズ)  名称  内容
  //   0x0000 (2)  bfType  ファイルタイプ 通常は'BM'
  //   0x0002 (4)  bfSize  ファイルサイズ (byte)
  //   0x0006 (2)  bfReserved1 予約領域 常に 0
  //   0x0008 (2)  bfReserved2 予約領域 常に 0
  //   0x000A (4)  bfOffBits ファイル先頭から画像データまでのオフセット (byte)
  //

  if(read16() != 0x4D42) 			  // BMPシグニチャチェック(2バイト)
	return 2;						  // フォーマットエラー
	
  tmp_val = read32();			   	  // ファイルサイズの取得(4バイト)
#if DEBUG_BMPLOAD == 1    
    Serial.print("File size: "); Serial.println(tmp_val);
#endif
    tmp_val = read32();				  // 予約領域の取得(4バイト)
    _bmpImageoffset = read32();		  // オフセット値の取得(4バイト)
#if DEBUG_BMPLOAD == 1    
    Serial.print("Image Offset: "); Serial.println(_bmpImageoffset, DEC);
#endif

  // [情報ヘッダ] (40バイト)
  //   0x000E (4)  bcSize          ヘッダサイズ 40
  //   0x0012 (4)  bcWidth         画像の幅 (ピクセル)
  //   0x0016 (4)  bcHeight        画像の高さ (ピクセル)
  //   0x001A (2)  bcPlanes        プレーン数 常に 1
  //   0x001C (2)  bcBitCount      1画素あたりのデータサイズ
  //   0x001E (4)  biCompression   圧縮形式 0:BI_RGB(無圧縮) 1:BI_RLE8 2:BI_RLE4 3: BI_BITFIELDS 4:BI_JPEG 5: BI_PNG
  //   0x0022 (4)  biSizeImage     画像データ部のサイズ (byte) 
  //   0x0026 (4)  biXPixPerMeter  横方向解像度
  //   0x002A (4)  biYPixPerMeter  縦方向解像度
  //   0x002E (4)  biClrUsed       格納されているパレット数
  //   0x0032 (4)  biCirImportant  重要なパレットのインデックス
  //
    
  tmp_val = read32();		// ヘッダサイズの取得

#if DEBUG_BMPLOAD == 1        
    Serial.print("Header size: "); Serial.println(tmp_val);
#endif

  _bmpWidth  = read32();	// 画像幅の取得
  _bmpHeight = read32();	// 画像高さの取得
   
  if(read16() != 1)     // プレーン数の取得(必ず1である必要がある)
 	return 2;
 
  bmpDepth = read16();  // 1ピクセル当たりのビット数
#if DEBUG_BMPLOAD == 1        
    Serial.print("Bit Depth: "); Serial.println(bmpDepth);
#endif

  if (bmpDepth != 1)	// 2値(白,黒)
  	return 2;			// 形式エラー

  if(read32() != 0 )  	// 圧縮形式:無圧縮か?
  	return 2;			// 形式エラー

  // 1ラインのバイト数
  _rowSize = (((_bmpWidth+7)>>3) + 3) & ~3;
  if(_bmpHeight < 0) {
    _bmpHeight = - _bmpHeight;
    _flip      = false;
  }

#if DEBUG_BMPLOAD == 1    
    Serial.print("flip: "); Serial.println(_flip);
    Serial.print("Image size: "); Serial.print(_bmpWidth); Serial.print('x'); Serial.println(_bmpHeight);
#endif
  return 0;
}

//
// ファイルのクローズ
//
void sdbitmap::close() {
  _bmpfile.close();
}

//
// 画像幅の取得
//
int16_t sdbitmap::getWidth() {
	return _bmpWidth;
}

//
// 画像高さの取得
//
int16_t sdbitmap::getHeight() {
	return _bmpHeight;
}

//
// ビットマップデータの取得
// 引数
//  bmp : データ格納アドレス
//  mode: 0:通常 1:反転
// 戻り値
//  0:     正常終了
//  0以外  異常終了
//

uint8_t sdbitmap::getBitmap(uint8_t*bmp, uint8_t mode) {
  getBitmap(bmp, 0, 0, _bmpWidth, _bmpHeight, mode);
}

//
// ビットマップデータの切り出し取得
// 引数
//  bmp : データ格納アドレス
//  x   : 取り出し位置:横 (8の倍数であること)
//  y   : 取り出し位置:縦
//  w   : 取り出し幅 (8の倍数であること)
//  h   : 取り出し高さ
//  mode: 0:通常 1:反転
// 戻り値
//  0:     正常終了
//  0以外  異常終了
// 
uint8_t sdbitmap::getBitmap(uint8_t*bmp, uint16_t x, uint16_t y, uint8_t w, uint8_t h, uint8_t mode) {
  uint32_t pos = 0;
  uint16_t ptr = 0;

  uint16_t bx = x>>3;
  uint16_t bw = (w+7)>>3;
  uint8_t bit_w = w & 7;
  
  if (y + h > _bmpHeight)
    return -1;
  if ( ((x + w)>>3) > _rowSize)
    return 1;

  for ( uint16_t row = y; row < y+h ; row++ ) { // ラインループ
    if(_flip) 
      pos = _bmpImageoffset + (_bmpHeight - 1 - row) * _rowSize + bx;
    else
      pos = _bmpImageoffset + row * _rowSize + bx;
      
    // ファイルのシーク  
    if( _bmpfile.position() != pos ) // Need seek?
      _bmpfile.seek(pos);

    // 1ライン分のデータの取得
    for ( uint16_t col = bx ; col < bx+bw ; col++ ) {
      bmp[ptr++] =  mode ? ~_bmpfile.read(): _bmpfile.read();
    }    
    if (bit_w && ptr) // 8の倍数でない
      bmp[ptr-1] &= 0xff<<(8-bit_w);
  }
}

//
// 指定位置から8ビット(1バイト)取り出し
//
uint8_t sdbitmap::getByte(uint16_t x, uint16_t y) {
  uint8_t d1,d2;
  uint32_t pos = 0;
  uint16_t bx = x>>3;        // 取り出しバイト位置:横
  uint8_t bit_x = x & 7;     // ビット位置

  if (bx >= _rowSize || y >= _bmpHeight ) 
    return 0;

  if(_flip) 
    pos = _bmpImageoffset + (_bmpHeight - 1 - y) * _rowSize + bx;
  else
    pos = _bmpImageoffset + y * _rowSize + bx;

    // ファイルのシーク  
    if( _bmpfile.position() != pos )
      _bmpfile.seek(pos);

  d1 = _bmpfile.read();
  if (bit_x) {
    // 8の倍数でない
    d1<<=bit_x;
    d2 = (bx+1 >= _rowSize) ? 0:_bmpfile.read()>>(8-bit_x);
  } else {
    // 8の倍数
    d2 = 0;
  }
  return d1|d2;
}

//
// ビットマップデータの切り出し取得
// 引数
//  bmp : データ格納アドレス
//  x   : 取り出し位置:横
//  y   : 取り出し位置:縦
//  w   : 取り出し幅 
//  h   : 取り出し高さ
//  mode: 0:通常 1:反転
// 戻り値
//  0:     正常終了
//  0以外  異常終了
// 
uint8_t sdbitmap::getBitmapEx(uint8_t*bmp, uint16_t x, uint16_t y, uint8_t w, uint8_t h, uint8_t mode) {
  // ラインループ
  uint16_t ptr = 0;
  uint8_t d;
  uint8_t bit_w = w % 8;
  for (uint16_t row = y; row < y+h ; row++ ) { 
    for (uint16_t col = x; col < x + w ; col += 8 ) {
      bmp[ptr++] = mode? ~getByte(col,row):getByte(col, row);      
    }
    if (bit_w && w > 8) // 右端の端数ビットの補正
      bmp[ptr-1] &= 0xff<<(8-bit_w);
  }  
}

もう少し整理して、ビットマップファイルの操作部分をArduinoのlibraryディレクトリに
登録できるよう、ライブラリ化したいと思います。

« aitendo 16x16LEDマトリックスの制御 (4) | トップページ | Arduino用ビットマップファイルロードライブラリを作りました »

arduino」カテゴリの記事

表示器制御関連」カテゴリの記事

コメント

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/571408/63800242

この記事へのトラックバック一覧です: aitendo 16x16LEDマトリックスの制御 (5):

« aitendo 16x16LEDマトリックスの制御 (4) | トップページ | Arduino用ビットマップファイルロードライブラリを作りました »