commit
c9fa341744
|
@ -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)
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue