さくらのジャンク箱ロゴ

Sakura87がほぼ月刊でお届けするPCや電子工作と写真の備忘録てきなブログ @なんと20周年

Raspberry Pi 2でカラーグラフィック液晶の制御

この記事は9年ほど前に投稿されました。内容が古くなっている可能性がありますので更新日時にご注意ください。

IMG_4769_20150726

いよいよ表示器制御の本命である、カラー液晶の制御に挑戦したいと思います。
GPIOを用いて制御させる表示器はこれが最後となると思います。

なぜなら、IOポートしか持っていないPICやAVRならともかく、Raspberry Piは基本的にはPCであって、HDMIもあればアナログビデオ出力も備えているので、これ以上の表示器となると、既製品の外付けモニタを使った方あ楽で確実だからです。

個人的にはあとはカラー有機ELを扱ってみたいのですが、なかなかいいものがないので。

必要なもの

  • ST7735R搭載の128x160px SPI接続のカラーグラフィック液晶
    当方はアマゾンのマーケットプレイスから買えるサインスマート(SainSmart) 1.8″ TFT カラー LCD ディスプレイ モジュール SPI インタフェース & MicroSD 付き for Arduino UNO MEGA R3を使用。ただしこの液晶は少々チャイナクオリティすぎるので当サイトの画像ではエナメル線で縛っております。
    基本的にST7735R搭載の液晶であれば同じコードが使えるはずです。
    この液晶コントローラには無印、S、Rとあって、若干制御方法が違うようです。どう違うのかは知りませんが。このコントローラ搭載の液晶は1000~2000円程度で売られているようです。
  • 24ビット 128×160のBMPファイル
    今回の液晶は12・16・18bitカラー搭載で最大26万色の表示が可能なのと、コントローラの仕様上、Windows標準ソフトウェアでの作りやすさから、24bitBMPファイルを読み込ませることにします。ちなみに今回からきちんとファイル形式のチェックを行うようにしました。※Windows7(XPから?)のMSペイントでは、24bitのBMPファイルを指定していても何故か32ビットのBMPファイルを吐き出す時があるというお茶目な仕様があるみたいです。(元画像がPNGなどのαチャンネルがある画像だった場合にそうなる?)なので、作成した画像データがきちんと24bitかどうかを確認しておいてください。Photoshopなどのツールの場合はそういうことはないと思いますので問題無いですが…。

説明等(省略)

基本的には前回の白黒グラフィック液晶のコマンドコードと転送量が増えるだけですので結線は同じ。SPIの3線にRS信号とリセット信号。そしてGNDだけ。これは他のST7735R搭載の液晶でも同じはず。

接続

基本的にこの液晶コントローラは3.3V駆動であり、Raspberry Piに接続する場合は3.3V電源に接続しますが、上記のモジュールは元々5V系のArduino用のモジュールなので電源にレギュレーターが接続されており、そこから3.3Vが供給されています。そのため、このサイトの画像では5Vに接続していますが。実際に多くのデバイスでは3.3Vか、選択式になっていると思いますのでご注意ください。

IMG_4771_20150726
届いた液晶モジュール改(エナメル線で縛っただけ)

IMG_4772_20150726
裏面。サインスマートのURLの「sa」の付近にあるものがレギュレータでこれで5Vを3.3Vに変換している模様。なのでこのモジュールを買った場合(と言うかArduino向けSPI接続のモジュール全部?)は5V端子につなぎます。

ほかの配線は前回の液晶と同様、SCLがSCLK、SDAがMOSI、CSがCE.x RESがリセット、DCがRS信号となります。ただしココの表記は購入した液晶によって違うと思うので購入した液晶のデータシートで確認して下さい。

IMG_4773_20150726

接続したらこんな感じ。SPI接続なので5Vに接続されていることと、モジュールが違うこと以外は前回のものと変わりありません。

制御方法

この液晶コントローラは、初期化後、描画エリアを設定してデータを送ると設定された描画エリアに、設定した方向からデータを行方向に書き込んでいくという方法で、白黒液晶と違うところは、1ドットあたり2バイト以上のデータを必要とする所と、液晶のアドレスが横向きに1ドットずつ変化していくということです。

dataput

ざっくり言うと、そこら辺の画像データ形式と同じということですね。だからBMPデータもそのままのデータをそのまま転送できます。(ただし、色深度をあわせる必要はありますが。)

