粉丝1163获赞6463

大家好,今天聊一个千人字开发中经常遇到的头疼问题。程序跑飞,也就是常说的 hot file, 相信不少朋友都遇到过代码运行,这运行这就挂掉了,没有任何提示,也不知道卡在哪一行,尤其在项目复杂之后,查起来 非常麻烦。今天我就分享一个实用技巧,如何利用翻译工具链自带的 adr 拖烂工具,快速定位导致程序跑飞的代码位置。这个方法在 zf、 esp、 三二、 idf 等常见框架中都支持。 首先我们故意制造一个程序跑飞的现场,比如访问非法内存,接着我们通过串口连接设备等待程序跑飞,系统会抛出异常信息。 然后我们从异常信息中找到关键的 pc 指向地址,也就是程序计数器当前指向的位置。最后在东端输入一条指令,结合变器生成的点 elf 文件,就能直接映射出对应源代码中文键和行号。 实际操作下来,定位到的代码行和我们故意设置跑飞的位置完全一致。这样一来,不用盲目猜测,也无需大量加打印,效率提升非常明显。如果大家有更好的调试技巧,也欢迎在评论区分享交流。

嵌入式开发进阶技能遇到 hard fault 怎么查原因?就像电脑的蓝屏死机, arm cortex m 核的 mcu 在 程序出现严重问题时,可能会进入 hard fault, 也可能进入其他几类 fault。 为了方便说明,后面我们把这些异常统一称为 hard fault。 今天用两分钟的时间教你怎么回溯 hard fault 的 发生现场,让你不用单步调试,只需简单复现一次,就能知道是哪一行代码出了问题。 建议先收藏,未来你很可能会用到。首先,我们需要了解一下 hard fault 是 什么? hard fault 是 cortex m 内核定义的一种严重异常,当程序发生无法正常处理的错误时, mcu 就 会进入 hard fault 中断函数。常见原因包括也指真访问数组越键、键键一出、非法地址读写、除零错误等。今天用一个开源库教你快速回溯 hardfault 的 发生现场。这个开源库叫 c n backtrace, 在 github 有 两千多个 star, 即使你不懂 cortex m 的 底层寄存器,也能很容易定位问题代码 c m backtrace 移植起来并不难。首先,将 c m backtrace 点 c 和一个汇编文件加入工程。由于各个编辑器的汇编与法略有区别,所以需要根据你的编辑器类型将对应的汇编文件加入工程。 例如我用的是 g c c 编辑,就加入 g c c 目录下的汇编文件。接下来在 c n b c f g d h 做一些配置,告诉 c m backtrace 你 的打印函数是什么架构,是 r t o s 以及 m c u 的 内核类型。 最后,在初步化的时候调用 c n backtrace in it 函数,参数就是固件名以及硬件版本和软件版本,建议正常填写,方便测试人员抓取信息后向开发人员正确反馈发生错误时的版本信息。现在移植完成了,我们在这块佳立创 s m t。 制作的开发版,模拟一次 hard fault, 在 系统菜单的阿爆目录创建时,原位制造一次访问空指,真实际操作。看看点击 about, 立刻就发生了一次 hard fault。 看一下打印,出现了一堆 c m backtrace 的 信息,内容表明了是在 u i 县城里发生的错误,以及发生错误时的寄存器信息。最关键的是,下面这条带有很多地址信息的命令,这些地址就是发生错误的回溯地址。在命令行调用 address to line 工具执行这条指令。需要注意的是,执行命令时需要 elf 文件, elf 文件一般在 build 目录下,所以你可以直接的 build 目录下执行命令。可以看到我们通过调用命令回溯出了一系列 c 文件的行号信息,这些就是发生 hard fault 时 最后的入站调用顺序,这个顺序是倒序的,第一行就是发生 hard fault 的 最后时间点。 about page 的 第六十二行就是我们刚才故意访问空指针的地方,这样我们就找到了发生 hard fault 的 现场,并且还得到了发生之前的调用步骤。下次遇见难查的 hard fault 就 试试这个方法吧。好了,下期再见。

