proteus工程文件及实验报告:XiaoBai1103/MCU-Experiment-Report: 单片机实验报告 (github.com)

实验1 单片机环境认识实验

编程实现在片内 50H~5FH 单元中建立 0~FH 字符串,将其移至片外数据区 8000H 开始处;然后将 8000H 开始处的各字节取反后送回片内 60H~6FH 单元。

#include"reg51.h"
#define uchar unsigned char
uchar data *dp1; //直接寻址片内RAM低128B
uchar xdata *dp2; //片外RAM,0x0000-0xffff
uchar data *dp3;
void main(void){
  char i;
  dp1=0x50;
  dp2=0x8000;
  dp3=0x60;
  for(i=0;i<16;i++)
	{
	   *(dp1+i)=i;
	}

  for(i=0;i<16;i++)
	{
	   *(dp2+i)=*(dp1+i);
	}
  for(i=0;i<16;i++)
	{
	   *(dp3+i)=~(*(dp2+i));
	}
  while(1);
}
 

编程将片内 50H 单元开始 10 字节的内容初始化为 0~9,然后移至 55H 开始的各单元中。

#include"reg51.h"
#define uchar unsigned char
uchar data *dp1; 
uchar data *dp2;

void main(void){
	  char i;
	  dp1=0x50;
	  dp2=0x5e;//55+9=5e
for(i=0;i<10;i++)
	{
	  *(dp1+i)=i;
	}
for(i=0;i<10;i++)
	{
          *(dp2-i)=*(dp1-i+9);
	}
while(1);
}

设 Number=0xABCD,将其转换为 BCD 码, 存入 Result 数组。Number 单元地址为片内 0x60。Result 单元地址为片内 0x70。

#include"reg51.h"
#include"intrins.h"
#define uchar unsigned char
#define uint unsigned int
data unsigned int num _at_ 0x60;
data unsigned char Result[5] _at_ 0x70;
void main(void){
	num=0xABCD;
	Result[0]=(num/10000)%10;
	Result[1]=(num/1000)%10;
	Result[2]=(num/100)%10;
	Result[3]=(num/10)%10;
	Result[4]=num%10;
	while(1);
}

实验2 PROTUES 认识实验

1. 编程实现 LED 流水灯,从上往下流水再从下往上流水。

#include<reg51.h> 
#include<intrins.h> 
#define uchar unsigned char 
#define uint unsigned int 
void DelayMS(uint x)                    //延时函数
{ 
 uchar i; 
 while(x--) 
 { 
 for(i=0;i<120;i++); 
 } 
} 
void main() 
{ 
 uchar i; 
 P0=0xFE;                          //点亮第一个灯
 DelayMS(150); 
 while(1) 
 { 
   for(i=0;i<7;i++) 
   { 
      P0=_crol_(P0,1);              //循环左移,使得灯向下移动
      DelayMS(150); 
   } 
   for(i=0;i<7;i++) 
   { 
      P0=_cror_(P0,1);              //循环右移,使得灯向上移动
      DelayMS(150); 
   } 
 } 
}

2. 编程实现堆叠流水灯效果:从上往下 1 盏灯亮、2 盏灯亮、3 盏灯亮….8 盏灯亮,再从下往上 1 盏灯灭、2 盏灯灭、3 盏灯灭….8 盏灯灭,周而复始。

#include<reg51.h> 
#include<intrins.h> 
#define uchar unsigned char 
#define uint unsigned int 
void DelayMS(uint x)                      //延时函数
{ 
 uchar i; 
 while(x--) 
 { 
 for(i=0;i<120;i++); 
 } 
} 

void LED(uint x)                    //灯全部的状态
{
   switch(x)
   {
   case 0:P0=0xFF; 
   break;
   case 1:P0=0xFE; //1111 1110
   break;
   case 2:P0=0xFC; //1111 1100
   break;
   case 3:P0=0xF8; //1111 1000
   break;
   case 4:P0=0xF0; //1111 0000
   break;
   case 5:P0=0xE0; //1110 0000
   break;
   case 6:P0=0xC0; //1100 0000
   break;
   case 7:P0=0x80; //1000 0000
   break;
   case 8:P0=0x00; //1000 0000
   break;
  }
  }
void main() 
{ 
     uchar i; 
 while(1) 
 { 
   
    for(i=0;i<=8;i++)
    {
      LED(i);
      DelayMS(150); 
    } 
    
    for(i=8;i>0;i--)
    {
      LED(i);
      DelayMS(150); 
    } 

  }
}

3. 编程实现, 上电时 LED 灯全亮。按下 K1 时, LED 上移一位;按下 K2, LED 下移一位。

#include<reg51.h> 
#include<intrins.h> 
#define uchar unsigned char 
#define uint unsigned int 
void DelayMS(uint x) 
{ 
 uchar i; 
 while(x--) 
for(i=0;i<120;i++); 
} 


void Move_LED() 
{ 
 if ((P1&0x01)==0) P0=_cror_(P0,1); //K1 
 else if((P1&0x02)==0) P0=_crol_(P0,1); //K2 

} 

void main() 
{ 
 uchar Recent_Key; //最近按键
 P0=0xfe; 
 P1=0xff; 
 Recent_Key=0xff; 
 while(1) 
 { 
 if(Recent_Key!=P1) 
 { 
 Recent_Key=P1; //保存最近按键
 Move_LED(); 
 DelayMS(10); 
 } 
 } 
}

4. 更改电路:将图 1 中的 2 个按键开关改为单刀单掷开关(switch),实现:K2K1=10 时,

出现实验 2 的效果;K2K1=01 时,出现实验 3 的效果;K2K1=00 时,实现闪烁效果;

K2K1=11 时,灯全灭。

#include<reg51.h> 
#include<intrins.h> 
#define uchar unsigned char 
#define uint unsigned int 
void DelayMS(uint x) 
{ 
 uchar i; 
 while(x--) 
for(i=0;i<120;i++); 
} 

