51单片机串口通信教程【8】

一、目标

  1. 单片机通过串口向电脑发送数据(数字递增)
  2. 电脑通过串口控制单片机上的LED

二、基本概念

1. 串口

串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力。
51单片机内部自带UART(Universal Asynchronous Receiver Transmitter,通用异步收发器),可实现单片机的串口通信。

2. 硬件电路

简单双向串口通信有两根通信线:发送端(Transmit Exchange Data, TXD)和接收端(Revieve Exchange Data, RXD)
TXD与RXD要交叉连接
当只需单向的数据传输时,可以直接一根通信线
当电平标准不一致时,需要加电平转换芯片

img

3. 点评标准

电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:

TTL电平:+5V表示1,0V表示0
RS232电平:-3~-15V表示1,+3~+15V表示0
RS485电平:两线压差+2~+6V表示1,-2~-6V表示0(差分信号)

4. 引脚接口及定义(DB9)

img

现在笔记本电脑上都没有这个接口了,都用USB接口。

5. 常见通信接口比较

名称 引脚定义 通信方式 特点
UART TXD、RXD 全双工、异步 点对点通信
I²C SCL、SDA 半双工、同步 可挂载多个设备
SPI SCLK、MOSI、MISO、CS 全双工、同步 可挂载多个设备
1-Wire DQ 半双工、异步 可挂载多个设备

此外还有:CAN、USB等

  • 全双工:通信双方可以在同一时刻互相传输数据

  • 半双工:通信双方可以互相传输数据,但必须分时复用一根数据线

  • 单工:通信只能有一方发送到另一方,不能反向传输

  • 异步:通信双方各自约定通信速率

  • 同步:通信双方靠一根时钟线来约定通信速率

  • 总线:连接各个设备的数据传输线路(类似于一条马路,把路边各住户连接起来,使住户可以相互交流)

  • 6. 51单片机的UART

    STC89C52有1个UART

    STC89C52的UART有四种工作模式:
    模式0:同步移位寄存器
    模式1:8位UART,波特率可变(常用)
    模式2:9位UART,波特率固定
    模式3:9位UART,波特率可变

    img

    img

    TXD接RXD,RXD接TXD

    img

    7. 串口参数及时序图

  • 波特率:串口通信的速率(发送和接收各数据位的间隔时间)
  • 检验位:用于数据验证
  • 停止位:用于数据帧间隔
  • img

    8. 串口模式图

    img

    SBUF:串口数据缓存寄存器,物理上是两个独立的寄存器,但占用相同的地址。写操作时,写入的是发送寄存器,读操作时,读出的是接收寄存器

    9. 串口和中断系统

    img

    在简单情况下,控制ES、EA即可控制中断。

    10. 串口相关寄存器

    img

    三、单片机通过串口向电脑发送数据

    新建项目“8-1 串口向电脑发送数据”,新建main.c。
    把之前的Delay.c和Delay.h复制到本项目文件夹中。

    SCON:串行控制寄存器(可位寻址)
    img

    其中SM0、SM1按下列组合确定串行口的工作方式:
    img

    REN:允许/禁止串行接收控制位。由软件置位REN,即REN=1为允许串行接收状态,可启动串行接收器RxD,开始接收信息。软件复位REN,即REN=0,则禁止接收。

    TI:发送中断请求标志位。在方式0,当串行发送数据第8位结束时,由内部硬件自动置位,即TI=1,向主机请求中断,响应中断后必须用软件复位,即TI=0。在其他方式中,则在停止位开始发送时由内部硬件置位,必须用软件复位。初始化一般置0

    RI:接收中断请求标志位。在方式0,当串行接收到第8位结束时由内部硬件自动置位RI1,向主机请求中断,响应中断后必须用软件复位,即RI0。在其他方式中,串行接收到停止位的中间时刻由内部硬件置位,即RI=1(例外情况见SM2说明),必须由软件复位,即RI=0。初始化一般置0

    1. 串口初始化

    我们选择工作方式1,所以SM0=0, SM1=1,其余的置零,0100 0000,即SCON=0x40

    电源控制寄存器PCON(不可位寻址)
    img

    SMOD:波特率选择位。当用软件置位SMOD,即SMOD=1,则使串行通信方式1、3的波特率加倍;SMOD=0,则各工作方式的波特率加倍。复位时SMOD=0。

    SMOD0:帧错误检测有效控制位。当SMOD0=1,SCON寄存器中的SM0/FE位用于FE(帧错误检测)功能;当SMOD0=0,SCON寄存器中的SM0/FE位用于SM0功能,和SM1一起指定串行口的工作方式。复位时SMOD0=0。

    我们需要让波特率加倍,所以SMOD=1,寄存器赋值应该为1000 0000所以PCON=0x80

    void Uart_Init(void)		//4800bps@11.0592MHz
    {
    	PCON &= 0x80;		//使能波特率倍速位SMOD
    	SCON = 0x50;		//8位数据,可变波特率
    	TMOD &= 0x0F;		//清除定时器1模式位
    	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
    	TL1 = 0xFA;		//设定定时初值
    	TH1 = 0xFA;		//设定定时器重装值
    	ET1 = 0;		//禁止定时器1中断
    	TR1 = 1;		//启动定时器1
    }
    

    上述代码可以从STC-ISP中的波特率计算器获得
    img
    要注意误差为0%

    2. 串口写入

    void UART_SendByte(unsigned char Byte)
    {
    	SBUF=Byte;	//写入
    	while(TI==0);
    	TI=0;	//响应中断后,软件复位
    }
    

    主函数为

    void main()
    {
    	Uart_Init();
    	UART_SendByte(0x66);
    	while(1)
    	{
    		
    	}
    }
    

    编译一下,发现按一下开发板的重置键,STC-ISP的串口助手就出现一次"66"

    img

    img

    如果串口助手的波特率改为其他数字了,按下充值键出现的就不是66了,数据就出错了,因为采样的时间不一样。

    3. 发送秒数

    定义一个unsigned char Sec1;用来记秒数,把UART_SendByte(Sec1);放入while(1)循环中。
    发送完Sec1后,要自增Sec1++;,然后延时1秒Delay(1000);

    void main()
    {
    	Uart_Init();
    	
    	while(1)
    	{
    		UART_SendByte(Sec1);
    		Sec1++;
    		Delay(1000);
    	}
    }
    

    4. 模块化

    新建UART.c和UART.h文件,把串口初始化程序和串口写入程序放到YART.c中,在UART.h中声明一下,在main.c中#include "UART.h"

    UART.c:

    #include <REGX52.H>
    
    /**
      * @brief	串口初始化,4800bps@11.0592MHz
      * @param	无
      * @retval	无
      */
    void Uart_Init(void)		//4800bps@11.0592MHz
    {
    	PCON &= 0x80;		//使能波特率倍速位SMOD
    	SCON = 0x50;		//8位数据,可变波特率
    	TMOD &= 0x0F;		//清除定时器1模式位
    	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
    	TL1 = 0xFA;		//设定定时初值
    	TH1 = 0xFA;		//设定定时器重装值
    	ET1 = 0;		//禁止定时器1中断
    	TR1 = 1;		//启动定时器1
    }
    
    /**
      * @brief	串口发送一个字节数据
      * @param	Byte 要发送的一个字节数据
      * @retval	无
      */
    void UART_SendByte(unsigned char Byte)
    {
    	SBUF=Byte;	//写入
    	while(TI==0);
    	TI=0;	//响应中断后,软件复位
    }
    

    UART.h:

    #ifndef __UART__H__
    #define __UART__H__
    
    void Uart_Init(void);
    void UART_SendByte(unsigned char Byte);
    
    #endif
    

    main.c:

    #include <REGX52.H>
    #include "Delay.h"
    #include "UART.h"
    
    unsigned char Sec1;
    
    void main()
    {
    	Uart_Init();
    	
    	while(1)
    	{
    		UART_SendByte(Sec1);
    		Sec1++;
    		Delay(1000);
    	}
    }
    

    四、电脑通过串口控制LED

    因为单片机不知道电脑什么时候发信息,所以需要一个中断程序,电脑发过来信息后,触发中断,在中断函数中处理数据。

    所以在初始化的时候要把中断程序接上,即EA=1;ES=1;
    SCON的REN之前置0,现在要置1,允许串行口接收信息。也就是说,SCON = 0x50;
    img

    void Uart_Init(void)		//4800bps@11.0592MHz
    {
    	PCON &= 0x80;		//使能波特率倍速位SMOD
    	SCON = 0x50;		//8位数据,可变波特率
    	TMOD &= 0x0F;		//清除定时器1模式位
    	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
    	TL1 = 0xFA;		//设定定时初值
    	TH1 = 0xFA;		//设定定时器重装值
    	ET1 = 0;		//禁止定时器1中断
    	TR1 = 1;		//启动定时器1
    	EA = 1;
    	ES = 1;
    }
    

    img
    在main.c中,定义一个中断服务子函数

    void UART_Routine() interrupt 4
    {
    	P2=0x00;
    }
    

    当中断号 4 产生时,程序就会跳转到UART_Rountine(),点亮所有LED

    编译一下,打开串口助手,随便发一个数据,可以看到,8个LED全亮了。

    把程序升级一下,让LED根据发送的数据亮对应的灯。

    修改一下中断子函数:

    void UART_Routine() interrupt 4
    {
    	if(RI=1)
    	{
    		P2=~SBUF;
    		RI=0;
    	}
    }
    

    编译一下,在串口助手中输入f0,点击发送数据
    img
    可以看到D5~D8亮了。

    发送aa,D2 D4 D6 D8亮了。

    下面再把接收到的数据发回给电脑

    void UART_Routine() interrupt 4
    {
    	if(RI=1)
    	{
    		P2=~SBUF;
    		UART_SendByte(SBUF);
    		RI=0;
    	}
    }
    

    五、数据集显示模式

    HEX模式/十六进制模式/二进制模式:以原始数据的形式显示

    文本模式/字符模式:以原始数据编码后的形式显示

    作者:茴香豆的茴1

    物联沃分享整理
    物联沃-IOTWORD物联网 » 51单片机串口通信教程【8】

    发表回复