Connect Ethernet controller with PIC Code

The ENC28J60 is a stand-alone Ethernet controller with an industry standard Serial Peripheral Interface (SPI). It is designed to serve as an Ethernet network interface for any controller equipped with SPI.
The ENC28J60 meets all of the IEEE 802.3 specifications. It incorporates a number of packet filtering schemes to limit incoming packets. It also provides an internal DMA module for fast data throughput and hardware assisted IP checksum calculations. Communication with the host controller is implemented via two interrupt pins and the SPI, with data rates of up to 10 Mb/s. Two dedicated pins are used for LED link and network activity indication.
This library is designed to simplify handling of the underlying hardware (ENC28J60). It works with any PIC with integrated SPI and more than 4 Kb ROM memory. 38 to 40 MHz clock is recommended to get from 8 to 10 Mhz SPI clock, otherwise PIC should be clocked by ENC28J60 clock output due to its silicon bug in SPI hardware. If you try lower PIC clock speed, there might be board hang or miss some requests.
SPI Ethernet ENC28J60 Library supports:

  • IPv4 protocol.
  • ARP requests.
  • ICMP echo requests.
  • UDP requests.
  • TCP requests (no stack, no packet reconstruction).
  • ARP client with cache.
  • DNS client.
  • UDP client.
  • DHCP client.
  • packet fragmentation is NOT supported.

  Important :

  • Due to PIC16 RAM/Flash limitations PIC16 library does NOT have ARP, DNS, UDP and DHCP client support implemented.
  • Global library variable SPI_Ethernet_userTimerSec is used to keep track of time for all client implementations (ARP, DNS, UDP and DHCP). It is user responsibility to increment this variable each second in it’s code if any of the clients is used.
  • For advanced users there are header files ("__EthEnc28j60.h" and "__EthEnc28j60Private.h") in Uses\P16 and Uses\P18 folders of the compiler with description of all routines and global variables, relevant to the user, implemented in the SPI Ethernet ENC28J60 Library.
  • The appropriate hardware SPI module must be initialized before using any of the SPI Ethernet ENC28J60 library routines.
  • For MCUs with two SPI modules it is possible to initialize both of them and then switch by using the SPI_Set_Active() routine.Connect Ethernet controller with PIC Code

Library Dependency Tree

External dependencies of SPI Ethernet ENC28J60 Library

The following variables must be defined in all projects using SPI Ethernet ENC28J60 Library: Description: Examples :
extern sfr sbit SPI_Ethernet_CS; ENC28J60 chip select pin. sbit SPI_Ethernet_CS at RC1_bit;
extern sfr sbit SPI_Ethernet_RST; ENC28J60 reset pin. sbit SPI_Ethernet_Rst at RC0_bit;
extern sfr sbit SPI_Ethernet_CS_Direction; Direction of the ENC28J60 chip select pin. sbit SPI_Ethernet_CS_Direction at TRISC1_bit;
extern sfr sbit SPI_Ethernet_RST_Direction; Direction of the ENC28J60 reset pin. sbit SPI_Ethernet_Rst_Direction at TRISC0_bit;
The following routines must be defined in all project using SPI Ethernet ENC28J60 Library: Description: Examples :
unsigned int SPI_Ethernet_UserTCP(unsigned char *remoteHost, unsigned int remotePort, unsigned int localPort, unsigned int reqLength, TEthPktFlags *flags); TCP request handler. Refer to the library example at the bottom of this page for code implementation.
unsigned int SPI_Ethernet_UserUDP(unsigned char *remoteHost, unsigned int remotePort, unsigned int localPort, unsigned int reqLength, TEthPktFlags *flags); UDP request handler. Refer to the library example at the bottom of this page for code implementation.

Library Routines

PIC16 and PIC18:

  • SPI_Ethernet_Init
  • SPI_Ethernet_Enable
  • SPI_Ethernet_Disable
  • SPI_Ethernet_doPacket
  • SPI_Ethernet_putByte
  • SPI_Ethernet_putBytes
  • SPI_Ethernet_putString
  • SPI_Ethernet_putConstString
  • SPI_Ethernet_putConstBytes
  • SPI_Ethernet_getByte
  • SPI_Ethernet_getBytes
  • SPI_Ethernet_UserTCP
  • SPI_Ethernet_UserUDP

PIC18 Only:

  • SPI_Ethernet_getIpAddress
  • SPI_Ethernet_getGwIpAddress
  • SPI_Ethernet_getDnsIpAddress
  • SPI_Ethernet_getIpMask
  • SPI_Ethernet_confNetwork
  • SPI_Ethernet_arpResolve
  • SPI_Ethernet_sendUDP
  • SPI_Ethernet_dnsResolve
  • SPI_Ethernet_initDHCP
  • SPI_Ethernet_doDHCPLeaseTime
  • SPI_Ethernet_renewDHCP

