第83節:矩陣鍵盤輸入任意數字或小數點的液晶屏顯示程序
本來這節打算講調用液晶屏內部字庫時讓某行內容反顯的,但是在昨天調試過程中,發現一個很奇怪的問題,當調用內部字庫時,按照數據手冊,我執行一條反顯指令時,應該是僅僅某一行反顯,但是卻同時出現兩行反顯。比如,當我執行
WriteCommand(0x34); //擴充指令集
WriteCommand(0x04); //第1行反顯
指令時,發現第一行和第三行反顯,后來想想,我猜測這種12864的屏應該是25632折成左右半屏,左半屏在上面,右半屏在下面。經過這次經驗,我覺得大家以后盡量不要用液晶屏的內部字庫模式,應該用自構字庫的模式(圖形模式)。因為我覺得用內部字庫模式的時候,這個集成的反顯擴展指令不好用。而用自構字庫的模式(圖形模式),卻可以順心所欲的靈活運用,適合做菜單程序。
WriteCommand(0x34); //擴充指令集
WriteCommand(0x04); //第1行反顯
指令時,發現第一行和第三行反顯,后來想想,我猜測這種12864的屏應該是25632折成左右半屏,左半屏在上面,右半屏在下面。經過這次經驗,我覺得大家以后盡量不要用液晶屏的內部字庫模式,應該用自構字庫的模式(圖形模式)。因為我覺得用內部字庫模式的時候,這個集成的反顯擴展指令不好用。而用自構字庫的模式(圖形模式),卻可以順心所欲的靈活運用,適合做菜單程序。
既然發現內部字庫不好用,所以不再講內部字庫模式,這節僅僅接著前面第79節內容,繼續講在自構字庫的模式(圖形模式)下,如何通過矩陣鍵盤直接輸入數字和小數點,就像普通的計算器一樣鍵盤輸入。這個功能表面簡單,其實有以下四個地方值得注意:
第一:如何用數組接收按鍵輸入的BCD碼數據。
第二:如何限制輸入參數的小數點個數和數組的有效個數。
第三:如果第0個位置是0,那么繼續輸入的數據直接覆蓋0,否則就移位再輸入。
第四:如果第0個位置是0,那么繼續輸入的小數點要移位輸入。
要仔細了解以上提到的關鍵點,必須好好研究本程序中的void set_data(…)函數。同時也要溫習一下之前講的自構字庫模式的液晶屏顯示內容,尤其是插入畫布顯示的內容。
具體內容,請看源代碼講解。
(1) 硬件平臺:
基于朱兆祺51單片機學習板。數字1鍵對應S1鍵,數字2鍵對應S2鍵,數字3鍵對應S3鍵…. 數字9鍵對應S9鍵, 數字0鍵對應S10鍵。小數鍵對應S11,清零鍵對應S16,其它按鍵不用。
基于朱兆祺51單片機學習板。數字1鍵對應S1鍵,數字2鍵對應S2鍵,數字3鍵對應S3鍵…. 數字9鍵對應S9鍵, 數字0鍵對應S10鍵。小數鍵對應S11,清零鍵對應S16,其它按鍵不用。
(2) 實現功能:
用矩陣鍵盤輸入任意數字或小數點。小數點不能超過2位,一旦超過2位,再按其它按鍵則輸入無效。有效數字也不能超過6位(包括小數點),一旦超過6位,再按其它按鍵則輸入無效。
想重新輸入,必須按S16清零按鍵才能重新輸入。

(3)源代碼講解如下:
- #include "REG52.H"
- #define const_voice_short40 //蜂鳴器短叫的持續時間
- #define const_key_time10 //按鍵去抖動延時的時間
- sbit key_sr1=P0^0; //第一行輸入
- sbit key_sr2=P0^1; //第二行輸入
- sbit key_sr3=P0^2; //第三行輸入
- sbit key_sr4=P0^3; //第四行輸入
- sbit key_dr1=P0^4; //第一列輸出
- sbit key_dr2=P0^5; //第二列輸出
- sbit key_dr3=P0^6; //第三列輸出
- sbit key_dr4=P0^7; //第四列輸出
- sbit beep_dr=P2^7; //蜂鳴器的驅動IO口
- sbitLCDCS_dr= P1^6;//片選線
- sbitLCDSID_dr = P1^7;//串行數據線
- sbitLCDCLK_dr = P3^2;//串行時鐘線
- sbitLCDRST_dr = P3^4;//復位線
- void SendByteToLcd(unsigned char ucData);//發送一個字節數據到液晶模塊
- void SPIWrite(unsigned char ucWData, unsigned char ucWRS); //模擬SPI發送一個字節的命令或者數據給液晶模塊的底層驅動
- void WriteCommand(unsigned char ucCommand); //發送一個字節的命令給液晶模塊
- void LCDWriteData(unsigned char ucData); //發送一個字節的數據給液晶模塊
- void LCDInit(void);//初始化函數內部包括液晶模塊的復位
- void display_clear(unsigned char ucFillDate); // 清屏 全部顯示空填充0x00 全部顯示點陣用0xff
- void insert_buffer_to_canvas(unsigned int x,unsigned int y,const unsigned char*ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount);//把字模插入畫布.
- void display_lattice(unsigned int x,unsigned int y,const unsigned char*ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount,unsigned int uiOffSetAddr); //顯示任意點陣函數
- unsigned char *number_to_matrix(unsigned charucBitNumber); //把一位數字轉換成字模首地址的函數
- void delay_short(unsigned int uiDelayshort); //延時
- void delay_long(unsigned int uiDelayLong);
- void key_number_input(unsigned char ucKeyNumber); //輸入數字按鍵
- void set_data(unsigned char ucKeyNumberTemp,unsigned char ucDotBitMax,unsigned char ucDataCntMax,unsigned char *p_ucDotCnt,unsigned char *p_ucDotBitS,unsigned char *p_ucWdPartCnt,unsigned char *p_ucSetDataBuffer);
- void key_delete_input(void); //刪除按鍵
- void T0_time(); //定時中斷函數
- void key_service();
- void key_scan(); //按鍵掃描函數 放在定時中斷里
- void initial_myself();
- void initial_peripheral();
- void lcd_display_service(void); //應用層面的液晶屏顯示程序
- void clear_all_canvas(void);//把畫布全部清零
- code unsigned char Zf816_0[]=
- {
- /*--文字:0--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=8x16 --*/
- 0x00,0x00,0x00,0x18,0x24,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x24,0x18,0x00,0x00,
- };
- code unsigned char Zf816_1[]=
- {
- /*--文字:1--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=8x16 --*/
- 0x00,0x00,0x00,0x10,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x7C,0x00,0x00,
- };
- code unsigned char Zf816_2[]=
- {
- /*--文字:2--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=8x16 --*/
- 0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x04,0x04,0x08,0x10,0x20,0x42,0x7E,0x00,0x00,
- };
- code unsigned char Zf816_3[]=
- {
- /*--文字:3--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=8x16 --*/
- 0x00,0x00,0x00,0x3C,0x42,0x42,0x04,0x18,0x04,0x02,0x02,0x42,0x44,0x38,0x00,0x00,
- };
- code unsigned char Zf816_4[]=
- {
- /*--文字:4--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=8x16 --*/
- 0x00,0x00,0x00,0x04,0x0C,0x14,0x24,0x24,0x44,0x44,0x7E,0x04,0x04,0x1E,0x00,0x00,
- };
- code unsigned char Zf816_5[]=
- {
- /*--文字:5--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=8x16 --*/
- 0x00,0x00,0x00,0x7E,0x40,0x40,0x40,0x58,0x64,0x02,0x02,0x42,0x44,0x38,0x00,0x00,
- };
- code unsigned char Zf816_6[]=
- {
- /*--文字:6--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=8x16 --*/
- 0x00,0x00,0x00,0x1C,0x24,0x40,0x40,0x58,0x64,0x42,0x42,0x42,0x24,0x18,0x00,0x00,
- };
- code unsigned char Zf816_7[]=
- {
- /*--文字:7--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=8x16 --*/
- 0x00,0x00,0x00,0x7E,0x44,0x44,0x08,0x08,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,
- };
- code unsigned char Zf816_8[]=
- {
- /*--文字:8--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=8x16 --*/
- 0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x24,0x18,0x24,0x42,0x42,0x42,0x3C,0x00,0x00,
- };
- code unsigned char Zf816_9[]=
- {
- /*--文字:9--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=8x16 --*/
- 0x00,0x00,0x00,0x18,0x24,0x42,0x42,0x42,0x26,0x1A,0x02,0x02,0x24,0x38,0x00,0x00,
- };
- code unsigned char Zf816_nc[]=//空字模
- {
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- };
- code unsigned char Zf816_dot[]=//小數點
- {
- /*--文字:.--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=8x16 --*/
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00,
- };
- code unsigned char Zf816_mao_hao[]=//冒號
- {
- /*--文字::--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=8x16 --*/
- 0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,
- };
- code unsigned char Hz1616_yi[]=
- {
- /*--文字:一--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=16x16 --*/
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x7F,0xFE,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- };
- code unsigned char Hz1616_xiang[]=
- {
- /*--文字:項--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=16x16 --*/
- 0x00,0x00,0x03,0xFE,0xFC,0x20,0x10,0x40,0x11,0xFC,0x11,0x04,0x11,0x24,0x11,0x24,
- 0x11,0x24,0x11,0x24,0x1D,0x24,0xE1,0x34,0x00,0x48,0x01,0x86,0x06,0x02,0x00,0x00,
- };
- code unsigned char Hz1616_shu[]=
- {
- /*--文字:數--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=16x16 --*/
- 0x08,0x20,0x49,0x30,0x2A,0x20,0x1C,0x20,0xFF,0x7E,0x1C,0x44,0x2B,0x44,0x48,0xC4,
- 0x08,0x28,0xFF,0x28,0x12,0x10,0x34,0x10,0x0C,0x28,0x32,0x4E,0xC0,0x84,0x00,0x00,
- };
- code unsigned char Hz1616_zhu[]=
- {
- /*--文字:組--*/
- /*--宋體12;此字體下對應的點陣為:寬x高=16x16 --*/
- 0x10,0x00,0x19,0xF8,0x11,0x08,0x25,0x08,0x25,0x08,0x79,0xF8,0x09,0x08,0x11,0x08,
- 0x21,0x08,0x7D,0xF8,0x01,0x08,0x01,0x08,0x0D,0x08,0x73,0xFE,0x00,0x00,0x00,0x00,
- };
- /* 注釋一:
- * 以下是畫布顯示數組。橫向是6個字節,縱向16行,可以顯示3個16x16的漢字.
- *注意,這節內容的畫布跟前面章節的畫布大小不一樣,前面章節的橫向是4個字節,這節的橫向是6個字節。
- */
- unsigned char ucCanvasBuffer[]=
- {
- 0x00,0x00,0x00,0x00,0x00,0x00,//上半屏
- 0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,
- //------------上半屏和下半屏的分割線-----------
- 0x00,0x00,0x00,0x00,0x00,0x00,//下半屏
- 0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,
- };
- /* 注釋二:
- * 以下4個變量記錄一個參數的4種信息,包括小數點的數量,個數,數據的位置,數組具體值.
- */
- unsigned char ucDotCnt_1=0;//記錄當前輸入的小數點數量,如果小數點的數量不為0,說明當前數組已包含小數點,此時再按小數點按鍵則無效
- unsigned char ucDotBitS_1=0; //記錄當前輸入的小數點個數,如果小數點的個量如果超過規定2位,此時再按任何輸入按鍵則無效
- unsigned char ucWdPartCnt_1=0; //記錄當前輸入的數據在數組中的位置。
- unsigned char ucDataBuffer_1[6]={0,10,10,10,10,10}; //一項的BCD碼數組緩沖
- unsigned char ucKeyStep=1;//按鍵掃描步驟變量
- unsigned char ucKeySec=0; //被觸發的按鍵編號
- unsigned intuiKeyTimeCnt=0; //按鍵去抖動延時計數器
- unsigned char ucKeyLock=0; //按鍵觸發后自鎖的變量標志
- unsigned char ucRowRecord=1; //記錄當前掃描到第幾列了
- unsigned intuiVoiceCnt=0;//蜂鳴器鳴叫的持續時間計數器
- unsigned char ucWd=1; //窗口變量
- unsigned char ucPart=1; //局部變量 0代表沒有選中任何一行,其它數值1到4代表選中某一行
- unsigned char ucWd1Update=1; //窗口1的整屏更新顯示變量 1代表更新顯示,響應函數內部會清零
- unsigned char ucWd1Part1Update=0; //窗口1的第1行局部更新顯示變量1代表更新顯示,響應函數內部會清零
- void main()
- {
- initial_myself(); //第一區,上電后馬上初始化
- delay_long(100); //一線,延時線。延時一段時間
- initial_peripheral();//第二區,上電后延時一段時間再初始化
- while(1) //第三區
- {
- key_service(); //按鍵服務程序
- lcd_display_service(); //應用層面的液晶屏顯示程序
- }
- }
- void initial_myself()//第一區 上電后馬上初始化
- {
- beep_dr=1; //用PNP三極管控制蜂鳴器,輸出高電平時不叫。
- TMOD=0x01;//設置定時器0為工作方式1
- TH0=0xf8; //重裝初始值(65535-2000)=63535=0xf82f
- TL0=0x2f;
- }
- void initial_peripheral() //第二區 上電后延時一段時間再初始化
- {
- LCDInit(); //初始化12864 內部包含液晶模塊的復位
- EA=1; //開總中斷
- ET0=1; //允許定時中斷
- TR0=1; //啟動定時中斷
- }
- void T0_time() interrupt 1
- {
- TF0=0;//清除中斷標志
- TR0=0; //關中斷
- key_scan();//按鍵掃描函數 放在定時中斷里
- if(uiVoiceCnt!=0)
- {
- uiVoiceCnt--; //每次進入定時中斷都自減1,直到等于零為止。才停止鳴叫
- beep_dr=0;//蜂鳴器是PNP三極管控制,低電平就開始鳴叫。
- }
- else
- {
- ; //此處多加一個空指令,想維持跟if括號語句的數量對稱,都是兩條指令。不加也可以。
- beep_dr=1;//蜂鳴器是PNP三極管控制,高電平就停止鳴叫。
- }
- TH0=0xf8; //重裝初始值(65535-2000)=63535=0xf82f
- TL0=0x2f;
- TR0=1;//開中斷
- }
- void key_scan()//按鍵掃描函數 放在定時中斷里
- {
- switch(ucKeyStep)
- {
- case 1: //按鍵掃描輸出第ucRowRecord列低電平
- if(ucRowRecord==1)//第一列輸出低電平
- {
- key_dr1=0;
- key_dr2=1;
- key_dr3=1;
- key_dr4=1;
- }
- else if(ucRowRecord==2)//第二列輸出低電平
- {
- key_dr1=1;
- key_dr2=0;
- key_dr3=1;
- key_dr4=1;
- }
- else if(ucRowRecord==3)//第三列輸出低電平
- {
- key_dr1=1;
- key_dr2=1;
- key_dr3=0;
- key_dr4=1;
- }
- else //第四列輸出低電平
- {
- key_dr1=1;
- key_dr2=1;
- key_dr3=1;
- key_dr4=0;
- }
- uiKeyTimeCnt=0;//延時計數器清零
- ucKeyStep++; //切換到下一個運行步驟
- break;
- case 2: //此處的小延時用來等待剛才列輸出信號穩定,再判斷輸入信號。不是去抖動延時。
- uiKeyTimeCnt++;
- if(uiKeyTimeCnt>1)
- {
- uiKeyTimeCnt=0;
- ucKeyStep++; //切換到下一個運行步驟
- }
- break;
- case 3:
- if(key_sr1==1&&key_sr2==1&&key_sr3==1&&key_sr4==1)
- {
- ucKeyStep=1;//如果沒有按鍵按下,返回到第一個運行步驟重新開始掃描
- ucKeyLock=0;//按鍵自鎖標志清零
- uiKeyTimeCnt=0; //按鍵去抖動延時計數器清零,此行非常巧妙
- ucRowRecord++;//輸出下一列
- if(ucRowRecord>4)
- {
- ucRowRecord=1; //依次輸出完四列之后,繼續從第一列開始輸出低電平
- }
- }
- else if(ucKeyLock==0)//有按鍵按下,且是第一次觸發
- {
- if(key_sr1==0&&key_sr2==1&&key_sr3==1&&key_sr4==1)
- {
- uiKeyTimeCnt++;//去抖動延時計數器
- if(uiKeyTimeCnt>const_key_time)
- {
- uiKeyTimeCnt=0;
- ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有松開按鍵,此標志位才會被清零
- if(ucRowRecord==1)//第一列輸出低電平
- {
- ucKeySec=1;//觸發1號鍵 對應朱兆祺學習板的S1鍵
- }
- else if(ucRowRecord==2)//第二列輸出低電平
- {
- ucKeySec=2;//觸發2號鍵 對應朱兆祺學習板的S2鍵
- }
- else if(ucRowRecord==3)//第三列輸出低電平
- {
- ucKeySec=3;//觸發3號鍵 對應朱兆祺學習板的S3鍵
- }
- else //第四列輸出低電平
- {
- ucKeySec=4;//觸發4號鍵 對應朱兆祺學習板的S4鍵
- }
- }
- }
- else if(key_sr1==1&&key_sr2==0&&key_sr3==1&&key_sr4==1)
- {
- uiKeyTimeCnt++;//去抖動延時計數器
- if(uiKeyTimeCnt>const_key_time)
- {
- uiKeyTimeCnt=0;
- ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有松開按鍵,此標志位才會被清零
- if(ucRowRecord==1)//第一列輸出低電平
- {
- ucKeySec=5;//觸發5號鍵 對應朱兆祺學習板的S5鍵
- }
- else if(ucRowRecord==2)//第二列輸出低電平
- {
- ucKeySec=6;//觸發6號鍵 對應朱兆祺學習板的S6鍵
- }
- else if(ucRowRecord==3)//第三列輸出低電平
- {
- ucKeySec=7;//觸發7號鍵 對應朱兆祺學習板的S7鍵
- }
- else //第四列輸出低電平
- {
- ucKeySec=8;//觸發8號鍵 對應朱兆祺學習板的S8鍵
- }
- }
- }
- else if(key_sr1==1&&key_sr2==1&&key_sr3==0&&key_sr4==1)
- {
- uiKeyTimeCnt++;//去抖動延時計數器
- if(uiKeyTimeCnt>const_key_time)
- {
- uiKeyTimeCnt=0;
- ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有松開按鍵,此標志位才會被清零
- if(ucRowRecord==1)//第一列輸出低電平
- {
- ucKeySec=9;//觸發9號鍵 對應朱兆祺學習板的S9鍵
- }
- else if(ucRowRecord==2)//第二列輸出低電平
- {
- ucKeySec=10;//觸發10號鍵 對應朱兆祺學習板的S10鍵
- }
- else if(ucRowRecord==3)//第三列輸出低電平
- {
- ucKeySec=11;//觸發11號鍵 對應朱兆祺學習板的S11鍵
- }
- else //第四列輸出低電平
- {
- ucKeySec=12;//觸發12號鍵 對應朱兆祺學習板的S12鍵
- }
- }
- }
- else if(key_sr1==1&&key_sr2==1&&key_sr3==1&&key_sr4==0)
- {
- uiKeyTimeCnt++;//去抖動延時計數器
- if(uiKeyTimeCnt>const_key_time)
- {
- uiKeyTimeCnt=0;
- ucKeyLock=1;//自鎖按鍵置位,避免一直觸發,只有松開按鍵,此標志位才會被清零
- if(ucRowRecord==1)//第一列輸出低電平
- {
- ucKeySec=13;//觸發13號鍵 對應朱兆祺學習板的S13鍵
- }
- else if(ucRowRecord==2)//第二列輸出低電平
- {
- ucKeySec=14;//觸發14號鍵 對應朱兆祺學習板的S14鍵
- }
- else if(ucRowRecord==3)//第三列輸出低電平
- {
- ucKeySec=15;//觸發15號鍵 對應朱兆祺學習板的S15鍵
- }
- else //第四列輸出低電平
- {
- ucKeySec=16;//觸發16號鍵 對應朱兆祺學習板的S16鍵
- }
- }
- }
- }
- break;
- }
- }
- void key_service() //按鍵服務的應用程序
- {
- switch(ucKeySec) //按鍵服務狀態切換
- {
- case 1:// 數字1 對應朱兆祺學習板的S1鍵
- key_number_input(1); //輸入數字按鍵
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- case 2:// 數字2 對應朱兆祺學習板的S2鍵
- key_number_input(2); //輸入數字按鍵
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- case 3:// 數字3 對應朱兆祺學習板的S3鍵
- key_number_input(3); //輸入數字按鍵
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- case 4:// 數字4 對應朱兆祺學習板的S4鍵
- key_number_input(4); //輸入數字按鍵
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- case 5:// 數字5 對應朱兆祺學習板的S5鍵
- key_number_input(5); //輸入數字按鍵
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- case 6:// 數字6 對應朱兆祺學習板的S6鍵
- key_number_input(6); //輸入數字按鍵
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- case 7:// 數字7 對應朱兆祺學習板的S7鍵
- key_number_input(7); //輸入數字按鍵
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- case 8: //數字8 對應朱兆祺學習板的S8鍵
- key_number_input(8); //輸入數字按鍵
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- case 9:// 數字9 對應朱兆祺學習板的S9鍵
- key_number_input(9); //輸入數字按鍵
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- case 10:// 數字0對應朱兆祺學習板的S10鍵
- key_number_input(0); //輸入數字按鍵
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- case 11:// 小數點按鍵 對應朱兆祺學習板的S11鍵
- key_number_input(11); //輸入數字按鍵11代表小數點
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- case 12:// 本節暫時不用 對應朱兆祺學習板的S12鍵
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- case 13:// 本節暫時不用 對應朱兆祺學習板的S13鍵
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- case 14:// 本節暫時不用對應朱兆祺學習板的S14鍵
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- case 15:// 本節暫時不用 對應朱兆祺學習板的S15鍵
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- case 16:// 清除按鍵 對應朱兆祺學習板的S16鍵
- key_delete_input(); //刪除按鍵
- uiVoiceCnt=const_voice_short; //按鍵聲音觸發,滴一聲就停。
- ucKeySec=0;//響應按鍵服務處理程序后,按鍵編號清零,避免一致觸發
- break;
- }
- }
- void key_number_input(unsigned char ucKeyNumber) //輸入數字按鍵
- {
- switch(ucWd)
- {
- case 1: //第1窗口。本節程序只有1個窗口
- switch(ucPart)
- {
- case 1://1窗口第1項
- set_data(ucKeyNumber,2,6,&ucDotCnt_1,&ucDotBitS_1,&ucWdPartCnt_1,ucDataBuffer_1); //設置參數,請看本函數具體內容。本節的核心內容,值得好好研究!
- ucWd1Part1Update=1;//更新顯示
- break;
- }
- break;
- }
- }
- /* 注釋三:
- * 本節的核心函數,值得好好研究!
- * 涉及到參數的4種信息,包括小數點的數量,個數,數據的位置,數組具體值。以及它們之間的相互作用關系。
- * 以下參數,指針類型的參數是讓代入的全局變量在退出函數后維持它當前最新更改的數值不變。
- * 第1個參數ucKeyNumberTemp是當前按鍵輸入的數值。
- * 第2個參數ucDotBitMax是限定被設置參數的小數點最大位數。
- * 第3個參數ucDataCntMax是限定被設置參數的最大數組個數。
- * 第4個參數*p_ucDotCnt是記錄當前輸入的小數點數量,如果小數點的數量不為0,說明當前數組已包含小數點,此時再按小數點按鍵則無效。
- * 第5個參數*p_ucDotBitS是記錄當前輸入的小數點個數,如果小數點的個量如果超過規定2位,此時再按任何輸入按鍵則無效
- * 第6個參數*p_ucWdPartCnt是記錄當前輸入的數據在數組中的位置,方便鎖定每次按鍵輸入的數字顯示位置。
- * 第7個參數*p_ucSetDataBuffer是BCD碼數組緩沖的具體數字內容。
- */
- void set_data(unsigned char ucKeyNumberTemp,unsigned char ucDotBitMax,unsigned char ucDataCntMax,unsigned char *p_ucDotCnt,unsigned char *p_ucDotBitS,unsigned char *p_ucWdPartCnt,unsigned char *p_ucSetDataBuffer)
- {
- unsigned int i;
- if(ucKeyNumberTemp==11) //等于小數點
- {
- if(ucDotBitMax==0) //如果限定的小數點最大數是0,就意味著此數據不允許帶小數點,必須是整數。
- {
- return; //直接返回退出
- }
- else if(*p_ucDotCnt>0)//小數點個數大于0,意味著當前數組已經包含了小數點,此時再輸入小數點則無效。
- {
- return; //直接返回退出
- }
- else//否則有效,記錄當前已經包含一個小數點的信息。
- {
- *p_ucDotCnt=1;//只能包含一個小數點
- }
- }
- else if(*p_ucDotCnt==1) //如果輸入的不是小數點,并且之前已經輸入了一個小數點,那么此時輸入的數字就是小數點后的數據
- {
- if(*p_ucDotBitS
- {
- *p_ucDotBitS=(*p_ucDotBitS)+1;
- }
- else //如果小數點位數已經超過允許的范圍,則輸入的按鍵無效,直接退出。
- {
- return; //直接返回退出
- }
- }
- if(*p_ucWdPartCnt<(ucDataCntMax-1))//當輸入的有效BCD碼不超過最大數組緩沖時
- {
- if(*p_ucWdPartCnt==0&&p_ucSetDataBuffer[0]==0&&ucKeyNumberTemp!=11)//如果當前默認位置是第0個位置,并且默認第0個數據是0,并且當前的按鍵輸入不是小數點,則不用移位
- {
- ;
- }
- else//否則,移位
- {
- for(i=0;i<(ucDataCntMax-1);i++)//移位
- {
- p_ucSetDataBuffer[ucDataCntMax-1-i]=p_ucSetDataBuffer[ucDataCntMax-2-i];
- }
- *p_ucWdPartCnt=(*p_ucWdPartCnt)+1;
- }
- p_ucSetDataBuffer[0]=ucKeyNumberTemp; //當前輸入的數字或者小數點永遠在第右邊第0個位置。
- }
- }
- void key_delete_input(void) //刪除按鍵
- {
- static unsigned int i;
- switch(ucWd)
- {
- case 1: //第1窗口。本節程序只有1個窗口
- switch(ucPart)
- {
- case 1://1窗口第1項
- //清零
- ucDotBitS_1=0;
- ucDotCnt_1=0;
- ucWdPartCnt_1=0;
- for(i=0;i<6;i++)
- {
- ucDataBuffer_1[i]=10;
- }
- ucDataBuffer_1[0]=0; //第0個位置填入0
- ucWd1Part1Update=1;//更新顯示
- break;
- }
- break;
- }
- }
- unsigned char *number_to_matrix(unsigned charucBitNumber)
- {
- unsigned char *p_ucAnyNumber;//此指針根據ucBitNumber數值的大小,分別調用不同的字庫。
- switch(ucBitNumber)//根據ucBitNumber數值的大小,分別調用不同的字庫。
- {
- case 0:
- p_ucAnyNumber=Zf816_0;
- break;
- case 1:
- p_ucAnyNumber=Zf816_1;
- break;
- case 2:
- p_ucAnyNumber=Zf816_2;
- break;
- case 3:
- p_ucAnyNumber=Zf816_3;
- break;
- case 4:
- p_ucAnyNumber=Zf816_4;
- break;
- case 5:
- p_ucAnyNumber=Zf816_5;
- break;
- case 6:
- p_ucAnyNumber=Zf816_6;
- break;
- case 7:
- p_ucAnyNumber=Zf816_7;
- break;
- case 8:
- p_ucAnyNumber=Zf816_8;
- break;
- case 9:
- p_ucAnyNumber=Zf816_9;
- break;
- case 10://空格
- p_ucAnyNumber=Zf816_nc;
- break;
- case 11: //小數點
- p_ucAnyNumber=Zf816_dot;
- break;
- default: //如果上面的條件都不符合,那么默認指向空字模
- p_ucAnyNumber=Zf816_nc;
- break;
- }
- return p_ucAnyNumber;//返回轉換結束后的指針
- }
- void lcd_display_service(void) //應用層面的液晶屏顯示程序
- {
- static unsigned char *p_ucAnyNumber; //經過數字轉換成字模后,分解變量的某位字模首地址
- static unsigned char ucCursorFlag;//光標標志,也就是反顯的標志,它是根據局部變量ucPart來定的
- static unsigned int i;
- switch(ucWd)//本程序的核心變量,窗口顯示變量。類似于一級菜單的變量。代表顯示不同的窗口。
- {
- case 1: //顯示窗口1的數據
- if(ucWd1Update==1)//窗口1整屏更新,里面只放那些不用經常刷新顯示的內容
- {
- ucWd1Update=0;//及時清零,避免一直更新
- ucWd1Part1Update=1; //激活窗口1的第1行局部更新顯示變量,這里在前面數碼管顯示框架上有所改進
- display_clear(0x00); // 清屏操作, 全部顯示空填充0x00,全部顯示點陣用0xff。
- clear_all_canvas();//把畫布全部清零
- display_lattice(0,0,Hz1616_yi,0,2,16,0); //一窗口一行,這些內容不用經常更新,只有在切換窗口的時候才更新顯示
- display_lattice(1,0,Hz1616_xiang,0,2,16,0);
- display_lattice(2,0,Hz1616_shu,0,2,16,0);
- display_lattice(3,0,Hz1616_zhu,0,2,16,0);
- display_lattice(4,0,Zf816_mao_hao,0,1,16,0); //冒號
- }
- if(ucWd1Part1Update==1) //窗口1的第1行局部更新顯示變量,里面放一些經常需要刷新顯示的內容
- {
- ucWd1Part1Update=0; //及時清零,避免一直更新
- if(ucPart==1) //被選中
- {
- ucCursorFlag=1; //反顯 顯示
- }
- else //沒被選中
- {
- ucCursorFlag=0; //正常 顯示
- }
- for(i=0;i<6;i++) //把每個數組緩沖的字模依次插入畫布
- {
- p_ucAnyNumber=number_to_matrix(ucDataBuffer_1[5-i]);
- insert_buffer_to_canvas(i,0,p_ucAnyNumber,0,1,16);//這里的i是畫布的橫向地址,一共可以顯示6個字符,因此取值范圍是0到5
- }
- display_lattice(5,0,ucCanvasBuffer,ucCursorFlag,6,16,0); //顯示整屏的畫布,最后的參數0是偏移量
- }
- break;
- //本程序只有1個窗口,所以只有一個case 1,如果要增加窗口,就直接增加 case 2, case 3...
- }
- }
- void clear_all_canvas(void)//把畫布全部清零
- {
- unsigned int j=0;
- unsigned int i=0;
- for(j=0;j<16;j++)//這里的16表示畫布有16行
- {
- for(i=0;i<4;i++) //這里的4表示畫布每行有4個字節
- {
- ucCanvasBuffer[j*4+i]=0x00;
- }
- }
- }
- void display_clear(unsigned char ucFillDate) // 清屏全部顯示空填充0x00 全部顯示點陣用0xff
- {
- unsigned char x,y;
- WriteCommand(0x34);//關顯示緩沖指令
- WriteCommand(0x34);//關顯示緩沖指令故意寫2次,怕1次關不了 這個是因為我參考到某廠家的驅動程序也是這樣寫的
- y=0;
- while(y<32)//y軸的范圍0至31
- {
- WriteCommand(y+0x80); //垂直地址
- WriteCommand(0x80); //水平地址
- for(x=0;x<32;x++)//256個橫向點,有32個字節
- {
- LCDWriteData(ucFillDate);
- }
- y++;
- }
- WriteCommand(0x36); //開顯示緩沖指令
- }
- /* 注釋四:
- * 注意,這節內容的畫布跟前面章節的畫布大小不一樣,前面章節的橫向是4個字節,這節的橫向是6個字節。
- * 把字模插入畫布的函數.
- * 這是本節的核心函數,讀者尤其要搞懂x_amount和y_amount對應的顯示關系。
- * 第1,2個參數x,y是在畫布中的坐標體系。
- * x的范圍是0至5,因為畫布的橫向只要6個字節。y的范圍是0至15,因為畫布的縱向只有16行。
- * 第3個參數*ucArray是字模的數組。
- * 第4個參數ucFbFlag是反白顯示標志。0代表正常顯示,1代表反白顯示。
- * 第5,6個參數x_amount,y_amount分別代表字模數組的橫向有多少個字節,縱向有幾橫。
- */
- void insert_buffer_to_canvas(unsigned int x,unsigned int y,const unsigned char*ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount)
- {
- unsigned int j=0;
- unsigned int i=0;
- unsigned char ucTemp;
- for(j=0;j
- {
- for(i=0;i
- {
- ucTemp=ucArray[j*x_amount+i];
- if(ucFbFlag==0)
- {
- ucCanvasBuffer[(y+j)*6+x+i]=ucTemp; //這里的6代表畫布每一行只有6個字節。前面章節的橫向是4個字節,要稍微注意的。
- }
- else
- {
- ucCanvasBuffer[(y+j)*6+x+i]=~ucTemp; //這里的6代表畫布每一行只有6個字節。前面章節的橫向是4個字節,要稍微注意的。
- }
- }
- }
- }
- /* 注釋五:
- * 顯示任意點陣函數.
- * 注意,本函數在前幾節的基礎上多增加了第7個參數uiOffSetAddr,它是偏移地址。
- * 對于這個函數,讀者尤其要搞懂x_amount和y_amount對應的顯示關系。
- * 第1,2個參數x,y是坐標體系。x的范圍是0至15,y的范圍是0至31.
- * 第3個參數*ucArray是字模的數組。
- * 第4個參數ucFbFlag是反白顯示標志。0代表正常顯示,1代表反白顯示。
- * 第5,6個參數x_amount,y_amount分別代表字模數組的橫向有多少個字節,縱向有幾橫。
- * 第7個參數uiOffSetAddr是偏移地址,代表字模數組的從第幾個數據開始顯示。
- */
- void display_lattice(unsigned int x,unsigned int y,const unsigned char*ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount,unsigned int uiOffSetAddr)
- {
- unsigned int j=0;
- unsigned int i=0;
- unsigned char ucTemp;
- //注意,要把以下兩行指令屏蔽,否則屏幕在更新顯示時會整屏閃動
- //WriteCommand(0x34);//關顯示緩沖指令
- //WriteCommand(0x34);//關顯示緩沖指令故意寫2次,怕1次關不了 這個是因為我參考到某廠家的驅動程序也是這樣寫的
- for(j=0;j
- {
- WriteCommand(y+j+0x80); //垂直地址
- WriteCommand(x+0x80); //水平地址
- for(i=0;i
- {
- ucTemp=ucArray[j*x_amount+i+uiOffSetAddr]; //uiOffSetAddr是字模數組的偏移地址
- if(ucFbFlag==1)//反白顯示
- {
- ucTemp=~ucTemp;
- }
- LCDWriteData(ucTemp);
- // delay_short(30000);//把上一節這個延時函數去掉,加快刷屏速度
- }
- }
- WriteCommand(0x36); //開顯示緩沖指令
- }
- void SendByteToLcd(unsigned char ucData)//發送一個字節數據到液晶模塊
- {
- unsigned char i;
- for ( i = 0; i < 8; i++ )
- {
- if ( (ucData << i) & 0x80 )
- {
- LCDSID_dr = 1;
- }
- else
- {
- LCDSID_dr = 0;
- }
- LCDCLK_dr = 0;
- LCDCLK_dr = 1;
- }
- }
- void SPIWrite(unsigned char ucWData, unsigned char ucWRS) //模擬SPI發送一個字節的命令或者數據給液晶模塊的底層驅動
- {
- SendByteToLcd( 0xf8 + (ucWRS << 1) );
- SendByteToLcd( ucWData & 0xf0 );
- SendByteToLcd( (ucWData << 4) & 0xf0);
- }
- void WriteCommand(unsigned char ucCommand) //發送一個字節的命令給液晶模塊
- {
- LCDCS_dr = 0;
- LCDCS_dr = 1;
- SPIWrite(ucCommand, 0);
- delay_short(90);
- }
- void LCDWriteData(unsigned char ucData)//發送一個字節的數據給液晶模塊
- {
- LCDCS_dr = 0;
- LCDCS_dr = 1;
- SPIWrite(ucData, 1);
- }
- void LCDInit(void) //初始化函數內部包括液晶模塊的復位
- {
- LCDRST_dr = 1;//復位
- LCDRST_dr = 0;
- LCDRST_dr = 1;
- }
- void delay_short(unsigned int uiDelayShort) //延時函數
- {
- unsigned int i;
- for(i=0;i
- {
- ;
- }
- }
- void delay_long(unsigned int uiDelayLong)
- {
- unsigned int i;
- unsigned int j;
- for(i=0;i
- {
- for(j=0;j<500;j++)//內嵌循環的空指令數量
- {
- ; //一個分號相當于執行一條空語句
- }
- }
- }
總結陳詞:
這節講的是鍵盤輸入數字或者小數點的BCD碼用來顯示,實際項目中,我們經常要知道所輸入的BCD碼數組到底有效數值是多少,這個該怎么辦?欲知詳情,請聽下回分解----
實時同步把鍵盤輸入的BCD碼數組轉換成數值的液晶屏顯示程序。


評論