smartpin questions (was: how to capture time on pin change)
ManAtWork
Posts: 2,174
Let's say I want to count position of a step/dir signal pair. That's easy, smart pin mode %01101 does "accumulate A-input positive edges with B-input supplying increment (B=1) or decrement (B=0)".
For a servo it would be useful to also measure velocity. The control loop samples the position counter at a rate of serveral 1000 times a second. At low speeds the velocity (position delta) dithers between 0 and 1 for positive or 0 and -1 for negative velocity. This is very coarse and results in a stepper motor like noise.
If I additionally recorded a time-stamp of the last step signal edge I could calculate velocity as (delta position)/(delta time) which gives a very high resolution even for speeds below 1 step per loop period.
I haven't found a smart pin mode for capturing (absolute) time. There are modes for measuring time between edges but I think they are not ideal for this purpose because I don't know in advance how many edges are to be counted/measured in the next period. Maybe the best method would be to trigger an interrupt on each positive edge of the step signal and record the current time with a GETCT instruction. Does anybody have a better idea?
Maximum step frequency is 1MHz or a bit higher. If the interrupt needs only one single instruction it won't load the cog too much. An incremental encoder works the same way just with a quadrature signal instead of a step/dir pair (mode %01011 instead of %01101). Input filtering can be used to avoid interrupt overload caused by a high frequency oscillation when the optical sensor of the encoder is close to the edge of a codewheel line. For capturing time-stamps of a quadrature signal change the interrupt has to be triggered on both edges of both A and B inputs.
For a servo it would be useful to also measure velocity. The control loop samples the position counter at a rate of serveral 1000 times a second. At low speeds the velocity (position delta) dithers between 0 and 1 for positive or 0 and -1 for negative velocity. This is very coarse and results in a stepper motor like noise.
If I additionally recorded a time-stamp of the last step signal edge I could calculate velocity as (delta position)/(delta time) which gives a very high resolution even for speeds below 1 step per loop period.
I haven't found a smart pin mode for capturing (absolute) time. There are modes for measuring time between edges but I think they are not ideal for this purpose because I don't know in advance how many edges are to be counted/measured in the next period. Maybe the best method would be to trigger an interrupt on each positive edge of the step signal and record the current time with a GETCT instruction. Does anybody have a better idea?
Maximum step frequency is 1MHz or a bit higher. If the interrupt needs only one single instruction it won't load the cog too much. An incremental encoder works the same way just with a quadrature signal instead of a step/dir pair (mode %01011 instead of %01101). Input filtering can be used to avoid interrupt overload caused by a high frequency oscillation when the optical sensor of the encoder is close to the edge of a codewheel line. For capturing time-stamps of a quadrature signal change the interrupt has to be triggered on both edges of both A and B inputs.
Comments
The problem is that I don't have a stable, repeating input frequency. The encoder position or step/dir command can stop at any moment or even reverse direction in the middle of a measurement period. Er, period is probably the wrong word. The P2 docs use "period" for the time between A/B input edges.
If the current period doesn't complete (step sequence stops) then no result is stored if I understand correctly. I'll draw a timing diagram to illustrate it...
For the fourth sample velocity is (2-1)/(336_789-243567)=1.073 counts/ms.
If the step pulses stop after the fourth sample then the fifth sample would give dp and dt both zero. Division by zero is undefined but as no pulses arrived velocity can be assumed to be zero.
If you are sure you can process each step one by one then you can measure just the duration of each step - mode %10000_0. But most likely you'll be wanting to also smoothly handle multiple steps per update, in that case, as Chip indicated, you'll be wanting to know both the step count and the duration over which they occurred. This requires two smartpins to give the two measurements - modes %10111_0 and %10101_0 respectively.
P2 can capture sysclks on either/both edges, and can do that for single, or over many (X) edges.
The issue may be around your wide dynamic range :
Single edges would work fine for lowest speeds (let's say to ~10kHz / 100us report rates), and you could run another pin cell in parallel, that captures every X events.
That second cell will be an average, and will not precisely catch a stall, but would be used for > 10kHz ( eg X = 1000, would read up to 10MHz at 100us report rate )
If you know the appx step rates at any ms, you could vary X to dynamically scale.
Addit: P2 will capture-and-auto-clear, so it will always give an edge-to-(Xth)edge result.
I'm rusty on how P2 manages overflows : ie if a) the first reading is given to users (later ones ignored), or if b) the most recent reading is given ?
If you poll it often enough, velocity will not change much
Low pass filters have a disadvantage: phase delay. Delays are critical in a motion control application. Delays in the feedback loop limit the gain or can cause overshot. Delays in the command path are no problem as long as they are constant. Variable delay distorts the toolpath trajectory and has to be avoided. My solution has the advantage that delta time is always as close to the sampling period as possible. So phase delay is not perfectly constant but as close as it could.
Of course, if there are no or very few pulses you have no information about velocity until the next edge arrives. Maybe the best solution is to caclulate a lower and upper limit based on the information available and make a guess that's somewhere in between when there is no exact value (last position info is outdated).
So the interval measurement modes of the smart pins are not really necessary. I could simply wait for a counter-change event of the quadrature or step/dir decoder and capture time with a GETCT. As the P2 supports waiting with timeout I could handle the stall case conveniently. If there were no pulses during the timeout duration T then I know that velocity must be less than 1/T. So I can adjust the measured velocity to the "best guess" even when no pulses/edges arrive.
High performance resolvers that emulate an encoder, maybe at the drive, have to use interpolation at the very least. And probably extrapolation too. The resolver frequency is only in the kHz whereas encoders have to be good for low MHz at least. In both cases, of interpolation and extrapolation, they should be using filtering techniques to make the emulation more accurate.
Yes, that should work and if there were no other possibility I'd have to do it that way. However, I'd like to avoid dynamical adaption if possible. You had to handle special cases as direction changes very carefully. Case-switching or dynamical parameter adaption could cause clicks and jerks or non-linear behaviour if not carefully designed and debugged.
And yes, my proposal also involves some case-switching but I think it's easy to prove that the "conditional corrections" applied always make the measurement better and not worse.
Ah thanks. This is a bit more understandable as the original documentation. I still don't understand every detail. But that's the good thing about the propeller. On other processors it's "eat or die". With the propeller you always have the choice. Peripheral hardware can help to make some things more efficiently but your're not bound to use it. You could always do it in software (bitbang) if the hardware doesn't fit.
Suddenly, I had random crashes depending on the number of instructions I inserted or removed. I took a look at the p2asm listing and found out that the compiler took me literally and put the actual address of the cntLoop label as immediate parameter instead of what I expected: #(cntLoop-PC)
I replaced the jmprel with a jmp and it all works, now.
Next problem: waitse1 doesn't work, it waits forever. To check if my setse1 is wrong I replaced waitse1 with a testp...wz + if_nz jmp loop. It also waits forever. So I think the problem is that the IN register bit does not become high when the counter is updated.
In the smartpin docs I read: "The IN bit serves as a flag to indicate to the cog(s) that the smart pin has completed some function or an event has occurred, and acknowledgment is perhaps needed." In lack of detailed information I assumed that the IN bit gets set automatically for the mode "%01011 = A/B-input quadrature encoder". Probably this is not the case. Do I have to set something in the lowlevel pin mode bits so that the event-flag is routed to IN?
If you want to wait for a encoder change, you can always do something like that:
In encoder mode the lowlevel pin mode is useful to set a pullup, or a schmitt-triger input, or to enable the glitch filters.
Andy
EDIT: If you're using another timer mechanism then setting X=0 and polling works well.
BTW... from what I understand how the glitch filters work they also have a negative effect on timing resolution. A counter tap feeds the clock input of the flipflops so they get updated only every 2^n sysclks. For example, if I select filt1 the default is 1:32 tap and 3 flipflops. So instead of a constant delay of 96 clocks the signal is only updated every 32nd clock cutting resolution down to 1/32.
Of course, the way the filters are implemented now is cheaper regarding silicon area. A shift register with hundreds of flipflops would be too expensive so it had to be implemented with a loadable counter which counts down while the input stays the same. This would still require at least a 16 bit counter for each pin.
I think I quit the whole smartpin stuff and return to manually decoding the quadrature signal as I did with the P1. Apart from reducing code size a bit the smartpins have only disadvantges in this case. Waiting for a pin change (what is the P2 instruction for waitpne?) gives full resolution to one sysclk. The extra time the decoding in software takes even has the side-effect of adding a dead-time in which further pin changes are ignored. This has the same effect as a hardware glitch filter.
PS: The deglitch "filter" hardware is intended for mechanical switches, namely push-buttons. Don't use it for real filtering.
> This has the same nearly the same effect as a hardware glitch filter.
The dead-time prevents the counter from changing faster than the dead-time but a glitch is not fully supressed. We had to check if the pin states are still the same after the dead-time and undo the counter update if not.
Yes, set the FFF field for wrpin to %011 and no smartpin (mode = %00000)
@evanh Thanks for the help.
Hello,
I am attempting to get the gated positive edge counter to work referenced in @JonTitus wrote on pages 24 and 25, see below. Attempting to use the edge counter to see a signal from the x_band sensor.
Did you ever get the 01011 to work?
I am uploading my code and the manual.
Thanks in advance.
Martin
The examples in the X-BandMotion Detector PDF just counts the frequency from the OUT pin.
In Spin2 you can do something like that:
Andy
I demonstrated how to do this in a different thread.
Once the smart pin is started you can clear its internal count by writing the smart pin DIR bit to 0; in Spin2 I do this with pinfloat(). To re-enable the smart pin use pinlow().
Since you mentioned the X-Band motion detector, I converted the P1 object I wrote in July to the P2; it's even simpler with the smart pin. See attached object.
Jon Titus wrote that document very early in the release of the P2 and things have come a long way, especially constant definitions for the P2 (see the online Spin2 docs) which make code easier to read and understand. For example, if you wanted to start the edge counter mode in PASM2, you could do this
These instructions were lifted from the Spin2 interpreters source code for pinstart() -- the values are set specifically for the edge counting feature you want.
@Ariba I found the p count rises command interesting.
@JonnyMac Can you give me the link to the thread that demos the p count rises?
Thank you both. I will experiment with both techniques.
On the pasm side, I am attempting to get a good handle on the asm type of coding.
On the spin side I am attempting to get a good handle on calling asm from spin as well as improve
my spin coding.
Martin
It's YOUR thread!
-- https://forums.parallax.com/discussion/176014/starting-smart-pins-from-p2spin#latest
The P2 Spin interpreter is much better than the P1 Spin interpreter. My casual testing shows that code runs about 16x faster on the P2 at the same clock speed on both platforms (I tested at 80MHz). My standard P2 code runs at 200MHz, so that's about a 40x speed boost of identical code moving from P1 to P2. Since you're still new to the P2, save the assembly stuff for later.
With smart pins and inline PASM2 you often don't need to use a separate cog for some processes. For example in a laser tag game I'm writing I need to update 45 WS2812b pixels. To do this takes less than 1.5ms so there's no need to devote a cog to the process when I can use inline assembly to handle the signaling details. I've attached that object in case you want to look at it later. Fair warning: it is advanced and only runs in interpreted Spin2; it will not work in FlexProp (which has its own mechanism for inline assembly).
@JonnyMac
Don't get old it is a pia can't remember my own name in any of the languages I speak.
I am reading the ws2812b_nc code. Thanks for the good comments in the code. Makes it
easier to follow than other folks code without comments.