最新教程下載:http://www.armbbs.cn/forum.php?mod=viewthread&tid=95243
第6章 RL-TCPnet底層驅動說明
本章節為大家講解RL-TCPnet的底層驅動,主要是STM32自帶MAC的驅動實現和PHY的驅動實現。
6.1初學者重要提示
6.2 MAC+PHY驅動實現方案
6.3 CMSIS-Driver簡介和驅動工作流程
6.4 CMSIS-Driver的PHY底層驅動實現
6.5 CMSIS-Driver的MAC底層驅動實現
6.6 總結
6.1 初學者重要提示
- 學習本章節前,務必學習STM32參考手冊中MAC章節的基礎知識講解,非常重要。
- DM9161和DM9162的手冊可以在官網地址下載,本章節需要用到部分寄存器:http://www.davicom.com.tw/production-item.php。
- 早期STM32F407開發板使用的PHY芯片是DM9161,不過現在基本已經停產了,當前F407,F429和H7開發板統一使用DM9162。底層代碼對這兩個芯片都可以正確驅動。
- MAC全稱Media Access Control,媒介訪問控制。
- PHY全稱Physical Interface Transceiver,物理層接口收發器。
6.2 MAC+PHY驅動實現方案
STM32F4自帶MAC,所以只需外置PHY芯片即可使用以太網,示意圖如下:
當前V5開發板使用的PHY芯片是DM9162。反映到硬件設計上,原圖如下:
通過這個原理圖,我們要注意以下兩點:
- 1.8V的電壓是PHY芯片DM916x自己產生的。
- PHY芯片的地址由PHYAD[0:3]引腳決定,當前是將PHYAD[0]引腳接了一個上拉電阻,也就是說DM916x的地址是0x01。
教程配套的開發板采用的RMII接口,即下面這種硬件接口方式:
RMII接口降低了 10/100Mbps下微控制器以太網外設與外部PHY間的引腳數。根據IEEE 802.3u標准, MII包括16個數據和控制信號的引腳。RMII規范將引腳數減少為7個(引腳數減少62.5%)。RMII具有以下特性:
- 支持10Mbps和100Mbps的運行速率。
- 參考時鍾必須是 50 MHz。
- 相同的參考時鍾必須從外部提供給 MAC 和外部以太網 PHY。
- 它提供了獨立的2位寬(雙位)的發送和接收數據路徑,即發生和接收都是占用了兩個引腳。
6.3 CMSIS-Driver簡介和驅動工作流程
這個是ARM做好的驅動框架,支持的外設如下:
針對不同廠商,ARM會出一個完整的驅動包,比如STM32F4系列,在MDK安裝目錄的此路徑下(前提是大家安裝了STM32H7軟件包):ARM\PACK\Keil\STM32F4xx_DFP\2.14.0\CMSIS\Driver。
ARM做的這個驅動跟HAL庫有什么區別呢?ARM做的這個庫要調用到HAL的一些API(H7版的有調用到,F4版的很少調用到,基本是獨立的),然后封裝了一些比較好用的API,方便用戶調用。
關於這些不同外設的驅動文件,它們都有統一的API函數,調用流程如下:
關於這個驅動的流程,大家有個認識即可,網絡協議棧會直接調用這些API進行操作,無需用戶去調用。
6.4 CMSIS-Driver的PHY底層驅動實現
PHY驅動由CMSIS-Driver軟件包提供,當前支持的PHY如下(位於MDK安裝路徑ARM\CMSIS-Driver\2.4.0\ETH,數字2.4.0表示當前的CMSIS-Driver版本):
這些驅動文件主要分為兩類:
- 以ETH開頭的,這些芯片是MAC+PHY二合一。
- 以PHY開頭的,這些芯片僅是個PHY。
CMSIS-Driver現有的驅動里面是沒有DM9162,所以需要用戶自己實現,這里將DM9162的實現函數逐一為大家做個說明。CMSIS-Driver已經定義好了API,用戶實現每個API的具體功能即可。
6.4.1 DM9161和DM9162的區別
早期我們發布的STM32F407開發板的PHY芯片使用的是DM9161,現在這個芯片基本已經停產,所以已經統一改成使用DM9162,這兩個型號主要在以下兩個地方有區別,其它基本都一樣。
- 兩個PHY芯片的的ID不一樣,DM9161的ID是0x0181B8B1,DM9162的ID是0x0181B8A0。
- 系統剛上電時,DM9161的ID寄存器支持立即讀取,但是DM9162不支持,這一點用戶在使用的時候要特別注意。但是DM9161和DM9162都支持立即寫寄存器BMCR,所以當前的操作就是直接對寄存器BMCR發復位命令,然后再進行相關設置。
對於這兩個芯片,了解這兩點區別就可以了。另外,這兩個芯片的手冊和其它的相關知識在這個帖子里面進行了簡單的匯總:http://www.armbbs.cn/forum.php?mod=viewthread&tid=19577
6.4.2 函數GetVersion
函數原型:
static ARM_DRIVER_VERSION GetVersion (void) { return DriverVersion; }
函數描述:
用於獲取當前的PHY驅動版本。
6.4.3 函數Initialize
函數原型:
static int32_t Initialize (ARM_ETH_PHY_Read_t fn_read, ARM_ETH_PHY_Write_t fn_write) { if ((fn_read == NULL) || (fn_write == NULL)) { return ARM_DRIVER_ERROR_PARAMETER; } if ((PHY.flags & PHY_INIT) == 0U) { /* Register PHY read/write functions. */ PHY.reg_rd = fn_read; PHY.reg_wr = fn_write; PHY.bmcr = 0U; PHY.flags = PHY_INIT; } return ARM_DRIVER_OK; }
函數描述:
初始化讀寫PHY芯片所需要的API
函數參數:
- 第1個參數是讀PHY芯片API地址。
- 第2個參數是寫PHY芯片API地址。
- 返回值,無參數錯誤返回ARM_DRIVER_OK。有參數錯誤返回ARM_DRIVER_ERROR_PARAMETER。
6.4.4 函數Uninitialize
函數原型:
static int32_t Uninitialize (void) { PHY.reg_rd = NULL; PHY.reg_wr = NULL; PHY.bmcr = 0U; PHY.flags = 0U; return ARM_DRIVER_OK; }
函數描述:
復位讀寫PHY芯片所需要的API。
函數參數:
- 返回值,返回ARM_DRIVER_OK
6.4.5 函數PowerControl
函數原型:
static int32_t PowerControl (ARM_POWER_STATE state) { uint16_t val; switch ((int32_t)state) { /* 將PHY斷電 */ case ARM_POWER_OFF: /* 初始化狀態才可以配置POWER OFF */ if ((PHY.flags & PHY_INIT) == 0U) { return ARM_DRIVER_ERROR; } PHY.flags &= ~PHY_POWER; PHY.bmcr = BMCR_POWER_DOWN; /* 設置BMCR寄存器,斷電 */ return (PHY.reg_wr(ETH_PHY_ADDR, REG_BMCR, PHY.bmcr)); /* PHY上電,並清除BMCR寄存器 */ case ARM_POWER_FULL: /* 初始化狀態才可以配置POWER FULL */ if ((PHY.flags & PHY_INIT) == 0U) { return ARM_DRIVER_ERROR; } /* 已經處於POWER ON狀態,直接返回OK */ if (PHY.flags & PHY_POWER) { return ARM_DRIVER_OK; } /* 讀取設備 */ PHY.reg_rd(ETH_PHY_ADDR, REG_PHYIDR1, &val); /* 讀取ID1 */ if (val != PHY_ID1) { return ARM_DRIVER_ERROR_UNSUPPORTED; } PHY.reg_rd(ETH_PHY_ADDR, REG_PHYIDR2, &val); /* 讀取ID2, 此處做了一個特別處理,屏蔽后面8個bit,方便DM9162和DM9161都可以識別,因為這兩個 PHY后面的ID不同。 */ if ((val & 0xFF00) != PHY_ID2) { return ARM_DRIVER_ERROR_UNSUPPORTED; } /* DM916X用不到這個 */ #if (ETH_PHY_REF_CLK_50M != 0) PHY.reg_rd(ETH_PHY_ADDR, REG_PHYCR2, &val); val |= PHYCR2_REF_CLK_SELECT; PHY.reg_wr(ETH_PHY_ADDR, REG_PHYCR2, val); #endif PHY.bmcr = 0U; /* BMCR寄存器清零 */ if (PHY.reg_wr(ETH_PHY_ADDR, REG_BMCR, PHY.bmcr) != ARM_DRIVER_OK) { return ARM_DRIVER_ERROR; } PHY.flags |= PHY_POWER; return ARM_DRIVER_OK; /* 不支持低功耗操作 */ case ARM_POWER_LOW: default: return ARM_DRIVER_ERROR_UNSUPPORTED; } }
函數描述:
用於控制PHY的上電和斷電。
函數參數:
- 第1個參數是PHY配置
- ARM_POWER_OFF 表示斷電,程序此處做了BMCR寄存器斷電操作。
- ARM_POWER_FULL 表示上電,程序此處讀取PHY的ID,並清除BMCR寄存器。
- ARM_POWER_LOW 表示低功耗,程序此處不支持。
- 第2個參數是寫PHY芯片API地址。
- 返回值,設置正確返回ARM_DRIVER_OK,設置錯誤返回ARM_DRIVER_ERROR,而ARM_DRIVER_ERROR_UNSUPPORTED表示不支持。
6.4.6 函數SetInterface
函數原型:
static int32_t SetInterface (uint32_t interface) { int32_t status; if ((PHY.flags & PHY_POWER) == 0U) { return ARM_DRIVER_ERROR; } /* 僅作了RMII接口支持 */ switch (interface) { case ARM_ETH_INTERFACE_RMII: status = ARM_DRIVER_OK; break; default: status = ARM_DRIVER_ERROR_UNSUPPORTED; break; } return (status); }
函數描述:
用於配置使用SMII,RMII還是MII接口外接的PHY芯片。
函數參數:
- 第1個參數設置使用的PHY接口類型。
- ARM_ETH_INTERFACE_MII,Media Independent Interface (MII)
- ARM_ETH_INTERFACE_RMII,Reduced Media Independent Interface (RMII)
- ARM_ETH_INTERFACE_SMII,Serial Media Independent Interface (SMII)
- 返回值,設置正確返回ARM_DRIVER_OK,設置錯誤返回ARM_DRIVER_ERROR,而ARM_DRIVER_ERROR_UNSUPPORTED表示不支持。
6.4.7 函數SetMode
函數原型:
static int32_t SetMode (uint32_t mode) { uint16_t val; /* 上電狀態才可以配置 */ if ((PHY.flags & PHY_POWER) == 0U) { return ARM_DRIVER_ERROR; } val = PHY.bmcr & BMCR_POWER_DOWN; /* 速度配置10M或者100M */ switch (mode & ARM_ETH_PHY_SPEED_Msk) { case ARM_ETH_PHY_SPEED_10M: break; case ARM_ETH_PHY_SPEED_100M: val |= BMCR_SPEED_SELECT; break; default: return ARM_DRIVER_ERROR_UNSUPPORTED; } /* 全雙工或者半雙工配置 */ switch (mode & ARM_ETH_PHY_DUPLEX_Msk) { case ARM_ETH_PHY_DUPLEX_HALF: break; case ARM_ETH_PHY_DUPLEX_FULL: val |= BMCR_DUPLEX_MODE; break; default: return ARM_DRIVER_ERROR_UNSUPPORTED; } /* 自動協商配置使能 */ if (mode & ARM_ETH_PHY_AUTO_NEGOTIATE) { val |= BMCR_ANEG_EN; } /* 回環配置使能,方便回環測試 */ if (mode & ARM_ETH_PHY_LOOPBACK) { val |= BMCR_LOOPBACK; } /* 設置隔離,電氣隔離RMII/MII/SMII接口 */ if (mode & ARM_ETH_PHY_ISOLATE) { val |= BMCR_ISOLATE; } PHY.bmcr = val; return (PHY.reg_wr(ETH_PHY_ADDR, REG_BMCR, PHY.bmcr)); }
函數描述:
用於設置PHY芯片的工作模式。
函數參數:
- 第1個參數設置工作模式,這幾項支持或操作。
- 返回值,設置正確返回ARM_DRIVER_OK,設置錯誤返回ARM_DRIVER_ERROR,而ARM_DRIVER_ERROR_UNSUPPORTED表示不支持。
6.4.8 函數GetLinkState
函數原型:
static ARM_ETH_LINK_STATE GetLinkState (void) { ARM_ETH_LINK_STATE state; uint16_t val = 0U; if (PHY.flags & PHY_POWER) { PHY.reg_rd(ETH_PHY_ADDR, REG_BMSR, &val); } state = (val & BMSR_LINK_STAT) ? ARM_ETH_LINK_UP : ARM_ETH_LINK_DOWN; return (state); }
函數描述:
用於獲取網線插拔狀態。
函數參數:
- 返回值,返回ARM_ETH_LINK_UP表示網線在連接狀態,返回ARM_ETH_LINK_DOWN表示網線處於斷開狀態。
6.4.9 函數GetLinkInfo
函數原型:
static ARM_ETH_LINK_INFO GetLinkInfo (void) { ARM_ETH_LINK_INFO info; uint16_t val = 0U; if (PHY.flags & PHY_POWER) { /* 讀取PHY DSCSR 寄存器 */ PHY.reg_rd(ETH_PHY_ADDR, REG_DSCSR, &val); } /* 獲取速度和雙工模式 */ info.speed = ((val & DSCSR_100M_FD)|(val & DSCSR_100M_HD)) ? ARM_ETH_SPEED_100M : ARM_ETH_SPEED_10M; info.duplex = ((val & DSCSR_100M_FD)|(val & DSCSR_10M_FD)) ? ARM_ETH_DUPLEX_FULL : ARM_ETH_DUPLEX_HALF; return (info); }
函數描述:
用於獲取速度和雙工模式。
函數參數:
- 返回值記錄速度(10Mbps或者100Mbps)和雙工模式(半雙工或者全雙工)。
6.5 CMSIS-Driver的MAC底層驅動實現
KEIL已經為STM32F4制作好MAC驅動文件EMAC_STM32F4xx.c。我們這里將相關實現函數為大家做個說明。
6.5.1 函數GetVersion
函數原型:
static ARM_DRIVER_VERSION GetVersion (void) { return DriverVersion; }
函數描述:
用於獲取當前的MAC驅動版本。
6.5.2 函數GetCapabilities
函數原型:
#ifndef EMAC_CHECKSUM_OFFLOAD #define EMAC_CHECKSUM_OFFLOAD 1 #endif static const ARM_ETH_MAC_CAPABILITIES DriverCapabilities = { (EMAC_CHECKSUM_OFFLOAD != 0) ? 1U : 0U, /* checksum_offload_rx_ip4 */ (EMAC_CHECKSUM_OFFLOAD != 0) ? 1U : 0U, /* checksum_offload_rx_ip6 */ (EMAC_CHECKSUM_OFFLOAD != 0) ? 1U : 0U, /* checksum_offload_rx_udp */ (EMAC_CHECKSUM_OFFLOAD != 0) ? 1U : 0U, /* checksum_offload_rx_tcp */ (EMAC_CHECKSUM_OFFLOAD != 0) ? 1U : 0U, /* checksum_offload_rx_icmp */ (EMAC_CHECKSUM_OFFLOAD != 0) ? 1U : 0U, /* checksum_offload_tx_ip4 */ (EMAC_CHECKSUM_OFFLOAD != 0) ? 1U : 0U, /* checksum_offload_tx_ip6 */ (EMAC_CHECKSUM_OFFLOAD != 0) ? 1U : 0U, /* checksum_offload_tx_udp */ (EMAC_CHECKSUM_OFFLOAD != 0) ? 1U : 0U, /* checksum_offload_tx_tcp */ (EMAC_CHECKSUM_OFFLOAD != 0) ? 1U : 0U, /* checksum_offload_tx_icmp */ (ETH_MII != 0) ? ARM_ETH_INTERFACE_MII : ARM_ETH_INTERFACE_RMII, /* media_interface */ 0U, /* mac_address */ 1U, /* event_rx_frame */ 1U, /* event_tx_frame */ 1U, /* event_wakeup */ (EMAC_TIME_STAMP != 0) ? 1U : 0U /* precision_timer */ #if (defined(ARM_ETH_MAC_API_VERSION) && (ARM_ETH_MAC_API_VERSION >= 0x201U)) , 0U /* reserved bits */ #endif }; static ARM_ETH_MAC_CAPABILITIES GetCapabilities (void) { return DriverCapabilities; }
函數描述:
用於獲取MAC的硬件功能。
從當前的宏定義來看,支持發送和接收的IP4,IP6,UDP,TCP和ICMP的硬件校驗和計算。
6.5.3 函數Initialize
函數原型:
static int32_t Initialize (ARM_ETH_MAC_SignalEvent_t cb_event) { #if defined(RTE_DEVICE_FRAMEWORK_CLASSIC) GPIO_InitTypeDef GPIO_InitStruct; const ETH_PIN *io; #endif /* 使能SYSCFG時鍾 */ RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; #if (ETH_MII == 0) SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL; #else SYSCFG->PMC &= ~SYSCFG_PMC_MII_RMII_SEL; #endif #if defined(RTE_DEVICE_FRAMEWORK_CLASSIC) /* 配置以太網引腳 */ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Alternate = GPIO_AF11_ETH; for (io = eth_pins; io != ð_pins[sizeof(eth_pins)/sizeof(ETH_PIN)]; io++) { Enable_GPIO_Clock (io->port); GPIO_InitStruct.Pin = io->pin; HAL_GPIO_Init (io->port, &GPIO_InitStruct); } #else heth.Instance = ETH; #endif /* 清空控制結構體 */ memset ((void *)&Emac, 0, sizeof (EMAC_CTRL)); Emac.cb_event = cb_event; Emac.flags = EMAC_FLAG_INIT; return ARM_DRIVER_OK; }
函數描述:
用於初始化MAC,配置以太網引腳,注冊回調函數。
函數參數:
- 第1個參數用於注冊回調函數。
- 返回值,固定返回ARM_DRIVER_OK。
6.5.4 函數Uninitialize
函數原型:
static int32_t Uninitialize (void) { #if defined(RTE_DEVICE_FRAMEWORK_CLASSIC) const ETH_PIN *io; /* 復位以太網引腳配置 */ for (io = eth_pins; io != ð_pins[sizeof(eth_pins)/sizeof(ETH_PIN)]; io++) { HAL_GPIO_DeInit(io->port, io->pin); } #else heth.Instance = NULL; #endif Emac.flags &= ~EMAC_FLAG_INIT; return ARM_DRIVER_OK; }
函數描述:
復位初始化。
函數參數:
- 返回值,固定返回ARM_DRIVER_OK。
6.5.5 函數PowerControl
函數原型:
static int32_t PowerControl (ARM_POWER_STATE state) { uint32_t hclk, clkdiv; if ((state != ARM_POWER_OFF) && (state != ARM_POWER_FULL) && (state != ARM_POWER_LOW)) { return ARM_DRIVER_ERROR_UNSUPPORTED; } switch (state) { /* 關閉電源 */ case ARM_POWER_OFF: 內容較多,省略未寫 break; /* 低功耗 */ case ARM_POWER_LOW: return ARM_DRIVER_ERROR_UNSUPPORTED; /* 上電 */ case ARM_POWER_FULL: 內容較多,省略未寫 break; } return ARM_DRIVER_OK; }
函數描述:
用於控制MAC的上電和斷電。
函數參數:
- 第1個參數是MAC配置
- ARM_POWER_OFF 表示斷電,程序此處做了MAC復位。
- ARM_POWER_FULL 表示上電,程序此處初始化MAC。
- ARM_POWER_LOW 表示低功耗,程序此處不支持。
- 返回值,設置正確返回ARM_DRIVER_OK,設置錯誤返回ARM_DRIVER_ERROR,而ARM_DRIVER_ERROR_UNSUPPORTED表示不支持。
6.5.6 函數GetMacAddress
函數原型:
static int32_t GetMacAddress (ARM_ETH_MAC_ADDR *ptr_addr) { uint32_t val; if (ptr_addr == NULL) { return ARM_DRIVER_ERROR_PARAMETER; } if ((Emac.flags & EMAC_FLAG_POWER) == 0U) { return ARM_DRIVER_ERROR; } val = ETH->MACA0HR; ptr_addr->b[5] = (uint8_t)(val >> 8); ptr_addr->b[4] = (uint8_t)(val); val = ETH->MACA0LR; ptr_addr->b[3] = (uint8_t)(val >> 24); ptr_addr->b[2] = (uint8_t)(val >> 16); ptr_addr->b[1] = (uint8_t)(val >> 8); ptr_addr->b[0] = (uint8_t)(val); return ARM_DRIVER_OK; }
函數描述:
用於獲取MAC地址。
函數參數:
- 第1個參數用於存儲獲取的MAC地址。
- 返回值,設置正確返回ARM_DRIVER_OK,設置錯誤返回ARM_DRIVER_ERROR,而ARM_DRIVER_ERROR_PARAMETER表示參數錯誤。
6.5.7 函數SetMacAddress
函數原型:
static int32_t SetMacAddress (const ARM_ETH_MAC_ADDR *ptr_addr) { if (ptr_addr == NULL) { return ARM_DRIVER_ERROR_PARAMETER; } if ((Emac.flags & EMAC_FLAG_POWER) == 0U) { return ARM_DRIVER_ERROR; } /* Set Ethernet MAC Address registers */ ETH->MACA0HR = ((uint32_t)ptr_addr->b[5] << 8) | (uint32_t)ptr_addr->b[4]; ETH->MACA0LR = ((uint32_t)ptr_addr->b[3] << 24) | ((uint32_t)ptr_addr->b[2] << 16) | ((uint32_t)ptr_addr->b[1] << 8) | (uint32_t)ptr_addr->b[0]; return ARM_DRIVER_OK; }
函數描述:
用於設置本身的MAC地址。包含這個MAC地址的以太網幀才會被這個芯片所接受。也可以通過函數ARM_ETH_MAC_SetAddressFilter設置接收其它的MAC地址。除此之外,還可以通過函數ARM_ETH_MAC_Control 的參數ARM_ETH_MAC_CONFIGURE設置廣播和組播。
函數參數:
- 第1個參數是MAC地址。
- 返回值,設置正確返回ARM_DRIVER_OK,設置錯誤返回ARM_DRIVER_ERROR,而ARM_DRIVER_ERROR_PARAMETER表示參數錯誤。
6.5.8 函數SetAddressFilter
函數原型:
static int32_t SetAddressFilter (const ARM_ETH_MAC_ADDR *ptr_addr, uint32_t num_addr) { uint32_t crc; if ((ptr_addr == NULL) && (num_addr != 0)) { return ARM_DRIVER_ERROR_PARAMETER; } if ((Emac.flags & EMAC_FLAG_POWER) == 0U) { return ARM_DRIVER_ERROR; } /* 使用單播過濾前三個MAC */ ETH->MACFFR &= ~(ETH_MACFFR_HPF | ETH_MACFFR_HM); ETH->MACHTHR = 0U; ETH->MACHTLR = 0U; if (num_addr == 0U) { ETH->MACA1HR = 0U; ETH->MACA1LR = 0U; ETH->MACA2HR = 0U; ETH->MACA2LR = 0U; ETH->MACA3HR = 0U; ETH->MACA3LR = 0U; return ARM_DRIVER_OK; } ETH->MACA1HR = ((uint32_t)ptr_addr->b[5] << 8) | (uint32_t)ptr_addr->b[4] | ETH_MACA1HR_AE; ETH->MACA1LR = ((uint32_t)ptr_addr->b[3] << 24) | ((uint32_t)ptr_addr->b[2] << 16) | ((uint32_t)ptr_addr->b[1] << 8) | (uint32_t)ptr_addr->b[0]; num_addr--; if (num_addr == 0U) { ETH->MACA2HR = 0U; ETH->MACA2LR = 0U; ETH->MACA3HR = 0U; ETH->MACA3LR = 0U; return ARM_DRIVER_OK; } ptr_addr++; ETH->MACA2HR = ((uint32_t)ptr_addr->b[5] << 8) | (uint32_t)ptr_addr->b[4] | ETH_MACA2HR_AE; ETH->MACA2LR = ((uint32_t)ptr_addr->b[3] << 24) | ((uint32_t)ptr_addr->b[2] << 16) | ((uint32_t)ptr_addr->b[1] << 8) | (uint32_t)ptr_addr->b[0]; num_addr--; if (num_addr == 0U) { ETH->MACA3HR = 0U; ETH->MACA3LR = 0U; return ARM_DRIVER_OK; } ptr_addr++; ETH->MACA3HR = ((uint32_t)ptr_addr->b[5] << 8) | (uint32_t)ptr_addr->b[4] | ETH_MACA3HR_AE; ETH->MACA3LR = ((uint32_t)ptr_addr->b[3] << 24) | ((uint32_t)ptr_addr->b[2] << 16) | ((uint32_t)ptr_addr->b[1] << 8) | (uint32_t)ptr_addr->b[0]; num_addr--; if (num_addr == 0U) { return ARM_DRIVER_OK; } ptr_addr++; /* 計算剩余MAC地址的64bit Hash表 */ for ( ; num_addr; ptr_addr++, num_addr--) { crc = crc32_data (&ptr_addr->b[0], 6U) >> 26; if (crc & 0x20U) { ETH->MACHTHR |= (1U << (crc & 0x1FU)); } else { ETH->MACHTLR |= (1U << crc); } } /* 使能單播和Hash地址過濾 */ ETH->MACFFR |= ETH_MACFFR_HPF | ETH_MACFFR_HM; return ARM_DRIVER_OK; }
函數描述:
用於以太網MAC接收地址過濾,通過這個函數可以設置此設備可以接收到的MAC地址(設備本身MAC以外的地址)。MAC還可以通過函數ARM_ETH_MAC_Control 的參數ARM_ETH_MAC_CONFIGURE設置廣播和組播。
函數參數:
- 第1個參數是MAC地址列表。
- 第2個參數是MAC地址個數。
- 返回值,設置正確返回ARM_DRIVER_OK,設置錯誤返回ARM_DRIVER_ERROR,而ARM_DRIVER_ERROR_PARAMETER表示參數錯誤。
6.5.9 函數SendFrame
函數原型:
static int32_t SendFrame (const uint8_t *frame, uint32_t len, uint32_t flags) { uint8_t *dst = Emac.frame_end; uint32_t ctrl; if ((frame == NULL) || (len == 0U)) { return ARM_DRIVER_ERROR_PARAMETER; } if ((Emac.flags & EMAC_FLAG_POWER) == 0U) { return ARM_DRIVER_ERROR; } if (dst == NULL) { /* 啟動新的傳輸幀 */ if (tx_desc[Emac.tx_index].CtrlStat & DMA_TX_OWN) { /* 傳輸忙 */ return ARM_DRIVER_ERROR_BUSY; } dst = tx_desc[Emac.tx_index].Addr; tx_desc[Emac.tx_index].Size = len; } else { /* 分步傳輸 */ tx_desc[Emac.tx_index].Size += len; } /* 快速復制數據到ETH-DMA */ for ( ; len > 7U; dst += 8, frame += 8, len -= 8U) { __UNALIGNED_UINT32_WRITE(&dst[0], __UNALIGNED_UINT32_READ(&frame[0])); __UNALIGNED_UINT32_WRITE(&dst[4], __UNALIGNED_UINT32_READ(&frame[4])); } /* 復制剩余字節 */ for ( ; len > 1U; dst += 2, frame += 2, len -= 2U) { __UNALIGNED_UINT16_WRITE(&dst[0], __UNALIGNED_UINT16_READ(&frame[0])); } if (len > 0U) { dst++[0] = frame++[0]; } if (flags & ARM_ETH_MAC_TX_FRAME_FRAGMENT) { /* 還有數據,記錄當前寫入位置 */ Emac.frame_end = dst; return ARM_DRIVER_OK; } /* 幀就緒,發送給DMA */ ctrl = tx_desc[Emac.tx_index].CtrlStat & ~DMA_TX_CIC; #if (EMAC_CHECKSUM_OFFLOAD != 0) if (Emac.tx_cks_offload) { /* The following is a workaround for EMAC silicon problem: */ /* "Incorrect layer 3 (L3) checksum is inserted in the sent */ /* IPv4 fragmented packets." */ /* Description: */ /* When automatic checksum insertion is enabled and the packet */ /* is IPv4 frame fragment, then the MAC may incorrectly insert */ /* checksum into the packet. This corrupts the payload data */ /* and generates checksum errors at the receiver. */ uint16_t prot = __UNALIGNED_UINT16_READ(&tx_desc[Emac.tx_index].Addr[12]); uint16_t frag = __UNALIGNED_UINT16_READ(&tx_desc[Emac.tx_index].Addr[20]); if ((prot == 0x0008) && (frag & 0xFF3F)) { /* Insert only IP header checksum in fragmented frame */ ctrl |= DMA_TX_CIC_IP; } else { /* Insert IP header and payload checksums (TCP,UDP,ICMP) */ ctrl |= DMA_TX_CIC; } } #endif ctrl &= ~(DMA_TX_IC | DMA_TX_TTSE); if (flags & ARM_ETH_MAC_TX_FRAME_EVENT) { ctrl |= DMA_TX_IC; } #if (EMAC_TIME_STAMP != 0) if (flags & ARM_ETH_MAC_TX_FRAME_TIMESTAMP) { ctrl |= DMA_TX_TTSE; } Emac.tx_ts_index = Emac.tx_index; #endif tx_desc[Emac.tx_index].CtrlStat = ctrl | DMA_TX_OWN; Emac.tx_index++; if (Emac.tx_index == NUM_TX_BUF) { Emac.tx_index = 0U; } Emac.frame_end = NULL; /* 啟動幀傳輸 */ ETH->DMASR = ETH_DMASR_TPSS; ETH->DMATPDR = 0U; return ARM_DRIVER_OK; }
函數描述:
用於控制以太網幀數據的發送。此函數會將用戶要發送的數據存入到以太網DMA緩沖里面,而不必等待發送完成,只要有緩沖,就可以繼續往里面存數據。
函數參數:
- 第1個參數是要發送的數據地址。
- 第2個參數是發送的字節數。
- 第3個參數支持的配置如下:
6.5.10 函數ReadFrame
函數原型:
static int32_t ReadFrame (uint8_t *frame, uint32_t len) { uint8_t const *src = rx_desc[Emac.rx_index].Addr; int32_t cnt = (int32_t)len; if ((frame == NULL) && (len != 0U)) { return ARM_DRIVER_ERROR_PARAMETER; } if ((Emac.flags & EMAC_FLAG_POWER) == 0U) { return ARM_DRIVER_ERROR; } /* 快速復制數據到幀緩沖 */ for ( ; len > 7U; frame += 8, src += 8, len -= 8U) { __UNALIGNED_UINT32_WRITE(&frame[0], __UNALIGNED_UINT32_READ(&src[0])); __UNALIGNED_UINT32_WRITE(&frame[4], __UNALIGNED_UINT32_READ(&src[4])); } /* 復制剩余7字節 */ for ( ; len > 1U; frame += 2, src += 2, len -= 2U) { __UNALIGNED_UINT16_WRITE(&frame[0], __UNALIGNED_UINT16_READ(&src[0])); } if (len > 0U) { frame[0] = src[0]; } /* 設置此塊到ETH-DMA */ rx_desc[Emac.rx_index].Stat = DMA_RX_OWN; Emac.rx_index++; if (Emac.rx_index == NUM_RX_BUF) { Emac.rx_index = 0; } if (ETH->DMASR & ETH_DMASR_RBUS) { /* 沒有緩沖,釋放 */ ETH->DMASR = ETH_DMASR_RBUS; ETH->DMARPDR = 0; } return (cnt); }
函數描述:
用於讀取以太網幀數據。
函數參數:
- 第1個參數是讀取數據的存儲地址。
- 第2個參數存儲數據的緩沖大小。
- 返回值,返回數值大於0,表示讀取的字節數,返回數值小於0表示出錯。
注意事項:
調用此函數前,需要先調用函數ARM_ETH_MAC_Control (ARM_ETH_MAC_CONTROL_RX , 1)使能接收。
6.5.11 函數GetRxFrameSize
函數原型:
static uint32_t GetRxFrameSize (void) { uint32_t stat = rx_desc[Emac.rx_index].Stat; if ((Emac.flags & EMAC_FLAG_POWER) == 0U) { return (0U); } if (stat & DMA_RX_OWN) { /* DMA使用中 */ return (0U); } if (((stat & DMA_RX_ES) != 0) || ((stat & DMA_RX_FS) == 0) || ((stat & DMA_RX_LS) == 0)) { /* 錯誤,塊無效 */ return (0xFFFFFFFFU); } return (((stat & DMA_RX_FL) >> 16) - 4U); }
函數描述:
用於獲取接收到的幀大小,此函數會在ARM_ETH_MAC_ReadFrame之前被調用。
函數參數:
- 返回值,返回接收到的數據大小。
注意事項:
幀大小包括MAC地址和接收到數據。此函數返回數值0表示接收緩沖區里面沒有數據,如果接收到的數數據大於最大的幀大小或者小於最小的幀大小,都將被函數ARM_ETH_MAC_ReadFrame放棄。
6.5.12 函數GetRxFrameTime
函數原型:
static int32_t GetRxFrameTime (ARM_ETH_MAC_TIME *time) { #if (EMAC_TIME_STAMP) RX_Desc *rxd = &rx_desc[Emac.rx_index]; if ((Emac.flags & EMAC_FLAG_POWER) == 0U) { return ARM_DRIVER_ERROR; } if (rxd->Stat & DMA_RX_OWN) { /* DMA使用中 */ return ARM_DRIVER_ERROR_BUSY; } time->ns = rxd->TimeLo; time->sec = rxd->TimeHi; return ARM_DRIVER_OK; #else (void)time; return ARM_DRIVER_ERROR; #endif }
函數描述:
用於獲取以太網接收幀時間戳。
函數參數:
- 第1個參數用於存儲獲取的以太網發送幀時間戳。
- 回值,設置正確返回ARM_DRIVER_OK,設置錯誤返回ARM_DRIVER_ERROR,而ARM_DRIVER_ERROR_UNSUPPORTED表示不支持。
注意事項:
必須在調用函數ARM_ETH_MAC_ReadFrame前,調用此函數。
6.5.13 函數GetTxFrameSize
函數原型:
static int32_t GetTxFrameTime (ARM_ETH_MAC_TIME *time) { #if (EMAC_TIME_STAMP) TX_Desc *txd = &tx_desc[Emac.tx_ts_index]; if ((Emac.flags & EMAC_FLAG_POWER) == 0U) { return ARM_DRIVER_ERROR; } if (txd->CtrlStat & DMA_RX_OWN) { /* DMA忙 */ return ARM_DRIVER_ERROR_BUSY; } if ((txd->CtrlStat & DMA_TX_TTSS) == 0) { /* 驅動錯誤,發送時間戳不可用 */ return ARM_DRIVER_ERROR; } time->ns = txd->TimeLo; time->sec = txd->TimeHi; return ARM_DRIVER_OK; #else (void)time; return ARM_DRIVER_ERROR; #endif }
函數描述:
用於獲取以太網發送幀時間戳。
函數參數:
- 第1個參數用於存儲返回的時間戳。
- 返回值,設置正確返回ARM_DRIVER_OK,設置錯誤返回ARM_DRIVER_ERROR,而ARM_DRIVER_ERROR_UNSUPPORTED表示不支持。
6.5.14 函數ControlTimer
函數原型:
static int32_t ControlTimer (uint32_t control, ARM_ETH_MAC_TIME *time) { #if (EMAC_TIME_STAMP != 0) if ((Emac.flags & EMAC_FLAG_POWER) == 0U) { return ARM_DRIVER_ERROR; } if ((control != ARM_ETH_MAC_TIMER_GET_TIME) && (control != ARM_ETH_MAC_TIMER_SET_TIME) && (control != ARM_ETH_MAC_TIMER_INC_TIME) && (control != ARM_ETH_MAC_TIMER_DEC_TIME) && (control != ARM_ETH_MAC_TIMER_SET_ALARM) && (control != ARM_ETH_MAC_TIMER_ADJUST_CLOCK)) { return ARM_DRIVER_ERROR_PARAMETER; } switch (control) { case ARM_ETH_MAC_TIMER_GET_TIME: /* 獲取當前時間 */ time->sec = ETH->PTPTSHR; time->ns = ETH->PTPTSLR; break; case ARM_ETH_MAC_TIMER_SET_TIME: /* 設置新時間*/ ETH->PTPTSHUR = time->sec; ETH->PTPTSLUR = time->ns; /* 初始TS */ ETH->PTPTSCR |= ETH_PTPTSCR_TSSTI; break; case ARM_ETH_MAC_TIMER_INC_TIME: /* 增加當前時間 */ ETH->PTPTSHUR = time->sec; ETH->PTPTSLUR = time->ns; /* 更新 */ ETH->PTPTSCR |= ETH_PTPTSCR_TSSTU; break; case ARM_ETH_MAC_TIMER_DEC_TIME: /* 減少當前時間 */ ETH->PTPTSHUR = time->sec; ETH->PTPTSLUR = time->ns | 0x80000000U; /* 更新 */ ETH->PTPTSCR |= ETH_PTPTSCR_TSSTU; break; case ARM_ETH_MAC_TIMER_SET_ALARM: /* 設置鬧鍾時間 */ ETH->PTPTTHR = time->sec; ETH->PTPTTLR = time->ns; /* 使能PTP控制中的時間戳中斷 */ ETH->PTPTSCR |= ETH_PTPTSCR_TSITE; if (time->sec || time->ns) { /* 使能時間戳觸發中斷 */ ETH->MACIMR &= ~ETH_MACIMR_TSTIM; } else { /* 禁能時間戳觸發中斷 Disable */ ETH->MACIMR |= ETH_MACIMR_TSTIM; } break; case ARM_ETH_MAC_TIMER_ADJUST_CLOCK: /* 調整當前時間,精確校准 */ /* 校准因子Q31 (0x80000000 = 1.000000000) */ ETH->PTPTSAR = (uint32_t)(((uint64_t)time->ns * ETH->PTPTSAR) >> 31); /* 精確的TS時鍾校准 */ ETH->PTPTSCR |= ETH_PTPTSCR_TSARU; break; } return ARM_DRIVER_OK; #else (void)control; (void)time; return ARM_DRIVER_ERROR; #endif }
函數描述:
高精度定時器控制。
函數參數:
- 第1個參數是高精度定時器配置選項,支持的配置如下:
- 第2個參數設置時間。
- 返回值,設置正確返回ARM_DRIVER_OK,設置錯誤返回ARM_DRIVER_ERROR,而ARM_DRIVER_ERROR_UNSUPPORTED表示不支持。
6.5.15 函數Control
函數原型:
static int32_t Control (uint32_t control, uint32_t arg) { uint32_t maccr; uint32_t dmaomr; uint32_t macffr; if ((Emac.flags & EMAC_FLAG_POWER) == 0U) { return ARM_DRIVER_ERROR; } if ((control != ARM_ETH_MAC_CONFIGURE) && (control != ARM_ETH_MAC_CONTROL_TX) && (control != ARM_ETH_MAC_CONTROL_RX) && (control != ARM_ETH_MAC_FLUSH) && (control != ARM_ETH_MAC_SLEEP) && (control != ARM_ETH_MAC_VLAN_FILTER)) { return ARM_DRIVER_ERROR_PARAMETER; } switch (control) { case ARM_ETH_MAC_CONFIGURE: maccr = ETH->MACCR & ~(ETH_MACCR_FES | ETH_MACCR_DM | ETH_MACCR_LM | ETH_MACCR_IPCO); /* 配置100Mbps或者10Mbps模式 */ switch (arg & ARM_ETH_MAC_SPEED_Msk) { case ARM_ETH_MAC_SPEED_10M: #if (ETH_MII == 0) /* RMII Half Duplex Colision detection does not work */ maccr |= ETH_MACCR_DM; #endif break; case ARM_ETH_SPEED_100M: maccr |= ETH_MACCR_FES; break; default: return ARM_DRIVER_ERROR_UNSUPPORTED; } /* 配置全雙工或者半雙工模式 */ switch (arg & ARM_ETH_MAC_DUPLEX_Msk) { case ARM_ETH_MAC_DUPLEX_FULL: maccr |= ETH_MACCR_DM; break; case ARM_ETH_MAC_DUPLEX_HALF: break; default: return ARM_DRIVER_ERROR; } /* 配置回環模式 */ if (arg & ARM_ETH_MAC_LOOPBACK) { maccr |= ETH_MACCR_LM; } dmaomr = ETH->DMAOMR & ~(ETH_DMAOMR_RSF| ETH_DMAOMR_TSF); #if (EMAC_CHECKSUM_OFFLOAD != 0) /* 使能接收校驗和驗證 */ if (arg & ARM_ETH_MAC_CHECKSUM_OFFLOAD_RX) { maccr |= ETH_MACCR_IPCO; dmaomr |= ETH_DMAOMR_RSF; } /* 使能發送校驗和產生 */ if (arg & ARM_ETH_MAC_CHECKSUM_OFFLOAD_TX) { dmaomr |= ETH_DMAOMR_TSF; Emac.tx_cks_offload = true; } else { Emac.tx_cks_offload = false; } #else if ((arg & ARM_ETH_MAC_CHECKSUM_OFFLOAD_RX) || (arg & ARM_ETH_MAC_CHECKSUM_OFFLOAD_TX)) { /* 驅動程序禁止了硬件校驗和 */ return ARM_DRIVER_ERROR; } #endif ETH->DMAOMR = dmaomr; ETH->MACCR = maccr; macffr = ETH->MACFFR & ~(ETH_MACFFR_PM | ETH_MACFFR_PAM | ETH_MACFFR_BFD); /* 使能廣播幀接收 */ if ((arg & ARM_ETH_MAC_ADDRESS_BROADCAST) == 0) { macffr |= ETH_MACFFR_BFD; } /* 使能組播幀接收 */ if (arg & ARM_ETH_MAC_ADDRESS_MULTICAST) { macffr |= ETH_MACFFR_PAM; } /* 設置無過濾,所有幀都可以接收 */ if (arg & ARM_ETH_MAC_ADDRESS_ALL) { macffr |= ETH_MACFFR_PM; } ETH->MACFFR = macffr; break; case ARM_ETH_MAC_CONTROL_TX: /* 使能或者禁止MAC發送 */ maccr = ETH->MACCR & ~ETH_MACCR_TE; dmaomr = ETH->DMAOMR & ~ETH_DMAOMR_ST; if (arg != 0) { init_dma (); maccr |= ETH_MACCR_TE; dmaomr |= ETH_DMAOMR_ST; } ETH->MACCR = maccr; ETH->DMAOMR = dmaomr; break; case ARM_ETH_MAC_CONTROL_RX: /* 使能或者禁止MAC接收 */ maccr = ETH->MACCR & ~ETH_MACCR_RE; dmaomr = ETH->DMAOMR & ~ETH_DMAOMR_SR; if (arg != 0) { init_dma (); maccr |= ETH_MACCR_RE; dmaomr |= ETH_DMAOMR_SR; } ETH->MACCR = maccr; ETH->DMAOMR = dmaomr; break; case ARM_ETH_MAC_FLUSH: /* 清空發送或者接收緩沖 */ if (arg & ARM_ETH_MAC_FLUSH_RX) { } if (arg & ARM_ETH_MAC_FLUSH_TX) { ETH->DMAOMR |= ETH_DMAOMR_FTF; } break; case ARM_ETH_MAC_VLAN_FILTER: /* 配置VLAN過濾 */ ETH->MACVLANTR = arg; break; } return ARM_DRIVER_OK; }
函數描述:
用於MAC的配置
函數參數:
- 第1個參數支持的配置如下
- 第2個參數針對第1個參數做的具體配置。
- ARM_ETH_MAC_CONFIGURE 支持的配置:
-
- ARM_ETH_MAC_CONTROL_TX
0表示禁止發送,1表示使能發送。
-
- ARM_ETH_MAC_CONTROL_RX
0表示禁止接收,1表示使能接收。
-
- ARM_ETH_MAC_FLUSH支持的配置:
ARM_ETH_MAC_FLUSH_RX 表示接收清空。
ARM_ETH_MAC_FLUSH_TX 表示發送清空。
-
- VLAN濾波器支持的配置:
6.5.16 函數PHY_Read
函數原型:
static int32_t PHY_Read (uint8_t phy_addr, uint8_t reg_addr, uint16_t *data) { uint32_t val, tick; if ((Emac.flags & EMAC_FLAG_POWER) == 0U) { return ARM_DRIVER_ERROR; } val = ETH->MACMIIAR & ETH_MACMIIAR_CR; ETH->MACMIIAR = val | ETH_MACMIIAR_MB | ((uint32_t)phy_addr << 11) | ((uint32_t)reg_addr << 6) ; /* 等待操作完成 */ tick = HAL_GetTick(); do { if ((ETH->MACMIIAR & ETH_MACMIIAR_MB) == 0U) { break; } } while ((HAL_GetTick() - tick) < PHY_TIMEOUT); if ((ETH->MACMIIAR & ETH_MACMIIAR_MB) == 0U) { *data = ETH->MACMIIDR & ETH_MACMIIDR_MD; return ARM_DRIVER_OK; } return ARM_DRIVER_ERROR_TIMEOUT; }
函數描述:
用於以太網PHY芯片的讀操作。
函數參數:
- 第1個參數是PHY地址。
- 第2個參數是寄存器地址。
- 第3個參數是寄存器寫入的數據。
- 返回值,操作正確返回ARM_DRIVER_OK,操作錯誤返回ARM_DRIVER_ERROR。
6.5.17 函數PHY_Write
函數原型:
static int32_t PHY_Write (uint8_t phy_addr, uint8_t reg_addr, uint16_t data) { uint32_t val, tick; if ((Emac.flags & EMAC_FLAG_POWER) == 0U) { return ARM_DRIVER_ERROR; } ETH->MACMIIDR = data; val = ETH->MACMIIAR & ETH_MACMIIAR_CR; ETH->MACMIIAR = val | ETH_MACMIIAR_MB | ETH_MACMIIAR_MW | ((uint32_t)phy_addr << 11) | ((uint32_t)reg_addr << 6) ; /* 等待操作完成 */ tick = HAL_GetTick(); do { if ((ETH->MACMIIAR & ETH_MACMIIAR_MB) == 0U) { break; } } while ((HAL_GetTick() - tick) < PHY_TIMEOUT); if ((ETH->MACMIIAR & ETH_MACMIIAR_MB) == 0U) { return ARM_DRIVER_OK; } return ARM_DRIVER_ERROR_TIMEOUT; }
函數描述:
用於以太網PHY芯片的寫操作。
函數參數:
- 第1個參數是PHY地址。
- 第2個參數是寄存器地址。
- 第3個參數是寄存器寫入的數據。
- 返回值,操作正確返回ARM_DRIVER_OK,操作錯誤返回ARM_DRIVER_ERROR。
6.6 總結
本章節就為大家講解這么多,主要是為學習下個章節RL-TCPnet的移植做准備。學完本章后,務必將STM32參考手冊中MAC章節讀一遍。