PDA

View Full Version : Clock question



lennyG
- 28th October 2015, 17:42
I have a question about an RTC clock. Before everyone jumps on and says there is lots of code please read my whole rant, er question. So, I'm designing an incubator that needs to log temperature and time. Simple, lots of applications have the same requirements, I've read lots of text about them. Lots of code on how to do a clock. However, I have never seen complete clock code. By that I mean, I can program the time in at code time but how do you change the time and date? Eventually you will need to set it. Either because of a power outage or maybe a time change. I started writing the code and two things became obvious. First, it took a lot of code to cover it all and second how to display the menu and choices was really subjective. I've done several projects where when it got to alpha testing someone always asks who coded this? What is obvious to me is less than clear to a user who has no idea what the buttons and lights mean. The main display is just the temp on an LED due to to the ability to read the display easier in a dim environment so do you add an LCD just to set the time? Add individual LED's for some functions? This involves a whole realm of user interface that I'm just not that good at. And please, I'm not looking for some answer like put in lights or use buttons, but rather concrete complete answers. I know this is a rather burdensome request but I'm hoping for a few ideas in which I get possibly a new direction to go. Currently I have an LCD, two line, 16 character that brings up date and time by button pushes. It's also used to display time/temp anomalies. One button to activate the menu, one button for up and one for down and another button for selection which advances to the next screen. When you get to the end of the choices it writes the date and time to the clock. The menu starts with the day, then month then year, then minutes then hour. Current value on top line and when you push a button the new value in written on the bottom line. I can post the code but as I said it is very lengthy and I'm not sure that really is productive in this situation. Anyone written a complete solution for a clock? Code isn't really as important to me as how the selections were made, what kind of display, buttons, switches, how was selection made. Is it really this simple or am I way off base?

Heckler
- 28th October 2015, 19:58
lennyG,

I think this is a very difficult question you have asked.
I have never studied programming or programming language, at least not in a formal classroom. Yet I have spent countless hours (hundreds of hours) blinking an LED, studying code snippets, writing to an LCD, trying different methods to detect a pushbutton, learning to use a subroutine, learning interrupts, studying more code examples, etc. etc.

I am just an electronics and microcontroller enthusiast. Yet I have done just what you describe.
For me it was really a process of building blocks... I started by learning how to read the time from an RTC module. Then I displayed that time on a given display (LCD or LED matrix, etc)

Then I learned how to set that time in the RTC. First as you said, from the program each time I loaded the code into the micro.
Then later by detecting a push button to cause the code to go to a separate "set time" routine.

Now this is what I think you are asking. How do I jump into this set time routine??
Well one way is for your main code to watch for a given button to be pressed. This is done periodically in your main code. When you want to set or adjust the time you would press this button and then the code jumps to the set time routine. Then in that routine it allows you to up/down the given value HH:MM:SS. You will want to be able to display that new value on your display so you can see when you get to the right number. Then you will want a different button (or a time out period) to automatically tell the micro to advance to the next thing to be adjusted HH>>MM>>SS (But usually you would skip SS and have the code set SS to zero).

I believe this is the heart of programming. You have to be able to DREAM up a way to literally direct the microcontroller to do what you want it to do.

There is no correct way to set the time. That is for you to imagine, code, test, debug, etc.
There are certainly good programming practices. There is elegant code and ugly code. Easy to understand and "what the hell was this guy thinking" kind of code, even though the ugly code may work correctly and never fail.

The set time routine must match and work with the rest of your code. The number of buttons required is dependent on your ability to dream up ways to signal different conditions to the micro. ie. short press, long press, double press within a time period, holding two buttons at once, only looking for a button press on powerup, etc, etc.

How do you tell someone to get to your house??? would someone else in that house use the EXACT same directions??? yet those two different set of directions would still result in arriving at your house. Although one may take longer, or drive a longer distance, etc.

To me (an untrained programmer) that is the beauty and the challenge of writing code.

Sorry for the non answer. But if someone provided you with an example it would likely take a considerable amount of effort to understand what and how they accomplished the given task. Would not likely work with your existing hardware. And they no-doubt did not write the code in one sitting. It likely evolved over several steps and testing phases.

If you are new at programming (guessing by your number of posts) then just start by doing simple things and adding bit by bit (no pun intended) to your code to make it do something else or blink an led faster/slower based on button press or up/down, etc. etc.

ask detailed questions, provide your coding examples when you ask, and you will get help here, world class programmers are lurking here and are very willing to help.

