Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Logic or sample code to detect the zero crossing Also how detect the positive or negative cycles

thannara123

Advanced Member level 5
Joined
Jan 7, 2010
Messages
1,580
Helped
122
Reputation
244
Reaction score
114
Trophy points
1,353
Location
India
Activity points
10,382
Hi,
Firstly saying this question is the part of desgin of a sinewave inverter by an STM32 controller

A Feed back volt is given to the ADC of the controller after , Scaling and necessory process as said in a previous thread .


Thorugh the internet , most of detecting by " in between the adc values at the zero level of the signal "
like wise
if (adc_value >=1400 && adc_value <=1600){ Zero crossing detected }


Here am reading 400 samples (each conversion takes 25 us) for 10 millisecond one half cycle which is saving to an array[400] by DMA

Hello experts
Here i am requesting the logic or sample code to detect the zero crossing Also how detect the positive or negative cycles

( I am doing this project for learning purpose , Error and accuracy may change it will correct after understanding the basics.And after making a working proto type so this project hasnt any accuracy ,error etc deal binding )
 
You can try a code like this:

Code:
#include <stdio.h>

#define SAMPLE_SIZE 400
#define ZERO_CROSS_THRESHOLD 50 // Adjust this threshold according to your signal

// Array to store ADC values obtained via DMA
uint16_t adc_samples[SAMPLE_SIZE];

// Function to detect zero crossings and determine positive/negative cycles
void detectZeroCrossings() {
    int zeroCrossDetected = 0;
    int i;

    // Check for zero crossing
    for (i = 1; i < SAMPLE_SIZE; i++) {
        if ((adc_samples[i - 1] <= 2048 && adc_samples[i] >= 2048) ||
            (adc_samples[i - 1] >= 2048 && adc_samples[i] <= 2048)) {
            zeroCrossDetected = 1;

            // Determine positive or negative cycle
            if (adc_samples[i - 1] < adc_samples[i]) {
                printf("Positive cycle detected\n");
            } else {
                printf("Negative cycle detected\n");
            }
            break;
        }
    }

    if (!zeroCrossDetected) {
        printf("No zero crossing detected\n");
    }
}

int main() {
    // Simulated ADC values obtained via DMA
    // Replace this section with your actual DMA code to fill adc_samples array

    // Assuming the array adc_samples is filled with ADC values

    // Call function to detect zero crossings and determine cycles
    detectZeroCrossings();

    return 0;
}
 
If you measured the Vref/2 bias , this value would be your zero.
or if you measure the average of 50 cycles with 100 samples per cycles, storing the average and the Max, min and average those separately to compare, that could be your stored average value. Then update it when convenient or keep a rolling average of all samples for Zero value . A hardware low pass filter has the same effect. Detecting a glitch can throw this off unless you use a timing window to mask out looking for zero crossing except within a 10% timing window. This is important when noise occurs near the zero crossing.

It is trivial to detect ZC in hardware, but software takes more memory or calculations unless you can measure Vref/2.

More ideas. https://www.mathworks.com/help/simulink/ug/zero-crossing-detection.html
 
Last edited:
Hello!

if (adc_value >=1400 && adc_value <=1600){ Zero crossing detected }

This will not detect a zero crossing. It will only detect if the value is in a given area.
The simple case: the last sample was less or equal to a value and the current sample is more, then you have
crossed that value.
BUT: if you have noise it might cross a few times. So you have to give it a hysteresis.

Dora.
 
Hi,

I fully agree with Dora.

You could also use analog or digital filters to suppress noise. Neither one is difficult.

Klaus
 
Hello!



This will not detect a zero crossing. It will only detect if the value is in a given area.
The simple case: the last sample was less or equal to a value and the current sample is more, then you have
crossed that value.
BUT: if you have noise it might cross a few times. So you have to give it a hysteresis.

Dora.
Thanks you very much simply understood , Thanks a lot of


Hi,

I fully agree with Dora.

You could also use analog or digital filters to suppress noise. Neither one is difficult.

Klaus
Sir , will remove Analog filter the dc offset ? then it cannot use by an ADC ?
 
Sir , will remove Analog filter the dc offset ? then it cannot use by an ADC ?
Removing the offset makes no sense. As you said you need the DC offset for proper ADC operation.
(There are ADC than can handle pos and neg input voltages)

But here the topic was "reducing noise". For reducing noise you are free to use analog or digital low pass filters.

***
Regarding zero cross detection: If possible I´d use:
signal input --> analog noise filter --> both: ADC as well as analog comparator that triggers an interrupt.
Depends on the microcontroller features.

Klaus
 
You should always have design specs before you start and list ALL your assumptions with inputs and desired outputs, then processes and user interface.
List your goals that you want to learn. Tools, processes, methods of test verification.


e.g. measure and compute Vrms from RMS calculations compared with Vpp and converted by 2 root 2
Must have Vrms
1% accuracy
Single supply 3.3 or 5V ?

Nice to have 0.1% accuracy and Irms , Pavg, VAR, pf. (min,mean,max over some period), RTC timestamp

Then figure out the easiest way to solve it. DIY H/W or off-the-shelf boards.

For example mean DC value vs ZCS trigger. H/W or S/W ?
--- Updated ---

HW using 4000 series CMOS @ 3.3V
1703114141453.png

The RC filter is matched to avoid phase shift with HPF and LPF. Of course using XNOR gives you an IRQ!

The advanced reader will notice the 1st gate was simulated at 3V to demonstrate that the input offset from ideal simulation @ 50% threshold is self adjusting to the mean to generate a 50% duty cycle.
 
Last edited:
To detect zero crossings, make use of the 90 degree capacitive phase advance. Detect peaks of the resulting sine wave. The peaks are zero crossings.

To detect positive portion of cycle, place a diode and capacitor to send a pulse at positive peaks. Add a similar network with diode oriented to detect negative peaks.

Simulation below. Values must be customized to work at 50 Hz (or whatever frequency you wish to operate at).
Click this link to open Falstad's website and run my schematic in his animated interactive simulator.

tinyurl.com/yvl7ltkx

AC source shifted 90 deg via series cap diodes n caps detect zero crossings.png
 
To detect zero crossings, make use of the 90 degree capacitive phase advance. Detect peaks of the resulting sine wave. The peaks are zero crossings.

To detect positive portion of cycle, place a diode and capacitor to send a pulse at positive peaks. Add a similar network with diode oriented to detect negative peaks.

Simulation below. Values must be customized to work at 50 Hz.
Click this link to open Falstad's website and run my schematic in his animated interactive simulator.

tinyurl.com/yvl7ltkx

Sir
Sir
I am trying to detect it in Digitally , (software level )
--- Updated ---

I just plan the logic to code See ,
Any body correct / advice me if i am wrong .
I dont have coding expirence well


ADC Conversion Complete Callback:
This function is triggered when the ADC completes a conversion. It processes the ADC data as follows:
Zero Crossing Detection:
It iterates through the adcBuffer to find values within the range 1450-1500, indicating a zero crossing.
If a zero crossing is found, it tracks its index and checks for consecutive zero crossings.(out of 6 iteration atleast 4 trigreed confirms the zerocrossing )
Consecutive Zero Crossings and RMS Calculation:

If 6 consecutive zero crossings are detected:
It determines whether the signal portion is positive or negative.
It calculates the RMS (Root Mean Square) value of the signal starting from the zero crossing.
The calculated RMS value can then be used for further processing or decision-making.

C++:
// ADC conversion complete callback
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
  // Static variables to maintain state across function calls
  static uint8_t consecutiveZeroCrossings = 0;
  static uint8_t zeroCrossingFlag = 0;

  // Check for zero crossing in the ADC buffer
  for (int i = 1; i < 760; i++)
  {
    if (adcBuffer[i] >= 1450 && adcBuffer[i] <= 1500)
    {
      // Zero crossing detected
      zeroCrossingDetected = 1;
      zeroCrossingDetected_index = i; // Record the index of the detected zero crossing

      // Check for consecutive zero crossings
      consecutiveZeroCrossings++;
      if (consecutiveZeroCrossings >= 6)
      {
        zeroCrossingFlag = 1;

        // Check the signal portion
        int8_t countPositive = 0;
        int8_t countNegative = 0;

        for (int j = i - 5; j < i; j++)
        {
          // Count positive and negative slopes in the signal portion
          if (adcBuffer[j] < adcBuffer[j + 1])
          {
            countPositive++;
          }
          else if (adcBuffer[j] > adcBuffer[j + 1])
          {
            countNegative++;
          }
        }

        if (countPositive >= 4)
        {
          // Signal portion is positive
          signalPartFlag = 1;
        }
        else if (countNegative >= 4)
        {
          // Signal portion is negative
          signalPartFlag = -1;
        }

        // Calculate RMS value starting from the detected zero crossing
        double sumSquaredValues = 0.0;
        int startIndex = zeroCrossingDetected_index % 380;

        for (int k = 0; k < 384; k++)
        {
         
          int index = (startIndex + k) % 380;

          // Add the squared value to the sum
          sumSquaredValues += pow(adcBuffer[index], 2);
        }

        // Calculate the RMS value
        double rmsValue = sqrt(sumSquaredValues / 380.0);

      
      }
    }
    else
    {
      // Reset consecutiveZeroCrossings if a non-zero crossing is detected
      consecutiveZeroCrossings = 0;
    }
  }
}

is it a good way ?
 
Last edited:
Hello!

It iterates through the adcBuffer to find values within the range 1450-1500, indicating a zero crossing.

Sorry to have to repeat, but NO, IT DOES NOT INDICATE A 0 CROSSING!!!
The only thing it indicates is that your signal is within the range 1450 - 1500.

Suppose your 0 is at 1475 and you keep a + / - 25 for noise fluctuations.
Suppose that your signal is slow, going 1500, 1498, 1496, 1494, 1492, 1494, 1496, 1498, 1500. With your method,
you would detect a 0 crossing but there is none.

Again, detection means that you use:
- A variable containing the previous value
- The current value
- Possibly some hysteresis.

Write a state machine. With a state pos or neg, if the current state is pos and the current value is neg,
then you have a zero crossing. Same thing in the other direction. Use a #define HYS something, and
you can also take the HYS into account. Try with a few values to check what value works best.

Dora.

NB: beside this, if your signal variation is steep and goes from 1501 to 1449 or faster, what will you conclude???
-> It will never be in the [1450..1500] interval
-> BUT: it's obviously a 0 crossing.
 
Last edited:
A zero crossing method must know the SNR of any input by knowing the signal BW and possible interference then choose a hysteresis threshold to exceed it or use as a timing window to restrict when looking for a ZC event as hysteresis also adds phase error.

The HW method I posted uses a filtered mean voltage for many cycles and subtracts this from the signal to remove DC.
Then a SW comparator converts to binary to be XNOR'd with a N+1 delayed sample to create the ZC event just like the XOR HW example.

But you can do this by either assuming the ideal Vref/2 digital value using the same for bias with your R/R tolerances or actually measure Vref/2 to eliminate this x% tolerance error.

Tthen average and use that to rectify the AC signal in digital code then compute the RMS and keep that as a rolling average then create the sqrt root result using Newton-Raphson quick method for a rolling result.

Choose your methods, with quasi-macro logic then by breaking down each process into discrete code and annotate your code to document the process. Test each function for validity, then stack all together with print statements in a simulator to validate your progress.
 
Last edited:
Hello!



Sorry to have to repeat, but NO, IT DOES NOT INDICATE A 0 CROSSING!!!
The only thing it indicates is that your signal is within the range 1450 - 1500.

Suppose your 0 is at 1475 and you keep a + / - 25 for noise fluctuations.
Suppose that your signal is slow, going 1500, 1498, 1496, 1494, 1492, 1494, 1496, 1498, 1500. With your method,
you would detect a 0 crossing but there is none.

Again, detection means that you use:
- A variable containing the previous value
- The current value
- Possibly some hysteresis.

Write a state machine. With a state pos or neg, if the current state is pos and the current value is neg,
then you have a zero crossing. Same thing in the other direction. Use a #define HYS something, and
you can also take the HYS into account. Try with a few values to check what value works best.

Dora.

NB: beside this, if your signal variation is steep and goes from 1501 to 1449 or faster, what will you conclude???
-> It will never be in the [1450..1500] interval
-> BUT: it's obviously a 0 crossing.
Thankyou very much sir ,
As I understood correctly I have a code as follows
C++:
// Define states
typedef enum {
    POSITIVE,
    NEGATIVE
} State;

// Define half cycles
typedef enum {
    NONE,
    POSITIVE_HALF_CYCLE,
    NEGATIVE_HALF_CYCLE
} HalfCycle;


void detectZeroCrossingsAndHalfCycle(int *signal, int numSamples, int adcThreshold, int hysteresis) {
    State currentState = POSITIVE;
    HalfCycle currentHalfCycle = NONE;

    for (int i = 0; i < numSamples; ++i) {
        // Check the current state and ADC value with hysteresis
        if (currentState == POSITIVE && signal[i] < (adcThreshold - hysteresis)) {
            // Transition from positive to negative
            currentState = NEGATIVE;
            currentHalfCycle = NEGATIVE_HALF_CYCLE;
            printf("Zero Crossing detected during Negative Half Cycle\n");
        } else if (currentState == NEGATIVE && signal[i] > (adcThreshold + hysteresis)) {
            // Transition from negative to positive
            currentState = POSITIVE;
            currentHalfCycle = POSITIVE_HALF_CYCLE;
            printf("Zero Crossing detected during Positive Half Cycle\n");
        }
    }
}
int adcThreshold = 1500;
    int hysteresis = 10;

Is it correct way ?

@ D.A.(Tony)Stewart

Sir How to impliment the Timing Winodow in the above code

I am hardly struggling to understand the english from forum answers , It makes me hard to get . I take time
Any way i think expireance make better for me
--- Updated ---

Have you selected which STM32? single ADC has 16 ADC inputs.What are all your assumptions for ADC inputs, Vref, Vdd and outputs?
Please list these now.

View attachment 187259
Sir
Sir How to impliment the Timing Winodow in the above code ? I use 380 samples per half cycle ,26 micro second for 1 samples
i want to use the time window , But i didnt get actually it ?
I use stm32f070RBT , ADC input shifted level 1.65volt+ 1 volt swing sinewave ( it is zero level) vref 3.3 ,vdd 3.3 My output is Zero crossing detection ,frequency Phase .
It is for learning purpose so any value is choisable ,I want to learn that is my aim ?
 
Last edited:
Ok then ignore hysteresis for now.

This is not code, it's just idea for you to code.

{Google says... A signed integer is a 32-bit datum that encodes an integer in the range [-2147483648 to 2147483647].
An unsigned integer is a 32-bit datum that encodes a nonnegative integer in the range [0 to 4294967295]. The signed integer is represented in twos complement notation.}
Your choice.

Make constant for "zero" or Vz = 2047? 2048? (4096/2) you decide.
Read Vin sample or stored array ? your choice. 2Vpp / 3.3ref is about 60% full scale
Save Vmax and Vmin for future error analysis is (Vmax+Vmin) = Vz ?? or does it include noise

Rectify signal ( used signed arithmetic or not ?? ) you decide
if Vin<Vz (invert) (change sign or Vabs= 4095-Vz
subtract offset, now values go near 0 to 1/3 of 4095 of 32 bit scale

Now Zero Crossing values are < 100 ? depends on N samples per array size.

If Vabs, i+1 < Vabs, i ( approaching Vz )
If Vabs i+1 > Vabs ( gone past Vz, so this sample is your zero crossing) Is there any noise? Does this cause error with noise? Do you need to filter noise?

Then choose RMS method
to square result , sum square in arrary , not overflow if using 32 bit range < 12 bit values depends on N samples^2, otherwise more precision is needed
If Vin<Vz Vabs=Vin

Samples go in a circular array in memory or fixed?
Can you compute while ADC is fetching result or does DMA prevent that.

Others can help more with code than I
--- Updated ---

The noise on signal and quantization error and hystereis will each cause some errors when squared, so find out how much total noise you have and effect on the result later.

If calibrated on a 2.00 Vpp signal then Vrms -1.414 is your error. Save errors in another array. for plotting. and compare each half cycle for offset errors. Since your samples are not synchronized or "sync'd" to f,

Find the point in array when polarity changes then restart array
- compute space been these zero crossing to get frequency
- use a variable phase timer to start saving your data in array
- and adjust array size to adapt to exactly half or full cycle using a timer , so you might only use part of initial array for the lowest frequency 50 Hz +/10% spec

Now you almost have a software Phase locked loop for your ADC , so you might want to have a status bit = Locked within some tolerance error.

Maybe there is code in some STM32 library for doing all this AC to DC rectified then RMS and PLL loop to sync ADC for ZCS (zero crossing signal) pointer to make better code.

Then consider what statistics you want to collect. Peak RMS +/_ Peak signal S/N ratio. error tolerance over different time intervals, 1 s, 1h , 1day, 1wk
Do the same for frequency error and correlate maybe. in plots.

When doing live recordings, expect glitches from grid tap changes or network brown-outs or network switches. Do you wish to record these events with a timestamp? then you need to use Real-time clock RTC.
--- Updated ---

You have many ADC and logic inputs. Consider another simple method to find ADC sync to ZCS usng a square wave with a 3.3V zener and a string of 100k resistors with withstand 250V each say 4x100k for 1kV

then XOR each sample to find the ZCS or pulse for each ZCS output. If you don't have 3.3V Zener a white LED will drop 2.8V at 260 uA and act as a zener which is good enough for a logic signal in 3.3V STM32. then a small signal diode like 1N4148 to clamp reverse voltages on LED. USe a large series R if you are concerned about undervoltage 0.5V to Input port but ESD internal protection ought to clamp this. (??) to less than 300 mV Abs Max.
1703436118073.png

--- Updated ---

When you get it working , you can compare your statistical results with data collected by Industry standards


good luck and have fun. Imagine this will be a great product first, then do it.
 
Last edited:
Ok then ignore hysteresis for now.

Thanks sir ,Why we dont need Hysteresis ?

Now you almost have a software Phase locked loop for your ADC , so you might want to have a status bit = Locked within some tolerance error.
Sir
How to impliment PLL in this case may get more details ?

Maybe there is code in some STM32 library for doing all this AC to DC rectified then RMS and PLL loop to sync ADC for ZCS (zero crossing signal) pointer to make better code.

Sir , Is there any example ,How to i find it ?


good luck and have fun. Imagine this will be a great product first, then do it.
Thanks .
I am planning to save the data to the memory by using DMA .After detecting zero crossing detection the DMA will restart to save from the base level , but is consume time ?

I dont require the detection through Hardware my main concern is the dection through software ,

Thanks
 

LaTeX Commands Quick-Menu:

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top