Control-Surface
Control-Surface copied to clipboard
Incomplete reading of a Fatar keyboard
Hello Peter,
Thanks to your help, the manufacture of my midi controller is progressing well, however I (still) need your help ...
I want to set up a 37 key keyboard with velocity, with my switch and my potentiometer.
For switches and potentiometers I use your library (works perfectly). For the keyboard I use this code (https://bitbucket.org/ulph/fatar88lux/src/master/teensy_fatar_88.ino )(works perfectly ). Both (Your library and the code for the keyboard) work perfectly independently of each other. However when I merge the two the keyboard reading is "bad", the velocity cannot exceed 34/127.
On the hardware level, I use a teensy4.1, the potentiometers (27), the selectors (3) and the switches (18) are connected to 4067 multiplexers, the keyboard is connected directly to the pins of the Teensy.
How can I make the keyboard read correctly?
Thank you so much ;)
Here is my code: Code
//
#define PRINT_SERIAL_DEBUG 0
#define MIDI_SEMI_PANIC 0 // should not have to :/
#include <errno.h>
#include <Control_Surface.h>
USBMIDI_Interface midi;
const byte adressCC[3] = {112 , 90, 92 };
const byte tlc4[4] = {32 , 33, 34, 35 };
const byte tlc2[2][2] = { {20 , 21 }, {18 , 17 }};
const byte CCsel[2] = { 0 , 2 };
const byte TlcSel[2] = { 1 , 2 };
char tlcCC[3];
int Selec3 ;
constexpr size_t numbuttons = 13;
uint8_t tlcpins[numbuttons] = {5, 24, 25, 47,
16, 15, 22, 19,
6, 4, 3, 23, 0
};
/*
CONFIGURATION
*/
const unsigned int switch_rest_period_micros = 25; // how long wait for debouncing
const unsigned int scan_period_micros = 5; // how often increment select pin. too often yeilds false readings
/*
SETTINGS
*/
struct Settings_t {
float vel_t0; // period for hardest strike
float vel_t1; // period for softest recognisable
byte vel_pow; // power law for mapping
byte vel_min; // lowest velocity value to send
byte midi_channel; // which
byte root_key; // which is the lowest key on the keybed
};
Settings_t g_settings = {
2200,
500000,
7,
0,
0,
21,
};
enum kb_pins_t {
// break / make pins
kb_MK0 = 32,
kb_BR0 = 30,
kb_MK1 = 28,
kb_BR1 = 12,
kb_MK2 = 8 ,
kb_BR2 = 6,
kb_MK3 = 41,
kb_BR3 = 36,
kb_MK4 = 34,
kb_BR4 = 33,
// select pins
kb_T0 = 31,
kb_T1 = 29,
kb_T2 = 26,
kb_T3 = 9,
kb_T4 = 7,
kb_T5 = 5,
kb_T6 = 37,
kb_T7 = 35,
kb_none = -1
};
kb_pins_t kbSwitches[] = {
kb_MK0,
kb_BR0,
kb_MK1,
kb_BR1,
kb_MK2,
kb_BR2,
kb_MK3,
kb_BR3,
kb_MK4,
kb_BR4,
};
const byte c_numSwitches = sizeof(kbSwitches) / sizeof(kb_pins_t);
const byte c_numSwitchPairs = c_numSwitches / 2;
kb_pins_t kbSelect[] = {
kb_T0,
kb_T1,
kb_T2,
kb_T3,
kb_T4,
kb_T5,
kb_T6,
kb_T7
};
const byte c_numSelect = sizeof(kbSelect) / sizeof(kb_pins_t);
enum key_sense_state_t {
key_sense_state_unbroken, // send midi OFF when entering
key_sense_state_broken,
key_sense_state_made, // send midi ON when entering
key_sense_state_unmade
};
static unsigned int g_num_switches_closed = 0;
CD74HC4067 mux1 = {
A0, // analog pin
{20, 21, 22, 23}, // Address pins S0, S1, S2, S3
// 13, // Optionally, specify the enable pin
};
CD74HC4067 mux7 = {
A15, // analog pin
{20, 21, 22, 23}, // Address pins S0, S1, S2, S3
// 13, // Optionally, specify the enable pin
};
CD74HC4067 mux8 = {
A16, // analog pin
{20, 21, 22, 23}, // Address pins S0, S1, S2, S3
// 13, // Optionally, specify the enable pin
};
class CC14Potentiometer : public MIDIFilteredAnalog<ContinuousCCSender14<10>> {
public:
CC14Potentiometer(pin_t analogPin, MIDIAddress address)
: MIDIFilteredAnalog(analogPin, address, {}) {}
};
CC14Potentiometer potentiometer[] = {
{mux1.pin(0), {11, CHANNEL_1}},
{mux1.pin(4), {10, CHANNEL_1}},
{mux1.pin(5), {26, CHANNEL_1}},
{mux1.pin(7), {7, CHANNEL_1}},
{mux1.pin(8), {9, CHANNEL_1}},
{mux1.pin(9), {25, CHANNEL_1}},
{mux1.pin(13), {8, CHANNEL_1}},
{mux1.pin(14), {23, CHANNEL_1}},
{mux7.pin(1), {4, CHANNEL_1}},
{mux7.pin(13), {5, CHANNEL_1}},
{mux7.pin(15), {6, CHANNEL_1}},
{mux8.pin(0), {2, CHANNEL_1}},
{mux8.pin(3), {3, CHANNEL_1}},
{mux8.pin(6), {22, CHANNEL_1}},
{mux8.pin(8), {1, CHANNEL_1}},
{mux8.pin(12), {24, CHANNEL_1}},
{mux8.pin(15), {0, CHANNEL_1}},
};
template<setting_t N>
class CCValueSelection : public Selectable<N> {
public:
CCValueSelection(MIDIAddress address, uint8_t delta = 32, uint8_t base = 0)
: address{ address }, delta{ delta }, base{ base } {}
void select(setting_t setting) override {
setting = Selectable<N>::validateSetting(setting);
Control_Surface.sendCC(address, base + setting * delta);
for (uint8_t i = 0; i < 3; ++i) {
if (address == adressCC[i]) {
tlcCC[i] = setting ;
}
}
}
private:
MIDIAddress address;
uint8_t delta, base;
};
CCValueSelection<4> SEL4[] {
{ {112, CHANNEL_1}, 32, 0 },
{ {90, CHANNEL_1}, 32, 0 },
{ {92, CHANNEL_1}, 32, 0 },
};
IncrementSelector<4> incSEL4[] {
{ SEL4[0], mux1.pin(6) },
{ SEL4[1], mux7.pin(14) },
{ SEL4[2], mux7.pin(0) },
};
CCPotentiometer pot3[] = {
{mux8.pin(11), {95, CHANNEL_1}},
};
CCPotentiometer Pot[] = {
{mux1.pin(2), {111, CHANNEL_1}},
{mux1.pin(11), {79, CHANNEL_1}},
{mux7.pin(2), {102, CHANNEL_1}},
{mux7.pin(4), {71, CHANNEL_1}},
{mux7.pin(6), {73, CHANNEL_1}},
{mux7.pin(7), {69, CHANNEL_1}},
{mux7.pin(9), {68, CHANNEL_1}},
{mux7.pin(10), {66, CHANNEL_1}},
{mux7.pin(12), {64, CHANNEL_1}},
{mux8.pin(2), {72, CHANNEL_1}},
{mux8.pin(7), {113, CHANNEL_1}},
{mux8.pin(10), {67, CHANNEL_1}},
{mux8.pin(14), {114, CHANNEL_1}},
};
CCButtonLatched buttVCA[] = {
{ mux1.pin(3), {82, CHANNEL_1}},
};
CCButtonLatched button[] = {
{ mux1.pin(1), {111, CHANNEL_1}},
{ mux1.pin(10), {84, CHANNEL_1}},
{ mux1.pin(12), {106, CHANNEL_1}},
{ mux1.pin(15), {86, CHANNEL_1}},
{ mux7.pin(3), {99, CHANNEL_1}},
{ mux7.pin(5), {101, CHANNEL_1}},
{ mux7.pin(8), {98, CHANNEL_1}},
{ mux7.pin(12), {85, CHANNEL_1}},
{ mux8.pin(1), {100, CHANNEL_1}},
{ mux8.pin(4), {87, CHANNEL_1}},
{ mux8.pin(5), {83, CHANNEL_1}},
{ mux8.pin(9), {97, CHANNEL_1}},
{ mux8.pin(13), {85, CHANNEL_1}},
};
#if PRINT_SERIAL_DEBUG
void debug_invalid_state(key_sense_state_t state, byte invalid) {
}
#else
#define debug_invalid_state(a, b) {}
#endif
struct FatarKeyState {
byte note;
kb_pins_t br_pin;
kb_pins_t mk_pin;
key_sense_state_t state;
unsigned long time;
int midi_balance; // debug
byte time_to_velocity(unsigned long time) {
// power curve
float t = time - g_settings.vel_t0;
t = t < 0 ? 0 : t;
t /= g_settings.vel_t1;
t = t > 1 ? 1 : t;
t = (1.0 - t);
for (byte i = 1; i < g_settings.vel_pow; i++) {
t *= t;
}
return (byte)(127.0 * t + 0.49);
};
void key_state_event(int note, bool state, unsigned long time) {
// convert to velocity
byte vel = time_to_velocity(time);
// send midi message
if (state) {
#if MIDI_SEMI_PANIC
usbMIDI.sendNoteOff(note, 0, g_settings.midi_channel);
#endif
usbMIDI.sendNoteOn(note, vel > g_settings.vel_min ? vel : g_settings.vel_min, g_settings.midi_channel); // never send on with 0 as it's silly
}
else {
usbMIDI.sendNoteOff(note, vel, g_settings.midi_channel);
}
}
void setState(key_sense_state_t newState, unsigned long sclk) {
state = newState;
time = sclk;
}
void update(unsigned long now) {
const byte br = digitalReadFast(br_pin);
const byte mk = digitalReadFast(mk_pin);
switch (this->state) {
case key_sense_state_unbroken:
debug_invalid_state(state, mk);
if (br && ( (now - time) > switch_rest_period_micros) ) {
g_num_switches_closed++;
setState(key_sense_state_broken, now);
}
break;
case key_sense_state_broken:
if (mk) {
g_num_switches_closed++;
key_state_event(note, true, now - time);
setState(key_sense_state_made, now);
}
else if (!br) {
g_num_switches_closed--;
setState(key_sense_state_unbroken, now);
}
break;
case key_sense_state_made:
debug_invalid_state(state, !br);
if ( !mk && ( (now - time) > switch_rest_period_micros) ) {
g_num_switches_closed--;
setState(key_sense_state_unmade, now);
}
break;
case key_sense_state_unmade:
if ( !br ) {
g_num_switches_closed--;
key_state_event(note, false, now - time);
setState(key_sense_state_unbroken, now);
}
else if (mk) {
g_num_switches_closed++;
setState(key_sense_state_made, now);
}
break;
}
}
};
static FatarKeyState g_KeyStates[c_numSwitchPairs * c_numSelect] = {};
void setup_keys() {
for (int g = 0; g < c_numSelect; g++) {
for (int i = 0; i < c_numSwitchPairs; i++) {
int state_idx = g + 8 * i;
FatarKeyState *keyState = &g_KeyStates[state_idx];
keyState->note = g_settings.root_key + state_idx;
keyState->br_pin = kbSwitches[2 * i + 1];
keyState->mk_pin = kbSwitches[2 * i + 0];
}
}
}
void setup_pins() {
int i = 0;
for (i = 0; i < c_numSwitches; i++) {
pinMode(kbSwitches[i], INPUT_PULLDOWN);
}
for (i = 0; i < c_numSelect; i++) {
pinMode(kbSelect[i], OUTPUT);
}
}
void setup() {
Control_Surface.begin();
setup_pins();
setup_keys();
}
static byte g_select = 0;
void increment_select() {
digitalWriteFast(kbSelect[g_select], LOW);
g_select++;
g_select %= c_numSelect;
digitalWriteFast(kbSelect[g_select], HIGH);
}
unsigned long last_scan_micros = 0;
unsigned long last_send_usb = 0;
void loop() {
Control_Surface.loop();
unsigned long now = micros();
if ((now - last_scan_micros) < scan_period_micros) {
return;
}
last_scan_micros = now;
for (int i = 0; i < c_numSwitchPairs; i++) {
int state_idx = g_select + 8 * i;
FatarKeyState *keyState = &g_KeyStates[state_idx];
keyState->update(now);
}
increment_select();
}
This might not be that easy. It looks like the original code expects to update every 50 µs, which is very fast. I suspect that it's not possible to read 27 potentiometers this quickly. Most time will probably be spent reading the analog inputs, given that the processor of the Teensy 4.1 should be plenty fast. You could try measuring how long that takes, and you could try increasing the ADC clock rate or the number of averaged samples if it turns out to be a bottleneck.
There's also a delay in the multiplexer implementation to prevent starting a reading before the changes to the address lines have propagated. This is definitely necessary on fast processors like the T 4.1, but you might get away with a shorter delay:
https://github.com/tttapa/Control-Surface/blob/feee931d44d5f20fb7913b171895316e5819549d/src/AH/Hardware/ExtendedInputOutput/AnalogMultiplex.hpp#L246
You could compare the duration of Control_Surface.loop() to the duration of 27×2 analogReads (times two because every time the multiplexer channel is changed, Control Surface does a dummy read):
https://github.com/tttapa/Control-Surface/blob/feee931d44d5f20fb7913b171895316e5819549d/src/AH/Hardware/ExtendedInputOutput/AnalogMultiplex.hpp#L217
Good evening Peter, thank you for this precious information (if you have any others, I'm a taker;) ).
Good concerning my problem, after a few ... minutes ... hours ... I found a solution! (I tried a lot of things without success).
In the end I use IntervalTimer to call the functions at specific times.
Regarding the execution time of control surface (with my parameters) it is 667 microseconds, for the keyboard it is 1 microsecond, however I left 50 microseconds in my code (I thought that maybe left more idle to processor).
Do you have any other tip to optimize the execution of your code?
PS: All the tips you gave me worked with a huge gain in speed of calculation (I cannot say precisely because I did not measure it)
#define PRINT_SERIAL_DEBUG 0
#define MIDI_SEMI_PANIC 0 // should not have to :/
#include <errno.h>
#include <Control_Surface.h>
IntervalTimer myTimer;
USBMIDI_Interface midi;
const byte adressCC[3] = {112 , 90, 92 };
const byte tlc4[4] = {32 , 33, 34, 35 };
const byte tlc2[2][2] = { {20 , 21 }, {18 , 17 }};
const byte CCsel[2] = { 0 , 2 };
const byte TlcSel[2] = { 1 , 2 };
char tlcCC[3];
int Selec3 ;
constexpr size_t numbuttons = 13;
uint8_t tlcpins[numbuttons] = {5, 24, 25, 47,
16, 15, 22, 19,
6, 4, 3, 23, 0
};
/*
CONFIGURATION
*/
const unsigned int switch_rest_period_micros = 25; // how long wait for debouncing
unsigned int scan_period_micros = 50; // how often increment select pin. too often yeilds false readings
/*
SETTINGS
*/
struct Settings_t {
float vel_t0; // period for hardest strike
float vel_t1; // period for softest recognisable
byte vel_pow; // power law for mapping
byte vel_min; // lowest velocity value to send
byte midi_channel; // which
byte root_key; // which is the lowest key on the keybed
};
Settings_t g_settings = {
2200,
500000,
7,
0,
0,
21,
};
enum kb_pins_t {
// break / make pins
kb_MK0 = 32,
kb_BR0 = 30,
kb_MK1 = 28,
kb_BR1 = 12,
kb_MK2 = 8 ,
kb_BR2 = 6,
kb_MK3 = 41,
kb_BR3 = 36,
kb_MK4 = 34,
kb_BR4 = 33,
// select pins
kb_T0 = 31,
kb_T1 = 29,
kb_T2 = 26,
kb_T3 = 9,
kb_T4 = 7,
kb_T5 = 5,
kb_T6 = 37,
kb_T7 = 35,
kb_none = -1
};
kb_pins_t kbSwitches[] = {
kb_MK0,
kb_BR0,
kb_MK1,
kb_BR1,
kb_MK2,
kb_BR2,
kb_MK3,
kb_BR3,
kb_MK4,
kb_BR4,
};
const byte c_numSwitches = sizeof(kbSwitches) / sizeof(kb_pins_t);
const byte c_numSwitchPairs = c_numSwitches / 2;
kb_pins_t kbSelect[] = {
kb_T0,
kb_T1,
kb_T2,
kb_T3,
kb_T4,
kb_T5,
kb_T6,
kb_T7
};
const byte c_numSelect = sizeof(kbSelect) / sizeof(kb_pins_t);
enum key_sense_state_t {
key_sense_state_unbroken, // send midi OFF when entering
key_sense_state_broken,
key_sense_state_made, // send midi ON when entering
key_sense_state_unmade
};
static unsigned int g_num_switches_closed = 0;
CD74HC4067 mux1 = {
A0, // analog pin
{20, 21, 22, 23}, // Address pins S0, S1, S2, S3
// 13, // Optionally, specify the enable pin
};
CD74HC4067 mux7 = {
A15, // analog pin
{20, 21, 22, 23}, // Address pins S0, S1, S2, S3
// 13, // Optionally, specify the enable pin
};
CD74HC4067 mux8 = {
A16, // analog pin
{20, 21, 22, 23}, // Address pins S0, S1, S2, S3
// 13, // Optionally, specify the enable pin
};
class CC14Potentiometer : public MIDIFilteredAnalog<ContinuousCCSender14<10>> {
public:
CC14Potentiometer(pin_t analogPin, MIDIAddress address)
: MIDIFilteredAnalog(analogPin, address, {}) {}
};
CC14Potentiometer potentiometer[] = {
{mux1.pin(0), {11, CHANNEL_1}},
{mux1.pin(4), {10, CHANNEL_1}},
{mux1.pin(5), {26, CHANNEL_1}},
{mux1.pin(7), {7, CHANNEL_1}},
{mux1.pin(8), {9, CHANNEL_1}},
{mux1.pin(9), {25, CHANNEL_1}},
{mux1.pin(13), {8, CHANNEL_1}},
{mux1.pin(14), {23, CHANNEL_1}},
{mux7.pin(1), {4, CHANNEL_1}},
{mux7.pin(13), {5, CHANNEL_1}},
{mux7.pin(15), {6, CHANNEL_1}},
{mux8.pin(0), {2, CHANNEL_1}},
{mux8.pin(3), {3, CHANNEL_1}},
{mux8.pin(6), {22, CHANNEL_1}},
{mux8.pin(8), {1, CHANNEL_1}},
{mux8.pin(12), {24, CHANNEL_1}},
{mux8.pin(15), {0, CHANNEL_1}},
};
template<setting_t N>
class CCValueSelection : public Selectable<N> {
public:
CCValueSelection(MIDIAddress address, uint8_t delta = 32, uint8_t base = 0)
: address{ address }, delta{ delta }, base{ base } {}
void select(setting_t setting) override {
setting = Selectable<N>::validateSetting(setting);
Control_Surface.sendCC(address, base + setting * delta);
for (uint8_t i = 0; i < 3; ++i) {
if (address == adressCC[i]) {
tlcCC[i] = setting ;
}
}
}
private:
MIDIAddress address;
uint8_t delta, base;
};
CCValueSelection<4> SEL4[] {
{ {112, CHANNEL_1}, 32, 0 },
{ {90, CHANNEL_1}, 32, 0 },
{ {92, CHANNEL_1}, 32, 0 },
};
IncrementSelector<4> incSEL4[] {
{ SEL4[0], mux1.pin(6) },
{ SEL4[1], mux7.pin(14) },
{ SEL4[2], mux7.pin(0) },
};
CCPotentiometer pot3[] = {
{mux8.pin(11), {95, CHANNEL_1}},
};
CCPotentiometer Pot[] = {
{mux1.pin(2), {111, CHANNEL_1}},
{mux1.pin(11), {79, CHANNEL_1}},
{mux7.pin(2), {102, CHANNEL_1}},
{mux7.pin(4), {71, CHANNEL_1}},
{mux7.pin(6), {73, CHANNEL_1}},
{mux7.pin(7), {69, CHANNEL_1}},
{mux7.pin(9), {68, CHANNEL_1}},
{mux7.pin(10), {66, CHANNEL_1}},
{mux7.pin(12), {64, CHANNEL_1}},
{mux8.pin(2), {72, CHANNEL_1}},
{mux8.pin(7), {113, CHANNEL_1}},
{mux8.pin(10), {67, CHANNEL_1}},
{mux8.pin(14), {114, CHANNEL_1}},
};
CCButtonLatched buttVCA[] = {
{ mux1.pin(3), {82, CHANNEL_1}},
};
CCButtonLatched button[] = {
{ mux1.pin(1), {111, CHANNEL_1}},
{ mux1.pin(10), {84, CHANNEL_1}},
{ mux1.pin(12), {106, CHANNEL_1}},
{ mux1.pin(15), {86, CHANNEL_1}},
{ mux7.pin(3), {99, CHANNEL_1}},
{ mux7.pin(5), {101, CHANNEL_1}},
{ mux7.pin(8), {98, CHANNEL_1}},
{ mux7.pin(12), {85, CHANNEL_1}},
{ mux8.pin(1), {100, CHANNEL_1}},
{ mux8.pin(4), {87, CHANNEL_1}},
{ mux8.pin(5), {83, CHANNEL_1}},
{ mux8.pin(9), {97, CHANNEL_1}},
{ mux8.pin(13), {85, CHANNEL_1}},
};
#if PRINT_SERIAL_DEBUG
void debug_invalid_state(key_sense_state_t state, byte invalid) {
}
#else
#define debug_invalid_state(a, b) {}
#endif
struct FatarKeyState {
byte note;
kb_pins_t br_pin;
kb_pins_t mk_pin;
key_sense_state_t state;
unsigned long time;
int midi_balance; // debug
byte time_to_velocity(unsigned long time) {
// power curve
float t = time - g_settings.vel_t0;
t = t < 0 ? 0 : t;
t /= g_settings.vel_t1;
t = t > 1 ? 1 : t;
t = (1.0 - t);
for (byte i = 1; i < g_settings.vel_pow; i++) {
t *= t;
}
return (byte)(127.0 * t + 0.49);
};
void key_state_event(int note, bool state, unsigned long time) {
// convert to velocity
byte vel = time_to_velocity(time);
// send midi message
if (state) {
#if MIDI_SEMI_PANIC
usbMIDI.sendNoteOff(note, 0, g_settings.midi_channel);
#endif
usbMIDI.sendNoteOn(note, vel > g_settings.vel_min ? vel : g_settings.vel_min, g_settings.midi_channel); // never send on with 0 as it's silly
}
else {
usbMIDI.sendNoteOff(note, vel, g_settings.midi_channel);
}
}
void setState(key_sense_state_t newState, unsigned long sclk) {
state = newState;
time = sclk;
}
void update(unsigned long now) {
const byte br = digitalReadFast(br_pin);
const byte mk = digitalReadFast(mk_pin);
switch (this->state) {
case key_sense_state_unbroken:
debug_invalid_state(state, mk);
if (br && ( (now - time) > switch_rest_period_micros) ) {
g_num_switches_closed++;
setState(key_sense_state_broken, now);
}
break;
case key_sense_state_broken:
if (mk) {
g_num_switches_closed++;
key_state_event(note, true, now - time);
setState(key_sense_state_made, now);
}
else if (!br) {
g_num_switches_closed--;
setState(key_sense_state_unbroken, now);
}
break;
case key_sense_state_made:
debug_invalid_state(state, !br);
if ( !mk && ( (now - time) > switch_rest_period_micros) ) {
g_num_switches_closed--;
setState(key_sense_state_unmade, now);
}
break;
case key_sense_state_unmade:
if ( !br ) {
g_num_switches_closed--;
key_state_event(note, false, now - time);
setState(key_sense_state_unbroken, now);
}
else if (mk) {
g_num_switches_closed++;
setState(key_sense_state_made, now);
}
break;
}
}
};
static FatarKeyState g_KeyStates[c_numSwitchPairs * c_numSelect] = {};
void setup_keys() {
for (int g = 0; g < c_numSelect; g++) {
for (int i = 0; i < c_numSwitchPairs; i++) {
int state_idx = g + 8 * i;
FatarKeyState *keyState = &g_KeyStates[state_idx];
keyState->note = g_settings.root_key + state_idx;
keyState->br_pin = kbSwitches[2 * i + 1];
keyState->mk_pin = kbSwitches[2 * i + 0];
}
}
}
void setup_pins() {
int i = 0;
for (i = 0; i < c_numSwitches; i++) {
pinMode(kbSwitches[i], INPUT_PULLDOWN);
}
for (i = 0; i < c_numSelect; i++) {
pinMode(kbSelect[i], OUTPUT);
}
}
void setup() {
Serial.begin (9600);
Control_Surface.begin();
myTimer.begin(keys, 50);
setup_pins();
setup_keys();
}
static byte g_select = 0;
void increment_select() {
digitalWriteFast(kbSelect[g_select], LOW);
g_select++;
g_select %= c_numSelect;
digitalWriteFast(kbSelect[g_select], HIGH);
}
unsigned long last_scan_micros = 0;
unsigned long first_scan_micros = 0;
unsigned long last_send_usb = 0;
unsigned long new_scan ;
void keys() {
unsigned long now = micros();
last_scan_micros = now;
for (int i = 0; i < c_numSwitchPairs; i++) {
int state_idx = g_select + 8 * i;
FatarKeyState *keyState = &g_KeyStates[state_idx];
keyState->update(now);
}
increment_select();
}
void loop() {
Control_Surface.loop();
}
Glad to hear you got it to work!
Regarding the execution time of control surface (with my parameters) it is 667 microseconds
That's a pretty good number, is that with the default settings or after changes to the library? I did a quick test, and a single analogRead() on the Teensy 4.1 seems to take about 20 µs, which would give a total run time of at least 27×2×20 µs ≃ 1 ms.
Pierre,
I have 667 µs with the modifications. I deleted :
- the delay in the multiplexer `
Control-Surface/src/AH/Hardware/ExtendedInputOutput/AnalogMultiplex.hpp
Line 246 in feee931
delayMicroseconds(5);
- the double reading of multiplexers
Control-Surface/src/AH/Hardware/ExtendedInputOutput/AnalogMultiplex.hpp
Line 217 in feee931
ExtIO::analogRead(analogPin); // Discard first reading
Is it possible to optimize multiplexer reading by "analogReadFast" or DigitalReadFast "?
I take this opportunity to ask you another question, I use 6-position selector, they allow me to select values (127/6). Currently I put them as potentiometer, it works however when I turn the selcteur I receive a multitude of values between the old position and the new one. I suspect that this is not the most suitable. What is the best way to implement this component in code?
Is it possible to optimize multiplexer reading by "analogReadFast" or DigitalReadFast "?
Not really, digitalReadFast only works if the pin numbers are constants, which is not the case here. You could gain some speedup by writing everything manually of course, without using ExtIO, but that requires you to write your own MIDI output elements for this purpose, or do everything in the main loop function.
I take this opportunity to ask you another question, I use 6-position selector, they allow me to select values (127/6). Currently I put them as potentiometer, it works however when I turn the selcteur I receive a multitude of values between the old position and the new one. I suspect that this is not the most suitable. What is the best way to implement this component in code?
The following is based on the Custom-MIDI-Output-Element example:
#include <Control_Surface.h>
template <uint8_t N>
class NPositionSelector : public MIDIOutputElement {
public:
NPositionSelector(pin_t pin, MIDIAddress address)
: analog(pin), address(address) {}
public:
// This method is called once by `Control_Surface.begin()`.
void begin() final override { /* Nothing to do here */ }
// This method is called continuously by `Control_Surface.loop()`.
void update() final override {
if (analog.update()) {
uint8_t selection = analogValueToSelection(analog.getValue());
if (selection != previousSelection) {
Control_Surface.sendCC(address, selectionToMIDI(selection));
previousSelection = selection;
}
}
}
constexpr static uint8_t resolution = 7; // bits
static uint8_t analogValueToSelection(analog_t v) {
return N * v / (1u << resolution);
}
static uint8_t selectionToMIDI(uint8_t s) {
return (128 * s + 64) / N;
}
private:
AH::FilteredAnalog<resolution> analog;
MIDIAddress address;
uint8_t previousSelection = 0xFF;
};
// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
USBMIDI_Interface midi;
NPositionSelector<6> sel {
A0, // Analog input pin
{7, CHANNEL_1}, // MIDI address
};
void setup() {
Control_Surface.begin(); // Initialize Control Surface (calls button.begin())
}
void loop() {
Control_Surface.loop(); // Update the Control Surface (calls button.update())
}