1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
| #include <reg52.h>
sbit ADDR0 = P1^0; sbit ADDR1 = P1^1; sbit ADDR2 = P1^2; sbit ADDR3 = P1^3; sbit ENLED = P1^4; sbit KEY_IN_1 = P2^4; sbit KEY_IN_2 = P2^5; sbit KEY_IN_3 = P2^6; sbit KEY_IN_4 = P2^7; sbit KEY_OUT_1 = P2^3; sbit KEY_OUT_2 = P2^2; sbit KEY_OUT_3 = P2^1; sbit KEY_OUT_4 = P2^0;
unsigned char code LedChar[] = { //数码管显示字符转换表 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E }; unsigned char LedBuff[6] = { //数码管显示缓冲区 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的映射表 { 0x31, 0x32, 0x33, 0x26 }, //数字键1、数字键2、数字键3、向上键 { 0x34, 0x35, 0x36, 0x25 }, //数字键4、数字键5、数字键6、向左键 { 0x37, 0x38, 0x39, 0x28 }, //数字键7、数字键8、数字键9、向下键 { 0x30, 0x1B, 0x0D, 0x27 } //数字键0、ESC键、 回车键、 向右键 }; unsigned char KeySta[4][4] = { //全部矩阵按键的当前状态 {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1} };
void KeyDriver();
void main() { EA = 1; //使能总中断 ENLED = 0; //选择数码管进行显示 ADDR3 = 1; TMOD = 0x01; //设置T0为模式1 TH0 = 0xFC; //为T0赋初值0xFC67,定时1ms TL0 = 0x67; ET0 = 1; //使能T0中断 TR0 = 1; //启动T0 LedBuff[0] = LedChar[0]; //上电显示0 while (1) { KeyDriver(); //调用按键驱动函数 } } /\* 将一个有符号长整型的数字显示到数码管上,num-待显示数字 \*/ void ShowNumber(long num) { signed char i; unsigned char sign; unsigned char buf[6]; if (num < 0) //首先提取并暂存符号位 { sign = 1; num = -num; } else { sign = 0; } for (i=0; i<6; i++) //把长整型数转换为6位十进制的数组 { buf[i] = num % 10; num = num / 10; } for (i=5; i>=1; i--) //从最高位起,遇到0转换为空格,遇到非0则退出循环 { if (buf[i] == 0) LedBuff[i] = 0xFF; else break; } if (sign != 0) //负数时,需在最前面添加负号 { if (i < 5) //当有效位数小于6位时添加负号,否则显示结果将是错的 { LedBuff[i+1] = 0xBF; } } for ( ; i>=0; i--) //剩余低位都如实转换为数码管显示字符 { LedBuff[i] = LedChar[buf[i]]; } } /\* 按键动作函数,根据键码执行相应的操作,keycode-按键键码 \*/ void KeyAction(unsigned char keycode) { static char oprt = 0; //用于保存加减运算符 static long result = 0; //用于保存运算结果 static long addend = 0; //用于保存输入的加数 if ((keycode>=0x30) && (keycode<=0x39)) //输入0-9的数字 { addend = (addend\*10)+(keycode-0x30); //整体十进制左移,新数字进入个位 ShowNumber(addend); //运算结果显示到数码管 } else if (keycode == 0x26) //向上键用作加号 { oprt = 0; //设置运算符变量 result = addend; //运算数存到结果中,准备进行加减 addend = 0; //清零运算数,准备接收下一个运算数 ShowNumber(addend); //刷新数码管显示 } else if (keycode == 0x28) //向下键用作减号 { oprt = 1; //设置运算符变量 result = addend; //运算数存到结果中,准备进行加减 addend = 0; //清零运算数,准备接收下一个运算数 ShowNumber(addend); //刷新数码管显示 } else if (keycode == 0x0D) //回车键,执行加减运算 { if (oprt == 0) { result += addend; //进行加法运算 } else { result -= addend; //进行减法运算 } addend = 0; ShowNumber(result); //运算结果显示到数码管 } else if (keycode == 0x1B) //Esc键,清零结果 { addend = 0; result = 0; ShowNumber(addend); //清零后的加数显示到数码管 } } /\* 按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用 \*/ void KeyDriver() { unsigned char i, j; static unsigned char backup[4][4] = { //按键值备份,保存前一次的值 {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1} }; for (i=0; i<4; i++) //循环检测4\*4的矩阵按键 { for (j=0; j<4; j++) { if (backup[i][j] != KeySta[i][j]) //检测按键动作 { if (backup[i][j] != 0) //按键按下时执行动作 { KeyAction(KeyCodeMap[i][j]); //调用按键动作函数 } backup[i][j] = KeySta[i][j]; //刷新前一次的备份值 } } } } /\* 按键扫描函数,需在定时中断中调用,推荐调用间隔1ms \*/ void KeyScan() { unsigned char i; static unsigned char keyout = 0; //矩阵按键扫描输出索引 static unsigned char keybuf[4][4] = { //矩阵按键扫描缓冲区 {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF} };
//将一行的4个按键值移入缓冲区 keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1; keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2; keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3; keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4; //消抖后更新按键状态 for (i=0; i<4; i++) //每行4个按键,所以循环4次 { if ((keybuf[keyout][i] & 0x0F) == 0x00) { //连续4次扫描值为0,即4\*4ms内都是按下状态时,可认为按键已稳定的按下 KeySta[keyout][i] = 0; } else if ((keybuf[keyout][i] & 0x0F) == 0x0F) { //连续4次扫描值为1,即4\*4ms内都是弹起状态时,可认为按键已稳定的弹起 KeySta[keyout][i] = 1; } } //执行下一次的扫描输出 keyout++; //输出索引递增 keyout = keyout & 0x03; //索引值加到4即归零 switch (keyout) //根据索引,释放当前输出引脚,拉低下次的输出引脚 { case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break; case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break; case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break; case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break; default: break; } } /\* 数码管动态扫描刷新函数,需在定时中断中调用 \*/ void LedScan() { static unsigned char i = 0; //动态扫描的索引 P0 = 0xFF; //显示消隐 switch (i) { case 0: ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0]; break; case 1: ADDR2=0; ADDR1=0; ADDR0=1; i++; P0=LedBuff[1]; break; case 2: ADDR2=0; ADDR1=1; ADDR0=0; i++; P0=LedBuff[2]; break; case 3: ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=LedBuff[3]; break; case 4: ADDR2=1; ADDR1=0; ADDR0=0; i++; P0=LedBuff[4]; break; case 5: ADDR2=1; ADDR1=0; ADDR0=1; i=0; P0=LedBuff[5]; break; default: break; } } /\* T0中断服务函数,用于数码管显示扫描与按键扫描 \*/ void InterruptTimer0() interrupt 1 { TH0 = 0xFC; //重新加载初值 TL0 = 0x67; LedScan(); //调用数码管显示扫描函数 KeyScan(); //调用按键扫描函数 }
|