制御コマンドは前回までの上位ビットコマンド、その後設定値 というものではなく、8bit全コマンドの後に複数の設定値をデータとして流すというパターンになります。

画像データ本体の転送はRaspberry PiのSPI通信の場合転送バッファは4096バイト(4KiB)なので4KiBごとにデータを区切って転送する必要があります。例えば16bitカラーの場合は10回、18bit(24bit)の場合は15回転送することになります。

画像データの変換

この液晶のコントローラは12・16・18bitの色深度に対応しています。それぞれ4096色、65536色、262,143色の表示が可能です。

それぞれ、1.5バイト、2バイト、2.25バイトのデータ長ですので2バイトである16bitデータが使い勝手がいいように見えますが、16bitのBMPファイルは非公式で、Photoshopなどでは作れるようですがMSペイントでは作れません。なので24bitから変換するのが手っ取り早いと思います。

bit

  • 12bitの場合(一応)
    12bitカラーの場合は各色4bit 合計12bitで表示されます。
    この色の場合は1バイト目がRG・2バイト目がBと次のピクセルのR、次がGB…と関係が微妙なのと、流石に色数が少なすぎるのでこのモードは使いません。余程メモリが足りないマイコン以外は使う機会はないでしょう。
    しかし、僕らは8色や16色で頑張っていた頃や、256で何とかしていた頃もあるんだ。あの頃にくらべたら4096色なんてフルカラーみたいなもんだよな。
  • 16bit
    ちょっと昔のパソコンの標準的なカラーだった16bit。RとBが5bit、Gが6bitで表示され、最大65536色表示できるモード。ちなみに緑が1bit多いのは人間の目の特性によるもの。このモードだと1ドットあたり丁度2バイトで表現できるので扱いが簡単ですね。
    24bitを16bitに変換する必要がありますが、計算式でどうにかなるのでそこまで手間ではありません。ラズパイの場合は1GBもの広大なメモリがあるので気になりませんが、少ないメモリのマイコンでもメジャーなものはこのモードは最低表示できると思います。
  • 18bit
    携帯電話で使われていたことの多い26万色表示が可能なモードです。安い液晶の6bit+FRCのモニタも液晶パネル自体は18bitになっています。 このモードの場合は26万色表示できますので、8bitフルカラーの1600万色よりは劣りますが、比較的自然な表示が可能です。このモードの場合、一見一番中途半端なデータ長になると思われますが、このコントローラは18bitモードの場合、各色8bit転送の後、上位6bitのみ使用するという方法をとっているので、細かいことを気にしないのであれば24bitBMPファイルをそのまま転送すれば表示できるので実は一番楽な方法です。

というわけで今回は16bitと18bitを使う事を前提として話を進めていきます。

もしかしたら液晶モジュールによっては16bitのみの場合もあるとおもいますので、どちらが使えるかは液晶の仕様書を見て確認して下さい。流石に12bitモードのみというモジュールは現在新品で入手できるものは存在しないと思います。

コーディング

SPI接続の液晶なので、基本的なコードは白黒液晶のものをそのまま使えます。
というかほぼそのままで動くはずです。あとは初期化コードと転送作法をこの液晶に合わせてやればいいだけです。だけですが、せっかくなので液晶に合わせたコードにしたいと思います。

最初のライブラリインクルード~main変数までの部分は、以下のとおりに変更しました。

static uint8_t SPIMode = 0,SPIbit=8;   //SPIモード0 bit数8
static uint32_t SPISpeed = 32000000;  // 32MHz
int lcd_bf = 4096; // 1度に転送するデータ数(最大4096)

// SPIデバイス定義
// # ls -l /dev/spidev* で確認できる。
// Pi2の場合CE1に接続した場合は0.0
//          CE2に接続した場合は0.1 だった。
static char *SPIDevice = "/dev/spidev0.0";
int fd,ret=0;

// 液晶解像度定義 _w=横 _h=縦 bpp=18bitor16bit
long dot_w = 128,dot_h = 160,bpp=18 ;

int LCD_Cmd(unsigned char,int,...);  // <-追加

この液晶はSPIモード0、8bit、32MHzで初期化します。(転送速度は60MHzくらいまで対応しているみたい。ちなみに30fpsで15MHzくらい必要な模様。)

今回液晶のサイズの他に色深度と、コマンド転送関数のプロトタイプを宣言しています。

コマンド送信部の作成

