前言:
硬件平台:
- 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。分别对它们进行处理如下。
- ①修改FreeRTOSConfig.h中如下,此处对应heap_4.o申请的空间,freertos会创建在heap_4.c中创建一个大数组,作为任务堆栈的使用空间,可将之改小至合适大小。
-
#define configTOTAL_HEAP_SIZE ((size_t)(29 * 1024)) //35*1024定义用户栈(任务栈),也叫HEAP区存在数据段(具体为.bss段),是一个静态数组,FreeRTOS 内核,用户动态内存申请,任务栈,任务创建,信号量创建,消息队列创建等都需要用这个空间。
②修改lwipopts.h如下,此处对应mem.o与memp.o申请的空间,MEM_SIZE作为lwip内存池数组下标,和freertos一样也会创建一个大数组,也将之改小至合适大小。(mem内存池:是用于数据的发送缓存,根据自己的实际情况改至合适大小)
-
而失能UDP功能后,lwip便不会创建属于UDP的内存池,也可以降低Sram的使用,同时也可结合opt.h这个文件修改各不同类型内存池中的内存块数量,后续我会发布下我修改后的lwipopts.h相关配置,先在此埋个坑。
-
#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行的宏定义检查屏蔽
-
//#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
-
③因为我使用 FreeRTOS 驱动 lwip,因此 lwip 的线程 栈大小,也是 可以减少内存的,即设定 TCPIP_THREAD_STACKSIZE ,在 lwipopts.h 文件中,这个线程栈的单位是按照 字 计算的哦,要注意。
-
#define TCPIP_THREAD_STACKSIZE 256
-
④在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