// THIS PROGRAM IS PROVIDED "AS IS". TI MAKES NO WARRANTIES OR
// REPRESENTATIONS, EITHER EXPRESS, IMPLIED OR STATUTORY, 
// INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS 
// FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR 
// COMPLETENESS OF RESPONSES, RESULTS AND LACK OF NEGLIGENCE. 
// TI DISCLAIMS ANY WARRANTY OF TITLE, QUIET ENJOYMENT, QUIET 
// POSSESSION, AND NON-INFRINGEMENT OF ANY THIRD PARTY 
// INTELLECTUAL PROPERTY RIGHTS WITH REGARD TO THE PROGRAM OR 
// YOUR USE OF THE PROGRAM.
//
// IN NO EVENT SHALL TI BE LIABLE FOR ANY SPECIAL, INCIDENTAL, 
// CONSEQUENTIAL OR INDIRECT DAMAGES, HOWEVER CAUSED, ON ANY 
// THEORY OF LIABILITY AND WHETHER OR NOT TI HAS BEEN ADVISED 
// OF THE POSSIBILITY OF SUCH DAMAGES, ARISING IN ANY WAY OUT 
// OF THIS AGREEMENT, THE PROGRAM, OR YOUR USE OF THE PROGRAM. 
// EXCLUDED DAMAGES INCLUDE, BUT ARE NOT LIMITED TO, COST OF 
// REMOVAL OR REINSTALLATION, COMPUTER TIME, LABOR COSTS, LOSS 
// OF GOODWILL, LOSS OF PROFITS, LOSS OF SAVINGS, OR LOSS OF 
// USE OR INTERRUPTION OF BUSINESS. IN NO EVENT WILL TI'S 
// AGGREGATE LIABILITY UNDER THIS AGREEMENT OR ARISING OUT OF 
// YOUR USE OF THE PROGRAM EXCEED FIVE HUNDRED DOLLARS 
// (U.S.$500).
//
// Unless otherwise stated, the Program written and copyrighted 
// by Texas Instruments is distributed as "freeware".  You may, 
// only under TI's copyright in the Program, use and modify the 
// Program without any charge or restriction.  You may 
// distribute to third parties, provided that you transfer a 
// copy of this license to the third party and the third party 
// agrees to these terms by its first use of the Program. You 
// must reproduce the copyright notice and any other legend of 
// ownership on each copy or partial copy, of the Program.
//
// You acknowledge and agree that the Program contains 
// copyrighted material, trade secrets and other TI proprietary 
// information and is protected by copyright laws, 
// international copyright treaties, and trade secret laws, as 
// well as other intellectual property laws.  To protect TI's 
// rights in the Program, you agree not to decompile, reverse 
// engineer, disassemble or otherwise translate any object code 
// versions of the Program to a human-readable form.  You agree 
// that in no event will you alter, remove or destroy any 
// copyright notice included in the Program.  TI reserves all 
// rights not specifically granted under this license. Except 
// as specifically provided herein, nothing in this agreement 
// shall be construed as conferring by implication, estoppel, 
// or otherwise, upon you, any license or other right under any 
// TI patents, copyrights or trade secrets.
//
// You may not use the Program in non-TI devices.

