前回からの続きです。
モノクロ(2値)のWindowsビットマップファイルを汎用的に利用したいなぁと思い、
ライブラリ化を目指して機能を追加しています。
取りあえず、手持ちのアナログジョイスティックを繋げて、ビットマップ画像を
「ぐりぐり」と任意にスクロール表示出来るよう、機能追加しました。
実装したものの、想像していたよりは面白くなかったです。 Otz..
(一度に16x16ドットしか表示出来ないのが敗因ですね)
まあ、ビットマップファイル操作機能は今後フラフィック液晶なんかにも使おうと思います。
接続の様子

動作の様子その1
下記のモノクロビットマップ画像658x431(36kバイト)から、任意の座標から16x16ドットを
切り出して表示をしています。青空文庫の「芥川龍之介 蜘蛛の糸」をキャプチャしたものです。
36kバイトの画像データの全てをArduinoにロードすることは出来ませんが、
ビットマップ画像から部分切り出しでデータをロードすることで表示可能にしています。
動作の様子その2
グラフィック画像っぽいののも試してみました。画像サイズは96x69です。
思っていたよりも面白くなかったですが、ビットマップ画像操作ライブラリの動作確認と
割り切ることにします^^
スケッチ ダウンロード 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ディレクトリに
登録できるよう、ライブラリ化したいと思います。
最近のコメント