-
-
Save DragRedSim/c88eae2913853c3e5e8dee6284426549 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//BUTTON BOX | |
//USE w ProMicro | |
//Tested in WIN10 + Assetto Corsa | |
//AMSTUDIO | |
//20.8.17 | |
#include <Keypad.h> | |
#include <Joystick.h> | |
#define ENABLE_PULLUPS | |
#define NUMROTARIES 4 | |
#define NUMBUTTONS 24 | |
#define NUMROWS 5 | |
#define NUMCOLS 5 | |
#define QUARTER_STEP | |
byte buttons[NUMROWS][NUMCOLS] = { | |
{0,1,2,3,4}, | |
{5,6,7,8,9}, | |
{10,11,12,13,14}, | |
{15,16,17,18,19}, | |
{20,21,22,23}, | |
}; | |
struct rotariesdef { | |
byte pin1; | |
byte pin2; | |
int ccwchar; | |
int cwchar; | |
volatile byte state; | |
}; | |
rotariesdef rotaries[NUMROTARIES] { | |
{0, 1, 24, 25, 0}, | |
{2, 3, 26, 27, 0}, | |
{4, 5, 28, 29, 0}, | |
{6, 7, 30, 31, 0}, | |
}; | |
#define DROP_THIS_CYCLE 0x80 | |
#define DROP_NEXT_CYCLE 0x40 | |
#define DIR_CW 0x20 | |
#define DIR_CCW 0x10 | |
#define R_START 0x0 | |
#if defined(QUARTER_STEP) | |
const byte ttable[4][4] = { | |
{0x0, DIR_CW | 0x1, DIR_CCW | 0x2, R_START | 0x3}, | |
{DIR_CCW | 0x0, 0x1, R_START | 0x2, DIR_CW | 0x3}, | |
{DIR_CW | 0x0, R_START | 0x1, 0x2, DIR_CCW | 0x3}, | |
{R_START | 0x0, DIR_CCW | 0x1, DIR_CW | 0x2, 0x3}, | |
//magic numbers are used to store the last state of the pins for comparison | |
}; | |
#elif defined(HALF_STEP) | |
#define R_CCW_BEGIN 0x1 | |
#define R_CW_BEGIN 0x2 | |
#define R_START_M 0x3 | |
#define R_CW_BEGIN_M 0x4 | |
#define R_CCW_BEGIN_M 0x5 | |
const byte ttable[6][4] = { | |
// R_START (00) | |
{R_START_M, R_CW_BEGIN, R_CCW_BEGIN, R_START}, | |
// R_CCW_BEGIN | |
{R_START_M | DIR_CCW, R_START, R_CCW_BEGIN, R_START}, | |
// R_CW_BEGIN | |
{R_START_M | DIR_CW, R_CW_BEGIN, R_START, R_START}, | |
// R_START_M (11) | |
{R_START_M, R_CCW_BEGIN_M, R_CW_BEGIN_M, R_START}, | |
// R_CW_BEGIN_M | |
{R_START_M, R_START_M, R_CW_BEGIN_M, R_START | DIR_CW}, | |
// R_CCW_BEGIN_M | |
{R_START_M, R_CCW_BEGIN_M, R_START_M, R_START | DIR_CCW}, | |
}; | |
#else //full-step | |
#define R_CW_FINAL 0x1 | |
#define R_CW_BEGIN 0x2 | |
#define R_CW_NEXT 0x3 | |
#define R_CCW_BEGIN 0x4 | |
#define R_CCW_FINAL 0x5 | |
#define R_CCW_NEXT 0x6 | |
const byte ttable[7][4] = { | |
// R_START | |
{R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START}, | |
// R_CW_FINAL | |
{R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW}, | |
// R_CW_BEGIN | |
{R_CW_NEXT, R_CW_BEGIN, R_START, R_START}, | |
// R_CW_NEXT | |
{R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START}, | |
// R_CCW_BEGIN | |
{R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START}, | |
// R_CCW_FINAL | |
{R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW}, | |
// R_CCW_NEXT | |
{R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START}, | |
}; | |
#endif | |
byte rowPins[NUMROWS] = {21, 20, 19, 18, 15}; | |
byte colPins[NUMCOLS] = {14, 16, 10, 9, 8}; | |
Keypad buttbx = Keypad( makeKeymap(buttons), rowPins, colPins, NUMROWS, NUMCOLS); | |
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, | |
JOYSTICK_TYPE_GAMEPAD, 32, 0, | |
false, false, false, false, false, false, | |
false, false, false, false, false); | |
void setup() { | |
Serial.begin(115200); | |
timer_loop_setup(); // set up an interrupt every 50ms to shift the rotaries state high bits to cycle towards clearing | |
rotary_init(); | |
Joystick.begin(); | |
} | |
void loop() { | |
CheckAllEncoders(); | |
CheckAllButtons(); | |
//delay(100); | |
} | |
void CheckAllButtons(void) { | |
if (buttbx.getKeys()) | |
{ | |
for (int _i = 0; _i < LIST_MAX; _i++) | |
{ | |
if ( buttbx.key[_i].stateChanged ) | |
{ | |
switch (buttbx.key[_i].kstate) { | |
case PRESSED: | |
case HOLD: | |
Joystick.setButton(buttbx.key[_i].kchar, 1); | |
break; | |
case RELEASED: | |
case IDLE: | |
Joystick.setButton(buttbx.key[_i].kchar, 0); | |
break; | |
} | |
} | |
} | |
} | |
} | |
void rotary_init() { | |
for (int _i = 0; _i < NUMROTARIES; _i++) { | |
pinMode(rotaries[_i].pin1, INPUT); | |
pinMode(rotaries[_i].pin2, INPUT); | |
#ifdef ENABLE_PULLUPS | |
digitalWrite(rotaries[_i].pin1, HIGH); | |
digitalWrite(rotaries[_i].pin2, HIGH); | |
#endif | |
rotaries[_i].state = (digitalRead(rotaries[_i].pin2) << 1) | digitalRead(rotaries[_i].pin1); // get 2-bit state of encoder at power on | |
Serial.print(String("Encoder ") + _i + " state: "); | |
printBinary(rotaries[_i].state); | |
Serial.println(); | |
} | |
} | |
byte rotary_process(int _i) { | |
byte pinstate = (digitalRead(rotaries[_i].pin2) << 1) | digitalRead(rotaries[_i].pin1); | |
#ifdef QUARTER_STEP | |
if(pinstate != (rotaries[_i].state & 0x03)) { | |
Serial.print(String("Current state for encoder ") + _i + ": "); | |
printBinary(rotaries[_i].state); | |
Serial.print(", 0x"); | |
Serial.print(rotaries[_i].state, HEX); | |
Serial.print(", pinstate "); | |
Serial.print(pinstate, HEX); | |
rotaries[_i].state = ttable[rotaries[_i].state & 0x0f][pinstate] + (rotaries[_i].state & (DROP_THIS_CYCLE | DROP_NEXT_CYCLE)); //check truth table against previous and current states (x and y respectively), then add the current status of the drop-cycle bits | |
Serial.print(", returning "); | |
printBinary(rotaries[_i].state); | |
Serial.print(", 0x"); | |
Serial.print(rotaries[_i].state, HEX); | |
if((rotaries[_i].state & DIR_CW) == DIR_CW) Serial.print(", rotated CW"); | |
if((rotaries[_i].state & DIR_CCW) == DIR_CCW) Serial.print(", rotated CCW"); | |
Serial.println(); | |
} | |
#endif | |
return rotaries[_i].state; //changed output filter to not remove the drop-cycles bits 6 and 7 | |
} | |
void CheckAllEncoders(void) { | |
for (int _i = 0; _i < NUMROTARIES; _i++) { | |
byte result = rotary_process(_i); | |
if ((result & DIR_CCW) == DIR_CCW) { | |
Serial.print("Returned byte: "); | |
printBinary(result); | |
Serial.println(); | |
Serial.print("Triggering CCW button on, state = "); | |
printBinary(rotaries[_i].state); | |
Serial.println(); | |
if ((result & DROP_THIS_CYCLE) == DROP_THIS_CYCLE) { //immediately turn button off, since it needs to trigger again | |
rotaries[_i].state = rotaries[_i].state & ~DROP_THIS_CYCLE; | |
Joystick.setButton(rotaries[_i].ccwchar, 0); | |
} | |
rotaries[_i].state = (rotaries[_i].state | DROP_NEXT_CYCLE) & ~DIR_CCW; //add drop-next-cycle bit, remove directional trigger | |
Joystick.setButton(rotaries[_i].ccwchar, 1); | |
Serial.print("Triggered CCW button on, state = "); | |
printBinary(rotaries[_i].state); | |
Serial.println(); | |
}; | |
if ((result & DIR_CW) == DIR_CW) { | |
Serial.print("Returned byte: "); | |
printBinary(result); | |
Serial.println(); | |
Serial.print("Triggering CW button on, state = "); | |
printBinary(rotaries[_i].state); | |
Serial.println(); | |
if ((result & DROP_THIS_CYCLE) == DROP_THIS_CYCLE) { //immediately turn button off, since it needs to trigger again | |
rotaries[_i].state = rotaries[_i].state & ~DROP_THIS_CYCLE; | |
Joystick.setButton(rotaries[_i].cwchar, 0); | |
} | |
rotaries[_i].state = (rotaries[_i].state | DROP_NEXT_CYCLE) & ~DIR_CW; | |
Joystick.setButton(rotaries[_i].cwchar, 1); | |
Serial.print("Triggered CW button on, state = "); | |
printBinary(rotaries[_i].state); | |
Serial.println(); | |
}; | |
}; | |
} | |
void timer_loop_setup() { | |
// TIMER 1 for interrupt frequency 20 Hz: | |
cli(); // stop interrupts | |
TCCR1A = 0; // set entire TCCR1A register to 0 | |
TCCR1B = 0; // same for TCCR1B | |
TCNT1 = 0; // initialize counter value to 0 | |
// set compare match register for 10 Hz increments | |
OCR1A = 24999; // = 16000000 / (64 * 20) - 1 (must be <65536) | |
// turn on CTC mode | |
TCCR1B |= (1 << WGM12); | |
// Set CS12, CS11 and CS10 bits for 64 prescaler | |
TCCR1B |= (0 << CS12) | (1 << CS11) | (1 << CS10); | |
// enable timer compare interrupt | |
TIMSK1 |= (1 << OCIE1A); | |
sei(); // allow interrupts | |
} | |
ISR(TIMER1_COMPA_vect) { // fires every 50ms (20Hz), clears rotary buttons | |
for (int _i = 0; _i < NUMROTARIES; _i++) { | |
#ifdef QUARTER_STEP | |
rotaries[_i].state = rotaries[_i].state & 0xc3; //keep high 2, low 2 bits; high 2 are drop, low 2 are used for state in quarter-step mode | |
#else | |
rotaries[_i].state = rotaries[_i].state & 0xc0; | |
#endif | |
if ((rotaries[_i].state & DROP_THIS_CYCLE) == DROP_THIS_CYCLE) { //check drop-this-cycle | |
//clear buttons, reset status bits | |
Joystick.setButton(rotaries[_i].cwchar, 0); | |
Joystick.setButton(rotaries[_i].ccwchar, 0); | |
rotaries[_i].state = rotaries[_i].state & ~DROP_THIS_CYCLE; //remove drop-this-cycle | |
Serial.print("Rotary buttons off, state = "); | |
printBinary(rotaries[_i].state); | |
Serial.println(); | |
}; | |
if ((rotaries[_i].state & DROP_NEXT_CYCLE) == DROP_NEXT_CYCLE) { //check drop-next-cycle | |
// set bit 7 (drop-this-cycle), clear bit 6 (drop-next-cycle) | |
rotaries[_i].state = (rotaries[_i].state | DROP_THIS_CYCLE) & ~DROP_NEXT_CYCLE; | |
Serial.print("Drop-next to drop-this, state = "); | |
printBinary(rotaries[_i].state); | |
Serial.println(); | |
} | |
}; | |
} | |
void printBinary(byte inByte) | |
{ | |
Serial.print('b'); | |
for (int b = 7; b >= 0; b--) | |
{ | |
Serial.print(bitRead(inByte, b)); | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I need an arduino program for 12 buttons directly connected to arduino micro ( no matrix) and 3 encoder CTS 288 (1 pulse every 1/4 step) .
Can you help?