www.pudn.com > slaa274.zip > oximeter_ext_probe_1.c, change:2005-10-24,size:29505b


//***************************************************************************** 
// 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. 
//***************************************************************************** 
//***************************************************************************** 
//    MSP430FG437 based pulse oximeter demonstration 
//    V. Chan and S. Underwood 
//    Texas Instruments Hong Kong Ltd. 
//    May 2005 
//***************************************************************************** 
 
#include  <msp430xG43x.h> 
#include "stdint.h" 
 
#define seg_a       0x01 
#define seg_b       0x02 
#define seg_c       0x10 
#define seg_d       0x04 
#define seg_e       0x80 
#define seg_f       0x20 
#define seg_g       0x08 
#define seg_h       0x40 
 
#define NUM_0   (seg_a | seg_b | seg_c | seg_d | seg_e | seg_f) 
#define NUM_1   (seg_b | seg_c) 
#define NUM_2   (seg_a | seg_b | seg_d | seg_e | seg_g) 
#define NUM_3   (seg_a | seg_b | seg_c | seg_d | seg_g) 
#define NUM_4   (seg_b | seg_c | seg_f | seg_g) 
#define NUM_5   (seg_a | seg_c | seg_d | seg_f | seg_g) 
#define NUM_6   (seg_a | seg_c | seg_d | seg_e | seg_f | seg_g) 
#define NUM_7   (seg_a | seg_b | seg_c) 
#define NUM_8   (seg_a | seg_b | seg_c | seg_d | seg_e | seg_f | seg_g) 
#define NUM_9   (seg_a | seg_b | seg_c | seg_d | seg_f | seg_g) 
#define NUM_A   (seg_a | seg_b | seg_c | seg_e | seg_f | seg_g) 
#define NUM_B   (seg_c | seg_d | seg_e | seg_f | seg_g) 
#define NUM_C   (seg_a | seg_d | seg_e | seg_f) 
#define NUM_D   (seg_b | seg_c | seg_d | seg_e | seg_g) 
#define NUM_E   (seg_a | seg_d | seg_e | seg_f | seg_g) 
#define NUM_F   (seg_a | seg_e | seg_f | seg_g) 
 
const unsigned char hex_table[] = 
{ 
    NUM_0,NUM_1,NUM_2,NUM_3,NUM_4,NUM_5,NUM_6,NUM_7, 
    NUM_8,NUM_9,NUM_A,NUM_B,NUM_C,NUM_D,NUM_E,NUM_F 
}; 
 
int32_t mul16(register int16_t x, register int16_t y); 
 
                                            //FIR filter coefficient for 
                                            //removing 50/60Hz and 100/120Hz 
                                            //from the signals 
#if 0 
static const int16_t coeffs[9] = 
{ 
    5225, 
    5175, 
    7255, 
    9453, 
    11595, 
    13507, 
    15016, 
    15983, 
    16315 
}; 
#else 
static const int16_t coeffs[12] = 
{ 
    688, 
    1283, 
    2316, 
    3709, 
    5439, 
    7431, 
    9561, 
    11666, 
    13563, 
    15074, 
    16047, 
    16384 
}; 
#endif 
 
//#define FIRST_STAGE_TARGET_HIGH         3900 
//#define FIRST_STAGE_TARGET_LOW          3600 
//#define FIRST_STAGE_TARGET_HIGH_FINE    4096 
//#define FIRST_STAGE_TARGET_LOW_FINE     3500 
 
#define FIRST_STAGE_TARGET_HIGH         3500 
#define FIRST_STAGE_TARGET_LOW          3000 
#define FIRST_STAGE_TARGET_HIGH_FINE    4096 
#define FIRST_STAGE_TARGET_LOW_FINE     2500 
#define FIRST_STAGE_STEP                5 
#define FIRST_STAGE_FINE_STEP           1 
 
enum scope_type_e 
{ 
    SCOPE_TYPE_OFF = 0, 
    SCOPE_TYPE_HEART_SIGNALS, 
    SCOPE_TYPE_RAW_SIGNALS, 
    SCOPE_TYPE_LED_DRIVE, 
}; 
int scope_type = SCOPE_TYPE_HEART_SIGNALS; 
int ir_dc_offset = 2000; 
int vs_dc_offset = 2000; 
int ir_LED_level; 
int vs_LED_level; 
int ir_heart_signal; 
int vs_heart_signal; 
int ir_heart_ac_signal; 
int vs_heart_ac_signal; 
int ir_sample; 
int vs_sample; 
unsigned int rms_ir_heart_ac_signal; 
unsigned int rms_vs_heart_ac_signal; 
unsigned int sample_count_ir = 0; 
unsigned int sample_count_vs = 0; 
int32_t ir_dc_register = 0; 
int32_t vs_dc_register = 0; 
int32_t ir_2nd_dc_register = 0; 
int32_t vs_2nd_dc_register = 0; 
unsigned long log_sq_ir_heart_ac_signal; 
unsigned long log_sq_vs_heart_ac_signal; 
unsigned long sq_ir_heart_ac_signal; 
unsigned long sq_vs_heart_ac_signal; 
unsigned int pos_edge = 0; 
unsigned int edge_debounce; 
unsigned int heart_beat_counter; 
unsigned int log_heart_signal_sample_counter; 
unsigned int heart_signal_sample_counter; 
unsigned int led_on_always; 
 
int32_t ir_pi_last_y = 0; 
int32_t ir_pi_last_x = 0; 
int32_t vs_pi_last_y = 0; 
int32_t vs_pi_last_x = 0; 
 
/* The results */ 
unsigned int heart_rate; 
unsigned int SaO2; 
 
/* Function prototypes */ 
unsigned long isqrt32(register unsigned long h); 
int16_t dc_estimator(register int32_t *p, register int16_t x); 
int16_t ir_filter(int16_t sample); 
int16_t vs_filter(int16_t sample); 
void set_LCD(void); 
void display_word(unsigned int); 
void display_number(int value, int start, int width); 
void display_pulse(int on); 
void display_correcting(int x, int on); 
 
void main(void) 
{ 
    double f1; 
    unsigned int i; 
    int32_t x; 
    int32_t y; 
 
    WDTCTL = WDTPW | WDTHOLD; 
 
                                            //Allow a little time for things 
                                            //like the 32kHz oscillator to 
                                            //settle 
    for (i = 0;  i < 0xFFFF;  i++) 
                                            //dummy loop */; 
#if 0 
    SVSCTL |= (SVSON | 0x40); 
                                            //Wait for adequate voltage to 
                                            //run at full speed 
    while ((SVSCTL & SVSOP)); 
                                            //Dummy loop */; 
                                            //The voltage should now be OK to 
                                            //run the CPU at full speed. Now 
                                            //it should be OK to use the SVS 
                                            //as a reset source. 
    SVSCTL |= PORON; 
#endif 
    SCFI0 |= FN_4;                          // x2 DCO frequency, 8MHz nominal 
                                            // DCO 
    SCFQCTL = 91;                           // 32768 x 2 x (91 + 1) = 6.03 MHz 
    FLL_CTL0 = DCOPLUS + XCAP14PF;          // DCO+ set so freq = xtal x D x 
                                            //(N + 1) 
    P1OUT = 0; 
    P1DIR = BIT0; 
 
    P2SEL = (BIT4 | BIT5); 
    P2DIR = (BIT2 | BIT3); 
    P2OUT = 0; 
 
    P3OUT = 0; 
    P3OUT |= (BIT2 | BIT3);                 //P3.2, P3.3 drives the LED 
                                            //PNP transistor 
    P3DIR |= (BIT2 | BIT3); 
 
    P4OUT = 0; 
    P5OUT = 0; 
    P6OUT = 0; 
 
    set_LCD(); 
 
    /* First amplifier stage - transconductance configuration */ 
    P6SEL |= (BIT0 | BIT1 | BIT2);          //Select OA0O 
                                            //-ve=OA0I0, +ve=OA0I1 
    OA0CTL0 = OAN_0 | OAP_1 | OAPM_3 | OAADC1; 
    OA0CTL1 = 0x00; 
 
    /* Second amplifier stage */ 
    P6SEL |= (BIT3 | BIT4);                 // Select 0A1O 0A1I 
                                            // -ve=OA1I0, +ve=DAC1 
    //OA1CTL0 = OAN_0 | OAP_3 | OAPM_3 | OAADC1; 
    //OA1CTL1 = 0x00; 
                                            //Inverted input internally 
                                            //connected to OA0 output 
 
    OA1CTL0 = OAN_2 | OAP_3 | OAPM_3 | OAADC1; 
    OA1CTL1 = OAFBR_7 | OAFC_6;             //OA as inv feedback amp, internal 
                                            //gain = 16; 
 
                                            //Configure DAC 1 to provide 
                                            //bias for the amplifier */ 
    P6SEL |= BIT7; 
    DAC12_1CTL = DAC12CALON | DAC12IR | DAC12AMP_7 | DAC12ENC; 
    DAC12_1DAT = 0; 
 
                                            //Configure DAC 0 to provide 
                                            //variable drive to the LEDs */ 
    DAC12_0CTL =  DAC12CALON | DAC12IR | DAC12AMP_7 | DAC12ENC; 
    P2OUT |= BIT3;                          //turn off source for D2 
    P2OUT &= ~BIT2;                         //turn on source for D3 
    DAC12_0DAT = 3340; 
 
                                            //Set initial values for the LED brightnesses */ 
    ir_LED_level = 1300; 
    vs_LED_level = 1450; 
    ir_pi_last_y = ir_LED_level*32768; 
    vs_pi_last_y = vs_LED_level*32768; 
 
    ir_dc_register = 0; 
    vs_dc_register = 0; 
    ir_2nd_dc_register = 0; 
    vs_2nd_dc_register = 0; 
 
    ADC12CTL0 &= ~ENC;                      // Enable conversions 
                                            // Turn on the ADC12, and 
                                            // set the sampling time 
    ADC12CTL0 = ADC12ON | MSC | SHT0_4 | REFON | REF2_5V; 
    ADC12CTL1 = SHP | SHS_1 | CONSEQ_1;     // Use sampling timer, single 
                                            //sequence, TA1 trigger 
    ADC12MCTL0 = INCH_1 | SREF_1;           // ref+=Vref, channel = A1 = OA0 
    ADC12MCTL1 = INCH_3 | SREF_1 | EOS;     // ref+=Vref, channel = A3 = OA1 
    ADC12IE = BIT1; 
    ADC12CTL0 |= ENC;                       // Enable the ADC 
    ADC12CTL0 |= ADC12SC;                   // Start conversion 
 
    TACTL = TASSEL0 | MC_1 | TACLR;         // ACLK, clear TAR, up mode 
    CCTL1 = OUTMOD_2;                       // Set/Reset enable interrupt 
    CCTL0 = CCIE; 
                                            //This gives a sampling rate of 
                                            //512sps 
    CCR0 = 31;                              //Do two channels, at 
                                            //512sps each. 
    CCR1 = 20;                              //Allow plenty of time for the 
                                            //signal to become stable before 
                                            //sampling 
                                            //Configure the USART, so we 
                                            //can report readings to a PC */ 
    P2DIR |= BIT4; 
    P2SEL |= BIT4; 
    UCTL0 &= ~SWRST; 
    ME1 |= UTXE0;                           // Enable USART1 TXD 
    UCTL0 |= CHAR;                          // 8-bit char, SWRST=1 
    UTCTL0 |= SSEL1;                        // UCLK = SMCLK 
    UBR00 = 52;                             // 115200 from 6.02MHz = 52.33 
    UBR10 = 0x00; 
    UMCTL0 = 0x45;                          // Modulation = 0.375 
    UCTL0 &= ~SWRST;                        // Initialise USART 
 
    led_on_always=1; 
 
    _EINT(); 
 
                                            //We now go round this small 
                                            //loop forever, with most of 
                                            //the work happening in the 
                                            //interrupt routines */ 
    for (;;) 
    { 
        _BIS_SR(LPM0_bits); 
 
        f1 = 60.0*500.0*3.0/(float) log_heart_signal_sample_counter; 
        heart_rate = f1; 
                                            //log_heart_signal_sample_counter /= 10; 
                                            //heart_rate = (6*500*3)/log_heart_signal_sample_counter; 
        display_number(heart_rate, 3, 3); 
 
//rms_ir_heart_ac_signal = (unsigned int) (isqrt32(log_sq_ir_heart_ac_signal/log_heart_signal_sample_counter) >> 16); 
//rms_vs_heart_ac_signal = (unsigned int) (isqrt32(log_sq_vs_heart_ac_signal/log_heart_signal_sample_counter) >> 16); 
 
        x = isqrt32(log_sq_ir_heart_ac_signal); 
        y = isqrt32(log_sq_vs_heart_ac_signal); 
        SaO2 = (unsigned int) (100.0*x/y); 
        display_number(SaO2, 7, 3); 
    } 
} 
 
// Timer A0 interrupt service routine 
#pragma vector=TIMERA0_VECTOR 
__interrupt void Timer_A0(void) 
{ 
    int i; 
 
    if ((DAC12_0CTL & DAC12OPS))            //D2 enabled in demo board 
    { 
                                            //Immediately enable the visible 
                                            //LED, to allow time for the 
                                            //transimpedance amp to settle 
        DAC12_0CTL &= ~DAC12ENC; 
        P2OUT |= BIT3;                      //turn off source for D2 
        DAC12_0CTL &= ~DAC12OPS;            // Disable IR LED, enable visible LED 
        DAC12_0CTL |= DAC12ENC; 
        DAC12_0DAT = vs_LED_level; 
        DAC12_1DAT = vs_dc_offset;          // Load op-amp offset value for visible 
        P2OUT &= ~BIT2;                     //turn on source for D3 
 
                                            //Read the IR LED results */ 
        ir_sample = ADC12MEM0; 
        i = ADC12MEM1; 
 
                                            //Enable the next conversion 
                                            //sequence. The sequence is 
                                            //started by TA1 */ 
        ADC12CTL0 &= ~ENC; 
        ADC12CTL0 |= ENC; 
 
                                            //Filter away 50/60Hz electrical 
                                            //pickup, and 100/120Hz room 
                                            //lighting optical pickup */ 
        ir_heart_signal = ir_filter(i); 
                                            //Filter away the large DC 
                                            //component from the sensor */ 
        ir_heart_ac_signal = ir_heart_signal - dc_estimator(&ir_2nd_dc_register, ir_heart_signal); 
 
                                            //Bring the signal into range 
                                            //through the second op-amp */ 
        if (i >= 4095) 
        { 
            if (ir_dc_offset > 0) 
                ir_dc_offset--; 
        } 
        else if (i < 100) 
        { 
            if (ir_dc_offset < 4095) 
                ir_dc_offset++; 
        } 
        sq_ir_heart_ac_signal += (mul16(ir_heart_ac_signal, ir_heart_ac_signal) >> 10); 
 
                                            //Tune the LED intensity to keep 
                                            //the signal produced by the first 
                                            //stage within our target range. 
                                            //We don't really care what the 
                                            //exact values from the first 
                                            //stage are. They need to be 
                                            //quite high, because a weak 
                                            //signal will give poor results 
                                            //in later stages. However, the 
                                            //exact value only has to be 
                                            //within the range that can be 
                                            //handled properly by the next 
                                            //stage. */ 
        if (ir_sample > FIRST_STAGE_TARGET_HIGH 
            || 
            ir_sample < FIRST_STAGE_TARGET_LOW) 
        { 
                                            //We are out of the target range 
                                            //Starting kicking the LED 
                                            //intensity in the right 
                                            //direction to bring us back 
                                            //into range. We use fine steps 
                                            //when we are close to the target 
                                            //range, and coarser steps when 
                                            //we are far away. 
            display_correcting(0, 1); 
            if (ir_sample > FIRST_STAGE_TARGET_HIGH) 
            { 
                if (ir_sample >= FIRST_STAGE_TARGET_HIGH_FINE) 
                    ir_LED_level -= FIRST_STAGE_STEP; 
                else 
                    ir_LED_level -= FIRST_STAGE_FINE_STEP; 
                                            //Clamp to the range of the DAC */ 
                if (ir_LED_level < 0) 
                    ir_LED_level = 0; 
            } 
            else 
            { 
                if (ir_sample < FIRST_STAGE_TARGET_LOW_FINE) 
                    ir_LED_level += FIRST_STAGE_STEP; 
                else 
                    ir_LED_level += FIRST_STAGE_FINE_STEP; 
                                             //Clamp to the range of the DAC */ 
                if (ir_LED_level > 4095) 
                    ir_LED_level = 4095; 
            } 
        } 
 
        switch (scope_type) 
        { 
        case SCOPE_TYPE_HEART_SIGNALS: 
            i = (ir_heart_ac_signal >> 6) + 128; 
                                            //Saturate to a byte */ 
            if (i > 255) 
                i = 255; 
            else if (i < 0) 
                i = 0; 
            TXBUF0 = i; 
            break; 
        case SCOPE_TYPE_RAW_SIGNALS: 
            TXBUF0 = ir_sample >> 4; 
            break; 
        case SCOPE_TYPE_LED_DRIVE: 
            TXBUF0 = ir_LED_level >> 4; 
            break; 
        } 
 
                                          //Track the beating of the heart */ 
        heart_signal_sample_counter++; 
        if (pos_edge) 
        { 
            if (edge_debounce < 120) 
            { 
                edge_debounce++; 
            } 
            else 
            { 
                if (ir_heart_ac_signal < -200) 
                { 
                    edge_debounce = 0; 
                    pos_edge = 0; 
                    display_pulse(0); 
                } 
            } 
        } 
        else 
        { 
            if (edge_debounce < 120) 
            { 
                edge_debounce++; 
            } 
            else 
            { 
                if (ir_heart_ac_signal > 200) 
                { 
                    edge_debounce = 0; 
                    pos_edge = 1; 
                    display_pulse(1); 
                    display_correcting(0, 0); 
                    display_correcting(1, 0); 
                    if (++heart_beat_counter >= 3) 
                    { 
                        log_heart_signal_sample_counter = heart_signal_sample_counter; 
                        log_sq_ir_heart_ac_signal = sq_ir_heart_ac_signal; 
                        log_sq_vs_heart_ac_signal = sq_vs_heart_ac_signal; 
                        heart_signal_sample_counter = 0; 
                        sq_ir_heart_ac_signal = 0; 
                        sq_vs_heart_ac_signal = 0; 
                        heart_beat_counter = 0; 
                        _BIC_SR_IRQ(LPM0_bits); 
                                            // Do a dummy wake up roughly 
                                            //every 2 seconds 
                    } 
                } 
            } 
        } 
    } 
    else                                    //D3 enabled in demoboard 
    { 
                                            //Immediately enable the IR LED, 
                                            //to allow time for the 
                                            //transimpedance amp to settle */ 
        DAC12_0CTL &= ~DAC12ENC; 
        P2OUT |= BIT2;                      //turn off source for D2 
        DAC12_0CTL |= DAC12OPS;             // Disable visible LED, enable IR 
                                            //LED 
        DAC12_0CTL |= DAC12ENC; 
        DAC12_0DAT = ir_LED_level; 
        DAC12_1DAT = ir_dc_offset;          // Load op-amp offset value for IR 
        P2OUT &= ~BIT3;                     //turn on source for D3 
 
                                            //Read the visible LED results */ 
        vs_sample = ADC12MEM0; 
        i = ADC12MEM1; 
 
                                            //Enable the next conversion 
                                            //sequence. The sequence is 
                                            //started by TA1 */ 
        ADC12CTL0 &= ~ENC; 
        ADC12CTL0 |= ENC; 
 
                                            //Filter away 50/60Hz electrical 
                                            //pickup, and 100/120Hz room 
                                            //lighting optical pickup */ 
        vs_heart_signal = vs_filter(i); 
                                            //Filter away the large DC 
                                            //component from the sensor */ 
        vs_heart_ac_signal = vs_heart_signal - dc_estimator(&vs_2nd_dc_register, vs_heart_signal); 
 
                                            //Bring the signal into range 
                                            //through the second op-amp */ 
        if (i >= 4095) 
        { 
            if (vs_dc_offset > 0) 
                vs_dc_offset--; 
        } 
        else if (i < 100) 
        { 
            if (vs_dc_offset < 4095) 
                vs_dc_offset++; 
        } 
        sq_vs_heart_ac_signal += (mul16(vs_heart_ac_signal, vs_heart_ac_signal) >> 10); 
 
        if (vs_sample > FIRST_STAGE_TARGET_HIGH 
            || 
            vs_sample < FIRST_STAGE_TARGET_LOW) 
        { 
            /* We are out of the target range */ 
            display_correcting(1, 1); 
            if (vs_sample > FIRST_STAGE_TARGET_HIGH) 
            { 
                if (vs_sample >= FIRST_STAGE_TARGET_HIGH_FINE) 
                    vs_LED_level -= FIRST_STAGE_STEP; 
                else 
                    vs_LED_level -= FIRST_STAGE_FINE_STEP; 
                if (vs_LED_level < 0) 
                    vs_LED_level = 0; 
            } 
            else 
            { 
                if (vs_sample < FIRST_STAGE_TARGET_LOW_FINE) 
                    vs_LED_level += FIRST_STAGE_STEP; 
                else 
                    vs_LED_level += FIRST_STAGE_FINE_STEP; 
                if (vs_LED_level > 4095) 
                    vs_LED_level = 4095; 
            } 
        } 
 
        switch (scope_type) 
        { 
        case SCOPE_TYPE_HEART_SIGNALS: 
            i = (vs_heart_ac_signal >> 6) + 128; 
                                            //Saturate to a byte */ 
            if (i > 255) 
                i = 255; 
            else if (i < 0) 
                i = 0; 
            TXBUF0 = i; 
            break; 
        case SCOPE_TYPE_RAW_SIGNALS: 
            TXBUF0 = vs_sample >> 4; 
            break; 
        case SCOPE_TYPE_LED_DRIVE: 
            TXBUF0 = vs_LED_level >> 4; 
            break; 
        } 
    } 
} 
 
#pragma vector=TIMERA1_VECTOR 
__interrupt void Timer_A1(void) 
{ 
    CCTL1 &= ~CCIFG; 
} 
 
#pragma vector=ADC_VECTOR 
__interrupt void ADC12ISR(void) 
{ 
    ADC12IFG &= ~BIT1; 
    //DAC12_0DAT = 0; 
    //DAC12_1DAT = (ir_heart_ac_signal >> 3) + 2000; 
} 
 
int16_t vs_filter(int16_t sample) 
{ 
    static int16_t buf[32]; 
    static int offset = 0; 
    int32_t z; 
    int i; 
 
                                            //Filter hard above a few Hertz, 
                                            //using a symmetric FIR. 
                                            //This has benign phase 
                                            //characteristics */ 
    buf[offset] = sample; 
    z = mul16(coeffs[11], buf[(offset - 11) & 0x1F]); 
    for (i = 0;  i < 11;  i++) 
        z += mul16(coeffs[i], buf[(offset - i) & 0x1F] + buf[(offset - 22 + i) & 0x1F]); 
    offset = (offset + 1) & 0x1F; 
    return  z >> 15; 
} 
 
int16_t ir_filter(int16_t sample) 
{ 
    static int16_t buf[32]; 
    static int offset = 0; 
    int32_t z; 
    int i; 
 
                                            //Filter hard above a few Hertz, 
                                            //using a symmetric FIR. 
                                            //This has benign phase 
                                            //characteristics */ 
    buf[offset] = sample; 
    z = mul16(coeffs[11], buf[(offset - 11) & 0x1F]); 
    for (i = 0;  i < 11;  i++) 
        z += mul16(coeffs[i], buf[(offset - i) & 0x1F] + buf[(offset - 22 + i) & 0x1F]); 
    offset = (offset + 1) & 0x1F; 
    return  z >> 15; 
} 
 
int16_t dc_estimator(register int32_t *p, register int16_t x) 
{ 
    /* Noise shaped DC estimator. */ 
    *p += ((((int32_t) x << 16) - *p) >> 9); 
    return (*p >> 16); 
} 
 
unsigned long isqrt32(register unsigned long h) 
{ 
    register unsigned long x; 
    register unsigned long y; 
    register int i; 
 
                                            //Calculate a 32 bit bit square 
                                            //root of a 32 bit integer, 
                                            //where the top 16 bits 
                                            //of the result is the integer 
                                            //part of the result, and the 
                                            //low 16 bits are fractional. 
    x = 
    y = 0; 
    for (i = 0;  i < 32;  i++) 
    { 
        x = (x << 1) | 1; 
        if (y < x) 
            x -= 2; 
        else 
            y -= x; 
        x++; 
        y <<= 1; 
        if ((h & 0x80000000)) 
            y |= 1; 
        h <<= 1; 
        y <<= 1; 
        if ((h & 0x80000000)) 
            y |= 1; 
        h <<= 1; 
    } 
    return  x; 
} 
 
void display_number(int value, int start, int width) 
{ 
    int i; 
 
    for (i = 0;  i < width;  i++) 
    {                                       // remainder = character in table 
                                            //to display 
        LCDMEM[7 + i - start] = hex_table[value%10]; 
        value /= 10; 
    } 
} 
 
void display_pulse(int on) 
{ 
    if (on) 
        LCDMEM[7] |= 0x4; 
    else 
        LCDMEM[7] &= ~0x4; 
} 
 
void display_correcting(int x, int on) 
{ 
    if (on) 
        LCDMEM[3] |= ((x)  ?  seg_a  :  seg_d); 
    else 
        LCDMEM[3] &= ~((x)  ?  seg_a  :  seg_d); 
} 
 
// For ease of construction (so that the whole LCD panel can be wired onto sockets) 
//    two I/O lines are used to provide VCC and GND voltages for the LCD driver. 
//    These I/O lines can be replaced by Vcc and GND in target boards. 
//    In addition, 3 resistors need to be connected to R03-R33 in order to provide 
//    Intemediate voltages. 
// Note that COM1-COM3 and R13-R33 is mux with I/O pins. 
//    To enable them for LCD function, the relevant bits in P5SEL 
//    should be set. 
// Also S0-S23 are MUX with I/O pins. 
//    To enable them for LCD function, the LCDCTL register is used. 
//    In this application, all S0-S23 have been selected for LCD function. 
// Refer to the architecture guide and the data sheets for more detail 
 
void set_LCD(void) 
{ 
    char *lcd_pointer; 
 
    lcd_pointer = LCDMEM; 
    while (lcd_pointer != LCDMEM + 20) 
        *lcd_pointer++ = 0; 
 
    /* Turn on the COM0-COM3 and R03-R33 pins */ 
    P5SEL |= (BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2); 
 
    LCDCTL = 0x7F;                          // Selected function: Analog generator on 
                                            // Low impedance of AG 
                                            // 4Mux active 
                                            // all outputs are Seg 
                                            // S0-S23 are LCD segment lines 
    BTCTL = BTFRFQ0;	                    // Start Basic Timer 1s + LCD 64Hz 
}