找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 8567|回復(fù): 1
打印 上一主題 下一主題
收起左側(cè)

LCD12864學(xué)習(xí)筆記及常用指令 附單片機(jī)源程序

  [復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:718503 發(fā)表于 2020-3-30 00:03 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
要學(xué)習(xí)LCD12864的可以參考一下。
12864液晶學(xué)習(xí)筆記

這是我對12864的學(xué)習(xí)筆記,12864液晶功能很全面,使用起來也很方便,能夠滿足一般的研究和工程應(yīng)用的需求。
下面我就對幾個方面講述一下我對它學(xué)習(xí)過程中的一些體會。我會盡量全面的介紹,并且會主要針對一些特殊的應(yīng)用和一些我認(rèn)為要特別注意的地方進(jìn)行較為詳細(xì)的說明。而對于那些較為固定的,常用的方面會簡略一些。
其 中的不管串行模式還是并行模式,對液晶的寫指令,寫數(shù)據(jù),讀操作等這些基本的操作都是一些比較固定的,基礎(chǔ)的東西。我認(rèn)為只要能夠看懂,并且能夠在不同的 處理器上移植就夠了,沒有必要非要自己對著手冊時序圖寫出來。因為有人已經(jīng)寫好了,而且工作穩(wěn)定,我們只要在它的基礎(chǔ)上學(xué)會應(yīng)用就可以了,F(xiàn)在是知識爆炸 時期,知識,信息迅速膨脹,我們要學(xué)會使用已有的成果,然后在這個基礎(chǔ)上自己再進(jìn)行開發(fā)應(yīng)用的研究。我們不必一定要從底層開始把別人已經(jīng)做的很成熟的東西 再做一遍,這樣不但效率不高,而且我們一般人的精力也不允許。
好了,不說廢話了,下面就開始介紹,當(dāng)然,那些基礎(chǔ)層面的東西我也會介紹的。
下面所涉及到的程序,是針對msp430g2553的,都是我已經(jīng)調(diào)通的,可以直接應(yīng)用。

一,12864的介紹
1,液晶顯示模塊是128×64 點陣的漢字圖形型液晶顯示模塊,可顯示漢字及圖形,內(nèi)置國標(biāo)GB2312 碼簡體中文字庫(16X16 點陣)、128 個字符(8X16 點陣)及64X256 點陣顯示RAM(GDRAM)?膳cCPU 直接接口,提供兩種界面來連接微處理機(jī):8-位并行及串行兩種連接方式。具有多種功能:光標(biāo)顯示、畫面移位、睡眠模式等。
2,常用的12864液晶內(nèi)部都是使用ST7920控制器。
    1),ST7920提供8位元,4位元及串行三種微處理器控制方式,大陸常用的是8位元和串行控制方式。
2),ST7920可以控制顯示字母,數(shù)字符號,中文字型和自定義的圖畫。可以用來顯示圖形,演示動畫,繪制曲線等。
3),字符顯示RAM (DDRAM)
ST7920的字符顯示RAM (DDRAM)最多可以控制16字元*4行,LCD的顯示范圍為16字元*2行。
這里要注意,其實ST7920的DDRAM每一行可以控制16個漢字的,共有4行。但是LCD的每行只能顯示8個字符,為了顯示觀察的方便,在lcd制作的過程中,是將DDRAM的其中兩行拆分開成四行,然后在lcd上顯示,也即是DDRAM只用到了一半。
lcd的顯示字符的坐標(biāo)地址如下表:
漢字顯示坐標(biāo)
Line1 80H 81H 82H 83H 84H 85H 86H 87H
Line2 90H 91H 92H 93H 94H 95H 96H 97H
Line3 88H 89H 8AH 8BH 8CH 8DH 8EH 8FH
Line4 98H 99H 9AH 9BH 9CH 9DH 9EH 9FH
從上表不難看出,其中第一行和第三行是DDRAM中的同一行拆分來的,同理2 4 行也是DDRAM中的同一行拆分而來的。
了 解了這一點就不難理解下面程序中在換行顯示時,要認(rèn)為手動地指定下一行的地址。例如:如果第一行顯示完了,下面的數(shù)據(jù)我要接著顯示在第二行,這樣才符合人 觀察的習(xí)慣,那么我就要在換第二行顯示之前要手動地把顯示地址切換到第二行。要不然的話,第一行顯示完了,地址會自動增加,就會顯示到第三行上去,這樣的 話我們觀察起來就不自然了。程序?qū)嵗龝谙旅嫔婕暗降摹?br />
    4),中文字庫ROM (CGROM)
