以太网

前言

1.jpg

作者——宋兰中

随着自动化、智能化、网联化的发展及其在汽车中越来越广泛的应用,行业对各个汽车控制单元安全高效的更新数据提出了越来越高的要求。本文作者通过研究汽车领域对ECU更新需求和英飞凌AURIX™ TC3xx系列A/B SWAP功能,以AURIX™ TC3xx千兆以太网为媒介,TC387为载体研究如何实现OTA,并使用英飞凌AURIX™ TC3xx HSM模块有效提高OTA安全,验证了当前热门OTA技术。

什么是OTA

OTA:Over-the-Air Technology,即空中下载技术。

OTA升级:通过OTA方式实现固件或软件的升级。通过无线通信方式实现软件升级,都可以叫OTA升级,比如无线以太网/蓝牙等。

HSM:Hardware Security Module 模块保证刷写的安全可靠。

OTA 系统功能示意如图1所示:

2.png

图1 系统功能示意图

几种常见的OTA实现方式比较及优势分析

在进行SOTA更新时,需要把旧的应用程序擦除,把新的应用程序写入。常规的实现方式需要分别开发BootLoader程序和APP程序,MCU上电先运行BootLoader,BootLoader根据情况选择是否跳转到APP和是否进行程序更新。具体来说有以下几种方式:

1、方案一

BootLoader中内置通讯协议栈,更新时,先向MCU发送指令使其跳转到BootLoader,之后先擦除旧APP,在接收新APP的同时直接将其写入Flash的APP运行地址处。该方案的优点是不需要额外的Flash暂存数据,缺点是BootLoader代码更复杂,且如果数据传输发生中断,旧的APP将不能被恢复。该方案更适合Flash容量较小的MCU。

2、方案二

更新程序时,由APP接收更新数据并暂存于Flash,再将APP更新标志位置位;MCU重启时,BootLoader检查更新标志位,如有效,则擦除旧的APP,再将暂存于Flash的新APP数据写入APP运行地址处。该方案的优点是更新数据的接收由APP完成,BootLoader不需要通讯协议栈,代码量更小,且数据传输中断时,原有APP不损坏。缺点是需要额外的Flash空间暂存更新数据。

3、方案三

在Flash中划分出两块相同大小的区域,分为A区和B区,都用来存放APP,但同一时间下只有一个区的APP是有效的,分别设置一个标志位标识其有效性。初始状态下先将APP写入A区,更新的时候,将新的APP写入B区,再把A区的APP擦除,同时更新两个区的有效性标志位状态。BootLoader中判断哪个区的APP有效,就跳转到哪个区运行。这种方法不需要重复拷贝APP数据,但最大的一个缺陷是AB区的APP程序运行地址不同,需要分别编译,从而使得可应用性大大降低。

注释:同时也可以将方案一和方案二相结合,即先采用方案一在BootLoader程序中内置通讯协议栈,更新时,先向MCU发送指令使其跳转到BootLoader。之后接收更新数据的时候,采用方案二的方法,先将数据暂存于Flash,待数据全部接收完成后再擦除旧的APP,写入新的APP。结合方案一和方案二的优点,且能在没有APP或APP损坏的状态下实现程序更新。缺点是BootLoader代码量更大,Flash空间占用更大。

英飞凌AURIX™ TC3xx实现上述SOTA方案拓扑图,如图2 所示:

3.png

图2  TC3xx实现SOTA方案常见拓扑图

经过上面的分析,可以看到几种常见方案都有其优缺点。但对于TC3xx这一类的MCU来说,Flash容量通常都很大,足够用,所以通常可以先把APP暂存下来再进行更新,防止数据传输中断导致APP不可用。

同时AURIX™ TC3xx也支持AB SWAP功能。以方案三为例:TC3xx系列如果使能SOTA功能,它的AB Bank Flash物理地址支持两种不同物理地址映射到同一个逻辑地址方式(MCU自动从两种物理地址映射一个虚拟地址),从而使得APP编译时不需要区分AB区,使用相同的逻辑地址即可,从而避免了方案三的硬伤,为我们提供了一种最佳的SOTA方案。接下来,我们将以方案三作为基础,结合实例详细讲解使用英飞凌AURIX™ TC3xx如何实现更优的SOTA。

推荐的OTA实现方式详解

TC3xx的Flash地址映射方式

首先, TC33x和TC33xED不支持AB SWAP功能,其他TC3xx设备都能够通过AB SWAP功能实现SOTA软件更新。

TC3xx 如果使能了AB SWAP功能,Flash大小实际能用的最少减半,TC3xx各系列AB SWAP能力如图3所示。

4.png

图3  TC3xx支持AB SWAP功能芯片系列及映射关系

启用SOTA功能时,通过将PFLASH拆分为两A和B两个Bank的能力,其中一组可以读取和执行BANK组,而另一组可以写入新代码。因此虽然单个物理PFLASH Bank中不支持同时读写(RWW)功能,但是通过AB分组支持未使用的BANK组提供安全可靠地对数据执行写入和擦除操作的能力来实现SOTA功能。

举例TC387 AB SWAP特性

为了方便理解英飞凌TC3xx  SOTA 功能,我们以TC387为例进行分析。TC387 PFLASH 10M空间映射关系,使能了AB  SWAP后,实际使用大小为4M,如图4所示:

5.png

图4 TC387 PFLASH 映射关系以及可用PFLASH大小

TC387的4M PFlash地址空间无论是A Bank还是B Bank, 对于用户来说,统一为虚拟地址0X80000000-0x803FFFFF 4M地址空间。但是刷写过程中, A bank实际操作物理地址0X80000000-0x803FFFFF 4M空间,B Bank 实际操作物理地址0X8060 0000-0x80AF FFFF 4M空间。

注意,如果使能了AB SWAP功能,TC3xx PFLASH就没有所谓Local PFLASH和Global PFLASH概念,统一理解为Global PFLASH。CPU访问PFLASH由之前的CPUx可以通过Local总线访问本PFLASHx提高访问速度,变为CPUx访问PFLASH只能通过Global总线从而稍微增加了CPU访问PFLASH时间。具体参考图5所示。

6.png

图5  SOTA功能使能后只能通过Global总线访问PFLASH

TC3xx的SOTA功能描述

当TC387 SOTA功能激活时,PFLash被划分为两部分A Bank和B Bank,一部分用来存储读取可执行代码(active bank),另一部分可用来写入(inactive bank)即刷写。当APP更新完毕后,两个部分互换,即切换上面两种地址映射方式。在标准模式下使用PF0-1作为active bank,后文称作组A,在Alternate模式下使用PF2-3作为active bank,后文称作组B,就可以实现第二章节所述方案三,且能写入完全相同的APP程序,以相同的地址(逻辑地址)进行运行。

需要注意的是,所有NVM操作都是通过DMU使用PFLASH的物理系统地址执行的,也就是说,NVM操作总是使用标准的地址映射,而不管选择使用哪种地址映射。“NVM操作”是一个术语,用于任何针对FLASH的命令,如程序、擦除等,但不包括读取代码。有关SOTA地址映射的参数在Flash中的UCB(User Configuration Block)中进行配置,在UCB中配置后,只有当下次MCU复位的时候才会更新配置,后文会有详细解释。

TC3xx的SOTA功能实现详解

实现SOTA功能所需关注配置项

英飞凌AURIX™ TC3xx实现SOTA功能主要需要配置如图6所示:

7.png

图6 SOTA功能所需关注配置项

1、SOTA模式使能UCB_OTP.PROCONTP.SWAPEN,该参数决定是否开启SOTA模式,在寄存器Tuning Protection Configuration中的SWAPEN进行配置,对应UCB定义如下:

使能AB SWAP功能的UCB定义(UCB32-39是ORIG, 40-47 COPY,建议全部都需要配置,内容可以一样。)如下:

8.png9.png

10.png

11.png

2、配置UCB_SWAP_ORIG/UCB_SWAP_COPY中的UCB_SWAP_ORIG_MARKERLx/UCB_SWAP_COPY_MARKERLx,激活下一次reset需要运行的标准(0x00000055)还是备选(0x000000AA)地址。在寄存器SCU_SWAPCTRL中,可以查看当前激活的是标准还是备选地址。

我们参考下面关于SOTA功能实现的UCB,内容定义:

12.png


13.png

14.png

15.png

16.png

3、同1描述UCB块,只要使能了SOTA就会自动禁止CPU通过本地总线访问PFLASH功能,红色方框中寄存器值自动为1,即禁止。

17.png

18.png

19.png

SOTA功能实现时SWAP配置及流程

SOTA功能应用时:分系统刚启动时SWAP配置和系统运行时SWAP配置。

系统启动时SWAP配置:

如果SOTA功能使能,那么代码生成的文件至少需要刷进Active Bank。为了信息安全,建议通过UCB_PFLASH设置相应的sectors读写保护。

起始地址需要在UCB_BMHD配置好。

如果当前选择的是标准地址,那么0x00000055H需要写入UCB_SWAP的MARKERL0.SWAP这个域。然后通过把MARKERL0.SWAP的地址写入MARKERH0.ADDR予以确认;同时,将CONFIRMATIONL0.CODE的地址写入CONFIRMATIONH0.ADDR;同时,将57B5327FH写入57B5327FH予以确认。

UCB_ OTP一次性刷写保护以设置所需的OTP、WOP和标定保护。请注意,任何受OTP或WOP保护的扇区都不能使用新映像重新编程。

如果使能了HSM,主核代码和HSM代码需要同时刷入到AB bank的PFLASH S0-S39。

任何受OTP保护的HSM扇区都不能使用新映像重新编程。

最后,由于SWAPEN是在UCB_OTP里面设置的,所以要在下一个重启后SOTA的使能才有效。具体流程,参考图7所示:

20.png

图7 系统刚启动时SWAP配置及流程

系统运行时SWAP配置:

下面是程序正在运行时,需要实现软件SWAP到新程序的配置流程。

为了可以正确切换到新程序中,首先新的程序需要刷到对应的非激活的PFLASH Bank,如果非激活的BANK中对应的sectors使能了读写保护,那么刷写之前要先解保护。

切记:由于NVM特性,PFLASH 和DFLASH不能同时操作。因此,在应用程序中运行的EEPROM驱动程序和执行BOOT刷写之间需要进行一些协调。确保要写入的新程序所用的的PFLASH正确无误。例如:如果在PFLASH的SOTA重新编程/擦除期间出现硬故障,可以使用替换逻辑扇区功能(有关更多详细信息,请参阅DMU章节)。此功能允许用户使用“替换逻辑扇区”命令序列将故障逻辑扇区映射到冗余扇区。

由于UCB刷写次数的限制(100次),我们可以通过16 个SWAP配置依次使用来增加SWAP的次数(100*16=1600次)。方式流程如下图8所示:

注意:上一次用过的配置,CONFIRMATIONL(x-1) ) 和CONFIRMATIONH(x-1) 全写为1。

增加SWAP次数,可以通过UCB_SWAP_ORIG/UCB_SWAP_COPY配置如下寄存器:

21.png

22.png

23.png

24.png

25.png

增加SWAP次数方法流程如图8所示:

26.png

图8 增加SWAP次数方法流程

新的配置写好后,选择下一次要激活的程序,等下一次重启即运行新刷写的程序。详细流程如图9所示:

27.png

图9 系统运行时SWAP配置

总结

TC3xx  SWAP特性实现OTA功能后,特别注意以下五点:

  1. Flash大小实际能用的最少减半,详情参考图3。

  2. CPU访问Flash只能通过Global总线从而稍微增加了访问时间,参考图5。详细参数请查相应的数据手册。

  3. PFLASH的prefetch功能被禁止,同样会稍微影响整个系统的性能。

  4. 功能安全方面:Active Bank 的safety_endinit保护依旧存在,但是Inactive Bank的safety_endinit保护无效。

  5. 信息安全方面:Active Bank 和Inactive Bank同样受信息安全相关寄存器的保护。

至此,TC3xx  SWAP特性实现SOTA功能的配置和流程介绍完毕。

关于以太网、HSM为媒介、对SOTA功能研究与实现介绍,请关注后续更新。

来源:英飞凌汽车电子生态圈(作者:宋兰中)

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 11

IAP概述

工作原理

在应用中编程(IAP)是一种在现场通过MCU通信接口(例如USART、USB、CAN和以太网)进行固件升级的方式。

启动微控制器时,可以选择让其在以下任一模式运行:

  • IAP模式,用于执行IAP代码

  • 正常模式,用于执行应用程序代码

无论是IAP代码还是应用程序代码都位于微控制器的内置FLASH中,IAP代码通常存储在MCU FLASH的第一页,而用户应用程序代码则占据剩余的FLASH区域。

图1介绍了IAP操作流程

1.png

图1 IAP操作流程

使用MCU以太网接口实现IAP

如果有以太网可用,则它通常是嵌入式系统中实现IAP功能的首选接口,其优势包含:

  • 高速通信接口(10/100 Mbps)

  • 通过网络(LAN或WAN)进行远程编程

  • 可以使用FTP、TFTP、HTTP等基于TCP/IP栈的标准应用协议实现IAP

通过以太网在AT32F437上实现IAP

本应用笔记将介绍两种使用以太网通信外设在AT32F437上实现IAP的解决方案:

  • 使用TFTP(简单文件传输协议)的IAP

  • 使用HTTP(超文本传输协议)的IAP

这两种解决方案均基于LwIP栈(2.1.2),它是轻量级的TCP/IP协议栈

使用TFTP实现IAP方法

使用TFTP实现IAP的方法广泛应用于需要具有固件升级功能的嵌入式系统应用中(例如,嵌入式Linux bootloader中)。

TFTP是一种在UDP传输层上执行的简单文件传输协议。此协议非常适合在局域网环境中使用。它基于客户端/服务器架构,在这种架构中,客户端会向服务器发出文件传输请求(读取或写入操作)。

为实现IAP,需要在LwIP协议栈上实现一个简单的TFTP服务器,服务器只须处理来自PC的TFTP客户端的写入请求即可。

使用HTTP实现IAP方法

使用HTTP协议进行固件升级没有使用TFTP常见,但是在需要通过Internet进行远程编程时,这种解决方案就显得极为有用。这时,需要使用TCP传输协议来实现HTTP服务。

HTTP基于TCP协议运行,它提供了一个一种以HTML表单形式从Web客户端(Mozilla Firefox或Microsoft Internet Explorer)发送一个二进制文件的方式。这称为HTTP文件上传(RFC1867)。

本文档中的后续章节将详细介绍这两种IAP方法的实现,并会对如何使用软件进行说明。

使用TFTP实现IAP

TFTP概述

TFTP是一种基于UDP的简单文件传输协议。文件传输由TFTP客户端发起,会向TFTP服务器发送读取或写入请求。服务器确认请求后,即开始进行文件数据传输。数据将以固定大小的块尽情发送(例如每块含512个字节)。

必须在每个发出的数据块都得到接收方确认后,才可以发送下一个数据块。这种确认机制通过随各个数据块一同发送的编块号来实现。数据块小于固定块大小表示文件传输的结束。

图2描述了各种TFTP数据包的格式:

2.png

图2 TFTP数据包

表1列出了TFTP操作码。

3.png

表1 TFTP操作码

使用TFTP为AT32F437实现IAP

此IAP实现由基于LwIP TCP/IP栈的TFTP服务器组成。此服务器会对远程TFTP客户端(PC)发来的写请求做出响应。TFTP读请求会被忽略。TFTP通常会将接收到的文件写入到文件系统,但是该服务器却并非如此,它会将接受到的数据块写入到MCU FLASH(用户FLASH区域中)。注:在这个实现过程中,数据块大小固定为512个字节。

图3 概述了使用TFTP实现IAP操作的过程。

4.png

图3 使用TFTP实现IAP流程图

使用软件

要通过TFTP对IAP进行测试,需执行以下步骤:

1. 在iap.h文件中,取消USE_IAP_TFTP选项的注释。

