简介
开始之前自我介绍一下,我在大学学的是物联网工程专业,可惜的是发现嵌入式并不好找工作。于是后面自学了前端,并到美团从事了1年相关的开发工作,但是发现嵌入式才是真爱,于是又转到嵌入式开发。
前端作为目前发展最快的互联网方向,其中非常多的思想值得发扬到嵌入式上,我将其中的精华部分总结出来,欢迎大家指正。
嵌入式系统作为最基础的系统,通常一个系统的开发会涉及到数个、数十个外设的使用。然而使用这些外设充满了不确定性:
- 外设开启关闭复杂
- 外设之间协同
- 第三方IC外设没有驱动
- 多线程下对外设的访问
在此基础上,还需要开发对应的业务逻辑。值得庆幸的是,嵌入式的业务逻辑相对简单。
因此,为了使得一个嵌入式系统的架构合理,耦合性低,将上述的外设和业务逻辑区分开是有必要的。
设计目的:
- 增加可维护性
- 降低代码耦合性
- 提升错误的可追查性
思路:
将整个系统分为3层: 内核层、驱动层、业务层。每层分为任意个模块。
内核层:只包含系统的核心,startup文件、HAL库、RTOS(可选)
驱动层:利用RTOS对HAL进行再次封装,对外提供函数,以符合业务层的需求
业务层:调用驱动层和内核层,完成业务逻辑
设计原则:
层与层内部: 每个模块之间不能相互调用。
层与层之间: 不能反向调用,例如驱动层调用业务层,但可以跨层调用,例如业务层调用内核层。
设计技巧:
内核层:无。
驱动层:
- 使用static将变量、函数都局域化,只对外暴露一个struct。
- 统一的命名风格,比如modbus_drv,usb_drv或者其他任何符合你自己的命名习惯
- 全面的生命周期控制,对外提供包含但不限于init、start、stop、deinit之类的接口
- 线程安全的考虑
- 回调函数的设计,通常某些业务逻辑需要业务层提供,举个栗子:现在需要读取usb的5个字节数据,但是受限制于底层usb库,读取只能是一个异步的过程,这个时候就需要提供一个register注册回调函数。
- 信号量、事件等通知标志位的隐藏。对于某些简单的事件,例如上述的读取usb的5个字节数据,业务层只需要知道读取完成,而不需要拿到数据;这个时候,在业务层维护一个信号量,然后再写一个回调修改这个信号量,显然很麻烦。这个时候可以把信号量维护在驱动层,对外提供一个check_is_read_done函数。
业务层:
- 在业务层使用看门狗对业务进行监控,可以单独写一个看门狗线程,监测业务层是否按预期时间运行。
- 如果驱动层没有做线程安全的考虑,则需要业务层自己做,这个时候可以把需要做线程安全的代码单独抽在一个Utils里面,这样的话可以避免锁在各个文件之间传递。