MSP430 - 24-hr Clock/Calendar

Here is a 24-hr Clock/Calendar project with a twist.

The idea is to create a perpetual clock that will run for 10,000 years without requiring calibration. This idea was inspired by the 10,000 Year Clock project from the Long Now Foundation:
http://longnow.org/clock/

To achieve this goal, I will present the project in stages:

Stage 1 - Simple 24-hr clock program.
Stage 2 - Add date calendar with automatic leap year correction.
Stage 3 - Add low power operation with solar power.
Stage 4 - Add automatic synchronization with the sun.

The goal of the 10,000 Year Clock project is to build a clock that will keep working for 10,000 years while requiring minimum maintenance. The Long Now Clock is built entirely with mechanical components.

In my project, I will relax the constraints and will use ultra low power components such as the MSP430G2553 microcontroller and an LCD module.

I am hoping to run the electronics entirely from a solar cell.

Of course, the tricky part is how to keep the clock synchronized with the sun. The plan is to have a photo detector which will detect the midday sun and use this signal for synchronization. The system will be designed such that synchronization information will only be used on sunny days. The clock would continue to keep time for days or weeks if the sun doesn't shine.

Stage 1 - 24-hr Clock

We begin by taking the basic timer code that was presented in a previous post.

MSP430 Low Power mode

The MSP430G2553 is operating in low power mode 3 running off a 32768Hz watch crystal.

The timer module is set to interrupt once every second. The mcu comes out of low power mode, updates the time and displays it on the LCD display.

Code:
//************************************************
//   MSP430G2553 Clock/Calendar
//************************************************

/*-----------------------------------------------

2013.07.17 - MrChips
   
Notes: LCD E has been changed from P2.6 to P1.6
------------------------------------------------*/

#include "io430.h"

//---------------------------------------------------
// Global Variables
//---------------------------------------------------

short Year, Month, Day, Weekday;
short Hour, Minute, Second;

//---------------------------------------------------
// Defines
//---------------------------------------------------

#define title1 "MrChips    Clock"
#define title2 "Version     1.00"

#define TRUE  1
#define FALSE 0
#define ON    1
#define OFF   0

#define LED1 P1OUT_bit.P0

// ASCII codes

#define SP    32
#define COLON 58

//---------------------------------------------------
// LCD Interface
//---------------------------------------------------

void LCD_init(void);
void LCD_ready(void);
void LCD_ir(char ch);
void LCD_dr(char ch);
void LCD_txt(char *s);
void LCD_Home(void);
void LCD_Clear(void);
void LCD_Line1(void);
void LCD_Line2(void);
void LCD_Line3(void);
void LCD_Line4(void);
void LCD_Posn(char n);

// define LCD interface on PORT2
#define LCD_OUT  P2OUT
#define LCD_IN   P2IN
#define LCD_DDR  P2DIR
#define LCD_RS   P2OUT_bit.P4
#define LCD_RW   P2OUT_bit.P5
#define LCD_E    P1OUT_bit.P6

void LCD_init(void)
{
  LCD_ir(0x28);         // dual line, 4 bits
  LCD_ir(0x06);         // increment mode
  LCD_ir(0x0C);         // cursor turned off
  LCD_ir(0x10);         // cursor right
  LCD_ir(0x01);         // clear display
}

void LCD_Clear(void)
{
  LCD_ir(0x01);
}

void LCD_Home(void)
{
  LCD_ir(0x02);
}

void LCD_Line1(void)
{
  unsigned char i;
  LCD_ir(0x80);
  for (i = 0; i < 16; i++)
  {
    LCD_dr(SP);
  }
  LCD_ir(0x80);
}

void LCD_Line2(void)
{
  unsigned char i;
  LCD_ir(0xC0);
  for (i = 0; i < 16; i++)
  {
    LCD_dr(SP);
  }
  LCD_ir(0xC0);
}

void LCD_Line3(void)
{
  LCD_ir(0x94);
}

void LCD_Line4(void)
{
  LCD_ir(0xD4);
}

void LCD_Posn(char n)
// direct positioning where n = 0-79
{
  char ch;
  ch = n;
  if (ch  >= 40) ch += 24;
  LCD_ir(ch | 0x80);
}

void LCD_ready(void)
{
  char busy;
  LCD_DDR = 0x30;
  LCD_RW = 1;
  LCD_RS = 0;
  do
  {
    LCD_E  = 1;
    // high 4 bits read first
    // busy flag is D7 of LCD = bit 3 of P2IN
    busy = (LCD_IN & 0x08);
    LCD_E  = 0;
    LCD_E  = 1;
    LCD_E  = 0;
  } while (busy);
  LCD_OUT = 0;
  LCD_DDR = 0x3F;
}

void LCD_ir(char ch)
{
  LCD_ready();
  //LCD_RS = 0;     // not needed      // select instruction register
  //LCD_RW = 0;     // not needed
  LCD_OUT |= ((ch >> 4) & 0x0F);
  LCD_E = 1;
  LCD_E = 0;
  LCD_OUT = 0;
  LCD_OUT |= (ch & 0x0F);
  LCD_E = 1;
  LCD_E = 0;
}

void LCD_dr(char ch)
{
  LCD_ready();
  LCD_RS = 1;        // select data register
  //LCD_RW = 0;      // not needed
  LCD_OUT |= ((ch >> 4) & 0x0F);
  LCD_E = 1;
  LCD_E = 0;
  LCD_OUT = 0;
  LCD_RS = 1;
  LCD_OUT |= (ch & 0x0F);
  LCD_E = 1;
  LCD_E = 0;
}

void LCD_txt(char *s)
{
  while(*s) LCD_dr(*s++);
}

void LCD_bcd2(short n)
// display two digits
{
  LCD_dr('0' + n / 10);
  LCD_dr('0' + n % 10);
}

//---------------------------------------------------
// Date & Time Display
//---------------------------------------------------

void ShowTime(void)
{
  LCD_Line2();
  LCD_bcd2(Hour);
  LCD_dr(COLON);
  LCD_bcd2(Minute);
  LCD_dr(COLON);
  LCD_bcd2(Second);
}

//---------------------------------------------------
// Hardware Initialization
//---------------------------------------------------

void init(void)
{
   // Stop watchdog timer to prevent time out reset
  WDTCTL = WDTPW + WDTHOLD;

    // Set up 32768Hz crystal
  BCSCTL1 |= DIVA_3;    // divide by 8
  BCSCTL3 |= XCAP_3;    // select 12pF caps

  P1DIR = 0xFF;  // configure output ports
  P1OUT = 0x00;

  P2DIR = 0x3F;
  P2OUT = 0x00;

  // initialize Timer0_A
  TA0CCR0 = 511;              // set up terminal count
  TA0CTL = TASSEL_1 + ID_3 + MC_1; // configure and start timer

  // enable interrupts
  TA0CCTL0_bit.CCIE = 1;   // enable timer interrupts
  __enable_interrupt();    // set GIE in SR

}

#pragma vector = TIMER0_A0_VECTOR
__interrupt void myTimerISR(void)
{
  if (++Second >= 60)
  {
    Second = 0;
    if (++Minute >= 60)
    {
      Minute = 0;
      if (++Hour >=24)
      {
        Hour = 0;
      }
    }
  }
  ShowTime();
}

void init_Date_Time(void)
{
  Year  = 2013;
  Month = 7;
  Day   = 17;
  Weekday = 4;

  Hour   = 0;
  Minute = 0;
  Second = 0;
}

//-----------------------------------
//  Main
//-----------------------------------

void main(void)
{
  init();
  init_Date_Time();
  LCD_init();

  LCD_txt(title1);
  LCD_Line2();
  LCD_txt(title2);

  // enter low power mode
  LPM3;
}

//-----------------------------------
//  end of Main
//-----------------------------------
Notes:

1. The LCD E signal has been moved from P2.6 to P1.6 to allow for the 32768Hz crystal.

2. The 32768Hz watch crystal is on pin-18 and pin-19.

3. I have yet to source a 3V LCD module. For now I will use a 5V LCD module for the time being.

4. There will be no provision to set the date and time. The expectation is that once the project is completed, we only need to set the date and time once at the time the mcu is programmed. Besides, the clock will adjust itself to the midday sun.

Coming next: Stage 2 - Adding date calendar.

PREVIOUS NEXT

MSP430 Tutorial - Index

Blog entry information

Author
MrChips
Views
5,989
Last update

More entries in General

More entries from MrChips

Share this entry

Top