Convert.bas

General discussion relating to the library modules supplied with the compiler

Moderators: David Barker, Jerry Messina

Post Reply
E_VH
Posts: 12
Joined: Fri Dec 09, 2011 3:08 pm
Location: Belgium

Convert.bas

Post by E_VH » Wed Dec 14, 2011 9:12 pm

Want to use an RTC PC8583

Must convert from and to BCD format.

Get curious result ? Tested with following program

[code]

Device = 18F452
Clock = 20

Config OSC = HS, OSCS=ON, PWRT=ON, WDT=OFF, LVP=OFF

'---LCD module options
#option LCD_DATA = PORTD.4
#option LCD_RS = PORTD.2
#option LCD_EN = PORTD.3

Include "ISRRX.bas"
Include "USART.bas"
Include "LCD.bas"
Include "Convert.bas"
Include "utils.bas"
Include "string.bas"

Const DaysOfMonth(12) As Byte = (31,28,31,30,31,30,31,31,30,31,30,31),
DaysOfWeek(7) As String = ("Mon","Tue","Wed","Thu","Fri","Sat","Sun")

Dim Tst As Word
Dim Result As Word
Dim Res_2 As Word


SetAllDigital

' program start...

DelayMS(500)

LCD.Cls
LCD.Write("Begin")
Tst = 125
Result = DecToBCD(Tst)
Res_2 = BCDToDec(Result)

LCD.WriteAt (2,1, BinToStr(Res_2,12) )
LCD.WriteAt (3,1, BinToStr(Result,12) )


LCD.WriteAt (4,1, "Einde")

End
[/code]

dec 125 = %0111 1101 Res_2 = OK

dec 125 = BCD %1100 0101 MUST BE %0001 0010 0101 ???
----------------------------------------------------1------2-----5
Checked the function DecToBCD seems to be correct ?

And how with the wrong BCD you can get the correct answer (Res_2) ?

Jerry Messina
Swordfish Developer
Posts: 1473
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Wed Dec 14, 2011 9:25 pm

If you look at the BCD functions, you'll see they only work with packed bytes up to a value of 99

E_VH
Posts: 12
Joined: Fri Dec 09, 2011 3:08 pm
Location: Belgium

Post by E_VH » Thu Dec 15, 2011 5:57 pm

Didn't fully understand "packed BCD"

In any case for my RTC I didn't need bigger numbers than 99.

Still have problems to send and read back the bytes.

When to use "I2C_ACKNOWLEDGE"

[code]
Const W_Add = $A0 //RTC write address
Const R_Add = $A1 //RTC read address


Public Structure TDate
Day As Byte // Date (0..31)
Month As Byte // Month (1..12)
Year As Byte // Year (0..99)
DayOfWeek As Byte // day of the week (1..7)
LeapYear As Byte
End Structure

Dim Date As TDate

Sub GetTime()
SI2C.Start
SI2C.WriteByte(W_Add)
SI2C.WriteByte($00) // RTC address 0
SI2C.Restart
SI2C.WriteByte(R_Add)
SI2C.WriteByte$02) //Address
Time.Second = BCDToDec(SI2C.ReadByte()) //2
Time.Minute = BCDToDec(SI2C.ReadByte()) //3
Time.Hour = BCDToDec(SI2C.ReadByte() And %00111111) //4
LyDd = SI2C.ReadByte() //5
WdMo = SI2C.ReadByte() //6
SI2C.Stop

Date.DayOfWeek = (WdMo And %11100000) >> 5

Tmp = (WdMo And %00011111)
Date.Month = BCDToDec(Tmp)

Date.LeapYear = (LyDd And %11000000) >> 6

Tmp = (LyDd And %00111111)
Date.Day = BCDToDec(Tmp)

End Sub


SetAllDigital
SI2C.Initialize()

INTCON2.7 = 0

[/code]

RTC address 2 seconds in BCD
RTC address 3 minutes in BCD
RTC address 4 hours bit 6-7 : PM/AM-12/24 bit 0-5 : BCD
RTC address 5 year/Date bit 6-7 : leap year = 0-3 bit 0-5 : day in BCD

RTC address 6 Weekday/Month bit 5-7 : binary WD bit 0-4 : month in BCD

display on LCD with
LCD.WriteAt(1,1,"Date:", DecToStr(Date.Day,2) ....)

When I do the convertions manualy all = OK

The RTC is running and OK when I plugin another PIC/program time and date are correcty displayed ?

Jerry Messina
Swordfish Developer
Posts: 1473
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Thu Dec 15, 2011 6:19 pm

Packed BCD means two four-bit nibbles (0-9) packed into one byte

I've never used that chip, but the sequence to read doesn't look correct.
To read from a slave you usually send

START
SLAVE ADDR + WR
REG ADDR
RESTART
SLAVE ADDR + RD
<read n bytes>
STOP


Try

Code: Select all

Sub GetTime()
SI2C.Start
SI2C.WriteByte(W_Add)  // RTC slave addr + WR
SI2C.WriteByte($02)       // read register addr 2 (seconds)
SI2C.Restart
SI2C.WriteByte(R_Add)   // RTC slave addr + RD
Time.Second = BCDToDec(SI2C.ReadByte()) // reg 2
Time.Minute = BCDToDec(SI2C.ReadByte()) // reg 3
Time.Hour = BCDToDec(SI2C.ReadByte() And %00111111) // reg 4
LyDd = SI2C.ReadByte() //reg 5
WdMo = SI2C.ReadByte() //reg 6
SI2C.Stop

E_VH
Posts: 12
Joined: Fri Dec 09, 2011 3:08 pm
Location: Belgium

Post by E_VH » Fri Dec 16, 2011 2:31 pm

doesn' t change the result

Tried to visualise the byteread using BinToStr whitout the BCD conversion = rubbish

Changed the
SI2C.WriteByte(W_Add)
SI2C.WriteByte($02) -> $01 thinking this set to read next byte = NOK

Even comparing the ASM files from the working pic file compiled with other basic compiler they are both equivalent.

Proc set_time()
I2CWrite sda, scl, w_add, 0, 0x80 'Stop counting
I2CWrite sda, scl, w_add, 2, _s
I2CWrite sda, scl, w_add, 3, _m
I2CWrite sda, scl, w_add, 4, _h
I2CWrite sda, scl, w_add, 5, _lydd
I2CWrite sda, scl, w_add, 6, _wdmon
I2CWrite sda, scl, w_add, 0, 0x00 'Start counting
End Proc

Proc set_rtc_ram(_ly As Byte, _yyyy As Word)
I2CWrite sda, scl, w_add, ly_reg, _ly
I2CWrite sda, scl, w_add, yyyy_reg, _yyyy.LB, _yyyy.HB
End Proc

Proc read_time()
I2CRead sda, scl, r_add, 2, _s
I2CRead sda, scl, r_add, 3, _m
I2CRead sda, scl, r_add, 4, _h
I2CRead sda, scl, r_add, 5, _lydd
I2CRead sda, scl, r_add, 6, _wdmon
End Proc




Maybe the communication speed is set to high ?

How can I see at witch speed the communication works ?
Can this speed be changed ?[code][/code]

Jerry Messina
Swordfish Developer
Posts: 1473
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Fri Dec 16, 2011 3:02 pm

Maybe the communication speed is set to high ?

How can I see at witch speed the communication works ?
Can this speed be changed ?
You're using the software I2C module, which will run at < 100KHz so speed won't be a problem.

If you look at the code in SI2C.BAS, you'll see

Code: Select all

{
****************************************************************************
* Name    : Delay (PRIVATE)                                                *
* Purpose : Delay a fixed number of microseconds                           *
****************************************************************************
}
inline sub Delay()
   delayus(5)
end sub
{
****************************************************************************
* Name    : ToggleClock (PRIVATE)                                          *
* Purpose : Clock the I2C bus                                              *
****************************************************************************
}
inline sub ToggleClock()
   high(SCL)
   Delay
   low(SCL)
   Delay
end sub
which controls the clock speed.
Even comparing the ASM files from the working pic file compiled with other basic compiler they are both equivalent.
I doubt that. The sequences from the other compiler appear to be radically different from what you posted. I2CRead and I2CWrite are doing entire bus transactions for each byte, at least from what I can tell.

E_VH
Posts: 12
Joined: Fri Dec 09, 2011 3:08 pm
Location: Belgium

Post by E_VH » Fri Dec 16, 2011 4:09 pm

Are the speed not predefined / fixed 100K 400K ?

In any case changing the 5µs delay to 10µs didn't change the problem.

Any suggestions to furthermore finding a solution.

in advance thanks for helping

Jerry Messina
Swordfish Developer
Posts: 1473
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Fri Dec 16, 2011 4:29 pm

Are the speed not predefined / fixed 100K 400K ?
The SI2C.BAS uses software to bit-bang the I2C on any pins, so the speed is purely set by the delays and code execution. The I2C.BAS module uses the hardware MSSP peripheral, which can run much faster.

If your hardware is working with another program, that's good.

Could you post the actual code you're using and which pins you're using for the I2C interface? I'll take a closer look at the datasheet for the RTC.

E_VH
Posts: 12
Joined: Fri Dec 09, 2011 3:08 pm
Location: Belgium

Post by E_VH » Fri Dec 16, 2011 4:45 pm

"If your hardware is working with another program, that's good. "

Other compiler less powerfull than Swordfish.

This the actual test code I use.
SetTime is commented out. Clock is running

[code]

Device = 18F452
Clock = 20

Config OSC = HS, OSCS=ON, PWRT=ON, WDT=OFF, LVP=OFF

'---LCD module options
#option LCD_DATA = PORTD.4
#option LCD_RS = PORTD.2
#option LCD_EN = PORTD.3

#option I2C_SDA = PORTB.2
#option I2C_SCL = PORTB.1

Include "Convert.bas" // other system libraries used
Include "LCD.bas"
Include "utils.bas" //
Include "SI2C.bas" //

// program constants
Const DaysOfMonth(12) As Byte = (31,28,31,30,31,30,31,31,30,31,30,31),
DaysOfWeek(7) As String = ("Mon","Tue","Wed","Thu","Fri","Sat","Sun")

Const W_Add = $A0
Const R_Add = $A1

Dim SetDateTime As PORTB.7 // Sw1

// program structured variables
Structure TTime
Second As Byte // Second (0..59)
Minute As Byte // Minute (0..59)
Hour As Byte // Hour (0..11 or 0..23)
End Structure

Dim Time As TTime


Public Structure TDate
Day As Byte // Date (0..31)
Month As Byte // Month (1..12)
Year As Byte // Year (0..99)
DayOfWeek As Byte // day of the week (1..7)
LeapYear As Byte
End Structure

Dim Date As TDate

Dim LyDd As Byte
Dim WdMo As Byte
Dim Tmp As Byte

Function LeapYear(ByVal Year As Byte) As Boolean
Year = Year + 2000 // scale to correct date
Result = False // reset the function result
If Year Mod 4 = 0 Then // Mod returns the remainder of a division
Result = True
If Year Mod 100 = 0 Then
If Year Mod 400 <> 0 Then
Result = False
EndIf
EndIf
EndIf
End Function

// increment by one day, handling month/year rollovers at the same time
Sub IncrementDay(ByRef Day, Month, Year, MaxDays As Byte)
If Day = MaxDays Then
Day = 1
If Month = 12 Then
Month = 1
Inc(Year)
Else
Inc(Month)
EndIf
Else
Inc(Day)
EndIf
End Sub

// increment the day of week
Sub IncrementDayOfWeek(ByRef DayOfWeek As Byte)
Inc(DayOfWeek)
If DayOfWeek = 8 Then
DayOfWeek = 1
EndIf
End Sub

Sub SetTime(ByRef Hour, Minute, Second, DayOfWeek, Day, Month, LY As Byte)
LyDd = ( DecToBCD(Day) Or (LY << 6) )
WdMo = ( DecToBCD(Month) Or ( DayOfWeek << 5))
SI2C.Start
SI2C.WriteByte(W_Add) // Send the RTC address, and put it in write mode
SI2C.WriteByte($00) // Move the pointer to first register
SI2C.WriteByte($80) // Stop counting
SI2C.Restart
SI2C.WriteByte(W_Add)
SI2C.WriteByte($02)
SI2C.WriteByte(DecToBCD(Second)) //2 Write each byte
SI2C.WriteByte(DecToBCD(Minute)) //3
SI2C.WriteByte(DecToBCD(Hour)) //4
SI2C.WriteByte(DecToBCD(LyDd)) //5 Ly + dd
SI2C.WriteByte(DecToBCD(WdMo)) //6 wd + mon
SI2C.WriteByte(W_Add)
SI2C.WriteByte($00) // Start counting
SI2C.WriteByte($00)
SI2C.Stop
End Sub

Sub GetTime()
SI2C.Start
SI2C.WriteByte(W_Add)
SI2C.WriteByte($02)
SI2C.Restart
SI2C.WriteByte(R_Add)
Time.Second = SI2C.ReadByte() //BCDToDec(SI2C.ReadByte()) //2 I2C_ACKNOWLEDGE
Time.Minute = SI2C.ReadByte() //BCDToDec(SI2C.ReadByte()) //3
Time.Hour = SI2C.ReadByte() //BCDToDec(SI2C.ReadByte() And %00111111) //4
LyDd = SI2C.ReadByte() //5
WdMo = SI2C.ReadByte() //6
SI2C.Stop

Date.DayOfWeek = (WdMo And %11100000) >> 5

Tmp = (WdMo And %00011111)
Date.Month = BCDToDec(Tmp)

Date.LeapYear = (LyDd And %11000000) >> 6

Tmp = (LyDd And %00111111)
Date.Day = BCDToDec(Tmp)

End Sub

// return a three letter month abbreviation
Function MonthToMMM(ByRef Month As Byte) As String(4)
Select Month
Case 1
Result = "Jan"
Case 2
Result = "Feb"
Case 3
Result = "Mar"
Case 4
Result = "Apr"
Case 5
Result = "May"
Case 6
Result = "Jun"
Case 7
Result = "Jul"
Case 8
Result = "Aug"
Case 9
Result = "Sep"
Case 10
Result = "Oct"
Case 11
Result = "Nov"
Case 12
Result = "Dec"
End Select
End Function


// start of program
SetAllDigital
SI2C.Initialize()
Input(SetDateTime)
INTCON2.7 = 0

DelayMS(1000) // allow the circuit to power up and stabilise
LCD.Cls
DelayMS(500)
LCD.WriteAt(4,1, "Getting Date Time")


Time.Second = 25
Time.Minute = 39
Time.Hour = 14

Date.Day = 14
Date.Month = 12
Date.Year = 11
Date.DayOfWeek = 1
Date.LeapYear = 3

//SetTime(Time.Second, Time.Minute, Time.Hour, Date.DayOfWeek, Date.Day, Date.Month, Date.LeapYear )

GetTime()

LCD.WriteAt(1,1, BinToStr(Time.Second,12) )
LCD.WriteAt(2,1, BinToStr(Time.Minute,12) )
LCD.WriteAt(3,1, BinToStr(Time.Hour,12) )
{
LCD.WriteAt(1,1,"Date:", DecToStr(Date.Day,2), "-", DecToStr(Date.Month,2), "-", DecToStr(2000 + Date.Year,4))
DelayMS(5)
LCD.WriteAt(2,1,"Time:", DecToStr(Time.Hour,2), ":", DecToStr(Time.Minute,2), ":", DecToStr(Time.Second,2))
DelayMS(5)
LCD.WriteAt(3,1, "Wd : ", DecToStr(Date.DayOfWeek, 2) )
DelayMS(5)
LCD.WriteAt(4,1, " ")
LCD.WriteAt(4,1, "LYear : ", DecToStr(Date.LeapYear, 2))
}
DelayMS(5)
// loop forever...
While 1=1
// GetTime()
// LCD.WriteAt(2,1,"Time:", DecToStr(Time.Hour,2), ":", DecToStr(Time.Minute,2), ":", DecToStr(Time.Second,2))
DelayMS(900)
Wend

//=================================================================================
End
[/code]

Jerry Messina
Swordfish Developer
Posts: 1473
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Fri Dec 16, 2011 6:13 pm

See if this works any better

Code: Select all

Device = 18F452
Clock = 20 

Config OSC = HS, OSCS=ON, PWRT=ON, WDT=OFF, LVP=OFF

'---LCD module options
#option LCD_DATA = PORTD.4
#option LCD_RS = PORTD.2
#option LCD_EN = PORTD.3

#option I2C_SDA = PORTB.2
#option I2C_SCL = PORTB.1

Include "Convert.bas" // other system libraries used
Include "LCD.bas"
Include "utils.bas" //
Include "SI2C.bas" //

// program constants
Const DaysOfMonth(12) As Byte = (31,28,31,30,31,30,31,31,30,31,30,31),
DaysOfWeek(7) As String = ("Mon","Tue","Wed","Thu","Fri","Sat","Sun")

Const W_Add = $A0
Const R_Add = $A1

Dim SetDateTime As PORTB.7 // Sw1

// program structured variables
Structure TTime
    Second As Byte // Second (0..59)
    Minute As Byte // Minute (0..59)
    Hour As Byte // Hour (0..11 or 0..23)
End Structure

Dim Time As TTime


Public Structure TDate
    Day As Byte // Date (0..31)
    Month As Byte // Month (1..12)
    Year As Byte // Year (0..99)
    DayOfWeek As Byte // day of the week (1..7)
    LeapYear As Byte
End Structure

Dim Date As TDate

Dim LyDd As Byte
Dim WdMo As Byte
Dim Tmp As Byte

Function LeapYear(ByVal Year As Byte) As Boolean
    Year = Year + 2000 // scale to correct date
    Result = False // reset the function result
    If Year Mod 4 = 0 Then // Mod returns the remainder of a division
        Result = True
        If Year Mod 100 = 0 Then
            If Year Mod 400 <> 0 Then
                Result = False
            EndIf
        EndIf
    EndIf
End Function

// increment by one day, handling month/year rollovers at the same time
Sub IncrementDay(ByRef Day, Month, Year, MaxDays As Byte)
    If Day = MaxDays Then
        Day = 1
        If Month = 12 Then
            Month = 1
            Inc(Year)
        Else
            Inc(Month)
        EndIf
    Else
        Inc(Day)
    EndIf
End Sub

// increment the day of week
Sub IncrementDayOfWeek(ByRef DayOfWeek As Byte)
    Inc(DayOfWeek)
    If DayOfWeek = 8 Then
        DayOfWeek = 1
    EndIf
End Sub

Sub SetTime(ByRef Hour, Minute, Second, DayOfWeek, Day, Month, LY As Byte)
    LyDd = ( DecToBCD(Day) Or (LY << 6) )
    WdMo = ( DecToBCD(Month) Or ( DayOfWeek << 5))


    // Stop the RTC from counting, before writing to the time and date registers
    SI2C.Start
    SI2C.WriteByte(W_Add)   // Send the RTC address, and put it in write mode
    SI2C.WriteByte($00)     // Move the pointer to the Control_Status register
    SI2C.WriteByte($80)     // Stop counting
    SI2C.Stop

    // Write to the date and time registers
    SI2C.Start
    SI2C.WriteByte(W_Add)
    SI2C.WriteByte($02)              // move pointer to the Seconds register
    SI2C.WriteByte(DecToBCD(Second)) //2 Write each byte
    SI2C.WriteByte(DecToBCD(Minute)) //3
    SI2C.WriteByte(DecToBCD(Hour)) //4
    SI2C.WriteByte(DecToBCD(LyDd)) //5 Ly + dd
    SI2C.WriteByte(DecToBCD(WdMo)) //6 wd + mon
    SI2C.Stop
    
    // allow the PCF8583 to start counting again    
    SI2C.WriteByte(W_Add)
    SI2C.WriteByte($00)     // Move the pointer to the Control_Status register
    SI2C.WriteByte($00)     // start counting
    SI2C.Stop
End Sub

Sub GetTime()

    // read the date and time registers
    SI2C.Start
    SI2C.WriteByte(W_Add)
    SI2C.WriteByte($02)         // start at the Seconds register
    SI2C.Restart
    SI2C.WriteByte(R_Add)
    Time.Second = SI2C.ReadByte(1)  // read with ack
    Time.Minute = SI2C.ReadByte(1)  // read with ack BCDToDec(SI2C.ReadByte()) //3
    Time.Hour = SI2C.ReadByte(1)    // read with ack BCDToDec(SI2C.ReadByte() And %00111111) //4
    LyDd = SI2C.ReadByte(1)         // read with ack 5
    WdMo = SI2C.ReadByte(0)         // read, NACK 6
    SI2C.Stop

    Date.DayOfWeek = (WdMo And %11100000) >> 5

    Tmp = (WdMo And %00011111)
    Date.Month = BCDToDec(Tmp)

    Date.LeapYear = (LyDd And %11000000) >> 6

    Tmp = (LyDd And %00111111)
    Date.Day = BCDToDec(Tmp)

End Sub

// return a three letter month abbreviation
Function MonthToMMM(ByRef Month As Byte) As String(4)
    Select Month
        Case 1
        Result = "Jan"
        Case 2
        Result = "Feb"
        Case 3
        Result = "Mar"
        Case 4
        Result = "Apr"
        Case 5
        Result = "May"
        Case 6
        Result = "Jun"
        Case 7
        Result = "Jul"
        Case 8
        Result = "Aug"
        Case 9
        Result = "Sep"
        Case 10
        Result = "Oct"
        Case 11
        Result = "Nov"
        Case 12
        Result = "Dec"
    End Select
End Function


// start of program
SetAllDigital
SI2C.Initialize()
Input(SetDateTime)
INTCON2.7 = 0

DelayMS(1000) // allow the circuit to power up and stabilise
LCD.Cls
DelayMS(500)
LCD.WriteAt(4,1, "Getting Date Time")


Time.Second = 25
Time.Minute = 39
Time.Hour = 14

Date.Day = 14
Date.Month = 12
Date.Year = 11
Date.DayOfWeek = 1
Date.LeapYear = 3

//SetTime(Time.Second, Time.Minute, Time.Hour, Date.DayOfWeek, Date.Day, Date.Month, Date.LeapYear )

GetTime()

LCD.WriteAt(1,1, BinToStr(Time.Second,12) )
LCD.WriteAt(2,1, BinToStr(Time.Minute,12) )
LCD.WriteAt(3,1, BinToStr(Time.Hour,12) )
{
LCD.WriteAt(1,1,"Date:", DecToStr(Date.Day,2), "-", DecToStr(Date.Month,2), "-", DecToStr(2000 + Date.Year,4))
DelayMS(5)
LCD.WriteAt(2,1,"Time:", DecToStr(Time.Hour,2), ":", DecToStr(Time.Minute,2), ":", DecToStr(Time.Second,2))
DelayMS(5)
LCD.WriteAt(3,1, "Wd : ", DecToStr(Date.DayOfWeek, 2) )
DelayMS(5)
LCD.WriteAt(4,1, " ")
LCD.WriteAt(4,1, "LYear : ", DecToStr(Date.LeapYear, 2))
}
DelayMS(5)
// loop forever...
While 1=1
// GetTime()
// LCD.WriteAt(2,1,"Time:", DecToStr(Time.Hour,2), ":", DecToStr(Time.Minute,2), ":", DecToStr(Time.Second,2))
DelayMS(900)
Wend

//=================================================================================
End 
I didn't check any of the BCD stuff, but the I2C should (hopefully) work. I forgot that you have to use the read() routine with ACK/NACK flag

Jerry Messina
Swordfish Developer
Posts: 1473
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Sat Dec 17, 2011 1:45 pm

Oops, looks like I got the logic sense of ACK/NACK backwards.

I should have used the provided constants...

Code: Select all

Sub GetTime()

    // define shorter versions of ACK/NACK
    const
        ACK = I2C_ACKNOWLEDGE,       // acknowledge data bit (acknowledge)
        NACK = I2C_NOT_ACKNOWLEDGE   // acknowledge data bit (NOT acknowledge)

    // read the date and time registers
    SI2C.Start
    SI2C.WriteByte(W_Add)
    SI2C.WriteByte($02)         // start at the Seconds register
    SI2C.Restart
    SI2C.WriteByte(R_Add)
    Time.Second = SI2C.ReadByte(ACK)  // read with ack
    Time.Minute = SI2C.ReadByte(ACK)  // read with ack BCDToDec(SI2C.ReadByte()) //3
    Time.Hour = SI2C.ReadByte(ACK)    // read with ack BCDToDec(SI2C.ReadByte() And %00111111) //4
    LyDd = SI2C.ReadByte(ACK)         // read with ack 5
    WdMo = SI2C.ReadByte(NACK)        // read, NACK 6
    SI2C.Stop

    Date.DayOfWeek = (WdMo And %11100000) >> 5

    Tmp = (WdMo And %00011111)
    Date.Month = BCDToDec(Tmp)

    Date.LeapYear = (LyDd And %11000000) >> 6

    Tmp = (LyDd And %00111111)
    Date.Day = BCDToDec(Tmp)

End Sub

E_VH
Posts: 12
Joined: Fri Dec 09, 2011 3:08 pm
Location: Belgium

RTC PCF8583

Post by E_VH » Sun Jan 15, 2012 10:03 am

Finaly get it to work !


[code]
Device = 18F452
Clock = 20

Config OSC = HS, OSCS=ON, PWRT=ON, WDT=OFF, LVP=OFF

'---LCD module options
#option LCD_DATA = PORTD.4
#option LCD_RS = PORTD.2
#option LCD_EN = PORTD.3

'--- SI2C module options
#option I2C_SDA = PORTB.2
#option I2C_SCL = PORTB.1

// other system libraries used
Include "Convert.bas"
Include "LCD.bas"
Include "utils.bas"
Include "SI2C.bas"

// program constants
Const W_Add = $A0 // PCF8583 Write address
Const R_Add = $A1 // PCF8583 Read address
Const Ram_Add = $EF // Free RAM ($10 to $FF) in PCF8583 to store Century and Year

Const DaysOfWeek(7) As String = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat")
Const LeapYear(4) As String = ("LY ","no-LY","no-LY","no-LY")

Dim Sw1_IsHigh As PORTB.booleans(7) // Sw1
Dim Sw2_IsHigh As PORTB.booleans(6) // Sw2
Dim Sw3_IsHigh As PORTB.booleans(5) // Sw3

Dim UpButtonCount As Byte '}Counters used for button debounce -
Dim DownButtonCount As Byte '}Counters track how long each button has
Dim SelectButtonCount As Byte '}been pressed and
Dim NoButtonCount As Byte '}how long no button has been pressed

Const DebounceCount As Byte = 4 'Default debounce delay in units of 5ms

// program structured variables
Structure TTime
Second As Byte // Second (0..59)
Minute As Byte // Minute (0..59)
Hour As Byte // Hour (0..11 or 0..23)
End Structure

Dim Time As TTime

Public Structure TDate
Day As Byte // Date (0..31)
Month As Byte // Month (1..12)
YearCentury As Byte
Year As Byte
DayOfWeek As Byte // day of the week (1..7)
LeapYear As Byte // 0..3 (0 = leap year -> Feb = 29 days)
End Structure

Dim Date As TDate

Dim yyyy As Word

// program Sub's
Sub GetYear()
SI2C.Start
SI2C.WriteByte(W_Add)
SI2C.WriteByte(Ram_Add) // start at the Ram address register
SI2C.Restart
SI2C.WriteByte(R_Add)
Date.YearCentury = SI2C.ReadByte(I2C_ACKNOWLEDGE)
Date.Year = SI2C.ReadByte(I2C_NOT_ACKNOWLEDGE)
SI2C.Stop
End Sub

Sub SetYear(Cn, Yy As Byte)
SI2C.Start
SI2C.WriteByte(W_Add)
SI2C.WriteByte(Ram_Add) //1 move pointer to the Ram register
SI2C.WriteByte(Cn) //2 Write Century 20
SI2C.WriteByte(Yy) //3 Write year 11
SI2C.Stop
End Sub

Sub SetDateTime(ByRef Hour, Minute, Second, DayOfWeek, Day, Month, LY As Byte)
Dim LyDd As Byte
Dim WdMo As Byte

LyDd = ( (DecToBCD(LY) << 6) Or DecToBCD(Day) )
WdMo = ( (DecToBCD(DayOfWeek) << 5) Or DecToBCD(Month) )

// Stop the RTC from counting, before writing to the time and date registers
SI2C.Start
SI2C.WriteByte(W_Add) // Send the RTC address, and put it in write mode
SI2C.WriteByte($00) // Move the pointer to the Control_Status register
SI2C.WriteByte($80) // Stop counting
SI2C.Stop

