In this tutorial, we are going to discuss RTC Interfacing with PIC24. Before that, we should know about the RTC (PCF8563) operations. Let’s start.
Table of Contents
Components Required:
1 pic24
2 pcf8563
3 serial terminal
Features and benefits
Provides year, month, day, weekday, hours, minutes, and seconds based on a
32.768 kHz quartz crystal
Century flag
Clock operating voltage: 1.0 V to 5.5 V at room temperature
Low backup current; typical 0.25 A at VDD = 3.0 V and Tamb = 25 C
400 kHz two-wire I2C-bus interface (at VDD = 1.8 V to 5.5 V)
Programmable clock output for peripheral devices (32.768 kHz, 1.024 kHz, 32 Hz, and
1 Hz)
Alarm and timer functions
Integrated oscillator capacitor
Internal Power-On Reset (POR)
I2C-bus slave address: read A3h and write A2h
Open-drain interrupt pic
I2c Introduction
2C is a serial protocol for a two-wire interface to connect low-speed devices like microcontrollers, EEPROMs, A/D and D/A converters, I/O interfaces, and other similar peripherals in embedded systems. It was invented by Philips and now it is used by almost all major IC manufacturers. Each I2C slave device needs an address – they must still be obtained from NXP (formerly Philips semiconductors).
The I2C bus is popular because it is simple to use, there can be more than one master, only upper bus speed is defined and only two wires with pull-up resistors are needed to connect an almost unlimited number of I2C devices. I2C can use even slower microcontrollers with general-purpose I/O pins since they only need to generate correct Start and Stop conditions in addition to functions for reading and writing a byte.
Step 1
Setting Baud Rate When Operating as a Bus Master
How to implement in code
unsigned long u32_temp;
unsigned char temp;
/* Calculate the value to be written in I2C Baud rate generator register for I2C_CLK baud rate */
u32_temp = (FCYC/I2C_CLK);
u32_temp = u32_temp - (FCYC/10000000) - 1;
I2C2BRG = u32_temp;
step 2
How to start i2c communication
1st open your data sheet and check
- Check I2C Control Register (I2CXCON)
- Check I2C Status Register (I2CXSTAT)
- Check I2C STOP Register (I2C2CONbits.PEN =1)
- Check I2C Reset registor (I2C2CONbits.RSEN = 1)
- Check RTC TIMER Address
- Check RTC Calender address
The PCF8563 contains sixteen 8-bit registers with an auto-incrementing register address, an on-chip 32.768 kHz oscillator with one integrated capacitor, a frequency divider which provides the source clock for the Real-Time Clock (RTC) and calendar, a programmable clock output, a timer, an alarm, a voltage-low detector, and a 400 kHz I2C-bus interface.
All 16 registers are designed as addressable 8-bit parallel registers although not all bits are implemented. The first two registers (memory address 00h and 01h) are used as control and/or status registers. The memory addresses 02h through 08h are used as counters for the clock function (seconds up to years counters). Address locations 09h through 0Ch contain alarm registers which define the conditions for an alarm. Address 0Dh controls the CLKOUT output frequency. 0Eh and 0Fh are the Timer control and Timer registers, respectively.
The Seconds, Minutes, Hours, Days, Months, Years as well as the Minute_alarm, Hour_alarm, and Day_alarm registers are all coded in Binary Coded Decimal (BCD) format.
Procedure for Writing Time and Date
Setting and reading the time
During read/write operations, the time counting circuits (memory locations 02h through 08h) are blocked.
This prevents
• Faulty reading of the clock and calendar during a carry condition
• Incrementing the time registers, during the read cycle
After this read/write access is completed, the time circuit is released again and any pending request to increment the time counters that occurred during the read access is serviced. A maximum of 1 request can be stored; therefore, all accesses must be completed within 1 second.
As a consequence of this method, it is very important to make a read or write access in one go, that is, setting or reading seconds through to years should be made in one single access. Failing to comply with this method could result in the time becoming corrupted.
As an example, if the time (seconds through to hours) is set in one access and then in a second access the date is set, it is possible that the time may increment between the two accesses. A similar problem exists when reading. A roll over may occur between reads thus giving the minutes from one moment and the hours from the next.
STOP bit function
The function of the STOP bit is to allow for accurate starting of the time circuits. The STOP bit function will cause the upper part of the prescaler (F2 to F14) to be held in reset and thus no 1 Hz ticks will be generated (see Figure 10). The time circuits can then be set and will not increment until the STOP bit is released (see Figure 11 and Table 26).
The STOP bit function will not affect the output of 32.768 kHz on CLKOUT, but will stop the generation of 1.024 kHz, 32 Hz, and 1 Hz. The lower two stages of the prescaler are not reset; and because the I2C-bus is asynchronous to the crystal oscillator, the accuracy of re-starting the time circuits will be between zero and one 8.192 kHz cycle.
I2C-bus protocol
Addressing
Before any data is transmitted on the I2C-bus, the device which should respond is addressed first. The addressing is always carried out with the first byte transmitted after the start procedure.
The PCF8563 acts as a slave receiver or slave transmitter. Therefore the clock signal SCL is only an input signal, but the data signal SDA is a bidirectional line.
Two slave addresses are reserved for the PCF8563:
Read: A3h (10100011)
Write: A2h (10100010)
The PCF8563 slave address is illustrated.
CODE STEP BY STEP
I2C INIT
void ini_i2c2(void){
//I2C2BRG = 0x009d ; // Fcy = 16 MHz -> Fscl = 100 kHz
I2C2BRG = 49 ; // Fcy = 5 MHz -> Fscl = 100 kHz
I2C2STAT= 0X0000 ;
I2C2CON = 0x1200 ; // release SCL1 clock, 7-bit slave address
// slew rate control disabled
I2C2RCV = 0x0000 ; // clear the transmit and receive registers
I2C2TRN = 0x0000;
I2C2CONbits.I2CEN=1 ; } // enable the I2C2 module
I2C START
void i2c_start(void)
{
while (I2C2STATbits.TRSTAT); // Wait for bus Idle
I2C2CONbits.SEN = 1 ; // Generate Start Condition
poll_tim1() ;
} // Wait for I2C/Timer1 interrupt flag
I2C STOP
void i2c_stop(void){
while (I2C2STATbits.TRSTAT); // Wait for bus Idle
I2C2CONbits.PEN = 1 ; // Generate Stop Condition
poll_tim1() ; } // Wait for I2C/Timer1 interrupt flag
I2C RESET
void i2c_restart(void){
while (I2C2STATbits.TRSTAT); // Wait for bus Idle
I2C2CONbits.RSEN = 1 ; // Generate a Restart
poll_tim1() ; } // Wait for I2C/Timer1 interrupt flag
I2C WRITE
void i2c_wr(unsigned char i2c_data){
while (I2C2STATbits.TRSTAT); // Wait for bus Idle
I2C2TRN=i2c_data ; // Load byte in the transmit buffer
poll_tim1() ; } // Wait for I2C/Timer1 interrupt flag
READ TIME
void Read_Time(char *sec, char *min1, char *hr) {
i2c_start();
i2c_wr(0xA2); // read address universal pcf8563
i2c_wr(0x02); //starts from addres for seconds
i2c_restart();
i2c_wr(0xA3); //A0+1 read data
*sec =i2c_rd1(0) & 0b01111111; //seconds and ignoring vl bit
*min1 =i2c_rd1(0)& 0b01111111; //minutes and ignoring unwanted msb bit (7 bit as per datasheet pcf8563 6th page)
*hr =i2c_rd1(1)& 0b00111111; //hours , ignoring unwanted msb bits (6,7 bits)
i2c_stop();
//UART1_Write(13);UART1_Write(10); //Delay_ms(100);
}
READ CALENDER
void Read_calendar(char *day,char *wkday,char *mn,char *year)
{
i2c_start();
i2c_wr(0xA2); //like A0 Example
i2c_wr(0x05); //starts from
i2c_restart();
i2c_wr(0xA3); //A0+1
*day =i2c_rd1(0)& 0b00111111; //day , ignoring unwanted msb bits (6,7 bits)
*wkday =i2c_rd1(0)& 0b00000111; //wkday , ignoring unwanted msb bits (3,4,5,6,7 bits)
*mn =i2c_rd1(0)& 0b00011111; //month , ignoring unwanted msb bits (5,6,7 bits)
*year =i2c_rd1(1); //year
i2c_stop();
//UART1_Write(13);UART1_Write(10); //Delay_ms(100);
}//~
CONVERT BCD FORMAT
int BCDToDecimal(int BCD)
{
return (((BCD>>4)*10) + (BCD & 0xF));
}
I2C ACT
void i2c_ack(void)
{
I2C2CONbits.ACKDT = 0 ; // Clear the related flag for Ack
I2C2CONbits.ACKEN = 1 ; // Start Ack sequence
poll_tim1() ; } // Wait for I2C/Timer1 interrupt flag
FINAL CODE
/*
* File: RTC_TEST_5.c
* Author: Devilal
*
* Created on 5 May, 2021, 8:43 PM
*/
// add freqency
//#define _XTAL_FREQ 20000000
#define FCY 10000000UL // Instruction cycle frequency, Hz - required for __delayXXX() to work
// add delay header file
#include <libpic30.h>
#include "string.h"
#include "libpic30.h"
#include "xc.h"
#include "stdio.h"
#include "stdint.h"
#include "stdlib.h"
#include "xc.h"
int uart_index,uart3_index,uart4_index;
char uart1_buffer[100];
uint8_t uart3_buffer[128];
uint8_t uart4_buffer[128];
// CONFIG3
#pragma config WPFP = WPFP511 // Write Protection Flash Page Segment Boundary (Highest Page (same as page 85))
#pragma config WPDIS = WPDIS // Segment Write Protection Disable bit (Segmented code protection disabled)
#pragma config WPCFG = WPCFGDIS // Configuration Word Code Page Protection Select bit (Last page(at the top of program memory) and Flash configuration words are not protected)
#pragma config WPEND = WPENDMEM // Segment Write Protection End Page Select bit (Write Protect from WPFP to the last page of memory)
// CONFIG2
#pragma config POSCMOD = XT // Primary Oscillator Select (XT oscillator mode selected)
#pragma config IOL1WAY = ON // IOLOCK One-Way Set Enable bit (Write RP Registers Once)
#pragma config OSCIOFNC = OFF // Primary Oscillator Output Function (OSCO functions as CLKO (FOSC/2))
#pragma config FCKSM = CSDCMD // Clock Switching and Monitor (Both Clock Switching and Fail-safe Clock Monitor are disabled)
#pragma config FNOSC = PRI // Oscillator Select (Primary oscillator (XT, HS, EC))
#pragma config IESO = OFF // Internal External Switch Over Mode (IESO mode (Two-speed start-up) enabled)
// CONFIG1
#pragma config WDTPS = PS32768 // Watchdog Timer Postscaler (1:32,768)
#pragma config FWPSA = PR128 // WDT Prescaler (Prescaler ratio of 1:128)
#pragma config WINDIS = OFF // Watchdog Timer Window (Standard Watchdog Timer is enabled,(Windowed-mode is disabled))
#pragma config FWDTEN = OFF // Watchdog Timer Enable (Watchdog Timer is enabled)
#pragma config ICS = PGx3 // Comm Channel Select (Emulator functions are shared with PGEC3/PGED3)
#pragma config GWRP = OFF // General Code Segment Write Protect (Writes to program memory are allowed)
#pragma config GCP = OFF // General Code Segment Code Protect (Code protection is disabled)
#pragma config JTAGEN = OFF // JTAG Port Enable (JTAG port is disabled)
#define ADDR_RTCC_WRITE 0xA2 // DEVICE ADDR for RTCC MCHP (writes)
#define ADDR_RTCC_READ 0xA3 // DEVICE ADDR for RTCC MCHP (reads)
#define ADDR_RTCC_WRITE 0xA2 // DEVICE ADDR for RTCC MCHP (writes)
#define ADDR_RTCC_READ 0xA3 // DEVICE ADDR for RTCC MCHP (reads)
#define ADDR_SEC 0x02 // address of SECONDS register
#define ADDR_MIN 0x03 // address of MINUTES register
#define ADDR_HOUR 0x04 // address of HOURS register
#define ADDR_DATE 0x05 // address of DATE register
#define ADDR_DAY 0x06 // address of DAY OF WK register
#define ADDR_MNTH 0x07 // address of MONTH register
#define ADDR_YEAR 0x08 // address of YEAR register
#define OSC_STAT 0x00
#define OSCRUN 0x00 // state of the oscillator(running or not)
#define I2C_CLK 100000 //100KHz
#define FOSC 8000000 //8MHz
#define FCYC (FOSC/2)
#define TIMEOUT 0x01FF
#define OK 0
#define ERROR 1
unsigned char err_flg ; // global error flag
typedef unsigned char uchar8;
uchar8 SetSec = 0x00;
uchar8 SetMin = 0x00;
uchar8 SetHour = 0x00;
char timer[30];
char calender[20];
uchar8 RtcInitFlag = 0;
uchar8 ReadYear = 0x00,ReadMonth = 0x00,ReadDay = 0x00,ReadDate = 0x00,ReadWeek = 0x00,ReadHour = 0x00,ReadMin = 0x00,ReadSec = 0x00;
unsigned char sec, min1,min, hr, day, wkday, mn, year;
int hr2,min2;
long rtc_tseconds;
int flag;
int son;
int soff;
typedef union
{
unsigned char RTCRawBuffer[7];
struct
{
unsigned char RTC_Sec;
unsigned char RTC_Min;
unsigned char RTC_Hour;
unsigned char RTC_Day;
unsigned char RTC_Date;
unsigned char RTC_Month;
unsigned char RTC_Year;
}RTCReg;
}RTC_t;
RTC_t RTC;
void UART4Init(void)
{
// U4BRG = 25; // 9600
U4BRG = 32; // 9600
U4MODEbits.UARTEN = 1; // UART2 is Enabled
U4MODEbits.USIDL = 0; // Continue operation at Idlestate
U4MODEbits.IREN = 0; // IrDA En/Decoder is disabled
U4MODEbits.RTSMD = 0; // flow control mode
U4MODEbits.UEN1 = 0b00; // UTX, RTX, are enabled U2CTS, U2RTS are disabled
U4MODEbits.UEN0 = 0b00; // UTX, RTX, are enabled U2CTS, U2RTS are disabled
U4MODEbits.WAKE = 1; // Wake-up on start bit is enabled
U4MODEbits.LPBACK = 0; // Loop-back is disabled
U4MODEbits.ABAUD = 0; // auto baud is disabled
U4MODEbits.RXINV = 0; // No RX inversion
U4MODEbits.BRGH = 0; // low boud rate
U4MODEbits.PDSEL = 0b00; // 8bit no parity
U4MODEbits.STSEL = 0; // one stop bit
U4STAbits.UTXISEL1 = 0b00;
U4STA &= 0xDFFF; // clear TXINV by bit masking
U4STAbits.UTXBRK = 0; // sync break tx is disabled
U4STAbits.UTXEN = 1; //transmit is enabled
U4STAbits.URXISEL = 0b00; // interrupt flag bit is set when RXBUF is filled whith 1 character
U4STAbits.ADDEN = 0; // address detect mode is disabled
IPC22bits.U4RXIP = 7; // sET uart1 Priority to 6
IFS5bits.U4RXIF = 0; // clear interrupt flag of rx
IFS5bits.U4TXIF = 0; // clear interrupt flag of rx
IEC5bits.U4RXIE = 1; // enAble rx recieved data interrupt
IEC5bits.U4TXIE = 0;
}
void UART4TransmitString(uint8_t *tx_str,uint16_t size)
{
_CNIE = 0;
uint8_t ch,i=0;
uart4_index = 0;
uint8_t temp = 0;
while (temp++<size)
{
ch = *tx_str;
U4TXREG = ch;
while(!(U4STAbits.TRMT))
{
}
tx_str++;
for(i=0;i<200;i++)
{
}
}
_CNIE = 1;
}
void __attribute__((interrupt,no_auto_psv)) _U4RXInterrupt()
{
uint8_t ch4;
_U4RXIF = 0;
ch4 = U4RXREG;
uart4_buffer[uart4_index++]=ch4;
if(uart4_index>350)uart4_index=0;
}
void poll_tim1(void)
{
T1CONbits.TON=1; // start Timer1
while (!(IFS0bits.T1IF|IFS3bits.MI2C2IF));
if (IFS0bits.T1IF){ // if Timer1 overflowed (the I2C device is unplugged)
IFS0bits.T1IF=0 ; // clear Timer1 & I2C Interrupt Flags
IFS3bits.MI2C2IF=0 ;
err_flg = 0x01 ; // set the error flag if a time-out occured
//_LATA = 0xff ;
} // set a breakpoint on LEDs
else { err_flg=0x00 ; // if no error in the I2C comm, clear the error flag
IFS3bits.MI2C2IF=0 ; } // and the I2C general flag
T1CONbits.TON=0 ; // stop Timer1 in any case
TMR1 = 0x0000 ;
} // and clear it
void ini_i2c2(void){
//I2C2BRG = 0x009d ; // Fcy = 16 MHz -> Fscl = 100 kHz
I2C2BRG = 49 ; // Fcy = 5 MHz -> Fscl = 100 kHz
I2C2STAT= 0X0000 ;
I2C2CON = 0x1200 ; // release SCL1 clock, 7-bit slave address
// slew rate control disabled
I2C2RCV = 0x0000 ; // clear the transmit and receive registers
I2C2TRN = 0x0000;
I2C2CONbits.I2CEN=1 ; } // enable the I2C2 module
void i2c_start(void)
{
while (I2C2STATbits.TRSTAT); // Wait for bus Idle
I2C2CONbits.SEN = 1 ; // Generate Start Condition
poll_tim1() ;
} // Wait for I2C/Timer1 interrupt flag
void i2c_wr(unsigned char i2c_data){
while (I2C2STATbits.TRSTAT); // Wait for bus Idle
I2C2TRN=i2c_data ; // Load byte in the transmit buffer
poll_tim1() ; } // Wait for I2C/Timer1 interrupt flag
void i2c_stop(void){
while (I2C2STATbits.TRSTAT); // Wait for bus Idle
I2C2CONbits.PEN = 1 ; // Generate Stop Condition
poll_tim1() ; } // Wait for I2C/Timer1 interrupt flag
void i2c_restart(void){
while (I2C2STATbits.TRSTAT); // Wait for bus Idle
I2C2CONbits.RSEN = 1 ; // Generate a Restart
poll_tim1() ; } // Wait for I2C/Timer1 interrupt flag
void RTC_init() //rtc initialization
{
ini_i2c2();
i2c_start(); // issue start signal
i2c_wr(0x51); // slave address PCF8563
i2c_stop();
}
unsigned char i2c_rd1(unsigned ack)
{
unsigned char rx_byte = 0xff;
while (I2C2STATbits.TRSTAT); // Wait for bus Idle
I2C2CONbits.RCEN = 1 ; // Enable Master receive
//IFS2bits.MI2C1IF = 0;
poll_tim1(); // Wait for I2C/Timer1 interrupt flag
rx_byte = I2C2RCV;
if(ack == 0)
i2c_ack();
else
i2c_nack();
return(rx_byte) ; } // Return data in buffer
void Read_Time(char *sec, char *min1, char *hr) {
i2c_start();
i2c_wr(0xA2); // read address universal pcf8563
i2c_wr(0x02); //starts from addres for seconds
i2c_restart();
i2c_wr(0xA3); //A0+1 read data
*sec =i2c_rd1(0) & 0b01111111; //seconds and ignoring vl bit
*min1 =i2c_rd1(0)& 0b01111111; //minutes and ignoring unwanted msb bit (7 bit as per datasheet pcf8563 6th page)
*hr =i2c_rd1(1)& 0b00111111; //hours , ignoring unwanted msb bits (6,7 bits)
i2c_stop();
//UART1_Write(13);UART1_Write(10); //Delay_ms(100);
}
void Read_calendar(char *day,char *wkday,char *mn,char *year)
{
i2c_start();
i2c_wr(0xA2); //like A0 Example
i2c_wr(0x05); //starts from
i2c_restart();
i2c_wr(0xA3); //A0+1
*day =i2c_rd1(0)& 0b00111111; //day , ignoring unwanted msb bits (6,7 bits)
*wkday =i2c_rd1(0)& 0b00000111; //wkday , ignoring unwanted msb bits (3,4,5,6,7 bits)
*mn =i2c_rd1(0)& 0b00011111; //month , ignoring unwanted msb bits (5,6,7 bits)
*year =i2c_rd1(1); //year
i2c_stop();
//UART1_Write(13);UART1_Write(10); //Delay_ms(100);
}//~
int BCDToDecimal(int BCD)
{
return (((BCD>>4)*10) + (BCD & 0xF));
}
void Transform_Time(char *sec, char *min, char *hr)
{
*sec = BCDToDecimal(*sec); // Transform seconds
*min = BCDToDecimal(*min); // Transform minutes
*hr = BCDToDecimal(*hr);// Transform hours
int kapil;
//UART4TransmitString(hr2,sizeof(hr2));
memset(timer,'\0',sizeof(timer));
// sprintf(timer,"%d",(*hr*100)+(*min));
hr2=(*hr*100)+(*min); //atoi(timer);
sprintf(timer,"RTC %d:",hr2);
// UART4TransmitString(timer,sizeof(timer));
}//~
void Transform_calendar(char *day ,char *wkday, char *mn, char *year)
{
*day = BCDToDecimal(*day);// Transform day
*wkday = BCDToDecimal(*wkday); // Transform weakday
*mn = BCDToDecimal(*mn); // Transform months
*year = BCDToDecimal(*year);// Transform year
memset(calender,'\0',sizeof(calender));
sprintf(calender,"%d-%d-%d\r\n",*day,*mn,*year);
strcat(timer,calender);
}//~
void i2c_ack(void)
{
I2C2CONbits.ACKDT = 0 ; // Clear the related flag for Ack
I2C2CONbits.ACKEN = 1 ; // Start Ack sequence
poll_tim1() ; } // Wait for I2C/Timer1 interrupt flag
void i2c_nack(void)
{
I2C2CONbits.ACKDT = 1 ; // Set the related flag for NotAck
I2C2CONbits.ACKEN = 1 ; // Start Ack sequence
poll_tim1() ; // Wait for I2C/Timer1 interrupt flag
I2C2CONbits.ACKDT = 0 ; } // Clear the related flag for ACk
void rtc_time_write()
{
ini_i2c2();
i2c_start(); // issue I2C start signal
i2c_wr(0xA2); // send byte via I2C (command to 24cO2)
i2c_wr(0x02); // seconds address
i2c_wr(0x00); // seconds
i2c_wr(0x37); // minutes
i2c_wr(0x13); // hours
i2c_stop();
}//~!
void rtc_calender_write()
{
ini_i2c2();
i2c_start(); // issue I2C start signal
i2c_wr(0xA2); // send byte via I2C (command to 24cO2)
i2c_wr(0x05); // starts from days address asper pcf8623 data sheet
i2c_wr(0x06); // current date(live date)
i2c_wr(0x01); // weak day like 0 t0 7
i2c_wr(0x05); // centure months
i2c_wr(0x21); // year
i2c_stop();
}
int main(void)
{
OSCCON = 0x0000;
OSCTUN = 0x0000;
CLKDIV = 0x0000;
//resetSource = RCON;
RCON = 0x00;
INTCON1bits.NSTDIS = 1;
AD1PCFGL = 0xFF; //dISABLE ANALOG I/P'S
//////// UART 4 /////////////
RPINR27bits.U4RXR = 23; // Assign U4RXR to RP23
_TRISD2 = 1; //CONFIGURE PIN TO I/P
RPOR12bits.RP24R = 30; //Assign U4TX To Pin RP24
_TRISD1 = 0; //CONFIGURE PIN TO O/P
CNPU1bits.CN9PUE = 1;
UART4Init();
RTC_init();
UART4TransmitString("Start Application\r\n",sizeof("Start Application\r\n"));
char on1[10];
son=1300;
soff=1339;
// sprintf(on1,"on %04d",son);
// sprintf(on1,"on %04d",soff);
// rtc_time_write();
// rtc_calender_write();
__delay_ms(1000);
while (1)
{
Read_Time(&sec,&min1,&hr); // read time from RTC(PCF8583)
Transform_Time(&sec,&min1,&hr); // format date and time
Read_calendar(&day,&wkday,&mn,&year);
Transform_calendar(&day,&wkday,&mn,&year);
if(( (hr2>son) && (hr2<2400) )&& ((hr2>0000) && (hr2<soff)))
//if((soff>=son))
{
//if((son<=timer)&&(soff>timer))
flag = 1;
UART4TransmitString("relay on\t",sizeof("relay on\t"));
}
else
{
flag = 0;
UART4TransmitString("relay off\t",sizeof("relay off\t"));
}
UART4TransmitString(timer,sizeof(timer));
__delay_ms(500);
}
return 0;
}