基於金沙灘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
成功完成以上功能。
喜歡的小伙伴支持一下唄~