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

news/2024/11/24 6:57:42/

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(&ETH_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(&ETH_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,&ethernetif_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&ETH_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;
}


http://www.ppmy.cn/news/145579.html

相关文章

STM32CubeIDE1.9.0\STM32CubeMX 6.5 F429IGT6加LAN8720A,配置ETH+LWIP

前段时间配置的F107系列和F7的LWIP功能&#xff0c;很顺利&#xff0c;但是到了F4系列&#xff0c;选择ETH之后没有PHY地址了&#xff0c;查了一下&#xff0c;MX 6.5有大改动&#xff0c;整理了一下各路大神的各种方法&#xff0c;配出来一个可以用的&#xff0c;我这里以Cube…

RTL8720DN开发笔记一 环境搭建与mqtt实例

简介 RTL8720DN是一款支持双频WiFi的Soc芯片&#xff0c;即2.4GHz或5.8GHz频段&#xff0c;可以告别在选择WiFi网络时仅支持2.4G的尴尬&#xff0c;不仅如此&#xff0c;RTL8720DN还支持了低功耗蓝牙BLE5.0&#xff0c;内部集成了两颗MCU&#xff0c;分别为ARM V8M(KM4 兼容Co…

以太网PHY芯片LAN8720A芯片研究

一、LAN8720A概述 1. 简介 LAN8720A/LAN8720Ai是一个低功耗的可变I/O电压的10/100M PHY收发器,符合IEEE 802.3-2005标准,支持通过RMII接口与以太网MAC通信。 LAN8720A/LAN8720Ai实现自动协商,自动确定最佳可能的速度和双工操作模式。HP Auto-MDIX支持允许使用直接连接或交…

新唐NUC980使用记录:访问以太网(LAN8720A) 启用SSH

文章目录 目的修改内核以访问以太网制作根文件系统并启用SSH总结 目的 这篇文章主要测试访问以太网&#xff08;PHY为LAN8720A&#xff09;以及启用SSH。 这篇文章中内容均在下面的开发板上进行测试&#xff1a; 《新唐NUC980使用记录&#xff1a;自制开发板&#xff08;基于…

5G WiFi 安信可 BW16 模组 RTL8720DN 入门笔记 2:Linux 搭建二次开发SDK开发环境,点亮一盏LED灯。

首先按照环境所依赖的包&#xff1a; sudo apt-get install git wget libc6-i386 lib32ncurses5 make bc gawk ncurses-dev 开始获取SDK源码&#xff0c;并且修改权限&#xff1a; git clone https://github.com/ambiot/ambd_sdk.git sudo chmod -R 777 ambd_sdk然后开始编译…

调试cube生成的f107+lan8720代码

之前用的w5500&#xff0c;无奈芯片越来越贵了&#xff0c;正好手头上有100来颗lan8720a&#xff0c;直接将方案改了吧。 以前在深圳工作时公司的网关正好用的这个方案&#xff0c;直接抄吧。 硬件设计网口无晶振&#xff0c;由mcu的mco脚输出&#xff0c;50Mhz模式&#xff0…

GD32F450以太网(2-1):PHY芯片LAN8720A介绍

PHY芯片LAN8720A 文章目录 PHY芯片LAN8720A1. MCU网口输出设计现状2. LAN8720A2.1 LAN8720A简介2.2 LAN8720A使用连接简图2.3 LAN8720A内部图2.4 LAN8720A和MCU连接图 3. LAN8720A内部寄存器[^1]3.1 BCR寄存器3.2 BSR寄存器3.3 PHY特殊功能寄存器 声明 1. MCU网口输出设计现状 …

【uboot】imx6ull uboot移植LAN8720A网卡驱动

文章目录 相关文章1. 前言2. IMX6ULL Ethernet LAN8720A硬件连接3. 支持LAN8720A修改步骤4. 验证测试问题1&#xff1a;如何确定LAN8720A网卡PHYAD地址&#xff1f;问题2&#xff1a;如何确定device tree中对reset gpio的定义&#xff1f;问题3&#xff1a;LAN8720A网卡nINTSEL…