很早以前就开始学习移植USOS ii了,移植倒没什么难的,主要是要清楚自己的单片机,最好是将启动代码分析一遍,那么在移植的时候,很多概念就不会陌生了。我用的是
stm32f103ze.
移植完了之后,就不知道干嘛了,于是中间搁置了很久,这几天又想到这个系统,于是又重新移植了一遍,把代码结构又明晰了一点,这次我就想,要把这个操作系统弄清楚。
那么切入正题吧,我是先从事件标志组开始分析的。首先看几个数据结构和数据类型:
typedef struct os_flag_grp { /* Event Flag Group */
INT8U OSFlagType; /* Should be set to OS_EVENT_TYPE_FLAG */
void *OSFlagWaitList; /* Pointer to first NODE of task waiting on event flag */
OS_FLAGS OSFlagFlags; /* 8, 16 or 32 bit flags */
#if OS_FLAG_NAME_EN > 0u
INT8U *OSFlagName;
#endif
} OS_FLAG_GRP;
在使用时间标志组之前,都要先声明这样一个变量,然后使用OSFlagCreate函数来初始化这个变量。
初始化这个变量之后,比如说任务一TASK1要等待这个事件标志组的某几位,就要使用OSFlagPend函数来进行这个操作,这个函数首先比较你要等待的标志位和事件标志组的标志位是否吻合,吻合就直接返回成功,不吻合,就调用OS_FlagBlock函数,在这个函数里:
主要目的是将等待事件标志组这个状态更新到当前任务的任务控制块里,然后设定好等待时间,如果设置成0,则表示无限等待。此时又用到了另外一个数据类型:
typedef struct os_flag_node { /* Event Flag Wait List Node */
void *OSFlagNodeNext; /* Pointer to next NODE in wait list */
void *OSFlagNodePrev; /* Pointer to previous NODE in wait list */
void *OSFlagNodeTCB; /* Pointer to TCB of waiting task */
void *OSFlagNodeFlagGrp; /* Pointer to Event Flag Group */
OS_FLAGS OSFlagNodeFlags; /* Event flag to wait on */
INT8U OSFlagNodeWaitType; /* Type of wait: */
/* OS_FLAG_WAIT_AND */
/* OS_FLAG_WAIT_ALL */
/* OS_FLAG_WAIT_OR */
/* OS_FLAG_WAIT_ANY */
} OS_FLAG_NODE;
在OS_FlagBlock函数里,使用你调用OSFlagPend函数时的参数对OS_FLAG_NODE变量进行填充,然后将这个node连接到OS_FLAG_GRP的OSFlagWaitList上,很多任务都可以同时对同一个OS_FLAG_GRP使用OSFlagPend函数,那么,也就是都有可能同时使用OS_FlagBlock函数,这样,就会创建很多个相对于各自任务的OS_FLAG_NODE变量,这些OS_FLAG_NODE变量连接成双链表,使用OS_FLAG_GRP的OSFlagWaitList指针来访问这个双链表。处理完OS_FLAG_NODE变量的填充和连接之后OS_FlagBlock函数再将当前任务的就绪状态取消。然后返回到OSFlagPend函数,OSFlagPend函数随即调用任务调度函数,将自己阻塞起来。
此时,能使这个任务恢复运行的办法有两个,一个就是时钟滴答里边会检测每个任务的timeout,当timeout等于1时,就会检测任务的状态位,如果状态不是就绪,则清空等待标志,将pend状态设置为pend过期,如果是就绪,则设置成pend成功,随即在任务就绪组和任务就绪表里恢复这个任务的就绪状态。二是通过OSFlagPost函数,只需传递给该函数相应的OS_FLAG_GRP变量和要设置的位掩码,即可完成相应的设置,并且据此更改各个对该OS_FLAG_GRP变量使用过OSFlagPend函数的任务的态。OSFlagsPost函数先根据要求更新事件标志,然后通过该OS_FLAG_GRP变量取得等待该标志组的任务链表的指针,接着遍历该链表,检查每个节点要求的标志位是否得到满足,如果满足,则调用OS_FlagTaskRdy函数,在该函数里,将当前TCB等待flag的状态清除掉,如果此TCB没有其他的原因阻塞,则进入到就绪状态,然后该函数将这个TCB对应的任务就绪组合任务就绪表更新在OS_FLAG_NODE链表中将该节点删除,然后返回到OSFlagsPost函数中,调用任务调度函数,完成此次标志位的发送