单片机抢答器V1.2.0.0

页左侧有关于抢答器的更新!

又修改了几个BUG...一个是修改了原来在抢答过程中还可以调整倒计时时间的BUG..一个是修改了错误的利用编译器的循环来实现自身程序的循环的错误

另外还有一点心得和发现

1 关于keil编译器的问题(就是为什么编译器会一直自动的重复运行main函数)
keil编译器自动在主程序后面加上一条跳转指令, 但是不一定会跳转到什么地方, 一般程序小的时候基本是跳转到我们没有使用的地方..然后接着运行至ffff(这个是视程序所存在的ROM大小而定), 再跳回main函数的开始继续运行..所以在编程过程中如果想使程序停止,则要加上while(1); 如果是让程序反复运行某个部分, 则要自己加上无限的循环, 一般最好不要利用编译器的那个反复运行main函数的特点.

2 P1口位数的顺序
基本是这样的P0^7-P0^6-P0^5-P0^4-P0^3-P0^2-P0^1-P0^0 就是这个字节的各个位数的排列. 显然, 高四位为P0^7-P0^4, 低四位为P0^3-P0^0, 例如, P1口的状态从P0^0到P0^7依次为10101010, 则它所对应的二进制为01010101, 顺序反过来了..

程序如下

#include <REG51.h>

/******************************************************************
*    自定义Macro
*******************************************************************/
#define LED_BEGIN 0xfe            // 定义开始时数码管的显示
#define LED_FOUL 0x47             // 犯规后显示字母"F",数码管编码
#define LED_C 0x4e                // 字母"C"的编码
#define LED_L 0x0e                // 字母"L"的编码,两个用来在主持人取消之后显示"CL"--cancel
#define GET 1                     // 这个是作为一个函数的参数来混的,就是成功抢答的意思
#define FOUL 0                    // 和上面的参数一起混的,犯规---这两个的用法在后面体现

/******************************************************************
*    自定义数据类型
*******************************************************************/
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^1;              // 主持人按键,用来重新开始的按键
sbit HOST_CANCEL=P0^2;           //主持人用来取消抢答的按键

sbit SWITCH1_3=P1^4;    // 调节倒计时时间的拨码开关,下划线前面的号代表开关的序号,下划线后面的号代表该开关的数值
sbit SWITCH2_2=P1^5;          // 同上
sbit SWITCH3_2=P1^6;          // 同上
sbit SWITCH4_1=P1^7;          // 同上

sbit BEEP=P0^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口上次状态的一个变量

/***********************************************************************
*   共阴极七段数码管显示对应段查询表(数字0-9分别对应code_table[0]-[9])
***********************************************************************/
Byte code code_table[10]=
{0x7e,0x30,0x6d,0x79,0x33,0x5b,0x5f,0x70,0x7f,0x7b};

/***********************************************************************
*   函数声明
***********************************************************************/
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=0; i < 8; i++)
     MAX7219_Write(i, 0x00);//把八个数码管全都清零了
}

/**************************************************************************
* 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,2);//这里设置的是扫描两个数码管
    MAX7219_Write(REG_DECODE, 0x00);
    MAX7219_ShutdownStop();
    MAX7219_SetBrightness(INTENSITY_MAX);//设置最大亮度显示
MAX7219_DisplayTestStart();
    MAX7219_DisplayTestStop();
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抢答
}
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)//每隔一秒的操作
{
   if (showNum!=1) SPEAKER_count();//倒计时声音
   else SPEAKER_start();//开始抢答的声音
   MAX7219_DisplayChar(DIG_1,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<25;i++)
{
   BEEP =1; //点亮
   delayus(20);
   BEEP =0; //熄灭
   delayus(15);
}
}
void SPEAKER_foul(void)
{
unsigned char i;
for(i=0;i<100;i++)
{
   BEEP =1; //点亮
   delayus(5);
   BEEP =0; //熄灭
   delayus(5);
}
   for(i=0;i<100;i++)
{
   BEEP =1; //点亮
   delayus(20);
   BEEP =0; //熄灭
   delayus(20);
}
}
void SPEAKER_get(void)
{
unsigned char i;
for(i=0;i<10;i++)
{
   BEEP =1; //点亮
   delayus(40);
   BEEP =0; //熄灭
   delayus(40);
}
}

/**************************************************************************
* 主程序
***************************************************************************/
void main()
{
Byte keyPressed,i;//选手按键号码,没有的话为0
Bool hostPressed;//用来记录主持人按键取消,0为没有动作,1为取消
number_temp=P1&0xf0;//P1口上次的状态,在调整倒计时时间的时候用到的  

MAX7219_Init();//数码管初始化

GetCounter();//获取开始时候设置的倒计时时间
MAX7219_DisplayChar(DIG_1,code_table[beginNum]);//显示开始时设置的倒计时时间

while(HOST_SWITCH==1)//当主持人没有按键的时候进入循环
{
   if (number_temp!=(P1&0xf0))//若调整了倒计时时间,则P1口状态变了,就要重新设置和显示
   {
    GetCounter();//获取调整以后的倒计时时间
    MAX7219_DisplayChar(DIG_1,code_table[beginNum]);//显示调整以后的倒计时时间
    number_temp=P1&0xf0;//记录下来现在P1口的状态,以备后面的比较
   }   
} //当主持人按键以后就结束调整进入抢答倒计时
    counterBack=intrCounter;
   //这里原来写的是while(1),写上后就不行了,不知道为何..
while(1)
{
   showNum=beginNum;//设置要显示的时间,当然时从倒计时时间开始
   intrCounter=counterBack;
   TR0=0;//禁用计时器0
   isPressed=0;//记录是否有人按键
   isStart=0;//没有开始抢答
   while(GetHostStartKey()==0);
   IT0_Init();//初始化计时器0, 启用.
   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);//处理抢答
    }
    else//否则,没有开始抢答
    {
     PressedHandle(keyPressed);//处理按键,即显示抢答选手号码
     GetOrFoulHandle(FOUL);//处理犯规,必须要放在后面,因为显示数字的里面有一个clear
     //SPEAKER_foul();//犯规发声
    }
   }
   if (hostPressed==1)
   {
    CancelHandle();
    for (i=100;i--;i>0)
     Delay10ms();//防止后面出现连读的情况..
   }
   //到这里一次循环结束
   while(GetHostCancelKey()==0);//如果主持人没有按键再次开始,则停在次死循环处
   MAX7219_Write(DIG_2,0x00);//清空右边一位数码管
   MAX7219_Write(DIG_1,code_table[beginNum]);//显示设置的倒计时时间
}
}

你可能感兴趣的:(timer,table,character,byte,编译器,delay)