Code: {* * Project Name: enc28j60Demo (Ethernet Library demo for ENC28J60 mcu) * Target Platform: PIC * Copyright: (c) mikroElektronika, 2006. * Revision History: 20060810: - Initial release. Author: Bruno Gavand. * * V1.0 : first release * V1.1 : bad MIME type for / path request, changed to HTML instead of SCRIPT (thanks Srdjan !) * * description : * this code shows how to use the Spi_Ethernet mini library : * the board will reply to ARP & ICMP echo requests * the board will reply to UDP requests on any port : * returns the request in upper char with a header made of remote host IP & port number * the board will reply to HTTP requests on port 80, GET method with pathnames : * / will return the HTML main page * /s will return board status as text string * /t0 ... /t7 will toggle RD0 to RD7 bit and return HTML main page * all other requests return also HTML main page * * target devices : * any PIC with integrated SPI and more than 4 Kb ROM memory * 32 to 40 MHz clock is recommended to get from 8 to 10 Mhz SPI clock, * otherwise PIC should be clocked by ENC clock output due to ENC silicon bug in SPI hardware * if you try lower PIC clock speed, don't be surprised if the board hang or miss some requests ! * * EP settings : * RA2 & RA3 pots jumper : closed * PORTA : pull-down (place jumper J1 to lower position) (board specific) * PORTB : pull-down (place jumper J2 to lower position) (board specific) * PORTC : pull-up (place jumper J3 to upper position) (board specific) * BUTTONS : pull-up (place jumper J17 to upper position) (board specific) * * RC0 : !RESET to ENC reset input pin * RC1 : !CS to ENC chip select input pin * the ENC28J60 SPI bus CLK, SO, SI must be connected to the corresponding SPI pins of the PIC * the INT and WOL signals from the ENC are not used * * Test configuration: MCU: PIC16F887 http://ww1.microchip.com/downloads/en/DeviceDoc/41291F.pdf Dev.Board: EasyPIC6 http://www.mikroe.com/en/tools/easypic6/ Oscillator: HS, 08.000MHz Ext. Modules: mE Serial Ethernet board http://www.mikroe.com/en/tools/serialeth/ SW: mikroPascal PRO for PIC http://www.mikroe.com/en/compilers/mikropascal/pro/pic/ * NOTES: - Since the ENC28J60 doesn't support auto-negotiation, full-duplex mode is not compatible with most switches/routers. If a dedicated network is used where the duplex of the remote node can be manually configured, you may change this configuration. Otherwise, half duplex should always be used. - External power supply should be used due to Serial Ethernet Board power consumption.
*}
program enc_ethernet;
{*********************************** * RAM variables *} var myMacAddr : array[6] of byte ; // my MAC address myIpAddr : array[4] of byte ; // my IP address
// mE ehternet NIC pinout SPI_Ethernet_Rst : sbit at PORTC.B0; SPI_Ethernet_CS : sbit at PORTC.B1; SPI_Ethernet_Rst_Direction : sbit at TRISC.B0; SPI_Ethernet_CS_Direction : sbit at TRISC.B1; // end ethernet NIC definitions
{************************************************************ * ROM constant strings *} const httpHeader : string[30] = 'HTTP/1.1 200 OK'+#10+'Content-type: ' ; // HTTP header const httpMimeTypeHTML : string[11] = 'text/html'+#10+#10 ; // HTML MIME type const httpMimeTypeScript : string[12] = 'text/plain'+#10+#10 ; // TEXT MIME type const httpMethod : string[5] = 'GET /'; {* * web page, splited into 2 parts : * when coming short of ROM, fragmented data is handled more efficiently by linker * * this HTML page calls the boards to get its status, and builds itself with javascript *} const indexPage : string[759] = '<meta http-equiv="refresh" content="3;url=http://192.168.20.60">' + '<HTML><HEAD></HEAD><BODY>'+ '<h1>PIC + ENC28J60 Mini Web Server</h1>'+ '<a href=/>Reload</a>'+ '<script src=/s></script>'+ '<table><tr><td valign=top><table border=1 style="font-size:20px ;font-family: terminal ;">'+ '<tr><th colspan=2>ADC</th></tr>'+ '<tr><td>AN2</td><td><script>document.write(AN2)</script></td></tr>'+ '<tr><td>AN3</td><td><script>document.write(AN3)</script></td></tr>'+ '</table></td><td><table border=1 style="font-size:20px ;font-family: terminal ;">'+ '<tr><th colspan=2>PORTB</th></tr>'+ '<script>'+ 'var str,i;'+ 'str="";'+ 'for(i=0;i<8;i++)'+ '{str+="<tr><td bgcolor=pink>BUTTON #"+i+"</td>";'+ 'if(PORTB&(1<<i)){str+="<td bgcolor=red>ON";}'+ 'else {str+="<td bgcolor=#cccccc>OFF";}'+ 'str+="</td></tr>";}'+ 'document.write(str) ;'+ '</script>';
const indexPage2 : string[466] = '</table></td><td>'+ '<table border=1 style="font-size:20px ;font-family: terminal ;">'+ '<tr><th colspan=3>PORTD</th></tr>'+ '<script>'+ 'var str,i;'+ 'str="";'+ 'for(i=0;i<8;i++)'+ '{str+="<tr><td bgcolor=yellow>LED #"+i+"</td>";'+ 'if(PORTD&(1<<i)){str+="<td bgcolor=red>ON";}'+ 'else {str+="<td bgcolor=#cccccc>OFF";}'+ 'str+="</td><td><a href=/t"+i+">Toggle</a></td></tr>";}'+ 'document.write(str) ;'+ '</script>'+ '</table></td></tr></table>'+ 'This is HTTP request #<script>document.write(REQ)</script></BODY></HTML>';
var getRequest : array[15] of byte; // HTTP request buffer dyna : array[30] of char; // buffer for dynamic response tmp: string[11]; // auxiliary ram string httpCounter : word ; // counter of HTTP requests
{******************************************* * user defined functions *}
{* * this function is called by the library * the user accesses to the HTTP request by successive calls to SPI_Ethernet_getByte() * the user puts data in the transmit buffer by successive calls to SPI_Ethernet_putByte() * the function must return the length in bytes of the HTTP reply, or 0 if nothing to transmit * * if you don't need to reply to HTTP requests, * just define this function with a return(0) as single statement * *}
function SPI_Ethernet_UserTCP(var remoteHost : array[4] of byte; remotePort, localPort, reqLength : word; var canClose: byte) : word; var i : word ; // my reply length bitMask : byte ; // for bit mask begin result := 0;
// should we close tcp socket after response is sent? // library closes tcp socket by default if canClose flag is not reset here // canClose := 0; // 0 - do not close socket // otherwise - close socket
if(localPort <> 80) then // I listen only to web request on port 80 begin result := 0; exit; end;
// get 10 first bytes only of the request, the rest does not matter here for i := 0 to 9 do getRequest[i] := SPI_Ethernet_getByte() ; getRequest[i] := 0 ;
// copy httpMethod to ram for use in memcmp routine for i := 0 to 4 do tmp[i] := httpMethod[i];
if(memcmp(@getRequest, @tmp, 5) <> 0) then // only GET method is supported here begin result := 0 ; exit; end;
Inc(httpCounter) ; // one more request done
if(getRequest[5] = 's') then // if request path name starts with s, store dynamic data in transmit buffer begin // the text string replied by this request can be interpreted as javascript statements // by browsers result := SPI_Ethernet_putConstString(@httpHeader) ; // HTTP header result := result + SPI_Ethernet_putConstString(@httpMimeTypeScript) ; // with text MIME type
// add AN2 value to reply WordToStr(ADC_Read(2), dyna) ; tmp := 'var AN2='; result := result + SPI_Ethernet_putString(@tmp) ; result := result + SPI_Ethernet_putString(@dyna) ; tmp := ';'; result := result + SPI_Ethernet_putString(@tmp) ;
// add AN3 value to reply WordToStr(ADC_Read(3), dyna) ; tmp := 'var AN3='; result := result + SPI_Ethernet_putString(@tmp) ; result := result + SPI_Ethernet_putString(@dyna) ; tmp := ';'; result := result + SPI_Ethernet_putString(@tmp) ;
// add PORTB value (buttons) to reply tmp := 'var PORTB= '; result := result + SPI_Ethernet_putString(@tmp) ; WordToStr(PORTB, dyna) ; result := result + SPI_Ethernet_putString(@dyna) ; tmp := ';'; result := result + SPI_Ethernet_putString(@tmp) ;
// add PORTD value (LEDs) to reply tmp := 'var PORTD= '; result := result + SPI_Ethernet_putString(@tmp) ; WordToStr(PORTD, dyna) ; result := result + SPI_Ethernet_putString(@dyna) ; tmp := ';'; result := result + SPI_Ethernet_putString(@tmp) ;
// add HTTP requests counter to reply WordToStr(httpCounter, dyna) ; tmp := 'var REQ= '; result := result + SPI_Ethernet_putString(@tmp) ; result := result + SPI_Ethernet_putString(@dyna) ; tmp := ';'; result := result + SPI_Ethernet_putString(@tmp) ; end else if(getRequest[5] = 't') then // if request path name starts with t, toggle PORTD (LED) bit number that comes after begin bitMask := 0; if(isdigit(getRequest[6]) <> 0) then // if 0 <= bit number <= 9, bits 8 & 9 does not exist but does not matter begin bitMask := getRequest[6] - '0' ; // convert ASCII to integer bitMask := 1 shl bitMask ; // create bit mask PORTD := PORTD xor bitMask ; // toggle PORTD with xor operator end; end;
if(result = 0) then // what do to by default begin result := SPI_Ethernet_putConstString(@httpHeader) ; // HTTP header result := result + SPI_Ethernet_putConstString(@httpMimeTypeHTML) ; // with HTML MIME type result := result + SPI_Ethernet_putConstString(@indexPage) ; // HTML page first part result := result + SPI_Ethernet_putConstString(@indexPage2) ; // HTML page second part end; // return to the library with the number of bytes to transmit end;
{* * this function is called by the library * the user accesses to the UDP request by successive calls to SPI_Ethernet_getByte() * the user puts data in the transmit buffer by successive calls to SPI_Ethernet_putByte() * the function must return the length in bytes of the UDP reply, or 0 if nothing to transmit * * if you don't need to reply to UDP requests, * just define this function with a return(0) as single statement * *} function SPI_Ethernet_UserUDP(var remoteHost : array[4] of byte; remotePort, destPort, reqLength : word) : word; begin result := 0; // reply is made of the remote host IP address in human readable format byteToStr(remoteHost[0], dyna) ; // first IP address byte dyna[3] := '.' ; byteToStr(remoteHost[1], tmp) ; // second dyna[4] := tmp[0]; dyna[5] := tmp[1]; dyna[6] := tmp[2]; dyna[7] := '.' ; byteToStr(remoteHost[2], tmp) ; // second dyna[8] := tmp[0]; dyna[9] := tmp[1]; dyna[10] := tmp[2]; dyna[11] := '.' ; byteToStr(remoteHost[3], tmp) ; // second dyna[12] := tmp[0]; dyna[13] := tmp[1]; dyna[14] := tmp[2];
dyna[15] := ':' ; // add separator
// then remote host port number WordToStr(remotePort, tmp) ; dyna[16] := tmp[0]; dyna[17] := tmp[1]; dyna[18] := tmp[2]; dyna[19] := tmp[3]; dyna[20] := tmp[4]; dyna[21] := '[' ; WordToStr(destPort, tmp) ; dyna[22] := tmp[0]; dyna[23] := tmp[1]; dyna[24] := tmp[2]; dyna[25] := tmp[3]; dyna[26] := tmp[4]; dyna[27] := ']' ; dyna[28] := 0 ;
// the total length of the request is the length of the dynamic string plus the text of the request result := 28 + reqLength ;
// puts the dynamic string into the transmit buffer SPI_Ethernet_putBytes(@dyna, 28) ;
// then puts the request string converted into upper char into the transmit buffer while(reqLength <> 0) do begin SPI_Ethernet_putByte(SPI_Ethernet_getByte()) ; reqLength := reqLength - 1; end; // back to the library with the length of the UDP reply end;
begin ANSEL := 0x0C ; // AN2 and AN3 convertors will be used PORTA := 0 ; TRISA := 0xff ; // set PORTA as input for ADC
ANSELH := 0; // Configure other AN pins as digital I/O PORTB := 0 ; TRISB := 0xff ; // set PORTB as input for buttons
PORTD := 0 ; TRISD := 0 ; // set PORTD as output
httpCounter := 0;
// set mac address myMacAddr[0] := 0x00; myMacAddr[1] := 0x14; myMacAddr[2] := 0xA5; myMacAddr[3] := 0x76; myMacAddr[4] := 0x19; myMacAddr[5] := 0x3F;
// set IP address myIpAddr[0] := 192; myIpAddr[1] := 168; myIpAddr[2] := 20; myIpAddr[3] := 60;
{* * starts ENC28J60 with : * reset bit on PORTC.B0 * CS bit on PORTC.B1 * my MAC & IP address * full duplex *}
SPI1_Init(); // init spi module SPI_Ethernet_Init(myMacAddr, myIpAddr, _SPI_Ethernet_FULLDUPLEX) ; // init ethernet module SPI_Ethernet_setUserHandlers(@SPI_Ethernet_UserTCP, @SPI_Ethernet_UserUDP); // set user handlers
while true do // do forever begin SPI_Ethernet_doPacket() ; // process incoming Ethernet packets
{* * add your stuff here if needed * SPI_Ethernet_doPacket() must be called as often as possible * otherwise packets could be lost *} end; end. |