找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2251|回复: 11
打印 上一主题 下一主题
收起左侧

用51单片机制作10路数字抢答器程序修改问题

[复制链接]
跳转到指定楼层
楼主
请各位大佬帮忙看下程序要怎么修改才对,谢谢谢谢!此程序已试过仿真和实物,现象显示不对,蜂鸣器一直响,数码管四位一起从0-9闪烁循环。
具体要求:(1)抢答器同时供 10 名选手或 10 个代表队比赛。 (2)设置一个系统清除和抢答控制开关 S,该开关由主持人控制。 (3) 抢答器具有锁存与显示功能。即选手按动按钮,锁存相应的编号,并在 LED 数码管上显示,同时 扬声器发出报警声响提示。选手抢答实行优先锁存,优先抢答选手的编号一直保持到主持人将系统清除为止。 (4)抢答器具有定时抢答功能,且一次抢答的时间由主持人设定(如 20 秒)。当主持人启动"开始"键后, 定时器进行减计时,同时扬声器发出短暂的声响,声响持续的时间 0.5 秒左右。 (5)参赛选手在设定的时间内进行抢答,抢答有效,定时器停止工作,显示器上显示选手的编号和抢 答的时间,并保持到主持人将系统清除为止。 (6)如果定时时间已到,无人抢答,本次抢答无效,系统报警并禁止抢答,定时显示器上显示 00。
原理图如下:


程序如下:
#include <reg51.h>     //包含单片机寄存器的头文件
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int

uchar LED[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf}; //共阳数码管:0-9,-
int count,qdtime,num_mark,qdtime1=20;
sbit smg_we1 = P2^0;
sbit smg_we2 = P2^1;
sbit smg_we3 = P2^2;
sbit smg_we4 = P2^3;
//选手相关区
sbit LED1=P3^7;
sbit LED2=P3^6;
sbit LED3=P3^5;
sbit LED4=P3^4;
sbit LED5=P3^3;
sbit LED6=P3^2;
sbit LED7=P3^1;
sbit LED8=P3^0;
sbit LED9=P2^6;
sbit LED10=P2^7;//选手抢答“监视灯”
sbit S1=P3^7;
sbit S2=P3^6;
sbit S3=P3^5;
sbit S4=P3^4;
sbit S5=P3^3;
sbit S6=P3^2;
sbit S7=P3^1;
sbit S8=P3^0;
sbit S9=P2^6;
sbit S10=P2^7;//选手抢答按钮
uchar S[]={1,1,1,1,1,1,1,1,1,1};//选手按钮按下标记位,按下后便标记为1
//主持人相关区
sbit S11=P1^0;//清除
sbit S12=P1^1;//抢答开始
sbit S13=P1^2;//加时间
sbit S14=P1^3;//减时间
sbit BeepIO= P1^6;
/******************************************
延时函数
**********************************************/
void delay10ms()  //@12.000MHz
{
unsigned char i, j;
i = 117;
j = 184;
do
{
  while (--j);
} while (--i);
}

void delay(uint i)//i微秒延时
{ while(i--) ;}

/************************************************************
蜂鸣器发音程序区
**************************************************************/
void BEEP()//提示音
{  unsigned int i;
  for(i=0;i<100;i++)//喇叭发声的时间循环
   {
   delay(100);     //参数决定发声的频率,估算值
   BeepIO=!BeepIO;
   }
  BeepIO=1;            //喇叭停止工作,间歇的时间
  delay(5000);
}
void BEEP2()//警告音
{  unsigned int i;
  for(i=0;i<400;i++)//喇叭发声的时间循环
   {
   BeepIO=0;
   delay(40);     //参数决定发声的频率,估算值
   BeepIO=1;
   delay(30);
   }            
  delay(12500);//喇叭停止工作,间歇的时间         
}
void BEEP3()//抢答成功提示音
{  unsigned int i;
   for(i=0;i<400;i++)//喇叭发声的时间循环
   {
   BeepIO=0;
   delay(25);     //参数决定发声的频率
   BeepIO=1;
   delay(25);
   }            
  delay(20000);//喇叭停止工作,间歇的时间
}
void shumaxs(int n,uchar m)//数码管显示程序
{ int sj1,sj2,dw1,dw2;
P0=0xff;
P1=0x00;
delay(50);
  sj1=n/10;
sj2=n%10;
dw1=m/10;
dw2=m%10;
  P0=LED[sj1];smg_we4=1;delay(20);
  P0=LED[sj2];smg_we3=1;delay(20);
P0=LED[dw1];smg_we2=1;delay(20);
P0=LED[dw2];smg_we1=1;delay(20);  
}
void Timer0_init (void)//T0初始化子函数
{
TMOD=0x01;//T0设置为16位定时方式
  TH0=0x3c;
  TL0=0xb0;//50ms
  ET0=1;  //允许T0中断
  EA=1;
// TR0=0;     //关闭定时开关
}
void timer0() interrupt 1  //倒计时
{
   TH0=0x3c;
  TL0=0xb0;     
  count++;
  if(count==20)
  { count=0;
   qdtime--;   
  }
}
void key1()
{ S[1]=1;S[2]=1;S[3]=1;S[4]=1;S[5]=1;S[6]=1;S[7]=1;S[8]=1;S[9]=1;S[10]=1;//选手按钮按下标记位归零
if(S12==1)
{ delay10ms();if(S12==1)
  { TR0=1;
   {switch(P3&&0xff)
    { case 0x7f: num_mark=1;LED1=0;break;
     case 0xbf: num_mark=2;LED2=0;break;
     case 0xdf: num_mark=3;LED3=0;break;
     case 0xef: num_mark=4;LED4=0;break;
     case 0xf7: num_mark=5;LED5=0;break;
     case 0xfb: num_mark=6;LED6=0;break;
     case 0xfd: num_mark=7;LED7=0;break;
     case 0xfe: num_mark=8;LED8=0;break;
     
    }
    switch(P2&&0xff)
    { case 0x7f: num_mark=9;LED9=0;break;
     case 0xbf: num_mark=10;LED10=0;break;
    }
  }
   if((num_mark!=0)&&(qdtime!=0))  
   { TR0=0;
    shumaxs(qdtime,LED[num_mark]);
    BEEP3();
   }//有人抢答成功则结束抢答
    if((qdtime==0)&&(num_mark==0))
   { TR0=0;
    shumaxs(0xbf,0xbf);
    BEEP2();
   }
  }
}
}

void key2()
{ if(S11==1)
{delay10ms();if(S11==1) {BeepIO=0;qdtime=0;shumaxs(0xbf,0xc0);}}
if(S12==1)
  {delay10ms();if(S12==1) {BEEP();shumaxs(LED[num_mark],qdtime);}}
if(S13==1)
{ delay10ms();
   if(S13==1)
   {qdtime++;if(qdtime>99) qdtime=0;shumaxs(0xbf,qdtime);}
}
if(S14==1)
{ delay10ms();
   if(S13==1)
    {qdtime--;if(qdtime>99) qdtime=0;shumaxs(0xbf,qdtime);}
}
}
  
void main()
{ qdtime=qdtime1;shumaxs(20,0);
  Timer0_init();//T0初始化
BeepIO=0;//蜂鸣器不响
TR0=0;
while(1)
  {
  LED1=1;LED2=1;LED3=1;LED4=1;LED5=1;LED6=1;LED7=1;LED8=1;LED9=1;LED10=1;//监视灯初始化为灭
  key2();
  key1();
}
}

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:648281 发表于 2020-1-1 20:48 | 只看该作者
你好!
从你描述的情况来看,程序对应要求的错误还很多;
只能根据你的要求,把程序分功能模块逐段调试。
回复

使用道具 举报

板凳
ID:332444 发表于 2020-1-2 08:36 | 只看该作者
用蛋骗鸡直接驱动数码管出现的问题,必须要用三极管或锁存器驱动数码管,你这是常见的驱动设计问题
回复

使用道具 举报

地板
ID:675815 发表于 2020-1-2 14:21 | 只看该作者
51hei**1140 发表于 2020-1-1 20:48
你好!
从你描述的情况来看,程序对应要求的错误还很多;
只能根据你的要求,把程序分功能模块逐段调试。

好的,谢谢!
回复

使用道具 举报

5#
ID:675815 发表于 2020-1-2 14:21 | 只看该作者
xianfajushi 发表于 2020-1-2 08:36
用直接驱动数码管出现的问题,必须要用三极管或锁存器驱动数码管,你这是常见的驱动设计问题

好的,谢谢
回复

使用道具 举报

6#
ID:1137639 发表于 2025-4-17 15:44 | 只看该作者
你好! 从你描述的情况来看,程序对应要求的错误还很多; 只能根据你的要求,把程序分功能模块逐段调试。
回复

使用道具 举报

7#
ID:332444 发表于 2025-4-19 08:36 | 只看该作者
622323wjl 发表于 2025-4-17 15:44
你好! 从你描述的情况来看,程序对应要求的错误还很多; 只能根据你的要求,把程序分功能模块逐段调试。

这个帖子也翻找出来了,许多年了未知题主还关注不,看看我当时回复挺搞笑的,不知道怎么想的!电路图中数码管没错,代码用阻塞不是很好选择,虽然是独立按键,但是整组使用时可以不单独定义,整组判断可对应一个按键操作,赋值255判断是否不等于255就有按键按下,特定值对应一个按键,另外2个按键可独立定义,按键写中断不是很好选择,尤其还有阻塞参与其中,按键要求尽量快判断结束,不影响数码管显示时间。
回复

使用道具 举报

8#
ID:332444 发表于 2025-4-19 08:42 | 只看该作者
当时提出用锁存器也是一个办法,现在看来从软件上能解决,那是要重写这个程序。
回复

使用道具 举报

9#
ID:1109793 发表于 2025-4-19 15:28 | 只看该作者
这种按键判定方式有问题,假设同一时刻有多个按键按下,那么接P3高位的就优先被判定,然而事实上极大可能是相对低位的按键先按下,只是在你读取之前,相对高位的按键也按下了,此时你读到的是2个位为0的数据,但是你在先判定了高位的就是后按下的那个之后便不再理会相对低位的那个开关了,虽然这个时间很短,参赛者只能认倒霉,但是这种情况还是极有可能的。
回复

使用道具 举报

10#
ID:1137639 发表于 2025-4-19 22:34 | 只看该作者
以下是对该程序的问题分析及修改建议:

### 一、程序问题分析
1. **按键检测与消抖**
    - 在`key1`和`key2`函数中,按键检测部分虽然有简单的延时消抖(`delay10ms()` ),但不够完善。对于按键释放的检测没有处理,可能导致按键误触发。比如在`key1`函数中,当检测到`S12`按键按下并进行一系列操作后,没有等待按键释放就继续检测其他按键,可能会在按键还未完全释放时再次检测到按键按下,造成逻辑混乱。
    - `switch`语句中对端口数据的处理逻辑有误。在`switch(P3&&0xff)`和`switch(P2&&0xff)`中,这种写法不能准确获取端口的实际电平状态。应该直接使用`P3`和`P2`进行判断,即`switch(P3)`和`switch(P2)` 。
2. **定时器相关问题**
    - 在`timer0`中断函数中,只对倒计时进行了处理,没有对定时时间到且无人抢答的情况进行全面处理。当`qdtime`减为0时,应该禁止后续的抢答操作,而当前代码没有相关设置。
    - `count`变量用于计数中断次数以实现倒计时,但在初始化时没有清零,可能导致初始倒计时不准确。
3. **数码管显示问题**
    - `shumaxs`函数中,数码管位选逻辑存在问题。在给不同位数码管赋值时,位选信号没有及时关闭,可能会导致数码管显示混乱。比如在给高位数码管赋值后,没有关闭其位选信号就给低位数码管赋值,会使数码管显示出现重叠或错误的现象。
    - 数码管显示程序没有考虑到不同情况下显示的切换逻辑。例如,在无人抢答定时时间到的情况下,应该显示特定的标识(如“00” ),但当前代码在这种情况下显示处理不够完善。
4. **抢答逻辑问题**
    - 选手抢答标记数组`S`的使用逻辑有误。在`key1`函数中,每次检测到抢答开始(`S12`按下 )就将所有选手的标记位归零,这会导致之前已经按下抢答按钮的选手标记被清除,无法实现优先锁存的功能。
    - 没有对多个选手同时抢答的情况进行处理。当多个选手同时按下抢答按钮时,当前代码不能准确判断并锁存最先抢答的选手编号。

### 二、修改建议
1. **按键检测与消抖改进**
    - 在`key1`和`key2`函数中,完善按键检测逻辑,增加按键释放检测。例如,在检测到按键按下并进行操作后,添加一个循环等待按键释放:
```c
while(S12 == 1); // 等待S12按键释放
```
    - 修正`switch`语句中对端口数据的判断方式,直接使用端口值进行判断,如:
```c
switch(P3)
{
    case 0x7f: num_mark = 1; LED1 = 0; break;
    case 0xbf: num_mark = 2; LED2 = 0; break;
    // 其他case语句类似
}
switch(P2)
{
    case 0x7f: num_mark = 9; LED9 = 0; break;
    case 0xbf: num_mark = 10; LED10 = 0; break;
}
```
2. **定时器相关改进**
    - 在`timer0`中断函数中,当`qdtime`减为0时,添加禁止抢答的逻辑,例如设置一个标志位:
```c
if(count == 20)
{
    count = 0;
    qdtime--;
    if(qdtime == 0)
    {
        // 设置禁止抢答标志位,比如定义一个全局变量 no_answer_flag = 1;
        no_answer_flag = 1;
    }
}
```
    - 在初始化时,将`count`变量清零:
```c
void Timer0_init (void)
{
    TMOD = 0x01;
    TH0 = 0x3c;
    TL0 = 0xb0;
    ET0 = 1;
    EA = 1;
    count = 0; // 初始化count为0
    // TR0 = 0;
}
```
3. **数码管显示改进**
    - 在`shumaxs`函数中,修改数码管位选逻辑,确保在给一位数码管赋值后及时关闭其位选信号,再给下一位数码管赋值:
```c
void shumaxs(int n, uchar m)
{
    int sj1, sj2, dw1, dw2;
    P0 = 0xff;
    P1 = 0x00;
    delay(50);
    sj1 = n / 10;
    sj2 = n % 10;
    dw1 = m / 10;
    dw2 = m % 10;

    P0 = LED[sj1]; smg_we4 = 1; delay(20); smg_we4 = 0; // 关闭位选信号
    P0 = LED[sj2]; smg_we3 = 1; delay(20); smg_we3 = 0;
    P0 = LED[dw1]; smg_we2 = 1; delay(20); smg_we2 = 0;
    P0 = LED[dw2]; smg_we1 = 1; delay(20); smg_we1 = 0;
}
```
    - 在主程序和相关函数中,完善不同情况下数码管显示的逻辑。例如,在无人抢答定时时间到的情况下,修改`shumaxs`函数的调用参数以显示“00”:
```c
if((qdtime == 0) && (num_mark == 0))
{
    TR0 = 0;
    shumaxs(0, 0); // 显示00
    BEEP2();
}
```
4. **抢答逻辑改进**
    - 修正选手抢答标记数组`S`的使用逻辑。在检测到有选手抢答后,只修改该选手对应的标记位,而不是将所有标记位归零。例如:
```c
if(S1 == 0) { S[1] = 0; num_mark = 1; LED1 = 0; }
else if(S2 == 0) { S[2] = 0; num_mark = 2; LED2 = 0; }
// 其他选手类似
```
    - 增加对多个选手同时抢答的处理逻辑。可以记录最早检测到的抢答选手编号,例如在检测到有选手抢答时,记录当前时间(通过定时器计数值等方式 ),比较不同选手抢答时的时间,优先锁存时间最早的选手编号。

通过以上修改,可以在一定程度上解决程序中存在的问题,使其更符合抢答器的功能要求。在实际调试过程中,还需要进一步根据具体现象进行优化和调整。
回复

使用道具 举报

11#
ID:332444 发表于 2025-4-21 14:28 | 只看该作者
xiaobendan001 发表于 2025-4-19 15:28
这种按键判定方式有问题,假设同一时刻有多个按键按下,那么接P3高位的就优先被判定,然而事实上极大可能是 ...

如果整组判断就不会出现因程序造成的不公平问题。
回复

使用道具 举报

12#
ID:1109793 发表于 2025-4-21 14:40 | 只看该作者
xianfajushi 发表于 2025-4-21 14:28
如果整组判断就不会出现因程序造成的不公平问题。

按键比较多,不可能同时。
而且读取时间间隔也必须越小越好,比如纳秒级别。
否则就只能是对每个按键引入中断,在中断里面固定证据。然后再判定,这样会好一些。
再不行就只有纯硬件了。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表