以太网外设ETH

news2024/11/25 21:16:43

1. 概述

近几年,项目需要,在多款单片机上使用了以太网外设。
本文为阶段知识整理,查缺补漏,方便以后再次遇到相关任务时,可以游刃有余的完成工作。

1.1 修改时间

  • 2023年5月6日创建本文。包含STM32的ETH外设。
  • 2023年5月9日修改本文。包含HHD32,GD32的ETH外设。

2. STM32F107的以太网外设

  • 没有使用中断引脚,通过DMA的方式进行收发。
  • RT-THREAD
  • LwIP

2.0 Chain Mode

在这里插入图片描述

#define ETH_MAX_PACKET_SIZE       1524U    /*!< ETH_HEADER + ETH_EXTRA + ETH_VLAN_TAG + ETH_MAX_ETH_PAYLOAD + ETH_CRC */
/* Definition of the Ethernet driver buffers size and count */
#define ETH_RX_BUF_SIZE                ETH_MAX_PACKET_SIZE /* buffer size for receive               */
#define ETH_TX_BUF_SIZE                ETH_MAX_PACKET_SIZE /* buffer size for transmit              */
#define ETH_RXBUFNB                    8U       /* 4 Rx buffers of size ETH_RX_BUF_SIZE  */
#define ETH_TXBUFNB                    4U       /* 4 Tx buffers of size ETH_TX_BUF_SIZE  */
__ALIGN_BEGIN ETH_DMADescTypeDef  DMARxDscrTab[ETH_RXBUFNB] __ALIGN_END;/* Ethernet Rx MA Descriptor */
__ALIGN_BEGIN ETH_DMADescTypeDef  DMATxDscrTab[ETH_TXBUFNB] __ALIGN_END;/* Ethernet Tx DMA Descriptor */
__ALIGN_BEGIN uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE] __ALIGN_END; /* Ethernet Receive Buffer */
__ALIGN_BEGIN uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE] __ALIGN_END; /* Ethernet Transmit Buffer */
  /* Initialize Tx Descriptors list: Chain Mode */
  HAL_ETH_DMATxDescListInit(&heth, DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);

  /* Initialize Rx Descriptors list: Chain Mode  */
  HAL_ETH_DMARxDescListInit(&heth, DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);

2.1 low_level_init

  • 根据STM32的型号,可以知道F107支持ETH外设。
  • 支持MII模式(25MHz时钟)和RMII模式(50MHz时钟)。
    在这里插入图片描述

2.1.1 初始化函数 HAL_ETH_Init

函数声明:HAL_StatusTypeDef HAL_ETH_Init(ETH_HandleTypeDef *heth); 用于初始化MAC和DMA。

(1)初始化引脚

  • 包括时钟,中断优先级
 /* Init the low level hardware : GPIO, CLOCK, NVIC. */
  HAL_ETH_MspInit(heth);

(2)选择MII或者RMII模式

 /* Select MII or RMII Mode*/
  AFIO->MAPR &= ~(AFIO_MAPR_MII_RMII_SEL);
  AFIO->MAPR |= (uint32_t) heth->Init.MediaInterface; //ETH_MEDIA_INTERFACE_MII      0x00000000
                                                      //ETH_MEDIA_INTERFACE_RMII     0x00800000

(3)软件复位

  • 等待复位完成,有超时机制
  /* Ethernet Software reset */
  /* Set the SWR bit: resets all MAC subsystem internal registers and logic */
  /* After reset all the registers holds their respective reset values */
  (heth->Instance)->DMABMR |= ETH_DMABMR_SR;
 /* Get tick */
  tickstart = HAL_GetTick();

  /* Wait for software reset */
  while (((heth->Instance)->DMABMR & ETH_DMABMR_SR) != (uint32_t)RESET)
  {
    /* Check for the Timeout */
    if ((HAL_GetTick() - tickstart) > ETH_TIMEOUT_SWRESET)
    {
      heth->State = HAL_ETH_STATE_TIMEOUT;

      /* Process Unlocked */
      __HAL_UNLOCK(heth);

      /* Note: The SWR is not performed if the ETH_RX_CLK or the ETH_TX_CLK are
         not available, please check your external PHY or the IO configuration */
         //注意:如果ETH_RX_CLK或ETH_TX_CLK不可用,则不执行SWR,请检查您的外部PHY或IO配置
      return HAL_TIMEOUT;
    }
  }

