📄 lib_emac.c
字号:
//*------------------------------------------------------------------------------------------------
//* 文件名 : lib_emac.c
//* 功能描述 : EMAC外设的函数库
//* 作者 : 焦海波
//* 版本 : 0.1
//* 建立日期、时间 : 2006/10/08 10:25
//* 最近修改日期、时间 :
//* 修改原因 :
//*------------------------------------------------------------------------------------------------
//*------------------------------------------ 头文件 ----------------------------------------------
#include "/uCOS-II/ucos_ii.h"
#include "/at91sam7x256/include/AT91SAM7X256.h"
#include "/at91sam7x256/include/lib_AT91SAM7X256.h"
#include "/LwIP/include/lwip/opt.h"
#include "lib_emac.h"
//*------------------------------------- 常量、变量、宏 -------------------------------------------
__align(8) static volatile AT91S_RxBDescriptor __staRxBDescriptors[NB_RX_BUFS]; //* 接收缓冲区描述符数组
__align(4) static volatile INT8S baRxBufs[NB_RX_BUFS][ETH_RX_BUF_SIZE]; //* 接收缓冲区
__align(8) static volatile AT91S_TxBDescriptor __staTxBDescriptors[NB_TX_BUFS]; //* 发送缓冲区描述符数组
static INT8S baTxBufs[NB_TX_BUFS][ETH_TX_BUF_SIZE]; //* 发送缓冲区
static INT32S __s32IdxToReset; //* EMAC发送缓冲区队列发送位置索引
//* 保存要读取的接收缓冲区的索引位置
static INT16U __u16CurRxBIdx;
static INT8S *__pbFrom;
static INT16U __u16TotalLenInRxBToRead; //* EMAC接收缓冲区中已经读了多少字节的数据
static INT16U __u16TxBIndex; //* 对已使用的发送缓冲区进行索引计数
//*-------------------------------------- 函数原型声明 --------------------------------------------
//*================================================================================================
//* 函 数 区
//*================================================================================================
//*------------------------------------------------------------------------------------------------
//* 函数名称 : __ResetPHY
//* 功能描述 : 复位PHY芯片以使PHY芯片进入UTP模式
//* 入口参数 : 无
//* 出口参数 : 无
//*------------------------------------------------------------------------------------------------
static void __ResetPHY(void)
{
//* PHY在上电或复位期间需要根据RXER/FXEN引脚(24脚)的锁存输入状态来选择是UTP模式还是光纤模式。每个PIO
//* 口线都被内置了一个上拉电阻,所以整机上电后该引脚的逻辑电平为高,这样就会使得PHY进入了光纤模式。我们
//* 需要UTP模式,也就是PHY上电或复位期间的锁存输入状态为低,所以在这里必须禁止该口线的内部上拉电阻,然后
//* 再复位PHY芯片,使其进入UTP模式
AT91C_BASE_PIOB->PIO_PPUDR = AT91C_PB7_ERXER;
//* 复位PHY芯片,RTL8201BL的数据手册要求必须维持至少10ms的低电平,这里设置为2的(8 + 1)次方个SCK周期,
//* 时间为15.621ms
AT91C_BASE_RSTC->RSTC_RMR = 0xA5000000 | (0x08 << 8);
AT91C_BASE_RSTC->RSTC_RCR = 0xA5000000 | AT91C_RSTC_EXTRST;
//* 等待NRST引脚恢复为高电平
while(!(AT91C_BASE_RSTC->RSTC_RSR & AT91C_RSTC_NRSTL));
}
//*------------------------------------------------------------------------------------------------
//* 函数名称 : __HandlePHY
//* 功能描述 : 对PHY进行读写操作
//* 入口参数 : <u8RegAddr>[in] 指定对PHY的哪个寄存器进行读写
//* : <pu16Data>[in/out] 指向操作数据的指针。对于读,这个地址保存读取的数据;对于写则是要写入
//* : 寄存器的数据
//* : <blIsRead>[in] 是否是读操作,如果不是则是写操作
//* 出口参数 : 无
//*------------------------------------------------------------------------------------------------
static void __HandlePHY(INT8U u8RegAddr, INT16U *pu16Data, BOOLEAN blIsRead)
{
INT32U __u32HandleVal;
if(blIsRead)
{
__u32HandleVal = (0x01 << 30)
| (0x02 << 28)
| (PHY_ADDR << 23)
| (u8RegAddr << 18)
| (0x02 << 16);
}
else
{
__u32HandleVal = (0x01 << 30)
| (0x01 << 28)
| (PHY_ADDR << 23)
| (u8RegAddr << 18)
| (0x02 << 16)
| (*pu16Data & 0xFFFF);
}
AT91C_BASE_EMAC->EMAC_MAN = __u32HandleVal;
while(!(AT91C_BASE_EMAC->EMAC_NSR & AT91C_EMAC_IDLE));
if(blIsRead)
*pu16Data = AT91C_BASE_EMAC->EMAC_MAN & 0x0000FFFF;
}
//*------------------------------------------------------------------------------------------------
//* 函数名称 : __CheckPHYID
//* 功能描述 : 检查PHY ID是否为0x82010000,如果不是则表明PHY还没有就绪或者出现故障,函数将一直查询直至正确
//* 入口参数 : 无
//* 出口参数 : 无
//*------------------------------------------------------------------------------------------------
static void __CheckPHYID(void)
{
INT32U __u32PHYID;
INT8U __u8DelaySeconds = 0;
macEnableMDI()
{
while(OS_TRUE)
{
__HandlePHY(PHY_REG_ID1, (INT16U*)&__u32PHYID, OS_TRUE);
__HandlePHY(PHY_REG_ID2, ((INT16U*)&__u32PHYID)+1, OS_TRUE);
if(__u32PHYID == PHY_ID_RTL8201)
break;
else
{
__ResetPHY();
if(__u8DelaySeconds < 15)
__u8DelaySeconds++;
OSTimeDlyHMSM(0, 0, __u8DelaySeconds, 0);
}
}
}
macDisableMDI()
}
//*------------------------------------------------------------------------------------------------
//* 函数名称 : __ilWaitLinkEstablished
//* 功能描述 : 等待PHY建立实际的物理链路
//* 入口参数 : 无
//* 出口参数 : 无
//*------------------------------------------------------------------------------------------------
__inline void __ilWaitLinkEstablished(void)
{
INT16U __u16HandleData;
INT8U __u8DelaySeconds = 0;
while(OS_TRUE)
{
__HandlePHY(PHY_REG_BMSR, &__u16HandleData, OS_TRUE);
if(__u16HandleData & PHY_BMSR_LINKESTABLISHED)
break;
else
{
__ResetPHY();
if(__u8DelaySeconds < 15)
__u8DelaySeconds++;
OSTimeDlyHMSM(0, 0, __u8DelaySeconds, 0);
}
}
}
//*------------------------------------------------------------------------------------------------
//* 函数名称 : __ilWaitAutoNegEnd
//* 功能描述 : 等待PHY自动协商结束
//* 入口参数 : 无
//* 出口参数 : 无
//*------------------------------------------------------------------------------------------------
__inline void __ilWaitAutoNegEnd(void)
{
INT16U __u16HandleData = 0;
do{
if(__u16HandleData & PHY_BMSR_AUTONEGEND)
break;
else
{
__HandlePHY(PHY_REG_BMSR, &__u16HandleData, OS_TRUE);
OSTimeDlyHMSM(0, 0, 1, 0);
}
}while(OS_TRUE);
}
//*------------------------------------------------------------------------------------------------
//* 函数名称 : __SetupLinkSpeedAndDuplex
//* 功能描述 : 从PHY获取自动协商的结果,设置EMAC自身的链路速度和单双工方式。注意,该函数会阻塞所在任务的正
//* : 常执行直至设置成功
//* 入口参数 : 无
//* 出口参数 : 无
//*------------------------------------------------------------------------------------------------
static void __SetupLinkSpeedAndDuplex(void)
{
INT16U __u16HandleData;
INT32U __u32SpdAndFD = 0x00000000;
macEnableMDI()
{
__ilWaitLinkEstablished();
__ilWaitAutoNegEnd();
//* 获得协商的结果
__HandlePHY(PHY_REG_ANLPAR, &__u16HandleData, OS_TRUE);
//* 决定线速
if((__u16HandleData & PHY_ANLPAR_100TX) || (__u16HandleData & PHY_ANLPAR_100TXFD))
__u32SpdAndFD = AT91C_EMAC_SPD;
//* 决定单双工方式
if((__u16HandleData & PHY_ANLPAR_100TXFD) || (__u16HandleData & PHY_ANLPAR_10TFD))
__u32SpdAndFD |= AT91C_EMAC_FD;
//* 将链路速度和单双工方式设置进EMAC的网络配置寄存器
AT91C_BASE_EMAC->EMAC_NCFGR = (AT91C_BASE_EMAC->EMAC_NCFGR & EMAC_NCFGR_SPD_FD_MASK) | __u32SpdAndFD;
}
macDisableMDI()
}
//*------------------------------------------------------------------------------------------------
//* 函数名称 : __InitDescriptorsForRxBAndTxB
//* 功能描述 : 初始化接收和发送缓冲区描述符,使每个描述符指向正确的缓冲区地址,然后将描述符首地址写入队列指
//* : 针寄存器
//* 入口参数 : 无
//* 出口参数 : 无
//*------------------------------------------------------------------------------------------------
static void __InitDescriptorsForRxBAndTxB(void)
{
INT32S i;
//* 将接收缓冲区地址填充到缓冲区描述符
for(i=0; i<NB_RX_BUFS; i++)
__staRxBDescriptors[i].u32RxBAddrAndFlag = (int)baRxBufs[i];
//* 置位最后一个缓冲区描述符的Wrap位
__staRxBDescriptors[NB_RX_BUFS - 1].u32RxBAddrAndFlag |= RxDESC_FLAG_WARP;
//* 将发送缓冲区地址填充到缓冲区描述符
for(i=0; i<NB_TX_BUFS; i++)
{
__staTxBDescriptors[i].u32TxBAddr = (int)baTxBufs[i];
//* 标记这个缓冲区为程序所有,根据数据手册,该位为0表示这个缓冲区为EMAC所有
__staTxBDescriptors[i].uStatus.bstStatus.bitIsUsed = 1;
}
//* 置位发送缓冲区的结束位(Wrap)
__staTxBDescriptors[NB_TX_BUFS - 1].uStatus.bstStatus.bitIsWrap = 1;
//* 将描述符队列首地址写入接收和发送队列指针寄存器
AT91C_BASE_EMAC->EMAC_RBQP = (INT32U)__staRxBDescriptors;
AT91C_BASE_EMAC->EMAC_TBQP = (INT32U)__staTxBDescriptors;
//* 初始化与缓冲区队列相关的私有静态变量
__s32IdxToReset = 0;
__u16CurRxBIdx = 0;
__u16TotalLenInRxBToRead = 0;
__u16TxBIndex = 0;
}
//*------------------------------------------------------------------------------------------------
//* 函数名称 : __ilResetTxBDescriptors
//* 功能描述 : 复位发送缓冲区描述符的Used位,使其能够继续被使用
//* 入口参数 : 无
//* 出口参数 : 无
//*------------------------------------------------------------------------------------------------
__inline void __ilResetTxBDescriptors(void)
{
//* 根据EMAC数据手册,EMAC在发送完毕后会置位帧的第一个缓冲区描述符的Used位,所以在这里只需置位其它描
//* 述符即可
if(__staTxBDescriptors[__s32IdxToReset].uStatus.bstStatus.bitIsUsed)
{
while(!__staTxBDescriptors[__s32IdxToReset].uStatus.bstStatus.bitIsLastBuf)
{
__s32IdxToReset++;
if(__s32IdxToReset >= NB_TX_BUFS)
__s32IdxToReset = 0;
__staTxBDescriptors[__s32IdxToReset].uStatus.bstStatus.bitIsUsed = 1;
}
__s32IdxToReset++;
if(__s32IdxToReset >= NB_TX_BUFS)
__s32IdxToReset = 0;
}
else;
}
//*------------------------------------------------------------------------------------------------
//* 函数名称 : irqEMACISR
//* 功能描述 : EMAC发送和接收结束中断处理函数
//* 入口参数 : 无
//* 出口参数 : 无
//*------------------------------------------------------------------------------------------------
void irqEMACISR(void)
{
extern HANDLER hEthernetInput;
INT32U __u32IntStatus, __u32ReceiveStatus;
//* 在读取时中断状态寄存器位会被清除
__u32IntStatus = AT91C_BASE_EMAC->EMAC_ISR;
//* 这个是容易忽略的地方,只有读取RSR寄存器,EMAC中断处理才能在写EOICR寄存器之后真正结束
__u32ReceiveStatus = AT91C_BASE_EMAC->EMAC_RSR;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -