請各位大神看一下我的仿真電路應該沒問題吧 仿真的這個計算器按鍵不起作用 請各位大神幫我分析一下原因
E80TDANI[9QLKA(2G8EE.png (27.88 KB, 下載次數: 61)
下載附件
2017-8-16 13:48 上傳
- #include <reg51.h>
- #define LCD1602_DB P0
- sbit LCD1602_RS = P1^0;
- sbit LCD1602_RW = P1^1;
- sbit LCD1602_E = P1^5;
- void LcdWaitReady() //等待液晶準備好
- {
- unsigned char sta;
- LCD1602_DB = 0xFF;
- LCD1602_RS = 0;
- LCD1602_RW = 1;
- do
- {
- LCD1602_E = 1;
- sta = LCD1602_DB; //讀取狀態字
- LCD1602_E = 0;
- } while (sta & 0x80); //bit7等于1表示液晶正忙,重復檢測直到其等于0為止
- }
- void LcdWriteCmd(unsigned char cmd) //寫入命令函數
- {
- LcdWaitReady();
- LCD1602_RS = 0;
- LCD1602_RW = 0;
- LCD1602_DB = cmd;
- LCD1602_E = 1;
- LCD1602_E = 0;
- }
- void LcdWriteDat(unsigned char dat) //寫入數據函數
- {
- LcdWaitReady();
- LCD1602_RS = 1;
- LCD1602_RW = 0;
- LCD1602_DB = dat;
- LCD1602_E = 1;
- LCD1602_E = 0;
- }
- void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str) //顯示字符串,屏幕起始坐標(x,y),字符串指針str
- {
- unsigned char addr;
- //由輸入的顯示坐標計算顯示RAM的地址
- if (y == 0)
- addr = 0x00 + x; //第一行字符地址從0x00起始
- else
- addr = 0x40 + x; //第二行字符地址從0x40起始
- //由起始顯示RAM地址連續寫入字符串
- LcdWriteCmd(addr | 0x80); //寫入起始地址
- while (*str != '\0') //連續寫入字符串數據,直到檢測到結束符
- {
- LcdWriteDat(*str);
- str++;
- }
- }
- void LcdAreaClear(unsigned char x, unsigned char y, unsigned char len) //區域清除,清除從(x,y)坐標起始的len個字符位
- {
- unsigned char addr;
- //由輸入的顯示坐標計算顯示RAM的地址
- if (y == 0)
- addr = 0x00 + x; //第一行字符地址從0x00起始
- else
- addr = 0x40 + x; //第二行字符地址從0x40起始
- //由起始顯示RAM地址連續寫入字符串
- LcdWriteCmd(addr | 0x80); //寫入起始地址
- while (len--) //連續寫入空格
- {
- LcdWriteDat(' ');
- }
- }
- void LcdFullClear()
- {
- LcdWriteCmd(0x01); //清屏
- }
- void LcdInit() //液晶初始化函數
- {
- LcdWriteCmd(0x38); //16*2顯示,5*7點陣,8位數據接口
- LcdWriteCmd(0x0C); //顯示器開,光標關閉
- LcdWriteCmd(0x06); //文字不動,地址自動+1
- LcdWriteCmd(0x01); //清屏
- LcdShowStr(15, 1, "0");
- }
- /***********************keyboard.c文件程序源代碼************************/
- #include <reg51.h>
- sbit KEY_IN_1 = P2^4; //矩陣按鍵的掃描輸入引腳1
- sbit KEY_IN_2 = P2^5; //矩陣按鍵的掃描輸入引腳2
- sbit KEY_IN_3 = P2^6; //矩陣按鍵的掃描輸入引腳3
- sbit KEY_IN_4 = P2^7; //矩陣按鍵的掃描輸入引腳4
- sbit KEY_OUT_1 = P2^3; //矩陣按鍵的掃描輸出引腳1
- sbit KEY_OUT_2 = P2^2; //矩陣按鍵的掃描輸出引腳2
- sbit KEY_OUT_3 = P2^1; //矩陣按鍵的掃描輸出引腳3
- sbit KEY_OUT_4 = P2^0; //矩陣按鍵的掃描輸出引腳4
- const unsigned char code KeyCodeMap[4][4] = { //矩陣按鍵編號到PC標準鍵盤鍵碼的映射表
- { '1', '2', '3', 0x26 }, //數字鍵1、數字鍵2、數字鍵3、向上鍵
- { '4', '5', '6', 0x25 }, //數字鍵4、數字鍵5、數字鍵6、向左鍵
- { '7', '8', '9', 0x28 }, //數字鍵7、數字鍵8、數字鍵9、向下鍵
- { '0', 0x1B, 0x0D, 0x27 } //數字鍵0、ESC鍵、 回車鍵、 向右鍵
- };
- unsigned char pdata KeySta[4][4] = { //全部矩陣按鍵的當前狀態
- {1, 1, 1, 1},
- {1, 1, 1, 1},
- {1, 1, 1, 1},
- {1, 1, 1, 1}
- };
- unsigned char step = 0; //操作步驟
- unsigned char oprt = 0; //運算類型
- signed long num1 = 0; //操作數1
- signed long num2 = 0; //操作數2
- signed long result = 0; //運算結果
- extern void LcdFullClear();
- extern void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str);
- extern void LcdAreaClear(unsigned char x, unsigned char y, unsigned char len);
- unsigned char NumToString(unsigned char *str, signed long num) //整型數轉換為字符串,字符串指針str,待轉換數num,返回值為字符串長度
- {
- unsigned char i, len;
- unsigned char buf[12];
- if (num < 0) //如果為負數,則首先輸出符號到指針上,并取其絕對值
- {
- *str = '-';
- str++;
- num = -num;
- }
- i = 0; //先轉換為低位在前的十進制數組
- do {
- buf[ i] = num % 10;
- num /= 10;
- i++;
- } while (num > 0);
- len = i; //i最后的值就是有效字符的個數
- while (i > 0) //然后將數組值轉換為ASCII碼反向拷貝到接收指針上
- {
- i--;
- *str = buf[ i] + '0';
- str++;
- }
- return len; //返回轉換后的字符串長度
- }
- void ShowOprt(unsigned char y, unsigned char type) //顯示運算符,顯示位置y,運算符類型type
- {
- switch (type)
- {
- case 0: LcdShowStr(0, y, "+"); break;
- case 1: LcdShowStr(0, y, "-"); break;
- case 2: LcdShowStr(0, y, "*"); break;
- case 3: LcdShowStr(0, y, "/"); break;
- default: break;
- }
- }
- void Reset() //計算器復位函數
- {
- num1 = 0;
- num2 = 0;
- step = 0;
- LcdFullClear();
- }
- void NumKeyAction(unsigned char n) //數字鍵動作函數,按鍵輸入的數值n
- {
- unsigned char len;
- unsigned char str[12];
- if (step > 1) //如計算已完成,則重新開始新的計算
- {
- Reset();
- }
- if (step == 0) //輸入第一操作數
- {
- num1 = num1*10 + n; //輸入數值累加到原操作數上
- len = NumToString(str, num1); //新數值轉換為字符串
- LcdShowStr(16-len, 1, str); //顯示到液晶第二行上
- }
- else //輸入第二操作數
- {
- num2 = num2*10 + n;
- len = NumToString(str, num2);
- LcdShowStr(16-len, 1, str);
- }
- }
- void OprtKeyAction(unsigned char type) //運算符按鍵動作函數,運算符類型type
- {
- unsigned char len;
- unsigned char str[12];
- if (step == 0) //第二操作數尚未輸入時響應,即不支持連續操作
- {
- len = NumToString(str, num1); //第一操作數轉換為字符串
- LcdAreaClear(0, 0, 16-len); //清除第一行左邊的字符位
- LcdShowStr(16-len, 0, str); //字符串靠右顯示在第一行
- ShowOprt(1, type); //在第二行顯示操作符
- LcdAreaClear(1, 1, 14); //清除第二行中間的字符位
- LcdShowStr(15, 1, "0"); //在第二行最右端顯示0
- oprt = type; //記錄操作類型
- step = 1;
- }
- }
- void GetResult() //計算結果
- {
- unsigned char len;
- unsigned char str[12];
- if (step == 1) //第二操作數已輸入時才執行計算
- {
- step = 2;
- switch (oprt) //根據運算符類型計算結果,未考慮溢出問題
- {
- case 0: result = num1 + num2; break;
- case 1: result = num1 - num2; break;
- case 2: result = num1 * num2; break;
- case 3: result = num1 / num2; break;
- default: break;
- }
- len = NumToString(str, num2); //原第二操作數和運算符顯示在第一行
- ShowOprt(0, oprt);
- LcdAreaClear(1, 0, 16-1-len);
- LcdShowStr(16-len, 0, str);
- len = NumToString(str, result); //計算結果和等號顯示在第二行
- LcdShowStr(0, 1, "=");
- LcdAreaClear(1, 1, 16-1-len);
- LcdShowStr(16-len, 1, str);
- }
- }
- void KeyAction(unsigned char keycode) //按鍵動作函數,根據鍵碼執行相應動作
- {
- if ((keycode>='0') && (keycode<='9')) //顯示輸入的字符
- {
- NumKeyAction(keycode - '0');
- }
- else if (keycode == 0x26) //向上鍵,+
- {
- OprtKeyAction(0);
- }
- else if (keycode == 0x28) //向下鍵,-
- {
- OprtKeyAction(1);
- }
- else if (keycode == 0x25) //向左鍵,*
- {
- OprtKeyAction(2);
- }
- else if (keycode == 0x27) //向右鍵,÷
- {
- OprtKeyAction(3);
- }
- else if (keycode == 0x0D) //回車鍵,計算結果
- {
- GetResult();
- }
- else if (keycode == 0x1B) //Esc鍵,清除
- {
- Reset();
- LcdShowStr(15, 1, "0");
- }
- }
- void KeyDrive() //按鍵動作驅動函數
- {
- unsigned char i, j;
- static unsigned char pdata 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];
- }
- }
- }
- }
- 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,即16ms(4*4ms)內都只檢測到按下狀態時,可認為按鍵已按下
- KeySta[keyout][ i] = 0;
- }
- else if ((keybuf[keyout][ i] & 0x0F) == 0x0F)
- { //連續4次掃描值為1,即16ms(4*4ms)內都只檢測到彈起狀態時,可認為按鍵已彈起
- KeySta[keyout][ i] = 1;
- }
- }
- //執行下一次的掃描輸出
- keyout++;
- keyout &= 0x03;
- 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;
- }
- }
- /*************************main.c文件程序源代碼**************************/
- #include <reg51.h>
- void ConfigTimer0(unsigned int ms);
- extern void KeyScan();
- extern void KeyDrive();
- extern void LcdInit();
- unsigned char T0RH = 0; //T0重載值的高字節
- unsigned char T0RL = 0; //T0重載值的低字節
- void main(void)
- {
- EA = 1; //開總中斷
- ConfigTimer0(1); //配置T0定時1ms
- LcdInit(); //初始化液晶
- while(1)
- {
- KeyDrive();
- }
- }
- void ConfigTimer0(unsigned int ms) //T0配置函數
- {
- unsigned long tmp;
- tmp = 11059200 / 12; //定時器計數頻率
- tmp = (tmp * ms) / 1000; //計算所需的計數值
- tmp = 65536 - tmp; //計算定時器重載值
- tmp = tmp + 18; //修正中斷響應延時造成的誤差
- T0RH = (unsigned char)(tmp >> 8); //定時器重載值拆分為高低字節
- T0RL = (unsigned char)tmp;
- TMOD &= 0xF0; //清零T0的控制位
- TMOD |= 0x01; //配置T0為模式1
- TH0 = T0RH; //加載T0重載值
- TL0 = T0RL;
- ET0 = 1; //使能T0中斷
- TR0 = 1; //啟動T0
- }
- void InterruptTimer0() interrupt 1 //T0中斷服務函數
- {
- TH0 = T0RH; //定時器重新加載重載值
- TL0 = T0RL;
- KeyScan(); //按鍵掃描
- }
復制代碼
|