STM32LWIP无操作系统移植

移植平台使用STM32F407为核心芯片的正点原子开发平台,网络芯片使用LAN8720。LWIP使用官方1.4.1版本。本文章只说移植,详细说明请参考正点原子《STM32F4 LWIP开发手册》

0、环境搭建
  需要使用到空间开辟函数,我使用的是正点原子。正点原子空间开辟函数移植后可以直接使用。
1、准备工作
(1)lwip下载
  官方下载地址:http://download.savannah.nongnu.org/releases/lwip/
  lwip-1.4.1.zip为官方原码
  contrib-1.4.1.zip包含官方例程和移植时所需要的一些头文件
(2)st以太网库
  F407带有以太网MAC模块,ST提供了以太网库
  官方下载地址:https://www.st.com/en/embedded-software/stsw-stm32070.html
2、添加修改以太网库
(1)添加以太网
  解压后找到所需文件夹STM32F4x7_ETH_Driver,并修改stm32f4x7_eth_conf_template.h文件改为stm32f4x7_eth_conf.h。
  将文件夹中的三个文件stm32f4x7_eth.c、stm32f4x7_eth.h、stm32f4x7_eth_conf.h添加到工程中
(2)修改stm32f4x7_eth_conf.h

#ifndef __STM32F4x7_ETH_CONF_H
#define __STM32F4x7_ETH_CONF_H
#include "stm32f4xx.h"

#define USE_ENHANCED_DMA_DESCRIPTORS

//如果使用自己定义的延时函数的话就注销掉下面一行代码,否则使用
//默认的低精度延时函数

//#define USE_Delay    	//使用默认延时函数,因此注销掉
#ifdef USE_Delay
	#include "main.h"               
	#define _eth_delay_    Delay     //Delay为用户自己提供的高精度延时函数
                                    
#else
	#define _eth_delay_    ETH_Delay //默认的_eth_delay功能函数延时精度差
#endif

#ifdef  CUSTOM_DRIVER_BUFFERS_CONFIG
	//重新定义以太网接收和发送缓冲区的大小和数量
	#define ETH_RX_BUF_SIZE    ETH_MAX_PACKET_SIZE //接收缓冲区的大小
	#define ETH_TX_BUF_SIZE    ETH_MAX_PACKET_SIZE //发送缓冲区的大小
	#define ETH_RXBUFNB        20                  //接收缓冲区数量
	#define ETH_TXBUFNB        5                   //发送缓冲区数量
#endif

//*******************PHY配置块*******************
#ifdef USE_Delay
	#define PHY_RESET_DELAY    ((uint32_t)0x000000FF)  	//PHY复位延时
	#define PHY_CONFIG_DELAY   ((uint32_t)0x00000FFF) 	//PHY配置延时
	#define ETH_REG_WRITE_DELAY ((uint32_t)0x00000001)	//向以太网寄存器写数据时的延时
#else
	#define PHY_RESET_DELAY    ((uint32_t)0x000FFFFF)	//PHY复位延时
	#define PHY_CONFIG_DELAY   ((uint32_t)0x00FFFFFF)	//PHY配置延时
	#define ETH_REG_WRITE_DELAY ((uint32_t)0x0000FFFF)	//向以太网寄存器写数据时的延时
#endif

//LAN8720 PHY芯片的状态寄存器
#define PHY_SR				((uint16_t)31) 		//LAN8720的PHY状态寄存器地址
#define PHY_SPEED_STATUS    ((uint16_t)0x0004) 	//LAN8720 PHY速度值掩码
#define PHY_DUPLEX_STATUS   ((uint16_t)0x00010) //LAN8720 PHY连接状态值掩码  
#endif 

相比例程无需改动太多,原码中最后的部分为dp83848的状态寄存器设置,需要改为lan8720状态寄存器设置
(3)修改stm32f4x7_eth.c文件
  在这里插入图片描述

//#if defined   (__CC_ARM) /*!< ARM Compiler */
//__align(4) 
//ETH_DMADESCTypeDef  DMARxDscrTab[ETH_RXBUFNB];/* Ethernet Rx MA Descriptor */
//__align(4) 
//ETH_DMADESCTypeDef  DMATxDscrTab[ETH_TXBUFNB];/* Ethernet Tx DMA Descriptor */
//__align(4) 
//uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; /* Ethernet Receive Buffer */
//__align(4) 
//uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; /* Ethernet Transmit Buffer */

//#elif defined ( __ICCARM__ ) /*!< IAR Compiler */
//#pragma data_alignment=4
//ETH_DMADESCTypeDef  DMARxDscrTab[ETH_RXBUFNB];/* Ethernet Rx MA Descriptor */
//#pragma data_alignment=4
//ETH_DMADESCTypeDef  DMATxDscrTab[ETH_TXBUFNB];/* Ethernet Tx DMA Descriptor */
//#pragma data_alignment=4
//uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; /* Ethernet Receive Buffer */
//#pragma data_alignment=4
//uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; /* Ethernet Transmit Buffer */

//#elif defined (__GNUC__) /*!< GNU Compiler */
//ETH_DMADESCTypeDef  DMARxDscrTab[ETH_RXBUFNB] __attribute__ ((aligned (4))); /* Ethernet Rx DMA Descriptor */
//ETH_DMADESCTypeDef  DMATxDscrTab[ETH_TXBUFNB] __attribute__ ((aligned (4))); /* Ethernet Tx DMA Descriptor */
//uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE] __attribute__ ((aligned (4))); /* Ethernet Receive Buffer */
//uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE] __attribute__ ((aligned (4))); /* Ethernet Transmit Buffer */

//#elif defined  (__TASKING__) /*!< TASKING Compiler */
//__align(4) 
//ETH_DMADESCTypeDef  DMARxDscrTab[ETH_RXBUFNB];/* Ethernet Rx MA Descriptor */
//__align(4) 
//ETH_DMADESCTypeDef  DMATxDscrTab[ETH_TXBUFNB];/* Ethernet Tx DMA Descriptor */
//__align(4) 
//uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; /* Ethernet Receive Buffer */
//__align(4) 
//uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; /* Ethernet Transmit Buffer */

//#endif /* __CC_ARM */

3、添加网卡驱动
  添加lan8720.c和lan8720.h到工程,在lan8720.c中对上述屏蔽的四个数组使用动态开闭进行重定义,并添加官方外设库stm32f4xx_syscfg.c

4、添加LWIP原码
  工程中添加三个分组并添加头文件
  LWIP-NETIF:lwip-1.4.1->src->netif路径下etharp.c和ethernetif.c文件
  LWIP-CORE:lwip-1.4.1->src->core->ipv4路径下所有文件
          lwip-1.4.1->src->core路径下所有.c文件
  LWIP-API:  lwip-1.4.1->src->api路径下所有文件
5、arch文件夹
  arch文件夹中包含5个文件:
cc.h:下列为不含操作系统的定义

#ifndef __CC_H__
#define __CC_H__
#include "cpu.h"
#include "stdio.h"
//定义与平台无关的数据类型
typedef unsigned char  u8_t;
typedef signed   char  s8_t;
typedef unsigned short u16_t;
typedef signed   short s16_t;
typedef unsigned long  u32_t;
typedef signed   long  s32_t;
typedef u32_t mem_ptr_t; //内存地址型数据
typedef int sys_prot_t; //临界保护型数据
//根据不同的编译器定义了一些符号
/* define compiler specific symbols */
#if defined (__ICCARM__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT 
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#define PACK_STRUCT_USE_INCLUDES
#elif defined (__CC_ARM)
#define PACK_STRUCT_BEGIN __packed
#define PACK_STRUCT_STRUCT 
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#elif defined (__GNUC__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#elif defined (__TASKING__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#endif
//lwip用printf调试时使用到的一些类型
/*---define (sn)printf formatters for these lwip types, for lwip DEBUG/STATS--*/
#define U16_F "4d"
#define S16_F "4d"
#define X16_F "4x"
#define U32_F "8ld"
#define S32_F "8ld"
#define X32_F "8lx"
//宏定义
#ifndef LWIP_PLATFORM_ASSERT
#define LWIP_PLATFORM_ASSERT(x) \
do  {   printf("Assertion \"%s\" failed at line %d in %s\r\n", x, __LINE__, __FILE__); \
    } while(0)
#endif
#ifndef LWIP_PLATFORM_DIAG
#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
#endif
#endif

若使用操作系统则添加下列代码

//使用操作系统时的临界区保护--ucosii为例子
//当定义了OS_CRITICAL_METHOD时说明使用了ucosii
/*-------------critical region protection (depends on uC/OS-II setting)-------*/
#if OS_CRITICAL_METHOD == 1
#define SYS_ARCH_DECL_PROTECT(lev)
#define SYS_ARCH_PROTECT(lev)		CPU_INT_DIS()
#define SYS_ARCH_UNPROTECT(lev)		CPU_INT_EN()
#endif
#if OS_CRITICAL_METHOD == 3  //method 3 is used in this port.
#define SYS_ARCH_DECL_PROTECT(lev)	u32_t lev
#define SYS_ARCH_PROTECT(lev)		lev = OS_CPU_SR_Save()
#define SYS_ARCH_UNPROTECT(lev)		OS_CPU_SR_Restore(lev)
#endif

cpu.h:定义了cpu大小端模式,stm32为小端模式

#ifndef __CPU_H__
#define __CPU_H__
#define BYTE_ORDER LITTLE_ENDIAN  //小端模式
#endif /* __CPU_H__ */

perf.h:我们不做任何测量和统计,因此两个宏定义为空