Archangel
- 28th October 2015, 20:07
when it got to alpha testing someone always asks who coded this? What is obvious to me is less than clear to a user
who has no idea what the buttons and lights mean . . .

which is 1/2 the reason for well commented code, additionaly if you put
functions in a group it is helpful. The other 1/2 reason is a year from now
You will not remember either.



Anyone written a complete solution for a clock?


I have not seen one.

I suggest you look at some of "Scampy's" work as he builds reptile incubators.
Melanie showed how to do menus.

You should post code, it helps generate interest among the helpful . . . Heckler is one of those, I see He's already on it . . .
And, Always, Always post your code as a Yourfilename.TXT file attachment or put in
code tags

http://www.picbasic.co.uk/forum/misc.php?do=bbcode#code

Very little "FREE FISH HERE", but lots of code strugglers, who will walk with you.

Scampy
- 2nd November 2015, 02:05
Here is an example taken from one of my projects. I don't confess to writing the tightest of code, but for the reasons mentioned above, it helps when or if I need to update the code, or use it for something else a year later !

This uses a DS1307 RTC chip. There are stacks of diagrams on the net that show you haw to wire one up to a PIC micro so I won't bother you with those details

First define some variables



;----[Variables/Aliases - Time]-------------------------------------------------


RTCSec var byte ' Seconds
RTCMin var byte ' Minutes
RTCHour var byte ' Hours
RTCWDay var byte ' Weekday
RTCDay var byte ' Day
RTCMonth var byte ' Months
RTCYear var byte ' Year
RTCCtrl var byte ' Control
SetTime var byte ' 12/24 Hour Clock
SetSec var byte ' Seconds
SetMin var byte ' Minutes
SetHour var byte ' Hours

TimeH var byte ' Variable to store current hour for comparison to drop temp time
TimeM var Byte ' Variable to store current minutes for comparison to drop temp time


Set some values for the basic hours and minutes and write it to the chip (14:00 in this case)



RTCSec=0
RTCMin=0
RTCHour=14

I2CWrite SDApin,SCLpin,$D0,$00,[RTCSec,RTCMin,RTCHour,RTCWDay,RTCDay,RTCMonth,RTCY ear,RTCCtrl]


Now to set up the LCD so the time is displayed. This example uses a 2 x 16 LCD, and its connected to port B of the micro



DEFINE LCD_DREG PORTB ' LCD Data port
DEFINE LCD_DBIT 0 ' starting Data bit (0 or 4)
DEFINE LCD_EREG PORTB ' LCD Enable port
DEFINE LCD_EBIT 5 ' Enable bit (on EasyPIC 5 LCD)
DEFINE LCD_RSREG PORTB ' LCD Register Select port
DEFINE LCD_RSBIT 4 ' Register Select bit (on EasyPIC 5 LCD)
DEFINE LCD_BITS 4 ' LCD bus size (4 or 8 bits)
DEFINE LCD_LINES 2 ' number of lines on LCD
DEFINE LCD_COMMANDUS 2000 ' Command delay time in us
DEFINE LCD_DATAUS 50 ' Data delay time in us

LCDOUT $FE,1:FLAGS=0:PAUSE 250:LCDOUT $FE,1:PAUSE 250 ' Initialize LCD



Where possible I've used self explanitary variable, ie RTCHour etc.



I2CRead SDApin,SCLpin,$D0,$00,[RTCSec,RTCMin,RTCHour,RTCWDay,RTCDay,RTCMonth,RTCY ear,RTCCtrl] ; read DS1307 chip

If RTCHour.6=1 then
CounterA=(RTCHour>>4)&$01 ' Work-Out 12 or 24 hour Display for Hours
else
CounterA=(RTCHour>>4)&$03
endif

CounterA=CounterA*10+(RTCHour&$0F) ' Display Hours appropriately for 12 or 24 hour Mode
timeH=(RTCHour>>4) 'convert the BCD format of the hours register and store in variable timeH
timeH=(timeH &$03)*10
timeH=timeH+(RTCHour&$0F)

timeM=(RTCMin>>4)
timeM=(timeM &$07)*10
timeM=timeM+(RTCMin&$0F) 'convert the BCD format of the mins register and store in variable timeM

If RTCHour.6=1 then
LCDOut $FE,$C0+11,#CounterA
else
LCDOut $FE,$C0+11,#CounterA Dig 1,#CounterA Dig 0
endif
LCDOut ":",#(RTCMin>>4)&$0F,#RTCMin&$0F


