Hatena::ブログ(Diary)

hijouguchiの日記

2010-12-10

OLED

| 18:37 |

OLEDでどうにかカラーバーを表示することに成功しました!

f:id:hijouguchi:20101210183006j:image


やってることは単純で、OLEDのサイズ(96x64)まで小さくしたビットマップファイルをmbedから使いやすくするために独自のバイナリを作成して、そのデータをOLEDに流しておしまい。

ビットマップ

すべてのhoge.bmpが同じモノかと思いきや微妙に違うらしく、そういった部分をどう吸収するか(あるいは切り捨てるか)は悩みどころです。詳しくはwikipediaを見てもらえば良いでしょう。

Windows bitmap - Wikipedia

とりあえずファイルのヘッダから拾ってくればいい情報としては

 があればほとんど問題無く扱えます。これらはほとんどバイトの位置が同じ場所に書かれてるので特に気にしなくて問題無いでしょう。一部例外があるみたいですが、それに関しては使えないと割り切りましょう。

 次にピクセルデータですが、RGBで3byteかと思いきや、ファイルによってまちまちで、ヘッダから1ピクセルのビット数を取ってきてそこから類推するのが良いでしょう。GIMPで吐かせたビットマップでは4byteになっていました。

 最後にビットマップデータ。ビットマップは何故か画像として下から上になるように書き込まれてるため、それを逆にするのが面倒です。*1

C言語による画像処理プログラミング


 以上から画像から独自のバイナリファイルを作る手順としては

画像 -> 96x64のbmp -> (変換プログラム) -> バイナリ

となりました。

変換プログラム

#include <stdio.h>
#include <stdlib.h>

int main()
{
  FILE *bmp;
  unsigned char data[50000];
  unsigned char buf1[32];
  unsigned int buf4[4];
  unsigned int pixel[3];
  unsigned int offset;
  size_t size;
  int i;
  int x, y;
  if((bmp = fopen("foo.bmp", "rb")) == NULL) {
    printf("file open error\n");
    exit(EXIT_FAILURE);
  }

  size = fread(buf1, 1, 2, bmp);
  if(buf1[0] != 0x42 && buf1[1] != 0x4d) {
    printf("this file is not bitmap\n");
    exit(-1);
  }
  fseek(bmp, 0x0a, SEEK_SET);
  size = fread(buf4, 4, 1, bmp);
  offset = buf4[0];

  fseek(bmp, 0x12, SEEK_SET);
  size = fread(buf4, 4, 2, bmp);
  pixel[0] = buf4[0];
  pixel[1] = buf4[1];

  fseek(bmp, 0x1c, SEEK_SET);
  size = fread(buf4, 4, 1, bmp);
  pixel[2] = buf4[0]/8;



  unsigned int tmp;
  unsigned char buffer[100000];
  size_t index = 0;

  for((y=pixel[1]-1); y>=0; y--) {
    fseek(bmp, offset+(y*pixel[0]*pixel[2]), SEEK_SET);
    size = fread(data, 1, pixel[0]*pixel[2], bmp);
    for(i=0; i<size; i++) {
      switch(i%pixel[2]) {
      case 0: // Blue
        //tmp = data[i]>>3;
        tmp = data[i]>>3;
        tmp = (data[i] & 0xf8)<<8;
        break;
      case 1: // Green
        tmp |= (data[i] & 0xf8)<<2;
        break;
      case 2: // Red
        //tmp |= (data[i] & 0xf8)<<8;
        tmp |= data[i]>>3;
        buffer[index++] = (unsigned char)((tmp & 0xFF00)>>8);
        buffer[index++] = (unsigned char)(tmp & 0x00FF);
        break;
      default:
        break;
      }
    }
  }

  FILE *fp;
  if((fp = fopen("hoge.bin", "wb")) == NULL) {
    printf("write file can't open\n");
    exit(-1);
  }
  fwrite(buffer, 1, index, fp);
  fclose(fp);

  fclose(bmp);

  return 0;
}

(コメントが付いてないのはご愛敬…)for文の前までがヘッダから情報を拾ってくる部分、for文がビットマップデータをバイナリに変換してる部分、その下がファイル書き出し。試行錯誤の後が残ってますが動いてるので気にしないことにします。

※覚えたこと

  • fread()
  • fwrite()
  • fseek()

mbedソース

 mbedの便利なところの1つは、デフォルトで外部ストレージとして使えるところです。作成したバイナリファイルを例えばSDカードに保存してそれをマイコンに読み込むなどのような作業が必要なく、単にfopen()で読み込めちゃうのは凄いですよね。

 さて、OLEDを動かす方ですが、データシート*2を参考にしながら「とりあえず光らせる」というのは前回の記事にちょっと書きましたが、今回はそれなりに使いやすくするためにクラスに書き直しました。

#include "mbed.h"

class OLED { //{{{
  private:
    SPI *spi;
    DigitalOut *dc;
    DigitalOut *cs;
    DigitalOut *res;
  public:
    OLED(PinName, PinName, PinName, PinName, PinName, PinName);
    void FullScreen(unsigned int);
    void writeRect(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int dot);
    void stream_data_init(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2)
    {
      *dc = 0;
      spi->write(0x75);  // set row address
      spi->write(y1);
      spi->write(y2);
      spi->write(0x15);  // set col address
      spi->write(x1);
      spi->write(x2);
      *dc = 1;
    }
    void write(unsigned char data) { spi->write(data); }
};

OLED::OLED(PinName mosi, PinName miso, PinName sclk, PinName dc_, PinName cs_, PinName res_)
{
  spi = new SPI(mosi, miso, sclk);
  dc  = new DigitalOut(dc_);
  cs  = new DigitalOut(cs_);
  res = new DigitalOut(res_);

  spi->frequency(6000000);

  // init befor pin configration
  *dc = 0; *cs = 0; *res = 0;
  wait(0.08);
  *res = 1;
  wait(0.02);

  // start oled init
  spi->write(0xAE);  // display off

  spi->write(0x75);  // set row address
  spi->write(0x00);  // start 0
  spi->write(0x3F);  // end 63
  spi->write(0x15);  // set col address
  spi->write(0x00);  // start 0
  spi->write(0x5F);  // end 95

  spi->write(0xA0); // set remap and data format 0111 0000
  spi->write(0x74);
  spi->write(0xA1); // set display start rom RAM
  spi->write(0x00);
  spi->write(0xA2); // set display offset
  spi->write(0x00);
  spi->write(0xA4); // set display mode
  spi->write(0xA8); // set multiplex ratio
  spi->write(0x3F);
  spi->write(0xAD); // set master configration
  spi->write(0x8F); // external vcc supply selected
  spi->write(0xB0); // set power saving mode
  spi->write(0x1A);
  spi->write(0xB1); // set phase 1 and 2 period adjustment
  spi->write(0x74);
  spi->write(0xB3); // set display clock divide ratio ocillator freq
  spi->write(0xD0);
  spi->write(0x8A); // set second pre-change speed of color A
  spi->write(0x81);
  spi->write(0x8B); // set second pre-change speed of color B
  spi->write(0x82);
  spi->write(0x8C); // set second pre-change speed of color C
  spi->write(0x83);
  spi->write(0xBB); // set pre-change level
  spi->write(0x3E);

  spi->write(0xBE); // set vcomh
  spi->write(0x3E);
  spi->write(0x87); // set master corrent control
  spi->write(0x0F);
  spi->write(0x81); // set contrast control for color A
  spi->write(0x80);
  spi->write(0x82); // set contrast control for color B
  spi->write(0x80);
  spi->write(0x83); // set contrast control for color C
  spi->write(0x80);
  spi->write(0xAF); // display on
}

void OLED::FullScreen(unsigned int dot)
{
  *dc = 1;
  unsigned char i, j;
  for(i=0; i<64; i++) {
    for(j=0; j<96; j++) {
      spi->write((unsigned char)((dot>>8)&0xFF));
      spi->write((unsigned char)(dot&0xFF));
    }
  }
}

void OLED::writeRect(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int dot)
{
  int i;

  *dc = 0;
  spi->write(0x75);  // set row address
  spi->write(y1);
  spi->write(y2);
  spi->write(0x15);  // set col address
  spi->write(x1);
  spi->write(x2);

  *dc = 1;
  for(i=0; i<((x2-x1+1)*(y2-y1+1)+1); i++) {
    spi->write((unsigned char)((dot>>8)&0xFF));
    spi->write((unsigned char)(dot&0xFF));
  }
} //}}}


size_t size;
FILE *fp;

LocalFileSystem local("local");
int main() {
  int i,j;
  unsigned char buffer[192*8];
  OLED oled(p5, p6, p7, p8, p9, p10);

  if((fp = fopen("/local/hoge.bin", "rb")) == NULL) exit(-1);
  oled.writeRect(0, 0, 95, 63, 0x0000);
  wait(3);
  oled.stream_data_init(0, 0, 95, 63);

  for(i=0; i<64/8; i++) {
    size = fread(buffer, 1, 192*8, fp);
    for(j=0; j<size; j++)
      oled.write(buffer[j]);
  }

  fclose(fp);
}

こっちも試行錯誤の後が残ってますが気にしない方向で。バイナリデータをすべて配列に入れるのは、バイナリが12.3KBあるので無理でした。とりあえず8回に分けてOLEDに流してます。

動作

f:id:hijouguchi:20101210182950j:image

色は怪しいですが、一応カラーバーです。

f:id:hijouguchi:20101210183056j:image

絵とか。


使ったOLED

no title

*1:モノによっては上から下になってるタイプもありますが、それは使わないことにします。

*2http://aitendo2.sakura.ne.jp/aitendo_data/product_img2/product_img/if/IFB-095BWNNJ9-SPI/IFB-095BWNNJ9-SPI_aitendo.pdf

トラックバック - http://d.hatena.ne.jp/hijouguchi/20101210/1291973831