'----------------------------------------------------------- ' Wake on LAN with ENC28J60 '----------------------------------------------------------- 'fusebits: 'ATMEGA8 - Internal clock (1 Mhz) Does not need to be fast $regfile = "m8def.dat" $crystal = 1000000 $baud = 19200 'put terminal on 19200 baud $hwstack = 64 $swstack = 64 $framesize = 64 'enc28j60.inc library from Bascom v.1.11.8.8 (EARLIER VERSIONS MAY NOT BE COMPATIBLE) $include "enc28j60.inc" $lib "tcpip.lbx" '/* Maximum frame length - The Atmega168 has only 1024 byte internal RAM! */ Const Max_framelen = 200 '200 byte will be enough for common tasks 'Set port B pins as output except pin 0 for input switch Ddrb = &B11111110 'Set output ports low and turn on internal pull-up resistor for pin 0 input switch Portb = &B00000001 'Chip Select Pin for SPI Interface Enc28j60_cs Alias Portb.2 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 'Ethernet packet destination Dim T_enetpacketdest0 As Byte At Buffer Overlay Dim T_enetpacketdest1 As Byte At Buffer + &H01 Overlay Dim T_enetpacketdest2 As Byte At Buffer + &H02 Overlay Dim T_enetpacketdest3 As Byte At Buffer + &H03 Overlay Dim T_enetpacketdest4 As Byte At Buffer + &H04 Overlay Dim T_enetpacketdest5 As Byte At Buffer + &H05 Overlay 'Ethernet packet source Dim T_enetpacketsrc0 As Byte At Buffer + &H06 Overlay Dim T_enetpacketsrc1 As Byte At Buffer + &H07 Overlay Dim T_enetpacketsrc2 As Byte At Buffer + &H08 Overlay Dim T_enetpacketsrc3 As Byte At Buffer + &H09 Overlay Dim T_enetpacketsrc4 As Byte At Buffer + &H0A Overlay Dim T_enetpacketsrc5 As Byte At Buffer + &H0B Overlay 'Ethernet packet type Dim T_enetpackettype As Word At Buffer + &H0C Overlay 'IP header layout IP version and header length Dim T_ip_vers_len As Byte At Buffer + &H0E Overlay Dim T_tos As Byte At Buffer + &H0F Overlay 'Buffer length Dim T_ip_pktlen0 As Byte At Buffer + &H10 Overlay Dim T_ip_pktlen1 As Byte At Buffer + &H11 Overlay Dim T_id0 As Byte At Buffer + &H12 Overlay Dim T_id1 As Byte At Buffer + &H13 Overlay Dim T_flags As Byte At Buffer + &H14 Overlay Dim T_offset As Byte At Buffer + &H15 Overlay Dim T_ttl As Byte At Buffer + &H16 Overlay 'protocol (ICMP=1, TCP=6, UDP=11) Dim T_ip_proto As Byte At Buffer + &H17 Overlay 'header checksum Dim T_ip_hdr_cksum0 As Byte At Buffer + &H18 Overlay Dim T_ip_hdr_cksum1 As Byte At Buffer + &H19 Overlay Dim T_ip_hdr_cksum As Word At Buffer + &H18 Overlay 'IP address of source Dim T_ip_srcaddr0 As Byte At Buffer + &H1A Overlay Dim T_ip_srcaddr1 As Byte At Buffer + &H1B Overlay Dim T_ip_srcaddr2 As Byte At Buffer + &H1C Overlay Dim T_ip_srcaddr3 As Byte At Buffer + &H1D Overlay Dim T_ip_srcaddr As Long At Buffer + &H1A Overlay 'IP address of destination Dim T_ip_destaddr0 As Byte At Buffer + &H1E Overlay Dim T_ip_destaddr1 As Byte At Buffer + &H1F Overlay Dim T_ip_destaddr2 As Byte At Buffer + &H20 Overlay Dim T_ip_destaddr3 As Byte At Buffer + &H21 Overlay Dim T_ip_destaddr As Long At Buffer + &H1E Overlay 'UDP header Dim T_udp_srcport0 As Byte At Buffer + &H22 Overlay Dim T_udp_srcport1 As Byte At Buffer + &H23 Overlay Dim T_udp_destport0 As Byte At Buffer + &H24 Overlay Dim T_udp_destport1 As Byte At Buffer + &H25 Overlay Dim T_udp_len0 As Byte At Buffer + &H26 Overlay Dim T_udp_len1 As Byte At Buffer + &H27 Overlay Dim T_udp_chksum As Word At Buffer + &H28 Overlay Dim T_udp_chksum0 As Byte At Buffer + &H28 Overlay Dim T_udp_chksum1 As Byte At Buffer + &H29 Overlay Dim T_udp_data As Byte At Buffer + &H2A Overlay Dim T_udp_data1 As Byte At Buffer + &H2B Overlay Dim T_udp_data2 As Byte At Buffer + &H2C Overlay Dim T_udp_data3 As Byte At Buffer + &H2D Overlay Dim T_udp_data4 As Byte At Buffer + &H2E Overlay Dim T_udp_data5 As Byte At Buffer + &H2F Overlay Dim I_chksum32 As Long Dim I_x As Word Dim I_odd As Byte Dim I_checksum16 As Word Dim Result16 As Word Dim Result16h As Byte At Result16 + 1 Overlay Dim Result16l As Byte At Result16 Overlay Dim I_value16 As Word Dim I_value16h As Byte At I_value16 + 1 Overlay Dim I_value16l As Byte At I_value16 Overlay Dim Hulp1 As Byte Dim Hulp2 As Word Dim Hulp3 As Word 'variables with overlays Dim Myip(4) As Byte Dim Mymac(6) As Byte Dim Myroutermac(6) As Byte Dim Wake_mac(6) As Byte 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 Udp_checksum Declare Sub Echopacket Declare Sub Ip_header_checksum Declare Sub Wol 'PUT YOUR ADDRESSES HERE 'MAC address Mymac(1) = &H Mymac(2) = &H Mymac(3) = &H Mymac(4) = &H Mymac(5) = &H Mymac(6) = &H 'PUT YOUR ADDRESSES HERE 'IP address Myip(1) = 192 Myip(2) = 168 Myip(3) = 1 Myip(4) = 199 'PUT YOUR ADDRESSES HERE 'Router MAC address Myroutermac(1) = &H Myroutermac(2) = &H Myroutermac(3) = &H Myroutermac(4) = &H Myroutermac(5) = &H Myroutermac(6) = &H 'PUT YOUR ADDRESSES HERE 'Computer to wake IP MAC address Wake_mac(1) = &H Wake_mac(2) = &H Wake_mac(3) = &H Wake_mac(4) = &H Wake_mac(5) = &H Wake_mac(6) = &H '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 Call Enc28j60selectbank(3) 'EREVID A(1) = &B000_10010 Enc28j60_cs = 0 Spiout A(1) , 1 Spiin A(1) , 2 Enc28j60_cs = 1 Call Enc28j60init 'LED Color Pulse Length Dim Pulse_r As Integer 'Maximum LED color pulse length Const Maxpulse = 4096 Pulse_r = Maxpulse 'Coefficient to adjust LED curve Dim Coef As Single Coef = Maxpulse * .000000001 Dim Curve As Single 'Loop speed/direction Dim S As Integer S = 1 Dim I As Integer Do 'Check for WOL input switch If Pinb.0 = 0 Then Set Portb.7 Call Wol Wait 3 Reset Portb.7 End If 'LED Pulse 'Increment pulse stepper I = I + S 'Reverse direction at maxium If I > 800 Then I = 800 S = S * -1 End If 'Reverse direction at minimum If I < 100 Then I = 100 S = S * -1 End If 'Calculate cubed exponential curve pulse value for smooth fading 'Use multiplication because it takes much less memory than powers Curve = I * I Curve = Curve * I 'Factor in coefficient Curve = Curve * Coef Pulse_r = Int(curve) If Pulse_r > 0 Then Pulseout Portb , 7 , Pulse_r Waitms 1 Loop End ' Routine to initialize ENC28J60 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 , Mymac(1)) Call Enc28j60writecontrolregbyte(maadr4 , Mymac(2)) Call Enc28j60writecontrolregbyte(maadr3 , Mymac(3)) Call Enc28j60writecontrolregbyte(maadr2 , Mymac(4)) Call Enc28j60writecontrolregbyte(maadr1 , Mymac(5)) Call Enc28j60writecontrolregbyte(maadr0 , Mymac(6)) '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) 'filters according to Guido Call Enc28j60writecontrolregbyte(epmm0 , &H3F) Call Enc28j60writecontrolregbyte(epmm1 , &H30) Call Enc28j60writecontrolregbyte(epmcsl , &HF9) Call Enc28j60writecontrolregbyte(epmcsh , &HF7) 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 ' Routine to select bank 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 ' Routine to write control regbyte 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 ' Routine to read control regbyte 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 ' Routine to set bitfield 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 ' Routine to clear bitfield 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 ' Routine to read phyword 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 ' Routine to write phyword 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 ' Routine to send packets 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 ' Routine to calculate the IP-checkum Sub Udp_checksum 'Local Hulp5 As Word 'Local Hulp6 As Word T_udp_chksum = &H00 'checksum TCP header I_chksum32 = 0 I_value16h = T_ip_srcaddr0 I_value16l = T_ip_srcaddr1 I_chksum32 = I_chksum32 + I_value16 I_value16h = T_ip_srcaddr2 I_value16l = T_ip_srcaddr3 I_chksum32 = I_chksum32 + I_value16 I_value16h = T_ip_destaddr0 I_value16l = T_ip_destaddr1 I_chksum32 = I_chksum32 + I_value16 I_value16h = T_ip_destaddr2 I_value16l = T_ip_destaddr3 I_chksum32 = I_chksum32 + I_value16 'proto I_chksum32 = I_chksum32 + T_ip_proto 'packet length I_value16h = T_udp_len0 I_value16l = T_udp_len1 I_chksum32 = I_chksum32 + I_value16 'misschien vanaf hier TCPCHECKSUM + extra waarden meegeven I_odd = T_udp_len1 Mod 2 Result16h = T_udp_len0 Result16l = T_udp_len1 'udp_srcport0 = packet(&h23) 'Hulp5 = Highw(i_chksum32) 'Hulp6 = I_chksum32 'I_checksum16 = Tcpchecksum(buffer(&H23) , Result16 , Hulp5 , Hulp6) Hulp1 = &H23 Hulp2 = &H23 + Result16 Hulp2 = Hulp2 - 2 For I_x = Hulp1 To Hulp2 Step 2 I_value16h = Buffer(i_x) Hulp3 = I_x + 1 I_value16l = Buffer(hulp3) I_chksum32 = I_chksum32 + I_value16 Next I_x If I_odd = 1 Then Incr Hulp2 I_value16h = Buffer(hulp2) I_value16l = 0 I_chksum32 = I_chksum32 + I_value16 End If I_checksum16 = Highw(i_chksum32) I_checksum16 = I_checksum16 + I_chksum32 ' only 16 lower bits of i_chksum32 is taken... I_checksum16 = Not I_checksum16 T_udp_chksum0 = High(i_checksum16) T_udp_chksum1 = Low(i_checksum16) End Sub ' Routine to calculate a IP-header checksum Sub Ip_header_checksum Local Ip_checksum16 As Word Local Ip_header_length As Byte 'Calculate the IP header checksum T_ip_hdr_cksum = &H00 'Calc starts with chksum=0 'Calculate IP header length Ip_header_length = T_ip_vers_len And &H0F 'Number of 32 bit words Ip_header_length = 4 * Ip_header_length 'Calc number of bytes Ip_checksum16 = Tcpchecksum(buffer(15) , Ip_header_length ) 'Store the checksum value in the packet buffer T_ip_hdr_cksum1 = High(ip_checksum16) T_ip_hdr_cksum0 = Low(ip_checksum16) End Sub ' Routine to echo packet Sub Echopacket 'packetlength Hulp2 = T_udp_len0 * 256 Hulp2 = Hulp2 + T_udp_len1 Hulp2 = Hulp2 + 34 Call Enc28j60packetsend(hulp2) End Sub ' Routine to perform Wake On LAN Sub Wol 'MAC-header 'Destination hardware address T_enetpacketdest0 = Myroutermac(1) T_enetpacketdest1 = Myroutermac(2) T_enetpacketdest2 = Myroutermac(3) T_enetpacketdest3 = Myroutermac(4) T_enetpacketdest4 = Myroutermac(5) T_enetpacketdest5 = Myroutermac(6) ' source (own source) T_enetpacketsrc0 = Mymac(1) T_enetpacketsrc1 = Mymac(2) T_enetpacketsrc2 = Mymac(3) T_enetpacketsrc3 = Mymac(4) T_enetpacketsrc4 = Mymac(5) T_enetpacketsrc5 = Mymac(6) T_enetpackettype = &H0008 ' = &h0800 ' fill IP-header T_ip_vers_len = &H45 T_tos = &H00 T_ip_pktlen0 = &H00 T_ip_pktlen1 = &H30 T_id0 = &H4A T_id1 = &HA5 T_flags = &H00 T_offset = &H00 T_ttl = &H80 'protocol (ICMP=1, TCP=6, UDP=11) T_ip_proto = &H11 'header checksum 'IP address of source T_ip_srcaddr0 = Myip(1) T_ip_srcaddr1 = Myip(2) T_ip_srcaddr2 = Myip(3) T_ip_srcaddr3 = Myip(4) 'IP address of destination 'broadcast IP T_ip_destaddr0 = &HFF T_ip_destaddr1 = &HFF T_ip_destaddr2 = &HFF T_ip_destaddr3 = &HFF 'UDP-header T_udp_srcport0 = &H13 T_udp_srcport1 = &H88 T_udp_destport0 = &H9C 'port (0x9C40 is 40000) T_udp_destport1 = &H40 T_udp_len0 = &H00 T_udp_len1 = &H6E T_udp_data = &HFF 'WOL 6-byte trailer T_udp_data1 = &HFF T_udp_data2 = &HFF T_udp_data3 = &HFF T_udp_data4 = &HFF T_udp_data5 = &HFF 'WOL magic packet = MAC address 16x Local Buf_counter As Byte 'Next UDP data (T_udp_data6) is buffer 49 Local Mac_counter As Byte Mac_counter = 1 For Buf_counter = 49 To 144 Buffer(buf_counter) = Wake_mac(mac_counter) 'MAC Address of computer to wake (6 byte array) Incr Mac_counter If Mac_counter > 6 Then Mac_counter = 1 End If Next Buf_counter Call Ip_header_checksum Call Udp_checksum Call Echopacket End Sub