EDN首页   博客首页

最新日志

发表于:2008-11-4 22:26:06
标签:无标签

4

51开发板学习之定时器与电子钟

已经好久没有登陆EDN了
最后一个51例程也在2个星期之前就做完了,这个时期也算是在调整吧
加入的电子园的USB学习小组,也申请了免费PCB,并且两次跑到元器件市场

终于买到了大部分的零件,下一个阶段就准备从51进阶到USB开发的学习上来

下面是我整理出来51开发板的最后一个学习程序
用定时器,按键,1602液晶做一个电子钟程序  另外说明一下,其实这并非是最

后一个我学习的51程序,只是因为那最后一个程序是运用DS12887做一个精密

的电子钟,但是12887板上没有下载口,调试起来很麻烦,为了不插拔弄坏单片

机,所以决定暂时先不做这个程序了。好了言归正传。
首先这个电子钟主要就是对51单片机的几个学习重点进行回顾和综合运用,键

盘扫描,定时器与中断,1602液晶的显示等等,如果把这个程序做好的话,基

本上51算是入门了,当然,学习之路还是很漫长的,入门不算是精通,还有很

多要继续学习掌握。
首先,我想先说说,至于C语言编程,还是能省则省,毕竟不是汇编。比如说一

个取字符串长度的函数就可以直接加入到头文件中,这样以后就不要一个个数

字符串的长度了(就视频教程的一些不尽人意之处而发,作为教学我觉得应该要

早培养学习者尽量写简洁代码的意识)。首先是位定义,这里应该基本上都没什

么问题。之后的延时函数也很有必要,我发现在大部分的学习程序中延时函数都

是必不可少的的函数。之后就是一些1602相关的字程序,在上一篇日志中已经做

了详尽描述。接着是键盘扫描程序,我觉得本程序的关键点有两个,一个就是键

盘扫描,另外一个就是中断,基本上所有的工作都要在上述两个关键点中完成。

我们设想,当定时器每1S产生一个中断,之后在中断中要刷新液晶显示,以实现

秒分时的跳动,另外一个就是我们可以通过键盘来设置时间。这就是这个电子种

的基本功能(多说两句,用定时器做的电子钟误差很大,所以这个程序没有实用

价值,纯粹是作为学习用途而已)定时器运用就很简单了,1S产生一个中断,使

得秒自加1,当然,秒分时都要做溢出判断,这是外话,之后在1602液晶中刷新

显示即可。而按键扫描稍微复杂一点,首先要先检测主按键是否按下,在主按键

按下的情况下,要把中断暂停,进而执行余下的按键扫描程序,这样的话主按键

按下后时钟就停止跳动,并且此时,假若再按下一次主按键,那么光标就会跳动

到时位上,依此类推,再按一次主按键,光标就跳到分,直至秒为止,假若光标

跳到秒以后,并无其他按键操作的情况下再按一次主按键,则光标消失,同时时

钟恢复跳动。剩下的难点就是按键扫描工作,主要是对时间+键和时间-键的检

测,时间+ 键按下一次对应的数字+1,时间-键则正好相反,只要注意这余下的

键盘扫描必需是嵌套在主按键按下时的服务程序中就可以了,这样的话基本上整

个程序就没什么难点,相当于是一个51开发板复习的好程序。


下面把代码贴出:

#include <reg52.h>
#include <string.h>
#define uchar unsigned char
sbit lcden="P2"^1;
sbit FM="P2"^2;
sbit rs="P2"^0;
sbit s1=P3^0;
sbit s2=P3^1;
sbit s3=P3^2;
uchar table1[]="2008-10-20 MON";
uchar table2[]="    11:58:00";
uchar i,long1,long2,cnum=0,pnum=0;
char miao="0",shi=11,fen=58;
void delay(uchar z)          //延时函数
{
 uchar x,y;
 for(x=z;x>0;x--)
  for(y=100;y>0;y--);
}
void write_con(uchar con)   //写指令函数
{
 rs=0;
 P0=con;
 delay(5);
 lcden=1;
 delay(5);
 lcden=0;
}

void write_data(uchar dat)   //写数据函数
{
 rs=1;
 P0=dat;
 delay(5);
 lcden=1;
 delay(5);
 lcden=0;
}
void init()        

//初始化函数
{
 FM=1;
 lcden=0;
 write_con(0x38);     //开显


 write_con(0x0e);     //显示

指针,不闪烁  
 write_con(0x06);     //每写

一字符指针自动+1
 write_con(0x01);     //清屏

指针归零
 write_con(0x80);     //定义

指针位置
}

void keyscan();                        //键盘扫描

void main()
{
 init();
 long1=strlen(table1);                  //取table1长度
 long2=strlen(table2);       //取talbe2长度
 for(i=0;i<long1;i++)     
 {
  write_data(table1[i]);
  delay(30);
 }
 write_con(0x80+0x40);
 for(i=0;i<long2;i++)
 {
  write_data(table2[i]);
  delay(30);
 }

 

 EA=1;       

  //开总中断
 ET0=1;       

  //打开定时器0
 TMOD=0x01;      

  //设置定时器工作方式为方式1
 TH0=(65536-50000)/256;     
 TL0=(65536-50000)%256;
 TR0=1; 
 while(1)
 {
  keyscan();
  if(cnum==20)
  {
   cnum=0;
   miao++;
   if(miao==60)
   {
    miao=0;
    fen++;
    if(fen==60)
    { 
     fen=0;
     shi++;
     if(shi==24)
     {
      shi=0;
      
     }
     write_con(0x80+0x44);
     write_data(0x30+shi/10);
     write_data(0x30+shi%10); 

   
    }
    write_con(0x80+0x47);
    write_data(0x30+fen/10);
    write_data(0x30+fen%10);

   }
   write_con(0x80+0x4a);
      write_data(0x30+miao/10);
      write_data(0x30+miao%10);
  }
  
 } 

 


void intertimer0() interrupt 1
{
 TH0=(65536-50000)/256;
 TL0=(65536-50000)%256;
 cnum++;
}


void keyscan()        //键盘

扫描程序
{
 if(s1==0)
 {
  delay(10);
  if(s1==0)
  {
   while(!s1);
   delay(10);
   while(!s1);
   pnum++;
   TR0=0;
   if(pnum==4)
   { 
    write_con(0x80+0x4c);
    pnum=0;
    TH0=(65536-50000)/256;
    TL0=(65536-50000)%256;
    TR0=1;
   }
  }  
 }
 if(pnum!=0)
 {
  if(pnum==1)
  {
   write_con(0x80+0x45);
   if(s2==0)
   { 
    delay(20);
    if(s2==0)
    {
     while(!s2);
     delay(20);
     while(!s2);
     shi++;
     if(shi==24)
      shi=0;
     write_con(0x80+0x44);
     write_data(0x30+shi/10);
     write_data(0x30+shi%10);
     write_con(0x80+0x45);
    }
   }
   if(s3==0)
   { 
    delay(20);
    if(s3==0)
    {
     while(!s3);
     delay(20);
     while(!s3);
     shi--;
     if(shi==-1)
      shi=23;
     write_con(0x80+0x44);
     write_data(0x30+shi/10);
     write_data(0x30+shi%10);
     write_con(0x80+0x45);
    }
   }
  }
  
  else if(pnum==2)
  {
   write_con(0x80+0x48);
   if(s2==0)
   { 
    delay(20);
    if(s2==0)
    {
     while(!s2);
     delay(20);
     while(!s2);
     fen++;
     if(fen==60)
      fen=0;
     write_con(0x80+0x47);
     write_data(0x30+fen/10);
     write_data(0x30+fen%10);
     write_con(0x80+0x48);
    }
   }
   if(s3==0)
   { 
    delay(20);
    if(s3==0)
    {
     while(!s3);
     delay(20);
     while(!s3);
     fen--;
     if(fen==-1)
      fen=59;
     write_con(0x80+0x47);
     write_data(0x30+fen/10);
     write_data(0x30+fen%10);
     write_con(0x80+0x48);
    }
   }
  }
 
  else
  {
   write_con(0x80+0x4b);
   if(s2==0)
   { 
    delay(20);
    if(s2==0)
    {
     while(!s2);
     delay(10);
     while(!s2);
     miao++;
     if(miao==60)
      miao=0;
     write_con(0x80+0x4a);
     write_data(0x30+miao/10);
     write_data(0x30+miao%10);
     write_con(0x80+0x4b);
    }
   }
   if(s3==0)
   { 
    delay(20);
    if(s3==0)
    {
     while(!s3);
     delay(20);
     while(!s3);
     miao--;
     if(miao==-1)
      miao=59;
     write_con(0x80+0x4a);
     write_data(0x30+miao/10);
     write_data(0x30+miao%10);
     write_con(0x80+0x4b);
    }
   }
  }
 }    
}                             

后记:学些51有两个月了,其实我觉得真正要入门的话,半个月够了,只是这

中间进度拖拖拉拉,并不是一直继续的而已。另外,这两个月我在学习51板的

同时也自己仿照做了一块51开发板,目前原理图和PCB图的绘制已经完毕,正

在修改错误,并在适当的时机送厂家试试自己的第一块PCB板 
 呵呵  我争取在下一篇日志中把上述的开发板原理图和PCB图都贴出来晒晒
一起学习共同进步

系统分类: 单片机   |    用户分类:    |    来源: 原创

评论(5) | 阅读(380)
发表于:2008-9-30 19:53:08
标签:1602  51单片机  

1

1602液晶初学试用

今日无事,就把1602液晶试着跟这教程走了一遍

边抄边自己写  写了一点代码 感觉目前按模块来学习51还是蛮轻松的,这种学习效率也挺高的。不说了,先从原理图和1602的datasheet开始了

本人的开发板是从同学那拿来的,算是一个精简的51开发板吧 先看看关于1602的连接方式信号定义

点击开大图

这里提一下,板上已经将5脚直接接地了,根据管脚信号定义可知5脚是读写控制端,接地就是意味着液晶就只能是写数据而不能读数据  这个没啥太大的关系,初学嘛,写就行了  3脚接了可变电阻用来调节亮度

下面查看一下datasheet的操作指令部分和读写操作部分

点击开大图

在根据时序图可以看出 针对本开发板  RW==0

写指令的时候,RS=0,需要写入指令先建立好,然后EN来个高脉冲(这个高脉冲有持续时间要求)就可以把指令写入LCD

写数据的过程和以上过程类似,就是RS=1罢了

点击开大图

点击开大图

下面根据上图总结一下基本操作指令

显示模式指令0x38    显示开关指令一般用0x0f或者0x0e(开显示,显示不闪烁光标)

之后的光标移动方向指令一般用0x06(写入字符后指针+1光标+1)

另外一个整屏移动指令0x18要在英文datasheet中才有,这个中文datasheet省略了

(这个指令主要针对的是写入的字符逐渐从右“飞入”显示屏)

地址指针指令:0x80+(0x00~0x27)   第一行

                            0x80+(0x40~0x67)   第二行

清屏指令:0x01

好了,了解了以上这些,可以开始试用了

 

下面是代码

 

/*################# 开发板说明:R/W读写使能端已经接地,说明该液晶的功能已经限定了只能写入#########*/
#include <reg52.h>
#include <string.h>
#define uchar unsigned char
#define uint unsigned int
sbit lcdrs="P2"^0;
sbit lcden="P2"^1;
uchar table1[]="I love Nightwish";
uchar table2[]="dnchina.com";
uchar i,l1,l2;

void delay(uchar z)         //延时函数

{
 uint a,b;
 for(a=100;a>0;a--)
  for(b=z;b>0;b--);
}
void writedata(uchar dat)                
{
lcdrs=1;
 P0=dat;
 delay(5);
 lcden=1;
 delay(5);
 lcden=0; 
}
void writecode(uchar cod)
{
 lcdrs=0;
 P0=cod;
 delay(5);
 lcden=1;
 delay(5);
 lcden=0;
}
void initlcd()                    //液晶初始化函数
{
 lcden=0;
 writecode(0x38);
 writecode(0x0e);
 writecode(0x06);
 writecode(0x01);
 writecode(0x80+0x10);


}
void main()
{
 initlcd();
 l1=strlen(table1);
 l2=strlen(table2);
 for(i=0;i<l1;i++)
 { 
  writedata(table1[i]);
  delay(30);
 }
 writecode(0x80+0x50);
 delay(5);
 for(i=0;i<l2;i++)
 {
  writedata(table2[i]);
  delay(10); 
 }
 for(i=0;i<16;i++)
 {
  writecode(0x18);
  delay(40);
 } 
 while(1);                                //不明白,不加个while(1)的话蜂鸣器居然会启动? 
}

不行了,代码贴上去本来还想加一点注释,没想到添的几个字把后面的字都覆盖了,暂时也不知道怎么解决就干脆不加注释了

从函数名就可以看出那几个函数是干什么的

另外非常搞不明白,不插LCD的时候把程序写入单片机居然蜂鸣器会启动,我真的是想不同,控制蜂鸣器的三极管开关是P2.2口,这里根本就没碰过P2.2,居然会有频率的发出声音,我真的见鬼了,还希望大侠给点解释。

烧入程序后,呵呵,字符就从右边飞入屏幕了,1602的初试成功啦

还有就是如果你想居中显示的,你要调整一下初始的指针地址就可以了  自己试试吧

还有,一开始我居然把液晶插反了,程序烧都烧不进去。。。

第一篇很二百五的日志,记录自己做的整个过程而已,学在其中,乐在其中~~

系统分类: 单片机   |    用户分类:    |    来源: 原创

评论(1) | 阅读(164)
发表于:2008-9-28 10:53:58
标签:无标签

0

开博了

从今日开始

技术类blog在Edn开博啦

 

我是个51单片机初学者  目前也刚进公司  什么都处于学习阶段

请各位高手多多指点!

系统分类: 单片机   |    用户分类: 无分类    |    来源: 无分类

评论(1) | 阅读(101)
总共 , 当前 /