实验一 任务的创建与多任务设计
实验目的
1、掌握任务创建和多任务启动的方法;
2、理解任务管理的基本原理,了解任务的各个基本状态及其变迁过程;
3、掌握 uC/OS-II 中任务管理的基本方法(创建、启动、挂起、解挂任务);
4、熟练使用 uC/OS-II 任务管理的基本系统调用;
5、熟悉 IAR软件的使用;
6、熟悉硬件系统和下载方法。
7、
实验仪器
1.LB-STM32 嵌入式实验开发系统;
2.USB 仿真器;
3.带IAR软件(集成开发环境)PC。
实验原理
从应用程序设计的角度来看,UC/OS-II的任务就是一个
线程,就是一个用来解决用户问题的C语言函数和与之相关
的一下数据结构而构成的一个实体,
由于系统存在着多个任务,于是系统如何来识别并管理一个任务就是一个需要解决的问题。识别一个任务的最直接的办法是为每一个任务起一个名称。由于μC/OS-II中的任务都有一个惟一的优先级别,因此μC/OS-II是用
任务的优先级来作为任务的标识的。所以,任务控制块还要来保存该任务的优先级别。
1、创建1个用户任务并运行
1 重新全编译
调试
程序代码
#define OS_GLOBALS
#include "includes.h"
#define TASK_STK_SIZE 512
OS_STK MyTaskStk[TASK_STK_SIZE];
u8 *s_M="0";
u8 x=0,y=0;
void MyTask(void *data);
* 函 数 名 : void main(void)
* 描 述 : main
* 输入参数 : None.
* 输出参数 : None.
* 返 回 : None.
void main(void)
{
#if (OS_TASK_NAME_SIZE > 14) && (OS_TASK_STAT_EN > 0)
INT8U err;
#endif
//目标板初化,
Target_Init();
#if OS_TASK_STAT_EN > 0
OSStatInit();
#endif
OSInit();
//设置空闲任务名称
#if OS_TASK_NAME_SIZE > 14
OSTaskNameSet(OS_TASK_IDLE_PRIO, "uC/OS-II Idle", &err);
#endif
//设置统计任务名称
#if (OS_TASK_NAME_SIZE > 14) && (OS_TASK_STAT_EN > 0)
OSTaskNameSet(OS_TASK_STAT_PRIO, "uC/OS-II Stat", &err);
#endif
OSTaskCreate(MyTask, s_M, &MyTaskStk[TASK_STK_SIZE - 1], 0);
OSStart( );
}
void MyTask (void *pdata)
{
u8 *s_Y="1";
pdata = pdata;
OSStatInit( );
for (;;)
{
if(x==9)
{
x=1;
y++;
Lightup_led(1); /*该函数点亮由x指定的led灯*/
Lightdown_led(8); /*该函数熄灭由x指定的led灯*/
}
else
{
Lightup_led(x);
Lightdown_led(x-1);
}
Show_num1(y);
x=x+1;
if (Get_key( )== 8)
{
Sys_return(); //此处停止系统
}
OSTimeDlyHMSM(0, 0, 1, 0);
}
}
显示一个数
#define OS_GLOBALS
#include "includes.h"
#define TASK_STK_SIZE 512
OS_STK MyTaskStk[TASK_STK_SIZE];
INT16S key;
u8 *s_M="0";
u8 X=0,Y=0;
void MyTask(void *data);
* 函 数 名 : void main(void)
* 描 述 : main
* 输入参数 : None.
* 输出参数 : None.
* 返 回 : None.
void main(void)
{
#if (OS_TASK_NAME_SIZE > 14) && (OS_TASK_STAT_EN > 0)
INT8U err;
#endif
//目标板初化,
Target_Init();
#if OS_TASK_STAT_EN > 0
OSStatInit();
#endif
OSInit();
//设置空闲任务名称
#if OS_TASK_NAME_SIZE > 14
OSTaskNameSet(OS_TASK_IDLE_PRIO, "uC/OS-II Idle", &err);
#endif
//设置统计任务名称
#if (OS_TASK_NAME_SIZE > 14) && (OS_TASK_STAT_EN > 0)
OSTaskNameSet(OS_TASK_STAT_PRIO, "uC/OS-II Stat", &err);
#endif
OSTaskCreate(MyTask, s_M, &MyTaskStk[TASK_STK_SIZE - 1], 0);
OSStart( );
}
void MyTask (void *pdata)
{
pdata = pdata;
OSStatInit( );
for (;;)
{
if (Y=X)
{
Y+=1;
Lightup_led(1); /*该函数点亮由x指定的led灯*/
Lightdown_led(8); /*该函数熄灭由x指定的led灯*/
}
Show_num2(Y);
X++;
if (Get_key( )== 8)
{
Sys_return(); //此处停止系统
}
OSTimeDlyHMSM(0, 0,1, 0);
}
}
#define OS_GLOBALS
#include "includes.h"
#define TASK_STK_SIZE 512
/ VARIABLES
OS_STK KingTaskStk[TASK_STK_SIZE];
OS_STK MyTaskStk[TASK_STK_SIZE];
OS_STK YouTaskStk[TASK_STK_SIZE];
INT16S key;
u8 *s_M="0",*s_Y="0",*S_K="0";
u8 x=0,y=0,z=0;
void KingTask(void *data);
void MyTask(void *data);
void YouTask(void *data);
* 函 数 名 : void main(void)
* 描 述 : main
* 输入参数 : None.
* 输出参数 : None.
* 返 回 : None.
void main(void)
{
#if (OS_TASK_NAME_SIZE > 14) && (OS_TASK_STAT_EN > 0)
INT8U err;
#endif
//目标板初化,
Target_Init();
#if OS_TASK_STAT_EN > 0
OSStatInit();
#endif
OSInit();
//设置空闲任务名称
#if OS_TASK_NAME_SIZE > 14
OSTaskNameSet(OS_TASK_IDLE_PRIO, "uC/OS-II Idle", &err);
#endif
//设置统计任务名称
#if (OS_TASK_NAME_SIZE > 14) && (OS_TASK_STAT_EN > 0)
OSTaskNameSet(OS_TASK_STAT_PRIO, "uC/OS-II Stat", &err);
#endif
OSTaskCreate(KingTask,S_K,&KingTaskStk[TASK_STK_SIZE - 1], 0);
OSStart( );
}
void KingTask (void *pdata)
{
OSTaskCreate(MyTask, s_M, &MyTaskStk[TASK_STK_SIZE - 1], 1);
OSTaskCreate(YouTask, s_Y, &YouTaskStk[TASK_STK_SIZE - 1], 2);
OSTimeDlyHMSM(0,0,100,0);
}
void MyTask (void *pdata)
{
#if OS_CRITICAL_METHOD ==3
OS_CPU_SR cpu_sr;
#endif
pdata=pdata;
OSStatInit();
for(;;)
{
if(x==9)
{
x=1;
y++;
Lightup_led(1); /*该函数点亮由x指定的led灯*/
Lightdown_led(8); /*该函数熄灭由x指定的led灯*/
}
else
{
Lightup_led(x);
Lightdown_led(x-1);
}
Show_num1(y);
x+=1;
if(Get_key()==8)
{
Sys_return();
}
OSTimeDlyHMSM(0,0,1,0);
}
}
void YouTask(void *pdata)
{
#if OS_CRITICAL_METHOD==3
OS_CPU_SR cpu_sr;
#endif
pdata=pdata;
for(;;)
{
if(z==5)
{
z=1;
y++;
Lightup_led(1); /*该函数点亮由x指定的led灯*/
Lightdown_led(4); /*该函数熄灭由x指定的led灯*/
}
else
{
Lightup_led(z);
Lightdown_led(z-1);
}
Show_num2(y);
z+=1;
if(Get_key()==5)
{
Sys_return();
}
OSTimeDlyHMSM(0,0,1,0);
}
}
第二篇:嵌入式实时操作系统
1、嵌入式系统的定义和三要素:嵌入式系统是以应用为中心,以计算机技术为基础,软硬件可裁剪,应用系统对功能,可靠性,成本,体积和功耗等严格要求的专用计算机系统。
(1) 系统级, (2) 板级, (3) 芯片级。
2、实时操作系统的定义,硬实时与软实时的区分,各自的特点是?
1) 实时系统的定义:一般地说,实时系统是指系统在限定的时间内能够提供所需要的服务水平的系统。实时系统根据对于实时性要求的不同,可以分为软实时和硬实时两种类型。
软实时系统要求各个任务运行得越快越好,但并不苛求任务运行的时限。如果系统特定的时序得不到满足,只会引起性能的严重下降,并不产生严重后果。
硬实时系统不仅要求各个任务执行无误,而且要求执行准时,如果特定的时序得不到满足,将会引起灾难性的后果。
3、嵌入式操作系统伴随着嵌入式系统的发展经历了四个比较明显的阶段。
第一阶段:无操作系统的嵌入算法阶段,通过汇编语言编程对系统进行直接控制,运行结束后清除内存。系统结构和功能都相对单一,处理效率较低,存储容量较小,几乎没有用户接口,比较适合于各类专用领域。
第二阶段:以嵌入式CPU为基础、简单操作系统为核心的嵌入式系统。CPU 种类繁多,通用性比较差;系统开销小,效率高;一般配备系统仿真器,操作系统具有一定的兼容性和扩展性;应用软件较专业,用户界面不够友好;操作系统主要用来控制系统负载以及监控应用程序运行。
第三阶段:通用的嵌入式实时操作系统阶段。以嵌入式操作系统为核心的嵌入式系统能运行于各种类型的微处理器上,兼容性好;内核精小、效率高,具有高度的模块化和扩展性;具备文件和目录管理、设备支持、多任务、网络支持、图形窗口以及用户界面等功能;具有大量的应用程序接口;嵌入式应用软件丰富。
第四阶段:以Internet为标志的嵌入式实时操作系统开始向网络操作系统方向发展,这是一个正在迅速发展的阶段。。
4、通用操作系统与实时操作系统区别。
(1)设计目标不同。(2) 调度原则不同。 (3) 内存管理机制不同。(4) 稳定性及交互性不同。 (5) 实时性不同。
5、嵌入式操作系统主要构成部分,核心是,其定义、功能
通常嵌入式操作系统由内核(Kernel)、文件系统、存储器管理系统、I/O管理系统、设备驱动程序、网络协议栈和标准化浏览器等部分组成,。
内核是多任务系统中的核心部分,提供多任务,为多任务分配CPU时间,提供任务管理与调度、时间管理、任务间通信和同步、内存管理等重要服务,并作为系统调用提供给任务的使用者。内核的基本任务是任务调度和任务间通信。实时内核主要有可剥夺型内核和不可剥夺型内核两种。内核允许将系统分成多个独立的任务,每个任务处理程序的一部分,从而简化系统的设计过程。
一个好的实时内核需要具备以下功能和特点:① 任务管理;② 任务间可以进行同步和通信;③ 实时时钟服务;④ 中断管理服务;⑤ 操作系统的行为是可知的和可预测的。
1、什么是前后台系统
前后台系统一般由前台(Foreground)和后台(Background)两部分程序组成。后台是一个无限循环的应用程序,循环中调用相应的任务函数完成相应的操作,各个任务依次运行,没有调度,运行的次序不能改变。前台是中断服务程序,处理异步事件。后台一般也叫任务级,前台也叫中断级。
2、基本的调度算法和什么是临界区
先来先服务,最短周期优先,优先级法,轮转法。
用于访问临界资源的代码段称为临界区。。
3、什么是任务切换?定义?任务有几个状态?
所谓任务切换(Context Switch或者Task Switch),实际上是模拟一次中断过程,从而实现CPU使用权的转移。每个任务都有自己独立的堆栈,称之为任务栈,用于保存任务的当前状态和所有寄存器内容。当内核决定运行另一个任务时,首先入栈,将当前任务用到的所有寄存器内容以及当前状态保存到自己的任务栈中去,然后像中断返回一样,将下一个将要运行的任务的所有寄存器内容和状态从该任务的任务栈中弹出,重新装入CPU的寄存器,任务即恢复到挂起前的状态,并开始执行。这个过程就是任务切换。
休眠态、就绪态、运行态、挂起态)和被中断态五种状态。
4、什么是死锁?产生的根本原因?产生死锁的4个必要条件?预防的基本思想?
死锁又称抱死,是指两个或者更多的任务相互等待对方占有的资源而无限期地僵持下去的局面。
产生死锁的根本原因在于:系统资源不足,任务运行推进的顺序不合理,资源分配不恰当等。
死锁产生有四个必要的条件:① 互斥条件,系统中某些资源只能独占使用;② 非抢占条件,系统中某些资源仅能被它的占有者所释放,而不能被别的任务强行抢占;③ 占有并等待条件,系统中的某些任务已占有了分给它的资源,但仍然等待其它资源;④ 循环等待条件,系统中由若干任务形成的环形请求链,每个任务均占有若干种资源中的某一种,同时还要求(链上)下一个任务所占有的资源。必要的条件具备后,当任务推进顺序不合理时,死锁就发生了。
死锁预防的基本思想:打破产生死锁的四个必要条件中的一个或几个。
预防死锁的策略:资源预先分配策略、资源有序分配策略。
(1) 资源预先分配策略:
(2) 资源有序分配策略:
5、内核的定义和基本任务和分类?
内核是多任务系统中的核心部分,提供多任务分配cpu时间,提供任务管理与调度,时间管理通信与同步,内存管理等重要服务,并作为系统供给任务的使用者。
内核的基本任务是任务调度与任务间的通信。
不可剥夺型内核(Non-Preemptive Kernel)的特点是运行的任务占有CPU的绝对使用权,若不自我放弃,准备就绪的高优先级任务不能抢占CPU的使用权。
使用可剥夺型内核,准备就绪的最高优先级任务总能得到CPU的使用权。
6、什么是互斥?本质?满足互斥最一般的方法有?
任务间通信的最简单方法就是使用共享数据结构或者共享变量,为了防止任务在使用共享资源时破坏数据,资源必须独占使用。这种独占使用资源的方法称为互斥,互斥的本质是为了有序地利用资源。
满足互斥最一般的方法有:禁止中断、禁止抢占、利用信号量和测试并置位等。
7、通信?常用的通信方法?同步?同步的方式?常用的同步方法?
任务与任务之间或者任务与中断服务之间的信息传递称为任务间的通信。任务间的通信机制是多任务之间相互同步和协调各活动的主要手段。任务间信息的传递有两个途径:通过全程变量或发消息给另一个任务。常用的通信方法主要有全局变量、信号量、消息邮箱、消息队列、事件标志组和内存块。
在实时系统中,一个工作的完成往往需要多个任务或者多个任务与多个任务中断共同完成,它们之间必须互相配合,协调动作,甚至交换信息,就要用到同步技术。同步可以是任务与任务之间的同步,也可以是任务与中断之间的同步。同步的方式有单向同步和双向同步。同步的方法有信号量,事件标志组,消息邮箱,消息队列。
1、什么是任务控制块?有何功能?
任务控制块(Task Control Blocks,OS_TCB)是一个用来保存任务各种状态信息的数据结构,它可以实现如下功能:① 一旦任务建立,任务控制块OS_TCB就会被赋值;② 当任务的CPU使用权被剥夺时,任务控制块用来保存该任务的状态;③ 当任务重新得到CPU使用权时,任务控制块能确保任务从当时被中断的那一点丝毫不差地继续执行。OS_ TCB全部驻留在RAM中。
2、任务调度的功能有哪些方式?
任务的调度机制是内核的核心。μC/OS-Ⅱ的调度器主要有两个功能:一是确定进入就绪态的任务中哪个优先级最高;二是进行任务切换。调度有两种方式:任务级的调度是由OSSched()函数完成的;中断级的调度是由OSIntExt()函数完成的。
1、中断定义,异步事件?中断详细处理过程?
中断定义为CPU对系统内外发生的异步事件的响应。异步事件是指没有一定时序关系的、随机发生的事件。当中断产生时,由硬件向CPU 发送一个异步事件请求,CPU接收到请求后,中止当前工作,保存当前运行环境,转去处理相应的异步事件任务,这个过程称为中断。事件处理完毕后,在前后台系统中,程序回到后台程序;在不可剥夺型内核中,程序回到被中断了的任务;在可剥夺型内核中,让进入就绪态的优先级最高的任务开始运行,若没有高优先级任务准备就绪,则回到被中断了的任务。
2、中断延迟
在前后台系统中:
在不可剥夺型和可剥夺内核中:
3、中断响应
在前后台系统和不可剥夺型内核中,保存寄存器以后立即执行用户代码,中断响应由下式给出:
中断响应?=?中断延迟?+?保存CPU内部寄存器的时间
在可剥夺型内核中,则要先调用一个特定的函数,通知内核即将进行中断服务,使得内核可以跟踪中断的嵌套。对μC/OS-Ⅱ说来,这个函数是OSIntEnter(),可剥夺型内核的中断响应由下式给出:
中断响应?=?中断延迟?+?保存CPU内部寄存器的时间?+?内核进入中断服务函数的执行时间
4、延时函数?
程序清单4.6 OSTimeDly()函数的源代码
void OSTimeDly (INT16U ticks) {
if (ticks > 0) { /*如果加入指定一个0值,则表示不想对任务延时,函数立即返回调用者*/
OS_ENTER_CRITICAL(); /*关中断*/
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { /*从就绪表中移出当前任务*/
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;}
OSTCBCur->OSTCBDly = ticks;/*保存节拍数,每隔一个时钟节拍,这个成员变量数减1*/
OS_EXIT_CRITICAL(); /*关中断*/
OSSched(); /*当前任务已经挂起,任务调度程序执行下一个优先级最高的就绪任务*/
}
}
5、中断程序流程图
1、移植的概念
这里所谓的移植,就是使一个实时内核能运行在另一种微处理器或者微控制器上。
2、移植对开发工具的要求,微处理器的要求(了解)
1. 移植对微处理器的要求
要使µC/OS-Ⅱ能够正常运行,处理器和编译器必须满足以下五项要求:
(1) 处理器的C编译器能产生可重入代码;
(2) 用C语言就可以实现开关中断;
(3) 处理器至少能支持定时中断,中断频率一般在10~100 Hz之间;
(4) 处理器能够支持硬件堆栈,容量可达几KB;
(5) 处理器有堆栈指针和读/写CPU其它寄存器、堆栈内容或内存的指令。
2. 对移植开发工具的要求
移植µC/OS-Ⅱ,需要一个针对用户用的CPU的C编译器,它必须满足如下要求:
(1) ?C编译器必须支持汇编语言程序。
(2) ?C编译器必须能支持可重入代码,因为µC/OS-Ⅱ是一个可剥夺型内核。
(3) ?C编译器必须包括汇编器、连接器和定位器。连接器用来将经编译和汇编后产生的不同的模块连接成目标文件。定位器用于将代码和数据放置在目标处理器的指定内存映射空间中。
(4) ?C编译器必须支持从C中打开和关闭中断。
(5) ?C编译器最好支持用户在C语言程序中嵌入汇编语言,这有利于用汇编语言来直接开关中断。
3、移植的主要工作
(1) 声明11个数据类型(OS_CPU.H);
(2) 用#define声明4个宏(OS_CPU.H);
(3) 用C语言编写10个简单的函数(OS_CPU_C.C);
(4) 编写4个汇编语言函数(OS_CPU_A.ASM)。
4、移植代码的测试。目的?如何进行?方法?测试的主要顺序?
在代码修改移植结束后,紧接着要进行的工作就是测试。测试的目的就是验证所移植的代码是否能正常地工作,这也可能是移植过程中最复杂的工作,但必不可少。
测试的主要方法如下:
(1) 在没有应用程序的情况下进行,让内核自己测试自己,以确保自身运行状况没有错误。这样做有两个好处:第一,避免使本来就复杂的事情变得更加复杂;第二,如果出现问题,可以知道问题出在内核代码上而不是应用程序。
(2) 简单的测试代码如程序清单10.7所示。当多任务调度运行成功后,再逐步添加应用程序。
#include "includes.h"
void main()
{
OSInit();
OSStart()
测试的主要顺序如下:
(1) 确保C编译器、汇编编译器及连接器正常工作;
(2) 验证OSTaskStkInit()和OSStartHighRdy()函数;
(3) 验证OSCtxSw()函数;
(4) 验证OSIntCtxSw()函数。
这些测试可以用少量的任务和中断来实现。
5、MC51移植程序(不知道考什么)
程序清单10.8 OS_CPU.H头文件在MCS-51单片机上的移植代码
/************************与编译器有关的数据类型**************************/
typedef unsigned char BOOLEAN; /*不能使用bit定义,因为在结构体里无法使用*/
typedef unsigned char INT8U; /*无符号8位数*/
typedef signed char INT8S; /*有符号8位数*/
typedef unsigned int INT16U; /*无符号16位数*/
typedef signed int INT16S; /*有符号16位数*/
typedef unsigned long INT32U; /*无符号32位数*/
typedef signed long INT32S; /*有符号32位数*/
typedef float FP32; /*单精度浮点数*/
typedef double FP64; /*双精度浮点数*/
typedef unsigned char OS_STK; /*定义堆栈入口宽度为8位 */
typedef unsigned char OS_CPU_SR; /*定义CPU状态字的宽度为8位*/
/***************************与处理器有关的代码*****************************/
#if OS_CRITICAL_METHOD == 1
#define OS_ENTER_CRITICAL() EA=0 /*直接禁止中断*/
#define OS_EXIT_CRITICAL() EA=1 /*直接允许中断*/
#endif
#if OS_CRITICAL_METHOD == 2
#define OS_ENTER_CRITICAL() /*未用*/
#define OS_EXIT_CRITICAL() /*未用*/
#endif
#if OS_CRITICAL_METHOD == 3
#define OS_ENTER_CRITICAL() cpu_sr = IE & 0x80;IE &= 0x7F /*禁止中断*/
#define OS_EXIT_CRITICAL() IE |= cpu_sr /*允许中断*/
#endif
#define OS_STK_GROWTH 0 /*MCU-51堆栈从下往上增长 1=向下,0=向上*/
#define OS_TASK_SW() OSCtxSw()
6、MC51移植代码重要函数OSTaskStkInit()函数功能
程序清单10.9 MCS-51的OSTaskStkInit()函数移植实例
void *OSTaskStkInit (void (*task)(void *pd), void *ppdata, void *ptos, INT16U opt) reentrant
{
OS_STK *stk;
ppdata = ppdata;
opt = opt; /*opt没被用到,保留此语句防止告警产生*/
stk = (OS_STK *)ptos; /*任务堆栈最低有效地址*/
*stk++ = 15; /*任务堆栈长度*/
*stk++ = (INT16U)task & 0xFF; /*任务代码地址低8位 */
*stk++ = (INT16U)task >> 8; /*任务代码地址高8位*/
*stk++ = 0x00; /*PSW*/
*stk++ = 0x0A; /*ACC*/
*stk++ = 0x0B; /*B*/
*stk++ = 0x00; /*DPL*/
*stk++ = 0x00; /*DPH*/
*stk++ = 0x00; /*R0 */
*stk++ = 0x01; /*R1*/
*stk++ = 0x02; /*R2 */
*stk++ = 0x03; /*R3 */
*stk++ = 0x04; /*R4 */
*stk++ = 0x05; /*R5 */
*stk++ = 0x06; /*R6 */
*stk++ = 0x07; /*R7 */
/*不用保存SP,任务切换时根据用户堆栈长度计算得出*/
*stk++ = (INT16U) (ptos+MaxStkSize) >> 8;/*?C_XBP仿真堆栈指针高8位(见程序清单10.10) */
*stk++ = (INT16U) (ptos+MaxStkSize) & 0xFF;/*?C_XBP 仿真堆栈指针低8位*/
return ((void *)ptos); /*返回最低地址,这里不用弹出栈顶指针是为了提高计算效率*/
}
1、程序清单9.6 OSMemQuery()函数的源代码
#if OS_MEM_QUERY_EN > 0
INT8U OSMemQuery (OS_MEM *pmem, OS_MEM_DATA *ppdata)
{
# if OS_CRITICAL_METHOD =?= 3
OS_CPU_SR cpu_sr;
# endif
# if OS_ARG_CHK_EN > 0
if (pmem =?= (OS_MEM *)0) { /*确保指针指向的内存控制块是有效的*/
return (OS_MEM_INVALID_PMEM);
}
if (ppdata =?= (OS_MEM_DATA *)0) { /*确保存储信息的数据结构是有效的*/
return (OS_MEM_INVALID_PDATA);
}
# endif
/*复制内存分区中的全部内容,为了防止中断打入而修改某些变量,所以要禁止中断*/
OS_ENTER_CRITICAL();
ppdata->OSAddr = pmem->OSMemAddr;
ppdata->OSFreeList ?= pmem->OSMemFreeList;
ppdata->OSBlkSize = pmem->OSMemBlkSize;
ppdata->OSNBlks = pmem->OSMemNBlks;
ppdata->OSNFree = pmem->OSMemNFree;
OS_EXIT_CRITICAL();
ppdata->OSNUsed = ppdata->OSNBlks - ppdata->OSNFree; /*计算正在使用的内存块数*/
return (OS_NO_ERR);
}
# endif