By zero, I mean actually zero, so not some offset voltage as a result of using an opto isolator or comparator with hysteresis.
My original plan was to use a timer interrupt and the ADC to sample at a high fixed rate, but this post
says that analogRead can no longer be used from within an interrupt.
(The signal will be from the low side of a 9v 50Hz isolation transformer.)
It looks like the I2S peripheral can control the ADC directly (for fast sampling), and put data directly into memory uisng DMA, but am I then allowed to access that data using a timer interrupt, or does it have to be via a task?
Do you mean 0.000 or 0.000000V or 0.000000000V
What is this signal and application where you need such precision?
Even the ESP ADC has an offset and gain error
Espressif does not publish any noise performance specifications of the ESP32 ADC but considering the poor performance of the ADCs in general, I would expect noise performance to also be poor.
Timing when to trigger a switch or read a pin.
That will depend on the signal.
There is no general purpose circuit design that will work with any signal.
Also, depending on the signal, the ADC may not be fast enough to detect a zero crossing within a given time period.
What you are asking for may not be possible with a simple circuit design.
Rather than talking about generalities, if you give specifics about what you want to do, maybe someone here or myself can provide you with a more concrete answer.
NOTE: Applying a zero crossing signal directly to an ESP pin will damage it.
The best option would be a comparator or op-amp with both positive and negative rails. Since you need the output to be exactly zero—not +-0.0000000001 or some other negligible value—this setup ensures accuracy. Using an interrupt to detect zero crossing can introduce latency in your code, allowing the voltage to swing past the 0V point.
A comparator sounds like the best idea. The idea would be to detect "negative or not-negative". (You'll probably need positive & negative power supplies fort the comparator.)
Note that there is no exact-zero in the real-analog world... There is no exact anything!
When I made a dimmer a million years ago I did that on purpose because it was easier and noise on the AC line is less likely to cause a trigger at the wrong time (near the zero crossing). Since you know the frequency you can calculate when the next zero-crossing is coming. But, I actually figured it out experimentally.
I also used a transformer (because it was part of the project anyway). And since the transformer might cause a phase-shift, that's another reason to dial-it-in experimentally.
Replying to myself, I did the experiment with interesting results. With a timer interrupt set to 100 microseconds I has able to use xTaskNotifyFromISR to wakeup a high priority task and take an analogRead measurement every 134 microseconds (7.4kHz). However this monopolized the core preventing all other tasks from running.
With no hysteresis it's going to oscillate at the crossover point, therefore as I see it the challenge is to detect when the osillation starts, when it ends and then use the calculated half way point as the true crossover point. Detecting the end point requires some arbitary rule such as "no transitions within the last x microseconds".
Once you have that middle point you can add 10 milliseconds (50Hz mains) to predict the next transition. If you repeatedly get the prediction right then you know that you have a working system.
No just another task, running in loop, reading adc at max possible rate (or rate you want), doing yield() sometimes. You even can do it on another core (Core#0) so this task will not affect main Arduino CPU. Rate could be controlled by doing delay() or microsecond delay functions
Those don't do anything if the task is running at a priority above the other tasks.
If you run it at the same priority as the other tasks then the time between readings increases to 1500 microseconds, so at least 10x slower than using a timer interrupt. At this slow sample rate It will take a long time to discover the cross over points.
I think you are missing something, you don't need to detect the crossover point exactly, if you are sampling the waveform multiple times and you know the waveform is repetitive with little variation then you don't need to hit the crossover point bang on because you can predict where and when it will occur from the samples you have.