SPI_Ethernet_Init

Prototype void SPI_Ethernet_Init(unsigned char *mac, unsigned char *ip, unsigned char fullDuplex);
Returns Nothing.
Description This is MAC module routine. It initializes ENC28J60 controller. This function is internaly splited into 2 parts to help linker when coming short of memory.
ENC28J60 controller settings (parameters not mentioned here are set to default):
  • receive buffer start address : 0x0000.
  • receive buffer end address : 0x19AD.
  • transmit buffer start address: 0x19AE.
  • transmit buffer end address : 0x1FFF.
  • RAM buffer read/write pointers in auto-increment mode.
  • receive filters set to default: CRC + MAC Unicast + MAC Broadcast in OR mode.
  • flow control with TX and RX pause frames in full duplex mode.
  • frames are padded to 60 bytes + CRC.
  • maximum packet size is set to 1518.
  • Back-to-Back Inter-Packet Gap: 0x15 in full duplex mode; 0x12 in half duplex mode.
  • Non-Back-to-Back Inter-Packet Gap: 0x0012 in full duplex mode; 0x0C12 in half duplex mode.
  • Collision window is set to 63 in half duplex mode to accomodate some ENC28J60 revisions silicon bugs.
  • CLKOUT output is disabled to reduce EMI generation.
  • half duplex loopback disabled.
  • LED configuration: default (LEDA-link status, LEDB-link activity).

Parameters:

  • mac: RAM buffer containing valid MAC address.
  • ip: RAM buffer containing valid IP address.
  • fullDuplex: ethernet duplex mode switch. Valid values: 0 (half duplex mode) and 1 (full duplex mode).
Requires Global variables :
  • SPI_Ethernet_CS: Chip Select line
  • SPI_Ethernet_CS_Direction: Direction of the Chip Select pin
  • SPI_Ethernet_RST: Reset line
  • SPI_Ethernet_RST_Direction: Direction of the Reset pin

must be defined before using this function.
The SPI module needs to be initialized. See the SPIx_Init and SPIx_Init_Advanced routines.

Example
#define SPI_Ethernet_HALFDUPLEX     0
#define SPI_Ethernet_FULLDUPLEX     1

// mE ethernet NIC pinout
sfr sbit SPI_Ethernet_Rst at RC0_bit;
sfr sbit SPI_Ethernet_CS  at RC1_bit;
sfr sbit SPI_Ethernet_Rst_Direction at TRISC0_bit;
sfr sbit SPI_Ethernet_CS_Direction  at TRISC1_bit;
// end ethernet NIC definitions

unsigned char myMacAddr[6] = {0x00, 0x14, 0xA5, 0x76, 0x19, 0x3f}; // my MAC address 
unsigned char myIpAddr     = {192, 168,   1, 60 };  // my IP addr

SPI1_Init(); 
SPI_Ethernet_Init(myMacAddr, myIpAddr, SPI_Ethernet_FULLDUPLEX);

SPI_Ethernet_Enable

Prototype void SPI_Ethernet_Enable(unsigned char enFlt);
Returns Nothing.
Description This is MAC module routine. This routine enables appropriate network traffic on the ENC28J60 module by the means of it’s receive filters (unicast, multicast, broadcast, crc). Specific type of network traffic will be enabled if a corresponding bit of this routine’s input parameter is set. Therefore, more than one type of network traffic can be enabled at the same time. For this purpose, predefined library constants (see the table below) can be ORed to form appropriate input value.
Parameters:
  • enFlt: network traffic/receive filter flags. Each bit corresponds to the appropriate network traffic/receive filter:
    Bit Mask Description Predefined library const
    0 0x01 MAC Broadcast traffic/receive filter flag. When set, MAC broadcast traffic will be enabled. _SPI_Ethernet_BROADCAST
    1 0x02 MAC Multicast traffic/receive filter flag. When set, MAC multicast traffic will be enabled. _SPI_Ethernet_MULTICAST
    2 0x04 not used none
    3 0x08 not used none
    4 0x10 not used none
    5 0x20 CRC check flag. When set, packets with invalid CRC field will be discarded. _SPI_Ethernet_CRC
    6 0x40 not used none
    7 0x80 MAC Unicast traffic/receive filter flag. When set, MAC unicast traffic will be enabled. _SPI_Ethernet_UNICAST

      Note :

    • Advanced filtering available in the ENC28J60 module such as Pattern Match, Magic Packet and Hash Table can not be enabled by this routine. Additionaly, all filters, except CRC, enabled with this routine will work in OR mode, which means that packet will be received if any of the enabled filters accepts it.
    • This routine will change receive filter configuration on-the-fly. It will not, in any way, mess with enabling/disabling receive/transmit logic or any other part of the ENC28J60 module. The ENC28J60 module should be properly cofigured by the means of SPI_Ethernet_Init routine.
Requires Ethernet module has to be initialized. See SPI_Ethernet_Init.
Example
SPI_Ethernet_Enable(_SPI_Ethernet_CRC | _SPI_Ethernet_UNICAST); // enable CRC checking and Unicast traffic

SPI_Ethernet_Disable

Prototype void SPI_Ethernet_Disable(unsigned char disFlt);
Returns Nothing.
Description This is MAC module routine. This routine disables appropriate network traffic on the ENC28J60 module by the means of it’s receive filters (unicast, multicast, broadcast, crc). Specific type of network traffic will be disabled if a corresponding bit of this routine’s input parameter is set. Therefore, more than one type of network traffic can be disabled at the same time. For this purpose, predefined library constants (see the table below) can be ORed to form appropriate input value.
Parameters:
  • disFlt: network traffic/receive filter flags. Each bit corresponds to the appropriate network traffic/receive filter:
    Bit Mask Description Predefined library const
    0 0x01 MAC Broadcast traffic/receive filter flag. When set, MAC broadcast traffic will be disabled. _SPI_Ethernet_BROADCAST
    1 0x02 MAC Multicast traffic/receive filter flag. When set, MAC multicast traffic will be disabled. _SPI_Ethernet_MULTICAST
    2 0x04 not used none
    3 0x08 not used none
    4 0x10 not used none
    5 0x20 CRC check flag. When set, CRC check will be disabled and packets with invalid CRC field will be accepted. _SPI_Ethernet_CRC
    6 0x40 not used none
    7 0x80 MAC Unicast traffic/receive filter flag. When set, MAC unicast traffic will be disabled. _SPI_Ethernet_UNICAST

      Note :

    • Advanced filtering available in the ENC28J60 module such as Pattern Match, Magic Packet and Hash Table can not be disabled by this routine.
    • This routine will change receive filter configuration on-the-fly. It will not, in any way, mess with enabling/disabling receive/transmit logic or any other part of the ENC28J60 module.
    • The ENC28J60 module should be properly cofigured by the means of SPI_Ethernet_Init routine.
Requires Ethernet module has to be initialized.
Example
SPI_Ethernet_Disable(_SPI_Ethernet_CRC | _SPI_Ethernet_UNICAST); // disable CRC checking and Unicast traffic

SPI_Ethernet_doPacket

Prototype unsigned char SPI_Ethernet_doPacket();
Returns
  • 0 – upon successful packet processing (zero packets received or received packet processed successfully).
  • 1 – upon reception error or receive buffer corruption. ENC28J60 controller needs to be restarted.
  • 2 – received packet was not sent to us (not our IP, nor IP broadcast address).
  • 3 – received IP packet was not IPv4.
  • 4 – received packet was of type unknown to the library.
Description This is MAC module routine. It processes next received packet if such exists. Packets are processed in the following manner:
  • ARP & ICMP requests are replied automatically.
  • upon TCP request the SPI_Ethernet_UserTCP function is called for further processing.
  • upon UDP request the SPI_Ethernet_UserUDP function is called for further processing.
  Note : SPI_Ethernet_doPacket must be called as often as possible in user’s code.
Requires Ethernet module has to be initialized.
Example
if (SPI_Ethernet_doPacket() == 0)(1) {  // process received packets
  ...
}

SPI_Ethernet_putByte

Prototype void SPI_Ethernet_putByte(unsigned char v);
Returns Nothing.
Description This is MAC module routine. It stores one byte to address pointed by the current ENC28J60 write pointer (EWRPT).
Parameters:
  • v: value to store
Requires Ethernet module has to be initialized.
Example
char data_;
...
SPI_Ethernet_putByte(data); // put an byte into ENC28J60 buffer

SPI_Ethernet_putBytes

Prototype void SPI_Ethernet_putBytes(unsigned char *ptr, unsigned int n);
Returns Nothing.
Description This is MAC module routine. It stores requested number of bytes into ENC28J60 RAM starting from current ENC28J60 write pointer (EWRPT) location.
Parameters:
  • ptr: RAM buffer containing bytes to be written into ENC28J60 RAM.
  • n: number of bytes to be written.
Requires Ethernet module has to be initialized.
Example
char *buffer =  "mikroElektronika";  
...
SPI_Ethernet_putBytes(buffer, 16); // put an RAM array into ENC28J60 buffer

SPI_Ethernet_putConstBytes

Prototype void SPI_Ethernet_putConstBytes(const unsigned char *ptr, unsigned int n);
Returns Nothing.
Description This is MAC module routine. It stores requested number of const bytes into ENC28J60 RAM starting from current ENC28J60 write pointer (EWRPT) location.
Parameters:
  • ptr: const buffer containing bytes to be written into ENC28J60 RAM.
  • n: number of bytes to be written.
