2017-05-07 23:04:49 tian_maer 阅读数 192
1、如果一个变量只有几种可能的值,那么可以将该变量定义为枚举类型。所谓“枚举”是将变量的值一一列举出来,变量的取值只限于列出的范围。
枚举定义的语句格式如下:
enum 枚举名{枚举值列表}变量列表;
定义和说明也可以分两句完成:
enum 枚举名{枚举值列表};
enum 枚举名 变量列表;
例如:今天是星期几,结果只能是星期一~星期日这七种。因此可以定义枚举变量:
enum weekday{mon,tue,wed,thu,fri,sat,sun}date1,date2;
或者:
enum weekday{mon,tue,wed,thu,fri,sat,sun};
enum weekday date1,date2;
 
2、枚举类型的说明
1)在C编译器中对枚举元素按常量处理,因此也称为枚举常量(注意:不能对枚举元素进行赋值)。
2)枚举元素作为常量,它是有值的,C语言编译时按定义时的顺序使他们的值为0,1,2,3...,也可以改变枚举元素的值,在定义时直接指定元素的值;
3)枚举值可以用来做比较判断,枚举值的比较规则是按其在定义时的顺序号比较。如果定义时未人为指定,则第一个枚举元素的值默认为0;
4)一个整数不能直接付给一个枚举常量,他们属于不同的类型,应先进行强制类型转换才能赋值
 
3、本例演示枚举类型的定义、枚举变量的定义、枚举元素的引用。
4、在keil c51中新建项目文件ex35,编写如下程序代码,编译并生成ex35.hex文件。
//实例35:枚举类型数据演示实例
#include <reg51.h>    // 包含头文件
void delay(void)     //延时函数
{
  unsigned int i,j;
 for(i = 0;i < 5000;i++)
  for(j = 0;j < 50;j++);
}
void main(void)
{
  enum weekday{mon=2,tue,wed,thu,fri,sat,sun};  //定义枚举数据类型
 enum weekday num1;        //定义枚举变量
 num1 = sat;            //给枚举变量赋值
 P0 = num1;            //送P0口显示
 delay();
 num1 = (enum weekday)3;         //使用强制类型转换,将整型值赋给枚举变量
 if(num1 == thu)        //用枚举值进行判断
 {
   P0 = 0xf0;        //
 }
 else
 {
   P0 = 0x0f;
 }
 while(1)
 {
 }
}
5、在proteus中新建仿真文件ex35.dsn,原理图如下图所示:

6、将ex35.hex文件载入at89c51中,启动仿真,运行结果如下

 
2016-12-28 13:29:41 qq_32506555 阅读数 363
做了一单片机设计,要用C语言与汇编语言同时实现,现将这次设计的感受和收获,还有遇到的问题写下,欢迎感兴趣的朋友交流想法,提出建议。
  单片机设计:基于51单片机的99码表设计
  软件环境:Proteus8.0 + Keil4
  要求:1,开关按一下,数码管开始计时。2,按两下,数码管显示静止。3,按三下,数码管数值清零。
  C语言程序如下:
  #include
  #define uint unsigned int
  #define uchar unsigned char
  uchar shi,ge,aa,keycount=0,temp;
  sbit anjian=P1^7;
  uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
  void display(shi,ge);
  void key ();
  void init();
  void delay(uint z);
  /*-----主程序-----*/
  void main()
  {
  init(); //初始化
  while(1)
  {
  key ();
  if(keycount==1)
  TR0=1; //开中断
  if(keycount==2)
  TR0=0;
  if(keycount==3)
  {
  temp=0;
  keycount=0;
  }
  if(aa==10){aa=0;
  if(temp<=99)
  {
  temp++;display(shi,ge);
  }
  else
  temp=0;}
  }
  }
  /*------初始化程序-------*/
  void init()
  {
  keycount=0;
  temp=0;
  TMOD=0x01;
  TH0=(65536-50000)/256;
  TL0=(65536-50000)%256;
  EA=1;
  ET0=1;
  //TR0=0;
  }
  /*-----定时器中断-----*/
  void timer0() interrupt 1
  {
  TH0=(65536-50000)/256;
  TL0=(65536-50000)%256;
  aa++;
  }
  /*-----显示子程序-----*/
  void display(shi,ge)
  {
  shi=temp/10;
  ge=temp%10;
  P0=table[shi];;delay(70);
  P2=table[ge]; ;delay(70);
  }
  /*-----按键检测子程序-----*/
  void key ()
  {
  if(anjian==0)
  {
  delay(5); //消抖
  if(anjian==0)
  keycount++;
  }
  //while(anjian==0);
  //display(shi,ge); //等待按键弹起
  }
  /*-----延时子程序-----*/
  void delay(uint z) //延时约1ms
  {
  uint x,y;
  for(x=z;x>0;x--)
  for(y=100;y>0;y--);
  }
  电路仿真结果如下:
QQ截图20160816102515.png
好了,那么接下来我们就开始C语言——>汇编语言之旅^_^
  (1)C语言1-10行改为
  ORG 0000H //汇编起始伪指令,功能是规定程序存储器中源程序或数据块存放的起始地址
  ajmp STAR //ajmp无条件跳转指令
  ORG 000bh
  ajmp timer0
  anjian equ P1.7 //位定义
  keycount equ 40h
  shi equ 41h
  gewei equ 42h
  aa equ 43h
  temp equ 44h
  tab: db 3fh,6h,5bh,4fh,66h //建表
  db 6dh,7dh,7h,7fh,6fh
  (2)C语言中的初始化函数 12-14行和39-49行改为
  1 STAR: 2 acall init //子程序近程调用指令,功能是主程序调用子程序,调用子程序的范围为2kb
  init:
  mov keycount,#0 //keycount=0
  mov temp,#0 //temp=1
  mov tmod,#01h //TMOD=0x01
  mov TH0,#60
  mov TL0,#176
  setb EA //位置位指令,对操作数所指出的位进行置1操作
  setb ET0
  setb TR0
  ret
  acall为子程序近程调用指令,返回用ret。
  (3)C语言中15-35行是个while循环,逻辑比较繁琐,注意了!
  START:
  acall display
  inc temp //加1指令,将操作数所指定的单元或寄存器中的内容加1
  acall delay70 //近程调用delay70
  x8: mov r0,keycount
  cjne r0,#2,F1 //cjne比较跳转指令,若r0=2则跳转到x8,否则跳转到F1。
  ajmp x8
  F1: mov r0,temp
  cjne r0,#99,START //若r0<99时,重复循环,否则temp=0
  mov temp,#0
  ajmp START
  F9:
  acall key
  mov r0,keycount
  cjne r0,#0,F2 //keycount=0顺序执行,否则跳转到F1
  CLR P1.3 //清0
  SETB TR0
  F2: mov r0,keycount //第二次按键
  cjne r0,#2,F2
  clr TR0
  reti
  mov r0,keycount //第三次按键
  cjne r0,#3,F3
  mov temp,#0
  mov keycount,#0
  inc 增量指令,功能是将操作数所指定的单元或寄存器中的内容加1,其结果返还回原操作数单元中。
  clr 位复位,功能是对操作数所指出的位进行清“0”操作。
  或者在中断函数中
  timer0:
  w1:
  acall key
  mov TH0,#60
  mov TL0,#176
  cpl p1.0
  JB keycount,x2
  ajmp x3
  x2:
  ajmp START
  clr p1.0
  ajmp w1
  ajmp w1
  x3: mov r0,keycount
  cjne r0,#3,w1 //若r0=3则顺序执行,否则跳转到w1
  mov temp,#0
  mov keycount,#0
  ret
  (4)C语言58-64行display函数改为
  display:
  mov a,temp
  mov b,#10
  div ab //除法指令,实现两个八位无符号数的除法操作。
  mov r2,A
  mov r3,B
  mov dptr,#tab //16位数据传送使用方式
  mov a,r2
  movc a,@a+dptr //查表,先将累加器A的内容与数据指针寄存器DPTR的内容相加,再以其结果为地址,将该地址的结果送入A中
  mov P0,a
  acall delay70
  nop //空指令
  mov a,r3
  movc a,@a+dptr
  mov P2,a
  nop
  acall delay70
  ret
  div为除法指令,功能是实现两个8位无符号数的除法操作,一般被除数放在累加器A中,除数放在寄存器B中。指令执行后,商放在A中,余数放在B中。
  movc为查表指令,先将累加器A的内容与数据指针寄存器DPTR的内容相加,再以其结果为地址,将该地址的内容送入A中。
  nop为空操作指令,它不作任何操作,但要占用一个机器周期(即12个振荡周期)的时间,常用于延时或等待。(有些程序执行的效果由于延时时间太短,在人眼视觉暂时作用下无法辨认清楚)
  此段程序的作用在于将一个两位数分别分在一个十位上的数码管和一个个位上的数码管显示。
  (5)C语言66-76行key函数改为
  1 key:2 jb anjian,F6 //若anjian=0则顺序执行,否则跳转到F63 ACALL delay54 inc keycount //keycount++5 F6: 6 ret
  jb为位条件转移指令,功能是若直接寻址的位bit=1,则程序转移到指定的目标地址去执行,若bit=0,则程序顺序执行。
  (6)C语言78-83行delay函数改为
  delay70:
  mov r6,#70
  D2: mov R7,#248
  d1: djnz R7,d1 //248*70次
  djnz R6,D2
  ret
  delay5:
  mov r6,#5 //消抖。
  F7: mov R7,#248
  F8: djnz r7,F8 //248*5次
  djnz r6,F7
  ret
  注意:248=2 8 ,约等于1ms。delay为延时程序。
  温馨提示:在汇编中程序代码的大小写不受影响,但在C语言中就有影响了。
  思考1:ret 和 reti都是程序返回指令,有什么区别?
  我的回答:ret是子程序返回指令,reti是中断子程序返回指令。区别在于如果是acall 、lcall指令调用的子程序,返回指令就用ret;如果地址是0003,0013,000B,001B,0023调用的子程序,返回指令就用reti。
  思考2:mov 20h,#0h 和 setb 20h 都是加1,用什么区别?
  我的回答:mov指令中的20h指字节,setb中的20h是位。
  旅途结束!
还记得前段时间我一直纠结于汇编语言中的各种指令的语法和功能,直到一个阳光明媚的中午,我一手拿着已经写好的两页半的C语言代码,一手拿着一本单片机的汇编指令查询手册,开始一行一行的翻译,可能汇编代码会在调试中有所错误,但基本逻辑是对的。而且这次C——>汇编,使我更加深入地理解了数据在计算机中的存储与调用。在此期间班主任和同学也给我答疑解惑,相信在以后的道路上,我会更加更深入地理解计算机。越努力,越幸运!

来源: Linux公社
2016-01-25 11:49:00 baidu_33836580 阅读数 10270

题目。写出一个60s倒计时的汇编语言,不要用C语言写,用汇编语言。

再设置一个按键,按一下,停止倒计时,再按一下,恢复倒计时。

;---------------------------------------
;假设系统晶振为 12MHz
;显示电路可见:http://xiangce.baidu.com/picture/detail/b72870d1c3d05f0783143da67f93282ecbab14f6

;按键连接在P3.2

;---------------------------------------
        ORG  0000H
        LJMP MAIN
        ORG  0003H
        LJMP X0_INT
        ORG  000BH
        LJMP T0_INT
;---------------------------------------
        ORG  0100H
MAIN:                            ;初始化
        MOV  TMOD, #01H
        MOV  TL0, #(65536 - 50000) MOD 256
        MOV  TH0, #(55536 - 50000) / 256
        SETB TR0
        SETB ET0
        SETB EX0
        SETB EA
        MOV  DPTR, #TAB          ;表首址送DPTR
        MOV  R1, #0
        MOV  R2, #0
        MOV  R3, #20
;---------------------------------------
M_LOOP:
        CALL DIS
        SJMP M_LOOP
;---------------------------------------
T0_INT:
        MOV  TL0, #(65536 - 50000) MOD 256
        MOV  TH0, #(55536 - 50000) / 256
        DJNZ R3, T0_E
        MOV  R3, #20
        DEC  R1
        CJNE R1, #255, T0_E
        MOV  R1, #9
        DEC  R2
        CJNE R2, #255, T0_E
        MOV  R2, #5
T0_E:
        RETI
;---------------------------------------
X0_INT:
        CALL DIS
        JNB  P3.2, X0_INT
        CPL  TR0
        RETI
;---------------------------------------
DIS:                             ;显示程序.
        MOV  A,  R2              ;显示十位.
        MOVC A,  @A+DPTR
        CLR  P2.1
        MOV  P1, A
        SETB P2.0                ;十位的位选.
        CALL DLY

        MOV  A,  R1              ;个位显示.
        MOVC A,  @A+DPTR
        CLR  P2.0                ;关闭十位的显示.
        MOV  P1, A
        SETB P2.1                ;个位的位选.
        CALL DLY
        RET
;------------------------------
TAB:    DB  0xc0,0xf9,0xa4,0xb0,0x99  ;共阳极段码 0~4
        DB  0x92,0x82,0xf8,0x80,0x90  ;共阳极段码 5~9
;---------------------------------------
DLY:    MOV  R6, #20            ;10ms延时.
D1:     MOV  R7, #248
        DJNZ R7, $
        DJNZ R6, D1
        RET
;---------------------------------------
end

原题网址:http://zhidao.baidu.com/question/282485139.html#here

2018-08-10 22:01:05 m0_37888031 阅读数 12558

1. 设计任务:

本次设计用由AT89C51编程控制LED七段数码管作显示的球赛计时计分系统。系统应具有赛程定时设置,赛程时间暂停,及时刷新甲、乙双方的成绩以及赛后成绩暂存等功能。
内容包括:显示、键盘、时钟等内容

2. 设计说明

2.1 需求分析

2.1.1 计分系统:

(1)分为“A队”和“B队”的“3分”,“2分”,“1分”各六个按键输入部分,以及两队比分的LED显示部分。在每次增加分数的同时,“24秒”计时恢复为初始值,“12分钟”计时暂停,等待比赛的继续进行

2.1.2 计时系统:

(1)比赛开始/继续:当“比赛开始/继续”键按下后,“24s”和“12分钟”计时器开始倒计时;
(2)手动24s复位:当“24s复位” 键按下后,“24s”倒计时恢复初始值,“12分钟”倒计时暂停,等待比赛的继续;
(3)比赛暂停:“比赛暂停”键按下后,“24s”倒计时和“12分钟”倒计时均暂停,等待比赛的继续进行;
(4)自动增加比赛节数:当“12分钟”倒计时完成后,判断当前节数是否为最后一节,若不是,则节数自动增加,并“12分钟”和“24s”均恢复默认值,等待下一节比赛的进行。

2.2 原理分析

采用单片机AT89C51作为本设计的核心元件。利用7段共阴LED作为显示器件。在此设计中共接入了6个7段共阴LED显示器,其中2个4位LED显示器用于记录AB队的分数,每队1个4位LED显示器显示范围可达到0~999分,足够满足赛程需要;2个两位LED显示器用来记录赛程时间,其中1个用于显示分钟,1个用于显示秒钟;使用1个2位LED显示器用于显示“24s”倒计时,赛程计时均采用倒计时方式。即比赛前将时间设置好,即12分钟,比赛开始时启动计时,直至计时到零为止。本设计中设立了10个按键,其中6个用于设置两队的比分,1个用于比赛开始/比赛继续,1个用于暂停,1个用于手动增加比赛节数,一个用于恢复“24s”倒计时等功能。

2.3 总体设计

系统主要包括单片机、LED显示电路、按键控制电路、时钟电路及复位电路等组成

2.4 软硬件设计

2.4.1 硬件设计

设计思路:
(1)按键模块设计 :
按键接至P1口和P3口,按键按下时输入是低电平。键的设置如下:“A1”,“A2”,“A3”键是A队加1分、加2分,加3分键盘,B队同理。“比赛开始/比赛继续”是启动键,“比赛暂停”是暂停键。此外,还有两个按键, “24s”复位键,“增加比赛节数”键。
(2) LED显示模块设计 :
显示分为计时和计分显示两个部分,均采用共阴极LED数码管动态扫描显示,由2个四位一体和4个两位一体的共阴极数码管构成。数码管的7个段控端输出经过74HC573驱动器进行电流放大驱动接至P0口。采用动态扫描方式,在足够短的周期内,使各位数码管逐个轮流受控显示。

设计截图:
设计截图

2.4.2 软件设计

软件设计采用C语言模块设计方法,程序由主程序、T0中断程序、显示程序、外部中断0程序、按键程序、初始函数、计时函数、延时函数组成。

C语言程序代码:

#include<reg52.h>
#include<absacc.h>

#define uchar unsigned char 
#define uint unsigned int

sbit A8=P2^0;  
sbit A9=P2^1;
sbit A10=P2^2;
sbit A11=P2^3;
sfr P00=0x80;


uchar code wei_1[]={0X01,0X02,0X04,0X08,0X10,0X20,0X40,0X80};
uchar code table[]={0X3F,0X06,0X5B,0X4F,0X66,0X6D,0X7D,0X07,0X7F,0X6F,0x77,0x7c,0x39,0x5e,0x79,0x71,0X00,0X40};

uchar minute=12;  //分
uchar second=0;   //秒
uchar key_status=0;     //按键状态值
uchar scoreA=0;     //A队得分
uchar scoreB=0;     //B队得分
uchar count1=20;    //记录定时器时间
uchar match=1;      //比赛节数
uchar rule_time=24; //记录24秒规则
uchar rule_flag=0;          //记录24秒规则是否启用


/*
初始化IO口
*/
void IO_init(){
    A8=0;
    A9=0;
}
/*
初始化时间
*/

void time_init(){
    TMOD = 0x01;
    TH0 = (65536-50000)/256;
    TL0 = (65536-50000)%256; 
    EA = 1;
    ET0 = 1;
}
/*
延时函数
*/
void delay_ms(uint z){
  uint i;
    uchar j;
    for(i=0;i<z;i++){

        for(j=0;j<100;j++);
    }
}

/*
计时函数
*/
void T0_Time() interrupt 1 using 1{
    if (count1-- == 0) 
    {
        second--;
        rule_time--;
        count1 = 20; 
    }
    TH0 = (65536-50000)/256;
    TL0 = (65536-50000)%256;
}

/*
时间显示函数
*/
void time_display(uchar w2,uchar d2){
    uchar shi1,ge1;
    shi1=d2/10;
    ge1=d2%10;

    P00=wei_1[w2];
    A10=1;
    A10=0;
    P00=table[shi1];;
    A8=1;
    A8=0;
    delay_ms(5);
    P00=0;
    A10=1;
    A10=0;
    A8=1;
    A8=0;

    P00=wei_1[w2+1];
    A10=1;
    A10=0;
    P00=table[ge1];
    A8=1;
    A8=0;
    delay_ms(5);
    P00=0;
    A10=1;
    A10=0;
    A8=1;
    A8=0;   
}

/*
分数显示函数
*/
void score_dispaly(uchar w2,uchar d2){
    uchar qian2,shi2,ge2;
    qian2=d2/100;
    shi2=d2%100/10;
    ge2=d2%100%10;

    P00=wei_1[w2];
    A9=1;
    A9=0;
    P00=table[qian2];;
    A8=1;
    A8=0;
    delay_ms(10);
    P00=0;
    A9=1;
    A9=0;
    A8=1;
    A8=0;

    P00=wei_1[w2+1];
    A9=1;
    A9=0;
    P00=table[shi2];
    A8=1;
    A8=0;
    delay_ms(10);
    P00=0;
    A9=1;
    A9=0;
    A8=1;
    A8=0;   

    P00=wei_1[w2+2];
    A9=1;
    A9=0;
    P00=table[ge2];
    A8=1;
    A8=0;
    delay_ms(10);
    P00=0;
    A9=1;
    A9=0;
    A8=1;
    A8=0;
}

/*
键盘输入函数
*/
void key(){
    static uchar key_new = 0, key_l,temp;
    key_status = 0;                  
    P1 = 0x0f;
    if((P1 & 0x0f) != 0x0f)     
    {
        delay_ms(1);            
        if(((P1 & 0x0f) != 0x0f) && (key_new == 1))
        {                       
            key_new = 0;
            /*
            矩阵键盘扫描
            */
            key_l = (P1 | 0xf0);   
            P1 = key_l;
            switch(P1)
            {
                /*
                开始计时/比赛继续   
                */
                case 0x7e:  
                    TR0=1;  
                    rule_flag=1;
                    time_display(6,rule_time);
                    break;      
                /*
                暂停比赛
                */
                case 0x7d:
                    TR0=0;
                    break;  

                /*
                加分,同时24秒清零和总计时器停止计时
                */
                case 0xee: 
                    TR0=0; 
                    scoreA +=3;
                    rule_time=24;   
                    rule_flag=0;
                    break;
                case 0xed: 
                    TR0=0;                  
                    scoreA +=2;
                    rule_time=24;   
                    rule_flag=0;
                    break;
                case 0xeb:  
                    TR0=0; 
                    scoreA +=1;
                    rule_time=24;   
                    rule_flag=0;
                    break;
                case 0xde:  
                    TR0=0; 
                    scoreB +=3;
                    rule_time=24;   
                    rule_flag=0;
                   break;
                case 0xdd:  
                    TR0=0; 
                    scoreB +=2;
                    rule_time=24;   
                    rule_flag=0;
                    break;
                case 0xdb:  
                    TR0=0; 
                    scoreB +=1;
                    rule_time=24;   
                    rule_flag=0;
                    break;
                /*
                增加比赛节数,同时24秒复位和总计时器复位
                */
                case 0xbd:
                    match++;
                    minute=12;
                    second=0;
                    TR0=0; 
                    rule_time=24;   
                    rule_flag=0;
                    break;
                /*
                手动24秒复位
                */
                case 0xbe:
                    if(TR0==1){
                            rule_time=0;   
                            rule_flag=0;
                    }else{
                            rule_flag=0;  
                    }
                    break;
            }   
        }           
    }
    else 
    {
        key_new = 1;
    }
}

void main()
{
    IO_init();
    time_init();
    while(1){
        key();

        if(second==0){
            /*
                判断某节比赛是否结束
            */
            if(minute==0){ 
                /*
                判断是否为最后一节比赛
                */
                if(match<4){
                    /*
                    计时系统恢复为默认值,比赛节数加一
                    */
                    second=60;
                    minute=11;
                    match++;
                    rule_time=24;   
                    rule_flag=0;
                }   
                TR0=0;
            }
            /*
            比赛分钟数减一
            */
            else {  
                second=60;
                minute--;
            }               
        }
        /*
        24秒结束,计时器暂停和24秒复位
        */
        if(rule_time<=0){
            TR0=0;
            rule_time=24;   
        }

        /*
        显示
        */
        time_display(0,minute);
        time_display(2,second);
        time_display(4,match);
        time_display(6,rule_time);
        score_dispaly(1,scoreA);
        score_dispaly(5,scoreB);
    }
}

2.5 方案论证及可行性分析

本设计以单片机AT89C51为核心的控制电路实现了计时计分、计时的功能,设计中,使用大尺寸数码管的制造构思巧妙和软件设计,降低了整个系统的成本,实现了多用途的目的。通过仿真和实物实验验证了该系统的正确性和实用性。系统电路制造简单,显示清晰,成本低廉。

2.6 测试结果与分析

设计采用C语言编程,在Keil C51软件编译环境中,经过编译和连接,生成十六进制的目标代码文件.hex。在硬件上,用Proteus软件绘制出原理图,经过纯电路调试无误后,将生成的目标代码文件加载到Proteus界面的AT89C51中。接着再进行软硬件联合调试,反复调试,直至所有显示及按键功能实现,直至仿真成功。\

运行截图:

这里写图片描述

2.7 参考文献:

[1] 胡辉. 单片机原理与应用[M].北京:中国水利水电出版社,2007.
[2] 戴佳. 51单片机C语言应用程序设计实例精讲[M].北京:电子工业出版社,2006.

3. 设计文件以及源码下载

CSDN下载地址
或者关注博主公众号,回复:篮球计时器即可下载,还有更多资源分享哦~

扫码关注作者个人技术公众号,有关技术问题后台回复即可,不定期将有学习资源分享
这里写图片描述

2020-02-10 16:25:38 weixin_44715117 阅读数 14

51单片机利用定时器和1602液晶显示实现一个计时器

说明

1.平台:windows10 64位教育版

2.工具:普中科技HC6800-ES V2.0 、keil uVision 2

3.注意:芯片STC89C52,8位1602液晶显示

4.实现效果:

通电之后,1602开始显示计时,按下第一个按键进入设置,按下第二个按键设置时分秒的各个位置,按下第三个按键在对应的时分秒的位置数字加1,按下第四个按键在对应的时分秒的位置数字减1

原理

笔者参考的是郭天祥的《新概念51单片机C语言教程》和网上的普中科技的单片机的使用教程,具体使用教程,在哔哩哔哩上面搜“普中科技51单片机”也有。要找到能用的代码,网上是很多的,不过笔者是自己在明白原理之后,自己编写程序实现的,自己独立思考的过程很重要。
以下是普中科技的使用教程:详细的代码里面也有

流程

我是按照一个一个功能的实现来把代码加进去的,利用单片机上面的独立模块就可以实现独立测试。

1.按键检测

这里我用到的是4个独立按键,实现的功能是第一个按键按下进入时间设置,第二个按键按下设置时分秒,第三个按键按下对应的位置加1,第四个按键按下对应位置减1,注意,这里的其他独立按键起作用的前提是第一个按键要先按下进入设置,否则按键无效。
以下是代码:
I/O口声明

sbit key1 = P3^0;	//第二个按键
sbit key2 = P3^1;	//第一个按键
sbit key3 = P3^2;	//第三个按键
sbit key4 = P3^3;	//第四个按键

按键检测函数

void keyscan()
{

	if (key1 == 0)
	{
		delay10ms();
		if (key1 == 0)
		{
			//要实现的功能
			while(!key1);	//等待按键松开		
		}
	}
	if (key2 == 0)
	{
		delay10ms();
		if (key2 == 0)
		{
			//要实现的功能
			while(!key2);			
		}
	}
	if (key3 == 0)
	{
		delay10ms();
		if (key3 == 0)
		{
			//要实现的功能
			while(!key3);
		}
	}
	if (key4 == 0)
	{
		delay10ms();
		if (key4 == 0)
		{
			//要实现的功能
			while(!key4);
		}
	}
}

//延时10ms以消除按键抖动
void delay10ms()
{
	unsigned char a,b,c;
	for(c = 1;c > 0; c--)
		for(b = 38;b > 0; b--)
			for(a = 130;a>0;a--);
}

2.定时器中断

采用的是定时器0,工作方式2
以下是代码:

	TMOD = 0x02;	//定时器0,工作方式2
	TH0 = 0x9c;		//装入初值,高4位
	TL0 = 0x9c;		//装入初值,低4位
	EA = 1;		//打开总中断
	ET0 = 1;	//打开定时器中断
	TR0 = 1;	//启动定时器
void Timer() interrupt 1
{
	TH0 = 0x9c;	//重装初值
	TL0 = 0x9c;
	num++;	//num = 10000时为1s
}

3.1602液晶显示

这里使用是8位的1602液晶,如果有人用的是4位的,详细代码在之前的普中科技的官方教程里面有。
以下是代码:
I/O口声明

sbit LED_RS = P2^6;	//0为写命令,1为写数据
sbit LED_WR = P2^5;	//0为从1602液晶写数据,1为读数据
sbit LED_EN = P2^7;	//使能接口,置为0

初始化函数

void init()
{
	write_data(0x38,0);	//设置16*2显示,5*7点阵
	write_data(0x0c,0);	//设置开显示,不显示光标
	write_data(0x06,0);	//写一个字符后地址指针加1
	write_data(0x01,0);	//显示清零,数据指针清零
	write_data(0x80,0); //开始写的地址
}

写命令和数据函数,通过传入0和1来控制写命令或者数据

void write_data(uchar dat,uchar wr)
{
	if(wr == 0)
		LED_RS = 0;	//写命令
	else
		LED_RS = 1;	//写命令		
	LED_WR = 0;	
	LED_EN = 0;
	P0 = dat;
	delay(5);	//延时等待数据稳定
	LED_EN = 1;	
	delay(5);
	LED_EN = 0;	
}
void delay(uchar t)
{
	uchar i;
	for(;t>0;t--)
		for(i=199;i>0;i--);
}

在进行测试之前,要连接一下电路,因为对于普中的这款单片机,它是通过U2锁存器来控制数据方向的,如下图所示,要把三个排针中的最右边两个短接在一起。在这里插入图片描述
独立模块测试完成之后就是进行完整的功能组合,即代码组合。

4.完整代码

#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int

sbit key1 = P3^0;	//第二个按键
sbit key2 = P3^1;	//第一个按键
sbit key3 = P3^2;	//第三个按键
sbit key4 = P3^3;	//第四个按键
sbit LED_RS = P2^6;	//用来确定是选择写命令还是数据
sbit LED_WR = P2^5;	//读写选择,1表示读,0表示写
sbit LED_EN = P2^7;	//使能信号,置为0
void keyscan();		//检测按键是否按下
void init();
void delay(uchar );
void write_data(uchar ,uchar );
void display(uchar ,uchar ,uchar );


//只有当按下第一个按键时(key_flag),其他按键才起作用
//position 用来记录光标位置
uchar sec,min,hour,key_flag,position;
uchar timer_op;//定时器启动和关闭标志
uchar timedis[8];//保存秒、分、时的各个位的数字
uint num;	//定时器计数使用

void main()
{
	init();
	while(1)
	{
		keyscan();			
		if(num == 10000)	//是否到了1s
		{
			sec++;
			if(sec > 59)
			{
				sec = 0;
				min++;
				if(min > 59)
				{
					min = 0;
					hour++;
					if(hour > 59)
						hour = 0;
				}					
			}
			num = 0;
			display(sec,min,hour);
		}	

	}
	
}

void init()
{
	//1602液晶显示初始化
	write_data(0x38,0); //设置16*2显示,8位数据口
	write_data(0x0f,0); //开显示,光标闪烁
	write_data(0x06,0); //写入一个字符,地址指针加1
	write_data(0x01,0); //显示清零,指针清零
	write_data(0x80,0); //写入的起始地址

	//定时器初始化
	TMOD = 0x02;	//设置定时器工作方式
	TH0 = 0x9c;		//装入初值,高四位
	TL0 = 0x9c;		//装入初值,低四位
	EA = 1;		//打开总中断
	ET0 = 1;	//打开定时器中断
	TR0 = 1;	//启动定时器
}

void keyscan()
{
	uchar i;
	if (key1 == 0)
	{
		delay(25);	//延时10ms
		if (key1 == 0)
		{
			position = 0;
			key_flag = 1;
			timer_op++;
			if(timer_op == 1)
				TR0 = 0;	//关闭定时器				
			else
			{
				TR0 = 1;	//开启定时器
				timer_op = 0;
				key_flag = 0;		//再按一次离开设置	
			}
			write_data(0x80,0);
			while(!key1);	//等待按键松开		
		}
	}
	if (key2 == 0)
	{
		delay(25);
		if (key2 == 0)
		{
			if(key_flag == 1)
			{
				add_num = 0;
				position++;
				if(position == 8)
					position = 0;
				write_data(position+0x80,0);	//更新光标位置				
			}
			while(!key2);			
		}
	}
	if (key3 == 0)
	{	//按下一次加1
		delay(25);
		if (key3 == 0)
		{
			if(key_flag == 1)
			{
				timedis[position]++;
				if((position == 1)||(position == 4)||(position == 7))
				{
					if(timedis[position] == 10)
						timedis[position] = 0;
				}
				else if((position == 0)||(position == 3)||(position == 6))
				{
					if(timedis[position] == 6)
						timedis[position] = 0;
				}
			}			
			hour = timedis[0]*10 + timedis[1];
			min = timedis[3]*10 + timedis[4];
			sec = timedis[6]*10 + timedis[7];
			//按下一次按键更新一次液晶显示
			write_data(0x80,0);
			for(i=0;i<8;i++)
				write_data(0x30+timedis[i],1);	
			while(!key3);
		}
	}
	if (key4 == 0)
	{	//按下一次减1
		delay(25);
		if (key4 == 0)
		{
			if(key_flag == 1)
			{
				timedis[position]--;
				if((position == 1)||(position == 4)||(position == 7))
				{
	
					if(timedis[position] == -1)
						timedis[position] = 9;
				}
				else if((position == 0)||(position == 3)||(position == 6))
				{
					if(timedis[position] == -1)
						timedis[position] = 5;
				}
			}
			hour = timedis[0]*10 + timedis[1];
			min = timedis[3]*10 + timedis[4];
			sec = timedis[6]*10 + timedis[7];
			write_data(0x80,0);
			for(i=0;i<8;i++)
				write_data(0x30+timedis[i],1);
			while(!key4);
		}
	}
}

void delay(uchar t)
{
	uchar i;
	for(;t>0;t--)
		for(i=199;i>0;i--);
}
void write_data(uchar dat,uchar wr)
{
	if(wr == 0)
		LED_RS = 0;	//写命令
	else
		LED_RS = 1;	//写数据
	LED_WR = 0;	
	LED_EN = 0;
	P0 = dat;
	delay(5);
	LED_EN = 1;	
	delay(5);
	LED_EN = 0;	
}

void Timer() interrupt 1
{
	TH0 = 0x9c;	//重装初值
	TL0 = 0x9c;
	num++;	
}

void display(uchar sec_,uchar min_,uchar hour_)
{
	uchar i;
	timedis[2] = 10;
	timedis[5] = 10;
	timedis[0] = hour_/10;
	timedis[1] = hour_%10;
	timedis[3] = min_/10;
	timedis[4] = min_%10;
	timedis[6] = sec_/10;
	timedis[7] = sec_%10;
	write_data(0x80,0);
	for(i=0;i<8;i++)
		write_data(0x30+timedis[i],1);
}

本人水平有限,如有错误或不当之处,望大家批评指正!

没有更多推荐了,返回首页