Because of the way SPI works out, it ought to be possible to replace the 16-bit multiplexer with a pair of 8-bit serial-in, parallel-out (SIPO) shift registers (the same kind they use for driving multi-character LCD displays and LED chains, typically 74HC595.)
The idea is to wire the outputs of the SIPO shift registers, each to the enable pin of one of the 16 PISO shift registers. The SCK (SPI master clock) signal goes to all 16 PISO chips at once but only the enabled one responds on the MISO (Master In Slave Out) line. When woken up by the enable pin it first samples its input pins and saves their values as its contents, then it cycles through its contents sending a single bit corresponding to an encoder pin for each SCK pulse it receives.
A variation would be to daisy chain the PISO registers in banks of 3 with their enable pins tied together. This would ensure that 8 encoders could be assigned to each bank with no left-over pins. To read a single bank would require 3 SPI read cycles. At the bank control level (the SIPO registers) we could control up to 8 banks from a single SIPO. 64 encoders maximum in banks of 8 seems like a reasonable target but if the microcontroller is powerful enough it could accommodate more banks simply by daisy chaining at the bank control level.
I’m ignoring any possible fan-out limitations and buffering requirements for now (as well as timing issues.)
PS, to clarify, the bank selection logic must be primed so that it selects a single bank, then each time it is shifted it deselects the current bank and selects the next one, with automatic rotation to the beginning after it reaches the final bank. Depending on how the enable logic of the PISO shift registers operates, the initial value would be either 00000001 or 11111110 binary. In this manner we can sample 64 EC11 encoders and their push buttons using 24 PISO shift registers and 1 SIPO shift register, at a rate of approximately 24 SPI byte transfers per sweep.
This is where I do some back of envelope arithmetic to decide whether this is feasible. A sanity check on the design.
At 16MHz an Arduino can handle a theoretical SPI transfer rate of 800kB/S. The handshake conditions and the capabilities of the shift registers may severely limit this capacity, but I think it demonstrates that synchronous serial data transfer is not going to be a bottleneck.
Say you have a manually operated encoder with a maximum output of 50 state transitions per second. You need 100Hz minimum scanning rate to capture these transitions faithfully. If 8 manually operated encoders are scanned using 3 PISO registers operating in parallel on their three output pins you need about 800Hz minimum scanning rate so that no state transitions in any of the encoders are missed.
Similarly if you’re operating 64 of these encoders in 8 banks you need 6400Hz minimum scanning rate. This rate is well within the capabilities of the shift registers available on the market, and also well below the capability of the SPI interfaces available on Arduino and similar low power microcontrollers.
Edit: I think I made a mistake in the above calculation. If the PISO registers operate in parallel this doesn’t increase the scanning rate requirements. So the minimum scanning rate per bank remains 100Hz and the minimum scanning rate for 8 banks scanned round-robin style is a paltry 800Hz.
You still have to debounce these nasty beasts…
That’s a tenfold (on the back of my envelope…) increase in scanning rate.
Still way below maximum.
But then you’ll have to process all these data to extract something meaningful out of the noise.
That’s where you may reach the limits of an arduino, at least in C with the arduino library.
Such low level bit-twiddling is best done in assembly.
But who knows, the arduino may just have the needed horsepower !
But you’re right, the noisiness does make the minimum scan rate considerably higher than the naive assumption I adopt here. The data rate may be 50Hz or thereabouts but that signal is embedded in masses of higher frequency noise.
I did initially want to write my code in AVR assembly but it may be more friendly to publish a C version. I also wanted to use an ATtiny85 but it’s not quite powerful enough for MIDI on top of all this. I’ll try to go with a Nano which most posters here are reasonably familiar with because it’s used in some of the official Kosmo modules. If that isn’t enough I’ll look at a Teensy or an ESP32, but from experience I think the Nano will be more than enough.
This sounds very interesting! I have kind of given up on my two encoders on a single arduino nano, because it would always miss some steps… I used this library which also uses the state table. I used a single interrupt pin for each of the encoders (nano only has 2 interrupt pins). I think it worked better for some time, but when I added midi out and moved from breadboard to strip board it started missing steps and I could not find out what was going on… maybe I need to have a look at this again. Maybe using shift registers and SPI could improve this, but probably it’s somthing different, like the MIDI messages block interrupts or something like this…
It does sound like MIDI may be overloading your design, and that has implications for my design. Adding shift registers wouldn’t improve the configuration you describe in any meaningful way, though you might find success from switching from interrupts to polling instead. How did you implement MIDI?
Thanks. I’m not that familiar with Timer1 but it looks as if you’re running it at 10KHz. The argument to Timer1.initialize() is in microseconds and you give it 100, which means your timer interrupt fires 10,000 times a second. There may be better ways to create Euclidean intervals. I’m going to take a closer look at your encoder processing when I have more time.
I assume you’re using a derivative of Ben Buxton’s Rotary encoder library. Good choice.
I see that you’re updating the LEDs every single time through the Arduino loop(). This only needs to be done at around 100Hz to avoid flicker. There are probably other tasks in loop () that overtask your processor, sensors and devices. A simple, crude way to reduce this problem is to increment a counter each time through loop() and only perform those tasks when it reaches a limit you set. After performing those tasks you set the counter variable back to zero.
Edit: you should declare your counter in the main body of the sketch, not inside loop(), like this:
static unsigned long myCounter = 0;
It won’t work if you declare the counter inside loop().
Hi, thank you for looking at it! Sounds like a good idea with the counter, I will try with micros() maybe… Timer1 is used for the actual beats, so the timer calls Clock() at 10KHz. This then looks at the time and checks if the next “clock cycle” has arrived and if we need to step to the next note. Maybe I could move the computation of the euclidean beats (computeBeats) into the Clock() function, so the beats are only computed when the next step is due… or only recompute if anything is changed, because I suspect this calculation to be a bit slow…
You could look at the possibility of setting up a single shot timer for the next beat. The Nano’s
underlying hardware is easily capable of that but I’m unfamiliar with how the Arduino software does it. It just seems really excessive to be checking every tenth of a millisecond to see if it’s time to emit a sequencer event.
The idea is that you set up a timer for the first event (do this in setup), and the last thing an event processor does is set up the timer for the next event.
Your calls to euclid() should be during setup () and after that only when your controls are changed.
I confess, I have a marked tendency towards minimalism. I like to cram everything into the smallest capable hardware because I enjoy the challenge of writing clever software.
To return briefly to give your question a proper answer, ATtiny85 is the smallest through hole chip in the AVR family. If you have something that runs on a Nano board but you need it even smaller or you want to make use of the power management features of the ATtiny85 chip, this is what you’d use. The ESP32 makes the AVR look like something from another century (which, to be fair, it is) but it’s not as much of a challenge to make it do anything useful or clever.
Okay it looks as if the problems @sebastian was facing with just two rotary encoders were likely due to various issues with software. This is great for me because I’m awaiting delivery of quite a lot of shift registers. I’ve already got a heap of multiplexers and binary counters (both no longer needed by my current design) and one of the two types of shift register I need, so I think I should probably start planting chips on a breadboard.
Just taking a few minutes to ponder the delicious irony of the topic I chose for this thread: “fast prototyping.”
So here’s a degenerate form of my basic concept, laid out for wiring up. By “degenerate” here I mean that it’s going to be functionally the same as connecting a single encoder directly to the Arduino. I’m using one 74HC595 SIPO which will drive just one 74HC165 PISO which will relay signals from just one EC11 encoder.
I’m using an Arduino Uno here but that will eventually be replaced by a Nano. The Uno just happens to be more convenient for breadboarding.
I’m posting this photograph on the thread because if I don’t I’ll just retreat into my head again and come up with a million designs that will never be built. It’s an incentive.