STM32以太网之站管理接口

        STM32芯片自带以太网模块,该模块包括带专用 DMA 控制器的 MAC 802.3(介质访问控制)。它支持介质独立接口(MII) 和简化介质独立接口 (RMII),并通过一个选择位在两个接口间进行切换(请参见SYSCFG_PMC 寄存器)。自带了一个用于外部 PHY 通信的 SMI 接口,通过一组配置寄存器,用户可以为 MAC 控制器和 DMA 控制器选择所需模式和功能。

DMA 控制器通过 AHB 主从接口与内核和存储器相连。AHB 主接口用于控制数据传输,而AHB 从接口则用于访问“控制和状态寄存器”(CSR) 的空间。

在进行数据发送时,首先将数据由系统存储器以 DMA 的方式送至发送 FIFO (Tx FIFO) 进行缓冲,再通过 MAC 内核发送。同样,接收 FIFO (Rx FIFO) 则存储通过线路接收的以太网帧,直到这些帧通过 DMA 传送到系统存储器,了解收据收发方式,下面看一下STM32以太网功能框图。

        从上图可以看出,STM32是必须外接 PHY 芯片,才可以完成以太网通信的,外部 PHY芯片可以通过 MII/RMII 接口与 STM32F4 内部 MAC 连接,并且支持 SMI(MDIO&MDC)接口配置外部以太网 PHY 芯片。

        注: 当使用以太网时,AHB 时钟频率必须至少为 25 MHz。

站管理接口:SMI

        站管理接口 (SMI) 允许应用程序通过 2 线时钟和数据线访问任意 PHY 寄存器。该接口支持访问多达 32 个 PHY。应用程序可以从 32 个 PHY 中选择一个 PHY,然后从任意 PHY 包含的 32 个寄存器中选择一个寄存器,发送控制数据或接收状态信息。任意给定时间内只能对一个 PHY 中的一个寄存器进行寻址。

        MDC 时钟线和 MDIO 数据线在微控制器中均用作复用功能 I/O:

        MDC:周期性时钟,提供以最大频率 2.5 MHz 传输数据时的参考时序。MDC 的最短高电平时间和最短低电平时间必须均为 160 ns。MDC 的最小周期必须为 400 ns。在空闲状态下,SMI 管理接口将 MDC 时钟信号驱动为低电平。

        MDIO:数据输入/输出比特流,用于通过 MDC 时钟信号向/从 PHY 设备同步传输状态信息。

SMI 帧格式

        表中给出了与读操作或写操作有关的帧结构,位传输顺序必须从左到右。

管理帧包括八个字段:

        报头:每个事务(读取或写入)均可通过报头字段启动,报头字段对应于 MDIO 线上 32个连续的逻辑“1”位以及 MDC 上的 32 个周期。该字段用于与 PHY 设备建立同步。

        起始:帧起始由 <01> 模式定义,用于验证线路从默认逻辑“1”状态变为逻辑“0”状态,然后再从逻辑“0”状态变为逻辑“1”状态。

        操作:定义正在发生的事务(读取或写入)的类型。

        PADDR:PHY 地址有 5 位,可构成 32 个唯一 PHY 地址。最先发送和接收地址的MSB 位。

        RADDR:寄存器地址有 5 位,从而可在所选 PHY 设备中对 32 个不同的寄存器进行寻址。最先发送和接收地址的 MSB 位。

        TA:周转字段在 RADDR 和 DATA 字段间定义了一个 2 位模式,以避免在读取事务期间出现竞争现象。读取事务时,MAC 控制器将 TA 的 2 个位驱动为 MDIO 线上的高阻态。PHY 设备必须将 TA 的第一位驱动为高阻态,将 TA 的第二位驱动为“0”。写入事务时,MAC 控制器针对 TA 字段驱动 <10> 模式。PHY 设备必须将 TA 的 2 个位驱动为高阻态。

        数据:数据字段为 16 位。最先发送和接收的位必须为 ETH_MIID 寄存器的位 15。

        空闲:MDIO 线驱动为高阻态。三态驱动器必须禁止,PHY 的上拉电阻使线路保持逻辑“1”状态。

        根绝上面的图片及说明,我们了解了mac与phy的通讯数据帧格式,接下来我们看一下STM32是怎么定义通讯寄存器及通讯实现方式。

以太网 MAC MII 地址寄存器 (ETH_MACMIIAR)

MII 地址寄存器通过管理接口控制外部 PHY 的管理周期。

        位 31:16 保留,必须保持复位值。

         位 15:11 PA:PHY 地址 (PHY address)该字段指示正在访问 32 个可能的 PHY 器件中的哪一个。

        位 10:6 MR:MII 寄存器 (MII register)这些位在所选 PHY 器件中选择需要的 MII 寄存器。

位 5 保留,必须保持复位值。

        位 4:2 CR:时钟范围 (Clock range)CR 时钟范围选项可确定 HCLK 频率并用于决定 MDC 时钟频率:

选项 HCLK MDC 时钟

        000 60-100 MHz HCLK/42

        001 100-150 MHzHCLK/62

        010 20-35 MHz HCLK/16

        011 35-60 MHz HCLK/26

       100 150-168 MHz HCLK/102

       101、110、111 保留 -

        位 1 MW:MII 写 (MII write),此位置 1 是在告知 PHY,将要启动一个使用 MII 数据寄存器的写操作。如果此位未置 1,则表示会启动一个读操作,将数据放入 MII 数据寄存器。

        位 0 MB:MII 忙碌 (MII busy),向 ETH_MACMIIAR 和 ETH_MACMIIDR 写入前,此位应读取逻辑 0。向 ETH_MACMIIAR写入过程中,此位也必须复位为 0。在 PHY 寄存器访问过程中,此位由应用程序设为 0b1,指示读或写访问正在进行中。在对 PHY 进行写操作过程中,ETH_MACMIIDR(MII 数据)应始终保持有效,直到 MAC 将此位清零。在对 PHY 进行读操作过程中,ETH_MACMIIDR始终无效,直到 MAC 将此位清零。在此位清零后,才可以向 ETH_MACMIIAR(MII 地址)写入。

以太网 MAC MII 数据寄存器 (ETH_MACMIIDR)

        MAC MII 数据寄存器存储要写入 PHY 寄存器的数据,该寄存器地址在 ETH_MACMIIAR 中指定。ETH_MACMIIDR 也将存储从 PHY 寄存器读取的数据,该寄存器地址由 ETH_MACMIIAR指定。

        位 31:16 保留,必须保持复位值。

        位 15:0 MD:MII 数据 (MII data)

        其中包含在某次管理读操作之后从 PHY 中读取的 16 位数据值,或在某次管理写操作之前要写入 PHY 的 16 位数据值。

SMI 时钟选择

        MAC 启动管理写/读操作。SMI 时钟是一个分频时钟,其时钟源为应用时钟(AHB 时钟)。分频系数取决于 MII 地址寄存器中设置的时钟范围。

SMI 写操作

        当应用程序将 MII 写入位和繁忙位(在以太网 MAC MII 地址寄存器 (ETH_MACMIIAR) 中)置 1 时,SMI 将通过传输 PHY 地址、PHY 中的寄存器地址以及写入数据(在以太网 MACMII 数据寄存器 (ETH_MACMIIDR) 中)来触发对 PHY 寄存器进行写操作。事务进行期间,应用程序不应更改 MII 地址寄存器的内容或 MII 数据寄存器。在此期间对 MII 地址寄存器或MII 数据寄存器执行的写操作将会忽略(繁忙位处于高电平状态),事务将无错完成。写操作完成后,SMI 将通过复位繁忙位进行指示。

         综合上面,我们大概了解mac与phy通讯时序流程及数据帧格式,我们看看是怎么实现写PHY寄存器操作的通讯接口。


/* 设置phy芯片内部寄存器值,用于配制phy芯片 
 * heth:eth句柄,包含了以太网寄存器信息
 * PHYReg:phy寄存器地址值
 * RegValue:寄存器设定值
HAL_StatusTypeDef HAL_ETH_WritePHYRegister(ETH_HandleTypeDef *heth, uint16_t PHYReg, uint32_t RegValue)
{
    uint32_t tmpreg = 0;
    uint32_t tickstart = 0;

    /* 获取当前以太网控制器状态,如果当前处于读写状态,则返回繁忙信息 */
    if (heth->State == HAL_ETH_STATE_BUSY_WR)
    {
        return HAL_BUSY;
    }

    /* 如果当前没有读写操作,则设定状态为写状体 */
    heth->State = HAL_ETH_STATE_BUSY_WR;

    /* 获取以太网mac mii地址寄存器值*/
    tmpreg = heth->Instance->MACMIIAR;

    /* 保留MDC时钟,其它位数据清零(PA, MR, MW, MB)*/
    tmpreg &= ~ETH_MACMIIAR_CR_MASK;

    /* 设置phy设备地址 */
    tmpreg |= (((uint32_t)heth->Init.PhyAddress << 11) & ETH_MACMIIAR_PA); 
    /* 设置phy寄存器地址 */
    tmpreg |= (((uint32_t)PHYReg << 6) & ETH_MACMIIAR_MR);              
    /* 使能写模式 */
    tmpreg |= ETH_MACMIIAR_MW; 
    /* 设定mii 繁忙位 */                                
    tmpreg |= ETH_MACMIIAR_MB;                                          

    /* 写入phy寄存器设定中值到以太网mac mii数据寄存器中 */
    heth->Instance->MACMIIDR = (uint16_t)RegValue;

    /* 将设定的phy地址,phy地址寄存器值写入到以太网mac mii地址寄存器中 */
    heth->Instance->MACMIIAR = tmpreg;

    /* 获取当前时间 */
    tickstart = HAL_GetTick();

    /* 获取以太网繁忙状态标志,判断数据是否发送成功,在对 PHY 进行写操作过程中,ETH_MACMIIDR(MII 数据)应始终保持有效,直到 MAC 将此位清零 */
    while ((tmpreg & ETH_MACMIIAR_MB) == ETH_MACMIIAR_MB)
    {
        /* 超时则返回超时状态 */
        if ((HAL_GetTick() - tickstart) > PHY_WRITE_TO)
        {
            heth->State = HAL_ETH_STATE_READY;
            return HAL_TIMEOUT;
        }
        tmpreg = heth->Instance->MACMIIAR;
    }

    /* 设定可写操作状态标志 */
    heth->State = HAL_ETH_STATE_READY;

    /* 返回操作完成 */
    return HAL_OK;
}

        有时我们想知道以太网链路状态是怎么样的,是有网线插入还是没有网线插入,我们就需要用读phy寄存器的操作,我们看看怎么实现phy寄存器值的读取

SMI 读操作

        当用户将以太网 MAC MII 地址寄存器 (ETH_MACMIIAR) 中的 MII 繁忙位置 1、MII 写入位清零时,SMI 将通过传输 PHY 地址和 PHY 中的寄存器地址在 PHY 寄存器中触发读操作。事务进行期间,应用程序不应更改 MII 地址寄存器的内容或 MII 数据寄存器。在此期间对MII 地址寄存器或 MII 数据寄存器执行的写操作将会忽略(繁忙位处于高电平状态),事务将无错完成。读操作完成后,SMI 将复位繁忙位,然后用从 PHY 中读取的数据更新 MII 数据寄存器。

/**
  * @brief  读取phy寄存器值
  * @param  heth 指向以太网句柄,包含mac寄存器地址
  * @param PHYReg phy寄存器地址
  * @param RegValue phy寄存器值
  */
HAL_StatusTypeDef HAL_ETH_ReadPHYRegister(ETH_HandleTypeDef *heth, uint16_t PHYReg, uint32_t *RegValue)
{
    uint32_t tmpreg = 0;
    uint32_t tickstart = 0;

    /* 获取当前以太网是否可读状态标志,正在读取则返回繁忙 */
    if (heth->State == HAL_ETH_STATE_BUSY_RD)
    {
        return HAL_BUSY;
    }
    /* 设定以太网正在读状态 */
    heth->State = HAL_ETH_STATE_BUSY_RD;

    /* 获取以太网mac mii地址寄存器值*/
    tmpreg = heth->Instance->MACMIIAR;

    /* 保留MDC时钟,其它位数据清零(PA, MR, MW, MB)*/
    tmpreg &= ~ETH_MACMIIAR_CR_MASK;

    /* 设置phy设备地址 */
    tmpreg |= (((uint32_t)heth->Init.PhyAddress << 11) & ETH_MACMIIAR_PA); 
    /* 设置phy寄存器地址 */
    tmpreg |= (((uint32_t)PHYReg << 6) & ETH_MACMIIAR_MR); 
    /* 使能读模式 */              
    tmpreg &= ~ETH_MACMIIAR_MW; 
    /* 设定mii 繁忙位 */                                       
    tmpreg |= ETH_MACMIIAR_MB;                                           

    /* 写入phy寄存器设定中值到以太网mac mii数据寄存器中 */
    heth->Instance->MACMIIAR = tmpreg;

    /* 获取当前时间 */
    tickstart = HAL_GetTick();

    /*检查phy寄存器值是否已获取到,在对 PHY 进行读操作过程中,ETH_MACMIIDR始终无效,直到 MAC 将此位清零。 */
    while ((tmpreg & ETH_MACMIIAR_MB) == ETH_MACMIIAR_MB)
    {
        /* 返回超时异常 */
        if ((HAL_GetTick() - tickstart) > PHY_READ_TO)
        {
            heth->State = HAL_ETH_STATE_READY;
            return HAL_TIMEOUT;
        }
        tmpreg = heth->Instance->MACMIIAR;
    }

    /* 获取phy寄存器值 */
    *RegValue = (uint16_t)(heth->Instance->MACMIIDR);

    /* 设定以太网可操作标志 */
    heth->State = HAL_ETH_STATE_READY;

    /* 返回状态ok */
    return HAL_OK;
}

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32MP157系列内嵌了千兆以太网,可以通过该以太网接口实现网络通信。下面是一个简单的使用STM32CubeMX和Keil MDK搭建的STM32MP157开发板的千兆以太网通信的例子: 1.首先,在STM32CubeMX中配置以太网模块。在Pinout & Configuration选项卡中,启用Ethernet PHY和Ethernet MAC,并将它们连接到正确的引脚上。 2.在中间的Configuration选项卡中,选择Ethernet PHY和Ethernet MAC的配置。在PHY配置中,选择正确的PHY类型和速度。在MAC配置中,选择正确的速度和双工模式,并启用DMA传输。 3.生成代码并导出到Keil MDK中。 4.在Keil MDK中,打开生成的工程并添加以下代码: ```c #include "stm32mp1xx_hal.h" #include "stm32mp1xx_hal_eth.h" ETH_HandleTypeDef heth; void ETH_Init(void) { /* Enable Ethernet clock */ __HAL_RCC_ETH1MAC_CLK_ENABLE(); __HAL_RCC_ETH1TX_CLK_ENABLE(); __HAL_RCC_ETH1RX_CLK_ENABLE(); /* Configure Ethernet GPIOs */ /* ... */ /* Configure Ethernet peripheral */ heth.Instance = ETH1; heth.Init.MACAddr = /* ... */; heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII; heth.Init.TxDesc = /* ... */; heth.Init.RxDesc = /* ... */; /* ... */ HAL_ETH_Init(&heth); } void ETH_SendPacket(uint8_t *data, uint16_t len) { HAL_ETH_Transmit(&heth, data, len, HAL_MAX_DELAY); } void ETH_ReceivePacket(uint8_t *data, uint16_t *len) { HAL_ETH_GetReceivedFrame(&heth, data, len, HAL_MAX_DELAY); } ``` 5.在主函数中调用ETH_Init()函数初始化以太网模块,并使用ETH_SendPacket()函数发送数据包,使用ETH_ReceivePacket()函数接收数据包。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值