一、嵌入式开发Bug定位解决
一、问题复现
稳定复现问题才能正确的对问题进行定位、解决以及验证。一般来说,越容易复现的问题越容易解决。
1.1 模拟复现条件
有的问题存在于特定的条件下,只需要模拟出现问题的条件即可复现。对于依赖外部输入的条件,如果条件比较复杂难以模拟可以考虑程序里预设直接进入对应状态。
1.2 提高相关任务执行频率
例如某个任务长时间运行才出现异常则可以提高该任务的执行频率。
1.3 增大测试样本量
程序长时间运行后出现异常,问题难以复现,可以搭建测试环境多套设备同时进行测试。
二、问题定位
缩小排查范围,确认引入问题的任务、函数、语句。
2.1 打印LOG
根据问题的现象,在抱有疑问的代码处增加LOG输出,以此来追踪程序执行流程以及关键变量的值,观察是否与预期相符。
2.2 在线调试
在线调试可以起到和打印LOG类似的作用,另外此方法特别适合排查程序崩溃类的BUG,当程序陷入异常中断(HardFault,看门狗中断等)的时候可以直接STOP查看call stack以及内核寄存器的值,快速定位问题点。
2.3 版本回退
使用版本管理工具时可以通过不断回退版本并测试验证来定位首次引入该问题的版本,之后可以围绕该版本增改的代码进行排查。
2.4 二分注释
二分注释即以类似二分查找法的方式注释掉部分代码,以此判断问题是否由注释掉的这部分代码引起。
具体方法为将与问题不相干的部分代码注释掉一半,看问题是否解决,未解决则注释另一半,如果解决则继续将注释范围缩小一半,以此类推逐渐缩小问题的范围。
2.5 保存内核寄存器快照
Cortex M内核陷入异常中断时会将几个内核寄存器的值压入栈中,如下图:
我们可以在陷入异常中断时将栈上的内核寄存器值写入RAM的一段复位后保留默认值的区域内,执行复位操作后再从RAM将该信息读出并分析,通过PC、LR确认当时执行的函数,通过R0-R3分析当时处理的变量是否异常,通过SP分析是否可能出现栈溢出等。
三、问题分析处理
结合问题现象以及定位的问题代码位置分析造成问题的原因。
3.1 程序继续运行
3.1.1 数值异常
3.1.1.1 软件问题
1、数组越界
写数组时下标超出数组长度,导致对应地址内容被修改。如下:
此类问题通常需要结合map文件进行分析,通过map文件观察被篡改变量地址附近的数组,查看对该数组的写入操作是否存在如上图所示不安全的代码,将其修改为安全的代码。
2、栈溢出
0x20001ff8 | g_val |
0x20002000 | 栈底 |
………… | 栈空间 |
0x20002200 | 栈顶 |
如上图,此类问题也需要结合map文件进行分析。假设栈从高地址往低地址增长,如果发生栈溢出,则g_val的值会被栈上的值覆盖。
出现栈溢出时要分析栈的最大使用情况,函数调用层数过多,中断服务函数内进行函数调用,函数内部申明了较大的临时变量等都有可能导致栈溢出。
解决此类问题有以下方法:
- 在设计阶段应该合理分配内存资源,为栈设置合适的大小;
- 将函数内较大的临时变量加”static”关键字转化为静态变量,或者使用malloc()动态分配,将其放到堆上;
- 改变函数调用方式,降低调用层数。
3、判断语句条件写错
判断语句的条件容易把相等运算符“==”写成赋值运算符“=”导致被判断的变量值被更改,该类错误编译期不会报错且总是返回真。
建议将要判断的变量写到运算符的右边,这样错写为赋值运算符时会在编译期报错。还可以使用一些静态代码检查工具来发现此类问题。
4、同步问题
例如操作队列时,出队操作执行的过程中发生中断(任务切换),并且在中断(切换后的任务)中执行入队操作则可能破坏队列结构,对于这类情况应该操作时关中断(使用互斥锁同步)。
5、优化问题
如上图程序,本意是等待irq中断之后不再执行foo()函数,但被编译器优化之后,实际运行过程中flg可能被装入寄存器并且每次都判断寄存器内的值而不重新从ram里读取flg的值,导致即使irq中断发生foo()也一直运行,此处需要在flg的申明前加“volatile”关键字,强制每次都从ram里获取flg的值。
3.1.1.2 硬件问题
1、芯片BUG
芯片本身存在BUG,在某些特定情况下给单片机返回一个错误的值,需要程序对读回的值进行判断,过滤异常值。
2、通信时序错误
例如电源管理芯片Isl78600,假设现在两片级联,当同时读取两片的电压采样数据时,高端芯片会以固定周期通过菊花链将数据传送到低端芯片,而低端芯片上只有一个缓存区。
如果单片机不在规定时间内将低端芯片上的数据读走那么新的数据到来时将会覆盖当前数据,导致数据丢失。此类问题需要仔细分析芯片的数据手册,严格满足芯片通信的时序要求。
3.1.2 动作异常
3.1.2.1 软件问题
1、设计问题
设计中存在错误或者疏漏,需要重新评审设计文档。
2、实现与设计不符
代码的实现与设计文档不相符需要增加单元测试覆盖所有条件分支,进行代码交叉review。
3、状态变量异常
例如记录状态机当前状态的变量被篡改,分析该类问题的方法同前文数值异常部分。
3.1.2.2 硬件问题
1、硬件失效
目标IC失效,接收控制指令后不动作,需要排查硬件。
2、通信异常
与目标IC通信错误,无法正确执行控制命令,需要使用示波器或逻辑分析仪去观察通信时序,分析是否发出的信号不对或者受到外部干扰。
3.2 程序崩溃
3.2.1 停止运行
3.2.1.1 软件问题
1、HardFault
以下情况会造成HardFault:
- 在外设时钟门未使能的情况下操作该外设的寄存器;
- 跳转函数地址越界,通常发生在函数指针被篡改,排查方法同数值异常;
- 解引用指针时出现对齐问题:
以小端序为例,如果我们声明了一个强制对齐的结构体如下:
地址 | 0x00000000 | 0x00000001 | 0x00000002 | 0x00000003 |
变量名 | Val0 | Val1_low | Val1_high | Val2 |
值 | 0x12 | 0x56 | 0x34 | 0x78 |
此时a.val1的地址为0x00000001,如果以uint16_t类型去解引用此地址则会因为对齐问题进入HardFault,如果一定要用指针方式操作该变量则应当使用memcpy()。
2、中断服务函数中未清除中断标志
中断服务函数退出前不正确清除中断标志,当程序执行从中断服务函数内退出后又会立刻进入中断服务函数,表现出程序的“假死”现象。
3、NMI中断
调试时曾遇到SPI的MISO引脚复用NMI功能,当通过SPI连接的外设损坏时MISO被拉高,导致单片机复位后在把NMI引脚配置成SPI功能之前就直接进入NMI中断,程序挂死在NMI中断中。这种情况可以在NMI的中断服务函数内禁用NMI功能来使其退出NMI中断。
3.2.1.2 硬件问题
1、晶振未起振
2、供电电压不足
3、复位引脚拉低
3.2 .2 复位
3.2.2.1 软件问题
1、看门狗复位
除了喂狗超时导致的复位以外,还要注意看门狗配置的特殊要求,以Freescale KEA单片机为例,该单片机看门狗在配置时需要执行解锁序列(向其寄存器连续写入两个不同的值),该解锁序列必须在16个总线时钟内完成,超时则会引起看门狗复位。此类问题只能熟读单片机数据手册,注意类似的细节问题。
3.2.2.2 硬件问题
1、供电电压不稳
2、电源带载能力不足
四、回归测试
问题解决后需要进行回归测试,一方面确认问题是否不再复现,另一方面要确认修改不会引入其他问题。
五、经验总结
总结本次问题产生的原因及解决问题的方法,思考类似问题今后如何防范,对相同平台产品是否值得借鉴,做到举一反三,从失败中吸取经验。
二、DNS 域名解析过程
什么是 DNS域名解析
我们首先要了解域名和 IP 地址的区别。IP 地址是互联网上计算机唯一的逻辑地址,通过 IP 地址实现不同计算机之间的相互通信,每台联网计算机都需要通过 IP 地址来互相联系和分别。
但由于 IP 地址是由一串容易混淆的数字串构成,人们很难记忆所有计算机的 IP 地址,这样对于我们日常工作生活访问不同网站是很困难的。基于这种背景,人们在 IP 地址的基础上又发展出了一种更易识别的符号化标识,这种标识由人们自行选择的字母和数字构成,相比 IP 地址更易被识别和记忆,逐渐代替 IP 地址成为互联网用户进行访问互联的主要入口。这种符号化标识就是域名。
域名虽然更易被用户所接受和使用,但计算机只能识别纯数字构成的 IP 地址,不能直接读取域名。因此要想达到访问效果,就需要将域名翻译成 IP 地址。而 DNS 域名解析承担的就是这种翻译效果。
DNS域名解析过程
当我们在浏览器地址栏中输入 www.baidu.com 时,DNS 解析将会有将近 10 个步骤,这个过程大体大体由一张图可以表示:
整个过程大体描述如下,其中前两个步骤是在本地电脑内完成的,后 8 个步骤涉及到真正的域名解析服务器:
第一步、
本地电脑会检查浏览器缓存中有没有这个域名对应的解析过的 IP 地址,如果缓存中有,这个解析过程就结束。浏览器缓存域名也是有限制的,不仅浏览器缓存大小有限制,而且缓存的时间也有限制,通常情况下为几分钟到几小时不等,域名被缓存的时间限制可以通过 TTL 属性来设置。这个缓存时间太长和太短都不太好,如果时间太长,一旦域名被解析到的 IP 有变化,会导致被客户端缓存的域名无法解析到变化后的 IP 地址,以致该域名不能正常解析,这段时间内有一部分用户无法访问网站。如果设置时间太短,会导致用户每次访问网站都要重新解析一次域名。
第二步、
如果浏览器缓存中没有数据,浏览器会查找操作系统缓存中是否有这个域名对应的 DNS 解析结果。其实操作系统也有一个[域名解析]的过程,在 Linux 中可以通过 / etc/hosts 文件来设置,而在 windows 中可以通过配置 C:\Windows\System32\drivers\etc\hosts 文件来设置,用户可以将任何域名解析到任何能够访问的 IP 地址。例如,我们在测试时可以将一个域名解析到一台测试服务器上,这样不用修改任何代码就能测试到单独服务器上的代码的业务逻辑是否正确。正是因为有这种本地 DNS 解析的规程,所以有黑客就可能通过修改用户的域名来把特定的域名解析到他指定的 IP 地址上,导致这些域名被劫持。
第三步、
前两个过程无法解析时,就要用到我们网络配置中的 "DNS 服务器地址" 了。操作系统会把这个域名发送给这个本地 DNS 服务器。每个完整的内网通常都会配置本地 DNS 服务器,例如用户是在学校或工作单位接入互联网,那么用户的本地 DNS 服务器肯定在学校或工作单位里面。它们一般都会缓存域名解析结果,当然缓存时间是受到域名的失效时间控制的。大约 80% 的域名解析到这里就结束了,后续的 DNS 迭代和递归也是由本地 DNS 服务器负责。
windows 在这配置:控制面板 -》网络和共享中心 -》更改适配器设置 -》选中目标适配器右键选择属性 -》Internet 协议版本 4(TCP/IPv4)-》配置 DNS 地址。
Linux 在这设置:/etc/resolv.conf
第四步、
如果本地 DNS 服务器仍然没有命中,就直接到根 DNS 服务器请求解析。
第五步、
根 DNS 服务器返回给本地 DNS 域名服务器一个顶级 DNS 服务器地址,它是国际顶级域名服务器,如. com、.cn、.org 等,全球只有 13 台左右。
第六步、
本地 DNS 服务器再向上一步获得的顶级 DNS 服务器发送解析请求。
第七步、
接受请求的顶级 DNS 服务器查找并返回此域名对应的 Name Server 域名服务器的地址,这个 Name Server 服务器就是我要访问的网站域名提供商的服务器,其实该域名的解析任务就是由域名提供商的服务器来完成。 比如我要访问 www.baidu.com,而这个域名是从 A 公司注册获得的,那么 A 公司上的服务器就会有 www.baidu.com 的相关信息。
第八步、
Name Server 服务器会查询存储的域名和 IP 的映射关系表,再把查询出来的域名和 IP 地址等等信息,连同一个 TTL 值返回给本地 DNS 服务器。
第九步、
返回该域名对应的 IP 和 TTL 值,本地 DNS 服务器会缓存这个域名和 IP 的对应关系,缓存时间由 TTL 值控制。
第十步、
把解析的结果返回给本地电脑,本地电脑根据 TTL 值缓存在本地系统缓存中,域名解析过程结束在实际的 DNS 解析过程中,可能还不止这 10 步,如 Name Server 可能有很多级,或者有一个 GTM 来负载均衡控制,这都有可能会影响域名解析过程。另外,搜索公众号Java就该这么学后台回复“面试”,获取一份惊喜礼包。
递归查询和迭代查询的区别
DNS 客户端和本地名称服务器是递归,而本地名称服务器和其他名称服务器之间是迭代。
DNS 递归名称解析:在 DNS 递归名称解析中,当所配置的本地名称服务器解析不了时,后面的查询工作是由本地名称服务器替代 DNS 客户端进行的(以 “本地名称服务器” 为中心),只需要本地名称服务器向 DNS 客户端返回最终的查询结果即可。
DNS 迭代名称解析:(或者叫 “迭代查询”)的所有查询工作全部是 DNS 客户端自己进行(以“DNS 客户端” 自己为中心)。在条件之一满足时就会采用迭代名称解析方式:
- 在查询本地名称服务器时,如果客户端的请求报文中没有申请使用递归查询,即在 DNS 请求报头部的 RD 字段没有置 1。相当于说 “你都没有主动要求我为你进行递归查询,我当然不会为你工作了”。
- 客户端在 DNS 请求报文中申请使用的是递归查询(也就是 RD 字段置 1 了),但在所配置的本地名称服务器上是禁用递归查询(DNS 服务器一般默认支持递归查询的),即在应答 DNS 报文头部的 RA 字段置 0。
域名解析记录
主要分为 A 记录、MX 记录、CNAME 记录、NS 记录和 TXT 记录
1、A 记录
A 代表 Address,用来指定域名对应的 IP 地址,如将 item.taobao.com 指定到 115.238.23.xxx,将 switch.taobao.com 指定到 121.14.24.xxx。A****记录可以将多个域名解析到一个 IP 地址,但是不能将一个域名解析到多个 IP 地址
2、MX 记录
Mail Exchange,就是可以将某个域名下的邮件服务器指向自己的 Mail Server,如 taobao.com 域名的 A 记录 IP 地址是 115.238.25.xxx,如果将 MX 记录设置为 115.238.25.xxx,即 xxx@taobao.com 的邮件路由,DNS 会将邮件发送到 115.238.25.xxx 所在的服务器,而正常通过 Web 请求的话仍然解析到 A 记录的 IP 地址
3、CNAME 记录
Canonical Name,即别名解析。所谓别名解析就是可以为一个域名设置一个或者多个别名,如将 aaa.com 解析到 bbb.net、将 ccc.com 也解析到 bbb.net,其中 bbb.net 分别是 aaa.com 和 ccc.com 的别名
4、NS 记录
为某个域名指定 DNS 解析服务器,也就是这个域名由指定的 IP 地址的 DNS 服务器取解析
5、TXT 记录
为某个主机名或域名设置说明,如可以为 ddd.net 设置 TXT 记录为 "这是 XXX 的博客" 这样的说明
三、STM32如何分配原理图IO
在画原理图之前,一般的做法是先把引脚分类好,然后才开始画原理图。
要想根据功能来分配 IO,那就得先知道每个 IO 的功能说明,这个我们可以从官方的数据手册里面找到。在学习的时候,有两个官方资料我们会经常用到,一个是参考手册(英文叫 Referencemanual),另外一个是数据手册(英文叫 Data Sheet)。两者的具体区别见下表。
数据手册主要用于芯片选型和设计原理图时参考,参考手册主要用于在编程的时候查阅。在数据手册中,有关引脚定义的部分在 Pinouts and pin description 这个小节中。数据手册中对引脚定义具体定义见下表。
对上表中引脚定义的解读,见下图。
举例,如果MCU 型号是 STM32F103VET6,封装为 LQFP100,我们在数据手册中找到这个封装的引脚定义,然后根据引脚序号,一个一个复制出来,整理成 excel 表,分配好之后就开始画原理图,具体引脚分类见下表。
四、开关电源里特殊电子元件的类型和用途总结
一、特种二极管
1. 快恢复二极管(FRD)----快恢复二极管的反向恢复时间一般为几百纳秒,正向压降为0.6V~1V,正向电流为几安培至几千安培,反向峰值电压可达几百伏特至几千伏特,可用作开关电源中的输出整流管、一次侧钳位保护电路的阻塞二极管。
快恢复二极管(FRD)
2. 超快恢复二极管(SRD)----超快恢复二极管则是在快恢复二极管基础上发展而成的,其反向恢复电荷进一步减小,反向恢复时间可低至几十纳秒,可用作开关电源适配器输出整流管、阻塞二极管、反馈电路中的整流管。
超快恢复二极管(SRD)
3. 肖特基二极管(SBD)----全称为肖特基势垒二极管,它属于低电压、低功耗、大电流、超高速半导体功率器件,其反向恢复时间可小到几纳秒,正向导通压降仅为0.4V左右,整流电流可达几十至几百安培。特别适合做开关电源充电器低压输出电路中的整流二极管、续流二极管。
肖特基二极管(SBD)
4. 瞬变变压抑制二极管(TVS)----亦称瞬态电压抑制器,其响应速度极快、钳位电压稳定,是一种新型过电压保护器件,可用来保护开关电源PWM集成电路、MOS功率器件以及其他对电压敏感的半导体器件。
瞬变变压抑制二极管(TVS)
5. 双向触发二极管(DIAC)----亦称二端交流器件,常与晶闸管配套使用,构成开关电源变压器输出过电压保护电路。
双向触发二极管(DIAC)
二、特种电阻器
1. 熔断电阻器(FR)----熔断电阻器亦称保险电阻器或可熔断电阻器,它兼有电阻器和熔断器的功能,熔断电流从几十毫安到几十安培,熔断时间为几秒至几十秒。
熔断电阻器(FR)
2. 自恢复熔丝管(RF)----亦称自恢复保险丝,具有可自动复原的性能,反复使用,不需要维修更换。
自恢复熔丝管(RF)
3. 软启动电阻----它属于负温度系数热敏电阻(NTCR),其特点时标称阻值低(仅为1Ω~47Ω)、额定功率高(10~500W)、工作电流大(1~10A),适合作开关电源适配器的启动保护元件。
软启动电阻
4. 压敏电阻器(VSR)----工作电压范围宽(6V~3000V,分若干档),对过电压脉冲响应速度快(几纳秒至几十纳秒),耐冲击电流的能力很强(可达100A~20Ka),漏电流小(低于几微安至几十微安),电阻温度系数低(小于0.05%/℃),价格低廉。
可构成开关电源适配器的过电压保护电路、防雷击保护电路、消除火花电路、浪涌电压吸收回路等。
5. 数字电位器(DCP)----与可调式开关稳压器配套使用,构成可编程开关稳压器。
数字电位器(DCP)
三、晶闸管
1. 单向晶闸管(SCR)----与双向触发二极管配套使用,构成过开关电源适配器的电压保护电路。
单向晶闸管(SCR)
2. 双向晶闸管(TRIAC)----可构成开关电源交流输入侧的过电压保护电路。
双向晶闸管(TRIAC)
四、其他
1. 光耦合器:线性光耦合器的电流传输比(CTR)与直流输入电流(If)的特性曲线具有良好的线性度。在传输小信号时,能使输出与输入呈线性关系,适合构成精密开关电源中的光藕反馈电路,并实现二次侧与一次侧的隔离。
光耦
2. 滤波器:亦称EMI滤波器,它属于双向射频滤波器,一方面能滤除从交流线引入的外部电磁干扰;
另一方面还可避免开关电源适配器向外部发出噪声干扰,能显著提高开关电源适配器的抗干扰能力,并使开关电源符合电磁兼容性(EMC)标准。
3. 可调式精密并联稳压器:例如TL431,可广泛用于精密开关电源适配器、充电器中,还能构成电压比较器、电源电压监视器、延时电路、精密恒流源等。
4. 磁珠:有管状、片状磁珠及磁珠阵列,能抑制开关电源适配器、充电器的开关噪声和尖峰干扰。有管状、片状磁珠及磁珠阵列,能抑制开关电源适配器、充电器的开关噪声和尖峰干扰。
五、嵌入式开发中的I2C总线
硬盘、U盘等这些设备中都有一个“掉电保存”的器件,他们的特点就是没电了之后,存在他们上面的信息不会丢失,就像人的大脑,有记忆功能。在工业领域也非常常见这种器件,一般有EEPROM和FLASH。它俩共同点是可读可写,断电保存。
不同点是EEPROM写之前不用擦除,而FLASH写之前一定要擦除,否则写不成功;EEPROM一般都使用I2C总线来通信,而FLASH一般是使用SPI总线。
EEPROM存储器系统架构图如下。
位传输
I2C总线是由飞利浦(Philips)公司开发的一种双向二线制同步串行总线,实现有效的IC间的控制,它只需要两根线(SDA和SCL)即可在连接于总线上的器件之间传送信息。
I2C总线在传输数据都是按照bit来传送。SCL为时钟线,SDA为数据线;在SCL时钟线为高电平时,SDA数据线上的电平不允许被修改,SCL时钟线为低电平时,SDA数据线上的电平可为高/低。I2C总线的位传输,如下图所示。
起始条件:SCL为高电平时,SDA由高电平向低电平切换;表示开始传送数据。
停止条件:SCL为高电平时,SDA由低电平向高电平跳变;表示结束传送数据。
空闲条件:I2C总线的SDA和SCL两条信号线同时处于高电平时;表示空闲状态。
数据传输
字节传输
发送数据时,由主机先发送一个起始信号,再将SDA信号切换为输出模式,然后将8位数据依次由高到低发送出去。
发送完成后,主机将SDA信号切换为输入模式,等待丛机回应ACK或NAK;再发下一笔数据。I2C总线数据传输示意图如下。
丛机地址
在I2C总线系统中,每个设备都有它的固定地址,一般由芯片的A0,A1和A2决定。丛机地址字节由七位地址位(D7-D1位)和一位方向位(为D0位)组成。
器件地址的D7-D4一般都是被厂家固定了为1111,余下的D3,D2和D1连接到芯片的A2,A1和A0决定;D0为0x00表示写,D0为0x01表示读。大家看例程都是些0xA0和0xA1就是这个原因。EEPROM的器件地址示意图如下。
读写过程
写数据过程
- 主机发送I2C总线停止信号,防止总线忙写数据失败
- 主机发送I2C总线复位信号,确保写数据之前总线处于空闲状态
- 主机发送I2C总线开始信号,启动一次数据的写入
- 主机发送I2C丛机地址和写模式(W/R=0)信号,并且等待一个丛机的应答信号
- 主机接收到ACK的应答信号后,开始多个字节的写入,每写完一个字节需要等待一个丛机的应答信号
- 主机接收到ACK的应答信号后,发送2IC总线停止信号,确保总线处于空闲状态
读数据过程
- 主机发送I2C总线停止信号,防止总线忙写数据失败
- 主机发送I2C总线复位信号,确保读数据之前总线处于空闲状态
- 主机发送I2C总线开始信号,启动一次数据读取
- 主机发送I2C丛机地址和读模式(W/R=1)信号,并且等待一个丛机的应答信号
- 主机接收到ACK的应答信号后,开始多个字节的读取,每读完一个字节需要给丛机发送一个ACK应答信号
- 主机接收到ACK的应答信号后,发送I2C总线停止信号,确保总线处于空闲状态
总结
I2C总线在嵌入式开发中应用中非常广泛,基本上所有的电力电子设备都会用到这个总线。