//------------------------------------------------------------------------------
// MSP430F169 Voice Demo
//
// Uses the new DMA and DAC12 modules to perform autonomous speech recording
// and playback. CPU runs from DCO at the default speed of ~800KHz.
//
//
//   +--------------------+
//   |                    |
//   |               ADC0 +-------< input from mic pre-amplifier
//   |               DAC0 +-------> output to audio output stage
//   |               P1.0 +-------> LED #1
//   |               P1.1 +-------> LED #2
//   |  MSP430F169   P1.3 +-------> input stage shutdown (0 = shutdown)
//   |               P1.4 +-------> output stage shutdown (1 = shutdown)
//   |               P1.6 +-------< KEY #1
//   |               P1.7 +-------< KEY #2
//   |               P6.2 +-------> charge pump snooze mode (0 = snooze)
//   |                    |
//   +--------------------+
//
//
// Andreas Dannenberg
// MSP430 Applications
// Texas Instruments Inc.
// July 2003
//
// History:  V1.01 (07/18/03)
//           - ADC12IFGx used as DMA trigger instead of Timer_B.CCIFG2
//           - lower sample rate for longer speech duration
//           V1.00 (07/15/03)
//           - initial release
//------------------------------------------------------------------------------
#include <msp430x16x.h>
//------------------------------------------------------------------------------
#define Memstart             0x1400         // memory range to be filled with
#define Memend               0xfe00         // sampled data
// ATTN: Care must be taken not to conflict with memory used by the program
//       code. Also both addresses need to be integer multipliers of 0x0200
//       due to the flash memory segmenting.

#define SamplePrd            90             // record&playback sample period
                                            // SampleR = 800,000 / SamplePrd

void Init_Sys(void);                        // function prototypes
void Record(void); 
void Playback(void);
void Erase(void);
void Delay(void);
//-----------------------------------------------------------------------------
void main(void)
{
  Init_Sys();                               // initialize system
  
  // power-down external hardware
  P1OUT &= ~0x08;                           // disable audio input stage
  P1OUT |= 0x10;                            // disable audio output stage
  P6OUT &= ~0x04;                           // enable charge pump snooze mode

  while (1)                                 // repeat forever
  {
    // wait for key-press event, hold CPU in low-power mode
    P1IFG &= ~0xc0;                         // clear all button interrupt flags
    P1IE |= 0xc0;                           // enable int for buttons
    _EINT();                                // enable interrupts
    _BIS_SR(LPM3_bits);                     // enter LPM3
    _DINT();                                // disable interrupts
    P1IE &= ~0xc0;                          // disable interrupts for buttons

    // process key-press event
    if (!(P1IN & 0x40))                     // record button pressed?
      Record();                             
    else                                    // no, -> must be playback button
      Playback();                           
  }
}
//-----------------------------------------------------------------------------
// record audio data and store in Flash memory using
// ADC12 module and the integrated DMA controller
//-----------------------------------------------------------------------------
void Record(void)
{
  // power-up external hardware
  P1OUT |= 0x09;                            // LED#1 & audio input stage on
  P6OUT |= 0x04;                            // disable charge pump snooze mode
                  
  // setup ADC12 module
  ADC12CTL0 = ADC12ON;                      // turn on ADC12, S&H in sample
                                            // ADC12 Clock=ADC12OSC
  ADC12CTL1 = SHS_3 + CONSEQ_2;             // S&H src select: Timer_B.OUT1
                                            // rep. single channel
  ADC12IFG = 0x00;                          // clear ADC12 interrupt flag reg
  ADC12CTL0 |= ENC;                         // enable conversion
                                            
  // Steps executed for automated ADC12-to-Flash transfer
  // everything without CPU intervention
  //
  // (1) Timer_B.OUT1 triggers ADC12 conversion
  // (2) ADC12IFGx triggers DMA transfer
  // (3) Goto (1) until DMA0SZ = 0

  // setup Timer_B for recording
  TBCTL = TBSSEL1;                          // use SMCLK as Timer_B source
  TBR = 0;
  TBCCR0 = SamplePrd;                       // initialize TBCCR0
  TBCCR1 = SamplePrd - 20;                  // trigger for ADC12 SC
  TBCCTL1 = OUTMOD_7;                       // reset OUT1 on EQU1, set on EQU0

  // setup DMA for recording
  DMA0SA = ADC12MEM0_;                      // src address = ADC12 module
  DMA0DA = Memstart;                        // dst address = Flash memory
  DMA0SZ = (Memend - Memstart) >> 1;        // size in words
  DMACTL0 = DMA0TSEL2 + DMA0TSEL1;          // ADC12IFGx triggers DMA0
  DMA0CTL = DMADSTINCR1 + DMADSTINCR0 + DMAEN + DMAIE;
                                            // single transfer,
                                            // increment dest. address,
                                            // leave source address unchanged,
                                            // src and dst are words size,
                                            // edge sensitive DMA trigger,
                                            // enable DMA, enable DMA int

  // setup and erase Flash memory
  // (Rem.: This time is also used to wait for
  //        the voltages getting stabilized)
  FCTL2 = FWKEY + FSSEL_2 + FN0;            // clk src = SMCLK / 2 (~440KHz)
  FCTL3 = FWKEY;                            // unlock Flash memory for write
  Erase();                                  // call Flash erase subroutine
  FCTL1 = FWKEY + WRT;                      // enable Flash write for recording
           
  // start recording
  P1OUT |= 0x01;                            // LED#1 on
  TBCTL |= MC0;                             // start Timer_B in UP mode
                                            // (counts up to TBCL0)

  // activate LPM during DMA recording, wake-up when finished
  _EINT();                                  // enable interrupts
  _BIS_SR(LPM0);                            // enter LPM0
  _DINT();                                  // disable interrupts
  
  // deactivate Flash memory write access
  FCTL1 = FWKEY;                            // disable Flash write
  FCTL3 = FWKEY + LOCK;                     // lock Flash memory     
           
  // power-down MSP430 modules
  ADC12CTL1 &= ~CONSEQ_2;                   // stop conversion immidiately
  ADC12CTL0 &= ~ENC;                        // disable ADC12 conversion
  ADC12CTL0 = 0;                            // switch off ADC12 & ref voltage
  TBCTL = 0;                                // disable Timer_B
 
  // power-down external hardware
  P1OUT &= ~0x09;                           // disable LED#1 & audio input stage
  P6OUT &= 0x04;                            // enable charge pump snooze mode
}
//-----------------------------------------------------------------------------           
// playback audio data stored in Flash memory using the
// integrated DMA controller and DAC12 module
//-----------------------------------------------------------------------------
void Playback(void)
{
  // power-up external hardware
  P1OUT |= 0x02;                            // LED#2 on
  P1OUT &= ~0x10;                           // enable audio output stage
  P6OUT |= 0x04;                            // disable charge pump snooze mode

  // setup DAC12 module
  ADC12CTL0 = REFON + REF2_5V;              // ADC12 ref needed for DAC12
  DAC12_0CTL = DAC12IR + DAC12AMP_7 + DAC12LSEL_3 + DAC12ENC;
                                            // configure DAC12
                                            // latch data on pos. Timer_B.OUT2

  Delay();                                  // wait until voltages have stab.

  // Steps executed for automated Flash-to-DAC12 transfer
  // everything without CPU intervention
  //
  // (1) Timer_B.CCIFG2 triggers DMA transfer
  // (2) Timer_B.OUT2 triggers DAC12 data latch
  // (3) Goto (1) until DMA0SZ = 0

  // setup Timer_B for playback
  TBCTL = TBSSEL1;                          // use SMCLK as Timer_B source
  TBCCR0 = SamplePrd;                       // initialize TBCCR0 w/ sample prd
  TBCCR2 = SamplePrd >> 1;                  // EQU2 will trigger DMA
  TBCCTL2 = OUTMOD_7;                       // reset OUT2 on EQU2, set on EQU0

  // setup DMA for playback
  DMA0SA = Memstart;                        // src address = Flash memory
  DMA0DA = DAC12_0DAT_;                     // dst address = DAC12 module
  DMA0SZ = (Memend - Memstart) >> 1;        // DMA block size
  DMACTL0 = DMA0TSEL1;                      // Timer_B.CCIFG2 triggers DMA0
  DMA0CTL = DMASRCINCR1 + DMASRCINCR0 + DMAEN + DMAIE;
                                            // single transfer,
                                            // increment source address,
                                            // leave dest. address unchanged,
                                            // src and dst are words size,
                                            // edge sensitive DMA trigger,
                                            // enable DMA, enable DMA int

  // start playback
  TBCTL |= MC0;                             // start TimerB in UP mode
                                            // (counts up to TBCL0)
           
  // activate LPM during DMA playback, wake-up when finished
  _EINT();                                  // enable interrupts
  _BIS_SR(LPM0);                            // enter LPM0
  _DINT();                                  // disable interrupts
                                          
  // power-down MSP430 modules
  TBCTL = 0;                                // disable Timer_B
  ADC12CTL0 = 0;                            // switch off ADC12 ref voltage
  DAC12_0CTL &= ~DAC12ENC;                  // disable DAC12 conversion
  DAC12_0CTL = 0;                           // switch off DAC12
           
  // power-down external hardware
  P1OUT |= 0x10;                            // disable audio output stage
  P6OUT &= ~0x04;                           // enable charge pump snooze mode
  P1OUT &= ~0x02;                           // LED#2 off
}
//-----------------------------------------------------------------------------
// setup system and peripherals
//-----------------------------------------------------------------------------
void Init_Sys(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // stop watchdog timer

  P1OUT = 0x00;                             // clear P1 output register
  P1DIR = 0x3f;                             // P1.6/P1.7 inp, all other outp
  P1IES = 0xc0;                             // H->L edge detect for buttons
  P2OUT = 0x00;                             // clear P2 output register
  P2DIR = 0xff;                             // unused pins as outp  
  P3OUT = 0x00;                             // clear P3 output register
  P3DIR = 0xf5;                             // all but I2C pins to output
  P4OUT = 0x00;                             // clear P4 output register
  P4DIR = 0xff;                             // unused pins as outp  
  P5OUT = 0x00;                             // clear P5 output register
  P5DIR = 0xff;                             // unused pins as outp  
  P6OUT = 0x00;                             // clear P6 output register
  P6SEL = 0xc1;                             // select ADC12 A0 inp, DAC0&1 outp
  P6DIR = 0xff;                             // unused pins as output                                                                    
}
//-----------------------------------------------------------------------------
// erase Flash memory for new recording
//-----------------------------------------------------------------------------
void Erase(void)
{
  unsigned int *pMemory = (unsigned int *)Memstart;
                                            // start of record memory array
  
  while (FCTL3 & BUSY);                     // loop till not busy
  
  do
  {
    if ((unsigned int)pMemory & 0x1000)     // use bit 12 to toggle LED#1
      P1OUT |= 0x01;
    else
      P1OUT &= ~0x01;    
    
    FCTL1 = FWKEY + ERASE;
    *pMemory = 0x00;                        // dummy write to activate
                                            // segment erase
    while (FCTL3 & BUSY);                   // loop till not busy
    pMemory += 0x0100;                      // point to next segment
  } while (pMemory < (unsigned int *)Memend);
}  
//-----------------------------------------------------------------------------
// software delay, ~100,000 CPU cycles
//-----------------------------------------------------------------------------
void Delay(void)
{
  unsigned int i;
  
  for (i = 0; i < 0x3fff; i++);
}
//----------------------------------------------------------------------------
// PORT1 interrupt handler
//-----------------------------------------------------------------------------
void PORT1ISR(void) __interrupt[PORT1_VECTOR] 
{
  P1IFG &= ~0xc0;                           // clear all button interrupt flags
  _BIC_SR_IRQ(LPM3_bits);                   // exit LPM3 on reti
}
//-----------------------------------------------------------------------------
//DAC12, DMA interrupt handler
//-----------------------------------------------------------------------------
void DACDMAISR(void) __interrupt[DACDMA_VECTOR] 
{
  DMA0CTL &= ~DMAIFG;                       // clear DMA0 interrupt flag
  _BIC_SR_IRQ(LPM0_bits);                   // exit LPM0 on reti
}
