PDA

View Full Version : UberNewbie can't set his clock...



Byte_Butcher
- 3rd January 2009, 01:36
Greetings all,

Well... another total PIC newbie here...

I've dabbled in electronics for a few decades and can generally navigate my way around hardware pretty well.
But I'm *totally* new to PIC's and programming in general, so please be gentle with me...

I'm attempting to build a project that requires an RTC with battery backup and an LCD display that displays (among other things).. the real time.
The problem I'm having is setting the clock.

I'm using a 16F690, a DS1302, and a standard 16x2 LCD (in 4-bit mode). Compiler is PICBASIC PRO and programmer is a PICkit2.
I've got the DS1302 and LCD up and running just fine. The clock keeps (and displays) time just fine. I also have no trouble detecting the set buttons.

The problem is SETTING the clock. I think I'm either math challenged and/or just generally clueless here.

The sample program I started with sets the initial time manually when you load the PIC, but what I need of course is to set it with buttons. That's where my troubles begin.

Here's some snippets of my code, so you've got an idea where I'm at: (just the relevant bits I think)



' Allocate variables
rtcyear var byte
rtcday var byte
rtcmonth var byte
rtcdate var byte
rtchr var byte
rtcmin var byte
rtcsec var byte
rtccontrol var byte

'**This is what I want to do away with and replace with buttons:

' Set initial time
rtcyear = $09 'Set Year
rtcday = $05 'Set Day
rtcmonth = $1 'Set Month
rtcdate = $1 'Set Date of month
rtchr = $21 'Set Hour
rtcmin = $03 'Set Minutes
rtcsec = $20 'Set Seconds
Gosub settime ' DO IT! - Set the time


'**This is the main goods:

' Subroutine to write time to RTC
settime:
RST = 1 ' Ready for transfer

Shiftout IO, SCLK, LSBFIRST, [$8e, 0] ' Enable write

RST = 0 ' Reset RTC

RST = 1 ' Ready for transfer

' Write all 8 RTC registers in burst mode
Shiftout IO, SCLK, LSBFIRST, [$be, rtcsec, rtcmin, rtchr, rtcdate, rtcmonth, rtcday, rtcyear, 0]

RST = 0 ' Reset RTC


Return


' Subroutine to read time from RTC
gettime:
RST = 1 ' Ready for transfer

' Read all 8 RTC registers in burst mode
Shiftout IO, SCLK, LSBFIRST, [$bf]
Shiftin IO, SCLK, LSBPRE, [rtcsec, rtcmin, rtchr, rtcdate, rtcmonth, rtcday, rtcyear, rtccontrol]

RST = 0 ' Reset RTC
Return

' Main program loop - updates the LCD with the time
mainloop:
Gosub gettime ' Read the time from the RTC

' Display time on LCD
Lcdout $fe, 1, $fe, 2, hex2 rtchr, ":", Hex2 rtcmin, ":", Hex2 rtcsec,_
$fe,$C0, " ", Hex2 rtcmonth, "/", Hex2 rtcdate, "/" , Hex2 rtcyear


Pause 100 ' slow things down a bit to keep the LCD from freaking out

gosub checkbuttons

Goto mainloop ' repeat until nauseated...


When a button is detected I call a subroutine that's supposed to increment the minutes (or hours) to set the clock. This is where I'm tOO sTOoPid:



increminute:
rtcmin= rtcmin + 1
if rtcmin >= 60 then
rtcmin = 0
endif

gosub settime

return

---------

Even that part works... to a degree. It does set the time on the clock. THE PROBLEM is that it sets the minutes on the LCD in HEX!

if I start at 03 minutes (as shown on the LCD) and increment with the set button, it works fine until I get past 09... then it goes to 0A, OB, OE, etc. instead of continuing at 10, 11, 12, etc.
Clearly, this will not do!

Can someone please help me out of the darkness here? Or at least give me a shove in the right direction? I've searched about a million websites and forum posts, but.... :(

Thanks very much,
Steve

sinoteq
- 3rd January 2009, 02:17
Hi
You have made this problem yourself and you will soon see it but do not worry we have all made something similar.

First,
In Set initial time you load the variables, this is good. But you load them in HEX form, that is bad. Example "rtchr = $21" will not load 21 in hours but 33. That are way too many hours for an earth clock:) "rtchr = 21 " will put 21 in hours so delete all the $ signs to use decimal numbers for the init.

Secondly,
Main loop Do not use the LCD command with HEX2 because this will make PBP to use HEX on the display and that is you main problem. Change the HEX2 to DEC2 and you will see a difference.

Good luck.

Byte_Butcher
- 3rd January 2009, 03:28
Thank you sinoteq !

I tried the code without the $ in front of the variables, and using DEC2 instead of HEX2 in the LCDOUT command as you suggested, and now the button to set the time works fine. BUT, the time no longer displays correctly.

Now it skips certain numbers (groups of 6 it seems) as it goes from 0 to 60, and it counts to 90 instead of 60 before it increments the minute and resets seconds to 0...

So now the time "increment minutes" button goes from 0 to 60 then resets as is desired, but the time displayed from the RTC goes to 90 seconds and skips numbers.

I'm open for more suggestions!

Thanks,
Steve

sinoteq
- 3rd January 2009, 03:48
If you look at the datasheet for the DS you will se that you have to do some formating of the DS result before you can display it. The DS does not store each SEC MIN HOUR and so on as a byte, it is stored in a mixed byte (I bet Melanie knows the correct word for it)

Example:
The register for seconds is 8 bits wide but is split into a 10 SEC part and a SEC part. So you have to look at the DS data as (n*10SEC)+SEC.
You have to move this into a byte before you can display it. I have never used DS1302 but I have used the DS1307 and the idea is similar.

Please look at http://pdfserv.maxim-ic.com/en/ds/DS1302.pdf Table 3 page 9

Melanie
- 3rd January 2009, 15:46
(I bet Melanie knows the correct word for it)

Melanie has all kinds of words for all kinds of situations... but in this instance I'll chose to use BCD (Binary Coded Decimal)...

Byte_Butcher
- 3rd January 2009, 19:23
Thanks folks,

Yes, BCD is what it is I guess. And that explains why it works to use HEX2 to send to the LCD and why DEC2 doesn't work right.

It also explains why when manually loading the time..


rtcmin = $11 'Set Minutes

..it works to say the numbers are HEX when you're really entering DEC info. For numbers from 0 to 9 it works, AND it puts the digits in the right place for BCD.

If I want to set the minutes to 11, it works to say "rtcmin = $11"
But if I try to enter it as "11" with out the $..

rtcmin = 11 'Set Minutes

..when it loads into the clock as BCD, it ends up as "1A" on the display.

I can picture what's going on (although I don't think I've put it in words well)
but I don't know how to solve the problem...

After I increment my variable "rtcmin" by 1, is there a way to "fool" things into thinking it's hex again? (without actually to converting hex. I want "12 = $12", not "12 = 0C")

Am I totally lost?

Melanie
- 3rd January 2009, 21:51
Can you figure the code behind this?...



BCDByte=DecimalByte DIG 1
BCDByte=BCDByte<<4
BCDByte=BCDByte+DecimalByte DIG 0


When DecimalByte=12, then BCDByte=$12

or put another way...



BCDByte=((DecimalByte DIG 1)<<4)+DecimalByte DIG 0

Byte_Butcher
- 4th January 2009, 04:35
Ahhh, thank you Melanie!
A first glance tells me that is exactly what I'm looking for !

I think the Shift Left Operator (<<) may be just what I'm after!

I got sidetracked today...

GOSUB DoWifeChores
DoWifeChores:
finish building new kitchen cabinet

RETURN


Tomorrow morning I'll see what I can do with that left shift operator and I'll report back.
Thanks very much!

Steve

Archangel
- 4th January 2009, 07:25
Ha Ha Ha ! :D Sorry, but it just struck me as funny . . .


If DoWifeChores = 1 THEN
goto WifeHappy
ELSE
IF DoWifeChores = 0 THEN
GOTO OnMyOWN
endif

OnMyOwn:
Cook
Laundry
Sleep Cold ' In all it's many possible meanings
Look for apartment

WifeHappy:
Wife cooks
Does Laundry
Keeps you warm
Let's you stay
You have time to PIC :D



finish building new kitchen cabinet
Revenge for all those New Yankee Workshop videos she had to endure

Byte_Butcher
- 5th January 2009, 22:50
Thanks to everyone that responded!

Melanie, I still couldn't that snippet of code you supplied to work right. Using Dig 0 and Dig 1 to pick the 1's and 10's digits out of the BCD didn't give the desired results...

What finally DID work for me was to split the minutes data apart using bitwise AND (&) like so:



BCDByteH = rtcmin & %11110000 'get the high nibble
BCDByteL = rtcmin & %00001111 'get the low nibble

That got my upper and lower 4 bits separated out OK so I can increment/decrement them with the pushbuttons.
When I'm done I put them back together into rtcmin with bitwise OR (|)



rtcmin = BCDByteH | BCDByteL

gosub settime

That seems to be working so far. More testing still to make sure...

Anyway, thanks to all!

(Oh, and Joe S., you MUST be a married man. You seem to know the code for it pretty well. :D

Steve

Archangel
- 6th January 2009, 00:42
(Oh, and Joe S., you MUST be a married man. You seem to know the code for it pretty well. :D

SteveHE HE HE, YOU GOT IT ! I got a list of honey do's longer than a road across Texas, AND I got another list of Sonny Do's from my mother. Keeps me outta trouble. :D

Melanie
- 6th January 2009, 09:10
Just for the record, the examples I posted are valid, tried and tested...

Not knowing how you are manipulating your code and Buttons behind the scenes doesn't help... but, if your Seconds Button for example incremented the Seconds every time you pressed or held it...



While SecondsButton=0
Seconds=Seconds+1
If Seconds>59 then Seconds=0
Pause 250
Wend


That will give you a variable 'Seconds' which will contain a (Decimal) value in the range 0-59.

Running it through either if the routines I posted previously you get...

DecimalByte Decimal (HEX) = BCDByte Decimal (HEX)

0-9 ($00-$09) = 0-9 ($00-$09)
10-19 ($0A-$13) = 16-25 ($10-$19)
20-29 ($14-$1D) = 32-41 ($20-$29)
30-39 ($1E-$27) = 48-57 ($30-$39)
40-49 ($28-$31) = 64-73 ($40-$49)
50-59 ($32-$3B) = 80-89 ($50-$59)

...which then can be used for the Seconds Register for the timer chip.

Now those routines are only valid for the range 0-99 giving a BCD of $00-$99, which is fine if you are setting clocks as you're only interested in 0-59 worst-case anyway.

The way you've implimented things seems kinda hard work, if individually you are incrementing units seperately from the tens.



Melanie, I still couldn't that snippet of code you supplied to work right. Using Dig 0 and Dig 1 to pick the 1's and 10's digits out of the BCD didn't give the desired results...

Of course it didn't... that routine pulls the units and tens digits out a DECIMAL number to construct (encode) the BCD - not to decode the BCD... you weren't asking for that in your original post! To construct DECIMAL from BCD, you can do something simple like...


DecimalByte=(BCDByte>>4)*10+(BCDByte & $0F)

sinoteq
- 6th January 2009, 13:37
Melanie and her "one line wonders" of code made me curious. In what order does PBP actually calculate and do things?
Let's use her latest code as an example:


DecimalByte=(BCDByte>>4)*10+(BCDByte & $0F)

Obviously the BCDByte is shifted right by 4 and the top 4 bits are made to 0. After the >>4 the data in BCDByte is "broken" and only the 4 low bits are valid.

Will
DecimalByte=(BCDByte & $0F)+((BCDByte>>4)*10) give the same results?

When does the shift command actually change the data in BCDByte?

mackrackit
- 6th January 2009, 13:58
When does the shift command actually change the data in BCDByte?
It is my understanding that the shift command in a case like this will not change the data.
It only tells what part of the value to use in the calculation.

Melanie
- 6th January 2009, 18:35
OK, lets break this down...

DecimalByte=(BCDByte>>4)*10+(BCDByte & $0F)

The calculations in the brackets will be perfomed first, and the results stored in a PICBasic variable behind the scenes. BCDByte is unchanged - just as well because we refer to it twice, once to access the top four bits, and once to access the bottom four bits.

Thereafter, PBP follows mathematical rules of priority... ie Multiplication and Division are performed before addition ans subtraction. Because items in brackets are always calculated from the centre out, and each set of brackets is it's own self-contained world, the result of the formula below, will be the same as that above.

DecimalByte=(BCDByte & $0F)+((BCDByte>>4)*10)

However, you do have an extra set of brackets there... but PBP actually resolved the formula without any additional penalty. Might not be so lucky on another occasion.

Byte_Butcher
- 6th January 2009, 22:57
Thanks again Melanie,
Hmmm, I think I almost understand what's going on with that code. I must actually be learning something. :)

I ended up with this, from the code you supplied and it works excellent.



increminute:

rtcmathtemp =(rtcmin>>4)*10+(rtcmin & $0F) 'convert BCD minutes into Decimal minutes

rtcmathtemp=rtcmathtemp+1 'increment by 1
If rtcmathtemp>59 then 'If minutes exceeds 59
rtcmathtemp=0 'reset to 0
endif

rtcmin=((rtcmathtemp DIG 1)<<4)+rtcmathtemp DIG 0 'convert Decimal back to BCD

gosub settime

return

Byte_Butcher
- 7th January 2009, 01:38
Sigh, well I seem to have a new problem. Although I'm obviously able to read/write to the DS1302 in "burst" mode, I seem to be having a problem addressing individual registers.

For instance, I need to turn the trickle charger on...
I *thought* it was as simple this:

rst = 1

'Set Battery charger on and set to "1 diode, 2Kohm"
Shiftout IO, SCLK, LSBFIRST, [$90, $A5] ' Set charger [$90, $A5]

RST = 0

But that doesn't seem to do it for me. :(

Anyone got the patience to help me along a little further....

p-p-p-please?

Thanks!

Steve

Archangel
- 7th January 2009, 02:57
Hi Steve, check uot pages5/ 6 of this data sheet, it says: DS 1302 powers up with trickle charger disabled and only a pattern of 1010 will enable charger. http://forums.parallax.com/forums/attach.aspx?a=26594 HTH

Byte_Butcher
- 7th January 2009, 03:30
Thanks Joe,
You can't imagine how many times I've read the data sheet.... (it's been open on my desktop for days now)
I'm clearly not quite understanding yet. :confused:

Yes, a pattern of 1010 turns on the charger, and the other 4 bits of the byte set the charge rate.

So what ( I think) I want to send is 10100101 which should tun on the charger and set it to "1 diode, 2Kohm" operation. In HEX, that's $A5, right?

So I've sent $90, which should be the register address for the charger, and $A5, which should be the code to turn it on and set it to my chosen charge mode. I even tried sending the data in binary, like so:

rst = 1

'Set Battery charger on and set to "1 diode, 2Kohm"
Shiftout IO, SCLK, LSBFIRST, [$90, %10100101] ' Set charger [$90, $A5]

RST = 0
but with the same (no) result.

What am I doing wrong? (please!)

Thanks,
Steve

Byte_Butcher
- 7th January 2009, 04:22
Ahh, never mind, I figured it out...

Setting up the trickle charger is the first task task I ask of the DS1302, and I just needed to pull the reset line LOW first, before I yank it high and move the data.
All I was missing was "rst = 0" up front:



rst = 0
rst = 1

'Set Battery charger on and set to "1 diode, 2Kohm"
Shiftout IO, SCLK, LSBFIRST, [$90, %10100101] ' Set charger [$90, $A5]

RST = 0

Thanks again to everyone!

Steve