最简单直接且有效的方式就是这篇文章:使用SWO代替UART,实现printf打印功能。
实现STM32的printf输出的四种方式
四种实现方式,我认为第二种最简单可行。
方式一二三通用配置



方式一:Keil官网说明 :
https://www.keil.com/support/man/docs/jlink/jlink_trace_itm_viewer.htm

方式二 和 方式三:
需添加两个头文件stm32f10x.h 和 stdio.h,方式三为串口方式,非ITM方式,还需进行硬件资源配置,看文章了解了就顺便记录下来了。

方式四:添加源文件以及初始化文件
需添加两个头文件stm32f10x.h 和 stdio.h,这点儿是后来弄懂其它方式后才了解到的。




为什么要用ITM
有一个模块的例程代码就用到了printf,然后就想到了之前鱼鹰写的ITM方式调试,就顺势了解了一下怎么实现的,调试方式的进步可能带来工作效率大的提升。
ITM的SWO输出优势
- 硬件:不占用外设。串口是MCU的片内外设,会占用一个外设资源,影响硬件设计。
- 代码:不需要写驱动。串口的话针对不同的MCU需要重新编写串口的驱动。
- 调试:不会引入中断嵌套,也避免了此类问题。串口输出一般使用中断方式发送,调试时可能需要在中断中观察某些信息,这样中断嵌套就容易出现问题。
- 速度:SWO的速度比串口快得多。在时序要求较高的情况下对代码的影响较小。发送相同的字符串,SWO的速度是串口的8.7倍(一篇文章的测试数据)。
目标
仿真器连接单片机 与 PC,在线调试,将“Hello World”输出到PC上,在开发工具(MDK) 的某个窗口显示。
原理
单片机借助PC的显示/输入设备 实现了自己的 输出和输入。
开发环境搭建:STM32 + MDK + 实现方式(ITM机制:使用硬件接口:五线SWD)
printf -------ITM提供寄存器-------> 仿真器 -------数据-------->PC机
printf-------底层调用fputc函数-------> 重写fputc函数(实现输出设备重定向)
背景知识
printf函数属于C语言标准库函数stdio.h ,用户能定义自己的C语言库函数 fputc,连接器在连接是自动使用新的功能函数。这个过程叫做重定向C语言库函数。在这里,fputc()就似乎目标硬件与标准库函数之间的一个抽象层,可以指定目标硬件。
在Cortex-M3\M4\M7系列MCU中,内核的调试组件有一个仪器跟踪宏单元(ITM)。ITM的主要用途是调试信息的输出(例如printf格式输出)。
想在mdk中使用printf,需要同时重定义fputc函数 和 避免使用semihosting(半主机模式)。
勾选microlib后,编译的时候就不把开启semihosting的文件包进去了(一位博主的理解)。
最终效果


源码:fputc实现
方式一
#define ITM_Port8(n) (*((volatile unsigned char *)(0xE0000000+4*n)))
#define ITM_Port16(n) (*((volatile unsigned short*)(0xE0000000+4*n)))
#define ITM_Port32(n) (*((volatile unsigned long *)(0xE0000000+4*n)))
#define DEMCR (*((volatile unsigned long *)(0xE000EDFC)))
#define TRCENA 0x01000000
struct __FILE { int handle; /* Add whatever you need here */ };
FILE __stdout;
FILE __stdin;
int fputc(int ch, FILE *f) {
if (DEMCR & TRCENA) {
while (ITM_Port32(0) == 0);
ITM_Port8(0) = ch;
}
return(ch);
}
方式二
/* ITM_SendChar(ch) 为core_cm3.h中定义的内联函数,而头文件可以通过引用stm32f1xx.h包含进来 */
struct __FILE { int handle; };
FILE __stdout;
FILE __stdin;
int fputc(int ch, FILE *f) {
ITM_SendChar(ch);
return(ch);
}
方式三
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (uint8_t) ch);
/* Loop until the end of transmission */
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
{}
return ch;
}
反思自己的问题
SWD模式的trace settings 很关键,之前看的那篇文章由于最关键界面的排版位置不对,没有注意。
很长时间没用到Keil,导致将源文件添加到工程都一时间忘记了。
收获总结
认为没啥难度的事情,那就该先实现了再说,遇到问题了再了解情况,再找文章实验,再了解,几篇文章下来只要有一篇实现目标,其它的也就能理解不足在哪儿了,此时掌握了多种实现方法。有句话是这么说的:人生没有白走的路,每一步都算数。
其中最大的收获是随着了解的深入,问问题的方式不断发生转变,从多角度去探究,上网找答案,最后:山重水复疑无路,柳暗花明又一村。 这种真是,看着挺简单,不自己动手试试就不知道有多“简单”。在得到最终结果前,你不知道距离达到目标还有多远。曾经有好几次类似的经历,都无果而终,很可惜,就卡在那么一个地方,试了很多次(重复实验很多),最后不了了之,真是打击积极性,破坏成就感。不过这次我成功了,真是一种激励。
弯路如果达到目的地了就没有白走,那些弯路让你对如何达到这个结果有更深刻的认识。甚至一条路走通之后,你还能找到其它几条通往这个目的地的路。豁然贯通的舒畅。
一个地方,试了很多次(重复实验很多),最后不了了之,真是打击积极性,破坏成就感。不过这次我成功了,真是一种激励。
弯路如果达到目的地了就没有白走,那些弯路让你对如何达到这个结果有更深刻的认识。甚至一条路走通之后,你还能找到其它几条通往这个目的地的路。豁然贯通的舒畅。
本文介绍了在STM32中通过ITM(Instrumentation Trace Macrocell)实现printf输出的四种方法,重点讨论了ITM的优势,如不占用外设资源、无需编写驱动、调试友好等。作者通过实践,最终成功利用SWO输出实现了高效、快速的调试信息传输,并分享了在Keil MDK环境下配置和代码实现的关键步骤,以及在此过程中遇到的问题和解决经验。
558

被折叠的 条评论
为什么被折叠?



