单纯经验总结,找个地方记录
一、小型硬件系统固件该怎么写?
在没有嵌入式操作系统的时候,对于任务稍微多一点的硬件系统,比如这样一个小系统
任务包括:
1、模拟信号采集(使用MCU的ADC,或者专用的信号前端使用SPI/I2C通信)
2、数字信号处理(有各种各样的算法)
3、数据保存(SPI的Nandflash)
4、交互(蓝牙、USB)
5、调试打印(UART/RTT)
对于我来说就是中断+状态机
- 中断:很简单,任务多了肯定不能全在主循环做完,特别是硬件器件(如SPI, I2C, UART)操作时,不用中断几乎无法实现功能。
所以像上面的小系统,有这样的优先级
任务 | 优先级 |
---|---|
模拟信号采集 | 1(最高) |
交互的中断级(比如蓝牙的Link layer) | 2 |
数据保存的中断级(SPI传输) | 3 |
调试打印 | 4 |
算法、交互的实现、数据保存的实现 | 5(主循环) |
- 状态机:是很常用的方法,其实也很简单,就是给每个任务设一个状态,比如
状态 | 操作 |
---|---|
蓝牙开始连接 | 初始化什么的 |
蓝牙保持连接 | 数据传输 |
蓝牙断开 | 其他一些造作 |
… | … |
不管是硬件还是通信协议软件,都有自己的状态,针对不同状态做不同的事
中断+状态机 我们可以没有任何延时函数,遇到延时就改变状态,等到下次循环到了再处理这种状态来实现延时,其他时间就让系统进入休眠。这样的模式对于低功耗系统,特别是穿戴领域非常重要,想想又提高电池续航了
二、状态机越来越复杂?
利用状态机确实能够保证不用暴力延时了(比如循环计数100000次代表1ms延时),但是有没有想过,有些情况状态会越来越复杂。
以flash驱动为例:
- 第一次驱动实现
对于flash至少需要读和写操作,同时由于flash的操作比较慢,需要要延时,那么这里就有了5种状态
状态 | 说明 |
---|---|
空闲 | 没有任何读写操作时 |
开始读取 | 发送了参数,等待读取完成 |
读取完成 | |
开始写入 | 发送了参数,等待写入完成 |
写入完成 |
- 第二次驱动实现:
由于SPI通信也是需要时间的,硬件SPI通信直接用DMA实现,释放了CPU做其他事情,也降低了能耗。但是这样以来就多了另外两种状态:
状态 |
---|
SPI发送完成 |
SPI接受完成 |
- 第三次驱动实现:
由于nandflash器件的特性,需要按叶读写擦除,在读写的时候nandflash有内部缓存,比如读取时先要将数据按页读到缓存中,在通过SPI读到MCU中。写也一样,需要先通过SPI写到缓存中,然后从缓存写到flash上。这些操作也是很高高延时,为实现数据可靠,我们仅仅加长状态延时会造成读写效率低下,为提高读写效率自然引入新的状态,也就是flash自己状态寄存器中的状态
状态 |
---|
flash读取到缓存完成 |
缓存写入到flash完成 |
flash块擦除完成 |
-
第四次驱动实现:
要知道一个可靠的驱动,不但要保证能够得到需要的,而且要处理各种异常,让驱动程序快速恢复。现在我们来看,每种状态都可能会有异常,比如异常 flash读取到缓存失败 缓存写入到flash失败 SPI发送失败 SPI接收失败 擦除失败 这里又涉及到了各种异常处理,还是nandflash重要的坏块管理
看起来整个驱动越来复杂,后面应用部分操作还会造成其他异常,这对于驱动维护非常伤脑筋了。
在这个时候想要是不用状态机,一件事情按顺序完成不就好了,简单明了好维护。但是想想这样就又回到了利用延时来做,单一任务倒是没问题,其他任务怎么办。正好嵌入式操作系统可以解决呀。
- 嵌入式操作系统,对于我们驱动来说那就是单一任务,对于其他任务也不会有影响。因为嵌入式操作系统的延时函数不是单纯的暴力延时,而是做任务切换。