//////////////////////////////////////////////////////////////////////
//
// File: 16F88_RTC_DS1307.c
//
// Author: J F Main.
//
// Description:
//
// A Real time clock using the Dallas chip DS1307.
// With 4 pushbuttons implemented on one analogue port
// and output on 4 x 7-segment displays.
//
// Compiler : MikroC, mikroElektronika C compiler
// for Microchip PIC microcontrollers
// Version: 6.0.1
//
// Note Testing:
//
// Tested on 16F88
//
// Requirements:
//
// Dallas chip DS1307, 4 x 7-segment clock display.
//
// Target : 16F88
//
// Notes :
//
// Uses internal oscillator at 8Mhz
//
// Version:
// 1.00 - Initial release.
// 1.01 - Dont't write to the D1307 if running - alters clock.
// 1.02 - Updated for MikroC 6.0.1
// Add Current state display for key 3 hit
// Only send start to DS1307 if changed data HRS,MINS or seconds
// otherwise assume is continuous i.e. if started = always started.
// i.e. only restart if mins seconds changed as this is all in 1 control reg.
// Concept of stopping the clock is wrong so never stop it,
// just update if sec min, changed - so oscillator never stops.
//
// Copyright : Copyright � John Main 2006-2014
// http://www.best-microcontroller-projects.com
// Free for non commercial use as long as this entire copyright notice
// is included in source code and any other documentation.
//
// Note: Functions
// BCD2Dec, Dec2Bcd and ByteToStr are built into mikroC compiler!
//
//////////////////////////////////////////////////////////////////////
#include "bit.h"
#include "I2C.h"
// Prototypes
void test();
void display_mode(unsigned char );
//////////////////////////////////////////////////////////////////////
// macros / defines
#define UP 1
#define DOWN -1
#define MODE_HRS_MINS 0
#define MODE_MINS_SECS 1
#define MODE_DAY_MON 2
#define MODE_YEAR 3
#define MODE_INIT 4
#define FINISHED_EDIT -1
//////////////////////////////////////////////////////////////////////
typedef short Byte;
typedef unsigned short UINT8;
//////////////////////////////////////////////////////////////////////
// Decode the analogue value to determing the key hit.
unsigned short decode_ANA_keys(unsigned int val) {
// Decode the analogue
if (val>800 && val<860) {val=1;}
else if (val>490 && val<530) {val=2;}
else if (val>300 && val<350) {val=3;}
else if (val<300) {val=4;}
else {val=0;}
return val;
}
//////////////////////////////////////////////////////////////////////
// Get a key in context of switching from display to analogue port.
unsigned int read_ANA_keys() {
unsigned int key;
TRISA = 0x01; // AN0 input to read the keys
ANSEL = 0x01; // Activate analogue input AN0
delay_us(100);
key = decode_ANA_keys( Adc_Read(0) );
TRISA &= ~0x01; // AN0 input set as o/p to control the 7segment display.
ANSEL = 0x00; // Digital I/O
return key;
}
//////////////////////////////////////////////////////////////////////
// Initialise.
//
void init(void) {
OSCCON = 0x70; // b6..4 = 110 = 4MHz 111=8MHz
ANSEL = 0x00; // RA0 enabled as needed in program.
// Timer 1 on
T1CON = (1<<TMR1ON);
}
//////////////////////////////////////////////////////////////////////
void init_ports(void) {
PORTA = 0;
TRISA = 0x00; // 0=o/p - sets analogue pins to digital output.
// AN0 set to input as needed.
PORTB = 0;
TRISB = 0x01; // 0=o/p // Receive on RB0.
}
//////////////////////////////////////////////////////////////////////
// This was because RA5 is an input so circuit was changed
// moving RA6 RA7 on display data bus to positions b5,b6
unsigned short move_b7_b6_to_b6_b5 (unsigned short digit) {
unsigned short r;
r =( digit & 0x1f ) | ( (digit & 0x60)<< 1);
return r;
}
//////////////////////////////////////////////////////////////////////
// RA5 can only be input on 16F88 so move bit 5 to 6, 6 to 7.
//
// ret table is for straight through
// PORT 0 1 2 3 4 5 6 7 to a b c d e f g dp
// So map to new outputs:
// 0 a, 1 b, 2 c, 3 d, 4 e, 6 f, 7 g - loosing dp
// i.e. keep 1st 5 and move bits 5,6 to 6,7
unsigned short int2seg(unsigned short digit) {
unsigned short r;
unsigned short ret[10] = { 0x3F, 0x06, 0x5B, 0x4F, 0x66,
0x6D, 0x7D, 0x07, 0x7F, 0x6F };
if (digit<0 || digit>9) {
r = 0x7f;
} else {
r = move_b7_b6_to_b6_b5(ret[digit]);
}
return r;
}
//////////////////////////////////////////////////////////////////////
void write_DS1307(unsigned short address, unsigned short b8data) {
short i;
_I2C_Start();
_I2C_Wr(0xd0);
_I2C_Wr(address);
_I2C_Wr(b8data);
_I2C_Stop();
_I2C_Start();
// Get an ACK from the slave but give up after 3 goes don't hang waiting.
for(i=0;i<3;i++) {
if (_I2C_Wr(0xd0)==1) { // Check for NACK
_I2C_start();
} else break; // found ACK
}
_I2C_Stop();
}
//////////////////////////////////////////////////////////////////////
unsigned short read_ds1307(unsigned short address) {
unsigned short b8data;
_I2C_Start();
_I2C_Wr(0xd0);
_I2C_Wr(address);
_I2C_Start();
_I2C_Wr(0xd1); // b0 is read
b8data=_I2C_Rd(0); // Read with NACK.
_I2C_stop();
return(b8data);
}
//////////////////////////////////////////////////////////////////////
// Read device if already started then do nothing else start it.
// Note
// CH=0 oscillator ENABLED, CH=1 oscillator DISABLED.
//
void check_start_ds1307() {
Byte sec;
sec = read_ds1307(0); // Has start(CH),mins,secs.
if ( (sec & 0x80)==0 ) {return;} // Active LOW = running.
sec = read_ds1307(0) & ~0x80; // CH = 0 - start.
write_DS1307(0,sec);
}
//////////////////////////////////////////////////////////////////////
void stop_ds1307() {
Byte sec;
sec = read_ds1307(0) | 0x80; // CH = 1 - stop.
write_DS1307(0,sec);
}
//////////////////////////////////////////////////////////////////////
// Register update only happens if year b8data is not present.
// Have to change this next centuary!
//
void init_ds1307(void) {
check_start_ds1307(); // Start oscillator if not started already.
// set square wave output
write_ds1307(7,(1<<7)|(1<<4)); // OUT=1 polarity, SQWE=1 enable o/p, b0,b1=0 = 1Hz
// Check if initialisation is needed by checking a ram location
// use the year value : RAM location 3F is used to hold the year b8data
if (read_ds1307(0x3f)==0x20) {return;} // initialised so return
display_mode(MODE_INIT);
// Set to 24H mode
write_ds1307(2,read_ds1307(2) & ~(1<<6) ); // reset bit 6 for 24 hour mode.
// Set the time
write_ds1307(0,0x59 & 0x7F); // bit 7 - oscailltor halt (if=1). : secs 0-59
write_ds1307(1,0x59); // : mins 0-59
write_ds1307(2,0x24 & ~(1<<6) ); // reset bit 6 for 24 hour mode.:0-23,1-12 hour
write_ds1307(3,0x07); // week day day 0-7 : day 1-7
write_ds1307(4,0x31); // (exceptions Feb,Apr,Jun,Sep,Nov 1-30) : date 1-31
write_ds1307(5,0x12); // : month 1-12
write_ds1307(6,0x99); // : year 0-99
// Write high digit of year into RAM easier code
// and gives initialisation check.
write_ds1307(0x3f,0x20); // high year digits : year 2000
}
//////////////////////////////////////////////////////////////////////
// Edit the DS1307 at address Addr
// add or subtract dir e.g. -1, +1, -10, +10.
//
// Stores the special case bits for
// CH and 12/24hour
// restoring them at the end.
//
void edit_ds1307(Byte Addr, Byte dir) {
UINT8 store=0;
UINT8 lim,low_lim;
int b8data=0;
b8data = read_ds1307(Addr);
switch (Addr) { // Control special case bits
case 0 : store = b8data & 0x80; b8data &=0x7f; break; // CH Clock Halt.
case 2 : store