这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 软件与操作系统 » 【白骨精】hanshuyujifen2的进程帖 8.20更新 示例工程中串口初始

共44条 1/5 1 2 3 4 5 ›| 跳转至

【白骨精】hanshuyujifen2的进程帖 8.20更新 示例工程中串口初始化分析

高工
2013-06-06 17:05:23     打赏

hanshuyujifen2的进程帖

1、目录 http://forum.eepw.com.cn/thread/232031/1#1

2、任务和计划 http://forum.eepw.com.cn/thread/232031/1#2

3、对工程代码的理解  http://forum.eepw.com.cn/thread/232031/1#3

4、问题及解答 http://forum.eepw.com.cn/thread/232031/1#4

4、MCU相关操作在  http://forum.eepw.com.cn/thread/232031/1#5

5、试验后作业 http://forum.eepw.com.cn/thread/232031/1#6

6、OS实验1-4描述及代码 http://forum.eepw.com.cn/thread/232031/1#7

7、OS实验5-6描述及代码 http://forum.eepw.com.cn/thread/232031/1#9

8、OS实验7-8-9描述及代码 http://forum.eepw.com.cn/thread/232031/1#10

9、OS实验10-11-12描述及代码 http://forum.eepw.com.cn/thread/232031/2#14

10、OS实验13描述及代码 http://forum.eepw.com.cn/thread/232031/3#28

11、解决uTenux链接时候的六个警告:http://forum.eepw.com.cn/thread/232031/4#35

12、英倍特提供例程中的启动文件分析:http://forum.eepw.com.cn/thread/232031/4#36

13、点亮板子上的三个LED:http://forum.eepw.com.cn/thread/232031/4#39

14、英蓓特示例代码中,串口初始化分析http://forum.eepw.com.cn/thread/232031/5#43

15、串口初始化及收发数据的实现:http://forum.eepw.com.cn/thread/232031/5#44

我要弄个最长的首页


------------------------------------------华------丽------的------分-----隔---符---------------------------------------

此分隔符版权所有,转载请添加署名:hanshuyujifen2




关键词: uTenux     实验    

高工
2013-06-06 17:05:35     打赏
2楼


1计划描述:

1、管理1-6BLDC和一个超声波测距器。利用硬件的PWM OS的多任务管理。

2、实现调试数据回显(串口发送)和记录(FATFS或者记录到Flash上),加入USB通信部分。

3、电机工作参数的采集和记录(电流、转速、工作电压等)

4、如果时间充足,将会在程序中添加姿态解算模块。用于解算飞行类玩具的实时姿态。(自备传感器模块)
5
、本次只是验证型实验,不会有正经产品出来。板子也不会上天。

6、参加这次活动只关注OS的使用,不关注移植。

电机驱动和通信功能是必须完成的,姿态解算作为补充。

做一个有理想有道德有文化有纪律的白领骨干精英(白骨精)

------------------------------------------华------丽------的------分-----隔---符---------------------------------------


2、认识硬件

AT91SAM3S处理器,基于CM3,内核最大速度64M

开发板资源:

    AT91SAM3S4C(Cortex M364MHzFlash 256KBRAM 48KB)

UART x1、(串口)

USART x1

