单片机串口通信软件

2019-02-27 16:13:36 xuewei_Li 阅读数 3960

单片机_串口通信详解

  
  楼主在实际的项目开发过程中,特别是用一些比较常见的传感器,使用最多的通信方式可能就是串口通信了,特别是UART(通用异步收发器),线少,虽然传输速度没有并行传输高,但是对我们要求不高的项目中,足够了!足够了!足够了!最重要的是简单!简单!简单!
  


  
理论篇
  

  • 串行通信并行通信

  速度和资源的问题,比如说一个字节八位,如果一位一位通过一根线传输那就是串行通信,但要是八位同时通过八根线一起发,就是并行通信。

  

  • 串行通信三种传送方式

  单工: 数据传输只支持数据在一个方向上传输,应用如监视器、电视机
  半双工:允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信,应用如对讲机,只能一个人讲一个人听,但是两个人都可以讲和听。不能同时进行。
  全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。应用如打电话,两个人可以同时讲话,同时听到对方的内容。
单工半双工双工

串行通信两种通信方式

  前面说过,串行通信是通过一根线来接受发送的,问题来了,怎么发送是不是就是一个问题?

  如果我想什么时候发送给你一帧数据(包括由起始位1位数据位8位奇偶校验位1位停止位1位组成),没有约定,也就是一帧数据发送完到下一帧数据中间间隔的时间是不确定的,这种就是异步通信
在这里插入图片描述
  图片来自:https://blog.csdn.net/u012160319/article/details/43486995

  注意这里每个字符都是固定的格式,但是中间的空闲位是不确定的。

  但如果,我们规定好1秒钟你就必须发送给我9600位数据,这个时候你也想既然时间都定了,没必要没一帧数据都加一个起始位、奇偶校验位、停止位,就在开始发送一两个同步字符,告诉我开始发了,后面连续按顺序传送数据,没有间隙,知道一块数据发送完成。这就叫同步通信
在这里插入图片描述
  图片来自:https://blog.csdn.net/u012160319/article/details/43486995

  注意这里在数据流的开始有规定的1或2个约定的同步字符后数据是连续按顺序的。

  所以同步和异步最重要的区别就是有没有同步时钟。而且同步通信是有一根时钟线的
  异步通信传输是以字节为单位的,但是同步通信传输是以数据块(帧)为单位的
  

  • 常见的串行通信

在这里插入图片描述

  这里说一下USART,USART是一个全双工通用同步/异步串行收发模块,该接口是一个高度灵活的串行通信设备。他是和UART兼容的,但是USART是支持同步的,所以如果你想使用其进行同步通信,还要接其时钟线,但是我们实际上都是按照UART用。  //手动滑稽

  

  • 波特率、奇偶校验位、停止位 (摘自百度)
      

  波特率指的是信号被调制以后在单位时间内的变化,即单位时间内载波参数变化的次数这是一个衡量符号传输速率的参数。如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位,1个停止位,8个数据位),这时的波特率为240Bd,比特率为10位*240个/秒=2400bps。

  起始位提示接收器数据传输即将开始,即标志传输一个字符的开始。必须是持续一个比特时间的逻辑0(低电平),使数据线处于逻辑0低电平状态,发送器通过发送起始位而开始一个字符传送,接收方可用起始位使自己的接收时钟与发送方的数据同步。
  奇偶校验位在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。
  停止位用于表示单个数据包的最后一位典型的值为1*,1.5和2位*。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。

  看到这里,博主突然想到一个问题,不知道大家有没有发现一个问题,就是我们平时使用的串口小助手,当我们用UART时候,我们使用设置了有无就检验,既然没有同步时钟,为啥还要有一个波特率的设置呢?

在这里插入图片描述
  看到没,看到没,这里下次给大家说明一下哦!

  


  

实战篇

//这里转自郭天祥单片机的串行口测试程序,使用51单片机的定时器1确定比特率,具体通信方式为UART
//补充一个知识点---串口中断,当单片机接收到一帧数据后,RI会置1,向CPU申请中断,若之前有中断允许,
//则产生了中断,进入中断服务程序。当然,单片机发送完一帧数据,TI也会置1,同样会产生中断!

#include "reg52.h"  //定义51单片机特殊功能寄存器

#define BAUDRATE     2400  //波特率
#define SYSTEMCLOCK  12000000  //系统时钟频率

void uart_sendstring(unsigned char *str);

//主函数
void main(void)
{ 

  	SCON = 0x50;
	TMOD |= 0x20;
	TH1 = 256 - (unsigned int)(SYSTEMCLOCK/BAUDRATE/384+0.5); 
	ES = 1;  //时能串口中断
	EA = 1;
	TR1 = 1;  //启动定时器1

    uart_sendstring("hello,world.\r\n");
   
    while(1); 
}

//通过串口发送字符串
void uart_sendstring(unsigned char *str)
{
    unsigned char *p;
    
    p = str;
    while(*p != '\0')
    {
        SBUF = *p;
		while(TI == 0);  //等待发送标志位置位
		TI = 0;
        p++;
    }
}
//这部分程序很简单,就是发送一串字符串,可用

皮一下,下篇就介绍为啥 异步通信还要设置波特率?_深入理解同步/异步通信。
在这里插入图片描述
图片来自水印,皮

2018-11-17 18:28:24 nanfeibuyi 阅读数 15392

单片机 串口编程之串口通信仿真实验

一、简述

       记--简单的使能串口,串口收发数据的例子。(使用Proteus仿真+虚拟串口调试)

       代码,仿真文件打包:链接: https://pan.baidu.com/s/1nyb46fTJrYcAy_VarFdO3A 提取码: j44s 

         蓝奏:https://www.lanzous.com/i2fx3oh

      虚拟串口调试软件打包:链接: https://pan.baidu.com/s/1qaOgM8P7ZRmXb903NkiMOQ 提取码: r18u 

         蓝奏:https://www.lanzous.com/i2fx41a

       串口助手源码:链接: https://pan.baidu.com/s/1T9ZA8jnsjXDhNLuL1ezGdg 提取码: 6usr 

二、效果

        PC机通过串口调试助手发送数据给单片机,单片机收到之后回复:I received.。如果单片机收到的是'1'则点亮LED灯,否则熄灭LED灯。

           

   使用c#编写的串口助手

      

  (真实硬件实验:使用Keil C51将代码编译为HEX文件,用串口线/USB转串口线连接PC机,可以使用STC-ISP软件烧写到51单片机,在PC机可以使用串口调试助手(STC-ISP也有这个功能,也可以是其他软件,当然也可以自己编写串口程序)与单片机进行串口通信。实际中,电脑串口采用232电平,而单片机串口则采用TTL电平,如果不进行电平转换,单片机与电脑就无法正常通信,甚至单片机芯片可能会被烧坏。MAX232 芯片可以进行电平转换,是将单片机输出的TTL电平转换成PC机能接收的232电平或将PC机输出的232电平转换成单片机能接收的TTL电平。​​​​电平是个电压范围,如可能规定输出电压>2.4V则为高电平,输出电压低于<0.4V则为低电平,不同的电子器件、不同的标准有着不同的电压范围​​​)

       注:一个COM一般不能同时被两个程序占用。

三、工程结构及各属性设置

        keil工程

         

         

         

          

        proteus仿真

        

添加虚拟串口

                                

四、源文件

       main.c文件 

#include <reg51.h>
sbit p1_0 = P1^0;

//初始化串口 (设置串口,开启串口中断)
void init_uart(void)
{
	SCON = 0x50;		        // SCON: 方式 1, 8-bit, 允许接收数据 
	TMOD |= 0x20;               // TMOD: 设置定时器1工作在方式2, 8-bit 自动重装
	TH1 = 0xFD;               // TH1:  初始值为0xFD  波特率:9600 晶振频率:11.0592MHz  
	TL1 = 0x0;
	TR1 = 1;                  // TR1:  开启定时器1                         
	EA  = 1;                  //打开总中断
	ES  = 1;                  //打开串口中断
}       

           
// 发送一个字节数据
void uart_send_byte(unsigned char dat)
{
	SBUF = dat; // 将数据送到发送缓冲寄存器SBUF,一位一位的发送
	while(!TI); // 等待发送完毕 (发送完毕TI硬件置1)
	TI = 0;// 将TI清零,表示可以发送下一字节数据。
}

// 发送字符串
void uart_send_str(unsigned char *s)
{
	while(*s != '\0')// '\0':字符串结束标志
	{
		uart_send_byte(*s);// 发送1个字节数据,1个字符占8位,1字节
		s++;// 指向下一个字符
	}
}


// 串口中断处理函数 (串口接收到数据,发送数据完毕都可以引起串口中断)
void uart_interrupt(void) interrupt 4 		//也叫串行中断服务程序
{
	unsigned char recv_data;// 用来存放接收到的数据
	unsigned char send_data[] = "I received.\n";// 要发送的信息
	
	if(RI) //接收数据(1字节)完毕,RI会被硬件置1
	{
		RI = 0;            		// 将 接收中断标志位 清零(让串口可以继续接收数据)
		recv_data = SBUF;           	//读取接收到的数据,并存放到data
	
		
		if(recv_data == '1')//如果收到的字符是'0',就让灯灭
		{
			p1_0 = 0;//  p1.0引脚低电平,LED灯亮
		}
		else
		{
			p1_0 = 1;// p1.0引脚高电平,LED灯灭
		} 

	    uart_send_str(send_data); //收到数据之后,发送字符串"I received."给对方
	}
	if(TI)// 发送数据(1字节)完毕
	{
		TI = 0;// 将 发送中断标志位 清零(让串口可以继续发送数据)
	}
} 

void main(void)
{
	init_uart();// 初始化串口
	
	while(1)// 主循环不做任何动作。(死循环)
	{}
}

五、总结

       51单片机系列

      1、单片机的串口相关的寄存器

            1) 串口控制寄存器SCON:(Serial CONtrol)

            2) 串行数据寄存器SBUF:(Serial data BUFfer)

            3)电源管理寄存器PCON:(Power CONtrol)

      2、串口控制寄存器SCON

            用来对串口进行控制,其寄存器地址为0x98,支持位寻址。

            一般来说单片机的寄存器的个数在制作好就已经确定了,MCS-51单片机有一些寄存器是8位的(可以存放8位数据),有一些是16位的,这些寄存器有点像全局变量(寄存器名-变量名),它们分配了固定的地址空间,我们可以对这个寄存器变量进行赋值或读取值。比如我们禁止串口中断,那么我们可以设置相应的值,当系统处理串口相关的任务时,读取到这个我们设置的值时,直到我们要禁止串口中断,那么系统就不会相应串口中断。串口控制寄存器SCON的地址为0x98,我们可以直接对SCON这个"变量"进行赋值,支持位寻址意思是:SCON一共有8位,每1位代表的意思都有不同,可以直接单独对某一位进行设置,有一些寄存器是不可以位寻址的。

           

SCON控制寄存器
位编号

D7

D6

D5

D4

D3

D2

D1

D0

位名称

SM0

SM1

SM2

REN

TB8

RB8

TI

RI

位地址

9FH

9EH

9DH

9CH

9BH

9AH

99H

98H

 

SM0,SM1串行口工作方式控制位

SM0

SM1

工作方式

功能

波特率

0

0

方式0

8位同步移位寄存器

晶振频率/12

0

1

方式1

10位UART

可变

1

0

方式2

11位UART

晶振频率/64或晶振频率/32

1

1

方式3

11位UART

可变

 

SM2 多机通信控制位 当该位置1为多机通信,多机通信仅在方式2、方式3有效。
REN 接收允许位  为1允许接收,为0禁止接收
TB8 待发送的第9位数据  同来存放工作方式2,工作方式3模式下等待发送的第9位数据
RB8 收到的第9位数据 同来存放工作方式2,工作方式3模式下接收到的第9位数据,在方式1下为接收到的停止位,工作当时0不使用该位。
TI 发送中断标志位  发送完成标志位,当SBUF中的数据发送完成后由硬件置1,并且当单片机硬件中断被使能之后触发中断事件,该位必须由软件清零,并且该位被清零之后才能进行下一个字节数据的发送。
RI 接收中断标志位 接受完成标志位,当SBUF收到一个字节的数据后由硬件系统置1,并且当单片机硬件中断被使能之后触发串行中断事件,该位必须由软件清零,并且只有该位被清零之后才能够进行下一字节数据的接收。

      3、串行数据寄存器SBUF

            用于存放串行通信中发送和接收的相关数据。其寄存器地址是0x99。

            SBUF由发送缓冲寄存器和接收缓冲寄存器组成,这两个寄存器占用同一个寄存器地址。其中发送缓冲寄存器只写,接收寄存器只读。

           发送数据:当将一个数据写入SBUF后,单片机立即根据选择的工作方式和波特率将写入的字节数据进行相应的处理然后从TXD(P3.1)引脚串行发送出去,发送完成之后置位相应寄存器的标志位,只有相应的标志位被清除之后才能进行下一次的数据发送。

           接收数据:当RXD(P3.0)引脚根据工作方式和波特率接收到一个完整的数据字节后单片机将该数据字节放入接收缓冲寄存器中,并置位相应的标志位。接收缓冲寄存器是双字节的,这样就可以在单片机读取接收缓冲寄存器的数据时同时进行下一个字节的数据接收。不会发生前后两个字节的数据冲突(数据覆盖)。

      4. 电源管理寄存器PCON

            可以控制串口波特率加倍。

                                                                                         PCON电源管理寄存器

位编号

D7

D6

D5

D4

D3

D2

D1

D0

位符号

SMOD

SMOD0

LVDF

POF

GF1

GF0

PD

IDL

      SMOD:该位与串口通信有关。

      SMOD=0; 串口方式1,2,3时,波特率正常。

      SMOD=1; 串口方式1,2,3时,波特率加倍。

      5、串口的工作方式

            MCS-51单片机的串口一共4种工作方式。其中工作方式0为同步通信方式,其余3种为异步通信。

            工作方式0:外部引脚TXD(P3.0)为数据的输出输入端,外部引脚TXD(P3.1)提供数据的同步脉冲。

                                因此工作在方式0进行串口通信不需要考虑波特率。

           工作方式1:使用定时器作为波特率发生器。

                               波特率的计算  

                                              

                                                                          常见的波特率对应的初始值
波特率/工作频率 11.0592MHz 12MHz

14.74456MHz

