LPC1768以太網控制器

LPC1768以太網控制器
本文記錄LPC1768的以太網控制器學習使用心得。網卡芯片以DP83848C爲例。主芯片內的以太網控制器(EMAC)位於ISO模型中的數據鏈路層,而外圍網卡(PHY)位於ISO模型中的物理層。這兩者通過RMII(簡化的媒體獨立接口)連接這樣就可以一個簡單的以太網通信了。下面通過分析函數來了解相應的知識。分析LPC1768的例程。

<一>、控制器初始化函數。

Status EMAC_Init(EMAC_CFG_Type *EMAC_ConfigStruct)
{
/* Initialize the EMAC Ethernet controller. */
int32_t regv,tout, tmp;
/* Set up clock and power for Ethernet module */
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCENET, ENABLE);//(1)打開以太網控制器
/* Reset all EMAC internal modules */
LPC_EMAC->MAC1    = EMAC_MAC1_RES_TX | EMAC_MAC1_RES_MCS_TX | EMAC_MAC1_RES_RX |
EMAC_MAC1_RES_MCS_RX | EMAC_MAC1_SIM_RES | EMAC_MAC1_SOFT_RES;//(2)置位MAC1配置寄存器中的一些位。
LPC_EMAC->Command = EMAC_CR_REG_RES | EMAC_CR_TX_RES | EMAC_CR_RX_RES | EMAC_CR_PASS_RUNT_FRM;//(3)復位控制命令寄存器
/* A short delay after reset. */
for (tout = 100; tout; tout--);
/* Initialize MAC control registers. */
LPC_EMAC->MAC1 = EMAC_MAC1_PASS_ALL;//允許所有的幀信息傳送,包括常規幀和控制幀,使能接收功能時生效
LPC_EMAC->MAC2 = EMAC_MAC2_CRC_EN | EMAC_MAC2_PAD_EN;//允許填充短幀,添加CRC
LPC_EMAC->MAXF = EMAC_ETH_MAX_FLEN;//幀最大長度爲1536
/*
* Find the clock that close to desired target clock
*/
tmp = SystemCoreClock / EMAC_MCFG_MII_MAXCLK;
for (tout = 0; tout < sizeof (EMAC_clkdiv); tout++){
if (EMAC_clkdiv[tout] >= tmp) break;
}
tout++;
// Write to MAC configuration register and reset
LPC_EMAC->MCFG = EMAC_MCFG_CLK_SEL(tout) | EMAC_MCFG_RES_MII; //設置MII的AHB總線時鐘分頻數,復位MII
// release reset
LPC_EMAC->MCFG &= ~(EMAC_MCFG_RES_MII);//釋放MII復位
LPC_EMAC->CLRT = EMAC_CLRT_DEF;// 設置衝突後重發次數和衝突時間槽
LPC_EMAC->IPGR = EMAC_IPGR_P2_DEF;//設置非連續包時間間隔

/* Enable Reduced MII interface. */
LPC_EMAC->Command = EMAC_CR_RMII | EMAC_CR_PASS_RUNT_FRM;//將MAC設置爲RMII模式並設置可接收短幀

/* Reset Reduced MII Logic. */
LPC_EMAC->SUPP = EMAC_SUPP_RES_RMII; //未定義?

for (tout = 100; tout; tout--);
LPC_EMAC->SUPP = 0;//PHY支持寄存器,操作速率將爲10M(10/100M)

/* Put the DP83848C in reset mode */
write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_BMCR_RESET);//(4)

/* Wait for hardware reset to end. */
for (tout = EMAC_PHY_RESP_TOUT; tout; tout--) {
regv = read_PHY (EMAC_PHY_REG_BMCR);
if (!(regv & (EMAC_PHY_BMCR_RESET | EMAC_PHY_BMCR_POWERDOWN))) {
/* Reset complete, device not Power Down. */
break;
}
if (tout == 0){
// Time out, return ERROR
return (ERROR);
}
}

// Set PHY mode
if (EMAC_SetPHYMode(EMAC_ConfigStruct->Mode) < 0){//(5)設置PHY模式,mode爲自動模式
return (ERROR);
}

// Set EMAC address
setEmacAddr(EMAC_ConfigStruct->pbEMAC_Addr);//設置MAC地址

/* Initialize Tx and Rx DMA Descriptors */
rx_descr_init ();//(6)
tx_descr_init ();//發射通道初始化,同理6

// Set Receive Filter register: enable broadcast and multicast
LPC_EMAC->RxFilterCtrl = EMAC_RFC_MCAST_EN | EMAC_RFC_BCAST_EN | EMAC_RFC_PERFECT_EN;
//RxFilterCtrl接收過濾控制寄存器,接收多播幀,接收廣播幀,接收和MAC地址相同的幀
/* Enable Rx Done and Tx Done interrupt for EMAC */
LPC_EMAC->IntEnable = EMAC_INT_RX_DONE | EMAC_INT_TX_DONE;//使能接收中斷和發送中斷

/* Reset all interrupts */
LPC_EMAC->IntClear  = 0xFFFF;//清除中斷

/* Enable receive and transmit mode of MAC Ethernet core */
LPC_EMAC->Command  |= (EMAC_CR_RX_EN | EMAC_CR_TX_EN);//接收使能,發送使能
LPC_EMAC->MAC1     |= EMAC_MAC1_REC_EN;//MAC寄存器中接收使能

return SUCCESS;
}
(1)CLKPWR_ConfigPPWR函數將設置外設功率控制寄存器(PCONP),以打開或者關閉相應的外設的時鐘源。
(2)復位的這些位定義如下,
#define EMAC_MAC1_RES_TX         0x00000100  /**< Reset TX Logic該位置位將使發送功能邏輯進入復位狀態*/
#define EMAC_MAC1_RES_MCS_TX     0x00000200  /**< Reset MAC TX Control Sublayer,MAC層的發送邏輯復位MCS執行流控制*/
#define EMAC_MAC1_RES_RX         0x00000400  /**< Reset RX Logic該位置位將使接受功能邏輯進入復位狀態*/
#define EMAC_MAC1_RES_MCS_RX     0x00000800  /**< Reset MAC RX Control Sublayer,MAC層的接受邏輯復位MCS執行流控制*/
#define EMAC_MAC1_SIM_RES        0x00004000  /**< Simulation Reset發送功能中的隨機數發生器復位*/
#define EMAC_MAC1_SOFT_RES       0x00008000  /**< Soft Reset MAC 將使MAC中的除主機接口的所有狀態復位*/
(3)定義如下
#define EMAC_CR_REG_RES          0x00000008   /**< Reset Host Registers 所有的通道和和主機寄存器都復位*/
#define EMAC_CR_TX_RES           0x00000010   /**< Reset Transmit Datapath 復位發送通道 */
#define EMAC_CR_RX_RES           0x00000020   /**< Reset Receive Datapath  復位接收通道*/
#define EMAC_CR_PASS_RUNT_FRM    0x00000040   /**< Pass Runt Frames  接收短幀功能 */
(4)這個函數是寫數據到外部網卡(PHY)的。
static int32_t write_PHY (uint32_t PhyReg, uint16_t Value)//PhyReg,PHY中寄存器地址 value,值
{
/* Write a data 'Value' to PHY register 'PhyReg'. */
uint32_t tout;

LPC_EMAC->MADR = EMAC_DEF_ADR | PhyReg;//EMAC_DEF_ADR是默認的PHY(最多可控制32個PHY)。MADR裏包含哪個PHY哪個寄存器
LPC_EMAC->MWTD = Value;//寄存器的值

/* Wait until operation completed */
tout = 0;
for (tout = 0; tout < EMAC_MII_WR_TOUT; tout++) {//等待操作結束
if ((LPC_EMAC->MIND & EMAC_MIND_BUSY) == 0) {
return (0);
}
}
// Time out!
return (-1);
}
(5)
int32_t EMAC_SetPHYMode(uint32_t ulPHYMode)
{
int32_t id1, id2, tout, regv;

/* Check if this is a DP83848C PHY. */
id1 = read_PHY (EMAC_PHY_REG_IDR1);//讀出PHY芯片的ID
id2 = read_PHY (EMAC_PHY_REG_IDR2);

#ifdef MCB_LPC_1768
if (((id1 << 16) | (id2 & 0xFFF0)) == EMAC_DP83848C_ID) {//ID是DP8384的ID
switch(ulPHYMode){//根據參數進入相應的部分設置PHY
case EMAC_MODE_AUTO:
write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_AUTO_NEG);//設置PHY的Base Control寄存器的位爲自動選擇傳輸速率和方式
#elif defined(IAR_LPC_1768) /* Use IAR LPC1768 KickStart board */
if (((id1 << 16) | id2) == EMAC_KSZ8721BL_ID) {
/* Configure the PHY device */
switch(ulPHYMode){
case EMAC_MODE_AUTO:
/* Use auto-negotiation about the link speed. */
write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_AUTO_NEG);
// write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_BMCR_AN);
#endif
/* Wait to complete Auto_Negotiation */
for (tout = EMAC_PHY_RESP_TOUT; tout; tout--) {
regv = read_PHY (EMAC_PHY_REG_BMSR);
if (regv & EMAC_PHY_BMSR_AUTO_DONE) {
/* Auto-negotiation Complete. */
break;
}
if (tout == 0){
// Time out, return error
return (-1);
}
}
break;
case EMAC_MODE_10M_FULL:
/* Connect at 10MBit full-duplex */
write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_FULLD_10M);
break;
case EMAC_MODE_10M_HALF:
/* Connect at 10MBit half-duplex */
write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_HALFD_10M);
break;
case EMAC_MODE_100M_FULL:
/* Connect at 100MBit full-duplex */
write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_FULLD_100M);
break;
case EMAC_MODE_100M_HALF:
/* Connect at 100MBit half-duplex */
write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_HALFD_100M);
break;
default:
// un-supported
return (-1);
}
}
// It's not correct module ID
else {
return (-1);
}

// Update EMAC configuration with current PHY status
if (EMAC_UpdatePHYStatus() < 0){//這個函數會讀取PHY中的0x10寄存器來查出現在PHY的速率和傳輸方式,來設置MAC的速率和傳輸方式
return (-1);
}

// Complete
return (0);
}

(6)
static void rx_descr_init (void)
{
/* Initialize Receive Descriptor and Status array. */
uint32_t i;

for (i = 0; i < EMAC_NUM_RX_FRAG; i++) {
Rx_Desc[i].Packet  = (uint32_t)&rx_buf[i];//<1>
Rx_Desc[i].Ctrl    = EMAC_RCTRL_INT | (EMAC_ETH_MAX_FLEN - 1);//控制字段
Rx_Stat[i].Info    = 0;
Rx_Stat[i].HashCRC = 0;
}

/* Set EMAC Receive Descriptor Registers. */
LPC_EMAC->RxDescriptor       = (uint32_t)&Rx_Desc[0];//RxDescriptor保存接收描述符數組的基地址(最低地址)
LPC_EMAC->RxStatus           = (uint32_t)&Rx_Stat[0]; //RxStatus
LPC_EMAC->RxDescriptorNumber = EMAC_NUM_RX_FRAG - 1;//接收描述符數目寄存器

/* Rx Descriptors Point to 0 */
LPC_EMAC->RxConsumeIndex  = 0;//接收消耗索引,它與RxProductIndex(接收生產索引)組成環狀緩存形式
}
<1>rx_buf是一個二維數組,4個接收數據數組,每個1536字節
如:static uint32_t rx_buf[EMAC_NUM_RX_FRAG][EMAC_ETH_MAX_FLEN>>2];

小結總結:EMAC_Init函數初始化了了以太網模塊中的各個寄存器並通過其設置了外部的PHY,爲收發數據做好了準備。


<二>
之後程序打開了設置了各個變量後,調用函數
void DoNetworkStuff(void)
{
  if (CheckFrameReceived()) // 函數將檢查RxConsumeIndex和RxProductIndex是否相等來判斷是否有數據來
  {
    if (IsBroadcast()) { //(1)
      ProcessEthBroadcastFrame(); //廣播消息的處理
    } else {
      ProcessEthIAFrame(); //處理ARP幀和IP幀,本文不深入協議部分。
    }
    EndReadFrame();     //設置消耗索引RxConsumeIndex,即++,若索引到盡頭,因爲是環形緩衝,所以索引置0
  }

  if (TCPFlags & TCP_TIMER_RUNNING)
    if (TCPFlags & TIMER_TYPE_RETRY)
    {
      if (TCPTimer > RETRY_TIMEOUT)
      {
        TCPRestartTimer();                       // set a new timeout

        if (RetryCounter)
        {
          TCPHandleRetransmission();             // resend last frame
          RetryCounter--;
        }
        else
        {
          TCPStopTimer();
          TCPHandleTimeout();
        }
      }
    }
    else if (TCPTimer > FIN_TIMEOUT)
    {
      TCPStateMachine = CLOSED;
      TCPFlags = 0;                              // reset all flags, stop retransmission...
      SocketStatus &= SOCK_DATA_AVAILABLE;       // clear all flags but data available
    }

  switch (TCPStateMachine)
  {
    case CLOSED :
    case LISTENING :
    {
      if (TCPFlags & TCP_ACTIVE_OPEN)            // stack has to open a connection?
        if (TCPFlags & IP_ADDR_RESOLVED)         // IP resolved?
          if (!(TransmitControl & SEND_FRAME2))  // buffer free?
          {
            TCPSeqNr = ((unsigned long)ISNGenHigh << 16) | (SysTick->VAL & 0xFFFF);  // NXP: changed from T0TC to SysTick->VAL;
                                                                // set local ISN
            TCPUNASeqNr = TCPSeqNr;
            TCPAckNr = 0;                                       // we don't know what to ACK!
            TCPUNASeqNr++;                                      // count SYN as a byte
            PrepareTCP_FRAME(TCP_CODE_SYN);                     // send SYN frame
            LastFrameSent = TCP_SYN_FRAME;
            TCPStartRetryTimer();                               // we NEED a retry-timeout
            TCPStateMachine = SYN_SENT;
          }
      break;
    }
    case SYN_RECD :
    case ESTABLISHED :
    {
      if (TCPFlags & TCP_CLOSE_REQUESTED)                  // user has user initated a close?
        if (!(TransmitControl & (SEND_FRAME2 | SEND_FRAME1)))   // buffers free?
          if (TCPSeqNr == TCPUNASeqNr)                          // all data ACKed?
          {
            TCPUNASeqNr++;
            PrepareTCP_FRAME(TCP_CODE_FIN | TCP_CODE_ACK);
            LastFrameSent = TCP_FIN_FRAME;
            TCPStartRetryTimer();
            TCPStateMachine = FIN_WAIT_1;
          }
      break;
    }
    case CLOSE_WAIT :
    {
      if (!(TransmitControl & (SEND_FRAME2 | SEND_FRAME1)))     // buffers free?
        if (TCPSeqNr == TCPUNASeqNr)                            // all data ACKed?
        {
          TCPUNASeqNr++;                                        // count FIN as a byte
          PrepareTCP_FRAME(TCP_CODE_FIN | TCP_CODE_ACK);        // we NEED a retry-timeout
          LastFrameSent = TCP_FIN_FRAME;                        // time to say goodbye...
          TCPStartRetryTimer();
          TCPStateMachine = LAST_ACK;
        }
      break;
    }
  }

  if (TransmitControl & SEND_FRAME2)
  {
    RequestSend(TxFrame2Size);

    if (Rdy4Tx()) {                               // NOTE: when using a very fast MCU, maybe
    _DBG_("Send Frm2");
    SendFrame2();                              // the EMAC isn't ready yet, include
    } else {                                       // a kind of timer or counter here
      TCPStateMachine = CLOSED;
      SocketStatus = SOCK_ERR_ETHERNET;          // indicate an error to user
      TCPFlags = 0;                              // clear all flags, stop timers etc.
    }

    TransmitControl &= ~SEND_FRAME2;             // clear tx-flag
  }

  if (TransmitControl & SEND_FRAME1)
  {
    PrepareTCP_DATA_FRAME();                     // build frame w/ actual SEQ, ACK....
    RequestSend(TxFrame1Size);

    if (Rdy4Tx()){                                // EMAC ready to accept our frame?
    _DBG_("Send Frm1");
    SendFrame1();                              // (see note above)
    } else {
      TCPStateMachine = CLOSED;
      SocketStatus = SOCK_ERR_ETHERNET;          // indicate an error to user
      TCPFlags = 0;                              // clear all flags, stop timers etc.
    }

    TransmitControl &= ~SEND_FRAME1;             // clear tx-flag
  }
}

