嵌入式STM32通过PHY芯片实现Macraw透传
Posted 菜老越
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了嵌入式STM32通过PHY芯片实现Macraw透传相关的知识,希望对你有一定的参考价值。
目录
前言
单片机接入有线网络(RJ45双绞线、光纤等)时,可使用外接集成了mac、phy、协议层的网络芯片,如CH395、W5500、DM9051等,这些芯片只靠SPI、串口、并口等即可实现单片机接入有线网络。且上述芯片除了TCP、UDP等常规网络协议,还支持Macraw模式。
Macraw模式在一些电力系统中有广泛的应用,因为Macraw模式可以在以太网链路的基础上,任意规定自己的通讯协议,而不必非要使用TCP、UDP等常规网络协议。
Macraw帧格式如下所示,数据域不得超过1500字节,当数据域小于46字节时,将会以0x00填充至46字节,最后的4字节CRC32不用管,因为在上述以太网芯片中,CRC32是自动计算并接入帧尾的,你只需要关注目的MAC、源MAC、类型及数据即可,网络帧的帧头也会在芯片中自动填充。
由我的上一篇博文【嵌入式】嵌入式设备实现网络功能——综述中可知道,使用上述以太网芯片能够最简单的实现单片机接入有线网络,你不需要关注MAC层、PHY层、协议层中的任何内容,只需要阅读芯片手册,会通过SPI等进行命令发送、数据收发即可。
但是,使用集成MAC层的单片机+PHY物理层芯片的方案存在即合理,比如市面上少有以太网芯片内集成的PHY具备光纤模式,同时,通过SPI控制以太网芯片,在数据量较大时也给占用了极大的运行时间。
通过STM32+PHY芯片+LwIP实现接入有线网络的方案网上一抓一大把,然而使用STM32+PHY芯片实现Macraw传输的资料却几乎搜不到。今天我们就来盘一盘,如何利用STM32CubeMx+PHY物理层芯片来实现Macraw的网络透传。
一、硬件平台
整个硬件平台使用手上现成的原子STM32F4探索者开发板,MCU是STM32F407ZET6,PHY芯片为LAN8720A,且板子上已经有了网络变压器以及RJ45接头,基本电路图如下。MCU通过RMII接口与LAN8720A连接。由于LAN8720的2号引脚接地,所以其14号引脚输出50MHz,作为时钟基准驱动自己和STM32的CLK口。
二、STM32的以太网外设
STM32F4系列的以太网外设主要掌握两点即可,一个是MAC配置,另一个是DMA理解。
1.MAC配置
关于MAC层的配置,基本使用STM32外设寄存器的默认值即可。初始阶段我们可能并不需要MAC地址过滤功能,因此我们可以通过配置MAC的MACFFR寄存器位接收所有、混杂模式,从而接收网络线上的所有数据。除了MACFFR寄存器,其他寄存器通常不需要更改,具体内容还需要通读参考手册中MAC寄存器的所有内容。
2.以太网DMA的理解
这是编程过程中比较难理解的一点,因为通常STM32运行时,外设只会在外设寄存器空间内进行数据操作,比如置位、清零等。但在以太网应用中,DMA需要和我们自己程序中开辟的内存进行联合工作。所有的一切都因为STM32的网路外设DMA中使用了一种特殊的模式——描述符模式。
所谓描述符,其实就是一个结构体,其基本形式如下,为hal库中自带的结构体定义:
typedef struct
__IO uint32_t Status; /*!< Status */
uint32_t ControlBufferSize; /*!< Control and Buffer1, Buffer2 lengths */
uint32_t Buffer1Addr; /*!< Buffer1 address pointer */
uint32_t Buffer2NextDescAddr; /*!< Buffer2 or next descriptor address pointer */
/*!< Enhanced ETHERNET DMA PTP Descriptors */
uint32_t ExtendedStatus; /*!< Extended status for PTP receive descriptor */
uint32_t Reserved1; /*!< Reserved */
uint32_t TimeStampLow; /*!< Time Stamp Low value for transmit and receive */
uint32_t TimeStampHigh; /*!< Time Stamp High value for transmit and receive */
ETH_DMADescTypeDef;
我们只需要关注前4个成员变量即可。
1.Status:这个32位的变量保存了当前描述符节点的状态,供DMA和程序件的联合工作;
2.ControlBufferSize:描述符作为发送描述符时,这里存的是待发送的字节数,如果是接收描述符,这里存的是接收到的字节数;
3.Buffer1Addr:作为发送描述符时,这是指向发送缓存的地址,所谓接收描述符时,这是指向接收缓存的地址。当然,无论是接收还是发送,它指向的是RAM中已经开辟好的一块缓存;
4.Buffer2NextDescAddr:因为DMA的描述符结构可能是由多个描述符串起来的链表,所以这里保存的是下一个描述符节点的地址。如果你需要发送的数据没有超过Macraw规定的最大1500字节,那么这个变量将会指向次描述符自己。
DMA配合描述符工作时,主要有以下步骤:
1.在RAM创建描述符链表,这个链表可以是多个,也可以是单个。如果要分别建立十个收发描述符可以这样写:
ETH_DMADescTypeDef txDesc[10];
unsigned char txBuf[10][1500];
ETH_DMADescTypeDef rxDesc[10];
unsigned char rxBuf[10][1500];
开辟好描述符数组,以及每个描述符中将使用的Buffer1Addr指向的收发缓存后,即可调用hal库的HAL_ETH_DMARxDescListInit、HAL_ETH_DMATxDescListInit函数进行描述符的初始化,通过阅读源码发现,其初始化过程即把描述出串成一个链表,并分别为每个节点赋值收发缓存空间,即txBuf、rxBuf中的内容;
2.描述符初始化后,即可通过HAL_ETH_Start函数开启DMA描述符轮询。所谓轮询,即DMA轮询每个描述符,通过status变量判断是否可操作,通过ControlBufferSize变量和Buffer1Addr来将数据通过DMA直接送到RMII发给PHY芯片;或者收到来自PHY的数据后,送到描述符中,通过中断通知程序进行即使读取;
对于描述符的理解大概就是以上内容,具体还要认真阅读参考手册。而且仅凭手册可能不会理解太透,主要还要结合阅读hal库源码来帮助理解。
三、PHY芯片寄存器
PHY芯片也有自己的众多寄存器,但国际标准中规定了每个PHY芯片至少要有一组控制寄存器和一组状态寄存器,分别是寄存器0和寄存器1 ,其他的外扩寄存器以及厂家的特殊寄存器因不同芯片而异。具体要理解这一点,只需要随便找一个PHY芯片的手册,阅读寄存器章节即可。
MCU通过RMII连接PHY芯片后,首先就是要对PHY的控制寄存器进行配置,然后在后续运行过程中通过读取PHY内的寄存器内容,来获取网络的工作状态,起到监视的作用。
四、STM32CubeMX配置及程序编写
STM32的网络外设过于复杂,仅凭上述晦涩的内容很难理解,需要通过反复阅读参考手册,并阅读源码,结合实践才能理解。下面就开始建立工程吧。
1.STM32CubeMX配置
1.使能ETH外设
2.配置ETH外设
因为硬件电路板中将LAN8720的地址引脚悬空,而LAN8720手册中提及此引脚内部带下拉,故地址为0.同时使能自动协商、接收模式改为中断接收、发送帧的帧头和CRC由硬件填充。
3.配置PHY芯片
由于我们使用的不是STM32CubeMX自带的LAN8742,故要对下面的external PHY Configuration进行配置。这个要结合LAN8720手册,其speial control/status register offset为31,即0x1f,通过阅读手册中的speial control/status register,其速度、双工模式屏蔽位为0x4和0x10;
至此配置完成,即可生成工程,进程程序编写。
2.程序编写
1.默认参数修改
对于MAC的默认参数需要做一点小小的修改,即将MACFFR寄存器设置为接受所有、混杂模式,否则有可能收不到数据,因为被MAC过滤给滤除了。
直接在ETH_MACDMAConfig函数中,对以下变量修改赋值为:
macinit.ReceiveAll = ETH_RECEIVEALL_ENABLE;
macinit.PromiscuousMode = ETH_PROMISCUOUS_MODE_ENABLE;
2.PHY初始化
生成的工程在初始化时已经有了MX_ETH_Init()函数,我们先直接下到板子上,看MX_ETH_Init()函数中的HAL_ETH_Init函数的返回值,如果其返回值为HAL_OK,说明你的PHY芯片已经被成功驱动起来了,可以进行下一步了。如果是HAL_TIMEOUT,那么很可能是RJ45口没有接网线,或者网线的另一端没有怼到电脑或者路由器上,从而使得link状态为dowm导致超时。如果是HAL_ERR,那么基本是硬件电路的问题,这时就要观察HAL_ETH_Init里的HAL_ETH_WritePHYRegister、HAL_ETH_ReadPHYRegister是否正常,读回的寄存器值是否是0xffff、0x0000等异常值,基本就是硬件电路问题,或者PHY地址设错。
3.HAL_ETH_Init返回正常后,开辟描述符空间,这里我只用了一个收描述符和一个发描述符。
unsigned char txbuf[256];
ETH_DMADescTypeDef txdesc;
unsigned char rxbuf[256];
ETH_DMADescTypeDef rxdesc;
然后进行描述符初始化
HAL_ETH_DMARxDescListInit(&heth, &rxdesc, rxbuf, 1);
HAL_ETH_DMATxDescListInit(&heth, &txdesc, txbuf, 1);
4.开启MAC、DMA工作
HAL_ETH_Start(&heth);
5.设置发送数据并发送
for (i = 0; i < 256; i++)
txbuf[i] = i;
HAL_ETH_TransmitFrame(&heth, 256)
6.编写接收中断回调
void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth)
if (__HAL_ETH_DMA_GET_FLAG(heth, ETH_DMA_FLAG_R))
heth->RxDesc->Status = ETH_DMARXDESC_OWN;
HAL_ETH_GetReceivedFrame_IT(heth);
五、实验验证
1.发送测试
循环发送256字节的数据,使用wireShark软件进行捕获。可见发送没有问题。
2.接收测试
使用自己用winpcap编写的上位机进行数据发送,发现可以正常进入接收中断,且接收缓存中数据接收无误。
以上是关于嵌入式STM32通过PHY芯片实现Macraw透传的主要内容,如果未能解决你的问题,请参考以下文章
《嵌入式 - Lwip开发指南》第2章 LWIP开发环境简介
《嵌入式 - Lwip开发指南》第2章 LWIP开发环境简介