Hi everyone, I am relatively new to Arduino and about programming I have some experience with Python but nothing with C or C++
I found this code online and wanted modify it so I can upload it on Arduino IDE. I understand part of the code but when it comes to registers and stuff I am lost.
The code is for SpO2 and heart beat monitor.
Could someone help me understand and modify this code or guide me to resources where I teach myself to comprehend it..
Any help will be appreciated
Following is code explanation as mentioned on site:
The program design consist of a few major parts. The samples are read in by using the built-in analog to digital converter (ADC) on the mega644. It performs a 10 bit analog to digital conversion based on a reference voltage (Aref). In this project, as the circuit ran on approximately five volt Vcc, so Aref was set to five volts. We know that ADC data registers are ADCH and ADCL, which are 8 bit individually and give a 16 bit register when used together. As the ADC in Atmega644 is a 10 bit ADC, he will give out only 10 bit of digital value. Since the least two significant bits of the ADC do not significantly affect the design, it has been ignored and the data has been stored in ADCH only. Twice of memory needs to be allocated, which cannot be afforded. To read two values from the ADC when needed, one would be read, then the ADMUX register changed to the other input, and then the program waited until the second conversion was complete. Due to these factors, I decided to use the ADCH register to store the 8 bit data. The analog signal is given to the default A0 pin of the microcontroller. The prescalar of the ADC is set to 128, giving us a frequency of 125KHz, very much in the ADC frequency range of 50-200KHz. The ADC is set in free running mode. Timer 1 is a 16 bit timer/counter and has been used to perform interrupts every 0.01 second. As the frequency of the heart rate is about 1.2 Hz, 1/100 of that is chosen as appropriate for ADC to sample. Thus prescalar of 1024 has been selected in clear timer on compare (CTC) mode.
Calculations:
Required delay = 0.01 seconds
Clock = 16MHz
Prescalar = 1024
Thus, New frequency = Clock/Prescalar = 15625Hz
Timer count = (Required delay/Delay after prescaling)-1
= (0.01/(1/15625))-1
=155
Thus OCR1A is loaded with 155 and TCNT1 is initialized to zero. As the timer starts, TCNT1 will start counting up to 155, and on 155, an interrupt will be generated and ISR will be executed. As the ISR is generated due to timer1 compare, it is called as ISR(TIMER1_COMPA_vect). UART has also been initialized to have a serial communication between the computer and the microcontroller. The Maximum voltage and the minimum voltage is required to be calculated to find out the average as well as the ratio. The average is inturn used to determine the heart rate by counting the number of average crossings. The average is initially initialized to 60, a value determined by experimenting for the readings on a couple of people. 60 is a fairly constant value for almost every person's reading, i.e. a 60 on ADC is about 1.17V. Thus a crossing will be detected at every average value. The ISR will be executed every 0.01 second, and in ISR, we start loading the value of count in a variable lastcount, so that we have the value of current count as well as previous count all time. Then, as we know that ADCH has the 8 bit digital data, its value is loaded into the count. For checking and determining the maximum and the minimum voltage, we start comparing the maximum voltage (Pre initialized to 0) and the minimum voltage (Pre initialized to 255) with the value of the count, and start upgrading their values. Now, to determine the values of maximum voltage, minimum voltage, average and heart rate in a set period, we need to incorporate average crossing, and on having every average crossing, a variable c is incremented and loaded into d. Variable d is thus the number of ISRs executed. We know that time for one ISR is 0.01 seconds, thus frequency = 1/(0.01d), and heart rate per minute is 60frequency. Also, after determining the maximum and minimum voltages, the ratio x is calculated and used in the SpO2 formula to give its value.
$)
#define F_CPU 16000000UL //16MHz crystal
#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <avr/eeprom.h>
#include <util/delay.h>
#include <stdlib.h>
#include <string.h>
#include <avr/pgmspace.h>
#include "uart.h" // serial communication library
#include "lcd_lib.h" //lcd library
const int8_t LCD_initialize[] PROGMEM ="\0";
int8_t lcd_buffer[17]; // LCD display buffer
int8_t lcd_buffer1[17]; // LCD display buffer
FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW); //UART initialization
volatile int max, min,a,b,count,c,d,average,lastcount;
float HeartRate,Vmax,Vmin,x,y,z;
float value=0.0158;
void timer1_init()
{
TCCR1B |= (1 << WGM12)|(1 << CS12)|(0 << CS11)|(1 << CS10); // set up timer with prescaler = 1024 and CTC mode
TCNT1 = 0; // initialize counter
OCR1A = 155; // initialize compare value
TIMSK1 |= (1 << OCIE1A); // enable compare interrupt
}
void init_lcd(void)
{
LCDinit(); //initialize the display
LCDcursorOFF(); //no cursor
LCDclr(); //clear the display
LCDGotoXY(0,0); //go to x and y position on the lcd
CopyStringtoLCD(LCD_initialize, 0, 0);
}
int main()
{
ADMUX= (0<<REFS1)|(1<<REFS0)|(1<<ADLAR); //AVcc as reference voltage and Left shift bits
ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)| (1<<ADATE); //Prescalar 128 and set bit for free running mode
ADCSRB= (0<<ADTS2)|(0<<ADTS1)|(0<<ADTS0); //Free running mode
init_lcd();
timer1_init(); //timer 1 initialization
average=60; //initialize average value for average crossing detection
max = 0; //initialize maximum =0
min = 255; //initialize minimum = 255
sei(); //Global interrupt enable
UCSR0B = _BV(TXEN0); //Serial transmission
UBRR0 = 103;
ADCSRA |= 1<<ADSC; //start conversion
stdout = stdin = stderr = &uart_str; //UART transmission
LCDclr(); //clear lcd
CopyStringtoLCD(LCD_initialize, 0, 0); //copy the string data to position x=0, position y=0
while (1)
{
z=1/(0.01*d); //calculation of frequency of ISR
HeartRate=z*60; //calculation of heart rate (beats per min)
fprintf(stdout,"HeartRate=%f \n",HeartRate); //UART transmission of heart rate
CopyStringtoLCD(LCD_initialize, 0, 0);
_delay_ms(2000);
sprintf(lcd_buffer,"HeartRate=%2.0f ",HeartRate); //sending heart rate values to lcd for display
LCDGotoXY(0, 0); // goto position x=0, position y=0
LCDstring(lcd_buffer, strlen(lcd_buffer));
sprintf(lcd_buffer1,"SP02=%f ",y); //sending SpO2 values to lcd for display
LCDGotoXY(0, 1); // goto position x=0, position y=1 (second row)
LCDstring(lcd_buffer1, strlen(lcd_buffer));
PORTD=0b00000001; //blinking led every 4 ms
_delay_ms(4);
PORTD=0b00000000;
_delay_ms(4);
PORTD=0b00000010;
_delay_ms(4);
PORTD=0b00000000;
_delay_ms(4);
}
}
ISR(TIMER1_COMPA_vect)
{
lastcount=count; // copying value of count into last count used for average crossing (becomes previous value_
count = ADCH; //copying value of ADCH into count (current value)
//we checking for maximum and minimum value for analog input
if(count>max)
{
max=count; //if current value in count is greater than maximum value, then copy it to maximum
}
else if (count<min)
{
min=count; //if current value in count is less than minimum value, then copy it to minimum
}
c++; //counter increments
if((lastcount>average) && (count<average)) //average crossing condition
{
average=((max+min)/2); //average calculation
a=max;
b=min;
fprintf(stdout,"a=%i\n",a); //UART transmission
fprintf(stdout,"b=%i\n",b);
x=value/(a-b); //calculation of ratio
fprintf(stdout,"x=%f\n",x);
y=(10.0002*x*x*x)-(52.887*x*x)+(26.871*x)+98.283; //calculation of SpO2
fprintf(stdout,"y=%f\n",y);
d=c; //copy counter value to d
c=0; //clear counter
fprintf(stdout,"d=%i\n",d);
max=0; //clear max
min=255; //clear min
}}