(1)
unsigned int IsBroadcast(void) {
  unsigned short RecdDestMAC[3];         // 48 bit MAC

  RecdFrameLength = StartReadFrame(); //<1>

  CopyFromFrame_EMAC(&RecdDestMAC,  6); //此函數從rptr中順序讀取6個字節到RecdDestMAC,這首6個字節是目標MAC地址
  CopyFromFrame_EMAC(&RecdFrameMAC, 6); //再讀6個字節到RecdFrameMAC,是源MAC地址

  if ((RecdDestMAC[0] == 0xFFFF) && //如果MAC地址是0xFFFFFFFFFFFF就是廣播地址
      (RecdDestMAC[1] == 0xFFFF) &&
      (RecdDestMAC[2] == 0xFFFF)) {
    return(1);
  } else {
    return (0);
  }
}
<1>
unsigned short StartReadFrame(void) {
unsigned short RxLen;
EMAC_PACKETBUF_Type RxPack;

RxLen = EMAC_GetReceiveDataSize() - 3; //函數將通過狀態寄存器中保存的狀態變量地址中的info字段來判斷幀的長度
// Copy packet to data buffer
RxPack.pbDataBuf = (uint32_t *)pgBuf; //pgBuf = LPC_AHBRAM1_BASE,這是一塊未使用的地址,可用來保存接收的幀數據
RxPack.ulDataLen = RxLen;
EMAC_ReadPacketBuffer(&RxPack);//從寄存器指向的環形緩衝中,拷貝一幀數據到pgBuf
// Point to the data buffer
rptr = (unsigned short *)pgBuf;//用全局rptr變量保存地址
return(RxLen);
}

第二節,分析和協議相關部分

如果不是廣播包,則調用函數
void ProcessEthIAFrame(void)
{
  unsigned short TargetIP[2];
  unsigned char ProtocolType;

  switch (ReadFrameBE_EMAC())                     // get frame type 讀取類型字段
  {
    case FRAME_ARP :                             // check for ARP
    {
      if ((TCPFlags & (TCP_ACTIVE_OPEN | IP_ADDR_RESOLVED)) == TCP_ACTIVE_OPEN)
        if (ReadFrameBE_EMAC() == HARDW_ETH10)         // check for the right prot. etc.
          if (ReadFrameBE_EMAC() == FRAME_IP)
            if (ReadFrameBE_EMAC() == IP_HLEN_PLEN)
              if (ReadFrameBE_EMAC() == OP_ARP_ANSWER)
              {
                TCPStopTimer();                       // OK, now we've the MAC we wanted ;-)
                CopyFromFrame_EMAC(&RemoteMAC, 6);    // extract opponents MAC
                TCPFlags |= IP_ADDR_RESOLVED;
              }
      break;
    }
    case FRAME_IP :                                        // check for IP-type
    {
      if ((ReadFrameBE_EMAC() & 0xFF00 ) == IP_VER_IHL)     // IPv4, IHL=5 (20 Bytes Header)
      {                                                    // ignore Type Of Service
        RecdIPFrameLength = ReadFrameBE_EMAC();             // get IP frame's length
        ReadFrameBE_EMAC();                                 // ignore identification

        if (!(ReadFrameBE_EMAC() & (IP_FLAG_MOREFRAG | IP_FRAGOFS_MASK)))  // only unfragm. frames
        {
          ProtocolType = ReadFrameBE_EMAC() & 0xFF;         // get protocol, ignore TTL
          ReadFrameBE_EMAC();                               // ignore checksum
          CopyFromFrame_EMAC(&RecdFrameIP, 4);              // get source IP
          CopyFromFrame_EMAC(&TargetIP, 4);                 // get destination IP

          if (!memcmp(&MyIP, &TargetIP, 4))                // is it for us?
            switch (ProtocolType) {
              case PROT_ICMP : { ProcessICMPFrame(); break; }
              case PROT_TCP  : { ProcessTCPFrame(); break; }
              case PROT_UDP  : break;                      // not implemented!
            }
        }      
      }
      break;
    }
  }
}

看到有人要這個源碼,這個是網上公開可以很容易下到的源碼,這裏附上工程文件
easyweb
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章