热度 15||
跑马灯的第四次变形:修改模块间的依赖关系
是的,只有把前面的三个诘问解决了,我们才能得到一个相对完美的跑马灯。最终,我们要的是一个干净清爽的关系依赖图,如下。
要实现这样的依赖关系,就需要明白上一次变形中存在的几个“思维误区”。
1) LIGHT_INTERVAL_TIME,这个参数调整跑马灯的间隔时间,最终影响跑马灯的循环闪烁速度,可快可慢。它本质上是属于用户的,如同街上的广告轮廓灯,它的样式和闪烁方式,是由订购者决定的。广告灯的供应商只需给出使用方法即可。
因此,LIGHT_INTERVAL_TIME这个参数是不属于设备层的,它归使用者,归main.c。设备层必须得给出一个接口实现方式,让使用者得以传递LIGHT_INTERVAL_TIME的数值,使“用户”可以灵活地控制跑马灯的运行速度。如何实现呢?这里介绍一种较为符合当前应用的方法。为此,我们需要将timer模块改装如下。
2)
led_turn.h中的端口定义“#define LED_PORT P1”。许多单片机的工程师喜欢将平台相关的东西定义在.h档里面,其本意是为了移植方便,想着届时只需要修改.h档即可。但这样的话,led_turn.h模块就得依赖
不要轻视这样的小细节,当系统较为庞大时,这样操作很容易就会造成二次、三次、多次间接依赖,像病毒一样“污染扩散”,牵一发就会动全身,最终使整个系统陷入永沦之地。正所谓,勿以恶小而为之,勿以善小而不为。
3)
上次变形中,我们把timer模块中的全局变量flag_80ms,通过timer.h,开发给main.c读写。在当前这个小小跑马灯系统中,也许问题不大。但对于构建商业软件系统,这是一个极为危险的做法。全局变量本身就是一个需要极力避免的东西,更何况还把写权限开放给外界,这如同将家中大门拆除一般令人心悸。关于全局变量的危害性及处理方法,详见本书章节《全局变量猛于虎》。此处我们用函数get_and_clr_timer_evt()进行了规避,详见上述代码。
多年来,我总结了一个经验,.h档里面,最好都是纯洁无瑕的函数声明。我一直尽力做到这一点,本书的后续章节,会陆续提到这一原则的带来的种种妙处,及其各种实现技巧。
综上,我们得到这样的一个依赖清晰的main.c文件,如下:
这样,我们的跑马灯完成了第四次变身,成为了一个具备良好移植性、可阅读性的商业系统。如果这个跑马灯要移植到M3/M0或者AVR等平台上,只需要修改“设备层”的led_trun.c和timer.c文件即可。而应用层的三个文件无需任何修改,因为它们没有依赖任何平台相关的文件。
sedatefire: 我自己重新看了一遍,发现这样的行文方式,对于没有耐心的读者,很难“入戏”。
太长了,还是要再增加图表来降低阅读难度。
在这样的快餐时代,作者要去迎合读者 ...
wlc824136: 看完大大的文章,也想学学将程序分层,我想问一下,我用定时器来做延时,需要多个时间标志位,我现在想的是将每个时间标志return出来,然后再其他函数要用到时读 ...