Merge branch 'master' of repos.ctdo.de:psychose

merged
This commit is contained in:
Stefan Kögl 2014-03-07 22:04:18 +01:00
commit c9fa341744
3 changed files with 1010 additions and 0 deletions

View File

@ -0,0 +1,91 @@
#
# Makefile for msp430
#
# 'make' builds everything
# 'make clean' deletes everything except source files and Makefile
# You need to set TARGET, MCU and SOURCES for your project.
# TARGET is the name of the executable file to be produced
# $(TARGET).elf $(TARGET).hex and $(TARGET).txt nad $(TARGET).map are all generated.
# The TXT file is used for BSL loading, the ELF can be used for JTAG use
#
TARGET = mod-pulse-psychose
MCU = msp430fg439
# List all the source files here
# eg if you have a source file foo.c then list it here
SOURCES = mod_pulse.c
# Include are located in the Include directory
INCLUDES = -IInclude
# Add or subtract whatever MSPGCC flags you want. There are plenty more
#######################################################################################
CFLAGS = -mmcu=$(MCU) -g -Os -Wall -Wunused $(INCLUDES)
ASFLAGS = -mmcu=$(MCU) -x assembler-with-cpp -Wa,-gstabs
LDFLAGS = -mmcu=$(MCU) -Wl,-Map=$(TARGET).map -lm -lfp -pipe
########################################################################################
CC = msp430-gcc
LD = msp430-ld
AR = msp430-ar
AS = msp430-gcc
GASP = msp430-gasp
NM = msp430-nm
OBJCOPY = msp430-objcopy
RANLIB = msp430-ranlib
STRIP = msp430-strip
SIZE = msp430-size
READELF = msp430-readelf
MAKETXT = srec_cat
CP = cp -p
RM = rm -f
MV = mv
########################################################################################
# the file which will include dependencies
DEPEND = $(SOURCES:.c=.d)
# all the object files
OBJECTS = $(SOURCES:.c=.o)
#all: $(TARGET).elf $(TARGET).hex $(TARGET).txt
all: $(TARGET).elf $(TARGET).hex
$(TARGET).elf: $(OBJECTS)
echo "Linking $@"
$(CC) $(OBJECTS) $(LDFLAGS) $(LIBS) -o $@
echo
echo ">>>> Size of Firmware <<<<"
$(SIZE) $(TARGET).elf
echo
%.hex: %.elf
$(OBJCOPY) -O ihex $< $@
#%.txt: %.hex
# $(MAKETXT) -O $@ -TITXT $< -I
# unix2dos $(TARGET).txt
# The above line is required for the DOS based TI BSL tool to be able to read the txt file generated from linux/unix systems.
%.o: %.c
echo "Compiling $<"
$(CC) -c $(CFLAGS) -o $@ $<
# rule for making assembler source listing, to see the code
%.lst: %.c
$(CC) -c $(ASFLAGS) -Wa,-anlhd $< > $@
# include the dependencies unless we're going to clean, then forget about them.
ifneq ($(MAKECMDGOALS), clean)
-include $(DEPEND)
endif
# dependencies file
# includes also considered, since some of these are our own
# (otherwise use -MM instead of -M)
%.d: %.c
echo "Generating dependencies $@ from $<"
$(CC) -M ${CFLAGS} $< >$@
.SILENT:
.PHONY: clean
clean:
-$(RM) $(OBJECTS)
-$(RM) $(TARGET).*
-$(RM) $(SOURCES:.c=.lst)
-$(RM) $(DEPEND)

2
mod_pulse_psychose/make.sh Executable file
View File

@ -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

View File

@ -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
}