rakur
Newbie level 2
Hi everyone,
I am currently working on a project to calculate the heart beat/rate of a person using a Red LED+Photodiode setup. The voltage output from the photodiode will vary with the heart beat and my goal is to detect the heart beat/peaks using the ADC module and display the result as Beats Per Minute(BPM) on the LCD. I am using a running average alogrithm to detect a peak(heart beat) in the voltage, where a counter is incremented everytime an ADC sample is greater than a threshold (which is updated by maintaining a running average of all sampled values). A Timer is used for timing and when 4 such peaks are detected, the time is averaged, BPM is calculated and displayed on the LCD.
The problem I am facing is that as soon as I program the microcontroller, the LCD is displaying a random number as BPM on the display even before analog input has been given. I'm sure there is some mistake in the code that I just can't seem to catch. If someone can take a look at it, I would really appreciate it. Please find my code below.
I'm working with the Explorer 16 Kit which has a PIC24FJ128GA010 16-bit microcontroller and using a C30 compiler.
Thanks in advance.
AR
I am currently working on a project to calculate the heart beat/rate of a person using a Red LED+Photodiode setup. The voltage output from the photodiode will vary with the heart beat and my goal is to detect the heart beat/peaks using the ADC module and display the result as Beats Per Minute(BPM) on the LCD. I am using a running average alogrithm to detect a peak(heart beat) in the voltage, where a counter is incremented everytime an ADC sample is greater than a threshold (which is updated by maintaining a running average of all sampled values). A Timer is used for timing and when 4 such peaks are detected, the time is averaged, BPM is calculated and displayed on the LCD.
The problem I am facing is that as soon as I program the microcontroller, the LCD is displaying a random number as BPM on the display even before analog input has been given. I'm sure there is some mistake in the code that I just can't seem to catch. If someone can take a look at it, I would really appreciate it. Please find my code below.
I'm working with the Explorer 16 Kit which has a PIC24FJ128GA010 16-bit microcontroller and using a C30 compiler.
Thanks in advance.
AR
Code:
#include <p24fj128ga010.h>
#include <stdio.h>
_CONFIG1(JTAGEN_OFF & FWDTEN_OFF)
//ADC
#define VINPUT 3 // Voltage signal from photodiode connected to AN3 input. Not using AN0 or AN1 as they are combined with PGC and PGD-not sure if can be used, AN2-USB.
#define AINPUTS 0xfff7 // Analog input to AN3 - in Explorer16 PICTailPlus 13th position
//LCD
#define LCDDATA 1
#define LCDCMD 0
#define PMDATA PMDIN1 // PMDIN1 8-bit Parallel Port Date-In Register
#define LCDbusy() LCDread( LCDCMD) & 0x80 // Bitwise '&' LCDread( LCDCMD) output with 0x80...where 0x80 is from Pg 130-Read busy-flag Instr-BF is 1 if internal op in progress
#define putLCD( d) LCDwrite( LCDDATA, (d)) // Send ASCII data to LCD
#define LCDsetC( a) LCDwrite( LCDCMD, (a & 0x7F) | 0x80) // To go to second line of LCD-starting at 0x40
//Timer1
int TimerCount = 0;
int ButtonPush = 0;
//------------------------------- LCD ---------------------------------
void LCDinit( void)
{
// PMP Initialization
PMCON = 0x83BF; // Enable the PMP, PMWREN & PTRDEN enabled, active highs, polaritys, long waits
PMMODE = 0x3FF; // Master Mode 1; 4TCY, 15TCY, 4TCY wait
PMAEN = 0x0001; // PMA0 enabled
T1CON = 0x8030; // Fosc/2 (osc=8MHz), prescaled 1:256, 64us/tick
TMR1 = 0;
while( TMR1<500) // 500 x 64us = 32ms. Need >30ms for LCD internal initialization
// LCD Initialization
PMADDR = LCDCMD; // command register
PMDATA = 0b00111000; // 8-bit interface, 2 lines, 5x7..Pg 130-Function Set
TMR1 = 0;
while( TMR1<1); // 1 x 64us = 64us. Need >40us for above operation. See "exec time" on Pg 130-Function Set
PMDATA = 0b00001100; // Pg 129-Display on/off control...display ON, cursor off, blink off
TMR1 = 0;
while( TMR1<1); // 1 x 64us = 64us. Need >40us for above operation. See "exec time" on Pg 129-Display ON/OFF
PMDATA = 0b00000001; // Pg 129-Clear Display...clear display.
TMR1 = 0;
while( TMR1<25); // 25 x 64us = 1.6ms. Need about 1.65ms for above operation. See "exec time" on Pg 129-Clear display
PMDATA = 0b00000110; // Pg 129-Entry mode set..increment cursor, no shift
TMR1 = 0;
while( TMR1<100); // 1 x 64us = 64us. Need >40us for above operation. See "exec time" on Pg 129-Entry Mode Set
// or 1.6ms??????
} // initLCD
// See if/Wait for PMP to be ready. Then look for LCD to be ready
char LCDread( int addr)
{
int dummy;
while( PMMODEbits.BUSY); // wait for PMP to be available
PMADDR = addr; // select the command address
dummy = PMDATA; // initiate a read cycle, dummy read
while( PMMODEbits.BUSY); // wait for PMP to be available
return( PMDATA); // read the status register
} // LCDread
void LCDwrite( int addr, char c)
{
while( LCDbusy()); // Wait till LCD is available
while( PMMODEbits.BUSY); // wait for PMP to be available
PMADDR = addr;
PMDATA = c;
} // writeLCD
void putsLCD( char *s)
{
while( *s)
{
putLCD( *s++);
}
} //putsLCD
//------------------------------- ADC ---------------------------------
// initialize the ADC for single conversion, select Analog input pins
void initADC( int amask)
{
AD1PCFG = amask; // select analog input pins
AD1CON1 = 0x00E0; // data output format-integer, auto convert after end of sampling. No need to include a delay loop to provide time for completion of sampling
AD1CSSL = 0; // no scanning required
AD1CON3 = 0x1FFF; // Use internal clock, max sample time avail is 31Tad, conversion time Tad = 128*Tcy = 128*(2/Fosc) = 256/8MHz = 32us which is > (required)75ns ); 31 Tad = 31*32us = 992us is the sample time. About 100samples/sec
AD1CON2 = 0; // use MUXA, AVss and AVdd are used as Vref+/-
AD1CON1bits.ADON = 1; // turn on the ADC
} //initADC
int readADC( int ch)
{
AD1CHS = ch; // select analog input channel
ADC1BUF0 = 0x0000;
AD1CON1bits.SAMP = 1; // start sampling, automatic conversion will follow at end of sampling
while (!AD1CON1bits.DONE); // wait to complete the conversion. DONE will be set as soon as conversion ends
return ADC1BUF0; // read the conversion result
} // readADC
//------------External Interrupt 0-------------
void _ISR __attribute__((no_auto_psv)) _INT0Interrupt (void)
{
ButtonPush = 1;
_INT0IF = 0;
}
//------------------- TMR1 --------------------
void _ISR __attribute__((no_auto_psv)) _T1Interrupt (void)
{
TimerCount++;
_T1IF = 0;
}
//------------------- main ---------------------
int main ()
{
// initializations
LCDinit(); // initialize the PMP and LCD
initADC( AINPUTS); // initialize the ADC for the Explorer16 analog inputs
TRISB= 0x0008; // RB3 from PORTB set as input since AN3 being used as analog input
TRISD= 0x0040; // Button S3 is RD6. Set RD6 as input.
_NSTDIS = 0; // Enable Interrupt nesting. Button push interrupt priority should be higher than TMR1.
_IPL = 0; // Processor priority level. Default 0.
T1CON = 0x8030; // TMR1 is ON, Fosc/2 (osc=8MHz), prescaled 1:256, 64us/tick, for 1 sec - post delay of 15625 -1.
PR1 = 15625 - 1; // period register
_T1IP = 4; // set TMR1 interrupt priority. 4 is default
_T1IF = 0; // clear TMR1 flag
_T1IE = 1; // set TMR1 enable
_INT0IP = 5; // External interrupt 0 priority set to 5. Higher than TMR1 and processor
_INT0IF = 0; // Clear Ext interrupt 0 flag
_INT0IE = 1; // Set Ext interrupt 0 enable bit
float a=0, sum=0, n=0, avg=0;
int up=0, counter=0, FASamp = 0;
float OneBeatTime = 0, FourBeatTime = 0;
static int BeatsPerMin =0;
char s[10];
while(1)
{
if(PORTDbits.RD6 == 0) // S3 button connected to RD6 is an active low pin.
{
_INT0IF = 1;
FASamp = 1;
}
while(ButtonPush == 1)
{
if(FASamp == 1)
{
FASamp = 0;
a = 0; sum = 0; avg = 0; up = 0; counter = 0; n = 0;
TimerCount = 0;
TMR1 = 0;
}
a = readADC(VINPUT); // Sample the Voltage Signal and convert - 10 bit result
sum = sum + a; // running average
++n;
avg = sum/n;
if (a > avg*1.5) // current value > 1.5 of average. If yes, a peak has been detected. Increment UP
up++;
else
up=0;
if (up == 1) // If one peak has already been detected, to ignore rest of the a values > avg in the same peak - increment another counter to count one heart beat
counter++;
if(counter == 4) // After four heart beats have been detected
{
FourBeatTime = (TMR1*256)/4000000 + TimerCount;
OneBeatTime = FourBeatTime/4 ; // divide by 4 to get time for one beat.
BeatsPerMin = 60/OneBeatTime;
LCDsetC(0x00);
putsLCD( "Beats Per Minute"); // put a title on the first line
LCDsetC(0x40); // Goto second line on LCD
sprintf(s, "%i", BeatsPerMin); // convert bpm from integer to string type
putsLCD(s);
ButtonPush = 0;
PORTDbits.RD6 = 1; // reset button to high to check for another button press in future
}
} // end button push - while loop
} // end main while
}// end main