4. 串口驱动实现
任务目标
实现一个基于轮询的串口驱动,支持波特率配置、单字符收发与字符串发送,为Bootloader提供与上位机通信的基础通道。驱动需在无中断、无RTOS的环境下工作,代码可直接集成至Bootloader的硬件初始化流程。
硬件依赖
以STM32F103系列(USART2)为例,其他MCU仅需替换寄存器地址与时钟使能位。需要以下外设:
- USART2:TX=PA2,RX=PA3
- RCC:使能GPIOA与USART2时钟
- GPIO:PA2推挽复用输出,PA3浮空输入
关键完整代码
// uart_drv.h
#ifndef UART_DRV_H
#define UART_DRV_H
#include <stdint.h>
void UART_Init(uint32_t baudrate);
void UART_SendByte(uint8_t ch);
uint8_t UART_ReceiveByte(void);
uint8_t UART_DataAvailable(void);
void UART_SendString(const char *str);
#endif
// uart_drv.c
#include "uart_drv.h"
#include "stm32f1xx.h"
#define USART2_BASE_ADDR 0x40004400U
#define USART2_SR (*(volatile uint32_t *)(USART2_BASE_ADDR + 0x00))
#define USART2_DR (*(volatile uint32_t *)(USART2_BASE_ADDR + 0x04))
#define USART2_BRR (*(volatile uint32_t *)(USART2_BASE_ADDR + 0x08))
#define USART2_CR1 (*(volatile uint32_t *)(USART2_BASE_ADDR + 0x0C))
#define SR_TXE (1U << 7)
#define SR_RXNE (1U << 5)
void UART_Init(uint32_t baudrate) {
// 使能时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
// GPIO配置:PA2=TX(推挽复用50MHz),PA3=RX(浮空输入)
GPIOA->CRL &= ~(0xFFU << (2 * 4)); // 清除PA2/PA3配置
GPIOA->CRL |= (0x0BU << (2 * 4)); // PA2: 1011=复用推挽
GPIOA->CRL |= (0x04U << (3 * 4)); // PA3: 0100=浮空输入
// 波特率计算:PCLK1=36MHz,OVER8=0
uint32_t pclk1 = 36000000U;
USART2_BRR = (pclk1 + baudrate / 2U) / baudrate;
// 使能USART,TX使能,RX使能,8数据位,无校验
USART2_CR1 = (1U << 13) | (1U << 3) | (1U << 2); // UE=1, TE=1, RE=1
}
void UART_SendByte(uint8_t ch) {
while (!(USART2_SR & SR_TXE)); // 等待发送数据寄存器空
USART2_DR = ch;
}
uint8_t UART_ReceiveByte(void) {
while (!(USART2_SR & SR_RXNE)); // 等待接收数据就绪
return (uint8_t)(USART2_DR & 0xFF);
}
uint8_t UART_DataAvailable(void) {
return (USART2_SR & SR_RXNE) ? 1U : 0U;
}
void UART_SendString(const char *str) {
while (*str) {
UART_SendByte((uint8_t)(*str++));
}
}
集成与调用
将上述两个文件加入Bootloader工程,在系统初始化时调用UART_Init(115200)。上位机与MCU的GND共地,TX/RX交叉连接。发送字符串示例:
UART_SendString("Bootloader Ready\r\n");
验证方法
- 编译工程,确认无错误。
- 将程序烧录至目标板,连接USB转串口模块(如CH340)至PA2/PA3。
- 打开串口助手(如PuTTY、sscom),设置波特率115200、8N1。
- 复位开发板,串口助手应显示
Bootloader Ready。 - 在串口助手中发送单字符(如
A),程序调用UART_ReceiveByte()后能正确读取;若使用轮询接收循环,可回显收到的字符。
通过条件:串口助手正确收到字符串且无乱码;发送字符后MCU能收到相同字节(可通过回显验证)。若乱码,检查波特率计算中的PCLK1值是否与系统时钟配置一致。
常见问题
- 无输出:检查GPIO复用配置是否正确,TX/RX是否交叉连接,GND是否共地。
- 乱码:确认
pclk1与RCC实际配置的APB1时钟一致;若使用HSE倍频,需按实际值修改。 - 卡死在发送循环:确认USART2_CR1的UE位已置1,且TX引脚配置为复用推挽输出。