// Olimex MSP430-449STK Demonstration Program
// Copyright (c) 2002, 2003 Rowley Associates Limited

// This program drives an incrementing count on the LCD
// off the basic timer interrupt.

#include <msp430x44x.h>
#include <string.h>

// Segment drive lines
#define SEGMENT_LEADING_1  24
#define SEGMENT_MINUS      25
#define SEGMENT_SINE_WAVE  26
#define SEGMENT_UP_ARROW   27
#define SEGMENT_PLUS_COLON 28
#define SEGMENT_BAT        29
#define SEGMENT_COLON      30

// 7-segment definitions.
#define _A  (1<<0)   //      AAA
#define _B  (1<<1)   //     F   B
#define _C  (1<<2)   //     F   B
#define _D  (1<<3)   //      GGG
#define _E  (1<<4)   //     E   C
#define _F  (1<<5)   //     E   C
#define _G  (1<<6)   //      DDD
#define _DP (1<<7)

// Map hex digit to logical segment
static const unsigned char vi_322_digits[16] =
{
  _A | _B | _C | _D | _E | _F,       // 0
  _B | _C,                           // 1
  _A | _B | _D | _E | _G,            // 2
  _A | _B | _C | _D | _G,            // 3
  _B | _C | _F | _G,                 // 4
  _A | _F | _G | _C | _D,            // 5
  _A | _F | _G | _C | _D | _E,       // 6
  _F | _A | _B | _C,                 // 7
  _A | _B | _C | _D | _E | _F | _G,  // 8
  _A | _B | _G | _C | _D | _F,       // 9
  _A | _B | _C | _E | _G | _F,       // A
  _C | _D | _E | _F | _F | _G,       // b
  _A | _D | _E | _F,                 // C
  _B | _C | _D | _E | _G,            // d
  _A | _D | _E | _F | _G,            // E
  _A | _E | _F | _G                  // F
};

static void
sblcd_init(void)
{
  // Setup LCD
  LCDCTL = LCDON | LCDSTATIC | LCDP0 | LCDP2;
  
  // Clear display.
  memset(LCDMEM, 0, 20);
}

void
vi_322_show_digit(int digit, int pos)
{
  unsigned char c;
  char *p = LCDMEM + pos*4;
  c = vi_322_digits[digit];
  p[0] = ((c & _A) << 0) | ((c & _B) << 3);
  p[1] = ((c & _C) >> 2) | ((c & _D) << 1);
  p[2] = ((c & _E) >> 4) | ((c & _F) >> 1);
  p[3] = p[3] & 0xf0 | ((c & _G) >> 6);
}

void
vi_322_segment_on(int segment)
{
  LCDMEM[segment >> 1] |= (segment & 1) ? 0x10 : 0x01;
}

void
vi_322_segment_off(int segment)
{
  LCDMEM[segment >> 1] &= (segment & 1) ? ~0x10 : ~0x01;
}

void
vi_322_segment_toggle(int segment)
{
  LCDMEM[segment >> 1] ^= (segment & 1) ? 0x10 : 0x01;
}

void
vi_322_blank_digit(int pos)
{
  char *p = LCDMEM + pos*4;
  p[0] = 0;
  p[1] = 0;
  p[2] = 0;
  p[3] &= 0xf0;
}

void
vi_322_show_number(int i)
{
  if (i >= 1000)
    vi_322_segment_on(SEGMENT_LEADING_1);
  else
    vi_322_segment_off(SEGMENT_LEADING_1);
  
  if (i >= 100)
    vi_322_show_digit(i / 100 % 10, 2);
  else
    vi_322_blank_digit(2);
    
  if (i >= 10)
    vi_322_show_digit(i / 10 % 10, 1);
  else
    vi_322_blank_digit(1);

  vi_322_show_digit(i % 10, 0);
}

static void
timer_interrupt(void) __interrupt[BASICTIMER_VECTOR]
{
  // Wake processor when delay expires.
  static int x = 0;
  vi_322_show_number(++x);
}
  
void
main(void)
{
  // Stop watchdog.
  WDTCTL = WDTHOLD | WDTPW;

  // Set FLL.
  FLL_CTL0 |= XCAP14PF;

  // Set up basic timer for LCD
  BTCTL = BT_fLCD_DIV64;

  // Set up LCD.
  sblcd_init();

  // Turn on Basic Timer interrupts
  IE2 |= BTIE;

  // Start things up  
  sblcd_init();
  _EINT();

  for (;;)
    LPM3;
}
        