精华内容
下载资源
问答
  • 利用51单片机的定时器设计一个时钟 一、功能要求 1602液晶显示时间,且每秒更新,自动计时。 用3个按键实现调节时、分、秒更能,可以定义为功能键、增加键、减小键。 当按键被按下时,蜂鸣器响一声提示。 利用AT24C...

    利用51单片机的定时器设计一个时钟
    一、功能要求

    1. 1602液晶显示时间,且每秒更新,自动计时。
    2. 用3个按键实现调节时、分、秒更能,可以定义为功能键、增加键、减小键。
    3. 当按键被按下时,蜂鸣器响一声提示。
    4. 利用AT24C02实现掉电记忆功能。
      二、 原理图设计
      在这里插入图片描述
      在这里插入图片描述
      三、源程序
    5. 在Keil中的同一个文件夹下新建一个AT24C02.h文件和Clock.c文件
    //*********************** AT24C02.h源程序***************************//
     bit write=0;		//写AT24C02的标志
    sbit SDA=P2^0;		//定义数据串行口,用于单片机通信
    sbit SCL=P2^1;	//定义时钟串行口,用于单片机通信
    
    void delayus()		//定义us级延时函数
    {;;}
    
    void start()	//起始信号
    {
    	SDA=1;
    	delayus();
    	SCL=1;
    	delayus();
    	SDA=0;
    	delayus();
    }
    
    void stop()//终止信号
    {
    	SDA=0;
    	delayus();
    	SCL=1;
    	delayus();
    	SDA=1;
    	delayus();
    }
    
    void ack()//应答信号
    {
    	uchar i;
    	SCL=1;
    	delayus();
    	while((SDA=1)&&i<250)i++;																	
    	SCL=0;
    	delayus();
    }
    
    
    void init_AT24C02()	//初始化
    {
    	SDA=1;
    	delayus();
    	SCL=1;
    	delayus();	
    }
    
    void write_byte(uchar dat)	//定义写1个字节程序
    {
    	uchar i,temp;
    	temp=dat;				//将数据赋值给temp。例如:dat为0x01,则temp=0x01=0000 0001。方便后续移位操作用。
    	for(i=0;i<8;i++)		//1个字节包括8位二进制数
    	{
    		temp=temp<<1;		//将temp左移1位,最低位补0
    		SCL=0;					//将SCL置0允许SDA数据变化
    		delayus();
    		SDA=CY;				//将最高位移出的CY位赋给SDA
    		delayus();
    		SCL=1;					//置1将数据写入
    		delayus();
    	}
    	SCL=0;					//置0允许数据变化,用于SDA置1释放总线。
    	delayus();
    	SDA=1;					//释放总线
    	delayus();			
    }
    
    uchar read_byte()	//读1个带返回值的字节
    {
    	uchar i,k;
    	SCL=0;				//置0允许数据变化
    	delayus();
    	SDA=1;				//假设SDA为1,也可以SDA=0。当SDA=0时,下面for语句会有所差别。
    	delayus();
    	for(i=0;i<8;i++)
    	{
    		SCL=1;					//上升沿时,IIC设备将数据放在sda线上,并在高电平期间数据已经稳定,可以接收啦
    		delayus();
    		k=(k<<1)|SDA;		//将k左移一位跟SDA或运算,再赋给k	
    		SCL=0;					//拉低SCL,使发送端可以把数据放在SDA上  
    		delayus();		
    	}
    	return k;
    }
    
    void write_add(uchar address, uchar dat)			//指定地址写一个字节
    {
    	start();
    	write_byte(0xa0);
    	ack();
    	write_byte(address);
    	ack();
    	write_byte(dat);
    	ack();
    	stop();
    }
    
    char read_add(uchar address)				//随机读取一个字节
    {
    	uchar dat;
    	start();
    	write_byte(0xa0);
    	ack();
    	write_byte(address);
    	ack();
    	start();
    	write_byte(0xa1);
    	ack();
    	dat=read_byte();
    	stop();
    	return dat;
    }
    //****************************Main主程序**************************************//
    #include <reg52.h>
    #define uchar unsigned char
    #define uint unsigned int
    #include"AT24C02.h"		//包含AT24C02头文件
    sbit s1=P3^0;
    sbit s2=P3^1;
    sbit s3=P3^2;
    sbit rd=P3^7;
    sbit lcden=P3^4;
    sbit rs=P3^5;
    sbit dula=P2^6;
    sbit wela=P2^7;
    sbit beep=P2^3;
    char shi,fen,miao;
    uchar s1num,count;
    uchar code table0[]="  2020-6-4 MON";			//字符2020前面3个空格
    uchar code table1[]="00:00:00";
    void delay_us()					//us级延时程序
    {;;}
    void delay_ms(uchar z)	//ms级延时程序
    {
    	uint i,j;
    	for(i=z;i>0;i--)
    		for(j=110;j>0;j--);
    }
    
    void buzzer()				//定义蜂鸣器程序
    {
    	beep=0;
    	delay_ms(100);
    	beep=1;
    
    }
    
    void write_com(uchar com)	//液晶写指令
    {
    	rs=0;
    	lcden=0;
    	P0=com;
    	delay_ms(5);
    	lcden=1;
    	delay_ms(5);
    	lcden=0;
    }
    
    void write_dat(uchar dat)	//液晶写数据
    {
    	rs=1;
    	lcden=0;
    	P0=dat;
    	delay_ms(5);
    	lcden=1;
    	delay_ms(5);
    	lcden=0;
    }
    
    void write_sfm(uchar add,uchar dat)		//写时分秒程序
    {
    	uchar shi,ge;
    	shi=dat/10;				//将时分秒分别分解成一个两位数,以便后续的位操作
    	ge=dat%10;
    	write_com(0x80+0x40+add);			//在液晶第2行写指令
    	write_dat(0x30+shi);						//0x30地址的数据为0
    	write_dat(0x30+ge); 
    }
    void init_1602()							//初始化液晶
    {
    	uchar num;
    	dula=0;
    	wela=0;
    	num=0;
    	s1num=0;
    	count=0;
    	rd=0;
    	lcden=0;
    	shi=0;
    	fen=0;
    	miao=0;
    	init_AT24C02_02();			
    	write_com(0x38);
    	write_com(0x0c);
    	write_com(0x06);
    	write_com(0x08);
    	write_com(0x01);
    	write_com(0x80);
    	for(num=0;num<14;num++)		//显示日期,加空格总共14个字节
    	{
    		write_dat(table0[num]);
    		delay_ms(5);
    	}
    	write_com(0x80+0x40+4);
    	for(num=0;num<8;num++)		//显示时分秒
    	{
    		write_dat(table1[num]);	
    		delay_ms(5);	
    	}
    	miao=read_add(1);	//首次上电从AT24C02中读取出存储器的数据,地址可以随机定义,只要整个程序用的地址相同即可。
    	fen=read_add(2);		
    	shi=read_add(3);
    	write_sfm(10,miao);	//在第十位写秒数据
    	write_sfm(7,fen);
    	write_sfm(4,shi);
    	TMOD=0x01;		//设置T0定时器为工作方式1
    	TH0=(65536-50000)/256;	//装初值
    	TL0=(65536-50000)%256;
    	EA=1;			//开总中断
    	ET0=1;		//开定时器0中断
    	TR0=1;		//启动定时器0
    }
    
    void keyscan()	//按键扫描程序
    {
    	if(s1==0)		//先定义功能键被按下,不然加减键为无效。
    	{
    		delay_ms(5);
    		if(s1==0)		//再次确认被按下,即去抖动
    		{
    			s1num++;	//记录功能键被按下次数
    			while(!s1);	//按键释放
    			buzzer();		//蜂鸣器短鸣提示
    			if(s1num==1)		//功能键按下1次
    			{
    				TR0=0;		//定时器停止计时
    				write_com(0x80+0x40+11);		//光标在11位处,调节11的大小可以进行光标位置设置
    				write_com(0x0f);						//光标闪烁
    			}	
    			if(s1num==2)
    			{
    				write_com(0x80+0x40+8);
    				write_com(0x0f);
    			}
    			if(s1num==3)
    			{
    				write_com(0x80+0x40+5);
    				write_com(0x0f);
    			}
    			if(s1num==4)		//功能键按下4次
    			{
    				s1num=0;		//按键次数清0
    				write_com(0x0c);		//取消光标闪烁
    				TR0=1;			//定时器继续计数
    			}
    		}
    	}
    	if(s1num!=0)		//只有功能键被按下,加减键才有效
    	{
    		if(s2==0)		//增加键被按下
    		{
    			delay_ms(5);
    			if(s2==0)		//再次确认增加键按下,去抖动
    			{
    				while(!s2);		//按键释放,只有按键释放了才可以认为一个完整的按键完成
    				buzzer();
    				if(s1num==1)		//功能键按下一次,此时选择调整秒
    				{
    					miao++;
    					if(miao==60)		//当秒数大于60就清0
    						miao=0;
    					write_sfm(10,miao);	//在第10位写秒,此时写的时一个两位数,调用的write_sfm()函数
    					write_com(0x80+0x40+11);	//光标定位在11位,由于前面已经定义光标闪烁,所以这里不用写write_com(0x0f)
    					write_add(1,miao);		//将变化的值写入AT24C02记忆起来,掉电恢复时可以显示此值
    				}
    				if(s1num==2)
    				{
    					fen++;
    					if(fen==60)
    						fen=0;
    					write_sfm(7,fen);
    					write_com(0x80+0x40+8);
    					write_add(2,fen);
    				}
    				if(s1num==3)
    				{
    					shi++;
    					if(shi==24)
    						shi=0;
    					write_sfm(4,shi);
    					write_com(0x80+0x40+5);
    					write_add(3,shi);
    				}
    			}
    		}
    		if(s3==0);		//减少键被按下
    		{
    			delay_ms(5);
    			if(s3==0)	//再次确认,去抖
    			{
    				while(!s3);
    				buzzer();
    				if(s1num==1)
    				{
    					miao--;	
    					if(miao==-1)//当秒数被减到-1时,秒数清0。
    						miao=59;
    					write_sfm(10,miao);
    					write_com(0x80+0x40+11);
    					write_add(1,miao);
    				}
    				if(s1num==2)
    				{
    					fen--;
    					if(fen==-1)
    						fen=59;
    					write_sfm(7,fen);
    					write_com(0x80+0x40+8);
    					write_add(2,fen);
    				}
    				if(s1num==3)
    				{
    					shi--;
    					if(shi==-1)
    						shi=23;
    					write_sfm(4,shi);
    					write_com(0x80+0x40+5);
    					write_add(3,shi);
    				}
    			}
    		}			
    	}		
    }
    
    void main()		//主程序
    {
    	init_1602();		//初始化1602液晶
    	while(1)	//进入while语句不停的进行按键扫描,来确认是否有按键按下
    	{
    		keyscan();
    	}
    }
    
    void timer0() interrupt 1	//定时器0工作方式1
    {
    	TH0=(65536-50000)/256;	//装初值,因为工作方式1无法自动重装初值,所以这里再次装初值。
    	TL0=(65536-50000)%256;
    	count++;								//将1s拆分成20个50ms,这里对50ms进行计数来累加到1s
    	if(count==20)						//当计数20次时,即为1s
    	{
    		count=0;							//清0重新计数
    		miao++;
    		if(miao==60)					//这里是没有按键被按下时,时钟自动的计数
    		{
    			miao=0;
    			fen++;
    			if(fen==60)
    			{
    				fen=0;
    				shi++;
    				if(shi==24);
    				{
    					shi=0;
    				}
    				write_sfm(4,shi);			//在第4位写小时的数据
    				write_add(3,shi);			//写入AT24C02进行掉电记忆,这里的3为地址,整个程序都要相同。
    			}
    			write_sfm(7,fen);
    			write_add(2,fen);
    		}
    		write_sfm(10,miao);
    		write_add(1,miao);
    	}
    }
    

    1602字符库
    在这里插入图片描述

    展开全文
  • 1.利用实验板和配件,设计一个时钟,时间显示在LCD1602上,并按秒更新,能够在实验板...利用板上AT24C02设计实现断电保护显示数据功能。 =============Clock.h============= #ifndef __CLOCK_H__ #define __CLOCK...

    1.利用实验板和配件,设计一个时钟,时间显示在LCD1602上,并按秒更新,能够在实验板上设计3个按键调整时,分,秒。其功能为:功能选择键,数值增大和数值减小键。利用板上AT24C02设计实现断电保护显示数据的功能。

    =============Clock.h=============

    #ifndef __CLOCK_H__

    #define __CLOCK_H__

     

     

    //========全局变量区============================================

    unsignedchar T_High_50ms=(65536-45872)/256;

    unsignedint T_Low_50ms=(65536-45872)%256;

    unsignedchar Count,Count_T1,Count_1s;//Count用来记录每50ms的计数,Count_T1用来记

     

    //========全局变量区结束============================================

    #endif

     

    =============I2C.H=============

    #ifndef __I2C_H__

    #define __I2C_H__

    #include<reg51.h>

    sbit sda=P2^0;                        

    sbit scl=P2^1;

    //========函数区============================================

    void nop();

    void initI2C();

    externvoid I2C_start();  //开始信号

    externvoid I2C_stop();   //停止

    externvoid I2C_respons();  //应答

    externvoid I2C_write_byte(unsignedchar date);

    externunsignedchar I2C_read_byte();

    externvoid I2C_write_address(unsignedchar address,unsignedchar date);

    externunsignedchar I2C_read_address(unsignedchar address);

    //========函数区结束============================================

    #endif

     

    =============OrphanKey.h=============

    #ifndef __ORPHANKEY_H__

    #define __ORPHANKEY_H__

     

    sbit FUNCTION_KEY=P3^2;//功能键 --按下暂停,再按开始

    sbit Key_ADD=P3^3;//1

    sbit KEY_MINUS=P3^4;//1

    const bit PRESSED=0;//按下

    bit SUSPEND=0;//0的时候运行,1的时候暂停

     

    #endif

     

    =============I2C.C=============

    #include "head/I2C.h"

    //=========全局变量区============================================

     

    #define uchar unsigned char

    #define uint unsigned int

     

      bit I2C_write=0;           //24C02的标志;

     

     unsignedchar sec,tcnt;

     

    //=========全局变量区结束============================================

    void nop()

    {;;}

     

    /***********************************************************

        I2C的初始化

    ***********************************************************/

    void initI2C(){

        sda=1;

        nop();

        scl=1;

        nop();

     

    }

    void I2C_start()  //开始信号

    {  

        sda=1;

        nop();

        scl=1;

        nop();

        sda=0;

        nop();

    }

    void I2C_stop()   //停止

    {

        sda=0;

        nop();

        scl=1;

        nop();

        sda=1;

        nop();

    }

    void I2C_respons()  //应答

    {

        uchar i;

        scl=1;

        nop();

        while((sda==1)&&(i<250))i++;

        scl=0;

        nop();

    }

     

    void I2C_write_byte(uchar date)

    {

        uchar i,temp;

        temp=date;

        for(i=0;i<8;i++)

        {

           temp=temp<<1;

           scl=0;

            nop();

           sda=CY;

           nop();

           scl=1;

           nop();

        }

        scl=0;

        nop();

        sda=1;

        nop();

    }

    uchar I2C_read_byte()

    {

        uchar i,k;

        scl=0;

        nop();

        sda=1;

        nop();

        for(i=0;i<8;i++)

        {

           scl=1;

           nop();

           k=(k<<1)|sda;

           scl=0;

           nop();

        }

        return k;

    }

    void I2C_write_address(uchar address,uchar date)

    {

        I2C_start();

        I2C_write_byte(0xa0);

        I2C_respons();

        I2C_write_byte(address);

        I2C_respons();

        I2C_write_byte(date);

        I2C_respons();

        I2C_stop();

    }

    uchar I2C_read_address(uchar address)

    {

        uchar date;

        I2C_start();

        I2C_write_byte(0xa0);

        I2C_respons();

        I2C_write_byte(address);

        I2C_respons();

        I2C_start();

        I2C_write_byte(0xa1);

        I2C_respons();

        date=I2C_read_byte();

        I2C_stop();

        return date;

    }

     

    =============lab8_1.c=================

    #include<reg51.h>

    #include "head/Clock.h"

    #include "head/OrphanKey.h"

    #include "head/I2C.H"

    /*

    1.利用实验板和配件,设计一个时钟,时间显示在LCD1602上,并按秒更新,能够在实验板上设计3个按键调整时,分,秒。其功能为:功能选择键,数值增大和数值减小键。利用板上AT24C02设计实现断电保护显示数据的功能。

    */

     

    /*

    步骤:

    1、设计一个中断,用来计时

    2、设计一个字符生成函数,用来生成所需的时间

    3、独立按键监测功能,用来监测按下了什么键

    4、一个外部中断

    5、一个断点保护的功能,其实就是通过I2C写入,读取数值

    */

     

    //=========全局变量区============================================

    #define uchar unsigned char

    #define uint unsigned int

    uchar code table[]="12:23:12";

    uchar code table1[]="I am a boy!";

    sbit lcden=P2^7;  //液晶使能端

    sbit lcdrs=P2^6;  //数据或命令控制(0代表命令,1代表数据)  

    sbit LCDWR=P2^5;  //读写控制(0代表写,1代表读)

    uchar num;

    uchar FIRST_LINE=0x80;

    uchar SECOND_LINE=0xc0;

    uchar Current_Time[9];

    uchar Hour,Minute,Second=0;//时、分、秒

    sbit beer = P1 ^4;      //蜂鸣器

    // sbit led1=P1^3;

     

    //=========全局变量区结束============================================

     

     

    //=========函数区============================================

     

    /***********************************************************

        延时函数

        参数说明:

        z代表要延时z ms

    ***********************************************************/

    void delay(uint z)

    {

        uint x,y;

        for(x=z;x>0;x--)

           for(y=110;y>0;y--);

    }

    /***********************************************************

        写命令或数据

        参数说明:

        isRs=1代表写数据,isRs=0代表写命令

    ***********************************************************/

    void write(uchar mdata, uchar isRs)

    {

        lcdrs=isRs;

        P0=mdata;

        delay(5);

        lcden=1;

        delay(5);

        lcden=0;

    }

    /***********************************************************

        写命令

        参数说明:

        data代表要写的命令

    ***********************************************************/

    void write_command(uchar com)

    {

        write(com,0);

    }

    /***********************************************************

        写数据

        参数说明:

        data代表要写的数据

    ***********************************************************/

    void write_data(uchar mdata)

    {

        write(mdata,1);

    }

     

    /***********************************************************

        初始化时钟

    ***********************************************************/

    void InitTime(){

        // 

        Current_Time[0]=Hour/10+'0';

        Current_Time[1]=Hour%10+'0';

        Current_Time[2]=':';

        Current_Time[3]=Minute/10+'0';

        Current_Time[4]=Minute%10+'0';

        Current_Time[5]=':';

        Current_Time[6]=Second/10+'0';

        Current_Time[7]=Second%10+'0';

        Current_Time[8]='\0';

    }

     

    /***********************************************************

        保存时钟的值

    ***********************************************************/

    void KeepClockValue(){

        I2C_write_address(2,Second); 

        delay(10); 

        I2C_write_address(3,Minute);     

        delay(10);

        I2C_write_address(4,Hour);   

    }

     

    /***********************************************************

        改变时间

    ***********************************************************/

    void changeTime()

    {

        // 

        if(Second>=60){

           Second=0;

           Minute++;

        }else{

           ++Second;

        }

     

        if(Minute==60){

           Hour++;

           Minute=0;

        }

        if(Hour==24){

           Second=0;

           Minute=0;

           Hour=0;

        }

        KeepClockValue();

    }

    /**

        初始化

    **/

    void InitClockInterrupt(){//初始化中断

     

        TMOD=0x11;        // TMOD赋值,以确定T0T1的工作方式。

        TH0=T_High_50ms;// 计算初值,并将其写入TH0TL0TH1TL1

        TL0=T_Low_50ms;

        EA =1;//开启中断总允许

        ET0 =1;  

        IT0 =1;   //开启定时器0,1中断允许

        TR0=1;     // 使TR0TR1置位,启动定时/计数器定时或计数

        Count=Count_T1=Count_1s=0;//计数

    }

    /***********************************************************

        初始化按钮中断

    ***********************************************************/

    void initButtonInterupt(){

        IT0 =0;

        //EA = 1;     //开启中断总允许

        EX0 =1;   //外部中断0允许位

    }

     

     

    void init()

    {

     

        InitTime();

        lcden=0;

        LCDWR=0;

        write_command(0x38);//设置16X2显示,5X7点阵,8位数据接口

        write_command(0x0c);//设置开显示,不显示光标

        write_command(0x06);//写一个字符后地址指针加1

        write_command(0x01);//显示清零,数据指针清零

    }

    void WriteStringToLCD(uchar *dat)

    {

        while(*dat!='\0')

        {  

           write_data(*dat);

           dat++;

           delay(5);

        }

    }

    /***********************************************************

        LCD整体左移动

    ***********************************************************/

    void LCD_MoveToLeft(){

        int num;

        for(num=0;num<16;num++)

        {

               write_command(0x18);

               delay(200);

        }

    }

     

     

    /***********************************************************

        LCD打印字符串

    ***********************************************************/

    void PrintToLCD(){

       

        //write_command(FIRST_LINE);

        //WriteStringToLCD(table);

        write_command(SECOND_LINE);

        WriteStringToLCD(Current_Time);

     

    }

     

    /***********************************************************

        是否加1

    ***********************************************************/

    void IsAdd(){

        if(PRESSED==Key_ADD){       //当加1按键按下

           delay(10);        //去抖动

           if(PRESSED==Key_ADD){    //当加1按键按下

               changeTime();

               InitTime();

               PrintToLCD();

               while(!Key_ADD);

           }

        }

    }

    /***********************************************************

        是否减1

    ***********************************************************/

    void IsMinus(){

        if(PRESSED==KEY_MINUS){     //当减1按键按下

           delay(10);        //去抖动

           if(PRESSED==KEY_MINUS){  //当减1按键按下

               if(Second>0){    //防止按下减1按钮的时候,被减成负数

                  Second--;

                  InitTime();

                  PrintToLCD();

                  while(!KEY_MINUS);

               }

           }

        }

    }

    /***********************************************************

        是否运行

    ***********************************************************/

    void IsRun(){

        if(PRESSED==FUNCTION_KEY){      //当减1按键按下

           delay(10);        //去抖动

              

           if(PRESSED==FUNCTION_KEY){  //当减1按键按下

               SUSPEND=~SUSPEND;

               // led1=SUSPEND;

           while(!KEY_MINUS);

            }

        }

    }

    /***********************************************************

        是否暂停

    ***********************************************************/

    void IsSuspend(){

        IsRun();

        IsMinus();        //监测是否需要减1

        IsAdd();          //监测是否需要加1     

    }

    /***********************************************************

        重新计数

    ***********************************************************/

    void ReCount(){

        Count=0;  //开始下一轮1s的计时

        TH0=T_High_50ms;// 计算初值,并将其写入TH0TL0TH1TL1

        TL0=T_Low_50ms;

    }

    /***********************************************************

        初始化时钟的值

    ***********************************************************/

    void initClockValue(){

        Second=I2C_read_address(2);    //读出保存的数据赋于Second

     

        Minute=I2C_read_address(3);

        Hour=I2C_read_address(4);

    }

    void main()

    {

        init();

        InitClockInterrupt();

        initButtonInterupt();

        initClockValue();

        if(Second>100)        //防止首次读取出错误数据

           Second=0;

     

        while(1){

        IsSuspend();

        if(Count>=20&&(!SUSPEND)){//定时器0 1s时间到

               ReCount();

               changeTime();

               InitTime();

               PrintToLCD();

           }

        }

     

    }

    //=========函数区结束============================================

     

    //=========中断函数区============================================

     

    /**

        初始化按钮外部中断

    **/

    void intButton() interrupt 0//外部中断0

    {

     

    }

     

    /**

        用定时0实现8个发光二极管以1s间隔闪烁

    **/

    void int0() interrupt 1//定时/计数器T0中断

    {

        Count++;

    }

     

     

    //=========中断函数区结束============================================

    本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/p/3174473.html如需转载请自行联系原作者


    kissazi2

    展开全文
  • 51单片机利用定时器和1602液晶显示实现一个计时器 说明 1.平台:windows10 64位教育版 2.工具:普中科技HC6800-ES V2.0 、keil uVision 2 3.注意:芯片STC89C52,8位1602液晶显示 原理 笔者参考是郭天祥《新概念...

    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);
    }
    
    

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

    展开全文
  • 51单片机的定时器/计数器

    千次阅读 2010-02-07 21:25:00
    以前写过单片机的计数器/定时器的相关程序。今天来系统总结下。借助网络和书本又把相关的知识复习了一遍。下面这个链接就计数器/定时器做了详细的介绍。...

       以前写过单片机的计数器/定时器的相关程序。今天来系统总结下。借助网络和书本又把相关的知识复习了一遍。下面这个链接就计数器/定时器做了详细的介绍。http://hi.baidu.com/at_this_time/blog/item/6fb0fba169bd5e804610640c.html

    对于系统学习计数器/定时器很有帮助。如果要用单片机对外部信号进行计数或者利用单片机对外部设备进行定时控制,就需要用到单片机的定时器/计数器。日常生活中也有很多地方需要用到计数,如流水线上生产药品,当药品瓶经过某处时有红外扫面从而对药物数量进行统计。而定时器用处也广泛,闹钟就是一个例子。设定时间,时间到了进行处理。51单片机有两个定时器/计数器T0和T1,当T0做计数器时(P3.4用来做输入信号)当T1用来做计数器时(P3.5引脚作为输入信号)。当检测到有脉冲信号输入时,则会技术,当计数达到65536时,则计数器会溢出且会发送给CPU一个信号,CPU会停止当前的任务去处理规定的任务。即产生定时器/计数器中断。联系52单片机的中断:52单片机有六个中断源。

    T0:定时器/计数器0中断,由T0计数器计满回0引起。中断级别为1,中断入口地址为:000BH

    T1定时器/计数器1中断,由T1计数器计满回0引起。中断级别为3,中断入口地址为:001BH

    当需要用到计数器时则单片机需要外接信号,而计数器则通过时钟振荡器12分频后产生技术信号。如时钟振荡器为12MHZ,则12分频后得到1MHZ信号,1MHZ每个脉冲的持续时间(1个周期为)1uS.

    利用计数器,编写发光二极管闪烁的程序,定时1M需要计算,这里随便写了一个数。能看到法官二极管闪烁。程序如下,这里选择模式2自动装载数据。

    #include<reg52.h>
    #define uint unsigned int
    sbit led=P1^0;
    uint count;

    void main()
    {
      count=0;
      TMOD=0x02;//选择计数器TO模式2自动再装入计数器
      //TH0=0;    //(256-256)/256
      TL0=0;   //(256-256)%256
      EA=1;    // 开总中断
      ET0=1;  //开计数器0中断
      TR0=1; //   开始计数
      while(1); //等待中断产生

    }

    void t0() interrupt 1
    {
     count++;
     if(count==168)
        {
         count=0;
         led=~led;

     }

    }

    单片机的几个周期:

    1:时钟周期 也是振荡周期,是单片机频率的倒数。如单片机的晶振是12MHZ,则其时钟周期为1/12us.它是单片机最基本最小的时间单位。

    2:状态周期:定义为时钟周期的两倍。

    3:机器周期:单片机的基本操作周期,在一个操作周期内,完成一项基本的操作,如取指令,读/写存取器等。由12个时钟周期(6个状态周期)组成。

    4:指令周期:CPU执行一条指令所需要的时间,一般一个指令周期犹1到4个机器周期组成。

    计算定时器初值:假如12MHZ的晶振,则其机器周期为12*  1/12us= 1us.定时器初始时TH0和TH1都为0,一旦开始计数则在原来的数值上加1.计满THO,TH1(16位)要2的16方-1个数再来一个计数脉冲则像CPU发送中断。因此溢出一次需65536us。

    下面用定时器T0选择模式1(自动再装入8位计数器)实现1秒定时,使得发光二极管以500HZ闪烁,亮灭各1s.晶振为11.0592MHZ,则其时钟周期为1/11.0592US,所以其机器周期为12*1/11.0592,计满256耗时为256*其机器周期。计时50MS需要定时器计满256,18次。定时1秒需要18*20=360次。程序如下:

    #include<reg52.h>
    #define uint unsigned int
    #define uchar unsigned char
    sbit led=P1^0;
    uint count;
    uchar num;

    void main()
    {
      count=0;
      TMOD=0x02;//选择计数器TO模式2
      TL0=0;//(256-256)%256  则计数256次耗时23.148148MS
      EA=1;//开总中断
      ET0=1;//开定时器中断
      TR0=1; //开始计数
      while(1);//等待中断

    }

    void t0() interrupt 1
    {
     count++;
     if(count==18)//50MS
        {
     count=0;
     num++;
     if(num==20)//定时满1s
        {
         num=0;
         led=~led;
      }

     }

    }

    展开全文
  • // 包含51单片机寄存器定义头文件 #include<stdio.h>sbit sound=P1^5; //将sound位定义为P1.5引脚 void main(void) { TMOD=0x10; //使用定时器T1模式1 TH1=(65536-921)/256; //定时器T1高8位赋初值 TL1...
  • 前言 本博文基于STC89C52RC和keil5 C51开发; 本博文用描述两种模拟输出PWM波方法; 如有不做之处还请多多指教;...利用定时器0完成(定时器1也一样); 利用P1.0~P1.3完成4路PWM输出(不同占空比); ...
  • 程序思路:该程序是利用51单片机的定时器中断,另外根据音符的频率计算每个音符定时时长从而输出不同音符对应的PWM波驱动蜂鸣器播放音乐。 使用工具:Keil4,protues8;程序具体代码和仿真原理图都已打包放到《基于...
  • 利用这个功能能够更为精确定时器进行设定,可控制精度在1个机器周期(12M晶振下就是1us),T0、T1都需要手动重载,其精度无法很好控制。 /*------------------------------ 硬件需求: 12T系列单片机(8051内核...
  • 记一次实践 题目要求: 电路图: 代码: 头文件 #ifndef traffic_h #define traffic_h #include <reg52.h> #define LedD P0 //段选 #define LedC P2 //位选 sbit red=P1^2; sbit yellow=P1^1;
  • 原理:利用PWM信号控制LED灯亮暗程度,实现LED灯由暗到亮又由亮到暗渐变过程。 方法:通过定时器设置LED所接引脚高低电平持续时间,模拟出PWM波。 二、关键问题 1.设定PWM周期: 由于人眼视觉暂留效果...
  • C51单片机定时器

    2020-10-02 14:50:32
    与以前软件延迟做对比:软件延迟利用若干次空循环实现,占用CPU 1、定时器的构成 定时器:硬件 ; 此件利用 T0定时器,可在四种模式下工作 实质是加1计数器(16位计数器),由高8位TH0和低8位TL0两个寄存器...
  • 51单片机定时器总结

    2020-08-28 19:34:35
    C/T这一位用来控制定时器工作方式。本来T上面有一横表示低电平有效。c为计数,也即是及外部脉冲个数,也会是定时器溢出及中断。GATE这一位用来设定外部中断脉冲是否可...我们就可以利用这个功能来测量脉冲宽度。
  • 我们可以利用51单片机的定时器中断实现这个功能。 具体实现的思路是设置1个长整型的全局时间变量sysRunmillis保存单片机上电后的运行时间。让定时器每1毫秒产生1次中断,在中断响应函数里让全局变量sysRunmillis自...
  • /********************************************** 方法1:延时法 ...描述:T1用于定时,方式1,定时时间50ms,中断方式,定时时间到,TF1=1,利用模拟串口发送字符0x69 ******************************
  • 此时钟利用的定时器的简单时钟,可以利用独立键盘进行分,时调整,并利用数码管进行显示 #include #include #define uint unsigned int #define uchar unsigned char sbit wela=P2^7; sbit dula=P2^6; sbit s2=...
  • 定时器是单片机最重要一个功能,一开始学时候遇到了不少问题,通过不断地...在这里我整理了一下定时器的使用,就用51单片机来举例。像别STC15等系列单片机其实原理上差不多,分享给大家希望能帮助大家。
  • 题目:80C51单片机的fosc = 6MHz,要求用T1定时器工作方式1,进行定时40ms。并由P1.7周期性的输出高电平40ms、低电平360ms的矩形波。;分析:可采用中断方式,定时40ms发生一次中断,利用R7记录中断次数,仅当R7=0的...
  • 51单片机利用定时器实现1秒后LED灯状态改变 作业要求: 用定时器/计数器定时功能控制实验板右下侧流水灯区域最右侧LED灯,初始上电时,该LED灯处于熄灭状态,每来1s,该LED灯改变一次状态。要求画出对应...
  • 51单片机定时器初值计算详解

    千次阅读 多人点赞 2019-10-24 23:42:44
    本文详细介绍了51单片机学习过程中定时器的初值计算问题以及相关概念,力求把每一个学习过程中可能会遇到难点说清楚,并举相关例子加以说明。学习完毕,又顺手利用刚学到定时器的相关知识写了一个“定时器...
  • 由于51单片机没有PWM功能,所以控制舵机等旋转旋转固定角度相对困难,本程序利用定时器模拟一个PWM,产生周期和频率较为准确。
  • 利用人眼视觉暂留,延时不能太长,且重复操作 本文将利用动态显示显示1000-9999加一显示器 代码如下 #include<reg51.h> #include <intrins.h>//包含移位标准库函数头文件#define uint unsigned int ...
  • 利用单片机定时器产生占空比可调PWM程序,采用C51编写
  • 51单片机利用PWM、定时器实现呼吸灯

    千次阅读 多人点赞 2020-09-16 13:06:24
    定时器定时时间:200us(即占空比每一份时间:20ms/100); 一次呼吸灯效果(暗->亮->暗):4s #include <reg52.h> //52头文件 typedef unsigned char u8; //别名 u8 Tcount, dutyfactor, ...
  • 定时器/计数器 什么是定时器/计数器? 在51单片机中,定时器/计数器是用来...AT89C51的定时器/计数器 从上图我们可以发现这款51单片机有2个16位的定时/计数器,他们被标识为T0和T1。 定时器/计数器相关的寄存器 TCON
  • 之前写过用内部pwm直接产生... 这次写是用定时器,不推薦用延时来模拟。此程序结合了我之前发矩阵按键程序,按键进行亮度调整。 #include"lcd12864.h" #include<reg51.h> uchar code num1[]={" 1234567...
  • 实验内容:利用AT89C51的定时器0 方式1实现流水灯效果,并用仿真验证。 定时器0方式1的工作方式:定时器的工作方式选择由寄存器TMOD控制,方式选择通过M0和M1控制,方式1为16位定时器/计数器,不会自动重装载,需要...

空空如也

空空如也

1 2 3 4 5 ... 11
收藏数 218
精华内容 87
热门标签
关键字:

利用51单片机的定时器