CW32开发板UART3串口通信与scanf重定向实战

发布时间:2026/7/17 5:19:10
CW32开发板UART3串口通信与scanf重定向实战 1. CW32饭盒派开发板与UART3串口基础CW32饭盒派开发板作为一款面向嵌入式开发的硬件平台其扩展板上的UART3串口是开发者与外部设备通信的重要接口。在实际项目中串口通信往往承担着调试信息输出、设备控制指令传输等关键功能。UARTUniversal Asynchronous Receiver/Transmitter是一种异步串行通信协议它通过TX发送和RX接收两根数据线实现全双工通信。CW32的UART3与其他串口相比具有以下特点独立的中断向量和DMA通道支持硬件流控RTS/CTS可配置的波特率最高可达3Mbps灵活的时钟源选择注意使用UART3前需确认扩展板的跳线帽设置正确避免与其他外设引脚冲突。2. 硬件连接与初始化配置2.1 扩展板物理接口识别CW32饭盒派扩展板的UART3接口通常标记为UART3_TX数据发送引脚连接外部设备的RXUART3_RX数据接收引脚连接外部设备的TXGND必须连接的共地线典型连接方式如下表所示CW32扩展板引脚外部设备引脚线缆颜色建议UART3_TXRX绿色UART3_RXTX黄色GNDGND黑色2.2 寄存器级初始化代码在CW32标准外设库中UART3的初始化需要配置以下关键参数void UART3_Init(uint32_t baudrate) { GPIO_InitTypeDef GPIO_InitStruct {0}; UART_InitTypeDef UART_InitStruct {0}; // 时钟使能 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART3, ENABLE); // GPIO配置 GPIO_InitStruct.Pin GPIO_Pin_10; // UART3_TX GPIO_InitStruct.Mode GPIO_Mode_AF_PP; GPIO_InitStruct.Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStruct); GPIO_InitStruct.Pin GPIO_Pin_11; // UART3_RX GPIO_InitStruct.Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOB, GPIO_InitStruct); // UART参数配置 UART_InitStruct.BaudRate baudrate; UART_InitStruct.WordLength UART_WordLength_8b; UART_InitStruct.StopBits UART_StopBits_1; UART_InitStruct.Parity UART_Parity_No; UART_InitStruct.HWFlowControl UART_HWFlowControl_None; UART_InitStruct.Mode UART_Mode_Tx | UART_Mode_Rx; UART_Init(UART3, UART_InitStruct); // 使能UART UART_Cmd(UART3, ENABLE); }3. scanf函数重定向实现3.1 重定向原理分析在标准库中scanf函数默认从stdin标准输入读取数据。在嵌入式环境中我们需要将其重定向到UART串口。这通过重写__io_getchar()函数实现// 重写底层获取字符函数 int __io_getchar(void) { while(UART_GetFlagStatus(UART3, UART_FLAG_RXNE) RESET); return (int)UART_ReceiveData(UART3); }3.2 完整重定向方案为确保重定向稳定可靠需要实现以下三个关键函数// 发送字符函数用于printf int _write(int file, char *ptr, int len) { for(int i0; ilen; i){ while(UART_GetFlagStatus(UART3, UART_FLAG_TXE) RESET); UART_SendData(UART3, (uint8_t)ptr[i]); } return len; } // 获取字符函数用于scanf int _read(int file, char *ptr, int len) { int ch; for(int i0; ilen; i){ ch __io_getchar(); *ptr ch; if(ch \r || ch \n) break; } return len; } // 文件描述符重定向 __attribute__((weak)) int __io_putchar(int ch) { while(UART_GetFlagStatus(UART3, UART_FLAG_TXE) RESET); UART_SendData(UART3, (uint8_t)ch); return ch; }4. 调试技巧与常见问题排查4.1 波特率不匹配问题当出现乱码时首先检查双方设备的波特率是否精确一致系统时钟配置是否正确是否存在时钟分频误差可通过以下代码验证波特率void Check_BaudRate(void) { uint32_t computed (SystemCoreClock / (UART3-BRR)); printf(实际波特率%lu\n, computed); }4.2 数据丢失解决方案若发现数据接收不完整可采取以下措施启用接收中断而非轮询方式增加硬件流控RTS/CTS使用DMA传输调整接收缓冲区大小中断配置示例void UART3_IRQHandler(void) { if(UART_GetITStatus(UART3, UART_IT_RXNE) ! RESET){ uint8_t ch UART_ReceiveData(UART3); // 处理接收数据 UART_ClearITPendingBit(UART3, UART_IT_RXNE); } }5. 进阶应用自定义输入输出控制5.1 输入超时机制实现增强scanf的健壮性添加超时检测int Timeout_GetChar(uint32_t timeout) { uint32_t tickstart GetTickCount(); while(UART_GetFlagStatus(UART3, UART_FLAG_RXNE) RESET){ if((GetTickCount() - tickstart) timeout){ return -1; // 超时 } } return (int)UART_ReceiveData(UART3); }5.2 命令解析器集成结合重定向功能实现简单CLIvoid CLI_Process(void) { char cmd[64]; while(1){ printf( ); scanf(%63s, cmd); if(strcmp(cmd, help) 0){ printf(Available commands:\n); printf(help - show this message\n); printf(info - show system info\n); } else if(strcmp(cmd, info) 0){ printf(CW32F030 System Info:\n); printf(Core Clock: %lu Hz\n, SystemCoreClock); } } }6. 性能优化建议6.1 减少串口传输延迟使用DMA传输替代中断方式void UART3_DMA_Init(void) { DMA_InitTypeDef DMA_InitStruct; // 发送DMA配置 DMA_InitStruct.DMA_PeripheralBaseAddr (uint32_t)UART3-TDR; DMA_InitStruct.DMA_MemoryBaseAddr (uint32_t)tx_buffer; DMA_InitStruct.DMA_DIR DMA_DIR_PeripheralDST; DMA_InitStruct.DMA_BufferSize 0; DMA_InitStruct.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStruct.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte; DMA_InitStruct.DMA_MemoryDataSize DMA_MemoryDataSize_Byte; DMA_InitStruct.DMA_Mode DMA_Mode_Normal; DMA_InitStruct.DMA_Priority DMA_Priority_High; DMA_InitStruct.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel2, DMA_InitStruct); UART_DMACmd(UART3, UART_DMAReq_Tx, ENABLE); }优化printf浮点数输出#pragma import(__use_no_semihosting) // 避免半主机模式开销6.2 电源管理集成在低功耗应用中可动态开关串口时钟void Enter_LowPowerMode(void) { UART_Cmd(UART3, DISABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART3, DISABLE); // 进入低功耗模式 } void Exit_LowPowerMode(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART3, ENABLE); UART_Cmd(UART3, ENABLE); }