├── 3DPro.c ├── 3DPro.h ├── 3DProasm.S ├── FFPJoystick.cpp ├── FFPJoystick.h ├── FFPtest └── FFPtest.ino ├── ffb.c ├── ffb.h └── includes.h /3DPro.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * File Name : 3DPro.c 3 | * Project : 3DP-Vert, Microsoft Sidewinder 3D Pro/PP/FFP to USB converter 4 | * Date : 2005/05/31, 2006/12/14, 2008/02/12, 2009/06/26 5 | * Version : 4.0 + MODS 6 | * Target MCU : AT90USB162/82, AT90USB646/1286, ATMEGA16U4/32U4 7 | * Tool Chain : Atmel AVR Studio 4.18 716 / WinAVR 20100110 8 | * Author : Detlef "Grendel" Mueller 9 | * detlef@gmail.com 10 | * References : Sidewinder.c by Vojtech Pavlik 11 | * Patent #5628686, Apperatus and Method for Bidirectional 12 | * Data Communication in a Gameport, by Microsoft 13 | * Description : 3D Pro driver and hardware interface 14 | * Device : Microsoft SideWinder 3D Pro, Precision Pro, Force Feedback Pro 15 | * 16 | * Release Notes: 17 | * 18 | * ================ 19 | * WARNING: This version has been heavily modified to only support 20 | * Force Feedback Pro and ATmega32U4. 21 | * Do not use except for testing purposes! 22 | * 23 | * -- tloimu 24 | * ================ 25 | * 26 | * 27 | * Pinout AT90USBX2 (out dated!) 28 | * ================ 29 | * 30 | * PB0 -- PC0 (XTAL2) PD0 Button1 31 | * PB1 (SCLK) PC1 (/RESET) PD1 Button2 32 | * PB2 (MOSI) PC2 -- PD2 Button3 33 | * PB3 (MISO) PD3 Button4 34 | * PB4 X1 axis PC4 -- PD4 -- 35 | * PB5 Y2 axis PC5 -- PD5 -- 36 | * PB6 -- PC6 -- PD6 LED 37 | * PB7 -- PC7 -- PD7 (HWB) 38 | * 39 | * Pinout ATMEGAXU4 40 | * ================ 41 | * 42 | * PB0 Button1 PD0 Button1 (INT) PF0 -- 43 | * PB1 Button2 PD1 -- PF1 -- 44 | * PB2 Button3 PD2 -- PE2 -- 45 | * PB3 Button4 PD3 MIDI (out) 46 | * PB4 X1 axis PD4 -- PF4 -- 47 | * PB5 Y2 axis PD5 -- PF5 -- 48 | * PB6 -- PC6 -- PD6 LED PE6 -- PF6 -- 49 | * PB7 -- PC7 -- PD7 -- PF7 -- 50 | * 51 | * Pinout AT90USBX6 (out dated!) 52 | * ================ 53 | * 54 | * PA0 -- PB0 -- PC0 -- PD0 Button1 PE0 -- PF0 -- 55 | * PA1 -- PB1 -- PC1 -- PD1 Button2 PE1 -- PF1 -- 56 | * PA2 -- PB2 -- PC2 -- PD2 Button3 PE2 -- PF2 -- 57 | * PA3 -- PB3 -- PC3 -- PD3 Button4 PE3 -- PF3 -- 58 | * PA4 -- PB4 X1 axis PC4 -- PD4 -- PE4 -- PF4 -- 59 | * PA5 -- PB5 Y2 axis PC5 -- PD5 -- PE5 -- PF5 -- 60 | * PA6 -- PB6 -- PC6 -- PD6 LED PE6 -- PF6 -- 61 | * PA7 -- PB7 -- PC7 -- PD7 -- PE7 -- PF7 -- 62 | * 63 | * $Id: 3DPro.c 1.6 2010/04/23 05:27:52 Detlef Exp Detlef $ 64 | ******************************************************************************/ 65 | 66 | #include "3DPro.h" 67 | //#include "ffb.h" 68 | 69 | //------------------------------------------------------------------------------ 70 | //****************************************************************************** 71 | //------------------------------------------------------------------------------ 72 | 73 | #define DDO( b ) _B1( b ) /* data direction: out */ 74 | #define DDI( b ) _B0( b ) /* data direction: in */ 75 | #define PU1( b ) _B1( b ) /* pull up on (if DDI) */ 76 | #define PU0( b ) _B0( b ) /* pull up off */ 77 | 78 | // Port B 79 | 80 | #define PBPU (PU1( PB7) | PU1( PB6) | PU0( PB5) | PU0( PB4) | \ 81 | PU1( PB3) | PU1( PB2) | PU1( PB1) | PU1( PB0)) 82 | #define DDB (DDI(DDB7) | DDI(DDB6) | DDI(DDB5) | DDI(DDB4) | \ 83 | DDI(DDB3) | DDI(DDB2) | DDI(DDB1) | DDI(DDB0)) 84 | 85 | // Port D 86 | 87 | #if defined(__AVR_AT90USBX2__) 88 | 89 | #define PDPU (PU0( PD7) | _B0( PD6) | PU1( PD5) | PU1( PD4) | \ 90 | PU1( PD3) | PU1( PD2) | PU1( PD1) | PU1( PD0)) 91 | 92 | #elif defined(__AVR_AT90USB646__) 93 | 94 | #define PDPU (PU1( PD7) | _B0( PD6) | PU1( PD5) | PU1( PD4) | \ 95 | PU1( PD3) | PU1( PD2) | PU1( PD1) | PU1( PD0)) 96 | 97 | #elif defined(__AVR_ATmegaXU4__) || defined(__AVR_AT90USB1286__) 98 | 99 | #define PDPU (PU1( PD7) | _B1( PD6) | PU1( PD5) | PU1( PD4) | \ 100 | PU0( PD3 ) | PU1( PD2) | PU1( PD1) | PU1( PD0)) 101 | // ???? removed PU1( PD3) for MIDI out via USART TX 102 | 103 | #endif 104 | 105 | #define DDD (DDI(DDD7) | DDO(DDD6) | DDI(DDD5) | DDI(DDD4) | \ 106 | DDO(DDD3) | DDI(DDD2) | DDI(DDD1) | DDI(DDD0)) 107 | // Ports A,C,E,F 108 | 109 | #if defined(__AVR_AT90USBX2__) 110 | 111 | #define PCPU (PU1( PC7) | PU1( PC6) | PU1( PC5) | PU1( PC4) | \ 112 | PU1( PC2) | PU1( PC1) | PU1( PC0)) 113 | #define DDC (DDI(DDC7) | DDI(DDC6) | DDI(DDC5) | DDI(DDC4) | \ 114 | DDI(DDC2) | DDI(DDC1) | DDI(DDC0)) 115 | 116 | #elif defined(__AVR_ATmegaXU4__) 117 | 118 | #define PCPU (PU1( PC7) | PU1( PC6)) 119 | #define DDC (DDI(DDC7) | DDI(DDC6)) 120 | 121 | #define PEPU (PU1( PE6) | PU0( PE2)) 122 | #define DDE (DDI(DDE6) | DDI(DDE2)) 123 | 124 | #define PFPU (PU1( PF7) | PU1( PF6) | PU1( PF5) | PU1( PF4) | \ 125 | PU1( PF1) | PU1( PF0)) 126 | #define DDF (DDI(DDF7) | DDI(DDF6) | DDI(DDF5) | DDI(DDF4) | \ 127 | DDI(DDF1) | DDI(DDF0)) 128 | #elif defined(__AVR_AT90USBX6__) 129 | 130 | #define PAPU (PU1( PA7) | PU1( PA6) | PU1( PA5) | PU1( PA4) | \ 131 | PU1( PA3) | PU1( PA2) | PU1( PA1) | PU1( PA0)) 132 | #define DDA (DDI(DDA7) | DDI(DDA6) | DDI(DDA5) | DDI(DDA4) | \ 133 | DDI(DDA3) | DDI(DDA2) | DDI(DDA1) | DDI(DDA0)) 134 | #define PCPU (PU1( PC7) | PU1( PC6) | PU1( PC5) | PU1( PC4) | \ 135 | PU1( PC3) | PU1( PC2) | PU1( PC1) | PU1( PC0)) 136 | #define DDC (DDI(DDC7) | DDI(DDC6) | DDI(DDC5) | DDI(DDC4) | \ 137 | DDI(DDC3) | DDI(DDC2) | DDI(DDC1) | DDI(DDC0)) 138 | #define PEPU (PU1( PE7) | PU1( PE6) | PU1( PE5) | PU1( PE4) | \ 139 | PU1( PE3) | PU1( PE2) | PU1( PE1) | PU1( PE0)) 140 | #define DDE (DDI(DDE7) | DDI(DDE6) | DDI(DDE5) | DDI(DDE4) | \ 141 | DDI(DDE3) | DDI(DDE2) | DDI(DDE1) | DDI(DDE0)) 142 | #define PFPU (PU1( PF7) | PU1( PF6) | PU1( PF5) | PU1( PF4) | \ 143 | PU1( PF3) | PU1( PF2) | PU1( PF1) | PU1( PF0)) 144 | #define DDF (DDI(DDF7) | DDI(DDF6) | DDI(DDF5) | DDI(DDF4) | \ 145 | DDI(DDF3) | DDI(DDF2) | DDI(DDF1) | DDI(DDF0)) 146 | #endif 147 | 148 | #define dis3DP_INT() clr_bit( EIMSK, INT0 ) 149 | 150 | #define TRG_pull() __WRAP__( { \ 151 | clr_bit( TRGDDR, TRGX1BIT ) ; \ 152 | clr_bit( TRGDDR, TRGY2BIT ) ; \ 153 | } ) 154 | #define TRG_rel() __WRAP__( { \ 155 | set_bit( TRGDDR, TRGX1BIT ) ; \ 156 | set_bit( TRGDDR, TRGY2BIT ) ; \ 157 | } ) 158 | 159 | //------------------------------------------------------------------------------ 160 | //****************************************************************************** 161 | //------------------------------------------------------------------------------ 162 | 163 | uint8_t 164 | sw_id, // ID of detected stick 165 | sw_report[SW_REPSZ_3DP + ADDED_REPORT_DATA_SIZE] ; // USB report data 166 | 167 | static uint8_t 168 | sw_buttons, // button buffer 169 | sw_problem ; // problem counter 170 | 171 | //------------------------------------------------------------------------------ 172 | //****************************************************************************** 173 | //------------------------------------------------------------------------------ 174 | 175 | // Delay using T1 as timing reference, assuming prescaler /1024 176 | 177 | static void FA_NOINLINE( Delay_1024 ) ( uint16_t time ) 178 | { 179 | set_bit( GTCCR, PSRSYNC ) ; // reset prescaler 180 | 181 | ResetTM( 1, time ) ; // set timer 182 | 183 | for ( ; ! TMexp( 1 ) ; ) // wait until timer expired 184 | ; 185 | } 186 | 187 | //------------------------------------------------------------------------------ 188 | 189 | // Delay using T0 as timing reference, assuming prescaler /64 190 | /* 191 | static void FA_NOINLINE( Delay_64 ) ( uint8_t time ) 192 | { 193 | set_bit( GTCCR, PSRSYNC ) ; // reset prescaler 194 | 195 | ResetTM( 0, time ) ; // set timer 196 | 197 | for ( ; ! TMexp( 0 ) ; ) // wait until timer expired 198 | ; 199 | } 200 | 201 | //------------------------------------------------------------------------------ 202 | 203 | // Trigger the stick 204 | 205 | static void FA_NOINLINE( Trigger ) ( void ) 206 | { 207 | TRG_pull() ; 208 | Delay_64( T6DEL48US ) ; // _delay_us( 47.8125 ) is max @ 16MHz 209 | TRG_rel() ; 210 | } 211 | */ 212 | //------------------------------------------------------------------------------ 213 | 214 | // Check if the FFP/PP data packet at *pkt is valid by xoring all bits together. 215 | // Result must be 1 for the packet to have correct parity. 216 | 217 | static uint8_t FA_NOINLINE( CheckFFPPkt ) ( uint8_t *pkt ) 218 | { 219 | uint8_t 220 | x ; 221 | 222 | x = pkt[0] ^ pkt[1] ^ pkt[2] ^ pkt[3] ^ pkt[4] ^ pkt[5] ; 223 | 224 | x ^= x >> 4 ; 225 | x ^= x >> 2 ; 226 | x ^= x >> 1 ; 227 | 228 | return ( x & 1 ) ; 229 | } 230 | 231 | //------------------------------------------------------------------------------ 232 | 233 | // Hang out 12ms w/ the LED on for the initial 4ms 234 | 235 | static void FA_NOINLINE( Flash_LED_12MS ) ( void ) 236 | { 237 | LED_on() ; 238 | Delay_1024( T0DEL4MS ) ; 239 | LED_off() ; 240 | Delay_1024( T0DEL8MS ) ; 241 | } 242 | 243 | 244 | //------------------------------------------------------------------------------ 245 | 246 | // Initialize FFP/PP by reading the ID. This has to be done, else the PP will 247 | // only use B0/1 for data transfer. Actually only the 3D Pro Plus does that. 248 | // Do it anyways for type determination. 249 | // QueryFFP() needs to be called before this function for a valid sw_clkcnt. 250 | 251 | static uint8_t InitFFPro ( void ) 252 | { 253 | uint8_t 254 | butt, 255 | idpktsz ; 256 | 257 | butt = BUTPIN;//MapPinsToButtons(BUTPIN) ; // Save current button data 258 | 259 | idpktsz = sw_clkcnt ; // remember packetsize 260 | 261 | // FORCE FFP interpretation 262 | if (CheckFFPPkt( ffp_packet )) 263 | { 264 | sw_id = SW_ID_FFP ; // Found FFP 265 | return (TRUE); 266 | } 267 | else 268 | return (FALSE); 269 | 270 | QueryFFP( -(sw_clkcnt - 6), 126 ) ; // Read ID, signal kick at -6 clk of data packet 271 | 272 | Delay_1024( T0DEL8MS ) ; // Give the stick some time.. 273 | 274 | idpktsz = sw_clkcnt - idpktsz ; // Sizeof ID packet 275 | 276 | if ( ! QueryFFP( 0, DATSZFFP ) ) // Signal for a regular packet, 16 triplets 277 | return ( FALSE ) ; // Timed out.. 278 | 279 | if ( CheckFFPPkt( ffp_packet ) ) // Packet parity checks out 280 | { 281 | if ( idpktsz == IDSZPP ) 282 | sw_id = SW_ID_PP ; // Found PP 283 | else 284 | if ( idpktsz == IDSZFFP ) 285 | sw_id = SW_ID_FFP ; // Found FFP 286 | else 287 | if ( idpktsz == 0 ) // 10nF timer, FFP or PP in 3bit mode 288 | { 289 | if ( (butt & BUTMSK) == BUTMSK ) // PP 290 | sw_id = SW_ID_PP ; 291 | else // FFP 292 | sw_id = SW_ID_FFP ; 293 | } 294 | else 295 | return ( FALSE ) ; // Unknown stick.. 296 | 297 | CopyFFPData( ffp_packet ) ; // Copy data to report buffer 298 | 299 | return ( TRUE ) ; // Got it 300 | } 301 | 302 | return ( FALSE ) ; // Stick not found 303 | } 304 | 305 | //------------------------------------------------------------------------------ 306 | 307 | // Reboot converter. Kill USB and wait for the watchdog to catch us. 308 | // Note: the watchdog is unleashed in main() after init_hw() returns. 309 | 310 | static void FA_NORETURN( reboot ) ( void ) 311 | { 312 | cli() ; // Disable interrupts 313 | 314 | USBCON = _B0(USBE) | _B1(FRZCLK) ; // Kill USB 315 | 316 | for ( ;; ) // Wait for watchdog to bite (.5s) 317 | ; 318 | } 319 | 320 | //------------------------------------------------------------------------------ 321 | 322 | // Initialize the hardware 323 | // 324 | // init_hw() is called only once, first thing in main(), 325 | // so it doesn't need a stack frame. Saves lots of push/pops. 326 | 327 | void FA_NAKED( init_hw ) ( void ) 328 | { 329 | // Initialize ports (ATmega32U4) 330 | DDRE = DDE ; 331 | PORTE = PEPU ; 332 | DDRF = DDF ; 333 | PORTF = PFPU ; 334 | // #endif 335 | 336 | // Initialize timers 337 | 338 | SetTMPS( 0, 64 ) ; // Set T0 prescaler to / 64 for query & us delay 339 | SetTMPS( 1, 1024 ) ; // Set T1 prescaler to / 1024 for ms delay 340 | 341 | EICRA = _B1(ISC01) | _B1(ISC00) ; // Need INT0 on rising edges 342 | 343 | Delay_1024( T0DEL200MS ) ; // Allow the stick to boot 344 | 345 | for ( ;; ) // Forever.. 346 | { 347 | Flash_LED_12MS() ; // Flash LED and wait 348 | Delay_1024( T0DEL200MS ) ; 349 | // Try to read a data packet, 350 | QueryFFP( 0, 126 ) ; // don't know how long - let it time out 351 | 352 | Flash_LED_12MS() ; 353 | 354 | // Analyze clock count 355 | 356 | if ( ! ~sw_clkcnt ) 357 | sw_clkcnt = 0 ; 358 | 359 | if ( sw_clkcnt == (16 + 1) // FFP/PP in 3-bit mode 360 | || sw_clkcnt == (48 + 1) ) // 3DPP in 1-bit mode 361 | { 362 | if ( InitFFPro() ) // found FFP/PP 363 | { 364 | break ; // break forever 365 | } 366 | } 367 | dis3DP_INT() ; 368 | } 369 | 370 | dis3DP_INT() ; // Disable INT 371 | 372 | cli() ; // Disable interrupts 373 | 374 | sw_buttons = BUTMSK ; // All buttons released 375 | 376 | RET() ; // naked also means no ret.. 377 | } 378 | 379 | //------------------------------------------------------------------------------ 380 | 381 | // Read the stick and create a report 382 | 383 | void getdata ( void ) 384 | { 385 | uint8_t 386 | i ; 387 | 388 | i = QueryFFP( 0, DATSZFFP ) ; // Query FFP 389 | 390 | dis3DP_INT() ; 391 | 392 | if ( ! i ) // If query timed out, 393 | { 394 | if ( ++sw_problem > 10 ) // 11th problem in a row 395 | reboot() ; // We lost the stick, lets start over 396 | } 397 | else 398 | { 399 | sw_problem = 0 ; // Reset problem counter 400 | 401 | if ( CheckFFPPkt( ffp_packet ) ) // If PP/FFP packet ok 402 | { 403 | // LED_on() ; // Signal good packet read 404 | 405 | CopyFFPData( ffp_packet ) ; // Copy data into report 406 | } 407 | } 408 | } 409 | 410 | //------------------------------------------------------------------------------ 411 | -------------------------------------------------------------------------------- /3DPro.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * File Name : 3DPro.h 3 | * Project : 3DP-Vert, Microsoft Sidewinder 3D Pro/PP/FFP to USB converter 4 | * Date : 2005/05/31, 2006/12/14, 2008/02/12, 2009/06/16 5 | * Version : 4.0 6 | * Target MCU : AT90USB162/82, AT90USB646/1286, ATMEGA16U4/32U4 7 | * Tool Chain : Atmel AVR Studio 4.18 716 / WinAVR 20100110 8 | * Author : Detlef "Grendel" Mueller 9 | * detlef@gmail.com 10 | * Release Notes: 11 | * 12 | * ================ 13 | * WARNING: This version has been heavily modified to only support 14 | * Force Feedback Pro and ATmega32U4. 15 | * Do not use except for testing purposes! 16 | * 17 | * -- tloimu 18 | * ================ 19 | * 20 | * $Id: 3DPro.h 1.6 2010/04/23 05:28:59 Detlef Exp Detlef $ 21 | ******************************************************************************/ 22 | 23 | #ifndef _3DPRO_H_ 24 | #define _3DPRO_H_ 25 | 26 | //------------------------------------------------------------------------------- 27 | 28 | #include "includes.h" 29 | 30 | //------------------------------------------------------------------------------- 31 | 32 | #define HID_PHYS 1 /* Physical min/max in HID report */ 33 | /* (W98 actually needs these...) */ 34 | 35 | //------------------------------------------------------------------------------- 36 | // SideWinder packet sizes 37 | 38 | #define DATSZ3DP 22 /* 3DP data packet size in clock ticks (x3 bit, sent 3x) */ 39 | #define IDSZ3DP 160 /* 3DP ID packet size in clock ticks */ 40 | #define DATSZFFP 16 /* FFP/PP data packet size in clock ticks (x3 bit) */ 41 | #define IDSZPP 40 /* PP ID packet size */ 42 | #define IDSZFFP 14 /* FFP ID packet size */ 43 | 44 | //------------------------------------------------------------------------------- 45 | // Things not defined in iom32u4.h 46 | 47 | #if defined(__AVR_ATmegaXU4__) 48 | 49 | #define PRTIM4 4 /* Power reduction register bit 4 for Timer 4 */ 50 | 51 | #define PB7 PORTB7 52 | #define PB6 PORTB6 53 | #define PB5 PORTB5 54 | #define PB4 PORTB4 55 | #define PB3 PORTB3 56 | #define PB2 PORTB2 57 | #define PB1 PORTB1 58 | #define PB0 PORTB0 59 | 60 | #define PC7 PORTC7 61 | #define PC6 PORTC6 62 | 63 | #define PD7 PORTD7 64 | #define PD6 PORTD6 65 | #define PD5 PORTD5 66 | #define PD4 PORTD4 67 | #define PD3 PORTD3 68 | #define PD2 PORTD2 69 | #define PD1 PORTD1 70 | #define PD0 PORTD0 71 | 72 | #define PE6 PORTE6 73 | #define PE2 PORTE2 74 | 75 | #define PF7 PORTF7 76 | #define PF6 PORTF6 77 | #define PF5 PORTF5 78 | #define PF4 PORTF4 79 | #define PF1 PORTF1 80 | #define PF0 PORTF0 81 | 82 | #endif 83 | 84 | //------------------------------------------------------------------------------- 85 | // Hardware specific definitions 86 | 87 | #define LEDPORT PORTD 88 | #define LEDBIT PD6 89 | 90 | #define TRGDDR DDRB 91 | 92 | #define TRGY2BIT DDB5 93 | #define TRGX1BIT DDB4 94 | 95 | #define BUTPIN PINB 96 | #define BUT1 PB0 97 | #define BUT2 PB1 98 | #define BUT3 PB2 99 | #define BUT4 PB3 100 | #define BUTMSK (_B1(BUT4) | _B1(BUT3) | _B1(BUT2) | _B1(BUT1)) 101 | 102 | #define ClrSerial() ((BUTPIN & BUTMSK) == (_B1(BUT2) | _B1(BUT1))) 103 | 104 | //------------------------------------------------------------------------------- 105 | // Timer 0/1 prescaler settings 106 | 107 | #define T0PS_64 (_B0(CS02) | _B1(CS01) | _B1(CS00)) 108 | #define T0PS_1024 (_B1(CS02) | _B0(CS01) | _B1(CS00)) 109 | 110 | #define T1PS_64 (_B0(CS12) | _B1(CS11) | _B1(CS10)) 111 | #define T1PS_1024 (_B1(CS12) | _B0(CS11) | _B1(CS10)) 112 | 113 | //------------------------------------------------------------------------------- 114 | 115 | #if ! defined(__ASSEMBLER__) 116 | 117 | //------------------------------------------------------------------------------- 118 | // C only definitions 119 | //------------------------------------------------------------------------------- 120 | 121 | //------------------------------------------------------------------------------- 122 | // Macros to deal w/ the timers 123 | 124 | #define ResetTM( n, del ) __WRAP__( { \ 125 | TCNT ## n = (del) ; \ 126 | TIFR ## n = _BV( TOV ## n ) ; \ 127 | } ) 128 | 129 | #define TMexp( n ) bit_is_set( TIFR ## n, TOV ## n ) 130 | 131 | #define SetTMPS( n, ps ) TCCR ## n ## B = T ## n ## PS_ ## ps 132 | 133 | //------------------------------------------------------------------------------- 134 | // Timer Macros, msec or usec to timer ticks. ps = prescaler value 135 | 136 | #define MS2TM( ms, ps ) -(int16_t)(((ms) * (F_CPU / 1000.)) / (ps) + .5) 137 | #define US2TM( us, ps ) -(int16_t)(((us) * (F_CPU / 1000000.)) / (ps) + .5) 138 | 139 | //------------------------------------------------------------------------------- 140 | // Timer delay values for /1024 prescaler 141 | 142 | #define T0DEL500US MS2TM( .5, 1024 ) 143 | #define T0DEL4MS MS2TM( 4, 1024 ) 144 | #define T0DEL8MS MS2TM( 8, 1024 ) 145 | #define T0DEL200MS MS2TM( 200, 1024 ) 146 | #define T0DEL300MS MS2TM( 300, 1024 ) 147 | #define T0DEL400MS MS2TM( 400, 1024 ) 148 | #define T0DEL500MS MS2TM( 500, 1024 ) 149 | 150 | //------------------------------------------------------------------------------- 151 | // Timer delay values for /64 prescaler 152 | 153 | #define T6DEL48US US2TM( 48, 64 ) 154 | #define T6DEL140US US2TM( 140, 64 ) 155 | #define T6DEL440US US2TM( 440, 64 ) 156 | #define T6DEL865US US2TM( 865, 64 ) 157 | 158 | #define T6DEL4MS MS2TM( 4, 64 ) 159 | #define T6DEL100MS MS2TM( 100, 64 ) 160 | #define T6DEL200MS MS2TM( 200, 64 ) 161 | #define T6DEL300MS MS2TM( 300, 64 ) 162 | 163 | //------------------------------------------------------------------------------- 164 | 165 | #define SW_REPSZ_3DP 7 /* report size for 3DP */ 166 | #define SW_REPSZ_FFP 6 /* report size for PP/FFP */ 167 | 168 | #define ADDED_REPORT_DATA_SIZE 0 /* added report size for additional controls */ 169 | 170 | #define SW_ID_3DP 1 /* 3DP connected */ 171 | #define SW_ID_PP 2 /* PP connected */ 172 | #define SW_ID_FFP 3 /* FFP connected */ 173 | 174 | //------------------------------------------------------------------------------- 175 | // Inline code 176 | 177 | #if defined(__AVR_AT90USBX2__) || defined(__AVR_AT90USB646__) 178 | 179 | #define LED_on() clr_bit( LEDPORT, LEDBIT ) 180 | #define LED_off() set_bit( LEDPORT, LEDBIT ) 181 | 182 | #elif defined(__AVR_ATmegaXU4__) || defined(__AVR_AT90USB1286__) 183 | 184 | #define LED_on() set_bit( LEDPORT, LEDBIT ) 185 | #define LED_off() clr_bit( LEDPORT, LEDBIT ) 186 | 187 | #endif 188 | 189 | #define LED_tog() tog_bit( LEDPORT, LEDBIT ) 190 | 191 | //------------------------------------------------------------------------------- 192 | // main.c interface 193 | 194 | extern uint8_t 195 | idle_rate, // idle rate in 4ms clicks, 0 for indefinite 196 | idle_cnt ; // idle counter 197 | 198 | //------------------------------------------------------------------------------- 199 | // 3DPro.c interface 200 | 201 | extern uint8_t 202 | sw_id, // Will be SW_ID_... 203 | sw_report[SW_REPSZ_3DP + ADDED_REPORT_DATA_SIZE], // Report buffer 204 | sw_reportsz ; // Size of report in bytes 205 | 206 | extern void 207 | init_hw( void ), // Initialize HW & wait for stick 208 | getdata( void ) ; // Read stick and set up sw_report 209 | 210 | //------------------------------------------------------------------------------- 211 | // 3DProasm.S interface 212 | 213 | extern uint8_t // Sidewinder 3D Pro packet buffers 214 | sw_idbuf[60], 215 | sw_packet3[8], 216 | sw_packet2[8], 217 | sw_packet1[8] ; 218 | 219 | extern uint8_t // Sidewinder PP/FFP packet buffer 220 | ffp_packet[6] ; 221 | 222 | extern volatile uint8_t 223 | sw_pktptr, // LSB of -> to current byte in buffer 224 | sw_clkcnt ; // no. of clock ticks on B1 225 | 226 | extern void 227 | CopyFFPData( uint8_t *packet ), // Copy PP/FFP data from packet to sw_report 228 | Copy3DPData( uint8_t *packet ) ; // Copy 3DP data from packet to sw_report 229 | 230 | extern uint8_t 231 | QueryFFP( uint8_t id, uint8_t sz ), // Read data from PP/FFP 232 | Query3DP( uint8_t id, uint8_t sz ) ;// Read data from 3DPro 233 | 234 | //------------------------------------------------------------------------------- 235 | 236 | #else // __ASSEMBLER__ 237 | 238 | //------------------------------------------------------------------------------- 239 | // Assembler only definitions 240 | 241 | #if F_CPU == 16000000 242 | 243 | // Receiver timeout 244 | 245 | #define T6TO48US 244 /* ( 48*16/64) = 12 (0-12 = 244) */ 246 | #define T6TO100US 231 /* (100*16/64) = 25 (0-25 = 231) */ 247 | 248 | // Initial rec. timeouts 249 | 250 | #define T6TO400US 156 /* (400*16/64) = 100 (0-100 = 156) */ 251 | #define T6TO150US 218 /* (150*16/64) = 37.5 (0-38 = 218) */ 252 | 253 | #else // F_CPU undefined or out of range 254 | #error "F_CPU must be 16000000" 255 | #endif // F_CPU 256 | 257 | //------------------------------------------------------------------------------- 258 | 259 | #endif // __ASSEMBLER__ 260 | 261 | //------------------------------------------------------------------------------- 262 | 263 | #endif // _3DPRO_H_ 264 | -------------------------------------------------------------------------------- /3DProasm.S: -------------------------------------------------------------------------------- 1 | ;------------------------------------------------------------------------------- 2 | ;******************************************************************************* 3 | ;* File Name : 3DProasm.S 4 | ;* Project : Microsoft Sidewinder 3D Pro/PP/FFP to USB converter 5 | ;* Date : 2005/05/31, 2006/12/14, 2008/02/12, 2009/06/26 6 | ;* Version : 4.0 + MODS 7 | ;* Target MCU : AT90USB162/AT90USB82, ATMEGA16U4/ATMEGA32U4 8 | ;* Tool Chain : Atmel AVR Studio 4.17 666 / WinAVR 20090313 9 | ;* Author : Detlef "Grendel" Mueller 10 | ;* detlef@gmail.com 11 | ;* References : Sidewinder.c by Vojtech Pavlik 12 | ;* Patent #5628686, Apperatus and Method for Bidirectional 13 | ;* Data Communication in a Gameport, by Microsoft 14 | ;* Description : 3D Pro driver 15 | ;* Device : Microsoft SideWinder 3D Pro, Precision Pro, Force Feedback Pro 16 | ;* 17 | ;* Release Notes: 18 | ;* ================ 19 | ;* WARNING: This version has been heavily modified to only support 20 | ;* Force Feedback Pro and ATmega32U4. 21 | ;* Do not use except for testing purposes! 22 | ;* 23 | ;* -- tloimu 24 | ;* ================ 25 | ;* 26 | ;* Joystick button mapping 27 | ;* ======================= 28 | ;* 29 | ;* B1 - PB0 30 | ;* B2 - PB1 31 | ;* B3 - PB2 32 | ;* B4 - PB3 33 | ;* 34 | ;* 3D Pro data packet structure (64bit) 35 | ;* ==================================== 36 | ;* 37 | ;* 0 ****0 0 0 0 0 0 1 38 | ;* 6666555555555544444444443333333333222222222211111111110000000000 39 | ;* 3210987654321098765432109876543210987654321098765432109876543210 40 | ;* ---------------------------------------------------------------- 41 | ;* sHHHccccsTTTTTTTsRRRRRRRsBBRRTTTsYYYYYYYsXXXXXXXsBBBBBBBsHXXXYYY 42 | ;* 210 6543210 6543210 8987987 6543210 6543210 7654321 3987987 43 | ;* 44 | ;* Bit Description Comment 45 | ;* === =========== ======= 46 | ;* 00 Y7 47 | ;* 01 Y8 48 | ;* 02 Y9 49 | ;* 03 X7 50 | ;* 04 X8 51 | ;* 05 X9 52 | ;* 06 H3 53 | ;* 07 Sync always 1 54 | ;* 08 B1 Trigger (Buttons are low active !) 55 | ;* 09 B2 2nd trigger 56 | ;* 10 B3 Upper thumb 57 | ;* 11 B4 Lower thumb 58 | ;* 12 B5 Base 1 59 | ;* 13 B6 Base 2 60 | ;* 14 B7 Base 3 61 | ;* 15 Sync always 0 62 | ;* 16 X0 X-axis 63 | ;* 17 X1 64 | ;* 18 X2 65 | ;* 19 X3 66 | ;* 20 X4 67 | ;* 21 X5 68 | ;* 22 X6 69 | ;* 23 Sync always 0 70 | ;* 24 Y0 Y-axis 71 | ;* 25 Y1 72 | ;* 26 Y2 73 | ;* 27 Y3 74 | ;* 28 Y4 75 | ;* 29 Y5 76 | ;* 30 Y6 77 | ;* 31 Sync always 0 78 | ;* 32 T7 79 | ;* 33 T8 80 | ;* 34 T9 81 | ;* 35 Rz7 82 | ;* 36 Rz8 83 | ;* 37 B9 mode switch 84 | ;* 38 B8 Base 4 85 | ;* 39 Sync always 0 86 | ;* 40 Rz0 Rz-axis (twist) 87 | ;* 41 Rz1 88 | ;* 42 Rz2 89 | ;* 43 Rz3 90 | ;* 44 Rz4 91 | ;* 45 Rz5 92 | ;* 46 Rz6 93 | ;* 47 Sync always 0 94 | ;* 48 T0 Throttle slider 95 | ;* 49 T1 96 | ;* 50 T2 97 | ;* 51 T3 98 | ;* 52 T4 99 | ;* 53 T5 100 | ;* 54 T6 101 | ;* 55 Sync always 0 102 | ;* 56 chksum Sum of all nibbles = 0 103 | ;* 57 chksum 104 | ;* 58 chksum 105 | ;* 59 chksum 106 | ;* 60 H0 Hat switch 107 | ;* 61 H1 108 | ;* 62 H2 109 | ;* 63 Sync always 0 110 | ;* 111 | ;* Timer Trigger Signal 112 | ;* --+ +--....-------------------------------....------------------------------- 113 | ;* | | 114 | ;* | | 115 | ;* +--+ 116 | ;* 117 | ;* 118 | ;* 3DP Clock Line (Button 1) w/ Button 1 Released 119 | ;* 120 | ;* --------....-------------------+ +----+ +----+ +------------------- 121 | ;* | | | | | | 122 | ;* | | | | | | 123 | ;* +----+ +-....-+ +----+ 124 | ;* |<-....--- 100us -------->|<--------- 660us -------->| 125 | ;* 126 | ;* 127 | ;* 3DP Clock Line (Button 1) w/ Button 1 Pressed 128 | ;* 129 | ;* +--------------+ +----+ +----+ +--------------+ 130 | ;* | | | | | | | | 131 | ;* | | | | | | | | 132 | ;* --------....----+ +----+ +-....-+ +----+ +---- 133 | ;* |<-- 17.5us -->| |<-- 17.7us -->| 134 | ;* |<-....--- 100us -------->|<--------- 660us -------->| 135 | ;* 136 | ;* 137 | ;* 3DP Clock Line (Button 1) Detail 138 | ;* 139 | ;* |<-- start of packet 2 or 3 140 | ;* ----+ +----+ +------+ +----+ 141 | ;* | | | | | | | 142 | ;* | | | | | | | 143 | ;* +----+ +----+ +----+ +---- 144 | ;* |<-10us ->|<-12.5us ->| 145 | ;* 146 | ;* 147 | ;* Buttons 2-4 data is valid on rising edges of the clock. Buttons 1-4 148 | ;* are mapped to the button lines if no packet transfer is going on 149 | ;* (3D Pro only !) 150 | ;* 151 | ;* Calling conventions on gcc: 152 | ;* First parameter passed in R24/R25, second in R22/R23 and so on. 153 | ;* Callee must preserve R1-R17, R28/R29, result is passed in R24/R25. 154 | ;* 155 | ;* $Id: 3DProasm.S 1.5 2010/04/23 05:29:34 Detlef Exp Detlef $ 156 | ;******************************************************************************* 157 | ;------------------------------------------------------------------------------- 158 | 159 | #include "3DPro.h" 160 | 161 | ;------------------------------------------------------------------------------- 162 | ;******************************************************************************* 163 | ;------------------------------------------------------------------------------- 164 | 165 | #define temp0 r21 166 | #define temp1 r20 167 | #define temp2 r19 168 | #define temp3 r18 169 | 170 | #define XL r26 171 | #define XH r27 172 | 173 | #define nop2 rjmp .+0 /* jump to next instruction */ 174 | 175 | #if F_CPU == 16000000UL 176 | #define TRGWAIT lo8((48*(16000000/1000000))/3) 177 | #endif 178 | 179 | ;------------------------------------------------------------------------------- 180 | ;******************************************************************************* 181 | ;------------------------------------------------------------------------------- 182 | 183 | ; Note: the following variables MUST be in the same 256 byte RAM segment !! 184 | 185 | .section .bss,"aw",@nobits 186 | 187 | .global sw_idbuf 188 | .global sw_packet3 189 | .global sw_packet2 190 | .global sw_packet1 191 | .global ffp_packet 192 | 193 | .global sw_pktptr 194 | .global sw_clkcnt 195 | 196 | .skip 1,0 197 | 198 | sw_idbuf: 199 | .skip 60,0 200 | 201 | sw_packet3: ; SideWinder 3D Pro packet buffers 202 | .skip 8,0 203 | 204 | sw_packet2: 205 | .skip 8,0 206 | 207 | sw_packet1: 208 | .skip 2,0 209 | 210 | ffp_packet: 211 | .skip 6,0 212 | sw_pktstart: 213 | 214 | sw_pktptr: ; LSB of -> to current byte in buffer 215 | .skip 1,0 216 | 217 | sw_clkcnt: ; no. of clock ticks on B1 218 | .skip 1,0 219 | 220 | ;------------------------------------------------------------------------------- 221 | ;******************************************************************************* 222 | ;------------------------------------------------------------------------------- 223 | 224 | .text 225 | 226 | ;------------------------------------------------------------------------------- 227 | ;******************************************************************************* 228 | ; Receive, decode, and store a triplet from the 3DPro, PP, or FFP 229 | ;------------------------------------------------------------------------------- 230 | 231 | .global INT0_vect 232 | 233 | INT0_vect: 234 | push temp0 ; Save temp0 2 +2 235 | in temp0,SREG ; Save S register 1 236 | push temp0 ; Save SREG 2 237 | 238 | ldi temp0,T6TO100US ; 1 239 | out TCNT0,temp0 ; Reset timeout timer 1 240 | 241 | push temp1 ; Save temp1 2 242 | push XL ; Save XL 2 243 | push XH ; Save XH 2 +4 244 | 245 | ldi XH,hi8(sw_pktstart) ; Load buffer pointer H !!! 1 +5 246 | lds XL,sw_pktptr ; Load buffer pointer L 2 +4 247 | 248 | lds temp0,sw_clkcnt ; Load clkcnt 2 +6 249 | mov temp1,temp0 ; 1 +7 250 | 251 | inc temp0 ; Maintain clkcnt 1 252 | sts sw_clkcnt,temp0 ; Save new clkcnt 2 +9 253 | 254 | in temp0,BUTPIN ; Read the button data 1 255 | swap temp0 ; AT90USBX2 1 256 | andi temp0,0b11100000 ; Mask B4-B2 1 257 | 258 | andi temp1,7 ; 1 12+ 01234567 259 | breq I0case0 ; 1/2 21111111 260 | cpi temp1,5 ; 1 1111111 261 | breq I0case5 ; 1/2 1111211 262 | cpi temp1,2 ; 1 1111 11 263 | breq I0case2 ; 1/2 1211 11 264 | cpi temp1,3 ; 1 1 11 11 265 | breq I0case3 ; 1/2 1 21 11 266 | cpi temp1,6 ; 1 1 1 11 267 | breq I0case6 ; 1/2 1 1 21 268 | cpi temp1,1 ; 1 1 1 1 269 | breq I0case1 ; 1/2 2 1 1 270 | cpi temp1,4 ; 1 1 1 271 | breq I0case4 ; 1/2 2 1 272 | rjmp I0case7 ; 2 2 273 | ; 1 1 11 274 | ; 22684405 275 | I0case0: 276 | swap temp0 ; move b7-b5 (B4-B3) to b2-b0 1 277 | lsr temp0 ; 1 -2 278 | 279 | st -X,temp0 ; Store it 2 280 | 281 | rjmp Int0End ; 2 8+2=10 +21=31 282 | 283 | I0case2: 284 | lsl temp0 ; store b7 in C, b6-b5 to b7-b6 1 285 | 286 | ld temp1,X ; Get current byte 2 287 | or temp0,temp1 ; 1 288 | st X,temp0 ; Save to buffer 2 289 | 290 | clr temp0 ; 1 291 | rol temp0 ; b7 to b0 1 292 | st -X,temp0 ; Save to buffer 2 293 | 294 | rjmp Int0End ; 2 12+6=18 +21=39 295 | 296 | I0case5: 297 | bst temp0,7 ; store b7 in T 1 298 | lsl temp0 ; drop b7 1 299 | lsl temp0 ; store b6 in C, b5 to b7 1 300 | 301 | ld temp1,X ; Get current byte 2 302 | or temp0,temp1 ; 1 303 | st X,temp0 ; Save to buffer 2 304 | 305 | clr temp0 ; 1 306 | rol temp0 ; b6 to b0 1 307 | bld temp0,1 ; b7 to b1 1 308 | st -X,temp0 ; Save to buffer 2 309 | 310 | rjmp Int0End ; 2 15+4=19 +21=40 311 | 312 | I0case3: ; b7-b5 to b3-b1 9+8=17 +21=38 313 | lsr temp0 ; 1 314 | I0case6: ; b7-b5 to b4-b2 8+10=18 +21=39 315 | lsr temp0 ; 1 316 | I0case1: ; b7-b5 to b5-b3 7+12=19 +21=40 317 | lsr temp0 ; 1 318 | I0case4: ; b7-b5 to b6-b4 6+14=20 +21=41 319 | lsr temp0 ; 1 320 | I0case7: ; b7-b5 are right 5+15=20 +21=41 321 | ld temp1,X ; Get current byte 2 322 | or temp0,temp1 ; 1 323 | st X,temp0 ; Save to buffer 2 324 | 325 | Int0End: 326 | sts sw_pktptr,XL ; Save buffer pointer L 2 327 | 328 | pop XH ; Restore XH 2 +11 329 | pop XL ; Restore XL 2 +4 330 | pop temp1 ; Restore temp1 2 331 | 332 | pop temp0 ; Restore SREG 2 333 | out SREG,temp0 ; Restore S register 1 334 | 335 | pop temp0 ; Restore temp0 2 +13 336 | reti ; 4 9+ 337 | 338 | ;------------------------------------------------------------------------------- 339 | ;******************************************************************************* 340 | ; Decode the raw FFP/PP data and store it into sw_report 341 | ; 342 | ; Input: 343 | ; Pointer to start of packet to copy 344 | ; 345 | ; FFP/PP data packet structure 346 | ; ============================ 347 | ; 348 | ; 44444444 33333333 33222222 22221111 11111100 00000000 349 | ; 76543210 98765432 10987654 32109876 54321098 76543210 350 | ; -------0 -------1 -------2 -------3 -------4 -------5 351 | ; ppHHHHRR RRRRTTTT TTTYYYYY YYYYYXXX XXXXXXXB BBBBBBBB 352 | ; 321054 32106543 21098765 43210987 65432109 87654321 353 | ; 354 | ; USB report data structure 355 | ; ========================= 356 | ; 357 | ; -------0 -------1 -------2 -------3 -------4 -------5 358 | ; XXXXXXXX YYYYYYXX HHHHYYYY BBRRRRRR TBBBBBBB 00TTTTTT 359 | ; 76543210 54321098 32109876 21543210 09876543 654321 360 | ; 361 | ;------------------------------------------------------------------------------- 362 | 363 | #define argPtrL r24 364 | #define argPtrH r25 365 | 366 | .global CopyFFPData 367 | 368 | CopyFFPData: 369 | movw XL,argPtrL 370 | 371 | ; X 372 | 373 | ldi temp0,4 374 | add XL,temp0 375 | 376 | ld temp0,X ; xl:6543210- 377 | 378 | com temp0 379 | bst temp0,0 ; b:9 380 | com temp0 381 | 382 | subi XL,1 383 | ld temp2,X ; xh:-----987 384 | 385 | lsr temp2 ; xh:------98 [7] 386 | ror temp0 ; xl:76543210 387 | 388 | sts sw_report,temp0 389 | 390 | ldi temp1,0b00000010 391 | add temp2,temp1 ; Add -512 392 | andi temp2,0b00000011 ; xh:------98 393 | 394 | ; Y 395 | 396 | ld temp0,X ; yl:43210--- 397 | 398 | subi XL,1 399 | ld temp1,X ; yh:---98765 400 | 401 | lsr temp1 ; yh:----9876 [5] 402 | ror temp0 ; yl:543210-- 403 | 404 | andi temp0,0b11111100 405 | 406 | or temp0,temp2 407 | 408 | sts sw_report+1,temp0 409 | 410 | ldi temp2,0b00001000 411 | add temp1,temp2 ; Add -512 412 | andi temp1,0b00001111 ; yh:----9876 413 | 414 | ; Hat 415 | 416 | subi XL,2 417 | ld temp0,X ; h:--3210-- 418 | 419 | lsl temp0 ; h:-3210--- 420 | lsl temp0 ; h:3210---- 421 | subi temp0,0x10 ; -1 422 | andi temp0,0b11110000 423 | 424 | or temp0,temp1 425 | 426 | sts sw_report+2,temp0 427 | 428 | ; Rz 429 | 430 | ld temp2,X ; rh:------54 431 | 432 | ldi temp1,0b00000010 433 | add temp2,temp1 ; Add -32 434 | andi temp2,0b00000011 435 | 436 | inc XL 437 | 438 | ld temp1,X ; rl:3210---- 439 | andi temp1,0b11110000 440 | 441 | or temp2,temp1 ; r:3210--54 442 | swap temp2 ; r:--543210 443 | lsl temp2 ; r:-543210- 444 | lsl temp2 ; r:543210-- 445 | 446 | ; B1-9 447 | 448 | ldi temp0,4 449 | add XL,temp0 450 | ld temp0,X ; b:87654321 451 | 452 | com temp0 453 | 454 | lsr temp0 ; b:-8765432 [1] 455 | ror temp2 ; r:1543210- 456 | lsr temp0 ; b:--876543 [2] 457 | ror temp2 ; r:21543210 458 | 459 | sts sw_report+3,temp2 460 | 461 | bld temp0,6 ; b:-9876543 462 | 463 | ; Throttle 464 | 465 | subi XL,3 466 | ld temp1,X ; tl:210----- 467 | 468 | bst temp1,5 469 | bld temp0,7 ; b:09876543 470 | 471 | sts sw_report+4,temp0 472 | 473 | subi XL,1 474 | ld temp0,X ; th:----6543 475 | 476 | ldi temp2,0b00001000 477 | add temp0,temp2 ; Add -64 478 | andi temp0,0b00001111 479 | 480 | lsl temp1 ; tl:1------- [2] 481 | rol temp0 ; th:---65432 482 | lsl temp1 ; tl:-------- [1] 483 | rol temp0 ; th:--654321 484 | 485 | sts sw_report+5,temp0 486 | 487 | ret 488 | 489 | ;------------------------------------------------------------------------------- 490 | ;******************************************************************************* 491 | ; Decode the raw 3DPro data and store it into sw_report 492 | ; 493 | ; Input: 494 | ; Pointer to start of packet to copy 495 | ; 496 | ; 3D Pro data packet structure 497 | ; ============================ 498 | ; 499 | ; 0 **** 0 0 0 0 0 0 1 500 | ; 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 501 | ; 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 502 | ; -------0 -------1 -------2 -------3 -------4 -------5 -------6 -------7 503 | ; sHHHcccc sTTTTTTT sRRRRRRR sBBRRTTT sYYYYYYY sXXXXXXX sBBBBBBB sHXXXYYY 504 | ; 210 6543210 6543210 8987987 6543210 6543210 7654321 3987987 505 | ; 506 | ; USB report data structure 507 | ; ========================= 508 | ; 509 | ; -------0 -------1 -------2 -------3 -------4 -------5 -------6 510 | ; XXXXXXXX YYYYYYXX RRRRYYYY HHHRRRRR BBBBBBBH TTTTTTTB 00000TTT 511 | ; 76543210 54321098 32109876 21087654 76543213 65432108 987 512 | ; 513 | ;------------------------------------------------------------------------------- 514 | 515 | .global Copy3DPData 516 | 517 | Copy3DPData: 518 | movw XL,argPtrL 519 | 520 | ; Get X 521 | 522 | ldi temp1,5 523 | add XL,temp1 524 | 525 | ld temp0,X ; xl:-6543210 526 | 527 | inc XL 528 | inc XL 529 | 530 | ld temp2,X 531 | andi temp2,0b00111000 ; xh:--987--- 532 | 533 | lsl temp2 ; xh:-987---- 534 | swap temp2 ; xh:-----987 535 | lsl temp0 ; xl:6543210- 536 | lsr temp2 ; xh:------98 [7] 537 | ror temp0 ; xl:76543210 538 | 539 | ldi temp1,0xFE 540 | add temp2,temp1 ; Add -512 541 | andi temp2,0b00000011 ; xh:------98 542 | 543 | sts sw_report,temp0 544 | 545 | ; Y 546 | 547 | ld temp1,X 548 | andi temp1,0b00000111 ; yh:-----987 549 | 550 | subi XL,3 551 | 552 | ld temp0,X ; yl:-6543210 553 | 554 | lsl temp0 ; yl:6543210- 555 | lsr temp1 ; yh:------98 [7] 556 | ror temp0 ; yl:76543210 557 | 558 | ldi temp3,0xFE 559 | add temp1,temp3 ; Add -512 560 | andi temp1,0b00000011 ; yh:------98 561 | 562 | lsl temp0 ; yl:6543210- 563 | rol temp1 ; yh:-----987 564 | lsl temp0 ; yl:543210-- 565 | rol temp1 ; yh:----8976 566 | or temp0,temp2 567 | 568 | sts sw_report+1,temp0 569 | mov temp2,temp1 570 | 571 | ; Rz 572 | 573 | subi XL,2 574 | 575 | ld temp0,X ; rl:-6543210 576 | 577 | inc XL 578 | 579 | ld temp1,X 580 | andi temp1,0b00011000 ; rh:---87--- 581 | 582 | lsl temp0 ; rl:6543210- 583 | lsl temp1 ; rh:--87---- 584 | swap temp1 ; rh:------87 585 | lsr temp1 ; rh:-------8 [7] 586 | ror temp0 ; rl:76543210 587 | 588 | com temp1 ; Add -256 589 | bst temp1,0 590 | 591 | swap temp0 ; rl:32107654 592 | mov temp1,temp0 593 | andi temp1,0b11110000 ; rl:3210---- 594 | or temp1,temp2 595 | 596 | sts sw_report+2,temp1 597 | andi temp0,0b00001111 ; rh:----7654 598 | bld temp0,4 ; rh:---87654 599 | mov temp2,temp0 600 | 601 | ; Hat 602 | 603 | subi XL,3 604 | 605 | ld temp0,X 606 | andi temp0,0b01110000 ; h:-210---- 607 | 608 | ldi temp1,7 609 | add XL,temp1 610 | 611 | ld temp1,X ; h:-3------ 612 | 613 | bst temp1,6 614 | bld temp0,7 ; h:3210---- 615 | subi temp0,0x10 616 | 617 | bst temp0,7 618 | lsl temp0 ; h:210----- 619 | or temp0,temp2 620 | 621 | sts sw_report+3,temp0 622 | clr temp2 623 | bld temp2,0 ; h:-------3 624 | 625 | ; Buttons 1-8 626 | 627 | dec XL 628 | 629 | ld temp0,X ; b:-7654321 630 | 631 | subi XL,3 632 | 633 | ld temp1,X ; b:-89----- 634 | bst temp1,6 635 | bld temp0,7 ; b:87654321 636 | 637 | com temp0 638 | 639 | clr temp1 640 | lsl temp0 ; b:7654321- 641 | rol temp1 ; b:-------8 642 | or temp0,temp2 643 | 644 | sts sw_report+4,temp0 645 | mov temp2,temp1 646 | 647 | ; Slider 648 | 649 | subi XL,2 650 | 651 | ld temp0,X ; sl:-6543210 652 | 653 | inc XL 654 | inc XL 655 | 656 | ld temp1,X 657 | andi temp1,0b00000111 ; sh:-----987 658 | 659 | lsl temp0 ; sl:6543210- 660 | lsr temp1 ; sh:------98 [7] 661 | ror temp0 ; sl:76543210 662 | 663 | ldi temp3,0xFE 664 | add temp1,temp3 ; Add -512 665 | andi temp1,0b00000011 666 | 667 | bst temp0,7 668 | lsl temp0 ; sl:6543210- 669 | or temp0,temp2 670 | 671 | sts sw_report+5,temp0 672 | 673 | lsl temp1 ; sh:-----98- 674 | bld temp1,0 675 | 676 | sts sw_report+6,temp1 677 | 678 | ret 679 | 680 | ;------------------------------------------------------------------------------- 681 | ;******************************************************************************* 682 | ; Initiate and monitor data transfer from a 3DP/FFP/PP. 683 | ; INT0 is left enabled upon exit. 684 | ; 685 | ; Input: 686 | ; temp1 0 for data packet, -n for ID kick 687 | ; temp2 No. of triplets to wait for 688 | ; Return: 689 | ; 1 - received, 0 - timed out 690 | ;------------------------------------------------------------------------------- 691 | 692 | #define argSZ r22 693 | #define argID r24 694 | 695 | #define resOkL r24 696 | 697 | .global QueryFFP 698 | 699 | QueryFFP: 700 | mov temp1,argID 701 | mov temp2,argSZ 702 | 703 | clr resOkL ; Default return 0 704 | 705 | cli ; Disable interrupts 706 | 707 | sbi EIFR,INTF0 ; Clear INT condition 708 | sbi EIMSK,INT0 ; Enable INT 709 | 710 | clr temp3 711 | 712 | sbis BUTPIN,BUT1 ; Button 1 pressed ? 713 | ser temp3 ; Yes, have to swallow 1st INT.. 714 | 715 | ldi temp0,_B1(PSRSYNC) ; reset prescaler 716 | out GTCCR,temp0 717 | 718 | ldi temp0,T6TO400US 719 | out TCNT0,temp0 ; Set up initial timeout 720 | 721 | sbi TIFR0,TOV0 ; Clear overflow flag 722 | 723 | sts sw_clkcnt,temp3 ; Preset clock counter 724 | 725 | ldi temp0,lo8(sw_pktstart) ; &ffp_packet[6] 726 | sts sw_pktptr,temp0 727 | 728 | sei ; 1 729 | 730 | Ptrigger: 731 | cbi TRGDDR,TRGX1BIT ; 2 732 | cbi TRGDDR,TRGY2BIT ; 2 733 | 734 | ldi temp0,TRGWAIT ; wait 48us 735 | 1: dec temp0 736 | brne 1b 737 | 738 | sbi TRGDDR,TRGX1BIT ; 2 739 | sbi TRGDDR,TRGY2BIT ; 2 740 | 741 | Ploop: 742 | lds temp0,sw_clkcnt ; +2 743 | cp temp0,temp3 ; Anything new ? 1 744 | brne Pgotsome ; 1/2 = 2/3 745 | 746 | sbis TIFR0,TOV0 ; Timeout ? Skip if TOV set 1/2 747 | rjmp Ploop ; Wait some more 2 = 4 748 | 749 | ret ; Signal timeout, return 0 750 | 751 | Pgotsome: 752 | lds temp0,sw_clkcnt ; 1 +1 753 | 754 | sub temp0,temp3 ; # clk's that occured 1 755 | add temp3,temp0 ; Correct clkcnt copy 1 756 | 757 | cp temp3,temp2 ; Got all we need ? 1 758 | brsh Pdone ; Yes, done 1/2 = 6 759 | 760 | tst temp1 ; Kick pending ? 1 761 | brpl Ploop ; Nope 1/2 = 8 762 | 763 | add temp1,temp0 ; Kick due ? 1 764 | brmi Ploop ; Nope 1/2 = 10 765 | 766 | rjmp Ptrigger ; Yes, kick 2 = 11 767 | 768 | Pdone: ; Packet arrived.. 769 | inc resOkL ; Signal Ok, return 1 770 | ret 771 | 772 | ;------------------------------------------------------------------------------- 773 | ;******************************************************************************* 774 | ; Initiate and monitor data transfer from a 3DPro. 775 | ; INT0 is left enabled upon exit. 776 | ; 777 | ; Input: 778 | ; temp1 0 for data packet, -n for ID kick 779 | ; temp2 No. of triplets to wait for 780 | ; Return: 781 | ; 1 - received, 0 - timed out 782 | ;------------------------------------------------------------------------------- 783 | 784 | .global Query3DP 785 | 786 | Query3DP: 787 | rcall QueryFFP 788 | 789 | tst resOkL 790 | breq Qexit 791 | 792 | lds temp0,sw_clkcnt 793 | cpi temp0,DATSZ3DP ; If it's a data packet.. 794 | brne Qexit 795 | 796 | cli 797 | clr temp0 798 | sts sw_clkcnt,temp0 ; Correct SWclkcnt and ptr so the next 799 | lds temp0,sw_pktptr 800 | inc temp0 ; packet will allign w/ sw_packet2 801 | sts sw_pktptr,temp0 802 | sei 803 | Qexit: 804 | ret 805 | 806 | ;------------------------------------------------------------------------------- 807 | ;******************************************************************************* 808 | ;* End of file 809 | ;******************************************************************************* 810 | ;------------------------------------------------------------------------------- 811 | -------------------------------------------------------------------------------- /FFPJoystick.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Test.h - Test library for Wiring - implementation 3 | Copyright (c) 2006 John Doe. All right reserved. 4 | */ 5 | 6 | // include core Wiring API 7 | 8 | 9 | // include this library's description file 10 | #include "FFPJoystick.h" 11 | // Data structures for input reports from the joystick positions 12 | 13 | // include description files for other libraries used (if any) 14 | //#include "HardwareSerial.h" 15 | 16 | // Constructor ///////////////////////////////////////////////////////////////// 17 | // Function that handles the creation and setup of instances 18 | 19 | FFPJoystick::FFPJoystick() 20 | { 21 | SetupHardware(); 22 | effectId = 2; 23 | DisableAutoCenter(); 24 | constantForceID = CreateConstantForce(0,0); 25 | vibrationForceID = CreateVibration(0,0); 26 | } 27 | 28 | /** Configures the board hardware and chip peripherals for the functionality. */ 29 | void FFPJoystick::SetupHardware(void) 30 | { 31 | /* Disable watchdog if enabled by bootloader/fuses */ 32 | MCUSR &= ~(1 << WDRF); 33 | wdt_disable(); 34 | 35 | /* Disable clock division */ 36 | clock_prescale_set(clock_div_1); 37 | 38 | /* Hardware Initialization */ 39 | //LEDs_Init(); 40 | 41 | // Call the joystick's init and connection methods 42 | //Joystick_Init(); 43 | uint8_t sw_sendrep ; 44 | 45 | // Initialize.. 46 | 47 | init_hw() ; // hardware. Note: defined as naked ! 48 | 49 | WaitMs(1000); 50 | // Force feedback 51 | FfbInitMidi(); 52 | sei(); 53 | } 54 | 55 | void FFPJoystick::EnableAutoCenter(void) 56 | { 57 | FfbSendData(enableAutoCenterFfbData_1, 2); 58 | } 59 | 60 | void FFPJoystick::DisableAutoCenter(void) 61 | { 62 | FfbSendData(disableAutoCenterFfbData_1, 2);//second parameter is number of bytes 63 | _delay_ms(35); 64 | _delay_ms(40); 65 | FfbSendData(disableAutoCenterFfbData_2, 8);//would like to use sizeof but can't, 66 | //since these variables are declared elsewhere 67 | } 68 | 69 | void FFPJoystick::playEffect(uint8_t effectID) 70 | { 71 | FfbSendEffectOper(effectID, 0x20); 72 | } 73 | 74 | 75 | uint8_t FFPJoystick::CreateConstantForce(uint8_t magnitude,uint16_t direction) 76 | { 77 | FFP_MIDI_Effect_Basic test; 78 | FFP_MIDI_Effect_Basic* midi_data = &test; 79 | 80 | midi_data->duration = 0;//UsbUint16ToMidiUint14(500);//100 ms 81 | midi_data->magnitude = magnitude; 82 | midi_data->waveLength = 0; 83 | midi_data->waveForm = 0x12; 84 | midi_data->attackLevel = 0x7f; 85 | midi_data->attackTime = 0x0000; 86 | midi_data->fadeLevel = 0x7f; 87 | midi_data->fadeTime = 0x195a; 88 | midi_data->direction = direction; 89 | 90 | // Constants 91 | midi_data->command = 0x23; 92 | midi_data->unknown1 = 0x7F; 93 | midi_data->unknown2 = 0x0000; 94 | midi_data->unknown3[0] = 0x7F; 95 | midi_data->unknown3[1] = 0x64; 96 | midi_data->unknown3[2] = 0x00; 97 | midi_data->unknown3[3] = 0x10; 98 | midi_data->unknown3[4] = 0x4E; 99 | midi_data->unknown5 = 0x00; 100 | 101 | midi_data->param1 = 0x007f; 102 | 103 | if (midi_data->waveForm == 0x12) // constant 104 | midi_data->param2 = 0x0000; 105 | else 106 | midi_data->param2 = 0x0101; 107 | 108 | 109 | FfbSendSysEx((uint8_t*) midi_data, standardEffectPacketSize); 110 | FfbSendEffectOper(effectId, 0x20); 111 | effectId++; 112 | return (effectId - 1); 113 | //FfbSendData(sineFfbData,sizeof(sineFfbData)); 114 | } 115 | 116 | uint8_t FFPJoystick::CreateVibration(uint8_t magnitude,uint8_t wavelength) 117 | { 118 | /* 119 | uint8_t command; // always 0x23 -- start counting checksum from here 120 | uint8_t waveForm; // 2=sine, 5=Square, 6=RampUp, 7=RampDown, 8=Triange, 0x12=Constant 121 | uint8_t unknown1; // ? always 0x7F 122 | uint16_t duration; // unit=2ms 123 | uint16_t unknown2; // ? always 0x0000 124 | uint16_t direction; 125 | uint8_t unknown3[5]; // ? always 7f 64 00 10 4e 126 | uint8_t attackLevel; 127 | uint16_t attackTime; 128 | uint8_t magnitude; 129 | uint16_t fadeTime; 130 | uint8_t fadeLevel; 131 | uint8_t waveLength; // 0x6F..0x01 => 1/Hz 132 | uint8_t unknown5; // ? always 0x00 133 | uint16_t param1; // Varies by effect type; Constant: positive=7f 00, negative=01 01, Other effects: 01 01 134 | uint16_t param2; // Varies by effect type; Constant: 00 00, Other effects 01 01 135 | } FFP_MIDI_Effect_Basic;*/ 136 | 137 | FFP_MIDI_Effect_Basic test; 138 | FFP_MIDI_Effect_Basic* midi_data = &test; 139 | 140 | midi_data->duration = 0;//UsbUint16ToMidiUint14(500);//100 ms 141 | midi_data->magnitude = 0x16; 142 | midi_data->waveLength = wavelength; 143 | midi_data->waveForm = 0x02;//sine 144 | midi_data->attackLevel = 0x7f; 145 | midi_data->attackTime = 0x0000; 146 | midi_data->fadeLevel = 0x00; 147 | midi_data->fadeTime = 0x0000; 148 | midi_data->direction = 0x0000; 149 | 150 | // Constants 151 | midi_data->command = 0x23; 152 | midi_data->unknown1 = 0x7F; 153 | midi_data->unknown2 = 0x0000; 154 | midi_data->unknown3[0] = 0x7F; 155 | midi_data->unknown3[1] = 0x64; 156 | midi_data->unknown3[2] = 0x00; 157 | midi_data->unknown3[3] = 0x10; 158 | midi_data->unknown3[4] = 0x4E; 159 | midi_data->unknown5 = 0x00; 160 | 161 | midi_data->param1 = 0x007f; 162 | 163 | if (midi_data->waveForm == 0x12) // constant 164 | midi_data->param2 = 0x0000; 165 | else 166 | midi_data->param2 = 0x0101; 167 | 168 | uint8_t sineFfbData[] = 169 | { 170 | /*0xf0, // define 171 | 0x00, 0x01, 0x0a, 0x01, //start sequence*/ 172 | 0x23,//command 173 | 0x02,//square 174 | 0x7f,//unknown1 175 | 0x5a, 0x19,//duration in 2ms intervals 176 | 0x00, 0x00,//unknown2 177 | 0x00, 0x00,//direction 178 | 0x7f, 0x64, 0x00, 0x10, 0x4e,//unknown 3 179 | 0x7f,//envelope attack level 180 | 0x00,0x00,//envelope attack time 181 | 0x16,//magnitude 182 | 0x5a,0x19,//envelope fade time 183 | 0x7f,//envelope fade level 184 | 0x17,//wavelength 185 | 0x00,//constant dir 186 | 0x7f,//constant dir 187 | 0x00, 0x01, 0x01/*, 0x33, 188 | 0xf7, 189 | 0xb5, 0x20, 0x02*/ // play 190 | }; 191 | 192 | 193 | FfbSendSysEx((uint8_t*) midi_data, sizeof(sineFfbData)); 194 | FfbSendEffectOper(effectId, 0x20); 195 | effectId++; 196 | return (effectId - 1); 197 | //FfbSendData(sineFfbData,sizeof(sineFfbData)); 198 | } 199 | 200 | void FFPJoystick::sendConstantForce(uint8_t magnitude,uint16_t direction) 201 | { 202 | FfbSendModify(constantForceID, 0x74, magnitude); 203 | FfbSendModify(constantForceID, 0x48, direction); 204 | FfbSendEffectOper(constantForceID, 0x20); 205 | } 206 | 207 | 208 | void FFPJoystick::sendVibration(uint8_t magnitude,uint8_t wavelength) 209 | { 210 | FfbSendModify(vibrationForceID, 0x74, magnitude); 211 | FfbSendModify(vibrationForceID, 0x70, wavelength); 212 | FfbSendEffectOper(vibrationForceID, 0x20); 213 | } 214 | 215 | void FFPJoystick::Poll(void) 216 | { 217 | SetTMPS( 0, 64 ) ; // Set T0 prescaler to / 64 for query 218 | getdata(); 219 | // Convert the raw input data to USB report 220 | 221 | reportId = 1; // Input report ID 1 222 | 223 | // Convert data from Sidewinder Force Feedback Pro 224 | X = sw_report[0] + ((sw_report[1] & 0x03) << 8); 225 | if (sw_report[1] & 0x02) 226 | X |= (0b11111100 << 8); 227 | Y = (sw_report[1] >> 2) + ((sw_report[2] & 0x0F) << 6); 228 | if (sw_report[2] & 0x08) 229 | Y |= (0b11111100 << 8); 230 | Button = ((sw_report[4] & 0x7F) << 2) + ((sw_report[3] & 0xC0) >> 6); 231 | Hat = sw_report[2] >> 4; 232 | Rz = (sw_report[3] & 0x3f) - 32; 233 | Throttle = ((sw_report[5] & 0x3f) << 1) + (sw_report[4] >> 7); 234 | if (sw_report[5] & 0x20) 235 | Throttle |= 0b11000000; 236 | 237 | if (Throttle > 63) 238 | Throttle = -1*(255-Throttle); 239 | //if ((int8_t)(Z) < 0) 240 | //Z = Z*-1+64; 241 | 242 | 243 | //FfbSendData(enableAutoCenterFfbData_1, 2); 244 | } -------------------------------------------------------------------------------- /FFPJoystick.h: -------------------------------------------------------------------------------- 1 | /* 2 | Test.h - Test library for Wiring - description 3 | Copyright (c) 2006 John Doe. All right reserved. 4 | */ 5 | 6 | // ensure this library description is only included once 7 | #ifndef FFPJoystick_h 8 | #define FFPJoystick_h 9 | #include "WProgram.h" 10 | // include types & constants of Wiring core API 11 | #include "WConstants.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | extern "C"{ 21 | #include "ffb.h" 22 | #include "3DPro.h" 23 | //#include "Joystick.h" 24 | //#include "debug.h" 25 | } 26 | 27 | 28 | 29 | extern uint8_t enableAutoCenterFfbData_1[]; 30 | extern uint8_t disableAutoCenterFfbData_1[]; 31 | extern uint8_t disableAutoCenterFfbData_2[]; 32 | 33 | // library interface description 34 | class FFPJoystick 35 | { 36 | // user-accessible "public" interface 37 | public: 38 | FFPJoystick(); 39 | void EnableAutoCenter(); 40 | void DisableAutoCenter(); 41 | void SetupHardware(); 42 | void Poll(); 43 | uint8_t CreateVibration(uint8_t magnitude,uint8_t wavelength); 44 | uint8_t CreateConstantForce(uint8_t magnitude,uint16_t direction); 45 | 46 | void sendConstantForce(uint8_t magnitude,uint16_t direction); 47 | void sendVibration(uint8_t effectID,uint8_t wavelength); 48 | void playEffect(uint8_t effectID); 49 | 50 | int getButton(int buttonNum){return ((Button & (1 << buttonNum)) >>buttonNum);} 51 | 52 | // Joystick Input Report 53 | uint8_t reportId; // =1 54 | int16_t X; 55 | int16_t Y; 56 | //int16_t Z; 57 | //int8_t Rz, Rx, Ry; 58 | int8_t Rz; 59 | //uint8_t Rudder; 60 | int16_t Throttle; 61 | uint16_t Button; 62 | uint8_t Hat; 63 | int effectId; 64 | int constantForceID; 65 | int vibrationForceID; 66 | const static int standardEffectPacketSize = 27; 67 | }; 68 | 69 | #endif 70 | 71 | -------------------------------------------------------------------------------- /FFPtest/FFPtest.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | FFPJoystick stick; 5 | uint8_t magnitude = 20; 6 | uint8_t vibration; 7 | 8 | /* 9 | * Trainer PPM Interface 10 | * ------------------- 11 | * 12 | * 13 | * Created 31 October 2007 14 | * copyleft 2007 Mathieu Glachant 15 | * mathieu@ban-sidhe.com 16 | * http://ban-sidhe.com/ 17 | * 18 | * adapted from todbot's Serial-Servo-Better 19 | */ 20 | 21 | int servoPin = 6; // Control pin for trainer interface 22 | 23 | // A pulse starts with a low signal of fixed width (0.3ms), 24 | // followed by a high signal for the remainder of the pulse. 25 | // Total pulse width is proportional to servo position (1 to 2ms) 26 | int pulseStart = 300; // pulse start width in microseconds 27 | int pulseMin = 724; // pulse minimum width minus start in microseconds 28 | int pulseMax = 2048; // pulse maximum width in microseconds 29 | int pulseMid = 1500; 30 | int conversionFactor = 5.7; // (pulseMax - pulseMin - pulseStart)/180 31 | 32 | // A frame is a succession of pulses, in order of channels, 33 | // followed by a synchronisation pulse to fill out the frame. 34 | // A frame's total length is fixed (20ms) 35 | int frameLength = 20; // The duration in millisecs of a frame 36 | 37 | long lastFrame = 0; // The time in millisecs of the last frame 38 | int channelNumber = 4; // Number of channels to send (keep below frameLength/pulseMax) 39 | int servo[4]; // Values to set for the servos in degrees 40 | int channel[4]; // Values to send on channels (duration of pulse minus start, in microseconds) 41 | int i; // Counter in for loop 42 | int j = 0; // Counter for servo updates 43 | 44 | 45 | 46 | void setup() 47 | { 48 | pinMode(servoPin, OUTPUT); // Set servo pin as an output pin 49 | Serial.begin(115200); // connect to the serial port 50 | for ( i = 0; i < channelNumber; i = i + 1 ) {servo[i] = 0;} 51 | for ( i = 0; i < channelNumber; i = i + 1 ) {channel[i] = 1500;} 52 | Serial.println("Trainer_PPM_Interface ready"); 53 | 54 | 55 | stick.EnableAutoCenter(); 56 | //stick.DisableAutoCenter(); 57 | // Serial.begin(115200); 58 | // stick.CreateConstantForce(0,0); 59 | } 60 | void loop() 61 | { 62 | 63 | // Save the time of frame start 64 | lastFrame = millis(); 65 | 66 | // This for loop generates the pulse train, one per channel 67 | for ( i = 0; i < channelNumber; i = i + 1 ) { 68 | digitalWrite(servoPin, LOW); // Initiate pulse start 69 | delayMicroseconds(pulseStart); // Duration of pulse start 70 | digitalWrite(servoPin, HIGH); // Stop pulse start 71 | delayMicroseconds(channel[i]); // Finish off pulse 72 | } 73 | digitalWrite(servoPin, LOW); // Initiate synchronisation pulse 74 | delayMicroseconds(pulseStart); // Duration of start of synchronisation pulse 75 | digitalWrite(servoPin, HIGH); // Stop synchronisation pulse start 76 | 77 | // We're done generating pulses and using delayMicroseconds() 78 | // Time to do some other processing before the next frame 79 | // How much time depends on how many channels you are running 80 | 81 | stick.Poll(); 82 | /* Serial.print(stick.effectId); 83 | Serial.print(" "); 84 | Serial.print(stick.constantForceID); 85 | Serial.print(stick.Throttle); 86 | Serial.print(" "); 87 | for(int i=0;i<9;i++) 88 | { 89 | Serial.print(stick.getButton(i)); 90 | Serial.print(" "); 91 | } 92 | Serial.println(stick.Button,BIN);*/ 93 | /* 94 | vibration = (126 - (stick.Throttle + 63)); 95 | Serial.print(magnitude); 96 | Serial.print(" "); 97 | Serial.println(vibration); 98 | 99 | if(stick.getButton(0)) 100 | magnitude++; 101 | if(stick.getButton(1)) 102 | magnitude--; 103 | stick.sendVibration(magnitude,vibration); 104 | // stick.forceTest(60); 105 | delay(50); 106 | /* if(stick.Throttle > 0) 107 | stick.sendConstantForce(abs(stick.Throttle),0); 108 | else 109 | stick.sendConstantForce(abs(stick.Throttle),0x3401);*/ 110 | 111 | // stick.playEffect(2); 112 | 113 | channel[0] = pulseMid + stick.Throttle*4.0; 114 | channel[1] = pulseMid + stick.Throttle*4.0; 115 | 116 | 117 | // Let's change the servo positions 118 | j=j+1; 119 | if (j==4) {j=0;} 120 | 121 | if (j==0) { 122 | for ( i = 0; i < channelNumber; i = i + 1 ) { 123 | // servo[i] = servo[i]+1; 124 | servo[i] = (126 - (stick.Throttle + 63)); 125 | if (servo[i] >= 360) {servo[i]=0;} 126 | } 127 | } 128 | 129 | Serial.print(servo[0]); 130 | Serial.print(" "); 131 | Serial.println(servo[1]); 132 | 133 | 134 | 135 | // Calculate pulse durations from servo positions 136 | for ( i = 0; i < channelNumber; i = i + 1 ) { 137 | channel[i] = abs(servo[i]-180); 138 | channel[i] = int(channel[i]*conversionFactor)+pulseMin; 139 | } 140 | 141 | 142 | // We're ready to wait for the next frame 143 | // Some jitter is allowed, so to the closest ms 144 | while (millis() - lastFrame < frameLength) { 145 | delay(1); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /ffb.c: -------------------------------------------------------------------------------- 1 | /* 2 | Force Feedback Joystick 3 | Joystick model specific code for handling force feedback data. 4 | This code is for Microsoft Sidewinder Force Feedback Pro joystick. 5 | 6 | Copyright 2012 Tero Loimuneva (tloimu [at] gmail [dot] com) 7 | 8 | Permission to use, copy, modify, distribute, and sell this 9 | software and its documentation for any purpose is hereby granted 10 | without fee, provided that the above copyright notice appear in 11 | all copies and that both that the copyright notice and this 12 | permission notice and warranty disclaimer appear in supporting 13 | documentation, and that the name of the author not be used in 14 | advertising or publicity pertaining to distribution of the 15 | software without specific, written prior permission. 16 | 17 | The author disclaim all warranties with regard to this 18 | software, including all implied warranties of merchantability 19 | and fitness. In no event shall the author be liable for any 20 | special, indirect or consequential damages or any damages 21 | whatsoever resulting from loss of use, data or profits, whether 22 | in an action of contract, negligence or other tortious action, 23 | arising out of or in connection with the use or performance of 24 | this software. 25 | */ 26 | 27 | #include "ffb.h" 28 | 29 | #include 30 | #include 31 | #include 32 | //#include 33 | 34 | //#include "joystick.h" 35 | //#include "debug.h" 36 | #include "3DPro.h" 37 | 38 | #define USART_BAUD 31250 39 | 40 | // Effect management 41 | volatile uint8_t nextEID = 2; // FFP effect indexes starts from 2 (yes, we waste memory for two effects...) 42 | volatile USB_FFBReport_PIDStatus_Input_Data_t pidState; // For holding device status flags 43 | 44 | void SendPidStateForEffect(uint8_t eid, uint8_t effectState); 45 | void SendPidStateForEffect(uint8_t eid, uint8_t effectState) 46 | { 47 | pidState.effectBlockIndex = effectState; 48 | 49 | pidState.effectBlockIndex = 0; 50 | } 51 | 52 | const uint16_t USB_DURATION_INFINITE = 0x7FFF; 53 | const uint16_t MIDI_DURATION_INFINITE = 0; 54 | 55 | // Bit-masks for effect states 56 | const uint8_t MEffectState_Free = 0x00; 57 | const uint8_t MEffectState_Allocated = 0x01; 58 | const uint8_t MEffectState_Playing = 0x02; 59 | const uint8_t MEffectState_SentToJoystick = 0x04; 60 | 61 | typedef struct { 62 | uint8_t state; // see constants 63 | uint16_t usb_duration, usb_fadeTime; // used to calculate fadeTime to MIDI, since in USB it is given as time difference from the end while in MIDI it is given as time from start 64 | // These are used to calculate effects of USB gain to MIDI data 65 | uint8_t usb_gain, usb_offset, usb_attackLevel, usb_fadeLevel; 66 | uint8_t usb_magnitude; 67 | FFP_MIDI_Effect_Basic data; // For FFP, this is enough for all types of effects - cast for other effect types when necessary 68 | } TEffectState; 69 | 70 | static volatile TEffectState gEffectStates[MAX_EFFECTS+1]; // one for each effect (array index 0 is unused to simplify things) 71 | 72 | volatile TDisabledEffectTypes gDisabledEffects; 73 | 74 | // All effects data start with this data 75 | static uint8_t sCommonEffectHeader[] = {0x00, 0x01, 0x0a, 0x01}; 76 | 77 | uint8_t GetNextFreeEffect(void); 78 | void StartEffect(uint8_t id); 79 | void StopEffect(uint8_t id); 80 | void StopAllEffects(void); 81 | void FreeEffect(uint8_t id); 82 | void FreeAllEffects(void); 83 | 84 | uint8_t GetNextFreeEffect(void) 85 | { 86 | if (nextEID == MAX_EFFECTS) 87 | return 0; 88 | 89 | uint8_t id = nextEID++; 90 | 91 | // Find the next free effect ID for next time 92 | while (gEffectStates[nextEID].state != 0) 93 | { 94 | if (nextEID >= MAX_EFFECTS) 95 | break; // the last spot was taken 96 | nextEID++; 97 | } 98 | 99 | gEffectStates[id].state = MEffectState_Allocated; 100 | memset((void*) &gEffectStates[id].data, 0, sizeof(gEffectStates[id].data)); 101 | 102 | return id; 103 | } 104 | 105 | void StopAllEffects(void) 106 | { 107 | uint8_t id; 108 | for (id = 1; id <= MAX_EFFECTS; id++) 109 | StopEffect(id); 110 | } 111 | 112 | void StartEffect(uint8_t id) 113 | { 114 | if (id > MAX_EFFECTS) 115 | return; 116 | gEffectStates[id].state |= MEffectState_Playing; 117 | } 118 | 119 | void StopEffect(uint8_t id) 120 | { 121 | if (id > MAX_EFFECTS) 122 | return; 123 | gEffectStates[id].state &= ~MEffectState_Playing; 124 | } 125 | 126 | void FreeEffect(uint8_t id) 127 | { 128 | if (id > MAX_EFFECTS) 129 | return; 130 | 131 | gEffectStates[id].state = 0; 132 | if (id < nextEID) 133 | nextEID = id; 134 | } 135 | 136 | void FreeAllEffects(void) 137 | { 138 | nextEID = 2; 139 | memset((void*) gEffectStates, 0, sizeof(gEffectStates)); 140 | } 141 | 142 | // Utilities 143 | 144 | // Function: CalculateSysExChecksum 145 | // Checksum for MIDI SysEx message content. 146 | // See e.g. algorhitm used by Roland 147 | // - sum all bytes (after "address" i.e. 4 bytes) 148 | // - reminder from division by 128 149 | // - subtract the reminder from 128 => checksum 150 | uint8_t CalculateSysExChecksum(uint8_t *data, uint8_t len); 151 | uint8_t CalculateSysExChecksum(uint8_t *data, uint8_t len) 152 | { 153 | uint8_t cs = 0; 154 | uint8_t i; 155 | for (i = 0; i < len; i++) // skip the "address" part 156 | { 157 | cs += data[i]; // let them overflow, which effectively leaves the reminder part 158 | } 159 | cs &= 0x7F; // take the potential final 128 off 160 | 161 | if (cs != 0) 162 | cs = 0x80 - cs; 163 | 164 | return cs; 165 | } 166 | 167 | uint16_t UsbUint16ToMidiUint14(uint16_t inUsbValue) 168 | { 169 | if (inUsbValue == 0xFFFF) 170 | return 0x0000; 171 | 172 | return (inUsbValue & 0x7F00) + ((inUsbValue & 0x00FF) >> 1); // loss of the MSB-bit! 173 | } 174 | 175 | int16_t UsbInt8ToMidiInt14(int8_t inUsbValue) 176 | { 177 | int16_t value; 178 | if (inUsbValue < 0) 179 | { 180 | value = inUsbValue; 181 | value += 0x7f80; 182 | } 183 | else 184 | value = inUsbValue; 185 | 186 | return value; 187 | } 188 | 189 | // Calculates the final value of the given when taking in given into account. 190 | // Returns MIDI value (i.e. max 0..7f). 191 | uint8_t CalcGain(uint8_t usbValue, uint8_t gain) 192 | { 193 | int16_t v = usbValue; 194 | return (((v * gain) / 256) >> 2 ) & 0x7f; 195 | } 196 | 197 | 198 | void FfbSendSysEx(void *midi_data, uint16_t len) 199 | { 200 | uint8_t mark = 0xF0; // SysEx Start 201 | FfbSendData(&mark, 1); 202 | 203 | FfbSendData(sCommonEffectHeader, sizeof(sCommonEffectHeader)); 204 | FfbSendData((uint8_t*) midi_data, len); 205 | 206 | uint8_t checksum = CalculateSysExChecksum((uint8_t*) midi_data, len); 207 | FfbSendData(&checksum, 1); 208 | 209 | mark = 0xF7; // SysEx End 210 | FfbSendData(&mark, 1); 211 | } 212 | 213 | 214 | // Send to MIDI Control Change "B5" 215 | void FfbSendEffectOper(uint8_t effectId, uint8_t operation) 216 | { 217 | uint8_t midi_cmd[3]; 218 | midi_cmd[0] = 0xB5; 219 | midi_cmd[1] = operation; 220 | midi_cmd[2] = effectId; 221 | FfbSendData(midi_cmd, 3); 222 | } 223 | 224 | 225 | // Send to MIDI effect data modification to the given address of the given effect 226 | void FfbSendModify(uint8_t effectId, uint8_t address, uint16_t value) 227 | { 228 | // Modify + Address 229 | uint8_t midi_cmd[3]; 230 | midi_cmd[0] = 0xB5; 231 | midi_cmd[1] = address; 232 | midi_cmd[2] = effectId; 233 | FfbSendData(midi_cmd, 3); 234 | 235 | // New value 236 | midi_cmd[0] = 0xA5; 237 | midi_cmd[1] = value & 0x7F; 238 | midi_cmd[2] = (value & 0x7F00) >> 8; 239 | FfbSendData(midi_cmd, 3); 240 | } 241 | 242 | // Lengths of each report type 243 | const uint16_t OutReportSize[] = { 244 | sizeof(USB_FFBReport_SetEffect_Output_Data_t), // 1 245 | sizeof(USB_FFBReport_SetEnvelope_Output_Data_t), // 2 246 | sizeof(USB_FFBReport_SetCondition_Output_Data_t), // 3 247 | sizeof(USB_FFBReport_SetPeriodic_Output_Data_t), // 4 248 | sizeof(USB_FFBReport_SetConstantForce_Output_Data_t), // 5 249 | sizeof(USB_FFBReport_SetRampForce_Output_Data_t), // 6 250 | sizeof(USB_FFBReport_SetCustomForceData_Output_Data_t), // 7 251 | sizeof(USB_FFBReport_SetDownloadForceSample_Output_Data_t), // 8 252 | 0, // 9 253 | sizeof(USB_FFBReport_EffectOperation_Output_Data_t), // 10 254 | sizeof(USB_FFBReport_BlockFree_Output_Data_t), // 11 255 | sizeof(USB_FFBReport_DeviceControl_Output_Data_t), // 12 256 | sizeof(USB_FFBReport_DeviceGain_Output_Data_t), // 13 257 | sizeof(USB_FFBReport_SetCustomForce_Output_Data_t), // 14 258 | }; 259 | 260 | 261 | void FfbSendData(uint8_t *data, uint16_t len); 262 | void FfbSendEnableInterrupts(void); 263 | 264 | void FfbHandle_SetEffect(USB_FFBReport_SetEffect_Output_Data_t *data); 265 | void FfbHandle_SetEnvelope(USB_FFBReport_SetEnvelope_Output_Data_t *data); 266 | void FfbHandle_SetCondition(USB_FFBReport_SetCondition_Output_Data_t *data); 267 | void FfbHandle_SetPeriodic(USB_FFBReport_SetPeriodic_Output_Data_t *data); 268 | void FfbHandle_SetConstantForce(USB_FFBReport_SetConstantForce_Output_Data_t *data); 269 | void FfbHandle_SetRampForce(USB_FFBReport_SetRampForce_Output_Data_t *data); 270 | void FfbHandle_SetCustomForceData(USB_FFBReport_SetCustomForceData_Output_Data_t *data); 271 | void FfbHandle_SetDownloadForceSample(USB_FFBReport_SetDownloadForceSample_Output_Data_t *data); 272 | void FfbHandle_EffectOperation(USB_FFBReport_EffectOperation_Output_Data_t *data); 273 | void FfbHandle_BlockFree(USB_FFBReport_BlockFree_Output_Data_t *data); 274 | void FfbHandle_DeviceControl(USB_FFBReport_DeviceControl_Output_Data_t *data); 275 | void FfbHandle_DeviceGain(USB_FFBReport_DeviceGain_Output_Data_t *data); 276 | void FfbHandle_SetCustomForce(USB_FFBReport_SetCustomForce_Output_Data_t *data); 277 | 278 | // Handle incoming data from USB and convert it to MIDI data to joystick 279 | void FfbOnUsbData(uint8_t *data, uint16_t len) 280 | { 281 | // Parse incoming USB data and convert it to MIDI data for the joystick 282 | //LEDs_SetAllLEDs(LEDS_ALL_LEDS); 283 | 284 | //if (gDebugMode & DEBUG_DETAIL) 285 | //LogReport("<= FfbData:", OutReportSize, data, len); 286 | 287 | switch (data[0]) // reportID 288 | { 289 | case 1: 290 | FfbHandle_SetEffect((USB_FFBReport_SetEffect_Output_Data_t*) data); 291 | break; 292 | case 2: 293 | FfbHandle_SetEnvelope((USB_FFBReport_SetEnvelope_Output_Data_t*) data); 294 | break; 295 | case 3: 296 | FfbHandle_SetCondition((USB_FFBReport_SetCondition_Output_Data_t*) data); 297 | break; 298 | case 4: 299 | FfbHandle_SetPeriodic((USB_FFBReport_SetPeriodic_Output_Data_t*) data); 300 | break; 301 | case 5: 302 | FfbHandle_SetConstantForce((USB_FFBReport_SetConstantForce_Output_Data_t*) data); 303 | break; 304 | case 6: 305 | FfbHandle_SetRampForce((USB_FFBReport_SetRampForce_Output_Data_t*) data); 306 | break; 307 | case 7: 308 | FfbHandle_SetCustomForceData((USB_FFBReport_SetCustomForceData_Output_Data_t*) data); 309 | break; 310 | case 8: 311 | FfbHandle_SetDownloadForceSample((USB_FFBReport_SetDownloadForceSample_Output_Data_t*) data); 312 | break; 313 | case 9: 314 | break; 315 | case 10: 316 | FfbHandle_EffectOperation((USB_FFBReport_EffectOperation_Output_Data_t*) data); 317 | break; 318 | case 11: 319 | FfbHandle_BlockFree((USB_FFBReport_BlockFree_Output_Data_t *) data); 320 | break; 321 | case 12: 322 | FfbHandle_DeviceControl((USB_FFBReport_DeviceControl_Output_Data_t*) data); 323 | break; 324 | case 13: 325 | FfbHandle_DeviceGain((USB_FFBReport_DeviceGain_Output_Data_t*) data); 326 | break; 327 | case 14: 328 | FfbHandle_SetCustomForce((USB_FFBReport_SetCustomForce_Output_Data_t*) data); 329 | break; 330 | default: 331 | break; 332 | }; 333 | 334 | //LEDs_SetAllLEDs(LEDS_NO_LEDS); 335 | } 336 | 337 | 338 | // Define and play constant force to left 339 | uint8_t leftData[] = 340 | { 341 | 0xf0, // define 342 | 0x00, 0x01, 0x0a, 0x01, 0x23, 0x12, 0x7f, 0x5a, 0x19, 0x00, 0x00, 0x5a, 0x00, 0x7f, 0x64, 0x00, 0x10, 0x4e, 0x7f, 0x00, 0x00, 0x7f, 0x5a, 0x19, 0x7f, 0x01, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x4e, 343 | 0xf7, 344 | // 0xb5, 0x20, 0x02 // play 345 | }; 346 | 347 | // Constant, Ramp, Square, Sine, Triange, SawtoothDown, SawtoothUp, Spring, Damper, Inertia, Friction, Custom? 348 | uint8_t usbToMidiEffectType[] = { 0x12,0x06,0x05,0x02,0x08,0x0A,0x0B,0x0D,0x0E,0x0F,0x10,0x01 }; 349 | 350 | 351 | void FfbOnCreateNewEffect(USB_FFBReport_CreateNewEffect_Feature_Data_t* inData, USB_FFBReport_PIDBlockLoad_Feature_Data_t *outData) 352 | { 353 | LogTextP(PSTR("Create New Effect:\n ")); 354 | LogBinaryLf(inData, sizeof(USB_FFBReport_CreateNewEffect_Feature_Data_t)); 355 | 356 | /* 357 | USB effect data: 358 | uint8_t reportId; // =1 359 | uint8_t effectType; // Enum (1..12): ET 26,27,30,31,32,33,34,40,41,42,43,28 360 | uint16_t byteCount; // 0..511 - only valid with Custom Force 361 | */ 362 | outData->reportId = 6; 363 | outData->effectBlockIndex = GetNextFreeEffect(); 364 | if (outData->effectBlockIndex == 0) 365 | outData->loadStatus = 2; // 1=Success,2=Full,3=Error 366 | else 367 | { 368 | outData->loadStatus = 1; // 1=Success,2=Full,3=Error 369 | 370 | // Set defaults to the effect data 371 | volatile TEffectState *effect_state = &gEffectStates[outData->effectBlockIndex]; 372 | effect_state->usb_duration = USB_DURATION_INFINITE; 373 | effect_state->usb_fadeTime = USB_DURATION_INFINITE; 374 | effect_state->usb_gain = 0xFF; 375 | effect_state->usb_offset = 0; 376 | effect_state->usb_attackLevel = 0xFF; 377 | effect_state->usb_fadeLevel = 0xFF; 378 | 379 | volatile FFP_MIDI_Effect_Basic *midi_data = &effect_state->data; 380 | 381 | midi_data->magnitude = 0x7f; 382 | midi_data->waveLength = 0x01; 383 | midi_data->waveForm = usbToMidiEffectType[inData->effectType - 1]; 384 | midi_data->attackLevel = 0x00; 385 | midi_data->attackTime = 0x0000; 386 | midi_data->fadeLevel = 0x00; 387 | midi_data->fadeTime = 0x0000; 388 | 389 | // Constants 390 | midi_data->command = 0x23; 391 | midi_data->unknown1 = 0x7F; 392 | midi_data->unknown2 = 0x0000; 393 | midi_data->unknown3[0] = 0x7F; 394 | midi_data->unknown3[1] = 0x64; 395 | midi_data->unknown3[2] = 0x00; 396 | midi_data->unknown3[3] = 0x10; 397 | midi_data->unknown3[4] = 0x4E; 398 | 399 | if (midi_data->waveForm == 0x12) // constant 400 | midi_data->param2 = 0x0000; 401 | else 402 | midi_data->param2 = 0x0101; 403 | } 404 | 405 | outData->ramPoolAvailable = 0xFFFF; // =0 or 0xFFFF - don't really know what this is used for? 406 | 407 | LogDataLf(" => Usb:", outData->reportId, outData, sizeof(USB_FFBReport_PIDBlockLoad_Feature_Data_t)); 408 | 409 | _delay_ms(5); 410 | } 411 | 412 | 413 | void FfbOnPIDPool(USB_FFBReport_PIDPool_Feature_Data_t *data) 414 | { 415 | LogTextP(PSTR("GetReport PID Pool Feature:\n => Usb:")); 416 | 417 | FreeAllEffects(); 418 | 419 | data->reportId = 7; 420 | data->ramPoolSize = 0xFFFF; 421 | data->maxSimultaneousEffects = 0x0A; // FFP supports playing up to 10 simultaneous effects 422 | data->memoryManagement = 3; 423 | 424 | LogBinaryLf(data, sizeof(USB_FFBReport_PIDPool_Feature_Data_t)); 425 | } 426 | 427 | 428 | void FfbHandle_SetEffect(USB_FFBReport_SetEffect_Output_Data_t *data) 429 | { 430 | uint8_t eid = data->effectBlockIndex; 431 | 432 | /* LogTextP(PSTR("Set Effect:")); 433 | LogBinaryLf(data, sizeof(USB_FFBReport_SetEffect_Output_Data_t)); 434 | LogTextP(PSTR(" id =")); LogBinaryLf(&eid, sizeof(eid)); 435 | LogTextP(PSTR(" type=")); LogBinaryLf(&data->effectType, sizeof(data->effectType)); 436 | LogTextP(PSTR(" gain=")); LogBinaryLf(&data->gain, sizeof(data->gain)); 437 | LogTextP(PSTR(" dura=")); LogBinaryLf(&data->duration, sizeof(data->duration)); 438 | if (data->enableAxis) 439 | { 440 | LogTextP(PSTR(" X=")); LogBinaryLf(&data->directionX, sizeof(data->directionX)); 441 | LogTextP(PSTR(" Y=")); LogBinaryLf(&data->directionY, sizeof(data->directionY)); 442 | } 443 | if (data->triggerRepeatInterval) 444 | { 445 | LogTextP(PSTR(" repeat=")); LogBinaryLf(&data->triggerRepeatInterval, sizeof(data->triggerRepeatInterval)); 446 | } 447 | if (data->triggerButton) 448 | { 449 | LogTextP(PSTR(" button=")); LogBinaryLf(&data->triggerButton, sizeof(data->triggerButton)); 450 | } 451 | */ 452 | 453 | /* 454 | USB effect data: 455 | uint8_t reportId; // =1 456 | uint8_t effectBlockIndex; // 1..40 457 | uint8_t effectType; // 1..12 (effect usages: 26,27,30,31,32,33,34,40,41,42,43,28) 458 | uint16_t duration; // 0..32767 ms 459 | uint16_t triggerRepeatInterval; // 0..32767 ms 460 | uint16_t samplePeriod; // 0..32767 ms 461 | uint8_t gain; // 0..255 (physical 0..10000) 462 | uint8_t triggerButton; // button ID (0..8) 463 | uint8_t enableAxis; // bits: 0=X, 1=Y, 2=DirectionEnable 464 | uint8_t directionX; // angle (0=0 .. 180=0..360deg) 465 | uint8_t directionY; // angle (0=0 .. 180=0..360deg) 466 | */ 467 | 468 | // Fill in data that is common to all effect MIDI data types 469 | volatile FFP_MIDI_Effect_Basic *common_midi_data = &gEffectStates[eid].data; 470 | 471 | if (data->duration == USB_DURATION_INFINITE) 472 | common_midi_data->duration = MIDI_DURATION_INFINITE; 473 | else 474 | common_midi_data->duration = UsbUint16ToMidiUint14(data->duration); // MIDI unit is 2ms 475 | gEffectStates[eid].usb_duration = data->duration; // store for later calculation of 476 | 477 | if (gEffectStates[eid].state & MEffectState_SentToJoystick) 478 | { // Send update 479 | FfbSendModify(eid, 0x40, common_midi_data->duration); 480 | } 481 | 482 | uint8_t midi_data_len = sizeof(FFP_MIDI_Effect_Basic); // default MIDI data size 483 | 484 | boolean is_periodic = false; 485 | 486 | // Fill in the effect type specific data 487 | switch (data->effectType) 488 | { 489 | case 3: // square (midi: 5) 490 | case 4: // sine (midi: 2) or cosine (midi: 3) 491 | 492 | 493 | case 5: // triangle (midi: 8) 494 | case 6: // sawtooth up (midi: 0x0a) 495 | case 7: // sawtooth down (midi: 0x0b) 496 | is_periodic = true; 497 | case 1: // constant force (midi: 0x12) 498 | case 2: // ramp up & down (midi: 6 or 7) 499 | { 500 | /* 501 | MIDI effect data: 502 | uint8_t command; // always 0x23 -- start counting checksum from here 503 | uint8_t waveForm; // 2=sine, 5=Square, 6=RampUp, 7=RampDown, 8=Triange, 0x12=Constant 504 | uint8_t unknown1; // ? always 0x7F 505 | uint16_t duration; // unit=2ms 506 | uint16_t unknown2; // ? always 0x0000 507 | uint16_t direction; 508 | uint8_t unknown3[5]; // ? always 7f 64 00 10 4e 509 | uint8_t attackLevel; 510 | uint16_t attackTime; 511 | uint8_t magnitude; 512 | uint16_t fadeTime; 513 | uint8_t fadeLevel; 514 | uint8_t waveLength; // 0x6F..0x01 => 1/Hz 515 | uint8_t unknown5; // ? always 0x00 516 | uint16_t param1; // Constant: positive=7f 00, negative=01 01, Other effects: 01 01 517 | uint16_t param2; // Constant: 00 00, Other effects 01 01 518 | */ 519 | 520 | volatile TEffectState *effect = &gEffectStates[eid]; 521 | volatile FFP_MIDI_Effect_Basic *midi_data = &effect->data; 522 | 523 | // Convert direction 524 | uint16_t usbdir = data->directionX; 525 | usbdir = usbdir * 2; 526 | uint16_t dir = (usbdir & 0x7F) + ( (usbdir & 0x0180) << 1 ); 527 | midi_data->direction = dir; 528 | 529 | // Recalculate fadeTime for MIDI since change to duration changes the fadeTime too 530 | if (data->duration == USB_DURATION_INFINITE) 531 | midi_data->fadeTime = MIDI_DURATION_INFINITE; 532 | else 533 | { 534 | if (effect->usb_fadeTime == USB_DURATION_INFINITE) 535 | midi_data->fadeTime = MIDI_DURATION_INFINITE; 536 | else 537 | { 538 | if (effect->usb_duration > effect->usb_fadeTime) // add some safety and special case handling 539 | midi_data->fadeTime = UsbUint16ToMidiUint14(effect->usb_duration - effect->usb_fadeTime); 540 | else 541 | midi_data->fadeTime = midi_data->duration; 542 | } 543 | } 544 | 545 | // Gain and its effects (magnitude and envelope levels) 546 | boolean gain_changed = (gEffectStates[eid].usb_gain != data->gain); 547 | if (gain_changed) 548 | { 549 | // LogTextP(PSTR(" New gain:")); 550 | // LogBinary(&data->gain, 1); 551 | 552 | gEffectStates[eid].usb_gain = data->gain; 553 | midi_data->attackLevel = CalcGain(effect->usb_attackLevel, data->gain); 554 | midi_data->fadeLevel = CalcGain(effect->usb_fadeLevel, data->gain); 555 | 556 | if (is_periodic) 557 | { 558 | // Calculate min-max from magnitude and offset, since magnitude may be affected by gain we must calc them here too for periodic effects 559 | uint8_t magnitude = CalcGain(effect->usb_magnitude, effect->usb_gain); // already at MIDI-level i.e. 1/2 of USB level! 560 | midi_data->param1 = UsbInt8ToMidiInt14(effect->usb_offset + magnitude); // max 561 | midi_data->param2 = UsbInt8ToMidiInt14(effect->usb_offset - magnitude); // min 562 | if (effect->state & MEffectState_SentToJoystick) 563 | { 564 | FfbSendModify(eid, 0x74, midi_data->param1); 565 | FfbSendModify(eid, 0x78, midi_data->param2); 566 | } 567 | } 568 | else 569 | midi_data->magnitude = CalcGain(effect->usb_magnitude, data->gain); 570 | } 571 | 572 | // Send data to MIDI 573 | if (gEffectStates[eid].state & MEffectState_SentToJoystick) 574 | { // Send update 575 | FfbSendModify(eid, 0x48, midi_data->direction); 576 | FfbSendModify(eid, 0x60, midi_data->fadeTime); 577 | if (gain_changed) 578 | { 579 | FfbSendModify(eid, 0x6C, midi_data->fadeLevel); // might have changed due gain 580 | FfbSendModify(eid, 0x64, midi_data->attackLevel); // might have changed due gain 581 | if (!is_periodic) 582 | FfbSendModify(eid, 0x74, midi_data->magnitude); // might have changed due gain 583 | } 584 | } 585 | else 586 | { 587 | FfbSendSysEx((uint8_t*) midi_data, sizeof(FFP_MIDI_Effect_Basic)); 588 | 589 | effect->state |= MEffectState_SentToJoystick; 590 | } 591 | } 592 | break; 593 | case 8: // spring (midi: 0x0d) 594 | case 9: // damper (midi: 0x0e) 595 | case 10: // inertia (midi: 0x0f) 596 | { 597 | /* 598 | MIDI effect data: 599 | uint8_t command; // always 0x23 -- start counting checksum from here 600 | uint8_t waveForm; // 2=sine, 5=Square, 6=RampUp, 7=RampDown, 8=Triange, 0x12=Constant 601 | uint8_t unknown1; // ? always 0x7F 602 | uint16_t duration; // unit=2ms 603 | uint16_t unknown2; // ? always 0x0000 604 | uint16_t coeffAxis0; 605 | uint16_t coeffAxis1; 606 | uint16_t offsetAxis0; 607 | uint16_t offsetAxis1; 608 | */ 609 | // volatile FFP_MIDI_Effect_Spring_Inertia_Damper *midi_data = (FFP_MIDI_Effect_Spring_Inertia_Damper *) &gEffectStates[eid].data; 610 | midi_data_len = sizeof(FFP_MIDI_Effect_Spring_Inertia_Damper); 611 | 612 | // Send data to MIDI 613 | if (gEffectStates[eid].state & MEffectState_SentToJoystick) 614 | { // Send update 615 | } 616 | } 617 | break; 618 | case 11: // friction (midi: 0x10) 619 | { 620 | /* 621 | MIDI effect data: 622 | uint8_t command; // always 0x23 -- start counting checksum from here 623 | uint8_t waveForm; // 2=sine, 5=Square, 6=RampUp, 7=RampDown, 8=Triange, 0x12=Constant 624 | uint8_t unknown1; // ? always 0x7F 625 | uint16_t duration; // unit=2ms 626 | uint16_t unknown2; // ? always 0x0000 627 | uint16_t coeffAxis0; 628 | uint16_t coeffAxis1; 629 | */ 630 | // volatile FFP_MIDI_Effect_Friction *midi_data = (FFP_MIDI_Effect_Friction *) &gEffectStates[eid].data; 631 | midi_data_len = sizeof(FFP_MIDI_Effect_Friction); 632 | 633 | // Send data to MIDI 634 | if (gEffectStates[eid].state & MEffectState_SentToJoystick) 635 | { // Send update 636 | } 637 | } 638 | break; 639 | case 12: // custom (midi: ? does FFP support custom forces?) 640 | { 641 | } 642 | break; 643 | default: 644 | break; 645 | } 646 | 647 | // Send full effect data to MIDI if this effect has not been sent yet 648 | if (!(gEffectStates[eid].state & MEffectState_SentToJoystick)) 649 | { 650 | FfbSendSysEx((uint8_t*) common_midi_data, midi_data_len); 651 | 652 | gEffectStates[eid].state |= MEffectState_SentToJoystick; 653 | } 654 | 655 | } 656 | 657 | 658 | 659 | void FfbHandle_SetEnvelope(USB_FFBReport_SetEnvelope_Output_Data_t *data) 660 | { 661 | uint8_t eid = data->effectBlockIndex; 662 | volatile TEffectState *effect = &gEffectStates[eid]; 663 | 664 | /* LogTextP(PSTR("Set Envelope:")); 665 | LogBinaryLf(data, sizeof(USB_FFBReport_SetEnvelope_Output_Data_t)); 666 | LogTextP(PSTR(" id =")); LogBinaryLf(&eid, sizeof(eid)); 667 | LogTextP(PSTR(" attack=")); LogBinaryLf(&data->attackLevel, sizeof(data->attackLevel)); 668 | LogTextP(PSTR(" fade =")); LogBinaryLf(&data->fadeLevel, sizeof(data->fadeLevel)); 669 | LogTextP(PSTR(" attackTime=")); LogBinaryLf(&data->attackTime, sizeof(data->attackTime)); 670 | LogTextP(PSTR(" fadeTime =")); LogBinaryLf(&data->fadeTime, sizeof(data->fadeTime)); 671 | */ 672 | // FlushDebugBuffer(); 673 | 674 | /* 675 | USB effect data: 676 | uint8_t reportId; // =2 677 | uint8_t effectBlockIndex; // 1..40 678 | uint8_t attackLevel; 679 | uint8_t fadeLevel; 680 | uint16_t attackTime; // ms 681 | uint16_t fadeTime; // ms 682 | 683 | MIDI effect data: 684 | uint8_t command; // always 0x23 -- start counting checksum from here 685 | uint8_t waveForm; // 2=sine, 5=Square, 6=RampUp, 7=RampDown, 8=Triange, 0x12=Constant 686 | uint8_t unknown1; // ? always 0x7F 687 | uint16_t duration; // unit=2ms 688 | uint16_t unknown2; // ? always 0x0000 689 | uint16_t direction; 690 | uint8_t unknown3[5]; // ? always 7f 64 00 10 4e 691 | uint8_t attackLevel; 692 | uint16_t attackTime; 693 | uint8_t magnitude; 694 | uint16_t fadeTime; 695 | uint8_t fadeLevel; 696 | uint8_t waveLength; // 0x6F..0x01 => 1/Hz 697 | uint8_t unknown5; // ? always 0x00 698 | uint16_t param1; // Constant: positive=7f 00, negative=01 01, Other effects: 01 01 699 | uint16_t param2; // Constant: 00 00, Other effects 01 01 700 | */ 701 | volatile FFP_MIDI_Effect_Basic *midi_data = &effect->data; 702 | 703 | effect->usb_attackLevel = data->attackLevel; 704 | effect->usb_fadeLevel = data->fadeLevel; 705 | effect->usb_fadeTime = data->fadeTime; 706 | 707 | midi_data->attackLevel = CalcGain(data->attackLevel, effect->usb_gain); 708 | midi_data->fadeLevel = CalcGain(data->fadeLevel, effect->usb_gain); 709 | 710 | midi_data->attackTime = UsbUint16ToMidiUint14(data->attackTime); 711 | 712 | if (data->fadeTime == USB_DURATION_INFINITE) 713 | midi_data->fadeTime = MIDI_DURATION_INFINITE; 714 | else 715 | midi_data->fadeTime = UsbUint16ToMidiUint14(gEffectStates[eid].usb_duration - gEffectStates[eid].usb_fadeTime); 716 | 717 | if (gEffectStates[eid].state & MEffectState_SentToJoystick) 718 | { // Send update 719 | FfbSendModify(eid, 0x60, midi_data->fadeTime); 720 | FfbSendModify(eid, 0x5C, midi_data->attackTime); 721 | FfbSendModify(eid, 0x6C, midi_data->fadeLevel); 722 | FfbSendModify(eid, 0x64, midi_data->attackLevel); 723 | } 724 | } 725 | 726 | 727 | void FfbHandle_SetCondition(USB_FFBReport_SetCondition_Output_Data_t *data) 728 | { 729 | uint8_t eid = data->effectBlockIndex; 730 | volatile FFP_MIDI_Effect_Basic *common_midi_data = &gEffectStates[eid].data; 731 | 732 | 733 | /* LogTextP(PSTR("Set Condition:")); 734 | LogBinaryLf(data, sizeof(USB_FFBReport_SetCondition_Output_Data_t)); 735 | LogTextP(PSTR(" id =")); LogBinaryLf(&eid, sizeof(eid)); 736 | LogTextP(PSTR(" block =")); LogBinaryLf(&data->parameterBlockOffset, sizeof(data->parameterBlockOffset)); 737 | LogTextP(PSTR(" offset=")); LogBinaryLf(&data->cpOffset, sizeof(data->cpOffset)); 738 | LogTextP(PSTR(" coeff+=")); LogBinaryLf(&data->positiveCoefficient, sizeof(data->positiveCoefficient)); 739 | */ 740 | // FlushDebugBuffer(); 741 | 742 | /* 743 | USB effect data: 744 | uint8_t effectBlockIndex; // 1..40 745 | uint8_t parameterBlockOffset; // bits: 0..3=parameterBlockOffset, 4..5=instance1, 6..7=instance2 746 | int8_t cpOffset; // -128..127 747 | uint8_t positiveCoefficient; // 0..255 748 | 749 | MIDI effect data: 750 | uint16_t coeffAxis0; 751 | uint16_t coeffAxis1; 752 | uint16_t offsetAxis0; // not in friction 753 | uint16_t offsetAxis1; // not in friction 754 | */ 755 | 756 | switch (common_midi_data->waveForm) 757 | { 758 | case 0x0d: // spring (midi: 0x0d) 759 | case 0x0e: // damper (midi: 0x0e) 760 | case 0x0f: // inertia (midi: 0x0f) 761 | { 762 | volatile FFP_MIDI_Effect_Spring_Inertia_Damper *midi_data = (FFP_MIDI_Effect_Spring_Inertia_Damper *) &gEffectStates[eid].data; 763 | 764 | if (data->parameterBlockOffset == 0) 765 | { 766 | midi_data->coeffAxis0 = UsbInt8ToMidiInt14(data->positiveCoefficient); 767 | midi_data->offsetAxis0 = UsbInt8ToMidiInt14(data->cpOffset); 768 | } 769 | else 770 | { 771 | midi_data->coeffAxis1 = UsbInt8ToMidiInt14(data->positiveCoefficient); 772 | if (data->cpOffset == 0x80) 773 | midi_data->offsetAxis1 = 0x007f; 774 | else 775 | midi_data->offsetAxis1 = UsbInt8ToMidiInt14(-data->cpOffset); 776 | } 777 | 778 | // Send data to MIDI 779 | if (gEffectStates[eid].state & MEffectState_SentToJoystick) 780 | { // Send update 781 | if (data->parameterBlockOffset == 0) 782 | { 783 | FfbSendModify(eid, 0x48, midi_data->coeffAxis0); 784 | FfbSendModify(eid, 0x50, midi_data->offsetAxis0); 785 | } 786 | else 787 | { 788 | FfbSendModify(eid, 0x4C, midi_data->coeffAxis1); 789 | FfbSendModify(eid, 0x54, midi_data->offsetAxis1); 790 | } 791 | } 792 | } 793 | break; 794 | case 0x10: // friction (midi: 0x10) 795 | { 796 | volatile FFP_MIDI_Effect_Friction *midi_data = (FFP_MIDI_Effect_Friction *) &gEffectStates[eid].data; 797 | 798 | if (data->parameterBlockOffset == 0) 799 | midi_data->coeffAxis0 = UsbInt8ToMidiInt14(data->positiveCoefficient); 800 | else 801 | midi_data->coeffAxis1 = UsbInt8ToMidiInt14(data->positiveCoefficient); 802 | 803 | // Send data to MIDI 804 | if (gEffectStates[eid].state & MEffectState_SentToJoystick) 805 | { // Send update 806 | if (data->parameterBlockOffset == 0) 807 | FfbSendModify(eid, 0x48, midi_data->coeffAxis0); 808 | else 809 | FfbSendModify(eid, 0x4C, midi_data->coeffAxis1); 810 | } 811 | } 812 | break; 813 | default: 814 | break; 815 | } 816 | } 817 | 818 | 819 | 820 | void FfbHandle_SetPeriodic(USB_FFBReport_SetPeriodic_Output_Data_t *data) 821 | { 822 | uint8_t eid = data->effectBlockIndex; 823 | 824 | LogTextP(PSTR("Set Periodic:")); 825 | LogBinaryLf(data, sizeof(USB_FFBReport_SetPeriodic_Output_Data_t)); 826 | LogTextP(PSTR(" id=")); LogBinaryLf(&eid, sizeof(eid)); 827 | LogTextP(PSTR(" magnitude=")); LogBinaryLf(&data->magnitude, sizeof(data->magnitude)); 828 | LogTextP(PSTR(" offset =")); LogBinaryLf(&data->offset, sizeof(data->offset)); 829 | LogTextP(PSTR(" phase =")); LogBinaryLf(&data->phase, sizeof(data->phase)); 830 | LogTextP(PSTR(" period =")); LogBinaryLf(&data->period, sizeof(data->period)); 831 | // FlushDebugBuffer(); 832 | 833 | /* 834 | USB effect data: 835 | uint8_t reportId; // =4 836 | uint8_t effectBlockIndex; // 1..40 837 | uint8_t magnitude; 838 | int8_t offset; 839 | uint8_t phase; // 0..255 (=0..359, exp-2) 840 | uint16_t period; // 0..32767 ms 841 | 842 | MIDI effect data: 843 | 844 | Offset values other than zero do not work and thus it is ignored on FFP 845 | */ 846 | volatile TEffectState *effect = &gEffectStates[eid]; 847 | volatile FFP_MIDI_Effect_Basic *midi_data = &effect->data; 848 | 849 | effect->usb_magnitude = data->magnitude; 850 | 851 | midi_data->param1 = 0x007f; 852 | midi_data->param2 = 0x0101; 853 | 854 | // Calculate waveLength (in MIDI it is in units of 1/Hz and can have value 0x6F..0x01) 855 | if (data->period >= 1000) 856 | midi_data->waveLength = 0x01; 857 | else if (data->period <= 9) 858 | midi_data->waveLength = 0x6F; 859 | else 860 | midi_data->waveLength = (1000 / data->period) & 0x7F; 861 | 862 | // Check phase if relevant (+90 phase for sine makes it a cosine) 863 | if (midi_data->waveForm == 2 || midi_data->waveForm == 3) // sine 864 | { 865 | if (data->phase >= 32 && data->phase <= 224) 866 | { 867 | midi_data->waveForm = 3; // cosine 868 | } 869 | else 870 | { 871 | midi_data->waveForm = 2; // sine 872 | } 873 | 874 | // Calculate min-max from magnitude and offset 875 | uint8_t magnitude = CalcGain(data->magnitude, effect->usb_gain); // already at MIDI-level i.e. 1/2 of USB level! 876 | midi_data->param1 = UsbInt8ToMidiInt14(data->offset / 2 + magnitude); // max 877 | midi_data->param2 = UsbInt8ToMidiInt14(data->offset / 2 - magnitude); // min 878 | if (effect->state & MEffectState_SentToJoystick) 879 | { 880 | FfbSendModify(eid, 0x74, midi_data->param1); 881 | FfbSendModify(eid, 0x78, midi_data->param2); 882 | } 883 | } 884 | 885 | if (effect->state & MEffectState_SentToJoystick) 886 | { 887 | // FfbSendModify(eid, 0x74, midi_data->magnitude); // FFP does not actually support changing magnitude on-fly here 888 | FfbSendModify(eid, 0x70, midi_data->waveLength); 889 | } 890 | } 891 | 892 | 893 | 894 | void FfbHandle_SetConstantForce(USB_FFBReport_SetConstantForce_Output_Data_t *data) 895 | { 896 | uint8_t eid = data->effectBlockIndex; 897 | volatile TEffectState *effect_data = &gEffectStates[eid]; 898 | 899 | /* LogTextP(PSTR("Set Constant Force:")); 900 | LogBinaryLf(data, sizeof(USB_FFBReport_SetConstantForce_Output_Data_t)); 901 | LogTextP(PSTR(" id=")); LogBinaryLf(&eid, sizeof(eid)); 902 | LogTextP(PSTR(" magnitude=")); LogBinaryLf(&data->magnitude, sizeof(data->magnitude)); 903 | */ 904 | // FlushDebugBuffer(); 905 | /* 906 | USB data: 907 | uint8_t reportId; // =5 908 | uint8_t effectBlockIndex; // 1..40 909 | int16_t magnitude; // -255..255 910 | 911 | MIDI effect data: 912 | uint8_t command; // always 0x23 -- start counting checksum from here 913 | uint8_t waveForm; // 2=sine, 5=Square, 6=RampUp, 7=RampDown, 8=Triange, 0x12=Constant 914 | uint8_t unknown1; // ? always 0x7F 915 | uint16_t duration; // unit=2ms 916 | uint16_t unknown2; // ? always 0x0000 917 | uint16_t direction; 918 | uint8_t unknown3[5]; // ? always 7f 64 00 10 4e 919 | uint8_t attackLevel; 920 | uint16_t attackTime; 921 | uint8_t magnitude; 922 | uint16_t fadeTime; 923 | uint8_t fadeLevel; 924 | uint8_t waveLength; // 0x6F..0x01 => 1/Hz 925 | uint8_t unknown5; // ? always 0x00 926 | uint16_t param1; // Constant: positive=7f 00, negative=01 01, Other effects: 01 01 927 | uint16_t param2; // Constant: 00 00, Other effects 01 01 928 | */ 929 | volatile FFP_MIDI_Effect_Basic *midi_data = &effect_data->data; 930 | 931 | effect_data->usb_magnitude = data->magnitude; 932 | 933 | if (data->magnitude >= 0) 934 | { 935 | midi_data->magnitude = CalcGain(data->magnitude, effect_data->usb_gain); 936 | midi_data->param1 = 0x007f; 937 | } 938 | else 939 | { 940 | midi_data->magnitude = CalcGain(-(data->magnitude+1), effect_data->usb_gain); 941 | midi_data->param1 = 0x0101; 942 | } 943 | 944 | midi_data->param2 = 0x0000; 945 | 946 | if (gEffectStates[eid].state & MEffectState_SentToJoystick) 947 | { // Send update 948 | FfbSendModify(eid, 0x74, midi_data->magnitude); 949 | FfbSendModify(eid, 0x7C, midi_data->param1); 950 | } 951 | } 952 | 953 | void FfbHandle_SetRampForce(USB_FFBReport_SetRampForce_Output_Data_t *data) 954 | { 955 | uint8_t eid = data->effectBlockIndex; 956 | 957 | LogTextP(PSTR("Set Ramp Force:")); 958 | LogBinaryLf(data, sizeof(USB_FFBReport_SetRampForce_Output_Data_t)); 959 | LogTextP(PSTR(" id=")); LogBinaryLf(&eid, sizeof(eid)); 960 | LogTextP(PSTR(" start=")); LogBinaryLf(&data->start, sizeof(data->start)); 961 | LogTextP(PSTR(" end =")); LogBinaryLf(&data->end, sizeof(data->end)); 962 | // FlushDebugBuffer(); 963 | 964 | // FFP supports only ramp up from MIN to MAX and ramp down from MAX to MIN? 965 | /* 966 | USB effect data: 967 | uint8_t reportId; // =6 968 | uint8_t effectBlockIndex; // 1..40 969 | int8_t start; 970 | int8_t end; 971 | */ 972 | 973 | volatile FFP_MIDI_Effect_Basic *midi_data = &gEffectStates[eid].data; 974 | if (data->start < 0) 975 | midi_data->param1 = 0x0100 | (-(data->start+1)); 976 | else 977 | midi_data->param1 = data->start; 978 | 979 | midi_data->param2 = UsbInt8ToMidiInt14(data->end); 980 | 981 | if (gEffectStates[eid].state & MEffectState_SentToJoystick) 982 | { // Send update 983 | FfbSendModify(eid, 0x78, midi_data->param1); 984 | FfbSendModify(eid, 0x74, midi_data->param2); 985 | } 986 | } 987 | 988 | 989 | 990 | void FfbHandle_SetCustomForceData(USB_FFBReport_SetCustomForceData_Output_Data_t *data) 991 | { 992 | LogTextLf("Set Custom Force Data"); 993 | } 994 | 995 | 996 | 997 | void FfbHandle_SetDownloadForceSample(USB_FFBReport_SetDownloadForceSample_Output_Data_t *data) 998 | { 999 | LogTextLf("Set Download Force Sample"); 1000 | } 1001 | 1002 | 1003 | 1004 | void FfbHandle_EffectOperation(USB_FFBReport_EffectOperation_Output_Data_t *data) 1005 | { 1006 | uint8_t eid = data->effectBlockIndex; 1007 | 1008 | // LogTextP(PSTR("Effect Operation:")); 1009 | LogBinary(&eid, 1); 1010 | // LogBinary(data, sizeof(USB_FFBReport_EffectOperation_Output_Data_t)); 1011 | 1012 | if (eid == 0xFF) 1013 | eid = 0x7F; // All effects 1014 | 1015 | if (data->operation == 1) 1016 | { // Start 1017 | LogTextLfP(PSTR(" Start")); 1018 | LogBinaryLf(&eid, 1); 1019 | 1020 | StartEffect(data->effectBlockIndex); 1021 | if (!gDisabledEffects.effectId[eid]) 1022 | FfbSendEffectOper(eid, 0x20); 1023 | } 1024 | else if (data->operation == 2) 1025 | { // StartSolo 1026 | LogTextLfP(PSTR(" StartSolo")); 1027 | 1028 | // Stop all first 1029 | StopAllEffects(); 1030 | if (!gDisabledEffects.effectId[eid]) 1031 | FfbSendEffectOper(0x7F, 0x30); 1032 | 1033 | // Then start the given effect 1034 | StartEffect(data->effectBlockIndex); 1035 | 1036 | if (!gDisabledEffects.effectId[eid]) 1037 | FfbSendEffectOper(0x7F, 0x20); 1038 | } 1039 | else if (data->operation == 3) 1040 | { // Stop 1041 | LogTextLfP(PSTR(" Stop")); 1042 | 1043 | StopEffect(data->effectBlockIndex); 1044 | 1045 | if (!gDisabledEffects.effectId[eid]) 1046 | FfbSendEffectOper(eid, 0x30); 1047 | } 1048 | else 1049 | LogBinaryLf(&data->operation, sizeof(data->operation)); 1050 | } 1051 | 1052 | 1053 | void FfbHandle_BlockFree(USB_FFBReport_BlockFree_Output_Data_t *data) 1054 | { 1055 | uint8_t eid = data->effectBlockIndex; 1056 | 1057 | LogTextP(PSTR("Block Free: ")); 1058 | LogBinaryLf(&eid, 1); 1059 | 1060 | if (eid == 0xFF) 1061 | { // all effects 1062 | FreeAllEffects(); 1063 | FfbSendEffectOper(0x7F, 0x10); 1064 | } 1065 | else 1066 | { 1067 | FreeEffect(eid); 1068 | FfbSendEffectOper(eid, 0x10); 1069 | } 1070 | } 1071 | 1072 | 1073 | // Commands to disable and enable auto-center spring 1074 | uint8_t disableAutoCenterFfbData_1[] = 1075 | { 1076 | 0xc5, 0x01 1077 | }; 1078 | // Wait 75 ms 1079 | uint8_t disableAutoCenterFfbData_2[] = 1080 | { 1081 | 0xb5, 0x7c, 0x7f, 1082 | 0xa5, 0x7f, 0x00, 1083 | 0xc5, 0x06 1084 | }; 1085 | 1086 | uint8_t enableAutoCenterFfbData_1[] = 1087 | { 1088 | 0xc5, 0x01 1089 | }; 1090 | 1091 | uint8_t enableAutoCenterFfbData_2[] = 1092 | { 1093 | 0xc5, 0x07 1094 | }; 1095 | 1096 | uint8_t enableAutoCenterFfbData_3[] = 1097 | { 1098 | 0xb0, 0x40, 0x00, 1099 | 0xb1, 0x40, 0x00, 1100 | 0xb2, 0x40, 0x00, 1101 | 0xb3, 0x40, 0x00, 1102 | 0xb4, 0x40, 0x00, 1103 | 0xb5, 0x40, 0x00, 1104 | 0xb6, 0x40, 0x00, 1105 | 0xb7, 0x40, 0x00, 1106 | 0xb8, 0x40, 0x00, 1107 | 0xb9, 0x40, 0x00, 1108 | 0xba, 0x40, 0x00, 1109 | 0xbb, 0x40, 0x00, 1110 | 0xbc, 0x40, 0x00, 1111 | 0xbd, 0x40, 0x00, 1112 | 0xbe, 0x40, 0x00, 1113 | 0xbf, 0x40, 0x00, 1114 | 0xb0, 0x40, 0x00, 1115 | 0xb1, 0x40, 0x00, 1116 | 0xb2, 0x40, 0x00, 1117 | 0xb3, 0x40, 0x00, 1118 | 0xb4, 0x40, 0x00, 1119 | 0xb5, 0x40, 0x00, 1120 | 0xb6, 0x40, 0x00, 1121 | 0xb7, 0x40, 0x00, 1122 | 0xb8, 0x40, 0x00, 1123 | 0xb9, 0x40, 0x00, 1124 | 0xba, 0x40, 0x00, 1125 | 0xbb, 0x40, 0x00, 1126 | 0xbc, 0x40, 0x00, 1127 | 0xbd, 0x40, 0x00, 1128 | 0xbe, 0x40, 0x00, 1129 | 0xbf, 0x40, 0x00 1130 | }; 1131 | 1132 | 1133 | void FfbHandle_DeviceControl(USB_FFBReport_DeviceControl_Output_Data_t *data) 1134 | { 1135 | // LogTextP(PSTR("Device Control: ")); 1136 | 1137 | uint8_t control = data->control; 1138 | // 1=Enable Actuators, 2=Disable Actuators, 3=Stop All Effects, 4=Reset, 5=Pause, 6=Continue 1139 | 1140 | // PID State Report: 1141 | // uint8_t reportId; // =2 1142 | // uint8_t status; // Bits: 0=Device Paused,1=Actuators Enabled,2=Safety Switch,3=Actuator Override Switch,4=Actuator Power 1143 | // uint8_t effectBlockIndex; // Bit7=Effect Playing, Bit0..7=EffectId (1..40) 1144 | 1145 | pidState.reportId = 2; 1146 | pidState.status |= 1 << 2; 1147 | pidState.status |= 1 << 4; 1148 | pidState.effectBlockIndex = 0; 1149 | 1150 | if (control == 0x01) 1151 | { 1152 | LogTextLf("Disable Actuators"); 1153 | pidState.status = (pidState.status & 0xFE); 1154 | } 1155 | else if (control == 0x02) 1156 | { 1157 | LogTextLf("Enable Actuators"); 1158 | pidState.status |= 1 << 2; 1159 | } 1160 | else if (control == 0x03) 1161 | { 1162 | // Stop all effects (e.g. FFB-application to foreground) 1163 | LogTextLf("Stop All Effects"); 1164 | 1165 | // Disable auto-center spring and stop all effects 1166 | // ???? The below would take too long? 1167 | FfbSendData(disableAutoCenterFfbData_1, sizeof(disableAutoCenterFfbData_1)); 1168 | _delay_ms(35); 1169 | _delay_ms(40); 1170 | FfbSendData(disableAutoCenterFfbData_2, sizeof(disableAutoCenterFfbData_2)); 1171 | 1172 | pidState.effectBlockIndex = 0; 1173 | } 1174 | else if (control == 0x04) 1175 | { 1176 | LogTextLf("Reset"); 1177 | // Reset (e.g. FFB-application out of focus) 1178 | // Enable auto-center spring and stop all effects 1179 | FfbSendData(enableAutoCenterFfbData_1, sizeof(enableAutoCenterFfbData_1)); 1180 | _delay_ms(35); 1181 | _delay_ms(40); 1182 | /* FfbSendData(enableAutoCenterFfbData_2, sizeof(enableAutoCenterFfbData_2)); 1183 | _delay_ms(10); 1184 | FfbSendData(enableAutoCenterFfbData_3, sizeof(enableAutoCenterFfbData_3));*/ 1185 | FreeAllEffects(); 1186 | // FfbSendEffectOper(0x7F, 0x10); // remove all 1187 | } 1188 | else if (control == 0x05) 1189 | { 1190 | LogTextLf("Pause"); 1191 | } 1192 | else if (control == 0x06) 1193 | { 1194 | LogTextLf("Continue"); 1195 | } 1196 | else if (control & (0xFF-0x3F)) 1197 | { 1198 | LogTextP(PSTR("Other ")); 1199 | LogBinaryLf(&data->control, 1); 1200 | } 1201 | 1202 | // Send response 1203 | 1204 | } 1205 | 1206 | 1207 | 1208 | void 1209 | FfbHandle_DeviceGain(USB_FFBReport_DeviceGain_Output_Data_t *data) 1210 | { 1211 | LogTextP(PSTR("Device Gain: ")); 1212 | LogBinaryLf(&data->gain, 1); 1213 | } 1214 | 1215 | 1216 | void 1217 | FfbHandle_SetCustomForce(USB_FFBReport_SetCustomForce_Output_Data_t *data) 1218 | { 1219 | LogTextLf("Set Custom Force"); 1220 | // LogBinary(&data, sizeof(USB_FFBReport_SetCustomForce_Output_Data_t)); 1221 | } 1222 | 1223 | 1224 | //------------------------------------------------------------------------------ 1225 | // Startup MIDI data procedure 1226 | uint8_t startupFfbData_0[] = 1227 | { 1228 | 0xc5, 0x01 // 0x01 1229 | }; 1230 | // Wait for 10-20 ms (20) 1231 | uint8_t startupFfbData_1[] = 1232 | { 1233 | 0xf0, 1234 | 0x00, 0x01, 0x0a, 0x01, 0x10, 0x05, 0x6b, // ???? - reset all? 1235 | 0xf7 1236 | }; 1237 | // Wait for 56 ms 1238 | uint8_t startupFfbData_2[] = 1239 | { 1240 | 0xb5, 0x40, 0x7f, // (Modify, 0x7f) 1241 | 0xa5, 0x72, 0x57, // offset 0x72 := 0x57 1242 | 0xb5, 0x44, 0x7f, 1243 | 0xa5, 0x3c, 0x43, 1244 | 0xb5, 0x48, 0x7f, 1245 | 0xa5, 0x7e, 0x00, 1246 | 0xb5, 0x4c, 0x7f, 1247 | 0xa5, 0x04, 0x00, 1248 | 0xb5, 0x50, 0x7f, 1249 | 0xa5, 0x02, 0x00, 1250 | 0xb5, 0x54, 0x7f, 1251 | 0xa5, 0x02, 0x00, 1252 | 0xb5, 0x58, 0x7f, 1253 | 0xa5, 0x00, 0x7e, 1254 | 0xb5, 0x5c, 0x7f, 1255 | 0xa5, 0x3c, 0x00, 1256 | 0xb5, 0x60, 0x7f 1257 | }; 1258 | 1259 | uint8_t startupFfbData_3[] = 1260 | { 1261 | 0xa5, 0x14, 0x65, 1262 | 0xb5, 0x64, 0x7f, 1263 | 0xa5, 0x7e, 0x6b, 1264 | 0xb5, 0x68, 0x7f, 1265 | 0xa5, 0x36, 0x00, 1266 | 0xb5, 0x6c, 0x7f, 1267 | 0xa5, 0x28, 0x00, 1268 | 0xb5, 0x70, 0x7f, 1269 | 0xa5, 0x66, 0x4c, 1270 | 0xb5, 0x74, 0x7f, 1271 | 0xa5, 0x7e, 0x01, 1272 | 0xc5, 0x01 1273 | }; 1274 | // Wait for 69 ms 1275 | uint8_t startupFfbData_4[] = 1276 | { 1277 | 0xb5, 0x7c, 0x7f, 1278 | 0xa5, 0x7f, 0x00, 1279 | 0xc5, 0x06 1280 | }; 1281 | 1282 | 1283 | //------------------------------------------------------------------------------ 1284 | // Trigger the stick 1285 | 1286 | #define X1_pull() __WRAP__( { \ 1287 | clr_bit( TRGDDR, TRGX1BIT ) ; \ 1288 | clr_bit( TRGDDR, TRGY2BIT ) ; \ 1289 | } ) 1290 | #define X1_rel() __WRAP__( { \ 1291 | set_bit( TRGDDR, TRGX1BIT ) ; \ 1292 | set_bit( TRGDDR, TRGY2BIT ) ; \ 1293 | } ) 1294 | 1295 | 1296 | static void FA_NOINLINE( PulseX1 ) ( void ) 1297 | { 1298 | X1_pull() ; 1299 | 1300 | _delay_us(30); 1301 | _delay_us(20); 1302 | 1303 | X1_rel() ; 1304 | 1305 | _delay_us(20); 1306 | _delay_us(30); 1307 | _delay_us(20); 1308 | _delay_us(30); 1309 | 1310 | _delay_us(20); 1311 | _delay_us(30); 1312 | 1313 | } 1314 | 1315 | void WaitMs(int ms) 1316 | { 1317 | wdt_reset() ; 1318 | 1319 | int left = ms; 1320 | while (left > 0) 1321 | { 1322 | if (left > 15) 1323 | { 1324 | _delay_ms(15); 1325 | left = left - 15; 1326 | } 1327 | else 1328 | { 1329 | _delay_ms(left); 1330 | return; 1331 | } 1332 | } 1333 | } 1334 | 1335 | 1336 | void FfbSendEnableInterrupts(void) 1337 | { 1338 | WaitMs(100); 1339 | 1340 | // -- Read 1341 | PulseX1(); 1342 | 1343 | _delay_ms(7); 1344 | 1345 | // --- 4-pulse 1346 | 1347 | PulseX1(); 1348 | PulseX1(); 1349 | PulseX1(); 1350 | PulseX1(); 1351 | 1352 | WaitMs(35); 1353 | 1354 | // --- 3-pulse 1355 | PulseX1(); 1356 | PulseX1(); 1357 | PulseX1(); 1358 | 1359 | WaitMs(14); 1360 | 1361 | // --- 2-pulse 1362 | PulseX1(); 1363 | PulseX1(); 1364 | 1365 | // delay of 78 ms 1366 | 1367 | WaitMs(78); 1368 | 1369 | // ----------------------- 1370 | 1371 | // --- 2-pulse 1372 | PulseX1(); 1373 | PulseX1(); 1374 | 1375 | _delay_ms(4); 1376 | 1377 | // --- 3-pulse 1378 | PulseX1(); 1379 | PulseX1(); 1380 | PulseX1(); 1381 | 1382 | WaitMs(59); 1383 | 1384 | // --- 2-pulse 1385 | PulseX1(); 1386 | PulseX1(); 1387 | 1388 | // -- START MIDI 1389 | FfbSendData(startupFfbData_0, sizeof(startupFfbData_0)); // Program change 1390 | 1391 | WaitMs(20); 1392 | 1393 | FfbSendData(startupFfbData_1, sizeof(startupFfbData_1)); // Init 1394 | 1395 | WaitMs(57); 1396 | 1397 | FfbSendData(startupFfbData_2, sizeof(startupFfbData_2)); // Initialize effects data memory 1398 | FfbSendData(startupFfbData_3, sizeof(startupFfbData_3)); // Initialize effects data memory and enable auto-center spring 1399 | 1400 | WaitMs(70); 1401 | 1402 | FfbSendData(startupFfbData_4, sizeof(startupFfbData_4)); // Disable auto-center...? 1403 | 1404 | WaitMs(70); 1405 | } 1406 | 1407 | 1408 | 1409 | // Initializes and enables MIDI to joystick using USART1 TX 1410 | void FfbInitMidi() 1411 | { 1412 | // Initialize some states 1413 | memset((void*) &gDisabledEffects, 0, sizeof(gDisabledEffects)); 1414 | 1415 | // Check TX-pin (PD3) settings 1416 | DDRD = DDRD | 0b00001000; 1417 | 1418 | // Set baud rate 1419 | UCSR1A = 0; 1420 | UBRR1 = ((F_CPU/(31250ul<<4))-1); 1421 | 1422 | // Set frame format to 8 data bits, no parity, 1 stop bit, 1 start bit 1423 | UCSR1C = (1<<7)|(1< Midi:")); LogBinaryLf(data, len); 1445 | }*/ 1446 | 1447 | uint16_t i = 0; 1448 | for (i = 0; i < len; i++) 1449 | FfbSendByte(data[i]); 1450 | } 1451 | 1452 | // ---------------------------------------------- 1453 | // Ring buffer for sending MIDI data to joystick 1454 | // ---------------------------------------------- 1455 | 1456 | // Buffer for sending data to MIDI 1457 | //#define MIDI_BUFFER_SIZE 128 1458 | 1459 | #ifndef MIDI_BUFFER_SIZE 1460 | 1461 | // Non-buffered MIDI 1462 | void FfbSendByte(uint8_t data) 1463 | { 1464 | // Wait if a byte is being transmitted 1465 | while((UCSR1A & (1<= MIDI_BUFFER_SIZE) 1491 | gMidiBufferHead = gMidiBuffer; 1492 | 1493 | sei(); 1494 | } 1495 | 1496 | 1497 | ISR(USART1_UDRE_vect) 1498 | { 1499 | cli(); 1500 | 1501 | uint8_t i; 1502 | 1503 | if (gMidiBufferHead == gMidiBufferTail) 1504 | { 1505 | // Buffer is empty, disable transmit interrupt 1506 | UCSR1B = (1<= MIDI_BUFFER_SIZE) i = 0; 1512 | UDR1 = gMidiBuffer[i]; 1513 | gMidiBufferTail = i; 1514 | } 1515 | 1516 | sei(); 1517 | } 1518 | #endif // MIDI_BUFFER_SIZE 1519 | 1520 | // ---------------------------------------------- 1521 | // Debug and other settings 1522 | // ---------------------------------------------- 1523 | 1524 | 1525 | // Send "enable FFB" to joystick 1526 | void FfbSendEnable() 1527 | { 1528 | } 1529 | 1530 | // Send "disable FFB" to joystick 1531 | void FfbSendDisable() 1532 | { 1533 | } 1534 | 1535 | /* 1536 | typedef struct { 1537 | uint8_t state; // see constants 1538 | uint16_t usb_duration, usb_fadeTime; // used to calculate fadeTime to MIDI, since in USB it is given as time difference from the end while in MIDI it is given as time from start 1539 | // These are used to calculate effects of USB gain to MIDI data 1540 | uint8_t usb_gain, usb_offset, usb_attackLevel, usb_fadeLevel; 1541 | uint8_t usb_magnitude; 1542 | FFP_MIDI_Effect_Basic data; // For FFP, this is enough for all types of effects - cast for other effect types when necessary 1543 | } TEffectState; 1544 | 1545 | const uint8_t MEffectState_Allocated = 0x01; 1546 | const uint8_t MEffectState_Playing = 0x02; 1547 | const uint8_t MEffectState_SentToJoystick = 0x04; 1548 | */ 1549 | 1550 | uint8_t FfbDebugListEffects(uint8_t *index) 1551 | { 1552 | if (*index == 0) 1553 | *index = 2; 1554 | 1555 | // if (*index >= nextEID) 1556 | if (*index >= MAX_EFFECTS) 1557 | return 0; 1558 | 1559 | TEffectState *e = (TEffectState*) &gEffectStates[*index]; 1560 | 1561 | LogBinary(index, 1); 1562 | if (e->state == MEffectState_Allocated) 1563 | LogTextP(PSTR(" Allocated")); 1564 | else if (e->state == MEffectState_Playing) 1565 | LogTextP(PSTR(" Playing\n")); 1566 | else if (e->state == MEffectState_SentToJoystick) 1567 | LogTextP(PSTR(" Sent")); 1568 | else 1569 | LogTextP(PSTR(" Free")); 1570 | 1571 | if (gDisabledEffects.effectId[*index]) 1572 | LogTextP(PSTR(" (Disabled)\n")); 1573 | else 1574 | LogTextP(PSTR(" (Enabled)\n")); 1575 | 1576 | if (e->state) 1577 | { 1578 | LogTextP(PSTR(" duration=")); 1579 | LogBinary(&e->usb_duration, 2); 1580 | LogTextP(PSTR("\n fadeTime=")); 1581 | LogBinary(&e->usb_fadeTime, 2); 1582 | LogTextP(PSTR("\n gain=")); 1583 | LogBinary(&e->usb_gain, 1); 1584 | } 1585 | 1586 | *index = *index + 1; 1587 | 1588 | return 1; 1589 | } 1590 | 1591 | 1592 | void FfbEnableSprings(uint8_t inEnable) 1593 | { 1594 | gDisabledEffects.springs = !inEnable; 1595 | } 1596 | 1597 | void FfbEnableConstants(uint8_t inEnable) 1598 | { 1599 | gDisabledEffects.constants = !inEnable; 1600 | } 1601 | 1602 | void FfbEnableTriangles(uint8_t inEnable) 1603 | { 1604 | gDisabledEffects.triangles = !inEnable; 1605 | } 1606 | 1607 | void FfbEnableSines(uint8_t inEnable) 1608 | { 1609 | gDisabledEffects.sines = !inEnable; 1610 | } 1611 | 1612 | void FfbEnableEffectId(uint8_t inId, uint8_t inEnable) 1613 | { 1614 | gDisabledEffects.effectId[inId] = !inEnable; 1615 | 1616 | if (gEffectStates[inId].state == MEffectState_Playing) 1617 | { 1618 | LogTextP(PSTR("Stop manual:")); 1619 | LogBinaryLf(&inId, 1); 1620 | StopEffect(inId); 1621 | FfbSendEffectOper(inId, 0x30); 1622 | } 1623 | } 1624 | -------------------------------------------------------------------------------- /ffb.h: -------------------------------------------------------------------------------- 1 | /* 2 | Force Feedback Joystick 3 | USB HID descriptors for a force feedback joystick. 4 | 5 | This code is for Microsoft Sidewinder Force Feedback Pro joystick. 6 | with some room for additional extra controls. 7 | 8 | Copyright 2012 Tero Loimuneva (tloimu [at] gmail [dot] com) 9 | MIT License. 10 | 11 | Permission to use, copy, modify, distribute, and sell this 12 | software and its documentation for any purpose is hereby granted 13 | without fee, provided that the above copyright notice appear in 14 | all copies and that both that the copyright notice and this 15 | permission notice and warranty disclaimer appear in supporting 16 | documentation, and that the name of the author not be used in 17 | advertising or publicity pertaining to distribution of the 18 | software without specific, written prior permission. 19 | 20 | The author disclaim all warranties with regard to this 21 | software, including all implied warranties of merchantability 22 | and fitness. In no event shall the author be liable for any 23 | special, indirect or consequential damages or any damages 24 | whatsoever resulting from loss of use, data or profits, whether 25 | in an action of contract, negligence or other tortious action, 26 | arising out of or in connection with the use or performance of 27 | this software. 28 | */ 29 | 30 | #include 31 | #include "WProgram.h" 32 | 33 | /* Type Defines: */ 34 | /** Type define for the joystick HID report structure, for creating and sending HID reports to the host PC. 35 | * This mirrors the layout described to the host in the HID report descriptor, in Descriptors.c. 36 | */ 37 | 38 | // Maximum number of parallel effects in memory 39 | #define MAX_EFFECTS 20 40 | 41 | // ---- Input 42 | 43 | typedef struct 44 | { 45 | uint8_t reportId; // =2 46 | uint8_t status; // Bits: 0=Device Paused,1=Actuators Enabled,2=Safety Switch,3=Actuator Override Switch,4=Actuator Power 47 | uint8_t effectBlockIndex; // Bit7=Effect Playing, Bit0..7=EffectId (1..40) 48 | } USB_FFBReport_PIDStatus_Input_Data_t; 49 | 50 | // ---- Output 51 | 52 | typedef struct 53 | { // FFB: Set Effect Output Report 54 | uint8_t reportId; // =1 55 | uint8_t effectBlockIndex; // 1..40 56 | uint8_t effectType; // 1..12 (effect usages: 26,27,30,31,32,33,34,40,41,42,43,28) 57 | uint16_t duration; // 0..32767 ms 58 | uint16_t triggerRepeatInterval; // 0..32767 ms 59 | uint16_t samplePeriod; // 0..32767 ms 60 | uint8_t gain; // 0..255 (physical 0..10000) 61 | uint8_t triggerButton; // button ID (0..8) 62 | uint8_t enableAxis; // bits: 0=X, 1=Y, 2=DirectionEnable 63 | uint8_t directionX; // angle (0=0 .. 255=360deg) 64 | uint8_t directionY; // angle (0=0 .. 255=360deg) 65 | // uint16_t startDelay; // 0..32767 ms 66 | } USB_FFBReport_SetEffect_Output_Data_t; 67 | 68 | typedef struct 69 | { // FFB: Set Envelope Output Report 70 | uint8_t reportId; // =2 71 | uint8_t effectBlockIndex; // 1..40 72 | uint8_t attackLevel; 73 | uint8_t fadeLevel; 74 | uint16_t attackTime; // ms 75 | uint16_t fadeTime; // ms 76 | } USB_FFBReport_SetEnvelope_Output_Data_t; 77 | 78 | typedef struct 79 | { // FFB: Set Condition Output Report 80 | uint8_t reportId; // =3 81 | uint8_t effectBlockIndex; // 1..40 82 | uint8_t parameterBlockOffset; // bits: 0..3=parameterBlockOffset, 4..5=instance1, 6..7=instance2 83 | uint8_t cpOffset; // 0..255 84 | int8_t positiveCoefficient; // -128..127 85 | // int8_t negativeCoefficient; // -128..127 86 | // uint8_t positiveSaturation; // -128..127 87 | // uint8_t negativeSaturation; // -128..127 88 | // uint8_t deadBand; // 0..255 89 | } USB_FFBReport_SetCondition_Output_Data_t; 90 | 91 | typedef struct 92 | { // FFB: Set Periodic Output Report 93 | uint8_t reportId; // =4 94 | uint8_t effectBlockIndex; // 1..40 95 | uint8_t magnitude; 96 | int8_t offset; 97 | uint8_t phase; // 0..255 (=0..359, exp-2) 98 | uint16_t period; // 0..32767 ms 99 | } USB_FFBReport_SetPeriodic_Output_Data_t; 100 | 101 | typedef struct 102 | { // FFB: Set ConstantForce Output Report 103 | uint8_t reportId; // =5 104 | uint8_t effectBlockIndex; // 1..40 105 | int16_t magnitude; // -255..255 106 | } USB_FFBReport_SetConstantForce_Output_Data_t; 107 | 108 | typedef struct 109 | { // FFB: Set RampForce Output Report 110 | uint8_t reportId; // =6 111 | uint8_t effectBlockIndex; // 1..40 112 | int8_t start; 113 | int8_t end; 114 | } USB_FFBReport_SetRampForce_Output_Data_t; 115 | 116 | typedef struct 117 | { // FFB: Set CustomForceData Output Report 118 | uint8_t reportId; // =7 119 | uint8_t effectBlockIndex; // 1..40 120 | uint8_t dataOffset; 121 | int8_t data[12]; 122 | } USB_FFBReport_SetCustomForceData_Output_Data_t; 123 | 124 | typedef struct 125 | { // FFB: Set DownloadForceSample Output Report 126 | uint8_t reportId; // =8 127 | int8_t x; 128 | int8_t y; 129 | } USB_FFBReport_SetDownloadForceSample_Output_Data_t; 130 | 131 | typedef struct 132 | { // FFB: Set EffectOperation Output Report 133 | uint8_t reportId; // =10 134 | uint8_t effectBlockIndex; // 1..40 135 | uint8_t operation; // 1=Start, 2=StartSolo, 3=Stop 136 | uint8_t loopCount; 137 | } USB_FFBReport_EffectOperation_Output_Data_t; 138 | 139 | typedef struct 140 | { // FFB: Block Free Output Report 141 | uint8_t reportId; // =11 142 | uint8_t effectBlockIndex; // 1..40 143 | } USB_FFBReport_BlockFree_Output_Data_t; 144 | 145 | typedef struct 146 | { // FFB: Device Control Output Report 147 | uint8_t reportId; // =12 148 | uint8_t control; // 1=Enable Actuators, 2=Disable Actuators, 4=Stop All Effects, 8=Reset, 16=Pause, 32=Continue 149 | } USB_FFBReport_DeviceControl_Output_Data_t; 150 | 151 | typedef struct 152 | { // FFB: DeviceGain Output Report 153 | uint8_t reportId; // =13 154 | uint8_t gain; 155 | } USB_FFBReport_DeviceGain_Output_Data_t; 156 | 157 | typedef struct 158 | { // FFB: Set Custom Force Output Report 159 | uint8_t reportId; // =14 160 | uint8_t effectBlockIndex; // 1..40 161 | uint8_t sampleCount; 162 | uint16_t samplePeriod; // 0..32767 ms 163 | } USB_FFBReport_SetCustomForce_Output_Data_t; 164 | 165 | // ---- Features 166 | 167 | typedef struct 168 | { // FFB: Create New Effect Feature Report 169 | uint8_t reportId; // =1 170 | uint8_t effectType; // Enum (1..12): ET 26,27,30,31,32,33,34,40,41,42,43,28 171 | uint16_t byteCount; // 0..511 172 | } USB_FFBReport_CreateNewEffect_Feature_Data_t; 173 | 174 | typedef struct 175 | { // FFB: PID Block Load Feature Report 176 | uint8_t reportId; // =2 177 | uint8_t effectBlockIndex; // 1..40 178 | uint8_t loadStatus; // 1=Success,2=Full,3=Error 179 | uint16_t ramPoolAvailable; // =0 or 0xFFFF? 180 | } USB_FFBReport_PIDBlockLoad_Feature_Data_t; 181 | 182 | typedef struct 183 | { // FFB: PID Pool Feature Report 184 | uint8_t reportId; // =3 185 | uint16_t ramPoolSize; // ? 186 | uint8_t maxSimultaneousEffects; // ?? 40? 187 | uint8_t memoryManagement; // Bits: 0=DeviceManagedPool, 1=SharedParameterBlocks 188 | } USB_FFBReport_PIDPool_Feature_Data_t; 189 | 190 | 191 | // ---------------------------------- 192 | // Microsoft Sidewinder Force Feedback Pro FFB structures 193 | 194 | typedef struct 195 | { 196 | uint8_t command; // always 0x23 -- start counting checksum from here 197 | uint8_t waveForm; // 2=sine, 5=Square, 6=RampUp, 7=RampDown, 8=Triange, 0x12=Constant 198 | uint8_t unknown1; // ? always 0x7F 199 | uint16_t duration; // unit=2ms 200 | uint16_t unknown2; // ? always 0x0000 201 | uint16_t direction; 202 | uint8_t unknown3[5]; // ? always 7f 64 00 10 4e 203 | uint8_t attackLevel; 204 | uint16_t attackTime; 205 | uint8_t magnitude; 206 | uint16_t fadeTime; 207 | uint8_t fadeLevel; 208 | uint8_t waveLength; // 0x6F..0x01 => 1/Hz 209 | uint8_t unknown5; // ? always 0x00 210 | uint16_t param1; // Varies by effect type; Constant: positive=7f 00, negative=01 01, Other effects: 01 01 211 | uint16_t param2; // Varies by effect type; Constant: 00 00, Other effects 01 01 212 | } FFP_MIDI_Effect_Basic; 213 | 214 | typedef struct 215 | { 216 | uint8_t command; // always 0x23 -- start counting checksum from here 217 | uint8_t waveForm; // 0xd=Spring, 0x0e=Damper, 0xf=Inertia 218 | uint8_t unknown1; // ? always 0x7F 219 | uint16_t duration; // unit=2ms 220 | uint16_t unknown2; // ? always 0x0000 221 | uint16_t coeffAxis0; 222 | uint16_t coeffAxis1; 223 | uint16_t offsetAxis0; 224 | uint16_t offsetAxis1; 225 | } FFP_MIDI_Effect_Spring_Inertia_Damper; 226 | 227 | 228 | typedef struct 229 | { 230 | uint8_t command; // always 0x23 -- start counting checksum from here 231 | uint8_t waveForm; // 0x10=Friction 232 | uint8_t unknown1; // ? always 0x7F 233 | uint16_t duration; // unit=2ms 234 | uint16_t unknown2; // ? always 0x0000 235 | uint16_t coeffAxis0; 236 | uint16_t coeffAxis1; 237 | } FFP_MIDI_Effect_Friction; 238 | 239 | // Lengths of each report type 240 | extern const uint16_t OutReportSize[]; 241 | 242 | // Handles Force Feeback data manipulation from USB reports to joystick's MIDI channel 243 | 244 | // Initializes and enables MIDI to joystick using USART1 TX 245 | void FfbInitMidi(void); 246 | 247 | // Send "enable FFB" to joystick 248 | void FfbSendEnable(void); 249 | 250 | // Send "disable FFB" to joystick 251 | void FfbSendDisable(void); 252 | 253 | // Handle incoming data from USB 254 | void FfbOnUsbData(uint8_t *data, uint16_t len); 255 | 256 | // Handle incoming feature requests 257 | void FfbOnCreateNewEffect(USB_FFBReport_CreateNewEffect_Feature_Data_t* inData, USB_FFBReport_PIDBlockLoad_Feature_Data_t *outData); 258 | void FfbOnPIDPool(USB_FFBReport_PIDPool_Feature_Data_t *data); 259 | 260 | // Utility to wait any amount of milliseconds. 261 | // Resets watchdog at the beginning of each call so 262 | // if watchdog is enabled, don't make too long waits 263 | // in once call. 264 | void WaitMs(int ms); 265 | 266 | // Send raw data to the 267 | void FfbSendData(uint8_t *data, uint16_t len); 268 | 269 | //these functions weren't in header before 270 | void FfbSendSysEx(void *midi_data, uint16_t len); 271 | void FfbSendEffectOper(uint8_t effectId, uint8_t operation); 272 | uint16_t UsbUint16ToMidiUint14(uint16_t inUsbValue); 273 | void FfbSendModify(uint8_t effectId, uint8_t address, uint16_t value); 274 | 275 | // Debugging 276 | // should be pointer to an index variable whose value should be set to 0 to start iterating. 277 | // Returns 0 when no more effects 278 | uint8_t FfbDebugListEffects(uint8_t *index); 279 | 280 | // Effect manipulations 281 | 282 | typedef struct 283 | { 284 | uint8_t midi; // disables all MIDI-traffic 285 | uint8_t springs; 286 | uint8_t constants; 287 | uint8_t triangles; 288 | uint8_t sines; 289 | uint8_t effectId[MAX_EFFECTS]; 290 | } TDisabledEffectTypes; 291 | 292 | extern volatile TDisabledEffectTypes gDisabledEffects; 293 | 294 | void FfbEnableSprings(uint8_t inEnable); 295 | void FfbEnableConstants(uint8_t inEnable); 296 | void FfbEnableTriangles(uint8_t inEnable); 297 | void FfbEnableSines(uint8_t inEnable); 298 | void FfbEnableEffectId(uint8_t inId, uint8_t inEnable); 299 | 300 | -------------------------------------------------------------------------------- /includes.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * File Name : includes.h 3 | * Project : Generic AVR based 4 | * Date : 2009/06/22 5 | * Version : 1.0 6 | * Target MCU : AVR 7 | * Tool Chain : Atmel AVR Studio 4.18 716 / WinAVR 20100110 8 | * Author : Detlef Mueller 9 | * detlef@gmail.com 10 | * Release Notes: 11 | * 12 | * $Id: includes.h 1.5 2010/04/23 05:30:16 Detlef Exp Detlef $ 13 | ******************************************************************************/ 14 | 15 | #ifndef __includes_h__ 16 | #define __includes_h__ 17 | 18 | //------------------------------------------------------------------------------ 19 | 20 | #if defined(__AVR_AT90USB162__) || defined(__AVR_AT90USB82__) 21 | #define __AVR_AT90USBX2__ 22 | #elif defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) 23 | #define __AVR_ATmegaXU4__ 24 | #elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) 25 | #define __AVR_AT90USBX6__ 26 | #else 27 | #error "Unsupported device" 28 | #endif 29 | 30 | //------------------------------------------------------------------------------ 31 | 32 | #ifdef __ASSEMBLER__ 33 | 34 | //------------------------------------------------------------------------------ 35 | 36 | #define __SFR_OFFSET 0 37 | //#define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */ 38 | 39 | #include 40 | 41 | //------------------------------------------------------------------------------ 42 | 43 | #else // ! __ASSEMBLER__ 44 | 45 | //------------------------------------------------------------------------------ 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | //------------------------------------------------------------------------------ 53 | 54 | #define set_bit( sfr, bit ) (_SFR_BYTE(sfr) |= _BV(bit)) 55 | #define clr_bit( sfr, bit ) (_SFR_BYTE(sfr) &= ~_BV(bit)) 56 | #define tog_bit( sfr, bit ) (_SFR_BYTE(sfr) ^= _BV(bit)) 57 | 58 | #define set_bits( sfr, msk ) (_SFR_BYTE(sfr) |= (msk)) 59 | #define clr_bits( sfr, msk ) (_SFR_BYTE(sfr) &= ~(msk)) 60 | #define tog_bits( sfr, msk ) (_SFR_BYTE(sfr) ^= (msk)) 61 | 62 | #define bits_are_set( n, msk ) (((n) & (msk)) == (msk)) 63 | #define bits_are_clear( n, msk ) (!((n) & (msk))) 64 | 65 | //------------------------------------------------------------------------------ 66 | 67 | #define __FA__( _func, ... ) __attribute__((__VA_ARGS__)) _func 68 | 69 | #define FA_NAKED( _f ) __FA__( _f, __naked__ ) 70 | #define FA_NORETURN( _f ) __FA__( _f, __noreturn__ ) 71 | #define FA_NOINLINE( _f ) __FA__( _f, __noinline__ ) 72 | #define FA_NOINRET( _f ) __FA__( _f, __noinline__,__noreturn__ ) 73 | #define FA_INIT3( _f ) __FA__( _f, __used__,__naked__,__section__(".init3") ) 74 | 75 | #define VA_PROGMEM( _v ) _v PROGMEM 76 | #define VA_NOINIT( _v ) _v __attribute__((__section__(".noinit"))) 77 | 78 | #define TA_PROGMEM( _t ) PROGMEM _t 79 | 80 | //------------------------------------------------------------------------------ 81 | 82 | #define __WRAP__( _c ) do _c while (0) 83 | 84 | //------------------------------------------------------------------------------ 85 | 86 | #define CRITICAL_VAR() uint8_t __sSREG 87 | #define ENTER_CRITICAL() __WRAP__( { __sSREG = SREG ; cli() ; } ) 88 | #define EXIT_CRITICAL() (SREG = __sSREG) 89 | #define EXIT_CRITICAL_RET( n ) __WRAP__( { SREG = __sSREG ; return ( n ) ; } ) 90 | 91 | //------------------------------------------------------------------------------ 92 | 93 | #define RET() __asm__ __volatile__ ( "ret\n\t" :: ) 94 | #define SLEEP() __asm__ __volatile__ ( "sleep\n\t" :: ) 95 | #define RESET() __asm__ __volatile__ ( "jmp 0\n\t" :: ) 96 | #define NOP() __asm__ __volatile__ ( "nop\n\t" :: ) 97 | 98 | //------------------------------------------------------------------------------ 99 | 100 | #define VP( p ) (void *)(p) 101 | #define ARRSZ( a ) (sizeof(a) / sizeof(*(a))) 102 | 103 | #ifndef FALSE 104 | #define FALSE 0 105 | #endif 106 | 107 | #ifndef TRUE 108 | #define TRUE (! FALSE) 109 | #endif 110 | 111 | #ifndef NULL 112 | #define NULL 0 113 | #endif 114 | 115 | //------------------------------------------------------------------------------ 116 | 117 | #endif // __ASSEMBLER__ 118 | 119 | //------------------------------------------------------------------------------ 120 | 121 | #define _B1( b ) _BV(b) 122 | #define _B0( b ) 0 123 | 124 | //------------------------------------------------------------------------------ 125 | 126 | #endif 127 | 128 | //------------------------------------------------------------------------------ 129 | --------------------------------------------------------------------------------