'----------------------------------------------------------- ' Atmega168 and ENC28J60 '----------------------------------------------------------- ' Version 1.0 - june 2007 by Ben Zijlstra ' august 2007 addins by Viktor Varga 'fusebits: 'Atmega168 - External clock (gaat van 6.25 Mhz naar 12.5 Mhz) No div by 8 $regfile = "m168def.dat" $crystal = 6250000 $baud = 9600 'put terminal on 19200 baud $hwstack = 64 $swstack = 64 $framesize = 64 $include "enc28j60.inc" '/* The Ethernet address. */ Const Ethaddr0 = &H43 Const Ethaddr1 = &HC6 Const Ethaddr2 = &HB3 Const Ethaddr3 = &HF3 Const Ethaddr4 = &HAB Const Ethaddr5 = &H9E '/* The IP address. */ Const Ipaddr0 = 192 Const Ipaddr1 = 168 Const Ipaddr2 = 2 Const Ipaddr3 = 78 '/* Maximum frame length - The Atmega168 has only 1024 byte internal RAM! */ Const Max_framelen = 200 '200 byte will be enough for common tasks Enc28j60_cs Alias Portb.2 Led Alias Portb.1 Config Enc28j60_cs = Output Config Led = Output Set Led 'Turn off the tuxgraphics board LED Dim X As Byte Dim A(5) As Byte Dim Bank As Byte Dim Enc28j60_data As Byte Dim Nextpacketptr As Word Dim Value As Byte Dim Wdata As Word Dim Buffer(max_framelen) As Byte Dim Rstatus As Byte Dim Length As Word Dim Rxstat As Word Dim Tempx As Byte Dim Ip_id As Word Declare Sub Enc28j60init Declare Sub Enc28j60readcontrolregbyte(byval Register As Byte) Declare Sub Enc28j60writecontrolregbyte(byval Register As Byte , Byval Value As Byte) Declare Sub Enc28j60selectbank(byval Bank As Byte) Declare Sub Enc28j60bitfield_set(byval Register As Byte , Byval Value As Byte) Declare Sub Enc28j60bitfield_clear(byval Register As Byte , Byval Value As Byte) Declare Sub Enc28j60readphyword(byval Phyregister As Byte) Declare Sub Enc28j60writephyword(byval Phyregister As Byte , Byval Wdata As Word) Declare Sub Enc28j60packetsend(byval Pcktlen As Word) Declare Sub Enc28j60packetreceive Declare Sub Enc28j60poll Declare Sub Calcchecksum(byval Startptr As Word , Byval Endptr As Word , Byval Result As Word) Declare Sub Arpreply Declare Sub Pingreply Declare Sub Setip_id Declare Sub Setst_mac Declare Sub Setst_ip 'Configuration of the SPI-bus Config Spi = Hard , Interrupt = Off , Data Order = Msb , Master = Yes , Polarity = Low , Phase = 0 , Clockrate = 4 , Noss = 0 'init the spi pins Spiinit Enc28j60_cs = 0 'reset ENC28J60 X = Enc28j60_soft_reset Spiout X , 1 Enc28j60_cs = 1 Do Call Enc28j60readcontrolregbyte(estat) X = Enc28j60_data.estat_clkrdy Loop Until X = 1 'clock from default divide/4 (6.25 Mhz) to divide/2 (12.5 Mhz) Call Enc28j60writecontrolregbyte(ecocon , &B00000010) Waitms 250 Print Print "Starting Tux" Call Enc28j60selectbank(3) 'EREVID A(1) = &B000_10010 Enc28j60_cs = 0 Spiout A(1) , 1 Spiin A(1) , 2 Print "Enc28j60-version = " ; A(2) Enc28j60_cs = 1 Call Enc28j60init Do Call Enc28j60poll Loop End Sub Enc28j60init 'do bank 0 stuff 'initialize receive buffer '16-bit transfers, must write low byte first 'set receive buffer start address Nextpacketptr = Rxstart_init Value = Low(rxstart_init) Call Enc28j60writecontrolregbyte(erxstl , Value) Value = High(rxstart_init) Call Enc28j60writecontrolregbyte(erxsth , Value) 'set receive pointer address Value = Low(rxstart_init) Call Enc28j60writecontrolregbyte(erxrdptl , Value) Value = High(rxstart_init) Call Enc28j60writecontrolregbyte(erxrdpth , Value) 'set receive buffer end Value = Low(rxstop_init) Call Enc28j60writecontrolregbyte(erxndl , Value) Value = High(rxstop_init) Call Enc28j60writecontrolregbyte(erxndh , Value) 'set transmit buffer start Value = Low(txstart_init) Call Enc28j60writecontrolregbyte(etxstl , Value) Value = High(txstart_init) Call Enc28j60writecontrolregbyte(etxsth , Value) 'do bank 2 stuff 'enable MAC receive Value = 0 Value.macon1_marxen = 1 Value.macon1_txpaus = 1 Value.macon1_rxpaus = 1 Call Enc28j60writecontrolregbyte(macon1 , Value) 'bring MAC out of reset 'enable automatic padding and CRC operations Value = 0 Value.macon3_padcfg0 = 1 Value.macon3_txcrcen = 1 Value.macon3_frmlnen = 1 Call Enc28j60writecontrolregbyte(macon3 , Value) 'set inter-frame gap (non-back-to-back) Call Enc28j60writecontrolregbyte(maipgl , &H12) Call Enc28j60writecontrolregbyte(maipgh , &H0C) 'set inter-frame gap (back-to-back) Call Enc28j60writecontrolregbyte(mabbipg , &H12) 'set the maximum packet size which the controller will accept Value = Low(max_framelen) Call Enc28j60writecontrolregbyte(mamxfll , Value) Value = High(max_framelen) Call Enc28j60writecontrolregbyte(mamxflh , Value) 'bank 3 stuff Call Enc28j60writecontrolregbyte(maadr5 , Ethaddr0) Call Enc28j60writecontrolregbyte(maadr4 , Ethaddr1) Call Enc28j60writecontrolregbyte(maadr3 , Ethaddr2) Call Enc28j60writecontrolregbyte(maadr2 , Ethaddr3) Call Enc28j60writecontrolregbyte(maadr1 , Ethaddr4) Call Enc28j60writecontrolregbyte(maadr0 , Ethaddr5) 'no loopback of transmitted frames Call Enc28j60writephyword(phcon2 , Phcon2_hdldis) 'switch to bank 0 Call Enc28j60selectbank(0) 'enable interrupts Value = 0 Value.eie_intie = 1 Value.eie_pktie = 1 Call Enc28j60bitfield_set(eie , Value) 'Setting receive filters Buffer(1) = &HFF : Buffer(2) = &HFF Buffer(3) = &HFF : Buffer(4) = &HFF Buffer(5) = &HFF : Buffer(6) = &HFF Buffer(7) = &H08 : Buffer(8) = &H06 Buffer(9) = Ipaddr0 : Buffer(10) = Ipaddr1 Buffer(11) = Ipaddr2 : Buffer(12) = Ipaddr3 Call Calcchecksum(1 , 11 , 13) 'Checksum will be in Buffer(13)(14) Call Enc28j60writecontrolregbyte(epmcsl , Buffer(14)) 'Checksum for ARP packets Call Enc28j60writecontrolregbyte(epmcsh , Buffer(13)) 'Checksum for ARP packets Call Enc28j60writecontrolregbyte(epmm0 , &H3F) 'Pattern Match Mask0 - Broadcast (FF FF FF FF FF FF) Call Enc28j60writecontrolregbyte(epmm1 , &H30) 'Pattern Match Mask1 - Packet type (ARP - 08 06) Call Enc28j60writecontrolregbyte(epmm4 , &HC0) 'Pattern Match Mask4 - Target IP first 3 byte (ARP packet for us) Call Enc28j60writecontrolregbyte(epmm5 , &H03) 'Pattern Match Mask5 - Target IP last 1 byte Value = 0 Value.erxfcon_pmen = 1 'Pattern Match enable (ARP only) Value.erxfcon_ucen = 1 'Unicast enable Call Enc28j60bitfield_set(erxfcon , Value) Value = 0 Value.erxfcon_bcen = 1 'Broadcast disable Call Enc28j60bitfield_clear(erxfcon , Value) 'CRC check is enabled by default 'Something is wrong with the Broadcast filter (or the whole theory), it seems 'like every packet is coming in 'enable packet reception Value = 0 Value.econ1_rxen = 1 Call Enc28j60bitfield_set(econ1 , Value) 'Reset transmit logic Value = 0 Value.econ1_txrst = 1 Call Enc28j60bitfield_set(econ1 , Value) Call Enc28j60bitfield_clear(econ1 , Value) End Sub Sub Enc28j60selectbank(bank As Byte) 'get ECON1 (BSEL1 en BSEL0) A(1) = &B000_11111 Enc28j60_cs = 0 Spiout A(1) , 1 Spiin A(1) , 2 Enc28j60_cs = 1 A(2) = A(2) And &B1111_1100 'strip bank part A(2) = A(2) Or Bank A(1) = &B010_11111 Enc28j60_cs = 0 Spiout A(1) , 2 Enc28j60_cs = 1 End Sub Sub Enc28j60writecontrolregbyte(register As Byte , Value As Byte) Bank = 0 If Register.7 = 1 Then Bank = 2 If Register.6 = 1 Then Bank = Bank + 1 Register = Register And &B00011111 Call Enc28j60selectbank(bank) Register.6 = 1 'to get a 010_register A(1) = Register A(2) = Value Enc28j60_cs = 0 Spiout A(1) , 2 Enc28j60_cs = 1 End Sub Sub Enc28j60readcontrolregbyte(register As Byte) Local Mcphy As Byte Bank = 0 Mcphy = 0 If Register.7 = 1 Then Bank = 2 If Register.6 = 1 Then Bank = Bank + 1 If Register.5 = 1 Then Mcphy = 1 Register = Register And &B00011111 Call Enc28j60selectbank(bank) A(1) = Register Enc28j60_cs = 0 Spiout A(1) , 1 Spiin A(1) , 3 Enc28j60_cs = 1 'Depending of register (E, MAC, MII) yes or no dummybyte If Mcphy = 1 Then Enc28j60_data = A(2) Else Enc28j60_data = A(3) End If End Sub Sub Enc28j60bitfield_set(register As Byte , Value As Byte) Bank = 0 If Register.7 = 1 Then Bank = 2 If Register.6 = 1 Then Bank = Bank + 1 Register = Register And &B00011111 Call Enc28j60selectbank(bank) Register = Register Or &B100_00000 A(1) = Register A(2) = Value Enc28j60_cs = 0 Spiout A(1) , 2 Enc28j60_cs = 1 End Sub Sub Enc28j60bitfield_clear(register As Byte , Value As Byte) Bank = 0 If Register.7 = 1 Then Bank = 2 If Register.6 = 1 Then Bank = Bank + 1 Register = Register And &B00011111 Call Enc28j60selectbank(bank) Register = Register Or &B101_00000 A(1) = Register A(2) = Value Enc28j60_cs = 0 Spiout A(1) , 2 Enc28j60_cs = 1 End Sub Sub Enc28j60readphyword(phyregister As Byte) 'set the right address and start the register read operation Call Enc28j60writecontrolregbyte(miregadr , Phyregister) Call Enc28j60writecontrolregbyte(micmd , Micmd_miird) 'wait until the PHY read complets Do Call Enc28j60readcontrolregbyte(mistat) Loop Until Enc28j60_data.mistat_busy = 0 'quit reading Call Enc28j60writecontrolregbyte(micmd , 0) 'get data value Call Enc28j60readcontrolregbyte(mirdl) Wdata = Enc28j60_data Shift Wdata , Left , 8 Call Enc28j60readcontrolregbyte(mirdh) Wdata = Wdata + Enc28j60_data End Sub Sub Enc28j60writephyword(phyregister As Byte , Wdata As Word) Call Enc28j60readphyword(phyregister) Local Temp As Byte 'set the PHY register address Call Enc28j60writecontrolregbyte(miregadr , Phyregister) Call Enc28j60readcontrolregbyte(miregadr) Temp = Miregadr Value = Low(wdata) Call Enc28j60writecontrolregbyte(miwrl , Value) Value = High(wdata) Call Enc28j60writecontrolregbyte(miwrh , Value) Do Call Enc28j60readcontrolregbyte(mistat) Loop Until Enc28j60_data.mistat_busy = 0 End Sub Sub Enc28j60poll Call Enc28j60readcontrolregbyte(epktcnt) If Enc28j60_data > 0 Then Call Enc28j60packetreceive End If End Sub Sub Enc28j60packetreceive Reset Led Print "EPKTCNT = " ; Bin(enc28j60_data) Local Wtemp As Word 'set the read pointer to the start of the received packet Value = Low(nextpacketptr) Call Enc28j60writecontrolregbyte(erdptl , Value) Value = High(nextpacketptr) Call Enc28j60writecontrolregbyte(erdpth , Value) Print "Present nextpacketpntr " ; Hex(nextpacketptr) Enc28j60_cs = 0 'Send Read Buffer Memory command Spdr = &H3A Do Loop Until Spsr.spif = 1 'Get the first 6 byte (3 word: Nextpacketptr, Packetlength, Rxstat) For X = 1 To 6 Spdr = &HFF 'SPI read Do Loop Until Spsr.spif = 1 'SPI ready Buffer(x) = Spdr Next X Nextpacketptr = Buffer(2) * 256 Nextpacketptr = Nextpacketptr + Buffer(1) Length = Buffer(4) * 256 Length = Length + Buffer(3) Rxstat = Buffer(6) * 256 Rxstat = Rxstat + Buffer(5) Print "New Nextpacketpntr " ; Hex(nextpacketptr) Print "Packetlength = " ; Hex(length) Print "Rxstat = " ; Bin(rxstat) 'Get the payload Length = Length - 4 'Discard CRC For X = 1 To Length Spdr = &HFF 'SPI read Do Loop Until Spsr.spif = 1 'SPI ready Buffer(x) = Spdr Next X Enc28j60_cs = 1 'move the rx read pointer to the start of the next received packet 'this frees the memory we just read out Value = Low(nextpacketptr) Call Enc28j60writecontrolregbyte(erxrdptl , Value) Value = High(nextpacketptr) Call Enc28j60writecontrolregbyte(erxrdpth , Value) 'decrement the packet counter indicate we are done with this packet Value = 0 Value.econ2_pktdec = 1 Call Enc28j60bitfield_set(econ2 , Value) Set Led 'Print the packet content Print "The packet:" For X = 1 To Length Print Hex(buffer(x)) ; " " ; Next X Print 'Handle the packet If Buffer(13) = &H08 Then If Buffer(14) = &H06 Then 'Type:ARP If Buffer(21) = &H00 Then 'ARP request If Buffer(22) = &H01 Then 'ARP request If Buffer(39) = Ipaddr0 Then 'ARP for us If Buffer(40) = Ipaddr1 Then If Buffer(41) = Ipaddr2 Then If Buffer(42) = Ipaddr3 Then Call Arpreply End If End If End If End If End If End If End If If Buffer(14) = &H00 Then 'Type:IP If Buffer(15) = &H45 Then 'We handle only simple IP packets If Buffer(21) = 0 Then 'We handle only non fragmented packets If Buffer(31) = Ipaddr0 Then 'Ip packet for us If Buffer(32) = Ipaddr1 Then If Buffer(33) = Ipaddr2 Then If Buffer(34) = Ipaddr3 Then If Buffer(24) = 1 Then 'Protocol:ICMP If Buffer(35) = &H08 Then 'ICMP echo request Call Pingreply End If End If If Buffer(24) = 17 Then 'Protocol:UDP End If If Buffer(24) = 6 Then 'Protocol:TCP End If End If End If End If End If End If End If End If End If End Sub Sub Enc28j60packetsend(pcktlen As Word) 'Load packet into the ENC Enc28j60_cs = 0 Spdr = Enc28j60_write_buf_mem Do Loop Until Spsr.spif = 1 Spdr = &B000_1110 'per packet byte Do Loop Until Spsr.spif = 1 For X = 1 To Pcktlen Spdr = Buffer(x) Do Loop Until Spsr.spif = 1 Next X Enc28j60_cs = 1 'Minimum packet length is 60 If Pcktlen < 60 Then Pcktlen = 60 'Reset transmit logic Value = 0 Value.econ1_txrst = 1 Call Enc28j60bitfield_set(econ1 , Value) Call Enc28j60bitfield_clear(econ1 , Value) 'set the write pointer to start of transmit buffer area Value = Low(txstart_init) Call Enc28j60writecontrolregbyte(ewrptl , Value) Value = High(txstart_init) Call Enc28j60writecontrolregbyte(ewrpth , Value) 'set the TXND pointer to correspond to the packet size given Value = Low(txstart_init) Value = Value + Low(pcktlen) Call Enc28j60writecontrolregbyte(etxndl , Value) Value = High(txstart_init) Value = Value + High(pcktlen) Call Enc28j60writecontrolregbyte(etxndh , Value) 'write per-packet control byte has been put in the writeroutine 'send the contents of the transmit buffer onto the network Value = 0 Value.econ1_txrts = 1 Call Enc28j60bitfield_set(econ1 , Value) End Sub Sub Calcchecksum(startptr As Word , Endptr As Word , Result As Word) Local I As Word Local Tmp As Word Local Sum As Long Sum = 0 For I = Startptr To Endptr Step 2 Sum = Sum + Buffer(i + 1) Tmp = Buffer(i) * 256 Sum = Sum + Tmp Next I I = Highw(sum) Tmp = Sum Tmp = Tmp + I Tmp = Not Tmp Buffer(result) = High(tmp) Buffer(result + 1) = Low(tmp) End Sub Sub Arpreply 'The original request packet is in the buffer, we just change some things Local I As Byte 'Swap MAC addresses Call Setst_mac 'Copy target MAC in ARP packet - starting at pos 33, copy from pos 1 For I = 1 To 6 Buffer(32 + I) = Buffer(i) Next I 'Set target IP in ARP packet - starting at pos 39, original starting at pos 29 For I = 1 To 4 Buffer(38 + I) = Buffer(28 + I) Next I 'Copy source MAC to ARP packet pos 23 from pos 7 For I = 1 To 6 Buffer(22 + I) = Buffer(6 + I) Next I 'Set source IP to ARP packet pos 29 Buffer(29) = Ipaddr0 Buffer(30) = Ipaddr1 Buffer(31) = Ipaddr2 Buffer(32) = Ipaddr3 'Set ARP type from Request to Reply Buffer(22) = 2 'Send the reply packet Call Enc28j60packetsend(42) End Sub Sub Pingreply 'The original request packet is in the buffer, we just change some things Local I As Byte Local Packetlength As Word 'Swap MAC addresses Call Setst_mac 'Set IP ID field Call Setip_id 'Swap IP addresses Call Setst_ip 'Zero out original IP checksum fields Buffer(25) = 0 Buffer(26) = 0 'Calc new IP checksum Call Calcchecksum(15 , 33 , 25) 'Set ICMP type to Echo reply Buffer(35) = 0 'Zero out original ICPM checksum fields Buffer(37) = 0 Buffer(38) = 0 'Calc new ICPM checksum, length calculated from IP packet length Packetlength = Buffer(17) * 256 Packetlength = Packetlength + Buffer(18) Packetlength = Packetlength + 13 'We are going to calculate the checksum till the end of packet (IP length + 14 byte of the ethernet stuff), -1 to get word start Call Calcchecksum(35 , Packetlength , 37) 'Setting packetlength to the correct value Packetlength = Packetlength + 1 'Send the reply packet Call Enc28j60packetsend(packetlength) End Sub Sub Setip_id Buffer(19) = High(ip_id) Buffer(20) = Low(ip_id) Ip_id = Ip_id + 1 End Sub Sub Setst_mac Local I As Byte 'Set target MAC in ethernet frame - starting at pos 1, original starting at pos 7 For I = 1 To 6 Buffer(i) = Buffer(6 + I) Next I 'Set source MAC in ethernet frame, pos 7 Buffer(7) = Ethaddr0 Buffer(8) = Ethaddr1 Buffer(9) = Ethaddr2 Buffer(10) = Ethaddr3 Buffer(11) = Ethaddr4 Buffer(12) = Ethaddr5 End Sub Sub Setst_ip Local I As Byte 'Set target IP in IP header - starting at pos 31, original starting at pos 27 For I = 1 To 4 Buffer(30 + I) = Buffer(26 + I) Next I 'Set source IP in IP header, pos 27 Buffer(27) = Ipaddr0 Buffer(28) = Ipaddr1 Buffer(29) = Ipaddr2 Buffer(30) = Ipaddr3 End Sub