📄 lib_emac.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 + -