S3c2440與DM9000驅動

網絡對於嵌入式系統來講必不可少。但是s3c2440沒有集成以太網接口,因此要想使s3c2440具有以太網的功能,就必須擴展網卡接口。在這裏,咱們外接DM9000,使其能夠與以太網相鏈接。數組

DM9000能夠直接與ISA總線相連,也能夠與大多數CPU相連。在這裏,咱們固然是要讓DM9000與s3c2440相鏈接了。DM9000對 外來講只有兩個端口——地址口和數據口,地址口用於輸入內部寄存器的地址,而數據口則完成對某一寄存器的讀寫。DM9000的CMD引腳用來區分這兩個端 口,當CMD引腳爲0時,DM9000的數據線上傳輸的是寄存器地址,當CMD引腳爲1時,傳輸的是讀寫數據。咱們把DM9000的A8和A9接爲高電 平,把A4~A7接爲低電平,而且把DM9000的AEN接到s3c2440的nGCS4引腳上,則DM9000的端口基址爲0x20000300,若是 再把DM9000的CMD引腳接到s3c2440的ADDR2引腳上,則咱們就能夠定義DM9000的這兩個端口地址,它們分別爲:緩存

#define DM_ADDR_PORT (*((volatile unsigned short *) 0x20000300)) //地址口網絡

#define DM_DATA_PORT (*((volatile unsigned short *) 0x20000304)) //數據口函數

若是要寫入DM9000中的某個寄存器,則先把該寄存器的地址賦予DM_ADDR_PORT,而後再把要寫入的數據賦予DM_DATA_PORT便可。讀取DM9000中的某個寄存器也相似。下面的函數的做用分別是DM9000的讀、寫寄存器操做:測試

  1. //寫DM9000寄存器   
  2. void __inline dm_reg_write(unsigned char reg, unsigned char data)   
  3. {   
  4. DM_ADDR_PORT = reg;            //將寄存器地址寫到地址端口   
  5. DM_DATA_PORT = data;            //將數據寫到數據端口   
  6. }   
  7.     
  8. //讀DM9000寄存器   
  9. unsigned char __inline dm_reg_read(unsigned char reg)   
  10. {   
  11. DM_ADDR_PORT = reg;              
  12. return DM_DATA_PORT;             //將數據從數據端口讀出   
  13. }  

完成了對DM9000寄存器的讀寫函數的編寫,下面咱們就能夠初始化DM9000,它的過程就是適當配置DM9000寄存器的過程。DM9000的內部寄存器在這裏就不作介紹,並且DM9000的應用數據手冊也有如何初始化DM9000的步驟,咱們這裏只給出具體的程序:spa

  1. void dm_init(void)   
  2. {   
  3.        dm_reg_write(DM9000_NCR,1);         //軟件復位DM9000   
  4.        delay(30);              //延時至少20μs   
  5.        dm_reg_write(DM9000_NCR,0);         //清除復位位   
  6.     
  7.        dm_reg_write(DM9000_NCR,1);         //爲了確保復位正確,再次復位   
  8.        delay(30);   
  9.        dm_reg_write(DM9000_NCR,0);   
  10.          
  11.        dm_reg_write(DM9000_GPCR,1);       //設置GPIO0爲輸出   
  12.        dm_reg_write(DM9000_GPR,0);         //激活內部PHY   
  13.          
  14.        dm_reg_write(DM9000_NSR,0x2c);           //清TX狀態   
  15.        dm_reg_write(DM9000_ISR,0xf);                     //清中斷狀態   
  16.          
  17.        dm_reg_write(DM9000_RCR,0x39);           //設置RX控制   
  18.        dm_reg_write(DM9000_TCR,0);                //設置TX控制   
  19.        dm_reg_write(DM9000_BPTR,0x3f);            
  20. dm_reg_write(DM9000_FCTR,0x3a);   
  21.        dm_reg_write(DM9000_FCR,0xff);   
  22. dm_reg_write(DM9000_SMCR,0x00);   
  23.       
  24.        dm_reg_write(DM9000_PAR1,0x00);         //設置MAC地址:00-01-02-03-04-05   
  25.        dm_reg_write(DM9000_PAR2,0x01);           
  26.        dm_reg_write(DM9000_PAR3,0x02);   
  27. dm_reg_write(DM9000_PAR4,0x03);   
  28.        dm_reg_write(DM9000_PAR5,0x04);   
  29. dm_reg_write(DM9000_PAR6,0x05);   
  30.       
  31.        dm_reg_write(DM9000_NSR,0x2c);           //再次清TX狀態   
  32.        dm_reg_write(DM9000_ISR,0xf);                     //再次清中斷狀態   
  33.     
  34.        dm_reg_write(DM9000_IMR,0x81);           //打開接受數據中斷   
  35. }  

DM9000內部有0x3FF大小的SRAM用於接受和發送數據緩存。在發送或接收數據包以前,數據是暫存在這個SRAM中的。當須要連續發送或接 收數據時,咱們須要分別把DM9000寄存器MWCMD或MRCMD賦予數據端口,這樣就指定了SRAM中的某個地址,而且在傳輸完一個數據後,指針會指 向SRAM中的下一個地址,從而完成了連續訪問數據的目的。但當咱們在發送或接受一個數據後,指向SRAM的數據指針不須要變化時,則要把MWCMDX或 MRCMDX賦予數據端口。下面的程序爲DM9000發送數據的函數,它的兩個輸入參數分別爲要發送數據數組首地址和數據數組長度。在這裏咱們已經知道數 據的寬爲16位,它是由DM9000的硬件引腳設置實現的。操作系統

  1. void dm_tran_packet(unsigned char *datas, int length)   
  2. {   
  3.        int i;   
  4.          
  5.        dm_reg_write(DM9000_IMR, 0x80);          //在發送數據過程當中禁止網卡中斷   
  6.     
  7.        dm_reg_write(DM9000_TXPLH, (length>>8) & 0x0ff);           //設置發送數據長度   
  8. dm_reg_write(DM9000_TXPLL, length & 0x0ff);   
  9.       
  10.        DM_ADDR_PORT = DM9000_MWCMD;                 //發送數據緩存賦予數據端口   
  11.          
  12.        //發送數據   
  13.        for(i=0;i<length;i+=2)   
  14.        {   
  15.               delay(50);   
  16.               DM_DATA_PORT = datas[i]|(datas[i+1]<<8);            //8位數據轉換爲16位數據輸出   
  17.        }       
  18.          
  19. dm_reg_write(DM9000_TCR, 0x01);          //把數據發送到以太網上   
  20.     
  21. while((dm_reg_read(DM9000_NSR) & 0x0c) == 0)   
  22.        ;                           //等待數據發送完成   
  23.          
  24. delay(50);   
  25.     
  26. dm_reg_write(DM9000_NSR, 0x2c);          //清除TX狀態   
  27. dm_reg_write(DM9000_IMR, 0x81);          //打開DM9000接收數據中斷   
  28. }  

發送數據比較簡單,接收數據就略顯複雜,由於它是有必定格式要求的。在接收到的一包數據中的首字節若是爲0x01,則表示這是一個能夠接收的數據 包;若是爲0x0,則表示沒有可接收的數據包。所以在讀取其餘字節時,必定要先判斷首字節是否爲0x01。數據包的第二個字節爲數據包的一些信息,它的高 字節的格式與DM9000的寄存器RSR徹底一致。第三個和第四個字節爲數據包的長度。後面的數據就是真正要接收的數據了。下面就是DM9000接收數據 的程序,其中輸入參數爲存放輸入數據數組的首地址,輸出參數爲接收數據的長度。指針

  1. int dm_rec_packet(unsigned char *datas)   
  2. {   
  3.        unsigned char int_status;   
  4.        unsigned char rx_ready;   
  5.        unsigned short rx_status;   
  6.        unsigned short rx_length;   
  7.        unsigned short temp;   
  8.        int i;   
  9.     
  10.        int_status = dm_reg_read(DM9000_ISR);           //讀取ISR   
  11.        if(int_status & 0x1)                     //判斷是否有數據要接受   
  12.        {   
  13.               rx_ready = dm_reg_read(DM9000_MRCMDX);         //先讀取一個無效的數據   
  14.               rx_ready = (unsigned char)DM_DATA_PORT;            //真正讀取到的數據包首字節   
  15.                 
  16.               if(rx_ready == 1)                 //判讀首字節是否爲1或0   
  17.               {   
  18.                      DM_ADDR_PORT = DM9000_MRCMD;           //連續讀取數據包內容   
  19.     
  20.                      rx_status = DM_DATA_PORT;                           //狀態字節   
  21.                        
  22.                      rx_length = DM_DATA_PORT;                          //數據長度   
  23.                        
  24.                      if(!(rx_status & 0xbf00) && (rx_length < 10000))     //判讀數據是否符合要求   
  25.                      {   
  26.                             for(i=0; i<rx_length; i+=2)          //16位數據轉換爲8位數據存儲   
  27.                             {   
  28.                                    delay(50);   
  29.                                    temp = DM_DATA_PORT;   
  30.                                    datas[i] = temp & 0x0ff;   
  31.                                    datas[i + 1] = (temp >> 8) & 0x0ff;   
  32.                             }   
  33.                      }   
  34.               }   
  35.               else if(rx_ready !=0)      //中止設備   
  36.               {   
  37.                      //dm_reg_write(DM9000_IMR,0x80);  //中止中斷   
  38.                      //dm_reg_write(DM9000_ISR,0x0F);   //清中斷狀態   
  39.                      //dm_reg_write(DM9000_RCR,0x0);    //中止接收   
  40.                      //還須要復位系統,這裏暫時沒有處理   
  41.               }   
  42.        }   
  43.        dm_reg_write(DM9000_ISR, 0x1);             //清中斷   
  44.        return rx_length;   
  45. }  

關於DM9000的設置咱們就介紹到這裏,下面就是s3c2440的設置。在這裏,網卡發送數據利用的是查詢方式,接收數據利用的是中斷方式,所以 咱們把DM9000的INT引腳鏈接到了s3c2440的EINT7上。另外咱們仍是用UART0接口來控制和顯示網卡數據。這兩個接口的初始化爲:調試

  1. //uart0 port   
  2. rGPHCON = 0x00faaa;   
  3. rGPHUP  = 0x7ff;   
  4. rULCON0 = 0x3;   
  5. rUCON0 = 0x5;   
  6. rUFCON0 = 0;   
  7. rUMCON0 = 0;   
  8. rUBRDIV0 = 26;   
  9.        
  10. rSRCPND = (0x1<<27)|(0x1<<28);   
  11. rSUBSRCPND = 0x1;   
  12. rINTPND = (0x1<<27)|(0x1<<28);   
  13. rINTSUBMSK = ~(0x1);   
  14. rINTMSK = ~((0x1<<27)|(0x1<<28));   
  15. pISR_UART0 = (U32)uartISR;   
  16.     
  17. //EINT7   
  18. rGPFCON = 2<<14;   
  19. rEXTINT0 = (rEXTINT0 & (~(0x07<<28))) | (0x01<<28);   
  20. rEINTMASK &= ~(1<<7);   
  21. rSRCPND = rSRCPND | (0x1<<4);   
  22. rINTPND = rINTPND | (0x1<<4);   
  23. rINTMSK &= ~(1<<4);   
  24. pISR_EINT4_7 = (U32)DM9000ISR;  

下面就利用DM9000來進行簡單的網卡傳輸數據的測驗。因爲以太網傳輸數據都是基於某種協議的,所以要傳輸數據,必須遵循必定的協議格式。這裏我 們實現較爲簡單的ARP協議。用於以太網的ARP請求/應答分組格式爲:14個字節的以太網首部+28個字節ARP請求/應答。以太網首部的格式爲:6個 字節的以太網目標地址+6個字節以太網源地址+2個字節幀類型,對於ARP來講,幀類型爲0x0806。ARP請求/應答的格式爲:2個字節的硬件類 型+2個字節的協議類型+1個字節的硬件地址長度+1個字節的協議地址長度+2個字節的操做碼+6個字節的發送端以太網地址+4個字節的發送端IP地 址+6個字節的目標以太網地址+4個字節的目標IP地址。硬件類型爲1表示的是以太網,協議類型爲0x0800表示的是IP地址,硬件地址長度和協議地址 長度分別爲6和4,它們都是以字節爲單位的,操做碼爲1表示的是ARP請求,爲2表示的是ARP應答。接口

在下面的測試程序中,咱們用交叉網線把開發板與PC機(操做系統爲Windows XP,網卡的IP地址爲192.168.1.120)相鏈接,咱們經過UART發出一個命令,讓開發板發出一個ARP請求數據包,而後接收來自PC機的應 答,並把該應答信息經過UART顯示出來。其中UART的中斷復位程序爲:

  1. void __irq uartISR(void)   
  2. {   
  3.        char ch;   
  4.        rSUBSRCPND |= 0x1;   
  5.        rSRCPND |= 0x1<<28;   
  6.        rINTPND |= 0x1<<28;   
  7.        ch=rURXH0;   
  8.        if(ch == 0x33)   
  9.               comm=3;               //表示發送一個ARP數據請求包   
  10.          
  11.        rUTXH0=ch;   
  12. }  

另外咱們還要事先定義一個遵循ARP協議格式的數組:

  1. unsigned char arpsendbuf[42]={   
  2.     
  3.        0xff,0xff,0xff,0xff,0xff,0xff,                     //以太網目標地址,全1表示爲廣播地址   
  4.        0x00,0x01,0x02,0x03,0x04,0x05,        //以太網源地址   
  5.        0x08,0x06,                                        //幀類型:ARP幀   
  6.          
  7.        0x00,0x01,                                        //硬件類型:以太網   
  8.        0x08,0x00,                                        //協議類型:IP協議   
  9.        0x06,                                                //硬件地址長度:6字節   
  10.        0x04,                                                //協議地址長度:4字節   
  11.        0x00,0x01,                                        //操做碼:ARP請求   
  12.          
  13.        0x00,0x01,0x02,0x03,0x04,0x05,        //發送端以太網硬件地址   
  14.        192, 168, 1, 50,                                 //發送端IP協議地址   
  15.        0x00,0x00,0x00,0x00,0x00,0x00,        //接收端以太網硬件地址   
  16.        192, 168, 1, 120                                 //接收端IP協議地址   
  17. };  

其中發送端硬件地址,即以太網源地址(00-01-02-03-04-05)是咱們初始化DM9000時定義的。而發送端IP協議地址是咱們任意定義的。

該測試程序的主程序爲:

  1. void Main(void)   
  2. {   
  3. ……   ……   
  4. //一些必要的初始化   
  5.     
  6.        comm=0;               //命令   
  7.        flag=0;                  //發送ARP請求包標識   
  8.          
  9.        dm_init();              //DM9000初始化   
  10.                 
  11.        while(1)   
  12.        {   
  13.               if(comm.==3)   
  14.               {       
  15.                      comm=0;   
  16.                      dm_tran_packet(arpsendbuf, 42 );               //發送ARP請求包   
  17.                      flag=1;           //置標識   
  18.               }   
  19. }   
  20. }  

接收網絡上的數據是經過外部中斷方式的,在這個中斷處理程序中,主要完成的是接收網卡數據,並把接收到的數據發送到UART,讓其顯示到PC機上。 這裏咱們還需解決一個問題,那就是當咱們發送一個ARP請求包的時候,XP系統並不會應答一個ARP數據包,而是應答一個IP協議數據包,當再屢次發出 ARP請求包後,纔會獲得ARP應答包。所以當s3c2440發送ARP請求包後,它首先要檢查所接收到的數據包,若是不是ARP應答包,它就要再次發送 ARP請求包,直到獲得ARP應答包爲止。所以中斷處理程序爲:

  1. void __irq DM9000ISR(void)   
  2. {   
  3.        int i;   
  4.          
  5.        rSRCPND = rSRCPND | (0x1<<4);   
  6.        rINTPND = rINTPND | (0x1<<4);   
  7.     
  8.        if(rEINTPEND&(1<<7))   
  9.        {   
  10.               rEINTPEND = rEINTPEND | (0x1<<7);   
  11.     
  12.               packet_len = dm_rec_packet(buffer);                  //接收網卡數據   
  13.     
  14.               if((buffer[12]==0x08)&&(buffer[13]==0x06))          //是ARP協議   
  15.               {              
  16.                      //經過UART顯示出來   
  17.                      for(i=0;i<packet_len;i++)   
  18.                      {   
  19.                             while(!(rUTRSTAT0 & 0x2)) ;   
  20.                             rUTXH0 = buffer[i];   
  21.                      }   
  22.     
  23.                      flag=0;                  //清標誌   
  24.               }   
  25.               else if(flag==1)             //若是在發出ARP請求包後,接收到的數據不是ARP協議   
  26.               {   
  27.                      comm=3;               //繼續發送ARP請求包   
  28.               }   
  29.        }   
  30. }  

這樣,整個網卡程序就編寫完畢。爲了使你們對程序的因果關係認識得更加清晰,咱們再敘述一遍程序的流程:首先初始化UART0,使其用中斷方式接收 數據,查詢方式發送數據;初始化EINT7,這是由於DM9000的數據中斷引腳INT是鏈接到s3c2440的外部中斷7引腳上的;而後初始化 DM9000,主要是配置一些它的寄存器,並使其用中斷方式接收網卡數據,查詢方式發送數據,這與UART0類似,最後是死循環等待UART0接收中斷服 務程序中獲得的發送ARP請求包命令。當獲得發送ARP請求包命令後,調用DM9000發送數據命令,發送事先準備好的一組數據。在發送完ARP數據 後,PC機會應答該請求,從而引起s3c2440外部中斷7中斷,在該中斷服務程序中,主要是完成接收ARP應答包的任務,並把它經過UART0顯示出 來。

當程序被執行完,並在PC機上經過串口調試軟件顯示出了一個正確的ARP應答包後,咱們還能夠經過下列方法來進一步驗證該程序的正確性:打開 Windows XP系統只帶的「命令提示符」小軟件,在提示符下輸入:arp –a,會出現咱們所設置的開發板的MAC地址(00-01-02-03-04-05)和IP地址(192.168.1.50),則說明Windows XP系統已經把咱們開發板上的網卡信息添加到了它的靜態列表中。

咱們對該系統進一步分析還會發現,當開發板上電而且DM9000初始化完成後,Windows XP系統會向該開發板發送一些目標地址爲廣播地址(FF-FF-FF-FF-FF-FF)的ARP數據包和IP數據包,只要咱們正確讀取它們,就能夠在開 發板上電後,自動知道與其相連的系統的MAC地址和IP地址了。另外,若是對這一部分感興趣,還能夠編寫ICMP協議的數據包,這樣就可讓PC機 ping通咱們的開發板了。

相關文章
相關標籤/搜索