嵌入式软件C语言可靠性设计 问题汇总

上传人:xgs****56 文档编号:8950283 上传时间:2020-04-02 格式:DOC 页数:30 大小:90.38KB
返回 下载 相关 举报
嵌入式软件C语言可靠性设计 问题汇总_第1页
第1页 / 共30页
嵌入式软件C语言可靠性设计 问题汇总_第2页
第2页 / 共30页
嵌入式软件C语言可靠性设计 问题汇总_第3页
第3页 / 共30页
点击查看更多>>
资源描述
嵌入式软件可靠性设计 问题集锦 目 录 1 程序员理解错误 2 1 1 英文标点被误写成中文标点 2 1 2 与 与 容易混 2 1 3 程序员输入错误 2 1 4 数组问题 2 1 5 switch case 语句中的 break 关键字 3 1 5 变量赋值 3 1 6 指针的加减运算 4 1 7 增量运算符 和减量运算符 5 2 编译器语义检查 6 2 1 数据类型问题 6 2 2 误加标点符号 6 2 3 编译器忽略掉多余的空格符和换行符 6 2 4 数组越界 7 2 5 数组声明具有外部链接时大小应显式声明 7 2 6 编译器检查不出数组越界 8 2 7 编译器与 volatile 限定符 9 2 8 定义为 volatile 的变量的作用过程 11 2 9 局部变量必须显式初始化 11 3 不合理的优先级 12 3 1 常规使用可能引起误会的运算符 13 4 隐式转换和强制转换 13 4 1 有符号和无符号 char 和 short 类型自动转换 13 4 2 混合数据类型运算中会转换成较高级别数据类型 14 4 3 赋值语句计算结果被转换成被赋予值的变量类型 15 4 4 作为函数参数被传递时的数据类型转换 15 4 5 C 语言强制类型转换规则 15 4 6 通用编程建议 15 5 判错 15 5 1 具有形参的函数 需判断传递来的实参是否合法 16 5 2 仔细检查函数的返回值 17 5 3 防止指针越界 17 5 4 防止数组越界 17 5 5 数学运算 18 5 6 其它可能出现运行时错误的地方 20 6 容错 20 6 1 关键数据多区备份 取数据采用 表决法 20 6 2 非易失性存储器的数据存储 21 6 3 软件锁 21 6 4 通信数据的检错 21 6 5 开关量输入的检测 确认 22 6 6 开关量输出 22 6 7 初始化信息的保存与恢复 22 6 8 陷阱 22 6 9 while 循环 22 6 10 系统自检 22 1 程序员理解错误 1 1 英文标点被误写成中文标点 比较运算符 误写成赋值运算符 代码 if x 5 本意是比较变量 x 是否等于常量 5 但是误将 写成了 if 语句恒为真 如 果在逻辑判断表达式中出现赋值运算符 现在的大多数编译器会给出警告信息 并非所有 程序员都会注意到这类警告 因此有经验的程序员使用下面的代码来避免此类错误 if 5 x 将常量放在变量 x 的左边 即使程序员误将 写成了 编译器会产生一个任 谁也不能无视的语法错误信息 不可给常量赋值 1 2 与 与 容易混 复合运算符会给程序带来隐含 Bug 如下所示代码 tmp 1 该代码本意是想表达 tmp tmp 1 但是将复合赋值运算符 误写成 将正整数常量 1 赋值给变量 tmp 编译器会欣然接受这类代码 连警告都不会产生 与 同理 类似的逻辑与 break case THING2 if x STUFF do first stuff if y OTHER STUFF break do later stuff 代码的意图是跳转到这里 initialize modes pointer break default processing 但事实上跳到了这里 use modes pointer 致使 modes pointer 未初始化 1 5 变量赋值 int a 34 int b 034 变量 a 和 b 相等吗 No 以 0 x 为前缀的 16 进制常量 10 进制常量不需要前缀 数字 0 为前缀的 8 进制 误用 8 进制的例子 最后一个数组元素赋值错误 1 a 0 106 十进制数 106 2 a 1 112 十进制数 112 3 a 2 052 实际为十进制数 42 本意为十进制 52 1 6 指针的加减运算 下面的代码运行在 32 位 ARM 架构上 执行后 a 和 p 的值 int a 1 int p int 0 x00001000 a a 1 p p 1 a 2 但是 p 的结果是 0 x00001004 指针 p 加 1 后 p 的值增加了 4 原因是指针做加 减运算时是以指针的数据类型为单位 p 1 实际上是 p 1 sizeof int 不理解这一点 在 使用指针直接操作数据时极易犯错 比如下面对连续 RAM 初始化零操作代码 unsigned int pRAMaddr 定义地址指针变量 for pRAMaddr StartAddr pRAMaddr EndAddr pRAMaddr 4 pRAMaddr 0 x00000000 指定 RAM 地址清零 由于 pRAMaddr 是一个指针变量 所以 pRAMaddr 4 代码其实使 pRAMaddr 偏移了 4 sizeof int 16 个字节 所以每执行一次 for 循环 会使变量 pRAMaddr 偏移 16 个字节 空间 但只有 4 字节空间被初始化为零 其它的 12 字节数据的内容 在大多数架构处理器 中都会是随机数 对于 sizeof 这里强调两点 第一它是一个关键字 而不是函数 并且它默认返回 无符号整型数据 无符号 第二 使用 sizeof 获取数组长度时 不要对指针应用 sizeof 操作符 比如下面的例子 void ClearRAM char array int i for i 0 i sizeof array sizeof array 0 i array 实际上是指针 for i 0 i sizeof array 20 sizeof array 0 i array i 0 x00 int main void char Fle 20 ClearRAM Fle 只能清除数组 Fle 中的前四个元素 对于数组 array 20 使用代码 sizeof array 20 sizeof array 0 可以获得数组 的元素 这里为 20 但数组名和指针容易混淆 有且只有一种情况下可以当做指针 就 是数组名作为函数形参时 数组名被认为是指针 同时不能再兼任数组名 只有这种情况 数组名才可当做指针 但容易引发风险 在 ClearRAM 函数内 作为形参的 array 不再是 数组名了 而成了指针 sizeof array 相当于求指针变量占用的字节数 在 32 位系统下 该值为 4 sizeof array sizeof array 0 的运算结果也为 4 所以在 main 函数中 调用 ClearRAM Fle 也只能清除数组 Fle 中的前四个元素了 1 7 增量运算符 和减量运算符 既可以做前缀也可以做后缀 前缀和后缀的区别在于值的增加或减少这一动作发生的 时间是不同的 作为前缀是先自加或自减然后做别的运算 作为后缀时 是先做运算 之 后再自加或自减 下面的例子可以很好的解释前缀和后缀的区别 1 int a 8 b 2 y 2 y a b 代码执行后 y 的值是多少 y a b 当赋值给变量 y 时 a 的值为 8 b 的值为 1 所以变量 y 的值为 9 赋值完成后 变 量 a 自加 a 的值变为 9 千万不要以为 y 的值为 10 分解成两条语句 1 y a b 2 a a 1 2 编译器语义检查 萝卜快了不洗泥 C 语言足够灵活且几乎不进行任何运行时检查 比如数组越界 指 针是否合法 运算结果是否溢出等 C 语言足够灵活 对于一个数组 a 30 它允许使用像 a 1 这样的形式来快速获取数 组首元素所在地址前面的数据 2 1 数据类型问题 下面的两个例子的问题是什么 死循环 unsigned char i for i 0 i 0 i 无符号 char 类型 范围为 0 255 所以无符号 char 类型变量 i 永远小于 256 第一个 for 循环无限执行 永远大于等于 0 第二个 for 循环无限执行 2 2 误加标点符号 1 if a b 这里误加了一个分号 2 a b 这句代码一直被执行 2 3 编译器忽略掉多余的空格符和换行符 1 if n 3 2 return 这里少加了一个分号 3 logrec data x 0 4 logrec time x 1 5 logrec code x 2 这段代码的本意是 n 3 时 表达式 logrec data x 0 就不会被执行 给程序 埋下了隐患 2 4 数组越界 代码在硬件上运行 一段时间后 LCD 显示屏上的一个数字不正常的被改变 经过一段 时间的调试 问题被定位到下面的一段代码中 int SensorData 30 for i 30 i 0 i SensorData i 这里声明了拥有 30 个元素的数组 不幸的是 for 循环代码中误用了本不存在的数 组元素 SensorData 30 按照代码改变了数组元素 SensorData 30 所在位置的值 SensorData 30 所在的位置原本是一个 LCD 显示变量 很多编译器会对上述代码产生警告 赋值超出数组界限 但并非所有程序员都对编 译器警告保持足够敏感 而且编译器也并不能检查出数组越界的所有情况 2 5 数组声明具有外部链接时大小应显式声明 模块 A 中定义数组 int SensorData 30 在模块 B 中引用该数组 但由于你引用代码并不规范 这里没有显式声明数组大小 但编译器也允许这么做 Comment w1 extern int SensorData 如果在模块 B中存在和上面一样的代码 for i 30 i 0 i SensorData i 这次 编译器不会给出警告信息 因为编译器压根就不知道数组的元素个数 所以 当一个数组声明为具有外部链接 它的大小应该显式声明 2 6 编译器检查不出数组越界 函数 func 的形参是一个数组形式 函数代码简化如下所示 char func char SensorData 30 unsigned int i for i 30 i 0 i SensorData i 这个给 SensorData 30 赋初值的语句 编译器也是不给任何警告的 实际上 编译器 是将数组名 SensorData隐含的转化为指向数组第一个元素的指针 函数体是使用指针的形 式来访问数组的 它当然也不会知道数组元素的个数了 造成这种局面的原因之一是 C 编 译器的作者们认为指针代替数组可以提高程序效率 而且 还可以简化编译器的复杂度 指针和数组容易给程序造成混乱 如何区分 可以将数组名等同于指针的情况有且只有一处 就是数组作为函数形参时 其它时候 数组名是数组名 指针是指针 另一个例子 编译器同样检查不出数组越界 用数组来缓存通讯中的一帧数据 在通讯中断中将接收的数据保存到数组中 直到一 帧数据完全接收后再进行处理 即使定义的数组长度足够长 接收数据的过程中也可能发 生数组越界 特别是干扰严重时 这是由于外界的干扰破坏了数据帧的某些位 对一帧的 数据长度判断错误 接收的数据超出数组范围 多余的数据会改写数组相邻的变量 造成 系统崩溃 由于中断事件的异步性 这类数组越界编译器无法检查到 如果局部数组越界 可能引发 ARM 架构硬件异常 一设备用于接收无线传感器的数据 一次升级后 发现接收设备工作一段时间后会死机 调试表明 ARM7 处理器发生了硬件异常 异常处理代码是一段死循环 死机的直接原因 接收设备有一个硬件模块用于接收无线 传感器的整包数据并存在自己的硬件缓冲区中 当一帧数据接收完成后 使用外部中断通 知设备取数据 外部中断服务程序精简后如下所示 irq ExintHandler void Unsigned char DataBuf 50 GetData DataBug 从硬件缓冲区取一帧数据 由于存在多个无线传感器近乎同时发送数据的可能 加之 GetData 函数保护力度不 够 数组 DataBuf 在取数据过程中发生越界 由于数组 DataBuf 为局部变量 被分配在堆 栈中 同在此堆栈中的还有中断发生时的运行环境以及中断返回地址 溢出的数据将这些 数据破坏掉 中断返回时 PC 指针可能变成一个不合法值 硬件异常由此产生 但如果被利用 则可作病毒 1988 年 第一个网络蠕虫在一天之内感染了 2000 到 6000 台计算机 利用的正是一个标准输入库函数的数组越界 Bug 起因是一个标准输入输 出库函数 gets 原来设计为从数据流中获取一段文本 遗憾的是 gets 函数没有规定 输入文本的长度 gets 函数内部定义了一个 500 字节的数组 攻击者发送了大于 500 字 节的数据 利用溢出的数据修改了堆栈中的 PC 指针 从而获取了系统权限 2 7 编译器与 volatile 限定符 源文件定义变量 unsigned int a volatile unsigned int a 头文件声明变量 extern unsigned long a extern unsigned int a 编译器 提示语法错误 变量 a 声明类型不一致 编译器 不给错误信息 或仅一条警告 volatile 属于类型限定符 另一个常见的类型限定符是 const 关键字 限定符 volatile 在嵌入式软件中至关重要 用来告诉编译器不要优化它修饰的变量 模块 A 源文件中定义变量 volatile unsigned int TimerCount 0 该变量用来在一个定时器服务程序中进行软件计时 TimerCount 读取 IO 端口 1 的值 在模块 A 的头文件中 声明变量 extern unsigned int TimerCount 这里漏掉了类型限定符 volatile 模块 B 中 要使用 TimerCount 变量进行精确的软件延时 include A h 首先包含模块 A 的头文件 TimerCount 0 while TimerCount TIMER VALUE 延时一段时间 这是一个死循环 在模块 B 中 变量 TimerCount 是被当作 unsigned int 类型变量 由于寄存器速度远 快于 RAM 编译器在使用非 volatile 限定变量时 先将变量从 RAM 中拷贝到寄存器中 如 果同一个代码块再次用到该变量 就不再从 RAM 中拷贝数据而是直接使用之前寄存器备份 值 代码 while TimerCount TIMER VALUE 中 变量 TimerCount 仅第一次执行时被使用 之后都是使用的寄存器备份值 而这个寄存器值一直为 0 所以程序无限循环 下面的流 程图说明了程序使用限定符 volatile 和不使用 volatile 的执行过程 使用了 volatile 会怎样 2 8 定义为 volatile 的变量的作用过程 优化器在用到这个变量时必须每次都小心地重新读取这个变量的值 而不是使用保存 在寄存器里的备份 下面左侧函数有什么问题 代码目的是用来返指针 ptr 指向值的平方 int square volatile int ptr return ptr ptr 如果在两次读数据的期间 ptr 的值被改变 a 和 b 将是不同的 则得不出 a a 的结果来 编译器将产生类似下面的代码 int square volatile int ptr int a b a ptr b ptr return a b 正确代码 long square volatile int ptr int a a ptr return a a 2 9 局部变量必须显式初始化 例 1 unsigned intGetTempValue void unsigned int sum 定义局部变量 保存总值 但其初值并不一定为 0 for i 0 i 10 i sum CollectTemp 函数 CollectTemp 可以得到当前的温度值 return sum 10 例 2 char GetData void char buffer 100 局部数组 return buffer 一旦程序执行结束离开 局部变量的作用域即被释放 所以返回指向局部变量的指针 是没有实际意义的 该指针指向的区域可能会被其它程序使用 其值会被改变 3 不合理的优先级 C 语言有 32 个关键字 却有 34 个运算符 要记住所有运算符的优先级是困难的 不合理的 define 会加重优先级问题 让问题变得更加隐蔽 define READSDA IO0PIN Comment w2 如果不了解表达式里的类型提升 认为在运算过程中变量 port一直是 unsigned char 类型的 期望的运算过程 port 结果为 0 xa5 0 xa5 4 结果为 0 x0a 实际上 result 8 的结果却是 0 xfa 在 ARM结构下 int 类型为 32位 变量 port在 运算前被提升为 int类型 port 结果为 0 xffffffa5 0 xa5 4 结果为 0 x0ffffffa 赋值 给变量 result 8 发生类型截断 隐性转换 result 8 0 xfa 正确表达式语句应该为 result 8 unsigned char port 4 强制转换 4 2 混合数据类型运算中会转换成较高级别数据类型 数据类型的级别从高到低的顺序 long double double float unsigned long long long long unsigned long long unsigned int int 这种类型提升通常都是件 好事 但往往有很多程序员不能真正理解这句话 从而做一些想当然的事情 比如下面的 例子 int 类型表示 16位 uint16 t u16a 40000 16位无符号变量 uint16 t u16b 30000 16位无符号变量 uint32 t u32x 32位无符号变量 uint32 t u32y u32x u16a u16b u32x 70000还是 4464 u32y uint32 t u16a u16b u32y 70000还是 4464 u32x和 u32y的结果都是 4464 16 位值范围 0 65536 超出值域范围部分 4464 不要认为表达式中有一个高类别 uint32 t类型变量 编译器都会帮你把所有其他低类别都 提升到 uint32 t类型 正确的书写方式 u32x uint32 t u16a uint32 t u16b 或者 u32x uint32 t u16a u16b 后一种写法在本表达式中是正确的 但是在其它表达式中不一定正确 比如 uint16 t u16a u16b u16c uint32 t u32x u32x u16a u16b uint32 t u16c 错误写法 u16a u16b 仍可能溢出 4 3 赋值语句计算结果被转换成被赋予值的变量类型 这一过程可能导致类型提升 也可能导致类型降级 降级可能会导致问题 比如将运 算结果为 321 的值赋值给 8 位 char 类型变量 程序必须对运算时的数据溢出做合理的处理 4 4 作为函数参数被传递时的数据类型转换 char 和 short 会被转换为 int float 会被转换为 double 4 5 C 语言强制类型转换规则 并非所有强制类型转换都是有风险的 把一个整数值转换为一种具有相同符号的更 宽类型时 是绝对安全的 精度高的类型强制转换为精度低的类型时 通过丢弃适当数量的最高有效位来获取 结果 也就是说会发生数据截断 并且可能改变数据的符号位 精度低的类型强制转换为精度高的类型时 如果两种类型具有相同的符号 那么没 什么问题 需要注意的是负的有符号精度低类型强制转换为无符号精度高类型时 会不直观的执行符号扩展 例如 unsigned int bob signed char fred 1 bob unsigned int fred 发生符号扩展 此时 bob 为 0 xFFFFFFFF 4 6 通用编程建议 打开编译器所有警告开关 重视所有警告 使用静态分析工具分析代码 安全的读写数据 检查所有数组边界 检查指针的合法性 检查函数入口参数合法性 检查所有返回值 在声明变量位置初始化所有变量 合理的使用括号 谨慎的进行强制转换 使用好的诊断信息日志和工具 5 判错 编写或移植一个类似 C 标准库中的 printf 函数 可以格式化打印字符 字符串 十进 制整数 十六进制整数 这里称为 UARTprintf unsigned int WriteData unsigned int addr if addr BASE ADDR 错误处理代码 假设 UARTprintf 函数位于 main c 模块的第 256 行 并且 WriteData 函数在读数据 时传递了错误地址 0 x00000011 则会执行 UARTprintf 函数 打印如下所示的信息 文件 main c 的第 256 行写数据时发生地址错误 错误地址为 0 x00000011 类似这样的信息会有助于程序员定位分析错误产生的根源 更快的消除 Bug 5 1 具有形参的函数 需判断传递来的实参是否合法 程序员可能无意识的传递了错误参数 外界的强干扰可能将传递的参数修改掉 或者 使用随机参数意外的调用函数 因此在执行函数主体前 需要先确定实参是否合法 int exam fun unsigned char str if str NULL 检查 假设指针不为空 这个条件 正常处理代码 else UARTprintf 打印错误信息 处理错误代码 5 2 仔细检查函数的返回值 char DoSomething char p p malloc 1024 if p NULL 对函数返回值作出判断 UARTprintf 打印错误信息 return NULL retuen p 5 3 防止指针越界 如果动态计算一个地址时 要保证被计算的地址是合理的并指向某个有意义的地方 特别对于指向一个结构或数组的内部的指针 当指针增加或者改变后仍然指向同一个结构 或数组 5 4 防止数组越界 C 不会对数组进行有效的检测 因此必须在应用中显式的检测数组越界问题 下面的 例子可用于中断接收通讯数据 define REC BUF LEN 100 unsigned char RecBuf REC BUF LEN 其它代码 void Uart IRQHandler void static RecCount 0 接收数据长度计数器 其它代码 if RecCount REC BUF LEN RecBuf RecCount 从硬件取数据 RecCount 其它代码 else UARTprintf 打印错误信息 其它错误处理代码 在使用一些库函数时 同样需要对边界进行检查 define REC BUF LEN 100 unsigned char RecBuf REC BUF LEN if len REC BUF LEN memset RecBuf 0 len 将数组 RecBuf 清零 else 处理错误 5 5 数学运算 检测除数是否为零 检测运算溢出情况 有符号整数除法的运算检查 两个整数相除 除了检测除数是否为零外 还要检测除法是否溢出 对于一个 signed long 类型变量 它能表示的数值范围为 2147483648 2147483647 如果让 2147483648 1 那么结果应该是 2147483648 但是这个结果已经超出了 signed long 所能表示的范围了 include signed long sl1 sl2 result 初始化 sl1 和 sl2 if sl2 0 sl1 LONG MIN 加法溢出检测 a 无符号加法 include unsigned int a b result 初始化 a b if UINT MAX a b 处理溢出 else result a b b 有符号加法 include signed int a b result 初始化 a b if a 0 乘法溢出检测 a 无符号乘法 include unsigned int a b result 初始化 a b if a 0 b 有符号乘法 include signed int a b tmp result 初始化 a b tmp a b if a 0 检测移位时丢失有效位 5 6 其它可能出现运行时错误的地方 C 语言在提供任何运行时检测方面能力较弱 要求可靠性较高的软件来说 动态检测 是必需的 因此 C 程序员需要谨慎考虑的问题是 在任何可能出现运行时错误的地方增加 代码的动态检测 大多数的动态检测与应用紧密相关 在程序设计过程中要根据系统需求 设置动态代码检测 6 容错 6 1 关键数据多区备份 取数据采用 表决法 RAM 中的数据在受到干扰情况下有可能被改变 对于系统关键数据必须进行保护 关 键数据包括全局变量 静态变量以及需要保护的数据区域 数据备份与原数据不应该处于相邻位置 因此不应由编译器默认分配备份数据位置 而应该由程序员指定区域存储 可以将 RAM 分为 3 个区域 第一个区域保存原码 第二个区域保存反码 第三个区域 保存异或码 区域之间预留一定量的 空白 RAM 作为隔离 可以使用编译器的 分散加 载 机制将变量分别存储在这些区域 需要进行读取时 同时读出 3 份数据并进行表决 取至少有两个相同的那个值 6 2 非易失性存储器的数据存储 非易失性存储器包括但不限于 Flash EEPROM 铁电 仅仅将写入非易失性存储器中 的数据再读出校验是不够的 强干扰情况下可能导致非易失性存储器内的数据错误 在写 非易失性存储器的期间系统掉电将导致数据丢失 因干扰导致程序跑飞到写非易失性存储 器函数中 将导致数据存储紊乱 一种可靠的办法是将非易失性存储器分成多个区 每个数据都将按照不同的形式写入 到这些分区中 需要进行读取时 同时读出多份数据并进行表决 取相同数目较多的那个 值 对于因干扰导致程序跑飞到写非易失性存储器函数 还应该配合软件锁以及严格的入 口检验 单单依靠写数据到多个区是不够的也是不明智的 应该在源头进行阻截 6 3 软件锁 软件锁可以实现但不局限于环环相扣 对于初始化序列或者有一定先后顺序的函数调 用 为了保证调用顺序或者确保每个函数都被调用 我们可以使用环环相扣 实质上这也 是一种软件锁 此外对于一些安全关键代码语句 是语句 而不是函数 可以给它们设置软件锁 只有持有特定钥匙的 才可以访问这些关键代码 比如 向 Flash 写一个数据 我们会判断数据是否合法 写入的地址是否合法 计算 要写入的扇区 之后调用写 Flash 子程序 在这个子程序中 判断扇区地址是否合法 数 据长度是否合法 之后就要将数据写入 Flash 由于写 Flash 语句是安全关键代码 所以 程序给这些语句上锁 必须具有正确的钥匙才可以写 Flash 这样即使是程序跑飞到写 Flash 子程序 也能大大降低误写的风险 6 4 通信数据的检错 通讯线上的数据误码相对严重 通讯线越长 所处的环境越恶劣 误码会越严重 通 讯数据除了传统的硬件奇偶校验外 还应该增加软件 CRC 校验 超过 16 字节的数据应至少 使用 CRC16 在通讯过程中 如果检测到发生了数据错误 则要求重新发送当前帧数据 6 5 开关量输入的检测 确认 开关量容易受到尖脉冲干扰 如果不进行滤除 可能会造成误动作 一般情况下 需 要对开关量输入信号进行多次采样 并进行逻辑判断直到确认信号无误为止 多次采样之 间需要有一定时间间隔 具体跟开关量的最大切换频率有关 一般不小于 1ms 6 6 开关量输出 开关信号简单的一次输出是不安全的 干扰信号可能会翻转开关量输出的状态 采取 重复刷新输出可以有效防止电平的翻转 6 7 初始化信息的保存与恢复 微处理器的寄存器值也可能会因外界干扰而改变 外设初始化值需要在寄存器中长期 保存 最容易被破坏 由于 Flash 中的数据相对不易被破坏 可以将初始化信息预先写入 Flash 待程序空闲时比较与初始化相关的寄存器值是否被更改 如果发现非法更改则使用 Flash 中的值进行恢复 6 8 陷阱 对于 8051 内核单片机 由于没有相应的硬件支持 可以用纯软件设置软件陷阱 用来 拦截一些程序跑飞 对于 ARM7 或者 Cortex M 系列单片机 硬件已经内建了多种异常 软 件需要根据硬件异常来编写陷阱程序 用来快速定位甚至恢复错误 6 9 while 循环 有时候程序员会使用 while flag 语句来等待标志 flag 改变 比如串口发送时用来 等待一字节数据发送完成 这样的代码时存在风险的 如果因为某些原因标志位一直不改 变则会造成系统死机 良好冗余的程序是设置一个超时定时器 超过一定时间后 强制程 序退出 while 循环 6 10 系统自检 对 CPU RAM Flash 外部掉电保存存储器以及其他线路自检
展开阅读全文
相关资源
相关搜索

当前位置:首页 > 办公文档 > 解决方案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!