STM32N6 在 CubeIDE 中实现 printf 重定向到 SWV-ITM,核心是通过重写__io_putchar函数、配置 SWO 引脚与 ITM 寄存器、匹配 IDE 调试参数,无需占用 UART 等外设 IO,仅用 SWD 链路即可输出调试信息。关键解决点:补全寄存器使能与引脚复用配置,避免因缺失关键步骤导致重定向失败。
1. 核心优势与适用场景
1.1 为什么选 SWV-ITM 重定向?
- 节省 IO 资源:无需占用 UART、SPI 等外设引脚,适合 IO 紧张的 NPU 相关开发场景;
- 调试便捷:依托 SWD 调试链路传输数据,无需额外硬件(如 USB 转串口);
- 低侵入性:不影响原有外设功能,仅通过 ITM(指令跟踪宏单元)输出打印信息。
1.2 重定向核心链路
printf 函数的底层调用逻辑为:printf() → _write()(syscalls.c内置) → __io_putchar() → ITM_SendChar() → SWV-ITM传输关键是重写__io_putchar函数,将字符通过 ITM 寄存器发送到 SWO 引脚,再由调试器接收并显示。
2. 实操步骤:5 步完成重定向(STM32N6-DK 为例)
2.1 步骤 1:重写__io_putchar 函数(核心代码)
在main.c中添加函数,将字符转发到 ITM 接口:
#include "stm32n6xx_hal.h"
#include <stdio.h>
// 重写__io_putchar,对接ITM发送字符
int __io_putchar(int ch) {
ITM_SendChar(ch); // ITM核心发送函数
return ch;
}
2.2 步骤 2:使能 DBGMCU 与 ITM 寄存器
在main函数的USER CODE BEGIN Init中添加代码,开启调试跟踪与 ITM 端口:
/* USER CODE BEGIN Init */
// 使能Trace Clock和Debug Clock(DBGMCU_CR寄存器配置)
DBGMCU->CR |= 0x00300000;
// 使能ITM Port 0(仅需启用Port 0即可满足printf输出)
ITM->TER |= 0x1;
// 使能ITM全局功能
ITM->TCR |= 0x00000001;
/* USER CODE END Init */
2.3 步骤 3:配置 SWO 引脚(PB5 复用 TRACESWO)
STM32N6 的 SWO 引脚为 PB5(复用功能 AF0_TRACE),需启用 GPIO 时钟并配置复用模式:
/* USER CODE BEGIN Init */
// 前面已添加的寄存器配置...
// 1. 使能GPIOB时钟(GPIOB挂在AHB4总线)
__HAL_RCC_GPIOB_CLK_ENABLE();
// 2. 配置PB5为TRACESWO复用模式
GPIO_InitTypeDef gpio_init = {0};
gpio_init.Pin = GPIO_PIN_5;
gpio_init.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
gpio_init.Pull = GPIO_PULLUP; // 上拉
gpio_init.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
gpio_init.Alternate = GPIO_AF0_TRACE; // 复用为TRACESWO
HAL_GPIO_Init(GPIOB, &gpio_init);
/* USER CODE END Init */
2.4 步骤 4:CubeIDE 调试配置(SWV 参数匹配)
- 点击「Run」→「Debug Configurations」,选中当前工程的调试配置,切换到「Debugger」选项卡;
- 勾选「Enable Serial Wire Viewer (SWV)」,设置「Core Clock (MHz)」:
- 需与 STM32N6 的 TPIU 时钟一致(示例中 SYSA=400MHz,TPIU=50MHz,故设为 50.0);
- 点击「Apply」保存配置。
2.5 步骤 5:启动 SWV 数据控制台
- 启动调试(Debug),待程序暂停后,点击 IDE 底部「SWV ITM Data Console」;
- 点击控制台右上角「Configure Trace」,勾选「ITM Stimulus Ports」中的 Port 0(与代码中
ITM->TER |= 0x1对应);
- 点击「Start Trace」,运行程序,printf 信息将实时显示在控制台中。
3. 关键注意点(避坑核心)
- SWO 引脚唯一性:STM32N6-DK 的 SWO 固定为 PB5(AF0_TRACE),需按数据手册确认引脚,不可随意替换;
- 总线时钟必须使能:GPIOB 挂在 AHB4 总线,需通过
__HAL_RCC_GPIOB_CLK_ENABLE()启用时钟,否则引脚配置无效;
- Core Clock 匹配:若配置的 Core Clock 与 TPIU 时钟不一致,会导致数据乱码或无输出,需参考时钟树确认 TPIU 频率;
- syscalls.c 文件:工程需包含
syscalls.c(CubeIDE 创建工程时默认生成),否则_write()函数缺失,重定向失败。
4. 完整代码片段(main.c 关键部分)
#include "stm32n6xx_hal.h"
#include <stdio.h>
// 重写__io_putchar函数
int __io_putchar(int ch) {
ITM_SendChar(ch);
return ch;
}
int main(void) {
HAL_Init();
/* USER CODE BEGIN Init */
// 1. 使能DBGMCU和ITM寄存器
DBGMCU->CR |= 0x00300000;
ITM->TER |= 0x1;
ITM->TCR |= 0x00000001;
// 2. 配置SWO引脚PB5
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitTypeDef gpio_init = {0};
gpio_init.Pin = GPIO_PIN_5;
gpio_init.Mode = GPIO_MODE_AF_PP;
gpio_init.Pull = GPIO_PULLUP;
gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
gpio_init.Alternate = GPIO_AF0_TRACE;
HAL_GPIO_Init(GPIOB, &gpio_init);
/* USER CODE END Init */
SystemClock_Config();
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
printf("STM32N6 SWV-ITM printf测试!rn");
printf("当前系统时钟:%d MHzrn", HAL_RCC_GetHCLKFreq()/1000000);
/* USER CODE END 2 */
while (1) {
HAL_Delay(1000);
printf("循环打印:%drn", __HAL_TIM_GET_COUNTER(&htim1)); // 示例:打印定时器值
}
}
STM32N6 的 SWV-ITM 重定向核心是 “代码配置 + IDE 参数匹配”:代码层需补全寄存器使能与引脚复用,IDE 层需确保 SWV 时钟与硬件一致。该方案无需额外 IO,适合 NPU 开发等 IO 紧张场景,调试效率高且侵入性低。