/* --------------------------------------------------------------------------
Name:       LED_Interrupt.c
Function:   Uses interrupts to flash the LED
Versions:
   1.0   2/15/2007   L Simone.  
         Compiled for FET.  Use Interrupt to flash LED on and off.
   1.1   2/4/2007    L Simone.
         Add logic to vary the flashing cycle from .2 to 10 Hz.  Based on original
         code, just using the interrupt to toggle the LED port pin.

         Knowns:  a) 1 TACCR0 tick = 1 usec
                  b) TACCR0 is 16 bit counter - max value is 65535
         Find timer reload values for the max and min (ranges)

         .2Hz = period of 5 seconds.  
            .2Hz: (5 sec)*(10^6 us/1 sec) * (1 tick/ 1 usec) = 5E6 - 5,000,000 ticks
         10Hz = period of .1 seconds
            10Hz: (.1sec)*(10^6 us/1 sec) * (1 tick/ 1 usec) = 100,000 ticks
                  
         Since just using TACCR0 will only allow us to delay each interrupt
         to 65535 usec = 65.535 msec, we need a way to count larger values.  
         So, make each interrupt a nice value like 10000 or 50000 for easier math
         and then use an intermediate variable, Interrupt_ctr, to count 
         numbers of interrupts. 
         Using an Excel spreadsheet, optimized a reload value of 5000.

         Also, since the Interrupt just toggles the LED on and off, we need
         to divide the # of interrupts in half because it takes two interrupt cycles
         to complete one LED ON/OFF cycle.

         .2 Hz: 5,000,000/5,000 = 1000 ...halve that ... 500
         10 Hz: 100,000/5,000   = 20   ...halve that ... 10
         
Using TACCR0 Reload of 5000:
              
         Hz	Reload,	Interrupt Loops,     User_Freq_ctr
         0.2	5000000	      1000	          500
         0.3	3333333	      667	          333
         0.5	2000000	      400	          200
         1	1000000	      200	          100
         2	500000	      100	          50
         3	333333	      67	          33
         4	250000	      50	          25
         5	200000	      40	          20
         6	166667	      33	          17
         7	142857	      29	          14
         8	125000	      25	          13
         9	111111	      22	          11
         10	100000	      20	          10
 
   1.5   2/27/2008    L Simone.
         Add ability to specify a duty cycle for the LED ON time rather than just
         toggling the LED with the ^= operator.

         Note!!! User_Freq_Ctr is a reload based on the fact that the LED
         was being toggled, and represents half of the total period needed.  
         For this version, the User_period is 2 * User_Freq_ctr from the table.

----------------------------------------------------------------------------- */
#include <msp430x20x3.h>

#define  LED_PIN   BIT0           /* LED is on Port 1, bit 1 (aka BIT0)  */
#define  LED_ON    BIT0           /* LED is located at BIT0 on Port 1. 1 = ON  */
#define  LED_OFF  (~BIT0)

/* Redeclarations       */
extern void initialize_hardware(void);
extern void initialize_TimerA0(void);
extern void Turn_LED_ON(void);
extern void Turn_LED_OFF(void);

/* Global Variables */
unsigned int User_Freq_ctr = 500;   /* Table value corresponding to a selected frequency */
unsigned int User_period;
unsigned int User_duty_cycle = 75;  /* This is a percentage ON time for LED [0 - 100%] */
unsigned int TimerA0_reload = 5000; /* Reload value for constant interrupt period   */
unsigned int Interrupt_Ctr = 0;     /* Counts the number of interrupts that have occurred */
unsigned int LED_ON_time;           /* This is the ON TIME in counts */
unsigned int LED_OFF_time;          /* This is the OFF TIME in counts */ 


void main(void)
{
  initialize_hardware();         /* Initialize ports, etc.     */
  initialize_TimerA0();          /* Configure TIMER_A0 to control LED flashing */

  /* Compute variables which define the LED ON and OFF time based on the duty cycle.
  Note: analysis of appropriate variable length - Duty_cycle max is 100, User_Freq_ctr max
  is 500 but we multiply it by 2 to get the full period interval. If we manipulate the
  math operations as shown, it can be contained less than 65535 in an unsigned long.*/ 
  
  User_period = User_Freq_ctr * 2;
  LED_ON_time = (User_duty_cycle * (User_period/2))/(100/2);
  LED_OFF_time = User_period - LED_ON_time;
  Interrupt_Ctr = User_period - 1;        /* initialization to cause the LED to turn on first time thru */

  
  /* Now that everything is ready to go, enable interrupts!  */
   __enable_interrupt();

   
  /* This is our endless main loop that contains other processing
     code that is not relevant to the flashing ISR.  Make it whatever. */
  do
  { 
     ;
  }
  while (1);
  
}

/* -------------------------------------------------------------
Name:       initialize_hardware()
Function:   Initialize all hardware settings
---------------------------------------------------------------- */
void initialize_hardware(void)
{
  WDTCTL = WDTPW + WDTHOLD;                  // Stop watchdog timer
  P1DIR |= 0x01;                            // Set P1.0 to output direction
}

/* -------------------------------------------------------------
Name:       initialize_TimerA0()
Function:   Initialize Timer in continuous mode to control flashing
            of the LED.  
---------------------------------------------------------------- */
void initialize_TimerA0(void)
{
   /* In the TimerA control register, configure the timer to use
   the subsystem clock source (SMCLK), and to use the continuous
   mode (Mode 2). */
   TACTL = TASSEL_2 + MC_2;            

   /* Enable TimerA0 interrupt via TimerA0 Control Register.
      This means an interrupt will occur when timer counts up and
      reaches value in TACCR0 */
   TACCTL0 = CCIE;                   

   /* Load an initial reload value into TACCR0 – Capture/Compare Register
   also known as the "period" register.  Period = ON time + OFF time. */
   TACCR0 = TimerA0_reload;          
}

/* -------------------------------------------------------------
Name:       Timer_A()
Function:   Timer A0 interrupt service routine
---------------------------------------------------------------- */
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
   Interrupt_Ctr++;

   if (Interrupt_Ctr == User_period)
   {
      Interrupt_Ctr = 0;
      Turn_LED_ON();
   }   
   if (Interrupt_Ctr == LED_ON_time)
   {
      Turn_LED_OFF();
   }

   TACCR0 += TimerA0_reload;      /* Reload timer for next interrupt  */
}

/* --------------------------------------------------------------------------
Name:       Turn_LED_ON()
Function:   Turn the LED ON.
----------------------------------------------------------------------------- */
void Turn_LED_ON(void)
{
  P1OUT |= LED_ON;
}

/* --------------------------------------------------------------------------
Name:       Turn_LED_OFF()
Function:   Turn the LED OFF.
----------------------------------------------------------------------------- */
void Turn_LED_OFF(void)
{
  P1OUT &= LED_OFF;
}

