基于MDK的万能printf方法!

嵌入式大杂烩 2023-03-24 21:30


【说在前面的话】

你听说过J-LinkRTT么?官方的宣传是这样的:

简单来说,只要拥有了J-Link,你就可以享受以下的便利:
  • 无需占用USART或者USB转串口工具
    ,将
    printf
    重定位到一个由J-LINK提供的虚拟串口上;


  • 支持任何J-LINK声称支持的芯片


  • 高速通信,不影响芯片的实时响应

它的缺点也是明显的:
  • 你必须拥有一个J-Link
    ,如果你使用的是 
    CMSIS-DAP
    或者
    ST-Link
    之类的第三方调试工具,就无法享受这一福利;
  • 你必须在工程中手动插入一段代码



曾几何时,J-Link的这一福利让多少非J-Link用户羡慕嫉妒恨,看看手中的ST-LinkULINKpro和各类廉价的CMSIS-DAP板载调试器——“隔壁邻居的小孩都馋哭了”

如果我告诉你,其实MDK中内置了一种非常简单廉价的方式,可以让你实现类似的功能,并具有以下特点:
  • 支持所有的调试仿真器,哪怕自己手搓的CMSIS-DAP都行

  • MDK

    原生功能,连CMSIS-Pack都不用安装

  • 点几下鼠标就可以通过
    RTE
    完成部署
  • 除了简单的初始化函数外,
    无需手动插入代码
  • 可以将你的printf输出直接打印在MDK的Debug (printf) View窗口中


你是否心动了呢?


【部署从未如此简单】

步骤一:RTE配置

依次通过菜单 Project->Manage->Run-Time Environment 打开RTE配置窗口:

找到并展开Compiler选项卡,勾选Event Recorder,并确保Variant下拉列表选中的是默认的DAP

展开 Compiler 下的 I/O ,勾选STDOUT,并在 Variant 下拉列表中选择 EVR——这里 EVREvent Recorder 的缩写。单击确定后,我们会在工程管理器中看到以下的内容:

至此,所需的工具都已经成功地加入到工程中了。


虽然这里EventRecorderConf.h 是一个可以编辑的状态,但实践中,我们基本不用去碰他——使用默认配置即可。



步骤二:服务初始化

在包含 main() 函数的C代码文件中,按照如下的格式添加对头文件的包含:

#include #if defined(RTE_Compiler_EventRecorder)#   include #endif
main() 函数中添加对EventRecorder服务的初始化:
void main(void){    ...#if defined(RTE_Compiler_EventRecorder) \ && defined(RTE_Compiler_IO_STDOUT_EVR)    EventRecorderInitialize(0, 1);#endif    ...}
如果你从未使用过EventRecorder也不必惊慌,这段代码的主要作用是为printf专门开启一个数据通道。

理论上,到这里,我们就已经完成了部署,可以在进入调试模式后,通过MDKDebug (printf) View窗口来观察 printf 的输出结果了。比如,我们在 main() 函数中打印一个 "hello world\r\n":

#include 
#include #if defined(RTE_Compiler_EventRecorder)# include #endif
void main(void){ ...#if defined(RTE_Compiler_EventRecorder) \ && defined(RTE_Compiler_IO_STDOUT_EVR) EventRecorderInitialize(0, 1);#endif ...
printf("Hello World\r\n"); ...}

编译,一切顺利的话,进入调试模式后通过菜单 View->Serial Windows->Debug (printf) View 打开窗口:

运行后,可以在 Debug (printf) View窗口中看到如下的结果:


【常见问题】

如果你的工程中从未提供过对 ".bss.noinit" 数据段的处理,那么很可能会发现通过上述方法实现的 printf 输出似乎不是很稳定——时有时无——处于一种薛定谔的状态。
这是由于 EventRecorder 有一段数据放置在了 “.bss.noinit” section中——以求芯片复位后不会破坏其中原有的内容。
如果你的工程没有专门针对 “.bss.noinit” 的处理,那么就会在进入调试模式后,从Command 窗口中看到类似如下的信息:

即:

Warning: Event Recorder not located in uninitialized memory!


如果遇到这种情况应该怎么办呢?

打开工程配置窗口“Options for Target”,切换到“Linker”选项卡:


首先,一定要确保你勾选了图中的“Use Memory Layout from Target Dialog”选项。在这一前提下,再次取消对它的勾选:

我们会看到,MDK基于当前的Memory Layout,为我们在Out目录下生成了一个与工程同名的链接脚本(比如图中的工程名叫example,因此生成的链接脚本为 example.sct)。

单击 Edit 按钮,可以看到脚本的内容:

先别着急半路开香槟——该文件是系统自动生成的,如果我们不移动它的位置,那么只要哪次手抖勾选了“Use Memory Layout from Target Dialog”,它的内容就会立即被覆盖掉——意味着我们在后续步骤中所做的改就会付诸东流。


为了避免该问题,应该将它从 Object 目录中移动到工程目录下。具体步骤为:右键单击脚本文件名:

选择“Open Container Folder”来打开文件所在目录:

找到Scatter Script脚本文件后,将其拷贝到上一级目录下(也就是工程目录):

重新打开工程配置窗口:

确保我们“没有”选中“Use Memory Layout from Target Dialog”选项,并在Scatter File文本框中直接填写我们刚刚拷贝出来的脚本文件名(由于我们直接放在工程目录下,因此这里直接用相对路径"./example.scat"或者"example.scat"就行)。单击OK保存配置。

打开example.sct,在 RW_IRAM1 后面追加如下的代码:

    ZI_RAM_UNINIT +0 UNINIT {        .ANY (.bss.noinit)    }

效果大约类似这样:

保存后重新编译,再次进入 Debug 模式,问题就应该解决了。

这里步骤的核心思想是在 scatter script 内紧接着为 RWZIexecution region.bss.noinit 提供一个属性为UNINIT的专属execution region

在领会精神的情况下,如果你的工程原本就使用了scatter script也可以如法炮制。俗话说解铃还须系铃人,如果你还是不知道怎么处理,那么就去找 你工程中scatter script 的作者吧。



值得强调的是:如果你的MDK版本太老,为了确保最佳的用户体验,还是推荐尽快升级吧。您可以在关注【裸机思维】公众号后发送关键字【MDK】来获取其最新的网盘链接


【说在后面的话】

总的来说,MDK 通过 EventRecorder 为我们提供了一个通用便捷的方式来重定向 printf——无论你使用什么调试仿真器,甚至是FVP,都可以享受来自“MDK”的阳光普照。
对很多有分发自己工程作为模板的小伙伴来说,使用该方法后将不再限制用户必须使用 J-Link 之类的工具,而是可以放开手脚,获得了“开袋即食”的调试体验。
最后强调一下哦,EventRecorder只在调试阶段有意义,如果我们需要在产品的正常工作模式下使用 printf,还是老老实实把 Compiler->IO->STDOUT 配置为 User:

实现 stdout_putchar() 函数——用它来发送字符到具体的外设吧,比如:
int stdout_putchar(int ch){    if ('\n' == ch) {        int temp = '\r';        while(Driver_USART0.Send(&temp, 1) != ARM_DRIVER_OK);    }        if (Driver_USART0.Send(&ch, 1) == ARM_DRIVER_OK) {        return ch;    }        return -1;}

本文来源网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。

注意

由于微信公众号近期改变了推送规则,为了防止找不到,可以星标置顶,这样每次推送的文章才会出现在您的订阅列表里。

猜你喜欢:

分享一份嵌入式软件工具清单!

分享一款嵌入式人必备绘图工具!

易懂 | 手把手教你编写你的第一个上位机

实用 | 10分钟教你搭建一个嵌入式web服务器

嵌入式常用通信传输协议动图,收藏!

适用于嵌入式的差分升级通用库!

分享一种灵活性很高的协议格式(附代码例子)

分享几个实用的代码片段(第二弹)


在公众号聊天界面回复1024,可获取嵌入式资源;回复 ,可查看文章汇总

嵌入式大杂烩 专注于嵌入式技术,包括但不限于C/C++、嵌入式、物联网、Linux等编程学习笔记,同时,内包含大量的学习资源。欢迎关注,一同交流学习,共同进步!
评论
  • 根据Global Info Research项目团队最新调研,预计2030年全球封闭式电机产值达到1425百万美元,2024-2030年期间年复合增长率CAGR为3.4%。 封闭式电机是一种电动机,其外壳设计为密闭结构,通常用于要求较高的防护等级的应用场合。封闭式电机可以有效防止外部灰尘、水分和其他污染物进入内部,从而保护电机的内部组件,延长其使用寿命。 环洋市场咨询机构出版的调研分析报告【全球封闭式电机行业总体规模、主要厂商及IPO上市调研报告,2025-2031】研究全球封闭式电机总体规
    GIRtina 2025-01-06 11:10 124浏览
  • 彼得·德鲁克被誉为“现代管理学之父”,他的管理思想影响了无数企业和管理者。然而,关于他的书籍分类,一种流行的说法令人感到困惑:德鲁克一生写了39本书,其中15本是关于管理的,而其中“专门写工商企业或为企业管理者写的”只有两本——《为成果而管理》和《创新与企业家精神》。这样的表述广为流传,但深入探讨后却发现并不完全准确。让我们一起重新审视这一说法,解析其中的矛盾与根源,进而重新认识德鲁克的管理思想及其著作的真正价值。从《创新与企业家精神》看德鲁克的视角《创新与企业家精神》通常被认为是一本专为企业管
    优思学院 2025-01-06 12:03 158浏览
  • 每日可见的315MHz和433MHz遥控模块,你能分清楚吗?众所周知,一套遥控设备主要由发射部分和接收部分组成,发射器可以将控制者的控制按键经过编码,调制到射频信号上面,然后经天线发射出无线信号。而接收器是将天线接收到的无线信号进行解码,从而得到与控制按键相对应的信号,然后再去控制相应的设备工作。当前,常见的遥控设备主要分为红外遥控与无线电遥控两大类,其主要区别为所采用的载波频率及其应用场景不一致。红外遥控设备所采用的射频信号频率一般为38kHz,通常应用在电视、投影仪等设备中;而无线电遥控设备
    华普微HOPERF 2025-01-06 15:29 164浏览
  • 本文介绍编译Android13 ROOT权限固件的方法,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。关闭selinux修改此文件("+"号为修改内容)device/rockchip/common/BoardConfig.mkBOARD_BOOT_HEADER_VERSION ?= 2BOARD_MKBOOTIMG_ARGS :=BOARD_PREBUILT_DTB
    Industio_触觉智能 2025-01-08 00:06 92浏览
  •  在全球能源结构加速向清洁、可再生方向转型的今天,风力发电作为一种绿色能源,已成为各国新能源发展的重要组成部分。然而,风力发电系统在复杂的环境中长时间运行,对系统的安全性、稳定性和抗干扰能力提出了极高要求。光耦(光电耦合器)作为一种电气隔离与信号传输器件,凭借其优秀的隔离保护性能和信号传输能力,已成为风力发电系统中不可或缺的关键组件。 风力发电系统对隔离与控制的需求风力发电系统中,包括发电机、变流器、变压器和控制系统等多个部分,通常工作在高压、大功率的环境中。光耦在这里扮演了
    晶台光耦 2025-01-08 16:03 58浏览
  • 根据环洋市场咨询(Global Info Research)项目团队最新调研,预计2030年全球无人机锂电池产值达到2457百万美元,2024-2030年期间年复合增长率CAGR为9.6%。 无人机锂电池是无人机动力系统中存储并释放能量的部分。无人机使用的动力电池,大多数是锂聚合物电池,相较其他电池,锂聚合物电池具有较高的能量密度,较长寿命,同时也具有良好的放电特性和安全性。 全球无人机锂电池核心厂商有宁德新能源科技、欣旺达、鹏辉能源、深圳格瑞普和EaglePicher等,前五大厂商占有全球
    GIRtina 2025-01-07 11:02 122浏览
  • 在智能家居领域中,Wi-Fi、蓝牙、Zigbee、Thread与Z-Wave等无线通信协议是构建短距物联局域网的关键手段,它们常在实际应用中交叉运用,以满足智能家居生态系统多样化的功能需求。然而,这些协议之间并未遵循统一的互通标准,缺乏直接的互操作性,在进行组网时需要引入额外的网关作为“翻译桥梁”,极大地增加了系统的复杂性。 同时,Apple HomeKit、SamSung SmartThings、Amazon Alexa、Google Home等主流智能家居平台为了提升市占率与消费者
    华普微HOPERF 2025-01-06 17:23 202浏览
  • 这篇内容主要讨论三个基本问题,硅电容是什么,为什么要使用硅电容,如何正确使用硅电容?1.  硅电容是什么首先我们需要了解电容是什么?物理学上电容的概念指的是给定电位差下自由电荷的储藏量,记为C,单位是F,指的是容纳电荷的能力,C=εS/d=ε0εrS/4πkd(真空)=Q/U。百度百科上电容器的概念指的是两个相互靠近的导体,中间夹一层不导电的绝缘介质。通过观察电容本身的定义公式中可以看到,在各个变量中比较能够改变的就是εr,S和d,也就是介质的介电常数,金属板有效相对面积以及距离。当前
    知白 2025-01-06 12:04 223浏览
  • 大模型的赋能是指利用大型机器学习模型(如深度学习模型)来增强或改进各种应用和服务。这种技术在许多领域都显示出了巨大的潜力,包括但不限于以下几个方面: 1. 企业服务:大模型可以用于构建智能客服系统、知识库问答系统等,提升企业的服务质量和运营效率。 2. 教育服务:在教育领域,大模型被应用于个性化学习、智能辅导、作业批改等,帮助教师减轻工作负担,提高教学质量。 3. 工业智能化:大模型有助于解决工业领域的复杂性和不确定性问题,尽管在认知能力方面尚未完全具备专家级的复杂决策能力。 4. 消费
    丙丁先生 2025-01-07 09:25 116浏览
  • 「他明明跟我同梯进来,为什么就是升得比我快?」许多人都有这样的疑问:明明就战绩也不比隔壁同事差,升迁之路却比别人苦。其实,之间的差异就在于「领导力」。並非必须当管理者才需要「领导力」,而是散发领导力特质的人,才更容易被晓明。许多领导力和特质,都可以通过努力和学习获得,因此就算不是天生的领导者,也能成为一个具备领导魅力的人,进而被老板看见,向你伸出升迁的橘子枝。领导力是什么?领导力是一种能力或特质,甚至可以说是一种「影响力」。好的领导者通常具备影响和鼓励他人的能力,并导引他们朝着共同的目标和愿景前
    优思学院 2025-01-08 14:54 61浏览
  • 故障现象一辆2017款东风风神AX7车,搭载DFMA14T发动机,累计行驶里程约为13.7万km。该车冷起动后怠速运转正常,热机后怠速运转不稳,组合仪表上的发动机转速表指针上下轻微抖动。 故障诊断 用故障检测仪检测,发动机控制单元中无故障代码存储;读取发动机数据流,发现进气歧管绝对压力波动明显,有时能达到69 kPa,明显偏高,推断可能的原因有:进气系统漏气;进气歧管绝对压力传感器信号失真;发动机机械故障。首先从节气门处打烟雾,没有发现进气管周围有漏气的地方;接着拔下进气管上的两个真空
    虹科Pico汽车示波器 2025-01-08 16:51 70浏览
  • By Toradex 秦海1). 简介嵌入式平台设备基于Yocto Linux 在开发后期量产前期,为了安全以及提高启动速度等考虑,希望将 ARM 处理器平台的 Debug Console 输出关闭,本文就基于 NXP i.MX8MP ARM 处理器平台来演示相关流程。 本文所示例的平台来自于 Toradex Verdin i.MX8MP 嵌入式平台。  2. 准备a). Verdin i.MX8MP ARM核心版配合Dahlia载板并
    hai.qin_651820742 2025-01-07 14:52 108浏览
  • 村田是目前全球量产硅电容的领先企业,其在2016年收购了法国IPDiA头部硅电容器公司,并于2023年6月宣布投资约100亿日元将硅电容产能提升两倍。以下内容主要来自村田官网信息整理,村田高密度硅电容器采用半导体MOS工艺开发,并使用3D结构来大幅增加电极表面,因此在给定的占位面积内增加了静电容量。村田的硅技术以嵌入非结晶基板的单片结构为基础(单层MIM和多层MIM—MIM是指金属 / 绝缘体/ 金属) 村田硅电容采用先进3D拓扑结构在100um内,使开发的有效静电容量面积相当于80个
    知白 2025-01-07 15:02 141浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