/* * file : ethdemo.c * project : ENC28J60 SPI ETHERNET LIBRARY DEMO * author : Bruno Gavand * compiler : mikroC V6.2 * date : october 27, 2006 * * description : * This program shows how to use undocumented mikroC Spi Ethernet functions * This example is an UDP push server : * * Start Ethernet services with ARP and PING reply * Listen to the UDP port 33000 for an incoming request : * 'b' will start the push * 'e' will stop the stop * any other incoming char has no effect * If push is enabled : * send to the remote UDP host a string with PORTB value one time per second * * target device : * Tested with PIC18F452 and PIC18F4620 @ 32 Mhz (8 Mhz crystal + HS PLL) * * Licence : * Feel free to use this source code at your own risks. * * history : october 30, 2006 : ARP cache management, little bug fixed. Thanks Philippe ! * * see more details on http://www.micro-examples.com/ */
#define NULL 0
#define Spi_Ethernet_FULLDUPLEX 1
#define TRANSMIT_START 0x19AE #define REPLY_START (TRANSMIT_START + 1) // reply buffer starts after per packet control byte
// some ENC28J60 registers, see datasheet #define ERDPTL 0x00 #define ERDPTH 0x01 #define ECON1 0x1f #define EDMACSL 0x16 #define EDMACSH 0x17
/*********************************** * RAM variables */ unsigned char macAddr[6] = {0x00, 0x14, 0xA5, 0x76, 0x19, 0x3f} ; // my MAC address
unsigned char ipAddr[4] = {192, 168, 1, 101} ; // my IP address
unsigned char destIpAddr[4] ; // IP address
unsigned char transmit = 0 ; unsigned char tmr = 0 ; unsigned long longTmr = 0 ; unsigned int destPortNum = 0 ; char udpStr[16] = "PORTB=" ;
#define ARPCACHELEN 3 struct { unsigned char valid ; unsigned long tmr ; unsigned char ip[4] ; unsigned char mac[6] ; } arpCache[ARPCACHELEN] ;
/******************************************* * functions */ /* * ARP resolution * this function returns the MAC address of an IP address * an ARP cache is used */ unsigned char *arpResolv(unsigned char *ip) { unsigned char i ; // general purpose register unsigned int rdptr ; // backup of ENC read pointer register unsigned char freeSlot ; // index of free slot in ARP cache unsigned long tmrSlot ; // oldest slot timer in ARP cache tmrSlot = 0xffffffff ; // will try to find less than this !
// step in ARP cache for(i = 0 ; i < ARPCACHELEN; i++) { // oldest slots must expire if(arpCache[i].tmr < longTmr - 1000) // too old ? { arpCache[i].tmr = 0 ; // clear timer arpCache[i].valid = 0 ; // disable slot }
// is it the address I want in a valid slot ? if(arpCache[i].valid && (memcmp(arpCache[i].ip, ip, 4) == 0)) { return(arpCache[i].mac) ; // yes, return ready to use MAC addr }
// find older slot, will be needed further if(arpCache[i].tmr < tmrSlot) { tmrSlot = arpCache[i].tmr ; // save best timer freeSlot = i ; // save best index } }
// select ENC register bank 0 Spi_Ethernet_clearBitReg(ECON1, 0b00000001) ; Spi_Ethernet_clearBitReg(ECON1, 0b00000010) ;
/*************************** * first, build eth header */ Spi_Ethernet_memcpy(REPLY_START, "\xff\xff\xff\xff\xff\xff", 6) ; // destination is broadcast Spi_Ethernet_memcpy(REPLY_START + 6, macAddr, 6) ; // source : my mac addr Spi_Ethernet_writeMemory(REPLY_START + 12, 8, 6, 1) ; // protocol is ARP
/**************************** * second, build ARP packet */
/* * 0, 1 : hardware type = ethernet * 8, 0 : protocole type = ip * 6, 4 : HW size = 6, protocol size = 4 * 0, 1 : ARP opcode = request */ Spi_Ethernet_memcpy(REPLY_START + 14, "\x0\x01\x08\x00\x06\x04\x00\x01", 8) ;
Spi_Ethernet_memcpy(REPLY_START + 22, macAddr, 6) ; // mac addr source Spi_Ethernet_memcpy(REPLY_START + 28, ipAddr, 4) ; // ip addr source Spi_Ethernet_memcpy(REPLY_START + 32, "\0\0\0\0\0\0", 6) ; // mac addr destination : broadcast addr Spi_Ethernet_memcpy(REPLY_START + 38, ip, 4) ; // who has this ip addr ?
/******************************************************************* * third, send packet over wires, ARP request packet is 42 bytes len */ Spi_Ethernet_txPacket(42) ;
tmr = 0 ; while(tmr < 45) // allows around 3 seconds for reply { Spi_Ethernet_doPacket() ; // do not stop eth services !
// select ENC register bank 0, may have been changed by library Spi_Ethernet_clearBitReg(ECON1, 0b00000001) ; Spi_Ethernet_clearBitReg(ECON1, 0b00000010) ;
/* * ERDPT[H|L] registers are used by library : * always save them before doing anything else ! */ rdptr = (Spi_Ethernet_readReg(ERDPTH) << 8) + Spi_Ethernet_readReg(ERDPTL) ;
/* * check if packet is ARP reply to my request (last byte = 2 means ARP reply) */ if((Spi_Ethernet_memcmp(REPLY_START + 12, "\x08\x06\x00\x01\x08\x00\x06\x04\x00\x02", 10) == 0) && (Spi_Ethernet_memcmp(REPLY_START, macAddr, 6) == 0) // is it my MAC addr ? && (Spi_Ethernet_memcmp(REPLY_START + 28, ip, 4) == 0) // is it my IP addr ? ) { /* * save remote MAC addr in a free slot of the cache */ // copy MAC addr arpCache[freeSlot].mac[0] = Spi_Ethernet_readMem(REPLY_START + 22) ; arpCache[freeSlot].mac[1] = Spi_Ethernet_readMem(REPLY_START + 23) ; arpCache[freeSlot].mac[2] = Spi_Ethernet_readMem(REPLY_START + 24) ; arpCache[freeSlot].mac[3] = Spi_Ethernet_readMem(REPLY_START + 25) ; arpCache[freeSlot].mac[4] = Spi_Ethernet_readMem(REPLY_START + 26) ; arpCache[freeSlot].mac[5] = Spi_Ethernet_readMem(REPLY_START + 27) ; // copy IP addr memcpy(arpCache[freeSlot].ip, ip, 4) ; // enable slot arpCache[freeSlot].valid = 1 ; // timestamp arpCache[freeSlot].tmr = longTmr ;
// restore ERDPT before exiting Spi_Ethernet_writeAddr(ERDPTL, rdptr) ; // return the MAC addr pointer return(arpCache[freeSlot].mac) ; }
// restore ERDPT before doing a new call to the library Spi_Ethernet_writeAddr(ERDPTL, rdptr) ; }
// failed, no response : MAC addr is null return((unsigned char *)NULL) ; }
/* * send an UDP packet : * destIp : remote host IP address * sourcePort : local UDP source port number * destPort : destination UDP port number * pkt : pointer to packet to transmit * pktLen : length in bytes of packet to transmit */ void sendUDP(unsigned char *destIP, unsigned int sourcePort, unsigned int destPort, unsigned char *pkt, unsigned int pktLen) { unsigned char *destMac ; // destination MAC addr static int idUniq ; // uniq packet id (serial number) unsigned char align ; // alignement flag unsigned int totlen ; // total packet length unsigned int rdptr ; // ENC read pointer backup
// is destination port valid ? if(destPort == 0) { return ; // no, do nothing }
// does destination IP address exist ? if((destMac = arpResolv(destIP)) == NULL) { return ; // no MAC addr matches, do nothing }
// select ENC register bank 0 Spi_Ethernet_clearBitReg(ECON1, 0b00000001) ; Spi_Ethernet_clearBitReg(ECON1, 0b00000010) ;
/* * ERDPT[H|L] registers are used by library : * always save them before doing anything else ! */ rdptr = (Spi_Ethernet_readReg(ERDPTH) << 8) + Spi_Ethernet_readReg(ERDPTL) ;
/************************** * first, build eth header */ Spi_Ethernet_memcpy(REPLY_START, destMAC, 6) ; // destination Spi_Ethernet_memcpy(REPLY_START + 6, macAddr, 6) ; // source Spi_Ethernet_writeMemory(REPLY_START + 12, 8, 0, 1) ; // IP type /************************** * second, build IP header */ totlen = 20 + 8 + pktLen ; Spi_Ethernet_writeMemory(REPLY_START + 14, 0x45, 0, 1) ; // IPV4, IHL = 20 bytes, TOS = 0 Spi_Ethernet_writeMemory(REPLY_START + 16, totlen >> 8, totlen, 1) ; // total packet length Spi_Ethernet_memcpy(REPLY_START + 18, (char *)&(idUniq++), 2) ; // identification Spi_Ethernet_writeMemory(REPLY_START + 20, 0, 0, 1) ; // flags + 0, fragment offset = 0, TTL = 128, protocol = UDP Spi_Ethernet_writeMemory(REPLY_START + 22, 0x80, 0x11, 1) ; // flags + 0, fragment offset = 0, TTL = 128, protocol = UDP Spi_Ethernet_writeMemory(REPLY_START + 24, 0, 0, 1) ; // clear IP checksum Spi_Ethernet_memcpy(REPLY_START + 26, ipAddr, 4) ; // source Spi_Ethernet_memcpy(REPLY_START + 30, destIP, 4) ; // destination
/*************************** * third, build UDP header */ totlen = pktLen + 8 ; Spi_Ethernet_writeMemory(REPLY_START + 34, sourcePort >> 8, sourcePort, 1) ; // source port Spi_Ethernet_writeMemory(REPLY_START + 36, destPort >> 8, destPort, 1) ; // destination port Spi_Ethernet_writeMemory(REPLY_START + 38, totlen >> 8, totlen, 1) ; // packet length Spi_Ethernet_writeMemory(REPLY_START + 40, 0, 0, 1) ; // clear UDP checksum
Spi_Ethernet_memcpy(REPLY_START + 42, pkt, pktLen) ; // payload
/***************** * fourth, IP checksum */ Spi_Ethernet_checksum(REPLY_START + 14, 20) ; Spi_Ethernet_writeMemory(REPLY_START + 24, Spi_Ethernet_readReg(EDMACSH), Spi_Ethernet_readReg(EDMACSL), 1) ;
/***************** * fifth, UDP checksum */ // pseudo header // word alignement align = totlen & 1 ; Spi_Ethernet_writeMemory(REPLY_START + 42 + pktLen, 0, 0, 0) ; // clear
// build pseudo header Spi_Ethernet_RAMcopy(REPLY_START + 26, REPLY_START + 26 + 8, REPLY_START + 42 + pktLen + align, 0) ; Spi_Ethernet_writeMemory(REPLY_START + 42 + pktLen + align + 8, 0, 17, 1) ; Spi_Ethernet_putByte(totlen >> 8) ; Spi_Ethernet_putByte(totlen) ;
// ready for checksum Spi_Ethernet_checksum(REPLY_START + 34, totlen + 12 + align) ; Spi_Ethernet_writeMemory(REPLY_START + 40, Spi_Ethernet_readReg(EDMACSH), Spi_Ethernet_readReg(EDMACSL), 1) ;
/************************** * sixth, send packet over wires */ Spi_Ethernet_txPacket(42 + pktLen) ; // restore ERDPT registers before exiting Spi_Ethernet_writeAddr(ERDPTL, rdptr) ; }
/* * no TCP */ unsigned int Spi_Ethernet_userTCP(unsigned char *remoteHost, unsigned int remotePort, unsigned int localPort, unsigned int reqLength) { return(0) ; }
/* * save remote UDP host and port number, then reply with 'Start' */ unsigned int Spi_Ethernet_UserUDP(unsigned char *remoteHost, unsigned int remotePort, unsigned int destPort, unsigned int reqLength) { if(destPort != 33000) return(0) ; // service is on port 33000 switch(Spi_Ethernet_getByte()) { // begin command case 'b': case 'B': transmit = 1 ; // set transmission flag memcpy(destIPAddr, remoteHost, 4) ; // save remote host IP addr destPortNum = remotePort ; // save remote host port number Spi_Ethernet_putByte('B') ; // reply with 'Begin' string Spi_Ethernet_putByte('e') ; Spi_Ethernet_putByte('g') ; Spi_Ethernet_putByte('i') ; Spi_Ethernet_putByte('n') ; return(5) ; // 5 bytes to transmit
// end command case 'e': case 'E': transmit = 0 ; // clear transmission flag Spi_Ethernet_putByte('E') ; // reply with 'End' Spi_Ethernet_putByte('n') ; Spi_Ethernet_putByte('d') ; return(3) ; // 3 bytes to transmit
// error default: Spi_Ethernet_putByte('?') ; return(1) ; } }
/* * timers tmr and longTmr are incremented Fosc / 4 / 65536 / TMR1prescaler * TMR1prescaler is 8 (see main) * about 15.25 times per second with 32 Mhz clock */ void interrupt() { if(PIR1.TMR1IF) // timer1 overflow ? { tmr++ ; // increment timers longTmr++ ; PIR1.TMR1IF = 0 ; // clear timer1 overflow flag } } /* * main entry */ void main() { PORTB = 0 ; TRISB = 0xff ; // set PORTB as input PORTC = 0 ; TRISC = 0b11011100 ; // set PORTC as input except for bits 0 (RESET) and 1 (CS) T1CON = 0b10110101 ; // 16 bits, from other source, prescaler 1:8, oscillator disabled, not synchronized, internal clock, enabled PIR1.TMR1IF = 0 ; // clear TMR1 overflow interrupt flag PIE1.TMR1IE = 1 ; // enable interrupt on TMR1 overflow INTCON |= 0b11000000 ; // enable global & peripheral interrupts // init ARP cache memset(&arpCache, 0, sizeof(arpCache)) ;
/* * starts Spi_Ethernet with : * reset bit on RC0, * CS bit on RC1, * my MAC & IP address, * full duplex */ Spi_Init() ; Spi_Ethernet_Init(&PORTC, 0, &PORTC, 1, macAddr, ipAddr, Spi_Ethernet_FULLDUPLEX) ;
while(1) // do forever { Spi_Ethernet_doPacket() ; // process incoming Ethernet packets
if(transmit && (tmr > 15)) // transmit one UDP packet per second { ByteToStr(PORTB, udpStr + 6) ; // read PORTB value and convert to string sendUDP(destIpAddr, 3456, destPortNum, udpStr, sizeof(udpStr)) ; // send to remote host tmr = 0 ; // reset timer } } }
|