/*******************************************************************
* LCD1602顯示 *
*【版權】Copyright(C) JAS All Rights Reserved *
*【聲明】此程序僅用于學習與參考,引用請注明版權和作者信息! *
* *
********************************************************************
* *
* 描述: *
* F--- 模擬出題控制開關; C--- 輸入正確答案; *
* (0-9)--- 有效數字輸入; *
* 答題正確,蜂鳴器響4聲。 *
* *
*******************************************************************/
#include < reg51.h >
#include < intrins.h >
#define uchar unsigned char
#define uint unsigned int
bit F_in=0,truer;
sbit LCD_RS = P2^0;
sbit LCD_RW = P2^1;
sbit LCD_EN = P2^2;
uchar code cdis1[ ] = {" MULTIPLICATION "};
uchar code cdis2[ ] = {"TABLE: 0*0=00 "};
#define delayNOP(); {_nop_();_nop_();_nop_();_nop_();};
sbit BEEP = P3^7; //蜂鳴器驅動線
uchar key_buf; //顯示緩存
uchar temp;
uchar key,key_num=0; //鍵順序嗎
uchar data testdata[] = {0x00,0x00,0x00,0x00};
uchar data in_data[] = {0x01,0x02};
uchar shift ;
/**********************************************************
延時子程序
**********************************************************/
void delay(uint ms)
{
uchar t;
while(ms--)
{
for(t = 0; t < 120; t++);
}
}
/*************************************************************/
/* */
/* 延時 x*0.14ms */
/* */
/*************************************************************/
void delay0(uchar x)
{
unsigned char i;
while(x--)
{
for (i = 0; i<13; i++) {}
}
}
/*************************************************************/
/* */
/* 蜂鳴器響一聲 */
/* */
/*************************************************************/
void beep()
{
unsigned char i;
for (i=0;i<180;i++)
{
delay0(6);
BEEP=!BEEP; //BEEP取反
}
BEEP=1; //關閉蜂鳴器
delay(250); //延時
}
/*************************************************************/
/* */
/*檢查LCD忙狀態 */
/*lcd_busy為1時,忙,等待。lcd-busy為0時,閑,可寫指令與數據 */
/* */
/*************************************************************/
bit lcd_busy()
{
bit result;
LCD_RS = 0;
LCD_RW = 1;
LCD_EN = 1;
delayNOP();
result = (bit)(P0&0x80);
LCD_EN = 0;
return(result);
}
/*******************************************************************/
/* */
/*寫指令數據到LCD */
/*RS=L,RW=L,E=高脈沖,D0-D7=指令碼。 */
/* */
/*******************************************************************/
void lcd_wcmd(uchar cmd)
{
while(lcd_busy());
LCD_RS = 0;
LCD_RW = 0;
LCD_EN = 0;
_nop_();
_nop_();
P0 = cmd;
delayNOP();
LCD_EN = 1;
delayNOP();
LCD_EN = 0;
}
/*******************************************************************/
/* */
/*寫顯示數據到LCD */
/*RS=H,RW=L,E=高脈沖,D0-D7=數據。 */
/* */
/*******************************************************************/
void lcd_wdat(uchar dat)
{
while(lcd_busy());
LCD_RS = 1;
LCD_RW = 0;
LCD_EN = 0;
P0 = dat;
delayNOP();
LCD_EN = 1;
delayNOP();
LCD_EN = 0;
}
/*************************************************************/
/* */
/* LCD初始化設定 */
/* */
/*************************************************************/
void lcd_init()
{
delay(15);
lcd_wcmd(0x38); //16*2顯示,5*7點陣,8位數據
delay(5);
lcd_wcmd(0x38);
delay(5);
lcd_wcmd(0x38);
delay(5);
lcd_wcmd(0x0c); //顯示開,關光標
delay(5);
lcd_wcmd(0x06); //移動光標
delay(5);
lcd_wcmd(0x01); //清除LCD的顯示內容
delay(5);
}
/*************************************************************/
/* */
/* 設定顯示位置 */
/* */
/*************************************************************/
void lcd_pos(uchar pos)
{
lcd_wcmd(pos | 0x80); //數據指針=80+地址變量
}
/*************************************************************
鍵掃描子程序
*************************************************************/
void keyscan(void)
{
P1=0x0F; //低四位輸入
delay(1);
temp=P1; //讀P1口
temp=temp&0x0F;
temp=~(temp|0xF0);
if(temp==1)
key=0;
else if(temp==2)
key=1;
else if(temp==4)
key=2;
else if(temp==8)
key=3;
else
key=16;
P1=0xF0; //高四位輸入
delay(1);
temp=P1; //讀P1口
temp=temp&0xF0;
temp=~((temp>>4)|0xF0);
if(temp==1)
key=key+0;
else if(temp==2)
key=key+4;
else if(temp==4)
key=key+8;
else if(temp==8)
key=key+12;
else
key=16;
key_buf = key; //鍵值入顯示緩存
key_buf = key_buf & 0x0f;
}
/*************************************************************
判斷鍵是否按下
*************************************************************/
void keydown(void)
{
P1=0xF0;
while(P1==0xf0);
{
keyscan();
beep();
}
}
/**********************************************************
隨機出題函數
**********************************************************/
void rubric()
{
uchar num1,num2,temp1;
TR1=0;
temp1=TL1; //取T1當前的計數值
temp1=~temp1; //取反得到對應的題號
num2=temp1/9; //就題目分成9組
num2++; //將組號0~8轉換為被乘數1~9
testdata[3]=num2+0x30; //保存被乘數
num1=temp1%9;
num1++; //將組號0~8轉換為乘數1~9。
testdata[2]=num1+0x30; //保存乘數
temp1=num2*num1; //計算乘積
testdata[1]=temp1/10+0x30; //乘積的高位
testdata[0]=temp1%10+0x30; //乘積的低位
TR1=1;
}
/**********************************************************
鍵入正確答案函數
**********************************************************/
void right()
{
if(testdata[1]==0x30)
{
testdata[1]=0x20;
}
lcd_pos(0x4b);
in_data[1]=testdata[1];
in_data[0]=testdata[0];
lcd_wdat(in_data[1]);
lcd_wdat(in_data[0]);
}
/**********************************************************
鍵入答案函數
**********************************************************/
void key_input()
{
if(testdata[1]==0x30) //先判斷十位數是否為0
{
in_data[1]=0x30;
key_num=1;
}
else
{
keydown();
if(key_buf==0x0c) //C 鍵顯示正確答案
{
right();
key_num=0;
}
if(key_buf<10)
{
key_buf=key_buf+0x30; //轉換為ASCII碼
in_data[1]=key_buf; //輸入乘積高位
lcd_pos(0x4b);
lcd_wdat(in_data[1]); //顯示乘積高位
lcd_wdat(0x20);
key_num=1;
}
}
if(key_num==1)
{
keydown();
if(key_buf==0x0c) //C 鍵顯示正確答案
{
right();
key_num=0;
}
if(key_buf<10)
{
key_buf=key_buf+0x30; //轉換為ASCII碼
in_data[0]=key_buf; //輸入乘積低位
lcd_pos(0x4c);
lcd_wdat(in_data[0]); //顯示乘積低位
key_num=0;
}
}
}
/**********************************************************
數據比較函數
**********************************************************/
void datacomp()
{
if(testdata[1]-in_data[1]==0) //比較乘積高位
{
if(testdata[0]-in_data[0]==0) //比較乘積低位
{
beep(); //計算正確蜂鳴器響3聲
beep();
beep();
truer=1; //比較正確
F_in=0; //可以重新出題
}
else truer=0; //比較錯誤
}
else truer=0; //比較錯誤
if(truer==0)
{
lcd_pos(0x4B); //清除錯誤答案
lcd_wdat(0x20);
lcd_wdat(0x3f);
F_in=1; //禁止重新出題
}
}
/**********************************************************
主函數
**********************************************************/
main()
{
uchar m;
lcd_init(); //初始化LCD
lcd_pos(0x00); //設置顯示位置為第一行
for(m=0;m<16;m++)
lcd_wdat(cdis1
-);
lcd_pos(0x40); //設置顯示位置為第二行
for(m=0;m<16;m++)
lcd_wdat(cdis2
-);
TMOD=0x21; //將T1設置為8位自動重裝工作方式。
TH1=175; //對T1定時常數進行預置。
TL1=175;
TR1=1; //啟動T1。
while(1)
{
keydown();
if(key_buf==0x0f) //F 鍵為模擬出題開關
{
rubric();
lcd_pos(0x47);
lcd_wdat(testdata[3]); //顯示被乘數
lcd_pos(0x49);
lcd_wdat(testdata[2]); //顯示乘數
lcd_pos(0x4B);
lcd_wdat(0x20);
lcd_wdat(0x3f); //顯示 ?
key_buf=0x00;
F_in=1; //出題目完畢標志
}
while(F_in==1)
{
key_input();
datacomp();
}
}
}
/*********************************************************/