基于金沙滩51单片机的电子跑表
很久之前学51单片机的时候做的了,现在分享一下。
基于金沙滩51单片机,很推荐这款单片机开发板,教程很好。
零、完成功能
本项目完成以下功能:
时钟模式:
- 在数码管上显示分、秒
跑表模式:
- 跑表显示范围:0-999.0秒
- 按下启动键开始计时
- 按下暂停键暂停计时
- 按下继续键继续计时
- 按下复位键计时归零
壹、硬件电路图
下面是项目用到的硬件电路图,完整原理图请点击:金沙滩51单片机原理图
数码管电路
贰、程序源码
注释挺多的,程序挺简单的,就不多介绍了,有问题可以在下面讨论
#include<reg52.h>
#define MAX_NUMBER 9999 //最大值(单位:0.1)
#define INIT_NUMBER 0 //初始值
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
sbit keyout = P2^0;
sbit key16 = P2^7;
sbit key13 = P2^4;
sbit key14 = P2^5;
sbit key15 = P2^6;
unsigned char code smg[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};/*数码管真值表*/
unsigned char smgbuff[] = {0xff,0xff,0xff,0xff,0xff,0xff};/*数码管显示缓存*/
unsigned int n = 0;
unsigned char add = 1;
bit keysta1 = 1; //按键1的当前状态
bit keysta2 = 1; //按键2的当前状态
bit keysta3 = 1; //按键3的当前状态
bit keysta4 = 1; //按键4的当前状态
bit p = 0; //计时状态,是否在计时,0否,1是,2暂停
char mode = 1; //时钟模式
unsigned char time[2] = {0,0};//分,秒
unsigned char timeb[2] = {1,1};//分,秒
bit keybackup1 = 1; //按键1备份
bit keybackup2 = 1; //按键2备份
bit keybackup3 = 1; //按键3备份
bit keybackup4 = 1; //按键4备份
unsigned int nb = 1; //第一次显示
void main()
{
unsigned char th = 0,tl = 0;
ENLED=0;
ADDR3=1;
TMOD = 0x11; /*让定时器1,0工作在模式1*/
TH0 = 0x0DC; /*10ms,计时用*/
TL0 = 0x00;
TH1 = 0x0F5; /*3ms,刷新用*/
TL1 = 0x33;
ET0 = 1; /*开定时器1,0的中断*/
ET1 = 1;
EA = 1; /*开总中断*/
TR0 = 0; /*打开定时器*/
TR1 = 1;
TR0 = 1;
keyout = 0; //按键初始化
while(1)
{
switch(mode)
{
case 0:
if(nb != n) //如果备份值不等于n,那么n变了,于是重载缓存
{
nb = n;
if((n/100000) == 0)smgbuff[0] = 0xff;else smgbuff[0] = smg[n/100000];
if((n/10000) == 0)smgbuff[1] = smg[0];else smgbuff[1] = smg[(n/10000)%10];
if((n/1000) == 0)smgbuff[2] = smg[0];else smgbuff[2] = smg[(n/1000)%10];
if((n/100) == 0)smgbuff[3] = smg[0];else smgbuff[3] = smg[(n/100)%10];
if((n/10) == 0)smgbuff[4] = smg[0];else smgbuff[4] = smg[(n/10)%10];
if((n) == 0)smgbuff[5] = smg[0];else smgbuff[5] = smg[n%10];
if(n == 0)smgbuff[4] = smg[0];
smgbuff[4] = smgbuff[4]&0x7f; //加小数点
/*以上是 载数码管缓存代码,大家自己研究这里不多解释*/
}
if(keysta2 != keybackup2) //按键状态发生了变化
{
//if(keybackup1 == 0) //上次是0,这次变化了,说明是抬起按键
{
//这里写抬起按键事件
}
if(keybackup2 == 1)
{
//同理,这里写按下事件
if(p == 0) //如果没有计时则开始计时
{
p = 1;
}
else //否则在计时了就暂停计时
{
//TR0 = 0;
p = 0;
}
}
keybackup2 = keysta2;//备份按键
}
if(keysta1 != keybackup1) //按键2状态发生了变化
{
//if(keybackup2 == 0) //上次是0,这次变化了,说明是抬起按键
{
//这里写抬起按键事件
}
if(keybackup1 == 1)
{
//同理,这里写按下事件
//TR0 = 0; //停止计时
//TH0 = 0x4C; /*重置50ms,计时初值,这里避免到一半继续计时,导致有50ms内误差*/
//TL0 = 0x00;
p = 0; //改状态为停止计时
n = INIT_NUMBER; //n置初值,为什么放到这?考虑没停止定时器置初值后进n-1中断导致n的值为998
}
keybackup1 = keysta1;//备份按键
}
if(keysta4 != keybackup4) //按键3状态发生了变化
{
static char ok = 1;
if(keybackup4 == 1)
{
if(ok){
add = 27; //n置初值,为什么放到这?考虑没停止定时器置初值后进n-1中断导致n的值为998
ok = 0;
}
else
{
add = 1;
ok = 1;
}
}
keybackup4 = keysta4;//备份按键
}
break;
case 1:
smgbuff[1] = 0xff;
//分
if(time[0]!=timeb[0])
{
timeb[0]=time[0];
smgbuff[2] = smg[timeb[0]/10];
smgbuff[3] = smg[timeb[0]%10];
smgbuff[3] = smgbuff[3]&0x7f; //加小数点
}
//秒
if(time[1]!=timeb[1])
{
timeb[1]=time[1];
smgbuff[4] = smg[timeb[1]/10];
smgbuff[5] = smg[timeb[1]%10];
}
break;
}
}
}
void timer() interrupt 1
{
static unsigned char cnt = 0,tim = 0;
TH0 = 0x4C; /*50ms,计时*/
TL0 = 0x00;
cnt++;
if(cnt>=2) //到100ms时,
{
cnt = 0; //清计数
tim++;
if(tim == 10)
{
time[1]++;
tim = 0;
if(time[1]==60)
{
time[0]++;
time[1]=0;
if(time[0]>99)
{
time[0] = 99;
}
}
}
if(p){
n+=add; //n+1
if(n <= 0) //考虑到倒计时结束
{
n = 0; //可不加,严谨考虑
//ET0 = 0;//这个可有可无
//TR0 = 0; //关闭定时器
p = 0; //把状态设置为停止计时
}
if(n>MAX_NUMBER) //最大限度
{
n = MAX_NUMBER;
//TR0 = 0; //关闭定时器
p = 0; //把状态设置为停止计时
}
}
}
}
void display() interrupt 3
{
static unsigned char i = 1;
static keybuff[] = {0xff,0xff,0xff}; //默认按键抬起
TH1 = 0x0F5; /*3ms,刷新数码管,按键扫描*/
TL1 = 0x33;
keybuff[0] = (keybuff[0]<<1)|key14; //按键缓冲右移一位,再把新状态加进来
/*这里实现的是:
按键时序状态:
11111111111111111111111111100101100000000000000000001100101111111111111111111
第一次按键buff:(11111111)抬起
1111111111111111111(11111111)00101100000000000000000001100101111111111111111111
第二次按键buff:(11111110)抬起
11111111111111111111(11111110)0101100000000000000000001100101111111111111111111
第三次按键buff:(11111100)抬起
111111111111111111111(11111100)101100000000000000000001100101111111111111111111
第七次按键buff:(11001011)抬起 这里就是已经消抖了
1111111111111111111111111(11001011)00000000000000000001100101111111111111111111
第十五次按键buff:(00000000)按下
111111111111111111111111111001011(00000000)000000000001100101111111111111111111
具体过程大家可以在草稿纸上演示,还有不懂的可以问
*/
keybuff[1] = (keybuff[1]<<1)|key15; //同上理
keybuff[2] = (keybuff[2]<<1)|key16;
keybuff[3] = (keybuff[3]<<1)|key13;
if((keybuff[2]&0x0F) == 0x00) //连续4个扫描都为0,也就是4*3=12ms的时间内扫描到的都是0,可认为按下了
{
keysta3 = 0;//0按下
}
if((keybuff[2]|0xF0) == 0xFF) //同上理,可认为抬起了
{
keysta3 = 1;//1抬起
}
if((keybuff[3]&0x0F) == 0x00) //连续4个扫描都为0,也就是4*3=12ms的时间内扫描到的都是0,可认为按下了
{
keysta4 = 0;//0按下
}
if((keybuff[3]|0xF0) == 0xFF) //同上理,可认为抬起了
{
keysta4 = 1;//1抬起
}
if((keybuff[0]&0x0F) == 0x00)
{
keysta1 = 0;//0按下
}
/*这里为什么要keybuff[0]&0x0F?
比如按键缓冲是11110000
按位与0x0f(00001111)后是0000000,而11110000是可以认为按下的
再比如缓冲11111000,按位与后是00001000,不是0x00,所以可行
*/
if((keybuff[0]|0xF0) == 0xFF) //同上理,可认为抬起了
{
keysta1 = 1;//1抬起
}
/*-----------------------下面同理对第二个按键处理----------------------*/
if((keybuff[1]&0x0F) == 0x00)
{
keysta2 = 0;//0按下
}
if((keybuff[1]|0xF0) == 0xFF)
{
keysta2 = 1;//1抬起
}
P0=0XFF;
switch(i)
{
case 1:ADDR2=0;ADDR1=0;ADDR0=0;i++;P0=smgbuff[5];break;
case 2:ADDR2=0;ADDR1=0;ADDR0=1;i++;P0=smgbuff[4];break;
case 3:ADDR2=0;ADDR1=1;ADDR0=0;i++;P0=smgbuff[3];break;
case 4:ADDR2=0;ADDR1=1;ADDR0=1;i++;P0=smgbuff[2];break;
case 5:ADDR2=1;ADDR1=0;ADDR0=0;i++;P0=smgbuff[1];break;
case 6:ADDR2=1;ADDR1=0;ADDR0=1;i=1;P0=smgbuff[0];break;
default:i = 1;
}
if(keysta3 != keybackup3) //按键状态发生了变化
{
if(keybackup3 == 1)
{
if(mode == 1)
{
mode = 0;
} else
{
mode = 1;
}
//mode = ~mode; //改变模式
timeb[0] = 255;
timeb[1] = 255;
nb = 65535;
}
keybackup3 = keysta3;//备份按键
}
}
叁、项目效果
视频演示:https://www.bilibili.com/video/BV1sv41117ZX
成功完成以上功能。
喜欢的小伙伴支持一下呗~