USCOSII
1.ucos是如何分层的?
分为四层:
- 硬件相关层
该层中,要尽量所有硬件相关都囊括在其中。不管是GPIO还是定时器,或串行接口。这其中有三个最为重要的接口Open、Close、Ctrl。
Open主要来完成对应硬件初始化,形参中包括了一些初始化的相关参数。
Close失能硬件。
Ctrl来实现一些控制的修改如优先级、中断回调函数等等,硬件不同,内容不同。 - 驱动接口层
该层会用到一个或多个硬件层的接口,进行组合来实现特定功能的程序。
以Flash为列进行说明:
这里主要调用硬件层的SPI函数接口,但是主要的写、读指令都是在以下函数中完成的。该层中需要提供5个标准统一的接口函数:XXXOpen、XXXClose、XXXWrite、XXXRead、XXXIoCtl。
没有被用到的函数,可以为空。本来还需要Install函数来进行动态加载和删除,因为stm32内存一般都很有限,所以舍弃动态分配。而把这5个函数用常量的形式直接编译到ROM中。在驱动的抽象接口层中可以做选择,哪些驱动要加载到内核,哪些不需要。不要的驱动不参与编译。这样有限的资源就可以得到合理的应用。该层大部分工作可以说属于一次性投入。 - 应用接口层
该层主要连接驱动和应用。又是连接应用层模块与模块之间的一层,这一块有很强的特殊性,第一包括了驱动抽象接口层,第二包括了模块与模块的接口层。第三又与应用层密不可分。
首先是驱动抽象接口,这个接口其实就是通过ID去访问ID对应的五个函数。抽象接口也是一次性投入的函数,在设计时对其可靠性要很重视。
然后是模块与模块的接口层,包括模块的接口头文件,这些头文件要求是非常独立的,不能加载模块内的内部头文件,应该包括接口函数的函数声明,在接口中尽量少用到全局变量。如果非要用到可以使用函数的方式进行传递,或ucos消息队列方式。最好用ucos进行传递,因为有很好的互斥保护功能。 - 应用层
该层中所有模块都算是应用层。模块内所有变量,或函数(接口除处)应该都本地化。在模块内可以有本模块化共用的主头文件,来方便本模块的维护。对硬件的访问其实直接调用应用接口就可完成。
2.HAL都有哪些代码?
stm32f2xx.h主要包含STM32同系列芯片的不同具体型号的定义,是否使用HAL库等的定义,接着,其会根据定义的芯片信号包含具体的芯片型号的头文件
`#if defined(STM32F205xx)
include "stm32f205xx.h"
elif defined(STM32F215xx)
include "stm32f215xx.h"
elif defined(STM32F207xx)
include "stm32f207xx.h"
elif defined(STM32F217xx)
include "stm32f217xx.h"
else
error "Please select first the target STM32F2xx device used in your application (in stm32f2xx.h file)"
endif`
紧接着,其会包含stm32f2xx_hal.h。
stm32f2xx_hal.h:stm32f2xx_hal.c/h 主要实现HAL库的初始化、系统滴答相关函数、及CPU的调试模式配置
stm32f2xx_hal_conf.h
:该文件是一个用户级别的配置文件,用来实现对HAL库的裁剪,其位于用户文件目录,不要放在库目录中。
接下来对于HAL库的源码文件进行一下说明,HAL库文件名均以stm32f2xx_hal开头,后面加上_外设或者模块名(如:stm32f2xx_hal_adc.c):
-
库文件:
stm32f2xx_hal_ppp.c/.h // 主要的外设或者模块的驱动源文件,包含了该外设的通用API
stm32f2xx_hal_ppp_ex.c/.h // 外围设备或模块驱动程序的扩展文件。这组文件中包含特定型号或者系列的芯片的特殊API。以及如果该特定的芯片内部有不同的实现方式,则该文件中的特殊API将覆盖_ppp中的通用API。
stm32f2xx_hal.c/.h // 此文件用于HAL初始化,并且包含DBGMCU、重映射和基于systick的时间延迟等相关的API
-
其他库文件
用户级别文件:
stm32f2xx_hal_msp_template.c
// 只有.c没有.h。它包含用户应用程序中使用的外设的MSP初始化和反初始化(主程序和回调函数)。使用者复制到自己目录下使用模板。
stm32f2xx_hal_conf_template.h // 用户级别的库配置文件模板。使用者复制到自己目录下使用
system_stm32f2xx.c// 此文件主要包含SystemInit()函数,该函数在刚复位及跳到main之前的启动过程中被调用。 **它不在启动时配置系统时钟(与标准库相反)**。 时钟的配置在用户文件中使用HAL API来完成。
startup_stm32f2xx.s // 芯片启动文件,主要包含堆栈定义,终端向量表等
stm32f2xx_it.c/.h // 中断处理函数的相关实现
main.c/.h //
3.分析任务是如何切换的。
ucos的任务切换方式一般有两种:
- 时钟节拍中断服务函数OSTickISR()进行切换。
- 任务中调用时间延迟函数OSTimeDly()进行切换。
- 延迟函数OSTimeDly()进行切换:
`y = OSTCBCur->OSTCBY;
OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;//清除准备优先级中的X标记量
if (OSRdyTbl[y] == 0) {
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;//如果对应的Y优先级中没有X的优先级标记了,则把Y优先级也清除
}
OSTCBCur->OSTCBDly = ticks;//把对应的延时时间赋值给任务控制块,在系统滴答中断中会自动减
- 延迟函数OSTimeDly()进行切换:
OS_SchedNew();//从准备好的任务中找到优先级最高的,赋值给OSPrioHighRdy,然后通过OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];从任务的链表中找出对应的任务控制块, OS_TASK_SW();实际切换任务的函数,一般由汇编代码完成。`
- 中断服务函数进行切换:
OSTimeTick()函数的主要内容,if (ptcb->OSTCBDly != 0) 和 if (--ptcb->OSTCBDly == 0) 判断该任务块中的设定的延时是否到了,OSRdyGrp |= ptcb->OSTCBBitY; OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;延时时间到后将该任务的优先级重新加入到优先级准备变量中。
OSIntExit()该函数和上面的内容差不多,是中断函数中真正切换任务的地方