Requires Ethernet module has to be initialized.
Example
const char *buffer =  "mikroElektronika";  
...
SPI_Ethernet_putConstBytes(buffer, 16); // put a const array into ENC28J60 buffer

SPI_Ethernet_putString

Prototype unsigned int SPI_Ethernet_putString(unsigned char *ptr);
Returns Number of bytes written into ENC28J60 RAM.
Description This is MAC module routine. It stores whole string (excluding null termination) into ENC28J60 RAM starting from current ENC28J60 write pointer (EWRPT) location.
Parameters:
  • ptr: string to be written into ENC28J60 RAM.
Requires Ethernet module has to be initialized.
Example
char *buffer =  "mikroElektronika";  
...
SPI_Ethernet_putString(buffer); // put a RAM string into ENC28J60 buffer

SPI_Ethernet_putConstString

Prototype unsigned int SPI_Ethernet_putConstString(const unsigned char *ptr);
Returns Number of bytes written into ENC28J60 RAM.
Description This is MAC module routine. It stores whole const string (excluding null termination) into ENC28J60 RAM starting from current ENC28J60 write pointer (EWRPT) location.
Parameters:
  • ptr: const string to be written into ENC28J60 RAM.
Requires Ethernet module has to be initialized.
Example
const char *buffer =  "mikroElektronika"; 
...
SPI_Ethernet_putConstString(buffer); // put a const string into ENC28J60 buffer

SPI_Ethernet_getByte

Prototype unsigned char SPI_Ethernet_getByte();
Returns Byte read from ENC28J60 RAM.
Description This is MAC module routine. It fetches a byte from address pointed to by current ENC28J60 read pointer (ERDPT).
Requires Ethernet module has to be initialized.
Example
char buffer; 
...
buffer = SPI_Ethernet_getByte(); // read a byte from ENC28J60 buffer

SPI_Ethernet_getBytes

Prototype void SPI_Ethernet_getBytes(unsigned char *ptr, unsigned int addr, unsigned int n);
Returns Nothing.
Description This is MAC module routine. It fetches equested number of bytes from ENC28J60 RAM starting from given address. If value of 0xFFFF is passed as the address parameter, the reading will start from current ENC28J60 read pointer (ERDPT) location.
Parameters:
  • ptr: buffer for storing bytes read from ENC28J60 RAM.
  • addr: ENC28J60 RAM start address. Valid values: 0..8192.
  • n: number of bytes to be read.
Requires Ethernet module has to be initialized.
Example
char buffer[16];  
...
SPI_Ethernet_getBytes(buffer, 0x100, 16); // read 16 bytes, starting from address 0x100

SPI_Ethernet_UserTCP

Prototype unsigned int SPI_Ethernet_UserTCP(unsigned char *remoteHost, unsigned int remotePort, unsigned int localPort, unsigned int reqLength, TEthPktFlags *flags);
Returns
  • 0 – there should not be a reply to the request.
  • Length of TCP/HTTP reply data field – otherwise.
Description This is TCP module routine. It is internally called by the library. The user accesses to the TCP/HTTP request by using some of the SPI_Ethernet_get routines. The user puts data in the transmit buffer by using some of the SPI_Ethernet_put routines. The function must return the length in bytes of the TCP/HTTP reply, or 0 if there is nothing to transmit. If there is no need to reply to the TCP/HTTP requests, just define this function with return(0) as a single statement.
Parameters:
  • remoteHost: client’s IP address.
  • remotePort: client’s TCP port.
  • localPort: port to which the request is sent.
  • reqLength: TCP/HTTP request data field length.
  • flags: structure consisted of two bit fields :
    typedef struct {
      unsigned canCloseTCP: 1;  // flag which closes socket
      unsigned isBroadcast: 1;  // flag which denotes that the IP package has been received via subnet broadcast address (not used for PIC16 family)
    } TEthPktFlags;
    
  Note : The function source code is provided with appropriate example projects. The code should be adjusted by the user to achieve desired reply.
Requires Ethernet module has to be initialized.
Example This function is internally called by the library and should not be called by the user’s code.

SPI_Ethernet_UserUDP

Prototype unsigned int SPI_Ethernet_UserUDP(unsigned char *remoteHost, unsigned int remotePort, unsigned int destPort, unsigned int reqLength, TEthPktFlags *flags);
Returns
  • 0 – there should not be a reply to the request.
  • Length of UDP reply data field – otherwise.
Description This is UDP module routine. It is internally called by the library. The user accesses to the UDP request by using some of the SPI_Ethernet_get routines. The user puts data in the transmit buffer by using some of the SPI_Ethernet_put routines. 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 the UDP requests, just define this function with a return(0) as single statement.
Parameters:
  • remoteHost: client’s IP address.
  • remotePort: client’s port.
  • destPort: port to which the request is sent.
  • flags: structure consisted of two bit fields :
    typedef struct {
      unsigned canCloseTCP: 1;  // flag which closes TCP socket (not relevant to UDP)
      unsigned isBroadcast: 1;  // flag which denotes that the IP package has been received via subnet broadcast address (not used for PIC16 family)
    } TEthPktFlags;
    
      Note : The function source code is provided with appropriate example projects. The code should be adjusted by the user to achieve desired reply.
Requires Ethernet module has to be initialized.
Example This function is internally called by the library and should not be called by the user’s code.

SPI_Ethernet_getIpAddress

Prototype unsigned char * SPI_Ethernet_getIpAddress();
Returns Pointer to the global variable holding IP address.
Description This routine should be used when DHCP server is present on the network to fetch assigned IP address.
  Note : User should always copy the IP address from the RAM location returned by this routine into it’s own IP address buffer. These locations should not be altered by the user in any case!
Requires Ethernet module has to be initialized.
Available for PIC18 family MCUs only.
Example
unsigned char ipAddr[4];  // user IP address buffer
... 
memcpy(ipAddr, SPI_Ethernet_getIpAddress(), 4); // fetch IP address

SPI_Ethernet_getGwIpAddress

Prototype unsigned char * SPI_Ethernet_getGwIpAddress();
Returns Pointer to the global variable holding gateway IP address.
Description This routine should be used when DHCP server is present on the network to fetch assigned gateway IP address.
  Note : User should always copy the IP address from the RAM location returned by this routine into it’s own gateway IP address buffer. These locations should not be altered by the user in any case!
Requires Ethernet module has to be initialized.
Available for PIC18 family MCUs only.
Example
unsigned char gwIpAddr[4];  // user gateway IP address buffer
... 
memcpy(gwIpAddr, SPI_Ethernet_getGwIpAddress(), 4); // fetch gateway IP address 

SPI_Ethernet_getDnsIpAddress

Prototype unsigned char * SPI_Ethernet_getDnsIpAddress()
Returns Pointer to the global variable holding DNS IP address.
Description his routine should be used when DHCP server is present on the network to fetch assigned DNS IP address.
  Note : User should always copy the IP address from the RAM location returned by this routine into it’s own DNS IP address buffer. These locations should not be altered by the user in any case!
Requires Ethernet module has to be initialized.
Available for PIC18 family MCUs only.
Example
unsigned char dnsIpAddr[4];  // user DNS IP address buffer
... 
memcpy(dnsIpAddr, SPI_Ethernet_getDnsIpAddress(), 4); // fetch DNS server address 

Connect Ethernet controller with PIC Code schematich

SPI_Ethernet_getIpMask

Prototype unsigned char * SPI_Ethernet_getIpMask()
Returns Pointer to the global variable holding IP subnet mask.
Description This routine should be used when DHCP server is present on the network to fetch assigned IP subnet mask.
  Note : User should always copy the IP address from the RAM location returned by this routine into it’s own IP subnet mask buffer. These locations should not be altered by the user in any case!
Requires Ethernet module has to be initialized.
Available for PIC18 family MCUs only.
Example
unsigned char IpMask[4];  // user IP subnet mask buffer
... 
memcpy(IpMask, SPI_Ethernet_getIpMask(), 4); // fetch IP subnet mask

SPI_Ethernet_confNetwork

Prototype void SPI_Ethernet_confNetwork(char *ipMask, char *gwIpAddr, char *dnsIpAddr);
Returns Nothing.
Description Configures network parameters (IP subnet mask, gateway IP address, DNS IP address) when DHCP is not used.
Parameters:
  • ipMask: IP subnet mask.
  • gwIpAddr gateway IP address.
  • dnsIpAddr: DNS IP address.
Note : The above mentioned network parameters should be set by this routine only if DHCP module is not used. Otherwise DHCP will override these settings.
Requires Ethernet module has to be initialized.
Available for PIC18 family MCUs only.
Example
char ipMask[4]    = {255, 255, 255,  0 };  // network mask (for example : 255.255.255.0)
char gwIpAddr[4]  = {192, 168,   1,  1 };  // gateway (router) IP address
char dnsIpAddr[4] = {192, 168,   1,  1 };  // DNS server IP address
... 
SPI_Ethernet_confNetwork(ipMask, gwIpAddr, dnsIpAddr); // set network configuration parameters

SPI_Ethernet_arpResolve

Prototype unsigned char *SPI_Ethernet_arpResolve(unsigned char *ip, unsigned char tmax);
Returns
  • MAC address behind the IP address – the requested IP address was resolved.
  • 0 – otherwise.
Description This is ARP module routine. It sends an ARP request for given IP address and waits for ARP reply. If the requested IP address was resolved, an ARP cash entry is used for storing the configuration. ARP cash can store up to 3 entries. For ARP cash structure refer to "eth_enc28j60LibDef.h" header file in the compiler’s Uses/P18 folder.
Parameters:
  • ip: IP address to be resolved.
  • tmax: time in seconds to wait for an reply.
  Note : The Ethernet services are not stopped while this routine waits for ARP reply. The incoming packets will be processed normaly during this time.
Requires Ethernet module has to be initialized.
Available for PIC18 family MCUs only.
Example
unsigned char IpAddr[4]  = {192, 168,   1,  1 };  // IP address
... 
SPI_Ethernet_arpResolve(IpAddr, 5); // get MAC address behind the above IP address, wait 5 secs for the response

SPI_Ethernet_sendUDP

Prototype unsigned char SPI_Ethernet_sendUDP(unsigned char *destIP, unsigned int sourcePort, unsigned int destPort, unsigned char *pkt, unsigned int pktLen);
Returns
  • 1 – UDP packet was sent successfully.
  • 0 – otherwise.
Description This is UDP module routine. It sends an UDP packet on the network.
Parameters:
  • destIP: remote host IP address.
  • sourcePort: local UDP source port number.
  • destPort: destination UDP port number.
  • pkt: packet to transmit.
  • pktLen: length in bytes of packet to transmit.
Requires Ethernet module has to be initialized.
Available for PIC18 family MCUs only.
Example
unsigned char IpAddr[4]  = {192, 168,   1,  1 };  // remote IP address
... 
SPI_Ethernet_sendUDP(IpAddr, 10001, 10001, "Hello", 5); // send Hello message to the above IP address, from UDP port 10001 to UDP port 10001 

SPI_Ethernet_dnsResolve

Prototype unsigned char * SPI_Ethernet_dnsResolve(unsigned char *host, unsigned char tmax);
Returns
  • pointer to the location holding the IP address – the requested host name was resolved.
  • 0 – otherwise.
Description This is DNS module routine. It sends an DNS request for given host name and waits for DNS reply. If the requested host name was resolved, it’s IP address is stored in library global variable and a pointer containing this address is returned by the routine. UDP port 53 is used as DNS port.
Parameters:
  • host: host name to be resolved.
  • tmax: time in seconds to wait for an reply.
  Note :
  • The Ethernet services are not stopped while this routine waits for DNS reply. The incoming packets will be processed normaly during this time.
  • User should always copy the IP address from the RAM location returned by this routine into it’s own resolved host IP address buffer. These locations should not be altered by the user in any case!
Requires Ethernet module has to be initialized.
Available for PIC18 family MCUs only.
Example
unsigned char * remoteHostIpAddr[4]; // user host IP address buffer
...
// SNTP server:
// Zurich, Switzerland: Integrated Systems Lab, Swiss Fed. Inst. of Technology
// 129.132.2.21: swisstime.ethz.ch
// Service Area: Switzerland and Europe 
memcpy(remoteHostIpAddr, SPI_Ethernet_dnsResolve("swisstime.ethz.ch", 5), 4);

SPI_Ethernet_initDHCP

Prototype unsigned char SPI_Ethernet_initDHCP(unsigned char tmax);
Returns
  • 1 – network parameters were obtained successfully.
  • 0 – otherwise.
Description This is DHCP module routine. It sends an DHCP request for network parameters (IP, gateway, DNS addresses and IP subnet mask) and waits for DHCP reply. If the requested parameters were obtained successfully, their values are stored into the library global variables.
These parameters can be fetched by using appropriate library IP get routines:
  • SPI_Ethernet_getIpAddress – fetch IP address.
  • SPI_Ethernet_getGwIpAddress – fetch gateway IP address.
  • SPI_Ethernet_getDnsIpAddress – fetch DNS IP address.
  • SPI_Ethernet_getIpMask – fetch IP subnet mask.

UDP port 68 is used as DHCP client port and UDP port 67 is used as DHCP server port.
Parameters:

  • tmax: time in seconds to wait for an reply.
  Note :
  • The Ethernet services are not stopped while this routine waits for DNS reply. The incoming packets will be processed normaly during this time.
  • When DHCP module is used, global library variable SPI_Ethernet_userTimerSec is used to keep track of time. It is user responsibility to increment this variable each second in it’s code.
Requires Ethernet module has to be initialized.
Available for PIC18 family MCUs only.
Example
... 
SPI_Ethernet_initDHCP(5); // get network configuration from DHCP server, wait 5 sec for the response 
...

SPI_Ethernet_doDHCPLeaseTime

Prototype unsigned char SPI_Ethernet_doDHCPLeaseTime();
Returns
  • 0 – lease time has not expired yet.
  • 1 – lease time has expired, it’s time to renew it.
Description This is DHCP module routine. It takes care of IP address lease time by decrementing the global lease time library counter. When this time expires, it’s time to contact DHCP server and renew the lease.
Requires Ethernet module has to be initialized.
Available for PIC18 family MCUs only.
Example
while(1) { 
  ...
  if(SPI_Ethernet_doDHCPLeaseTime())
    ... // it's time to renew the IP address lease                  
}

SPI_Ethernet_renewDHCP

Prototype unsigned char SPI_Ethernet_renewDHCP(unsigned char tmax);
Returns
  • 1 – upon success (lease time was renewed).
  • 0 – otherwise (renewal request timed out).
Description This is DHCP module routine. It sends IP address lease time renewal request to DHCP server.
Parameters:
  • tmax: time in seconds to wait for an reply.
Requires Ethernet module has to be initialized.
Available for PIC18 family MCUs only.
Example
while(1) { 
  ...
  if(SPI_Ethernet_doDHCPLeaseTime())
    SPI_Ethernet_renewDHCP(5); // it's time to renew the IP address lease, with 5 secs for a reply                  
  ...  
}

Library Example

This code shows how to use the 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.
#include  "__EthEnc28j60.h"

// duplex config flags
#define Spi_Ethernet_HALFDUPLEX     0x00  // half duplex
#define Spi_Ethernet_FULLDUPLEX     0x01  // full duplex
// mE ehternet NIC pinout
sfr sbit SPI_Ethernet_Rst at RC0_bit;
sfr sbit SPI_Ethernet_CS  at RC1_bit;
sfr sbit SPI_Ethernet_Rst_Direction at TRISC0_bit;
sfr sbit SPI_Ethernet_CS_Direction  at TRISC1_bit;
// end ethernet NIC definitions

/************************************************************
 * ROM constant strings
 */
const unsigned char httpHeader[] = "HTTP/1.1 200 OKnContent-type: " ;  // HTTP header
const unsigned char httpMimeTypeHTML[] = "text/htmlnn" ;              // HTML MIME type
const unsigned char httpMimeTypeScript[] = "text/plainnn" ;           // TEXT MIME type
unsigned char httpMethod[] = "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 char    *indexPage =                   // Change the IP address of the page to be refreshed
"<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 char    *indexPage2 =  "</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>
" ;

/***********************************
 * RAM variables
 */
unsigned char   myMacAddr[6] = {0x00, 0x14, 0xA5, 0x76, 0x19, 0x3f} ;   // my MAC address
unsigned char   myIpAddr[4]  = {192, 168, 20, 60} ;                     // my IP address
unsigned char   getRequest[15] ;                                        // HTTP request buffer
unsigned char   dyna[30] ;                                              // buffer for dynamic response
unsigned long   httpCounter = 0 ;                                       // counter of HTTP requests

/*******************************************
 * functions
 */

/*
 * put the constant string pointed to by s to the ENC transmit buffer.
 */
/*unsigned int    putConstString(const char *s)
        {
        unsigned int ctr = 0 ;

        while(*s)
                {
                Spi_Ethernet_putByte(*s++) ;
                ctr++ ;
                }
        return(ctr) ;
        }*/
/*
 * it will be much faster to use library Spi_Ethernet_putConstString routine
 * instead of putConstString routine above. However, the code will be a little
 * bit bigger. User should choose between size and speed and pick the implementation that
 * suites him best. If you choose to go with the putConstString definition above
 * the #define line below should be commented out.
 *
 */
#define putConstString  SPI_Ethernet_putConstString

/*
 * put the string pointed to by s to the ENC transmit buffer
 */
/*unsigned int    putString(char *s)
        {
        unsigned int ctr = 0 ;

        while(*s)
                {
                Spi_Ethernet_putByte(*s++) ;

                ctr++ ;
                }
        return(ctr) ;
        }*/
/*
 * it will be much faster to use library Spi_Ethernet_putString routine
 * instead of putString routine above. However, the code will be a little
 * bit bigger. User should choose between size and speed and pick the implementation that
 * suites him best. If you choose to go with the putString definition above
 * the #define line below should be commented out.
 *
 */
#define putString  SPI_Ethernet_putString

/*
 * 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
 *
 */
