#include
<avr/io.h>
#define F_CPU
8000000UL // 8 MHz
#include
<util/delay.h>
#include
<math.h>
#include
<avr/interrupt.h>
#include
<avr/signal.h>
#define N 600 //number
of sample for calculate the average value of the temperature
#define MAX_INIT
100000 //number of sample to find the maximun at the begininnig of the pulse
#define perc 0.6 //percentual for threshold
#define perc2 0.3 //percentual for threshold2
#define N_average 1
//how many frequency pulse values to make an average
#define N_reset 1 //number of dislayed values before
come back to pulse initialization
//void _delay_ms (double __ms)
//void _delay_us (double __us) included in delay.h
int EXIT;
char MODE;
int freq_prev;
void set_pin(){
//at the beginning of
the program the direction (and other functions)
//of pins is setted
DDRA=0x00; //port A input ADC
DDRB=0xff;
DDRC=0xc0;
DDRD=0xf2;
}
void write_pin(char
letter, unsigned long numb, char value){
// usefull function for writing
only one pin in a port without
// modifying the other pins
value
//read the port, copy the value,
modify pin and set again the register
char x; //x is a mask
x=(1 << numb);
switch (letter){
case 'A':
if
(value == 1 ){
PORTA
|= x; }
else{
x=~x;
PORTA
&= x;}
break;
case 'B':
if
(value == 1 ){
PORTB
|= x; }
else{
x=~x;
PORTB
&= x;}
break;
case 'C':
if
(value == 1 ){
PORTC
|= x; }
else{
x=~x;
PORTC
&= x;}
break;
case 'D':
if
(value == 1 ){
PORTD
|= x; }
else{
x=~x;
PORTD
&= x;}
break;
}
}
void
write_data_LCD(char data) //enable
signal and send data to DBx
{ PORTB=data; }
// Function that
configures the LCD to receive instructions
void set_write_IR() { //set
RS and RW for those operation
write_pin('D',PD4,0); //RS=0
write_pin('D',PD5,0); //RW=0
}
// Function that
configures the LCD to receive data
void set_write_DR(){
write_pin('D',PD4,1); //RS=1
write_pin('D',PD5,0); //RW=0
}
void enable(){ //set E=1 of
LCD
write_pin('D',PD6,1);
}
void disable(){ //E=0 of LCD
write_pin('D',PD6,0);
}
void initial_LCD(){
enable();
set_write_IR();
write_data_LCD(0x38); //function set ->
8bit mode
disable();
enable();
_delay_us (40);
write_data_LCD(0x01); //clear display
disable();
enable();
_delay_ms (2);
write_data_LCD(0x02); //cursor home
disable();
enable();
_delay_ms (2);
write_data_LCD(0x0c); //display on/off -> on, no cursor, no
blinking
disable();
enable();
_delay_us (40);
}
void clear_LCD(){
set_write_IR();
write_data_LCD(0x01); //clear display
disable();
enable();
_delay_ms (50);
}
void next_line_LCD(){
set_write_IR();
write_data_LCD(0b11000000); //cursor shifted to the start of the second
line
disable();
enable();
_delay_us (400);
}
//function take a
character and write on LCD
void write_char(char
letter){
char t;
switch (letter){
case '0':
t=0x30;
break;
case '1':
t=0x31;
break;
case '2':
t=0x32;
break;
case '3':
t=0x33;
break;
case '4':
t=0x34;
break;
case '5':
t=0x35;
break;
case '6':
t=0x36;
break;
case '7':
t=0x37;
break;
case '8':
t=0x38;
break;
case '9':
t=0x39;
break;
case '-': // The
"-" symbol will be considered a white space
t=0x20;
break;
case 'A':
t=0x41;
break;
case 'B':
t=0x42;
break;
case 'C':
t=0x43;
break;
case 'D':
t=0x44;
break;
case 'E':
t=0x45;
break;
case 'F':
t=0x46;
break;
case 'G':
t=0x47;
break;
case 'H':
t=0x48;
break;
case 'I':
t=0x49;
break;
case 'J':
t=0x4a;
break;
case 'K':
t=0x4b;
break;
case 'L':
t=0x4c;
break;
case 'M':
t=0x4d;
break;
case 'N':
t=0x4e;
break;
case 'O':
t=0x4f;
break;
case 'P':
t=0x50;
break;
case 'Q':
t=0x51;
break;
case 'R':
t=0x52;
break;
case 'S':
t=0x53;
break;
case 'T':
t=0x54;
break;
case 'U':
t=0x55;
break;
case 'V':
t=0x56;
break;
case 'W':
t=0x57;
break;
case 'X':
t=0x58;
break;
case 'Y':
t=0x59;
break;
case 'Z':
t=0x5a;
break;
case 'a':
t=0x61;
break;
case 'b':
t=0x62;
break;
case 'c':
t=0x63;
break;
case 'd':
t=0x64;
break;
case 'e':
t=0x65;
break;
case 'f':
t=0x66;
break;
case 'g':
t=0x67;
break;
case 'h':
t=0x68;
break;
case 'i':
t=0x69;
break;
case 'j':
t=0x6a;
break;
case 'k':
t=0x6b;
break;
case 'l':
t=0x6c;
break;
case 'm':
t=0x6d;
break;
case
'n':
t=0x6e;
break;
case
'o':
t=0x6f;
break;
case
'p':
t=0x70;
break;
case
'q':
t=0x71;
break;
case
'r':
t=0x72;
break;
case
's':
t=0x73;
break;
case
't':
t=0x74;
break;
case
'u':
t=0x75;
break;
case
'v':
t=0x76;
break;
case
'w':
t=0x77;
break;
case
'x':
t=0x78;
break;
case
'y':
t=0x79;
break;
case
'z':
t=0x7a;
break;
case
':':
t=0x3a;
break;
case
'?':
t=0x3f;
break;
case
'.':
t=0x2E;
break;
}
write_data_LCD(t);
disable();
enable();
_delay_us
(40);
}
void write_initial_screen(){
set_write_IR(); //entry mode of this text
write_data_LCD(0x06);
disable();
enable();
_delay_us(40);
set_write_DR(); //write Select
Measurment
write_char('S');
write_char('e');
write_char('l');
write_char('e');
write_char('c');
write_char('t');
write_char('-');
write_char('M');
write_char('e');
write_char('a');
write_char('s');
write_char('u');
write_char('r');
write_char('e');
next_line_LCD();
set_write_DR(); //write Select
Measurment
write_char('1');
write_char('T');
write_char('e');
write_char('m');
write_char('p');
write_char('-');
write_char('-');
write_char('-');
write_char('2');
write_char('P');
write_char('u');
write_char('l');
write_char('s');
write_char('e');
}
void write_temp_screen(){
set_write_IR(); //entry mode of this text
write_data_LCD(0x06);
disable();
enable();
_delay_us(40);
set_write_DR();
write_char('T');
write_char('e');
write_char('m');
write_char('p');
_delay_ms
(500);
}
void write_pulse_screen(){
set_write_IR(); //entry mode of this text
write_data_LCD(0x06);
disable();
enable();
_delay_us(40);
set_write_DR();
write_char('P');
write_char('u');
write_char('l');
write_char('s');
write_char('e');
_delay_ms
(500);
}
//function used when the heart rate value is
not correct
void write_error_screen(){
set_write_IR(); //entry mode of this text
write_data_LCD(0x06);
disable();
enable();
_delay_us(40);
set_write_DR();
write_char('W');
write_char('a');
write_char('i');
write_char('t');
write_char('
');
write_char('
');
}
int buttom(){
//it
is a function that returns 1 if you press the button 1 and returns
//2
if you press the button 2. wait until one buttom is released.
char
flag = 0;
char
input;
while
(flag==0){
input
= PINC&0x01 ;
if
(input== 0x00 ){
_delay_ms(200);
while
(input== 0x00){input = PINC&0x01 ;}; //contrilling
if the last bit is equal to 0
flag
= 1; }
else {input = PINC&0x02 ;
if
(input== 0x00 ){ //contrilling
if the last bit is equal to 1
_delay_ms(200);
while
(input== 0x00){input = PINC&0x02 ;};
flag
= 2; }}
}
return
flag;
}
//start the adc convertion
void start_conv(){
char
t;
t=
(1<<ADSC);
ADCSRA|=t;
}
//this show in the LCD a number between 0 to
1024 in a int variable
//It's necessary to convert the decimal number
to different characters in order to be displayed
void send_int(int a, char meas){
char
t1,t2,t3,t4,t5,t6;
t1
= a%10;
t2
= (a/10)%10;
t3
= (a/100)%10;
t4
= (a/1000)%10;
t5
= (a/10000)%10;
t6
= (a/100000)%10;
char
p;
//
start of the code necessary to do the serial communication
//send
type of measure
while
((UCSRA & (1 << UDRE)) == 0) {}; //do nothing until buffer is empty
UDR
= meas+0x30;
//send
data
p=t6+0x30;
while
((UCSRA & (1 << UDRE)) == 0) {};
UDR
= p;
p=t5+0x30;
while
((UCSRA & (1 << UDRE)) == 0) {};
UDR
= p;
p=t4+0x30;
while
((UCSRA & (1 << UDRE)) == 0) {};
UDR
= p;
p=t3+0x30;
while
((UCSRA & (1 << UDRE)) == 0) {};
UDR
= p;
p=t2+0x30;
while
((UCSRA & (1 << UDRE)) == 0) {};
UDR
= p;
p=t1+0x30;
while
((UCSRA & (1 << UDRE)) == 0) {};
UDR
= p;
}
void write_int_LCD(int a ){
int
t1,t2,t3,t4,t5,t6;
t1
= a%10;
t2
= (a/10)%10;
t3
= (a/100)%10;
t4
= (a/1000)%10;
t5
= (a/10000)%10;
t6
= (a/100000)%10;
char
p;
p
= t6;
if
(p!= 0){
p+=0x30;
write_data_LCD(p);
disable();
enable();
_delay_us
(40);
}
p
= t5;
p+=0x30;
write_data_LCD(p);
disable();
enable();
_delay_us
(40);
p
= t4;
p+=0x30;
write_data_LCD(p);
disable();
enable();
_delay_us
(40);
p
= t3;
p+=0x30;
write_data_LCD(p);
disable();
enable();
_delay_us
(40);
p
= t2;
p+=0x30;
write_data_LCD(p);
disable();
enable();
_delay_us
(40);
write_char('.');
//decimal dot
p
= t1;
p+=0x30;
write_data_LCD(p);
disable();
enable();
_delay_us
(40);
}
//simple procedure for temperature measure.
single shot acquisistion
//This function will collect the temperature
data and make the average
int temp_measure(){
char
t;
int
HVAL,LVAL, prova, i;
double
temp;
long
int sum;
sum=0;
for(i=0;i<N;i++)
{ // N is declared in the
beginning of the file to easy reconfiguration
ADMUX
= 0xc0;
ADCSRA
=0b10000000;//BIT 3 INTERRUPT DISABLE
start_conv();
t=0;
while
(t==0) {t = (ADCSRA & 0x10);};
LVAL
= ADCL;
HVAL
= ADCH & 0x03; //only
for security (not needed)
LVAL
= (HVAL*256)+LVAL; //HVAL
is shifted 8 bits to get the total value of 10 bits.
temp
= LVAL*(2.56/(1024))*1000; //
Vref=2,56V , 2^10 bits , 1000=100(10mV per degree)*10(one decimal digit)
sum
+= temp; //average
_delay_us(311);
};
prova
= sum/N;
//
prova contain the value of temperature that will be show in the LCD
//
the temp value is obtained by a mean of N sample of the sensor
//
insert here any data elaboration of this value before showing
write_int_LCD(prova);
return
prova;
}
//configuration of the interruption (for
resetting)
void set_interrupt(){
char
x; //x is a mask
x
= 0x03;
MCUCR
|= x; //int0 generates intererrupt on
rising edge
x=0x40;
GICR
|= x; //interrupt
int0 enable
}
void enable_interrupt()
{
char
x; //x is a mask
x
= 0x80;
x=
~x;
GIFR
&= x;
x
= 0x80;
SREG
|= x;
}
void disable_interrupt()
{
char
x; //x is a mask
x
= 0x80;
x=
~x;
SREG
&= x;
}
//Routine of the interruption which changes
the flag of resetting
SIGNAL (SIG_INTERRUPT0){
EXIT
= 1;
}
//Main calculation of the pulse measurement
void pulse_measure_1(){
char
t,above,b;
int
HVAL,LVAL, max,min;
long
int i;
long
int j;
int
time;
double
freq;
double
sum;
int
k=0;
int
thres,thres2;
ADMUX
= 0b01000001; //channel1
selected, Aref
ADCSRA
=0b10000000; //enable
ADC
TCCR1B=0; //timer off
TCNT1
=0; // Reset timer value
start_conv();
t=0;
while
(t==0) {t = (ADCSRA & 0x10);}; //check
if sample is ready
LVAL
= ADCL;
HVAL
= ADCH & 0x03;
LVAL
= (HVAL*256)+LVAL;
max
= LVAL;
min
= LVAL;
//We
check the maximun and minimum during a little bit more than a period
for
(i=0;i<MAX_INIT;i++)
{
start_conv();
t=0;
while
(t==0) {t = (ADCSRA & 0x10);}; //check
if sample is ready
LVAL
= ADCL;
HVAL
= ADCH & 0x03;
LVAL
= (HVAL*256)+LVAL;
if(LVAL<min) min= LVAL;
if
(LVAL>max) max=LVAL;
}
//calculation
of the thresholds with those previous values
thres
= min + ((max-min)*perc);
thres2
= min + ((max-min)*perc2);
//code
necessary to enter correctly in the correct case of above (1 or 0)
above=2;
while(above!=0){
start_conv();
t=0;
while
(t==0) {t = (ADCSRA & 0x10);}; //check
if sample is ready
LVAL
= ADCL;
HVAL
= ADCH & 0x03;
LVAL
= (HVAL*256)+LVAL;
//
We are above or below the threshold?
if
(LVAL>thres) above=1;
if
(LVAL<thres2) above=0;
}
sum=0;
while(1){
j=0;
while(j<N_average){
if(MODE==1){
if((UCSRA
& (1 << RXC)) != 0) { //if something received send
the data
b=UDR;
send_int(freq_prev,
2); }
}
//
Check the interrupt flag and jump outside the function if it's necessary
if
(EXIT==1) {
goto
RESET;}
start_conv();
t=0;
while
(t==0) {t = (ADCSRA & 0x10);}; //check
if sample is ready
LVAL
= ADCL;
LVAL
= (ADCH*256)+LVAL;
//structure
of state diagram distinguish the above and below case
switch(above)
{
case
0: {
if
(LVAL>thres) {
//
timer() capability start;
TCCR1B=0; //timer
off
time
= TCNT1; //collect
the value of the timer
TCNT1
=0; //reset
the timer
TCCR1B
|= ((1 << CS10) | (1 << CS12)); //timer
on
if
(time!=0) {
if(((UCSRA
& (1 << RXC)) != 0)) { //if
something received send the data
send_int(freq, '2'); }
freq
= (4915200)/time; //4915200=8MHz
(ferquency)*60(minute)*10(decimal)
sum
+= freq,
j++;
}
//
timer() capability finish;
above=1;
}
break;
}
case
1: {
if
(LVAL<thres2) { above=0;
}
}
}
}
next_line_LCD();
set_write_DR();
freq=sum/N_average;
if
((freq<500)|(freq>2000)) write_error_screen(); //Range of correct pulse
between 50 and 200 ppm
else
{write_int_LCD(freq);
freq_prev=freq;
}
sum=0;
if
(k<N_reset) k++;
else {
k=0;
goto
RESET;}
} //close while(1)
RESET:
TCCR1B=0; //timer off
TCNT1
=0; // Reset timer value
sum=0;
} //close function
void write_initial_screen_0(){
set_write_IR(); //entry mode of this text
write_data_LCD(0x06);
disable();
enable();
_delay_us(40);
set_write_DR(); // Show in the
Screen PC Connected Press one for yes and Two for no
write_char('P');
write_char('C');
write_char('-');
write_char('C');
write_char('o');
write_char('n');
write_char('n');
write_char('e');
write_char('c');
write_char('t');
write_char('e');
write_char('d');
write_char('?');
next_line_LCD();
set_write_DR();
write_char('1');
write_char('Y');
write_char('e');
write_char('s');
write_char('-');
write_char('-');
write_char('-');
write_char('-');
write_char('2');
write_char('N');
write_char('o');
}
void main(void)
{
int
a,b, measure;
b=0;
set_pin();
initial_LCD();
START: //starting
point after reset
{EXIT=0;
MODE=0; //MODE=1
if we want to connect to PC
freq_prev=0;
disable_interrupt();
set_interrupt();
clear_LCD();
write_initial_screen_0();
MODE=buttom(); //choose if the PC
is connected
if
(MODE==1){
enable_interrupt();
clear_LCD();
set_write_IR(); //entry mode of this text
write_data_LCD(0x06);
disable();
enable();
_delay_us(40);
set_write_DR();
write_char('W');
write_char('a');
write_char('i');
write_char('t');
write_char('i');
write_char('n');
write_char('g');
write_char('-');
write_char('P');
write_char('C');
//enable
serial communication and setting parameters
UCSRB
|= (1 << RXEN) | (1 << TXEN);
UCSRC
|= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1);
UBRRH
= 0x00;;
UBRRL
= 0x33; //=51 (9600bps at 8MHz) look
table on datasheet
_delay_ms(1000);
//receive
1 from PC to start the communication
do{
while((UCSRA
& (1 << RXC)) == 0) {
enable(); //is for the RESET
if
(EXIT==1) {goto START;}};
b=UDR;
write_char(b);
//until
a 1 is received indicating the PC is ready the program cannot go on
}while(b!='1');
b=0;
_delay_us(100);
//send
a byte to the PC
while
((UCSRA & (1 << UDRE)) == 0) {}; //do nothing until buffer is empty
UDR
= 0x33;
clear_LCD();
set_write_IR(); //entry mode of this text
write_data_LCD(0x06);
disable();
enable();
_delay_us(40);
set_write_DR();
write_char('P');
write_char('C');
write_char('-');
write_char('M');
write_char('o');
write_char('d');
write_char('e');
write_char('-');
write_char('O');
write_char('N');
_delay_ms(1000);
//to have enough time to visualize the written before
}
clear_LCD();
_delay_ms(1000);
write_initial_screen();
a
= buttom(); //from
this point we wait for the preassure of the button
enable_interrupt();
switch
(a) { //type of measurament
case
1:
_delay_ms(500);
clear_LCD();
write_temp_screen();
//the
measurement of the temperature is continously running
while(1){
if
(EXIT==1) { //exit point for
resetting the device
goto
START;}
next_line_LCD();
set_write_DR();
measure
= temp_measure(); //temperature is
calculated and displayed
if(MODE==1){ //if PC is connected
//if
something is received from the PC the device send the temp data back
if((UCSRA
& (1 << RXC)) != 0) {
b=UDR;
send_int(measure,
1); } //the 1 indicates temperature
measure
_delay_ms(1000);
}
_delay_ms(400);
}
break;
case
2: clear_LCD();
write_pulse_screen();
//pulse
measurement is continuosly running
while(1){
if
(EXIT==1) {
goto
START; }
pulse_measure_1();
}
break;
default: write_char('X'); //only for debugging
}
}
}