FreeRTOS+LWIP

更新时间:2024-05-31 13:15:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

FreeRTOS与LWIP的移植

1 FreeRTOS任务管理

1-1任务函数

任务是由C 语言函数实现的。唯一特别的只是任务的函数原型,其必须返回void,而且带有一个void 指针参数(void ATaskFunction( void *pvParameters );)。每个任务都是在自己权限范围内的一个小程序。其具有程序入口,通常会运行在一个死循环中,也不会退出。

FreeRTOS 任务不允许以任何方式从实现函数中返回——它们绝不能有一 条”return”语句,也不能执行到函数末尾。如果一个任务不再需要,可以显式地将其删除。

一个任务函数可以用来创建若干个任务——创建出的任务均是独立的执行实例,拥有属于自己的栈空间,以及属于自己的自动变量(栈变量),即任务函数本身定义的变量。 例:

void ATaskFunction( void *pvParameters )

{

/* 可以像普通函数一样定义变量。用这个函数创建的每个任务实例都有一个属于自己的

iVarialbleExample变量。但如果iVariableExample被定义为static,这一点则不成立– 这种情况下只存在一个变量,所有的任务实例将会共享这个变量。*/ int iVariableExample = 0;

/* 任务通常实现在一个死循环中。*/ for( ;; ) {

/* 完成任务功能的代码将放在这里。*/ }

/* 如果任务的具体实现会跳出上面的死循环,则此任务必须在函数运行完之前删除。传入NULL参数表示删除

的是当前任务*/ vTaskDelete( NULL ); }

1-2创建任务

创建任务使用FreeRTOS 的API 函数xTaskCreate()。 接下来描述用到的数据类型和命名约定。

xTaskCreate() API 函数原型如下:

portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode,

const signed portCHAR * const pcName, unsigned portSHORT usStackDepth, void *pvParameters,

unsigned portBASE_TYPE uxPriority,

xTaskHandle *pxCreatedTask );

参数名 pdTASK_CODE pcName usStackDepth 表1 xTaskCreate()参数与返回值

含义描述 任务只是永不退出的C 函数,实现常通常是一个死循环。参数pvTaskCode 只一个指向任务的实现函数的指针(效果上仅仅是函数名)。 具有描述性的任务名。这个参数不会被FreeRTOS 使用。其只是单纯地用于辅助调试。识别一个具有可读性的名字总是比通过句柄来识别容易得多。 当任务创建时,内核会分为每个任务分配属于任务自己的唯一状态。usStackDepth值用于告诉内核为它分配多大的栈空间。 这个值指定的是栈空间可以保存多少个字(word),而不是多少个字节(byte)。比如说,如果是32 位宽的栈空间,传入的usStackDepth值为100,则将会分配400字节的栈空间(100 * 4bytes)。栈深度乘以栈宽度的结果千万不能超过一个size_t类型变量所能表达的最大值。 应用程序通过定义常量configMINIMAL_STACK_SIZE 来决定空闲任务任用的栈空间大小。在FreeRTOS 为微控制器架构提供的Demo 应用程序中,赋予此常量的值是对所有任务的最小建议值。如果你的任务会使用大量栈空间,那么你应当赋予一个更大的值。没有任何简单的方法可以决定一个任务到底需要多大的栈空间。计算出来虽然是可能的,但大多数用户会先简单地赋予一个自认为合理的值,然后利用FreeRTOS提供的特性来确证分配的空间既不欠缺也不浪费。 pvParameters uxPriority 任务函数接受一个指向void 的指针(void*)。pvParameters的值即是传递到任务中的值。这篇文档中的一些范例程序将会示范这个参数可以如何使用。 指定任务执行的优先级。优先级的取值范围可以从最低优先级0到最高优先级(configMAX_PRIORITIES–1)。 pxCreatedTask 用于传出任务的句柄。这个句柄将在API调用中对该创建出来的任务进行引用,比如改变任务优先级,或者删除任务。如果应用程序中不会用到这个任务的句柄,则pxCreatedTask 可以被设为NULL。 返回值 有两个可能的返回值: 1. pdTRUE表明任务创建成功。 2. errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY由于内存堆空间不足,FreeRTOS无法分配足够的空间来保存任务结构数据和任务栈,因此无法创建任务。 1-3任务优先级

xTaskCreate()API函数的参数uxPriority为创建的任务赋予了一个初始优先级。优先级可以在调度器启动后调用vTaskPrioritySet()API函数进行修改。任意数量的任务可以共享同一个优先级——以保证最大设计弹性。低优先级号表

示任务的优先级低,优先级号0表示最低优先级。调度器保证总是在所有可运行的任务中选择具有最高优先级的任务,并使其进入运行态。如果被选中的优先级上具有不止一个任务,调度器会让这些任务轮流执行。两个测试任务被创建在同一个优先级上,并且一直是可运行的。所以每个任务都执行一个“时间片”,任务在时间片起始时刻进入运行态,在时间片结束时刻又退出运行态。

调度器总是在可运行的任务中,选择具有最高优级的任务,并使其进入运行态。要能够选择下一个运行的任务,调度器需要在每个时间片的结束时刻运行自己本身。一个称为心跳(tick,有些地方被称为时钟滴答,本文中一律称为时钟心跳)中断的周期性中断用于此目的。时间片的长度通过心跳中断的频率进行设定,心跳中断频率由FreeRTOSConfig.h 中的编译时配置常configTICK_RATE_HZ 进行配置。比如说,如果configTICK_RATE_HZ设为100(HZ),则时间片长度为10ms。

API 函数vTaskPriofitySet()可以用于在调度器启动后改变任何任务的优先级。

表2 vTaskPrioritySet() 参数 参数 含义描述 被修改优先级的任务句柄(即目标任务)——参考xTaskCreate() API函数的参数pxTask pxCreatedTask 以了解如何得到任务句柄方面的信息。任务可以通过传入NULL 值来修改自己的优先级。 uxNewPriority 目标任务将被设置到哪个优先级上。如果设置的值超过了最大可用优先级(configMAX_PRIORITIES – 1),则会被自动封顶为最大值。常量configMAX_PRIORITIES 是FreeRTOSConfig.h 头文件中设置的一个编译时选项。 1-4调度算法 优先级抢占式调度

?

? ? ?

每个任务都赋予了一个优先级。

每个任务都可以存在于一个或多个状态。

在任何时候都只有一个任务可以处于运行状态。

调度器总是在所有处于就绪态的任务中选择具有最高优先级的任务来执行。 这种方法被称为“固定优先级抢占式调度”,所谓“固定优先级”是指每个任务都被赋予了一个优先级,这个优先级不能被内核本身改变(只能被任务修改)。”抢占式”是指当任务进入就绪态或是优先级被改变时,如果处于运行态的任务优先级更低,则该任务总是抢占当前运行的任务。

任务可以在阻塞状态等待一个事件,当事件发生时其将自动回到就绪态。时间事件发生在某个特定的时刻,比如阻塞超时。时间事件通常用于周期性或超时行为。任务或中断服务例程往队列发送消息或发送任务一种信号量,都将触发同步事件。同步事件通常用于触发同步行为,比如某个外围的数据到达了。

下图为某个应用程序的执行流程展现了抢占式调度的行为方式。

图1执行流程中的主要抢占点

1. 空闲任务

空闲任务具有最低优先级,所以每当有更高优先级任务处于就绪态是,空闲任务就会被抢占——如图中t3, t5 和t9 时刻。 2. 任务3

任务3 是一个事件驱动任务。其工作在一个相对较低的优先级,但优先级高于空闲任务。其大部份时间都在阻塞态等待其关心的事件。每当事件发生时其就 从阻塞态转移到就绪态。FreeRTOS 中所有的任务间通信机制(队列,信号量等) 都可以通过这种方式用于发送事件以及让任务解除阻塞。事件在t3,t5 以及t9 至t12 之间的某个时刻发生。发生在t3 和t5 时刻的事件可以立即被处理,因为这些时刻任务3 在所有可运行任务中优先级最高。发生在t9 至t12 之间某个时刻的事件不会得到立即处理,需要一直等到t12 时刻。因为具有更高优先级的任务1 和任务2 尚在运行中,只有到了t12 时刻,这两个任务进入阻塞态,使得任务3 成为具有最高优先级的就绪态任务。 3. 任务2

任务2 是一个周期性任务,其优先级高于任务3 并低于任务1。根据周期间隔,任务2 期望在t1,t6 和t9 时刻执行。在t6 时刻任务3 处于运行态,但是任务2 相对具有更高的优先级,所以会抢占任务3,并立即得到执行。任务2 完成处理后,在t7 时刻返回阻塞态。同时,任务3 得以重新进入运行态,继续完成处理。任务3 在t8 时刻进入阻塞状态。 4. 任务1

任务1 也是一个事件驱动任务。任务1 在所有任务中具有最高优先级,因此可以抢占系统中的任何其它任务。在图中看到,任务1 的事件只是发生在在t10时刻,此时任务1 抢占了任务2。只有当任务1 在t11 时刻再次进入阻塞态之后,任务2 才得以机会继续完成处理。

选择任务优先级

单调速率调度(Rate Monotonic Scheduling, RMS)是一种常用的优先级分配

技术。其根据任务周期性执行的速率来分配一个唯一的优先级。具有最高周期执行频率的任务赋予高最优先级;具有最低周期执行频率的任务赋予最低优先级。这种优先级分配方式被证明了可以最大化整个应用程序的可调度性

(schedulability),但是运行时间不定以及并非所有任务都具有周期性,会使得对这种方式的全面计算变得相当复杂。

协作式调度

采用一个纯粹的协作式调度器,只可能在运行态任务进入阻塞态或是运行态任务显式调用taskYIELD()时,才会进行上下文切换。任务永远不会被抢占,而具有相同优先级的任务也不会自动共享处理器时间。协作式调度的这作工作方式虽然比较简单,但可能会导致系统响应不够快。

实现混合调度方案也是可行的,这需要在中断服务例程中显式地进行上下文切换,从而允许同步事件产生抢占行为,但时间事件却不行。这样做的结果是得到了一个没有时间片机制的抢占式系统。或许这正是所期望的,因为获得了效率,并且这也是一种常用的调度器配置。

2 FreeRTOS队列管理

2-1队列的特性

1、数据存储

队列可以保存有限个具有确定长度的数据单元。队列可以保存的最大单元数目被称为队列的“深度”。在队列创建时需要设定其深度和每个单元的大小。

通常情况下,队列被作为FIFO(先进先出)使用,即数据由队列尾写入,从队列首读出。当然,由队列首写入也是可能的。

往队列写入数据是通过字节拷贝把数据复制存储到队列中;从队列读出数据使得把队列中的数据拷贝删除。 2、可被多任务存取

所有任务都可以向同一队列写入和读出。 3、读队列时阻塞

当某个任务试图读一个队列时,其可以指定一个阻塞超时时间。在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。当其它任务或中断服务例程往其等待的队列中写入了数据,该任务将自动由阻塞态转移为就绪态。当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转移为就绪态。

由于队列可以被多个任务读取,所以对单个队列而言,也可能有多个任务处于阻塞状态以等待队列数据有效。这种情况下,一旦队列数据有效,只会有一个任务会被解除阻塞,这个任务就是所有等待任务中优先级最高的任务。而如果所有等待任务的优先级相同,那么被解除阻塞的任务将是等待最久的任务。 4、写队列时阻塞

pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ #endif

LINK_STATS_INC(link.recv); } else {

//drop packet();

LINK_STATS_INC(link.memerr); LINK_STATS_INC(link.drop); }

return p; }

1.5 low_level_output函数

该函数的功能是将pbuf中的数据帧通过底层发送函数ENC28J60_Packet_Send发送出去,由于要发送的数据可能被分割成多个pbuf,而这些pbuf通过pbuf->next指针连接起来,因此low_level_output函数需要使用foe循环将这些pbuf中的数据拷贝至当前的发送缓冲区中。源代码如下: static err_t

