STC89C52RC是STC公司生产的一种低功耗、高性能CMOS8位微控制器,具有8K字节系统可编程Flash存储器。STC89C52使用经典的MCS-51内核,但是做了很多的改进使得芯片具有传统51单片机不具备的功能。在单芯片上,拥有灵巧的8 位CPU 和在系统可编程Flash,使得STC89C52为众多嵌入式控制应用系统提供高灵活、超有效的解决方案。
stc52单片机键盘原理图
说明:
1. 独立键盘部分
S2~S5为4个独立键盘, 与单片机的P3.4~P3.7分别相连
2. 矩阵键盘部分
S6~S21为16个矩阵键盘,8条线分别与单片机的P3口相连, 其中矩阵键盘的4行分别与单片机的P3.0~P3.3相连,矩阵键盘的4列分别与单片机的P3.4~P3.7相连。
程序设计详解
1. 用数码管的前两位显示一个十进制数,变化范围为00-59.
程序功能:
用数码管的前两位显示一个十进制数,变化范围为00-59,开始时显示00,每按下S2键一次,数值加1;每按下S3键一次,数值减1;每按下S4键一次,数值归零;按下S5键一次,利用定时器功能使数值开始每秒加1,再次按下S5键,数值停止自动加1,保持原数。
程序源码:
#include
#define uchar unsigned char
#define uint unsigned int
sbit key1 = P3 ^ 4;
sbit key2 = P3 ^ 5;
sbit key3 = P3 ^ 6;
sbit key4 = P3 ^ 7;
sbit dula = P2 ^ 6;
sbit wela = P2 ^ 7;
uchar code table[] = {
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71
};
void init();
void keyscan();
void display(uchar);
void delayms(uint);
uchar t0, num;
void main()
{
init();
while (1)
{
keyscan();
display(num);
}
}
void init()
{
TMOD = 0x01;
TH0 = (65536 - 45872) / 256;
TL0 = (65536 - 45872) % 256;
EA = 1;
ET0 = 1;
}
void keyscan()
{
if (key1 == 0)
{
delayms(10);
if (key1 == 0)
{
if (num == 60)
num = 0;
num++;
while (!key1)
display(num);
}
}
if (key2 == 0)
{
delayms(10);
if (key2 == 0)
{
if (num == 0)
num = 60;
num--;
while (!key2)
display(num);
}
}
if (key3 == 0)
{
delayms(10);
if (key3 == 0)
{
num = 0;
while (!key3)
display(num);
}
}
if (key4 == 0)
{
delayms(10);
if (key4 == 0)
{
TR0 = ~TR0;
while (!key4)
display(num);
}
}
}
void display(uchar numDis)
{
dula = 1;
P0 = table[numDis / 10];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0xfe;
wela = 0;
delayms(5);
dula = 1;
P0 = table[numDis % 10];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0xfd;
wela = 0;
delayms(5);
}
void delayms(uint xms)
{
uint i, j;
for (i = xms; i 》 0; i--)
for (j = 110; j 》 0; j--)
;
}
void T0_timer() interrupt 1
{
TH0 = (65536 - 45872) / 256;
TL0 = (65536 - 45872) % 256;
t0++;
if (t0 == 20)
{
t0 = 0;
num++;
if (num == 60)
num = 0;
}
}
程序小结:
a. 键盘按键检测需要做两次 (每个键盘按键用了两个if)
b. 键盘按键退出也需要检测 (每次按键退出用了一个while(!key))
c. 这段程序包含按键加1,按键减1,按键归零,按键开始计数,再按停止计数
2. 按矩阵键盘,在数码管上显示0~F,6个数码管同时静态显示即可。
程序功能
上电不显示, 按矩阵键盘,在数码管上显示0~F,6个数码管同时静态显示即可。
程序源码
#include
#define uchar unsigned char
#define uint unsigned int
sbit dula = P2 ^ 6;
sbit wela = P2 ^ 7;
uchar code table[] = {
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71
};
void delayms(uint);
void display(uchar);
void matrixkeyscan();
void main()
{
dula = 1;
P0 = 0;
dula = 0;
wela = 1;
P0 = 0xc0;
wela = 0;
while (1)
{
matrixkeyscan();
}
}
void delayms(uint xms)
{
uint i, j;
for (i = xms; i 》 0; i--)
for (j = 110; j 》 0; j--)
;
}
void display(uchar num)
{
dula = 1;
P0 = table[num];
dula = 0;
}
void matrixkeyscan()
{
uchar temp, key;
P3 = 0xfe;
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
delayms(10);
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
temp = P3;
switch(temp)
{
case 0xee:
key = 0;
break;
case 0xde:
key = 1;
break;
case 0xbe:
key = 2;
break;
case 0x7e:
key = 3;
break;
}
while (temp != 0xf0)
{
temp = P3;
temp = temp & 0xf0;
}
display(key);
}
}
P3 = 0xfd;
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
delayms(10);
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
temp = P3;
switch(temp)
{
case 0xed:
key = 4;
break;
case 0xdd:
key = 5;
break;
case 0xbd:
key = 6;
break;
case 0x7d:
key = 7;
break;
}
while (temp != 0xf0)
{
temp = P3;
temp = temp & 0xf0;
}
display(key);
}
}
P3 = 0xfb;
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
delayms(10);
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
temp = P3;
switch(temp)
{
case 0xeb:
key = 8;
break;
case 0xdb:
key = 9;
break;
case 0xbb:
key = 10;
break;
case 0x7b:
key = 11;
break;
}
while (temp != 0xf0)
{
temp = P3;
temp = temp & 0xf0;
}
display(key);
}
}
P3 = 0xf7;
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
delayms(10);
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
temp = P3;
switch(temp)
{
case 0xe7:
key = 12;
break;
case 0xd7:
key = 13;
break;
case 0xb7:
key = 14;
break;
case 0x77:
key = 15;
break;
}
while (temp != 0xf0)
{
temp = P3;
temp = temp & 0xf0;
}
display(key);
}
}
}
程序小结
a. 先送行线低电平,检测列线信号
b. 通过延时来消抖
c. 需要检查释放
3. 数码管前三位显示一个跑表,从000到999之间以1%秒速度运行
程序功能
当按下一个独立键盘时跑表停止,松开手后跑表继续运行。用定时器设计表。
程序源码
#include
#define uint unsigned int
#define uchar unsigned char
sbit dula = P2 ^ 6;
sbit wela = P2 ^ 7;
sbit s2 = P3 ^ 4;
uchar code table[] = {
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71,
0x76,0x79,0x38,0x3f,0
};
uchar flag, t0, bai, shi, ge;
uint shu;
void init();
void display(uchar aa, uchar bb, uchar cc);
void delayms(uint);
void main()
{
init();
while (1)
{
display(bai, shi, ge);
if (s2 == 0)
{
delayms(10);
if (s2 == 0)
{
TR0 = 0;
while (!s2)
display(bai, shi, ge);
TR0 = 1;
}
}
}
}
void init()
{
TMOD = 0x01;
TH0 = (65536 - 10000) / 256;
TL0 = (65536 - 10000) % 256;
EA = 1;
ET0 = 1;
TR0 = 1;
}
void T0_timer() interrupt 1
{
TH0 = (65536 - 10000) / 256;
TL0 = (65536 - 10000) % 256;
t0++;
if (t0 == 1)
{
t0 = 0;
shu++;
if (shu == 1000)
shu = 0;
bai = shu / 100;
shi = shu % 100 / 10;
ge = shu % 10;
}
}
void display(uchar aa, uchar bb, uchar cc)
{
dula = 1;
P0 = table[aa];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0xfe;
wela = 0;
delayms(1);
dula = 1;
P0 = table[bb];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0xfd;
wela = 0;
delayms(1);
dula = 1;
P0 = table[cc];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0xfb;
wela = 0;
delayms(1);
}
void delayms(uint xms)
{
uint i, j;
for (i = xms; i 》 0; i--)
for (j = 110; j 》 0; j--)
;
}
程序小结
a. 松开检测 while (!s2)
b. 1%秒速度运行: 选择定时基数为1000(即10ms), 定时倍数为1,相乘为10ms
说明: 由于间隔时间太短,所以这里出现的问题是低位数据显示看不清楚,可以选用定时倍数为10 (但定时数变成了100ms, 可能与题意不符)
4. 数码管前三位显示一个跑表,从000到999之间以1%秒速度运行
程序功能
当按下第一个独立键盘时跑表停止,松开手后跑表继续运行。
当按下第二个独立键盘时计时停止,
当按下第三个独立键盘时计时开始,
当按下第三个独立键盘时计数值清零从头开始
程序源码
#include
#define uint unsigned int
#define uchar unsigned char
sbit dula = P2 ^ 6;
sbit wela = P2 ^ 7;
sbit s2 = P3 ^ 4;
sbit s3 = P3 ^ 5;
sbit s4 = P3 ^ 6;
sbit s5 = P3 ^ 7;
uchar code table[] = {
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71,
0x76,0x79,0x38,0x3f,0
};
uchar flag, t0;
uint shu;
void init();
void display(uint num);
void delayms(uint);
void keyscan();
void main()
{
init();
while (1)
{
display(shu);
keyscan();
}
}
void init()
{
TMOD = 0x01;
TH0 = (65536 - 10000) / 256;
TL0 = (65536 - 10000) % 256;
EA = 1;
ET0 = 1;
TR0 = 1;
}
void keyscan()
{
if (s2 == 0)
{
delayms(10);
if (s2 == 0)
{
TR0 = 0;
while (!s2)
display(shu);
TR0 = 1;
}
}
if (s3 == 0)
{
delayms(10);
if (s3 == 0)
{
TR0 = 0;
while (!s3)
display(shu);
}
}
if (s4 == 0)
{
delayms(10);
if (s4 == 0)
{
TR0 = 1;
while (!s4)
display(shu);
}
}
if (s5 == 0)
{
delayms(10);
if (s5 == 0)
{
shu = 0;
while (!s5)
display(shu);
}
}
}
void T0_timer() interrupt 1
{
TH0 = (65536 - 10000) / 256;
TL0 = (65536 - 10000) % 256;
t0++;
if (t0 == 1)
{
t0 = 0;
shu++;
if (shu == 1000)
shu = 0;
}
}
void display(uint num)
{
dula = 1;
P0 = table[num / 100];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0xfe;
wela = 0;
delayms(1);
dula = 1;
P0 = table[num % 100 / 10];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0xfd;
wela = 0;
delayms(1);
dula = 1;
P0 = table[num % 100 % 10];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0xfb;
wela = 0;
delayms(1);
}
void delayms(uint xms)
{
uint i, j;
for (i = xms; i 》 0; i--)
for (j = 110; j 》 0; j--)
;
}
程序小结
1. 按键退出检测时需要加上display(shu), 否则数码管显示前两位为空白
2. 按键退出检测与执行动作的顺序问题
1) 如果要求一按键马上执行动作, 应该时先执行动作,再做按键退出检测
即如:
shu = 0;
while (!s5)
display(shu);
2) 如果要求按键退出后再执行动作, 应该时先检查按键退出,再执行动作
即如:
while (!s5)
display(shu);
shu = 0;
5. 按下16个矩阵键盘,依次在前三个数码管上显示1~16的平方
程序功能
按下16个矩阵键盘,依次在前三个数码管上显示1~16的平方,即按下第一个显示1,按下第二个显示4,…按下第16个显示16*16 (256)
程序源码
#include
#define uchar unsigned char
#define uint unsigned int
sbit dula = P2 ^ 6;
sbit wela = P2 ^ 7;
uchar code table[] = {
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71
};
void delayms(uint);
void display(uint);
void matrixkeyscan();
void main()
{
while (1)
{
matrixkeyscan();
}
}
void delayms(uint xms)
{
uint i, j;
for (i = xms; i 》 0; i--)
for (j = 110; j 》 0; j--)
;
}
void display(uint num)
{
dula = 1;
P0 = table[num / 100];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0xfe;
wela = 0;
delayms(1);
dula = 1;
P0 = table[num % 100 / 10];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0xfd;
wela = 0;
delayms(1);
dula = 1;
P0 = table[num % 100 % 10];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0xfb;
wela = 0;
delayms(1);
}
void matrixkeyscan()
{
uchar temp, key;
P3 = 0xfe;
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
delayms(10);
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
temp = P3;
switch(temp)
{
case 0xee:
key = 1;
break;
case 0xde:
key = 2;
break;
case 0xbe:
key = 3;
break;
case 0x7e:
key = 4;
break;
}
while (temp != 0xf0)
{
temp = P3;
temp = temp & 0xf0;
}
}
}
P3 = 0xfd;
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
delayms(10);
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
temp = P3;
switch(temp)
{
case 0xed:
key = 5;
break;
case 0xdd:
key = 6;
break;
case 0xbd:
key = 7;
break;
case 0x7d:
key = 8;
break;
}
while (temp != 0xf0)
{
temp = P3;
temp = temp & 0xf0;
}
}
}
P3 = 0xfb;
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
delayms(10);
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
temp = P3;
switch(temp)
{
case 0xeb:
key = 9;
break;
case 0xdb:
key = 10;
break;
case 0xbb:
key = 11;
break;
case 0x7b:
key = 12;
break;
}
while (temp != 0xf0)
{
temp = P3;
temp = temp & 0xf0;
}
}
}
P3 = 0xf7;
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
delayms(10);
temp = P3;
temp = temp & 0xf0;
if (temp != 0xf0)
{
temp = P3;
switch(temp)
{
case 0xe7:
key = 13;
break;
case 0xd7:
key = 14;
break;
case 0xb7:
key = 15;
break;
case 0x77:
key = 16;
break;
}
while (temp != 0xf0)
{
temp = P3;
temp = temp & 0xf0;
}
}
}
display(key * key);
}
程序小结
1. display(key * key)不能马上放到每一个按键退出检测后,如果那样做,数码管上只会显示最后一位数字(即个位)
换句话说,
程序不能这样:
while (temp != 0xf0)
{
temp = P3;
temp = temp & 0xf0;
}
display(key*key);
而是应该放到程序最后结束处