16MHz 20MHz SMOD值
150(b/s) 0x40 0x30 0x00     0
300(b/s) 0xA0 0x98 0x80 0x75 0x52 0
600(b/s) 0xD0 0xCC 0xC0 0xBB 0xA9 0
1200(b/s) 0xF8 0xE6 0xE0 0xDE 0xD5 0
2400(b/s) 0xF4 0xF3 0xF0 0xEF 0xEA 0
4800(b/s)   0xF3 0xEF 0xEF   1
4800(b/s) 0xFA   0xF8   0xF5 0
9600(b/s) 0xFD   0xFC     0
9600(b/s)         0xF5 1
19200(b/s) 0xFD   0xFC     1

      工作方式2:

                                                                     

      工作方式3:波特率计算方式与工作方式1相同。 

     

      6.波特率

           波特率是指数据信号对载波的调制速率,它用单位时间内载波调制状态改变的次数来表示。

           在信息传输通道中,携带数据信息的信号单元叫码元,每秒钟通过信道传输的码元数称为码元传输速率,简称波特率。

           通信双方约定一致的波特率以便正确的接收数据。(数据发送前可能进行了调制,波特率不同,调制参数不同)

          如果收发双方的波特率不一样,那么调制与解调的参数不一样,就很有可能读取不到正确的数据,从而解读为乱码。

          通常串口工作方式1使用定时器作为波特率发生器。

      7、串口中断

            使能串口中断:打开总中断-中断控制寄存器IE的EA位置1,打开串口中断中断控制寄存器IE的ES位置1

                                                                                               中断控制寄存器IE(Interrupt Enable

位序号

D0

D1

D2

D3

D4

D5

D6

D7

说明

外部中断0

定时/计数0

外部中断1

定时/计数1

串行口中断

保留位

保留位

全局中断位

位符号

EX0

ET0

EX1

ET1

ES

--

--

EA

位地址

A8H

A9H

AAH

ABH

ACH

--

--

AFH

EX0 外部中断0允许位 EX0=1,允许外部中断0中断;EX0=0,禁止外部中断0中断。
ET0 T0溢出中断允许位 ET0=1,允许T0中断;ET0=0,禁止T0中断。
EX1 外部中断1允许位 EX1=1,允许外部中断1中断;EX1=0,禁止外部中断1中断。
ET1 T1溢出中断允许位 ET1=1,允许T1中断;ET1=0,禁止T1中断。
ES

串行中断允许位

(Enable Serial)

ES=1,允许串行口中断;ES=0,禁止串行口中断。
EA

中断总允许位

(Enable All)

EA=1,CPU开放中断;EA=0,CPU禁止所有的中断请求。总允许EA好比一个总开关。

 

     使能串口中断之后,接收到数据(RI置1)/完成发送(TI置1),RI/TI被置1就会触发串口中断事件,然后执行中断处理函数。

     中断处理函数编写格式:               

void 函数名(void) interrupt 4 using 工作寄存器组编号
{
    //要执行的动作
}

      4的含义: 一共5个中断编号为0~4,串口中断的编号为4

      using 寄存器组编号:一共4组工作寄存器组,编号为0~3,如果不写默认为工作寄存器组0。

8、初始化串口为模式1过程。

      1) SCON设置

位名称

SM0

SM1

SM2

REN

TB8

RB8

TI

RI

设置值

0

1

0

1

0

0

0

0

含义 工作在方式1 波特率不加倍 允许接收数据 工作方式1不设置该位 初始为未发送完数据

初始为为接收到数据

      SCON = 0x01010000 = 0x50    

       2) PCON设置

       

位编号

D7

D6

D5

D4

D3

D2

D1

D0

位符号

SMOD

SMOD0

LVDF

POF

GF1

GF0

PD

IDL

默认波特率不加倍 0 0 0 0 0 0 0 0
波特率加倍 1 0 0 0 0 0 0 0

      PCON = 0x00 (SMOD=0,波特率不加倍)

      PCON = 0x80 (SMOD=1,波特率加倍)

      3) 定时器设置

              工作方式设置:

                                                                  MI,M0定时器模式选择

M1

M0

工作方式

功能说明

0

0

方式0

13位定时器/计数器

0

1

方式1

16位定时器/计数器

1

0

方式2

自动重载8位定时器/计数器

1

1

方式3

T0分为2个8位独立计数器,T1无方式3

            开启定时器1:将控制寄存器TCON的TR1置1

            设置定时器1的初值:TH1数据寄存器装载初值的高8位,TL1数据寄存器装载初值的低8位。

            定时器用作波特率发生器时,使用上面的波特率计算公式得到初始值,或直接查看常见的波特率对应的初始值。

            TH1 = 0xFD;// 11.0592MHz的工作频率,9600的波特率 对应的初始值。 

        4) 打开总中断 EA = 1; 打开串口中断 ES = 1;

              

================以下回复 qq_45096251 这位兄弟==============

两个单片机之间的串口通信仿真实验:

测试的仿真文件和代码:链接: https://pan.baidu.com/s/1PMu6dT-1DMEJllgv9gxqMA 提取码: hrcy

使用虚拟串口软件,虚拟出两对串口,例子中是com1<==>com2,  com3<==>com4,其中com2,com4分别对应单片机A和单片机B。com1和com3分别是PC端与单片机A、B通信的端口。在PC端使用串口工具实现两个仿真单片机的串口通信。

测试效果:

2019-11-04 22:26:31 lin5103151 阅读数 1480

串口,作为单片机程序开发中最常用、最方便,也是应用最广泛的程序调试方法;无论是作为调试工具,打印出调试信息,还是对功能模块进行通信,串口是每个单片机开发人员最常用的单片机外设。
由于大部分51单片机不支持在线调试功能,所以串口作为一种有效的调试功能,所以在51单片机程序开发时,无法进行在线调试,不妨可以多采用串口来进行调试。
1.串口配置

51单片机配置除了需要配置2个8位寄存器SCON、PCON外,还要配置定时器1的控制寄存器TMOD,因为串口在进行收发需要使用定时器来采样。
(1)状态控制寄存器 SCON
SCON 是一个逐位定义的 8 位寄存器,用于控制串行通信的方式选择、接收和发送,指 示串口的状态,SCON 即可以字节寻址也可以位寻址,字节地址 98H,地址位为 98H~9FH。 它的各个位定义如下:
在这里插入图片描述
SM0 和 SM1 是串口的工作方式选择位,2 个选择位对应 4 种工作方式,如下表,其中 Fosc 是振荡器的频率。
在这里插入图片描述
(2)电源与波特率控制寄存器
控制寄存器 PCON 也是一个逐位定义的 8 位寄存器,目前仅仅有几位有定义,如下所示:
在这里插入图片描述
仅最高位 SMOD 与串口的控制有关,其他位与掉电方式有关。PCON 的地址为 87H 只能按 字节寻址,SMOD 是串行通信波特率系数控制位,当串口工作在工作方式 1、2 时,若使用 T1 作为波特率发生器其 SMOD=1 则波特率加倍。
(3)定时器控制模式寄存器
TMOD是定时器、计数器模式控制寄存器,它是一个逐位定义的8为寄存器,但只能使用字节寻址
在这里插入图片描述
当串口工作在工作方式0和2是,波特率固定,方式0时fosc/12;方式2时fosc/32或fosc/64(根据SMOD判断)。当串口工作在方式1时,波特率=(2^SMOD/32)*(单片机时钟频率/(256-X)),X是初值;C/T#为定时器和计数器选择位,0为定时器,1为计数器

2.串口通用程序
为了有较好的通用性,将串口程序配置为一个H文件和C文件,往后只要开发51单片机程序,都可以将该两个文件复制在工程中直接使用,无须修改。
(1)uart.H

#ifndef _UART_H_
#define _UART_H_

#include "reg52.h"

void UartInit(void);
void Send_string(unsigned char *c);
void Send_Data(unsigned char DAT);

#endif

(2)uart.c

#include "uart.h"

//串口初始化
void UartInit(void)
{
	/*串口初始化 */
	TMOD = 0x20;	  //定时器T1使用工作方式2
	SCON = 0x50;	  //工作方式1,,允许接收   
	PCON = 0x10;
	TH1 = 253;        // 设置初值,波特率9600bps
	TL1 = 253;
	TR1 = 1;          // 开始计时	
	ES = 1;         //打开接收中断
	EA = 1;           // 打开所以中断   
	TI = 0;
	RI = 0;
}

void Send_Data(unsigned char DAT)
{
	ES = 0;
	TI=0;
	SBUF = DAT ;
	while(TI==0);
	TI=0;
	ES = 1;
}

void Send_string(unsigned char  *c)
{
	while(*c != '\0')
	{
		Send_Data(*c++);
	}	
}

void RSINTR() interrupt 4 using 2
{
	EA=0;
	if(TI==1) //发送中断	  
	{
		TI=0;
	}

	if(RI==1)	 //接收中断		  
	{	

		RI=0;								 

	}
	EA=1;
}

如需串口参考例程, 请关注公众号,首页回复“串口”获取资料
在这里插入图片描述

2018-06-09 11:19:44 nanfeibuyi 阅读数 870

单片机 串行通信练习

一、简述

    简单的双单片机间串行通信演示。

    例子中有甲乙两片单片机系统构成一对单片机系统,每片单片机系统有2位数码管用来显示收到的数据,两片单片机系统之间进行串行通信。

甲方发送1,乙方发送2,甲方发送3,乙方发送4……每次加1,直至99。

文件打包:链接: https://pan.baidu.com/s/1er2xDqgrecxuGYeBG-Q0ug 密码: karu

二、效果


三、工程文件

1、Keil文件


2、仿真电路图


四、源文件

SBUF.c文件

#include<reg51.h>
#define uint unsigned int
#define uchar unsigned char
uchar led[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
uchar num;
//定义引脚变量
sbit P2_0=P2^0;
sbit P2_1=P2^1;
sbit P1_0=P1^0;
/**********************************************************/
//函数名:delay(uint x)
//功能:延时程序	
//调用函数:
//输入参数:x
//输出参数:
//说明:程序的延时时间为x乘以0.5ms
/**********************************************************/
void delay(uchar x)
{
	uchar y,z;
	for(y=x;y>0;y--)
		for(z=250;z>0;z--);//该步运行时间约为0.5ms
}
/**********************************************************/
//函数名:display(uchar num)
//功能:2位数码管显示
//调用函数:delay(uint x)
//输入参数:num
//输出参数:
//说明:P0口做数码管段选,P2口做位选
/**********************************************************/
void display(uchar num)
{	
	P2_0=0;//开十位位选口
	P0=~led[num/10];//P0口输入段选数据
	delay(5);//延时2.5sm
	P2_0=1;//关十位位选口

	P2_1=0;//开个位位选口
	P0=~led[num%10];//P0口输入段选数据
	delay(5);//延时2.5sm
	P2_1=1;//关个位位选口
}
/**********************************************************/
//函数名:TAKE_SBUF(uchar dat)
//功能:串数据发送程序
//调用函数:无
//输入参数:dat
//输出参数:
//说明:dat为要发送的八位串口通讯数据
/**********************************************************/
void TAKE_SBUF(uchar dat)
{
	ES=0;//关串口中断,当一个字节数据被发送成功后,会自动置位TI标志位,如果上一次发送成功之后,
	//会标志为已发送,所以在要发送数据之前先关闭串口中断,再装载要发送的数据,否则可能你还没装载号新的数据,对方就再次请求,然后拿到的还是上一次数据
	SBUF=dat;//将要发送的数据存入SBUF寄存器中,对方会在某一个时刻获取。

	while(~TI);//等待发送结束,验证数据是否发送成功,如果还没发送成功,则CPU进行空循环等待硬件把数据发送完毕。			  
	TI=0;//发送中断标志位置0,清零发送中断标志位,因为数据发送成功后,该标志位会被自动置1,把它清零以免影响误中断。
	ES=1;//开串口中断 ,打开刚才被关闭的串行口中断,等待接收数据。
}
void main()
{
	SCON=0X90;//0x10010000,设置串口通讯为方式2,11位异步串行通信
	EA=1;//开总中断
	ES=1;//开串口中断
	P2=0xff;//数码管位选口初始化 ,全部灭灯
	num=0;//显示值初始化
	if(P1_0==0)
	TAKE_SBUF(num+1);//串口发送数据(num+1)
	while(1)
	{
			
	}//主程序不做任何事情,通过串口中断,进入控制数码管的处理程序
}
/**********************************************************/
//函数名:int0rupt() interrupt 4	
//功能:串口中断响应程序
//调用函数:display(uchar num)
//输入参数:
//输出参数:
//说明:显示接收到的串口数据,将数据加一后通过串口发送
/**********************************************************/
void int0rupt() interrupt 4
{						 
   	uchar i;
	num=SBUF;//从串行口寄存器SBUF中取得数据
	if(num>99)
	{
		num=0;//限定num值范围0到99
	}
	for(i=200;i>0;i--) //因为单片机执行速度很快,所以用一个for循环语句执行多遍目的就是为了让我们看清楚数据这里我们选择了100次
		display(num);//显示收到的串口数据,显示在数码管
	TAKE_SBUF(num+1);//将num值加一后通过串口发送
	RI=0;//接收中断标志位置0 ,	清零接收中断标志位,就是恢复没有中断的状态。
}

五、总结

1、串行通信

        51单片机内部有一个可编程的全双工串行通信电路。通过引脚RXD(P3.0串行数据接收端)和引脚TXD(P3.1串行数据发送端)与外界通信。

        内部通信电路中有一个SBUF串行口缓冲寄存器,它既是发送寄存器也是接收寄存器。发送与接收数据就是通过该寄存器实现的。通信编程的关键是对寄存器的一些设置,在编程中需要用到有几个寄存器。

       1)数据缓冲寄存器SBUF

            当需要发送数据时,只要把被发送的数据写入该寄存器,当需要接收数据时,只需从此寄存器读取数据。

       2)串行口控制与状态寄存器SCON


51单片机全双工串行口具有4种工作方式。

方式0:为移位寄存器输入/输出方式
方式1:为波特率由T1控制的10位异步通信方式
方式2:为固定波特率的11位异步通信方式
方式3:为波特率由T1控制的11位异步通信方式

总结:方式0通常用于扩展I/O,实现串行与并行的相互转换。方式1用于单片机的双机通信中用得比较常见;方式2和方式3由于多了第9位可以实现多机通信,经常在主从单片机系统中得到应用。

2、波特率

        在串行通信中,收发双方的数据传输速度要有一定约定,不然双方速度不一样就会导致收到乱码的数据。

    方式0的波特率是固定的主振频率的1/12。
方式2的波特率由PCON中的选择位SMOD来决定,其计算公式:
波特率=(2SMOD/64)*fosc

方式1和方式3的波特率也受PCON中的选择位SMOD影响,其计算公式:



2018-08-05 20:33:39 qq_27485531 阅读数 18059

1、串行通信的的基本知识

在实际的工业生产,或者生活中,计算机的CPU要与外部的设备之间进行信息的交流,数据的交换,所有的这些信息交换均可称为通信。

通信的方式有两种,分别为串行通信和并行通信。我们通常根据实际信息传输的距离还决定采用哪种通信方式。

并行通信:

并行通信是指数据的各位同时进行传送(发送或者接收)的通信方式。其优点是传送速度快,缺点是数据有多少位,就需要有多少根传送线。

串行通信:

串行通信是指数据 一位一位的按顺序传送的通信,他的优点是传送线少(只需要一对传输线),特别适用于远距离的数据通信,缺点是传送速度低。

其通信的原理如下图所示

串行通信的数据传输方式:

分为3种:1、单向(单工)配置,只允许数据向一个方向传送。2、半双向(半双工)配置,允许数据向两个方向中的任一方向传送,但每次只能有一个站点在发送。3、全双向(全双工)配置,允许同时双向传送数据,因此,他要求两端的通信设备都具有完整和独立的发送和接收的能力。如下图所示

串行通信的通信方式

串行通信有两种基本的通信方式:同步通信和异步通信

1、异步通信

异步通信数据格式一般为字符格式

一个字符一个字符地传输,每个字符一位一位的传输,并且传输一个字符时,总是以“起始位”开始,以“停止位”结束,字符间没有固定的间隔要求。每一个字符的前面都有一位起始位(低电平),字符本身由5-7位数据位组成,接着字符后面是一位校验位(也可以没有校验位),最后一位或一位半或两位停止位,停止位后面是不定长的空闲位。停止位和空闲位都规定为高电平,这样就保证了起始位开始处有一个下跳沿。

                                       图一异步通信的传输方式

                                       图二异步通信的数据传输格式

2、同步通信(了解即可)

同步通信就比如说是老师发作业本,约定学习委员来取作业本。建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步,而且传输数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符之间不留间隙,即保持位同步关系,也保持字符同步关系。

同步通信使用的数据格式根据采用的控制规程(通信双方就如何交换信息所建立的一些规定和过程称为通信控制规程),可分为面向字符型面向位型两种,面向字符型数据格式又有单同步,双同步,和外同步之分。三个同步方式均以2个字节的冗余检验码CRC作为一帧信息的结束。

单同步:发送方先发送一个同步字符,再传送数据块,接收方检测到同步字符后接收数据

双同步:发送方先传送2个同步字符,再传送数据块,接收方检测到同步字符后接收数据;

外同步:用一条专用线来传送同步字符,以实现收发双方同步操作同步通信中,在数据开始传送前用同步字符来指示(常约定1~2个),并由时钟来实现发送端与接收端的同步,我们使用QQ的文件传输功能就可以看做是一个同步通信的例子,首先传文件的双方必须说好一个传文件的时间,如果双方有一个不在线上,就不能传输。其次,发送方发送文件命令后,接收方要确认是否接收,这个就是建立文件传输的过程,一旦传输开始,所有文件数据必须连续的传输过去,任何中断都将导致传输失败。

3、波特率

波特率即数据传速率,表示每秒传送二进制代码的位数,它的单位是b/s。波特率对于CPU与外界的通信是很重要的,假设数据传送的速率是120字符/s,而每个字符格式包含10个代码位(一个起始位,一个中止位、8位数据位D0-D7),这是传送的波特率为:10bit/字符*120字符/s = 1200 b/s。波特率即为1200 b/s。每一位的时间即为1/1200。

4、串行通信接口种类

根据串行通信格式及约定(如同步方式、通信速率、数据块格式等)不同,形成了许多串行通信接口标准,如常见的:

UART(串行异步通信接口)

USB(通用串行总线接口)

I2C(集成电路间的串行总线)

SPI(串行外设总线)

485总线、CAN总线接口等。

二、89c51串行口及应用

我所采用的单片机串口通信是用一个USB接口接了一个CH340(USB转异步串口)来进行计算机与单片机双方的通信,如下图所示

下图所用硬件原理图

当开关SW1按下之后,经过USB1再连接一个USB转异步通信串口芯片CH340,使计算机通过USB来与单片机进行通信。

串行通信的工作原理如下

串行通信必须保持设备的同步性,即1、数据双方必须采用统一的编码。2、通信双方必须使用相同的传送速率

1、89c51串行口特点

※全双工串口,能同时发送和接收数据

※可编程:其帧格式可以是8位,10位,11位,并能设置各种波特率

※发送完一个数据TI标志置1,接受完一个数据RI标志置1,可以工作在中断或查询方式

※波特率可以是固定的,也可以是由定时器1的溢出率来决定

※有一个地址相同的接受(发送)缓冲器SBUF(地址99H)

2、结构与工作原理

89c51通过引脚RXD(P3.0串行数据接收端)和引脚TXD(P3.1串行数据发送端)与外界进行通信。其内部结构简化图如图所示

图中有两个物理上独立的接收、发送缓冲器SBUF,它们占用同一地址99H,可同时发送。接收数据。发送缓冲器只能写入,不能读出;接收缓冲器只能读出,不能写入。

串行发送与接收的速率与移位时钟同步。89c51用定时器1作为串行通信的波特率发生器。T1溢出率经2分频(或不分频)后又经16分频作为串行发送或接收的移位脉冲。移位脉冲的速率即波特率。

从图中可以看出,接收器是双缓冲结构,在前一个字节被从接收缓冲器SBUF读出之前,第二个字节即开始接收(串行输入至移位寄存器),但是,在第二个字节接收完毕而前一个字节CPU未读取时,会丢失前一个字节。

串行口的发送和接收都是以特殊功能寄存器SBUF的名义进行读/写的。当向SBUF发送“写”命令时(SBUF = num,假设num是我已经定义好的一个变量),即是向发送缓冲器SBUF装载并开始由TXD引脚向外发送一帧数据,发送完后发送中断标志位TI则自动置1。

在满足串行口接收中断标志位RI = 0的条件下,置允许接收位REN = 1,就会接收一帧数据进入移位寄存器,并装载到接收SBUF中,同时使RI = 1,当发读SBUF命令时(num = SBUF),便由接收缓冲器SBUF取出信息通过89c51内部总线送CPU。

对于发送缓冲器,因为发送时CPU是主动的,不会产生重叠错误,一般不需要用双缓冲结构来保持最大传送速率。

3、 串行口控制字及控制寄存器

 89c51串行口是可编程接口,对它初始化编程只用两个控制字分别写入特殊功能寄存器SCON和电源控制寄存器PCON中即可。

1)SCON

SM0、SM1工作方式控制位

SM2:多机通信控制位,1-允许、0-不允许

REN:串行接收允许位。1-允许、0-不允许

TB8:发送数据第九位

RB8:接收数据第九位

TI:发送中断标志位

RI:接收中断标志位

①SM0和SM1 :串行口工作方式选择位 ,两个选择位对应四种通信方式,如下图所示,其中fosc是振荡频率

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

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

若SM2 = 0,即不属于多机通信情况,则接收完一帧数据后,不管第9位数据是0还是1,都置RI = 1,接收到的数据装入SBUF中。在方式0时SM2必须置0。在方式1时,若SM2 = 1,则只有接收到有效停止位时,RI才置1,以便接收下一帧数据。

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

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

REN = 0时,禁止接收

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

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

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

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

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

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

2)PCON

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

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

4、串行通信工作方式

根据实际需要,89c51串口可以设置四种工作方式,可有8位、10位或11位帧格式

方式0:8位同步移位寄存器,以8位数据为一帧,不设起始位和停止位,先发送或接收最低位(用于I/O口扩展)。

 ※RXD:数据输入/输出端

※TXD:同步脉冲输出端,每个脉冲对应一个数据位

基本很少用到方式0,一般都是用到方式0是通过串口扩展8位并行I/O输出口,如下图所示

方式1:10位异步通信模式

一帧10位:起始位+8位数据位+停止位

波特率:定时器1作为波特率发生器,公式为

方式2和方式3:11位异步通讯方式

一帧11位:起始位+9位数据位+停止位

第九位数据位在TB8/RB8中,常用作校验位或者多机通信地址标识位或者数据标识位

波特率分别为:

发送:先填写TB8,写入SBUF(启动发送),发送完TI = 1.

接收:REN = 1,RI = 0且第9位为1(或SM2 = 0),将接收数据装入SBUF,第9位装入RB8,使RI = 1,否则丢弃接收数据,不置位RI。

接下来我用串口通信来实现计算机与51单片机之间的串口通信,计算机给单片机发送一个数,单片机接收后把这个数加1再发送给计算机,具体操作如下

#include<reg52.h>
#define uint unsigned int
void UART_Init();
uint num ;
/*
******************************************
**函数名称:主函数
*******************************************
*/
int main()
{
        UART_Init();
        while(1);
}
/*
*****************************************
**函数功能:串口初始化函数
*****************************************
*/
void UART_Init()
{
     
            TMOD = 0x20; //定时器0工作模式2,自动重装8位计数器
            TH1 = 0xfd;
            TL1 = 0xfd;//定时器溢出时,会自动将高8位中的值赋值给低8位.比特率9600
            TR1 = 1;
            SM0 = 0;
            SM1 = 1;
            REN = 1;
            EA = 1;
            ES = 1;
}
/*
********************************************
**函数名称:串口中断函数
********************************************
*/
void UART() interrupt 4
{
    if(RI == 1)        //如果接收到计算机发的数据
        {
            num = SBUF;  //取出数据
            num++;       
            RI = 0;       //清除标志位
            SBUF = num;  //把数据加1后再发送给计算机
        }
        if(TI == 1)    //如果发送完毕
        {
            TI = 0;       //清除标志位
        }
}
    
我用串口助手循环发送从1-8,单片机会返回2-9,实现现象如下: