UART Hal库与寄存器比较

在 STM32 开发中,UART 通信可以通过 HAL 库函数或直接操作寄存器实现。下面将详细对比两种方式,并解释 HAL 库函数背后的寄存器操作原理。

1. 串口初始化

HAL 库方式
复制代码
UART_HandleTypeDef huart1;

void MX_USART1_UART_Init(void) {
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK) {
    Error_Handler();
  }
}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) {
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1) {
    __HAL_RCC_USART1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    // 配置TX引脚(PA9)
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 配置RX引脚(PA10)
    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  }
}
寄存器方式
复制代码
void UART1_Init(void) {
  // 使能USART1和GPIOA时钟
  RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
  
  // 配置PA9(TX)为复用功能
  GPIOA->MODER |= GPIO_MODER_MODER9_1;    // 复用模式
  GPIOA->OTYPER &= ~GPIO_OTYPER_OT_9;    // 推挽输出
  GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9_0 | GPIO_OSPEEDER_OSPEEDR9_1; // 高速
  GPIOA->AFR[1] |= (7 << 4);             // AF7: USART1
  
  // 配置PA10(RX)为复用功能
  GPIOA->MODER |= GPIO_MODER_MODER10_1;  // 复用模式
  GPIOA->AFR[1] |= (7 << 8);             // AF7: USART1
  
  // 计算波特率分频值(假设PCLK2=84MHz)
  uint32_t baud = 115200;
  uint32_t div = 84000000 / (16 * baud); // 16倍过采样
  
  // 配置USART1
  USART1->BRR = div;                    // 设置波特率
  USART1->CR1 = USART_CR1_TE | USART_CR1_RE; // 使能发送和接收
  USART1->CR2 = 0;                      // 1个停止位
  USART1->CR3 = 0;                      // 无硬件流控制
  USART1->CR1 |= USART_CR1_UE;          // 使能USART
}
寄存器解释
  • 时钟使能RCC->APB2ENRRCC->AHB1ENR 分别控制 USART 和 GPIO 的时钟。
  • GPIO 配置
    • MODER:设置引脚为复用模式(10)。
    • OTYPER:设置为推挽输出(0)。
    • OSPEEDR:设置为高速模式(11)。
    • AFR:选择复用功能 AF7(USART1)。
  • 波特率计算BRR 寄存器根据公式 fPCLK / (16 * 波特率) 计算分频值。
  • 控制寄存器
    • CR1:使能发送(TE)、接收(RE)和 USART(UE)。
    • CR2:设置停止位(默认 00 为 1 位)。
    • CR3:禁用硬件流控制(0)。

2. 数据发送

HAL 库方式
复制代码
uint8_t txData[] = "Hello, World!";
HAL_UART_Transmit(&huart1, txData, sizeof(txData), 1000);
寄存器方式
复制代码
void UART1_Send(uint8_t* data, uint32_t length) {
  for (uint32_t i = 0; i < length; i++) {
    // 等待发送缓冲区为空
    while (!(USART1->SR & USART_SR_TXE));
    USART1->DR = data[i]; // 写入数据到数据寄存器
  }
  // 等待发送完成
  while (!(USART1->SR & USART_SR_TC));
}
寄存器解释
  • SR 寄存器
    • TXE(发送缓冲区空):当置位时,表示可以写入新数据。
    • TC(发送完成):当置位时,表示最后一个数据已发送完毕。
  • DR 寄存器:存储要发送的数据(低位 8 位有效)。

3. 数据接收

HAL 库方式
复制代码
uint8_t rxData[10];
HAL_UART_Receive(&huart1, rxData, 10, 1000);
寄存器方式
复制代码
void UART1_Receive(uint8_t* data, uint32_t length) {
  for (uint32_t i = 0; i < length; i++) {
    // 等待接收缓冲区非空
    while (!(USART1->SR & USART_SR_RXNE));
    data[i] = USART1->DR; // 读取数据寄存器
  }
}
寄存器解释
  • SR 寄存器
    • RXNE(接收缓冲区非空):当置位时,表示接收到新数据。
  • DR 寄存器:存储接收到的数据(低位 8 位有效)。

