28X2 to drive a Rev-Ed 4x20 OLED to tell the time with a DS3231

Rampz

Well-known member
Hello All

I thought best to start a new thread to do as the title says, seems to me that it nay be of interest in the first part for people that want to drive the OLED directly with a X2 picaxe, in my case the 28X2 and get the time displayed from the DS3231

I found the following code to atleast display something from the picaxe i want to use, its a 2 line in this setup i think and i want to use the 4 line, the code below is from a previous post by cpedw aug13 2021, seems to be a good starting point. I will use his post as a starting point to get the display to work and then add the clock module

Code:
; AXE133 Serial LCD/OLED using PICAXE-28X2
; Emulates basic serial operation of the popular AXE033 module
; CPS, May 2011
; v2 18/01/2012
; DW Aug 2021

#picaxe 28X2

; ********************************************
; Note you must uncomment just one of these two options
; depending on whether you have an LCD or OLED module
#define use_OLED
;#define use_LCD
; ********************************************
#define use_welcome    ; display the welcome message upon power up


; Supported Commands
; 0-7, 8-15    CGRAM characters
; 16-252    normal ASCII characters, according to selected character map table
; 253, X    display 16 character pre-saved message from EEPROM memory, X can be 0-15
; 254, X    LCD command, X can be 0 to 255 

symbol line_length = 16    ; change to 20 for displays with 20 character lines

symbol baud = N2400_16    ; Serial baud rate 2400,N,8,1.

'symbol spare0     = C.0 ; spare output 0
'symbol spare1     = C.1 ; spare output 1
'symbol spare2     = C.2 ; spare output 2 (or optional backlight)
symbol RX        = A.0    ; serial receive pin leg 2. Don't use with integral frequency meter
symbol enable     = B.0    ; OLED/LCD enable leg 21
symbol rs         = A.4    ; OLED/LCD RS leg 7


; OLED/LCD data pins are on C.0 to C.7

; Store the 16 character user defined messages in EEPROM data memory
; First two messages are optionally used as welcome message

; If using a display with 20 characters you will need to edit 
; the start addresses to be multiples of 20 (currently at 16) 
; and add 4 characters to each message.
; Please remember 4 line displays always use the strange 1-3-2-4 layout.

#ifdef use_OLED        
EEPROM $00, ("Serial OLED 28X2")     ; store msg in the EEPROM memory
#else
EEPROM $00, (" Serial LCD 28X2")     ; store msg in the EEPROM memory
#endif


EEPROM $10, (" picaxe display ")     ; store msg in the EEPROM memory

EEPROM $20, ("This is msg 2   ")     ; store msg in the EEPROM memory
EEPROM $30, ("This is msg 3   ")     ; store msg in the EEPROM memory
EEPROM $40, ("This is msg 4   ")     ; store msg in the EEPROM memory
EEPROM $50, ("This is msg 5   ")     ; store msg in the EEPROM memory
EEPROM $60, ("This is msg 6   ")     ; store msg in the EEPROM memory
EEPROM $70, ("This is msg 7   ")     ; store msg in the EEPROM memory
EEPROM $80, ("This is msg 8   ")     ; store msg in the EEPROM memory
EEPROM $90, ("This is msg 9   ")     ; store msg in the EEPROM memory
EEPROM $A0, ("This is msg 10  ")     ; store msg in the EEPROM memory
EEPROM $B0, ("This is msg 11  ")     ; store msg in the EEPROM memory
EEPROM $C0, ("This is msg 12  ")     ; store msg in the EEPROM memory
EEPROM $D0, ("This is msg 13  ")     ; store msg in the EEPROM memory
EEPROM $E0, ("This is msg 14  ")     ; store msg in the EEPROM memory
EEPROM $F0, ("This is msg 15  ")     ; store msg in the EEPROM memory

;initialise OLED/LCD
SETFREQ M4
init:
    gosub LCD_init         ; initialise LCD

; display welcome message if desired
#ifdef use_welcome    
    low rs                ; command mode
    let pinsC = 128        ; move to line 1
    pulsout enable,1      ; pulse the enable pin to send data.
    high rs                ; character mode again
    
    let b1 = 0            ; message 0 on top line
    gosub msg            ; do it

    low rs                ; command mode
    let pinsC = 192        ; move to line 2, instruction 192
    pulsout enable,1      ; pulse the enable pin to send data.
    high rs                ; character mode again
    
    let b1 = 1            ; message 1 on bottom line
    gosub msg            ; do it
#endif        
        
; main program loop, runs at 16MHz

main:

    serin RX,baud,b1            ; wait for the next byte

    ; NB keep character mode test as first item in this list to optimise speed
    if b1 < 253 then
        let pinsC = b1         ; output the data
        pulsout enable,1      ; pulse the enable pin to send data.
        goto main            ; quickly loop back to top
    else if b1 = 254 then
        low rs                  ; change to command mode for next character
        serin RX,baud,b1        ; wait for the command byte
        let pinsC = b1         ; output the data
        pulsout enable,1      ; pulse the enable pin to send data.
        high rs                ; back to character mode
        goto main            ; quickly loop back to top
    else if b1 = 253 then
        serin RX,baud,b1        ; wait for the next byte
        gosub msg            ; do the 16 character message
        goto main            ; back to top
'    else ; must be 255            Disable the spare outputs
'        serin RX,baud,b1        ; wait for the next byte
'        let pinsC = b1 & %00000111 | %10000000
'                        ; output the data on C.0 to C.1, keep RS high
'        goto main            ; back to top
    end if
        goto main

; power on LCD initialisation sub routine
LCD_init:
    let dirsA = %00010000    ; PortA 4 output
    let dirsB = %00000001    ; PortB 0 output
    let dirsC = %11111111    ; PortC all outputs
    

    
#ifdef use_OLED
    ; Winstar OLED Module Initialisation
    ; according to WS0010 datasheet (8 bit mode)

    pause 500             ; Power stabilistation = 500ms

    ; Function set - select only one of these 4 character table modes
    ;let pinsC = %00111000     ; 8 bit, 2 line, 5x8 , English_Japanese table
    let pinsC = %00111001     ; 8 bit, 2 line, 5x8 , Western_European table1
    ;let pinsC = %00111010     ; 8 bit, 2 line, 5x8 , English_Russian  table
    ;let pinsC = %00111011     ; 8 bit, 2 line, 5x8 , Western_European table2
    
    pulsout enable,1      ; 
        
    let pinsC = %00001100    ; Display on, no cursor, no blink
    pulsout enable,1     

    let pinsC = %00000001     ; Display Clear
    pulsout enable,1
    pause 7            ; Allow 6.2ms to clear display

    setfreq m16            ; now change to 16Mhz

    let pinsC = %00000010     ; Return Home
    pulsout enable,1

    let pinsC = %00000110     ; Entry Mode, ID=1, SH=0
    pulsout enable, 1


#else    
    ; Standard LCD Module Initialisation
    pause 15             ; Wait 15ms for LCD to reset.

    let pinsC = %00110000     ; 8 bit, 2 line
    pulsout enable,1      ; Send data by pulsing enable
    pause 5             ; Wait 5 ms
    pulsout enable,1          ; Send data 48 again
    pulsout enable,1      ; Send data 48 again
    
    setfreq m16            ; now change to 16Mhz

    let pinsC = %00111000     ; LCD  - 8 bit, 2 line, 5x8  
    pulsout enable,1
            
    let pinsC = %00000001    ; Clear Display
    pulsout enable,1     
    pause 8            ; 8 = 2ms at 16MHz
    
    let pinsC = %00000010     ; return home
    pulsout enable,1

    let pinsC = %00000110    ; Entry mode
    pulsout enable,1     
    pause 1            

    let pinsC = %00001100    ; Display on, no cursor, no blink
    pulsout enable,1     
#endif
    
    high rs            ; Leave in character mode
    return


; display message from EEPROM sub routine
; message number 0-15 must be in b1 when called
; uses (alters) b1, b2, b3, b4
msg:
    let b2 = b1 & %00001111 * line_length
                        ; EEPROM start address is 0 to 15 multiplied by 16
    let b3 = b2 + line_length - 1 ; end address is start address + (line_length - 1)
    for b4 = b2 to b3            ; for 16 times
        read b4,b1            ; read next character from EEPROM data memory into b1
        let pinsC = b1         ; output the data
        pulsout enable,1      ; pulse the enable pin to send data.
    next b4                ; next loop
    return
    
; Check end user has defined just one type of display
#ifndef use_OLED
#ifndef use_LCD
#error "Oops - no OLED / LCD type defined at top of program!"
#endif
#endif

#ifdef use_OLED
#ifdef use_LCD
#error "Oops - both OLED / LCD types defined at top of program!"
#endif
#endif
 

Rampz

Well-known member
That appears to be the "AXE133 firmware" or based upon it, taking serial data in and displaying what is sent.
Hippy am i making things more difficult by trying to do everything with 1x picaxe? Am i best to just use the backpack and a second picaxe?

The end for this project is that when mains fail is detected, it will send an actuator to do something in my case stop the pendulum of a clock, store the time that it did, then when mains returns, start the clock again at the correct time based on the time it stopped it, there will be a sensor detecting the pendulem swing so that i will know when it has actually stopped the pendulem.

This project will then form part of a future project to regulate the time of the clock, but a stage at a time

That appears to be the "AXE133 firmware" or based upon it, taking serial data in and displaying what is sent.
Yes a very good point, how does sending commands differ, when the commands are within the picaxe driving the screen, i am not intending to send data to it in this project, more a case the picaxe will do everything
 
Last edited:

cpedw

Senior Member
I was able to incorporate a freqency meter with the LED driver in a 28X" - https://picaxeforum.co.uk/threads/frequency-meter.32431/
It works fine and saves an 18M2. The functions are both plagiarised - the frequency meter from Kranenborg and the LED driver is from Picaxe's AXE133 firmware.

There's a certain appeal to keeping the component count low but for simplicity, keeping the two functions in separate picaxes has much to commend it.
 

hippy

Technical Support
Staff member
Hippy am i making things more difficult by trying to do everything with 1x picaxe? Am i best to just use the backpack and a second picaxe?
Depends on exactly what you ultimately want to do ...

The end for this project is that when mains fail is detected, it will send an actuator to do something in my case stop the pendulum of a clock, store the time that it did, then when mains returns, start the clock again at the correct time based on the time it stopped it, there will be a sensor detecting the pendulem swing so that i will know when it has actually stopped the pendulem.

This project will then form part of a future project to regulate the time of the clock, but a stage at a time
But all quite irrelevant to this part of the project :)

What we need to know is what input this PICAXE gets, what outputs it produces, and what its functionality is with respect to those.

In many ways the data and dataflow is more important on the 'project overview' diagram than what actually happens within the black boxes which may appear on it. Those along with what the black box has to do with the data will define what is the best way to implement that black box. From its perspective it doesn't need to know where the data comes from, goes to, or achieves, only what input and out and what it has to do.

Yes a very good point, how does sending commands differ, when the commands are within the picaxe driving the screen, i am not intending to send data to it in this project, more a case the picaxe will do everything
For a typical PICAXE + AXE133 project the PICAXE will use SEROUT commands to send what needs to be shown on the display. For example "ADC = xxx" can be shown by using -

Code:
ReadAdc ADC_PIN, b1
SerOut OLED_PIN, OLED_BAUD, ( "ADC = ", #b1, "  ")
If you have a PICAXE with the display directly connected to it you would need to put the characters on the display one at a time -
Code:
b0 = "A" : Gosub WrChar
b0 = "D" : Gosub WrChar
b0 = "C" : Gosub WrChar
b0 = " " : Gosub WrChar
b0 = "=" : Gosub WrChar
b0 = " " : Gosub WrChar
BinToAscii b1, b11, b12, b13
If b1 >= 100 Then
  b0 = b11 : Gosub WrChar
End If
If b1 >= 10 Then
  b0 = b12 : Gosub WrChar
End If
b0 = b13 : Gosub WrChar
b0 = " " : Gosub WrChar
b0 = " " : Gosub WrChar
There are some tricks which can be used to make that more compact but that's the basic principle.

So what it boils down to is; a PICAXE with an AXE133 with ease of use of SEROUT, or a single PICAXE with more challenging coding. I would always suggest easiest should win but there can be cost and other matters which need to be taken into account.
 

Rampz

Well-known member
What we need to know is what input this PICAXE gets, what outputs it produces, and what its functionality is with respect to those.
Thank You Hippy for a in depth reply, i will list what i see happening based on inputs and outputs.

I would always suggest easiest should win but there can be cost and other matters which need to be taken into account.
Yes i agree with looking for the easiest route, i guess it also depends how much of a burden driving a screen is to a picaxe with respect to it doing other tasks to, the more i read about it the more it seems such

If you have a PICAXE with the display directly connected to it you would need to put the characters on the display one at a time -
I guess the trick where possible is to write text to the screen in a command as in your example, then each time the adc is read only update the values and not keep sending the text?
 

lbenson

Senior Member
it also depends how much of a burden driving a screen is to a picaxe with respect to it doing other tasks to
If you're talking of a basic resolution for acting of one second (for a clock), then there's a great deal that a 28x2 can do in a second, and doing any kind of updating on a 4x20 display won't be a significant problem. The question is whether you want to have simple code with serial output to the LCD, or more complex code (which you'll never look at again after you get it working once--and there are plenty of examples) to do all the work in the single PICAXE.
 

hippy

Technical Support
Staff member
i guess it also depends how much of a burden driving a screen is to a picaxe with respect to it doing other tasks to, the more i read about it the more it seems such
Controlling a text display or small graphical display, should be easily possible for a PICAXE., though it may require choosing a particular PICAXE to have the resources and speed of execution to achieve it.

The real issue comes when wanting to do more than one thing on that single PICAXE. Reading a temperature, clock or ADC reading and displaying that is unlikely to cause too much problem. But doing something more might.

The usual approach is to determine the functionality required, then work out how that will be split between PICAXE's, whether it could be combined onto a single PICAXE, and what limitations there would be if it is.

I guess the trick where possible is to write text to the screen in a command as in your example, then each time the adc is read only update the values and not keep sending the text?
That, plus wrapping what's needed within macro commands so the code to do it is 'hidden', at least from the main body of the code, and it's easier to specify. For example The earlier code could be expressed as -
Code:
SendTxt("ACDC = ")
SendNum(b1)
SendTxt("  ")
Which makes it almost as easy to use as SEROUT but does require those macros to be written, and each invocation may use more memory than the equivalent SEROUT would.
 

Rampz

Well-known member
The real issue comes when wanting to do more than one thing on that single PICAXE. Reading a temperature, clock or ADC reading and displaying that is unlikely to cause too much problem. But doing something more might.
Thanks Hippy, all i can do is try and give info towards what i want to acheive, and see what's possible

The concept is to stop a turret clock by grabbing its pendulem when there is a power fail either directly for a mains driven system or towards the end of battery back up for low voltage systems, stop the clock and read the current time. after the power has been restored start the clock again at the exact time it was stopped by releasing the pendulem again.

Inputs

Time module DS3231 scl and sda, read the clock every second
pendulem sensor consisting of a reed switch and led in series
an ADC input for measuring power fail
2x push buttons for setting time, i would see that 1 button increments the screen and the second increments the value on screen, when the screen increments the previous value is saved and this carries on until we are back at the start screen

outputs

1x relay for actuator direction
1x relay for actuator on or off

Other info

I would want to alter the time if needed and add a correction to make it more accurate

The grabbing of the pendulem would need calibration, this being letting the actuator run for say 1 second and stop, check that the pendulem is still swinging from its sensor then repeat until there is no more pendulem signal, count how many 1 seconds was needed to do this and save that in eeprom for use when its needed, its fine for this to be done at start up and not expecting the system to work while this is being done, if that helps.

This is how i see it from my electrical background.

Code wise i will use most of the code in this project for the next few associated projects around a similar theme
 

Rampz

Well-known member
In many ways the data and dataflow is more important on the 'project overview' diagram than what actually happens within the black boxes which may appear on it. Those along with what the black box has to do with the data will define what is the best way to implement that black box. From its perspective it doesn't need to know where the data comes from, goes to, or achieves, only what input and out and what it has to do.
Hippy in normal operation I see a loop where it reads and displays the time and date then reads and displays the ADC value while comparing it to a symbol value.

If ADC value is below a value for say 10 seconds the Gosub, do something
In this macro I need to read the pendulum input and output the actuator to grab the pendulum and store the time in eeprom when pendulum input stays constantly on.

Then

Next stage reads ADC value waiting for it to go above a symbol value, when power has returned calculate how long to wait till releasing pendulum at the right time again, maybe display a countdown on display.

Then

Release pendulum and return to normal operation of checking time and ADC value.

Time and actuator calibration this could happen at initial power up, so to keep it out of the main code and means the main loop doesn't also need to check the push button to see if calibration is being entered

I am sure the is more to it all in some areas, like how exactly to add slight alterations to the displayed time, I guess the initial time and date is set up in code.
I don't see that I am ever trying to do 2 things at the same time where the picaxe may struggle?
 

oracacle

Senior Member
No, iirc macros pieces of code that are compiled into the code.
This mean that in places where the code is the same you can change the code and re-compile getting the code updates in all locations with just one edit.
The compiled code that is loaded onto the PICAXE will have each macro calls replaced with the code that is in the macro.
You can pass data into that macro when the macro is called. I don't know if there is a limit to the number and type of parameters that can be passed into them. changes to variables within the macro I think will effect the variables in the entire programme.
Due to the fact that macros are essentially word substitution (ie macro_name becomes the entire macro code), code space requirments may need to be considered, which is when a gosub maybe used instead of a macro. Speed may also be a factor, I resonably sure that macros will be faster than calling a gosub and returning.
 

Jack Burns

New Member
Are Macro's the same as Gosub? eg go to another part of the code?

The following article from hippy explains macro’s really well.
 

Rampz

Well-known member
The following article from hippy explains macro’s really well.
Thank You Jack Burns very good article, seems for the moment i am best using an Oled with a backpack and a seperate picaxe for the project, the screens do come with the backpack although they don't connect in the best manner, hung off the back, then later maybe look at bringing it all to 1x picaxe
 

Rampz

Well-known member
Hello All

So at the moment i have copied some code from "Marks" from https://picaxeforum.co.uk/threads/ds3231-code-examples.18694/

That works fine, this is the code, currently running on a 20x2

Code:
#no_data
#terminal 9600
#picaxe 20x2
SETFREQ M8

   SYMBOL secs          = b2
   SYMBOL mins          = b3
   SYMBOL hours         = b4
   SYMBOL day           = b5
   SYMBOL date          = b6
   SYMBOL month         = b7
   SYMBOL year          = b8
   SYMBOL PM_AM         = b9
   SYMBOL D0            = b10
   SYMBOL D1            = b11
   SYMBOL D2            = b12
   SYMBOL D3            = b13
   SYMBOL D4            = b14
   SYMBOL D5            = b15
            
Initialize:           
HI2Csetup I2Cmaster, %11010000, I2Cfast_8, I2Cbyte       ' Set to 400kbps
'      HI2Cout $0 , ( $14, $59, $11 ,day, $11, $01 , $15) ' Uncomment to Program DS3231  example (11.59.00 AM)
;Progam  Registers  (secs,mins,hours,day,date,month,year) ' Enter in BCD           hours example ( $0 to $23 )

'      HI2Cout $0E, ($0)                                  ' Control   (turn On 1hz $0)  output Pin3  ( Off $4)
Main:
PAUSE 1000
  ReadRegisters:    
       HI2Cin  $0 , (secs,mins,hours,day,date,month,year) ' Read from DS3231
     
   ClockDisplay:
   PM_AM ="P" : IF hours < $12 THEN :PM_AM = "A" :ENDIF       'Indicate P or A
   IF hours =$20 OR hours =$21 THEN :hours = hours -$6 :ENDIF
   hours = hours //$12 : IF hours =$0 THEN: hours =$12 :ENDIF '24 to 12 hour format
   
   BcdTOASCII hours,D5,D4 : IF D5 = "0" THEN: D5 = " " :ENDIF 'Zero blanking
   BcdTOASCII mins ,D3,D2
   BcdTOASCII secs ,D1,D0  
     sertxd (CR,LF,D5,D4,".",D3,D2,".",D1,D0," ",PM_AM,"M  ") '(11.59.00 PM )
  
   BcdTOASCII date ,D5,D4
   BcdTOASCII month,D3,D2
   BcdTOASCII year ,D1,D0  
     sertxd (D5,D4,"/",D3,D2,"/20",D1,D0)                     '( 28/06/2011 )
               
  GOTO Main
I noticed further down his post, he had setup the code to collect the time and date from the PE6 editor, i tried to intergrate this code into my first part and although it didn't show any errors it also didn't work, this is my attempt at cut and paste code, he shows the code as being on a 18M2, am i able to get the time and date from the editor? would make it easier to get to set up like that

Code:
#no_data
#picaxe 20X2
#terminal 9600
SETFREQ M8


   SYMBOL index         = b0       : SYMBOL halfsecond  =b0
   SYMBOL CRC           = b1
   SYMBOL secs          = b2
   SYMBOL mins          = b3
   SYMBOL hours         = b4
   SYMBOL day           = b5
   SYMBOL date          = b6
   SYMBOL month         = b7
   SYMBOL year          = b8
   SYMBOL PM_AM         = b9
   SYMBOL D0            = b10
   SYMBOL D1            = b11
   SYMBOL D2            = b12
   SYMBOL D3            = b13
   SYMBOL D4            = b14
   SYMBOL D5            = b15
 
            


  ;;;;   Collect  date and time from the PICAXE Editor 6.0.7.5 (BETA)
          bptr=28
          FOR  index = 2 TO 18   
           LOOKUP index,(ppp_datetime),@bptrinc      '  ("2015-01-31 10:00:25")
          NEXT index
;;;;    Entering date and time in $BCD  convert from ASCII  
bptr =28
year  = @bptrinc*$10+@bptrinc-$30 :inc bptr
month = @bptrinc*$10+@bptrinc-$30 :inc bptr
date  = @bptrinc*$10+@bptrinc-$30 :inc bptr
hours = @bptrinc*$10+@bptrinc-$30 :inc bptr          ' $0 to $23
mins  = @bptrinc*$10+@bptrinc-$30 :inc bptr
secs  = @bptrinc*$10+@bptrinc-$30 +$24               ' add 16 seconds (how long it takes for PC to program)   

InitialiseTime:
   hours = hours /$10 *250 +hours                            ' Convert ($00 to $23) BCD to Decimal
   
   PM_AM = hours /12 *$20 +$40                               ' Convert to 12hour
   hours = hours //12 : IF hours =0 THEN : hours =12 : ENDIF ' Convert to 12hour
   hours = hours /10 *6 +hours +PM_AM                        ' hoursAM 1-12 ($41 to $52) hoursPM 1-12 ($61 to $72)
   
    READ  255,CRC : IF CRC <> 0 THEN Main:                    ' Skip if time already programmed
     HI2Csetup I2Cmaster, %11010000, I2Cfast_32, I2Cbyte     ' Set  DS3231 to 400kbps
      HI2Cout $0 , (secs,mins,hours,day,date,month,year)     ' Write to DS3231
    WRITE 255,53                                             ' we can make use of an existing eeprom entry if any


  MAIN:
    
