Special Aircraft Service

Please login or register.

Login with username, password and session length
Advanced search  
Pages: 1 2 3 [4] 5 6 7   Go Down

Author Topic: Mike's new project  (Read 20193 times)

0 Members and 2 Guests are viewing this topic.

max_thehitman

  • SAS~Area51
  • Modder
  • member
  • Offline Offline
  • Posts: 8976
  • Beer...Girls...IL2+Mods!
Re: Mike's new project
« Reply #36 on: April 30, 2018, 07:51:12 PM »


This sounds very cool and amazing Storebror. Very cool indeed!
Congratulations on the project
Logged
Everything I like is either illegal, immoral or fattening ! Welcome to SAS1946

SAS~Storebror

  • Editor
  • member
  • Offline Offline
  • Posts: 23561
  • Taking a timeout
    • STFU
Re: Mike's new project
« Reply #37 on: May 02, 2018, 02:21:43 AM »

One more hint about VJoy:
Depending on which other Joystick Devices you have plugged into your PC, the VJoy installation process might get stuck at 100%, see report here:
https://vjoy.freeforums.net/thread/50/installation-stuck-finish-error?page=2&scrollTo=245

This is no real issue. If the progress bar of your VJoy installation reached 100% and is stuck there for a minute, just reboot your PC and everything will be fine.

Alright, small update again.
I've got my Teensy to be recognized as 32-Button, 5 Axis and 4x8-way hat controller now.
The latter is necessary because IL-2 1946 cannot distinguish between hats on different controllers, it throws all of them into one according to their index, so I need 3 at least to move them out of the way of each other, and 4 (windows max. number for hats) to be on the safe side.

Before I mess up my code in the attempt to turn the Teensy into a 2-Joystick-Device, let me dump all the related sources here.

This is the so called "Arduino Sketch" holding the internal logic. I called it "Mike_s_Controller_Box_v001":
Code: [Select]
#include "ButtonMatrix.h"

const byte ROWS = 10; //four rows
const byte COLS = 8; //three columns
byte rowPins[ROWS] = {8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {27, 0, 1, 2, 3, 4, 5, 7}; //connect to the column pinouts of the keypad

int16_t buttonDirs[8] = {0, 45, 90, 135, 180, 225, 270, 315};
ButtonMatrix buttonmatrix = ButtonMatrix( rowPins, colPins, ROWS, COLS );

unsigned long loopCount;
unsigned long startTime;
unsigned long ledTime;
unsigned long joystickTime;
const int ledPin = 6;
String msg;

const int numButtons = 80;

void setup() {
  Serial.begin(9600);
  Joystick.useManualSend(true);
  Serial.println("Begin Mike's Switchbox TEST");
  Serial.print("Maximum Number of Buttons: ");
  Serial.println(buttonmatrix.numButtons());
  loopCount = 0;
  startTime = millis();
  ledTime = millis();
  joystickTime = millis();
  pinMode(ledPin, OUTPUT);
  msg = "";

  buttonmatrix.getButtons();
  for (int i = 0; i < numButtons; i++) {
    if (buttonmatrix.button[i].bstate == PRESSED || buttonmatrix.button[i].bstate == HOLD) {
      Joystick.button(i + 1, 1);
    } else {
      Joystick.button(i + 1, 0);
    }
  }

  Joystick.send_now();

  Joystick.X(512);
  Joystick.Y(512);
  Joystick.Z(512);
  Joystick.Xrotate(512);
  Joystick.Yrotate(512);
  Joystick.hat(1, -1);
  Joystick.hat(2, -1);
  Joystick.hat(3, -1);

  for (int i = 0; i < 12; i++) {
    Serial.print("joystick_report_data[");
    Serial.print(i);
    Serial.print("]=");
    Serial.println(Joystick.get_joystick_report_data(i));
  }

  buttonmatrix.addEventListener(buttonEvent); // Add an event listener for this keypad
}

byte allButtons[numButtons];
byte prevButtons[numButtons];
int angle = 0;
int analog = 0;

void loop() {
  loopCount++;
  if ( (millis() - startTime) > 30000 ) {
    Serial.print("Average loops per second = ");
    Serial.println(loopCount / 30);
    startTime = millis();
    loopCount = 0;
  }

  if ( (millis() - ledTime) > 1 ) {
    ledTime = millis();
    digitalWrite(ledPin, LOW);
  }
  buttonmatrix.getButtons();

  if ( (millis() - joystickTime) > 10 ) {
    joystickTime = millis();
    Joystick.X(analogRead(0));
    Joystick.Y(analogRead(1));
    Joystick.Z(analogRead(2));
    Joystick.Xrotate(analogRead(3));
    Joystick.Yrotate(analogRead(4));
    Joystick.send_now();
  }
  delay(5);
}

void buttonEvent(ButtonEvent listIndex) {
  switch (buttonmatrix.button[listIndex].bstate) {
    case PRESSED:
      msg = " PRESSED.";
      break;

    case RELEASED:
      msg = " RELEASED.";
      break;

    case HOLD:
    case IDLE:
    default:
      return;
  }
  ledTime = millis();
  digitalWrite(ledPin, HIGH);
  Serial.print("Button ");
  Serial.print(buttonmatrix.button[listIndex].bindex);
  Serial.println(msg);
  int buttonIndex = buttonmatrix.button[listIndex].bindex;
  bool buttonPressed = false;
  if (buttonmatrix.button[listIndex].bstate == PRESSED || buttonmatrix.button[listIndex].bstate == HOLD) {
    buttonPressed = true;
  }

  if (buttonIndex < 32) {
    if (buttonPressed) {
      Joystick.button(buttonIndex + 1, 1);
    } else {
      Joystick.button(buttonIndex + 1, 0);
    }
  }
  else if (buttonIndex < 40) {
    if (buttonPressed) {
      Joystick.hat(1, buttonDirs[buttonIndex-32]);
    } else {
      Joystick.hat(1, -1);
    }
  }
  else if (buttonIndex < 48) {
    if (buttonPressed) {
      Joystick.hat(2, buttonDirs[buttonIndex-40]);
    } else {
      Joystick.hat(2, -1);
    }
  }
  else if (buttonIndex < 56) {
    if (buttonPressed) {
      Joystick.hat(3, buttonDirs[buttonIndex-48]);
    } else {
      Joystick.hat(3, -1);
    }
  }
  else if (buttonIndex < 64) {
    if (buttonPressed) {
      Joystick.hat(4, buttonDirs[buttonIndex-56]);
    } else {
      Joystick.hat(4, -1);
    }
  }
  //Joystick.send_now();

}

The Button Matrix is based on Keyboard Matrix Code samples available on the net. Here are the C++ and Header files:
Code: [Select]
#include "ButtonMatrix.h"

ButtonMatrix::ButtonMatrix(byte *row, byte *col, byte numRows, byte numCols) {
rowPins = row;
columnPins = col;
sizeBM.rows = numRows;
sizeBM.columns = numCols;

setDebounceTime(10);
setHoldTime(500);
buttonEventListener = 0;

startTime = 0;
single_button = false;
}

// Returns a single button only. Retained for backwards compatibility.
int ButtonMatrix::getButton() {
single_button = true;

if (getButtons() && button[0].stateChanged && (button[0].bstate==PRESSED))
return button[0].bindex;

single_button = false;

return NO_BUTTON;
}

// Populate the button list.
bool ButtonMatrix::getButtons() {
bool buttonActivity = false;

// Limit how often the button matrix is scanned. This makes the loop() run 10 times as fast.
if ( (millis()-startTime)>debounceTime ) {
scanButtons();
buttonActivity = updateList();
startTime = millis();
}

return buttonActivity;
}

// Private : Hardware scan
void ButtonMatrix::scanButtons() {
// Re-intialize the row pins. Allows sharing these pins with other hardware.
for (byte r=0; r<sizeBM.rows; r++) {
pin_mode(rowPins[r],INPUT_PULLUP);
}

// bitMap stores ALL the buttons that are being pressed.
for (byte c=0; c<sizeBM.columns; c++) {
pin_mode(columnPins[c],OUTPUT);
pin_write(columnPins[c], LOW); // Begin column pulse output.
for (byte r=0; r<sizeBM.rows; r++) {
bitWrite(bitMap[r], c, !pin_read(rowPins[r]));  // button press is active low so invert to high.
}
// Set pin to high impedance input. Effectively ends column pulse.
pin_write(columnPins[c],HIGH);
pin_mode(columnPins[c],INPUT);
}
}

// Manage the list without rearranging the keys. Returns true if any keys on the list changed state.
bool ButtonMatrix::updateList() {

bool anyActivity = false;

// Delete any IDLE buttons
for (byte i=0; i<LIST_MAX; i++) {
if (button[i].bstate==IDLE) {
button[i].bindex = NO_BUTTON;
button[i].stateChanged = false;
}
}

// Add new buttons to empty slots in the button list.
for (byte r=0; r<sizeBM.rows; r++) {
for (byte c=0; c<sizeBM.columns; c++) {
boolean bbutton = bitRead(bitMap[r],c);
int buttonIndex = r * sizeBM.columns + c;
int idx = findInList (buttonIndex);
// Key is already on the list so set its next state.
if (idx > -1) {
nextButtonState(idx, bbutton);
}
// Button is NOT on the list so add it.
if ((idx == -1) && bbutton) {
for (byte i=0; i<LIST_MAX; i++) {
if (button[i].bindex==NO_BUTTON) { // Find an empty slot or don't add button to list.
button[i].bindex = buttonIndex;
button[i].bstate = IDLE; // Buttons NOT on the list have an initial state of IDLE.
nextButtonState (i, bbutton);
break; // Don't fill all the empty slots with the same button.
}
}
}
}
}

// Report if the user changed the state of any button.
for (byte i=0; i<LIST_MAX; i++) {
if (button[i].stateChanged) anyActivity = true;
}

return anyActivity;
}

// Private
// This function is a state machine but is also used for debouncing the buttons.
void ButtonMatrix::nextButtonState(byte idx, boolean bbutton) {
button[idx].stateChanged = false;

switch (button[idx].bstate) {
case IDLE:
if (bbutton==CLOSED) {
transitionTo (idx, PRESSED);
holdTimer = millis(); } // Get ready for next HOLD state.
break;
case PRESSED:
if ((millis()-holdTimer)>holdTime) // Waiting for a key HOLD...
transitionTo (idx, HOLD);
else if (bbutton==OPEN) // or for a key to be RELEASED.
transitionTo (idx, RELEASED);
break;
case HOLD:
if (bbutton==OPEN)
transitionTo (idx, RELEASED);
break;
case RELEASED:
transitionTo (idx, IDLE);
break;
}
}

bool ButtonMatrix::isPressed(int buttonIndex) {
for (byte i=0; i<LIST_MAX; i++) {
if ( button[i].bindex == buttonIndex ) {
if ( (button[i].bstate == PRESSED) && button[i].stateChanged )
return true;
}
}
return false; // Not pressed.
}

// Search by index for a button in the list of active buttons.
// Returns -1 if not found, returns the active buttons list index if found.
int ButtonMatrix::findInList (int buttonIndex) {
for (byte i=0; i<LIST_MAX; i++) {
if (button[i].bindex == buttonIndex) {
return i;
}
}
return -1;
}

int ButtonMatrix::waitForButton() {
int waitButton = NO_BUTTON;
while( (waitButton = getButton()) == NO_BUTTON ); // Block everything while waiting for a keypress.
return waitButton;
}

ButtonState ButtonMatrix::getState() {
return button[0].bstate;
}

// The end user can test for any changes in state before deciding
// if any variables, etc. needs to be updated in their code.
bool ButtonMatrix::buttonStateChanged() {
return button[0].stateChanged;
}

// The number of buttons on the button list, button[LIST_MAX], equals the number
// of bytes in the button list divided by the number of bytes in a Button object.
byte ButtonMatrix::numButtons() {
return sizeof(button)/sizeof(Button);
}

// Minimum debounceTime is 1 ms. Any lower *will* slow down the loop().
void ButtonMatrix::setDebounceTime(uint debounce) {
debounce<1 ? debounceTime=1 : debounceTime=debounce;
}

void ButtonMatrix::setHoldTime(uint hold) {
    holdTime = hold;
}

void ButtonMatrix::addEventListener(void (*listener)(int)){
buttonEventListener = listener;
}

void ButtonMatrix::transitionTo(byte idx, ButtonState nextState) {
button[idx].bstate = nextState;
button[idx].stateChanged = true;

// Sketch used the getButton() function.
// Calls buttonEventListener only when the first button in slot 0 changes state.
if (single_button)  {
  if ( (buttonEventListener!=NULL) && (idx==0) )  {
buttonEventListener(0);
}
}
// Sketch used the getButtons() function.
// Calls buttonEventListener on any button that changes state.
else {
  if (buttonEventListener!=NULL)  {
buttonEventListener(idx);
}
}
}

Code: [Select]
#ifndef BUTTON_MATRIX_H
#define BUTTON_MATRIX_H

#include "Button.h"

#ifndef INPUT_PULLUP
#warning "Using  pinMode() INPUT_PULLUP AVR emulation"
#define INPUT_PULLUP 0x2
#define pinMode(_pin, _mode) _mypinMode(_pin, _mode)
#define _mypinMode(_pin, _mode)  \
do { \
if(_mode == INPUT_PULLUP) \
pinMode(_pin, INPUT); \
digitalWrite(_pin, 1); \
if(_mode != INPUT_PULLUP) \
pinMode(_pin, _mode); \
}while(0)
#endif


#define OPEN LOW
#define CLOSED HIGH

typedef int ButtonEvent;
typedef unsigned int uint;
typedef unsigned long ulong;

// Made changes according to this post http://arduino.cc/forum/index.php?topic=58337.0
// by Nick Gammon. Thanks for the input Nick. It actually saved 78 bytes for me. :)
typedef struct {
    byte rows;
    byte columns;
} ButtonMatrixSize;

#define LIST_MAX 100 // Max number of keys on the active list.
#define MAPSIZE 10 // MAPSIZE is the number of rows (times 16 columns)

//class ButtonMatrix : public Button, public HAL_obj {
class ButtonMatrix : public Button {
public:

ButtonMatrix(byte *row, byte *col, byte numRows, byte numCols);

virtual void pin_mode(byte pinNum, byte mode) { pinMode(pinNum, mode); }
virtual void pin_write(byte pinNum, boolean level) { digitalWrite(pinNum, level); }
virtual int  pin_read(byte pinNum) { return digitalRead(pinNum); }

uint bitMap[MAPSIZE]; // 10 row x 16 column array of bits. Except Due which has 32 columns.
Button button[LIST_MAX];
unsigned long holdTimer;

int getButton();
bool getButtons();
ButtonState getState();
bool isPressed(int buttonIndex);
void setDebounceTime(uint);
void setHoldTime(uint);
void addEventListener(void (*listener)(int));
int findInList(int buttonIndex);
int waitForButton();
bool buttonStateChanged();
byte numButtons();

private:
unsigned long startTime;
  byte *rowPins;
  byte *columnPins;
ButtonMatrixSize sizeBM;
uint debounceTime;
uint holdTime;
bool single_button;

void scanButtons();
bool updateList();
void nextButtonState(byte n, boolean bbutton);
void transitionTo(byte n, ButtonState nextState);
void (*buttonEventListener)(int);
};

#endif

The Button Matrix uses a "Button" Class to represent each Button's State. These are the C++ and Header files for that class:
Code: [Select]
#include "Button.h"

Button::Button() {
bindex = NO_BUTTON;
bstate = IDLE;
stateChanged = false;
}

void Button::button_update (int userButtonIndex, ButtonState userState, boolean userStatus) {
bindex = userButtonIndex;
bstate = userState;
stateChanged = userStatus;
}

Code: [Select]
#ifndef BUTTON_H_
#define BUTTON_H_

#include <Arduino.h>

#define OPEN LOW
#define CLOSED HIGH

typedef unsigned int uint;
typedef enum{ IDLE, PRESSED, HOLD, RELEASED } ButtonState;

const int NO_BUTTON = -1;

class Button {
public:
// members
int bindex;
ButtonState bstate;
boolean stateChanged;

// methods
Button();
void button_update(int userButtonIndex, ButtonState userState, boolean userStatus);

private:

};

#endif

USB connector comes next...
Logged
Don't split your mentality without thinking twice.

SAS~Storebror

  • Editor
  • member
  • Offline Offline
  • Posts: 23561
  • Taking a timeout
    • STFU
Re: Mike's new project
« Reply #38 on: May 02, 2018, 02:22:02 AM »

Last but not least, the whole USB connector code needed to be modified.
This one is based on a Teensy++ 2.0 "Keyboard + Mouse + Joystick" HID sample, but it's vastly modified meanwhile.
In it's current state, it holds one Joystick Interface and one Debugging Interface, representing a virtual serial port to send debug messages back to the PC.
The API is C++, the USB interface is ANSI C internally, but with external API class references (dirty but works).

usb_private.h:
Code: [Select]
#ifndef usb_serial_h__
#define usb_serial_h__

#include <stdint.h>

#ifdef __cplusplus
extern "C"{
#endif

/**************************************************************************
 *
 *  Configurable Options
 *
 **************************************************************************/

#define VENDOR_ID               0x16C0
#define PRODUCT_ID              0x0482
#define TRANSMIT_FLUSH_TIMEOUT  4   /* in milliseconds */
#define TRANSMIT_TIMEOUT        25   /* in milliseconds */


/**************************************************************************
 *
 *  Endpoint Buffer Configuration
 *
 **************************************************************************/

// 0: control 64
// 1: debug IN 64x2
// 2: debug OUT 32x2
// 3: joystick IN 16x2

// Some operating systems, especially Windows, may cache USB device
// info.  Changes to the device name may not update on the same
// computer unless the vendor or product ID numbers change, or the
// "bcdDevice" revision code is increased.

#ifndef STR_PRODUCT
#define STR_PRODUCT             L"Mikes Switchbox WIP"
#endif

#define ENDPOINT0_SIZE          64

#define DEBUG_INTERFACE 1
#define DEBUG_TX_ENDPOINT 1
#define DEBUG_TX_SIZE 64
#define DEBUG_TX_BUFFER EP_DOUBLE_BUFFER
#define DEBUG_TX_INTERVAL 1
#define DEBUG_RX_ENDPOINT 2
#define DEBUG_RX_SIZE 32
#define DEBUG_RX_BUFFER EP_DOUBLE_BUFFER
#define DEBUG_RX_INTERVAL 2

#define JOYSTICK_INTERFACE 2
#define JOYSTICK_ENDPOINT 3
#define JOYSTICK_SIZE 16
#define JOYSTICK_BUFFER EP_DOUBLE_BUFFER
#define JOYSTICK_INTERVAL 2

#define NUM_ENDPOINTS 4
#define NUM_INTERFACE 2


// setup
void usb_init(void); // initialize everything
void usb_shutdown(void); // shut off USB

// variables
extern volatile uint8_t usb_configuration;
extern volatile uint8_t usb_suspended;
extern volatile uint8_t debug_flush_timer;
extern uint8_t joystick_report_data[13];

#ifdef __cplusplus
} // extern "C"
#endif

#endif

core_id.h:
Code: [Select]
#define CORE_TEENSY_HID
#if defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
#define CORE_TEENSY_JOYSTICK
#endif

usb_api.cpp:
Code: [Select]
/* USB API for Teensy USB Development Board
 * http://www.pjrc.com/teensy/teensyduino.html
 * Copyright (c) 2008 PJRC.COM, LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <stdint.h>
#include "usb_common.h"
#include "usb_private.h"
#include "usb_api.h"
#include "wiring.h"



void usb_joystick_class::send_now(void)
{
        uint8_t intr_state, timeout;

        if (!usb_configuration) return;
        intr_state = SREG;
        cli();
        UENUM = JOYSTICK_ENDPOINT;
        timeout = UDFNUML + 50;
        while (1) {
                // are we ready to transmit?
                if (UEINTX & (1<<RWAL)) break;
                SREG = intr_state;
                // has the USB gone offline?
                if (!usb_configuration) return;
                // have we waited too long?
                if (UDFNUML == timeout) return;
                // get ready to try checking again
                intr_state = SREG;
                cli();
                UENUM = JOYSTICK_ENDPOINT;
        }
        UEDATX = joystick_report_data[0];
        UEDATX = joystick_report_data[1];
        UEDATX = joystick_report_data[2];
        UEDATX = joystick_report_data[3];
        UEDATX = joystick_report_data[4];
        UEDATX = joystick_report_data[5];
        UEDATX = joystick_report_data[6];
        UEDATX = joystick_report_data[7];
        UEDATX = joystick_report_data[8];
        UEDATX = joystick_report_data[9];
        UEDATX = joystick_report_data[10];
        UEDATX = joystick_report_data[11];
        UEDATX = joystick_report_data[12];
        UEINTX = 0x3A;
        SREG = intr_state;
}

uint16_t usb_joystick_class::get_joystick_report_data(uint8_t index) {
return joystick_report_data[index];
}

static volatile uint8_t prev_byte=0;

void usb_serial_class::begin(long speed)
{
// make sure USB is initialized
usb_init();
uint16_t begin_wait = (uint16_t)millis();
while (1) {
if (usb_configuration) {
delay(200);  // a little time for host to load a driver
return;
}
if (usb_suspended) {
uint16_t begin_suspend = (uint16_t)millis();
while (usb_suspended) {
// must remain suspended for a while, because
// normal USB enumeration causes brief suspend
// states, typically under 0.1 second
if ((uint16_t)millis() - begin_suspend > 250) {
return;
}
}
}
// ... or a timout (powered by a USB power adaptor that
// wiggles the data lines to keep a USB device charging)
if ((uint16_t)millis() - begin_wait > 2500) return;
}
prev_byte = 0;
}

void usb_serial_class::end()
{
usb_shutdown();
delay(25);
}



// number of bytes available in the receive buffer
int usb_serial_class::available()
{
        uint8_t c;

c = prev_byte;  // assume 1 byte static volatile access is atomic
if (c) return 1;
c = readnext();
if (c) {
prev_byte = c;
return 1;
}
return 0;
}

// get the next character, or -1 if nothing received
int usb_serial_class::read()
{
uint8_t c;

c = prev_byte;
if (c) {
prev_byte = 0;
return c;
}
c = readnext();
if (c) return c;
return -1;
}

int usb_serial_class::peek()
{
uint8_t c;

c = prev_byte;
if (c) return c;
c = readnext();
if (c) {
prev_byte = c;
return c;
}
return -1;
}

// get the next character, or 0 if nothing
uint8_t usb_serial_class::readnext(void)
{
        uint8_t c, intr_state;

        // interrupts are disabled so these functions can be
        // used from the main program or interrupt context,
        // even both in the same program!
        intr_state = SREG;
        cli();
        if (!usb_configuration) {
                SREG = intr_state;
                return 0;
        }
        UENUM = DEBUG_RX_ENDPOINT;
try_again:
        if (!(UEINTX & (1<<RWAL))) {
                // no packet in buffer
                SREG = intr_state;
                return 0;
        }
        // take one byte out of the buffer
        c = UEDATX;
if (c == 0) {
// if we see a zero, discard it and
// discard the rest of this packet
UEINTX = 0x6B;
goto try_again;
}
        // if this drained the buffer, release it
        if (!(UEINTX & (1<<RWAL))) UEINTX = 0x6B;
        SREG = intr_state;
        return c;
}

// discard any buffered input
void usb_serial_class::flush()
{
        uint8_t intr_state;

        if (usb_configuration) {
                intr_state = SREG;
                cli();
UENUM = DEBUG_RX_ENDPOINT;
                while ((UEINTX & (1<<RWAL))) {
                        UEINTX = 0x6B;
                }
                SREG = intr_state;
        }
prev_byte = 0;
}

// transmit a character.
size_t usb_serial_class::write(uint8_t c)
{
        //static uint8_t previous_timeout=0;
        uint8_t timeout, intr_state;

        // if we're not online (enumerated and configured), error
        if (!usb_configuration) goto error;
        // interrupts are disabled so these functions can be
        // used from the main program or interrupt context,
        // even both in the same program!
        intr_state = SREG;
        cli();
        UENUM = DEBUG_TX_ENDPOINT;
        // if we gave up due to timeout before, don't wait again
#if 0
// this seems to be causig a lockup... why????
        if (previous_timeout) {
                if (!(UEINTX & (1<<RWAL))) {
                        SREG = intr_state;
                        return;
                }
                previous_timeout = 0;
        }
#endif
        // wait for the FIFO to be ready to accept data
        timeout = UDFNUML + TRANSMIT_TIMEOUT;
        while (1) {
                // are we ready to transmit?
                if (UEINTX & (1<<RWAL)) break;
                SREG = intr_state;
                // have we waited too long?  This happens if the user
                // is not running an application that is listening
                if (UDFNUML == timeout) {
                        //previous_timeout = 1;
goto error;
}
                // has the USB gone offline?
                if (!usb_configuration) goto error;
                // get ready to try checking again
                intr_state = SREG;
                cli();
                UENUM = DEBUG_TX_ENDPOINT;
        }
        // actually write the byte into the FIFO
        UEDATX = c;
        // if this completed a packet, transmit it now!
        if (!(UEINTX & (1<<RWAL))) {
UEINTX = 0x3A;
        debug_flush_timer = 0;
} else {
        debug_flush_timer = TRANSMIT_FLUSH_TIMEOUT;
}
        SREG = intr_state;
return 1;
error:
setWriteError();
return 0;
}


// These are Teensy-specific extensions to the Serial object

// immediately transmit any buffered output.
// This doesn't actually transmit the data - that is impossible!
// USB devices only transmit when the host allows, so the best
// we can do is release the FIFO buffer for when the host wants it
void usb_serial_class::send_now(void)
{
        uint8_t intr_state;

        intr_state = SREG;
        cli();
        if (debug_flush_timer) {
                UENUM = DEBUG_TX_ENDPOINT;
while ((UEINTX & (1<<RWAL))) {
UEDATX = 0;
}
                UEINTX = 0x3A;
                debug_flush_timer = 0;
        }
        SREG = intr_state;
}

uint32_t usb_serial_class::baud(void)
{
return ((uint32_t)DEBUG_TX_SIZE * 10000 / DEBUG_TX_INTERVAL);
}

uint8_t usb_serial_class::stopbits(void)
{
return 1;
}

uint8_t usb_serial_class::paritytype(void)
{
return 0;
}

uint8_t usb_serial_class::numbits(void)
{
return 8;
}

uint8_t usb_serial_class::dtr(void)
{
return 1;
}

uint8_t usb_serial_class::rts(void)
{
return 1;
}

usb_serial_class::operator bool()
{
if (usb_configuration) return true;
return false;
}


// Preinstantiate Objects //////////////////////////////////////////////////////

usb_serial_class Serial = usb_serial_class();
usb_joystick_class Joystick = usb_joystick_class();


usb_api.h:
Code: [Select]
#ifndef USBserial_h_
#define USBserial_h_

#include <inttypes.h>

#include "Print.h"
#include "Stream.h"

extern uint8_t joystick_report_data[13];

class usb_joystick_class
{
public:
usb_joystick_class() { manual_mode = 0; }
inline void button(uint8_t button, bool val) {
button--;
uint8_t mask = (1 << (button & 7));
if (val) {
if (button < 8) joystick_report_data[0] |= mask;
else if (button < 16) joystick_report_data[1] |= mask;
else if (button < 24) joystick_report_data[2] |= mask;
else if (button < 32) joystick_report_data[3] |= mask;
} else {
mask = ~mask;
if (button < 8) joystick_report_data[0] &= mask;
else if (button < 16) joystick_report_data[1] &= mask;
else if (button < 24) joystick_report_data[2] &= mask;
else if (button < 32) joystick_report_data[3] &= mask;
}
if (!manual_mode) send_now();
}

inline void X(uint16_t val) {
if (val > 1023) val = 1023;
joystick_report_data[4] = val;
joystick_report_data[5] = (joystick_report_data[5] & 0xFC) | (val >> 8);
if (!manual_mode) send_now();
}
inline void Y(uint16_t val) {
if (val > 1023) val = 1023;
joystick_report_data[5] = (joystick_report_data[5] & 0x03) | (val << 2);
joystick_report_data[6] = (joystick_report_data[6] & 0xF0) | (val >> 6);
if (!manual_mode) send_now();
}
inline void position(uint16_t x, uint16_t y) {
if (x > 1023) x = 1023;
if (y > 1023) y = 1023;
joystick_report_data[4] = x;
joystick_report_data[5] = (x >> 8) | (y << 2);
joystick_report_data[6] = (joystick_report_data[6] & 0xF0) | (y >> 6);
if (!manual_mode) send_now();
}
inline void Z(uint16_t val) {
if (val > 1023) val = 1023;
joystick_report_data[6] = (joystick_report_data[6] & 0x0F) | (val << 4);
joystick_report_data[7] = (joystick_report_data[7] & 0xC0) | (val >> 4);
if (!manual_mode) send_now();
}
inline void Xrotate(uint16_t val) {
if (val > 1023) val = 1023;
joystick_report_data[7] = (joystick_report_data[7] & 0x3F) | (val << 6);
joystick_report_data[8] = (val >> 2);
if (!manual_mode) send_now();
}
inline void Yrotate(uint16_t val) {
if (val > 1023) val = 1023;
joystick_report_data[9] = val;
joystick_report_data[10] = (joystick_report_data[10] & 0xFC) | (val >> 8);
if (!manual_mode) send_now();
}

inline void hat(uint8_t hatnum, int16_t dir) {
uint8_t val = 15;
if (dir < 0) val = 15;
else if (dir < 23) val = 0;
else if (dir < 68) val = 1;
else if (dir < 113) val = 2;
else if (dir < 158) val = 3;
else if (dir < 203) val = 4;
else if (dir < 245) val = 5;
else if (dir < 293) val = 6;
else if (dir < 338) val = 7;
switch(hatnum) {
case 1:
joystick_report_data[10] = (joystick_report_data[10] & 0x0F) | (val << 4);
break;
case 2:
joystick_report_data[11] = (joystick_report_data[11] & 0xF0) | val;
break;
case 3:
joystick_report_data[11] = (joystick_report_data[11] & 0x0F) | (val << 4);
break;
case 4:
joystick_report_data[12] = (joystick_report_data[12] & 0xF0) | val;
break;
default:
break;
}
if (!manual_mode) send_now();
}
inline void useManualSend(bool mode) {
manual_mode = mode;
}
uint16_t get_joystick_report_data(uint8_t index);
void send_now(void);
private:
uint8_t manual_mode;
};

extern usb_joystick_class Joystick;




class usb_serial_class : public Stream
{
public:
// standard Arduino functions
void begin(long);
void end();
virtual int available();
virtual int read();
virtual int peek();
virtual void flush();
virtual size_t write(uint8_t);
using Print::write;
operator bool();
// Teensy extensions
void send_now(void);
uint32_t baud(void);
uint8_t stopbits(void);
uint8_t paritytype(void);
uint8_t numbits(void);
uint8_t dtr(void);
uint8_t rts(void);
private:
uint8_t readnext(void);
};

extern usb_serial_class Serial;


#endif

usb.c:
Code: [Select]
/* USB Serial Example for Teensy USB Development Board
 * http://www.pjrc.com/teensy/usb_serial.html
 * Copyright (c) 2008 PJRC.COM, LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */


#include "usb_common.h"
#include "usb_private.h"



/**************************************************************************
 *
 *  Endpoint Buffer Configuration
 *
 **************************************************************************/


static const uint8_t PROGMEM endpoint_config_table[] = {
EP_TYPE_INTERRUPT_IN,  EP_SIZE(DEBUG_TX_SIZE) | DEBUG_TX_BUFFER,
EP_TYPE_INTERRUPT_OUT, EP_SIZE(DEBUG_RX_SIZE) | DEBUG_RX_BUFFER,
EP_TYPE_INTERRUPT_IN,  EP_SIZE(JOYSTICK_SIZE) | JOYSTICK_BUFFER,
};


/**************************************************************************
 *
 *  Descriptor Data
 *
 **************************************************************************/

// Descriptors are the data that your computer reads when it auto-detects
// this USB device (called "enumeration" in USB lingo).  The most commonly
// changed items are editable at the top of this file.  Changing things
// in here should only be done by those who've read chapter 9 of the USB
// spec and relevant portions of any USB class specifications!

static const uint8_t PROGMEM device_descriptor[] = {
18, // bLength
1, // bDescriptorType
0x00, 0x02, // bcdUSB
0, // bDeviceClass
0, // bDeviceSubClass
0, // bDeviceProtocol
ENDPOINT0_SIZE, // bMaxPacketSize0
LSB(VENDOR_ID), MSB(VENDOR_ID), // idVendor
LSB(PRODUCT_ID), MSB(PRODUCT_ID), // idProduct
0x05, 0x01, // bcdDevice
0, // iManufacturer
1, // iProduct
0, // iSerialNumber
1 // bNumConfigurations
};

#ifdef JOYSTICK_INTERFACE
static const uint8_t PROGMEM joystick_hid_report_desc[] = {
        0x05, 0x01,                     // Usage Page (Generic Desktop)
        0x09, 0x04,                     // Usage (Joystick)
        0xA1, 0x01,                     // Collection (Application)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x20, // Report Count (32)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (Button #1)
0x29, 0x20, // Usage Maximum (Button #32)
0x81, 0x02, // Input (variable,absolute)
        0x05, 0x01,                     // Usage Page (Generic Desktop)
0x09, 0x01, // Usage (Pointer)
        0xA1, 0x00,                     // Collection ()
0x15, 0x00, //   Logical Minimum (0)
0x26, 0xFF, 0x03, //   Logical Maximum (1023)
0x75, 0x0A, //   Report Size (10)
0x95, 0x05, //   Report Count (5)
0x09, 0x30, //   Usage (X)
0x09, 0x31, //   Usage (Y)
0x09, 0x32, //   Usage (Z)
0x09, 0x33, //   Usage (Rx)
0x09, 0x34, //   Usage (Ry)
0x81, 0x02, //   Input (variable,absolute)
        0xC0,                           // End Collection
0x75, 0x02, // Report Size (2)
0x95, 0x01, // Report Count (1)
0x81, 0x03,     // INPUT (Cnst,Var,Abs)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x07, // Logical Maximum (7)
0x35, 0x00, // Physical Minimum (0)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x75, 0x04, // Report Size (4)
0x95, 0x04, // Report Count (4)
0x65, 0x14, // Unit (20)
        0x05, 0x01,                     // Usage Page (Generic Desktop)
0x09, 0x39, // Usage (Hat switch)
0x09, 0x39, // Usage (Hat switch)
0x09, 0x39, // Usage (Hat switch)
0x09, 0x39, // Usage (Hat switch)
0x81, 0x42, // Input (variable,absolute,null_state)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x81, 0x03,     // INPUT (Cnst,Var,Abs)
        0xC0                            // End Collection
};
#endif

static const uint8_t PROGMEM debug_hid_report_desc[] = {
        0x06, 0xC9, 0xFF,                       // Usage Page 0xFFC9 (vendor defined)
        0x09, 0x04,                             // Usage 0x04
        0xA1, 0x5C,                             // Collection 0x5C
        0x75, 0x08,                             // report size = 8 bits (global)
        0x15, 0x00,                             // logical minimum = 0 (global)
        0x26, 0xFF, 0x00,                       // logical maximum = 255 (global)
        0x95, DEBUG_TX_SIZE,                    // report count (global)
        0x09, 0x75,                             // usage (local)
        0x81, 0x02,                             // Input
        0x95, DEBUG_RX_SIZE,                    // report count (global)
        0x09, 0x76,                             // usage (local)
        0x91, 0x02,                             // Output
        0x95, 0x04,                             // report count (global)
        0x09, 0x76,                             // usage (local)
        0xB1, 0x02,                             // Feature
        0xC0                                    // end collection
};


#define DEBUG_HID_DESC_OFFSET ( 9 + 9 )
#define JOYSTICK_HID_DESC_OFFSET ( 9 + 9+9+7+7 + 9 )
#define CONFIG1_DESC_SIZE ( 9 + 9+9+7+7 + 9+9+7 )

static const uint8_t PROGMEM config1_descriptor[CONFIG1_DESC_SIZE] = {
// configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10
9, // bLength;
2, // bDescriptorType;
LSB(CONFIG1_DESC_SIZE), // wTotalLength
MSB(CONFIG1_DESC_SIZE),
NUM_INTERFACE, // bNumInterfaces
1, // bConfigurationValue
0, // iConfiguration
0xC0, // bmAttributes
50, // bMaxPower

        // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
        9,                                      // bLength
        4,                                      // bDescriptorType
        DEBUG_INTERFACE,                        // bInterfaceNumber
        0,                                      // bAlternateSetting
        2,                                      // bNumEndpoints
        0x03,                                   // bInterfaceClass (0x03 = HID)
        0x00,                                   // bInterfaceSubClass
        0x00,                                   // bInterfaceProtocol
        0,                                      // iInterface
        // HID interface descriptor, HID 1.11 spec, section 6.2.1
        9,                                      // bLength
        0x21,                                   // bDescriptorType
        0x11, 0x01,                             // bcdHID
        0,                                      // bCountryCode
        1,                                      // bNumDescriptors
        0x22,                                   // bDescriptorType
        sizeof(debug_hid_report_desc),          // wDescriptorLength
        0,
        // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
        7,                                      // bLength
        5,                                      // bDescriptorType
        DEBUG_TX_ENDPOINT | 0x80,               // bEndpointAddress
        0x03,                                   // bmAttributes (0x03=intr)
        DEBUG_TX_SIZE, 0,                       // wMaxPacketSize
        DEBUG_TX_INTERVAL,                      // bInterval
        // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
        7,                                      // bLength
        5,                                      // bDescriptorType
        DEBUG_RX_ENDPOINT,                      // bEndpointAddress
        0x03,                                   // bmAttributes (0x03=intr)
        DEBUG_RX_SIZE, 0,                       // wMaxPacketSize
        DEBUG_RX_INTERVAL,                      // bInterval

        // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
        9,                                      // bLength
        4,                                      // bDescriptorType
        JOYSTICK_INTERFACE,                     // bInterfaceNumber
        0,                                      // bAlternateSetting
        1,                                      // bNumEndpoints
        0x03,                                   // bInterfaceClass (0x03 = HID)
        0x00,                                   // bInterfaceSubClass
        0x00,                                   // bInterfaceProtocol
        0,                                      // iInterface
        // HID interface descriptor, HID 1.11 spec, section 6.2.1
        9,                                      // bLength
        0x21,                                   // bDescriptorType
        0x11, 0x01,                             // bcdHID
        0,                                      // bCountryCode
        1,                                      // bNumDescriptors
        0x22,                                   // bDescriptorType
        sizeof(joystick_hid_report_desc),       // wDescriptorLength
        0,
        // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
        7,                                      // bLength
        5,                                      // bDescriptorType
        JOYSTICK_ENDPOINT | 0x80,               // bEndpointAddress
        0x03,                                   // bmAttributes (0x03=intr)
        JOYSTICK_SIZE, 0,               // wMaxPacketSize
        JOYSTICK_INTERVAL,                      // bInterval
};

// If you're desperate for a little extra code memory, these strings
// can be completely removed if iManufacturer, iProduct, iSerialNumber
// in the device desciptor are changed to zeros.
struct usb_string_descriptor_struct {
uint8_t bLength;
uint8_t bDescriptorType;
int16_t wString[];
};
static const struct usb_string_descriptor_struct PROGMEM string0 = {
4,
3,
{0x0409}
};
static const struct usb_string_descriptor_struct PROGMEM string1 = {
sizeof(STR_PRODUCT),
3,
STR_PRODUCT
};

// This table defines which descriptor data is sent for each specific
// request from the host (in wValue and wIndex).
static const struct descriptor_list_struct {
uint16_t wValue;
uint16_t wIndex;
const uint8_t *addr;
uint8_t length;
} PROGMEM descriptor_list[] = {
{0x0100, 0x0000, device_descriptor, sizeof(device_descriptor)},
{0x0200, 0x0000, config1_descriptor, sizeof(config1_descriptor)},
        {0x2200, DEBUG_INTERFACE, debug_hid_report_desc, sizeof(debug_hid_report_desc)},
        {0x2100, DEBUG_INTERFACE, config1_descriptor+DEBUG_HID_DESC_OFFSET, 9},
        {0x2200, JOYSTICK_INTERFACE, joystick_hid_report_desc, sizeof(joystick_hid_report_desc)},
        {0x2100, JOYSTICK_INTERFACE, config1_descriptor+JOYSTICK_HID_DESC_OFFSET, 9},
{0x0300, 0x0000, (const uint8_t *)&string0, 4},
{0x0301, 0x0409, (const uint8_t *)&string1, sizeof(STR_PRODUCT)},
};
#define NUM_DESC_LIST (sizeof(descriptor_list)/sizeof(struct descriptor_list_struct))


/**************************************************************************
 *
 *  Variables - these are the only non-stack RAM usage
 *
 **************************************************************************/

// zero when we are not configured, non-zero when enumerated
volatile uint8_t usb_configuration USBSTATE;
volatile uint8_t usb_suspended USBSTATE;

// the time remaining before we transmit any partially full
// packet, or send a zero length packet.
volatile uint8_t debug_flush_timer USBSTATE;

// joystick data
uint8_t joystick_report_data[13] USBSTATE;

/**************************************************************************
 *
 *  Public Functions - these are the API intended for the user
 *
 **************************************************************************/



// initialize USB serial
void usb_init(void)
{
uint8_t u;

u = USBCON;
if ((u & (1<<USBE)) && !(u & (1<<FRZCLK))) return;
HW_CONFIG();
        USB_FREEZE(); // enable USB
        PLL_CONFIG(); // config PLL
        while (!(PLLCSR & (1<<PLOCK))) ; // wait for PLL lock
        USB_CONFIG(); // start USB clock
        UDCON = 0; // enable attach resistor
usb_configuration = 0;
usb_suspended = 0;
debug_flush_timer = 0;
joystick_report_data[0] = 0;
joystick_report_data[1] = 0;
joystick_report_data[2] = 0;
joystick_report_data[3] = 0;
joystick_report_data[4] =  0;
joystick_report_data[5] =  0x02;
joystick_report_data[6] =  0x08;
joystick_report_data[7] =  0x20;
joystick_report_data[8] =  0x80;
joystick_report_data[9] =  0;
joystick_report_data[10] = 0xF2;
joystick_report_data[11] = 0xFF;
joystick_report_data[12] = 0xFF;
UDINT = 0;
        UDIEN = (1<<EORSTE)|(1<<SOFE);
//sei();  // init() in wiring.c does this
}

void usb_shutdown(void)
{
UDIEN = 0; // disable interrupts
UDCON = 1; // disconnect attach resistor
USBCON = 0; // shut off USB periperal
PLLCSR = 0; // shut off PLL
usb_configuration = 0;
usb_suspended = 1;
}


/**************************************************************************
 *
 *  Private Functions - not intended for general user consumption....
 *
 **************************************************************************/



// USB Device Interrupt - handle all device-level events
// the transmit buffer flushing is triggered by the start of frame
//
ISR(USB_GEN_vect)
{
uint8_t intbits, t;//, i;
// static uint8_t div4=0;

        intbits = UDINT;
        UDINT = 0;
        if (intbits & (1<<EORSTI)) {
UENUM = 0;
UECONX = 1;
UECFG0X = EP_TYPE_CONTROL;
UECFG1X = EP_SIZE(ENDPOINT0_SIZE) | EP_SINGLE_BUFFER;
UEIENX = (1<<RXSTPE);
usb_configuration = 0;
        }
        if ((intbits & (1<<SOFI)) && usb_configuration) {
                t = debug_flush_timer;
                if (t) {
                        debug_flush_timer = -- t;
                        if (!t) {
                                UENUM = DEBUG_TX_ENDPOINT;
                                while ((UEINTX & (1<<RWAL))) {
                                        UEDATX = 0;
                                }
                                UEINTX = 0x3A;
                        }
                }
        }
if (intbits & (1<<SUSPI)) {
// USB Suspend (inactivity for 3ms)
UDIEN = (1<<WAKEUPE);
usb_configuration = 0;
usb_suspended = 1;
#if (F_CPU >= 8000000L)
// WAKEUPI does not work with USB clock freeze
// when CPU is running less than 8 MHz.
// Is this a hardware bug?
USB_FREEZE(); // shut off USB
PLLCSR = 0; // shut off PLL
#endif
// to properly meet the USB spec, current must
// reduce to less than 2.5 mA, which means using
// powerdown mode, but that breaks the Arduino
// user's paradigm....
}
if (usb_suspended && (intbits & (1<<WAKEUPI))) {
// USB Resume (pretty much any activity)
#if (F_CPU >= 8000000L)
PLL_CONFIG();
while (!(PLLCSR & (1<<PLOCK))) ;
USB_CONFIG();
#endif
UDIEN = (1<<EORSTE)|(1<<SOFE)|(1<<SUSPE);
usb_suspended = 0;
return;
}
}


// Misc functions to wait for ready and send/receive packets
static inline void usb_wait_in_ready(void)
{
while (!(UEINTX & (1<<TXINI))) ;
}
static inline void usb_send_in(void)
{
UEINTX = ~(1<<TXINI);
}
static inline void usb_wait_receive_out(void)
{
while (!(UEINTX & (1<<RXOUTI))) ;
}
static inline void usb_ack_out(void)
{
UEINTX = ~(1<<RXOUTI);
}



// USB Endpoint Interrupt - endpoint 0 is handled here.  The
// other endpoints are manipulated by the user-callable
// functions, and the start-of-frame interrupt.
//
ISR(USB_COM_vect)
{
        uint8_t intbits;
const uint8_t *list;
        const uint8_t *cfg;
uint8_t i, n, len;
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
uint16_t desc_val;
const uint8_t *desc_addr;
uint8_t desc_length;

UENUM = 0;
intbits = UEINTX;
if (intbits & (1<<RXSTPI)) {
bmRequestType = UEDATX;
bRequest = UEDATX;
read_word_lsbfirst(wValue, UEDATX);
read_word_lsbfirst(wIndex, UEDATX);
read_word_lsbfirst(wLength, UEDATX);
UEINTX = ~((1<<RXSTPI) | (1<<RXOUTI) | (1<<TXINI));
if (bRequest == GET_DESCRIPTOR) {
list = (const uint8_t *)descriptor_list;
for (i=0; ; i++) {
if (i >= NUM_DESC_LIST) {
UECONX = (1<<STALLRQ)|(1<<EPEN);  //stall
return;
}
pgm_read_word_postinc(desc_val, list);
if (desc_val != wValue) {
list += sizeof(struct descriptor_list_struct)-2;
continue;
}
pgm_read_word_postinc(desc_val, list);
if (desc_val != wIndex) {
list += sizeof(struct descriptor_list_struct)-4;
continue;
}
pgm_read_word_postinc(desc_addr, list);
desc_length = pgm_read_byte(list);
break;
}
len = (wLength < 256) ? wLength : 255;
if (len > desc_length) len = desc_length;
list = desc_addr;
do {
// wait for host ready for IN packet
do {
i = UEINTX;
} while (!(i & ((1<<TXINI)|(1<<RXOUTI))));
if (i & (1<<RXOUTI)) return; // abort
// send IN packet
n = len < ENDPOINT0_SIZE ? len : ENDPOINT0_SIZE;
for (i = n; i; i--) {
pgm_read_byte_postinc(UEDATX, list);
}
len -= n;
usb_send_in();
} while (len || n == ENDPOINT0_SIZE);
return;
                }
if (bRequest == SET_ADDRESS) {
usb_send_in();
usb_wait_in_ready();
UDADDR = wValue | (1<<ADDEN);
return;
}
if (bRequest == SET_CONFIGURATION && bmRequestType == 0) {
usb_configuration = wValue;
debug_flush_timer = 0;
usb_send_in();
cfg = endpoint_config_table;
for (i=1; i<7; i++) {
UENUM = i;
UECONX = 1;
pgm_read_byte_postinc(UECFG0X, cfg);
pgm_read_byte_postinc(UECFG1X, cfg);
}
        UERST = 0x1E;
        UERST = 0;
return;
}
if (bRequest == GET_CONFIGURATION && bmRequestType == 0x80) {
usb_wait_in_ready();
UEDATX = usb_configuration;
usb_send_in();
return;
}
if (bRequest == GET_STATUS) {
usb_wait_in_ready();
i = 0;
if (bmRequestType == 0x82) {
UENUM = wIndex;
if (UECONX & (1<<STALLRQ)) i = 1;
UENUM = 0;
}
UEDATX = i;
UEDATX = 0;
usb_send_in();
return;
}
if ((bRequest == CLEAR_FEATURE || bRequest == SET_FEATURE)
  && bmRequestType == 0x02 && wValue == 0) {
i = wIndex & 0x7F;
if (i >= 1 && i <= NUM_ENDPOINTS) {
usb_send_in();
UENUM = i;
if (bRequest == SET_FEATURE) {
UECONX = (1<<STALLRQ)|(1<<EPEN);
} else {
UECONX = (1<<STALLRQC)|(1<<RSTDT)|(1<<EPEN);
UERST = (1 << i);
UERST = 0;
}
return;
}
}
                if (wIndex == JOYSTICK_INTERFACE) {
                        if (bmRequestType == 0xA1) {
                                if (bRequest == HID_GET_REPORT) {
                                        usb_wait_in_ready();
for (i=0; i<13; i++) {
UEDATX = joystick_report_data[i];
}
                                        usb_send_in();
                                        return;
}
}
}
                if (wIndex == DEBUG_INTERFACE) {
                        if (bRequest == HID_GET_REPORT && bmRequestType == 0xA1) {
                                len = wLength;
                                do {
                                        // wait for host ready for IN packet
                                        do {
                                                i = UEINTX;
                                        } while (!(i & ((1<<TXINI)|(1<<RXOUTI))));
                                        if (i & (1<<RXOUTI)) return;    // abort
                                        // send IN packet
                                        n = len < ENDPOINT0_SIZE ? len : ENDPOINT0_SIZE;
                                        for (i = n; i; i--) {
                                                UEDATX = 0;
                                        }
                                        len -= n;
                                        usb_send_in();
                                } while (len || n == ENDPOINT0_SIZE);
                                return;
                        }
                        if (bRequest == HID_SET_REPORT && bmRequestType == 0x21) {
if (wValue == 0x0300 && wLength == 0x0004) {
uint8_t b1, b2, b3, b4;
                                        usb_wait_receive_out();
b1 = UEDATX;
b2 = UEDATX;
b3 = UEDATX;
b4 = UEDATX;
                                        usb_ack_out();
                                        usb_send_in();
if (b1 == 0xA9 && b2 == 0x45 && b3 == 0xC2 && b4 == 0x6B)
_reboot_Teensyduino_();
if (b1 == 0x8B && b2 == 0xC5 && b3 == 0x1D && b4 == 0x70)
_restart_Teensyduino_();
}
}
                }
if (bRequest == 0xC9 && bmRequestType == 0x40) {
usb_send_in();
usb_wait_in_ready();
_restart_Teensyduino_();
}
        }
UECONX = (1<<STALLRQ) | (1<<EPEN); // stall
}



Cheers!
Mike
Logged
Don't split your mentality without thinking twice.

SAS~Storebror

  • Editor
  • member
  • Offline Offline
  • Posts: 23561
  • Taking a timeout
    • STFU
Re: Mike's new project
« Reply #39 on: May 02, 2018, 03:32:06 AM »

Yessss!
It works!
Needs a bit code cleaning now, but it works!
The single Teensy++ 2.0 can act as two independent Joysticks with 5 axis, 32 buttons and 4x8-way hats each!
No additional software needed!
Yippieh!
Code will follow once the cleaning is done...

Cheers!
Mike
Logged
Don't split your mentality without thinking twice.

SAS~Storebror

  • Editor
  • member
  • Offline Offline
  • Posts: 23561
  • Taking a timeout
    • STFU
Re: Mike's new project
« Reply #40 on: May 02, 2018, 08:25:46 AM »

Alright, here we are.
This is the full set of files required, including sources (including new USB Type), circuit diagram, compiled hex file for the Teensy++ 2.0 including "Loader" to load the hex into your Teensy.
The device needs no drivers, it will be detected automatically by Windows 7 or newer.

https://www.mediafire.com/file/fskhz3h3yy21qy6/Mike%27s_Controller_Box_v0.01.7z

Cheers!
Mike
Logged
Don't split your mentality without thinking twice.

SAS~Storebror

  • Editor
  • member
  • Offline Offline
  • Posts: 23561
  • Taking a timeout
    • STFU
Re: Mike's new project
« Reply #41 on: May 07, 2018, 08:14:10 AM »

The box survived it's first flight on sunday.

Few pictures of the wiring.
The beginning looks confusing, it's the "column" wiring between toggle switches, momentary switches and red buttons.
This has to look odd since the layout of the controls on our box isn't consistent.


Next are the diodes in that section. My initial idea was to use the diodes' wires to build up the row matrix. Looks ugly and turns out to be the wrong idea. To top it off, I've made a mistake half way through when I've soldered some diodes in opposite way. I thought I could turn them around, but this caused an error later on. That error is still present in this picture but it's solved meanwhile. If you find the issue, feel free to name it!


Proceeding with the remaining buttons and potentiometers, I've used some solid copper wire for the row chain instead. Looks much better. Note the capacitors between poti wiper and ground, in order to eliminate readout spikes (works perfectly!):


Now to the wiring between rows/columns/potis and the Teensy's base board:


This is how the wires arrive at the Teensy.
Note that the pins are soldered to the "wrong" side of the Teensy.
This is intentional.
There's not enough room in the case to put the pins on the right (lower) side, but I didn't want to lose the ability to unplug the Teensy board either.
This was the only possible compromise and it works just well.
Note that there's a 10µF capacitor soldered to the +5V/GND pins right to the reset button, with the capacitor lying below the board, in order to stabilize power supply.


Another image showing how the row/column/poti wires are grouped by functionality:


Last but not least, the final result as closed box. The USB connector gives an idea how small it is - which was just my intention.


]cheers[
Mike'
Logged
Don't split your mentality without thinking twice.

SAS~Gerax

  • SAS Team
  • member
  • Offline Offline
  • Posts: 4765
Re: Mike's new project
« Reply #42 on: May 07, 2018, 08:43:58 AM »

Dem Ingenieur ist nix zu schwör.  ;)
Logged
i7-13700K, MSI RTX4090, Kingston 64GB, Asus Z790-P, Crucial SSD 1TB, Kingston SSD 4TB;

