Compiler Issues

Building a delta throttle? Make a thread here to share your progress and ask questions.

Compiler Issues

Postby Nemesisghost » Thu Apr 09, 2015 5:23 pm

I missed the memo that only an earlier version of the Arduino compiler will compile the source code, so I've been working on figuring out how to "fix" the source to get it to work. For the most part it was just figuring out what the latest version from Arduino was missing from the Delta Throttle source files & adding it back in. Now I've got the code to compile. We'll see if it'll work once I get the code values to match my setup(I missed wired a couple of things that'll be easier to fix in code). Once that's done I plan on cleaning up the code by removing all the unnecessary code and moving some things around. When I get it done I'll post it here.
Nemesisghost
 
Posts: 14
Joined: Thu Apr 09, 2015 12:44 am

Re: Compiler Issues

Postby Nemesisghost » Sun Apr 12, 2015 2:20 pm

I've gotten the code rewritten for the latest version of the Arduino Compiler & cleaned it up a bit. I've also added an Enable/Disable feature, which will re-initialize the throttle when enabled. I haven't tested it yet, so things could change. But this should help anyone out who didn't grab the older compiler.

Here's the Sketch. Some of the cosmetic changes I made was to make as many values constant as I could. I did the same to the Pin usage as well, which should make it easier to adjust to different wirings. At the moment I'm only reading 3 digital pins, so that'll need to be increased/decreased to match how many buttons you wish to have. If you don't want to have the Enable/Disable feature, you'll also need to set the "enabled" & "needInit" to TRUE.
Code: Select all
const bool DEBUG = false;  // set to true to debug the raw values

//Geometry
const float handle_rad = 1.75;     // end effector
const float base_rad = 1.75;     // base
const float pushrod_lng = 3.5;
const float pivot_lng = 2.25;

//Configuration
const float deadzone = 0.1;  // smaller values will be set to 0
const int gain = 150;

 // trigonometric constants
const float sqrt3 = sqrt(3.0);
const float pi = 3.141592653;    // PI
const float sin120 = sqrt3/2.0;   
const float cos120 = -0.5;       
const float tan60 = sqrt3;
const float sin30 = 0.5;
const float tan30 = 1/sqrt3;

//Pins Used
const int btn1Pin = 1;
const int btn2Pin = 2;
const int btn3Pin = 3;
const int ADC1Pin = A0; //Bottom Pot
const int ADC2Pin = A1; //Top/Right Pot
const int ADC3Pin = A2; //Top/Left
const int enabledLED = 6;
const int enabledInterrupt = 7;

//Global Values
volatile bool enabled = false;
volatile bool needInit = false;
float xZero, yZero, zZero;
float xValue, yValue, zValue;

void setup() {
  pinMode(ADC1Pin, INPUT);
  pinMode(ADC2Pin, INPUT);
  pinMode(ADC3Pin, INPUT);
  pinMode(btn1Pin, INPUT);
  pinMode(btn2Pin, INPUT);
  pinMode(btn3Pin, INPUT);
  digitalWrite(btn1Pin, HIGH);
  digitalWrite(btn2Pin, HIGH);
  digitalWrite(btn3Pin, HIGH);
  attachInterrupt(enabledInterrupt, interrupt, FALLING);

  if (DEBUG) {
    Serial.begin(9600);
  }
}

void initialize()
{
    xZero = 0;
    yZero = 0;
    zZero = zValue;
    needInit = false;
}

void loop() {
  int xVal = 0;
  int yVal = 0;
  int zVal = 0;
  int btn1 = LOW;
  int btn2 = LOW;
  int btn3 = LOW;
 
  if(enabled)
  {
    getForwardKinematic();

    if(needInit) initialize();
 
    btn1 = !digitalRead(btn1Pin);
    btn2 = !digitalRead(btn2Pin);
    btn3 = !digitalRead(btn3Pin);
 
    xVal = GetAxisValue(xValue, xZero, gain, deadzone, -100, 100);
    yVal = GetAxisValue(yValue, yZero, gain, deadzone, -100, 100);
    zVal = GetAxisValue(yValue, zZero, gain, deadzone, -100, 100);   
     
    if (DEBUG) {
      Serial.print("X: ");
      Serial.println(xVal);
      Serial.print("Y: ");
      Serial.println(yVal);
      Serial.print("Z: ");
      Serial.println(zVal);
      Serial.print("B1: ");
      Serial.println(btn1);
      Serial.print("B2: ");
      Serial.println(btn2);
      Serial.print("B3: ");
      Serial.println(btn3);
    }
  }
 
  if(enabled)
  {
    Joystick.SetAxis(Joystick_::XAXIS, map(xVal, -100, 100, 0, 255));
    Joystick.SetAxis(Joystick_::YAXIS, map(yVal, -100, 100, 0, 255));
    Joystick.SetAxis(Joystick_::ZAXIS, map(zVal, -100, 100, 0, 255));
    Joystick.SetButton(1, btn1 == HIGH);
    Joystick.SetButton(2, btn2 == HIGH);
    Joystick.SetButton(3, btn3 == HIGH);
    digitalWrite(enabledLED, HIGH);
  }
  else
  {
    Joystick.SetAxis(Joystick_::XAXIS, 0);
    Joystick.SetAxis(Joystick_::YAXIS, 0);
    Joystick.SetAxis(Joystick_::ZAXIS, 0);
    Joystick.SetButton(1, false);
    Joystick.SetButton(2, false);
    Joystick.SetButton(3, false);
    digitalWrite(enabledLED, LOW);
  }

  // Send to USB
  Joystick.ReportState();

  if (DEBUG)
    delay(1000);
}

void interrupt()
{
  if(enabled)
  {
    enabled = false;
  }
  else
  {
    enabled = true;
    needInit = true;
  } 
}
//The delta kinematic math
//You shouldn't need to change anything here.
void getForwardKinematic()
{
  //p1 is the bottom joint, p2 is the top right, p3 is the top left
  int p1ADC = analogRead(ADC1Pin);
  int p2ADC = analogRead(ADC2Pin);
  int p3ADC = analogRead(ADC3Pin);


  //get the angle of each joint in radians
  float theta1 = (p1ADC - 60) * 0.003475;
  float theta2 = (p2ADC - 60) * 0.003475;
  float theta3 = (p3ADC - 60) * 0.003475;

  float t = base_rad - handle_rad;
  float y1 = -(t + pivot_lng * cos(theta1));
  float z1 = pivot_lng * sin(theta1);

  float y2 = (t + pivot_lng * cos(theta2)) * sin30;
  float x2 = y2 * tan60;
  float z2 = pivot_lng * sin(theta2);

  float y3 = (t + pivot_lng * cos(theta3)) * sin30;
  float x3 = -y3 * tan60;
  float z3 = pivot_lng * sin(theta3);

  float dnm = (y2 - y1) * x3 - (y3 - y1) * x2;

  float w1 = y1 * y1 + z1 * z1;
  float w2 = x2 * x2 + y2 * y2 + z2 * z2;
  float w3 = x3 * x3 + y3 * y3 + z3 * z3;

  // x = (a1*z + b1)/dnm
  float a1 = (z2 - z1) * (y3 - y1) - (z3 - z1) * (y2 - y1);
  float b1 = -((w2 - w1) * (y3 - y1) - (w3 - w1) * (y2 - y1)) / 2.0;

  // y = (a2*z + b2)/dnm;
  float a2 = -(z2 - z1) * x3 + (z3 - z1) * x2;
  float b2 = ((w2 - w1) * x3 - (w3 - w1) * x2) / 2.0;

  // a*z^2 + b*z + c = 0
  float a = a1 * a1 + a2 * a2 + dnm * dnm;
  float b = 2 * (a1 * b1 + a2 * (b2 - y1 * dnm) - z1 * dnm * dnm);
  float c = (b2 - y1 * dnm) * (b2 - y1 * dnm) + b1 * b1 + dnm * dnm * (z1 * z1 - pushrod_lng * pushrod_lng);

  // discriminant
  float d = b * b - 4.0 * a * c;

  zValue = 0.5 * (-b + sqrt(d)) / a;
  xValue = (a1 * zValue + b1) / dnm;
  yValue = (a2 * zValue + b2) / dnm;

  if (DEBUG) {
    Serial.print("T1: ");
    Serial.println(theta1);
    Serial.print("T2: ");
    Serial.println(theta2);
    Serial.print("T3: ");
    Serial.println(theta3);
    Serial.print("Z1: ");
    Serial.println(z1);
    Serial.print("Z2: ");
    Serial.println(z2);
    Serial.print("Z3: ");
    Serial.println(z3);
    Serial.print("DNM: ");
    Serial.println(dnm);

  }
}

float GetAxisValue(float value, float zero, float gain, float deadZone, float minValue, float maxValue)
{
    float rtValue = value - zero;
   
    if (rtValue < -1 * deadZone)
    {
      rtValue += deadZone;
    }
    else if (rtValue > deadZone)
    {
      rtValue -= deadZone;
    }
    else
    {
      rtValue = 0;
    }
   
    //apply gain
    rtValue = rtValue * gain;
 
    //constrain outputs to +- 100
    rtValue = constrain(rtValue, minValue, maxValue); 
   
    return rtValue;
}


From the USBAPI.h I removed the Keyboard & Mouse functionality, and added an improved Joystick class. The Joystick class contains methods for setting each axis(6 total) & up to 32 buttons.
Code: Select all
/*
  USBAPI.h
  Copyright (c) 2005-2014 Arduino.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#ifndef __USBAPI__
#define __USBAPI__

#include <inttypes.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <util/delay.h>

typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned long u32;

#include "Arduino.h"

#if defined(USBCON)

#include "USBDesc.h"
#include "USBCore.h"

//================================================================================
//================================================================================
//   USB

class USBDevice_
{
public:
   USBDevice_();
   bool configured();

   void attach();
   void detach();   // Serial port goes down too...
   void poll();
};
extern USBDevice_ USBDevice;

//================================================================================
//================================================================================
//   Serial over CDC (Serial1 is the physical port)

struct ring_buffer;

#if (RAMEND < 1000)
#define SERIAL_BUFFER_SIZE 16
#else
#define SERIAL_BUFFER_SIZE 64
#endif

class Serial_ : public Stream
{
private:
   int peek_buffer;
public:
   Serial_() { peek_buffer = -1; };
   void begin(unsigned long);
   void begin(unsigned long, uint8_t);
   void end(void);

   virtual int available(void);
   virtual int peek(void);
   virtual int read(void);
   virtual void flush(void);
   virtual size_t write(uint8_t);
   virtual size_t write(const uint8_t*, size_t);
   using Print::write; // pull in write(str) and write(buf, size) from Print
   operator bool();

   volatile uint8_t _rx_buffer_head;
   volatile uint8_t _rx_buffer_tail;
   unsigned char _rx_buffer[SERIAL_BUFFER_SIZE];
};
extern Serial_ Serial;

#define HAVE_CDCSERIAL

//================================================================================
//================================================================================
//   Joystick
//  Implemented in HID.cpp
//  The list of parameters here needs to match the implementation in HID.cpp

class Joystick_
{   
   private:
      uint32_t buttons;
      uint8_t axes[6];
   public:
      Joystick_();
      void ReportState();
      void SetAxis(int Axis, uint8_t value);
      void SetButton(int Button, bool value);
   
      static const int XAXIS = 0;
      static const int YAXIS = 1;
      static const int ZAXIS = 2;
      static const int RXAXIS = 3;
      static const int RYAXIS = 4;
      static const int RZAXIS = 5;
};
extern Joystick_ Joystick;
//================================================================================
//================================================================================
//   Low level API

typedef struct
{
   uint8_t bmRequestType;
   uint8_t bRequest;
   uint8_t wValueL;
   uint8_t wValueH;
   uint16_t wIndex;
   uint16_t wLength;
} Setup;

//================================================================================
//================================================================================
//   HID 'Driver'

int      HID_GetInterface(uint8_t* interfaceNum);
int      HID_GetDescriptor(int i);
bool   HID_Setup(Setup& setup);
void   HID_SendReport(uint8_t id, const void* data, int len);

//================================================================================
//================================================================================
//   MSC 'Driver'

int      MSC_GetInterface(uint8_t* interfaceNum);
int      MSC_GetDescriptor(int i);
bool   MSC_Setup(Setup& setup);
bool   MSC_Data(uint8_t rx,uint8_t tx);

//================================================================================
//================================================================================
//   CSC 'Driver'

int      CDC_GetInterface(uint8_t* interfaceNum);
int      CDC_GetDescriptor(int i);
bool   CDC_Setup(Setup& setup);

//================================================================================
//================================================================================

#define TRANSFER_PGM      0x80
#define TRANSFER_RELEASE   0x40
#define TRANSFER_ZERO      0x20

int USB_SendControl(uint8_t flags, const void* d, int len);
int USB_RecvControl(void* d, int len);

uint8_t   USB_Available(uint8_t ep);
int USB_Send(uint8_t ep, const void* data, int len);   // blocking
int USB_Recv(uint8_t ep, void* data, int len);      // non-blocking
int USB_Recv(uint8_t ep);                     // non-blocking
void USB_Flush(uint8_t ep);

#endif

#endif /* if defined(USBCON) */


For the HID.cpp I did quite a bit of changing. First off, my HID Descriptor is setup to only register 16 buttons, but has 5 axes. This is because I have a PSP thumb stick with 2 additional axes & it's easier to keep the number of buttons as multiples of 8. I tried to keep the hat switch in the HID Descriptor, but when I did it was 67 bytes long, which is a problem because I don't know how to report an HID Descriptor larger than 64 bytes(which is the default). Since the 2 extra axes take up 4 bytes, if you wanted to replace them with the Hat Stick you should be able to. Also, even though the Joystick class will allow 6 axes & 32 buttons, my implementation will only send 5 axes & 16 bytes.
Code: Select all
/* Copyright (c) 2011, Peter Barrett 
** 
** Permission to use, copy, modify, and/or distribute this software for 
** any purpose with or without fee is hereby granted, provided that the 
** above copyright notice and this permission notice appear in all copies. 
**
** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 
** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 
** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 
** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES 
** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 
** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
** SOFTWARE. 
*/

#include "USBAPI.h"

#if defined(USBCON)
#ifdef HID_ENABLED

//#define RAWHID_ENABLED

Joystick_ Joystick;

//================================================================================
//================================================================================

//   HID report descriptor

#define LSB(_x) ((_x) & 0xFF)
#define MSB(_x) ((_x) >> 8)

#define RAWHID_USAGE_PAGE   0xFFC0
#define RAWHID_USAGE      0x0C00
#define RAWHID_TX_SIZE 64
#define RAWHID_RX_SIZE 64

extern const u8 _hidReportDescriptor[] PROGMEM;
const u8 _hidReportDescriptor[] = {   
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x04,                    // USAGE (Joystick)
    0xa1, 0x01,                    // COLLECTION (Application)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x10,                    //     USAGE_MAXIMUM (Button 16)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x10,                    //     REPORT_COUNT (16)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x09, 0x32,                    //     USAGE (Z)
    0x09, 0x33,                    //     USAGE (Rx)
    0x09, 0x34,                    //     USAGE (Ry)
    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x05,                    //     REPORT_COUNT (5)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0xc0,                          //   END_COLLECTION
    0xc0                           // END_COLLECTION
};

extern const HIDDescriptor _hidInterface PROGMEM;
const HIDDescriptor _hidInterface =
{
   D_INTERFACE(HID_INTERFACE,1,3,0,0),
   D_HIDREPORT(sizeof(_hidReportDescriptor)),
   D_ENDPOINT(USB_ENDPOINT_IN (HID_ENDPOINT_INT),USB_ENDPOINT_TYPE_INTERRUPT,0x40,0x01)
};

//================================================================================
//================================================================================
//   Driver

u8 _hid_protocol = 1;
u8 _hid_idle = 1;

#define WEAK __attribute__ ((weak))

int WEAK HID_GetInterface(u8* interfaceNum)
{
   interfaceNum[0] += 1;   // uses 1
   return USB_SendControl(TRANSFER_PGM,&_hidInterface,sizeof(_hidInterface));
}

int WEAK HID_GetDescriptor(int /* i */)
{
   return USB_SendControl(TRANSFER_PGM,_hidReportDescriptor,sizeof(_hidReportDescriptor));
}

void WEAK HID_SendReport(u8 id, const void* data, int len)
{
   USB_Send(HID_TX, &id, 1);
   USB_Send(HID_TX | TRANSFER_RELEASE,data,len);
}

bool WEAK HID_Setup(Setup& setup)
{
   u8 r = setup.bRequest;
   u8 requestType = setup.bmRequestType;
   if (REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType)
   {
      if (HID_GET_REPORT == r)
      {
         //HID_GetReport();
         return true;
      }
      if (HID_GET_PROTOCOL == r)
      {
         //Send8(_hid_protocol);   // TODO
         return true;
      }
   }
   
   if (REQUEST_HOSTTODEVICE_CLASS_INTERFACE == requestType)
   {
      if (HID_SET_PROTOCOL == r)
      {
         _hid_protocol = setup.wValueL;
         return true;
      }

      if (HID_SET_IDLE == r)
      {
         _hid_idle = setup.wValueL;
         return true;
      }
   }
   return false;
}
//================================================================================
//================================================================================
//   Joystick
//  Usage: Joystick.move(inputs go here)
//
//  The report data format must match the one defined in the descriptor exactly
//  or it either won't work, or the pc will make a mess of unpacking the data
//
Joystick_::Joystick_()
{
}

void Joystick_::SetAxis(int Axis, uint8_t value)
{
   if(Axis >= 0 && Axis < 6)
   {
      axes[Axis] = value;
   }
}

void Joystick_::SetButton(int Button, bool value)
{
   if(Button > 0 && Button <= 32)
   {
      uint32_t temp = 1;
      temp <<= (Button - 1);
      if(value)
      {
         buttons |= temp;
      }
      else
      {
         temp = ~temp;
         buttons &= temp;
      }
   }
}

#define joyBytes 7

void Joystick_::ReportState()
{
   uint8_t data[joyBytes];
   
   memcpy(data, &buttons, 2);   
   memcpy(data+2, axes, 5);   

   USB_Send(HID_TX | TRANSFER_RELEASE,data,joyBytes);
}

#endif

#endif /* if defined(USBCON) */
Nemesisghost
 
Posts: 14
Joined: Thu Apr 09, 2015 12:44 am

Re: Compiler Issues

Postby Nemesisghost » Sun Apr 12, 2015 2:26 pm

I didn't change the USBCore.cpp much from what comes with the Compiler. All I did was add the code so that the device would register itself as a "Delta Throttle".

Code: Select all

/* Copyright (c) 2010, Peter Barrett 
** 
** Permission to use, copy, modify, and/or distribute this software for 
** any purpose with or without fee is hereby granted, provided that the 
** above copyright notice and this permission notice appear in all copies. 
**
** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 
** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 
** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 
** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES 
** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 
** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
** SOFTWARE. 
*/

#include "USBAPI.h"

#if defined(USBCON)

#define EP_TYPE_CONTROL            0x00
#define EP_TYPE_BULK_IN            0x81
#define EP_TYPE_BULK_OUT         0x80
#define EP_TYPE_INTERRUPT_IN      0xC1
#define EP_TYPE_INTERRUPT_OUT      0xC0
#define EP_TYPE_ISOCHRONOUS_IN      0x41
#define EP_TYPE_ISOCHRONOUS_OUT      0x40

/** Pulse generation counters to keep track of the number of milliseconds remaining for each pulse type */
#define TX_RX_LED_PULSE_MS 100
volatile u8 TxLEDPulse; /**< Milliseconds remaining for data Tx LED pulse */
volatile u8 RxLEDPulse; /**< Milliseconds remaining for data Rx LED pulse */

//==================================================================
//==================================================================

extern const u16 STRING_LANGUAGE[] PROGMEM;
extern const u8 STRING_PRODUCT[] PROGMEM;
extern const u8 STRING_MANUFACTURER[] PROGMEM;
extern const DeviceDescriptor USB_DeviceDescriptor PROGMEM;
extern const DeviceDescriptor USB_DeviceDescriptorA PROGMEM;

const u16 STRING_LANGUAGE[2] = {
   (3<<8) | (2+2),
   0x0409   // English
};

#ifndef USB_PRODUCT
// If no product is provided, use USB IO Board
#define USB_PRODUCT     "Delta Throttle"
#endif

const u8 STRING_PRODUCT[] PROGMEM = "Delta Throttle";

#if USB_VID == 0x2341
#  if defined(USB_MANUFACTURER)
#    undef USB_MANUFACTURER
#  endif
#  define USB_MANUFACTURER "Arduino LLC"
#elif USB_VID == 0x1b4f
#  if defined(USB_MANUFACTURER)
#    undef USB_MANUFACTURER
#  endif
#  define USB_MANUFACTURER "SparkFun"
#elif !defined(USB_MANUFACTURER)
// Fall through to unknown if no manufacturer name was provided in a macro
#  define USB_MANUFACTURER "Unknown"
#endif

const u8 STRING_MANUFACTURER[] PROGMEM = USB_MANUFACTURER;


#ifdef CDC_ENABLED
#define DEVICE_CLASS 0x02
#else
#define DEVICE_CLASS 0x00
#endif

//   DEVICE DESCRIPTOR
const DeviceDescriptor USB_DeviceDescriptor =
   D_DEVICE(0x00,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1);

const DeviceDescriptor USB_DeviceDescriptorA =
   D_DEVICE(DEVICE_CLASS,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1);

//==================================================================
//==================================================================

volatile u8 _usbConfiguration = 0;

static inline void WaitIN(void)
{
   while (!(UEINTX & (1<<TXINI)))
      ;
}

static inline void ClearIN(void)
{
   UEINTX = ~(1<<TXINI);
}

static inline void WaitOUT(void)
{
   while (!(UEINTX & (1<<RXOUTI)))
      ;
}

static inline u8 WaitForINOrOUT()
{
   while (!(UEINTX & ((1<<TXINI)|(1<<RXOUTI))))
      ;
   return (UEINTX & (1<<RXOUTI)) == 0;
}

static inline void ClearOUT(void)
{
   UEINTX = ~(1<<RXOUTI);
}

void Recv(volatile u8* data, u8 count)
{
   while (count--)
      *data++ = UEDATX;
   
   RXLED1;               // light the RX LED
   RxLEDPulse = TX_RX_LED_PULSE_MS;   
}

static inline u8 Recv8()
{
   RXLED1;               // light the RX LED
   RxLEDPulse = TX_RX_LED_PULSE_MS;

   return UEDATX;   
}

static inline void Send8(u8 d)
{
   UEDATX = d;
}

static inline void SetEP(u8 ep)
{
   UENUM = ep;
}

static inline u8 FifoByteCount()
{
   return UEBCLX;
}

static inline u8 ReceivedSetupInt()
{
   return UEINTX & (1<<RXSTPI);
}

static inline void ClearSetupInt()
{
   UEINTX = ~((1<<RXSTPI) | (1<<RXOUTI) | (1<<TXINI));
}

static inline void Stall()
{
   UECONX = (1<<STALLRQ) | (1<<EPEN);
}

static inline u8 ReadWriteAllowed()
{
   return UEINTX & (1<<RWAL);
}

static inline u8 Stalled()
{
   return UEINTX & (1<<STALLEDI);
}

static inline u8 FifoFree()
{
   return UEINTX & (1<<FIFOCON);
}

static inline void ReleaseRX()
{
   UEINTX = 0x6B;   // FIFOCON=0 NAKINI=1 RWAL=1 NAKOUTI=0 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=1
}

static inline void ReleaseTX()
{
   UEINTX = 0x3A;   // FIFOCON=0 NAKINI=0 RWAL=1 NAKOUTI=1 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=0
}

static inline u8 FrameNumber()
{
   return UDFNUML;
}

//==================================================================
//==================================================================

u8 USBGetConfiguration(void)
{
   return _usbConfiguration;
}

#define USB_RECV_TIMEOUT
class LockEP
{
   u8 _sreg;
public:
   LockEP(u8 ep) : _sreg(SREG)
   {
      cli();
      SetEP(ep & 7);
   }
   ~LockEP()
   {
      SREG = _sreg;
   }
};

//   Number of bytes, assumes a rx endpoint
u8 USB_Available(u8 ep)
{
   LockEP lock(ep);
   return FifoByteCount();
}

//   Non Blocking receive
//   Return number of bytes read
int USB_Recv(u8 ep, void* d, int len)
{
   if (!_usbConfiguration || len < 0)
      return -1;
   
   LockEP lock(ep);
   u8 n = FifoByteCount();
   len = min(n,len);
   n = len;
   u8* dst = (u8*)d;
   while (n--)
      *dst++ = Recv8();
   if (len && !FifoByteCount())   // release empty buffer
      ReleaseRX();
   
   return len;
}

//   Recv 1 byte if ready
int USB_Recv(u8 ep)
{
   u8 c;
   if (USB_Recv(ep,&c,1) != 1)
      return -1;
   return c;
}

//   Space in send EP
u8 USB_SendSpace(u8 ep)
{
   LockEP lock(ep);
   if (!ReadWriteAllowed())
      return 0;
   return 64 - FifoByteCount();
}

//   Blocking Send of data to an endpoint
int USB_Send(u8 ep, const void* d, int len)
{
   if (!_usbConfiguration)
      return -1;

   int r = len;
   const u8* data = (const u8*)d;
   u8 timeout = 250;      // 250ms timeout on send? TODO
   while (len)
   {
      u8 n = USB_SendSpace(ep);
      if (n == 0)
      {
         if (!(--timeout))
            return -1;
         delay(1);
         continue;
      }

      if (n > len)
         n = len;
      {
         LockEP lock(ep);
         // Frame may have been released by the SOF interrupt handler
         if (!ReadWriteAllowed())
            continue;
         len -= n;
         if (ep & TRANSFER_ZERO)
         {
            while (n--)
               Send8(0);
         }
         else if (ep & TRANSFER_PGM)
         {
            while (n--)
               Send8(pgm_read_byte(data++));
         }
         else
         {
            while (n--)
               Send8(*data++);
         }
         if (!ReadWriteAllowed() || ((len == 0) && (ep & TRANSFER_RELEASE)))   // Release full buffer
            ReleaseTX();
      }
   }
   TXLED1;               // light the TX LED
   TxLEDPulse = TX_RX_LED_PULSE_MS;
   return r;
}

extern const u8 _initEndpoints[] PROGMEM;
const u8 _initEndpoints[] =
{
   0,
   
#ifdef CDC_ENABLED
   EP_TYPE_INTERRUPT_IN,      // CDC_ENDPOINT_ACM
   EP_TYPE_BULK_OUT,         // CDC_ENDPOINT_OUT
   EP_TYPE_BULK_IN,         // CDC_ENDPOINT_IN
#endif

#ifdef HID_ENABLED
   EP_TYPE_INTERRUPT_IN      // HID_ENDPOINT_INT
#endif
};

#define EP_SINGLE_64 0x32   // EP0
#define EP_DOUBLE_64 0x36   // Other endpoints

static
void InitEP(u8 index, u8 type, u8 size)
{
   UENUM = index;
   UECONX = 1;
   UECFG0X = type;
   UECFG1X = size;
}

static
void InitEndpoints()
{
   for (u8 i = 1; i < sizeof(_initEndpoints); i++)
   {
      UENUM = i;
      UECONX = 1;
      UECFG0X = pgm_read_byte(_initEndpoints+i);
      UECFG1X = EP_DOUBLE_64;
   }
   UERST = 0x7E;   // And reset them
   UERST = 0;
}

//   Handle CLASS_INTERFACE requests
static
bool ClassInterfaceRequest(Setup& setup)
{
   u8 i = setup.wIndex;

#ifdef CDC_ENABLED
   if (CDC_ACM_INTERFACE == i)
      return CDC_Setup(setup);
#endif

#ifdef HID_ENABLED
   if (HID_INTERFACE == i)
      return HID_Setup(setup);
#endif
   return false;
}

int _cmark;
int _cend;
void InitControl(int end)
{
   SetEP(0);
   _cmark = 0;
   _cend = end;
}

static
bool SendControl(u8 d)
{
   if (_cmark < _cend)
   {
      if (!WaitForINOrOUT())
         return false;
      Send8(d);
      if (!((_cmark + 1) & 0x3F))
         ClearIN();   // Fifo is full, release this packet
   }
   _cmark++;
   return true;
};

//   Clipped by _cmark/_cend
int USB_SendControl(u8 flags, const void* d, int len)
{
   int sent = len;
   const u8* data = (const u8*)d;
   bool pgm = flags & TRANSFER_PGM;
   while (len--)
   {
      u8 c = pgm ? pgm_read_byte(data++) : *data++;
      if (!SendControl(c))
         return -1;
   }
   return sent;
}

// Send a USB descriptor string. The string is stored in PROGMEM as a
// plain ASCII string but is sent out as UTF-16 with the correct 2-byte
// prefix
static bool USB_SendStringDescriptor(const u8*string_P, u8 string_len) {
        SendControl(2 + string_len * 2);
        SendControl(3);
        for(u8 i = 0; i < string_len; i++) {
                bool r = SendControl(pgm_read_byte(&string_P[i]));
                r &= SendControl(0); // high byte
                if(!r) {
                        return false;
                }
        }
        return true;
}

//   Does not timeout or cross fifo boundaries
//   Will only work for transfers <= 64 bytes
//   TODO
int USB_RecvControl(void* d, int len)
{
   WaitOUT();
   Recv((u8*)d,len);
   ClearOUT();
   return len;
}

int SendInterfaces()
{
   int total = 0;
   u8 interfaces = 0;

#ifdef CDC_ENABLED
   total = CDC_GetInterface(&interfaces);
#endif

#ifdef HID_ENABLED
   total += HID_GetInterface(&interfaces);
#endif

   return interfaces;
}

//   Construct a dynamic configuration descriptor
//   This really needs dynamic endpoint allocation etc
//   TODO
static
bool SendConfiguration(int maxlen)
{
   //   Count and measure interfaces
   InitControl(0);   
   int interfaces = SendInterfaces();
   ConfigDescriptor config = D_CONFIG(_cmark + sizeof(ConfigDescriptor),interfaces);

   //   Now send them
   InitControl(maxlen);
   USB_SendControl(0,&config,sizeof(ConfigDescriptor));
   SendInterfaces();
   return true;
}

u8 _cdcComposite = 0;

static
bool SendDescriptor(Setup& setup)
{
   u8 t = setup.wValueH;
   if (USB_CONFIGURATION_DESCRIPTOR_TYPE == t)
      return SendConfiguration(setup.wLength);

   InitControl(setup.wLength);
#ifdef HID_ENABLED
   if (HID_REPORT_DESCRIPTOR_TYPE == t)
      return HID_GetDescriptor(t);
#endif

   const u8* desc_addr = 0;
   if (USB_DEVICE_DESCRIPTOR_TYPE == t)
   {
      if (setup.wLength == 8)
         _cdcComposite = 1;
      desc_addr = _cdcComposite ?  (const u8*)&USB_DeviceDescriptorA : (const u8*)&USB_DeviceDescriptor;
   }
   else if (USB_STRING_DESCRIPTOR_TYPE == t)
   {
      if (setup.wValueL == 0) {
         desc_addr = (const u8*)&STRING_LANGUAGE;
      }
      else if (setup.wValueL == IPRODUCT) {
         return USB_SendStringDescriptor(STRING_PRODUCT, strlen(USB_PRODUCT));
      }
      else if (setup.wValueL == IMANUFACTURER) {
         return USB_SendStringDescriptor(STRING_MANUFACTURER, strlen(USB_MANUFACTURER));
      }
      else
         return false;
   }

   if (desc_addr == 0)
      return false;
   u8 desc_length = pgm_read_byte(desc_addr);

   USB_SendControl(TRANSFER_PGM,desc_addr,desc_length);
   return true;
}

//   Endpoint 0 interrupt
ISR(USB_COM_vect)
{
    SetEP(0);
   if (!ReceivedSetupInt())
      return;

   Setup setup;
   Recv((u8*)&setup,8);
   ClearSetupInt();

   u8 requestType = setup.bmRequestType;
   if (requestType & REQUEST_DEVICETOHOST)
      WaitIN();
   else
      ClearIN();

    bool ok = true;
   if (REQUEST_STANDARD == (requestType & REQUEST_TYPE))
   {
      //   Standard Requests
      u8 r = setup.bRequest;
      if (GET_STATUS == r)
      {
         Send8(0);      // TODO
         Send8(0);
      }
      else if (CLEAR_FEATURE == r)
      {
      }
      else if (SET_FEATURE == r)
      {
      }
      else if (SET_ADDRESS == r)
      {
         WaitIN();
         UDADDR = setup.wValueL | (1<<ADDEN);
      }
      else if (GET_DESCRIPTOR == r)
      {
         ok = SendDescriptor(setup);
      }
      else if (SET_DESCRIPTOR == r)
      {
         ok = false;
      }
      else if (GET_CONFIGURATION == r)
      {
         Send8(1);
      }
      else if (SET_CONFIGURATION == r)
      {
         if (REQUEST_DEVICE == (requestType & REQUEST_RECIPIENT))
         {
            InitEndpoints();
            _usbConfiguration = setup.wValueL;
         } else
            ok = false;
      }
      else if (GET_INTERFACE == r)
      {
      }
      else if (SET_INTERFACE == r)
      {
      }
   }
   else
   {
      InitControl(setup.wLength);      //   Max length of transfer
      ok = ClassInterfaceRequest(setup);
   }

   if (ok)
      ClearIN();
   else
   {
      Stall();
   }
}

void USB_Flush(u8 ep)
{
   SetEP(ep);
   if (FifoByteCount())
      ReleaseTX();
}

//   General interrupt
ISR(USB_GEN_vect)
{
   u8 udint = UDINT;
   UDINT = 0;

   //   End of Reset
   if (udint & (1<<EORSTI))
   {
      InitEP(0,EP_TYPE_CONTROL,EP_SINGLE_64);   // init ep0
      _usbConfiguration = 0;         // not configured yet
      UEIENX = 1 << RXSTPE;         // Enable interrupts for ep0
   }

   //   Start of Frame - happens every millisecond so we use it for TX and RX LED one-shot timing, too
   if (udint & (1<<SOFI))
   {
#ifdef CDC_ENABLED
      USB_Flush(CDC_TX);            // Send a tx frame if found
#endif
      
      // check whether the one-shot period has elapsed.  if so, turn off the LED
      if (TxLEDPulse && !(--TxLEDPulse))
         TXLED0;
      if (RxLEDPulse && !(--RxLEDPulse))
         RXLED0;
   }
}

//   VBUS or counting frames
//   Any frame counting?
u8 USBConnected()
{
   u8 f = UDFNUML;
   delay(3);
   return f != UDFNUML;
}

//=======================================================================
//=======================================================================

USBDevice_ USBDevice;

USBDevice_::USBDevice_()
{
}

void USBDevice_::attach()
{
   _usbConfiguration = 0;
   UHWCON = 0x01;                  // power internal reg
   USBCON = (1<<USBE)|(1<<FRZCLK);      // clock frozen, usb enabled
#if F_CPU == 16000000UL
   PLLCSR = 0x12;                  // Need 16 MHz xtal
#elif F_CPU == 8000000UL
   PLLCSR = 0x02;                  // Need 8 MHz xtal
#endif
   while (!(PLLCSR & (1<<PLOCK)))      // wait for lock pll
      ;

   // Some tests on specific versions of macosx (10.7.3), reported some
   // strange behaviuors when the board is reset using the serial
   // port touch at 1200 bps. This delay fixes this behaviour.
   delay(1);

   USBCON = ((1<<USBE)|(1<<OTGPADE));   // start USB clock
   UDIEN = (1<<EORSTE)|(1<<SOFE);      // Enable interrupts for EOR (End of Reset) and SOF (start of frame)
   UDCON = 0;                     // enable attach resistor
   
   TX_RX_LED_INIT;
}

void USBDevice_::detach()
{
}

//   Check for interrupts
//   TODO: VBUS detection
bool USBDevice_::configured()
{
   return _usbConfiguration;
}

void USBDevice_::poll()
{
}

#endif /* if defined(USBCON) */
Nemesisghost
 
Posts: 14
Joined: Thu Apr 09, 2015 12:44 am

Re: Compiler Issues

Postby jeruw » Sun Apr 12, 2015 4:48 pm

Would you be willing/able to re-apply these code changes in a fork of the project on GitHub? That will make it a lot easier to review changes and for zdayton or others to pull bits into their own repositories.

Great work, by the way!
jeruw
 
Posts: 80
Joined: Tue Feb 17, 2015 3:10 pm

Re: Compiler Issues

Postby Nemesisghost » Sun Apr 12, 2015 11:02 pm

Never worked with Git Hub, so I have no idea how to even go about doing that. I am a software developer by trade, so I'm familiar with source repos, just gonna take a bit to figure out.
Nemesisghost
 
Posts: 14
Joined: Thu Apr 09, 2015 12:44 am

Re: Compiler Issues

Postby SC-Maik » Thu May 28, 2015 8:21 am

How can we program throttle to register as a 10bit resolution joystick (0-1023 per axis) and not 8bit (0-255)?

Since we are moving towards hall effect sensors, shouldn't we also use full potential of both Pro Micro ADC and sensors' greater resolution?
SC-Maik
 
Posts: 142
Joined: Tue Mar 03, 2015 3:20 pm

Re: Compiler Issues

Postby mike43110 » Wed Jun 03, 2015 1:17 pm

SC-Maik wrote:How can we program throttle to register as a 10bit resolution joystick (0-1023 per axis) and not 8bit (0-255)?

Since we are moving towards hall effect sensors, shouldn't we also use full potential of both Pro Micro ADC and sensors' greater resolution?


I would like that as well!

But I am not to sure on how to do it... I am looking it up. A pull request would be great for this kind of thing as then everyone's work can be integrated.
mike43110
 
Posts: 21
Joined: Fri Apr 24, 2015 11:45 pm

Re: Compiler Issues

Postby Nemesisghost » Wed Jun 10, 2015 2:03 pm

I'm not sure that you can exactly. The reason is the limitations on the HID descriptor. All of the examples limit the range to 1 byte(0-255 or -127-127), but I've not seen anything that says you can't do more. At the same time I'd make sure my values stuck to byte boundaries, otherwise you'll have to deal with spacer bits. Which is why I have 16 buttons in my HID descriptor while only using 4. So don't do 10-bit, go to 16-bit or +/- 32K.

What you'll need to do is get an HID descriptor writing program, then once you've built your descriptor with the larger values & replace my HID descriptor in the HID.cpp file. Next, you'll want to make the necessary changes to the Joystick_ object defined in that same file. I have all my axes in an array of unsigned 8-bit integers & then memcpy that to the byte array sent through the USB methods. Since I have more than the 3 axes for the throttle, you'll probably want to break that up & store the non-throttle axes in 1 array & the throttle axes in another, unless you plan on reporting all axes with the larger range. If you use 2 different axes arrays, you'll need to add a new memcpy line, and in either case you'll have to adjust the copy length to match your new array(s). Whatever you do, make sure that your byte array matches the HID Descriptor in length & bit placement.
Nemesisghost
 
Posts: 14
Joined: Thu Apr 09, 2015 12:44 am

Re: Compiler Issues

Postby mike43110 » Wed Jun 10, 2015 4:21 pm

Nemesisghost wrote:I'm not sure that you can exactly. The reason is the limitations on the HID descriptor. All of the examples limit the range to 1 byte(0-255 or -127-127), but I've not seen anything that says you can't do more. At the same time I'd make sure my values stuck to byte boundaries, otherwise you'll have to deal with spacer bits. Which is why I have 16 buttons in my HID descriptor while only using 4. So don't do 10-bit, go to 16-bit or +/- 32K.

What you'll need to do is get an HID descriptor writing program, then once you've built your descriptor with the larger values & replace my HID descriptor in the HID.cpp file. Next, you'll want to make the necessary changes to the Joystick_ object defined in that same file. I have all my axes in an array of unsigned 8-bit integers & then memcpy that to the byte array sent through the USB methods. Since I have more than the 3 axes for the throttle, you'll probably want to break that up & store the non-throttle axes in 1 array & the throttle axes in another, unless you plan on reporting all axes with the larger range. If you use 2 different axes arrays, you'll need to add a new memcpy line, and in either case you'll have to adjust the copy length to match your new array(s). Whatever you do, make sure that your byte array matches the HID Descriptor in length & bit placement.


Quite interesting! I haven't dealt with USB before so this will be something - a future problem as I have other things that need to be done (booo - at least SC is taking forever! (game not person)). Will the descriptor also deal with the multiple reports that have to be sent? i.e. each 64-bit block.

I know multiple reports can be sent to deal with larger sizes so you have to store the number of reports when describing the descriptor. I though have NO idea how to do that exactly. Once my work is done and my personal designs are as well then I will only deal with the software.
mike43110
 
Posts: 21
Joined: Fri Apr 24, 2015 11:45 pm

Re: Compiler Issues

Postby Nemesisghost » Thu Jun 11, 2015 11:50 am

You define the reports in the HID Descriptor. I know there are some limitations on how large each report can be, which is one of the reasons I didn't group the axes(something that's not entirely necessary, but is ascetically pleasing in the joystick properties screen). I didn't do multiple reports just because 1 was enough to do what I needed. But it was something I was looking into so I could group my axes.

Here's a good tutorial on how to build HID descriptors & how to align the bits to match. It has a link to the official USB HID documentation, which itself has an HID writing app. That app is what I used to build mine.
Nemesisghost
 
Posts: 14
Joined: Thu Apr 09, 2015 12:44 am

Next

Return to Build progress and showcase

Who is online

Users browsing this forum: No registered users and 0 guests

cron