4. Bootloader检测启动模式引脚

任务目标

实现一个基于轮询的串口驱动,支持波特率配置、单字符收发与字符串发送,为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");

验证方法

  1. 编译工程,确认无错误。
  2. 将程序烧录至目标板,连接USB转串口模块(如CH340)至PA2/PA3。
  3. 打开串口助手(如PuTTY、sscom),设置波特率115200、8N1。
  4. 复位开发板,串口助手应显示Bootloader Ready。
  5. 在串口助手中发送单字符(如A),程序调用UART_ReceiveByte()后能正确读取;若使用轮询接收循环,可回显收到的字符。

通过条件:串口助手正确收到字符串且无乱码;发送字符后MCU能收到相同字节(可通过回显验证)。若乱码,检查波特率计算中的PCLK1值是否与系统时钟配置一致。

常见问题

  • 无输出:检查GPIO复用配置是否正确,TX/RX是否交叉连接,GND是否共地。
  • 乱码:确认pclk1与RCC实际配置的APB1时钟一致;若使用HSE倍频,需按实际值修改。
  • 卡死在发送循环:确认USART2_CR1的UE位已置1,且TX引脚配置为复用推挽输出。