3、UART硬件设计要点:CMS32串口引脚分配、电平转换电路与硬件流控设计
做嵌入式开发这么多年,我有个很深的体会:软件上的bug顶多让你多调几天,硬件上的坑往往让你直接重做一版PCB。串口通信看似简单,但引脚分配错了、电平没匹配、流控没接对——板子回来就是废的。这一节,我把CMS32上UART硬件设计的几个关键点掰开揉碎了讲清楚。
3.1 CMS32串口引脚分配:别踩复用功能的坑
CMS32系列单片机的UART外设,通常有多个可选引脚组。我见过不少新手,拿到原理图就开始画,结果发现UART2的TX和某个SPI的SCK冲突了——这种问题在布局阶段就该规避。
以CMS32L051为例,它的UART0、UART1、UART2分别对应不同的GPIO组。我习惯在项目初期就做一张引脚分配表,把功能、复用号、备注列清楚:
| UART模块 | TX引脚 | RX引脚 | 复用功能号 | 注意事项 |
|---|---|---|---|---|
| UART0 | PA9 | PA10 | AF1 | 与I2C0_SCL共用,注意不要同时使能 |
| UART1 | PB6 | PB7 | AF2 | 与TIM1_CH1共用,PWM输出时慎用 |
| UART2 | PC0 | PC1 | AF3 | 与SPI0_NSS共用,注意片选信号 |
3.2 电平转换电路:TTL、RS232、RS485怎么选?
说白了,CMS32的UART引脚输出的是3.3V TTL电平。但你的设备可能要跟PC的DB9串口通信,或者要在工业现场拉几百米线——这时候电平转换就是必须的。
3.2.1 TTL转RS232
PC的RS232串口用±12V电平,而CMS32是3.3V。直接连?烧IO都是轻的。你需要一颗电平转换芯片,比如MAX3232或SP3232。
我常用的电路结构是这样的:
CMS32_TX → MAX3232_T1IN → MAX3232_T1OUT → DB9 Pin2 (RX)
CMS32_RX ← MAX3232_R1OUT ← MAX3232_R1IN ← DB9 Pin3 (TX)
GND → DB9 Pin5 (GND)
3.2.2 TTL转RS485
工业现场用得最多的就是RS485。它用差分信号传输,抗干扰能力强,最远能到1200米。常用的芯片有MAX3485(3.3V供电)或SP3485。
这里有个关键点:RS485是半双工的,发送和接收不能同时进行。你需要用CMS32的一个GPIO来控制收发使能。
// 伪代码示例:RS485收发切换
#define RS485_DE_RE PB0 // 控制使能引脚
void RS485_Send_Enable(void) {
GPIO_SetBits(RS485_DE_RE); // DE=1, RE=1,进入发送模式
}
void RS485_Receive_Enable(void) {
GPIO_ResetBits(RS485_DE_RE); // DE=0, RE=0,进入接收模式
}
3.3 硬件流控设计:RTS/CTS到底用不用?
很多人觉得流控是可有可无的东西。但如果你做过高速串口通信(比如921600bps以上),或者跟蓝牙模块、4G模块对接过,你就会知道——没有流控,丢数据是家常便饭。
3.3.1 什么是RTS/CTS?
简单说,就是两个设备之间握手:
- RTS(Request To Send):发送方告诉接收方“我要发数据了,你准备好了吗?”
- CTS(Clear To Send):接收方回复“我准备好了,你发吧”或者“我忙不过来,你等等”。
在CMS32上,UART的硬件流控通常由外设自动管理。你只需要把RTS和CTS引脚连好,配置好寄存器就行。
3.3.2 什么时候必须用流控?
我总结了几种场景:
| 场景 | 是否建议用流控 | 原因 |
|---|---|---|
| 波特率 ≤ 115200,短距离(<1m) | 可选 | CPU处理速度足够,软件缓冲能应付 |
| 波特率 ≥ 460800 | 强烈建议 | CPU来不及处理中断,硬件流控可以自动暂停发送 |
| 对接蓝牙/WiFi模块 | 建议 | 模块内部缓冲有限,流控可以防止溢出 |
| 多机通信(RS485总线) | 不适用 | RS485是半双工,流控由软件控制DE/RE引脚实现 |
3.3.3 CMS32硬件流控配置示例
CMS32的UART外设支持自动RTS/CTS。配置起来其实不复杂:
// 假设使用UART1,RTS=PB4,CTS=PB5
void UART1_HwFlowCtrl_Init(void) {
// 1. 使能UART1时钟
RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_UART1, ENABLE);
// 2. 配置GPIO复用功能
GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_UART1); // RTS
GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_UART1); // CTS
// 3. 配置UART参数,使能硬件流控
UART_InitTypeDef uart_init;
uart_init.UART_BaudRate = 115200;
uart_init.UART_WordLength = UART_WORDLENGTH_8B;
uart_init.UART_StopBits = UART_STOPBITS_1;
uart_init.UART_Parity = UART_PARITY_NONE;
uart_init.UART_Mode = UART_MODE_TX_RX;
uart_init.UART_HardwareFlowControl = UART_HWCONTROL_RTS_CTS; // 使能硬件流控
UART_Init(UART1, &uart_init);
// 4. 使能UART
UART_Cmd(UART1, ENABLE);
}
3.4 本章知识结构图
下面这张图把UART硬件设计的三个核心维度串起来了。你可以把它当作设计检查清单:
核心要点回顾:
- 引脚分配:先做交叉表,再画原理图,避免复用功能冲突
- 电平转换:根据通信距离和接口类型选择RS232或RS485,去耦电容不能省
- 硬件流控:高速场景和模块对接时强烈建议使用,RS485用软件控制收发使能