DS1307 decimal and BCD confusion

lpsarsam

Member
Hi :)
I have a pic 18m2 wired up to the DS1307 time chip via I2C. As an output, I have two 7 segment displays connected to two 4026 chips. So a pulse out of B.5 will increment the value on the 7 segment displays and a pulse out of B.6 will reset the displays back to 00.

I would like to made a program that reads the seconds value on the DS1307 and outputs that onto the displays.

I attempted to do it like this


Code:
pause 1000
i2cslave %11010000,i2cslow, i2cbyte ;setup device as slave
readi2c 0, (b0) ;read seconds value from it into b0

for b1 =0 to b0 ;loop amount of seconds in b1
pulsout b.5,1 ;increment number on 7 seg
next ;finish loop

end
The issue is, I get weird results that are over 60 seconds. I presume this is due to me reading a BCD number from the clock chip and looping the decimal equivalent of that number.
Do any of you know how I could make this work properly?

Thanks :D
 

nick12ab

Senior Member
Convert the DS1307 reading to decimal. Unfortunately because it's an M2 part, you can't just use BCDTOBIN but there's another way:
Code:
b2 = b0 / 16 * 10
b3 = b0 AND $0F
b0 = b2 + b3
b0 will now have the decimal value in it.
 

giuseppe

Member
Hi :)
I have a pic 18m2 wired up to the DS1307 time chip via I2C. As an output, I have two 7 segment displays connected to two 4026 chips. So a pulse out of B.5 will increment the value on the 7 segment displays and a pulse out of B.6 will reset the displays back to 00.

I would like to made a program that reads the seconds value on the DS1307 and outputs that onto the displays.

I attempted to do it like this


Code:
pause 1000
i2cslave %11010000,i2cslow, i2cbyte ;setup device as slave
readi2c 0, (b0) ;read seconds value from it into b0

for b1 =0 to b0 ;loop amount of seconds in b1
pulsout b.5,1 ;increment number on 7 seg
next ;finish loop

end
The issue is, I get weird results that are over 60 seconds. I presume this is due to me reading a BCD number from the clock chip and looping the decimal equivalent of that number.
Do any of you know how I could make this work properly?

Thanks :D
When you say read it out to the display I presume you just want it just shown and you are doing this by the incrementing process you showed. That should work, however, the problem is that you are using the BCD representation and not the decimal representation. What I would do is convert BCD to ascii and then ascii to decimal parts(seperate digits for each place). Then after that, take those parts and make the decimal number. Here is a try at it (I am not an expert member but I'l' try):

Code:
#picaxe 18M2

symbol seconds	  =     b0	'holds seconds
symbol secondtens   =     b8	'holds tens place of the second
symbol secondones   =     b9  'holds ones place of the second
symbol counter      =     b1  'counter for loops

symbol increment    =     b.5 'increment pin

Read_Seconds:
i2cslave %11010000,i2cslow, i2cbyte ;setup device as slave
readi2c 0, (seconds) ;read seconds value from it into b0

Convert_Seconds:
bcdtoascii seconds,secondtens,secondones	'convert hour data to ascii
let secondtens = secondtens - $30		'convert "secondtens" to decimal
let secondones = secondones - $30		'convert "secondones" to decimal

'do seconds = (secondtens x 10) + (secondones x 1)
let seconds = secondtens * 10	     '(secondtens x 10)
let seconds = seconds + secondones '(secondtens x 10) + (secondones x 1)

Display_Seconds:
for counter = 0 to seconds ;loop amount of seconds in b1
	pulsout increment,1 ;increment number on 7 seg
next ;finish loop

goto Read_Seconds 'if you want to refresh the display
Let me know how this goes.
 

giuseppe

Member
Convert the DS1307 reading to decimal. Unfortunately because it's an M2 part, you can't just use BCDTOBIN but there's another way:
Code:
b2 = b0 / 16 * 10
b3 = b0 AND $0F
b0 = b2 + b3
b0 will now have the decimal value in it.
Ha this is what you suggested to me when I had a similar issue for optimization.

nick12ab's answer is soooooo much better.
 

westaust55

Moderator
If you do not need to retain the BCD value in variable b0 then you can save on a byte variable and 5 bytes of program space with:
Code:
b2 = b0 / 16 * 10
b0 = b0 AND $0F + b2
 

hippy

Ex-Staff (retired)
Bingo!

Both solutions worked, one was just a bit neater than the other :p
How does this measure up for neatness ? Not short and not very obvious but I just knew there was a one line maths solution which did not need to use additional variables ...

b0 = b0 / 16 * 6 ^ $FFFF + 1 + b0

Demo code which runs in the simulator or real chip ...

Code:
b0 = $09 : Gosub Convert
b0 = $18 : Gosub Convert
b0 = $27 : Gosub Convert
b0 = $36 : Gosub Convert
b0 = $45 : Gosub Convert
b0 = $54 : Gosub Convert
b0 = $63 : Gosub Convert
b0 = $72 : Gosub Convert
b0 = $81 : Gosub Convert
b0 = $90 : Gosub Convert
Stop

Convert:
  b0 = b0 / 16 * 6 ^ $FFFF + 1 + b0
  SerTxd(#b0," ")
  Return
Code:
For b1 = $00 to $90 Step $10
  For b2 = $0 To $9
    b0 = b1 | b2 : Gosub Convert
  Next
Next
Stop

Convert:
  b0 = b0 / 16 * 6 ^ $FFFF + 1 + b0
  SerTxd(#b0," ")
  Return
The trick was thinking in a different way; how would one need to adjust the decimal value of the BCD number as it is to get what one actually wants ...

$10 = 16, should be 10, so need to subtract 6
$20 = 32, should be 20, so need to subtract 12
$30 = 48, should be 30, so need to subtract 18
$40 = 64, should be 40, so need to subtract 24
$50 = 80, should be 50, so need to subtract 30
etc

So there's a very obvious and convenient pattern; the number we need to subtract is the most significant BCD nibble * 6. What we'd like is -

b0 = b0 - ( (b0/16) * 6 )

That won't syntax check so we need to rearrange it to something which does. Subtraction is commutative so we can create a negative number and add to it and get the same result. That is x=y-z is the same as x=(-z)+y ...

b0 = b0 - ( (b0/16) * 6 )

becomes ...

b0 = ( - ( (b0/16) * 6 ) ) + b0

For 16-bit two's complement arithmetic, to negate we can invert the value and add one, and inversion is the same as XOR'ing with $FFFF ...

b0 = ( ( ( (b0/16) * 6 ) ^ $FFFF ) + 1 ) + b0

And bingo, precedence of operation there matches the left to right nature of PICAXE maths ...

b0 = b0 / 16 * 6 ^ $FFFF + 1 + b0

Added : D'Oh!

Why multiply by six and then negate when you can multiply by minus six ?

Even neater ...

b0 = b0 / 16 * $FFFA + b0
 
Last edited:

hippy

Ex-Staff (retired)
I did wake up wondering why I wasn't using the more obvious ...

b0 = - b0 / 16 * 6 + b0

But the problem there is that division treats -b0 as unsigned so divide by 16 doesn't keep it negative. The same with the variant below (6*16=96) ...

b0 = -96 * b0 / 16 + b0

Thus the negation has to be done after the division, not before it.

As the 'magic number', $FFFA, is not exactly obvious in the maths ...

b0 = b0 / 16 * $FFFA + b0

I'd recommend in a real program defining "MINUS_6" and using that in the maths lines. That avoids hard to detect typo errors in the number and makes it clearer to the reader what it is doing ...

Symbol MINUS_6 = $FFFA ' -6 = 0 - 6 & $FFFF

b0 = b0 / 16 * MINUS_6 + b0

I'll add a Code Snippets entry later.
 

giuseppe

Member
I did wake up wondering why I wasn't using the more obvious ...

b0 = - b0 / 16 * 6 + b0

But the problem there is that division treats -b0 as unsigned so divide by 16 doesn't keep it negative. The same with the variant below (6*16=96) ...

b0 = -96 * b0 / 16 + b0

Thus the negation has to be done after the division, not before it.

As the 'magic number', $FFFA, is not exactly obvious in the maths ...

b0 = b0 / 16 * $FFFA + b0

I'd recommend in a real program defining "MINUS_6" and using that in the maths lines. That avoids hard to detect typo errors in the number and makes it clearer to the reader what it is doing ...

Symbol MINUS_6 = $FFFA ' -6 = 0 - 6 & $FFFF

b0 = b0 / 16 * MINUS_6 + b0

I'll add a Code Snippets entry later.
Psh, cheater :p
 
Top