Pages

Sunday, May 18, 2014

Simple led clock thermomter with DS1302 and LM35 for beginners using arduino

In this article, we are going to make a simple led clock using seven segment led module and rtc1302 real time clock module. As an addon, there is an lm35, temperature sensor so that the clock shown both time and temperature. The construction is simple and uses readily available cheap components and can be hooked up on a simple breadboard.

Led clock thermometer with DS1302
In  aprevious article we made a clock using ntp to get the time from internet and the led display used is same (more details here and more details on connecting the led display can be read here).

LM35 is connected to analogue pin 5 (A5) and rtc module pins can be configured in the sketch (14,15,16 ie analogue pin A0, A1 and A2) .

Connect all the components as shown in figure and upload the sketch below. Make sure that led module is connected with a 220 ohm resistor to limit the current and prevent the damage to atmega/led (see here).

Now upload the sketch below or can download at my github (here)

Arduino Code (no need for external libraries, and direct led drive for rtc1302

 // Arduino Clock thermometer with RTC1302 and LM35 by Riyas (blog.riyas.org)  
 // Uses arduino uno and seven segment led display (direct drive)  
 // LM 35 for temperature sensing   
 // Credits to krodal (arduino.cc) and hobbycomponents.com  
 // Set your own pins with these defines !  
 #define DS1302_SCLK_PIN  14  // Arduino pin for the Serial Clock  
 #define DS1302_IO_PIN   15  // Arduino pin for the Data I/O  
 #define DS1302_CE_PIN   16  // Arduino pin for the Chip Enable  
 #define bcd2bin(h,l)  (((h)*10) + (l))  
 #define bin2bcd_h(x)  ((x)/10)  
 #define bin2bcd_l(x)  ((x)%10)  
 #define DS1302_SECONDS      0x80  
 #define DS1302_MINUTES      0x82  
 #define DS1302_HOURS       0x84  
 #define DS1302_DATE       0x86  
 #define DS1302_MONTH       0x88  
 #define DS1302_DAY        0x8A  
 #define DS1302_YEAR       0x8C  
 #define DS1302_ENABLE      0x8E  
 #define DS1302_TRICKLE      0x90  
 #define DS1302_CLOCK_BURST    0xBE  
 #define DS1302_CLOCK_BURST_WRITE 0xBE  
 #define DS1302_CLOCK_BURST_READ 0xBF  
 #define DS1302_RAMSTART     0xC0  
 #define DS1302_RAMEND      0xFC  
 #define DS1302_RAM_BURST     0xFE  
 #define DS1302_RAM_BURST_WRITE  0xFE  
 #define DS1302_RAM_BURST_READ  0xFF  
 #define DS1302_D0 0  
 #define DS1302_D1 1  
 #define DS1302_D2 2  
 #define DS1302_D3 3  
 #define DS1302_D4 4  
 #define DS1302_D5 5  
 #define DS1302_D6 6  
 #define DS1302_D7 7  
 #define DS1302_READBIT DS1302_D0   
 #define DS1302_RC DS1302_D6  
 #define DS1302_CH DS1302_D7  // 1 = Clock Halt, 0 = start  
 // Hour Register  
 #define DS1302_AM_PM DS1302_D5 // 0 = AM, 1 = PM  
 #define DS1302_12_24 DS1302_D7 // 0 = 24 hour, 1 = 12 hour  
 // Enable Register  
 #define DS1302_WP DS1302_D7  // 1 = Write Protect, 0 = enabled  
 // Trickle Register  
 #define DS1302_ROUT0 DS1302_D0  
 #define DS1302_ROUT1 DS1302_D1  
 #define DS1302_DS0  DS1302_D2  
 #define DS1302_DS1  DS1302_D2  
 #define DS1302_TCS0 DS1302_D4  
 #define DS1302_TCS1 DS1302_D5  
 #define DS1302_TCS2 DS1302_D6  
 #define DS1302_TCS3 DS1302_D7  
 #define LM35Pin 5  
 // Structure for the first 8 registers.  
 // These 8 bytes can be read at once with   
 // the 'clock burst' command.  
 // Note that this structure contains an anonymous union.  
 // It might cause a problem on other compilers.  
 typedef struct ds1302_struct  
 {  
  uint8_t Seconds:4;   // low decimal digit 0-9  
  uint8_t Seconds10:3;  // high decimal digit 0-5  
  uint8_t CH:1;      // CH = Clock Halt  
  uint8_t Minutes:4;  
  uint8_t Minutes10:3;  
  uint8_t reserved1:1;  
  union  
  {  
   struct  
   {  
    uint8_t Hour:4;  
    uint8_t Hour10:2;  
    uint8_t reserved2:1;  
    uint8_t hour_12_24:1; // 0 for 24 hour format  
   } h24;  
   struct  
   {  
    uint8_t Hour:4;  
    uint8_t Hour10:1;  
    uint8_t AM_PM:1;   // 0 for AM, 1 for PM  
    uint8_t reserved2:1;  
    uint8_t hour_12_24:1; // 1 for 12 hour format  
   } h12;  
  };  
  uint8_t Date:4;      // Day of month, 1 = first day  
  uint8_t Date10:2;  
  uint8_t reserved3:2;  
  uint8_t Month:4;     // Month, 1 = January  
  uint8_t Month10:1;  
  uint8_t reserved4:3;  
  uint8_t Day:3;      // Day of week, 1 = first day (any day)  
  uint8_t reserved5:5;  
  uint8_t Year:4;      // Year, 0 = year 2000  
  uint8_t Year10:4;  
  uint8_t reserved6:7;  
  uint8_t WP:1;       // WP = Write Protect  
 };  
 /* Controller for the 7 segment display */  
 #include "Arduino.h"  
 /* Pin order for digit select DIO */  
 const byte u8PinOut_Digit[] = {13,6,5,2};  
 /* Pin order for segment DIO. The segment order is A,B,C,D,E,F,G,DP */  
 const byte u8PinOut_Segment[] = {3,7,11,9,8,4,12,10};  
 const byte u8Digit_Map[] = {252,96,218,242,102,182,190,224,254,230,2};  
 class HC7Segment  
 {  
  public:  
  HC7Segment(byte Digits, bool DigitSelectState);  
  void vDisplay_Number(int u16Value);  
  void vDisplay_Number(int Value, byte DecimalPoint);  
  private:  
  void vWrite_Digit(byte Value, bool IncludeDecimalPoint);  
  void vSelect_Digit(byte u8Value);  
  void vDeselect_Digits(void);  
  byte _Digits;  
  bool _DigitSelectState;  
 };  
 /* Create an instance of HC7Segment(). In this example we will be using a 4 digit  
 common cathode display (CAI5461AH) */  
 HC7Segment HC7Segment(4, LOW);  
 //storage for temperature  
 float Temperature;  
 int decimal;  
 /* Place any setup code that you require here */  
 void setup()  
 {     
  ds1302_struct rtc;  
  analogReference(INTERNAL);  
  Serial.begin(9600);  
  // Start by clearing the Write Protect bit  
  // Otherwise the clock data cannot be written  
  // The whole register is written,   
  // but the WP-bit is the only bit in that register.  
  DS1302_write (DS1302_ENABLE, 0);  
  // Disable Trickle Charger.  
  DS1302_write (DS1302_TRICKLE, 0x00);  
  // Remove the next define,   
  // after the right date and time are set.  
  //#define SET_DATE_TIME_JUST_ONCE  
 #ifdef SET_DATE_TIME_JUST_ONCE   
  // Fill these variables with the date and time.  
  int seconds, minutes, hours, dayofweek, dayofmonth, month, year;  
  // Example for May 17, 2014, 17:21, thursday is 5th day of Week.  
  // Set your own time and date in these variables.  
  seconds  = 0;  
  minutes  = 21;  
  hours   = 17;  
  dayofweek = 4; // Day of week, any day can be first, counts 1...7  
  dayofmonth = 15; // Day of month, 1...31  
  month   = 5; // month 1...12  
  year    = 2014;  
  // Set a time and date  
  // This also clears the CH (Clock Halt) bit,   
  // to start the clock.  
  // Fill the structure with zeros to make   
  // any unused bits zero  
  memset ((char *) &rtc, 0, sizeof(rtc));  
  rtc.Seconds  = bin2bcd_l( seconds);  
  rtc.Seconds10 = bin2bcd_h( seconds);  
  rtc.CH     = 0;   // 1 for Clock Halt, 0 to run;  
  rtc.Minutes  = bin2bcd_l( minutes);  
  rtc.Minutes10 = bin2bcd_h( minutes);  
  // To use the 12 hour format,  
  // use it like these four lines:  
  //  rtc.h12.Hour  = bin2bcd_l( hours);  
  //  rtc.h12.Hour10 = bin2bcd_h( hours);  
  //  rtc.h12.AM_PM = 0;   // AM = 0  
  //  rtc.h12.hour_12_24 = 1; // 1 for 24 hour format  
  rtc.h24.Hour  = bin2bcd_l( hours);  
  rtc.h24.Hour10 = bin2bcd_h( hours);  
  rtc.h24.hour_12_24 = 0; // 0 for 24 hour format  
  rtc.Date    = bin2bcd_l( dayofmonth);  
  rtc.Date10   = bin2bcd_h( dayofmonth);  
  rtc.Month   = bin2bcd_l( month);  
  rtc.Month10  = bin2bcd_h( month);  
  rtc.Day    = dayofweek;  
  rtc.Year    = bin2bcd_l( year - 2000);  
  rtc.Year10   = bin2bcd_h( year - 2000);  
  rtc.WP = 0;   
  // Write all clock data at once (burst mode).  
  DS1302_clock_burst_write( (uint8_t *) &rtc);  
 #endif  
 }  
 void loop()  
 {  
  ds1302_struct rtc;  
  char buffer[80];   // the code uses 70 characters.  
  char charBuf[20];  // for thermometer  
  char decBuf[4];   // for thermometer  
  int k;  
  Temperature = analogRead(LM35Pin) / 9.31;  
  decimal=Temperature*100;  
  decimal=decimal%100;  
  //make string for serial  
  itoa (Temperature,charBuf,10);  
  strcat( charBuf, "." );  
  itoa (decimal,decBuf,10);  
  strcat( charBuf,decBuf);  
  // Read all clock data at once (burst mode).  
  DS1302_clock_burst_read( (uint8_t *) &rtc);  
  //Display the time for ~2 seconds  
  for ( k=0; k<2000; k++)  
   HC7Segment.vDisplay_Number( 100*bcd2bin( rtc.h24.Hour10, rtc.h24.Hour)+bcd2bin( rtc.Minutes10, rtc.Minutes),3);  
   sprintf( buffer, "Time = %02d:%02d:%02d,\n ", \  
   bcd2bin( rtc.h24.Hour10, rtc.h24.Hour), \  
   bcd2bin( rtc.Minutes10, rtc.Minutes), \  
   bcd2bin( rtc.Seconds10, rtc.Seconds));  
   //Serial.print(buffer); //time  
   //Display the temperature for ~2 seconds  
  for ( k=0; k<2000; k++)  
   HC7Segment.vDisplay_Number( Temperature*10,2);  
   Serial.println(charBuf); //temperature to the serial port for web viewer  
 }  
 // --------------------------------------------------------  
 // DS1302_clock_burst_read  
 //  
 // This function reads 8 bytes clock data in burst mode  
 // from the DS1302.  
 //  
 // This function may be called as the first function,   
 // also the pinMode is set.  
 //  
 void DS1302_clock_burst_read( uint8_t *p)  
 {  
  int i;  
  _DS1302_start();  
  // Instead of the address,   
  // the CLOCK_BURST_READ command is issued  
  // the I/O-line is released for the data  
  _DS1302_togglewrite( DS1302_CLOCK_BURST_READ, true);   
  for( i=0; i<8; i++)  
  {  
   *p++ = _DS1302_toggleread();  
  }  
  _DS1302_stop();  
 }  
 // --------------------------------------------------------  
 // DS1302_clock_burst_write  
 //  
 // This function writes 8 bytes clock data in burst mode  
 // to the DS1302.  
 //  
 // This function may be called as the first function,   
 // also the pinMode is set.  
 //  
 void DS1302_clock_burst_write( uint8_t *p)  
 {  
  int i;  
  _DS1302_start();  
  // Instead of the address,   
  // the CLOCK_BURST_WRITE command is issued.  
  // the I/O-line is not released  
  _DS1302_togglewrite( DS1302_CLOCK_BURST_WRITE, false);   
  for( i=0; i<8; i++)  
  {  
   // the I/O-line is not released  
   _DS1302_togglewrite( *p++, false);   
  }  
  _DS1302_stop();  
 }  
 // --------------------------------------------------------  
 // DS1302_read  
 //  
 // This function reads a byte from the DS1302   
 // (clock or ram).  
 //  
 // The address could be like "0x80" or "0x81",   
 // the lowest bit is set anyway.  
 //  
 // This function may be called as the first function,   
 // also the pinMode is set.  
 //  
 uint8_t DS1302_read(int address)  
 {  
  uint8_t data;  
  // set lowest bit (read bit) in address  
  bitSet( address, DS1302_READBIT);   
  _DS1302_start();  
  // the I/O-line is released for the data  
  _DS1302_togglewrite( address, true);   
  data = _DS1302_toggleread();  
  _DS1302_stop();  
  return (data);  
 }  
 // --------------------------------------------------------  
 // DS1302_write  
 //  
 // This function writes a byte to the DS1302 (clock or ram).  
 //  
 // The address could be like "0x80" or "0x81",   
 // the lowest bit is cleared anyway.  
 //  
 // This function may be called as the first function,   
 // also the pinMode is set.  
 //  
 void DS1302_write( int address, uint8_t data)  
 {  
  // clear lowest bit (read bit) in address  
  bitClear( address, DS1302_READBIT);    
  _DS1302_start();  
  // don't release the I/O-line  
  _DS1302_togglewrite( address, false);   
  // don't release the I/O-line  
  _DS1302_togglewrite( data, false);   
  _DS1302_stop();   
 }  
 // --------------------------------------------------------  
 // _DS1302_start  
 //  
 // A helper function to setup the start condition.  
 //  
 // An 'init' function is not used.  
 // But now the pinMode is set every time.  
 // That's not a big deal, and it's valid.  
 // At startup, the pins of the Arduino are high impedance.  
 // Since the DS1302 has pull-down resistors,   
 // the signals are low (inactive) until the DS1302 is used.  
 void _DS1302_start( void)  
 {  
  digitalWrite( DS1302_CE_PIN, LOW); // default, not enabled  
  pinMode( DS1302_CE_PIN, OUTPUT);   
  digitalWrite( DS1302_SCLK_PIN, LOW); // default, clock low  
  pinMode( DS1302_SCLK_PIN, OUTPUT);  
  pinMode( DS1302_IO_PIN, OUTPUT);  
  digitalWrite( DS1302_CE_PIN, HIGH); // start the session  
  delayMicroseconds( 4);      // tCC = 4us  
 }  
 // --------------------------------------------------------  
 // _DS1302_stop  
 //  
 // A helper function to finish the communication.  
 //  
 void _DS1302_stop(void)  
 {  
  // Set CE low  
  digitalWrite( DS1302_CE_PIN, LOW);  
  delayMicroseconds( 4);      // tCWH = 4us  
 }  
 // --------------------------------------------------------  
 // _DS1302_toggleread  
 //  
 // A helper function for reading a byte with bit toggle  
 //  
 // This function assumes that the SCLK is still high.  
 //  
 uint8_t _DS1302_toggleread( void)  
 {  
  uint8_t i, data;  
  data = 0;  
  for( i = 0; i <= 7; i++)  
  {  
   // Issue a clock pulse for the next databit.  
   // If the 'togglewrite' function was used before   
   // this function, the SCLK is already high.  
   digitalWrite( DS1302_SCLK_PIN, HIGH);  
   delayMicroseconds( 1);  
   // Clock down, data is ready after some time.  
   digitalWrite( DS1302_SCLK_PIN, LOW);  
   delayMicroseconds( 1);    // tCL=1000ns, tCDD=800ns  
   // read bit, and set it in place in 'data' variable  
   bitWrite( data, i, digitalRead( DS1302_IO_PIN));   
  }  
  return( data);  
 }  
 // --------------------------------------------------------  
 // _DS1302_togglewrite  
 //  
 // A helper function for writing a byte with bit toggle  
 //  
 // The 'release' parameter is for a read after this write.  
 // It will release the I/O-line and will keep the SCLK high.  
 //  
 void _DS1302_togglewrite( uint8_t data, uint8_t release)  
 {  
  int i;  
  for( i = 0; i <= 7; i++)  
  {   
   // set a bit of the data on the I/O-line  
   digitalWrite( DS1302_IO_PIN, bitRead(data, i));   
   delayMicroseconds( 1);   // tDC = 200ns  
   // clock up, data is read by DS1302  
   digitalWrite( DS1302_SCLK_PIN, HIGH);     
   delayMicroseconds( 1);   // tCH = 1000ns, tCDH = 800ns  
   if( release && i == 7)  
   {  
    // If this write is followed by a read,   
    // the I/O-line should be released after   
    // the last bit, before the clock line is made low.  
    // This is according the datasheet.  
    // I have seen other programs that don't release   
    // the I/O-line at this moment,  
    // and that could cause a shortcut spike   
    // on the I/O-line.  
    pinMode( DS1302_IO_PIN, INPUT);  
    // For Arduino 1.0.3, removing the pull-up is no longer needed.  
    // Setting the pin as 'INPUT' will already remove the pull-up.  
    // digitalWrite (DS1302_IO, LOW); // remove any pull-up   
   }  
   else  
   {  
    digitalWrite( DS1302_SCLK_PIN, LOW);  
    delayMicroseconds( 1);    // tCL=1000ns, tCDD=800ns  
   }  
  }  
 }  
 /* Constructor to initiliase the GPIO as outputs and in the OFF state*/  
 HC7Segment::HC7Segment(byte Digits, bool DigitSelectState)  
 {  
  byte LED_Cur_Digit;  
  byte LED_Cur_Segment;  
  _Digits = Digits;  
  _DigitSelectState = DigitSelectState;  
  for (LED_Cur_Digit = 0; LED_Cur_Digit < _Digits; LED_Cur_Digit++)  
  {  
   pinMode (u8PinOut_Digit[LED_Cur_Digit], OUTPUT);  
   digitalWrite(u8PinOut_Digit[LED_Cur_Digit], !_DigitSelectState);  
  }  
  for (LED_Cur_Segment = 0; LED_Cur_Segment < 8; LED_Cur_Segment++)  
  {  
   pinMode (u8PinOut_Segment[LED_Cur_Segment], OUTPUT);  
   digitalWrite(u8PinOut_Segment[LED_Cur_Segment], _DigitSelectState);  
  }  
 }  
 void HC7Segment::vDisplay_Number(int Value)  
 {  
  byte Loop;  
  bool IsNegative = Value < 0;  
  for (Loop = 0; Loop < _Digits; Loop++)  
  {  
   vDeselect_Digits();  
   if (Loop == (_Digits - 1) && IsNegative)  
      {  
           vWrite_Digit(10, 0);  
      }else  
      {  
             vWrite_Digit(abs(Value) % 10, 0);  
      }  
   Value /= 10;  
   vSelect_Digit(Loop);  
  }  
  vDeselect_Digits();  
 }  
 void HC7Segment::vDisplay_Number(int Value, byte DecimalPoint)  
 {  
  byte Loop;  
  bool IsNegative = Value < 0;  
  for (Loop = 0; Loop < _Digits; Loop++)  
  {  
   vDeselect_Digits();  
   if (Loop == (_Digits - 1) && IsNegative)  
   {  
           vWrite_Digit(10, 0);  
      }else  
      {  
        vWrite_Digit(abs(Value) % 10, Loop + 1 == DecimalPoint);  
      }  
   Value /= 10;  
   vSelect_Digit(Loop);  
  }  
  vDeselect_Digits();  
 }  
 void HC7Segment::vWrite_Digit(byte Value, bool IncludeDecimalPoint)  
 {  
  byte Loop;  
  for(Loop = 0; Loop < 8; Loop++)  
  {  
   digitalWrite(u8PinOut_Segment[Loop], (~((u8Digit_Map[Value] >> 7-Loop) ^ !_DigitSelectState))&1);  
  }  
  if(IncludeDecimalPoint)  
       digitalWrite(u8PinOut_Segment[7], !_DigitSelectState);  
 }  
 void HC7Segment::vSelect_Digit(byte Value)  
 {  
  byte Loop;  
  for (Loop = 0; Loop < _Digits; Loop++)  
  {  
   digitalWrite(u8PinOut_Digit[Loop],!_DigitSelectState);  
  }  
  digitalWrite(u8PinOut_Digit[Value],_DigitSelectState);  
 }  
 void HC7Segment::vDeselect_Digits(void)  
 {  
  byte Loop;  
  for (Loop = 0; Loop < _Digits; Loop++)  
  {  
       digitalWrite(u8PinOut_Digit[Loop],!_DigitSelectState);  
  }  
 }  

A video of the clock is shown below



The same clock has been updated with hourly chime or beeps using a buzzer and the whole setup has been ported in to a prototype pcb along with an arduino pro mini. The arduino sketch and some of the pictures are given below.

Take care of the led pins as am using a direct drive ( a more efficient way is to use serial type led modules which can work with 2-3 pins on the arduino)

DS1302 is hooked and one buzzer is hooked to Digital pin 3 in the sketch below.

A simple arduino rtc clock with chime/ hourly buzzer
Connecting the cable (usb to uart for uploading the sketch to arduino pro mini

Connecting usb uart to pro min for programming

Pro mini on the pcb (quick and dirty mount)

Arduino promini for a simple rtc clock

Sketch (updated for the chime/buzzer function)





// DS1302 RTC
// ----------
//
// Open Source / Public Domain
//
// Version 1
//     By arduino.cc user "Krodal".
//     June 2012
//     Using Arduino 1.0.1
// Version 2
//     By arduino.cc user "Krodal"
//     March 2013
//     Using Arduino 1.0.3, 1.5.2
//     The code is no longer compatible with older versions.
//     Added bcd2bin, bin2bcd_h, bin2bcd_l
//     A few minor changes.
//
//
// Documentation: datasheet
// 
// The DS1302 uses a 3-wire interface: 
//    - bidirectional data.
//    - clock
//    - chip select
// It is not I2C, not OneWire, and not SPI.
// So the standard libraries can not be used.
// Even the shiftOut() function is not used, since it
// could be too fast (it might be slow enough, 
// but that's not certain).
//
// I wrote my own interface code according to the datasheet.
// Any three pins of the Arduino can be used.
//   See the first defines below this comment, 
//   to set your own pins.
//
// The "Chip Enable" pin was called "/Reset" before.
//
// The chip has internal pull-down registers.
// This keeps the chip disabled, even if the pins of 
// the Arduino are floating.
//
//
// Range
// -----
//      seconds : 00-59
//      minutes : 00-59
//      hour    : 1-12 or 0-23
//      date    : 1-31
//      month   : 1-12
//      day     : 1-7
//      year    : 00-99
//
//
// Burst mode
// ----------
// In burst mode, all the clock data is read at once.
// This is to prevent a rollover of a digit during reading.
// The read data is from an internal buffer.
//
// The burst registers are commands, rather than addresses.
// Clock Data Read in Burst Mode
//    Start by writing 0xBF (as the address), 
//    after that: read clock data
// Clock Data Write in Burst Mode
//    Start by writing 0xBE (as the address), 
//    after that: write clock data
// Ram Data Read in Burst Mode
//    Start by writing 0xFF (as the address), 
//    after that: read ram data
// Ram Data Write in Burst Mode
//    Start by writing 0xFE (as the address), 
//    after that: write ram data
//
//
// Ram
// ---
// The DS1302 has 31 of ram, which can be used to store data.
// The contents will be lost if the Arduino is off, 
// and the backup battery gets empty.
// It is better to store data in the EEPROM of the Arduino.
// The burst read or burst write for ram is not implemented 
// in this code.
//
//
// Trickle charge
// --------------
// The DS1302 has a build-in trickle charger.
// That can be used for example with a lithium battery 
// or a supercap.
// Using the trickle charger has not been implemented 
// in this code.
//


// Set your own pins with these defines !
#define DS1302_SCLK_PIN   2    // Arduino pin for the Serial Clock
#define DS1302_IO_PIN     15    // Arduino pin for the Data I/O
#define DS1302_CE_PIN     17    // Arduino pin for the Chip Enable


// Macros to convert the bcd values of the registers to normal
// integer variables.
// The code uses seperate variables for the high byte and the low byte
// of the bcd, so these macros handle both bytes seperately.
#define bcd2bin(h,l)    (((h)*10) + (l))
#define bin2bcd_h(x)   ((x)/10)
#define bin2bcd_l(x)    ((x)%10)


// Register names.
// Since the highest bit is always '1', 
// the registers start at 0x80
// If the register is read, the lowest bit should be '1'.
#define DS1302_SECONDS           0x80
#define DS1302_MINUTES           0x82
#define DS1302_HOURS             0x84
#define DS1302_DATE              0x86
#define DS1302_MONTH             0x88
#define DS1302_DAY               0x8A
#define DS1302_YEAR              0x8C
#define DS1302_ENABLE            0x8E
#define DS1302_TRICKLE           0x90
#define DS1302_CLOCK_BURST       0xBE
#define DS1302_CLOCK_BURST_WRITE 0xBE
#define DS1302_CLOCK_BURST_READ  0xBF
#define DS1302_RAMSTART          0xC0
#define DS1302_RAMEND            0xFC
#define DS1302_RAM_BURST         0xFE
#define DS1302_RAM_BURST_WRITE   0xFE
#define DS1302_RAM_BURST_READ    0xFF



// Defines for the bits, to be able to change 
// between bit number and binary definition.
// By using the bit number, using the DS1302 
// is like programming an AVR microcontroller.
// But instead of using "(1<<X)", or "_BV(X)", 
// the Arduino "bit(X)" is used.
#define DS1302_D0 0
#define DS1302_D1 1
#define DS1302_D2 2
#define DS1302_D3 3
#define DS1302_D4 4
#define DS1302_D5 5
#define DS1302_D6 6
#define DS1302_D7 7


// Bit for reading (bit in address)
#define DS1302_READBIT DS1302_D0 // READBIT=1: read instruction

// Bit for clock (0) or ram (1) area, 
// called R/C-bit (bit in address)
#define DS1302_RC DS1302_D6

// Seconds Register
#define DS1302_CH DS1302_D7   // 1 = Clock Halt, 0 = start

// Hour Register
#define DS1302_AM_PM DS1302_D5 // 0 = AM, 1 = PM
#define DS1302_12_24 DS1302_D7 // 0 = 24 hour, 1 = 12 hour

// Enable Register
#define DS1302_WP DS1302_D7   // 1 = Write Protect, 0 = enabled

// Trickle Register
#define DS1302_ROUT0 DS1302_D0
#define DS1302_ROUT1 DS1302_D1
#define DS1302_DS0   DS1302_D2
#define DS1302_DS1   DS1302_D2
#define DS1302_TCS0  DS1302_D4
#define DS1302_TCS1  DS1302_D5
#define DS1302_TCS2  DS1302_D6
#define DS1302_TCS3  DS1302_D7
#define LM35Pin 7

// Structure for the first 8 registers.
// These 8 bytes can be read at once with 
// the 'clock burst' command.
// Note that this structure contains an anonymous union.
// It might cause a problem on other compilers.
typedef struct ds1302_struct
{
  uint8_t Seconds:4;      // low decimal digit 0-9
  uint8_t Seconds10:3;    // high decimal digit 0-5
  uint8_t CH:1;           // CH = Clock Halt
  uint8_t Minutes:4;
  uint8_t Minutes10:3;
  uint8_t reserved1:1;
  union
  {
    struct
    {
      uint8_t Hour:4;
      uint8_t Hour10:2;
      uint8_t reserved2:1;
      uint8_t hour_12_24:1; // 0 for 24 hour format
    } h24;
    struct
    {
      uint8_t Hour:4;
      uint8_t Hour10:1;
      uint8_t AM_PM:1;      // 0 for AM, 1 for PM
      uint8_t reserved2:1;
      uint8_t hour_12_24:1; // 1 for 12 hour format
    } h12;
  };
  uint8_t Date:4;           // Day of month, 1 = first day
  uint8_t Date10:2;
  uint8_t reserved3:2;
  uint8_t Month:4;          // Month, 1 = January
  uint8_t Month10:1;
  uint8_t reserved4:3;
  uint8_t Day:3;            // Day of week, 1 = first day (any day)
  uint8_t reserved5:5;
  uint8_t Year:4;           // Year, 0 = year 2000
  uint8_t Year10:4;
  uint8_t reserved6:7;
  uint8_t WP:1;             // WP = Write Protect
};

/* Include the 7 segment display library */
#include <HC7Segment.h>
/* Pin order for digit select DIO */
const byte u8PinOut_Digit[] = {14,12,13,10};

/* Pin order for segment DIO. The segment order is A,B,C,D,E,F,G,DP */
const byte u8PinOut_Segment[] = {8,9,6,4,5,11,7,16};
/* Create an instance of HC7Segment(). In this example we will be using a 4 digit
common cathode display (CAI5461AH) */
HC7Segment HC7Segment(4, LOW);
//storage for temperature
float Temperature;
int decimal;
/* Place any setup code that you require here */
void setup()
{      
  ds1302_struct rtc;
  analogReference(INTERNAL);
  pinMode(3, OUTPUT);
  Serial.begin(9600);
  beep(50);
  //Serial.println(F("DS1302 Real Time Clock"));
  //Serial.println(F("Version 2, March 2013"));


  // Start by clearing the Write Protect bit
  // Otherwise the clock data cannot be written
  // The whole register is written, 
  // but the WP-bit is the only bit in that register.
  DS1302_write (DS1302_ENABLE, 0);

  // Disable Trickle Charger.
  DS1302_write (DS1302_TRICKLE, 0x00);

// Remove the next define, 
// after the right date and time are set.
//#define SET_DATE_TIME_JUST_ONCE
#ifdef SET_DATE_TIME_JUST_ONCE  

  // Fill these variables with the date and time.
  int seconds, minutes, hours, dayofweek, dayofmonth, month, year;

  // Example for april 15, 2013, 10:08, monday is 2nd day of Week.
  // Set your own time and date in these variables.
  seconds    = 0;
  minutes    = 24;
  hours      = 20;
  dayofweek  = 2;  // Day of week, any day can be first, counts 1...7
  dayofmonth = 26; // Day of month, 1...31
  month      = 5;  // month 1...12
  year       = 2014;

  // Set a time and date
  // This also clears the CH (Clock Halt) bit, 
  // to start the clock.

  // Fill the structure with zeros to make 
  // any unused bits zero
  memset ((char *) &rtc, 0, sizeof(rtc));

  rtc.Seconds    = bin2bcd_l( seconds);
  rtc.Seconds10  = bin2bcd_h( seconds);
  rtc.CH         = 0;      // 1 for Clock Halt, 0 to run;
  rtc.Minutes    = bin2bcd_l( minutes);
  rtc.Minutes10  = bin2bcd_h( minutes);
  // To use the 12 hour format,
  // use it like these four lines:
  //    rtc.h12.Hour   = bin2bcd_l( hours);
  //    rtc.h12.Hour10 = bin2bcd_h( hours);
  //    rtc.h12.AM_PM  = 0;     // AM = 0
  //    rtc.h12.hour_12_24 = 1; // 1 for 24 hour format
  rtc.h24.Hour   = bin2bcd_l( hours);
  rtc.h24.Hour10 = bin2bcd_h( hours);
  rtc.h24.hour_12_24 = 0; // 0 for 24 hour format
  rtc.Date       = bin2bcd_l( dayofmonth);
  rtc.Date10     = bin2bcd_h( dayofmonth);
  rtc.Month      = bin2bcd_l( month);
  rtc.Month10    = bin2bcd_h( month);
  rtc.Day        = dayofweek;
  rtc.Year       = bin2bcd_l( year - 2000);
  rtc.Year10     = bin2bcd_h( year - 2000);
  rtc.WP = 0;  

  // Write all clock data at once (burst mode).
  DS1302_clock_burst_write( (uint8_t *) &rtc);
#endif
}


void loop()
{

  ds1302_struct rtc;
  char buffer[80];     // the code uses 70 characters.
  char charBuf[20];    // for thermometer
  char decBuf[4];      // for thermometer
  int k;
 Temperature = analogRead(LM35Pin) / 9.31;
 decimal=Temperature*100;
 decimal=decimal%100;
 
 //make string for serial
 itoa (Temperature,charBuf,10);
 strcat( charBuf, "." );
 itoa (decimal,decBuf,10);
 strcat( charBuf,decBuf);
 
 // Read all clock data at once (burst mode).
  DS1302_clock_burst_read( (uint8_t *) &rtc);
 //sprintf( buffer, "Time = %02d:%02d:%02d, ", \
  // bcd2bin( rtc.h24.Hour10, rtc.h24.Hour), \
  // bcd2bin( rtc.Minutes10, rtc.Minutes), \
  // bcd2bin( rtc.Seconds10, rtc.Seconds));
   //Serial.print(buffer);
  for ( k=0; k<2000; k++)
    HC7Segment.vDisplay_Number( 100*bcd2bin( rtc.h24.Hour10, rtc.h24.Hour)+bcd2bin( rtc.Minutes10, rtc.Minutes),3);
   
   sprintf( buffer, "Time = %02d:%02d:%02d,\n ", \
   bcd2bin( rtc.h24.Hour10, rtc.h24.Hour), \
   bcd2bin( rtc.Minutes10, rtc.Minutes), \
   bcd2bin( rtc.Seconds10, rtc.Seconds));
   //Serial.print(buffer); //time
   for ( k=0; k<2000; k++)
    HC7Segment.vDisplay_Number( Temperature*10,2);
   Serial.println(charBuf); //temperature
 if((rtc.Minutes10+rtc.Minutes+rtc.Seconds10==0) && (bcd2bin( rtc.h24.Hour10, rtc.h24.Hour)>4) && rtc.Seconds<4)
  {
  beep(50);
  beep(50);
  beep(50);
  }
  //every 30minutes
  if( (bcd2bin( rtc.Minutes10, rtc.Minutes) ==30)&& (rtc.Seconds<2)&& (rtc.Seconds10==0) && (bcd2bin( rtc.h24.Hour10, rtc.h24.Hour)>4) )
  {
  beep(50);  
  }
   
}


// --------------------------------------------------------
// DS1302_clock_burst_read
//
// This function reads 8 bytes clock data in burst mode
// from the DS1302.
//
// This function may be called as the first function, 
// also the pinMode is set.
//
void DS1302_clock_burst_read( uint8_t *p)
{
  int i;

  _DS1302_start();

  // Instead of the address, 
  // the CLOCK_BURST_READ command is issued
  // the I/O-line is released for the data
  _DS1302_togglewrite( DS1302_CLOCK_BURST_READ, true);  

  for( i=0; i<8; i++)
  {
    *p++ = _DS1302_toggleread();
  }
  _DS1302_stop();
}


// --------------------------------------------------------
// DS1302_clock_burst_write
//
// This function writes 8 bytes clock data in burst mode
// to the DS1302.
//
// This function may be called as the first function, 
// also the pinMode is set.
//
void DS1302_clock_burst_write( uint8_t *p)
{
  int i;

  _DS1302_start();

  // Instead of the address, 
  // the CLOCK_BURST_WRITE command is issued.
  // the I/O-line is not released
  _DS1302_togglewrite( DS1302_CLOCK_BURST_WRITE, false);  

  for( i=0; i<8; i++)
  {
    // the I/O-line is not released
    _DS1302_togglewrite( *p++, false);  
  }
  _DS1302_stop();
}


// --------------------------------------------------------
// DS1302_read
//
// This function reads a byte from the DS1302 
// (clock or ram).
//
// The address could be like "0x80" or "0x81", 
// the lowest bit is set anyway.
//
// This function may be called as the first function, 
// also the pinMode is set.
//
uint8_t DS1302_read(int address)
{
  uint8_t data;

  // set lowest bit (read bit) in address
  bitSet( address, DS1302_READBIT);  

  _DS1302_start();
  // the I/O-line is released for the data
  _DS1302_togglewrite( address, true);  
  data = _DS1302_toggleread();
  _DS1302_stop();

  return (data);
}


// --------------------------------------------------------
// DS1302_write
//
// This function writes a byte to the DS1302 (clock or ram).
//
// The address could be like "0x80" or "0x81", 
// the lowest bit is cleared anyway.
//
// This function may be called as the first function, 
// also the pinMode is set.
//
void DS1302_write( int address, uint8_t data)
{
  // clear lowest bit (read bit) in address
  bitClear( address, DS1302_READBIT);   

  _DS1302_start();
  // don't release the I/O-line
  _DS1302_togglewrite( address, false); 
  // don't release the I/O-line
  _DS1302_togglewrite( data, false); 
  _DS1302_stop();  
}


// --------------------------------------------------------
// _DS1302_start
//
// A helper function to setup the start condition.
//
// An 'init' function is not used.
// But now the pinMode is set every time.
// That's not a big deal, and it's valid.
// At startup, the pins of the Arduino are high impedance.
// Since the DS1302 has pull-down resistors, 
// the signals are low (inactive) until the DS1302 is used.
void _DS1302_start( void)
{
  digitalWrite( DS1302_CE_PIN, LOW); // default, not enabled
  pinMode( DS1302_CE_PIN, OUTPUT);  

  digitalWrite( DS1302_SCLK_PIN, LOW); // default, clock low
  pinMode( DS1302_SCLK_PIN, OUTPUT);

  pinMode( DS1302_IO_PIN, OUTPUT);

  digitalWrite( DS1302_CE_PIN, HIGH); // start the session
  delayMicroseconds( 4);           // tCC = 4us
}


// --------------------------------------------------------
// _DS1302_stop
//
// A helper function to finish the communication.
//
void _DS1302_stop(void)
{
  // Set CE low
  digitalWrite( DS1302_CE_PIN, LOW);

  delayMicroseconds( 4);           // tCWH = 4us
}


// --------------------------------------------------------
// _DS1302_toggleread
//
// A helper function for reading a byte with bit toggle
//
// This function assumes that the SCLK is still high.
//
uint8_t _DS1302_toggleread( void)
{
  uint8_t i, data;

  data = 0;
  for( i = 0; i <= 7; i++)
  {
    // Issue a clock pulse for the next databit.
    // If the 'togglewrite' function was used before 
    // this function, the SCLK is already high.
    digitalWrite( DS1302_SCLK_PIN, HIGH);
    delayMicroseconds( 1);

    // Clock down, data is ready after some time.
    digitalWrite( DS1302_SCLK_PIN, LOW);
    delayMicroseconds( 1);        // tCL=1000ns, tCDD=800ns

    // read bit, and set it in place in 'data' variable
    bitWrite( data, i, digitalRead( DS1302_IO_PIN)); 
  }
  return( data);
}


// --------------------------------------------------------
// _DS1302_togglewrite
//
// A helper function for writing a byte with bit toggle
//
// The 'release' parameter is for a read after this write.
// It will release the I/O-line and will keep the SCLK high.
//
void _DS1302_togglewrite( uint8_t data, uint8_t release)
{
  int i;

  for( i = 0; i <= 7; i++)
  { 
    // set a bit of the data on the I/O-line
    digitalWrite( DS1302_IO_PIN, bitRead(data, i));  
    delayMicroseconds( 1);     // tDC = 200ns

    // clock up, data is read by DS1302
    digitalWrite( DS1302_SCLK_PIN, HIGH);     
    delayMicroseconds( 1);     // tCH = 1000ns, tCDH = 800ns

    if( release && i == 7)
    {
      // If this write is followed by a read, 
      // the I/O-line should be released after 
      // the last bit, before the clock line is made low.
      // This is according the datasheet.
      // I have seen other programs that don't release 
      // the I/O-line at this moment,
      // and that could cause a shortcut spike 
      // on the I/O-line.
      pinMode( DS1302_IO_PIN, INPUT);

      // For Arduino 1.0.3, removing the pull-up is no longer needed.
      // Setting the pin as 'INPUT' will already remove the pull-up.
      // digitalWrite (DS1302_IO, LOW); // remove any pull-up  
    }
    else
    {
      digitalWrite( DS1302_SCLK_PIN, LOW);
      delayMicroseconds( 1);       // tCL=1000ns, tCDD=800ns
    }
  }
}

void beep(unsigned char delayms){
  analogWrite(3, 55);      // Almost any value can be used except 0 and 255
                           // experiment to get the best tone
  delay(delayms);          // wait for a delayms ms
  analogWrite(3, 0);       // 0 turns it off
  delay(delayms);          // wait for a delayms ms   
}

No comments:

Post a Comment