need help with accurate frequency measurement

Hello, I am trying to build a VFD without galvanic isolation based on arduino mega. I need to measure the high DC voltage after the rectifier, to do so I am planning to use a V/F converter LM331 and an optocoupler to isolate the sensitive parts from the mains. Since i don’t have all the components at hand I am using Proteus to simulate the circuits. I am using pulseInLong to measure the with of the pulse after the JK trigger, because the output of the optocoupler does not have a sharp rising edge.
Here is what I currently have:

At the input of the Lm331 (left side of R2) I have a voltage from 0 to 10 V which at the output corresponds to frequency of 0-10kHz. When I try to measure the frequency, the result is verry unstable and unsuitable for my needs.

This is the code I am using just to test the vf convertion:

#define pulse_ip 30
int period;
byte i=0;
float freq, volts;

void setup()
{
  Serial.begin(9600);
}
void loop()
{  
  
   period = pulseInLong(pulse_ip,HIGH);
   freq = 1000.0/period; //frequenci in kHz
   Serial.println(freq);

}

This is the output on the serial interface, when the input is 5V:

5.00
5.00
5.00
4.81
5.00
4.90
4.90
5.00
4.81
4.90
4.90
5.00
4.90
5.00
4.81
5.00
4.90
4.90
4.90
4.90
5.00
5.00
4.90
5.00
5.00
5.00
4.90
5.00
4.90
5.10
5.00
5.00
5.00
5.00
5.00
5.00
5.00
5.00
5.00
5.00
4.90
5.00
5.00
5.00
5.00
5.00
4.90
5.00
4.81
5.00
5.00
5.00
4.90
4.90
5.00
5.00
4.90
4.90
5.00
4.81
5.00
5.00
4.90
5.00
4.90
4.90
5.00
5.00
5.00
5.00
4.90
5.00
4.90
5.00
4.90
5.00
4.90
5.00

Can you suggest me a method that has a better accuracy and possibly requires as little processing time as possible?

EDIT
Found a sollution. used the input capture pin of timer 4, as DrDiettrich suggesed. This is the code:

#include <avr/io.h>
#include <avr/interrupt.h>

void setup()
{
  Serial.begin(115200);
  //Timer4 setup
  TCCR4A = 0b00000000;
  TCCR4B = 0b00000010; // 0bXXXXX010 means internal clock with clk/8 prescaler
  TIMSK4 = 0b00100000; //Input capture interrupt enabled
  
  sei();

  
}
void loop()
{  
  //empty
}

ISR(TIMER4_CAPT_vect){
  static unsigned int currentTime;
  static unsigned int lastTime=0;
  //const float _step = 0.5; //one increment of the timer4 is equal to 0.5 us 
  int freq;
  float timePeriod;
  currentTime = ICR4;
  
  if(lastTime > currentTime){ //if the timer oveflowed add the difference
    timePeriod = (0xFFFF-lastTime+currentTime)/2; // one increment of the timer4 is equal to 0.5 us, by dividing by 2 the total period of the pulse is recieved in us
  }
  else{
    timePeriod = (currentTime-lastTime)/2;
  }
  timePeriod /=1000; // convert to ms
  freq = 1/(timePeriod/1000); //freqency in Hz
  lastTime = currentTime;
  Serial.println(freq);
}

Dunno what pulseInLong does. A frequency can be determined by measuring the time of a full wave, or the number of full waves in a longer time interval. A half wave duration, as measured by pulseIn(), is very unreliable.

pulseInLong() is verry similar to pulseIn(), but is supposed to work better with enabled interrupts. Since I have a JK trigger connected at the output of the optocoupler one HIGH or LOW period is equal to the full period of the signal from the Lm331, seen in the picture below


Yellow line is the output from the lm331,
blue line is from the optocoupler
and pink line is from the output of the JK trigger and is connected to pin 30 of the Arduino MEGA.

What if you wait for a LOW input before you start timing a HIGH pulse?

Notice that the output values are quantized to three discrete values (5.0, 4.90, 4.81).

You're almost certainly seeing the effect of pulseIn() time quantized to the resolution of micros() which is 4 microseconds, thus a count of 50 for the 200 microsecond period of your 5000 Hz nominal frequency. The 4.9 reading would be 51 counts of micros() and 4.81 would be 52 counts.

To get a better measurement using pulseIn() you'll need to operate at a lower frequency, either by changing the V/F circuit, by dividing the frequency output down prior to measuring the pulse width, or by averaging enough pulseIn() measurements.

Thank you for the ideas. I tried a different approach, instead of measuring the with of a single impulse I thought about counting impulses over time, using Timer4 with external clock, but it did not work at all, the results are all over the place.
This is the scech:

unsigned long currentTime;
unsigned long lastTime = 0;
unsigned int pulseCount, period;
float freq, voltage;
void setup()
{
  Serial.begin(9600);
  TCCR4A = 0b00000000;
  TCCR4B = 0b00000110;
}
void loop()
{  
  currentTime = micros();
  pulseCount=TCNT4;
  TCNT4 = 0;
  if (currentTime < lastTime) 
  {
    currentTime = 0xFFFFFFFF - lastTime + currentTime;
  }
  period = lastTime-currentTime;
  freq = (float)pulseCount/((float)period/1000);// frequency in kHz
  Serial.println(freq);
  delay(50);
}

and this is the output on the serial terminal:

0.02
1.78
4.38
2.74
2.06
12.02
4.90
3.07
2.24
22.48
6.04
3.48
2.45
176.55
7.98
4.03
2.71
2.04
11.37
4.78
3.03
2.21
20.33
5.88
3.43
2.42
96.45
7.61
3.96
2.67
2.02
10.76
4.67
2.98
2.19
18.48
5.71
3.37
2.39
65.38
7.33
3.88
2.64
2.00
10.21
4.57
2.94
2.16
16.89
5.55
3.31
2.36
49.14
7.13
3.80
2.60
1.98
9.71
4.46
2.89
2.14
15.52
5.39
3.26
2.33
38.97
6.81
3.73
2.57
1.96
9.23
4.35
2.85
2.12
14.32
5.24
3.20
2.31
32.28
6.58
3.66
2.53
1.94
8.80
4.25
2.80
2.09
13.27
5.09
3.15
2.28
27.36
6.34
3.58
2.50
1.92
8.39
4.15
2.76
2.07
12.38
4.95
3.09
2.25
23.78
6.13
3.51
2.46
309.41
8.14
4.08
2.7

While inspecting the registers of the ATmega in proteus noticed that address 0xA4, which is TCNT4L increases very fast, like it is clocked internally not by the T4 pin. But TCCR4B on 0xA1 is set to 0x00000110, which is supposed to set it to external clock.
I don’t understand what I am missing here.

If you have a free timer you can use input capture for exact timing.

I do have timer 4 and 5 free, but cant understand how to use them

Read the data sheet about Input Capture.