Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)


Closed Thread
Results 1 to 40 of 63

Hybrid View

  1. #1
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,612

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    ...continued from previous post...

    In the W5100 there's a total of 8k of RAM reserved for the sockets receive memory buffer as well as 8k for the sockets transmit memory buffer. The memory can be assigned to each socket in various configuration by writing to the RMSR and TMSR registers. By default both these registers are set to $55 which assigns 2k of RX and TX memory to each of the four sockets. Since we're only about to use one socket we COULD assign then full 8k of each buffer to socket 0 but for this exercise we'll just leave it at the default.

    Once we've established a connection with the client we can determine if any data has been received by reading the sockets Received Size register. The value returned tells us the number of "new" bytes in the sockets receive buffer. What it doesn't tell us is where in the buffer these bytes are....

    The sockets receive (and transmit) buffers are circular buffers meaning once they hit the highest memory adress they wrap around and start over at the beginning. The first byte we're interested in might be somewhere in the middle of the buffer and the last byte might be just before it even though it intuitively should be after the first byte. And, because all four sockets are assigned a "segment" of memory out of a total of 8k the actual start and end adresses of each sockets buffer can "move around" depending on the amount of memory assigned to each socket. If we take a look at the bottom of the W5100_defs.pbp file posted earlier we'll find this:
    Code:
    '**************************************************************************************
    ' ----------- THESE VALUES MAY NEED TO BE CHANGED BY THE USER OF THIS FILE ------------
    '**************************************************************************************
    ' Socket 0-3 RX memory START & END adresses and MASK values for calculating pointer.
    ' Make sure to change these if the RMSR value is changed from its default value of $55
     
    W5100_S0_RX_START        CON $6000  'Start adress of Socket 0 (not changeable)
    W5100_S0_RX_END          CON $67FF
    W5100_S0_RX_MASK         CON $07FF  'Mask value for 2k
     
    W5100_S1_RX_START        CON $6800  'Start adress of Socket 1 (depends on the size of socket 0)
    W5100_S1_RX_END          CON $6FFF
    W5100_S1_RX_MASK         CON $07FF  'Mask value for 2k
     
    W5100_S2_RX_START        CON $7000  'Start adress of socket 2 (depends on the size of sockets 0 & 1)
    W5100_S2_RX_END          CON $77FF
    W5100_S2_RX_MASK         CON $07FF  'Mask value for 2k
     
    W5100_S3_RX_START        CON $8000  'Start adress of socket 3 (depends on the size of sockets 0, 1 & 2)
    W5100_S3_RX_END          CON $8FFF
    W5100_S3_RX_MASK         CON $07FF  'Mask value for 2k
    '**************************************************************************************
    These are the physical start and end adresses for each sockets RX memory buffer when they are each assigned 2k of memory. If the memory allocation between sockets are changed these constants must be updated to reflect the start and end adress of each sockets memory area.

    OK, so how do we know where in the buffer our new data is? This is done by reading the sockets RX Read Pointer register but unfortunately this doesn't give us the real adress of the data, instead it gives us an offset into the sockets memory area. If the value of the pointer is 0 then the first byte of our new data is located at the start-adress of the sockets RX memory ($6000 in this case), if the value is $123 then the first byte is located at adress $6123. Not to bad.... But to make it a bit more complicated this pointer doesn't wrap around "in sync" with the circular buffer. The Rx Read Pointer register is a 16bit register meaning it counts from 0 to 65535 even though we only have 2048 bytes assigned to our buffer - so where is our data when the pointer value is $1234?

    This is where the sockets RX_MASK constant defined in the W5100_defs.pbp file comes in. When the raw pointer value is bitwise AND'ed with the RX_MASK value we'll get the correct offset into the sockets buffer. If we then add this offset to the physical start adress of sockets buffer we finally have the physical adress of the first byte of new data in the buffer.

    So now we know where the first byte of our new data is as well as how many byte there is to read, now we only need to figure out where the last byte is. Given the above this shouldn't be too hard to do... If the adress of the first byte plus the total number of bytes does not "extend" beyond the end of the buffer (no buffer wrap-around) we're done - just read from first byte to first byte + number of bytes. But if it does "extend" beyond the end of the buffer we need to first read up to the end of the buffer, then start over at the beginning, subtracting the number of bytes already accounted for and continue reading. I've wrapped all of this into, yet another, subroutine:
    Code:
    '******************************************************************************************
    ' Variables WizPtr, WizStart, WizEnd and WizSize (all WORDs) declared elsewhere.
     
    CheckBufferForData:
     
        ' Read the sockets receive size register.
        WizAdress = W5100_S0_RX_RSR0 : GOSUB Read_W5100 : WizSize.HighByte = WizData
        WizAdress = W5100_S0_RX_RSR1 : GOSUB Read_W5100 : WizSize.LowByte = WizData
     
        ' If received size is more than 0 we do indeed have new data in the buffer.
        ' In that case we read the socket 0 receive pointer register.
        IF WizSize > 0 THEN
     
            WizAdress = W5100_S0_RX_RD0 : Gosub Read_W5100 : WizPtr.HighByte = WizData
            WizAdress = W5100_S0_RX_RD1 : GOSUB Read_W5100 : WizPtr.LowByte = WizData
     
            ' Calculate the physical adress of the first "fresh" byte in the buffer.
            WizStart = W5100_S0_RX_START + (WizPtr & W5100_S0_RX_MASK)
     
            ' Is the buffer about to wrap around?
            IF WizStart + WizSize > W5100_S0_RX_END THEN
                WizEnd = W5100_S0_RX_START + ((WizStart + WizSize) - W5100_S0_RX_END)
                WizEnd = WizEnd - 2         ' Account for the "zero indexing"
     
            ELSE                                  'No wrap around.
     
                WizEnd = WizStart + WizSize - 1
            ENDIF
     
        ENDIF
    RETURN
    '******************************************************************************************
    When returning from this subroutine the physical adress of the first byte as well as that of the last byte of new data are available to us in the WizStart and WizEnd variables. If WizEnd is less than WizStart the buffer has wrapped around and we need to account for that when actually "extracting" the data. If WizEnd is bigger than [/i]WizStart[/i] then it's as simple as reading from the WizStart to WizEnd.
    Code:
    '*****************************************************************************************************
    GetData:
        HSEROUT [REP "-"\20, " Data read from Rx buffer follows ", REP "-"\20, 13]
     
        Counter = 0                                 ' Temporary as a dubug aid
     
        ' Do we have a buffer wrap around condition?
        If WizEnd < WizStart THEN
     
           ' When that happends we need to start reading at the first byte in the received
           ' package and go on until the end of the sockets RX memory....
            For WizAdress = WizStart to W5100_S0_RX_END
                Gosub Read_W5100
                HSEROUT[WizData]
                Counter = Counter + 1                ' Keep count
            NEXT
     
            ' ...then we need to start over at the beginning of the buffer and keep reading
            ' until we've got all the bytes in the buffer.
            For WizAdress = W5100_S0_RX_START to WizEnd
                Gosub Read_W5100
                HSEROUT[WizData]
                Counter = Counter + 1                ' Keep count
            Next
     
        ELSE       ' There's no buffer wrap-around.
     
            ' WizStart now contains the physical adress of the first byte to read.
            For WizAdress = WizStart to WizEnd
                Gosub Read_W5100
                HSEROUT[WizData]
                Counter = Counter + 1                ' Keep count
            NEXT
     
        ENDIF
     
        HSEROUT [REP "-"\23, " End of data from Rx buffer ", REP "-"\23, 13]
        HSEROUT ["Total number of bytes read:", #Counter,13]
        HSEROUT [REP "-"\76, 13, 13]
    When all data have been "extracted" from the buffer we have to tell the W5100 that we're done so it can recycle that memory for new data that comes in. This is done by first updating the RX Read pointer register so it points to where we left off and finally issuing the RECV command to the sockets command register. Issuing the RECV command tells the W5100 that we're done with the recevied data up to the "adress" of the newly written RX Read pointer register.

    Finally, since we're currently not responding to the request from the client, we simply disconnect and switch back into Listen mode. Not very polite but it'll look the browser as if no one is at the other end so it's OK for now.
    Code:
        WizPtr = WizPtr + WizSize
     
        WizAdress = W5100_S0_RX_RD0 : WizData = WizPtr.HighByte : GOSUB Write_W5100
        WizAdress = W5100_S0_RX_RD1 : WizData = WizPtr.LowByte  : GOSUB Write_W5100
     
        WizAdress = W5100_S0_CR : WizData = W5100_Sn_RECV : GOSUB Write_W5100
        WizAdress = W5100_S0_CR : WizData = W5100_Sn_LISTEN : GOSUB Write_W5100
    Using the above code you'll be able to "see" the HTTP GET request issued by a web-browser pointing at the W5100's IP-adress:

    Name:  HTTP Get request.jpg
Views: 8123
Size:  84.7 KB

    To this post I've attached the exact file I compiled to arrive at the screenshot above (remove .txt extension), you'll also need the W5100_defs.pbp posted earlier in the thread.

    /Henrik.
    Attached Files Attached Files

  2. #2
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,612

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hi,
    Anyone knows what needs to be done to have a device listen to a "name" as well as its IP-adress?
    For example, I have a SOHO server which has a web-interface, I can access that by either typing in its IP-adress or its name in the adress field of the browser.

    I don't know if it's even possible to do with the W5100 but without knowing how the name is mapped to the IP-adress I don't know how to find out if it can be done. I'm guessing the W5100 needs to "present itself" to the network somehow but how does that mechanism work? Anyone knows or have a reference you can point me at?

    /Henrik.

  3. #3
    Join Date
    Dec 2005
    Posts
    1,073

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Quote Originally Posted by HenrikOlsson View Post
    Anyone knows what needs to be done to have a device listen to a "name" as well as its IP-adress?
    For example, I have a SOHO server which has a web-interface, I can access that by either typing in its IP-adress or its name in the adress field of the browser.
    I think your SOHO server (or router) has a DNS/DHCP server which maintains a list of names and corresponding IP addresses. This page might help.
    Last edited by dhouston; - 5th June 2011 at 16:11.

  4. #4
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,612

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Thanks a lot Dave!
    Simply knowing it's the DNS servers job pairing up names with IP-numbers narrows the search for me. I feel I should've know that but for some reason I was only thinking of DHCP which I knew it wasn't.
    Wiznet has an appnote on DNS but I'm not sure it pertains to the W5100, but again, now I know what to look for.

    Thanks!
    /Henrik.

  5. #5
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,612

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hi,
    I now have a working DHCP client. This means I can just plug in the network cable and the thing gets an IP-adress from the DHCP server (my router in this case). It also passes a host name to the DHCP-server which then somehow passes that on to the DNS server that apparently runs on the router. In the end it means that I can now get to it by browsing to http://amicus or whatever name I wish the unit to have - good times :-)

    But, this has lead me to the unavoidable...
    The routines previously posted are more or less hardcoded to use socket 0. This is of course not optimal and now is the time to do something about that. Each socket has its own control and status registers etc at a fixed offset of $100 between them so it's pretty easy to calculate the correct adress based on the socket we want the routine to access. For example:
    Code:
    WizSocket VAR BYTE
    WizSocket = 2
     
    WizAdress = W5100_S0_MR + (WizSocket * $100) : WizData = 1 : GOSUB Write_W5100
    This will access socket 2's mode register - pretty straight forward.

    The problem comes with the resizable circular receive and transmitt buffers. The only fixed point here is the start of socket 0's RX and TX buffer. The end of it as well as the start AND end of the other sockets buffers can move around based on how we set it up. I'm looking for ideas on how to handle this effeciently and user friendly.

    Currently I've done this:
    Code:
    GetSocketRXAdress:
    ' Complete routine executes in 174 cycles.
        WizAdressOffset = WizSocket * $100
     
    ' These 3 lookup statements takes 259 bytes of program memory  
        Lookup2 WizSocket, [W5100_S0_RX_Start, W5100_S1_RX_Start, W5100_S2_RX_Start, W5100_S3_RX_Start], Socket_RX_Start
        Lookup2 WizSocket, [W5100_S0_RX_END,   W5100_S1_RX_END,   W5100_S1_RX_END,   W5100_S1_RX_END],   Socket_RX_End
        Lookup2 WizSocket, [W5100_S0_RX_MASK,  W5100_S1_RX_MASK,  W5100_S2_RX_MASK,  W5100_S3_RX_MASK],  Socket_RX_MASK  
     
    RETURN
    And of course a similar routine for the TX buffers. Instead of using W5100_S0_RX_Start etc the routines that accesses the RX buffer now uses Socket_RX_Start etc instead.

    These routines are then called when "changing sockets" meaning that we don't have to calculate the adresses every time we access a register we only do it when changing sockets. The routine to check the amount of free space in any sockets TX buffer then looks like this:
    Code:
    GetFreeTXSize:
        If WizSocket <> WizOldSocket THEN            ' No need to get offsets etc if we're using the same socket as last time.
            GOSUB GetSocketTXAdress
        ENDIF
        
        ' Get the total amount of free memory in the sockets transmit buffer.
        WizAdress = W5100_S0_TX_FSR0 + WizAdressOffset : GOSUB Read_W5100 : WizSize.HighByte = WizData
        WizAdress = W5100_S0_TX_FSR1 + WizAdressOffset : GOSUB Read_W5100 : WizSize.LowByte = WizData
        
        ' Get the pointer adress of where to put new data.
        WizAdress = W5100_S0_TX_WR0 + WizAdressOffset : GOSUB Read_W5100 : WizPtr.HighByte = WizData
        WizAdress = W5100_S0_TX_WR1 + WizAdressOffset : GOSUB Read_W5100 : WizPtr.LowByte = WizData
        
        'Calculate physical adress of first byte
        WizStart = Socket_TX_START + (WizPtr & Socket_TX_MASK)
        
        WizOldSocket = WizSocket             ' Remember which socket we used.
    RETURN
    I think this is a workable solution to allowing access to any socket without duplicating all the routines with hardcoded adresses AND without having to calculate/retreive the adresses every time we access the W5100.

    What I'm looking for is ideas on how to make it more efficient, I particullarly dislike the fact that the lookup routines (for both TX and RX buffers) takes well over 500 bytes (that's something like 10% of a my complete working web-server including a couple of hundred bytes HTML). Me think there must be a slicker way...

    Thank you in advance!

    /Henrik.

  6. #6
    Join Date
    Sep 2009
    Posts
    755

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Try something like this
    Code:
    Socket_RX_Start var word[4]
    Socket_RX_Start[0]=W5100_S0_RX_Start
    Socket_RX_Start[1]=W5100_S1_RX_Start
    Socket_RX_Start[2]=W5100_S2_RX_Start
    Socket_RX_Start[3]=W5100_S3_RX_Start
    Then you won't need GetSocketRXAdress. In routines just use
    WizStart = Socket_RX_Start[WizSocket] + (WizPtr & Socket_TX_MASK)

  7. #7
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,612

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hello again,
    I guess you can all see the bug in the Lookup code - the line where it looks up the sockets end adress currently returns the wrong adress for socket 2 and 3. As usual I can't edit the post :-(

    /Henrik.

  8. #8
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,612

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Thanks!
    That's one solution which I actually have thought of but it comes at a cost of 48 vs 12 bytes of RAM. But in the end it might be worth it though, I'll give it second thought, thanks!

    /Henrik.

Members who have read this thread : 0

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts