• AVR Freaks
Reply to post

Hot!Unable to read changing values from a PCF8563 RTC. Read values stuck to 0x3F or 0xFF

Author
jpdesroc
Starting Member
  • Total Posts : 67
  • Reward points : 0
  • Joined: 2011/01/31 11:20:06
  • Location: 0
  • Status: offline
2020/03/07 15:39:19 (permalink)
0

Unable to read changing values from a PCF8563 RTC. Read values stuck to 0x3F or 0xFF

I'm trying to write and read a PCF8653 RTC and I only get
0x3F and 0xFF values read back from the I2C channel.
I use a PIC16F877 and MPLAB X with a ICD3 debugger.
I first write some numbers in BCD format in the RTC registers
then I read them back in a while(1) loop.
I checked the SDA and SCL data on my scope and can say
these lines are not stuck at any low or high levels..
They are alive and changing states.
 
By the way I'm using a demo board with a 24C02 I2C EEPROM
connected on the same I2C wire pair and can read and write it
very successfully. 2 x 1K pullup resistors are used.
I changed them with 4K7 and 10k without any changes..
When I put breakpoints to check the time variables
I always get static values like 0x3F or 0xFF
in the sec,min,hour, etc variables.
I hope you will see something I didn't see in my code here:
Thanks for your precious help.
Jean-Pierre

 
**********************************************************************************
Developed by NXP, the PCF8563 is a Real-time clock based on a 32.768KHz crystal.
It comes with one integrated capacitor at OSC0 pin and can communicate with a data rate of 400KHz.
PCF8563 has two distinct addresses, i.e Read(A3h) and write(A2h) address.
A Master device can initiate communication with PCF8563 by sending A3h for a read operation or A2h for a write operation on the bus.

PCF8563 provides a separate register for each of its timing function, i.e. sec, minutes, hours, days, weekdays, month and year.
Along with the timing registers, PCF8563 also provides a set of registers for setting an alarm as well as a timer.
All these registers are read/write registers and have auto-incrementing register addresses.
The programmer must specify the starting address for a read or write operation.
Once the operation is performed, the register address is automatically incremented,
and next operation will take place on the subsequent address.
It is also important to note, that these registers are coded in binary decimal format(BCD).

Below are a few key points to remember, followed by the associated registers with PCF8563.
Key points about PCF8563

    * Provides real-time track of date and time along with alarm and timer functions.
    * Communicates with a data rate of 400Khz at VDD = 1.8V to 5.5V.
    * One integrated capacitor on OSC0 pin.
    * Provides a programmable clock output for other peripheral devices if needed.
    * A3H read address, A2H write address.
    * PCF8563 clock can operate at a voltage as low as 1V up to 5.5V at room temperature.

Typical Read/Write sequence for PCF8563

The following is a typical write sequence for setting date and time in PCF8563 RTC.

    Generate the START condition.
    0xA2: Slave address and write.
    0x00: Write the address from where you want to start ?writing?.
    0x00: Write to Control status register 1.
    0x00: Control status register 2.
    0x00: setting seconds to 0.
    0x15: setting minutes to 15.
    0x10: setting hours to 10.
    0x03: setting day 3.
    0x06: Setting weekday to Sunday.
    0x02 Setting Month to February.
    0x19 Setting year to 19.
    Generate STOP condition.

Read sequence for PCF8563.

    Generate START condition.
    0xA2: Slave address and write.
    0x02: Address from where you want to read. (Obviously, you would want to read from seconds).
    Generate a repeated start condition.
    Generate the start condition.
    0xA3: Slave address and Read.
    Set RCEN bit in PIC16F887 to enable Reception. (SSPCON2 register).
    Read seconds received in SSPBUF register. Initiate the acknowledge sequence.
    Repeat the above 2 steps until you receive years.
    Initiate No acknowledge sequence.
    Generate STOP condition.

Program for Interfacing PCF8563 RTC with PIC16F877

The program given below is written in MPLAB X.
No RTC or LCD libraries were used.
*/

#include<pic.h>

#define _XTAL_FREQ 20000000

__CONFIG(FOSC_HS & WDTE_OFF & LVP_OFF);

// Pins for LCD 1602 controls including PORTD bits 7..4
#define RS RB5
#define RW RB4
#define E  RB3


// LCD functions
void lcd_cmd(unsigned char c);
void lcd_data(unsigned char d);
void lcd_init(void);
void enable(void);


// I2c Functions
void i2c_init(void);            // Function to initialize I2C
void i2c_transmit(void);        // Function to transmit data
void i2c_receive(void);         // Function to receive data
void i2c_busIdle(void);         // Function to check if the bus is idle
void i2c_start(void);           // Start condition function
void i2c_stop(void);            // Stop condition function
void i2c_repeatedStart(void);   // Function to initiate repeated start
void i2c_ack(void);             // Acknowledge function
void i2c_Noack(void);           // No Acknowledge Function
void receive_enable(void);      // Function to enable Reception of data


void display_clock(void);          // Function to display data
void display(unsigned int);        // Convert and display received data from HEX to ASCII
void check_weekday(unsigned int);  // Function to check Weekday

unsigned int sec,min,hour,day,weekday,month,year;
void disp_wd(char *wd);


int main()
{
 
  TRISC = 0x18;  // Setting SCL(RC3) and SDA(RC4) data direction to input
  TRISD = 0x00;
  TRISB = 0x00;  // Pins RS, R/W & E of LCD
  PORTB = 0x00;
  PORTD = 0x00;  // Bits 7..4 for LCD 4 bits communications
 
  lcd_init();               // Function to initialize LCD
  i2c_init();               // I2C initialization
  i2c_transmit();           // Set starting data i.e sec, min, hours etc
     
   while(1)
   {
       lcd_cmd(0x80);
       i2c_receive();          // Receive data  
       
       display_clock();        // Display date and time
         
       lcd_cmd(0x89);
       if(hour>0x11)
           disp_wd("PM");
       else
           disp_wd("AM");

       lcd_cmd(0xc9);
       check_weekday(weekday);    // Display Weekdays
       
   }        
    return 0;
}

/////////////////////////////////////////////////
/////////// Functions definitions ///////////////
/////////////////////////////////////////////////

void check_weekday(unsigned int weekday)  // Function to check the weekday
 {
  switch(weekday)
    {
        case 0:
            disp_wd("MON");
            break;
        case 1:
            disp_wd("TUE");
            break;
        case 2:
            disp_wd("WED");
            break;
        case 3:
            disp_wd("THU");
            break;
        case 4:
            disp_wd("FRI");
            break;
        case 5:
            disp_wd("SAT");
            break;
        case 6:
            disp_wd("SUN");
            break;
    }
 }
 
////////////// I2C functions ////////////////////

void i2c_init(void)
 {
 
  SSPCON = 0b00101000;
  SSPCON2 = 0x00;
  SSPSTAT = 0x00;
  SSPADD = 49;  // I2C 100khz bus @ 20Mhz clk --> SSPADD = ((Fosc/BitRate)/4)-1
   
 }

void i2c_busIdle()
 {
  while((SSPCON2 & 0x1F) || (SSPSTAT & 0x04));    // Check if bus is idle
 }

void i2c_start(void)         // Function to initiate start condition
 {
  SEN = 1;
  i2c_busIdle();
 }

void i2c_stop(void)          // Function to initiate stop condition
 {
  PEN=1;
  i2c_busIdle();
 }

void i2c_repeatedStart(void)    // Initiating repeated start condition
 {
  RSEN=1;
  i2c_busIdle();
 }

void ack(void)                 // Function to initiate acknowledge sequence
 {
  ACKDT = 0;
  ACKEN = 1;
  i2c_busIdle();
 }

void Noack(void)              // Function to initiate No acknowledge sequence
 {
  ACKDT = 1;
  ACKEN = 1;
  i2c_busIdle();
 }

void receive_enable(void)      // Function to enable reception
 {
  RCEN =1;
  i2c_busIdle();
 }

void i2c_transmit()          // Function to transmit data
 {
  i2c_start();    
  i2c_busIdle();
    
  SSPBUF = 0xA2;           // 'Write' address of pcf8563
  i2c_busIdle();
  SSPBUF = 0x00;           // Register address from where you want to start writing
  i2c_busIdle();
  SSPBUF = 0x00;           // control status reg1
  i2c_busIdle();
  SSPBUF = 0x00;           // control status reg2
  i2c_busIdle();
  SSPBUF = 0x00;           // Setting integrity and Seconds to zero
  i2c_busIdle();
  SSPBUF = 0x15;           // Setting Minutes to 15
  i2c_busIdle();
  SSPBUF = 0x10;           // Setting Hour to 10
  i2c_busIdle();
  SSPBUF = 0x04;           // Setting day to 4th
  i2c_busIdle();
  SSPBUF = 0x00;           // Setting weekday to monday
  i2c_busIdle();
  SSPBUF = 0x02;           // Setting month to February
  i2c_busIdle();
  SSPBUF = 0x19;           // Setting year to 2019
    
  i2c_busIdle();
  i2c_stop();
 }

void i2c_receive(void)        // Function to receive sec,min,... etc
 {
  i2c_start();  
    
  SSPBUF = 0xA2;              // Slave address + write
  i2c_busIdle();
 
  SSPBUF = 0x02;              // Write memory address from where you want to start reading
  i2c_busIdle();
 
  i2c_repeatedStart();        // Repeated start condition
  i2c_start();                // start condition
    
  SSPBUF = 0xA3;              // Slave address + read
  i2c_busIdle();
 
  receive_enable();            // Receive seconds
  sec = SSPBUF;
  i2c_busIdle();
  ack();

  receive_enable();            // Receive Minutes
  min = SSPBUF;
  i2c_busIdle();
  ack();   
 
  receive_enable();            // Receive Hours
  hour = SSPBUF;
  i2c_busIdle();
  ack();
 
  receive_enable();            // Receive days
  day = SSPBUF;
  i2c_busIdle();
  ack();
 
  receive_enable();            // Receive Weekdays
  weekday = SSPBUF;
  i2c_busIdle();
  ack();
 
  receive_enable();            // Receive months
  month = SSPBUF;
  i2c_busIdle();
  ack();
 
  receive_enable();            // Receive Years
  year = SSPBUF;
  i2c_busIdle();
  Noack();

  i2c_stop();
 }

/////////////// Display functions /////////////////
void display(unsigned int x)    //Function to convert received hex no to Ascii
 {                              
  lcd_data((x/16)+48);
  __delay_ms(5);
  lcd_data((x%16)+48);
  __delay_ms(5);
 }

void display_clock(void)       // Function to display date and time
 {   
  hour = hour&0x3f;
  display(hour);
  lcd_data(':');
       
  min = min&0x7f;
  display(min);
  lcd_data(':');
      
  sec = sec&0x7f;
  display(sec);
       
      
  lcd_cmd(0xc0);
  __delay_ms(5);
       
  day = day&0x3f;
  display(day);
  lcd_data('/');
       
  month = month&0x1f;
  display(month);
  lcd_data('/');
       
  year = year&0xff;
  display(year);    
 }

void disp_wd(char *wd)   // Function to display weekday
 {
  while(*wd != '\0')
   {
    lcd_data(*wd);
    wd++;
   }
 }

void lcd_init(void)
 {
    // LCD initial initialization
    
    __delay_ms(15);
    lcd_cmd(0x03);
    __delay_ms(5);
    lcd_cmd(0x03);
    __delay_ms(5);
    lcd_cmd(0x03);
    __delay_ms(5);
    lcd_cmd(0x02);
    __delay_ms(5);
    
    // LCD command setting initialization
    
    lcd_cmd(0x28);       // 4 bit interface length. 2 rows enabled.
    __delay_ms(5);
    
    lcd_cmd(0x10);       // Move cursor or shift display
    __delay_ms(5);
    
    lcd_cmd(0x0c);       // Enable display. Cursor off.
    __delay_ms(5);
    
    lcd_cmd(0x06);      // Increment cursor position after each byte
    __delay_ms(5);
    
    lcd_cmd(0x01);      // clear display
    __delay_ms(5);
    
 }

void lcd_cmd(unsigned char c)
 {
  RS = 0;
  RW = 0;
  PORTD = (PORTD&0x0f)|(0xf0&c);
  enable();
  PORTD = ((PORTD&0x0f)|(0x0f&c)<<4);
  enable();
 }

void lcd_data(unsigned char d)
{
 RS = 1;
 RW = 0;
 PORTD = (PORTD&0x0f)|(0xf0&d);
 enable();
 PORTD = ((PORTD&0x0f)|(0x0f&d)<<4);
 enable();   
}

void enable(void)
 {
  E = 1;
  __delay_ms(5);
  E = 0;

4 Replies Related Threads

    ric
    Super Member
    • Total Posts : 26159
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: online
    Re: Unable to read changing values from a PCF8563 RTC. Read values stuck to 0x3F or 0xFF 2020/03/07 16:01:34 (permalink)
    5 (1)
    Why oh why does everyone ignore the ACK bit when they start using I2C?
    That is your PRIMARY indication that everything is working.
     
    e.g. right here:
    void i2c_transmit()          // Function to transmit data
     {
      i2c_start();    
      i2c_busIdle();
        
      SSPBUF = 0xA2;           // 'Write' address of pcf8563
      i2c_busIdle();
      SSPBUF = 0x00;           // Register address from where you want to start writing
      i2c_busIdle();

    After you write the slave address (0xA2), and wait for "busIdle()", the ACKSTAT bit indicates if the slave responded with an ACK. If you didn't get an ACK, there's no point procedding, as there's noone listening.
     
    Also, I'm sure you are using someone else's "busIDLE" function, but do be aware it's a bad name.
    It indicates that the previous command has completed its action, but the bus is most cetainly not "idle" until after you send the STOP cycle at the end.
     
     
     

    I also post at: PicForum
    Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
    NEW USERS: Posting images, links and code - workaround for restrictions.
    To get a useful answer, always state which PIC you are using!
    Guest
    Super Member
    • Total Posts : 80502
    • Reward points : 0
    • Joined: 2003/01/01 00:00:00
    • Location: 0
    • Status: online
    Re: Unable to read changing values from a PCF8563 RTC. Read values stuck to 0x3F or 0xFF 2020/03/07 18:25:28 (permalink)
    0
    Ok Rick. I appreciate your finds and knowledge.
    So this function does not wait enough for the slave ACK to be present ?
     
    void i2c_busIdle()
     {
      while((SSPCON2 & 0x1F) || (SSPSTAT & 0x04));    // Check if bus is idle
     }


    So what are the errors in this code ? How would you write it
    so when this function exit it's because an ACK got flagged ?
     
    JP
    Guest
    Super Member
    • Total Posts : 80502
    • Reward points : 0
    • Joined: 2003/01/01 00:00:00
    • Location: 0
    • Status: online
    Re: Unable to read changing values from a PCF8563 RTC. Read values stuck to 0x3F or 0xFF 2020/03/07 18:25:56 (permalink)
    0
    Ok Rick. I appreciate your finds and knowledge.
    So this function does not wait enough for the slave ACK to be present ?
     
    void i2c_busIdle()
     {
      while((SSPCON2 & 0x1F) || (SSPSTAT & 0x04));    // Check if bus is idle
     }


    So what are the errors in this code ? How would you write it
    so when this function exit it's because an ACK got flagged ?
     
    JP
    ric
    Super Member
    • Total Posts : 26159
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: online
    Re: Unable to read changing values from a PCF8563 RTC. Read values stuck to 0x3F or 0xFF 2020/03/26 12:22:53 (permalink)
    5 (1)
    That only tells you the transaction is finished.
    After that, it is YOUR JOB to read the ACKSTAT bit after you have sent a value to the slave.
    If the bit is 1, you got a NAK, meaning the slave is not responding.
    There's is no point sending more, there's noone listening. You need to stop the transaction, and report an error to yourself so you can fix the problem.
     

    I also post at: PicForum
    Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
    NEW USERS: Posting images, links and code - workaround for restrictions.
    To get a useful answer, always state which PIC you are using!
    Guest
    Quick Reply: (Open Full Version)
      Enter the random characters shown
    Submit Post
    Some restrictions apply to prevent link (URL) Spam.
    URLs in messages, signatures, and PM's are removed unless you have ...
    • been a member for at least 0 day(s);
    • made a total of 0 post(s);
    • earned at least 0 point(s) for post scores (based on the ratings on your posts);
    • earned at least 0 reward point(s);
    Jump to:
    © 2020 APG vNext Commercial Version 4.5