unsigned int  SPI_Ethernet_UserTCP(unsigned char *remoteHost, unsigned int remotePort, unsigned int localPort, unsigned int reqLength, TEthPktFlags *flags)
{
        unsigned int    len = 0 ;                   // my reply length
        unsigned int    i ;                         // general purpose integer

        // should we close tcp socket after response is sent?
        // library closes tcp socket by default if canClose flag is not reset here
        // flags->canClose = 0; // 0 - do not close socket
                          // otherwise - close socket

        if(localPort != 80)                         // I listen only to web request on port 80
                {
                return(0) ;
                }

        // get 10 first bytes only of the request, the rest does not matter here
        for(i = 0 ; i < 10 ; i++)
                {
                getRequest[i] = SPI_Ethernet_getByte() ;
                }
        getRequest[i] = 0 ;

        if(memcmp(getRequest, httpMethod, 5))       // only GET method is supported here
                {
                return(0) ;
                }

        httpCounter++ ;                             // one more request done

        if(getRequest[5] == 's')                    // if request path name starts with s, store dynamic data in transmit buffer
                {
                // the text string replied by this request can be interpreted as javascript statements
                // by browsers

                len = putConstString(httpHeader) ;              // HTTP header
                len += putConstString(httpMimeTypeScript) ;     // with text MIME type

                // add AN2 value to reply
                IntToStr(ADC_Read(2), dyna) ;
                len += putConstString("var AN2=") ;
                len += putString(dyna) ;
                len += putConstString(";") ;

                // add AN3 value to reply
                IntToStr(ADC_Read(3), dyna) ;
                len += putConstString("var AN3=") ;
                len += putString(dyna) ;
                len += putConstString(";") ;

                // add PORTB value (buttons) to reply
                len += putConstString("var PORTB=") ;
                IntToStr(PORTB, dyna) ;
                len += putString(dyna) ;
                len += putConstString(";") ;

                // add PORTD value (LEDs) to reply
                len += putConstString("var PORTD=") ;
                IntToStr(PORTD, dyna) ;
                len += putString(dyna) ;
                len += putConstString(";") ;

                // add HTTP requests counter to reply
                IntToStr(httpCounter, dyna) ;
                len += putConstString("var REQ=") ;
                len += putString(dyna) ;
                len += putConstString(";") ;
                }
        else if(getRequest[5] == 't')                           // if request path name starts with t, toggle PORTD (LED) bit number that comes after
                {
                unsigned char   bitMask = 0 ;                   // for bit mask

                if(isdigit(getRequest[6]))                      // if 0 <= bit number <= 9, bits 8 & 9 does not exist but does not matter
                        {
                        bitMask = getRequest[6] - '0' ;         // convert ASCII to integer
                        bitMask = 1 << bitMask ;                // create bit mask
                        PORTD ^= bitMask ;                      // toggle PORTD with xor operator
                        }
                }

        if(len == 0)                                            // what do to by default
                {
                len =  putConstString(httpHeader) ;             // HTTP header
                len += putConstString(httpMimeTypeHTML) ;       // with HTML MIME type
                len += putConstString(indexPage) ;              // HTML page first part
                len += putConstString(indexPage2) ;             // HTML page second part
                }

        return(len) ;                                           // return to the library with the number of bytes to transmit
        }

/*
 * 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
 *
 */
unsigned int  SPI_Ethernet_UserUDP(unsigned char *remoteHost, unsigned int remotePort, unsigned int destPort, unsigned int reqLength, TEthPktFlags *flags)
        {
        unsigned int    len ;                           // my reply length

        // 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], dyna + 4) ;            // second
        dyna[7] = '.' ;
        ByteToStr(remoteHost[2], dyna + 8) ;            // third
        dyna[11] = '.' ;
        ByteToStr(remoteHost[3], dyna + 12) ;           // fourth

        dyna[15] = ':' ;                                // add separator

        // then remote host port number
        WordToStr(remotePort, dyna + 16) ;
        dyna[21] = '[' ;
        WordToStr(destPort, dyna + 22) ;
        dyna[27] = ']' ;
        dyna[28] = 0 ;

        // the total length of the request is the length of the dynamic string plus the text of the request
        len = 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--)
                {
                SPI_Ethernet_putByte(toupper(SPI_Ethernet_getByte())) ;
                }

        return(len) ;           // back to the library with the length of the UDP reply
        }

/*
 * main entry
 */
void    main()
        {
        ANSEL = 0x0C ;          // AN2 and AN3 convertors will be used
        C1ON_bit = 0;           // Disable comparators
        C2ON_bit = 0;
        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

        /*
         * starts ENC28J60 with :
         * reset bit on RC0
         * CS bit on RC1
         * my MAC & IP address
         * full duplex
         */
        SPI1_Init();
        SPI_Ethernet_Init(myMacAddr, myIpAddr, Spi_Ethernet_FULLDUPLEX) ;

        while(1)                            // do forever
                {
                /*
                 * if necessary, test the return value to get error code
                 */
                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
                 */
                }
        }

HW Connection

For more detail: Connect Ethernet controller with PIC Code


About The Author

Ibrar Ayyub

I am an experienced technical writer holding a Master's degree in computer science from BZU Multan, Pakistan University. With a background spanning various industries, particularly in home automation and engineering, I have honed my skills in crafting clear and concise content. Proficient in leveraging infographics and diagrams, I strive to simplify complex concepts for readers. My strength lies in thorough research and presenting information in a structured and logical format.

Follow Us:
LinkedinTwitter

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.