第7周作业——单片机定时器与串口通信的学习与应用

2024-06-18 19:12

本文主要是介绍第7周作业——单片机定时器与串口通信的学习与应用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、蜂鸣器
(一)蜂鸣器介绍
蜂鸣器是一种将电信号转换为声音信号的器件,常用来产生设备的按键音、报警音等提示信号,按照驱动方式可以分为如下两种:

1、有源蜂鸣器:内部自带振荡源,将正负极接上直流电压即可持续发声,频率固定。

包括如下种类蜂鸣器:

(1)声音模块蜂鸣器:

工作原理:集成了声音芯片和振膜的组件,通过控制芯片内部的声音文件或音调来产生声音。
特点:可以通过简单的控制方式实现多种声音效果,适合于需要播放特定声音的应用,如警报、音乐等。
(2)电磁式蜂鸣器:

工作原理:利用电磁感应原理产生声音,通过交变电流在线圈产生磁场,使得振膜振动产生声音。
特点:声音相对较大,适合于需要较高音量的应用,但相对于压电蜂鸣器,体积稍大、功耗稍高。
2、无源蜂鸣器:内部不带振荡源,需要控制器提供振荡脉冲才可发声,调整提供振荡脉冲的频率,可发出不同频率的声音。

包括如下种类蜂鸣器:

(1)压电蜂鸣器:

工作原理:利用压电效应产生声音,当施加电压时,压电材料会收缩或膨胀,导致振动产生声音。
特点:体积小,功耗低,响应速度快,适合于需要高频率、短促声音的应用。
(2)压电陶瓷蜂鸣器:

工作原理:结合了压电和陶瓷材料的特性,利用压电效应产生振动,从而产生声音。
特点:结合了压电蜂鸣器和陶瓷蜂鸣器的优点,具有较高的效率和响应速度。
3、有源蜂鸣器和无源蜂鸣器的区别:

有源蜂鸣器和无源蜂鸣器的区别在于是否有源,这个源并不指的是电源而是指震荡源,也就是说有源蜂鸣器内部自带震荡源,所以只要一通电就会发出声音,而无源蜂鸣器内部不带震荡源,所以用直流信号无法令其鸣叫,必须使用交流信号。

(二)蜂鸣器驱动
1、有源蜂鸣器驱动:
有源蜂鸣器不需要外部给予激励源,只需要直接接入直流电源即可自动发出声音(声音频率相对固定),它的发声工作原理是直流电源经过振荡系统的放大取样电路在谐振装置的作用下产生声音信号,原理如下图所示:

2、无源蜂鸣器驱动:

无源蜂鸣器内部没有激励源,只要给予它一个一定频率的方波信号,就能让蜂鸣器的振动装置起振,从而实现无源蜂鸣器发声。同时由于输入的方波频率不同,发出的声音也不同。原理如下图所示:

当我们使用无源蜂鸣器时,如果我们使用直流电源,它不会发出声音。只有交流电源才能发出声音。我们可以通过改变交流电的频率来发出相应的声音。被动蜂鸣器可以通过改变频率来改变其音调,因为没有内部振动源,所以被动蜂鸣器具有声音频率可控的特点,可以演奏音乐。

无源蜂鸣器频率计算:

 公式:频率 = 1/周期,即f = 1/T,二者互为倒数。其中f(频率)的单位为赫兹Hz,T(周期)的单位是秒s。一般我们通过频率来求得周期,比如1KHz对应的周期就是0.001s就是1ms。在实验中我们可以通过延时函数来实现一个周期内输出相同时间的高电平和低电平模拟方波信号将这个信号输给无源蜂鸣器使其发声。

(三)实际应用

1、题目要求:

利用T1的中断控制P1.7引脚输出频率为1kHz方波音频信号,驱动蜂鸣器发声。系统时钟为12MHz。方波音频信号周期1ms,因此T1的定时中断时间为0.5 ms,进入中断服务程序后,对P1.7求反。

2、Proteus电路原理图:

3、代码实现:
#include <REGX51.H>
 
sbit sound = P3^5;
 
void main()
{
    EA=1;               //开总中断.
      ET1=1;              //允许定时器T1中断         .
       TMOD=0x10;             //TMOD=0001 000B,使用T1的方式1定时        
    TH1=0xFE;                  
       TL1=0x33;              //设置定时器1的初值为0xFE33,使定时器每次溢出所需的时间为500微秒。
       TR1=1;              //启动T1
    while(1)
    {
    }
}
 
