Master Slave Clocks with PIC16F887

Status
Not open for further replies.
XC8 is free (limited ) and covers all 8 bit pics.....XC16 and XC32 are also free (limited )..

MikroC also have a 2k free version...
Hello,
i am watching but need to understand it more clearly!

All we've been through!!! I thought you would be a professional by now... What would you like me to explain!
 
An Australian that would like to help!!!
I want to contribute some complete fully working projects to the forum. Do you have an special place to submit them?

My projects come with:
  • Schematics (Protel / Circuit Maker 2000)
  • Monochrome Artwork
  • PCBs (single sided PDF)
  • Raw Documentation
  • MikroC Source Code & Hex File (I am running an registered version of MikroC, but I try to keep it under 2KB)
 
The Article section is for posting this kind of stuff... I have a few articles as does a few other members... Have a look and see...
https://www.electro-tech-online.com/articles/
 
Hello,
I find this manual which say.


Here we have DS1307 at the factory default address of 0xD0 (in hex).
To start the writing operation we would write 0x01 to the seconds register at 0x00 as follows: // 0x01 mean 1 sec??
 Send the start sequence
 Send 0xD0 (I2C address of the DS1307 with the R/W bit low (even address))
 Send 0x00( internal address of seconds register)
 Send 0x01 (01 data as second)
 Send the stop sequence To read seconds from DS1307 the following steps are followed:
 Send a start sequence
 Send 0xD0 (I2C address of the DS1307 with the R/W bit low (even address))
 Send 0x00( internal address of seconds register)
 Send the start sequence again (repeated start)
 Send 0xD1 (I2C address of the DS1307 with the R/W bit high (odd address))
 Read data byte from DS1307
 Send the stop sequence
 
Reference that against mine...
C:
char E_Write(int addr, unsigned char* ch, char len)
   {
   I2C_Start();                   // Send a start condition
   I2C_Write(WRITE);                 // chip write address
   if(!I2C_nack()) return 0;             // wait for ack
   if(WRITE == 0xA0)
     {
     I2C_Write((unsigned char)( addr >> 8 & 0xff));// Not used if out if single byte address
     if(!I2C_nack()) return 0;           // wait for ack
     }
   if(!(WRITE==0x90))  
     {
     I2C_Write((unsigned char) addr & 0xff);     // low address (ADC not used)
     if(!I2C_nack()) return 0;           // wait for ack
     }
   if(len == 1)
     {
     I2C_Write(*ch);
     if(!I2C_nack()) return 0;
     }
   else if(len > 1)
     {          
     while(len--)
       {
       I2C_Write(*ch++);             // Write values to Eeprom
       if(!I2C_nack()) return 0;    
       }
     }
   else
     {
     while(*ch != 0)
       {
       I2C_Write(*ch++);             // Write values to Eeprom
       if(!I2C_nack()) return 0;
       }
     I2C_Write(0);                 // Write values to Eeprom
     if(!I2C_nack()) return 0;
     }
   I2C_Stop();                     // Send a stop.
   return 1;                     // All went well
   }

The difference is I have allowed sequential and random reads in one..

Sequential reading is faster as you don't need to keep restarting and re addressing.

Once you read a byte you can send an Ack instead of a Nack and keep reading..

 Send the start sequence
 Send 0xD0 (I2C address of the DS1307 with the R/W bit low (even address))
 Send 0x00( internal address of seconds register)
 Send 0x01 (01 data as second)
 Send the stop sequence To read seconds from DS1307 the following steps are followed:
 Send a start sequence
 Send 0xD0 (I2C address of the DS1307 with the R/W bit low (even address))
 Send 0x00( internal address of seconds register)
 Send the start sequence again (repeated start)
 Send 0xD1 (I2C address of the DS1307 with the R/W bit high (odd address))
 Read data byte from DS1307 <---- run a loop here to read all 8 bytes....
 Send the stop sequence
 
Hello,
I was testing pic16f877a due to some ages problem i think pin has crack of it!
and right now i dont have pic16f877a
I have pic16f887 and pic16f72
I need some upper definition file i have no idea to make it, here i i2c i have used for BLDC many year ago.
now i want to update the code to PIC16f887, plaese have look the the portb0 is not working!

C:
#include <htc.h>
__CONFIG(LVP_OFF & BOREN_OFF & PWRTE_ON & WDTE_OFF & FOSC_HS);
#define _XTAL_FREQ 20000000
#define SDATA RC4
#define SCLK RC3
void I2C_init(),I2C_start(void),I2C_write(char x), I2cSTOP(void);   
void i2c_Wait(void);
void main(void){
    TRISC3=1; //direction to input have be changed
    TRISC4=1;
TRISC0=0;
TRISB=0X00;
TRISC1=0;
TRISC2=0;
RC0=0;
RC1=0;
RC2=0;
//__delay_ms(500);
    I2C_init();
//__delay_ms(50);
//    I2C_start();
//
//    I2C_write(0x12);//esc default addr

    //I2C_write(100);//testing speed 0-255

  // I2cSTOP();
while(1){
RB0=0;
RC1=0;
RB7=0;
//__delay_ms(500);
RB0=1;
RC1=1;
RB7=1;
//__delay_ms(500);
}
}
void I2C_init(void)
    {
  SSPCON = 0x38;      // set I2C master mode
SSPCON2 = 0x00;
SSPADD = 0x0C;  //400KHZ  20MHz xtal
SSPSTAT|=0X80;
PSPIF=0;      // clear SSPIF interrupt flag
BCLIF=0;      // clear bus collision flag
}
void I2C_start(void)
{
    i2c_Wait();
    SEN=1;
}
void I2C_write(char x){
    i2c_Wait();   
SSPBUF=x;
        }
void i2c_Wait(void){
    while((SSPCON2 & 0X1F || (SSPSTAT & 0X04)));
}
void I2cSTOP(void)
{
    i2c_Wait();
    PEN=1;
}





 
First... Can you download the free version of XC8 lite.... It covers the newer chips..

The pic16f887 has more Adc registers.. There is a config bit that turns off the port b adc's

PBADEN = 0ff ... Only then can you use port b as an output!!... Why don't you use PORTC SDA and SCL pins????
 
My apologies.... The pic18 has the PBADEN.... On the pic16 you need to use " ANSELH = 0;"
 
You know you don't need the DS1307 RTC to make an accurate clock.

You can do it all with the PIC and interrupts.... Accuracy better than +/- 1 second p/day

Code:
/*
 * PROJECT:
                      < LED 6-digit 24 / 12hr Clock >
              Copyright ©2011 Trent Jackson all rights reserved

 * MCU:
              PIC16F628a @4MHz

 * Compiler:
              mikro PRO C ...
     
 * Revision:
              0.1b (conforms to hardware revision 0.1b and above)
           
 * Status:
              Working & complete
   
   Compiler Messages:
   
   Available RAM: 208 [bytes], Available ROM: 2048 [bytes]
   Used RAM (bytes): 25 (12%)  Free RAM (bytes): 183 (88%)
   Used ROM (program words): 660 (32%)  Free ROM (program words): 1388 (68%)
   Project Linked Successfully clock.mcppi
   Linked in 657 ms
   Project 'clock.mcppi' completed: 2032 ms
   Finished successfully: 12 Apr 2011, 12:18:55 clock.mcppi

*/

//                     :: Global Scope Variables ::

unsigned short digitSc = 0; // Digit num (1 of 6) in current multiplex scope
unsigned short hrsOnes = 2; // Hours LSD (least significant digit)
unsigned short hrsTens = 1; // Hours MSD (most significant digit)
unsigned short minOnes = 0; // Minutes LSD
unsigned short minTens = 0; // Minutes MSD
unsigned short secOnes = 0; // Seconds LSD
unsigned short secTens = 0; // Seconds MSD
unsigned short userSet = 0; // Flag set true when user is adjusting time
unsigned short dgToSet = 0; // Current set of digits in scope of adjustment
unsigned short tScaler = 0; // Time scaler to derive seconds from mS

unsigned int flDgHrs   = 0; // Flash hours digits when being adjusted
unsigned int flDgMin   = 0; // ^^ Minutes
unsigned int flDgSec   = 0; // ^^ Seconds
unsigned int dBounce   = 0; // De-bounce switch (timeout period no polling)


//                           :: Defines ::

// Segments on displays ...
#define segA PORTB.F0
#define segB PORTB.F1
#define segC PORTB.F2
#define segD PORTB.F3
#define segE PORTB.F4
#define segF PORTB.F5
#define segG PORTB.F6

// User-defined 12 or 24hr mode operation ...
#define mode PORTB.F7

