Jump to content
43oh

Software Real Time Clock (RTC) - two methods


Recommended Posts

The Launchpad comes with a 32.768 kHz crystal that can be used when timing more precise than the DCO is needed. The crystal frequency is useful for simple timing tasks, such as a RTC, because it is a power of 2. That allows a simple cascade of 15 flip-flops to create a 1 pulse per second time base. It is common for microcontroller timers to have prescalers, postscalers and/or preset divisors that are simply a tap on the flip-flip chain. The watchdog timer in the MSP430 is an example of a timer with power of 2 presets (64, 512, 8192, 32768). TimerA units have a prescaler with power of 2 presets (1, 2, 4, 8).

 

The sample code in this post uses the watchdog timer to create an interrupt that occurs once per second. TimerA can also be used to do this. One of the advantages of using TimerA is the ability to change the preset +/- 1 every N cycles to allow for calibration and temperature compensation of the crystal. I chose to use the watchdog timer so that TimerA was free to be used for alarm tones or PWM backlight control.

 

post-2341-13513554337_thumb.jpg

 

Using the C standard library time functions for a RTC

The C standard library provides several functions for working with real time. The function prototypes are in time.h. The type time_t is typically an integer type that is in units of 1 second. It doesn't have to be that, but usually is. The epoch is usually January 1, 1970 for "unix time." Be aware that CCS uses an epoch of January 1, 1900 and time_t is unsigned rather than signed. I assume GCC uses the more common unix time epoch and time_t is signed.

 

Since time_t is in units of 1 second, the ISR for timekeeping is trivial...

#pragma vector = WDT_VECTOR                             // - Watchdog timer interrupt vector 
__interrupt void wdt_isr(void)                          // This interrupt will occur once per second
{                                                       //
   ++tt;                                               // Increment time_t
}                                                       //

 

To display the time it can be converted to a tm structure that has elements for hours, minutes, day, month, ect. There are two functions to do this: gmtime() and localtime(). gmtime() converts to UTC, and localtime converts to the time zone for the set locale. Both functions take a time_t and return a pointer to a static tm struct.

 

To set the time the mktime() function will convert a tm struct to a time_t value. It will also determine day of week and day or year for you.

 

Benefits

Tivial ISR. Simple and efficient.

Portable code - the C standard library time functions work on almost any device with a C compiler.

Easy to calulate time deltas - use ordinary integer math on time_t values.

Easy to compare times using <, >, ==, etc.

Compact representation of time (32 bit time_t typically) is good for timestamps of data logs.

Some file systems (not FAT unfortunately) use time_t as for timestamps.

 

Concerns

Converting from time_t to tm struct takes quite a few clock cycles, so not good for low power (battery powered) systems.

Time functions take up quite a bit of flash - will not fit in G2211 for example.

 

Software model of a hardware RTC

Hardware real time clocks typically have counters for seconds, minutes, hours, ect. Each counter will reset at the appropriate

limit - 60 seconds, 24 hours, ect. This approch can be use for a software RTC. The ISR has more code than the previous version...

#pragma vector = WDT_VECTOR                             // - Watchdog timer interrupt vector 
__interrupt void wdt_isr(void)                          // This interrupt will occur once per second
{                                                       //
   if(++t.tm_sec > 59) {                               // Increment seconds, check for overflow
       t.tm_sec = 0;                                   // Reset seconds
       if(++t.tm_min > 59) {                           // Increment minutes, check for overflow
           t.tm_min = 0;                               // Reset minutes
           if(++t.tm_hour > 23) {                      // Increment hours, check for overflow
               t.tm_hour = 0;                          // Reset hours
               ++t.tm_yday;                            // Increment day of year
               if(++t.tm_wday > 6)                     // Increment day of week, check for overflow
                   t.tm_wday = 0;                      // Reset day of week
                                                       // Increment day of month, check for overflow
               if(++t.tm_mday > dim[t.tm_mon][is_leap_year(t.tm_year + 1900)]) {
                   t.tm_mday = 1;                      // Reset day of month
                   if(++t.tm_mon > 11) {               // Increment month, check for overflow
                       t.tm_mon = 0;                   // Reset month
                       t.tm_yday = 0;                  // Reset day of year
                       ++t.tm_year;                    // Increment year
                   }                                   // - year       
               }                                       // - month
           }                                           // - day
       }                                               // - hour
   }                                                   // - minute
}                                                       //

 

Most of the time the ISR will just increment the seconds member and return. The worst case is at the end of the year when the year is incremented and all other counters are reset to inital values.

 

There is no need for any conversion for display of the time or setting the time. Be aware that day of week and day of year must be explicity set and can be out-of-sync if not properly set.

 

Benefits

No conversion function needed for display of time or setting of time.

Small code size. Fits in G2211.

High efficiency is good for battery powered applications.

Use of tm struct provides some familiarity to those who have used the C standard library functions.

 

Concerns

No easy to way calulate time delta.

No support for time zones / localization.

Time comparison (for alarms) requires more MCU cycles because several structure members require comparason rather than just a long integer.

 

This sample code can be configured to use either of the two methods descibed by (un)commenting the "#define USE_STDLIB_TIME" in main.c

 

main.c

#include 
#include 
#include 
#include "rtc.h"
#include "lcd.h"

//#define USE_STDLIB_TIME       // Use functions in time.h

void show_time(const struct tm *t)  // Show time on LCD
{
   static const char *dow[7] = {
       "Sunday",
       "Monday",
       "Tuesday",
       "Wednesday",
       "Thursday",
       "Friday",
       "Saturday"
   };

   int x, w;
   const char *d;

   if(t->tm_hour < 10) {
       x = -1;
       lcd_fill(0, 1, 10, 2, 0);
   } else {
       x = 4;
       lcd_fill(0, 1, 4, 2, 0);
       lcd_pd12(t->tm_hour / 10, x, 1);
   }
   lcd_pd12(t->tm_hour % 10, x + 11, 1);
   lcd_pd12(11, x + 22, 1);
   lcd_pd12(t->tm_min / 10, x + 27, 1);
   lcd_pd12(t->tm_min % 10, x + 38, 1);
   lcd_pd12(11, x + 49, 1);
   lcd_pd12(t->tm_sec / 10, x + 54, 1);
   lcd_pd12(t->tm_sec % 10, x + 65, 1);
   lcd_fill(x + 76, 1, 8 - x, 2, 0); 

   if(t->tm_mon < 9) {
       x = -4;
       lcd_fill(0, 3, 7, 2, 0);
   } else {
       x = 1;
       lcd_fill(0, 3, 1, 2, 0);
       lcd_pd12((t->tm_mon + 1) / 10, x, 3);
   }
   lcd_pd12((t->tm_mon + 1) % 10, x + 11, 3);
   lcd_pd12(13, x + 22, 3);
   lcd_pd12(t->tm_mday / 10, x + 30, 3);
   lcd_pd12(t->tm_mday % 10, x + 41, 3);
   lcd_pd12(13, x + 52, 3);
   lcd_pd12(t->tm_year %100 / 10, x + 60, 3);
   lcd_pd12(t->tm_year % 10, x + 71, 3);
   lcd_fill(x + 82, 3, 2 - x, 2, 0); 

   d = dow[t->tm_wday];
   w = strlen(d) * 6;
   x = (84 - w) >> 1;
   lcd_fill(0, 5, x, 1, 0);
   lcd_print(d, x, 5);
   x += w;
   lcd_fill(x, 5, 84 - x, 1, 0);
}
                                                       //
struct tm ts;                                           // Time structure
time_t tt;                                              // Time in seconds since epoch
                                                       //
                                                       //
#pragma vector = WDT_VECTOR                             // - Watchdog timer interrupt vector 
__interrupt void wdt_isr(void)                          // This interrupt will occur once per second
{                                                       //
#ifdef USE_STDLIB_TIME                                  //
   ++tt;                                               // Increment time_t
#else                                                   //
   rtc_tick(&ts);                                      // Increment tm struct
#endif                                                  //
   __bic_SR_register_on_exit(LPM0_bits);               // Wakeup main code
}                                                       //
                                                       //
