STM32F4XX LWIP+freeRTOS移植(一)

有疑问请加扣扣技术交流群:460189483

TM32F429IGT6原子开发板进行验证的,PHY芯片为LAN8720

原子哥的程序都是ucos_ii+lwip的工程,没有freeRTOS+lwip的工程,这里对比二种系统的差异,来进行lwip的freeRTOS的移植

LWIP 无操作系统移植实验的LWIP 文件夹可以发现有一个arch 文件夹,在arch 中有5 个文件cc.h、cpu.h、perf.h、sys_arch.h和sys_arch.c。根据sys_arch.txt 中的描述,cc.h 主要完成了协议栈内部使用的数据类型的定义,如果使用操作系统的话还有临界代码区保护等等。

lwip_comm文件夹,这个文件中有lwip_comm.c、lwip_comm.h 和lwipopts.h这三个文件,lwip_comm.c 和lwip_comm.h 是将LWIP 源码和前面的以太网驱动库结合起来的桥梁!这两个文件非常重要,这两个文件有ALIENTEK 提供。lwipopts.h 是用来裁剪和配置LWIP的文件,以后我们想要使用LWIP 的什么功能的话就在这个文件中配置就行了。

综上所述,ucos_ii+lwip的工程涉及到LWIP的总计8个文件,分别是cc.h、cpu.h、perf.h、sys_arch.h、sys_arch.c、lwip_comm.c 、lwip_comm.h和lwipopts.h。要变更为freeRTOS+lwip,只需要更改cc.h、sys_arch.h、sys_arch.c三个文件即可,其他与ucos_ii+lwip的保持一致即可(将里面ucos_ii的函数替换为freeRTOS的即可)。这几个文件作用如下:

最主要是重写sys_arch.c,sys_arch.h,cc.h这三个文件即可,是参照lwip教程的实验2进行修改进行,要重写的函数基本上都在lwip_sys.h这个文件中定义好了,只要将里面的函数都实现了即可!

1. cc.h的重写

第一步:包含的头文件要改变,includes.h是指包含ucos_ii系统的所有头文件,要使用freeRTOS必须包含FreeRTOS.h

下面如图是使用ucos_ii系统时的头文件

扫描二维码关注公众号,回复: 3673858 查看本文章

变更为如下所示的头文件,因为在sys_arch.c中要使用到freeRTOS的任务句柄,所以也要包含task.h

第二步:要改变的区域就是临界区代码保护区域,ucos_ii的内容如下:

因为ucos_ii的临界区保护有2种类型,类型根据OS_CRITICAL_METHOD来决定,一般都是用的3,而且ucos_ii的临界区保护不分任务级与中断级,所有保护函数只有一个,而freeRTOS是区分任务级与中断级的,这一点一定要注意!

使用freeRTOS时,这一部分的代码更改如下:

其中,SCB_ICSR_REG定义的寄存器是用于区分当前是任务级还是中断级的,主要在Enter_Critical()与Exit_Critical()中使用,

Enter_Critical()与Exit_Critical()这两个函数是在sys_arch.c中实现的,所以此处加extern声明为外部函数,Enter_Critical()用于声明进入保护临界区,Exit_Critical()声明退出保护临界区,cc.h的内容更改完成

2. sys_arch.h中的重写

在使用ucos_ii时,该文件内容如下:

头文件中includes.h包含的是ucos_ii的头文件,需要更改为FreeRTOS.h,sys_arch.c中要使用FreeRTOS的消息队列和信号量,还必须要包含queue.h和semphr.h;ucos_ii没有消息队列,所以这里使用了自定义的消息队列结构,freeRTOS中本身就有消息队列,可以直接使用,所以变更如下:

经过对比,有些童鞋可能疑问为什么ucos_ii系统中对sys_sem_t、sys_mutex_t、sys_mbox_t数据结构的定义是用的指针,而freeRTOS系统中没有用指针,其实你定义为指针,或者非指针,都可以,对系统没有什么影响,不同之处在于sys_arch.c中函数的实现,如果使用的是指针,那么对变量引用一定要 (*变量)->变量  这样才可以,如果定义为非指针 变量->变量 这样就可以了,大家完全可以按照自己的喜好来定义。

3. sys_arch.c中的重写

sys_arch.c中要重写的函数与ucos_ii一样的,也是实现这几个函数而已,现将两个系统的函数拿来做一下对比,根据原子教程,要实现的函数如下所示:

如果大家想知道这些函数的原型定义,可以在文件lwip_sys.h中找到

第一个:sys_sem_new函数,用于创建消息邮箱,lwip_sys.h中函数原型如下:

使用ucos_ii系统时,实现方法如下:

因为是二级指针,所以要给mbox分配内存,分配内存使用的是原子的内存池的方法,从192k的内部sram中分配,然后使用ucos_II的队列创建函数创建队列,大小为size,如果创建失败就释放掉分配的内存,并返回队列创建的状态信息

使用freeRTOS时变更如下:

使用freeRTOS的队列创建函数创建队列,队列长度为size,消息类型长度为指针长度(4字节),这样就创建了一个存放指针的消息队列,如果创建失败,返回错误,与ucos_ii相比,缺省了内存分配环节,这是因为xQueueCreate函数是动态创建消息队列,由freeRTOS系统自动分配内存,分配失败了不需要自己释放!

第二个:sys_mbox_free函数,用于删除一个消息邮箱,lwip_sys.h中函数原型如下:

使用ucos_ii系统时,实现方法如下:

先创建一个消息队列指针,保存要删除的消息队列,然后调用ucos_ii的函数删除这个消息队列,然后调用原子的sh释放函数释放掉消息队列的内存,将消息队列指针赋值为nu'll,这两个函数都比较好理解。

使用freeRTOS时变更如下:

第三个:sys_mbox_post函数,用于向消息邮箱发送消息,阻塞式发送,不成功一直等待,lwip_sys.h中函数原型如下:

ucos_ii系统时,该函数实现方法如下:

该函数比较好理解,就是向消息邮箱发送消息,如果不成功则一直发送,直到成功为止!

注意pvNullPointer是一个空指针的处理方法,如下值:

freeRTOS系统时,实现方法如下:

此处的空指针处理方法,NullMessage与上面不一样,仅仅是定义了一下,并没有赋值的操作,如下,可以赋值试试:

与ucos相比,分中断中发送消息与任务中发送消息,如果返回pdPASS表示发送成功,xHigherPriorityTaskWoken主要用于中断中发送完消息后,进行判断是否要进行任务切换!

其实如果细心的朋友一定会发现,在这个函数原型中有一句话,说明该函数仅仅用于在任务中发送消息,如下:

所以其实上面的在中断中发送消息,并进行任务切换的操作是多余的,本着严谨的态度还是写上了,只是永远不会执行到!

第四个:sys_mbox_trypost函数,尝试向消息邮箱发送消息,不阻塞式发送,lwip_sys.h中函数原型如下:

在ucos_ii系统中的实现方式如下:

此函数比较简单,只是尝试向消息邮箱发送消息,失败就返回错误,不阻塞!

在freeRTOS中的实现方式如下:

因为该函数没有讲是在任务中还是中断中使用,所以一定要将中断考虑进去,比较简单!

第五个:sys_arch_mbox_fetch函数,从消息邮箱中等待一个新消息,阻塞式等待,lwip_sys.h中函数原型如下:

在ucos_ii系统中的实现方法如下:其中timeout的单位是毫秒

此函数调用OSQPend从消息队列中获取消息,如ucos_timeout=0,就阻塞式等待直到新消息的到来,如果ucos_timeout不为0,等待ucos_timeout个时钟节拍后自动退出,返回OS_ERR_TIMEOUT,函数最终返回SYS_ARCH_TIMEOUT!通过判断消息地址是不是pvNullPointer来判断消息是不是NULL,该指针上面有说明。

freeRTOS的实现方式如下:

这部分的代码是经过优化的,一开始直接尝试进行一次接收,如果有消息就马上返回,节省下了执行下面时间解析的时间,如果消息是空的,再执行等待timeout时间,超时返回!有些人可能疑问freeRTOS系统是分任务级出队函数与中断级出队函数的,为什么我们这里只用到了任务级出队函数,主要是考虑到该函数需要delay timeout时间,应该不会在中断中使用,否则就太傻了吧,中断中使用的应该是下面 sys_arch_mbox_tryfetch这个函数,所以这里只进行了任务级出队函数,大家知道就好!

第六个:sys_arch_mbox_tryfetch函数,尝试从消息邮箱中接收一个新消息,非阻塞式尝试,lwip_sys.h中函数原型如下:

如果接收到消息,返回0,如果没有接收到消息,返回SYS_MBOX_EMPTY

在ucos_ii系统中,该函数的实现方法如下:

这里非常巧妙的调用sys_arch_mbox_fetch函数,将超时时间timeout设置为1毫秒,可能大家会疑惑,sys_arch_mbox_fetch超时时返回的是SYS_ARCH_TIMEOUT,而这里需要返回SYS_MBOX_EMPTY,其实这两个宏定义是一个值,在lwip_sys.h中定义

如果不超时,sys_arch_mbox_fetch返回值是2毫秒,但是按照函数原型应该返回0毫秒才对,这一点没搞懂,希望大神留言!

第7个:sys_mbox_valid函数,检查一个邮箱是否有效,lwip_sys.h中函数原型如下:

返回1表示有效,返回0表示无效

ucos_ii系统中,该函数实现方式如下:

这个就是使用ucos自带的函数实现,通过查看返回值与队列类型来判断,不太理解的可以查看该函数原型

返回值<2表示消息队列为NULL,OSNMsgs是消息队列中的消息数(.OSQEntries的拷贝)OSQSize是消息队列的总的容量

freeRTOS的实现方法如下:

这里的实现比较简单,只是判断该消息队列是否为NULL,感兴趣的可以通过判断队列结构内容来进行判断

第8个:sys_mbox_set_invalid函数,设置一个邮箱无效,lwip_sys.h中函数原型如下:

此函数只是将该一个消息队列设置为无效NULL即可,ucos_ii系统实现方式如下:

freeRTOS的实现方法也很简单,如下:

第9个:sys_sem_new函数,创建一个信号量,lwip_sys.h中函数原型如下:

创建一个信号量,成功返回ERR_OK,失败返回其它,count表示信号量初值,

ucos_ii系统实现方式如下:

就是使用uc内部函数实现信号量的创建,设置初值为count

freeRTOS系统实现方式如下:

freeRTOS中有二值信号量、计数信号量、互斥信号量、递归信号量,此处需要设置初值count,很明显使用计数信号量,第一个参数OXFF表示计数信号量最大计数值,当信号量值等于此值时释放信号量就会失败

第10个:sys_arch_sem_wait函数,等待一个信号量,lwip_sys.h中函数原型如下:

在规定的时间内等待一个信号量,时间是毫秒,如果timeout是0,表示一直等待,成功的话返回值表示等待的时间,如果超时返回SYS_ARCH_TIMEOUT

ucos_ii系统实现方法如下:

比较好理解不解释了,freeRTOS的实现方法如下:

这部分的代码是经过优化的,一开始直接尝试进行一次信号量接收,如果有信号量就马上返回,节省下了执行下面时间解析的时间,如果信号量获取失败,再等待timeout时间,超时返回,或者一直等待,直到有信号量为止!有些人可能疑问freeRTOS系统是分任务级获取信号量函数与中断级获取信号量函数的,为什么我们这里只用到了任务级获取信号量函数,主要是考虑到该函数需要delay timeout时间,应该不会在中断中使用,否则就太傻了吧,所以这里只进行了任务级获取信号量函数

第11个:sys_sem_signal函数,发送(释放)信号量,lwip_sys.h中函数原型如下:

该函数也比较简单,直接调用系统函数即可,ucos_ii系统实现方法如下:

freeRTOS系统实现方法如下:

这里有一个问题,freeRTOS的信号量释放是区分任务与中断的,ucos不用区分,所以这里接下来会更新,更新如下:

第12个:sys_sem_free函数,删除一个信号量,lwip_sys.h中函数原型如下:

此函数比较简单,就是调用系统函数删除一个信号量,ucos_ii系统实现方法如下:

freeRTOS系统实现方法如下:

第13个:sys_sem_valid函数,查询一个信号量是否有效,lwip_sys.h中函数原型如下:

查询一个信号量是否有效,返回1表示有效,返回0表示无效,ucos_ii实现方法如下:

freeRTOS系统没有查询信号量API,简单实现方法如下:

第14个:sys_sem_set_invalid函数,设置一个信号量无效,lwip_sys.h中函数原型如下:

这个函数比较简单,ucos_ii系统实现如下:

freeRTOS系统实现如下:

第15个:sys_thread_new函数,创建进程,lwip_sys.h中函数原型如下:

仅仅用于创建一个新的进程,该进程用于TCP IP内核进程,name表示线程名称,thread表示线程执行函数,arg传递给线程的参数,stacksize线程堆栈大小,prio线程优先级,ucos_ii系统实现方法如下:

freeRTOS系统实现方法如下:

与uc不同的是,多出来一个任务句柄全局变量的定义LWIP_ThreadHandler,是因为函数需要!

第16个:sys_init函数,初始化操作系统模拟层,lwip_sys.h中函数原型如下:

这个函数不需要填写内容,写一个空函数即可

第17个:sys_msleep函数,LWIP的延时函数,lwip_sys.h中函数原型如下:

ucos_ii系统、freeRTOS系统使用的原子的延时函数

第18个:sys_now函数,获取当前系统时间,单位ms,lwip_sys.h中函数原型如下:

ucos_ii系统直接调用系统API获得时间节拍,再转化为毫秒即可,实现方式如下:

freeRTOS系统实现方式如下:

通过调用HAL_GetTick()获得系统时钟节拍,该函数在系统的滴答时钟中断中被周期的调用,来记录系统时间,过程如下:

第18个:Enter_Critical、Exit_Critical函数,cc.h中声明的临界区代码保护函数,ucos_ii系统直接使用的系统临界区保护函数,freeRTOS中因为分任务级与中断级,所以需要做一个判断,freeRTOS实现代码如下:

通过SCB_ICSR_REG&0XFF来判断是在中断还是任务中

至此,sys_arch.c函数的内容全部完成!

猜你喜欢

转载自blog.csdn.net/u014453443/article/details/81328222