用 8 個 LED 發光管 (4 個紅, 4 個綠 ) 的來回滾動顯示來模擬打乒乓球時乒乓球在兩邊球臺上的來回運動。比賽雙方用按按鈕 ( 雙方各用 1 個按鈕 ) 的方法來模擬發球與接球 , 即發球方按動其控制的按鈕 , 球從發球方壹側向對方運動 (LED 發光管從發球方到對方逐個點亮 , 滾動顯示 ), 當球運動至接球方時 , 接球方立即按動其控制的按鈕 , “擊球”使球“彈回”發球方壹側 , 如此周而復始 , 直至在規定的擊球時間內有壹方未能完成擊球動作 , 從而對方得壹分 , 然後繼續比賽。
2.設計要求
比賽規則可參照壹般的乒乓球比賽規則。要求:
(1)球以每0.5秒滾過1個LED的速度移動;
(2)回球需在球到達後的0.5秒內進行(按下按鈕), 否則按失球計;
(3)球未到達提前按下按鈕不起回球作用 ;
(4)用 4 個八段碼為雙方記分 ( 每方 2 個八段碼 ,10 進制計數 , 初始值 0:0) 。
設備:單脈沖電路,可編程並行接口8255,可編程定時計數器8253,LED流水燈,七段數碼管,開關等。
壹、 實驗原理
1.8253定時器選用方式0,實現定時功能。兩個七段數碼管要實現連續顯示,必須每隔5~10ms刷新壹次,而燈移動速度為300~500ms。設定定時周期為5ms,當計數停止,8253輸出高電平時,分別顯示比分的高位和低位。經過壹定數量的定時周期後,再進行燈移動的操作。
2.8255並行接口A口B口分別用來控制LED流水燈及數碼管的顯示,C口則用於讀入各開關狀態及輸出數碼管片選信號。
3.單脈沖開關模擬擊球,當8255讀入脈沖信號為高電平時,通過判斷燈當前移動方向和擊球者,來確定燈下壹次的移動方向。
4.LED流水燈表示乒乓球移動,燈每次移動應留有足夠的時間給操作者撥動調速開關,或按下脈沖開關。
5.七段數碼管與比賽同步顯示比分,顯示0~f相應的16進制控制數為0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x7b,0x71。
二、 硬件接線圖
三、 程序框圖
四、 源程序
程序源代碼如下:
(含註解)
//--------------------------------------------------------------------------------------------------
// File Name: table tennis.cpp
// Version: 1.0標準版
// Created By: 楓飛
// Created date: 01/12/2010
// Description: 實現可記分、判斷勝負,可速度調整的乒乓球對打
// 本程序全部由本人編寫,如有雷同,不勝榮幸!
//--------------------------------------------------------------------------------------------------
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <iostream.h>
#include "ApiEx.h"
#pragma comment(lib,"ApiEx.lib")
int pa=0x280;
int pb=0x281;
int pc=0x282;
int pn1=0x283;
int p0=0x288;
int p1=0x289;
int p2=0x28a;
int pn2=0x28b;
int s[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x7b,0x71};
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
void leddic(unsigned char &led, int f) //燈移動函數
{
int x=led;
if(f==0) x=x/2; //若方向向右,燈向右移動壹個
else if(f==1) x=x*2; //若方向向左,燈向左移動壹個
else printf("Error!");
led=x;
}
void speed(int &s) //球速控制,從8255C口讀入開關狀態,
{ //若為高電平,調整速度為高速
unsigned char data;
int x;
PortReadByte(pc,&data);
x=data;
x=x&4;
if(x==0) s=100; //默認5*100ms
if(x==1) s=60; //高速5*60ms
}
void scorem(int m) //通過數碼管顯示比分
{
unsigned char data;
data=s[m];
PortWriteByte(pc,0x20); //選擇數碼管高位
PortWriteByte(pa,data); //顯示甲得分
}
void scoren(int n)
{
unsigned char data;
data=s[n];
PortWriteByte(pc,0x10); //選擇數碼管低位
PortWriteByte(pa,data); //顯示乙得分
}
void start(unsigned char &led, int &f,int &s,int m,int n) //發球子函數
{
unsigned char data;
int x;
PortWriteByte(pn2,0x57); //8253計數器1初始化,低八位,方式3,控制字01010111B
PortWriteByte(p1,0x10); //8253計數器1計數初值10
for(;1<2;)
{
speed(s);
PortReadByte(pc,&data);
x=data;
x=x&1;
if(x==1) //若甲發球,燈從左往右
{
led=0x80;
f=0;
break;
}
x=data;
x=x&2;
if(x==1) //若乙發球,燈從右往左
{
led=0x1;
f=1;
break;
}
x=data;
x=x&8;
if(x==1) //當8253輸出為高電平時,顯示甲得分
{
scorem(m);
}
else //當8253輸出為低電平時,顯示乙得分 scoren(n);
}
}
void out(unsigned char &led, int &f,int &m,int &n) //判斷球是否出界,即選手得分
{
if(f==1) //球向左運動時
{
if(led==0x0) //球超出左邊界
{
printf("乙得分!\n");
n++;
printf("比分:");
printf("%d-%d",m,n);
printf("\n\n");
}
}
else //球向右運動時
{
if(led==0x0) //球超出右邊界
{
printf("甲得分!\n");
m++;
printf("比分:");
printf("%d-%d",m,n);
printf("\n\n");
}
}
if(((m==11)&&(m-n>=2))||((m>11)&&(m-n>=2))) //判斷整局比賽的勝負
{
printf("此局甲獲勝!重新計分!\n\n\n");
m=n=0;
}
else if(((n==11)&&(n-m>=2))||((n>11)&&(n-m>=2)))
{
printf("此局乙獲勝!重新計分!\n\n\n");
m=n=0;
}
}
void attack(unsigned char &led,int &s, int &f,int &m,int &n) //擊球子函數
{
unsigned char data;
int x,y;
STA:
printf("請發球!\n");
start(led,f,s,m,n);
PortWriteByte(pn2,0x51); //8253計數器1初始化,低八位,方式0,控制字01010001B
Red:
PortWriteByte(pb,led);
for(int i=1;i<=s;i++)
{
PortWriteByte(p1,5); //8253計數器1計數初值5
Goo:
PortReadByte(pc,&data);
x=data;
y=data;
y=y&8;
if(y==0) goto Goo; //當8253未輸出高電平時,繼續讀入
if(i%2==1)scorem(m);
else scoren(n);
if(f==1) //球向左運動
{
x=x&1;
if((x==1)&&(led>=0x10)) //球靠近甲且甲擊球
{
f=0; //調整球的運動方向為向右
}
}
else //球向右運動
{
x=data;
x=x&2;
if((x==2)&&(led<=0x8)) //球靠近乙且乙擊球
{
f=1; //調整球的運動方向為向左
}
}
}
leddic(led,f);
out(led,f,m,n);
if(led==0x0)
{
PortWriteByte(pb,led);
goto STA;
}
goto Red;
}
void main()
{
unsigned char led;
int f=1,s=0; //f為燈移動方向,默認向右;s為燈移動速度
int m=0,n=0; //m為甲得分,n為乙得分
Startup(); //打開設備
PortWriteByte(pn1,0x81); //8255初始化,A口B口輸出,
//C口高位輸出,低位輸入,控制字10000001B
PortWriteByte(pn2,0x27); //8253計數器0初始化,高八位,方式3,控制字00100111B
PortWriteByte(p0,0x20); //8253計數器0計數初值2000
attack(led,s,f,m,n); //調用擊球子函數
Cleanup(); //關閉設備
}
五、 結論
實現了能夠計分、同步顯示比分的模擬乒乓球比賽。
能夠實現或完成的功能如下:
1. LED燈模擬乒乓球的移動
2. 模擬乒乓球的擊球、發球
3. 與實際比賽壹樣判斷得分和整局的勝負情況
4. 可以調整發球速度
5. 計分、同步顯示比分