|
|
View previous topic :: View next topic |
Author |
Message |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
PCF8583 driver |
Posted: Tue Aug 22, 2006 12:53 am |
|
|
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
|
|
Posted: Tue Apr 01, 2008 1:47 am |
|
|
Thank you very much you saved lots of my work
_________________ sohail khan |
|
|
Jettro
Joined: 03 Jun 2008 Posts: 1
|
Freakin Brilliant! |
Posted: Tue Jun 03, 2008 10:04 am |
|
|
Thanks mucho for this! |
|
|
vasiliok
Joined: 17 May 2011 Posts: 19 Location: Kaunas, Lithuania
|
|
Posted: Wed Jun 29, 2011 2:10 pm |
|
|
Thanx a lot!
Working fine with PIC18F14K50
CCS 4.120
Woohoo! |
|
|
Bill Legge
Joined: 07 Sep 2010 Posts: 24 Location: West Australia
|
PCF8583 |
Posted: Sat Jul 02, 2011 10:27 pm |
|
|
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
|
|
Posted: Tue Mar 20, 2012 12:22 am |
|
|
Thank you for this driver PCM Programmer, this helped me alot. |
|
|
kieranjefferies
Joined: 22 Dec 2016 Posts: 6 Location: Ireland
|
PCF8583 |
Posted: Wed Dec 27, 2017 3:48 pm |
|
|
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 |
|
|
|
|
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
|