30min拿下UART,一文从基础到面试!

0、本文要点

学完本文应该掌握的几个关键问题:

1.能否画出UART的时序图,讲述其数据位的特点

2.根据发送数据寄存器/发送移位寄存器,接收移位寄存器/接收数据寄存器讲述UART工作过程

1、UART简介

低速+全双工+异步+串行+三根线

拓展知识1:同步与异步通信

同步通信:时钟信号;异步通信:波特率(单位,bps)

拓展知识2:并行与串行通信,如右上的图

2、通信原理

UART:发送器根据其时钟信号生成的位流取代了时钟信号,接收器使用其内部时钟信号对输入数据进行采样。通过将收发数据双方设置相同波特率进行通信。
如果波特率不同,发送和接收数据的时序可能会受影响,导致数据处理过程出现不一致。允许的波特率差异最大值为10%,超过此值,位的时序就会脱节。

奇校验:数据位中1的数目+校验位(0/1)的1数目是奇数

偶校验:数据位中1的数目+校验位(0/1)的1数目是偶数

3、通信详解

其硬件简图如下所示:

第1步:数据从发送器的数据总线到发送器。

第2步:发送 UART 将起始位、奇偶校验位和停止位添加到数据帧。

第3步:从起始位到结束位,整个数据包以串行方式从发送器送至接收器 。

接收 UART 以预配置的波特率对数据线进行采样

第4步:接收器丢弃数据帧中的起始位、奇偶校验位和停止位。

第5步:接收器将串行数据转换回并行数据,并将其传输到接收端的数据总线。

4、代码理解

4.1 HAL库开发

#include "stm32f4xx_hal.h" // 包含HAL库的头文件
 
UART_HandleTypeDef huart2; // UART句柄对象
 
void SystemClock_Config(void);
 
int main(void) {
  // 初始化HAL库
  HAL_Init();
  
  // 配置系统时钟
  SystemClock_Config();
  
  // 使能串口时钟
  __HAL_RCC_USART2_CLK_ENABLE();
 
  // 配置串口参数
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 9600;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  HAL_UART_Init(&huart2);
  
  // 发送数据
  char txData[] = "Hello, World!";
  HAL_UART_Transmit(&huart2, (uint8_t*)txData, strlen(txData), HAL_MAX_DELAY);
  
  // 接收数据
  char rxData[50]; // 接收缓冲区
  HAL_UART_Receive(&huart2, (uint8_t*)rxData, sizeof(rxData), HAL_MAX_DELAY);
  
  while (1) {
    // 运行其他任务
  }
}
 
// 配置系统时钟
void SystemClock_Config(void) {
 
  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
 
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
 
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 16;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);
 
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1
                              |RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}

上述代码使用了STM32的HAL库来简化UART通信的操作。首先,通过初始化HAL库和配置系统时钟来准备系统。然后,通过使能串口时钟、配置串口参数和初始化UART句柄对象来配置UART模块。接下来,可以使用`HAL_UART_Transmit()`函数发送数据,使用`HAL_UART_Receive()`函数接收数据。

4.2 标准库开发

#include "stm32f4xx.h"      // 包含STM32标准库的头文件
 
void USART2_Init(void);
void USART2_Write(char ch);
char USART2_Read(void);
 
int main(void) {
    USART2_Init();          // 初始化USART2
    
    // 发送数据
    USART2_Write('H');
    USART2_Write('e');
    USART2_Write('l');
    USART2_Write('l');
    USART2_Write('o');
    
    // 接收数据
    char received_data;
    while (1) {
        received_data = USART2_Read();
        // 处理接收到的数据
    }
}
 
// USART2初始化
void USART2_Init(void) {
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);         // 使能USART2时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);          // 使能GPIOA时钟
    
    // 配置USART2的引脚
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;        // PA2为USART2的TX引脚,PA3为USART2的RX引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                  // 复用功能
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                // 推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;             // GPIO速度为50MHz
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;                  // 上拉
    GPIO_Init(GPIOA, &GPIO_InitStructure);                        // 初始化GPIOA
    
    // 将引脚映射到USART2
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);     // 将PA2引脚映射到USART2的TX引脚
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);     // 将PA3引脚映射到USART2的RX引脚
    
    // 配置USART2的参数
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = 9600;                     // 波特率为9600
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;    // 数据位为8位
    USART_InitStructure.USART_StopBits = USART_StopBits_1;         // 停止位为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(USART2, &USART_InitStructure);                      // 初始化USART2
    
    USART_Cmd(USART2, ENABLE);                                     // 使能USART2
}
 
// 发送数据到USART2
void USART2_Write(char ch) {
    while (!(USART2->SR & USART_FLAG_TXE));    // 等待发送缓冲区为空
    USART2->DR = (ch & 0xFF);
}
 
// 从USART2接收数据
char USART2_Read(void) {
    while (!(USART2->SR & USART_FLAG_RXNE));   // 等待接收缓冲区有数据
    return (char)(USART2->DR & 0xFF);
}

上述代码通过调用相应的库函数和使用相关的寄存器配置来初始化USART2,并实现了通过USART2发送和接收数据的功能。

5、面试问答

1.为什么要使用UART这种协议

1.成本低,只需要两根线(发送线TX和接收线RX)
2.硬件简单,不需要额外的硬件支持
3.自定义,用户可以根据需要自定义波特率和数据格式

2.UART为什么使用RX和TX两根线来处理

1.单向信号传输:UART通信是基于单向信号传输的
2.简化错误处理和信号完整性:分开的发送和接收线路有助于简化硬件设计和信号处理
3.减少干扰:使用单独的线路发送和接收数据有助于减少电磁干扰和信号串扰,提高通信的可靠性

3.UART的工作原理是什么

  • 初始化:通信开始前,两个设备的UART接口需要被设置为相同的波特率、数据位数、停止位和校验位,以确保双方能正确解读传输的数据;
  • 发送数据:当发送设备准备发送数据时,其UART控制器将数据放置在内部的发送缓冲区中,然后按预设的格式逐位发送出去。这包括在TX线上生成起始位、校验位(如果使用)和停止位;
  • 接收数据:接收设备的UART控制器监控其RX线,检测到起始位后开始按设定的波特率接收数据位。接收的数据存入内部的接收缓冲区,等待进一步处理;
  • 错误检测和校正:如果开启了校验位,接收设备还会进行错误检测,并在检测到错误时进行相应的处理,可能包括请求重发等。

4.UART 数据帧结构

UART的数据传输是通过数据帧来完成的,一个典型的UART数据帧包括:

  • 起始位:数据帧的开始,通常是一个逻辑“0”。
  • 数据位:通常是5到9位,承载实际的数据内容。
  • 校验位(可选):用于错误检测。
  • 停止位:标志数据帧的结束,可以是一个或两个逻辑“1”。

5.UART中为什么有时候我们还会对串口将TTL转换成RS232、RS485、USB呢?

TTL电平:逻辑“1”通常为高电平(5V或3.3V),逻辑“0”为低电平(0V)。广泛应用于微控制器和数字电路中,不适合长距离传输。
RS232:相对于TTL电平,RS232使用更高的电压范围(通常+/-3V至+/-25V)。逻辑“1”(-3V到-25V)和逻辑“0”(+3V到+25V)的定义与TTL相反。它适用于长距离通信
USB(通用串行总线):用于计算机和外围设备之间的通信,支持更高的数据传输速率和电源传输。
RS485:使用差分信号传输,可以实现远距离和高可靠性通信,常用于工业环境。

这些标准(TTL, RS232, USB, RS485)在应用和物理层面有所不同,但都是实现设备间数据传输的有效方式,选择合适的标准取决于具体应用需求,如距离、速率、成本和电气环境的要求。

6.如下图所示

参考链接

1.单片机底层通信协议① —— 同步和异步、并行和串行、全双工和半双工以及单工、电平信号和差分信号_单片机底层硬件协议SDN博客
2.一文搞懂UART通信协议

3.一文读懂UART通信协议-优快云博客

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值