想使用正点原子战舰的战舰V3板子做一个udp的demo程序

查看: 749|回复: 3
求战舰V3板的综合测试程序??
主题帖子精华
初级会员, 积分 150, 距离下一级还需 50 积分
在线时间15 小时
如题,找了光盘没有发现有综合测试程序啊???就淘宝视频那个综合测试程序???
工业4.0的节奏
主题帖子精华
在线时间1472 小时
百度网盘下载链接,资料盘A,程序源码里面
开往春天的手扶拖拉机
主题帖子精华
初级会员, 积分 150, 距离下一级还需 50 积分
在线时间15 小时
找到了,谢谢,之前自己没看清
工业4.0的节奏
主题帖子精华
中级会员, 积分 392, 距离下一级还需 108 积分
在线时间55 小时
你是在哪里找到的&&我找了也没又找到
Powered by查看: 5280|回复: 9
资料分享:新战舰V3开发板综合实验移植到V2开发板
主题帖子精华
初级会员, 积分 184, 距离下一级还需 16 积分
在线时间1 小时
修改说明:
——原程序是原子哥新战舰V3的综合实验,看到新的综合实验界面炫酷,于是禁不住就修改了一下,使之能在V2板子上运行。
——V2和V3板子硬件大部分一样,所以大部分功能都能用。
修改内容:
——修改了新旧板冲突的引脚
——修改了USART3的硬件部分为USART2的硬件,使得SIM900A模块能正常使用。
——把V2板的声音选择程序添加到V3中了,在初始化时直接选中了MP3解码芯片的输出。实测MP3,汤姆猫,录音机都有声音
——修改了SPI FLASH的大小。V2板的SPI FLASH是8M的,V3是16M的,新板子FLASH中存的东西比较多(就是SD卡根目录文件\SYSTEM那个文件夹),在V2板上放不下。不想折腾换芯片了
,就把里面适配大屏用的图标给替换掉了(不能删,删了要改程序,不然初始化不能通过)。如果你也是小屏幕(240*320),那恭喜,我已经改好了,你直接用就行了(见附件)。如
果你的屏幕分辨率超过了240*320,那就弄个W25Q128换上吧(程序需要改的,不然初始化过不去),然后复制官方资料中的文件进去。
特别说明:
——V3板SD卡用的SDIO,V2板支持SPI和SDIO两种模式,但是V2板在SDIO模式下,游戏手柄和PS2接口都不能用了。本人对SD卡和SDIO不甚了解,所以没做修改,NES玩不了,只能预览过
——V3板好像没有收音机,所以V2板上的收音机模块也就闲置了
——V2没有陀螺仪,陀螺仪程序用不了
——V2没有DM9000,网络通信用不了,SMI900A还是可以连上用的。
使用方法:
1.下载附件
2.把程序下到V2开发板上,板子将进行初始化。
3.在检查字库时会出错,会提示是否连接USB来拷贝数据,用按键选择是。
4.把开发板USB接口和电脑连接起来,电脑上就会出现一个可以动磁盘,把附件中的SYSTEM文件夹复制到磁盘根目录即可(可能是没改好吗?速度超慢,耐心等待,一定能完成的)
5.复制完成后,根据提示按一下K0键,程序重启,然后会把你刚刚复制进去的文件复制到一个你看不到的地方(FLASH的另一半空间里面)……
6.复制完就看到了炫酷的界面了。
新界面的滑屏特别流畅,原子哥真是没少下功夫。
本人的STM32技术,基本上全是从原子哥这里学的,非常感谢能有这么好的老师,在这里向老大问好!
ab1aef2b.jpg (0 Bytes, 下载次数: 53)
22:54 上传
e52f7f0d8ee14eab7d8cb66df014a611_667.jpg (0 Bytes, 下载次数: 0)
22:54 上传
381ce3187600ced1fbadfdf1_48.jpg (0 Bytes, 下载次数: 49)
22:54 上传
21:53 上传
点击文件名下载附件
5.68 MB, 下载次数: 3213
主题帖子精华
金钱128932
在线时间1149 小时
楼主,这个必须cool啊.
非常感谢你的工作,和对我们产品的支持.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺:
主题帖子精华
初级会员, 积分 184, 距离下一级还需 16 积分
在线时间1 小时
回复【2楼】正点原子:
---------------------------------
开发了半年STM32产品,真的很感谢原子哥的开发板,很多例程都特别有使用价值,底层的驱动好多我都是直接拿过来一改就能用了,特别方便!感谢开源!
主题帖子精华
金钱128932
在线时间1149 小时
回复【3楼】ICAN1992:
---------------------------------
谢谢
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺:
主题帖子精华
新手入门, 积分 24, 距离下一级还需 -4 积分
在线时间1 小时
回复【4楼】正点原子:
原子哥你好:我把我的V2开发板烧录成以上V3版本后再重新烧录回V2综合实验,界面有但是操作没用了,flash里面的文件我是通过USB连接拷贝回去的(V2&SD卡的文件)
主题帖子精华
高级会员, 积分 668, 距离下一级还需 332 积分
在线时间280 小时
我是初学,是不是还需要USB驱动?
主题帖子精华
中级会员, 积分 328, 距离下一级还需 172 积分
在线时间46 小时
拿到了师哥的v2板子,想试试.
主题帖子精华
高级会员, 积分 982, 距离下一级还需 18 积分
在线时间110 小时
***********& && && && & **& && && && && && && &&&**********& && && && && && && && && &*************&&
*& && && && && && && && && & **& && && && && && && && &*& && && &&&*& && && && && && && && && && && && && && && & *
*& && && && && && && && && & **& && && && && && && && &*& && && &&&*& && && && && && && && && && && && && && && & *
***********& && && && & **& && && && && && && &&&*& && && && &*& && && && && && && && && & ************
*& && && && && && && && && & **& && && && && && && && &*& && && && &*& && && && && && && && && && && && && && && &*
*& && && && && && && && && & **& && && && && && && && &*& && && && &*& && && && && && && && && && && && && && && &*
*& && && && && && && && && & **& && && && && && && && &**********& && && && && && && && && &*************
越努力,越优秀;越独立,越自由
主题帖子精华
金牌会员, 积分 1207, 距离下一级还需 1793 积分
在线时间243 小时
谢谢!!!!
知识是你最好的财富
主题帖子精华
初级会员, 积分 130, 距离下一级还需 70 积分
在线时间49 小时
谢谢楼主分享,最近在v3程序移植v2,我是菜鸟及的学习哈!
Powered by初学stm32求推荐开发板? - 知乎有问题,上知乎。知乎作为中文互联网最大的知识分享平台,以「知识连接一切」为愿景,致力于构建一个人人都可以便捷接入的知识分享网络,让人们便捷地与世界分享知识、经验和见解,发现更大的世界。35被浏览<strong class="NumberBoard-itemValue" title="2分享邀请回答35 条评论分享收藏感谢收起stmcu.com.cn/上有很多开发板资源。STM32 Nucleo开发板基于ARM Cortex-M内核的STM32 Nucleo开发板为用户提供了一个经济实惠且灵活的方式以供用户尝试新的想法,并能与任何STM32微控制器搭建技术原型,从性能、功耗和功能的各种组合中做以选择。Arduino(TM)的连接支持和ST Morpho的扩展连接头使用户很容易扩大 STM32 Nucleo板对外开发平台的功能,该平台提供了多种可供选择的适配外设板。STM32的Nucleo板集成了ST-LINK/V2-1调试器/编程器,和STM32 Nucleo板搭配的软件包有包含了众多例程的STM32 HAL软件库,在mbed在线平台上也能找到很多资源。Discovery探索套件板STM32 探索套件是帮助新用户探索STM32性能的入门工具,同时为项目工程师提供快速制作样机的开发平台。集成若干外设模块,此探索套件由两个部分组成,一个ST-LINK/V2用于调试和编程,一个STM32的最小开发板集成了与对应STM32芯片特殊外设相关的其他器件。STM32全功能评估板集成众多外设模块,通常板载对应系列最大封装的目标芯片。同时搭载STLINK-V2,方便客户进行项目全功能评估开发。然后因为现在ST在国内很火,很多第三方也在用STM32芯片做开发板。一般提供很详细的本地化资源,包括中文教程,视频讲解,论坛技术支持等,降低了STM32的入门门槛。这里介绍其中几个做的比较大的。【正点原子】阿波罗STM32F429开发板阿波罗STM32F767开发板战舰V3开发板(Alientek)【野火】STM32F103-霸道开发板F429-挑战者【安富莱】STM32-V5, STM32F407开发板安富莱STM32F103ZE开发板V42添加评论分享收藏感谢收起丁丁的博客
&是一个基于JAVA;适合JAVA初、中级水平编程人员使用、阅读并修改源代码的个人博客系统,不如来丁丁的&&瞧瞧?
前言:项目需求使用STM32F407进行以太网通信,并涉及到数据链路层的工业以太网通信,使用LWIP协议栈并不能满足需求,因此需要自己摸清STM32F407调用外部PHY进行网络收发包的过程,并在此基础上尝试自己构建适用于项目的网络协议栈。我基本上是从零开始着手这个项目的,之前只有一些STC51系列和STM32F1系列单片机开发的经验,项目开发过程中学习、参考、借鉴了很多网络上的教程和博客,在此尤其感谢正点原子团队发布的相关视频教程。公司买的开发板主芯片是一颗STM32F407,搭配了一颗DP83848的PHY,因此本系列教程将使用DP83848进行适配,同时原子哥的学习板和教程中是使用LAN8720这颗PHY进行适配的,本系列指南也会花一些篇幅介绍各种PHY与STM32芯片进行适配的方法。
为了您更好地阅读本系列,请点击原创连接进行浏览:
本章为系列指南的第八章,讲述如何使用STM32F407芯片配合DP83848进行以太网数据的收包流程,将监听到的网络包数据通过UART传给PC,同时辅以WireShark监听对比验证。
关于UART,也就是串口通信的使用,这里不做赘述,需要补充知识的朋友可以先浏览一下我在,F4系列也是大同小异,资源和引脚有所调整而已。我们这里预设两个函数分别为UART6Init()和UART6Send(),实现的功能是串口6的初始化和发送。
以太网中断在中,我们已经添加了以太网中断,其思路就是想让每次以太网上有收到包都能触发中断,我们可以在中断中将DMA中的数据包取出来进行分析,然后复位,让芯片下一次继续响应中断。
配置中断的代码非常简单,跟其他任何中断都一样,这里再复习一次:
void ETH_NVIC_Config(void) {
NVIC_InitTypeDef
NVIC_InitS
/* Enable the Ethernet global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = ETH_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
整个工程的优先级组别选用的NVIC_PriorityGroup_4,有4位抢占位,0位响应位,也就是可以分配16个可以互相嵌套的中断等级。这里以太网中断的主优先级为1,相当于第二高,前面预留了个优先级为0的,用来分配给系统计时器,毕竟不能因为以太网数据的响应影响系统走时。
在UART6Init()串口初始化函数中,给串口的中断等级是2,低于以太网中断,因为串口的波特率是9600,要远远低于以太网速率,如果给串口的优先级过高,会影响以太网的使用。
配置了中断后,我们还需要知道中断的入口函数,这个函数名是固定死的,不能乱写,我们去找找。
在我提到,中断配置代码中的ETH_IRQn变量,我们可以在stm32f4xx.h文件中找到定义,是在一个枚举结构中。那与之对应的中断入口名称该怎么找呢,原来所有的中断入口的定义,都用汇编入口的方式定义在启动文件中,这份启动文件我们之前一直没有关注过,现在打开看一看,在startup_stm32f40_41xxx.s中148行有其定义,截图如下。
中断中的数据处理配置好了以太网中断,也知道了中断入口函数的名称,下面我们就来编写以太网中断函数。打开工程中的stm32f4xx_it.c文件,一般每一个使用了中断的STM32工程都会有这么一个文件,用来集中管理中断入口。追加以下代码:
This function handles ethernet DMA interrupt request.
* @retval None
void ETH_IRQHandler(void)
/* Handles all the received frames */
/* check if any packet received */
while(ETH_CheckFrameReceived()){
/* process received ethernet packet */
Pkt_Handle();
/* Clear the Eth DMA Rx IT pending bits */
ETH_DMAClearITPendingBit(ETH_DMA_IT_R);
ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);
上面的代码配上英文的注释也很好理解,以太网中断中首先检查是否接受到以太网的数据包,如果是,就调用Pkt_Handle()函数进行下一层的分析和处理,最后两行Clear复位中断标记,让下一次中断能够产生。那么问题就集中到Pkt_Handle()函数上来了。
Pkt_Handle()函数是我自己命名的,这个函数的原型取自LWIP中的LWIP_Pkt_Handle(),我们先来观察一下LWIP中的包分析函数怎么写的:
在STM32F4x7_ETH_LwIP_V1.1.1/Project/Standalone/udp_echo_client/src/netconf.c中,有如下代码:
Called when a frame is received
* @retval None
void LwIP_Pkt_Handle(void)
/* Read a received packet from the Ethernet buffers and send it to the lwIP for handling */
ethernetif_input(&gnetif);
可以看到LWIP继续调用了下层的ethernetif_input(),继续追踪到我们之前提到的最底层文件ethernetif.c,是不是有种似曾相识的感觉,这个文件前几章我们不止一次遇到过,分别为我们提供了low_level_init、low_level_output多个重要函数,我们现在又一次遇到它了,看上去它这次要为我们的以太网监听提供low_level_input了。
果不其然,在ethernetif_input函数中,我们看到了这个预料中的函数调用,截图如下:
我这里把路径为STM32F4x7_ETH_LwIP_V1.1.1/Utilities/Third_Party/lwip-1.4.1/port/STM32F4x7/Standalone/ethernetif.c的原版low_level_input()的所有代码都贴出来,感兴趣的朋友可以仔细研读:
* Should allocate a pbuf and transfer the bytes of the incoming
* packet from the interface into the pbuf.
* @param netif the lwip network interface structure for this ethernetif
* @return a pbuf filled with the received packet (including MAC header)
NULL on memory error
static struct pbuf * low_level_input(struct netif *netif)
struct pbuf *p, *q;
FrameTypeD
uint32_t i=0;
__IO ETH_DMADESCTypeDef *DMARxNextD
/* get received frame */
frame = ETH_Get_Received_Frame();
/* Obtain the size of the packet and put it into the &len& variable. */
len = frame.
buffer = (u8 *)frame.
/* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
/* copy received frame to pbuf chain */
if (p != NULL)
for (q = q != NULL; q = q-&next)
memcpy((u8_t*)q-&payload, (u8_t*)&buffer[l], q-&len);
l = l + q-&
/* Release descriptors to DMA */
/* Check if frame with multiple DMA buffer segments */
if (DMA_RX_FRAME_infos-&Seg_Count & 1)
DMARxNextDesc = DMA_RX_FRAME_infos-&FS_Rx_D
DMARxNextDesc = frame.
/* Set Own bit in Rx descriptors: gives the buffers back to DMA */
for (i=0; i&DMA_RX_FRAME_infos-&Seg_C i++)
DMARxNextDesc-&Status = ETH_DMARxDesc_OWN;
DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc-&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;
同样的,配合注释应该也容易理解。22行的frame变量取到了以太网数据包,ETH_Get_Received_Frame函数,不用LWIP也是有的,在之前我们提到过的stm32f4x7_eth.c文件中,len和buffer两个变量一个是包长度,一个是包内容头指针。
下面/* copy received frame to pbuf chain */那一段是用链表遍历的方式,将以太网包数据放入LWIP处理数据的pbuf链表中,方便LWIP上层逻辑获取数据,这里我们不使用LWIP,这一段可忽略。
接下来所有的操作都是针对DMA进行的,将DMA复位,因此我们需要保留,否则会一直产生重复的中断。
通过以上的分析,我们可以轻松写出自己的Pkg_Handle()函数了:
void Pkt_Handle(void) {
FrameTypeD
/* get received frame */
frame = ETH_Get_Received_Frame();
/* Obtain the size of the packet and put it into the &len& variable. */
receiveLen = frame.
receiveBuffer = (u8 *)frame.
printf(&\n&, receiveLen);
//将每一个的包长度发往串口
if(receiveBuffer[41] == 201){
//如果第42字节是十进制201,则将整个包内容发往串口
for (i = 0; i & receiveL i++) {
printf(&%c&, receiveBuffer[i]);
/* Check if frame with multiple DMA buffer segments */
if (DMA_RX_FRAME_infos-&Seg_Count & 1) {
DMARxNextDesc = DMA_RX_FRAME_infos-&FS_Rx_D
DMARxNextDesc = frame.
/* Set Own bit in Rx descriptors: gives the buffers back to DMA */
for (i = 0; i & DMA_RX_FRAME_infos-&Seg_C i++) {
DMARxNextDesc-&Status = ETH_DMARxDesc_OWN;
DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc-&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;
验证和总结上述函数,配合ETH_IRQHandler中断中的调用,完成了以太网的收包,并且将接受的包的长度使用通过printf函数通过UART发往了PC端,因为如果将整个包内容发往PC的话,串口数据会非常多。同时如果为了验证buffer中的内容能正确获取,我们写了一个if判断,判断如果数据包中的第42个字节为201,则将包内容转发到串口中去。
我们用JLink烧录进STM32F4,将PC的有线网卡与STM32直接,打开WireShark和串口通信助手进行观察和验证,截图如下。
红色框框部分是关注重点,我们在CMD命令窗口ping 192.168.1.201,可以触发一个ARP包,这个包中的第42个字节就是201,因此可以触发STM32中的if判断,将包内容通过串口转发给PC,而其他普通包,STM32则使用的格式将包长度发给了PC,整个实验顺利完成。
总结一下,本章我们依旧分析到了ethernetif.c文件,这次是观察的它的low_level_input()函数,借助这个函数,我们编写了我们自己的处理包的逻辑函数Pkg_Handle(),并通过以太网中断入口函数ETH_IRQHandler调用它,最后我们成功的使用WireShark配合串口进行了收包的验证。
我们可以发现这个系列教程的后半段几乎都在不停地围绕LWIP库中的ethernetif.c文件进行分析,到目前为止,它的几个重要底层函数low_level_init、low_level_output、low_level_input已经分别为我们的以太网初始化、以太网发包、以太网收包等代码提供了重要的核心逻辑。
下一章,是我们这个系列的最后一章,我们将在STM32F4上,利用之前实验过的各个功能,自己构建一个能响应ARP协议的程序。有了ARP协议的处理过程,我们就能在此基础上扩展更多其他的协议,即使遇到工业以太网跑在链路层的各个协议,我们也能捡其重点,按照自己的意愿,随心所欲的搭建了。
前言:项目需求使用STM32F407进行以太网通信,并涉及到数据链路层的工业以太网通信,使用LWIP协议栈并不能满足需求,因此需要自己摸清STM32F407调用外部PHY进行网络收发包的过程,并在此基础上尝试自己构建适用于项目的网络协议栈。我基本上是从零开始着手这个项目的,之前只有一些STC51系列和STM32F1系列单片机开发的经验,项目开发过程中学习、参考、借鉴了很多网络上的教程和博客,在此尤其感谢正点原子团队发布的相关视频教程。公司买的开发板主芯片是一颗STM32F407,搭配了一颗DP83848的PHY,因此本系列教程将使用DP83848进行适配,同时原子哥的学习板和教程中是使用LAN8720这颗PHY进行适配的,本系列指南也会花一些篇幅介绍各种PHY与STM32芯片进行适配的方法。
为了您更好地阅读本系列,请点击原创连接进行浏览:
本章为系列指南的第七章,讲述如何在之前的基础上,编写程序在STM32上发送一个网络包,并使用WireShark进行验证。
先回顾一下之前的章节我们做好的准备工作,在结束时我们封装了一个DP83848的初始化函数,该函数完成了PHY的配置,MAC层的配置,DMA的配置,并且启用了以太网中断,函数命名为DP83848Init(),那么今天,我们要做的主要任务就是编写一个类似的DP83848Send(u8* data, u16 length)函数。
可以在本章的一开始跟大家剧透一个好消息,有了 和
的基础,我们本章最终实现的DP83848Send(u8* data, u16 length)函数,只有两行代码,非常非常简单。这两行代码我暂时先不贴出来,我们来顺着原来的思路,根据相关文档和官方示例代码,顺藤摸瓜,一步一步深入了解以太网发包的流程,最终理解体系结构后,也就水到渠成能够写出来了。
最后一部分提到在中,路径为STM32F4x7_ETH_LwIP_V1.1.1\Utilities\Third_Party\lwip-1.4.1\port\STM32F4x7\Standalone\ethernetif.c的文件中,第76行有个low_level_init()函数,该函数调用ETH库函数对MAC底层及DMA进行了初始化。同样的,这份文件的138行,有个名为low_level_output(struct netif *netif, struct pbuf *p)的函数,疑似是向外输出网络包的函数,下面就对这部分代码进行分析,并试着用其中的核心逻辑进行测试。
因为ethernetif.c这份代码本身隶属于LWIP,而我们是不使用LWIP的,所以这份代码只能尽量去看懂和借鉴,想要原封不动地使用是不可以的。
我们先完整地贴出这个函数:
* This function should do the actual transmission of the packet. The packet is
* contained in the pbuf that is passed to the function. This pbuf
* might be chained.
* @param netif the lwip network interface structure for this ethernetif
* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
* @return ERR_OK if the packet could be sent
an err_t value if the packet couldn&#39;t be sent
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
strange results. You might consider waiting for space in the DMA queue
to become availale since the stack doesn&#39;t retry to send a packet
dropped because of memory failure (except for the TCP timers).
static err_t low_level_output(struct netif *netif, struct pbuf *p) {
struct pbuf *q;
u8 *buffer =
(u8 *)(DMATxDescToSet-&Buffer1Addr);
__IO ETH_DMADESCTypeDef *DmaTxD
uint16_t framelength = 0;
uint32_t bufferoffset = 0;
uint32_t byteslefttocopy = 0;
uint32_t payloadoffset = 0;
DmaTxDesc = DMATxDescToS
bufferoffset = 0;
/* copy frame from pbufs to driver buffers */
for(q = q != NULL; q = q-&next) {
/* Is this buffer available? If not, goto error */
if((DmaTxDesc-&Status & ETH_DMATxDesc_OWN) != (u32)RESET) {
errval = ERR_BUF;
/* Get bytes in current lwIP buffer */
byteslefttocopy = q-&
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( (u8_t *)((u8_t *)buffer + bufferoffset), (u8_t *)((u8_t *)q-&payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) );
/* Point to next descriptor */
DmaTxDesc = (ETH_DMADESCTypeDef *)(DmaTxDesc-&Buffer2NextDescAddr);
/* Check if the buffer is available */
if((DmaTxDesc-&Status & ETH_DMATxDesc_OWN) != (u32)RESET) {
errval = ERR_USE;
buffer = (u8 *)(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( (u8_t *)((u8_t *)buffer + bufferoffset), (u8_t *)((u8_t *)q-&payload + payloadoffset), byteslefttocopy );
bufferoffset = bufferoffset +
framelength = framelength +
/* Note: padding and CRC for transmitted frame
are automatically inserted by DMA */
/* Prepare transmit descriptors to give to DMA*/
ETH_Prepare_Transmit_Descriptors(framelength);
errval = ERR_OK;
/* 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;
这个函数的官方注释描述的就是用来向外发送以太网包的,函数中说要发的包在第二个参数,类型为pbuf结构体指针的参数p中,并且说了p可能是个链表,我们看到函数的两个入参都是结构体参数,这两个结构体的定义我们不需要管,是LWIP自己封装的一个结构体。我们去寻迹参数p的用法,在代码片段的30行,使用q变量和for循环遍历p,因此我们能够确定p就是个头尾相接的pbuf链表。继续观察遍历体中的操作逻辑,我们看到整个for循环的主要目的就是在尝试将q-&payload中的byte,利用函数memcopy()向buffer变量中堆,并且做了一些长度的校验,我们继而去观察一下buffer变量的定义,第19行的u8 *buffer =
(u8 *)(DMATxDescToSet-&Buffer1Addr);是一个比较重要的线索,由此我们可以抽丝剥茧出整体的逻辑,应该就是将首尾相接的p遍历出来,取其中每个元素的payload区域,向DMATxDescToSet-&Buffer1Addr中压。最后,第73行的ETH_Prepare_Transmit_Descriptors(framelength);调用了ETH库中的函数,实现了最终的结局,将网络包发出去,入参的framelength应该就是需要发出去的包长度,包内容应该就是通过DMA技术,将内存中的DMATxDescToSet-&Buffer1Addr发出去了。
有了以上针对low_level_output()函数的分析,我们来做实验印证一下,因为我们从零开始构建的项目没有LWIP,也没有ethernetif.c,更没有low_level_output()函数,因此,函数内部的逻辑都需要我们自己手动实现,慢着,不要一看到「手动实现」就头疼,你以为手动实现就很复杂吗?不,LWIP把事情搞复杂了,又是pbuf又是链表的,还有长度判断导致的Buffer2NextDescAddr切换(详见第43-62行一整段,不过不重要),如果我们手动写这段逻辑,放弃一些异常处理,再放弃那些跟LWIP强相关的结构体,我们整个发包函数只要两行就行:
void DP83848Send(u8* data, u16 length){
memcpy((u8 *)DMATxDescToSet-&Buffer1Addr, data, length);
/* Prepare transmit descriptors to give to DMA*/
ETH_Prepare_Transmit_Descriptors(length);
这里附带说明一下,并不是LWIP原版代码又臭又长,LWIP要做一个TCP/IP全栈协议,还要考虑包长度溢出的众多问题,我们精简版的协议很多不需要考虑,因此可以放弃很多繁琐的操作。
有了上述DP83848Send()函数,下面来做个小程序试验一下:
int main() {
u8 MyMacAddr[6] = {0x08, 0x00, 0x06, 0x00, 0x00, 0x09};
/* 下面是一段60byte大小的ARP报文,手动构建的 */
u8 mydata[60] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x08, 0x06, 0x00, 0x01, 0x08, 0x00, 0x06, 0x04,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0xa8,
0x02, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8,
0x02, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
/* 默认调用SystemInit,系统时钟168MHz */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
//4位抢占,0位响应
DP83848Init(MyMacAddr);
DP83848Send(mydata, 60);
//1s延时,while中每个步进需要4个周期
while(clock--);
使用Keil编译,用JLink下载到STM32F407中,给开发板接上网线,用WireShark就可以在网口中观察到STM32每隔1秒钟向外发送ARP报文了,虽然这段报文几乎没有任何意义。
我使用WireShark截图如下:
总结一下,这一章我们完成了一个DP83848Send()发包函数,这个函数可以接受一个字节buffer,一个字节buffer的长度,将这个buffer通过以太网发送出去,buffer内部的内容全部需要我们手工构建。DP83848Send()函数的设计思路来自于分析LWIP官网示例,主要是ethernetif.c中的代码。下一章我们同样根据这份代码,分析收包逻辑,实现STM32对以太网上数据的监听。
前言:项目需求使用STM32F407进行以太网通信,并涉及到数据链路层的工业以太网通信,使用LWIP协议栈并不能满足需求,因此需要自己摸清STM32F407调用外部PHY进行网络收发包的过程,并在此基础上尝试自己构建适用于项目的网络协议栈。我基本上是从零开始着手这个项目的,之前只有一些STC51系列和STM32F1系列单片机开发的经验,项目开发过程中学习、参考、借鉴了很多网络上的教程和博客,在此尤其感谢正点原子团队发布的相关视频教程。公司买的开发板主芯片是一颗STM32F407,搭配了一颗DP83848的PHY,因此本系列教程将使用DP83848进行适配,同时原子哥的学习板和教程中是使用LAN8720这颗PHY进行适配的,本系列指南也会花一些篇幅介绍各种PHY与STM32芯片进行适配的方法。
为了您更好地阅读本系列,请点击原创连接进行浏览:
本章为系列指南的第六章,这一章我们暂时离开Keil,离开STM32,离开C语言,这一章我们要去了解一些以太网相关的知识,特别是学习使用大名鼎鼎的调试以太网通信程序的利器:WireShark。
帧结构我们从小就听说过计算机中全都是二进制的0和1,这个道理几乎连考不上三本的文科生都懂,但是很难有直观的感受,甚至连由16个bit,也就是16个二进制的0/1构成的十六进制0/F,再由两个十六进制的0/F形成的0x00/0xFF这种Byte(字节),都很难直观感受到,毕竟计算机世界太丰富了,各类色彩,图片,网站,视频,游戏充斥着我们的日常,所谓「乱花渐欲迷人眼,浅草才能没马蹄」,形形色色的计算机资源就是「乱花」,其背后单调的Byte才是「浅草」,我们在网络通信时只需要Byte的颗粒度,至于细化到bit的精度,只有在研究纯电子电路时才会涉及到。如果说如何传输bit是PHY层负责的问题,那么如何构造和解析Byte就是MAC层要肩负的责任了。
由Byte组成的一个个网络帧,势必要按照一定的格式进行排列,就像如果要你自己自己编排一个串口通信的协议,你也一定会考虑包头、包尾、校验位这些。
Ethernet V2协议在链路层规定了一个基本的帧结构,如下:
循环冗余校验FCS
46-1500字节
因此,最小的帧长度是6 + 6 + 2 + 46 + 4 = 64个Byte,最大长度是6 + 6 + 2 + 1500 + 4 = 1518个Byte。
上表最后4个Byte形成了一组循环冗余的CRC校验数据,很遗憾的是我们能通过软件手段监听出来的帧结构都不会含有这段数据,因为网卡在接受到报文后就会立即对数据进行校验,凡是不满足校验规则的数据包会直接丢弃,不会触发操作系统的以太网事件。因此你能监听到的报文都是符合规则的,网卡交给上层软件应用时也会自动将校验数据剥离。此外,具体的循环冗余CRC校验的算法是什么大家也不用管了,在使用STM32调用发包指令时,MAC层会帮我们计算好的。
一个典型的ARP帧报文如下:
ff ff ff ff ff ff 00 11 0e 0b 03 8a 08 06 00 01
08 00 06 04 00 01 00 11 0e 0b 03 8a c0 a8 01 c7
00 00 00 00 00 00 c0 a8 01 fe 00 00 00 00 00 00
00 00 00 00 00 00 00 00 20 20 20 20
这是一个最简单的60Byte的ARP报文(另有4个FCS校验数据被网卡吞了,总长度64Byte),看上去是不是像是乱码,完全无法理解,别着急,下面介绍网络编程的神器WireShark。
WireShark·线路鲨鱼这款名字里带有「鲨鱼」的软件,真的是名副其实,大鳄级的网络编程神器:精确到微妙级别的底层网络包监听,内置上千种网络协议,上下文分析得丝丝入扣,你通过它即可完全理解整个网络上正在发生了什么。无论是初学网络的思科学员想一窥网络的究竟,还是努力工作的程序员需要进行协议的分析,亦或是躲在阴暗处的黑客试图在交换机上刮起一场网络风暴,WireShark都能助你一臂之力。
软件下载地址:
WireShark为了能够监听到网络底层的数据包,在Windows上使用了叫做的技术,而winpcap又是使用了这个库,这样的调用关系其实是为了解决一件事:能在操作系统上层操作底层网络数据包。
我们平时在C++,JAVA中谈论到的网络编程,其实所处的层级都是高层,回想一下那些Socket编程所讲到的知识,一般上来就会讲TCP/UDP,也就是处在高层应用开发的高度来讲,网络编程非TCP即UDP,别无其他。在里我讲到以太网分为很多层(按照OSI模型有七层,按照TCP/IP协议有五层),除了应用层以外,我们还有很多底层的数据包跑在网络上(比如ARP数据包、ICMP数据包等),这些数据包大部分不是由一般编程人员通过各种网络应用程序发起的,那么它们是由谁发起的,又是由谁来负责接受和解析呢?这个答案是网卡、交换机,以及操作系统,这些数据包关系到网络的通路、拓扑、路由,用户层面一般不需要了解和控制,对操作系统来说,一般也是不会让用户随意触碰甚至自行构建网络帧的。
WireShark利用winpcap做到了监听底层网络包,它严格地限制了自己无法对网络数据进行修改、编辑、转发。一旦普通用户对以太网的了解更多一些,并且能够利用WireShark处理和转发网络包,那对于网络安全真是一件非常恐怖的事情。
说了这么多,其实都是在讲一些周边知识。WireShark的安装步骤我就不赘述了,下载和安装都是很简单的,现在我们来看看上面提到的那份看上去像乱码的ARP数据包,其实它就是我用WireShark捕获到的,上面的「乱码」只不过是我用十六进制的方式直接复制出来,接下来我们看看这份数据包在WireShark的帮助下会不会更清晰一点:
如上图所示,在WireShark的帮助下,数据包中每一个结构都清晰可见,WireShark帮助我们将一堆十六进制的Byte理解得就如同json数据一样清晰,其内置的数千种网络协议,使得WireShark对各种各样的网络包每一位的作用都了如指掌。在软件的下方区域点击任意一位Byte,它都会在中部区域将这一位的含义和作用清晰地显示出来。
我们再来看一个稍微复杂点的IPV4数据包,是我们常用的一个ping指令发出去的ICMP帧,在WireShark上显示74个Byte:
同样,WireShark帮助我们理解起来要非常清晰,截图其实并不能很好地体现,我们在使用WireShark的时候随时可以用鼠标点击任意字节,显示其含义。
广播和点对点最后我们再介绍一些交换机的附带知识,我们知道网络帧一般会在开头的6个字节标明这个网络帧的接受设备的MAC地址,如果这个包要向局域网内所有设备进行广播,比如上面那个ARP包,这六个字节一般为FF FF FF FF FF FF,如果这个包要向一个特定的设备进行通信,这六个字节则为一个确定的MAC地址,比如上面那个ICMP包,其指向了交换机,交换机收到了以后需要向上行网络请求。
如果局域网内的主机A需要向主机B发送一个点对点的数据,那么封装后的以太网帧的前六个字节就为主机B的MAC地址,交换机收到后会查询自己的维护的映射表,比如找到自己的端口2上的设备的MAC地址就是B的地址,那么交换机就向端口2上的网线投送这个数据包,交换机其他端口上的设备就收不到这则数据包了。
那么我们在做以太网协议分析时,一般是处于主机C的位置来监听和分析A和B的通信,大多数情况下他们之间是点对点通信,主机C在局域网中是观察不到这些数据包的,因为交换机压根就不将A和B的通信数据转发给主机C。
这时候就要祭出又一款硬件神器了:带有端口镜像功能的网管型交换机
利用这种交换机,可以进入管理控制界面,配置端口映射,将设备A和设备B之间通信的双向数据全都转发给设备C,方便WireShark进行协议分析,最终我们可以根据分析出来的协议,在嵌入式STM32中编写自己的协议栈,实现DIY的设备D,与设备A进行通信,取代设备B。
上图就是我购买的一款网管型交换机中配置端口映射的界面。
小结这一章我们对以太网的帧结构有了一个基本的认识,了解了网络上跑的各种数据包在MAC层的表现形式,能够运用WireShark观察一些简单的由各种十六进制数据组成的数据包,最后我们还了解了如何运用网管型交换机配置端口映射,在局域网中的C端监听A和B的通信。
以上,为下一步在STM32嵌入式设备中编写网络协议打下了基础。

我要回帖

更多关于 原子战舰 的文章

 

随机推荐