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

news2024/11/29 4:37:05

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

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仿真:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1848054.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自动化测试:Autorunner的使用

自动化测试&#xff1a;Autorunner的使用 一、实验目的 1、掌握自动化测试脚本的概念。 2、初步掌握Autorunner的使用 二、Autorunner的简单使用 autoRunner使用方法 新建项目 a) 在项目管理器空白区域,右键鼠标,选择新建项目 b) 输入项目名后,点击[确定]. 在初次打开aut…

[java]集合类stream的相关操作

1.对list中的map进行分组 下面例子中&#xff0c;根据高度height属性进行分组 List<Map<String, Float>>originalList new ArrayList<>();originalList.add(new HashMap<String,Float>() {{put("lng", 180.0f);put("lat",90f);…

Eclipse使用TFS(Team Foundation Server) 超详细

Eclipse使用TFS 1、什么是TFS2、TFS和Git的区别3、签出代码4、签入代码4.1、签出以进行编辑4.2、修改本地代码4.3、签入挂起的更改4.4、签入 如果不能 签入挂起的更改&#xff0c;则先 签出以进行编辑如果 签入挂起的更改不可选中&#xff0c;则 如下操作 1、什么是TFS Team F…

阿里云使用域名访问部署网站【2024 详细版】

目录 一、注册域名 1.创建信息模板 2.查询注册域名 二、域名设置 1.SSL证书 2.域名解析 3.宝塔设置 一、注册域名 1.创建信息模板 点击右上角【三】-【域名】-【信息模板】-【创建信息模板】- 填写信息 模板分为个人和企业两种&#xff0c;根据情况进行创建即可&…

c++ 里构造函数的形参与数据成员的同名问题

如题&#xff0c;这时&#xff0c;或许在 java 里&#xff0c;会报语法错误。但在 c vs2019 开了 c20语法规范。这不再是错误。这样的好处是解决了咱们的起变量名的麻烦&#xff1a;重名现在已不是错误&#xff0c;编译器可以解决了。测试如下&#xff1a; 我们看看 c 编译器是…

揭秘!速卖通、敦煌网、国际站出单背后的黑科技:自养号测评技术

在竞争激烈的跨境电商平台上&#xff0c;如亚马逊、速卖通、Lazada、Shopee、敦煌网、Temu、Shein、美客多和阿里国际等&#xff0c;稳定出单成为每位卖家共同追求的目标。为了实现这一目标&#xff0c;卖家需要从产品选择、运营策略和客户服务等多个维度进行全面考量&#xff…

【C语言】13.指针与数组的关系

一、数组名的理解 #include <stdio.h> int main() {int arr[10] { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0] %p\n", &arr[0]);printf("arr %p\n", arr);return 0; }通过上述代码输出结果我们发现结果相同&#xff0c;因此我们得出结论&a…

电脑开机后出现Aptio Setup Utility 处理方法

电脑开机后出现Aptio Setup Utility怎么处理 Aptio Setup Utility界面的原因&#xff1a; 这是由于 bios设置与真实的硬件情况不匹配硬盘故障找不到可启动的硬盘情况 我的问题是找不到可启动的硬盘情况 解决方式如下&#xff1a; 进入如下界面了&#xff0c;选择Boot选项…

SAP_ABAP模块-记录第三方通过Webservice调用SAP接口时的问题

一、业务背景&#xff1a; 最近SAP有个货物移动的接口需要优化&#xff0c;之前与第三方销售管理平台对接&#xff0c;一直都没有问题的&#xff0c;但是现在SAP这边优化后&#xff0c;然后对方平台的开发同事说&#xff0c;调用不成功&#xff0c;报错了&#xff0c;最开始一直…

vue3 antdv RadioButton默认值选择问题处理

1、先上官方文档&#xff1a; Ant Design Vue — An enterprise-class UI components based on Ant Design and Vue.js 官方代码&#xff1a; <template><div><div><a-radio-group v-model:value"value1"><a-radio-button value"a…

添加右键菜单(以git为例)

1、打开注册表编辑器 打开系统注册表&#xff0c;使用组合键“Win R”输入“regedit”。 依次展开”HKEY_CLASSES_ROOT\Directory\Background\shell”。 2、新建右键菜单项 在[Background]下找到“shell”如果没有则新建项shell&#xff0c;接着在“shell”下右键-新建项名…

M12单端I/O预铸法兰插座A-code

M12单端I/O预铸法兰插座A-code概述 M12单端I/O预铸连接器A-code是一种常用于工业自动化领域的连接器件&#xff0c;主要用于传感器和执行器之间的信号传输。它的设计遵循国际标准IEC 61076-2-101&#xff0c;具有良好的防水防尘性能&#xff0c;通常达到IP67的保护等级。M12连…

kettle无法启动问题_PENTAHO_JAVA_HOME

1&#xff0c;遇到spoon.bat启动报错&#xff1a;先增加pause看清错误信息 1.1&#xff0c;错误信息 1.2&#xff0c;因为本地安装jdk1.6无法支持现有版本kettle。只能手动执行kettle调用的java路径&#xff1b;如下 系统--高级系统设置--高级--环境变量 启动成功

Day 29:1600. 王位继承顺序

Leetcode1600. 王位继承顺序 一个王国里住着国王、他的孩子们、他的孙子们等等。每一个时间点&#xff0c;这个家庭里有人出生也有人死亡。 这个王国有一个明确规定的王位继承顺序&#xff0c;第一继承人总是国王自己。我们定义递归函数 Successor(x, curOrder) &#xff0c;给…

java内存溢出堆栈分析

一、背景 java应用系统内存溢出导致服务不可用&#xff0c;可手动生成dump文件或添加配置参数生成文件&#xff0c;进行dump文件分析定位具体内存OOM的原因&#xff0c;并优化修复。 二、JProfile分析 分析工具下载 https://download.csdn.net/download/xinpz/89463963 三、…

【案例分析:基于 Python 的几种神经网络构建 一维的和二维的全介质和金属SPR 材料的光谱预测与逆向设计】

案例分析&#xff1a;传播相位与几何相位超构单元仿真与器件库提取与二维超构透镜设计与传播光场仿真 案例分析&#xff1a; 片上的超构单元仿真与光学参数提取 案例分析&#xff1a;基于粒子群方法的耦合器设计 案例分析&#xff1a;基于 Python 的几种神经网络构建 一维的和二…

硫化物固态电解质在全固态锂电池制造领域发展潜力大

硫化物固态电解质在全固态锂电池制造领域发展潜力大 固态电解质主要包括氧化物、硫化物、聚合物等类型。氧化物固态电解质由于研发难度相对较低&#xff0c;是目前主流技术路线。硫化物固态电解质研发难度较高&#xff0c;但性能优异&#xff0c;特别适合制造全固态锂电池&…

【秋招刷题打卡】Day01-自定义排序

Day01-自定排序 前言 给大家推荐一下咱们的 陪伴打卡小屋 知识星球啦&#xff0c;详细介绍 >笔试刷题陪伴小屋-打卡赢价值丰厚奖励 < ⏰小屋将在每日上午发放打卡题目&#xff0c;包括&#xff1a; 一道该算法的模版题 (主要以力扣&#xff0c;牛客&#xff0c;acwin…

信息学奥赛初赛天天练-31-CSP-J2022基础题-指针、数组、链表、进制转换、深度优先搜索、广度优先搜索、双栈实现队列应用

PDF文档公众号回复关键字:20240621 2022 CSP-J 选择题 单项选择题&#xff08;共15题&#xff0c;每题2分&#xff0c;共计30分&#xff1a;每题有且仅有一个正确选项&#xff09; 3.运行以下代码片段的行为是 ( ) int x 101; int y 201; int * p &x; int * q &y;…

【Golang - 90天从新手到大师】Day09 - string

系列文章合集 Golang - 90天从新手到大师 String 一个字符串是一个不可改变的字节序列。字符串可以包含任意的数据&#xff0c;但是通常是用来包含人类可读的文本。 len()返回字符串字节数目&#xff08;不是rune数&#xff09;。 通过索引可以访问某个字节值&#xff0c;0…