Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Problem with ADC module in Microchip Explorer 16 Kit

Status
Not open for further replies.

rakur

Newbie level 2
Joined
May 27, 2010
Messages
2
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,281
Activity points
1,303
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

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
 

because you didn't calculate FourBeatTime correctly. based on your code Assuming TMR1 is a 16-bit timer on your chip, TimerCount is essentially the higher 16-bit word. so the time elapsed, in timer ticks, is ((TimerCount << 16) + TMR1) << 8; the last "<<8" is to factor in the prescaler but you don't have to do that here, because the resulting figure is a 40-bit number.

what I think you should do is to make your own fixed point notation to handle decimal points with integers.
 
  • Like
Reactions: rakur

    rakur

    Points: 2
    Helpful Answer Positive Rating
Thanks a lot for the reply millwood. What you pointed out is correct. That is something I need to correct. Thanks for pointing that out.

But I still don't understand why the if(counter==4) section is being entered into and some result being displayed on the LCD when no input is being given yet? Is there something wrong with my ADC setting that is making it give values even when no actual input has been given? I hope I am making sense.
 

the adc port is of very high impedance so it is open to random interference. so even if you don't have anything connected to it it will still read something, especially if the adc pin is close to a large conductor.

one way to avoid this is to ground the adc input pin with a resistor. its value will depend on your application.

whether your logic to detect peaks is another story.
 
  • Like
Reactions: rakur

    rakur

    Points: 2
    Helpful Answer Positive Rating
Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top