准备
1.C8T6 ucosii工程模板
链接: UCOSII移植到STM32F103C8T6上之移植记录(一).
2.参考书籍(如有侵权,留言即删)
链接: uCOSⅡ中文教程(邵贝贝). 提取码: 4q4j
链接: μCOS-II_2.92 API函数详解 v2.0. 提取码: nt52
1.在 OS_CFG.H 文件中设 OS_TASK_CREATE_EXT 为 1
2.用 OSTaskCreateExt()建立任务,并给予任务比实际需要更多的内存空间。(用OSTaskCreate确实不行,ERROR 69)
3.在 OSTaskCreateExt()中, 将参数 opt 设置为 OS_TASK_OPT_STK_CHK+OS_TASK_OPT_STK_
CLR。注意如果用户的程序启动代码清除了所有的 RAM,并且从未删除过已建立了的任务, 那么用户就不必设置选项OS_TASK_OPT_STK_CLR了。这样就会减少OSTaskCreateExt()的执行时间。
4. 将用户想检验的任务的优先级作为 OSTaskStkChk()的参数并调用之。
INT8U OSTaskDel (INT8U prio) //prio是要删任务的优先级
删除任务前应保证所删任务并非空闲任务。
任务可以传递自己的优先级给 OSTaskDel(),从而删除自身。
如果任务不知道自己的优先级,还可以传递参数 OS_PRIO_SELF。
被删除的任务将回到休眠状态。任务被删除后可以用函数 OSTaskCreate() 或 OSTaskCreateExt()重新建立。
有时,任务会占用一些内存缓冲或信号量一类的资源。这时,假如另一个任务试图删除该任务,这些被占用的资源就会因为没有被释放而丢失。在这种情况下,需想办法拥有这些资源的任务在使用完资源后先释放资源,再删除自己。
INT8U OSTaskChangePrio (INT8U oldprio, INT8U newprio)
oldprio : 是任务原先的优先级。
newprio : 是任务的新优先级。
INT8U OSTaskSuspend (INT8U prio)//prio是要删任务的优先级
任务挂起是一个附加功能,也就是说,如果任务在被挂起的同时也在等待延迟时间到,那么,需要对任务做取消挂起的操作,并且等待延迟时间到,任务才能转让就绪状态。任务可以挂起自己或者其他任务。
INT8U OSTaskResume(INT8U prio)//prio是要删任务的优先级
挂起的任务只有通过该函数才能被恢复。
通过调用该函数,来获得自身或其他应用任务的信息。
1. OSTimeDly()
任务调用OSTimeDly()后,一旦规定的时间期满或者有其他的任务通过调用OSTimeDlyResume()取消了延时,他就会进入就绪状态。只有当该任务在所有就绪态任务中具有最高的优先级,它才会立即运行。
2. OSRimeDLyHMSM() 按时,分,秒延时函数
调用OSRimeDlyHMSM()函数也会是UCOS进行一次任务调度,并且执行下一个优先级最高的就绪任务。当OSTimeDlyHMSM()后,一旦规定的时间期满,或者有OSTimeDlyResume(),它就会马上处于就绪态。同样,只有当该任务在所有就绪态任务中具有最高的优先级,他才开始运行。
延时的任务可以不等待延时的期满,而是通过其他任务取消延时而使自己处于就绪态,可以通过该函数来实现,实际上,OSTimeDlyResume()也可以唤醒正在等待的事件。
OSTimeGet()得到的值是一直变化的 可以用来做随便函数的随机种子等。
OSTimeSet()设置当前系统时钟数值。系统时钟是一个 32 位的计数器,记录系统上电后或时钟重新设置后
的时钟计数。OSTimeSet(0L); /* 复位系统时钟 */
开关量 :OS_MEM_EN 设置为 1
最大内存分区数:OS_MAX_MEM_PART
内存控制块的数据结构
Typedef struct
{
void *osmemaddr ;指向内存分区起始地址的指针。
Void *osmemfreelist ;指向下一个空余内存控制块或者下一个空余内存块的指针,
Int32u osmemblksize ;内存分区中内存块的大小,是建立内存分区时定义的。
Int32u osmemnblks ;内存分区中总的内存块数量,也是建立该内存分区时定义的。
Int32u osmemnfree ;内存分区块中当前获得的空余块数量。
}os_mem;
OS_MEM *OSMemCreate (void *addr,INT32U nblks,INT32U blksize,INT8U *perr);
addr : 建立的内存区的起始地址。内存区可以使用静态数组或在初始化时使用 malloc()函数建立。
nblks : 需要的内存块的数目。每一个内存区最少需要定义两个内存块。
blksize : 每个内存块的大小,最少应该能够容纳一个指针。
err : 是指向包含错误码的变量的指针。
返回指向内存区控制块的指针。如果没有空闲内存区,返回空指针。
INT32U CommBuf[16][32];
OSMemCreate(&CommBuf[0][0], 16, 32 * sizeof(INT32U), &err);
void *OSMemGet (OS_MEM *pmem,INT8U *perr);
pmem: 是指向内存区控制块的指针,可以从 OSMemCreate()函数返回得到。
OSMemCreate()函数返回指向内存区控制块的指针。如果没有空闲的内存区,返回空指针。
应用程序通过调用该函数,从已经建立的内存分区中申请一个内存块。该函数唯一的参数是指向特定内存分区的指针。
INT8U OSMemPut(OS_MEM *pmem,void *pblk);
pmem: 是指向内存区控制块的指针,可以从 OSMemCreate()函数 返回得到。
Pblk: 是指向将被释放的内存块的指针。
当应用程序不再使用一个内存块时,必须及时的把它释放,并放回到相应的内存分区中,这个操作就是通过调用该函数实现的。
INT8U OSMemQuery (OS_MEM *pmem,OS_MEM_DATA *p_mem_data);
pmem: 是指向内存区控制块的指针,可以从 OSMemCreate()函数 返回得到。
p_mem_data: 是指向 OS_MEM_DATA 数据结构的指针
OS_MEM_DATA mem_data;
OSMemQuery(CommMem, &mem_data); /*获取内存分区信息*/
MsgBox = OSMboxCreate((void *)0);
返回值是一个指向事件控制块的指针。 如果系统中已经没有事件控制块可用, 函数OSMboxCreate()将返回一个 NULL 指针。邮箱一旦建立,是不能被删除的。
1.必须先建立消息邮箱,然后使用。
2.不允许传递一个空指针,因为这意味着消息邮箱为空。
timeout:任务等待超时周期,为 0 时表示无限等待;其它,递减到 0 时任务恢复执行。
OSMboxPend()首先检查该事件控制块是由 OSMboxCreate()函数建立的。
当.OSEventPtr 域是一个非 NULL 的指针时,说明该邮箱中有可用的消息。这种情况下,OSMboxPend()函数将该域的值复制到局部变量 msg 中,然后将.OSEventPtr 置为 NULL。这正是我们所期望的,也是执行 OSMboxPend()函数最快的路径。
如果邮箱中没有可用的消息,OSMboxPend()的调用任务就被挂起,直到邮箱中有了消息或者等待超时。当有其它的任务向该邮箱发送了消息后(或者等待时间超时),这时,该任务再一次成为最高优先级任务,OSSched()返回。这时,OSMboxPend()函数要检查是否有消息被放到该任务的任务控制块中。如果有,那么该次函数调用成功,对应的消息被返回到调用函数。
简单来说就是直接读邮箱,有没有数据都读一次,读完清空邮箱。
得到邮箱中的当前内容,并判断是否有消息是可用的。如果邮箱中有消息,就把邮箱清空,而邮箱中原
来指向消息的指针被返回给 OSMboxAccept()的调用函数。中断服务子程序在试图得到一个消息时, 应该使用 OSMboxAccept()函数, 而不能使用 OSMboxPend()函数。
OSMboxAccept()函数的另一个用途是,用户可以用它来清空一个邮箱中现有的内容。
需要两个参数:
一个是指向邮箱的指针 pevent。该指针是在建立该邮箱时,由OSMboxCreate()函数返回的;
一个是指向用来保存有关邮箱的信息的 OS_MBOX_DATA(见uCOS_II.H) 数据结构的指针 pdata。
将邮箱中的等待任务列表和邮箱中的消息从 OS_EVENT 数据结构复制到OS_MBOX_DATA 数据结构。
简单来说:AB都要进行操作,为0代表空闲。A先置1,运行完置0,B查到是0,B再用。(大概是这个意思吧,类似于标志位)
在初始化时, 将邮箱设置为一个非零的指针 (如 void*1) 。 这样 一个任务可以调用OSMboxPend()函数来请求一个信号量, 然后通过调用OSMboxPost()函数来释放一个信号量。如果用户只需要二值信号量和邮箱,这样做可以节省代码空间。这时可以将 OS_SEM_EN 设置为 0,只使用邮箱就可以了。
OS_EVENT *MboxSem;
void Task1 (void *pdata)
{
INT8U err;
for (;;)
{
OSMboxPend(MboxSem, 0, &err); /* 获得对资源的访问权 */
. /* 任务获得信号量,对资源进行访问 */
OSMboxPost(MboxSem, (void*)1); /* 释放对资源的访问权 */
}
}
如果在指定的时间段 TIMEOUT 内, 没有消息到来, Task1()函数将继续执行。 这OSTimeDly(TIMEOUT)功能很相似。 如果 Task2()在指定的时间结束之前,向该邮箱发送了一个 “哑” 消息,Task1()就会提前开始继续执行。这和调用OSTimeDlyResume()函数的功能是一样的。
互斥型信号量(mutex)是二值信号量。由于uC/OS-II 不支持多任务处于同一优先级,可以把占有 mutex 的低优先级任务的优先级提高到略高于等待 mutex的高优先级任务的优先级。等到低优先级任务使用完共享资源后,调用 OSMutexPost() ,将低优先级任务的优先级恢复到原来的水平。
功能开关:OS_MUTEX_EN,OS_MUTEX_DEL_EN
1.在使用之前必须先创建一互斥信号量。
2.此功能不能被 ISR 调用。
3.通过 OSMutexAccept()成功请求互斥量后,当共享资源使用完成时必须调用 OSMutexPost()来释放互斥锁。
BOOLEAN OSMutexAccept (OS_EVENT *pevent,INT8U *perr);
pevent : 指向要请求的互斥信号量的指针。当建立互斥信号量时,该指针返回到用户程序。(参考OSMutexCreate()函数) 。
OS_EVENT *DispMutex;
OSMutexAccept(DispMutex, &err);
OS_EVENT *OSMutexCreate (INT8U prio,INT8U *perr);
prio : 较高的空闲优先级,用于任务提权。
返回值 :一个指向分配给互斥事件控制块。
注意:确保 prio具有比任何使用互斥访问资源的任务更高的优先级。
OS_EVENT *OSMutexDel (OS_EVENT *pevent,INT8U opt,INT8U *perr);
pevent : 指向要请求的互斥信号量的指针。当建立互斥信号量时,该指针返回到用户程序。 (参考OSMutexCreate()函数)
opt :选择指定要删除消息邮箱类型:
OS_DEL_NO_PEND:只有当不存在悬而未决的任务删除时才删除,否则不删除。
OS_DEL_ALWAYS: 直接删除。
注意:使用这个函数是很危险的, 因为多个任务可能是依赖于互斥信号量而运行的。所以在删除互斥信号量之前,必须先删除所有访问互斥信号量的任务。
void OSMutexPend (OS_EVENT *pevent,INT32U timeout,INT8U *perr);
pevent : 指向要请求的互斥信号量的指针。当建立互斥信号量时,该指针返回到用户程序。 (参考OSMutexCreate()函数)
用于任务试图取得设备的使用权,任务需要和其他任务或中断同步,任务需要等待特定事件的发生的场合。如果任务调用 OSMutexPend()函数时,如果互斥信号量被其他任务持有,OSMutexPend()函数挂起当前任务直到其他的任务或中断置起信号量或超出等待的预期时间。 并且, 该互斥信号号的任务的持有者会把自己的优先级提升到一个比较高的优先级(在创建互斥量时指定) ,来保证他不被其他任务中止,来确保在等待该信号的高优先级任务能尽快得到互斥信号。 如果在预期的时钟节拍内信号量被置起, uC/OS-Ⅱ默认最高优先级的任务取得信号量恢复执行。一个被 OSTaskSuspend()函数挂起的任务也可以接受互斥信号量,但这个任务将一直保持挂起状态直到通过调用 OSTaskResume()函数恢复任务的运行。
OS_EVENT *DispMutex;
OSMutexPend(DispMutex, 0, &err);/*申请互斥信号量*/
/*共享设备(资源)代码;*/
OSMutexPost(DispMutex);/*释放互斥信号量*/
INT8U OSMutexPost (OS_EVENT *pevent);
pevent : 指向一个互斥型信号量的指针。
INT8U OSMutexQuery (OS_EVENT *pevent,OS_MUTEX_DATA *p_mutex_data);
pevent : 指向一个互斥型信号量的指针。
一旦这部分代码开始执行, 则不允许任何中断打入。为确保临界段代码的执行,在进入临界段之前要关中断,而临界段代码执行完以后要立即开中断。从代码的角度上来看,处在关中断和开中断之间的代码段就是临界段。
OS_ENTER_CRITICAL() 进入临界区,禁止被中断打断。
//你要写的不允许打断的代码
OS_EXIT_CRITICAL() 退出临界区,允许被中断打断。
小知识(纯属个人理解,如果错误还望评论区留言更改):
OS_ENTER_CRITICAL()是关中断实现不被打断。
1.5.4 OSMutexPend () 是提高当前任务优先级实现不被打断。
OSTaskCreate()建立一个新任务,可以在多任务环境启动之前,或者运行任务中建立任务。
OSTaskCreateExt()为OSTaskCreate()的扩展函数,允许更多的内容设置。
INT8U OSTaskCreate (void (*task)(void *pd), /* 函数指针,void *pd为函数的参数 */
void *pdata, /* 建立任务时,传递的参数 */
OS_STK *ptos, /* 指向堆栈任务栈顶的指针 */
INT8U prio) /* 任务优先级 */
INT8U OSTaskCreateExt (void (*task)(void *pd), /* 同上 */
void *pdata, /* 同上 */
OS_STK *ptos, /* 同上 */
INT8U prio, /* 同上 */
INT16U id, /* 任务ID,2.52版本,无实际作用,保留作为扩展用 */
OS_STK *pbos, /* 指向堆栈底部的指针,用于OSTaskStkChk()函数 */
INT32U stk_size, /* 指定任务堆栈的大小,由OS_STK类型决定 */
void *pext, /* 定义数据结构的指针,作为TCB的扩展 */
INT16U opt) /* 存放于任务操作相关的信息,详见uCOS-II.H */
OS_EVENT *MsgBox;
MsgBox = OSMboxCreate((void *)0); // 创建邮箱并初始化为空
INT8U err;
INT32U *msg;
在其循环函数中接受邮箱信息:
msg = OSMboxPend(MsgBox, 0, &err); // 接收Task1发来的消息
Messgae = msg[0]; // 接收消息到指定的变量
INT8U err;
volatile INT32U MsgData = 1000;
也是在其循环函数中发送邮箱信息:
err = OSMboxPost(MsgBox, (void*)&MsgData); // 向TaskPWM发送消息
注意:在使用时一定要注意数据类型的定义,一定要统一起来。
将task2中的数据3s改变一次并发到消息邮箱,task1 1s打印一次邮箱数据并更新。OSMboxPend不会自动清数据。
OS_EVENT *MsgBox=NULL;
static void task1(void *p_arg)
{
uint8_t Messgae,err,*msg;
MsgBox = OSMboxCreate((void *)0);
for (;;)
{
msg = OSMboxPend(MsgBox, 1, &err);//将邮箱数据读出来
if(msg!=NULL)
Messgae = msg[0];
printf("\r\n*Messgae:%02x*\r\n",Messgae);
OSTimeDly(1000);
}
}
static void task2(void *p_arg)
{
uint8_t MsgData = 0x20;
for (;;)
{
OSMboxPost(MsgBox, (void*)&MsgData);//向消息邮箱丢数据MsgData
MsgData++;
if(MsgData>=0xF0)
MsgData=0x20;
printf("\r\n*+++++++++++++++*\r\n");
OSTimeDly(3000);
}
}
将task2中的数据3s改变一次并发到消息邮箱,task1 检测到有数据更新才打印(3s一次,OSMboxPend()配置为0一直等待,程序不往下运行)。
static void task1(void *p_arg)
{
MsgBox = OSMboxCreate((void *)0);
uint8_t Messgae,err,*msg;
for (;;)
{
msg = OSMboxPend(MsgBox, 0, &err);//区别就在0和1 ? 分析一下
Messgae = msg[0];
printf("\r\n*Messgae:%02x*\r\n",Messgae);
OSTimeDly(1000);
}
}
static void task2(void *p_arg)
{
uint8_t MsgData = 0x20;
for (;;)
{
OSMboxPost(MsgBox, (void*)&MsgData);//向消息邮箱丢数据MsgData
MsgData++;
if(MsgData>=0xF0)
MsgData=0x20;
printf("\r\n*+++++++++++++++*\r\n");
OSTimeDly(3000);
}
}}
OS_Task_CREATE_EN
作用:设定控制用户程序是否使用OSTaskCreate()函数。
说明:在配置时OS_TASK_CREATE_EN()和OS_TASK_CREATE_EXT_EN()至少有一个要为1。
OS_TASK_CREATE_EXT_EN 关
作用:设置程序中是否使用OSTaskCreateExt().
OS_TASK_DEL_EN 关
作用:设定程序中是否使用删除任务函数OSTaskDel()
OS_TASK_SUSPEND_EN 关
作用:设定程序中使用任务挂起和唤醒函数OSTaskSupend()和OSTaskResume().
OS_TASK_STAT_EN 关
作用:设置系统是否使用UC/OS中的统计任务OSTaskStat()及其初始化函数。
说明:设为1,则使用统计任务。统计任务每秒运行一次,计算当前系统CPU使用频率并把结果保存在8位全局变量OSCPUUsage中。每次运行,OSTaskStat()都将调用函数OSTaskStatHook(),用户自定义的统计功能可以放在这个函数中。若设为0时,UC/OS在系统初始化时,全局变量OSCPUUsage,OSIdleCtrMax,OSIdleCtrRun和OSStatRdy都将不被声明,以节省内存空间
OS_TASK_CHANGE_PRIO_EN关
作用:设定程序中是否使用UC/OS的改变任务优先级函数OSTaskChangePrio().
不确定对OSMutexPend(),OSMutexPost()函数是否有影响,有知道的希望留言说一下
OS_TASK_QUERY_EN关
作用:设定程序中是否需要使用获取任务信息函数OSTaskQuery。
OS_SCHED_LOCK_EN
作用:设定应用程序中是否使用关调度锁函数OSSchedLock()和开调度锁函数OSSchedUnlock()。
注:若配置为0,则所有有关标志组的函数均不能使用。即使对应的常量配置为1。
OS_SEM_EN(不影响内存大小)
作用:设定程序中是否使用信号量管理函数和其相关数据结构。
OS_MUTEX_EN(不影响内存大小)
作用:设定程序中是否使用互斥信号量
OS_FLAG_EN 关
作用:设定程序中是否使用事件标志组
OS_MBOX_EN
作用:设置程序是否使用消息邮箱函数及其相关数据结构。
OS_Q_EN 关
作用:设定程序中是否使用消息队列函数及其相关数据结构。
OS_MEM_EN 关
作用:设置程序中是否使用内存块管理函数及其相关数据结构。
OS_TIME_DLY_HMSM_EN
作用:设定程序中是否使用OSTimeDlyHMSM()函数。
OS_TIME_DLY_RESUME_EN 关
作用:设定应用系统是否需要使用OSTimeDlyResume()函数。
OS_TIME_GET_SET_EN 关
作用:设定应用系统中是否使用OSTimeGet()函数。
OS_CPU_HOOKS_EN
作用:设定是否在文件OS_CPU_C.C中实现各钩子函数(Hook Function).
说明:UC/OS中提供了5个对外接口函数:
OSTaskCreateHook();
OSTaskDelHook();
OS TaskStatHook();
OSTaskSwHook();
OSTimeTickHook();
这五个钩子函数即可以在文件OS_CPU_C.C中声明,也可以在用户代码中声明。
OS_ARG_CHK_EN关
作用:设定系统中是否使用参数检查功能。