DS1620 Dual 32-bit Elapsed Time Counter


Overview

The DS1602 consists of two 32 bit counters which provides the capability of some 125 years. The continuous counter increments each second when there is power on either V_bat or on V_cc. The V_cc counter increments only when voltage is present on V_cc. Thus, the DS1602 is useful in determining the amount of time equipment has been in the field and the amount of time it has actually been powered.

When V_bat is present and V_cc is less than V_bat (or at ground), the continuous counter increments, but the interface circuitry is shut down. I have used this in event logging applications where power consumption was critical. V_bat (was present at all times). When a reading was desired, V_cc was momentarily turned on using a PIC output to read the continuous counter.

There is an additional discussion in the context of the Parallax BASIC Stamp 2 elsewhere on this web site.


// DS1602_1.C (CCS - PCM, PIC16F84)
//
// Illustrates how to interface with DS1602 3-Wire Elapsed Time Counter
//
// 16F84                                  DS1602
//
// RB.2 (term 8) ------10K- ---------- DQ (term 2)
// RB.1 (term 7) -------------------- CLk (term 3)
// RB.0 (term 6) -------------------- RST (term 1)
//
// In this program V_bat was connected to ground.
//
// Clears both continuous and V_cc elapsed time counters.  Reads
// and displays content of continuous counter at five second intervals
// ten times.
//
// Sets V_cc counter to 0x12345678.  Reads and displays at five second
// intervals ten times.
//
// Note that structures may not be passed to nor returned from a function.
// Rather, they are passed by reference.  That is, by using a pointer.
//
// copyright Peter H. Anderson, Baltimore, MD, April, '99

#case
#include <16f84.h>
#include <defs_f84.h>

#define DQ_PIN rb2
#define	DQ_DIR trisb2
#define CLK_PIN rb1
#define CLK_DIR trisb1
#define RST_PIN rb0
#define RST_DIR trisb0

#define TxData 0	// RA.0 for serial LCD

#define CONT_CNTR 0
#define V_CC_CNTR 1

#define IN 1
#define OUT 0

struct long32
{
   long hi_long;
   long lo_long;
};

void ds1602_set_osc_trim(int osc_trim);
void ds1602_clear_counter(int counter);
void ds1602_write_counter(int counter, struct long32 *p_t);
void ds1602_read_counter(int counter, struct long32 *p_t);
void ds1602_out_byte(int v);
int ds1602_in_byte(void);
void ds1602_begin(void);
void ds1602_end(void);

// delay routines
void delay_ms(long t);
void delay_10us(int t);

// LCD routines
void lcd_init(void);
void out_RAM_str(int *s);
void lcd_hex_byte(int val);
void lcd_dec_byte(int val, int digits);
int num_to_char(int val);
void lcd_char(int ch);
void lcd_new_line(void);

void main(void)
{
   int n;
   struct long32 time32;

   lcd_init();

   RST_DIR=OUT;
   CLK_DIR=OUT;
   DQ_DIR=OUT;

   ds1602_set_osc_trim(0x03);  // in range of 0 - 7

   ds1602_clear_counter(CONT_CNTR);	// clear both counters
   ds1602_clear_counter(V_CC_CNTR);

   for(n=0; n<10; n++)	// ten readings
   {
      delay_ms(5000);	// wait for 5 seconds

      ds1602_read_counter(CONT_CNTR, &time32);
      lcd_hex_byte(time32.hi_long>>8);
      lcd_hex_byte(time32.hi_long&0xff);
      lcd_char(' ');
      lcd_hex_byte(time32.lo_long>>8);
      lcd_hex_byte(time32.lo_long&0xff);
      lcd_new_line();
   }

   time32.hi_long = 0x0123;
   time32.lo_long = 0x4567;

   ds1602_write_counter(V_CC_CNTR, &time32); // set counter to 0x01234567

   for(n=0; n<10; n++) // 10 readings at nominal 5 second intervals
   {
      delay_ms(5000);	// wait for 5 seconds

      ds1602_read_counter(V_CC_CNTR, &time32);
      lcd_hex_byte(time32.hi_long>>8);
      lcd_hex_byte(time32.hi_long&0xff);
      lcd_char(' ');
      lcd_hex_byte(time32.lo_long>>8);
      lcd_hex_byte(time32.lo_long&0xff);
      lcd_new_line();
   }
}

void ds1602_set_osc_trim(int osc_trim)
// sets trim of osc to value contained in osc_trim
{
	ds1602_begin();
	ds1602_out_byte(0xc0 | (osc_trim<<3)); 
 	// 11 OSC2 OSC1 OSC0 X X 0
	ds1602_end();
}

void ds1602_clear_counter(int counter)
{
   ds1602_begin();
   if (counter == 0)
   {
      ds1602_out_byte(0x04);	// continuous counter
   }
   else
   {
      ds1602_out_byte(0x02);	// V_cc counter
   }
   ds1602_end();
}

void ds1602_write_counter(int counter, struct long32 *p_t)
// writes 32 bits contained in hi_count and lo_count
// to specified counter, beginning with least sig bit
{
   ds1602_begin();
   if (counter==0)
   {
      ds1602_out_byte(0x80);	// continuous counter
   }
   else
   {
      ds1602_out_byte(0x40);	// V_cc counter
   }
   ds1602_out_byte(p_t->lo_long	& 0xff);	// lo byte of lower long
   ds1602_out_byte((p_t->lo_long) >> 8);
   ds1602_out_byte(p_t->hi_long	& 0xff);	// lo byte of high long
   ds1602_out_byte((p_t->hi_long) >> 8);
   ds1602_end();
}

void ds1602_read_counter(int counter, struct long32 *p_t)
// note that a pointer is used to pass values back to calling routine
{
   int lo_byte, hi_byte;
   ds1602_begin();
   if (counter==0)
   {
      ds1602_out_byte(0x81);  // continuous counter
   }
   else
   {
      ds1602_out_byte(0x41);  // V_CC counter
   }
   lo_byte = ds1602_in_byte();      // lo
   hi_byte = ds1602_in_byte();
   p_t->lo_long = ((long)hi_byte << 8) | lo_byte;

   lo_byte = ds1602_in_byte();      // lo
   hi_byte = ds1602_in_byte();
   p_t->hi_long = ((long)hi_byte << 8) | lo_byte;
}

void ds1602_out_byte(int v)
{
   int n;
   DQ_DIR=OUT;
   for(n=0; n<8; n++)
   {
      DQ_PIN = v&0x01;	// least sig bit first
      CLK_PIN=1; 	
      CLK_PIN=0;
      v=v>>1;
   }
}

int ds1602_in_byte(void)
{
   int n, v;
   DQ_DIR=IN;
   for (n=0; n<8; n++)
   {
      CLK_PIN=1;
      if(DQ_PIN)
      {
         v=(v>>1) | 0x80;	// least significant bit first
      }
      else
      {
         v=v>>1;
      }
      CLK_PIN=0;
   }
   DQ_DIR=OUT;
   return(v);
}

void ds1602_begin(void)
{
   RST_PIN=0;
   CLK_PIN=0;
   RST_PIN=1;	// initiate by bringing RST high
}


void ds1602_end(void)
{
   CLK_PIN=1;
   RST_PIN=0;
}

// delay routines

void delay_10us(int t)
{
#asm
      BCF STATUS, RP0
DELAY_10US_1:
      CLRWDT
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      DECFSZ t, F
      GOTO DELAY_10US_1
#endasm
}

void delay_ms(long t)	// delays t millisecs
{
   do
   {
     delay_10us(100);
   } while(--t);
}

// LCD routines
int num_to_char(int val)	// converts val to hex character
{
   int ch;
   if (val < 10)
   {
     ch=val+'0';
   }
   else
   {
     val=val-10;
     ch=val + 'A';
   }
   return(ch);
}

void lcd_char(int ch)	// serial output to PIC-n-LCD, 9600 baud
{
   int n, dly;
   		// start bit + 8 data bits

  #asm
       BCF STATUS, RP0
       MOVLW 9
       MOVWF n
       BCF STATUS, C

  LCD_CHAR_1:

       BTFSS STATUS, C
       BSF PORTA, TxData
       BTFSC STATUS, C
       BCF PORTA, TxData
       MOVLW 32
       MOVWF dly

  LCD_CHAR_2:
       DECFSZ dly, F
       GOTO LCD_CHAR_2
       RRF ch, F
       DECFSZ n, F
       GOTO LCD_CHAR_1

       BCF PORTA, TxData
       CLRWDT
       MOVLW 96
       MOVWF dly

  LCD_CHAR_3:
       DECFSZ dly, F
       GOTO LCD_CHAR_3
       CLRWDT
   #endasm
}

void lcd_init(void)	// sets TxData in idle state and resets PIC-n-LCD
{
#asm
        BCF STATUS, RP0
        BCF PORTA, TxData
        BSF STATUS, RP0
        BCF TRISA, TxData
        BCF STATUS, RP0
#endasm
   lcd_char(0x0c);
   delay_ms(250);
}

void lcd_new_line(void)	// outputs 0x0d, 0x0a
{
   lcd_char(0x0d);
   delay_ms(10);	// give the PIC-n-LCD time to perform the
   lcd_char(0x0a);	// new line function
   delay_ms(10);
}


void out_RAM_str(int *s)
{
   while(*s)
   {
      lcd_char(*s);
      ++s;
   }
}

void lcd_hex_byte(int val) // displays val in hex format
{
   int ch;
   ch = num_to_char((val>>4) & 0x0f);
   lcd_char(ch);
   ch = num_to_char(val&0x0f);
   lcd_char(ch);
}

void lcd_dec_byte(int val, int digits)
// displays byte in decimal as either 1, 2 or 3 digits
{
   int d;
   int ch;
   if (digits == 3)
   {
      d=val/100;
      ch=num_to_char(d);
      lcd_char(ch);
   }
   if (digits >1)	// take the two lowest digits
   {
       val=val%100;
       d=val/10;
       ch=num_to_char(d);
       lcd_char(ch);
   }
   if (digits == 1)	// take the least significant digit
   {
       val = val%100;
   }

   d=val % 10;
   ch=num_to_char(d);
   lcd_char(ch);
}