void Timer1_Riutine(void)  interrupt 3
{
    
    sound = ~sound;
    TH1=0xFE;                  
       TL1=0x33;
 
}
4、Proteus仿真:

5、开发板实现:

二、LED数码管秒表
(一)数码管原理介绍:
1、简单介绍:
LED数码管由多个发光二极管封装在一起组成“8”字型的器件,引线已在内部连接完成,只需要引出它的各个笔画以及公共电极。数码管实际上是由七个发光管组成八字形构成的,加上一个小数点加上8个。这些段分别由字母a,b,c,d,e,f,g,dp来表示。

2、LED数码管的分类:

按显示段数分:分为七段、八段、九段、十四段和十六段

七段:七段数码管由7个LED构成;八段:八段数码管比七段多了一个小数点;

九段:九段数码管由9个LED构成;十四段:十四段数码管由14个LED构成;

十六段:十六段数码管由16个LED构成

3、按内部发光二极管单元的连接方式分类:
在proteus元器件库中,共阳数码管为7SEG-MPX1-CA,共阴数码管为7SEG-MPX1-CC。

(1)共阳极:

当LED另一端接入电源的时候,与另一端产生电势差因此会有电流从正极流到GND,最后会亮灯

当LED另一端接入地电源的时候,则不会产生电势差也就不会亮灯。

(2)共阴极:

当LED另一端接入电源的时候,不会产生电势差因此不会亮灯。

当LED另一端接地的时候,会产生电势差,电流会从电源端流经LED到地端,会亮灯。

4、数码管段码表:
将数码管的8个段当成8个LED小灯来控制,即a、b、c、d. e、f、g、dp- -共8个LED小灯。如果点亮b和c这两个LED小灯,也就是数码管的a,b,c,d,e,f段,其他的所有的段都熄灭的话,就可以让数码管显示出一个数字0,以共阳极数码管为例,二进制数字为0b11000000,十六进制为0xC0。同理可得其他数字多对应的数码管的真值,如下表:

共阳极数码管段码表:

共阴极数码管的段码表:

(二)实际应用:
1、题目要求:
用2位数码管显示计时时间,最小计时单位为“百毫秒”,计时范围0.1~9.9s。当第1次按一下计时功能键时,秒表开始计时并显示;第2次按一下计时功能键时,停止计时,将计时的时间值送到数码管显示;如果计时到9.9s,将重新开始从0计时;第3次按一下计时功能键,秒表清0。再次按一下计时功能键,则重复上述计时过程。    本秒表应用定时器模式,计时范围0.1~9.9s。此外还涉及如何编写控制LED数码管显示的程序。

2、Proteus电路原理图:

3、代码实现:
#include <REGX51.H>
typedef unsigned int uint;
typedef unsigned char uchar;
 
uchar discode1[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};//第一个
uchar discode2[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//第二个
 
uchar timer = 0;//中断次数
uchar second;//秒数
uchar key = 0;//按键次数
 
sbit keyif = P3^7;//定义按键引脚
 
void Delay1ms(unsigned int xms)        //@12.000MHz
{
    unsigned char i, j;
    while(xms)
    {
        i = 2;
        j = 239;
        do
        {
            while (--j);
        } while (--i);
        xms--;
    }
}//延时函数,按键消抖
 
void main()
{
    TMOD = 0x01;
    ET0 = 1;
    EA = 1;
    second = 0;
    P0=discode1[second/10];           //显示秒位0
    P2=discode2[second%10];           //显示0.1s位0
    while(1)
    {
      if(keyif == 0)
      {
        Delay1ms(5);
          if(keyif == 0)
          {
            key++;
            switch(key)
            {
                case 1:
                TH0 = 0xEE;
                TL0 = 0x00;
                TR0 = 1;
                break;
                case 2:
                timer = 0;
                TR0 = 0;
                break;
                case 3:
                key = 0;
                second = 0;
                P0 = discode1[0];
                P2 = discode2[0];
                break;    
            }
            while(keyif == 0); //如果按键时间过长在此循环
 
            }
        }          
    }
}
 
void timer0() interrupt 1
{
    TR0 = 0;
    timer++;
    if(timer == 20)
    {
        second++;
        P0=discode1[second/10];     //根据计时,即时显示秒位        
        P2=discode2[second%10];     //根据计时,即时显示0.1s位
        timer = 0;
    }
    if(second == 99)//计时到达9.9s
    {
        TR0 = 0;//停止计时
        second = 0;//秒数清零
        key = 2;//停止计时
    }
    else
    {
        TR0 = 1;//继续计时
    }
 
}
4、Proteus仿真:

三、LCD1602显示时钟
(一)LCD1602模块简介
LCD1602(Liquid Crystal Display)液晶显示屏是一种字符型液晶显示模块,可以显示ASCII码的标准字符和其它的一些内置特殊字符,还可以有8个自定义字符
显示容量:16×2个字符,每个字符为5*7点阵

LCD1602引脚接线:

各个引脚的作用如下:

其中:

  • RS是寄存选择,高电平选择数据寄存器,低电平选择指令寄存器。
  • RW为读写选择,高电平进行读操作,低电平进行写操作。
  • E端为使能端,后面与时序联系在一起。

LCD1602内部结构框图:

1、DDRAM(数据显示区)

2、CGROM(字模库)

(二)LCD1602驱动

1、写数据/指令时序

我们来分析一下时序图,当我们要写指令的时候,RS置为低电平,RW置为低电平,EN置为低电平,然后将指令数据送到数据口D0~D7,延时tsp1,让1602准备接收数据,这时候将EN拉高,产生一个上升沿,这时候指令就开始写入LCD,延时一段时间,将EN置低电平。

当我们要写数据的时候,RS置为高电平,RW置为低电平,EN置为低电平,然后将指令数据送到数据口D0~D7,延时tsp1,让1602准备接收数据,这时候将EN拉高,产生一个上升沿,这时候数据就开始写入LCD,延时一段时间,将EN置低电平。

写指令的步骤:

Step1:将RS置0;
Step2:将RW置0;
Step3:将指令(Command)写入LCD_DataPort
Step4:将EN置1;
Step5:延时1ms;
Step6:将EN置0;
Step7:延时1ms;

写指令代码:

void LCD_WriteCommand(unsigned char Command)//LCD写命令
{
    LCD_RS=0;
    LCD_RW=0;
    LCD_DataPort=Command;
    LCD_EN=1;
    LCD_Delay();
    LCD_EN=0;
    LCD_Delay();
}
写数据的步骤:

Step1:将RS置0;
Step2:将RW置0;
Step3:将数据(Data)写入LCD_DataPort
Step4:将EN置1;
Step5:延时1ms;
Step6:将EN置0;
Step7:延时1ms;

写数据代码:

void LCD_WriteData(unsigned char Data)//LCD写数据
{
    LCD_RS=1;
    LCD_RW=0;
    LCD_DataPort=Data;
    LCD_EN=1;
    LCD_Delay();
    LCD_EN=0;
    LCD_Delay();
}
2、指令集

常用指令:

(1)清显示,指令码01H

功能:

  • 光标复位到地址00H位置,
  • LCD显示DDRAM的内容全部写入” “的ASCII码20H

(2)显示开关控制

功能:

  • D(Dispaly):控制整体的显示开与关,高电平表示开显示屏,低电平表示关显示屏
  • C(Cursor):控制光标的开与关,高电平表示有光标,低电平表示无光标
  • B(Blink):控制光标是否闪烁,高电平闪烁,低电平不闪烁

(3)功能设置命令

DL:DL=1代表数据长度为8位,DL=0代表数据长度为4位
N:低电平时只有一行可以显示,高电平时两行都可以显示,
F:低电平时一个字符大小为5X7的点阵字符,高电平时一个字符大小为5X10的点阵字符。
3、LCD初始化
初始化:     发送指令0x38    //八位数据接口,两行显示,5*7点阵     

                   发送指令0x0C    //显示开,光标关,闪烁关     

                   发送指令0x06    //数据读写操作后,光标自动加一,画面不动     

                   发送指令0x01    //清屏

代码:

void LCD_Init()
{
    LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
    LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
    LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
    LCD_WriteCommand(0x01);//光标复位,清屏
}
4、其余常用LCD1602代码:
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)//LCD显示字符
{
    LCD_SetCursor(Line,Column);
    LCD_WriteData(Char);
}
 
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)//LCD显示字符串
{
    unsigned char i;
    LCD_SetCursor(Line,Column);
    for(i=0;String[i]!='\0';i++)
    {
        LCD_WriteData(String[i]);
    }
}
 