Pivoyvo

  • member
  • Offline Offline
  • Posts: 331
  • Ein Tiecher bleibt immer Siecher
Re: Mike's new project
« Reply #43 on: May 08, 2018, 12:28:59 AM »

Respekt für diese Arbeit
Logged

Stainless

  • Modder
  • member
  • Offline Offline
  • Posts: 1530
Re: Mike's new project
« Reply #44 on: May 08, 2018, 04:50:13 AM »

Mike have you had any problems with the USB stalling or crashing?

When I was writing my joystick drivers I initially started along the same sort of path you followed, but I found huge delays between physical inputs and the data in game.

I also found that if the USB was not read from fast enough, the USB buffer would overflow and crash the device.

I eventually gave up and switched to HID, but even that was complex.

I now have one cpu thread per connected device, they poll the HID interface every 8 milliseconds and do async reads everywhere.

This works very well, but I have a lot of thread management going on throughout my engine and I am starting to think HID was a bad idea.

Logged

SAS~Storebror

  • Editor
  • member
  • Offline Offline
  • Posts: 23561
  • Taking a timeout
    • STFU
Re: Mike's new project
« Reply #45 on: May 08, 2018, 05:04:28 AM »

The interface in use actually is the generic HID interface since that's the one which works without any need for additional drivers on Windows.
I've faced the stalled USB protocol issue once briefly, when my initial sketch just polled the current button/switch/axis status and forwarded it straight to the PC.
That's a bad idea.
Even the poor old 8-bit 16MHz AVR CPU of my Teensy++ 2.0 manages to operate roughly 50.000 loops per second in this polling mode, and it's simply impossible to offload the amount of data on USB reliably that way.
When I tried, all test applications reading the raw USB data stalled and DirectInput could hardly keep up.

What I did now is to smoothen out spikes from axis readout both physically (1µF Capacitor on all poti wipers, with 10k internal resistance this results in a 16Hz Low Pass) and programmatically (ignore any changes below 2 steps, and smoothen readout across last 10 results).
Then I'm reacting on changed values only, so instead of polling anything, I'll only send a fresh datagram when something really changed (except for startup that is).
This way I got rid of 99.99% of the data being transferred and didn't encounter any USB stall on my 4-hour test session last sunday.

Cheers!
Mike
Logged
Don't split your mentality without thinking twice.

SAS~Malone

  • flying as #46 with the FAC
  • Editor
  • member
  • Offline Offline
  • Posts: 14562
  • proud member of that 'other' site
Re: Mike's new project
« Reply #46 on: May 08, 2018, 08:12:04 AM »

whoa, i've stumbled upon some dark arts magick trickery here, lol ..... :D
looking pretty interesting, i must say
Logged
.....taking fun seriously since 1968.....  8)

SAS~Storebror

  • Editor
  • member
  • Offline Offline
  • Posts: 23561
  • Taking a timeout
    • STFU
Re: Mike's new project
« Reply #47 on: May 08, 2018, 10:44:56 PM »

Temporary Labels for finding the best layout:


Semi-Final Labelling:


Note the white stickers: These are non-working buttons (hardware issue). Replacements have been ordered and should arrive today.
With hindsight, the upper and lower momentary switch rows should have been swapped, to make trim controls easier to reach, but that's life: You always find something left to improve ;)

Cheers!
Mike
Logged
Don't split your mentality without thinking twice.
Pages: 1 2 3 [4] 5 6 7   Go Up
 

Page created in 0.078 seconds with 22 queries.