单片机考试编程题
一、C语言编程实现单片机I/0口控制数码管的显示
1.用定时0的方式1实现数码管两位59S循环计时(计时器)
思路方案:利用单片机的定时器/计数器定时和计数的原理,通过proteus 仿真软件来实现模拟实现。模拟利用AT89C51单片机、LED数码管实现秒表的计时。
其中一个两位数码管用来显示数据,一位用来显示个位秒,另一位用来显示十位秒。当计数超过范围时所有数码管全部清零重新计数。
程序代码如下:
#include
#define uchar unsigned char
#define uint unsigned int
uchar num,count,ge,shi;
void delay(uint);
void display(uchar,uchar);
uchar SEG[]={0xc0,0xf9,0xa4,0xb0,0x99, //显示数码管0-9数字
0x92,0x83,0xf8,0x80,0x98};
void main()
{
TMOD=0x01; //设置定时器工作方式 T0的工作方式为1
TH0=(65536-45872)/256; //装初值11.0592M晶振定时50ms数为45872
TL0=(65536-45872)%256;
EA=1; //开总中断
ET0=1; //开定时器0中断
TR0=1; //启动定时器0
while(1) //程序在这里不停的对数码管动态扫描同时等待中断发生{
display(shi,ge);
}
}
/*以下是设计中断服务程序的时间和显示*/
void T0_time()interrupt 1
{
TH0=(65536-45872)/256; //重装初值
TL0=(65536-45872)%256;
if(++num= =20) //如果到了20次,说明1秒时间到
{
num=0; //然后把num清0重新再记20次
if(++count==60) //这个数用来送数码管显示,到60后归0
count=0;
shi=count/10; //把一个2位数分离后分别送数码管显示,十位和个位
ge=count%10;
}
}
/*以下为延迟函数*/
void delay(uint xms)
{
uint i,j;
for(i=xms;i>0;i--) //延迟时间为 xms乘以50ms
for(j=110;j>0;j--);
}
/*以下为显示数码管的子函数*/
void display(uchar shi,uchar ge)
{
P3=0xfe; //打开P3.0,及打开数码管1引脚
P2=SEG[ge];
delay(20);
P3=0xfd; //打开P3.1,及打开数码管2引脚
P2=SEG[shi];
delay(20);
}
2. 基于单片机V1或V2实验系统,编写一个程序,实现以下功能:1)首先在数码管上显示“P_ _ _”4个字符;2)等待按键,如按了任何一个键,则将这4个字符清除,改为显示“0000”4个字符(为数字的0)。
#include
unsigned char code
Dig[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x 86,0x8e}; //gongyang数码管 0-F 代码
unsigned char k; //设置全局变量k 为键盘的键值
/************************************键盘延时函数****************************/
void key_delay(void) //延时函数
{
int t;
for(t=0;t<500;t++);
}
/************************************键盘扫描函数
******************************/
void keyscan(void) //键盘扫描函数
{
unsigned char a;
P2 = 0xf0; //键盘初始化
if(P2!=0xf0) //有键按下?
{
key_delay(); //延时
if(P2!=0xf0) //确认真的有键按下?
{
P2 = 0xfe; //使行线P2.4为低电平,其余行为高电平 key_delay();
a = P2; //a作为缓存
switch (a) //开始执行行列扫描
{
case 0xee:k=15;break;
case 0xde:k=11;break;
case 0xbe:k=7;break;
case 0x7e:k=3;break;
default:P2 = 0xfd; //使行线P2.5为低电平,其余行为高电平
a = P2;
switch (a)
{
case 0xed:k=14;break;
case 0xdd:k=10;break;
case 0xbd:k=6;break;
case 0x7d:k=2;break;
default:P2 = 0xfb; //使行线P2.6为低电平,其余行为高电平
a = P2;
switch (a)
{
case 0xeb:k=13;break;
case 0xdb:k=9;break;
case 0xbb:k=5;break;
case 0x7b:k=1;break;
default:P2 = 0xf7; //使行线P2.7为低电平,其余行为高电平
a = P2;
switch (a)
{
case 0xe7:k=12;break;
case 0xd7:k=8;break;
case 0xb7:k=4;break;
case 0x77:k=0;break;
default:break;
}
}
}
break;
}
}
}
}
/****************************** ***主函数*************************************/ void main(void)
{
while(1)
{
keyscan(); //调用键盘扫描函数
switch(k) //查找按键对应的数码管显示代码
{
case 0:P0=Dig[0];break;
case 1:P0=Dig[1];break;
case 2:P0=Dig[2];break;
case 3:P0=Dig[3];break;
case 4:P0=Dig[4];break;
case 5:P0=Dig[5];break;
case 6:P0=Dig[6];break;
case 7:P0=Dig[7];break;
case 8:P0=Dig[8];break;
case 9:P0=Dig[9];break;
case 10:P0=Dig[10];break;
case 11:P0=Dig[11];break;
case 12:P0=Dig[12];break;
case 13:P0=Dig[13];break;
case 14:P0=Dig[14];break;
case 15:P0=Dig[15];break;
default:break; //退出
}
}
}/**********************************end***************************************/
3.利用AT89S52单片机来制作一个0~99计时器,并且通过两个共阴数码管显示计数结果,数码管显示采用静态显示方式。
#include
/*定义0~9十个数字的字型码表*/
unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,
0x6d,0x7d,0x07,0x7f,0x6f};
unsigned char Count; //定义变量放置计数数值
void delay10ms(void) //定义10ms延时函数
{
unsigned char i,j;
for(i=20;i>0;i--)
for(j=248;j>0;j--);
}
void main(void)
{
Count=0; //计数变量初始为0
For(Count=0; Count<10; Count++)
{
P0=table[Count/10]; //显示十位数
P2=table[Count%10]; //显示个位数
delay10ms();
}
while(1) //等待
}
4.将单片机与数码管接成图4-2所示静态显示方式,编程实现数码管每隔0.5s
的0-9数字显示。
#include
#define uchar unsigned char
sbit a=P0^5;
sbit b=P0^6;
sbit c=P0^7;
unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,
0x6d,0x7d,0x07,0x7f,0x6f};
unsigned char dispcount;
void delay02s(void)
{ unsigned char i,j,k;
for(i=10;i>0;i--)
for(j=200;j>0;j--)
for(k=248;k>0;k--);
}
void main(void)
{ while(1)
{for(dispcount=0;dispcount<10;dispcount++)
{
a=0;
b=0;
c=0;
P1=table[dispcount];
delay02s();
}
}
}
5.单片机与数码管接成动态显示方式,编程实现数码管的0-7数字显示。
#include
#define uchar unsigned char
unsigned char code tabledu[]={0x3f,0x06,0x5b,0x4f,0x66,
0x6d,0x7d,0x07,0x7f,0x6f};
unsigned char code tablewe[]={0x1f,0x3f,0x5f,0x7f,0x9f,
0xbf,0xdf,0xff};
unsigned char dispcount;
void delay02s(void)
{ unsigned char i,j,k;
for(i=10;i>0;i--)
for(j=200;j>0;j--)
for(k=248;k>0;k--);}
void main(void)
{ while(1)
{for(dispcount=0;dispcount<8;dispcount++)
{ P0=tablewe[dispcount];
P1=tabledu[dispcount];
delay02s(); }
}
}
6.本任务是实现0~999的加1S计数显示。
#include
#define uchar unsigned char
sbit a=P0^5;
sbit b=P0^6;
sbit c=P0^7;
unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
unsigned char dispcount,bai,shi,ge;
unsigned int temp,aa;
void display();
void delay02s(void)
{ unsigned char i,j,k;
for(i=1;i>0;i--)
for(j=100;j>0;j--)
for(k=200;k>0;k--);}
void delay05s(void)
{ unsigned char s,m,t;
for(s=2;s>0;s--)
{ for(m=200;m>0;m--)
{ for(t=250;t>0;t--)
{;}
}
}
}
void main(void)
{ temp=0;
while(1) { delay05s();
delay05s();
temp++;
display(); } }
void display()
{if(temp==999)
{temp=0;}
bai=temp/100;
shi=temp%100/10;
ge=temp%10;
a=0; b=1; c=0;
P1=table[bai];
delay02s();
a=1; b=0; c=0;
P1=table[shi];
delay02s();
a=0; b=0; c=0;
P1=table[ge];
delay02s(); }
7.采用数码管进行数字时钟显示时间,并通过按键调整时间。
#include
#define uint unsigned int
#define uchar unsigned char
sbit key3=P0^3;//时调整
sbit key2=P0^2;//分调整
sbit key1=P0^1;//秒调整
sbit key0=P0^0;//全部清零键
uchar code tab1[]=
{0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7d,0x07,0x7f,0x6f,0x00,0x40,0x80};//共阳0~9/-段码/点uchar code tab2[]={0x1f,0x3f,0x5f,0x7f,0x9f,0xbf,0xdf,0xff};//0~7依次位码表
uchar sec, min, hour, count; //定义秒,分,时,中断计数
void delay(uint z) //延时子函数
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
//--------------------------------------------------------------
void init() //初始化T0函数
{
TMOD=0x01; //T0-方式1-16位计时器
TH0=(65536-50000)/256; //50MS
TL0=(65536-50000)%256; //晶振:12M计算
EA=1; //开总中断
ET0=1;//允许T0中断
TR0=1;//启动T0中断
}
void timer0() interrupt 1 //定时器函数
{
TH0=(65536-50000)/256; //50MS
TL0=(65536-50000)%256; //晶振:12M计算
count++; //50毫秒中断一次count便加1
if(count>=20) //中断了20次?(20*50=1秒)
{count=0;sec++;} //计数清零/秒加1
if(sec==60) //满了60秒?
{sec=0;min++;} //秒清零/分加1
if(min==60) //满了60分钟?
{min=0;hour++;} //分清零/时加1
if(hour==24) //满了24小时?
{hour=0;} //时清零
}
void display() //数码管显示子函数
{
uchar s ;
for(s=0;s<=10;s++)//扫描//控制显示刷新和按键速度
{
P1=tab1[hour/10];
P0=tab2[7]; delay(2);//时十位
P1=tab1[hour%10];
P0=tab2[6]; delay(2);//时个位
P1=tab1[11];
P0=tab2[5]; delay(2);//横
P1=tab1[min/10];
P0=tab2[4]; delay(2);//分十位
P1=tab1[min%10];
P0=tab2[3]; delay(2);//分个位
P1=tab1[11];
P0=tab2[2]; delay(2);//横
P1=tab1[sec/10];
P0=tab2[1]; delay(2);//秒十位
P1=tab1[sec%10];
P0=tab2[0]; delay(2);//秒个位
}
}
void key() //按键调整子函数
{
if(key1==0) //秒调整
{ delay(10);
if(key1==0){sec++;if(sec==60) sec=0;}//调到了60?
}
if(key2==0) //分调整
{ delay(10);
if(key2==0){min++; if(min==60) min=0;} //调到了60?
}
if(key3==0) //时调整
{ delay(10);
if(key3==0){hour++;if(hour==24) hour=0;} //调到了24?
}
if(key0==0) //全部归清零按
{ delay(10);
if(key0==0){sec = min = hour = 0;} //秒/分/时清零
}
}
void main() //主函数
{
init(); //T0初始化
while(1) //死循环
{
key(); //调整函数
display(); //显示函数
}
}
8.在8个LED数码管上,按从左到右的顺序,循环滚动显示数字“5”,每次滚动延时500ms #include
unsigned char LED;
void Delay(unsigned int milisec) //延时1ms
{
unsigned int i,j;
for(i=milisec;i>0;i--) //双重for循环实现延时1ms
for(j=110;j>0;j--);
}
void Main(void)
{
P0=0x6d; //显示数字“5”
while(1)
{
for(LED=0; LED<=7; LED++)
{
P2=LED; //选择LED,实现滚动显示
Delay(500); //延时500ms
}
}
}
9.在8个LED上稳定地显示“01234567”
#include
unsigned char code table[10] = {0x3f,0x06,0x5b,0x4f,0x66,
0x6d,0x7d,0x07,0x7f,0x6f};
unsigned char code LED[8] = {0,1,2,3,4,5,6,7};
void Delay(unsigned int milisec) //延时1ms
{
unsigned int i,j;
for(i=milisec;i>0;i--)
for(j=110;j>0;j--);
}
void Main(void)
{
unsigned char i = 0;
while(1)
{
P0 = table[i]; // 取一个数的段位码
P2 = LED[i]; //点亮一个数码管
Delay(2); //延时2ms
i++; //移位
if(i == 8) i = 0; //重新开始
}
}
unsigned char code table[10] = {0x3f,0x06,0x5b,0x4f,0x66, 0x6d,0x7d,0x07,0x7f,0x6f};
10.在8个LED上显示计算机教研室的电话号码“68752219”#include
unsigned char code table[10] = {0x3f,0x06,0x5b,0x4f,0x66, 0x6d,0x7d,0x07,0x7f,0x6f};
unsigned char code Number[8] = {6,8,7,5,2,2,1,9};
void Delay(unsigned int milisec) //延时1ms
{
unsigned int i,j;
for(i=milisec;i>0;i--)
for(j=110;j>0;j--);
}
void Main(void)
{
unsigned char i = 0,j;
while(1)
{
j = Number[i]; //取一个待显示的数
P0 = table[j]; // 取一个数的段位码
P2 = i; //点亮一个数码管
Delay(2); //延时2ms
i++; //移位
if(i == 8) i = 0; //重新开始
}
}
二.串行数据的发送和接收
1.用89C52串行口外接164串入/并出移位寄存器扩展8位并行输出口,外接165并入/串出移位寄存器扩展8位并行输入口。8位并行输出口的每位都接一个发光二极管,要求从8位并行输入口读入开关的状态值,使闭合开关对应的发光二极管点亮。
解:数据的输入输出通过RXD接收和发送,移位时钟通过TXD送出,74HC164用于串/并转换,74HC165用于并/串转换。
C语言程序清单:
#include
sbit P1_0=P1^0;
sbit P1_1=P1^1;
unsigned char data1;
void main()
{
SCON=0x10; //串行口方式0,允许接收
ES=1;
EA=1; //允许串行口中断
P1_0=0; //关闭并行输出
P1_1=1; //并行置入数据
P1_1=0; //开始串行移位
SBUF=0; //送入串行数据
while(1); //等待中断
}
void s_srv() interrupt 4 //中断服务程序
{
if(TI) //发送中断
{ TI=0;
P1_0=1; //打开并行输出
}
else //接收中断
{ RI=0;
data1=SBUF; //读取接收的数据
P1_0=0; //关闭并行输出
SBUF=~data1; //送入串行数据
P1_1=1; //为接收下一次
P1_1=0; //数据做准备
}
}
2.将片内RAM 50H~5FH中的数据串行发送,用第9个数据位作奇偶校验位,设晶振为11.059 2MHz,波特率为2 400b/s,编制串行口方式3的发送程序。
解:用TB8作奇偶校验位,在数据写入发送缓冲器之前.先将数据的奇偶位P写入TB8,这时,第9位数据作奇偶校验用,发送采用中断方式。
#include
unsigned char i=0;
unsigned char array[16] _at_ 0x50; //发送缓冲区
void main()
{ SCON=0xc0; //串行口初始化
TMOD=0x20; //定时器初始化
TH1=0xf4; TL1=0xf4;
TR1=1;
ES=1; EA=1; //中断初始化
ACC=array[i]; //发送第一个数据送
TB8=P; //累加器,目的取P位
SBUF=ACC; //发送一个数据
while(1); //等待中断
}
void server() interrupt 4 //串行口中断服务程序
{
TI=0; //清发送中断标志
ACC=array[++i]; //取下一个数据
TB8=P;
SBUF=ACC;
if(i==16) //发送完毕,
ES=0; //禁止串口中断
}
3.编写一个接收程序,将接收的16字节数据送入片内RAM 50H~5FH单元中。设第9个数据位作奇偶校验位,晶振为11.059 2 MHz,波特率为2 400b/s。
#include
unsigned char i;
unsigned char array[16] _at_ 0x50; //接收缓冲区
void main()
{
SCON=0xd0; //串行口初始化,允许接收
TMOD=0x20;
TH1=0xf4;
TL1=0xf4;
TR1=1;
for(i=0;i<16;i++) //循环接收16个数据
{ while(!RI); //等待一次接收完成
RI=0;
ACC=SBUF;
if(RB8==P) //校验正确
array[i]=ACC;
else //校验不正确
{ F0=1;
break;
}
}
while(1);
}
4.用第9个数据位作奇偶校验位,编制串行口方式3的全双工通信程序,设双机将各自键盘的按键键值发送给对方,接收正确后放入缓冲区(可用于显示或其它处理),晶振为11.059 2 MHz,波特率为9 600b/s。
解:因为是全双工方式,通信双方的程序一样。发送和接收都采用中断方式。
#include
char k;
unsigned char buffer;
void main()
{
SCON=0xd0; //串行口初始化, 允许接收
TMOD=0x20; //定时器初始化
TH1=0xfd;
TL1=0xfd;
TR1=1;
ES=1; //开串行口中断
EA=1; //开总中断
while(1)
{
k=key(); //读取按键按下键值
if(k!=-1) //无键按下返回-1
{
ACC=k; //将键值送累加器,取P位
TB8=P; //送TB8
SBUF=ACC; //发送
}
display(); //显示程序
}
}
void serial_server() interrupt 4
{ if(TI) //发送引起,清TI
TI=0;
else //否则,接收引起
{ RI=0;
ACC=SBUF; //读取接收数据
if(RB8==P) //校验正确,buffer=ACC; //存入缓冲区
}
}
三.定时器编程问题
1.设单片机的振荡频率为12MHz,用定时器/计数器0的模式1编程,在P1.0引脚产生一
个周期为1000μs的方波,定时器T0采用中断的处理方式。
#include
sbit P1_0=P1^0; //进行位定义
void main( )
{ TMOD=0x01; //T0做定时器,模式1
TL0=0x0c;
TH0=0xfe; //设置定时器的初值
ET0=1; //允许T0中断
EA=1; //允许CPU中断
TR0=1; //启动定时器
while(1); //等待中断
}
void time0_int(void) interrupt 1
{ //中断服务程序
TL0=0x0c;
TH0=0xfe; //定时器重赋初值
P1_0=~P1_0; //P1.0取反,输出方波
}
2.设单片机的振荡频率为12MHz,用定时器/计数器0编程实现从P1.0输出周期为500μs
的方波。
# include
sbit P1_0=P1^0;
void main( )
{ TMOD=0x02; //选择工作模式
TL0=0x06;
TH0=0x06; //为定时器赋初值
ET0=1; //允许定时0中断
EA=1;
TR0=1; //启动定时器0
while(1); //等待中断
}
void time0_int(void) interrupt 1
{
P1_0=~P1_0;
}
3.利用定时器T1的模式2对外部信号进行计数,要求每计满100次,将P1.0端取反。#include
sbit p1_0=p1^0; //进行位定义
void main ( )
{
TMOD=0x60; //T1工作在模式2,计数
TL1=0x9c; //装入计数(重装)初值
TH1=0x9c;
ET1=1; //允许定时器1中断
EA=1; //开中断
TR1=1 ; //启动定时器1
while(1);
}
void time0_int(void) interrupt 3 //中断服务程序
{
P1_0=~P1_0; //取反,产生方波
}
3.某应用系统要求通过P1.0和P1.1口分别输出脉冲周期为200μs和400μs的方波,
fosc=6MHz。
# include
sbit P1_0=P1^0; //进行位定义
sbit P1_1=P1^1;
void main( )
{
TMOD=0x03; //设置T0定时,工作在模式3
TL0=0xce; //设置TL0计数初值,产生200μs方波
TH0=0x9c; //设置TH0计数初值,产生400μs方波
ET0=1; //设置定时器0中断允许位
ET1=1; //设置定时器/计数//器1中断允许位
EA=1; //设置总中断允许位
TR0=1; //启动定时器T0
TR1=1; //启动定时器T1
while(1); //等待溢出
}
void time0L_int(void) interrupt 1
{ //T0中断服务程序
TL0=0xce; //定时器重赋初值
P1_0=~P1_0; //产生方波
}
void time0H_int(void) interrupt 3
{ //T1中断服务程序
TH0=0x9c; //定时器重赋初值
P1_1=~P1_1; //产生方波
}