Does TyCommander handle FTDI? Or am I doing something wrong?

Disabling of interrupts for ~15 us is consistent with delay I have measured in a system with a high-priority timer interrupt. I did a lot of testing to show that as long as USB serial wasn't used, the delay in executing the timer ISR was close to 0, so I agree it's not okay to log to USB serial if your system cannot tolerate interrupts being disabled for those relatively short periods. Note that writing to flash on T4.x (including emulated EEPROM) also disables interrupts, and for much longer, so should not be done while a critical control loop is active.

This brings me back to a topic we've gone back and forth on several times, and that is QuadEncoder (no encoder interrupts) versus EncoderTool (interrupt on every edge of A and B). As a thought experiment, consider these alternatives for your lathe running at 400 rpm:
  • EncoderTool, with edge interrupt @ 27 kHz, ISR updates encoder count and does stepper micro-pulse per ELS ratio
  • QuadEncoder, with IntervalTimer @ 27 kHz, ISR reads encoder count and does stepper micro-pulse per ELS ratio
While not 100% identical in terms of timing, wouldn't these two systems be equivalent? I think the answer is yes, and then you would have the following additional benefits:
  • no possibility to lose encoder counts, even when interrupts are disabled
  • writing to USB serial would be okay as long as 15-us delay in stepper pulse is tolerable
  • integrated detection of encoder edge error
  • integrated support for encoder index pulse
If you need to support higher spindle speeds, the IntervalTimer interrupt frequency could be set higher. It would never have to be higher than the highest encoder edge frequency, and I think you would find that it only needs to be as high as the highest stepper micro-pulse frequency.
Explain your concept on QuadEncoder with IntervalTimer. How do these get phase locked together? What is the enforcement mechanism? If there is none, then it's guaranteed they will drift apart in a rotation or so. BTDT. The stepper motor position has to be phase locked to the angular position of the spindle. The spindle can vary in speed, either under load, or simply via the operator.

I think conceptually using QuadEncoder may be possible, but I don't understand how to use it in practice for this application. Maybe if I rewrote parts of it, essentially a fork, and added functionality. But right now, it seems awkward for the purpose, or I don't understand it enough. It seems I'd have to use the position compare interrupt repeatedly (and have to update it on the fly, to effectively do the Bresenham algorithm) and write lots of additional stuff - basically making my own callbacks. Which, sadly, I really don't understand all that well.

Perhaps if you had some ideas to share on how to effectively update the position compare interrupt simply? Basically I need to do the Bresenham algorithm to generate the timing of pulses for the stepper.

It would be a total rewrite of the core of my application, and could very well be required, but I wouldn't be looking forward to a rewrite. The core app has been running for three years, I've machined a lot of things using it.

Now it's possible that writing version 0.44 (yes, I've incremented by 0.01, since V0.01) won't take as long, but it's likely to take some serious debug effort.
 
Last edited:
Explain your concept on QuadEncoder with IntervalTimer. How do these get phase locked together? What is the enforcement mechanism? If there is none, then it's guaranteed they will drift apart in a rotation or so. BTDT. The stepper motor position has to be phase locked to the angular position of the spindle. The spindle can vary in speed, either under load, or simply via the operator.

The question is what you mean by "phase-locked". You need a stepper pulse every X edges of the spindle encoder. If the timer interrupt was the same frequency as the spindle encoder edges, the stepper pulses will occur at virtually the same time with both methods. I say virtually because the timer interrupts won't be synchronized with encoder edges, but no timer interrupt can be more than 36 us away from a given encoder edge. If you think that's too much, you could run the timer at 2x or 4x the encoder edge frequency, though my guess is you would not be able to see or even measure the difference in the resulting thread.

I think conceptually using QuadEncoder may be possible, but I don't understand how to use it in practice for this application. Maybe if I rewrote parts of it, essentially a fork, and added functionality. But right now, it seems awkward for the purpose, or I don't understand it enough. It seems I'd have to use the position compare interrupt repeatedly (and have to update it on the fly, to effectively do the Bresenham algorithm) and write lots of additional stuff - basically making my own callbacks. Which, sadly, I really don't understand all that well.

Again, this is conceptual, but we're talking about replacing your A and B interrupts with one timer interrupt at the combined frequency. All of the quadrature up/down count logic would be replaced by one call to QuadEncoder::read(). If the position has changed since the previous interrupt, then an edge has occurred, and you can do exactly what you are doing now on each encoder edge, i.e. update Bresenham and output to stepper as required. I don't think you would have to rewrite any of your application logic. Is there some reason that wouldn't work?

It would be a total rewrite of the core of my application, and might be required, but I wouldn't be looking forward to doing it. The core app has been running for three years, I've machined a lot of things using it. Now it's possible that writing version 0.44 (yes, I've incremented by 0.01, since V0.01) won't take as long, but it sure will take a long while to debug.

If it would be a total rewrite, then I have no idea what you're doing. Have you ever shared this code?
 
The question is what you mean by "phase-locked". You need a stepper pulse every X edges of the spindle encoder. If the timer interrupt was the same frequency as the spindle encoder edges, the stepper pulses will occur at virtually the same time with both methods. I say virtually because the timer interrupts won't be synchronized with encoder edges, but no timer interrupt can be more than 36 us away from a given encoder edge. If you think that's too much, you could run the timer at 2x or 4x the encoder edge frequency, though my guess is you would not be able to see or even measure the difference in the resulting thread.
How does the timer track the spindle? What if for some operation I want to use 150 RPM? Or I set to 150, but it's really 152, or 148? Or 2000 RPM for a fast feed? What if the motor speed wows or flutters? What keeps it in frequency lock (which is what you are proposing). That's sort of tracking the RPM. What prevents the timer events from walking by the spindle events (phase lock). Phase lock is when there is a fixed invariant relationship between the spindle position and the stepper position. I can turn the spindle slowly or quickly and the stepper moves to exactly in the correct place, as if the spindle and stepper were geared together. Having that exact "geared" relationship is required to cut threads. It's the whole point of an Electronic Lead Screw. Otherwise it's effectively open loop, with no corrections. ELS systems are closed loop.
Again, this is conceptual, but we're talking about replacing your A and B interrupts with one timer interrupt at the combined frequency. All of the quadrature up/down count logic would be replaced by one call to QuadEncoder::read(). If the position has changed since the previous interrupt, then an edge has occurred, and you can do exactly what you are doing now on each encoder edge, i.e. update Bresenham and output to stepper as required. I don't think you would have to rewrite any of your application logic. Is there some reason that wouldn't work?
The devil is always in the details. I know you are bending over backwards to help out, and it's greatly appreciated. There's 3500 LOC that has to work, so it's not always as easy as it looks from the outside. It's easy to break stuff, especially if one is not a skilled programmer, like me.
If it would be a total rewrite, then I have no idea what you're doing. Have you ever shared this code?
Think it would be a huge change in the core of what makes it work. That's not to say it's impossible. It may have to be done. At the moment, you can see I'm resisting. Primarily, because I haven't conceptualized that the proposed solution has a greater guarantee of success than the current one.

My code is not open source, (private repo on gitlab) at the moment. After I get this to work, I will revisit that. I don't want to release code with a special function (threading to stop module) that has a significant issue. (It doesn't work yet.) There's a lot that does work, but not threading to stop.
 
How does the timer track the spindle? What if for some operation I want to use 150 RPM? Or I set to 150, but it's really 152, or 148? Or 2000 RPM for a fast feed? What if the motor speed wows or flutters? What keeps it in frequency lock (which is what you are proposing). That's sort of tracking the RPM. What prevents the timer events from walking by the spindle events (phase lock).
QuadEncoder counts edges in hardware. When you call QuadEncoder::read(), you get the running count of encoder edges, so if you set the IntervalTimer to a frequency higher than the encoder edge frequency, the timer ISR can execute the same code you are executing now from your encoder edge ISR. Here's another way to look at it. Let's call your existing encoder ISR by the name code_to_run_when_count_changes(). Now, imagine a timer ISR that runs more often than encoder edges arrive. The ISR for that timer interrupt could look like this:

Code:
void timer_isr(void) {
  count = QuadEncoder::read();
  if (count != prev_count) {
    code_to_run_when_count_changes();  // all of your existing encoder ISR code
  }
}

Can you see that as long as you call this function faster then edges arrive, your lathe should do exactly what it's doing now?

Phase lock is when there is a fixed invariant relationship between the spindle position and the stepper position. I can turn the spindle slowly or quickly and the stepper moves to exactly in the correct place, as if the spindle and stepper were geared together. Having that exact "geared" relationship is required to cut threads. It's the whole point of an Electronic Lead Screw. Otherwise it's effectively open loop, with no corrections. ELS systems are closed loop.
See above.
The devil is always in the details. I know you are bending over backwards to help out, and it's greatly appreciated. There's 3500 LOC that has to work, so it's not always as easy as it looks from the outside. It's easy to break stuff, especially if one is not a skilled programmer, like me.
That worries me a little bit. Can your 3500 lines of code always execute in less than 36 us when running at 400 rpm?
Think it would be a huge change in the core of what makes it work.
I hope the answer above will convince you it's not a big change.
That's not to say it's impossible. It may have to be done. At the moment, you can see I'm resisting. Primarily, because I haven't conceptualized that the proposed solution has a greater guarantee of success than the current one.
The reason I'm suggesting is that I have the impression that your system can lose the spindle position, and if that's true, it's likely because your software is not counting correctly, and the easiest fix for that is to do the counting in hardware.
My code is not open source, (private repo on gitlab) at the moment. After I get this to work, I will revisit that. I don't want to release code with a special function (threading to stop module) that has a significant issue. (It doesn't work yet.) There's a lot that does work, but not threading to stop.
As you know from your time on the forum, unless we can see the code, we're all just guessing at what might be wrong.

If what I'm suggesting is more than you want to do now, that's fine. Logging to PSRAM might help you identify the issue.
 
Hmm, getting tough to respond.
Can you see that as long as you call this function faster then edges arrive, your lathe should do exactly what it's doing now?
I need to think about that. Haven't thought through the corner cases.
That worries me a little bit. Can your 3500 lines of code always execute in less than 36 us when running at 400 rpm?
The code (3500 LOC) obviously doesn't run that fast, nor does it need to. Just updating the Z display value takes 2ms...
The main loop can take from slightly less than 2ms to 50ms, depending on what's happening. All the important stuff takes place in the ISRs. Obviously, the encoder ISR has to be less than 30us. Main loop eventually gets done...
The reason I'm suggesting is that I have the impression that your system can lose the spindle position, and if that's true, it's likely because your software is not counting correctly, and the easiest fix for that is to do the counting in hardware.
Could very well be. I need to convince myself that count slippage actually is the problem - before ripping things up. I've gotten some good ideas here. Logging should help confirm or deny slippage.

Your point is well taken about showing code. I get it. Have some thinking to do - about what my next steps are.
 
I need to convince myself that count slippage actually is the problem - before ripping things up.
100% agree with this - time spent instrumenting your code is rarely wasted, in the long run.

Assuming that loss of interrupts is an issue, or even that it could be so in the future, I've come up with a couple of "straw man" code fragments. These are in no way real, just stripped back to the bare essentials to provoke discussion.

Here's the "current implementation":
C++:
// in library
void EncoderToolISR(void)
{
  updateCount();
  yourCallback();
}

// your code
EncoderTool yourEncoder;
int posFwd, posBack; // current step trigger positions

void yourCallback(void)
{
  if (yourEncoder.position() >= posFwd)
    stepFwd();
  if (yourEncoder.position() <= posBack)
    stepBack();

  // re-compute step trigger positions
  // here - Bresenham or whatever   
}

And here's what I understand @joepasquariello's proposed alternative would look like:
C++:
// in library
// QuadEncoder has no ISR,
// implemented in hardware

// your code
QuadEncoder yourEncoder;
int posFwd, posBack; // current step trigger positions

void yourCallback(int newPos)
{
  if (newPos >= posFwd)
    stepFwd();
  if (newPos <= posBack)
    stepBack();

  // re-compute step trigger positions
  // here - Bresenham or whatever   
}

// let's say you set this up for 1us
int lastPos;
void yourIntervalTimerFn(void)
{
  int newPos = yourEncoder.position(); // read once only!
  if (newPos != lastPos)
  {
    yourCallback(newPos);
    lastPos = newPos;
  }
}

For the sake of argument, let's suppose a spindle speed of 1000rpm, so its encoder interrupts are ocurring every 14.6µs; and that something might mask interrupts for any duration up to 15µs. For the IntervalTimer version, let's say it's triggered every 1µs - probably overkill, but it's only going to take significant time about every 14th trigger.

With the existing code, an un-blocked callback will be exactly in sync with the spindle; a delayed one may be late by up to ~14µs; and a lost one will be late by 15µs, and (crucially) have lost an encoder count.

With the IntervalTimer version, nominally un-blocked callbacks will actually be delayed by between 0 and 1µs, depending on the skew between the encoder and the timer; delayed ones will be as late by as much as the interrupts were masked for (they'll go pending, then execute when interrupts are un-masked); even a long delay will get serviced eventually, though the callback may find the encoder count has changed by more than one, and have to deal with it.

I know that steppers don't much like an irregular step interval, but have no idea if an occasional 15µs displacement will be imperceptible, sound more or less nasty, or cause loss of steps.
 
100% agree with this - time spent instrumenting your code is rarely wasted, in the long run.

Assuming that loss of interrupts is an issue, or even that it could be so in the future, I've come up with a couple of "straw man" code fragments. These are in no way real, just stripped back to the bare essentials to provoke discussion.

Here's the "current implementation":
C++:
// in library
void EncoderToolISR(void)
{
  updateCount();
  yourCallback();
}

// your code
EncoderTool yourEncoder;
int posFwd, posBack; // current step trigger positions

void yourCallback(void)
{
  if (yourEncoder.position() >= posFwd)
    stepFwd();
  if (yourEncoder.position() <= posBack)
    stepBack();

  // re-compute step trigger positions
  // here - Bresenham or whatever  
}

And here's what I understand @joepasquariello's proposed alternative would look like:
C++:
// in library
// QuadEncoder has no ISR,
// implemented in hardware

// your code
QuadEncoder yourEncoder;
int posFwd, posBack; // current step trigger positions

void yourCallback(int newPos)
{
  if (newPos >= posFwd)
    stepFwd();
  if (newPos <= posBack)
    stepBack();

  // re-compute step trigger positions
  // here - Bresenham or whatever  
}

// let's say you set this up for 1us
int lastPos;
void yourIntervalTimerFn(void)
{
  int newPos = yourEncoder.position(); // read once only!
  if (newPos != lastPos)
  {
    yourCallback(newPos);
    lastPos = newPos;
  }
}

For the sake of argument, let's suppose a spindle speed of 1000rpm, so its encoder interrupts are ocurring every 14.6µs; and that something might mask interrupts for any duration up to 15µs. For the IntervalTimer version, let's say it's triggered every 1µs - probably overkill, but it's only going to take significant time about every 14th trigger.

With the existing code, an un-blocked callback will be exactly in sync with the spindle; a delayed one may be late by up to ~14µs; and a lost one will be late by 15µs, and (crucially) have lost an encoder count.

With the IntervalTimer version, nominally un-blocked callbacks will actually be delayed by between 0 and 1µs, depending on the skew between the encoder and the timer; delayed ones will be as late by as much as the interrupts were masked for (they'll go pending, then execute when interrupts are un-masked); even a long delay will get serviced eventually, though the callback may find the encoder count has changed by more than one, and have to deal with it.

I know that steppers don't much like an irregular step interval, but have no idea if an occasional 15µs displacement will be imperceptible, sound more or less nasty, or cause loss of steps.
Thanks for the illustrative code. I'll mull it over.

Steppers in motion have inertia, so there's some smoothing, but they don't really like pwm style control such as a train of pulses then blanking some and restarting the pulse train. I tried that while testing coming to a faster stop. It was noisy and simply less effective (final position error) than exponentially increasing the period between steps.

I think what I've concocted is a position control loop, with angular position as input and z as the output. It's my belief and engineering assessment that this is the only good solution for the application requirements. But the methodology to implement is whatever works. So thanks for your ideas, they're truly insightful.

Been hard to stay on task for this, life is full of interuptions. Logging to PSRAM is new to me, so I need to play with that a bit. I also need to take a hard look at my encoder ISR, suspect it's getting too fat. Have a bit of work ahead of me.

What makes this hard to debug is the behavior of the lathe is different than my desktop simulator. My simulator can weed out the simple stuff, but isn't faithful to the real lathe. But that's typical, so I have to deal with it. Putting it bluntly, the debug physical environment is not as comfortable as an office space.
 
Fully sympathise with your current position - there's clearly a lot to explore, and no clear best first direction. Plus life, yes ...

I think I'd first be inclined to try to get accurate data somehow. As you implied in post #130, without this you're really just thrashing about and possibly "fixing" things that actually work. Also, it would help decide if working on improvements to your simulator would be useful.

You're right, steppers do not cope well with missing pulses - "PWM style" is definitely wrong! All the required pulses absolutely need to be present and at least close to the right positions to get the ELS you're after. That would be another avenue to explore, of course ... how far off can the pulses be before something irrecoverable happens? But to find that out, you still need the logging.
 
Went on a HW upgrade party today. Located some spare PSRAM and installed it on my spare ELS controller, and on my spare UART repeater board. Now have an equivalent setup on my desk, Teensy wise. Comms is functional. Integrated the PSRAM logging for my syncing issue, well at least the angle stuff. Just put in a check for the index pulse input during the syncing interval. Over the course of a couple of thread starts, I may be able to detect if the index has drifted. Pushed most of the logging messages to HWSerial.

Tested PSRAM logging on the sample program, (on the ELS controller) which seems to work. Now have to orchestrate this mess of hardware and see what happens. Hopefully it's insightful, rather than confusing!
 
First trial didn't go so well. I have 3 Teensy's running. Kind of like a 3 ring circus! The main ELS controller, the UART box, and a driver for the spindle simulator. I went for the whole enchilada and things more or less gummed up. Display very slowly updated, dump log never did anything, a few other issues.:) Next is to take out the encoder ISR index pulse check, and see if I can log the syncing process. I will slow down the spindle, just to make things easier for that poor controller.
 
:cry:
It’s always worth trying … and works out on depressingly few occasions. Still, at least you have the elements in place, and can work towards the enchilada gradually - first the tortilla, then add beans, then … darn, I’m getting hungry :rolleyes:

Good luck
 
Much better now. The angle stuff counting to zero, ie sync looks ok. Was in the while loop spooling to PSRAM too fast, fixed that. Had a second bug where I was looking for index=1, when due to inversion, I should have been looking for index=0. Ran the test at 200 RPM.

See an issue. Index pulse seems to be 4 counts wide. That's ok, I suppose. Haven't made an attempt at setting my count=0 at the rise of the index. But I get the following counts, at least during the two threading passes (before and after):

Code:
IDX = 0, Count = -2942
IDX = 0, Count = -2943
IDX = 0, Count = -2944
IDX = 0, Count = -2945

But at the very beginning of the dump, I see:
Code:
Dumping Log!!!
IDX = 0, Count = -2946
IDX = 0, Count = -2947
IDX = 0, Count = -2948
IDX = 0, Count = -2949
IDX = 0, Count = -2946
IDX = 0, Count = -2947
IDX = 0, Count = -2948
IDX = 0, Count = -2949
IDX = 0, Count = -2946
IDX = 0, Count = -2947
IDX = 0, Count = -2948
IDX = 0, Count = -2942
IDX = 0, Count = -2943
IDX = 0, Count = -2944
IDX = 0, Count = -2945

Seems there are two or three cycles with different counts, then things settle down to the 42, 43, 44, 45. Clearly something is off. If the count is drifting relative to the index, something is NOT right.

But it is progress!

ELS controller writes to PSRAM.
Dump to HWSerial after experiment is done. (3M baud)
2nd Teensy is converting HWSerial to USB and logging in TyTerminal.
 
Getting 4 counts is expected, according to the data sheet. That's good. Encoder drift, that isn't good. Still haven't proven that it's happening or not. The two threading passes did start at exactly the same angle and they had the same index count.

At some point, I have to log the angle at point of first contact with the work piece. That's actually the key to the process. If the carriage speed is at the correct ratio to that of the spindle AND the angle at first contact is the same every pass, the thread will be correctly cut.
 
Think the 4-edge-wide index pulse is standard - the encoder data sheet will tell you. Interesting that it’s only 3 edges wide just before it jumps (no -2949 count).

Looks like progress, anyway.
 
8 successive trials, no index slip. 4 at 0.08 degree sync angle, 4 at 180.08 degrees. No slip of the index pulse.

Slight bug revealed in the while statement. Sometimes I sync one count early. Compared to the errors I've been chasing.
Code:
float old_angle = 0.0f;
while (fabs(compute_error(spindle_relative_angle, syncangle)) >= dtheta/2.0f) {
  // write to PSRAM
  // write index value & angle & count
  if (write_data && dataFile) {
     if (spindle_relative_angle != old_angle) {
       dataFile.print(spindle_relative_angle, 6); dataFile.print(", ");
       dataFile.print(syncangle, 6); dataFile.print(", ");
       dataFile.print(digitalReadFast(IDX)); dataFile.print(", ");
       dataFile.println( fabs(compute_error(spindle_relative_angle, syncangle)), 6);
     }
     old_angle = spindle_relative_angle;
  }
}
So changed the inequality to > dtheta/4.0f. Not sure it will really help. Really want to exit the loop when the compute_error == 0.0f, but that's tricky with floats.
 
Following along. I don't remember if you ever told us what was the value of dtheta?

Maybe doesn't matter, but should you initialize old_angle to a value that can't occur, such as 9999999.0f, or is that already true of 0.0f?
 
Following along. I don't remember if you ever told us what was the value of dtheta?

Maybe doesn't matter, but should you initialize old_angle to a value that can't occur, such as 9999999.0f, or is that already true of 0.0f?
dtheta = 360/4096 = 0.087890625 degrees

Might be a good idea to initialize old_angle. (Good practice and all.) The value gets updated pretty fast to the right value though. 1/( 400(rpm)/60 *4096) = 36.6us.

Beginning to see evidence of count slippage. (Relative to the index.) Not good. Will have to log some more before I really believe it. Sometimes it may slip, other times not. Borderline execution perhaps. But whatever reason, count slippage is a problem.
 
Can you wire both QuadEncoder and EncoderTool pins at the same time, and log them for comparison? QuadEncoder should never lose counts, being in hardware.
 
dtheta = 360/4096 = 0.087890625 degrees

dtheta = 360/4096 is the arc (deg) for 1 encoder count, right? In that case, position error will always be >= dtheta (or dtheta/2 or dtheta/4) except when the error is exactly 0, right? In other words, if your while condition was in units of encoder counts instead of degrees, it would be:

Code:
while (abs(error_in_counts) > 0) {
  ...
}

Beginning to see evidence of count slippage. (Relative to the index.) Not good. Will have to log some more before I really believe it. Sometimes it may slip, other times not. Borderline execution perhaps. But whatever reason, count slippage is a problem.

It seems like there are only 2 ways that count can slip. One is that the edge is not triggering an interrupt (hardware issue), and the other is if the software can't keep up. Are there any other possibilities? Do you know the worst-case execution time of your A and B signal ISRs?
 
Can you wire both QuadEncoder and EncoderTool pins at the same time, and log them for comparison? QuadEncoder should never lose counts, being in hardware.
I'll have to find pins for it. QE has pin limitations, not sure about EncoderTool. I'm currently wired (via the PCB) to A = pin 0, B = pin 2, index = 4.
 
dtheta = 360/4096 is the arc (deg) for 1 encoder count, right? In that case, position error will always be >= dtheta (or dtheta/2 or dtheta/4) except when the error is exactly 0, right? In other words, if your while condition was in units of encoder counts instead of degrees, it would be:

Code:
while (abs(error_in_counts) > 0) {
  ...
}



It seems like there are only 2 ways that count can slip. One is that the edge is not triggering an interrupt (hardware issue), and the other is if the software can't keep up. Are there any other possibilities? Do you know the worst-case execution time of your A and B signal ISRs?
No, I don't know the WC time at the moment. I looked at it long ago, but there's more stuff now. Need to plow through this again. Haven't been home all day, just getting caught up.

As for other possibilities, I'd need to check, but don't think it's that likely.
 
Can you wire both QuadEncoder and EncoderTool pins at the same time, and log them for comparison? QuadEncoder should never lose counts, being in hardware.

That could work, though might not be (easily) doable with his existing hardware. Rather than use QuadEncoder and EncoderTool, use QuadEncoder to do the counting and also connect A and B to two additional pins configured for interrupts on both rising and falling edges. There could be one function to handle all 4 edges, which would call QuadEncoder::read() to get current count, then execute the same logic as is executed in the EncoderTool interrupt handlers. If the change in position was ever anything except +1 or -1, you'd know something had gone wrong.
 
EncoderTool already uses pins configured to interrupt on change, and @clinker8 is using the callback to run his ELS. All I'm hoping is that a couple of the allowed QuadEncoder pins are unused, or at least can be re-purposed temporarily, so that the existing callback code can read the QuadEncoder value and log it. We want minimal hardware and code changes required to corroborate the possible lost edges at this point.

@clinker8, are you using the EncoderTool error callback? It looks as if that triggers if a single edge interrupt has been missed, resulting in an apparent simultaneous change on both A and B signals. I could be reading the code wrong, though.
 
Just re-read post #145; QuadEncoder can use A=0, B=2, so I think you could temporarily divert your EncoderTool to any two spare pins (IIRC all GPIOs can interrupt), add a couple of wires and the relevant code, and you're good to go.

Simply...
 
Back
Top