(4)初始化MAC

  /*-------------------------------- MAC Initialization ----------------------*/
  /* Get the ETHERNET MACMIIAR value */
  tmpreg1 = (heth->Instance)->MACMIIAR;
  /* Clear CSR Clock Range CR[2:0] bits */
  tmpreg1 &= ETH_MACMIIAR_CR_MASK;

  /* Get hclk frequency value */
  hclk = HAL_RCC_GetHCLKFreq();

  /* Set CR bits depending on hclk value */
  if ((hclk >= 20000000U) && (hclk < 35000000U))
  {
    /* CSR Clock Range between 20-35 MHz */
    tmpreg1 |= (uint32_t)ETH_MACMIIAR_CR_DIV16;
  }
  else if ((hclk >= 35000000U) && (hclk < 60000000U))
  {
    /* CSR Clock Range between 35-60 MHz */
    tmpreg1 |= (uint32_t)ETH_MACMIIAR_CR_DIV26;
  }
  else
  {
    /* CSR Clock Range between 60-72 MHz */
    tmpreg1 |= (uint32_t)ETH_MACMIIAR_CR_DIV42;
  }

  /* Write to ETHERNET MAC MIIAR: Configure the ETHERNET CSR Clock Range */
  (heth->Instance)->MACMIIAR = (uint32_t)tmpreg1;

(5)PHY初始化和配置

  • 让PHY芯片进行寄存器复位。
 /* Put the PHY in reset mode */
HAL_ETH_WritePHYRegister(heth, PHY_BCR, PHY_RESET));
/* Delay to assure PHY reset */
HAL_Delay(PHY_RESET_DELAY);
  • 如果启用了“自协商”
if ((heth->Init).AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE)
{
    ......
}
  • 如果禁用了“自协商” , 需要写入“双工模式”,“速度”。
  else /* AutoNegotiation Disable */
  {
    /* Set MAC Speed and Duplex Mode */
    HAL_ETH_WritePHYRegister(heth, PHY_BCR,  ((uint16_t)((heth->Init).DuplexMode >> 3U) | (uint16_t)((heth->Init).Speed >> 1U)  );
    /* Delay to assure PHY configuration */
    HAL_Delay(PHY_CONFIG_DELAY);
  }
  • 配置MAC和DMA
  /* Config MAC and DMA */
  ETH_MACDMAConfig(heth, err);

  /* Set ETH HAL State to Ready */
  heth->State = HAL_ETH_STATE_READY;

2.1.2 模式配置修改

  • 可以通过SMI总线,软件操作PHY的寄存器,配置PHY的工作模式
  • PHY处于 MII到copper的模式
  • PHY处于 MII到SGMII的模式

2.1.3 配置收发的缓冲区

  • 链模式 Chain Mode
  /* Initialize Tx Descriptors list: Chain Mode */
  HAL_ETH_DMATxDescListInit(&heth, DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);

  /* Initialize Rx Descriptors list: Chain Mode  */
  HAL_ETH_DMARxDescListInit(&heth, DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);

2.1.4 配置网口信息

  • 硬件MAC地址
  • 最大传输单元
  • 接收广播和ARP

2.1.5 创建信号量

  • 生成一个信号量,用于通知数据的到来
/* create a binary semaphore used for informing ethernetif of frame reception */	
gRxSem = rt_sem_create("gRxSem", 0, RT_IPC_FLAG_FIFO);

2.1.6 创建一个接收线程

mytask   = rt_thread_create("if_input", ethernetif_input, &gnetif, 512, 4, 1);
if(mytask != NULL)
{
		rt_thread_startup(mytask);
}
  • 接收线程,函数定义如下。通过等待信号量的方式进行线程的切换实现。
void ethernetif_input(void* argument)
{
  struct pbuf *p;
  struct netif *netif = (struct netif *) argument;
  struct eth_hdr *ethhdr;
  for( ;; )
  {
   // if (osSemaphoreAcquire(s_xSemaphore, TIME_WAITING_FOR_INPUT) == osOK)
	waitEthData();	//等待信号量
    {
        do
        {
	        LOCK_TCPIP_CORE();
	        p = low_level_input( netif );
	        if   (p != NULL)
	        {
				ethhdr = p->payload;//指向数据包有效负载,它以以太网报头开始
				switch (htons(ethhdr->type))
				{
					/* IP or ARP packet? */
					case ETHTYPE_IP:
					case ETHTYPE_ARP:
						/* 将完整数据包发送到tcpip_thread以进行处理 */
						//tcpip_input(p, netif)  --> sys_mbox_trypost(&mbox, msg) 发送邮箱信息,tcpip主线程接收邮箱信息
						if (netif->input(p, netif)!=ERR_OK) 
						{
							 pbuf_free(p);
							 p = NULL;
						}
						break;
					default:
					    pbuf_free(p);
						p = NULL;
						break;
		       }//end of switch
            }//end of  if   (p != NULL)
            UNLOCK_TCPIP_CORE();
        } while(p!=NULL);
    }//end of  if (osSem...
    }//end of for(;;)
}

2.1.7 启动网卡

/* Enable MAC and DMA transmission and reception */ 
  HAL_ETH_Start(&heth);

2.2 low_level_output

2.2.1 获取驱动发送缓冲区

__IO ETH_DMADescTypeDef *DmaTxDesc = heth.TxDesc;
uint8_t                 *buffer    = (uint8_t *)(heth.TxDesc->Buffer1Addr);

在这里插入图片描述

2.2.2 检查 能否访问

  • 驱动发送缓冲区描述块 DmaTxDesc
  • DmaTxDesc->Status 的 Bit 31 OWN: Own bit
    1 - 置位时,该位指示描述符归 DMA 所有。(DMA占用中)
    0 - 复位时,表示描述符归 CPU 所有。(CPU可控制)DMA 在完成帧传输或在描述符中分配的缓冲区被完全读取时清除该位。
    在这里插入图片描述
/* Is this buffer available? If not, goto error */
if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET)
{
  errval = ERR_USE;
  goto error;
}

2.2.3 转移数据

  • 把数据从pbufs中,转移到驱动缓冲区中,用于发送。
//遍历全部pbuf数据块
for(q = p; q != NULL; q = q->next)
{
     //(1) 检查当前的驱动发送缓冲区能否访问
     ... ...
      //(2) 获取当前 lwIP的pbuf 缓冲区的字节长度
      byteslefttocopy = q->len;
      payloadoffset = 0;
     //(3) 检查 剩余需要复制的字节长度是否超过驱动设备缓冲区的长度,如果超过,则一次搬运 ETH_TX_BUF_SIZE 字节的数据
      while( (byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE )
      {
        //(3.1) 将数据从pbuf 拷贝到 驱动发送缓冲区buffer ,bufferoffset=0
        memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) );
        //(3.2) 指向下一个驱动发送缓冲区块
        DmaTxDesc = (ETH_DMADescTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);
        //(3.3) 检查能否被访问
        ... ...
        //(3.4) 获取驱动发送缓冲区
        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;
      }

     //(4) 复制转移剩余的字节
      memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), byteslefttocopy );
      bufferoffset = bufferoffset + byteslefttocopy;
      framelength = framelength + byteslefttocopy;
    }

2.2.4 传递给DMA

  • 把驱动发送描述块 传递给DMA,DMA自动发送出数据
 /* Prepare transmit descriptors to give to DMA */
  HAL_ETH_TransmitFrame(&heth, framelength);

2.3 low_level_input

  • 分配pbuf,将传入数据包的字节从接口传输到pbuf。

2.3.1 获取接收的帧数据

HAL_ETH_GetReceivedFrame_IT(&heth)
len = heth.RxFrameInfos.length;
buffer = (uint8_t *)heth.RxFrameInfos.buffer;

2.3.2 分配pbuf

  • 如果收到了数据,需要创建接收存放数据的pbuf。用于在lwip协议栈里面流转。
/* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);

2.3.3 获取驱动接收缓冲区

  • 存放帧数据的驱动接收缓冲区
dmarxdesc = heth.RxFrameInfos.FSRxDesc;
bufferoffset = 0;

2.3.4 转移数据

  • 分配pbuf成功后,开始转移数据。
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*/
//(1) 检查 剩余的需要转移的数据字节 是否 大于 当前的pbuf空间大小
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));

  //获取驱动接收缓冲区
  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;
}

//(2) 拷贝剩余的数据,到pbuf中。
memcpy( (uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), byteslefttocopy);
bufferoffset = bufferoffset + byteslefttocopy;
}

2.3.5 释放接收描述块到DMA

  • 1 - 置位时,该位指示描述符归 DMA 所有。(DMA占用中)
  • 0 - 复位时,表示描述符归 CPU 所有。(CPU可控制)
/* Release descriptors to DMA */
/* Point to first descriptor */
dmarxdesc = heth.RxFrameInfos.FSRxDesc;
/* Set Own bit in Rx descriptors: gives the buffers back to DMA */
for (i=0; i< heth.RxFrameInfos.SegCount; i++)
{
  dmarxdesc->Status |= ETH_DMARXDESC_OWN;
  dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
}

2.3.6 恢复DMA接收

/* Clear Segment_Count */
heth.RxFrameInfos.SegCount =0;

/* When Rx Buffer unavailable flag is set: clear it and resume reception */
if ((heth.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET)
{
  /* Clear RBUS ETHERNET DMA flag */
  heth.Instance->DMASR = ETH_DMASR_RBUS;
  /* Resume DMA reception */
  heth.Instance->DMARPDR = 0;
}

2.3.7 返回pbuf数据

return p;

2.4 函数调用

  • 主线程 tcpip_thread
  • 接收线程 ethernetif_input
    在这里插入图片描述

2.5 其他函数

u32_t sys_now(void);
u32_t sys_jiffies(void);

3. HHD32F1xx的以太网外设

  • 海威华芯
  • 未使用low_level 低级别驱动函数。使用“设备Device”模块化接口。
  • 因工程代码由多人接手,此处调用结构略微混乱。已做简化处理。
    在这里插入图片描述

3.1 rt_stm32_eth_init

3.1.1 配置MAC地址

在函数rt_hw_hhd_eth_init中定义了MAC地址。

    /* OUI 00-80-E1 STMICROELECTRONICS. */
    stm32_eth_device.dev_addr[0] = 0x00;
    stm32_eth_device.dev_addr[1] = 0x80;
    stm32_eth_device.dev_addr[2] = 0xE1;
	
    /* generate MAC addr from 96bit unique ID (only for test). */
	mark_mac = get_mark();
    stm32_eth_device.dev_addr[3] = ETH_MAC3;
    stm32_eth_device.dev_addr[4] = ETH_MAC4;
    stm32_eth_device.dev_addr[5] = (mark_mac & 0x0F) + ETH_MAC5;

在调用rt_stm32_eth_init时,会将MAC地址写入到相应的寄存器中。

ETH_MACAddressConfig(ETH_MAC_Address0, (u8*)&stm32_eth->dev_addr[0]);
void ETH_MACAddressConfig(uint32_t MacAddr, uint8_t *Addr)
{
    uint32_t tmpreg;
    /* Calculate the selectecd MAC address high register */
    tmpreg = ((uint32_t)Addr[5] << 8) | (uint32_t)Addr[4];
    /* Load the selectecd MAC address high register */
    (*(__IO uint32_t *) (ETH_MAC_ADDR_HBASE + MacAddr)) = tmpreg;
    /* Calculate the selectecd MAC address low register */
    tmpreg = ((uint32_t)Addr[3] << 24) | ((uint32_t)Addr[2] << 16) | ((uint32_t)Addr[1] << 8) | Addr[0];

    /* Load the selectecd MAC address low register */
    (*(__IO uint32_t *) (ETH_MAC_ADDR_LBASE + MacAddr)) = tmpreg;
}

3.1.2 初始化chain mode

  • 初始化环形链表,并赋值关联到收发空间。
  • 参考目录的2.0小节。

(1)空间大小定义

#define ETH_MAX_PACKET_SIZE    1520    /*!< ETH_HEADER + ETH_EXTRA + MAX_ETH_PAYLOAD + ETH_CRC */
#define ETH_RXBUFNB        			6
#define ETH_TXBUFNB        			4

(2)数组定义

static ETH_DMADESCTypeDef  DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB];
static rt_uint8_t Rx_Buff[ETH_RXBUFNB][ETH_MAX_PACKET_SIZE], Tx_Buff[ETH_TXBUFNB][ETH_MAX_PACKET_SIZE];

(3)初始化

/* Initialize Tx Descriptors list: Chain Mode */
ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);
/* Initialize Rx Descriptors list: Chain Mode  */
ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);

(4)启用 DMA 接收描述块的 接收中断标志。

/* Enable Ethernet Rx interrrupt */
int i;
for(i=0; i<ETH_RXBUFNB; i++)
{
     ETH_DMARxDescReceiveITConfig(&DMARxDscrTab[i], ENABLE);
}

3.1.3 启动ETH外设

  • 启动DMA相关的功能
/* Enable MAC and DMA transmission and reception */
ETH_Start();
void ETH_Start(void)
{
  /* Enable transmit state machine of the MAC for transmission on the MII */  
  ETH_MACTransmissionCmd(ENABLE);
  /* Flush Transmit FIFO */
  ETH_FlushTransmitFIFO();
  /* Enable receive state machine of the MAC for reception from the MII */  
  ETH_MACReceptionCmd(ENABLE);
   /* Start DMA reception */
  ETH_DMAReceptionCmd(ENABLE);   
  /* Start DMA transmission */
  ETH_DMATransmissionCmd(ENABLE);
}

3.2 rt_stm32_eth_tx

3.2.1 获取当前发送缓冲区

uint8_t *buffer =  (rt_uint8_t*)ETH_GetCurrentTxBuffer();
u32 ETH_GetCurrentTxBuffer(void)
{ 
  /* Return Buffer address */
  return (DMATxDescToSet->Buffer1Addr);   
}

3.2.2 转移数据

  • 将pbuf数据复制到驱动缓冲区中。
for(q = p; q != NULL; q = q->next) 
{
	memcpy((rt_uint8_t*)&buffer[offset], q->payload, q->len);

	offset = offset + q->len;
}

3.2.3 使能发送

  • offset 表示数据长度
ETH_TxPkt_ChainMode(offset);

(1)检查能否访问

/* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
if((DMATxDescToSet->Status & ETH_DMATxDesc_OWN) != (u32)RESET)
{  
/* Return ERROR: OWN bit set */
  return RT_ERROR;
}

(2)控制发送缓冲区

  • 设置待发送的数据的长度。
  • 同时设置帧头帧尾标志,表示一帧数据全在一个发送描述块中。
/* Setting the Frame Length: bits[12:0] */
DMATxDescToSet->ControlBufferSize = (FrameLength & ETH_DMATxDesc_TBS1);
/* Setting the last segment and first segment bits (in this case a frame is transmitted in one descriptor) */    
DMATxDescToSet->ControlBufferSize |= ETH_DMATxDesc_LS | ETH_DMATxDesc_FS;
DMATxDescToSet->ControlBufferSize |= ETH_DMATxDesc_TCH;  // /*!< Second Address Chained 和ST不一样 

(3)启动发送

  • 将描述块的访问权限还给DMA,用于DMA的发送。
  • 恢复DMA的发送。
/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
DMATxDescToSet->Status |= ETH_DMATxDesc_OWN;
/* When Tx Buffer unavailable flag is set: clear it and resume transmission */
if ((HHD_ETH->DMASR & ETH_DMASR_TBUS) != (u32)RESET)
{
  /* Clear TBUS ETHERNET DMA flag */
  HHD_ETH->DMASR = ETH_DMASR_TBUS;
  /* Resume DMA transmission*/
  HHD_ETH->DMATPDR = 0;
}

(4)切换当前发送缓冲区

  • 用于下一次获取。
 /* Update the ETHERNET DMA global Tx descriptor with next Tx decriptor */  
 /* Chained Mode */
 /* Selects the next DMA Tx descriptor list for next buffer to send */ 
if((DMATxDescToSet->ControlBufferSize & ETH_DMATxDesc_TCH) != (uint32_t)RESET)
{
  DMATxDescToSet = (ETH_DMADESCTypeDef*) (DMATxDescToSet->Buffer2NextDescAddr); 
}

3.3 rt_stm32_eth_rx

3.3.1 检查能否访问

  • 判断接收描述块,属于CPU还是属于DMA。
  • 当属于DMA是,不可以被访问。
p = RT_NULL;
/* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
if(((DMARxDescToGet->Status & ETH_DMARxDesc_OWN) != (uint32_t)RESET))
    return p;

3.3.2 接收数据

  • 检测数据块完整性,是不是一个接收描述块 包含了帧头帧尾。ETH_DMARxDesc_FS,ETH_DMARxDesc_LS。
  • 检测是否发生错误。ETH_DMARxDesc_ES。
  • 获取驱动接收缓冲区的数据长度。
  • 分配pbuf空间。
  • 复制数据。
if (((DMARxDescToGet->Status & ETH_DMARxDesc_ES) == (uint32_t)RESET) &&
         ((DMARxDescToGet->Status & ETH_DMARxDesc_LS) != (uint32_t)RESET) &&
         ((DMARxDescToGet->Status & ETH_DMARxDesc_FS) != (uint32_t)RESET))
 {
     /* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */
     framelength = ((DMARxDescToGet->Status & ETH_DMARxDesc_FL) >> ETH_DMARXDESC_FRAME_LENGTHSHIFT) - 4;
     //rt_kprintf("%pkg len %d\n", framelength);
     /* allocate buffer */
     p = pbuf_alloc(PBUF_LINK, framelength, PBUF_RAM);
     if (p != RT_NULL)
     {
         struct pbuf* q;

         for (q = p; q != RT_NULL; q= q->next)
         {
          /* Copy the received frame into buffer from memory pointed by the current ETHERNET DMA Rx descriptor */
             memcpy(q->payload, (uint8_t *)((DMARxDescToGet->Buffer1Addr) + offset), q->len);
             offset += q->len;
         }

     }
 }

3.3.3 恢复DMA接收

  • 将驱动描述块的访问权限,还给DMA。
  • 恢复DMA的接收功能。
/* Set Own bit of the Rx descriptor Status: gives the buffer back to ETHERNET DMA */
DMARxDescToGet->Status = ETH_DMARxDesc_OWN;

/* When Rx Buffer unavailable flag is set: clear it and resume reception */
if ((HHD_ETH->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET)
{
    /* Clear RBUS ETHERNET DMA flag */
    HHD_ETH->DMASR = ETH_DMASR_RBUS;
    /* Resume DMA reception */
    HHD_ETH->DMARPDR = 0;
}

3.3.4 切换当前接收描述块

  • 用于下一次接收。
  • 如果是已经是最后一个环节,则需要切换到第一个描述块。
/* Update the ETHERNET DMA global Rx descriptor with next Rx decriptor */
/* Chained Mode */
if((DMARxDescToGet->ControlBufferSize & ETH_DMARxDesc_RCH) != (uint32_t)RESET)
{
    /* Selects the next DMA Rx descriptor list for next buffer to read */
    DMARxDescToGet = (ETH_DMADESCTypeDef*) (DMARxDescToGet->Buffer2NextDescAddr);
}
else /* Ring Mode */
{
    if((DMARxDescToGet->ControlBufferSize & ETH_DMARxDesc_RER) != (uint32_t)RESET)
    {
        /* Selects the first DMA Rx descriptor for next buffer to read: last Rx descriptor was used */
        DMARxDescToGet = (ETH_DMADESCTypeDef*) (HHD_ETH->DMARDLAR);
    }
    else
    {
        /* Selects the next DMA Rx descriptor list for next buffer to read */
        DMARxDescToGet = (ETH_DMADESCTypeDef*) ((uint32_t)DMARxDescToGet + 0x10 + ((HHD_ETH->DMABMR & ETH_DMABMR_DSL) >> 2));
    }
}

3.4 eth_device_init

  • 调用,参数为 stm32_eth_device.parent。
  • 下面提到的dev,都是指这里的stm32_eth_device.parent。
eth_device_init(&(stm32_eth_device.parent), "NT");

3.4.1 创建一个netif网络接口

  • 传递给dev,作为dev的数据成员。
struct netif* netif;
netif = (struct netif*) rt_malloc (sizeof(struct netif));
if (netif == RT_NULL)
{
	rt_kprintf("malloc netif failed\n");
	return -RT_ERROR;
}
rt_memset(netif, 0, sizeof(struct netif));
dev->netif = netif;

3.4.2 注册设备

  • 函数rt_device_register。
  • 设备类型为“网络接口”。
  • 初始化信号量。
/* register to rt-thread device manager */
rt_device_register(&(dev->parent), name, RT_DEVICE_FLAG_RDWR);

dev->parent.type = RT_Device_Class_NetIf;
rt_sem_init(&(dev->tx_ack), name, 0, RT_IPC_FLAG_FIFO);

3.4.3 对netif进行部分赋值

  • 函数rt_device_control,也就是rt_stm32_eth_control函数,用于将MAC硬件地址 stm32_eth_device.dev_addr,复制到 网络接口的硬件地址区 netif->hwaddr。
/* set name */
netif->name[0] = name[0];//N
netif->name[1] = name[1];//T

/* set hw address to 6 */
netif->hwaddr_len	= 6;
/* maximum transfer unit */
netif->mtu			= ETHERNET_MTU;
/* broadcast capability */
netif->flags		= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;

/* get hardware address */
rt_device_control(&(dev->parent), NIOCTL_GADDR, netif->hwaddr);

/* set output */
netif->output		= etharp_output;
netif->linkoutput	= ethernetif_linkoutput;
	

3.5 函数调用

在这里插入图片描述

4. GD32F307的以太网外设

  • 和HHD32一样使用device的框架。

4.1 rt_stm32_eth_init

4.1.1 配置MAC地址

参考3.1.1。
在调用rt_stm32_eth_init时,会将MAC地址写入到相应的寄存器中。

enet_mac_address_set(ENET_MAC_ADDRESS0, (uint8_t*)&stm32_eth->dev_addr[0]);//netif->hwaddr

low_level_init();

4.1.2 初始化chain mode

参考3.1.2。
(1)初始化

/* Initialize Tx Descriptors list: Chain Mode */
enet_descriptors_chain_init(ENET_DMA_TX);
/* Initialize Rx Descriptors list: Chain Mode  */
enet_descriptors_chain_init(ENET_DMA_RX);

(2)启用 DMA 接收描述块的 接收中断标志。

/* enable ethernet Rx interrrupt */
{   int i;
	for(i=0; i<ENET_RXBUF_NUM; i++){ 
	    enet_rx_desc_immediate_receive_complete_interrupt(&rxdesc_tab[i]);
	}
 }

4.1.3 启动ETH外设

/* Enable MAC and DMA transmission and reception */
enet_enable();
void enet_enable(void)
{
   enet_tx_enable();
   enet_rx_enable();
}

4.2 rt_stm32_eth_tx

4.2.1 获取当前发送缓冲区

uint8_t *buffer =  (uint8_t *)(enet_desc_information_get(dma_current_txdesc, TXDESC_BUFFER_1_ADDR));
uint32_t enet_desc_information_get(enet_descriptors_struct *desc, enet_descstate_enum info_get)
{
    uint32_t reval = 0xFFFFFFFFU;

    switch(info_get){
    case RXDESC_BUFFER_1_SIZE:
        reval = GET_RDES1_RB1S(desc->control_buffer_size);
        break;
    case RXDESC_BUFFER_2_SIZE:
        reval = GET_RDES1_RB2S(desc->control_buffer_size);
        break;
    case RXDESC_FRAME_LENGTH:
        reval = GET_RDES0_FRML(desc->status);
        if(reval > 4U){
            reval = reval - 4U;

            /* if is a type frame, and CRC is not included in forwarding frame */ 
            if((RESET != (ENET_MAC_CFG & ENET_MAC_CFG_TFCD)) && (RESET != (desc->status & ENET_RDES0_FRMT))){
                reval = reval + 4U;
            }
        }else{
             reval = 0U;
        }

        break;
    case RXDESC_BUFFER_1_ADDR:    
        reval = desc->buffer1_addr;    
        break;
    case TXDESC_BUFFER_1_ADDR:    
        reval = desc->buffer1_addr;  
        break;
    case TXDESC_COLLISION_COUNT:    
        reval = GET_TDES0_COCNT(desc->status);
        break;
    default:
        break;
    }
    return reval;
}

4.2.2 转移数据

for(q = p; q != NULL; q = q->next) 
{
	memcpy((rt_uint8_t*)&buffer[offset], q->payload, q->len);

	offset = offset + q->len;
}

4.2.3 使能发送

参考3.2.3。

ETH_TxPkt_ChainMode(offset);
uint32_t ETH_TxPkt_ChainMode(uint16_t FrameLength)
{ 
	return ENET_NOCOPY_FRAME_TRANSMIT(FrameLength); 
}

4.3 rt_stm32_eth_rx

4.3.1 获取接收缓冲区的数据

/* init p pointer */
p = RT_NULL;
/* obtain the size of the packet and put it into the "len" variable. */
len = enet_desc_information_get(dma_current_rxdesc, RXDESC_FRAME_LENGTH);
buffer = (uint8_t *)(enet_desc_information_get(dma_current_rxdesc, RXDESC_BUFFER_1_ADDR));

4.3.2 复制数据

  • 创建pbuf,用于存放接收的数据。
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){
     for(q = p; q != NULL; q = q->next){
         memcpy((uint8_t *)q->payload, (u8_t*)&buffer[l], q->len);
         l = l + q->len;
     }
 }

4.3.3 恢复dma接收

ENET_NOCOPY_FRAME_RECEIVE();//enet_frame_receive(NULL, 0U)

X. 后续补充,欢迎指正!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/525628.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

利用CNN对车牌进行智能识别(python代码,解压缩后直接运行)

1.代码流程 该段代码主要利用卷积神经网络&#xff08;CNN&#xff09;来识别车牌。下面是代码的主要流程&#xff1a; 导入所需的库和模块&#xff0c;包括matplotlib、numpy、cv2、tensorflow等。 加载用于检测车牌的级联分类器&#xff08;cascade classifier&#xff09;…

可见光遥感目标检测(一)任务概要介绍

前言 本篇开始对遥感图像的目标检测进行介绍&#xff0c;介绍了其目标前景、数据集以及评价指标。 本教程禁止转载。同时&#xff0c;本教程来自知识星球【CV技术指南】更多技术教程&#xff0c;可加入星球学习。 Transformer、目标检测、语义分割交流群 欢迎关注公众号CV技…

机器学习13(正则化)

文章目录 简介正则化经验风险和结构风险过拟合正则化建模策略 逻辑回归逻辑回归评估器 练习评估器训练与过拟合实验评估器的手动调参 简介 这一节详细探讨关于正则化的相关内容&#xff0c;并就 sklearn 中逻辑回归&#xff08;评估器&#xff09;的参数进行详细解释由于 skle…

javaweb项目实战之myBlog

项目简介 技术栈&#xff1a; Java Mysql Html Ajax Css JS Json 项目说明 &#xff1a;项目使用maven创建&#xff0c;使用MVC架构模式 表示层&#xff1a;通俗讲就是展现给用户的界面和控制器层Servlet&#xff0c;接受请求、封装数据、调用业务 逻辑层&#xff0c;响…

libevent高并发网络编程 - 05_libevent实现http客户端

文章目录 1 http客户端相关的APIevhttp_uri_parse()evhttp_uri_get_scheme()evhttp_uri_get_port()evhttp_uri_get_host()evhttp_uri_get_path()evhttp_uri_get_query()evhttp_connection_base_bufferevent_new()evhttp_request_new()evhttp_make_request()evhttp_request_get_…

刷题刷题,开心

一先来每日一题 在一个仓库里&#xff0c;有一排条形码&#xff0c;其中第 i 个条形码为 barcodes[i]。 请你重新排列这些条形码&#xff0c;使其中任意两个相邻的条形码不能相等。 你可以返回任何满足该要求的答案&#xff0c;此题保证存在答案。 示例 1&#xff1a; 输入&…

基于html+css图展示59

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

Prompt learning 教学[最终篇]:Chatgpt使用场景推荐、优秀学习资料推荐、AI工具推荐

Prompt learning 教学[最终篇]&#xff1a;Chatgpt使用场景推荐、优秀学习资料推荐、AI工具推荐 1.chatgpt使用场景推荐 各位应该在各种平台看到不少可以尝试使用的场景&#xff0c;我这里仅收录&#xff1a; 有意思的场景&#xff1a;一般比较垂直或者小众&#xff0c;或者出…

CobaltStrike项目实战

环境介绍 模拟内网有三台机器&#xff1a;WEB、PC和DC。 WEB服务器有两个网络适配器&#xff0c;适配器1处于NAT模式用于连接外网&#xff0c;适配器2用于内网。 PC和WEB服务器一样&#xff0c;有两个适配器&#xff0c;能够同时访问外网和内网&#xff1b;DC作为域控制器&…

神经网络的训练过程、常见的训练算法、如何避免过拟合

神经网络的训练是深度学习中的核心问题之一。神经网络的训练过程是指通过输入训练数据&#xff0c;不断调整神经网络的参数&#xff0c;使其输出结果更加接近于实际值的过程。本文将介绍神经网络的训练过程、常见的训练算法以及如何避免过拟合等问题。 神经网络的训练过程 神…

henan Problem E. 矩阵游戏

hunan Problem E. 矩阵游戏 Attachments - 2023 CCPC Henan Provincial Collegiate Programming Contest - Codeforces 思路&#xff1a; 我们考虑用dp,定义f[i][j][k],代表从1,1走到i,j并且使用k次变换操作能够获得的最大 价值&#xff0c;那么类似于01背包&#xff0c;接下…

分布式数据库集成解决方案2

分布式数据库集成解决方案2 扩展阅读内部结构1.表空间&#xff08;TABLESPACE&#xff09; # 摘要 : 本文讨论了某公司发货系统的分布式数据库集成解决方案。该公司由于业务的发展&#xff0c;要在另三个城市设立货仓进行发货。为此&#xff0c;需要增加原先的MIS系统实现这一功…

javaweb系列-js函数、数组、字符串

1.4 函数 JavaScript中的函数被设计为执行特定任务的代码块&#xff0c;通过关键字function来定义。JavaScript中定义函数有2种语法。 第一种方法&#xff1a; <script>//定义function add(a,b){return a b;}//调用var a add(1,2);alert(a); </script> 第二种方…

【LeetCode】204.计数质数

204.计数质数&#xff08;中等&#xff09; 思路 埃拉托斯特斯筛法&#xff08;简称埃氏筛法&#xff09;&#xff0c;适用于「判断一个整数是否是质数」&#xff0c;该方法可以在判断一个整数 n 时&#xff0c;同时判断所有小于 n 的整数。 从 1 到 n 进行遍历&#xff0c;假…

内网渗透之权限维持-域控后门-SSPHOOKDSRMSIDSkeleton-Key

权限维持-基于验证DLL加载-SSP 方法一:但如果域控制器重启&#xff0c;被注入内存的伪造的SSP将会丢失。 mimikatz privilege::debug misc::memsspC:\Windows\System32\mimilsa.log 记录登录的账号密码 方法二:使用此方法即使系统重启&#xff0c;也不会影响到持久化的效果…

Plus and Multiply

题目&#xff1a; 题意解析&#xff1a; 有一个无穷大的正整数集合 S&#xff0c;该集合按下面所述方法生成&#xff1a; 数字 1 在集合 S 中。 若数字 x 在该集合中&#xff0c;那么数 xa 和数 xb 均在集合 S 中。&#xff08;其中 a 与 b 为给定常数&#xff09; 现在给出…

快速创作攻略:10分钟让你实现从文章撰写到多平台发布,支持公众号、B站、微博、知乎、头条等20种

快速创作攻略&#xff1a;10分钟让你实现从文章撰写到多平台发布&#xff0c;支持公众号、B站、微博、知乎、头条等20种 很多文字内容创作者&#xff0c; 想分享一个idea&#xff0c; 想介绍一个工具&#xff0c; 想分享经验或知识。 常碰到以下难题&#xff1a; 有个灵感但是要…

神经网络的基本概念、架构和训练方法

神经网络是一种由多个神经元按照一定的拓扑结构相互连接而成的计算模型。其灵感来自于人类大脑中神经元之间的相互作用。 在过去的几十年里&#xff0c;神经网络一直是人工智能领域中的热门研究方向之一。随着深度学习的兴起&#xff0c;神经网络的应用越来越广泛。本文将详细…

目标检测经典论文精读-R-CNN

目标检测经典论文精读-R-CNN R-CNN基本原理产生候选框-Selective Search 论文链接&#xff1a; https://www.cv-foundation.org/openaccess/content_cvpr_2014/papers/Girshick_Rich_Feature_Hierarchies_2014_CVPR_paper.pdf 论文技术报告链接&#xff1a; https://arxiv.org/…

无热图不组学!高阶文献热图R语言绘制小专场拿捏

一、概述 近在阅读近五年的一区高分的机器学习文献&#xff0c;其中有一种图出现频率特别高——热图。《Machine Learning and the Future of Cardiovascular Care: JACC State-of-the-Art Review》 《Comparison of Machine Learning Methods for Predicting Outcomes After…