int LCD_Pow(int X,int Y)
{
    unsigned char i;
    int Result=1;
    for(i=0;i<Y;i++)
    {
        Result*=X;
    }
    return Result;
}
 
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)//LCD显示数字
{
    unsigned char i;
    LCD_SetCursor(Line,Column);
    for(i=Length;i>0;i--)
    {
        LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
    }
}
 
(三)实际应用:
1、题目要求:
使用定时器实现一个LCD显示时钟。采用LCD1602,最小计时单位是秒,如何获得1s的定时?可将T0定时时间定为50ms,采用中断方式进行溢出次数累计,满20次,则秒计数变量second加1;若秒计满60,则分计数变量minute加1,同时将秒计数变量second清0;若分钟计满60,则小时计数变量hour加1;若小时计数变量满24,则将小时计数变量hour清0。

2、Proteus电路原理图:

3、代码实现:
#include <REGX51.H>
 
 
typedef unsigned char uchar;
typedef unsigned int uint;
 
uchar Hour=23,Min=59,Sec=55;
 
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P3
 
void LCD_Delay()
{
    unsigned char i, j;
 
    i = 2;
    j = 239;
    do
    {
        while (--j);
    } while (--i);
}
 
void LCD_WriteCommand(unsigned char Command)//LCD写命令
{
    LCD_RS=0;
    LCD_RW=0;
    LCD_DataPort=Command;
    LCD_EN=1;
    LCD_Delay();
    LCD_EN=0;
    LCD_Delay();
}
 
void LCD_WriteData(unsigned char Data)//LCD写数据
{
    LCD_RS=1;
    LCD_RW=0;
    LCD_DataPort=Data;
    LCD_EN=1;
    LCD_Delay();
    LCD_EN=0;
    LCD_Delay();
}
 
void LCD_SetCursor(unsigned char Line,unsigned char Column)//LCD设置光标位置
{
    if(Line==1)
    {
        LCD_WriteCommand(0x80|(Column-1));
    }
    else if(Line==2)
    {
        LCD_WriteCommand(0x80|(Column-1+0x40));
    }
}
 
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)//LCD显示字符
{
    LCD_SetCursor(Line,Column);
    LCD_WriteData(Char);
}
 
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)//LCD显示字符串
{
    unsigned char i;
    LCD_SetCursor(Line,Column);
    for(i=0;String[i]!='\0';i++)
    {
        LCD_WriteData(String[i]);
    }
}
 
int LCD_Pow(int X,int Y)
{
    unsigned char i;
    int Result=1;
    for(i=0;i<Y;i++)
    {
        Result*=X;
    }
    return Result;
}
 
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)//LCD显示数字
{
    unsigned char i;
    LCD_SetCursor(Line,Column);
    for(i=Length;i>0;i--)
    {
        LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
    }
}
 
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
    unsigned char i;
    unsigned int Number1;
    LCD_SetCursor(Line,Column);
    if(Number>=0)
    {
        LCD_WriteData('+');
        Number1=Number;
    }
    else
    {
        LCD_WriteData('-');
        Number1=-Number;
    }
    for(i=Length;i>0;i--)
    {
        LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
    }
}
 
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
    unsigned char i,SingleNumber;
    LCD_SetCursor(Line,Column);
    for(i=Length;i>0;i--)
    {
        SingleNumber=Number/LCD_Pow(16,i-1)%16;
        if(SingleNumber<10)
        {
            LCD_WriteData(SingleNumber+'0');
        }
        else
        {
            LCD_WriteData(SingleNumber-10+'A');
        }
    }
}
 
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
    unsigned char i;
    LCD_SetCursor(Line,Column);
    for(i=Length;i>0;i--)
    {
        LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
    }
}
 
void LCD_Init()
{
    LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
    LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
    LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
    LCD_WriteCommand(0x01);//光标复位,清屏
}
 
void Timer0_Init()        //定时器初始化
{
    TMOD |= 0x01;        //设置定时器模式
    TL0 = 0x18;        //设置定时初值,与通过计算的得到有一定误差。
    TH0 = 0xFC;        //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 1;        //定时器0开始计时
    
    ET0=1;
    EA=1;//将中断打通即令ET0=1。
}
 
void main()
{
    LCD_Init();
    Timer0_Init();
    LCD_ShowString(1,1,"  :  :  ");
    while(1)
    {
        LCD_ShowNum(1,1,Hour,2);
        LCD_ShowNum(1,4,Min,2);
        LCD_ShowNum(1,7,Sec,2);    
    }
}
 
void Timer0_Routine() interrupt 1//定时器0的中断程序
{
        static unsigned int T0Count;//静态变量,让局部变量变为全局变量。
        TL0 = 0x18;        
        TH0 = 0xFC;//重新赋值,计时器重新开始运行。
        T0Count++;
        if(T0Count>=1000)//定时为1s
        {
            T0Count=0;
            Sec++;
            if(Sec>=60)
            {
                Sec=0;
                Min++;
                if(Min>=60)
                {
                    Min=0;
                    Hour++;
                    if(Hour>=24)
                    {
                        Hour=0;
                    }
                }
            }
        }//每隔1s。
        
}//中断函数
4、Proteus仿真:

5、开发板实现:

四、串口通信
(一)串口简介
串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。

单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力。

51单片机内部自带UART(Universal Asynchronous Receiver Transmitter,通用异步收发器),可实现单片机的串口通信。

1、串口通信的功能
数据通信:

数据通信通常是指单片机与单片机之间,或者单片机与其他设备之间的信息交换。通常把数据通信分为如下两种:

(1)并行通信:数据的各位同时进行发送或接收的通信方式。优点是速率高。缺点是需要的传输线多,成本高,只适合近距离的数据通信。

(2)串行通信:一位一位的按顺序的进行发送或接收的通信方式。优点是需要的传输线少,成本低。缺点是传输的速率慢,适合远距离的数据通信。

2、单片机串口结构

结构图:

(1)两个数据缓冲器:SBUF,串口数据缓存寄存器,物理上是两个独立的寄存器,但占用相同的地址。写操作时,写入的是发送寄存器,读操作时,读出的是接收寄存器。

(2)定时器1产生波特率, 串口一般使用定时器1,模式2,八位自动重装模式,来产生溢出率,从而产生波特率。而且在配置定时器相关的寄存器时不用配置定时器中断,只是使用定时器1来产生波特率的功能

(3)移位寄存器,在接受控制器的控制下,将输入的数据逐位移入接收SBUF。

(4)串行控制寄存器SCON,SCON的功能是控制串行通信口的工作方式以及工作的状态。

(二)串行通信口的控制寄存器
1、串行口控制寄存器SCON
STC89C51RC/RD+系列单片机的串行口设有两个控制寄存器:串行控制寄存器SCON和波特率选择特殊功能寄存器PCON。
串行控制寄存器SCON用于选择串行通信的工作方式和某些控制功能。

器来对串行通信的工作模式进行控制。
(1)SM0/FE:当PCON寄存器中的SMODO/PCON.6位为1时,该位用于帧错误检测。当检测到一个SMO/FE:无效停止位时,通过UART接收器设置该位。它必须由软件清零。当PCON寄存器中的SMOD0/PCON.6位为0时,该位和SM1一起指定串行通信的工作方式,如下表所示。

(2)REN:允许/禁止串行接收控制位。由软件置位REN,即REN=1为允许串行接收状态,可启动串行接收器RD,开始接收信息。软件复位REN,即REN-0,则禁止接收。

(3)TI:发送中断请求标志位。在方式0,当串行发送数据第8位结束时,由内部硬件自动置位,即TI=1,向主机请求中断,响应中断后必须用软件复位,即TI=0。在其他方式中,则在停止位开始发送时由内部硬件置位,必须用软件复位。

(4)RI:接收中断请求标志位。在方式0,当串行接收到第8位结束时由内部硬件自动置位RI=1,向主机请求中断,响应中断后必须用软件复位,即RI=0。在其他方式中,串行接收到停止位的中间时刻由内部硬件置位,即RI=1(例外情况见SM2说明),必须由软件复位,即RI=0。

2、电源控制寄存器PCON
配置PCON寄存器

(1)SMOD:波特率选择位。当用软件置位SMOD,即SMOD=1,则使串行通信方式1、2、3的波特率加倍;SMOD=0,则各工作方式的波特率加倍。复位时SMOD=0。

(2)SMOD0:帧错误检测有效控制位。当SMOD0=1,SCON寄存器中的SMO/FE位用于FE(帧错误检测)功能;当SMOD0=0,SCON寄存器中的SMOFE位用于SM0功能,和SM1一起指定串行口的工作方式。复位时SMOD0=0。

3、配置中断

(三)实际应用
1、题目一:
(1)题目要求:
将单片机串口与笔记本电脑串口模块相连,单片机每隔2秒发送“Hello C51”,笔记本电脑用串口助手软件接收。 如果串口助手发送字符“0" 给单片机,则单片机停止发送; 如果单片机收到“1”,则继续每隔2秒发送“Hello C51”。

(2)代码实现:

#include <REGX51.H>
#include "stdio.h"
 
unsigned char a;
unsigned char Flag=1;
void Delay1ms(unsigned int xms)        //@12.000MHz
{
    unsigned char i, j;
    while(xms)
    {
        i = 2;
        j = 239;
        do
        {
            while (--j);
        } while (--i);
        xms--;
    }
}
void UartInit(void)        //9600bps@12.000MHz
{
    PCON &= 0x7F;        //波特率不倍速
    SCON = 0x50;        //8位数据,可变波特率
    TMOD &= 0x0F;        //清除定时器1模式位
    TMOD |= 0x20;        //设定定时器1为8位自动重装方式
    TL1 = 0xFD;        //设定定时初值
    TH1 = 0xFD;        //设定定时器重装值
    ET1 = 0;        //禁止定时器1中断
    TR1 = 1;        //启动定时器1
    EA = 1;
    ES = 1;
}
 
 
void UartSend()
{
        TI=1;
        puts("Hello C51");
        while(!TI);
        TI=0;
        Delay1ms(2000);
}
 
void main()
{
    UartInit();
    while(1)
    {
        if(Flag==1)
        UartSend();
    }    
}
 
 
//串口中断函数模板
void UART_Routine()    interrupt 4 //串口中断
{
    if(RI==1)
    {
        RI=0;
        a=SBUF;
        if(a=='1')Flag=1;
        if(a=='0')Flag=0;
    }
}

(3)串口助手效果

2、题目二:
(1)题目要求:
甲、乙两单片机进行 方式3(或方式2)串行通信。甲机把控制8个流水灯点亮的数据发送给乙机并点亮其P1口的8个LED。方式3比方式1多了1个可编程位TB8,该位一般作奇偶校验位。乙机接收到的8位二进制数据有可能出错,需进行奇偶校验,其方法是将乙机的RB8和PSW的奇偶校验位P进行比较,如果相同,接收数据;否则拒绝接收。

(2)Proteus电路原理图:

(3)代码实现:
甲机:

#include <REGX51.H>
sbit T_P=PSW^0;
unsigned char code Tab[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//流水灯程序
void Send(unsigned char dat)
{
    TB8=T_P;
    SBUF=dat;
    while(TI==0);
    TI=0;
}
void Delay1ms(unsigned int xms)        //@12.000MHz
{
    unsigned char i, j;
    while(xms)
    {
        i = 2;
        j = 239;
        do
        {
            while (--j);
        } while (--i);
        xms--;
    }
}
void main()
{
    unsigned char i;
    TMOD=0x20;
    SCON=0xc0;
    PCON&=0x7f;
    TH1=0xfd;
    TL1=0xfd;
    TR1=1;
    while(1)
    {
        for(i=0;i<8;i++)
        {
            Send(Tab[i]);
            Delay1ms(200);
        }
    }
}
乙机:

#include <REGX51.H>
 
sbit R_P=PSW^0;
 
unsigned char Receive()//接收一字节数据
{
    unsigned char dat;
    while(RI==0);//检测RI,RI=0,未接收完
    RI=0;            //接收数据完成RI手动清0
    ACC=SBUF;        //将接收缓冲器的数据存于ACC
    if(RB8=R_P)     //只有偶检验成功才能往下执行,接收数据
    {
        dat=ACC;    //将ACC数据存于dat
        return dat;    //将接收的数据返回
    }
}
 
void main()
{
    TMOD=0x20;  
    SCON=0xd0;    
    PCON&=0x7f;
    TH1=0xfd;    
    TL1=0xfd;
    TR1=1;
    while(1)
    {
         P2=Receive();    
    }
}
(4)Proteus仿真:

这篇关于第7周作业——单片机定时器与串口通信的学习与应用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1072864

相关文章

51单片机学习记录———定时器

文章目录 前言一、定时器介绍二、STC89C52定时器资源三、定时器框图四、定时器模式五、定时器相关寄存器六、定时器练习 前言 一个学习嵌入式的小白~ 有问题评论区或私信指出~ 提示:以下是本篇文章正文内容,下面案例可供参考 一、定时器介绍 定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。 定时器作用: 1.用于计数系统,可

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

[word] word设置上标快捷键 #学习方法#其他#媒体

word设置上标快捷键 办公中,少不了使用word,这个是大家必备的软件,今天给大家分享word设置上标快捷键,希望在办公中能帮到您! 1、添加上标 在录入一些公式,或者是化学产品时,需要添加上标内容,按下快捷键Ctrl+shift++就能将需要的内容设置为上标符号。 word设置上标快捷键的方法就是以上内容了,需要的小伙伴都可以试一试呢!

AssetBundle学习笔记

AssetBundle是unity自定义的资源格式,通过调用引擎的资源打包接口对资源进行打包成.assetbundle格式的资源包。本文介绍了AssetBundle的生成,使用,加载,卸载以及Unity资源更新的一个基本步骤。 目录 1.定义: 2.AssetBundle的生成: 1)设置AssetBundle包的属性——通过编辑器界面 补充:分组策略 2)调用引擎接口API

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J

大学湖北中医药大学法医学试题及答案,分享几个实用搜题和学习工具 #微信#学习方法#职场发展

今天分享拥有拍照搜题、文字搜题、语音搜题、多重搜题等搜题模式,可以快速查找问题解析,加深对题目答案的理解。 1.快练题 这是一个网站 找题的网站海量题库,在线搜题,快速刷题~为您提供百万优质题库,直接搜索题库名称,支持多种刷题模式:顺序练习、语音听题、本地搜题、顺序阅读、模拟考试、组卷考试、赶快下载吧! 2.彩虹搜题 这是个老公众号了 支持手写输入,截图搜题,详细步骤,解题必备

零基础STM32单片机编程入门(一)初识STM32单片机

文章目录 一.概要二.单片机型号命名规则三.STM32F103系统架构四.STM32F103C8T6单片机启动流程五.STM32F103C8T6单片机主要外设资源六.编程过程中芯片数据手册的作用1.单片机外设资源情况2.STM32单片机内部框图3.STM32单片机管脚图4.STM32单片机每个管脚可配功能5.单片机功耗数据6.FALSH编程时间,擦写次数7.I/O高低电平电压表格8.外设接口

《offer来了》第二章学习笔记

1.集合 Java四种集合:List、Queue、Set和Map 1.1.List:可重复 有序的Collection ArrayList: 基于数组实现,增删慢,查询快,线程不安全 Vector: 基于数组实现,增删慢,查询快,线程安全 LinkedList: 基于双向链实现,增删快,查询慢,线程不安全 1.2.Queue:队列 ArrayBlockingQueue:

通信系统网络架构_2.广域网网络架构

1.概述          通俗来讲,广域网是将分布于相比局域网络更广区域的计算机设备联接起来的网络。广域网由通信子网于资源子网组成。通信子网可以利用公用分组交换网、卫星通信网和无线分组交换网构建,将分布在不同地区的局域网或计算机系统互连起来,实现资源子网的共享。 2.网络组成          广域网属于多级网络,通常由骨干网、分布网、接入网组成。在网络规模较小时,可仅由骨干网和接入网组成

ROS话题通信流程自定义数据格式

ROS话题通信流程自定义数据格式 需求流程实现步骤定义msg文件编辑配置文件编译 在 ROS 通信协议中,数据载体是一个较为重要组成部分,ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty… 但是,这些数据一般只包含一个 data 字段,结构的单一意味着功能上的局限性,当传输一些复杂的数据,比如: