Congratulations, looks cool indeed. Here a few remarks:
...
74HC165's @ +5v and 470R/910R voltage divider between SERA and SERB and respective T3.2 pins.
The T3.2 is 5V tolerant by design. So, the voltage divider to get the 5V input down to 2.6V is not really needed. (But won't hurt of course).
Using the 74165 example, all 16 encoders work beautifully
Glad to hear that. I never tried more than 8.
I like the way the library deals with setLimits etc. and currently experimenting with 0-127 in most cases for Midi purposes.
I added range limiting (hard and cyclic) after I realized that this is surprisingly complex if a user only has the counter values. It is trivial however from within the library where you have the count up / down events directly.
Experimented with 2R/1C debounce (Bourns) circuit which was counterproductive as the HC165 thresholds did not like it, so reduced the bottom R which got it working but made absolutely no difference to readings. No debounce was addded.
Generally, there is absolutely no need for debounce electronics for the
half / and quarter count modes. The implemented state machines can always distinguish between a bounce pulse and a count pulse.
Here the state machine I use for the quarter countMode. The circles are the states, arrows denote state transitions, the numbers are the A/B signals. State A(0,0) is the state at the mechanical detent.
Bouncing can only happen between two neighboring states (only one switch is operated at a time in quadrature encoders). As you can see, the count up/down events (val++/val--) have no chance to be triggered by bouncing. You 'pay' for that by having only a quarter of the possible counts per revolution. But this usually is no problem since this corresponds to the mechanical detents. Actually, they don't place the detents at every possible edge of the encoder to allow such algorithms to eliminate bouncing without additional (expensive) parts.
When I find some time I'll add an explanation of the used algorithms to the library documentation.
Looked into the Callbacks example and all 16 encoders work there. Interestingly, (CountMode::full) and setLimits(0,16), when you tweak an encoder past a limit, jumpy values are still emitted. I sniffed in delay.h and fiddled which seemed to subdue some jumpiness even with silly numbers.
I'm afraid, this is to be expected. In 'full' count mode the algorithm counts each edge of the quadrature signal. This is usually done for bounce free optical/mechanical encoders only. Also, it wouldn't make sense for mechanical encoders with detents since at each detent you'd get increments of 4 (or 2 for x2 encoders).
In full mode the quadrature algorithm will ensure that after the bouncing period the counter will be at the right position but while the switches bounce the counter will go up/down on each and every bounce. The polling algorithm will reduce the pain a bit since it doesn't see all edges. Interrupt based algorithms can give you hundreds of up/down events during bouncing. For setpoint applications this is normally not a big deal, for menu selection applications you might get weird effects.
If you limit the count range you normally get no events/callbacks if the encoder is turned above the limit. But since you are using full mode the counter will see rapid up/down events during bouncing and will trigger corresponding callbacks. You can fix this by using quarter or half count mode but this will reduce your resolution by 4 or 2 respectively. If this is not acceptable you might try your hardware debouncing again. In this case it might actually improve the behaviour.
Please also note that you can choose the count mode per encoder. So you could use the quarter mode for encoders where necessary and use the full mode for the others. Here a quick example showing this. Setting encoder 4 to half eliminates the spurious callbacks at the limits.
Code:
#include "Arduino.h"
#include "EncoderTool.h"
using namespace EncoderTool;
constexpr unsigned encoderCount = 8; // number of attached (daisy chain shift registers for more than 8)
constexpr unsigned QH_A = 0; //output pin QH of shift register B
constexpr unsigned QH_B = 1; //output pin QH of shift register A
constexpr unsigned pinLD = 3; //load pin for all shift registers)
constexpr unsigned pinCLK = 4; //clock pin for all shift registers
//74165 datasheet: http://www.ti.com/product/SN74HC165
EncPlex74165 encoders(encoderCount, pinLD, pinCLK, QH_A, QH_B);
void setup()
{
encoders.begin([](int i, int v, int) { Serial.printf("%d: %d\n", i, v); }, CountMode::full);
encoders[4].setCountMode(CountMode::half);
encoders[4].setLimits(0, 16);
}
void loop()
{
encoders.tick();
}
Am aware that the 74HCT245 will introduce some propagation delay but used it as am thinking of playing with at least another 16 encoders (not on this build) and maybe double that again so am figuring some help to drive CLK and LD might be a good idea.
I can't imagine that the small propagation delay of the '245 will do any harm. However, the whole thing is limited by the duration of the readout of all encoders. If we assume that reading out will take some 5µs per encoder you'll end up with a time of 160µs for 32 encoders. Thus, if the Teensy does nothing else than reading out the encoders it can only do that a frequency of 1/160µs = 6.3kHz which might get borderline for your encoders. However, the readout algorithm is not really optimized at the moment (using delayMicroseconds(1) for wait times where 150ns should be sufficient). Optimizing this should get you some additional headroom. A T4 might be a better solution then (and since you have your voltage dividers you should be able to replace it without hardware change...)