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

📄 lib_emac.c

📁 基于sam7x256平台做的CAN与网口互转接口
💻 C
字号:

/////////////*** 与err有关的变量在net_err.h中定义 ********///////////////////

//*------------------------------------------ 头文件 ----------------------------------------------
#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"
#include	"lib_aic.h"
#include	"/LwIP/include/lwip/err.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 NetBSP_Phy_HW_Init (void); 
static ULONG  BSP_CPU_ClkFreq (void);
static void  NetNIC_PhyAutoNeg (NET_ERR *perr);
static BOOLEAN  NetNIC_PhyLinkState (NET_ERR *perr);
static UWORD  NetNIC_PhyRegRd (UBYTE  phy,
                             UBYTE  reg,
                             NET_ERR    *perr);

 static void  NetNIC_PhyRegWr (UBYTE  phy,
                       UBYTE  reg,
                       UWORD  val,
                       NET_ERR    *perr);
static void __InitDescriptorsForRxBAndTxB(void);
static LONG NetPHY_GetLinkDuplex (NET_ERR *perr);
static void NetBSP_EMAC_Settings_Update (ULONG link_speed, ULONG link_duplex);
static void  NetNIC_PhyInit (NET_ERR *perr);
static LONG NetPHY_GetLinkSpeed (NET_ERR *perr);
__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()函数调用,完成实际的数据发送。
//* 入口参数 :         <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(NET_ERR  *perr)
{
	extern HANDLER hEthernetInput;
	ULONG  MCK_frq;
	ULONG  link_speed;
    ULONG  link_duplex;
	
#if OS_CRITICAL_METHOD == 3                     
    OS_CPU_SR  		cpu_sr = 0;
#endif
    
     /*完成了resetPHY()和引脚配置的工作等同于步骤1和2,同时又设置了18引脚的功能 18引脚在焦中未出现*/
    NetBSP_Phy_HW_Init();     	/*注:此函数未声明*/
	
	/*等待一段时间使PHY就绪*/
	OSTimeDlyHMSM(0, 0, 3, 0); 
	
	/*使能EMAC管理端口*/
	AT91C_BASE_EMAC->EMAC_NCR   |=  AT91C_EMAC_MPE;
	
	/*设置MDC分频*/
	 MCK_frq                      =  BSP_CPU_ClkFreq();                  /* Get the MCK frequency                                */

    if (MCK_frq        <= 20000) {                                      /* If MCK <= 20MHZ, set MDC to MCK / 8                  */
        MCK_frq                  =  AT91C_EMAC_CLK_HCLK_8;
    } else if (MCK_frq <= 40000) {                                      /* If 20MHZ < MCK <= 40MHZ, set MDC to MCK / 16         */
        MCK_frq                  =  AT91C_EMAC_CLK_HCLK_16;
    } else if (MCK_frq <= 80000) {                                      /* If 40MHZ < MCK <= 80MHZ, set MDC to MCK / 32         */
        MCK_frq                  =  AT91C_EMAC_CLK_HCLK_32;
    } else {                                                            /* If 80MHZ < MCK <= 160MHZ, set MDC to MCK / 64        */
        MCK_frq                  =  AT91C_EMAC_CLK_HCLK_64;
    }

     AT91C_BASE_EMAC->EMAC_NCFGR  =  MCK_frq;                            /* Set the MDC CLk divider */
    
    /* 复制所有有效帧到接收缓冲区,不接收广播帧,不复制FCS字段*/
	AT91C_BASE_EMAC->EMAC_NCFGR |= AT91C_EMAC_CAF | AT91C_EMAC_NBC | AT91C_EMAC_DRFCS;
	
	/*自动协商*/
    NetNIC_PhyInit(perr);/*注:入口参数peer问题 此函数未声明*/
   
    /*此函数可以不要设置变量NetNIC_ConnStatus为真
    NetNIC_LinkUp()
    */
    
    /*设置MII模式*/
    #ifdef RMII
    AT91C_BASE_EMAC->EMAC_USRIO    =   AT91C_EMAC_RMII | (0x02);        /* Enable RMII mode and RMII clk on ERFCK               */
#else
    AT91C_BASE_EMAC->EMAC_USRIO    =  (0x02);                           /* Enable MII mode, and clk ERXCK and ETXCK  */ 
  
#endif
    /* 从PHY获取自动协商的结果,设置EMAC自身的链路速度和单双工方式。注意,该步骤会阻塞所在任务的正常执行
	直至设置成功*/
	link_speed                     =   NetPHY_GetLinkSpeed(perr);       /* Read the PHY's current link speed                    */
    link_duplex                    =   NetPHY_GetLinkDuplex(perr);      /* Read the PHY's current link duplex mode              */

    NetBSP_EMAC_Settings_Update (link_speed, link_duplex);              /*设置EMAC速度和单双工*/
    
    //* 建立接收任务使用的信号量,对uCOS的配置保证信号量在软件逻辑上能够百分百建立成功
	hEthernetInput = OSAPISemNew(0);
	
	//* 初始化接收和发送缓冲区描述符,使每个描述符指向正确的缓冲区地址
	__InitDescriptorsForRxBAndTxB();
	
	//* 清除接收状态寄存器
	AT91C_BASE_EMAC->EMAC_RSR = AT91C_EMAC_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA;
	
	//* 复制所有有效帧到接收缓冲区,不接收广播帧,不复制FCS字段
	AT91C_BASE_EMAC->EMAC_NCFGR |= AT91C_EMAC_CAF | AT91C_EMAC_NBC | AT91C_EMAC_DRFCS;
	
	//* 注:需要修改!设置EMAC地址
	AT91C_BASE_EMAC->EMAC_SA1L = 0xBC | ((ULONG)(0x20 << 8)) | ((ULONG)(0x06 << 16)) | ((ULONG)(0x09 << 24));
	AT91C_BASE_EMAC->EMAC_SA1H = 0x30 | ((ULONG)(0x11 << 8));
	
	//* 设置EMAC中断
	OS_ENTER_CRITICAL()
	{
		//* 发送和接收结束中断使能
		AT91C_BASE_EMAC->EMAC_IER = AT91C_EMAC_RCOMP | AT91C_EMAC_TCOMP;
		
 		at91_irq_open(AT91C_ID_EMAC, AT91C_AIC_PRIOR_HIGHEST, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, irqEMACISR);
	}
	OS_EXIT_CRITICAL()
	
	//* 最后,接收、发送使能
	AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TE | AT91C_EMAC_RE;
	
	
	
}

/*
*********************************************************************************************************
*函数名称

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -