DS3231 Clock data display

Bayside888

Active member
I have used the Wizard to program/set a DS3231 RTC. I am getting consistent but mostlly incorrect results when displaying to terminal with sertxd. The code is below, along with the results. Any help appreciated. Regards:
Code:
symbol secs     = b0
symbol mins     = b1
symbol hour     = b2
symbol dow      = b3 : symbol chkdow   = b9
symbol day      = b4 : symbol chkday   = b10
symbol month    = b5 : symbol chkmonth = b11
symbol year     = b6 : symbol chkyear  = b12
symbol century  = b7
symbol control  = b8 : symbol chkctrl  = b13

  hi2csetup i2cmaster, %11010000, i2cslow, i2cbyte

  century = $20
  year    = $25
  month   = $04
  day     = $23
  dow     = $04
  hour    = $15
  mins    = $17
  secs    = $45
  control = $04

  hi2cout 0, (secs, mins, hour, dow, day, month, year)
  hi2cout $0E, (control,0)
  pause 50

  hi2cin 3, (chkdow, chkday, chkmonth, chkyear)
  if chkdow   <> dow     then fail
  if chkday   <> day     then fail
  if chkmonth <> month   then fail
  if chkyear  <> year    then fail

ok:
  sertxd("Time set okay",cr,lf)
  pause 1000
     sertxd(#month,"/",#day,"/",#year,"  ",#hour,":",#mins,":",#secs,cr,lf)
  goto ok

fail:
  sertxd("Time setting failed",cr,lf)
  pause 1000
  goto fail

this is the result:

Time set okay
4/35/37 21:23:69

should be: 4/23/25 15:17:09

should be
 
The data read from the RTC is in binary coded decimal format but in the sertxd command the values are being printed as if they are decimal. The year (2025) is stored as 0x25 in the RTC which is 37 in decimal, hence 37 being printed as the year.

To display the values correctly, you could use the bcdtoascii command.
 
Nick: after almost a year with the PicAxe, I'm still not sure how to use that command and the manual is no help. Can you give me an example for the "secs" variable, for example?

bcdtoascii secs, ??????

Thanks
 
OK -- got it working correctly. A lot of program steps and variables used. I will have to try to reduce the code and re-use variables. Thanks for your help. I did learn a few things about the language.
 
Hi,

The BCDTOASCII instruction is in the on-line command reference, but personally (as with BINTOASCII) I rarely use it because it is quite "greedy" with its variable requirements. BCDTOASCII is effectively a "Macro" which splits a Binary Coded Decimal number into separate digits and then adds 48 (alternatively represented as "0") to each digit, to turn it into an ASCII character, ready for "printing". Thus the separate characters do NOT need the normal prefix of # to convert to printable characters.

The issue is that the High 4 bits of a BCD number (or the High "Nibble") are treated as a separate number so have a "weight" of 16 instead of 10. PICaxe Basic has the convenient prefix "$" which uses two "HEX(adecimal)" characters (0 to 9 followed by A,B,C,D,E,F) to represent its first and second Nibbles. Thus the first nibble is automatically multiplied by 16 instead of the normal 10 (i.e "$45" = 4 * 16 + 5 = 69) which is what the DS3231 requires. Unfortunately, there is no equivalent Prefix to convert HEXadecimal numbers back into Decimal (or Denary) numbers.

It's too late tonight to explain how I would do it, but perhaps tomorrow. ;)

Cheers, Alan.
 
Last edited:
I had a similar problem a few years ago. This is the routine I wrote to display BCD:
Rich (BB code):
'Definitions
Symbol   Temp = b0
Symbol   bcdVal = b1
Symbol mskLoNibble   = %00001111
Symbol mskHiNibble   = %11110000


'Revise code for main loop
bcdVal = century: Gosub ShowBCD
bcdVal = year: Gosub ShowBCD

'Subroutines
ShowBCD: Temp = bcdVal & mskHiNibble / 16
         SerTxd (#Temp)
         '
         Temp = bcdVal & mskLoNibble
         SerTxd (#Temp," ")
         Return
 
I had a similar problem a few years ago. This is the routine I wrote to display BCD:
Rich (BB code):
'Definitions
Symbol   Temp = b0
Symbol   bcdVal = b1
Symbol mskLoNibble   = %00001111
Symbol mskHiNibble   = %11110000


'Revise code for main loop
bcdVal = century: Gosub ShowBCD
bcdVal = year: Gosub ShowBCD

'Subroutines
ShowBCD: Temp = bcdVal & mskHiNibble / 16
         SerTxd (#Temp)
         '
         Temp = bcdVal & mskLoNibble
         SerTxd (#Temp," ")
         Return
This augments Nick's previous post, both allowing me to get to the real objective with the RTC, which is to perform tests on the time - how long a process lasts, etc. That, of course, requires Denary (haven't seen that word in years, Nick!) values to make calculations easier. So, I added the following little code to replace the BCD value from the RTC, back into the variables (added TempHi and tempLo to Pete's code:

century = TempHi*10+tempLo 'put decimal value in clock variables

hope this helps later users of the forum. I may post the entire working code for reading the DS3231, displaying on terminal, LCD, and storing denary numbers
 
Hi,

If you ONLY want to load explicit values into a DS3231 then using the # prefix to the digits is probably as good as anything. But, although I dislike (excessively) "long" variable names, it could be wise to use symbols such as secsBCD as a "reminder" that you can't manipulate them as "normal" numbers (and not even print them out directly). Similarly a fully Symbolised expression could be BCDTOASCII secsBCD , secsX10ascii , secsascii so that the corresponding "Printing" instruction would be SERTXD ( secsX10ascii , secsascii ) instead of the normal SERTXD ( #secs ) where secs is a pure binary number.

Therefore, if I were planning to do ANY numerical processing (or even just printing out the values) I would normally store and keep the numbers in "pure" (binary) format. Converting Binary numbers to Binary Coded Decimal is really quite easy (provided that the Binary number is no greater than 99) : For example you can separate the two digits (nibbles) from the byte, multiply the High (tens) digit by 16 and add the two values together again, but (because PICaxe Basic doesn't permit brackets) this needs two stages, i.e. units = secs AND 15 : secsBCD = secs / 10 * 16 + units (or ideally avoid the "Magic Numbers" 15 and 16 by using the constants mskLoNibble and mskHiNibble as from post #6 ).

However, we can take advantage of the fact that for each unit (or step) in the High Nibble, the "BCD" number is 6 units larger than the Binary number. Therefore we can simply convert from Binary to BCD with the expression secsBCD = secs / 10 * 6 + secs (e.g. working left to right in sequence: 45 / 10 * 6 + 45 becomes 4 * 6 + 45 = 24 + 45 = 69 ). Note that even though the variable secs is used twice, the expression will still work correctly if the result is self-assigned to secs, i.e. secs = secs / 10 * 6 + secs is perfectly acceptable, provided that you (or the program) "remembers" which format is currently being used.

The reverse operation from BCD to Binary is also quite easy, but there is the complication that PICaxe doesn't "officially" support negative numbers, whilst for a "one-liner" expression the number that needs to be subtracted is calculated first. A trick is to define a Symbol such as minus6, i.e. SYMBOL minus6 = 250 . It's possible to set a variable to 0 - 6 , which will be reported as 250 (or 65530 for a Word variable), but the compiler generally won't allow a Constant to be symbolised as "0 - 6". However, the following expression will still work correctly : secs = secsBCD / 16 * minus6 + secsBCD (e.g. secs = 69 / 16 * (0-6) + 69 becomes 4 * (0-6) + 69 = (0-24) + 69 = 45 ) .

Of course you will probably also need a conversion for mins and maybe many other BCD variables. You could Copy / Edit / Paste for each variable name, or create a Subroutine, or a Macro (#Define). But I prefer to use the system's "Byte Pointer" (@bptr) either over a range of the existing Registers (b0 - b27), or alternatively the "Indirect RAM", if running out of variable space. Thus the following Program should convert all the bytes within the selected range to (and from) BCD in a single process:

Code:
#picaxe 08M2    ; Or any other
Symbol SecsPtr = 10       ; Can be outside normal "named variables" space
Symbol Secs = b10         ; Optional (not used in this example)
Symbol Mins = b11
; ..... etc..
Symbol Century = b17      ; Optional
Symbol CentPtr = 17
Symbol minus6 = 250     ; Or 65530 for Word calculations
POKE SecsPtr , 45,17,15,04,23,04,25,20    ; Store some Test data
Do
AllBinToBCD:         ; Routine to convert group of Binary-Coded bytes to BCD 
   FOR bptr = SecsPtr TO CentPtr
      @bptr = @bptr / 10 * 6 + @bptr
   NEXT bptr
Wait 5
AllBCDToBin:       ; Routine to convert group of BCD to Binary-Coded bytes 
   FOR bptr = SecsPtr TO CentPtr
      @bptr = @bptr / 16 * minus6 + @bptr
   NEXT bptr
Wait 5
Loop

Cheers, Alan.
 
Last edited:
Alan: I have seen the @bptr command in some forum posts, but that is a really good example of the usage. Really condenses code space. Thanks everyone for your help. My code is working well -- the DS3231 is keeping better time than the wall clock in my office (which, ok,, was from Amazon, but still...) Regards: David
 
Back
Top