How to make a heartrate monitor ?

Status
Not open for further replies.
I have a pulse watch. I press my finger on its metal surface, and after a second it displays my heart rate.

It updates my heart rate after every heartbeat. I would rather see it update frequently, rather than wait ten seconds each time.
 
Hello!

Should I measure time between two pulses (two rising edges) and calculate BPM from that or should I count number of pulses in 10 seconds and multiply it by 6

Beside what BradtheRad replied, what you are asking is a matter of accuracy.
If you have a signal of frequency f, how to measure it with a reference frequency fr?
In the heartbeat case, the frequency is around 1 Hz. If you measure it with a frequency of, say, 1MHz, then you will have a
resolution of 1 µs which is more than enough for a hear beat that you want to measure with a resolution of about 1 beat
per minute.
If you measure a much higher frequency, you can still do it with the same method if you divide it first by a ratio large enough
to be measurable with your reference frequency.

Dora.
 
Ok. Here is my code and calculations but it is now working in Proteus and also hardware. So, what is wrong ? The displays always show 0.0.

I am using mikroC PRO PIC Compiler, PIC18F45K22, 4 MHz external crystal. mikroC project and Proteus 8.1 SP1 format file attached.


The working and calculations are like this

I have a 100 ms Timer1 Interrupt and INT0 Interrupt

In ISR when 1st INT0 is triggered that is first pulse is detected
then a timer (100 ms) is started and when second INT0 (pulse) is detected
the timer is stopped.

Now the maths

Lets say 800 + x ms was the time between two pulses. Then

the variable counter in timer ISR would hold 8 which means 800 ms.
I divide it by 10 to get 0.8 seconds.

Now the fraction. Lets say the remaining value in TMRH and TMRL registers was
60000

For 65536 ticks the time is 100 ms, for 1 tick, time is 100ms / 65536 = 1.52587890625e-6 seconds

Now I subtract

65536 - 60000 = 5536

Now I multiply

5536 * 1.52587890625e-6 = 0.008447265625 seconds

So, total time between two pulses is

time = 0.8 + 0.008447265625 seconds = 0.808447265625 seconds

I convert this to frequency

I know I can calculate BPM (heartrate) like heartRate = 60 / time

frequency = 1 / time

= 1 / 0.808447265625 = 1.236939059008274 Hz

heartRate = 60 * frequency

= 60 * 1.236939059008274 Hz = 74.21634354049647 BPM

but this is not giving any values. Why ?



Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#include "float2ascii.h"
 
sbit MAX7219_DATA at LATD0_bit;
sbit MAX7219_CS at LATD1_bit ;
sbit MAX7219_CLK at LATD2_bit;
 
sbit MAX7219_DATA_Direction at TRISD0_bit;
sbit MAX7219_CS_Direction at TRISD1_bit;
sbit MAX7219_CLK_Direction at TRISD2_bit;
 
const unsigned char Font_B[16] = {0x7e,0x30,0x6d,0x79,0x33,0x5b,0x5f,0x70,0x7f,0x7b,0x77,0x1f,0x4e,0x3d,0x4f,0x47};
double counter = 0, beats_per_minute = 0, time_in_seconds = 0, frequency = 0;
 
void MAX7219_Send_Byte(unsigned char);
void MAX7219_Send(char, unsigned char);
void MAX7219_Init();
void MAX7219_Display(unsigned char);
 
void Interrupt(){
 
    if((INT0IF_bit) && (TMR1ON_bit == 0)) {
        TMR1ON_bit = 1;
        INT0IF_bit = 0;
    }
 
    if((INT0IF_bit) && (TMR1ON_bit)) {
        TMR1ON_bit = 0;
        INT0IE_bit = 0;
        INT0IF_bit = 0;
    }
 
 
    if(TMR1IF_bit){
        TMR1IF_bit = 0;
        TMR1H = 0x3C;
        TMR1L = 0xB0;
        
        //Enter your code here
        counter = counter + 1;
    }
}
 
 
void MAX7219_Init() {
    MAX7219_DATA_Direction = 0;
    MAX7219_CS_Direction = 0;
    MAX7219_CLK_Direction = 0;
    MAX7219_DATA = 0;
    MAX7219_CS = 0;
    MAX7219_CLK = 0;
    MAX7219_Send(0x09, 0x00); //Decode Mode
    MAX7219_Send(0x0A, 0x05); //Brightness
    MAX7219_Send(0x0B, 0x07); //Scan limit
    MAX7219_Send(0x0C, 0x01);
    MAX7219_Send(0x0F, 0x00);
}
 