void main(void)                                         //
{                                                       //
   WDTCTL = WDTPW | WDTHOLD;                           // Disable watchdog reset
                                                       //
   lcd_init();                                         // Init LCD
   lcd_clear(0);                                       //
   lcd_print("MSP430 RTC", 12, 0);                     //
                                                       //
                                                       // 32 kHz xtal loading
   //BCSCTL3 = XCAP_1;                                 // 6 pF (default)
   BCSCTL3 = XCAP_2;                                   // 10 pF
   //BCSCTL3 = XCAP_3;                                 // 12.5 pF
                                                       //
   WDTCTL = WDTPW | WDTTMSEL | WDTCNTCL | WDTSSEL;     // Use WDT as interval timer
   IE1 |= WDTIE;                                       // Enable WDT interrupt
   _EINT();                                            // Enable interrupts
                                                       //
                                                       // Set initial time - there is no UI for this
   ts.tm_hour = 13;                                        // Hour
   ts.tm_min = 37;                                     // Minute
   ts.tm_sec = 42;                                     // Second
   ts.tm_mon = 3;                                      // Month (0 based!)
   ts.tm_mday = 20;                                    // Day of Month
   ts.tm_year = 2012 - 1900;                           // Year
   ts.tm_wday = 5;                                     // Day of Week - Not used by mktime()
   ts.tm_yday = 0;                                     // Not used by mktime()
   ts.tm_isdst = 0;                                    // DST flag - Not used by rtc_tick()
                                                       //
#ifdef USE_STDLIB_TIME                                  // Convert tm struct to time_t
   tt = mktime(&ts);                                   //
#endif                                                  //
                                                       //
   for(; {                                           // for-ever
#ifdef USE_STDLIB_TIME                                  //
       show_time(localtime(&tt));                      // Convert time_t to tm struct and show on LCD
#else                                                   //
       show_time(&ts);                                 // Show time on LCD
#endif                                                  //
       __bis_SR_register(LPM0_bits + GIE);             // Sleep until WDT interrupt
   }                                                   //
}                                                       //

 

rtc.h

void rtc_tick(struct tm *t);

 

rtc.c - Software model of hardware RTC

#include 

static int is_leap_year(const int y)
{
   if(y & 3) return 0;         // Not divisible by 4
   switch(y % 400) {           // Divisible by 100, but not by 400 (1900, 2100, 2200, 2300, 2500, 2600)
       case 100: case 200: case 300: return 0; 
   }
   return 1;                   // Divisible by 4 and !(100 and !400)
}

void rtc_tick(struct tm *t)
{
   static const signed char dim[12][2] = {             // Number of days in month for non-leap year and leap year
       31,     31,                                     // January
       28,     29,                                     // February
       31,     31,                                     // March
       30,     30,                                     // April
       31,     31,                                     // May
       30,     30,                                     // June
       31,     31,                                     // July
       31,     31,                                     // August
       30,     30,                                     // September
       31,     31,                                     // October
       30,     30,                                     // November
       31,     31                                      // December
   };                                                  //
                                                       //
   if(++t->tm_sec > 59) {                              // Increment seconds, check for overflow
       t->tm_sec = 0;                                  // Reset seconds
       if(++t->tm_min > 59) {                          // Increment minutes, check for overflow
           t->tm_min = 0;                              // Reset minutes
           if(++t->tm_hour > 23) {                     // Increment hours, check for overflow
               t->tm_hour = 0;                         // Reset hours
               ++t->tm_yday;                           // Increment day of year
               if(++t->tm_wday > 6)                    // Increment day of week, check for overflow
                   t->tm_wday = 0;                     // Reset day of week
                                                       // Increment day of month, check for overflow
               if(++t->tm_mday > dim[t->tm_mon][is_leap_year(t->tm_year + 1900)]) {
                   t->tm_mday = 1;                     // Reset day of month
                   if(++t->tm_mon > 11) {              // Increment month, check for overflow
                       t->tm_mon = 0;                  // Reset month
                       t->tm_yday = 0;                 // Reset day of year
                       ++t->tm_year;                   // Increment year
                   }                                   // - year       
               }                                       // - month
           }                                           // - day
       }                                               // - hour
   }                                                   // - minute
}                                                       //

 

lcd.h

typedef enum {
   lcd_command = 0,        // Array of one or more commands
   lcd_data = 1,           // Array of one or more bytes of data
   lcd_data_repeat = 2     // One byte of data repeated
} lcd_cmd_type;

void lcd_send(const unsigned char *cmd, unsigned len, const lcd_cmd_type type);
void lcd_home(void);
void lcd_pos(unsigned char x, unsigned char y);
void lcd_clear(unsigned char x);
void lcd_init(void);
void lcd_fill(unsigned x, unsigned y, unsigned w, unsigned h, unsigned char z);
void lcd_print(const char *s, unsigned x, unsigned y);
void lcd_pd12(unsigned n, unsigned x, unsigned y);

 

lcd.c

#include 
#include "lcd.h"

//static const unsigned TXD = BIT1;
static const unsigned RXD = BIT2;
static const unsigned SWITCH = BIT3;
static const unsigned LCD_CLK = BIT5;
static const unsigned LCD_BACKLIGHT = BIT6;
static const unsigned LCD_DATA = BIT7;

static const unsigned LCD_DC = BIT0;    // PORT2
static const unsigned LCD_CE = BIT1;    // PORT2

void lcd_send(const unsigned char *cmd, unsigned len, const lcd_cmd_type type)
{
   register unsigned mask;

   P2OUT &= ~LCD_CE;
   do {
       mask = 0x0080;
       do {
           if(*cmd & mask) {
               P1OUT &= ~LCD_CLK;
               P1OUT |= LCD_DATA;
           } else {
               P1OUT &= ~(LCD_CLK | LCD_DATA);
           }
           P1OUT |= LCD_CLK;
           mask >>= 1;
       } while(!(mask & 1));
       if(!type) P2OUT &= ~LCD_DC;
       if(*cmd & mask) {
           P1OUT &= ~LCD_CLK;
           P1OUT |= LCD_DATA;
       } else {
           P1OUT &= ~(LCD_CLK | LCD_DATA);
       }
       P1OUT |= LCD_CLK;
       P2OUT |= LCD_DC;
       if(!(type & 2)) ++cmd;
   } while(--len);
   P2OUT |= LCD_CE;
}

static const unsigned char home[] = { 0x40, 0x80 };

void lcd_home(void)
{
   lcd_send(home, sizeof(home), lcd_command);
}

void lcd_pos(unsigned char x, unsigned char y)
{
   unsigned char c[2];
   c[0] = 0x80 | x;
   c[1] = 0x40 | y;
   lcd_send(c, sizeof(c), lcd_command);
}

void lcd_clear(unsigned char x)
{
   lcd_home();
   lcd_send(&x, 504, lcd_data_repeat);
   lcd_home();
}

void lcd_init(void)
{
   static const unsigned char init[] = {
       0x20 + 0x01,    // function set - extended instructions enabled
       0x80 + 64,      // set vop (contrast) 0 - 127
       0x04 + 0x02,    // temperature control
       0x10 + 0x03,    // set bias system
       0x20 + 0x00,    // function set - chip active, horizontal addressing, basic instructions
       0x08 + 0x04     // display control - normal mode
   };

   P1REN = RXD | SWITCH;
   P1DIR = LCD_CLK | LCD_BACKLIGHT | LCD_DATA;
   P1OUT = LCD_CLK | RXD | SWITCH | LCD_BACKLIGHT;

   P2REN = 0;
   P2DIR = LCD_DC | LCD_CE;
   P2OUT = LCD_CE;

   __delay_cycles(20000);
   P2OUT |= LCD_DC;
   __delay_cycles(20000);

   lcd_send(init, sizeof(init), lcd_command);
}

void lcd_fill(unsigned x, unsigned y, unsigned w, unsigned h, unsigned char z)
{
   unsigned yy = y + h;
   unsigned char c[2];
   c[0] = 0x80 | x;
   for(;y < yy; ++y) {
       c[1] = 0x40 | y;
       lcd_send(c, sizeof(c), lcd_command);
       lcd_send(&z, w, lcd_data_repeat);
   }
}

static const unsigned char font6x8[96][5] = {
   0x00, 0x00, 0x00, 0x00, 0x00, // 20  32   
   0x00, 0x00, 0x5F, 0x00, 0x00, // 21  33   !
   0x00, 0x07, 0x00, 0x07, 0x00, // 22  34   "
   0x14, 0x7F, 0x14, 0x7F, 0x14, // 23  35   #
   0x24, 0x2A, 0x7F, 0x2A, 0x12, // 24  36   $
   0x23, 0x13, 0x08, 0x64, 0x62, // 25  37   %
   0x36, 0x49, 0x56, 0x20, 0x50, // 26  38   &
   0x00, 0x08, 0x07, 0x03, 0x00, // 27  39   '
   0x00, 0x1C, 0x22, 0x41, 0x00, // 28  40   (
   0x00, 0x41, 0x22, 0x1C, 0x00, // 29  41   )
   0x2A, 0x1C, 0x7F, 0x1C, 0x2A, // 2A  42   *
   0x08, 0x08, 0x3E, 0x08, 0x08, // 2B  43   +
   0x00, 0x40, 0x38, 0x18, 0x00, // 2C  44   ,
   0x08, 0x08, 0x08, 0x08, 0x08, // 2D  45   -
   0x00, 0x00, 0x60, 0x60, 0x00, // 2E  46   .
   0x20, 0x10, 0x08, 0x04, 0x02, // 2F  47   /
   0x3E, 0x51, 0x49, 0x45, 0x3E, // 30  48   0
   0x00, 0x42, 0x7F, 0x40, 0x00, // 31  49   1
   0x42, 0x61, 0x51, 0x49, 0x46, // 32  50   2
   0x21, 0x41, 0x49, 0x4D, 0x33, // 33  51   3
   0x18, 0x14, 0x12, 0x7F, 0x10, // 34  52   4
   0x27, 0x45, 0x45, 0x45, 0x39, // 35  53   5
   0x3C, 0x4A, 0x49, 0x49, 0x30, // 36  54   6
   0x41, 0x21, 0x11, 0x09, 0x07, // 37  55   7
   0x36, 0x49, 0x49, 0x49, 0x36, // 38  56   8
   0x06, 0x49, 0x49, 0x29, 0x1E, // 39  57   9
   0x00, 0x00, 0x14, 0x00, 0x00, // 3A  58   :
   0x00, 0x00, 0x40, 0x34, 0x00, // 3B  59   ;
   0x00, 0x08, 0x14, 0x22, 0x41, // 3C  60   <
   0x14, 0x14, 0x14, 0x14, 0x14, // 3D  61   =
   0x00, 0x41, 0x22, 0x14, 0x08, // 3E  62   >
   0x02, 0x01, 0x51, 0x09, 0x06, // 3F  63   ?
   0x3E, 0x41, 0x5D, 0x59, 0x4E, // 40  64   @
   0x7C, 0x12, 0x11, 0x12, 0x7C, // 41  65   A
   0x7F, 0x49, 0x49, 0x49, 0x36, // 42  66   B
   0x3E, 0x41, 0x41, 0x41, 0x22, // 43  67   C
   0x7F, 0x41, 0x41, 0x41, 0x3E, // 44  68   D
   0x7F, 0x49, 0x49, 0x49, 0x41, // 45  69   E
   0x7F, 0x09, 0x09, 0x09, 0x01, // 46  70   F
   0x3E, 0x41, 0x49, 0x49, 0x7A, // 47  71   G
   0x7F, 0x08, 0x08, 0x08, 0x7F, // 48  72   H
   0x00, 0x41, 0x7F, 0x41, 0x00, // 49  73   I
   0x20, 0x40, 0x41, 0x3F, 0x01, // 4A  74   J
   0x7F, 0x08, 0x14, 0x22, 0x41, // 4B  75   K
   0x7F, 0x40, 0x40, 0x40, 0x40, // 4C  76   L
   0x7F, 0x02, 0x1C, 0x02, 0x7F, // 4D  77   M
   0x7F, 0x04, 0x08, 0x10, 0x7F, // 4E  78   N
   0x3E, 0x41, 0x41, 0x41, 0x3E, // 4F  79   O
   0x7F, 0x09, 0x09, 0x09, 0x06, // 50  80   P
   0x3E, 0x41, 0x51, 0x21, 0x5E, // 51  81   Q
   0x7F, 0x09, 0x19, 0x29, 0x46, // 52  82   R
   0x26, 0x49, 0x49, 0x49, 0x32, // 53  83   S
   0x01, 0x01, 0x7F, 0x01, 0x01, // 54  84   T
   0x3F, 0x40, 0x40, 0x40, 0x3F, // 55  85   U
   0x1F, 0x20, 0x40, 0x20, 0x1F, // 56  86   V
   0x3F, 0x40, 0x38, 0x40, 0x3F, // 57  87   W
   0x63, 0x14, 0x08, 0x14, 0x63, // 58  88   X
   0x03, 0x04, 0x78, 0x04, 0x03, // 59  89   Y
   0x61, 0x51, 0x49, 0x45, 0x43, // 5A  90   Z
   0x00, 0x7F, 0x41, 0x41, 0x41, // 5B  91   [
   0x02, 0x04, 0x08, 0x10, 0x20, // 5C  92   '\'
   0x00, 0x41, 0x41, 0x41, 0x7F, // 5D  93   ]
   0x04, 0x02, 0x01, 0x02, 0x04, // 5E  94   ^
   0x80, 0x80, 0x80, 0x80, 0x80, // 5F  95   _
   0x00, 0x03, 0x07, 0x08, 0x00, // 60  96   '
   0x20, 0x54, 0x54, 0x54, 0x78, // 61  97   a
   0x7F, 0x28, 0x44, 0x44, 0x38, // 62  98   b
   0x38, 0x44, 0x44, 0x44, 0x28, // 63  99   c
   0x38, 0x44, 0x44, 0x28, 0x7F, // 64  100  d
   0x38, 0x54, 0x54, 0x54, 0x18, // 65  101  e
   0x00, 0x08, 0x7E, 0x09, 0x02, // 66  102  f
   0x18, 0xA4, 0xA4, 0xA4, 0x7C, // 67  103  g
   0x7F, 0x08, 0x04, 0x04, 0x78, // 68  104  h
   0x00, 0x44, 0x7D, 0x40, 0x00, // 69  105  i
   0x00, 0x20, 0x40, 0x40, 0x3D, // 6A  106  j
   0x00, 0x7F, 0x10, 0x28, 0x44, // 6B  107  k
   0x00, 0x41, 0x7F, 0x40, 0x00, // 6C  108  l
   0x7C, 0x04, 0x78, 0x04, 0x78, // 6D  109  m
   0x7C, 0x08, 0x04, 0x04, 0x78, // 6E  110  n
   0x38, 0x44, 0x44, 0x44, 0x38, // 6F  111  o
   0xFC, 0x18, 0x24, 0x24, 0x18, // 70  112  p
   0x18, 0x24, 0x24, 0x18, 0xFC, // 71  113  q
   0x7C, 0x08, 0x04, 0x04, 0x08, // 72  114  r
   0x48, 0x54, 0x54, 0x54, 0x24, // 73  115  s
   0x04, 0x04, 0x3F, 0x44, 0x24, // 74  116  t
   0x3C, 0x40, 0x40, 0x20, 0x7C, // 75  117  u
   0x1C, 0x20, 0x40, 0x20, 0x1C, // 76  118  v
   0x3C, 0x40, 0x30, 0x40, 0x3C, // 77  119  w
   0x44, 0x28, 0x10, 0x28, 0x44, // 78  120  x
   0x4C, 0x90, 0x90, 0x90, 0x7C, // 79  121  y
   0x44, 0x64, 0x54, 0x4C, 0x44, // 7A  122  z
   0x00, 0x08, 0x36, 0x41, 0x00, // 7B  123  {
   0x00, 0x00, 0x77, 0x00, 0x00, // 7C  124  |
   0x00, 0x41, 0x36, 0x08, 0x00, // 7D  125  }
   0x02, 0x01, 0x02, 0x04, 0x02, // 7E  126  ~
   0x00, 0x06, 0x09, 0x09, 0x06, // 7F  127 degrees
};

void lcd_print(const char *s, unsigned x, unsigned y)
{
   unsigned char c[2];
   c[0] = 0x80 | x;
   c[1] = 0x40 | y;
   lcd_send(c, sizeof(c), lcd_command);
   while(*s) {
       lcd_send(&font6x8[*s - 32][0], 5, lcd_data);
       lcd_send(&font6x8[0][0], 1, lcd_data);
       ++s;
   }
}

static const unsigned char num11x16[19][11 * 2] = {
   0x00,0xF0,0xFC,0xFE,0x06,0x02,0x06,0xFE,0xFC,0xF0,0x00, // 0
   0x00,0x07,0x1F,0x3F,0x30,0x20,0x30,0x3F,0x1F,0x07,0x00,
   0x00,0x00,0x08,0x0C,0xFC,0xFE,0xFE,0x00,0x00,0x00,0x00, // 1
   0x00,0x20,0x20,0x20,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x00,
   0x00,0x0C,0x0E,0x06,0x02,0x02,0x86,0xFE,0x7C,0x38,0x00, // 2
   0x00,0x30,0x38,0x3C,0x36,0x33,0x31,0x30,0x30,0x38,0x00,
   0x00,0x0C,0x0E,0x86,0x82,0x82,0xC6,0xFE,0x7C,0x38,0x00, // 3
   0x00,0x18,0x38,0x30,0x20,0x20,0x31,0x3F,0x1F,0x0E,0x00,
   0x00,0x00,0xC0,0x20,0x18,0x04,0xFE,0xFE,0xFE,0x00,0x00, // 4
   0x00,0x03,0x02,0x02,0x02,0x22,0x3F,0x3F,0x3F,0x22,0x02,
   0x00,0x00,0x7E,0x7E,0x46,0x46,0xC6,0xC6,0x86,0x00,0x00, // 5
   0x00,0x18,0x38,0x30,0x20,0x20,0x30,0x3F,0x1F,0x0F,0x00,
   0x00,0xC0,0xF0,0xF8,0xFC,0x4C,0xC6,0xC2,0x82,0x00,0x00, // 6
   0x00,0x0F,0x1F,0x3F,0x30,0x20,0x30,0x3F,0x1F,0x0F,0x00,
   0x00,0x06,0x06,0x06,0x06,0x06,0xC6,0xF6,0x3E,0x0E,0x00, // 7
   0x00,0x00,0x00,0x30,0x3C,0x0F,0x03,0x00,0x00,0x00,0x00,
   0x00,0x38,0x7C,0xFE,0xC6,0x82,0xC6,0xFE,0x7C,0x38,0x00, // 8
   0x00,0x0E,0x1F,0x3F,0x31,0x20,0x31,0x3F,0x1F,0x0E,0x00,
   0x00,0x78,0xFC,0xFE,0x86,0x02,0x86,0xFE,0xFC,0xF8,0x00, // 9
   0x00,0x00,0x00,0x21,0x21,0x31,0x1D,0x1F,0x0F,0x03,0x00,
   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 
   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
   0x00,0x70,0x70,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // :
   0x00,0x0E,0x0E,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // .
   0x00,0x38,0x38,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
   0x00,0x00,0x00,0x00,0xC0,0x30,0x0C,0x00,0x00,0x00,0x00, // /
   0x00,0x30,0x0C,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
   0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, // -
   0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
   0x00,0x18,0x3C,0x66,0x66,0x3C,0x18,0x00,0x00,0x00,0x00, // 
   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
   0xF0,0xF8,0x0C,0x06,0x02,0x02,0x02,0x02,0x0E,0x0C,0x00, // C
   0x03,0x07,0x0C,0x18,0x10,0x10,0x10,0x10,0x1C,0x0C,0x00,
   0xFE,0xFE,0x42,0x42,0x42,0x42,0x42,0x42,0x00,0x00,0x00, // F
   0x1F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
   0xFE,0xFE,0x40,0xE0,0xB0,0x18,0x0C,0x06,0x02,0x00,0x00, // K
   0x1F,0x1F,0x00,0x00,0x01,0x03,0x06,0x0C,0x18,0x10,0x00
};

void lcd_pd12(unsigned n, unsigned x, unsigned y)
{
   unsigned char c[2];
   c[0] = 0x80 | x;
   c[1] = 0x40 + y; lcd_send(c, 2, lcd_command);   lcd_send(num11x16[n], 11, lcd_data);
   c[1] = 0x41 + y; lcd_send(c, 2, lcd_command);   lcd_send(num11x16[n] + 11, 11, lcd_data);
}

Link to post
Share on other sites
  • 4 months later...

I'm looking into using this on a clock project - would it be easier to re-write a version that uses BCD, or would the overhead (extra if statements + variables) be about the same as converting the values for display? I'm guessing since you're converting to string for this example - you decided to convert after-the-fact.

Link to post
Share on other sites

BCD is very efficient for display. The TI sample code uses BCD and is written in assembly to make use of the BCD math instructions. It is possible to do some limited BCD math in C using a few intrinsic functions.

 

 

*Warning: untested code*

struct btm                                              // BCD time structure
{
   uint16_t sec;
   uint16_t min;
   uint16_t hour;
   uint16_t wday;
   uint16_t mday;
   uint16_t yday;
   uint16_t month;
   uint16_t year;
}

unsigned is_leap_year_bcd(unsigned year)
{
   if(year & 3) return 0;
   switch(year & 0x03FF) { 
       case 0x0100: case 0x0200: case 0x0300: return 0; 
   }
   return 1;
}

void rtc_tick_bcd(struct btm *t)
{
   static const uint8_t dim[12][2] = {                 // Number of days in month for non-leap year and leap year
       0x31,   0x31,                                   // 00 January
       0x28,   0x29,                                   // 01 February
       0x31,   0x31,                                   // 02 March
       0x30,   0x30,                                   // 03 April
       0x31,   0x31,                                   // 04 May
       0x30,   0x30,                                   // 05 June
       0x31,   0x31,                                   // 06 July
       0x31,   0x31,                                   // 07 August
       0x30,   0x30,                                   // 08 September
       0x31,   0x31,                                   // 09 October
       0,      0,                                      // 0A
       0,      0,                                      // 0B
       0,      0,                                      // 0C
       0,      0,                                      // 0D
       0,      0,                                      // 0E
       0,      0,                                      // 0F
       0x30,   0x30,                                   // 10 November
       0x31,   0x31                                    // 11 December
   };                                                  //
                                                       //
   t->sec = _bcd_add_short(t->sec, 1);                 // Increment seconds
   if(t->sec > 0x59) {                                 // Check for overflow
       t->sec = 0;                                     // Reset seconds
       t->min =  _bcd_add_short(t->min, 1);            // Increment minutes
       if(t->min > 0x59) {                             // Check for overflow
           t->min = 0;                                 // Reset minutes
           t->hour = _bcd_add_short(t->hour, 1);       // Increment hours
           if(t->hour > 0x23) {                        // Check for overflow
               t->hour = 0;                            // Reset hours
               t->yday = _bcd_add_short(t->yday, 1);   // Increment day of year
               t->wday = _bcd_add_short(t->wday, 1);   // Increment day of week
               if(t->wday > 0x06)                      // Check for overflow
                   t->wday = 0;                        // Reset day of week
               t->mdat = _bcd_add_short(t->mday, 1);   // Increment day of month, check for overflow                                                       
               if(t->mday > dim[t->mon][is_leap_year_bcd(t->year)]) {
                   t->mday = 0x01;                     // Reset day of month
                   t->mon = _bcd_add_short(t->mon, 1); // Increment month
                   if(t->mon > 0x11) {                 // Check for overflow
                       t->mon = 0;                     // Reset month
                       t->yday = 0;                    // Reset day of year
                       t->year = _bcd_add_short(t->year, 1); // Increment year
                   }                                   // - year       
               }                                       // - month
           }                                           // - day
       }                                               // - hour
   }                                                   // - minute
}                                                       //
												//

Link to post
Share on other sites

Thanks! I wasn't aware of the BCD function. I'll look at implementing that logic - looks nice and clean to me.

 

One question - to convert the hour from 24h to 12h AM/PM, I would need subtraction and there doesn't appear to be a BCD subtract function. I'm guessing I'd need to write my own function?

Link to post
Share on other sites
  • 10 months later...

 Hello, Possum!

 

 I'm Begginer with mcu's and I have the Launchpad Msp430G2553 and 1 eZ430 (Msp430F2013).

 

So I am starting one course but, the examples showed make use of the msp430xG46x, and that board have Basic Timer and RTC by Hardware.. however  my Launchpad don't have this.

 

Can you help-me with a code which just count a time ( can be with interrupt), after to squeeze one botton?

 

I hope which you have understood my ask, I'm Brazilian and my english is  the worst.

 

 

 

                 Thank You very much! Hug!

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...