Best way to measure period between pulses? Counters and FPGA

I had missed that in the original post. However, now I understand why the original poster doesn’t want to use “Method 1.” Given the statement of seeing RPM values with a resolution of 375 RPM and a hypothesized 20ms sampling period, their system must be using a wheel that has only 8 counts per revolution. In order to get greater RPM measurement resolution while keeping their 8 CPR sensor, they would need to have a longer sampling interval (longer than 20ms).

Similarly, with a 250 CPR sensor such as 1519 used in 2012, our computed RPM values are discretized at units of 12RPM. Measuring wheel speed to the nearest multiple of 12RPM was fine for our wheel shooter last year. Measuring to the nearest 375RPM might be troublesome.

Thanks, we will try utilizing this in some way because we are having similar problems

Joe,

How is this implemented under the hood in FPGA? Is there a ring buffer in the FPGA which the FPGA populates with the 6.525us_resolution_timestamp for each rising edge it detects (assuming it’s not in semi-period mode), and then when requested retrieves the elapsed time between the N+1 most recent samples in the ring (which the cRIO CPU then divides by N)?

Count will always be 1 if you specify not to average (NumberOfSamplesToAverage = 1).

How large can “NumberOfSamplesToAverage” be? I searched the 2012 C++ and the 2013 Java WPILib code but couldn’t find that search string.

EDIT:

@all:

I attached a small Excel app that computes the RPM jitter caused by the 6.525us timing resolution of the edge detections. There will be additional jitter due to manufacturing tolerances in the physical locations of the edges in the sensor, but I’ve not included those here.

Note how large the jitter is with a 360 PPR sensor at 5000 RPM with averaging set to 1. If you set the averaging to 120 (1/3 revolution) you can reduce the jitter dramatically, at the cost of some phase lag in the signal.

If you make a 1 PPR sensor with averaging set to 1 you’ll be able to get an updated reading every 12ms at 5000 RPM with very low jitter.

*

GetPeriod() RPM jitter02.xls (16.5 KB)


GetPeriod() RPM jitter02.xls (16.5 KB)

There is a buffer that will hold up to 127 samples for each counter / encoder. It has write pointer that does form a ring buffer. The sum of the samples between the write pointer - average_size (usually) and write pointer are summed every 0.1 us * average_size (yeah I know… overkill) and made available to the driver (i.e. not on demand) along with the number of samples available. When a counter is reset, the write pointer is moved to mark all samples unwritten (so the “count” returned would be 0).

The I/O is 6.525 us from sample to sample. The timer used has a 1 us resolution. So the actual resolution is 6…7 us. However, the error is not cumulative across the average. Regardless of the number of samples averaged, the error is less than 7.525 us.

The FPGA interface function is called writeTimerConfig_AverageSize(). Looks like we never exposed that to the WPILib API layer in C++ (and therefore Java). It’s exposed in LabVIEW.

Cheers,
-Joe

And I was sitting in this discussion wondering how nobody knows that you can configure the hardware averaging. It’s a register in the FPGA’s counter/encoder timer config register, exposed by the Set Timer functions, and is very useful.

Our 2012 robot (with a 4-line handmade aluminum and gaffers tape disc) averaged 12 samples (three whole rotations) to get a perfect signal with ~2.5rpm minimum step. If I set the graph to autosize I could see the controller oscillate between exactly one step above or below the target speed, when steady state.

What source code file would that be in? I cannot find it.

It’s not in the 2012 C++ WPILib source rev3111 timer.cpp

Encoder.cpp and Counter.cpp

OK, so we’re talking about modifying and re-compiling the WPILib source, not simply passing a parameter?

That is what I was saying. It is not in the WPILib API layer in C++ and Java. That means if you care to use the hardware feature, you need to move down a layer.

Cheers,
-Joe

Got it.

Thanks a TON Joe!

This post clarified pretty much everything we’re seeing.

We WERE hitting the 6.525us timing resolution per edge. That corresponded (coincidentally) with the resolution of 375 RPM we were seeing, at our desired speed.

There’ll be a more detailed post, or even a whitepaper once we’ve sorted everything out, but we’ve got a really neat solution together involving SetSemiPeriod(true), a mostly white disc with a single small black sector, and an averaging window that holds about 10ms worth of reads at our slowest speed.

May I ask? What is the slowest speed you want to control, and what speed are you running your speed control algorithm?

So, we are unable to use this with C++?

See post #29 in this thread:

Are you aware of any other features that are not implemented in C++ or Java but are in LabView (even outside of counters)?

I’d also appreciate any clarification on what you mean by moving down a layer. Other than switching to LabView, is there some other action that can be taken? I thought the FPGA code was handled by the cRIO imaging tool and no modifications to the FPGA programming are allowed. Is this your understanding as well? I am not sure if your comment implies more is available.

I think Java is missing SPI maybe?

In general we try to make everything the same.

The FPGA code is there. It does not need to be edited.

The layer that most people write their code is only in the “User Code” layer. The layer below that is the “WPILib” layer. That is the layer that I mean that you need to edit if you want this functionality in that language. You may even be able to call into the FPGA interface layer directly from your user code if you call it at a time that it won’t be overwritten by WPILib.

I’ve been filing bugs in the WPILIB Java tracker when I find something that is implemented in LabVIEW but not in Java. See http://firstforge.wpi.edu/sf/tracker/do/listArtifacts/projects.wpilib/tracker.wpilib_java_bugs

The things I’ve found are SPI, DMA, and CAN periodic status. Someone else filed a bug about digital input interrupts only being partially implemented. For SPI, I attached an implementation that works for me in the tracker.

Thanks for your clarification, jhersh. I understand what you mean now. The students do edit WPILIB also. However, this year the netbeans plugin makes this more difficult. The wpilib netbeans project directory and source code were placed in .zip files. I have not understood why this was done. I think we need to make the code more quickly accessible, and this repackaging does the opposite.

Joe, thanks for this input. I had actually stopped looking at this website since it seemed like the development work slowed down there and WPI had since created other websites. I do know that java wpilib has changed some since last year, but the underlying squawk java virtual machine is still timestamped December 2011 when the cRIO boots. There are other features missing as well, such as UDP networking support. This is actually highly misleading in the wpilib javadoc, as the documentation implies that the functionality exists. However, the code executes with a runtime exception. Frustrates the students to be this misleading. Some of the javadoc in general leaves a lot to be desired also.

I’ll happily work with the key java developers on the above.

The Support Page on screenstepslive says to file bugs in the firstforge tracker. There have been 7 java bugs closed since kickoff (4 documentation and 3 fairly simple bugs).

I figure that if there isn’t a bug filed, you can’t complain about it not being fixed.

Hey, kinda a thread hijack, but this seemed like the best place to ask. We’re using a photo-eye based encoder on our team (due to issues mounting traditional shaft encoders) and are having issues with GetPeriod() when we drop below 20 rps where it returns infinity about half of the time, and the correct speed the rest of the time. GetPeriod() apparently returns infinity only if the counter is “stalled”. Does anyone know what this means, or if there’s a way around it? Overriding GetPeriod doesn’t work, as m_counter is private, not protected. I’m guessing that the FPGA has some kind of threshold where if it doesn’t see an edge it assumes the wheel has stopped. Is there any way of modifying that, maybe giving it a large tolerance?