SHARP製の2.7インチHR-TFTメモリ液晶がうまく動かない件について

秋月電子で売っている中小企業に成り下がったシャープさんのメモリ液晶を購入し、
mbedRaspberryPiからサクサク動かせるものを作ろうと考えていました。
データシートを見る限り、台湾製液晶にありがちな初期化コードもなく
SPI通信でパパッとできると考えていたのですが・・・実際はそうでもないようです。

なんで動かないのかさっぱりわかりません・・・。
追記にコードもすべて載せていますので、
どなたか何かご存じでしたらYoutubeでもこのブログでもいいのでコメントをいただけませんか?

解決しました!!(2015/06/28)
SHARP製の2.7インチHR-TFTメモリ液晶がうまく動かない件について(2)
mbed用のライブラリも書きました!(2015/12/11)
秋月で売っているSHARPのモノクロメモリ液晶を動かすライブラリ作った


起こっている現象とやってみたこと

  • 電源を投入して、コマンドを発行しても何も起こらない
  • SPIの周波数を100KHzから10MHzまで試したけど動かない
  • SPIのクロックを上げっぱなしにすると、反応する(奇怪な動作をする)
  • もう一個同じものを購入して試しても一緒(初期不良じゃない)
  • ロジックアナライザで見る限り正常に通信できている
  • つなげるブレークアウト基板を購入しても、やっぱり動かない
  • むしろブレークアウト基板のデモコードすら動かない
  • 処理が早すぎるのかと思ってWAITをかけまくってもかわらない
  • mbed側(Nucleo F401RE)が壊れたのかと思って、RaspberryPiでやってみても変わらない

一通りまわりのブログも調べましたが、みんな完璧にうごいてドヤ顔で公開しています。
なんなんでしょう・・・この差。

環境

親機
ST NucleoF401RE、 Raspberry Pi2 Model B
子機
秋月電子 フレキコネクタDIP化キットみんなのラボ メモリ液晶ブレークアウト基板
液晶
SHARP(シャープ) HR-TFT memory LCD (2.7inch 400×240 メモリ液晶)
通信環境
SPI通信 1MHz、モード0(CPOL=0,CPHA=0) MISO信号は接続してもしなくても変化せず

画像とか

sharpMlcd_pic1.png
sharpMlcd_pic2.png
sharpMlcd1.png
sharpMlcd2.png
sharpMlcd4.png

コード

mbed(Nucleo)

SharpMemoryLCD.h

#include "mbed.h"
#ifndef SHARP_MLCD
#define SHARP_MLCD
class SHARPmLCD{
private:
SPI *si;
DigitalOut *cs;
bool invert;
unsigned int width, height;
unsigned int curX, curY;
unsigned int posX, posY;
Ticker tk1;
bool flipVal;
unsigned char *buf;
bool isValid;
unsigned char lsbf(unsigned char data);										//ビット列を逆にする(__rbit()と同等)
public:
SHARPmLCD(SPI *i, DigitalOut *csel, bool normallyWhite, int DispWidth, int DispHeight);
~SHARPmLCD();
void COM_flip();															//COM反転信号
void init();																//初期化
void init(bool spiInit);													//初期化
void cls();																	//クリア
void clear();																//クリア(alias)
void setPixel(unsigned int x, unsigned int y, int mode);					//打点
//left, topの位置から、right, bottomの位置に1ドット幅の線を指定したmodeで描画
void drawLine(
unsigned int left, unsigned int top,
unsigned int right, unsigned int bottom,
unsigned int mode
);
//left, topの位置からright, bottomの位置に対角線を持つ1ドット幅の短径を指定したmodeで描画
void drawRect(
unsigned int left, unsigned int top,
unsigned int right, unsigned int bottom,
unsigned int mode
);
//left, topの位置からright, bottomの位置に対角線を持つ塗りつぶされた短径を指定したmodeで描画
void fillRect(
unsigned int left, unsigned int top,
unsigned int right, unsigned int bottom,
unsigned int mode
);
};
#endif

SharpMemoryLCD.cpp

#include "SharpMemoryLCD.h"
//ショートカット
#define FLIP_TIME			0.3						//COM反転信号周期
#define CS0					cs->write(0)			//CS立ち下げ
#define CS1					cs->write(1)			//CS立ち上げ
#define WWAIT				wait_us(5)				//待ち処理
#define INVALID_CHECK		if(!isValid)return		//バッファーが確保できているか
//ビット列を逆に並び替える(01101110 -> 01110110)
unsigned char SHARPmLCD::lsbf(unsigned char data){
data = (data&0xAA)>>1 | (data&0x55)<<1;
data = (data&0xCC)>>2 | (data&0x33)<<2;
data = (data&0xF0)>>4 | (data&0x0F)<<4;
return data;
}
//コンストラクタ
SHARPmLCD::SHARPmLCD(SPI *i, DigitalOut *csel, bool normallyWhite, int DispWidth, int DispHeight)
:si(i), cs(csel), invert(!normallyWhite), width(DispWidth), height(DispHeight){
curX = curY = posX = posY = 0;
flipVal = false;
CS0;
//バッファーサイズを確保(width/8 * height)
if(width>8 && height>8){
buf = new unsigned char[(width>>3)*height]();
isValid = !(buf == NULL);
}
}
//デストラクタ
SHARPmLCD::~SHARPmLCD(){
tk1.detach();
delete[] buf;
}
//COM反転処理
void SHARPmLCD::COM_flip(){
WWAIT;
//コマンド0b01000000(0x40)を送る
CS1;
WWAIT;
si->write(flipVal?0x40:0);
si->write(0);
WWAIT;
CS0;
WWAIT;
//COM反転状態をひっくり返す
flipVal = !flipVal;
}
//初期化(引数なし)
void SHARPmLCD::init(){
init(true);
}
//初期化(引数有)
void SHARPmLCD::init(bool spiInit){
INVALID_CHECK;
if(spiInit){
si->format(8, 0);	//SPIモード 8bit, CPOL=0 CPHA=0
si->frequency(100000);	//SPIクロック 1MHz(MAX 2MHz)
}
//すべてクリア
cls();
//COM反転をアタッチ
tk1.attach(this, &SHARPmLCD::COM_flip, FLIP_TIME);
}
//表示クリア
void SHARPmLCD::cls(){
//COM反転を一旦止める
tk1.detach();
//コマンド0b00100000(0x20)とCOM反転信号を送る
CS1;
WWAIT;
si->write(0x20|(flipVal?0x40:0));
si->write(0);
WWAIT;
CS0;
//COM反転状態をひっくり返す
flipVal = !flipVal;
//再度COM反転処理をアタッチ
tk1.attach(this, &SHARPmLCD::COM_flip, FLIP_TIME);
}
//表示クリア:エイリアス
void SHARPmLCD::clear(){
cls();
}
//打点(1ドットの点を打つ)
void SHARPmLCD::setPixel(unsigned int x, unsigned int y, int mode){
INVALID_CHECK;
x = (x>width-1)? width-1:x;
y = (y>height-1)? height-1:y;
int line = y*(width>>3);
int offset = line+(x>>3);
switch(mode){
case 0: //Erase
buf[offset] &= 0xFF^(1 << (7-(x%8)));
case 1: //Write
buf[offset] |= 1 << (7-(x%8));
case -1: //Invert
buf[offset] ^= 1 << (7-(x%8));
}
tk1.detach();
CS1;
WWAIT;
si->write(0x80|(flipVal?0x40:0));
si->write(lsbf(y+1));
for(char i=0,w8=width>>3;i<w8;i++) si-="">write(buf[line+i]);
si->write(0x00);
si->write(0x00);
WWAIT;
CS0;
flipVal = !flipVal;
tk1.attach(this, &SHARPmLCD::COM_flip, FLIP_TIME);
}
//未完成
void SHARPmLCD::drawLine(unsigned int left, unsigned int top, unsigned int right, unsigned int bottom, unsigned int mode){
INVALID_CHECK;
tk1.detach();
flipVal = !flipVal;
tk1.attach(this, &SHARPmLCD::COM_flip, FLIP_TIME);
}
//未完成
void SHARPmLCD::drawRect(unsigned int left, unsigned int top, unsigned int right, unsigned int bottom, unsigned int mode){
INVALID_CHECK;
tk1.detach();
flipVal = !flipVal;
tk1.attach(this, &SHARPmLCD::COM_flip, FLIP_TIME);
}
//未完成
void SHARPmLCD::fillRect(unsigned int left, unsigned int top, unsigned int right, unsigned int bottom, unsigned int mode){
INVALID_CHECK;
tk1.detach();
flipVal = !flipVal;
tk1.attach(this, &SHARPmLCD::COM_flip, FLIP_TIME);
}
</w8;i++)>

