Jump to content

looking for some help with button matrix programming with teensyduino


goodpoints

Recommended Posts

First off, be warned I'm next to clueless when it comes to building electronic and programming.

 

That said, the current project I'm attempting is a USB conversion (using a Teensy 2++) of a Thrustmaster F-16 FLCS stick and Mark II WCS throttle with the stick grip being mounted on a MS FF2. So only the buttons from the FLCS are being used, and they're read via the original shift registers and will be plugged into the same Teensy that the throttle button matrix and pot I have will be connected to.

 

I've completed the first half of the project, the F-16 mounted on my FF2 and all the buttons are working. It works great so far, the throttle however has got me stumped. I've wired the push buttons and switches in a 6-row diode matrix scheme to the best of my abilities (with some very gracious help from sokol1) but I can't figure out how to code it in teensyduino.

 

So the throttle unit has:

6 push buttons

1 3-way ON-OFF-MOM rocker

1 3-way ON-OFF-ON rocker

2 2-way ON-(NONE)-ON DPDT switches

 

I still don't entirely understand how the switches can work, but I believe I have it wired so the DPDTs will work like: switch up = FUNC1 ON, FUNC2 OFF, switch down = FUNC1 OFF, FUNC2 ON. And I believe I have the rockers wired so the UP and DOWN positions are 2 seperate inputs and the CENTER position is OFF. (the idea being that I could use them for something like the A-10's Boat or China Hat Switch with some possible lua editing or macros)

 

Here's a schematic of what I have so far, text is color-coded by wire (black slashthrough for wires with black stripes):

 

tuBE17K.png

 

and the current teensy connections ('S' denotes the 5 wires for the stick handle):

 

4d3dMYS.png

 

Note, I haven't hooked up the switches in the base yet as I'm just trying to get the buttons to work so I can then experiment more with how I want the switches set up.

 

So, as I understand it, my current matrix scheme is:

 

B1,D1 |S1,D2 | S1,D3

B2,D1 |

B3,D1 |

B4,D1 |

B5,D1 |

B6,D1 |

 

 

I'm trying to work off a Teensy 3.0 sketch intended for a TM F-22 stick (same wiring as the F-16) + Suncom SFS throttle I found on this blog with some slight modification needed to get the stick shift registers working. I'm just not understanding at all how the code for the throttle matrix works, I've tried just changing the values for Column/Row amounts and pins but the throttle is unresponsive. (though the stick still works) The examples for coding a keypad in matrix seemed much simpler than this and like something I could manage, but I just have no idea what I'm looking at now and I haven't been able to find much relevant info that would help someone with as little programming knowledge as me.

 

If someone would be able to help me understand what I'm looking at and how to modify it for my setup, I would be just overjoyed to finally have my HOTAS up and running.

 

The sketch (with my current modifications as described before) is as follows:

 

/* USB Thrustmaster F22 & Suncom SFS Conversion
  for Teensy 3.1 MCU
  You must select Joystick from the "Tools > USB Type" menu

  Throttle Buttons are muxed into rows and columns
  SEL1, 2, 3 (columns) on Pins 17, 18, 19
  Rows on pins 27, 0, 1, 2, 3, 4 - all one register
  Buttons read LOW on activation, so initial state is HIGH

  Joystick Buttons are muxed into shift registers, use the SPI protocol to read them
  F22 Shift-reg are 3 x HCF4021BE
  Wiring from Handle to SPI as follows
  Brown:  +5v (VIN)
  Green:  GND
  Orange: SCLK (pin13)
  Red:    SS (pin10)
  Yellow: MISO (pin12)

  Wiring from Pots as follows - I have X & Y on A2 & A3. I have Left & Right throttles from Suncom SFS Throttle to A0 & A1
  1:          3.3v
  2: (wiper)  Analog Inputs
  3:          GND

*/

#include <SPI.h>
const int ss = 20;

unsigned int buttonInputs1;   // data read from SPI
unsigned int buttonInputs2;
unsigned int buttonInputs3;
unsigned int pitch, roll, throttle1, throttle2;