內(nèi)置的是GB2312碼簡體中文字庫,共提供了8192個16*16點的中文字型。
5),半寬字型ROM (HCGROM)
提供126個16*8點半字寬的字母符號字型。
6),圖畫顯示RAM (GDRAM)
提供64*256位元的GDRAM

其中我們常用的RAM就是上面所提到的,還有一些CGRAM ,IRAM這些我們不經(jīng)常使用,就不再介紹了。
上面介紹的DDRAM控制顯示漢字,字符。GDRAM控制顯示圖畫,上電后,默認(rèn)DDRAM是打開的,控制液晶顯示。GDRAM默認(rèn)不打開,它里面的數(shù)據(jù)是隨機(jī)的,如果此時打開了GDRAM的話,lcd會同時受到DDRAM和GDRAM的控制,由于GDRAM中的數(shù)據(jù)是隨機(jī)的,所以會顯示亂碼。所以在使用GDRAM之前要先清除里面的隨機(jī)數(shù)據(jù)。
清除GDRAM的函數(shù)如下:

void Clear_GDRAM(void)   //清除GDRAM中的的隨機(jī)數(shù)據(jù)。因為上電后GDRAM中的數(shù)據(jù)是隨機(jī)的,如果不清除而直接打開GDRAM顯示時,會顯示亂碼
                        //所以在局部使用GDRAM顯示圖形時,要先清除隨機(jī)數(shù)據(jù)。如果是全局使用GDRAM,即整個lcd屏全部設(shè)置顯示數(shù)據(jù),則可以
                       //不必清除,因為新數(shù)據(jù)會把隨機(jī)數(shù)據(jù)給覆蓋掉
{
    uchar i,j,k;

    wr_lcd(comm,0x34);        //打開擴(kuò)展指令集   操作GDRAM是擴(kuò)展指令集
i = 0x80;
for(j = 0;j < 32;j++)
{
wr_lcd(comm,i++);
wr_lcd(comm,0x80);
   for(k = 0;k < 16;k++)
   {
   wr_lcd(dat,0x00);   //寫入空字符,就相當(dāng)于清零
   }
}
i = 0x80;
  for(j = 0;j < 32;j++)
{
  wr_lcd(comm,i++);
  wr_lcd(dat,0x88);
   for(k = 0;k < 16;k++)
   {
   wr_lcd(dat,0x00);
    }
}
  wr_lcd(comm,0x30);        //回到基本指令集
}

   3,12864有兩種工作模式
   1),并行模式和串行模式。并行模式就是常用的8位數(shù)據(jù)線,4為控制線。這種方式雖然占用的IO口較多,但是向液晶收發(fā)數(shù)據(jù)較容易實現(xiàn),數(shù)據(jù)傳輸速度較快。所以在一些連續(xù)顯示多幅圖畫,演示動畫或?qū)︼@示的實時性要求較高的場合應(yīng)該考慮這種方式。其中在并行模式中,在向液晶寫數(shù)據(jù)或命令前,要進(jìn)行液晶忙標(biāo)志判斷 BF,要確定液晶顯示不忙了,才能進(jìn)行操作。
其中并行模式的液晶的讀寫數(shù)據(jù),命令函數(shù)如下:

void Write_Cmd(uchar cmd)
{
    uchar lcdtemp = 0;

    LCD_RS_L;
    LCD_RW_H;
    LCD_DataIn;    //數(shù)據(jù)輸入單片機(jī)
    do                       //判忙
    {
        LCD_EN_H;
        _NOP();
        lcdtemp = LCD2MCU_Data;
        LCD_EN_L;

    }
    while(lcdtemp & 0x80);  //判斷忙標(biāo)志   等待忙

    LCD_DataOut;   //數(shù)據(jù)輸出到lcd
    LCD_RW_L;
    MCU2LCD_Data = cmd;   //單片機(jī)向lcd輸入命令
    LCD_EN_H;
    _NOP();
    LCD_EN_L;
}

void  Write_Data(uchar dat)
{
    uchar lcdtemp = 0;

    LCD_RS_L;
    LCD_RW_H;
    LCD_DataIn;
    do                       //判忙
    {
        LCD_EN_H;
        _NOP();
        lcdtemp = LCD2MCU_Data;
        LCD_EN_L;
    }
    while(lcdtemp & 0x80);     //等待忙

    LCD_DataOut;
    LCD_RS_H;
    LCD_RW_L;

    MCU2LCD_Data = dat;   //單片機(jī)向lcd中輸入數(shù)據(jù)
    LCD_EN_H;
    _NOP();
    LCD_EN_L;
}

   2),串行模式只用到了兩根線WR  EN于單片機(jī)進(jìn)行通信。這種方式可以大大減少單片機(jī)IO口的開銷,適用于IO口資源有限的單片機(jī)(如msp430g2553)。但是這種方式實現(xiàn)起來較麻煩,數(shù)據(jù)的傳輸效率不高。對于一般的文字,簡單圖形的顯示還是可以的。(有可能是因為msp430g2553的處理能力較強(qiáng),我現(xiàn)在用串行連接方式,顯示下面的幾幅圖畫,顯示效果很好,看不出有什么數(shù)據(jù)傳輸速度慢的問題)。
由于我用的是msp430g2553,所以我一直都是使用串行的控制模式。
//下面重點講一下串行的時序
//SCLK:串行同步時鐘線,每操作一位數(shù)據(jù)都要有一個SCLK跳變沿,而且在這里是上升沿有效。也即是說,每次SCLK由低電平變?yōu)楦唠娖降乃查g,液晶控
//制器將SID上的數(shù)據(jù)讀入或輸出。
//SID:串行數(shù)據(jù),每一次操作都由三個字節(jié)數(shù)據(jù)組成,第一個字節(jié)向控制器發(fā)送命令控制字,告訴控制器接下來是什么操作,若為寫指令則發(fā)送11111000
//(0xf8),若為若為寫數(shù)據(jù)則發(fā)送11111010(0xfa),若為讀狀態(tài)則發(fā)送11111100(0xfc),若為讀數(shù)據(jù)則發(fā)送11111110(0xfe)。
//第二個字節(jié)的高4位為發(fā)送指令或數(shù)據(jù)的高4位,第二個字節(jié)的低4位補(bǔ)0.
//第三個字節(jié)的高4位為發(fā)送指令或數(shù)據(jù)的低4位,第三個字節(jié)的低4位補(bǔ)0
//具體的可以觀察時序進(jìn)行理解


其中數(shù)據(jù)的傳輸?shù)暮瘮?shù)如下:
//12864串行連接寫數(shù)據(jù),寫命令函數(shù)    按照手冊上的時序進(jìn)行編程
void wr_lcd(uchar dat_comm,uchar content)//
{   //              要寫的數(shù)據(jù)
  uchar a,i,j;
      delay_us(50);
  a=content;
       LCD_SCLK0; //en=0;
       LCD_SID1;  //wr=1
  for(i=0;i<5;i++) //數(shù)據(jù)時序*****************8  前5個高電平的同步碼
  {
  LCD_SCLK1;
  LCD_SCLK0;
  }
  LCD_SID0;       //wr=0  寫操作
  LCD_SCLK1;      //en=1  來一個時鐘
  LCD_SCLK0;      //en=0
  if(dat_comm)
   LCD_SID1;  //RS=1  寫數(shù)據(jù)
  else
   LCD_SID0;   //RS=0  寫指令

  LCD_SCLK1; //來一個時鐘
  LCD_SCLK0;
  LCD_SID0;  //控制字的最后一位為0
  LCD_SCLK1;  //來一個時鐘
  LCD_SCLK0;
   for(j=0;j<2;j++)//************一共2*4次循環(huán)寫寫一字節(jié)數(shù)據(jù),第一次大循環(huán)寫高4位,第二次大循環(huán)寫低4位
  {
    for(i=0;i<4;i++)
    {
      if(a&0x80)
           LCD_SID1;
        else
           LCD_SID0;
      a=a<<1;
      LCD_SCLK1;
      LCD_SCLK0;
    }
      LCD_SID0;
   for(i=0;i<4;i++)  //時鐘下面來4個時鐘脈沖
    {
       LCD_SCLK1;
       LCD_SCLK0;
    }
  }
}

void Draw_TX(uchar Yaddr,uchar Xaddr,const uchar * dp)
{
    uchar j;
    uchar k=0;

   // wr_lcd(comm,0x01);  //清屏,只能清除DDRAM
    wr_lcd(comm,0x34);  //使用擴(kuò)展指令集,關(guān)閉繪圖顯示   打開擴(kuò)展指令集
    for(j=0;j<16;j++)
    {
     wr_lcd(comm,Yaddr++);       //Y地址
     wr_lcd(comm,Xaddr);    //X地址
     wr_lcd(dat,dp[k++]);     //寫入數(shù)據(jù)
     wr_lcd(dat,dp[k++]);
    }
    wr_lcd(comm,0x36);  //打開繪圖顯示
  //  wr_lcd(comm,0x30);  //回到基本指令集模式
}


5,向液晶全屏寫圖像的函數(shù)如下:

void Draw_PM(const uchar *ptr)    //整屏顯示圖形
{
    uchar i,j,k;

    wr_lcd(comm,0x34);        //打開擴(kuò)展指令集
i = 0x80;
for(j = 0;j < 32;j++)
{
wr_lcd(comm,i++);
wr_lcd(comm,0x80);
   for(k = 0;k < 16;k++)
   {
   wr_lcd(dat,*ptr++);      //先寫入32*16個數(shù)據(jù)
   }
}
i = 0x80;
  for(j = 0;j < 32;j++)
{
  wr_lcd(comm,i++);
  wr_lcd(comm,0x88);
   for(k = 0;k < 16;k++)
   {
   wr_lcd(dat,*ptr++);      //再寫入32*16個數(shù)據(jù)
    }
}
  wr_lcd(comm,0x36);        //打開繪圖顯示
  wr_lcd(comm,0x30);        //回到基本指令集
}

6,下面我就貼上一個對液晶功能測試的函數(shù),其中用到了液晶的很多功能,可以有實現(xiàn)現(xiàn)象觀察得到,注釋的也較詳細(xì),代碼如下:

#include  "msp430g2553.h"
#include  "ser_12864.h"


