1. Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.
    Dismiss Notice

Clock

Discussion in 'Microcontrollers' started by AtomSoft, Dec 24, 2008.

  1. crocu

    crocu Member

    Joined:
    Feb 20, 2009
    Messages:
    118
    Likes:
    1
    Location:
    France
    Can we use bcd2dec function as below and avoid then having "type" as parameter ?

    If so it shoud simplify the rest of code and avoid some shifting and masking commands ?

    Code (text):
    unsigned char bcd2dec(unsigned char val)
    {
      return ((val/0x10*0xA)+(val%0x10));
    }
    ------

    Regarding the reading function byte per byte i found the source here : ( see section 10.7 )

    http://books.google.fr/books?id=CB9GaAU1dwsC&pg=PA489&lpg=PA489&dq=ds1306.c&source=bl&ots=Hfj8B1oy5g&sig=cPxc-cI0quvBR27qTD8ke3lLESc&hl=fr&ei=9VkYTY2mHISb8QPm5_iKCg&sa=X&oi=book_result&ct=result&resnum=8&ved=0CFsQ6AEwBzgU#v=onepage&q&f=false
     
    Last edited: Dec 27, 2010
  2. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    maybe... i havent tested it but... i would simply use....

    Code (text):

    unsigned char bcd2dec(unsigned char val)
    {
      return (((val>>4)*10)+(val & 0x0F));
    }
     
    But we still need a type ... mainly because this code works perfect perhaps for SECONDS and MINUTES but for HOURS we only use 6 bits BIT0 to BIT5...

    Bits 0 to 3 are Lower part of hour
    Bit 4 only is the Upper Hour in 12 Hr mode
    or
    Bits 4 & 5 are used in 24 hour mode

    Bits 6 & 7 are used for other purpose so including them in the math will throw off the value a lot

    Code (text):

    unsigned char bcd2dec(unsigned char val, char type)
    {
      if(type == 'h')
        return ((((val>>4) & 0x03)*10)+(val & 0x0F));
      else
        return (((val>>4)*10)+(val & 0x0F));
    }
     
     
    Last edited: Dec 27, 2010
  3. crocu

    crocu Member

    Joined:
    Feb 20, 2009
    Messages:
    118
    Likes:
    1
    Location:
    France
    Thanks,

    How knowing if a 'h' type should or not be used when you call the bcd2dec function ?

    I mean, then time goes through the code does not know if time is from 1 to 9 o'clock or from 10 to 12 o'clock ...

    Do you always check bit 4 in the main loop ?

    I do not see where you do this in your code, can you show me please ?


    In fact, i will try to reduce your code because i do not need LCD display and buttons.
    i only need date & time setting + date & time readings in 24h format.
    Alarms should also be usefull for me.
     
  4. dave

    Dave New Member

    Joined:
    Jan 12, 1997
    Messages:
    -
    Likes:
    0


     
  5. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US

    Im fixing my toilet right not but ill write some quick info ...

    If you take a look at the datasheet about the registers...

    [​IMG]

    To get the seconds we need to read register 0x00
    To get the minutes we need to read register 0x01
    To get the Hours we need to read register 0x02

    Lets call these variables bcdSec,bcdMin,bcdHour...

    Now to convert them to decimal so we can add or subtract easier we have to do a BCD to DEC conversion.

    Using this code:
    Code (text):

    unsigned char bcd2dec(unsigned char val)
    {
      return (((val>>4)*10)+(val & 0x0F));
    }
     
    we can convert seconds and minutes to decimal since these registers use the upper 4 bits for 1 number and the lower 4bits for another number...

    For example if the time is 10:20:40 PM

    The bcdSec variable will hold the seconds which is 0x40
    This is the same for bcdMin... It will have 0x20
    If using the 24 hour mode bcdHour will be 0x10
    BUT....
    if using the 12 hour mode bcdHour will be 0x70

    Basically in 12 Hour mode our code has to check the AM PM bit... and also the 12hr mode bit...

    so we have to take care of that



    LONG STORY SHORT... Since your using 24 hour mode. You can use the bcd2dec without a type:

    Code (text):

    unsigned char bcd2dec(unsigned char val)
    {
      return (((val>>4)*10)+(val & 0x0F));
    }
     
     
    Last edited: Dec 27, 2010
  6. crocu

    crocu Member

    Joined:
    Feb 20, 2009
    Messages:
    118
    Likes:
    1
    Location:
    France
    Hello Jason, all

    Regarding dec to bcd function, its name is uint2bcd
    actually parameter is unsigned char ival.

    So if i understand well, there is no need to convert char to int prior using this function ?

    if parameter is an unsigned char, shouldn't we name the function uchar2bcd instead ?
    Code (text):
    unsigned char uint2bcd(unsigned char ival)
    {
        return ((ival / 10) << 4) | (ival % 10);
    }
    --------------------------

    In fact i'm trying to adapt your working to my application ( i do not need buttons for time setting and LCD )

    i defined tables that will contents the time to set :
    One for DEC values ( this one will be filled-in first by a webserver )
    another one for BCD values.
    Code (text):
    unsigned char DEC_set_time_rtc[19] = "- --/--/-- --:-- --";
    unsigned char BCD_set_time_rtc[];
    Let's say DEC_set_time_rtc is already filled-in as follow :
    DEC_set_time[2] and DEC_set_time[3] are the DATE values : Decimal and Unit

    How should i convert each in BCD and send the result to BCD_set_time_rtc[2] and BCD_set_time_rtc[3] ?

    Is this correct ? my problem is sending BOTH BCD values Decimal and Unit to the RTC register :

    Code (text):
    BCD_set_time[2] = uint2bcd(DEC_set_time_rtc[2]);   
    BCD_set_time[3] = uint2bcd(DEC_set_time_rtc[3]);

    // Send the result in RTC register ( this is done in 2 steps , maybe not correct ? )
    RTC_write(0x84, BCD_set_time_rtc[2]);
    RTC_write(0x84, BCD_set_time_rtc[3]);   // < -- Will this command not overwrite the previous one just sent ?
     
    Last edited: Dec 29, 2010
  7. crocu

    crocu Member

    Joined:
    Feb 20, 2009
    Messages:
    118
    Likes:
    1
    Location:
    France
    Hello

    I need your help, please :

    I stored all my my DEC value in a table :

    Code (text):
    unsigned char DEC_set_time_rtc[19] = "- --/--/-- --:-- --";
    DEC_set_time_rtc[0] is DAY
    DEC_set_time_rtc[1] is ' ' space character
    DEC_set_time_rtc[2] is DATE (x10)
    DEC_set_time_rtc[3] is DATE (unit)
    DEC_set_time_rtc[4] is '/' character
    DEC_set_time_rtc[5] is MONTH (x10)
    DEC_set_time_rtc[6] is MONTH (unit)
    DEC_set_time_rtc[7] is '/' character
    DEC_set_time_rtc[8] is YEAR (x10)
    DEC_set_time_rtc[9] is YEAR (unit)
    DEC_set_time_rtc[10] is ' ' space character
    DEC_set_time_rtc[11] is HOUR (x10)
    DEC_set_time_rtc[12] is HOUR (unit)
    DEC_set_time_rtc[13] is ':' character
    DEC_set_time_rtc[14] is MIN (x10)
    DEC_set_time_rtc[15] is MIN (unit)
    DEC_set_time_rtc[16] is ' ' space character
    DEC_set_time_rtc[17] is SEC (x10)
    DEC_set_time_rtc[18] is SEC (unit)
    DEC_set_time_rtc[19] is '\0' character

    Then i convert each values to BCD and store each in a BCD table :
    Code (text):
    unsigned char BCD_set_time_rtc[] = "- --/--/-- --:-- --";
    Code (text):
    BCD_set_time_rtc[0] = dec2bcd(DEC_set_time_rtc[0]
    BCD_set_time_rtc[2] = dec2bcd(DEC_set_time_rtc[2]);
    BCD_set_time_rtc[3] = dec2bcd(DEC_set_time_rtc[3])
    ... etc ...
     
    Once my BCD table is filled-in and ready, i would like to know how i should send the high and low bytes to registers.

    How should i concatenate high and low bytes and send them to register ?

    I think about this, but not sure it is the good way to proceed :
    Code (text):

    char tmp;
    BCD_set_time_rtc[2] = (tmp << 4);
    BCD_set_time_rtc[3] = ????? i don't know

    RTC_write(0x84, tmp);
    I'm not confortable with masking and shifting. :eek:
    Many thanks for your help,
     
    Last edited: Dec 30, 2010
  8. futz

    futz Active Member

    Joined:
    Sep 15, 2007
    Messages:
    2,043
    Likes:
    24
    Location:
    Vancouver, B.C.
    Well, now's the perfect time to get comfortable with it. Dive in and play with it a bit and you'll find it's really not difficult at all. It'll only take you an hour or two tops (more likely a LOT less) to figure it out. Dealing with displays (7-segs and GLCDs) you'll use it constantly. Also useful for doing SPI-type stuff, again for displays and other things.
     
  9. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    you have to send the decimal values to the RTC not ASCII ok..

    Code (text):

    char upper;
    char lower;
    char bcdOut;

    upper = BCD_set_time_rtc[2] - 0x30;
    lower = BCD_set_time_rtc[3] - 0x30;
    bcdOut = (upper <<4) + lower ;

    RTC_write(0x84, bcdOut );
     
    Something like that... Shifting and masking is so easy!

    For this example we had to first convert the ASCII back to decimal format which is as simple as deducting 0x30 from it.
    Then we have to take the 2 numbers and have them into separate variables for clarity ...
    now that we have 2 bytes and they have the decimal version of our time... upper and lower
    we have to convert these to 1 bcd byte...

    remember a BCD byte for the seconds register is ..
    HIGH---LOW
    0000 - 0000

    so lets say its 21 seconds... our upper and lower should hold the below values after we remove the ascii part
    upper = 2
    lower = 1

    and in binary:
    upper = 0000-0010
    lower = 0000-0001

    Remember how our register works... we have to use
    upper - lower
    0000 - 0000

    so we simply make a new variable call it.. bcdSec
    Code (text):

    bcdSec = upper << 4; //This loads the upper byte into bcdSec then shifts bcdSec 4 bits to the left
    //bcdSec should be 0x20 (binary 0010-0000)

    bcdSec += lower; //This adds our lower byte in..
    bcdSec should now be 21 ( 0010-0001 )
     
     
    Last edited: Dec 30, 2010
  10. crocu

    crocu Member

    Joined:
    Feb 20, 2009
    Messages:
    118
    Likes:
    1
    Location:
    France
    Many thanks Jason, the BCD conversion is now working perfectly. :)

    How do you display current time once time is set ?
    I mean, did you create an infinite loop that will always request values from each registers and do a BCD to DEC -> ASCII conversion ?
    doing this from an infinite loop with check RTC registers many time per second, maybe it is not a good way to proceed ?

    Maybe best is to go with an interrupt sent from RTC to the Pic ?
    Actually, I would like to display time in "real time" .
     
  11. AtomSoft

    AtomSoft Well-Known Member

    Joined:
    Feb 7, 2008
    Messages:
    5,670
    Likes:
    41
    Location:
    Brooklyn, NY US
    what i do is store the Seconds value and everytime i check the clock i determine if the second has changed and if so update the time. this way even if i check the clock 20 times in 1 second i draw it to lcd 1 time
     
  12. crocu

    crocu Member

    Joined:
    Feb 20, 2009
    Messages:
    118
    Likes:
    1
    Location:
    France
    Hello,

    I've finish to write my DS1305/DS1306 but i'm not able to communicate with the RTC, Maybe SPI is not well initiated or maybe SPI W/R functions have problem ?

    My Pic is 18F97J60 ( 40 Mhz clock )

    When i write a value to RTC register and then try to read it back i always get 0x00 . :(
    I should find there seconds getting incremented there instead ...

    I first initiated SPI in mode 1 and wrote RTC control register to clear WP flag with SPI_RTC_Init();

    SPI test from main loop :
    Code (text):
            RTC_write(0x80, 0b00000010);               // set second register = 3
           
            Nop();Nop();Nop();

            second_from_rtc = RTC_read(0x00);
    Any help will be much appreciated.
    Many thanks,


    Here are my SPI functions :

    DS1306.c

    Code (text):
    #define __DS1306_C

    #include "DS1306.h"
    #include <stdio.h>


    // *******************************/
    //             VARIABLES
    // *******************************/
    unsigned char BCD_read_time_rtc[];
    unsigned char rtc_ctrl_register;
    // *******************************/
    //      Init SPI configuration
    // *******************************/
    void SPI_RTC_Init(void)
    {
        BYTE SPICON1Save;

        RTC_CS_IO = 0;
        RTC_CS_TRIS = 0;                        // Pic CS pin is output

    #if defined(__18CXX)                       
        RTC_SCK_TRIS = 0;
        RTC_SDO_TRIS = 0;
        RTC_SDI_TRIS = 1;
    #endif

    // Save current SPI config 
        SPICON1Save = SSP1CON1;                 // Save current SPI setting
    // End Save SPI config

    // Configure SPI
        RTC_SPI_ON_BIT = 0;                     // Stop SPI
        RTC_SPICON1 = RTC_SPICON1_CFG;          // Apply new SPI settings for RTC
        RTC_SPI_ON_BIT = 1;                     // Start SPI
        RTC_SPI_IF = 0;                         // Clear SPI Flag

    #if defined(__18CXX)
        RTC_SPISTAT = RTC_SPISTAT_CFG;          // Apply new SPISTAT register config
    #endif


    // Send control command : disable WP, enable 1 Hz output.  
        RTC_write(WR_CTRL_REGISTER, 0b00000000);
        RTC_write(WR_CTRL_REGISTER, 0b00000100);
        rtc_ctrl_register = RTC_read(RD_CTRL_REGISTER);

                     
    // Restore SPI config
        SSP1CON1 = SPICON1Save;                 // Restore old SPI setting
    // End Restore SPI config
    }



    /************************************
        Read a Register off the DS1306
    ************************************/
    unsigned char RTC_read(unsigned int address)
    {
        unsigned char p;
        BYTE SPICON1Save;

    // Save SPI config 
       SPICON1Save = SSP1CON1;                  // Save current SPI setting
    // End Save SPI config

    // Configure SPI
        RTC_SPI_ON_BIT = 0;                     // Stop SPI
        RTC_SPICON1 = RTC_SPICON1_CFG;          // Apply new SPI settings for RTC
        RTC_SPI_ON_BIT = 1;                     // Start SPI
       
            RTC_CS_IO = 1;                      // Enable CS
            RTC_SPI_IF = 0;                     // Clear SPI Flag

            RTC_SSPBUF = address;               // Send SPI command
            while (!RTC_SPI_IF);                // Wait until address is shifted out
            RTC_SPI_IF = 0;
            p = RTC_SSPBUF;                     // Get register value

            return(p);                          // Return read value
            RTC_CS_IO = 0;                      // Disable CS

    // Restore SPI config
        SSP1CON1 = SPICON1Save;                 // Restore old SPI setting
    // End Restore SPI config
    }


    /************************************
          Write to DS1306 Register
    ************************************/
    void RTC_write(unsigned int address, unsigned char value)
    {
      BYTE SPICON1Save;
      volatile BYTE Dummy;

    // Save SPI config 
       SPICON1Save = SSP1CON1;                  // Save current SPI setting
    // End Save SPI config

    // Configure SPI
        RTC_SPI_ON_BIT = 0;                     // Stop SPI
        RTC_SPICON1 = RTC_SPICON1_CFG;          // Apply new SPI settings for RTC
        RTC_SPI_ON_BIT = 1;                     // Start SPI

        RTC_CS_IO = 1;                          // Enable CS
        RTC_SPI_IF = 0;                         // Clear SPI Flag

        RTC_SSPBUF = address;                   // Transmit address
        while(!RTC_SPI_IF);                     // Wait until data is shifted out
        RTC_SPI_IF = 0;
        Dummy = RTC_SSPBUF;                     // Clear SPI Buffer
     
        RTC_SSPBUF = value;                     // Transmit value
        while(!RTC_SPI_IF);                     // Wait until data is shifted out
        RTC_SPI_IF = 0;
        Dummy = RTC_SSPBUF;                     // Clear SPI Buffer
     
        RTC_CS_IO = 0;                          // Disable CS

    // Restore SPI config
        SSP1CON1 = SPICON1Save;                 // Restore old SPI setting
    // End Restore SPI config
    }
     
    DS1306.h
    Code (text):

    #ifndef _RTC_H
    #define _RTC_H


    // RTC pins
    #define RTC_CS_IO                   (LATGbits.LATG3)
    #define RTC_CS_TRIS     (TRISGbits.TRISG3)
    #define RTC_SCK_TRIS        (TRISCbits.TRISC3)
    #define RTC_SDI_TRIS        (TRISCbits.TRISC4)
    #define RTC_SDO_TRIS        (TRISCbits.TRISC5)

    #define RTC_SPI_IF            (PIR1bits.SSPIF)
    #define RTC_SSPBUF          (SSP1BUF)
    #define RTC_SPISTAT         (SSP1STAT)
    #define RTC_SPICON1         (SSP1CON1)
    #define RTC_SPI_ON_BIT     (SSP1CON1bits.SSPEN)


    //////////////////////////
    //     SPI settings     //
    //////////////////////////
        #define RTC_SPISTAT_CFG         (0x40)      // Sample at middle of data output time (SMP, bit7 = 0) - Tx on transition from active to idle clock (CKE, bit 6 = 1)
        #define RTC_SPICON1_CFG         (0x21)      // SSPEN bit is set, SPI in master mode, FOSC/16, IDLE state is low level


    // RTC SPI opcodes:
    #define WR_CTRL_REGISTER    0x8F                    // Write CTRL register address
    #define RD_CTRL_REGISTER    0x0F                    // Read CTRL register address

    #define RD_SEC          0x00
    #define WR_SEC          0x80
    #define RD_MIN          0x01
    #define WR_MIN          0x81
    #define RD_HOUR         0x02
    #define WR_HOUR     0x82
    #define RD_DAY                  0x03
    #define WR_DAY          0x83
    #define RD_DATE         0x04
    #define WR_DATE         0x84
    #define RD_MONTH        0x05
    #define WR_MONTH        0x85
    #define RD_YEAR         0x06
    #define WR_YEAR         0x86

    #define RD_ALR_SEC      0x07
    #define WR_ALR_SEC      0x87
    #define RD_ALR_MIN      0x08
    #define WR_ALR_MIN      0x88
    #define RD_ALR_HOUR             0x09
    #define WR_ALR_HOUR     0x89
    #define RD_ALR_DAY              0x0A
    #define WR_ALR_DAY      0x8A
    #define RD_ALR_DATE     0x0B
    #define WR_ALR_DATE     0x8B
    #define RD_ALR_MONTH    0x0C
    #define WR_ALR_MONTH    0x8C
    #define RD_ALR_YEAR     0x0D
    #define WR_ALR_YEAR     0x8D


    //////////////////////////
    //       Variables      //
    //////////////////////////

    extern unsigned char BCD_set_time_rtc;

    extern unsigned char read_ascii_Day;
    extern unsigned char read_ascii_Date;
    extern unsigned char read_ascii_Month;
    extern unsigned char read_ascii_Year;
    extern unsigned char read_ascii_Hour;
    extern unsigned char read_ascii_Min;
    extern unsigned char read_ascii_Sec;

    extern unsigned char BCD_Day;
    extern unsigned char BCD_Date;
    extern unsigned char BCD_Month;
    extern unsigned char BCD_Year;
    extern unsigned char BCD_Hour;
    extern unsigned char BCD_Min;
    extern unsigned char BCD_Sec;

    //////////////////////////
    //       Prototypes     //
    //////////////////////////

    void SPI_RTC_Init(void);

    unsigned char RTC_read(unsigned int address);

    void RTC_write(unsigned int address, unsigned char value);

    #endif // _RTC_H
     
     
    Last edited: Jan 5, 2011
  13. crocu

    crocu Member

    Joined:
    Feb 20, 2009
    Messages:
    118
    Likes:
    1
    Location:
    France
    Hello,

    Nobody can help me ?
     

Share This Page