void MAX7219_Send(char digit, unsigned char data_) {
    MAX7219_Send_Byte(digit);
    MAX7219_Send_Byte(data_);
    MAX7219_DATA = 0;
    MAX7219_CS = 1;
    Delay_us(20);
    MAX7219_CS = 0;
}
 
void MAX7219_Send_Byte(unsigned char byte) {
    unsigned char i;
 
    for(i = 0; i < 8; i++, byte <<= 1)
    {
        if(byte & 0x80)
                MAX7219_DATA = 1;
        else
                MAX7219_DATA = 0;
 
        MAX7219_CLK = 1;
        Delay_us(50);
        MAX7219_CLK = 0;
    }
}
 
void MAX7219_Display(double myDbl) {
 
    unsigned char dispskip;
    unsigned char i, dp, str[30];
 
    float2ascii(myDbl, str, 1);
    Ltrim(str);
    dispskip = strlen(str) - 1;
 
    if(dispskip >= 8)dispskip = 7;
 
    i = 0;
    dp = 0;
    while(i <= dispskip) {
 
            if((str[dispskip-i] >= 0x30) && (str[dispskip - i] <= 0x39) && (dp == 0)) {
                  MAX7219_Send(i+1, (Font_B[str[dispskip-i] - 0x30]));//0-9
            }
 
            if((str[dispskip-i] >= 0x30) && (str[dispskip - i] <= 0x39) && (dp)) {
                  MAX7219_Send(i, (Font_B[str[dispskip-i] - 0x30]));//0-9
            }
 
            if(str[dispskip-i] == '.') {
                   MAX7219_Send(i+1, (Font_B[str[dispskip-(i+1)] - 0x30]) | 0x80);//
                   i++;
                   dp = 1;
            }
 
            i++;
    }
 
    while(i <= 8) {
          MAX7219_Send(i, 0);
          i++;
    }
}
 
void main() {
 
    CM1CON0 = 0x00;
    CM2CON0 = 0x00;
 
    ANSELA = 0x00;
    ANSELB = 0x00;
    ANSELC = 0x00;
    ANSELD = 0x00;
    ANSELE = 0x00;
 
    TRISA = 0xC0;
    TRISB = 0x01;
    TRISC = 0x01;
    TRISD = 0x00;
    TRISE = 0x08;
 
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;
    PORTD = 0x00;
    PORTE = 0x00;
 
    LATA = 0x00;
    LATB = 0x00;
    LATC = 0x00;
    LATD = 0x00;
    LATE = 0x00;
 
    MAX7219_Init();
    MAX7219_Display(beats_per_minute);
 
    while(1) {
 
        counter = 0;
        beats_per_minute = 0;
        time_in_seconds = 0;
        frequency = 0;
        
        //Timer1
        //Prescaler 1:2; TMR1 Preload = 15536; Actual Interrupt Time : 100 ms
        //Place/Copy this part in declaration section
 
 
        /*
        
        TxCON
        
        bit 7-6 TMRxCS<1:0>: Timer1/3/5 Clock Source Select bits
        11 =Reserved. Do not use.
        10 =Timer1/3/5 clock source is pin or oscillator:
        If TxSOSCEN = 0:
        External clock from TxCKI pin (on the rising edge)
        If TxSOSCEN = 1:
        Crystal oscillator on SOSCI/SOSCO pins
        01 =Timer1/3/5 clock source is system clock (F OSC )
        00 =Timer1/3/5 clock source is instruction clock (F OSC /4)
        bit 5-4 TxCKPS<1:0>: Timer1/3/5 Input Clock Prescale Select bits
        11 = 1:8 Prescale value
        10 = 1:4 Prescale value
        01 = 1:2 Prescale value
        00 = 1:1 Prescale value
        bit 3 TxSOSCEN: Secondary Oscillator Enable Control bit
        1 = Dedicated Secondary oscillator circuit enabled
        0 = Dedicated Secondary oscillator circuit disabled
        bit 2 TxSYNC: Timer1/3/5 External Clock Input Synchronization Control bit
        TMRxCS<1:0> = 1X
        1 = Do not synchronize external clock input
        0 = Synchronize external clock input with system clock (F OSC )
        TMRxCS<1:0> = 0X
        This bit is ignored. Timer1/3/5 uses the internal clock when TMRxCS<1:0> = 1X.
        bit 1 TxRD16: 16-Bit Read/Write Mode Enable bit
        1 = Enables register read/write of Timer1/3/5 in one 16-bit operation
        0 = Enables register read/write of Timer1/3/5 in two 8-bit operation
        bit 0 TMRxON: Timer1/3/5 On bit
        1 = Enables Timer1/3/5
        0 = Stops Timer1/3/5
        Clears Timer1/3/5 Gate flip-flop
 
        */
 
        T1CON = 0x11;
        TMR1IF_bit = 0;
        TMR1H = 0x3C;
        TMR1L = 0xB0;
        TMR1ON_bit = 0;
        TMR1IE_bit = 1;
 
        INTEDG0_bit = 0;
        INT0IF_bit = 0;
        
        GIE_bit = 0;
        
        INT0IE_bit = 1;
 
        PEIE_bit = 1;
        GIE_bit = 1;
        
        while(!TMR1ON_bit);
        while(TMR1ON_bit);
        
        Delay_ms(500);
        
        time_in_seconds = ((counter / 10.0) + ((65536 - ((TMR1H << 8) + TMR1L)) * 1.52587890625e6));
        frequency = 1.0 / time_in_seconds;
        beats_per_minute = 60 * frequency;
        
        MAX7219_Display(beats_per_minute);
    }
}



