码迷,mamicode.com
首页 > 其他好文 > 详细

[转载]轻松玩转LCD12864-基于AVR单片机的LCD12864串行显示

时间:2016-06-13 18:58:37      阅读:236      评论:0      收藏:0      [点我收藏+]

标签:

原文链接: 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

[转载]轻松玩转LCD12864-基于AVR单片机的LCD12864串行显示

标签:

原文地址:http://www.cnblogs.com/watson8544/p/5581330.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!