To set the time to the current time rather than the preset used in the variables you need some form of menu, and three buttons, one to cycle through the hours, second to cycle through the minutes, and the 3rd to store the settings in the variables.



time:

LCDOUT $FE,1
LCDOUT $FE,2,"Set Time"

inp3:

IF H_butt = 0 THEN GOSUB IncHours
IF M_butt = 0 THEN GOSUB IncMinutes
SetHour=Hours
SetMin=Minutes

LCDOUT $FE,$C0,#SetHour DIG 1,#SetHour DIG 0,":",#SetMin DIG 1,#SetMin DIG 0
pause 200
If S_butt = 0 then
pause 250
goto savetime
endif
goto inp3


savetime:
' Save 12/24 Hours to BCD DS1307's Format
' ---------------------------------------
CounterA=SetHour
If SetTime=1 then
If CounterA>12 then CounterA=CounterA-12
If CounterA=0 then CounterA=12
endif
Gosub ConvertBCD
RTCHour=CounterB
' Save the Hours Value
If SetTime=1 then
RTCHour.6=1
' Save the 12 Hour Mode Flag
If SetHour=>12 then RTCHour.5=1
' Save the 'PM' Flag
endif
'
' Save Minutes
' ------------
CounterA=SetMin
Gosub ConvertBCD
RTCMin=CounterB

CounterA=SetSec
Gosub ConvertBCD
RTCSec=CounterB

I2CWrite SDApin,SCLpin,$D0,$00,[RTCSec,RTCMin,RTCHour,RTCWDay,RTCDay,RTCMonth,RTCY ear,RTCCtrl]

IncHours:
Hours = Hours + 1
IF Hours = 24 THEN Hours = 0

pause 250
RETURN

IncMinutes:
Minutes = Minutes + 1
IF Minutes = 60 THEN Minutes = 0

pause 250
RETURN


This will get you as far as setting and displaying the hours and minutes on an LCD. As far as logging the time and other data for an incubator, I'll let you figure that one out... I've been building vivarium controllers and thermostats with the help of these nice guys on the forum for some years now, and a lot of the projects have been discussed and documented in this forum. So I'll let you do some research and look forward to seeing your code in the future, but in the meantime I hope this helps

Amoque
- 2nd November 2015, 23:18
Below is my clock code. I wrote it a couple years ago and never looked at it again, but as I recall it worked and, as it says in the comments, it works with 3 buttons to set the date/ time.

When reading from the RTC, I read the clock registers and decode them into decimal format in the REG_VAL array - element order matches the clock register order. Writing new values to the clock is accomplished the same way, but in reverse - load the decimal value into the REG_VAL array and then loop through all 7 registers converting them to BCD and writing them back to the clock registers. Let's say I want to advance the hour by one for Daylight Savings Time. A quick example:

Read clock registers [0..7], decode to decimal and write to REG_VAL[0..7]. Add one to REG_VAL[2] (the hour register). In a loop convert each register back to BCD and write it back to the RTC.

In addition, two other arrays are defined - one the MINIMIUM value allowed in each register (0 mostly, but for year its the current year and for month its one) and the other is the MAXIMUM value allowed in each register (59 mostly, but for month its 12)

To set the clock, BUTTON 1 is used to scroll through the registers (0 - 7), BUTTON 2 advances the value of the register to the MAX value before rolling over to MIN value and BUTTON 3 writes the register value to the clock and resets the register count.

-- as I recall




'************************************************* ***************
'* Name : 3 BTN CLOCK.BAS *
'* Author : [AMOQUE] *
'* Notice : Copyright (c) 2013 [PBP 2.6] *
'* : All Rights Reserved *
'* Date : 10/21/2013 *
'* Version : 1.0 *
'* Notes : 16F88, DS1337, 3 BUTTONS - ACTIVE HIGH *
'* : *
'************************************************* ***************

#CONFIG
__config _CONFIG1, _INTRC_IO & _WDT_OFF & _PWRTE_OFF & _MCLR_ON & _BODEN_OFF & _LVP_OFF & _CPD_OFF & _WRT_PROTECT_OFF & _DEBUG_OFF
__config _CONFIG2, _FCMEN_OFF & _IESO_OFF
#ENDCONFIG
DEFINE OSC 8

'-----DEBUG (SERIAL) SETUP----------------------------------------
DEFINE DEBUG_REG PORTB
DEFINE DEBUG_BIT 5
DEFINE DEBUG_BAUD 2400
DEFINE DEBUG_MODE 0

'-----CHIP REGISTER SET-UP----------------------------------------
TRISA = %00000000 ' Set PORTA output
TRISB = %00001101
OSCCON = %01110000
CCP1CON = %00001100
T2CON = %00000101
'ADCON0 = %11000000 ' Configure and turn on A/D Module
'ADCON1 = %10000000 ' Set PORTA analog and LEFT justify result
'ANSEL = %00000011

'-----PORT SET-UP-------------------------------------------------
SDA VAR PORTB.1
SCL VAR PORTB.4
BTN1 VAR PORTB.0
BTN2 VAR PORTB.2
BTN3 VAR PORTB.3

'-----CLOCK SET-UP------------------------------------------------
RTC_ADD CON %11010000
REG_TIM CON $00
REG_AL1 CON $07 ' Not implimented
REG_AL2 CON $0B ' Not implimented
REG_SPC con $0E
REG_STS CON $0F
'STP_ADD var byte
STP_add con %01000000

'-----TIME/ CLOCK VARIABLES----------------------------------------
REG_MIN VAR BYTE[7] ' Minimum register values
REG_VAL VAR Byte[7] ' Seconds, minutes, hours, day of week, date, month, year
REG_MAX VAR BYTE[7] ' Maximum register values
ACT_REG VAR BYTE ' Active register (setting)

'-----GENERAL USE VARIABLES---------------------------------------
LP VAR BYTE ' For/ Next value
D0 VAR BYTE
D1 VAR BYTE
D2 VAR BYTE

'-----INITIALIZE CLOCK--------------------------------------------
'CHECK STATUS
debug "---STABALIZE---", 10
pause 250
D0 = $00 : D1 = $00
gosub SPCL_READ
D0 = $00 : D1 = $00 ' Assign 'special' registers
gosub SPCL_WRITE ' Write 'special' registers
gosub SPCL_READ ' Verify

'SET INITIAL TIME
FOR lp = 0 TO 6 ' For each register
REG_VAL[LP] = LP ' Set value to address
NEXT LP ' Loop
'----------------------' Alarm set-up
GOSUB CLK_WRITE ' Write initial values and start clock

'LOAD REGISTER MIN/ MAX VALUES
REG_MIN[0] = 00: REG_MIN[1] = 00: REG_MIN[2] = 00: REG_MIN[3] = 1: REG_MIN[4] = 01: REG_MIN[5] = 01: REG_MIN[6] = 00
REG_MAX[0] = 59: REG_MAX[1] = 59: REG_MAX[2] = 23: REG_MAX[3] = 6: REG_MAX[4] = 31: REG_MAX[5] = 12: REG_MAX[6] = 99

D0=4
goto Stepper_Control


'+++++MAIN LOOP++++++++++++++++++++++++++++++++++++++++++++++ +++++
MAIN:
if BTN1 then goto CLK_SET ' Button 1 = Enter set mode
GOSUB CLK_READ: ' Read clock
PAUSE 500 ' Pause
GOTO MAIN ' Loop til the end of time (lol)



'-----SUB-ROUTINES------------------------------------------------
'READ TIME, CONVERT TO DECIMAL VALUES, DISPLAY
CLK_READ:
I2CRead SDA, SCL, RTC_ADD, REG_TIM, [REG_VAL[0], REG_VAL[1], REG_VAL[2], REG_VAL[3], REG_VAL[4], REG_VAL[5], REG_VAL[6]]
FOR LP = 0 TO 6 ' For each register
D0 = (REG_VAL[LP] >> 4) * 10 ' Decode 10's digit
D1 = REG_VAL[LP] & %0001111 ' Blank 10's, get 1's
REG_VAL[LP] = D0 + D1 ' Write Decimal value to register
NEXT LP ' Next register
DEBUG "TIME:[", dec1 REG_VAL[3], "] ", dec2 REG_VAL[5], "/", dec2 REG_VAL[4], "/", dec2 REG_VAL[6], " ", dec2 REG_VAL[2], ":", dec2 REG_VAL[1], ":", dec2 REG_VAL[0], 10
Return

'CONVERT TO BCD VALUES, WRITE TO CLOCK REGISTERS
CLK_WRITE:
FOR LP = 0 TO 6 ' For each register
D0 = REG_VAL[LP] / 10 ' Get decimal 10's digit
D1 = REG_VAL[LP] // 10 ' Get decimal 1's
REG_VAL[LP] = (D0 << 4) + d1 ' Shift BCD 10's in, add 1's
NEXT LP ' Next register
I2CWrite SDA, SCL, RTC_ADD, REG_TIM, [REG_VAL[0], REG_VAL[1], REG_VAL[2], REG_VAL[3], REG_VAL[4], REG_VAL[5], REG_VAL[6]]
GOSUB CLK_READ ' Update display
Return

'SPECIAL REGISTERS--------------------------------------------------------------
SPCL_WRITE:
I2CWrite SDA, SCL, RTC_ADD, REG_SPC, [D0, D1]
Return

SPCL_READ:
I2CRead SDA, SCL, RTC_ADD, REG_SPC, [D0, D1]
debug "SPCL: [", bin8 D0, "] STATUS: [", BIN8 D1, "]", 10, 10
pause 1000
Return

'BEGIN SET ROUTINE--------------------------------------------------------------
CLK_SET:
debug "SET TIME", 10 ' Announce in debug window
For LP = 0 to 100 ' Start timer loop
IF BTN2 Then Goto SELECT_REG ' Button 2 = select register
Pause 100 ' Advance time
NEXT LP ' Loop
GOTO MAIN ' Time expired, drop out

'SELECT REGISTER
SELECT_REG:
debug "SELECT REGISTER" , 10
ACT_REG = 0 : D2 = 0 ' Reset active register
For LP = 0 to 100 ' Start timer loop
IF BTN2 Then ' Button 2 press
ACT_REG = ACT_REG + 1 ' Advance active register
LP = 0 ' Restart time loop
IF ACT_REG > 6 THEN ACT_REG = 0 ' Keep register number in bounds
DEBUG "EDIT=", DEC1 ACT_REG, " MAX=" ' Announce register and max value to debug
debug DEC2 REG_MAX[ACT_REG], 10
Endif
If BTN3 Then ' Button 3 press
debug "SET REGISTER VALUE" , 10 ' Announce to debug
LP = 0 ' Restart time loop
Goto ADV_VALUE ' Goto advanve register value routine
Endif ' End button 3 loop
Pause 100 ' Advance time
NEXT LP ' Loop
GOTO MAIN ' Time expired, drop out

'ADVANCE REGISTER VALUE
ADV_VALUE:
debug "ADVANCE REGISTER VALUE" , 10
for LP = 0 to 100 ' Announce to debug
IF Btn3 Then ' Button 3 press
LP = 0 ' Restart time loop
D2 = D2 + 1 ' Advance register value
IF D2 > REG_MAX[ACT_REG] THEN D2 = REG_MIN[ACT_REG] ' Keep register value in bounds
debug "REG=", DEC2 ACT_REG, " NEW VALUE=", DEC2 D2 , 10 ' Announce to debug
ENDIF ' End button 3 press
IF BTN1 Then ' Button 1 press
debug "WRITE VALUE TO CLOCK" , 10 ' Announce to debug
LP = 0 ' Restart timer
GOSUB CLK_READ ' Read fresh clock values
REG_VAL[ACT_REG] = D2 ' Insert new value
GOSUB CLK_WRITE ' Write to chip
GOTO SELECT_REG ' Goto 'select register'
EndIF ' End button 1 press
Pause 100 ' Advance timer
Next LP ' Loop
GOTO MAIN ' Time expired, drop out

Stepper_Control:


DEBUG "DO=",DEC2 D0,10
for LP = 1 to 100
'---------------------------------2 --1
I2CWrite SDA, SCL, STP_ADD, [%00000010] : pause D0 '1 on 2 off
'I2CWrite SDA, SCL, STP_ADD, [%00001010] : pause D0 '1 on 2 on
I2CWrite SDA, SCL, STP_ADD, [%00001000] : pause D0 '2 on 1 off
'I2CWrite SDA, SCL, STP_ADD, [%00001001] : pause D0 '2 on 1 onr
I2CWrite SDA, SCL, STP_ADD, [%00000010] : pause D0 '1 onr
'I2CWrite SDA, SCL, STP_ADD, [%00000001] : pause D0 '2 onr 1 onr
I2CWrite SDA, SCL, STP_ADD, [%00000101] : pause D0 '2 onr
'I2CWrite SDA, SCL, STP_ADD, [%00000110] : pause D0 '2 onr 1 onr
Next LP
'D0=D0+1

Goto Stepper_control











END