程序跑飞了,你第一反应是什么?加 profile? 重新上电?还是拍桌子骂变音器?行了,别演了,今天就教你三招,让你从玄学调试变成科学破案。第一,你还在靠猜 bug。 很多嵌入式工程师调试 bug 的 方式说白了就四个字,试猜,重启 heart, fault 了。重新上电试试变量莫名其妙变零,肯定是内存踩了,问具体踩了哪里不知道。面试让你现场定位一个站一出,你反问公司有没有 q mx, 这不是段子,是真事。想在嵌入式行业站住脚,先把下面这三把火烧起来。二、第一把火,手写断言和异常补货,别再用官方那个弱鸡 ass 铜了, 自己动手。当程序跑飞时,主动把 pc 指针、 l 二寄存器、调用站地址全打印出来,然后拿 adr lab 工具一转,直接得到文件名和行号。这套组合拳下来,百分之九十五的 hard fork, 你 能在三十秒内锁定肇事代码, 不夸张,很多老工程师就靠这一手吃饭。三、第二把火,把内存布局玩透。你会看 map 文件吗?大部分人止步于边,一玩看一眼大小。 真正的做法是自己写链接脚本,自定义段,把变量强行塞到指定的 r m 区域,然后故意写一个数组,越界观察它怎么破坏邻居变量的值。下次你遇到某个变量,莫名其妙变成零,脑子里立刻蹦出八成是隔壁数组溢出了,这不是天赋,是你亲手验证过的认知。 四、第三把火扔掉 print file, 拥抱 itm。 print file 好 用吗?好用,但它是阻色的,还占体积,在中段里打 print file, 分 分钟把实质性干废,改用 itm 调试, 通过 s w o 引角,输出速度飞快,不丢数据,不阻塞,中断得随便打 log, 你 甚至感觉不到他在工作。 arm 内核基本都支持你只需要一根调试器,把 print 的 重定向改一下就行。学会这一首,你比百分之八十只会用串口打印的人高一个档次。五、 行业现状,会调试的人太少了。现在的侵入式市场不缺会点灯会调库的人,缺的是那种给一块砖头一样的板子连上调试器半小时给你定位出问题的硬茬工程师。你是不是那个只会复位重启的运气工程师?别让开发版一出问题你就只会拍桌子。

二零二六年潜入式工程师绝不能划走这条视频,不然你将错过这个超强的 agent props, 他的写代码、翻译稍录调试功能已经被用爆了。今天我将给你介绍一个更加炸裂的功能,对硬件进行深度诊断的调试报告。 在 props 中选中报告模式,输入你的需求或需要解决的问题,他会自动进行软硬件连调 总结测试结果为报告。它会先判断固件有没有启动,主循环有没有再跑异常有没有触发,是不是 heartfall 哪个外设挂了? 别再问怎么使用了,来这里即可享有免费额度了!