2. 重新编译软件。使用生成的映射文件,确保IAP代码区域之间没有重叠(从地址0x0开始),而且用户FLASH区域从以下地址开始:APP_START_SECTOR_ADDR(在iap.h中定义)。

3. 在AT32 FLASH中编写并运行软件程序。

4. 要进入IAP模式,需要在按住开发版上的USER Key。

5. 分配完IP后(可以是静态或动态地址),用户即可启动IAP流程。

6. 在PC侧,打开TFTP客户端(例如Tftpd64),然后配置TFTP服务器地址(Tftpd64中的主机地址)

7. 单击Tftpd64实用程序中的Put(写入)按钮,启动文件写请求

8. 在IAP操作结束时,可以复位开发板并在AT32 FLASH中运行刚刚编写的应用程序

5.png


图4 TFTP64对话框

使用HTTP实现IAP

HTTP文件上传概述

RFC1867中定义了使用HTTP进行文件上传。此文件上传方法是基于HTTP表单。发送原始二进制数据时,要使用HTTP POST方法而不是GET方法。

以下是一个HTML代码示例,用于实现基于表单的文件上传:

6.png

图5 文件上传HTML表单的浏览器画面

注:在发送文件数据前,Web客户端会首先发送HTTP头文件数据,其中包含诸如文件名称和内容长度等信息,Web服务器必须对其中的一些信息进行解析。

Web客户端使用的HTTP头文件格式并不总是相同。图6显示的是Internet Explorer在POST请求中的HTTP头文件格式。图7显示的是Mozilla Firefox的HTTP头文件格式。

HTTP Web服务器必须能够处理这些不同的格式。

7.png

图6 IE11 HTTP头文件格式

8.png

图7 Mozilla Firefox HTTP头文件格式


使用HTTP在AT32F437上实现IAP

此IAP实现由基于LwIP栈的HTTP Web服务器组成。

在浏览器中输入的AT32的IP地址后,将显示登录Web页面(图8)。此登录Web页面只有已获授权的用户才能使用IAP文件上传功能。

9.png

图8 登录web页面

注:

1. 默认的User ID(用户ID)为user, Password(密码)为at32

2. 如果User ID(用户ID)或Password(密码)不正确,登录Web页面会重新加载。

登录成功后,浏览并选择要上传到AT32 FLASH的二进制文件

注:确保二进制文件大小不超过AT32用户FLASH区域的总容量。

单击Upload(上传)按钮后(参见图x),将向服务器发出POST请求。这时,服务器开始擦除用户FLASH区域的全部内容,等待接受二进制文件原始数据。然后将街收到的数据写入用户FLASH区域。

注意,要接收的数据总长度信息将从传输开始时发出的HTTP头文件数据中提取。

在IAP操作结束后,Web页面将只是IAP操作成功,同时显示一个可用于复位MCU的按钮。

10.png

图9 文件上传完成web页面

图10对使用HTTP实现IAP方法进行了总结

11.png

图10 使用HTTP实现IAP的流程图

使用软件

要使用HTTP对IAP进行测试,需执行以下步骤:

1. 在iap.h文件中,取消选项USE_IAP_HTTP的注释。

2. 重新编译软件,使用生成的映射文件,确保IAP区域代码之间没有重叠(从地址0x0开始),而且用户FLASH区域从以下地址开始:APP_START_SECTOR_ADDR(在iap.h中定义)。

3. 在AT32 FLASH中编写并运行软件程序。

4. 要进入IAP模式,需要再按住开发板上的USER Key。

5. 分配完IP地址后(可以是静态或动态地址),用户即可启动IAP流程

6. 打开Web客户端(Mozilla Firefox或Internet Explorer),输入AT32 IP地址

7. 会显示登录Web页面。在User ID(用户ID)字段中输入”user”,在Password(密码)字段中输入”at32”,然后按下Login(登录)按钮。

8. IP操作结束后,将加载新的Web页面,只是文件上传操作已经成功完成。

9. 可以按下Reset MCU(复位MCU)按钮复位MCU,然后在AT32 FLASH中运行刚刚编写的应用程序。

注:使用以下Web客户端对软件进行测试:Microsoft Internet Explorer 11和Mozilla Firefox 80.0

已知限制

二进制文件中添加的额外字节

Internet浏览器(Microsoft Internet Explorere或Mozilla Firefox)会在上传的二进制文件的末尾添加一个随机边界标记(根据RFC1521规定,此标记不得超过72个字节)。在最新的IAP软件版本中,并没有删除此边界标记,而是在空间足够的情况下将其存储在FLASH中。如果没有足够空间,则不会在FLASH中写入额外字节,也不会返回错误。

环境

硬件配置

1、DM9162以太网模块

2、AT-START-F407开发板

3、以太网线

软件源码

  • utilities\AT32F437_emac_iap_demo\source_code\bootloader, emac iap源程序,运行iap升级程序

MAC地址和IP地址设置

在netconf.h文件中对MAC地址进行了定义。

默认的MAC地址固定为:00:00:44:45:56:01。

在netconf.h文件中对IP地址进行了定义。

IP地址可以设置为静态地址,也可以设置为由DHCP服务器分配的动态地址。默认的静态地址为:192.168.81.37。

可以通过在lwipopts.h文件中使能LWIP_DHCP来选择DHCP模式。

软件文件组成

12.png

表2 介绍了项目源文件

注:表格中没有列出标准固件库和 LwIP 栈中所使用的文件。

构建IAP映像

为了构建IAP映像(将会使用IAP软件加载),应确保以下几点:

1. 编译/链接的软件必须从用户FLASH区域的起始地址开始运行(此地址应与iap.h的APP_START_SECTOR_ADDR中所定义的地址相同)。

2. 将向量表的起始地址配置为用户FLASH区域的起始地址:

A. 在应用程序代码中,使用misc.h/.c驱动程序的NVIC_SetVectorTable函数来重新定位应用程序加载地址的向量表。

例如,将向量表基本位置设置为0x08010000:

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x10000);

B. 通过修改system_at32f4xx.c文件中定义的VECT_TAB_OFFSET常量的值。

例如,将向量表基本位置设置为0x08010000:

#define VECT_TAB_OFFSET 0x10000

3. 编译后的软件大小不超过用户FLASH区域的总容量。

来源:AT32 MCU 雅特力科技

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 16

IAP概述

工作原理

在应用中编程(IAP)是一种在现场通过 MCU 通信接口(例如 USART、USB、CAN 和以太网)进行固件升级的方式。

启动微控制器时,可以选择让其在以下任一模式运行:

  • IAP模式,用于执行IAP代码

  • 正常模式,用于执行应用程序代码

无论是IAP代码还是应用程序代码都位于微控制器的内置FLASH中,IAP代码通常存储在MCU FLASH的第一页,而用户应用程序代码则占据剩余的FLASH区域。

图1介绍了IAP操作流程

“图1

使用MCU以太网接口实现IAP

如果有以太网可用,则它通常是嵌入式系统中实现IAP功能的首选接口,其优势包含:

  • 高速通信接口(10/100 Mbps)

  • 通过网络(LAN或WAN)进行远程编程

  • 可以使用FTP、TFTP、HTTP等基于TCP/IP栈的标准应用协议实现IAP

通过以太网在AT32F407上实现IAP

本应用笔记将介绍两种使用以太网通信外设在AT32F407上实现IAP的解决方案:

  • 使用TFTP(简单文件传输协议)的IAP

  • 使用HTTP(超文本传输协议)的IAP

这两种解决方案均基于LwIP栈(2.1.2), 它是轻量级的TCP/IP协议栈

使用TFTP实现IAP方法

使用TFTP实现IAP的方法广泛应用于需要具有固件升级功能的嵌入式系统应用中(例如,嵌入式Linux bootloader中)。

TFTP是一种在UDP传输层上执行的简单文件传输协议。此协议非常适合在局域网环境中使用。它基于客户端/服务器架构,在这种架构中,客户端会向服务器发出文件传输请求(读取或写入操作)。为实现IAP,需要在LwIP协议栈上实现一个简单的TFTP服务器,服务器只须处理来自PC的TFTP客户端的写入请求即可。

使用HTTP实现IAP方法

使用HTTP协议进行固件升级没有使用TFTP常见,但是在需要通过Internet进行远程编程时,这种解决方案就显得极为有用。这时,需要使用TCP传输协议来实现HTTP服务。

HTTP基于TCP协议运行,它提供了一个一种以HTML表单形式从Web客户端(Mozilla Firefox或Microsoft Internet Explorer)发送一个二进制文件的方式。这称为HTTP文件上传(RFC1867)。

本文档中的后续章节将详细介绍这两种IAP方法的实现,并会对如何使用软件进行说明。

使用TFTP实现IAP

TFTP概述

TFTP是一种基于UDP的简单文件传输协议。文件传输由TFTP客户端发起,会向TFTP服务器发送读取或写入请求。服务器确认请求后,即开始进行文件数据传输。数据将以固定大小的块尽情发送(例如每块含512个字节)。

必须在每个发出的数据块都得到接收方确认后,才可以发送下一个数据块。这种确认机制通过随各个数据块一同发送的编块号来实现。数据块小于固定块大小表示文件传输的结束。

图2描述了各种TFTP数据包的格式:

“图2
图2 TFTP数据包

表1列出了TFTP操作码。

“表1
表1 TFTP操作码

使用TFTP为AT32F407实现IAP

此IAP实现由基于LwIP TCP/IP栈的TFTP服务器组成。

此服务器会对远程TFTP客户端(PC)发来的写请求做出响应。

TFTP读请求会被忽略。

TFTP通常会将接收到的文件写入到文件系统,但是该服务器却并非如此,它会将接受到的数据块写入到MCU FLASH(用户FLASH区域中)。

注:在这个实现过程中,数据块大小固定为512个字节。

图3概述了使用TFTP实现IAP操作的过程。

“图3
图3 使用TFTP实现IAP流程图

使用软件

要通过TFTP对IAP进行测试,需执行以下步骤:

1. 在iap.h文件中,取消USE_IAP_TFTP选项的注释。

2. 重新编译软件。使用生成的映射文件,确保IAP代码区域之间没有重叠(从地址0x0开始),而且用户FLASH区域从以下地址开始:APP_START_SECTOR_ADDR(在iap.h中定义)。

3. 在AT32 FLASH中编写并运行软件程序。

4. 要进入IAP模式,需要在按住开发版上的USER Key。

5. 分配完IP后(可以是静态或动态地址),用户即可启动IAP流程。

6. 在PC侧,打开TFTP客户端(例如Tftpd64),然后配置TFTP服务器地址(Tftpd64中的主机地址)

7. 单击Tftpd64实用程序中的Put(写入)按钮,启动文件写请求

8. 在IAP操作结束时,可以复位开发板并在AT32 FLASH中运行刚刚编写的应用程序

“图4
图4 TFTP64对话框

使用HTTP实现IAP

HTTP文件上传概述

RFC1867中定义了使用HTTP进行文件上传。此文件上传方法是基于HTTP表单。发送原始二进制数据时,要使用HTTP POST方法而不是GET方法。

以下是一个HTML代码示例,用于实现基于表单的文件上传:

“图5
图5 文件上传HTML表单的浏览器画面

注:在发送文件数据前,Web客户端会首先发送HTTP头文件数据,其中包含诸如文件名称和内容长度等信息,Web 服务器必须对其中的一些信息进行解析。

Web客户端使用的HTTP头文件格式并不总是相同。图6显示的是Internet Explorer在POST请求中的HTTP头文件格式。图7显示的是Mozilla Firefox的HTTP头文件格式。

HTTP Web服务器必须能够处理这些不同的格式。

“图6
图6 IE11 HTTP头文件格式

“图7
图7 Mozilla Firefox HTTP头文件格式

使用HTTP在AT32F407上实现IAP

此IAP实现由基于LwIP栈的HTTP Web服务器组成。

在浏览器中输入的AT32的IP地址后,将显示登录Web页面(图8)。此登录Web页面只有已获授权的用户才能使用IAP文件上传功能。

“图8
图8 登录web页面

注:1. 默认的User ID(用户ID)为user, Password(密码)为at32
2. 如果User ID(用户ID)或Password(密码)不正确,登录Web页面会重新加载。

登录成功后,浏览并选择要上传到AT32 FLASH的二进制文件

注:确保二进制文件大小不超过AT32用户FLASH区域的总容量。
单击Upload(上传)按钮后(参见图x),将向服务器发出POST请求。这时,服务器开始擦除用户FLASH区域的全部内容,等待接受二进制文件原始数据。然后将街收到的数据写入用户FLASH区域。

注意,要接收的数据总长度信息将从传输开始时发出的HTTP头文件数据中提取。

在IAP操作结束后,Web页面将只是IAP操作成功,同时显示一个可用于复位MCU的按钮。

“图9
图9 文件上传完成web页面

图10对使用HTTP实现IAP方法进行了总结

“图10
图10 使用HTTP实现IAP的流程图

使用软体

要使用HTTP对IAP进行测试,需执行以下步骤:

1. 在iap.h文件中,取消选项USE_IAP_HTTP的注释。

2. 重新编译软件,使用生成的映射文件,确保IAP区域代码之间没有重叠(从地址0x0开始),而且用户FLASH区域从以下地址开始:APP_START_SECTOR_ADDR(在iap.h中定义)。

3. 在AT32 FLASH中编写并运行软件程序。

4. 要进入IAP模式,需要再按住开发版上的USER Key。

5. 分配完IP地址后(可以是静态或动态地址),用户即可启动IAP流程

6. 打开Web客户端(Mozilla Firefox或Internet Explorer),输入AT32 IP地址

7. 会显示登录Web页面。在User ID(用户ID)字段中输入”user”,在Password(密码)字段中输入”at32”,然后按下Login(登录)按钮。

8. IP操作结束后,将加载新的Web页面,只是文件上传操作已经成功完成。

9. 可以按下Reset MCU(复位MCU)按钮复位MCU,然后在AT32 FLASH中运行刚刚编写的应用程序。

注:使用以下Web客户端对软件进行测试:Microsoft Internet Explorer 11和Mozilla Firefox 80.0

已知限制

二进制文件中添加的额外字节

Internet浏览器(Microsoft Internet Explorere或Mozilla Firefox)会在上传的二进制文件的末尾添加一个随机边界标记(根据RFC1521规定,此标记不得超过72个字节)。在最新的IAP软件版本中,并没有删除此边界标记,而是在空间足够的情况下将其存储在FLASH中。如果没有足够空间,则不会在FLASH中写入额外字节,也不会返回错误。

环境

硬件配置

1. DM9162以太网模块

2. AT-START-F407开发板

3. 以太网线

软件源码

utilities\at32f407_emac_iap_demo\source_code\bootloader, emac iap源程序,运行iap升级程序

MAC地址和IP地址设置

在netconf.h文件中对MAC地址进行了定义。

默认的MAC地址固定为:00:00:44:45:56:01。

在netconf.h文件中对IP地址进行了定义。

IP地址可以设置为静态地址,也可以设置为由DHCP服务器分配的动态地址。默认的静态地址为:192.168.81.37。

可以通过在lwipopts.h文件中使能LWIP_DHCP来选择DHCP模式。

软件文件组成

“表2
表2 介绍了项目源文件

注:表格中没有列出标准固件库和LwIP栈中所使用的文件。

构建IAP映像

为了构建IAP映像(将会使用IAP软件加载),应确保以下几点:

1. 编译/链接的软件必须从用户FLASH区域的起始地址开始运行(此地址应与iap.h的
APP_START_SECTOR_ADDR 中所定义的地址相同)。

2. 将向量表的起始地址配置为用户FLASH区域的起始地址:
A. 在应用程序代码中,使用misc.h/.c驱动程序的NVIC_SetVectorTable函数来重新定位应用程序加载地址的向量表。

例如,将向量表基本位置设置为0x08010000:

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x10000);

B. 通过修改system_at32f4xx.c文件中定义的VECT_TAB_OFFSET常量的值。

例如,将向量表基本位置设置为0x08010000:

#define VECT_TAB_OFFSET 0x10000

3. 编译后的软件大小不超过用户FLASH区域的总容量。

来源:AT32 MCU 雅特力科技
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 29

1、前言

STM32H7 以太网的 MMC(MAC management counter)中断是个有点特别的中断。特殊之处在于它是默认使能。如果我们在代码里不针对 MMC 进行相关处理,就会造成一些异常现象。我们先来看一个真实的客户案例。

2、客户案例

客户使用 STM32H750 作为主控,与其他设备之间进行以太网通讯。

客户在压力测试中发现:

• 设备从第一次通讯开始,累计 7 到 8 天,就会发现 STM32H750 不再响应用户的请求。

• 客户通过使用 IDE 和添加辅助代码可以发现,STM32H750 会不停地进入以太网中断,导致所使用的操作系统无法进行有效的系统调度。

• 问题发生后,客户无论拔下网线或者再次连上网线,STM32H750 依然会不停的进入以太网中断。

• 客户尝试使用 IDE 查看所有以太网寄存器,会发现有时侯能够让系统恢复正常。

3、分析

系统不停的进入以太网中断,说明某个中断在被某种条件下被不停的触发,或者中断触发后没有被处理。进一步,当系统出现异常状况后,拔掉网线,中断依然不断的进入,说明该异常并不需要外界不停的输入,也就说明可能是中断没有被处理所导致。所以,客户首先想到的是补全所有使能的以太网中断的清除代码。然而,客户再次测试,却发现累计 7 到 8 天,问题再次发生。

在这种情况下,为了深刻了解该状况的原因,我们建议客户,抓取异常时的寄存器现场,然后和正常状态时的寄存器进行对比。我们在设备未发生异常前,抓取了以太网的三组寄存器 DMA、 MTL 和 MAC。同时,我们在发生异常后,在同一设备再次进行这三组寄存器的抓取。然后,我们使用文本比较工具,对两次的寄存器进行比较。我们很快就可以发现,MAC 寄存器存在值得关注的差异。MAC 寄存器对比如下:

“STM32H7

我们可以看到在系统异常情况下下,MMCRXIS 和 MMCIS 被置位了。

我们从参考手册 RM0433 (STM32H742, STM32H743/753 and STM32H750 Value line advanced Arm®-based 32-bit MCUs)(直接搜索关键子 MMCRXIS)中可以看到 MMCRXIS 和 MMCIS 表示系统收到了 MMC 接收中断。

“STM32H7

在两次三组寄存器的比较中,我们看到系统生成了 MMC 接收中断(MMC_RX_INTERRUPT 中 RXUCGPIS)。这个符合前文的 MMCRXIS 和 MMCIS 的状态。

“STM32H7

从参考手册 RM0433 中我们可以看到,只要 MMC 选项使能,该中断标志就为有效。但是我们并没有使能 MMC 选项,甚至我们都没有使能 MMC 中断,为什么还是有中断产生呢?

4、MMC 中断的特点

MMC 选项其实是默认使能。我们可以从参考手册 RM0433 中看到这一点。

“STM32H7

在 MMC 默认使能的情况下,什么情况下会产生中断呢?

让我们在 RM0433 里搜索下两次寄存器比较发现的 RXUCGPIS 寄存器:

“STM32H7

综合这两点,我们可以认为,在长时间以太网收发包之后,MMC 中断几乎一定会发生。这符合客户案例的场景,例如,重现这个问题需要 7 到 8 天。当然从这里我们也可以推断出,我们如果加快测试数据包收发的发送,MMC 中断会发生更早。那么,如何避免在产品应用中这种问题发生呢?

5、解决方案

1.1. 使用 MMC 中断

MMC 中断是个有用的功能。如果我们要使用的话,可以参考 MMC Rx interrupt register (ETH_MMC_RX_INTERRUPT)和 MMC Tx interrupt register (ETH_MMC_TX_INTERRUPT)的描述。我们需要对 MMC 进行一个读的操作。

“STM32H7

“STM32H7

这也解释了,客户为什么发现,通过调试器一个一个去读取以太网寄存器,会在某个操作时让异常状态恢复到正常。

1.2. 关闭 MMC 中断

在很多情况下,MMC 中断对实际产品没有意义。例如,在这个案例中,我们可以选择关闭 MMC中断。这就需要用到 MMC 中断的 mask 寄存器:

• MMC Rx interrupt mask register (ETH_MMC_RX_INTERRUPT_MASK)

• MMC Tx interrupt mask register (ETH_MMC_TX_INTERRUPT_MASK)

我们可以添加以下代码到我们的应用代码里

“STM32H7

客户反馈找不到 ETH 的定义。其实在 STM32H7 的例程里,我们可以很容易发现 ETH 定义在

STM32Cube\Repository\STM32Cube_FW_H7_V1.8.0\Drivers\CMSIS\Device\ST\STM32H7xx\Include\stm32h750xx.h:

“STM32H7

也就是说,如果你的工程代码源自 STM32Cube 例程,你应该能够加入以上代码并且能够成功运行。

在加入上述代码或者类似操作后,客户反馈,再次进行超过 7 天以上的压力测试,系统运行正常。

6、总结

STM32H7 的 MMC 中断需要加以注意,如果不使用 MMC,需要确保它已经关闭;否则在经过长时间网络收发后,系统会产生并非用户所期望的中断,导致系统假死。另外,我们也看到了调试STM32 以太网的常规方式,也就是借助工具而不需要写代码就可以进行寄存器的比较。这种方法值得使用 STM32 以太网的用户进行调试时参考。

来源:STM32单片机
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 18

STM32H7 以太网的MMC(MAC management counter)中断是个有点特别的中断。特殊之处在于它是默认使能。如果我们在代码里不针对MMC 进行相关处理,就会造成一些异常现象。我们先来看一个真实的客户案例。

详阅请点击下载《STM32H7以太网的MMC中断》

来源:ST
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 15

HTTP(Hyper Text Transfer Protocol超文本传输协议)是用于从万维网服务器传输超文本到本地浏览器的传输协议,它基于TCP/IP协议通信,因此也是基于<客户端-服务器>模型运作的。HTTP属于应用层协议,我们可以用它来传输服务器的各种资源,如文本、图片、音频等,具有简单、快捷、灵活、无连接、无状态等优点。

在一次完整的HTTP通信过程中,浏览器与服务器之间大致可以分为七个步骤:

01、建立TCP连接

HTTP工作前,浏览器首先要通过网络与服务器建立连接,该连接通过TCP完成。HTTP属于应用层协议,根据规则只有低层协议建立之后才能进行更深层协议的连接。因此,首先要建立TCP连接,端口号为80。

02、浏览器向服务器发送请求命令

TCP连接成功建立后,浏览器就会向服务器发送请求命令。例如:GET/sample/hello.jsp HTTP/1.1

03、浏览器发送请求头信息

浏览器发送请求命令后,还要以头信息的形式向服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。

04、服务器应答

客户机向服务器发出请求后,服务器会客户机回送应答,例如:HTTP/1.1 200 OK

应答的第一部分是协议的版本号和应答状态码。

05、服务器发送应答头信息

正像客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。

06、服务器向浏览器发送数据

服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着会按照应答头信息所描述的格式发送用户所请求的实际数据。

07、服务器关闭TCP连接

一般情况下,一旦服务器完成了数据,就会关闭TCP连接。

在创建工程前我们还需要了解HTTP请求格式,HTTP请求由三部分构成:请求方法URI协议、请求头、请求正文。

请求方法URI(URL相当于URI的子集)协议

我们使用前文中的例子“GET/sample.jsp HTTP/1.1”。“GET“代表请求方法,“/sample.jsp”表示URI,“HTTP/1.1代表协议和协议的版本。

请求头

请求头包含许多有关的客户端环境和请求正文的有用信息。例如,请求头可以声明浏览器所用的语言,请求正文的长度等。

请求正文

请求头和请求正文之间是一个空行,这个行非常重要,它表示请求头已经结束,接下来的是请求正文,请求正文中可以包含客户提交的查询字符串信息。

实验使用MB-039开发板,在工程中使用LwIP+FreeRTOS,实验展示如何实现HTTP服务器,实验使用到的硬件如下:

“基于MM32F3270

如图是MB-039(完整原理图可以通过MM32官网下载)的ETH部分。

各个信号引脚对应如下:

“基于MM32F3270

“基于MM32F3270

我们创建HTTP工程:

static void
http_server_netconn_thread(void* arg)
{
*********************************************************
 do {
        err = netconn_accept(conn, &newconn);     //(1)
        if (err == ERR_OK) {
            http_server_netconn_serve(newconn);    //(2)
            netconn_delete(newconn);            
        }
    } while(err == ERR_OK);
*********************************************************
}
static void http_server_netconn_serve(struct netconn* conn)
{
    struct netbuf* inbuf;
    char* buf;
    u16_t buflen;
err_t err;

    err = netconn_recv(conn, &inbuf);           //(3)
    if (err == ERR_OK) {
        netbuf_data(inbuf, (void**)&buf, &buflen);
        if (buflen >= 5 &&
                buf[0] == 'G' &&
                buf[1] == 'E' &&
                buf[2] == 'T' &&
                buf[3] == ' ' &&
                buf[4] == '/' ) {        //(4)

            netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);              //(5)

            netconn_write(conn, http_index_html, sizeof(http_index_html) - 1, NETCONN_NOCOPY);           //(6)
        }
    }

    netconn_close(conn);      //(7)

    netbuf_delete(inbuf);
}

01、等待连接请求

02、执行数据发送的主要函数,工程的主要部分

03、接收客户端(浏览器)发送的数据

04、通过前面五个字节判断是否为请求方法,我们只是为例简单的功能展示不需要去关注正文部分

05、发送应答头信息

06、发送网页信息(html)

07、关闭连接

到这里已经完成了工程的创建,看一下PC的IP地址,设备需要处于同一网段方便测试。打开命令行窗口输入:ipconfig

“基于MM32F3270

PC的地址为:192.168.105.34,在sys_arch.h文件中对DEST_IP_ADDR0 、DEST_IP_ADDR1、DEST_IP_ADDR2、DEST_IP_ADDR3进行修改,DEST_PORT 随意修改。

#define LOCAL_PORT                 2021

#define IP_ADDR0                    192
#define IP_ADDR1                    168
#define IP_ADDR2                    105
#define IP_ADDR3                    35

将程序下载入开发板中,打开浏览器,输入设备地址:192.168.105.35

“基于MM32F3270

(1)点击灵动微电子即可跳转到我们的官网

“基于MM32F3270

(2)我们按F12来查看网页的信息

“基于MM32F3270

关注画圈部分,点击灵动微电子可以执行跳转的原因是他的href属性指向的超链接目标的URL为我们的官网地址。同样,使图片显示的方式是类似的,我们先找到一张图片,将他的地址填入img src中,此时图片并非存储在MCU中,是通过网络访问读取的。

我们也可以使用Wireshark进行监视抓取整个过程,选择IP过滤ip.addr==192.168.105.35。

“基于MM32F3270

可以看到整个过程和前文介绍的步骤完全一致。

实验程序请登录我们的官网(https://www.mindmotion.com.cn/products/mm32mcu/mm32f/mm32f_mainstream/mm32f3270/)下载MM32F3270 SDK,工程路径如下:

~\MM32F3270_Lib_Samples_V0.90\Demo_app\Ethernet_Demo\ETH_RTOS\Freertos_http

到这里已经完成了以太网专题的介绍,在样例包中,还提供了其他的样例工程,如:mqtt_onenet、mqtt_baidu、Freertos_dns、Freertos_dhcp、TFTP等。以太网协议本身非常庞大,需要用户花费时间去研究。专题通过对六个简单实验的介绍,起到抛砖引玉的作用,更大的意义在于使刚接触以太网的用户有个着手点,实现更复杂的功能。

来源:灵动MM32MCU
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 39

前面重点对Client的创建方式及使用方式进行了介绍,本节通过Server实验对TCP通信过程进行一次介绍。

在TCP/IP协议中,传输层及以下层的机制是由内核提供的。应用层由用户提供,应用层程序对通信数据进行解析处理,传输层及以下层处理通信的细节(将数据从一端传入另外一端)。应用层数据通过协议栈发送到网络上时,每层协议都要增加一个数据部首(header),进行一次封装。其中不同的协议层对数据包有不同的称谓,在传输层叫段(segment),在网络层叫做数据报(datagram),在链路层称为帧(frame)。

“基于MM32F3270

“基于MM32F3270

在通信过程中,发送端执行以下动作:首先程序进行编码,确定通信的建立连接、发送数据的时间。接着建立TCP连接,TCP根据应用指示负责建立连接、发送数据及断开连接。TCP首部包括源端口号和目标端口号、序号和校验和,加完首部后数据包继续往下传递到IP层,IP层加上IP首部包括地址等信息用于寻址操作,之后将数据继续往下传递附加数据链路层首部。最后发送时的分组数据包会加上以太网包尾(用于循环冗余校验)。

主机端:收到数据包后会在以太网数据包中找到MAC地址,判断是否为自己的数据包,如果不是则丢弃。如果是传递给IP层处理,以此类推,不断往上传递到TCP层。在TCP层通过校验和判断数据是否损坏,然后检查是否按序号接收数据,最后检查端口号。处理完成这一切后数据包继续往上层发送,即应用层。如果出现主机空间已满等情况,主机则会发送“处理异常”通知发送端。

“基于MM32F3270

实验使用MB-039开发板,在工程中使用LwIP+FreeRTOS,实验展示如何制作一个TCP Server,并收发数据,实验使用到的硬件如下:

“基于MM32F3270

如图是MB-039(完整原理图可以通过MM32官网下载)的ETH部分。

各个信号引脚对应如下:

“基于MM32F3270

“基于MM32F3270

在进行Server实验前,我们先了解需要使用到的API:

1)netconn_bind ()

2)netconn_listen ()

3)netconn_accept ()

以下分API展开介绍:

01、netconn_bind ()

从源码中可以看出其主要功能:为conn(服务器端)绑定地址与端口号。

err_t netconn_bind(struct netconn* conn, const ip_addr_t* addr, u16_t port)
{
    API_MSG_VAR_DECLARE(msg);
    err_t err;

    LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
#if LWIP_IPV4
    /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
    if (addr == NULL) {
        addr = IP4_ADDR_ANY;
    }
#endif /* LWIP_IPV4 */
#if LWIP_IPV4 && LWIP_IPV6
    if ((netconn_get_ipv6only(conn) == 0) &&
            ip_addr_cmp(addr, IP6_ADDR_ANY)) {
        addr = IP_ANY_TYPE;
    }
#endif /* LWIP_IPV4 && LWIP_IPV6 */
    API_MSG_VAR_ALLOC(msg);
    API_MSG_VAR_REF(msg).conn = conn;
    API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
    API_MSG_VAR_REF(msg).msg.bc.port = port;
    err = netconn_apimsg(lwip_netconn_do_bind, &API_MSG_VAR_REF(msg));
    API_MSG_VAR_FREE(msg);
    return err;
}

02、netconn_listen ()

netconn_listen指向的函数是:netconn_listen_with_backlog,作用:使服务器进入监听状态,等待远端的连接请求。

err_t netconn_listen_with_backlog(struct netconn* conn, u8_t backlog)
{
#if LWIP_TCP
    API_MSG_VAR_DECLARE(msg);
    err_t err;

    /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
    LWIP_UNUSED_ARG(backlog);

    LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);

    API_MSG_VAR_ALLOC(msg);
    API_MSG_VAR_REF(msg).conn = conn;
#if TCP_LISTEN_BACKLOG
    API_MSG_VAR_REF(msg).msg.lb.backlog = backlog;
#endif /* TCP_LISTEN_BACKLOG */
    err = netconn_apimsg(lwip_netconn_do_listen, &API_MSG_VAR_REF(msg));
    API_MSG_VAR_FREE(msg);
    return err;
#else /* LWIP_TCP */
    LWIP_UNUSED_ARG(conn);
    LWIP_UNUSED_ARG(backlog);
    return ERR_ARG;
#endif /* LWIP_TCP */
}

03、netconn_accept ()

netconn_accept(代码较长,这里不进行粘贴)用于TCP服务器中,等待着远端主机的连接请求,并且建立一个新的TCP连接,在调用这个函数之前需要通过调用 listen()函数让服务器进入监听状态。accept()函数的调用会阻塞应用线程直至与远程主机建立TCP连接。参数addr是一个返回结果参数,它的值由accept()函数设置,其实就是远程主机的地址与端口号等信息,当新的连接已经建立后,远端主机的信息将保存在连接句柄中,能够标识连接对象。

了解了以上3个API,我们开始创建Server工程:

static void server(void* thread_param)
{
    struct netconn* conn, *newconn;
    err_t err;
    LWIP_UNUSED_ARG(arg);

#if LWIP_IPV6
    conn = netconn_new(NETCONN_TCP_IPV6);
    netconn_bind(conn, IP6_ADDR_ANY, LOCAL_PORT);         
#else /* LWIP_IPV6 */
    conn = netconn_new(NETCONN_TCP);               //①
    netconn_bind(conn, IP_ADDR_ANY, LOCAL_PORT);      //②
#endif /* LWIP_IPV6 */
    LWIP_ERROR("tcpecho: invalid conn", (conn != NULL), return;);

    printf("The local port number is%d\n\n", LOCAL_PORT);
    netconn_listen(conn);                              //③
    while (1) {
        err = netconn_accept(conn, &newconn);         //④
        if (err == ERR_OK) {
            struct netbuf* buf;
            void* data;
            u16_t len;

            while ((err = netconn_recv(newconn, &buf)) == ERR_OK) {    //⑤
                do {
                    netbuf_data(buf, &data, &len);
                    err = netconn_write(newconn, data, len, NETCONN_COPY);  //⑥
                } while (netbuf_next(buf) >= 0);
                netbuf_delete(buf);                 //⑦
            }
            netconn_close(newconn);         //⑧
            netconn_delete(newconn);        //⑨
        }
    }
}

1、申请一个连接结构,指定参数是NETCONN_TCP,即TCP连接

2、绑定本地的IP地址与端口号

3、使TCP服务器进入监听状态

4、处理客户端的连接请求,当只有当有客户端发送连接请求的时候才会处理,否则将进入阻塞态,而客户端的信息保存在newconn连接结构中

5、接收数据,并装填进buf

6、对接收的数据进行转发(指定为不拷贝方式NETCONN_COPY)

7、释放数据空间

8、主动关闭客户端的连接

9、释放newconn空间

到这里已经完成了工程的创建,看一下PC的IP地址,设备需要处于同一网段,以方便测试。

打开命令行窗口输入:ipconfig

“基于MM32F3270

PC的地址为:192.168.105.34,在sys_arch.h文件中对DEST_IP_ADDR0 、DEST_IP_ADDR1、DEST_IP_ADDR2、DEST_IP_ADDR3进行修改,DEST_PORT 随意修改。

#define LOCAL_PORT                 2021

#define IP_ADDR0                   192
#define IP_ADDR1                   168
#define IP_ADDR2                   105
#define IP_ADDR3                   21

将程序下载入开发板中,使用NetAssist进行如下设置:

1)协议设置,此时设备为Server,则PC为Client

2)设置远程主机地址(即设备地址)

3)端口号

“基于MM32F3270

点击连接,若提示连接失败,则Ping一下开发板地址,可以正常Ping通则检查端口号;如果无法Ping通则需要对工程进行检查。

“基于MM32F3270

任意输入字符进行发送。

“基于MM32F3270

通过上图可以观察到发送成功,并且设备返回数据与发送数据一致,表明实验成功。实验程序请登录我们的官网(https://www.mindmotion.com.cn/download.aspx?cid=2542&page=2)下载MM32F3270 SDK,工程路径如下:~\MM32F3270_Lib_Samples_V0.90\Demo_app\Ethernet_Demo\ETH_RTOS\Freertos_Server。

来源:灵动MM32MCU
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 45

UDP(User Datagram Protocol用户数据报协议)是一种无连接、不可靠的协议。UDP协议没有流控制,没有应答确认机制,不能解决丢包、重发、错序问题。它只是简单地实现从一端主机到另一端主机的数据传输功能,数据通过IP层发送,在网络中传输,到达目标主机的顺序是无法预知的,因此需要应用程序对这些数据进行排序处理。在发送端,UDP只是把上层应用的数据封装到UDP报文中;在差错检测方面,仅仅是对数据进行了简单的校验,然后将其封装到IP数据报中发送出去。在接收端,无论是否收到数据,都不会产生一个应答返回送给源主机。如果接收到数据校验错误,接收端丢弃该UDP报文,也不会告诉源主机,这样数据是无法保障其准确性的。但是在如今的网络环境下,UDP协议传输出现错误的概率很小,并且它的实时性是非常好,常用于实时视频的传输,比如直播、网络电话等,即使是出现了数据丢失的情况,导致视频卡帧,也不会产生大的影响。UDP被广泛应用于与对传输速度有要求,并且可以容忍出现差错的数据传输中。

对于UDP通信来说,可以没有服务器,服务器只是一种规定。主动发送的一方为客户端,被动接受的一方为服务器(下图为通信过程,程序也是按照这个步骤进行设计)。

“基于MM32F3270以太网

实验使用MB-039开发板,在工程中使用LwIP+FreeRTOS,实验展示如何实现UDP数据收发,实验使用到的硬件如下:

“基于MM32F3270以太网

如图是MB-039(完整原理图可以通过MM32官网下载)的ETH部分接口电路。

各个信号引脚对应如下:

“基于MM32F3270以太网

“基于MM32F3270以太网

实验用到的API前面几节已经进行了讲解,我们直接创建UDP工程:

static void udpecho_thread(void* arg)
{
    struct netconn* conn;
    struct netbuf* buf;
    char buffer[4096];
    err_t err;
    LWIP_UNUSED_ARG(arg);

    conn = netconn_new(NETCONN_UDP);        // (1)
    netconn_bind(conn, IP_ADDR_ANY, LOCAL_PORT);   // (2)

    while (1) {
        err = netconn_recv(conn, &buf);          // (3)
        if (err == ERR_OK) {
            if(netbuf_copy(buf, buffer, sizeof(buffer)) != buf->p->tot_len) {   // (4)
                LWIP_DEBUGF(LWIP_DBG_ON, ("copy failed\n"));
            }
            else {
                buffer[buf->p->tot_len] = '\0';
                err = netconn_send(conn, buf);                 // (5)
                if(err != ERR_OK) {
                   LWIP_DEBUGF(LWIP_DBG_ON, ("netconn_send failed: %d\n", (int)err));
                }
                else {
                    LWIP_DEBUGF(LWIP_DBG_ON, ("got %s\n", buffer));
                }
            }
            netbuf_delete(buf);
        }
    }
}

1)创建一个UDP类型的连接结构

2)绑定IP地址与端口

3)接收客户端发送的数据(值得指出的是UDP可以一对一,也可以一对多以广播的形式发送)

4)拷贝出接收的数据

5)将数据发送给目标主机

到这里已经完成了工程的创建,看一下PC的IP地址,设备需要处于同一网段,以方便测试。打开命令行窗口输入:ipconfig

“基于MM32F3270以太网

PC的地址为:192.168.105.34,在sys_arch.h文件中对DEST_IP_ADDR0 、DEST_IP_ADDR1、DEST_IP_ADDR2、DEST_IP_ADDR3进行修改,DEST_PORT 随意修改。

#define LOCAL_PORT                 2021

#define IP_ADDR0                    192
#define IP_ADDR1                    168
#define IP_ADDR2                    105
#define IP_ADDR3                    35

将程序下载入开发板中,使用NetAssist进行如下设置:

1)协议设置,选择UDP

2)本机主机地址(即PC地址)

3)端口号

4)设置远程主机地址(即设备地址)

“基于MM32F3270以太网

点击打开,在进行这一步前可以则Ping一下开发板地址,可以正常Ping通则检查端口号;如果无法Ping通则需要对工程进行检查。任意输入字符进行发送。

“基于MM32F3270以太网

可以发现对接收到的数据进行了正确的转发,我们也可以使用Wireshark进行监视抓取整个过程,选择IP过滤:ip.addr==192.168.105.35。

“基于MM32F3270以太网

通过上图表明实验成功。

实验程序请登录我们的官网(https://www.mindmotion.com.cn/download.aspx?cid=2542&page=2)下载MM32F3270 SDK,工程路径如下:

~\MM32F3270_Lib_Samples_V0.90\Demo_app\Ethernet_Demo\ETH_RTOS\Freertos_UDP

来源:灵动MM32MCU
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 40

上一节我们对TCP的报文和连接过程做了介绍,本节通过Socket的方式对整个通信过程再次进行一次整理(使用Socket方式易于加深对以太网通信过程的理解,在此讲解使用的是完整版Socket)。

“基于MM32F3270

服务器端初始化

1)调用socket,向内核申请一个套接字sock

2)调用bind将sock与服务器端的IP与PORT绑定

3)调用listen将套接字设为监听模式,准备接收客户端连接请求

4)调用accept等待并接收客户端的连接请求,建立好TCP连接后,该函数会返回一个新的已连接套接字connected

创建连接

1)客户端调用socket创建套接字

2)调用connect,向服务器发送连接请求

3)connect会发送一个请求SYN段并阻塞等待服务器应答(第一次握手)

4)服务器收到SYN,会给客户端发送一个确认应答ACK,同时发送一个请求(SYN)建立连接(第二次握手)

5)客户端收到服务器发的SYN+ACK段,表明客户端连接已建立成功,进入已连接状态。客户端再向服务器发送一个ACK段,服务器收到后则服务器连接成功

数据传输

1)服务器端使用accept连接建立成功后(通信双方可同时写数据,支持全双工),调用read开始读数据,若没有数据则阻塞等待

2)客户端调用write向服务器发送数据请求,客户端收到之后调用read处理请求,此过程服务器调用read阻塞等待

3)服务器调用write将处理好的请求发送给客户端,再次调用read等待下一个请求

4)服务器收到SYN,会给客户端发送一个确认应答ACK,同时发送一个请求(SYN)建立连接(第二次握手)

4)客户端收到后从read返回,发送下一条请求,如此循环下去

断开连接

1)没有数据,则客户端调用close关闭连接,给服务器发送一个断开连接请求FIN段(第一次握手)

2)服务器收到客户端的FIN段,给客户端发送一个确认应答ACK段,表明同意断开连接。客户端收到ACK段并调用read返回0,表明客户端连接已经断开(第二次握手)

3)read返回0后,服务器知道客户端已经断开连接,它也调用close关闭连接,给客户端发送一个断开连接请求FIN段(第三次握手)

4)客户端收到服务器发送的FIN段,就给服务器一个确认应答ACK段,表明同意断开连接。客户端进入TIME_WAIT状态,服务器收到客户端的ACK段后也断开连接

“基于MM32F3270

实验使用MB-039开发板,在应用工程中使用LwIP+FreeRTOS,实验展示如何制作一个TCP Server_socket,并收发数据,实验使用到的硬件如下:

“基于MM32F3270

如图是MB-039(完整原理图可以通过MM32官网下载)的Ethermac部分。

各个信号引脚对应如下:

“基于MM32F3270

“基于MM32F3270

Server_socket实验用到的API大部分在前面已经进行讲解(只是对NETCONN接口编辑方式进行二次封装),本节只介绍一个比较关键的API:setsockopt(s,level,optname,opval,optlen)。

从名称中就可以看出函数功能用于设置套接字的一些选项,我们关注一下参数:

(1)level有多个常用的选项

SOL_SOCKET:表示在Socket层
IPPROTO_TCP:表示在TCP层
IPPROTO_IP:表示在IP层

(2)optname 表示该层的具体选项名称

level为SOL_SOCKET时,有以下选项:SO_REUSEADDR(允许重用本地地址和端口)、

SO_SNDTIMEO(设置发送数据超时时间)、SO_SNDTIMEO(设置接收数据超时时间)、SO_RCVBUF(设置发送数据缓冲区大小)等。

level为IPPROTO_TCP时,有以下选项:TCP_NODELAY(不使用Nagle算法)、TCP_KEEPALIVE(设置TCP保活时间)等。

level为IPPROTO_IP选项,有以下选项:IP_TTL(设置生存时间)、IP_TOS(设置服务类型)等。

实现Server_socket函数:

static void server_socket(void* thread_param)
{ 
    int sock = -1, connected;
    char* recv_data;
    struct sockaddr_in server_addr, client_addr;
    socklen_t sin_size;
    int recv_data_len;

    printf("The local port number is%d\n\n", LOCAL_PORT);
    recv_data = (char*)pvPortMalloc(RECV_DATA);
    if (recv_data == NULL) {
        printf("No memory\n");
        goto __exit;
    }
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        printf("Socket error\n");
        goto __exit;
    }
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(LOCAL_PORT);
    memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
    if (bind(sock, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)) == -1) {
        printf("Unable to bind\n");
        goto __exit;
    }
    if (listen(sock, 5) == -1) {                         // (1)
        printf("Listen error\n");
        goto __exit;
    }
    while(1) {
        sin_size = sizeof(struct sockaddr_in);
        connected = accept(sock, (struct sockaddr*)&client_addr, &sin_size);  // (2)
        printf("new client connected from (%s, %d)\n",
               inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
        {
            int flag = 1;
            setsockopt(connected,
                       IPPROTO_TCP,     /* set option at TCP level */
                       TCP_NODELAY,     /* name of option */
                       (void*) &flag,   /* the cast is historical cruft */
                       sizeof(int));    /* length of option value */      // (3)
        }
        while(1) {
            recv_data_len = recv(connected, recv_data, RECV_DATA, 0);    // (4)
            if (recv_data_len <= 0)
                break;
            printf("recv %d len data\n", recv_data_len);
            write(connected, recv_data, recv_data_len);   // (5)
        }
        if (connected >= 0)
            closesocket(connected);                  //  (6)
        connected = -1;
    }
__exit:
    if (sock >= 0) closesocket(sock);
    if (recv_data) free(recv_data);
}

1)进入监听状态

2)阻塞应用线程直至与远端主机建立TCP连接,建立成功后远程主机的信息将保持在连接句柄中(connected)

3)对套接字connected进行设置:在TCP层,不使用Nagle算法

4)处理客户端的连接请求,接收远程主机信息

5)将接收的数据进行转发

6)主动关闭客户端的连接

到这里已经完成了Server_socket函数的创建,看一下PC的IP地址,设备需要处于同一网段方便测试。打开命令行窗口输入:ipconfig

“基于MM32F3270

PC的地址为:192.168.105.34,在sys_arch.h文件中对DEST_IP_ADDR0 、DEST_IP_ADDR1、DEST_IP_ADDR2、DEST_IP_ADDR3进行修改,DEST_PORT 随意修改。

#define LOCAL_PORT                 2021

#define IP_ADDR0                    192
#define IP_ADDR1                    168
#define IP_ADDR2                    105
#define IP_ADDR3                    26

将程序下载入开发板中,使用NetAssist进行如下设置:

1)协议设置,此时设备为Server,则PC为Client

2)设置远程主机地址(即设备地址)

3)端口号

“基于MM32F3270

点击连接,若提示连接失败,则Ping一下开发板地址,可以正常Ping通则检查端口号;如果无法Ping通则需要对工程进行检查。

“基于MM32F3270

任意输入字符进行发送。

“基于MM32F3270

通过上图可以观察到发送成功,并且设备返回数据与发送数据一致,表明实验成功。

实验程序请登录我们的官网(http://www.mindmotion.com.cn/download.aspx?cid=2542&page=2)下载MM32F3270 SDK,工程路径如下:

~\MM32F3270_Lib_Samples_V0.90\Demo_app\Ethernet_Demo\ETH_RTOS\Freertos_Server_socket

来源:灵动MM32MCU
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 18

页面

订阅 RSS - 以太网