|
/********************* * 自定义Macro ***************/ //编码的均为反向编码 #define CLEAR 0x7f //定义清空的反码 #define LED_BEGIN 0x01 // 定义开始时数码管的显示 #define LED_FOUL 0x38 // 犯规后显示字母"F",数码管编码 #define LED_C 0x31 // 字母"C"的编码 #define LED_L 0x71 // 字母"L"的编码,两个用来在主持人取消之后显示"CL"--cancel #define GET 1 // 这个是作为一个函数的参数来混的,就是成功抢答的意思 #define FOUL 0 // 和上面的参数一起混的,犯规---这两个的用法在后面体现 #define READY 0x7e /********************* * 自定义数据类型 *****************************/ typedef unsigned char Byte; // 一个字节 typedef unsigned int Word; // 一个字,两个字节 typedef bit Bool; // 模仿布尔型变量 //typedef sbit Port; // 本想用自定义一个端口类型的变量,比较方便,但是这句话步知道为何通不过编译 /************************* * 定义MAX7219寄存器 *************************/ #define REG_NO_OP 0x00 // 定义空操作 register #define DIG_1 0x01 // 定义数码管1 register #define DIG_2 0x02 // 定义数码管2 register #define DIG_3 0x03 // 定义数码管3 register #define DIG_4 0x04 // 定义数码管4 register #define DIG_5 0x05 // 定义数码管5 register #define DIG_6 0x06 // 定义数码管6 register #define DIG_7 0x07 // 定义数码管7 register #define DIG_8 0x08 // 定义数码管8 register #define REG_DECODE 0x09 // 定义解码控制 register #define REG_INTENSITY 0x0a // 定义显示亮度 register #define REG_SCAN_LIMIT 0x0b // 定义扫描限制 register #define REG_SHUTDOWN 0x0c // 定义"shutdown"模式 register #define REG_DISPLAY_TEST 0x0f // 定义"display test"模式 register #define INTENSITY_MIN 0x00 // 定义最低显示亮度 #define INTENSITY_MAX 0x0f // 定义最高显示亮度 /*************************** * 定义硬件引脚连接 ****************************/ sbit DATA=P2^0; // MAX7219的数据口 sbit LOAD=P2^1; // MAX7219的锁存端口 sbit CLK=P2^2; // MAX7219的时钟端口 //sbit HOST_SWITCH=P0^0; // 主持人开关的接口 sbit HOST_START=P0^0; //主持人按键,用来重新开始的按键 start sbit HOST_CANCEL=P0^1; //主持人用来取消抢答的按键 clear sbit SWITCH1_3=P1^4; // 调节倒计时时间的拨码开关,下划线前面的号代表开关的序号,下划线后面的号代表该开关的数值 sbit SWITCH2_2=P1^5; // 同上 sbit SWITCH3_2=P1^6; // 同上 sbit SWITCH4_1=P1^7; // 同上 sbit BEEP=P0^7; //定义蜂鸣器端口 sbit LS138_C=P2^4; //定义译码器输入端 sbit LS138_B=P2^5; //同上 sbit LS138_A=P2^6; //同上 sbit LS138_E1=P2^7; //定义译码器使能端 /************************* * 定义全局变量 ***********************/ Byte data intrCounter; // 计时器中断次数 Byte data beginNum; // 开始倒计时的时间 Byte data counterBack; // 将中断次数放在里面以备后用 Byte data showNum; // 数码管正在显示的时间 Bool data isStart; // 是否开始抢答 Bool data isFoul; // 是否犯规 Bool data isPressed; // 是否有抢答的键按下 Byte data number_temp; // 用来记录P1口上次状态的一个变量 code unsigned char C51BOX2[3] _at_ 0x43; /********************************* * 共阴极七段数码管显示对应段查询表(数字0-9分别对应code_table[0]-[9]) ****************************/ Byte code code_table_zheng[10]= {0x7e,0x30,0x6d,0x79,0x33,0x5b,0x5f,0x70,0x7f,0x7b}; Byte code code_table[10]= {0x01,0x4f,0x12,0x06,0x4c,0x24,0x20,0x0f,0x00,0x04}; /************************** * 函数声明 ****************************/ void MAX7219_SendByte (Byte dataout); void MAX7219_Write (Byte reg_number, Byte dataout); void MAX7219_DisplayChar(Byte digit, Byte character); void MAX7219_Clear (void); void MAX7219_SetBrightness (Byte brightness); void MAX7219_DisplayTestStart (void); void MAX7219_DisplayTestStop (void); void MAX7219_ShutdownStart (void); void MAX7219_ShutdownStop (void); void MAX7219_Init (void); void Delay10ms(void); Bool GetHostStartKey (void); Bool GetHostCancelKey (void); void GetCounter(void); Byte GetPressed(Byte KeyState); void IT0_Init(void); void Timer0_Overflow(); void PressedHandle(Byte keyPressed); void GetOrFoulHandle(Bool state); void CancelHandle(); void SPEAKER_count (void); //声明倒计时声音函数 void SPEAKER_start(void); //声明开始抢答声音函数 void SPEAKER_get(void); //声明抢到声音函数 void SPEAKER_foul(void); // 声明犯规声音函数 /*********************** * MAX7219_SendByte() * * 描述: 向MAX7219传送一个字节的数据 * Arguments : dataout = data to send * Returns : none ***************************/ void MAX7219_SendByte (Byte dataout) { Byte i; for (i=8;i>0;i--) { Byte mask=1<<(i-1);//mask是个掩码,取位使用 CLK=0;//MAX7219的位传入是在时钟的上升沿之前,所以在每发一位之前都要变为低电平 if (dataout&mask) DATA=1; else DATA=0; CLK=1;//八个bit都传递完成后变为高电平,锁存 } } /************************* * MAX7219_Write() * * 描述: 向 MAX7219 写命令 * Arguments : reg_number = register to write to * dataout = data to write to MAX7219 * Returns : none 未完~ ****************************/ void MAX7219_Write (Byte reg_number, Byte dataout) { LOAD=0;//也是锁存上升沿之前的,发这两个字节之前要变为低电平 MAX7219_SendByte(reg_number);//发送寄存器地址 MAX7219_SendByte(dataout);//发送数据 LOAD=1;//变为高电平,锁存 } /********************** * MAX7219_DisplayChar() * * 描述: 使某一位显示一个数字 * Arguments : digit = digit number (0-7) * character = character to display (0-9, A-Z) * Returns : none ********************/ void MAX7219_DisplayChar(Byte digit, Byte character) { MAX7219_Write(digit, character); } /************************* * MAX7219_Clear() * * 描述: 清除所有位的显示 * Arguments : none * Returns : none ***************************/ void MAX7219_Clear (void) { Byte i; for (i=1; i<=2; i++) MAX7219_Write(i, CLEAR);//把八个数码管全都清零了,已经写反了^_^ } /********************* * MAX7219_SetBrightness() * * 描述: 设置数码管显示亮度 * Arguments : brightness (0-15) * Returns : none **************************/ void MAX7219_SetBrightness (Byte brightness) { brightness &= 0x0f; MAX7219_Write(REG_INTENSITY, brightness); } /*************************** * MAX7219_DisplayTestStart() * * 描述: 进入 test 模式 * Arguments : none * Returns : none ******************************/ void MAX7219_DisplayTestStart (void) { MAX7219_Write(REG_DISPLAY_TEST, 1); } /********************************* * MAX7219_DisplayTestStop() * * 描述: 退出 test 模式 * Arguments : none * Returns : none *********************************/ void MAX7219_DisplayTestStop (void) { MAX7219_Write(REG_DISPLAY_TEST, 0); } /************************************ * MAX7219_ShutdownStart() * * 描述: 进入 shutdown 模式 * Arguments : none * Returns : none ***************************************/ void MAX7219_ShutdownStart (void) { MAX7219_Write(REG_SHUTDOWN, 0); } /*********************************** * MAX7219_ShutdownStop() * * 描述: 退出 shutdown 模式 * Arguments : none * Returns : none *************************************/ void MAX7219_ShutdownStop (void) { MAX7219_Write(REG_SHUTDOWN, 1); } /***************************** * MAX7219_Init() * * Description: MAX7219初始化模块; 应该先于其他MAX7219函数而被调用 * Arguments : none * Returns : none *************************/ void MAX7219_Init (void) { DATA=1; CLK=1; LOAD=1; MAX7219_Write(REG_SCAN_LIMIT,1);//这里设置的是扫描两个数码管 MAX7219_Write(REG_DECODE, 0x00); MAX7219_SetBrightness(INTENSITY_MAX);//设置最大亮度显示 MAX7219_DisplayTestStart(); MAX7219_DisplayTestStop(); MAX7219_ShutdownStop(); MAX7219_Clear(); } /************************ * Delay_100us() * * 描述: 延时100us,主要用在消除开关抖动时 * Arguments : none * Returns : none *********************/ void Delay10ms(void) { unsigned char i,j; for(i=20;i>0;i--) for(j=248;j>0;j--); } /************************ * GetHostStartKey() * * Description: 取得主持人开始按键的键值 * Arguments : none * Returns : 1-->主持人按键; 0-->主持人未按键 ****************************/ Bool GetHostStartKey (void) { if (HOST_START ==1) return 0; else Delay10ms ();//如果发现主持人按键接通,要先延时100us,防止抖动 if (HOST_START==1) return 0; else return 1;//延时时候还是接通,则判断为该键确实按下 } /************************************** * GetHostCancelKey() * * Description: 取得主持人取消按键的键值 * Arguments : none * Returns : 1-->主持人按键; 0-->主持人未按键 **********************************/ Bool GetHostCancelKey (void) { if (HOST_CANCEL ==1) return 0; else Delay10ms ();//如果发现主持人按键接通,要先延时100us,防止抖动 if (HOST_CANCEL ==1) return 0; else return 1;//延时时候还是接通,则判断为该键确实按下 } /********************************* * GetCounter * * Description: 取得预先设置的倒计时时间 * Arguments : none * Returns : none *******************************/ void GetCounter(void) { beginNum=1;//在所有开关都没有拨动的时候倒计时为1秒,比设置为0秒要好 intrCounter=20;//每一秒对应的中断次数为20次 if (SWITCH1_3==1) { beginNum+=3; } if (SWITCH2_2==1) { beginNum+=2; } if (SWITCH3_2==1) { beginNum+=2; } if (SWITCH4_1==1) { beginNum+=1; }//以上判断语句为判断拨码开关状态 intrCounter=20*beginNum;//计算总扫描次数 } /******************************* * GetPressed * * Description: 从P1口连接抢答端的四位来判断抢答情况 * Arguments : Byte KeyState-->P1 state * Returns : 抢答端的号码 ; 0-->没人抢答 **********************************/ Byte GetPressed(Byte KeyState) { Byte key;//记录抢答端的号码 KeyState&=0x0f;//取P1口的低四位
switch (KeyState) { case 0x0f: key=0;break;//全高,无人抢答 case 0x0e: key=1;break;//只有P1.1,key1抢答 case 0x0d: key=2;break;//只有P1.2,key2抢答 case 0x0b: key=3;break;//只有P1.3,key3抢答 case 0x07: key=4;break;//只有P1.4,key4抢答 } /* switch (KeyState) { case 0x00: key=0;break;//全高,无人抢答 case 0x01: key=1;break;//只有P1.0,key1抢答 case 0x02: key=2;break;//只有P1.1,key2抢答 case 0x04: key=3;break;//只有P1.2,key3抢答 case 0x08: key=4;break;//只有P1.3,key4抢答 } */ //上面是在用高电平来判断抢答状态时的程序,经证明不知道为何无效 return key; } /***************************** * IT0_Init * * Description: 初始化计时器T0的状态 * Arguments : none * Returns : none ********************************/ void IT0_Init(void) { TMOD=0x01;//设置T0在方式1下工作 TH0=0x3C; TL0=0xAF;//这两个寄存器存的是计数器的计数开始的值,计算发现这两个值累加至溢出后正好是50ms ET0=1;//使T0中断可以溢出 EA=1;//开启总中断 TF0=0;//溢出位清零 TR0=1;//开启T0 } /****************************** * Timer0_Overflow() interrupt 1 * * Description: 中断溢出服务程序, 采用的是中断方式1, 后面最好不加using选择寄存器组以免与系统用在主程序的寄存器冲突 * Arguments : none * Returns : none *********************************/ void Timer0_Overflow() interrupt 1 { static Byte second=20;//用20次中断来判断1秒 TH0=0x3C; TL0=0xAF; second--; intrCounter--; /* if (second==0)//每隔一秒的操作 { MAX7219_DisplayChar(DIG_2,code_table[--showNum]); //要避免用上面的会造成显示"0"后面一秒钟,才进入开始 second=20;//重新赋值每秒计数器 if (intrCounter==0) { TR0=0;//关闭T0计数器 isStart=1;//计时结束,进入正常抢答 //SPEAKER_start();//开始抢答的声音 } //待显示"0"以后就开始抢答 else { //SPEAKER_count();//倒计时声音 } } */ if (second==0)//每隔一秒的操作 { //if (showNum!=1) SPEAKER_count();//倒计时声音 //else SPEAKER_start();//开始抢答的声音 MAX7219_DisplayChar(DIG_2,code_table[--showNum]);//显示数字 second=20;//重新赋值每秒计数器 }//待显示"0"以后就开始抢答 if (intrCounter==0) { TR0=0;//关闭T0计数器 isStart=1;//计时结束,进入正常抢答 } } /******************************* * PressedHandle * * Description: 按键处理 * Arguments : Byte keyPressed-->按下的按键 * Returns : none ********************************/ void PressedHandle(Byte keyPressed) { MAX7219_Clear();//LED clear MAX7219_DisplayChar(DIG_2,code_table[keyPressed]);//在右侧数码管显示抢答选手号码,此时没有去判断是否犯规 } /************************* * GetOrFoulHandle(Bool state) * * Description: 正常抢答或是犯规处理 * Arguments : Bool state-->是GET和FOUL两个宏的取之之一 * Returns : none **********************************/ void GetOrFoulHandle(Bool state) { if (!state) { MAX7219_DisplayChar(DIG_1,LED_FOUL);//如果是犯规的话左边的LED要显示"F",foul } } /******************************** * CancelHandle() * * Description: 处理主持人取消倒计时 * Arguments : none * Returns : none *********************************/ void CancelHandle() { MAX7219_DisplayChar(DIG_1,LED_C); MAX7219_DisplayChar(DIG_2,LED_L);//主持人取消倒计时之后,两个数码管显示"CL"-->cancel } /******************************* * delayus() * * Description: 延时程序 * Arguments : t-->us * Returns : time delayed **************************/ void delayus(unsigned char t ) { unsigned char j; for(;t>0;t--) for(j=19;j>0;j--); } /************************************ * SPEAKER_count/start/foul/get() * * Description: speaker发声程序->计数/开始/犯规/抢答 四种声音 * Arguments : none * Returns : none ******************************/ void SPEAKER_count(void) { unsigned char i; for (i=0;i<10;i++) { BEEP =1; //点亮 delayus(20); BEEP =0; //熄灭 delayus(20); } } void SPEAKER_start(void) { unsigned char i; for(i=0;i<200;i++) { BEEP =1; //点亮 delayus(10); BEEP =0; //熄灭 delayus(10); } } void SPEAKER_foul(void) { unsigned char i; for(i=0;i<250;i++) { BEEP =1; //点亮 delayus(15); BEEP =0; //熄灭 delayus(17); } } void SPEAKER_get(void) { unsigned char i; for(i=0;i<250;i++) { BEEP =1; //点亮 delayus(10); BEEP =0; //熄灭 delayus(10); } for(i=0;i<250;i++) { BEEP =1; //点亮 delayus(20); BEEP =0; //熄灭 delayus(20); } } /**************************** * 主程序 *****************************/ void main() { Byte keyPressed,i;//选手按键号码,没有的话为0 Bool hostPressed;//用来记录主持人按键取消,0为没有动作,1为取消 number_temp=P1&0xf0;//P1口上次的状态,在调整倒计时时间的时候用到的 LS138_E1=1; //译码器初始化
MAX7219_Init();//数码管初始化 GetCounter();//获取开始时候设置的倒计时时间 MAX7219_DisplayChar(DIG_1,code_table[beginNum]);//显示开始时设置的倒计时时间,在左位设置 MAX7219_DisplayChar(DIG_2,READY);//调时间的时候右位的显示
//while(1); while(GetHostStartKey()==0)//当主持人没有按键的时候进入循环 { if (number_temp!=(P1&0xf0))//若调整了倒计时时间,则P1口状态变了,就要重新设置和显示 { GetCounter();//获取调整以后的倒计时时间 MAX7219_DisplayChar(DIG_1,code_table[beginNum]);//显示调整以后的倒计时时间 number_temp=P1&0xf0;//记录下来现在P1口的状态,以备后面的比较 } } //当主持人按键以后就结束调整进入抢答倒计时 MAX7219_DisplayChar(DIG_1,READY); while(GetHostCancelKey()==0); MAX7219_DisplayChar(DIG_1,READY);//清空右边一位数码管 MAX7219_DisplayChar(DIG_2,code_table[beginNum]); for (i=100;i--;i>0) Delay10ms();//防止后面出现连读的情况.. //调整好倒计时时间后,按下start显示"--",再按下cancel则显示倒计时时间,此时可以开始倒计时了.
counterBack=intrCounter; //这里原来写的是while(1),写上后就不行了,不知道为何.. while(1)//这里要用自己加的循环来把程序束缚在这里运行 { showNum=beginNum;//设置要显示的时间,当然时从倒计时时间开始 intrCounter=counterBack;//设置总中断的次数 TR0=0;//禁用计时器0 isPressed=0;//记录是否有人按键 isStart=0;//没有开始抢答 while(GetHostStartKey()==0); IT0_Init();//初始化计时器0, 启用. MAX7219_DisplayChar(DIG_1,CLEAR);//清空左边一位数码管 MAX7219_DisplayChar(DIG_2,code_table[beginNum]); while(!isPressed)//如果没有记录到有人按键就进入 { keyPressed=GetPressed(P1);//查询一下P1口的状态,即按键情况 hostPressed=GetHostCancelKey(); if (!keyPressed&&!hostPressed)//如果没有人按键,就进入下次循环 continue; else { TR0=0;//关闭定时器 isPressed=1;//记录到有人按键,提供条件跳出循环 } } if (keyPressed!=0) { if (isStart)//如果已经开始抢答 { PressedHandle(keyPressed);//处理按键,即显示抢答选手号码 GetOrFoulHandle(GET);//处理抢答 LS138_E1=0; //译码器准备工作 switch (keyPressed) { case 1: LS138_A=0;LS138_B=0;LS138_C=0;break; //1号成功灯亮 case 2: LS138_A=0;LS138_B=1;LS138_C=0;break; //2号成功灯亮 case 3: LS138_A=1;LS138_B=0;LS138_C=0;break; //3号成功灯亮 case 4: LS138_A=1;LS138_B=1;LS138_C=0;break; //4号成功灯亮 default : break; } //SPEAKER_get();//处理抢答 } else//否则,没有开始抢答 { PressedHandle(keyPressed);//处理按键,即显示抢答选手号码 GetOrFoulHandle(FOUL);//处理犯规,必须要放在后面,因为显示数字的里面有一个clear LS138_E1=0; //译码器准备工作 switch (keyPressed) { case 1: LS138_A=0;LS138_B=0;LS138_C=1;break; //1号犯规灯亮 case 2: LS138_A=0;LS138_B=1;LS138_C=1;break; //2号犯规灯亮 case 3: LS138_A=1;LS138_B=0;LS138_C=1;break; //3号犯规灯亮 case 4: LS138_A=1;LS138_B=1;LS138_C=1;break; //4号犯规灯亮 default : break; } //SPEAKER_foul();//犯规发声 } } if (hostPressed==1) { CancelHandle(); for (i=100;i--;i>0) Delay10ms();//防止后面出现连读的情况.. } while(GetHostCancelKey()==0);//如果主持人没有按键再次开始,则停在次死循环处 LS138_E1=1; //关闭译码器 MAX7219_DisplayChar(DIG_1,READY);//清空右边一位数码管 MAX7219_DisplayChar(DIG_2,code_table[beginNum]);//左位显示设置的倒计时时间 //到这里一次循环结束 } }
下一篇链接:基于89S52的电容表
上一篇链接:Keil单片机教程(1)
|