前回のコマンドでは、データ送信とコマンド送信で分けましたが。今回の液晶は基本的にコマンド+設定値データ という仕様なので設定コマンド+データ送信関数、データ送信関数、コマンド送信関数。の3つに分けたいと思います。

まずコマンド・データ単体の送信関数を作成します。

// LCDへのデータ転送
// TrByte=転送データサイズ
// TrData=転送するデータ
//         8bit値 うまく転送できない場合はchar型の変数を渡すとうまくいく。
// この液晶は受信専用なのだが今後のために一応送受信可能に
// しておく。
int DataSend(int TrByte,unsigned char TrData[]){
	digitalWrite(RSpin,1); // RS信号切替
	uint8_t ReData[TrByte];//受信用変数
	
	// 転送するデータの準備
	struct spi_ioc_transfer tr={
	.tx_buf       =(unsigned long)TrData,
	.rx_buf       =(unsigned long)ReData,
	.len          = TrByte,
	.delay_usecs  = 1,
	.speed_hz     = SPISpeed,
	.bits_per_word= SPIbit,
	};
	
	
	// 転送
	ret =ioctl(fd,SPI_IOC_MESSAGE(1),&tr);
	if(ret<1)printf("SPI 転送エラー(Data)\n");
	
	int i=0;
	
	return ret; // 受信データサイズを返す
}

// コマンド転送
int CmdSend(unsigned long TrData){
	digitalWrite(RSpin,0); // RS信号切替
	uint8_t ReData;        //受信用変数
	// 転送するデータの準備
	struct spi_ioc_transfer tr={
	.tx_buf       =(unsigned long)&TrData,
	.rx_buf       =(unsigned long)&ReData,
	.len          = 1,
	.delay_usecs  = 1,
	.speed_hz     = SPISpeed,
	.bits_per_word= SPIbit,
	};
	
	// 転送
	ret =ioctl(fd,SPI_IOC_MESSAGE(1),&tr);
	if(ret<1)printf("SPI 転送エラー(Com)\n");

	return ReData; // 受信データを返す
}

といってもこれは変数名が変わっただけで前のものと同一です。
次に設定値のあるコマンドを送信するための関数を作成します。

int LCD_Cmd(unsigned char TrCmd,int TrByte, ...){
	int dti=0;
	unsigned char TrData[TrByte];
	
	va_list dt;
	va_start(dt,TrByte);
	for(dti=0;dti<TrByte;dti++)TrData[dti]=va_arg(dt,int);
	va_end(dt);
	
	CmdSend(TrCmd);	
	if(TrByte > 0)DataSend(TrByte,TrData);
}

これはコマンド1バイトと、設定値データのバイト数、可変引数にてその設定値を転送、
その受け取った設定値データを配列に突っ込んでそれをデータ転送コマンドに投げています。

その命令を用いて作成した液晶初期化関数がこちらになります。

int LCD_init(void){
	// 液晶リセット信号送信
	digitalWrite(REpin,0);
	delay(1);
	digitalWrite(REpin,1);
	delay(1);
	
	//液晶初期化コマンド送信
	CmdSend(0x01); // ソフトリセット
	delay(15);
	CmdSend(0x11); // スリープ解除
	delay(10);
	
	// フレームレート設定
	LCD_Cmd(0xB1,3,0x01,0x2C,0x2D);
	LCD_Cmd(0xB2,3,0x01,0x2C,0x2D);
	LCD_Cmd(0xB3,6,0x01,0x2C,0x2D,0x01,0x2C,0x2D);
	
	// 液晶反転設定
	LCD_Cmd(0xB4,1,0x07);

	// 電源設定(PWCTR1~3)
	LCD_Cmd(0xC0,3,0xA2,0x02,0x84);
	LCD_Cmd(0xC1,1,0xC5);
	LCD_Cmd(0xC2,2,0x0A,0x00);
	LCD_Cmd(0xC3,2,0x8A,0x2A);
	LCD_Cmd(0xC4,2,0x8A,0xEE);
	LCD_Cmd(0xC5,1,0x0E);
	CmdSend(0x20);
	// ガンマ設定
	LCD_Cmd(0xE0,16,0x0F,0x1A,0x0F,0x18,0x2F,0x28,0x20,0x22,0x1F,0x1B,0x23,0x37,0x00,0x07,0x02,0x10);
	LCD_Cmd(0xE1,16,0x0F,0x1B,0x0F,0x17,0x33,0x2C,0x29,0x2E,0x30,0x30,0x39,0x3F,0x00,0x07,0x03,0x10);
	CmdSend(0x29);
	CmdSend(0x13);	
	
	}

