...continued from previous post...
OK, lets write some code....(finally)....
The first thing to do, as always is to set up the hardware. I'm using the AMICUS18 and the modifed Arduino Ethernet Shield, here's the first part of the code:
Code:
DEFINE OSC 64 ' Default oscillator speed on the AMICUS18 is 16*4MHz
DEFINE LOADER_USED 1 ' We're using a bootloader.
DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive
DEFINE HSER_TXSTA 24h ' Enable transmit, BRGH = 1
DEFINE HSER_CLROERR 1 ' Clear overflow automatically
DEFINE HSER_SPBRG 138 ' 115200 Baud @ 64MHz, -0,08%
SPBRGH = 0
BAUDCON.3 = 1 ' Enable 16 bit baudrate generator
TRISC = %10010110 ' RX, TX, MOSI, MISO, SCK, N/U, N/U, LED
TRISB = %11011011 ' N/U, N/U, W5100_CS, N/U, N/U, SD_CS, N/U, W5100_Interrupt
' Aliases for the MSSP module status and control bits.
SSPEN VAR SSPCON1.5 ' SSP Enable bit
CKP VAR SSPCON1.4 ' Clock Polarity Select
SMP VAR SSPSTAT.7 ' Data input sample phase
CKE VAR SSPSTAT.6 ' Clock Edge Select bit
SSPIF VAR PIR1.3 ' SPI interrupt flag
milliseconds VAR WORD ' Timer variable, used in the ISR.
TMR1_Temp VAR WORD ' Used when reloading the timer in the ISR
i VAR BYTE ' General purpose.
CLEAR
' Set up the MSSP module.
CKP = 0 ' Clock idles low
CKE = 1 ' Transmit on active to idle transition.
SSPIF = 0 ' Clear buffer full status
SMP = 0 ' Sample in middle of data
SSPEN = 1 ' Enable SPI pins
SSPCON1.0 = 1 ' Slow down SPI clock to Fosc/16 for now.
Then we need some what to keep track of time. The DHCP server leases us an IP-adress for a finite (usually) amount of time and we need to keep track of that time so we can renew the lease before the time runs out. For this purpose I'm using Darrels DT-Ints so I'm including his files and while I'm at it I'll also include the W5100.pbp and the DHCP.pbp files and the set up an interrupt to trip when TMR1 overflows:
Code:
INCLUDE "DT_Ints-18.bas" ' <- Darrel Taylores interupt engine
INCLUDE "ReEnterPBP-18.bas" ' <- Part of Darrels interrupt engine.
INCLUDE "W5100.pbp" ' <- Main W5100 "driver" and auxiallary routines.
INCLUDE "DHCP.pbp" ' <- Main file for the DHCP engine.
'--- Set up TMR1 interupt.
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR1_INT, _Tick, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM
TMR1_Reload CON 25539 ' Reload value for 200Hz @64MHz, 1:2 prescaler
T1CON.0 = %00010001 ' Start timer 1, 1:2 prescaler
@ INT_ENABLE TMR1_INT
I've put my TMR1 Interrupt service routine at the end of the program but to keep the disussion on track I'll show it here:
Code:
' This is the TMR1 interrupt service routine.
Tick:
milliseconds = milliseconds + 5
If milliseconds => 1000 THEN
DHCP_Seconds = DHCP_Seconds + 1
milliseconds = 0
ENDIF
T1CON.0 = 0
TMR1_Temp.HighByte = TMR1H
TMR1_Temp.LowByte = TMR1L
TMR1_Temp = TMR1_Temp + TMR1_Reload
TMR1H = TMR1_Temp.HighByte
TMR1L = TMR1_Temp:LowByte
T1CON.0 = 1
@ INT_RETURN
This runs at ~200Hz, each time it increments the millisecond variable by 5 and when it reaches 1000 (1 second) the variable DHCP_Seconds is incremented - and this is the important thing. The DHCP code must be called periodically, even after an IP-adress had been recieved. This is because it has to keep track of the lease time which it does by subtracting the number of seconds on DHCP_Seconds from the total lease time given to us by the server. It then resets DHCP_Seconds to 0.
So, "we" must make sure that DHCP_Seconds contains "true time" and GOSUB DHCP periodically. We don't have to increment DHCP_Seconds at exactly 1Hz, we can increment it by 10 every 10 seconds or whatever but it's impotant that it's fairly close to real time. Likewise, we don't have to GOSUB DHCP every second or even every minute but because DHCP_Seconds is a BYTE variable we must atleast do it once per 4 minutes or there abouts.
Next we initilise the W5100 and set the network parameters to "all zeros":
Code:
Begin:
HSEROUT["Program start...", 13]
' A substantial pause is needed for the W5100 to start up and establish
' a link with the network.
Pause 2000
' We don't know any network parameters so these should all be set to 0
' when using DHCP.
HSEROUT["Clearing IP, Netmask and Gateway adress",13]
For i = 0 to 3
W5100_IP[i] = 0 ' Set our IP adress to 0.0.0.0
W5100_Netmask[i] = 0 ' Set our netmask to 0.0.0.0
W5100_GateWay[i] = 0 ' Set gatway adress to 0.0.0.0
NEXT
' Now initilise the W5100 with the above parameters.
GOSUB Init_W5100
There's a bit variable called DHCP_Enabled which should be set to 1 if DHCP is to be used. There might be ocations where we want to have the abbility to use DHCP but also to turn it off, this flag makes sure that the DHCP code doesn't execute, even if called, if not specifically "enabled". Then we must tell the DHCP code which of the W5100's four sockets to use, this is done thru the DHCP_Socket variable. Finally we set the state of the DHCP state machine (it's all implemented as a big state machine by the way) to DHCP_State_Start.
Now, all we need to to is keep GOSUB'ing DHCP periodically and if everything goes well we'll have all the network parameters we need within a second or three:
Code:
DHCP_Enabled = 1 ' Enable DHCP.
DHCP_Socket = 2 ' Use socket 2 for DHCP.
DHCP_State = DHCP_State_Start ' Reset the DHCP state machine.
' Loop around here until we recieve a valid IP adress.
HSEROUT["Entering DHCP",13]
While DHCP_State <> DHCP_State_Bound
GOSUB DHCP
Pause 500
WEND
The key to detect when the process is finished is that the state variable (DHCP_State) is set to DHCP_State_Bound and that's what weäre using here. We keep calling DHCP untill we see the correct state. As long as we also updates DHCP_Seconds in the background the DHCP state machine will automatically issue retransmissions at specific intervals (as per the DHCP specification).
Once we see the bound state we can get the parameters from the DHCP_IP, DHCO_NetMask and DHCP_GateWay arrays and use those to re-initilise the W5100:
Code:
' At this point we should have received a set of valid network paramters from
' the DHCP server on the network. Let's start by simply printing them out.
HSEROUT["The offered IP adress is: "]
For i = 0 to 3
HSEROUT[#DHCP_IP[i], "."]
NEXT
HSEROUT[13,"The gateway is: "]
For i = 0 to 3
HSEROUT[#DHCP_Gateway_IP[i], "."]
NEXT
HSEROUT[13, "The netmask is: "]
For i = 0 to 3
HSEROUT[#DHCP_NetMask[i], "."]
NEXT
' The DHCP engine does NOT actually SET the IP adress for us, it just TELLS
' us what to use. We must make the changes ourselves:
HSEROUT[13,"Setting the W5100 parameters..."]
For i = 0 to 3
W5100_IP[i] = DHCP_IP[i]
W5100_GateWay[i] = DHCP_GateWay_IP[i]
W5100_NetMask[i] = DHCP_NetMask[i]
NEXT
' Now re-initilise the W5100 with the new network parameters.
GOSUB Init_W5100
HSEROUT["DONE!",13]
HSEROUT["Try pingning by IP or name!",13]
Don't forget that we still need to have DHCP_Seconds updated in the ISR and we still have to GOSUB DHCP on a regular basis so that it can automatically renew the IP adress lease when it's about to expire:
Code:
Main:
Toggle PortC.0
Pause 500
GOSUB DHCP
Goto Main
Attached to this post are all the updated files (W5100.pbp, W5100_defs.pbp, W5100_subs.pbp, DHCP.pbp, DHCP_defs.pbp, DHCP_subs.pbp and the DHCP_Demo application used to get the above results.
/Henrik.
Bookmarks