抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

相关概念介绍

USART与UART

USART(通用同步/异步收发器)是STM32系列微控制器内部集成的UART模块

并行和串行通信

并行:各个位同时在不同的数据线上进行数据传输

串行:各个位一位位在同一根数据线上进行数据传输

单工、半双工、双工

单工:数据只能单向传输,一端发送、一端接收(设备只有发送端或者接收端)

半双工:数据在同一根线上可以双向传输,但同一时间内数据只能单向传输

双工:同一时间内数据可以双向传输(两根线,每个设备都有发送端和接收端)

同步与异步

串行通信又可以分为同步、异步方式

同步:发送和接收方之间使用共同的时钟从而使他们的通信保持协调,因此发送和接收端需要额外的一根时钟线进行相连

异步:发送端和接收端不存在共同的时钟。在异步通信中,数据以指定的格式打包成帧进行传输,并在一个数据帧的开头和结尾使用起始位和停止位来实现收发的协调。起始位和停止位用于告诉接收端数据帧的开头和结尾。由于多了起始位和停止位,异步传输速率远低于同步传输速率。

UART通信原理

UART物理层

UART

UART接口

这里介绍异步串行全双工通信的UART接口,所以最简单的UART设备需要有3个接口(TXD、RXD、GND,没有时钟接口),两个UART设备之间互联方式如下图。如果需要实现更复杂的功能,需要增加更多接口(如CTS、RTS)

image-20211007134921219

UART电平

UART电平采用TTL/CMOS的逻辑电平标准(0~5V, 0~3.3V, 0~2.5V, 0~1.8V表示数据),1表示高电平,0表示低电平。例如在TTL电平标准中,1通常表示+5V, 0 通常表示0V

RS232

UART采用的电平标准决定了它的通信距离较短,因此为了增加传输距离,产生了RS232,RS232是在UART的基础上扩展产生的,PC机上的COM口就是RS232的应用实例。

RS232接口

通常采用DB-25或者DB-9的形式,目前以DB-9(使用9根线)最为常见

RS232电平

RS232采用负逻辑:-3V~-15V表示逻辑1,+3V~+15V表示逻辑0;这样的好处是可以降低信号因传输距离远带来的衰减

UART接口要与RS232接口连接,需要采用电平转换芯片

image-20211007140704776

UART协议层

数据格式

UART通过一定格式将数据打包成帧,包括

  1. 起始位(必须,占1位)
  2. 数据位(必须,占5~8位)
  3. 校验位(可选,占0/1位)
  4. 停止位(必须,占1/1.5/2位)
  5. 空闲位(数据传输完毕,线路保持逻辑电平1,即空闲状态)等

传输速率

UART双方必须约定使用相同的传输速率进行发送和接收

传输速率可以用比特率(单位bps,kbps等)或波特率来表示,由于UART使用NRZ(不归零)编码,所以其波特率与比特率是相同的。常用UART的传输速率有:1200、2400、4800、9600、19200、38400、115200……(单位bps)

STM32的UART工作原理

内部结构

image-20211007142154884

主要特性

  • 全双工的,异步通信

  • NRZ标准格式

  • 分数波特率发生器系统

    发送和接收共用的可编程波特率,最高达4.5Mbits/s

  • 可编程数据字长度(8位或9位)

  • 可配置的停止位

    支持1或2个停止位

  • 智能卡模拟功能

    智能卡接口支持ISO7816-3标准里定义的异步智能卡协议

    智能卡用到的0.5和1.5个停止位

  • 单线半双工通信

  • 可配置的使用DMA的多缓冲器通信 ─ 在SRAM里利用集中式DMA缓冲接收/发送字节

  • 单独的发送器和接收器使能位

  • 检测标志

    接收缓冲器满

    发送缓冲器空

    传输结束标志

  • 校验控制 ─ 发送校验位 ─ 对接收数据进行校验

  • 四个错误检测标志

    溢出错误

    噪音错误

    帧错误

    校验错误

  • 10个带标志的中断源

    ─ CTS改变 ─ LIN断开符检测 ─ 发送数据寄存器空 ─ 发送完成 ─ 接收数据寄存器满 ─ 检测到总线为空闲 ─ 溢出错误 ─ 帧错误 ─ 噪音错误 ─ 校验错误

  • 多处理器通信 – 如果地址不匹配,则进入静默模式

  • 从静默模式中唤醒(通过空闲总线检测或地址标志检测)

  • 两种唤醒接收器的方式:地址位(MSB,第9位),总线空闲

USART功能

接口通过三个引脚与其他设备连接在一起(见内部结构图)。

任何USART双向通信至少需要两个脚:接收数据输入(RX)和发送数据输出(TX)。

  • RX:接收数据串行输。通过过采样技术来区别数据和噪音,从而恢复数据。
  • TX:发送数据输出。

当发送器被禁止时,输出引脚恢复到它的I/O端口配置。

当发送器被激活, 并且不发送数据时,TX引脚处于高电平。(在单线和智能卡模式里,此I/O口被同时用于数据的发送和接收)

  • 总线在发送或接收前应处于空闲状态
  • 一个起始位
  • 一个数据字(8或9位),最低有效位在前
  • 0.5,1.5,2个的停止位,由此表明数据帧的结束
  • 使用分数波特率发生器 —— 12位整数和4位小数的表示方法。
  • 一个状态寄存器(USART_SR)
  • 数据寄存器(USART_DR)
  • 一个波特率寄存器(USART_BRR),12位的整数和4位小数
  • 一个智能卡模式下的保护时间寄存器(USART_GTPR)