#ifndef __PERF_H__
#define __PERF_H__
#define PERF_START   //空定义
#define PERF_STOP(x) //空定义 
#endif /* __PERF_H__ */

sys_arch.c:由sys_arch.txt中可知,文件中主要定义了信号量、邮箱等操作系统用到的函数。无操作系统时只是获取了系统时间为lwip提供计时。

extern uint32_t lwip_localtime;//lwip本地时间计数器,单位:ms
u32 sys_now(void){
	return lwip_localtime;
}

sys_arch.h:函数声明
6、添加通用文件:
  工程创建两个分类:
   lwip-arch:添加sys_arch.c
   lwip-com:添加lwip-comm.c。文件为正点原子代码,不再展示,无需修改

7、LWIP原码文件修改
(1)LWIP原码文件名
   正点原子自定义的sys.c和sys.h与LWIP源码中的的文件重名,因此需要修改其中一个
(2)ethernetif.c文件修改
文件中有五个函数,后三个与网卡密切相关,函数内容参考正点原子代码不再展示,说明如下:
//对low_level_init()函数简单封装,并且初始化了netif的相关字段
err_t ethernetif_init(struct netif *netif);
//对low_level_input()函数进行封装,然后将接受的数据送入制定网卡结构
err_t ethernetif_input(struct netif *netif);
//主要完成网卡的复位、协议栈网络接口管理结构体netif中相关字节的初始化、发送和接受DMA描述符链表的初始化,硬件帧校验的开启并且打开以太网
static err_t low_level_init(struct netif *netif);
//用于发送数据
static err_t low_level_output(struct netif *netif, struct pbuf *p);
//从网卡中提取数据,并将数据封装在pbuf结构体中供LWIP使用
static struct pbuf * low_level_input(struct netif *netif);

lwip源码中缺少ethernetif.h文件,需要自写

////网卡的名字
#define IFNAME0 'e'
#define IFNAME1 'n'

err_t ethernetif_init(struct netif *netif);
err_t ethernetif_input(struct netif *netif);
static err_t low_level_init(struct netif *netif);
static err_t low_level_output(struct netif *netif, struct pbuf *p);
static struct pbuf * low_level_input(struct netif *netif);
#endif

(3)修改mem.c和memp.c文件
lwip中的内存池和内存堆使用大数组,将其定义屏蔽掉并使用指针定义
在这里插入图片描述
STM32LWIP无操作系统移植_第1张图片
并在memp.c文件330行左右添加如下代码

//得到memp_memory数组大小
u32_t memp_get_memorysize(void)
{
	u32_t length=0;
	length=(
			MEM_ALIGNMENT-1 //全局型数组 为所有POOL分配的内存空间
			//MEMP_SIZE表示需要在每个POOL头部预留的空间  MEMP_SIZE = 0
			#define LWIP_MEMPOOL(name,num,size,desc)+((num)*(MEMP_SIZE+MEMP_ALIGN_SIZE(size)))
			#include "lwip/memp_std.h"
			);
	return length;
}

(4)修改icmp.c文件
修改icmp.c使其支持硬件帧校验。在195行左右更改如下

//屏蔽代码
//#if CHECKSUM_GEN_ICMP
//    /* adjust the checksum */
//    if (iecho->chksum >= PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {
//      iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1;
//    } else {
//      iecho->chksum += PP_HTONS(ICMP_ECHO << 8);
//    }
//#else /* CHECKSUM_GEN_ICMP */
//    iecho->chksum = 0;
//#endif /* CHECKSUM_GEN_ICMP */
//添加代码
#ifdef CHECKSUM_BY_HARDWARE
    iecho->chksum = 0;
#else
	/* adjust the checksum */
    if (iecho->chksum >= htons(0xffff - (ICMP_ECHO << 8))) {
      iecho->chksum += htons(ICMP_ECHO << 8) + 1;
    } else {
      iecho->chksum += htons(ICMP_ECHO << 8);
    }	
#endif

8、LWIP裁剪与配置
lwip源码中有个opt.h文件,为裁剪配置文件,但一般我们在lwipopt.h文件中对其进行配置,不在详细说明

9、主函数设计

int main(void)
{
	uint8_t err;
	SysLED_Init();
	SysTick_init();
	USART_Config();
	usmart_dev.init(168);
	FSMC_SRAM_Init();
	my_mem_init(SRAMIN);		//初始化内部内存池
	my_mem_init(SRAMEX);		//初始化外部内存池
	my_mem_init(SRAMCCM);	//初始化CCM内存池
	lwip_comm_init();
	printf("初始化完成\r\n");
	usmartled(1);
	while(1)
	{
		
		if(sysledrun[0]>=sysledrun[1])
		{
			sysledrun[0] = 0;
			SysLed();
		}
		lwip_periodic_handle();	//LWIP内核需要定时处理的函数
	}
}

你可能感兴趣的:(STM32LWIP无操作系统移植)