第17章 串口通信实验

第十七章 串口通信实验

1. 导入

在前面章节我们就说过, 学习 51 单片机重点内容主要有中断、定时/计数器、 串口通信。 这一章我们就来学习 51 单片机的串口通信。 开发板上集成了 1 个串口通信电路, 是 USB 转串口模块, 它既可下载程序也可实现串口通信功能。

本章要实现的功能是: 51 单片机通过串口( UART) 实现与 PC 机对话, 51 单片机的串口收到 PC 机发来的数据后原封不动的返回给 PC 机显示。

2. 通信的基本概念

  • 串行通信

串行通信是指使用一条数据线, 将数据一位一位地依次传输, 每一位数据占据一个固定的时间长度。 其只需要少数几条线就可以在系统间交换信息, 特别适用于计算机与计算机、 计算机与外设之间的远距离通信。 如下图所示:

屏幕截图 2024 06 09 185109

串行通信的特点: 传输线少, 长距离传送时成本低, 且可以利用电话网等现成的设备, 但数据的传送控制比并行通信复杂。

  • 并行通信

并行通信通常是将数据字节的各位用多条数据线同时进行传送, 通常是 8位、 16 位、 32 位等数据一起传输。 如下图所示:

屏幕截图 2024 06 09 185347

并行通信的特点: 控制简单、 传输速度快; 由于传输线较多, 长距离传送时成本高且接收方的各位同时接收存在困难, 抗干扰能力差。

关于更多串行/并行通信可以参考:串口、串行通信与并行通信的主要区别在哪? - 21ic电子网

  • 异步通信

异步通信是指通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程。 为使双方的收发协调, 要求发送和接收设备的时钟尽可能一致。
    异步通信是以字符( 构成的帧) 为单位进行传输, 字符与字符之间的间隙( 时间间隔) 是任意的, 但每个字符中的各位是以固定的时间传送的, 即字符之间不一定有“ 位间隔” 的整数倍的关系, 但同一字符内的各位之间的距离均为“ 位间隔” 的整数倍。 如下图所示:

屏幕截图 2024 06 10 112120

异步通信的特点: 不要求收发双方时钟的严格一致, 实现容易, 设备开销较小, 但每个字符要附加 2~3 位用于起止位, 各帧之间还有间隔, 因此传输效率不高。

  • 同步通信

同步通信时要建立发送方时钟对接收方时钟的直接控制, 使双方达到完全同步。 此时, 传输数据的位之间的距离均为“ 位间隔” 的整数倍, 同时传送的字符间不留间隙, 即保持位同步关系, 也保持字符同步关系。 发送方对接收方的同步可以通过两种方法实现。 如下图所示:

屏幕截图 2024 06 10 112235

更多异步/同步通信信息参考:浅析同步通信与异步通信_为什么说异步传输是为了实现同步-CSDN博客

  • 通信速率

衡量通信性能的一个非常重要的参数就是通信速率, 通常以比特率来表示。 比特率是每秒钟传输二进制代码的位数, 单位是: 位/ 秒( bps) 。 如每秒钟传送 240 个字符, 而每个字符格式包含 10 位(1 个起始位、 1 个停止位、8 个数据位), 这时的比特率为:10 位× 240 个/秒 = 2400 bps

我们在后面会遇到一个“ 波特率” 的概念, 它表示每秒钟传输了多少个码元。 而码元是通信信号调制的概念, 通信中常用时间间隔相同的符号来表示一个二进制数字, 这样的信号称为码元。

如常见的通信传输中, 用 0V 表示数字 0, 5V 表示数字 1, 那么一个码元可以表示两种状态 0 和 1, 所以一个码元等于一个二进制比特位, 此时波特率的大小与比特率一致; 如果在通信传输中, 有 0V、 2V、4V 以及 6V 分别表示二进制数 00、 01、 10、 11, 那么每个码元可以表示四种状态, 即两个二进制比特位, 所以码元数是二进制比特位数的一半, 这个时候的波特率为比特率的一半。

由于很多常见的通信中一个码元都是表示两种状态,所以我们常常直接以波特率来表示比特率。

参考:详谈数据通信的传输速率分类和原理 - 通信网络 - 电子发烧友网 (elecfans.com)

3. 单片机串口介绍

串口通信:是指外设和计算机间通过数据信号线、地线等按位进行传输数据的一种通信方式, 属于串行通信方式。 串口是一种接口标准, 它规定了接口的电气标准, 没有规定接口插件电缆以及使用的协议。

有许多不那么重要的知识可以参考下面:

3.1 串口工作方式

  • 方式0

方式 0 时, 串行口为同步移位寄存器的输入输出方式。 主要用于扩展并行输入或输出口。 数据由 RXD( P3.0) 引脚输入或输出, 同步移位脉冲由 TXD( P3.1)引脚输出。 发送和接收均为 8 位数据, 低位在先, 高位在后。 波特率固定为fosc/12。 对应的输入输出时序图如下所示(需要有基础的数电知识才能看懂):

方式0输出:

屏幕截图 2024 06 10 113733

方式0输入:

屏幕截图 2024 06 10 113812

  • 方式1

方式 1 是 10 位数据的异步通信口。 TXD 为数据发送引脚, RXD 为数据接收引脚, 传送一帧数据的格式如下所示。 其中 1 位起始位, 8 位数据位, 1 位停止位。

屏幕截图 2024 06 10 114015

方式1输出:

屏幕截图 2024 06 10 114104

方式1输入:

屏幕截图 2024 06 10 114121

用软件置 REN 为 1 时, 接收器以所选择波特率的 16 倍速率采样 RXD 引脚电平, 检测到 RXD 引脚输入电平发生负跳变时, 则说明起始位有效, 将其移入输入移位寄存器, 并开始接收这一帧信息的其余位。

接收过程中, 数据从输入移位寄存器右边移入, 起始位移至输入移位寄存器最左边时, 控制电路进行最后一次移位。 当 RI=0, 且 SM2=0( 或接收到的停止位为 1) 时, 将接收到的 9 位数据的前8 位数据装入接收 SBUF, 第 9 位( 停止位) 进入 RB8, 并置 RI=1, 向 CPU 请求中断。

  • 方式2和方式3

方式 2 或方式 3 时为 11 位数据的异步通信口。 TXD 为数据发送引脚, RXD 为数据接收引脚。 其数据格式如下所示:

屏幕截图 2024 06 10 114239

方式2、3输出:

屏幕截图 2024 06 10 114331

发送开始时, 先把起始位 0 输出到 TXD 引脚, 然后发送移位寄存器的输出位( D0) 到 TXD 引脚。 每一个移位脉冲都使输出移位寄存器的各位右移一位, 并由TXD 引脚输出。 第一次移位时, 停止位“ 1” 移入输出移位寄存器的第 9 位上,以后每次移位, 左边都移入 0。 当停止位移至输出位时, 左边其余位全为 0, 检测电路检测到这一条件时, 使控制电路进行最后一次移位, 并置 TI=1, 向 CPU请求中断。

发生2、3输入:

屏幕截图 2024 06 10 114409

接收时, 数据从右边移入输入移位寄存器, 在起始位 0 移到最左边时, 控制电路进行最后一次移位。 当 RI=0, 且 SM2=0( 或接收到的第 9 位数据为 1) 时,接收到的数据装入接收缓冲器 SBUF 和 RB8( 接收数据的第 9 位) , 置 RI=1, 向CPU 请求中断。 如果条件不满足, 则数据丢失, 且不置位 RI, 继续搜索 RXD 引脚的负跳变。

更多可以参考:51单片机 | 串口工作原理知识点汇总 - hugh.dong - 博客园 (cnblogs.com)

3.2 串口相关寄存器

  • 串口控制寄存器SCON

屏幕截图 2024 06 10 113301

SM0 和 SM1 为工作方式选择位:

屏幕截图 2024 06 10 113319

SM2: 多机通信控制位, 主要用于方式 2 和方式 3。 当 SM2=1 时可以利用收到的 RB8 来控制是否激活 RI( RB8= 0 时不激活 RI, 收到的信息丢弃; RB8= 1 时收到的数据进入 SBUF, 并激活 RI, 进而在中断服务中将数据从 SBUF 读走) 。 当SM2=0 时, 不论收到的 RB8 为 0 和 1, 均可以使收到的数据进入 SBUF, 并激活 RI ( 即此时 RB8 不具有控制 RI 激活的功能) 。 通过控制 SM2, 可以实现多机通信。

REN: 允许串行接收位。 由软件置 REN=1, 则启动串行口接收数据; 若软件置REN=0, 则禁止接收。

TB8: 在方式 2 或方式 3 中, 是发送数据的第 9 位, 可以用软件规定其作用。可以用作数据的奇偶校验位, 或在多机通信中, 作为地址帧/数据帧的标志位。在方式 0 和方式 1 中, 该位未用到。

RB8: 在方式 2 或方式 3 中, 是接收到数据的第 9 位, 作为奇偶校验位或地址帧/数据帧的标志位。 在方式 1 时, 若 SM2=0, 则 RB8 是接收到的停止位。

TI: 发送中断标志位。 在方式 0 时, 当串行发送第 8 位数据结束时, 或在其它方式, 串行发送停止位的开始时, 由内部硬件使 TI 置 1, 向 CPU 发中断申请。在中断服务程序中, 必须用软件将其清 0, 取消此中断申请。

RI: 接收中断标志位。 在方式 0 时, 当串行接收第 8 位数据结束时, 或在其它方式, 串行接收停止位的中间时, 由内部硬件使 RI 置 1, 向 CPU 发中断申请。也必须在中断服务程序中, 用软件将其清 0, 取消此中断申请。

  • 电源控制寄存器PCON

屏幕截图 2024 06 10 113533

SMOD: 波特率倍增位。 在串口方式 1、 方式 2、 方式 3 时, 波特率与 SMOD 有关, 当 SMOD=1 时, 波特率提高一倍。 复位时, SMOD=0。

更多可参考:关于51单片机串口通信的相关知识(寄存器)_51单片机串口寄存器-CSDN博客

3.3 如何计算波特率

在学习 51 单片机串口时, 非常重要的一点是学会如何计算波特率。 以下列出了几种方式下波特率的计算公式:

屏幕截图 2024 06 10 152143

当然除了手算。我们还可以使用工具自动计算波特率,下面介绍一下怎么使用(教程里用到的工具都在开发软件这个文件夹里):

  • 双击打开该工具, 其界面如下:

屏幕截图 2024 06 10 152726

  • 选择定时器工作方式, 输入开发板上使用的晶振频率大小, 选择所要使用的波特率, SMOD 为是否倍频, 这个在前面介绍寄存器时说过, 下面的误差大小可以反映出通信时是否出现乱码。 在使用串口通信时, 定时器 1 工作方式为 2,串口工作方式为 1, 以开发板晶振是 11.0592Mh 为例, 假如晶振频率是 12M, 那么在生成的波特率就会有误差而导致通信出错。 为什么替换可以从误差值反映出来。 在本章实验中波特率选择 9600, 使用 SMOD, 即值为 1, 点击确定后即会自动生成定时/计数器 THx 的值, 如下所示:

屏幕截图 2024 06 10 160651

从上图可知, 使用 11.0592M 晶振时, 误差为 0。 我们对比下当外部晶振使用12Mh 时, 波特率误差多大, 如下所示:

屏幕截图 2024 06 10 160807

上图可知, 当使用 12M 晶振时, 波特率误差有 6.98%, 是比较大的, 会导致在通信过程中出现乱码等错误信息。 这是我们不希望看到的, 所以再次说明下,在做串口通信实验时, 一定要确认外部晶振是否是 11.0592M。

3.4 串口初始化步骤

关于如何使用串口,可以按照下面几个步骤配置:

  • 确定 T1 的工作方式( TMOD 寄存器) ;

  • 确定串口工作方式( SCON 寄存器) ;

  • 计算 T1 的初值( 设定波特率) , 装载 TH1、 TL1;

  • 启动 T1( TCON 中的 TR1 位) ;

  • 如果使用中断, 需开启串口中断控制位( IE 寄存器) 。

例如: 我们要设置串口为工作方式 1、 波特率为 9600、 波特率加倍、 使用中断。

其配置程序如下:

void uart_init(unsigned int  baud)
{
   TMOD |= 0X20; // 设置计数器工作方式 2
   SCON = 0X50; // 设置为工作方式 1
   PCON = 0X80; // 波特率加倍
   TH1 = baud; // 计数器初始值设置
   TL1 = baud;

   ES = 1; // 打开接收中断
   EA = 1; // 打开总中断
   TR1 = 1; // 打开计数器
 } 

配置完成后,在主函数调用即可,我们这些想设置的波特率为9600

uart_init(0XFA); // 波特率为 9600

4. 硬件设计

开发板上板载一个 USB 转 TTL 模块和一个 RS232 模块, 这两个模块都可进行串口通信。 其硬件电路如下所示:

  • USB 转 TTL 模块

屏幕截图 2024 06 10 161612

从上图中可以看出, 通过 CH340 芯片把 51 单片机的串口与 PC 机的 USB 口进行连接, 不仅可以实现程序的烧入, 还可实现串口通信功能。 根据前面介绍,串口通信需将数据收发管脚交叉连接, 所以可以看到在 CH340 芯片的 2 和 3 脚已做处理。 电路中其他部分是自动下载电路部分, 目的是控制单片机的电源, 无需冷启动。 使用 USB 转串口芯片, 免去了一根串口线, 使用普通 USB 数据线( 支持安卓手机数据线) 就可以进行串口通信。

上图中可以看到 CH340 的 2、 3 脚串口并非直接连接到单片机串口, 而是连接在 J39 和 J44 端子上, 这样就把 CH340 的串口与单片机串口独立出来, 为什么不直接连接而要使用这个 J39 和 J44 端子呢? 这是方便用户可以使用开发板上的 USB 转 TTL 模块( 也就是 CH340 转串口模块) 做一些串口类模块的调试, 比如: WIFI、 蓝牙、 GPS、 GPRS 等, 直接利用 PC 上位机来调试模块。 同时也方便用户使用板载 USB 转 TTL 模块给其它类型单片机下载程序。

如果使用黄色跳线帽将 J39 和 J44 端子的 2、 3 短接, 那么 CH340 串口与单片机串口是连接一起的, 此时即可实现程序的下载或串口通信。 如果使用黄色跳线帽将 J39 和 J44 端子的 3、 4 短接, 那么 RS232 模块串口与单片机串口是连接一起的, 此时可通过 RS232 模块下载程序或串口通信。

  • RS232模块

屏幕截图 2024 06 10 161921

此电路是按照 RS232 接口标准搭建, 使用了一个 DB9 母头, 电平转换芯片使用的是 MAX232。 如使用该模块进行串口通信, 可使用一根 USB 转 RS232 线, 一端与 DB9 母头连接, 另一头与 PC 端 USB 口连接, 同时将 J39 和 J44 端子的 3、 4短接, 那么 RS232 模块串口与单片机串口是连接一起的, 此时可通过 RS232 模块下载程序或串口通信。 还要注意: RS232 接口是不带电源的, 因此需要给开发板额外供电。

因为开发板是双核的, 即使用了 2 个单片机, 另一个单片机同样需要串口下载, 所以在 J39 和 J44 端子上也将单片机串口引出, 通过使用黄色跳线帽将 J39和 J44 端子的 1、 2 脚短接, 此时即可对另一个单片机下载程序和串口通信。

5. 软件设计

本章所要实现的功能是: 当串口助手发送数据给单片机, 单片机原封不动转发给串口助手显示。

#include <REGX52.H>

// 配置串口通信初始化
void uart_init(unsigned int baud)
{
    TMOD |= 0X20; // 设置计数器工作方式2
    SCON = 0X50; // 设置为工作方式1
    PCON = 0X80; // 波特率加倍
    TH1 = baud; // 计数器初始值设置
    TL1 = baud;

    // 配置中断
    ES = 1; // 打开接收中断
    EA = 1; // 打开总中断
    TR1 =1 ; //打开计数器        
}


void uart() interrupt 4 //串口通信中断函数
{
    unsigned int rec_data; // 接收到的数据

    RI = 0;    // 清除接收中断标志位

    rec_data = SBUF; // 存储接收到的数据
    SBUF = rec_data; // 将接收到的数据放入到发送寄存器

    while(!TI);    // 等待发送数据完成
    TI = 0; // 清除发送完成标志位                
}

void main()
{    
    uart_init(0XFA); // 波特率为9600

    while(1)
    {            

    }        
}

实验代码比较简单,和前面中断/定时器一样,关键是配置/初始化:

首先定义了串口通信中断配置函数 uart_init, 该函数有一个入口参数 baud, 该值可改变通信波特率。 该函数的实现即是按照前面介绍的串口配置步骤。 最后进入 while 循环, 在循环体内不执行任何功能程序。 如果发生接收中断, 即会进入串口中断执行, 执行完后回到主函数内继续运行, 如此循环。

6. 小结

再来复习一下串口配置吧:

void uart_init(unsigned int baud)
{
    TMOD |= 0X20; // 设置计数器工作方式2
    SCON = 0X50; // 设置为工作方式1
    PCON = 0X80; // 波特率加倍
    TH1 = baud; // 计数器初始值设置
    TL1 = baud;

    // 配置中断
    ES = 1; // 打开接收中断
    EA = 1; // 打开总中断
    TR1 =1 ; //打开计数器        
}

  1. TMOD |= 0X20;
  • 这行代码将定时器1(Timer 1)设置为模式2(自动重装模式)。在这个模式下,定时器会自动重新加载TH1和TL1的值,从而产生稳定的计时脉冲。
  • 0x20 表示将 TMOD 寄存器的第五位(定时器1模式选择位)设置为1。此设置的结果是定时器1在模式2下运行。
  1. SCON = 0X50;
  • 这行代码配置串口控制寄存器(SCON)。0x50 的二进制形式是 01010000,表示:
    • SM0 = 0 和 SM1 = 1,设置为工作方式1(8位可变波特率)。
    • REN = 1,启用串口接收。
    • TB8 和 RB8 为0(这两个位在工作方式1下没有作用)。
    • TI = 0 和 RI = 0,初始时接收中断标志和发送中断标志均为0。
  1. PCON = 0X80;
  • PCON 寄存器的 0x80 表示波特率加倍。设置PCON的第7位(SMOD),将波特率加倍。
  1. TH1 = baud; TL1 = baud;
  • TH1 和 TL1 用于设置定时器1的初值,从而确定波特率。baud 参数应根据所需的波特率计算出来并传递给这个函数。
  1. 中断配置
  • ES = 1;:启用串口接收中断。ES 是串口中断使能位。
  • EA = 1;:启用总中断。EA 是全局中断使能位。
  • TR1 = 1;:启动定时器1。TR1 是定时器1启动位。

中断服务函数:

void uart() interrupt 4 // 串口通信中断函数
{
    unsigned int rec_data; // 接收到的数据

    RI = 0;    // 清除接收中断标志位

    rec_data = SBUF; // 存储接收到的数据
    SBUF = rec_data; // 将接收到的数据放入到发送寄存器

    while(!TI);    // 等待发送数据完成
    TI = 0; // 清除发送完成标志位                
}

  1. void uart() interrupt 4
  • 这是一个中断服务程序(ISR)。interrupt 4 表示这是中断向量表中的第4个中断源。对于大多数8051系列微控制器,串口中断通常位于第4个中断源。
  1. unsigned int rec_data;
  • 定义一个无符号整型变量 rec_data 用于存储接收到的数据。
  1. RI = 0;
  • 清除串口接收中断标志位 RI。接收中断标志位被置位表示接收了一个字节的数据,处理完毕后需要清除以允许新的接收中断触发。
  1. rec_data = SBUF;
  • 从串口接收缓冲区 SBUF 读取接收到的数据,并存储到 rec_data 变量中。SBUF 是串口数据寄存器,用于接收和发送数据。
  1. SBUF = rec_data;
  • 将接收到的数据写回到 SBUF,以便将其发送出去。这样实现了回显功能,即接收到的数据会被发送回去。
  1. while(!TI);
  • 等待串口发送完成标志位 TI 变为1。TI 表示发送中断标志位,当数据发送完成时 TI 被置位。这个循环确保数据在继续之前被完全发送。
  1. TI = 0;
  • 清除发送完成标志位 TI,以便下一次数据发送能够正常进行。

2024.7.20 第一次修订

2024.8.21 第二次修订,后期不再维护

posted @ 2024-08-21 16:31  hazy1k  阅读(52)  评论(0编辑  收藏  举报