Measuring time span very precise - Page 1
Forum section: electronics / projects

author: HendriXML
author_rank: Super Contributor
timestamp: September 06, 2019, 10:46:32 pm
content: From the project below I get two digital signals from laser GateA and GateB I would like to determine the time between them.Nerfo meter - measuring the speed of Nerf dartsThe timing circuit will be reused in other experiments and needs to be readable by an Arduino Mega2560.I came up with the following:Why not use the 74LV8154 to count ticks in 32 bit mode. At the GateA signal the counter is saved to its registers. At the same time the Arduino is triggered/interrupted to read this data. This is allowed to take 100 us. The same will happen when the GateB signal fires.The ticks will be at around 1 Mhz. Thus allowing for 1 hour of high precision timing. But if I where to generate the ticks with a M74HC4060 (oscillator/14 stage binary counter), I could also use a divider output of 16x or 32x etc. This will mean running that IC on about 16 Mhz. Which would mean it could run for 50 days using the greatest divider (off course with a lowered resolution).Is there a way to use an special purpose IC to hook the counter IC to a by a software chosen divider pin? Or is this a case where AND gate's would be needed? (I could also use jumpers, losing software capabilities...)If the Arduino reads the counter registers too slow, the circuit is limited by how small the time span between GateA and GateB might be. To solve this 2 synchronous counters (2x 74LV8154) could be used, clocking and starting them at the same time. But that seems like a uneconomical solution.This seems to me like it could work, but any thoughts or suggestions are appreciated!

author: HendriXML
author_rank: Super Contributor
timestamp: September 06, 2019, 11:52:00 pm
content: I think that doing a counter clear on the GateA signal would be the most easy solution to keep the minimal timespan short. This way the registers will not need to be read during the 2 events.

author: hamster_nz
author_rank: Super Contributor
timestamp: September 07, 2019, 12:12:27 am
content: Simple, accurate, and cheap. Borrow an FPGA dev board

author: DaJMasta
author_rank: Super Contributor
timestamp: September 07, 2019, 12:16:56 am
content: You can probably get good results with a hardware interrupt for the end pulse edge and a software start, but if you want really fast, why not a hardware counter?Whatever timebase you want piped into an AND gate to control when it runs and then into the clock input of a counter like a 74 series 4040. Micro turns on the other and gate input to enable the clock, counter runs, ending trigger pulse either goes into the AND as well, or pulls down the micro's control line, or through another mechanism to stop it. Micro reads the 12 bits of output from the counter, then triggers the reset line, then you wait until you want to time another.If you don't have the micro controlling the start, you can have the first laser trigger a latch (like a D flip flop), then have the second laser trigger a second latch. Have the normal output of the first latch start the AND gate and the inverted output of the second latch enter the AND gate as well to stop it. If you reset between laser trigger pulses, that should give you the 12 bits of counting for each counter. If you run your counter's clock from a 1MHz or 10MHz crystal oscillator, it should be pretty accurate and stable and you'll get a 1us/100ns resolution on your reading.The problem with the 14 bit is that the two least significant bits aren't broken out in the standard pinout. Cascading counters works great, and at 1MHz especially, 24 bits of counting gives you a maximum of over 16s of delay that can be measured at 1us.

author: IDEngineer
author_rank: Super Contributor
timestamp: September 07, 2019, 04:55:24 am
content: How about a small MCU? Many are available with relatively low pin counts and small packages, and you'd have more flexibility than any dedicated IC.I happen to have a PIC18F spec sheet sitting here. At quick glance, it has direct support for cascading eight and 16 bit timers to form a 24 bit counter that can be clocked by anything from the main oscillator (62.5nS resolution) on up to a bunch of intervals that are selectable via prescalers. That's a counting range of 16M, and at 62.5nS resolution that's a run time of one second. You can obviously scale resolution to lengthen the max time period.Still not enough? With a single pin-to-pin connection you could cascade two 16 bit timers. Now you're at 4G counts, which at the highest resolution gives you 268 seconds. I suspect you could cascade two of the 8+16 arrangements to get up to 48 bits, again with a single pin-to-pin connection. That would give you 203 DAYS of counting before overflow at 62.5nS resolution.As for reading/writing, MCU's (including this one) have all sorts of communications peripherals on chip. This one has SPI, I2C, UART, etc. and you could bit-bang your own if necessary (nybble-wide bus, anyone?). Personally I'd go for SPI if the Arduino supports it since you cannot get out of phase and the bit rates are probably several megahertz, but even a serial link would probably be fast enough for what you want to do. Again, all on chip, ready to go, and reconfigurable with a reflash as you gain experience and redesign things.Just something to consider. Small MCU's are often a great alternative to one or more dedicated devices.Edit: Oh yeah, forgot to mention that MCU timer/counters generally have several hardware pin options. You can externally clock them, gate them for easy start/stop, they can emit a level or pulse upon match with an internal preset register... all of which would ease your interfacing. This one even has gating based on an on-chip analog comparator so you could trigger based on a non-digital-friendly voltage threshold.

author: Rerouter
author_rank: Super Contributor
timestamp: September 07, 2019, 05:17:08 am
content: Best of both worlds, just have 2 cheap micros running off the same clock, with reset lines tied together. have them both use analog comparator hardware to capture the time of a pin change, they just count the overflow timers and save the timer capture registers when they fire, have the chip that measured a trigger assert some interrupt pin low, the mega reads it off by SPI, and away you go.The gets you the timing resolution of up to your external clock / 2, a total duration of up to 400 days if you use a 32 bit register to count the overflows, and allows you to set arbitrary trigger voltages within its supply range.

author: magic
author_rank: Super Contributor
timestamp: September 07, 2019, 05:43:55 am
content: Easily a job for one AVR micro. Probably other families could do it too.Use the "input capture" function of the hardware counters. It counts cycles of the system clock (optionally divided by 2,4,...) and stores the current count in a side register when a specific input pin changes state. Then an interrupt is fired and you can process the event; simply copying this register to RAM will take a few µs at most.Counter overflow can also trigger an interrupt, so a 16 bit counter can be extended to 32 bits by software.You don't need 64bit timers to run for a long time. When you see that timeB < timeA you can know that the real time was INT_MAX-timeA+timeB. The only catch is, if the counter overflows every second, you won't be able to tell apart 1ms from 1ms+1s or 1ms+2s and so on. But you don't need to write software which craps out when the counter overflows after a year of continuous operation.

author: FenTiger
author_rank: Regular Contributor
timestamp: September 07, 2019, 05:50:32 am
content: Quote from: HendriXML on September 06, 2019, 10:46:32 pmWhat resolution do you need?This seems like a reasonably good fit, if you can find a 10MHz reference for it:https://www.tapr.org/kits_ticc.html

author: Ian.M
author_rank: Super Contributor
timestamp: September 07, 2019, 06:51:38 am
content: See the ATmega2560 datasheet section 18.4.1 GTCCR - General Timer/Counter Control Register, Bit 7 – TSM: Timer/Counter Synchronization Mode. Using it, multiple Arduino Mega2560 16 bit timers can be initialised to run in lock-step with each other. Then you can use separate input capture units to timestamp your start and stop external events and calculate the interval as Magic suggests in reply 6 above, even if the events are only a single ATmega clock cycle apart. Assuming the counter and captures are extended in software to 32 bit, at the highest resolution of 1/16 us, you get a rollover period of a bit under five minutes. Drop the resolution to 1/2 using the prescaler and the rollover interval becomes slightly under 36 minutes. The next step down has a resolution of 4us and a rollover period of 4 3/4 hours.The only fly in the ointment is the poor accuracy of the typical Arduino ceramic resonator clock oscillator, so you will probably need to feed the Arduino a master clock from a much better quality oscillator.

author: Kleinstein
author_rank: Super Contributor
timestamp: September 07, 2019, 07:11:55 am
content: The mega2560 or other µC in the Arduino can do the timing with the input capture function. So no extra hardware is needed. One can even use the analog comparator as the trigger source and route the signal from the ADC inputs. So one can use different inputs for start and stop, if there is some minimum time (some 1-5 µs) to switch the trigger source.It is possible to extend the resolution in software to 32 Bit, or if needed also 48 bits. So there is no real need to use a pre-scaler for the clock. However there is a tricky point when counter overflow and capture event happen at the same time. This difficulty can be solved, but it needs some extra thoughts or searching for an existing solution.

author: magic
author_rank: Super Contributor
timestamp: September 07, 2019, 09:01:35 am
content: Mega2560 is a big part so it probably has multiple counters with input capture on different pins, in which case no switching is even needed.Quote from: Kleinstein on September 07, 2019, 07:11:55 amGood observation.If I'm reading the docs correctly, a lower numbered interrupt has higher priority, so input capture IRQ should be executed before overflow IRQ if they occur in the same clock cycle. Probably the simplest way to deal with it is tocheck if the captured timestamp is zeroand then execute the overflow IRQ handler and clear the overflow IRQ flag before proceeding further.editOr not. Better check the overflow flag.Interrupts may be disabled when an overflow happens and then an event may be captured at count=1 and yet the capture IRQ will be executed before the overflow IRQ when interrupts are unblocked.

author: Kleinstein
author_rank: Super Contributor
timestamp: September 07, 2019, 09:22:21 am
content: The interrupt priorities do not fully help to solve the possible race condition, as it applies to the interrupt waiting in the queue. There still is a possibility that the capture interrupt is executes before an overflow interrupt.The way to check for the race condition is to check the interrupt flag for the overflow inside the ICP ISR. The value of the captured time stamp tells if a possible pending overflow interrupt should have come before ( small capture value) or after the capture event ( large capture value).A suitable program can be found here (text in German):https://rn-wissen.de/wiki/index.php/Timer/Counter_(Avr)#Input_Capture

author: HendriXML
author_rank: Super Contributor
timestamp: September 07, 2019, 10:20:06 am
content: Thanks for all the comprehensive suggestions! I'll investigate them.Initially the idea was to use a prefab Arduino with a not so great oscillator. I could try to replace it with a good spec one. I've used them Mega2560 timers controlling a steppers using an acceleration table (instead of linear acceleration) which took loss off torque in higher RPM's into account. I also did a fun project to have 15 rbg leds be driven using color event tables which where created for supporting "thunderstruck by AC/DC".If there's a possibility to reset their counter without software interference then that would be neat, I'll have to try to figure that out.If interrupts are needed for starting and stopping then I would probably leave this road, not because it would not do the current job well. But mainly because off my the goals I've set to minimize the delays to less that a usec. I'd also like to use electronic components to build up experience with them, instead of the more know territory. (I haven't created a digital circuit yet)I'm now short in time, but will respond more later on.Thanks to you all!

author: Kleinstein
author_rank: Super Contributor
timestamp: September 07, 2019, 10:59:57 am
content: There are likely interrupts used for the control at start and stop and if needed also the extension of the resolution. However the actual timing is done by the timer hardware. So the accuracy is as good as the clock with clock cycle resolution.The trick is not to reset the timer, but already have the timer running and use time stamps at the start and end.

author: magic
author_rank: Super Contributor
timestamp: September 07, 2019, 11:02:50 am
content: If your MCU has two timers with separate input capture pins, events A and B can be captured by separate timers.In such case they may be spaced even 1 clock cycle apart, which is 1/16µs at 16MHz.The software will then read timestamps captured by each counter and calculate the difference.editBasically, we are trying to convince you that all the digital logic hardware you described in the original post is already in your MCU, just use it

author: iMo
author_rank: Super Contributor
timestamp: September 07, 2019, 11:20:38 am
content: TDC7200, easy to use, libs for arduino..PS: with that chip (works fine) you may upgrade from the nerf darts gun to a railgun later on..

author: max_torque
author_rank: Super Contributor
timestamp: September 07, 2019, 02:03:08 pm
content: define precise?I recently did a project with a 32b counter ic, driven by an over stabilised oscillator at 40Mhz, and a micro just uploads the counts when it's triggers, and takes one for the other (as mentioned previously) to get the counts difference. The oscillator was calibrated using a GPSDO, so i've got 25ns resolution. At that level you have to be really carefully of your triggering jitter and latency, matching your triggers so as to not create an offset to the "true" events!even for a flight time measured in ms, your resolution looks good (10ms = 40,000 counts!)

author: Ian.M
author_rank: Super Contributor
timestamp: September 07, 2019, 02:25:29 pm
content: There's not much fun and learning in using a single chip time of flight event timer breakout board + 'canned' Arduino libraries. If we want to do it the smart hard way (but not the hard hard way, which would be a board full of individual counter chips, or the lazy way which would be throw a FPGA at the problem), you have to understand the constraints on a practical solution.Lets put some numbers on the problem. A M61 Vulcan rotary cannon, has a published muzzle velocity of 1050 m/s. Assuming a 10MHz counter clock, you'll get fractionally under 10 counts for every mm the round travels. Nerf guns in the Rival series allegedly top out at around 30 m/s (other series are around half that), so with the same 10MHz counter clock, you'll get 333 counts per mm of round travel. For comparison, a good fastball bowler is about as fast as the Nerf Rival dart.At 10MHz input clock, a 16 bit counter rolls over in slightly over 6.55 ms, 24 bit in slightly under 1.68 seconds, and 32 bit in slightly over 7 minutes 10 seconds.That means that with 100mm between sensors slower Nerf darts will probably overflow a 16 bit timer clocked at 10MHz overflow a 16 bit counter clocked at 10MHz. Put the sensors further apart or use the Arduino 16MHz resonator and you'll definitely overflow the AVR 16 bit timer 1 if you don't use the prescaler to drop the timer clock to 1MHz.Some MCUs are much better than others for high clock speed counting. e.g. the AVRs used in 8 bit Arduino boards synchronise the external counter clock to the core clock, so with a 16Mhz resonator, the maximum input frequency is fractionally under 8MHz with an exact 50% duty cycle.PIC18 series chips generally do much better as they have an asynchronous up to 8 bit prescaler for timer 0 followed by a synchronous 16 bit timer, and the prescaler can be clocked at up to 50MHz, (50% duty cycle again) as long as you meet the constraint FT0CKI<N/(4/Fosc + 40ns), where N is the prescaler ratio.At even 1MHz Fosc this constraint can be met for a 50MHz input signal. There's also a trick where art the end of the couter gate period, one clocks the input under software control, and by counting the number of pulses till the counter next increments, you can calculate the count that was in the prescaler, allowing you to use Timer 0 as a 24 bit hardware timer.No doubt others will chip in with the max. counter clock of their favourite MCU.Although some MCUs may have all the logic and counters you need for high precision timing internally (e.g. some 8 bit PICs have a gated Timer 1 that can operate at up to 33.3MHz from an external high precision clock source), you may well be better off implementing the timer gate logic externally, which can be as simple as a few gates and flipflops. e.g. adding a single 74LVC1G74 flipflop in front of a PIC18 T0CKI pin gives you a 100MHz gated counter with an uncertainty of 1 count (due to the flipflop.) If you are being really cheap and minimalist, use the flipflop /R input as the gate, (runs when high) and connect both /R and /S back to PIC I/O pins, so you can pulse the Q output under program control to clock out the count in the Timer 0 prescaler by pulsing /S low while /R is low. However adding one AND gate ahead of the flipflop clock pin to handle the gate lets you read the flipflop output before clearing it, for an extra bit of resolution. For external gating with separate start and stop inputs, use another one as a simple SR flipflop, with 1K||33pf between its Q output and the first one + PIC I/O pin so the PIC can override it and hold the gate inactive while it cocks out the prescaler count. That's three tinylogic gates, the cheapest USB PIC18 + commodity crystal, and a good 100MHz TCXO + a significant amount of software development to get a USB interfaced timer with 20ns resolution and on a $30 budget you can probably get 5ppm accuracy.Similar approaches can be used with Arduino, but due to the AVR timer clock frequency limitation, you'd probably want to use an external prescaler of at least four bits.

author: HendriXML
author_rank: Super Contributor
timestamp: September 07, 2019, 05:01:43 pm
content: Quote from: magic on September 07, 2019, 11:02:50 amFrom what I've read and understood the Mega2560 can indeed be used. It has 2 16 bit timers with capture mode. I think only one would be needed. The time between events is not that small. The 16 bit timers have a overflow bit. That overflow bit will be important in 2 interrupts, the capture interrupt and the overflow interrupt. If the overflow interrupt hasn't counted the overflow yet, the capture interrupt might need to do that to determine what its 16+ bits are if its "near" overflowing. This checking should be done with interrupts disabled (delaying the overflow interrupt). I'm not entirely sure yet if the Arduino software makes this possible.Using capture mode register gets the current counter and an interrupt will follow to process that value. In meanwhile no other capture events should happen: that seems doable. So thanks for sharing this option!Will continue to digest the other ones!

author: HendriXML
author_rank: Super Contributor
timestamp: September 07, 2019, 05:35:04 pm
content: Quote from: max_torque on September 07, 2019, 02:03:08 pmThe nice thing about the 74LV8154 is that the current counter can be copied to an internal register, so bits can't change during readings. Does the counter IC you've used offer something similar?

author: HendriXML
author_rank: Super Contributor
timestamp: September 07, 2019, 05:37:51 pm
content: Quote from: hamster_nz on September 07, 2019, 12:12:27 amFPGA will eventually be used in the projects, but for now a bit too soon.

author: HendriXML
author_rank: Super Contributor
timestamp: September 07, 2019, 05:52:46 pm
content: Quote from: Kleinstein on September 07, 2019, 09:22:21 amHmm you already said somewhat the same thing I later brought up. Another safe way to check for overflows would use the 2 counter compare events A and B. On at 25% of counter one on 75% and have them count high bits as well. Using the read time stamp one of them is the most safe to use (with some extra logic). Staying many ticks away from the uncertain value.I'm not sure whether the compare A B mode works together with the capture mode.

author: HendriXML
author_rank: Super Contributor
timestamp: September 07, 2019, 06:15:06 pm
content: Quote from: Ian.M on September 07, 2019, 02:25:29 pmYou gave a lot of stuff to think about, especially on how to go faster using MCU's. Scaling seems not to be necessary when counting overflows is handled in a correct way. I shall order some flipflops to experiment with, however solutions where the counting is uninterrupted might be beneficial in other usages. Like maybe measuring RPM's.

author: HendriXML
author_rank: Super Contributor
timestamp: September 07, 2019, 06:23:30 pm
content: Quote from: imo on September 07, 2019, 11:20:38 amI have to investigate this further, but it seem pretty advanced IC. With the option to time many things in parallel using more of them. With for an external timer very little pin burden on the Mega2560. Nice!

author: HendriXML
author_rank: Super Contributor
timestamp: September 07, 2019, 07:42:11 pm
content: Very interesting to think about interrupts and their order of execution again. That's how I started programming in DOS years ago. In the more modern OS's these problems got shifted to multi threading.There critical sections, mutex's are used to be sure that access to data is synchronized: no simultaneous writing of data (or reading of dynamic data). In the DOS days one had to CLI (clear interrupts) to stop another "thread" from interrupting. Arduino has the same functionality. (On the 386 there were interrupts that could not be stopped, I recall). B.t.w. on PC's even the writing of a multiple byte (word, dword etc.) value can be interrupted, maybe for Arduino's as well.What makes me uncomfortable with the overflow interrupt is that there's also stuff going on beyond code (which can be protected). Like overflowing (going to zero) and the setting of the overflow flag should be atomic from the perspective of the code. Between one and the other instruction both should be updated, not only one. This I guess is the case.However reading them will not be atomic.It would have make me then more comfortable if there where higher order functions that would give both of them, and do a clear of the flag atomically. The caller that gets the overflow flag is the one that increments the higher bits. (Those higher bits would be read from / written to in a protected way.)I don't think such a function can be written without interpretation of the counter, because of the external nature of updating the counter and flag.So the next best thing would be to first read the counter, and only read the flag if the counter is less the 50% of max. (If it's more than that the flag is always 0. In reality it could be 1, but that wouldn't match the counter) If the flag is set then clear it and update the high bit counter. The same should be done in the overflow interrupt. The reason for this is only to not have the overflow happen twice without the execution of this code in between. (Anything happening more frequent than that would be usable - it should however be called before the counter reaches 50%)99.99% of the time the overflow interrupt will be updating the higher bits. But that interrupt can't be the only one.

author: magic
author_rank: Super Contributor
timestamp: September 07, 2019, 07:52:12 pm
content: AVR has the CLI/SEI instructions to temporarily disable/enable interrupts.Interrupts are automatically disabled when the core starts executing an interrupt handler and re-enabled when it ends.SEI can enable interrupts within an interrupt handler, but I doubt that Arduino would do it behind your back and of course you don't need to do it either if it's undesirable.

author: HendriXML
author_rank: Super Contributor
timestamp: September 07, 2019, 08:33:27 pm
content: There're 2 ways to hook on interrupts one using ISR(vector) and one using AttachInterrupt.One is (I think) replacing the low level interrupt handler, one is adding something to be called by the standard low level handler.It wouldn't surprise me if the latter has interrupts disabled and the other one not. If it's time critical the first option is probably the better choice.It is good to know only one interrupt of a kind can be queued, so staying in them with interrupts disabled (for long) can have side effects. So if one knows what he's doing it's best to have only a small portion of code in CLEI/SEI. Besides that is getting harder to understand interrupts within interrupts within ... this is probably the main idea behind the common advice to keep the interrupt code very short.But if one had to do some lengthy calculation (of lets say 500 ticks) every 1000th of a timer interrupt then it can still work fine. It will be interrupted many times (500).I haven't tested this, but that is how I expect it to function.

author: Ian.M
author_rank: Super Contributor
timestamp: September 07, 2019, 08:58:02 pm
content: The key to handling software extended capture registers with a software extended timer is: right at the start of the ISR, to grab the timer high bit, timer overflow interrupt flag, and all capture interrupt flags, and pack them into a byte or word for further processing. Then do it again and check they haven't changed. Repeat until you get two consistent sets of high bit and flags, and you then avoid all race conditions due to captures on different channels happening too close to each other or to the timer rollover. All the possible cases encoded in the flag set, including incrementing the software timer extension if the stored overflow flag is set and doing a soft capture of the timer extension if any capture flag is set can then be handled without races or severe timing constraints.If multiple close captures on a single channel are required, that capture channel's capture register must be read in the same loop that gets a consistent flag set, and also checked for consistency.On MCUs with vectored interrupts like the AVRs, point the timer overflow and all the capture interrupt vectors at the same ISR as described above. Arduino attachInterrupt() is not useful here as it only supports interrupt capable digital pins, and one needs to route the timer overflow to the same ISR.However that's a *LOT* of work and is only really appropriate when you have multiple input events (and possible generated output events) that you need to relate to a common long period timebase, so if you just want to measure an interval (or a frequency), its much simpler conceptually to use an internally or externally hardware gated timer that can simply count up from zero, with or without software extension, and only needs to be read when the gate signal has ended and the timer is thus stopped. I described this for a PIC18 Timer 0 in my previous post.The original method of clocking a prescaler under program control to extract the count in it that you cant read directly comes fromMicrochip AN592.

author: HendriXML
author_rank: Super Contributor
timestamp: September 07, 2019, 09:05:47 pm
content: Quote from: HendriXML on September 07, 2019, 08:33:27 pmHmm what if a time critical interrupt was called and the bulky one immediately steps in... That would not be so great. Starting with interrupts disabled would have benefits then..I've checked this, the interrupts are disabled by HW when entering an interrupt. Thus when doing a bulky operation one should enable them.These things also show why using external stuff (the counters are kind of external too) can be advantageous.

author: HendriXML
author_rank: Super Contributor
timestamp: September 07, 2019, 09:55:55 pm
content: Quote from: HendriXML on September 07, 2019, 07:42:11 pmAs said by Ian above reading stuff repeatedly is also a solution, but its good to know that this is because they are written atomically (at least practically). Otherwise one could theoretically read inconsistent values twice.(Just to point out the difference between externally changed stuff, and for instance updating internal memory, which can be interrupted and be inconsistent a lot longer.)

author: HendriXML
author_rank: Super Contributor
timestamp: September 07, 2019, 11:09:53 pm
content: Quote from: imo on September 07, 2019, 11:20:38 amThe time range it can measure is a bit narrow for a multi usable timer circuit.

author: magic
author_rank: Super Contributor
timestamp: September 08, 2019, 06:32:13 am
content: Not sure why you are trying to read anything "atomically"?Kleinstein showed how to do it.Check the overflow flag. If it's zero, no problem. If it's one, you need to figure out if the event has been captured just after the overflow or just before the overflow. You do it by checking if the captured timestamp is just above 0 or almost 0xffff.Alternatively, compare it against the current counter value. That enables you to block interrupts for almost entire counter period and still avoid overflow errors. Figuring out the details is left as an exercise for the reader.Because it's pointless, just don't block interrupts for longer than a few dozen cycles.By the way, the value of TCNTn or ICRn is one of the few things that can actually be read atomically; consult the datasheet.

author: HendriXML
author_rank: Super Contributor
timestamp: September 08, 2019, 09:27:42 am
content: Quote from: magic on September 08, 2019, 06:32:13 amI was not trying to make a point to the very experienced. I mostly write with starters like myself in mind.Off course you can always read the flag, but why do so if you act on its value only if the counter is low (overflow happened in counter as well).From my perspective the "thread" that reads a positive flag takes responsibility to also do the high bit increment and clearing the flag. It's showing a way of thinking about this stuff. It is mostly about "atomic" read and writes, having stuff consistent with each other. Think about it that way leads to solid solutions in other problems as well, that why I like to hammer on it.

author: HendriXML
author_rank: Super Contributor
timestamp: September 08, 2019, 09:33:18 am
content: Quote from: magic on September 08, 2019, 06:32:13 amIn my posts I compare it to the 50% value, 0x7fff- just to explain the 50% terminology

author: magic
author_rank: Super Contributor
timestamp: September 08, 2019, 10:48:49 am
content: Quote from: HendriXML on September 08, 2019, 09:27:42 amI'm not sure if I understand what you are doing and I'm not sure if you are doing it right.You don't need to check if TCNT1 is low or high to detect overflows. The overflow flag tells you that an overflow has just happened and hasn't been handled yet and that's all you need to know about overflows.But if you use input capture, you need to check ifICR1is low or high, if you discovered that there is a recent unhandled overflow.The value of ICR1 tells you whether the captured event happened before (ICR1>50%) or after (ICR1<50%) the unhandled overflow.This information is required to decide whether to increment the high bits before processing the captured event or leave it to be incremented later by the overflow ISR.editWell, if you want to, of course you don't need to leave that job to the overflow ISR, you can increment it yourself and clear the flag.But it needs to happenaftera copy of ICR1 and the old high bits has been recorded by the input capture ISR as the event timestamp.Again, I don't know. Maybe I'm saying the obvious, maybe you are making a mistake.

author: HendriXML
author_rank: Super Contributor
timestamp: September 08, 2019, 11:15:39 am
content: Quote from: magic on September 08, 2019, 06:32:13 amGood info! A good example of how they took extra effort of making something atomic..So first read low byte and then high byte.However reading both the counter and the flag together cannot be done consistent (atomically). That was something I was referring to. So my brain gets to think how to make it atomic? Why not take a derived overflow flag, which is always 0 when no overflow has happened in the counter. And is only 1 when the captured counter is low, and the real flag is still 1. It would even be possible that the flag would have been cleared HW after reading it (I think this kind of "tricks" are done), so hence the "practice" of not touching the flag if not needed. (You touch it, you eat it) In this case the flag probably won't change when reading (haven't checked it) but it could have been a way to only have one "thread" made responsible for it.A side note, this atomic stuff is relative to the HW changing things. There still the requirement that only one thread reads the low and the high counter value. That is not atomically by it self, so interrupts should be disabled.Normally api would try to handle these kind of pitfalls. So for young players this discussion might be of use.

author: Kleinstein
author_rank: Super Contributor
timestamp: September 08, 2019, 11:49:46 am
content: There is no need to make reading the overflow flag and ICP value atomic. The check is done inside the ICP ISR and thus with disabled interrupt flag. If the ICP value is changing due to a 2 nd event, one has a problem anyway.With the AVR there is no problem reading the interrupt flag (no "You touch it, you eat it").When the overflow happens just before the ICP, it can happen that the ICP ISR is called first. The case has to be handled separately and is checked with the overflow interrupt flag and ICP value. When actually handling this special case one can just do the jobs normally done in the overflow ISR and clear the flag or only correct the one reading and let the overflow ISR run (just after the ICP ISR is done). The 2 nd case is preferred if there are more jobs done in the timer overflow ISR.Reading and writing low and high byte in the correct sequence is handled by GCC. However it does not use CLI/SEI to disable interrupts.Quite often the ISRs don't write to 16 bit registers that would effect the 16 bit access. In this case no CLI/SEI needed to make the access atomic.

author: Ian.M
author_rank: Super Contributor
timestamp: September 08, 2019, 12:45:59 pm
content: The problem is, on many MCUs you need a snapshot of the flags and timer high bit state at a single moment in time to unequivocally guarantee that you are dealing with the capture extension and timer overflow extension soft registers correctly without inadvertant race conditions if they are too close together that might result in out of order processing and a 65536 count error in the full extended capture time stamp. As the interrupt flags and the timer high bit are in different registers and are hardware volatile, so can never be read atomically, repeating till you get no changes in the snapshot is needed. The same approach will work on *ANY* MCU that doesn't have clear on read interrupt flags, or even by polling in your main loop if you cant hook the necessary interrupts.On some MCUs with vectored interrupts at the same priority level you *MAY* be able to get away without a snapshot and use separate ISRs, but to confirm that requires a deep understanding of the specific MCU's interrupt structure and worst case latencies, and if any library code can hook the same interrupts or runs at a higher interrupt priority, is horribly fragile.

author: Kleinstein
author_rank: Super Contributor
timestamp: September 08, 2019, 01:26:31 pm
content: The correction inside the ISR depends on the ISR priorities and HW details. So it is hardware specific (should still work for all AVRs with ICP so far).The AVRs don't have interrupt priorities in the sense of a higher priority interrupt interrupting another ISR. There is only a sequence in which the pending flages are checked. When doing only the correction (still keep the overflow flag active), there should be no problem if the lib code / Arduino environment would add extra code to the ISRs (as long as it would not enable nested interrupts before the timer code). Unless extremely long interrupt latency does not matter.Repeated reading of capture register and software extension is quite tricky and would add other limitations form the loop speed. It does not really solve the problem and one would still have to handler the race condition case separate.So I would not consider repeated reading in the main loop a viable solution. In the ISR there is no need to repeat the register readings - it's more like the opposite to read the ICP register only once, so that in case of 2 events one gets at least the correct time of one of the events and not in rare cases a wrong mixed value.

author: Ian.M
author_rank: Super Contributor
timestamp: September 08, 2019, 01:52:36 pm
content: To get a consistent state for further processing you only need two identical repeated reads of the interrupt flags + the timer high bit. Initialise the old_state variable with a spare bit set and the state variable with the spare bit cleared to guarantee an initial mismatch and it simply becomes:Code:while(state!=old_state){old_state=state;state=pack_flags_and_timer_high_bit(); //replace with inline bitwise stuff}//Smash old_state here if in main loopThat cant hang for more than four passes unless you have a storm of short pulses on either capture trigger pin as only three independent event sources contribute to the state. (Timer overflow is a dependent event - it can only happen coincident with a high bit 1 to 0 transition). Therefore, if in each timing run you disable each capture unit after a single acquisition or if you are making do with a single capture unit, disable it after the second acquisition, then execution time is strictly bounded.There is absolutely no need to 'blind read' or repeatedly read any capture registers, or reread the timer or keep any part of the timer value except its high bit.If you handle the various cases in the main loop, then you don't use any ISRs, and don't enable the relevant interrupts. AVR peripherals set their interrupt flag when their trigger condition is met even when that interrupt is masked or otherwise disabled, so one would simply poll the interrupt flags to grab the state as described above then handle the various cases, as if in an ISR except with worse latency (i.e. the superloop execution time). With an AVR @16MHz, you've got 2ms for the full superloop including the worst case capture and timer handling code

author: HendriXML
author_rank: Super Contributor
timestamp: September 08, 2019, 01:59:30 pm
content: I'll try to summarize the things that have been said and are important:Is the counter high/low byte and flag written consistent?I assume it's atomic between 2 instructionsCan the counter be read consistent?Yes by reading low byte before high byte. The high byte is kept safe.Can this reading be interrupted?Not in an interrupt, otherwise disabling interrupts is needed.Is reading the counter and the flag always consistent?No, when a overflow happened, reading the first one of them can be old news. Reading it repeatedly can assure one self of the consistency. How ever because the counter can be read consistent and there's a lot of time between overflows, one also could assume there isn't a overflow when counter is larger then x7fff. And do a check is it's less than that. This way the counter is always consistent with the flag. (Only if the counter is xffff (or a bit less) and the flag is 1, there's inconsistency)How about counting the 16+ bits?On every overflow one should be added to the high word of the 32 bit counter. Its best to take the overflow flag for that, it is like a 17th bit. But the 16 bit value can only overflow once in that bit so it has to be cleared.How to ensure the overflow isn't counted double?The code that updates the counter, should also be the one that clears the flag. This operation must not be interrupted. Within a interrupt this is ok, outside an interrupt interrupts should be disabled.How to ensure every overflow is counted?Before the counter reaches x7fff the flag should be checked, counted and cleared. The overflow interrupt is very useful for that.How to ensure that the counter and overflow flag are related?Because the captured value is old news and the overflow flag is actual, the capture interrupt must be executed before they are not related anymore. The flag is checked when the counter is less than x7fff and might be unrelated if this is done x7fff ticks after HW capturing. So interrupts should not be disabled more than a portion of that.There're different implementations possible but i think this lists what to consider. If something's missing or wrong I'd like to hear it.

author: HendriXML
author_rank: Super Contributor
timestamp: September 08, 2019, 02:30:48 pm
content: Quote from: Ian.M on September 08, 2019, 01:52:36 pmAs a general solution one should ensure that reading 2x an inconsistent state is not possible. (In this case that's ensured)

author: magic
author_rank: Super Contributor
timestamp: September 08, 2019, 02:34:59 pm
content: This seems correct.Writes to 16 bit counter registers are atomic if you write the high byte first and then the low byte. The high byte is remembered and applied later in the same clock cycle when the low byte is written. Interrupts must not touch the timer in the meantime.I'm not sure if this clears the overflow flag. It may need to be cleared manually; in such case you better do it fast before the timer overflows for the first timeQuote from: Ian.M on September 08, 2019, 01:52:36 pmThat's all nice until you need to deal with an 8 bit timer running at full core clock speed

author: HendriXML
author_rank: Super Contributor
timestamp: September 08, 2019, 02:49:31 pm
content: The captured counter and the current overflow flag can also be unrelated.That would be the case when the capture interrupt was delayed very long. I'll have to put that in the list as well.

author: Ian.M
author_rank: Super Contributor
timestamp: September 08, 2019, 03:01:06 pm
content: Consider the ATmega case with two capture units. Their timers are synchronised so there is only one overflow event, but there are two independent capture events. If the events are well spaced apart (and not too close to the timer high bit toggling) the loop only takes two passes. However if the start, stop and overflow events happen *VERY* close together, each of the later ones can invalidate the state match and force another pass of the loop.Quote from: magic on September 08, 2019, 02:34:59 pmOnce set up you *never* need to write to the timer.You've got half the timer rollover period before the state you've grabbed becomes useless. As I edited in above, that's fractionally over 2ms on an AVR @16MHz. Use two capture units and complete the whole multi-state processing ISR including any flag clearing that needs to be done in under 2ms and you can guarantee not to miss any even if they are coincident and bang on top of a timer rollover, unless the same event recurs within your ISR execution dead time before you've read the event's capture register and cleared its flag. If you need to handle fast repeating events put grabbing any active captures from their registers immediately after the consistent state loop. You'll still be in trouble if a third event on the same channel occurs within the total ISR execution time of the second one.Quote from: magic on September 08, 2019, 02:34:59 pmWhich is why you don't try to do this sort of s--t on a 12 bit core PIC, which as they generally don't have interrupts, also dont have an interrupt flag for their 8 bit timer 0, so you have to treat it as a 7 bit timer with a (self-resetting) overflow flag in the 8th bit that worst case will only be valid for 128 instruction cycles.BTDTGTTS

author: HendriXML
author_rank: Super Contributor
timestamp: September 08, 2019, 04:01:36 pm
content: There's something missing in my list of things to consider.At high (near xffff) captured counters it might be possible to add a high word that is new (and has an overflow in it). This would be the case if the overflow interrupt executes before the capture interrupt but after the capture has happened. I don't know whether that's actually possible. But i think the only way to counter attack that situation is to delay the increment of the high word of the counter.

author: magic
author_rank: Super Contributor
timestamp: September 08, 2019, 04:28:56 pm
content: This scenario shouldn't be possible.When interrupts are enabled and multiple IRQs are waiting for execution, the one with lower number will be executed first. In all AVRs I have seen, input capture comes before overflow. Check the list of interrupt vectors in mega2560 datasheet to confirm.

author: Kleinstein
author_rank: Super Contributor
timestamp: September 08, 2019, 04:46:04 pm
content: Not using repeated reads and no interrupts still leaved the task to decide if a new capture and an overflow occur in the same loop to decide if capture data are before or after the overflow. So the repeated reading still need to catch and separate the specific case of capture just before overflow. It is not much more than avoiding the µC internal interrupt handling and do essentially the same tasks under software control.Usually the hardware interrupts should be faster, when watching for more than 1 signal.

author: HendriXML
author_rank: Super Contributor
timestamp: September 08, 2019, 04:46:34 pm
content: That would be great. I don't know whether the following is possible. Have a compare A and a compare B event set up. One increments HiWordA, one HiWordB.Which HiWord to take is dependent on the captured counter value. That is shown below the X axis. This solution doesn't use the overflow flag anymore.I know the compare A and B stuff can work, but can they work together with capture events?

author: Kleinstein
author_rank: Super Contributor
timestamp: September 08, 2019, 05:06:33 pm
content: The system with 2 counters for software extension should also work. The counter used for large ICP values could still use the overflow interrupt.Compare A and B work together with input capture - unless used as the counter upper limit, the ICP function is always active - there is no specific way to turn it off (except using the register as upper limit). One normally just does not look at the ICP registers.

author: HendriXML
author_rank: Super Contributor
timestamp: September 08, 2019, 06:10:59 pm
content: That's great! This solution with 3 high word counters using also the overflow interrupt has some longer periods on which the linking of a captured counter to its high word is valid (25% -> 33% of a full cycle)

author: Ian.M
author_rank: Super Contributor
timestamp: September 08, 2019, 06:58:17 pm
content: Heh. It should be possible to determine the correct software high word for one cycle under half the rollover period after the instant of capture using the 'read all the flags + high bit''method. That's 2ms on a 16 bit counter clocked at 16MHz e.g on most 5V AVR Arduinos. Just plot out all the possible race conditions assuming interrupts are never disabled for more than 2 ms, and build the logic accordingly.

author: KrudyZ
author_rank: Frequent Contributor
timestamp: September 08, 2019, 07:33:09 pm
content: Measuring the time between two trigger signals is a fairly common requirement.The equipment used is called a time interval counter or analyzer.An interesting read describing a state of the art instrument from 1978 !!! which achieved 20 ps single shot resolution can be found here:https://www.hpl.hp.com/hpjournal/pdfs/IssuePDFs/1978-08.pdfAs you can imagine newer instruments far exceed the 5370B in capabilities, but HP provided chip level schematics with detailed explanations if you want to dive deeper, which makes this very educational to read.

author: HendriXML
author_rank: Super Contributor
timestamp: September 08, 2019, 08:08:33 pm
content: It's clear to me that using the Mega 2560 board can do very nice stuff with event timing. With a resolution smaller than 1 usec. I will implement the time span measurement with that board. I'll use 2 timers 4 and 5. Their event capture pins on the board are 49 (ipc4) and 48 (ipc5).Why use 2 timers? That give 2 timestamp registers, so they won't get in each others way. This way (as has been said by Ian and others) very small time spans can be measured. The interrupts will be also queued independent of each other, so no loss of interrupts.Also the events can be discriminated from each other (start vs stop). Especially in the situation when a Nerfdart is shot wrong (no start gate or no stop gate) this is handy.Is there a special way to synchronize them? Or is this just a matter of finding the code toDisableInterruptsSetCounterTimer4(0)SetCounterTimer5(0)EnableInterrups

author: HendriXML
author_rank: Super Contributor
timestamp: September 08, 2019, 08:23:01 pm
content: Quote from: Ian.M on September 07, 2019, 06:51:38 amI think this answers the synchronization method. I'm not sure what it does yet, but I'll figure it out.Is it better to redo the oscillator instead of replacing the resonator with a crystal (don't know if that possible )?

author: HendriXML
author_rank: Super Contributor
timestamp: September 08, 2019, 08:37:23 pm
content: B.t.w. many thanks for all the thoughts and suggestions!

author: HendriXML
author_rank: Super Contributor
timestamp: September 08, 2019, 10:22:12 pm
content: Just to get an idea, can this be used as a master clock?https://nl.mouser.com/ProductDetail/ECS/ECS-TXO-2016-33-160-TR?qs=PzGy0jfpSMvKXA%252Blz%252B4RRQ%3D%3DNo extra components needed? Except for level conversion and 3.3 V power supply?

author: Ian.M
author_rank: Super Contributor
timestamp: September 09, 2019, 02:15:12 am
content: The Arduino Mega 2560 has an on-board 3.3V 50mA regulator for 3.3V peripheral interfacing, so all you'd really need to use that TCXO would be to add level shifting (e.g 74LVC1T45 in SOT26 package) to get a full 5V swing , and run the output to XTAL1, after disconnecting the resonator.I've written you up a set of instructions to do the mod, assuming a genuine MEGA 2560 or a clone with a very similar layout. They are written for someone who hasn't done a lot of SMD rework, but is confident doing fine PCB work neatly:You'll need an Arduino MEGA 2560 + a suitable PSU for it - not your PC unless you use a power only USB lead; the above TCXO (or a very similar one with a compatible pinout), a 74LVC1T45 level shifter in SOT26 package, two very small SMD 10nF ceramic caps (metric 1608 imp. 0603); fine Magnet wire - preferably with solder-through polyurethane enamel, a few inches of fine single strand tinned copper wire - pull a couple of strands from any factory tinned multistrand wire you've got handy; gel superglue, clear nail varnish; IPA, foam Q-tips, and kitchen towel for cleanup; an Xacto Knife or other scalpel with a small convex curved cutting edge for track cuts and solder mask scraping, a small flat blade Jeweller's screwdriver for prodding stuff and holding wires in place, a small softwood disposable chopstick to assist with forming the magnet wires, a pair of fine heat resistant non-magnetic tweezers for handling SMD parts, and also the usual soldering tools and consumables. You may also need an AVR ISP programmer.CAUTION: The following instructions were written without either a Mega 2560 or the level shifter IC and TCXO, in front of me, so please carefully read through each step, and check that it both makes sense to you and makes sense electrically when checked against the datasheets and the Mega 2560 schematic, and check part positioning before you do anything to the Arduino board.General instruction for later steps:Try to keep all wiring as neat as possible, ends inline with pins and tracks and at rightangles to pads for passives. Form the wires to lie along the board surface using the end of the chopstick to hold the wire against the board and make a bend with the free end of the wire where needed. If you don''t have a really good pair of flush cutting miniature precision dykes, the tinned wire and magnet wire are probably best cut with the scalpel with firm pressure against a hard surface, if possible while bending the wire up and down so it breaks at the blade. If cutting wire in-situ on the board, be careful not to cut any tracks!Preload the Arduino with the simple 'Blink LED' demo sketch so its easy to check its working. Confirm that!Scratch up the surface of the solder mask for adhesion and dead bug*the 16MHz TCXO on a drop of superglue on the area of topside ground plane above the wordPOWERnext to the 100K (105) resistor beside the resonator etc. Leave space for the '1T45 in the next but one step.Very carefully bend up the middle pin each side of the '1T45, leaving the four end pins alone. With the tip of the jeweller's screwdriver held against the root of each middle pin in turn (so you don't attempt to reverse the bend right on the original factory bend line and crack the pin) carry on bending the pins up. Flip it over, put it on a hard surface and form the middle pins till their ends are flat to the surface its top is on.Scrape a bit of ground plane next to the TCXO and at rightangles away from the middle of its long side (on the long side with both pin pads fully square, not the one with the pin 1 pad with a bevelled inner corner), and solder the '1T45 down by both its middle pins with its pin 6 next to TCXO 'pin' 4 ánd its pin 4 next to TCXO 'pin' 3. The spacing is almost the same so you can get it really closeSolder a 10nF SMD ceramic (fly-s**t sized 0603 as it needs to be no more than 1.8mm long) across TCXO 'pins' 1 and 2.Scrape a spot for a really close ground next to the TCXO 'pin' 2 (Gnd) and run a strand of thin tinned bare copper wire up to the end of the 10nF cap to connect it.Using thin magnet wire, tin the end far enough to get it across '1T45 pin 6 (VccB), and TCXO 'pins' 4 (Vcc) and 1 (Tri-State) control (on the cap end), solder it to all three, and take the other end of the magnet wire to a spot scraped and tinned on the track to the 3.3V pin of the nearby power header.Solder a strand of thin tinned copper wire between TCXO 'pin' 3 (Output) to '1T45 pin 4 (B - configured as input) to hook up the TCXO to the level shifter.Tombstone#another 10nF cap soldered to the ground plane right next to '1T45 pin 1 (VccA), and solder the cap top end to the pin 1. Run magnet wire from pin 1 / cap top to the 5V end pad of the decoupling cap next to the Power header.You should now have a working 16MHz TCXO with 5V level output on the remaining unsoldered pin 3 (A) of the '1T45. Power on the Arduino and check its there with an x10 scope probe, and that the sketch still runs. Disconnect the Arduino again.Cut the tracks to the resonator on the longish straight run in line with the edge of the ATmega, so you can more easily reverse the mod if you need to, as neatly as possible, making two parallel cuts side by side approx 0.5mm apart, at rightangles across both tracks, with moderate pressure and a rocking motion of the scalpel blade. Clear any remaining copper out of the gaps between the cuts, and scrape back the solder resist on the lower track to pin 34 (Xtal1) of the MCU, on the MCU side of where you cut, to bare it starting a few mm in from the end you just cut. Leave solder resist on the track end right next to the cut so the wire you will add there doesn't short to the resonator side of the track cut if you've tinned slightly too much of the wire endTin the track you just scraped and run magnet wire from it to pin 3 (A - configured as output) of the '1T45.Congratulations, the hookup is now complete. Power on the Arduino, and check it still runs the sketch.Now clean-up the flux residue with a foam Q-tip wetted with IPA. Keep blotting it on the kitchen paper to remove dirty IPA, and wet it again with clean IPA, changing Q tips as required. Blot off excess IPA with a dry foam Q-tip. Dry the board thoroughly, with warm air if you've got it.Check all magnet wires are formed to the board and arranged neatly, then tack them down at bends etc. with small drops of superglue, holding them in place with the jewler's screwdriver if they try to spring up.Finally, coat the mod and its wires with the clear nail varnish to protect them and allow to dry thoroughly. Overnight is good, or wait till touch dry then an hour in a very warm, but not boiling hot place.Ideally you should reFlash the ATmega to change all the CKSEL Fuse bits to binary 0000 to permanently select the external clock. You'll need an ISP programmer (e.g. USBasp, or another Arduino running the ArduinoISP sketch). Follow your programmers instructions (or find a tutorial) for changing FUSE bits. Be careful - if you get them wrong you can 'brick'the ATmega and wont be able to recover it without a HV ISP programmer or another Arduino + a 'fuse-buster'shield. If you do zero the CKSEL fuse bits, you'll need to reprogram them if you ever want to revert to the resonator.If you ever want to revert the mod, after reflashing the fuse bits (if you changed them) with the usual Mega 2560 ones, disconnect the 3.3V supply to the mod to power down the TCXO, and the pin A output to the cut track, then bridge the two track cuts with strands of fine tinned copper wire. The 5V to the '1T45 can be left as it goes inactive when its 3.3V VccB is removed.Corrections to any errors or omissions are welcome.H.T.HIan.* Dead Bug / Dead Bugging: Mounting a chip upside down on a dot of glue, pins (or pads for leadless packages) up in the air for patch-wiring with magnet wire. So called because of its resemblance to a deceased fly on its back! Don't forget to mirror image the datasheet pinout, as that will originally be a top view and you've now got it bottom up.# Tombstone / Tombstoning: When a SMD passive is flipped up on end with only one end soldered to the board. Usually a process fault when one end is solder starved, or reflows much sooner than the other then surface tension flips it up, but sometimes done deliberately for Manhatten Island prototyping and for SMD prototyping if you don't have a pair of pads to put the part across. So called because of the resemblance to a traditional grave marker.

author: magic
author_rank: Super Contributor
timestamp: September 09, 2019, 05:57:05 am
content: Quote from: HendriXML on September 08, 2019, 08:08:33 pmNo, because counter4 will already be at a few counts by the time you set counter5 to zero.Maybe SetCounterTimer5(4) would do what you want, but it's better to stop clock to the timers for the time of setting them up. See the first post by Ian.M in this thread.I'm not sure what's the point of using a quartz generator. Does it come with some ceramic resonator out of the box? Even then it's accuracy may be good enough.If you build the project and find out it's not, it may be easier to replace the resonator with a passive quartz crystal than mess with generators and level shifters.By the way, contrary to the datasheet, my experience shows that 3.3V clocks drive 5V AVRs with no problem.

author: Kleinstein
author_rank: Super Contributor
timestamp: September 09, 2019, 08:43:03 am
content: If the board is fitted with a ceramic resonator (usually plastic case) one may consider converting to a quartz resonator (metal case). A quartz may need extra caps to ground (some 20 pF). Many of the boards I saw already have a quartz - ceramic resonators are not that common at 16 MHz.Just a normal external resonator may not be much better than a quartz directly at the µC, especially if the supply and ground connection is not good. An external oscillator would in my opinion only make sense if a TCXO, so one with very stable frequency.

author: hamster_nz
author_rank: Super Contributor
timestamp: September 09, 2019, 10:57:12 am
content: Quote from: HendriXML on September 07, 2019, 05:37:51 pmPlease reconsider... as an example of how this is the perfect domain for this tool, here it the complete timing module, which will be accurate to maybe +/-20ns with a 100MHz clock (pretty much standard for an FPGA).You just need outside code to filter the inputs (synchronise them to the system clock using a few flipflips, and maybe de-glitch the start and end signals to prevent double triggering):Code:library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.NUMERIC_STD.ALL;entity timer isPort ( clk : in STD_LOGIC;-- control inputsstart_signal : in STD_LOGIC;end_signal : in STD_LOGIC;rearm_signal : in STD_LOGIC;-- statue signalsarmed : out std_logic := '0';running : out std_logic := '0';-- counter outputscount : out STD_LOGIC_VECTOR (39 downto 0) := (others => '0');new_count : out STD_LOGIC := '0');end timer;architecture Behavioral of timer issignal counter : unsigned(39 downto 0) := (others => '0');beginprocess(clk)beginif rising_edge(clk) thennew_count <= '0'; -- default for the new countarmed <= '0'; -- default for the 'armed' status LEDrunning <= '0'; -- default for the 'running' status LEDif counter = 0 thenarmed <= '1'; -- we are armed!if start_signal = '1' thencounter <= counter +1; -- We are triggered!end if;else -- if we are countingif end_signal = '1' then-- time to stop and output the new countcount <= std_logic_vector(counter);new_count <= '1';counter <= (others => '0');elsif rearm_signal = '1' then-- reset into the 'armed' statecounter <= (others => '0');else-- keep on counting as the timer is runningrunning <= '1';counter <= counter +1;end if;end if;end if;end process;end Behavioral;If you send that 40-bit "counter" to the AVR when new_count is '1' (most likely as ASCII hex). your problem is fixed. Far better than these "angels dancing on the head of a H/W timer's IRQ latency" issues.

author: iMo
author_rank: Super Contributor
timestamp: September 09, 2019, 11:55:04 am
content: The jump into the FPGA world is for most ..duino users a difficult exercise. It is not only about the verilog but the entire infrastructure (boards, programmers, tool chain, etc) you have to master.Even I've been driving the TDC7200 off an FPGA (start/stop TOF for my reciprocal counter), wiring it to an ..duino is matter of say 100 minutes (14pins smd, SPI interface). The arduino library is there, what you need is an external source of up to 10MHz for the TDC7200 calibration (the chip is 3.3V). And the resolution is 55ps with 35ps stddev in two modes (upt to 500ns and up to 8ms). So no need to mess with FPGAs, and you get a tool comparable with $10k HP rigs..

author: HendriXML
author_rank: Super Contributor
timestamp: September 09, 2019, 12:33:48 pm
content: Thanks again for the responses.FPGA's are not in my comfort zone. If I use a component and scripts I only do so if I understand them well, or in the process of learning them. I don't like to copy 'n paste stuff.I'm now going trough the elaborate post of Ian together with a good image of the Mega 2560 board, to figure out which traces/components have a role.Before the mod is ordered/finished I'll off course use the resonator that is on board. Experimenting with a little oled display to show the measured speed. I also need to invert the signal coming from the gates (I guess). It is a comparator pull down signal when something crosses the beam.What I like about this exercise is:"Building" a high freq accurate clock signalModding the board, that is one step towards lower level interaction with the AVR. Seeing it as a component and not only as a module (together with the board).Being already familiar with IDE and stuff. So that part won't be too difficult.

author: HendriXML
author_rank: Super Contributor
timestamp: September 09, 2019, 12:39:23 pm
content: I've just started, but it might be interesting to show what's standard on the board. (I also haven't checked the powerlines yet..)I think it's now driven by the resonator (AG) near R14.

author: HendriXML
author_rank: Super Contributor
timestamp: September 09, 2019, 01:04:44 pm
content: Quote from: magic on September 09, 2019, 05:57:05 amI think I might try that as well.

author: HendriXML
author_rank: Super Contributor
timestamp: September 09, 2019, 01:49:33 pm
content: A summary action list without a level shifter would be:1) Program CKSEL Fuse to 00002) Cut traces to XTAL1 and XTAL23) Add clock output of TCXO Oscillator to XTAL14) Add power and components to OscillatorI think I'll glue the TCXO Oscillator to the Atmel chip, in that case it would be very easy to solder the output to XTAL1.

author: HendriXML
author_rank: Super Contributor
timestamp: September 09, 2019, 02:36:19 pm
content: Quote from: HendriXML on September 09, 2019, 01:49:33 pmGlueing the oscillator to a heat source might not be the best of idea. So will do it on the board.

author: Kleinstein
author_rank: Super Contributor
timestamp: September 09, 2019, 03:13:26 pm
content: The external oscillator should also work with the clock setting for the crystal currently used. Chances are even better than that it works with 3.3 V level. In this case one needs cut the XTAL2 line. With fuse setting 0000 (that is external clock) one could get away without cutting the trace at XTAL2 as the pin would be unused in this case.The board just below the µC is probably the better position than on top of the µC.

author: magic
author_rank: Super Contributor
timestamp: September 09, 2019, 06:33:29 pm
content: Desolder the resonator instead of cutting traces. Put a big blob of solder on each end and melt them both at the same time by applying soldering iron to the side.Then you will realize it's easier to put an SMD quartz there instead of modding the board for active generators.

author: HendriXML
author_rank: Super Contributor
timestamp: September 09, 2019, 06:40:49 pm
content: I also searched for a different TCXO Oscillator:https://nl.mouser.com/ProductDetail/ECS/ECS-TXO-5032-160-TR?qs=GSXfdiLIrOFsAHbhYZImDw%3D%3DThe previous one was shockingly small 2.0 x 1.6 mm.., this one 5.0 x 3.2 mm that's a bit better. I guess the pictures didn't match the smaller component.I understand now what Ian meant with the small s..t caps.They don't show not much info on how to implement this component. Would a normal THT ceramic bypass cap of 100 nF be ok? Or does it need to be SMT for its "short" legs?

author: HendriXML
author_rank: Super Contributor
timestamp: September 09, 2019, 06:42:57 pm
content: Quote from: magic on September 09, 2019, 06:33:29 pmOnly the resonator would need replacing by a quartz ? Not the caps? I'll try to find a replacement.

author: HendriXML
author_rank: Super Contributor
timestamp: September 09, 2019, 06:56:06 pm
content: Hmm, days ago it would haven been accurate enough, but now I got attached to the better specs. And as said the modding needs to be a bit challenging.

author: magic
author_rank: Super Contributor
timestamp: September 09, 2019, 08:10:37 pm
content: Yes, the capacitors may need replacement too unless they are close enough in value. Removal technique is the same.If their value is only slightly wrong, the frequency will be off a bit. Less than 0.1% IIIRC.

author: Ian.M
author_rank: Super Contributor
timestamp: September 10, 2019, 01:40:42 am
content: The problem with resonators is their accuracy: At best the standard resonator will be several hundreds of ppm off from the nominal frequency, at worst it could easily be 0.1% out. It is also likely to drift horribly with varying temperature. A crystal will be a lot better: With the specified load capacitance, you can reasonably expect it to be within 20 to 30 ppm at 25 deg C room temperature. If you trim the caps you can ge:t a bit closer, but it still wont be particularly stable with respect to temperature. If the wrong caps are used with the crystal you could easily introduce another 50 ppm of frequency error. OTOH the TCXO above is specced to hold its frequency within +/-5 ppm over quite a wide temperature range. That's a worst case error of three seconds a week or to put it another way, if all your input signals are clean with sharp edges, you could build a five digit seconds and decimal fractions timer and trust the last digit.Replacing the resonator with a crystal is relatively high risk if all you've got to do the job is an iron and the usual hand tools. Track cut and patching to fit a TCXO is a *LOT*lower risk. OTOH, if you've got a well kitted out SMD rework station with a hot air pencil, and enough practice using it, and don't mind the lower accuracy result, by all means go for swapping the resonator for a crystal and swapping its caps for the right ones to match the crystal. The only problem will be finding a small enough footprint crystal.There's also a cheap and dirty 'no parts' (except fine magnet wire) oscillator improvement hack for genuine Arduinos that use a separate USB AVR for their host USB interface. Tack a fine modwire onto XTAL2 of the USB AVR, which has a real crystal, because of the tight frequency control reqired for USB, and onto the XTAL1 pin of the main MCU, also cutting the track from that pin from the resonator. However due to the long modwire it will probably 'pull' the crystal frequency slightly and will radiate more EMI so maybe not the best idea if you live next door to a radio ham with an interest in DXing..For the mod you really want good groundplane where you are going to put the TCXO, so you can tie its Gnd to board ground with a very short wire, and placed well away from heat sources like regulators and the MCU itself. The area under the G of the yellow GND you added to the image looks like the best choice. You've already identified the best place to pick up 3.3V, and 5V if you need it.That board layout is a bit different to the one I was working from for the instructions. I'd make the track cuts where they are still straight and parallel coming from the MCU XTAL pins just above where they bend 45 deg to the left towards the resonator and just below where the adjacent track bends to the right. That's where IMHO you've got the best chance of not nicking an adjacent track with the scalpel blade. Alternatively find the silk screened "C5" and "C6" of the resonator caps, and cut the tracks on a line between the right edges of those silkscreened markings. That gives you a wider spacing between them if you ever have to undo the mod, so less fiddly to bridge the cuts.When you are bypassing tiny little parts you need tiny little capacitors. If you can find a N.O.S 5V metal can DIL14 (corner pins only) 16MHz TCXO with glass frit hermetic seals on three of its four legs and the Gnd leg welded direct to the can, then you can use your disk ceramic 100nF cap reasonably safely, but if you used it with a SMD TCXO and it got bumped you''d be risking ripping the 'pin' pads off the SMD package. For the SMD TCXOs you need SMD caps just to get ones that will fit conveniently across the pins that need decoupling, and as the pin pitch shrinks you need to go increasingly towards the 'invisible fly sh-t' end of the SMD passives size range. When you are right down at the limit of what an expert with steady hands can deadbug without needing a micromanipulator, a through hole passive would have leads considerably larger diameter than the pin pitch so would be even more of a nightmare to use than the SMD fly sh-t. Its really not hard to put one in place, tombstoned or flat. Tin the pad its got to go on, (one only if mounting flat). get a trace of paste flux on the corresponding end of the part., place it with tweezers and reheat the tinned pad to make the joint. Then hold flat ones down with a toothpick or small flat jeweller's screwdriver while you solder the other end. Tombstoned ones are a bit trickier, but you've got the benefit of a lot of groundplane area at the bottom end, so if you are quick and have the top end wire or pin in exactly the right place you can solder it without remelting the bottom joint. If you dwell too long. it *WILL* remelt then stick to your bit (and the same if you slip while holding a flat one down). Don't try to retrieve small passives stuck on your bit - just curse eloquently, wipe them off, clean up one pad with braid if the SMD part needs to go flat, then get a fresh one off the reel or cut tape and try again. A good nights sleep and 24H without coffee or other high caffeine drinks helps most SMD novices considerably, as does obscenely bright easily positionable bench task lighting. If you need sunglasses because of the glare, its probably a little too bright!The first TCXO you found was a good convenient match to the pin order and spacing of the suggested 74LVC1T45 level shifter. so although its tiny they were a good pair. If you are trying to go direct to XTAL1 with a 3.3V signal level, it may or may not work. The MCU datasheet says 'don't count on it' but practical experience says the odds are in your favour. The larger TCXO will definitely be easier to deadbug. It has a control voltage input on 'pin' pad 1 for +/-5 PPM of frequency trim, which could be useful if you become a 'time-nut' but it says it can be left N/C so you can ignore it for now*. Its decoupling cap would probably be best fitted diagonally across 'pin' pads 2 and 4, again before attaching any wires. Don't be scared of working with tiny parts. Press the TCXO on some thin double sided sticky tape, and you can get the cap and wires attached to it working on your bench before you even touch the Arduino board, so if that doesn't go well all you are out is the cost of the TCXO and the 10nF cap. Once you've got the wires on and its looking like a crippled spider, you can pry it off the tape with the tip of your scalpel blade and transplant it to its final position on a drop of glue on the board.* Future Time nuttery could include ovenizing the TCXO in a little copper foil 'jacket' inside an expanded polystyrene block, itself with an aluminum foil 'jacket', with a small power transistor to heat it and a DS18S20 digital temperature sensor for the Arduino to read and control the oven temperature, a high precision DAC to control the pin 1 voltage to trim the TCXO, and a GPS module with one PPS output pulse so the Arduino has a long-term stable reference to trim the TCXO to. That's the basics of a budget GPSDO, and could easily let you get a reliable sixth digit of accuracy. Obviously you'd need to put all the gubbins on a proto shield and run a modwire down to the Arduino XTAL1 track. (Or if you are feeling really too clever, desolder the resonator and caps and solder a pogo pin to the underside of the protoshield so it lands on an XTAL1 pad!)

author: HendriXML
author_rank: Super Contributor
timestamp: September 10, 2019, 09:09:37 am
content: Please don't mention more accuracy using ovens.. I'm a bit sensitive for thatThen untreated TCXO will for now be just fine. I'll be using this timer mostly in conjunction with other measurements that are far less accurate. But it may be a nice project to execute just for fun in the future.About the level shifter I'm not sure yet, but I'll order the parts anyway. And already know how it works: once I have the oscillator running standalone (A good reason to have it a little bit bigger) when it's not new anymore, I will to follow the Atmel specs.I only once soldered a small SMD op amp dead bug style before, and looking through mounted binocularshttps://www.bestbinocularsreviews.com/Pentax-Papilio-85x21-Binoculars-118.htmI really enjoyed soldering the wires. Wouldn't want to create whole boards this way, but only a few components is satisfying.

author: HendriXML
author_rank: Super Contributor
timestamp: September 10, 2019, 09:28:53 am
content: About the bench lighting: my first project was a fume extractor led ring combo.I't basically a flower bucket with not bottom anymore mounted on a monitor stand and thus adjustable. There's a silent fan in it, that only goes to high speed once you pick up the soldering iron from it's stand.The bucked faces downwards and near the outer diameter at the level where the fan is mounted, there is a ring of 6 adjustable 3 watt LED's. They're a behind diffuser cloth, otherwise the individual leds make unpleasant bright spots on shiny surfaces of the work item. I really like the setup, but would go for more leds the next time.Quality light is important! And because of the ring of leds, there almost no shadows while working.

author: HendriXML
author_rank: Super Contributor
timestamp: September 10, 2019, 03:01:41 pm
content: I finished an example sketch that outputs stuff to the serial.I know it could do with less interrupts, because of them being in sync. (Sorry for that the codingstyle is a bit Pascal like)Code:#include <HardwareSerial.h>enum TCaptureState { csNone, csCapture4, csDiff, csErrorCapture5 };volatile uint64_t TimerCycles4A = 0;volatile uint64_t TimerCycles4B = 0;volatile uint64_t TimerCycles4O = 0;volatile uint64_t TimerCycles5A = 0;volatile uint64_t TimerCycles5B = 0;volatile uint64_t TimerCycles5O = 0;volatile uint64_t CaptureCount4;volatile uint64_t CaptureCountDiff;volatile uint16_t CaptureSequence = 0;volatile TCaptureState CaptureState = csNone;uint16_t PrevCaptureSequence = 0;TCaptureState PrevCaptureState = csNone;double MilliSecondsTickPeriod = 1000.0 / 16000000.0;const uint16_t HighCountA = 0xFFFF / 3;const uint16_t HighCountB = 0xFFFF / 3 * 2;ISR(TIMER4_COMPA_vect){TimerCycles4A++;}ISR(TIMER4_COMPB_vect){TimerCycles4B++;}ISR(TIMER4_OVF_vect){TimerCycles4O++;}ISR(TIMER4_CAPT_vect){CaptureSequence++;uint16_t CaptureCount = ICR4;CaptureState = csCapture4;// Using the cycle count that is linked to the captured counterif (CaptureCount < HighCountA)CaptureCount4 = (TimerCycles4B << 16) + CaptureCount;else if (CaptureCount < HighCountB)CaptureCount4 = (TimerCycles4O << 16) + CaptureCount;elseCaptureCount4 = ((TimerCycles4A - 1) << 16) + CaptureCount;}ISR(TIMER5_COMPA_vect){TimerCycles5A++;}ISR(TIMER5_COMPB_vect){TimerCycles5B++;}ISR(TIMER5_OVF_vect){TimerCycles5O++;}ISR(TIMER5_CAPT_vect){uint16_t CaptureCount = ICR5;uint64_t CaptureCount5;if (CaptureState != csCapture4){CaptureState = csErrorCapture5;return;}// Using the cycle count that is linked to the captured counterif (CaptureCount < HighCountA)CaptureCount5 = (TimerCycles5B << 16) + CaptureCount;else if (CaptureCount < HighCountB)CaptureCount5 = (TimerCycles5O << 16) + CaptureCount;elseCaptureCount5 = ((TimerCycles5A - 1) << 16) + CaptureCount;CaptureCountDiff = CaptureCount5 - CaptureCount4;CaptureState = csDiff;}// The setup() function runs once each time the micro-controller startsvoid setup(){Serial.begin(115200);Serial.println("Setup started");noInterrupts();// pinMode(48, INPUT);// pinMode(49, INPUT);// Halt timersGTCCR = (1 << TSM) | (1 << PSRASY) | (1 << PSRSYNC);// enable capture, compare a, compare b, and overflow interruptsTIMSK4 = (1 << ICIE4) | (1 << OCIE4A) | (1 << OCIE4B) | (1 << TOIE4);TIMSK5 = (1 << ICIE5) | (1 << OCIE5A) | (1 << OCIE5B) | (1 << TOIE5);TCCR4A = 0;TCCR5A = 0;// no noise cancelling, falling edge, no prescalingTCCR4B = (0 << ICNC4) | (0 << ICES4) | ((0 << CS42) | (0 << CS41) | (1 << CS40));TCCR5B = (0 << ICNC5) | (0 << ICES5) | ((0 << CS52) | (0 << CS51) | (1 << CS50));OCR4A = HighCountA;OCR5A = HighCountA;OCR4B = HighCountB;OCR5B = HighCountB;TCNT4 = 0;TCNT5 = 0;// just to be sure..TimerCycles4A = 0;TimerCycles4B = 0;TimerCycles4O = 0;TimerCycles5A = 0;TimerCycles5B = 0;TimerCycles5O = 0;// Continue timersGTCCR = 0;interrupts();Serial.println("Setup is ready");}// Add the main program code into the continuous loop() functionvoid loop(){// these must be constistentnoInterrupts();uint64_t LocCaptureCountDiff = CaptureCountDiff;uint16_t LocCaptureSequence = CaptureSequence;TCaptureState LocCaptureState = CaptureState;interrupts();if ((LocCaptureState != PrevCaptureState) || (LocCaptureSequence != PrevCaptureSequence)){switch (LocCaptureState){case csNone:{break;}case csCapture4:{Serial.println("Timer started");break;}case csDiff:{Serial.print("Timer stopped: ");double MilliSecs = LocCaptureCountDiff * MilliSecondsTickPeriod;double Speed = 150 / MilliSecs;Serial.print(MilliSecs, 14);Serial.println(" ms");Serial.print(Speed, 14);Serial.println(" m/s");Serial.print(Speed * 3.6, 14);Serial.println(" km/h");Serial.print((uint32_t)LocCaptureCountDiff);Serial.println(" ticks");Serial.print(MilliSecondsTickPeriod, 14);Serial.println(" msec/tick");break;}case csErrorCapture5:{Serial.println("Timer hasn't started!!");break;}default:Serial.println("??");break;}PrevCaptureState = LocCaptureState;PrevCaptureSequence = LocCaptureSequence;}}

author: HendriXML
author_rank: Super Contributor
timestamp: September 10, 2019, 03:04:07 pm
content: The output of 2 rapid nerf darts:Code:16:55:22.796 -> Timer started16:55:22.796 -> Timer stopped: 10.87300014495849 ms16:55:22.796 -> 13.79563999176025 m/s16:55:22.796 -> 49.66430282592773 km/h16:55:22.796 -> 173968 ticks16:55:22.796 -> 0.00006250000476 msec/tick16:55:23.245 -> Timer started16:55:23.278 -> Timer stopped: 9.80293750762939 ms16:55:23.278 -> 15.30153560638427 m/s16:55:23.278 -> 55.08552551269531 km/h16:55:23.278 -> 156847 ticks16:55:23.278 -> 0.00006250000476 msec/tick

author: HendriXML
author_rank: Super Contributor
timestamp: September 12, 2019, 02:28:13 pm
content: Found a good reference on changing fuse bits and stuff:https://youtu.be/93dSfEczqgk

author: HendriXML
author_rank: Super Contributor
timestamp: September 27, 2019, 10:37:40 am
content: Just to give an update. I found a smaller ATMega2560 board with a crystal resonator which I use for the measurements.https://www.eevblog.com/forum/projects/nerfo-meter-measuring-the-speed-of-nerf-darts/msg2711130/#msg2711130The higher precision timing will be used for something else or at least for experimenting.