Arduino で LCDを操作する @ ST7735

attendのTFTカラーLCDモジュールを買いました。

LCD@aitendo

ドライバIC(ST7735)もついて、2.54mmピッチ変換基板も付いて1000円以下で購入できます。

1.8インチと小さめですが、キャラクタLCDから一歩進んで、表現自由度が高いLCDを始めて使用するとき、とっつきやすいと思います。

ドライバICの仕様書がネット自由に閲覧できるし、かなり細かく丁寧で分かりやすいので良いです。

Adafruitsがこのドライバ用のLCDライブラリを作っているので、仕様書とそれを見ればLCDの操作について理解できます。

 

LCDの操作方法としては以下の2種類に分かれます。

・パラレルデータ通信で制御

・シリアルデータ通信で制御

当然、ピンが多いCPUやマイコンで操作するときは、パラレル通信の方が早いですが、扱いやすいとか、CPUやマイコンのピン数が少ないという場合はシリアル通信制御の方が良いかと。

 

ST7735の場合、パラレル/シリアル通信に共通して、CPU側から送信したデータがコマンドなのかパラメータ(関数の引数のようなもの)なのかを判別するためのD/CXというピンがあります。

このピンを0(LOW)にしてデータを送るとコマンドとして判別されて、1(HIGH)にしてデータを送信するとパラメータとして判別されます。

コマンドの中には、パラメータを必要とするものと、そうじゃないものがあるので適宜データシートを見て操作。

 

そして、操作するときに大切なのが設定の初期値。

ST7735がリセットされた後にどんな初期状態になっているのか?

例えば、電源は入力されているけど、ディスプレイがOFFの状態になってるとか、カラーモードが18bitになってるとかあるので適宜確認。

しかもリセットは下記3種類あってリセット内容によって初期状態違うので注意です。

・パワーオンリセット

・ハードウェアリセット

・ソフトウェアリセット

 

パワーオンリセットを抜き出して話をすると、初期状態でカラーモードが18bitになっているので、扱いやすい16bit(2Byte)にしてから20,480個パラメータを送信して、ディスプレイONにするとLCDになんか写ります。

 

あと、ハードウェアとして意識したいのは、送ったデータって中でどう処理されてるのかという部分。

そもそも液晶ディスプレイって、ざっくり言うとRGBの各セルにTFTが付いていてそれが光の通過量を操作しているわけですが、マイコン側から送ってるデータってただの1画素のRGB値をデータとして送ってるだけです。

ドライバ内では、このデータを内臓のSRAM(Static RAM)に書き込んでいます。

マイコンはSRAMに対してデータを書き込んでるだけです。

ST7735のパラレル通信での操作方法を見てみると、まさにSRAMの操作そのものになります。マイコンからはSRAMに対してデータを書き込み、そのあと、ドライバIC内部でSRAMからデータを読んできて、画面に展開するためにCulommやRowにいつ電圧与えるかとかのタイミングを決めたり、ガンマの設定とかをしてくれています。

SRAMってところが結構キモです。

 

以下、クソコードですが、とりあえずのけっておきます。


#include <SPI.h>;
#include "disp.h"
#include "stdio.h"
#include <avr/sleep.h>;
#include <avr/pgmspace.h>;

#define TFT_CS 10
#define TFT_RST 9
#define TFT_DC 8

#define TFT_SCLK 52 // set these to be whatever pins you like!
#define TFT_MOSI 51 // set these to be whatever pins you like!

volatile uint8_t *csport,*rsport,*clkport,*dataport;
uint32_t cspinmask,rspinmask,datapinmask,clkpinmask;
uint16_t trg_data;

int y,x;
#define DELAY 0x80
void commandLists(const uint8_t *addr);
void writecommands(uint8_t c);
void writedatas(uint8_t c);
void spiwrites(uint8_t c);


const uint16_t trg[80][64] PROGMEM = {{2次元配列データ}};
 char buf[128];
 PROGMEM static const uint8_t Rcmd1s[] = { // Init for 7735R, part 1 (red or green tab)
 5, // 15 commands in list:
 ST7735_SWRESET, DELAY, // 1: Software reset, 0 args, w/delay
 150, // 150 ms delay
 ST7735_SLPOUT , DELAY, // 2: Out of sleep mode, 0 args, w/delay
 255, // 500 ms delay
 ST7735_MADCTL , 1 , // 14: Memory access control (directions), 1 arg:
 0x03, // row addr/col addr, bottom to top refresh
 ST7735_COLMOD , 1 , // 15: set color mode, 1 arg, no delay:
 0x05, // 4 commands in list:
 ST7735_DISPON , DELAY, // 4: Main screen turn on, no args w/delay
 100 
}; // 100 ms delay

void setup(void) {
 Serial.begin(9600);
 Serial.println("Hello World! Display Test!");

 pinMode(TFT_CS , OUTPUT);
 pinMode(TFT_DC, OUTPUT);
 pinMode(TFT_RST, OUTPUT);

 csport = portOutputRegister(digitalPinToPort(TFT_CS));
 rsport = portOutputRegister(digitalPinToPort(TFT_DC));

 cspinmask = digitalPinToBitMask(TFT_CS);
 rspinmask = digitalPinToBitMask(TFT_DC);

 SPI.begin();
 SPI.setClockDivider(SPI_CLOCK_DIV4);
 SPI.setDataMode(SPI_MODE0);

 *csport &amp;= ~cspinmask;
 pinMode(TFT_RST, OUTPUT);
 digitalWrite(TFT_RST, HIGH);
 delay(50);
 digitalWrite(TFT_RST, LOW);
 delay(50);
 digitalWrite(TFT_RST, HIGH);
 delay(50);

 commandLists(Rcmd1s);

 writecommands(ST7735_CASET); // Column addr set
 writedatas(0x00);
 writedatas(0x02); // XSTART
 writedatas(0x00);
 writedatas(129); // XEND

 writecommands(ST7735_RASET); // Row addr set
 writedatas(0x00);
 writedatas(0x01); // YSTART
 writedatas(0x00); 
 writedatas(160); // YEND

 writecommands(ST7735_RAMWR); // write to RAM

 *rsport |= rspinmask;
 *csport &amp;= ~cspinmask;
 for(y=160; y&gt;0; y--) {
  for(x=128; x&gt;0; x--) {
   spiwrites(0xF8);
   spiwrites(0x1F);
  }
 }

 *csport |= cspinmask;

 delay(2000);

 uint16_t i=0;
 int rb = 0;
 int gr = 0;

 writecommands(ST7735_RAMWR); // write to RAM

 *rsport |= rspinmask;
 *csport &amp;= ~cspinmask;
 for(y=160; y&gt;0; y--) {
  if((i&amp;0x8F1F)&lt;0x8F1F){
   if(rb&gt;=5){
    i += 0x0801;
    rb = 0;
   }
   rb++;
  }
  if((i&amp;0x7E0)&lt;0x7E0){
   if(gr==3){
    i+=0x20;
   }
   if(gr==5){
    i+=0x20;
    gr=0;
   }
    gr++;
  }
  for(x=128; x&gt;0; x--) {
   spiwrites(i&gt;&gt;8);
   spiwrites(i);
  }
 }

 *csport |= cspinmask;

 delay(1000);

 i=0xFFE0;
 rb = 0;
 gr = 0;
 writecommands(ST7735_RAMWR); // write to RAM

 *rsport |= rspinmask;
 *csport &amp;= ~cspinmask;
 for(y=160; y&gt;0; y--) {
  if(rb&gt;=5){
   i -= 0x0800;
   i += 0x0001;
   rb = 0;
 }
 i = i|0x07E0;
 for(x=128; x&gt;0; x--) {
  if(gr==2){
   i-=0x20;
  }if(gr==4){
   i-=0x20;
  }if(gr==7){
   i-=0x20;
   gr=0;
  }

  spiwrites(i&gt;&gt;8);
  spiwrites(i);
   gr++;
  }
  rb++;
 }
 *csport |= cspinmask;

 delay(2000);

 uint8_t vv =1;
 uint8_t cc = 1;
 uint8_t v=0;
 uint8_t c=0;
 writecommands(ST7735_RAMWR); // write to RAM

 *rsport |= rspinmask;
 *csport &amp;= ~cspinmask;
 for(y=160; y&gt;0; y--) {
  if(vv &gt;= 4){
   v++;
   vv = 0;
  }
  for(x=128; x&gt;0; x--) {
   if(cc &gt;= 4){
    c++;
    cc=0;
   }
   spiwrites(Rcmd2s[v]&gt;&gt;8);
   spiwrites(Rcmd2s[v]);
   cc++;
  }
  cc=0;
  c=0;
  vv++;
 }

 *csport |= cspinmask;

 delay(1000);

 vv =1;
 cc = 1;
 v = 0;
 c = 0;
 writecommands(ST7735_RAMWR); // write to RAM

 *rsport |= rspinmask;
 *csport &amp;= ~cspinmask;
 for(y=160; y&gt;0; y--) {
  Serial.println("Hello!");
  if(vv &gt;= 2){
   v++;
   vv = 0;
  }
  for(x=128; x&gt;0; x--) {
   if(cc &gt;= 2){
    c++;
    cc=0;
   }
   spiwrites(pgm_read_word_near((*(trg + v) + c))&gt;&gt;8);
   spiwrites(pgm_read_word_near((*(trg + v) + c)));
   cc++;
  }
  cc=0;
  c=0;
  vv++;
 }

 *csport |= cspinmask;

 delay(2000);
}
void loop(){
 Serial.println("SLEEP");
 delay(500);
 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 sleep_mode();
}

void commandLists(const uint8_t *addr) {

 uint8_t numCommands, numArgs;
 uint16_t ms;

 numCommands = pgm_read_byte(addr++); // Number of commands to follow
 Serial.println(numCommands);
 while(numCommands--) { // For each command...
  writecommands(pgm_read_byte(addr++)); // Read, issue command
  numArgs = pgm_read_byte(addr++); // Number of args to follow
  ms = numArgs &amp; DELAY; // If hibit set, delay follows args
  numArgs &amp;= ~DELAY; // Mask out delay bit
  while(numArgs--) { // For each argument...
  writedatas(pgm_read_byte(addr++)); // Read, issue argument
 }

 if(ms) {
  ms = pgm_read_byte(addr++); // Read post-command delay time (ms)
  if(ms == 255) ms = 500; // If 255, delay for 500 ms
   delay(ms);
  }
 }
}

inline void spiwrites(uint8_t c) {
 SPI.transfer(c);
}

void writecommands(uint8_t c) {
 *rsport &amp;= ~rspinmask; //RS = LOW;
 *csport &amp;= ~cspinmask; //CS = LOW;
 spiwrites(c);
 *csport |= cspinmask; //CS = HIGH;
}
void writedatas(uint8_t c) {
 *rsport |= rspinmask; //RS = HIGH;
 *csport &amp;= ~cspinmask; //CS = LOW;
 spiwrites(c);
 *csport |= cspinmask; //CS = HIGH;
}

コメントを残す

メールアドレスが公開されることはありません。