- - - Updated - - -

Edit

I changed to

T1CON = 0b0101000;

but still not working.
 

Attachments

  • Heartrate Monitor.rar
    101.6 KB · Views: 151
Last edited:

I am only getting 0.0. Please somebody answer.
 

Attachments

  • hrm.png
    27.3 KB · Views: 180

My calculation was wrong and also I don't know how the micro second became mega second in the formula.

The working and calculations are like this

I have a 100 ms Timer1 Interrupt and INT0 Interrupt.

In ISR when 1st INT0 is triggered that is first pulse is detected
then a timer (100 ms) is started and when second INT0 (pulse) is detected
the timer is stopped.

Now the maths

Lets say 800 + x ms was the time between two pulses. Then

the variable counter in timer ISR would hold 8 which means 800 ms.
I divide it by 10 to get 0.8 seconds.

Now the fraction. Lets say the value TMRH and TMRL registers was 15540

Timer preload is 15536. Timer needs 65536 - 15536 = 50000 to overflow.

For 50000 ticks the time is 100 ms, for 1 tick, time is 100ms / 50000 = 2e-6 seconds

Now I subtract

15540 - 15536 = 4 ticks = 4 * 2e-6 = 0.000008 (8e-6)

So, total time between two pulses is

time = 0.8 + 0.000008 seconds = 0.800008 seconds

I convert this to frequency

I know I can calculate BPM (heartrate) like heartRate = 60 / time

frequency = 1 / time

= 1 / 0.800008 = 1.249987500124999 Hz

heartRate = 60 * frequency

= 60 * 1.249987500124999 Hz = 74.99925000749993 BPM

Another example

if Frequency is 1 Hz then there will be 10 x 100ms timer1 interrupts between two pulses and so
the value of counter will be 10.

TMRH and TMRL values would be 15536

so time in seconds will be

(counter / 10) + ((15536 - 15536) * 2e-6)

= 1 + 0 = 1

frequency will be 1 Hz

beats per minute will be frequency * 60 = 1 * 60 = 60

but this is not giving correct values. Why ?

I have used mikroE Timer Calculator tool to generate Timer configuration code. Is it wrong ?

Here is the new code. But still it is not giving 60 BPM for 1 Hz. It is giving some 54.7. See image. It shows value for 1 Hz. What is the problem ?


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#include "float2ascii.h"
 
sbit MAX7219_DATA at LATD0_bit;
sbit MAX7219_CS at LATD1_bit ;
sbit MAX7219_CLK at LATD2_bit;
 
sbit MAX7219_DATA_Direction at TRISD0_bit;
sbit MAX7219_CS_Direction at TRISD1_bit;
sbit MAX7219_CLK_Direction at TRISD2_bit;
 
const unsigned char Font_B[16] = {0x7e,0x30,0x6d,0x79,0x33,0x5b,0x5f,0x70,0x7f,0x7b,0x77,0x1f,0x4e,0x3d,0x4f,0x47};
double counter = 0.0, beats_per_minute = 0.0, time_in_seconds = 0.0, frequency = 0.0;
 
void MAX7219_Send_Byte(unsigned char);
void MAX7219_Send(char, unsigned char);
void MAX7219_Init();
void MAX7219_Display(double);
 
void interrupt(){
 
    if((INT0IF_bit) && (TMR1IE_bit == 0)) {
        TMR1IE_bit = 1;
        INT0IF_bit = 0;
    }
 
    if((INT0IF_bit) && (TMR1IE_bit)) {
        TMR1IE_bit = 0;
        INT0IE_bit = 0;
        INT0IF_bit = 0;
    }
 
    if(TMR1IF_bit) {
        TMR1IF_bit = 0;
        TMR1H = 0x3C;
        TMR1L = 0xB0;
        //Enter your code here
        counter = counter + 1;
    }
}
 
 
void MAX7219_Init() {
    MAX7219_DATA_Direction = 0;
    MAX7219_CS_Direction = 0;
    MAX7219_CLK_Direction = 0;
    MAX7219_DATA = 0;
    MAX7219_CS = 0;
    MAX7219_CLK = 0;
    MAX7219_Send(0x09, 0x00); //Decode Mode
    MAX7219_Send(0x0A, 0x05); //Brightness
    MAX7219_Send(0x0B, 0x07); //Scan limit
    MAX7219_Send(0x0C, 0x01);
    MAX7219_Send(0x0F, 0x00);
}
 
void MAX7219_Send(char digit, unsigned char data_) {
    MAX7219_Send_Byte(digit);
    MAX7219_Send_Byte(data_);
    MAX7219_DATA = 0;
    MAX7219_CS = 1;
    Delay_us(20);
    MAX7219_CS = 0;
}
 
void MAX7219_Send_Byte(unsigned char byte) {
    unsigned char i;
 
    for(i = 0; i < 8; i++, byte <<= 1)
    {
        if(byte & 0x80)
                MAX7219_DATA = 1;
        else
                MAX7219_DATA = 0;
 
        MAX7219_CLK = 1;
        Delay_us(50);
        MAX7219_CLK = 0;
    }
}
 
void MAX7219_Display(double myDbl) {
 
    unsigned char dispskip;
    unsigned char i, dp;
    char str[30];
 
    float2ascii(myDbl, str, 1);
    Ltrim(str);
    dispskip = strlen(str) - 1;
 
    if(dispskip >= 8)dispskip = 7;
 
    i = 0;
    dp = 0;
    while(i <= dispskip) {
 
            if((str[dispskip-i] >= 0x30) && (str[dispskip - i] <= 0x39) && (dp == 0)) {
                  MAX7219_Send(i+1, (Font_B[str[dispskip-i] - 0x30]));//0-9
            }
 
            if((str[dispskip-i] >= 0x30) && (str[dispskip - i] <= 0x39) && (dp)) {
                  MAX7219_Send(i, (Font_B[str[dispskip-i] - 0x30]));//0-9
            }
 
            if(str[dispskip-i] == '.') {
                   MAX7219_Send(i+1, (Font_B[str[dispskip-(i+1)] - 0x30]) | 0x80);//
                   i++;
                   dp = 1;
            }
 
            i++;
    }
 
    while(i <= 8) {
          MAX7219_Send(i, 0);
          i++;
    }
}
 
void main() {
 
    CM1CON0 = 0x00;
    CM2CON0 = 0x00;
 
    ANSELA = 0x00;
    ANSELB = 0x00;
    ANSELC = 0x00;
    ANSELD = 0x00;
    ANSELE = 0x00;
 
    TRISA = 0xC0;
    TRISB = 0x01;
    TRISC = 0x00;
    TRISD = 0x00;
    TRISE = 0x08;
 
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;
    PORTD = 0x00;
    PORTE = 0x00;
 
    LATA = 0x00;
    LATB = 0x00;
    LATC = 0x00;
    LATD = 0x00;
    LATE = 0x00;
 
    MAX7219_Init();
    MAX7219_Display(beats_per_minute);
 
    while(1) {
 
        counter = 0;
        beats_per_minute = 0;
        time_in_seconds = 0;
        frequency = 0;
        
        //Timer1
        //Prescaler 1:2; TMR1 Preload = 15536; Actual Interrupt Time : 100 ms
        //Place/Copy this part in declaration section
 
        /*
        
        TxCON
        
        bit 7-6 TMRxCS<1:0>: Timer1/3/5 Clock Source Select bits
        11 =Reserved. Do not use.
        10 =Timer1/3/5 clock source is pin or oscillator:
        If TxSOSCEN = 0:
        External clock from TxCKI pin (on the rising edge)
        If TxSOSCEN = 1:
        Crystal oscillator on SOSCI/SOSCO pins
        01 =Timer1/3/5 clock source is system clock (F OSC )
        00 =Timer1/3/5 clock source is instruction clock (F OSC /4)
        bit 5-4 TxCKPS<1:0>: Timer1/3/5 Input Clock Prescale Select bits
        11 = 1:8 Prescale value
        10 = 1:4 Prescale value
        01 = 1:2 Prescale value
        00 = 1:1 Prescale value
        bit 3 TxSOSCEN: Secondary Oscillator Enable Control bit
        1 = Dedicated Secondary oscillator circuit enabled
        0 = Dedicated Secondary oscillator circuit disabled
        bit 2 TxSYNC: Timer1/3/5 External Clock Input Synchronization Control bit
        TMRxCS<1:0> = 1X
        1 = Do not synchronize external clock input
        0 = Synchronize external clock input with system clock (F OSC )
        TMRxCS<1:0> = 0X
        This bit is ignored. Timer1/3/5 uses the internal clock when TMRxCS<1:0> = 1X.
        bit 1 TxRD16: 16-Bit Read/Write Mode Enable bit
        1 = Enables register read/write of Timer1/3/5 in one 16-bit operation
        0 = Enables register read/write of Timer1/3/5 in two 8-bit operation
        bit 0 TMRxON: Timer1/3/5 On bit
        1 = Enables Timer1/3/5
        0 = Stops Timer1/3/5
        Clears Timer1/3/5 Gate flip-flop
 
        */
        
        GIE_bit = 0;                   //Disable Interrupts till congiguration is done
        
        T1CON = 0x11;                  //Configure timer 1 for 100 ms interrupt
        TMR1IF_bit = 0;
        T1GCON = 0x40;
        TMR1IE_bit = 0;
        TMR1ON_bit = 1;
        TMR1H = 0x3C;
        TMR1L = 0xB0;
        
        INTEDG0_bit = 0;               //Configure INT0 interrupt to detect rising edge
        INT0IF_bit = 0;
        INT0IE_bit = 1;
        
        PEIE_bit = 1;                  //Enable interrupts
        GIE_bit = 1;
        
        while(!TMR1IE_bit);            //TMR1IE_bit is set in ISR when first pulse is detected
        
        while(TMR1IE_bit);             //TMR1IE_bit is cleared in ISR when 2nd pulse is detected
        
        time_in_seconds = ((counter / 10) + ((((TMR1H << 8) + TMR1L) - 15536) * 2e-6));
        //time_in_seconds = ((counter * 50000 * 2e-6) + ((((TMR1H << 8) + TMR1L) - 15536) * 2e-6));
 
        frequency = 1 / time_in_seconds;
        beats_per_minute = 60.0 * frequency;
 
        MAX7219_Display(beats_per_minute);
    }
}

 

Attachments

  • hrm.png
    27.2 KB · Views: 161
  • Heartrate Monitor rev1.rar
    130.2 KB · Views: 157
Last edited:

Mods said not to attach big videos here and instead use youtube links. My video is of poor quality and youtube makes it more poor by changing to lower resolution and so I attached video of sensor working at mikroe forum.

I am providing mikroe forum link here. You can download video from there.

https://www.mikroe.com/forum/viewtopic.php?f=147&t=63763&p=253821#p253821
 

I fixed the code and now it works fine and precisely. Here is Proteus simulation and .hex files. My Proteus file is Proteus 8.1 SP1 format.
 

Attachments

  • Heartrate Monitor working.rar
    23.2 KB · Views: 169
  • hrm working.png
    56.6 KB · Views: 174

I fixed the code and now it works fine and precisely. Here is Proteus simulation and .hex files. My Proteus file is Proteus 8.1 SP1 format.

Very positive. Congratulations!

Regarding videos...
Your video does not contain spam ads or disallowed content.
However we cannot be sure that is the case with all videos (if we were to encourage video attachments). For that reason Edaboard's moderators would need to sit through every video, and examine it for disallowed content, if we were to encourage video attachments.
 

Thank you BradtheRad.

I will soon post the project (code) files here. I am just making three versions of the project like one for 4 bit LCD, one for 12C LCD and another for MAX7219 7 Segment display types.
 

I think still there is some mistake in my calculation. My T1CON value is 0x10 and Fosc is 4 MHz. So Timer1 prescalar is 1:2. How should I do the calculation ?

I am getting correct value but if I change Fosc to 8 MHz and T1CON to 0x20 (1:4 prescalar) and use a 100 ms timer1 interrupt then I get wrong values.

Here is my code.

How to use the prescalar in my calculation ? If I use Timer1 interrupt with 1:4 prescalar then should not I multiply by 4 in the calculation ? But here for bpm if 60 * frequency * 2 is used then I get the correct value when Fosc is 8 Mhz and T1CON is 0x20 (1:4 prescalar).

I am attaching my complete project which works for Fosc = 4 MHz and T1OSC = 0x10 = (1:2 prescalar). Please tell me how should I take care of the Prescalar used in the calaculation.


If I use Fosc of 8 MHz and T1CON = 0x20 (1:4 prescalar) then I get 120 BPM for 1 Hz which is wrong. I should get 60 BPM. So, what changes should I do to the calculation. I used BPM = 60 * frequency * 2; and got the correct value but I don't know why I should divide by 2 when prescalar is 1:4. Should not I multiply time by 4 as prescalar divided it by 4 ?


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#include "float2ascii.h"
 
sbit MAX7219_DATA at LATD0_bit;
sbit MAX7219_CS at LATD1_bit ;
sbit MAX7219_CLK at LATD2_bit;
 
sbit MAX7219_DATA_Direction at TRISD0_bit;
sbit MAX7219_CS_Direction at TRISD1_bit;
sbit MAX7219_CLK_Direction at TRISD2_bit;
 
const unsigned char Font_B[16] = {0x7e,0x30,0x6d,0x79,0x33,0x5b,0x5f,0x70,0x7f,0x7b,0x77,0x1f,0x4e,0x3d,0x4f,0x47};
double counter = 0.0, beats_per_minute = 0.0, time_in_seconds = 0.0, frequency = 0.0;
 
void MAX7219_Send_Byte(unsigned char);
void MAX7219_Send(char, unsigned char);
void MAX7219_Init();
void MAX7219_Display(double);
 
void interrupt(){
 
    if((INT0IF_bit) && (!TMR1ON_bit)) {
        TMR1ON_bit = 1;
        INT0IF_bit = 0;
 
    }
 
    if((INT0IF_bit) && (TMR1ON_bit)) {
        TMR1ON_bit = 0;
        INT0IE_bit = 0;
        INT0IF_bit = 0;
    }
 
    if(TMR1IF_bit) {
        TMR1IF_bit = 0;
        TMR1H = 0x3C;
        TMR1L = 0xB0;
        //Enter your code here
        counter = counter + 1;
    }
}
 
 
void MAX7219_Init() {
    MAX7219_DATA_Direction = 0;
    MAX7219_CS_Direction = 0;
    MAX7219_CLK_Direction = 0;
    MAX7219_DATA = 0;
    MAX7219_CS = 0;
    MAX7219_CLK = 0;
    MAX7219_Send(0x09, 0x00); //Decode Mode
    MAX7219_Send(0x0A, 0x05); //Brightness
    MAX7219_Send(0x0B, 0x07); //Scan limit
    MAX7219_Send(0x0C, 0x01);
    MAX7219_Send(0x0F, 0x00);
}
 
void MAX7219_Send(char digit, unsigned char data_) {
    MAX7219_Send_Byte(digit);
    MAX7219_Send_Byte(data_);
    MAX7219_DATA = 0;
    MAX7219_CS = 1;
    Delay_us(20);
    MAX7219_CS = 0;
}
 
void MAX7219_Send_Byte(unsigned char byte) {
    unsigned char i;
 
    for(i = 0; i < 8; i++, byte <<= 1)
    {
        if(byte & 0x80)
                MAX7219_DATA = 1;
        else
                MAX7219_DATA = 0;
 
        MAX7219_CLK = 1;
        Delay_us(50);
        MAX7219_CLK = 0;
    }
}
 
void MAX7219_Display(double myDbl) {
 
    unsigned char dispskip;
    unsigned char i, dp;
    char str[30];
 
    float2ascii(myDbl, str, 1);
    Ltrim(str);
    dispskip = strlen(str) - 1;
 
    if(dispskip >= 8)dispskip = 7;
 
    i = 0;
    dp = 0;
    while(i <= dispskip) {
 
            if((str[dispskip-i] >= 0x30) && (str[dispskip - i] <= 0x39) && (dp == 0)) {
                  MAX7219_Send(i+1, (Font_B[str[dispskip-i] - 0x30]));//0-9
            }
 
            if((str[dispskip-i] >= 0x30) && (str[dispskip - i] <= 0x39) && (dp)) {
                  MAX7219_Send(i, (Font_B[str[dispskip-i] - 0x30]));//0-9
            }
 
            if(str[dispskip-i] == '.') {
                   MAX7219_Send(i+1, (Font_B[str[dispskip-(i+1)] - 0x30]) | 0x80);//
                   i++;
                   dp = 1;
            }
 
            i++;
    }
 
    while(i <= 8) {
          MAX7219_Send(i, 0);
          i++;
    }
}
 
void main() {
 
    CM1CON0 = 0x00;
    CM2CON0 = 0x00;
 
    ANSELA = 0x00;
    ANSELB = 0x00;
    ANSELC = 0x00;
    ANSELD = 0x00;
    ANSELE = 0x00;
 
    TRISA = 0xC0;
    TRISB = 0x01;
    TRISC = 0x00;
    TRISD = 0x00;
    TRISE = 0x08;
 
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;
    PORTD = 0x00;
    PORTE = 0x00;
 
    LATA = 0x00;
    LATB = 0x00;
    LATC = 0x00;
    LATD = 0x00;
    LATE = 0x00;
 
    MAX7219_Init();
    MAX7219_Display(beats_per_minute);
 
    while(1) {
 
        counter = 0;
        beats_per_minute = 0;
        time_in_seconds = 0;
        frequency = 0;
        
        //Timer1
        //Prescaler 1:2; TMR1 Preload = 15536; Actual Interrupt Time : 100 ms
        //Place/Copy this part in declaration section
 
        /*
        
        TxCON
        
        bit 7-6 TMRxCS<1:0>: Timer1/3/5 Clock Source Select bits
        11 =Reserved. Do not use.
        10 =Timer1/3/5 clock source is pin or oscillator:
        If TxSOSCEN = 0:
        External clock from TxCKI pin (on the rising edge)
        If TxSOSCEN = 1:
        Crystal oscillator on SOSCI/SOSCO pins
        01 =Timer1/3/5 clock source is system clock (F OSC )
        00 =Timer1/3/5 clock source is instruction clock (F OSC /4)
        bit 5-4 TxCKPS<1:0>: Timer1/3/5 Input Clock Prescale Select bits
        11 = 1:8 Prescale value
        10 = 1:4 Prescale value
        01 = 1:2 Prescale value
        00 = 1:1 Prescale value
        bit 3 TxSOSCEN: Secondary Oscillator Enable Control bit
        1 = Dedicated Secondary oscillator circuit enabled
        0 = Dedicated Secondary oscillator circuit disabled
        bit 2 TxSYNC: Timer1/3/5 External Clock Input Synchronization Control bit
        TMRxCS<1:0> = 1X
        1 = Do not synchronize external clock input
        0 = Synchronize external clock input with system clock (F OSC )
        TMRxCS<1:0> = 0X
        This bit is ignored. Timer1/3/5 uses the internal clock when TMRxCS<1:0> = 1X.
        bit 1 TxRD16: 16-Bit Read/Write Mode Enable bit
        1 = Enables register read/write of Timer1/3/5 in one 16-bit operation
        0 = Enables register read/write of Timer1/3/5 in two 8-bit operation
        bit 0 TMRxON: Timer1/3/5 On bit
        1 = Enables Timer1/3/5
        0 = Stops Timer1/3/5
        Clears Timer1/3/5 Gate flip-flop
 
        */
        
        GIE_bit = 0;                   //Disable Interrupts till congiguration is done
        
        T1CON = 0x10;                  //Configure timer 1 for 100 ms interrupt
        TMR1IF_bit = 0;
        TMR1IE_bit = 1;
        TMR1H = 0x3C;
        TMR1L = 0xB0;
        
        INTEDG0_bit = 1;               //Configure INT0 interrupt to detect rising edge
        INT0IF_bit = 0;
        INT0IE_bit = 1;
        
        PEIE_bit = 1;                  //Enable interrupts
        GIE_bit = 1;
        
        while(!TMR1ON_bit);            //TMR1ON_bit is set in ISR when first pulse is detected
        
        while(TMR1ON_bit);             //TMR1ON_bit is cleared in ISR when 2nd pulse is detected
        
        time_in_seconds = ((counter / 10) + ((((TMR1H << 8) + TMR1L) - 15536) * 2e-6));
        //time_in_seconds = ((counter * 50000 * 2e-6) + ((((TMR1H << 8) + TMR1L) - 15536) * 2e-6));
 
        frequency = 1.0 / time_in_seconds;
        beats_per_minute = 60.0 * frequency;
 
        MAX7219_Display(beats_per_minute);
    }
}



- - - Updated - - -

I don't have to consider prescalar value as I am doing calculation using 100 ms timer interrupt. I had done a mistake. I had changed clock to 8 MHz in code but was using 4 MHz in Proteus. Without any changes to the code I am getting 60 as BPM for 1 MHz input when Fosc is 8 MHz and T1CON is 0x20 (1:4 prescalar) and also when Fosc is 16 MHz and T1CON = 0x30 (1:8 prescalar).

- - - Updated - - -

The image shows that when a 10 Hz clock is fed to the heartrate monitor's RB0/INT0 pin I get exact 600 BPM but when I use elecrow heartrate sensor to feed pulses to heartrate monitor then the value fluctuates between two values. Am I missing something in code ? How to make the value stable ?
 

Attachments

  • Heartrate Monitor.rar
    94.5 KB · Views: 136
  • heartrate monitor.png
    401.3 KB · Views: 177
Last edited:

In the previous post there was a typo. I am getting 60 BPM for 1 Hz and not 1 MHz.
 

Does the right-hand digit change? My digital meter has 4 digits, and it does something similar. It might read a voltage as 1.222 one moment, then 1.223 the next moment. The real voltage is somewhere in between.

It's okay with me. It gives me more precision than I need, really.
 

Between heartbeats, your program counts up to a number of cycles (or ticks, or whatever is your unit of time). Then your program does arithmetic, and obtains the pulse rate.

When we are at rest, our pulse rate stays about the same. However you are getting a final result which fluctuates between two values.

To solve this problem, you will need to display the sequence of results, each step of the way, from the number of cycles between heartbeats, to the final result.

You need to examine how fast the counter increases between heartbeats.
 

I am disabling and enabling the timer in the code for each read. Also the timer reload values are loaded for 100 ms timer interrupts. So, I think every loop the same piece of code executes. Also timer is enabled when first pulse's rising edge is detected and stopped on detecting the 2nd pulse's rising edge. Is it not enough. If I feed pulse from signal generator then I get a stable value. It is only when heartrate sensor is connected I get fluctuating value.
 

Also the timer reload values are loaded for 100 ms timer interrupts.

This is 1/10 second. Is that the resolution of each heartbeat period? (I'm not familiar with the program language.)

I believe you need a timer which has resolution to the 1/100 second, so you can calculate a heart rate with 2 digits of accuracy.

For heart rates over a hundred bpm, it depends on whether you want 3 digits of accuracy you want. If so then you'll want your timer to resolve to 1/200 of a second (I think).

It's a good idea to display a verbose report on all that your program is doing, from one heartbeat to the next.
Print a line saying 'heartbeat detected', time in milliseconds, etc.
 

Status
Not open for further replies.

Similar threads

Cookies are required to use this site. You must accept them to continue using the site. Learn more…