// 2-dimensional array of row pin numbers:
const int row[6] = { 27, 0, 1, 2, 3, 4 };

// 2-dimensional array of column pin numbers:
const int col[3] = { 26, 25, 24 };

int buttonstate[24];
int buttonprevstate[24];

/*
const int buttonlookup[6][3] = { {19,20,21,22,23,24,25,26},
                                {27,28,29,30,,,,},
                                {31,32

*/

#define PINKY  !(buttonInputs1 & 0x80)    /* Pinky Switch */
#define TG1    !(buttonInputs1 & 0x40)    /* Trigger 1 */
#define TG2    !(buttonInputs1 & 0x20)    /* Trigger 2 */
#define S1     !(buttonInputs1 & 0x10)    /* Nose Wheel Steering */
#define PADDLE !(buttonInputs1 & 0x08)    /* Paddle Switch */
#define THUMB  !(buttonInputs1 & 0x04)    /* Pickle */

#define H1D  !(buttonInputs2 & 0x80)    /* HAT */
#define H1R  !(buttonInputs2 & 0x40)
#define H1U  !(buttonInputs2 & 0x20)
#define H1L  !(buttonInputs2 & 0x10)
#define H4U  !(buttonInputs2 & 0x08)    /* Castle */
#define H4L  !(buttonInputs2 & 0x04)
#define H4D  !(buttonInputs2 & 0x02)
#define H4R  !(buttonInputs2 & 0x01)
#define H3D  !(buttonInputs3 & 0x80)    /* Weap */
#define H3R  !(buttonInputs3 & 0x40)
#define H3U  !(buttonInputs3 & 0x20)
#define H3L  !(buttonInputs3 & 0x10)
#define H2D  !(buttonInputs3 & 0x08)    /* Target */
#define H2R  !(buttonInputs3 & 0x04)
#define H2U  !(buttonInputs3 & 0x02)
#define H2L  !(buttonInputs3 & 0x01)

void setup() {
 pinMode (ss, OUTPUT);
 SPI.begin();
 for (int x = 0; x < 24; x++)  {
   buttonstate[x] = 1;
   buttonprevstate[x] = 1;
 }
 for (int x = 0; x < 3; x++) {
   pinMode(col[x], OUTPUT);
 }
 for (int x = 0; x < 8; x++) {
   pinMode(row[x], INPUT_PULLUP);
 }
 Joystick.useManualSend(true);
}