low_level_output(struct netif *netif, struct pbuf *p) {

// struct ethernetif *ethernetif = netif->state; struct pbuf *q; int send_len=0;

// initiate transfer(); #if ETH_PAD_SIZE

pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ #endif

for(q = p; q != NULL; q = q->next) {

/* Send the data from the pbuf to the interface, one pbuf at a time. The size of the data in each pbuf is kept in the ->len variable. */

//send data from(q->payload, q->len); memcpy((u8_t*)&lwip_buf[send_len], (u8_t*)q->payload, q->len); send_len +=q->len; }

// signal that packet should be sent();

ENC28J60_Packet_Send(send_len,lwip_buf); #if ETH_PAD_SIZE

pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ #endif

LINK_STATS_INC(link.xmit); return ERR_OK; }

2、 网卡驱动

本次用的是ENC28J60网卡模块。ENC28J60 是带有行业标准串行外设接口

( Serial Peripheral Interface,SPI)的独立以太网控制器。它可作为任何配备有 SPI 的控制器的以太网接口。ENC28J60 符合IEEE 802.3的全部规范,采用了一系列包过滤机制以对传入数据包进行限制。它还提供了一个内部 DMA 模块,以实现快速数据吞吐和硬件支持的IP校验和计算。与主控制器的通信通过两个中断引脚和SPI 实现,数据传输速率高达10Mb/s。

ENC28J60由七个主要功能模块组成:

1、 SPI接口——充当主控制器和ENC28J60之间通信通道。 2、 控制寄存器|——用于控制和监视ENC28J60. 3、 双端口RAM缓冲器——用于接收和发送数据包。 4、 判优器——当DMA、发送和接收模块发出请求是对RAM缓冲器的访问进行控制。 5、 总线接口——对通过SPI接收的数据和命令进行解析。

6、 MAC(Medium Access control)模块——实现符合IEEE802.3标准的MAC逻

辑。

7、 PHY(物理层)模块——对双绞线上的模拟数据进行编码和译码。该期间还包

括其他支持模块,诸如振荡器、片内稳压器、电平变换器(提供可以接受5V电压的I/O引脚)和系统控制逻辑。

enc28j60.c:ENC28J60(以太网芯片)SPI的接口应用函数库。

ENC28J60_Reset(void)这个函数里SPI的硬件初始化、设置SPI的时钟SCK的频率和复位ENC28J60.

ENC28J60_Read_Op(u8 op,u8 addr)读取ENC28J60寄存器(带操作码)op:操作码addr:寄存器地址/参数返回值:读到的数据。

ENC28J60_Write_Op(u8 op,u8 addr,u8 data)读取ENC28J60寄存器(带操作码)op:操作码addr:寄存器地址data:参数 返回值 :无返回值(函数为Void类型的)。

ENC28J60_Read_Buf(u32 len,u8* data)读取ENC28J60接收缓存数据len:要读取的数据长度data:输出数据缓存区(末尾自动添加结束符) 返回值 :无返回值(函数为Void类型的)。

ENC28J60_Write_Buf(u32 len,u8* data)向ENC28J60写发送缓存数据len:要写入的数据长度data:数据缓存区 返回值 :无返回值(函数为Void类型的)。

ENC28J60_Set_Bank(u8 bank)设置ENC28J60寄存器Bank

bank:要设置的bank 返回值 :无返回值(函数为Void类型的)。

ENC28J60_Read(u8 addr)读取ENC28J60指定寄存器 addr:寄存器地址返回值:读到的数据

ENC28J60_Write(u8 addr,u8 data)向ENC28J60指定寄存器写数据 addr:寄存器地址data:要写入的数据 返回值 :无返回值(函数为Void类型的)。

ENC28J60_PHY_Write(u8 addr,u32 data)向ENC28J60的PHY寄存器写入数据addr:寄存器地址data:要写入的数据 返回值 :无返回值(函数为Void类型的)。

ENC28J60_Init(u8* macaddr)初始化ENC28J60

macaddr:MAC地址返回值:0,初始化成功;1,初始化失败;

ENC28J60_Get_EREVID(void)在EREVID 内也存储了版本信息。 EREVID 是一个只读控制寄存器,包含一个5 位标识符,用来标识器件特定硅片的版本号

ENC28J60_Packet_Send(u32 len,u8* packet)通过ENC28J60发送数据包到网络len:数据包大小packet:数据包 返回值 :无返回值(函数为Void类型的)。

ENC28J60_Packet_Receive(u32 maxlen,u8* packet)从网络获取一个数据包内容maxlen:数据包最大允许接收长度

packet:数据包缓存区 返回值:收到的数据包长度(字节)

基于FreeRTOS的LWIP协议栈移植

在FreeRTOS操作系统下的LWIP任务模型:

1 操作系统模拟层文件sys_arch.c的移植

在LWIP中,操作系统模拟层是LWIP协议栈的一部分,它存在的目的是方便将LWIP移植到各种不同的操作系统上,它为操作系统和LWIP协议栈之间提供一个接口桥梁,当用户移植LWIP到一个新的操作系统的时候,只需要修改操作系统模拟层内的各函数即可。Sys_arch.txt文件给出了详细说明。总的来说,操作系统模拟层主要完成了与信号量、消息邮箱机制、线程相关的功能。

看如下三个类型定义:

typedef xSemaphoreHandle sys_sem_t;//在LWIP中信号量使用这个类型定义 typedef xQueueHandle sys_mbox_t;//在LWIP中队列消息使用这个类型定义 typedef xTaskHandle sys_thread_t;//在LWIP中线程使用这个类型定义 1. sys_mbox_new函数

该函数的功能是使用FreeRTOS提供的消息队列机制创建一个空的消息队列。在FreeRTOS中,消息队列创建函数是xQueueCreate。创建的邮箱大小由sys_arch.h中的宏定义archMESG_QUEUE_LENGTH实现。具体代码如下:

err_t sys_mbox_new(sys_mbox_t *mbox, int size) {

(void ) size;

*mbox = xQueueCreate( archMESG_QUEUE_LENGTH, sizeof( void * ) ); #if SYS_STATS

++lwip_stats.sys.mbox.used;

if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) {

lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used; }

#endif /* SYS_STATS */ if (*mbox == NULL) return ERR_MEM; return ERR_OK; } 2. sys_mbox_free函数

该函数功能与sys_mbox_new相反,它用于删除一个队列。该队列中还有未被取出的消息时,该函数应当报错,并通知应用程序。代码如下:

void sys_mbox_free(sys_mbox_t *mbox)

{

if( uxQueueMessagesWaiting( *mbox ) ) { /* Line for breakpoint. Should never break here! */ portNOP(); #if SYS_STATS

lwip_stats.sys.mbox.err++; #endif /* SYS_STATS */ // TODO notify the user of failure. }

vQueueDelete( *mbox ); #if SYS_STATS

--lwip_stats.sys.mbox.used; #endif /* SYS_STATS */ }

3. sys_mbox_post函数

该函数用于将消息发送至消息队列中。该函数是一个阻塞函数。当消息被发送至队列后,该函数才退出阻塞状态。代码如下:

void sys_mbox_post(sys_mbox_t *mbox, void *data) {

while ( xQueueSendToBack(*mbox, &data, portMAX_DELAY ) != pdTRUE ){} }

4. sys_mbox_trypost函数

该函数用于尝试将某个消息发送至消息队列中,当消息被成功投递后,则返回成功,否则返回失败。代码如下:

err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg) {

err_t result;

if ( xQueueSend( *mbox, &msg, 0 ) == pdPASS ) {

result = ERR_OK; } else {

// could not post, queue must be full result = ERR_MEM; #if SYS_STATS

lwip_stats.sys.mbox.err++; #endif /* SYS_STATS */ }

return result; }

5. sys_arch_mbox_fetch函数

该函数用于从消息队列中取出一条消息。该函数是一个阻塞函数。调用该函数的线程若未取到消息,则在形参timeout所指定的时间内,该线程被阻塞。当超过timeout所指定的时间后,该线程恢复至就绪状态。若timeout为0,则调用该函数的线程一直被阻塞,直到收到消息。

u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) {

void *dummyptr;

portTickType StartTime, EndTime, Elapsed;

StartTime = xTaskGetTickCount();

if ( msg == NULL ) { msg = &dummyptr; }

if ( timeout != 0 ) { if ( pdTRUE == xQueueReceive( *mbox, &(*msg), timeout / portTICK_RATE_MS ) )

{ EndTime = xTaskGetTickCount(); Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; return ( Elapsed ); } else // timed out blocking for message { *msg = NULL; return SYS_ARCH_TIMEOUT; } }

else // block forever for a message. { while( pdTRUE != xQueueReceive( *mbox, &(*msg), portMAX_DELAY ) ){} // time is arbitrary

EndTime = xTaskGetTickCount(); Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; return ( Elapsed ); // return time blocked TODO test } }

6. sys_arch_mbox_tryfetch函数

该函数尝试从消息队列中取出消息。它是一个非阻塞函数。当去带消息时,则返回成功,否则立即退出,返回“队列空”。具体代码如下:

u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg) {

void *dummyptr; if ( msg == NULL ) { msg = &dummyptr; }

if ( pdTRUE == xQueueReceive( *mbox, &(*msg), 0 ) ) {

return ERR_OK; } else {

return SYS_MBOX_EMPTY; } }

7. sys_sem_new函数

该函数用于创建一个信号量,其中形参count指明了当前信号量的状态。若count为0,则该信号量在创建时就被“取走”(take)了。代码如下:

err_t sys_sem_new(sys_sem_t *sem, u8_t count) {

vSemaphoreCreateBinary(*sem ); if(*sem == NULL) {

#if SYS_STATS

++lwip_stats.sys.sem.err; #endif /* SYS_STATS */ return ERR_MEM; }

if(count == 0) // Means it can't be taken { xSemaphoreTake(*sem,1); }

#if SYS_STATS

++lwip_stats.sys.sem.used;

if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) { lwip_stats.sys.sem.max = lwip_stats.sys.sem.used; }

#endif /* SYS_STATS */

return ERR_OK;

}

8. sys_arch_sem_wait函数

该函数是一个阻塞函数。调用该函数的线程在形参timeout指定的时间内被阻塞。若timeout为0,则调用该函数的线程一直被阻塞,直到等待的信号量被释放。当该函数取到信号量时,它将返回取得该信号量所占用的时间。代码如下:

u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) {

portTickType StartTime, EndTime, Elapsed;

StartTime = xTaskGetTickCount();

if( timeout != 0) { if( xSemaphoreTake( *sem, timeout / portTICK_RATE_MS ) == pdTRUE ) { EndTime = xTaskGetTickCount(); Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; return (Elapsed); // return time blocked TODO test } else { return SYS_ARCH_TIMEOUT; } }

else // must block without a timeout { while( xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE){} EndTime = xTaskGetTickCount(); Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; return ( Elapsed ); // return time blocked } }

9. sys_sem_signal函数

该函数用于释放一个信号量。代码如下: void sys_sem_signal(sys_sem_t *sem) {

xSemaphoreGive(*sem); }

10. sys_sem_free函数

该函数是用于删除一个信号量。代码如下:

void sys_sem_free(sys_sem_t *sem) {

#if SYS_STATS

--lwip_stats.sys.sem.used; #endif /* SYS_STATS */ vQueueDelete(*sem); }

11. sys_thread_new函数

该函数用于创建一个新的线程。其中,形参name指定了该线程的名称,thread是该线程对应的函数,arg是该线程的形参,stacksize指定了该线程对应的堆栈大小,prio则指定该线程的优先级。代码如下:

sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread , void *arg, int stacksize, int prio)

{

xTaskHandle CreatedTask; int result;

if ( s_nextthread < SYS_THREAD_MAX ) {

result = xTaskCreate( thread, ( signed portCHAR * ) name, stacksize, arg, prio, &CreatedTask );

// For each task created, store the task handle (pid) in the timers array. // This scheme doesn't allow for threads to be deleted //s_timeoutlist[s_nextthread++].pid = CreatedTask;

if(result == pdPASS) { return CreatedTask; } else { return NULL; } } else {

return NULL; } }

12. sys_init函数

该函数是操作系统模拟层的初始化函数。它主要对定时器管理数组进行了初始化。代码如下:

sys_init(void)

{

int i;

// Initialize the the per-thread sys_timeouts structures // make sure there are no valid pids in the list for(i = 0; i < SYS_THREAD_MAX; i++) {

timeoutlist[i].pid = 0; }

// keep track of how many threads have been created nextthread = 0; }

13. sys_arch_timeouts函数

该函数用于返回当前任务的定时器管理链表首地址。代码如下:

sys_arch_timeouts(void) {

int i;

xTaskHandle pid;

struct timeoutlist *tl;

pid = xTaskGetCurrentTaskHandle( ); for(i = 0; i < nextthread; i++) {

tl = &timeoutlist[i]; if(tl->pid == pid) {

return &(tl->timeouts); } }

// Error return NULL; }

14. sys_arch_protect函数

该函数用于保护临界区资源,代码如下:

sys_prot_t sys_arch_protect(void) {

vPortEnterCritical(); return 1; }

15. sys_arch_unprotect函数

该函数在访问临界区资源时使用,必须与sys_arch_protect函数成对使用。代码如下: void sys_arch_unprotect(sys_prot_t pval) {

( void ) pval;

vPortExitCritical(); }

本文来源:https://www.bwwdw.com/article/lw06.html

Top