2012年2月19日 星期日

繪圖型 LCD 128x64 顯示控制

說明

本文章是使用 PIC18F65J50 來驅動 WinStar 128x64 LCD (WG12864A),PIC 使用並列方式和 LCD 通訊,程式由 WinStar 官方提供的組合語言驅動範例程式修改成 C 語言版本。
因為此款 LCD 的電源使用 5V,PIC 和 LCD 連接的通訊腳位要選擇有 5V Toleranted Input 的腳位,如 PORTD。
image

 

LCD 資料

外觀
image

腳位資訊
image
電子特性
image
電路圖
image

 

LCD 初始化步驟

在初始化設定時,送出的 LCD 命令,必須分別送給 CS1 及 CS2 兩顆晶片。
clip_image001

 

LCD 顯示畫面步驟

在顯示整個 LCD 畫面時,先送出 LCD 命令將 CS1 及 CS2 兩顆晶片切到第 1 個 page,Column 設為從最左邊開始寫入 (CS1 從 S1 開始、CS2 從 S65 開始)。接著對 CS1 送出左邊畫面資料,CS2 送出右邊畫面資料,依序的寫滿 8 個 page 後,整個 LCD 的畫面 (128 x 64 bits) 即填滿,最後加個 Delay 延遲一段時間再顯示下一個畫面,調整 Delay 的延遲時間,可以改變顯示的速度。
clip_image003

 

LCD 畫面顯示控制示意圖

每個 page 分為左、右兩邊的晶片控制,每個晶片控制 64 x 8 bits,所以 1 個 page = 128 x 8 bits,總共有 8 個 page,所以全部有 (128 x 8) x 8 = 128 x 64 = 8,192 bits。
clip_image002

 

通訊時序圖

在送出 DB0 ~ DB7 的 LCD 資料後,必須注意 RW 和 E 腳間拉的時間。
clip_image004

 

寫入時序圖

  • tASU (Address Setup time) 是 LCD 取得位址的設定時間 (取得要寫入的位址),在 R/W 腳拉 Low 和 E 腳拉 High 之間的時間,最短不能低於 140 ns。
  • tDSU (Data Setup time) 為 LCD 資料的設定時間 (取得要寫入的資料),在 R/W 拉 Low 到 E 拉 High 之間的時間,最短不能低於 200 ns。
clip_image005

 

讀取時序圖

  • tASU (Address Setup time) 是 LCD 取得位址的設定時間 (取得要寫入的位址),在 R/W 腳拉 Low 和 E 腳拉 High 之間的時間,最短不能低於 140 ns。
clip_image006

 

對比控制

LCD 的對比控制是調整 LCD 畫面亮點與暗點的深度比例,在這個電路接了一個 10K 的 VR (可變電阻) 用來調整 LCD 的對比。
如下圖,使用 10K ~ 20K 的 VR,將 Vdd 及 Vee 接到 VR 的兩端,VR 的中間腳則拉到 Vo。
clip_image007
 

背光控制

image
LCD 底部有一塊會發光的 LED,有些 LCD 在上電時會自動點亮 LCD 背光,此款 LCD 則是提供 LED 的 A (陽極) 及 K (陰極) 腳位,使用外接一個可變電阻調整背光的亮度,在此電路是直接接上一個 220Ω 固定電阻。
WinStar 官方討論區的「How to turn on the backlight by 5V」說明如何計數背光接的電阻,舉例來說,以 WH1602L 的 LCD 為例,Datasheet 說 LED 的電壓為 3.5V,順向電流為 48mA,以歐姆定律來計算背光 LED 需接的電阻值為 31.25Ω。
clip_image008
參考目前此款 WG12864A 的 Datasheet,LED 的電壓為 4.2V,順向電流為 120mA。
clip_image009
以歐姆定律計算需接的電阻值如下:
RA = (5 - 4.2) / 120mA = 6.67Ω
但實際接上 220Ω 電阻就夠亮了,此處計算方法可能有問題,擔心接上 6.67Ω 的電阻會燒掉背光 LED,所以直接接一個 220Ω 電阻,流過背光 LED 的電流計算如下。
Iled = (5V - 4.2V) / 220Ω = 3.6mA
clip_image010

 

程式碼

本程式由 WinStar 官方提供的組合語言驅動範例程式修改成 C 語言版本,此程式會輪流顯示全畫面的 7 張圖片,加上適當的延遲,形成一個動畫。
下載完整程式碼:LCD_128x64.zip (20KB)
void LCDInit(void)
{
    LCD_RS_TRIS = 0;
    LCD_RW_TRIS = 0;
    LCD_DATA_TRIS = 0;
    LCD_E_IO = 0;
    LCD_E_TRIS = 0;
    LCD_CS1_IO = 0;
    LCD_CS1_TRIS = 0;
    LCD_CS2_IO = 0;
    LCD_CS2_TRIS = 0;
    LCD_nRESET_IO = 1;
    LCD_nRESET_TRIS = 0;
    
    LCDSendCmd(0x40);        // Set Y Column Address from 0
    LCDSendCmd(0xB8);        // Set Page to 0
    LCDSendCmd(0xC0);        // Set Display Start Line to 0
    LCDSendCmd(0x3F);        // Set Display ON
}

void LCDShow(void)
{
    unsigned char image, page, column;
    const rom unsigned char *ptrData = Page[0];
    
    for (image = 0; image < 7; image++) {
        for (page = 0; page < 8; page++) {
            LCDSendCmd(0xB8 + page);    // Set Page (0xB8 ~ 0xBF)
            LCDSendCmd(0x40);            // Set Y Column Address from 0
            
            for (column = 0; column < 64; column++)
                LCDSendData(LCD_CS1, *ptrData++);
                
            for (column = 64; column < 128; column++)
                LCDSendData(LCD_CS2, *ptrData++);
        }
        
        LED_Toggle(LED1_IO);
        LED_Toggle(LED2_IO);
        
        delay100ms(5);
    }
}

void LCDSendCmd(unsigned char cmd)
{
    // Send command to CS1
    LCD_CS2_IO = 0;
    LCD_CS1_IO = 1;
    _LCDWrite(LCD_FUN_CMD, cmd);
    
    // Send command to CS2
    LCD_CS1_IO = 0;
    LCD_CS2_IO = 1;
    _LCDWrite(LCD_FUN_CMD, cmd);
}

void LCDSendData(unsigned char cs, unsigned char data)
{
    LCD_CS1_IO = (cs == LCD_CS1);
    LCD_CS2_IO = (cs == LCD_CS2);
    _LCDWrite(LCD_FUN_DATA, data);
}

static void _LCDWrite(unsigned char fun, unsigned char data)
{
    LCD_RS_IO = fun;
    LCD_RW_IO = 0;
    LCD_DATA_IO = data;
    Nop();
    LCD_E_IO = 1;            // Address Setup time must > 140ns before high
    Nop();
    Nop();
    Nop();
    LCD_E_IO = 0;            // Data Setup time must > 200ns before low
}

執行結果


電路有另外加一顆 10K 的可變電阻,用來調整 LCD 的對比度。



參考資料




















0 意見:

張貼留言