วันศุกร์ที่ 1 มีนาคม พ.ศ. 2556

LPC1768-Mini-DK Web Server

LPC1768-Mini-DK Ethernet Web Server



                ช่วงเมื่อปิดเทอมที่แล้วพอดีมีโอกาสอยู่บ้านกับเจ้าบอร์ด LPC1768 ที่สั่งมาจากเว็ป www.micro4you.net ซึ่งสเป็คการใช้งานและราคาถือว่าคุ้มมากครับ และตัวบอร์ดเองก็สามารถต่อกับอุปกรณ์ภายนอกได้เยอะพอสมควรเลยครับ แต่บล็อกนี้ขอพูดถึงการเอาเจ้าบอร์ด LPC1768-Mini-DK มาเชื่อมต่อกับคอมพิวเตอร์ในรูปแบบ Ethernet Web Server นะครับ

               การเชื่อมต่อนั้นง่ายมากครับ อุปกรณ์ที่ต้องใช้ก้จะมีสาย LAN ที่เราทำเองหรือซื้อมาก็ได้นะครับสายที่ผมใช้อยู่เป็นสายไขว้ คือเอาไว้เชื่อมต่อโดยตรงระหว่างบอร์ดกับคอมพิวเตอร์ ส่วนโค้ดที่รันในตัว MCU เองผมเอามาจาก Example ครับใช้สองอันรวมกันโดยใช้ Lib ของ SD-Card+Ethernet ที่แถมมาให้ในแผ่น CD มาแก้นิดหน่อยเดี๋ยวเรามาดูกันครับว่าโค้ดเบื้องต้นนั้นเป็นอย่างไรกันบ้าง

ผมจะอธิบายไฟล์เดียวนะครับคือไฟล์ Easyweb.c ซึ่งสามารถโหลดโปรเจคที่ผมอัพโหลดเอาไว้ได้มีลิ้งค์อยู่ข้างล่างครับ

/******************************************************************
 *****                                                        *****
 *****  Name: easyweb.c                                       *****
 *****  Ver.: 1.0                                             *****
 *****  Date: 07/05/2001                                      *****
 *****  Auth: Andreas Dannenberg                              *****
 *****        HTWK Leipzig                                    *****
 *****        university of applied sciences                  *****
 *****        Germany                                         *****
 *****  Func: implements a dynamic HTTP-server by using       *****
 *****        the easyWEB-API                                 *****
 *****                                                        *****
 ******************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "LPC17xx.h"      // Keil: Register definition file for LPC17xx
#include "ff.h"
#include "./SPI_SD_Card/SPI_MSD_Driver.h"
#define extern            // Keil: Line added for modular project management
#include "easyweb.h"
#include "type.h"
#include "EMAC.h"         // Keil: *.c -> *.h    // ethernet packet driver
#include "tcpip.h"        // Keil: *.c -> *.h    // easyWEB TCP/IP stack
//#include "webpage.h"                             // webside for our HTTP server (HTML)
FATFS fs;         /* Work area (file system object) for logical drive */
FIL fsrc;         /* file objects */  
FILINFO fi;
void getPageDir(char*,char*);
void lastname(char*,char*);
int getType(char *p);
extern void TCPClockHandler(void);
unsigned char mounted=0;
unsigned char getFileActive=0;
char GetFile[100];
volatile DWORD TimeTick  = 0;
/* SysTick interrupt happens every 10 ms */
void SysTick_Handler (void) {
  TimeTick++;
  if (TimeTick >= 20) {
    TimeTick = 0;
    LPC_GPIO3->FIOPIN ^= 1 << 25;
    TCPClockHandler();
  }
}


//void main(void)
int main(void)
{
//InitOsc();              // Keil: No oscillator initialization necessary at this time.
//InitPorts();  // Keil: No port initialization necessary at this time.
  SystemInit();                                      /* setup core clocks */
  SysTick_Config(SystemFrequency/100);               /* Generate interrupt every 10 ms */
  LPC_GPIO0->FIODIR   |= 1 << 21;   LPC_GPIO0->FIOPIN  |= 1 << 21;
  LPC_GPIO3->FIODIR   |= 1 << 25;                    /* P2.0 defined as Output (LED) */
  TCPLowLevelInit();
MSD_SPI_Configuration();
  HTTPStatus = 0;                                // clear HTTP-server's flag register
  TCPLocalPort = TCP_PORT_HTTP;                  // set port we want to listen to

  while (1)                                      // repeat forever
  {
    if (!(SocketStatus & SOCK_ACTIVE)) TCPPassiveOpen();   // listen for incoming TCP-connection
    DoNetworkStuff();                                      // handle network and easyWEB-stack
               
if( _card_insert() == 0 && mounted == 0)
{ f_mount(0,&fs); mounted = 1;
}else if(_card_insert()){
mounted = 0;// events
}
    HTTPServer();
  }
}
// This function implements a very simple dynamic HTTP-server.
// It waits until connected, then sends a HTTP-header and the
// HTML-code stored in memory. Before sending, it replaces
// some special strings with dynamic values.
// NOTE: For strings crossing page boundaries, replacing will
// not work. In this case, simply add some extra lines
// (e.g. CR and LFs) to the HTML-code.
void HTTPServer(void)
{
char lName[4];
unsigned int len,lPut = 0;
int iType;
int a = sizeof(unsigned);
  if (SocketStatus & SOCK_CONNECTED)             // check if somebody has connected to our TCP
  {
    if (SocketStatus & SOCK_DATA_AVAILABLE)      // check if remote TCP sent data
{
getPageDir((char*)TCP_RX_BUF,&GetFile[0]);
      TCPReleaseRxBuffer();                      // and throw it away
}else if (SocketStatus & SOCK_TX_BUF_RELEASED){
      if (!(HTTPStatus & HTTP_SEND_PAGE) && getFileActive == 1)        // init byte-counter and pointer to webside
      { memcpy(TCP_TX_BUF, GetResponse, sizeof(GetResponse) - 1); lPut = sizeof(GetResponse) - 1;
if(mounted)// if called the 1st time
{ if(strcmp(GetFile,"0:/") == 0)
{
if(f_stat("0:/index.htm",&fi)==FR_OK)
{
const char res[]={
"HTTP/1.0 200 OK\r\n"
"Content-Type: text/html\r\n\r\n"
};
f_open(&fsrc,"0:/index.htm",FA_READ);
HTTPBytesToSend = fi.fsize; memcpy(TCP_TX_BUF, res, sizeof(res) - 1); TCPTxDataCount = sizeof(res);   }
else
{
char str2[]="HTTP/1.0 404 Not Found\r\n";
HTTPBytesToSend = 0; TCPTxDataCount = sizeof(str2);
memcpy(TCP_TX_BUF,str2,sizeof(str2)); }
}else{
lastname(&GetFile[0],lName);
iType=getType(lName);
if(f_stat(&GetFile[0],&fi)==FR_OK)
{ f_open(&fsrc,&GetFile[0],FA_READ);
HTTPBytesToSend = fi.fsize;
memcpy(TCP_TX_BUF + lPut,strContent,sizeof(strContent));
lPut += sizeof(strContent)-1;
memcpy(TCP_TX_BUF + lPut,ContentType[iType],strlen(ContentType[iType]));
lPut += strlen(ContentType[iType]);
memcpy(TCP_TX_BUF + lPut,"\r\n\r\n",4);
lPut += 4;
TCPTxDataCount =lPut;  
}
else
{
char str2[]="HTTP/1.0 404 Not Found\r\n";
HTTPBytesToSend = 0; TCPTxDataCount = sizeof(str2);
memcpy(TCP_TX_BUF,str2,sizeof(str2)); } } }
else
{
char str2[]="<html><body>Plase Insert SC-Card.</body></html>\r\n";
HTTPBytesToSend = 0; TCPTxDataCount = sizeof(str2);
memcpy(TCP_TX_BUF,str2,sizeof(str2));
}
TCPTransmitTxBuffer(); HTTPStatus |= HTTP_SEND_PAGE;              // ok, 1st loop executed       }
else if(HTTPBytesToSend)
{
f_read(&fsrc,TCP_TX_BUF,MAX_TCP_TX_DATA_SIZE,&len);
HTTPBytesToSend -= len;
TCPTxDataCount = len;
TCPTransmitTxBuffer();
}
if(HTTPBytesToSend < 1)
{ if(getFileActive == 1)
TCPClose();
getFileActive = 0;
f_close(&fsrc);
HTTPStatus &= ~HTTP_SEND_PAGE;
}
    }
  }
else
{
getFileActive = 0;
f_close(&fsrc);
HTTPStatus &= ~HTTP_SEND_PAGE;
}
}
void getPageDir(char* rxBuff,char *file_dir)
{ if(rxBuff[0]=='G' && rxBuff[1]=='E' && rxBuff[2]=='T')
{
file_dir[0]='0';
file_dir[1]=':';
file_dir[2]='/';
file_dir+=3;
rxBuff+=5;
while(*rxBuff != ' ')
{
*file_dir++ = *rxBuff++;
}
*file_dir = '\0';
getFileActive = 1;
}
}
void lastname(char* filname,char *lastname)
{
while(*filname++ != '.');
while(*filname != '\0')
{
*lastname = *filname++;
if(*lastname>='a' && *lastname<='z')
*lastname++ -= 0x20; }
*lastname = '\0';
}
int getType(char *p)
{
int i=0;
while(strcmp((char*)xContent[i++],p)!=0)
if(i==10)return 10; return i - 1;
}
//EOF

เราจะมาดูกันเป็นส่วนๆนะครับ

1.ส่วนของฟังก์ชั่น main() ครับ
//void main(void)
int main(void)
{
//InitOsc();              // Keil: No oscillator initialization necessary at this time.
//InitPorts();  // Keil: No port initialization necessary at this time.
  SystemInit();                                      /* setup core clocks */
  SysTick_Config(SystemFrequency/100);               /* Generate interrupt every 10 ms */
  LPC_GPIO0->FIODIR   |= 1 << 21;   LPC_GPIO0->FIOPIN  |= 1 << 21;
  LPC_GPIO3->FIODIR   |= 1 << 25;                    /* P2.0 defined as Output (LED) */
  TCPLowLevelInit();
  MSD_SPI_Configuration();
  HTTPStatus = 0;                                // clear HTTP-server's flag register
  TCPLocalPort = TCP_PORT_HTTP;                  // set port we want to listen to

  while (1)                                      // repeat forever
  {
    if (!(SocketStatus & SOCK_ACTIVE)) TCPPassiveOpen();   // listen for incoming TCP-connection
    DoNetworkStuff();                                      // handle network and easyWEB-stack
               
if( _card_insert() == 0 && mounted == 0)
{ f_mount(0,&fs); mounted = 1;
}else if(_card_insert()){
mounted = 0;// events
}
    HTTPServer();
  }
}

          บรรทัดแรกๆก็ไม่มีอะไรมากครับ เป็นส่วนการ Setup กับตั้งค่าส่วนต่างๆของ MCU ครับ มาดูกันครับว่าโค้ดเราเป็นอย่างไรบ้าง

                  - TCPLowLevelInit(); เรียกใช้ฟังก์ชันเพื่อตั้งค่าระบบ Ethernet ของ MCU ครับ
                  - MSD_SPI_Configuration(); เรียกใช้ฟังก์ชั่นเพื่อนตั้งค่า SPI ให้สามารถเชื่อต่อกับ SD-Card ได้
                  - while(1){} ใน Loop นี้จะเห็นว่าโปรแกรมถูกเขียนเป็น State เอาไว้คือทำงานกันตลอดเวลาตามลำดับคือเมื่อมีการเชื่อมต่อจากเครื่องลูกข่ายเข้ามายัง MCU โปรแกรมจะเข้าไปทำงานในส่วน HTTPServer(); ครับ

-----------------------------------------------------------------------------------------------------------
void HTTPServer(){}

เดี๋ยวเรามาดูกันครับว่าผมเขียนอะไรเข้าไปบ้างในฟังก์ชั่นนี้

              - เริ่มเเรกเลยคือโปรแกรมเราจะเช็คว่า มีการเชื่อมต่อและส่งข้อมูลจากลูกข่ายหรือไม่
ถ้ามีการส่งข้อมูลเข้ามา โปรแกรมจะแปรคำสั่งจากลูกข่ายซึ่งใช้ฟังก์ชั่น
getPageDir((char*)TCP_RX_BUF,&GetFile[0]);
ฟังก์ชั่นนี้จะจัดการกับคำสั่งที่ได้รับมาจาก Client ครับคือลูกข่ายจะเรียกข้อมูลหรือไฟล์จาก Server โดยมีคำสั่ง GET (file directory) ครับคือขึ้นต้นด้วย GET หรือ POST ครับ แต่โปรแกรมที่ผมเขียนไว้ให้นั่น มันสามารถรับได้แค่คำสั่ง GET ครับ

              - เมื่อเราอ่านข้อมูลที่ได้รับมาจาก  Client แล้วเราจะมาเช็คว่า ลูกข่ายนั้นต้องการไฟล์หรือข้อมูลอะไรครับ ซึ่งจะทำงานภายใต้กรอบ
}else if (SocketStatus & SOCK_TX_BUF_RELEASED){
                คือเมื่อมีการส่งหรือรับข้อมูลเสร็จแล้ว โปรแกรมจะเข้ามาทำงานในส่วนนี้ครับ มาดูคำสั่งคร่าวๆกันครับ
if (!(HTTPStatus & HTTP_SEND_PAGE) && getFileActive == 1) 
                    เมื่อเริ่มต้นการส่งข้อมูลหรือไฟล์ไปยัง Client เราจะต้องส่ง HEADER ไปก่อนครับว่าเราจะส่งไฟล์แบบไหน ประเภทอะไร ไฟล์เก็บข้อมูลในลักษณะอย่างไร ดูคำสั่งใน easyweb.h นะครับ เมื่อเราได้แยกประเภทการส่งแล้ว เราจะส่ง HEADER ไปหา Client ครับลูกข่ายจะรู้ว่าจะต้องเก็บไฟล์แบบนี้อย่างไร เช่นในกรอบของ
if(strcmp(GetFile,"0:/") == 0)
                    คือลูกข่ายนั้นต้องการไฟล์ Index ของ Server ซึ่งผมได้ตั้งให้เป็น index.htm ผมก็จะเริ่มเปิดไฟล์และอ่านข้อมูลในไฟล์เพื่อส่งไปยังลูกข่ายครับ แต่ถ้าหากไฟล์มีขนาดมากกว่า TX Buffer ของ MCU เราก็จะเป็นจะต้องส่งหลายๆรอบครับ เรียกการส่งแบบนี้ว่าเป็นการส่งข้อมูลแบบ Page คือส่งไปทีละชุดจนกว่าข้อมูลจะหมด แล้วเลิกเชื่อมต่อกับ Client ซึ่งฝั่ง Client จะทราบเองว่าข้อมูลของไฟล์นี้ สิ้นสุดแล้ว

เรามาดูการทำงานเป็นขั้นกันครับ

  1. เช็คว่าลูกข่ายต้องการไฟล์อะไร
  2. เช็คว่าไฟล์ที่ต้องการมีอยู่ในระบบหรือไม่
  • ถ้าไฟล์ไม่มีในระบบ ให้ส่ง HEADER ว่าไม่พบไฟล์ ไปทำงานที่ข้อ 3
  • ถ้าไฟล์มีในระบบ ให้ส่ง HEADER ไปว่าพบไฟล์ และจะส่งข้อมูลในรูปแบบไหนเป็น TEXT ,Binary ฯลฯ
  • ส่งข้อมูลในไฟล์ไปยัง Client และเช็คว่าข้อมูลที่ส่งไปนั่นหมดหรือยัง สมมุติ TX Buffer ผมมี 512 Byte ผมส่งไฟล์ขนาด 800 Byte ผมจะต้องส่งข้อมูล 2 รอบ คือรอบแรก 512 Byte และรอบสอง 288 Byte เป็นต้น
     3.ปิดการเชื่อมต่อกับ Client

           ในการเรียกข้อมูลของ Client ในแต่ละครั้งนั้นจะขึ้นอยู่กับว่าหน้าเว็ปของเรานั้นใช้ไฟล์ในหนึึ่งหน้าเว็ปนั้น กี่ไฟล์ ถ้ามีมากกว่าหนึ่ง ก็จะมีการเชื่อมต่อกับลูกข่ายอีกครั้ง เช่น ลูกข่ายเรียกไฟล์ index ของเว็ปเรา แล้วเรา้ส่งข้อมูลของหน้า index ไปแล้วลูกข่ายแปลคำสั่งในหน้าเว็ปว่ามีไฟล์ที่ต้องใช้อีก 1 ไฟล์ (สมมุติเป็นไฟล์ภาพแล้วกันครับ) ลูกข่ายจะเชื่อมต่อกับ Server อีกครั้งพร้อมเรียกไฟล์จาก Server ซึ่งไฟล์ที่เรียกนั้นก็จะเป็นไฟล์ภาพหรือไฟล์ที่ลูกข่ายนั้นต้องใช้ในการประมวลหรือแสดงผลในหน้าเว็ปนั้นๆครับ
           การทำ Web Server โดยการใช้ MCU เบื้องต้นก็จะมีกันประมาณนี้ครับ ข้อมูลผมมันอาจจะไม่ละเอียดพอ ยังไงถ้าสงสัยหรือมีข้อซักถามยังไง ก็สอบถามเข้ามาได้ครับ หรือส่ง Email ไปที่ mr.ozuke@gmail.com ได้ครับ ถ้าข้อมูลมีความผิดพลาดประการใดก็ขออภัยไว้ ณ ที่นี้ด้วยนะครับ


           สุดท้ายต้องขอบคุณ
           - บอร์ด LPC1768-Mini-DK จากเว็ป www.micro4you.net ด้วยครับ ราคาถูกและคุ้มค่าที่จะซื้อมาศึกษาการใช้งาน MCU ในระดับที่สูงขึ้นครับ
           - www.electoday.com เว็ปบอร์ดที่เป็นแหล่งข้อมูลที่ดีมากๆอีกเว็ปหนึ่งครับ


ไฟล์โปรเจ็ค LPC1768-Mini-DK Web Server สามารถ Download ได้ตามลิ้งค์ข้างล่างนี้ครับ

ไม่มีความคิดเห็น:

แสดงความคิดเห็น