今回は aitendo で 122×32 のグラフィックLCD が 299円(税抜)とかなり安かったので買ってみました。商品ページはこちらです。
グラフィックLCD については以前に記事を書きましたが、それとは制御IC が異なり使用方法も全然違ったので、別の記事として書くことにしました。
以前のグラフィックLCD についての記事はこちら↓
目次
制御IC SED1520
この LCD に使われている制御IC は SED1520 というものです。
ちなみに商品ページの説明欄には “SED1502” と書いてありますが多分誤植です。紛らわしいことに “SED1502” という IC も実在するのですが、リンク先のデータシートが SED1520 でそれを参考にしたら動作しました。
この LCD の制御チップ SED1502 のデータ構造は次のようになっています。
まず122×32 を左右に半分ずつ分けて、2つの同一の IC で制御しています。
片方の IC に着目すると、8行ずつがページとしてまとめられていて、各ページの各列が 1Byte のコラムとなっています。
ビットマップファイルの場合、データは左下から横方向に並んでいるので、この LCD に表示させるにはデータを変換する必要があります。
一見するとこのようなデータ構造は使いにくいと思うのですが、高さが8pt の文字を表示させようと思ったときには便利なようです。
接続・ピン説明
LCD 基板からは 18本のピンが出ていて、各ピンの役割は次のようになっています。
続いて各ピンについて説明します。
VDD, VSS : 電源(5V)
VO : コントラスト調整。半固定抵抗を接続。
RES : リセット。PIC の MCLR に接続。詳しくは後述します。
E1, E2 : 通信の Enable。先述のように制御IC が2つあるのでそれぞれに対応しています。
R/W : Read/Write。読取り不要の場合は GND に接続。
A0 : データ/コマンド選択。
DB0 ~ DB7 : データ。8bit 専用で4bitモードはないようです。
LED-A, K : バックライト電源。メインの電源と違って 3.3V なので注意。
リセットピンについて、aitendo の商品ページの説明では無接続になっていましたが、無接続では動作しませんでした。
データシートを見てみると次のような記述があります。
The SED1520 Series uses 8 bits of bi-directional data bus (D0–D7) to transfer data. The reset pin is capable of selecting MPU interface; setting the polarity of RES to either “H” or “L” can provide direct interface of the SED1520 with a 68 or 80 family MPU (see Table 1 below).
リセットの極性 (active L / active H) によって MPU の種類を選択し、通信方法も異なるようです。68系とか 80系といのはよく分かっていないのですが、aitendo のピンの説明は 68系のものだったので、それに従うことにしました。(Table 1 の”↑”は同上という意味だと思います。)
気を付けたいのは、active L = idle H なので H か L のどちらかに接続するとしたら H に接続するということです。
PIC の MCLR は active L なので、そこに接続しておけば PIC のリセットと同時に LCD もリセットできます。
通信方法
続いて SED1520 との通信方法を見ていきます。データシートの 86系のタイミングチャートは次のようになっています。
立上がりか立下がりかは置いといて、E にパルスを入力したときにデータが送信されるということみたいです。E は idle L にしておけば良さそうです。
なお、今回は読取りは行いません。
コマンド
基本的な動作をさせるために必要なコマンドを紹介します。全部のコマンドを知りたい方はデータシートを参照してください。
Display ON/OFF
D 0:OFF 1:ON
リセット直後はディスプレイが OFF になっているので、自分で ON にしてやる必要があります。2つの制御 IC にそれぞれコマンドを送る必要があるので注意してください。
Set Page Address
A1, A0 ページ番号(0 ~ 3)
先程説明したページを指定するコマンドです。
Column Address
A6~A0 コラム番号 (0 ~ 79)
コラムを指定するコマンドです。A6~A0 の 7bit ありますが、79 が上限のようです。この LCD では 0 ~ 60 を指定することになります。
コラムアドレスはデータを送信する毎にインクリメントされるので、初めにコラムを指定したら連続してデータを送信することができます。ただし、ページはインクリメントされないので注意してください。
どうでも良いことではありますが、コマンドの名前がページのものと異なるのが気になりますね。
ライブラリ
以上のことを踏まえてこの LCD を制御するためのライブラリを作りました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
#include <xc.h> #include "sed1520.h" #define A0 RA0 #define E1 RA1 #define E2 RA2 #define DATAPORT PORTC char currentPage = 0; // データ送信先の判断とページインクリメントの char currentColumn = 0; // ために現在のアドレスを保持 void Sed_Cmd(char c){ // コマンドは両方の制御ICに送信 A0 = 0; DATAPORT = c; E1 = 1; E2 = 1; E1 = 0; E2 = 0; } void Sed_Dat(char c){ A0 = 1; DATAPORT = c; if(currentColumn < 61){ // 送信先判断 E1 = 1; E1 = 0; } else{ E2 = 1; E2 = 0; } currentColumn++; // コラム自動インクリメント if(currentColumn == 122){ // ページ手動インクリメント currentColumn = 0; currentPage = (currentPage + 1) % 4; Sed_SetAddress(currentPage, currentColumn); } } void Sed_SetAddress(char page, char column){ currentPage = page; currentColumn = column; Sed_Cmd(0b10111000 + page); // 両方ともページセット if(column < 61){ // 前半の場合は後半のコラムを0にするのも DATAPORT = column; E1 = 1; E1 = 0; DATAPORT = 0; E2 = 1; E2 = 0; } else{ DATAPORT = column-61; E2 = 1; E2 = 0; } } void Sed_Init(){ Sed_Cmd(0b10101111); //Display ON Sed_SetAddress(0,0); //page 0, column 0 } |
大体見れば分かるかと思いますが、ポイントとしてはコマンドは両方の IC に送信するのに対して、データは適切な方にのみ送信します。
そのために現在のアドレスを保持する変数を用意しています。
また、LCD の表示の端まで来たらページをインクリメントするようにしています。
Sed_Init() 関数ではアドレスを左上に設定しているだけで、中身に何かデータが入っているかもしれないので、まっさらにしたい場合は0を 488回書き込んでください。
動作テスト
今回は PIC16F1579 を使って LCD に画像を表示させてみたいと思います。
122×32 のモノクロBMP画像としてこんな画像を用意してみました。
データを変換するプログラムを作って、このデータをあらかじめ変換しておきます。
変換したデータを表示するプログラムを書いてみた結果このようになりました。
ちゃんと表示されたと思いきや、右のトランジスタの絵が切れてますね。
どうやらセグメントがそもそも16行x2段に分割されているようです。それを知らずに買ってしまったのでちょっと残念です。
ですが今回はとりあえず表示することができたので良しとしましょう。
回路図は次のようになります。
※見やすさのために A0, E1, E2, DB0~DB7 の配線は省略しました。
プログラムは以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
#pragma config FOSC = INTOSC // Oscillator Selection Bits (INTOSC oscillator; I/O function on CLKIN pin) #pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled) #pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled) #pragma config MCLRE = ON // MCLR Pin Function Select (MCLR/VPP pin function is MCLR) #pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled) #pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled) #pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin) #pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off) #pragma config PPS1WAY = ON // PPSLOCK bit One-Way Set Enable bit (PPSLOCKED Bit Can Be Cleared & Set Once) #pragma config PLLEN = ON // PLL Enable (4x PLL enabled) #pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset) #pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.) #pragma config LPBOREN = OFF // Low Power Brown-out Reset enable bit (LPBOR is disabled) #pragma config LVP = OFF // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming) #include <xc.h> #include "sampleImg.h" #include "sed1520.h" #define _XTAL_FREQ 500000 void Init(){ TRISA = 0; ANSELA = 0; PORTA = 0; TRISB = 0; ANSELB = 0; PORTB = 0; TRISC = 0; ANSELC = 0; PORTC = 0; } void main(void) { Init(); Sed_Init(); for(int i=0; i<488; i++){ Sed_Dat(sampleImg[i]); } while(1); } |
1 |
char sampleImg[] = {0x00,0x00,0x84,0x84,0xFC,0x84,0x84,0x00,0xFE,0x12,0x12,0xFE,0x12,0x12,0x12,0xFE,0x00,0x00,0x00,0x02,0x0A,0x0A,0x12,0x2A,0x46,0xC2,0xA2,0x92,0x89,0x81,0xA1,0xC1,0x00,0x00,0x00,0x00,0x7F,0x49,0x49,0x49,0x49,0xFF,0x49,0x49,0x49,0x49,0x7F,0x00,0x00,0x00,0x80,0x80,0x82,0x82,0x82,0x82,0x82,0xF2,0x92,0x8A,0x8A,0x86,0x82,0x80,0x80,0x00,0x80,0x60,0x10,0x08,0x08,0x84,0x7C,0x04,0x04,0x04,0x08,0x08,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x40,0x40,0xE0,0xF0,0x10,0x10,0x08,0x08,0x08,0x08,0x08,0x88,0xC8,0x70,0x30,0x10,0x20,0x40,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x1F,0x08,0x08,0x40,0x49,0x49,0x49,0x7F,0x49,0x49,0x49,0x49,0x40,0x00,0x01,0x21,0x11,0x0D,0x01,0x01,0x01,0x7F,0x00,0x00,0x00,0x04,0x08,0x30,0x03,0x00,0x02,0x42,0x42,0x22,0x22,0x12,0x0A,0x07,0x02,0x02,0x42,0x42,0x42,0x32,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x08,0x10,0x10,0x0C,0x03,0x00,0x00,0x20,0x20,0x10,0x10,0x0C,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x9C,0x82,0x81,0x80,0x80,0xFF,0xFF,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x1C,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x05,0x55,0x55,0x55,0x55,0x05,0x7F,0x05,0x55,0x55,0x55,0x55,0x05,0x1C,0x00,0x80,0x80,0x82,0x82,0x82,0x82,0x82,0xF2,0x92,0x8A,0x8A,0x86,0x82,0x80,0x80,0x00,0x00,0x02,0x02,0x02,0x02,0x02,0x02,0xFE,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x00,0x40,0x30,0xFC,0x03,0x40,0x20,0x10,0x0C,0x0B,0xF8,0x88,0x88,0x88,0x88,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x71,0x81,0x01,0x01,0x01,0xFF,0xFF,0x03,0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x70,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x15,0x15,0x15,0x15,0x7F,0x55,0x55,0x55,0x55,0x5F,0x40,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x04,0x04,0x0F,0x1F,0x10,0x10,0x20,0x20,0x20,0x20,0x20,0x25,0x27,0x17,0x17,0x18,0x08,0x04,0x04,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
▼SED1520ライブラリ
1 2 3 4 |
void Sed_Cmd(char c); void Sed_Dat(char c); void Sed_SetAddress(char page, char column); void Sed_Init(); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
#include <xc.h> #include "sed1520.h" #define A0 RA0 #define E1 RA1 #define E2 RA2 #define DATAPORT PORTC char currentPage = 0; // データ送信先の判断とページインクリメントの char currentColumn = 0; // ために現在のアドレスを保持 void Sed_Cmd(char c){ // コマンドは両方の制御ICに送信 A0 = 0; DATAPORT = c; E1 = 1; E2 = 1; E1 = 0; E2 = 0; } void Sed_Dat(char c){ A0 = 1; DATAPORT = c; if(currentColumn < 61){ // 送信先判断 E1 = 1; E1 = 0; } else{ E2 = 1; E2 = 0; } currentColumn++; // コラム自動インクリメント if(currentColumn == 122){ // ページ手動インクリメント currentColumn = 0; currentPage = (currentPage + 1) % 4; Sed_SetAddress(currentPage, currentColumn); } } void Sed_SetAddress(char page, char column){ currentPage = page; currentColumn = column; Sed_Cmd(0b10111000 + page); // 両方ともページセット if(column < 61){ // 前半の場合は後半のコラムを0にするのも DATAPORT = column; E1 = 1; E1 = 0; DATAPORT = 0; E2 = 1; E2 = 0; } else{ DATAPORT = column-61; E2 = 1; E2 = 0; } } void Sed_Init(){ Sed_Cmd(0b10101111); //Display ON Sed_SetAddress(0,0); //page 0, column 0 } |
今回の記事は以上となります。ご覧いただきありがとうございました。