4. 中断接收

HAL 库方式
复制代码
uint8_t rxBuffer;
HAL_UART_Receive_IT(&huart1, &rxBuffer, 1);

void HAL_UART_RxCpltCallback(UART_HandleTypeDef* huart) {
  if (huart->Instance == USART1) {
    // 处理接收到的数据 rxBuffer
    HAL_UART_Receive_IT(&huart1, &rxBuffer, 1); // 重新开启中断
  }
}
寄存器方式
复制代码
void UART1_Receive_IT(void) {
  // 使能接收中断
  USART1->CR1 |= USART_CR1_RXNEIE;
  // 配置NVIC
  NVIC_SetPriority(USART1_IRQn, 0);
  NVIC_EnableIRQ(USART1_IRQn);
}

void USART1_IRQHandler(void) {
  if (USART1->SR & USART_SR_RXNE) { // 接收缓冲区非空
    uint8_t data = USART1->DR;
    // 处理接收到的数据
    USART1->SR &= ~USART_SR_RXNE; // 清除标志(读取DR自动清除)
  }
}
寄存器解释
  • CR1 寄存器
    • RXNEIE:使能接收中断。
  • NVIC 配置:设置中断优先级并使能 USART1 中断。
  • 中断处理
    • 检查RXNE标志是否置位。
    • 读取DR寄存器获取数据(读取后RXNE自动清除)。

总结

功能 HAL 库函数 对应寄存器操作
初始化 HAL_UART_Init() 配置 BRR、CR1、CR2、CR3 寄存器
发送数据 HAL_UART_Transmit() 等待 TXE 置位,写入 DR 寄存器
接收数据 HAL_UART_Receive() 等待 RXNE 置位,读取 DR 寄存器
中断接收 HAL_UART_Receive_IT() 使能 RXNEIE,配置 NVIC,在中断中读取 DR
发送完成回调 HAL_UART_TxCpltCallback() 检查 TC 标志
接收完成回调 HAL_UART_RxCpltCallback() 检查 RXNE 标志

通过对比可以看出,HAL 库函数本质上是对寄存器操作的封装,简化了开发流程,但理解寄存器原理有助于深入掌握 UART 通信机制。

相关推荐
d111111111d37 分钟前
STM32外设学习--TIM定时器--编码器接口(程序)
笔记·stm32·嵌入式硬件·学习
辰哥单片机设计42 分钟前
STM32项目分享:水质检测系统(升级版)
stm32·单片机·嵌入式硬件
straw_hat.4 小时前
32HAL——RTC时钟
stm32·学习
电子科技圈5 小时前
XMOS与飞腾云联袂以模块化方案大幅加速音频产品落地
经验分享·嵌入式硬件·mcu·自然语言处理·音视频·腾讯会议·游戏机
Tracy9735 小时前
XMSRC4194_VC1:4通道192KHz ASRC音频采样率转换器产品介绍
嵌入式硬件·音视频·智能硬件·xmos模组固件
xiaotianyun886 小时前
NCP13992 CS 分压计算
单片机·嵌入式硬件·ncp13992
偶像你挑的噻7 小时前
Linux应用开发-17-套接字
linux·网络·stm32·嵌入式硬件
Msshu1238 小时前
PD快充诱骗芯片 XSP15 支持获取快充电压可与外部MCU共用D+D-网络与电脑传输数据
单片机·嵌入式硬件
brave and determined8 小时前
MCU学习Day24——STM32G030多路ADC DMA采集深度解析:完全可配置序列器与不完全可配置序列器的陷阱与抉择
stm32·单片机·嵌入式硬件·dma·adc·hal·多通道采集
d111111111d8 小时前
通过操作地址,来进行STM32的写入GPIO端口值
stm32·单片机·嵌入式硬件