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 จะทราบเองว่าข้อมูลของไฟล์นี้ สิ้นสุดแล้ว
เรามาดูการทำงานเป็นขั้นกันครับ
- เช็คว่าลูกข่ายต้องการไฟล์อะไร
- เช็คว่าไฟล์ที่ต้องการมีอยู่ในระบบหรือไม่
- ถ้าไฟล์ไม่มีในระบบ ให้ส่ง 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 ได้ตามลิ้งค์ข้างล่างนี้ครับ
ไม่มีความคิดเห็น:
แสดงความคิดเห็น