main.cpp

#include "mbed.h"
#include "SharpMemoryLCD.h"
SPI spi1(SPI_MOSI, SPI_MISO, SPI_SCK);
DigitalOut cs1(D2);
int main(){
	wait(5);
	SHARPmLCD lcd(&spi1, &cs1, true, 400, 240);
	lcd.init();
	wait(5);
	lcd.setPixel(128,105,1);
	lcd.setPixel(129,105,1);
	lcd.setPixel(130,105,1);
	lcd.setPixel(131,105,1);
	lcd.setPixel(132,105,1);
	lcd.setPixel(128,105,-1);
	lcd.setPixel(129,106,-1);
	lcd.setPixel(130,107,-1);
	lcd.setPixel(131,108,-1);
	lcd.setPixel(132,109,-1);
	wait(5);
	lcd.setPixel(28,115,0);
	lcd.setPixel(29,115,0);
	lcd.setPixel(30,115,0);
	lcd.setPixel(31,115,0);
	lcd.setPixel(32,115,0);
	lcd.setPixel(28,115,1);
	lcd.setPixel(29,116,1);
	lcd.setPixel(30,117,1);
	lcd.setPixel(31,118,1);
	lcd.setPixel(32,119,1);
}

Raspberry Pi2 (wiringPi)

SHARPmLCDutil.h

#include 
#include 
#ifndef SHARPMLCDUTIL_H
#define SHARPMLCDUTIL_H
class SHARPmLCDutil{
private:
	bool invert;
	int curX, curY;
	int posX, posY;
	int spiCH;
	int cs_lcd;
	int cs_eep;
	bool flipVal;
public:
	SHARPmLCDutil(int SPIchannel, int SPIcs_eep, int SPIcs_lcd, bool initUnnecessary);
	void COM_flip();
	unsigned char lsbf(unsigned char data);
	void init();																//初期化
	void cls();																	//表示クリア
	void clear();																//表示クリア(alias)
	void mem_cls();																//メモリクリア
	void mem_clear();															//メモリクリア(alias)
	void writeLine(unsigned int lineNumber, const char *data);					//ゲートライン更新
	void setPixel(unsigned int x, unsigned int y, int mode);					//打点
	void drawLine(	//left, topの位置から、right, bottomの位置に1ドット幅の線を指定したmodeで描画
		unsigned int left, unsigned int top,
		unsigned int right, unsigned int bottom,
		unsigned int mode
	);
	void drawRect(	//left, topの位置からright, bottomの位置に対角線を持つ1ドット幅の短径を指定したmodeで描画
		unsigned int left, unsigned int top,
		unsigned int right, unsigned int bottom,
		unsigned int mode
	);
	void fillRect(	//left, topの位置からright, bottomの位置に対角線を持つ塗りつぶされた短径を指定したmodeで描画
		unsigned int left, unsigned int top,
		unsigned int right, unsigned int bottom,
		unsigned int mode
	);
};
#endif

SHARPmLCDutil.cpp

#include "SHARPmLCDutil.h"
unsigned char SHARPmLCDutil::lsbf(unsigned char data){
	data = (data&amp;0xAA)>>1 | (data&0x55)&lt;&lt;1;
	data = (data&amp;0xCC)>>2 | (data&amp;0x33)&lt;&lt;2;
	data = (data&amp;0xF0)>>4 | (data&amp;0x0F)&lt;&lt;4;
	return data;
}
SHARPmLCDutil::SHARPmLCDutil(int SPIchannel, int SPIcs_eep, int SPIcs_lcd, bool initUnnecessary):
spiCH(SPIchannel), cs_eep(SPIcs_eep), cs_lcd(SPIcs_lcd){
	if(!initUnnecessary){
		wiringPiSPISetup(spiCH, 500000);
	}
	wiringPiSetupGpio();
	pinMode(cs_eep, OUTPUT);
	pinMode(cs_lcd, OUTPUT);
	digitalWrite(cs_eep, 1);
	flipVal = false;
}
void SHARPmLCDutil::COM_flip(){
	digitalWrite(cs_lcd, 1);
	delayMicroseconds(3);
	unsigned char sData[2] = {flipVal?0x40:0x00, 0x00};
	wiringPiSPIDataRW(spiCH, sData, 2);
	delayMicroseconds(3);
	digitalWrite(cs_lcd, 0);
	flipVal = !flipVal;
}
void SHARPmLCDutil::init(){
	digitalWrite(cs_lcd, 1);
	delayMicroseconds(3);
	unsigned char aryData[12482] = {0};
	unsigned char *p;
	p = aryData;
	*p = 0x80;
	for(int line=1, idx=0;line&lt;=240;line++){
		*(++p) = lsbf(line &amp; 0xFF);
		for(int i=0;i&lt;50;i++){
			*(++p) = 0b01011010;
		}
		*(++p) = 0x00;
	}
	*(++p) = 0x00;
	wiringPiSPIDataRW(spiCH, aryData, 12482) ;
	delayMicroseconds(3);
	digitalWrite(cs_lcd, 0);
	flipVal = !flipVal;
}
void SHARPmLCDutil::cls(){
	digitalWrite(cs_lcd, 1);
	delayMicroseconds(3);
	unsigned char sData[2] = {0x20|(flipVal?0x40:0x00), 0x00};
	wiringPiSPIDataRW(spiCH, sData, 2);
	delayMicroseconds(3);
	digitalWrite(cs_lcd, 0);
	flipVal = !flipVal;
}
void SHARPmLCDutil::clear(){
	cls();
}
void SHARPmLCDutil::mem_cls(){}
void SHARPmLCDutil::mem_clear(){}
void SHARPmLCDutil::writeLine(unsigned int lineNumber, const char *data){}
void SHARPmLCDutil::setPixel(unsigned int x, unsigned int y, int mode){}
void SHARPmLCDutil::drawLine(unsigned int left, unsigned int top, unsigned int right, unsigned int bottom, unsigned int mode){}
void SHARPmLCDutil::drawRect(unsigned int left, unsigned int top, unsigned int right, unsigned int bottom, unsigned int mode){}
void SHARPmLCDutil::fillRect(unsigned int left, unsigned int top, unsigned int right, unsigned int bottom, unsigned int mode){}

wiringPinMap.h

#ifndef GPIO_WIRINGPI_FOR_PI2
#define GPIO_WIRINGPI_FOR_PI2
#define IO_0 0
#define IO_1 1
#define IO_2 2
#define IO_3 3
#define IO_4 4
#define IO_5 5
#define IO_6 6
#define IO_7 7
#define IO_8 8
#define IO_9 9
#define IO_10 10
#define IO_11 11
#define IO_12 12
#define IO_13 13
#define IO_14 14
#define IO_15 15
#define IO_16 16
#define IO_17 17
#define IO_18 18
#define IO_19 19
#define IO_20 20
#define IO_21 21
#define IO_22 22
#define IO_23 23
#define IO_24 24
#define IO_25 25
#define IO_26 26
#define IO_27 27
#define IO_28 28
#define IO_29 29
#define I2C_SDA 8
#define I2C_DATA 8
#define I2C_CSL 9
#define I2C_CLK 9
#define SPI_CS 10
#define SPI_CS0 10
#define SPI_CS1 11
#define SPI_MOSI 12
#define SPI_SDO 12
#define SPI_MISO 13
#define SPI_SDI 13
#define SPI_SCK 14
#define SPI_CLK 14
#define UART_TX 15
#define UART_RX 16
#endif

memlcd_testPi.cpp(メイン)

#include 
#include 
#include "SHARPmLCDutil.h"
#include "wiringPinMap.h"
int main(){
	if (wiringPiSetupGpio() == -1) {
		printf("cannot setup gpio. \n");
		return 1;
	}
	printf("1秒待ち\n");
	delay(1000);
	SHARPmLCDutil lcd(0, IO_2, IO_3, false);
	printf("2秒待ち\n");
	delay(2000);
	printf("lcd.init();\n");
	lcd.init();
	printf("5秒待ち\n");
	delay(5000);
	printf("lcd.cls();\n");
	lcd.cls();
	printf("COM FLIP\n");
	for(int i=0;i&lt;500;i++){
		delay(250);
		lcd.COM_flip();
	}
	printf("おわった\n");
	return 0;
}

2 comments to this article

  1. 横山 照久

    on 2015年6月25日 at 10:38 PM - 返信

    私は電子回路は超初心者で、mbedは、なんか面白そうなので購入してみた、という人です。sharpのメモリ液晶に文字を書いて、できればワープロのようなことをしたいと思い、とりあえず、画面の表示方法をネットで検索しましたが、全然わかりませんでした。ブレークアウト基盤にも説明書らしきものはなく、作者のmbedサイトに図が載つていますが、初心者の私には、「は?」って感じでした。
    秋葉原のマルツ電子(だったきがする)に、mbedでメモリ液晶を動かしている展示があり、それを見て、ジャンパー線をどこに繋ぐのかメモし、家でつなぎ、作者のmbedサイト
    mbed.org/users/k4zuki/

    AkiSpiLcd_example
    というところをクリックして、
    「Import this program」というボタンをおし、もともと自分がログインしていた、mbedのコンパイラのページに、プログラムを読みこんで、コンパイルし、
    AkiSpiLcd_example_LPC1768.bin
    というファイルをダウンロードして、mbed本体に入れました。
    でも、全然動かない。黒いノイズばっかり出る。
    でもあきらめずに、プログラムをみて、何となく、配線をかえてみたら、
    次のようにつなぐと、「Hoge」という、意味不明な画像がやっと出ました。
    左はブレークアウト基盤の端子、右はmbedの端子番号を書きます。
    GND-----1(GND)
    SCK-----13(sck)
    MOSI-----11(mosi)
    MISO-----12(miso)
    CSR-----10(rx Serial scl)
    CSL-----9(tx Serial sda)
    5V-----22(5.0v USB Out VU)
    ちなみに自分は、ここに書いてある英字のたいていの意味は分かりません。だいたい、それを勉強するために買ったのだから。
    mbedは、なんだか、だれでも電気回路に親しめるのが売りのように、書籍に書かれていますが、ウソばっか、ってかんじです。
    回路をよく勉強されてる方には楽ちんかもしれませんが、そういう人はそもそも、mbedなんて使わなくても色々できてしまうのではないでしょうか。
    だいたい、こういう質問に、その道のプロの人がすぐ分かりやすい答えを出さないのも、なんか解せない。だって、最初にこういう表示を実現している人はその道の專門なのだから、素人に分かりやすく説明してあげればいいのに、って思います。(とくにブレークアウト基盤とかを作ってしまうような人は)
    いろいろグチってすみません!
    もしかしたら本当の配線はちがうかも知れませんが、少しでも参考になるならうれしいです。

  2. Level52

    on 2015年6月26日 at 10:24 AM - 返信

    コメントをいただきありがとうございます。
    私と同じくブレークアウト基板(http://www.marutsu.co.jp/pc/i/259032/)を購入されて
    作者が提供しているサンプル(https://developer.mbed.org/users/k4zuki/code/AkiSpiLcd_example/)にて実験をされたのですね。
    記載された配線を見て、今ようやく、なぜ私の環境で動作しないのかがわかりました。
    この作者(k4zukiさん)は、自作の基板「mbed shield(https://www.switch-science.com/catalog/1717/)」という製品を
    使用する前提でサンプルプログラムを掲載しています。
    そして、自分の環境(青い標準品のmbedに自作基板をセットした状態)以外での検証を行っていないのでしょう。
    私が使用しているmbedのプラットフォームは「Nucleo F401RE」という機種で、
    標準品の青いmbedとは多少ピン配置が異なります。作者のプログラム上でピン配置が上書きされているために
    私の環境でピン配置を修正したとしても動作しなかった模様です。
    私も標準の青いmbedを購入して検証してみます。
    過去の経緯を調べてみると、作者さんももともとは仲間内で配っていた基板を
    周りの人間の要望で一般向けに公開しただけみたいです。
    初心者がパパッと動作させるようには作られていないようですね。
    ちなみに、記載された配線は正しいものです。その配線であれば、
    作者が公開している他のサンプル等も動くかとは思いますが、私も検証できていないので・・・なんともいえません。
    >mbedは、なんだか、だれでも電気回路に親しめるのが売りのように、書籍に書かれていますが、ウソばっか、ってかんじです。
    これについてはもの凄く同意できます。超初心者を中級者へステップアップさせるような案内がまったくありません。
    しかしこれは日本人だけの問題で、欧州海外等では盛んに情報交換がされており、何かプログラムが動かなかったとしても
    他の人がすぐに回答を提示してくれるようなシステムが整っています。
    (英語ができれば簡単に問題を解決できるということです)
    ただ単に日本人のユーザーが少なく、日本語によるサポートが無いように感じるというところが問題なんですよね・・・。

コメントを残す