Sei sulla pagina 1di 25

Thermistor Respiratory Monitor

A low-cost, easy-to-use device to monitor the breathing of patients.

Our final project for ECE 4760 is a respiratory monitor that was designed for low-resource environments. The
device calculates a patient's breathing rate by detecting changes in temperature when the patient breathes
through a mask. Features of the device include an alarm through a piezoelectric speaker which goes off when
the patient stops breathing and a low-battery indicator signal when the battery powering the device reaches a
threshold voltage.

Prototype of our Thermistor Respiratory Monitor

Project Overview
Motivation

For our project, we wanted to create a solution to a real-world problem. Many biomedical devices, such as
commercially available respiratory monitors, are designed for the developed world and require a stable power-
supply to operate. We wanted to implement a solution that is adaptable to different environments.

Our device uses several concepts we learned not only this semester in ECE 4760, but also in other classes at
Cornell. We use analog-to-digital conversions to sample readings from both the thermistor and the battery,
pulse-width modulation to generate a signal for the speaker, and timers to switch between tasks. Our
implantation also includes various analog circuitry for voltage regulation and signal amplification. Developing a
robust and accurate respiratory monitor served to be a challenging, yet rewarding experience.

Previous Work

When doing research for this project, we came across several respiratory monitors using different sensors for
measuring breathing. Other methods of monitoring respiratory rate include a chest force sensor (to detect force
produced by chest movement), impedance pneumograph (which uses skin electrodes to calculate transthoracic
impedance), and pulse oximetry (which measures O2 saturation in blood). While commercial devices to monitor
respiratory rate exist, they use motion or force sensors to determine respiration rate. These monitors tend to be
better suited for developed countries where a stable power supply is not a concern. We want to design our
device geared to the needs of undeveloped areas. Therefore, we aimed for our thermistor monitor to be
comparatively cheaper, easier to use, as well as reliable.

We also discovered another thermistor respiratory monitor research project. The design of the device differed
from ours, in that it included two thermistors; one that remains in the outside environment for reference and one
that goes inside the nostril.

High-Level Block Diagram

1. Thermistor Measurements

The resistance over the thermistor drops when its surrounding temperature increases, and goes back down
when the temperature decreases. The voltage also, accordingly drops when a person exhales and rises when a
person inhales. We use an operational amplifier to make the changes in temperature more apparent. The output
of the amplifier is read into the microcontroller's ADC channel 0.

2. Voltage Measurements

Under battery operation, our device takes a voltage sample across the battery about every minute. Since our
device uses a 9V battery, a voltage divider is used to drop the voltage so that it is about 2.2V at PINA1 (ADC
channel 1).

3. Power

Our device can be powered through either a standard AC power suppy or a single 9V battery.

4. Turn On Display

To conserve power, the user must hold down a button to turn on the display to read the respiration rate. This
prevents the user from leaving the display on by mistake and draining the battery.

5. Output Sound
There are two different alarms for our device generated by a piezoelectric speaker. The first is higher in pitch
and alarms if the patient is not breathing. The second is a signal that the device is running on low battery. While
the first alarm is a continuous sound, the low-battery alert is a one second long tone.

6. Respiration Rate Display

Breathing rate is measured and displayed in breaths/min on an LCD. This allows whoever is monitoring the
patient to see if the patient is breathing too fast or too slowly.

7. Measurement

This task does the actual calculation of the respiration rate using samples from ADC0.

Design Decisions

Microcontroller

When choosing a microcontroller for this project, we first wanted to find the smallest microcontroller possible
so that it could be mounted right onto the device's mask. However, when identifying how many I/O ports and
ADC channels would be neccessary for our project, we realized that the AtMega1284p microcontroller that we
had been using all semester would be ideal.

Thermistor

During the initial stages of our project design we experimented with several thermistors and their placement on
the device. Since a drastic temperature difference is not produced when a human breathes, we needed the
smallest, and therefore most sensitive thermistor we could find. Larger thermistors we experimented with either
could not detect a "breath" or had a very slow response time. We also thought a mask around the patient's nose
and mouth would be the least intrusive placement of the thermistor.

Display

Because we wanted to design our device to be low-powered, choosing the right display for the respiratory rate
was a concern. Orignally, we planned to use two 7-segment displays which turned on when the user pressed a
button. However, we decided to use an LCD screen for a nicer display. In order to reduce power usage, the LCD
only receives power when the user presses the display button, which reduces the load on the battery.

Standards

As a medical device, this respiratory monitor must meet regulatory requirements outlined by governments. This
includes meeting HIPPA standards which protects individuals medical records and other personal health
information, including respiration rate. However, since we are not planning to store this information, this
regulation should not be an issue. Before it can be sold commercially in the United States, the device would also
need FDA approval.
Hardware Design & Implementation

There are six different hardware components necessary to make the respiratory monitor work. The six
components are listed below and are described in that order.

1. Thermistor Measurement
2. Thermistor Amplification
3. Analog Comparator
4. Capacitor Charger
5. LCD Display Button
6. Battery Monitor

Use of device

1. Thermistor Measurement

We used a thermistor based measuring system to measure the breathing rate of the patients. The thermistor is
mounted inside a mask which is worn by the patient. The mask is a standard nebulizer mask that is used by
asthma patients. The thermistor is mounted inside the mask so that it is directly in front of the patients mouth.
As the patient breaths the hot air from the patients breath changes the resistance (Rth) of the thermistor. As a
result, the voltage across the thermistor (Vth) will also change proportionally to how the patient breaths. We can
therefore use Vth as an indirect measurement of how the patient is breathing. To measure Vth we used a voltage
divider as shown below in Figure 1.

Figure 1: Thermistor Measurement Circuit


When the thermistor is at room temperature its resistance is approximately 1.2 k so we choose R1 to be 1 k.
This was chosen so that Vth would be about 2.5 V if no one was breathing on the thermistor. We wanted to
have 2.5 V be our no breathing voltage so that extreme inhalations or exhalations could be detected with our
device.

2. Thermistor Amplification

The thermistor we choose for our project was the NTHS0402 from Vishay Dale. This thermistor has a thermal
time constant of 5 seconds which makes it extremely sensitive. During experimentation we found that the
change in voltage from a person breathing on the thermistor was on the order of 10 mV. Since this was too
small of a change to work with we decided to amplify this change by using an Op Amp with a high pass filter.
An Op Amp alone will not suffice because it will amplify the entire signal whereas we only want to amplify the
change in voltage. Literature research shows that the average adult male breaths about 15-20 breaths/min and
the average infant breaths about 30-60 breaths/min. Our slowest desired period is about 4 seconds. We choose
the time constant for our high pass filter to be 22 seconds. This means that any signal with a period less than 22
seconds will be amplified by our op amp. We choose 22 seconds so that our device could detect
hyperventilation as well as normal breathing. The time constant was set to 22 seconds by choosing R2 to by 10
k and C1 to 2200 F as seen in Figure 2. The gain was then set to 4 by choosing R3 to be 30 k.

Figure 2: Thermistor Amplification Circuit

The output of this circuit goes to both PortA0 and PortB2. PortA0 is the input to the ADC while PortB2 is the +
reference pin for the AC (see software design section).

3. Analog Comparator

The respiratory rate is measured by using an analog comparator (AC) to compare the amplified voltage across
the thermistor to a reference voltage. Since the respiratory signal is approximately equivalent to a sine wave
with a DC offset, we choose the reference voltage to be at the DC offset. However, patients of different ages
will have different steady state breathing temperatures. For example older patients will breath out more hot air
than younger patients. This means that the DC offset will be lower than those of younger patients. To
automatically determine the reference voltage for a patient we used a low pass filter shown below in Figure 3.
This low pass filter has a time constant of 22 seconds which means it rejects all of the signals we are interested
in. Since it rejects the respiratory signal the output of this filter must be at the average value of the incoming
signal. As the incoming signal is equivalent to a sine wave with a DC offset, the average value of the signal is
the DC. The output of this filter than goes into the voltage reference pin of the AC as shown in Figure 3. The
respiratory signal comes from PortB2 and goes into both the filter and positive voltage pin of the AC of the
MCU. To achieve a time constant of 22 seconds R4 is 100 k and C2 is 220 F.

Figure 3: Analog Comparator Circuit

4. Capacitor Charger

The time constant for the thermistor amplification circuit described above is 22 seconds which means it takes
several minutes for the capacitor to charge up. During this charging period the device does not work properly
because the capacitor is not fully charged. To decrease this charging time another circuit was added with a
smaller resistor so that the time constant for charging would be faster. The charging capacitor schematic is
shown below in Figure 4. When PortB4 and PortB5 are set to 5 V and 0 V respectively (see software design for
further details) the capacitor begins to charge through R6. Both R5 and R6 are 330 so that they would
provide as little resistance as possible. The time constant for charging then becomes 0.73 seconds and the total
time for charging is about 6 seconds. R5 is necessary to provide protect for PortB4 so that it is not damaged.

Figure 4: Charging Capacitor Circuit


5. LCD Display Button

In order to conserve power the user must hold down a button to turn the LCD on. This way the LCD is not on
when it is not being used and it cannot be left on by mistake. This was accomplished by connecting the LCD
Vcc pin to the MCU Vcc via a push button as shown in Figure 5. When the button is pushed the LCD Vcc pin
receives 5 V. Resistor 10 and Rsesistor 7 are needed to ensure that the voltage at the pink node is below the
threshold voltage of the MCU pins. This way PortB1 will read 0 V when the button isnt pushed and 5 V when
the button is pushed. Both R10 and R7 are 330 .

Figure 5: LCD Display Button Circuit

The LCD we used the MDL(s)-16264 LCD from Vartronix along with a 10 k trimpot to control the contrast.

6. Battery Monitor

To measure the voltage of the 9 V battery we used a voltage divider along with a low pass filter as shown in
Figure 6 below. The voltage divider is used to convert the voltage range from 0-9 V to 0-5 V so that the MCU
will not be damaged. The ADC is then used convert the analog voltage to a digital format so that the MCU and
analyze it. The purpose of the low pass filter is to eliminate any high frequency signals that might occur. The
time constant of the filter is 1 s and is achieved by setting C2 to 1 nF and R8 to 1 k. R9 is set to 3 k so that
the voltage divider outputs of the original voltage.

Figure 6: Battery Measurement Circuit


Software Design & Implementation

Software Overview

The C programming language was used to program the MCU using the AVR Studio version 4.15 and was
compiled using the WINAVR GCC C compiler. The software for the respiratory monitor contains 5 different
tasks and 2 interrupt service routines (ISR). The first ISR is the Timer0 Compare Match Vector. This ISR
activates every 16.4 ms and decrements three different timeout counters whenever it activates. It also zeroes the
variable count (used to measure respiration rate) to prevent it from overflowing. Since the clock of the MCU is
16 MHz, we set the prescaler to 1024 and OCR0A to 255. Setting OCR0A to 255 means the ISR will activate
every 256 clk cycles. The second ISR used was the AC ISR. This ISR activates when the onboard AC outputs a
1. This ISR was used to measure the respiration rate of the person. This will be described in detail below in the
appropriate section.

Main

The main function initializes the device and then goes into the main loop. Both the low pass and high pass
filters have very large time constants (22 seconds) and capacitors. This means that they will take an extremely
long time to charge before the overall device can begin to work. This start up time was on the order of minutes,
which was too long in our opinion. To solve this problem we created a separate circuit that would charge our
capacitors using much smaller time constants. This was done in the first 10 lines of code in main before any
other function was initialized. First, PinB5 and B4 are set to output. B5 is set to 0 V and B4 is set to 5 V. This
essentially turns on the Vcc and ground for the charging capacitor circuit. The charging capacitor circuit is left
on for 5 seconds before it is turned off. It is turned off by switching B4 and B5 to inputs. Afterwards the
initialize functions are turned on and the while(1) loop begins.

The while-loop goes through a series of conditions to see if the device needs to call one of the tasks. The
thermistor and sampling tasks are both timed, so if either timer count equals 0, the counter is reset and the task
is called. The thermistor sampling task is called every second, while the battery sampling task is called every
minute. The measure task is called when the measure_flag indicates that a measurement is ready to be taken.
Lastly, the display task activates when the user holds down the display button.

Thermistor Sampling

Samples from the thermistor are taken through PINA0. First, the ADMUX register is set to turn on the left
adjust result and external reference. The MCU then waits for a conversion to occur, and then sets the variable
sample_old to the current sample and the sample variable to the high bits from the ADC. The absolute value of
the difference between sample_old and sample is used to determine if the patient has stopped breathing. If this
difference is less than or equal to 8, then this is considered a "no breath". If the MCU detects 10 consecutive "no
breaths", a PWM signal is generated to produce a sound from the speaker. If the difference is above the
tolerance, then the "no breath" counter variable c is reset to 0 and the PWM signal is set to be turned off.

The tolerance for the difference between breaths was found through experimentation. Looking at the ADC
values on the UART, the range of the sampled values was 6-8 when a person was not breathing. To compensate
for this fluctation, we compare the difference between consecutive thermistor samples against 8 instead of 0.
We also decided to have a counter to keep track of the number of "no breaths" detected because depending on
how a person is breathing, consecutive samples could fall into the tolerance range. Counting up to 10
consecutive "no breaths" drastically reduces the number of false positive alarms.

ADC sampling and PWM signal generation were concepts we learned earlier in the semester in Labs 2 and 3.
Battery Sampling

To take a sample from the battery, ADC channel 1 on PINA1 is turned on by setting the ADMUX register. The
MCU waits for a conversion to occur and then sets the variable bat_samp to the high bits from the ADC. The
sample is then compared to the ADC value that was found to correspond to a voltage of 7.6 V. During testing it
was found that device operation becomes unstable at 7.5 V. Therefore, we decided to warn the user when the
voltage of the battery becomes 7.6 V

If the battery sample is found to be less than or equal to the tolerance, then a PWM signal is generated, left on
for about 1 second, and then is turned off. The PWM channel which produces a tone for a low-battery warning
has a larger prescalar than the PWM channel to produce the alarm for when a patient is not breathing, making
the low-battery tone lower in pitch.

Measuring Task

The respiratory rate is measured by using the AC and measuring the time difference between when the voltage
across the thermistor crosses the reference voltage. The first part of this is done in the ISR. When the voltage
across the thermistor crosses the reference voltage the AC ISR activates. It then stores the number of clk cycles
into the variable tau2 and stores the number of clk cycles of the previous crossing in tau1. The difference
between tau2 and tau1 is then stored in the variable period. In essence this ISR measures the difference in the
number of clk cycles between two reference voltage crossings. Once this measurement is done the measure flag
is set and the measure task is activated. In the measure task an average difference in clk cycles is calculated by
doing a weighted average between the newly measured difference in clk cycles and the previous average
difference in clk cycles. The weights are 0.75 of the previous average and 0.25 of the new difference. This
weighted average is taken so that an average respiratory rate can be calculated. This average difference in clk
cycles is then converted to respiratory rate in breaths per a min (bpm). This is done by converting the difference
in clk cycles to minutes and taking the reciprocal. We take the reciprocal because the AC ISR is activated every
breath, i.e. it measures the time for one breath.

While writing this code we ran into the problem that the AC ISR was activating several times per one crossing.
This was happening because as our signal crossed the reference voltage the noise in our system caused the
signal to cross the reference voltage several more times. To solve this when the AC ISR is activated the first
time the first line in the ISR turns off the AC ISR and the flag called flag is set. Then in main there is an if
statement that says if flag is set to 1, wait 10 ms, turn on the AC ISR and reset flag. The 10 ms delay is used to
wait for our signal to cross the reference voltage far enough so that there is no interference from the noise.

Display Task

The display function is used to display the respiratory rate on the LCD when the user is holding down the LCD
push button. This was achieved by having the display function activate when PinB1 was read as 1. When PinB1
is 1 the LCD is initialized and the value of the respiratory rate stored in the variable breathrate and written to the
LCD buffer. This value was then displayed on the LCD.
Display of respiration rate

External Project Code

Author Description
Scienceprog.com LCD library-used for displaying the respiration rate

File Breakdown

Name Description
resp_monitor.c ATmega1284p implemenation of thermistor respiratory monitor
lcd_lib.c ATmega1284p source code for display onto an LCD
lcd_lib.h ATmega1284p header for display onto an LCD

Conclusions

Results

The major results of our design are as follows:

Measures respiratory rate with error of less than 10%


Takes less than 5 seconds to determine if patient isnt breathing
Startup time is about 30 seconds
LCD only receives power when the LCD button is held down
Piezoelectric speaker is activated when patient isnt breathing or the battery is below 7.5 V. Different sounds are
used for not breathing vs low battery
Speaker is automatically turned off when the patient begins to breathe again
Runs off of 9 V battery

Final Thoughts

Our device met the original design specifications we set out to accomplish at the beginning of this project. We
were able to accurately measure the respiratory rate, determine if someone was breathing or not and output an
alarm if he or she wasnt, measure a battery and output an alarm if it was too low and turn on the LCD only
when a button was being held down. We were surprised that we were able to get the piezoelectric speaker to go
off in under 5 seconds when we stopped breathing into the mask. This is an extremely important
accomplishment because it is imperative to know as soon as possible when a patient is not breathing. We were
also surprised by how much lag there was in our system. At first our device took several minutes to starting
amplifying the voltage across the thermistor but once we determined the cause we were able to get that down to
under 5 seconds. Unfortunately there is still some lag between when the voltage across the thermistor begins to
be amplified and when the device begins measuring respiratory rate. Fortunately this lag is very small so it is
still acceptable but in our next iteration of this design we would like to minimize this. We believe it has to do
with the low pass filter that creates the reference voltage for the device but this theory needs to be tested. We
would also like to increase the accuracy of the measured respiratory rate. This can be done by either fine tuning
the weights or by using a different averaging method. We dont think the current method is the best one because
it doesnt coverage to a final value fast enough and it isnt sensitive enough to change. Satisfying both of these
requirements is difficult to accomplish since satisfying one generally means making the other worse. Hence,
more research into appropriate averaging methods is needed. We would also like to do a more in depth power
analysis so that we can minimize the power requirements further. We saved power by only turning on the LCD
when the button was held down but more can be done. For example, using the AC instead of the ADC to
determine if someone is breathing would be save power.

Safety

To enforce safety in our design we choose to use a nebulizer mask which is a standard mask used by healthcare
professions all over the world. This mask is easy to put on, comfortable to wear and prevents infants from
picking at the thermistor inside. The other dangerous part of our device are the big capacitors. However this
danger can easily be avoided by using smaller capacitors and increasing the resistance of the corresponding
resistors so that the time constants are still met.

Interference

The main form of interference that can affect our device is temperature interference from the ambient
environment. To prevent this interference we enclosed our thermistor inside a nebulizer mask so that it would
not be exposed to the external environment. We also choose a nebulizer mask because this mask has holes in it
to allow cross ventilation. This cross ventilation is needed so that the thermistor is not saturated by the patients
breath.

Usability

Our intention with this project was build a respiratory monitor designed to be simple to use. The only
information displayed is a number so there are no language barriers that need to be considered. The instructions
for the device are very simple. Place the mask on the patient, then turn on the device, and wait 1 minute to
ensure device has started properly. Afterwards, the LCD button may be held down to display the respiration
rate. If the patient isnt breathing an alarm will sound, or if the battery is low another alarm will sound.
Conforming to Standards

Since our device was designed for the developing world, there isnt a set of standards that we could compare
our device too. In general we designed our device so that it would be safe and easy to use. We choose to use a
nebulizer mask because it is widely used and trusted by healthcare professionals as well as cheap. We also made
sure there were only numbers on the device so that there would be no language barriers. Furthermore, we sought
to make the device power efficient for use in low resource environments

Intellectual Property Considerations

There are many other medical devices that use thermistors to monitor the human body, mostly for the purpose
of sleep apnea. The main feature that distinguishes our device from these commercial products is that our device
uses one thermistor while these other devices have two or more thermistors. Even with this distinction we dont
believe there are many opportunities for patenting with our device. However we do believe there are publishing
opportunities for our device which we plan to pursue. For example, we intend to submit an abstract to the
American Society for Artificial Internal Organs (ASAIO) student design competition held this June 12-15. This
competition is intended to promote undergraduate engineering design in medical technology. We feel that our
project is a good fit for this competition.

Ethical Considerations

Throughout the last few weeks while we worked on or device we made sure to adhere to the IEEE Code of
Ethics. In writing this report we did not embellish the results of our project and we gave over estimations for the
error our device produced. We also described the strengths and weaknesses of our device to ensure that whoever
read our report would understand what our device can and cannot do. In addition, while we worked in lab we
helped our fellow students with debugging their projects. For example, another group came to us asking for help
with their AC. They had the exact same problem we had and we were able to save them countless hours
debugging by pointing out their problem and telling them the solution. Furthermore, we designed our product so
that it would not only work but it would also be safe at the same time. We choose to buy a nebulizer mask
instead of modifying some mask we had laying around because nebulizer masks are approved medical
equipment used by healthcare professionals that are meant to be worn on the face. We also pointed out the
potential danger with having large capacitors in our device and constructed a solution for this problem. In
summary, we followed the IEEE code of ethics by accurately portraying our results, assisting our colleagues,
choosing safe parts and pointing out dangers of our device.

Legal Considerations

Because this device is desired for the third world there are not many legal considerations we need to take. If this
device were to be sold in the U.S. it would need to pass FDA and we would have to make sure we dont infringe
on any patents which we mostly likely do. Furthermore, our device would be classified as a Class 1 medical
device due to its simplicity. If we wanted to get this device on the market our best path would be to file a
510(k). What we would need to do is prove that our device is substantially equivalent to an existing device
already approved by the FDA. This shouldnt be too difficult because there are existing medical devices using
thermistors
B. Source Code
Main file: resp_monitor.c
// Includes + Defines
#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <avr/eeprom.h>
#include <math.h>
#include <avr/pgmspace.h>
#include <stdlib.h>
#include <string.h>
//#define F_CPU 16000000 UL
#include <util/delay.h> // needed for lcd_lib
#include <avr/sleep.h>

#include "uart.h"

#define begin {
#define end }
#define t1 15 //every 0.25 seconds
#define t2 500 //every 8.2 seconds
#define bat_tol 101 //tolerance value of battery code
#define tol 8 //tolerance value of determine if patient is breathing code
FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW); //setup
uart

//*************Macros************************************************
#define READ(U, N) ((U) >> (N) & 1u)
#define SET(U, N) ((void)((U) |= 1u << (N)))
#define CLR(U, N) ((void)((U) &= ~(1u << (N))))
#define FLIP(U, N) ((void)((U) ^= 1u << (N)))

//*************Functions*********************************************
void thermistor(void); //determine if patient is breathing
void battery(void); //measure battery voltage
void display(void); //display breathing rate (bpm) on LCD
void measure(void); //measure breathing rate (bpm)
void initialize (void);

//**************Variables***************************************
//LCD variables
const int8_t LCD_initialize[] PROGMEM = "LCD Initialized\0";
const int8_t LCD_line[] PROGMEM = "line 1\0";
const int8_t LCD_number[] PROGMEM = "Number=\0";
int8_t lcd_buffer[20]; // LCD display buffer

volatile unsigned int time1, time2; //time out variables


volatile int count; //keeps running count of clk cycles
float breathrate; //breathing rate bpm
char sample_old; //old time sample measured by ADC
char sample; //new time sample measured by ADC
volatile int tau1; //time 1 for respiration rate measurement
volatile int tau2; //time 2 for respiration rate
measurement
int c; //counter for number of "no
breaths"
volatile char measure_flag; //set to one if AC ISR goes off
volatile int period; //period of one breath in clk cycles
volatile char flag; //set to one when AC ISR goes off
float avg_period; //average period of one breath in clk cycles
char bat_samp; //battery voltage sample

//************ISR************************************************

//***********Timeout Counter ISR********************************


// timer 0 compare ISR
ISR (TIMER0_COMPA_vect)
begin
//Decrement the three times if they are not already zero
if (time1>0) --time1; //Used to time the thermistor measurements
if (time2>0) --time2; //Used to time the battery voltage measurements
if (count == 20000) //reset count so it doesnt overflow
begin
count = 0;
tau1 = 0;
end
count++;
end

//**************AC ISR************************************
ISR (ANALOG_COMP_vect)
begin

CLR(ACSR, ACIE); //turn off AC inturrupt


flag = 1; //flag for turning on AC inturrupt

tau2 = count; //store new count value


period = tau2 - tau1; //calculate period
tau1 = tau2; //store old count value
measure_flag = 1; //activate measure function

end

//*************Main*********************************************
//Main
int main (void)
begin

//charge capacitors
DDRB = 0b00110000; //set B5,B4 to output
SET(PORTB,PORTB4); //set B4 to 5V

_delay_ms(5000); //wait for 5 seconds while charging

CLR(PORTB,PORTB4); //turn B4 off


DDRB = 0b00000000; //turn DDRB to all inputs

initialize();

//for uart
stdout = stdin = stderr = &uart_str;
fprintf(stdout,"Starting...\n\r");

//main task scheduler loop


while(1)
begin
//testing fprintf statements
//fprintf(stdout,"while\n\r");
//fprintf(stdout,"second = %d\n\r ",second);
//fprintf(stdout,"tau 1 = %d tau2 = %d \n\r",tau1, tau2);
//fprintf(stdout,"count = %d \n\r", count);
//fprintf(stdout,"tau2 = %d \n\r",tau2);
//fprintf(stdout,"period = %d\n\r ",period);
//fprintf(stdout,"tau1 = %d \n\r",tau1);

if (flag == 1) //for turning back on AC inturrupt


begin

_delay_ms(10);
SET(ACSR,ACI);
SET(ACSR, ACIE); //turn on AC inturrupt

flag = 0; //reset flag


end

if (time1==0){ time1=t1; thermistor();} //determines if patient is

//breathing or not

if (time2==0){ time2=t2; battery();} //measures battery life

if (measure_flag == 1){measure_flag = 0; measure();} //measures breathing rate

if (PINB & 0b00000010){display();} //if display button is pushed


//activate the display function

end
end //main

//**************Thermistor****************************
void thermistor(void)
begin

ADMUX = (1<<REFS0) | (1<<ADLAR) ; //turn on left adjust result, turn on external


reference

while(ADCSRA & (1<<ADSC)) ; // wait for conversion

sample_old = sample;
sample = ADCH; // take sample from ADC

ADCSRA |= (1<<ADSC); // shift left

if(abs(sample_old - sample) <= tol)


begin

c++; // increment counter if "no breath"


detected

if(c >= 10) {

//turn on pwm

SET(DDRD,DDD5);
TCCR1B = (1<<WGM12) | (1<< CS11);

TCCR1A = (1<<COM1A0);
OCR1A = 255;

breathrate = 0; //since patient isn't breathing, breathrate is 0

end

else
begin
c = 0; // reset "no breath" count
TCCR1A = 0; // keep PWM off
end
end

//*************Battery*******************************
void battery(void)
begin

ADMUX = (1<<REFS0) | (1<<ADLAR) + 1 ; //turn on left adjust result, turn


on second external reference

ADCSRA |= (1<<ADSC);

while(ADCSRA & (1<<ADSC)) ; //wait for conversion

bat_samp = ADCH; //set variable to


ADC sample

//for testing
fprintf(stdout, "bat samp = %d\n\r", bat_samp);

if(bat_samp <= bat_tol)


begin

//turn on pwm

SET(DDRD,DDD5);
TCCR1B = (1<<WGM12) | (1<<CS11) | (1<<CS10);

TCCR1A = (1<<COM1A0);
OCR1A = 255;

_delay_ms(1000); //pwm on for 5


seconds

TCCR1A = 0;

end

end

//*************Display*******************************
void display(void)
begin

LCDinit(); //initialize the display


LCDcursorOFF();
LCDclr(); //clear the display

//sprintf(lcd_buffer, "win"); for testing


sprintf(lcd_buffer, "%f Bpm", breathrate);
LCDGotoXY(0,0);
LCDstring(lcd_buffer, strlen(lcd_buffer));
//fprintf(stdout,"display \n\r"); for testing

end

//*************Measure*******************************
void measure(void)
begin

avg_period = 0.75*avg_period + 0.25*period; //calculate avg period


breathrate = 3/(avg_period*0.00164); //convert to bpm

//for testing
//fprintf(stdout,"breathrate: %f B/min\n\r", breathrate);

end // measure task

//****************Initialize*****************************
void initialize(void)
begin

DDRA = 0b00000000; //Make PortA all inputs


PORTA = 0b00000000; //turn off pull up resistors

DDRB = 0b01000000; //set B6 to output


SET(PORTB,PORTB6); //set B6 to 5V output

ADMUX = (1<<REFS0) | (1<<ADLAR) ; //turn on left adjust result, turn on


external reference
ADCSRA = (1<<ADEN) | (1<<ADSC) + 7; //turn on ADC, start conversion and set
sample rate to 1Mhz

//set up timer 0 for 1 mSec timebase


TIMSK0= (1<<OCIE0A); //turn on timer 0 cmp match ISR
OCR0A = 255; //set the compare reg to
256 time ticks

//turn on AC
SET(ACSR, ACIE); //turn analog input capture
on
SET(ACSR, ACIS1); //ACIS1 and ACIS0 make the
comparater trigger on the rising edge

//set prescalar to divide by 1024


TCCR0B= 5;

// turn on clear-on-match
TCCR0A= (1<<WGM01) ;

//init variables
flag = 0;
avg_period = 0;

time1=t1;
time2=t2;

uart_init();

//crank up the ISRs


sei();
end

LCD header file: lcd_lib.h


// File Name : 'lcd_lib.h'
// Title : 8 and 4 bit LCd interface
// Author : Scienceprog.com - Copyright (C) 2007
// Created : 2007-03-29
// Revised : 2007-08-08
// Version : 1.0
// Target MCU : Atmel AVR series
//
// This code is distributed under the GNU Public License
// which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************
#ifndef LCD_LIB
#define LCD_LIB

#include <inttypes.h>

//Uncomment this if LCD 4 bit interface is used


//******************************************
#define LCD_4bit
//***********************************************

#define LCD_RS 0 //define MCU pin connected to LCD RS


#define LCD_RW 1 //define MCU pin connected to LCD R/W
#define LCD_E 2 //define MCU pin connected to LCD E
#define LCD_D0 0 //define MCU pin connected to LCD D0
#define LCD_D1 1 //define MCU pin connected to LCD D1
#define LCD_D2 2 //define MCU pin connected to LCD D1
#define LCD_D3 3 //define MCU pin connected to LCD D2
#define LCD_D4 4 //define MCU pin connected to LCD D3
#define LCD_D5 5 //define MCU pin connected to LCD D4
#define LCD_D6 6 //define MCU pin connected to LCD D5
#define LCD_D7 7 //define MCU pin connected to LCD D6
#define LDP PORTC //define MCU port connected to LCD data pins
#define LCP PORTC //define MCU port connected to LCD control pins
#define LDDR DDRC //define MCU direction register for port connected to LCD data pins
#define LCDR DDRC //define MCU direction register for port connected to LCD control
pins

#define LCD_CLR 0 //DB0: clear display


#define LCD_HOME 1 //DB1: return to home position
#define LCD_ENTRY_MODE 2 //DB2: set entry mode
#define LCD_ENTRY_INC 1 //DB1: increment
#define LCD_ENTRY_SHIFT 0 //DB2: shift
#define LCD_ON_CTRL 3 //DB3: turn lcd/cursor on
#define LCD_ON_DISPLAY 2 //DB2: turn display on
#define LCD_ON_CURSOR 1 //DB1: turn cursor on
#define LCD_ON_BLINK 0 //DB0: blinking cursor
#define LCD_MOVE 4 //DB4: move cursor/display
#define LCD_MOVE_DISP 3 //DB3: move display (0-> move cursor)
#define LCD_MOVE_RIGHT 2 //DB2: move right (0-> left)
#define LCD_FUNCTION 5 //DB5: function set
#define LCD_FUNCTION_8BIT 4 //DB4: set 8BIT mode (0->4BIT mode)
#define LCD_FUNCTION_2LINES 3 //DB3: two lines (0->one line)
#define LCD_FUNCTION_10DOTS 2 //DB2: 5x10 font (0->5x7 font)
#define LCD_CGRAM 6 //DB6: set CG RAM address
#define LCD_DDRAM 7 //DB7: set DD RAM address
// reading:
#define LCD_BUSY 7 //DB7: LCD is busy
#define LCD_LINES 2 //visible lines
#define LCD_LINE_LENGTH 16 //line length (in characters)
// cursor position to DDRAM mapping
#define LCD_LINE0_DDRAMADDR 0x00
#define LCD_LINE1_DDRAMADDR 0x40
#define LCD_LINE2_DDRAMADDR 0x14
#define LCD_LINE3_DDRAMADDR 0x54
// progress bar defines
#define PROGRESSPIXELS_PER_CHAR 6

void LCDsendChar(uint8_t); //forms data ready to send to 74HC164


void LCDsendCommand(uint8_t); //forms data ready to send to 74HC164
void LCDinit(void); //Initializes LCD
void LCDclr(void); //Clears LCD
void LCDhome(void); //LCD cursor home
void LCDstring(uint8_t*, uint8_t); //Outputs string to LCD
void LCDGotoXY(uint8_t, uint8_t); //Cursor to X Y position
void CopyStringtoLCD(const uint8_t*, uint8_t, uint8_t);//copies flash string to LCD at
x,y
void LCDdefinechar(const uint8_t *,uint8_t);//write char to LCD CGRAM
void LCDshiftRight(uint8_t); //shift by n characters Right
void LCDshiftLeft(uint8_t); //shift by n characters Left
void LCDcursorOn(void); //Underline cursor ON
void LCDcursorOnBlink(void); //Underline blinking cursor ON
void LCDcursorOFF(void); //Cursor OFF
void LCDblank(void); //LCD blank but not cleared
void LCDvisible(void); //LCD visible
void LCDcursorLeft(uint8_t); //Shift cursor left by n
void LCDcursorRight(uint8_t); //shif cursor right by n
// displays a horizontal progress bar at the current cursor location
// <progress> is the value the bargraph should indicate
// <maxprogress> is the value at the end of the bargraph
// <length> is the number of LCD characters that the bargraph should cover
//adapted from AVRLIB - displays progress only for 8 bit variables
void LCDprogressBar(uint8_t progress, uint8_t maxprogress, uint8_t length);

#endif

LCD source code: lcd_lib.c

// File Name : 'lcd_lib.c'


// Title : 8 and 4 bit LCd interface
// Author : Scienceprog.com - Copyright (C) 2007
// Created : 2007-03-29
// Revised : 2007-08-08
// Version : 1.0
// Target MCU : Atmel AVR series
//
// This code is distributed under the GNU Public License
// which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************
#include "lcd_lib.h"
#include <inttypes.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>

const uint8_t LcdCustomChar[] PROGMEM=//define 8 custom LCD chars


{
0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, // 0. 0/5 full progress block
0x00, 0x1F, 0x10, 0x10, 0x10, 0x10, 0x1F, 0x00, // 1. 1/5 full progress block
0x00, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, // 2. 2/5 full progress block
0x00, 0x1F, 0x1C, 0x1C, 0x1C, 0x1C, 0x1F, 0x00, // 3. 3/5 full progress block
0x00, 0x1F, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x00, // 4. 4/5 full progress block
0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, // 5. 5/5 full progress block
0x03, 0x07, 0x0F, 0x1F, 0x0F, 0x07, 0x03, 0x00, // 6. rewind arrow
0x18, 0x1C, 0x1E, 0x1F, 0x1E, 0x1C, 0x18, 0x00 // 7. fast-forward arrow
};

void LCDsendChar(uint8_t ch) //Sends Char to LCD


{

#ifdef LCD_4bit
//4 bit part
LDP=(ch&0b11110000);
LCP|=1<<LCD_RS;
LCP|=1<<LCD_E;
_delay_ms(1);
LCP&=~(1<<LCD_E);
LCP&=~(1<<LCD_RS);
_delay_ms(1);
LDP=((ch&0b00001111)<<4);
LCP|=1<<LCD_RS;
LCP|=1<<LCD_E;
_delay_ms(1);
LCP&=~(1<<LCD_E);
LCP&=~(1<<LCD_RS);
_delay_ms(1);
#else
//8 bit part
LDP=ch;
LCP|=1<<LCD_RS;
LCP|=1<<LCD_E;
_delay_ms(1);
LCP&=~(1<<LCD_E);
LCP&=~(1<<LCD_RS);
_delay_ms(1);
#endif
}
void LCDsendCommand(uint8_t cmd) //Sends Command to LCD
{
#ifdef LCD_4bit
//4 bit part
LDP=(cmd&0b11110000);
LCP|=1<<LCD_E;
_delay_ms(1);
LCP&=~(1<<LCD_E);
_delay_ms(1);
LDP=((cmd&0b00001111)<<4);
LCP|=1<<LCD_E;
_delay_ms(1);
LCP&=~(1<<LCD_E);
_delay_ms(1);
#else
//8 bit part
LDP=cmd;
LCP|=1<<LCD_E;
_delay_ms(1);
LCP&=~(1<<LCD_E);
_delay_ms(1);
#endif
}
void LCDinit(void)//Initializes LCD
{
#ifdef LCD_4bit
//4 bit part
_delay_ms(15);
LDP=0x00;
LCP=0x00;
LDDR|=1<<LCD_D7|1<<LCD_D6|1<<LCD_D5|1<<LCD_D4;
LCDR|=1<<LCD_E|1<<LCD_RW|1<<LCD_RS;
//---------one------
LDP=0<<LCD_D7|0<<LCD_D6|1<<LCD_D5|1<<LCD_D4; //4 bit mode
LCP|=1<<LCD_E|0<<LCD_RW|0<<LCD_RS;
_delay_ms(1);
LCP&=~(1<<LCD_E);
_delay_ms(1);
//-----------two-----------
LDP=0<<LCD_D7|0<<LCD_D6|1<<LCD_D5|1<<LCD_D4; //4 bit mode
LCP|=1<<LCD_E|0<<LCD_RW|0<<LCD_RS;
_delay_ms(1);
LCP&=~(1<<LCD_E);
_delay_ms(1);
//-------three-------------
LDP=0<<LCD_D7|0<<LCD_D6|1<<LCD_D5|0<<LCD_D4; //4 bit mode
LCP|=1<<LCD_E|0<<LCD_RW|0<<LCD_RS;
_delay_ms(1);
LCP&=~(1<<LCD_E);
_delay_ms(1);
//--------4 bit--dual line---------------
LCDsendCommand(0b00101000);
//-----increment address, invisible cursor shift------
LCDsendCommand(0b00001100);
//init 8 custom chars
uint8_t ch=0, chn=0;
while(ch<64)
{
LCDdefinechar((LcdCustomChar+ch),chn++);
ch=ch+8;
}

#else
//8 bit part
_delay_ms(15);
LDP=0x00;
LCP=0x00;
LDDR|=1<<LCD_D7|1<<LCD_D6|1<<LCD_D5|1<<LCD_D4|1<<LCD_D3
|1<<LCD_D2|1<<LCD_D1|1<<LCD_D0;
LCDR|=1<<LCD_E|1<<LCD_RW|1<<LCD_RS;
//---------one------
LDP=0<<LCD_D7|0<<LCD_D6|1<<LCD_D5|1<<LCD_D4|0<<LCD_D3
|0<<LCD_D2|0<<LCD_D1|0<<LCD_D0; //8 it mode
LCP|=1<<LCD_E|0<<LCD_RW|0<<LCD_RS;
_delay_ms(1);
LCP&=~(1<<LCD_E);
_delay_ms(1);
//-----------two-----------
LDP=0<<LCD_D7|0<<LCD_D6|1<<LCD_D5|1<<LCD_D4|0<<LCD_D3
|0<<LCD_D2|0<<LCD_D1|0<<LCD_D0; //8 it mode
LCP|=1<<LCD_E|0<<LCD_RW|0<<LCD_RS;
_delay_ms(1);
LCP&=~(1<<LCD_E);
_delay_ms(1);
//-------three-------------
LDP=0<<LCD_D7|0<<LCD_D6|1<<LCD_D5|1<<LCD_D4|0<<LCD_D3
|0<<LCD_D2|0<<LCD_D1|0<<LCD_D0; //8 it mode
LCP|=1<<LCD_E|0<<LCD_RW|0<<LCD_RS;
_delay_ms(1);
LCP&=~(1<<LCD_E);
_delay_ms(1);
//--------8 bit dual line----------
LDP=0<<LCD_D7|0<<LCD_D6|1<<LCD_D5|1<<LCD_D4|1<<LCD_D3
|0<<LCD_D2|0<<LCD_D1|0<<LCD_D0; //8 it mode
LCP|=1<<LCD_E|0<<LCD_RW|0<<LCD_RS;
_delay_ms(1);
LCP&=~(1<<LCD_E);
_delay_ms(1);
//-----increment address, invisible cursor shift------
LDP=0<<LCD_D7|0<<LCD_D6|0<<LCD_D5|0<<LCD_D4|1<<LCD_D3
|1<<LCD_D2|0<<LCD_D1|0<<LCD_D0; //8 it mode
LCP|=1<<LCD_E|0<<LCD_RW|0<<LCD_RS;
_delay_ms(1);
LCP&=~(1<<LCD_E);
_delay_ms(5);
//init custom chars
uint8_t ch=0, chn=0;
while(ch<64)
{
LCDdefinechar((LcdCustomChar+ch),chn++);
ch=ch+8;
}

#endif
}
void LCDclr(void) //Clears LCD
{
LCDsendCommand(1<<LCD_CLR);
}
void LCDhome(void) //LCD cursor home
{
LCDsendCommand(1<<LCD_HOME);
}
void LCDstring(uint8_t* data, uint8_t nBytes) //Outputs string to LCD
{
register uint8_t i;

// check to make sure we have a good pointer


if (!data) return;

// print data
for(i=0; i<nBytes; i++)
{
LCDsendChar(data[i]);
}
}
void LCDGotoXY(uint8_t x, uint8_t y) //Cursor to X Y position
{
register uint8_t DDRAMAddr;
// remap lines into proper order
switch(y)
{
case 0: DDRAMAddr = LCD_LINE0_DDRAMADDR+x; break;
case 1: DDRAMAddr = LCD_LINE1_DDRAMADDR+x; break;
case 2: DDRAMAddr = LCD_LINE2_DDRAMADDR+x; break;
case 3: DDRAMAddr = LCD_LINE3_DDRAMADDR+x; break;
default: DDRAMAddr = LCD_LINE0_DDRAMADDR+x;
}
// set data address
LCDsendCommand(1<<LCD_DDRAM | DDRAMAddr);

}
//Copies string from flash memory to LCD at x y position
//const uint8_t welcomeln1[] PROGMEM="AVR LCD DEMO\0";
//CopyStringtoLCD(welcomeln1, 3, 1);
void CopyStringtoLCD(const uint8_t *FlashLoc, uint8_t x, uint8_t y)
{
uint8_t i;
LCDGotoXY(x,y);
for(i=0;(uint8_t)pgm_read_byte(&FlashLoc[i]);i++)
{
LCDsendChar((uint8_t)pgm_read_byte(&FlashLoc[i]));
}
}
//defines char symbol in CGRAM
/*
const uint8_t backslash[] PROGMEM=
{
0b00000000,//back slash
0b00010000,
0b00001000,
0b00000100,
0b00000010,
0b00000001,
0b00000000,
0b00000000
};
LCDdefinechar(backslash,0);
*/
void LCDdefinechar(const uint8_t *pc,uint8_t char_code){
uint8_t a, pcc;
uint16_t i;
a=(char_code<<3)|0x40;
for (i=0; i<8; i++){
pcc=pgm_read_byte(&pc[i]);
LCDsendCommand(a++);
LCDsendChar(pcc);
}
}

void LCDshiftLeft(uint8_t n) //Scrol n of characters Right


{
for (uint8_t i=0;i<n;i++)
{
LCDsendCommand(0x1E);
}
}
void LCDshiftRight(uint8_t n) //Scrol n of characters Left
{
for (uint8_t i=0;i<n;i++)
{
LCDsendCommand(0x18);
}
}
void LCDcursorOn(void) //displays LCD cursor
{
LCDsendCommand(0x0E);
}
void LCDcursorOnBlink(void) //displays LCD blinking cursor
{
LCDsendCommand(0x0F);
}
void LCDcursorOFF(void) //turns OFF cursor
{
LCDsendCommand(0x0C);
}
void LCDblank(void) //blanks LCD
{
LCDsendCommand(0x08);
}
void LCDvisible(void) //Shows LCD
{
LCDsendCommand(0x0C);
}
void LCDcursorLeft(uint8_t n) //Moves cursor by n poisitions left
{
for (uint8_t i=0;i<n;i++)
{
LCDsendCommand(0x10);
}
}
void LCDcursorRight(uint8_t n) //Moves cursor by n poisitions left
{
for (uint8_t i=0;i<n;i++)
{
LCDsendCommand(0x14);
}
}
//adapted fro mAVRLIB
void LCDprogressBar(uint8_t progress, uint8_t maxprogress, uint8_t length)
{
uint8_t i;
uint16_t pixelprogress;
uint8_t c;

// draw a progress bar displaying (progress / maxprogress)


// starting from the current cursor position
// with a total length of "length" characters
// ***note, LCD chars 0-5 must be programmed as the bar characters
// char 0 = empty ... char 5 = full

// total pixel length of bargraph equals length*PROGRESSPIXELS_PER_CHAR;


// pixel length of bar itself is
pixelprogress = ((progress*(length*PROGRESSPIXELS_PER_CHAR))/maxprogress);

// print exactly "length" characters


for(i=0; i<length; i++)
{
// check if this is a full block, or partial or empty
// (u16) cast is needed to avoid sign comparison warning
if( ((i*(uint16_t)PROGRESSPIXELS_PER_CHAR)+5) > pixelprogress )
{
// this is a partial or empty block
if( ((i*(uint16_t)PROGRESSPIXELS_PER_CHAR)) > pixelprogress )
{
// this is an empty block
// use space character?
c = 0;
}
else
{
// this is a partial block
c = pixelprogress % PROGRESSPIXELS_PER_CHAR;
}
}
else
{
// this is a full block
c = 5;
}

// write character to display


LCDsendChar(c);
}

Potrebbero piacerti anche