基于FreeRTOS的N32G457VEL7之LWIP网络协议栈移植

前言:

硬件平台:

  • Demo板:N32G45XVL-STB
  • ETH芯片:LAN8720A模组

LAN8720A模组端口和原理图如下:

注意:

  • LAN8720A模组自带25MHZ的内部晶振,LAN8720A自带PLL倍频器,可以倍频到50MHZ,通过RCLK口输出,
  • LED2/nINTSET接下拉,即nINTSEL=0,为REF_CLK Out Mode模式;

1、LWIP源码下载

   打开http://savannah.nongnu.org/projects/lwip/网址下载lwIP源码包和contrib

下载STM32Cube_FW_F4_V1.26.0,地址https://www.st.com/zh/embedded-software/stm32cubef4.html

2、在电脑的文件搜索栏上输入国民技术资料网址:ftp://download.nationstech.com/

下载并解压:

3、准备一个已经移植好FreeRTOS的N32G457项目文件(此处可以参考国民技术ETH的官方例程,移植步骤很简单,此处不过多解释)

4、在项目文件夹目录中新建LWIP文件夹,在LWIP文件夹下新建port文件夹,在port文件夹下新建arch文件夹。

5、按照以下步骤将资源文件复制到各文件夹下。

①将lwip-2.1.3中的src文件夹全部复制到LWIP下。

②将contrib-2.1.0中的sys_arch.c与sys_arch.h文件拷贝至LWIP\port\arch文件夹下。

③将STM32Cube_FW_F4_V1.26.0\Middlewares\Third_Party\LwIP\system\arch\下的文件拷贝至LWIP\port\arch文件夹下。

④N32G457xx_V3.1.0\6-Software Development Kit\Nationstech.N32G45x_Library.2.2.0\projects\n32g45x_EVAL\examples\ETH\TcpServer\src下的文件拷贝至LWIP\port文件夹下。

⑤同时将N32G457xx_V3.1.0\6-Software Development Kit\Nationstech.N32G45x_Library.2.2.0\projects\n32g45x_EVAL\examples\ETH\TcpServer\src\arch下的cc.h复制到LWIP\port\arch下,将之前复制的cc.h进行替换。

6、添加文件到工程中

①新建LWIP组并将相关文件添加文件到工程中

②新建LWIP_PORT组并将相关文件添加文件到工程中

7、添加头文件

8、修改工程文件

①打开sys_arch.c文件并添加头文件lwipopts.h。
/* lwIP includes. */
#include "lwipopts.h"
#include "lwip/debug.h"
#include "lwip/def.h"
#include "lwip/sys.h"
#include "lwip/mem.h"
#include "lwip/stats.h"
#include "FreeRTOS.h"
#include "semphr.h"
#include "task.h"

②因为我现在用的网卡模块是LAN8720A,所以需要修改相关驱动代码。

打开lwip_port.c文件将网卡类型修改为HY_TYPE_LAN8710 ,接线模式可以按照需求选择模式1或者模式0,此处我选择模式1。

#define PHY_TYPE      PHY_TYPE_LAN8710	 // PHY_TYPE_DM9161
#define ETH_INTERFACE ETH_INTERFACE_RMII // 0:RMII, 1:MII
#define REMAP_MODE    1					 // 接线模式

IP,掩码,网关修改如下。

#if LWIP_DHCP
    ip.addr  = 0;
    msk.addr = 0;
    gw.addr  = 0;
#else  // !LWIP_DHCP
    IP4_ADDR(&ip, 192, 168, 12, 247);
    IP4_ADDR(&msk, 255, 255, 255, 0);
    IP4_ADDR(&gw, 192, 168, 12, 1);
#endif // LWIP_DHCP

mac地址可根据需要进行修改,保证唯一性就行,此处选择默认。

const uint8_t mac_address[] = {0x20, 0x34, 0x56, 0x78, 0x9a, 0xbc};

因为我现在使用的是静态IP,所以打开lwipopts.h文件,将DHCP功能关闭。

/* ---------- DHCP options ---------- */
#define LWIP_DHCP 0

打开test_tcp_server.c文件,修改端口(可以根据实际需求进行更改)。

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = netif_dev.ip_addr.addr;
    serverAddr.sin_port = htons(8080)
③RMII模式下,接线模式1对应的GPIO口,参考国名技术用户手册ETH章节如下,标黄部分为需要接线的IO口。

9、将N32G457xx_V3.1.0\6-Software Development Kit\Nationstech.N32G45x_Library.2.2.0\projects\n32g45x_EVAL\examples\ETH\TcpServer\src下的测试文件test_tcp_server.c复制到工程目录下,并添加到工程中。

10、修改main.c如下

/**
 * @file main.c
 * @author Nations
 * @version v1.0.0
 *
 * @copyright Copyright (c) 2019, Nations Technologies Inc. All rights reserved.
 */
#include <stdio.h>
#include "drv_log.h"
#include "drv_rcc.h"
#include "drv_log_segger.h"

#include "FreeRTOS.h"
#include "cmsis_os.h"
#include "task.h"
#include "misc.h"

#include "lwip/netif.h"
#include "lwip/api.h"
#include "lwip/sockets.h"
#include "lwipopts.h"
#include "lwip_port.h"

/** @addtogroup N32G45X_StdPeriph_Examples
 * @{
 */
int errno = 0;
void vApplicationStackOverflowHook(TaskHandle_t xTask, char* pcTaskName)
{
    printf("stack overflow\n");
}

void vApplicationMallocFailedHook(void)
{
    printf("malloc failed\n");
}
/** @addtogroup USART_Printf
 * @{
 */

/***********************************task init**********************************/
#define START_TASK_PRIO         osPriority_5      
#define START_STK_SIZE          ((unsigned short)128)      
TaskHandle_t StartThread_Handler;           
static void StartThread(void const * argument);     

#define LWIP_DMEO_TASK_PRIO     osPriority_10         
#define LWIP_DMEO_STK_SIZE      ((unsigned short)128)     
TaskHandle_t LwipThread_Handler;
static void LwipThread(void const * argument);   
/***********************************task end***********************************/
//uint32_t PreviousWakeTime;

/**
 * @brief  Main program
 */
extern void test_tcp_init(void);
int main(void)
{
	//drv_SysClockInit_144MHZ();
    RCC_ClocksType clks;
	
	drv_log_init();
	drv_log_segger_Init();

    /* Output a message on Hyperterminal using printf function */
    RCC_GetClocksFreqValue(&clks);
    log_info("SYSCLK: %d\r\n", clks.SysclkFreq);
    log_info("HCLK: %d\r\n", clks.HclkFreq); 
    log_info("PCLK1: %d\r\n", clks.Pclk1Freq);
    log_info("PCLK2: %d\r\n", clks.Pclk2Freq);
    log_info("AdcPllClkFreq: %d\r\n", clks.AdcPllClkFreq);	
    log_info("AdcHclkFreq: %d\r\n", clks.AdcHclkFreq);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	/* Initialize all configured peripherals */
    osThreadDef(Start_Thread, StartThread, START_TASK_PRIO, 0, START_STK_SIZE);
    StartThread_Handler  = osThreadCreate(osThread(Start_Thread), NULL);

    /* Start scheduler */
	osKernelStart();	
		
    while (1){}
}

static void StartThread(void const * argument)
{	
	uint16_t u16Val = 0;
	UBaseType_t uxHighWaterMark;
	uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL );  
	log_info("1---StartThread: uxHighWaterMark:%ld\r\n",uxHighWaterMark);	
		
	
	for(int i=0;i<5;i++)
	{
		osDelay(500);
		log_info("StartThread\r\n");
	}
//	uint8_t UIDbuf[0x0C];
//	GetUID(UIDbuf);
//	log_info("GetUID:%d\r\n",UIDbuf);	
	
	/* Initialize all configured peripherals */
    osThreadDef(Lwip_Thread, LwipThread, LWIP_DMEO_TASK_PRIO, 0, 2*LWIP_DMEO_STK_SIZE);
    LwipThread_Handler  = osThreadCreate(osThread(Lwip_Thread), NULL);	
		
	u16Val = uxTaskGetNumberOfTasks();
	log_info("In all tasks:%d\r\n",u16Val);
	
	uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL );  
	log_info("2---StartThread: uxHighWaterMark:%ld\r\n",uxHighWaterMark);	

	uxHighWaterMark = uxTaskGetStackHighWaterMark( LwipThread_Handler );  
	log_info("3---LwipThread: uxHighWaterMark:%ld\r\n",uxHighWaterMark);

	uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL );  
	log_info("2---StartThread: uxHighWaterMark:%ld\r\n",uxHighWaterMark);		
	
	vTaskDelete(xTaskGetCurrentTaskHandle());
}	

static void LwipThread(void const * argument)
{	
    RCC_EnableAPB2PeriphClk(  RCC_APB2_PERIPH_AFIO  | RCC_APB2_PERIPH_GPIOA | RCC_APB2_PERIPH_GPIOB | RCC_APB2_PERIPH_GPIOC
                            | RCC_APB2_PERIPH_GPIOD | RCC_APB2_PERIPH_GPIOE | RCC_APB2_PERIPH_GPIOF
                            | RCC_APB2_PERIPH_GPIOG ,
                            ENABLE);
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ETHMAC, ENABLE);
    if (lwip_port_init())
    {
        printf("LWIP Init Falied!\n");
        while (1)
            ;
    }
	
    log_info("LWIP Init Success!\n");
    test_tcp_init();    
    log_info("TCP Task Success!\n");	
	vTaskDelete(NULL);//删除开始任务
}

11、网卡模块LAN8720A与N32G45XVL_STB接线如下

REMAP_MODE为1时
RMII信号		MCU接线			ETH接线	
MDC				PC1				MDC			
REF_CLK			PA1				RCLK	
MDIO			PA2				MDIO	
CRS_DV			PD8				CRS	
RxD0			PD9				RX0	
RxD1 			PD10			RX1		
Tx_EN			PB11			TXEN	
TxD0			PB12			TX0	
TxD1			PB13			TX1	
								XT1		不接				
								RXE		不接
								RST		不接

12、修改本地IP,子网掩码,网关,DNS。注意电脑IP和开发板IP地址一定要在同一个网络内。

13、编译下载测试:


至此移植LWIP完毕,值得一提的是,大家也可以完全将国名技术的ETH例程直接移植过来。

补充说明:

后面我在将该例程移植到项目过程中发现Sram的内存不够,编译报错的故障,在此记录下我解决问题的思路和步骤。

打开.map文件发现在Image component sizes中发现

分别是.heap_4.o,memp.o,mem.o,lwip_port.o的ZI Data占据大量空间。也就是这几个文件占据大量Sram。分别对它们进行处理如下。

  1. ①修改FreeRTOSConfig.h中如下,此处对应heap_4.o申请的空间,freertos会创建在heap_4.c中创建一个大数组,作为任务堆栈的使用空间,可将之改小至合适大小。
  2. #define configTOTAL_HEAP_SIZE          ((size_t)(29 * 1024))	//35*1024定义用户栈(任务栈),也叫HEAP区存在数据段(具体为.bss段),是一个静态数组,FreeRTOS 内核,用户动态内存申请,任务栈,任务创建,信号量创建,消息队列创建等都需要用这个空间。

    ②修改lwipopts.h如下,此处对应mem.o与memp.o申请的空间,MEM_SIZE作为lwip内存池数组下标,和freertos一样也会创建一个大数组,也将之改小至合适大小。(mem内存池:是用于数据的发送缓存,根据自己的实际情况改至合适大小)

  3. 而失能UDP功能后,lwip便不会创建属于UDP的内存池,也可以降低Sram的使用,同时也可结合opt.h这个文件修改各不同类型内存池中的内存块数量,后续我会发布下我修改后的lwipopts.h相关配置,先在此埋个坑

  4. #define MEM_SIZE (5 * 1024) //54 * 1024 heap size for lwip, set a big value if transmit many data
    
    #define LWIP_UDP 0                      //1
    

    此时会编译报错,要把init.c中的120行的宏定义检查屏蔽

  5. //#if (LWIP_IGMP && !LWIP_MULTICAST_TX_OPTIONS)
    //#error "If you want to use IGMP, you have to define LWIP_MULTICAST_TX_OPTIONS==1 in your lwipopts.h"
    //#endif
  6. ③因为我使用 FreeRTOS 驱动 lwip,因此 lwip 的线程 栈大小,也是 可以减少内存的,即设定 TCPIP_THREAD_STACKSIZE ,在 lwipopts.h 文件中,这个线程栈的单位是按照 字 计算的哦,要注意。

  7. #define TCPIP_THREAD_STACKSIZE          256
  8. ④在lwip_port.c中也定义了两个收发ETH帧的大数组,它们作为缓存区也可以适当改小。

#define ETH_RXBUFNB     2                //5
#define ETH_TXBUFNB     2                //5   
#define ETH_RX_BUF_SIZE 1524
#define ETH_TX_BUF_SIZE 1524

ETH_DMADescType g_RxDesc[ETH_RXBUFNB];
ETH_DMADescType g_TxDesc[ETH_TXBUFNB];
uint8_t g_RxBuf[ETH_RX_BUF_SIZE * ETH_RXBUFNB];
uint8_t g_TxBuf[ETH_TX_BUF_SIZE * ETH_TXBUFNB];

⑤修改startup_n32g45x.s中的主栈大小,当然不建议改动这里,因为此处并不占据大量空间。

Stack_Size      EQU     0x00000800	

修改完后编译通过,收发数据也是正常的。

Image component sizes
      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name
      1124        402        138         24      20480      13822   heap_4.o
      1772        230         52          4      10840       8455   lwip_port.o
      2592       1002          0         16      34835       8741   mem.o
       580        282        416         52      27411      11910   memp.o

此处参考:减少 lwip 消耗 的 RAM - 所长 - 博客园 (cnblogs.com)

  • 17
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
官网下载的最新的LWIP源码,非常详细,不但有完整的IP,TCP源码,还包括http, mttp源码。 FEATURES * IP (Internet Protocol, IPv4 and IPv6) including packet forwarding over multiple network interfaces * ICMP (Internet Control Message Protocol) for network maintenance and debugging * IGMP (Internet Group Management Protocol) for multicast traffic management * MLD (Multicast listener discovery for IPv6). Aims to be compliant with RFC 2710. No support for MLDv2 * ND (Neighbor discovery and stateless address autoconfiguration for IPv6). Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 (Address autoconfiguration) * DHCP, AutoIP/APIPA (Zeroconf), ACD (Address Conflict Detection) and (stateless) DHCPv6 * UDP (User Datagram Protocol) including experimental UDP-lite extensions * TCP (Transmission Control Protocol) with congestion control, RTT estimation fast recovery/fast retransmit and sending SACKs * raw/native API for enhanced performance * Optional Berkeley-like socket API * TLS: optional layered TCP ("altcp") for nearly transparent TLS for any TCP-based protocol (ported to mbedTLS) (see changelog for more info) * PPPoS and PPPoE (Point-to-point protocol over Serial/Ethernet) * DNS (Domain name resolver incl. mDNS) * 6LoWPAN (via IEEE 802.15.4, BLE or ZEP) APPLICATIONS * HTTP server with SSI and CGI (HTTPS via altcp) * SNMPv2c agent with MIB compiler (Simple Network Management Protocol), v3 via altcp * SNTP (Simple network time protocol) * NetBIOS name service responder * MDNS (Multicast DNS) responder * iPerf server implementation * MQTT client (TLS support via altcp)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值