单片机项目——电子表项目逐句解析

电路图:

程序设计

// main.c
/**
  电子表设计程序分析
  author: 胡云飞
  程序步骤如下:
  1. 初始化定时器
  2. 写中断服务函数
  	- 初始化计时器的初值
	- 让cp++,当cp>=250时,为半秒,让cp2++,flash取反(分隔符闪烁)
	- 如果cp2>=2,复位cp2=0,并让秒加1,通过秒来联动分钟和小时
	- 通过cp3对八位数码管的各个位进行显示
  4. 写display.c文件,通过guanjian变量的值来进行不同的显示,guanjian=0代表普通显示,分隔符闪烁,=1小时闪烁,=2分钟闪烁,=3秒闪烁。
  3. 获取按键,通过按不同的键,修改guanjian变量的值,通过guanjian变量的值的变化进入对应的显示
*/

#include<reg51.h>
// 由于key和display会用到下面的变量,所以一定要把变量定义在include之前,否则会报错
// 由于这几个变量都不会大于255,所以使用char类型,节省资源
unsigned char cp,cp2,cp3,guanjian,flash,sec,min,hour;
// 为了防止代码过长,将按键处理相关代码封装到key.c文件
#include<key.c>
// 将显示相关代码封装到display.c文件
#include<display.c>

// 中断记满会自动运行中断服务函数,格式如下
// isr的i就是中断,sr是server(服务),也就是中断服务
// 需要跟一个关键字interrupt,中断到定时器T0的中断号1
void timer0_isr(void) interrupt 1
{
	// 由于计时器不会每次都从init中的初值运行,所以我们在中断服务函数的开头设定初值
	TH0 = (65535 - 2000) / 255;
	TL0 = (65535 - 2000) % 255;
	// 每一次中断,就让一个变量加1
	cp++;
	// 由于cp是2000微妙(2毫秒)会中断一次,所以500次就是1000毫秒,就是1秒,250次就是半秒,半秒的原因是需要让分隔符闪烁(可以发现分隔符变化两次,数字变一下)。
	// cp用来控制分隔符的闪烁,cp2用来控制时分秒变量的递增
	if(cp >= 250)
	{
		cp = 0;
		// 让cp2加加
		cp2++;
		// 分隔符闪烁
		flash = ~flash;
	}
	
	// cp2为2时,就是1秒
	if(cp2 >= 2)
	{
		// 复位cp2
		cp2 = 0;
		sec++;
		if(sec >= 60){sec = 0; min++;}
		if(min >= 60){min = 0; hour++;}
		if(hour >= 24)hour=0;
	}
	// guanjian = 0 代表普通显示状态
	// guanjian = 1 代表时闪烁(要修改小时)
	// guanjian = 2 代表分闪烁(要修改分钟)
	// guanjian = 3 代表秒闪烁(要修改秒)
	if(guanjian == 0)display0(); 
	if(guanjian == 1)display1(); 
	if(guanjian == 2)display2();
	if(guanjian == 3)display3();
	// 上面的循环会根据cp3的值来显示对应位的数据
	// 每过2000微妙(0.002秒),就让cp3++,让cp3从0-7循环,目的是和上面的循环显示电子表上的八个部分(6个数字,2个分隔符)。
	cp3++;
	if(cp3 >= 8)cp3 = 0;
}

void timer0_init(void)
{
	// 1. 工作方式
	// TMOD等于1,就是对内部脉冲进行计数
	TMOD = 0x01;
	// 2. 设定初值
	// 晶振,一微妙一个,最大是65535,可以有一个初值,是在初值上进行计数,计数到65535就会产生中断。
	// TH0是高八位,还差2000到65535,除以255是得到高八位的数。
	// 这个2000可以随意设定,设定2000的意思就是这个定时器每2000微妙(2毫秒)会中断一次。
	TH0 = (65535 - 2000) / 255;
	// 赋值低八位
	TL0 = (65535 - 2000) % 255; 
	// 3. 开中断
	// 开中断,用IE寄存器
	// EA——总中断
	EA = 1;
	// T0中断打开
	ET0 = 1;
	// 4. 启动  T——定时器,R——Run启动
	TR0 = 1;
}
void main()
{
	// 开启中断
	timer0_init();
	while(1)
	{
		// 获取按键状态
	   	key();
	}
}
// key.c
sbit key_set = P1^0;
sbit key_add = P1^1;
sbit key_sub = P1^2;
sbit key_rst = P1^3;

void key(void)
{
	// 如果按下了key_set按钮
	if(key_set == 0)
	{
		// key_set按键用来控制“我们要修改的是谁。”,比如时,分,秒。
		// 第一次按下的时候,guanjian=1,时闪烁,再按下guanjian=2,分闪烁,再按下guanjian=3,秒闪烁,再按下guanjian=4,分闪烁,再按下guanjian=5,guanjian被重置为1,时闪烁。 一直循环。
		// 让guanjian++
		guanjian++;
		// 如果大于等于5了,让guanjian=1.
		if(guanjian >= 4)guanjian = 1;
		// 如果一直按下就进入这个死循环,即我们想要让按钮抬起才视为一次按键。
		while(key_set == 0);	
	}
	// 如果按下了add键
	if(key_add == 0)
	{
		// 如果guanjian=0,意味着没有按下key_set按钮,也就没有进入修改状态,所以不添加。
		// 如果guanjian=1,意味着通过key_set调到了时的位置,此时按下add键,会让小时加1,最高加到23的时候,就封顶了。
		if(guanjian == 1){hour++;if(hour >= 24)hour = 23;}
		// 同上,控制分
		if(guanjian == 2){min++;if(min >= 60)min = 59;}
		// 同上,控制秒
		if(guanjian == 3){sec++;if(sec >= 60)sec = 59;}	
		// 等待按键抬起
		while(key_add == 0);	
	}
	// 如果按下了sub键
	if(key_sub == 0)
	{
		// 如果guanjian=0,意味着没有按下key_sub按钮,也就没有进入修改状态。
		// 如果guanjian=1,意味着通过key_set调到了时的位置,此时按下sub键,会让小时键1,最高减到0的时候,就到底了。 
		if(guanjian == 1){hour--;if(hour <= 0)hour = 0;}
		// 同上,减分
		if(guanjian == 2){min--;if(min <= 0)min = 0;}
		// 同上,减秒
		if(guanjian == 3){sec--;if(sec <= 0)sec = 0;}
		// 等待按键抬起	
		while(key_sub == 0);	
	}
	// 如果按下了reset按钮
	if(key_rst == 0)
	{
		// 把guanjian复位为0,即不进入修改状态
		guanjian = 0;
		// 等待按键抬起
		while(key_rst == 0);	
	}
}
// display.c
const unsigned char seven_seg[] = 
{
	0xc0,0xf9,0xa4,0xb0,0x99,
	0x92,0x82,0xf8,0x80,0x90,
};
// 每一次中断,都会让cp3++,一次中断是2000微秒,也就是0.002秒,也就是说0.016秒就可以让cp3从0-7循环一轮。
// 每循环一轮都会把对应变量上的值显示出来,因为它运行的太快了,所以你看不到变化的过程。
// 正常显示,不加选中闪烁
void display0(void)
{								
	// 从接线上可以看到,P2.0 - P2.7,P2.0 P2.1是控制秒的个位和十位,P2.2是控制秒左边的分隔符的闪烁,P2.3 P2.4是控制分的个位和十位,P2.5是控制分左边的分隔符,P2.6 P2.7控制小时	   
	if(cp3 == 0){P2 = 0x01;P0 = seven_seg[sec % 10];}
	if(cp3 == 1){P2 = 0x02;P0 = seven_seg[sec / 10];}
	if(cp3 == 2){P2 = 0x04;P0 = 0xbf | flash;}
	if(cp3 == 3){P2 = 0x08;P0 = seven_seg[min % 10];}
	if(cp3 == 4){P2 = 0x10;P0 = seven_seg[min / 10];}
	if(cp3 == 5){P2 = 0x20;P0 = 0xbf | flash;}
	if(cp3 == 6){P2 = 0x40;P0 = seven_seg[hour % 10];}
	if(cp3 == 7){P2 = 0x80;P0 = seven_seg[hour / 10];}
}
// 让小时闪烁,只需要在小时的位置上或flash即可,并且需要把分隔符上的flash去掉
void display1(void)
{
	if(cp3 == 0){P2 = 0x01;P0 = seven_seg[sec % 10];}
	if(cp3 == 1){P2 = 0x02;P0 = seven_seg[sec / 10];}
	if(cp3 == 2){P2 = 0x04;P0 = 0xbf;}
	if(cp3 == 3){P2 = 0x08;P0 = seven_seg[min % 10];}
	if(cp3 == 4){P2 = 0x10;P0 = seven_seg[min / 10];}
	if(cp3 == 5){P2 = 0x20;P0 = 0xbf;}
	if(cp3 == 6){P2 = 0x40;P0 = seven_seg[hour % 10] | flash;}
	if(cp3 == 7){P2 = 0x80;P0 = seven_seg[hour / 10] | flash;}
}
// 让分钟闪烁
void display2(void)
{
	if(cp3 == 0){P2 = 0x01;P0 = seven_seg[sec % 10];}
	if(cp3 == 1){P2 = 0x02;P0 = seven_seg[sec / 10];}
	if(cp3 == 2){P2 = 0x04;P0 = 0xbf;}
	if(cp3 == 3){P2 = 0x08;P0 = seven_seg[min % 10] | flash;}
	if(cp3 == 4){P2 = 0x10;P0 = seven_seg[min / 10] | flash;}
	if(cp3 == 5){P2 = 0x20;P0 = 0xbf;}
	if(cp3 == 6){P2 = 0x40;P0 = seven_seg[hour % 10];}
	if(cp3 == 7){P2 = 0x80;P0 = seven_seg[hour / 10];}
}
// 让秒闪烁
void display3(void)
{
	if(cp3 == 0){P2 = 0x01;P0 = seven_seg[sec % 10] | flash;}
	if(cp3 == 1){P2 = 0x02;P0 = seven_seg[sec / 10] | flash;}
	if(cp3 == 2){P2 = 0x04;P0 = 0xbf;}
	if(cp3 == 3){P2 = 0x08;P0 = seven_seg[min % 10];}
	if(cp3 == 4){P2 = 0x10;P0 = seven_seg[min / 10];}
	if(cp3 == 5){P2 = 0x20;P0 = 0xbf;}
	if(cp3 == 6){P2 = 0x40;P0 = seven_seg[hour % 10];}
	if(cp3 == 7){P2 = 0x80;P0 = seven_seg[hour / 10];}
}

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jacob_云飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值