設定値は液晶についてきたサンプルを参考にしました。

そしてこの後、main関数にてBMPを読み込んで…としますがもうすべてのプログラムを乗せますね。

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <wiringPi.h>

#define RSpin 5 // レジスタ選択ピン指定
#define REpin 6 // リセット信号ピン指定

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

static uint8_t SPIMode = 0,SPIbit=8;   //SPIモード0 bit数8
static uint32_t SPISpeed =60000000;  // 32MHz
int lcd_bf = 4096; // 1度に転送するデータ数(最大4096)

// SPIデバイス定義
// # ls -l /dev/spidev* で確認できる。
// Pi2の場合CE1に接続した場合は0.0
//          CE2に接続した場合は0.1 だった。
static char *SPIDevice = "/dev/spidev0.0";
int fd,ret=0;

// 液晶解像度定義 _w=横 _h=縦 bpp=18bitor16bit
long dot_w = 128,dot_h = 160,bpp=18 ;

int LCD_Cmd(unsigned char,int,...);



int main(int argc,char *argv[]){
unsigned char madctr,bpps[19]={0,};bpps[16]=0x05;bpps[18]=0x06;
	
	
	if(argc == 3) bpp=atoi(argv[2]);
	FILE *bmpr;
	
	if(19<=bpp) {printf("カラーモード指定エラー\nカラーモードは16/18 ビットに対応しています。\n");exit(1);}
	if(bpps[bpp] == 0) {printf("カラーモード指定エラー\nカラーモードは16/18 ビットに対応しています。\n");exit(1);}
	
	//BMPファイル読み込み
	unsigned char b_rs[4];
	unsigned long b_ty,b_fs,b_of,b_hs;
	long b_px[2];
	unsigned short b_bit,b_pr;
	if((bmpr=fopen(argv[1],"rb"))==NULL){printf("ファイル読み込みエラー\n");exit(1);}
	
	// BMPファイルのヘッダを読み込み
	fread(&b_ty,2,1,bmpr);  // ファイルタイプ(BM(0x4D42)ならBMP)*
	fread(&b_fs,4,1,bmpr);  // ファイルサイズ(Byte)*
	fread(b_rs,1,4,bmpr);   // 予約領域
	fread(&b_of,4,1,bmpr);  // 画像データまでのオフセット値 *
	fread(&b_hs,4,1,bmpr);  // 情報ヘッダーサイズ *
	fread(b_px,4,2,bmpr);   // 画像サイズ(ピクセル数 [0]=W [1]=H) *
	fread(&b_pr,2,1,bmpr);  // プレーン数(常に1)
	fread(&b_bit,2,1,bmpr); // ピクセルあたりのビット数(1 4 8 16 24 32)*
	
	// ↑*=使用するデータ プレーン数と予約領域は使用しない。(常に1と0なので)
	

	printf("****************************\n");
	printf("ファイルサイズ : %d Byte\n",b_fs);
	printf("画像オフセット : %d Byte\n",b_of);
	printf("ヘッダサイズ  : %d Byte\n",b_hs);
	printf("画像サイズ(WxH): %d x %d\n",b_px[0],b_px[1]);
	printf("ビット深度   : %d\n",b_bit);
	printf("****************************\n\n");

	// ファイルの形式チェック
	if(b_ty!=0x4D42){printf("ビットマップファイルを使用してください。\n");exit(1);}
	// ファイルのビット深度チェック
	if(b_bit!=24) {printf("24ビット ビットマップを使用してください。\n");exit(1);}
	// 画像サイズチェック
	// 実はこれだと例えば640x32 とかいう画像でも通るが(゚ε゚)キニシナイ!!
	if(b_px[0]*b_px[1]!=dot_h*dot_w) {printf("%dx%dpxの画像を使用してください。\n",dot_h,dot_w);exit(1);}
	
	unsigned char BMPData[(int)b_fs-(int)b_of],BMPData2[(dot_w*dot_h*((bpp+7)/8))/lcd_bf][lcd_bf];
	
	// データ本体読み込み
	fseek(bmpr,b_of,SEEK_SET);
	fread(BMPData,1,(int)b_fs-(int)b_of,bmpr);
	fclose(bmpr);
	
	// BMPファイルの長辺に合わせて回転
	madctr=0x28+(0x20*(b_px[1]>b_px[0]));
		

	// wiringPi初期化
	if(wiringPiSetup()==-1) printf("wiringPi初期化エラー\n");
	
	// ピンの初期化
	pinMode(RSpin,OUTPUT);
	pinMode(REpin,OUTPUT);
	
	// 液晶リセット信号送信
	digitalWrite(REpin,0);
	delay(100);
	digitalWrite(REpin,1);
	delay(100);
	
	
	// SPIデバイスの初期化
	fd = open(SPIDevice,O_RDWR);if(fd<0) printf("SPI デバイス初期化エラー\n");
	ret = ioctl(fd,SPI_IOC_WR_MODE,&SPIMode);if(ret <0) printf("SPI Mode設定エラー\n");
	ret =ioctl(fd,SPI_IOC_WR_BITS_PER_WORD,&SPIbit);if(ret <0) printf("SPI bit/Word設定エラー\n");
	ret =ioctl(fd,SPI_IOC_WR_MAX_SPEED_HZ,&SPISpeed);if(ret <0) printf("SPI Speed設定エラー\n");

	if(ret <0) exit(1);

	LCD_init();

	// 液晶描画方向設定
	LCD_Cmd(0x36,1,madctr); // 縦長 0x48 横長0x28
	
	
	// 描画エリア設定 0~BMPのサイズ
	LCD_Cmd(0x2A,4,0x00,0x00,0x00,b_px[0]);
	LCD_Cmd(0x2B,4,0x00,0x00,0x00,b_px[1]);
	LCD_Cmd(0x3A,1,bpps[bpp]);  //色深度設定 16bit=0x05 18bit=0x06

	
	// 表示RAM転送モードに設定
	CmdSend(0x2C);
	int i,bix;
	// 色深度16bitの場合16bitに変換し、バッファサイズで区切って配列に入れる。
	// 参考サイト:https://raspberrylife.wordpress.com/2013/12/03/color-lcd2/
	if(bpp==16){
		for (bix=0;bix<b_px[0]*b_px[1];bix++){
			BMPData2[bix/(lcd_bf/2)][bix%(lcd_bf/2)*2] =BMPData[bix*3] & 0xF8 |BMPData[bix*3+1]>>5;
			BMPData2[bix/(lcd_bf/2)][bix%(lcd_bf/2)*2+1] =BMPData[bix*3+1] & 0xFC <<3  | BMPData[bix*3+2] >>3;
		}}
	// 色深度18bitの場合はそのまま転送
	// 液晶コントローラが18bitの場合は8bit中各色上位6bit(D7~D2)を
	// 使用するので変換せずにバッファサイズで区切って配列に入れる。
	if(bpp==18)for(bix=0;bix<(int)b_fs-(int)b_of;bix++)BMPData2[bix/lcd_bf][bix%lcd_bf] = BMPData[bix];

	// 配列を1バッファごとに転送する
	int max =ARRAY_SIZE(BMPData2);
	for(i=0;i<max;i++)DataSend(lcd_bf,BMPData2[i]);
	
	close(fd); // SPIデバイス開放
	
	return 0;
	}
	