void loop() {
 SPISettings settingsA(1000000, MSBFIRST, SPI_MODE0);

 SPI.beginTransaction(settingsA);
 digitalWrite(ss, LOW);
 // reading only, so data sent does not matter
 buttonInputs1 = SPI.transfer(0x00);
 buttonInputs2 = SPI.transfer(0x00);
 buttonInputs3 = SPI.transfer(0x00);
 digitalWrite(ss, HIGH);
 SPI.endTransaction();

 digitalWrite(col[0], 0);
 digitalWrite(col[1], 1);
 digitalWrite(col[2], 1);
 delay(10);
 for (int y = 0; y < 4; y++) {
   if (digitalRead(row[y]) == 0)  {
     buttonstate[y] = 0;
     if (buttonstate[y] != buttonprevstate[y])
       Joystick.button(19 + y, 1);
   } else  {
     buttonstate[y] = 1;
     if (buttonstate[y] != buttonprevstate[y])
       Joystick.button(19 + y, 0);
   }
   buttonprevstate[y] = buttonstate[y];
 }

 digitalWrite(col[0], 1);
 digitalWrite(col[1], 0);
 digitalWrite(col[2], 1);
 delay(10);
 for (int y = 0; y < 8; y++) {
   if (digitalRead(row[y]) == 0)  {
     buttonstate[8 + y] = 0;
     if (buttonstate[8 + y] != buttonprevstate[8 + y])
       Joystick.button(23 + y, 1);
   } else  {
     buttonstate[8 + y] = 1;
     if (buttonstate[8 + y] != buttonprevstate[8 + y])
       Joystick.button(23 + y, 0);
   }
   buttonprevstate[8 + y] = buttonstate[8 + y];
 }

 digitalWrite(col[0], 1);
 digitalWrite(col[1], 1);
 digitalWrite(col[2], 0);
 delay(10);
 for (int y = 0; y < 8; y++) {
   if (digitalRead(row[y]) == 0 && y > 3)  {
     buttonstate[16 + y] = 0;
     if (buttonstate[16 + y] != buttonprevstate[16 + y])
       Joystick.button(25 + y, 1);
   } else  {
     buttonstate[16 + y] = 1;
     if (buttonstate[16 + y] != buttonprevstate[16 + y])
       Joystick.button(25 + y, 0);
   }
   buttonprevstate[16 + y] = buttonstate[16 + y];
 }


 roll = analogRead(2);
 pitch = analogRead(3);
 throttle1 = analogRead(0);
 throttle2 = analogRead(1);

 // throttle = analogRead(3);
 digitalWrite(ss, HIGH);
 Joystick.button(1,  TG1);
 Joystick.button(2,  THUMB);
 Joystick.button(3,  PINKY);
 Joystick.button(4,  PADDLE);
 Joystick.button(5,  S1);
 Joystick.button(6,  TG2);
 Joystick.button(7,  H2U);
 Joystick.button(8,  H2R);
 Joystick.button(9,  H2D);
 Joystick.button(10, H2L);
 Joystick.button(11, H3U);
 Joystick.button(12, H3R);
 Joystick.button(13, H3D);
 Joystick.button(14, H3L);
 Joystick.button(15, H4U);
 Joystick.button(16, H4R);
 Joystick.button(17, H4D);
 Joystick.button(18, H4L);

 Joystick.X(roll);
 Joystick.Y(pitch);
 Joystick.sliderLeft(throttle1);
 Joystick.sliderRight(throttle2);

 int angle = -1;

 if (H1U) {
   if (H1R) {
     angle = 45;
   } else if (H1L) {
     angle = 315;
   } else {
     angle = 0;
   }
 } else if (H1D) {
   if (H1R) {
     angle = 135;
   } else if (H1L) {
     angle = 225;
   } else {
     angle = 180;
   }
 } else if (H1R) {
   angle = 90;
 } else if (H1L) {
   angle = 270;
 }
 Joystick.hat(angle);
 Joystick.send_now();
}

 

Thanks,

Jacob


Edited by goodpoints
Link to comment
Share on other sites

Your matrix wiring looks ok (some of the signal names / color coding do not match up between the pictures, e.g. the throttle base has a "D4" that is not found anywhere else, but I assume those are oversights in the schematics and that the actual wiring is correct.)

 

What is the behavior with the current sketch?

If I interpret the code correctly, the first four buttons will work, the last two won't. (Disclaimer: I am not familiar with the Teensy, I just have some experience with C++ and the Arduino platform in general.)

 

To understand what is going on here, let's take a closer look at one of the three parts responsible for scanning the button matrix:

 digitalWrite(col[0], 0);
 digitalWrite(col[1], 1);
 digitalWrite(col[2], 1);
 delay(10);
 for (int y = 0; y < 4; y++) {
   if (digitalRead(row[y]) == 0)  {
     buttonstate[y] = 0;
     if (buttonstate[y] != buttonprevstate[y])
       Joystick.button(19 + y, 1);
   } else  {
     buttonstate[y] = 1;
     if (buttonstate[y] != buttonprevstate[y])
       Joystick.button(19 + y, 0);
   }
   buttonprevstate[y] = buttonstate[y];
 }

 

The first three lines make sure that only the column we want to scan (column 0 in this case, corresponding to pin 26) is logic low, the other two are set to logic high. The delay(10) is probably not necessary, but helps to avoid problems when you have very long wires, where the stray capacitance of the wire can slow down the signal transition.

 

The for loop now scans through the rows. Because y goes from 0 to 3, it will only scan the first four rows. If it reads a logic 0, the button is pressed. If it reads a logic 1, the button is not pressed. In both cases, it stores the value in the buttonstate array, and if it has changed from the previous value, it will tell the USB joystick code to send a new button value to the PC.

 

The other two code blocks work the same, but they scan the other two rows. They also add different values to 'y' so they don't use the same entries in the buttonstate array.

 

The code is more verbose than it needs to be, but it should work.

 

Note that the next two sections scan eight rows, but your rows array only has six entries. This means you are accessing memory beyond the bounds of the array, which can lead to undefined behavior, but I think this won't break anything in this specific case because (a) you never write out of bounds and (b) the value you get is only passed to digitalRead(), which will probably determine it is not a valid pin number and return 0 or something.

 

In general though, reading (or worse, writing) to an array outside of its bounds (for example, declaring "char foo[6]" and then accessing "foo[6]" -- because with 6 entries, foo only has elements foo[0] through foo[5]!) is very dangerous, because it leads to very hard to find bugs. On a microcontroller, there is no operating system that may notice this and stop the program with a segmentation fault error -- your code will just keep running, and the effects of that memory access can be anything from no problem at all (because the memory location beyond your array happens to contain the right value) to causing arbitrary behavior (because the memory location you overwrote contained a return address, causing program execution to jump somewhere it was not supposed to). Worse, the memory layout (and thus the effects of out-of-bounds array access bugs) can change when you are working on entirely different parts of the code, so you might add a variable somewhere else which will break the program.

Link to comment
Share on other sites

  • 2 weeks later...

Thanks for your reply and sorry for the late response,

 

Ian;2488709']Your matrix wiring looks ok (some of the signal names / color coding do not match up between the pictures' date=' e.g. the throttle base has a "D4" that is not found anywhere else, but I assume those are oversights in the schematics and that the actual wiring is correct.)[/quote']

 

I mentioned below the Teensy diagram that I haven't yet wired any of the switches in the throttle base as I'm just trying to get the grip working first. I'll likely merge the D2, 3, 5, & 6 lines if it works when I get to that point.

 

Ian;2488709']What is the behavior with the current sketch?

If I interpret the code correctly, the first four buttons will work, the last two won't. (Disclaimer: I am not familiar with the Teensy, I just have some experience with C++ and the Arduino platform in general.)

 

Thanks a ton for your clarifications on the code, I have a better understanding of what's going on now. Now I've just gone back to basics though and am just trying to get the six push buttons to work. So, if it's indeed possible, I've attempted to modify the code for a 6x1 matrix. However, my results are the same as my previous attempts: I get no response at all in Windows joystick config from the throttle buttons, while the joystick buttons all still work. (should I be using something different to analyze the buttons? I don't understand how to use Arduino's serial monitor) Here's the code sections I modified to try using a 6x1 matrix:

 

// 2-dimensional array of row pin numbers:
const int row[6] = { 0,1,2,3,4,5 };

// 2-dimensional array of column pin numbers:
const int col[1] = { 26 };

int buttonstate[24];
int buttonprevstate[24];

/*
const int buttonlookup[6][1] = { {19,20,21,22,23,24,25,26},
                                {27,28,29,30,,,,},
                                {31,32

 

digitalWrite(col[0], 0);
 for (int y = 0; y < 6; y++) {
   if (digitalRead(row[y]) == 0)  {
     buttonstate[y] = 0;
     if (buttonstate[y] != buttonprevstate[y])
       Joystick.button(19 + y, 1);
   } else  {
     buttonstate[y] = 1;
     if (buttonstate[y] != buttonprevstate[y])
       Joystick.button(19 + y, 0);
   }
   buttonprevstate[y] = buttonstate[y]

 

 

Just to make sure I didn't mess up the diode wiring: I'm using IN4148 diodes and the cathode end is the side with the black line? So the black line should be closer to the button terminal?

 

I actually decided to just get an SFS Throttle as well as I found one for $35 on ebay and it's a huge improvement in functionality. I'd still like to get the WCS working though. (could always use it as mixture control or something) Unfortunately I'm having issues with the SFS too, some buttons are constantly on and some are reading as the same button. But since all the SFS + the F16 buttons would exceed the 32 button limit, I just decided to order another teensy board and make them separate devices. There's already a thread on SFS usb conversion though, so I think I'll post in there if I can't figure that out.

 

- Jacob


Edited by goodpoints
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...