zoukankan      html  css  js  c++  java
  • [转载]轻松玩转LCD12864-基于AVR单片机的LCD12864串行显示

    原文链接: http://bbs.elecfans.com/forum.php?mod=viewthread&tid=282698&extra=&highlight=12864&page=1

     参考帖子:http://home.eeworld.com.cn/my/space-uid-159112-blogid-40752.html

    http://v.youku.com/v_show/id_XNDYwOTM2Njc2.html

    LCD12864是一种常用的图形液晶显示模块,顾名思义,就是可以在水平方向显示128个点,在竖直方向显示64个点。通过对控制芯片写入数据,可以控制点的亮灭,从而显示字符、数字、汉字或者自定义的图形。尽管LCD12864有各个不同厂家生产的产品,控制芯片和引脚定义也不尽相同,但是控制原理都大同小异。本文是对我个人使用LCD12864的经验做一个总结,希望能对入门者起到抛砖引玉的作用。
    就以深圳市亚晶达电子有限公司生产的YJD12864C-1为例,我不想深究显示屏的内部结构,单讲讲各个引脚的作用以及数据读写时的时序。
    上图是YJD12864C-1的实物图,从右往左,1脚到20脚的定义如下:
    1VSS接地端
    2VDD电源正,接+5V
    3VO,对比度调整,一般接+5V就行了
    4D/I(CS*),片选,也叫使能,接+5V
    5R/W(SID*),数据输入端
    6E(SCLK*),时钟输入端
    7~14DB0 ~ DB7,并行数据总线
    15PSB,串并模式选择,串行模式下接地,并行模式下接+5V
    16NC,空引脚,不需要连接
    17RSTB,复位端,低电平有效,一般接+5V就行了
    18VEE,空引脚,不需要连接
    19BLA,背光正极,接+5V
    20: BLK,背光负极,接地
    在实际编程时,有串行、并行两种模式可以选择。个人觉得,并行模式占用单片机引脚多(11个),优点是速度快(一次传8位数据,速度自然快),串行模式占用引脚少(2个),速度慢点。我喜欢使用串行模式,AVR单片机的时钟频率最快可达20MHZ(不用除以12),经过实测,从头到尾刷一次屏大约只需0.1s,这在很多场合已经够用了。由于并行模式用的少,不熟悉,下面只讲串行模式
    在串行模式下,硬件的连接为:11520接地,2341719+5V5接单片机SPI输出(下图第6脚),6接单片机SPI时钟信号输出(下图第8脚)。
    ATMEGA324引脚图
    下面介绍程序:
    1、底层数据通信程序:包括SPI设置,SPI发送单字节,LCD写数据,LCD初始化
    //////////////////////////////////////////////////////////////////////////////////////////
    #define F_CPU 8//时钟频率8MHZ
    #include<ioavr.h>
    #include"delay.h"
    //////////////////////////////////////////////////////////////////////////////////////////
    上面是开头部分,其中delay.h内容如下:
    #ifndef __IAR_DELAY_H
    #define __IAR_DELAY_H
    #include <intrinsics.h>
    #define  delay_us(x)   __delay_cycles((unsigned long)(x * F_CPU))
    #define  delay_ms(x)   __delay_cycles((unsigned long)(x * F_CPU*1000UL))
    #define  delay_s(x)    __delay_cycles((unsigned long)(x * F_CPU*1000000UL))
    #endif
    //////////////////////////////////////////////////////////////////////////////////////////
    SPI设置:
    void SPI_MasterInit(void)
    {
      DDRB=(1<<7)|(1<<5)|(1<<4);//(1<<5)|(1<<7);//设置MOSISCK 为输出
    SPCR = (1<<6)|(1<<4);// 1<<SPE 1<<MSTR  使能SPI,主机模式
    SPSR = 1<<0;// 1<<SPI2X倍速
    }
    //////////////////////////////////////////////////////////////////////////////////////////
    SPI发送单字节:
    void SPI_MasterTransmit(char cData)
    {
    SPDR = cData;
    while(!(SPSR & (1<<7)));//1<<SPIF
    }
    LCD写数据:
    void LCD_Write (char RS,char content)//RS=1发数据RS=0发命令
    {        charStart,High4,Low4;
             Start=0xf8|(RS<<1);
             High4=content&0xf0;
             Low4=(content<<4)&0xf0;
             SPI_MasterTransmit(Start);//发送开始字节,前面51,倒数第二位RS
             SPI_MasterTransmit(High4);//发送数据高4
             SPI_MasterTransmit(Low4);//发送数据低4
        delay_us(300);
    }
    //////////////////////////////////////////////////////////////////////////////////////////
    LCD初始化
    void LCD_INIT()
    {
             LCD_Write(0,0x30); /*30---基本指令动作*/
             LCD_Write(0,0x01); /*清屏,地址指针指向00H*/
             LCD_Write(0,0x06); /*光标的移动方向*/
             LCD_Write(0,0x0c); /*开显示,关游标*/
    }
    //////////////////////////////////////////////////////////////////////////////////////////
    2、应用层程序,包括汉字显示,字符显示,图形显示等等
    汉字显示:
    4行,一行可显示8个汉字,每个汉字占16*16个格
    void Show_Chinese(char x0,char y0,chark,char *chn)
    //x0,y0为显示位置x0: 0~3 y0: 0~7 k为汉字个数, chn为汉字数组
    {
             charadr,i;
             switch(x0)
             {
                       case0: adr = 0x80 + y0;break; //在第1y列显示
                       case1: adr = 0x90 + y0;break; //在第2y列显示
                      case 2: adr = 0x88 + y0;break; //在第3y列显示
                       case3: adr = 0x98 + y0;break; //在第4y列显示
                       default:;
             }
             LCD_Write(0,adr);
             for(i=0;i<2*k;i++)
             LCD_Write(1,chn);
    }
    //////////////////////////////////////////////////////////////////////////////////////////
    显示字符串:
    4行,一行可显示16个汉字,每个汉字占8*16个格
    void Show_String(char x0,char y0,chark,char *chn)
    //x0,y0为显示位置x0: 0~8 y0:0~3 k为字符串个数, chn为字符串
    {
             charadr,i;
             switch(x0)
             {
                       case0: adr = 0x80 + y0;break; //在第1y列显示
                       case1: adr = 0x90 + y0;break; //在第2y列显示
                       case2: adr = 0x88 + y0;break; //在第3y列显示
                       case3: adr = 0x98 + y0;break; //在第4y列显示
                       default:;
             }
             LCD_Write(0,0x30);
             LCD_Write(0,adr);
             for(i=0;i<k;i++)
             LCD_Write(1,chn);
    }
    //////////////////////////////////////////////////////////////////////////////////////////
    显示数字:
    void Show_Number(char x0,char y0,charnum)//显示两位数字
    //x0,y0为显示位置x0: 0~8 y0:0~3,显示数字位数可调
    {
             charadr;
             switch(x0)
             {
                       case0: adr = 0x80 + y0;break; //在第1y列显示
                       case1: adr = 0x90 + y0;break; //在第2y列显示
                       case2: adr = 0x88 + y0;break; //在第3y列显示
                       case3: adr = 0x98 + y0;break; //在第4y列显示
                       default:;
             }
             LCD_Write(0,0x30);
             LCD_Write(0,adr);
             //LCD_Write(1,num/100%10+'0');
    //上句不注释则显示3位数字,要显示更多位可以此类推,不过要注意num的字长
    LCD_Write (1,num/10%10+'0');
        LCD_Write(1,num%10+'0');
    }
    //////////////////////////////////////////////////////////////////////////////////////////
    显示图片:
    char lcd_x,lcd_y;
    void Show_Image(char *p) //水平扫描
    //P128*64/8=1024字节
    //可用字模软件获得任意图片的水平扫描码
    {
             chari,j,k;
             lcd_x=0x9f;
             lcd_y=0x80;
             LCD_Write(0,0x34);
             for(i=0;i<2;i++)//分为上下两屏
             {
                       for(j=0;j<32;j++)
                       {
                                LCD_Write(0,lcd_y+j);
                            LCD_Write (0,lcd_x);
                                for(k=0;k<16;k++)//写入显示数据
                            { LCD_Write (1,*p++); }
                       }
                       lcd_x=0x87;
             }
             LCD_Write(0,0x36);
             LCD_Write(0,0x30);
    }
    图片模式下清屏:
    void Clear_Gcrom()
    {
             chari,j,k;
           lcd_x=0x80;
           lcd_y=0x80;
           LCD_Write (0,0x34);
             for(i=0;i<2;i++)
             {
                       for(j=0;j<32;j++)
                       {
                                LCD_Write(0,lcd_y+j);
                            LCD_Write (0,lcd_x);
                                for(k=0;k<16;k++){ LCD_Write (1,0x00); }
                       }
             lcd_x=0x88;
             }
             LCD_Write(0,0x36);
             LCD_Write(0,0x30);
    }
    显示两字节,LCD12864每次至少刷新16格:
    void Show_2Byte(char x,char y,chardat1,char dat2)// x:0~7 y:0~63
    {
    if(y<32)//下屏
      {
       x+=8;
       y=31-y;
      }
    else//上屏
      {
       y=63-y;
      }
    LCD_Write (0,0x34);
    LCD_Write (0,0x80+y);//y:0~31
    LCD_Write (0,0x80+x);//x:0~15
    LCD_Write (1,dat1);
    LCD_Write (1,dat2);
    LCD_Write (0,0x36);
    LCD_Write (0,0x30);
    }
    刷新1行:
    void Show_Hang(char y,char p[64][16]) //显示一行 y:0~63
    {
             chark,y0=y;  
             lcd_y=0x80;
             LCD_Write(0,0x34);
             if(y<32)
           {
             lcd_x=0x80+8;//下屏
             y=31-y;
           }
           else
           {
             lcd_x=0x80;//上屏
             y=63-y;
             }
             //LCD_Write(0,0x80+0);//y1:0~31
           //LCD_Write (0,0x80+0);//x1:0~15
           LCD_Write (0,lcd_y+y);
           LCD_Write (0,lcd_x);
             for(k=0;k<16;k++)//写入显示数据
           { LCD_Write (1,p[y0][k]); }
             LCD_Write(0,0x36);
             LCD_Write(0,0x30);
    }
    有了以上函数就可以轻松玩转LCD12864了,注意在main函数中应先执行初始化程序:

    void main()
    {
    SPI_MasterInit(); 
    LCD_INTI ();
    while(1)
    {

    }
    }

    最后是我用ATMEGA324PA单片机和LCD12864做的一个贪吃蛇游戏,见开头视频。

    有兴趣的可以看下

    全部程序如下:
    /***********************************************************************
    LCD12864贪吃蛇
    贪吃蛇游戏(不死版)
    单片机:ATMEGA324PA
    编程软件:IAR
    编程语言:C++
    屏幕:LCD12864
    按键:上下左右
    ***********************************************************************/
    <main.cpp>:
    #define F_CPU 8
    #include"delay.h"
    #include <stdlib.h>
    #include <math.h>
    #include<ioavr.h>
    #include "function.h"
    #define K0 PINA_Bit0
    #define K1 PINA_Bit1
    #define K2 PINA_Bit2
    #define K3 PINA_Bit3
    #define K4 PINA_Bit4
    #define K5 PINA_Bit5
    class Point{
    public:
      char x;//x:0~127
      char y;//y:0~63
      Point(){};
      Point(char m,char n){x=m;y=n;}
      bool operator==(Point &a)
      {
        if(x==a.x&&y==a.y)return true;
        else return false;
      }
      //char Get_x(){return x;}
      //char Get_y(){return y;}  
    };
    class Snake{
    public:
      char length;
      Point body[64];//头:body[0]尾:body[length-1] 其余:空
      Point food;
      Snake()
      {
        length=3;
        for(char i=0;i<64;i++)body=Point(0,0);
        body[0]=Point(64,33);
        body[1]=Point(64,32);
        body[2]=Point(64,31);
      }
      void Go_ahead();
      //void Go_back();
      void Turn_left();
      void Turn_right();
      void Turn_up();
      void Turn_down();
      void Generate_food();
      void Eat();
      void Restart();
      bool Near_food();
      char Get_direction();
      void Refresh(Point p);
    };
    void Snake::Go_ahead()
    {
      Point temp=body[0],temp1=body[length-1];
      char v=Get_direction();
      switch(v)
      {
      case 0:
        if(body[0].y==63)Restart();
        else body[0].y++;break;
      case 1:
        if(body[0].y==0)Restart();
        else body[0].y--;break;
      case 2:
        if(body[0].x==0)Restart();
        else body[0].x--;break; 
      case 3:
        if(body[0].x==127)Restart();
        else body[0].x++;break;
      default:break;
      }
      if(body[0].x==food.x&&body[0].y==food.y)
      {
        length++;
        for(char i=length-1;i>1;i--)
        {
          body=body[i-1];
        }
        body[1]=temp;
        Generate_food();
      }
      else 
      {
        for(char i=length-1;i>1;i--)
        {
          body=body[i-1];
        }
        body[1]=temp;
      }
      Refresh(body[0]);
      Refresh(temp1);
    }
    void Snake::Turn_left()
    {
      Point temp1=body[length-1];
      for(char i=length-1;i>0;i--)
      {
        body=body[i-1];
      }
      if(body[0].x==0)Restart();
      else body[0].x--; 
      Refresh(body[0]);
      Refresh(temp1);
    }
    void Snake::Turn_right()
    {
      Point temp1=body[length-1];
      for(char i=length-1;i>0;i--)
      {
        body=body[i-1];
      }
      if(body[0].x==127)Restart();
      else body[0].x++;
      Refresh(body[0]);
      Refresh(temp1); 
    }
    void Snake::Turn_up()
    {
      Point temp1=body[length-1];
      for(char i=length-1;i>0;i--)
      {
        body=body[i-1];
      }
      if(body[0].y==63)Restart();
      else body[0].y++;
      Refresh(body[0]);
      Refresh(temp1);
    }
    void Snake::Turn_down()
    {
      Point temp1=body[length-1];
      for(char i=length-1;i>0;i--)
      {
        body=body[i-1];
      }
      if(body[0].y==0)Restart();
      else body[0].y--;
      Refresh(body[0]);
      Refresh(temp1);  
    }
    void Snake::Generate_food()
    {
      char x,y,i=0;
      while(i!=length)
      { x=rand()%128;
        y=rand()%64;
        food=Point(x,y);
        for(i=0;i<length;i++)
        {
          if(food==body)break;
        }
      }
      Refresh(food);
    }
    bool Snake::Near_food()
    {
      bool k;
      if(body[0].x==food.x&&body[0].y==food.y)
        k=true;
      else k=false;
      return k;
    }
    void Snake::Eat()
    {

      for(char i=length;i>0;i--)
      {
        body=body[i-1];
      }
      body[0]=food;
      length++;
      Generate_food();
    }
    //enum direction{up,down,left,right}
    char Snake::Get_direction()
    {
      char i;
      if(body[0].x==body[1].x)
      {
        if(body[0].y>body[1].y){i=0;}
          else i=1;
      }
      else
      {
        if(body[0].x>body[1].x){i=3;}
          else i=2;
      }
      return i;
    }
    void Snake::Refresh(Point p)
    {
      char temp1=0,temp2=0,a,b,c;  
      for(char i=0;i<2;i++)
      {
        a=p.x-p.x%8;
        if((p.x/8)%2==0)
        {
          b=a+8;
          c=b+8;
        }
        else
        {
          b=a;
          a-=8;
          c=b+8;
        }
        for(char i=0;i<length;i++)
        {
          if(body.y==p.y)
          {
            if(body.x>=a&&body.x<b)temp1|=(1<<7-body.x%8);
            if(body.x>=b&&body.x<c)temp2|=(1<<7-body.x%8);
          }
          if(food.y==p.y)
          {
            if(food.x>=a&&food.x<b){temp1|=(1<<7-food.x%8);}
            if(food.x>=b&&food.x<c){temp2|=(1<<7-food.x%8);}
          }      
        }
        Show_2Byte(p.x/16,p.y,temp1,temp2);
      }
    }

    void WDT_off(void)
    {
      __disable_interrupt();
      __watchdog_reset();
      /* Clear WDRF in MCUSR */
      MCUSR &= ~(1<<WDRF);
      /* Write logical one to WDCE and WDE */
      /* Keep old prescaler setting to prevent unintentional time-out */
      WDTCSR |= (1<<WDCE) | (1<<WDE);
      /* Turn off WDT */
      WDTCSR = 0x00;
      __enable_interrupt();
    }
    void WDT_Prescaler_Change(void)
    {
      __disable_interrupt();
      __watchdog_reset();
      /* Start timed equence */
      WDTCSR |= (1<<WDCE) | (1<<WDE);
      /* Set new prescaler(time-out) value = 64K cycles (~16 ms) */
      WDTCSR = (1<<WDE);
      __enable_interrupt();
    }
    void Snake::Restart()
    {  
      //WDT_Prescaler_Change();
    }
    void main()
    {
      WDT_off();
      Snake Snake1;
      DDRB=0xff;
      PORTA=0x00;
      DDRA=0x00;
      SPI_MasterInit();
      LCD_INIT();
      Clear_Gcrom();
      Snake1.Generate_food();
      while(1)
      {

        {
          delay_ms(50);
          Snake1.Go_ahead();
          if(!K2)
          {
            Snake1.Turn_left();
          }
          if(!K3)
          {
            Snake1.Turn_up();
          }
          if(!K4)
          {
            Snake1.Turn_right();
          }
          if(!K5)
          {
            Snake1.Turn_down();
          }      
          //if(Snake1.body[0].x<0||Snake1.body[0].x>127||Snake1.body[0].y<0||

    Snake1.body[0].y>63)
          //Snake1.Die();
        }
      }
    }
    ///////////////////////////////////////////////////////////////////////////
    <LCD12864.c>:
    #define F_CPU 8
    #include<ioavr.h>
    #include"delay.h"
    void SPI_MasterInit(void)
    {
      /* Set MOSI and SCK output, all others input */
      //DDRB =(1<<7)|(1<<5)|(1<<4);//(1<<5)|(1<<7);
      /* Enable SPI, Master, set clock rate fck/16 */
      SPCR = (1<<6)|(1<<4);//|(1<<1)|0x01;//1<<SPE 1<<MSTR 1<<SPR1//128分频
      SPSR = 1<<0;//SPI2X倍速
    }

    void SPI_MasterTransmit(char cData)
    {

      /* Start transmission */
      SPDR = cData;
      /* Wait for transmission complete */
      while(!(SPSR & (1<<7)));//1<<SPIF
    }
    void LCD_Write (char RS,char content)
    {        char Start,High4,Low4;
            Start=0xf8|(RS<<1);
            High4=content&0xf0;
            Low4=(content<<4)&0xf0;
            //SS=1;
            SPI_MasterTransmit(Start);
            SPI_MasterTransmit(High4);
            SPI_MasterTransmit(Low4);
            //SS=0;
            delay_us(300);
    }
    /*-----------------------------------*/
    void LCD_INIT(void)
    {
            LCD_Write (0,0x30); /*30---基本指令动作*/ 
            LCD_Write (0,0x01); /*清屏,地址指针指向00H*/
            LCD_Write (0,0x06); /*光标的移动方向*/
            LCD_Write (0,0x0c); /*开显示,关游标*/
    }
    /*--------------清DDRAM------------------*/
    void ClearRam(void)
    {
            LCD_Write (0,0x30);
            LCD_Write (0,0x01);
    }
    char lcd_x,lcd_y;
    void Clear_Gcrom() 
    {
            char i,j,k; 
            lcd_x=0x80; 
            lcd_y=0x80; 
            LCD_Write (0,0x34); 
            for(i=0;i<2;i++) 
            { 
                    for(j=0;j<32;j++) 
                    {
                            LCD_Write (0,lcd_y+j); 
                            LCD_Write (0,lcd_x); 
                            for(k=0;k<16;k++) { LCD_Write (1,0x00); } 
                    } 
            lcd_x=0x88; 
            } 
            LCD_Write (0,0x36); 
            LCD_Write (0,0x30); 
    }
    void Show_2Byte(char x,char y,char dat1,char dat2)// x:0~7 y:0~63
    {
      if(y<32)//下屏
      {
        x+=8;
        y=31-y;
      }
      else//上屏
      {
        y=63-y;
      }
      LCD_Write (0,0x34);
      LCD_Write (0,0x80+y);//y:0~31
      LCD_Write (0,0x80+x);//x:0~15
      LCD_Write (1,dat1);
      LCD_Write (1,dat2);
      LCD_Write (0,0x36); 
      LCD_Write (0,0x30); 
    }
    void Show_Hang(char y,char p[64][16]) //显示一行 y:0~63

            char k,y0=y;  
            lcd_y=0x80; 
            LCD_Write (0,0x34); 
            if(y<32)
            {
              lcd_x=0x80+8;//下屏
              y=31-y;
            }
            else 
            {
              lcd_x=0x80;//上屏
              y=63-y;
            } 
            //LCD_Write (0,0x80+0);//y1:0~31
            //LCD_Write (0,0x80+0);//x1:0~15
            LCD_Write (0,lcd_y+y);
            LCD_Write (0,lcd_x); 
            for(k=0;k<16;k++) //写入显示数据 
            { LCD_Write (1,p[y0][k]); } 

            LCD_Write (0,0x36); 
            LCD_Write (0,0x30); 

    //////////////////////////////////////////////////////////////////////////////
    <delay.h>:
    #ifndef __IAR_DELAY_H
    #define __IAR_DELAY_H
    #include <intrinsics.h>
    #define   delay_us(x)   __delay_cycles ((unsigned long)(x * F_CPU)) 
    #define   delay_ms(x)   __delay_cycles ((unsigned long)(x * F_CPU*1000UL))
    #define   delay_s(x)    __delay_cycles ((unsigned long)(x * F_CPU*1000000UL))
    #endif 
    //////////////////////////////////////////////////////////////////////////////
    <function.h>:
    #ifndef _FUNCTION_INCLUDED
    #define _FUNCTION_INCLUDED
    extern void Show_Hang(char y,char p[64][16]); //显示一行 y:0~63
    extern void LCD_INIT(void);
    extern void Clear_Gcrom();
    extern void ClearRam(void);
    extern void SPI_MasterInit(void);
    extern void Show_2Byte(char x,char y,char dat1,char dat2);// x:0~7 y:0~63
    #endif

  • 相关阅读:
    bzoj2101:[USACO2010 DEC]TREASURE CHEST 藏宝箱
    P3976 [TJOI2015]旅游(未完成)
    洛谷 P 5 3 0 4 [GXOI/GZOI2019]旅行者
    NOIP原题 斗地主(20190804)
    P2860 [USACO06JAN]冗余路径Redundant Paths
    vue中的插槽(slot)
    vue动态绑定class
    发现一个ps抠毛发简单快捷高质量的方法
    propsData传递数据
    sort排序原理
  • 原文地址:https://www.cnblogs.com/watson8544/p/5581330.html
Copyright © 2011-2022 走看看