ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

KEIL5 Debug调试全攻略:工程配置、变量观察与常见问题解决

KEIL5 Debug调试全攻略:工程配置、变量观察与常见问题解决 KEIL5中Debug调试从工程配置到高阶技巧嵌入式开发者的调试实操笔记我入行做嵌入式也有十年了从最早的C51一路用到现在的STM32、GD32中间也折腾过IAR、ECLIPSE那套工具链但最顺手的还是KEIL5。前阵子带几个新人做项目发现大家Debug调试基本只会按一个F5然后看到程序跑飞了不知道从哪儿下手。问得最多的问题翻来覆去就是那几句结构体变量在调试界面里怎么显示全烧录总失败是什么原因编译怎么这么慢说实话KEIL5的调试功能非常强大但它的很多实用功能都藏得比较深没有老手带一带全靠自己摸索效率确实低。所以这篇东西我打算认认真真把KEIL5 MDK的Debug调试这件事讲透。从调试前的工程配置讲起到调试界面怎么用再到结构体、数组、指针这类复杂变量的观察技巧最后把编译慢、烧录失败、XTAL配置变灰这堆常见坑一次性说清楚。不管你是刚装好KEIL5准备入门的新手还是已经写了两年固件但调试用得不太六的朋友这篇应该都能给你一些实在的参考。1. 调试前的环境准备工程配置这一步偷懒后面全要还很多人觉得调试就是从点那个放大镜按钮开始的但实际上下没下断点、能不能单步、能不能看变量这些在工程配置阶段就已经决定了。如果配置不对后面进调试模式不是卡死就是窗口里全是乱码。1.1 芯片包和编译环境检查用KEIL5的第一步不是新建工程是确认你的芯片支持包Device Pack装好了。KEIL5和KEIL4不一样KEIL5把芯片支持拆成了独立的Pack包你新建工程的时候选不到STM32F103C8T6往往是Pack没装而不是KEIL5有问题。去官网或者用软件自带的Pack Installer把对应厂商的Pack装上就行。这里有两个实际中很容易踩的坑我分开说第一个坑是版本兼容性。有些老项目是用KEIL4写的你用KEIL5打开后虽然会自动提示迁移但迁移之后Device那边选的芯片型号可能还留着而对应的Pack没装编译直接报错提示找不到设备。这种情况最快的解决办法是去Pack Installer里搜索对应型号把带Device字样的Pack装上不用装那一大堆Example。第二个坑是芯片型号选择。STM32F103系列里C8T6和RBT6引脚兼容但Flash大小不一样选错了芯片编译链接阶段会提示Flash溢出或者干脆烧录不进去。选型的时候对照数据手册看清楚别偷懒随便点一个。编译慢这个问题也值得说一句。很多人一编译就是两三分钟其实多半是没开增量编译的功劳。KEIL5的编译按钮旁边有个小箭头下拉菜单里选Rebuild是全部重新编译选Build才是增量编译。平时修改代码后点Build就行只有换了芯片型号、改了头文件路径这类全局性操作才需要Rebuild。另外一个常见拖慢编译的原因是把Output选项卡里的Browse Information打开了这个选项有时候调试确实需要它支持源码跳转但如果你不在乎代码跳转把它关掉编译速度能快不少。1.2 Debug选项卡配置选对调试器才有灵魂工程配置里跟调试最直接相关的是Options for Target的Debug选项卡。很多人打开发现默认选的是Use Simulator也就是说你点了调试按钮后程序根本不往板子上烧只是在电脑上模拟运行。这没问题纯逻辑调试是够用的但如果你要看真实外设GPIO、UART、ADC这些的波形和数据必须板子上跑这时候就要选右侧的Use:然后在下拉框里选择你的调试器比如ST-Link Debugger、J-LINK / J-TRACE Cortex。选择不同的调试器有个容易忽略的细节旁边的Settings按钮点进去Interface要选对。ST-Link用SWD模式J-Link一般也用SWD但如果你用了J-Trace或者某些开发板自带的CMSIS-DAP接口可能要换成JTAG或者SWV。接口选错了KEIL5会提示无法连接很多人拿到新开发板第一件事就卡在这。Settings里还有一个关键参数是SWDIO的速率。默认是4MHz或5MHz绝大多数情况没问题。但如果你的调试线特别长超过20cm的杜邦线就算长了或者板子电源不稳建议把速率适当往下降比如降到1MHz。我在调试一块自己画的板子时就遇到过这个问题ST-Link怎么都连不上代码逻辑确认没问题最后一查是线太长降速后一下就通了。1.3 烧录设置下载失败的根源一半在这调试之前要先能把程序烧进去所以Flash Download相关的配置也要一并检查。在Debug选项卡切到Utilities选项卡或者直接点击Settings里的Flash Download选项需要确认这么几项Download勾选了Program、Erase Full Chip还是Erase Sectors、Reset and Run是否勾选。这个Reset and Run挺有意思的。勾上之后烧录完成芯片自动复位运行省得你手动按复位键。对一些需要上电就跑的板子来说这个选项一定要勾上。我见过好几个新手烧完程序后看现象不对以为是程序问题最后发现是压根没点复位程序还是在跑上一次烧进去的版本。Erase Full Chip和Erase Sectors的区别也要理解。前者烧录前把整颗芯片擦干净时间慢但干净后者只擦除需要用到的扇区速度快但偶尔会因为地址配置不对留下旧数据。日常调试用Erase Sectors够了量产或者需要干净Flash的时候切Erase Full Chip。这个选项如果选错烧进去的程序表现可能会很诡异比如改了个定时器初值但行为没变化很有可能就是旧代码没擦干净。2. 正式进入调试界面、窗口与手速配置好之后点一下那个放大镜图标或者CtrlF5编译并进入调试模式。我第一次用KEIL5调试时被满屏的窗口吓了一跳说实话这个IDE的默认布局确实有点乱但搞清楚了每一项是什么后面就很顺手了。2.1 调试界面的五个常用区域进入调试模式后主界面大致分成这么几块左侧是寄存器窗口Registers调试时自动展开显示当前CPU各个寄存器的值比如R0-R15、xPSR、MSP、PSP这些。新手一般不用盯寄存器但出了诡异问题翻一翻还是很有帮助的比如程序跑飞了PC指针的值基本就是泄露地点。中间最大的区域是反汇编窗口和源码窗口。默认情况下你写的是C代码所以看到的是源码还能在每一行左边打断点。如果你勾选了View菜单下的Disassembly Window就能看到汇编指令单步的时候可以顺便看每条C语言对应的汇编是什么这对理解编译器行为和学习汇编很有用。右侧是Call Stack调用堆栈和Locals局部变量窗口。Call Stack非常重要程序跑飞或者进入HardFault异常时这里的调用堆栈能直接告诉你卡在了哪个函数的哪一层。底部或者右侧还会有一个Watch窗口这就是我们观察变量的主力区域后文会重点展开。这些窗口在View菜单下都能找到丢失了可以随时重新打开布局乱了可以通过View菜单下的Reset View to Defaults恢复。2.2 调试按钮不全是你以为的那个F5调试工具栏上有十来个按钮大部分人只熟悉运行和停止。我把这些按钮的功能和快捷键列出来好用程度拉满按钮/操作快捷键说明进入调试模式AltF5 或 F5取决于版本编译并进入调试全速运行F5让程序全速跑直到遇到断点或手动停止停止运行Esc 或 ShiftF5退出调试模式停止程序单步跳过F10单步执行一行不进入函数内部单步进入F11单步执行一行进入函数内部单步跳出CtrlF11执行完当前函数跳出到调用处运行到光标处CtrlF10直接运行到光标所在行不打断点插入/移除断点F9当前行切换断点这几组快捷键必须形成肌肉记忆。我个人最常用的其实是CtrlF10运行到光标处比临时打断点方便太多了尤其是你在一个长函数里排查问题时光标往目标行一放按一下就跑过去了不用管断点清没清。2.3 断点的高级玩法条件断点和临时断点很多人对断点的理解是“这行代码执行前暂停一下”但KEIL5的断点系统远比这个强。右键一个断点选择Breakpoint Properties可以设置条件。比如你有一个for循环要跑1000次想在第500次的时候停下来观察不需要傻傻按500次F10直接在断点属性里输入条件i 500程序运行到满足条件的那一次才会停下。条件断点还有一个比较隐蔽的用途是观察变量变化。比如某个全局变量g_flag会被好几个地方修改你不好定位到底是谁把它改掉的。可以在g_flag赋值语句那行下个条件断点条件是g_flag ! 0每次它被修改时断点都会触发看看Call Stack就能知道谁干的。这个方法我调试时用过无数次比自己写Log再打印省事得多。H. 逻辑分析仪窗口调试时的隐形成员除了Watch窗口KEIL5还有一个非常好用但容易被忽略的窗口叫Logic Analyzer逻辑分析仪在View菜单里可以打开。这个窗口可以实时显示变量值的变化曲线不需要接任何外部设备。用法是先点击Logic Analyzer窗口左上角的Setup按钮添加你要观察的变量比如ADC采样值、PWM占空比变量、某个状态机的状态变量然后运行程序就能在窗口里看到变量值的波形。这比满屏跑数字好用多了特别是观察一个信号连续变化的过程。注意一点Logic Analyzer窗口对普通变量能显示数值变化对IO口比如PA口的某个pin可以用GPIO的方式观察电平高低。如果你要做简单的时序分析比如测量某个函数的执行时间、看信号翻转频率这个窗口在完全不接示波器的情况下可以快速给出一个大概的参考虽然不是精确到纳秒级但判断有没有信号变化已经足够用。3. 硬核技巧结构体变量、数组与指针的查看方法这个部分一定是很多人的痛点。调试时Watch窗口里加了一个结构体变量看到的却是一个带加号的可展开项展开后只有第一个成员后面全是undefined或者干脆问号。我自己刚接触KEIL5时也被这个问题折磨过后来摸清了规律。3.1 为什么结构体变量显示不全或显示乱码先说结论KEIL5的Watch窗口和Locals窗口对结构体、数组这类复合类型的支持是有限的尤其是涉及指针、动态内存分配、以及一些编译器优化后的局部变量时显示就会出问题。具体来说一个结构体变量如果是在函数内部定义的局部变量并且定义了多个但你没有在实际用到它的地方下断点那么编译器可能已经把它优化掉了这时候Watch里看到的自然就是undefined。解决办法有两个方向一是把这个结构体变量改成全局变量二是在你真正读取该变量的地方下断点让程序停在那里变量才会有实际值。另外一个常见问题是结构体成员是数组或者指针时展开后显示的是地址而不是内容。比如结构体里有一个uint8_t buf[64]你在Watch里看到buf显示的是0x20000100这样的地址点加号展开可能仍旧是一堆地址。这就是因为KEIL5不知道你要按什么类型来解释这块内存。3.2 结构体变量的三种查看姿势这里把我实际在用的几种方法整理出来各有各的适用场景第一种是直接Watch结构体变量。在Watch窗口的Name列输入结构体变量名按回车正常情况下会展开显示出所有成员及它们的值。这个方法的前提是这个变量在当前作用域内可见且有实际值。如果你用的是局部结构体数组展开后会出现很多个下标点开下标就是对应元素的成员。第二种是给Watch窗口里的变量加类型转换。比如你有一个void *指针ptr你知道它实际指向的是一个MyStruct类型的结构体你在Watch里直接写(MyStruct *)ptrKEIL5就会把指针指向的内存按MyStruct结构体来解析展开后所有成员的值一目了然。这个方法在调试链表、队列这些数据结构时极其好用不用把指针退化成一堆十六进制字节。第三种是用Memory窗口直接看内存。Memory窗口你从View菜单打开后在Address那一栏输入结构体变量的地址比如my_struct右边的内存区会以十六进制显示那一块内存的内容。如果你希望以结构体成员的方式解析这段内存还是回到第二种方法用类型转换。3.3 Watch窗口的“Formatter”设置让数据更直观KEIL5的Watch窗口还支持自定义数据格式。右键Watch窗口里某个变量选择Format可以切换数据显示为十进制、十六进制、二进制、浮点等。比如你调试一个ADC采样值寄存器默认显示十六进制看着不方便直接右键切成十进制或者浮点读数直观很多。这个功能还有一个巧妙的用法是处理数组。如果你在Watch里输入一个数组名比如adc_buf默认显示第一个元素的值。你要是想看整个数组可以用表达式adc_buf[0] 64这样的写法表示从adc_buf[0]开始显示64个元素。这个语法我第一次知道的时候觉得简直像在作弊但确实能用尤其是看采样缓冲区数据时比逐个加Watch变量方便一百倍。3.4 关于指针看见地址还不够要看见地址背后的东西指针调试是很多人头疼的另一个点。你在Watch里输入一个指针变量p看到的是一个地址0x200001B0这只是一个数字而已它的目标才是你真正关心的。搞清楚这一步需要你能区分几种类型的表达式p显示指针变量本身的值也就是它指向的地址。p显示指针指向的那个变量的值前提是KEIL5知道p指向什么类型。如果p是int那么*p显示的就是int值。p显示指针变量p自己的地址它是另一个地址和*p完全不一样。当涉及到指向结构体的指针时比如StructPtr是一个指向MyStruct结构体的指针你在Watch里写(*StructPtr).member或者直接用StructPtr-member表达式也可以。KEIL5的Watch是支持表达式的不光支持结构体成员访问还支持一些简单的算术运算比如数组下标arr[i]。我这里特别提醒一句如果p是指向无效区域的野指针*p这个表达式会直接导致调试器异常甚至整个调试会话崩掉。遇到这种情况不要慌把那个Watch变量先删掉然后用Memory窗口单独看看这个地址范围是不是合法的一般RAM地址范围在0x20000000之后确认后再去访问。4. 只靠断点不够Watch、内存与串口的三板斧很多人的调试习惯是打断点加Watch变量发现程序没按预期走之后就跟个无头苍蝇一样乱试。真正高效的调试思路应该是配合多种窗口和工具先缩小问题范围再定位具体代码行。我把平时用得比较顺的方法整理成三段。4.1 程序跑飞与HardFault异常的定位套路程序跑飞或者进入HardFault是嵌入式的家常便饭一般出现这种问题第一件事不是看代码而是看Call Stack窗口。如果KEIL5能定位到HardFault_Handler的调用堆栈你就能看到栈上保存的返回地址顺着地址在源码里查是哪个函数在执行时触发的。如果Call Stack显示的是HardFault_Handler且没有上层调用信息这通常表示栈已经被破坏得很严重函数返回地址都给冲掉了。这时候就不能指望堆栈了需要转去查看CPU寄存器尤其是xPSR、PC、LR这几个。KEIL5的Registers窗口里能看到结合编译生成的map文件里的函数地址PC值落在哪个函数内部就能推算出来。这一招用得多了就能慢慢积累出排除栈溢出、野指针、中断优先级配置不当这些“疑难杂症”的手感。4.2 Memory窗口的正确打开方式Memory窗口在没有JTAG/SWD跟踪工具的情况下是审视内存的唯一窗户。但很多人只把它当十六进制查看器看了半天不知道有什么意义。我给一个更实操的用法观察全局缓冲区是否被意外改写。比如你有一个全局数组g_buf[100]怀疑某处越界写入了导致数组后面的其他变量被篡改。先在Watch窗口或者直接看变量的地址比如g_buf的地址是0x20000100然后在Memory窗口地址栏输入0x20000100这时候你会看到从该地址开始的一段内存的十六进制内容你可以把数据改成指定值然后运行程序再看这块内存是否发生变化。如果数组后面的一些字节突然被改了基本可以断定有越界写操作再结合断点去抓“凶手”。Memory窗口还有一个隐藏很深的设置就是下图的右下角有个三个点的小按钮点开可以选择内存数据显示的格式默认是16进制字节也可以切换成16位半字或者32位整型。如果要快速读几个寄存器值弄成32位显示会方便很多。4.3 虚拟串口与printf重定向调试的续航神器当逻辑复杂到单步调试已经代价太高时就该请出printf了。把printf重定向到UART输出然后通过串口调试助手或者IDE自带的Serial窗口打印log这几乎成了嵌入式开发的默认操作。KEIL5里做printf重定向其实很简单核心是重写fputc函数让它把字符发给串口。我这里给出一个最小可用的代码模板#include stdio.h int fputc(int ch, FILE *f) { // 等待发送寄存器空 while ((USART1-SR USART_FLAG_TXE) 0); USART1-DR (uint8_t)ch; return ch; }这段代码用了STM32标准库的操作方式如果你是HAL库改成HAL_UART_Transmit一次发一个字节就行。重定向之后还需要在工程设置里勾选Use MicroLIB否则使用标准库的printf会在链接时占用过多Flash甚至可能因为半主机模式Semihosting导致程序卡死。这个细节我提醒过无数人用printf调试前先查这个有没有勾选。4.4 串口打印的时机和节奏串口打印不是说在程序里随便摆几个printf就行。打印位置的摆放要遵循几个原则一次只看一个关键信号的变化打印量不要太大否则UART波特率会拖慢程序运行影响时序打印的信息要带上下文比如打印函数入口时带上传入的参数值打印状态机切换时带上原状态和新状态。另外如果你担心printf太慢影响实时性可以在调试阶段用一个简单的#define控制打印开关比如#define DEBUG_ENABLE 1 #if DEBUG_ENABLE #define DBG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__) #else #define DBG_PRINT(fmt, ...) #endif这样发布版本直接把DEBUG_ENABLE改成0所有调试打印就全部消失不用到处删代码。这个小技巧几乎成了我所有工程的标配。5. 编译慢、烧录失败、XTAL变灰这些坑我替你踩过了前面讲了不少调试技巧但如果工程环境本身有问题再好的技巧也用不上。这一节我集中回答一下大家问得最多的几个KEIL5相关的问题每个都是我现实中见过或者自己跌过的坑。5.1 编译慢怎么处理编译慢这个问题网上讨论很多我这里给几个立竿见影的方案按效果排序第一确认你用的是Build而不是Rebuild。很多人习惯性点那个带两个箭头的图标这就是全量编译自然慢。Build或F7是增量编译只编译修改过的文件。第二关闭Browse Information。在Options for Target的Output选项卡里把Browse Information勾掉。这个功能提供源码跳转和Find All References的能力平时写代码确实好用但它会让编译器为每个源文件生成额外的浏览数据库编译时间暴涨。我实测过关掉后编译时间能缩短30%到50%。第三利用分散加载和预编译头。对一些超大型工程来说可以考虑把不常改的底层驱动封装成静态库通过源码文件的Target栏把它们归到独立的Target下单独编译。这样日常开发只对应用层的若干文件进行增量编译编译速度会有质的提升。第四升级机器或者用RAM虚拟磁盘。把工程放到RAM Disk里编译速度确实会快一点但这个很多新电脑的磁盘本来就是SSD提升幅度没那么明显所以我一般不主动推荐因为治标不治本。5.2 烧录失败的常见原因一览我在调试过程中总结的烧录失败基本逃不出这五种情况这里列成一张表方便大家对照排查故障现象常见原因排查/解决方案无设备连接 No Target Connected调试器未识别SDK驱动没装好检查设备管理器重装驱动或者换USB口SWD/JTAG Communication Failure线序接错、接口电压不匹配、复位脚被占用核对SWDIO、SWCLK、GND接线降低SWD速率Flash Timeout / Erase FailedFlash下载算法没选对检查Flash Download里的Programming Algorithm是否匹配芯片型号RDDI-DAP Error芯片被读保护或调试端口被禁用先用ST-Link Utility或STM32CubeProgrammer去掉读保护Verify Failed at 0x...程序镜像和Flash内容不一致检查分散加载文件确认下载起始地址是否对齐其中RDDI-DAP Error对新手来说最容易蒙圈。这个问题的本质是芯片的Flash被设置了读保护RDP级别或者联调时调试端口被关掉了KEIL5无法正常访问调试接口就会报这个错。解决办法是换用专用的烧录工具ST-Link Utility或STM32CubeProgrammer执行去除读保护操作然后再回到KEIL5烧录。我有一块板子很久没动插上后怎么都烧不进去就是这个问题折腾了小半天才想起来是一年前设置过Flash保护。5.3 Target选项卡XTAL变灰的原因与对策热搜里有“keil5 target选项卡的xtal 变灰”这一条很多人疑惑为什么External Clock那里的XTAL是灰色的改不了。这个其实跟芯片型号有关。XTAL配置项是给早期的8051内核或者某些简单的ARM调试场景用的它代表外部晶振频率。新版KEIL5在Target选项卡里如果你选择的芯片是Cortex-M系列比如STM32那么XTAL这项通常是不可编辑的因为这个频率在系统初始化时已经由SystemInit函数根据宏定义比如HSE_VALUE来配置了IDE层面的参数对程序运行没有实际影响。如果你在调试C51单片机那么XTAL是可编辑的因为它参与模拟调试时的时序计算。所以看到XTAL变灰先不用慌确认你用的是不是ARM内核芯片如果是这属于正常现象。如果你确实需要修改外部晶振频率直接改目标工程里的HSE_VALUE宏定义即可不是在Target选项卡里硬改。5.4 左侧目录不显示的解决办法“keil5左侧目录怎么显示”这个问题也很常见。Project窗口是KEIL5导航源码的主入口如果突然消失了从View菜单里打开Project Window即可。如果你是想让Project窗口显示文件的具体路径右键窗口选择Show Include File Dependencies之类的选项就能看到头文件依赖关系。但如果你遇到的是文件在Project里能看到但双击打开时提示找不到文件或者打不开那大概率是工程文件丢失路径引用需要在Project窗口中删除该文件重新添加。具体操作是选中文件名右键选择Remove File然后使用Add Existing Files重新添加正确路径下的文件。5.5 兼容C51和STM32的安装配置装了KEIL5之后又需要开发C51单片机于是去搜索“keil5兼容c51和stm32安装”。实际上KEIL5的MDK-ARM和C51是两套不同的工具链它们可以共存安装在同一台电脑上但需要分别安装、分别激活而且要注意安装顺序。比较稳妥的做法是先安装C51版本再安装MDK-ARM版本两个都完成后分别用对应的激活码注册。如果你先安装了MDK-ARM再装C51可能会出现其中一个IDE的标签页或者菜单被覆盖的情况这时候最好还是重新解压安装一次按顺序来。安装完成后Keil uVision5的同一个IDE是可以同时编译C51和ARM工程的只要你正确安装了对应软件包。打开工程时IDE会自动识别工程类型加载对应的编译器。我目前就是一台电脑同时装着两套日常开发C51老项目的空档切到ARM没出过什么兼容问题就是内存别省安装时尽量装到C盘默认路径。6. 调试效率提升的杂七杂八心得前面把主流程讲完了最后写点零散的、不成体系但特别想分享的经验都属于那种“知道了一辈子受用不知道就在Debug里多熬几晚”的东西。6.1 善用串行视图Serial Wire Viewer如果你的调试器支持SWV比如ST-Link和J-Link都支持那你就可以用KEIL5的Serial Viewer功能来实现实时的printf和事件观测占用极少的CPU资源。传统方式用串口UART输出log时CPU要被占用来做波特率匹配和字符发送而SWV是通过调试接口直接实时把数据导出的对程序本身几乎透明。要在KEIL5里启用SWV需要在Debug选项卡的Settings里打开Trace使能SWV然后设置合适的时钟频率。设置好之后View菜单下可以打开Serial Wire Viewer窗口通过printf重定向还是要接fputc但底层使用ITM_SendChar往这个窗口输出调试信息。比起外接UART这个方法少占一个串口速度还快非常适合没有多余串口的板子。6.2 善用“临时断点”和“日志断点”KEIL5的断点系统里有一个非常隐蔽但特别好用的功能日志断点Log Breakpoint。在断点属性里你可以让程序运行到某处时不暂停而是打印一行消息。这个行为和“条件断点”不同条件断点是满了条件才停下来日志断点是到点就打日志但不停。通过这个特性你在关键路径上布几个日志断点就能分析程序的执行路径和变量变化还省去了自己写printf再编译下载的时间。我举一个典型场景有一段数据处理函数处理完一个结构体后需要确认某个数组里所有元素是否都满足取值范围。直接打断点看数组元素累得慌写日志又麻烦这时用日志断点设置条件i 99时打印g_data[i]的值运行起来后看日志记录输出一次搞定。6.3 调试高层次记录自定义变量到串行视图SWV不仅可以输出日志还可以把任意全局变量的实时值传输到IDE侧实时绘图。这在没有上位机软件的情况下用来画一条ADC采样波形或者传感器曲线非常方便。具体做法是在调试模式下打开View里面的Analysis Windows → Serial Wire Viewer窗口点击窗口里的配置按钮添加一个全局变量选择类型然后在代码中用ITM_SendChar把数据包装发送到SWV。连续运行程序就能看到实时更新的变量曲线。这个功能的价值在于你不用在代码里写一堆UART协议和上位机对接直接把数据流吐到IDE里看开发调试阶段反馈非常快。6.4 最后一个小技巧给变量加“固定内存地址”调试协程、RTOS任务栈或者一些动态分配的内存时经常需要反复观察某个固定地址的内容。在Watch窗口里按以下格式添加一个变量*(unsigned long *)0x20000100这能直接以无符号长整型的方式解读0x20000100处的内存值。如果你要看的是一个全局数组可以用(unsigned char ()[64])0x20000100 这种函数指针风格的转换但平时我一般直接Memory窗口手动换算浓度没有大到需要记这种奇技淫巧。结合我这些年的经验调试这件事其实很吃“手感”。很多人觉得调试浪费时间但实际上真正的高手花在调试上的时间并不少区别在于他们有一套系统的方法论能快速缩小范围、定位问题。上面讲的这些东西每一个都是我踩过坑之后总结出来的照着做一遍至少不会在环境配置和基本操作上再卡壳。剩下的就是多写代码、多调Bug让这些工具成为你的肌肉记忆。
返回列表