在同步模式中需要下列引脚:

  • CK:发送器时钟输出。此引脚输出用于同步传输的时钟, (在Start位和Stop位上没有时钟 脉冲,软件可选地,可以在最后一个数据位送出一个时钟脉冲)。数据可以在RX上同步被接收。这可以用来控制带有移位寄存器的外部设备(例如LCD驱动器)。时钟相位和极性都是软件可编程的。在智能卡模式里,CK可以为智能卡提供时钟。

在IrDA模式里需要下列引脚:

  • IrDA_RDI: IrDA模式下的数据输入。
  • IrDA_TDO: IrDA模式下的数据输出。

下列引脚在硬件流控模式中需要:

  • nCTS: 清除发送,若是高电平,在当前数据传输结束时阻断下一次的数据发送
  • nRTS: 发送请求,若是低电平,表明USART准备好接收数据

波特率控制

通过改变USART外设的时钟源(不同的USART挂载的APB总线可能不同)的分频系数USARTDIV可以设置USART的波特率

收发控制

由若干寄存器组成,如CR1,CR2,CR3,USART状态寄存器(SR)等,通过向寄存器写入各种参数来控制USART数据的发送和接收;同时,通过读取状态寄存器可以查询USART当前状态

数据存储转移

核心为两个移位寄存器:发送移位寄存器、接收移位寄存器,用于收发数据同时完成并串转换

发送数据过程

发送数据时,先将数据从内存写入发送数据寄存器TDR,发送控制器适时的把数据从TDR中加载到发送移位寄存器,再通过TX端发送出去

当数据全部从TDR中转移出去后,会产生TDR已空的事件TXE;当数据移位寄存器将数据全部发送到TX后,产生发送完成事件TC。这些事件可以在状态寄存器查到。

接收数据过程

接收数据时,数据从RX端一位位输入到接收移位寄存器,然后接收控制器将移位寄存器的数据转移至接收数据寄存器RDR,最后内核指令或DMA将RDR的数据读取进内存中。

当数据全部从接收移位寄存器转移至RDR寄存器后,产生接收寄存器RDR非空或RDR已满事件RXNE

数据传输整个过程如下:

image-20211007150406321

USART中断

发送期间

中断事件包括

  • TXE(发送数据寄存器空)
  • TC(发送完成)
  • CTS(清除发送)

接收期间

  • IDLE(空闲总线检测)
  • ORE(溢出错误)
  • RXNE(接收数据寄存器非空)
  • PE(校验错误)
  • LBD(LIN断开检测)
  • NE(噪声错误,仅在多缓冲器通信)
  • FE(帧错误,仅在多缓冲器通信)

使用USART进行通信

初始化USART

  • GPIO初始化(输出模式为推挽输出,输入模式为浮空输入,速率50MHz)
  • USART基本配置
    • 波特率,数据位数,奇偶校验,停止位数,工作模式,硬件数据流控制
    • 清除标志、使能USART、开启相关中断
  • 中断向量配置(中断通道配置——监听哪个外设产生的中断,优先级配置,使能通道)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void USART1_Init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); ////使能 GPIOA 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //使能 USART1时钟


/*配置GPIO的模式和IO口 */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; // 串口输出 PA9<->TX
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_Init(GPIOA,&GPIO_InitStructure); // 初始化串口输出

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; // 串口输入 PA10<->RX
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化串口输入

//USART1 初始化设置
USART_InitStructure.USART_BaudRate = bound;//波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
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(USART1, &USART_InitStructure); //初始化串口1

USART_Cmd(USART1, ENABLE); //使能串口1

USART_ClearFlag(USART1, USART_FLAG_TC);

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断

//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
}

中断处理

  • 判断该外设产生的中断类型(这里是接收到数据产生的中断RXNE)
  • 数据处理
1
2
3
4
5
6
7
8
9
10
11
void USART1_IRQHandler(){

u8 r;
if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET){ //如果产生接收中断RXNE

r = USART_ReceiveData(USART1); //(USART1->DR); 读取接收到的数据(一个字符)
USART_SendData(USART1, r); //发送数据
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET); //标志位TC==1时(表示发送完成)退出循环
USART_ClearFlag(USART1, USART_FLAG_CTS); //清除中断标志
}
}

解释:

1
2
3
4
5
u8 r; //用于存储接收到的数据,因为每次接收的数据是8位的,所以r大小也是8位

r = USART_ReceiveData(USART1); //每次只能接收一个字符,若发送多个字符,会依次发送每个字符,每次发送一个字符都会产生一次USART1中断

USART_SendData(USART1, r); //发送数据时也是一个个字符发送

main函数

  • 初始化USART;
1
2
3
4
5
6
7
8
int main(){

USART1_Init(115200);
//初始化完成后,USART1等待接收数据,接收到数据后产生中断RXNE,执行中断处理函数

while(1){
}
}

printf重定向

用printf函数将变量的值通过串口打印到pc机的串口调试工具的接收区

首先要重写 fputc函数

1
2
3
4
5
6
int fputc(int ch, FILE *p){

USART_SendData(USART1,(u8)ch);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //发送寄存器空标志TXE,数据全部发送后被置1,导致循环停止
return ch;
}

这样就能使用 printf函数将数据通过USART1发送到pc机进行查看变量的值了,例如

1
2
3
4
5
6
7
8
9
10
int main(){
char str[] = "我是str";

USART1_Init(115200);

printf("打印字符串str:%s\n1234", str);

while(1){
}
}

打印在串口调试器接收区:

image-20211010000433813

评论