一、ETH简介
STM32F4xx 以太网外设集成在系列控制器内,实际上是通过 DMA 介质访问控制器(MAC),它的功能是实现 MAC 层的任务。借助以太网外设,STM32F4xx 可通过控制器 ETH 外设按照 IEEE 802.3-2002 发送和接收标准 MAC 数据包。ETH 内部专用 DMA 控制器用于 MAC,ETH 支持两个工业标准接口介质的独立接口(MII)简化介质独立接口(RMII)用于与外部 PHY 芯片连接。MII 和 RMII 接口用于 MAC 数据包传输,ETH 还集成了站管接口(SMI)接口专门用于外部 PHY 通信,用于访问 PHY 芯片寄存器。
物理层定义了以太网使用的传输介质、传输速度、数据编码方法和冲突检测机制,PHY 芯片是实现物理层功能的实体,是生活中常用的水晶头网线 水晶头插座 PHY 组合构成物理层。
ETH 有专用的 DMA 控制器,它通过 AHB 主接口与内核和存储器相连,AHB 控制数据传输的主接口, AHB 接口用于访问控制和状态寄存器(CSR)空间。在发送数据时,首先有数据存储器 DMA 传输到发送 TX FIFO 缓冲,然后由 MAC 内核发送;接收数据时,RX FIFO 先接收以太网数据帧,再通过 DMA 传输到存储器。ETH 见下图。
二、LwIP简介
,瑞士计算机科学院 Adam Dunkels开发适用于嵌入式领域的开源轻量级 TCP/IP 协议栈。它可以移植到包含操作系统的平台上,也可以在没有操作系统的平台下运行。因为它开源占用 RAM 和 ROM 较少,支持相对完整 TCP/IP 协议,切割调试非常方便,广泛应用于中低端 32 位控平台。
针对 LwIP 应用程序开发了测试平台之一 STM32F4x7 由系列控制器运行 (文件编号为:STSW-STM32070)。
- LwIP官网:http://savannah.nongnu.org/projects/lwip/
2.1 DHCP
它通常用于大型局域网环境,主要用于集中管理和分配IP网络环境中的主机动态获取地址IP地址、Gateway地址、DNS服务器地址等信息可以提高地址的利用率。
DHCP协议采用客户端/服务器模型,主机地址的动态分配任务由网络主机驱动。DHCP当服务器收到网络主机申请地址的信息时,将相关地址配置等信息发送给网络主机,实现网络主机地址信息的动态配置。DHCP具有以下功能:
- 保证任何IP地址只能在同一时刻由一个地址组成DHCP使用客户机。
- DHCP永久固定应能够分配给用户IP地址。
- DHCP应该可以用其他方法获得IP地址主机共存(如手动配置)IP地址主机)。
- DHCP服务器应该是现有的BOOTP客户端提供服务。
DHCP分配机制有三种IP地址:
- ,DHCP服务器为主机指定永久性IP地址,一旦DHCP第一次从客户端成功DHCP租用服务器端IP地址可以永久使用。
- ,DHCP服务器指定主机有时间限制IP当地址到期或主机明确表示放弃时,其他主机可以使用地址。
- ,客户端的IP网络管理员指定地址,DHCP服务器只指定IP地址告诉客户端主机。
只有动态分配才能重复使用客户端不再需要的地址。
DHCP消息的格式是基于BOOTP(Bootstrap Protocol)新闻格式要求设备有BOOTP中继代理的功能可以与BOOTP客户端和DHCP实现服务器交互。BOOTP中继代理的功能,使得没有必要在每个物理网络都部署一个DHCP服务器。RFC 951和RFC 1542对BOOTP详细描述了协议。
三、LAN8720A简介
LAN8720A 是 SMSC 公司(已被 Microchip 公司收购)设计体积小,功耗低,全能 10/100Mbps 以太网物理层(PHY)收发器。LAN8720A 总共只有 24Pin,
仅支持 RMII 接口
。由其组成的网络结构见下图 LAN8720A 通过 RMII 与 MAC 连接。RJ45 是网络插座,在和 LAN8720A 连接之间还需要变压器,因此通常使用带电压转换和 LED 指示灯的 HY911105A 型号插座。一般来说,
必须为使用 RMII 接口的 PHY 提供 50MHz 时钟源输入 REF_CLK 引脚
,不过LAN8720A 内部集成 PLL,可以将 25MHz 时钟源陪频到 50MHz 并在指定的引脚上输出时钟,因此我们可以直接与之匹配 REF_CLK 提供连接 50MHz 时钟的效果
。
-
LAN8720A 可以通过 PHYAD0 配置引脚和引脚 RXER 引脚复用,芯片内部有下拉电阻,硬复位后, LAN8720A 将引脚电平读取为器件 SMI 地址,下拉电阻(浮空也可以,因为芯片内部有下拉电阻),设置 SMI 地址为 0,当外接上拉电阻后,可以设置为 1。
-
nINT/REFCLKO 引脚用于 RMII 接口中 REF_CLK 信号线
当 nINTSEL 低电平时引脚
,也可设置为 50MHz 时钟输出可以直接与 STM32F4xx 的 REF_CLK 为其提供引脚连接 50MHz 这种模式需要时钟源为 XTAL1 与 XTAL2 之间或为 XTAL1/CLKIN 提供 25MHz 时钟
,由 LAN8720A 内部 PLL 获得电路陪频 50MHz 时钟,此时 nIN/REFCLKO 不能使用引脚的中断功能。 50MHz 时钟输出。当 nINTSEL 高电平时引脚
,LAN8720A 设置为时钟输入,即直接提供外部时钟源 50MHz 时钟
接入 STM32F4xx 的 REF_CLK 引脚和 LAN8720A 的 XTAL1/CLKIN 引脚,此时 nINT/REFCLKO 可用于中断功能
。
- 如果没有外部晶体振动,需要通过板MCO1或者MCO通过分频倍频操作输出50Mhz驱动网口。
四、引脚分布
ETH 相关硬件在 STM32F4xx 控制器分布如下:
接口 | ETH | GPIO |
---|---|---|
MII | MII_TX_CLK | PC3 |
MII_TXD0 | PB12/PG13 | |
MII_TXD1 | PB13/PG14 | |
MII_TXD2 | PC2 | |
MII_TXD3 | PB8/PE2 | |
MII_TX_EN | PB11/PG11 | |
MII_RX_CLK | PA1 | |
MII_RXD0 | PC4 | |
MII_RXD1 | PC5 | |
MII_RXD2 | PB0 | |
MII_RXD3 | PB1 | |
MII_RX_ER | PB10 | |
MII_RX_DV | PA7 | |
MII_CRS | PA0 | |
MII_COL | PA3 | |
RMII | RMII_TXD0 | PB12/PG13 |
RMII_TXD1 | PB13/PG14 | |
RMII_TX_EN | PG11 | |
RMII_RXD0 | PC4 | |
RMII_RXD1 | PC5 | |
RMII_CRS_DV | PA7 | |
RMII_REF_CLK | PA1 | |
SMI | MDIO | PA1 |
MDC | PC1 | |
其他 | PPS_OUT | PB5/PG8 |
五、新建工程
RCC 设置,选择 HSE(外部高速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器) 选择 Clock Configuration,配置系统时钟 SYSCLK 为 168MHz 修改 HCLK 的值为 168 后,输入回车,软件会自动修改所有配置
SYS 设置,选择 Debug 为 Serial Wire
六、ETH
6.1 参数配置
在 Connectivity
中选择 ETH
设置,模式选择 RMII
使用简化版MII(介质独立接口)。
- Medium Independent Interface(介质独立接口),用于连接介质访问控制层(MAC)子层和物理层(PHY)之间的标准以太网接口,提供数据传输路径。由于 MII
需要多达16根信号线
,由此产生的 I/O 口需求及功耗较大。对于 MII 接口,一般是外部为 PHY 提供 25MHz 时钟源
,再由 PHY 提供 TX_CLK 和 RX_CLK 时钟,不需要与 MAC 层时钟一致。
- Reduced Medium Independent Interface,RMII 接口是 MII 接口的简化版本,MII 需要 16 根通信线,RMII
只需 7 根通信
,在功能上是相同的。对于 RMII 接口,一般需要外部直接提供 50MHz 时钟源
,同时接入 MAC 和 PHY。需与MAC层时钟一致,通常从 MAC 层获取该时钟源。现在一般都用RMII模式。
在 Parameter Settings
进行具体参数配置。
- 选择
Enabled
,一般选择使能自适应功能,系统会自动寻找最优工作方式,包括选择 10MBit/s 或者 100MBit/s 的以太网速度以及全双工模式或半双工模式。LAN8720A支持自适应功能
- 可选 10MBit/s 或 100MBit/s,它设定 ETH_MACCR 寄存器的 FES 位的值,
一般设置 100MBit/s,但在使能自适应功能之后该位设置无效
。 - 可选全双工模式或半双工模式,它设定 ETH_MACCR 寄存器 DM 位的值。
一般选择全双工模式,在使能了自适应功能后该成员设置无效
。
- 默认即可
-
0
注意:LAN8720A 可以通过 PHYAD0 引脚(如PHY芯片引脚10)来配置,该引脚与 RXER 引脚复用,芯片内部自带下拉电阻,当硬复位结束后, LAN8720A 会读取该引脚电平,作为器件的 SMI 地址,。
本硬件 RXER 引脚浮空,其 PHY 芯片地址为 0
。
- 选择
Polling Mode
轮询方法。ST 官方例程文件包含了中断引脚的相关配置,主要用于指示接收到以太网帧,我们这里不需要使用。 - 选择
By hardware
使能发送数据硬件校验和。这个需要硬件支持,STM32F4xx 控制器是支持的
。
在 Advanced Parameters
进行高级参数配置。
- 选择
user PHY
,因为没有我的 PHY 芯片型号 LAN8720A - 可改为 PHY 芯片型号 LAN8720A
- 按照芯片手册填写,
0x1F
- 按照芯片手册填写,
0x0004
- 按照芯片手册填写,
0x0010
6.2 引脚配置
GPIO 设置,在右边图中找到 ETH 对应引脚,将引脚配置成跟原理图上的一致
七、LwIP
7.1 参数配置
在 Middleware
中选择 LWIP
设置,勾选 Enabled
使能协议栈。
在 General Settings
进行通用参数配置。
- 选择
Enabled
。如果使用开发板连接带 DHCP 服务功能的路由器,可以使能。否则开发板直接连接电脑,电脑是没办法提供 DHCP 服务功能的。
- 选择
Enabled
。主要用于网络的调试与维护,ping 的时候用。 - 选择
Disabled
。可以实现多播数据的接收。 - 选择
Disabled
。通过域名解析用户就可以在知道服务器域名的情况下,获得该服务器的 IP 地址。 - 选择
Enabled
。看需求,一般选择用 TCP 协议。 - UDP协议控制块数量,决定 UDP 协议控制块需要的 POOL 资源。
- 选择
Enabled
。 - 同时活动的TCP连接数。
在 Key Options
进行关键选项配置。
-
OS Not Used
表示无操作系统模拟层,这个宏非常重要,因为无操作系统与有操作系统的移植和编写是完全不一样的,我们现在是无操作系统移植。
- 默认
Enabled
。使用 LwIP 提供的定时器,用于超时机制。
- 默认
Disabled
。平台锁,保护关键区域内缓存的分配与释放。
- 默认
1600 Byte(s)
。堆内存的大小。如果应用程序将发送很多需要复制的数据应该设置得大一点。
- 默认
16
。memp 结构的 pbuf 数量,如果应用从 ROM 或者静态存储区发送大量数据时,这个值应该设置大一点。 - 默认
4
。 原始连接(就是应用程不经过传输层直接到IP层获取数据)PCB 的数目,该项依赖 LWIP_RAW 项的开启。 - 默认
8
。 同时建立激活的 TCP 连接的数目(要求参数 LWIP_TCP 使能)。 - 默认
16
。 最多同时在队列的 TCP_SEG 的数目。
- 默认
16
。 内存池大小。 - 默认
592 Byte(s)
。 每个 pbuf 内存池大小。
- 选择
Enabled
。 地址解析协议,通过目标设备的 IP 地址,查询目标设备的 MAC 地址,以保证通信的 顺利进行。
- 默认
255 Node(s)
。TCP TTL时间。 - 默认
2144 Byte(s)
。TCP 窗口长度。 - 默认
Enabled
。TCP队列到达顺序。如果设备内存不足,则定义为0。 - 默认
536 Byte(s)
。最大 TCP 报文段,TCP_MSS = MTU - IP 报头大小 - TCP 报头大小。 - 默认
1072 Byte(s)
。TCP 发送缓冲区大小(字节)。 - 默认
1072 Byte(s)
。TCP 发送缓冲区队列的最大长度。
- 默认
Disabled
。当 netif 状态设置为 up 或 down 时调用此函数。 - 默认
Enabled
。当 netif 链接设置为 up 或 down 时,将调用此函数。
- 默认
Disabled
。支持发送数据包的目的地 IP。
- 默认
Disabled
。Socket API。
八、生成代码
输入项目名和项目路径 选择应用的 IDE 开发环境 MDK-ARM V5 每个外设生成独立的 ’.c/.h’
文件 不勾:所有初始化代码都生成在 main.c 勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。 点击 GENERATE CODE 生成代码
九、修改main.c
在 main()
的死循环中添加 MX_LWIP_Process()
函数。
然后加入以下代码不断获取 DHCP 动态分配到的 IP 地址。
extern struct netif gnetif;
struct dhcp *dhcp;
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_LWIP_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("lwip test\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
MX_LWIP_Process();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
dhcp = netif_dhcp_data(&gnetif);
printf("DHCP IP address: %s\n", ip4addr_ntoa(&dhcp->offered_ip_addr));
printf("DHCP Subnet mask: %s\n", ip4addr_ntoa(&dhcp->offered_sn_mask));
printf("DHCP Default gateway: %s\n", ip4addr_ntoa(&dhcp->offered_gw_addr));
HAL_Delay(1000);
}
/* USER CODE END 3 */
}
查看效果: 使用电脑ping上面的ip:
十、工程代码
链接:https://pan.baidu.com/s/19zJHKqO3ghuhNWwKnAP–Q?pwd=ukl8 提取码:ukl8
十一、相关API说明
11.1 MX_LWIP_Init
按顺序执行了:
- 网络接口的添加
netif_add()
- 初始化底层
ethernetif_init()
- DHCP
dhcp_start()
然后LwIP就可以用了。
收包用的是调用 low_level_input
把数据包接回来,给 netif->input
处理。 发包则是由 netif->output
交由 etharp_output
制作数据包,调用 low_level_output
发出去。
void MX_LWIP_Init(void)
{
/* Initilialize the LwIP stack without RTOS */
lwip_init();
/* IP addresses initialization with DHCP (IPv4) */
ipaddr.addr = 0;
netmask.addr = 0;
gw.addr = 0;
/* add the network interface (IPv4/IPv6) without RTOS */
// 如果有多个接口则需多次调用
// 需要提供一个init函数指针,这个指针指向我们自己的硬件接口初始化函数,一般来说就是ethernetif.c中的ethernetif_init()
netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input);
/* Registers the default network interface */
// 将网络接口设置为默认的网络接口
netif_set_default(&gnetif);
// 查看是否有链接
if (netif_is_link_up(&gnetif))
{
/* When the netif is fully configured this function must be called */
// 使能网络接口
netif_set_up(&gnetif);
}
else
{
/* When the netif link is down this function must be called */
// 关闭网络接口
netif_set_down(&gnetif);
}
/* Set the link callback function, this function is called on change of link status*/
netif_set_link_callback(&gnetif, ethernetif_update_config);
/* Create the Ethernet link handler thread */
/* Start DHCP negotiation for a network interface (IPv4) */
dhcp_start(&gnetif);
/* USER CODE BEGIN 3 */
/* USER CODE END 3 */
}
11.2 MX_LWIP_Process
不断地接收来自接口的信息,并检查是否延时
/** * ---------------------------------------------------------------------- * Function given to help user to continue LwIP Initialization * Up to user to complete or change this function ... * Up to user to call this function in main.c in while (1) of main(void) *----------------------------------------------------------------------- * Read a received packet from the Ethernet buffers * Send it to the lwIP stack for handling * Handle timeouts if LWIP_TIMERS is set and without RTOS * Handle the llink status if LWIP_NETIF_LINK_CALLBACK is set and without RTOS */
void MX_LWIP_Process(void)
{
/* USER CODE BEGIN 4_1 */
/* USER CODE END 4_1 */
ethernetif_input(&gnetif);
/* USER CODE BEGIN 4_2 */
/* USER CODE END 4_2 */
/* Handle timeouts */
sys_check_timeouts();
/* USER CODE BEGIN 4_3 */
/* USER CODE END 4_3 */
}
十二、注意事项
• 由 Leung 写于 2022 年 7 月 22 日
• 参考:从零开始Cubemx配置STM32搭载freeRTOS以及lwip实现tcp网络通信(二) STM32cubeMX配置LWIP+FREERTOS LwIP的配置