// LCDへのコマンド転送
// LCD_Cmd(コマンド,設定値サイズ(Byte),設定値(カンマ区切り));
// コマンドが0x29 設定値が 0xBE 0xEF 0x01 0x41(4バイト)なら
//   LCD_Cmd(0x29,4,0xBE,0xEF,0x01,0x41);
// 設定値がないコマンドは上の例なら
//   LCD_Cmd(0x29,0);
// もしくは、このコマンドは受け渡された引数をDataSend、CmdSendの各
// コマンドに分裂させて渡しているだけなので直接CmdSendを叩いて
//   CmdSend(0x29);
// のほうがスッキリします。
int LCD_Cmd(unsigned char TrCmd,int TrByte, ...){
	int dti=0;
	unsigned char TrData[TrByte];
	
	va_list dt;
	va_start(dt,TrByte);
	for(dti=0;dti<TrByte;dti++)TrData[dti]=va_arg(dt,int);
	va_end(dt);
	
	CmdSend(TrCmd);	
	if(TrByte > 0)DataSend(TrByte,TrData);
}

// LCDへのデータ転送
// TrByte=転送データサイズ
// TrData=転送するデータ
//         8bit値 うまく転送できない場合はchar型の変数を渡すとうまくいく。
// この液晶は受信専用なのだが今後のために一応送受信可能に
// しておく。
int DataSend(int TrByte,unsigned char TrData[]){
	digitalWrite(RSpin,1); // RS信号切替
	uint8_t ReData[TrByte];//受信用変数
	
	// 転送するデータの準備
	struct spi_ioc_transfer tr={
	.tx_buf       =(unsigned long)TrData,
	.rx_buf       =(unsigned long)ReData,
	.len          = TrByte,
	.delay_usecs  = 1,
	.speed_hz     = SPISpeed,
	.bits_per_word= SPIbit,
	};
	
	
	// 転送
	ret =ioctl(fd,SPI_IOC_MESSAGE(1),&tr);
	if(ret<1)printf("SPI 転送エラー(Data)\n");
	
	int i=0;
	
	return ret; // 受信データサイズを返す
}