void main( void )
{
    uint i;
    uchar laba[]=     //16*16大小圖形數(shù)據(jù)
    { 0x00,0x00,0x00,0xC0,0x01,0x48,0x02,0x44,0x04,0x52,0xF8,0x49,0x88,0x49,0x88,0x49,
      0x88,0x49,0x88,0x49,0xF8,0x49,0x04,0x52,0x02,0x44,0x01,0x48,0x00,0xC0,0x00,0x00};

    WDTCTL = WDTPW + WDTHOLD;    //關(guān)狗

    BCSCTL1 = CALBC1_12MHZ;  //設(shè)定cpu時鐘DCO頻率為12MHz
    DCOCTL = CALDCO_12MHZ;
    P2DIR |=BIT5+BIT4;      //液晶的兩條線

    init_lcd();                 //初始化液晶


    //下面是顯示液晶字符表中的字符
    wr_lcd(comm,0x80);            //寫第一行的顯示地址    寫命令0x80
    for(i = 0; i < 16; i++)        //每一行可以顯示16個字符
     wr_lcd(dat,0x00 + i);     //顯示0x00~0x0f對應(yīng)的字符  寫數(shù)據(jù)

    wr_lcd(comm,0x90);            //寫第二行的顯示地址    因為12864的1 3行和2 4行分別由控制器ST7920的兩行切割來的,為了看起來方便
    //這里要手動把地址切換到第二行。要不然的話,顯示完了第一行,液晶會自動切換顯示到第三行上去
    for(i = 0; i < 16; i++)
     wr_lcd(dat,0x10 + i);     //顯示0x10~0x1f對應(yīng)的字符

    wr_lcd(comm,0x88);            //寫第三行的顯示地址
    for(i = 0; i < 16; i++)
     wr_lcd(dat,0x20 + i);     //顯示0x20~0x2f對應(yīng)的字符

    wr_lcd(comm,0x98);            //寫第四行的顯示地址
    for(i = 0; i < 16; i++)
     wr_lcd(dat,0x30 + i);     //顯示0x30~0x3f對應(yīng)的字符

//     delay_ms(1000);   //延遲1s,觀察效果



//調(diào)試時,可以在此處設(shè)置斷點,單步執(zhí)行下面的指令,觀察結(jié)果
    //在顯示時DDAM和GDRAM是同時顯示的,也就是它們的顯示結(jié)果是疊加在一起的
   
    //1.設(shè)定DDRAM地址命令
     wr_lcd(comm,0x90);            //設(shè)定DDRAM地址,因為此時DDRAM地址已經(jīng)溢出
    //2.顯示狀態(tài)命令
     wr_lcd(comm,0x08);            //整體顯示關(guān),游標(biāo)關(guān),游標(biāo)位置關(guān)
     wr_lcd(comm,0x0c);            //整體顯示開,游標(biāo)關(guān),游標(biāo)位置關(guān)
     wr_lcd(comm,0x0e);            //整體顯示開,游標(biāo)開,游標(biāo)位置關(guān)
     wr_lcd(comm,0x0f);            //整體顯示開,游標(biāo)開,游標(biāo)位置開  游標(biāo)閃爍
    //3.位址歸位
     wr_lcd(comm,0x02);            //位址歸位,游標(biāo)回到原點

     wr_lcd(comm,0x84);            //將DDRAM地址設(shè)為0x88,游標(biāo)在此閃爍
    //4.點設(shè)定指令
    //(以下四個命令是控制寫入字符以后光標(biāo)及整屏顯示的移動)
     wr_lcd(comm,0x07);            //光標(biāo)右移整體顯示左移
     wr_lcd(comm,0x20);           //寫入兩個空格
     wr_lcd(dat,0x20);

    wr_lcd(comm,0x05);            //光標(biāo)左移整體顯示右移
    wr_lcd(dat,0x20);           //寫入兩個空格
    wr_lcd(dat,0x20);

    wr_lcd(comm,0x06);            //光標(biāo)右移整體顯示不移動
    wr_lcd(dat,0x20);           //寫入兩個空格
    wr_lcd(dat,0x20);

    wr_lcd(comm,0x04);            //光標(biāo)左移整體顯示不移動
    wr_lcd(dat,0x20);           //寫入兩個空格
    wr_lcd(dat,0x20);
    //5.游標(biāo)和顯示移位控制
    //(以下四個命令無需寫入顯示數(shù)據(jù),直接控制光標(biāo)和整屏顯示的移動,上面執(zhí)行的命令是寫入空格實現(xiàn)光標(biāo)移動和整屏的移動)
    wr_lcd(comm,0x10);            //光標(biāo)左移
    wr_lcd(comm,0x14);            //光標(biāo)右移
    wr_lcd(comm,0x18);            //整體顯示左移,光標(biāo)跟隨
    wr_lcd(comm,0x1c);            //整體顯示右移,光標(biāo)跟隨

    wr_lcd(comm,0x0c);            //關(guān)閉光標(biāo)

    //6.進(jìn)入擴(kuò)展功能模式命令
    wr_lcd(comm,0x34);            //打開擴(kuò)展功能模式,繪圖顯示關(guān)閉
    //7.反白命令
    wr_lcd(comm,0x04);            //同時反白1、3行
    wr_lcd(comm,0x04);            //再次反白1、3行,相當(dāng)于關(guān)閉1、3行反白
    wr_lcd(comm,0x05);            //同時反白2、4行
    wr_lcd(comm,0x05);            //再次反白2、4行,相當(dāng)于關(guān)閉2、4行反白
    //8.睡眠模式命令
    wr_lcd(comm,0x08);            //進(jìn)入睡眠模式  液晶的控制器ST7920關(guān)閉,降低功耗。但是背光還是亮的
    wr_lcd(comm,0x0c);            //退出睡眠模式   繼續(xù)顯示
    //9.待命模式命令
    wr_lcd(comm,0x01);            //進(jìn)入待命模式  也是不顯示內(nèi)容的,液晶已經(jīng)準(zhǔn)備好接受數(shù)據(jù)或命令了
    //10.打開GDRAM顯示                        //這樣打開GDRAM顯示,液晶的GDRAM和DDRAM會同時控制液晶的顯示
    wr_lcd(comm,0x36);            //打開擴(kuò)展功能模式,打開繪圖顯示  由于上電后GDRAM中的數(shù)據(jù)是隨機(jī)的,所以如果在顯示之前不清除的話,會顯示亂碼
    Draw_TX(0x80,0x84,laba);    //顯示16*16大小圖形  顯示上面定義的圖像,是一個小喇叭
    Clear_GDRAM();              //清除上電復(fù)位后GDRAM中的隨機(jī)數(shù)值,此時GDRAM顯示空字符,但是DDRAM中還是有數(shù)據(jù),所以此時會顯示DDRAM中的數(shù)據(jù)
    Draw_TX(0x80,0x84,laba);    //重新顯示設(shè)置16*16大小圖形
   //11.關(guān)閉GDRAM顯示
    wr_lcd(comm,0x34);            //打開擴(kuò)展功能模式,關(guān)閉繪圖顯示   顯示DDRAM中的數(shù)據(jù)
    //12.設(shè)定基本指令集
    wr_lcd(comm,0x30);              //回到基本指令集
    //13.清除顯示命令
    wr_lcd(comm,0x01);            //清屏  只能清除DDRAM  此時液晶就什么都不顯示了


     //下面是顯示液晶字符表中的字符
     wr_lcd(comm,0x80);            //寫第一行的顯示地址
    for(i = 0; i < 16; i++)
     wr_lcd(dat,0x40 + i);     //顯示0x40~0x4f對應(yīng)的字符

    wr_lcd(comm,0x90);            //寫第二行的顯示地址
    for(i = 0; i < 16; i++)
     wr_lcd(dat,0x50 + i);     //顯示0x50~0x5f對應(yīng)的字符

    wr_lcd(comm,0x88);            //寫第三行的顯示地址
    for(i = 0; i < 16; i++)
     wr_lcd(dat,0x60 + i);     //顯示0x60~0x6f對應(yīng)的字符

    wr_lcd(comm,0x98);            //寫第二行的顯示地址
    for(i = 0; i < 16; i++)
     wr_lcd(dat,0x70 + i);     //顯示0x70~0x7f對應(yīng)的字符

    LPM4;
}

以上全部資料51hei下載地址:
12864學(xué)習(xí)筆記及常用指令.zip (28.44 KB, 下載次數(shù): 66)

評分

參與人數(shù) 2黑幣 +55 收起 理由
楊雪飛 + 5 感謝分享
admin + 50 共享資料的黑幣獎勵!

查看全部評分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏8 分享淘帖 頂 踩
回復(fù)

使用道具 舉報

沙發(fā)
ID:377382 發(fā)表于 2020-6-9 21:09 | 只看該作者
非常不錯
回復(fù)

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表