出处:http://www.douban.com/note/248637026/

-----------------------------------------------------------------------------------------------

作者:prife
感谢:hexlog@gmail.com
-----------------------------------------------------------------------------------------------
使用ITM机制实现调试stm32单片机,实现printf与scanf。

1. ITM简介
ITM机制是一种调试机制,是新一代调试方式,在这之前,有一种比较出名的调试方式,称为半主机(semihosting)方式。

在pc上编写过C语言的人都知道,printf可以向控制台输出,scanf可以从控制台获取输入,这里的printf/scanf都是标准库函数,利用操作系统的这些函数,我们可以很方便的调试程序。在嵌入式设备上(如stm32单片机平台上)开发工具(如MDK/IAR)也都提供了标准库函,自然也提供了printf/scanf函数,那么这些函数是否可以使用呢? 问题来了,printf向哪里输出呢?并且大部分情况下,也没有键盘,又如何使用scanf实现输入呢?

我们都知道,嵌入式设备一般的使用仿真器,如常见Jlink/ulink,可以实现烧录,单步,下断点,查看变量,等等。仿真器将PC机和单片机连接器来。聪明的设计者们就在考虑是否可以借助仿真器,使得单片机可以借助PC机的屏幕以及PC机的键盘实现printf的输出和scanf的按键获取。
也就是说,如下的hello,world程序

  1. #include <stdio.h>
  2. int main()
  3. {
  4. //硬件初始化
  5. //....
  6. printf("hello, world");
  7. for(;;);
  8. }

这个程序烧录到单片机中后,仿真器连接接单片机与PC,开始在线调试后,那么这个程序会将"Hello, world"输出到PC机上,在开发工具(MDK/IAR等)的某个窗口中显示。

这就相当于,单片机借助了PC机的显示/输入设备实现了自己的输出/输入。这种方式无疑可以方便程序开发者调试。

这种机制有多种实现方式,比较著名的就是semihosting(半主机机制)和ITM机制。
ITM是ARM在推出semihosting之后推出的新一代调试机制。现在我们来尝试一下这种方式调试。

2. stm32使用ITM调试
MCU:stm32f207VG
仿真器:Jlink V8
IDE:MDK4.50

2.1 硬件连接
ITM机制要求使用SWD方式接口,并需要连接SWO线,一般的四线SWD方式(VCC SDCLK,SDIO,GND)是不行的。标准的20针JTAG接口是可以的,只需要在MDK里设置使用SWD接口即可。

2.2 添加重定向文件
将下面的文件保存成任意C文件,并添加到工程中。这里对这个文件简单说明一下,要知道我们的程序是在单片机上运行的,为什么printf可以输出到MDK窗口里去呢?这是因为 标准库中的printf实际上调用 fputc实现输出,所以我们需要自己编写一个fputc函数,这个函数会借助ITM(类似于USART)提供的寄存器,实现数据的发送,仿真器会收到这些数据,并发往PC机。

实际上,如果你的单片机和一块LCD连接,那么你只需要重新实现fputc函数,并向LCD上输出即可,那么你调用printf时就会输出到LCD上了。这中机制,就是所谓的重定向机制。

  1. #include <stdio.h>
  2. #define ITM_Port8(n)    (*((volatile unsigned char *)(0xE0000000+4*n)))
  3. #define ITM_Port16(n)   (*((volatile unsigned short*)(0xE0000000+4*n)))
  4. #define ITM_Port32(n)   (*((volatile unsigned long *)(0xE0000000+4*n)))
  5. #define DEMCR           (*((volatile unsigned long *)(0xE000EDFC)))
  6. #define TRCENA          0x01000000
  7. struct __FILE { int handle; /* Add whatever you need here */ };
  8. FILE __stdout;
  9. FILE __stdin;
  10. int fputc(int ch, FILE *f)
  11. {
  12. if (DEMCR & TRCENA)
  13. {
  14. while (ITM_Port32(0) == 0);
  15. ITM_Port8(0) = ch;
  16. }
  17. return(ch);
  18. }

2.2 配置JLINK的初始化配置文件

将下面文件放置在你的工程下,并取任意名称,这里笔者取名为 STM32DBG.ini

  1. /******************************************************************************/
  2. /* STM32DBG.INI: STM32 Debugger Initialization File                           */
  3. /******************************************************************************/
  4. // <<< Use Configuration Wizard in Context Menu >>>                           //
  5. /******************************************************************************/
  6. /* This file is part of the uVision/ARM development tools.                    */
  7. /* Copyright (c) 2005-2007 Keil Software. All rights reserved.                */
  8. /* This software may only be used under the terms of a valid, current,        */
  9. /* end user licence from KEIL for a compatible version of KEIL software       */
  10. /* development tools. Nothing else gives you the right to use this software.  */
  11. /******************************************************************************/
  12. FUNC void DebugSetup (void) {
  13. // <h> Debug MCU Configuration
  14. //   <o1.0>    DBG_SLEEP     <i> Debug Sleep Mode
  15. //   <o1.1>    DBG_STOP      <i> Debug Stop Mode
  16. //   <o1.2>    DBG_STANDBY   <i> Debug Standby Mode
  17. //   <o1.5>    TRACE_IOEN    <i> Trace I/O Enable
  18. //   <o1.6..7> TRACE_MODE    <i> Trace Mode
  19. //             <0=> Asynchronous
  20. //             <1=> Synchronous: TRACEDATA Size 1
  21. //             <2=> Synchronous: TRACEDATA Size 2
  22. //             <3=> Synchronous: TRACEDATA Size 4
  23. //   <o1.8>    DBG_IWDG_STOP <i> Independant Watchdog Stopped when Core is halted
  24. //   <o1.9>    DBG_WWDG_STOP <i> Window Watchdog Stopped when Core is halted
  25. //   <o1.10>   DBG_TIM1_STOP <i> Timer 1 Stopped when Core is halted
  26. //   <o1.11>   DBG_TIM2_STOP <i> Timer 2 Stopped when Core is halted
  27. //   <o1.12>   DBG_TIM3_STOP <i> Timer 3 Stopped when Core is halted
  28. //   <o1.13>   DBG_TIM4_STOP <i> Timer 4 Stopped when Core is halted
  29. //   <o1.14>   DBG_CAN_STOP  <i> CAN Stopped when Core is halted
  30. // </h>
  31. _WDWORD(0xE0042004, 0x00000027);  // DBGMCU_CR
  32. _WDWORD(0xE000ED08, 0x20000000);   // Setup Vector Table Offset Register
  33. }
  34. DebugSetup();                       // Debugger Setup

这里对这个文件做简单的解释,
_WDWORD(0xE0042004, 0x00000027); // DBGMCU_CR
这一句表示想 0xE0042004地址处写入 0x000000027,这个寄存器是各个位表示的含义在注释中给出了详细的解释。 0x27即表示
        BIT0 DBG_SLEEP
        BIT1 DBG_STOP
        BIT2 DBG_STANDBY
        BIT5 TRACE_IOEN
注意,要使用ITM机制,必须要打开BIT5。

打开MDK工程,按照下图修改。

2.3 MDK中对JLINK的配置

下图中注意两点
1). 这里的CoreClock是120M,因为笔者使用的是stm32F207VG这款芯片,并且时钟配置为120M,所以这里填入120M,如果你使用stm32F10x,时钟配置成72M,那么这里需要填入72M。即需要跟实际情况保持一致。
2). 最后一定要将 0处打勾,并将其他bit位上的勾去掉,最好与此图保持一致,除CoreClock外。

2.4 烧录程序,并启动调试。可以看到,笔者在程序源码中插入了一句printf语句输出,然后按照下图,就可以看到程序的输出了。

3. 综合版本使用scanf和printf
3.1 添加retarget文件
将如下代码保存成retarget.c,然后加入到工程中。

  1. #pragma import(__use_no_semihosting_swi)
  2. struct __FILE { int handle; /* Add whatever you need here */ };
  3. FILE __stdout;
  4. FILE __stdin;
  5. int fputc(int ch, FILE *f)
  6. {
  7. return ITM_SendChar(ch);
  8. }
  9. volatile int32_t ITM_RxBuffer;
  10. int fgetc(FILE *f)
  11. {
  12. while (ITM_CheckChar() != 1) __NOP();
  13. return (ITM_ReceiveChar());
  14. }
  15. int ferror(FILE *f)
  16. {
  17. /* Your implementation of ferror */
  18. return EOF;
  19. }
  20. void _ttywrch(int c)
  21. {
  22. fputc(c, 0);
  23. }
  24. int __backspace()
  25. {
  26. return 0;
  27. }
  28. void _sys_exit(int return_code)
  29. {
  30. label:
  31. goto label;  /* endless loop */
  32. }

3.2 编译运行
编译,烧录,运行,打开Debug (printf) viewer,就可以看到输入,参看下图

这里对retarget.c文件做几点说明.
1). 上面的代码实际是在X:\Keil\ARM\Startup\Retarget.c上修改而成的,scanf依赖的函数共有两个,fgetc和__backspace都需要实现,如果缺少__backespace函数,则scanf胡无法从Debug Viewer Dialog 窗口获取输入。另外上面提供的代码只是个demo,用于演示效果,用于生产时应该处理的更完善一些。见参考文献[1]

2). 函数ITM_SendChar,ITM_CheckChar,ITM_ReceiveChar在库文件CMSIS\Include\core_cm3.h中。

3) 查看函数的符号引用关系,可以通过生成详细的map文件来查看。命令行增加 --verbose --list rtt.map选项即可生成名为rtt.map的文件。

4. ITM与RTT结合(待实现)
grissiom 写道:
忽然想到,或许可以把这个半主机做成 device,然后 rt_console_set_device("semi") 就可以直接用半主机做 finsh/rt_kprintf 了…… 不知可行不可行……

prife: ITM的接收不知道是否支持中断,目前接收字符使用是轮询方式。如果是中断才有意义。这样可以把ITM设备做成一个 rtt 的device了,让finsh跑在 Debug printf Viewer窗口上。以后只要接一个jtag/SWD口就可以调试了,不用再接串口线了

参考文献
[1] MDK help. Indirect semihosting C library function dependencies
[2] MDK help ARM Development Tools.
         Debugger Adapter User's Guides
             J-Link/J-Trace User's Guide
         Libraries and Floating Point Support Referencee
         Libraries and Floating Point Support Guide

Linker Reference Guide

【转】keil+stm32+jlink利用swd方式进行printf输出的更多相关文章

  1. keil下JLINK在线调试仿真设置,SWD连接

    keil下JLINK在线调试仿真设置,以下三个步骤搞定: 有时我们编译时会遇到空间不足的情况,首先我们应该把 flash和RAM的size 设置为当前所用芯片的大小,如下我使用了一个片上flash 2 ...

  2. STM32烧录的常用方式

    stm32烧录常用的方式一般为ST-LINK(或者J-tag)下载仿真和ISP下载 一.仿真器下载 仿真器分为J-TAG和SWD仿真,SWD仿真只需要4根线(VCC.GND.CLK.DATA)就可以了 ...

  3. 利用ADO方式连接SQLServer2008出现的问题

    在利用ADO方式连接SQLServer2008的过程中遇到了很多问题,在网上也没有找到许多有利的信息,花了两天时间,终于把所有问题都搞定了.在这里和大家分享一下经验,希望后来者能少走弯路. 很多教程说 ...

  4. java 利用HttpURLConnection方式获取restful格式的服务数据

    /** * @Author: * @Description:利用HttpURLConnection方式获取restful格式的服务数据 * @Date: */ private static List& ...

  5. Cloudera Manager安装之利用parcels方式安装3或4节点集群(包含最新稳定版本或指定版本的安装)(添加服务)(CentOS6.5)(五)

    参考博客 Cloudera Manager安装之利用parcels方式安装单节点集群  Cloudera Manager安装之Cloudera Manager 5.3.X安装(三)(tar方式.rpm ...

  6. Cloudera Manager安装之利用parcels方式安装单节点集群(包含最新稳定版本或指定版本的安装)(添加服务)(CentOS6.5)(四)

    不多说,直接上干货! 福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号:   大数据躺过的坑      Java从入门到架构师      人工智能躺过的坑          ...

  7. Cloudera Manager安装之利用parcels方式(在线或离线)安装3或4节点集群(包含最新稳定版本或指定版本的安装)(添加服务)(Ubuntu14.04)(五)

    前期博客 Cloudera Manager安装之Cloudera Manager 5.6.X安装(tar方式.rpm方式和yum方式) (Ubuntu14.04) (三) 如果大家,在启动的时候,比如 ...

  8. ubuntu 14.04 下利用apt-get方式安装opencv

    转载,请注明出处:http://blog.csdn.net/tina_ttl 目录(?)[+] 标签(空格分隔): Linux学习 OpenCV ubuntu 1404 下利用apt-get方式安装O ...

  9. 【stm32】实现STM32的串口数据发送和printf函数重定向

    在调试电机驱动程序的时候,是不能随便利用中断来进行一些寄存器或数据的查看的,不然你在运行的时候突然来一下,如果占空比大的话那可能直接就把MOS管给烧了,所以我们很多情况下只能使用USART(串口)来进 ...

随机推荐

  1. 把crosswalk打包到cordova项目中

    (1)从Crosswalk官网下载Cordova Android (ARM) 点击下载 (2)解压压缩包到任意目录 (3)创建工程 cordova工程 cordova create Crosswalk ...

  2. Sliverlight 样式

    UserControl 页面级样式UserControl.Resources style setter Property value. TargetType 应用的类型 使用 style static ...

  3. 使用xib创建cell时 bug

    UITableView (<UITableView: 0x15799a800; frame = (0 4797; 375 733); clipsToBounds = YES; tag = 305 ...

  4. [原]centos6.5系统可用yum源(32位)以及rpmforge

    [10gen] name=10gen Repository baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/i686 gpgche ...

  5. SQL注入的字符串连接函数

    在select数据时,我们往往需要将数据进行连接后进行回显.很多的时候想将多个数据或者多行数据进行输出的时候,需要使用字符串连接函数.在sqli中,常见的字符串连接函数有concat(),group_ ...

  6. 用 Express4 写一个简单的留言板

    Knowledge Dependence:阅读文本前,你需要熟悉 Node.js 编程.Express 以及相关工具和常用中间件的使用. Node.js 以其单线程异步非阻塞的特点,越来越被广大的 W ...

  7. datatables ajax后端请求那些坑

    在对datatables做后端数据填充的时候,遇到一个,翻页问题.在多次操作翻页后,总是提示加载中.反了很多博客没有找到原因. 经过测试,发现原来自己坑了自己. 代码如下: datatables : ...

  8. CLR via C# 3rd - 02 - Building, Packaging, Deploying, and Administering Applications and Types

    1. C# Compiler - CSC.exe            csc.exe /out:Program.exe /t:exe /r:MSCorLib.dll Program.cs       ...

  9. ipython安装

    sudo pip install jupyter 然后用jupyter notebook来运行

  10. 个人制作-css+html旋转立方体的制作

    源代码: <!DOCTYPE html><html><head>    <title></title>    <meta charse ...