精华内容
下载资源
问答
  • 本程序在C8051F020单片机上通过串口进行机通信,主机和从机之间可以安全可靠地传输数据,程序已在020单片机上调试成功。
  • 51单片机串口多机通信的原理与编程实现

    千次阅读 多人点赞 2020-06-11 12:15:44
    51单片机串口多机通信 需要用的的寄存器 (了解的可直接跳到下一节) TMOD 定时器/计数器模式控制寄存器 TCON 定时器控制寄存器 SCON 串口控制寄存器 PCON 电源控制位寄存器 IE 中断中断使能寄存器 补充说明,...
     
    

    51单片机串口多机通信

    实验所用硬件: STC89C52、11.0592MHz 晶振,9600bps

    需要用的的寄存器 (了解的可直接跳到下一节)

    • TMOD 定时器/计数器模式控制寄存
    • TCON 定时器控制寄存器
    • SCON 串口控制寄存器
    • PCON 电源控制位寄存器
    • IE(interrupt enable) 中断中断使能寄存器

    TMOD 定时器/计数器模式控制寄存器

    定时器/计数器模式控制寄存器TMOD是一个逐位定义的8位寄存器,但只能使用字节寻址,其字节地址为89H。

    D7D6D5D4D3D2D1D0
    GATEC/TM1M0GATEC/TM1M0
    • D0~D3 :为T0定时/计数器的设置,D4~D7为T1定时/计数器的设置 。
    • GATE :为门控位,GATE=0时,只要在编写程序时,使TCON中的TRO或TR1为1,就可以启动定时器/计数器工作。
      • GATE=1时,不仅要在编写程序时,使TCON中的TR0或TR1为1,且需要外部引脚也为高电平,才能工作。
    • C/T :定时/计数模式切换,C/T=0时为定时模式,C/T=1时为计数模式。
    • M1,M0 :用来选择定时计/计数器的工作方式,一般使用都是采用16位的计时计数器
    M1M0工作模式说明
    00013位计时计数器(8192)
    01116位计时计数器(65535)
    1028位计时计数器,可自动重新载入计数值(256)
    113当成两组独立的8位计时器 (256,T0 和T1 不能同时使用

    TCON 定时器控制寄存器

    定时器控制寄存器,作用是控制定时器的启、停,标志定时器溢出和中断情况。

    D7D6D5D4D3D2D1D0
    TF1TR1TF0TR0IE1IT1IE0IT0
    • TF1 :TF1=1表示T1有中断产生。(Timer Flag,定时器标志位)
    • TR1 :TR1=1表示T1开始运行。(单片机中T0引脚,需要高低电平的驱动)
    • TF0 :TF0=1表示T0有中断产生。
    • TR0 :TR0=1表示T0开始运行。(单片机中T1引脚,需要高低电平的驱动)
    • IE1 :IE1=1表示INT1有中断产生。
    • IT1 :IT1=1表示INT1为下降沿触发,IT1=0表示INT1为低电平触发。
    • IE0 :IE0=1表示INT0有中断产生。
    • IT0 :IT0=1表示INT0为下降沿(负跳变)触发,IT0=0表示INT0为低电平触发。
    1. 外部中断:

      • IE0/IE1:外部中断请求标志位 当INT0(INT1)引脚出现有效的请求信号,此位由单片机自动置1,CPU开始响应,处理中断,而当入中断程序后由单片机自动置0.

      • IT0/IT1:外部中断触发方式控制位 //选择有效信号 IT0/IT1=1:脉冲触发方式,下降沿有效。IT0/IT1=0:电平触发方式,低电平有效。

    SCON 串口控制寄存器

    D7D6D5D4D3D2D1D0
    SM0SM1SM2RENTB8RB8T1RI
    • SM0、SM1工作方式控制位
    • SM2:多机通信控制位,1-允许、0-不允许
    • REN:串行接收允许位。1-允许、0-不允许
    • TB8:发送数据第九位
    • RB8:接收数据第九位
    • TI:发送中断标志位
    • RI:接收中断标志位
    1. SM0和SM1

      串行口工作方式选择位 ,两个选择位对应四种通信方式,如下表,其中fosc是振荡频率

      SM0SM1工作方式功能波特率
      00方式08位同步移位寄存器fosc/12
      01方式110位UART可变
      10方式211位UARTfosc/32 或 fosc/64
      11方式311位UART可变
      1. 工作方式2

        (SM0 SM1 :1 0):串行口为11位异步通信接口。

        • 发送或接收一帧信息包括1位起始位“0”、8位数据位、1位可编程位、1位停止位“1”。
        • 发送数据:
          • 发送前,先根据通信协议由软件设置TB8为“奇偶校验位”或“数据标识位”,然后将要发送的数据写入SBUF,即能启动发送器。
          • 发送过程是由执行任何一条以SBUF为目的寄存器的指令而启动的,把8位数据装入SBUF,同时还把TB8装到发送移位寄存器的第9位上,然后从TXD(P3.1)端口输出一帧数据。
        • 接收数据:
          • 先置REN=1,使串行口为允许接收状态,同时还要将RI清“0”。
          • 然后再根据SM2的状态和所接收到的RB8的状态决定此串行口在信息到来后是否置R1=1,并申请中断,通知CPU接收数据。
      2. 工作方式3

        (SM0 SM1 :1 1):为波特率可变的11位异步通信方式,除了波特率有所区别之外,其余方式都与方式2相同。

    2. SM2

      多机通信控制位,主要用于方式2和方式3。

      • 若SM2 = 1,允许多机通信。

        • 多机通信协议规定,第9位数据(D8)为1,说明本帧数据为地址帧;
        • 若第9位数据为0,则本帧数据为数据帧。
      • 若SM2 = 0, 即不属于多机通信情况,则接收完一帧数据后,不管第9位数据是0还是1,都置RI = 1,接收到的数据装入SBUF中。

        • 在方式0时SM2必须置0。在方式1时,若SM2 = 1,则只有接收到有效停止位时,RI才置1,以便接收下一帧数据。
      1. 多机通信时

        • 当一个89c51(主机)与多个89c51(从机)通信时,所有从机的SM2位都置1,主机首先发送的一帧数据为地址,即某从机号,其中第9位为1。
        • 所有的从机接收数据后,将其中第9位数据装入RB8中。各个从机根据接收到的第9位数据(RB8中)的值来决定从机是否再接收主机的信息。
          • 若(RB8)= 0,说明是数据帧,则使接收中断标志位RI = 0,信息丢失,
          • 若RB8 = 1,说明是地址帧,数据装入SBUF并置RI = 1,中断所有从机,被寻址的目标从机清除SM2,以接收主机发来的一帧数据,其它从机仍然保持SM2 = 1。
    3. REN

      允许接收控制位,由软件置1或清0

      • REN = 1时,允许接收,相当于串行接收的开关
      • REN = 0时,禁止接收

      在串行通信接收控制过程中,如果满足RI = 0和REN = 1的条件,就允许接收。

    4. TB8, RB8

      • TB8

      发送数据的第9位(D8)装入TB8中。在方式2或方式3中,根据发送数据的需求由软件置位或复位。在许多通信协议中可用作奇偶校验位,也可以在多机通信中作为发送地址帧或者数据帧的标志位。

      • RB8

      接收数据的第9位,原理同TB8

    5. TI, RI

      • TI

      发送中断标志位,在一帧数据发送完时被置位。在串行发送到停止位的开始时由硬件置位,可用软件查询。它同时也申请中断。TI置位意味着向CPU提供“发送缓冲器SBUF已空”的信息,CPU可以准备发送下一帧数据。串行口发送中断被响应后,TI不会自动清0,必须软件清0.

      • RI

      接收中断标志,在接收到一帧数据后由硬件置位。当RI = 1时,申请中断,表示一帧数据接收结束,并已装入接收SBUF中,要求CPU取走数据,CPU响应中断,取走数据。RI位也必须由软件来清0,。

      • 注意

      串行发送中断标志TI和接收中断标志RI是同一个中断源,CPU事先不知道是发送中断TI还是接收中断RI产生的中断请求,所以,在全双工通信时,必须由软件来判别。复位时SCON所有位都清0.

    PCON 电源控制位寄存器

    电源控制位寄存器PCON中只有SMOD位与串口工作有关,如下图所示

    D7D6D5D4D3D2D1D0
    SMODXXXGF1GF0PDIDL

    SMOD:波特率倍增位。在方式1、2、3中,当SMOD = 1时,波特率提高一倍。

    IE 中断中断使能寄存器

    D7D6D5D4D3D2D1D0
    符号EAET2ESET1EX1ET0EX0
    复位值0000000

    想要中断发生时,CPU能处理中断函数,就必须使能相应的中断,通过IE配置即可。为1时使能,为0不使能

    • EA (enable ALL ):中断使能总开关位。1开启,0关闭。若EA不开启,即便5个中断都开启,CPU也不会处理中断。
    • EX0:外部中断INT0使能
    • EX1:外部中断INT1使能
    • ET0:定时器0中断使能
    • ET1:定时器1中断使能
    • ET2:定时器2中断使能
    • ES: 串口中断使能

    补充说明,TH1 预置值计算,中断源

    TH1 预置值计算公式

    注意 SMOD=0时,K=1;SMOD=1时,K=2;

    T H 1 = 256 − K ∗ F o s c 384 ∗ B a u d R a t e TH1 = 256 - \frac{K*F_osc}{384*BaudRate} TH1=256384BaudRateKFosc

    举例,波特率为9600 bps,所用晶振为 11.0592 MHz,SMOD = 1,TH1 预置值应为 0xFA.
    T H 1 = 256 − 2 ∗ 11059200 384 ∗ 9600 = 250 ( 0 x F A ) TH1 = 256 - \frac{2*11059200}{384*9600} = 250 (0xFA) TH1=2563849600211059200=250(0xFA)

    中断源

    中断源引发原因默认优先级中断序号(C语言)入口地址(汇编用)中断标号*8 +3得到
    INT0引脚输入低电平或者下降沿引发。此时标志位IE0=1最高0 0003
    T0T0对应的TF0溢出时发生。此时标志位TF0 = 11000B
    INT1引脚输入低电平或者下降沿引发。此时标志位IE1=120013
    T1T1对应的TF1溢出时发生。此时标志位TF1 = 13001B
    串口中断串口完成一帧字符的接受/发送引发。40023
    T2(如果有的话)T2对应的TF2溢出时发生。标志位TF2 = 1最低5002B

    若配置好了相应的中断,当中断发生时,单片机就会自动去调用中断函数,来处理中断。

    在执行中断函数前,除了串行口中断的标志位需要用代码指令软件归零外,其他的中断标志位都是硬件自动归零。

    • 注意

    中断函数可以写在分文件里,在.c源文件中的写法。

    void functionNmae() interrupt 中断序号 {
    
    }
    

    在头文件中的写法

    void functionName(); //直接省略中断序号即可
    

    主从机工作模式和串口初始化代码

    51单片机的主从模式,首先要设定工作方式3:(主从模式+波特率可变)。SCON串口功能寄存器:SM0=1;SM1=1(工作方式3)

    (Master host)主机串口初始化,串口发送,中断接收代码

    • 串口发送

    主机的配置发送“地址”时,把TB8设定为1,发送数据时TB8设定为0,(类似于:主机 TB8=1发送的是地址,TB8=0发送的是数据)

    void TXdata(uchar addr,uchar *str){
      TB8 = 1; //发送地址
      SBUF = addr; //把地址发送出去
      while(!TI); //判断是否发送成功(发送成功后TI会置1,需手动清0)
      TI = 0;
      TB8 = 0; //发送数据
      while(*str != '\0') //发送数组
        {
          SBUF = (*str);
          while(!TI);
          TI = 0;
          str++;
        }
    }
    

    假设主机将发送“1234”给地址为1的从机:调用函数:TXdata(1,“1234$”);

    • 主机接收
    void chuan() interrupt 4 //串口中断服务函数
    {
      ES = 0; //关闭串口中断
      if(RI) //再次判断,是否接收到数据(接收到数据后,RI会置1,需手动清0)
        {
          RXData = SBUF;
          if(RXstart) //判断是否接收到过本地址
    	{
    	  if(RXData != '$') //判断是否接收到 数据结束 标志 $
    	    {
    	      temp[j] = RXData; //没有接收到结束标志,正常保存数据至数组
    	      j++;
    	    }
    	  else //接收到 结束标志 $
    	    {
    	      RXstart= 0; //本次接收结束
    	      j = 0;
    	    }
    	}
          if(RXData == 1) //判断是否呼叫本机,地址范围:000 – 254(00 - FE)
    	{
    	  RXstart = '0'; //开始接收数据
    	}
        }
      RI = 0; //清除接收标志位
      ES = 1; //重新开启串口中断
    }
    
    • 主机串口初始化
    void UART_init()
    {
      TMOD = 0x20; //定时器1,工作方式2:8位、自动重装
      TH1 = 0xfd; //fd: 9600bps @ 11.0592M
      TL1 = 0xfd; //e8: 1200bps @ 11.0592M
      //f4: 2400bps @ 11.0592M
      REN = 1; //允许串口接收
      SM0 = 1;
      SM1 = 1; //SM0和SM1:串口工作模式3,主从模式 + 波特率可变
      //SM2 = 1; //只接收地址(从机如此配置,主机不需要)
      ES = 1; //开串口中断
      TR1 = 1; //启动定时器1
      EA = 1; //中断 总开关
    }
    

    (Slave host )从机串口初始化,串口发送,中断接收代码

    • 注意

    从机发送给主机的数据帧要以字符 ‘0’ 开头,标识这个要主机接收的。

    • 串口发送
    void TXdata(uchar *str){
      while(*str != '\0') //发送数组
        {
          SBUF = (*str);
          while(!TI);
          TI = 0;
          str++;
        }
    }
    
    • 从机接收
      • 从机接收时,首先串口初始化时,使SM2=1(接收地址模式,即只能接收到TB8=1的数据,才触发中断),主机发送TB=0的数据,被认为是总线上的主机发送给别机的通信数据,本机丢弃,不产生中断。
      • 接收的地址与本机地址相符后,使SM2=0(接收数据模式,接收数据正常触发中断)。(类似于:从机 SM2=1只接收地址,SM2=0只接收数据)
    void chuan() interrupt 4 //串口中断服务函数
    {
      ES = 0; //关闭串口中断
      if(RI) //再次判断,是否接收到数据(接收到数据后,RI会置1,需手动清0)
        {
          RXData = SBUF;
          if(RXstart) //判断是否接收到过本地址
    	{
    	  if(RXData != '$') //判断是否接收到 数据结束 标志 $
    	    {
    	      temp[j] = RXData; //没有接收到结束标志,正常保存数据至数组
    	      j++;
    	    }
    	  else //接收到 结束标志 $
    	    {
    	      RXstart= 0; //本次接收结束
    	      SM2 = 1; //重新 配置为:只接收地址 模式,下次发送TB8=1才中断
    	      j = 0;
    	    }
    	}
          if(RXData == 1) //判断是否呼叫本机,地址范围:000 – 254(00 - FE)
    	{
    	  RXstart = 1; //开始接收数据
    	  SM2 = 0; //配置为:接收数据 模式
    	}
        }
      RI = 0; //清除接收标志位
      ES = 1; //重新开启串口中断
    }
    
    • 从机串口初始化
    void UART_init()
    {
      TMOD = 0x20; //定时器1,工作方式2:8位、自动重装
      TH1 = 0xfd; //fd: 9600bps @ 11.0592M
      TL1 = 0xfd; //e8: 1200bps @ 11.0592M
      //f4: 2400bps @ 11.0592M
      REN = 1; //允许串口接收
      SM0 = 1;
      SM1 = 1; //SM0和SM1:串口工作模式3,主从模式 + 波特率可变
      SM2 = 1; //只接收地址(从机如此配置,主机不需要)
      ES = 1; //开串口中断
      TR1 = 1; //启动定时器1
      EA = 1; //中断 总开关
    }
    

    主从机的地址标识

    地址用一个8位字符表示可以,大小可以设置从 000-255。主机默认是0。

    主从机的数据帧约束

    无论是主机还是从机发送的数据帧都要以 ‘address’ 开头, ‘$’ 结尾。

    • { ‘address’, ‘…’, ‘$’ }

    接线图和注意事项

    主从机串口连接方式
    主机的RX 与所有从机的TX 连接。主机的TX 与所有从机的RX 连接。

    1. 从机和从机之间通信,只能通过主机中转。
    2. 各从机的TXD输出不能设置为推挽输出,要设置为开漏输出。
    3. 通信总线不能过长,最好不超过2米。

    后续可以进行的功能扩展(设想)

    • 添加控制指令

    由 主机 -> 从机. 可以设置数据帧 {‘slave 1 addresss’, ‘GET’, ‘$’}. 表示主机想要获取从机从传感器中获得的数据。

    • 主机中转传输

    如设置一个指令字符帧开头 {‘master address’, ‘TRANSFER’, ‘slave 2 address’, “Data”, ‘$’}. 表示数据由主机代为转发到从机 “slave 2 address”.

    完整代码链接

    • 主机

    serialmaster.h
    serialmaster.c

    • 从机

    serial_slave.h
    serial_slave.c

    展开全文
  • 单片机串口接收字节

    千次阅读 2019-05-02 22:28:13
    感觉串口多字节接收部分的逻辑相对于配置寄存器跟串口回复来说,是有点难度的——... 串口接收程序是基于串口中断的,单片机串口每次接收到一字节数据产生一次中断,然后再读取某个寄存器就可以得到串口接收的数...

    转自:http://bbs.ednchina.com/BLOG_ARTICLE_3007162.HTM

      感觉串口多字节接收部分的逻辑相对于配置寄存器跟串口回复来说,是有点难度的——寄存器配置基本上都是死的,串口回复多字节跟回复一字节只是多了一个循环。

     

            串口接收程序是基于串口中断的,单片机的串口每次接收到一字节数据产生一次中断,然后再读取某个寄存器就可以得到串口接收的数据了。然而在实际应用当中,基本上不会有单字节接收的情况。一般都是基于一定串口通信协议的多字节通信。在422或者485通信中,还可能是一个主机(一般是计算机)带多个从机(相应的有单片机的板卡)。这就要求我们的单片机能够在连续接收到的串口数据序列中识别出符合自己板卡对应的通信协议,来进行控制操作,不符合则不进行任何操作。简而言之就是,单片机要在一串数据中找到符合一定规律的几个字节的数据。

     

            先来说下怎样定串口协议吧。这个协议指的不是串口底层的协议,而是前面提到的数据帧协议。一般都是有帧头(2~3个字节吧),数据(长度根据需要),结束位(1位,有时候设计成校验字节,最简单的校验也就是前面所有数据求和)。

            比如0xaa 0x55 +(数据部分省略)+校验和(除了aa 55 之外数据的和),如果要是多板卡的话有时候还要在帧头后面加一个板选字节(相当于3字节帧头了)。

     

           第一次写串口接收程序的时候,我首先想到的就是定义一个全局变量(实际上最好是定义局部静态变量),初始值设置为0,然后每进一次中断+1,然后加到串口通信协议的长度的时候再清零。然后判断帧头、校验。写完了之后我自己都觉得不对,一旦数据错开了一位,后面就永远都接收不到数了。无奈看了一下前辈们的代码,跟我的思路差不多,只不过那个计数值跟接收到的数据时同时判断的,而且每次中断都要判断,一旦不对计数的那个变量就清零。

     

           废话少说,直接上一段代码让大家看看就明白了。(通信协议姑且按照简单的aa 55 一个字节数据 一个字节校验,代码是基于51单片机的)。接收成功则在中断程序中把串口接收成功标志位置1。

     

    下面是全局变量定义

    unsigned char receive[4]={0,0,0,0};//接收缓存

    bit uart_flag;//串口接收成功标志

     

    然后串口中断部分

    void ser()interrupt 4

    {

    static unsigned char count;//串口接收计数的变量

      RI=0;//手动清某个寄存器,大家都懂的

      receive[count]=SBUF;

      if(count==0&&receive[count]==0xaa)//同时判断count跟收到的数据

      {

           count=1;

      }

      else if(count==1&&receive[count]==0x55)

      {

         count=2;

      }

      else if(count==2)

      {

           count++;

      }

      else if(count==3&&receive[count]== receive [2])//判断校验和,数据多的话是求//和,或者其他的校验方法,也可能是固定的帧尾

      {

        count=0;

         uart_flag =1;//串口接收成功标志,为1时在主程序中回复,然后清零

       ES=0;      //关中断,回复完了再ES=1;

      }

      else

      {

         count=0;//判断不满足条件就将计数值清零

      }

    }

     

            第一次做的串口大概就按照这个方法写完了(我后来看过其他的代码,有人用switch语句写的,逻辑跟这个也差不多,不过我还是感觉用if else来写清晰一些),

            不过在测试的时候发现了bug,如果数据帧发送一半,然后突然停止,再来重新发,就会丢失一帧的数据。比如先接受到aa 55,然后断了,再进来aa 55 01 01,就不受控制了。后来我也想到一个bug,如果在多设备通信中,属于其他设备的的帧数据最后一位是aa(或者最后两位为aa 55 ,或者最后3位为aa 55 板选),下一次通信的数据就接收不到了。

     

            当时对于数据突然中断的bug,没有想到很好的解决办法,不过这种情况几率极小,所以一直用这个方法写也没有问题。多设备通信最后一位恰好是aa的几率也很小,出问题的可能也很小。当时项目里面的控制数据跟校验恰好不可能出现aa,于是我把if(count==0&&receive[count]==0xaa)改成了if(receive[count]==0xaa)其他都没变,解决了,没有bug了。

     

            后来我又写了几次单片机程序,才想到了一些解决问题的方法——不过改天再接着写吧,太累了,明天还要上班呢。

     

            在后来的项目中,真的遇到了数据位跟校验位都可能出现aa的情况。我考虑到每次数据都是连续发送的(至少我们用labwindows做的上位机程序是这样的),成功接收到了一帧数据是要有一定时间回复的,也就是说如果接收到一半,但是很长时间没接收到数据,把计数值count清零就ok啦。涉及时间的问题自然要用定时器来实现啦。

    这次的通信协议如下,串口波特率19200,2个帧头aa 55 ,一个板选,6字节数据,一个校验字节(除帧头外其他数据的和)。

     

    全局变量定义

    unsigned char boardAddr;//板选地址,通过检测几个io引脚,具体怎么得到的就不写了,很简单的

    unsigned char g_DatRev [10]={0};//接收缓存

    bit retFlag=0;//为1代表串口接收到了一帧数据

     

     

    串口初始化函数,晶振22.1184

     

    void init_uart()

    {

           SCON = 0x50;                 //串口方式1允许接收

           TMOD = 0x21;                //定时器1,方式2,8位自动重载,同时配置定时器0,工作方式1

           PCON = 0x80;                // 波特率加倍

           TH1 = 0xfa;

           TL1 = 0xfa;               //写入串口定时器初值

           TH0=(65536-2000)/256;    //写入定时器0初值,串口传输一个字节时间为(1/19200)*10,计算得0.52ms

           TL0=(65536-2000)%256;   //定时器0定时大约1ms多

        EA=1;

        ET0=1;                  //波特率:19200    22.1184M  初值:250(0xfa)

           IE |= 0x90;           

        TR1 = 1;                   

    }

     

    串口中断函数

     

    void UART_INT(void) interrupt 4

           static unsigned char count;//串口接收计数的变量

     

                  RI = 0;

                  g_DatRev[count] = SBUF;

                  if(g_DatRev[count]==0xaa&&count==0)             //帧头

               {

                       count=1;                                                 

                }

                   else if(count==1&&g_DatRev[count]==0x55) 

                {  

                             count=2;          

                }

     

                    else if (count==2&&g_DatRev[2] == boardAddr)

                   { 

                      CK = g_DatRev[count];

                       count=3;

                     

                   }

         

                   else if(count>=3&&count<9)

                  {     

                    

                         CK += g_DatRev[count];

                        count ++;

                }

                 

               else if(count == 9&&CK==g_DatRev[9])

                         {     

                             ES = 0; 

                            retFlag = 1;

                             count=0;            

                         }            

                  else

                   {

                        count=0;

                   } 

                 resettimer();

     

    }

     

    //判断count不为0的话就启动定时器

    void resettimer()

    {

           TR0=0;

           TH0=(65536-2000)/256;

           TL0=(65536-2000)%256;

           if(count!=0)

           {

                  TR0=1;

           }

    }

     

    定时器中断函数

    void T0_time()interrupt 1

    {     

        TR0=0;

           TH0=(65536-2000)/256;

           TL0=(65536-2000)%256;

           count=0;

     

    }

     

            这种方法的确是本人自己想出来的,别人可能也这样做过,但我这个绝对不是抄袭或者模仿来的。这样写的确可以避免前面提到过的bug,不过代价是多用了一个定时器的资源,而且中断函数里的内容更多了,占用了更多的时间。

     

            要是能把第一种方法改进一下就好了,主要是那个校验不能为aa的那个bug,因为毕竟传输到一半突然断了的可能性是非常小的。后来我想第一个判断if(count==0&&receive[count]==0xaa)好像有点太严格了,考虑到第二字节的帧头,跟板选地址不可能为aa,于是把这个改写为if(count>=0&&count<=2&& receive[count]==0xaa),这样就把bug出现的几率降到了非常小,也只是在前一帧结尾数据恰好为 aa 55 板选 的时候才出现,几率是多少大家自己算一下吧,呵呵。这样我自己觉得,昨天写的那种方法改进到这个程度,应该算可以啦,反正我是很满意了。

     

            实际上我还想过其他的方法,比如缓存的数组采用移位寄存的方式。拿前面的4个字节的协议为例。

     

    void ser()interrupt 4

    {

     unsigned char i;

      RI=0;

     

      for(i=0;i<3;i++)

      {

         receive[i]=receive[i+1];

      }

      receive[3]=SBUF;

      if(reveive[0]==0xaa&&receive[1]==0x55&&receive[2]==receive[3])

      {

         ret_flag=1;

           ES = 0;  

      }

     

    }

            这段代码看上去可是简单明了,这样判断可是不错啊,同时判断帧头跟校验不会产生前面提到的bug。说实话当时我刚想出这种方法并写出来的时候,马上就被我给否了。那个for循环可真是很占时间的啊,延时函数都是这样写的。每次都循环一下,这延时太长,通信速度太快的话就不能接收到下一字节数据了。最要命的是这个时间的长度是随着通信协议帧的字节数增加而增加的,如果一次要接收几十个字节,肯定就玩完了。这种方法我一次都没用过。

     

            不过我居然又想出来了这种方法的改良措施,是前两天刚想出来的,呵呵,还没有实践过呢。

    下面代码的协议就按第二段程序(定时器清零的那个协议,一共10字节)

     

    全局变量

     

    bit ret_flag;

    unsigned char receive[256]={0};

    unsigned char boardaddress;

     

    中断函数

     

    void ser()interrupt 4

    {

     

     

      static unsigned char i=0;

      static unsigned char total=0;

      RI=0;

      receive[i]=SBUF;

      total=total-receive[i-7]+receive[i-1];

     

      if(receive[i-9]==0xaa&&receive[i-8]==0x55

      &&receive[i-7]==boardaddress&&receive[i]==total

      )

      {

         ret_flag=1;

           ES = 0;  

      }

      i++;

     

    }

            之所以要定义256个长度的数组,就是为了能够让数组“首尾相接”。因为0 -1 = 255 , 255+1 = 0。而且我在计算校验的时候也改进了算法,不会因为数据长度的增加而增加计算校验值的时间。这种方法也是我不久前才想出来的,所以还没有经过实际的验证。上面的代码可能会有逻辑上的错误,如果真有错误,有网友看出来的话,请在下面留言告诉我。这个方法也是我原创的哦,别人也肯能会想到,不过我这个绝对不是抄袭别人的。

     

            上面的代码最大的缺点就是变量定义的太多了,太占ram资源了,编译的时候可能会出现错误,毕竟51单片机才128字节的ram(有的资源也很丰富的,比如c8051系列的),这一下子就是256字节的变量。不过对于资源多一些的单片机,这样写还是可以的。要是能有4bit在一起的数据类型就好了,呵呵,verilog代码里面是可以的,C语言里貌似不行啊。

     

            要想能在例如51单片机上运行,只能按照下面的折中方式了,也就是把i相关的量都与一个0x0f

     

    全局变量

     

    bit ret_flag;

    unsigned char receive[16]={0};// 可以考虑在定义时加上idata,毕竟还可能是32

    //或者64长度的数组呢unsigned char idata receive[16]={0};

     

    unsigned char boardaddress;

     

    中断函数

     

    void ser()interrupt 4

    {

     

     

      static unsigned char i=0;

      static unsigned char total=0;

      RI=0;

      receive[i&0x0f]=SBUF;

      total=total-receive[(i-7)&0x0f]+receive[(i-1)&0x0f];

     

      if(receive[(i-9)&0x0f]==0xaa&&receive[(i-8)&0x0f]==0x55

      &&receive[(i-7)&0x0f]==boardaddress&&receive[i&0x0f]==total

      )

      {

         ret_flag=1;

           ES = 0;  

      }

      i++;

     

    }

     

        这样就可以了。等我有机会试一下吧,呵呵。我写了这么多,想必大家都能搞定串口接收了吧。

    PS:字体有点小,大家凑合看吧,编辑字体的话就显示字数超了,真的不是我犯懒哦。

    展开全文
  • 51单片机串口组网

    2020-11-23 20:33:56
    /***单片机串口组网 * 主机程序 * MASTER * ***/ #include <reg52.h> #define uchar unsigned char #define uint unsigned int #define SLAVE 0x02 #define BN 16 uchar rbuf[16]; uchar tbuf[16]={...

    模仿I2C协议利用串口组网

    先祭上代码
    书上郭天祥的代码有点问题,没法进行通信,但是程序流程图是对的。
    我百度过关于51单片机串口多机组网资料,除了郭天祥的代码目前没看到有其他的资料。
    我在郭天祥的基础上再改进一下

    单片机连接方式在这里插入图片描述

    主机通信流程图

    流程图

    主机代码

    /***单片机串口组网
    *		主机程序
    *		MASTER
    *
    ***/
    
    #include <reg52.h>
    #define uchar unsigned char
    #define uint unsigned int
    
    #define SLAVE 0x02
    #define BN 16
    
    uchar rbuf[16];
    uchar tbuf[16]={"master transmit"};
    
    void err(void)//发送复位信号
    {
    	SBUF = 0xff;
    	while(TI!=1);
    	TI = 0;
    }
    
    
    
    uchar master(uchar addr,uchar command)
    {
    	uchar aa,i,p;
    	while(1)
    	{
    		SBUF = SLAVE;//呼叫从机地址
    		while(TI!=1);
    			TI = 0;
    		while(RI!=1);//等待从机响应
    			RI = 0;		
    		
    		if(SBUF!=addr)//如果地址错误,发送复位信号
    		{
    			err();
    		}
    		
    		else	//发送完地址发送数据
    		{
    			TB8 = 0;//清除地址标志,变为数字标志	
    			
    			SBUF = command;//发命令
    			while(TI!=1);
    				TI = 0;
    			while(RI!=1);//等待从机响应
    				RI = 0;		
    			//读取返回的数据
    			aa = SBUF;		//接收从机返回的状态
    			if((aa&0x08)==0x08)//若命令未接收,发送复位信号
    			{
    				TB8 = 1;
    				err();
    			}
    			
    			else
    			{
    				if(command==0x01)//是发送命令
    				{
    					//************
    					if((aa&0x01)==0x01)//从机准备好接收
    					{
    						do
    						{
    							p = 0;
    							//此处可优化
    							for(i=0;i<BN;i++)
    							{
    								SBUF = tbuf[i];
    								p += tbuf[i];	//p为校验和
    								while(TI!=1);
    								TI = 0;
    							}
    							
    							//发送校验和
    							SBUF = p;
    							while(TI!=1);
    								TI = 0;
    							while(RI!=1);
    								RI = 0;
    						}
    						while(SBUF!=0);//接收不对,重新发送
    						TB8 = 1;//切换为地址标志
    						return(0);
    					}
    					//************
    					else
    					{
    						if((aa&0x02)==0x02)//接收命令,从机准备好发送
    						{
    							while(1)
    							{
    								p = 0;//清校验
    								//接收数据,并计算校验p
    								for(i=0;i<BN;i++)
    								{
    									while(RI!=1);
    										RI = 0;
    									rbuf[i] = SBUF;
    									p += rbuf[i];
    								}
    								//接收p
    								while(RI!=1);
    									RI=0;
    								if(SBUF==p)		//校验和相同发送0x00,向从机确认
    								{
    									SBUF=0x00;
    									while(TI!=1);
    									TI = 0;
    									break;
    								}
    								else
    								{
    									SBUF = 0xff;//校验和不同发送0xff,
    									while(TI!=1);
    									TI = 0;
    								}
    							}
    							TB8 = 1;//切换回地址标志
    							return(0);
    						}
    					}
    				}
    				
    			}
    				
    		}
    	}
    }
    
    void Init_Timer()
    {
    	TMOD = 0x20;
    	TH1 = 0xfd;
    	TL1 = 0xfd;
    	PCON = 0x00;
    	TR1 = 1;
    	SCON = 0x50;
    }
    
    void main()
    {
    	Init_Timer();
    	master(SLAVE,0x01);
    	master(SLAVE,0x02);
    	while(1);
    }
    

    从机通信流程图

    从机流程图

    从机代码

    /***单片机串口组网
    *		从机程序
    *		SLAVE
    *
    ***/
    
    #include <reg52.h>
    #define uchar unsigned char
    #define uint unsigned int
    
    #define SLAVE 0x02
    #define BN 16
    
    uchar rebuf[16];
    uchar trbuf[16];
    
    bit tready;
    bit rready;
    
    
    //发送数据块
    void str(void)
    {
    	uchar p,i;
    	tready = 0;
    	do
    	{
    		p = 0;
    		for(i=0;i<BN;i++)
    		{
    			SBUF = trbuf[i];
    			p += trbuf[i];
    			while(TI!=1);
    				TI = 0;
    		}
    		SBUF = p;	//发送校验和
    		while(TI!=1);
    			TI = 0;
    		while(RI!=1);
    			RI = 0;
    	}while(SBUF!=0);//主机接收错误,重新发送
    	SM2 = 1;
    	ES = 1;	//发送完,恢复监听
    }
    
    void sre(void)//接收数据块
    {
    	uchar p,i;
    	rready = 0;
    	while(1)
    	{
    		p = 0;
    		for(i=0;i<BN;i++)
    		{
    			while(RI!=1);
    				RI = 0;
    			rebuf[i]=SBUF;
    			p += rebuf[i];
    		}
    		while(RI!=1);
    			RI = 0;	//等待接收完成
    		if(SBUF==p)
    		{
    			SBUF = 0x00;//校验和相同发送0x00
    			break;
    		}	
    		else
    		{
    			SBUF = 0xff;
    			while(TI==0);//校验和不同,重新接收
    			TI = 0;
    		}	
    	}
    	SM2 = 1;
    	ES = 1;
    }
    
    void Init_Timer()
    {
    	TMOD = 0x20;
    	TH1 = 0xfd;
    	TL1 = 0xfd;
    	PCON = 0x00;
    	TR1 = 1;
    	SCON = 0x50;
    	ES = 1;
    	EA = 1;
    }
    
    void main()
    {
    	Init_Timer();
    	while(1)
    	{
    		tready = 1;
    		rready = 1;
    	}
    }
    
    void ssio(void) interrupt 4
    {
    	uchar a;
    	RI = 0;
    	ES = 0; 	//关闭串口中断
    	if(SBUF!=SLAVE)	//非本机地址
    	{
    		ES = 1;		//继续监听
    		goto reti;
    	}
    	SM2 = 0;	//取消监听状态
    	
    	SBUF = SLAVE;//发送本机地址,让主机识别
    	while(TI!=1);
    		TI = 0;
    	while(RI!=1);
    		RI = 0;
    	
    	if(RB8==1)	//????
    	{
    		SM2 = 1;
    		ES = 1;
    		goto reti;
    	}			//如果接收到复位信号,恢复监听
    	
    	a = SBUF;	//传递接收的数据
    	
    	if(a==0x01)//从主机接收到数据
    	{
    		if(rready==1)
    		{
    			SBUF = 0x01;
    		}
    		else
    		{
    			SBUF = 0x00;
    		}
    		while(TI!=1);
    			TI = 0;
    		while(RI!=1);
    			RI = 0;
    		if(RB8==1)
    		{
    			SM2 = 1;
    			ES = 1;
    			goto reti;
    		}
    		sre();	//接收数据
    	}
    	else
    	{
    		if(a==0x02)//从机向主机发数据
    		{
    			if(tready==1)
    			{
    				SBUF = 0x02; //向主机表明,已准备好数据发送
    			}
    			else
    			{
    				SBUF = 0x00;
    			}
    			while(TI!=1);
    				TI = 0;
    			while(RI!=1);
    				RI = 0;
    			
    			if(RB8==1)
    			{
    				SM2 = 1;
    				ES = 1;
    				goto reti;
    			}
    			str();
    		}
    		else
    		{
    			SBUF = 0x80;//非法命令,发送状态
    			while(TI!=1);
    				TI=0;
    			SM2 = 1;
    			ES = 1;	//开启监听
    		}
    	}
    	reti:;
    }
    
    

    目前做到了地址识别,向从机发送数据,但被数据校验难到了,待更新。。。

    展开全文
  • 目前普遍采用的MCS51 和PIC 系列单片机通常只有一个(或没有)UART 异步串行通信接口,在应用系统中若需要个串行接口(例如在机通信系 ...在单片机的最小应用系统中实现与两个以上串行接口设备的机通信。
  • 51单片机串口发送

    2017-12-13 17:27:28
    51单片机串口,自己学习时做写的东西。希望能有用,串口_printf(关闭接受不断发送)
  • 两个单片机串口通信控制LED状态翻转,内含主机程序、从机程序、仿真文件以及仿真视频。
  • 用于51单片机串口通信的调试程序,主机发出的字符,可以原样传回
  • 51单片机作为主机进行串口数据发送,LCD进行发送的数据字符串显示,有注释可更改,又proteus仿真
  • 单片机串口实验c51.zip

    2019-06-06 01:29:36
    课堂作业展示,实验1是串口方式1 的8个按钮分别控制8个led,实验2 是 按钮二按下一次 led轮流点亮 ,实验3 是主机机通信。第一次分享
  • 52单片机串口通讯

    2021-03-05 21:49:13
    串口通讯 基本概念 数据帧:就是在线路上传递的一组数据,这组数据可大可小。以电子,无线为介质传输。(对应OSI网络7层模型中的数据链路层) 比特率:在1秒钟所传递的bit数量。(bit/s) 波特率:一秒内的...

    串口通讯

     

     

    基本概念

     

    数据帧:就是在线路上传递的一组数据,这组数据可大可小。以电子,无线为介质传输。(对应OSI网络7层模型中的数据链路层)

    比特率:在1秒钟所传递的bit数量。(bit/s)

    波特率:一秒内的载波数量。(Baud)

    比特率和波特率不同的是,比特率重点在传输了多少bit,波特率是指传输了多少承载信息的波。在高低电平信息传输中,两者相同。

    晶振频率:指在一秒内晶振的震动频率。(fosc)

    时钟频率:时钟频率 = 晶振频率

    状态频率:时钟频率X2

    机械频率:在1T单片机中,机械频率 = 时钟频率。在12T单片机中,机械频率 = 12乘以时钟频率

     

     

    串口与并口

     

    并口传输

    优点:传输速度快

    缺点:传输范围短

    串口传输

    优点:传输距离长

    缺点:传输速度慢

     

     

    同步通讯与异步通讯

    同步通讯:指通讯双方严格按照相同的时钟频率通讯。但是传输的帧很长。

    异步通讯

    每帧很短,而且有控制bit起到开始结束效验的作用。传输效率低。且每帧之间都用空闲,空闲时电平为1;

     

    效验位

     

    硬件与兼容

    232协议和TTL协议

    232协议为大多数主机使用的,高电平为12v,低电平为-12v。

    TTL协议多为单片机所用,高电平为5v,低电平为0v。

    为了保障两种协议兼容,使用兼容芯片如CH340芯片。使得232转TTL,TTL转232。

    由于许多主机没有串口,CH340会将串口转USB。如果使用340芯片,则要在PC上下载对应的驱动。

    TXD为发送引脚,RXD为接收引脚。

     

    串口

    目前只用管着三个帧就好了,公母区别稍微联想一下就很好记。处了这辆还有个串口,就是USB接口。现在基本都是这种方式,传统的串口很少见了。

    目前的技术都是用USB虚拟出串口来使用。这个模块有个MAX3232,负责232协议和TTL兼容的,GND用来使得两边电平相同。要不电平会乱跳。

     

     

     

    寄存器

    串口有关寄存器有:IE,TCON,SCON,PCON,AUXR,SUBF,移位寄存器

    IE寄存器和TCON寄存器是定时器里的。

    SCON寄存器

    RI :接收中断标志位,数据接收结束后,该位由硬件自动置1。由软件置0

    TI:发送中断标志位,数据发送结束后,该位由硬件自动置1,由软件置0

    RB8:

    当SM = 2时,存放接收数据的第9位,在模式2 3 中,用于存放收到的第九位数据,处理数据特征,特征与TB8对应。

    当SM2 = 0,且模式为2 3 时,此位用于接收奇偶效验位

    TB8:

    当SM2 = 0,且模式为2 3时,此位用于发送奇偶效验位

    当SM2 = 1时,存放发送数据的第9位,在模式 2 3 中,如果此位置1,则发送的是地址,如果此位置0,则发送的是数据。主要用于多处理机通讯,在此不深究。

    REN:串行接收允许位,1允许,0禁止。

    SM2:多机通讯使能位,用于处理多机通讯。与接收到的第九位数据有关,当SM2 = 1时,只有第九位数据为1时,才将数据交由SBUF并将第九位数据交由RB8,如果SM2 = 0时,无论第九位数据为什么,都将数据交由SBUF。此位用于多机通讯,不再深究。

     

    SUBF寄存器

    一个寄存器两个空间,一个收一个发。地址相同。SBUF = A,为发送,A = SBUF为读取

     

    PCON有关串口就一位,最高位SMOD。当此位置1时,波特率翻倍。

     

    移位寄存器:从SBUF获得并行数据,然后在移位脉冲下一位一位的输出或接收。

     

    IE寄存器和TCON寄存器在这里不再赘述,为定时器内容。需要注意的是当一个定时器作为串口波特率发生器时,需要静止该定时器产生中断

     

    AUXR一般无需设置,他决定了使用那个时钟频率作为波特率发生器以及详细参数。

     

     

    四种工作方式

    工作方式基础

     

    1:SM0和SM1的组合决定了串口的四种工作方式。

    方式0:串口为同步移位寄存器的输入输出方式(波特率固定为fosc/12)fosc = 晶振频率,且必须满足SM2 = 0

    输出:

    首先向SBUF向移位寄存器写入要输出的数据。RXD引脚负责发送数据。TXD负责发送移位脉冲。每发送一次脉冲,数据都从移位寄存器中移出来一位,就像C语言中的移位操作符。移位会导致丢弃和填充。在这里丢弃对应发送,也就是从移位寄存器移出的数据,填充就是填充0。发送随着移位脉冲的节拍有序发送。当发送完数据后,TI计时器会发送中断。也就是前面有过的串行中断。

    输入:

    首先REN 由1变为0,表示允许接收。然后RXD随着TXD移位脉冲输入到移位寄存器中。RI在输入过程中为0,输入结束后为1,表示输入结束。发送接收中断。并将数据交给SBUF寄存器

     

    方式1:10位异步通信(波特率可变)

    从起始位开始到停止位结束,中间1字节的数据。

    REN置1时,不断采样RXD的脉冲,当检测到RXD中有脉冲变化,则REN置0开始接收数据。使其移位到移位寄存器中去。REN为1时对RXD的检测称为位采样脉冲。采样频率为波特率的16倍。

    将数据八位不断移入移位寄存器,数据从移位寄存器右边移入,当起始位位于移位 。

     

    方式2和3 方式2的波特率为fosc/64或fosc/32,方式3可变

    输出

    先将起始位输出到TXD引脚,随后移位寄存器开始输出。第一次右移位将D0输出,并在移位寄存器第9位补停止位1,随后依次输出,并补0。当停止位移动到输出位时,检测到这一行为会控制最后一次移位并使得T1置1,引发中断。

    输出

    接收数据时,移位寄存器从右向左存入数据。当起始位移动到最左边时,检测这一行为并控制最后一次移位。此时诺RI = 0且 SM2 = 0或第九位为1,则RI置1触发中断。并将移位寄存器的数据前八位装入SBUF,第九位装入RB8中。

     

     

     

    代码1:实现数码管显示主机用串口发来的数据

    #include<reg52.h>
    sbit la = P0^0;
    sbit lb = P0^1;
    #define dataport P1//定义数码管数据端口
    unsigned int lbarr[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//段码
    unsigned int laarr[] = {0x7f};//位码
    void delay(unsigned int time)//延迟函数
    {
        while(time--){}
    }
    void dispaly(unsigned char a)//显示函数
    {
        int i = 0;
        while(i<8)
        {
        dataport = laarr[0];
        la = 1;
        la = 0;
        
        dataport = lbarr[a];
        lb = 1;
        lb = 0;
        delay(1000);
        i++;
        }
    }
    void init_serial()
    {
        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;
    }//串口初始化,在工具中得到正确的值复制并打开IE寄存器各项开关,当定时器作为波特率发生器时必须关闭对应的计时器中断
    int main()
    {
        while(1)
        {
            init_serial();
        }
    }
    void interrupt_serial() interrupt 4
    {
        unsigned char serial_data;//SBUF缓冲变量
        if(RI)//如果中断是由接收引起的
        {
            serial_data = SBUF;//读取SBUF数据
            P1 = serial_data;//显示在P1针脚上方便观察
            dispaly(serial_data);//带入显示函数
            RI = 0;//置0
        }
        if(TI)//如果是发送引起的
        {
            TI = 0;//置0
        }
    }

    代码2

    实现单片机返还从主机的通讯

    #include<reg52.h>
    sbit la = P0^0;
    sbit lb = P0^1;
    #define dataport P1//定义数码管数据端口
    unsigned int lbarr[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//段码
    unsigned int laarr[] = {0x7f};//位码
    void delay(unsigned int time)//延迟函数
    {
        while(time--){}
    }
    void dispaly(unsigned char a)//显示函数
    {
        int i = 0;
        while(i<8)
        {
        dataport = laarr[0];
        la = 1;
        la = 0;
        
        dataport = lbarr[a];
        lb = 1;
        lb = 0;
        delay(1000);
        i++;
        }
    }
    void init_serial()
    {
        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;
    }//串口初始化,在工具中得到正确的值复制并打开IE寄存器各项开关,当定时器作为波特率发生器时必须关闭对应的计时器中断
    int main()
    {
        while(1)
        {
            init_serial();
        }
    }
    void interrupt_serial() interrupt 4
    {
        unsigned char serial_data;
        if(RI)
        {
            serial_data = SBUF;
            P1 = serial_data;
            dispaly(serial_data);
            RI = 0;
        }
        if(TI)
        {
            TI = 0;
        }
    }

     

    展开全文
  • 51单片机串口通信初始化

    千次阅读 2020-10-23 20:15:35
    51单片机串口通信 实验仿真图: 实验原理: 主机的P3,1(TXD)连接到从机的P3,0(RXD),利用开关对主机的P1口赋值,将P1的数据存到主机SBUF,在通过串口传动到从机的SBUF,从机将接收到的数据从机的P1口体现。 说明...
  • 单片机串口理解

    千次阅读 2013-06-05 15:54:56
    8051单片机的通讯方式有两种: 并行通讯:数据的各位同时发送或接收。 串行通讯:数据一位一位顺序发送或接收。参看下图:  串行通讯的方式: 异步通讯:它用一个起始位表示字符的开始,用停止位表示字符...
  • 单片机串口收发数据程序(485通讯)串口通讯产品专业制造商
  • pic 单片机 串口通讯

    2010-06-07 16:43:17
    PIC单片机串口通讯,采用C语言,利用串口可以将数据传送给主机,实现通讯
  • 文章目录前言一、计算机通信简介二、串口通信简介1、简介2、同步通信和异步通信2.1 同步通信2.2 异步通信3、串行通信的传输方式4、串口通信硬件电路5、常见接口介绍三、串口相关寄存器详解1、特殊功能寄存器SCON2、...
  • 51单片机串口程序

    千次阅读 2015-08-05 00:12:48
    工作了一年,写了不少单片机串口程序。感觉串口字节接收部分的逻辑相对于配置寄存器跟串口回复来说,是有点难度的——寄存器配置基本上都是死的,串口回复字节跟回复一字节只是了一个循环。    串口接收...
  • 51单片机串口通讯UART

    万次阅读 多人点赞 2018-08-05 20:33:39
    我所采用的单片机串口通信是用一个USB接口接了一个CH340(USB转异步串口)来进行计算机与单片机双方的通信,如下图所示 下图所用硬件原理图 当开关SW1按下之后,经过USB1再连接一个USB转异步通信串口芯片...
  • 姓名:周崇杰 学号:16040120059 专业:机械设计制造及其自动化转载自:http://blog.csdn.net/a514371309/article/details/73481423...【嵌牛鼻子】:单片机,C语言,串口通信协议【嵌牛提问】:单片机是通过协议进...
  • 单片机串口通信理解(一)

    千次阅读 2019-11-08 14:59:56
    文章目录一、单片机串口通信寄存器串行口数据缓冲器SBUF串行口控制寄存器SCONSM2:机通信控制位REN:串口接受允许位TB8:发送的第9位数据RB8:接收的第9位数据TI:发送中断标志RI:接收中断标志电源控制寄存器PCON...
  • 这里我在Keil 5上使用官方固件库进行HT32F52352单片机串口通信实验。 目录(一)官方例程移植1、添加printf函数支持2、更改串口中断处理函数(二)实验例程1、printf函数2、串口初始化函数3、main函数(三)实验...
  • 蓝桥杯单片机串口通信

    千次阅读 2020-01-19 20:10:13
    内容有部分来源于网络(侵删) 相关寄存器 串行控制寄存器SCON (可位寻址) ...REN:允许/禁止串行接收控制...在方式0,当串行发送数据第8位结束时,由内部硬件自动置位,即TI=1,向主机请求中断,响应中断后必...
  • STC51单片机串口通信-单片机端程序

    万次阅读 多人点赞 2016-12-26 09:57:21
    随着单片机系统的广泛应用和计算机网络技术的普及,单片机的通信功能愈来愈显得重要。...这里我们们也是基于串口通信的,且设置为常用的异步串行通信方式。  并行通信方式  串行通信方式    异
  • STC15W408AS只有一个串口串口1,有4种工作方式,其中两种方式的波特率是可变的,另两种是固定的,以供不同应用场合选用。 一、串口1相关寄存器 下面只把接下来我需要的寄存器和寄存器的位说明一下。 1.1 控制...
  • 单片机串口通信 串行接口 (Serial Interface) 是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,793
精华内容 1,917
关键字:

单片机串口多主机