# Finding zero cross of AC signal digitally

I'm measuring 50/60 Hz with STM32 microcontroller. The signal comes from current transformer. I'm adding a DC shift to the signal equal to 1.65V. As the signal is riding in 1.65V, this will be my zero cross. I would like to detect the zero cross in software so what comes to my mind is to sample quick enough, say 8 kHz, to have more chance of finding the zero cross. So my plan is: take ADC sample -> Substract 2049 (1.65V) -> compare to 0 (I'm using integer math). Would this be a reliable way of doing it? Would I need some extra hardware? I've seen other solutions with some kind of hardware which I don't have at the moment. I only have the signal properly amplified riding on 1.65V.

• What about noise at zero-cross? Do you need to debounce? Is there any chance of harmonics causing a second zero-cross per half-cycle? Nov 3 '20 at 0:09
• If high accuracy is your goal then I would recommend you read Bogdan Kasztenny’s paper here. He talks about the problems with zero crossing detection, and proposes a correlation method that is much superior. Nov 3 '20 at 0:44
• After exactly 100 ms you will capture exactly 5 full cycles at 50 Hz and exactly 6 full cycles at 60 Hz. So you could sample for an time interval of 100ms (or multiple) and then maybe you don't need to do zero crossing or frequency detection. Just a thought. Nov 3 '20 at 5:44
• Another option is to store the min and max samples over an interval of 25ms or so, and use the difference between min and max to calculate the peak-to-peak voltage amplitude. This is kind of cheating, and won't give a good result when the input waveform is not sinusoidal. Nov 3 '20 at 5:47
• How clean is the signal? If it has no harmonics you would take the $\min, \max$ over a (sampled) cycle and then compute $V_{RMS} = {1 \over 2 \sqrt{2}} (V_{MAX}-V_{MIN})$. Nov 3 '20 at 18:19

According to your comments, you don't want to find the zero crossing per se. You want to find the RMS of the AC, and quickly, within a cycle or so (put these more specific requirements into your question). No need for extreme accuracy? The method you choose depends heavily on what accuracy you require, you can get away with all sorts of things at 10% that you could not at 0.1%.

It's neither necessary nor sufficient to have the zero crossing available. What is required is to sample exactly one cycle of the mains waveform, if you want to do it in one cycle, or at least 5 cycles without being synchronous, if you can tolerate the latency and want to target 0.1%.

The errors for sampling slightly more or less than one cycle are small, and if you don't want to sync up your sampling rate to exactly an integer multiple of the mains frequency by PLL (as the mains frequency can vary a little), then capturing just over 1.5 cycles allows you to select an entire cycle between approximately zero crossings.

Once you have a whole cycle, then the DC value is just the average of those samples.

It's well worth cranking up an excel spreadsheet to experiment to see what sort of errors you get with a sampling rate deviating from exactly an integer multiple of mains, and start and finish deviating from zero crossings.

Note that if you measure the RMS, and the measurement is in error because of the DC term, then that error is due to the RMS sum of those. For instance, the following 8x sampling 100,71,0,-71,-100,-71,0,71 gives an rms of 70.86, whereas 110,81,10,-61,-90,-61,10,81 (a DC offset of 10% of the peak) gives an rms of 71.47, an error of 0.8%. That took me 60 seconds to do on a spreadsheet. Now do the same for yourself, but varying the accuracy of the integer oversampling, and starting/stopping at near or far from zero, and compare the accuracy with what you want.

You may want to think about what sort of rate you capture at. You suggest 8kHz, which is a lot of processing, and not an exact multiple of 50 or 60 Hz. A multiple of 300 Hz gives you an exact multiple of both. How many samples do you want? It depends on the harmonics, and the accuracy you want in their presence. 1200 Hz is adequate for most commercial metering applications. Fewer samples mean you have more time too do work with them.

I can suggest a specific algorithm. Set your sampling rate to 1200 Hz. When you first switch on, do an initial calibration to find out whether you're in 50 Hz or 60 Hz land. Then record exactly 24 or 20 samples respectively, which will occupy exactly one cycle. Average them to get the DC value, and subtract it. Then square, sum, and divide by 24 or 20 (which means multiply by the reciprocal of 24 or 20, you don't need to divide by a constant). You may if you want assume that the DC level is either going to remain consistent, or if it does vary, shift very slowly. You can then, for minimum latency, switch to capturing half cycles of 12 or 10 samples, or even quarter cycles (though for 60 Hz you'd have to increase the sampling rate to 2400, with the corresponding other adjustments, to get an integer number of samples in a quarter cycle), and use the assumed value of DC. If needed, keep track of the current DC level with a very slow filter, updated every time you take a half or quarter cycle.

Of course, if your hardware can set the sampling rate, then there's no need to pick something fixed like 1200 to handle 50/60. Once you've discovered which it is, you can set the sampling rate to 16x say, and always take and scale by 16 samples, which of course just means a shift of 4 places. Note that while recording a whole cycle will reject all harmonics, taking a half cycle will now not reject even harmonics, for instance a half-wave rectified load, which are fairly unlikely to occur. A half cycle will still handle the flat-topped distortion due to a (very common) rectifier+capacitor load.

• I can assume my input frequency to be 50 or 60 Hz depending on the region, and adjust the sampling rate to match an integer multiple of the mains frequency. As for the frequency variations, do you mean that if I capture 1.5 cycles I need to use zero cross detection anyways to extract an entire cycle? Nov 3 '20 at 4:19
• Zero cross would be one way, but if you have approximately a whole cycle, then the errors from asynchronous sampling are smallest when you truncate when the signal is smallest, so nearest the mid point, you don't need, and can't get, an exact zero crossing unless you interpolate, and it doesn't sound like you want to do that. If you have exactly synchrponous sampling, then you only need one cycle, it doesn't matter that it starts at any particular phase. I worked for a metering company, and they got their accuracy with 16x mains sampling, nominal rather than PLL multiplied. Nov 3 '20 at 5:03
• The minimum exact multiple of 50/60 Hz is 100 ms, which is 5/6 cycles respectively. But I cannot wait 5 or 6 cycles. I want to find a estimation in one cycle. So I will need to live with the inaccuracy of not having an exact multiple of 50/60Hz. Nov 6 '20 at 19:55
• @Blue_Electronx That 100mS is a mulitple of the periods of 50/and 60 Hz is one of those 'true, but irrelevant' things, nobody is suggesting you sample for that length of time. I've added your ideal algortihm to my answer. Nov 7 '20 at 7:55
• Thanks for the suggestion. My only concern would be that my current transformer only detects earth leakage currents (Zero Sequence Transformer), so I need to take an action when the current comes in. I don't think I will have time to initially calibrate to find out if it is 50 or 60 Hz. So it is a trade-off, that's why I was thinking of using a fixed sampling rate (perhaps using samples for 55 Hz and split the error between 50 and 60 Hz) and just accept some inaccuracy in RMS, maybe 5% or 10%. Nov 8 '20 at 14:49

I would look to do the whole thing computationally.

You can compute the mean value of the signal by sampling at your chosen frequency and using a ring buffer to accumulate and continuously calculate the mean value. This will work irrespective of the line frequency being 50/60Hz provided you sample sufficiently often. You can play around with sampling frequency vs RMS calculations in Excel to tune this.

In the same ring buffer you can calculate the deviation of your current sample value from your calculated mean, square this value and again calculate a running mean of the squared value and average it into another accumulator.

The final piece is to trigger the points at which you want to read out the RMS (square root mean squared deviation from the mean value). If the signal is sinusoidal you can assume symmetry and do this every half cycle. This can be detected as the maximum value of the sample after crossing the mean. If you want to be completely conservative and not assume a particular waveform you can measure the true RMS value after crossing the mean for the second time.

This approach has the added advantage of following any DC drift in the signal under test.

Reading a signal by hardware or software you'll have to set a threshold to validate if a signal has passed the zero-cross. You don't need to add a DC voltage to your signal and subtract it back to just read a zero-cross, you can change ADC's reference to set where your "zero" will be and compare it on software. If the value is equal to zero you reset a flag and when the ADC value greater than zero and the flag is reset then there was a zero-cross event.

• The ADC on an STM32, as with pretty much any microcontroller ADC, must not be given a voltage below GND if you want to keep the magic smoke in. A DC offset is necessary to measure an AC signal which dips below GND. Nov 3 '20 at 12:20
• You're right, it would need to cut the alternating signal first that would require extra hardware, a diode in series with a resistor comes into mind but there still would be a -0.7V left in the signal so it wouldn't work, maybe there are other ways but probably offsetting the signal still easier, so disregard the answer I gave @Blue and thank you Sneftel to point this out. Nov 4 '20 at 2:24

the STM32 has an Analog watchdog, which sets a flag when an ADC channel is above or below 2 predefined tresholds. in the worst case of polling zero cross, you can just read this status bit, clear it, wait a little (based on your sampling time) and read it again to see if it got set. the good thing is, the tresholds make a good margin for neglecting noise. (e.g. HTR of 1.7v and LTR and 1.6v) 