RS485 x1、(半双工串口,485标准 ADM3485ARZ

USB x1(Device DEVICE/USB2.0/FullSpeed/DDPDDM)

JTAG x1 (调试接口)

2.8" Touch LCD x1、(ILI9325

NANDFLASH x2Gb 128MB 相当壮观的NANDFlashMT29F2G08AADWP

MicroSD x1(手上没有这种卡,NANDFlash足够用 MicroSD/SDV1.0/HSMCI

麦克风/耳机 TS922/TPA0223DGQ

外部时钟:12M+32.768

AT91SAM3S4C资源:

100引脚

SRAM48K

FLASH256K

MPU

PORBORWatchdog

22DMA通道

全速USB2.0设备

2USART(支持ISO7816,IrDA,RS485,Modem,SPI,Manchester)

2UART

2TWI(I2C)1SPI1SSC(I2S)1HSMCI(SDIO/SD/MMC)

6通道16位定时/计数器(TC),用于捕捉、比较和PWM

4通道16PWM

32位实时定时器(RTT)和带有日历及报警特性的RTC

15通道121MspsADC

1DAC

79GPIO

232PIO

 ------------------------------------------华------丽------的------分-----隔---符---------------------------------------

2013.06.05


计划使用PWM模块完成电机驱动,今天先了解下MCU的PWM模块

AT91SAM3S PWM模块特性

1116PWM控制器

2、通用时钟发生器,提供13个不同的时钟

3、独立通道编程

   独立的使能/失能控制

   独立的时钟选择

   独立的带双缓冲的周期、占空比

   可编程选择输出极性

   可编程的中心或者左对齐输出波形

   每个通道都有的独立的输出判决

   每个通道相互独立的12比特互补死区发生器

   独立的使能/失能指令

   独立的时钟选择

   独立的带双缓冲的周期、占空比

4、同步通道模式

   同步通道共用计数器

   在可编程的周期后,更新同步通道寄存器

5、连接到一个PDC通道

   不需要处理器介入的输出缓冲,用于更新同步通道的占空比

6、可在一个周期内发送四次ADC触发的独立的事件线

7、提供异步保护的 可编程的故障输入

82路步进电机控制





高工
2013-06-06 17:05:42     打赏
3楼

对工程代码的理解在这里

2013.06.12

关于在一个Workspace中分多个工程来组织程序。

这种方式,我只在微软的VS中见过,得益于微软VS强大的编辑功能、提示功能和工程协同管理功能,这个方式确实很方便。但是,在MDK环境下,真的很不方便,MDK的编辑功能跟VS完全没得比。查看变量定义只局限于当前活动的工程。要想看一个变量定义,需要先将这个工程设置为当前活跃的工程,然后按F12找到变量定义。如果变量又跳到了别的工程,就必须设置那个工程为活跃工程,还不一定能跟踪到。

这个问题我纠结了好多天了,前几天王老板说出新版本就不是这么个组织结构了。在学习这种工程组织方式优点的同时,期待新版本。

------------------------------------------华------丽------的------分-----隔---符---------------------------------------

2013.06.12

依赖于宏定义和目录结构的头文件引用

uT工程中包含了很多芯片配置和应用,估计是为了方便编译和扩展,使用了一种很奇怪的头文件引用方式。对于一般的开发,感觉很繁琐。

比如,我使用STM32F4VG这个芯片,按照悠龙提供的配置手册就要打开D:\uLoong\uTenux\bin\app_stm32f4\workspace.uvmpw这个工程文件。

在这个工程的编译选项里边,定义了这么几个宏定义:_APP_STM32F4_,_CHIP_STM32F407VG_,CPU_ARMV7E_M

Appusermain工程的usermain.c文件中,我们可以看到这么个引用:

#include

tk_config.h文件位于D:\uLoong\uTenux\config目录下,这个文件中有这么个引用:

#include

我们可以在sysdepend这个文件夹找到tk_config_common.h。这里就将引用导向了另外一个位置。

在文件tk_config_common.h中,宏定义就开始登场了:这个文件里做的是一些根据宏定义而进行的条件编译。这里的条件编译,同样是将引用指向另外的位置。

F4的工程中定义了_APP_STM32F4_于是,引用又导向到了sysdepend/app_stm32f4/tk_config_depend.h

同样,这个tk_config_depend.h也在做根据宏定义的导向:

#ifdef _CHIP_STM32F407IG_

#include

#endif

 

#ifdef _CHIP_STM32F407VG_

#include

#endif

#ifdef _CHIP_STM32F407ZG_

#include

#endif

我们定义了_CHIP_STM32F407VG_ 于是引用被最终导向到了这里

D:\uLoong\uTenux\config\sysdepend\app_stm32f4\chip_stm32f407vg\tk_config_depend.h

这才是真正的系统配置文件。各种芯片信息都在这里了。

 

还有几个地方使用的也是这种配置引用导向的方式,比如各种MakeFile,暂时还没去关心。

 

这种引用方式,对于程序的扩展,添加更多的MCU确实很方便。可是我在看代码的时候却费死了劲。也许悠龙这么做,就是为了让俺们不要关心最底层的代码吧!

------------------------------------------华------丽------的------分-----隔---符---------------------------------------

创建第一个任务:

main函数是c程序的入口点,我们来看看uTenuxmain函数。uTmain位于D:\uLoong\uTenux\app\usermain\src\usermain.c中。

一般情况下,在创建第一个任务之前要完成所有硬件外设的初始化工作。进入main第一步就这么做了,使用bsp_init()完成外设的初始化。

然后,通过一个任务结构体initctsk创建并启动第一个任务inittask

initctsk.task    = (FP)&inittask;

然后使用tk_sta_sys((T_CTSK *)&initctsk);完成系统的启动和任务的创建。

在任务inittask中,调用usermain函数进行系统的一般性CRUD工作,即创建、读取、更新、删除。

这里进行了很多很多的调用和跳转,最后跳转到底层的各个驱动程序里边完成MCU的操作。感觉就像在用VSC#代码


一点总结:

1uTWorkSpace里边所以的工程都会引用根目录下includeconfig这两个文件夹。

2、使用宏定义确定使用的是哪个系列的哪种芯片。如果uT没有提供,可以自己加。

3uT的跳转很多,引用包含很多。但是结构一致,搞懂一个剩下的就容易了。

4、要添加自己的驱动,将文件放到D:\uLoong\uTenux\lib\libdev\src\sysdepend\app_stm32f4中,并将驱动文件添加到工程目录的libdev目录里。

5、各个工程有不同的分工,写哪个部分将哪个工程设置为活跃,不然就会有各种难题出现。

------------------------------------------华------丽------的------分-----隔---符---------------------------------------

2013.06.14

小插曲:关于libtm工程。

这个工程中,有用的职业 tm_monitor这个东西。

昨天咨询了uTenux的工程师,工程中tm_monitor是一只打开的,不管配置文件中的USE_TMONITOR1还是0.

于是,这个monitor就完全可以作为一个串口处理类来使用了。

先了解一下tm_monitor.c中的几个函数(内核规范中没得介绍,可能是因为不属于内核吧)。

1uint32_t tm_getchar( uint32_t wait );

   关中断,然后从控制台接收字符。

2uint32_t tm_getline( uint8_t *buff );

   关中断,然后接收一行字符。直到遇见结束符(空字符)

3void tm_monitor( void );

   就是一个空的for死循环

4uint32_t tm_putchar( uint32_t c );

   向控制台发送一个字符

5uint32_t tm_putstring( const uint8_t *buff );

  向控制台发送字符串

 

这里的各个功能都依赖于串口实现。串口的实现在libdev中的ts_uart.c中完成。只有三个函数:初始化,接收字符,发送字符:

1void uart_init(void)

串口初始化

2void uart_sendchar(uint8_t c)

串口发送一个字符

3uint8_t uart_recvchar(void)

   通过串口接收一个字符。如果没有字符进来,就一直死循环。

------------------------------------------华------丽------的------分-----隔---符---------------------------------------

2013.06.14

向工程中添加底层驱动:

将驱动文件放入D:\uLoong\uTenux\lib\libdev\src\sysdepend\app_at91sam3文件夹中

将文件添加到工程libdevsrc目录中

D:\uLoong\uTenux\include\dev\sysdepend\ts_devdef_common.h中添加对驱动头文件的引用。

将硬件初始化函数添加到文件D:\uLoong\uTenux\include\dev\sysdepend\ts_devdef_common.h中的函数Inline void bsp_init ( void )中。

重新编译。之后就可以使用你写的硬件驱动了。

 

当然,想文件中添加这些内容的时候,要注意宏定义,别搞错了MCU

------------------------------------------华------丽------的------分-----隔---符---------------------------------------

2013.06.15


uTenux中任务的状态

运行状态:当前任务正在运行

就绪状态:任务已经完成运行前的准备,但由于有更高优先权的任务正在运行,使得它不能运行

等待状态:由于运行的条件未达到而导致任务不能运行。分三种:

    1、等待状态:等待资源

    2、挂起状态:执行被其他任务强行中断。 等待优先权

3、等待挂起状态; 任务在同一时间内既在等待状态,也在挂起状态

静止状态:任务还未启动或者已经完成执行的状态

不存在状态:任务未在系统中注册







高工
2013-06-06 17:05:51     打赏
4楼

2012.06.13

每次修改代码之后,都要Batch Build。(针对版本1.5)

Bin这个工程每次都需要重新编译

Appusermain工程中操作使用OS提供的功能,比如创建/删除任务。当更改这个工程内容时需要重新编译

Kernel工程只需要在首次编译时候或者修改了配置文件之后编译下即可,因为我们一般是不需要修改内核的。

Libcpulibtmlibdev在修改了与芯片有关的内容时候才需要重新编译。

------------------------------------------华------丽------的------分-----隔---符---------------------------------------

还是创建第一个任务:

在启动OS的时候需要有一个任务结构体指针做参数,启动OS的时候必须创建一个任务。用户田间任务都需要在这个任务中进行。

任务结构体定义如下:

typedef struct t_ctsk {

         VP                  exinf;               /*任务扩展信息 Extended information */

         ATR                 tskatr;                  /* 任务属性Task attribute */

         FP                  task;                    /* 任务入口点 */

         PRI                 itskpri;                 /* 启动任务时候的优先级 */

         W                   stksz;                   /* 用户堆栈User stack size (byte) */

         UB                  dsname[8];               /* 任务的对象名Object name */

         VP                  bufptr;                  /* 用户缓存指针User buffer */

} T_CTSK;

示例中,第一个任务的说明:

任务名taskinittask

优先级itskpri: (MAX_PRI-2); = 30;

任务名dsnameinittsk

任务属性tskatrTA_HLNG | TA_RNG0 | TA_DSNAME;

        使用高级语言/保护级别为0/使用对象名

任务缓存bufptr:空

任务额外信息exinf:空

任务堆栈大小stksz512字节


ucos相似,任务名就是任务函数的入口。使用函数指针做参数

任务堆栈跟usos一样,只是已经弄到任务初始化结构中了。

 

问题贴:

uT最多支持多少个任务?

uT是否支持时间片轮询?

  不支持(王总亲自回答的问题)

uT有没有OSTimeDly() OSTimeDlyHMSM()之类的时间处理函数?

  有类似的 

uT有多少个优先级?

  任务优先级的值被指定为1140之间的数,数值越小,优先级越高。

uT是否支持多个任务具有相同的优先级?

uT任务的优先权跟优先级是什么关系?优先级相同的时候,怎样确认优先权?

uT任务的任务缓存和任务堆栈大小如何确定?

DS对象是什么?


任务堆栈是做什么用的?怎样确定其大小? http://forum.eepw.com.cn/thread/232654/1

【已解决】怎样解决在工程选项中,选中use microlib之后,连接时出错的问题

http://forum.eepw.com.cn/thread/232661/1

【已解决】打开内核调试功能后USE_HOOK_TRACE后链接出错

http://forum.eepw.com.cn/thread/232768/1


【已解决】问题,关于邮箱发送消息和接收消息函数中使用的空类型指针void*

http://forum.eepw.com.cn/thread/233206/1

【已解决】关于互斥体中顶置优先级的一点问题

  http://forum.eepw.com.cn/thread/233310/1






高工
2013-06-06 17:06:00     打赏
5楼

MCU相关操作在这里

------------------------------------------华------丽------的------分-----隔---符---------------------------------------

2013.06.18

STM32F4中,打开FPU功能:

先在编译选项中设置使用FPU,然后在内核文件knl_kernel.h中第97行设置即可

#define __FPU_PRESENT          1      

当然,我不知道为什么要用FPU。但是有这个功能就打开好了      


高工
2013-06-13 00:33:33     打赏
6楼

每个实验后的问题在这里


 ------------------------------------------华------丽------的------分-----隔---符---------------------------------------


1) 如果打开调试支持和钩子函数(HOOK),重新构造运行,会有什么不同?

源文件\uTenux_V1.6.00r180\uTenux_OpenSource_Edition\source\uTenux\config\sysdepend\app_stm32f4\chip_stm32f407vg\tk_config_depend.h

,有以下几个宏:

USE_DBGSPT   USE_HOOK_TRACE  USE_OBJECT_NAME   OBJECT_NAME_LENGTH 

USE_DBGSPT设置默认为0,当设置为1表示打开调试支持模式,内核在构造时会链接所有的内核状态查询调用代码

USE_HOOK_TRACE设置默认为0。在CortexM3/M4内核芯片上,当设置为1表示打开执行跟踪调用,内核在构造时会链接所有的执行跟踪调用代码,前提是必须打开调试支持模式宏USE_DBGSPT

打开之后会出现一些内核相关信息:

OS进行系统调度的时候输出内核信息,退出时,输出程序中各个任务所使用的CPU时间等内核信息:

 

Task sample create step 1-Create Task A;

Task sample create step 2-Create Task B;

Task sample create step 3-Create Task C;

Task sample create step 4-Start Task B;

----------------------------------------系统调度信息

Debug svc hook function is entering...

task ID is: 3

----------------------------------------

Task B parameter stacd is 5

Task B will start task C;

----------------------------------------

Debug svc hook function is entering...

task ID is: 4

----------------------------------------

----------------------------------------

Debug svc hook function is leaving...

task ID is: 4

----------------------------------------

Task B will start task A;

----------------------------------------

Debug svc hook function is entering...

task ID is: 2

----------------------------------------

Task A will be sleeping;

----------------------------------------

Debug svc hook function is entering...

task ID is:

----------------------------------------

----------------------------------------

Debug svc hook function is leaving...

task ID is:

----------------------------------------

Task B is running,input Command(e=exit):

...Task A total running time is 20ms//占用时间统计

...Task B total running time is 70ms

...Task C total running time is 0ms

 

 

2) 如果任务C在任务循环中不是进行循环,而是也调用tk_slp_tsk,会出现什么情况?为什么?

根据实验结果,如果这样,系统将会在TaskC执行完成之后退出。

而且不管使用tk_dly_tsk函数tk_slp_tsk都会退出。

如果既不延迟也不休眠任务,这是MCU好像就要reset了。MCU运行脱离了OS的管理

至于原因,没有资料俺看不懂。不瞎分析了


3) 找找参考程序中有没有永远不会执行的代码?实际修改一下,看看判断的是否准确?为什么?

关于任务中永远不会执行的代码:

实验发现,每个任务中的最后一段输出是不会执行的:

tm_putstring((UB*)"task A will Exit ;\n");

    tk_ext_tsk();

tm_putstring((UB*)"task C will Exit ;\n");

    tk_ext_tsk();

因为每个任务都是一个死循环。而TaskB中的死循环,会通过

if(c=='e') {

                    break;      

           }

而退出



高工
2013-06-13 00:34:18     打赏
7楼

我所做OS实验的描述及代码

2013.06.22

实验1:任务:

本任务参考sample 01.Task

同样在TaskSample任务中创建三个任务:TaskA,TaskB,TaskC,优先级分别为242628。之后启动优先级最低的任务TaskC。在任务TaskC中分别启动TaskATaskBTaskATaskB完成一段标志信息的输出后进入休眠状态等待唤醒。

TaskC完成上述动作后,就开始等待输入了。我使用了这样的代码:

if('e' == tm_getchar(-1))

    {

      break;

    }

    else

    {

      tm_putstring((UB*)"**************************************\n");

      tm_putstring((UB*)"task a will wup\n");

      tk_wup_tsk(TaskID_A);

      tm_putstring((UB*)"task b will wup\n");

      tk_wup_tsk(TaskID_B);

}

如果输入为字母‘e’,则系统退出。否则唤醒TaskATaskB

这里要特别注意函数tm_getchar。因为如果tm_getchar没有完成,OS的中断就是一直关着的。不能进行任务调度,即使此时TaskA或者TaskB都等待了tmout时间,已经处于就绪状态。

这个实验设计的有一个缺点。任务C中使用到的tm_getchar关闭了系统中断,导致系统停止调度。不能很好地表现任务优先级的关系。

 

如果UserMain创建的任务(TaskSample)的优先级高于TaskATaskBTaskC会怎样?

在我的实验中,如果TaskSample的优先级高于TaskC,那么由于TaskSample创建TaskC之后,Task将不会立即执行。TaskSample继续执行,即在执行完tk_sta_tsk(TaskID_C,4);之后,继续执行 return E_OK;。程序返回,继续执行inittask中的代码。即:tk_ext_sys();退出OS

以下是我的实验代码:

实验1.rar

实验中使用到的任务处理函数:

tk_sta_sys

启动内核。这个东西是uT自己加进去的,必须在其他系统调用之前执行。需要有一个任务做完参数。启动内核时会立即创建和启动这个任务,即将CPU的控制权交给这个任务。

tk_ext_sys

退出内核

tk_sta_tsk

启动任务,将任务从静止状态转换成就绪状态。需要提供TaskID和另外一个参数stacd。参数Stacd没有什么大用处。

tk_slp_tsk

将任务休眠。参数tmout指的是OS时钟的周期数。如果这个周期结束前调用了tk_wup_tsk

,那么任务被唤醒。如果在tmout周期内没有收到tk_wup_tsk唤醒,那么任务自动唤醒。但会返回一个错误代码。

tk_wup_tsk

唤醒由tk_slp_tsk休眠的任务。将任务从等待状态释放

tk_dly_tsk

暂停调用这个函数的任务的执行。但是这时,这个任务的状态还是等待状态而不是休眠状态。。如果要提前终止这个等待时间,可以用tk_rel_wai来执行。

tk_ter_tsk

终止其他任务。强制将tskid指定的任务变为静止状态。任务使用的资源不会自动释放。

tk_del_tsk

删除任务。将任务从静止状态转为不存在状态。所删除的任务必须是静止状态

tk_ext_tsk

退出调用任务。就是调用这个函数的任务自己把自己退出了。自杀,变成静止状态。但是这个函数不会释放任务所使用的资源,需要手动释放。

 

实验1完成

------------------------------------------华------丽------的------分-----隔---符---------------------------------------

2013.06.23


实验2:信号量

关于信号的量申请:

如果一次需要获得多个信号量,但是信号量又不够。比如我需要3个信号量,但剩余的信号量只有1个。那么用tk_wai_sem申请信号量时候,不会改变信号量计数,然后开始等待其他信号量。

实验验证:

创建三个任务:任务ABC。其中任务A需要三个信号量,任务B需要4个信号量。任务A的优先级大于任务B。任务C为最低优先级的空闲任务。

创建任务后,先启动任务B,任务B先申请4个信号量。然后启动任务A

由于任务A的优先级高于任务B,任务B被中断,任务A开始执行。由于系统中没有可用信号量,任务A申请信号量失败 进入等待状态。

之后回到任务B,任务B释放四个信号量。此时,系统中有足够的信号量供任务A使用,任务A被唤醒。继续执行。

任务A执行一次之后进入休眠,任务B开始执行。任务B申请并获得4个信号量,完成LED反转。之后释放信号量进入休眠。

两个任务都进入等待状态,任务C开始执行。任务C是一个死循环。用于防止OS没有正在运行的任务而退出。

 

实验代码如下:

ER SemSample(void)

{

  ER ercd = E_OK;

  T_CTSK ctsk;

  T_CSEM csem;

 

  ctsk.exinf = NULL;

  ctsk.task = SemSampleTaskA;

  ctsk.tskatr = TA_HLNG | TA_RNG0;

  ctsk.stksz = 512;

  ctsk.bufptr = NULL;

  ctsk.itskpri = 24;

  TaskID_A = tk_cre_tsk(&ctsk);

  if(TaskID_A< E_OK)

  {

    ercd = TaskID_A;

    return ercd;

  }

 

  ctsk.itskpri = 26;

  ctsk.stksz = 256;

  ctsk.task = SemSampleTaskB;

  TaskID_B = tk_cre_tsk(&ctsk);

  if(TaskID_B < E_OK)

  {

    ercd = TaskID_B;

    return ercd;

  }

 

  ctsk.itskpri = 28;

  ctsk.stksz = 256;

  ctsk.task = SemSampleTaskC;

  TaskID_C = tk_cre_tsk(&ctsk);

  if(TaskID_C < E_OK)

  {

    ercd = TaskID_C;

    return ercd;

  }

 

  //创建一个信号量

  csem.exinf = NULL;

  csem.isemcnt = 4;

  csem.maxsem = 4;

  csem.sematr = TA_TFIFO | TA_FIRST;

  semid = tk_cre_sem(&csem);

  if(semid < E_OK)

  {

    ercd = semid;

    return ercd;

  }

  SemSamplePutCnt();

 

  //启动任务B

  tk_sta_tsk(TaskID_B,5);

 

    return TRUE;

}

 

 

void SemSampleTaskA(W stacd,VP exinf)

{

  ER ercd = E_OK;

  T_RSEM rsem;

  while(1)

  {

    tm_putstring((UB*)"任务A开始申请3个信号量\n");

    SemSamplePutCnt();

    ercd = tk_wai_sem(semid,3,500);

   

    SemSamplePutCnt();

    if(ercd == E_OK)

    {

      LEDTog(LED1);

      LEDTog(LED2);

      LEDTog(LED3);

    }

   

    tm_putstring((UB*)"任务A开始释放3个信号量\n");

    tk_sig_sem(semid,3);

    SemSamplePutCnt();

   

    tk_slp_tsk(500);

  }

 

}

 

void SemSampleTaskB(W stacd,VP exinf)

{

  ER ercd = E_OK;

  T_RSEM rsem;

 

  ercd = tk_sta_tsk(TaskID_C,0);

 

  SemSamplePutCnt();

  tk_wai_sem(semid,4,-1);

  ercd = tk_sta_tsk(TaskID_A,0);

  if(E_OK == ercd)

  {

    tm_putstring((UB*)"Start TaskA sucessfuly.\n");

  }

  else

  {

    tm_putstring((UB*)"TaskB Failed start Task A.\n");

  }

  tk_sig_sem(semid,4);

  //任务循环

  while(1)

  {

    //任务B需要4个信号量

 

    SemSamplePutCnt();

    tm_putstring((UB*)"任务B开始申请4个信号量\n");

 

    if(tk_wai_sem(semid,4,-1) == E_OK)

    {

      tm_putstring((UB*)"任务B成功获得4个信号量\n");

      SemSamplePutCnt();

      LEDTog(LED1);

      LEDTog(LED2);

      LEDTog(LED3);

      LEDTog(LED4);

      if(tk_sig_sem(semid,4) == E_OK)

      {

        tm_putstring((UB*)"任务B成功释放4个信号量\n");

      }

      else

      {

        tm_putstring((UB*)"任务B释放信号量失败\n");

      }

    }

    else

    {

      tm_putstring((UB*)"任务B获取信号量失败\n");

    }

    tk_slp_tsk(200);

   }

}

 

//防止任务全部休眠导致OS退出的空闲任务

void SemSampleTaskC(W stacd,VP exinf)

{

  B b;

  while(1)

  {

    tm_putstring((UB*)"this is in Task C\n");

    for(b = 0;b<200;b++);

  }

}

 

实验时串口输出信息:

 

----------------------------------------------------

        micro Tenux Version 1.6.00(build 0180)     

            Supported MCU is ST STM32F407VG        

  Copyright(c) 2008-2013 by Dalian uLoong Co.,Ltd. 

----------------------------------------------------

 

当前的可用信号量数为: 4

当前的可用信号量数为: 4

任务A开始申请3个信号量

当前的可用信号量数为: 0

Start TaskA sucessfuly.

当前的可用信号量数为: 1

任务A开始释放3个信号量

当前的可用信号量数为: 4

当前的可用信号量数为: 4

任务B开始申请4个信号量

任务B成功获得4个信号量

当前的可用信号量数为: 0

任务B成功释放4个信号量

this is in Task C

当前的可用信号量数为: 4

任务B开始申请4个信号量

任务B成功获得4个信号量

当前的可用信号量数为: 0

任务B成功释放4个信号量

当前的可用信号量数为: 4

任务B开始申请4个信号量

任务B成功获得4个信号量

………………………………………………….

 

实验总结:

1、当高优先级的任务获得足够的信号量时,立即中断当前任务开始执行。

2、要在系统中添加优先级最低的空闲任务,防止系统任务全部休眠。

3、任务属性sematr的设置:

         TA_TFIFO任务按FIFO的顺序排队

TA_TPRI 任务按优先级顺序排队

TA_FIRST队列中第一个任务最先获得资源

TA_CNT 请求越少的任务越先获得资源

4、谁先获得信号量,由OS根据sem的属性进行调度。

实验代码及输出

实验2.rar

 ------------------------------------------华------丽------的------分-----隔---符---------------------------------------

 2013.06.25

实验3:事件标志

 

事件结构体:

typedef    struct t_cflg {

         VP                  exinf;                         /* 事件标志扩展信息*/

         ATR                 flgatr;                        /* 事件标志的属性*/

         UINT                iflgptn;                 /* 事件标志的初始值 */

         UB                  dsname[8];               /* 对象名,没啥用 */

} T_CFLG;

 

其中事件属性flgatr,可设置为以下的一个或多个值:

TA_TFIFO 任务按FIFO的顺序排队

TA_TPRI 任务按优先级顺序排队

TA_WSGL 不允许等待多个任务(等待一个任务)

TA_WMUL 允许等待多个任务(等待多个任务)

TA_DSNME 设定DS对象名

OS根据事件标志的属性对使用这个事件标志的任务进行调度。


或等待:等待任意一个条件满足,与等待:等待所有条件满足

 

我的理解,事件标志就是一个二进制数。因为事件标志本身就是个UINT类型的数字。设置事件标志就是将这个数的某一位置位,清除事件标志就是将这个数的某一位清零。同一个事件标志有多个位,多个任务可以同时使用一个事件标志。

 

创建事件标志,OS会建立一个事件标志的控制块,用于调度。创建事件标志的时候,会为这个时间标志设置个初始值:iflgtn。与任务和信号量相同,FlgID只是这个事件标志的代表,并不是事件标志的值。

 

事件标志的属性中如果TA_WMUL设置为1,那么就允许多个任务同时等待一个事件标志。当一个事件标志被设置时,如果多个任务都满足条件,此时这些任务都会被释放。进入等待状态,有系统调度决定哪个先执行哪个后执行。

 

实验描述:

创建一个事件标志:FlgIDFlgID事件标志的初始值设置为1. 如果初始值iflgtn被设置为0x02,那么TaskB将先执行一遍循环,然后进入等待后TaskA开始执行。

 

创建两个优先级相同的任务:EventflagSampleTaskATaskAEventflagSampleTaskBTaskB

创建任务后启动TaskB,在TaskB中启动TaskA。此时由于TaskB的优先权较大,TaskB继续执行,遇到等待事件标志时候停止执行。此时TaskA开始执行。

TaskA首先为TaskB设置事件标志(0x02),然后申请事件标志Flag0x01。条件满足,继续执行清除这个事件标志(0x01)。循环执行,重新等待事件标志,此时事件标志没有被设置,所以TaskA进入休眠状态。TaskB继续执行。

TaskB获得优先权开始执行,完成FlagID0x02)的清除,之后继续等待资格事件。当然,同样没等到进入休眠,任务A继续。

如此往复。。。

 

【实验代码和输出】

实验三:事件标志.rar


【事件标志实验总结】

事件标志应该是为了等待两个任务同步而设立的。每个任务完成一定的工作之后,通过事件标志通知另一个任务可以执行了。从而使得两个任务之间协调完成一项工作。

 ------------------------------------------华------丽------的------分-----隔---符---------------------------------------


实验4:邮箱

每个邮箱都包含一个用来发送消息的消息队列和一个用于等待接收消息的任务队列

 

关于void型指针  VP

创建邮箱时,需要提供一个邮箱消息的指针T_MSG

这个指针指向存放消息的地址。查看定义可发现,他是这样定义的:

typedef struct t_msg {

         VP                  msgque[1];               /* Area for message queue */

} T_MSG;

VP的定义是:

typedef void                 *VP;

这里就用到了一个void型的指针。在示例程序中,将自己定义的消息类型强制转换成T_MSG型的指针。即void型的指针。

Void类型的指针是无类型的指针,可以指向任何结构。因为它就是一个指针!

在引用这个指针时候,需要预先知道这个指针指向地址的结构。不然编译器无法知道所指数据的类型而报错

这样的消息传递,使得mbx所传递的消息类型不再固定。需要使用者手动定义。

 

发送消息时,发送任务不会进入等待状态。即:只要把发送的消息往邮箱已仍就可以了,至于消息到哪里去了,交给OS处理了。当邮箱是空的时候,消息直接进邮箱。邮箱不空,消息进入该邮箱的消息队列。这些,发送任务都不关心。

 

接收消息时候,使用tm_rcv_mbx完成。提供邮箱ID和消息数据包起始地址,以及一个等待超时时间。如果要接收的邮箱没有消息,那么接收任务进入等待状态。

 

问题,关于邮箱发送消息和接收消息函数中使用的空类型指针void*

两个函数的定义是这样的:

ERercd= tk_snd_mbx(IDmbxid,T_MSG*pk_msg);

ERercd= tk_rcv_mbx(IDmbxid,T_MSG**ppk_msg,TMOtmout);

其中T_MSG的定义如下:

typedef struct t_msg {

         VP  msgque[1];    /* Area for message queue */

} T_MSG;

其中VP就是 typedef void *VP;

也就是说,T_MSG这个结构本身包含了一个空类型的指针

下面是我对这里使用空类型指针的理解,求鉴定。

先明确的地方:消息传递中,传递的只是地址

1tk_snd_mbx使用T_MSG*

    T_MSG* 是指向消息结构的指针,与msgque是相同的。这个指针是一个void*类型的指针。可以指向任意类型的数据。

    所以tk_snd_mbx(IDmbxid,T_MSG*pk_msg);

这种用法,就是要向函数传递一个空类型的指针。

2tk_rcv_mbx中的使用。

这里是一个指向指针的指针(T_MSG** ppk_msg)。关于ppk_msg内核规范中有这么个说明:ppk_msg是包含消息的数据包(包括消息头在内)的起始地址。

    也就是说ppk_msg是一个地址(地址1)。* ppk_msg是就是地址中的数据,这个数据也是个地址(地址2)。于是T_MSG** ppk_msg 就是地址2中存储的数据。这个数据是T_MSG类型的。(不知对错,求鉴定)

    在示例工程中,有这么个用法:

    tk_rcv_mbx(MbxID_S,(T_MSG**)&pk_rcvmsg,-1);

    其中pk_rcvmsg 的定义是这样的:U_MSG *pk_rcvmsg;  也是个指针。

那么&pk_rcvmsg,就是取这个指针的地址。(T_MSG**)&pk_rcvmsg就成了将地址pk_rcvmsg中存储的地址所指向的数据块,强制转换成T_MSG*类型。

刚好与函数定义部分相同。


【实验描述】

参考示例代码。首先创建两个任务TaskATaskB优先级分别为2425,两个邮箱Mbx_SMbx_R。之后启动TaskA。在TaskA中启动TaskB。然后向Mbx_S发送一个消息,然后开始等待Mbx_R中的消息。由于Mbx_R中没有消息,TaskA进入休眠。

此时TaskB开始执行,TaskB先从Mbx_S中取消息,然后输出消息。最后再向Mbx_R中发送消息。Mbx_R中有了消息,TaskA释放等待进入Ready就绪状态。TaskA优先级高于TaskB,抢占TaskB开始执行,继续完成以后的消息处理代码

【代码和输出】

实验4:邮箱.rar





高工
2013-06-13 00:37:22     打赏
8楼
支持,哈哈哈

高工
2013-06-13 00:57:24     打赏
9楼

实验5:互斥体

关于互斥体,最经典的有两个例子。一个是串口的使用,另一个是打印机的使用。

串口在发送字符串的时候,是不能被中断的。比如发送一个字符串abcdefg,如光刚发完abc时候搞优先级的任务来了,要使用串口发送123456789。这时候如果高优先级任务抢占了低优先级任务对MCU的控制,那么接收端收到的字符串将会是这样的:abc123456789defg。接收到的数据被中断搞混乱了。

再有就是打印机,当低优先级任务在打印时候,刚打印完半页纸,搞优先级任务来了。下半页纸上打出来的将是高优先级任务的数据。整个一张纸上既不是高优先级的东西也不是低优先级任务的东西。

于是就出来了一个互斥体的概念。我使用资源的时候,设置个互斥体将资源锁住,表明正在使用的资源不能被抢断。不管你就绪的任务优先级有多高都得等我用完。

uTenux的互斥体就是一个资源锁。用于确保使用资源的任务不被抢断。

使用优先级继承或者优先级置顶的方法来避免优先级反转。这两种方式,用户不用管,OS已经完全搞定。用户只需要在创建互斥体的时候,告诉OS使用哪种方法即可。

uTenux的互斥体都有一个状态和一个等待锁定互斥体的任务队列

 

问题:关于创建互斥体函数中的PRI ceilpri 互斥体顶置优先级

函数说明中只有这么一句:仅当TA_CEILING被设置时,ceilpri才有效,才可以设置互斥体的顶置优先级。即使用优先级顶置协议时ceilpri才有效。

我的问题是,ceilpri究竟该怎样设置?设置成所有使用这个互斥他中的最高优先级吗?如果设置的低了会怎样?比如有一个优先级为10的任务使用这个互斥他,而我将ceilpri设置成15.

解答:实验结果是任务的优先级不变化,仍旧保持原有的较高优先级

我想,这里这么用虽然不对,但是也不至于导致系统崩溃。保持原有优先级,虽然可能导致优先级反转的问题,但是系统依旧能够运行。这个算是做软件时候的一个BUG了。

 

【实验描述】

首先创建任务TaskATaskB和互斥体MtxIDTaskA TaskB优先级相同,都是20。然后启动TaskA

TaskA中首先启动TaskB。两个任务优先级相同,所以此时TaskA具有较高的优先权。TaskB进入就绪等待状态。TaskA继续执行,进入循环体。

在循环体中,TaskA首先输出自己当前的优先级,然后锁定Mtx并延时一段时间。最后解锁互斥体。

TaskA解锁互斥体之后,TaskB获得优先权,开始执行。同样TaskB也是先输出优先级然后锁定Mtx之后再次输出优先级。最后解锁互斥体。

这个实验使用了支持优先级顶置协议,即任务锁定互斥体之后会将自己的优先级提升到创建互斥体时候设置的级别。

使用优先级继承协议在本次实验也有验证,结果是当低优先级任务获得mtx之后并不会被立即提升的优先级,而是当有较高优先级任务请求这个mtx时才会提升优先级。这个实验我在代码中也有

【实验代码和输出】

实验5:互斥体.rar


实验6:消息缓冲区

uTenux的消息缓冲区是一个通过传递大小可变的消息来实现同步和通信的对象。

消息缓冲区由三部分组成:1、待发消息队列  2、等接收消息的任务队列  3、用来保存缓冲消息的空间。

和信号相比,消息队列能够传递更多的信息。与管道相比,消息队列提供了有格式的数据,这可以减少开发人员的工作量。但消息队列仍然有大小限制。

uTenux中当缓冲区空时,接收消息的任务进入等待状态。当缓冲区满时,发送消息的任务进入等待状态。

每个消息只能接收一次,消息被接收接收后消息从消息缓冲区中删除。

 

使用tk_snd_mbf发送消息到消息缓冲区时,由于发送的消息字提供了VP型的消息头指针和消息长度。接收数据时,只有OS知道数据的长度。至于数据内部结构谁都不知道,因此必须消息的收发双方必须事先约定好消息类型,接收方根据消息数据的类型准备接收到消息的存放空间。关于VP指针(void*)前文已详诉,这里就不唠叨了。关于VP类型,只需要记住一点即可:

Void类型的指针是无类型的指针,可以指向任何结构。因为它就是一个指针!

在引用这个指针时候,需要预先知道这个指针指向地址的结构。不然编译器无法知道所指数据的类型而报错

 

与邮箱不同,消息缓冲传递的不是地址而是实实在在的将消息复制到缓冲区

基于这个特性,我觉得消息缓冲区更适合处理大量数据的传输。比如usart接收数据的处理。Usart每次接收到数据,直接丢到缓冲去即可。uasrt后来接收到的数据不会覆盖上次的数据,OS只管根据自己的需要处理下缓冲去消息即可。

比如对于STM32F4USART的数据寄存器地址是(0x40001004),如果是按照邮箱的方式来处理数据。只需要将这个地址0x40001004传递给邮箱,接收消息的任务结束时候,只需要从这个地址取出来数据即可。至于中间这个数据是否发生了变化,接收端不知道。

而使用消息队列的时候却是首先将地址0x40001004中的数据,复制到消息队列中。任务从消息队列中取消息的时候,也不会关心原数据的地址。(感觉我这样分析像数据队列了)

 

消息缓冲区的SVC

tk_cre_mbf  mbf = message buffer)创建消息缓冲区

消息队列的各种属性在设置时设置。其中mbfatr消息缓冲区的属性的设置如下;

TA_TFIFO          等待发送的任务按FIFO的数序排队

„   TA_TPRI            等待发送的任务按优先级顺序排队

„   TA_USERBUF    表示任务使用用户指定的区域作为缓冲区

„   TA_DSNAME       设定DS对象名

 

 

一个悲剧的小插曲:

按照习惯性的写法,在接收消息的时候我这样写的:

{

      tm_putstring((UB*)"Task A receive a message.the message is:\n");

      tm_putstring((UB*)msgrcv);

    }

    else

    {

      tm_putstring((UB*)"Task A Failed receive the message\n");

      PutErcd(ercd);

    }

然后发现TaskA死活收不到消息,这就怪了。一直检查发送部分,没出错。最后看手册才注意到:返回值是接收的消息大小(字节数)或错误编码。这样写即使受到消息了也不会正常显示。。。

不仔细看手册函数说明害死人啊!

 

【实验描述】

首先创建两个任务TaskATaskB,优先级分别为1820.然后创建两个消息队列。

启动任务TaskA,在TaskA中启动TaskB

TaskAMbfID_1发送消息,然后去等待MbfID_2中的消息。此时MbfID_1中没消息,TaskA进入休眠状态。任务TaskB开始执行。

TaskB首先接收MbfID_1中的消息,通过串口显示出来后再向MbfID_2中发送消息。发送完成后,TaskA释放等待条件满足,立即抢断TaskB。开始执行剩下的代码和循环。

如果没有上面的小插曲,这个实验是相当低简单。可惜疏忽了一下,费了一晚上时间才搞定。

【实验代码及输出】

 实验6:消息缓冲.rar

【附加实验】

在上述消息队列中,一次放置多个消息。

如果消息缓冲空间足够,发送多个消息跟发送一个消息,对发送发来说没什么区别。当消息缓冲快满的时候,容纳不下新消息,发送任务就会进入等待状态。直到缓冲区有足够空间。

但是接收第二条消息时候一定要记得把用于存放消息的内存块先清空了。否则的话,会将上一条信息没有被覆盖掉的东西输出来。

 



高工
2013-06-13 00:58:25     打赏
10楼


2013.06.28

实验7:集合点端口实验

这个是头一次接触的概念。比较不好理解。内核规范中的说明就要20页!

看了王总写的uTenux内核规范之后,有那么一点明白了但理解不深。

集合点端口就像每次工作前的收集情况会。首长下达收集情况指令,各个部门聆听。当给某个部门下达指令之后,这个部门开始工作。并返回信息。所有部门都会报完信息之后,首长心里就有数了:今天天气不错,昨天的机器故障排除了,原料到位。可以开工了!于是,一天的生产工作就此开始。各个部门反馈完情况,就自由活动了。没事唠嗑都行。。

这里的首长,就是创建这个集合点端口的任务。各个部门就是被集合点召集的任务。他们反馈的情况就是被调用任务的反馈。反馈完情况的任务,进入下一个循环周期。

先按照这个理解做了实验再说!

 

【实验描述】

创建两个任务一个集合点端口PorID。之后启动TaskA

TaskA中启动TaskB,进入循环。

循环体开始时候,TaskA调用集合点端口,准备创建集合点。由于条件不满足,TaskA进入休眠,TaskB开始执行

TaskB,首先接受集合点端口调用,然后回复调用信息。

此时TaskA创建集合点条件满足,继续执行。将接收到的信息输出。

【代码及输出】

 实验7:集合点端口.rar

实验不足处

由于不太理解集合点端口中消息传输机制,接收消息到的消息最后部分会出现乱码。使用固定长度消息暂时避免了这个问题,即发送和接收消息长度相同,而且等于集合点端口接收和发送的最大消息数。有待改进

------------------------------------------华------丽------的------分-----隔---符---------------------------------------

2013.06.29



实验8:固定内存池管理

内存管理是操作系统的一个基础功能。uTenux的内存池管理函数提供了基于软件的内存池管理和内存块分配管理。uTenux的内存池有固定大小的内存池和大小可变的内存池之分,它们被看成是互相独立的对象,需要不同的系统调用集来进行操作。

内存池管理函数管理的内存全部包含在系统空间以内。

固定尺寸内存池实验

固定尺寸内存池是一个用来动态管理固定尺寸内存块的对象。每个固定尺寸内存池都有一个用作固定尺寸内存池的内存空间(简称为内存池区)和一个等待内存块分配的任务队列。

uTenux提供了五个供操作固定内存池的API,分别用于创建、删除、获取、释放、查询内存池状态。

创建固定尺寸内存池,需要提供一个T_CMPF类型的结构体。定义如下:

typedef    struct t_cmpf {

         VP                  exinf;                   /* 扩展信息 */

         ATR                 mpfatr;                     /* 内存池属性 */

         W                   mpfcnt;                   /* 创建的内存池块数 */

         W                   blfsz;                        /* 每块的大小(byte) */

         UB                  dsname[8];               /* Object name */

         VP                  bufptr;                      /* User buffer */

} T_CMPF;

内存池属性mpfatr是的定义如下:

mpfatr:=(TA_TFIFO||TA_TPRI)|TA_USERBUF|TA_DSNAME|(TA_RNG0||TA_RNG1

||TA_RNG2||TA_RNG3)

TA_TFIFO         等待内存分配的任务按FIFO的顺序排队

TA_TPRI           等待内存分配的任务按优先级顺序排队

TA_RNGn         内存访问权设置成保护级别n

TA_USERBUF 指示系统使用用户指定的内存空间

TA_DSNAME   设定DS对象名

有余uTenux没有内存保护功能,TA_RNGn 只能是TA_RNG0TA_USERBUF是不使用OS提供的内存空间时候使用的。TA_DSNAME基本没什么用。

 

获取内存池函数:ER ercd= tk_get_mpf(ID mpfid,VP *p_blf,TMO tmout);中,p_blf为获取的内存块的起始地址。为VP类型,即一个void*的指针。使用时需要注意。

【实验描述】

1、创建两个任务一个包含5块区域的内存池。

2、启动TaskB,在TaskB中启动TaskATaskA开始执行就立即休眠。等待TaskB唤醒

3TaskB继续执行。先输出当前可用内存块数目,申请一块内存后再次输出内存块数目。

4、最后将一段数据放入这块内存。之后唤醒TaskA

5TaskA输出由TaskB放入内存块的数据之后,释放内存块。再次输出可用内存块数。。然后进入休眠状态,TaskB继续执行。

【代码及输出】

 实验8:固定内存池.rar

------------------------------------------华------丽------的------分-----隔---符---------------------------------------

实验9:可变内存池管理

预备知识:C语言中的内存块操作函数

1memcpy
原型:void *memcpy(void *dest, const void *src, size_t n);
功能:从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中
2
memmove

原型:void *memmove( void* dest, const void* src,size_tcount );

头文件:<string.h>

功能:src所指内存区域复制count个字节到dest所指内存区域。

说明memmove用于从src拷贝count个字符到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。但复制后src内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。

3memcmp
int memcmp(const void *buf1, const void *buf2, unsigned int count);
比较内存区域buf1buf2的前count个字节。

buf1<buf2时,返回值<0

buf1=buf2时,返回值=0

buf1>buf2时,返回值>0

4memchr

原型:extern void *memchr(const void *buf, int ch, size_t count);

用法:#include <string.h>

功能:buf所指内存区域的前count个字节查找字符ch

说明:当第一次遇到字符ch时停止查找。如果成功,返回指向字符ch的指针;否则返回NULL

5memset

void *memset(void *s, int ch, size_t n);

函数解释:s中前n个字节(typedef unsigned int size_t)用 ch 替换并返回s

作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法

 

可变尺寸内存池是一个用来动态管理任何大小的内存块的对象。与固定尺寸内存池一样,每个可变尺寸内存池都有一个用作可变尺寸内存池的内存空间和一个等待内存块分配的任务队列。

可变尺寸内存池管理SVC

创建可变尺寸内存池:tk_cre_mpl 

需要提供参数T_CMPL的定义如下:

typedef     struct t_cmpl {

          VP                  exinf;                         /* 扩展属性*/

          ATR                 mplatr;                     /* 内存池属性 */

          W                   mplsz;                      /* 内存池的大小 (byte) */

          UB                  dsname[8];               /* Object name */

          VP                  bufptr;                      /* User buffer */

} T_CMPL;

内存池属性的定义:

mplatr:=(TA_TFIFO||TA_TPRI)|TA_USERBUF|TA_DSNAME|(TA_RNG0||TA_RNG1||TA_RNG2

||TA_RNG3)

TA_TFIFO         等待内存分配的任务按FIFO的顺序排队

TA_TPRI           等待内存分配的任务按优先级顺序排队

TA_RNGn         内存访问权设置成保护级别n

TA_USERBUF 指示系统使用用户指定的内存空间

TA_DSNAME   设定DS对象名

 

可以发现,uTenux中各个对象的属性定义是很相似的:

1、两种排队顺序TA_TFIFOTA_TPRI

2、内存保护级别TA_RNGn  必须是TA_RNG0

3TA_DSNAME对象名

4TA_USERBUF 用户缓存

之后就不列出来这东西了。

至于申请,释放都比较简单,这里不一一列出了。

【实验描述】

本实验参考源码包中的09.mempoolv

1、首先创建两个人物一个大小512字节的内存池。然后启动任务A,任务A自动休眠等待唤醒。之后启动任务B

2、任务B首先输出可用内存数,之后申请128Byte内存,再次输出可用内存数。

3、任务B将申请到的内存清空,将数据放入。之后唤醒任务A

4、任务A优先级高,开始执行。

5、任务A首先输出任务B放入内存块的数据,任何释放这个内存块。

6、最后任务A再次休眠,任务B开始循环执行。

【实验代码和输出】

实验9:可变大小内存池.rar



共44条 1/5 1 2 3 4 5 ›| 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]