21章通讯(USART)

基础理论知识

串行和并行通讯

比喻为单车道和多车道

全双工、半双工及单工通讯

通讯方式说明
全双工在同一时刻,两个设备之间可以同时收发数据
半双工两个设备之间可以收发数据,但不能在同一时刻进行
单工在任何时刻都只能进行一个方向的通讯,即一个固定为发送设备,另一个固定为接收设备

同步通讯与异步通讯

根据通讯过程中是否有使用到时钟信号进行简单的区分。实时性要求不高多用异步通信
同步通讯中,收发设备双方会使用一根信号线表示时钟信号;
异步通讯中不使用时钟信号进行数据同步,它们直接在数据信号中穿插一些同步用的信号位,或者把主体数据进行打包,以数据帧的格式传输数据,见图某种异步通讯,某些通讯中还需要双方约定数据的传输速率,以便更好地同步。

通讯速率

通常以比特率(Bitrate) 来表示,即每秒钟传输的二进制位数,单位为比特每秒(bit/s)。不要和波特率混淆。
“波特率”(Baudrate),它表示每秒钟传输了多少个码元(一组有效数据,大小不固定,不一定是一比特)。

USART-串口

S同步A异步,
波特率一致,开始位为0;
停止位为0.5、1、1.5 或2 个逻辑1 的数据位表示,只要双方约定一致;
校验位;
有效数据的长度常被约定为5、6、7 或8 位长;

物理层

串口通讯的物理层有很多标准及变种,我们主要讲解RS-232 标准,RS-232 标准主要规定了信号的用途、通讯接口以及信号的电平标准。

TTL电平标准与RS232电平标准

通讯标准电平标准(发送端)
5V TTL逻辑l: 2.4V-5V
逻辑0: 0~0.5V
RS-232逻辑1: -15V~-3V
逻辑0: +3V~+15V

协议层

这里主要介绍异步通信:串口通讯的协议层中,规定了数据包的内容,它由启始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据串口数据包组成
波特率:异步通讯中由于没有时钟信号,所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码,图串口数据包的基本组成中用虚线分开的每一格就是代表一个码元。常见的波特率为4800、9600、115200 等。
通讯的起始和停止信号:串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑0 的数据位表示,而数据包的停止信号可由0.5、1、1.5 或2 个逻辑1 的数据位表示,只要双方约定一致即可。
有效数据:在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为5、6、7 或8 位长。
数据校验:校验方法有奇校验(odd)、偶校验(even)、0 校验(space)、1 校验(mark) 以及无校验(noparity)。

STM32 的USART 简介

通用同步异步收发器(Universal Synchronous Asynchronous Receiver and Transmitter) 是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。有别于USART 还有一个UART(UniversalAsynchronous Receiver and Transmitter),它是在USART 基础上裁剪掉了同步通信功能,只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是UART。
应用:USART 支持同步单向通信和半双工单线通信;还支持局域互连网络LIN、智能卡(SmartCard) 协议与lrDA(红外线数据协会) SIR ENDEC 规范。

USART 功能框图

USART功能框图

功能引脚

TX:发送数据输出引脚。
RX:接收数据输入引脚。
SW_RX:数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体外部引脚。

nRTS:请求以发送(Request To Send),n 表示低电平有效。如果使能RTS 流控制,当USART 接收器准备好接收新数据时就会将nRTS 变成低电平;当接收寄存器已满时,nRTS 将被设置为高电平。该引脚只适用于硬件流控制
nCTS:清除以发送(Clear To Send),n 表示低电平有效。如果使能CTS 流控制,发送器在发送下一帧数据之前会检测nCTS 引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。
SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。

芯片的USART 引脚
芯片的USART 引脚

STM32F103VET6 系统控制器有三个USART 和两个UART,其中USART1 和时钟来源于APB2 总线时钟,其最大频率为72MHz,其他四个的时钟来源于APB1 总线时钟,其最大频率为36MHz。UART 只是异步传输功能,所以没有SCLK、nCTS 和nRTS 功能引脚。
数据寄存器 —— USART_DR
USART 数据寄存器(USART_DR) 只有低9 位有效,并且第9 位数据是否有效要取决于USART控制寄存器1(USART_CR1) 的M 位设置,当M 位为0 时表示8 位数据字长,当M 位为1 表示9位数据字长,我们一般使用8 位数据字长。
USART_DR 包含了已发送的数据或者接收到的数据。USART_DR 实际是包含了两个寄存器,一个专门用于发送的可写TDR,一个专门用于接收的可读RDR。当进行发送操作时,往USART_DR写入数据会自动存储在TDR 内;当进行读取操作时,向USART_DR 读取数据会自动提取RDR数据。
TDR 和RDR 都是介于系统总线和移位寄存器之间。串行通信是一个位一个位传输的,发送时把TDR 内容转移到发送移位寄存器,然后把移位寄存器数据每一位发送出去,接收时把接收到的每一位顺序保存在接收移位寄存器内然后才转移到RDR。
USART 支持DMA 传输,可以实现高速数据传输。
控制器
先向USART_CR1 寄存器的UE 位置1 使能USART,UE 位用来开启串口的时钟。
发送器
当USART_CR1 寄存器的发送使能位TE 置1 时,启动数据发送,发送移位寄存器的数据会在TX引脚输出,低位在前,高位在后。如果是同步模式SCLK 也输出时钟信号。
一个字符帧发送需要三个部分:起始位+ 数据帧+ 停止位
起始位是一个位周期的低电平,位周期就是每一位占用的时间;数据帧就是我们要发送的8 位或9 位数据,数据是从最低位开始传输的;停止位是一定时间周期的高电平。停止位时间长短是可以通过USART 控制寄存器2(USART_CR2) 的STOP[1:0] 位控制,可选0.5个、1 个、1.5 个和2 个停止位。默认使用1 个停止位。2 个停止位适用于正常USART 模式、单线模式和调制解调器模式。0.5 个和1.5 个停止位用于智能卡模式。
当发送使能位TE 置1 之后,发送器开始会先发送一个空闲帧(一个数据帧长度的高电平),接下来就可以往USART_DR 寄存器写入要发送的数据。在写入最后一个数据后,需要等待USART 状态寄存器(USART_SR) 的TC 位为1,表示数据传输完成,如果USART_CR1 寄存器的TCIE 位置1,将产生中断。
在发送数据时,编程的时候有几个比较重要的标志位。

名称描述
RE接收使能
RXNE读数据寄存器非空
RXNEIE发送完成中断使能

接收器
如果将USART_CR1 寄存器的RE 位置1,使能USART 接收,使得接收器在RX 线开始搜索起始位。在确定到起始位后就根据RX 线电平状态把数据存放在接收移位寄存器内。接收完成后就把接收移位寄存器数据移到RDR 内,并把USART_SR 寄存器的RXNE 位置1,同时如果USART_CR2 寄存器的RXNEIE 置1 的话可以产生中断。
小数波特率生成
对于USART 波特率与比特率相等

波 特 率 = f p l c k 16 × U S A R T D I V {波特率=\cfrac{f_{plck}}{16×USARTDIV}} =16×USARTDIVfplck
fPLCK 为USART 时钟,USARTDIV 是一个存放在波特率寄存器(USART_BRR) 的一个无符号定点数。其中DIV_Mantissa[11:0] 位定义USARTDIV 的整数部分,DIV_Fraction[3:0] 位定义小数部分。
实例讲解:
我们知道USART1 使用APB2 总线时钟,最高可达72MHz,其他USART 的最高频率为36MHz。我们选取USART1 作为实例讲解,即fPLCK=72MHz。为得到115200bps 的波特率,此时:
115200 = 72 × 1 0 6 16 × U S A R T D I V {115200=\cfrac{72×10^{6}}{16×USARTDIV}} 115200=16×USARTDIV72×106
解得USARTDIV=39.0625,可算得DIV_Fraction=0.0625*16=1=0x01,DIV_Mantissa=39=0x17,即应该设置USART_BRR 的值为0x171。
校验控制
中断控制

实例

USART 初始化结构体

typedef struct {
	uint32_t USART_BaudRate; // 波特率
	uint16_t USART_WordLength; // 字长
	uint16_t USART_StopBits; // 停止位
	uint16_t USART_Parity; // 校验位
	uint16_t USART_Mode; // USART 模式
	uint16_t USART_HardwareFlowControl; // 硬件流控制
} USART_InitTypeDef;
  1. USART_BaudRate:波特率设置。一般设置为2400、9600、19200、115200。标准库函数会根据设定值计算得到USARTDIV 值,从而设置USART_BRR 寄存器值。
  2. USART_WordLength:数据帧字长,可选8 位或9 位。它设定USART_CR1 寄存器的M 位的值。如果没有使能奇偶校验控制,一般使用8 数据位;如果使能了奇偶校验则一般设置为9 数据位。
  3. USART_StopBits:停止位设置,可选0.5 个、1 个、1.5 个和2 个停止位,它设定USART_CR2寄存器的STOP[1:0] 位的值,一般我们选择1 个停止位。
  4. USART_Parity:奇偶校验控制选择,可选USART_Parity_No(无校验)、USART_Parity_Even(偶校验) 以及SART_Parity_Odd(奇校验),它设定USART_CR1 寄存器的PCE 位和PS 位的值。
  5. USART_Mode:USART 模式选择,有USART_Mode_Rx 和USART_Mode_Tx,允许使用逻辑或运算选择两个,它设定USART_CR1 寄存器的RE 位和TE 位。
  6. USART_HardwareFlowControl:硬件流控制选择,只有在硬件流控制模式才有效,可选有 使能RTS、 使能CTS、 同时使能RTS 和CTS、不使能硬件流。
    当使用同步模式时需要配置SCLK 引脚输出脉冲的属性,标准库使用一个时钟初始化结构体USART_ClockInitTypeDef 来设置,该结构体内容也只有在同步模式才需要设置。

USART 时钟初始化结构体(同步模式需要时钟)

typedef struct {
	uint16_t USART_Clock; // 时钟使能控制
	uint16_t USART_CPOL; // 时钟极性
	uint16_t USART_CPHA; // 时钟相位
	uint16_t USART_LastBit; // 最尾位时钟脉冲
} USART_ClockInitTypeDef;
  1. USART_Clock:同步模式下SCLK 引脚上时钟输出使能控制,可选禁止时钟输出(USART_Clock_Disable) 或开启时钟输出(USART_Clock_Enable);如果使用同步模式发送,一般都需要开启时钟。它设定USART_CR2 寄存器的CLKEN 位的值。
  2. USART_CPOL:同步模式下SCLK 引脚上输出时钟极性设置,可设置在空闲时SCLK 引脚为低电平(USART_CPOL_Low) 或高电平(USART_CPOL_High)。它设定USART_CR2 寄存器的CPOL位的值。
  3. USART_CPHA:同步模式下SCLK 引脚上输出时钟相位设置,可设置在时钟第一个变化沿捕获数据(USART_CPHA_1Edge) 或在时钟第二个变化沿捕获数据。它设定USART_CR2 寄存器的CPHA 位的值。USART_CPHA 与USART_CPOL 配合使用可以获得多种模式时钟关系。
  4. USART_LastBit:选择在发送最后一个数据位的时候时钟脉冲是否在SCLK 引脚输出,可以是不输出脉冲(USART_LastBit_Disable)、输出脉冲(USART_LastBit_Enable)。它设定USART_CR2 寄存器的LBCL 位的值。

USART1 接发通信实验

USART 只需两根信号线即可完成双向通信,对硬件要求低,使得很多模块都预留USART 接口来实现与其他模块或者控制器进行数据传输,比如GSM 模块,WIFI 模块、蓝牙模块等等。在硬件设计时,注意还需要一根“共地线”。
硬件设计
为利用USART 实现开发板与电脑通信,需要用到一个USB 转USART 的IC,我们选择CH340G芯片来实现这个功能,CH340G 是一个USB 总线的转接芯片,实现USB 转USART、USB 转lrDA红外或者USB 转打印机接口。
软件设计
编程要点

  1. 使能RX 和TX 引脚GPIO 时钟和USART 时钟;
  2. 初始化GPIO,并将GPIO 复用到USART 上;Rx浮空输入Tx推挽复用
  3. 配置USART 参数;
  4. 配置中断控制器并使能USART 接收中断;
  5. 使能USART;
  6. 在USART 接收中断服务函数实现数据接收和发送。

这里我们使用USART1,设定波特率为115200,选定USART 的GPIO 为PA9 和PA10。
中断控制器NVIC 配置:

static void NVIC_Configuration(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	
	/* 嵌套向量中断控制器组选择*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	/* 配置USART 为中断源*/
	NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
	 /* 抢断优先级为1 */
	  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	 /* 子优先级为1 */
	 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	 /* 使能中断*/
	 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	 /* 初始化配置NVIC */
	 NVIC_Init(&NVIC_InitStructure);
 }

配置USART 作为中断源(所有的通讯协议配置中断都是类似的)
USART 初始化配置

void USART_Config(void)
{
	 GPIO_InitTypeDef GPIO_InitStructure;
	 USART_InitTypeDef USART_InitStructure;
	
	 // 打开串口GPIO 的时钟
	 DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	 // 打开串口外设的时钟
	 DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
	
	 // 将USART Tx 的GPIO 配置为推挽复用模式
	 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	 GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
	 // 将USART Rx 的GPIO 配置为浮空输入模式
	 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	 GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	 // 配置串口的工作参数
	 // 配置波特率
	 USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	 // 配置针数据字长
	 USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	 // 配置停止位
	 USART_InitStructure.USART_StopBits = USART_StopBits_1;
	 // 配置校验位
	 USART_InitStructure.USART_Parity = USART_Parity_No ;
	 // 配置硬件流控制
	 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	 // 配置工作模式,收发一起
	 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	 // 完成串口的初始化配置
	 USART_Init(DEBUG_USARTx, &USART_InitStructure);
	
	 // 串口中断优先级配置
	 NVIC_Configuration();
	
	 // 使能串口接收中断
	 USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
	
	 // 使能串口
	 USART_Cmd(DEBUG_USARTx, ENABLE);
}
  1. 调用RCC_APB2PeriphClockCmd 函数开启GPIO 端口时钟,使用GPIO 之前必须开启对应端口的时钟。使用RCC_APB2PeriphClockCmd 函数开启USART 时钟。
  2. 初始化GPIO,并且还要添加特殊设置,因为我们使用它作为外设的引脚,一般都有特殊功能。我们在初始化时需要把它的模式设置为复用功能。这里把串口的Tx 引脚配置为复用推挽输出,Rx 引脚为浮空输入,数据完全由外部输入决定。
  3. 接下来,我们配置USART1 通信参数为:波特率115200,字长为8,1 个停止位,没有校验位,不使用硬件流控制,收发一体工作模式,然后调用USART 初始化函数完成配置。
  4. 程序用到USART 接收中断,需要配置NVIC,这里调用NVIC_Configuration 函数完成配置。配置完NVIC 之后调用USART_ITConfig 函数使能USART 接收中断。
  5. 最后调用USART_Cmd 函数使能USART,这个函数最终配置的是USART_CR1 的UE 位,具体的作用是开启USART 的工作时钟,没有时钟那USART 这个外设自然就工作不了。

字符发送
Usart_SendByte 函数用来在指定USART 发送一个ASCLL 码值字符,它有两个形参,第一个为USART,第二个为待发送的字符。它是通过调用库函数USART_SendData来实现的,
Usart_SendString 函数用来发送一个字符串,它实际是调用Usart_SendByte 函数发送每个字符,直
到遇到空字符才停止发送。
USART 中断服务函数

void DEBUG_USART_IRQHandler(void)
 {
	 uint8_t ucTemp;
	 if (USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET) 	//获取中断事件标志
	 {
	 ucTemp = USART_ReceiveData( DEBUG_USARTx );
	 USART_SendData(USARTx,ucTemp);
 	 }
 }

如果是真的就使用USART数据读取函数USART_ReceiveData 读取数据到指定存储区。然后再调用USART 数据发送函数USART_SendData 把数据又发送给源设备,即PC 端的串口调试助手。
重定向printf函数
在C 语言标准库中,fputc 函数是printf 函数内部的一个函数,功能是将字符ch 写入到文件指针f所指向文件的当前写指针位置,简单理解就是把字符写入到特定文件中。我们使用USART 函数重新修改fputc 函数内容,达到类似“写入”的功能。
重定向scanf函数
fgetc 函数与fputc 函数非常相似,实现字符读取功能。在使用scanf 函数时需要注意字符输入格式。
为使用printf、scanf 函数需要在文件中包含stdio.h 头文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值