ALIENTEK 戰艦ENC28J60 LWIP和UIP補充例程(LWIP WEB有驚喜)

前面的話:自從接觸網絡模塊,到如今有一陣子時間了,將來一定是網絡的世界。學一些網絡方面的知識是有必要的。咱們ALINTEK 推出的ENC28J60網絡模塊塊做爲入門仍是不錯的。詳細見此貼:
http://www.openedv.com/posts/list/9355.htm。時間對於一個開發人員是很寶貴的,如何快速應用是咱們作技術的,都想要的。廢話很少說了。由於主要集中在怎麼應用因此有些細節可能不是正確的,
這個須要你們去質疑,去驗證。

1、LWIP的應用
1.什麼是LWIP?
lwip是瑞典計算機科學院(SICS)的Adam Dunkels 開發的一個小型開源的TCP/IP協議棧。

2.哪裏能夠下載源碼?
在這裏能夠下載到最新的應用:http://savannah.nongnu.org/projects/lwip/

3.更多詳細介紹?
在這裏有詳細的:http://lwip.wikia.com/wiki/LwIP_Wiki

4.如何移植?
LWIP 有三種應用模式 RAW API,Netconn API,Socket API.這裏咱們主要簡單講解如何移植RAW API。移植LWIP的過程也是一個瞭解LWIP大概結構的過程。
當你把全部的錯誤,和警告幹掉的後就差很少了,呵呵。 
①首先到官網上下載好源碼和應用例程。
<ignore_js_op> 
lwip-1.4.1中src文件夾下的源碼文件結構:
<ignore_js_op> ②按如下這個目錄結構將源碼加入到工程
<ignore_js_op> 
前面三個就不用解釋了,看看上面的源碼文檔結構就知道了,LWIP_ARCH文件夾是什麼東東呢?它是一個操做系統與處理器平臺配置有關的代碼,你要問我這個東西哪裏來的,
嘿嘿,從ST的官方固件庫中偷來滴。裏面包含了大小端的配置,類型定義,和操做系統有關的部分,不過這裏沒有用到操做系統,因此這個sys_arch.c文件須要改裝下。
LWIP_APP顧名思義就是LWIP的應用程序了,這裏面編寫了,TCP服務器,TCP客戶端,UDP服務器,UDP客戶端,Webserver(應用程序和服務器的接口技術(CGI),動態網頁技術(SSI),和ajax技術,作異步提交,獲取數據)。的方法和例程。接下來請看我如何修改移植。


③底層驅動的移植。
在LWIP-NETIF文件加下,咱們須要對ethernetif.c文件修改。該文件是一個底層網絡接口框架。LWIP的做者已經爲咱們搭好了框架,其中有些代碼是僞代碼,咱們只須要去實現就行了。

A.修改底層硬件初始化函數low_level_init(struct netif *netif)函數,在函數中設置好,網絡的MAC地址,最大傳輸單元,而後再初始化ENC28J60,若是初始化失敗則返回錯誤給上層調用的函數。
<ignore_js_op> 

B.修改底層發送包的函數low_level_output(struct netif *netif, struct pbuf *p),在這個函數中將LWIP數據包中的緩衝區pbuf 複製到待發送的緩衝區lwip_buf中來,
而後利用ENC28J60的發包函數發出去。
<ignore_js_op> 
C.修改底層接收包的函數low_level_input(struct netif *netif),這個過程就是把接收緩衝區lwip_buf中的數據複製到LWIP的pbuf中,若是收到了包, 咱們就能夠在lwip_buf中讀取數據了。注:lwip_buf做爲接收和發送數據的緩衝。
<ignore_js_op> 
移植完這三個函數就差很少啦,簡單吧,嘿嘿。javascript

④在主函數main.c初始化LWIP。
lwip_init()是用來LWIP初始化的,可是其中的netif_init()初始化的是迴環網絡接口,咱們想把數據發出去必須初始化ENC28J60。因此咱們先調用lwip_init()來初始化LWIP的各個模塊。
再利用netif_add函數添加網絡接口就能夠。這個函數會調用底層的 low_level_init函數,若是返回值爲空,則說明ENC28J60初始化失敗了。而後再註冊,創建一下這個網絡接口就OK了。
<ignore_js_op> 注意到上面的有一個函數 init_lwip_timer();  這個是什麼呢,這個是用來初始化LWIP的定時器,用來計時用的,至關於LWIP的心臟。

⑤在主函數main.c中編寫輪訓協議棧函數
LWIP須要週期性的處理一些函數,這些函數是是根據你使能的協議和功能模塊來調用的。好比你要用到TCP協議,你就必須週期性的調用tcp_tmr()。
注意到有個timer_expired函數,這個函數就在sys_arch.c文件中,這個文件原本與操做系統有關,如今改裝了。用來初始化LWIP的時鐘,提供超時檢測的功能,至關於定時器了。

<ignore_js_op> 
⑥配置LWIP
LWIP有一個標準配置文件在opt.h中,這個頭文件又包含了lwipopts.h,一般咱們配置LWIP是經過這個文件來配置的。
這個頭文件能夠覆蓋opt.h中任何你須要的配置。這樣作的好處,是源碼更加健壯了,並且可以防止咱們錯誤的配置,而改不回去了。
有幾個配置是很重要的,NO_SYS  1-----無操做系統   MEM_ALIGNMENT    4 ---  分配內存是四字節對齊,這個很重要,曾經莫名其妙的出現hardfault
TCP_MSS      1460 --TCP最大段大小,這個是極限值了,咱們能夠傳更多的數據。
<ignore_js_op> 


⑦在main函數中加入LWIP_Polling(),基本上就移植完畢了。這時可使用ping 命令。
好比我設置的IP地址爲19.168.1.10則,經過cmd命令進入dos環境。經過 ping 192.168.1.10 -t 就能夠不斷查詢網絡是否聯通了。php

 

5.如何應用?
RAW API中文譯爲原始API能夠說比較接近底層了,玩過socket編程的人都知道,socket編程用起來,是比較簡單了,不像RAW API的模式,用起來比較麻煩,須要應用者對TCP,UDP這些協議,有一個
稍微深刻的瞭解。這裏我推薦一款抓包軟件Wireshark。這個軟件能夠幫助你分析這些協議是怎麼工做的。不過在用這個軟件的,分析協議的時候最好不要鏈接到外網,會干擾的哦。


①TCP服務器和客戶端
A.TCP通訊簡介
TCP是面向鏈接的,在進行通訊前須要創建鏈接。下面以TCP服務器模式爲例簡單介紹下這個過程。
創建鏈接:(截圖看不清見附件)創建鏈接.jpg發送數據:
發送數據.jpg
關閉鏈接:
服務器關閉鏈接.jpg

B.TCP服務器模式
RAW API大部分都是基於回調函數的API,咱們須要按照必定的規範去實現這個回調函數。做爲TCP服務器,必需要一個本地IP和端口。
處於服務器模式,是不須要設置遠程主機IP和端口,由於遠程主機在鏈接到服務器的過程中,服務器會把它的IP地址等信息記錄下來。就能夠雙向傳輸了。
tcp_bind(tcp_server_pcb,IP_ADDR_ANY,TCP_SERVER_PORT);//綁定本地全部IP地址和端口號 
tcp_listen(tcp_server_pcb); //開始監聽端口

tcp_accept(tcp_server_pcb,tcp_server_accept); //指定監聽狀態的鏈接聯通以後將要調用的回調函數
tcp_recv(newpcb, tcp_server_recv); //指定鏈接接收到新的數據以後將要調用的回調函數tcp_err(newpcb, tcp_server_error); //指定鏈接出錯將要調用的函數tcp_poll(newpcb, tcp_server_poll, 0); //指定輪詢時將要調用的回調函數對於不熟悉這種習慣的,開始可能有點迷糊糊,不過看多了就行了。上面四個函數,都是用來指定回調函數的。
他們指定的回調函數在指定事件(好比創建了鏈接,接收到了數據,鏈接空閒等等)發生的時候將會被調用。

C.TCP客戶端模式
對於TCP客戶端模式,咱們須要指定鏈接的遠程服務器的IP地址和端口號,其餘的函數就很少說了,詳細見代碼
ip_addr_t ipaddr;IP4_ADDR(&ipaddr, 192, 168, 1, 101);   //設置本地ip地址tcp_connect(tcp_client_pcb,&ipaddr,TCP_CLIENT_PORT,tcp_client_connect);  //鏈接到遠程服務器

②UDP的服務器和客戶端
相對於TCP來講UDP則就簡單了許多,他不須要像TCP同樣須要創建鏈接通道才能夠進行通訊。沒有重發機制。因此它是不可靠的通行。可是速度快,是他的一大亮點。
A:UDP服務器模式
udp_bind(udp_server_pcb,IP_ADDR_ANY,UDP_SERVER_PORT);  //幫頂本地IP地址和端口udp_recv(udp_server_pcb,udp_server_rev,NULL); //指定收到數據包時的回調函數不過須要注意在接受數據的udp_server_rev回調函數中須要將客戶端的IP地址和端口記下來,方便UDP服務器發送數據給客戶端。
udp_server_pcb->remote_ip = *addr; //記錄遠程主機的IPudp_server_pcb->remote_port = port;//記錄遠程主機的端口號
B:UDP客戶端模式
udp_bind(udp_client_pcb,IP_ADDR_ANY,UDP_CLIENT_PORT); //這裏能夠綁定任意的端口,不必定是服務器的端口號,做爲客戶端主要關心的是要鏈接的端口和IPudp_connect(udp_client_pcb,&ipaddr,UDP_CLIENT_PORT);  //設置鏈接到遠程主機
③web服務器
如今那種browse+server的模式很流行,在PC機中。這種模式,客戶端只須要一個瀏覽器就OK了,不須要下載專門的客戶端,如今的網頁遊戲這麼流行就證實了這一點。你想一想在家裏,
個人手機經過WIFI鏈接到路由器,你的嵌入式服務器也鏈接到路由器,只要有手機,不須要攜帶笨重的電腦,裝麻煩軟件,就能夠控制家裏的電器,多爽啊,這就是物聯網的應用。
如何減小嵌入式服務器的開發難度,是不少人研究的重點。感受LWIP的CGI和SSI接口用起來仍是比較爽的,結合一些流行的web前端技術,咱們能夠開發比較漂亮的應用。

A.什麼是CGI?
CGI是Common Gateway Interface通用網關接口的簡稱。百度一下解釋多多。其實,他的做用是用來讓服務器和應用程序打交道的。當咱們從瀏覽器獲取參數和對應的值後,而後與應用程序
進行交互的這個函數,就是CGI函數了。對應的URLl路徑就寫一個對應的CGI函數。這樣不一樣的請求,就有不一樣的處理,這個函數將會返回,你想發送給瀏覽器的文件的文件名路徑。

B.CGI函數介紹
typedef  char *(*tCGIHandler)(int iIndex, int iNumParams, char *pcParam[],                             char *pcValue[]);參數說明:
iIndex :表示在ppcURLs數組中的索引號碼, ppcURLs 是個什麼玩意呢?
ppcURLs  的類型是這個東東。
typedef struct{    const char *pcCGIName;      //瀏覽器請求的URL    tCGIHandler pfnCGIHandler;  //定義的CGI函數指針} tCGI;那麼這個東東有什麼做用呢?,他是用來註冊URL路徑和對應的函數,void http_set_cgi_handlers(const tCGI *pCGIs, int iNumHandlers),這個函數會對它進行設置。當瀏覽器發出一個請求URL時,這個URL路徑對應的CGI函數就會執行。
lwip已經幫咱們作好了,我只修須要註冊URL路徑和對應的CGI函數就能夠了,爽吧。
iNumParams :表示URL中你請求參數和其參數對應值的個數,好比http://192.168.1.16/login.cgi?name=onetree&password=12345,參數是name和password,值是onetree和12345,個數就是2.pcParam :參數的數組,包含全部的參數
pcValue:  對應參數的值的數組
C.CGI應用舉例
好比。瀏覽器,採用get的方式,發了一個請求。http://192.168.1.16/login.cgi?/name=onetree&password=12345
咱們的應用程序怎麼接收咱們的用戶名name和密碼password呢?

1.註冊URL路徑和對應的函數
static const tCGI ppcURLs[] ={    { "/login.cgi",      Login_CGIHandler },  };

2.設置請求的URL的路徑和CGI函數
#define NUM_CONFIG_CGI_URIS     (sizeof(ppcURLs ) / sizeof(tCGI))
 http_set_cgi_handlers(ppcURLs , NUM_CONFIG_CGI_URIS); 

3.編寫CGI函數
static char * Login_CGIHandler  ( int iIndex, int iNumParams, char *pcParam[], char *pcValue[] ){
  int index;
  index = FindCGIParameter ( "name", pcParam, iNumParams );
  if(index!=-1){
      name對應的參數值=  pcValue[index];      
  }
   index = FindCGIParameter ( " password", pcParam, iNumParams ); 
  if(index!=-1){
    password對應的參數值 =pcValue[index];  }
 return  "/index.html";  //這裏返回你要發給瀏覽器的文件名路徑

}
注意到有個 FindCGIParameter  的函數,這個函數是用來查找你想要的參數。
static int FindCGIParameter(const char *pcToFind, char *pcParam[], int iNumParams){    int iLoop;
    for(iLoop = 0; iLoop < iNumParams; iLoop++)    {        if(strcmp(pcToFind, pcParam[iLoop]) == 0)        {            return(iLoop);        }    }    return(-1);}到此爲止,咱們就完成了,瀏覽器和應用程序的交互了。

D.什麼是SSI?
SSI是Server Side Include服務器嵌入端的簡稱。是一種相似於ASP的基於服務器的網頁製做技術。
工做原理:LWIP對於.shtml,.ssi,.shtm後綴的文件,會檢測文件中<!--#name-->格式的TAG標誌。
而後再這個標記後面添加你想要的字符串。並非替換,不過這個方法在腳本中不行, <!--#name-->
是html文件的註釋,可是在 <script>...</script>中就不是註釋了,因此在添加js代碼的時候必須把整個
JS腳本添加進來。

E.SSI函數簡介
typedef int (*tSSIHandler)(int iIndex, char *pcInsert, int iInsertLen)這個函數除了這三個參數外,還有其餘三個可選的參數,這裏沒寫出來,具體怎麼用交給你們研究啦。
參數說明:
iIndex:在ppcTags數組中的索引號,ppcTags是一個字符串數組,用來標記tag名字的,好比<!--#name-->,「name」就是一個tag名
pcInsert:在tag後面插入的字符串
iInsertLen:在tag後面插入字符串的長度

F.SSI應用舉例
1.新建一個index.shtml的文件,在文件中輸入一下代碼:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>welcom</title><meta http-equiv="Content-Type" content="text/html; charset=gb2312" /><meta   http-equiv= "Cache-Control"   content= "no-cache" /> </head><body><form id="login" name="login" method="get" action="login.cgi"><table width="200" border="1" align="center"><tr><td colspan="2" align="center">請按F5進行刷新</td></tr>
<tr><td width="50">內容:</td><td width="134"><label><!--#alientek--></label></td></tr></table></form></body></html>注意到上面有一個 <!--#alientek--> 的TAG,,利用LWIP的SSI的功能能夠將咱們想插入的字符串插入到後面。

2.編寫SSI處理函數
static const char *ppcTags[] =  //TAG數組{    " alientek ",                 };enum ssi_index_s        //索引號{    SSI_INDEX_ALIENTEK_GET = 0, //該表對應ppcTags[]的排序} ;
static int SSIHandler ( int iIndex, char *pcInsert, int iInsertLen ){       switch(iIndex)    {        case  SSI_INDEX_ALIENTEK_GET:
           pcInsert = "alentek";
           iInsertLen = strlen(pcInsert );            break;               default:            strcpy( pcInsert , "" );                }    return  iInsertLen ;  //返回字符串的長度}
 
3.再編寫一個返回URL爲/index.shtml的文件的CGI函數就能夠了。在頁面中就能夠看到alientek內容了。


最後重點介紹下ajax作異步提交,更新數據:
1、如何將靜態頁面轉換爲16進制的c語言數組?
前面說了這麼多都沒提這個重要的問題,但願你們別見怪,嘿嘿。
這裏我謝謝 zhangpisces網友提供的資料。他的資料給了我很大的參考價值,喝水不忘,挖井人。 
步驟:
1.首先將網頁源文件編寫好,如工程中atk文件夾下的文件。2.將makefsfile工具和atk放在一個文件夾內.3.運行cmd,進入到makefsfile工具的目錄。4.使用makefsfile -i  atk  -o fsdata.h -r -h 命令生成一個 fsdata.h文件
注意:在fs.c文件中須要註釋掉//#include "fsdata.c"這個,咱們不用。

二,添加/response.ssi文件
#define RESPONSE_BUF_SIZE 512   //http響應緩存大小
unsigned char data_response_ssi[RESPONSE_BUF_SIZE+14] ={ /* /response.ssi */ 0x2F, 0x72, 0x65, 0x73, 0x70, 0x6F, 0x6E, 0x73,  0x65, 0x2E, 0x73, 0x73, 0x69, 0x00, };struct fsdata_file file_response_ssi[] ={ { NULL, data_response_ssi, data_response_ssi + 14, sizeof(data_response_ssi) - 14 }};makefsfile 生成的都是ROM文件,若是咱們想要改變返回給瀏覽器的響應內容必須定義一個能夠變化的文件。
在CGI函數中咱們就能夠把想發送的數據寫入到這個文件當中,return "/response.ssi"就OK了。
3、編寫基於ajax的JS代碼
我把戰艦原來uip的web例程(不過這個例程並無用到SSI,是綜合例程)改爲了ajax異步獲取數據,參考JS源碼以下:
<script type="text/javascript">var xmlhttp;function loadXMLDoc(url,cfunc){  if (window.XMLHttpRequest)  {     xmlhttp=new XMLHttpRequest();  }  else  {     xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");  }  xmlhttp.onreadystatechange=cfunc;  xmlhttp.open("GET",url,true);  xmlhttp.send();}
function led0(){  loadXMLDoc("/led_red.cgi?red=1&t="+ Math.random(),function()  {    if (xmlhttp.readyState==4 && xmlhttp.status==200)    { document.getElementById("red").src=xmlhttp.responseText;    }  });}
function led1(){  loadXMLDoc("/led_green.cgi?green=1&t="+ Math.random(),function()  {    if (xmlhttp.readyState==4 && xmlhttp.status==200)    {  document.getElementById("green").src=xmlhttp.responseText;    }  });}var text;
function update(){   loadXMLDoc("/orther.cgi?t="+ Math.random(),function()  {    if (xmlhttp.readyState==4 && xmlhttp.status==200)    {  text = xmlhttp.responseText;  text = text.split(";");  document.getElementById("temperature").innerHTML=text[0]+"℃";  document.getElementById("time").innerHTML=text[1];
    }  });
}function init(){ setInterval(update,1000);  }
</script>

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
到如今爲止基本上講完了,第一次發這麼長的帖子,各位看官慢慢看。有錯誤請多多包容哦^_^
附件中還有戰艦的UIP補充例程,添加了UDP的服務器和客戶端模式,web沒怎麼搞。
還有LWIP例程。包括了TCP服務器,TCP客戶端,UDP服務器,UDP客戶端,WEBSEVER。這些功能,配合咱們的ALIENTEK的ENC28J60模塊就能夠
直接在戰艦開發板上運行了。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
LWIP例程說明:
KEY_UP:選擇實驗(TCP服務器,TCP客戶端,UDP服務器,UDP客戶端,WEBSEVER)KEY_DOWN:發送數據
建議你們先學習UIP,而後再學LWIP可能效果要好些,畢竟UIP仍是要簡單得多。


<ignore_js_op>

創建鏈接 .jpg (65.03 KB, 下載次數: 1761)html

 

創建鏈接 .jpg
<ignore_js_op>

發送數據包.jpg (21.27 KB, 下載次數: 1298)前端

 

發送數據包.jpg
<ignore_js_op>

服務器關閉鏈接.jpg (31.6 KB, 下載次數: 1103)java

 

服務器關閉鏈接.jpg
<ignore_js_op>

makefsfile.rarweb

 

20.68 KB, 下載次數: 4854ajax

 

<ignore_js_op>

atk.rar編程

 

53.25 KB, 下載次數: 4647數組

 

<ignore_js_op>

ALIENTEK ENC28J60 UIP.rar瀏覽器

 

494.66 KB, 下載次數: 7081

 

<ignore_js_op>

ALIENTEK ENC28J60 LWIP.rar

 

轉載自:http://www.openedv.com/forum.php?mod=viewthread&tid=25178&extra=&page=1

相關文章
相關標籤/搜索