Keil µVision 调试指南---UART#1 模拟/调试窗口 完全使用教程

💡UART#1 窗口 是 Keil 调试模式下的"虚拟串口终端",可实时查看 MCU 串口输出、模拟输入数据。无论是否拥有硬件,都能高效调试串口通信代码。

🔧 1. 核心概念

🧪 软件仿真模式

无需真实开发板,Keil 模拟 MCU 外设。UART#1 窗口直接显示 printf 或串口发送的数据,并可模拟串口输入。适合算法验证、初期调试。

无硬件 · 快速迭代

🔌 硬件调试模式

连接 J-Link/ST-Link 等调试器,真实运行目标板。通过 ASSIGN 命令将物理串口映射到 UART#1 窗口,实现输出捕获。

真实外设 · 映射显示

💻 2. 软件仿真模式 ------ 让 UART#1 显示 printf

📌 步骤 1:开启软件仿真

  • 点击菜单 Project → Options for Target (或工具栏"魔术棒"图标)。
  • 切换到 Debug 标签页 → 选中 Use Simulator → 确定。
  • Ctrl+F5 进入调试模式。
  • 通过菜单 View → Serial Windows → UART #1 打开串口窗口。

📌 步骤 2:重定向 printf → 串口1 (关键代码)

在工程中添加以下代码,将标准输出映射到 USART1,否则 printf 无法显示在 UART#1 窗口。

复制代码
/* 禁用半主机模式 (必须) */
#pragma import(__use_no_semihosting_swi)

struct __FILE { int handle; };
FILE __stdout;

void _sys_exit(int x) { 
    x = x; 
}

/* 重定向 fputc 函数 ------ 将 printf 内容发往 USART1 */
int fputc(int ch, FILE *f) {
    // 等待发送缓冲区为空 (根据芯片库函数调整)
    while (!(USART1->SR & (1<<7)));  // 示例: 检查 TXE 标志
    USART1->DR = (uint8_t)ch;
    return ch;
}

📢 提示: 如果使用标准外设库,常用写法为 while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET);

HAL 库用户可使用 while(!(USART1->ISR & USART_ISR_TXE_TXFIFOE)); 或者直接调用 HAL_UART_Transmit 实现重定向。

📌 步骤 3:编写测试代码 & 运行

复制代码
// 初始化 USART1 (波特率 115200, 8N1 等)
void UART1_Init(void) {
    // 使能时钟,配置 GPIO, 波特率等 (略)
    // 以 STM32 为例: RCC_APB2PeriphClockCmd...
    USART_Init(USART1, &USART_InitStruct);
    USART_Cmd(USART1, ENABLE);
}

int main(void) {
    UART1_Init();
    printf("Hello from Keil UART#1!\r\n");
    printf("当前仿真时间: 正常输出\r\n");
    while(1);
}

全速运行 (F5) 后,UART#1 窗口 会显示打印的字符串,效果与真实串口助手完全一致。

🌍 3. 硬件调试模式 ------ 映射物理串口到 UART#1 窗口

当连接真实开发板调试时,MCU 实际通过 TX/RX 引脚输出数据,Keil 默认不会自动抓取。使用 ASSIGN 命令建立映射关系。

⚙️ 操作流程

  • 进入硬件调试模式(Options for Target → Debug → 选择硬件调试器如 ST-Link, J-Link)。
  • 打开 View → Serial Windows → UART #1 窗口。
  • 同时打开 Command 窗口 (View → Command Window)。
  • Command 窗口输入映射命令后回车:

ASSIGN WIN1 < S0IN > S0OUT

命令解析:

  • WIN1 → 对应 UART #1 窗口。
  • S0IN / S0OUT → Keil 仿真器虚拟串口通道,S0 一般映射到 MCU 的 USART1。对于 USART2 则使用 S1IN / S1OUT
  • 执行后,真实硬件通过 USART1 发出的所有数据会实时显示在 UART#1 窗口。

注意事项: 某些较新的 Cortex-M 系列需要确保调试接口 SWO 或者串口引脚未被占用。若命令无效,检查芯片型号是否支持 ASSIGN 映射。另外也可以使用 ITM 方式,但 ASSIGN 是最直观的串口映射方法。

📡 示例:多串口映射

UART 外设 目标窗口 ASSIGN 命令
USART1 UART #1 ASSIGN WIN1 < S0IN > S0OUT
USART2 UART #2 ASSIGN WIN2 < S1IN > S1OUT
USART3 UART #3 ASSIGN WIN3 < S2IN > S2OUT

⌨️ 4. 进阶技巧:向 MCU 发送模拟数据(软件仿真)

在软件仿真下,你可以通过 Command 窗口模拟串口接收数据,用于测试协议解析、命令响应。

  • 确保处于软件仿真模式,并已打开 UART #1 窗口和 Command 窗口。
  • 在 Command 窗口输入模拟发送命令:

S0IN = 'A' // 发送字符 A

