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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

警告:Hydration attribute mismatch on Note: this mismatch is check-only.(水合不匹配)

vue3Nuxt3运行代码是提示如下警告 [Vue warn]: Hydration attribute mismatch on <ul id​"sub_menu_5_$$_sub1-popup" class​"ant-menu ant-menu-sub ant-menu-inline" data-menu-list​"true" style​"display:​none;​">​…

苹果手机618多次降价:京东618iPhone 15 Pro Max降价超2300元 销量重回销量榜第一

一年一度的618大促快接近尾声&#xff0c;在大促期间&#xff0c;iPhone 15多次降价后重回销量榜第一名。 买手机这个一定要领&#xff0c;你懂的&#xff01; &#xff08;20号结束 &#xff09; 淘宝APP搜&#xff1a;领到就赚3300 京东APP搜&#xff1a;好运红包588 如下…

88. 合并两个有序数组(简单)

88. 合并两个有序数组 1. 题目描述2.详细题解3.代码实现3.1 Python3.2 Java 1. 题目描述 题目中转&#xff1a;88. 合并两个有序数组 2.详细题解 两个数组均有序&#xff08;非递减&#xff09;&#xff0c;要求合并两个数组&#xff0c;直观的思路&#xff0c;借助第三个数…

【多线程】线程状态

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. 枚举线程所有状态2. 线程转移2.1 示意图2.2 观察 NEW 、 RUNNABLE 、 TERMINATED 状态的转换2.3 观察 WAI…

体育世界杂志体育世界杂志社体育世界编辑部2024年第5期目录

体育社会学 高校体育专业学生思想政治教育实效性探析 王小会; 11-13 高海拔援建人员体育科学保障的探索实践 冯斌;陈宇;赵文男; 14-16 温州市小学小篮球运动发展现状及优化策略研究 林秀丽; 17-19《体育世界》投稿&#xff1a;cn7kantougao163.com 上海市空竹运…

面向对象的进阶---static

1.static 静态变量 package com.itheima.a01staticdemo01;public class Student {private String name;private int age;public static String teacherName;public Student() {}public Student(String name, int age) {this.name name;this.age age;}/*** 获取* return n…

前端传进来的单选值是0,到了后端加了个逗号

如上图所示&#xff0c;标记的var的值org和id的值orgOrNot不能一样&#xff0c;如果一样&#xff0c;通过id获取&#xff08;#(“#orgOrNot”).find(“option:selected”).val()&#xff09;时候就会出现这种情况 改成如下情况&#xff0c;区别开id

C++ 61 之 函数模版

#include <iostream> #include <string> using namespace std;void swapInt(int &a,int &b){int temp a;a b;b temp; }void swapDou(double& a, double& b){double temp a;a b;b temp; }// T代表通用数据类型&#xff0c;紧接着后面的代码&a…

做户用光伏代理需要多少钱?

随着全球对可再生能源和清洁能源的关注度日益提高&#xff0c;光伏技术作为其中的佼佼者&#xff0c;已经成为许多投资者和创业者关注的焦点。户用光伏系统作为其中的一个重要分支&#xff0c;其市场潜力巨大&#xff0c;吸引了越来越多的投资者和创业者进入这一领域。那么&…

Linux实现: 客户端(cli01)通过TCP(或UDP)连接到聊天服务器(serv)进行聊天?(伪代码版本)

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

使用 C# 学习面向对象编程:第 7 部分

多态性 我们在程序中使用多态的频率是多少&#xff1f;多态是面向对象编程语言的第三大支柱&#xff0c;我们几乎每天都在使用它&#xff0c;却不去想它。 这是一个非常简单的图表&#xff0c;它将解释多态性本身。 简单来说&#xff0c;我们可以说&#xff0c;只要我们重载类…

音乐管理系统 SpringBoot + vue

文章目录 1、简要介绍2、数据库设计3、解决的问题1、图片和音频的上传和存储2、分页功能 4、数据返回 也算是进行了半个学期&#xff0c;跟着老师讲的进行 后端使用SpringBoot 前端 vue layui jdk 18 项目地址&#xff1a;gitee 1、简要介绍 只有管理端&#xff0c;但是对用…

超多细节—app图标拖动排序实现详解

前言&#xff1a; 最近做了个活动需求大致类似于一个拼图游戏&#xff0c;非常接近于咱们日常app拖动排序的场景。所以想着好好梳理一下&#xff0c;改造改造干脆在此基础上来写一篇实现app拖动排序的文章&#xff0c;跟大家分享下这个大家每天都要接触的场景&#xff0c;到底…

Golang并发控制的三种方案

Channel Channel是Go在语言层面提供的一种协程间的通信方式&#xff0c;我们可以通过在协程中向管道写入数据和在待等待的协程中读取对应协程的次数来实现并发控制。 func main() {intChan : make(chan int, 5)waitCount : 5for i : 0; i < waitCount; i {go func() {intC…

ISCC2024 WriteUp

msic Funzip Funzip writeup解题思路 1.打开题目发现是一个base64 2.看了一遍后发现他不是很全于是写一个脚本进行补全 wf open("5.txt", "w") with open("1 (2).txt", "r") as f: data f.read() data data.splitlines() for l…

【AI+多智能体框架】个人整理的几款AI多智能体框架

昨天无意间了解到 alipay开源 的多智能体框架agentUniverse &#xff0c;这里聊一下。现在这个信息社会&#xff0c;讲究多角色协同工作。人工智能时代&#xff0c;多智能体协同工作也是大势所趋&#xff0c;虽然现在框架或多或少还存在瑕疵。 但所有新技术都是在发展中逐步迭代…

vue引入aos.js实现滚动动画

aos.js官方网站&#xff1a;http://michalsnik.github.io/aos/ aos.js介绍 AOS (Animate on Scroll) 是一个轻量级的JavaScript库&#xff0c;用于实现当页面元素随着用户滚动进入可视区域时触发动画效果。它不需要依赖 jQuery&#xff0c;可以很容易地与各种Web开发框架&#…

掌握rpc、grpc并探究内在本质

文章目录 rpc是什么&#xff1f;又如何实现服务通信&#xff1f;理解rpcRPC的通信过程通信协议的选择小结RPC VS Restful net_rpc实践案例net/rpc包介绍创建服务端创建client 看看net_rpc的通信调度实现的内部原理明确目标基于自己实现的角度分析我会怎么做代码分析 grpc介绍与…

QT修改界面图标及exe程序图标

目录 步骤1. 添加图标文件2. 添加保存图标变量3. 窗口启动初始化图标文件4. 构建后即可完成图标的更改 步骤 1. 添加图标文件 如下&#xff0c;添加一个名为 favicon.ico 的文件到.pro 工程文件所在的目录中。 2. 添加保存图标变量 RC_ICONS是一个变量&#xff0c;它被用于存储…

MaxKB-无需代码,30分钟创建基于大语言模型的本地知识库问答系统

简介 MaxKB 是一个基于大语言模型 (LLM) 的智能知识库问答系统。它能够帮助企业高效地管理知识&#xff0c;并提供智能问答功能。想象一下&#xff0c;你有一个虚拟助手&#xff0c;可以回答各种关于公司内部知识的问题&#xff0c;无论是政策、流程&#xff0c;还是技术文档&a…