void LED(uint x)
{
   switch(x)
   {
   case 0:P0=0xFF; 
   break;
   case 1:P0=0xFE; //1111 1110
   break;
   case 2:P0=0xFC; //1111 1100
   break;
   case 3:P0=0xF8; //1111 1000
   break;
   case 4:P0=0xF0; //1111 0000
   break;
   case 5:P0=0xE0; //1110 0000
   break;
   case 6:P0=0xC0; //1100 0000
   break;
   case 7:P0=0x80; //1000 0000
   break;
   case 8:P0=0x00; //1000 0000
   break;
  }
  }
void Move_LED() 
{ 
 if ((P1&0x04)==0) P0=_cror_(P0,1); //K1        
 else if((P1&0x08)==0) P0=_crol_(P0,1); //K2 

} 

void main() 
{ 
 uchar a=0;
 P0=0xfe; 
 P1=0xff; 

 while(1) 
 { 


 if(((P1&0x01)==0)&&((P1&0x02)==0))//00 
 {
   P0=0xff;
   DelayMS(150); 
   P0=0x00;
   DelayMS(150); 
    a=0;
 }
 else if(((P1&0x01)==1)&&((P1&0x02)==2))//11
 {
    P0=0xff;
    a=0;
 }
  else if(((P1&0x01)==1)&&((P1&0x02)==0))//10:实验 2 
 {uchar i;
    a=0;
      for(i=0;i<=8;i++)
    {
      LED(i);
      DelayMS(150); 
    } 
    
    for(i=8;i>0;i--)
    {
      LED(i);
      DelayMS(150); 
    } 
 }


 else if(((P1&0x01)==0)&&((P1&0x02)==2))//01:实验 3 
 {        
     uchar Recent_Key; //最近按键
      if(a==0)
      {
      P0=0xfe; 
      P1=0xff; 
      Recent_Key=0xff; 
	 a=1;
      }

       if((Recent_Key!=P1)&&(a==1))
       { 
         Recent_Key=P1; //保存最近按键
         Move_LED(); 
         DelayMS(10); 
       } 
 } 	
 } 
}

实验3 按键-LED数码管设计

电路原理图

1.LED循环显示字符0~7

#include <reg51.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int 

//段码表  
uchar code dis_code[]={0x3f,0x06,0x5b,0x4f,//0 1 2 3 
                  0x66,0x6d,0x7d,0x07, 
				  0x7f,0x6f,0x77,0x7c,
				  0x39,0x5e,0x79,0x71,0x40};
//延时
void delay(unsigned int x)
{
  unsigned char i;
 while(x--)
 {
  for(i=0;i<120;i++);
 }
}
//数码管显示
shownum(m)
{
   unsigned char k;      
   for(k = 0;k < 8;k++)       //循环8次
  {
    //P2 = 0x00;      //每显示一位都要关闭位选端口一次
   m=_crol_(m,1);   //循环左移
   P2=m;            //每次选通一个位选端口
   P0=dis_code[k];   //段码送P0口
   delay(2);
  }
 }
//键盘扫描程序
 scankey()//键盘扫描程序
{
   uchar KeyNo=16,Tmp;
   delay(20);
   P3=0x0f; //高4位置零,低四位置1
   Tmp=P3;
   Tmp=Tmp&0x0f;//低4位数
    switch(Tmp) //判断哪行被按下,并为按键赋行初始值
 { 
 case (0x0e): KeyNo=0;break; //0000 1110
 case (0x0d): KeyNo=4;break; //0000 1101
 default:KeyNo=16; //无键按下 
 } 
   P3=0xf0; //高4位置1,低四位置0
   Tmp=P3;
   Tmp=Tmp&0xf0;//低4位数
 switch(Tmp) //判断哪列被按下,并加上对行初始值的偏移量
 { 
 case (0xe0): KeyNo+=0;break; //1110 0000
 case (0xd0): KeyNo+=1;break; //1101 0000
 case (0xb0): KeyNo+=2;break; //1011 0000
 case (0x70): KeyNo+=3;break; //0111 0000
 } 

 return KeyNo;
}

//主函数
void main()
{
  uchar m=0x7f;
 P0 = 0xff;       //先关闭数码管
 P2 = 0x00;        //

 while(1)
 {
    uchar key;
   key=scankey();
    if(key==0)
    {
       delay(200);
       m=_crol_(m,1);
    }
    if(key==1)
    {
       delay(200);
       m=_cror_(m,1);
    }
    if(key==2)
    {
       delay(200);
       m=0x7f;
    }
   shownum(m);
 }
}

2.LED按键数值显示

#include <reg51.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int 

//段码表  
uchar code dis_code[]={0x3f,0x06,0x5b,0x4f,//0 1 2 3 
                  0x66,0x6d,0x7d,0x07, 
				  0x7f,0x6f,0x77,0x7c,
				  0x39,0x5e,0x79,0x71,0x40};
//延时
void delay(unsigned int x)
{
  unsigned char i;
 while(x--)
 {
  for(i=0;i<120;i++);
 }
}
//数码管显示
shownum(k)
{
   uchar m=0x7f;
   P2=m;            //每次选通一个位选端口
   P0=dis_code[k];   //段码送P0口
   delay(2);
 }
//键盘扫描程序
 scankey()//键盘扫描程序
{
   uchar KeyNo=16,Tmp;
   delay(20);
   P3=0x0f; //高4位置零,低四位置1
   Tmp=P3;
   Tmp=Tmp&0x0f;//低4位数
    switch(Tmp) //判断哪行被按下,并为按键赋行初始值
 { 
 case (0x0e): KeyNo=0;break; //0000 1110
 case (0x0d): KeyNo=4;break; //0000 1101
 case (0x0b): KeyNo=8;break; //0000 1011
 case (0x07): KeyNo=12;break; //0000 0111
 default:KeyNo=16; //无键按下 
 } 
   P3=0xf0; //高4位置1,低四位置0
   Tmp=P3;
   Tmp=Tmp&0xf0;//低4位数
 switch(Tmp) //判断哪列被按下,并加上对行初始值的偏移量
 { 
 case (0xe0): KeyNo+=0;break; //1110 0000
 case (0xd0): KeyNo+=1;break; //1101 0000
 case (0xb0): KeyNo+=2;break; //1011 0000
 case (0x70): KeyNo+=3;break; //0111 0000
 } 

 return KeyNo;
}

//主函数
void main()
{
 while(1)
 {
    uchar key;
   key=scankey();
    if(key!=16)//16为无按键返回标志
   shownum(key);
 }
}

3.LED数字右移

#include <reg51.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int 

//段码表  
uchar code dis_code[]={0x3f,0x06,0x5b,0x4f,//0 1 2 3 
                  0x66,0x6d,0x7d,0x07, 
				  0x7f,0x6f,0x77,0x7c,
				  0x39,0x5e,0x79,0x71,0x40};
//延时
void delay(unsigned int x)
{
  unsigned char i;
 while(x--)
 {
  for(i=0;i<120;i++);
 }
}
//数码管显示
shownum(uchar cache[8])
{
   uchar m=0x7f;
   uchar k;      
   for(k = 0;k < 8;k++)       //循环8次
  {
    //P2 = 0x00;      //每显示一位都要关闭位选端口一次
   m=_crol_(m,1);   //循环左移
   P2=m;            //每次选通一个位选端口
   P0=dis_code[cache[k]];   //段码送P0口
   delay(2);
  }
 }
//键盘扫描程序
 scankey()//键盘扫描程序
{
   uchar KeyNo=16,Tmp;
   delay(20);
   P3=0x0f; //高4位置零,低四位置1
   Tmp=P3;
   Tmp=Tmp&0x0f;//低4位数
    switch(Tmp) //判断哪行被按下,并为按键赋行初始值
 { 
 case (0x0e): KeyNo=0;break; //0000 1110
 case (0x0d): KeyNo=4;break; //0000 1101
 case (0x0b): KeyNo=8;break; //0000 1011
 case (0x07): KeyNo=12;break; //0000 0111
 default:KeyNo=16; //无键按下 
 } 
   P3=0xf0; //高4位置1,低四位置0
   Tmp=P3;
   Tmp=Tmp&0xf0;//低4位数
 switch(Tmp) //判断哪列被按下,并加上对行初始值的偏移量
 { 
 case (0xe0): KeyNo+=0;break; //1110 0000
 case (0xd0): KeyNo+=1;break; //1101 0000
 case (0xb0): KeyNo+=2;break; //1011 0000
 case (0x70): KeyNo+=3;break; //0111 0000
 } 

 return KeyNo;
}

//主函数
void main()
{
   uchar cache[8]={0,1,2,3,4,5,6,7};
   uchar cache1[8];
   uchar i;
 while(1)
 {
    uchar key;
   key=scankey();
   if(key!=16)//16为无按键返回标志
   {
      delay(200);
      for(i=6;i>0;i--)
      {
	 cache[i+1]=cache[i];
      }
      cache[1]=cache[0];
      cache[0]=key;
   }
   shownum(cache);
 }
}

参考资料

基于proteus的51单片机仿真实例六十、8位数码管显示实例:CSDN博主「老马识途单片机」链接:https://blog.csdn.net/tian_maer/article/details/71436045

实验4 定时计数器设计实验

实验1

用定时计数器编程实现:每隔1s点亮1只发光二极管。要求分别用查询方式和中断方式

//查询方法
#include<reg51.h> 
#include<intrins.h> 
#define uchar unsigned char 
#define uint unsigned int 
void LED(uchar x)                    //灯全部的状态
{
   switch(x)
   {
   case 0:P0=0xFF; 
   break;
   case 1:P0=0xFE; //1111 1110
   break;
   case 2:P0=0xFC; //1111 1100
   break;
   case 3:P0=0xF8; //1111 1000
   break;
   case 4:P0=0xF0; //1111 0000
   break;
   case 5:P0=0xE0; //1110 0000
   break;
   case 6:P0=0xC0; //1100 0000
   break;
   case 7:P0=0x80; //1000 0000
   break;
   case 8:P0=0x00; //1000 0000
   break;
   }
}
void Init_T0 (void) //定时器初始化
{
   TMOD = 0x01;//T0工作方式1
   TH0 = (65536-1000) /256;
   TL0 = (65536-1000) %256;//12M机器周期是1us  定时1ms:65536-10^(-3)/10^(-6)
   TR0=1;
}
void Main() 
{
   uint i=0;
   uchar count=1;
   P0=0xFF;//开始灯全灭  
   Init_T0 ();
   while(1)
  { 
      if(TF0) //查询中断标志
      {
         TH0 = (65536-1000) /256;
	 TL0 = (65536-1000) %256;
	 TF0=0;
	 i++;
	      if(i==1000)
         {
	         i=0;
		      LED(count);
	         count++;
		      if(count==9)
		      {
		        count=0;
		      }
	      }
      }
  }
}
//中断方法
#include<reg51.h> 
#include<intrins.h> 
#define uchar unsigned char 
#define uint unsigned int 
bit flag=0;	
void LED(uchar x)                    //灯全部的状态
{
   switch(x)
   {
   case 0:P0=0xFF; 
   break;
   case 1:P0=0xFE; //1111 1110
   break;
   case 2:P0=0xFC; //1111 1100
   break;
   case 3:P0=0xF8; //1111 1000
   break;
   case 4:P0=0xF0; //1111 0000
   break;
   case 5:P0=0xE0; //1110 0000
   break;
   case 6:P0=0xC0; //1100 0000
   break;
   case 7:P0=0x80; //1000 0000
   break;
   case 8:P0=0x00; //1000 0000
   break;
  }
  }

void Init_T0 (void) //定时器初始化
{
   TMOD = 0x01;//T0工作方式1
   TH0 = (65536-1000) /256;
   TL0 = (65536-1000) %256;//12M机器周期是1us  定时1ms:65536-10^(-3)/10^(-6)  
   TR0=1;
}

void Time0_Int() interrupt 1 //中断处理函数
{
 flag=1;
}

void Main() 
{
   uint i=0;
   uchar count=1;
   EA=1;//中断允许
   ET0=1;//定时器0中断允许
   P0=0xFF;//开始灯全灭 
   Init_T0 ();
   while(1)
  { 
      if(flag) //中断被触发
      {
              TH0 = (65536-1000) /256;
	      TL0 = (65536-1000) %256;
	      i++;
	      flag=0;
	      if(i==1000)
              {
	         i=0;
		 LED(count);
	         count++;
		 if(count==9)
		 {
		     count=0;
		 }
		 
	      }
      } 
  }
}

实验2

定时器T0每接收3个脉冲,流水灯下移一位。

#include<reg51.h> 
#include<intrins.h> 
#define uchar unsigned char 
#define uint unsigned int 
void Init_T0 (void) //定时器初始化
{
   TMOD=0x06; /工作方式2 
   TH0 =0;
   TL0 =0;
   TR0=1;
}
void Main() 
{
   P0=0xFE; 
   Init_T0 ();
   while(1)
  { 
      if(TL0==0x03) //计到3,初值重新置0
      {
	 P0=_crol_(P0,1);
	 TL0=0;
      }
  }
}

实验3

设计电路并编程实现电子钟:24小时制,显示格式XX-XX-XX,由左到右分别为时分秒。初值为23-59-59.

#include<reg51.h> 
#include<intrins.h> 
#define uchar unsigned char 
#define uint unsigned int 
//断码表
uchar code dis_code[]={
   0x3f,0x06,0x5b,0x4f, 
                  0x66,0x6d,0x7d,0x07, 
				  0x7f,0x6f,0x77,0x7c,
				  0x39,0x5e,0x79,0x71,0x40};
uint ms = 0,s = 59,min = 59,h = 23;//ms,s,min,h分别是毫秒,秒,分,时。
uchar num[8]; //数码管时钟显示内容                                                               
//定时器初始化
void Init_T0 (void) 
{
        TMOD=0x01;//定时器方式1
        TH0 = (65536-1000) /256;
	TL0 = (65536-1000) %256;
        TR0=1;
        ET0=1;
        EA=1;
}
void delay(unsigned int x)
{
 uchar i;
 while(x--)
 {
  for(i=0;i<120;i++);
 }
}
//数码管显示
shownum()
{
   uchar m=0x7f;
   uchar k;  
   for(k = 0;k < 8;k++)       //循环8次
  {
   m=_crol_(m,1);   //循环左移
   P2=m;            //每次选通一个位选端口
   P0=dis_code[num[k]];   //段码送P0口
   delay(2);
  }
 }
void main(){
        Init_T0 () ;
        while(1){
                shownum();
        }            
}
void TimeInt0() interrupt 1{ //1ms触发中断
   TH0 = 0xfc;
   TL0 = 0x18;
   ms++;
   if(ms == 1000) // 中断1000次,即1s
   {                       
        ms = 0;
        s++;
        if(s == 60)//60s归零
        {                
           s = 0;
           min++;
        }
   }
        if(min == 60)//60min归零
        {             
           min = 0;
           h++;                
           if(h == 24)//24h归零)
                h = 0;
        }

        num[7] = s % 10;   
        num[6] = s / 10;
	num[5]=16;
        num[4] = min % 10; 
        num[3] = min / 10;
	num[2]=16;
        num[1] = h % 10; 
        num[0] = h / 10;
}

实验5 中断定时计数器综合

参考电路图

1&2

#include<reg51.h>
#define uchar unsigned char 
#define uint unsigned int 
#define ulong unsigned long
uchar code DIS_CODE[12] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0xFF,0x0C};
ulong num;    //计数值
void delay(unsigned int x)
{
 uchar i;
 while(x--)
 {
  for(i=0;i<120;i++);
 }
}

void shounum(ulong num)
{
   uchar num_1; //个位
   uchar num_2; //十位
   uchar num_3; //百位
   uchar num_4; //千位
   num_1 = num%10;
   num_2 = num%100/10;
   num_3 = num%1000/100;
   num_4 = num%10000/1000;
   P2 = 0x01;    
   P0 = DIS_CODE[num_4];
   delay(5); 
   P2 = 0x02;
   P0 = DIS_CODE[num_3];
   delay(5);
   P2 = 0x04;
   P0 = DIS_CODE[num_2];
   delay(5);
   P2 = 0x08;
   P0 = DIS_CODE[num_1];
}

void int0() interrupt 0//外中断0
{
   num++;
}			
	
void main (void)
{
   num = 0;     //要显示的变量初始化
   P0 = 0xFF;
   P2 = 0xFF;
   P3 = 0xFF;
   IT0 = 1;   //下降沿触发
   EX0 = 1;   //允许Int0中断 
   EA = 1;    //允许中断
   while(1)
   {
      if(num<=9999)
      {
	 P1=~(num%256);//实验内容2部分
	 shounum(num);
      }
      else
	 num=0;     
   }
}

3

#include<reg51.h>
#include<intrins.h> 
#define uchar unsigned char 
#define uint unsigned int 
#define ulong unsigned long
uchar code dis_code[]={
   0x3f,0x06,0x5b,0x4f, 
                  0x66,0x6d,0x7d,0x07, 
				  0x7f,0x6f,0x77,0x7c,
				  0x39,0x5e,0x79,0x71,0x40};
uint num;//计数值
uchar num_LED[8]; //数码管显示内容   
void delay(unsigned int x)
{
  unsigned char i;
 while(x--)
 {
  for(i=0;i<120;i++);
 }
}

shownum()
{
   uchar m=0x7f;
   uchar k;      
   for(k = 0;k < 8;k++)      
  {
   m=_crol_(m,1);   //循环左移
   P2=m;            //每次选通一个位选端口
   P0=dis_code[num_LED[k]];   //段码送P0口
   delay(2);
  }
 }


void Init_T0 (void) //定时器初始化
{
   TMOD=0x05; //工作方式1(16位)
   TH0 =0;
   TL0 =0;
   TR0=1;
}
				
void main (void)
{
   uchar i;
   num = 0; 
   
   Init_T0 ();
   for(i=0;i<8;i++)
   {
      num_LED[i]=0;
   }
   while(1)
   {
      num=TL0+TH0*256;
      num_LED[7]=num%10; 
      num_LED[6]=num%100/10;
      num_LED[5]=num%1000/100;
      num_LED[4]=num%10000/1000;
      num_LED[3]=num%100000/10000;
      num_LED[2]=0;             //计数器最大值65535,因此最多用到5位数码管
      num_LED[1]=0;
      num_LED[0]=0;
      P1=~(num%256);
      shownum();
   }
}

实验6 串口通信实验

1.甲、乙双机串行通信,甲机 P1 口接 8 个开关,乙机 P1 口接8 个发光二极管,要求读入甲机的 8 个开关状态后,通过串行口发送到乙机,乙机将接收到的数据送发光二极管的显示。双方晶振均采用11.0592MHz,波特率9600,串口工作方式采用方式 1。参考电路见图 1,补齐晶振电路。

/*甲机串行发送*/
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
void main()
{
   uchar temp=0;
   TMOD=0x20; 
   TR1=1;
   TH1=0xfd;/*波特率9600*/
   TL1=0xfd;
   SCON=0x40; /*方式1*/
   PCON=0x00;
   TR1=1; 
   P1=0xff;
   ES = 1; 
   EA = 1; 
while(1)
{
   temp=P1; 
   SBUF=temp; 
   while(TI==0); 
   TI=0; 
}
}

/*乙机串行接收*/
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
void main( )
{
   uchar temp=0;
   TMOD=0x20; /*定时器T1为方式2*/
   TR1=1;
   TH1=0xfd; /*波特率9600*/
   TL1=0xfd;
   SCON = 0x50; /*方式1接收,REN=1*/
   PCON = 0x00;
   RI=1;
   ES = 1; 
   EA = 1; 
while(1)
   {
    while(RI==0);
    RI=0;
    temp=SBUF; 
    P1=temp; 
   }
}

2.甲、乙双机串行通信,乙机 P1 口接 8 个发光二极管,甲机通过串口发送数据给乙机,点亮 8 个发光二极管,晶振均采用 11.0592MHz,波特率 9600,串口工作方式采用方式 3。参考电路见图 2,补齐晶振和复位电路。

/*甲机串行发送*/
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
sbit p=PSW^0; /* p位定义为PSW 寄存器的第0位,即奇偶校验位*/
void Send(unsigned char dat ) 
{ ACC=dat;
TB8=p;
SBUF=dat; 
while(TI==0); 
TI=0; 
}
void Delay(unsigned int x)
{
 uchar i;
 while(x--)
 {
  for(i=0;i<250;i++);
 }
}

void main(void) 
{ 
unsigned char i;
TMOD=0x20;
TR1=1;
TH1=0xfd; /*波特率设置为9600*/
TL1=0xfd;
SCON=0xc0; /*串口方式3*/
PCON=0x00; 
TR1=1;
RI=1;
ES = 1;  
EA = 1;   
while(1)
{
   Send(0x00);
   Delay(100); 
   Send(0xff);
   Delay(100);
}
} 

/*乙机串行接收*/
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
unsigned char Receive(void) /*接收一个字节数据的函数*/
{ 
   unsigned char dat;
   while(RI==0); 
   RI=0; 
   ACC=SBUF; 
   if(RB8==P) 
   {
   dat=ACC;
   return dat; 
   }
}

void main(void) 
{ 
   TMOD=0x20; /*T1为方式2*/
   TR1=1;
   TH1=0xfd; /*波特率9600*/
   TL1=0xfd;
   SCON=0xd0; /*串口方式3*/
   PCON=0x00; 
   TR1=1;
   RI=1;
   ES = 1;  
   EA = 1;  
while(1)
   P1= Receive( ); 
}

3.在实验 2 的基础上,每隔 1s 甲机发送 1 次数据给乙机,即乙机的流水灯是每隔 1s 流水 1 次。

/*甲机串行发送*/
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
sbit p=PSW^0; /* p位定义为PSW 寄存器的第0位,即奇偶校验位*/
unsigned char code Tab[]= {0xfe , 0xfd , 0xfb , 0xf7 , 0xef , 0xdf , 0xbf , 0x7f};
/*控制流水灯显示数据,数组被定义为全局变量*/
void Send(unsigned char dat ) /* 发送一个字节数据的函数*/
{ ACC=dat;
TB8=p; /* 将奇偶校验位写入TB8*/
SBUF=dat; /* 将待发送的数据写入发送缓冲器*/
while(TI==0); /* 检测发送标志位TI, TI=0,未发送完*/
TI=0; /* 一个字节发送完,TI清0*/
}

void main(void)
{ 
   unsigned char i=0;
   uchar count=0;
   TMOD=0x21; //设置定时器T1为方式2 T0工作方式1
   TH1=0xfd; /*波特率设置为9600*/
   TL1=0xfd;
   TH0= (19456)/256;    
   TL0= (19456)%256;
   TR1=1;//打开定时器1
   TR0=1;//打开定时器1
   SCON=0xc0; /*设置串口为方式3*/
   PCON=0x00; /*SMOD=0*/ 
   
   //RI=1;//启动T1*/ RI=1表示收到数据
   /*中断*/
   ES = 1;   //允许串行中断
   EA = 1;    //允许中断
   
   while(1)
   {
	 if(TF0) //查询中断标志
	 {
	    TH0 = (19456) /256;
	    TL0 = (19456) %256;
	    TF0=0;
	    count++;
	      if(count==20)
	      {
	        count=0;
	        if(i<8)
	        {
		  Send(Tab[i]);
		  i++;
	        }
	        else
	        {
		  i=0;
	        }  
	      }
	 }
   }
}   

实验7 键盘-串口通信综合实验

1.甲、乙双机串行通信,甲机 P1 口接 8 个按键开关,乙机 P1口接 1 个 LED 数码管,要求读入甲机的开关状态,通过串行口发送到乙机,乙机在 LED 数码管上显示 0~F。双方晶振均采用 11.0592MHz,波特率 2400,采用串口工作方式 1。

/*甲机串行发送*/
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
void main()
{
   uchar temp=0;
   /*定时器1初始化*/
   TMOD=0x20; //设置定时器T1为方式2
   TR1=1;//打开定时器
   TH1=0xf4;//波特率2400
   TL1=0xf4;
   /*串口初始化*/
   SCON=0x40; //方式1只发送,不接收
   PCON=0x00;
   P1=0xff; //P1口为输入
   /*中断*/
   ES = 1;   //允许串口中断 
   EA = 1;    //允许中断
   while(1)
   {
      temp=P1; //读入P1口开关的状态数据
      SBUF=temp; //数据送串行口发送
      while(TI==0); //如果TI=0,未发送完,循环等待
      TI=0; //已发送完,再把TI清0
   }
}

/*乙机串行接收*/
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
uchar code seg[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
void main( )
{
   uchar temp=0;
   /*定时器1初始化*/
   TMOD=0x20; //设置定时器T1为方式2
   TR1=1;//打开定时器
   TH1=0xf4; //波特率2400
   TL1=0xf4;
   /*串口初始化*/
   SCON = 0x50; //串行口控制寄存器设置串口为方式1接收,REN=1
   PCON = 0x00; //串行口控制寄存器	SMOD=0
   RI=1;//启动T1
   /*中断*/
   ES = 1;   //允许Int0中断 
   EA = 1;    //允许中断
   while(1)
   {
      while(RI==0);//若RI为0,未接收到数据
      RI=0;//接收到数据,则把RI清0
      temp=SBUF; //读取数据存入temp中
      temp=temp&0x0f;
      P1=seg[temp];

      //temp; //接收数据送P1口控制8个LED的亮与灭
   }
}

甲、乙双机串行通信。甲机接键盘和 LED 数码管,按键显示1~9,甲机外中断 0 接键盘扫描,每当有键按下,触发 1 次中断,外中断 1 接按键开关,开关触发 1 次,甲机的数据传给乙机显示。双方晶振均采用 11.0592MHz,波特率 2400,采用串口工作方式 1。

/*甲机串行发送*/
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
uchar code seg[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf,0xc0,0x8c};
uchar key,num;

void delayms(uint x)
{
 uchar i;
 while(x--)
 {
  for(i=0;i<250;i++);
 }
}
uchar scan(void)
{
uchar k=13,m,n,in;//k=13无按键按下标志
delayms(10);
P1=0x07;  
if((P1&0x07)!=0x07)//有按键被按下
{
	for(m=0;m<4;m++)//行扫描
	{
	 	P1=~(0x01<<(m+3));//行扫描
	 	for(n=0;n<3;n++)
			{
		 	in=P1;
		 	in=in>>n;//in右移n位 非循环移位
	 	 	if((in&0x01)==0)//列判断
				{
			 	if((in&0x01)==0)
				   {
				      k=n+m*3+1;//行数+列数
				      break;
				   }
				}
			}
		if(k!=13)
		   break;
	}
}
return(k);
}

void ext0()interrupt 0//外中断0 用于扫描键盘
{
EX0=0;//外中断0禁止
key=scan();
if(key!=13)
   num=key;
EX0=1;//外中断0允许
}

void ext1()interrupt 2//外中断2 发送按钮
{
SBUF=num;
while(TI==0); //如果TI=0,未发送完,循环等待
   TI=0;
}

void main(void)
{
   TMOD=0X20; //设置定时器1工作在方式2
   TH1=0xf4;//波特率2400
   TL1=0xf4;
   TR1=1;
   
   SCON=0X40; //串口工作在方式1
   PCON=0X00; 
   
   EA=1;
   ES=1;
   EX1=1;
   EX0=1;
   
   while(1)
   {
      P1=0x07;
      P2=seg[num];
   }
}


/*乙机串行接收*/
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
uchar code seg[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf,0xc0,0x8c}; 

void main( )
{
   uchar temp;
   /*定时器1初始化*/
   TMOD=0x20; //设置定时器T1为方式2
   TR1=1;//打开定时器
   TH1=0xf4; //波特率9600
   TL1=0xf4;
   /*串口初始化*/
   SCON = 0x50; //串行口控制寄存器设置串口为方式1接收,REN=1
   PCON = 0x00; //串行口控制寄存器	SMOD=0
   RI=1;//启动T1
   /*中断*/
   ES = 1;   //允许Int0中断 
   EA = 1;    //允许中断
   while(1)
   {
      while(RI==0);//若RI为0,未接收到数据
      RI=0;//接收到数据,则把RI清0
      temp=SBUF; //读取数据存入temp中
      P1=seg[temp];
   }
}

实验 8 单片机系统扩展实验

1.实验内容 1:扩展一片 RAM6264,实现:(1)片外 00 地址开始 16 个单元赋初值,0~F;(2)片外 00 地址开始 16 个单元取反后送 80H~8FH;(3)片外 80H~8FH 的值点亮 P1 口灯。

#include "reg52.h"
#include "absacc.h"
#define uchar unsigned char
#define uint unsigned int
uchar code data_write []={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f};		
sbit LED=P2^7;

   void delayms(uint j)
{
uchar i;
for(;j>0;j--)
  	{i=250;
   	 while(--i);
   	 i=249;
   	 while(--i);
   	}
}

void main(void)
{
   	uchar i,j;
	LED=1;
	 for(i=0;i<16;i++)
	{
		XBYTE[i]= data_write [i];
		delayms(50);
	}
	for(i=0;i<=16;i++)
	{
	   j=i+128;//80H-8FH  128~143
		XBYTE[j]=~XBYTE[i];
		delayms(50);
	}
	LED=0;//写入完毕,LED灯亮起
	
	
	while(1){
	    for(i=128;i<=143;i++)					
	{
		P1=XBYTE[i];
		delayms(300);
	}
}
}
  1. 实验内容 2:用 8255A 扩展 51 单片机并口,如图 2 所示,要求读入开关状态,送灯显示。
#include "reg51.h"
#include "absacc.h"	
#define uchar unsigned char
#define uint unsigned int	
#define con_8255 XBYTE[0x7f03]	//控制口地址
#define pb_8255  XBYTE[0x7f01]	//PB口地址
#define pa_8255  XBYTE[0x7f00]	//PA口地址
sbit rst_8255=P3^5; 
   void delayms(uint j)
{
uchar i;
for(;j>0;j--)
  	{i=250;
   	 while(--i);
   	 i=249;
   	 while(--i);
   	}
}

void main(void)
{
   uchar temp;
   rst_8255=1;
   delayms(1);
   rst_8255=0;
   con_8255=0x82;//PB口输入,PA口输出
   while(1)
	{
 	temp=pb_8255;  //读PB口值	
	pa_8255=temp;  //写PA口
	}
}
  1. 使用定时器 T0,在 P1.7 输出频率为 100Hz,占空比为 70%的矩形波。要求 P1.7 引脚接虚拟示波器,观察波形,如图 3 所示。假设 fosc=6MHz。
#include<reg51.h> 
#include<intrins.h> 
#define uchar unsigned char 
#define uint unsigned int 
sbit output=P1^7;//示波器接口
void Init_T0 (void) //定时器初始化
{
   TMOD = 0x01;//T0工作方式1
   TH0 = (65536-500) /256;
   TL0 = (65536-500) %256;	
   TR0=1;
}
void Main() 
{
   uint i=0;
   uchar count=1;
   output=1;
   Init_T0 ();
   while(1)
  { 
      if(TF0)
      {
         TH0 = (65536-500) /256;
	 TL0 = (65536-500) %256;
	 TF0=0;
	 i++;
	 if(i==3)
	 output=1;
	 if(i==10)
	 { 
	    output=0;
	    i=0;
	 }
      }
  }
}

实验 9 DA接口设计实验

1.设计 51 单片机与 DAC0832 的接口电路,DAC0832 的地址 为 8000H,参考电压为 5V。编程实现矩形波,要求:波形占空比为 20%,高电 平时电压为 2.5V,低电平时电压为 1.25V。

#include<absacc.h>
#include<reg51.h>
#define DAC0832 XBYTE[0x8000]
#define uchar unsigned char
#define uint unsigned int
void delay(uint x)
{
 uchar i;
 while(x--)
 {
  for(i=0;i<50;i++);
 }
}
void main()
{
 uchar i=0;
 while(1) {
	 if(i<2)
	 {
	  DAC0832=0x80;//高电平 2.5V   2.5*256=D*5  D=128=80h
	  delay(1);
	    i++;
	 }
	 else
	 {
	  DAC0832=0x40;//低电平 1.25V   1.25*256=D*5  D=64=40h
	  delay(1);
	    i++;
	    if(i==10)
	       i=0;
	 }
 }
}

2. 实验内容 2:DAC0832 用作波形发生器,分别产生锯齿波、三角波、正弦 波和矩形波,能够用按键选择波形。芯片地址 7FFFH。实验参考图如图 1 所示。

#include<reg51.h>       
#include<absacc.h>     
sbit CS=P2^7;        
sbit WR12=P3^6;  
sbit sin=P1^0;
sbit square=P1^1;    
sbit triangle=P1^2;
sbit sawtooth=P1^3;
#define DAC0832 XBYTE[0x7FFF]
#define uchar unsigned char
#define uint unsigned int
unsigned char const code sin_code[256]={0x80,0x83,0x86,0x89,0x8d,0x90,0x93,0x96,0x99,0x9c,0x9f,
0xa2,0xa5,0xa8,0xab,0xae,0xb1,0xb4,0xb7,0xba,0xbc,0xbf,0xc2,0xc5 ,
0xc7,0xca,0xcc,0xcf,0xd1,0xd4,0xd6,0xd8,0xda,0xdd,0xdf,0xe1,0xe3,
0xe5,0xe7,0xe9,0xea,0xec,0xee,0xef,0xf1,0xf2,0xf4,0xf5 ,0xf6,0xf7,
0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfd,0xfe,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfd ,0xfd,0xfc,0xfb,0xfa,
0xf9,0xf8,0xf7,0xf6,0xf5,0xf4,0xf2,0xf1,0xef,0xee,0xec,0xea,0xe9,
0xe7,0xe5,0xe3,0xe1,0xde,0xdd,0xda ,0xd8,0xd6,0xd4,0xd1,0xcf,0xcc,
0xca,0xc7,0xc5,0xc2,0xbf,0xbc,0xba,0xb7,0xb4,0xb1,0xae,0xab,0xa8,
0xa5,0xa2,0x9f,0x9c,0x99 ,0x96,0x93,0x90,0x8d,0x89,0x86,0x83,0x80,
0x80,0x7c,0x79,0x76,0x72,0x6f,0x6c,0x69,0x66,0x63,0x60,0x5d,0x5a,
0x57,0x55,0x51 ,0x4e,0x4c,0x48,0x45,0x43,0x40,0x3d,0x3a,0x38,0x35,
0x33,0x30,0x2e,0x2b,0x29,0x27,0x25,0x22,0x20,0x1e,0x1c,0x1a,0x18,
0x16 ,0x15,0x13,0x11,0x10,0x0e,0x0d,0x0b,0x0a,0x09,0x08,0x07,0x06,
0x05,0x04,0x03,0x02,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,
0x00,0x00,0x00,0x00,0x00,0x01,0x02 ,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0a,0x0b,0x0d,0x0e,0x10,0x11,0x13,0x15 ,0x16,0x18,0x1a,
0x1c,0x1e,0x20,0x22,0x25,0x27,0x29,0x2b,0x2e,0x30,0x33,0x35,0x38,
0x3a,0x3d,0x40,0x43,0x45,0x48,0x4c,0x4e ,0x51,0x55,0x57,0x5a,0x5d,
0x60,0x63,0x66 ,0x69,0x6c,0x6f,0x72,0x76,0x79,0x7c,0x80 };
void delay(uint x)
{
 uchar i;
 while(x--)
 {
  for(i=0;i<50;i++);
 }
}
void main(void)
{
   uint i,j;
   CS=0;       
   WR12=0;
   P1=0xff;
   while(1)
     {
    if(sin==0)  //正弦波          
     {    
	while(!sin)
	{
          for(i=0;i<255;i++) 	  
          DAC0832=sin_code[i]; 
       }
       }
       
    if(square==0)   //方波   
     {
	   DAC0832=0xff; 
	   delay(1);
	   DAC0832=0x00; 
	   delay(1);   
           }
	   
     if(triangle==0)//三角波
     {
	
	for(i=0;i<0xff;i++)
	{
	 DAC0832=i;
	 delay(1);
	}
	 for(i=0xff;i>0;i--)
	 {
	 DAC0832=i;
	 delay(1);
	 }
      }   
	   
	   
	
     if(sawtooth==0)  //锯齿波
     {

	 for(i=0;i<=0xff;i++) 
	 DAC0832=i;  
	 delay(1);

      }
 }

3.查阅资料,了解呼吸灯的概念,设计呼吸灯实验

#include<absacc.h>
#include<reg51.h>
#define DAC0832 XBYTE[0x8000]
#define uchar unsigned char
#define uint unsigned int
void delay(uint x)
{
 uchar i;
 while(x--)
 {
  for(i=0;i<50;i++);
 }
}

void main()
{
 uchar i=0,j=1;
 while(1) {
    for(j=1;j<255;j++)
    {
	 if(i<j)
	 {
	  DAC0832=0xff;
	    i++;
	 }
	 else
	 {
	  DAC0832=0x00;
	    i++;
	    if(i==255)
	       i=0;
	 }
	 
      }
      
 }
}

实验 10 AD 接口设计实验

1. 单片机控制 ADC0809 进行 A/D 转换的接口与程序设计。参考图 1,采用查询方式控制 ADC0809 进行 A/D 转换,电位器 RV1 调节输入给ADC0809 的模拟电压,ADC0809 转换结果由 P1 口输出,通过控制发光二极管的亮与灭,来显示转换结果的二进制数字量。

#include "reg51.h"
#define uchar unsigned char
#define uint unsigned int
#define led  P0
#define out  P1
sbit start=P2^1;
sbit oe=P2^7;
sbit eoc=P2^3;
sbit clock=P2^0;
sbit add_a=P2^4;
sbit add_b=P2^5;
sbit add_c=P2^6;

void main(void)
{
uchar  temp;
add_a=0;add_b=0;add_c=0;  //选择ADC0808的通道0
while(1)
	{
	start=0;
	start=1;
	start=0;  //启动转换
	while(1){clock=!clock;if(eoc==1)break;}//等待转换结束
	oe=1;	  //允许输出
	temp=out; //暂存转换结果
	oe=0;	  //关闭输出
	led=temp; //采样结果输出到LED	
	}
}

2. 利用单片机 AT89S51 与 ADC0809 设计一个数字电压表,能够测量 0-5V 之间的直流电压值,四位数码显示。

#include <reg51.H>
#define uchar unsigned char
#define uint unsigned int
uchar code dispcode[4]={0x08,0x04,0x02,0x00};//LED显示的控制代码
uchar code codevalue[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90}; //0~9共阳极字段码
uint temp; //存储ADC0808转换后处理过程中的临时数值
uchar temp_int;
uchar dispbuf[4]; //存储十进制值
sbit ST=P3^0;
sbit OE=P3^1;
sbit EOC=P3^2;
sbit CLK=P3^7;

uchar count; //LED显示位控制
uchar getdata; //ADC0808转换后的数值
void delay(uchar m) //延时
{ while(m--)
{}
}

void main(void) 
{
   ET0=1;
   ET1=1;
   EA=1;
   TMOD=0x12; //0工作在模式2,T1工作在模式1
   TH0=246;
   TL0=246;
   TH1=(65536-10000)/256;
   TL1=(65536-10000)%256;
   TR1=1;
   TR0=1;
while(1)
{ 
   ST=0;
   ST=1; //产生启动转换的正脉冲信号
   ST=0;
   while(EOC==0) {;} //等待转换结束
   OE=1;
   getdata=P0;
   OE=0;
   temp=getdata*19.5; //转换为电压 量程为5V 分辩率为8位 5/256=19.5mV,即1代表19.5mV      
   dispbuf[2]=temp/1000;//1V
   dispbuf[1]=temp/100%10;//0.1V
   dispbuf[0]=temp/10%10;//0.01V
   }
}

void T0X(void)interrupt 1 using 0 //定时/计数器0中断,产生转换时钟 50KHz
{
CLK=~CLK;
}


void T1X(void) interrupt 3 using 0 //定时/计数器1中断,数码管显示
{
   TH0=(65536-10000)/256;
   TL0=(65536-10000)%256;
   for(count=0;count<3;count++)
   {
     if(count==2)
      {
	 P2=dispcode[count];//LED显示的控制代码
	 P1=(codevalue[dispbuf[count]])&(0x7f); //加小数点
      }
      else
      {
      P2=dispcode[count];
      P1=codevalue[dispbuf[count]]; //输出字段码
	 }
	 
	 
      delay(255);
}
}

3. 将实验 1 改为中断方式

#include "reg51.h"
#define uchar unsigned char
#define uint unsigned int
#define led  P0
#define out  P1
sbit start=P2^1;
sbit oe=P2^7;
sbit clock=P2^0;
sbit add_a=P2^4;
sbit add_b=P2^5;
sbit add_c=P2^6;
uchar  temp;


void main(void)
{
   add_a=0;add_b=0;add_c=0;  //选择ADC0808的通道0
   EA=1;
   EX0=1;
   start=0;
   start=1;
   start=0;  //启动转换
	while(1){
	   clock=!clock;
	}
}

void output(void) interrupt 0
{
	oe=1;	  //允许输出
	temp=out; //暂存转换结果
	oe=0;	  //关闭输出
	led=temp; //采样结果输出到LED
   	start=0;
	start=1;
	start=0;  //启动转换
}