// Write to the date and time registers
SI2C.Start
SI2C.WriteByte(W_Add)
SI2C.WriteByte($02) // move pointer to the Seconds register
SI2C.WriteByte(DecToBCD(Second))//2 Write each byte
SI2C.WriteByte(DecToBCD(Minute))//3
SI2C.WriteByte(DecToBCD(Hour)) //4
SI2C.WriteByte(LyDd) //5 Ly + dd
SI2C.WriteByte(WdMo) //6 wd + mon
SI2C.Stop

// allow the PCF8583 to start counting again
SI2C.Start
SI2C.WriteByte(W_Add)
SI2C.WriteByte($00) // Move the pointer to the Control_Status register
SI2C.WriteByte($00) // start counting
SI2C.Stop

SetYear(Date.YearCentury, Date.Year)
End Sub


Sub GetDateTime()
Dim LyDd As Byte
Dim WdMo As Byte
Dim Tmp As Byte
// define shorter versions of ACK/NACK
Const
ACK = I2C_ACKNOWLEDGE, // acknowledge data bit (acknowledge)
NACK = I2C_NOT_ACKNOWLEDGE // acknowledge data bit (NOT acknowledge)

// read the date and time registers
SI2C.Start
SI2C.WriteByte(W_Add)
SI2C.WriteByte($02) // start at the Seconds register
SI2C.Restart
SI2C.WriteByte(R_Add)
Time.Second = BCDToDec(SI2C.ReadByte(ACK)) // read with ack
Time.Minute = BCDToDec(SI2C.ReadByte(ACK)) // read with ack //3
Time.Hour = BCDToDec( (SI2C.ReadByte(ACK) And %00111111) ) // read with ack //4
LyDd = SI2C.ReadByte(ACK) // read with ack 5
WdMo = SI2C.ReadByte(NACK) // read, NACK 6
SI2C.Stop

Tmp = ((WdMo And %11100000) >> 5)
Date.DayOfWeek = BCDToDec( Tmp )

Tmp = (WdMo And %00011111)
Date.Month = BCDToDec(Tmp)

Tmp = ((LyDd And %11000000) >> 6)
Date.LeapYear = BCDToDec( Tmp )

Tmp = (LyDd And %00111111)
Date.Day = BCDToDec(Tmp)

GetYear()

yyyy = Date.Year + (Date.YearCentury * 100)
End Sub

//-----------------------------------------------------------------------
// start of program
ADCON1 = $07 // AD Converters OFF

TRISB.7 = 1 // Sw1 configure as an input
TRISB.6 = 1 // Sw2 configure as an input
TRISB.5 = 1 // Sw3 configure as an input

UpButtonCount = 0
DownButtonCount = 0
SelectButtonCount = 0
NoButtonCount = 0
SI2C.Initialize()


DelayMS(500) // allow the circuit to power up and stabilise
LCD.Cls
DelayMS(500)

// Remove comment {} to Setting DateTime
{
Time.Second = 0
Time.Minute = 20
Time.Hour = 11

Date.Day = 14
Date.Month = 1
Date.Year = 12
Date.YearCentury = 20
Date.DayOfWeek = 6
Date.LeapYear = 0 // 2012 is leapyear

SetDateTime(Time.Hour, Time.Minute, Time.Second, Date.DayOfWeek, Date.Day, Date.Month, Date.LeapYear )
}

LCD.WriteAt(1,1, "Time:")
LCD.WriteAt(2,1, "Date:")
LCD.WriteAt(3,1, "WD: ")
LCD.WriteAt(3,9, "Ly: ")

// loop forever...
While 1=1
GetDateTime()
DelayMS(5)

LCD.WriteAt(1,6, DecToStr(Time.Hour,2), ":", DecToStr(Time.Minute,2), ":", DecToStr(Time.Second,2))
DelayMS(5)
LCD.WriteAt(2,6, DecToStr(yyyy,4), "/", DecToStr(Date.Month,2), "/", DecToStr(Date.Day,2))
DelayMS(5)
LCD.WriteAt(3,5, DaysOfWeek(Date.DayOfWeek) )
DelayMS(5)
LCD.WriteAt(3,13, LeapYear(Date.LeapYear) )
DelayMS(50)
Wend

//=================================================================================
End // Program
[/code]

Hoop this can help someone.

But is there an easy way to set DateTime with only three buttons ?

Attempted to use the menu.bas, couldn't make it to work

Regards

Erik

Post Reply