32 bit seconds math (how do I include the upper 16 bits?)


Closed Thread
Results 1 to 17 of 17

Hybrid View

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


    Did you find this post helpful? Yes | No

    Default Re: 32 bit seconds math (how do I include the upper 16 bits?)

    Hi Dwight,
    Try the following:
    Code:
    ooDays VAR WORD
    i VAR WORD
    Temp VAR WORD
    
    ' This uses DIV32 by preloading the system variable it uses with
    ' the value we want to divide. To find out how many days has passed
    ' we divide the number of seconds with 86400 (number of seconds in a day).
    ' However, DIV32 only supports 16 bit divisor so we need to do the
    ' division in two steps, 21600 * 4 = 86400.
    
    R0 = AB			' High word of seconds into system var
    R2 = CD			' Low word of seconds into system var
    ooDays = DIV32 21600	' Divide by 86400
    ooDays = ooDays / 4
    
    ' Now we need to subtract 86400 seconds from the running time
    ' one time for each day that has passed. Since we're working with
    ' 16-bit words we need to this sort of "manually".
    ' 65536 + 20864 = 86400
    
    For i = 1 to ooDays
      Temp = CD
      CD = CD - 20864
    
      IF Temp < CD THEN	' Did we underflow the low word?
        AB = AB - 1		' If so, decrement high word
      ENDIF
    
      AB = AB - 1            ' Subtract 65536 
    NEXT
    
    ' At this point ABCD contains anything from 0 to 86399 and we need to
    ' figure out how many "full hours" is in it. Because 86399 is more than
    ' what fits in a 16-bit word we're using DIV32 trick to divide the
    ' number of seconds by 3600 (number of seconds in an hour). 
    
    R0 = AB			' High word of what's left in seconds into system var
    R2 = CD			' Low word of what's left in seconds into system var
    ooHH = DIV32 3600	' Divide by the number of seconds in an hour
    
    ' Subtract 3600 from the running time, one time for each hour.
    For i = 1 to ooHH
      Temp = CD
      CD = CD - 3600
     
      IF Temp < CD THEN	' Did we underflow the low word?
        AB = AB - 1		' Subtract one from high word.
      ENDIF
    
    ' Now, we're down to minutes and there can be no more than 3599 seconds left
    ' so we can easily handle it with just the low word and normal math
    ooMM = CD / 60
    
    ' And finally we can get the seconds by getting the reminder.
    ooSS = CD // 60
    It's based on a piece of code I originally wrote for doing SNTP with W5100 chip. SNTP basically gives you the numner of seconds passed since 1900-01-01 00:00:00 so I stripped out the year, leapyear, and month stuff and adopted it to your variables. I have not verified the above to be working.

    /Henrik.
    Last edited by HenrikOlsson; - 25th January 2015 at 17:55.

  2. #2
    Join Date
    Oct 2009
    Location
    Utah, USA
    Posts
    427


    Did you find this post helpful? Yes | No

    Default Re: 32 bit seconds math (how do I include the upper 16 bits?)

    @TABSoft

    Sorry, I should have posted that info...

    Using PBP 3
    the PIC is 16F690 (but I also want to be able use the newer 16F1828)

    I had thought about using Long's but I've never used PBPL and what little info is in the manual is very vague.
    What does it take to use a Long?? and invoke PBPL?

    I haven't done much searching on PBPL yet. I'm going to give the code that Henrik (thanks!!) posted and see what it does for me.
    Dwight
    These PIC's are like intricate puzzles just waiting for one to discover their secrets and MASTER their capabilities.

  3. #3
    Join Date
    Oct 2009
    Location
    Utah, USA
    Posts
    427


    Did you find this post helpful? Yes | No

    Wink Re: 32 bit seconds math (how do I include the upper 16 bits?)

    Wow Henrik!!

    You write amazingly good "unverified" code!
    You did miss one "NEXT" down at the end.
    I had to change your ooHH, etc. variables to match mine (owHH)

    But it is working beautifully!

    Code:
    '=============================================================
    ' convert the one wire seconds count to HH:MM:SS
    '=============================================================
    HMS:
    
    owDays VAR WORD
    i VAR WORD
    Temp VAR WORD
    
    ' This uses DIV32 by preloading the system variable it uses with
    ' the value we want to divide. To find out how many days has passed
    ' we divide the number of seconds with 86400 (number of seconds in a day).
    ' However, DIV32 only supports 16 bit divisor so we need to do the
    ' division in two steps, 21600 * 4 = 86400.
    
    R0 = AB			' High word of seconds into system var
    R2 = CD			' Low word of seconds into system var
    owDays = DIV32 21600	' Divide by 86400
    owDays = owDays / 4
    
    ' Now we need to subtract 86400 seconds from the running time
    ' one time for each day that has passed. Since we're working with
    ' 16-bit words we need to this sort of "manually".
    ' 65536 + 20864 = 86400
    
    For i = 1 to owDays
      Temp = CD
      CD = CD - 20864
    
      IF Temp < CD THEN	' Did we underflow the low word?
        AB = AB - 1		' If so, decrement high word
      ENDIF
    
      AB = AB - 1            ' Subtract 65536 
    NEXT
    
    ' At this point ABCD contains anything from 0 to 86399 and we need to
    ' figure out how many "full hours" is in it. Because 86399 is more than
    ' what fits in a 16-bit word we're using DIV32 trick to divide the
    ' number of seconds by 3600 (number of seconds in an hour). 
    
    R0 = AB			' High word of what's left in seconds into system var
    R2 = CD			' Low word of what's left in seconds into system var
    owHH = DIV32 3600	' Divide by the number of seconds in an hour
    
    ' Subtract 3600 from the running time, one time for each hour.
    For i = 1 to owHH
      Temp = CD
      CD = CD - 3600
     
      IF Temp < CD THEN	' Did we underflow the low word?
        AB = AB - 1		' Subtract one from high word.
      ENDIF
    next
    ' Now, we're down to minutes and there can be no more than 3599 seconds left
    ' so we can easily handle it with just the low word and normal math
    owMM = CD / 60
    
    ' And finally we can get the seconds by getting the reminder.
    owSS = CD // 60
    
    return
    Thank you so much.
    I preloaded the clock with 23:59:00 and it rolled over to 00:00:00 as expected!

    Now to go digest and try and understand what you actually told my little micro-conntroller to do.

    after all that is what we are doing when we write code "telling these little pieces of silicone" how and when to do something

    thanks again!

    I actually have two clocks and a DS18b20 on my little breadboard...
    the I2C DS1307 and the onewire DS2417 (boy its a liittle guy) you can see it just below the LCD and on the PCB in the breadboard.

    You can see the time from the DS1307 (blue PCB) on the top line
    you can see the 4 bytes of seconds count from the DS2417 on the 3rd line
    on the bottom line is the result of you conversion code.

    Name:  rtc.jpg
