lwip 建议不要使能 硬件校验 CHECKSUM_BY_HARDWARE 使用软件建议 在测试 LAN8720 ping 时硬件的检验 大包会PING不通的 #define ETH_MAX_PACKET_SIZE 1524 默认的1524字节 ping 1472 以上就不回复了( 例子 ping 192.168.1.1 -t -l 2048)
且 lwip + EC20 PPP拨号是不能使用硬件检验的
操作系统接口已经抽象出来(创建任务 信号量) 不一定是FreeRTOS ucos 也是一样的 lwip的操作系统接口(FreeRTOS ) sys_arch.c 官方文档已经提供了
u8 LAN8720_Init(void)
{
u8 rval=0;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOG|RCC_AHB1Periph_GPIOD, ENABLE);//使能GPIO时钟 RMII接口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); //使能SYSCFG时钟
SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII); //MAC和PHY之间使用RMII接口
/*网络引脚设置 RMII接口
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 -------------------> PG11
ETH_RMII_TXD0 --------------------> PG13
ETH_RMII_TXD1 --------------------> PG14
ETH_RESET-------------------------> PD3*/
//配置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);
//配置PG11, PG14 and PG13
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14;
GPIO_Init(GPIOG, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource11, GPIO_AF_ETH);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource13, GPIO_AF_ETH);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource14, 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);
GPIO_ResetBits(GPIOD,GPIO_Pin_3); //硬件复位LAN8720
HAL_msleep(50);
GPIO_SetBits(GPIOD,GPIO_Pin_3); //复位结束
ETHERNET_NVICConfiguration(); //设置中断优先级
rval=ETH_MACDMA_Config(); //配置MAC及DMA
return !rval; //ETH的规则为:0,失败;1,成功;所以要取反一下
}
//以太网中断分组配置
void ETHERNET_NVICConfiguration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
//configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 //系统可管理的最高中断优先级
//在FreeRTOS 中中断优先级必须低于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 才能使用系统调用(信号量邮箱互斥量等)
NVIC_InitStructure.NVIC_IRQChannel = ETH_IRQn; //以太网中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY+1; //中断寄存器组2最高优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0X00;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
初始化ETH MAC层及DMA配置
u8 ETH_MACDMA_Config(void)
{
u8 rval;
uint32_t timeout = 0;
ETH_InitTypeDef ETH_InitStructure;
//使能以太网MAC以及MAC接收和发送时钟
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)//等待软件重启网络完成
{
HAL_msleep(50);
timeout++;
set_netifstate_resterror(timeout > 3);
}
set_netifstate_resterror(0);
ETH_StructInit(Ð_InitStructure); //初始化网络为默认值
///网络MAC参数设置
ETH_InitStructure.ETH_Mode =ETH_Mode_FullDuplex;
ETH_InitStructure.ETH_Speed = ETH_Speed_10M;
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;
rval=ETH_Init(Ð_InitStructure,LAN8720_PHY_ADDRESS); //配置ETH
if(rval==ETH_SUCCESS)//配置成功
{
ETH_DMAITConfig(ETH_DMA_IT_NIS|ETH_DMA_IT_R,ENABLE); //使能以太网接收中断
}
return rval;
}
//以太网DMA接收中断服务函数
void ETH_IRQHandler(void)
{
ENTER_CRITICAL_FROM_ISR_DATA();
ENTER_CRITICAL_FROM_ISR();//进入临界区
if(ETH_GetDMAFlagStatus(ETH_DMA_FLAG_R))//(ETH_GetRxPktSize(DMARxDescToGet)!=0) //检测是否收到数据包
{
lwip_pkt_handle();
ETH_DMAClearITPendingBit(ETH_DMA_IT_R); //清除DMA中断标志位
}
ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS); //清除DMA接收中断标志位
EXIT_CRITICAL_FROM_ISR();//退出临界区
}
LAN8720 中断调用 lwip_pkt_handle 函数释放计数信号量 唤醒数据读取任务
//计数型信号量句柄
static HAL_Sem_t s_xSemaphore;//计数型信号量
//用于以太网中断调用
void lwip_pkt_handle(void)
{
HAL_OSSemGiveFromISR(&s_xSemaphore); //释放计数信号量
//ethernetif_input(&lwip_netif);
}
ethernetif_input_task 数据输入task 等待 s_xSemaphore
extern struct pbuf * low_level_input(struct netif *netif);
void ethernetif_input_task(void *para)
{
struct pbuf *p;
struct netif *netif =&lwip_netif;
ENTER_CRITICAL_DATA();
while(1)
{
if(HAL_OSSemTake( &s_xSemaphore, ALWAYS_WAITE ) == RETURN_OK)
{
/* move received packet into a new pbuf */
ENTER_CRITICAL();//进入临界区
TRY_GET_NEXT_FRAGMENT:
p = low_level_input(netif);
EXIT_CRITICAL();//退出临界区
/* points to packet payload, which starts with an Ethernet header */
if(p != NULL)
{
ENTER_CRITICAL();
/* full packet send to tcpip_thread to process */
if (netif->input(p, netif) != ERR_OK)
{
LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
pbuf_free(p);
p = NULL;
}
else
{
HAL_OSSemTryTake( &s_xSemaphore); //尝试拿一次如果获取到数据不需要被重新换新(xSemaphoreTake( s_xSemaphore, portMAX_DELAY ) == pdTRUE)
goto TRY_GET_NEXT_FRAGMENT; //去获取数据
}
EXIT_CRITICAL();
}
}
}
}
lwip_comm_init 主要是初始化 LAN8720, 创建读取任务ethernetif_input_task, 初始化tcp ip内核,网卡添加,打开netif网口等操作
//回调函数
void netif_status_callback(struct netif *netif)
{
volatile uint8_t ctx = 0;
ctx=0;
}
void netif_link_callback(struct netif *netif)
{
volatile uint8_t ctx = 0;
ctx=0;
}
void netif_remove_callback(struct netif *netif)
{
volatile uint8_t ctx = 0;
ctx=0;
}
u8 lwip_comm_init(int nDHCP)
{
struct netif *Netif_Init_Flag; //调用netif_add()函数时的返回值,用于判断网络初始化是否成功
ip_addr_t ipaddr; //ip地址
ip_addr_t netmask; //子网掩码
ip_addr_t gw; //默认网关
//创建计数型信号量,最大值255,初始值0
HAL_OSSemCreate(&s_xSemaphore,0); //创建计数信号量
sys_thread_new("ethernetif_input_task", ethernetif_input_task, NULL , 512, LWIP_GETDATA_TASK_PRIO);
lwip_comm_default_ip_set(&lwipdev); //设置默认IP等信息
while(LAN8720_Init()) //初始化LAN8720失败
{
if(!(ETH_ReadPHYRegister(LAN8720_PHY_ADDRESS, PHY_BSR) & PHY_Linked_Status))
{
//没插网线
lwipdev.netifstate=LWIP_ININ_ERROR_NOLINK;
}
else
{
lwipdev.netifstate=LWIP_ININ_ERROR_HW;
}
sys_msleep(1000);
}
lwipdev.netifstate=0x00;
tcpip_init(NULL,NULL); //初始化tcp ip内核,该函数里面会创建tcpip_thread内核任务
netif_set_status_callback(&lwip_netif, netif_status_callback);//注册回调函数
netif_set_link_callback(&lwip_netif, netif_link_callback);//注册回调函数
netif_set_remove_callback(&lwip_netif, netif_remove_callback);
#if LWIP_DHCP //使用动态IP
if(nDHCP)
{
ipaddr.addr = 0;
netmask.addr = 0;
gw.addr = 0;
}
else
#endif
{
//使用静态IP
IP4_ADDR(&ipaddr,lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);
IP4_ADDR(&netmask,lwipdev.netmask[0],lwipdev.netmask[1] ,lwipdev.netmask[2],lwipdev.netmask[3]);
IP4_ADDR(&gw,lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);
#if LWIP_DNS
ip_addr_t dnsserver;
IP4_ADDR(&dnsserver,114,114,114,114);
dns_setserver(0, &dnsserver);
IP4_ADDR(&dnsserver,208,67,222,222);
dns_setserver(1, &dnsserver);
#endif
}
Netif_Init_Flag=netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,ðernetif_init,&tcpip_input);//向网卡列表中添加一个网口
if(Netif_Init_Flag==NULL)//网卡添加失败
{
while(1)
{
lwipdev.netifstate=LWIP_ININ_ERROR_ADD;
sys_msleep(1000);
}
}
else//网口添加成功后,设置netif为默认值,并且打开netif网口
{
netif_set_default(&lwip_netif); //设置netif为默认网口
netif_set_up(&lwip_netif); //打开netif网口
}
lwip_comm_GetMac(lwip_netif.hwaddr);
#if LWIP_DHCP
if(nDHCP)
{
DHCP_Task();
}
#endif
//创建检测进程
sys_thread_new("dhcp_thread", lwip_check_task, NULL, 512, LWIP_CHECK_TASK_PRIO);
return 0;//操作OK.
}
//如果使能了DHCP
#if LWIP_DHCP
void DHCP_Task(void)
{
u32 ip=0,netmask=0,gw=0,tries=0;
dhcp_start(&lwip_netif);//开启DHCP
lwipdev.dhcpstatus=0; //正在DHCP
while(1)
{
ip=lwip_netif.ip_addr.addr; //读取新IP地址
netmask=lwip_netif.netmask.addr;//读取子网掩码
gw=lwip_netif.gw.addr; //读取默认网关
if(ip!=0) //当正确读取到IP地址的时候
{
lwipdev.dhcpstatus=2; //DHCP成功
//解析出通过DHCP获取到的IP地址
lwipdev.ip[3]=(uint8_t)(ip>>24);
lwipdev.ip[2]=(uint8_t)(ip>>16);
lwipdev.ip[1]=(uint8_t)(ip>>8);
lwipdev.ip[0]=(uint8_t)(ip);
//解析通过DHCP获取到的子网掩码地址
lwipdev.netmask[3]=(uint8_t)(netmask>>24);
lwipdev.netmask[2]=(uint8_t)(netmask>>16);
lwipdev.netmask[1]=(uint8_t)(netmask>>8);
lwipdev.netmask[0]=(uint8_t)(netmask);
//解析出通过DHCP获取到的默认网关
lwipdev.gateway[3]=(uint8_t)(gw>>24);
lwipdev.gateway[2]=(uint8_t)(gw>>16);
lwipdev.gateway[1]=(uint8_t)(gw>>8);
lwipdev.gateway[0]=(uint8_t)(gw);
break;
}
else if(tries > LWIP_MAX_DHCP_TRIES) //通过DHCP服务获取IP地址失败,且超过最大尝试次数
{
dhcp_stop(&lwip_netif); //关闭DHCP
lwipdev.dhcpstatus=0XFF;//DHCP失败.
//使用静态IP地址
IP4_ADDR(&(lwip_netif.ip_addr),lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);
IP4_ADDR(&(lwip_netif.netmask),lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);
IP4_ADDR(&(lwip_netif.gw),lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);
#if LWIP_DNS
ip_addr_t dnsserver;
IP4_ADDR(&dnsserver,114,114,114,114);
dns_setserver(0, &dnsserver);
IP4_ADDR(&dnsserver,208,67,222,222);
dns_setserver(1, &dnsserver);
#endif
break;
}
sys_msleep(1000); //延时1000
tries++;
}
}
//lwip_check_task 检查网线插拔的情况
void lwip_check_task(void *pdata)
{
pdata = pdata;
while(1)
{
sys_msleep(1000); //延时1000ms
if(!(ETH_ReadPHYRegister(LAN8720_PHY_ADDRESS, PHY_BSR) & PHY_Linked_Status))
{
if(netif_is_link_up(&lwip_netif))
{
netif_set_link_down(&lwip_netif);
}
lwipdev.netifstate=LWIP_ININ_ERROR_NOLINK;//没插网线
}
else
{
if(!netif_is_link_up(&lwip_netif))
{
netif_set_link_up(&lwip_netif);
}
lwipdev.netifstate=0x00;
}
}
}
ethernetif_init 的底层初始化
/* Ethernet Rx & Tx DMA Descriptors */
extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB];
/* Ethernet Driver Receive buffers */
extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE];
/* Ethernet Driver Transmit buffers */
extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE];
/* Global pointers to track current transmit and receive descriptors */
extern ETH_DMADESCTypeDef *DMATxDescToSet;
extern ETH_DMADESCTypeDef *DMARxDescToGet;
/* Global pointer for last received frame infos */
extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos;
err_t ethernetif_init(struct netif *netif)
{
LWIP_ASSERT("netif!=NULL",(netif!=NULL));
#if LWIP_NETIF_HOSTNAME //LWIP_NETIF_HOSTNAME
netif->hostname="lwip"; //初始化名称
#endif
netif->name[0]=IFNAME0; //初始化变量netif的name字段
netif->name[1]=IFNAME1; //在文件外定义这里不用关心具体值
netif->output=etharp_output;//IP层发送数据包函数
netif->linkoutput=low_level_output;//ARP模块发送数据包函数
low_level_init(netif); //底层硬件初始化函数
return ERR_OK;
}
low_level_init 底层初始化函数
static err_t low_level_init(struct netif *netif)
{
#ifdef CHECKSUM_BY_HARDWARE
int i;
#endif
netif->hwaddr_len = ETHARP_HWADDR_LEN; //设置MAC地址长度,为6个字节
//初始化MAC地址,设置什么地址由用户自己设置,但是不能与网络中其他设备MAC地址重复
netif->hwaddr[0]=1;
netif->hwaddr[1]=2;
netif->hwaddr[2]=3;
netif->hwaddr[3]=4;
netif->hwaddr[4]=5;
netif->hwaddr[5]=6;
netif->mtu=1500; //最大允许传输单元,允许该网卡广播和ARP功能
//并且该网卡允许有硬件链路连接
netif->flags = NETIF_FLAG_BROADCAST|NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP;
ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr); //向STM32F4的MAC地址寄存器中写入MAC地址
ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);
ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);
#ifdef CHECKSUM_BY_HARDWARE //使用硬件帧校验
for(i=0;i<ETH_TXBUFNB;i++) //使能TCP,UDP和ICMP的发送帧校验,TCP,UDP和ICMP的接收帧校验在DMA中配置了
{
ETH_DMATxDescChecksumInsertionConfig(&DMATxDscrTab[i], ETH_DMATxDesc_ChecksumTCPUDPICMPFull);
}
#endif
ETH_Start(); //开启MAC和DMA
return ERR_OK;
}
low_level_input 以太网数据读取函数
struct pbuf * low_level_input(struct netif *netif)
{
struct pbuf *p = NULL;
struct pbuf *q = NULL;
uint16_t len = 0;
uint8_t *buffer;
__IO ETH_DMADESCTypeDef *dmarxdesc;
FrameTypeDef frame;
uint32_t bufferoffset = 0;
uint32_t payloadoffset = 0;
uint32_t byteslefttocopy = 0;
uint32_t i=0;
/* get received frame */
if (!ETH_CheckFrameReceived())
{
return NULL;
}
/* Obtain the size of the packet and put it into the "len" variable. */
frame=ETH_Get_Received_Frame();
len = frame.length;
buffer = (uint8_t *)frame.buffer;
if (len > 0)
{
/* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
}
if (p != NULL)
{
if (DMA_RX_FRAME_infos->Seg_Count >1)
{
dmarxdesc = DMA_RX_FRAME_infos->FS_Rx_Desc ;
}
else
{
dmarxdesc = frame.descriptor;
}
bufferoffset = 0;
for(q = p; q != NULL; q = q->next)
{
byteslefttocopy = q->len;
payloadoffset = 0;
/* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/
while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE )
{
/* Copy data to pbuf */
memcpy( (uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));
/* Point to next descriptor */
dmarxdesc = (ETH_DMADESCTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
buffer = (uint8_t *)(dmarxdesc->Buffer1Addr);
byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);
payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);
bufferoffset = 0;
}
/* Copy remaining data in pbuf */
memcpy( (uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), byteslefttocopy);
bufferoffset = bufferoffset + byteslefttocopy;
}
}
/* Release descriptors to DMA */
/* Point to first descriptor */
if (DMA_RX_FRAME_infos->Seg_Count >1)
{
dmarxdesc = DMA_RX_FRAME_infos->FS_Rx_Desc ;
}
else
{
dmarxdesc = frame.descriptor;
}
/* Set Own bit in Rx descriptors: gives the buffers back to DMA */
for (i=0; i< DMA_RX_FRAME_infos->Seg_Count; i++)
{
dmarxdesc->Status |= ETH_DMARxDesc_OWN;
dmarxdesc = (ETH_DMADESCTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
}
/* Clear Segment_Count */
DMA_RX_FRAME_infos->Seg_Count =0;
/* When Rx Buffer unavailable flag is set: clear it and resume reception */
if ((ETH->DMASRÐ_DMASR_RBUS)!=(u32)RESET)
{
/* Clear RBUS ETHERNET DMA flag */
ETH->DMASR=ETH_DMASR_RBUS;
/* Resume DMA reception */
ETH->DMARPDR=0;
}
return p;
}
low_level_output 写数据到以太网
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
static sys_sem_t ousem ;
if(ousem.sem == NULL)
{
sys_sem_new(&ousem,0);
sys_sem_signal(&ousem);
}
err_t errval;
struct pbuf *q;
uint8_t *buffer = (uint8_t *)(DMATxDescToSet->Buffer1Addr);
__IO ETH_DMADESCTypeDef *DmaTxDesc= DMATxDescToSet;
uint32_t framelength = 0;
uint32_t bufferoffset = 0;
uint32_t byteslefttocopy = 0;
uint32_t payloadoffset = 0;
__IO uint32_t timeout = 0;
sys_sem_wait(&ousem); //wait sem
buffer = (uint8_t *)(DMATxDescToSet->Buffer1Addr);
DmaTxDesc = DMATxDescToSet;
bufferoffset = 0;
/* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
/* Is this buffer available? If not, goto error */
while((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (uint32_t)RESET)
{
sys_msleep(1);
timeout++;
if(timeout > 50) // wait 50ms
{
break;
}
}
if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (uint32_t)RESET)
{
errval = ERR_USE;
goto error;
}
/* copy frame from pbufs to driver buffers */
for(q = p; q != NULL; q = q->next)
{
/* Get bytes in current lwIP buffer */
byteslefttocopy = q->len;
payloadoffset = 0;
/* Check if the length of data to copy is bigger than Tx buffer size*/
while( (byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE )
{
/* Copy data to Tx buffer*/
memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) );
/* Point to next descriptor */
DmaTxDesc = (ETH_DMADESCTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);
/* Check if the buffer is available */
timeout =0;
while((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (uint32_t)RESET)
{
sys_msleep(1);
timeout++;
if(timeout > 50)
{
break;
}
}
if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (uint32_t)RESET)
{
errval = ERR_USE;
goto error;
}
buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr);
byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);
payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);
framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);
bufferoffset = 0;
}
/* Copy the remaining bytes */
memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), byteslefttocopy );
bufferoffset = bufferoffset + byteslefttocopy;
framelength = framelength + byteslefttocopy;
}
/* Prepare transmit descriptors to give to DMA */
ETH_Prepare_Transmit_Descriptors(framelength);
errval = ERR_OK;
error:
/* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */
if ((ETH->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET)
{
/* Clear TUS ETHERNET DMA flag */
ETH->DMASR = ETH_DMASR_TUS;
/* Resume DMA transmission*/
ETH->DMATPDR = 0;
}
sys_sem_signal(&ousem); //post the sem
return errval;
}