You are on page 1of 24

Low Cost Electrocardiography Monitoring System

using Microcontroller

A project report submitted in partial fulfillment of the requirement for the


award of the degree of

BACHELOR OF ENGINEERING
IN
ELECTRONICS AND INSTRUMENTATION

Submitted by

Pradeep Kumar Terli V.S.Satya Praveen


T.Anil Kumar

Under the esteemed guidance of

Mrs.P.Indira,M.E.,
Assistant Professor
Dept. of Electronics and Instrumentation Engineering

DEPARTMENT OF ELECTRONICS AND INSTRUMENTATION ENGINEERING


GITAM INSTITUTE OF TECHNOLOGY
(Autonomous)
GITAM UNIVERSITY
Visakhapatnam-45
2010-2011
DECLARATION

This is to certify that Report entitled “Low Cost Electrocardiography Monitoring System Using
Microcontroller” which is submitted by me in partial fulfillment of the requirement for the award
of Bachelors degree in Electronics and Instrumentation Engineering, GITAM Institute of
Technology , GITAM University comprises only my original work and due acknowledgement
has been made in the text to all other material used.

Date: Name of student

(signature)

APPROVED BY

Name & Designation of the Project Guide

(signature)
CERTIFICATE

This is to certify that report entitled “Measurment of Temperature using Thermocouple with the
help of Microcontroller” which is submitted by BATCH 6 in partial fulfillment of the
requirement for the award of degree B.Tech in Electronics & Instrumentation Engineering to
Department of Electronics & Instrumentation Engineering, GITAM Institute of
Technology is a record of the candidate own work carried out by him under my supervision. The
matter embodied in this thesis is original and has not been submitted for the award of any other
degree.

Date: Name & Desigantion of the Project Guide

(signature)

(signature) (signature)

Name and Designation Head of the Department

Project Coordinator
Electrocardiograph ( ECG )

Electrocardiography (ECG, or EKG [from the German Elektrokardiogramm]) is a transthoracic


interpretation of the electrical activity of the heart over timecaptured and externally recorded by
skin electrodes.[1] It is a noninvasiverecording produced by an electrocardiographic device.
The ECG works mostly by detecting and amplifying the tiny electrical changes on the skin that
are caused when the heart muscle "depolarises" during each heart beat. At rest, each heart muscle
cell has a charge across its outer wall, or cell membrane. Reducing this charge towards zero is
called de-polarisation, which activates the mechanisms in the cell that cause it to contract.
During each heartbeat a healthy heart will have an orderly progression of a wave of
depolarisation that is triggered by the cells in the sinoatrial node, spreads out through the atrium,
passes through "intrinsic conduction pathways" and then spreads all over the ventricles. This is
detected as tiny rises and falls in the voltage between two electrodes placed either side of the
heart which is displayed as a wavy line either on a screen or on paper. This display indicates the
overall rhythm of the heart and weaknesses in different parts of the heart muscle.

ECG Waveform
Lead Configurations

Limb leads
Leads I, II and III are called limb leads. The electrodes that form these signals are located on the
limbs—one on each arm and one on the left leg. The limb leads form the points of what is known
as Einthoven's triangle.

Lead I is the voltage between the (positive) left arm (LA) electrode and right arm (RA) electrode:

I = LA − RA.

Lead II is the voltage between the (positive) left leg (LL) electrode and the right arm (RA)
electrode:
II = LL − RA.
Lead III is the voltage between the (positive) left leg (LL) electrode and the left arm (LA)
electrode:

III = LL − LA.
Augmented limb leads

Lead augmented vector right (aVR) has the positive electrode (white) on the right arm. The
negative electrode is a combination of the left arm (black) electrode and the left leg (red)
electrode, which "augments" the signal strength of the positive electrode on the right arm:

Lead augmented vector left (aVL) has the positive (black) electrode on the left arm. The
negative electrode is a combination of the right arm (white) electrode and the left leg (red)
electrode, which "augments" the signal strength of the positive electrode on the left arm:

Lead augmented vector foot (aVF) has the positive (red) electrode on the left leg. The negative
electrode is a combination of the right arm (white) electrode and the left arm (black) electrode,
which "augments" the signal of the positive electrode on the left leg:

Unipolar vs. bipolar leads

Bipolar leads have one positive and one negative pole


Unipolar leads also have two poles, as a voltage is measured; however, the negative pole is a
composite pole (Wilson's central terminal) made up of signals from lots of other electrodes
PROJECT’s BLOCK DIAGRAM

Surface Electrodes
Amplification Band Pass Filter
to Pick up the ECG Opto Isolator
Instrumentation 0.05 Hz – 120
signal from human
Amplifier Hz
body

Graphical ATMega32
LCD Display Or
For ECG display PIC Microcontroller Notch Filter
For programming 50Hz
Graphical LCD

Adaptive Filtering
(LMS Algorithm)

Disposable Electrodes
INSTRUMENTATION AMPLIFIER

SPECIFICATIONS
Easy to use

Higher performance than discrete design

Single-supply and dual-supply operation

Input voltage range extends 150 mV below ground (single supply)

Low power, 550 μA maximum supply current

Gain set with one external resistor

Gain range: 1 (no resistor) to 1000

Noise: 35 nV/√Hz RTI noise @ 1 kHz (G = 1)

Excellent AC specifications

90 dB minimum CMRR (G = 10); 70 dB minimum CMRR (G = 1) at 60 Hz, 1 kΩ source


imbalance

800 kHz bandwidth (G = 1)

APPLICATIONS : Low power medical instrumentation, Transducer interfaces


Thermocouple amplifiers, Industrial process controls,Difference amplifiers.
ATMEGA32 Microcontroller

• High-performance, Low-power AVR® 8-bit Microcontroller


• Advanced RISC Architecture
• 131 Powerful Instructions – Most Single-clock Cycle Execution
• Nonvolatile Program and Data Memories
• 32K Bytes of In-System Self-Programmable Flash
• True Read-While-Write Operation
• 1024 Bytes EEPROM
– 2K Byte Internal SRAM
– Four PWM Channels
– 8-channel, 10-bit ADC
– On-chip Analog Comparator
– Internal Calibrated RC Oscillator
• External and Internal Interrupt Sources
– Six Sleep Modes: Idle, ADC Noise Reduction, Power-save, Power-down, Standby
and Extended Standby
• I/O and Packages
– 32 Programmable I/O Lines
• Operating Voltages
– 4.5 - 5.5V for ATmega32
• Speed Grades
– 0 - 16 MHz for ATmega32
• Power Consumption at 1 MHz, 3V, 25°C for ATmega32L
– Active: 1.1 mA
– Idle Mode: 0.35 mA
– Power-down Mode: < 1 μA

Features
High current transfer ratio : CTR >50%
High I/O isolation voltage :
VISO = 5000 Vrms (min.)
Fast response :
tr = 2 ms, tf = 3 ms(typ.)
Low dark current : ICEO < 100nA
Design Procedure:
High pass filter
Pick C1 = C2: __________
Calculate R1:2 * *C1*Frequency
1

: ___________

Low pass filter

Pick C1: __________


Calculate C2 = C1 * 2: __________
Calculate R1 and R2 =
2 2 * *C1*Frequency
1

: __________
Notch Filter Design

ADAPTIVE 50HZ NOISE CANCELLATION


Least mean squares (LMS) algorithms are a class of adaptive filter used to mimic a desired
filter by finding the filter coefficients that relate to producing the least mean squares of the error
signal (difference between the desired and the actual signal). It is a stochastic gradient descent
method in that the filter is only adapted based on the error at the current time.

Hardware Implementation
60 Noise Cancellation System (Block Diagram)
This is the overall noise cancellation technique we use. Here we show the entire system in a
reduced "block diagram" format. Input/output pins are shown on the ATmega32.

Noise Adder (60 Hz Adaptive Noise Cancellation)


This circuit is used to create a 60 Hz contaminated signal for adaptive filter cancellation. Since
we need to be able to test our adaptive algorithm with a well-known and understood signal, we
used this non-inverting adding circuit so that we can sum a 60 Hz "noise" signal and a valid
reference signal. We use one function generator to make a "clean" signal and sum it with the
output of the transformer circuit
shown below.
Noise Remover (60 Hz Adaptive Noise Cancellation)
This circuit takes in the noise contaminated signal and an adaptive output signal from the
ATmega32 and sums them to remove the contamination. The overall output is then fed back to
the ATmega32 as an error reference signal through a RC lowpass to prevent aliasing.

AC Transformer Input Converter

We used an AC transformer to adaptively cancel "real" 60 Hz noise in from the 120 V


powerlines (the standard voltage and frequency of powerlines across the U.S.). In order to obtain
an reference voltage suitable for the ATmega32, we used the following circuit. The AC
transformer takes in a 56 V peak-to-peak signal (18 V RMS) and converts it to a 1.6 V peak-to-
peak, 1.8 V DC offset. We are using a 2.56 V reference voltage on the ATmega32 ADC.
RC Filter

The RC lowpass to filter the PWM out from the ATmega32 was made of a 20k Ohm resistor and
a 0.04 uF capacitor so that it has a cutoff frequency of about 230 Hz. Because are resistors and
capacitors are only valid to 5% and because the RC doesn't have the steepest cutoff, the cutoff
frequency is appropriate for the function.

ATMEGA32 PROGRAM FOR FILTERING


/*
* Captures a reference signal (60Hz) from A.0, an error signal in
* A.1, and adaptively generate an output to B.3 that minimizes the
* correlation between input and error.
*
* By Robert Ochshorn and Kyle Wesson
*/

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

//output rate select


/* #define OUT_CLK_OVR_8 /\* output with clock speed over 8 *\/
*/
/* #define OUT_CLK_OVR_64 /\* output with clock speed over 64
*\/ */
#define OUT_FULL_CLK /* output with full clock speed */

//input rate select


/* #define IN_CLK_OVR_8 /\* output with clock speed over 8 *\/
*/
#define IN_CLK_OVR_64 /* output with clock speed over 64 */
/* #define IN_FULL_CLK /\* output with full clock speed *\/ */

/* the increment shift determines what to divide the difference


between previous output and next desired output in order to
interpolate towards the desired output.*/

#ifdef OUT_FULL_CLK
#ifdef IN_CLK_OVR_64
#define INC_SHIFT 7
#endif
#ifdef IN_CLK_OVR_8
#define INC_SHIFT 4
#endif
#ifdef IN_FULL_CLK
#define INC_SHIFT 1
#endif
#endif
#ifdef OUT_CLK_OVR_8
#ifdef IN_CLK_OVR_64
#define INC_SHIFT 4
#endif
#ifdef IN_CLK_OVR_8
#define INC_SHIFT 2
#endif
#ifdef IN_FULL_CLK
#define INC_SHIFT -2
#endif
#endif
#ifdef OUT_CLK_OVR_64
#ifdef IN_CLK_OVR_64
#define INC_SHIFT 2
#endif
#ifdef IN_CLK_OVR_8
#define INC_SHIFT -2
#endif
#ifdef IN_FULL_CLK
#define INC_SHIFT -5
#endif
#endif

#define FILTER_LENGTH 8 /* how many samples do we hold onto? */


#define STEP_SIZE 1 /* filter one out of X inputs */

unsigned char INPUT_LENGTH = FILTER_LENGTH*STEP_SIZE; /* length of


input
buffer */

//output
signed long accumulator; /* 16:16 fixed-point...the integer value
is what we send to OCR0 */
signed int increment; /* 0:16 fixed-pi */
signed long prev_out; /* previous output value */
signed long out; /* next output value desired */

//input
unsigned char input[FILTER_LENGTH*STEP_SIZE]; /* reference input
sampled as an 8:0 char. TODO:
10-bit a/d? */
unsigned char* input_next; /* pointer to the next open spot in
the input array */
unsigned char* input_ptr; /* "disposable" pointer for use in
loops */

//filter
signed long w[FILTER_LENGTH]; /* filter weights in 16:16 fixed
point */

signed long safe_w[FILTER_LENGTH]; /* a safe way to restore our


weights */

#define SAFE_W_RESET 10000


unsigned int safe_w_time;

unsigned long err_mu; /* fixed-point error*mu */

#define MU 2 /* convergence rate */


#define MU_SHIFT 0; /* Sets a threshold for error of
magnitude of error that will cause
weights to be updated (0-8) */

#define THRESHOLD 12 /* 16-X*/

char shift;

char error, error_cutoff; /* the captured error value and the


cutoff between positive and
negative, respectively. */

//fixed point math


/* These defines allow 16:16 fixed point math */
#define multFix80(a,b) (((a*((signed long)b)))) /* multiply fixed by 8:0 char
*/
#define int2fix(a) (((signed long)(a))<<16) /* Convert char to
fix. a is a char */
#define fix2int(a) ((signed int)((a)>>16)) /* Convert fix to char. a
is an int */
#define float2fix(a) ((signed long)((a)*65536.0)) /* Convert float to
fix. a is a float */
#define fix2float(a) ((float)(a)/65536.0) /* Convert fix to float. a
is an int */

#define LED_TIME_RESET 250 /* blink an LED every x times through


the timer loop */
int led_time;
int i;

int error_acc;

enum capture_states {REF_CAPT, ERR_CAPT};


int capture_state;

enum flags {REF_READY, ERR_READY, WAITING};


int process_state;

enum safety_states {SAFE_READY, NOTHING_IS_SAFE};


int safety_state;

void init(void);
void computeOutput(void);
void updateWeightsLMS(void);
void timing_fail(void);

// OUTPUT
ISR (TIMER0_OVF_vect) {
accumulator += increment;
OCR0 = 128 + fix2int(accumulator);
}

// INPUT
ISR (TIMER2_OVF_vect) {

switch(capture_state) {
case REF_CAPT:
if(++input_next == input+INPUT_LENGTH) {
input_next = input; /* reset buffer ptr */

error_cutoff = error_acc / INPUT_LENGTH; /* normalize


error cutoff */
error_acc = 0; /* reset error_acc */

capture_state = ERR_CAPT;
*input_next = ADCH; /* get input sample */

process_state = REF_READY;
break;

case ERR_CAPT:

capture_state = REF_CAPT;
error = ADCH;
error_acc += error;
process_state = ERR_READY;
break;
}

ADMUX ^= 1; /* toggle input source to A.1 */


ADCSRA |= 1<<ADSC; /* start next a/d conversion */

void computeOutput(void) {
prev_out = out;

//compute output: y = \sum_{i=0}^{n} w_i * input_i


i=0;
out=0;
//from least-recent input to end of buffer
for(input_ptr=input_next; input_ptr<input+INPUT_LENGTH;
input_ptr+=STEP_SIZE) {
out += multFix80(w[i++], *input_ptr);
}
//from start of buffer to most-recent input
for(input_ptr=input; input_ptr<input_next; input_ptr+=STEP_SIZE) {
out += multFix80(w[i++], *input_ptr);
}
if(fix2int(out) > 128 || fix2int(out) < -128 || fix2int(out-prev_out) > 80
|| fix2int(prev_out-out) > 80) {
switch(safety_state) {
case SAFE_READY:
safe_w_time = SAFE_W_RESET;
safety_state = NOTHING_IS_SAFE;
//revert weights
for(i=0; i<FILTER_LENGTH; i++) {
w[i] = safe_w[i];
}
PORTD ^= 0xff; //toggle LED
computeOutput();
return;
break;
case NOTHING_IS_SAFE:
for(i=0; i<FILTER_LENGTH; i++) {
w[i] = 0;
}
PORTD ^= 0xff; //toggle LED
computeOutput();
return;
break;
}
}
increment = (out-prev_out)>>INC_SHIFT;

/* efficient LMS implementation */


void updateWeightsLMS(void) {
i = 0;
if(error > error_cutoff) {
shift = (error - error_cutoff)>>THRESHOLD; /* this serves as
an amaglamation
of mu and error
magnitude */
for(input_ptr=input_next; input_ptr<input+INPUT_LENGTH;
input_ptr+=STEP_SIZE) {
w[i++] -= *input_ptr>>shift;
}
for(input_ptr=input; input_ptr<input_next; input_ptr+=STEP_SIZE) {
w[i++] -= *input_ptr>>shift;
}
}
else {
shift = (error_cutoff - error)>>THRESHOLD;
for(input_ptr=input_next; input_ptr<input+INPUT_LENGTH;
input_ptr+=STEP_SIZE) {
w[i++] += *input_ptr>>shift;
}
for(input_ptr=input; input_ptr<input_next; input_ptr+=STEP_SIZE) {
w[i++] += *input_ptr>>shift;
}
}
}

int main(void) {
init();
while(1) {
switch(process_state) {
case WAITING:
break;
case REF_READY:
computeOutput();
process_state = WAITING;
break;
case ERR_READY:
if(--safe_w_time == 0) { /* backup a hopefully-working set of
weights */
safe_w_time = SAFE_W_RESET;
for(i=0; i<FILTER_LENGTH; i++) {
safe_w[i] = w[i];
}
safety_state = SAFE_READY;
}
updateWeightsLMS();
process_state = WAITING;
break;
}
}
return(0);
}

void init(void) {
//led
DDRD = 0xff; /* drive PORTD to output */
PORTD = 0x00; /* initialize LED on (active low)*/
led_time = LED_TIME_RESET; /* initialize LED reset scalar */

//ain
DDRA = 0x00; /* drive PORTA to input */

//ocr0
DDRB = 0xff; /* drive PORTB to output */

//buffers
input_next = input; /* initialize input_next pointer to
the beginning of the input array */

for(i=0; i<(FILTER_LENGTH*STEP_SIZE); i++) {


w[i] = int2fix(0); /* set filter weights to start
at 0 */
input[i] = 0; /* initialize input buffer to 0 */
}

safe_w_time = SAFE_W_RESET;

process_state = WAITING;

safety_state = NOTHING_IS_SAFE;
/* w[0] = float2fix(-0.3); */

//error
capture_state = REF_CAPT;
error_cutoff = 40; /* divide between positive and negative */

//init a/d and start capture


ADMUX = 1<<ADLAR | /* left-adjust result */
1<<REFS1 | 1<<REFS0; /* internal vref with external cap at
aref pin */

ADCSRA = 1<<ADEN | /* ADC enable */


1<<ADSC | /* start conversion */
1<<MUX2 | 1<<MUX1 | 1<<MUX0; /* 128 prescalar */

TCCR0 = 1<<WGM01 | 1<<WGM00 | /* fast PWM mode */


1<<COM01; /* clear OC0 on compare match */

#ifdef OUT_CLK_OVR_8
TCCR0 |= 0<<CS02 | 1<<CS01 | 0<<CS00; /* clk/8 prescaler */
#else
#ifdef OUT_CLK_OVR_64
TCCR0 |= 0<<CS02 | 1<<CS01 | 1<<CS00; /* clk/64 prescaler */
#else
TCCR0 |= 0<<CS02 | 0<<CS01 | 1<<CS00; /* full clock speed*/
#endif
#endif

TCCR2 = 0<<WGM01 | 0<<WGM00 | /* normal timer/counter


operation */
1<<COM21 | 0<<COM20; /* clear oc2 on compare match */

#ifdef IN_CLK_OVR_64
TCCR2 |= 1<<CS22 | 0<<CS21 | 0<<CS20; /* clk/64 prescaler */
#else
#ifdef IN_CLK_OVR_8
TCCR2 |= 0<<CS22 | 1<<CS21 | 0<<CS20; /* clk/8 prescaler */
#else
TCCR2 |= 0<<CS22 | 0<<CS21 | 1<<CS20; /* full speed ahead */
#endif
#endif

TIMSK = 1<<TOIE0 | 1<<TOIE2; /* timer/counter0&2 overflow


interrupt enable */

//crank up 'em interrupts!


sei (); /* enable ISRs */
}

void timing_fail(void) {

//disable interrupts
TIMSK = 0<<TOIE0 | 0<<TOIE2;

int reset = 19000000;

//die badly
while(1) {
if(led_time != 0) {
led_time--;
}
else {
led_time = reset;

PORTD ^= 0xff; //blink LED


}
}

}
Graphic LCD - 128x64 G B/L

Dot matrix LCD segment driver with 64 channel output


·Input and Output signal
-Input: 8 bit parallel display data
Control signal from MPU
Splitted bias voltage (V1R, V1L, V2R, V2L,V3R. V3L, V4R, V4L)
- Output: 64 channel waveform for LCD driving.
· Display data is stored in display data RAM from MPU.
· Interface RAM
- Capacity: 512 bytes (4096 bits)
- RAM bit data: RAM bit data = 1:ON
RAM bit data- = 0:OFF
· Applicable LCD duty: 1/32~1/64
· LCD driving voltage: 8V~17V(VDD-VEE)
· Power supply voltage: + 5V±10%
· High voltage CMOS process.
· 100QFP and bare chip available.

You might also like