3. 串口发送字符串的分层封装
任务定义
在单片机裸机开发中,上层业务代码(如日志输出、命令解析)需要发送字符串,但不应直接操作UART寄存器。本案例要求封装一个串口发送模块,对外仅暴露两个接口:uart_send_string()和uart_send_byte(),上层调用者无需了解任何寄存器细节。底层基于STM32F103的USART1实现,通过查询方式发送(非中断),以最小代码量完成分层隔离。
分层结构
整个设计分为两层:驱动层(uart_driver.c/h)负责寄存器操作;服务层(uart_service.c/h)负责字符串处理逻辑。上层业务只包含uart_service.h,不直接包含驱动头文件,从而在编译层面强制隔离寄存器访问。
驱动层实现
// uart_driver.h
#ifndef UART_DRIVER_H
#define UART_DRIVER_H
#include "stm32f1xx.h"
void uart_driver_init(void);
void uart_driver_send_byte(uint8_t ch);
#endif
// uart_driver.c
#include "uart_driver.h"
void uart_driver_init(void)
{
// 使能USART1和GPIOA时钟
RCC->APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_IOPAEN;
// PA9=TX(推挽复用), PA10=RX(浮空输入)
GPIOA->CRH &= ~(GPIO_CRH_CNF9_Msk | GPIO_CRH_MODE9_Msk);
GPIOA->CRH |= (GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9_1 | GPIO_CRH_MODE9_0); // 50MHz复用推挽
GPIOA->CRH &= ~(GPIO_CRH_CNF10_Msk | GPIO_CRH_MODE10_Msk);
GPIOA->CRH |= GPIO_CRH_CNF10_0; // 浮空输入
// 115200-8-N-1 (PCLK2=72MHz)
USART1->BRR = 0x2710; // 72MHz/115200 = 625 = 0x271
USART1->CR1 = USART_CR1_UE | USART_CR1_TE;
}
void uart_driver_send_byte(uint8_t ch)
{
// 等待发送数据寄存器空
while (!(USART1->SR & USART_SR_TXE));
USART1->DR = ch;
}
服务层实现
// uart_service.h
#ifndef UART_SERVICE_H
#define UART_SERVICE_H
#include <stdint.h>
void uart_service_init(void);
void uart_send_byte(uint8_t ch);
void uart_send_string(const char *str);
#endif
// uart_service.c
#include "uart_service.h"
#include "uart_driver.h"
void uart_service_init(void)
{
uart_driver_init();
}
void uart_send_byte(uint8_t ch)
{
uart_driver_send_byte(ch);
}
void uart_send_string(const char *str)
{
if (str == 0) return;
while (*str != '\0') {
uart_driver_send_byte((uint8_t)(*str));
str++;
}
}
上层调用示例
// main.c 片段
#include "uart_service.h"
int main(void)
{
uart_service_init();
while (1) {
uart_send_string("Hello from UART layer\r\n");
for (volatile uint32_t i = 0; i < 2000000; i++); // 简单延时
}
}
接口隔离验证
上层main.c只包含uart_service.h,不包含uart_driver.h。若上层代码尝试直接访问USART1->DR或调用uart_driver_send_byte(),编译器将报错(未声明标识符),从编译层面保证分层不被破坏。
工程文件与编译
需要添加的文件:uart_driver.c、uart_service.c、main.c。头文件路径需包含上述三个.h所在目录。使用STM32标准外设库或直接寄存器方式均可,本实现不依赖HAL库,仅需CMSIS头文件stm32f1xx.h。
验证方法与判定标准
硬件验证:将PA9(TX)连接至USB转TTL模块的RX,PC端打开串口助手(115200-8-N-1)。上电后应持续收到字符串Hello from UART layer,每行以回车换行结尾。
判定标准:
- 串口助手能稳定收到预期字符串,无乱码、无丢失;
- 在
main.c中尝试包含uart_driver.h并直接操作USART1->DR,编译应报错,证明接口隔离有效; - 将
uart_send_string()的参数改为NULL,程序不崩溃(空指针保护生效)。