大家好,今天我将和大家分享的主题是 c t m 三二标准库自定义函数入参。注意点, 在嵌入式开发中,函数参数的设计和使用直接关系到代码的效率、可读性和健壮性。 希望通过这一次分享,能帮助大家避开一些常见的陷阱,编写出更优雅、更可靠的 stm 三二代码。本次分享将分为四个部分,首先我们会探讨最基础的参数传递方式,直传递和指真传递的区别与选择。 接着我们会深入到数据类型的规范使用和指真的安全防护。然后我们会针对数组合、结构体这两种复杂类型的传餐技巧和常见陷阱进行分析。 最后,我们将讨论如何设计一个健壮的函数接口,包括参数校验和错误处理机制,好让我们进入第一部分参数传递的抉择, 这是我们编辑任何函数都必须首先考虑的问题。首先来看直传递,他的核心思想很简单,就是函数会复制一范你传入的参数,然后在这个拷贝上进行操作。 这意味着函数内部的任何修改都不会影响到原始的变量。这种方式的优点是简单、安全, 不会有指真带来的各种问题。因此,当我们传递的是像 int、 叉二这样的基本数据类型,并且函数只需要读取它们的值而不需要修改时值,传递是最佳选择。 对于一些非常小的结构体,拷贝的开销微乎其微,也可以考虑使用直传递。与直传递相对的是指真传递。在这种方式下,函数接收的不是变量的值,而是变量在内存中的地址,也就是指真 通过这个地址,函数可以直接找到并修改原始的变量。这带来了两个关键优势, 第一,对于大型的数据结构,比如一个包含很多成员的结构体,传递纸质只需要四个字节,在三十二位系统上远享于拷贝整个结构体的开销,效率极高。 第二,它允许函数修改外部的数据,这在很多场景下是必须的,比如初步化一个外设,更新传感器数据等。 此外,在 stm 三二中,操作硬件寄存器本质上也是通过纸真来访问固定的内存地址。这张表格清晰地总结了直传递和纸真传递的核心差异,从内存开销、性能到使用场景,我们都能看到它们的不同。 最关键的一点是,使用纸针传递时,必须在函数内部进行空纸针 no 检查,否则程序可能会因为访问非法内存而崩溃。 这是纸针使用中最基本也是最重要的安全原则。了解了传递方式后,我们进入第二部分,来谈谈数据类型的选择和纸针使用的安全性问题。 这是写出高质量可移植代码的关键。在新语言中,像 int long 这样的基本数据类型,其具体的字节大小是由翻译器和目标平台决定的, 这在嵌入式开发中可能会导致严重的问题。比如代码在三十二位的 stm 三二 f 一 上能正常运行,但移植到六十四位的处理器上就会出错。为了避免这种情况,我们应该养成一个好习惯,始终使用 stdn h 头文件中定义的明确大小的类型,比如 e、 n、 t 八下划线 t 代表八位无符号整数。 int 三十二下划线 t 代表三十二位有符号整数,这不仅让代码更清晰,也保证了其可移植性。 stm c 二的标准库本身就是这样做的,我们应该向它学习。使用指针就必须时刻警惕空指针的风险。如果一个指针参数是 n o, 而你又直接去解,引用它,程序几乎肯定会立刻崩溃。 在 s d m 三二上通常表现为 hard fault 错误。因此,一个良好的编程习惯是在函数的最开始就对所有的指数参数进行 no 检查。如果发现是空指数,我们需要做出处理,比如返回一个错误码, 或者在调试版本中触发断言 assert, 让问题在开发阶段就暴露出来,而不是等到产品发布后才发现。另一个重要的指征安全技巧是善用 ct 关键字。当一个指征参数前面加上 cost, 它向编辑器和所有阅读代码的人传递了一个明确的信息,这个函数只会读取该指征指向的数据,而不会修改它。 这不仅极大地增强了代码的可读性,让接口的意图一目了然,还能让翻译器帮助我们检查错误。如果我们不小心在函数内部试图修改 const 指真指向的数据,翻译器会直接报错, 从而在翻译阶段就阻止了潜在的 bug。 这是一种非常优雅的自我保护机制。 接下来我们进入第三部分,讨论在 stm 三二开发中非常常见的两种数据类型,宿组和结构体的传餐问题。 这里面有一些特别的技巧,也有不少容易掉进去的陷阱。宿组传餐有一个非常经典的陷阱,那就是宿组退化。 当你把一个宿组名作为参数传给函数时,它会自动变成一个指向宿组第一个元素的指征。 这意味着在函数内部,你无法通过 ccf r 来获取原始数组的总长度,因为 r 此时只是一个整数, ccf 得到的只是整数本身的大小,通常是四字节。这个错误非常隐蔽,新手很容易犯。 正确的做法是,永远不要依赖函数内部去计算数组长度,而是必须作为一个单独的参数显示地传递给函数。 对于结构体,特别是包含了数组或很多成员的大型结构体传餐时,我们应该毫不犹豫地选择纸真传递。如果使用直传递,整个结构体的内容都会被拷贝到函数的占空间中, 这在结构体很大时会带来巨大的性能开销和占空间浪费。而使用纸质传递只需要传递一个四字节的地址,效率天差地别。这也是为什么我们看 s t m 三二标准库的函数,比如 g, p, i o init, 它的参数都是结构体纸质,这是一个值得我们完全遵循的最佳实践。 最后,我们来探讨如何将前面学到的知识整合起来,设计出真正健壮、易于使用的函数接口。这主要包括两个方面,全面的参数校验和清晰的错误处理。一个 健壮的函数,其入口应该像一个安检站,对所有进入的参数进行全面检查。除了检查指征是否为空,我们还需要检查参数的值是否合理。比如你写了一个控制 g p i o。 的 函数, 就需要检查传入的引角号是否在零到十五之间。写了一个串口配置函数就需要检查波特率是否是一个被支持的值。 stm 三二标准库为此提供了一套完整的机制,它定义了大量以 i s 开头的红用于检查参数的合法性,然后通过 assign parameter 红在调试模式下强制执行这些检查。一旦参数不合法,程序就会在断言处停止,方便我们快速定位问题。 函数执行失败是不可避免的,关键在于如何处理失败。一个糟糕的函数会在失败时默默无语,让调用者一头雾水,而一个优秀的函数应该通过返回值清晰地告诉调用者发生了什么。 最佳实践是定义一个美举类型,比如模仿 h a l 库的 h a l status type def, 用不同的美举值来表示成功,一般错误设备忙超时等不同状态。 这样调用者就能根据返回值做出相应的处理,让整个程序的逻辑更加健壮和可靠。 最后,我们来简单回顾一下今天分享的核心要点,记住这六点,你的 stm 三二代码质量将会有一个质的飞跃。根据数据大小和是否需要修改来选择传递方式。使用明确的数据类型,时刻注意指战安全 检查尼欧病,善用 constant 数组传餐一定要带长度结构体传餐优先用纸针以及设计健壮的接口做好参数校验和错误处理。

嵌入式工程师不能错过这个五万人看过的开发工具,可以调试硬件的 agent。 今天要给你分享的功能是对硬件进行深度诊断的调试报告。在 props 中选中报告模式,输入你的需求或需要解决的问题,它会自动进行软硬件联调 总结测试结果为报告。它会先判断固件有没有启动,主循环有没有在跑异常有没有触发,是不是 heartbox 哪个外设挂了? 作为一款插件形式的 agent, 它既保留 vsco 开发的所有优点,支持 git 管理等功能,又实现了全流程的嵌入式开发。

板子不跑了,先他妈拍个照发群里大神求带,在线等,急!串口打印出一堆乱码,你不去看波特率,直接怀疑芯片烧了,到处借新板 子。你调试代码的手段就他妈只剩下按复位键和拔电重来,稍微遇到个 hot file 异常中断,你连 cosplay 都看不懂,不知道用低 bug 打断点定位,直接注销代码,一行一行试,这他妈能叫调试?这就是原始人,刚学会用火试一天,运气好能跑通,运气不好直接咸鱼卖掉。板子宣布这行太难,你缺的不 是智商,是排查问题的逻辑和经验。快艾特你那个写代码,只要报错就问人,从来不自己看。劳根日的朋友告诉他,这就是废物行为的巅峰,怎么改变?一、报错了,先看报错提示和慢繁状态,学会用故障诊断工具定位错误,把错 物现场保留下来分析。二、用串口打印,在线调试逻辑分析仪,试播器四版俯分层定位问题别瞎猜。三、把你踩的每一个坑和修复方式记录下来,写成薄壳或笔记,这东西是你以后最值钱的经验,总被 bug 折磨的死去活来的,来三个六,我把 s、 d、 n 三二常见 bug 排查实录加调试思维导图传授给你,别他妈一辈子遇到问题就知道喊大神和拔电源线,但会。

欠入市老铁们,今天咱们不聊外设,不聊电路,聊聊 cortex m 内核里的黑匣子。你写代码跑着跑着,突然 heart bursts 单片机直接摆烂,你抓耳挠腮,他一动不动,你是不是很想跟他说,你倒是告诉我错哪了?别急, 今天我就带你手搓一个 hard fault 分 析工具,当场把肇事者揪出来,让死机也变得明明白白。一、 hard fault 是 什么?就是单片机的脑梗, hard fault 是 contact man 内核的终极议程, 当你访问了非法地址、战役出彩报,或者执行了未定义的指令,内核就瞬间触发 hard fault 程序原地去世。 你唯一的线索就是他死了。为什么死?天知道!官方给的 heartfelt handler 默认就是一个 while。 这就像你去医院检查,医生给你一张纸条,你有病,但我不告诉你什么病, 自己猜,气得你当场想换医生。所以我们来给他动手术,让这个 handler 学会临终交代。注意,今天的内容主要基于 cortex 三 m 四 m 七 m 零 m 零加的 c f s 二、寄存器可能不完全一样,到时候以你的数据手册为准。二、收集案发现场证据关键寄存器。 当 hard fork 发生时,内核会自动把 r 零到 r 三、 r 十二 l r p, c x, p, s r 这八个计算器压入当前站中。我们只需要在 hard fork handler 里把这些值捞出来,通过串口或者 s w o 打印,就能反推是哪行代码惹的祸。 你需要重点收集这几个嫌疑犯,一、 pc 死机时正在执行的指定地址,把它打印出来,再结合 mac 文件就知道死在哪一行。 c 代码二、 l r r 帮你还原函数的调用链,谁调用了谁一目了然。 三 c f s。 二内核的验尸报告直接告诉你是因为越界访问除零还是未对其访问而死。这个寄存器地址是,零 x e 零零零一 d 二八三十二位。不同编辑器下占的字节数可能不一样,用的时候注意类型长度。 三、手把手写代码, hard fault 不 再沉默,下面咱们直接上代码。为了避开编码写代码, hard fault 不 再沉默,下面咱们直接用缩影读取占的值。 第一步,在启动文件里把 hotspot handler 的 weak 函数替换成我们自己的版本。第二步,写一段会编来判断当前用的是 msp 还是 psp, 然后调用 c 处理函数。注意 merix 这种写法是错的。正确写法如下, 测试 exc return the bit r i t e q。 如果等于零,使用 msp, m r s e q r 零 m s p r 零等于 m s p m r s n e r o p s p r o equals p s p b hard fought c underscore c underscore handler 第三步,写 c 处理函数直接从站里提取寄存器值, 所以 hard fought simmers harder un 三十二 t pc 等于 sp。 压站顺序中, pc 在 第七个 un 三十二 t l r 等于 sp lr 在 第六个 un two three two underscore t p s r equals sp private unit three two underscore tcfsr equals asterisk zero x e o o o e d two eight semicolon p r i n t f if printridge clocks mail 数据访问违规 n if 或为 perfect if perfect well。 注意, c f s r 的 低十六位是 m m f s r, 高十六位是 bus port 和 usg port 上面的位判断对 cortex m 三 m 四 m 七有效, m 零除以 m 零加可能不完全一样,调试时一定差内存。 实际工程中,我建议直接用开源库 c m batch, 它已经把 hard fork 自动分析调用栈打印全做好了,丢进项目就能用。但是亲自写一遍会让你彻底理解内核是怎么运作的。 你写的不是代码,是给单片机装了个黑匣子。四、实战技巧,如何用 pc 找 bug? 当你拿到 pc 等于零乘零八 八零零一二三四、怎么定位?代码简单迁移时生成点 map 文件,打开 map, 搜地址,零乘零八零零一二三四落在哪个函数区间里?如果地址在函数 a 的 范围内,你就知道死在了函数 a, 如果正好在某个数组长量区,说明直角飞了,配合返回边,你能精准定位到是哪条 c 地址。这一套组合拳打完 part 对 你来说就不叫事,以后同事的板子死机,你过去敲两下打一个 pc, 然后深沉的说, 数组越界了,是不是逼格拉满?五、不要只靠调湿器,现场没有 jink 怎么办? 很多产品发到客户那里,你不能插着 jink 守着,这时候你的 heart fold 分 析工具就成救命稻草。把寄存器值存到后背寄存器或者 e e p r o m 重启后上报,或者干脆通过串口打出让客户拍照发给你。永远不要让你的单片机死的不明不白,就像飞机失事要留黑匣子, 单片机死机也得留个遗言。最后提醒一句, cortex main 除以 m 零加内核的 c f s 二定义不同。调试前先翻数据手册,我是若韵,觉得有用就点赞,下次你的硬汉死机,记得让他写个日记再投胎,下期见。