// Control lines to 4017 decade counter for display multiplexing
#define mclr PORTA.F0
#define clkc PORTA.F1

// Switch lines (set / inc switches for time adjustment) ...
#define cSet PORTA.F2
#define cInc PORTA.F3


//                             :: Methods ::

/*
   Tables containing segment settings corresponding to numbers for displays
*/

void num0()
{
  segA = 1;
  segB = 1;
  segC = 1;
  segD = 1;
  segE = 1;
  segF = 1;
  segG = 0;
}

void num1()
{
  segA = 0;
  segB = 1;
  segC = 1;
  segD = 0;
  segE = 0;
  segF = 0;
  segG = 0;
}

void num2()
{
  segA = 1;
  segB = 1;
  segC = 0;
  segG = 1;
  segD = 1;
  segE = 1;
  segF = 0;
}

void num3()
{
  segA = 1;
  segB = 1;
  segC = 1;
  segD = 1;
  segE = 0;
  segF = 0;
  segG = 1;
}

void num4()
{
  segA = 0;
  segD = 0;
  segE = 0;
  segF = 1;
  segG = 1;
  segB = 1;
  segC = 1;
}

void num5()
{
  segA = 1;
  segB = 0;
  segE = 0;
  segF = 1;
  segG = 1;
  segC = 1;
  segD = 1;
}

void num6()
{
  segA = 1;
  segB = 0;
  segC = 1;
  segD = 1;
  segE = 1;
  segF = 1;
  segG = 1;
}

void num7()
{
  segA = 1;
  segB = 1;
  segC = 1;
  segD = 0;
  segE = 0;
  segF = 0;
  segG = 0;
}

void num8()
{
  segA = 1;
  segB = 1;
  segC = 1;
  segD = 1;
  segE = 1;
  segF = 1;
  segG = 1;
}

void num9()
{
  segA = 1;
  segB = 1;
  segC = 1;
  segD = 0;
  segE = 0;
  segF = 1;
  segG = 1;
}

void blankDigit()
{
  segA = 0;
  segB = 0;
  segC = 0;
  segD = 0;
  segE = 0;
  segF = 0;
  segG = 0;
}

void interrupt()
{
   /*
      Interrupt handler triggered by MCU's TMRO (pre-scaler enabled)
   */
   
   // Inc scaler
   tScaler ++;

   // 1000mS / 1 sec of time elapsed?
   if (tScaler == 121)
   {
      // Inc seconds if enabled
      if (userSet == 0)
      {
         secOnes ++;
      }
     
      // Fine tune adj
      delay_us(2);

      // Reset scaler
      tScaler = 0;
   }

   // 50uS before re-triggering interrupt
   delay_us(50);
   
    // Fine tune adj
   delay_us(3);

   // Re-enable and bail
   TMR0   = 0;
   INTCON = 0x20;
}


void setDigit(unsigned short digit)
{
   /*
      Apply data to current display digit in scope
     
      Structure implements no "ELSE IF" logic enabling for a relatively
      good equalization of execution time regardless of program flow. Program
      memory is also preserved.
   */

   if (digit == 0)
   {
      num0();
   }
   if (digit == 1)
   {
      num1();
   }
   if (digit == 2)
   {
      num2();
   }
   if (digit == 3)
   {
      num3();
   }
   if (digit == 4)
   {
      num4();
   }
   if (digit == 5)
   {
      num5();
   }
   if (digit == 6)
   {
      num6();
   }
   if (digit == 7)
   {
      num7();
   }
   if (digit == 8)
   {
      num8();
   }
   if (digit == 9)
   {
      num9();
   }
}

flashDigits(unsigned int digits)
{
   /*
      Counter for flashing digits on / off when user is adjusting them
   */

   digits ++; // Inc
   
   // Counter reached targed? -- reset if so ...
   if (digits == 5000)
   {
      digits = 0;
   }

   // Return current count to calling procedure
   return digits;
}

void incCounter()
{
   // Current display off ...
   blankDigit();

   //  Clock decade counter (inc to next display)
   clkc = 1;
   clkc = 0;
}

void multiplexDisplays()
{
   /*
      1. Procedure applies data to current display digit in scope ...
      2. Successive handling of the displays in an multiplexing fashion
     
      Structure implements no "ELSE IF" logic between digit scanning, enabling
      for a relatively good equalization of execution time, regardless of
      program flow. Program memory is also preserved, and consistent scanning
      frequencies are the end results
   */

   //                         :: Hours Digit 1 ::
   if (digitSc == 0)
   {
      if (flDgHrs < 2500)
      {
         // Lead zero blanking for 12hr mode ...
         if (mode == 0 && hrsTens == 0)
         {
            blankDigit();
         }
         else
         {
            setDigit(hrsTens);
         }
      }
      else
      {
         blankDigit();
      }
   }
   
   //                         :: Hours Digit 2 ::
   if (digitSc == 1)
   {
      if (flDgHrs < 2500)
      {
         setDigit(hrsOnes);
      }
      else
      {
         blankDigit();
      }
   }
   
   //                         :: Minutes Digit 1 ::
   if (digitSc == 2)
   {
      if (flDgMin < 2500)
      {
         setDigit(minTens);
      }
      else
      {
         blankDigit();
      }
   }

   //                         :: Minutes Digit 2 ::
   if (digitSc == 3)
   {
      if (flDgMin < 2500)
      {
         setDigit(minOnes);
      }
      else
      {
         blankDigit();
      }
   }

   //                         :: Seconds Digit 1 ::
   if (digitSc == 4)
   {
      if (flDgSec < 2500)
      {
         setDigit(secTens);
      }
      else
      {
         blankDigit();
      }
   }

   //                         :: Seconds Digit 2 ::
   if (digitSc == 5)
   {
      if (flDgSec < 2500)
      {
         setDigit(secOnes);
      }
      else
      {
         blankDigit();
      }
   }

   // Next digit to apply to ...
   digitSc++;

   // Reset 'n start again if this is the final digit in the chain
   if (digitSc == 6)
   {
      digitSc = 0;
   }
}

void doTime()
{
   /*
      Handles the incrementing and control of all time-related variables
      in either 12 or 24hr time

      1. Seconds
      2. Minutes
      3. Hours ...
   */
   
   //                            :: Seconds ::
   
   if (secOnes == 10)
   {
      secTens  ++; // Inc tens
      secOnes = 0; // Reset ones
   }
   if (secTens == 6)
   {
      minOnes  ++; // Inc / carry min ones
      secTens = 0; // Reset tens
   }

   //                            :: Mins ... ::
   
   if (minOnes == 10)
   {
      minTens  ++; // Inc tens ...
      minOnes = 0; // Reset ones
   }
   if (minTens == 6)
   {
      hrsOnes  ++; // Inc / carry hour ones
      minTens = 0; // Reset min tens
   }
   
   //                            :: Hours ... ::
   if (hrsOnes == 10)
   {
      hrsTens  ++; // Inc / carry tens
      hrsOnes = 0; // Reset ones ...
   }

   // 12 or 24 mode in scope?
   if (mode == 0)
   {
   //                            :: (12hr mode) ::
     
      // 12 hrs elapsed? -- reset ones & tens ...
      if (hrsTens == 1)
      {
         if (hrsOnes == 3)
         {
            hrsTens = 0;
            hrsOnes = 1;
         }
      }
   }
   else //                      :: (24hr mode) ::
   {
      // 24 hrs elapsed? -- reset ones & tens ...
      if (hrsTens == 2)
      {
         if (hrsOnes == 4)
         {
            hrsTens = 0;
            hrsOnes = 0;
         }
      }
   }
}

void doUser()
{
   /*
      Procedure handles the user adjusting the time via the 2
      push button tactile switches ...
     
      This proc renders the above proc out of scope until the
      user has set all digits
   */
   
   switch (dgToSet)
   {
      case 1: //              :: Setting Hours ::
     
         // Inc button pressed and enabled after debounce period?
         if (dBounce == 0)
         {
            if (cInc == 0)
            {
               // Debounce switch contacts (ignore port for a short time)
               dBounce = 1250;

               // 12 or 24 mode in play?
               if (mode == 0)
               {
                  // (12hr mode) -- reset tens & ones if exceeded 12
                  if (hrsTens == 1)
                  {
                     if (hrsOnes == 2)
                     {
                        hrsTens = 0;
                        hrsOnes = 0;
                     }
                  }
               }
               else // (24hr mode) -- reset tens & ones if exceeded 24
               {
                  // Reset tens & ones
                  if (hrsTens == 2)
                  {
                     if (hrsOnes == 4)
                     {
                        hrsTens = 0;
                        hrsOnes = 0;
                     }
                  }
               }

               // Inc ones?
               if (Hrsones != 9)
               {
                  hrsOnes ++;
               }
               else // Inc tens & reset ones
               {
                  hrsTens ++;
                  hrsOnes =0;
               }
            }
         }

         // Call method to flash digits
         flDgHrs = flashDigits(flDgHrs);
         break;

      case 2: //               :: Setting Minutes ::

         // Inc button pressed and enabled after debounce period?
         if (dBounce == 0)
         {
            if (cInc == 0)
            {
               // Debounce switch contacts (ignore port for a short time)
               dBounce = 1250;

               // Reset tens & ones if exceeded 60
               if (minTens == 5)
               {
                  if (minOnes == 9)
                  {
                     minTens = 0;
                     minOnes = 0;
                  }
               }

               // Inc ones?
               if (minOnes != 9)
               {
                  minOnes ++;
               }
               else // Inc tens & reset ones
               {
                  minTens ++;
                  minOnes =0;
               }
            }
         }

         // Call method to flash digits
         flDgMin = flashDigits(flDgMin);
         break;

      case 3: //              :: Setting Seconds ::

         // Inc button pressed and enabled after debounce period?
         if (dBounce == 0)
         {
            if (cInc == 0)
            {
               // Debounce switch contacts (ignore port for a short time)
               dBounce = 1250;

               // Reset tens & ones if exceeded 60
               if (secTens == 5)
               {
                  if (secOnes == 9)
                  {
                     secTens = 0;
                     secOnes = 0;
                  }
               }

               // Inc ones?
               if (secOnes != 9)
               {
                  secOnes ++;
               }
               else // Inc tens & reset ones
               {
                  secTens ++;
                  secOnes =0;
               }
            }
         }

         // Call method to flash digits
         flDgSec = flashDigits(flDgSec);
         break;
         
      case 4: //  Exit adj time proc ...
         userSet = 0;
         dgToSet = 0;
         break;
   }
}

void main()
{
   /*
      Program entry point ... (as per usual with any C compiler)
   */

  // Configuration of ports etc ...
  CMCON      = 7;       // Disable analog comparators
  TRISA      = 0x0C;    // 2 inputs rest outputs
  TRISB      = 0x00;    // All outputs ...
  PORTA      = 0x00;    // Init port, all pins low
  PORTB      = 0x00;    // Init port, all pins low
  OPTION_REG = 0x84;    // assign prescaler to TMR0
  TMR0       =   96;    // initial TMR0 value
  INTCON     = 0xA0;    // enable TMRO interrupt


  // Reset decade counter & merger to main loop below ...
  mclr = 1;
  mclr = 0;
  while(1)
  {
                        // :: Infinite program loop :: //
                       
     // Display current digit ...
     multiplexDisplays();

     // Button allowed to be polled? / debounce period expired
     if (dBounce == 0)
     {
        // Set button pressed?
        if (cSet == 0)
        {
           userSet = 1;     // Flag set denoting user is adjusting time ...
           dgToSet ++;      // Sec / min / hr digits in scope of being set
           dBounce = 2500;  // Set debounce period
           
           // Reset specific variables:
           
           flDgHrs = 0;     // Flash hours digits
           flDgMin = 0;     // ^^ Minutes
           flDgSec = 0;     // ^^ Seconds
        }
     }
     else
     {
        dBounce --; // Dec counter to re-enable key polling
     }
     
     // Run clock or user is setting new time:
     if (userSet == 0)
     {
        doTime(); // Update clock ...
     }
     else
     {
        doUser(); // User is adjusting the time
     }

     // Next display digit in scope
     incCounter();
  }
}
 
Here is my prototype on veroboard and schematic below...



 

Attachments

  • clk1.jpg
    312.4 KB · Views: 722
  • clkSchematic.jpg
    289.8 KB · Views: 624
Hello,
Your idea and project is nice, i appreciate!
But i want to make master slave clock with features.
that's why i choose RTC may be after i will go to GPS Clocks.
 
I have an interest in clocks, I've put togther a few including ones that use real time clock ic's, however my favorite is using a watch crystal in conjunction with timer1, its designed to be used with a watch crystal, and with a samll amount if code gives good accuracy, with a simple crystal oven super accuracy is achievable.

Your circuit building is nice and neat.
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…