はじめに
何かに使えないかと買っておいた240x240のカラーLCDを動かしてみました。
Amazonで1000円くらいでした。
DiyStudio 1.3" カラーIPS LCDディスプレイ 240X240解像度
ドライバーIC ST7789VW SPIインターフェース RGB 65Kフルカラー 3.3V
サンプルプログラムなどでグラフィック描画(線や円など)は
一通り動かしてみたのですが、せっかくのカラーLCDなので
写真やカラー画像を表示してみたくなりました。
ただ、SDカードから画像ファイルを読み出すサンプルプログラムしか見つけられず、
SDカードスロットモジュールも手元になかったのでシリアル通信で
ビットマップファイルを転送して表示してみました。
システム構成
PCからシリアル通信(TeraTerm)でビットマップファイルをマイコン(Seeduino XIAO)に
送信しLCDを制御します。
ファイルのパース処理を簡単にするため、ビットマップのサイズは今回使うLCDに合わせて
幅は240ピクセル固定とし、色深度は24bitのみとしました。
ただし、LCDに表示する際にには16bit(R:G:B=5:6:5)に減色しています。
ハードウェア
部品
- Seeeduino XIAO
- 1.3 inc カラー LCD(ST7789)
- ブレッドボード、ジャンパ、など
回路図
今回使ったLCDはI2Cっぽい端子名になっていますがSPIで接続します。
Backlight(BLK端子)は無くても十分明るく表示できたので使いませんでした。
ソフトウェア
グラフィック描画には Adafruit GFX ライブラリを使いました。
ちなみに、このLCDはCS端子がなくXIAO+ST7789でのサンプルプログラムが
見つからずSPI設定にちょっとハマしました。
いろいろ調べて試行錯誤してみてSPI_MODE=3、CS=-1の設定で表示する事ができました。
プログラム
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>
#define TFT_CS (-1)
#define TFT_RST (4)
#define TFT_DC (5)
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
void setup(void)
{
Serial.begin(115200); // XIAOの場合は効かない?
tft.init(240, 240, SPI_MODE3); // MODE3 is for the display without /CS line
tft.setRotation(2);
tft.fillScreen(ST77XX_BLACK);
tft.setTextColor(ST77XX_YELLOW);
tft.setTextSize(2);
tft.setCursor(0, 60);
tft.println("Transfer Bitmap...");
}
void loop()
{
unsigned long width, height;
if (0 == Serial.available()) {
return;
}
if (0 == rcv_bitmap_header(&width, &height)) {
rcv_bitmap_data(width, height);
// 残りは捨てる
while (0 < Serial.available()) {
volatile unsigned char tmp = Serial.read();
}
}
}
int rcv_bitmap_header(unsigned long *p_width, unsigned long *p_height)
{
unsigned char buf[64];
unsigned long bm, size = 0;
int err = 0;
*p_width = 0;
*p_height = 0;
// 面倒なので固定
rcv_n(buf, sizeof(buf), 0x36);
bm = get_val(&buf[0], 2);
if (0x4d42 != bm) { // BM
err = -1;
} else {
size = get_val(&buf[2], 4);
if ((size == 0) || (size > (tft.height() * tft.width() * 3 + 64))) {
err = -2;
} else {
*p_width = get_val(&buf[0x12], 4);
if ((*p_width == 0) || (*p_width > tft.width())) {
err = -3;
} else {
*p_height = get_val(&buf[0x16], 4);
if ((*p_height == 0) || (*p_height > tft.height())) {
err = -4;
}
}
}
}
// Error message
if (err < 0) {
tft.fillRect(0, 50, tft.width(), 100, ST77XX_BLACK);
tft.setCursor(0, 60);
tft.printf("Error : %d\n", err);
tft.printf("BM : %x\n", bm);
tft.printf("Size : %d\n", size);
tft.printf("Width : %d\n", *p_width);
tft.printf("Height: %d\n", *p_height);
}
return err;
}
void rcv_bitmap_data(unsigned long width, unsigned long height)
{
for (int y=height; y>=0; y--) {
for (int x=0; x<width; x++){
tft.drawPixel(x, y, get_pielx());
}
}
}
unsigned long get_pielx(void)
{
unsigned char pix[3];
rcv_n(pix, sizeof(pix), sizeof(pix));
return tft.color565(pix[2], pix[1], pix[0]);
}
int rcv_n(unsigned char buf[], int buf_size, int num)
{
int i=0, t = 0;
while (1) {
if (Serial.available()) {
t = 0;
if (i < buf_size) {
buf[i++] = Serial.read();
}
if (i >= num) {
break;
}
if (i >= buf_size) {
return -2;
}
} else {
t++;
if (t > 1000) {
return -1; // Timeout
}
}
}
return 0;
}
unsigned long get_val(unsigned char *pb, int n)
{
unsigned long v = 0;
for (int i=0; i<n; i++) {
v |= ((*pb++) << (8*i));
}
return v;
}
動作の様子
ビットマップファイルは上下反転してデータが格納されているので下から表示させています。
XIAOのシリアル通信は仮想シリアル(CDC)として認識させて通信しているので、
通常のUARTより高速にデータ通信ができるようです。それでも6~7秒くらいかかりますが…。
※注:TeraTermで送信する際はバイナリのチェックをしないと正常に送信できません。
まとめ
思ったより鮮明に表示できました。
何に使おうかな~
投稿者の人気記事
-
momo
さんが
2021/08/27
に
編集
をしました。
(メッセージ: 初版)
-
momo
さんが
2021/08/27
に
編集
をしました。
-
momo
さんが
2021/08/27
に
編集
をしました。
-
momo
さんが
2021/08/27
に
編集
をしました。
-
momo
さんが
2021/12/27
に
編集
をしました。
-
momo
さんが
2022/02/17
に
編集
をしました。
ログインしてコメントを投稿する