diff --git a/mod_pulse_psychose/make.sh b/mod_pulse_psychose/make.sh new file mode 100755 index 0000000..a03a97d --- /dev/null +++ b/mod_pulse_psychose/make.sh @@ -0,0 +1,2 @@ +msp430-gcc -I/usr/msp430/include -Wall mod_ -mmcu=msp430fg439 -o mod-pulse-pyschose -L /usr/msp430/lib/ldscripts/msp430fg439 -lm -lfp -pipe + diff --git a/mod_pulse_psychose/mod_pulse.c b/mod_pulse_psychose/mod_pulse.c new file mode 100644 index 0000000..cb758c1 --- /dev/null +++ b/mod_pulse_psychose/mod_pulse.c @@ -0,0 +1,917 @@ +//***************************************************************************** +// 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 - Version II +// V. Chan and S. Underwood +// May 2005 +// Modified by Bhargavi Nisarga +// April 2008 +// All modifications related to Olimex's LCD were made by +// Penko T. Bozhkov, Olimex LTD +// June 2011 +//***************************************************************************** +#include "msp430fg439.h" +#include "stdint.h" +#include "intrinsics.h" +#include "math.h" + +// LCD Segment Configuration +#define seg_a 0x01 +#define seg_b 0x02 +#define seg_c 0x04 +#define seg_d 0x08 +#define seg_e 0x40 +#define seg_f 0x10 +#define seg_g 0x20 +#define seg_h 0x80 + +#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) + + +// ***************************************************************** +// Definitions related to Olimex's LCD Digits and initialization!!!! +// ***************************************************************** +// Definitions for Olimex LCD digits 10 and 11 +#define a 0x10 +#define b 0x01 +#define c 0x04 +#define d 0x08 +#define e 0x40 +#define f 0x20 +#define g 0x02 +#define h 0x80 +// Character generator definition for display digits 10 and 11 +const char char_gen_10_11[] = { + a+b+c+d+e+f, // 0 Displays "0" + b+c, // 1 Displays "1" + a+b+d+e+g, // 2 Displays "2" + a+b+c+d+g, // 3 Displays "3" + b+c+f+g, // 4 Displays "4" + a+c+d+f+g, // 5 Displays "5" + a+c+d+e+f+g, // 6 Displays "6" + a+b+c, // 7 Displays "7" + a+b+c+d+e+f+g, // 8 Displays "8" + a+b+c+d+f+g, // 9 Displays "9" +}; +// undefines +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h + +// Definitions for Olimex LCD digits 8 and 9 +#define a 0x01 +#define b 0x02 +#define c 0x04 +#define d 0x80 +#define e 0x40 +#define f 0x10 +#define g 0x20 +#define h 0x08 +// Character generator definition for display digits 8 and 9 +const char char_gen_8_9[] = { + a+b+c+d+e+f, // 0 Displays "0" + b+c, // 1 Displays "1" + a+b+d+e+g, // 2 Displays "2" + a+b+c+d+g, // 3 Displays "3" + b+c+f+g, // 4 Displays "4" + a+c+d+f+g, // 5 Displays "5" + a+c+d+e+f+g, // 6 Displays "6" + a+b+c, // 7 Displays "7" + a+b+c+d+e+f+g, // 8 Displays "8" + a+b+c+d+f+g, // 9 Displays "9" +}; +// undefines +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h + +// Definitions for Olimex LCD digits 1 to 7. Here each digit definition require 2 bytes +#define a 0x0080 +#define b 0x0040 +#define c 0x0020 +#define d 0x0010 +#define e 0x2000 +#define f 0x4000 +#define g 0x0402 +#define h 0x1000 +// Character generator definition for display digits 1 to 7 +const int char_gen_1_7[] = { + a+b+c+d+e+f, // 0 Displays "0" + b+c, // 1 Displays "1" + a+b+d+e+g, // 2 Displays "2" + a+b+c+d+g, // 3 Displays "3" + b+c+f+g, // 4 Displays "4" + a+c+d+f+g, // 5 Displays "5" + a+c+d+e+f+g, // 6 Displays "6" + a+b+c, // 7 Displays "7" + a+b+c+d+e+f+g, // 8 Displays "8" + a+b+c+d+f+g, // 9 Displays "9" +}; +// undefines +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h + + +int heart_pulse = 0; + +int itobcd(int i) // Convert hex word to BCD. +{ + int bcd = 0; // + char j = 0; // + + while (i > 9) // + { + bcd |= ((i % 10) << j); // + i /= 10; // + j += 4; + } // + return (bcd | (i << j)); // Return converted value +}// itobcd(i) + + +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) { + return ((long) x * 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 + +// SaO2 Look-up Table +const unsigned int Lookup [43] = {100,100,100,100,99,99,99,99,99,99,98,98,98,98, + 98,97,97,97,97,97,97,96,96,96,96,96,96,95,95, + 95,95,95,95,94,94,94,94,94,93,93,93,93,93}; +// +// #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 + +// LED Target Range +#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 2700 +#define FIRST_STAGE_STEP 5 +#define FIRST_STAGE_FINE_STEP 1 + +// UART Transmission Structure Definition +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 scope_type = SCOPE_TYPE_RAW_SIGNALS; + +int ir_dc_offset = 2000; +int vs_dc_offset = 2000; +int ir_LED_level; +int vs_LED_level; +int ir_sample; +int vs_sample; +char is_IR; +int ir_heart_signal; +int vs_heart_signal; +int ir_heart_ac_signal; +int vs_heart_ac_signal; +unsigned int rms_ir_heart_ac_signal; +unsigned int rms_vs_heart_ac_signal; +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; + +volatile unsigned int j; + +/* The results */ +unsigned int heart_rate; +unsigned int heart_rate_LSB = 0; +unsigned int SaO2, Ratio; +unsigned int SaO2_LSB = 0; + +/* 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_number(int value, int start, int width); +void display_pulse(int on); +void display_correcting(int x, int on); + +void delay(long cycles){ + while(cycles){ cycles--; } +} + +int main(void) +{ + double f1; + int32_t x; + int32_t y; + + WDTCTL = WDTPW | WDTHOLD; + SCFI0 |= FN_4; // x2 DCO frequency, 8MHz nominal + // DCO + SCFQCTL = 91; // 32768 x 2 x (91 + 1) = 6.03 MHz + FLL_CTL0 = DCOPLUS + XCAP10PF; // DCO+ set so freq = xtal x D x + //(N + 1) + // Loop until 32kHz crystal stabilizes + do + { + IFG1 &= ~OFIFG; // Clear oscillator fault flag + for (j = 50000; j; j--); // Delay + } + while (IFG1 & OFIFG); // Test osc fault flag + + // Setup GPIO + P1DIR = 0xFF; + P1OUT = 0; + P2DIR = 0xFF; + P2DIR |= BIT2 + BIT3; // P2.2 and P2.3 o/p direction - + // drives PNP transistors in H-Bridge + P2OUT = 0; + P3DIR = 0xFF; + P3OUT = 0; + P4DIR = 0xFF; + P4OUT = 0; + P5DIR = 0xFF; + P5OUT = 0; + P6OUT = 0; + + /* Setup LCD */ + 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 = 0; + + /* Second amplifier stage */ + P6SEL |= (BIT3 | BIT4); // Select 0A1O 0A1I + // -ve=OA1I0, +ve=DAC1 + // -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 = 15; + + /* 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; // VRef+, high speed/current, + // DAC12OPS=0 => DAC12_0 output on P6.6 (pin 5) */ + // Configure P2.2 and P2.3 to + // provide variable drive to LEDs + P2OUT |= BIT2; // turn off source for D2 + P2OUT &= ~BIT3; // turn on source for D3 + DAC12_0DAT = 3340; + + // Set initial values for the LED brightnesses + ir_LED_level = 1300; + vs_LED_level = 1450; + + /* Configure ADC12 */ + 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(SHS_1), start with ADC12MEM0 + ADC12MCTL0 = INCH_1 + SREF_1; // ref+=Vref, channel = A1 = OA0 + ADC12MCTL1 = INCH_3 + SREF_1 + EOS; // ref+=Vref, channel = A3 = OA1 + ADC12IE = BIT1; // ADC12MEM1 interrupt enable + ADC12CTL0 |= ENC; // Enable the ADC + ADC12CTL0 |= ADC12SC; // Start conversion + + /* Configure Timer */ + TACTL = TASSEL0 + TACLR; // ACLK, clear TAR, + TACCTL1 = OUTMOD_2; + TACCTL0 = CCIE; + // This gives a sampling rate of + // 512sps + TACCR0 = 31; // Do two channels, at + // 512sps each. + TACCR1 = 10; // Allow plenty of time for the + // signal to become stable before + // sampling + TACTL |= MC_1; // Timer A on, up mode + + /*Configure 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 + + +/* + // For Olimex's LCD debug purpose only! + int j=999; + for(int i=0;i<10;i++){ + delay(700000); + display_number(j, 3, 3); // The Small digits + display_number(j, 7, 3); // The Large digits + j = j-111; + } + set_LCD(); +*/ + + while(1) + { + __bis_SR_register(LPM0_bits + GIE); + __bis_SR_register(LPM0_bits); // Enter LPM0 needed for UART TX completion + __no_operation(); + + /* Heart Rate Computation */ + f1 = 60.0*512.0*3.0/(float)log_heart_signal_sample_counter; + heart_rate = (unsigned int)f1; + //heart_rate = f1; + display_number(heart_rate, 3, 3); + heart_rate_LSB = heart_rate & 0x00FF; + + /* SaO2 Computation */ + x = log_sq_ir_heart_ac_signal/log_heart_signal_sample_counter; + y = log_sq_vs_heart_ac_signal/log_heart_signal_sample_counter; + Ratio = (unsigned int) (100.0*logf(y)/logf(x)); + if (Ratio > 66) + SaO2 = Lookup[Ratio - 66]; // Ratio - 50 (Look-up Table Offset) - 16 (Ratio offset) + else if (Ratio > 50) + SaO2 = Lookup[Ratio - 50]; // Ratio - 50 (Look-up Table Offset) + else + //SaO2 = 100; + SaO2 = 99; + display_number(SaO2, 7, 3); + SaO2_LSB = SaO2 & 0x00FF; + } + return 0; +} + + +// 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 on source for D3 + 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 off source for D2 + + is_IR = 0; // IR LED OFF + + ir_sample = ADC12MEM0; // Read the IR LED results + 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 IR signal into range through the second opamp */ + if (i >= 4095) + { + if (ir_dc_offset > 100) + 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. + 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; + } + } + + /* UART Transmission - IR heart signals */ + switch (scope_type) + { + case SCOPE_TYPE_HEART_SIGNALS: + i = (ir_heart_ac_signal >> 6) + 128; + // Saturate to a byte + if (i >= 255) // Make sure the data != 0x0 or 0xFF + i = 254; // as 0x0 and 0xFF are used for sync + else if (i <= 0) // bytes in the LABVIEW GUI + i = 1; + + TXBUF0 = 0x00; // Byte 1 - 0x00 (synchronization byte) + while (!(IFG1 & UTXIFG0)); + TXBUF0 = 0xFF; // Byte 2 - 0xFF (synchronization byte) + while (!(IFG1 & UTXIFG0)); + TXBUF0 = i; // Byte 3 - IR Heart signal (AC only) + while (!(IFG1 & UTXIFG0)); + TXBUF0 = heart_rate_LSB; // Byte 4 - Heart rate data + while (!(IFG1 & UTXIFG0)); + TXBUF0 = SaO2_LSB; // Byte 5 - %SaO2 data + while (!(IFG1 & UTXIFG0)); + TXBUF0 = heart_pulse; + break; + + case SCOPE_TYPE_RAW_SIGNALS: + while (!(IFG1 & UTXIFG0)); + 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(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 on source for D3 + 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 off source for D2 + + is_IR = 1; // IR LED ON + + vs_sample = ADC12MEM0; //Read the visible LED results + 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 VS signal into range through the second opamp */ + if (i >= 4095) + { + if (vs_dc_offset > 100) + 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; + } + } + } + +} + +#pragma vector=ADC_VECTOR +__interrupt void ADC12ISR(void) +{ + ADC12IFG &= ~BIT1; // Clear the ADC12 interrupt flag + DAC12_0DAT = 0; // Turn OFF the LED + DAC12_1DAT = 0; + // Turn OFF the H-Bridge completely + if(is_IR) // If IR LED was ON in TA0 ISR + P2OUT |= BIT2; // P2.2 = 1 + else // Else if VS LED ON in TA0 ISR + P2OUT |= BIT3; // P2.3 = 1 +} + +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 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; +} + +/*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; +} */ + +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); +} + +/* LCD number Display */ +void display_number(int value, int start, int width) +{ +/* + unsigned int i; + unsigned int Output; + char *pLCD = (char *)&LCDMEM[7-start]; + + for (i = 16, Output = 0; i; i--) // BCD Conversion, 16-Bit + { + Output = __bcd_add_short(Output, Output); + if (value & 0x8000) + Output = __bcd_add_short(Output, 1); + value <<= 1; + } + + for (i = 0; i < width; i++) // Process 4 digits + { + *pLCD++ = hex_table[Output & 0x0f]; // Segments to LCD + Output >>= 4; // Process next digit + } +*/ + + value = itobcd(value); + + if(start == 3){ + // Display heart rate + LCDMEM[2] = char_gen_10_11[value & 0x0f]; // Display current heart rate units -> LCD Digit 11 + LCDMEM[3] = char_gen_10_11[(value & 0xf0) >> 4]; // tens -> LCD Digit 10 + LCDMEM[4] = char_gen_8_9[(value & 0xf00) >> 8]; // hundreds -> LCD Digit 9 + } + else if(start == 7){ + // Display oxigenation + LCDMEM[7] = ((char)(char_gen_1_7[value & 0x0f]>>8)); // LCD -> Digit 7 High Byte + LCDMEM[6] = ((char)(char_gen_1_7[value & 0x0f]&0x00FF)); // LCD -> Digit 7 Low Byte + LCDMEM[9] = ((char)(char_gen_1_7[((value & 0xf0) >> 4)]>>8)); // LCD -> Digit 6 High Byte + LCDMEM[8] = ((char)(char_gen_1_7[((value & 0xf0) >> 4)]&0x00FF)); // LCD -> Digit 6 Low Byte + // Don't display values bigger than 99 + //LCDMEM[11] = ((char)(char_gen_1_7[((value & 0xf00) >> 8)]>>8)); // LCD -> Digit 5 High Byte + //LCDMEM[10] = ((char)(char_gen_1_7[((value & 0xf00) >> 8)]&0x00FF)); // LCD -> Digit 5 Low Byte + } + + +} + +/* LCD Pulse Display */ +void display_pulse(int on) +{ + if (on) { + LCDMEM[1] = 0xF0; // Heart beat detected enable "<^>" on LCD + heart_pulse = 1; + } + else { + heart_pulse = 0; + LCDMEM[1] = 0x00; // Disable "<^>" on LCD for blinking effect + } +} + +/* LCD Correcting info Display */ +void display_correcting(int x, int on) +{ + if (on) + LCDMEM[3] |= ((x) ? seg_a : seg_d); + else + LCDMEM[3] &= ~((x) ? seg_a : seg_d); +} + +/* Configure LCD */ +void set_LCD(void) +{ + volatile unsigned int i; + for(i=0;i<20;i++) // Clear LCD memory + { + LCDMEM[i] = 0x00; + } + + /* 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 +} + diff --git a/mod_pulse_psychose/mod_pulse_psychose b/mod_pulse_psychose/mod_pulse_psychose new file mode 100755 index 0000000..d246eb5 Binary files /dev/null and b/mod_pulse_psychose/mod_pulse_psychose differ