打印

GD32F407 移植FreeRTOS+Lwip

[复制链接]
2151|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
qsrg51|  楼主 | 2023-7-26 18:44 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
移植过程
freeRTOS V10.4.5 下载 https://github.com/FreeRTOS/FreeRTOS

Lwip V2.1.3 http://download.savannah.nongnu.org/releases/lwip/

GD32F407VET6

keil5

移植FreeRTOS
源码拷贝
下载源码,解压。源码在FreeRTOS文件夹中Source文件夹中。

在我们的工程文件中创建文件夹FreeRTOS。将Source中下图中的内容拷贝到我们创建的FreeRTOS文件夹中。这些是各平台通用的头文件和C文件。

接下来移植平台相关的接口文件,在portable文件夹中。我用的是keil。打开portable中的keil文件夹。里面是各txt文本。不需要打开了,里面啥都没有。看txt文件名字,其实就是让你去看RVDS文件夹中的内容。打开portable中的RVDS文件夹。我用的是CM4的平台,因此在我们自己创建的FreeRTOS文件家中创建一个portable,将RVDS文件夹中ARM_CM4F的内容拷贝到我们自己创建的portable中。接下来将源码protable文件夹中的MemMang拷贝到我们自己创建的protable文件中。

MemMang其实就是Memory management的缩写,就是内存管理的意思。这里面右5个C文件,代表了5中内存管理的方式。其实我们在使用的时候只需要使用其中一种即可,最终我们往工程中添加哪个,就使用哪个。我添加的是heap_4这个C文件。

以上这些是FreeRTOS的基本文件,我们不需要进行修改。


使用特权

评论回复
沙发
qsrg51|  楼主 | 2023-7-26 18:44 | 只看该作者
FreeRTOSConfig.h
FreeRTOSConfig.h 是freeRTOS工程的配置文件。里面通过各种宏定义来使能FreeRTOS的各种功能,这个过程就叫做裁剪。这里我直接在demo里的FreeRTOSConfig.h 来使用,后续再根据自己的需要进行修改。下载的FreeRTOS文件夹下的demo包含了各种平台的完整的demo工程。我选择了比较接近我用的平台的demo中的FreeRTOSConfig.h(在CORTEX_M4F_STM32F407ZG-SK文件夹下)。

至此,所有必须文件都拷贝完成,接下来加入到工程中。

使用特权

评论回复
板凳
qsrg51|  楼主 | 2023-7-26 18:45 | 只看该作者
注意事项
1、我用的是keil 编译器应该为__CC_ARM 而demo的FreeRTOSConfig.h中是 __ICCARM__ 这个是IAR的,需要改掉。

2、中断映射

#define vPortSVCHandler SVC_Handler  
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
1
2
3
上面这些宏定义是FreeRTOSConfig.h中的。

使用特权

评论回复
地板
qsrg51|  楼主 | 2023-7-26 18:45 | 只看该作者
SVC_Handler是系统调用异常的中断入口,将vPortSVCHandler映射到SVC_Handler,产生SVC系统调用异常的时候就会执行vPortSVCHandler,在vPortSVCHandler主要做的就是启动第一个任务,切换堆栈指针从MSP到PSP。这个中断只在任务开始的时候使用一次。

PendSV_Handler是用来做任务切换,是可挂起的异常,SVC异常是必须立即得到响应的(若因优先级不比当前正处理的高,或是其它原因使之无法立即响应,将会产生Hardfault)。,PendSV 它是可以像普通的中断一样被悬起的。OS 可以利用它“缓期执行”一个异常——直到其它重要的任务完成后才执行动作。xPortPendSVHandler中主要用来作为任务切换。当需要进行任务切换的时候就手动产生一个PendSV异常,进入xPortPendSVHandler完成任务切换。

SysTick_Handler是系统时钟定时器的中断入口。xPortSysTickHandler主要用来对系统运行时间进行加1.判断任务列表是否有延时到期的,将延时到期的任务转换为就绪态。然后就是触发PendSV异常,这就是心跳节拍的由来,每一次心跳节拍进入一次xPortSysTickHandler,然后触发一次PendSV异常,在异常中看是否右更高优先级的程序需要切换。

系统时钟定时器的初始化问题:freeRTOS已经写好了一个系统时钟初始化的函数vPortSetupTimerInterrupt。这个函数根据我们在FreeRTOSConfig.h中配置的configTICK_RATE_HZ和configSYSTICK_CLOCK_HZ来设置中断产生的时间。配置系统时钟的寄存器已经在port.c里面映射了,这个和平台关系比较大。在任务启动的函数里面调用了vPortSetupTimerInterrupt。

freeRTOS移植主要就是上面这三个中断。我的理解是这三个中断可以使用其他中断去代替,但是向SVC,penSV都是专门针对操作系统设计的,相较于其他中断来说更合适一些。

使用特权

评论回复
5
qsrg51|  楼主 | 2023-7-26 18:45 | 只看该作者
移植LWIP
源码拷贝与编译
我下载的是lwip-2.1.3和contrib-2.1.0。源码放在了lwip-2.1.3下的src文件夹。在移植好的freeRTOS工程下新建一个文件夹LWIP。将src文件夹下面的文件拷贝到我们创建的LWIP文件夹下面

使用特权

评论回复
6
qsrg51|  楼主 | 2023-7-26 18:46 | 只看该作者
这个是lwip的基本代码,不需要我们进行修改。需要我们自己来实现的有三个:操作系统相关的接口、底层网卡的操作接口、配置文件。如果是裸机的话,那么我们需要实现的是裸机相关的接口(lwip的时基,类似于操作系统的心跳)、底层网卡的操作接口、配置文件。

