?? lib_emac.c
字號:
//*------------------------------------------------------------------------------------------------
//* 文件名 : lib_emac.c
//* 功能描述 : EMAC外設的函數庫
//* 作者 : 焦海波
//* 版本 : 0.1
//* 建立日期、時間 : 2006/06/08 10:17
//* 最近修改日期、時間 :
//* 修改原因 :
//*------------------------------------------------------------------------------------------------
//*------------------------------------------ 頭文件 -----------------------------------------------
#include "/uCOS_II/includes.h"
#include "/at91sam7x256/include/AT91SAM7X256.h"
#include "/at91sam7x256/include/lib_AT91SAM7X256.h"
#include "/LwIP/include/lwip/opt.h"
#include "lib_emac.h"
//*------------------------------------- 常量、變量、宏 --------------------------------------------
//* 因為接收緩沖區描述符字0的2到31位保存緩沖區地址,最低兩位有其它用處,所以指定4字節對齊,以保證緩沖區地址
//* 的最低兩位為0
__align(4) static volatile BYTE baRxBufs[NB_RX_BUFS][ETH_RX_BUF_SIZE]; //* 接收緩沖區
static BYTE baTxBufs[NB_TX_BUFS][ETH_TX_BUF_SIZE]; //* 發送緩沖區
/* 因為緩沖區描述符長度為雙字,正好8個字節,所以指定雙字對齊 */
__align(8) static volatile AT91S_RxBDescriptor __staRxBDescriptors[NB_RX_BUFS]; //* 接收緩沖區描述符數組
__align(8) static volatile AT91S_TxBDescriptor __staTxBDescriptors[NB_TX_BUFS]; //* 發送緩沖區描述符數組
//* 保存要讀取的接收緩沖區的索引位置
static UWORD __uwCurRxBIdx = 0;
static BYTE *__pbFrom;
//*-------------------------------------- 函數原型聲明 ---------------------------------------------
static void __HandlePHY(UBYTE ubRegAddr, UWORD *puwData, BOOLEAN blIsRead);
static void __ResetPHY(void);
static void __CheckPHYID(void);
static void __SetupLinkSpeedAndDuplex(void);
static void __InitDescriptorsForRxBAndTxB(void);
__inline void __ilResetTxBDescriptors(void);
//*================================================================================================
//* 函 數 區
//*================================================================================================
//*------------------------------------------------------------------------------------------------
//* 函數名稱 : irqEMACISR
//* 功能描述 : EMAC發送和接收結束中斷處理函數
//* 入口參數 : 無
//* 出口參數 : 無
//*------------------------------------------------------------------------------------------------
__irq void irqEMACISR(void)
{
extern HANDLER hEthernetInput;
ULONG __ulIntStatus, __ulReceiveStatus;
OSIntEnter();
{
//* 在讀取時中斷狀態寄存器位會被清除
__ulIntStatus = AT91C_BASE_EMAC->EMAC_ISR;
//* 這個是容易忽略的地方,只有讀取RSR寄存器,EMAC中斷處理才能在寫EOICR寄存器之后真正結束
__ulReceiveStatus = AT91C_BASE_EMAC->EMAC_RSR;
if((__ulIntStatus & AT91C_EMAC_RCOMP) || (__ulReceiveStatus & AT91C_EMAC_REC))
{
//* 向EMAC接收任務發送信號
OSAPISemSend(hEthernetInput);
//* 清除REC(Frame Received)位
AT91C_BASE_EMAC->EMAC_RSR = AT91C_EMAC_REC;
}
if(__ulIntStatus & AT91C_EMAC_TCOMP)
{
//* 復位發送緩沖區描述符的Used位使其為程序所有
__ilResetTxBDescriptors();
AT91C_BASE_EMAC->EMAC_TSR = AT91C_EMAC_COMP;
}
//* 清除中斷標志,結束中斷處理
AT91C_BASE_AIC->AIC_EOICR = 0;
}
OSIntExit();
}
//*------------------------------------------------------------------------------------------------
//* 函數名稱 : EMACSendPacket
//* 功能描述 : 由__low_level_output()函數調用。其實現過程如下:首先我們知道,要發送的這一幀數據保存在pbuf
//* : 鏈中,每一個pbuf節點保存一部分數據,它們通過pbuf->next指針順序鏈接在一起。其上層調用函數
//* : __low_level_output()使用一個for循環將這些pbuf通過pbFrom參數傳遞過來。EMACSendPacket()函數
//* : 將每一個pbuf節點的數據長度與當前使用的發送緩沖區長度進行比較,如果其數據長度超出發送緩沖區長
//* : 度則將其分割保存在多個發送緩沖區中。然后判斷當前處理的pbuf是否已經到了鏈表的末尾,也就是
//* : pbuf->next為空(通過blIsEndOfFrame參數傳遞)。如果到了末尾并且數據復制完畢,則置位當前緩沖
//* : 區描述符的Last Buffer位,沒有,則只需將當前緩沖區中保存的實際數據長度填充到緩沖區描述符中即
//* : 可。如果我們在多次調用EMACSendPacket()函數之后到達了緩沖區隊列的末尾,也就是函數中使用的私有
//* : 靜態變量__uwTxBIndex其索引計數等于發送緩沖區個數減1(即,__uwTxBIndex == (NB_TX_BUFS-1))
//* : ,則索引計數立即復位為0,從緩沖區隊列的開始位置繼續保存剩余數據。同時,置位當前緩沖區描述符的
//* : Wrap位,以使發送緩沖區隊列指針寄存器能夠在數據發送時回到隊列開始位置以順利找到剩余數據。注意,
//* : 置位緩沖區描述符的Wrap位并不會使EMAC認為已經到達幀尾,從而停止發送,只有Last Buffer位置位才行。
//* : 上述過程處理完畢,最后,如果標記了Last Buffer則立即啟動傳輸(置位網絡控制寄存器NCR的位9)。在
//* : 這里需要進一步說明的是:假設發送緩沖區隊列存在10個緩沖區,在發送第一幀數據時(實際上就是調用了
//* : 一次__low_level_output()函數)我只使用了7個緩沖區,這時__uwTxBIndex索引計數就是8,其指向了下
//* : 一個可用緩沖區,同樣在數據發送完畢后,發送緩沖區隊列指針寄存器(EMAC_TBQP)也指向了該位置。我
//* : 需要再發送一幀數據時,同樣是再次調用__low_level_output()函數。現在要發送的這幀數據需要占用10
//* : 個緩沖區隊列。EMACSendPacket()函數在復制數據到緩沖區時,__uwTxBIndex索引增加到了10,也就是到
//* : 緩沖區末尾的時候,前7個緩沖區并沒有被使用,所以函數將__uwTxBIndex縮影寵幸復位為0,剩余數據被保
//* : 到了前面。同樣,按照EMAC的數據手冊,置位Wrap位的目的就是使EMAC_TBQP寄存器重新指向發送隊列開始
//* : 位置。從上面的描述我們還看出,發送隊列一定要大于或等于幀的最大長度,否則就會出現尾部數據覆蓋頭
//* : 部數據的問題。
//* 入口參數 : <pbFrom>[in] 指向pbuf中數據的指針
//* : <ulLength>[in] pbuf中的數據長度
//* : <blIsEndOfFrame>[in] 是否是pbuf鏈中的最后一個,也就是幀尾
//* 出口參數 : 如果無法申請下內存則返回ERR_MEM,成功則返回ERR_OK
//*------------------------------------------------------------------------------------------------
BOOLEAN EMACSendPacket(BYTE *pbFrom, ULONG ulLength, BOOLEAN blIsEndOfFrame)
{
ULONG __ulTotalLenToWrite = 0, __ulCurLenToWrite, __ulLenRemainToWrite, __ulIsLastBuf;
//* 注意這是一個私有的靜態變量
static UWORD __uwTxBIndex = 0;
LONG i;
BYTE *__pbBuf;
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr = 0;
#endif
//* 如果要發送的數據長度大于一個發送緩沖區,則需要將這些數據分割進多個緩沖區進行發送
while(__ulTotalLenToWrite < ulLength)
{
//* 等待緩沖區可用,最長等待3秒鐘
i = 0;
while(!__staTxBDescriptors[__uwTxBIndex].uStatus.bstStatus.bitIsUsed)
{
//* 如果已經到達等待時間仍然沒有可用緩沖區,則立即返回
if(i > 300)
return FALSE;
OSTimeDly(1);
i++;
}
OS_ENTER_CRITICAL()
{
//* 從描述符中獲得緩沖區地址,然后把數據復制到緩沖區
__pbBuf = (BYTE*)__staTxBDescriptors[__uwTxBIndex].ulTxBAddr;
//* 計算向緩沖區寫入的數據長度
__ulLenRemainToWrite = ulLength - __ulTotalLenToWrite;
if(__ulLenRemainToWrite > ETH_TX_BUF_SIZE)
__ulCurLenToWrite = ETH_TX_BUF_SIZE;
else
__ulCurLenToWrite = __ulLenRemainToWrite;
//* 將pbuf中的數據復制到發送緩沖區
memcpy(__pbBuf, &(pbFrom[__ulTotalLenToWrite]), __ulCurLenToWrite);
__ulTotalLenToWrite += __ulCurLenToWrite;
//* 看看是否是已經到達pbuf鏈的末尾,如果是則標記當前使用的緩沖區為最后一個緩沖區
if(blIsEndOfFrame && (__ulTotalLenToWrite >= ulLength))
{
__ulIsLastBuf = TxDESC_STATUS_LAST_BUF;
}
else
__ulIsLastBuf = 0;
//* 填充當前的描述符:緩沖區中的數據實際長度、最后一個緩沖區標記、WRAP位(如果確實是最后一個描述符)
if(__uwTxBIndex >= (NB_TX_BUFS-1))
{
__staTxBDescriptors[__uwTxBIndex].uStatus.ulStatus = (__ulCurLenToWrite & TxDESC_STATUS_BUF_SIZE)
| __ulIsLastBuf
| TxDESC_STATUS_WRAP;
__uwTxBIndex = 0;
}
else
{
__staTxBDescriptors[__uwTxBIndex].uStatus.ulStatus = (__ulCurLenToWrite & TxDESC_STATUS_BUF_SIZE)
| __ulIsLastBuf;
__uwTxBIndex++;
}
//* 如果已經到達pbuf鏈的末尾則立即發送
if(__ulIsLastBuf)
AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TSTART;
}
OS_EXIT_CRITICAL()
}
return TRUE;
}
//*------------------------------------------------------------------------------------------------
//* 函數名稱 : EMACReadPacket
//* 功能描述 : 從EMAC讀取信息包到申請的pbuf鏈,該函數要求PBUF_POOL_BUFSIZE大于或等于接收緩沖區,最好是整
//* : 數倍,這樣函數處理最簡單。注意,在調用該函數之前必須先調用GetInputPacketLen()函數,這樣才
//* : 能獲取正確的讀取位置
//* 入口參數 : <pbTo>[in] 指向pbuf的指針
//* : <uwSegmentLen>[in] pbuf中需要存儲的實際數據長度,因為在申請pbuf時,pbuf_alloc()已經根據
//* : 實際的幀長將其分割進了多個pbuf組成的pbuf鏈中,而pstPbuf->len則保存了
//* : 每個pbuf需要存儲的數據。換句話說pbuf鏈中的最后一個pbuf的len字段長度應
//* : 該小于或等于PBUF_POOL_BUFSIZE,而前面的pbuf則等于PBUF_POOL_BUFSIZE
//* : <blIsLastPbuf>[in] 是否是最后一個Pbuf
//* 出口參數 : 無
//*------------------------------------------------------------------------------------------------
void EMACReadPacket(BYTE *pbTo, UWORD uwSegmentLen, BOOLEAN blIsLastPbuf)
{
UWORD __uwTotalLenToRead = 0, //* 已經讀取到pbuf的總字節數
__uwRemainLenToRead, //* 還剩多少字節沒有讀取到pbuf中
__uwRemainLenInRxBToRead; //* EMAC接收緩沖區中還剩下多少字節沒有讀取
static UWORD __uwTotalLenInRxBToRead = 0; //* EMAC接收緩沖區中已經讀了多少字節的數據
BOOLEAN __blIsNotRelease; //* 是否已經主動釋放給EMAC
/* 注意,必須保證PBUF_POOL_BUFSIZE大于或等于接收緩沖區 */
while(__uwTotalLenToRead < uwSegmentLen)
{
__uwRemainLenInRxBToRead = ETH_RX_BUF_SIZE - __uwTotalLenInRxBToRead;
__uwRemainLenToRead = uwSegmentLen - __uwTotalLenToRead;
if(__uwRemainLenToRead >= __uwRemainLenInRxBToRead)
{
memcpy(pbTo + __uwTotalLenToRead, __pbFrom, __uwRemainLenInRxBToRead);
__uwTotalLenToRead += __uwRemainLenInRxBToRead;
__uwTotalLenInRxBToRead = 0;
//* 將接收緩沖區歸還給EMAC
__staRxBDescriptors[__uwCurRxBIdx].ulRxBAddrAndFlag &= (~RxDESC_FLAG_OWNSHIP);
__blIsNotRelease = FALSE;
//* 調整描述符索引與讀取指針
__uwCurRxBIdx++;
if(__uwCurRxBIdx >= NB_RX_BUFS)
__uwCurRxBIdx = 0;
__pbFrom = (BYTE*)(__staRxBDescriptors[__uwCurRxBIdx].ulRxBAddrAndFlag & EMAC_RxB_ADDR_MASK);
}
else
{
memcpy(pbTo + __uwTotalLenToRead, __pbFrom, __uwRemainLenToRead);
__uwTotalLenToRead += __uwRemainLenToRead;
__uwTotalLenInRxBToRead += __uwRemainLenToRead;
__pbFrom = __pbFrom + __uwTotalLenInRxBToRead;
__blIsNotRelease = TRUE;
}
}
if(blIsLastPbuf)
{
//* 將接收緩沖區歸還給EMAC,如果存在恰好是PBUF_POOL_BUFSIZE的整數倍的數據包,則沒有必要再一次釋放,因為它已經在
//* 上面被釋放
if(__blIsNotRelease)
{
__staRxBDescriptors[__uwCurRxBIdx].ulRxBAddrAndFlag &= (~RxDESC_FLAG_OWNSHIP);
__uwCurRxBIdx++;
if(__uwCurRxBIdx >= NB_RX_BUFS)
__uwCurRxBIdx = 0;
}
__uwTotalLenInRxBToRead = 0;
}
}
//*------------------------------------------------------------------------------------------------
//* 函數名稱 : EMACInit
//* 功能描述 : 初始化EMAC:對PHY、MII口線、EMAC操作模式進行配置,設置接收和發送緩沖區描述符。設置EMAC接收
//* : 和發送中斷
//* 入口參數 : 無
//* 出口參數 : 無
//*------------------------------------------------------------------------------------------------
void EMACInit(void)
{
extern HANDLER hEthernetInput;
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr = 0;
#endif
//* 復位PHY芯片,使其進入UTP模式
__ResetPHY();
//* 等待一段指定的時間,使PHY就緒
OSTimeDlyHMSM(0, 0, 3, 0);
//* 設置PIOB引腳為外設A引腳(即EMAC引腳),禁止PIOB控制,改為外設控制
AT91C_BASE_PIOB->PIO_ASR = EMAC_MII_PINS;
AT91C_BASE_PIOB->PIO_PDR = EMAC_MII_PINS;
//* 因為沒有使用ETXER,所以這里將其配置為由PIO控制
AT91C_BASE_PIOB->PIO_PER = AT91C_PB12_ETXER;
AT91C_BASE_PIOB->PIO_ODR = AT91C_PB12_ETXER;
//* 設置MDC時鐘分頻數
AT91C_BASE_EMAC->EMAC_NCFGR |= AT91C_EMAC_CLK_HCLK_32;
//* 檢查PHY ID是否為0x82010000,如果不是則表明PHY還沒有就緒或者出現故障,函數將一直查詢直至正確
__CheckPHYID();
//* 從PHY獲取自動協商的結果,設置EMAC自身的鏈路速度和單雙工方式。注意,該函數會阻塞所在任務的正常執行
//* 直至設置成功
__SetupLinkSpeedAndDuplex();
//* 建立接收任務使用的信號量,對uCOS的配置保證信號量在軟件邏輯上能夠百分百建立成功
hEthernetInput = OSAPISemNew(0);
//* 初始化接收和發送緩沖區描述符,使每個描述符指向正確的緩沖區地址
__InitDescriptorsForRxBAndTxB();
//* 設置EMAC為MII模式,使能EMAC時鐘
AT91C_BASE_EMAC->EMAC_USRIO = AT91C_EMAC_CLKEN;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -