Last Update : 2016/11/09 |
PICでGLCDを駆動
以前Aliで何かのついでにポチッた128x128 262KColor SPI GLCD(グラフィック液晶)(たしか@350円ほど)が眠っていたので、駆動させてみました。
ATmega系のサンプルは多数サイトにアップされているので、とりあえずATmega168で動かせて動作を確認してみました。
コントローラはST7735って書いてあったけど、ILI9163のライブラリで動きました。( <- コンパチか?)
I/FはSPIって事ですが、2線式シリアル(データとクロック)なので、タイミングさえ合えばSPIのMasterModeでもUSART in SPI mode(ちょっと遅いですが)でも駆動できます。
宣言部およびメイン
プログラムは、単純にLCDの画面を周期的(1秒おき)にRGBWで塗りつぶすだけのプログラムです。
LCDの初期化は簡略化してあるので、ガンマ補正とかは使用していません。
LCDとの通信部分(spi_init、spi_send)の3種類の転送方法は、別途記述します。
#include <xc.h>
#include <pic16f886.h>
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000
#endif
// CONFIG1
#pragma config FOSC = INTRC_NOCLKOUT// Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF // RE3/MCLR pin function select bit (RE3/MCLR pin function is RE3)
#pragma config CP = OFF // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = ON // Brown Out Reset Selection bits (BOR enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)
#pragma config LVP = OFF // Low Voltage Programming Enable bit (RB3 pin has digital I/O, HV on MCLR must be used for programming)
// CONFIG2
#pragma config BOR4V = BOR21V // Brown-out Reset Selection bit (Brown-out Reset set to 2.1V)
#pragma config WRT = OFF // Flash Program Memory Self Write Enable bits (Write protection off)
// data type definition
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned long uint32_t;
// I/F pin definition
#define _CS RC0
#define _RESET RC1
#define _A0 RC2
#define _SDA RC5
#define _SCK RC3
// LCD size definition
#define LCD_HSIZE 0x80
#define LCD_VSIZE 0x80
#define LCD_HPIX (LCD_HSIZE-1)
#define LCD_VPIX (LCD_VSIZE-1)
// Color definition for RGB=565(16bit)
#define COLOR_BLUE 0xF800
#define COLOR_RED 0x001F
#define COLOR_GREEN 0x07E0
#define COLOR_WHITE 0xFFFF
typedef enum {
DATA = 1,
COMMAND = 0
} ParamType;
void spi_init(void);
void spi_send(uint8_t data);
void writeByte(uint8_t address, ParamType dc);
void writeWord(uint16_t value);
void writePixel(uint16_t value, short size);
void ILI9163_init(void);
void writeByte(uint8_t value, ParamType dc) {
_CS = 0;
_A0 = (unsigned)dc;
// send data by SPI
spi_send(value);
_CS = 1;
}
void writeWord(uint16_t value) {
_CS = 0;
_A0 = (unsigned)DATA;
// send data by SPI
spi_send((uint8_t)((value >> 8) & 0x00FF));
spi_send((uint8_t)(value & 0x00FF));
_CS = 1;
}
void writePixel(uint16_t value, short size) {
_CS = 0;
_A0 = (unsigned)DATA;
// send data by SPI
for (short i=0;i<size;i++) {
spi_send((uint8_t)((value >> 8) & 0x00FF));
spi_send((uint8_t)(value & 0x00FF));
}
_CS = 1;
}
// Initialise the display with the require screen orientation
void ILI9163_init(void) {
_CS = 1;
// Hardware reset the LCD
_RESET = 0;
__delay_ms(50);
_RESET = 1;
__delay_ms(120);
writeByte(0x11, COMMAND); // Exit sleep mode command
__delay_ms(120); // Wait for voltage stable to wake up
writeByte(0x3A, COMMAND); // Set color format command
writeByte(0x55, DATA); // 16 bits per pixel
writeByte(0x29, COMMAND); // Set display on command
}
void ILI9163_fill(uint16_t color) {
writeByte(0x2A, COMMAND); // Set column address command
writeWord(0); // Start = 0
writeWord(LCD_HPIX); // End = LCD horizontal size -1
writeByte(0x2B, COMMAND); // Set page address command
writeWord(0); // Start = 0
writeWord(LCD_VPIX); // End = LCD vertical size -1
writeByte(0x2C, COMMAND); // Write memory command
writePixel(color, LCD_HSIZE*LCD_VSIZE); // fill LCD size by color data
}
void main(void) {
uint16_t colr[] = {COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_WHITE};
OSCCON = 0b01101000 ; // Internal 4MHz(default value)
TRISA = 0b00000000; // Set output for non use port
TRISB = 0b00000000;
spi_init(); // TRISC & PORTC & SPI setting
ILI9163_init(); // Initialize LCD module
// Loop for change color LCD
for (char i=0; ; i++) {
ILI9163_fill(colr[i % 4]);
__delay_ms(1000);
}
}
GPIO駆動
まずはオーソドックスなPIO駆動です。ILI9163のタイミングチャートさえ読めば簡単に駆動できます。
LCD Pin | PIC Pin |
---|---|
-CS | RC0 |
-RESET | RC1 |
A0 | RC2 |
SDA | RC5 |
SCK | RC3 |
void spi_init(void) {
PORTC = 0b11111111;
TRISC = 0b00000000;
_SCK = 0;
}
void spi_send(uint8_t data) {
for(char i=0;i<8;i++) {
if(data & 0x80) _SDA = 1; else _SDA = 0;
_SCK = 1;
data <<= 1;
_SCK = 0;
}
}
MASTER SYNCHRONOUS SERIAL PORT (MSSP)駆動
次にシリアル専用モジュールのMSSP駆動です。PICのマニュアルを読めば簡単に駆動できるかと・・ただ注意すべきはクロックの極性です(CKP=0,CKE=1)。
後は、データの送信完了を待つ事です。(SSPBUFにデータを代入して放置する人もWebサイトでは散見されますが)
LCD Pin | PIC Pin |
---|---|
-CS | RC0 |
-RESET | RC1 |
A0 | RC2 |
SDA | RC5(SDO) |
SCK | RC3(SCK) |
void spi_init(void) {
PORTC = 0b11111111;
TRISC = 0b00000000;
_SCK = 0;
SSPCON = 0b00100000; // SSPEN=1, CKP=0, SSPM=0x00(SPI Master mode, clock = FOSC/4)
SSPSTAT = 0b01000000; // CKE=1
}
void spi_send(uint8_t data) {
SSPBUF = data; // data output to SPI buffer
while (SSPIF == 0); // wait until the transmission is finished
SSPIF = 0; // clear SSPIF
}
ENHANCED UNIVERSAL SYNCHRONOUS ASYNCHRONOUS RECEIVER TRANSMITTER (EUSART)駆動
長いモジュール名ですね(^^;)。あまり、このモードでSPI通信をする人が少ないようですが、一応動いたので掲載します。
EUSARTは、SYNCHRONOUS MASTER MODEで使用します。
このモードで使う場合も、MSSPと同様にクロックの極性に注意する必要がありますが、一番の問題は、LSB Firstでデータが出力されてしまう点です。
よって、データを出力する前にLSB-MSBの上位・下位ビット入れ替えを行う必要があります。
LCD Pin | PIC Pin |
---|---|
-CS | RC0 |
-RESET | RC1 |
A0 | RC2 |
SDA | RC7(DT) |
SCK | RC6(CK) |
void spi_init(void) {
PORTC = 0b11111111;
TRISC = 0b00000000;
_SCK = 0;
SYNC = 1; // Synchronous mode
BRG16 = 0; // not use 16bit baud rate register
SPBRG = 15; // baud rate i.e.(fosc / (15 + 1)
SCKP = 1; // Synchronous Clock Polarity
SPEN = 1; // Serial Port Enable
CSRC = 1; // Clock Source = baud rate gen.
CREN = 0; // Continuous Receive disable
SREN = 0; // Single Receive disable
}
void spi_send(uint8_t data) {
uint8_t txd = 0;
for (char i=0;i<8;i++) { // bit swap msb-lsb
txd <<=1;
txd |= (data & 1);
data >>= 1;
}
TXEN = 1;
TXREG = txd;
while (TRMT == 0) ;
TXEN = 0;
}
【補足】UNIVERSAL SYNCHRONOUS ASYNCHRONOUS RECEIVER TRANSMITTER (USART)駆動
EUSARTで動いたので、ついでにUSARTしか持っていないデバイス(今回はPIC16F648)で試してみます。
USARTは、EUSARTと違って、クロックの極性の反転ができません。よってハードを少々改良してSCKラインを反転させる必要があります。(図2)
クロック反転はどんな手段でも良いのですが、MOS-FETによる回路が一番シンプルです。ただ、Vcc=3Vで駆動している場合はFETのVGS(th)が小さいものを使う必要があります。
2N7002のVGS(th)は、2V(Typ)なので、PICの出力VOH(VDD-0.7V)=2.3Vだとぎりぎりですが...
ロジックインバーターやNAND、NORゲートが余っている場合は、それを使う方がもっとシンプルです。
LCD Pin | PIC Pin (PIC16F648) |
---|---|
-CS | RB3 |
-RESET | RB4 |
A0 | RB5 |
SDA | RB1(DT) |
SCK | RB2(CK) |
// I/F pin definition for PIC16F648
#define _CS RB3
#define _RESET RB4
#define _A0 RB5
#define _SDA RB1
#define _SCK RB2
void spi_init(void) {
PORTB = 0b11111111;
TRISB = 0b00000000;
_SCK = 1;
SYNC = 1; // Synchronous mode
SPBRG = 3; // baud rate i.e.(fosc / (3 + 1)
SPEN = 1; // Serial Port Enable
CSRC = 1; // Clock Source = baud rate gen.
CREN = 0; // Continuous Receive disable
SREN = 0; // Single Receive disable
}
void spi_send(uint8_t data) {
uint8_t txd, ord = data;
char cnt;
cnt = 8;
txd = 0;
asm("bitloop: rrf spi_send@ord,f");
asm("rlf spi_send@txd, f");
asm("decfsz spi_send@cnt, f");
asm("goto bitloop");
TXEN = 1;
TXREG = txd;
while (TRMT == 0) ;
TXEN = 0;
}
Copyright MAGICEYE. All rights reserved. | Mail: |
734,933
|
This menu is optimized for IE6.0. |