In the last blog, "Arduino with 8 KY040 Rotary Encoders", I made passing reference that switches could be added into the sketch. The Microsoft flight simulator software, FSX, can accept data from an Arduino when it is programmed as a HID-USB joystick with 8-axes and 32 buttons or switches. The joyReport data structure within the sketch holds and conveys the axes, switches, and encoders data through USB to the simulator software on a PC. Eight (8) rotary encoders add 16 switches to the joyReport, so there are 16 additional bits of data that can be added from other pins on the Mega 2560.
The 8 rotary encoders were added to the sketch by setting 2 8-bit ports (Port B and Port K) as pin-change-interrupts, effectively adding 16 additional hardware interrupts to the Mega 2560. The analog pins A0-A7 are used in the sketch for axis sensors (potentiometers). The 6 external hardware interrupts on the Mega 2560 are pins 21, 20, 19, 18 (INT0, INT1, INT2, INT3) and, pins 2 and 3 (INT4, INT5), which can be used for additional switches that require immediate attention of the software. Thirty-eight (38) remaining digital pins can be used for switches, but without a hardware interrupt, polling is required to cycle through the pins to determine if a pin has changed state. In FSX, buttons and switches are generally used to turn on and off lights or other airplane functions that are slow human-speed activities, so polling of switch states will not degrade simulator performance to any significant degree. Interrupts, rather than polling, were used for encoders since the timing of the A-B switching of the encoder was critical to reading the encoder accurately. Since changed switch states happens so rarely (relative to CPU speed), all the switch states can be accumulated into the joyReport data structure, and that data structure can then be transmitted when any one or more switches has changed HIGH-LOW or LOW-HIGH. Any switch that stays ON or OFF will have its value transmitted in the joyReport each time the joyReport is sent out through USB. The FSX/FSUIPC software on the PC has to deal with the repetition of switch setting values.
Sketch Design --
In the sketch, the existing Axes (pins A0-A7) will be retained. Also the 8 rotary encoders which write their 16 values into joyReport bytes [0] and [1] will be retained.
Sixteen digital pins will be added to populate joyReport bytes [2] and [3]. And for demonstration, 2 of 6 hardware interrupts will be used to capture switch values, and those values will be written to joyReport byte [4] bits [1,0]. FSX does not use the data from byte 4, but the programming will have been provided for anyone wanting an example of how to use those interrupts.
Sketch --
/* Sketch - Arduino_ISR_KY040_Mega2560_16_8X40Joystick_Oleg Lowell Bahner February, 2017 This code sends joyStick data when rotary encoders change values. This code adds 16 digital pins for switches plus 2 hardware interrupt pins for switches. This code uses 16 pins on 2 ports on a Mega2560 for 8 KY040 rotary encoders. Other encoders may also work but have not been tested with this code. Each encoder pin is on a separate port (Port B and Port K) Ref: www_atmel.com/Images/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640- 1280-1281-2560-2561_datasheet.pdf 8 Rotary Encoders are connected to the 16 Arduino pins. Each encoder CLK "A" pin is connected to a successive Mega Port B pin [0,1,...7] and the encoder DT "B" pin is connected to the Mega Port K pin [A8,A9,...A15]. The function "processRE()" code is adapted from Oleg Mazurov (2011 Mar 30) Ref: www_circuitsathome.com/mcu/rotary-encoder- interrupt-service-routine-for-avr-micros/ This code uses 2 ISR's (one per port) to sense port interrupts on the two Mega ports. A one detent turn of an encoder takes either the "A" or "B" pin LOW (ground) which triggers the respective Arduino port interrupt. That interrupt triggers the Arduino ISR code which reads the port pins and sets flags for further processing to determine the encoder direction of turn and the number of turns. Each port interrupt is processed independently of other interrupts. Ref: thewanderingengineer.com/2014/08/11/arduino-pin-change-interrupts/ Ref: www_gammon.com.au/forum/?id=11130 For use as USB-HID joyStick: 1) connect potentiometer wiper pins to analog pins A0, A1, A2 2) In loop(), comment out "sendFlag = 0;" line to use axes 3) connect rotary encoders to Mega digital pins 4) test with DEBUG to check joyReport data are correct 5) #undef DEBUG so that joyReport data are not corrupted with Serial.print 6) program USB ATmega16U2 as USB-HID ====================================================== Hardware Wiring Example: KY040 Rotary Encoder #0, pin CLK "A" wired to Ard pin 53 (on port B), and Rotary Encoder #0, pin DT "B" wired to Ard pin A8 (on port K). Arduino digital pins are set as pin-change-interrupt INPUT_PULLUP which trigger the port interrupt when state changes HIGH-LOW or LOW-HIGH. A clockwise (CW) turn of a Rotary Encoder evaluates to -1. A counter-clockwise (CCW) turn of a Rotary Encoder evaluates to 1. Switches are wired to pins 34-49 to ground and will display as joystick pins 16-31. Switches can also be wired to hardware interrupt pins 21 and 20, however those switches will not be sensed if used with FSX. ====================================================== */ // to turn on DEBUG, define DEBUG. Make sure to undef DEBUG for joystick use //#undef DEBUG #define DEBUG // ====================================================== // Sketch Code const byte ENCODERS = 8; // the number of rotary encoders, 4 per pciPort const byte NUM_BUTTONS = 40; // do not change this value. const byte NUM_AXES = 8; // do not change this value. // ====================================================== // add 2 hardware interrupts const byte HWINTS = 2; uint8_t hwArray[HWINTS] = {21, 20}; uint8_t hwFlag[HWINTS]; // ====================================================== // add 16 switches const byte SWITCHES = 16; uint8_t swArray[SWITCHES] = {34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49 }; uint8_t swFlag = 0; // switch data placed into joyReport flag // ====================================================== // define joyReport typedef struct joyReport_t { int16_t axis[NUM_AXES]; uint8_t btnArray[(NUM_BUTTONS + 7) / 8]; // 8 buttons per byte } joyReport_t; joyReport_t joyReport; joyReport_t prevjoyReport; uint8_t sendFlag = 0; // axis data placed into joyReport flag // ====================================================== // add 8 encoders volatile uint8_t reFlag = 0; // encoder data placed into joyReport flag volatile uint8_t pciPortReadFlag [3]; // flag if pciPort was read volatile uint8_t pciPortRead [3]; // save the port interrupt pin value byte // create the ENCODER struct typedef struct { int reAPin; // which RE pin for the "A" side interrupt int reBPin; // which RE pin for the "B" side interrupt byte aBitMask; // which interrupt bit in the A port byte bBitMask; // which interrupt bit in the B port byte pciPortIntA; // which pin-change interrupt port (0, 1, 2) byte pciPortIntB; // which pin-change interrupt port (0, 1, 2) volatile uint8_t reAB; // retained value volatile int8_t reValue; // current RE value -1, 1 volatile int32_t reCounter; // running count for this re[0,1] volatile uint8_t reAPinGPIO; // current value reAPin volatile uint8_t reBPinGPIO; // current value reBPin //volatile uint8_t reAPinPrev; // previous value reAPin //volatile uint8_t reBPinPrev; // previous value reBPin volatile uint8_t reAFlag; // reAFlag volatile uint8_t reBFlag; // reBFlag } RE; // Use these arrays for encoder A pins on Port B and B pins on Port K interrupts volatile RE re[ENCODERS] = { {53, A8}, {52, A9}, {51, A10}, {50, A11}, {10, A12}, {11, A13}, {12, A14}, {13, A15}, }; // end of encoders // Mega 2560 External Interrupt pins, 2, 3, 18, 19, 20, & 21 // Mega 2560 Pin-Change-Interupt Ports and pins // PB7...PB4 DPins 13...10 and PB3...PB0 DPins 50...53 (PCINT7...0) // PK7...PK0 APins A15...A8 (PCINT23...16) // PJ1, PJ0 DPins D14, D15 not used // 8 Rotary Encoders are possible with 16 PCI pins across two 8-pin ports PB and PK // Mega 2560 PCI pins: PB7...PB0 D13...D10 & D50...D53 (no SPI possible) // Mega 2560 PCI pins: PK7...PK0 A15...A8 // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // function prototypes - not necessary but show the // functions used in this sketch // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // pin change interrupts void checkForPinChange (const byte pciPort); ISR (PCINT0_vect); //ISR (PCINT1_vect); ISR (PCINT2_vect); void hw0Interrupt(); void hw1Interrupt(); void setup(); void sendJoyReport(struct joyReport_t *report); void processRE(uint8_t pciPort); void loop (); void print8Bits(uint8_t myByte); void print16Bits(uint16_t myWord); void crPrintHEX(unsigned long DATA, unsigned char numChars); // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // function checkForPinChange() // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // Rotary encoder pin change triggers "pciPort" Arduino ISR. // pciPort==0 is Ard Port B, pciPort==1 is not used, pciPort==2 is Ard Port K. // Once the ISR finishes, interrupts are cleared, then loop() processes the pins. // The pin data are written to the joyReport data structure for sending out USB. void checkForPinChange (const byte pciPort) { //Serial.print ("\n\n =============== pciPort: "); Serial.print (pciPort); //Serial.print ("\n checkForPinChange "); if (pciPort == 0) { pciPortRead [pciPort] = ~PINB; pciPortReadFlag [pciPort] = 1; } else if (pciPort == 1) { pciPortRead [pciPort] = 0b0; pciPortReadFlag [pciPort] = 1; } else if (pciPort == 2) { pciPortRead [pciPort] = ~PINK; pciPortReadFlag [pciPort] = 1; } for (uint8_t reID = 0; reID < ENCODERS; reID++) { if (re[reID].pciPortIntA == pciPort) { if ((digitalRead(re[reID].reAPin) == LOW)) { re[reID].reAFlag = 1; #ifdef DEBUG /* // print what pins are changing and in what order of interrupt Serial.print ("\n, reAFlag: "); Serial.print (re[reID].reAFlag); Serial.print (", pciPortRead [pciPort]: "); print8Bits (pciPortRead [pciPort]); */ #endif } } if (re[reID].pciPortIntB == pciPort) { if ((digitalRead(re[reID].reBPin) == LOW)) { re[reID].reBFlag = 1; #ifdef DEBUG /* // print what pins are changing and in what order of interrupt Serial.print ("\n, reBFlag: "); Serial.print (re[reID].reBFlag); Serial.print (", pciPortRead [pciPort]: "); print8Bits (pciPortRead [pciPort]); */ #endif } } } // end of for each encoder } // end of checkForPinChange // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // ISR (PCINT0_vect) // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // handle pin change interrupt for Mega 2560 pins 53-50 & 10-13 ISR (PCINT0_vect) { cli(); //stop interrupts happening before we read pin values checkForPinChange (PCIE0); sei(); //restart interrupts } // end of PCINT0_vect // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // ISR (PCINT1_vect) // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // handle pin change interrupt not used //ISR (PCINT1_vect) //{ // cli(); //stop interrupts happening before we read pin values // checkForPinChange (PCIE1); // sei(); //restart interrupts //} // end of PCINT1_vect // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // ISR (PCINT2_vect) // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // handle pin change interrupt for Mega 2560 pins A8-A15 ISR (PCINT2_vect) { //Serial.println ("ISR (PCINT2_vect) ..."); cli(); //stop interrupts happening before we read pin values checkForPinChange (PCIE2); sei(); //restart interrupts } // end of PCINT2_vect // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // hw0Interrupt() // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // hwArray[0] pin triggers this hardware interrupt void hw0Interrupt() { //Serial.println ("hw0Interrupt() ..."); cli(); //stop interrupts happening before we read pin values hwFlag[0] = 0; // clear the flag if (digitalRead(hwArray[0] < 1)) { // pin is LOW hwFlag[0] = 1; // set the flag } sei(); //restart interrupts } // end of hw0Interrupt() // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // hw1Interrupt() // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // hwArray[1] pin triggers this hardware interrupt void hw1Interrupt() { //Serial.println ("hw1Interrupt() ..."); cli(); //stop interrupts happening before we read pin values hwFlag[1] = 0; // clear the flag if (digitalRead(hwArray[1] < 1)) { // pin is LOW hwFlag[1] = 1; // set the flag } sei(); //restart interrupts } // end of hw1Interrupt() // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // function setup // ++++++++++++++++++++++++++++++++++++++++++++++++++++ void setup() { Serial.begin (115200); #ifdef DEBUG Serial.println(F("\n Starting Mega 2560 Pin-Change-Interrupt Joystick")); #endif //---------------------------------------------------------- // Arduino pin-change-interrupts configuration //---------------------------------------------------------- // // clear any outstanding Arduino pin-change-interrupts PCIFR |= bit (PCIF0) | bit (PCIF1) | bit (PCIF2); for (uint8_t reID = 0; reID < ENCODERS; reID++) { // set interrupt pins to INPUT_PULLUP pinMode (re[reID].reAPin, INPUT_PULLUP); pinMode (re[reID].reBPin, INPUT_PULLUP); // Create a Mask with the pin bit = 1 re[reID].aBitMask = digitalPinToBitMask (re[reID].reAPin); re[reID].bBitMask = digitalPinToBitMask (re[reID].reBPin); // Which ISR for each interrupt pin (0=PCIE0, 1=PCIE1, 2=PCIE2) re[reID].pciPortIntA = digitalPinToPCICRbit (re[reID].reAPin); re[reID].pciPortIntB = digitalPinToPCICRbit (re[reID].reBPin); // Activate this pin-change interrupt bit (eg. PCMSK0, PCMSK1, PCMSK2) /* Ref: thewanderingengineer.com/2014/08/11/arduino-pin-change-interrupts/ // Example PCMSK definitions: PCMSK0 |= 0b00000011; // turn on pins PB0 & PB1, PCINT0 & PCINT1, pins D8, D9 PCMSK1 |= 0b00010000; // turn on pin PC4, pciPort is PCINT12, pin A4 PCMSK2 |= 0b00001100; // turn on pins PD2 & PD3, PCINT18 & PCINT19, pins D2, D3 */ PCMSK0 |= 0b11111111; // turn on port b pins PCINT0 to PCINT7 PCMSK1 |= 0b00000000; // not used PCMSK2 |= 0b11111111; // turn on port k pins PCINT16 to PCINT23 // Enable this pin-change interrupt /* Ref: thewanderingengineer.com/2014/08/11/arduino-pin-change-interrupts/ // Example PCICR definitions: PCICR |= 0b00000001; // turn on port b PCICR |= 0b00000010; // turn on port c PCICR |= 0b00000100; // turn on port d PCICR |= 0b00000111; // turn on all ports */ PCICR |= bit (digitalPinToPCICRbit (re[reID].reAPin)); //Serial.print ("\nPCICR A: "); print8Bits (PCICR); PCICR |= bit (digitalPinToPCICRbit (re[reID].reBPin)); //Serial.print (", PCICR B: "); print8Bits (PCICR); #ifdef DEBUG // examine the Arduino ISR setup Serial.print ("\n\nMega 2560: "); Serial.print ("\n...re[reID].reAPin: "); Serial.print (re[reID].reAPin); Serial.print (", re[reID].reBPin: "); Serial.print (re[reID].reBPin); Serial.print (", re[reID].aBitMask: "); print8Bits(re[reID].aBitMask); Serial.print (", re[reID].bBitMask: "); print8Bits(re[reID].bBitMask); Serial.print ("\n...re[reID].pciPortIntA: "); Serial.print (re[reID].pciPortIntA); Serial.print (", re[reID].pciPortIntB: "); Serial.print (re[reID].pciPortIntB); Serial.print (", PCICR: "); print8Bits (PCICR); Serial.print (", PCMSK0: "); print8Bits (PCMSK0); Serial.print (", PCMSK1: "); print8Bits (PCMSK1); Serial.print (", PCMSK2: "); print8Bits (PCMSK2); #endif // initalize the 8 encoders on each port for (uint8_t reID = 0; reID < ENCODERS; reID++) { //re[reID].reAPin = RE pin for the "A" side interrupt //re[reID].reBPin = RE pin for the "B" side interrupt re[reID].reAB = 3; // old_AB re[reID].reValue = 0; // current RE value -1, 1 re[reID].reCounter = 0; // running count for this re[0,1] re[reID].reAPinGPIO = 0; // current value reAPin re[reID].reBPinGPIO = 0; // current value reBPin //re[reID].reAPinPrev = 0; // previous value reAPin //re[reID].reBPinPrev = 0; // previous value reBPin re[reID].reAFlag = 0; // reAFlag re[reID].reBFlag = 0; // reBFlag } // end of setup encoders } // end of Arduino interrupts for each expander // ------------------------------------------------- // set up two hardware interrupt pins (Mega 2560 = 21, 20, 19, 18, 2, 3) for (uint8_t hw = 0; hw < HWINTS; hw ++) { pinMode (hwArray[hw], INPUT_PULLUP); hwFlag[hw] = 0; // clear flag } attachInterrupt(digitalPinToInterrupt(hwArray[0]), hw0Interrupt, CHANGE); attachInterrupt(digitalPinToInterrupt(hwArray[1]), hw1Interrupt, CHANGE); // ------------------------------------------------- // set up 16 switch pins for (uint8_t sw = 0; sw < SWITCHES; sw++) { pinMode (swArray[sw], INPUT_PULLUP); } } // end of setup // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // function sendJoyReport() // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // Send an HID report to the USB interface void sendJoyReport(struct joyReport_t *report) { #ifndef DEBUG //Serial.write((uint8_t *)report, sizeof(joyReport_t)); // do not send duplicate values // if (memcmp( report, &prevjoyReport, sizeof( joyReport_t ) ) != 0) { Serial.write((uint8_t *)report, sizeof(joyReport_t)); memcpy ( &prevjoyReport, report, sizeof( joyReport_t ) ); } // // end do not send duplicate values #else if (memcmp( report, &prevjoyReport, sizeof( joyReport_t ) ) != 0) { //Serial.write((uint8_t *)report, sizeof(joyReport_t)); memcpy ( &prevjoyReport, report, sizeof( joyReport_t ) ); //} // dump human readable output for debugging Serial.println("\n"); for (uint8_t ind = 0; ind < NUM_AXES; ind++) { Serial.print("axis["); Serial.print(ind); Serial.print("]= "); Serial.print(report->axis[ind]); Serial.print(" "); } Serial.println(); for (uint8_t ind = 0; ind < NUM_BUTTONS / 8; ind++) { Serial.print("btnArray["); Serial.print(ind); Serial.print("]= "); print8Bits(report->btnArray[ind]); //Serial.print(report->btnArray[ind], HEX); Serial.print(" "); } } else { //Serial.print("\n ...No Change in joyReport..."); } #endif } // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // function processRE() // ++++++++++++++++++++++++++++++++++++++++++++++++++++ void processRE(uint8_t pciPort) { /* * Process the encoders on each expander * 4 ENCODERS per Arduino Port Interrupt * for (uint8_t reID = 0; reID < ENCODERS; reID++) { re[reID].reAPin = read array; // which RE pin for the "A" side interrupt re[reID].reBPin = read array; // which RE pin for the "B" side interrupt re[reID].reValue = 0; // current RE value -1, 1 re[reID].reCounter = 0; // running count for this re[0,1] re[reID].reAPinGPIO = 0; // current value reAPin re[reID].reBPinGPIO = 0; // current value reBPin re[reID].reAPinPrev = 0; // previous value reAPin re[reID].reBPinPrev = 0; // previous value reBPin re[reID].reAFlag = 0; // reAFlag re[reID].reBFlag = 0; // reBFlag } // end of setup encoders */ // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // this code is adapted from Oleg Mazurov (2011 Mar 30) // Ref: www_circuitsathome.com/mcu/rotary-encoder- // interrupt-service-routine-for-avr-micros/ // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // This code determines the direction of turn and cumulative count // for up to 8 rotary encoders on 1 Arduino Mega 2560 using 2 8-pin ports. // A turn of a rotary encoder on a port // causes a series of pin change interrupts // that triggers this code for each interrupt. // For the KY040 Rotary Encoder: // pin CLK ==> RE Pin reAPin, pin DT ==> RE Pin reBPin // The reAPin goes to port B [0...7] pins (53, 52, 51, 50, 10, 11, 12, 13) // The reBPin goes to port K [0...7] pins (A8, A9, A10, A11, A12, A13, A14, A15) // This arrangement gives a separate port interrupt for each pin of an encoder static const int8_t enc_states [] PROGMEM = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0}; //encoder lookup table // cycle through each of 8 rotary encoders // with pin A on Port B and pin B on Port K for (uint8_t reID = 0; reID < ENCODERS; reID++) { // recall retained value of old_AB uint8_t old_AB = re[reID].reAB; uint8_t encport = 0; int8_t dir; // get the expander:encoder:pinA, pinB values at time of interrupt // eg, Port_B [0] & Port_K [0], Port_B [5] & Port_K [5] // for encoders with pin A on Port B and pin B on Port K re[reID].reAPinGPIO = bitRead(pciPortRead [re[reID].pciPortIntA], reID); re[reID].reBPinGPIO = bitRead(pciPortRead [re[reID].pciPortIntB], reID); // check if this RE sent a signal, if not, ignore it if ((re[reID].reAPinGPIO > 0) || (re[reID].reBPinGPIO > 0)) { old_AB <<= 2; //remember previous RE state and shift-left by 2 bits // copy encoder pin values to encport bits 1,0 if (re[reID].reAPinGPIO > 0) bitSet(encport, 0); if (re[reID].reBPinGPIO > 0) bitSet(encport, 1); //copy bits 1,0 to old_AB old_AB |= encport & 0x03; // use index to obtain direction and state dir = pgm_read_byte(&(enc_states[( old_AB & 0x0f )])); //check if at detent and transition is valid // dir=1 CCW, dir=-1 CW if ( dir && ( encport == 3 )) { re[reID].reValue = dir; re[reID].reCounter = re[reID].reCounter - dir; reFlag = 1; // joyReport data changed flag // place the 8-encoder data into // joyReport [ 0 and 1 ] if (dir < 0) bitSet(joyReport.btnArray[1], reID); // CW data if (dir > 0) bitSet(joyReport.btnArray[0], reID); // CCW data // btnArray[2] and btnArray[3] are available for use with 16 switches // connected to other Mega 2560 pins (code has to be added) #ifdef DEBUG Serial.print ("\n RE["); Serial.print (reID); Serial.print ("]"); Serial.print (", encport: "); print8Bits (encport); Serial.print (", old_AB: "); print8Bits (old_AB); Serial.print (", enc_states["); Serial.print (old_AB & 0x0f); Serial.print ("]: "); Serial.print (" dir: "); Serial.print (dir); if ( dir == 1 ) { Serial.print (", CCW"); } else { Serial.print (", CW"); } Serial.print (", Count "); Serial.print ( re[reID].reCounter); #endif } // end if (dir... } // end if ((re[reID].reAPinGPIO... // retain settings for this encoder for next indent comparison re[reID].reAB = old_AB; } // end encoder for (uint8_t reID... } // end code adapted from Oleg Mazurov (2011 Mar 30) // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // function loop // ++++++++++++++++++++++++++++++++++++++++++++++++++++ void loop () { // This code runs Arduino Mega 2560 pin-change-interrupt ISR's // and then takes action (checkForPinChange()) for the Arduino pins that // went LOW or HIGH. Port B and Port K pins are set as pin-change-interrupts. // checkForPinChange() flags that a pin interrupt (re[reID].reAFlag>0) has occurred // and each pciPortReadFlag[pciPort] is set if a port pin // changed state. // checkForPinChange() reads the inverse value of the port pins // (pciPortRead[pciPort] = ~PINB;) and (pciPortRead[pciPort] = ~PINK;) // processRE(pciPort) writes the encoder values into joyReport[0], [1]. // process Rotary Encoders for (uint8_t pciPort = 0; pciPort < 3; pciPort++) { if (pciPortReadFlag [pciPort] > 0) { processRE(pciPort); } } // process the switches for ON and OFF swFlag = 1; // process switches attached to hardware interrupts for (uint8_t hw = 0; hw < HWINTS; hw++) { if (hwFlag[hw] > 0) { // test if interrupt occurred with pin LOW if (digitalRead(hwArray[hw]) < 1) { // check if pin is still LOW bitSet(joyReport.btnArray[4], hw); // switch is ON } } } // process 16 switches and place their values into joyReport[2], [3]. // poll each switch to determine its state // sendJoyReport() will determine if there is a change in one or more switches uint8_t bt = 0; // bit number uint8_t pn = 2; // port number for (uint8_t sw = 0; sw < SWITCHES; sw++) { if (digitalRead(swArray[sw]) < 1) { if (sw < 8) { bt = sw; // 0...7 pn = 2; } else { bt = sw - 8; // 8...15 ==> 0...7 pn = 3; } bitSet(joyReport.btnArray[pn], bt); } } /* Axes connect to Analog pins A0, A1, A2...A7 */ /* Arduino UNO has 6 analog pins of 8 possible. Set pin to 0 if not used */ /* Ground any analog ports that are not connected to potentiometers to reduce noise */ uint8_t axisCount = 0; // set the number of axes you want to use, 3=[0,1,2] for (uint8_t axis = 0; axis < axisCount; axis++) { int tmp = joyReport.axis[axis]; // copy previous axis value // Average 5 readings of port to get better values from noisy potentiometers // Use >5 to average more readings per pot long sumAxis = 0; int avg = 0; int count = 5; for (int i = 0; i < count; i++) { sumAxis = sumAxis + analogRead(axis); } avg = sumAxis / count; joyReport.axis[axis] = map(avg, 0, 1023, -32768, 32767 ); // flag change in axis if avg reading changes by > 100 if (abs(joyReport.axis[axis] - tmp) > 100) sendFlag = 1; } //Set un-used analog pins to 0 to reduce spurious values in joyReport. for (uint8_t i = axisCount; i < 8; i++) { joyReport.axis[i] = 0; } // for now, turn off axis data for testing digital inputs // comment out "sendFlag = 0;" line to use axes sendFlag = 0; if ((swFlag > 0) || (reFlag > 0) || (sendFlag > 0)) { //Send Data to HID sendJoyReport(&joyReport); // clear the joyReport sendFlag = 0; reFlag = 0; swFlag = 0; joyReport.btnArray[0] = 0b0; joyReport.btnArray[1] = 0b0; joyReport.btnArray[2] = 0b0; joyReport.btnArray[3] = 0b0; joyReport.btnArray[4] = 0b0; } // clear pci port read flags for (uint8_t pciPort = 0; pciPort < 3; pciPort++) { pciPortReadFlag [pciPort] = 0; } //} delay (10); // give loop something to do while idle } #ifdef DEBUG // ++++++++++++++++++++++++++++++++++++++++++++++++++++ // print binary8 binary16 and hex functions // ++++++++++++++++++++++++++++++++++++++++++++++++++++ //--------------------------------------------------------------------------------- // print 8-bit byte as 8 bit binary string //--------------------------------------------------------------------------------- void print8Bits(uint8_t myByte) { for (uint8_t mask = 0x80; mask; mask >>= 1) { if (mask & myByte) Serial.print('1'); else Serial.print('0'); } } //--------------------------------------------------------------------------------- // print 16-bit word as 2 8-bit bit binary strings with space //--------------------------------------------------------------------------------- void print16Bits(uint16_t myWord) { print8Bits(highByte(myWord)); Serial.print (" "); print8Bits(lowByte(myWord)); /* for (uint16_t mask = 0x8000; mask; mask >>= 1) { if (mask & myWord) Serial.print('1'); else Serial.print('0'); } */ } //--------------------------------------------------------------------------------- // crPrintHEX print value as hex with specified number of digits //--------------------------------------------------------------------------------- void crPrintHEX(unsigned long DATA, unsigned char numChars) { unsigned long mask = 0x0000000F; mask = mask << 4 * (numChars - 1); Serial.print("0x"); for (unsigned int eID = numChars; eID > 0; --eID) { Serial.print(((DATA & mask) >> (eID - 1) * 4), HEX); mask = mask >> 4; } Serial.print(" "); } #endif
(February 2, 2017)
No comments:
Post a Comment