Views: 17925
Size:  283.6 KB



    Thanks
    Dwight
    These PIC's are like intricate puzzles just waiting for one to discover their secrets and MASTER their capabilities.

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


    Did you find this post helpful? Yes | No

    Default Re: 32 bit seconds math (how do I include the upper 16 bits?)

    Hi Dwight,
    That's excellent news, glad I could help!
    Sorry for the missing NEXT, I should have at least compiled it before posting but I was really short on time.

    Regarding using LONGs. If you're using Microcode Studio as your IDE there's a checkbox in the View -> Compile and program options saying Use Compiler Long Words (18 series MCU Only). Tick that and you'll be compiling with LONGs enables. As the text says you have to use an 18F part so the 16F690 or 16F1828 will not work.

    /Henrik.

  5. #5
    Join Date
    Oct 2009
    Location
    Utah, USA
    Posts
    427


    Did you find this post helpful? Yes | No

    Default Re: 32 bit seconds math (how do I include the upper 16 bits?)

    Sorry for the missing NEXT, I should have at least compiled it before posting but I was really short on time.
    Henrik,
    Please don't feel bad. I could only hope to quickly analyze someone elses code/project and toss out such a quick and good answer.

    The value of forums like this is the willingness of folks like you, TABsoft and others to donate their time and talent to help others out.

    The fact that you missed a "next" is trivial. The fact that you could understand my need and share a possible solution is the key.

    I had looked at the DIV32 function in the PBP manual but could not understand how to apply it in my situation. The manual led me to believe that I first had to multiply two numbers. Which I did not need to do (or is that actually what one is doing by combining a LowWord and a HighWord??). ((don't think so... because FF x FF =FE01 not FFFF))

    I did not know that one could pre-load the system variables manually (no mention of that fact in the manual) or which system variables would be involved (R0, R2)
    Last edited by Heckler; - 27th January 2015 at 03:56.
    Dwight
    These PIC's are like intricate puzzles just waiting for one to discover their secrets and MASTER their capabilities.

  6. #6
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,627


    Did you find this post helpful? Yes | No

    Default Re: 32 bit seconds math (how do I include the upper 16 bits?)

    Hi Dwight,
    No, that sort of info is not in the manual. I don't remember from where I got it but I'd be surprised if it wasn't from one of Darrels post.

    Anyway, yes, normally what you do is multiply two 16bit number which results in a 32bit result then, imediately after, you execute the DIV32 which will divide that 32bit result by the 16bit divisor. The trick is to know that when you do the 16*16bit multiplication the result of that ends up in system variables R0 and R2. Once you know that there's nothing stopping you from manually loading those registers, the DIV32 function will not know the difference.

    If you have a total of 1,125,000 seconds in your ABCD variable, looking at that in hex: 0011 2A88 what goes in R0 is 11h (17) and what goes on R2 is 2A88 (10888).
    17*65536+10888=1,125,000.

    I think there might be a possible issue with the code as posted. When the number of seconds passed is less than 86400 (one day) then owDays will be 0 and the FOR-NEXT loop subtracting 86400 seconds per day will execute 65535 times. I think the result will still be correct since it'll just wrap around but it may take a fair bit of time..... You might want to enclose that FOR/NEXT loop in an IF/THEN block checking if owDays > 0.

    /Henrik.
    Last edited by HenrikOlsson; - 27th January 2015 at 06:31.

  7. #7
    Join Date
    Jan 2013
    Location
    Texas USA
    Posts
    229


    Did you find this post helpful? Yes | No

    Default Re: 32 bit seconds math (how do I include the upper 16 bits?)

    Henrik and Dwight.

    I am not an expert by any stretch of the immagination, but I think I have uncovered some potential issues when using DIV32 in this manner.

    After reviewing the algorithm provided by Henrik and the DIV32 command (much searching through the PBP manual, this forum, the assembler listing and using MPLAM Simulator), there are a few of rules regarding DIV32 that may impact the results from the algorithm.
    These rules (including the obscure Rule 3 below) may not impact your particular application, but we might need to be aware of the implications if I am right.


    Rules for using DIV32:
    Rule 1. The dividend must be a maximum 31-bit number.
    Rule 2. The divisor must be a maximum 15-bit number.
    Rule 3: The quotient (result) of a DIV32 operation, (minding Rule 1 & 2), must not be more than a 16-bit number.

    Here is where I derived these rules from.

    From the manual "DIV32 is actually limited to dividing a 31-bit unsigned integer (max 2147483647) by a 15-bit unsigned integer (max 32767).
    This should suffice in most circumstances."

    This drives two rules.

    Rule 1. The dividend must be a maximum 31-bit number.
    Rule 2. The divisor must be a maximum 15-bit number.

    From the forum LINK :
    Q: "Is there any way of telling if the DIV32 operation result is greater than 16 bits?"
    A: From Darrel Taylor: "If the result is too large, it will return 65535.
    Consequently, 65535 isn't a valid result, even if that's really the answer."

    This drives a new rule.

    Rule 3: The quotient (result) of a DIV32 operation (minding Rule 1 & 2) must not be more than a 16-bit number.


    With these Rules (1,2 & 3) plugged into your application with Henrik's algorithm, the implications as I see them are the following:
    1. You will have to discard the MSB (bit-31) of the total seconds from your OW RTC.
    This will initially limit the total number of seconds to 2,147,483,647($7FFF:FFFF) (24855 days, 3 hours, 14 minutes, 7 seconds).
    Now this is ~68 years!

    Note: Rule 2 is taken care of by Henrik in his 2-step division for owDays (owDays = DIV32 21600, owDays = owDays / 4)


    2. Because of Rule 3, the total number of seconds from the OW RTC you can use reliably is 1,415,577,599($545F:FFFF)
    (16383 days, 23 hours, 59 minutes, 59 seconds) this is ~45 years.
    This number when the "owDays = DIV32 21600" operation is performed, returns a quotient of 65,535($FFFF) (16 bits).
    This will be a valid result for the remaining operations in the algorithm.

    I ran a few tests using pre-loaded values for total seconds.
    The values I used were both <= $545F:FFFF and => $5460:0000.
    The values equal to or below the 16-bit quotient limit returned the correct answer.
    The values above the 16-bit quotient limit failed to return the correct answer.

    Now I suppose if your total number of seconds is above 1,415,577,599($545F:FFFF) AND your "owDays = DIV32 21600" result should actually return 65,535($FFFF), then your ultimate results for days, hours, minutes, seconds should be correct.

    Not that any of this might impact your particular application, but for other potential applications these rules may have significant impact.

    I guess there might be multiple ways to handle this restriction in your particular application.
    What I have come up with is a test within the "HMS:" subroutine that can then output an error value for HH:MM:SS.
    This needs to take care of limiting the total seconds from the OW RTC to a 31-bit number and limiting the total seconds equal to or below the 16-bit quotient for the DIV32 operation.
    1. Test the value of the AB word, if it is greater than "$545F" then shortcircut the algorithm, place decimal "99" for each of the owDays, owHH, OWMM and owSS.
    Display : 99 days, 99:99:99

    Here is a revised subroutine incorporating these rules. I also included Henrik's suggestion about owDays = 0 issue and I extrapolated that into the owHH as well.

    Code:
    '=============================================================
    ' convert the one wire seconds count to HH:MM:SS
    '=============================================================
    HMS:
    
    'owDays VAR WORD
    'i VAR WORD
    'Temp VAR WORD
    
    ' This uses DIV32 by preloading the system variable it uses with
    ' the value we want to divide. To find out how many days has passed
    ' we divide the number of seconds with 86400 (number of seconds in a day).
    ' However, DIV32 only supports 16 bit divisor so we need to do the
    ' division in two steps, 21600 * 4 = 86400.
    
    'Place the entire algorithm in an If/Then test to limit the total seconds to 
    'a 31-bit number and to check that the DIV32 will return a result <= 16-bits.
    
    if AB < $5460 then  ' Result of DIV32 will return a quotient that is 16-bit
                        ' and caps the total seconds to 31-bits.
    
        R0 = AB			' High word of seconds into system var
        R2 = CD			' Low word of seconds into system var
        owDays = DIV32 21600	' Divide by 86400
        owDays = owDays / 4
        
        ' Now we need to subtract 86400 seconds from the running time
        ' one time for each day that has passed. Since we're working with
        ' 16-bit words we need to this sort of "manually".
        ' 65536 + 20864 = 86400
        
        if owDays > 0 then  'As per Henrik, Speeds up the computation instead of wrapping around
            For i = 1 to owDays
              Temp = CD
              CD = CD - 20864
            
              IF Temp < CD THEN	' Did we underflow the low word?
                AB = AB - 1		' If so, decrement high word
              ENDIF
            
              AB = AB - 1            ' Subtract 65536 
            NEXT
        else
        
        endif
        
        ' At this point ABCD contains anything from 0 to 86399 and we need to
        ' figure out how many "full hours" is in it. Because 86399 is more than
        ' what fits in a 16-bit word we're using DIV32 trick to divide the
        ' number of seconds by 3600 (number of seconds in an hour). 
        
        R0 = AB			' High word of what's left in seconds into system var
        R2 = CD			' Low word of what's left in seconds into system var
        owHH = DIV32 3600	' Divide by the number of seconds in an hour
        
        ' Subtract 3600 from the running time, one time for each hour.
        if owHH > 0 then    'Also speeds up computation instead of wrapping around
            For i = 1 to owHH
              Temp = CD
              CD = CD - 3600
             
              IF Temp < CD THEN	' Did we underflow the low word?
                AB = AB - 1		' Subtract one from high word.
              ENDIF
            next
        endif
        ' Now, we're down to minutes and there can be no more than 3599 seconds left
        ' so we can easily handle it with just the low word and normal math
        owMM = CD / 60
        
        ' And finally we can get the seconds by getting the reminder.
        owSS = CD // 60
    
    else    'Result of DIV32 21600 will return 65535 because it is > 16-bit
            'or the total seconds was a 32-bit number.
            
        'Create an error value for output
        owDays = 99
          owHH = 99
          owMM = 99
          owSS = 99    
    endif
    
    return
    I hope this is correct and it helps.
    Regards,
    TABSoft

  8. #8
    Join Date
    Oct 2009
    Location
    Utah, USA
    Posts
    427


    Did you find this post helpful? Yes | No

    Default Re: 32 bit seconds math (how do I include the upper 16 bits?)

    TABSoft,

    As best I can tell, your concern for accuracy comes into play when the clock is allowed to count up to a (relatively) large number.

    While doing some testing on seconds counts in the 5 year or greater range, I found that Henrik's routine took over a second to complete (@ 4 MHz osc speed) so I added this bit of code...
    Code:
    IF owDays >=365 THEN
            GOSUB calcOWtime
            GOSUB setOWtime
            owDays=0
        ENDIF
    This way the 32 bit counter will not get to large (assuming the microcontroller is turned on at least every year or so. Then this next bit of code is executed. Which converts the HH:MM:SS back into a "seconds count" and programmed back into the RTC, thus clearing the upper portion of the 32 bit counter.

    Code:
    '= = = = = = = = = = = = = = = = = = = = = = = = = = = =
       ' Subroutine to calculate OW time bytes A,B,C,D
    '= = = = = = = = = = = = = = = = = = = = = = = = = = = =
    'this routine take the three values HH, MM, SS and converts them to a 
    ' 32 bit seconds count and loads the 4 Bytes A,B,C,D
    calcOWtime:
        AB= owHH ** 3600
        CD= owHH *  3600 + owMM*60 + owSS 
        ByteA=AB.byte1
        ByteB=AB.byte0
        ByteC=CD.byte1
        ByteD=CD.byte0
    return
    
    '= = = = = = = = = = = = = = = = = = = = = = = = = = = =
             ' Subroutine to write OW time
    '= = = = = = = = = = = = = = = = = = = = = = = = = = = =
    setOWtime:
       OWOUT OWTPin,%011,[$CC,$99,%10001100,ByteD,ByteC,ByteB,ByteA] 'set OW clock %10001100 OSC on
    return
    I realize that this little routine might introduce a few milliseconds error once every year or so to reset the counter to <365 Days.

    But, bear in mind that my project is a BOY SCOUT "Electronics" merit badge kit. The kit is powered by a CR2032 coin cell. It fills the final requirement to solder up a project.
    Over the past 3-4 years we have built over 400 of these little kits.

    I am just toying with the idea of allowing the kit to keep time.
    Currently here is what the kit does...

    * displays the Boy Scout Law
    * display current temperature
    * track and remember Low and High temperature (if left on,
    goes to sleep to save batt and wakes up every 60 sec to check temp)
    * play a little race car game
    * display several smiley faces and other icons

    I designed this kit to be useful and teach microcontrollers
    (which is what most hobby "electronics" today is centered around)
    Since I have to re-order more PC boards I thought I would add an RTC to the design.
    (I would pre solder the RTC and crystal as it is surface mount)
    the Scouts do amazingly well for such small soldering. I have an experienced solder coach sit across from them as they build the kit in about 30 min.

    What do you think...

    Name:  2015-01-30_162336.jpg
Views: 18114
Size:  74.6 KB
    Name:  2015-01-30_162231.jpg
Views: 18137
Size:  94.8 KB
    Name:  2015-01-30_162210.jpg
Views: 18503
Size:  123.2 KB
    Name:  2015-01-30_162152.jpg
Views: 18230
Size:  172.9 KB
    Dwight
    These PIC's are like intricate puzzles just waiting for one to discover their secrets and MASTER their capabilities.

Similar Threads

  1. 32 bit math
    By fnovau in forum General
    Replies: 4
    Last Post: - 12th February 2008, 23:55
  2. Averaging 16 bit values without using 32 bit math
    By sirvo in forum mel PIC BASIC Pro
    Replies: 2
    Last Post: - 5th October 2007, 22:18
  3. PIC18Fxx math 24 & 32 bit
    By ronsimpson in forum mel PIC BASIC Pro
    Replies: 8
    Last Post: - 2nd December 2006, 12:52
  4. 32 bit math
    By Charles Linquis in forum mel PIC BASIC Pro
    Replies: 12
    Last Post: - 28th August 2006, 13:34
  5. PBP 16-bit ADC result math
    By sonic in forum mel PIC BASIC Pro
    Replies: 0
    Last Post: - 13th March 2005, 14:21

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