zoukankan      html  css  js  c++  java
  • 51单片机串口通信

    1.通信方式分类

    (1)并行通信
    发送方和接收方用多根数据线连接,多位数据同时发送。传输线多,长距离传输时成本大。

    (2)串行通信
    单根数据线发送数据,逐位发送。长距离传送成本低,但控制相对复杂。


    串行通信又可分为:异步串行通信同步串行通信

    异步串行通信:所谓“异步”,指的是双方设备使用各自的时钟,以字符为单位传输,采用一种特殊的格式称为“帧”(如下图),且各字符之间的间隙不等。
    一帧数据由起始位,数据位,校验位和停止位构成。

    常态下,数据线上为高电平。起始位为低电平,也就是说,起始位出现,表示有一帧数据要传输了。

    校验方式有奇偶校验、和校验和循环冗余校验三种方式。

    其中“和校验”是指,对数据块求和,产生一个字节的校验数据存到数据块末尾,接收方接受数据时对数据块再求和,和末尾的校验数据比较,不一致就表示传输发生错误。

    同步串行通信:双方的时钟严格一致,传送的字符数据间没有间隙,双方实现同步。

    2.RS232和TTL电平的转换

    RS232是美国电子工业协会于1962年发布的串行通信接口标准,RS即Recomend Standard,推荐标准,232为标示号。RS232用的是负电平逻辑,-3V ~ -15V 为1,+3 ~ +15V为0。

    TTL是Transistor-Transistor Logic的简写,晶体管-晶体管逻辑。工作电压5V。规定:

    对于输出电路:电压大于等于(≥)2.4V为逻辑1;电压小于等于(≤)0.4V为逻辑0;
    对于输入电路:电压大于等于(≥)2.0V为逻辑1;电压小于等于(≤)0.8V为逻辑0;
    RS232和TTL接口不仅有工作电压的不同。RS232传输速率低,传输距离不长,采用共地传输产生共模干扰。二者之间需要通过转换芯片转换电平,如MAX232。

    MAX232外围电路图:

    上半部分为电源转换电路,下半部分为发送和接收部分。

    注意输入输出要一一对应。从T1in输入就要从T1out输出,从R1in输入就要从R1out输出。

    3.波特率

    波特率是衡量串行数据传输速率的指标,和比特率一个单位,即每秒传输了多少位,bit per second,bps。

    波特率的计算公式:

    计算定时器装入的初值

    设初值为X,那么定时器就是每计 256-X 个数溢出一次。

    首先根据晶振频率计算计一个数需要的时间。

    如11.0592MHz,12个时钟周期等于一个机器周期,所以计一个数需要的时间为频率11.0592MHz的倒数再乘上12,即12/11059200(s)。

    那么定时器溢出一次的时间就是 12/11059200*(256-X)。作个倒数就是溢出率。

    接着根据采用的波特率和选择的工作方式SMOD,代入上面相应的计算公式,就可以计算出初值X了。此时计算出的X为十进制,然后转成16进制。

    常用波特率初值表:


    为什么51系列单片机的晶振会用11.0592MHz这个神奇的数?

    因为如果采用整数如12MHz或6MHz的话,计算出的初值就不是一个整数,导致定时出现累积误差。试来试去,用11.0592MHz能非常准确地计算出定时器的初值。只要是标准的通信速率,算出来的初值都是整的。

    4.通信例程

    串口的初始化

    1. 设定定时器的工作方式 TMOD = …
    2. 根据设定的波特率和晶振频率(以及SMOD),计算定时器的初值。
    3. 启动定时器 TR1 = 1;
    4. 设定串行口的工作方式 SCON = …
    5. 串行口开中断 ES = 1,打开总中断EA = 1。

    串口中断程序

    • 从SBUF里取数据
    • RI清0
    • 发送数据
    • 判断是否发送完成;TI清0
    • (可通过设定标志位来把代码移动到主函数里)

    下面是例程的注释,程序实现在上位机上输入字符,下位机(单片机)返回“I get ”+输入的字符。

    #include "reg51.h"
    
    typedef unsigned char u8;
    typedef unsigned int u16;
    
    u8 flag,r,i;	//定义标志位,取数据的变量r和发送字符的变量i
    
    u8 code table[] = "I get ";
    
    void UsartInit()
    {
     	TMOD = 0x20;	//设定定时器1为工作方式2,8位自动重装
    	TH1 = 0xF3;		  //装入初值(波特率4800,晶振12M,加倍)
    	TL1 = 0xF3;
    	PCON = 0x80;	 //SMOD是TCON的最高位,此处设SMOD为1,1000 0000
    	TR1 = 1;		//启动定时器1
    	SCON = 0x50;  	 //设置串口为工作方式1,允许接收位REN置1,0101 0000
    	ES = 1;			//串口中断允许
    	EA = 1;			 //打开总中断
    }
    
    void main()
    {
     	UsartInit();	   //串口初始化
    	while(1)		  //持续等待中断的出现
    	{
    	 	if(flag == 1)	  //中断发生且中断程序跑完
    		{
    		   	ES = 0;		  //把中断允许先关掉,防止下面发送数据时又申请中断
    			for (i=0;i<6;i++)
    			{
    				SBUF = table[i];   //一位一位地发送数据
    				while(!TI);		   //判断是否发送完成
    				TI = 0;			   //TI在发送数据完成后自动置1,把TI清0
    			}
    			SBUF = r;	//发送最初接收的数据
    			while(!TI);
    			TI = 0;		 //同上清0
    			ES = 1;		  //中断允许重新打开
    			flag = 0;	  //标志位清0,等待下次数据的输入
    		}
    	
    	}	  
    }
    
    void Usart() interrupt 4
    {
    	r = SBUF;	   //从SBUF里取接受到的数据
    	RI = 0;			 //RI在接收数据完成后自动置1,把RI清0
    	flag = 1;		 //标志位置为1
    }
    

      

  • 相关阅读:
    GUI 监听事件 (两个按钮,实现同一个监听)
    GUI 监听事件
    GUI 练习
    GUI 之表格布局
    GUI 之边界布局
    GUI 之流布局
    [转帖]Linux 下解压 rar 文件
    Linux 启动、停止、重启jar包脚本
    关于linux下,ls vi等命令失效的解决方法(配置下环境变量出现问题)
    超好用的UnixLinux 命令技巧 大神为你详细解读
  • 原文地址:https://www.cnblogs.com/banmei-brandy/p/11258686.html
Copyright © 2011-2022 走看看