ETH LAN8720 调试记录
本节我们开始调试网络功能。 主要分三部分:STM32以太网控制器、PHY芯片LAN8720、LWIP协议栈。
STM32 MAC控制器
框图
1. STM32的以太网是基于DMA控制器的。 2. 介质访问控制,也就是我们通常说的MAC控制器。这个是以太网功能的核心部分。 3. 以太网提供3种接口:SMI、MII、RMII。 4. SMI叫做站管理接口。用来访问PHY的寄存器。 5. MII&RMII功能一样,都是MAC控制器跟PHY进行数据传输的接口。RMII是精简的MII,用更少的IO口。 6. 框图里面的外部PHY并不包含在STM32芯片内,我们外部的LAN8720就是这个外部PHY。 7. 在PHY外,应该还有一个带变压器的网口。
特性
在《STM32F4xx中文参考手册.pdf》中列出了以太网的3种特性 1. MAC内核特性 2. DMA特性 3. PTP特性
细节请看文档。
RMII接口
精简介质独立接口(RMII)只要用7个引脚(MII需要16个),因此我们选用这个接口控制PHY芯片。 RMII有以下特性:
支持10/100M运行速率 参考时钟必须是50MHz 相同的参考时钟必须从外部提供给MAC和外部PHY 提供独立的2位宽的发送和接收数据路径
图中的REF_CLK是共用的参考时钟,50MHz。 我们用LAN8720方案,这个时钟由LAN8720提供给STM32 MAC控制器。
LAN8720A芯片
LAN8720A是低功耗的10/100M以太网PHY芯片,支持通过RMII接口和MAC层通信。
特性
- 10/100M
- 支持RMII接口
- 支持全双工和半双工
- 使用外部25M晶振,生成50MHz参考时钟给MAC层使用
- 支持自协商模式
- 支持HP Auto-MDIX自动翻转
- 支持SMI串行管理接口
框图
- 内部框图
- 应用图
左边10/100M网络控制器就是STM32内部的MAC控制器。 右边的RJ45就是网口。 下边框图说明LAN8720需要一个外部晶振。
更多请参考《LAN8720A.pdf》文档。
LWIP
现在我们天天上网,基本上都知道,有一种TCP/IP协议。 协议是什么?最底层的协议就是数据传输的格式。可以相当于网络上的一种语言。 高级的协议,就是一种行为规范。 以太网世界设备几十亿,如果没有行为规范,机器之间就无法正常进行通信。 不理解的话可以想象在一间房子里面有10个不同国家的人,各自都在胡言乱语。
网络七层协议
国际标准化组织ISO 于1981年正式推荐了一个网络系统结构----七层参考模型,叫做开放系统互连模型(Open System Interconnection,OSI)。 由于这个标准模型的建立,使得各种计算机网络向它靠拢,大大推动了网络通信的发展。 OSI 参考模型将整个网络通信的功能划分为七个层次,见图。 它们由低到高分别是物理层(PH)、数据链路层(DL)、网络层(N)、传输层(T)、会话层(S)、表示层(P)、应用层(A)。 每层完成一定的功能,每层都直接为其上层提供服务,并且所有层次都互相支持。 第四层到第七层主要负责互操作性,而一层到三层则用于创造两个网络设备间的物理连接。
TCP/IP协议
Transmission Control Protocol/Internet Protocol的简写,中译名为传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成。TCP/IP 定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。协议采用了4层的层级结构,每一层都呼叫它的下一层所提供的协议来完成自己的需求。通俗而言:TCP负责发现传输的问题,一有问题就发出信号,要求重新传输,直到所有数据安全正确地传输到目的地。而IP是给因特网的每一台联网设备规定一个地址。
TCP/IP协议只使用了4层结构,跟OSI的对应关系如下图。
LWIP
LwIP是Light Weight (轻型)IP协议,有无操作系统的支持都可以运行。 LwIP实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用,它只需十几KB的RAM和40K左右的ROM就可以运行,这使LwIP协议栈适合在低端的嵌入式系统中使用。 lwIP协议栈主要关注的是怎么样减少内存的使用和代码的大小,这样就可以让lwIP适用于资源有限的小型平台例如嵌入式系统。 为了简化处理过程和内存要求,lwIP对API进行了裁减,可以不需要复制一些数据。
学习
我们本次只是移植官方以太网的例子。不会对LWIP做深入学习。 因为TCP/IP协议太复杂了。以前公司做无线通信的同事,每天都抗一本书看,对,就是下面这本,他说这书是一套,总共更有3本。 如果大家想学习网络协议,可以买这个书看看。 如果只是想了解LWIP的使用,那就先从例程上学习学习,再看看LWIP的结构跟接口就可以了。 以后我们会单独出一个对于LWIP的使用说明
原理说明
1. 原理图分两部分:PHY芯片LAN8720A、HR911105A(带变压器RJ45网口)。 2. PHY芯片通过RMII接口与STM32内部MAC层通信。 3. LAN8720需要一个25M的晶振。 4. STM32通过一个SMI接口控制LAN8720。 5. 第10脚可以配置LAN8720地址。
移植调试
ST提供了ETH例程《STM32F4x7_ETH_LwIP_V1.1.1》。 在Libraries文件夹内有STM32F4x7_ETH_Driver库文件。 Project文件夹内有两个文件夹,FreeRTOS是带操作系统的例程,Standalone则是不带操作系统的例程。 目前我们还没有移植操作系统,先用不带操作系统的例程测试硬件。 我们选择里面的tcp_echo_server例程。 在app文件夹建立一个eth文件,用于存放网络应用。 把例程中src和inc文件夹内的相关文件拷贝到eth。例程的main.c跟main.h改名eth_app。
-
C文件
-
头文件
-
将文件添加到工程 lwip文件较多,一共34个。 lwip-1.4.1\src\api目录下8个。 lwip-1.4.1\src\core目录下16个。 lwip-1.4.1\src\core\ipv4目录下8个。 lwip-1.4.1\src\netif目录下的etharp.c lwip-1.4.1\port\STM32F4x7\Standalone目录下的ethernetif.c
修改
- 修改ETH_GPIO_Config函数,根据我们的硬件配置GPIO。
- 修改所有DP83848_PHY_ADDRESS,改为ETH_PHY_ADRESS,在stm32f4x7_eth_bsp.h宏定义
//#define DP83848_PHY_ADDRESS 0x01 /* Relative to STM324xG-EVAL Board */
#define LAN8720A_PHY_ADDRESS 0x00 /* Relative to WUJIQUE F407 Board */
#define ETH_PHY_ADRESS LAN8720A_PHY_ADDRESS
- 打开宏,使用DHCP,DHCP就是自动获取IP的意思。
#define USE_DHCP /* enable DHCP, if disabled static address is used */
- 打开接口定义,我们用的是RMII模式,原来例程用的是MII模式
/* wujique F407硬件使用RMII接口*/
#define RMII_MODE // User have to provide the 50 MHz clock by soldering a 50 MHz
// oscillator (ref SM7745HEV-50.0M or equivalent) on the U3
// footprint located under CN3 and also removing jumper on JP5.
// This oscillator is not provided with the board.
// For more details, please refer to STM3240G-EVAL evaluation
// board User manual (UM1461).
//#define MII_MODE
对源码移植过程大概修改上面这些,具体修改了什么,可以跟原来例程对比。
修改系统滴答
网络需要一个Time_Get_LocalTime函数,其实是一个系统滴答。 我们代码中一直使用一个Delay函数,我们修改这个Delay,改为滴答形式。
main函数开始初始化系统滴答,改为1MS
/* SysTick end of count event each 10ms */
RCC_GetClocksFreq(&RCC_Clocks);
SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000);
将原来的延时函数改为下面三个函数: Delay还是延时; Time_Get_LocalTime获取系统滴答; Time_Update放到SysTick_Handler函数内,替换原来的函数。
/* this variable is used to create a time reference incremented by 10ms */
__IO uint32_t LocalTime = 0;
uint32_t timingdelay;
/**
* @brief Inserts a delay time.
* @param nCount: number of 10ms periods to wait for.
* @retval None
*/
void Delay(uint32_t nCount)
{
/* Capture the current local time */
timingdelay = LocalTime + nCount;
/* wait until the desired delay finish */
while(timingdelay > LocalTime)
{
}
}
uint32_t Time_Get_LocalTime(void)
{
return LocalTime;
}
/**
* @brief Updates the system local time
* @param None
* @retval None
*/
void Time_Update(void)
{
LocalTime += SYSTEMTICK_PERIOD_MS;
}
读芯片ID
到现在,大家应该都熟悉外设调试流程了:可以读ID的芯片,肯定是先调试能读取ID,再调试其他功能。 在函数ETH_BSP_Config内增加读ID功能,初始化ETH后就读。
/* Configure the GPIO ports for ethernet pins */
ETH_GPIO_Config();
/* Configure the Ethernet MAC/DMA */
ETH_MACDMA_Config();
uart_printf("read phy id\r\n");
ID1 = ETH_ReadPHYRegister(ETH_PHY_ADRESS, 0X02);
ID2 = ETH_ReadPHYRegister(ETH_PHY_ADRESS, 0X03);
uart_printf("PHY ID:%02x %02x\r\n", ID1, ID2);
修改完之后成功读取ID。
获取IP
接上网线,通过路由器分配IP地址。
hello word! ETH_BSP_Config read phy id PHY ID:07 c0f1 PHY_BSR: 782d phy ETH_LINK_FLAG Looking for
DHCP server
please wait... IP address assigned by a DHCP server
192.168.2.169
成功获取IP地址
通信测试
我们移植的是tcp_echo_server,也就是一个TCP协议自动回显的server。 在函数tcp_echoserver_init();中有以下初始化代码 ```c {.line-numbers} void tcp_echoserver_init(void) { / create new tcp pcb / tcp_echoserver_pcb = tcp_new();
if (tcp_echoserver_pcb != NULL) { err_t err;
/* bind echo_pcb to port 7 (ECHO protocol) */
err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 7);
if (err == ERR_OK)
{
/* start tcp listening for echo_pcb */
tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);
/* initialize LwIP tcp_accept callback function */
tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept);
}
else
{
/* deallocate the pcb */
memp_free(MEMP_TCP_PCB, tcp_echoserver_pcb);
printf("Can not bind pcb\n");
}
} else { printf("Can not create new pcb\n"); } } ``` 其中第11行代码,将tcp绑定到端口7。
我们运行网络调试助手,设置如下图,
协议选择Tcp Client IP地址选择开发板获取到的地址 使用端口7
点击链接 连接成功后点击发送, 开发板会显数据给电脑。 测试成功。 勾上左下角数据流循环发送,点击发送,就可以重复发送,测试是否会出现丢包。
总结
ST提供的例程有多种,大家可以尝试其他例程。