STM32的以太网外设+PHY(LAN8720)使用详解(6):以太网数据接收及发送

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, size=0,i=0;__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 (i=0; i< buf_count; i++){/* Clear FIRST and LAST segment bits */DMATxDesc->Status &= ~(ETH_DMATxDesc_FS | ETH_DMATxDesc_LS);if (i==0) {/* 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描述符运作方式类似于环形buffer,CPU是尾指针,DMA是头指针;接收DMA描述符运作方式类似于环形buffer,CPU是头指针,DMA是尾指针
(3)在接收以太网数据时一定要及时取出DMA描述符中的数据,将控制权交还给DMA,避免报文阻塞
(4)在发送以太网数据时一定要及时清除Tx Buffer标志并恢复发送

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

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

相关文章

2023 下半年系统架构设计师学习进度

文章目录 复习计划&#xff1a;每周350分钟第一周&#xff08;339分钟&#xff09;第二周&#xff08;265分钟&#xff09;第三周&#xff08;171分钟&#xff09;第四周&#xff08;214分钟&#xff09;第五周&#xff08;274分钟&#xff09;第六周&#xff08;191分钟&#…

im6ull学习归纳总结(一)APP——04_文件IO

4.1文件从何而来 如图所示文件可以是 1真实文件保存在设备上 2内核提供的虚拟文件 3设备节点 4.2文件的访问方式 4.2.1通用IO模型&#xff1a;open/read/write/lseek/close 实验1 copy文件 代码 #include <sys/types.h> #include <sys/stat.h> #include <fc…

大模型杀入HR赛道,AI能扮演好企业的“人才捕手”吗?

导读&#xff1a;生成式AI如何让HR回归本质。 当很多人焦虑未来会“被AI夺走工作”时&#xff0c;HR行业本身也在AI浪潮推动下发生巨变。 AI技术现已应用于人力资源管理的各个环节中。根据领英发布的《2024全球人才趋势报告》&#xff0c;61%的HR已经在使用AI相关技术辅助日常工…

Python密码魔法:制作个性化、安全性满分的密码生成器秘籍!

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 密码是保护个人信息安全的关键&#xff0c;而一个强密码生成器能够帮助用户创建高度安全的密码。本文将介绍如何使用 Python 制作一个简单而功能强大的密码生成器&#xff0c;涵盖了各种生成密码的方法、密码强度…

Linux数据库主从复制(单主单从)

MySQL主从复制的优点包括&#xff1a; 1、横向扩展解决方案 - 在多个从站之间分配负载以提高性能。在此环境中&#xff0c;所有写入和更新都必须在主服务器上进行。但是&#xff0c;读取可以在一个或多个从设备上进行。该模型可以提高写入性能&#xff08;因为主设备专用于更新…

【接口测试】如何定位BUG的产生原因

我们从在日常功能测试过程中对UI的每一次操作说白了就是对一个或者多个接口的一次调用&#xff0c;接口的返回的内容(移动端一般为json)经过前端代码的处理最终展示在页面上。http接口是离我们最近的一层接口&#xff0c;web端和移动端所展示的数据就来自于这层&#xff0c;那么…

用C爬取人人文库并分析实现免积分下载资料

最近有个学妹学习遇到问题&#xff0c;想要的学习资料都在文库中&#xff0c;因为资料太多太杂&#xff0c;想要一篇篇找太难了&#xff0c;主要是太浪费精力了。因此&#xff0c;听说这个事情我能解决&#xff0c;立马找到我&#xff0c;给我一杯奶茶就把我收买了&#xff0c;…

【Seata源码学习 】 扫描@GlobalTransaction注解 篇一

1. SeataAutoConfiguration 自动配置类的加载 基于SpringBoot的starter机制&#xff0c;在应用上下文启动时&#xff0c;会加载SeataAutoConfiguration自动配置类 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfigurationio.seata.spring.boot.aut…

DPDK单步跟踪(3)-如何利用visual studio 2019和visual gdb来单步调试dpdk

准备工作 因为时间的关系&#xff0c;我想到哪说到哪&#xff0c;可能没那么高的完成度。 但其实有心的人&#xff0c;看到这个标题&#xff0c;就关了本文自己能做了。 why和how to build debug version DPDK,见前两篇。这里我们准备开始。 首先&#xff0c;你有一台linux机…

算法题系列7·获得数组中多数元素

目录 题目描述 实现 提交结果 题目描述 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1&#xff1a; 输入&#xff1a;…

第11章 GUI Page400~402 步骤二 画直线

运行效果&#xff1a; 源代码&#xff1a; /**************************************************************** Name: wxMyPainterApp.h* Purpose: Defines Application Class* Author: yanzhenxi (3065598272qq.com)* Created: 2023-12-21* Copyright: yanzhen…

黑芝麻智能与亿咖通科技签署战略合作协议,深化协同助力智能驾驶量产落地

12月22日&#xff0c;全球智能汽车计算芯片引领者黑芝麻智能与全球出行科技企业亿咖通科技共同签署战略合作协议&#xff0c;通过深化合作&#xff0c;整合双方研发、产品和技术资源&#xff0c;联手打造领先智能驾驶系统解决方案&#xff0c;合力推进商业拓展和市场应用&#…

微前端样式隔离、sessionStorage、localStorage隔离

1、样式隔离 前端样式不隔离&#xff0c;会产生样式冲突的问题&#xff0c;这个点在qiankun也存在 子应用1修改一个样式 button {background: red&#xff01;important&#xff1b; }其它应用也会受到影响 qiankun的css隔离方案&#xff08;shadow dom&#xff09; shadow …

Hive-high Avaliabl

hive—high Avaliable ​ hive的搭建方式有三种&#xff0c;分别是 ​ 1、Local/Embedded Metastore Database (Derby) ​ 2、Remote Metastore Database ​ 3、Remote Metastore Server ​ 一般情况下&#xff0c;我们在学习的时候直接使用hive –service metastore的方式…

FreeRTOS之二值信号量(实践)

信号量相当于一个标志&#xff0c;实现对资源多少的管理。 比如停车场空位的数量。 这里使用的是二值信号量&#xff0c;其队列长度为1&#xff0c;只有空或满两种状态。 1、步骤&#xff1a; 1.1、创建信号量 1.2、释放信号量 1.3、获取信号量 注&#xff1a;若想深入还…

《深入理解计算机系统》学习笔记 - 第七课 - 机器级别的程序三

Lecture 07 Machine Level Programming III Procedures 机器级别的程序三 文章目录 Lecture 07 Machine Level Programming III Procedures 机器级别的程序三概述程序机制 栈结构栈说明栈定义推入数据弹出数据 调用控制代码示例程序控制流程%rip 传递数据ABI 标准示例 管理局部…

LangChain 29 调试Debugging 详细信息verbose

LangChain系列文章 LangChain 实现给动物取名字&#xff0c;LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字LangChain 3使用Agent访问Wikipedia和llm-math计算狗的平均年龄LangChain 4用向量数据库Faiss存储&#xff0c;读取YouTube的视频文本搜索I…

计算机msvcp140.dll丢失的多种解决方法,哪种更值得推荐

在使用电脑进行工作&#xff0c;娱乐的时候&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“msvcp140.dll丢失”&#xff0c;这个问题可能会导致某些应用程序无法正常运行。msvcp140.dll是微软Visual C 2015运行库中的一个动态链接库文件&#xff0c;全名为M…

MyBatis的延迟加载!!!

首先&#xff1a;MyBatis的关联查询&#xff01;&#xff01;&#xff01;&#xff08;一对一、一对多、多对多&#xff09;-CSDN博客以这个项目为基础。 1.在UserMapper接口中创建一个方法&#xff1a; package com.by.mapper;import com.by.pojo.User;import java.util.Lis…

STM32的以太网外设+PHY(LAN8720)使用详解(5):MAC及DMA配置

0 工具准备 1.野火 stm32f407霸天虎开发板 2.LAN8720数据手册 3.STM32F4xx中文参考手册1 MAC及DMA配置 1.1 使能ETH时钟 stm32的ETH外设挂载在AHB1总线上&#xff0c;位于RCC_AHB1ENR的bit25-bit27&#xff1a; 相关语句如下&#xff1a; RCC_AHB1PeriphClockCmd(RCC_AHB1…