//在带操作系统的移植中,时基,也就是sys_now函数如下
#if LWIP_FREERTOS_SYS_NOW_FROM_FREERTOS
u32_t
sys_now(void)
{
  return xTaskGetTickCount() * portTICK_PERIOD_MS;
}
#endif

使用特权

评论回复
7
qsrg51|  楼主 | 2023-7-26 18:46 | 只看该作者
操作系统相关接口移植,我使用的是freeRTOS,这个contrib-2.1.0里面有相应的文件,contrib-2.1.0\ports\freertos,在LWIP文件夹下面新建一个port文件夹,contrib-2.1.0\ports\freertos下面的C文件和H文件拷贝到port。

底层网卡操作文件先将contrib-2.1.0\examples\ethernetif下面的C文件拷贝过来,在这个上面根据我们网卡的驱动程序自己修改。

配置文件先将contrib-2.1.0\examples\example_app下面的lwipopts.h拷贝过来,在这个上面根据我们的需要进行配置,就像FreeRTOSConfig.h一样。

编译显示缺少cc.h文件,cc.h 文件中包含处理器相关的变量类型、数据结构及字节对齐的相关宏。LwIP 中使用的基本变量类型均以位数进行命名,为抽象的变量类型定义,开发者需要根据所用处理器及编译器特性进行定义,一般我们直接将变量直接定义为 C 语言的基本类型,如 unsignedchar、int 等,这样子可以保证 LwIP 协议栈就与平台无关了。我这里使用的是GD32已经编写好的cc.h文件。在LWIP文件夹下面新建一个arch文件夹,将cc.h拷贝进来。

编译显示cc.h里面缺少cpu.h,我看GD的工程里cpu.h里面没啥内容,野火的教程里也没有引用这个,所以我把这这行代码屏蔽了。

编译显示找不到“arch/sys_arch.h”这个文件,我的sys_arch.h文件是放在port文件夹下面,我又拷贝了一分到arch文件夹中。

Error: L6218E: Undefined symbol errno (referred from if_api.o). 在if_api.c文件中加入了int errno;

移植的时候报了很多错误,大部分是lwipopts.h配置文件引起的,最终我重新将GD工程里的lwipopts.h拿来了,错误就不见了,后面在去研究这些配置文件的意义。

使用特权

评论回复
8
qsrg51|  楼主 | 2023-7-26 18:47 | 只看该作者
另外不是所有源码里所有的C文件都需要添加到工程里的,下面的是我工程里添加的。


使用特权

评论回复
9
qsrg51|  楼主 | 2023-7-26 18:47 | 只看该作者
初始化
首先是硬件的初始化,包括了GPIO、时钟、MAC,这个每个平台初始化内容不一样。

接下来是LWIP协议栈的初始化
void lwip_stack_init(void)
{
    ip_addr_t ipaddr;
    ip_addr_t netmask;
    ip_addr_t gw;

    /* 创建一个tcpip进程 */
    tcpip_init( NULL, NULL );

    IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
    IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3);
    IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);

    netif_add(&g_mynetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input);

    /* 设置默认网卡,因为可能一个系统里包含了多个 */
    netif_set_default(&g_mynetif);

    /* 该函数设置NETIF_FLAG_UP标记 */
    netif_set_up(&g_mynetif);
     /* 该函数设置NETIF_FLAG_LINK_UP标记 */
    netif_set_link_up(&g_mynetif);
}

使用特权

评论回复
10
qsrg51|  楼主 | 2023-7-26 18:47 | 只看该作者
协议栈初始化,首先调用 tcpip_init( NULL, NULL );
void tcpip_init(tcpip_init_done_fn initfunc, void *arg)
{
  lwip_init();

  tcpip_init_done = initfunc;
  tcpip_init_done_arg = arg;
  if (sys_mbox_new(&tcpip_mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
    LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
  }
#if LWIP_TCPIP_CORE_LOCKING
  if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
    LWIP_ASSERT("failed to create lock_tcpip_core", 0);
  }
#endif /* LWIP_TCPIP_CORE_LOCKING */

  sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
}

使用特权

评论回复
11
qsrg51|  楼主 | 2023-7-26 18:47 | 只看该作者
tcpip_init中调用lwip_init初始化了lwip协议栈内核然后创建邮箱、根据配置文件创建互斥信号量、启动一个TCPIP的线程。邮箱用于接收从底层或者上层传递过来的消息,消息传递给 tcpip_thread 线程,用这个线程来处理数据。简单理解就是freeRTOS中专门给网络数据接收起了一个任务,任务接收邮箱消息,根据对应的消息对数据做对应的处理,处理完后给到我们用户任务。

使用特权

评论回复
12
qsrg51|  楼主 | 2023-7-26 18:47 | 只看该作者
问题点:

调试过程中可以ping通,但是起了TCP任务之后就ping不通,最后发现在创建socket的时候会创建邮箱(freeRTOS中的队列),因为我没有定义DEFAULT_TCP_RECVMBOX_SIZE,导致创建队列的时候size默认为0,最后增加定义#define DEFAULT_TCP_RECVMBOX_SIZE 10,问题解决。

移植工程 :https://github.com/IJustLoveMyself/csdn-example/tree/main/example1

使用特权

评论回复
13
sfd123| | 2024-2-3 16:05 | 只看该作者
这个应定要好好看看,我正要搞!谢谢分享!

使用特权

评论回复
14
sfd123| | 2024-2-24 11:02 | 只看该作者
请问 你的工程怎么才能下载呢?

使用特权

评论回复
15
申小林一号| | 2024-4-30 16:26 | 只看该作者
非常不错的帖子,值得推广扩散!!!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

53

主题

395

帖子

2

粉丝