S0IN = 0x31 // 发送十六进制 0x31 (ASCII '1')

S0IN = "Hello" // 发送字符串

此时 MCU 的串口接收中断或查询方式会收到上述数据,程序可进行相应处理,便于调试 AT 指令等逻辑。

💡**提示:**硬件调试模式下也可以通过类似方式注入数据,但需要确保硬件上无电气冲突;推荐纯软件仿真时使用此功能。

5. 性能优化:加速串口输出(仿真模式)

软件仿真时串口数据传输会按位时间模拟,速度较慢。可以修改 S0TIME 参数强制让数据"瞬间"传输完毕。

S0TIME = 0

S0TIME 代表发送每一位数据需要的时间(单位:秒),默认值为 0.000104(约 9600 波特率每一位时长)。设为 0 后,输出不再延迟,极大提高调试效率。若恢复模拟真实波特率可重新赋正值。

🚀 小贴士:S0TIME = 0 对于大量 printf 调试信息尤其有用,避免长时间等待仿真输出。

6. 常见问题与排查

现象 可能原因 & 解决方案
UART#1 窗口无任何输出 1. 未重定向 fputc 或者重定向函数内未发送数据。 2. 软件仿真下未正确配置系统时钟,导致外设未使能。 3. 硬件调试下未执行 ASSIGN 映射命令。 4. 检查串口初始化代码是否真正使能了 USART。
printf 卡死/程序跑飞 多半是半主机模式问题,确保添加了 #pragma import(__use_no_semihosting_swi) 及空实现 _sys_exit
硬件映射后无输出但硬件真实串口有数据 确认调试器连接稳定,部分 MCU 需要正确配置 DBGMCU 寄存器以允许调试时串口继续工作;也可以使用逻辑分析仪对比。
Command窗口无法识别ASSIGN 请检查是否在硬件调试会话中且芯片支持。部分较老的ARM7/9支持,Cortex-M系列通常支持。也可以尝试将串口映射到 ITM 控制台。
输出乱码 波特率/时钟配置与实际仿真设置不符?仿真默认按目标时钟计算,若时钟初始化错误则乱码。检查 SystemInit 及波特率寄存器。

📁 7. 完整示例流程(软件仿真+硬件通用)

📌 快捷检查表:

  • ✔ 工程中包含 UART 初始化代码(波特率配置正确)。

  • ✔ 重定向代码已添加(对于 printf 方式)或直接使用 USART_SendData 发送,但显示到 UART#1 仍需重定向或映射。

  • ✔ 进入调试模式 → 打开 UART #1 窗口 → 如果是硬件模式则执行 ASSIGN 命令。

  • ✔ 运行程序,查看窗口实时打印。

  • ✔ 想模拟接收,软件仿真下使用 S0IN = "指令"

    // 典型初始化 + 重定向整体示例 (适用于STM32F103 标准库)
    #include "stm32f10x.h"
    #include <stdio.h>

    #pragma import(__use_no_semihosting_swi)
    struct __FILE { int handle; };
    FILE __stdout;
    void _sys_exit(int x) { x = x; }

    int fputc(int ch, FILE *f) {
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    USART_SendData(USART1, (uint8_t)ch);
    return ch;
    }

    void USART1_Config(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);
    USART_Cmd(USART1, ENABLE);
    }

    int main(void) {
    USART1_Config();
    printf("✅ Keil UART#1 窗口调试成功!\r\n");
    while(1);
    }

🎯结论: 掌握 UART#1 窗口相当于拥有一个集成在调试器里的"虚拟串口终端"。无论是代码初期仿真还是后期硬件联调,都能大幅提升串口相关功能的调试效率。灵活使用printf重定向、ASSIGN映射和S0TIME加速,让调试更加丝滑。

Keil µVision 调试技巧 · UART#1 完整指南 | 适用于 MDK v5 / v6 及主流 Cortex-M 系列

相关推荐
玩转单片机与嵌入式8 小时前
别再只把 MCU 当控制器:新一代芯片正在把 AI 推理搬到设备端
人工智能·单片机·嵌入式硬件
三佛科技-134163842128 小时前
迷你除湿机方案开发,基于FT61E145-TRB单片机方案
单片机·嵌入式硬件·物联网·智能家居
czhaii9 小时前
STC15W408AS单片机不锈钢切割机C语言
单片机·嵌入式硬件
CHINA红旗下9 小时前
如何使用vscode开发STM32
ide·vscode·stm32
嵌入式小杰10 小时前
一阶低通滤波入门教程:从原理到单片机 C 代码实现
c语言·开发语言·stm32·单片机·算法
嵌入式小杰10 小时前
一阶卡尔曼滤波入门教程:从原理到单片机 C 代码实现
c语言·单片机
济61710 小时前
FreeRTOS传感器采集任务 ——SensorTask 传感器采集任务整体实现
stm32·单片机·嵌入式·freertos
xiangw@GZ10 小时前
ADS与HFSS 全维度异同分析
嵌入式硬件