本帖最后由 shanyuxiang 于 2023-10-15 16:09 编辑
#申请原创# @21小跑堂APM32串口打印输出的几种方法
通过printf()格式化输出调试信息是一种常用的调试手段,下面介绍一下几种实现printf()的方法。
●通过串口UART输出
●通过Jlink的SWD接口
●通过Jlink的SWO管脚
关于JTAG - SWD接口的详细资料,可以查看这里:
https://www.segger.com/products/debug-probes/j-link/technology/interface-description/
这几种方式使用的硬件管脚和上位机都有所不同,各有优劣,可以根据实际情况进行选择。
下面以APM32E103为例进行讲解,E103是F103的增强型,能支持到120MHz主频,其他方面并没什么不同,
对于我们来说和其他Cortex M单片机类似,以下方法同样可以用在其他的Geehy APM32芯片上面。
为方便测试,这次使用 “APM32E103 MINI”开发板和“Jink V9”调试器进行实验,该开发板板载串口RS232,
同时带全功能的JTAG接口,包含了SWDIO、SWCLK、SWO,非常方便进行这次的实验。
一、通过串口UART输出
这是最常用的一种方法,通过重定义 fputc 然后调用C语言的标准库 printf 函数来输出调试信息,
这种方式适应性广,不光是ARM,其他芯片也可以用,只要有串口就行。
1.1、硬件连接
这里需要使用到 PA9-USART1_TX,使用USB转RS232线将电脑与DB9接口相连,也可以用USB转串口TTL线将PA9与电脑相连。
1.2、代码实现
1.2.1、初始化串口
只需要USART1的发送,所以只初始化PA9,只开启发送使能。
void usart_printf_init()
{
GPIO_Config_T GPIO_ConfigStruct;
USART_Config_T USART_ConfigStruct;
RCM_EnableAPB2PeriphClock((RCM_APB2_PERIPH_T)(RCM_APB2_PERIPH_GPIOA | RCM_APB2_PERIPH_USART1));
GPIO_ConfigStruct.mode = GPIO_MODE_AF_PP;
GPIO_ConfigStruct.pin = GPIO_PIN_9;
GPIO_ConfigStruct.speed = GPIO_SPEED_50MHz;
GPIO_Config(GPIOA, &GPIO_ConfigStruct);
USART_ConfigStruct.baudRate = 115200;
USART_ConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE;
USART_ConfigStruct.mode = USART_MODE_TX;
USART_ConfigStruct.parity = USART_PARITY_NONE;
USART_ConfigStruct.stopBits = USART_STOP_BIT_1;
USART_ConfigStruct.wordLength = USART_WORD_LEN_8B;
USART_Config(USART1, &USART_ConfigStruct);
USART_Enable(USART1);
}
1.2.2、重定义 fputc
在fputc()函数中添加单个字节的发送代码。
int fputc(int ch, FILE *f)
{
while(USART_ReadStatusFlag(USART1, USART_FLAG_TXBE) == RESET);
USART_TxData(USART1, ch);
return ch;
}
1.2.3、勾选“Micro LIB” 或者定义 __stdout
完成了以上步骤,串口还不能输出信息,需要下面的步骤,下面两种方式二选一即可。
●启用微库,方法如下
从菜单栏依次点开: Projec -> Options for Target ' ' -> Target -> 勾选 Use Micro LIB。
●如果不想勾选 Use Micro LIB,则添加如下代码。
struct __FILE
{
int handle;
};
FILE __stdout;
1.3、 测试效果
在程序中调用printf, 为了一直能看到调试信息,在while(1)中每隔1秒输出一个递增的计数值,
while(1)
{
Delay();
printf("%03d\r\n",LoopCount);
LoopCount++;
}
打开串口调试助手,下载程序复位并运行,可以在上位机的接收窗口看到打印输出的信息。
二、通过Jlink的SWD接口
SWD是一种很常见的调试接口,只有两根线: 双向数据SWDIO、时钟SWCLK,除了用来调试,也可以用来作串口打印。
配合上位机“JLinkRTTViewer.exe”也可以实现和printf()一样的功能。
2.1、硬件连接
通过数据手册可看出: SWDIO与PA13相连、SWCLK与PA14相连 。
2.2、代码实现
2.2.1、添加SEGGER_RTT源代码
这里用到了SEGGER的RTT技术,在Jlink的驱动安装目录"SEGGER\JLink_V600d\Samples\RTT\"下可以找到一个压缩包,解压得到文件夹“RTT”,
把RTT文件夹及其内部的源文件复制到我们的Keil工程目录下,添加这两个源文件。
2.2.2、调用 SEGGER_RTT_printf()
在要调用 SEGGER_RTT_printf() 的C文件中包含头文件:
调用 SEGGER_RTT_printf()函数,其中第1个参数0代码端口,选0即可,后面的参数与printf()函数一样。
SEGGER_RTT_printf(0,"APM32E103 printf test(SWD RTT)\r\n");
2.3、测试效果
打开 Jlink安装目录的工具 "SEGGER\JLink_V600d\JLinkRTTViewer.exe",按照下面这样设置,
目标芯片"Specify Target Device"因为这里没有Gheey,所以选择相似的 STM32F103VE。
连接Jlink,运行程序,可以看到“JLinkRTTViewer”的窗口中显示出了要打印输出的信息。
三、通过Jlink的SWO管脚
SWD模式的SWO脚是用来输出调试信息的,这个信号是可选的并不是SWD所必需的。
3.1、硬件连接
APM32E103是带有SWO脚的,SWO与PB3相连。
3.2、代码实现
3.2.1、使能跟踪调试引脚
参考用户手册“11 调试 MCU(DBGMCU)”章节 , 有对“DBGMCU_CFG”寄存器的描述,先使能“TRACE_IOEN”位。
DBGMCU->CFG |= (1<<5); //TRACE_IOEN 1:使能跟踪调试引脚
3.2.2 重定义 fputc
这里也是实现单个数据的发送,涉及到调试单元DBGMCU,以及内核的ITM,相关寄存器定义在 apm32e10x_dbgmcu.c 和 core_cm3.h 中。
int fputc(int ch, FILE *f)
{
if(CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)
{
while(ITM->PORT[0].u32 == 0);
ITM->PORT[0].u8 = ch;
}
return(ch);
}
3.2.3 勾选“Micro LIB” 或者定义 __stdout
参考前面的1.2.3章节:
1.2.3、勾选“Micro LIB” 或者定义 __stdout
3.3、测试效果
打开 Jlink安装目录的工具 "SEGGER\JLink_V600d\JLinkSWOViewer.exe",按照下面这样设置,
目标芯片"Specify Target Device"因为这里没有Gheey,所以选择相似的 STM32F103VE。
连接Jlink,运行程序,可以看到“JLinkSWOViewer.exe”的窗口中显示出了要打印输出的信息。
四、总结
下面一张表格总结以上三种方式的特点:
这三种打印输出调试信息的方式都经过实践检验,附件中是相关源代码,通过宏定义"PRINTF_SELECT"可选择其他一种进行实验。
|
介绍单片机三种输出调试信息的方法,对比各种方式的特点和使用方式。(添加蓝V认证可提升打赏额度,继续输出原创,联系管理员升级权限吧!)