第5章 CHAPTER 5 串 口 通 信 5.1本章导读 串口通信在实际中有着广泛的应用,很多设备的数据传输通过串口和上位机进行通信。在实际应用中,对于需要大量处理的数据,可以通过上位机处理后,通过串口发送给下位机,本章从串行通信的基本概念开始,说明STM32的串口通信过程,读者可以获取到以下信息: (1) 串口通信的基本概念,常用串行接口分类及接线方式。 (2) STM32串口操作的方式。 (3) 采用寄存器方法和库函数方法操作串口通信的详细步骤。 5.2串口通信基础 串口是计算机中的一种通用设备通信的协议,也是仪器仪表设备通用的通信协议,很多GPIB兼容的设备也带有RS232口。同时,串口通信协议也可以用来获取远程采集设备的数据。 5.2.1基本概念 1. 计算机通信方式 并行通信与串行通信是计算机常用的两种通信方式。并行通信指数据的各位同时进行传送(发送或接收)的通信方式。其优点是通信速度快; 缺点是设备之间的数据线多,通信距离短。例如打印机与计算机之间的通信一般都采用并行通信方式。串行通信指数据是一位一位按顺序传送的通信方式。虽然通信速率低,但实现的方法及连线简单。 串行通信有同步和异步两种方式,同步通信时相互通信的设备之间需要时钟同步,必须有同步信号,实现复杂。异步通信时相互通信的设备之间不需要同步,只要求通信的接口方式及速率相同,以起始位和停止位为标志表示数据发送的开始和结束。监控系统中常采用串行异步通信方式实现智能设备或采集器与监控主机(前置机)之间的通信。 2. 通信协议 异步通信时数据一帧一帧地传送,帧的格式和通信速率一起称为通信协议。帧的格式由起始位、数据位、奇偶校验位和停止位组成,如图51所示。起始位都只有一位; 数据位长为1~8位; 校验位只有一位或没有,常用的校验方式为3种,偶校验记为e,奇校验记为o,无校验位记为n; 停止位为1位或2位。一个数据帧的长度称为字长,字长=起始位+数据位+校验位+停止位。 图51一个数据帧 波特率用于描述串行通信的速率,一般单位为“位/秒”,记为b/s。常用的异步串行通信波特率有1200b/s、2400b/s、4800b/s、9600b/s、19200b/s等。 在监控系统中,为了指明两台设备之间的通信协议,需要对通信端口进行设置,端口设置的格式为“波特率,校验位,数据位位数,停止位位数”。例如,某一个串口的端口设置为“9600,n,8,1”,表示该串口的通信速率为9600b/s,没有校验位,数据位的长度为8,停止位为1位,字长为10。又如“2400,e,7,1”,由通信协议的定义易知,字长也为10。 设置具体的通信协议时,常遇到“流控制”这一概念,设置了流控制,设备串口的通信速率可以自动调整,不致发生数据的溢出或丢失。流控制一般有两种可选的方式,“硬件流控制”指用串口的两个引脚之间的电压差做流控信号,需要硬件设备的支持,监控系统中遇到的采集器和智能设备一般不支持硬件流控制; “软件流控制”指用两个特殊的ASCII字符Xon和Xoff做流控信号,由于监控系统中涉及的设备之间传输时大多为二进制数据,里面很有可能刚好含有字符Xon和Xoff,为了不至于引起设备的误解而导致传输错误,不能使用软件流控制。因此在设置通信串口时,一般不设置流控制,即选择“无流控制信号”。 5.2.2常用的串行通信接口 串行通信有多种接口方式,监控系统中常用的有RS232、RS422、RS485三种接口方式,下面分别对它们进行简要的介绍。 1. RS232串行通信接口 RS232是RS232C接口的简称,RS232C是一种广泛使用的串行通信标准接口,例如计算机上的串行接口(简称串口)COM1、COM2。 1) RS232定义 RS232的机械接口有DB9、DB25两种形式,均有公头(针)、母头(孔)之分,常用的DB9接口外形及针脚序号如图52所示。DB9及DB25两种串行接口的引脚信号定义如表51所示。 图52RS232常用接口 表51RS232接口中DB9、DB25引脚信号定义 9针 25针 信号名称 信号流向 简称 信号功能 3 2 发送数据 DTE→DCE TxD DTE发送串行数据 2 3 接收数据 DTE←DCE RxD DTE接收串行数据 7 4 请求发送 DTE→DCE RTS DTE请求切换到发送方式 8 5 清除发送 DTE←DCE CTS DCE已切换到准备接收 6 6 数据设备就绪 DTE←DCE DSR DCE准备就绪可以接收 5 7 信号地 GND 公共信号地 1 8 载波检测 DTE←DCE DCD DCE已接收到远程载波 4 20 数据终端就绪 DTE→DCE DTR DTE准备就绪可以接收 9 22 振铃指示 DTE←DCE RI 通知DTE,通信线路已接通 设备分为两种: 一种是数据终端设备,简称为DTE,例如计算机、采集器、智能设备等; 另一种是数据电路设备(通信设备),简称DCE,例如调制解调器、数据端接设备(DTU)、数据服务单元/通道服务单元(DCU/DSU)等。对于大多数设备,通常只用到TxD、RxD、GND三个针脚。注意表51中的信号流向,对于DTE,Txd是DTE向对方发送数据; 而对于DCE,TxD是对方向自己发送数据。RS232电气标准中采用负逻辑,逻辑“1”电平为-3~-15V,逻辑“0”电平为+3~+15V,可以通过测量DTE的TxD(或DCE的RxD)和GND之间的电压了解串口的状态,空载状态下,它们之间应有-10V左右(-5~-15V)的电压,否则该串口可能已损坏或驱动能力弱。 按照RS232标准,传输速率一般不超过20kb/s,传输距离一般不超过15m。实际使用时,传输速率最高可达115200b/s。 2) RS232串行接口基本接线原则 设备之间的串行通信接线方法,取决于设备接口的定义。设备间采用RS232串行电缆连接时有两类连接方式。 (1) 直通线: 相同信号(RxD对RxD、TxD对TxD)相连,用于DTE(数据终端设备)与DCE(数据通信设备)相连。例如计算机与MODEM(或DTU)相连。 (2) 交叉线: 不同信号(RxD对TxD、TxD对RxD)相连,用于DTE与DTE相连。例如计算机与计算机、计算机与采集器之间相连。 以上两种连接方法可以认为同种设备相连采用交叉线连接,不同种设备相连采用直通线连接。少数情况下会出现两台具有DCE接口的设备需要串行通信的情况,此时也用交叉方式连接。当一台设备本身是DTE,但它的串行接口按DCE接口定义时,应按DCE接线。例如艾默生网络能源有限公司生产的一体化采集器IDA采集模块上的调测接口是按DCE接口定义的,当计算机与IDA采集模块的调测口连接时,就要采用直通串行电缆。 一般,RS232接口若为公头,则该接口按DTE接口定义; 若为母头,则该接口按DCE接口定义。但也有反例,不能一概而论。一些DTE设备上的串行接口按DCE接口定义,采用DB9或DB25母接口,主要因为DTE接口一般都采用公头,当用手接触时易接触到针脚; 采用母头时因不易碰到针脚,可避免人体静电对设备的影响。 对于某些设备上的非标准RS232接口,需要根据设备的说明书确定针脚的定义。如果已知TxD、RxD和GND三个针脚,但不清楚哪一个针脚是TxD,哪一个针脚是RxD,可以通过万用表测量它们与GND之间的电压来判别,如果有一个电压为-10V左右,则万用表红表笔所接的是DTE的TxD或DCE的RxD。 3) RS232的3种接线方式 (1) 三线方式: 两端设备的串口只连接收、发、地三根线。一般情况下,三线方式即可满足要求,例如监控主机与采集器及大部分智能设备之间相连。 (2) 简易接口方式: 两端设备的串口除了连接收、发、地三根线外,另外增加一对握手信号(一般是DSR和DTR)。具体需要哪对握手信号,需查阅设备接口说明。 (3) 完全口线方式: 两端设备的串口9线全接,例如Modem电缆(计算机与外置Modem的连接电缆)。 此外,有些设备虽然需要握手信号,但并不需要真正的握手信号,可以采用自握手的方式,连接方法如图53所示。 图53RS232自握手的接线方式 2. RS422串行通信接口 RS422接口的定义很复杂,一般只使用4个端子,其针脚定义分别为TX+、TX-、RX+、RX-,其中TX+和TX-为一对数据发送端子,RX+和RX-为一对数据接收端子,如图54所示。RS422采用了平衡差分电路,差分电路可在受干扰的线路上拾取有效信号,由于差分接收器可以分辨0.2V以上的电位差,因此可大大减弱地线干扰和电磁干扰的影响,有利于抑制共模干扰,传输距离可达1200m。 图54RS422方式通信接口定义与接线 和RS232不同的是,在RS422总线上可以挂接多台设备组网,总线上连接的设备RS422串行接口同名端相接,与上位机则收发交叉,可以实现点到多点的通信,如图55所示。RS232只能点到点通信,不能组成串行总线。 图55RS422总线组网示意图 通过RS422总线与计算机某一串口通信时,要求各设备的通信协议相同。为了在总线上区分各设备,各设备需要设置不同的地址。上位机发送的数据,所有的设备都能接收到,但只有地址符合上位机要求的设备响应。 3. RS485串行通信接口 RS485是RS422的子集,只需要DATA+(D+)、DATA-(D-)两根线。RS485与RS422的不同之处在于RS422为全双工结构,可以在接收数据的同时发送数据; 而RS485为半双工结构,在同一时刻只能接收或发送数据,如图56所示。 图56RS485通信接口定义与接线 RS485总线上也可以挂接多台设备,用于组网,实现点到多点及多点到多点的通信(多点到多点指总线上接的所有设备及上位机任意两台之间均能通信),如图57所示。 图57RS485方式组网 连接在RS485总线上的设备也要求具有相同的通信协议,且地址不能相同。不通信时,所有的设备处于接收状态,当需要发送数据时,串口才翻转为发送状态,以避免冲突。为了抑制干扰,RS485总线常在最后一台设备之后接入一个120Ω的电阻。 很多设备同时有RS485接口方式和RS422接口方式,常共用一个物理接口,如图58所示。图中,RS485的D+和D-与RS422的T+和T-共用。 图58RS422/485共用接口 4. 3种串行通信接口方式比较 RS232、RS422、RS485串行通信接口性能比较,如表52所示。 表52RS232、RS422、RS485接口性能比较 接口性能 RS232 RS422 RS485 操作方式 电平 差分 差分 最大传输速率 20kb/s(15m) 10Mb/s(12m) 1Mb/s(120m) 100kb/s(1200m) 10Mb/s(12m) 1Mb/s(120m) 100kb/s(1200m) 驱动器输出电压 无负载 ±5~±15V ±5V ±5V 有负载时 ±2V ±1.5V 驱动器负载阻抗 3~7kΩ 100Ω(min) 54Ω(min) 接收输入阻抗 3~7kΩ 4kΩ 12kΩ 接收器灵敏度 ±3V ±200mV ±200mV 工作方式 全双工 全双工 半双工 连接方式 点到点 点到多点 多点到多点 5.3STM32串口操作 STM32的串口资源相当丰富,功能也很强大。STM32Fxxx一般可提供5路串口,有分数波特率发生器、支持同步单线通信和半双工单线通信、支持LIN、支持调制解调器操作、智能卡协议和IrDA SIR ENDEC规范、具有DMA等。 下面从寄存器层面设置串口,以达到基本的通信功能。本节将实现利用串口1不停地打印信息到计算机,同时接收从串口发过来的数据,把发送过来的数据直接送回给计算机。本实验使用的开发板有1个USB串口,通过USB串口和计算机通信。 串口最基本的设置,就是波特率的设置。STM32的串口使用简单、方便。只要开 启了串口时钟,并设置相应I/O端口的模式,然后配置波特率、数据位长度、奇偶校验位等信息,就可以使用。下面简单介绍这几个与串口基本配置直接相关的寄存器。 5.3.1寄存器方式操作串口 1. 串口时钟使能 串口作为STM32的一个外设,其时钟由外设时钟使能寄存器控制,这里使用的串口1是在APB2ENR寄存器的第14位。除了串口1的时钟使能在APB2ENR寄存器,其他串口的时钟使能都在APB1ENR寄存器。 2. 串口复位 当外设出现异常时,可以通过复位寄存器里面的对应位设置,实现该外设的复位,然后重新配置这个外设达到让其重新工作的目的。一般在系统刚开始配置外设的时候,都会先执行复位该外设的操作。串口1的复位通过配置APB2RSTR寄存器的第14位来实现。APB2RSTR寄存器结构如图59所示。 图59APB2RSTR寄存器结构 从图59可知串口1的复位设置位在APB2RSTR的第14位。通过向该位写1复位串口1,写0结束复位。其他串口的复位在APB1RSTR里面。 3. 串口波特率设置 每个串口都有一个独立的波特率寄存器USART_BRR,通过设置该寄存器可以达到配置不同波特率的目的。其各位描述如图510和表53所示。 图510USART_BRR寄存器结构 表53寄存器USART_BRR各位描述 位描述 位31:16 保留位,硬件强制为0 位15:4 DIV_Mantissa[11:0] USARTDIV的整数部分,这12位定义了USART分频器除法因子(USARTDIV)的整数部分 位3:0 DIV_Fraction[3:0] USARTDIV的小数部分,这4位定义了USART分频器除法因子(USARTDIV)的小数部分 前面提到STM32的分数波特率概念,其实就在这个寄存器(USART_BRR)里面体现。USART_BRR的最低4位(位[3:0])用来存放小数部分DIV_Fraction,紧接着的12位(位[15:4])用来存放整数部分DIV_Mantissa,最高16位未使用。STM32的串口波特率计算公式如下: Tx/Rx波特率=fPCLKx16×USARTDIV 式中,fPCLKx是给串口的时钟(PCLK1用于USART2、3、4、5,PCLK2用于USART1); USARTDIV是一个无符号定点数。只要得到USARTDIV的值,就可以得到串口波特率寄存器USART1→BRR的值; 反之,得到USART1→BRR的值,也可以推导出USARTDIV的值。但我们更关心的是如何从USARTDIV的值得到USART_BRR的值,因为一般知道的是波特率和PCLKx的时钟,要求的是USART_BRR的值。 下面介绍如何通过USARTDIV得到串口USART_BRR寄存器的值。假设串口1要设置为9600的波特率,而PCLK2的时钟为72MHz。这样,根据公式有 USARTDIV=72000000/(9600×16)=468.75 得到 DIV_Fraction=16×0.75=12=0X0C DIV_Mantissa=468=0X1D4 这样,就得到了USART1→BRR的值为0X1D4C。只要设置串口1的BRR寄存器值为0X1D4C,就可以得到9600的波特率。当然,并不是任何条件下都可以随便设置串口波特率,在某些波特率和PCLK2频率下,还是会存在误差,设置波特率时的误差计算如表54所示。 表54设置波特率时的误差计算 波特率 fPCLK=36MHz fPCLK=72MHz 序号 kb/s 实际 置于波特率 寄存器中的值 误差% 实际 置于波特率 寄存器中的值 误差% 1 2.4 2.400 937.5 0 2.4 1875 0 2 9.6 9.600 234.375 0 9.6 468.75 0 3 19.2 19.2 117.1875 0 19.2 234.375 0 4 57.6 57.6 39.0625 0 57.6 78.125 0 5 115.2 115.384 19.5 0.15 115.2 39.0625 0 6 230.4 230.769 9.75 0.16 230.769 19.5 0.16 7 460.8 461.538 4.875 0.16 461.538 9.75 0.16 8 921.6 923.076 2.4375 0.16 923.076 4.875 0.16 9 2250 2250 1 0 2250 2 0 10 4500 不可能 不可能 不可能 4500 1 0 4. 串口控制 STM32的每个串口都有3个控制寄存器USART_CR1~3,串口的很多配置都是通过这3个寄存器来设置。这里只要用到USART_CR1就可以实现功能了,该寄存器的各位描述如图511和表55所示。 图511USART_CR寄存器结构 表55USART_CR寄存器各位描述 位描述 位31:14 保留位,硬件强制为0 位13 UE USART使能,当该位被清“0”,在当前字节传输完成后,USART的分频器和输出停止工作,以减少功耗。该位由软件设置和清“0” 0: USART分频器和输出被禁止 1: USART模块使能 续表 位描述 位12 M 字长,该位定义了数据字的长度,由软件对其设置和清“0” 0: 1个起始位,8个数据位,n个停止位 1: 1个起始位,9个数据位,n个停止位 注意: 在数据传输过程中(发送或者接收时),不能修改这个位 位11 WAKE 唤醒的方法,该位决定了把USART唤醒,由软件对该位设置和清“0” 0: 被空闲总线唤醒 1: 被地址标记唤醒 位10 PCE 检验控制使能,用该位选择是否进行硬件校验控制(对于发送就是校验位的产生; 对于接收就是校验位的检测)。当使能了该位,在发送数据的最高位(如果M=1,最高位就是第9位; 如果M=0,最高位就是第8位)插入校验位; 对接收到的数据检查其校验位。软件对它置“1”或清“0”。一旦设置了该位,当前字节传输完成后,校验控制才生效 0: 禁止校验控制 1: 使能校验控制 位9 PS 校验选择,当校验控制使能后,该位用来选择是采用偶校验还是奇校验。软件对它置“1”或清“0”。当前字节传输完成后,该选择生效 0: 偶校验 1: 奇校验 位8 PEIE PE中断使能,该位由软件设置或清除 0: 禁止产生中断 1: 当USART_SR中的PE为“1”时,产生USART中断 位7 TXEIE 发送缓冲区空中断使能,该位由软件设置或清除 0: 禁止产生中断 1: 当USART_SR中的TXE为“1”时,产生USART中断 位6 TCIE 发送完成中断使能,该位由软件设置或清除 0: 禁止产生中断 1: 当USART_SR中的TC为“1”时,产生USART中断 位5 RXNEIE 接收缓冲区非空中断使能,该位由软件设置或清除 0: 禁止产生中断 1: 当USART_SR中的ORE或者RXNE为“1”时,产生USART中断 位4 IDLEIE IDLE中断使能,该位由软件设置或清除 0: 禁止产生中断 1: 当USART_SR中的IDLE为“1”时,产生USART中断 位3 TE 发送使能,该位使能发送器。该位由软件设置或清除 0: 禁止发送 1: 使能发送 注意: (1) 数据传输过程中,除了在智能卡模式下,如果TE位上有个0脉冲(即设置为“0”之后再设置为“1”),会在当前数据字传输完成后,发送一个“前导符”(空闲总线) (2) 当TE设置后,在真正发送开始之前,有一个比特时间的延迟 位2 RE 接收使能,该位由软件设置或清除 0: 禁止接收 1: 使能接收,并开始搜寻RX引脚上的起始位 续表 位描述 位1 RWU 接收唤醒,该位用来决定是否把USART置于静默模式。该位由软件设置或清除。当唤醒序列到来时,硬件也会将其清“0” 0: 接收器处于正常工作模式 1: 接收器处于静默模式 注意: (1) 把USART置于静默模式(设置RWU位)之前,USART要已经先接收了一个数据字节; 否则在静默模式下,不能被空闲总线检测唤醒 (2) 当配置成地址标记检测唤醒(WAKE位=1),RXNE位被置位时,不能用软件修改RWU位 位0 SBK 发送断开帧,使用该位来发送断开字符。该位可以由软件设置或清除。操作过程应该是软件设置位,然后在断开帧的停止位时,由硬件将该位复位 0: 没有发送断开字符 1: 将要发送断开字符 5. 数据发送与接收 STM32的发送与接收通过数据寄存器USART_DR来实现,这是一个双寄存器,包含了TDR和RDR。当向该寄存器写数据时,串口就会自动发送,当收到收据时,也存在该寄存器内。该寄存器的结构如图512所示。 图512USART_DR寄存器结构 可以看出,虽然是一个32位寄存器,但是只用了低9位(DR[8:0]),其他都保留。 DR[8:0]为串口数据,包含了发送或接收的数据。由于它是由两个寄存器组成的,一个给发送用(TDR),一个给接收用(RDR),该寄存器兼具读和写的功能。TDR寄存器提供了内部总线和输出移位寄存器之间的并行接口。RDR寄存器提供了输入移位寄存器和内部总线之间的并行接口。 当使能校验位(USART_CR1种PCE位被置位)进行发送时,写到MSB的值(根据数据的长度不同,MSB是第7位或者第8位)会被后来的校验位取代。当使能校验位进行接收时,读到的MSB位是接收到的校验位。 6. 串口状态 串口的状态可以通过状态寄存器USART_SR读取。USART_SR的结构如图513所示。 图513USART_SR寄存器结构 这里关注两个位,第5、6位RXNE和TC。 RXNE(读数据寄存器非空): 当该位被置1的时候,就是提示已经有数据被接收到,并且可以读出来了。这时候要尽快去读取USART_DR,通过读USART_DR可以将该位清“0”,也可以向该位写0,直接清除。 TC(发送完成): 当该位被置位的时候,表示USART_DR内的数据已经被发送完成。如果设置了这个位的中断,则会产生中断。该位也有两种清“0”方式: ①读USART_SR,写USART_DR; ②直接向该位写0。 通过以上一些寄存器的操作外加I/O端口的配置,就可以达到串口最基本的配置。 5.3.2库函数方式操作串口 通过以上寄存器的介绍,了解了STM32的USART寄存器模式的相关设置,接下来学习库函数操作USART。表56给出了操作USART的库函数列表,重点介绍几个常用的函数。 表56操作USART库的函数 函数名 描述 USART_DeInit 将外设USARTx寄存器重设为缺省值 USART_Init 根据USART_InitStruct中指定的参数初始化外设USARTx寄存器 USART_StructInit 把USART_InitStruct中的每一个参数按缺省值填入 USART_Cmd 使能或者失能USART外设 USART_ITConfig 使能或者失能指定的USART中断 USART_DMACmd 使能或者失能指定USART的DMA请求 USART_SetAddress 设置USART节点的地址 USART_WakeUpConfig 选择USART的唤醒方式 USART_ReceiverWakeUpCmd 检查USART是否处于静默模式 USART_LINBreakDetectLengthConfig 设置USART LIN中断检测长度 USART_LINCmd 使能或者失能USARTx的LIN模式 USART_SendData 通过外设USARTx发送单个数据 USART_ReceiveData 返回USARTx接收到的数据 USART_SendBreak 发送中断字 USART_SetGuardTime 设置指定的USART保护时间 USART_SetPrescaler 设置USART时钟预分频 USART_SmartCardCmd 使能或者失能指定USART的智能卡模式 USART_SmartCardNackCmd 使能或者失能NACK传输 USART_HalfDuplexCmd 使能或者失能USART半双工模式 USART_IrDAConfig 设置USART IrDA模式 续表 函数名 描述 USART_IrDACmd 使能或者失能USART IrDA模式 USART_GetFlagStatus 检查指定的USART标志位设置与否 USART_ClearFlag 清除USARTx的待处理标志位 USART_GetITStatus 检查指定的USART中断发生与否 USART_ClearITPendingBit 清除USARTx的中断待处理位 1. USART_Init函数 初始化外设USART的函数为USART_Init,具体的含义如表57所示。 表57USART_Init函数 函数名 USART_Init 函数原形 void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct) 功能描述 根据USART_InitStruct中指定的参数初始化外设USARTx寄存器 输入参数1 USARTx: 选择USART外设,x可以是1,2或者3 输入参数2 USART_InitStruct: 指向结构USART_InitTypeDef的指针,包含了外设USART的配置信息。参阅Section: USART_InitTypeDef查阅更多该参数允许的取值范围 输出参数 无 返回值 无 先决条件 无 被调用函数 无 USART_InitTypeDef的结构体定义于文件stm32f10x_usart.h中: typedef struct { u32 USART_BaudRate; u16 USART_WordLength; u16 USART_StopBits; u16 USART_Parity; u16 USART_HardwareFlowControl; u16 USART_Mode; u16 USART_Clock; u16 USART_CPOL; u16 USART_CPHA; u16 USART_LastBit; } USART_InitTypeDef; 表58描述了结构USART_InitTypeDef在同步和异步模式下使用的不同成员。 表58USART_InitTypeDef成员USART模式对比 成员 异 步 模 式 同 步 模 式 USART_BaudRate X X USART_WordLength X X 续表 成员 异 步 模 式 同 步 模 式 USART_StopBits X X USART_Parity X X USART_HardwareFlowControl X X USART_Mode X X USART_Clock X USART_CPOL X USART_CPHA X USART_LastBit X (1) USART_BaudRate: 该成员设置了USART传输的波特率。 (2) USART_WordLength: 表示在一个帧中传输或者接收到的数据位数。表59给出了该参数可取的值。 表59USART_WordLength定义 USART_WordLength 描述 USART_WordLength_8b 8位数据 USART_WordLength_9b 9位数据 (3) USART_StopBits: 定义了发送的停止位数目。表510给出了该参数可取的值。 表510USART_StopBits定义 USART_StopBits 描述 USART_StopBits_1 在帧结尾传输1个停止位 USART_StopBits_0.5 在帧结尾传输0.5个停止位 USART_StopBits_2 在帧结尾传输2个停止位 USART_StopBits_1.5 在帧结尾传输1.5个停止位 (4) USART_Parity: 定义了奇偶模式。表511给出了该参数可取的值。 表511USART_Parity定义 USART_Parity 描述 USART_Parity_No 奇偶失能 USART_Parity_Even 偶模式 USART_Parity_Odd 奇模式 注意 奇偶校验一旦使能,在发送数据的MSB位插入经计算的奇偶位(字长9位时的第9位,字长8位时的第8位)。 (5) USART_HardwareFlowControl: 指定了硬件流控制模式使能还是失能。表512给出了该参数可取的值。 表512USART_HardwareFlowControl定义 USART_HardwareFlowControl 描述 USART_HardwareFlowControl_None 硬件流控制失能 USART_HardwareFlowControl_RTS 发送请求RTS使能 USART_HardwareFlowControl_CTS 清除发送CTS使能 USART_HardwareFlowControl_RTS_CTS RTS和CTS使能 (6) USART_Mode: 指定了使能或者失能发送和接收模式。表513给出了该参数可取的值。 表513USART_Mode定义 USART_Mode 描述 USART_Mode_Tx 发送使能 USART_Mode_Rx 接收使能 (7) USART_CLOCK: 表示USART时钟使能还是失能。表514给出了该参数可取的值。 表514USART_CLOCK定义 USART_CLOCK 描述 USART_Clock_Enable 时钟高电平活动 USART_Clock_Disable 时钟低电平活动 (8) USART_CPOL: 表示SLCK引脚上时钟输出的极性。表515给出了该参数可取的值。 表515USART_CPOL定义 USART_CPOL 描述 USART_CPOL_High 时钟高电平 USART_CPOL_Low 时钟低电平 (9) USART_CPHA: 表示SLCK引脚上时钟输出的相位,和CPOL位一起配合产生用户希望的时钟/数据的采样关系。表516给出了该参数可取的值。 表516USART_CPHA定义 USART_CPHA 描述 USART_CPHA_1Edge 时钟第一个边沿进行数据捕获 USART_CPHA_2Edge 时钟第二个边沿进行数据捕获 (10) USART_LastBit: 控制是否在同步模式下,在SCLK引脚上输出后发送的那个数据字(MSB)对应的时钟脉冲。表517给出了该参数可取的值。 表517USART_LastBit定义 USART_LastBit 描述 USART_LastBit_Disable 后一位数据的时钟脉冲不从SCLK输出 USART_LastBit_Enable 后一位数据的时钟脉冲从SCLK输出 2. 串口复位函数USART_DeInit 表518描述了函数USART_DeInit的具体含义。 表518USART_DeInit函数 函数名 USART_DeInit 函数原形 void USART_DeInit(USART_TypeDef* USARTx) 功能描述 将外设USARTx寄存器重设为缺省值 输入参数 USARTx: x可以是1、2或3,来选择USART外设 输出参数 无 返回值 无 先决条件 无 被调用函数 RCC_APB2PeriphResetCmd() RCC_APB1PeriphResetCmd() 3. 使能或者失能USART外设函数USART_Cmd 描述使能或者失能USART外设的函数为USART_ Cmd,具体含义如表519所示。 表519USART_ Cmd函数 函数名 USART_ Cmd 函数原形 void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState) 功能描述 使能或者失能USART外设 输入参数1 USARTx: x可以是1、2或3,来选择USART外设 输入参数2 NewState: 外设USARTx新状态参数可以取 ENABLE或者DISABLE 输出参数 无 返回值 无 先决条件 无 被调用函数 无 4. 使能或者失能指定的USART中断函数USART_ITConfig 使能或者失能指定的USART中断函数为USART_ITConfig,具体含义如表520所示。 表520USART_ITConfig函数 函数名 USART_ITConfig 函数原形 void USART_ITConfig(USART_TypeDef* USARTx, u16 USART_IT, FunctionalState NewState) 功能描述 使能或者失能指定的USART中断 输入参数1 USARTx: x可以是1、2或3,来选择USART外设 续表 函数名 USART_ITConfig 输入参数2 USART_IT: 待使能或者失能的USART中断源参阅表521: USART_IT查阅更多该参数允许取值范围 输入参数3 NewState: USARTx中断的新状态参数可以取ENABLE或者DISABLE 输出参数 无 返回值 无 先决条件 无 被调用函数 无 输入参数USART_IT含义是使能或者失能USART的中断。可以把表521中的一个或者多个取值的组合作为该参数的值。 表521USART_IT值 USART_IT 描述 USART_IT_PE 奇偶错误中断 USART_IT_TXE 发送中断 USART_IT_TC 传输完成中断 USART_IT_RXNE 接收中断 USART_IT_IDLE 空闲总线中断 USART_IT_LBD LIN中断检测中断 USART_IT_CTS CTS中断 USART_IT_ERR 错误中断 5. 使能或者失能指定USART的DMA请求函数USART_DMACmd 使能或者失能指定USART的DMA请求函数为USART_DMACmd,具体含义如表522所示。 表522函数USART_ DMACmd 函数名 USART_ DMACmd 函数原形 void USART_DMACmd( USART_TypeDef *USARTx, uint16_tUSART_DMAReq, FunctionalStateNewState ) 功能描述 使能或者失能指定USART的DMA请求 输入参数1 USARTx: 选择USART外设,x可以是1、2或3 输入参数2 USART_DMAreq: 指定DMA请求 输入参数3 NewState: USARTx DMA请求源的新状态,这个参数可以取ENABLE或者DISABLE 输出参数 无 返回值 无 先决条件 无 被调用函数 无 USART_DMAreq选择待使能或者失能的DMA请求。表523给出了该参数可取的值。 表523USART_LastBit值 USART_DMAreq 描述 USART_DMAReq_Tx 发送DMA请求 USART_DMAReq_Rx 接收DMA请求 6. 通过外设USARTx发送单个数据函数USART_SendData 通过外设USARTx发送单个数据函数为USART_SendData,具体含义如表524所示。 表524USART_ SendData函数 函数名 USART_ SendData 函数原形 void USART_SendData(USART_TypeDef* USARTx, u8 Data) 功能描述 通过外设USARTx发送单个数据 输入参数1 USARTx: 选择USART外设,x可以是1、2或3 输入参数2 Data:待发送的数据 输出参数 无 返回值 无 先决条件 无 被调用函数 无 7. USART收到数据函数USART_ReceiveData 表525描述了串口接收函数USART_ ReceiveData 表525USART_ReceiveData函数 函数名 USART_ ReceiveData 函数原形 u8 USART_ReceiveData(USART_TypeDef* USARTx) 功能描述 返回USARTx接收到的数据 输入参数 USARTx: 选择USART外设,x可以是1、2或3 输出参数 无 返回值 接收到的字 先决条件 无 被调用函数 无 8. 检查指定的USART标志位设置与否函数USART_GetFlagStatus 检查指定的USART标志位设置与否函数为USART_GetFlagStatus,具体含义如表526所示。 表526USART_ GetFlagStatus函数 函数名 USART_ GetFlagStatus 函数原形 FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, u16 USART_FLAG) 续表 函数名 USART_ GetFlagStatus 功能描述 检查指定的USART标志位设置与否 输入参数1 USARTx: 选择USART外设,x可以是1、2或3 输入参数2 USART_FLAG: 待检查的USART标志位 输出参数 无 返回值 USART_FLAG的新状态(SET或者RESET) 先决条件 无 被调用函数 无 表527给出了所有可以被函数USART_ GetFlagStatus检查的标志位列表。 表527USART_FLAG值 USART_FLAG 描述 USART_FLAG_CTS CTS标志位 USART_FLAG_LBD LIN中断检测标志位 USART_FLAG_TXE 发送数据寄存器空标志位 USART_FLAG_TC 发送完成标志位 USART_FLAG_RXNE 接收数据寄存器非空标志位 USART_FLAG_IDLE 空闲总线标志位 USART_FLAG_ORE 溢出错误标志位 USART_FLAG_NE 噪声错误标志位 USART_FLAG_FE 帧错误标志位 USART_FLAG_PE 奇偶错误标志位 9. 清除USARTx的待处理标志位函数USART_ClearFlag 清除USARTx的待处理标志位函数为USART_ClearFlag,具体含义如表528所示。 表528USART_ ClearFlag函数 函数名 USART_ ClearFlag 函数原形 void USART_ClearFlag(USART_TypeDef* USARTx, u16 USART_FLAG) 功能描述 清除USARTx的待处理标志位 输入参数1 USARTx: 选择USART外设,x可以是1、2或3 输入参数2 USART_FLAG: 待清除的USART标志位 输出参数 无 返回值 无 先决条件 无 被调用函数 无 10. 检查指定的USART中断发生与否函数USART_GetITStatus 检查指定的USART中断发生与否函数为USART_GetITStatus,具体含义如表529所示。 表529USART_ GetITStatus函数 函数名 USART_ GetITStatus 函数原形 ITStatus USART_GetITStatus(USART_TypeDef* USARTx, u16 USART_IT) 功能描述 检查指定的USART中断发生与否 输入参数1 USARTx: 选择USART外设,x可以是1、2或3 输入参数2 USART_IT: 待检查的USART中断源 输出参数 无 返回值 USART_IT的新状态 先决条件 无 被调用函数 无 表530给出了所有可以被函数USART_ GetITStatus检查的中断标志位列表。 表530USART_IT值 USART_IT 描述 USART_IT_PE 奇偶错误中断 USART_IT_TXE 发送中断 USART_IT_TC 发送完成中断 USART_IT_RXNE 接收中断 USART_IT_IDLE 空闲总线中断 USART_IT_LBD LIN中断探测中断 USART_IT_CTS CTS中断 USART_IT_ORE 溢出错误中断 USART_IT_NE 噪音错误中断 USART_IT_FE 帧错误中断 11. 清除USARTx的中断待处理位函数USART_ClearITPendingBit 清除USARTx的中断待处理位函数为USART_ClearITPendingBit,具体含义如表531所示。 表531USART_ ClearITPendingBi函数 函数名 USART_ ClearITPendingBit 函数原形 void USART_ClearITPendingBit(USART_TypeDef* USARTx, u16 USART_IT) 功能描述 清除USARTx的中断待处理位 输入参数1 USARTx: 选择USART外设,x可以是1、2或3 输入参数2 USART_IT: 待检查的USART中断源 输出参数 无 返回值 无 先决条件 无 被调用函数 无 5.3.3串口设置步骤 串口设置的一般步骤如下: (1) 串口时钟使能,GPIO时钟使能; (2) 串口复位; (3) GPIO端口模式设置; (4) 串口参数初始化; (5) 开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤); (6) 使能串口; (7) 编写中断处理函数。 5.4串口通信操作实例 下面两段代码,简单地完成了串口通信功能,主程序主要完成串口的初始化然后打印两条信息,编写代码的方式仍然是先写主函数,然后调用子函数,这么编写代码含义清晰,容易理解和分层设计。 5.4.1主程序 主程序主要完成串口代码初始化(第6行),然后打印两条信息说明串口配置正确(第7,8行),串口主程序代码如下: 1.#include "stm32f10x.h" 2.#include "usart1.h" 3.int main(void) 4.{ 5./*USART1 config 115200 8-N-1 */ 6.USART1_Config(); 7.printf("\r\n this is a usart printf test \r\n"); 8.printf("\r\n 欢迎您来到哈尔滨! \r\n"); 9.for(;;) 10.{ 11.} 12.} 5.4.2串口初始化代码 串口初始化代码如下: 1.#include "usart1.h" 2.void USART1_Config(void) 3.{ 4.GPIO_InitTypeDef GPIO_InitStructure; 5.USART_InitTypeDef USART_InitStructure; 6.RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1| RCC_APB2Periph_GPIOA,ENABLE); 7.GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; 8.GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; 9.GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; 10.GPIO_Init(GPIOA,&GPIO_InitStructure); 11.GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; 12.GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING; 13.GPIO_Init(GPIOA,&GPIO_InitStructure); 14.USART_InitStructure.USART_BaudRate=115200; 15.USART_InitStructure.USART_WordLength= USART_WordLength_8b; 16.USART_InitStructure.USART_StopBits= USART_StopBits_1; 17.USART_InitStructure.USART_Parity=USART_Parity_No; 18.USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 19.USART_InitStructure.USART_Mode=USART_Mode_Rx| USART_Mode_Tx; 20.USART_Init(USART1,&USART_InitStructure); 21.USART_Cmd(USART1,ENABLE); 22.} 23.///重定向c库函数printf到USART1 24.int fputc(int ch, FILE *f) 25.{ 26./*发送1字节数据到USART1*/ 27.USART_SendData(USART1,(uint8_t) ch); 28./*等待发送完毕*/ 29.while (USART_GetFlagStatus(USART1,USART_FLAG_TC)== RESET); 30.return(ch); 31.} 32.///重定向c库函数scanf到USART1 33.int fgetc(FILE *f) 34.{ 35./*等待串口1输入数据*/ 36.while (USART_GetFlagStatus(USART1,USART_FLAG_RXNE) ==RESET); 37.return (int)USART_ReceiveData(USART1); 38.} 第6行配置USART1时钟。 第7~10行配置USART1 USART1 Tx (PA.09)为复用推挽输出,速度为50MHz。 第11~13行配置USART1 Rx (PA.10)为浮空输入。 第14~21行配置USART1模式。 下面简单介绍这几个与串口基本配置直接相关的固件库函数。这些函数和定义主要分布在stm32f10x_usart.h和stm32f10x_usart.c文件中。 1. 串口时钟使能 串口是挂载在APB2下面的外设,所以使能函数为 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1| RCC_APB2Periph_GPIOA,ENABLE); 2. 串口复位 当外设出现异常的时候可以通过复位设置,实现该外设的复位,然后重新配置这个外设达到让其重新工作的目的。一般在系统刚开始配置外设的时候,都会先执行复位该外设的操作。复位是在函数USART_DeInit()中完成: void USART_DeInit(USART_TypeDef* USARTx); 比如要复位串口1,方法为 USART_DeInit(USART1); 3. 串口参数初始化 串口初始化是通过USART_Init()函数实现的: void USART_Init(USART_TypeDef* USARTx,USART_InitTypeDef* USART_InitStruct); 这个函数的第一个入口参数是指定初始化的串口标号,这里选择USART1。 第二个入口参数是一个USART_InitTypeDef类型的结构体指针,这个结构体指针的成员变量用来设置串口的一些参数。一般的实现格式为 (1) 设置波特率: USART_InitStructure.USART_BaudRate=bound; (2) 设置字长: USART_InitStructure.USART_WordLength=USART_WordLength_8b; (3) 设置停止位: USART_InitStructure.USART_StopBits=USART_StopBits_1; (4) 设置奇偶校验位: USART_InitStructure.USART_Parity=USART_Parity_No; (5) 设置件数据流控制: USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; (6) 设置收发模式: USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; (7) 初始化串口: USART_Init(USART1,&USART_InitStructure); 从上面的初始化格式可以看出初始化需要设置的参数为波特率、字长、停止位、奇偶校验位、硬件数据流控制、模式(收,发)。读者可以根据需要设置这些参数。 4. 数据发送与接收 STM32的发送与接收是通过数据寄存器USART_DR来实现的,这是一个双寄存器,包含了TDR和RDR。当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也是存在该寄存器内。 STM32库函数操作USART_DR寄存器发送数据的函数是: void USART_SendData(USART_TypeDef* USARTx,uint16_t Data); 通过该函数向串口寄存器USART_DR写入一个数据。 STM32库函数操作USART_DR寄存器读取串口接收到的数据的函数是: uint16_t USART_ReceiveData(USART_TypeDef* USARTx); 通过该函数可以读取串口接收到的数据。 5. 串口状态 串口的状态可以通过状态寄存器USART_SR读取。 在固件库函数里面,读取串口状态的函数是: FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx,uint16_t USART_FLAG); 这个函数的第二个入口参数非常关键,用于标识要查看串口的状态,比如上面讲解的 RXNE(读数据寄存器非空)以及TC(发送完成)。例如要判断读寄存器是否非空(RXNE),操作库函数的方法是: USART_GetFlagStatus(USART1,USART_FLAG_RXNE); 要判断发送是否完成(TC),操作库函数的方法是: USART_GetFlagStatus(USART1,USART_FLAG_TC); 这些标识号在MDK里面是通过宏定义定义的: #define USART_IT_PE((uint16_t)0x0028) #define USART_IT_TXE((uint16_t)0x0727) #define USART_IT_TC((uint16_t)0x0626) #define USART_IT_RXNE((uint16_t)0x0525) #define USART_IT_IDLE((uint16_t)0x0424) #define USART_IT_LBD((uint16_t)0x0846) #define USART_IT_CTS((uint16_t)0x096A) #define USART_IT_ERR((uint16_t)0x0060) #define USART_IT_ORE((uint16_t)0x0360) #define USART_IT_NE((uint16_t)0x0260) #define USART_IT_FE((uint16_t)0x0160) 6. 串口使能 串口使能是通过函数USART_Cmd()来实现的,这个很容易理解,使用的方法是: USART_Cmd(USART1,ENABLE); 7. 开启串口响应中断 当开启串口中断时,还需要使能串口中断,使能串口中断的函数是: void USART_ITConfig(USART_TypeDef* USARTx,uint16_t USART_IT,FunctionalState NewState) 这个函数的第二个入口参数是标识使能串口的类型,也就是使能哪种中断,因为串口的中断类型有很多种。比如在接收到数据的时候(RXNE,读数据寄存器非空),要产生中断,那么开启中断的方法是: USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); 在发送数据结束的时候(TC,发送完成)要产生中断,那么产生中断的方法是: USART_ITConfig(USART1,USART_IT_TC,ENABLE); 8. 获取相应中断状态 当使能了某个中断,该中断发生了的时候,就会设置状态寄存器中的某个标志位。经常在中断处理函数中,要判断该中断是哪种中断,使用的函数是: ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT) 比如使能了串口发送完成中断,那么当中断发生了,便可以在中断处理函数中调用这个函数来判断到底是否是串口发送完成中断,方法是: USART_GetITStatus(USART1,USART_IT_TC) 返回值是SET,说明串口发送完成中断发生。 串口代码实验现象如图514所示。 图514串口代码实验现象 5.5本章小结 本章主要讲述了STM32串口通信的基本方法,通过第4、5章的学习,对STM32的控制方法有了一定了解,注意操作步骤之间的相同点和不同点。 本章例子是通过printf输出到串口,实际上读者可以直接使用USART_SendData(USART1,(uint8_t) ch)函数完成发送数据,读者思考一下,区别在哪里?读者应详细掌握串口配置每个步骤使用的函数和方法,后续其他功能还会用到大量的STM32库函数,读者应掌握如何使用官方库函数的方法。 5.6习题 (1) RS232有哪些接线方式? (2) 串口波特率的计算方法是什么? (3) 简述STM32采用库函数方式操作串口的步骤。 (4) 如何同时打开两个终端实现串口之间数据通信?