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

📄 etherdev.c

📁 web服务器单片机开发
💻 C
字号:
#include "etherdev.h"
void __Tmr_TickISR_Handler(void);
#define   NET_RST   					0x00000040
#define 	ETH_TX_PAGE_START  	0x40    // 0x4000 Tx buffer is  6 * 256 = 1536 bytes
#define 	ETH_RX_PAGE_START  	0x46    // 0x4600 Rx buffer is 26 * 256 = 6656 bytes
#define 	ETH_RX_PAGE_STOP   	0x80    // 0x6000
#define 	ETH_MIN_PACKET_LEN 	0x3C

static void etherdev_reg_write(uint8 reg, uint16 wr_data);
static uint16 etherdev_reg_read(uint8 reg);
static unsigned int etherdev_poll(void);

static uint8 tick_count = 0;	

void __irq  Tmr_TickISR_Handler(void){
	tick_count++;   	     
	T0IR = 0x01;	    			            	// 清除中断标志
	VICVectAddr = 0x00;				            // 通知VIC中断处理结束
}

static uint16 etherdev_reg_read(uint8 reg){
	return (uint16)(*((volatile uint16*)ETH_REG_BASE + reg));
}

static void etherdev_reg_write(uint8 reg, uint16 data){
	*((volatile uint16*)ETH_REG_BASE + reg) = data;
}

// Manipulate PS1 & PS0 in CR to select RTL8019AS register page. 
#define ETHERDEV_SELECT_REG_PAGE(page)                                      \
          do                                                                \
          {                                                                 \
              etherdev_reg_write(CR, etherdev_reg_read(CR) & ~(PS1 | PS0)); \
              etherdev_reg_write(CR, etherdev_reg_read(CR) | (page << 6));  \
          } while(0)
          
int etherdev_init(void){
	int i, j;
	IODIR = IODIR|NET_RST;
	IOCLR = NET_RST;
	IOSET = NET_RST;
	for(i=0; i<200; i++){
		for(j=0; j<200; j++);
	}
	IOCLR = NET_RST;
	
	etherdev_reg_read(0x1f);
	etherdev_reg_write(0x1f, 0x00);
	for(i=0; i<200; i++){
		for(j=0; j<200; j++);
	}
	
	etherdev_reg_write(CR, RD2 | STP);
	etherdev_reg_write(DCR, FT1 | LS | BOS | WTS);
	etherdev_reg_write(RBCR0, 0x00);//复位远程字节计数寄存器
  etherdev_reg_write(RBCR1, 0x00);
  etherdev_reg_write(RCR, MON);			//设置接收配置寄存器为监听模式
	etherdev_reg_write(TCR, LB0);			//设置传输配置寄存器为内部回环模式
  etherdev_reg_write(BNRY, ETH_RX_PAGE_START);
  etherdev_reg_write(PSTART, ETH_RX_PAGE_START);
  etherdev_reg_write(PSTOP, ETH_RX_PAGE_STOP);
  etherdev_reg_write(TPSR, ETH_TX_PAGE_START);
  etherdev_reg_write(ISR, 0xFF);		//清除所有的中断标志位
	etherdev_reg_write(IMR, 0x00);		//屏蔽所有中断
	etherdev_reg_write(CR, RD2 | PS0 | STP);//选择page1
	etherdev_reg_write(CURR, ETH_RX_PAGE_START);	
	etherdev_reg_write(PAR0, UIP_ETHADDR0);
  etherdev_reg_write(PAR1, UIP_ETHADDR1);
  etherdev_reg_write(PAR2, UIP_ETHADDR2);
  etherdev_reg_write(PAR3, UIP_ETHADDR3);
  etherdev_reg_write(PAR4, UIP_ETHADDR4);
  etherdev_reg_write(PAR5, UIP_ETHADDR5);
  
  // Select RTL8019AS register page 0 and abort DMA operation.
  etherdev_reg_write(CR, RD2 | STP);
  // Restart RTL8019AS. 
  etherdev_reg_write(CR, RD2 | STA); 
  etherdev_reg_write(RCR, AB);
  etherdev_reg_write(TCR, 0x00);
  
  VICIntSelect &= ~(1 << VIC_TIMER0);          /* 选择TIMER0为IRQ                           */
  VICVectAddr2  = (uint32)Tmr_TickISR_Handler; /* 设置中断服务程序地址                      */
  VICVectCntl2  = 0x20 | VIC_TIMER0;           /* 分配TIMER0为slot2                         */
  VICIntEnable  =  (1 << VIC_TIMER0);          /* 使能中断                                  */
  T0TCR         = 0;                           /* 禁止 timer 0.                             */
  T0PC          = 0;                           /* 时钟不分频                 								*/
  T0MR0         = CPU_PERIPHERAL_CLK_FREQ / OS_TICKS_PER_SEC;  /* 计数到这个值           		*/
  T0MCR         = 3;                           /*设置T0MR0匹配后复位T0TC,并产生中断标志    */
  T0CCR         = 0;                           /* 禁止捕获功能                              */
  T0EMR         = 0;                           /* 无外部匹配输出                          	*/
  T0TCR         = 1;                           /* 使能timer 0                               */	
  return 1; 
}

void etherdev_send(void){
		int i;		
		uint16* ptr_16 = (uint16 *)uip_buf;
		etherdev_reg_write(CR, RD2 | STA);
  		// Wait until pending transmit operation completes.
    while(etherdev_reg_read(CR) & TXP) continue;

    	// Clear remote DMA complete interrupt status register bit.
    etherdev_reg_write(ISR, RDC);

    	// Set remote DMA start address registers to indicate where to load packet.
    etherdev_reg_write(RSAR0, 0x00);
    etherdev_reg_write(RSAR1, ETH_TX_PAGE_START);

    	// Set remote DMA byte count registers to indicate length of packet load.
			//设置远程DMA字节计数寄存器来表示packet的长度
    etherdev_reg_write(RBCR0, (uint8)(uip_len & 0xFF));
    etherdev_reg_write(RBCR1, (uint8)(uip_len >> 8));

    	// Initiate DMA transfer of uip_buf & uip_appdata buffers to RTL8019AS.
			//初始化DMA发送
    etherdev_reg_write(CR, RD1 | STA);
    
	    // DMA transfer packet from uip_buf & uip_appdata to RTL8019AS local
	    // transmit buffer memory.发送到RTL8018AS的本地发送缓冲区
		for(i = 0; i < (uip_len + 1)/2; i++){
					/*首先发送在uip_buf中的link level headers和TCP/IP headers,
						这两个头部的总大小是40 + UIP_LLH_LEN。*/
        if(i == (40 + UIP_LLH_LEN)/2 ){
            /*这时,发送应用程序的数据部分,因为第(40 + UIP_LLH_LEN)个字节应该是应用程序的数据*/
					ptr_16 = (uint16 *)uip_appdata;
        }
        etherdev_reg_write(RDMA, *ptr_16++);
    }
	    // Wait until remote DMA operation complete.
			//等待,直到远程DMA操作完成。
    while(!(etherdev_reg_read(ISR) & RDC)) continue;
	  	// Abort/ complete DMA operation.
			//中止/完成DMA操作
    etherdev_reg_write(CR, RD2 | STA);

	    // Set transmit page start to indicate packet start.
			//设置PSTART,它指向packet的开始
    etherdev_reg_write(TPSR, ETH_TX_PAGE_START);

	    // Ethernet packets must be > 60 bytes, otherwise are rejected as runts.
			//以太网packets必须大于60字节,否则认为不完整,而会被丢弃
    if(uip_len < ETH_MIN_PACKET_LEN){
        uip_len = ETH_MIN_PACKET_LEN;
    }
			// Set transmit byte count registers to indicate packet length.
			//设置TBCR,指出packet的长度
    etherdev_reg_write(TBCR0, (unsigned char)(uip_len & 0xFF));
    etherdev_reg_write(TBCR1, (unsigned char)(uip_len >> 8));

	    // Issue command for RTL8019AS to transmit packet from it's local buffer.
			//发出命令,让NIC把packet从它的本地缓冲区中发送到网络上。
    etherdev_reg_write(CR, RD2 | TXP | STA);
    return;
}


	//这个函数从网卡中读出一个packet,并将其放入缓冲区uip_buf中。如果必须等待超过0.5秒
	//返回0。相反当一个成功读取了一个full packet并放入uip_buf缓冲区时,则返回packet的长度。
unsigned int etherdev_read(void)
{    
    unsigned int bytes_read;

    /* tick_count threshold should be 12 for 0.5 sec bail-out
       One second (24) worked better for me, but socket recycling
       is then slower. I set UIP_TIME_WAIT_TIMEOUT 60 in uipopt.h
       to counter this. Retransmission timing etc. is affected also. */
		/*一秒(24)可能更好,这时socket循环会更慢一点,但是为了安全起见,tick_count阀值应该是12。
		与此对应我在uipopt.h中设置UIP_TIME_WAIT_TIMEOUT为60。重发等也受到影响*/
    while ((!(bytes_read = etherdev_poll())) && (tick_count < 12)) continue;
    tick_count = 0;											//在此处将tick_count清零
    return bytes_read;										//返回读取的字节的个数
}
  
/*轮询网卡是否有到来的packets*/
static unsigned int etherdev_poll(void){
    int len = 0;
    	// Check if there is a packet in the rx buffer.
			//检查在rx缓冲区中是否有一个packet
    if(etherdev_reg_read(ISR) & PRX){
        // Check if the rx buffer has overflowed.
				//检查rx缓冲区是否溢出
       if(etherdev_reg_read(ISR) & OVW){
       		int i,j;
       		int resend = 0;
        	uint8 status = (uint8)etherdev_reg_read(CR) & TXP;
        	etherdev_reg_write(CR, RD2 | STP);
        	for(i=0; i<200; i++){
						for(j=0; j<200; j++);
					}
						// Wait for controller to halt after any current tx completes.
          while(!(etherdev_reg_read(ISR) & RST)) continue;
          etherdev_reg_write(RBCR0, 0x00);
          etherdev_reg_write(RBCR1, 0x00);
          if(status){
          	if(!((etherdev_reg_read(ISR) & PTX) || (etherdev_reg_read(ISR) & TXE))){
                    resend = 1;
            }
          }
          // Set transmit configuration register to loopback internally.
          etherdev_reg_write(TCR, LB0);

            // Restart the RTL8019AS.
          etherdev_reg_write(CR, RD2 | STA);
            // Re-initialise last receive buffer read pointer.
          etherdev_reg_write(BNRY, ETH_RX_PAGE_START);

            // Select RTL8019AS register page 1.
          ETHERDEV_SELECT_REG_PAGE(1);

            // Re-initialise current packet receive buffer page pointer.
          etherdev_reg_write(CURR, ETH_RX_PAGE_START);

            // Select RTL8019AS register page 0.
          ETHERDEV_SELECT_REG_PAGE(0);

            // Clear rx buffer overflow & packet received interrupt flags.
          etherdev_reg_write(ISR, PRX | OVW);
          etherdev_reg_write(TCR, 0x00);
          if(resend){
             // Retransmit packet in RTL8019AS local tx buffer.
            etherdev_reg_write(CR, RD2 | TXP | STA);
          }
        }
  					// Rx buffer has not overflowed, so read a packet into uip_buf.
  					//没有溢出,因此读出一个packet放入uip_buf
        else { 
            int i;
            uint8 next_rx_packet;								//指向下一个packet的页位置
            uint8 current;
            uint16* pbuf_16 = (uint16*)uip_buf;
            union {
            	uint16 words;
            	uint8  bytes[2];
            }status;

	            // Retrieve packet header. (status, next_ptr, length_l, length_h)
							//获取packet头部。(状态,指向下一个packet的指针,packet长度的低位字节,packet长度的高位字节)
	            // Clear remote DMA complete interrupt status register bit.
							//复位远程DMA ISR寄存器的RDC位(此位在远程DMA操作完成后置位)写"1"清除对应位的标志
							//使此次操作从一个全新的状态开始
            etherdev_reg_write(ISR, RDC);
	            // Set remote DMA start address registers to packet header.
							//将远程DMA起始地址寄存器设置为packet头部
            etherdev_reg_write(RSAR0, 0x00);
            etherdev_reg_write(RSAR1, etherdev_reg_read(BNRY));//注意第一个packet就放在BNRY指向的页

            	// Set remote DMA byte count registers to packet header length.
							//将远程DMA字节计数寄存器设置为packet头部的长度
							//首先读取四个字节的数据,包含了(status, next_ptr, length_l, length_h)
            etherdev_reg_write(RBCR0, 0x04);
            etherdev_reg_write(RBCR1, 0x00);

	            // Initiate DMA transfer of packet header.
							//初始化packet头部的DMA传输
            etherdev_reg_write(CR, RD0 | STA);

            // Drop packet status. We don't use it.
						//丢弃packet状态,状态对我们没有多大用处,总是假设是好的
						status.words = etherdev_reg_read(RDMA);
						// Save next packet pointer.
						//保存指向下一个packet的指针。
            next_rx_packet = status.bytes[1];
            // Retrieve packet data length and subtract CRC bytes.
						//获取packet的数据长度并减去CRC(4个字节)的长度。
            len =  etherdev_reg_read(RDMA);
            len -= 4;
            // Wait until remote DMA operation completes.
						//等待直到远程DMA操作完成,操作完成时,RDC置位
            while(!(etherdev_reg_read(ISR) & RDC)) continue;     
            // Abort/ complete DMA operation.
						//中止/完成DMA操作
            etherdev_reg_write(CR, RD2 | STA);
            
            // Retrieve packet data.
						//前面只是读取了一些有用的信息,后面将获取packet的数据
            // Check if incoming packet will fit into rx buffer.
						//检查packet的长度,看看它是否大于UIP_BUFSIZE
            if(len <= UIP_BUFSIZE)
            {
                // Clear remote DMA complete interrupt status register bit.
								//依然是复位远程DMA ISR寄存器的RDC位(此位在远程DMA操作完成后置位)写"1"清除对应位的标志
					    	//使此次操作从一个全新的状态开始
                etherdev_reg_write(ISR, RDC);

                // Set remote DMA start address registers to packet data.
								//设置远程DMA RSAR,注意这次跳过了前四个字节
                etherdev_reg_write(RSAR0, 0x04);
                etherdev_reg_write(RSAR1, etherdev_reg_read(BNRY));

                // Set remote DMA byte count registers to packet data length.
								//设置远程DMA字节计数寄存器为packet的数据长度,已经减去了CRC的那四个字节
                etherdev_reg_write(RBCR0, (uint8)(len & 0xFF));
                etherdev_reg_write(RBCR1, (uint8)(len >> 8));

                // Initiate DMA transfer of packet data.
                etherdev_reg_write(CR, RD0 | STA);

                // Read packet data directly into uip_buf.               	   	
								for(i = 0; i < (len + 1)/2; i++){
									*(pbuf_16 + i) = etherdev_reg_read(RDMA);
                }

                // Wait until remote DMA operation complete.
                while(!(etherdev_reg_read(ISR) & RDC)) continue;

                // Abort/ complete DMA operation.
                etherdev_reg_write(CR, RD2 | STA);
            }    	
            else{
                // Incoming packet too big, so dump it.
								//到来的包太大,因此丢弃它
                len = 0;
            }

            // Advance boundary pointer to next packet start.
						//设置指针BNRY为下一个packet所在的页
            etherdev_reg_write(BNRY, next_rx_packet);

            // Select RTL8019AS register page 1.
						//选择第一页寄存器
            ETHERDEV_SELECT_REG_PAGE(1);

            // Retrieve current receive buffer page
						//获取指针CURR
            current = etherdev_reg_read(CURR);

            // Select RTL8019AS register page 0.
						//选择第0页寄存器
            ETHERDEV_SELECT_REG_PAGE(0);

            // Check if last packet has been removed from rx buffer.
						//检查是否最后一个packet已经被从接收缓冲区中取走
            if(next_rx_packet == current){
                	// Clear packet received interrupt flag.
									//如果是的话,清除接收中断标志。
                etherdev_reg_write(ISR, PRX);
            }
        }
    }
		//返回接收的packet的长度
    return len;
}
						       
          

⌨️ 快捷键说明

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