⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lib_emac.c

📁 《嵌入式网络系统设计-基于Atmel ARM7 系列》这个本书的光盘资料
💻 C
📖 第 1 页 / 共 2 页
字号:
//*------------------------------------------------------------------------------------------------
//* 文件名				: 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 + -