前言

整理代码的时候发现之前写的一个单片机(STC89C51)代码,主要是利用3个中断源(计时中断,外部中断),蜂鸣器,8个LED灯,独立按键(矩阵按键)以及8片阴极数码管完成一个数字电子时钟,并且具有调分钟数(+,-),调小时数(+,-),报警的功能。贴上来(代码很乱,emm不想改了,不过跑是没问题的2333

设计

  • 给两个中断
    一,时间处理中断时间每秒刷新一下数码管
    二,中断按键1 设置时间 中断按键2 退出设置时间

  • 按键控制
    按键1 时针+1
    按键2 时针-1
    按键3 分针+1
    按键4 分针-1

  • 当时间的table运行到某个状态的时候报警(led闪烁5秒 可以配合着时间中断进行变化)


C51程序代码

#include<reg52.h>
#include<intrins.h>

#define uint unsigned int
	
/* 32 -- 25 33 -- 24 s23:hour+1 s17:hour-1 s10:minute+1 s5:minute-1 s6:set timer s11:exit set */
	
sbit led_en = P1^2;
sbit lcd_en = P1^7;
sbit line_en = P1^3;
sbit wei = P1^1;
sbit duan = P1^0;
sbit wengmingqi = P3^7; 

sbit KEY1 = P2^4;
sbit KEY2 = P2^5;

uint table1[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40}; //0~9,- 段码
uint table2[] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};  //位码

uint time_table[] = {1,2,10,0,0,10,0,0}; //12-00-00 当前时间
uint time_tag_table[] = {1,2,10,0,0,10,0,4}; //12-10-00 目标事件时间

uint led_table[2]={0xff,0xfe};  //led亮灭

int second=0,minute=0,hour=12; //当前时钟的时间
int res_second=4,res_minute=0,res_hour=12;  //目标事件时间

uint key_table1[4] = {0xfe,0xfd,0xfb,0xf7};
uint key_table2[4] = {0x7e,0x7d,0x7b,0x77};


uint i = 1,j = 1,temp,flag,s,t;
uint count = 0, res_count = 0;

void delay(uint k)
{
	for(s=0;s<k;s++) {
		for(t=0;t<110;t++)
		;
	}
}

void display() 
{
	for(j=0;j<8;j++) {
			duan = 1;
			P0=table1[time_table[j]];
			duan = 0;
			wei = 1;
			P0 = table2[j];
			wei = 0;
			delay(2);
		}
}

void timeself_change() 
{
	if(second==60) {
		second = 0;
		minute++;
	}	
	if(minute==60) {
		minute = 0;
		hour++;
	}
	if(hour==24) {
		hour = 0;
	}
	if(minute <0) {
		minute = (minute+60)%60;
		hour--;
	}
	if(hour <0) {
		hour = (hour+24)%24;
	}
	time_table[0]=hour/10;
	time_table[1]=hour%10;
	time_table[3]=minute/10;
	time_table[4]=minute%10;
	time_table[6]=second/10;
	time_table[7]=second%10;
}

void interrupt_Init()
{

	EX0 = 1;
	IT0 = 1;
	PX0 = 1;
	EX1 = 1;
	IT1 = 1;
	PX1 = 0;  //低优先级
	
	TMOD=0x10;   	        //T1做定时器,方式1
	TL1=(65536 - 50000) % 256;
	TH1=(65536 - 50000) / 256;			//设置定时器的初值
	ET1=1;		 	//允许T1中断
	TR1=1;		 	//启动定时器
	EA = 1;
}

void timer0_interrupt() interrupt 3
{
	TL1=(65536 - 50000) % 256;
	TH1=(65536 - 50000) / 256;			//设置定时器的初值
	count++;
}

void key() 
{
	int i;
	for(i=03;i>=0;i--) {
		P2 = key_table1[i];
		temp = P2;
		temp = temp&0xf0;
		while(temp!=0xf0) {
			delay(5);
			temp = P2;
			temp = temp & 0xf0;
			while(temp!=0xf0) {
				temp = P2;
				if(temp == key_table2[i]) {
					switch(i) {
						case 0:
							minute--;
							break;
						case 1:
							minute++;
							break;
						case 2:
							hour--;
							break;
						case 3:
							hour++;
							break;							
					}
				}
				while(temp!=0xf0) {
					temp=P2;
					temp = temp & 0xf0;
				}
			}
		}
	}
	timeself_change();
	delay(28);
}

void digital_interrupt_1() interrupt 2
	
{
	delay(28);
	flag = 1;
	if(KEY1 == 0)
	{
		while(1) {
			key();
			display();
			if(flag == 0)
				break;
		}
	}
}

void digital_interrupt_2() interrupt 0
{
	delay(28);
	if(KEY2 == 0) {
		flag = 0;
	}
}

int pan()
{
	if(hour != res_hour || minute != res_minute || second != res_second)
		return 0;
	return 1;
}

void main()
{
	lcd_en = 0;
	P0 = 0x00;
	line_en = 0;
	P0 = 0xff;
	wei = 0;
	led_en = 0;
	interrupt_Init();
	wengmingqi = 1;
	
	while(1)
	{
		display();
		if(count == 20) {  //updatedisplay
			count=0;
			second++;
			timeself_change();
			if(pan() || res_count) {
					if(res_count == 0) {
						res_count=5;
					}
					else {
						res_count--;
						if(res_count == 0) {
							led_en = 1;
							P0=0xff;
							led_en = 0;
							wengmingqi=1;
						}
					}
			}
		}
		if(res_count != 0) {  //处理时间到的事件
			led_en = 1;
			P0=led_table[res_count&1];
			led_en = 0;
			wengmingqi = !(res_count&1);
		}
	}
}

总结

写这种片上的代码主要还是要把电路基础弄清楚,以及会看引脚功能图,对于IIC那些模块通信的话注重把原理弄清除,必要的时候还要利用示波器观察调试,让代码符合运行标准。(Ps:例程运用十分重要!因为不是写驱动啥的,要学会高效地完成一个任务。