// コマンド転送
int CmdSend(unsigned long TrData){
	digitalWrite(RSpin,0); // RS信号切替
	uint8_t ReData;        //受信用変数
	// 転送するデータの準備
	struct spi_ioc_transfer tr={
	.tx_buf       =(unsigned long)&TrData,
	.rx_buf       =(unsigned long)&ReData,
	.len          = 1,
	.delay_usecs  = 1,
	.speed_hz     = SPISpeed,
	.bits_per_word= SPIbit,
	};
	
	// 転送
	ret =ioctl(fd,SPI_IOC_MESSAGE(1),&tr);
	if(ret<1)printf("SPI 転送エラー(Com)\n");

	return ReData; // 受信データを返す
}

int LCD_init(void){
	// 液晶リセット信号送信
	digitalWrite(REpin,0);
	delay(1);
	digitalWrite(REpin,1);
	delay(1);
	
	//液晶初期化コマンド送信
	CmdSend(0x01); // ソフトリセット
	delay(15);
	CmdSend(0x11); // スリープ解除
	delay(10);
	
	// フレームレート設定
	LCD_Cmd(0xB1,3,0x01,0x2C,0x2D);
	LCD_Cmd(0xB2,3,0x01,0x2C,0x2D);
	LCD_Cmd(0xB3,6,0x01,0x2C,0x2D,0x01,0x2C,0x2D);
	
	// 液晶反転設定
	LCD_Cmd(0xB4,1,0x07);

	// 電源設定(PWCTR1~3)
	LCD_Cmd(0xC0,3,0xA2,0x02,0x84);
	LCD_Cmd(0xC1,1,0xC5);
	LCD_Cmd(0xC2,2,0x0A,0x00);
	LCD_Cmd(0xC3,2,0x8A,0x2A);
	LCD_Cmd(0xC4,2,0x8A,0xEE);
	LCD_Cmd(0xC5,1,0x0E);
	CmdSend(0x20);
	// ガンマ設定
	LCD_Cmd(0xE0,16,0x0F,0x1A,0x0F,0x18,0x2F,0x28,0x20,0x22,0x1F,0x1B,0x23,0x37,0x00,0x07,0x02,0x10);
	LCD_Cmd(0xE1,16,0x0F,0x1B,0x0F,0x17,0x33,0x2C,0x29,0x2E,0x30,0x30,0x39,0x3F,0x00,0x07,0x03,0x10);
	CmdSend(0x29);
	CmdSend(0x13);	
	
	}

BMPファイル読み込み→ヘッダを読み込み→ヘッダ情報からBMPファイルのフォーマットチェック。→その値にあわせて描画領域などを設定→液晶を初期化→必要なら16bitに変換→データ転送

という流れになっているはずです。

実行

コンパイル方法も以前の液晶と同じです。それをコンパイルしたものに以下の画像データを読み込ませた場合にトップ画像と同じ表示になると思います。

logoc

 

 

IMG_4769_20150726

 

少々駆け足で進めてきましたが、実際の扱いは結構簡単で特に書くことはないです。
注意しないといけないところは、液晶の仕様をよく理解してから使用するというところくらいで。
データも24bitBMPをそのまま転送することで表示ができますし。縦向きに8bitごとに区切られていることの多いモノクロ液晶と比べると多くの画像ファイルを展開後そのまま転送でき、エリア指定をすればそのままデータを流せばいいのでフォントデータの取り扱いも楽です。

