Shop OBEX P1 Docs P2 Docs Learn Events
DS1302 Module C code — Parallax Forums

DS1302 Module C code

The program below is something that Daniel Robert put together. I made a very slight adjustment, but it needs more improvements.

All of the code, for the DS1302, that is available, on the internet, is for other processors and not the Propeller. So, taking an exiting C program and just plugging into SimpleIDE does not work out very well.

The problem that I am having is trying to figure out how to write a time and date to the module. Not sure what format the module is expecting, and what the command would be for doing the write. If some of you C experts could show me some examples, it would be very helpful.

Of course I am using SimpleIDE and the FLiP module for the testing procedure.

Thanks


Ray
/*
  rtc_test.c
  
  September 15, 2018
  Parallax DS1302 Real_Time Clock module.
  Original program by Daniel Robert.
*/
#include "simpletools.h"

int *cog;

/* Using pins 17,18,19 with the FLiP module.*/
#define ds1302cs 19
#define ds1302sclk 18
#define ds1302io 17

#define dsSec 0x80
#define dsMin 0x82
#define dsHour 0x84

#define dsDate 0x86
#define dsMon 0x88
#define dsDay 0x8A
#define dsYear 0x8C



#define dsWpa 0x8E
#define dsTca 0x90

volatile int seconds,minutes,hours,date,month,day,year,prevSec;
volatile long flagWord;

void writeDs1302(int myreg, int value)
{
  high(ds1302cs);
  shift_out(ds1302io, ds1302sclk, LSBFIRST, 8, myreg);
  shift_out(ds1302io, ds1302sclk, LSBFIRST, 8, value);
  low(ds1302cs);
}

int readDs1302(int myreg)
{
  int readreg = myreg + 1;
  high(ds1302cs);
  shift_out(ds1302io, ds1302sclk, LSBFIRST, 8, readreg);
  int result = shift_in(ds1302io, ds1302sclk, LSBPRE, 8);
  low(ds1302cs);
  return result;
}    

/* COG 1*/
void dsClock();

/* COG 0 */
int main()
{
  // Add startup code here.
  flagWord = 0x80000000;
  cog = cog_run(dsClock, 128);
  
  while(flagWord != 0){}
  flagWord = 0x80000000 | (dsMin << 8) | 0x40;
  while (flagWord !=0){}
 
  while(1)
  {
    // Add main loop code here.
    putHexLen(hours, 2);
    putStr(":");
    putHexLen(minutes,2);
    putStr(":");
    putHexLen(seconds,2);
    putStr("    ");
    
    /* Display in month day year format. */
    putHexLen(month,2);
    putStr("/");
    putHexLen(date,2);
    putStr("/");
    putHexLen(year,2);
    
    putChar(NL);  // New line
    pause(500);
  }  
}

/* COG 1*/
void dsClock()
{
  low(ds1302cs);
  low(ds1302sclk);
  writeDs1302(dsWpa, 0);
  int started = readDs1302(dsSec);
  if((started & 0x80) !=0)
  {
    writeDs1302(dsSec, 0);
  }
  flagWord = 0;
  
  while(1)
  {
    if(prevSec != readDs1302(dsSec))
    {
      seconds = readDs1302(dsSec);
      prevSec = seconds;
      hours = readDs1302(dsHour);
      minutes = readDs1302(dsMin);
      date = readDs1302(dsDate);
      month = readDs1302(dsMon);
      day = readDs1302(dsDay);
      year = readDs1302(dsYear);
    }
    if(flagWord != 0)
    {
      writeDs1302(((flagWord >> 8) & 0xFF), (flagWord & 0xFF));
      flagWord = 0;
    }
    pause(100);            
  }        
}  

Comments

  • tomcrawfordtomcrawford Posts: 1,126
    edited 2018-09-16 17:44
    The module is expecting the same format for a write as it produces for the corresponding read. Most registers are two digits of BCD.

    writeDs1302(int myreg, int value) is the "command" that is used for write.

    Determining the values to be written (that is, current date and time) is a little messier. I always used a couple of buttons and a state machine.

    Edit: There is something I don't understand about this program: writeDs1302 and readDs1302 are defined prior to int main(). Yet they are apparently accessible to void dsClock() in another cog. How does this work?
  • Here is a library I wrote for this device some time ago.

    DS1302 Library

    Mike
  • The code below does the job, but using the scanf() pushes the code size up 21,228. Need to find a way to get around using scanf().

    Doing a quick test, it looks like the time and date are correct when it is printed back. I guess this could be a stand alone program that you would run one time to set the time and date of the DS1302. I guess I need to write some code for accessing the time and date within a general program.

    Anybody have any ideas for simplifying or condensing the program code below?

    Ray
    /*
      rtc_test1.c
      September 16, 2018
    */
    #include "simpletools.h"
    
    /* Using pins 17,18,19 with the FLiP module.*/
    #define ds1302cs 19
    #define ds1302sclk 18
    #define ds1302io 17
    
    #define dsSec 0x80
    #define dsMin 0x82
    #define dsHour 0x84
    
    #define dsDate 0x86
    #define dsMon 0x88
    #define dsDay 0x8A
    #define dsYear 0x8C
    
    #define dsWpa 0x8E
    #define dsTca 0x90
    
    volatile int seconds,minutes,hours,date,month,day,year,prevSec;
    
    
    int readDs1302(int myreg)
    {
      int readreg = myreg + 1;
      high(ds1302cs);
      shift_out(ds1302io, ds1302sclk, LSBFIRST, 8, readreg);
      int result = shift_in(ds1302io, ds1302sclk, LSBPRE, 8);
      low(ds1302cs);
      return result;
    }
    
    void writeDs1302(int myreg, int value)
    {
      high(ds1302cs);
      shift_out(ds1302io, ds1302sclk, LSBFIRST, 8, myreg);
      shift_out(ds1302io, ds1302sclk, LSBFIRST, 8, value);
      low(ds1302cs);
    }
    
    static unsigned int dToBcd(unsigned int byte)
    {
      return((byte / 10) << 4) + (byte % 10);
    }  
    
    
    
    void get_Time();
    void get_Date();
    void set_Time();
    void set_Date();
    
    
    int main()
    {
      // Add startup code here.
      set_Time();
      set_Date();
    
     
      while(1)
      {
        // Add main loop code here.
        get_Time();
        get_Date();
        pause(500);
      }  
    }
    
    
    void get_Time()
    {
      hours = readDs1302(dsHour);
      putHexLen(hours,2);
      putStr(":");
      minutes = readDs1302(dsMin);
      putHexLen(minutes,2);
      putStr(":");
      seconds = readDs1302(dsSec);
      putHexLen(seconds,2);
      putChar(NL);  // New line  
    }
    
    void get_Date()
    {
      month = readDs1302(dsMon);
      putHexLen(month,2);
      putStr("/");
      day = readDs1302(dsDate);
      putHexLen(day,2);
      putStr("/");
      year = readDs1302(dsYear);
      putHexLen(year,2);
      putChar(NL);  // New line 
    }
    
    void set_Time()
    {
      int hour = 0;
      int minutes = 0;
      int seconds = 0;
      int hour1,minutes1,seconds1;
      
      print("Hour(xx): ");
      scanf("%d",&hour);
      hour1 = dToBcd(hour);
      print("Minutes(xx): ");
      scanf("%d",&minutes);
      minutes1 = dToBcd(minutes);
      print("Seconds(xx): ");
      scanf("%d",&seconds);
      seconds1 = dToBcd(seconds);
      
      writeDs1302(dsSec,seconds1);
      writeDs1302(dsHour,hour1);
      writeDs1302(dsMin,minutes1);
    }
    
    void set_Date()
    {
      int date = 0;
      int month = 0;
      int year = 0;
      int day = 0;
      int date1,month1,year1,day1;
      
      
      print("Day(DD): ");
      scanf("%d",&date);
      date1 = dToBcd(date);  
      print("Month(MM): ");
      scanf("%d",&month);
      month1 = dToBcd(month);
      print("Year(YY): ");
      scanf("%d",&year);
      year1 = dToBcd(year);
      print("Day(0=Sunday): ");
      scanf("%d",&day);
      day1 = dToBcd(day);
      
      writeDs1302(dsDate,date1);
      writeDs1302(dsMon,month1);
      writeDs1302(dsDay,day1);
      writeDs1302(dsYear,year1);
    
    }       
    
  • You could used gets() and atoi() instead of scanf(). This should reduce the code size.
  • I just tried that, and it reduced the code down to 11,284. That is a good improvement, but I wonder if there is some more code cutting that could be done. I was thinking maybe get it down to 3,000, am I dreaming?

    Ray
  • kwinnkwinn Posts: 8,697
    Rsadeika wrote: »
    I just tried that, and it reduced the code down to 11,284. That is a good improvement, but I wonder if there is some more code cutting that could be done. I was thinking maybe get it down to 3,000, am I dreaming?

    Ray

    I'm not a C guru by any means, but you might be able to reduce the number of "print" statements quite a bit by putting the constants text (ie "Day: Month: etc) in a string that has space for the actual variables and then converting the variables to text and inserting them in the string. Not sure how well it would work with C, but it works like a charm in Spin.
  • Replace your "print" function calls with "puts" function calls and that should have a significant affect as well. If there's anywhere that you're passing multiple arguments to print, rewrite to be more like your get_Date function.
  • The program below, code size, is now 6436. My C skills are not that advanced, in order to get some more code reduction. The next thing on my list, possibly implement usage of WiFi to have a time and date update from the Internet.

    But, before I proceed with that, I also purchased an ADC0831 chip. I will start another thread for some advise as to how to proceed with that. Of course it will be C code using SimpleIDE.

    Ray
    /*
      rtc_test1.c
      September 16, 2018
    */
    #include "simpletools.h"
    
    /* Using pins 17,18,19 with the FLiP module.*/
    #define ds1302cs 19
    #define ds1302sclk 18
    #define ds1302io 17
    
    #define dsSec 0x80
    #define dsMin 0x82
    #define dsHour 0x84
    
    #define dsDate 0x86
    #define dsMon 0x88
    #define dsDay 0x8A
    #define dsYear 0x8C
    
    #define dsWpa 0x8E
    #define dsTca 0x90
    
    volatile int seconds,minutes,hours,date,month,day,year,prevSec;
    
    
    int readDs1302(int myreg)
    {
      int readreg = myreg + 1;
      high(ds1302cs);
      shift_out(ds1302io, ds1302sclk, LSBFIRST, 8, readreg);
      int result = shift_in(ds1302io, ds1302sclk, LSBPRE, 8);
      low(ds1302cs);
      return result;
    }
    
    void writeDs1302(int myreg, int value)
    {
      high(ds1302cs);
      shift_out(ds1302io, ds1302sclk, LSBFIRST, 8, myreg);
      shift_out(ds1302io, ds1302sclk, LSBFIRST, 8, value);
      low(ds1302cs);
    }
    
    static unsigned int dToBcd(unsigned int byte)
    {
      return((byte / 10) << 4) + (byte % 10);
    }
    
    static int bcdToD(unsigned int byte, unsigned int mask)
    {
      unsigned int b1, b2;
      byte &= mask;
      b1 = byte & 0x0F;
      b2 = ((byte >> 4) & 0x0F) * 10;
      return b1 + b2;
    }
      
    
    
    
    void get_Time();
    void get_Date();
    void set_Time();
    void set_Date();
    
    
    int main()
    {
      // Add startup code here.
      set_Time();
      set_Date();
    
     
      while(1)
      {
        // Add main loop code here.
        get_Time();
        get_Date();
        pause(500);
      }  
    }
    
    
    void get_Time()
    {
      hours = readDs1302(dsHour);
      putHexLen(hours,2);
      putStr(":");
      minutes = readDs1302(dsMin);
      putHexLen(minutes,2);
      putStr(":");
      seconds = readDs1302(dsSec);
      putHexLen(seconds,2);
      putChar(NL);  // New line  
    }
    
    void get_Date()
    {
      month = readDs1302(dsMon);
      putHexLen(month,2);
      putStr("/");
      day = readDs1302(dsDate);
      putHexLen(day,2);
      putStr("/");
      year = readDs1302(dsYear);
      putHexLen(year,2);
      putChar(NL);  // New line 
    }
    
    void set_Time()
    {
      int hour = 0;
      int minutes = 0;
      int seconds = 0;
      int hour1,minutes1,seconds1;
      char inBuff[2];
      
    
      putStr("Hour(xx): ");
      gets(inBuff);
      hour = atoi(inBuff);
      hour1 = dToBcd(hour);
      
    
      putStr("Minutes(xx): ");
      gets(inBuff);
      minutes = atoi(inBuff);
      minutes1 = dToBcd(minutes);
      
    
      putStr("Seconds(xx): ");
      gets(inBuff);
      seconds = atoi(inBuff);
      seconds1 = dToBcd(seconds);
      
      writeDs1302(dsSec,seconds1);
      writeDs1302(dsHour,hour1);
      writeDs1302(dsMin,minutes1);
    }
    
    void set_Date()
    {
      int date = 0;
      int month = 0;
      int year = 0;
      int day = 0;
      int date1,month1,year1,day1;
      char inBuff[2];
      
    
      putStr("Day(DD): ");
      gets(inBuff);
      date = atoi(inBuff);
      date1 = dToBcd(date);  
      
    
      putStr("Month(MM): ");
      gets(inBuff);
      month = atoi(inBuff);
      month1 = dToBcd(month);
      
    
      putStr("Year(YY): ");
      gets(inBuff);
      year = atoi(inBuff);
      year1 = dToBcd(year);
      
    
      putStr("Day(0=Sunday): ");
      gets(inBuff);
      day = atoi(inBuff);
      day1 = dToBcd(day);
      
      writeDs1302(dsDate,date1);
      writeDs1302(dsMon,month1);
      writeDs1302(dsDay,day1);
      writeDs1302(dsYear,year1);
    
    }       
    
  • I have very interesting results over here.

    First, I took your code and copied it into one of my scratch projects which is built with PropWare. There are two important differences and I don't know which one is causing the result (or both?)

    Building your code with SimpleIDE:
    Code size: 6,420 b
    Total size: 7,184 b

    Building your code with PropWare:
    Code size: 7,128 b
    Total size: 7,480 b

    Strange that they're different. Not only are they different, but the ratio of code size to total size is significantly different.



    Anyway, then I got really curious how the PropWare classes compared in code size to what you were doing (since you were trying to get your code size down as small as possible). So, I took your code and converted it to PropWare and left all of the original code commented out so you can see line-for-line what everything does. Here's my code size results:
    Code size: 6,212 b
    Total size: 6,604 b

    I don't have a DS1302 sitting around to test it out... no idea if it works... hopefully?
    /*
      rtc_test1.c
      September 16, 2018
    */
    //#include "simpletools.h"
    #include <PropWare/hmi/output/printer.h>
    #include <PropWare/hmi/input/scanner.h>
    #include <PropWare/serial/spi/spi.h>
    
    using PropWare::SPI;
    using PropWare::Pin;
    
    /* Using pins 17,18,19 with the FLiP module.*/
    #define ds1302cs 19
    #define ds1302sclk 18
    //#define ds1302io 17
    #define ds1302mosi 17
    #define ds1302miso 16
    
    #define dsSec 0x80
    #define dsMin 0x82
    #define dsHour 0x84
    
    #define dsDate 0x86
    #define dsMon 0x88
    #define dsDay 0x8A
    #define dsYear 0x8C
    
    #define dsWpa 0x8E
    #define dsTca 0x90
    
    volatile int seconds,minutes,hours,date,month,day,year,prevSec;
    
    
    static const SPI spi(Pin::to_mask(ds1302mosi),
                         Pin::to_mask(ds1302miso),
                         Pin::to_mask(ds1302sclk),
                         100000, SPI::Mode::MODE_0, SPI::BitMode::LSB_FIRST);
    static const Pin CS(Pin::to_mask(ds1302cs), Pin::Dir::OUT);
    
    int readDs1302(int myreg)
    {
        int readreg = myreg + 1;
    
        //high(ds1302cs);
        //shift_out(ds1302io, ds1302sclk, LSBFIRST, 8, readreg);
        //int result = shift_in(ds1302io, ds1302sclk, LSBPRE, 8);
        //low(ds1302cs);
    
        CS.set();
        spi.shift_out(8, readreg);
        int result = spi.shift_in(8);
        CS.clear();
    
        return result;
    }
    
    void writeDs1302(int myreg, int value)
    {
        //high(ds1302cs);
        //shift_out(ds1302io, ds1302sclk, LSBFIRST, 8, myreg);
        //shift_out(ds1302io, ds1302sclk, LSBFIRST, 8, value);
        //low(ds1302cs);
    
        CS.set();
        spi.shift_out(16, myreg << 8 | value);
        CS.clear();
    }
    
    static unsigned int dToBcd(unsigned int byte)
    {
        return((byte / 10) << 4) + (byte % 10);
    }
    
    static int bcdToD(unsigned int byte, unsigned int mask)
    {
        unsigned int b1, b2;
        byte &= mask;
        b1 = byte & 0x0F;
        b2 = ((byte >> 4) & 0x0F) * 10;
        return b1 + b2;
    }
    
    
    
    
    void get_Time();
    void get_Date();
    void set_Time();
    void set_Date();
    
    
    int main()
    {
        // Add startup code here.
        set_Time();
        set_Date();
    
        // This line sets the format of all numerical data to be printed as 2 digits wide, 0-padded, and base 16 (hex)
        pwOut << PropWare::Printer::Format(2, 0, 16);
    
        while(1)
        {
            // Add main loop code here.
            get_Time();
            get_Date();
            //pause(500);
            waitcnt(CNT + 500*MILLISECOND);
        }
    }
    
    
    void get_Time()
    {
        hours = readDs1302(dsHour);
        //putHexLen(hours,2);
        //putStr(":");
        minutes = readDs1302(dsMin);
        //putHexLen(minutes,2);
        //putStr(":");
        seconds = readDs1302(dsSec);
        //putHexLen(seconds,2);
        //putChar(NL);  // New line
    
        pwOut << hours << ':' << minutes << ':' << seconds << '\n';
    }
    
    void get_Date()
    {
        month = readDs1302(dsMon);
        //putHexLen(month,2);
        //putStr("/");
        day = readDs1302(dsDate);
        //putHexLen(day,2);
        //putStr("/");
        year = readDs1302(dsYear);
        //putHexLen(year,2);
        //putChar(NL);  // New line
    
        pwOut << month << '/' << day << '/' << year << '\n';
    }
    
    void set_Time()
    {
        int hour = 0;
        int minutes = 0;
        int seconds = 0;
        int hour1,minutes1,seconds1;
        //char inBuff[2];
    
    
        //putStr("Hour(xx): ");
        //gets(inBuff);
    
        pwOut << "Hour(xx): ";
        pwIn >> hour;
    
        //hour = atoi(inBuff);
        hour1 = dToBcd(hour);
    
    
        //putStr("Minutes(xx): ");
        //gets(inBuff);
        //minutes = atoi(inBuff);
    
        pwOut << "Minutes(xx): ";
        pwIn >> minutes;
    
        minutes1 = dToBcd(minutes);
    
    
        //putStr("Seconds(xx): ");
        //gets(inBuff);
        //seconds = atoi(inBuff);
    
        pwOut << "Seconds(xx): ";
        pwIn >> seconds;
    
        seconds1 = dToBcd(seconds);
    
        writeDs1302(dsSec,seconds1);
        writeDs1302(dsHour,hour1);
        writeDs1302(dsMin,minutes1);
    }
    
    void set_Date()
    {
        int date = 0;
        int month = 0;
        int year = 0;
        int day = 0;
        int date1,month1,year1,day1;
        //char inBuff[2];
    
    
        //putStr("Day(DD): ");
        //gets(inBuff);
        //date = atoi(inBuff);
    
        pwOut << "Day(DD): ";
        pwIn >> date;
    
        date1 = dToBcd(date);
    
    
        //putStr("Month(MM): ");
        //gets(inBuff);
        //month = atoi(inBuff);
    
        pwOut << "Month(MM): ";
        pwIn >> month;
    
        month1 = dToBcd(month);
    
    
        //putStr("Year(YY): ");
        //gets(inBuff);
        //year = atoi(inBuff);
    
        pwOut << "Year(YY): ";
        pwIn >> year;
    
        year1 = dToBcd(year);
    
    
        //putStr("Day(0=Sunday): ");
        //gets(inBuff);
        //day = atoi(inBuff);
    
        pwOut << "Day(0=Sunday): ";
        pwIn >> day;
    
        day1 = dToBcd(day);
    
        writeDs1302(dsDate,date1);
        writeDs1302(dsMon,month1);
        writeDs1302(dsDay,day1);
        writeDs1302(dsYear,year1);
    
    }
    

    Overall, your code is pretty darned effecient. I think you did a good job. Use of the individual putChar, putHex, etc functions was a very smart way to start.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2018-09-17 23:38
    Just on the subject of accessing RTC chips in general although I do find I2C versions do not take any extra I/O pins is that rather than accessing register by register I tend to read and write all the time-keeping registers in one burst (7 to 10 bytes). It's much faster plus software can operate directly upon the contents in memory. Perhaps this is a method that you could use instead of the individual readDs1302(x) methods etc.

    More so now though, I only read the RTC hardware itself at reset and once a day at midnight to update the date and to synchronize a 32-bit milliseconds time of day counter which is used directly for time stamps (I add in day of month into the the top 5 bits) and converted easily and quickly for whatever format I need to read the time in. This also means I can support multiple types of RTC chips (DS3231/PCF8563/MCP79410 etc) at runtime without hardly any complication and the bonus is that I don't suffer I2C bus conflicts or lengthy transactions. None of this really takes much code memory, I worked out that even with all the fancy formatting and soft and hard RTC code including my general 1ms cog timing task that maintains runtime, milliseconds of the day, linked 32-bit timeouts and alarms etc that this is around 1100 bytes.
Sign In or Register to comment.