大家好,我是家电青龙市老杨,做空调 r t o s 项目,几乎都踩过同一个致命的坑。在中断里随手多写一行代码,比如加个日期打印,写个判断逻辑。当时测试没事,可一闪增肌压力测试 系统突然卡死, hardfort, 甚至行为乱的无法复现。很多新手一脸懵,觉得代码没写错,怎么就崩了? 其实根源只有一个,你越过了 r t o s 和中段之间那条看不见的边界。今天就结合空调 r t o s 实战 五分钟,讲到中段以 r t o s 的 核心规则,边界禁忌,还有我当年踩过的血泪坑,不教复杂理论,只讲直接,能用工程经验帮你避开系统崩溃的伏笔。先搞懂一个关键事实,中段和 r t o s 任务是两个平行世界, 这是所有问题的起点。一定要记死在 free r t o s 里,终端不属于 r t o s 的 调度体系,和任务完全是两回事。任务的世界有规矩, 有明确优先级。比如空调的压缩机控制任务,优先级高于温湿度采集任务,受调度器统一管理,能堵塞,能就绪,能让出 cpu。 但终端的世界没有规则,不必调度,不可堵塞, 能在任何时刻打断任务,哪怕正在执行最高运行级的压缩机任务,一个中断过来,也会立即被打断。 rto s 的 设计哲学很简单, 中断只负责打断并通知任务,负责处理和决策。比如空调的风机转速检测中中段,中段只需要告诉任务转速变了,至于怎么调整转速,交给任务去做,别在中段里瞎折腾。 核心铁力中断,你只做三件事,多一件,多危险。如果这节课只记一句话,一定是这句,中断 rs 只负责三件事,读起状态、清除标志、通知任务。 任何超出这个范围的行为都是系统崩溃的。复比结合空调实战几个实力,空调的温湿度传感器中断。正确的做法是在中断里读起传感器的状态,比如采集完成标志,清除中断标志,然后通知信号量或队列, 通知温湿度处理任务,去读取具体数据做逻辑判断。我当年踩过的坑,为了图省事,在中断中直接解析温湿度的数据打印日期,一开始没事,后来空调运行久了, 频繁出现卡死,这就是中断执行时间太长,抢占了任务的 cpu 时间,破坏了 ios 的 调度秩序。记住,健康的 ios 必须执行时间极短, 不写业务逻辑,不操作共享资源,不打印日期,尽快交差给任务。致命误区,在中段里调研普通 r t, o, s, a, p i 并埋坑。很多新手会犯一个错误,在中段里调研 q, c, s, d 类,这些普通的 r, t, o, s, a, p i, 觉得功能一样,殊不知这是致命的。 free r to s 中端只能调动带 from isr 后缀的 api, 比如 q send from s l, 而不是普通的 q send。 这不是笼络设计,而是 free r to s 给中端留的唯一合法通道。带 from isr 的 api 核心是三点,绝不阻塞、不破坏调度器的状态,能正确触发任务切换。 而普通 api 会堵塞,会打乱调度秩序。在中断里调用不会当场崩溃, 但会悄悄破坏系统状态,后续某个时刻突然崩溃,排查起来比登天还难。我当年排查一个空调通信中断的 bug, 折腾了三天之后发现就是在中断里调用普通的队列 api。 红线配置 conferate makes intel 别乱改,这是 free r t o s d。 最容易被忽略的后果。最严重的配置项,很多新手不知道它的作用,随手乱改导致系统不稳定, 一句话就能说清楚它的作用。不是所有中断都能调用 r t o s a p i。 只有中断优先级,数值上大于这个配置之下的中断才能调用。 from i s r a p i u。 限级太高的中断,不允许触碰 r t o s。 内核,比如空调的紧急停机中断 u 限级极高,必须保证实时响应,就不能调用任何 r t o s。 api, 只能做最基础的状态读起和清除标志。而传感器采集中断 u 限级较低,才能调用 from is api 的 通知任务。如果在高 u 限级中断里调用了 from is api, 系统运行就会变得不可预测,不是一定崩,而是你再也推理不出他会怎么运行,现场翻车概率极高。三个灵魂拷问,判断你的终端代码是否安全?写空调 rts 终端代码时,反复问自己三个问题, 能避开百分之八十的坑。这段逻辑真的必须在中段里做吗?比如解析数据,打印日期,交给任务做,完全可以。二、把它放在任务里会发生什么, 只要不影响实质性,就坚决移除中断他是否碰了 rts 边界?比如调用了普通 api, 操作了共享资源,都是越界。最后总结一下,空调 rts 开发中, 中段的核心是克制,中段是系统强制插队者, r to s。 允许他插队,但不允许他在队伍乱搞,只做最基础的通知工作,把复杂的任务交给任务,尊重中段与 r to s 边界系统才能稳定运行。 很多空调 rts 项目崩溃,不是任务设计的不好,而是中途越过了自己的边界,吃透了今天的规则。避开这些坑,你的空调 mcu 系统稳定性会提升。