Skip to content

Instantly share code, notes, and snippets.

@bruce965
Last active July 14, 2018 12:07
Show Gist options
  • Save bruce965/dac9e55f1259b72de3cc364afee742bd to your computer and use it in GitHub Desktop.
Save bruce965/dac9e55f1259b72de3cc364afee742bd to your computer and use it in GitHub Desktop.
Code for my 61-keys USB MIDI keyboard made with 1x STM32F103C8T6 and 8x 74HC165 ICs.
#include <USBMIDI.h>
// we use 74HC165 8-bit shift register ICs
#define CLOCK_ENABLE_PIN PB4
#define PARALLEL_LOAD_PIN PB7
#define CLOCK_PIN PB6
#define SERIAL_DATA_PIN PB8
#define FIRST_NOTE (60 - 12*2) // leftmost note of the keyboard (60 is middle C)
#define NOTES_COUNT (5 * 12 + 1) // number of notes
#define REGISTRIES_COUNT (NOTES_COUNT/8 + 1)
#define LAST_REGISTRY_NOTE (NOTES_COUNT - (REGISTRIES_COUNT-1)*8)
// how many cycles to wait before actually turning a note off (max 255)
#define DEBOUNCING_CYCLES 128
// DEBOUNCING_CYCLES = pressed
// > 0 and < DEBOUNCING_CYCLES = not pressed, debouncing
// 0 = not pressed, debounce completed
byte keyStates[NOTES_COUNT] = {0};
void setup() {
pinMode(CLOCK_ENABLE_PIN, OUTPUT);
pinMode(PARALLEL_LOAD_PIN, OUTPUT);
pinMode(CLOCK_PIN, OUTPUT);
pinMode(SERIAL_DATA_PIN, INPUT);
digitalWrite(PARALLEL_LOAD_PIN, HIGH);
digitalWrite(CLOCK_ENABLE_PIN, HIGH);
USBMIDI.begin();
}
void loop() {
// load key press status on all shift register ICs in parallel
digitalWrite(PARALLEL_LOAD_PIN, LOW);
delayMicroseconds(5);
digitalWrite(PARALLEL_LOAD_PIN, HIGH);
delayMicroseconds(5);
// for each shift register IC...
for (int r = 0; r < REGISTRIES_COUNT; r++)
{
// START: read one byte from the daisy-chained shift register ICs
digitalWrite(CLOCK_PIN, HIGH);
delayMicroseconds(5);
digitalWrite(CLOCK_ENABLE_PIN, LOW);
delayMicroseconds(5);
byte incoming = shiftIn(SERIAL_DATA_PIN, CLOCK_PIN, MSBFIRST);
digitalWrite(CLOCK_ENABLE_PIN, HIGH);
delayMicroseconds(5);
// END
// what is the offset of the first note this shift register IC handles?
int registryOffset = r * 8;
// how many notes does this shift register IC handle?
int lastRegistryNote = (r == (REGISTRIES_COUNT - 1)) ? LAST_REGISTRY_NOTE : 8;
// for each note handled by this shift register IC...
for (int i = 0; i < lastRegistryNote; i++)
{
// acquire a pointer to the data relative to this key
byte* keyState = &keyStates[registryOffset + i];
// is the key being pressed? was it pressed before?
bool isCurrentlyPressed = incoming & (1 << i);
bool wasPressedBefore = *keyState;
// if the key was not pressed and is pressed now, send a NOTE_ON signal
if (!wasPressedBefore && isCurrentlyPressed)
{
USBMIDI.sendNoteOn(0, FIRST_NOTE + registryOffset + i, 127);
*keyState = DEBOUNCING_CYCLES; // reset debouncing
}
// if the key was pressed and is not pressed anymore, start debouncing
if (wasPressedBefore && !isCurrentlyPressed)
{
*keyState = *keyState - 1; // reduce debouncing counter
// if debouncing counter is completed, send a NOTE_OFF signal
if (*keyState == 0)
{
USBMIDI.sendNoteOff(0, FIRST_NOTE + registryOffset + i, 127);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment