Hi Paul,
So I've managed to get half of my keyboard working with a 74HC595 and 74HC165. To get the whole thing working I'm cascading two 595's and two 165's together. With them cascaded together, I can get the lower half working, but not the upper half. With the lower half, I get both an MK and BK switch to close. With the upper half, I either get only one to close, or two, but they are the wrong two, such as BR4 and MK5. I have a feeling that is has something to do with how I have wired the HC165's together, or the code to control the 165's, (the "scanColumn" function I think is the most pertinent part) but I haven't really found any good examples of cascading 165's together. I've included a schematic (my first so it could be full of errors), code and a pic of the circuit that probably isn't helpful. Any thoughts would be helpful.
KeyBoard.pdf (276.2 KB)
#include <MIDIUSB.h>
#define GPIO2_PREFER_SPEED 1
//#include <DIO2.h>
#include <math.h>
//MIDI baud rate
#define SERIAL_RATE 31250 //31250
#define thresholdTime_max 2400
#define constrain_min 1
#define constrain_max 200
#define log_multiplier 25
//Exponential Velocity response curves data
int expCurve[127] = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5,
6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13,
14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27,
27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 35, 36, 37, 38, 40, 41, 43, 44, 46, 47, 49, 50, 52, 53, 55, 56, 58, 59,
61, 62, 64, 65, 67, 68, 70, 71, 73, 74, 76, 77, 79, 80, 82, 83, 85, 86, 88, 89, 91, 92, 94, 95, 97, 98, 100,
101, 103, 104, 106, 107, 109, 110, 112, 113, 115, 116, 118, 119, 121, 122, 124, 125, 127};
//Lograthmic Velocity response curve data
int logCurve[127] = {1,3,5,6,8,9,
11,12,14,15,17,18,20,21,23,24,
26,27,29,30,32,33,35,36,38,39,
41,42,44,45,47,48,50,51,53,54,
56,57,59,60,62,63,65,66,68,69,
71,72,74,75,77,78,80,81,83,84,
86,87,89,90,91,92,93,94,95,95,
96,97,97,98,99,99,100,100,101,101,
102,102,103,103,104,104,105,105,106,106,
107,107,108,108,109,109,110,110,111,111,
112,112,113,113,114,114,115,115,116,116,
117,117,118,118,119,119,120,120,121,121,
122,122,123,123,124,124,125,125,126,
126,127};
//S-Type Curve Velocity response curve data
int sCurve[127] = {1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5,6,6,6,6,6,7,7,8,8,9,9,
10,11,13,14,17,19,21,24,26,28,31,33,35,37,40,42,44,47,49,
51,54,56,58,60,63,65,67,70,72,74,77,79,81,83,86,88,90,93,95,97,
100,102,104,106,109,111,113,115,116,117,118,119,119,120,120,
121,121,121,121,121,
122,122,122,122,122,
123,123,123,123,123,
124,124,124,124,124,
125,125,125,125,125,
126,126,126,126,126,
127,127,127};
//N_Type Curve Velocity response curve data
int nCurve[127] = {10,11,12,14,15,17,18,20,21,23,24,26,27,28,29,
30,31,33,34,35,36,37,38,39,40,41,42,43,43,44,45,46,46,47,48,48,49,49,
50,50,51,51,51,52,52,53,53,53,54,54,54,55,55,55,56,56,56,57,57,57,57,58,58,59,59,59,
60,60,60,60,61,62,62,62,62,63,63,63,63,64,64,64,65,65,66,66,67,67,67,68,69,
70,71,73,74,76,78,80,81,83,85,87,90,92,93,95,97,99,101,102,104,105,107,108,
110,111,113,115,116,118,119,121,122,123,125,127,127};
// bitmasks for scanning columns //VECTOR
int bits[] =
{
B00000001, //A unique signal is sent through pin 1 of the bit shifter
B00000010, //A unique signal is sent through pin 2 of the bit shifter
B00000100, //So 7 pins are high, and 1 pin is low. This is the pull up version...need to figure that out.
B00001000, //247
B00010000, //239
B00100000, //223
B01000000, //191
B10000000 //127
};
int note;
int i;
byte aftertouch = 0xD0;
byte channel = 0;
byte firstNote = 36;
byte velocityOn;
byte velocityOff;
byte incoming_1, incoming_2;
uint16_t incomming;
int constrainTimeOn;
int constrainTimeOff;
double logTime1;
double logTime2;
int keyState[64]; //rows x columns
int col, row, blk;
unsigned long startTime1[64];
unsigned long startTime2[64];
unsigned long startTimeOn;
unsigned long startTimeOff;
unsigned long afterTouchStartThreshold = 1;
unsigned long startTimeBottom[64];
int afterTouchVal[64];
int afterTouchValPast[64];
int afterTouchValue;
int afterTouchValuePast = 0;
int maxVal = 0;
int testAnalogInput;
//74HC165 pins
int load = 13;// PL pin 1
int clockEnablePin = 10;// CE pin 15
int dataIn = 11;// Q7 pin 7
int clockIn = 12;// CP pin 2
// 74HC595 pins
const int dataPin = 7;
const int latchPin = 8;
const int clockPin = 9;
void setup() { // put your setup code here, to run once:
// Shift Register setup 74HC595
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(latchPin, OUTPUT);
// 74HC165
// setup pins output/input mode
pinMode(load, OUTPUT);
pinMode(clockEnablePin, OUTPUT);
pinMode(clockIn, OUTPUT);
pinMode(dataIn, INPUT);
//Send out a signal through all
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, B00000000);
shiftOut(dataPin, clockPin, MSBFIRST, B00000000);
digitalWrite(latchPin, HIGH);
for (i=0; i<64; i++){ // i is row x columns
startTime1[i] = 0;
startTime2[i] = 0;
keyState[i] = 0;
startTimeBottom[i] = 0;
afterTouchVal[i] = 0;
afterTouchValPast[i] = 0;
}
Serial.begin(SERIAL_RATE);
while (!Serial);
}
void loop() {
// put your main code here, to run repeatedly:
//delay(10000);
scanAllKeys();
//delay(10000);
}
// columns 0-1 are 0, 2-3 are 1, 4-5 are 2, 6-7 are 3, 7-8 are 4. need to round down the col/2 so use floor function
void scanAllKeys(){
blk = 0;
for (col = 0; col < 15; col+=2) {//increment by 2 through the MK columns{
for (row = 0; row < 8; row++){
scanKeys(keyState[row + (int)floor(col/2) + blk]); //scan the col * row key based on its key state.
}
blk = blk + 7;
}
}
void scanColumn(int i)
{
digitalWrite(latchPin, LOW); //595
//if(i < 8) //go through first 8 columns assigned to the left most shift register
{
shiftOut(dataPin, clockPin, MSBFIRST, bits[i]); //right sr All high, so no unique signal is sent MSBFIRST is most significant bit first, which pins get which bite
shiftOut(dataPin, clockPin, MSBFIRST, bits[i]); //left sr Only a unique signal is sent through the pin with the 1
//Serial.println(bits[col]);
}
//else
//{
// shiftOut(dataPin, clockPin, MSBFIRST, bits[i-8]); //go through the remaining 3 columns assigned to the right most register
// shiftOut(dataPin, clockPin, MSBFIRST, B00000000); //left sr
//}
digitalWrite(latchPin, HIGH); //Send a signal to that column
digitalWrite(load, LOW); //165
delayMicroseconds(5);
digitalWrite(load, HIGH);
delayMicroseconds(5);
digitalWrite(clockIn, HIGH);
digitalWrite(clockEnablePin, LOW);
incoming_2 = shiftIn(dataIn, clockIn, LSBFIRST);
incoming_1 = shiftIn(dataIn, clockIn, LSBFIRST);
digitalWrite(clockEnablePin, HIGH);
//Serial.println("incoming");
incomming = (incoming_1<<8) | (incoming_2);
Serial.println(incomming, BIN);
}
void scanKeys(int var) {
switch (var) {
case 0:
//digitalWrite(BR[col], LOW); //Switch BR ON
scanColumn(row); //col + 1
//Serial.println(digitalRead(ROW[row]));
//if (digitalRead(ROW[row]) == LOW){ //BR key is pressed
if (bitRead(incomming, col+1) == LOW){
startTime1[row + col + blk] = millis(); //(int)floor(col/2)
keyState[row + (int)floor(col/2) + blk] = 1;
}
//digitalWrite(BR[col], HIGH); //Switch BR OFF I don't think I need an equivalent with the Shift Register
//Serial.println(incoming, BIN);
//Serial.println(bitRead(incoming, row));
//Serial.println(keyState[row + (int)floor(col/2) + blk]);
//Serial.println("column");
//Serial.println(col);
//Serial.println("Row");
//Serial.println(row);
break;
case 1:
//if (digitalRead(ROW[row]) == HIGH){ //No key pressed
if (bitRead(incomming, col) == HIGH){//should be incomming#######################
keyState[row + (int)floor(col/2) + blk] = 2;
}
//Serial.println(keyState[row + col + blk]);
//Serial.println("column");
//Serial.println(col);
//Serial.println("Row");
//Serial.println(row);
//Serial.println("Digital Read");
//Serial.println(digitalRead(ROW[row]));
break;
case 2:
//digitalWrite(MK[col], LOW); //Switch MK ON
scanColumn(row);
//if(digitalRead(ROW[row]) == LOW){ //MK key is pressed
if (bitRead(incomming, col) == LOW){//should be incomming#######################
startTimeOn = millis()-startTime1[row + col + blk];
logTime1 = log_multiplier * log(startTimeOn);
startTimeBottom[row + (int)floor(col/2) + blk] = millis(); //start timer for aftertouch Threshold
if (startTimeOn > thresholdTime_max){
keyState[row + (int)floor(col/2) + blk] = 0;
//Serial.println(keyState[row + col + blk]);
//Serial.println("column");
//Serial.println(col);
//Serial.println("Row");
//Serial.println(row);
//digitalWrite(MK[col], HIGH); //Switch MK OFF
break;
}
constrainTimeOn = constrain(logTime1, constrain_min, constrain_max);
velocityOn = map(constrainTimeOn, constrain_min, constrain_max, 127, 1);
//Select Midi Velocity Curve
velocityOn = expCurve[velocityOn];
//velocityOn = logCurve[velocityOn];
//velocityOn = sCurve[velocityOn];
//velocityOn = nCurve[velocityOn];
note = row + floor(col/2) + blk;
// MIDI.sendNoteOn(firstNote + note, velocityOn, channel);
noteOn(channel, firstNote + note, velocityOn);
MidiUSB.flush();
keyState[row + (int)floor(col/2) + blk] = 3;
}
//digitalWrite(MK[col], HIGH); //Switch MK off
break;
case 3:
//digitalWrite(MK[col], LOW); //Switch MK ON
scanColumn(row);
//if (digitalRead(ROW[row]) == LOW){ //Key still at bottom
if (bitRead(incomming, col) == LOW){//should be incomming#######################
//afterTouch();
}
else{
keyState[row + (int)floor(col/2) + blk] = 4; //no, key is erleased
}
//digitalWrite(MK[col], HIGH); //Switch MK OFF
break;
case 4:
//digitalWrite(MK[col, LOW]) //Switch MK ON
scanColumn(row);
//if(digitalRead(ROW[row]) == HIGH){ //MK key is released
if (bitRead(incomming, col) == HIGH){//should be incomming#######################
startTime2[row + (int)floor(col/2) + blk] = millis();
keyState[row + (int)floor(col/2) + blk] = 5;
}
//digitalWrite(MK[col], HIGH); // Switch MK OFF
//Serial.println(keyState[row + col + blk]);
//Serial.println("column");
//Serial.println(col);
//Serial.println("Row");
//Serial.println(row);
scanKeys(keyState[row + (int)floor(col/2) + blk]);
break;
case 5:
//digitalWrite(BR[col], LOW): //Switch BR ON
scanColumn(row);
//if(digitalRead(ROW[row]) == HIGH) {//BR key reached top
if (bitRead(incomming, col + 1) == HIGH){//should be incomming#######################
startTimeOff = millis();
startTime2[row + (int)floor(col/2) + blk];
logTime2 = log_multiplier * log(startTimeOff);
constrainTimeOff = constrain(logTime2, constrain_min, constrain_max);
velocityOff = map(constrainTimeOff, constrain_max, constrain_min, 1, 127);
velocityOff = expCurve[velocityOff];
//Select Velocity Curve
velocityOff = logCurve[velocityOff];
velocityOff = sCurve[velocityOff];
velocityOff = nCurve[velocityOff];
note = row + floor(col/2) + blk;
//MIDI.sendNoteOff(firstNote + note, velocityOn, channel);
noteOff(channel, firstNote + note, velocityOn);
MidiUSB.flush();
keyState[row + (int)floor(col/2) + blk] = 0;
}
//digitalWrite(BR[col], HIGH); //Switch BR OFF
break;
default:
break;
}
}
void afterTouch(){
if((millis() - startTimeBottom[row + col + blk]) >= afterTouchStartThreshold){
testAnalogInput = analogRead(A0) / 8; //1023/8 = values 0-127
if ( testAnalogInput <= 125){
testAnalogInput = 125 - testAnalogInput;
afterTouchVal[row + col + blk] = testAnalogInput;
for(i = 0; i < 64; i++){
if(afterTouchVal[i] > maxVal){
maxVal = afterTouchVal;
} //maxVal now contains the largest Value
afterTouchValue = maxVal;
}
if(afterTouchVal[row + col + blk] != afterTouchValPast[row + col + blk]){
//Midi.sendAfterTouch(afterTouchValue, channel);
channelPressure(channel, afterTouchValue);
MidiUSB.flush();
afterTouchValPast[row + col + blk] = afterTouchVal[row + col + blk];
maxVal = 0;
}
}
}
}
//http://midi.teragonaudio.com/tech/midispec.htm
void noteOn(byte channel, byte pitch, byte velocity) {
midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
MidiUSB.sendMIDI(noteOn);
}
void noteOff(byte channel, byte pitch, byte velocity) {
midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
MidiUSB.sendMIDI(noteOff);
}
void afterTouch(byte channel, byte pitch, byte aftertouch) {
midiEventPacket_t afterTouch = {0x0D, 0xD0 | channel, pitch, aftertouch};
MidiUSB.sendMIDI(afterTouch);
}
void channelPressure(byte channel, byte aftertouch){
midiEventPacket_t channelPressure = {0x0D, 0xD0 | channel, aftertouch};
MidiUSB.sendMIDI(channelPressure);
}
void controlChange(byte channel, byte control, byte value) {
midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
MidiUSB.sendMIDI(event);
}
void pitchBend(byte channel, int value) {
byte lowValue = value & 0x7F;
byte highValue = value >> 7;
midiEventPacket_t pitchBend = {0x0E, 0xE0 | channel, lowValue, highValue};
MidiUSB.sendMIDI(pitchBend);
}