第11课【串口通讯 USART协议】串口入门 RS232 TTL 物理层 协议层

前言

文章中的部分概念可参考第8课【通讯的基本概念】串行并行 单工半双工全双工 同步异步 比特率波特率

串口通讯

串口通讯指的设备与设备之间通过串行接口(Communication Port),串行通讯协议进行通信的通讯方式,也是串行通讯的一种实现方式。由于其成本低,结构简单,传输距离长等优点,在工业领域被广泛运用;开发者在日常的调试工作中也经常会用到

在分析复杂的问题或设计庞大的系统时,为了能对问题有更快更清晰的理解,通常会使用分层的思想将问题按照一定的类别进行拆分。对于串口通讯系统,可以将其分为两层:

  • 物理层:指的是硬件串行接口,例如RS-232,RS-485,TTL标准等。它规定了硬件串口接口的电气,机械特性,逻辑信号定义,连接方式等,以确保原始数据能在设备之间进行可靠传输
  • 协议层:指的是串行通信协议,一般有UART,I2C,SPI协议等。它定义了原始数据的打包/解包标准,重传机制,数据校验标准等,以确保原始数据能转化为有效信息,同时它还完成了协议层与应用层的数据交换,传输等工作

以下内容主要讨论RS232标准+UART协议实现的串口通讯

RS-232

串口通讯的物理层有很多标准和标准变种,RS232是较为经典的物理层标准,它起源于19世纪60年代,如今已经被广泛地运用于计算机同各种电子设备通讯的场合。它的通讯模型是这样的:

在这里插入图片描述
可以看出以下特点:

  • RS-232标准是设备和设备之间的点对点通讯方式
  • 设备与设备之间通过DB9/DB25接口和DB9/DB25连接线连接(当然也可使用其他接口和连接线)
  • 设备内部的元器件之间通常使用TTL标准进行通信,所以内部电路接收外部RS-232标准传输来的信号时,需要经过电平转换芯片转换

此外RS-232标准还有以下特点:

  • 支持全双工异步通讯通讯(具体需要全双工取决于协议层需求),不包含时钟信号(不支持同步传输)
  • 最高速度可达200Kbps

RS-232通讯通常用于计算机,手机,PDA和路由,调制解调设备之间。计算机,手机,PDA这类设备被称为DTE(Data Terminal Equipment 数据终端设备),可以理解为生成/接收数据的设备,例如你通过手机向服务器发起获取网页的请求(手机作为请求数据的起点),或者手机接收服务器发来的网页(手机作为网页数据的终点);路由,调制解调设备被称为DCE(Data Cicuit-terminal Equipment 数据电路终端设备),可以理解为负责信号传递,放大,调制解调等辅助信号传输的设备,它可以连接两个/多个DTE设备,并负责设备间的信号处理

电平标准

物理层的本质目的是要传输原始数据,即0/1组成的比特流,所以在物理层中需要规定0/1比特流的表示方法:通过电流/电压(电平)高低差异去表示0/1信号,通过电流表示的成为电流环路标准,通过电压表示的成为电平标准。对于不同物理层的信号表示方法不同,常见的RS-232标准,TTL标准都是通过电平标准来定义0/1比特流。具体电平规定如下图:

通讯标准电平标准
5V TTL逻辑1:2.4V~5V
逻辑0:0~0.5V
RS-232逻辑1:-15V~-3V
逻辑0:+3V~+15V

可以看出,5V TTL和RS-232电平标准的电压范围以及逻辑0/1的判断范围都有很大不同。这是因为5V TTL电平标准适用于近距离(同一块电路板上),抗干扰能力要求不高,功耗要求较严格的场景;而RS-232电平标准适用于远距离(15米左右),抗干扰能力要求高,功耗要求不高的场景

连接器

通讯标准中,连接器通常用于连接需要建立通讯的设备或器件,以便进行数据传输。它为数据传输的流程提供了物理上的信号通路。RS-232标准中没有指定使用哪种连接器,只给出了连接器应该有的最低标准,例如引脚至少需要9根。而DB9连接器是RS-232连接器实现最广泛使用的一种,它由EIA-574标准定义的,符合RS-232标准的要求,开发者也经常称其为串口线。串口线的外观如下图:

在这里插入图片描述
串口线分为公头(Male)和母头(Female),公头引出针式信号线用于传输信号,如上图中印有UGreen字样一端的串口线接头,母头引出孔氏信号线,如上图中印有RS-232字样一端的串口线接头。一般DTE设备上配置公头,DCE设备上配置母头,两设备之间通过公头-母头串口线连接到一起。通讯时,传续的信号按照RS-232标准要求调制,通过串口线进行传输

串口线公头/母头的9个接线方式/引脚定义如下图:

在这里插入图片描述

引脚编号功能解释
1Carrier Detect (CD)数据载波检测,用于检测传输线路上是否有数据载波信号
2Receive Data (RD)接收数据,用于接收发送方发送过来的数据
3Transmit Data (TD)发送数据,用于向接收方发送数据
4Data Terminal Ready (DTR)数据终端就绪,用于告诉接收方,发送方已经准备好进行数据通信
5Signal Ground (SG)信号地,用于连接信号线的地线
6Data Set Ready (DSR)数据设备就绪, 用于告诉发送方,接收方已经准备好进行数据通信
7Request to Send (RTS)请求发送,接收方通过RTS,告知发送方已经准备好发送数据
8Clear to Send (CTS)清除发送,发送方检测CTS,判断接收方是否准备好接收数据
9Ring Indicator (RI)响铃指示,用于指示对端设备正在呼叫本地设备

引脚定义中,需要注意DTE,DCE之间TXD和RXD线的接线顺序,DTE的TXD对应DCE的RXD,DTE的RXD对应DCE的TXD,双线全双工通信的情况下,接线如下图:

在这里插入图片描述
只有将数接收信号线,数据发送信号线相互交叉,才能确保数据能在DTE和DCE设备之间正常传输。在目前的工业控制领域,串口线通常只会使用3个引脚:TXD,RXD,GND,直接进行信号传输,其余引脚会被裁剪掉

硬件流控制下的TXD,RXD接线
在双线全双工,还额外使能了硬件流控制的情况下,DTE/DCE会同时扮演数据发送设备和接收设备。所以还需要连接RTS,CTS引脚,RTS,CTS引脚的链接和TXD,RXD的相互交叉连接类似,如上图虚线部分。数据接收方会通过拉低RTS表示允许发送数据,将RTS拉高表示禁止发送数据,而数据发送方会在发送数据前检测CTS,CTS为低时进行数据发送操作,CTS为高时,等待接收方允许数据发送

UART

设备之间以RS-232标准调制信息通过串口线进行数据传输时,并不是胡乱地发送二进制数据,而是有组织的通过数据包的形式进行数据的传输。对于UART协议,它的串口数据包基本组成为:

在这里插入图片描述
可以看出:

  • 空闲状态时,数据收/发信号线上的信号应该是逻辑1,具体电平由电平标准决定
  • 基本串口数据包由起始位(1bit)+有效数据位(8bits)+校验位(1bit)+停止位(1bit)共十一位二进制数据组成

UART协议(Universal Asynchronous Receiver and Transmitter 通用异步传输协议)是USART协议(Universal Synchronous Asynchronous Receiver and Transmitter 通用同步异步传输协议)的一种实现方式,USART协议满足工业标准的NRZ串行数据格式要求,兼容多种硬件标准,同时还支持异步通讯或同步通讯。UART协议在USART协议的基础上裁剪掉了同步传输功能,只支持异步传输,其余的特性都基本和USART协议基本保持一致

USART/UART为了提高对硬件的兼容性,很多传输参数可以由开发者自定义,包括但不限于:

波特率

UART是异步通讯,不需要额外的时钟信号线。但是需要通信双方指定一个通信波特率,数据发送方按照波特率发送数据,数据接收方按照波特率解析数据即可。常用的波特率有4800bps,9600bps,115200bps

不同波特率对UART通讯的影响
波特率代表着单位时间内传输的码元数量的多少,对于二进制数据传输而言,波特率=比特率。波特率越高,说明单位时间内传输的码元数量越多,也就是每个码元的持续时间会变短,会导致信号传输中的衰减和噪声的影响就会得越明显,同时对线路传输线缆的要求就会提高。所以在选择波特率时,应当考虑传输距离等外部因素


小数波特率发生器
USART协议中还规定了小数波特率发生器的实现,小数波特率发生器可以让波特率的范围可以更随意的调节,提高了USART协议的兼容性

起始位/停止位

起始位/停止位表示一个数据包的开始与结束。这对信号对于数据接收端尤为重要,由于异步通信不存在时钟信号线,所以需要通过起始位和停止位的上升下降沿,以及设定的波特率来同步时钟信号,根据时钟信号对有效数据部分进行分割和提取。数据包的起始位用一个单位时间的逻辑0表示,数据包的结束位用0.5,1,1.5或2个单位时间的逻辑1表示

有效数据位

有效数据位即开发者需要传输的数据,长度可以约定为5,6,7或8bits。通常使用8bits

校验位

正常环境中,数据通信可能会受到外部的干扰,导致数据出现偏差,所以经常会在有效数据位之后加上一个可选的数据校验位,用于校验有效数据位的数据是否传输正确。常见的校验方法有:

  • 奇校验:奇校验要求有效数据位的逻辑1个数+校验位逻辑1个数=奇数,如果为偶数,则接收到的数据无效。例如有效数据位为1001 0011,校验位必须为1,这时候接收到的数据才是有效的
  • 偶校验:类似于奇校验,偶校验要求有效数据位的逻辑1个数+校验位逻辑1个数=偶数,如果为奇数,则接收到数据无效。
  • 0校验:无论有效数据位是什么,校验位都是0
  • 1校验:无论有效数据位是什么,校验位都是1

STM32 USART功能框图

STM32内部的USART功能框图如下:
在这里插入图片描述

引脚

USART功能模块有以下引脚,分布在框图左右两侧:

  • TX:数据发送引脚
  • RX:数据接收引脚
  • nRTS:请求发送引脚,n代表是低电平有效。当数据发送端准备好发送数据时,nRTS引脚会被拉低,表示可以发送数据;如果数据发送端无法及时发送数据,nRTS引脚会被拉高,表示不能发送数据。此引脚仅在启用硬件流控制时才会生效
  • nCTS:允许发送引脚,n代表是低电平有效。当数据接收端准备好接收数据时,nCTS引脚会被拉低,表示可以接收数据;如果数据接收端无法及时接收数据,nCTS引脚会被拉高,表示不能接收数据。此引脚仅在启用硬件流控制时才会生效
  • SCLK:串行时钟引脚,仅在同步传输时会使用
  • SW_RX:软件数据接收引脚,仅用于单线和智能卡模式,没有外引引脚,属于内部引脚

STM32的部分GPIO引脚可复用为USART或UART功能,具体到STM32F103VET野火指南者这块MCU上,可复用的引脚如下:
在这里插入图片描述
可以看出野火指南者的MCU支持3组USART,2组UART,其中USART1复用的GPIO属于APB2总线下的外设,时钟源也来源于APB2总线时钟,最高频率可达72MHz。而其余的USART2,USART3,UART4,UART5属于APB1总线下的外设,时钟源来源于APB1总线时钟,最高频率可达36MHz。UART由于不需要时钟线,所以没有SCLK引脚

数据寄存器

USART_DR数据寄存器有32bits,其中只有低九位有效。一般传输的数据长度都为8bits,仅使用数据寄存器的D0~D8,是否使用D9,取决于USART_CR1控制寄存器的M位设置,M位设置为0时,传输数据长度为8bits,M位设置为1时,传输数据长度为9bits

USART_DR数据寄存器可写可读,但是读取/写入操作对应的是不同的功能,写入数据对应发送数据功能,而读取操作对应读取数据功能。这是因为USART_DR数据寄存器实际上是由两个寄存器组成:分别是专门用于发送数据的发送数据寄存器TDR和专门用于接收数据的接收数据寄存器RDR,对应框图中偏上灰色虚线框部分。作为辅助,这两个寄存器还分别搭配了发送数据移位寄存器和接收数据移位寄存器

当写入数据到USART_DR数据寄存器,总线上的数据会先保存到发送数据寄存器TDR,当发送移位寄存器可用时,将数据发送到发送移位寄存器,默认按照MSB-LSB的顺序传输。当从USART_DR数据寄存器读取数据时,恰恰和写入数据相反,先将数据一位一位保存到接收移位寄存器,数据存满后,将数据转移到接收数据寄存器RDR,再发送到总线

模块控制

USART功能模块的数据发送/接收/中断/波特率调整/数据校验/唤醒等功能,主要由USART_CR1,USART_CR2,USART_CR3,GTPR四个寄存器控制,位于功能框图的中部位置

使用USART功能模块前,要先将USART_CR1控制寄存器中的UE位置1,以启用USART模块的时钟

发送控制

数据的主要发送流程如下:

  • 通过将USART_CR1控制寄存器中的TE位置1,可以启用数据发送功能
  • 通过将数据写入USART_DR数据寄存器,可以进行数据发送操作
  • 数据发送完成后,硬件会将USART_SR状态寄存器中的TC位置1,表示数据发送操作完成
  • 数据发送完成的同时,如果检测到USART_CR1控制寄存器中的TXIE位置1,那么还会产生中断信号

以上内容中总结成表:

寄存器标志位操作功能
USART_CR1TE启用数据发送功能
USART_CR1TXIE启用数据发送完成触发中断
USART_DR-写入数据
USART_SRTC判断发送操作是否完成

通过控制寄存器还可以对数据包中的部分发送数据的参数进行调整:

  • 数据包长度:通过USART_CR1控制寄存器的M位可以调整数据包长度。M位置0时,传输数据长度为8bits,M位置1时,传输数据长度为9bits
  • 停止位时长:通过USART_CR1控制寄存器中的STOP[1:0]位,可以调整为0.5,1,1.5或2个位周期长度的停止位。默认使用1个位周期的停止位。2个位周期长度的停止位用于正常USART模式通讯,单线模式通讯,调制解调模式通讯,0.5,1.5个位周期长度的停止位用于智能卡模式通讯

接收控制

数据的主要接收流程如下:

  • 通过将USART_CR1控制寄存器中的RE位置1,可以启用数据接收功能
  • 启用数据接收功能后,模块会搜索RX上的数据包起始信号,搜索到起始信号表示接收到数据,硬件会将USART_SR状态寄存器中的RXNE位置1,表示寄存器非空,数据接收操作完成
  • 通过读取USART_DR数据寄存器,可以将接收到数据读取出来
  • 数据接收完成的同时,如果检测到USART_CR1控制寄存器中的RXNEIE位置1,那么还会产生中断信号

将以上内容总结成表:

寄存器标志位操作功能
USART_CR1RE启用数据接收功能
USART_CR2RXNEIE启用数据接收完成触发中断
USART_DR-读取数据
USART_SRRXNE判断发送操作是否完成

小数波特率发生器

USART模块的发送与接收使用相同的波特率,计算公式如下:
在这里插入图片描述
其中fck为USART模块的时钟频率。USARTDIV为无符号定点数,由USART_BRR波特率寄存器控制,波特率寄存器有16bits,分为整数部分DIV_Fraction[11:0]和小数部分DIV_Fraction[3:0]

举例说明,波特率的计算/USART_BSS寄存器配置

  • USART_BSS寄存器配置:如果已知发送/接收波特率,USART模块时钟频率。那么假设计算出USARTDIV值为27.68,那么DIV_Fraction=16*0.68=10.88,最接近的正整数为11, 所以DIV_Fraction[3:0]为0xB;DIV_Mantissa=整数(27.68)=27,即为0x1B
  • 波特率的计算:DIV_Mantissa=24(0x18),DIV_Fraction=10(0x0A),此时USART_BRR值为0x18A; 那么USARTDIV的小数位10/16=0.625;整数位24,最终USARTDIV的值为24.625,通过与USART模块时钟频率配合算出发送/接收波特率

数据校验

STM32F103VET野火指南者的USART模块支持奇偶校验。启动奇偶校验要通过USART_CR1寄存器的PCE位来控制,PCE位置1启用奇偶校验功能,奇偶校验由硬件自动完成,发送数据时自动添加校验位,接收数据时自动判断校验位。需要注意的是,启动奇偶校验时,必须同时将通过USART_CR1控制寄存器将M位置1时,调整传输数据长度为9bits,相当于8bits有效数据+1bit校验位;如果接收数据时,还将USART_SR寄存器的PE位置1,那么奇偶校验失败还会触发中断

中断类型表

USART模块可以触发的中断类型总结如下表:

中断请求名称中断类型中断触发条件中断功能
USART_TXE发送中断USART的数据寄存器为空触发数据发送操作
USART_TC发送完成中断USART成功发送完一个数据帧发送完成后的处理操作
USART_RXNE接收中断USART接收到新的数据触发数据接收操作
USART_IDLE空闲中断USART接收线路上出现空闲状态空闲状态检测和处理
USART_ORE溢出中断USART的数据寄存器溢出处理数据溢出情况

实例分析

以下内容基于STM32F103VET野火指南者开发板,实现将按键的按下/释放消息发送到PC端,同时PC端发送任意数据到开发板会回传回PC的功能。使用到了开发板的按键模块,USB串口模块(实际上是USART模块+CH340G芯片),原理图分别如下:

  • 按键模块:
    在这里插入图片描述
  • CH340G芯片:
    在这里插入图片描述

实现思路为:

  • 初始化UART对应复用GPIO口,配置GPIO口参数,启用GPIO时钟
  • 初始化UART结构体,配置UART参数,启用USART时钟
  • 配置UART中断优先级
  • 配置UART中断参数
  • 启用UART

首先,需要解析标准库提供的USART初始化结构体,以及相关操作函数。外设初始化结构体+外设相关操作函数是使用标准库的精髓所在:

/** 
  * @brief  USART Init Structure definition  
  */ 
typedef struct
{
  uint32_t USART_BaudRate;            /*!< This member configures the USART communication baud rate.
                                           The baud rate is computed using the following formula:
                                            - IntegerDivider = ((PCLKx) / (16 * (USART_InitStruct->USART_BaudRate)))
                                            - FractionalDivider = ((IntegerDivider - ((u32) IntegerDivider)) * 16) + 0.5 */

  uint16_t USART_WordLength;          /*!< Specifies the number of data bits transmitted or received in a frame.
                                           This parameter can be a value of @ref USART_Word_Length */

  uint16_t USART_StopBits;            /*!< Specifies the number of stop bits transmitted.
                                           This parameter can be a value of @ref USART_Stop_Bits */

  uint16_t USART_Parity;              /*!< Specifies the parity mode.
                                           This parameter can be a value of @ref USART_Parity
                                           @note When parity is enabled, the computed parity is inserted
                                                 at the MSB position of the transmitted data (9th bit when
                                                 the word length is set to 9 data bits; 8th bit when the
                                                 word length is set to 8 data bits). */
 
  uint16_t USART_Mode;                /*!< Specifies wether the Receive or Transmit mode is enabled or disabled.
                                           This parameter can be a value of @ref USART_Mode */

  uint16_t USART_HardwareFlowControl; /*!< Specifies wether the hardware flow control mode is enabled
                                           or disabled.
                                           This parameter can be a value of @ref USART_Hardware_Flow_Control */
} USART_InitTypeDef;

其中结构体成员的含义分别如下:

  • USART_BaudRate:波特率,常用波特率有9600,115200
  • USART_WordLength:有效数据长度,可以是8bits或9bits
  • USART_StopBits:停止位长度,可以是0.5,1,1.5,2个位周期的长度
  • USART_Parity:校验位,可以使用奇偶校验,也可不使用校验,即无校验
  • USART_Mode:USART传输模式,可以选择单独启用数据发送或接收功能,或者同时启用数据发送接收功能
  • USART_HardwareFlowControl:硬件流控制,在需要进行硬件流控制来控制数据传输时使用

之后,建立UART相关板级支持文件bsp_uart.h,bsp_uart.c,内容分别如下:

  • bsp_uart.h:
#ifndef __BSP_UART_H__
#define __BSP_UART_H__

#include "stm32f10x_usart.h"

// GPIO for USART1
#define USART_GPIO_CLK        (RCC_APB2Periph_GPIOA)
#define USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd

#define USART_TX_GPIO_PORT    GPIOA
#define USART_TX_GPIO_PIN     GPIO_Pin_9 
#define USART_RX_GPIO_PORT    GPIOA
#define USART_RX_GPIO_PIN     GPIO_Pin_10

// USART1
#define USARTx                USART1
#define USART_CLK             RCC_APB2Periph_USART1
#define USART_APBxClkCmd      RCC_APB2PeriphClockCmd
#define USART_BAUDRATE        115200
// USART1 Interrupt
#define USART_IRQ             USART1_IRQn 
#define USART_IRQHandler      USART1_IRQHandler 

static void NVIC_Configuration(void);
void USART_Config(void);
void USART_SendByte(USART_TypeDef *pUSARTx, uint8_t ch);
void USART_SendString(USART_TypeDef *pUSARTx, char *str);

#endif
  • bsp_uart.c:
#include "misc.h"
#include "core_cm3.h"
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "bsp_uart.h"
#include "stm32f10x_usart.h"

static void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    // Priority group config
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    // USART NVIC param config 
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);
}

void USART_Config(void)
{
    // GPIO
    GPIO_InitTypeDef GPIO_InitStructure;
    // USART GPIO CLK enable
    USART_GPIO_APBxClkCmd(USART_GPIO_CLK, ENABLE);
    // USART TX GPIO config
    GPIO_InitStructure.GPIO_Pin   = USART_TX_GPIO_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(USART_TX_GPIO_PORT, &GPIO_InitStructure);
    // USART RX GPIO config
    GPIO_InitStructure.GPIO_Pin   = USART_RX_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING;
    GPIO_Init(USART_RX_GPIO_PORT, &GPIO_InitStructure);

    // USART1
    USART_InitTypeDef USART_InitStructure;
    // USART1 CLK enable
    USART_APBxClkCmd(USART_CLK, ENABLE);
    // USART1 param config
    USART_InitStructure.USART_Mode                = USART_Mode_Tx | USART_Mode_Rx;
    USART_InitStructure.USART_BaudRate            = USART_BAUDRATE;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
    USART_InitStructure.USART_Parity              = USART_Parity_No;
    USART_InitStructure.USART_StopBits            = USART_StopBits_1;
    USART_Init(USARTx, &USART_InitStructure);

    // Interupt
    // Interupt Priority
    NVIC_Configuration(); 
    // Interupt enable
    USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE);

    // USART enable
    USART_Cmd(USARTx, ENABLE);
}

void USART_SendByte(USART_TypeDef *pUSARTx, uint8_t ch)
{
    USART_SendData(pUSARTx, ch); 

    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

void USART_SendString(USART_TypeDef *pUSARTx, char *str)
{
    unsigned int bit = 0;

    do 
    {
        USART_SendByte(pUSARTx, str[bit]);

        bit++;
    }while (str[bit] != '\0');


    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}

USART数据发送中寄存器TXE和TC位的区别
在以上函数USART_SendByte和USART_SendString中,分别通过TXE和TC位取值来判断数据是否发送完成。初学者再次容易混淆概念,认为这两者都是用于判断数据发送完成情况的,其实并不是!!!

  • TXE:这个位由硬件控制,置1时表示数据发送寄存器TDR的数据已经转移到了发送数据移位寄存器中,数据发送寄存器为空,可以重新写入数据
  • TC:这个位由硬件控制,置1时表示发送缓冲区中的所有数据都已经通过发送数据移位寄存器发送到数据接收方,此次发送操作完成

可以看出,TXE位置1的顺序应该是要比TC要早的,TXE位置1时,数据发送操作还没有完成,数据仅仅是传输到了发送数据移位寄存器中,而TC位置1才真正代表着所有数据都通过发送数据移位寄存器发送出去了

通过板级支持文件,可以完善相应的上层功能需求:

  • 按键的按下/释放消息发送到PC端:
uint8_t KEY1_STATE = KEY_ON;

int userapp(void)
{
    USART_Config();
    Key_GPIO_Init();

    uint8_t Key1_state;
    while (1)
    {
    	// Get key state, detect edge only
        Key1_state = Key_Scan(KEY1_INT_GPIO_PORT, KEY1_GPIO_PIN, &KEY1_STATE);
        if (Key1_state == KEY_PRESS)
        {
            USART_SendString(USARTx, "KEY 1 Pressed\n");
        }
        else if (Key1_state == KEY_RELEASE)
        {
            USART_SendString(USARTx, "KEY 1 Released\n");
        }
    }
  • PC端发送任意数据到开发板回传回PC(使用到了硬件中断,提高运行效率)
void USART1_IRQHandler(void)
{
    uint8_t rev_char;

	// Is receive buffer empty?
    if (USART_GetFlagStatus(USARTx, USART_IT_RXNE))
    {
    	// Get receive data & send it back
        rev_char = USART_ReceiveData(USARTx); 
        USART_SendData(USARTx, rev_char);
    }
}

示例代码已上传Github:Sinuxtm32

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值