本帖最后由 RISCVLAR 于 2023-12-27 18:44 编辑
文章来源:https://www.cnblogs.com/liaigu/p/17913243.html
测试平台:CH32V307VCT6+RTL8211FS PS:沁恒CH32V307VCT6-R2开发板带有千兆PHY(RTL8211FS),本次测试基于该开发板。该开发板原理图以及PCB图可在CH32V 307 EVT中获取到,如下图。EVT下载链接如下: https://www.wch.cn/downloads/CH32V307EVT_ZIP.html
MCU介绍:CH32V307是一款RISC-V内核的互联型MCU,系统主频最大支持144MHz,FLASH 256KB,RAM 64KB,FLASH、RAM大小可配置修改。支持以太网通信,集成千兆MAC,并内置10M PHY。本次测试主要对MAC层收发速度进行一个测试。
PHY介绍:RTL8211FS是一款具有高集成度的千兆以太网PHY芯片,可用于网卡、路由器、交换机等网络设备。
测试程序:CH32V307 EVT提供了以太网相关例程,包含了CH32V307 MAC_RAW的例程,如下图。MAC_RAM例程为MAC层接收和发送数据演示例程,本次测试程序基于该例程进行修改,进行接收、发送的速度测试。 例程修改:由于测试是进行MAC层最大收发速度测试,以太网驱动文件选择RGMII.c这个文件,其他文件排除编译,如下图:
例程讲解:测试例程为MAC层数据包发送和接收演示例程,不涉及以太网协议栈的处理,单纯进行MAC层的收发。关于该例程main函数,如下: <font face="Tahoma">/*********************************************************************
* @fn main
*
* [url=home.php?mod=space&uid=247401]@brief[/url] Main program
*
* [url=home.php?mod=space&uid=266161]@return[/url] none
*/
int main(void)
{
u8 i;
uint32_t PktCnt = 0;
SystemCoreClockUpdate();
Delay_Init();
USART_Printf_Init(115200); //USART initialize
printf("MAC RAW Test\r\n");
printf("SystemClk:%d\r\n", SystemCoreClock);
printf("ChipID:%08x\r\n", DBGMCU_GetCHIPID());
WCHNET_GetMacAddr(MACAddr); //get the chip MAC address
printf("mac addr:");
for(i = 0; i < 6; i++)
printf("%x ", MACAddr[i]);
printf("\n");
TIM2_Init();
ETH_Init(MACAddr);
/* change source MAC address */
memcpy(&ARPPackage[6], MACAddr, 6);
memcpy(&ARPPackage[22], MACAddr, 6);
while(1)
{
WCHNET_MainTask();
if(LinkSta)
{
MACRAW_Tx(ARPPackage, sizeof(ARPPackage));
if(++PktCnt % 100 == 0)
printf("PktCnt:%d\r\n",PktCnt);
Delay_Ms(100);
}
}
}</font>
SystemCoreClockUpdate函数、Delay_Init函数、USART_Printf_Init函数主要进行系统时钟、延时函数、串口打印函数的初始化,不做具体介绍。 WCHNET_GetMacAddr函数主要用于获取CH32V307的MAC地址。MAC地址,即发送这个帧的设备希望接收这个帧的设备地址,由IEEE为生产商分配,全球唯一,6字节48 位。 CH32V307的MAC地址出厂时已烧录在芯片内部。地址的发送遵循低有效位在前的原则。 <font face="Tahoma">* @fn WCHNET_GetMacAddr
*
* [url=home.php?mod=space&uid=247401]@brief[/url] Get the MAC address
*
* [url=home.php?mod=space&uid=266161]@return[/url] none.
*/
void WCHNET_GetMacAddr( uint8_t *p )
{
uint8_t i;
uint8_t *macaddr = (uint8_t *)(ROM_CFG_USERADR_ID+5);
for(i=0;i<6;i++)
{
*p = *macaddr;
p++;
macaddr--;
}
}</font>
WCHNET_GetMacAddr函数具体内容如上,MAC地址存放于0x1FFFF7E8+5起始处,读取对应地址可获取CH32V307内部MAC地址。main函数中获取MAC地址之后将其打印出来。 TIM2_Init函数主要用于检测以太网PHY连接的状态。定时器10ms进一次中断。在定时器中断函数中,主要对定时器周期进行加法计数。 LocalTime主要用于查询PHY状态,1s查询一次,PHY状态查询函数如下: <font face="Tahoma">/*********************************************************************
* @fn WCHNET_QueryPhySta
*
* [url=home.php?mod=space&uid=247401]@brief[/url] Query external PHY status
*
* [url=home.php?mod=space&uid=266161]@return[/url] none.
*/
#if !LINK_STAT_ACQUISITION_METHOD
void WCHNET_QueryPhySta(void)
{
u16 phy_stat;
if(QUERY_STAT_FLAG){ /* Query the PHY link status every 1s */
LastQueryPhyTime = LocalTime / 1000;
ETH_WritePHYRegister( PHY_ADDRESS, 0x1F, 0x0a43 );
/*In some cases the status is not updated in time,
* so read this register twice to get the correct status value.*/
ETH_ReadPHYRegister( PHY_ADDRESS, 0x1A);
phy_stat = ETH_ReadPHYRegister( PHY_ADDRESS, 0x1A) & 0x04;
if(phy_stat != LastPhyStat){
ETH_PHYLink();
}
}
}
#endif</font>
WCHNET_QueryPhySta函数为外部PHY连接状态查询函数,函数中,首先对QUERY_STAT_FLAG标志进行判断,QUERY_STAT_FLAG的定义如下: 根据QUERY_STAT_FLAG的定义,当LocaTime的值为1000时,其值为1。WCHNET_QueryPhySta函数中if条件成立,执行if判断内的语句。在if语句中,LastQueryPhyTime记录查询次数,然后通过对PHY寄存器的操作查询外部PHY的连接状态;ETH_WritePHYRegister( PHY_ADDRESS, 0x1F, 0x0a43 );完成对PHY寄存器的写入,该操作相当于选择PHY寄存器0xa43这个页面,默认是0xa42,如下图 选择对应的PHY寄存器页面之后,可对该页面的寄存器进行读取。 ETH_ReadPHYRegister( PHY_ADDRESS, 0x1A);函数对0x1A寄存器进行读取,0x1A寄存器具体如下: phy_stat = ETH_ReadPHYRegister( PHY_ADDRESS, 0x1A) & 0x04;主要对0x1A寄存器位2的值进行判断,位2表示Link连接状态,当Link连接,该位值为1,执行ETH_PHYLink函数。 ETH_PHYLink函数内容如下: <font face="Tahoma">/*********************************************************************
* @fn ETH_PHYLink
*
* [url=home.php?mod=space&uid=247401]@brief[/url] Configure MAC parameters after the PHY Link is successful.
*
* @param none.
*
* [url=home.php?mod=space&uid=266161]@return[/url] none.
*/
void ETH_PHYLink( void )
{
u32 phy_stat;
ETH_WritePHYRegister( gPHYAddress, 0x1F, 0x0a43 );
/*In some cases the status is not updated in time,
* so read this register twice to get the correct status value.*/
ETH_ReadPHYRegister( gPHYAddress, 0x1A);
phy_stat = ETH_ReadPHYRegister( gPHYAddress, 0x1A);
#if !LINK_STAT_ACQUISITION_METHOD
LastPhyStat = phy_stat & 0x04;
#endif
if( phy_stat & 0x04 )
{
printf("Link Suc\r\n");
if( phy_stat & 0x08 )
{
ETH->MACCR |= ETH_Mode_FullDuplex;
}
else
{
ETH->MACCR &= ~ETH_Mode_FullDuplex;
}
if( (phy_stat & 0x30) == 0x00 )
{
ETH->MACCR &= ~(ETH_Speed_100M|ETH_Speed_1000M);
}
else if( (phy_stat & 0x30) == 0x10 )
{
ETH->MACCR &= ~(ETH_Speed_100M|ETH_Speed_1000M);
ETH->MACCR |= ETH_Speed_100M;
}
else if( (phy_stat & 0x30) == 0x20 )
{
ETH->MACCR &= ~(ETH_Speed_100M|ETH_Speed_1000M);
ETH->MACCR |= ETH_Speed_1000M;
}
ETH_Start( );
LinkSta = 1;
}
else {
LinkSta = 0;
}
phy_stat = ETH_ReadPHYRegister( gPHYAddress, 0x1D); /* Clear the Interrupt status */
}</font>
具体操作解释如下: 该if语句主要对0x1A寄存器位3的值进行判断,位3表示PHY工作模式,当该位值为1,工作模式为全双工模式,否则为半双工模式。 该if语句主要对0x1A寄存器位4、位5的值进行判断,位4、5表示PHY工作在10M、100M或1000M。00表示工作在10M模式,01表示100M,11表示1000M。 以上判断设置完成后,调用ETH_Start函数开启MAC和DMA的接收传输,ETH_Start函数具体内容如下: <font face="Tahoma">/*********************************************************************
* @fn ETH_Start
*
* [url=home.php?mod=space&uid=247401]@brief[/url] Enables ENET MAC and DMA reception/transmission.
*
* [url=home.php?mod=space&uid=266161]@return[/url] none
*/
void ETH_Start(void)
{
ETH_MACTransmissionCmd(ENABLE);
ETH_FlushTransmitFIFO();
ETH_MACReceptionCmd(ENABLE);
ETH_DMATransmissionCmd(ENABLE);
ETH_DMAReceptionCmd(ENABLE);
}</font>
ETH_MACTransmissionCmd(ENABLE);函数使能开启MAC发送器; ETH_FlushTransmitFIFO();使能复位DMA发送FIFO; ETH_MACReceptionCmd(ENABLE);函数使能开启MAC接收器; ETH_DMATransmissionCmd(ENABLE);使能开启发送控制位,把发送进程置位运行状态; ETH_DMAReceptionCmd(ENABLE);使能开始接收控制位,开启接收流程,DMA从当前位置取接收描述符或者从标识符表头位置取接收描述符。
ETH_Start函数执行完成后,将LinkSta标志置1,phy_stat = ETH_ReadPHYRegister( gPHYAddress, 0x1D);函数将清除相应状态标志位。
回归到main函数,TIM2初始化完成后,进行以太网初始化,ETH_Init(MACAddr);函数具体内容如下: <font face="Tahoma">/*********************************************************************
* @fn ETH_Init
*
* @brief Ethernet initialization.
*
* @return none
*/
void ETH_Init( uint8_t *macAddr )
{
ChipId = DBGMCU_GetCHIPID();
ETH_Configuration( macAddr );
ETH_DMATxDescChainInit(DMATxDscrTab, MACTxBuf, ETH_TXBUFNB);
ETH_DMARxDescChainInit(DMARxDscrTab, MACRxBuf, ETH_RXBUFNB);
pDMARxSet = DMARxDscrTab;
pDMATxSet = DMATxDscrTab;
NVIC_EnableIRQ(ETH_IRQn);
NVIC_SetPriority(ETH_IRQn, 0);
}</font>
ChipId = DBGMCU_GetCHIPID();该语句主要用于获取新片ID; ETH_Configuration( macAddr );函数主要对以太网进行配置,该函数内容如下: <font face="Tahoma">/*********************************************************************
* @fn ETH_Configuration
*
* @brief Ethernet configure.
*
* @return none
*/
void ETH_Configuration( uint8_t *macAddr )
{
ETH_InitTypeDef ETH_InitStructure;
uint16_t timeout = 10000;
/* Enable Ethernet MAC clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ETH_MAC | \
RCC_AHBPeriph_ETH_MAC_Tx | \
RCC_AHBPeriph_ETH_MAC_Rx, ENABLE);
gPHYAddress = PHY_ADDRESS;
/* Enable 1G MAC*/
EXTEN->EXTEN_CTR |= EXTEN_ETH_RGMII_SEL;
RCC_ETH1GCLKConfig(RCC_ETH1GCLKSource_PB1_IN);
RCC_ETH1G_125Mcmd(ENABLE);
/* Enable RGMII GPIO */
ETH_RGMIIPinInit();
/* Reset ETHERNET on AHB Bus */
ETH_DeInit();
/* Software reset */
ETH_SoftwareReset();
/* Wait for software reset */
do{
Delay_Us(10);
if( !--timeout ) break;
}while(ETH->DMABMR & ETH_DMABMR_SR);
/* ETHERNET Configuration */
/* Call ETH_StructInit if you don't like to configure all ETH_InitStructure parameter */
ETH_StructInit(Ð_InitStructure);
/* Fill ETH_InitStructure parameters */
/*------------------------ MAC -----------------------------------*/
ETH_InitStructure.ETH_Mode = ETH_Mode_FullDuplex; //全双工模式
ETH_InitStructure.ETH_Speed = ETH_Speed_1000M; //1000M模式
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; //填充&CRC 自动剥离使能位,配置MAC不改变帧的内容
/* Filter function configuration */
ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Disable; //MAC只把通过了过滤器的帧转发到接收队列中
ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable; //关闭混杂模式
ETH_InitStructure.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable; //接收所有广播帧
ETH_InitStructure.ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_Perfect; //多播过滤,完美地址过滤
ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect; //单播过滤,完美地址过滤
/*------------------------ DMA -----------------------------------*/
/* When we use the Checksum offload feature, we need to enable the Store and Forward mode:
the store and forward guarantee that a whole frame is stored in the FIFO, so the MAC can insert/verify the checksum,
if the checksum is OK the DMA can handle the frame otherwise the frame is dropped */
ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable;
ETH_InitStructure.ETH_ReceiveStoreForward = ETH_ReceiveStoreForward_Enable;
ETH_InitStructure.ETH_TransmitStoreForward = ETH_TransmitStoreForward_Enable;
ETH_InitStructure.ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Enable; //转发错误帧控制位,接收FIFO会丢弃有错误的帧
ETH_InitStructure.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Enable; //转发过短帧控制位,接收FIFO转发长度过短的帧
ETH_InitStructure.ETH_SecondFrameOperate = ETH_SecondFrameOperate_Disable;
/* Configure Ethernet */
ETH_RegInit( Ð_InitStructure, gPHYAddress );
/* Configure MAC address */
ETH->MACA0HR = (uint32_t)((macAddr[5]<<8) | macAddr[4]);
ETH->MACA0LR = (uint32_t)(macAddr[0] | (macAddr[1]<<8) | (macAddr[2]<<16) | (macAddr[3]<<24));
/* Mask the interrupt that Tx good frame count counter reaches half the maximum value */
ETH->MMCTIMR = ETH_MMCTIMR_TGFM;
/* Mask the interrupt that Rx good unicast frames counter reaches half the maximum value */
/* Mask the interrupt that Rx crc error counter reaches half the maximum value */
ETH->MMCRIMR = ETH_MMCRIMR_RGUFM | ETH_MMCRIMR_RFCEM;
ETH_DMAITConfig(ETH_DMA_IT_NIS |\
ETH_DMA_IT_R |\
ETH_DMA_IT_T |\
ETH_DMA_IT_AIS |\
ETH_DMA_IT_RBU,\
ENABLE);
/* Configure the polarity and delay of TXC */
RGMII_TXC_Delay(0, 4);
#if LINK_STAT_ACQUISITION_METHOD
/* Configure the PHY interrupt function, the supported chip is: RTL8211FS */
PHY_InterruptInit( );
/* Configure EXTI Line7. */
EXTI_Line_Init( );
#endif
}</font>
首先开启以太网MAC相关时钟; EXTEN->EXTEN_CTR |= EXTEN_ETH_RGMII_SEL;用于启用1000M以太网RGMI接口并使能时钟; RCC_ETH1GCLKConfig(RCC_ETH1GCLKSource_PB1_IN); 用于设置PB1引脚输入千兆以太网 125M时钟; RCC_ETH1G_125Mcmd(ENABLE);用于使能开启千兆以太网125M时钟; ETH_RGMIIPinInit();用于将RGMII引脚初始化配置; ETH_DeInit();用于以太网复位; ETH_SoftwareReset();用于软件复位,置1时,MAC的DMA控制器复位MAC所有子系统的内部寄存器和逻辑电路。 关于以太网的MAC、DMA等配置,具体看代码注释
<font face="Tahoma">/* Configure MAC address */
ETH->MACA0HR = (uint32_t)((macAddr[5]<<8) | macAddr[4]);
ETH->MACA0LR = (uint32_t)(macAddr[0] | (macAddr[1]<<8) | (macAddr[2]<<16) | (macAddr[3]<<24));</font>
以上两句函数主要用于对MAC地址的配置,MAC地址一共6个字节48位。
<font face="Tahoma">/* Mask the interrupt that Tx good frame count counter reaches half the maximum value */
ETH->MMCTIMR = ETH_MMCTIMR_TGFM;</font>
该操作用于屏蔽发送好帧计数器值达到一半时产生的中断。
<font face="Tahoma">/* Mask the interrupt that Rx good unicast frames counter reaches half the maximum value */
/* Mask the interrupt that Rx crc error counter reaches half the maximum value */
ETH->MMCRIMR = ETH_MMCRIMR_RGUFM | ETH_MMCRIMR_RFCEM;</font>
该操作用于屏蔽接收好帧计数器值达到一半时产生的中断以及屏蔽接收到CRC错误的帧计数器值达到一半时产生的中断。
<font face="Tahoma">ETH_DMAITConfig(ETH_DMA_IT_NIS |ETH_DMA_IT_R |ETH_DMA_IT_T |ETH_DMA_IT_AIS |ETH_DMA_IT_RBU,ENABLE);</font>
该操作用于使能开启以太网DMA状态寄存器的正常中断汇总位(NIS)、接收状态完成位(R)、发送完成标志位(T)、异常中断汇总位(AIS)、接收缓存不可用状态位(RBU)。
<font face="Tahoma">/* Configure the polarity and delay of TXC */
RGMII_TXC_Delay(0, 4);</font>
该操作主要用于将TCES选择的TXC直接作为芯片输出的GTX_CLK以及配置延迟发送时钟,延迟2ns。 延时时间计算公式:
Tdalay=TCD(ETH_MACCR[31:29])*0.5ns;
回到main函数,以太网初始化配置完成后主要进行如下操作: <font face="Tahoma">/* change source MAC address */
memcpy(&ARPPackage[6], MACAddr, 6);
memcpy(&ARPPackage[22], MACAddr, 6);</font>
主要将MAC地址复制到ARP pack包数组中。关于ARP包,解析如下: 以太网信息: ARPPackage[0]到ARPPackage[5],前6个字节为目的地址; ARPPackage[6]到ARPPackage[11],该6个字节为源地址; ARPPackage[12]到ARPPackage[13],该两个字节表示协议类型,0x806表示ARP数据包 地址解析协议信息: ARPPackage[14]到ARPPackage[15],该两个字节表示硬件类型,0x01表示以太网 ARPPackage[16]到ARPPackage[17],该两个字节表示互联网协议版本,0x800表示IPV4 ARPPackage[18]到ARPPackage[19],该两个字节分别表示硬件长度6和协议长度4 ARPPackage[20]到ARPPackage[21],该两个字节表示操作码,1表示ARP请求 ARPPackage[22]到ARPPackage[27],该6个字节表示源MAC地址; ARPPackage[28]到ARPPackage[31],该4个字节表示源IP地址; ARPPackage[32]到ARPPackage[37],该6个字节表示目标MAC地址; ARPPackage[38]到ARPPackage[41],该4个字节表示目标IP地址; 额外数据信息: 其余均为额外数据
回到main函数,接下来是while循环,首先执行WCHNET_MainTask();函数,该函数具体内容如下: <font face="Tahoma">/*********************************************************************
* @fn WCHNET_MainTask
*
* @brief library main task function
*
* @param none.
*
* @return none.
*/
void WCHNET_MainTask(void)
{
RecDataPolling();
#if !LINK_STAT_ACQUISITION_METHOD
WCHNET_QueryPhySta(); /* Query external PHY status */
#endif
}</font>
该函数中,首先会执行RecDataPolling函数,处理接收到的数据,RecDataPolling函数具体内容如下: <font face="Tahoma">/*********************************************************************
* @fn RecDataPolling
*
* @brief process received data.
*
* @param none.
*
* @return none.
*/
void RecDataPolling(void)
{
uint32_t length, buffer;
while(!(pDMARxSet->Status & ETH_DMARxDesc_OWN))
{
if( !(pDMARxSet->Status & ETH_DMARxDesc_ES) &&\
(pDMARxSet->Status & ETH_DMARxDesc_LS) &&\
(pDMARxSet->Status & ETH_DMARxDesc_FS) )
{
/* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */
length = (pDMARxSet->Status & ETH_DMARxDesc_FL) >> 16;
/* Get the addrees of the actual buffer */
buffer = pDMARxSet->Buffer1Addr;
/* Do something*/
printf("rec data:%d bytes\r\n",length);
printf("data:%x\r\n",*((uint8_t *)buffer));
}
pDMARxSet->Status = ETH_DMARxDesc_OWN;
pDMARxSet = (ETH_DMADESCTypeDef *)pDMARxSet->Buffer2NextDescAddr;
}
}</font>
while(!(pDMARxSet->Status & ETH_DMARxDesc_OWN)) 该句主要对接收描述符位31进行判断,接收DMA描述符如下图: if( !(pDMARxSet->Status & ETH_DMARxDesc_ES) &&\ (pDMARxSet->Status & ETH_DMARxDesc_LS) &&\ (pDMARxSet->Status & ETH_DMARxDesc_FS) ) 该if语句主要对接收描述符的ES位、LS位、FS位进行判断,当ES位为0(表示没有错误)、LS位为1(表示此描述符指示的缓冲区里包含帧的结束部分)、FS位为1(表示此描述符指示的缓冲区里包含帧的开头部分)时,该if判断条件成立。
/* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */ length = (pDMARxSet->Status & ETH_DMARxDesc_FL) >> 16; 该函数表示接收的字节长度,FL解释如下: /* Get the addrees of the actual buffer */ buffer = pDMARxSet->Buffer1Addr; 关于Buffer1Addr介绍如下,定义了接收缓冲区的地址。 pDMARxSet->Status = ETH_DMARxDesc_OWN; pDMARxSet = (ETH_DMADESCTypeDef *)pDMARxSet->Buffer2NextDescAddr; 关于Buffer2NextDescAddr的介绍,如上图,表示缓冲区2的地址。
执行完RecDataPolling()函数之后,开始执行WCHNET_QueryPhySta()函数,查询PHY的状态。关于WCHNET_QueryPhySta()函数,前面有介绍,在此不再赘述。
main函数while循环中,执行完成WCHNET_MainTask()函数之后,开始执行if判断语句,如下: <font face="Tahoma">if(LinkSta)
{
MACRAW_Tx(ARPPackage, sizeof(ARPPackage));
if(++PktCnt % 100 == 0)
printf("PktCnt:%d\r\n",PktCnt);
Delay_Ms(100);
}</font>
当PHY已连接,开始发送ARP数据包,MACRAW_Tx(ARPPackage, sizeof(ARPPackage))函数具体内容如下: <font face="Tahoma">/*********************************************************************
* @fn MACRAW_Tx
*
* @brief Ethernet sends data frames in chain mode.
*
* @param buff send buffer pointer
* len Send data length
*
* @return Send status.
*/
uint32_t MACRAW_Tx(uint8_t *buff, uint16_t len)
{
/* 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 ETH_ERROR;
}
/* Setting the Frame Length: bits[12:0] */
DMATxDescToSet->ControlBufferSize = (len & ETH_DMATxDesc_TBS1);
memcpy((uint8_t *)DMATxDescToSet->Buffer1Addr, buff, len);
/* Setting the last segment and first segment bits (in this case a frame is transmitted in one descriptor) */
DMATxDescToSet->Status |= ETH_DMATxDesc_LS | ETH_DMATxDesc_FS;
/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
DMATxDescToSet->Status |= ETH_DMATxDesc_OWN;
/* Clear TBUS ETHERNET DMA flag */
ETH->DMASR = ETH_DMASR_TBUS;
/* Resume DMA transmission*/
ETH->DMATPDR = 0;
/* Update the ETHERNET DMA global Tx descriptor with next Tx descriptor */
/* Chained Mode */
/* Selects the next DMA Tx descriptor list for next buffer to send */
DMATxDescToSet = (ETH_DMADESCTypeDef*) (DMATxDescToSet->Buffer2NextDescAddr);
/* Return SUCCESS */
return ETH_SUCCESS;
}</font>
/* 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 ETH_ERROR; } 当该描述符归CPU所有,修改描述符的值
/* Setting the Frame Length: bits[12:0] */ DMATxDescToSet->ControlBufferSize = (len & ETH_DMATxDesc_TBS1); 设置发送缓冲区1的大小,如下图: memcpy((uint8_t *)DMATxDescToSet->Buffer1Addr, buff, len); 将buff的数据放到发送缓冲区1地址,如下图 /* Setting the last segment and first segment bits (in this case a frame is transmitted in one descriptor) */ DMATxDescToSet->Status |= ETH_DMATxDesc_LS | ETH_DMATxDesc_FS; 配置表示该描述符指示的缓冲区包含帧的开头和结束部分 /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ DMATxDescToSet->Status |= ETH_DMATxDesc_OWN; 配置该描述符归DMA所有,禁止CPU修改
/* Clear TBUS ETHERNET DMA flag */ ETH->DMASR = ETH_DMASR_TBUS; /* Resume DMA transmission*/ ETH->DMATPDR = 0; 暂停发送流程之后重启发送流程
/* Update the ETHERNET DMA global Tx descriptor with next Tx descriptor */ /* Chained Mode */ /* Selects the next DMA Tx descriptor list for next buffer to send */ DMATxDescToSet = (ETH_DMADESCTypeDef*) (DMATxDescToSet->Buffer2NextDescAddr); 为要发送的下一个缓冲区选择下一个DMA Tx描述符列表
以上就是整个MAC_RAW例程发送与接收的具体讲解介绍。下面将在该例程的基础上进行发送与接收的测试。
发送测试:基于MAC_RAM例程进行修改,由于测试最大速度,将例程中打印等相关函数去掉。如main函数中while循环中的if判断以及打印去掉。此外,WCHNET_MainTask函数中包含接收部分,将接收也去掉,如下图: 上图红框部分程序注释掉以后,编译下载到开发板,测试速度如下: 发送速度最大能达到900M左右。
以太网帧长度最大是1518字节,目前例程配置ARP包最大长度为490个字节,加上CRC4个字节即494字节。为测试最大速度,将ARP设置为最大,再增加1518-494=1024字节。ARP包配置为最大之后,测试发送速度最大为970Mbps左右,如下图: 若要验证发送的数据是否正确,可以通过Wireshark查看发送的数据,如下图:
接收测试:同样基于MAC_RAM例程进行修改,注释掉程序中发送部分以及打印部分,如下图: 利用两个开发板进行MAC层的收发测试,在接收中断中接收1000M的数据进行一次GPIO翻转,测试GPIO翻转一次的时间为1.072s. 通过该时间,测得接收速度为1000/1.072约等于932.8Mbps。
结果总结:发送速度:970Mbps 接收速度:933Mbps
|