1 b: h6 z# Z% ~6 e% D6 g, I$ ]尽管许多嵌入式工程师充满了希望和梦想,但高可靠性的代码不是一蹴而就的。它是一个艰苦的过程,需要开发人员维护和管理系统的每个比特和字节。当一个应用程序被确认为“成功”的那一刻,通常会有一种如释重负的感觉,但仅仅因为软件在受控条件下的那一刻运行正常并不意味着明天或一年后还会运行正常。0 V( s$ l. |8 r [4 C. a
* R+ S0 ^8 @8 e 1 ]& r3 n# Z( H
! q! c2 w0 H# e; G" c1 }
绝对确保堆栈正常工作的一种方法是实现堆栈监视器,将它作为系统“保健”代码的一部分(有多少工程师会这样做?)。堆栈监视器会在堆栈和“其它”内存区域之间创建一个缓冲区域,并填充已知的位模式。然后监视器会不断的监视图案是否有任何变化。如果该位模式发生了改变,那就意味着堆栈增长得太大了,即将要把系统推向黑暗地狱!此时监视器可以记录事件的发生、系统状态以及任何其它有用的数据,供日后用于问题的诊断。 ' b: x2 u7 {4 I, ?' ~- n. B k' v# t4 b# y3 I4 {
1 e& a4 |* x9 F
5 M- j0 j1 R4 O* b' y9 ?5 y: L4 I大多数实时操作系统(RTOS)或实现了内存保护单元(MPU)的微控制器系统中都提供有堆栈监视器。可怕的是,这些功能默认都是关闭状态,或者经常被开发人员有意关闭。在网络上快速搜寻一下可以发现,很多人建议关闭实时操作系统中的堆栈监视器以节省56字节的闪存空间。等等,这可是得不偿失的做法! 6 Y& a. E! a( I/ i4 N! W2 T8 W% n4 v$ r
/ m5 A$ A4 |$ @# D * {% [: ^! w" J, y. I) r# h. \' W7 J技巧#5 - 使用MPU5 ]/ l& _0 Y& u, R0 Z D4 y3 `
1 \ U. |4 N4 i% A7 o, C
在过去,是很难在一个小而廉价的微控制器中找到内存保护单元(MPU)的,但这种情况已经开始改变。现在从高端到低端的微控制器都已经有MPU,而这些MPU为嵌入式软件开发人员提供了一个可以大幅提高其固件(firmware)鲁棒性(robustness)的机会。 & x9 U+ T8 D/ c9 D, ?9 i" s8 ?" C, c* M% Z( Z( v' H% \; [
; @* N( S3 {" M+ n7 S3 X5 r9 N
4 G; X) w/ { [& m2 W& n+ H
MPU 已逐渐与操作系统耦合,以便建立内存空间,其中的处理都分开,或任务可执行其代码,而不用担心被stomped on。倘若真有事情发生,不受控制的处理会被取消,也会执行其他的保护措施。请留意带有这种组件的微控制器,如果有,请多加利用它的这种特性。 # B' [' P5 H+ A: D& G) n6 Z i4 Z# N4 T; p
9 i/ E8 g. G$ x5 d% _9 v( E( [技巧#6 - 建立一个强大的看门狗系统 ; ?. K. v! K7 G/ ?) h; K 2 s7 o* Y/ u, m6 e+ X你经常会发现的一种总是最受喜爱的看门狗(watchdog)实现是,在看门狗被启用之处(这是一个很好的开始),但也是可以用周期性定时器将该看门狗清零之处;定时器的启用是完全与程序中出现的任何情况隔离的。使用看门狗的目的是协助确保如果出现错误,看门狗不会被清零,即当工作暂停,系统会被迫去执行硬件重设定(hardware reset),以便恢复。使用与系统活动独立的定时器可以让看门狗保持清零,即使系统已失效。 / _( a! x3 w# T5 S: o对应用任务如何整合到看门狗系统中,嵌入式开发人员需要仔细考虑和设计。例如,有种技术可能可以让每个在一定时期内运行的任务标示它们可以成功地完成其任务。在此事件中,看门狗不被清零,强制被复位。还有一些比较先进的技术,像是使用外部看门狗处理器,它可用来监视主处理器如何表现,反之亦然。7 t/ l5 o7 b: r [
4 a/ {9 y+ q4 R6 |: `3 ` 0 u" f# q2 @8 B7 k : ]% r! ^5 z0 J对一个可靠的系统而言,建立一个强大的看门狗系统是很重要的。由于有太多的技术,难以在这几个段落中完全涵盖,但针对此一议题,笔者未来还会发表相关的文章。 " c) @( `/ g* h4 P/ G7 n0 a3 H; B
7 P6 l; A% i' c; v/ A' N
% o* `0 ^0 ]2 Q0 i2 a* S. x
技巧#7 - 避免易失存储器分配 # [1 k6 |' `: N9 O) P6 |4 w6 j ; O7 n. [. [5 h, _不习惯在资源有限环境下工作的工程师,可能会试图使用其编程语言的特性,这种语言让他们可以使用易失存储器分配。毕竟,这是一种常在计算器系统中使用的技术,在计算器系统中,只有在有必要时,内存才会被分配。例如,以C开发时,工程师可能倾向于使用malloc来分配在堆(heap)上的空间。有一个操作会执行,一旦完成,可以使用free将被分配的内存返回,以便堆的使用。/ n3 w* _% D. g9 l% l* u5 r. W
- B, T9 I( @) t! h
* E! [ Q& W8 ]0 B0 a4 n. T在资源受限的系统,这可 能是一场灾难!使用易失存储器分配的其中一个问题是,错误或不当的技术可能会导致内存泄漏或内存碎片。如果出现这些问题时,大多数的嵌入式系统并没有资源或知识来监视堆或妥善地处理它。而当它们发生时,如果应用程序提出对空间的要求,但却没有所请求的空间可以使用,会发生什么事呢?3 N! M v. X. R+ X( O7 N
1 @ W1 }8 J. d2 o1 Y, ~$ b7 N6 h
/ c" U4 ~7 w& t3 E. y
3 C& _% w0 H- ~3 }4 ?使用易失存储器分配所产生的问题是很复杂的,要妥善处理这些问题,可以说是一个噩梦!一种替代的方法是,直接以静态的方式,简化内存的分配。例如,只要在程序中简单地建立一个大小为256字节长的缓冲区,而不是经由malloc请求这样大小的内存缓冲区。此一分配的内存可在整个应用程序的生命周期期 间保持,且不会有堆或内存碎片问题方面的顾虑。+ G2 O; {+ q( k$ ~
1 i! T3 S y# F+ |. v
" B5 }& D5 k5 C& I! r! M, p) ^) ]/ L( F
结论! p- {" T$ M6 T) Z