基于51单片机的金沙滩12864的计算器
生活随笔
收集整理的這篇文章主要介紹了
基于51单片机的金沙滩12864的计算器
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
很久以前就想把LCD1602的計(jì)算器換成LCD12864,
目前邏輯是弄出來(lái)了,顯示出了點(diǎn)小問(wèn)題,往后再看看顯示的問(wèn)題
第一個(gè)文件:config.h
第二個(gè)文件keyboard.c
#include <reg52.h>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 KeyCodeMap[4][4] = { //矩陣按鍵編號(hào)到標(biāo)準(zhǔn)鍵盤鍵碼的映射表{ '1', '2', '3', 0x26 }, //數(shù)字鍵1、數(shù)字鍵2、數(shù)字鍵3、向上鍵{ '4', '5', '6', 0x25 }, //數(shù)字鍵4、數(shù)字鍵5、數(shù)字鍵6、向左鍵{ '7', '8', '9', 0x28 }, //數(shù)字鍵7、數(shù)字鍵8、數(shù)字鍵9、向下鍵{ '0', 0x1B, 0x0D, 0x27 } //數(shù)字鍵0、ESC鍵、 回車鍵、 向右鍵 };unsigned char pdata KeySta[4][4] = { //全部矩陣按鍵的當(dāng)前狀態(tài){1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1} }; extern void Reset(); extern void GetResult(); extern void NumKeyAction(unsigned char n); extern void OprtKeyAction(unsigned char type); extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *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){Reset();LcdShowStr(15, 1, "0");} } /* 按鍵驅(qū)動(dòng)函數(shù),檢測(cè)按鍵動(dòng)作,調(diào)度相應(yīng)動(dòng)作函數(shù),需在主循環(huán)中調(diào)用 */ void KeyDriver() {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++) //循環(huán)檢測(cè)4*4的矩陣按鍵{for (j=0; j<4; j++){if (backup[i][j] != KeySta[i][j]) //檢測(cè)按鍵動(dòng)作{if (backup[i][j] != 0) //按鍵按下時(shí)執(zhí)行動(dòng)作{KeyAction(KeyCodeMap[i][j]); //調(diào)用按鍵動(dòng)作函數(shù)}backup[i][j] = KeySta[i][j]; //刷新前一次的備份值}}} }/* 按鍵掃描函數(shù),需在定時(shí)中斷中調(diào)用,推薦調(diào)用間隔1ms */ void KeyScan() {unsigned char i;static unsigned char keyout = 0; //矩陣按鍵掃描輸出索引static unsigned char keybuf[4][4] = { //矩陣按鍵掃描緩沖區(qū){0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF},{0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}};//將一行的4個(gè)按鍵值移入緩沖區(qū)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;//消抖后更新按鍵狀態(tài)for (i=0; i<4; i++) //每行4個(gè)按鍵,所以循環(huán)4次{if ((keybuf[keyout][i] & 0x0F) == 0x00){ //連續(xù)4次掃描值為0,即4*4ms內(nèi)都是按下狀態(tài)時(shí),可認(rèn)為按鍵已穩(wěn)定的按下KeySta[keyout][i] = 0;}else if ((keybuf[keyout][i] & 0x0F) == 0x0F){ //連續(xù)4次掃描值為1,即4*4ms內(nèi)都是彈起狀態(tài)時(shí),可認(rèn)為按鍵已穩(wěn)定的彈起KeySta[keyout][i] = 1;}}//執(zhí)行下一次的掃描輸出keyout++; //輸出索引遞增keyout &= 0x03; //索引值加到4即歸零switch (keyout) //根據(jù)索引,釋放當(dāng)前輸出引腳,拉低下次的輸出引腳{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;} }第三個(gè)文件LCD12864.c
/* ******************************************************************************* * 《手把手教你學(xué)51單片機(jī)(C語(yǔ)言版)》 * 配套 KST-51 單片機(jī)開發(fā)板 示例源代碼 * * (c) 版權(quán)所有 2014 金沙灘工作室/清華大學(xué)出版社 保留所有權(quán)利 * 獲取更多資料請(qǐng)?jiān)L問(wèn):http://www.kingst.org * * 文件名:Lcd12864.c * 描 述:12864點(diǎn)陣液晶驅(qū)動(dòng)模塊 * 版本號(hào):v1.0.0 * 備 注:適用于KST-51開發(fā)板配套的12864液晶模塊 ******************************************************************************* */#define _LCD_12864_C #include "config.h" #include "Lcd12864.h" /* 等待液晶準(zhǔn)備好 */ void LcdWaitReady() {uint8 sta;LCD12864_DB = 0xFF;LCD12864_RS = 0;LCD12864_RW = 1;do {LCD12864_E = 1;sta = LCD12864_DB;LCD12864_E = 0;} while (sta & 0x80); //bit7等于1表示液晶正忙,重復(fù)檢測(cè)直到其等于0為止 }/* 向液晶寫入一字節(jié)命令,cmd-待寫入命令值 */ void LcdWriteCmd(uint8 cmd) {LcdWaitReady();LCD12864_RS = 0;LCD12864_RW = 0;LCD12864_DB = cmd;LCD12864_E = 1;LCD12864_E = 0; } /* 向液晶寫入一字節(jié)數(shù)據(jù),dat-待寫入數(shù)據(jù)值 */ void LcdWriteDat(uint8 dat) {LcdWaitReady();LCD12864_RS = 1;LCD12864_RW = 0;LCD12864_DB = dat;LCD12864_E = 1;LCD12864_E = 0; }/* 設(shè)置顯示RAM起始地址,亦即光標(biāo)位置,(x,y)-對(duì)應(yīng)屏幕上的字符坐標(biāo) */ void LcdSetCursor(uint8 x, uint8 y) {unsigned char addr;if (y == 0) //由輸入的屏幕坐標(biāo)計(jì)算顯示RAM的地址addr = 0x80 + x; //第一行字符地址從0x00起始else if (y == 1)addr = 0x90 + x; //第二行字符地址從0x40起始else if (y == 2)addr = 0x88 + x; //第二行字符地址從0x40起始else if (y == 3)addr = 0x98 + x; //第二行字符地址從0x40起始LcdWriteCmd(addr | 0x80); //設(shè)置RAM地址 }void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str) {LcdSetCursor(x, y); //設(shè)置起始地址while (*str != '\0') //連續(xù)寫入字符串?dāng)?shù)據(jù),直到檢測(cè)到結(jié)束符{LcdWriteDat(*str++);} } /* * 函數(shù)名:LcdShowString * 描 述:在顯示屏上顯示一串字符串 * 輸 入:str - 待顯示字符串指針 * x - 屏幕顯示橫坐標(biāo)(以像素為單位) * y - 屏幕顯示縱坐標(biāo)(以像素為單位) * 輸 出:無(wú) * 備 注:輸入的字符串必須符合C語(yǔ)言規(guī)范,即以'\0'-NULL為結(jié)束標(biāo)識(shí); * x、y坐標(biāo)必須是16的整數(shù)倍,因DDRAM地址以全角字符(16*16pixel)為單位。 */ void LcdShowString(uint8 x, uint8 y, uint8 *str) {uint8 addr;//由輸入的顯示坐標(biāo)計(jì)算DDRAM的地址x >>= 4;// y=48y >>= 4;// y=3if (y >= 2){//y = 3 - 2y -= 2;//x = 8 + 8x += 8;}//addr = 16+8//16+16addr = y*16 + x;//由起始DDRAM地址連續(xù)寫入字符串LcdWriteCmd(0x30); //啟動(dòng)DDRAM操作LcdWriteCmd(0x80|addr);//1000 0000 | 0001 1000while (*str != '\0'){LcdWriteDat(*str);str++;} } /* * 函數(shù)名:LcdShowImage * 描 述:在顯示屏上顯示一幅圖像 * 輸 入:img - 待顯示圖像指針 * x - 屏幕顯示橫坐標(biāo)(以像素為單位) * y - 屏幕顯示縱坐標(biāo)(以像素為單位) * w - 圖像寬度(以像素為單位) * h - 圖像高度(以像素為單位) * 輸 出:無(wú) * 備 注:x與w必須是16的整數(shù)倍,因CGRAM最小尋址單位為2字節(jié);y與h可為0-63的任意值。 */ void LcdShowImage(uint8 x, uint8 y, uint8 w, uint8 h, uint8 *img) {int16 i;uint8 xi,yi;uint8 xt,yt;x >>= 4;w >>= 3;i = 0;LcdWriteCmd(0x36); //啟動(dòng)CGRAM操作for (yi=0; yi<h; yi++){yt = y+yi;xt = x;if (yt >= 32){yt -= 32;xt += 8;}LcdWriteCmd(0x80|yt);LcdWriteCmd(0x80|xt);for (xi=0; xi<w; xi++){LcdWriteDat(img[i++]);}} }/* 區(qū)域清除,清除從(x,y)坐標(biāo)起始的len個(gè)字符位 */ void LcdAreaClear(uint8 x, uint8 y, uint8 len) {LcdSetCursor(x, y); //設(shè)置起始地址while (len--) //連續(xù)寫入空格{LcdWriteDat(' ');} } void LcdFullClear() {LcdWriteCmd(0x01); } /* * 函數(shù)名:LcdClearArea * 描 述:清除屏幕上的一塊圖形顯示區(qū)域 * 輸 入:x - 區(qū)域起始橫坐標(biāo)(以像素為單位) * y - 區(qū)域起始縱坐標(biāo)(以像素為單位) * w - 區(qū)域?qū)挾?以像素為單位) * h - 區(qū)域高度(以像素為單位) * 輸 出:無(wú) * 備 注:x與w必須是16的整數(shù)倍,因CGRAM最小尋址單位為2字節(jié);y與h可為0-63的任意值。 */ void LcdClearArea(uint8 x, uint8 y, uint8 w, uint8 h) {uint8 xi,yi;uint8 xt,yt;x >>= 4;w >>= 3;LcdWriteCmd(0x36); //啟動(dòng)CGRAM操作for (yi=0; yi<h; yi++){yt = y+yi;xt = x;if (yt >= 32){yt -= 32;xt += 8;}LcdWriteCmd(0x80|yt);LcdWriteCmd(0x80|xt);for (xi=0; xi<w; xi++){LcdWriteDat(0x00); //寫入0x00以清除顯示}} }/* 12864液晶初始化函數(shù) */ void InitLcd12864() {uint8 x, y;//字符模式初始化LcdWriteCmd(0x30); //基本指令集LcdWriteCmd(0x01); //清零字符顯示LcdWriteCmd(0x02); //地址歸位LcdWriteCmd(0x0C); //開顯示//圖形模式初始化LcdWriteCmd(0x34); //啟動(dòng)擴(kuò)展指令,關(guān)閉圖形顯示for (y=0; y<32; y++) //清零圖形顯示緩沖區(qū){LcdWriteCmd(0x80|y);LcdWriteCmd(0x80|0);for (x=0; x<32; x++){LcdWriteDat(0x00);}}LcdWriteCmd(0x36); //開啟圖形模式顯示 }第四個(gè)文件LCD12864.H
/* ******************************************************************************* * 《手把手教你學(xué)51單片機(jī)(C語(yǔ)言版)》 * 配套 KST-51 單片機(jī)開發(fā)板 示例源代碼 * * (c) 版權(quán)所有 2014 金沙灘工作室/清華大學(xué)出版社 保留所有權(quán)利 * 獲取更多資料請(qǐng)?jiān)L問(wèn):http://www.kingst.org * * 文件名:Lcd12864.c * 描 述:12864點(diǎn)陣液晶驅(qū)動(dòng)模塊的頭文件 * 版本號(hào):v1.0.0 * 備 注: ******************************************************************************* */#ifndef _LCD_12864_H #define _LCD_12864_H #include <reg52.h> #include <intrins.h>//12864液晶口線與板載1602液晶相同 #define LCD12864_DB LCD1602_DB #define LCD12864_RS LCD1602_RS #define LCD12864_RW LCD1602_RW #define LCD12864_E LCD1602_E#ifndef _LCD_12864_C#endifvoid InitLcd12864(); void LcdWaitReady(); void LcdFullClear(); //void LcdWriteCmd(uint8 cmd);#endif主函數(shù):main.c文件
#include <reg52.h> #include "Lcd12864.h" #include "config.h" unsigned char step = 0; unsigned char oprt = 0; signed long num1 = 0; signed long num2 = 0; signed long result = 0; unsigned char T0RH = 0; unsigned char T0RL = 0;void ConfigTimer0(unsigned int ms); extern void KeyScan(); extern void KeyDriver(); extern void InitLcd1602(); extern void LcdShowString(uint8 x, uint8 y, uint8 *str); extern void LcdFullClear(); extern void LcdAreaClear(uint8 x, uint8 y, uint8 len);//extern void LcdShowString(uint8 x, uint8 y, uint8 *str); extern void LcdShowImage(uint8 x, uint8 y, uint8 w, uint8 h, uint8 *img); extern void LcdClearArea(uint8 x, uint8 y, uint8 w, uint8 h); extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);void main() {EA = 1; //開總中斷ConfigTimer0(1); //配置T0定時(shí)1msInitLcd12864(); //初始化液晶//LcdShowString(0, 0, "111111111111111"); //初始顯示一個(gè)數(shù)字0//LcdShowString(0, 16, "1111111111111111");//LcdShowString(0, 32, "1111111111111111");LcdShowString(112, 48, "0"); //因?yàn)樽址@示只能占用一半while (1){KeyDriver(); //調(diào)用按鍵驅(qū)動(dòng)} } unsigned char LongToString(unsigned char *str, signed long dat) {signed char i = 0;unsigned char len = 0;unsigned char buf[12];if(dat < 0){dat = -dat;*str++ = '-';len++;}do{buf[i++] = dat % 10;dat /= 10;}while(dat > 0);len += i;while(i-- > 0){*str++ = buf[i] + '0';}*str = '\0';return len;} void ShowOprt(unsigned char y, unsigned char type) {switch(type){case 0: LcdShowString(0, y, "+"); break;case 1: LcdShowString(0, y, "-"); break;case 2: LcdShowString(0, y, "*"); break;case 3: LcdShowString(0, y, "/"); break;default: break;} } void Reset() {num1 = 0;num2 = 0;step = 0;LcdFullClear(); } void NumKeyAction(unsigned char n) {unsigned char len = 0;unsigned char str[12];if(step > 1){Reset();}if(step == 0){num1 = num1*10 + n;len = LongToString(str, num1);LcdShowString(128 - len, 48, str);}else {num2 = num2*10 + n;len = LongToString(str, num2);LcdShowString(128 - len, 48, str);} } void OprtKeyAction(unsigned char type) { unsigned char len;unsigned char str[12];if(step == 0){len = LongToString(str, num1);LcdAreaClear(0, 48, 128-len);LcdShowString(128-len, 32, str);ShowOprt(48, type); LcdAreaClear(1, 48, 14);//待定 LcdShowString(112, 48, "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 = LongToString(str, num2); LcdAreaClear(1, 32, 128-1-len);ShowOprt(32, oprt);LcdShowString(128-len, 32, str);len = LongToString(str, result);LcdShowString(0, 48, "=");LcdAreaClear(1, 48, 16-1-len);LcdShowString(128-len, 48, str); } }/* 配置并啟動(dòng)T0,ms-T0定時(shí)時(shí)間 */ void ConfigTimer0(unsigned int ms) {unsigned long tmp; //臨時(shí)變量tmp = 11059200 / 12; //定時(shí)器計(jì)數(shù)頻率tmp = (tmp * ms) / 1000; //計(jì)算所需的計(jì)數(shù)值tmp = 65536 - tmp; //計(jì)算定時(shí)器重載值tmp = tmp + 12; //補(bǔ)償中斷響應(yīng)延時(shí)造成的誤差T0RH = (unsigned char)(tmp>>8); //定時(shí)器重載值拆分為高低字節(jié)T0RL = (unsigned char)tmp;TMOD &= 0xF0; //清零T0的控制位TMOD |= 0x01; //配置T0為模式1TH0 = T0RH; //加載T0重載值TL0 = T0RL;ET0 = 1; //使能T0中斷TR0 = 1; //啟動(dòng)T0 } /* T0中斷服務(wù)函數(shù),執(zhí)行按鍵掃描 */ void InterruptTimer0() interrupt 1 {TH0 = T0RH; //重新加載重載值TL0 = T0RL;KeyScan(); //按鍵掃描 }遇到的問(wèn)題:中文字符是1616,字符是816,數(shù)字屬于英文字符,所以只占了8*16,中文字符的一半,默認(rèn)是左半邊對(duì)齊,右移是需要同時(shí)顯示一個(gè)英文空格和一個(gè)英文字符。目的:想讓字符0顯示在12864的右下角
更新:在LCD12864數(shù)字顯示在左邊解決了移位的問(wèn)題,+號(hào)和=號(hào)顯示在右邊,和課程例子有點(diǎn)區(qū)別
LCD12864.c
#include <reg52.h>typedef signed char int8; // 8位有符號(hào)整型數(shù) typedef signed int int16; //16位有符號(hào)整型數(shù) typedef signed long int32; //32位有符號(hào)整型數(shù) typedef unsigned char uint8; // 8位無(wú)符號(hào)整型數(shù) typedef unsigned int uint16; //16位無(wú)符號(hào)整型數(shù) typedef unsigned long uint32; //32位無(wú)符號(hào)整型數(shù)#define LCD1602_DB P0 //1602液晶數(shù)據(jù)端口 sbit LCD1602_RS = P1^0; //1602液晶指令/數(shù)據(jù)選擇引腳 sbit LCD1602_RW = P1^1; //1602液晶讀寫引腳 sbit LCD1602_E = P1^5; //1602液晶使能引腳#define LCD12864_DB LCD1602_DB #define LCD12864_RS LCD1602_RS #define LCD12864_RW LCD1602_RW #define LCD12864_E LCD1602_Evoid LcdShowString(uint8 x, uint8 y, uint8 *str); void InitLcd12864(); void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str); void LcdFullClear(); /* 等待液晶準(zhǔn)備好 */ void LcdWaitReady() {uint8 sta;LCD12864_DB = 0xFF;LCD12864_RS = 0;LCD12864_RW = 1;do {LCD12864_E = 1;sta = LCD12864_DB;LCD12864_E = 0;} while (sta & 0x80); //bit7等于1表示液晶正忙,重復(fù)檢測(cè)直到其等于0為止 }/* 向液晶寫入一字節(jié)命令,cmd-待寫入命令值 */ void LcdWriteCmd(uint8 cmd) {LcdWaitReady();LCD12864_RS = 0;LCD12864_RW = 0;LCD12864_DB = cmd;LCD12864_E = 1;LCD12864_E = 0; }void LcdWriteDat(uint8 dat) {LcdWaitReady();LCD12864_RS = 1;LCD12864_RW = 0;LCD12864_DB = dat;LCD12864_E = 1;LCD12864_E = 0; }void LcdSetCursor(unsigned char x, unsigned char y) {uint8 addr;//由輸入的顯示坐標(biāo)計(jì)算DDRAM的地址x >>= 4;y >>= 4;if (y >= 2){y -= 2;x += 8;}addr = y*16 + x;//由起始DDRAM地址連續(xù)寫入字符串LcdWriteCmd(0x30); //啟動(dòng)DDRAM操作LcdWriteCmd(0x80|addr); }void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str) {LcdSetCursor(x, y);while (*str != '\0'){LcdWriteDat(*str);str++;} } /* 區(qū)域清除,清除從(x,y)坐標(biāo)起始的len個(gè)字符位 */ void LcdAreaClear(unsigned char x, unsigned char y, unsigned char len) {LcdSetCursor(x, y); //設(shè)置起始地址while (len--) //連續(xù)寫入空格{LcdWriteDat(' ');} }/* 整屏清除 */ void LcdFullClear() {LcdWriteCmd(0x01); }void InitLcd12864() {uint8 x, y;//字符模式初始化LcdWriteCmd(0x30); //基本指令集LcdWriteCmd(0x01); //清零字符顯示LcdWriteCmd(0x02); //地址歸位LcdWriteCmd(0x0C); //開顯示//圖形模式初始化LcdWriteCmd(0x34); //啟動(dòng)擴(kuò)展指令,關(guān)閉圖形顯示for (y=0; y<32; y++) //清零圖形顯示緩沖區(qū){LcdWriteCmd(0x80|y);LcdWriteCmd(0x80|0);for (x=0; x<32; x++){LcdWriteDat(0x00);}}LcdWriteCmd(0x36); //開啟圖形模式顯示 }keyboard.c
#include <reg52.h>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 KeyCodeMap[4][4] = { //矩陣按鍵編號(hào)到標(biāo)準(zhǔn)鍵盤鍵碼的映射表{ '1', '2', '3', 0x26 }, //數(shù)字鍵1、數(shù)字鍵2、數(shù)字鍵3、向上鍵{ '4', '5', '6', 0x25 }, //數(shù)字鍵4、數(shù)字鍵5、數(shù)字鍵6、向左鍵{ '7', '8', '9', 0x28 }, //數(shù)字鍵7、數(shù)字鍵8、數(shù)字鍵9、向下鍵{ '0', 0x1B, 0x0D, 0x27 } //數(shù)字鍵0、ESC鍵、 回車鍵、 向右鍵 }; unsigned char pdata KeySta[4][4] = { //全部矩陣按鍵的當(dāng)前狀態(tài){1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1} };extern void KeyAction(unsigned char keycode);/* 按鍵驅(qū)動(dòng)函數(shù),檢測(cè)按鍵動(dòng)作,調(diào)度相應(yīng)動(dòng)作函數(shù),需在主循環(huán)中調(diào)用 */ void KeyDriver() {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++) //循環(huán)檢測(cè)4*4的矩陣按鍵{for (j=0; j<4; j++){if (backup[i][j] != KeySta[i][j]) //檢測(cè)按鍵動(dòng)作{if (backup[i][j] != 0) //按鍵按下時(shí)執(zhí)行動(dòng)作{KeyAction(KeyCodeMap[i][j]); //調(diào)用按鍵動(dòng)作函數(shù)}backup[i][j] = KeySta[i][j]; //刷新前一次的備份值}}} } /* 按鍵掃描函數(shù),需在定時(shí)中斷中調(diào)用,推薦調(diào)用間隔1ms */ void KeyScan() {unsigned char i;static unsigned char keyout = 0; //矩陣按鍵掃描輸出索引static unsigned char keybuf[4][4] = { //矩陣按鍵掃描緩沖區(qū){0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF},{0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}};//將一行的4個(gè)按鍵值移入緩沖區(qū)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;//消抖后更新按鍵狀態(tài)for (i=0; i<4; i++) //每行4個(gè)按鍵,所以循環(huán)4次{if ((keybuf[keyout][i] & 0x0F) == 0x00){ //連續(xù)4次掃描值為0,即4*4ms內(nèi)都是按下狀態(tài)時(shí),可認(rèn)為按鍵已穩(wěn)定的按下KeySta[keyout][i] = 0;}else if ((keybuf[keyout][i] & 0x0F) == 0x0F){ //連續(xù)4次掃描值為1,即4*4ms內(nèi)都是彈起狀態(tài)時(shí),可認(rèn)為按鍵已穩(wěn)定的彈起KeySta[keyout][i] = 1;}}//執(zhí)行下一次的掃描輸出keyout++; //輸出索引遞增keyout &= 0x03; //索引值加到4即歸零switch (keyout) //根據(jù)索引,釋放當(dāng)前輸出引腳,拉低下次的輸出引腳{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 <reg52.h>unsigned char step = 0; //操作步驟 unsigned char oprt = 0; //運(yùn)算類型 signed long num1 = 0; //操作數(shù)1 signed long num2 = 0; //操作數(shù)2 signed long result = 0; //運(yùn)算結(jié)果 unsigned char T0RH = 0; //T0重載值的高字節(jié) unsigned char T0RL = 0; //T0重載值的低字節(jié)void ConfigTimer0(unsigned int ms); extern void KeyScan(); extern void KeyDriver(); extern void InitLcd12864(); extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str); extern void LcdAreaClear(unsigned char x, unsigned char y, unsigned char len); extern void LcdFullClear();void main() {EA = 1; //開總中斷ConfigTimer0(1); //配置T0定時(shí)1msInitLcd12864(); //初始化液晶LcdShowStr(0, 48, "0"); //初始顯示一個(gè)數(shù)字0while (1){KeyDriver(); //調(diào)用按鍵驅(qū)動(dòng)} } /* 長(zhǎng)整型數(shù)轉(zhuǎn)換為字符串,str-字符串指針,dat-待轉(zhuǎn)換數(shù),返回值-字符串長(zhǎng)度 */ unsigned char LongToString(unsigned char *str, signed long dat) {signed char i = 0;unsigned char len = 0;unsigned char buf[12];if (dat < 0) //如果為負(fù)數(shù),首先取絕對(duì)值,并在指針上添加負(fù)號(hào){dat = -dat;*str++ = '-';len++;}do { //先轉(zhuǎn)換為低位在前的十進(jìn)制數(shù)組buf[i++] = dat % 10;dat /= 10;} while (dat > 0);len += i; //i最后的值就是有效字符的個(gè)數(shù)while (i-- > 0) //將數(shù)組值轉(zhuǎn)換為ASCII碼反向拷貝到接收指針上{*str++ = buf[i] + '0';}*str = '\0'; //添加字符串結(jié)束符return len; //返回字符串長(zhǎng)度 }void ShowOprt(unsigned char y, unsigned char type) {switch(type){case 0: LcdShowStr(112, y, "+"); break;case 1: LcdShowStr(112, y, "-"); break;case 2: LcdShowStr(112, y, "*"); break;case 3: LcdShowStr(112, y, "/"); break;default: break;} }void Reset() {num1 = 0;num2 = 0;step = 0;LcdFullClear(); }/* 數(shù)字鍵動(dòng)作函數(shù),n-按鍵輸入的數(shù)值 */ void NumKeyAction(unsigned char n) {unsigned char len;unsigned char str[12];if (step > 1) //如計(jì)算已完成,則重新開始新的計(jì)算{Reset();}if (step == 0) //輸入第一操作數(shù){num1 = num1*10 + n; //輸入數(shù)值累加到原操作數(shù)上len = LongToString(str, num1); //新數(shù)值轉(zhuǎn)換為字符串LcdShowStr(16 - len, 48, str); //顯示到液晶第二行上}else {num2 = num2*10 + n;len = LongToString(str, num2);LcdShowStr(16 - len, 48, str);} }void OprtKeyAction(unsigned char type) { unsigned char len;unsigned char str[12];if(step == 0){len = LongToString(str, num1);LcdAreaClear(0, 48, 16-len);LcdShowStr(16-len, 32, str);ShowOprt(32, type); LcdAreaClear(1, 48, 14); LcdShowStr(0, 48, "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 = LongToString(str, num2); ShowOprt(32, oprt); //LcdAreaClear(1, 32, 16-1-len); //待定--發(fā)現(xiàn)LCD12864上刪除了沒(méi)有啥影響LcdShowStr(16-len, 32, str);len = LongToString(str, result);LcdShowStr(112, 48, "=");//LcdAreaClear(1, 48, 16-1-len); //待定--發(fā)現(xiàn)LCD12864上刪除了沒(méi)有啥影響LcdShowStr(16-len, 48, str); } }/* 按鍵動(dòng)作函數(shù),根據(jù)鍵碼執(zhí)行相應(yīng)的操作,keycode-按鍵鍵碼 */ 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){Reset();LcdShowStr(0, 48, "0"); //初始顯示一個(gè)數(shù)字0} } /* 配置并啟動(dòng)T0,ms-T0定時(shí)時(shí)間 */ void ConfigTimer0(unsigned int ms) {unsigned long tmp; //臨時(shí)變量tmp = 11059200 / 12; //定時(shí)器計(jì)數(shù)頻率tmp = (tmp * ms) / 1000; //計(jì)算所需的計(jì)數(shù)值tmp = 65536 - tmp; //計(jì)算定時(shí)器重載值tmp = tmp + 28; //補(bǔ)償中斷響應(yīng)延時(shí)造成的誤差T0RH = (unsigned char)(tmp>>8); //定時(shí)器重載值拆分為高低字節(jié)T0RL = (unsigned char)tmp;TMOD &= 0xF0; //清零T0的控制位TMOD |= 0x01; //配置T0為模式1TH0 = T0RH; //加載T0重載值TL0 = T0RL;ET0 = 1; //使能T0中斷TR0 = 1; //啟動(dòng)T0 } /* T0中斷服務(wù)函數(shù),執(zhí)行按鍵掃描 */ void InterruptTimer0() interrupt 1 {TH0 = T0RH; //重新加載重載值TL0 = T0RL;KeyScan(); //按鍵掃描 }總結(jié)
以上是生活随笔為你收集整理的基于51单片机的金沙滩12864的计算器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 算法导论习题C.2-7,C.2-8答案
- 下一篇: 基于snowflake的Id序列号生成器