PAUSE 1000
  ReadRegisters:    
       HI2Cin  $0 , (secs,mins,hours,day,date,month,year) ' Read from DS3231
     
   ClockDisplay:
   PM_AM ="P" : IF hours < $12 THEN :PM_AM = "A" :ENDIF       'Indicate P or A
   IF hours =$20 OR hours =$21 THEN :hours = hours -$6 :ENDIF
   hours = hours //$12 : IF hours =$0 THEN: hours =$12 :ENDIF '24 to 12 hour format
   
   BcdTOASCII hours,D5,D4 : IF D5 = "0" THEN: D5 = " " :ENDIF 'Zero blanking
   BcdTOASCII mins ,D3,D2
   BcdTOASCII secs ,D1,D0  
     sertxd (CR,LF,D5,D4,".",D3,D2,".",D1,D0," ",PM_AM,"M  ") '(11.59.00 PM )
  
   BcdTOASCII date ,D5,D4
   BcdTOASCII month,D3,D2
   BcdTOASCII year ,D1,D0  
     sertxd (D5,D4,"/",D3,D2,"/20",D1,D0)                     '( 28/06/2011 )
               
  GOTO Main
 
Last edited:

Jack Burns

New Member
Definitely worked on PE 6.1 with an 08M2 the last time I tried it.

You probably need to change the line bptr =28 to bptr =56 as the X2’s use a different area of memory for temp storage.

See page 28 of manual 2 for further details.

Edit:
Having thought about this I would expect both of the addresses to work, however you should make the above changes otherwise the X2 will overwrite some of the variables starting from b28 upwards.

Are you running this code on a chip as I can’t remember if it works on the simulator?
 
Last edited:

Rampz

Well-known member
Are you running this code on a chip as I can’t remember if it works on the simulator?
Yep running it on a 20x2, the second part of code by Marks is for a direct driven oled, I have just taken the part that looks to update the time from the editor.
I will alter the code along the lines you say, doesn't help not really understanding what's going on with the code

I have both bits of code saved in different files, when the first is used it reads correctly but slightly different to time on laptop, i upload second part of code and nothing happens in the terminal, so i go back to first bit of code and time is still wrong by same amount, so i think that the time isn't being written to the clock module and then later isn't being read and displayed at the terminal

I did see "I2Cfast_32" I altered it back to 8mhz, that wasn't the cause

In the second bit of code if i hover over "ppp_datetime" it does show the laptop time so it is being taken from the PE6 editor ok just something wrong from there.

EDIT
Seems there are quite a few differences between the 2 bits of code, can anyone help please with adding reading time and date from the PE6 editor and writing to the ds3231 module in my first bit of borrowed code by "Marks"
I have looked up info on "ppp_datetime" i can't workout how "Marks" has used this to program the DS3231 and why i can't use the same extract to do the same, seems to simulate ok athough shows the wrong time
 
Last edited:

Jack Burns

New Member
During a program download, the date and time is temporarily stored in the picaxe, it is then transferred to the DS3231 once the picaxe restarts. This all take a certain amount of time and "Marks" has allowed for it by adding 16 seconds to the computer time. I haven't tried it to see what happens if you end up with a value greater that 59 secs.

Code:
secs  = @bptrinc*$10+@bptrinc-$30 +$24               ' add 16 seconds (how long it takes for PC to program)
How far out is the time during setting? I assume the differences also depend on whether you're simulating or running the code on a picaxe?


Here is a combination of the 2 sets of code which seems to work okay in the simulator. It could probably do with tidying up a bit, however I wanted to leave it as close to the original code as possible so that you could see what I had done.

Edit: Code corrected as hours displayed incorrectly when running in a chip.

Note: Erase picaxe (PE6 'Clear' button) before downloading new code, otherwise the RTC won't be initialised.

Code:
#no_data
#terminal 9600
#picaxe 20x2
SETFREQ M8
  
   SYMBOL index         = b0
   SYMBOL CRC           = b1
   SYMBOL secs          = b2
   SYMBOL mins          = b3
   SYMBOL hours         = b4
   SYMBOL day           = b5
   SYMBOL date          = b6
   SYMBOL month         = b7
   SYMBOL year          = b8
   SYMBOL PM_AM         = b9
   SYMBOL D0            = b10
   SYMBOL D1            = b11
   SYMBOL D2            = b12
   SYMBOL D3            = b13
   SYMBOL D4            = b14
   SYMBOL D5            = b15
   SYMBOL MemAddr = 56 ' Start address of RAM used for temp storage
   
              
              
Initialize:           
  HI2Csetup I2Cmaster, %11010000, I2Cfast_8, I2Cbyte       ' Set to 400kbps

  READ  127,CRC
  IF CRC <> 53 THEN                ' Set time if NOT already programmed
    gosub InitialiseRTC
  ENDIF

;      HI2Cout $0 , ( $14, $59, $11 ,day, $11, $01 , $15) ' Uncomment to Program DS3231  example (11.59.00 AM)
;Progam  Registers  (secs,mins,hours,day,date,month,year) ' Enter in BCD           hours example ( $0 to $23 )

'      HI2Cout $0E, ($0)                                  ' Control   (turn On 1hz $0)  output Pin3  ( Off $4)



Main:
PAUSE 1000
  ReadRegisters:    
       HI2Cin  $0 , (secs,mins,hours,day,date,month,year) ' Read from DS3231
     
   ClockDisplay:
   PM_AM ="P" : IF hours < $12 THEN :PM_AM = "A" :ENDIF       'Indicate P or A
   IF hours =$20 OR hours =$21 THEN :hours = hours -$6 :ENDIF
   hours = hours //$12 : IF hours =$0 THEN: hours =$12 :ENDIF '24 to 12 hour format
   
   BcdTOASCII hours,D5,D4 : IF D5 = "0" THEN: D5 = " " :ENDIF 'Zero blanking
   BcdTOASCII mins ,D3,D2
   BcdTOASCII secs ,D1,D0  
     sertxd (CR,LF,D5,D4,".",D3,D2,".",D1,D0," ",PM_AM,"M  ") '(11.59.00 PM )
  
   BcdTOASCII date ,D5,D4
   BcdTOASCII month,D3,D2
   BcdTOASCII year ,D1,D0  
     sertxd (D5,D4,"/",D3,D2,"/20",D1,D0)                     '( 28/06/2011 )
               
  GOTO Main
  
  
InitialiseRTC:
    ' The following code came from "Marks"
    ' https://picaxeforum.co.uk/threads/ds3231-code-examples.18694/
     
   ;;;;   Collect  date and time from the PICAXE Editor 6.1.0.0
          bptr = MemAddr
          FOR  index = 2 TO 18   
           LOOKUP index,(ppp_datetime),@bptrinc      '  ("2015-01-31 10:00:25")
          NEXT index
;;;;    Entering date and time in $BCD  convert from ASCII  
bptr  = MemAddr
year  = @bptrinc*$10+@bptrinc-$30 :inc bptr
month = @bptrinc*$10+@bptrinc-$30 :inc bptr
date  = @bptrinc*$10+@bptrinc-$30 :inc bptr
hours = @bptrinc*$10+@bptrinc-$30 :inc bptr          ' $0 to $23
mins  = @bptrinc*$10+@bptrinc-$30 :inc bptr
secs  = @bptrinc*$10+@bptrinc-$30 +$24               ' add 16 seconds (how long it takes for PC to program)   
HI2Cout $0 , (secs,mins,hours,day,date,month,year)     ' Write to DS3231

;InitialiseTime:
;   hours = hours /$10 *250 +hours                            ' Convert ($00 to $23) BCD to Decimal
;   
;   PM_AM = hours /12 *$20 +$40                               ' Convert to 12hour
;   hours = hours //12 : IF hours =0 THEN : hours =12 : ENDIF ' Convert to 12hour
;   'hours = hours /10 *6 +hours +PM_AM                        ' hoursAM 1-12 ($41 to $52) hoursPM 1-12 ($61 to $72)
;   
;   'HI2Csetup I2Cmaster, %11010000, I2Cfast_32, I2Cbyte     ' Set  DS3231 to 400kbps
;   HI2Cout $0 , (secs,mins,hours,day,date,month,year)     ' Write to DS3231
   WRITE 127,53                                             ' we can make use of an existing eeprom entry if any
   return
 
Last edited:

Rampz

Well-known member
This all take a certain amount of time and "Marks" has allowed for it by adding 16 seconds to the computer time
Thank You Jack, i will try when i get home later, i can always do a download shortly after the start of a new minute in case it becomes and issue, and i can alter that time depending how long the ultimate code takes.
Its great you have left the code as close as possible to the original, good to be able to try and get my head around it
 

hippy

Technical Support
Staff member
Cross-posted with Jack Burns

The way the code works is a quite clever. the 'ppp_datetime' is a literal string which reflects the time on the PC when the code is compiled and downloaded to the PICAXE.

That LookUp (ppp_datetime) effectively becomes LookUp("2022-03-22 11:32:46") for example. The FOR-NEXT loop wrapping it takes each character at a time and stores it in sequential bytes of RAM through the use of 'bptr'. -
Code:
                   0123456789-12345678
                  "2022-03-22 11:32:48"
          .-----.    ||              |
bptr + 0  | "2" |<---'|              |
bptr + 1  | "2" |<----'              |
bptr + 2  | "-" |                    |
          :     :                    |
bptr + 15 | "4" |                    |
bptr + 16 | "8" |<-------------------'
          `-----'
A second stage then converts pairs of ASCII data bytes into BCD byte values which will be used to program the RTC -
Code:
          .-----.        .-----.
bptr + 0  | "2" |---.--->| $22 | Year
bptr + 1  | "2" |---'    `-----'
bptr + 2  | "-" |        .-----.
bptr + 3  | "0" |---.--->| $03 | Month
bptr + 4  | "3" |---'    `-----'
          :     :        .-----.
bptr + 15 | "4" |---.--->| $48 | Seconds
bptr + 16 | "8" |---'    `-----'
          `-----'
The next stage sets some bits in the varibles which are written to the RTC, such as whether the RTC should be operating in 12 or 24 hour mode, and adjusts the data to be written as appropriate.

The final stage is to write those bytes to the RTC if the code has never been run before, the RTC hasn't previously been set.

Once that's done the RTC will keep track of real time. Though slightly behind real world time, because it was set to the time of compilation, not the time the code started running.

Marks' attempts to adjust for by adding 16 seconds to the time, but that's not implemented well, because adding $24 will put the seconds value out of valid range in a number of cases.

In most cases one wouldn't use 12 hour time and, if one forgoes trying to adjust for download time, the code I would start with would be as below. Note that it uses low 'bPtr' addresses so will work on any chip without alteration -
Code:
#Picaxe 28X2
#Terminal 9600

Symbol CLOCK_SET_ADR = 0   ; Clock has been set byte

Symbol reserveW0     = w0  ; b1:b0

Symbol year          = b2  ; Must be b2 through b7
Symbol month         = b3
Symbol day           = b4
Symbol hour          = b5
Symbol minutes       = b6
Symbol seconds       = b7

Symbol lastSeconds   = b8  ; Last seconds shown

Init:
  Read CLOCK_SET_ADR, b0
  If b0 = 0 Then
    Gosub SetTheClock
    Write CLOCK_SET_ADR, 1
  End If
  lastSeconds = $FF

Main:
  Do
    Gosub DisplayTime
    Pause 10
  Loop

DisplayTime:
  Gosub ReadTheClock
  If seconds <> lastSeconds Then
    SerTxd("20", #year, "-", #month,   "-", #day)
    SerTxd(" ",  #hour, ":", #minutes, ":", #seconds, CR, LF)
    lastSeconds = seconds
  End If
  Return

SetTheClock:
  ; Copy the 'ppp_datetime' to RAM, b2 through b18
  ;    0123456789-12345678
  ;   "YYYY-MM-DD HH:MM:SS"
  For bPtr = 2 To 18
    LookUp bPtr, (PPP_DATETIME), @bPtr
  Next
  ; Create the BCD values
  year    = b2  * 16 + b3  - "0"
  month   = b5  * 16 + b6  - "0"
  day     = b8  * 16 + b9  - "0"
  hour    = b11 * 16 + b12 - "0"
  minutes = b14 * 16 + b15 - "0"
  seconds = b17 * 16 + b18 - "0"
  ; Write the data to the RTC, force day of week to 1
  HI2cSetup I2CMASTER, %11010000, I2CSLOW, I2CBYTE
  HI2cOut $00, (seconds, minutes, hour, 1, day, month, year)
  ; Reset b2 through b18 to zero
  For bPtr = 2 To 18
     @bPtr = 0
  Next
  ; And we are done
  Return

ReadTheClock:
  ; Read the raw data from the clock, ignore day of week
  HI2cSetup I2CMASTER, %11010000, I2CSLOW, I2CBYTE
  HI2cIn  $00, (seconds, minutes, hour, day, day, month, year)
  ; Convert BCD to decimal - dec = ((bcd / 16) * (-6)) + bcd
  year    = year    / 16 * $FA + year
  month   = month   / 16 * $FA + month
  day     = day     / 16 * $FA + day
  hour    = hour    / 16 * $FA + hour
  minutes = minutes / 16 * $FA + minutes
  seconds = seconds / 16 * $FA + seconds
   ; And we are done; all values are decimal
  Return
That should work on a real chip but untested, and seems to be crashing my PE6 when simulated. YMMV.
 

Jack Burns

New Member
Hippy’s code worked fine for me during simulation on PE6, and its clean layout made it easy to understand.

; Convert BCD to decimal - dec = ((bcd / 16) * (-6)) + bcd
This method of BCD to decimal conversion is explained in greater detail in the following link for anyone who’s interested.
 

hippy

Technical Support
Staff member
Hippy’s code worked fine for me during simulation on PE6
Thanks for confirming that. It seems to be an issue with my PC. For some code I click the 'simulate' button and it steps as expected, all the simulation buttons get greyed out and it then grinds to a halt some time later or if I click on PE6 or the Terminal. Time for a re-install I guess.

Anyway, this was my "even more elegant" version which doesn't use 'bPtr', uses Eeprom. If you or anyone could try simulating that and seeing if it works I'd be grateful. The "Set" falls through to the "Read" because it might as well, and saves having to shadow the time variables when updating. "initialise" and "Set" as separated to make it easier to update, keeps variables as always decimal -
Code:
#Picaxe 28X2
#Terminal 9600

;                          ;  0123456789-12345678
Data 0, (PPP_DATETIME)     ; "YYYY-MM-DD HH:MM:SS

Symbol reserveW0     = w0  ; b1:b0

Symbol year          = b2
Symbol month         = b3
Symbol day           = b4
Symbol hour          = b5
Symbol minutes       = b6
Symbol seconds       = b7

Symbol lastSeconds   = b8  ; Last seconds shown

Init:
  Read 0, b0
  If b0 <> 0 Then
    Gosub InitialiseTheClock
    Write 0, 0
  End If
  lastSeconds = $FF

Main:
  Do
    Gosub DisplayTime
    Pause 10
  Loop

DisplayTime:
  Gosub ReadTheClock
  If seconds <> lastSeconds Then
    SerTxd("20", #year, "-", #month,   "-", #day)
    SerTxd(" ",  #hour, ":", #minutes, ":", #seconds, CR, LF)
    lastSeconds = seconds
  End If
  Return

#Macro GetBcdDateTime(var, adr)
  Read adr, WORD w0
  var = b0 * 16 + b1 - "0" 
#EndMacro

InitialiseTheClock:
  ; Get the BCD values to initialise the clock to
  GetBcdDateTime( year    ,  2 )
  GetBcdDateTime( month   ,  5 )
  GetBcdDateTime( day     ,  8 )
  GetBcdDateTime( hour    , 11 )
  GetBcdDateTime( minutes , 14 )
  GetBcdDateTime( seconds , 17 )
  Goto SetTheClock_FromBcd

#Macro DecToBcd(var)
  var = var / 10 * 6 + var   ; bcd = ((dec / 10) * 6) + dec
#EndMacro

#Macro BcdToDec(var)
  var = var / 16 * $FA + var ; dec = ((bcd / 16) * (-6)) + bcd
#EndMacro

SetTheClock:
  ; Convert decimal to BCD
  DecToBcd( year    )
  DecToBcd( month   )
  DecToBcd( day     )
  DecToBcd( hour    )
  DecToBcd( minutes )
  DecToBcd( seconds )

SetTheClock_FromBcd:
  ; Write the BCD data to the RTC, force day of week to 1
  HI2cSetup I2CMASTER, %11010000, I2CSLOW, I2CBYTE
  HI2cOut $00, (seconds, minutes, hour, 1, day, month, year)

ReadTheClock:
  ; Read the BCD data from the clock, ignore day of week
  HI2cSetup I2CMASTER, %11010000, I2CSLOW, I2CBYTE
  HI2cIn  $00, (seconds, minutes, hour, day, day, month, year)
  ; Convert BCD to decimal
  BcdToDec( year    )
  BcdToDec( month   )
  BcdToDec( day     )
  BcdToDec( hour    )
  BcdToDec( minutes )
  BcdToDec( seconds )
  ; And we are done; all values are decimal
  Return
 

Rampz

Well-known member
Anyway, this was my "even more elegant" version which doesn't use 'bPtr', uses Eeprom. If you or anyone could try simulating that and seeing if it works I'd be grateful. The "Set" falls through to the "Read" because it might as well, and saves having to shadow the time variables when updating. "initialise" and "Set" as separated to make it easier to update, keeps variables as always decimal -
Hippy I have a 28x2 on the AXE091 development board that I am working through the various versions posted by you and Jack Burns

Seems to work ok on the 28x2 on the development board, i altered the sertxd format so it was mins, hrs, sec, day, month, year

If the time or date is a single figure, the say the time can show as 21:3:9 when may look better showing as 21:03:09

I guess due to download time the time is wrong by 23-24 seconds

Next then how best to get the time accurate following a download, can we read live from the editor and write to the ds3231, trying to get my head around whats actually happening, ok I see its taken at time of download, the part that checks if time has been set previously I guess stops the code getting the time on a subsequent download?

Edit

Just run on simulator and seems to run fine as far as i can see, i did notice that the time is wrong by only maybe 6 seconds, strange how it differs between simulator and actual picaxe 28x2
 
Last edited:

Rampz

Well-known member
How far out is the time during setting? I assume the differences also depend on whether you're simulating or running the code on a picaxe?
Jack Burns thank you for your code, i am using it on a picaxe 20x2 or 28x2 i have both on the development board, i have tried your code on both.

Something is slightly off with the displayed hours it shows >.07.13PM as an example, i haven't looked to see why i think hours are showing as a ">"

Then with the added few seconds for download time part, i couldn't get that to work very well, no matter what figure i put there between say 3 seconds and 24 seconds i was always 3 seconds fast, so i removed it altogether and was still 3 seconds fast, really strange.

Thank you to Hippy and Jack Burns for helping me with this.
 
Last edited:

papaof2

Senior Member
I wrote a clock driver long ago and just added code that could be set to a slightly future time and a variable that had to be set for that code to run and reset the clock. With battery backup, it didn't need time adjustment often so not a problem to load the code, put in the numbers for the "fix" and download the "new" version. That clock is still running.

I do like the idea of getting the time from the PC because there's nothing to rewrite and possibly get wrong - just reload the PICAXE and you're ready to go.
 

PhilHornby

Senior Member
One of my first Picaxe projects needed to know the time, so it incorporated a DS1307. Time was set using a Sony IR remote - with feedback via a Piezo sounder. It could also confirm the time, using the flashes of a single LED and beeps of the buzzer :)

It added 14 seconds every day at 04:00, to make up for the deficiencies of the eBay clock module I used, so only had to manually change it twice a year for daylight saving.

All a bit clunky, but functional...
 

Bill.b

Senior Member
I also wrote a clock program some 10 years ago.
It includes :
12 /24 time
temperature
date
alarm
Winchester chimes - on the quarter, half, three quarter and hourly.
The chimes and alarm use a SPE035 MP3 module.
Setting by IR remote
Display is an AXE134Y oled 4 line
25297

program PDF File. (Program to big to post as Code).
PICaxe 14M2 Code
Code:
#Picaxe 14M2
'#Terminal 4800
'setfreq m8              .-------------- Serial IN
'                       |
;                        | .------------------------------------------.
;           PICAXE-14M2  | |                                          |
;          .-----------. | | -.- V+     V+ -.-   SPE035 (DFP-Mini)    |
;         -| B.5   C.0 | | |  |             |   .-----------------.   |
;      .---| B.4   C.1 |-' |  |             `---| VCC        BUSY |---'
;      |  -| B.3   C.2 |<--'  |  .------------->| RX         USB- |-
;      |  -| B.2   C.3 |<-----|--|--------------| TX         USB+ |-
;      |  -| B.1   C.4 |-     |  |             -| DACR     ADKEY2 |-
;      |  -| B.0   C.5 |      |  |     |\  _   -| DACL     ADKEY1 |-
;   .--|---| 0V  _  V+ |------'  |     | \|+|---| SPK1  ___   IO2 |-
;   |  |   `----' `----'   ___   |  8R |  | |  -| GND  |   |  GND |---.
;   |  `------------------|___|--'     | /|_|---| SPK2 |   |  IO1 |-  |
;   |                                  |/       `------|___|------'   |
;  _|_ 0V                  1K0                                    0V _|_ 
                                                 
Symbol TX        = B.4
Symbol RX        = C.3
Symbol BUSY_PIN  = pinC.2
Symbol BAUD_FREQ = M8
Symbol BAUD      = T9600_8
symbol BAUDPX    = t9600_8
symbol SerialData = b10
Symbol cmd       = b0
Symbol arg       = w1 ; b3:b2
Symbol arg.lsb   = b2
Symbol arg.msb   = b3
Symbol counter   = w2
setint %00000010,%00000010,c
High TX
Pause 2000
cmd = $09 : arg = $0002 : Gosub Send
Pause 4000
cmd = $06 : arg = 40 : Gosub Send
Pause 1000
main:
do
pause 10
loop
Playvoice:
'b11 = 0
  Select case b10
    case 1    'quarter hour
           cmd = $12 : arg = 1 : Gosub Send
    case 2    'half hour
           cmd = $12 : arg = 2 : Gosub Send
    case 3    'three quarter hour
           cmd = $12 : arg = 3 : Gosub Send
    case 4    ' hour
           cmd = $12 : arg = 4 : Gosub Send
           pause 30000
           gosub chime
       case 6    'Chime hour
           cmd = $12 : arg = 6 : Gosub Send

end Select
    B10 = 0

      Pause 1000
      Do While BUSY_PIN = 0
           Pause 100
      Loop
      pause 1000
    return

Send:
  SetFreq BAUD_FREQ
  Pause 10
  SerOut TX, BAUD, ( $7E, $FF, $06, cmd, $00, arg.msb, arg.lsb, $EF )

  arg = 0
  Return
 

interrupt:
b10 = 0
pause 100
  serin  C.1, BAUDPX, b10,b11
PAUSE 100
debug

gosub Playvoice
pause 20
setint %00000010,%00000010,c
return

chime:

Pause 5000
for b12 = 1 to b11
    cmd = $12 : arg = 5 : Gosub Send
    pause 4000
next b12
return
Bill
 

Attachments

Last edited:

Rampz

Well-known member
One of my first Picaxe projects needed to know the time, so it incorporated a DS1307. Time was set using a Sony IR remote - with feedback via a Piezo sounder. It could also confirm the time, using the flashes of a single LED and beeps of the buzzer :)

It added 14 seconds every day at 04:00, to make up for the deficiencies of the eBay clock module I used, so only had to manually change it twice a year for daylight saving.

All a bit clunky, but functional...
Thank you Phil, I went for the ds3231 for its accuracy, I really need to get it to actual real time, I was thinking a routine with a plus and minus button so the time can be altered slightly?

With time being a moving target I wonder how to do it, I guess a third button to take me to setting the time routine.
 

Rampz

Well-known member
I also wrote a clock program some 10 years ago.
It includes :
12 /24 time
temperature
date
alarm
Winchester chimes - on the quarter, half, three quarter and hourly.
The chimes and alarm use a SPE035 MP3 module.
Setting by IR remote
Display is an AXE134Y oled 4 line
Thank you Bill.B I have looked through your project, great work.
 

Jack Burns

New Member
Hippy your code in post#20 and post#22 both work perfectly on real hardware. Tested with a DS3231 and 08M2 (haven't got any X2s).

As a test I tried substituting PPP_DATETIME for a different litereral string, for example "2022-01-17 10:42:50". This also worked fine when running the code in a chip, however the simulator only seems to retreive the computer's current date & time when the following command is executed.

HI2cIn $00, (seconds, minutes, hour, day, day, month, year)

Does PE6 have any settings to alter this behaviour?
 

Jack Burns

New Member
Something is slightly off with the displayed hours it shows >.07.13PM as an example, i haven't looked to see why i think hours are showing as a ">"
Rampz, Updated code (hopefully fixed). See post#18.

Note: Erase picaxe (PE6 'Clear' button) before downloading the new code, otherwise the RTC won't be initialised.
 

Rampz

Well-known member
Rampz, Updated code (hopefully fixed). See post#18.

Note: Erase picaxe (PE6 'Clear' button) before downloading the new code, otherwise the RTC won't be initialised.
Jack Burns yep i noticed nothing made much difference and while looking around the PE6 editor to see if it's time was the same as laptop time i noticed the clear button and wondered if it had any bearing on doing it before a download, So tried the code played with different + seconds and ended up at +16 seconds, i did notice it was still part of a second out.

I am using this code on a 28X2 seems to interchange fine between 20X2 and 28X2 just fine

In my application i really need it spot on, so maybe there is a way to add maybe a 1/10th of a second via a push button that each times increments the time in the ds3231 by that amount, so after programming through a download i can get it spot on, i can make sure i am always slightly behind so it only need to add.

It also looks like the module can go upto 2ppm per year wrong which is 65ish seconds i will need to correct that at a service at the end of each year, so would be good to have a way to sort this error on-site.

I see also there is an ageing register on the DS3231 where a fraction of a ppm can be written to the mdule to take care of error, i guess i would run the module for a month having got the time right and then adjust that ageing register to bring the error lower?

What was wrong with your code for the hour digit, i looked but just couldn't see any issue?

Thank you for your time on my project
 
Last edited:

Rampz

Well-known member
Attached is the data sheet for the DS3231

Aging Offset
The aging offset register takes a user-provided value to
add to or subtract from the codes in the capacitance array
registers. The code is encoded in two’s complement, with
bit 7 representing the sign bit. One LSB represents one
small capacitor to be switched in or out of the capacitance
array at the crystal pins. The aging offset register capaci-
tance value is added or subtracted from the capacitance
value that the device calculates for each temperature
compensation. The offset register is added to the capaci-
tance array during a normal temperature conversion, if
the temperature changes from the previous conversion, or
during a manual user conversion (setting the CONV bit).
To see the effects of the aging register on the 32kHz out -
put frequency immediately, a manual conversion should
be started after each aging register change.
Positive aging values add capacitance to the array, slow-
ing the oscillator frequency. Negative values remove
capacitance from the array, increasing the oscillator
frequency.
The change in ppm per LSB is different at different tem-
peratures. The frequency vs. temperature curve is shifted
by the values used in this register. At +25°C, one LSB
typically provides about 0.1ppm change in frequency.
Use of the aging register is not needed to achieve the
accuracy as defined in the EC tables, but could be used
to help compensate for aging at a given temperature.
See the Typical Operating Characteristics section for a
graph showing the effect of the register on accuracy over
temperature.
 

Attachments

Jack Burns

New Member
Rampz, I combined the 2 chunks of code you got from Marcs by making the RTC initialisation into a subroutine and then made a call to it during the very first power up. As far as possible I kept the code as you supplied it.

I found Marcs code quite difficult to understand compared to Hippy’s, however the problem seemed to be wrong information being loaded into the RTC during initialisation.

If you look at the bottom of the InitialiseRTC routine, you will notice I moved the following line up and also REM’d out a chunk of code Below ‘InitialiseTime:’

HI2Cout $0 , (secs,mins,hours,day,date,month,year)

When I previously experimented with a DS3231, I had a switch which when pressed would synchronise the time to the nearest minute. So I would keep an eye on a radio controlled clock and press my button when the seconds reached zero.

When the button was pushed, the picaxe would set the RTC seconds to zero and adjust the time/date if required.

A value of 29 or less would only change the seconds.

A value of 30 or more would zero the seconds and move the time/date to the following minute.
 

Rampz

Well-known member
When I previously experimented with a DS3231, I had a switch which when pressed would synchronise the time to the nearest minute. So I would keep an eye on a radio controlled clock and press my button when the seconds reached zero.
Jack Burns have you got a bit of code to do the seconds zeroing? That would probably be close enough for initial setup following the basic setup in the code as we have it.
 

PhilHornby

Senior Member
I went for the ds3231 for its accuracy, I really need to get it to actual real time, I was thinking a routine with a plus and minus button so the time can be altered slightly?

With time being a moving target I wonder how to do it, I guess a third button to take me to setting the time routine.
Yes, I probably wouldn't choose a DS1307 these days, although they were not as bad as people make out. The datasheet for the actual IC specifies some physical layout constraints that none of the eBay modules followed and the crystals may not have been the best quality. Once I'd applied my 14 seconds a day correction, I was quite happy with mine - but I was only setting the timing of the hot-water :)
(The best thing about the DS1307, is the 56 bytes of battery-backed RAM!)

When I did need to change the time, the signal from the IR receiver triggered a 20X2 interrupt - so it was just a matter of hitting a "Set" button on the Remote, followed by the 4 digits for the time.

If you want a "maintenance free" solution, then you could consider a GPS receiver. (I would normally reach for an ESP8266 module, getting the time via NTP - but the lack of WiFi up a church clock tower might kibosh that!). There's always those nasty 'Rugby-clock' receivers ... don't know what they're called, now the signal isn't broadcast from Rugby!
 

Rampz

Well-known member
If you want a "maintenance free" solution, then you could consider a GPS receiver. (I would normally reach for an ESP8266 module, getting the time via NTP - but the lack of WiFi up a church clock tower might kibosh that!). There's always those nasty 'Rugby-clock' receivers ... don't know what they're called, now the signal isn't broadcast from Rugby!
Phil I wonder if I will actually get a useable GPS signal in a clock tower, regards the Rugby-clock receiver things, they are used currently on the time regulation, but we find church roof alarms interfere with them and stop them receiving the signal.
I have seen on other people's clock project that they have several screens they scroll though to adjust timing etc, I would want to do something similar, I will have other settings I need to amend by then end of the project
 

marks

Senior Member
Hi rampz,
as phil has said if you want more accurate time a gps would be better.
the ds3231 is quoted as 2ppm so you could expect it to loose a second
every six days.(or 63 seconds over the year).

here's some code to try on a 20x2 just add your oled and rtc
it seemed to work ok
just hit program (make sure to close the serial terminal first)
and dont move the mouse I find it sometimes delays the programming

once completed the terminal will open but you will need patience
it will eventually show date and time after the first minute starts.
and time on the oled in ampm format.
Rich (BB code):
#picaxe 20X2    '  version C.3     * marks                       
                '    .-----_-----.
#terminal 76800 '    | V+     0V |
'               ' -->| SI     SO |-->
'                    | C.7   B.0 |--> LCD DB4
'                    | C.6   B.1 |--> LCD DB5
'                    | C.5   B.2 |--> LCD DB6
'                    | C.4   B.3 |--> LCD DB7
'          LCD E  <--| C.3   B.4 |
'          LCD RS <--| C.2   B.5 |<-> SDA
'                    | C.1   B.6 |<-- HserIN 
'         HserOUT <--| C.0   B.7 |--> SCL
'                    '-----------'                 
     dirsB = %00001111           
     dirsC = %10111111   
    SYMBOL E      = C.3
    SYMBOL RS     = C.2                                                              
    SYMBOL senddata = b0 : SYMBOL sentdata = b0        
   
    TABLE   0, ($3,$3,$2,$2,$8,$0,$C,$0,$1,$0,$2,$0,$6)  '(4Bit)(2line/5x8)(Display On)(Clear Display)(Return Home)(Entry Mode Set)
    TABLE  40, ("marks......")
   
SYMBOL index    = b1  :  SYMBOL CRC      = b1
SYMBOL hours    = b2
SYMBOL mins     = b3
SYMBOL secs     = b4
SYMBOL day      = b5       ' not used
SYMBOL date     = b5
SYMBOL month    = b6 
SYMBOL year     = b7
SYMBOL PM_AM    = b8
SYMBOL D0 = b20
SYMBOL D1 = b21
SYMBOL D2 = b22
SYMBOL D3 = b23
SYMBOL D4 = b24
SYMBOL D5 = b25   
       
SETFREQ M64

IntialiseOLed:
      FOR  index = 0 to 12                                              
READTABLE index, senddata : outpinsB = senddata : PULSOUT E,1 ' Initialise OLED
     NEXT index  :  PAUSE 10   
   
    Display:
       LOW RS                                                                                                                             
       senddata = 128 : outpinsB = senddata >> 4 : PULSOUT E,1 : outpinsB = senddata : PULSOUT E,1'(128-147) Line 1 Cursor Position
       HIGH RS                                                                                                                             
              
       FOR index = 40 TO 50                                                 
READTABLE index, senddata
         outpinsB = senddata >> 4 : PULSOUT E,1 : outpinsB = senddata : PULSOUT E,1 ' sending characters to line one            
       NEXT index

IntialiseTime:
HI2Csetup I2Cmaster, %11010000, I2Cslow_64, I2Cbyte ' DS3231
;;;;   Collect  date and time from the PICAXE Editor
          bptr=28
          FOR  index = 2 TO 18    
            LOOKUP index,(ppp_datetime),@bptrinc      '  ("2015-01-31 10:00:25")
          NEXT index
;;;;    Entering date and time in $BCD  convert from ASCII 
bptr =28
year  = @bptrinc*$10+@bptrinc-$30 :inc bptr          ' $0 to $99
month = @bptrinc*$10+@bptrinc-$30 :inc bptr          ' $1 to $12
date  = @bptrinc*$10+@bptrinc-$30 :inc bptr          ' $1 to $31
hours = @bptrinc*$10+@bptrinc-$30 :inc bptr          ' $0 to $23
mins  = @bptrinc*$10+@bptrinc-$30 :inc bptr          ' $0 to $59
secs  = @bptrinc*$10+@bptrinc-$30                    ' $0 to $59

    READ  255,CRC : IF CRC <> 0 THEN Main     ' Skip if time already programmed 
      HI2Cout $0 , (secs,mins,hours,day,date,month,year) '    
    Adjustsecs:
    HI2Cin  $0 , (secs)
    IF secs <> $38 THEN adjustsecs
     secs  = secs + $21                                  ' add seconds (how long it takes for to program)
      HI2Cout $0 , (secs)
    WRITE 255,53                                         ' we can make use of an existing eeprom entry
  
Main:   
      HI2Cin  $0 , (secs,mins,hours,day,date,month,year) ' Read DS3231
                                   ;
        FOR bptr = 2 TO 7 
          @bptr = @bptr/16*250+@bptr                     ' Convert BCD to Binary
        NEXT
 
     PM_AM = "P" : IF hours <12 THEN : PM_AM ="A": ENDIF ' Indicate PM or AM
  hours = hours //12 : IF hours =0 THEN: hours =12 : ENDIF ' 24 to 12 hour format
D5 = hours  /10 +$30 : IF D5 = "0" THEN : D5 = " " : ENDIF 'Zero blanking  
D4 = hours //10 +$30
D3 = mins /10 +$30
D2 = mins //10 +$30
D1 = secs /10 +$30
D0 = secs //10 +$30   
   
DisplayOled:
       LOW RS                                                                                                                             
       senddata = 128 : outpinsB = senddata >> 4 : PULSOUT E,1 : outpinsB = senddata : PULSOUT E,1'(128-147) Line 1 Cursor Position
       HIGH RS                                                                                                                             
     
         FOR  index = 0 TO 11                                ' sending characters to LCD line one
LOOKUP index,(" ",D5,D4,":",D3,D2,":",D1,D0," ",PM_AM,"M"),senddata
    outpinsB = senddata >> 4 : PULSOUT E,1 : outpinsB = senddata : PULSOUT E,1'
NEXT index

DisplayTerminal:
      sertxd (" ",#date,"/",#month,"/20",#year,"  ")             ' (Date dd/mm/20yy)
      sertxd (" ",D5,D4,":",D3,D2,":",D1,D0," ",PM_AM,"M",cr,lf) ' (Time HH:MM:SS AM)

GOTO main
 
Last edited:

hippy

Technical Support
Staff member
Something is slightly off with the displayed hours it shows >.07.13PM as an example, i haven't looked to see why i think hours are showing as a ">"
That sounds like it's displaying BCD as if decimal or the chip is incrementing invalid data written to it.

With time being a moving target I wonder how to do it, I guess a third button to take me to setting the time routine.
My preference is for a button which resets to 'the nearest hour'. That way it's easy to sync things up to 'on the hour pips' or other references.

In my application i really need it spot on, so maybe there is a way to add maybe a 1/10th of a second via a push button that each times increments the time in the ds3231 by that amount,
The only way to do that would be to disable the internal clocking and then enable it again, and one would have to try it to see if it actually works.

But do you really need it "spot on" ? How would you even know it was 100ms out of sync ?

I see also there is an ageing register on the DS3231 where a fraction of a ppm can be written to the mdule to take care of error, i guess i would run the module for a month having got the time right and then adjust that ageing register to bring the error lower?
I suspect you are inviting a world of pain in trying to tweak aging registers. My expectation would be of getting stuck in a see-saw between too fast and too slow, never being exactly right.

the simulator only seems to retreive the computer's current date & time when the following command is executed ... Does PE6 have any settings to alter this behaviour?
Not that I am aware of. One way to do it would be to have a routine which adjusts the value read to be what you'd like before returning from the read routine.

f the time or date is a single figure, the say the time can show as 21:3:9 when may look better showing as 21:03:09
Yes, that was just the basic code to show the functionality, without the clutter of number formatting routines.
 

David_Reynolds

Well-known member
The grabbing of the pendulem would need calibration, this being letting the actuator run for say 1 second and stop, check that the pendulem is still swinging from its sensor then repeat until there is no more pendulem signal, count how many 1 seconds was needed to do this and save that in eeprom for use when its needed, its fine for this to be done at start up and not expecting the system to work while this is being done, if that helps.


Hi. Rampz

I just had a quick look in,, I have missed something.
I would like to know if I may what you are going to hold the pendulum on with when all power is gone?
Just my inquisitive nature, I have it in mind that you will need power to grab the thing when the mains stops, or when you use a battery.
So if using an electromagnet it will need a constant supply.
 
Top