CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

PCF8583 driver

 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PCF8583 driver
PostPosted: Tue Aug 22, 2006 12:53 am     Reply with quote

Here is a driver for the PCF8583 real time clock/calender chip for
the CCS compiler. It was tested with the PCM compiler on a 16F877
and with PCH on a 18F452. It was tested with compiler versions
3.191, 3.203, 3.236, and 3.249.

Here are some schematics that show how to connect the PCF8583
to a PIC. The schematic from Dontronics has the A0 pin connected to
Vdd, which will change the i2c address to 0xA2. This is necessary if
an EEPROM is used on the same bus, because the normal EEPROM
address is 0xA0 (same as the PCF8583). To avoid a conflict, either
the PCF8583 or the EEPROM must be configured to use a different
address.
http://www.mikroe.com/pdf/rtc_board_schematic.pdf
http://www.dontronics.com/pdf/4D-16F73-Module.pdf

This is not really the best RTC chip, because it doesn't have a full
8-bit register for the year and because it doesn't have a dedicated
pin for battery backup. The DS1307 is probably a better chip for
those reasons. However, a PCF8583 driver is requested every few
months on the CCS forum so I decided to create one and post it.
I have included a feature to emulate a 'year' register by storing the
year in a spare byte of NVRAM. As long as you read the date at
least once every 3 years or so, the driver will maintain the proper
year. If that's not good enough, then you should use a DS1307
instead.

Here is a simple test program for the driver. The driver code is
posted below the test program.
Code:

#include <16F877.H>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)

#include <ctype.h>

#include <PCF8583.c>

// The default date format for this test program is MM/DD/YY.
// If you want to use Euro format (DD/MM/YY), then un-comment
// the following line.
// #define USE_EURO_DATE_FORMAT  1

//=================================
void main()
{
char c;
char weekday[10];
date_time_t dt;


PCF8583_init();

// Allow the user to write a preset date and time to the
// PCF8583, if desired. 
printf("Do you want to write a sample date/time of\n\r");

#ifdef USE_EURO_DATE_FORMAT
printf("23/12/06 23:59:50 (Sunday) to the PCF8583 ? (Y/N)\n\r");
#else  // Use U.S. date format
printf("12/23/06 23:59:50 (Sunday) to the PCF8583 ? (Y/N)\n\r");
#endif

while(1)
  {
   c = getc();    // Wait for user to press a key
   c = toupper(c);
   
   if(c == 'Y')
     {
      dt.month   = 12;    // December
      dt.day     = 31;    // 31
      dt.year    = 06;    // 2006
      dt.hours   = 23;    // 23 hours (11pm in 24-hour time)
      dt.minutes = 59;    // 59 minutes 
      dt.seconds = 50;    // 50 seconds
      dt.weekday = 0;     // 0 = Sunday, 1 = Monday, etc.

      PCF8583_set_datetime(&dt);       
      printf("\n\r");
      printf("New date/time written to PCF8583.\n\r");
      printf("Watch it rollover to 2007.\n\r");
      break;
     }

   if(c == 'N')
      break; 
  }

printf("\n\r");
printf("Reading date/time from PCF8583:\n\r");

// Read the date and time from the PCF8583 and display
// it once per second.
while(1)
  {
   delay_ms(1000);   

   PCF8583_read_datetime(&dt);   

   strcpy(weekday, weekday_names[dt.weekday]);

   #ifdef USE_EURO_DATE_FORMAT
   printf("%s, %u/%u/%02u, %u:%02u:%02u\n\r",
           weekday, dt.day, dt.month, dt.year,
           dt.hours, dt.minutes, dt.seconds);
   #else  // Use U.S. date format
   printf("%s, %u/%u/%02u, %u:%02u:%02u\n\r",
           weekday, dt.month, dt.day, dt.year,
           dt.hours, dt.minutes, dt.seconds);
   #endif
  }

}


Here is the driver file:
Code:

// PCF8583.C

#ifndef PCF8583_SDA
#define PCF8583_SDA  PIN_C4
#define PCF8583_SCL  PIN_C3
#endif

#use i2c(master, sda=PCF8583_SDA, scl=PCF8583_SCL)

#ifndef PCF8583_WRITE_ADDRESS
#define PCF8583_WRITE_ADDRESS 0xA0
#define PCF8583_READ_ADDRESS  0xA1
#endif

// Register addresses
#define PCF8583_CTRL_STATUS_REG    0x00
#define PCF8583_100S_REG           0x01
#define PCF8583_SECONDS_REG        0x02
#define PCF8583_MINUTES_REG        0x03
#define PCF8583_HOURS_REG          0x04
#define PCF8583_DATE_REG           0x05
#define PCF8583_MONTHS_REG         0x06
#define PCF8583_TIMER_REG          0x07

