STM32407 + FreeRTOS +LAN8720 +lwip 标准库 +stm32f4x7_eth

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_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;
}

你可能感兴趣的:(stm32)