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 通信机制。

相关推荐
weixin_1122333 小时前
基于STM32闭环步进电机控制系统设计说明
stm32·单片机·嵌入式硬件
机器视觉知识推荐、就业指导3 小时前
STC89C52系列单片机简介
单片机·嵌入式硬件·51单片机
qq_25929724735 小时前
STM32
嵌入式硬件
qq_2151383275 小时前
【51单片机-B020】【protues仿真】基于51单片机智能晾衣架仿真
单片机·嵌入式硬件
小猪写代码6 小时前
STM32 GPIO的八种工作模式
stm32·单片机·嵌入式硬件·gpio
1+2单片机电子设计6 小时前
STM32 单片机的停车场管理系统设计与实现
stm32·单片机·嵌入式硬件·51单片机
如愿小李7 小时前
STM32之土壤湿度传感器模块
stm32·单片机·嵌入式硬件
景彡先生9 小时前
STM32以太网开发详解:基于LwIP协议栈实现TCP/UDP通信(附网络摄像头案例)
网络·stm32·tcp/ip