准备工作(源码下载)
具体下载步骤,已有许多文章,小编在此给出网址,不再介绍。。。
STM32F4驱动库下载
https://www.st.com/zh/embedded-software/stsw-stm32065.html
https://www.st.com/zh/embedded-software/stsw-stm32070.html
FreeRTOS源码下载
- 下载长期支持版FreeRTOS 202210.01 LTS
https://www.freertos.org/zh-cn-cmn-s/a00104.html
LwIP源码下载
- LwIP2.1.3下载LwIP
http://savannah.nongnu.org/projects/lwip
STM32F407不带RTOS移植LwIP
创建工程目录
├─Firmware(STM32的驱动库)
├─MDK-ARM(MDK的工程文件)
├─Middleware(FreeRTOS和LwIP源码)
│ ├─FreeRTOS
│ └─LwIP
├─SYSTEM(STM32运行的必要文件)
└─USER(用户编写)
添加文件
- Firmware中添加的文件
STSW-STM32070解压后的en.stm32f4x7_stdperiph_ETH_LwIP_V1.1.1\STM32F4x7_ETH_LwIP_V1.1.1\Libraries\下的STM32F4x7_ETH_Driver文件夹
STSW-STM32065解压后的STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries下的CMSIS和STM32F4xx_StdPeriph_Driver两个文件夹
- Middleware/FreeRTOS下的文件
FreeRTOS 202210.01 LTS解压后的FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel/下的include、portable两个文件夹以及所有.c文件
删除portable文件夹中除RVDS,MemMang以外的所有文件夹
- Middleware/LwIP下的文件
lwip解压后的lwip-2.1.3\src/下的所有文件
- 其他文件在附录源码工程中自行查看
MDK工程文件添加
以太网板级外设外设驱动代码
中断优先级的分组设置
为了方便移植FreeRTOS,中断优先级的分组做如下处理
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//NVIC仅支持抢占式优先级
MAC外设+LAN8720A(PHY)配置
小编使用MCU具体型号为STM32F407VET6,PHY芯片为LAN8720A,关于LAN8720A芯片使用在stm32f4x7_eth_conf.h文件做如下定义
#define LAN8720_PHY_ADDRESS 0x00 //LAN8720 PHY芯片地址.
/* The LAN8720A PHY status register */
#define PHY_SR ((uint16_t)0x1F) /* PHY status register Offset */
#define PHY_SPEED_STATUS ((uint16_t)0x0004) /* PHY Speed mask */
#define PHY_DUPLEX_STATUS ((uint16_t)0x0010) /* PHY Duplex mask */
- LAN8720A与STM32F407VE的连接有如下定义,移植时需要根据自己的板子做修改
ETH_MDIO ------------> PA2
ETH_MDC -------------> PC1
ETH_RMII_REF_CLK-----> PA1
ETH_RMII_CRS_DV -----> PA7
ETH_RMII_RXD0 -------> PC4
ETH_RMII_RXD1 -------> PC5
ETH_RMII_TX_EN ------> PB11
ETH_RMII_TXD0 -------> PB12
ETH_RMII_TXD1 -------> PB13
LAN8720_RST ---------> PD3
那么GPIO端口配置代码如下
/**
* @brief Configures the different GPIO ports.
*/
static void ETH_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOC|
RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOD , ENABLE);//使能GPIO时钟 RMII接口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); //使能SYSCFG时钟
SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII); //MAC和PHY之间使用RMII接口
//配置PA1 PA2 PA7
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_ETH); //引脚复用到网络接口上
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_ETH);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_ETH);
//配置PC1,PC4 and PC5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource1, GPIO_AF_ETH); //引脚复用到网络接口上
GPIO_PinAFConfig(GPIOC, GPIO_PinSource4, GPIO_AF_ETH);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_ETH);
//配置PB11, PG12 and PB13
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_ETH);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_ETH);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_ETH);
//配置PD3为推完输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推完输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOD, &GPIO_InitStructure);
PDout(3)=0; //硬件复位LAN8720
delay_ms(50);
PDout(3)=1; //复位结束
}
MAC外设和中断配置:
/**
* @brief Configures the Ethernet Interface
*/
static uint8_t ETH_MACDMA_Config(void)
{
uint8_t retval;
ETH_InitTypeDef ETH_InitStructure;
//使能以太网时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_ETH_MAC | RCC_AHB1Periph_ETH_MAC_Tx |RCC_AHB1Periph_ETH_MAC_Rx, ENABLE);
ETH_DeInit(); //AHB总线重启以太网
ETH_SoftwareReset(); //软件重启网络
while (ETH_GetSoftwareResetStatus() == SET);//等待软件重启网络完成
ETH_StructInit(Ð_InitStructure); //初始化网络为默认值
///网络MAC参数设置
ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable; //开启网络自适应功能
ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable; //关闭反馈
ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable; //关闭重传功能
ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable; //关闭自动去除PDA/CRC功能
ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Disable; //关闭接收所有的帧
ETH_InitStructure.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable;//允许接收所有广播帧
ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable; //关闭混合模式的地址过滤
ETH_InitStructure.ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_Perfect;//对于组播地址使用完美地址过滤
ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect; //对单播地址使用完美地址过滤
#ifdef CHECKSUM_BY_HARDWARE
ETH_InitStructure.ETH_ChecksumOffload = ETH_ChecksumOffload_Enable; //开启ipv4和TCP/UDP/ICMP的帧校验和卸载
#endif
//当我们使用帧校验和卸载功能的时候,一定要使能存储转发模式,存储转发模式中要保证整个帧存储在FIFO中,
//这样MAC能插入/识别出帧校验值,当真校验正确的时候DMA就可以处理帧,否则就丢弃掉该帧
ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable; //开启丢弃TCP/IP错误帧
ETH_InitStructure.ETH_ReceiveStoreForward = ETH_ReceiveStoreForward_Enable; //开启接收数据的存储转发模式
ETH_InitStructure.ETH_TransmitStoreForward = ETH_TransmitStoreForward_Enable; //开启发送数据的存储转发模式
ETH_InitStructure.ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Disable; //禁止转发错误帧
ETH_InitStructure.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Disable; //不转发过小的好帧
ETH_InitStructure.ETH_SecondFrameOperate = ETH_SecondFrameOperate_Enable; //打开处理第二帧功能
ETH_InitStructure.ETH_AddressAlignedBeats = ETH_AddressAlignedBeats_Enable; //开启DMA传输的地址对齐功能
ETH_InitStructure.ETH_FixedBurst = ETH_FixedBurst_Enable; //开启固定突发功能
ETH_InitStructure.ETH_RxDMABurstLength = ETH_RxDMABurstLength_32Beat; //DMA发送的最大突发长度为32个节拍
ETH_InitStructure.ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat; //DMA接收的最大突发长度为32个节拍
ETH_InitStructure.ETH_DMAArbitration = ETH_DMAArbitration_RoundRobin_RxTx_2_1;
/* Configure Ethernet */
retval = ETH_Init(Ð_InitStructure, LAN8720_PHY_ADDRESS);
/* Enable the Ethernet Rx Interrupt */
ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R, ENABLE);
return retval;
}
/**
* @brief Configures and enable the Ethernet global interrupt.
*/
static void ETH_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the Ethernet global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = ETH_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
为了保证移植LwIP后MAC地址设置后不会与其他设备重复,故从MCU中获取唯一的MAC地址,读取代码如下
/**
* @brief 获取mac地址
* @param pMacBuf 存储MAC地址buffer的首地址
*/
void GET_STM32MACID(uint8_t* pMacBuf)
{
uint32_t uiMcuId = 0;
uint8_t i = 0;
uint8_t pMcuID[15] = {0};
uint32_t CpuID[3] = {0};
if(pMacBuf == NULL)return;
//获取CPU唯一ID
#if 0//STM32F1系列
CpuID[0] =*(vuint32_t*)(0x1ffff7e8); //按全字(32位)读取
CpuID[1] =*(vuint32_t*)(0x1ffff7ec);
CpuID[2] =*(vuint32_t*)(0x1ffff7f0);
#endif
#if 1//STM32F4系列
CpuID[0]=*(uint32_t*)(0x1fff7a10);
CpuID[1]=*(uint32_t*)(0x1fff7a14);
CpuID[2]=*(uint32_t*)(0x1fff7a18);
#endif
// printf("MCU UID: %08X-%08X-%08X\r\n",CpuID[0],CpuID[1],CpuID[2]);
// printf("MCU Flash Size: %dK\r\n", *(uint16_t *)(0X1FFF7a22));
//按字节(8位)读取
pMcuID[0] = (uint8_t)(CpuID[0] & 0x000000FF);
pMcuID[1] = (uint8_t)((CpuID[0] & 0xFF00) >>8);
pMcuID[2] = (uint8_t)((CpuID[0] & 0xFF0000) >>16);
pMcuID[3] = (uint8_t)((CpuID[0] & 0xFF000000) >>24);
pMcuID[4] = (uint8_t)(CpuID[1] & 0xFF);
pMcuID[5] = (uint8_t)((CpuID[1] & 0xFF00) >>8);
pMcuID[6] = (uint8_t)((CpuID[1] & 0xFF0000) >>16);
pMcuID[7] = (uint8_t)((CpuID[1] & 0xFF000000) >>24);
pMcuID[8] = (uint8_t)(CpuID[2] & 0xFF);
pMcuID[9] = (uint8_t)((CpuID[2] & 0xFF00) >>8);
pMcuID[10] = (uint8_t)((CpuID[2] & 0xFF0000) >>16);
pMcuID[11] = (uint8_t)((CpuID[2] & 0xFF000000) >>24);
uiMcuId = (CpuID[0]>>1)+(CpuID[1]>>2)+(CpuID[2]>>3);
for(i=0; i<12; i++) //获取McuID[12]
{
pMcuID[12] += pMcuID[i];
}
for(i=0; i<12; i++) //获取McuID[13]
{
pMcuID[13] ^= pMcuID[i];
}
//获取MAC地址
pMacBuf[0] = (uint8_t)(uiMcuId & 0xF0);
pMacBuf[1] = (uint8_t)((uiMcuId & 0xFF00) >>8);
pMacBuf[2] = (uint8_t)((uiMcuId & 0xFF0000) >>16);
pMacBuf[3] = (uint8_t)((uiMcuId & 0xFF000000) >>24);
pMacBuf[4] = pMcuID[12];
pMacBuf[5] = pMcuID[13];
}
整理以上配置程序,并加入中断函数
/**
* @brief ETH_BSP_Config
* @retval 0:成功
1:失败
*/
uint8_t ETH_BSP_Config(void)
{
ETH_GPIO_Config();
ETH_NVIC_Config();
ETH_MACDMA_Config();
if(ETH_MACDMA_Config()==ETH_SUCCESS)return 0;
return 1;
}
//以太网中断
void ETH_IRQHandler(void)
{
//从网络缓冲区中读取接收到的数据包并将其发送给LWIP处理
ethernetif_input(&g_lwip_netif);
/* Clear the interrupt flags. */
/* Clear the Eth DMA Rx IT pending bits */
ETH_DMAClearITPendingBit(ETH_DMA_IT_R);
ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);
}
LwIP移植的必要文件添加和创建
配置文件和部分定义文件
- cc.h
#ifndef __CC_H__
#define __CC_H__
#include "sys.h"
typedef int sys_prot_t;
#if defined (__GNUC__) & !defined (__CC_ARM)
#define LWIP_TIMEVAL_PRIVATE 0
#include <sys/time.h>
#endif
/* 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 (__GNUC__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#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 (__TASKING__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#endif
#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 /* __CC_H__ */
- lwipopts.h->LwIP的配置文件(!!!),copy了正点原子的LwIP移植代码,并根据自己需求做了一些修改
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__
/**
SYS_LIGHTWEIGHT_PROT==1:如果您确实需要任务间保护
*/
#define SYS_LIGHTWEIGHT_PROT 0
/* NO_SYS 表示无操作系统模拟层,无操作系统为1,有操作系统设置为0
注意这个参数设置会编译不同 */
#define NO_SYS 1
/**
* NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1
* Mainly for compatibility to old versions.
*/
#define NO_SYS_NO_TIMERS 0
/* ---------- 内存选项 ---------- */
/* 内存对齐,按照 4 字节对齐 */
#define MEM_ALIGNMENT 4
/* 堆内存的大小,如果需要更大的堆内存,那么设置高一点 */
#define MEM_SIZE (16*1024)
/* MEMP_NUM_PBUF: 设置内存池的数量 */
#define MEMP_NUM_PBUF 25
/* MEMP_NUM_UDP_PCB: UDP协议控制块的数量. */
#define MEMP_NUM_UDP_PCB 4
/* MEMP_NUM_TCP_PCB: TCP的数量. */
#define MEMP_NUM_TCP_PCB 4
/* MEMP_NUM_TCP_PCB_LISTEN: 监听TCP的数量. */
#define MEMP_NUM_TCP_PCB_LISTEN 2
/* MEMP_NUM_TCP_SEG: 同时排队的TCP的数量段. */
#define MEMP_NUM_TCP_SEG 150
/* MEMP_NUM_SYS_TIMEOUT: 超时模拟活动的数量. */
#define MEMP_NUM_SYS_TIMEOUT 6
/* ---------- Pbuf选项 ---------- */
/* PBUF_POOL 内存池中每个内存块大小 */
#define PBUF_POOL_SIZE 20
/* PBUF_POOL_BUFSIZE: pbuf池中每个pbuf的大小. */
#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN)
/* ---------- TCP选项 ---------- */
#define LWIP_TCP 1
#define TCP_TTL 255
/* 控制TCP是否应该对到达的段进行排队
秩序。如果你的设备内存不足,定义为0. */
#define TCP_QUEUE_OOSEQ 0
/* TCP最大段大小 */
#define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */
/* TCP发送者缓冲区空间(字节). */
#define TCP_SND_BUF (11*TCP_MSS)
/* TCP_SND_QUEUELEN: TCP发送缓冲区空间。这必须是至少
需要(2 * TCP_SND_BUF/TCP_MSS)才能正常工作 */
#define TCP_SND_QUEUELEN (8* TCP_SND_BUF/TCP_MSS)
/* TCP接收窗口 */
#define TCP_WND (20*TCP_MSS)
/* ---------- ICMP 选项 ---------- */
#define LWIP_ICMP 1
/* ---------- DHCP 选项 ---------- */
/* 如果您希望DHCP配置为,请将LWIP_DHCP定义为1 */
#define LWIP_DHCP 1
/* ---------- UDP 选项 ---------- */
#define LWIP_UDP 1
#define UDP_TTL 255
/* ---------- Statistics 选项 ---------- */
#define LWIP_STATS 0
#define LWIP_PROVIDE_ERRNO 1
/* ---------- 链接回调选项 ---------- */
/* WIP_NETIF_LINK_CALLBACK==1:支持来自接口的回调函数
每当链接改变(例如,向下链接)
*/
#define LWIP_NETIF_LINK_CALLBACK 1
/*
--------------------------------------
---------- Checksum options ----------
--------------------------------------
*/
/*
The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware:
- To use this feature let the following define uncommented.
- To disable it and process by CPU comment the the checksum.
*/
#define CHECKSUM_BY_HARDWARE
#ifdef CHECKSUM_BY_HARDWARE
/* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/
#define CHECKSUM_GEN_IP 0
/* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/
#define CHECKSUM_GEN_UDP 0
/* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/
#define CHECKSUM_GEN_TCP 0
/* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/
#define CHECKSUM_CHECK_IP 0
/* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/
#define CHECKSUM_CHECK_UDP 0
/* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/
#define CHECKSUM_CHECK_TCP 0
/* CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/
#define CHECKSUM_GEN_ICMP 0
#else
/* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/
#define CHECKSUM_GEN_IP 1
/* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/
#define CHECKSUM_GEN_UDP 1
/* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/
#define CHECKSUM_GEN_TCP 1
/* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/
#define CHECKSUM_CHECK_IP 1
/* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/
#define CHECKSUM_CHECK_UDP 1
/* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/
#define CHECKSUM_CHECK_TCP 1
/* CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/
#define CHECKSUM_GEN_ICMP 1
#endif
/*
----------------------------------------------
---------- Sequential layer options ----------
----------------------------------------------
*/
/**
* LWIP_NETCONN==1:启用Netconn API(需要使用api_lib.c)
*/
#define LWIP_NETCONN 0
/*
------------------------------------
---------- Socket options ----------
------------------------------------
*/
/**
* LWIP_SOCKET==1:启用Socket API(要求使用Socket .c)
*/
#define LWIP_SOCKET 0
#endif /* __LWIPOPTS_H__ */
ethernetif.c->LwIP的底层驱动编写(!!!)
#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/timeouts.h"
#include "netif/etharp.h"
#include "lwip/err.h"
#include "ethernetif.h"
#include "ethernet/ethernet.h"//stm32f407的MAC外设配置的头文件
//网卡名
#define IFNAME0 's'
#define IFNAME1 't'
//以太网Rx & Tx DMA描述符
extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB];
//以太网收发缓冲区
extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE],Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE];
//跟踪当前发送和接收描述符的全局指针
extern ETH_DMADESCTypeDef *DMATxDescToSet,*DMARxDescToGet;
//最后接收帧信息的全局指针
extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos;
/**
* In this function, the hardware should be initialized.
* Called from ethernetif_init().
*
* @param netif the already initialized lwip network interface structure
* for this ethernetif
*/
static void low_level_init(struct netif *netif)
{
uint8_t s_local_mac[6];
uint32_t i;
netif->hwaddr_len = ETHARP_HWADDR_LEN;//设置nettif MAC硬件地址长度
//初始化MAC地址,设置什么地址由用户自己设置,但是不能与网络中其他设备MAC地址重复
GET_STM32MACID(s_local_mac);//获取STM32的唯一MAC地址
netif->hwaddr[0]=s_local_mac[0];
netif->hwaddr[1]=s_local_mac[1];
netif->hwaddr[2]=s_local_mac[2];
netif->hwaddr[3]=s_local_mac[3];
netif->hwaddr[4]=s_local_mac[4];
netif->hwaddr[5]=s_local_mac[5];
printf("网卡%c%c的MAC地址为:%x-%x-%x-%x-%x-%x\r\n",netif->name[0],netif->name[1],
netif->hwaddr[0],netif->hwaddr[1],netif->hwaddr[2],netif->hwaddr[3],netif->hwaddr[4],netif->hwaddr[5]);
netif->mtu = 1500;//nettif最大传输单位
//网卡状态信息标志位,是很重要的控制字段,它包括网卡功能使能、广播
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP;//接受广播地址和ARP流量
ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr);//初始化以太网MAC中的MAC地址
ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);//初始化Tx描述符列表:链式模式
ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);//初始化Rx描述符列表:链式模式
//初始化Tx描述符列表:链式模式
{
for(i=0; i<ETH_RXBUFNB; i++)
{
ETH_DMARxDescReceiveITConfig(&DMARxDscrTab[i], ENABLE);
}
}
#ifdef CHECKSUM_BY_HARDWARE
//使能Tx帧的校验和插入
{
for(i=0; i<ETH_TXBUFNB; i++)
{
ETH_DMATxDescChecksumInsertionConfig(&DMATxDscrTab[i], ETH_DMATxDesc_ChecksumTCPUDPICMPFull);
}
}
#endif
ETH_Start();//使能MAC和DMA传输和接收
}
/**
* This function should do the actual transmission of the packet. The packet is
* contained in the pbuf that is passed to the function. This pbuf
* might be chained.
*
* @param netif the lwip network interface structure for this ethernetif
* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
* @return ERR_OK if the packet could be sent
* an err_t value if the packet couldn't be sent
*
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
* strange results. You might consider waiting for space in the DMA queue
* to become availale since the stack doesn't retry to send a packet
* dropped because of memory failure (except for the TCP timers).
*/
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
// static xSemaphoreHandle xTxSemaphore = NULL;
err_t errval;
struct pbuf *q;
uint8_t *buffer ;
__IO ETH_DMADESCTypeDef *DmaTxDesc;
uint16_t framelength = 0;
uint32_t bufferoffset = 0;
uint32_t byteslefttocopy = 0;
uint32_t payloadoffset = 0;
DmaTxDesc = DMATxDescToSet;
buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr);
bufferoffset = 0;
#if ETH_PAD_SIZE
pbuf_remove_header(p, ETH_PAD_SIZE); /* drop the padding word */
#endif
for(q = p; q != NULL; q = q->next)//从pbuf中拷贝要发送的数据
{
//判断此发送描述符是否有效,即判断此发送描述符是否归以太网DMA所有
if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (uint32_t)RESET)
{
errval=ERR_USE;
goto error;
}
//获取当前lwIP缓冲区中的字节
byteslefttocopy = q->len;
payloadoffset = 0;
/*将pbuf中要发送的数据写入到以太网发送描述符中,有时候我们要发送的数据可能大于一个以太网
描述符的Tx Buffer,因此我们需要分多次将数据拷贝到多个发送描述符中*/
while((byteslefttocopy+bufferoffset)>ETH_TX_BUF_SIZE)
{
//将数据拷贝到以太网发送描述符的Tx Buffer中
memcpy((u8_t*)((u8_t*)buffer+bufferoffset),(u8_t*)((u8_t*)q->payload+payloadoffset),
(ETH_TX_BUF_SIZE-bufferoffset));
//DmaTxDsc指向下一个发送描述符
DmaTxDesc = (ETH_DMADESCTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);
//检查新的发送描述符是否有效
if((DmaTxDesc->StatusÐ_DMATxDesc_OWN)!=(u32)RESET)
{
errval = ERR_USE;
goto error;
}
buffer = (u8 *)(DmaTxDesc->Buffer1Addr);//更新buffer地址,指向新的发送描述符的Tx Buffer
byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);
payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);
framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);
bufferoffset = 0;
}
//拷贝剩余的数据
memcpy( (u8_t*)((u8_t*)buffer+bufferoffset),(u8_t*)((u8_t*)q->payload+payloadoffset),byteslefttocopy );
bufferoffset = bufferoffset + byteslefttocopy;
framelength = framelength + byteslefttocopy;
}
//当所有要发送的数据都放进发送描述符的Tx Buffer以后就可发送此帧了
ETH_Prepare_Transmit_Descriptors(framelength);
errval = ERR_OK;
error:
//发送缓冲区发生下溢,一旦发送缓冲区发生下溢TxDMA会进入挂起状态
if((ETH->DMASRÐ_DMASR_TUS)!= (uint32_t)RESET)
{
ETH->DMASR = ETH_DMASR_TUS;//清除下溢标志
/* 当发送帧中出现下溢错误的时候TxDMA会挂起,这时候需要向DMATPDR寄存器
随便写入一个值来将其唤醒,此处我们写0 */
ETH->DMATPDR=0;
}
return errval;
}
/**
* Should allocate a pbuf and transfer the bytes of the incoming
* packet from the interface into the pbuf.
*
* @param netif the lwip network interface structure for this ethernetif
* @return a pbuf filled with the received packet (including MAC header)
* NULL on memory error
*/
static struct pbuf * low_level_input(struct netif *netif)
{
struct pbuf *p= NULL, *q;
u32_t len;
FrameTypeDef frame;
u8 *buffer;
__IO ETH_DMADESCTypeDef *DMARxDesc;
uint32_t bufferoffset = 0;
uint32_t payloadoffset = 0;
uint32_t byteslefttocopy = 0;
uint32_t i=0;
frame = ETH_Get_Received_Frame_interrupt();//接收一帧数据帧
//获取数据包的大小并将其放入“len”变量中
len = frame.length;
buffer = (u8 *)frame.buffer;
//从Lwip缓冲池中分配一个pbuf链
if(len > 0) p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
//pbuf链分配成功
if (p != NULL)
{
DMARxDesc = frame.descriptor;//获取接收描述符链表中的第一个描述符
bufferoffset = 0;
for(q = p; q != NULL; q = q->next)
{
byteslefttocopy = q->len;
payloadoffset = 0;
//检查当前pbuf中要复制的字节长度是否大于Rx缓冲区大小
while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE )
{
//将数据复制到pbuf
memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset),
(u8_t*)((u8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));
//指向下一个描述符
DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr);
buffer = (unsigned char *)(DMARxDesc->Buffer1Addr);
byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);
payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);
bufferoffset = 0;
}
//复制pbuf中的剩余数据
memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset),
(u8_t*)((u8_t*)buffer + bufferoffset), byteslefttocopy);
bufferoffset = bufferoffset + byteslefttocopy;
}
}
DMARxDesc =frame.descriptor;//向DMA释放描述符
//在Rx描述符中设置自己的位:将缓冲区返回给DMA
for(i=0; i<DMA_RX_FRAME_infos->Seg_Count; i++)
{
DMARxDesc->Status = ETH_DMARxDesc_OWN;
DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr);
}
DMA_RX_FRAME_infos->Seg_Count =0;//清除段计数
//当设置Rx缓冲区不可用标志时:清除它并恢复接收
if((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET)
{
ETH->DMASR = ETH_DMASR_RBUS;//清除接收缓冲区不可用标志
/* 当接收缓冲区不可用的时候 RxDMA 会进去挂起状态,
通过向 DMARPDR 写入任意一个值来唤醒 Rx DMA */
ETH->DMARPDR = 0;
}
return p;
}
/**
* This function is the ethernetif_input task, it is processed when a packet
* is ready to be read from the interface. It uses the function low_level_input()
* that should handle the actual reception of bytes from the network
* interface. Then the type of the received packet is determined and
* the appropriate input function is called.
*
* @param netif the lwip network interface structure for this ethernetif
*/
err_t ethernetif_input(struct netif *netif)
{
struct pbuf *p;
err_t err;
/* move received packet into a new pbuf */
p = low_level_input(netif);
/* no packet could be read, silently ignore this */
if (p == NULL) return ERR_MEM;
/* entry point to the LwIP stack */
err = netif->input(p, netif);
if (err != ERR_OK)
{
LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
pbuf_free(p);
}
return err;
}
/**
* Should be called at the beginning of the program to set up the
* network interface. It calls the function low_level_init() to do the
* actual setup of the hardware.
*
* This function should be passed as a parameter to netif_add().
*
* @param netif the lwip network interface structure for this ethernetif
* @return ERR_OK if the loopif is initialized
* ERR_MEM if private data couldn't be allocated
* any other err_t on error
*/
err_t ethernetif_init(struct netif *netif)
{
LWIP_ASSERT("netif != NULL", (netif != NULL));
#if LWIP_NETIF_HOSTNAME
/* Initialize interface hostname */
netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
netif->output = etharp_output;
netif->linkoutput = low_level_output;
/* initialize the hardware */
low_level_init(netif);
return ERR_OK;
}
LwIP时基编写
LwIP运行需要一个时钟,在操作系统中由操作系统提供时钟,而在裸机移植中,需要用户编写一个时基程序,
本移植例程中,小编使用TIM7定时器为LwIP提供一个时基,sys_arch.c文件内容如下
u32 lwip_localtime; //lwip本地时间计数器,单位:ms
//定时器11中断服务函数
void TIM7_IRQHandler(void)
{
if(TIM_GetITStatus(TIM7,TIM_IT_Update)!=RESET)//溢出中断
{
lwip_localtime +=10; //加10
}
TIM_ClearITPendingBit(TIM7,TIM_IT_Update);//清除中断标志位
}
//通用定时器11中断初始化->裸机移植LwIP提供时钟
//100khz的频率,计数1000为10ms
void TIM7_Int_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7,ENABLE); ///使能TIM7时钟
TIM_TimeBaseInitStructure.TIM_Prescaler=840-1; //定时器分频
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInitStructure.TIM_Period=1000-1; //自动重装载值
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM7,&TIM_TimeBaseInitStructure);
TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE); //允许定时器11更新中断
TIM_Cmd(TIM7,ENABLE); //使能定时器11
NVIC_InitStructure.NVIC_IRQChannel=TIM7_IRQn; //定时器11中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=6; //抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
//为LWIP提供计时
u32_t sys_now(void){
return lwip_localtime;
}
DHCP功能实现
LwIP的应用,实现一个DHCP功能,copy了正点原子移植例程中的lwip_comm.h和lwip_comm.c两个文件并修改为netconf.c,netconf.h文件,做了如下修改
- netconf.h
#ifndef _NETCONF_H
#define _NETCONF_H
#include "ethernet/ethernet.h"
//DHCP进程状态
#define LWIP_DHCP_OFF (uint8_t) 0 //DHCP服务器关闭状态
#define LWIP_DHCP_START (uint8_t) 1 //DHCP服务器启动状态
#define LWIP_DHCP_WAIT_ADDRESS (uint8_t) 2 //DHCP服务器等待分配IP状态
#define LWIP_DHCP_ADDRESS_ASSIGNED (uint8_t) 3 //DHCP服务器地址已分配状态
#define LWIP_DHCP_TIMEOUT (uint8_t) 4 //DHCP服务器超时状态
#define LWIP_DHCP_LINK_DOWN (uint8_t) 5 //DHCP服务器链接失败状态
#define LWIP_MAX_DHCP_TRIES 4 //DHCP服务器最大重试次数
//lwip控制结构体
typedef struct
{
uint8_t remoteip[4];//远端主机IP地址
uint8_t ip[4]; //本机IP地址
uint8_t netmask[4];//子网掩码
uint8_t gateway[4];//默认网关的IP地址
volatile uint8_t dhcpstatus;//dhcp状态
//0,未获取DHCP地址;
//1,进入DHCP获取状态
//2,成功获取DHCP地址
//0XFF,获取失败.
}__lwip_dev;
extern __lwip_dev g_lwipdev; //lwip控制结构体
void lwip_pkt_handle(void);
void lwip_periodic_handle(void);
uint8_t LwIP_Init(void);
void lwip_dhcp_process_handle(void);
#endif
- netconf.c
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/tcp.h"
#include "lwip/tcpip.h"
#include "lwip/udp.h"
#include "netif/etharp.h"
#include "lwip/dhcp.h"
#include "lwip/init.h"
#include "lwip/timeouts.h"
#include "lwip/netif.h"
#include "ethernetif.h"
#include "LwIP_arch/netconf.h"
__lwip_dev g_lwipdev; //lwip控制结构体
struct netif g_lwip_netif; //定义一个全局的网络接口
#if LWIP_DHCP
uint32_t g_dhcp_fine_timer = 0; //DHCP精细处理计时器
__IO uint8_t g_lwip_dhcp_state = LWIP_DHCP_OFF; //DHCP状态初始化
#endif
uint32_t LinkTimer = 0;Link精细处理计时器
//检测PHY链路状态的实际间隔(单位:ms)
#ifndef LINK_TIMER_INTERVAL
#define LINK_TIMER_INTERVAL 1000
#endif
void lwip_link_status_updated(struct netif *netif);
void lwip_dhcp_process(struct netif *netif);
/**
* @breif lwip 默认IP设置
* @param lwipx: lwip控制结构体指针
* @retval 无
*/
void lwip_comm_default_ip_set(__lwip_dev *lwipx)
{
//默认远端IP为:192.168.0.120
lwipx->remoteip[0] = 192;
lwipx->remoteip[1] = 168;
lwipx->remoteip[2] = 0;
lwipx->remoteip[3] = 120;
//默认本地IP为:192.168.0.30
lwipx->ip[0] = 192;
lwipx->ip[1] = 168;
lwipx->ip[2] = 0;
lwipx->ip[3] = 30;
//默认子网掩码:255.255.255.0
lwipx->netmask[0] = 255;
lwipx->netmask[1] = 255;
lwipx->netmask[2] = 255;
lwipx->netmask[3] = 0;
//默认网关:192.168.1.1
lwipx->gateway[0] = 192;
lwipx->gateway[1] = 168;
lwipx->gateway[2] = 1;
lwipx->gateway[3] = 1;
lwipx->dhcpstatus = 0;//没有DHCP
}
/**
* @breif LWIP初始化(LWIP启动的时候使用)
* @param 无
* @retval 0,成功
1,内存错误
2,以太网芯片初始化失败
3,网卡添加失败.
*/
uint8_t LwIP_Init(void)
{
struct netif *netif_init_flag; //调用netif_add()函数时的返回值,用于判断网络初始化是否成功
ip_addr_t ipaddr; //ip地址
ip_addr_t netmask; //子网掩码
ip_addr_t gw; //默认网关
// if(ETH_Mem_Malloc())return 1; //内存申请失败
if(ETH_BSP_Config())return 2; //初始化LAN8720失败
lwip_init(); //初始化LWIP内核
lwip_comm_default_ip_set(&g_lwipdev);//设置默认IP等信息
#if LWIP_DHCP //使用动态IP
ipaddr.addr = 0;
netmask.addr = 0;
gw.addr = 0;
#else //使用静态IP
IP4_ADDR(&ipaddr,g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);
IP4_ADDR(&netmask,g_lwipdev.netmask[0],g_lwipdev.netmask[1] ,g_lwipdev.netmask[2],g_lwipdev.netmask[3]);
IP4_ADDR(&gw,g_lwipdev.gateway[0],g_lwipdev.gateway[1],g_lwipdev.gateway[2],g_lwipdev.gateway[3]);
printf("静态IP地址........%d.%d.%d.%d\r\n",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);
printf("子网掩码..........%d.%d.%d.%d\r\n",g_lwipdev.netmask[0],g_lwipdev.netmask[1],g_lwipdev.netmask[2],g_lwipdev.netmask[3]);
printf("默认网关..........%d.%d.%d.%d\r\n",g_lwipdev.gateway[0],g_lwipdev.gateway[1],g_lwipdev.gateway[2],g_lwipdev.gateway[3]);
#endif
netif_init_flag=netif_add(&g_lwip_netif,(const ip_addr_t *)&ipaddr,(const ip_addr_t *)&netmask,
(const ip_addr_t *)&gw,NULL,ðernetif_init,ðernet_input);//向网卡列表中添加一个网口
#if LWIP_DHCP //如果使用DHCP的话
g_lwipdev.dhcpstatus=0; //DHCP标记为0
#endif
if(netif_init_flag==NULL)return 3;//网卡添加失败
else//网口添加成功后,设置netif为默认值,并且打开netif网口
{
netif_set_default(&g_lwip_netif);//设置netif为默认网口
if (netif_is_link_up(&g_lwip_netif)) netif_set_up(&g_lwip_netif);//打开netif网口
else netif_set_down(&g_lwip_netif);
#if LWIP_DHCP //如果使用DHCP的话
lwip_link_status_updated(&g_lwip_netif);// DHCP链接状态更新函数
netif_set_link_callback(&g_lwip_netif, lwip_link_status_updated);
#endif
}
return 0;//操作OK.
}
/**
* @brief 通知用户网络接口配置状态
* @param netif:网卡控制块
*/
void lwip_link_status_updated(struct netif *netif)
{
if(netif_is_up(netif))
{
#if LWIP_DHCP
/* Update DHCP state machine */
g_lwip_dhcp_state = LWIP_DHCP_START;
#endif /* LWIP_DHCP */
}
else
{
#if LWIP_DHCP
/* Update DHCP state machine */
g_lwip_dhcp_state = LWIP_DHCP_LINK_DOWN;
#endif /* LWIP_DHCP */
}
}
/* This function is called periodically each second */
/* It checks link status for ethernet controller */
static void ETH_CheckLinkStatus(void)
{
static uint8_t status = 0;
//读取link状态
uint32_t t = ETH_ReadPHYRegister(LAN8720_PHY_ADDRESS,PHY_BSR)&PHY_SPEED_STATUS;
/* If we have link and previous check was not yet */
if (t && !status) {
/* Set link up */
netif_set_link_up(&g_lwip_netif);
printf ("The network cable is connected \r\n");
status = 1;
}
/* If we don't have link and it was on previous check */
if (!t && status) {
/* Set link down */
netif_set_link_down(&g_lwip_netif);
printf ("The network cable is not connected \r\n");
status = 0;
}
}
/**
* @breif LWIP轮询任务
*/
void lwip_periodic_handle(void)
{
sys_check_timeouts();
#if LWIP_DHCP /* 如果使用DHCP */
/* Fine DHCP periodic process every 500ms */
if (sys_now() - g_dhcp_fine_timer >= DHCP_FINE_TIMER_MSECS)
{
g_dhcp_fine_timer = sys_now();
/* process DHCP state machine */
lwip_dhcp_process(&g_lwip_netif);
}
#endif
/* Check link status periodically */
if ((sys_now() - LinkTimer) >= LINK_TIMER_INTERVAL) {
ETH_CheckLinkStatus();
LinkTimer=sys_now();
}
}
#if LWIP_DHCP /* 如果使用DHCP */
/**
* @brief lwIP的DHCP分配进程
* @param netif:网卡控制块
* @retval 无
*/
void lwip_dhcp_process(struct netif *netif)
{
uint32_t ip = 0;
uint32_t netmask = 0;
uint32_t gw = 0;
struct dhcp *dhcp;
g_lwipdev.dhcpstatus = 1;//DHCP标记为1
switch (g_lwip_dhcp_state)//根据DHCP状态进入执行相应的动作
{
case LWIP_DHCP_START:{
//对IP地址、网关地址及子网页码清零操作
ip_addr_set_zero_ip4(&netif->ip_addr);
ip_addr_set_zero_ip4(&netif->netmask);
ip_addr_set_zero_ip4(&netif->gw);
//设置DHCP的状态为等待分配IP地址
g_lwip_dhcp_state = LWIP_DHCP_WAIT_ADDRESS;
//开启DHCP
dhcp_start(netif);
printf("Looking for DHCP server,please wait...\r\n");
}break;
case LWIP_DHCP_WAIT_ADDRESS:{
ip = g_lwip_netif.ip_addr.addr; //读取新IP地址
netmask = g_lwip_netif.netmask.addr; //读取子网掩码
gw = g_lwip_netif.gw.addr; //读取默认网关
if(dhcp_supplied_address(netif))
{
g_lwip_dhcp_state = LWIP_DHCP_ADDRESS_ASSIGNED;
if (ip != 0)
{
printf("DHCP分配成功......\r\n");
g_lwipdev.dhcpstatus = 2;//DHCP成功
//解析出通过DHCP获取到的IP地址 */
g_lwipdev.ip[3] = (uint8_t)(ip >> 24);
g_lwipdev.ip[2] = (uint8_t)(ip >> 16);
g_lwipdev.ip[1] = (uint8_t)(ip >> 8);
g_lwipdev.ip[0] = (uint8_t)(ip);
printf("通过DHCP获取到IP地址......%d.%d.%d.%d\r\n", g_lwipdev.ip[0], g_lwipdev.ip[1], g_lwipdev.ip[2], g_lwipdev.ip[3]);
//解析通过DHCP获取到的子网掩码地址
g_lwipdev.netmask[3] = (uint8_t)(netmask >> 24);
g_lwipdev.netmask[2] = (uint8_t)(netmask >> 16);
g_lwipdev.netmask[1] = (uint8_t)(netmask >> 8);
g_lwipdev.netmask[0] = (uint8_t)(netmask);
printf("通过DHCP获取到子网掩码....%d.%d.%d.%d\r\n", g_lwipdev.netmask[0], g_lwipdev.netmask[1], g_lwipdev.netmask[2], g_lwipdev.netmask[3]);
//解析出通过DHCP获取到的默认网关
g_lwipdev.gateway[3] = (uint8_t)(gw >> 24);
g_lwipdev.gateway[2] = (uint8_t)(gw >> 16);
g_lwipdev.gateway[1] = (uint8_t)(gw >> 8);
g_lwipdev.gateway[0] = (uint8_t)(gw);
printf("通过DHCP获取到的默认网关..%d.%d.%d.%d\r\n", g_lwipdev.gateway[0], g_lwipdev.gateway[1], g_lwipdev.gateway[2], g_lwipdev.gateway[3]);
}
}
else
{
dhcp = (struct dhcp *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP);
if(dhcp->tries > LWIP_MAX_DHCP_TRIES)//DHCP超时
{
printf ("DHCP分配失败,进入静态分配......\r\n");
g_lwip_dhcp_state = LWIP_DHCP_TIMEOUT;
//停止DHCP
dhcp_stop(netif);
g_lwipdev.dhcpstatus = 0XFF;
//使用静态IP地址
IP4_ADDR(&(g_lwip_netif.ip_addr), g_lwipdev.ip[0], g_lwipdev.ip[1], g_lwipdev.ip[2], g_lwipdev.ip[3]);
IP4_ADDR(&(g_lwip_netif.netmask), g_lwipdev.netmask[0], g_lwipdev.netmask[1], g_lwipdev.netmask[2], g_lwipdev.netmask[3]);
IP4_ADDR(&(g_lwip_netif.gw), g_lwipdev.gateway[0], g_lwipdev.gateway[1], g_lwipdev.gateway[2], g_lwipdev.gateway[3]);
netif_set_addr(netif, &g_lwip_netif.ip_addr, &g_lwip_netif.netmask, &g_lwip_netif.gw);
printf("静态IP地址........%d.%d.%d.%d\r\n",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);
printf("子网掩码..........%d.%d.%d.%d\r\n",g_lwipdev.netmask[0],g_lwipdev.netmask[1],g_lwipdev.netmask[2],g_lwipdev.netmask[3]);
printf("默认网关..........%d.%d.%d.%d\r\n",g_lwipdev.gateway[0],g_lwipdev.gateway[1],g_lwipdev.gateway[2],g_lwipdev.gateway[3]);
}
}
}break;
case LWIP_DHCP_LINK_DOWN:{
dhcp_stop(netif);//停止DHCP
g_lwip_dhcp_state = LWIP_DHCP_OFF;
}break;
default: break;
}
}
#endif
运行验证
最后一步,在main函数中加入以下代码
//main
int main(void)
{
//SW-DP Enabled
GPIO_PinAFConfig(GPIOA,GPIO_PinSource13,GPIO_AF_SWJ);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource14,GPIO_AF_SWJ);
//NVIC仅支持抢占式优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
//延时配置:若加入OS,那么us级的延时实现由TIM6完成
delay_init(168);
Usart1_Init(115200u);//串口1配置
TIM7_Int_Init(void); //100khz的频率,计数1000为10ms
switch(LwIP_Init())
{
case 1:printf("LwIP->内存错误!!!\r\n");break;
case 2:printf("LwIP->以太网外设初始化失败!!!\r\n");break;
case 3:printf("LwIP->网卡添加失败!!!\r\n");break;
default:break;
}
//检查MCU与PHY芯片是否通信成功
if(!ETH_ReadPHYRegister(LAN8720_PHY_ADDRESS,PHY_SR))
printf("MCU与PHY芯片通信失败,请检查电路或者源码!!!!\r\n");
while(1)lwip_periodic_handle();
}
串口将会输出一些运行信息
STM32F407带RTOS移植LwIP
拷贝裸机例程,添加以下文件
- 创建FreeRTOSConfig.h文件
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
#if defined(__CC_ARM) || defined(__ICCARM__)
#include "sys.h"
extern uint32_t SystemCoreClock;
#endif
/* 基础配置项 */
#define configUSE_PREEMPTION 1 /* 1: 抢占式调度器, 0: 协程式调度器, 无默认需定义 */
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 /* 1: 使用硬件计算下一个要运行的任务, 0: 使用软件算法计算下一个要运行的任务, 默认: 0 */
#define configUSE_TICKLESS_IDLE 0 /* 1: 使能tickless低功耗模式, 默认: 0 */
#define configCPU_CLOCK_HZ ( SystemCoreClock ) /* 定义CPU主频, 单位: Hz, 无默认需定义 */
//#define configSYSTICK_CLOCK_HZ (configCPU_CLOCK_HZ / 8)/* 定义SysTick时钟频率,当SysTick时钟频率与内核时钟频率不同时才可以定义, 单位: Hz, 默认: 不定义 */
#define configTICK_RATE_HZ ((TickType_t)1000) /* 定义系统时钟节拍频率, 单位: Hz, 无默认需定义 */
#define configMAX_PRIORITIES (32) /* 定义最大优先级数, 最大优先级=configMAX_PRIORITIES-1, 无默认需定义 */
#define configMINIMAL_STACK_SIZE ((unsigned short)128) /* 定义空闲任务的栈空间大小, 单位: Word, 无默认需定义 */
#define configMAX_TASK_NAME_LEN (16) /* 定义任务名最大字符数, 默认: 16 */
#define configUSE_16_BIT_TICKS 0 /* 1: 定义系统时钟节拍计数器的数据类型为16位无符号数, 无默认需定义 */
#define configIDLE_SHOULD_YIELD 1 /* 1: 使能在抢占式调度下,同优先级的任务能抢占空闲任务, 默认: 1 */
#define configUSE_TASK_NOTIFICATIONS 1 /* 1: 使能任务间直接的消息传递,包括信号量、事件标志组和消息邮箱, 默认: 1 */
#define configTASK_NOTIFICATION_ARRAY_ENTRIES 1 /* 定义任务通知数组的大小, 默认: 1 */
#define configUSE_MUTEXES 1 /* 1: 使能互斥信号量, 默认: 0 */
#define configUSE_RECURSIVE_MUTEXES 1 /* 1: 使能递归互斥信号量, 默认: 0 */
#define configUSE_COUNTING_SEMAPHORES 1 /* 1: 使能计数信号量, 默认: 0 */
#define configUSE_ALTERNATIVE_API 0 /* 已弃用!!! */
#define configQUEUE_REGISTRY_SIZE 16 /* 定义可以注册的信号量和消息队列的个数, 默认: 0 */
#define configUSE_QUEUE_SETS 1 /* 1: 使能队列集, 默认: 0 */
#define configUSE_TIME_SLICING 1 /* 1: 使能时间片调度, 默认: 1 */
#define configUSE_NEWLIB_REENTRANT 0 /* 1: 任务创建时分配Newlib的重入结构体, 默认: 0 */
#define configENABLE_BACKWARD_COMPATIBILITY 1 /* 1: 使能兼容老版本, 默认: 1 */
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0 /* 定义线程本地存储指针的个数, 默认: 0 */
#define configSTACK_DEPTH_TYPE uint16_t /* 定义任务堆栈深度的数据类型, 默认: uint16_t */
#define configMESSAGE_BUFFER_LENGTH_TYPE size_t /* 定义消息缓冲区中消息长度的数据类型, 默认: size_t */
#define configUSE_APPLICATION_TASK_TAG 0
/* 内存分配相关定义 */
#define configSUPPORT_STATIC_ALLOCATION 0 /* 1: 支持静态申请内存, 默认: 0 */
#define configSUPPORT_DYNAMIC_ALLOCATION 1 /* 1: 支持动态申请内存, 默认: 1 */
#define configTOTAL_HEAP_SIZE ((size_t)(32 * 1024)) /* FreeRTOS堆中可用的RAM总量, 单位: Byte, 无默认需定义 */
#define configAPPLICATION_ALLOCATED_HEAP 0 /* 1: 用户手动分配FreeRTOS内存堆(ucHeap), 默认: 0 */
#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 /* 1: 用户自行实现任务创建时使用的内存申请与释放函数, 默认: 0 */
/* 钩子函数相关定义 */
#define configUSE_IDLE_HOOK 0 /* 1: 使能空闲任务钩子函数, 无默认需定义 */
#define configUSE_TICK_HOOK 0 /* 1: 使能系统时钟节拍中断钩子函数, 无默认需定义 */
#define configCHECK_FOR_STACK_OVERFLOW 0 /* 1: 使能栈溢出检测方法1, 2: 使能栈溢出检测方法2, 默认: 0 */
#define configUSE_MALLOC_FAILED_HOOK 0 /* 1: 使能动态内存申请失败钩子函数, 默认: 0 */
#define configUSE_DAEMON_TASK_STARTUP_HOOK 0 /* 1: 使能定时器服务任务首次执行前的钩子函数, 默认: 0 */
/* 运行时间和任务状态统计相关定义 */
#define configGENERATE_RUN_TIME_STATS 0 /* 1: 使能任务运行时间统计功能, 默认: 0 */
#if configGENERATE_RUN_TIME_STATS
#include "./BSP/TIMER/btim.h"
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ConfigureTimeForRunTimeStats()
extern uint32_t FreeRTOSRunTimeTicks;
#define portGET_RUN_TIME_COUNTER_VALUE() FreeRTOSRunTimeTicks
#endif
#define configUSE_TRACE_FACILITY 1 /* 1: 使能可视化跟踪调试, 默认: 0 */
#define configUSE_STATS_FORMATTING_FUNCTIONS 1 /* 1: configUSE_TRACE_FACILITY为1时,会编译vTaskList()和vTaskGetRunTimeStats()函数, 默认: 0 */
#define configSUPPORT_STATIC_ALLOCATION 0
#define configUSE_TICKLESS_IDLE 0
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0
/* 协程相关定义 */
#define configUSE_CO_ROUTINES 0 /* 1: 启用协程, 默认: 0 */
#define configMAX_CO_ROUTINE_PRIORITIES 2 /* 定义协程的最大优先级, 最大优先级=configMAX_CO_ROUTINE_PRIORITIES-1, 无默认configUSE_CO_ROUTINES为1时需定义 */
/* 软件定时器相关定义 */
#define configUSE_TIMERS 1 /* 1: 使能软件定时器, 默认: 0 */
#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) /* 定义软件定时器任务的优先级, 无默认configUSE_TIMERS为1时需定义 */
#define configTIMER_QUEUE_LENGTH 5 /* 定义软件定时器命令队列的长度, 无默认configUSE_TIMERS为1时需定义 */
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2) /* 定义软件定时器任务的栈空间大小, 无默认configUSE_TIMERS为1时需定义 */
/* 可选函数, 1: 使能 */
#define INCLUDE_vTaskPrioritySet 1 /* 设置任务优先级 */
#define INCLUDE_uxTaskPriorityGet 1 /* 获取任务优先级 */
#define INCLUDE_vTaskDelete 1 /* 删除任务 */
#define INCLUDE_vTaskSuspend 1 /* 挂起任务 */
#define INCLUDE_xResumeFromISR 1 /* 恢复在中断中挂起的任务 */
#define INCLUDE_vTaskDelayUntil 1 /* 任务绝对延时 */
#define INCLUDE_vTaskDelay 1 /* 任务延时 */
#define INCLUDE_xTaskGetSchedulerState 1 /* 获取任务调度器状态 */
#define INCLUDE_xTaskGetCurrentTaskHandle 1 /* 获取当前任务的任务句柄 */
#define INCLUDE_uxTaskGetStackHighWaterMark 1 /* 获取任务堆栈历史剩余最小值 */
#define INCLUDE_xTaskGetIdleTaskHandle 1 /* 获取空闲任务的任务句柄 */
#define INCLUDE_eTaskGetState 1 /* 获取任务状态 */
#define INCLUDE_xEventGroupSetBitFromISR 1 /* 在中断中设置事件标志位 */
#define INCLUDE_xTimerPendFunctionCall 1 /* 将函数的执行挂到定时器服务任务 */
#define INCLUDE_xTaskAbortDelay 1 /* 中断任务延时 */
#define INCLUDE_xTaskGetHandle 1 /* 通过任务名获取任务句柄 */
#define INCLUDE_xTaskResumeFromISR 1 /* 恢复在中断中挂起的任务 */
#define INCLUDE_xSemaphoreGetMutexHolder 0
#define INCLUDE_vTaskCleanUpResources 0
/* 中断嵌套行为配置 */
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x0f /* 中断最低优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 0x05 /* FreeRTOS可管理的最高中断优先级 */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_API_CALL_INTERRUPT_PRIORITY configMAX_SYSCALL_INTERRUPT_PRIORITY
/* 断言 */
#define vAssertCalled(char,int) printf("Error: %s,%d\r\n", char, int)
//#define configASSERT(x) if((x)==0)vAssertCalled( __FILE__, __LINE__)
/* FreeRTOS中断服务函数相关定义 */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
/* FreeRTOS MPU 特殊定义 */
//#define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0
//#define configTOTAL_MPU_REGIONS 8
//#define configTEX_S_C_B_FLASH 0x07UL
//#define configTEX_S_C_B_SRAM 0x07UL
//#define configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY 1
//#define configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS 1
/* ARMv8-M 安全侧端口相关定义。 */
//#define secureconfigMAX_SECURE_CONTEXTS 5
#endif
文件修改
lwipopts.h
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__
/**
SYS_LIGHTWEIGHT_PROT==1:如果您确实需要任务间保护
*/
#define SYS_LIGHTWEIGHT_PROT 1
/* NO_SYS 表示无操作系统模拟层,无操作系统为1,有操作系统设置为0
注意这个参数设置会编译不同 */
#define NO_SYS 0
/**
* NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1
* Mainly for compatibility to old versions.
*/
#define NO_SYS_NO_TIMERS 0
/* ---------- 内存选项 ---------- */
/* 内存对齐,按照 4 字节对齐 */
#define MEM_ALIGNMENT 4
/* 堆内存的大小,如果需要更大的堆内存,那么设置高一点 */
#define MEM_SIZE (16*1024)
/* MEMP_NUM_PBUF: 设置内存池的数量 */
#define MEMP_NUM_PBUF 15
/* MEMP_NUM_UDP_PCB: UDP协议控制块的数量. */
#define MEMP_NUM_UDP_PCB 4
/* MEMP_NUM_TCP_PCB: TCP的数量. */
#define MEMP_NUM_TCP_PCB 4
/* MEMP_NUM_TCP_PCB_LISTEN: 监听TCP的数量. */
#define MEMP_NUM_TCP_PCB_LISTEN 2
/* MEMP_NUM_TCP_SEG: 同时排队的TCP的数量段. */
#define MEMP_NUM_TCP_SEG 150
/* MEMP_NUM_SYS_TIMEOUT: 超时模拟活动的数量. */
#define MEMP_NUM_SYS_TIMEOUT 6
/* ---------- Pbuf选项 ---------- */
/* PBUF_POOL 内存池中每个内存块大小 */
#define PBUF_POOL_SIZE 20
/* PBUF_POOL_BUFSIZE: pbuf池中每个pbuf的大小. */
#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN)
/* ---------- TCP选项 ---------- */
#define LWIP_TCP 1
#define TCP_TTL 255
/* 控制TCP是否应该对到达的段进行排队
秩序。如果你的设备内存不足,定义为0. */
#define TCP_QUEUE_OOSEQ 0
/* TCP最大段大小 */
#define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */
/* TCP发送者缓冲区空间(字节). */
#define TCP_SND_BUF (11*TCP_MSS)
/* TCP_SND_QUEUELEN: TCP发送缓冲区空间。这必须是至少
需要(2 * TCP_SND_BUF/TCP_MSS)才能正常工作 */
#define TCP_SND_QUEUELEN (8* TCP_SND_BUF/TCP_MSS)
/* TCP接收窗口 */
#define TCP_WND (20*TCP_MSS)
/* ---------- ICMP 选项 ---------- */
#define LWIP_ICMP 1
/* ---------- DHCP 选项 ---------- */
/* 如果您希望DHCP配置为,请将LWIP_DHCP定义为1 */
#define LWIP_DHCP 1
/* ---------- UDP 选项 ---------- */
#define LWIP_UDP 1
#define UDP_TTL 255
/* ---------- Statistics 选项 ---------- */
#define LWIP_STATS 0
#define LWIP_PROVIDE_ERRNO 1
/* ---------- 链接回调选项 ---------- */
/* WIP_NETIF_LINK_CALLBACK==1:支持来自接口的回调函数
每当链接改变(例如,向下链接)
*/
#define LWIP_NETIF_LINK_CALLBACK 1
/*
--------------------------------------
---------- Checksum options ----------
--------------------------------------
*/
/*
The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware:
- To use this feature let the following define uncommented.
- To disable it and process by CPU comment the the checksum.
*/
#define CHECKSUM_BY_HARDWARE
#ifdef CHECKSUM_BY_HARDWARE
/* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/
#define CHECKSUM_GEN_IP 0
/* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/
#define CHECKSUM_GEN_UDP 0
/* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/
#define CHECKSUM_GEN_TCP 0
/* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/
#define CHECKSUM_CHECK_IP 0
/* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/
#define CHECKSUM_CHECK_UDP 0
/* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/
#define CHECKSUM_CHECK_TCP 0
/* CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/
#define CHECKSUM_GEN_ICMP 0
#else
/* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/
#define CHECKSUM_GEN_IP 1
/* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/
#define CHECKSUM_GEN_UDP 1
/* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/
#define CHECKSUM_GEN_TCP 1
/* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/
#define CHECKSUM_CHECK_IP 1
/* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/
#define CHECKSUM_CHECK_UDP 1
/* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/
#define CHECKSUM_CHECK_TCP 1
/* CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/
#define CHECKSUM_GEN_ICMP 1
#endif
/*
----------------------------------------------
---------- 连续层的选择 ----------
----------------------------------------------
*/
/**
* LWIP_NETCONN==1:启用Netconn API(需要使用api_lib.c)
*/
#define LWIP_NETCONN 1
/**
* LWIP_SOCKET==1:启用Socket API(要求使用Socket .c)
*/
#define LWIP_SOCKET 1
/*
---------------------------------
---------- 操作系统选项 ----------
---------------------------------
*/
#define DEFAULT_UDP_RECVMBOX_SIZE 10
#define DEFAULT_TCP_RECVMBOX_SIZE 10
#define DEFAULT_ACCEPTMBOX_SIZE 10
#define DEFAULT_THREAD_STACKSIZE 1024
#define TCPIP_THREAD_NAME "lwip_thread"
#define TCPIP_THREAD_STACKSIZE 2048
#define TCPIP_MBOX_SIZE 8
#define TCPIP_THREAD_PRIO (configMAX_PRIORITIES - 2)
#define LWIP_SO_RCVTIMEO 1
/*
----------------------------------------
---------- Lwip调试选项 ----------
----------------------------------------
*/
#define LWIP_DEBUG LWIP_DBG_OFF /* 关闭DEBUG选项 */
#define ICMP_DEBUG LWIP_DBG_OFF /* 开启/关闭ICMPdebug */
#endif /* __LWIPOPTS_H__ */
ethernetif.c
#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/timeouts.h"
#include "netif/etharp.h"
#include "lwip/err.h"
#include "ethernetif.h"
#include "ethernet/ethernet.h"
#define netifINTERFACE_TASK_STACK_SIZE (256) //ethernetif_input的任务堆栈
#define netifINTERFACE_TASK_PRIORITY (configMAX_PRIORITIES-1)//ethernetif_input的任务优先级
#define emacBLOCK_TIME_WAITING_FOR_INPUT ((portTickType)100)//输入阻塞的等待时间
#define netifGUARD_BLOCK_TIME ((portTickType)250)//块的保护时间
//网卡名
#define IFNAME0 's'
#define IFNAME1 't'
static struct netif *s_pxNetIf = NULL;
//以太网Rx的二值信号量
xSemaphoreHandle g_Eth_xSemaphore = NULL;
//以太网Rx & Tx DMA描述符
extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB];
//以太网收发缓冲区
extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE],Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE];
//跟踪当前发送和接收描述符的全局指针
extern ETH_DMADESCTypeDef *DMATxDescToSet,*DMARxDescToGet;
//最后接收帧信息的全局指针
extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos;
//以太网数据接收处理任务
static void ethernetif_input( void * pvParameters );
/**
* In this function, the hardware should be initialized.
* Called from ethernetif_init().
*
* @param netif the already initialized lwip network interface structure
* for this ethernetif
*/
static void low_level_init(struct netif *netif)
{
uint8_t s_local_mac[6];
uint32_t i;
netif->hwaddr_len = ETHARP_HWADDR_LEN;//设置nettif MAC硬件地址长度
//初始化MAC地址,设置什么地址由用户自己设置,但是不能与网络中其他设备MAC地址重复
GET_STM32MACID(s_local_mac);//获取STM32的唯一MAC地址
netif->hwaddr[0]=s_local_mac[0];
netif->hwaddr[1]=s_local_mac[1];
netif->hwaddr[2]=s_local_mac[2];
netif->hwaddr[3]=s_local_mac[3];
netif->hwaddr[4]=s_local_mac[4];
netif->hwaddr[5]=s_local_mac[5];
printf("网卡%c%c的MAC地址为:%x-%x-%x-%x-%x-%x\r\n",netif->name[0],netif->name[1],
netif->hwaddr[0],netif->hwaddr[1],netif->hwaddr[2],netif->hwaddr[3],netif->hwaddr[4],netif->hwaddr[5]);
netif->mtu = 1500;//nettif最大传输单位
//网卡状态信息标志位,是很重要的控制字段,它包括网卡功能使能、广播
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP;//接受广播地址和ARP流量
s_pxNetIf =netif;
//创建用于通知以太网帧接收的二进制信号量
if(g_Eth_xSemaphore == NULL)
{
vSemaphoreCreateBinary(g_Eth_xSemaphore);
xSemaphoreTake(g_Eth_xSemaphore,0);
}
ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr);//初始化以太网MAC中的MAC地址
ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);//初始化Tx描述符列表:链式模式
ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);//初始化Rx描述符列表:链式模式
//初始化Tx描述符列表:链式模式
{
for(i=0; i<ETH_RXBUFNB; i++)
{
ETH_DMARxDescReceiveITConfig(&DMARxDscrTab[i], ENABLE);
}
}
#ifdef CHECKSUM_BY_HARDWARE
//使能Tx帧的校验和插入
{
for(i=0; i<ETH_TXBUFNB; i++)
{
ETH_DMATxDescChecksumInsertionConfig(&DMATxDscrTab[i], ETH_DMATxDesc_ChecksumTCPUDPICMPFull);
}
}
#endif
//创建处理ETH MAC的任务
xTaskCreate(ethernetif_input,"Eth_if", netifINTERFACE_TASK_STACK_SIZE, NULL,
netifINTERFACE_TASK_PRIORITY,NULL);
ETH_Start();//使能MAC和DMA传输和接收
}
/**
* This function should do the actual transmission of the packet. The packet is
* contained in the pbuf that is passed to the function. This pbuf
* might be chained.
*
* @param netif the lwip network interface structure for this ethernetif
* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
* @return ERR_OK if the packet could be sent
* an err_t value if the packet couldn't be sent
*
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
* strange results. You might consider waiting for space in the DMA queue
* to become availale since the stack doesn't retry to send a packet
* dropped because of memory failure (except for the TCP timers).
*/
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
static xSemaphoreHandle xTxSemaphore = NULL;
err_t errval;
struct pbuf *q;
uint8_t *buffer ;
__IO ETH_DMADESCTypeDef *DmaTxDesc;
uint16_t framelength = 0;
uint32_t bufferoffset = 0;
uint32_t byteslefttocopy = 0;
uint32_t payloadoffset = 0;
if(xTxSemaphore == NULL)
{
vSemaphoreCreateBinary(xTxSemaphore);
xSemaphoreGive(xTxSemaphore);
}
if(xSemaphoreTake(xTxSemaphore, netifGUARD_BLOCK_TIME))
{
DmaTxDesc = DMATxDescToSet;
buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr);
bufferoffset = 0;
#if ETH_PAD_SIZE
pbuf_remove_header(p, ETH_PAD_SIZE); /* drop the padding word */
#endif
for(q = p; q != NULL; q = q->next)//从pbuf中拷贝要发送的数据
{
//判断此发送描述符是否有效,即判断此发送描述符是否归以太网DMA所有
if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (uint32_t)RESET)
{
errval=ERR_USE;
goto error;
}
//获取当前lwIP缓冲区中的字节
byteslefttocopy = q->len;
payloadoffset = 0;
/*将pbuf中要发送的数据写入到以太网发送描述符中,有时候我们要发送的数据可能大于一个以太网
描述符的Tx Buffer,因此我们需要分多次将数据拷贝到多个发送描述符中*/
while((byteslefttocopy+bufferoffset)>ETH_TX_BUF_SIZE)
{
//将数据拷贝到以太网发送描述符的Tx Buffer中
memcpy((u8_t*)((u8_t*)buffer+bufferoffset),(u8_t*)((u8_t*)q->payload+payloadoffset),
(ETH_TX_BUF_SIZE-bufferoffset));
//DmaTxDsc指向下一个发送描述符
DmaTxDesc = (ETH_DMADESCTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);
//检查新的发送描述符是否有效
if((DmaTxDesc->StatusÐ_DMATxDesc_OWN)!=(u32)RESET)
{
errval = ERR_USE;
goto error;
}
buffer = (u8 *)(DmaTxDesc->Buffer1Addr);//更新buffer地址,指向新的发送描述符的Tx Buffer
byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);
payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);
framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);
bufferoffset = 0;
}
//拷贝剩余的数据
memcpy( (u8_t*)((u8_t*)buffer+bufferoffset),(u8_t*)((u8_t*)q->payload+payloadoffset),byteslefttocopy );
bufferoffset = bufferoffset + byteslefttocopy;
framelength = framelength + byteslefttocopy;
}
//当所有要发送的数据都放进发送描述符的Tx Buffer以后就可发送此帧了
ETH_Prepare_Transmit_Descriptors(framelength);
errval = ERR_OK;
error:
//发送缓冲区发生下溢,一旦发送缓冲区发生下溢TxDMA会进入挂起状态
if((ETH->DMASRÐ_DMASR_TUS)!= (uint32_t)RESET)
{
ETH->DMASR = ETH_DMASR_TUS;//清除下溢标志
/* 当发送帧中出现下溢错误的时候TxDMA会挂起,这时候需要向DMATPDR寄存器
随便写入一个值来将其唤醒,此处我们写0 */
ETH->DMATPDR=0;
}
xSemaphoreGive(xTxSemaphore);
}
return errval;
}
/**
* Should allocate a pbuf and transfer the bytes of the incoming
* packet from the interface into the pbuf.
*
* @param netif the lwip network interface structure for this ethernetif
* @return a pbuf filled with the received packet (including MAC header)
* NULL on memory error
*/
static struct pbuf * low_level_input(struct netif *netif)
{
struct pbuf *p= NULL, *q;
u32_t len;
FrameTypeDef frame;
u8 *buffer;
__IO ETH_DMADESCTypeDef *DMARxDesc;
uint32_t bufferoffset = 0;
uint32_t payloadoffset = 0;
uint32_t byteslefttocopy = 0;
uint32_t i=0;
frame = ETH_Get_Received_Frame_interrupt();//接收一帧数据帧
//获取数据包的大小并将其放入“len”变量中
len = frame.length;
buffer = (u8 *)frame.buffer;
//从Lwip缓冲池中分配一个pbuf链
if(len > 0) p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
//pbuf链分配成功
if (p != NULL)
{
DMARxDesc = frame.descriptor;//获取接收描述符链表中的第一个描述符
bufferoffset = 0;
for(q = p; q != NULL; q = q->next)
{
byteslefttocopy = q->len;
payloadoffset = 0;
//检查当前pbuf中要复制的字节长度是否大于Rx缓冲区大小
while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE )
{
//将数据复制到pbuf
memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset),
(u8_t*)((u8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));
//指向下一个描述符
DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr);
buffer = (unsigned char *)(DMARxDesc->Buffer1Addr);
byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);
payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);
bufferoffset = 0;
}
//复制pbuf中的剩余数据
memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset),
(u8_t*)((u8_t*)buffer + bufferoffset), byteslefttocopy);
bufferoffset = bufferoffset + byteslefttocopy;
}
}
DMARxDesc =frame.descriptor;//向DMA释放描述符
//在Rx描述符中设置自己的位:将缓冲区返回给DMA
for(i=0; i<DMA_RX_FRAME_infos->Seg_Count; i++)
{
DMARxDesc->Status = ETH_DMARxDesc_OWN;
DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr);
}
DMA_RX_FRAME_infos->Seg_Count =0;//清除段计数
//当设置Rx缓冲区不可用标志时:清除它并恢复接收
if((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET)
{
ETH->DMASR = ETH_DMASR_RBUS;//清除接收缓冲区不可用标志
/* 当接收缓冲区不可用的时候 RxDMA 会进去挂起状态,
通过向 DMARPDR 写入任意一个值来唤醒 Rx DMA */
ETH->DMARPDR = 0;
}
return p;
}
/**
* This function is the ethernetif_input task, it is processed when a packet
* is ready to be read from the interface. It uses the function low_level_input()
* that should handle the actual reception of bytes from the network
* interface. Then the type of the received packet is determined and
* the appropriate input function is called.
*
* @param netif the lwip network interface structure for this ethernetif
*/
void ethernetif_input( void * pvParameters )
{
struct pbuf *p;
for(;;)
{
if(xSemaphoreTake(g_Eth_xSemaphore, emacBLOCK_TIME_WAITING_FOR_INPUT)==pdTRUE)
{
TRY_GET_NEXT_FRAME:
p = low_level_input(s_pxNetIf);
if(p != NULL)
{
if(ERR_OK!=s_pxNetIf->input(p,s_pxNetIf))
{
pbuf_free(p);
}
else
{
goto TRY_GET_NEXT_FRAME;
}
}
}
}
}
/**
* Should be called at the beginning of the program to set up the
* network interface. It calls the function low_level_init() to do the
* actual setup of the hardware.
*
* This function should be passed as a parameter to netif_add().
*
* @param netif the lwip network interface structure for this ethernetif
* @return ERR_OK if the loopif is initialized
* ERR_MEM if private data couldn't be allocated
* any other err_t on error
*/
err_t ethernetif_init(struct netif *netif)
{
LWIP_ASSERT("netif != NULL", (netif != NULL));
#if LWIP_NETIF_HOSTNAME
/* Initialize interface hostname */
netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
netif->output = etharp_output;
netif->linkoutput = low_level_output;
/* initialize the hardware */
low_level_init(netif);
return ERR_OK;
}
LwIP内核和RTOS接口文件sys_arch.c
#include "lwip/debug.h"
#include "lwip/def.h"
#include "lwip/sys.h"
#include "lwip/mem.h"
#include "lwip/stats.h"
#include "sys.h"
裸机移植为LWIP提供计时
//extern uint32_t lwip_localtime;//lwip本地时间计数器,单位:ms
//u32_t sys_now(void){
// return lwip_localtime;
//}
#include "FreeRTOS.h"
#include "semphr.h"
#include "task.h"
int errno;
/** Set this to 1 if you want the stack size passed to sys_thread_new() to be
* interpreted as number of stack words (FreeRTOS-like).
* Default is that they are interpreted as byte count (lwIP-like).
*/
#ifndef LWIP_FREERTOS_THREAD_STACKSIZE_IS_STACKWORDS
#define LWIP_FREERTOS_THREAD_STACKSIZE_IS_STACKWORDS 1
#endif
/** Set this to 1 to use a mutex for SYS_ARCH_PROTECT() critical regions.
* Default is 0 and locks interrupts/scheduler for SYS_ARCH_PROTECT().
*/
#ifndef LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
#define LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX 0
#endif
/** Set this to 1 to include a sanity check that SYS_ARCH_PROTECT() and
* SYS_ARCH_UNPROTECT() are called matching.
*/
#ifndef LWIP_FREERTOS_SYS_ARCH_PROTECT_SANITY_CHECK
#define LWIP_FREERTOS_SYS_ARCH_PROTECT_SANITY_CHECK 0
#endif
/** Set this to 1 to let sys_mbox_free check that queues are empty when freed */
#ifndef LWIP_FREERTOS_CHECK_QUEUE_EMPTY_ON_FREE
#define LWIP_FREERTOS_CHECK_QUEUE_EMPTY_ON_FREE 0
#endif
/** Set this to 1 to enable core locking check functions in this port.
* For this to work, you'll have to define LWIP_ASSERT_CORE_LOCKED()
* and LWIP_MARK_TCPIP_THREAD() correctly in your lwipopts.h! */
#ifndef LWIP_FREERTOS_CHECK_CORE_LOCKING
#define LWIP_FREERTOS_CHECK_CORE_LOCKING 0
#endif
/** Set this to 0 to implement sys_now() yourself, e.g. using a hw timer.
* Default is 1, where FreeRTOS ticks are used to calculate back to ms.
*/
#ifndef LWIP_FREERTOS_SYS_NOW_FROM_FREERTOS
#define LWIP_FREERTOS_SYS_NOW_FROM_FREERTOS 1
#endif
#if !configSUPPORT_DYNAMIC_ALLOCATION
# error "lwIP FreeRTOS port requires configSUPPORT_DYNAMIC_ALLOCATION"
#endif
#if !INCLUDE_vTaskDelay
# error "lwIP FreeRTOS port requires INCLUDE_vTaskDelay"
#endif
#if !INCLUDE_vTaskSuspend
# error "lwIP FreeRTOS port requires INCLUDE_vTaskSuspend"
#endif
#if LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX || !LWIP_COMPAT_MUTEX
#if !configUSE_MUTEXES
# error "lwIP FreeRTOS port requires configUSE_MUTEXES"
#endif
#endif
#if SYS_LIGHTWEIGHT_PROT && LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
static SemaphoreHandle_t sys_arch_protect_mutex;
#endif
#if SYS_LIGHTWEIGHT_PROT && LWIP_FREERTOS_SYS_ARCH_PROTECT_SANITY_CHECK
static sys_prot_t sys_arch_protect_nesting;
#endif
/* Initialize this module (see description in sys.h) */
void
sys_init(void)
{
#if SYS_LIGHTWEIGHT_PROT && LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
/* initialize sys_arch_protect global mutex */
sys_arch_protect_mutex = xSemaphoreCreateRecursiveMutex();
LWIP_ASSERT("failed to create sys_arch_protect mutex",
sys_arch_protect_mutex != NULL);
#endif /* SYS_LIGHTWEIGHT_PROT && LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX */
}
#if configUSE_16_BIT_TICKS == 1
#error This port requires 32 bit ticks or timer overflow will fail
#endif
#if LWIP_FREERTOS_SYS_NOW_FROM_FREERTOS
u32_t
sys_now(void)
{
return xTaskGetTickCount() * portTICK_PERIOD_MS;
}
#endif
u32_t
sys_jiffies(void)
{
return xTaskGetTickCount();
}
#if SYS_LIGHTWEIGHT_PROT
sys_prot_t
sys_arch_protect(void)
{
#if LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
BaseType_t ret;
LWIP_ASSERT("sys_arch_protect_mutex != NULL", sys_arch_protect_mutex != NULL);
ret = xSemaphoreTakeRecursive(sys_arch_protect_mutex, portMAX_DELAY);
LWIP_ASSERT("sys_arch_protect failed to take the mutex", ret == pdTRUE);
#else /* LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX */
taskENTER_CRITICAL();
#endif /* LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX */
#if LWIP_FREERTOS_SYS_ARCH_PROTECT_SANITY_CHECK
{
/* every nested call to sys_arch_protect() returns an increased number */
sys_prot_t ret = sys_arch_protect_nesting;
sys_arch_protect_nesting++;
LWIP_ASSERT("sys_arch_protect overflow", sys_arch_protect_nesting > ret);
return ret;
}
#else
return 1;
#endif
}
void
sys_arch_unprotect(sys_prot_t pval)
{
#if LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
BaseType_t ret;
#endif
#if LWIP_FREERTOS_SYS_ARCH_PROTECT_SANITY_CHECK
LWIP_ASSERT("unexpected sys_arch_protect_nesting", sys_arch_protect_nesting > 0);
sys_arch_protect_nesting--;
LWIP_ASSERT("unexpected sys_arch_protect_nesting", sys_arch_protect_nesting == pval);
#endif
#if LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
LWIP_ASSERT("sys_arch_protect_mutex != NULL", sys_arch_protect_mutex != NULL);
ret = xSemaphoreGiveRecursive(sys_arch_protect_mutex);
LWIP_ASSERT("sys_arch_unprotect failed to give the mutex", ret == pdTRUE);
#else /* LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX */
taskEXIT_CRITICAL();
#endif /* LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX */
LWIP_UNUSED_ARG(pval);
}
#endif /* SYS_LIGHTWEIGHT_PROT */
void
sys_arch_msleep(u32_t delay_ms)
{
TickType_t delay_ticks = delay_ms / portTICK_RATE_MS;
vTaskDelay(delay_ticks);
}
#if !LWIP_COMPAT_MUTEX
/* Create a new mutex*/
err_t
sys_mutex_new(sys_mutex_t *mutex)
{
LWIP_ASSERT("mutex != NULL", mutex != NULL);
mutex->mut = xSemaphoreCreateRecursiveMutex();
if(mutex->mut == NULL) {
SYS_STATS_INC(mutex.err);
return ERR_MEM;
}
SYS_STATS_INC_USED(mutex);
return ERR_OK;
}
void
sys_mutex_lock(sys_mutex_t *mutex)
{
BaseType_t ret;
LWIP_ASSERT("mutex != NULL", mutex != NULL);
LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL);
ret = xSemaphoreTakeRecursive(mutex->mut, portMAX_DELAY);
LWIP_ASSERT("failed to take the mutex", ret == pdTRUE);
}
void
sys_mutex_unlock(sys_mutex_t *mutex)
{
BaseType_t ret;
LWIP_ASSERT("mutex != NULL", mutex != NULL);
LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL);
ret = xSemaphoreGiveRecursive(mutex->mut);
LWIP_ASSERT("failed to give the mutex", ret == pdTRUE);
}
void
sys_mutex_free(sys_mutex_t *mutex)
{
LWIP_ASSERT("mutex != NULL", mutex != NULL);
LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL);
SYS_STATS_DEC(mutex.used);
vSemaphoreDelete(mutex->mut);
mutex->mut = NULL;
}
#endif /* !LWIP_COMPAT_MUTEX */
err_t
sys_sem_new(sys_sem_t *sem, u8_t initial_count)
{
LWIP_ASSERT("sem != NULL", sem != NULL);
LWIP_ASSERT("initial_count invalid (not 0 or 1)",
(initial_count == 0) || (initial_count == 1));
sem->sem = xSemaphoreCreateBinary();
if(sem->sem == NULL) {
SYS_STATS_INC(sem.err);
return ERR_MEM;
}
SYS_STATS_INC_USED(sem);
if(initial_count == 1) {
BaseType_t ret = xSemaphoreGive(sem->sem);
LWIP_ASSERT("sys_sem_new: initial give failed", ret == pdTRUE);
}
return ERR_OK;
}
void
sys_sem_signal(sys_sem_t *sem)
{
BaseType_t ret;
LWIP_ASSERT("sem != NULL", sem != NULL);
LWIP_ASSERT("sem->sem != NULL", sem->sem != NULL);
ret = xSemaphoreGive(sem->sem);
/* queue full is OK, this is a signal only... */
LWIP_ASSERT("sys_sem_signal: sane return value",
(ret == pdTRUE) || (ret == errQUEUE_FULL));
}
u32_t
sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout_ms)
{
BaseType_t ret;
LWIP_ASSERT("sem != NULL", sem != NULL);
LWIP_ASSERT("sem->sem != NULL", sem->sem != NULL);
if(!timeout_ms) {
/* wait infinite */
ret = xSemaphoreTake(sem->sem, portMAX_DELAY);
LWIP_ASSERT("taking semaphore failed", ret == pdTRUE);
} else {
TickType_t timeout_ticks = timeout_ms / portTICK_RATE_MS;
ret = xSemaphoreTake(sem->sem, timeout_ticks);
if (ret == errQUEUE_EMPTY) {
/* timed out */
return SYS_ARCH_TIMEOUT;
}
LWIP_ASSERT("taking semaphore failed", ret == pdTRUE);
}
/* Old versions of lwIP required us to return the time waited.
This is not the case any more. Just returning != SYS_ARCH_TIMEOUT
here is enough. */
return 1;
}
void
sys_sem_free(sys_sem_t *sem)
{
LWIP_ASSERT("sem != NULL", sem != NULL);
LWIP_ASSERT("sem->sem != NULL", sem->sem != NULL);
SYS_STATS_DEC(sem.used);
vSemaphoreDelete(sem->sem);
sem->sem = NULL;
}
err_t
sys_mbox_new(sys_mbox_t *mbox, int size)
{
LWIP_ASSERT("mbox != NULL", mbox != NULL);
LWIP_ASSERT("size > 0", size > 0);
mbox->mbx = xQueueCreate((UBaseType_t)size, sizeof(void *));
if(mbox->mbx == NULL) {
SYS_STATS_INC(mbox.err);
return ERR_MEM;
}
SYS_STATS_INC_USED(mbox);
return ERR_OK;
}
void
sys_mbox_post(sys_mbox_t *mbox, void *msg)
{
BaseType_t ret;
LWIP_ASSERT("mbox != NULL", mbox != NULL);
LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL);
ret = xQueueSendToBack(mbox->mbx, &msg, portMAX_DELAY);
LWIP_ASSERT("mbox post failed", ret == pdTRUE);
}
err_t
sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{
BaseType_t ret;
LWIP_ASSERT("mbox != NULL", mbox != NULL);
LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL);
ret = xQueueSendToBack(mbox->mbx, &msg, 0);
if (ret == pdTRUE) {
return ERR_OK;
} else {
LWIP_ASSERT("mbox trypost failed", ret == errQUEUE_FULL);
SYS_STATS_INC(mbox.err);
return ERR_MEM;
}
}
err_t
sys_mbox_trypost_fromisr(sys_mbox_t *mbox, void *msg)
{
BaseType_t ret;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
LWIP_ASSERT("mbox != NULL", mbox != NULL);
LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL);
ret = xQueueSendToBackFromISR(mbox->mbx, &msg, &xHigherPriorityTaskWoken);
if (ret == pdTRUE) {
if (xHigherPriorityTaskWoken == pdTRUE) {
return ERR_NEED_SCHED;
}
return ERR_OK;
} else {
LWIP_ASSERT("mbox trypost failed", ret == errQUEUE_FULL);
SYS_STATS_INC(mbox.err);
return ERR_MEM;
}
}
u32_t
sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout_ms)
{
BaseType_t ret;
void *msg_dummy;
LWIP_ASSERT("mbox != NULL", mbox != NULL);
LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL);
if (!msg) {
msg = &msg_dummy;
}
if (!timeout_ms) {
/* wait infinite */
ret = xQueueReceive(mbox->mbx, &(*msg), portMAX_DELAY);
LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
} else {
TickType_t timeout_ticks = timeout_ms / portTICK_RATE_MS;
ret = xQueueReceive(mbox->mbx, &(*msg), timeout_ticks);
if (ret == errQUEUE_EMPTY) {
/* timed out */
*msg = NULL;
return SYS_ARCH_TIMEOUT;
}
LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
}
/* Old versions of lwIP required us to return the time waited.
This is not the case any more. Just returning != SYS_ARCH_TIMEOUT
here is enough. */
return 1;
}
u32_t
sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{
BaseType_t ret;
void *msg_dummy;
LWIP_ASSERT("mbox != NULL", mbox != NULL);
LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL);
if (!msg) {
msg = &msg_dummy;
}
ret = xQueueReceive(mbox->mbx, &(*msg), 0);
if (ret == errQUEUE_EMPTY) {
*msg = NULL;
return SYS_MBOX_EMPTY;
}
LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
/* Old versions of lwIP required us to return the time waited.
This is not the case any more. Just returning != SYS_ARCH_TIMEOUT
here is enough. */
return 1;
}
void
sys_mbox_free(sys_mbox_t *mbox)
{
LWIP_ASSERT("mbox != NULL", mbox != NULL);
LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL);
#if LWIP_FREERTOS_CHECK_QUEUE_EMPTY_ON_FREE
{
UBaseType_t msgs_waiting = uxQueueMessagesWaiting(mbox->mbx);
LWIP_ASSERT("mbox quence not empty", msgs_waiting == 0);
if (msgs_waiting != 0) {
SYS_STATS_INC(mbox.err);
}
}
#endif
vQueueDelete(mbox->mbx);
SYS_STATS_DEC(mbox.used);
}
sys_thread_t
sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
{
TaskHandle_t rtos_task;
BaseType_t ret;
sys_thread_t lwip_thread;
size_t rtos_stacksize;
LWIP_ASSERT("invalid stacksize", stacksize > 0);
#if LWIP_FREERTOS_THREAD_STACKSIZE_IS_STACKWORDS
rtos_stacksize = (size_t)stacksize;
#else
rtos_stacksize = (size_t)stacksize / sizeof(StackType_t);
#endif
/* lwIP's lwip_thread_fn matches FreeRTOS' TaskFunction_t, so we can pass the
thread function without adaption here. */
ret = xTaskCreate(thread, name, (configSTACK_DEPTH_TYPE)rtos_stacksize, arg, prio, &rtos_task);
LWIP_ASSERT("task creation failed", ret == pdTRUE);
lwip_thread.thread_handle = rtos_task;
return lwip_thread;
}
#if LWIP_NETCONN_SEM_PER_THREAD
#if configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0
sys_sem_t *
sys_arch_netconn_sem_get(void)
{
void* ret;
TaskHandle_t task = xTaskGetCurrentTaskHandle();
LWIP_ASSERT("task != NULL", task != NULL);
ret = pvTaskGetThreadLocalStoragePointer(task, 0);
return ret;
}
void
sys_arch_netconn_sem_alloc(void)
{
void *ret;
TaskHandle_t task = xTaskGetCurrentTaskHandle();
LWIP_ASSERT("task != NULL", task != NULL);
ret = pvTaskGetThreadLocalStoragePointer(task, 0);
if(ret == NULL) {
sys_sem_t *sem;
err_t err;
/* need to allocate the memory for this semaphore */
sem = mem_malloc(sizeof(sys_sem_t));
LWIP_ASSERT("sem != NULL", sem != NULL);
err = sys_sem_new(sem, 0);
LWIP_ASSERT("err == ERR_OK", err == ERR_OK);
LWIP_ASSERT("sem invalid", sys_sem_valid(sem));
vTaskSetThreadLocalStoragePointer(task, 0, sem);
}
}
void sys_arch_netconn_sem_free(void)
{
void* ret;
TaskHandle_t task = xTaskGetCurrentTaskHandle();
LWIP_ASSERT("task != NULL", task != NULL);
ret = pvTaskGetThreadLocalStoragePointer(task, 0);
if(ret != NULL) {
sys_sem_t *sem = ret;
sys_sem_free(sem);
mem_free(sem);
vTaskSetThreadLocalStoragePointer(task, 0, NULL);
}
}
#else /* configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 */
#error LWIP_NETCONN_SEM_PER_THREAD needs configNUM_THREAD_LOCAL_STORAGE_POINTERS
#endif /* configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 */
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
#if LWIP_FREERTOS_CHECK_CORE_LOCKING
#if LWIP_TCPIP_CORE_LOCKING
/** Flag the core lock held. A counter for recursive locks. */
static u8_t lwip_core_lock_count;
static TaskHandle_t lwip_core_lock_holder_thread;
void
sys_lock_tcpip_core(void)
{
sys_mutex_lock(&lock_tcpip_core);
if (lwip_core_lock_count == 0) {
lwip_core_lock_holder_thread = xTaskGetCurrentTaskHandle();
}
lwip_core_lock_count++;
}
void
sys_unlock_tcpip_core(void)
{
lwip_core_lock_count--;
if (lwip_core_lock_count == 0) {
lwip_core_lock_holder_thread = 0;
}
sys_mutex_unlock(&lock_tcpip_core);
}
#endif /* LWIP_TCPIP_CORE_LOCKING */
#if !NO_SYS
static TaskHandle_t lwip_tcpip_thread;
#endif
void
sys_mark_tcpip_thread(void)
{
#if !NO_SYS
lwip_tcpip_thread = xTaskGetCurrentTaskHandle();
#endif
}
void
sys_check_core_locking(void)
{
/* Embedded systems should check we are NOT in an interrupt context here */
/* E.g. core Cortex-M3/M4 ports:
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
Instead, we use more generic FreeRTOS functions here, which should fail from ISR: */
taskENTER_CRITICAL();
taskEXIT_CRITICAL();
#if !NO_SYS
if (lwip_tcpip_thread != 0) {
TaskHandle_t current_thread = xTaskGetCurrentTaskHandle();
#if LWIP_TCPIP_CORE_LOCKING
LWIP_ASSERT("Function called without core lock",
current_thread == lwip_core_lock_holder_thread && lwip_core_lock_count > 0);
#else /* LWIP_TCPIP_CORE_LOCKING */
LWIP_ASSERT("Function called from wrong thread", current_thread == lwip_tcpip_thread);
#endif /* LWIP_TCPIP_CORE_LOCKING */
}
#endif /* !NO_SYS */
}
#endif
netconf.c
__lwip_dev g_lwipdev; //lwip控制结构体
struct netif g_lwip_netif; //定义一个全局的网络接口
#if LWIP_DHCP
uint32_t g_dhcp_fine_timer = 0; //DHCP精细处理计时器
__IO uint8_t g_lwip_dhcp_state = LWIP_DHCP_OFF; //DHCP状态初始化
#endif
/* LINK线程配置 */
#define LWIP_LINK_TASK_PRIO 3 //任务优先级
#define LWIP_LINK_STK_SIZE 128 * 2 //任务堆栈大小
void lwip_link_thread( void * argument ); //链路线程
/* DHCP线程配置 */
#define LWIP_DHCP_TASK_PRIO 4 //任务优先级
#define LWIP_DHCP_STK_SIZE 128 * 2 //任务堆栈大小
void lwip_link_status_updated(struct netif *netif);
void lwip_dhcp_process(void *pvParameters);
/**
* @breif lwip 默认IP设置
* @param lwipx: lwip控制结构体指针
* @retval 无
*/
void lwip_comm_default_ip_set(__lwip_dev *lwipx)
{
//默认远端IP为:192.168.0.120
lwipx->remoteip[0] = 192;
lwipx->remoteip[1] = 168;
lwipx->remoteip[2] = 0;
lwipx->remoteip[3] = 120;
//默认本地IP为:192.168.0.30
lwipx->ip[0] = 192;
lwipx->ip[1] = 168;
lwipx->ip[2] = 0;
lwipx->ip[3] = 30;
//默认子网掩码:255.255.255.0
lwipx->netmask[0] = 255;
lwipx->netmask[1] = 255;
lwipx->netmask[2] = 255;
lwipx->netmask[3] = 0;
//默认网关:192.168.0.1
lwipx->gateway[0] = 192;
lwipx->gateway[1] = 168;
lwipx->gateway[2] = 0;
lwipx->gateway[3] = 1;
lwipx->dhcpstatus = 0;//没有DHCP
}
/**
* @breif LWIP初始化(LWIP启动的时候使用)
* @param 无
* @retval 0,成功
1,内存错误
2,以太网芯片初始化失败
3,网卡添加失败.
*/
uint8_t LwIP_Init(void)
{
struct netif *netif_init_flag; //调用netif_add()函数时的返回值,用于判断网络初始化是否成功
ip_addr_t ipaddr; //ip地址
ip_addr_t netmask; //子网掩码
ip_addr_t gw; //默认网关
if(ETH_BSP_Config())return 2; //初始化LAN8720失败
tcpip_init(NULL, NULL); //初始化tcp ip内核,该函数里面会创建tcpip_thread内核任务
lwip_comm_default_ip_set(&g_lwipdev);//设置默认IP等信息
#if LWIP_DHCP //使用动态IP
ipaddr.addr = 0;
netmask.addr = 0;
gw.addr = 0;
#else //使用静态IP
IP4_ADDR(&ipaddr,g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);
IP4_ADDR(&netmask,g_lwipdev.netmask[0],g_lwipdev.netmask[1] ,g_lwipdev.netmask[2],g_lwipdev.netmask[3]);
IP4_ADDR(&gw,g_lwipdev.gateway[0],g_lwipdev.gateway[1],g_lwipdev.gateway[2],g_lwipdev.gateway[3]);
printf("静态IP地址........%d.%d.%d.%d\r\n",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);
printf("子网掩码..........%d.%d.%d.%d\r\n",g_lwipdev.netmask[0],g_lwipdev.netmask[1],g_lwipdev.netmask[2],g_lwipdev.netmask[3]);
printf("默认网关..........%d.%d.%d.%d\r\n",g_lwipdev.gateway[0],g_lwipdev.gateway[1],g_lwipdev.gateway[2],g_lwipdev.gateway[3]);
#endif
taskENTER_CRITICAL();//进入临界区
netif_init_flag=netif_add(&g_lwip_netif, (const ip_addr_t *)&ipaddr, (const ip_addr_t *)&netmask,
(const ip_addr_t *)&gw, NULL, ðernetif_init, &tcpip_input);//向网卡列表中添加一个网口
taskEXIT_CRITICAL();//退出临界区
if(netif_init_flag==NULL)return 3;//网卡添加失败
else//网口添加成功后,设置netif为默认值,并且打开netif网口
{
netif_set_default(&g_lwip_netif);//设置netif为默认网口
if (netif_is_link_up(&g_lwip_netif)) netif_set_up(&g_lwip_netif);//打开netif网口
else netif_set_down(&g_lwip_netif);
#if LWIP_NETIF_LINK_CALLBACK
lwip_link_status_updated(&g_lwip_netif);// DHCP链接状态更新函数
netif_set_link_callback(&g_lwip_netif, lwip_link_status_updated);
taskENTER_CRITICAL();//进入临界区
sys_thread_new("eth_link",lwip_link_thread,&g_lwip_netif,
LWIP_LINK_STK_SIZE,LWIP_LINK_TASK_PRIO);
taskEXIT_CRITICAL();//退出临界区
#endif
}
g_lwipdev.link_status = LWIP_LINK_OFF;//链接标记为0
#if LWIP_DHCP //如果使用DHCP的话
g_lwipdev.dhcpstatus=0; //DHCP标记为0
taskENTER_CRITICAL();//进入临界区
sys_thread_new("eth_dhcp",lwip_dhcp_process,&g_lwip_netif,
LWIP_DHCP_STK_SIZE,LWIP_DHCP_TASK_PRIO);
taskEXIT_CRITICAL();//退出临界区
#endif
return 0;//操作OK.
}
/**
* @brief 通知用户网络接口配置状态
* @param netif:网卡控制块
*/
void lwip_link_status_updated(struct netif *netif)
{
if(netif_is_up(netif))
{
#if LWIP_DHCP
/* Update DHCP state machine */
g_lwip_dhcp_state = LWIP_DHCP_START;
printf ("The network cable is connected \r\n");
#endif /* LWIP_DHCP */
}
else
{
#if LWIP_DHCP
/* Update DHCP state machine */
g_lwip_dhcp_state = LWIP_DHCP_LINK_DOWN;
printf ("The network cable is not connected \r\n");
#endif /* LWIP_DHCP */
}
}
#if LWIP_DHCP /* 如果使用DHCP */
/**
* @brief lwIP的DHCP分配进程
* @param pvParameters:网卡控制块
*/
void lwip_dhcp_process(void *pvParameters)
{
struct netif *netif = (struct netif *) pvParameters;
uint32_t ip = 0;
uint32_t netmask = 0;
uint32_t gw = 0;
struct dhcp *dhcp;
g_lwipdev.dhcpstatus = 1;//DHCP标记为1
while(1)
{
switch (g_lwip_dhcp_state)//根据DHCP状态进入执行相应的动作
{
case LWIP_DHCP_START:{
//对IP地址、网关地址及子网页码清零操作
ip_addr_set_zero_ip4(&netif->ip_addr);
ip_addr_set_zero_ip4(&netif->netmask);
ip_addr_set_zero_ip4(&netif->gw);
//设置DHCP的状态为等待分配IP地址
g_lwip_dhcp_state = LWIP_DHCP_WAIT_ADDRESS;
//开启DHCP
dhcp_start(netif);
printf("Looking for DHCP server,please wait...\r\n");
}break;
case LWIP_DHCP_WAIT_ADDRESS:{
ip = g_lwip_netif.ip_addr.addr; //读取新IP地址
netmask = g_lwip_netif.netmask.addr; //读取子网掩码
gw = g_lwip_netif.gw.addr; //读取默认网关
if(dhcp_supplied_address(netif))
{
g_lwip_dhcp_state = LWIP_DHCP_ADDRESS_ASSIGNED;
if (ip != 0)
{
taskENTER_CRITICAL();//进入临界区
printf("DHCP分配成功......\r\n");
g_lwipdev.dhcpstatus = 2;//DHCP成功
//解析出通过DHCP获取到的IP地址 */
g_lwipdev.ip[3] = (uint8_t)(ip >> 24);
g_lwipdev.ip[2] = (uint8_t)(ip >> 16);
g_lwipdev.ip[1] = (uint8_t)(ip >> 8);
g_lwipdev.ip[0] = (uint8_t)(ip);
printf("通过DHCP获取到IP地址......%d.%d.%d.%d\r\n", g_lwipdev.ip[0], g_lwipdev.ip[1], g_lwipdev.ip[2], g_lwipdev.ip[3]);
//解析通过DHCP获取到的子网掩码地址
g_lwipdev.netmask[3] = (uint8_t)(netmask >> 24);
g_lwipdev.netmask[2] = (uint8_t)(netmask >> 16);
g_lwipdev.netmask[1] = (uint8_t)(netmask >> 8);
g_lwipdev.netmask[0] = (uint8_t)(netmask);
printf("通过DHCP获取到子网掩码....%d.%d.%d.%d\r\n", g_lwipdev.netmask[0], g_lwipdev.netmask[1], g_lwipdev.netmask[2], g_lwipdev.netmask[3]);
//解析出通过DHCP获取到的默认网关
g_lwipdev.gateway[3] = (uint8_t)(gw >> 24);
g_lwipdev.gateway[2] = (uint8_t)(gw >> 16);
g_lwipdev.gateway[1] = (uint8_t)(gw >> 8);
g_lwipdev.gateway[0] = (uint8_t)(gw);
printf("通过DHCP获取到的默认网关..%d.%d.%d.%d\r\n", g_lwipdev.gateway[0], g_lwipdev.gateway[1], g_lwipdev.gateway[2], g_lwipdev.gateway[3]);
}
taskEXIT_CRITICAL();//退出临界区
}
else
{
dhcp = (struct dhcp *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP);
if(dhcp->tries > LWIP_MAX_DHCP_TRIES)//DHCP超时
{
taskENTER_CRITICAL();//进入临界区
printf ("DHCP分配失败,进入静态分配......\r\n");
g_lwip_dhcp_state = LWIP_DHCP_TIMEOUT;
//停止DHCP
dhcp_stop(netif);
g_lwipdev.dhcpstatus = 0XFF;
//使用静态IP地址
IP4_ADDR(&(g_lwip_netif.ip_addr), g_lwipdev.ip[0], g_lwipdev.ip[1], g_lwipdev.ip[2], g_lwipdev.ip[3]);
IP4_ADDR(&(g_lwip_netif.netmask), g_lwipdev.netmask[0], g_lwipdev.netmask[1], g_lwipdev.netmask[2], g_lwipdev.netmask[3]);
IP4_ADDR(&(g_lwip_netif.gw), g_lwipdev.gateway[0], g_lwipdev.gateway[1], g_lwipdev.gateway[2], g_lwipdev.gateway[3]);
netif_set_addr(netif, &g_lwip_netif.ip_addr, &g_lwip_netif.netmask, &g_lwip_netif.gw);
printf("静态IP地址........%d.%d.%d.%d\r\n",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);
printf("子网掩码..........%d.%d.%d.%d\r\n",g_lwipdev.netmask[0],g_lwipdev.netmask[1],g_lwipdev.netmask[2],g_lwipdev.netmask[3]);
printf("默认网关..........%d.%d.%d.%d\r\n",g_lwipdev.gateway[0],g_lwipdev.gateway[1],g_lwipdev.gateway[2],g_lwipdev.gateway[3]);
taskEXIT_CRITICAL();//退出临界区
}
}
}break;
case LWIP_DHCP_LINK_DOWN:{
dhcp_stop(netif);//停止DHCP
g_lwip_dhcp_state = LWIP_DHCP_OFF;
}break;
default: break;
}
vTaskDelay(1000);//wait 1000 ms
}
}
#endif
#if LWIP_NETIF_LINK_CALLBACK
/**
* @brief 检查ETH链路状态,更新netif
* @param argument: netif
* @retval 无
*/
void lwip_link_thread( void * argument )
{
uint32_t regval = 0;
struct netif *netif = (struct netif *) argument;
int link_again_num = 0;
while(1)
{
//读取PHY状态寄存器,获取链接信息
regval=ETH_ReadPHYRegister(LAN8720_PHY_ADDRESS,PHY_BSR);
//判断链接状态
if((regval&PHY_LINKED_STATUS)==0)
{
g_lwipdev.link_status = LWIP_LINK_OFF;
link_again_num++ ;
if(link_again_num>=2)continue;//网线一段时间没有插入
else//关闭虚拟网卡及以太网中断
{
#if LWIP_DHCP //如果使用DHCP的话
g_lwip_dhcp_state = LWIP_DHCP_LINK_DOWN;
dhcp_stop(netif);
#endif
ETH_Stop();
netif_set_down(netif);
netif_set_link_down(netif);
}
}
else //网线插入检测
{
link_again_num = 0;
if (g_lwipdev.link_status == LWIP_LINK_OFF)/* 开启以太网及虚拟网卡 */
{
g_lwipdev.link_status = LWIP_LINK_ON;
ETH_Start();
netif_set_up(netif);
netif_set_link_up(netif);
}
}
vTaskDelay(1000);//wait 1000 ms
}
}
#endif
运行验证
- main.c做如下修改
//main
int main(void)
{
//SW-DP Enabled
GPIO_PinAFConfig(GPIOA,GPIO_PinSource13,GPIO_AF_SWJ);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource14,GPIO_AF_SWJ);
//NVIC仅支持抢占式优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
//延时配置:若加入OS,那么us级的延时实现由TIM6完成
delay_init(168);
xTaskCreate(start_RTOS,"start_RTOS",512,NULL,31,NULL);//创建起始任务
vTaskStartScheduler();//开启任务调度
/* Infinite loop */
for(;;){}
}
- 在mainTask.c中编写任务函数
//任务相关---------------------------------------------------------------------------
//任务相关---------------------------------------------------------------------------
#define TASK1_PRIO 10//任务优先级
#define TASK2_PRIO 10
#define TASK1_STK_SIZE 64//任务堆栈大小
#define TASK2_STK_SIZE 64
TaskHandle_t osTask1_Handler;//任务句柄
TaskHandle_t osTask2_Handler;
TaskHandle_t osTask3_Handler;
void ostask1(void *pvParameters);//任务函数
void ostask2(void *pvParameters);
//RTOS入口---------------------------------------------------------------------------
void start_RTOS(void *pvParameters)
{
Usart1_Init(115200u);//串口1配置
//配置以太网
switch(LwIP_Init())
{
case 1:printf("LwIP->内存错误!!!\r\n");break;
case 2:printf("LwIP->以太网外设初始化失败!!!\r\n");break;
case 3:printf("LwIP->网卡添加失败!!!\r\n");break;
default:break;
}
//检查MCU与PHY芯片是否通信成功
if(!ETH_ReadPHYRegister(LAN8720_PHY_ADDRESS,PHY_SR))
printf("MCU与PHY芯片通信失败,请检查电路或者源码!!!\r\n");
//等待DHCP获取成功/超时溢出
while((g_lwipdev.dhcpstatus!=2)&&(g_lwipdev.dhcpstatus!=0XFF)) delay_ms(1);
taskENTER_CRITICAL();//进入临界区
//创建->任务
xTaskCreate(ostask1,"task1",TASK1_STK_SIZE,NULL,TASK1_PRIO,&osTask1_Handler);
xTaskCreate(ostask2,"task2",TASK2_STK_SIZE,NULL,TASK2_PRIO,&osTask2_Handler);
taskEXIT_CRITICAL();//退出临界区
vTaskDelete(NULL);//删除开始任务
}
/**
* @brief task1任务函数
* @note
*/
void ostask1(void *pvParameters)
{
for(;;)
{
printf("任务1---\r\n");
vTaskDelay(300);//延时
}
}
/**
* @brief task2任务函数
* @note
*/
void ostask2(void *pvParameters)
{
for(;;)
{
printf("任务2运行中\r\n");
vTaskDelay(500);//延时
// taskYIELD();//任务切换
}
}
- 编译下载后运行