BME 361 Biomeasurement Laboratory Demonstration Biomedical Engineering Program University of Rhode Island June 10, 2015

Similar documents
unsigned char ReadADC() { /************* start A/D, read from an A/D channel *****************/ unsigned char ADC_VALUE;

unsigned char ReadADC() { /************* start A/D, read from an A/D channel *****************/ unsigned char ADC_VALUE;

Electromyogram Based Controls Acting on Lego Mindstorm Car

Embedded systems. Exercise session 3. Microcontroller Programming Lab Preparation

C:\Users\cunningh\StaysOnPC\ME430 Downloads & Projects\exam2_problem1\problem1Cunningham.c

Speed Control of a DC Motor using Digital Control

Timer0..Timer3. Interrupt Description Input Conditions Enable Flag

Interrupts on PIC18F252 Part 2

Human Response Timer

/*Algorithm: This code display a centrifuge with five variable speed RPM by increaseing */

Accurate Time and Interrupts

Laboratory Exercise 5 - Analog to Digital Conversion

The University of Texas at Arlington Lecture 21_Review

Embedded Systems Programming and Architectures

Remote Controlled KitchenAid Mixer for the Clients at ADEC. Appendices. Arnaud Bacye Karina Dubé Justin Erman Matthew Martin

Interrupts on PIC18F252 Part 2. Interrupts Programming in C Language

Analog Output with a Digital to Analog Converter

1 Introduction to Computers and Computer Terminology Programs Memory Processor Data Sheet Example Application...

EEE394 Microprocessor and Microcontroller Laboratory Lab #6

LCD. Configuration and Programming

Embedded Systems Module. 6EJ505. C Tutorial 3: using the ICD3 rev tjw

Outlines. PIC Programming in C and Assembly. Krerk Piromsopa, Ph.D. Department of Computer Engineering Chulalongkorn University

Capture Mode of Pic18F252

MicroToys Guide: PS/2 Mouse N. Pinckney April 2005

Interfacing PIC Microcontrollers. ADC8BIT2 Schematic. This application demonstrates analogue input sampling

1 Introduction to Computers and Computer Terminology Programs Memory Processor Data Sheet... 4

Timer1 Capture Mode:

C and Embedded Systems. So Why Learn Assembly Language? C Compilation. PICC Lite C Compiler. PICC Lite C Optimization Results (Lab #13)

BME 4900 Page 1 of 2. Meeting 2: Personal Progress Report 12/2/09 Team 12 with Drew Seils. Semester One Week Two

ELCT 912: Advanced Embedded Systems

ECE Homework #10

EE6008-Microcontroller Based System Design Department Of EEE/ DCE

Laboratory 10. Programming a PIC Microcontroller - Part II

Dept. of Computer Engineering Final Exam, First Semester: 2016/2017

EET203 MICROCONTROLLER SYSTEMS DESIGN Serial Port Interfacing

Using Timers of Microchip PIC18F Microcontrollers

2. (2 pts) If an external clock is used, which pin of the 8051 should it be connected to?

ME 6405 Introduction to Mechatronics

Hong Kong Institute of Vocational Education Digital Electronics & Microcontroller. 8. Microcontroller

LABORATORY MANUAL Interfacing LCD 16x2, Keypad 4x4 and 7Segment Display to PIC18F4580

Introduction to Microcontroller Apps for Amateur Radio Projects Using the HamStack Platform.

UNIVERSITY OF BOLTON SCHOOL OF ENGINEERING MSC SYSTEMS ENGINEERING AND ENGINEERING MANAGEMENT SEMESTER 2 EXAMINATION 2016/2017

EE 361L Digital Systems and Computer Design Laboratory

Team 8: Robert Blake Craig Goliber Alanna Ocampo

Lecture (09) PIC16F84A LCD interface LCD. Dr. Ahmed M. ElShafee

Chapter 11: Interrupt On Change

MPLAB SIM. MPLAB IDE Software Simulation Engine Microchip Technology Incorporated MPLAB SIM Software Simulation Engine

The Freescale MC908JL16 Microcontroller

More Fun with Timer Interrupts

3. (a) Explain the steps involved in the Interfacing of an I/O device (b) Explain various methods of interfacing of I/O devices.

SANKALCHAND PATEL COLLEGE OF ENGINEERING, VISNAGAR. ELECTRONICS & COMMUNICATION DEPARTMENT Question Bank- 1

Timer2 Interrupts. NDSU Timer2 Interrupts September 20, Background:

DPScope SE Programming Interface Description

University of Jordan Faculty of Engineering and Technology Department of Computer Engineering Embedded Systems Laboratory

By the end of Class. Outline. Homework 5. C8051F020 Block Diagram (pg 18) Pseudo-code for Lab 1-2 due as part of prelab

ALFRED MAZHINDU

16.317: Microprocessor Systems Design I Fall Exam 3 December 15, Name: ID #:

8051 Peripherals. On-Chip Memory Timers Serial Port Interrupts. Computer Engineering Timers

UNIVERSITY OF BOLTON SCHOOL OF ENGINEERING B.ENG (HONS) ELECTRICAL AND ELECTRONIC ENGINEERING EXAMINATION SEMESTER /2016

Microcontroller Overview

Implementation of Temperature Sensor on PICM4520 Microcontroller

PIC Microcontroller Introduction

UNIVERSITY OF BOLTON SCHOOL OF ENGINEERING. BEng(Hons) Electrical and Electronics Engineering SEMESTER 1 EXAMINATION 2016/2017

Lab 5: LCD and A/D: Digital Voltmeter

University Program Advance Material

Embedded System Design

ECG (EKG) Primer Jingxi Zhang ABSTRACT

MSI-P450 USER MANUAL

8051 Training Kit Lab Book

Embedded Systems. PIC16F84A Internal Architecture. Eng. Anis Nazer First Semester

CENG-336 Introduction to Embedded Systems Development. Timers

Section 13. Timer0 HIGHLIGHTS. Timer0. This section of the manual contains the following major topics:

PIC32MX150F128 Breakout Board

Chapter 9. Input/Output (I/O) Ports and Interfacing. Updated: 3/13/12

Lecture (03) PIC16F84 (2)

MEMO TO: ECE 4175 students February 8, 2008 FROM: SUBJECT: First quiz on Wednesday, February 13 Open book, open notes.

UNIVERSITY OF CALIFORNIA, SANTA CRUZ BOARD OF STUDIES IN COMPUTER ENGINEERING CMPE13/L: INTRODUCTION TO PROGRAMMING IN C SPRING 2013

Department of Electronics and Instrumentation Engineering Question Bank

MICROPROCESSORS A (17.383) Fall Lecture Outline

Table of Contents COMPANY PROFILE 1-1 SECTION 1. INTRODUCTION 1-1

Laboratory 9. Programming a PIC Microcontroller - Part I

Week1. EEE305 Microcontroller Key Points

Application Note One Wire Digital Output. 1 Introduction. 2 Electrical Parameters for One Wire Interface. 3 Start and Data Transmission

OSC Ring Type Ring or Resonator type (optional) RESET Pin No Yes

Chapter 13. PIC Family Microcontroller

Analog Conversion and MAC. Student's name & ID (1): Partner's name & ID (2): Your Section number & TA's name

Microcontroller & Interfacing

DEV-1 HamStack Development Board

MTRX3700 Mechatronics

Lab 5: LCD and A/D: Digital Voltmeter

The Atmel ATmega328P Microcontroller

EECE.3170: Microprocessor Systems Design I Summer 2017

Embedded Systems Design (630470) Lecture 4. Memory Organization. Prof. Kasim M. Al-Aubidy Computer Eng. Dept.

Interrupts. Embedded Systems Interfacing. 08 September 2011

Hardware Interfacing. EE25M Introduction to microprocessors. Part V. 15 Interfacing methods. original author: Feisal Mohammed

Serial Terminal EEPROM Interface

More PIC Programming. Serial and parallel data transfer External busses Analog to digital conversion

NH-67, TRICHY MAIN ROAD, PULIYUR, C.F , KARUR DT. DEPARTMENT OF ELECTRONICS AND COMMUNICATION ENGINEERING COURSE MATERIAL

The Atmel ATmega168A Microcontroller

// and verify that there is a sine wave with frequency <FREQUENCY> and

Transcription:

BME 361 Biomeasurement Laboratory Demonstration Biomedical Engineering Program University of Rhode Island June 10, 2015 The functional units on this demonstration bread board include a PIC18F4525 processor, a D/A converter, an LCD display, a waveform/noise generator, an analog ECG simulator, and an ECG amplifier. The system is programmed with the MPLab IDE v8.89 via the ICD 3 programmer. The schematics, bill of materials, and C source code are attached in the end. The breadboard is shown below with the ICD 3 programmer connected. The ECG leads are also shown. The picture on the next page shows the connectors for attaching two oscilloscope probes (D/A out and Wave out), the potentiometer for adjusting the inout voltage to A/D channel AN2, the potentiometer for adjusting the ECG gain, and the connector for attaching the ECG leads. Copyright 2015 BME, University of Rhode Island

The system has been programmed to perform the following 10 functions. Function 00 Binary counter The binary count is displayed via the 4 LED's driven by RB4-RB7. The count is also sent to the D/A converter to generate a ramp signal. The ramp can be used to verify the linearity and offset of the D/A converter. The offset can be eliminated by adjusting Vref- (pin 12) of the DAC0800. Currently this voltage is set at 1.2 V with a voltage divider circuit (1.5 KΩ and 4.7 KΩ). Function 01 ECG Simulation Digitally simulated ECG is shown on channel 2 (cyan), compared to a real ECG waveform recorded from a human subject on channel 1 (orange). The timer TMR0 is set to generate interrupts at 1KHz, which provides a temporal resolution of 1 ms for the synthetic ECG waveform. Function 02 Echo (A/D D/A) The analog signal channel 1 (orange) is from the on-board waveform generator that produces a 9-Hz square wave mixed with simulated 60 Hz noise. The analog signal is acquired by the on-chip D/A via channel AN0 (pin 2) at a sampling rate of 240 Hz and directly outputted to the D/A converter as shown on channel 2 (cyan).

Function 03 Echo @ fs 17 Hz The sampling rate can be adjusted via the 100 KΩ potentiometer connected to AN2 (pin 4). A 47 µf capacitor is temporarily connected between AN0 (pin 2) and ground to change the square wave closer to a sine wave. A set of sampling rates ranging from 16 Hz to 228 Hz is programmed into the processor. The purpose is to observe the the effect of sampling rate. For example, aliasing is demonstrated in the right figure with a sampling rate of 17 Hz, which is just below the Nyquest sampling rate for the 9 Hz signal. The next figure shows the result with a sampling rate of 70 Hz. Function 04 Derivative A digital differentiator is implemented according to the following backward difference: y[n] = x[n] x[n-1] The sampling rate is 240 Hz.

Function 05 Low-pass filter An FIR low-pass filter is implemented according to the following equation: y[n] = (x[n] + 2*x[n-1] + x[n-2) / 4 The sampling rate is 240 Hz. Function 06 Hi-freq enhance An FIR high-frequency enhancement filter is implemented according to the following equation: y[n] = 2*x[n] - (x[n] + 2*x[n-1] + x[n-2) / 4 The sampling rate is 240 Hz. Function 07 60Hz notch filtr An FIR 60-Hz notch filter is implemented according to the following equation: y[n] = (x[n] + x[n-2) / 2 The sampling rate is 240 Hz.

Function 08 Median filter A median filter is implemented by performing a bubble sort of the present sample point and the past 8 sample points. The output is the median of the 9 points. The result in the figure shows the effect of edge-preserving smoothing of the median filter. The 60 Hz noise is eliminated, while the edges are preserved. The filter output shows a delay of 25 ms, which is longer than those of the previous filters. Function 09 HR: 74 bpm A heart meter is implemented by executing the multiplication of backward differences (MOBD) algorithm for QRS detection. The sampling rate is set at 200 Hz. Figure on the right shows the ECG signal recorded from a human subject and the nonlinear transform of the MOBD algorithm. The MOBD algorithm significantly enhances the signal-to-noise ratio readily for the threshold detection. Except for the QRS complex other components such as the P wave, the T wave, and the noise are reduced to the baseline. The next figure shows the analog simulated ECG and the MOBD nonlinear transform. The simulated ECG contains 60 Hz noise, which is effectively eliminated by the MOBD algorithm.

/*********************************************************************************************/ /* BME 361 Biomeasurement Lab - PIC18F4525 Demo */ /* Laboratories 1-8: A/D, D/A, LCD display, ECG simulation, filters, QRS detection */ /* Instructors: John DiCecco, Ying Sun */ /* Latest update: 6/06/2015 */ /*********************************************************************************************/ /**************************** Specify the chip that we are using *****************************/ #include <p18cxxx.h> #include <timers.h> #include <math.h> #include <stdlib.h> /************************* Configure the Microcontroller PIC18f4525 **************************/ #pragma config OSC = XT #pragma config WDT = OFF #pragma config PWRT = OFF #pragma config FCMEN = OFF #pragma config IESO = OFF #pragma config BOREN = ON #pragma config BORV = 2 #pragma config WDTPS = 128 #pragma config PBADEN = OFF #pragma config DEBUG = OFF #pragma config LVP = OFF #pragma config STVREN = OFF #define _XTAL_FREQ 4000000 /******************************** Define Prototype Functions *********************************/ unsigned char ReadADC(); void rx_int (void); void _highpriorityint(void); void Backlight(unsigned char state); void SetPosition(unsigned char position); void PrintLine(rom unsigned char *string, unsigned char numchars); void PrintInt(int value, unsigned char position); void PrintNum(unsigned char value1, unsigned char position1); void SetupSerial(); void ClearScreen(); void SetupADC(unsigned char channel); void Delay_ms(unsigned int x); /************************************** Global variables *************************************/ unsigned char function, mode, update, debounce0, debounce1, LEDcount, output, counter, i, j; unsigned char data0, data1, data2, array[9], rank[9], do_median, do_mobd, refractory, display; unsigned char temp, sampling[16], TMRcntH[16], TMRcntL[16], sampling_h, sampling_l; int dummy, d0, d1, d2, mobd, threshold, rri_count, hr; unsigned char ReadADC() { /************* start A/D, read from an A/D channel *****************/ unsigned char ADC_VALUE; ADCON0bits.GO = 1; // Start the AD conversion while(!pir1bits.adif) continue; // Wait until AD conversion is complete ADC_VALUE = ADRESH; // Return the highest 8 bits of the 10-bit AD conversion return ADC_VALUE; #pragma code _highpriorityint=0x8 void rx_int (void){ /****************** Set up highpriority interrup vector ******************/ _asm goto _highpriorityint _endasm Page 1 of 8

#pragma interrupt _highpriorityint void _highpriorityint(void) { /********** high priority interrupt service routine ************/ checkflags: if (INTCONbits.TMR0IF == 1) { // When there is a timer0 overflow, this loop runs INTCONbits.TMR0IE = 0; // Disable TMR0 interrupt INTCONbits.TMR0IF = 0; // Reset timer 0 interrupt flag to 0 if (debounce0!= 0) debounce0--; // switch debounce delay counter for INT0 if (debounce1!= 0) debounce1--; // switch debounce delay counter for INT1 TMR0H = 0xF0; // Reload TMR0 for 4.167 ms count, Sampling rate = 240 Hz TMR0L = 0x7C; // 0xFFFF-0xEFB8 = 0x1047 = 4167, adjust for delay by 68 us switch (function) { case 1: // Function 1: ECG simulation TMR0H = 0xFC; // Reload TMR0 for 1 ms count, sampling rate = 1KHz TMR0L = 0xFA; // 0xFFFF-0xFC17 = 0x3E8 = 1000,adust for delay by 227 us PORTBbits.RB4 =!PORTBbits.RB4; // Toggle RB4 (pin 37) for frequency check switch (mode) { case 0: // P wave up counter++; output++; if (counter == 30) mode++; case 1: // P wave flat counter--; if (counter == 0) mode++; case 2: // P wave down counter++; output--; if (counter == 30) mode++; case 3: // PR segment counter++; if (counter == 100) mode++; case 4: // QRS complex - Q counter++; output -= 3; if (counter == 105){ counter = 0; mode++; case 5: // QRS complex - R up counter++; output += 6; if (counter == 30) mode++; case 6: // QRS complex - R down counter++; output -= 6; if (counter == 62) mode++; case 7: // QRS complex - S counter++; output += 3; if (counter == 71) { mode++; counter = 0; case 8: // ST segment counter++; if (counter == 89){ counter = 0; mode++; case 9: // T wave up counter++; output++; if (counter == 55)mode++; case 10: // T wave flat counter++; if (counter == 110) mode++; Page 2 of 8

case 11: // T wave down counter++; output--; if (counter == 165) mode++; case 12: // End ECG counter--; if (counter == 0) mode++; case 13: // Reset ECG counter++; if (counter == 202){ counter = mode = 0; output = 50; PORTD = output; case 2: // Function 2: Echo data0 = ReadADC(); // Read A/D and save the present sample in data0 PORTD = data0; // Echo back PORTBbits.RB4 =!PORTBbits.RB4; // Toggle RB4 (pin 37) for frequency check case 3: // Function 3: Echo (vary rate) TMR0H = sampling_h; // Reload TMR0 high-order byte TMR0L = sampling_l; // Reload TMR0 low-order byte data0 = ReadADC(); // Read A/D and save the present sample in data0 PORTD = data0; // Echo back PORTBbits.RB4 =!PORTBbits.RB4; // Toggle RB4 (pin 37) for frequency check case 4: // Function 4: Derivative data1 = data0; // Store previous data points data0 = ReadADC(); // Read A/D and save the present sample in data0 dummy = (int)data0 - data1 + 128; // Take derivative and shift to middle PORTD = (unsigned char)dummy; // Output to D/A case 5: // Function 5: Low-pass filter data2 = data1; // Store previous data points data1 = data0; data0 = ReadADC(); // Read A/D and save the present sample in data0 dummy = ((int)data0 + data1 + data1 + data2) / 4; // smoother PORTD = (unsigned char)dummy; // Output to D/A case 6: // Function 6: High-frequency enhancement filter data2 = data1; // Store previous data points data1 = data0; data0 = ReadADC(); // Read A/D and save the present sample in data0 dummy = ((int)data0 + data1 + data1 + data2) / 4; // smoother dummy = data0 + data0 - dummy; PORTD = (unsigned char)dummy; // Output to D/A case 7: // Function 7: 60Hz notch filter data2 = data1; // Store previous data points data1 = data0; data0 = ReadADC(); // Read A/D and save the present sample in data0 dummy = ((int)data0 + data2) / 2; // 60 Hz notch PORTD = (unsigned char)dummy; // Output to D/A case 8: // Function 8: Median filter data0 = ReadADC(); // Read A/D and save the present sample in data0 do_median = 1; // Flag main() to do median filter Page 3 of 8

case 9: // Function 9: Heart rate meter TMR0H = 0xED; // Reload TMR0 for 5 ms count, sampling rate = 200 Hz TMR0L = 0x44; // 0xFFFF-0xED44 = 0x12BB = 4795, (205 us delay) data1 = data0; // Move old ECG sample to data1 data0 = ReadADC(); // Store new ECG sample from ADC to data0 PORTBbits.RB2 =!PORTBbits.RB2; // Toggle RB2 for sampling rate check do_mobd = 1; // Flag main() to do MOBD for QRS detection INTCONbits.TMR0IE = 1; // Enable TMR0 interrupt if (INTCONbits.INT0IF == 1) { // INT0 (pin 33) negative edge INTCONbits.INT0IE = 0; // Disable interrupt INTCONbits.INT0IF = 0; // Reset interrupt flag if (debounce0 == 0) { if (function <= 0) function = 9; // Set function range 0-9 else function--; if (function == 9) SetupADC(1); // ECG comes from AN1 channel else SetupADC(0); // Others come from AN0 channel update = 1; // Signal main() to update LCD dispaly debounce0 = 10; // Set switch debounce delay counter decremented by TMR0 INTCONbits.INT0IE = 1; // Enable interrupt goto checkflags; // Check again in case there is a timer interrupt if (INTCON3bits.INT1IF == 1) { // INT1 (pin 34) negative edge INTCON3bits.INT1IE = 0; // Disable interrupt INTCON3bits.INT1IF = 0; // Reset interrupt flag if (debounce1 == 0) { if (function >= 9) function = 0; // Set function range 0-9 else function++; if (function == 9) SetupADC(1); // ECG comes from AN1 channel else SetupADC(0); // Others come from AN0 channel update = 1; // Signal main() to update LCD dispaly debounce1 = 10; // Set switch debounce delay counter decremented by TMR0 INTCON3bits.INT1IE = 1; // Enable interrupt goto checkflags; // Check again in case there is a timer interrupt void Transmit(unsigned char value) { /********** Send an ASCII Character to USART ***********/ while(!pir1bits.txif) continue; // Wait until USART is ready TXREG = value; // Send the data while (!PIR1bits.TXIF) continue; // Wait until USART is ready Delay_ms (5); // Wait for 5 ms void ClearScreen(){ /************************** Clear LCD Screen ***************************/ Transmit(254); // See datasheets for Serial LCD and HD44780 Transmit(0x01); // Available on our course webpage void Backlight(unsigned char state){ /************* Turn LCD Backlight on/off ***************/ Transmit(124); if (state) Transmit(0x9D); // If state == 1, backlight on else Transmit(0x81); // otherwise, backlight off void SetPosition(unsigned char position){ /********** Set LCD Cursor Position *************/ Transmit(254); Page 4 of 8

Transmit(128 + position); void PrintLine(rom unsigned char *string, unsigned char numchars){ /**** Print characters ****/ unsigned char count; for (count=0; count<numchars; count++) Transmit(string[count]); void PrintInt(int value, unsigned char position){ /******** Print number at position *********/ int units, tens, hundreds, thousands; SetPosition(position); // Set at the present position if (value > 9999) { PrintLine((rom unsigned char*)"over", 4); return; if (value < -9999) { PrintLine((rom unsigned char*)"under", 5); return; if (value < 0) { value = -value; Transmit(45); else Transmit(43); thousands = value / 1000; // Get the thousands digit, convert to ASCII and send if (thousands!= 0) Transmit(thousands + 48); value = value - thousands * 1000; hundreds = value / 100; // Get the hundreds digit, convert to ASCII and send Transmit(hundreds + 48); value = value - hundreds * 100; tens = value / 10; // Get the tens digit, convert to ASCII and send Transmit(tens + 48); units = value - tens * 10; Transmit(units + 48); // Convert to ASCII and send void PrintNum(unsigned char value1, unsigned char position1){ /** Print number at position ***/ int units, tens, hundreds, thousands; SetPosition(position1); // Set at the present position hundreds = value1 / 100; // Get the hundreds digit, convert to ASCII and send if (hundreds!= 0) Transmit(hundreds + 48); else Transmit(20); value1 = value1 - hundreds * 100; tens = value1 / 10; // Get the tens digit, convert to ASCII and send Transmit(tens + 48); units = value1 - tens * 10; Transmit(units + 48); // Convert to ASCII and send void SetupSerial(){ /*********** Set up the USART Asynchronous Transmit (pin 25) ************/ TRISC = 0x80; // Transmit and receive, 0xC0 if transmit only SPBRG = 25; // 9600 BAUD at 4MHz: 4,000,000/(16x9600) - 1 = 25.04 TXSTAbits.TXEN = 1; // Transmit enable TXSTAbits.SYNC = 0; // Asynchronous mode RCSTAbits.CREN = 1; // Continuous receive (receiver enabled) RCSTAbits.SPEN = 1; // Serial Port Enable TXSTAbits.BRGH = 1; // High speed baud rate void SetupADC(unsigned char channel){ /******** Configure A/D and Set the Channel **********/ TRISA = 0b11111111; // Set all of Port A as input Page 5 of 8

// ADCON2 Setup // bit 7: Left justify result of AD (Lowest 6bits of ADRESL are 0's) (0) // bit 6: Unimplemented // bit 5-3: AD aquisition time set to 2 TAD (001) // bit 2-0: Conversion clock set to Fosc/8 (001) ADCON2 = 0b00001001; // ADCON1 Setup // bit 7-6: Unimplemented // bit 5: Vref - (VSS) (0) // bit 4: Vref + (VDD) (0) // bit 3-0: Configuration of AD ports (Set A0-A4, others to digital, including the PORTB) ADCON1 = 0b00001010; // ADCON0 Setup // bit 7,6 = Unimplemented // bits 5-2 = Channel select // bit 1: GO Bit (Starts Conversion when = 1) // bit 0: AD Power On ADCON0 = (channel << 2) + 0b00000001; PIE1bits.ADIE = 0; // Turn off the AD interrupt PIR1bits.ADIF = 0; // Reset the AD interrupt flag void Delay_ms(unsigned int x){ /****** Generate a delay for x ms, assuming 4 MHz clock ******/ unsigned char y; for(;x > 0; x--) for(y=0; y< 82;y++); void main(){ /****************************** Main program **********************************/ function = mode = LEDcount = counter = debounce0 = debounce1 = 0; // Initialize display = do_median = do_mobd = rri_count = 0; threshold = 50; // Threshold for the MOBD QRS-detection algorithm update = 1; // Flag to signal LCD update output = 50; // Baseline for ECG simulation sampling[0] = 16; sampling[1] = 17; sampling[2] = 18; sampling[3] = 19; sampling[4] = 20; sampling[5] = 25; sampling[6] = 30; sampling[7] = 50; sampling[8] = 70; sampling[9] = 88; sampling[10] = 108; sampling[11] = 126; sampling[12] = 145; sampling[13] = 173; sampling[14] = 192; sampling[15] = 228; TMRcntH[0] = 11; TMRcntH[1] = 26; TMRcntH[2] = 38; TMRcntH[3] = 50; TMRcntH[4] = 60; TMRcntH[5] = 99; TMRcntH[6] = 125; TMRcntH[7] = 177; TMRcntH[8] = 200; TMRcntH[9] = 212; TMRcntH[10] = 220; TMRcntH[11] = 225; TMRcntH[12] = 229; TMRcntH[13] = 234; TMRcntH[14] = 236; TMRcntH[15] = 239; TMRcntL[0] = 219; TMRcntL[1] = 55; TMRcntL[2] = 251; TMRcntL[3] = 103; TMRcntL[4] = 175; TMRcntL[5] = 191; TMRcntL[6] = 201; TMRcntL[7] = 223; TMRcntL[8] = 49; TMRcntL[9] = 151; TMRcntL[10] = 124; TMRcntL[11] = 242; TMRcntL[12] = 244; TMRcntL[13] = 75; TMRcntL[14] = 119; TMRcntL[15] = 184; sampling_h = 0xF0; // initialize for 4.167 ms count, Sampling rate = 240 Hz sampling_l = 0x7C; // 0xFFFF-0xEFB8 = 0x1047 = 4167, adjust for delay by 68 us TRISB = 0b00000011; // RB0-1 as inputs, others outputs, RB3 drives buzzer TRISD = 0b00000000; // Set all port D pins as outputs PORTD = 0; // Set port D to 0's SetupADC(0); // Call SetupADC() to set up channel 0, AN0 (pin 2) SetupSerial(); // Set up USART Asynchronous Transmit for LCD display T0CON = 0b10001000; // Setup the timer control register for interrupt INTCON = 0b10110000; // GIE(7) = TMR0IE = INT0IE = 1 INTCONbits.TMR0IE = 1; // Enable TMR0 interrupt INTCON2bits.INTEDG0 = 0; // Set pin 33 (RB0/INT0) for negative edge trigger INTCON2bits.INTEDG1 = 0; // Set pin 34 (RB1/INT1) for negative edge trigger INTCONbits.INT0IE = 1; // Enable INT0 interrupt INTCON3bits.INT1IE = 1; // Enable INT1 interrupt Delay_ms(3000); // Wait until the LCD display is ready Backlight(1); // turn LCD display backlight on Page 6 of 8

ClearScreen(); // Clear screen and set cursor to first position PrintLine((rom unsigned char*)" BME 361 Demo", 14); SetPosition(64); // Go to beginning of Line 2; PrintLine((rom unsigned char*)" Biomeasurement",15); // Put your trademark here Delay_ms(3000); ClearScreen(); // Clear screen and set cursor to first position PrintLine((rom unsigned char*)"function", 8); while (1) { if (update) { // The update flag is set by INT0 or INT1 update = 0; // Reset update flag PrintNum(function, 8); // Update the function number on LCD display if (function!= 0) LEDcount = PORTB = 0; // Reset LED's SetPosition(64); // Go to beginning of Line 2; switch (function) { case 0: PrintLine((rom unsigned char*)"binary counter ",16); case 1: PrintLine((rom unsigned char*)"ecg simulation ",16); case 2: PrintLine((rom unsigned char*)"echo (A/D - D/A)",16); case 3: PrintLine((rom unsigned char*)"echo @ fs Hz",16); case 4: PrintLine((rom unsigned char*)"derivative ",16); case 5: PrintLine((rom unsigned char*)"low-pass filter ",16); case 6: PrintLine((rom unsigned char*)"hi-freq enhance ",16); case 7: PrintLine((rom unsigned char*)"60hz notch filtr",16); case 8: PrintLine((rom unsigned char*)"median filter ",16); case 9: PrintLine((rom unsigned char*)"hr = bpm ",16); switch (function) { case 0: // Function 0: Binary counter LEDcount++; // Upcounter PORTB = LEDcount & 0b11110000; // Mask out the lower 4 bits PORTD = LEDcount; // Output ramp to verify linearity of the D/A Delay_ms(10); // Delay to slow down the counting case 3: // Function 3: Echo (vary rate) INTCONbits.TMR0IE = 0; // Disable TMR0 interrupt SetupADC(2); // Switch to A/D channel AN2 counter = ReadADC(); // Read potentiometer setting from AN2 SetupADC(0); // Switch to A/D channel AN0 counter = counter >> 4; // Scale it to 0-15 temp = sampling[counter]; // Display sampling rate PrintNum(temp,74); sampling_l = TMRcntL[counter]; // Load TMR0 low-order byte sampling_h = TMRcntH[counter]; // Load TMR0 high-order byte INTCONbits.TMR0IE = 1; // Enable TMR0 interrupt Delay_ms(1000); // Delay to slow down the counting case 8: // Function 8: Median filter (9-point) if (do_median) { do_median = 0; // Reset do_median flag INTCONbits.TMR0IE = 0; // Disable TMR0 interrupt for (i=8; i>0; i--) array[i] = array[i-1]; // Store the previous 8 points array[0] = data0; // Get new data point from A/D for (i=0; i<9; i++) rank[i] = array[i]; // Make a copy of data array for (i=0; i<5; i++) { // Perform a bubble sort for (j=i+1; j<9; j++) { if (rank[i] < rank[j]) { temp = rank[i]; // Swap rank[i] = rank[j]; rank[j] = temp; Page 7 of 8

PORTD = rank[4]; // Median is at rank[4] of rank[0-8] INTCONbits.TMR0IE = 1; // Enable TMR0 interrupt case 9: // Function 9: Multiplication of Backward Differences (MOBD) if (do_mobd){ // MOBD = Multiplication of Backwards Differences INTCONbits.TMR0IE = 0; // Disable TMR0 interrupt do_mobd = 0; // Reset new_data flag d2 = d1; // Move oldest difference to d2 d1 = d0; // Move older difference to d1 d0 = (int)data0 - data1; // Store new difference in d0, (int) casting important rri_count++; // Increment RR-interval mobd = 0; // mobd = 0, unless sign consistency is met: if (d0 > 0 && d1 > 0 && d2 > 0){ // (1) If 3 consecutive positive differences mobd = d0 * d1; // Multiply first two differences mobd = mobd >> 3; // Scale down (divide by 8) mobd = mobd * d2; // Multiply the oldest difference if (d0 < 0 && d1 < 0 && d2 < 0){ // (2) If 3 consecutive negative differences d0 = -d0; // Take absolute value of differences d1 = -d1; d2 = -d2; mobd = d0 * d1; // Multiply first two differences mobd = mobd >> 3; // Scale down (divide by 8) mobd = mobd * d2; // Multiply the oldest difference if (refractory){ // Avoid detecting extraneous peaks after QRS refractory++; if (refractory == 40){ // Delay for 200 ms refractory = 0; // Reset refractory flag to 0 PORTBbits.RB3 = 0; // Turn buzzer/led off (Pin 36) else if (mobd > threshold){ // If a peak is detected, refractory = 1; // Set refractory flag PORTBbits.RB3 = 1; // Turn buzzer/led on (Pin 36) display = 1; // Set display flag PORTD =(unsigned char)mobd; // Output mobd value to Port D INTCONbits.TMR0IE = 1; // Enable TMR0 interrupt if (display){ // Display Heart Rate in 3 digits hr = 12000/rri_count; // 60/0.005 = 12000 rri_count = 0; // Reset RRI counter PrintNum(hr, 71); // Isolates each digit and displays display = 0; // Reset display flag Page 8 of 8