站长之家seo概况查询,应用中心安装,东莞债务优化公司,站长工具搜索0 工具准备
1.野火 stm32f407霸天虎开发板
2.LAN8720数据手册
3.STM32F4xx中文参考手册1 以太网数据接收及发送
1.1 以太网数据接收#xff08;轮询#xff09;
1.1.1 检查是否接收到一帧完整报文
使用轮询的方式接收以太网数据是一种简单但是效率低下的方法#xff0c;…0 工具准备
1.野火 stm32f407霸天虎开发板
2.LAN8720数据手册
3.STM32F4xx中文参考手册1 以太网数据接收及发送
1.1 以太网数据接收轮询
1.1.1 检查是否接收到一帧完整报文
使用轮询的方式接收以太网数据是一种简单但是效率低下的方法为了保证及时处理以太网数据我们需要在主循环内高频轮询是否接收到了以太网数据。轮询的函数为ETH_CheckFrameReceived内容如下
uint32_t ETH_CheckFrameReceived(void)
{/* check if last segment */if(((DMARxDescToGet-Status ETH_DMARxDesc_OWN) (uint32_t)RESET) ((DMARxDescToGet-Status ETH_DMARxDesc_LS) ! (uint32_t)RESET)) {DMA_RX_FRAME_infos-Seg_Count;if (DMA_RX_FRAME_infos-Seg_Count 1){DMA_RX_FRAME_infos-FS_Rx_Desc DMARxDescToGet;}DMA_RX_FRAME_infos-LS_Rx_Desc DMARxDescToGet;return 1;}/* check if first segment */else if(((DMARxDescToGet-Status ETH_DMARxDesc_OWN) (uint32_t)RESET) ((DMARxDescToGet-Status ETH_DMARxDesc_FS) ! (uint32_t)RESET)((DMARxDescToGet-Status ETH_DMARxDesc_LS) (uint32_t)RESET)){DMA_RX_FRAME_infos-FS_Rx_Desc DMARxDescToGet;DMA_RX_FRAME_infos-LS_Rx_Desc NULL;DMA_RX_FRAME_infos-Seg_Count 1; DMARxDescToGet (ETH_DMADESCTypeDef*) (DMARxDescToGet-Buffer2NextDescAddr);}/* check if intermediate segment */ else if(((DMARxDescToGet-Status ETH_DMARxDesc_OWN) (uint32_t)RESET) ((DMARxDescToGet-Status ETH_DMARxDesc_FS) (uint32_t)RESET)((DMARxDescToGet-Status ETH_DMARxDesc_LS) (uint32_t)RESET)){(DMA_RX_FRAME_infos-Seg_Count) ;DMARxDescToGet (ETH_DMADESCTypeDef*) (DMARxDescToGet-Buffer2NextDescAddr);} return 0;
}当以太网帧大于我们设置的DMA描述符buffer大小时以太网帧将会被分成若干段被存储在不同的DMA描述符中DMA描述符使用接收描述符字0来表示当前DMA描述符是第一个描述符或最后一个描述符或中间描述符 当DMA描述符是首个描述符时将段计数置为1保存首个描述符到FS_Rx_Desc同时将Rx描述符指向下一个DMA描述符当DMA描述符是中间描述符时将段计数1同时将Rx描述符指向下一个DMA描述符当DMA描述符是最后一个描述符时将段计数1保存最后一个描述符到LS_Rx_Desc如果段计数为1也就是一个完整的以太网帧被保存在一个DMA描述符内保存最后一个描述符到FS_Rx_Desc同时返回1表明接收到了一帧完整以太网数据。
1.1.2 读取一帧完整报文
在我们检查到接收了一帧完整报文后就可以调用low_level_input函数读取该帧报文。
FrameTypeDef low_level_input(void)
{struct pbuf *p, *q;uint32_t len;FrameTypeDef frame;u8 *buffer;__IO ETH_DMADESCTypeDef *DMARxDesc;uint32_t bufferoffset 0;uint32_t payloadoffset 0;uint32_t byteslefttocopy 0;uint32_t i 0;/* get received frame 接收报文 */frame ETH_Get_Received_Frame();/* Obtain the size of the packet and put it into the len variable. 获取数据包大小 */len frame.length;buffer (u8 *)frame.buffer;/* Release descriptors to DMA 将描述符释放到DMA */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 恢复DMA接收 */ETH-DMARPDR 0;}return frame;
}该函数操作流程如下: 1获取报文长度 调用ETH_Get_Received_Frame函数会返回以太网帧最后一个描述符存储的报文长度和buffer地址。我们可以将DMA描述符buffer数据拷贝到协议栈buffer中。 2释放DMA控制权给DMA 在我们拷贝完了DMA描述符的buffer数据后需要释放DMA控制权相关语句如下
for (i 0; i DMA_RX_FRAME_infos-Seg_Count; i){DMARxDesc-Status ETH_DMARxDesc_OWN;DMARxDesc (ETH_DMADESCTypeDef *)(DMARxDesc-Buffer2NextDescAddr);}DMA_RX_FRAME_infos-Seg_Count 0;上述语句将首个DMA描述符到最后一个DMA描述符的控制权交给DMA最后清空段计数。 3检查DMA状态寄存器 涉及到寄存器如下 相关bit描述 这里检查位7是否为1如果为1则设置DMASR寄存器的值为0x00000080然后恢复DMA接收。相关语句如下
if ((ETH-DMASR ETH_DMASR_RBUS) ! (u32)RESET){/* Clear RBUS ETHERNET DMA flag */ETH-DMASR ETH_DMASR_RBUS;/* Resume DMA reception 恢复DMA接收 */ETH-DMARPDR 0;}DMARPDR寄存器描述如下
1.2 以太网数据接收中断
在主循环或者线程内使用轮询的方式判断是否接收到以太网报文效率比较低下而且容易出现未及时处理接收报文导致溢出的问题。因此建议使能ETH接收中断在中断内释放信号量然后处理以太网数据。 打开ETH接收中断语句如下
NVIC_InitStructure.NVIC_IRQChannel ETH_IRQn; // 以太网中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 0X00; // 中断寄存器组2最高优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority 0X00;
NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE;
NVIC_Init(NVIC_InitStructure);ETH_DMAClearITPendingBit(ETH_DMA_IT_R);
ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);
if (EthStatus ETH_SUCCESS)
{/* 使能接收中断 */ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R, ENABLE);
}使能接收中断涉及的寄存器如下 接收中断服务函数如下
void ETH_IRQHandler(void)
{int i;FrameTypeDef frame;while(ETH_CheckFrameReceived() ! 0) // 检测是否收到数据包{frame low_level_input();printf(Len : %d\r\n, frame.length);for (i 0; i frame.length; i){printf(%02X , ((u8 *)frame.buffer)[i]);}printf(\r\n);}ETH_DMAClearITPendingBit(ETH_DMA_IT_R);ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);
} 1特别要注意我们这里使用的是while而不是if因为每次触发接收中断可能接收到了多个报文我们应该尽快将报文全部取出避免DMA描述符占用标志一直是CPU。 2上面的中断服务函数只是用于演示我们直接在中断内打印接收到的报文。正常操作是释放信号量或者将接收标志置位通知RTOS的接收线程或者裸机下的主循环内的回调函数处理。
1.3 以太网数据发送
uint8_t low_level_output(uint8_t *sendBuffer, uint16_t len)
{uint8_t errval;struct pbuf *q;u8 *buffer (u8 *)(DMATxDescToSet-Buffer1Addr);__IO ETH_DMADESCTypeDef *DmaTxDesc;uint16_t framelength 0;uint32_t bufferoffset 0;uint32_t byteslefttocopy 0;uint32_t payloadoffset 0;DmaTxDesc DMATxDescToSet;bufferoffset 0;memcpy((u8_t *)buffer, (u8_t *)sendBuffer, len);/* Prepare transmit descriptors to give to DMA 准备发送描述符给DMA使用 */ETH_Prepare_Transmit_Descriptors(len);errval 0;error:errval -1;/* 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;}return errval;
}相比起接收以太网数据发送则显得比较简单因为DMA描述符的主动操作方在CPU这一侧。上述函数的操作如下 1将待发送数据拷贝到当前跟踪的发送DMA描述符 2将跟踪的发送DMA描述符控制权交给DMA设置DMA描述符相关状态
uint32_t ETH_Prepare_Transmit_Descriptors(u16 FrameLength)
{ uint32_t buf_count 0, size0,i0;__IO ETH_DMADESCTypeDef *DMATxDesc;/* 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;}DMATxDesc DMATxDescToSet;if (FrameLength ETH_TX_BUF_SIZE){buf_count FrameLength/ETH_TX_BUF_SIZE;if (FrameLength%ETH_TX_BUF_SIZE) buf_count;}else buf_count 1;if (buf_count 1){/*set LAST and FIRST segment */DMATxDesc-Status |ETH_DMATxDesc_FS|ETH_DMATxDesc_LS;/* Set frame size */DMATxDesc-ControlBufferSize (FrameLength ETH_DMATxDesc_TBS1);/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */DMATxDesc-Status | ETH_DMATxDesc_OWN;DMATxDesc (ETH_DMADESCTypeDef *)(DMATxDesc-Buffer2NextDescAddr);}else{for (i0; i buf_count; i){/* Clear FIRST and LAST segment bits */DMATxDesc-Status ~(ETH_DMATxDesc_FS | ETH_DMATxDesc_LS);if (i0) {/* Setting the first segment bit */DMATxDesc-Status | ETH_DMATxDesc_FS; }/* Program size */DMATxDesc-ControlBufferSize (ETH_TX_BUF_SIZE ETH_DMATxDesc_TBS1);if (i (buf_count-1)){/* Setting the last segment bit */DMATxDesc-Status | ETH_DMATxDesc_LS;size FrameLength - (buf_count-1)*ETH_TX_BUF_SIZE;DMATxDesc-ControlBufferSize (size ETH_DMATxDesc_TBS1);}/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */DMATxDesc-Status | ETH_DMATxDesc_OWN;DMATxDesc (ETH_DMADESCTypeDef *)(DMATxDesc-Buffer2NextDescAddr);}}DMATxDescToSet DMATxDesc;/* When Tx Buffer unavailable flag is set: clear it and resume transmission */if ((ETH-DMASR ETH_DMASR_TBUS) ! (u32)RESET){/* Clear TBUS ETHERNET DMA flag */ETH-DMASR ETH_DMASR_TBUS;/* Resume DMA transmission*/ETH-DMATPDR 0;}/* Return SUCCESS */return ETH_SUCCESS;
}这个函数篇幅有点长这里举例说一下当我们发送的以太网报文可以被一个发送DMA描述符容纳时的操作 2.1设置发送DMA描述符的LS和FS位为1也就是一个发送DMA描述符对应一个以太网报文
DMATxDesc-Status |ETH_DMATxDesc_FS|ETH_DMATxDesc_LS;2.2设置报文长度
DMATxDesc-ControlBufferSize (FrameLength ETH_DMATxDesc_TBS1);2.3设置发送DMA描述符控制权为DMA
DMATxDesc-Status | ETH_DMATxDesc_OWN)2.4将跟踪发送DMA描述符指向下一个发送DMA描述符 3当Tx Buffer不可用标志被设置时清除该标志并恢复传输 这里的DMA就相当于头指针而CPU则相当于尾指针。
2 总结
1以太网数据接收可以使用轮询和中断2种方式建议使用中断方式在中断内释放信号量通知以太网报文接收线程进行处理 2发送DMA描述符运作方式类似于环形bufferCPU是尾指针DMA是头指针接收DMA描述符运作方式类似于环形bufferCPU是头指针DMA是尾指针 3在接收以太网数据时一定要及时取出DMA描述符中的数据将控制权交还给DMA避免报文阻塞 4在发送以太网数据时一定要及时清除Tx Buffer标志并恢复发送