だからもしかするとグラフィック液晶はモノクロ液晶を採用するより最初からカラーにしてしまったほうが楽かもしれません。Raspberry Piみたいなあまりメモリ容量の厳しくなく、演算能力も高いマイコンの場合は特に。

 

  • コンパイルなどでエラーが出ないのに当プログラムが動かない場合について
    このコードで最新のRaspberryPi用OSを利用すると日本語関係の処理で躓いてうまく動作しない場合があるようです。もしうまく動作しない場合はprintfで出力される文字をすべてアスキー文字(半角英数字)に変更してみてください。
総閲覧数:1,245 PV

“Raspberry Pi 2でカラーグラフィック液晶の制御” への11件のフィードバック

  1. […] 「サインスマート 1.8″ LCDをArduinoに表示させる」で サインスマート 1.8″ LCDをArduinoで表示させましたが、今度は、Raspberry PI 3で表示させます。 参考にさせてもらったサイトは、「Raspberry Pi 2でカラーグラフィック液晶の制御」です。 […]

  2. 桐山建二 より:

    Sakura87 さま

     早速の御返答有難う御座います。更に、本編にも追加頂き有難う御座います。

    OSの日本語環境設定はPI2用OS(jessie 4.1.13)とPI2&3用(jessie 4.4.21)は同様な設定にしています。

    ロケール(言語と地域)の選択の①で「jp_JP.UTF-8 UTF-8」選択、更に
    ロケール(言語と地域)の選択の②で「jp_JP.UTF-8」を選択して決定しています。
    尚、Libre Office、Text Editor等は日本語環境で使用しています。

     当方では双方のOSで前の記事のようにLXterminal画面上での全角文字ので出来ませんでした。

    しかもPI2&3用(jessie 4.4.21)ではサインスマート!.8″LCDの表示の障害になっているようです。
    そのためにエラー発生の箇所が判読出来ず、其方にお訊ねする事になってしまい、ご迷惑をおかけしました。

      今後も宜しくお願い致します。         桐山建二

     

    • Sakura87 より:

      桐山建二さま。

      了解しました。また何かありましたらその際はよろしくお願いします。

  3. 桐山建二 より:

    Sakura87 さま

     早速のアドバイス有難う御座いました。

     結論から申しますと、75行と77行と80行をコメントアウトしてコンパイルし直して正常に表示出来ました。
    しかし、新しいPI2&3用OS(jessi4.4.8又はjessie4.4.21)では全角でエラーになりPI2用OS(jessi4.1.13)
    ではエラーとならないのかは疑問がが残ります。

     PI2用OS(jessi4.1.13)で液晶への表示が上手く表示できても、モニターのLXterminalの全角表示(66行から72行)
    は全角文字は表示出来ません。勿論新しいPI2&3用OS(jessi4.4.8又はjessie4.4.21)でも同様です。

     今回は貴重なアドバイス有難う御座いました。

     今後も宜しくお願い致します。         桐山建二

    • Sakura87 より:

      桐山建二さま
      こん○○は

      表示できてよかったです。
      なるほど、日本語関係の処理で躓いていたのですね。
      ところで新しいOSの方は日本語環境の構築はお済でしょうか。
      もしまだでしたら日本語環境を構築することで改善がみられるかもしれません。

      一応このことは本編にも注釈として書き加えておきます。

      それでは。

  4. 桐山建二 より:

    Sakura87 さま

     早速の御返答有難う御座います。

    中国製の安価ななロジックアナザイラーが有りますが、殆ど使ったことがありません。
    倉庫から取り出してマニュアルを読んでいるところです。

    「http://tomosoft.jp/design/?p=7944」このサイトのサインスマート!.8″LCD
    表示は正常に表示できました。
    また、SPI接続のAD変換IC(MPC3204)を使った温度測定・表示もOKでした。

     WiringPiについてはPi3&3用(ファイル数274、サイズ1.3MB)とPi2用(ファイル数247、サイズ1.1MB)
    入れ替えてコンパイルしてみましたがNGでした。

     ロジックアナザイラーの勉強を兼ねて「正常動作」と「表示NG」を比較から始めてみようと思っています。

     もしお時間ありましたら引き続きアドバイスお願い致します。   桐山建二

    • Sakura87 より:

      桐山建二さま

      こん○○は。

      成功した例のプログラムと当方のプログラムを比較して関係のありそうな部分としては当方のプログラムの113行目にあります

      if(ret <0) exit(1);

      というところが関係してそうな感じです。

      これはエラーが出ていたらプログラムを終了するというコードなのですが。
      もしかするとSPIライブラリがエラーだった時の出力が変わっているのかもしれません。
      この行を削除もしくはこの前後に何らかのメッセージを表示する命令を追加して例えば以下のように変更してみてどうなるかを確認したほうがいいかもしれません。

      printf("TEST1");
      if(ret <0) exit(1);
      printf("TEST2");

      これでTEST1のみ出力されるのであればこの命令を削除してどうなるか、TEST2まで出力されるのであれば、恐らくBMPファイルの入出力がうまくいっていない可能性があります。
      このあたりも確認してみてください。

      それでは。

  5. 桐山建二 より:

    Sakura87 さま

     早速の御返答有難う御座います。

    使用している基板は「RaspberryPI2」で「OS(SDカード)」のみ差し替えて実行しています。
    尚、「RaspberryPI3」基板でも「OS、jessi4.4.21(Raspberry PI2&PI3用OS)」表示は出来ません。

    コンパイル「gcc -o SPI_Coloure SPI_Coloure.c -l wiringPi」は正常に終了しているようにみえます。
    実行「sudo ./SPI_Coloure ./logoc.bmp」の実行では液晶に全く何も変化が無く表示も無し。

    「WiringPi」が「Pi3」向けに変更された部分が良く分らないので「Gordon」さんのサイトから
    最新版ダウンロードしようと試みましたが、「’wiringPi’ already exists and is not a empty directory」
    と表示して入手出来ませんでした。

     更に続けてみる積もりですが、勝手なお願いですが、もしお時間ありましたら引き続きアドバイスお願い致します。

     桐山建二

    • Sakura87 より:

      お返事ありがとうございます。

      特に何のエラーもなく表示が行えない場合なのですが。そこまでの事態になるとそもそもSPIとdigitalWriteの命令で正常にI/Oが行われているかどうかというのをロジックアナライザなどで確認する必要があると思います。(ロジックアナライザがないのであればLEDが光るか・点滅するかどうかである程度代用ができると思います)

      それとWiringPiの入手ですが。gitコマンドで入手しているのであればこのあたりが参考になるかもしれません。

      http://blog.dksg.jp/2015/05/git-clone.html

      ただ、WiringPiはすでに1年近く更新されていないみたいなのであまり意味がない作業かと思われます。(そもそも入手の段階で失敗している可能性を考えるなら妥当ですが)

      ちなみにこのプログラム以外のSPIを制御するプログラム(できれば別の作者のもの)は正常に動作するのでしょうか?
      もし動作するのであれば当プログラムの問題、動作しないのであればOS側の問題となると思います。

  6. 桐山建二 より:

    この記事非常に参考になりました、有難うございました。

    「RaspberryPI2」上で「OS、jessi4.1.13(RaspberryPI2用OS)」でこのプログラム、
    「Raspberry Pi 2でカラーグラフィック液晶の制御」は綺麗に表示されます。

    「RaspberryPI2」上で「OS、jessi4.4.21(Raspberry PI2&PI3用OS)」でこのプログラム
    「Raspberry Pi 2でカラーグラフィック液晶の制御」表示できませんでした。

    何らかの対策ありましたら、お教え頂きたくメールいたしました。  桐山建二

    • Sakura87 より:

      コメントありがとうございます。

      新しいOSで等プログラムが動作しないとのことですが。

      どのあたりでエラーが出ているか、もしくはエラーが出ていないのか。コンパイルはできているのかなど、少々情報が少ないので断言できませんが。
      ラズペリーパイの開発元曰くPi2とPi3は互換性があるそうなので、動かないとすれば

       ・SPIのデバイス名が変わっていないか

      というのを一番に確認するべきだと思います。
      あとはSPIのI/O回りですが。こちらは微妙な仕様変更がなければOSのライブラリを直接叩いていますので問題があるとは考えられず。(ゼロとは言えないが高くはない)

      残る可能性としてはWiringPiがPi3向けに変更された部分が影響して対応しなくなったという可能性が考えられますので、digitalWriteなどのWiringPi固有の命令を標準I/Oに置き換えるなどしてみてどうなるかというところです。

      お役に立てれば幸いです。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください