(程序详解)51单片机+DS12C887+1602高精度时钟,软件:keil4,Proteus 8_12c887时序图详解-程序员宅基地

技术标签: 51系列单片机  ds12c887  lcd1602  单片机  

总体图
在这里插入图片描述
在这里插入图片描述

/*总体要求*/
 /*在1602上显示年月日星期时分秒,并且按照秒来实时更新显示
  可以闹钟设定,到点报警功能,报警响起时,任意键可以取消报警
  四个按键,根据功能可以调节参数,分别为功能键,数值增大键,数值减少键,闹钟查看键,
  每次按键按下,蜂鸣器都会滴一声,
  利用DS12C887实现断电后,再次上电,时间仍可以准确显示 */
  /*另外这个程序中文部分是学习了一个半月C语言的人的理解,难免有错误的地方
  如果你想移植程序,除了开头的各种脚需要修改,程序里面1602和DS12C887的写指令写数据,读指令读数据的IO口也要修改
  应该是有4~6处,一定要注意,不然你的程序不会亮起来,然后初始化部分,记得初始化后去掉,但是仿真的时候不要去掉,去掉没法显示*/
  /*非常非常非常重要的一个细节,总线上要加    AD[0..7]	 这个名字,如果没有永远也仿真不起来*/
#include<reg52.h>                         //52系列单片机机头文件
#define uchar unsigned char               //宏定义 定义一个无符号字符型数据
#define uint  unsigned int             	  //宏定义 定义一个无符号整型数据
sbit lcdrs=P2^0;                          //自定义数据/命令选择端口
sbit lcden=P2^1;                          //自定义使能信号
                                          /*P0口接液晶1602和时钟芯片DS12C887的AD0-AD7*/
sbit beep=P2^2;                           //蜂鸣器。有的按键接rd为低电平,就需要也定义一下rd,我这里面四个按键采用的是接地的结构!
sbit dsas=P2^3;							  //定义地址选通输入端 (读到那里去)
sbit dscs=P2^4;							  //定义芯片片选端	   (读哪个芯片)
sbit dsds=P2^5;							  //定义读数据允许输入脚,这里mot接地	(让不让读的开关)
sbit dsirq=P3^3;						  //定义中断请求输出
sbit dsrw=P2^6;							   //定义读允许输入	   (让读以后,我来输入数据)
sbit lcdrd=P2^7;						

sbit s1=P3^4;	                          //功能键	
sbit s2=P3^5;							  //数值增大键
sbit s3=P3^6;							  //数值减少键
sbit s4=P3^7;							  //闹钟查看键
										  //因为功能键要8次才能推出,这里可以加一个直接退出按键,可以放在外部中断上
bit    flag1, flag_ri;                                      //定义两个位变量
uchar  count,s1num,flag,t0_num;                             //定义几个无符号字符型数据,用到时再解释定义干啥用的
char   miao,shi,fen,year,month,day,week,amiao,afen,ashi;    //定义几个字符型数据,这个基本上都能理解。
uchar  code table[]=" 20  -  -      ";                          //让液晶固定显示的内容
uchar  code table1[]="      :  :  ";	                        //让液晶固定显示的内容
void   write_ds(uchar, uchar);			                    //函数申明	   寄存器初始化
void   set_alarm(uchar, uchar, uchar);	                    //函数申明	   设定闹钟
void   read_alarm();						                //函数申明	   读报警器
void   set_time();						                    //函数申明       寄存器初始化
void   delay(uint z)						                //延时函数
{										  
  uint x,y;								  //定义两个无符号整形数
  for(x=z;x>0;x--)						  //如果x>0,则y执行110次
     for(y=110;y>0;y--);				  //本小段为单位是1ms的延时
}
void di()								  //蜂鸣器报警
{
 beep=0;								  //低电平有效,蜂鸣器开始蜂鸣
 delay(100);							  //延时100毫秒,响0.1秒
 beep=1;								  //蜂鸣器停止鸣叫
}
void write_com(uchar com)				   //写液晶命令函数
{
  lcdrs=0;								   //rs低电平,表示要写命令
  lcden=0;								   //en低电平,表示为了下面送入数据,防止此时还是高电平,无法制造高脉冲
  P0=com;								   //D0-D7我接的是P0上面,数据送给它
  delay(3);								   //延时3ms
  lcden=1;								   //en高电平,延时三秒	再变成低电平,完成一次高脉冲,使数据送入液晶显示屏
  delay(3);								   //表示接下来,液晶显示屏可以过来读数据了
  lcden=0;
}
void write_date(uchar date)				   //写液晶数据函数
{
 lcdrs=1;								   //高电平表示要写数据
 lcden=0;								   //en低电平,表示为了下面送入数据,防止此时还是高电平,无法制造高脉冲
 P0=date;								   //D0-D7我接的是P0上面,数据送给它
 delay(3);								   //延时3ms
 lcden=1;								   //en高电平,延时三秒	再变成低电平,完成一次高脉冲,使数据送入液晶显示屏
 delay(3);								   //表示接下来,液晶显示屏可以过来读数据了
 lcden=0;
}
void init()                                //液晶初始化
{ 
  uchar  num;							   //定义一个字符
  EA=1;									   //打开全局中断控制
  EX1=1;								   //打开外部中断1中断
  IT1=1;								   //设置负跳变沿触发中断
  flag=0;								   // 自定义字符全部归零,为了方便实用 
  flag1=0;
  s1num=0;									//键盘按下的次数
  week=1;								   //星期显示最低也是周一的一
  lcden=0;								   //使能信号定义为零,表示还没有数据
  lcdrd=0;
  write_ds(0x0A,0x20);					   //首次使用ds12c887时使用,以后不必在写。打开振荡器
  write_ds(0x0B,0x26);					    // 设置24小时模式,数据二进制格式,开启闹钟中断。
                                            //0A,0B表示地址,20,26翻译成二进制是00100000,00100110对应着对着寄存器A和B从D7-D0 
  set_time();							   //设置默认上电时间
  write_com(0x38);						   //设置16x2显示,5x7点阵,8位数据接口
  write_com(0x0c);						   //设置开显示,不显示光标
  write_com(0x06);						   //写一个字符后,地址指针自动加1
  write_com(0x01);						   //显示清0,数据指针清0

  write_com(0x80);						   //设置数据指针位置,此处时数据指针第一行,第一处
  for(num=0;num<15;num++)				   //<15原因是第一行显示的数字或者空格一共14个
  {
    write_date(table[num]);				   //把要显示的数据送入到液晶的第一行
	delay(1);							   //延迟一毫秒
  }

    write_com(0x80+0x40);				   //设置数据指针位置,此处时数据指针第二行,第一处
    for(num=0;num<11;num++)				   //<11原因是第二行显示的数字或者空格一共10个
  {
    write_date(table1[num]);				   //把要显示的数据送入到液晶的第二行
	delay(1);							   //延迟一毫秒
  }
}
void write_sfm(uchar add,char date)		   //1602液晶刷新时分秒函数4为时,7为分,10为秒	  ,4.7.10均是显示位置
{   
   char shi,ge;							   //定义两个数,来表示十位数和个位数
   shi=date/10;							   // 要送去显示的十位
   ge=date%10;							   //要送去显示的个位  
   write_com(0x80+0x40+add);				   //时间是在第二行显示,所以是加0X40,在add处显示
   write_date(0x30+shi);				   //shi和ge都是整形数值,但是液晶1602是内置ASCII表格,0在第048位
   write_date(0x30+ge);					   //转成十六进制就是0x30,
}
/*这个地方可能会有人迷糊,设置时分秒,怎么只设置十位和个,好像给人的感觉就是只设置时位,和分位,秒位怎么不设置,
实际上是,这里写入并显示的是  把时的十位和个位,分的十位和个位,秒的十位和个位,分别送去进行运算 ,然后先写命令确定位置,
再写数据,用来显示,下面一个子函数也是同样的道理*/
void write_nyr(uchar add,char date)		   //1602液晶刷新年月日函数,3为年,6为月9为天	 ,369也分别是位置
{   
   char shi,ge;							   //定义两个数,来表示十位数和个位数
   shi=date/10;							   // 要送去显示的十位
   ge=date%10;							   //要送去显示的个位  
   write_com(0x80+add);			           //时间是在第二行显示,所以是加0X40,在add处显示
   write_date(0x30+shi);				   //shi和ge都是整形数值,但是液晶1602是内置ASCII表格,0在第048位
   write_date(0x30+ge);					   //转成十六进制就是0x30,
}   /*实际上到了主函数,时分秒,年月日会有六个子函数进行分别调用,这里用两个子函数来表示,不能用一个的原因是显示的内容不在一行,
主要是写入命令的位置不一样,大家可以观察一下*/
void write_week(char we)					//写液晶1602星期显示函数   
{
  write_com(0x80+12);						//让we在0x80+12这个位置显示
  switch(we)								//多分支结构的条件选择语句,we等于几就去执行几号分支
  {
   case 1:write_date('M');				   //写入M。整个的意思是如果we等于1,则显示MON,即星期一
          delay(5);						   //延时5毫秒后写入O
		  write_date('O');				   //写入O
          delay(5);						   //延时5毫秒后写入N
  	      write_date('N');				   //写入N
          break;						   //间断,为了和后面分开
   case 2:write_date('T');				   //写入T。整个的意思是如果we等于2,则显示TUE,即星期二
          delay(5);						   //延时5毫秒后写入U
		  write_date('U');				   //写入U
          delay(5);						   //延时5毫秒后写入E
  	      write_date('E');				   //写入E
          break;						   // 间断,为了和后面分开
   case 3:write_date('W');				   //写入W。整个的意思是如果we等于3,则显示WED,即星期三
          delay(5);						   //延时5毫秒后写入E
		  write_date('E');				   //写入E
          delay(5);						   //延时5毫秒后写入D
  	      write_date('D');				   //写入D
          break;						   //间断,为了和后面分开
   case 4:write_date('T');				   //写入T。整个的意思是如果we等于4,则显示THU,即星期四
          delay(5);						   //延时5毫秒后写入H
		  write_date('H');				   //写入H
          delay(5);						   //延时5毫秒后写入U
  	      write_date('U');				   //写入U
          break;						   //间断,为了和后面分开
   case 5:write_date('F');				   //写入F。整个的意思是如果we等于5,则显示FRI,即星期五
          delay(5);						   //延时5毫秒后写入R
		  write_date('R');				   //写入R
          delay(5);						   //延时5毫秒后写入I
  	      write_date('I');				   //写入I
          break;						   //间断,为了和后面分开
   case 6:write_date('S');				   //写入S。整个的意思是如果we等于6,则显示SAT,即星期六
          delay(5);						   //延时5毫秒后写入A
		  write_date('A');				   //写入A
          delay(5);						   //延时5毫秒后写入T
  	      write_date('T');				   //写入T
          break;						   //间断,为了和后面分开
   case 7:write_date('S');				   //写入S。整个的意思是如果we等于7,则显示SUN,即星期日
          delay(5);						   //延时5毫秒后写入U
		  write_date('U');				   //写入U
          delay(5);						   //延时5毫秒后写入N
  	      write_date('N');				   //写入N
          break;						   //间断,为了和后面分开
	
   }
}
void keyscan()							   //数字时钟按键扫描函数
{
   if(flag_ri==1)						   //如果标志位,闹钟等于1,则蜂鸣器会响 ,这一句和按键扫描没有关系,主要是为了任意键可以消除
                                           //闹钟的报警,可以理解为,为闹钟取消而增加的一个扫描小程序。
   {
	if((s1==0)||(s2==0)||(s3==0)||(s4==0))	//如果s1-s4有一个为零,低电平	 ||这个符号是关系运算符, 或的意思,有一个是低电平,则进行下一步。
	{
	 delay(5);								//延时去抖动
	 if((s1==0)||(s2==0)||(s3==0)||(s4==0))	//再次确认,如果s1-s4有一个为零,低电平
	 {
	   while(!(s1&&s2&&s3&&s4));			/*/如果他们运算的结果为0	 ,当闹钟响起,就会进入这个按键扫描程序,这没有按下键盘的时候,
	   就会运行到这里,进行判断循环,一直等在,四个按键,有一个为0,这个语句就会判断为假,进行下一步运算,&&这个符号是关系运算的 与,
	   一个值为零,则整体结果运算为0*/
	   di();						    	//则蜂鸣器会响,闹钟有自己的蜂鸣程序,这里有个DI主要原因是,每次按键 都会响,这是事先规定号的
	   flag_ri=0;							//清除报警标志
	 }
	}
   }

  if(s1==0)									  //检测按键s1
  {
   delay(5);								  //延时5毫秒,也可叫延时去抖动。
   if(s1==0)								  //再次检测按键s1
   {
    s1num++;								  //定义为键盘按下的次数
	if(flag1 == 1)							  //这个是用来闹钟的,如果等予1,说明现在是在设置闹钟值,从下面来看这个只能调节每天的闹钟,不能调节年月
	   if(s1num == 4)						 //因为只能调节时分秒,让他们在里面循环
	      s1num=1;							  //这个地方我卡了几天,大家要综合前后文,明白flag1是闹钟被按下了,flag是为了跳出主程序循环,为了设置时间
	flag=1;	    							  //按键计数以后,则这个位变量变成1,
	while(!s1);								  /*!s1 的意思是取s1相反的数,是用来测试判断按键按下后有没有松开,松开则计数一次后,电平
	                     					  变成高电平,说明此时已经松开,也确定了s1num的具体数值,也就是按了几下*/
	di();									  //每按下一次则响一声
	switch(s1num)							  //判断是按了几下,则选择什么位置光标点闪烁。
	{
		case 1:write_com(0x80+0x40+10);		  //很明显这是显示屏第二行的显示指令,10  说明光标调节的是秒
		       write_com(0x0f);     		  /*/不管你按的再快,光标也会从第一步开始,所以把 开显示,显示光标,光标闪烁  这个lcd的这个
											     指令设置放在这里	*/
			   break;						  //会不停的跳出,再去问现在是按了几下了,按了几下,就会进入对应的里面,下面都是一样的不解释了。
		case 2:write_com(0x80+0x40+7);     	  /*这个就是光标在分钟上闪烁,(不光时分秒都是在他们的十位闪烁,
		                                       如果送的是两位数,LCD的指针可以自动加一,把个位也给显示了)*/
			   break;
		case 3:write_com(0x80+0x40+4);    	   //这个是在小时的位置显示闪烁
			   break;
		case 4:write_com(0x80+12);   			//这就是进入显示屏第一行了,在显示星期的位置,第十位,最右边,是调节星期的位置。
			   break;
		case 5:write_com(0x80+9);    			 //日
			   break;
		case 6:write_com(0x80+6);    			 //月
			   break;
		case 7:write_com(0x80+3);     			  //年
			   break;
		case 8:s1num=0;							  //把按键数置零,等这个小程序运行完,则光标不在闪缩,一切正常运行。相当于不选择任何位置。
		       write_com(0x0c);					  //设置开显示,不显示光标
			   flag=0;							  //等于0就是回到主程序的正常运行(可以参看以下主程序,更能方便理解)
				/*下面的几句基本一样,明白一个就全明白了,这个地方主要是查表,查寄存器A,功能列表有时分秒,闹钟的也有时分秒,年月日他们分别
				对应的寄存器地址,比如秒对应的是00H,分钟对应的是02H,所以0,2,4,6,7,8,9都是地址,往这个里面写,就是写的时分秒,
				可能有人问,既然是00H,02H,为什么写成0,2等等,其实是因为十六进制和十进制在0~9,书写虽然有区别,但是结果都一样*/
			   write_ds(0,miao);				   
			   write_ds(2,fen);
			   write_ds(4,shi);
			   write_ds(6,week);
			   write_ds(7,day);
			   write_ds(8,month);
			   write_ds(9,year);
			   break;	                           //跳出
	}
   }
  }
  if(s1num!=0)	                                    //测试不等于 0,也就是只有当s1按下时才能检测和用来启动s2和s3.
  {
   if(s2==0)                               	        //问S2按键有没有按下
   {
	delay(1);	 									//延时1毫秒
	if(s2==0)	 									//再次来检测
	{
	  while(!s2); 									 //给s2取反
	  di();											 //滴一声
	  switch(s1num)									 //根据s1按下的次数,来确定调节哪个位置
	  {				   								 //下面给大家解释一组,应该可以全部明白
	   case 1:miao++;								 //按键s2按一下,这里就会加一	  设置秒钟
	          if(miao==60) 							 //如果加到了60 ,秒就会变成0
			     miao=0;
		     write_sfm(10,miao);	                 //把秒的值经过运算,送到LCD相应的位置去显示
			 write_com(0x80+0x40+10);                /*这一句是调用写指令,因为指针把秒显示完毕后,光标就会在秒的个位,
			                                         所以这一句是把光标在拉回来,因为不能正好一下子就把秒的值设置的正好*/
			 break;					                 //然后跳出,等待按键再次按下
	   case 2:fen++;					             //设置分钟
	          if(fen==60)				             //分,秒都用到60,生活中并没有60,这里也不能设置成59,但是可以改程序为>59.道理是一样的
			     fen=0;
			 write_sfm(7,fen);
			 write_com(0x80+0x40+7);
			 break;
	   case 3:shi++;					              //设置时钟
	          if(shi==24)				               //和上面一样的道理
			     shi=0;
			  write_sfm(4,shi);
			  write_com(0x80+0x40+4);
			  break;
	   case	4:week++;				                  //设置星期
	          if(week ==8)				              //当按下第八次的时候,则8就会变成1
			     week=1;
			  write_week(week);
			  write_com(0x80+12);
			  break;
	  case 5:day++;					                    //设置天数
	         if(day==32)			  
			    day=1;
			 write_nyr(9,day);
			 write_com(0x80+9);
			 break;
	 case 6:month++;			                       //设置月数
	        if(month==13)
			   month=1;
			write_nyr(6,month);
			write_com(0x80+6);
			break;
	case 7:year++;					                    //设置年
	       if(year==100)
		      year=0;
			 write_nyr(3,year);
			 write_com(0x80+3);
			 break;	  
	  }
    }
   }
   if(s3==0)	                                       //上面是增大键,下面就是减少键
   {
	delay(1);	                                       //延迟1毫秒
	if(s3==0)	                                      //再次判断有没有按下
	{
	  while(!s3);                                     //取相反数,看看是不是为真
	  di();
	  switch(s1num)	                                  //根据按键1,就可以确定光标在什么位置,下面介绍和上面基本上一样,
	  {					                              //只不过没有60了,反而有59,可以好好理解一下,其实很简单这个地方,就不解释了
	   case 1:miao--;				                   //设置秒钟
	          if(miao==-1)
			     miao=59;
		     write_sfm(10,miao);
			 write_com(0x80+0x40+10);
			 break;
	   case 2:fen--;				                  //设置分钟
	          if(fen==-1)
			     fen=59;
			 write_sfm(7,fen);
			 write_com(0x80+0x40+7);
			 break;
	   case 3:shi--;					              //设置小时
	          if(shi==-1)
			     shi=23;
			  write_sfm(4,shi);
			  write_com(0x80+0x40+4);
			  break;
	   case	4:week--;					              //设置星期
	          if(week==-0)
			     week=7;
			  write_week(week);
			  write_com(0x80+12);
			  break;
	   case 5:day--;					              //设置天数
	          if(day==0)
			     day=31;
		 	  write_nyr(9,day);
			  write_com(0x80+9);
			  break;
	   case 6:month--;					               //设置月数
	         if(month==0)
			    month=12;
			 write_nyr(6,month);
			 write_com(0x80+6);
			 break;
 	   case 7:year--;					                 //设置年
	         if(year==-1)
		      year=99;
			 write_nyr(3,year);
			 write_com(0x80+3);
			 break;	  
	  }
    }
   }
  }
  if(s4==0)	                                                 //这个地方是闹钟设置键
  {
   delay(5);	                                             //延时5毫秒
   if(s4==0)	                                             //再次判断是否按下
   {
	flag1=~flag1;                                            //如果按下,则按键1程序,进入时分秒的循环,这里的讲解和前面相呼应。
	while(!s4);	                                             //取反是不是为真
	di();		                                             //为真说明,有按键按下,响一声,
	s1num=0;												 
	/*调试仿真时发现如果按下了按键一  在按闹钟,然后取消闹钟,在按按键一,会不出现光标,为了让闹钟和按键一不冲突,我添加这一句
	每当我按下按键一,不管按几次,只要按下按键4,都会让光标变成0位 */
	if(flag1==0)                                             //如果等于0,说明按得第二下,属于退出闹钟设置。
	{
	 flag=0;												 //程序运行到这一步,说明运行完这个程序,就要回到主程序了,
	                                                         //把按键1标志指令以后,本程序结束,可以进入主程序,否则还要在去按键1,按几下     
	 write_com(0x80+0x40);									 //把指针选择第二行,第一位,
	 write_date(' ');										 //写入两个空的符号,主要是为了区别状态,是调整时间还是设置闹钟
	 write_date(' ');
	 write_com(0x0c);										  //关闭光标闪烁
	 write_ds(1,miao);										  //把时分秒送到DS12C887,程序正常运行
	 write_ds(3,fen);										  //这个时候的135要注意,可以看出是新设置闹钟值
	 write_ds(5,shi);
	}
	else													   //如果flag不等于0,则进入这个程序,实际上程序都是先运行这个
	{														   //进入闹钟设置
	 read_alarm();											   //读闹钟原来的数据
	 miao=amiao;											   //分别送入时分秒
	 fen=afen;
	 shi=ashi;
	 write_com(0x80+0x40);									   //写指令,把指针,也就是光标位置放在第二排第一位
	 write_date('R');										   //分别送入R  I送去显示,可以区分现在是在调整时间,还是修改闹钟
	 write_date('I');
	 write_com(0x80+0x40+3);								   /*这个地方还有一个指针,就会让很多人不理解,其实这个地方设计很巧妙
	                                                           3号位置,正好是时钟前面一位,然后再那边闪烁,接着等待按键1,来选择调整时分秒,
															   按一下变换一个位置,在3号位闪烁还不影响开始时的闹钟值*/
	                                                            
	 write_sfm(4,ashi);										   //送到液晶去显示
	 write_sfm(7,afen);
	 write_sfm(10,amiao);
	}
   }
  } 
}
void write_ds(uchar add,uchar date)	                           //写DS12C887函数 
{
 dscs=0;													   //根据时序图,把相应的脚变化高低,片选端,低电平有效
 dsas=1;													   //地址选通输入端
 dsds=1;													   //读允许输入脚
 dsrw=1;														//写输入脚
 P0=add;														//写地址
 dsas=0;
 dsrw=0;
 P0=date;													    //写命令
 dsrw=1;
 dsas=1;
 dscs=1;
}
uchar read_ds(uchar add)									   //读DS12C887函数	,这里没有用void要注意
{
 uchar ds_date;												   //定义一个无符号函数
 dsas=1;													   //根据时序图,进行高低电平设置
 dsds=1;
 dsrw=1;
 dscs=0;
 P0=add;													   //写地址,确定要读什么位置的值
 dsas=0;
 dsds=0;
 P0=0xff;													   //先把P1进行置高,清理数据
 ds_date=P0;													//把新的得到了  12c887里面的数值,赋值给ds_date
 dsds=1;														//根据时序图继续设置电平
 dsas=1;
 dscs=1;
 return ds_date;											   //谁调用它,什么位置调用的,就会把这个新得到的值在返回给他,
															   //看得出来只有读闹钟值是调用了。
}
 void set_time()											   //首次操作12c887时给寄存器初始化,完成后,要把这一段用 给屏蔽起来
{
 write_ds(0,0);												  //根据寄存器A,相当于	所有的位置,全部显示00。
 write_ds(1,0);
 write_ds(2,0);
 write_ds(3,0);
 write_ds(4,0);
 write_ds(5,0);
 write_ds(6,0);
 write_ds(7,0);
 write_ds(8,0);
 write_ds(9,0);
} 	
void read_alarm()											  //读取闹钟值
{
 amiao=read_ds(1);											   //根据寄存器A,分别把闹钟值读出来,并且赋值给时分秒
 afen=read_ds(3);
 ashi=read_ds(5);
}
void main()													 //主程序
{
 init();													  //调用初始化函数
 while(1)													  //为真不断循环,等待闹钟中断
 {
 keyscan();													   //按键子程序,不断地扫描
 if(flag_ri==1)												   //如果有闹钟,则进入这里
 {
  di();
  delay(100);													//一直响 滴滴 
  di();
  delay(500);
 }
 if((flag==0) && (flag1==0))								   //如果按键标志位和闹钟标志位都为0,则继续
 {
  keyscan();												   //按键继续扫描
  year=read_ds(9);											   //把DS12C887里面的数值不断读取
  month=read_ds(8);
  day=read_ds(7);
  week=read_ds(6);
  shi=read_ds(4);
  fen=read_ds(2);
  miao=read_ds(0);
  write_sfm(10,miao);										   //这里把DS12c887的值不断地写入到lcd1602
  write_sfm(7,fen);
  write_sfm(4,shi);
  write_week(week);
  write_nyr(3,year);
  write_nyr(6,month);
  write_nyr(9,day);
 }
 }
}															 
void exter()interrupt 0									    //中断1程序
{
 uchar c;														//定义一个无符号数值
 flag_ri=1;														//闹钟中断时间到了
 c=read_ds(0x0c);												//读取c寄存器的值,表示响应了中断
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/huang873036652/article/details/91460594

智能推荐

FTP命令字和返回码_ftp 登录返回230-程序员宅基地

文章浏览阅读3.5k次,点赞2次,收藏13次。为了从FTP服务器下载文件,需要要实现一个简单的FTP客户端。FTP(文件传输协议) 是 TCP/IP 协议组中的应用层协议。FTP协议使用字符串格式命令字,每条命令都是一行字符串,以“\r\n”结尾。客户端发送格式是:命令+空格+参数+"\r\n"的格式服务器返回格式是以:状态码+空格+提示字符串+"\r\n"的格式,代码只要解析状态码就可以了。读写文件需要登陆服务器,特殊用..._ftp 登录返回230

centos7安装rabbitmq3.6.5_centos7 安装rabbitmq3.6.5-程序员宅基地

文章浏览阅读648次。前提:systemctl stop firewalld 关闭防火墙关闭selinux查看getenforce临时关闭setenforce 0永久关闭sed-i'/SELINUX/s/enforcing/disabled/'/etc/selinux/configselinux的三种模式enforcing:强制模式,SELinux 运作中,且已经正确的开始限制..._centos7 安装rabbitmq3.6.5

idea导入android工程,idea怎样导入Android studio 项目?-程序员宅基地

文章浏览阅读5.8k次。满意答案s55f2avsx2017.09.05采纳率:46%等级:12已帮助:5646人新版Android Studio/IntelliJ IDEA可以直接导入eclipse项目,不再推荐使用eclipse导出gradle的方式2启动Android Studio/IntelliJ IDEA,选择 import project3选择eclipse 项目4选择 create project f..._android studio 项目导入idea 看不懂安卓项目

浅谈AI大模型技术:概念、发展和应用_ai大模型应用开发-程序员宅基地

文章浏览阅读860次,点赞2次,收藏6次。AI大模型技术已经在自然语言处理、计算机视觉、多模态交互等领域取得了显著的进展和成果,同时也引发了一系列新的挑战和问题,如数据质量、计算效率、知识可解释性、安全可靠性等。城市运维涉及到多个方面,如交通管理、环境监测、公共安全、社会治理等,它们需要处理和分析大量的多模态数据,如图像、视频、语音、文本等,并根据不同的场景和需求,提供合适的决策和响应。知识搜索有多种形式,如语义搜索、对话搜索、图像搜索、视频搜索等,它们可以根据用户的输入和意图,从海量的数据源中检索出最相关的信息,并以友好的方式呈现给用户。_ai大模型应用开发

非常详细的阻抗测试基础知识_阻抗实部和虚部-程序员宅基地

文章浏览阅读8.2k次,点赞12次,收藏121次。为什么要测量阻抗呢?阻抗能代表什么?阻抗测量的注意事项... ...很多人可能会带着一系列的问题来阅读本文。不管是数字电路工程师还是射频工程师,都在关注各类器件的阻抗,本文非常值得一读。全文13000多字,认真读完大概需要2小时。一、阻抗测试基本概念阻抗定义:阻抗是元器件或电路对周期的交流信号的总的反作用。AC 交流测试信号 (幅度和频率)。包括实部和虚部。​图1 阻抗的定义阻抗是评测电路、元件以及制作元件材料的重要参数。那么什么是阻抗呢?让我们先来看一下阻抗的定义。首先阻抗是一个矢量。通常,阻抗是_阻抗实部和虚部

小学生python游戏编程arcade----基本知识1_arcade语言 like-程序员宅基地

文章浏览阅读955次。前面章节分享试用了pyzero,pygame但随着想增加更丰富的游戏内容,好多还要进行自己编写类,从今天开始解绍一个新的python游戏库arcade模块。通过此次的《连连看》游戏实现,让我对swing的相关知识有了进一步的了解,对java这门语言也有了比以前更深刻的认识。java的一些基本语法,比如数据类型、运算符、程序流程控制和数组等,理解更加透彻。java最核心的核心就是面向对象思想,对于这一个概念,终于悟到了一些。_arcade语言 like

随便推点

【增强版短视频去水印源码】去水印微信小程序+去水印软件源码_去水印机要增强版-程序员宅基地

文章浏览阅读1.1k次。源码简介与安装说明:2021增强版短视频去水印源码 去水印微信小程序源码网站 去水印软件源码安装环境(需要材料):备案域名–服务器安装宝塔-安装 Nginx 或者 Apachephp5.6 以上-安装 sg11 插件小程序已自带解析接口,支持全网主流短视频平台,搭建好了就能用注:接口是公益的,那么多人用解析慢是肯定的,前段和后端源码已经打包,上传服务器之后在配置文件修改数据库密码。然后输入自己的域名,进入后台,创建小程序,输入自己的小程序配置即可安装说明:上传源码,修改data/_去水印机要增强版

verilog进阶语法-触发器原语_fdre #(.init(1'b0) // initial value of register (1-程序员宅基地

文章浏览阅读557次。1. 触发器是FPGA存储数据的基本单元2. 触发器作为时序逻辑的基本元件,官方提供了丰富的配置方式,以适应各种可能的应用场景。_fdre #(.init(1'b0) // initial value of register (1'b0 or 1'b1) ) fdce_osc (

嵌入式面试/笔试C相关总结_嵌入式面试笔试c语言知识点-程序员宅基地

文章浏览阅读560次。本该是不同编译器结果不同,但是尝试了g++ msvc都是先计算c,再计算b,最后得到a+b+c是经过赋值以后的b和c参与计算而不是6。由上表可知,将q复制到p数组可以表示为:*p++=*q++,*优先级高,先取到对应q数组的值,然后两个++都是在后面,该行运算完后执行++。在电脑端编译完后会分为text data bss三种,其中text为可执行程序,data为初始化过的ro+rw变量,bss为未初始化或初始化为0变量。_嵌入式面试笔试c语言知识点

57 Things I've Learned Founding 3 Tech Companies_mature-程序员宅基地

文章浏览阅读2.3k次。57 Things I've Learned Founding 3 Tech CompaniesJason Goldberg, Betashop | Oct. 29, 2010, 1:29 PMI’ve been founding andhelping run techn_mature

一个脚本搞定文件合并去重,大数据处理,可以合并几个G以上的文件_python 超大文本合并-程序员宅基地

文章浏览阅读1.9k次。问题:先讲下需求,有若干个文本文件(txt或者csv文件等),每行代表一条数据,现在希望能合并成 1 个文本文件,且需要去除重复行。分析:一向奉行简单原则,如无必要,绝不复杂。如果数据量不大,那么如下两条命令就可以搞定合并:cat a.txt >> new.txtcat b.txt >> new.txt……去重:cat new...._python 超大文本合并

支付宝小程序iOS端过渡页DFLoadingPageRootController分析_类似支付宝页面过度加载页-程序员宅基地

文章浏览阅读489次。这个过渡页是第一次打开小程序展示的,点击某个小程序前把手机的开发者->network link conditioner->enable & very bad network 就会在停在此页。比如《支付宝运动》这个小程序先看这个类的.h可以看到它继承于DTViewController点击左上角返回的方法- (void)back;#import "DTViewController.h"#import "APBaseLoadingV..._类似支付宝页面过度加载页

推荐文章

热门文章

相关标签