[转载]轻松玩转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脚的定义如下:

1:VSS,接地端

2:VDD,电源正,接+5V

3:VO,对比度调整,一般接+5V就行了

4:D/I(CS*),片选,也叫使能,接+5V

5:R/W(SID*),数据输入端

6:E(SCLK*),时钟输入端

7~14:DB0 ~ DB7,并行数据总线

15:PSB,串并模式选择,串行模式下接地,并行模式下接+5V

16:NC,空引脚,不需要连接

17:RSTB,复位端,低电平有效,一般接+5V就行了

18:VEE,空引脚,不需要连接

19:BLA,背光正极,接+5V

20: BLK,背光负极,接地

在实际编程时,有串行、并行两种模式可以选择。个人觉得,并行模式占用单片机引脚多(11个),优点是速度快(一次传8位数据,速度自然快),串行模式占用引脚少(2个),速度慢点。我喜欢使用串行模式,AVR单片机的时钟频率最快可达20MHZ(不用除以12),经过实测,从头到尾刷一次屏大约只需0.1s,这在很多场合已经够用了。由于并行模式用的少,不熟悉,下面只讲串行模式。

在串行模式下,硬件的连接为:1、15、20接地,2、3、4、17、19接+5V,5接单片机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);//设置MOSI和SCK 为输出

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);//发送开始字节,前面5个1,倒数第二位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; //在第1行y列显示

case1: adr = 0x90 + y0;break; //在第2行y列显示

case 2: adr = 0x88 + y0;break; //在第3行y列显示

case3: adr = 0x98 + y0;break; //在第4行y列显示

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; //在第1行y列显示

                   case1: adr = 0x90 + y0;break; //在第2行y列显示

                   case2: adr = 0x88 + y0;break; //在第3行y列显示

                   case3: adr = 0x98 + y0;break; //在第4行y列显示

                   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; //在第1行y列显示

                   case1: adr = 0x90 + y0;break; //在第2行y列显示

                   case2: adr = 0x88 + y0;break; //在第3行y列显示

                   case3: adr = 0x98 + y0;break; //在第4行y列显示

                   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) //水平扫描

//P含128*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

时间: 2024-10-06 01:19:58

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

基于51单片机DS18B20测温LCD1602显示可设时设温调时的项目

一.前言 1.基于51单片机DS18B20测温LCD1602显示可设时设温调时的项目包括用Keil软件编写单片机C语言程序和用Proteus软件仿真单片机外围电路 2.基于51单片机DS18B20测温LCD1602显示可设时设温调时的项目构思 (1).声明程序变量思维导图 (2).程序子函数思维导图 (3).程序主函数思维导图 二.基于51单片机DS18B20测温LCD1602显示可设时设温调时的项目的Keil软件编写的单片机C语言程序 1 #include<reg52.h>//声明51单片机

基于51单片机的温度控制系统毕设

分享一下单片机毕设课程计参考资料33个,献给大四的朋友,毕业设计就不用愁了,如果有什么不懂的地方可以加群大家一起交流:813238832资料下载的地址:https://bbs.usoftchina.com/thread-208012-1-1.html 1.CDMA通信系统中的接入信道部分进行仿真与分析2.USB接口设计3.毕业设计(论文)OFDM通信系统基带数据4.电气工程系毕业设计开题报告5.电信运营商收入保障系统设计与实现6.基于51单片机的16×16点阵(滚动显示)7.基于51单片机的LE

基于51单片机的万年历(算法实现)

基于51单片机的万年历,用到了单片机独立键盘.数码管.LED灯模块实现. 想要简单还是DS1302好用. 1 /************************************************** 2 3 作者:纟彖氵戋 博客:http://www.cnblogs.com/yllinux/ 4 5 时间:2017年6月7日 6 7 目标:利用单片机独立键盘.数码管.LED灯模块实现万年历(算法实现) 8 9 ************************************

AVR单片机入门

ATMEL公司有基于8051内核.基于AVR内核和基于ARM内核的三大系列单片机产品. 先进的EEPROM电可擦除和Flash ROM闪速存储器技术. 8051单片机采用复杂指令系统:CISC:由于CISC结构存在指令系统不等长,指令数多,CPU利用效率低,执行速度慢等缺陷:AVR单片机采用精简指令系统:RISC:RISC采用流水线操作(Pipelining),和等长指令体系结构. AVR单片机吸取了PIC及8051等单片机的优点,改进: 1.程序存储器为价格低廉.可擦写1万次以上.指令长度单元

[原创]基于51单片机的红外遥控课程设计

[注]: 一眨眼,大学接近尾声,具有找工作需要,所以把大学做的电子设计“劣作”放上来.希望考研失意,还能赶上“好工作”的春招班车.如果大伙有什么工作推荐也可以联系我哦,因为一年考研少接触了这方面,所以难免有些生疏.但请相信我!给我机会我会很认真学的! 邮箱:[email protected] 转载请注明出处呀! 基于51单片机的红外遥控课程设计 目录 第一章 设计简介... 3 第二章 系统方案... 3 一.设计方案对比... 3 二.方案设计... 4 第三章 硬件设计... 5 一.红外遥

单片机成长之路(avr基础篇)- 003 AVR单片机的BOOT区

BOOT区的由来基于一个简单的道理,即单片机的程序是保存在FLASH中的,要运行程序就必须不停的访问FLASH存储器.对于一般的FLASH存储器,数据的写入需要一定的时间来完成,在数据写入完成之前,存储器中所有的数据都是不可读的,这就在运行旧程序和写入新程序之间造成了一个矛盾. 使用BOOT区是解决这个矛盾的方法之一,它将FLASH存储器从物理上分为两个独立的区域,对其中的一个区的数据写入不会影响到另一个区的数据读取操作.我们可以让单片机的程序在其中一个区(通常是BOOT区)运行,而运行着的程序

AVR单片机教程——串口发送

到目前为止,我们的开发板只能处理很小量的数据:读取几个引脚电平,输出几个LED,顶多用数码管显示一个两位数字.至于输入一个指令.输出一条调试信息,甚至用scanf和printf来输入输出,在已经接触过的这些器件上是难以想象的.而本讲"串口发送"与下一讲"串口接收",将打开这一扇大门. 硬件 本讲的主题是UART(Universal Asynchronous Receiver-Transmitter,通用异步收发器),俗称串口.实际上串口是串行接口的统称,在单片机领域

AVR单片机教程——矩阵键盘

本文隶属于AVR单片机教程系列. ? 开发板上有4个按键,我们可以把每一个按键连接到一个单片机引脚上,来实现按键状态的检测.但是常见的键盘有104键,是每一个键分别连接到一个引脚上的吗?我没有考证过,但我们确实有节省引脚的方法. 矩阵键盘 这是一个4*4的矩阵键盘,共有16个按键只需要8个引脚就可以驱动.我们先来看看它的原理. 每个按键有两个引脚,当按键按下时接通.每一行的一个引脚接在一起,分别连接到左边4个端口,称为"行引脚":每一列的另一个引脚接在一起,分别连接到右边的4个端口,称

【001】AVR单片机简介

概述: AVR单片机是1997年由ATMEL公司研发出的增强型内置Flash的RISC(Reduced Instruction Set CPU) 精简指令集高速8位单片机. AVR单片机内嵌高质量的Flash程序存储器,擦写方便,支持ISP和IAP,便于产品的调试.开发.生产.更新.内嵌长寿命的EEPROM可长期保存关键数据,避免断电丢失.片内大容量的RAM不仅能满足一般场合的使用,同时也更有效的支持使用高级语言开发系统程序,并可像MCS-51单片机那样扩展外部 RAM. AVR单片机的I/O线