#define PCF8583_ALARM_CONTROL_REG  0x08
#define PCF8583_ALARM_100S_REG     0x09
#define PCF8583_ALARM_SECS_REG     0x0A
#define PCF8583_ALARM_MINS_REG     0x0B
#define PCF8583_ALARM_HOURS_REG    0x0C
#define PCF8583_ALARM_DATE_REG     0x0D
#define PCF8583_ALARM_MONTHS_REG   0x0E
#define PCF8583_ALARM_TIMER_REG    0x0F

// Use the first NVRAM address for the year byte.
#define PCF8583_YEAR_REG           0x10


// Commands for the Control/Status register.
#define PCF8583_START_COUNTING     0x00
#define PCF8583_STOP_COUNTING      0x80



char const weekday_names[7][10] =
{
{"Sunday"},
{"Monday"},
{"Tuesday"},
{"Wednesday"},
{"Thursday"},
{"Friday"},
{"Saturday"}
};   
               
// This structure defines the user's date and time data.
// The values are stored as unsigned integers.  The user
// should declare a structure of this type in the application
// program. Then the address of the structure should be
// passed to the PCF8583 read/write functions in this
// driver, whenever you want to talk to the chip.
typedef struct
{
int8 seconds;    // 0 to 59
int8 minutes;    // 0 to 59
int8 hours;      // 0 to 23  (24-hour time)
int8 day;        // 1 to 31
int8 month;      // 1 to 12
int8 year;       // 00 to 99
int8 weekday;    // 0 = Sunday, 1 = Monday, etc.
}date_time_t;


//----------------------------------------------
void PCF8583_write_byte(int8 address, int8 data)
{
disable_interrupts(GLOBAL);
i2c_start();
i2c_write(PCF8583_WRITE_ADDRESS);
i2c_write(address);
i2c_write(data);
i2c_stop();
enable_interrupts(GLOBAL);
}   

//----------------------------------------------
int8 PCF8583_read_byte(int8 address)
{
int8 retval;

disable_interrupts(GLOBAL);
i2c_start();
i2c_write(PCF8583_WRITE_ADDRESS);
i2c_write(address);
i2c_start();
i2c_write(PCF8583_READ_ADDRESS);
retval = i2c_read(0);
i2c_stop();
enable_interrupts(GLOBAL);
return(retval);
}   


void PCF8583_init(void)
{
PCF8583_write_byte(PCF8583_CTRL_STATUS_REG,
                              PCF8583_START_COUNTING);
}   

//----------------------------------------------
// This function converts an 8 bit binary value
// to an 8 bit BCD value.
// The input range must be from 0 to 99.

int8 bin2bcd(int8 value)
{
char retval;

retval = 0;

while(1)
  {
   // Get the tens digit by doing multiple subtraction
   // of 10 from the binary value.
   if(value >= 10)
     {
      value -= 10;
      retval += 0x10;
     }
   else // Get the ones digit by adding the remainder.
     {
      retval += value;
      break;
     }
   }

return(retval);
}

//----------------------------------------------
// This function converts an 8 bit BCD value to
// an 8 bit binary value.
// The input range must be from 00 to 99.

char bcd2bin(char bcd_value)
{
char temp;

temp = bcd_value;

// Shifting the upper digit right by 1 is
// the same as multiplying it by 8.
temp >>= 1;

// Isolate the bits for the upper digit.
temp &= 0x78;

// Now return: (Tens * 8) + (Tens * 2) + Ones
return(temp + (temp >> 2) + (bcd_value & 0x0f));

}

//----------------------------------------------
void PCF8583_set_datetime(date_time_t *dt)
{
int8 bcd_sec;
int8 bcd_min;
int8 bcd_hrs;
int8 bcd_day;
int8 bcd_mon;

// Convert the input date/time into BCD values
// that are formatted for the PCF8583 registers. 
bcd_sec = bin2bcd(dt->seconds);
bcd_min = bin2bcd(dt->minutes);
bcd_hrs = bin2bcd(dt->hours);   
bcd_day = bin2bcd(dt->day) | (dt->year << 6);
bcd_mon = bin2bcd(dt->month) | (dt->weekday << 5);

// Stop the RTC from counting, before we write to
// the date and time registers.
PCF8583_write_byte(PCF8583_CTRL_STATUS_REG,
                              PCF8583_STOP_COUNTING);

// Write to the date and time registers.  Disable interrupts
// so they can't disrupt the i2c operations.
disable_interrupts(GLOBAL);
i2c_start();
i2c_write(PCF8583_WRITE_ADDRESS);
i2c_write(PCF8583_100S_REG);   // Start at 100's reg.   
i2c_write(0x00);               // Set 100's reg = 0
i2c_write(bcd_sec); 
i2c_write(bcd_min);
i2c_write(bcd_hrs);   
i2c_write(bcd_day);
i2c_write(bcd_mon);
i2c_stop();
enable_interrupts(GLOBAL);

// Write the year byte to the first NVRAM location.
// Leave it in binary format.
PCF8583_write_byte(PCF8583_YEAR_REG, dt->year);

// Now allow the PCF8583 to start counting again.
PCF8583_write_byte(PCF8583_CTRL_STATUS_REG,
                              PCF8583_START_COUNTING);
}

//----------------------------------------------
// Read the Date and Time from the hardware registers
// in the PCF8583.   We don't have to disable counting
// during read operations, because according to the data
// sheet, if any of the lower registers (1 to 7) is read,
// all of them are loaded into "capture" registers.
// All further reading within that cycle is done from
// those registers.

void PCF8583_read_datetime(date_time_t *dt)
{
int8 year_bits;
int8 year;

int8 bcd_sec;
int8 bcd_min;
int8 bcd_hrs;
int8 bcd_day;
int8 bcd_mon;

// Disable interrupts so the i2c process is not disrupted.
disable_interrupts(GLOBAL);

// Read the date/time registers inside the PCF8583.
i2c_start();
i2c_write(PCF8583_WRITE_ADDRESS);
i2c_write(PCF8583_SECONDS_REG);   // Start at seconds reg.
i2c_start();
i2c_write(PCF8583_READ_ADDRESS);

bcd_sec = i2c_read();     
bcd_min = i2c_read();     
bcd_hrs = i2c_read(); 
bcd_day = i2c_read();
bcd_mon = i2c_read(0);
i2c_stop();

enable_interrupts(GLOBAL);

// Convert the date/time values from BCD to
// unsigned 8-bit integers.  Unpack the bits
// in the PCF8583 registers where required.
dt->seconds = bcd2bin(bcd_sec);     
dt->minutes = bcd2bin(bcd_min);     
dt->hours   = bcd2bin(bcd_hrs & 0x3F); 
dt->day     = bcd2bin(bcd_day & 0x3F);
dt->month   = bcd2bin(bcd_mon & 0x1F);
dt->weekday = bcd_mon >> 5;
year_bits   = bcd_day >> 6;   

// Read the year byte from NVRAM.
// This is an added feature of this driver.
year = PCF8583_read_byte(PCF8583_YEAR_REG);

// Check if the two "year bits" were incremented by
// the PCF8583.  If so, increment the 8-bit year
// byte (read from NVRAM) by the same amount.
while(year_bits != (year & 3))
      year++;

dt->year = year;

// Now update the year byte in the NVRAM
// inside the PCF8583.
PCF8583_write_byte(PCF8583_YEAR_REG, year);

}
sohailkhanonline



Joined: 06 Mar 2008
Posts: 35
Location: pakistan

View user's profile Send private message Visit poster's website Yahoo Messenger MSN Messenger

PostPosted: Tue Apr 01, 2008 1:47 am     Reply with quote

Thank you very much you saved lots of my work
Smile
_________________
sohail khan
Jettro



Joined: 03 Jun 2008
Posts: 1

View user's profile Send private message Yahoo Messenger MSN Messenger

Freakin Brilliant!
PostPosted: Tue Jun 03, 2008 10:04 am     Reply with quote

Thanks mucho for this!
vasiliok



Joined: 17 May 2011
Posts: 19
Location: Kaunas, Lithuania

View user's profile Send private message

PostPosted: Wed Jun 29, 2011 2:10 pm     Reply with quote

Thanx a lot!

Working fine with PIC18F14K50

CCS 4.120

Woohoo! Very Happy
Bill Legge



Joined: 07 Sep 2010
Posts: 24
Location: West Australia

View user's profile Send private message

PCF8583
PostPosted: Sat Jul 02, 2011 10:27 pm     Reply with quote

Thank you. Working fine with no changes on:

Mikroelektronika BIGPIC5 board
With PIC18F8722 at 4X10MHz

Regards Bill Legge
_________________
Denmark in West Australia
'Where the forest meets the sea'
danyboy666



Joined: 01 Mar 2011
Posts: 30

View user's profile Send private message

PostPosted: Tue Mar 20, 2012 12:22 am     Reply with quote

Thank you for this driver PCM Programmer, this helped me alot.
kieranjefferies



Joined: 22 Dec 2016
Posts: 6
Location: Ireland

View user's profile Send private message

PCF8583
PostPosted: Wed Dec 27, 2017 3:48 pm     Reply with quote

Thanks PCM Programmer, your driver from way back is working perfectly on a PIC24FJ128GA204 using PCWHD 5.075. And I had not came across 'typedef' before and its use in your driver has shown me how to use it, thanks again, Kieran
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group