Generating a "good enough" 10kHz sine wave from a square one - Page 1
Forum section: electronics / beginners

author: robca
author_rank: Frequent Contributor
timestamp: July 06, 2022, 02:05:36 am
content: I'm working on a project to read a sensor using a phase difference approach. The original circuit to validate the sensor was rather complex, with an AD9837 sine generator, variable attenuation, and two fast comparators generating a pulse to detect zero crossing for the excitation signal and the signal thru the sensor. The pulses start and stop a counter, which is then read by a microprocessor to derive the sensor value.I need to use a much simplified circuit to do the same, now that we know the frequency (10kHz), voltages and sensor ranges. The processor I'm using (nRF52840) has no DAC, very limited PWM, and no PDM functionality. And the BLE stack is running on the same core, forcing me to use only HW functionality for my project. I tried many tricks to generate a clean 10kHz sine, but there's still way too much noise especially at zero crossing, which is the most important part of the signal.The sensor doesn't need a clean sine. Just a signal that is "sinusoidal enough", 10kHz in frequency, and very clean around zero crossing. I'm running at 1.8V, so "zero crossing 'is defined by 0.9V, and I need a sine centered on 0.9V, with enough Vpp to properly excite the sensor. I will use the built in comparator to start and stop a hardware timer, and since the sensor will also induce an attenuation, having a reliable value for zero crossing is critical: I need to make sure that the value I choose for the comparator zero is actually the sine zero reference for both the excitation signal and the sensor signal.So, I’m planning to generate a 10kHz square wave (0-1.8V), a triple RC filter and some amplification. The triple RC filter works, but as expected, it reduces the signal to a 400mVpp one, still 0.9V centered (0.7-1.1V), which is too low for the sensor. Here is what I came up with, which works perfectly for what I need, but uses 3 opamps: U2 shifts the filtered 0.7-1.1V sine down and amplifies it to 0.2V to 1.4V or so, then U1 works as a voltage follower for an AC signal, centering it around 0.9V. I need U3, because the RC filter signal would be distorted if sent directly into a capacitor (C5). So U3 acts as a buffer. Brute force, not elegant at all, I know. But it worksI can simplify the circuit, using a divider and amplifier in one and only 2 opamps, but the values of the R4/R5 voltage divider and amplification factor were too critical. I would need to use a trimmer for R4/R5 and R6/R7 to ensure that the resulting sine is perfectly centered and properly amplified without clipping (any resistor tolerance would get amplified). On the contrary, in the circuit above, even if the resistors have high tolerance, the signal is always guaranteed to be amplified enough and centered (within the tolerances of R4/R5, but without extra amplification error)Is there a simpler way to start from a 0-1.8V square wave and get a >1.4Vpp sine, perfectly centered on 0.9V? Maybe using active filters, instead of passive one?I'm really a beginner when it comes to analog stuff. If you are providing a suggestion like "simply use a third order Sallen-Key lowpass filter", I'd appreciate more info like the cutoff frequency and a pointer to a simple example circuit implementing itI guess I know that a third order Sallen-Key should work, I just keep getting lost trying to figure out if there's a way to do one using only one opamp

author: MikeK
author_rank: Super Contributor
timestamp: July 06, 2022, 02:39:11 am
content: The middle two op-amps of this?

author: gf
author_rank: Super Contributor
timestamp: July 06, 2022, 09:56:02 am
content: For the filter design, you can try theAnalog Filter Wizard.It also helps to select suitable opamps and can includes the GBW product, noise, voltage swing, component tolerances, etc. of the selected opamps in the calculations.EDIT: What accuracy exactly do you actually need for the measured phase? Can you quantify?

author: Zero999
author_rank: Super Contributor
timestamp: July 06, 2022, 10:14:14 am
content: Only one of those op-amps are really needed.

author: jwet
author_rank: Frequent Contributor
timestamp: July 06, 2022, 12:09:15 pm
content: I would use an online or other filter design tool. Most analog companies have some sort of filter tool. TI and Analog Devices have decent ones.You can figure out what is "good enough" for yourself before hand. A square wave can be decomposed into a sum of odd harmonic sine waves with decreasing amplitudes as 1/n. If you start with a square at 10kHz, how much harmonic content can you live with. You can probably play with a Fourier simulator on line or do some math. If you'd like to have a 1% distortion sine then you basically want to push down the harmonics 40 db.Go into a filter wizard/simulator of some kind and design a filter with those specs. The simplest filter will like be a Chebyshev but you can play with this in the designer. They tend to have the fastest rollofff but introduce some artifacts like passband ripple, etc. Any simulator will let you play with all this. You should be able to make a pretty nice sine with two second order low pass stages set just past youu fundamental- maybe 12 Khz. Play with this. It might work best to set you pole at the fundamental.Active filters are more than just simple poles cascaded and amplified back up like your RCRCRC + Amp. They essentially simulate inductance and can make much better (steeper) filters with less stages. They create a polynomial equation with all the terms rather than just a cascade of derivative terms- these other terms shape the response and let you control things more precisely and trade off performance parameters. If you want to dig in more, Maxim had a good app note about all this. It showed and explained all the complex plane, poles and zeros moving around. This stuff is complicated but is worth knowing if you're doing much with filters. The Maxim app note is one of the most approachable tutorials that I've seen. Most engineers just scale cookbook circuits or use "wizards" and get away with because the math gets so hairy.One additional big hammer to consider is to use a switched capacitor filter. Maxim, LTC and National (and others) make canned filters with fifth or higher order responses with few components. The tradeoff is some residual noise and cost which may or may not matter. Look at the Max 7410 as an example, there may be better and cheaper solutions.As a Maxim FAE, I called on a customer that made Antishoplifting tags- the kind you see at the exit of department stores. They drive big coils with audio frequencies and receive reflection for tags. The tags worked by absorption and reflection of the energy. The received a sine wave into a little antenna that had a diode across it. The diode would create a non linear reflection with harmonic content that the system could detect and alarm on. The checker would put the tag in a little tool that could crush the diode or could remove the tag in some cases. They needed a pretty pure sine, it set the range of the gadget basically. They used an square wave and a switched cap filter.One other issue, measuring phase of signals with comparators looking at zero crossings in the time domain is a kind of old solution. I don't know if it applies, but there are some really nice DSP type techniques to do these kind of things in the frequency domain. We can discuss if you're interested.Good problem.

author: WatchfulEye
author_rank: Regular Contributor
timestamp: July 06, 2022, 12:49:48 pm
content: Try the filter calculator here:http://sim.okawa-denshi.jp/en/Sallen3tool.phpSelect a chebyshev filter, choose your passband ripple (i'd suggest 1dB) and cutoff frequency of 12 khz.It would be worth trying a few different options, and verifying performance using monte carlo simulation in spice to check that component tolerances don't cause excessive drifts or loss of signal.

author: robca
author_rank: Frequent Contributor
timestamp: July 06, 2022, 11:07:27 pm
content: Quote from: WatchfulEye on July 06, 2022, 12:49:48 pmThis is exactly the type of answer I was hoping to get, I can't thank you enoughNot only you pointed me to the tool (which I had already found, but got lost using), but also provided all the necessary info to use it to find a starting point and start figuring out how to actually use the toolIncidentally, using 12kHz and 1dB the circuit saturates (at least in my simulation), while using 10kHz and 1dB it seems to generate an almost perfect, 180 out of phase sine, 0.05V to 1.75V. Will need to build the circuit to make sure it's actually that goodAnd here's what it looks like, for "future me" and anyone else finding this thread

author: robca
author_rank: Frequent Contributor
timestamp: July 06, 2022, 11:10:00 pm
content: Quote from: Zero999 on July 06, 2022, 10:14:14 amI actually did try out that design in my simulator. I used smaller values for the divider, which resulted in a distorted sine. Thanks for taking the time to actually run a simulation on my values. In the end I ruled it out, because the R4/R5 tolerances would get amplified together with the signal, and the zero crossing could move from PCB to PCB.But I truly appreciate the input...

author: robca
author_rank: Frequent Contributor
timestamp: July 06, 2022, 11:23:10 pm
content: Quote from: jwet on July 06, 2022, 12:09:15 pmThanks for the all info (the part I removed with [...] for sake of brevity). Reading and digesting all that...As for why "old style", it's because my processor choice is defined by the existing hardware platform, built around a nRF52840. Great processor for BLE and low power devices, really good digital peripherals support, limited analog ones. The A/D is only sampling at 200ksps, single channel, 12 bits. So I ruled out any DSP technique, because I didn't think I would get a good enough resolution of the sensor signal. Working in the digital domain was my first instinct, until I realized the SAADC limitations. And using an external ADC would defeat the goal of minimizing components.Also, I know I can easily implement the phase difference technique with better resolution than the original circuit (which used a 2MHz clock for the counter, I can easily use 16MHz), purely in hardware on the nRF52840, without impact on the rest of the code performance (the nRF52840 is always multitasking to keep the BLE communication stack active)But if you think that a single 220Ksps, 12 bit SAADC is good enough for the DSP technique (and with enough resolution to be better than a 2MHz phase difference counter), I'm definitely interested to learn more

author: jwet
author_rank: Frequent Contributor
timestamp: July 07, 2022, 01:25:17 am
content: Can you do an 256 Bin FFT do you think? I'm not sure if you'll need to do that but if you have enough juice to do an FFT, you could can probably do it in DSP domain. You would only need to sample the signal at 30 or Khz, 40k would be comfortable. I dig up a reference for you to look at. Since you have the excitation source and the reciever in the same box, it might be simpler- you just build a phase sensitive detector. Give me a day or two and I'll get back to you. Let me know how many mips you can throw at it. Maybe the metric is 16 bit multiply and add instruction rate.

author: robca
author_rank: Frequent Contributor
timestamp: July 07, 2022, 02:06:14 am
content: Quote from: jwet on July 07, 2022, 01:25:17 amFirst of all, thanks! Really appreciate the help and opportunity to learn something new. I'm still trying to wrap my head about how to detect a very small phase difference of a 10kHz signal when sampling only at 40kHz, but it's a field I'm profoundly ignorant aboutLooking forward to hear more (no hurry, at this point I'm really curious, though)Well, the nRF52840 has an M4 core, with the full ARM DSP libraries available. According to thishttps://devzone.nordicsemi.com/f/nordic-q-a/58077/fpu-fft-demo-on-nrf52840it should be able to do a 256 bin FFT using floating point math in ~670us. Given I'm reading a temperature and sending it over BLE, one reading per second is more than enough, so there should be processing cycles to spare. I haven't tried it yet, but I'm positive I'm not CPU-bound for thisProcessing power is significant: it's a M4 ARM core running at 64MHz. What is challenging on the nRF52840, is doing "hard real time". In many cases, the response to an interrupt can be delayed up to ~300us while the BLE stack is running. But the nRF52840 has a hardware event router, so its easy to start and stop a timer using a comparator completely without software intervention, then read the timer value when needed. All while the BLE stack runs untouched. It's truly a nifty processor, low cost, low power and very easy to program. Not the best analog processor, though

author: jwet
author_rank: Frequent Contributor
timestamp: July 07, 2022, 04:21:28 am
content: The M4 is a beast- no problem. I hope I'm not blowing smoke here- I've done software defined radios at similar frequencies with I/Q processing with the Teensy 3- an M4F.What is the sensor that you're interfacing to if you can say?Look at the Analog Device AD5933, this is the kind of signal path I'm envisioning. You wouldn't use this part, you'd do it in software but the datasheet for this part will give you some idea of what I'm proposing. Its basically a vector voltmeter done in DSP domain.

author: robca
author_rank: Frequent Contributor
timestamp: July 07, 2022, 05:14:31 am
content: Quote from: jwet on July 07, 2022, 04:21:28 amUnfortunately the sensor is a prototype and part of a grant in cooperation with a major US university, and I'm not at liberty to say more, sorryI'll look at the AD5933 datasheet and see what I can learn. Understood that you are suggesting something similar in SW, not using that part, but I'll see what I can learn from the datasheet

author: Kleinstein
author_rank: Super Contributor
timestamp: July 07, 2022, 06:29:48 am
content: The 200 ksps ADC is not that bad and one can get quite good resolution for the phase: alone 2 points around zero crossing can interpolate the 5 µs time steps to better than 5 ns. So the resolution would definitely be better than with a comparator (even with a 100 MHz clock). Usually the ADC is also better in noise supression and especially in handling residual harmonic content. The limiting factor is more likely the processing power and not the ADC. With a known and syncronous signal the phase calculation should be relatively straight forward and could be relatively efficient (e.g. averaging over multiple periods first and than do the fourier transform or sine / cosine decomposition.

author: gf
author_rank: Super Contributor
timestamp: July 07, 2022, 09:12:44 am
content: I think it's just a single ADC, and with two multiplexed input channels (measured signal + reference signal) it is only 100kSa/s per channel (please correct me if this is wrong).100kSa/s not particularly high, so potential aliasing needs to be considered. For this use case we only care about those frequencies beyond Nyquist which fold down to 10Khz (i.e. to the fundamental), and these are the 9th, 11th, 19th, 21th, etc. harmonics.In order to keep the angle error below (say) 2*pi/1000 their contribution should stay below -60dBc. With the above mentioned 3rd order Chebycheff filter, and given the fact that the amplitudes of square wave harmonics decrease proportinal to 1/f, this seems to be granted, though. (This consideration presumes of course that the DUT is linear. If it is not, then we have a different situation, since the measured signal can then contain harmonics which are not present in the stimulus. Is it linear?)Since we are only interested in amplitude/phase at the fundamental frequency (10kHz), the DFT does not need to be calculated for all frequency bins, but only for the 10kHz bin.Computational cost is low, it's just two mutiply and two add per sample, and per captured channel. This should not be a big challenge for a Cortex M4, even if it were done in floating point.Ideally, the spectrum of a (filtered) square wave should only contain fundamental+harmonics, and should be empty in the gaps in between.If this is granted, then a rectangular DFT window covering an integral multiple of 10kHz periods is fine (and even optimal, since it has the lowest ENBW, compared to other window functions).If there happen to be unexpected spurs in the spectrum (whereever they may come from, e.g. PSU ripple), then a different window function can be useful. But this needs to be analyzed individually then.

author: Zero999
author_rank: Super Contributor
timestamp: July 07, 2022, 09:24:47 am
content: Quote from: robca on July 06, 2022, 11:10:00 pmSet R5 to 1M and connect R6 to 0V, via a capacitor. Now the op-amp has a DC gain of 1, with its output biased at half the supply. The AC gain is still 1+R7/R6, because the capacitor lets through AC.

author: robca
author_rank: Frequent Contributor
timestamp: July 07, 2022, 04:59:42 pm
content: Quote from: gf on July 07, 2022, 09:12:44 amCorrect, single ADC, multiplexing up to 8 inputs. In my case, it would indeed allow only a 100kSa/s per channel, which is on the low sideThe data on the sensor is very limited, being an ongoing research. As far as I can tell, it should behave linearly in the temperature interval I'm considering.I'm readinghttp://www.metrology.pg.gda.pl/full/2005/M&MS_2005_427.pdfto get a sense of the techniques available. I'll need to play with octave to wrap my head around how this might work. Any pointer to code examples is always appreciatedThanks for the extra info

author: fourfathom
author_rank: Super Contributor
timestamp: July 07, 2022, 06:11:05 pm
content: While an active filter design will give you much better performance, if you want to use the cascaded passive filter you can do better by scaling the R/C values to reduce inter-stage loading. Here's a comparison of your circuit with one having x10 impedance scaling. Note the faster initial rolloff and resulting improvement in high-frequency attenuation. R/V values are for example only, you will probably want to use standard values.

author: gf
author_rank: Super Contributor
timestamp: July 07, 2022, 07:35:13 pm
content: Quote from: robca on July 07, 2022, 04:59:42 pmFor processing the captured ADC samples, something like this is coming into my mind:https://godbolt.org/z/W6YcbWqrhPresumes that ref_samples[] and meas_samples[] have been captured simultaneously with the ADC.(sorry, untested, so there may still be bugs)

author: robca
author_rank: Frequent Contributor
timestamp: July 07, 2022, 11:59:17 pm
content: Quote from: gf on July 07, 2022, 07:35:13 pmThe EEVblog community is awesome. Someone takes the time to write you code to solve your problem, and apologize for not having fully debugged and unit tested the snippetyou rock, thanks!Having said that, though, I do think that there's either a problem or I'm misunderstanding how the code works. I modified your code as followsCode:#include <cstdint>#include <complex>#include <iostream>#define PI2 6.2832#define M_PI 3.14159265358979323846 /* pi */// value of the L1/R1 voltage divider resistor// fill-in the actual valueconstexpr float R1 = 10000;// samples per 10kHz period (at 100kSa/s)constexpr int SPP = 10;// captured ADC samples, must be integral multiple of SPPconstexpr int NSAMPLES = SPP*100;// pre-calculated sin/cos tables// cos_tab = cos(2*pi*i/SPP)// sin_tab = -sin(2*pi*i/SPP)static float cos_tab = {1,0.809016994,0.309016994,-0.309016994,-0.809016994,-1,-0.809016994,-0.309016994,0.309016994,0.809016994 };static float sin_tab = {0,-0.587785252,-0.951056516,-0.951056516,-0.587785252,-1.2246468e-16,0.587785252,0.951056516,0.951056516,0.587785252};std::complex<float> calculate_DUT_impedance(){int16_t ref_samples; // reference samples captured with ADCint16_t meas_samples; // measurement samples captured with ADC// calculate single-frequency DFTfloat ref_re = 0, ref_im = 0;float meas_re = 0, meas_im = 0;for (int i=0; i<NSAMPLES; i++) // fill ref_samples and meas_samples with 10kHz sine, phase shifted{ref_samples = sin(i * 2 * M_PI / 10.0) * 2048 + 2048;meas_samples = sin((i - 0.07) * 2 * M_PI / 10.0) * 2048 + 2048;}for (int i=0; i<NSAMPLES; i++){ref_re += ref_samples + cos_tab[i % SPP];ref_im += ref_samples + sin_tab[i % SPP];meas_re += meas_samples + cos_tab[i % SPP];meas_im += meas_samples + sin_tab[i % SPP];}// complex voltages in frequency domainstd::complex<float> ref_voltage(ref_re, ref_im);std::complex<float> meas_voltage(meas_re, meas_im);// Phase-shift one of the two voltages by 2*pi/20 to compensate that// the muxed channels are not sampled at the same time, but interleaved.// ref_voltage *= exp(std::complex<float>(0, -2*M_PI/(2*SPP)));// meas_voltage *= exp(std::complex<float>(0, -2*M_PI/(2*SPP)));// return impedance of DUTstd::cout << "Real part: " << real((ref_voltage - meas_voltage) / meas_voltage * R1) << std::endl;std::cout << "Imaginary part: " << imag((ref_voltage - meas_voltage) / meas_voltage * R1) << std::endl;return (ref_voltage - meas_voltage) / meas_voltage * R1;}I'm basically filling the reference and measure arrays with 0-4096 values using a sine equation to simulate a 12bit ADC like the one I have. I manually shift the measured array values by a small amount. using 0.01 as a shift, the returned values are real -0.004884, imaginary 0. With values below 0.39, I get the same. When I use 0.4, the returned values are real=-0.00518925 and imaginary=0.00030525, The next bump is for a phase delay of 1. Which means 3 useful values for a 1ms period shiftIs there something wrong in the code, or is the resolution really that low? I'm reasonably sure that the sine curves I'm generating are correct (they plot like a reasonable 10kHz sine, with some phase offset)

author: gf
author_rank: Super Contributor
timestamp: July 08, 2022, 05:54:10 am
content: Silly typoWrote "+" instead of "*". Correction is below.There is of course the 12-bit quantization error. Still the error seems to be well below your target of 100ns.In the presence of a small amount of random noise (with an RMS amplitude of say one LSB) I'd even expect an improvement, since it will whiten the quantization error, spreading it over the spectrum.EDIT: When I add Gaussian noise with stdev=1LSB to the samples before quantization, I see an uncertainty of roughly +/-1ns for the time delay estimated from the samples (I checked it for a couple of values, but not extensively).EDIT: If your aim is to measure the inductance of L1 (referring to your schematic in the other thread), then you don't need the phase angle directly, but you can rather solve the voltage dividermeas_voltage = ref_voltage * R1 / (Z_L1 + R1)for Z_L1. Then L1 = imag(Z_L1) / (2*pi*10000). Additionally the inductor may have a resistive component which is real(Z_L1).Code:#include <cstdint>#include <complex>#include <iostream>#define PI2 6.2832#define M_PI 3.14159265358979323846 /* pi */// value of the L1/R1 voltage divider resistor// fill-in the actual valueconstexpr float R1 = 10000;// samples per 10kHz period (at 100kSa/s)constexpr int SPP = 10;// captured ADC samples, must be integral multiple of SPPconstexpr int NSAMPLES = SPP*100;// pre-calculated sin/cos tables// cos_tab = cos(2*pi*i/SPP)// sin_tab = -sin(2*pi*i/SPP)static float cos_tab = {1,0.809016994,0.309016994,-0.309016994,-0.809016994,-1,-0.809016994,-0.309016994,0.309016994,0.809016994 };static float sin_tab = {0,-0.587785252,-0.951056516,-0.951056516,-0.587785252,-1.2246468e-16,0.587785252,0.951056516,0.951056516,0.587785252};std::complex<float> calculate_DUT_impedance(){int16_t ref_samples; // reference samples captured with ADCint16_t meas_samples; // measurement samples captured with ADCfor (int i=0; i<NSAMPLES; i++) // fill ref_samples and meas_samples with 10kHz sine, phase shifted{// round to nearest integerref_samples = floor(sin(i * 2 * M_PI / 10.0) * 2048 + 2048 + 0.5);meas_samples = floor(sin((i - 0.03) * 2 * M_PI / 10.0) * 2048 + 2048 + 0.5);}// calculate single-frequency DFTfloat ref_re = 0, ref_im = 0;float meas_re = 0, meas_im = 0;for (int i=0; i<NSAMPLES; i++){ref_re += ref_samples * cos_tab[i % SPP];ref_im += ref_samples * sin_tab[i % SPP];meas_re += meas_samples * cos_tab[i % SPP];meas_im += meas_samples * sin_tab[i % SPP];}// complex voltages in frequency domainstd::complex<float> ref_voltage(ref_re, ref_im);std::complex<float> meas_voltage(meas_re, meas_im);std::cout << "ref phase: " << arg(ref_voltage) << std::endl;std::cout << "meas phase: " << arg(meas_voltage) << std::endl;std::cout << "dt: " << (arg(meas_voltage) - arg(ref_voltage)) / 2 / M_PI / 10000 << std::endl;// Phase-shift one of the two voltages by 2*pi/20 to compensate that// the muxed channels are not sampled at the same time, but interleaved.// ref_voltage *= exp(std::complex<float>(0, -2*M_PI/(2*SPP)));// meas_voltage *= exp(std::complex<float>(0, -2*M_PI/(2*SPP)));// return impedance of DUT// solve voltage divider meas_voltage=ref_voltage*R1/(Z_L1+R1) for Z_L1return (ref_voltage - meas_voltage) / meas_voltage * R1;}int main(){calculate_DUT_impedance();}

author: robca
author_rank: Frequent Contributor
timestamp: July 08, 2022, 05:13:45 pm
content: Quote from: gf on July 08, 2022, 05:54:10 amworks beautifully, thanks! very elegant.

author: jwet
author_rank: Frequent Contributor
timestamp: July 09, 2022, 02:10:34 am
content: I knew there were greater minds here that could give you details. I found an old academic paper that compares a bunch of techniques from the brute force time delay techniques to several interesting dsp techniques. Attached.This board is really is an amazing resource- I contribute what I can but inevitably learn way more from others.Good luck on your project.

author: gf
author_rank: Super Contributor
timestamp: July 09, 2022, 10:06:15 am
content: Two more points coming into my mind:How clean is the power supply rail? A square wave from a digital output likely carries the power rail ripple/noise during the high cycle. PSRR of the opamp also drops significantly with increasing frequency. How immune is the ADC against power supply ripple? Do you have a separate analog supply rail? As first step, I'd at least measure the spectrum of the ripple/noise on the power supply rail, preferably at the target sample rate of 100kSa/s (in order to directly see the contribution of the folded-down aliases from higher frequency components of the ripple/noise). A rather large number of samples should be captured in order that wide band (random) noise and dedicated spurs can be clearly distinguished. A problem with measuring the power rail noise is of course that it may depend significantly on what the processor is currently doing.Is the ADC input buffered, or does it directly connect to the sampling capacitor (via mux and S&H switch)? The latter kind of ADCs usually need to be driven by a low impedance source, so external buffering may be rquired. If the stimulus is fed through an active filter, then the source if buffered anyway, but the signal at the center tap of the L/R divider might need to be buffered as well. At least the potential error needs to be anlyzed and estimated when buffering is renounced.

author: robca
author_rank: Frequent Contributor
timestamp: July 09, 2022, 06:08:20 pm
content: Quote from: gf on July 09, 2022, 10:06:15 amThanks for the additional thoughts. Measuring noise on the nRF52840 is, as you point out, difficult due to the presence of a radio that could be using orders of magnitude more power depending on the device connected and activity. I will have to take measurements and see what is the impact of noise.Right now, the team is discussing adding one more analog sensor, which would require the use of another one or two ADC channels, making it next to impossible to use the technique we were just discussing. For now I'm proceeding in parallel with the comparator/timer approach and the DSP one. But until things are more defined on the PCB, it's hard to make any realistic measurement. The good news is that we have an extra power rail from the PMIC, so might be able to get that one clean enough.

author: mawyatt
author_rank: Super Contributor
timestamp: July 09, 2022, 10:34:39 pm
content: Just remembered (memory is fading) an old EDN article that might be useful. The output could be selected as a triangular wave and the filter with a lower corner, which may produce acceptable sine waves rather the triangular. Circuit was shown on front cover for 92 and 93 on EDN Design Ideas.Best,

author: gf
author_rank: Super Contributor
timestamp: July 10, 2022, 10:00:38 am
content: Quote from: robca on July 09, 2022, 06:08:20 pmAnother option could be to use a Stereo Audio Codec IC for the analog output and input.Most of them are intended for 16-bit or 24-bit audio, and have have a high dynamic range and low distortion.

author: Kleinstein
author_rank: Super Contributor
timestamp: July 10, 2022, 11:54:46 am
content: One could still measure the sensors one at a time, measuring the phase relative to the PWM. The phase of the excitation relative to the PWM would than be 1 extra measurement if common excitation is used.The measurments would not be at the same time, but this could still be good enough.An external ADC would be an option. Besides ADCs for audio, there are also ADCs made for power metering that may be interesting (e.g. MCP39xx series)). Some metering chips can also directly calculate the phase - so they have ADCs and the math inside.Feeding an audio codes could be demanding for the relatively small µC.

author: robca
author_rank: Frequent Contributor
timestamp: July 10, 2022, 05:02:35 pm
content: Quote from: gf on July 10, 2022, 10:00:38 amYep, but we are trying to avoid using additional ICs. Especially ones that would lock us into a device that might have availability issues. While even using a specific opamp is potentially risky, a programmable chip is the worst: if that chip becomes hard to get, any replacement would require a different firmware.The goal is to use only the nRF52840, passives and opamps. All things that are (relatively) easy to second source

author: robca
author_rank: Frequent Contributor
timestamp: July 13, 2022, 04:45:43 pm
content: To contribute back to the community, I posted sample code to generate a square wave on the nRF52840 using timers and PPI, and use the comparator to get the phase delayhttps://github.com/robcazzaro/nRF52_sample_code. Clearly useful only for nRF52 family chips, but I could not find an existing example on how to tie everything together. Using PPI, the entire process happen in hardware, and the updated delay value is always available in the timer CC5 register

author: jwet
author_rank: Frequent Contributor
timestamp: July 15, 2022, 02:29:48 am
content: And in the end, you ended up where you started. I hope it was an entertaining detour at least. The best design is the one that does what you need. What you came up with is pretty clean. As far as parts availability- it will be a cold dark day after the Zombie apocalypse. Use any low voltage dual op-amp, there are thousands and they all share a pin out. I generically put an LM358 in SO-8 for my layout but the actual part can be anything. Well done- enjoyed it from my end.

author: robca
author_rank: Frequent Contributor
timestamp: July 15, 2022, 03:33:48 pm
content: Quote from: jwet on July 15, 2022, 02:29:48 amOh, no, actually I ended up in a much better place than I started, thanks to the help I got for all of yousorry if I gave the wrong impressionI now have 2 working versions, one using the ADC and one using the comparator. I just posted the code sample for the timer/comparator one because there was no example and I used techniques that don't have many examples in the Nordic SDK. I also think it's a relatively elegant use of the nRF52 functions done purely in hardware, and hopefully it will help other people. I didn't post the ADC/DSP solution which incidentally is more portable to other processors, because capturing a signal with an ADC is a trivial piece of coding and the necessary DSP code was already posted here and working well.I learned a ton, and have 2 working solutions from which to choose, depending on how the sensor will be used in future boards/devices. I couldn't be happier