-
Notifications
You must be signed in to change notification settings - Fork 66
/
Joystick.c
343 lines (277 loc) · 10.5 KB
/
Joystick.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/*
Force Feedback Joystick
Joystick model specific code for handling joystick input data.
This code is for Microsoft Sidewinder Force Feedback Pro joystick.
Copyright 2012 Tero Loimuneva (tloimu [at] gmail [dot] com)
Copyright 2013 Saku Kekkonen - Modifications for FF Wheel support
MIT License.
Code marked with "3DPVert" is from Detlef "Grendel" Mueller's
3DPVert-project and is distributed with artistic/GPL license.
Some code is based on LUFA Library, for which uses MIT license:
Copyright 2012 Dean Camera (dean [at] fourwalledcubicle [dot] com)
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name of the author not be used in
advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
The author disclaim all warranties with regard to this
software, including all implied warranties of merchantability
and fitness. In no event shall the author be liable for any
special, indirect or consequential damages or any damages
whatsoever resulting from loss of use, data or profits, whether
in an action of contract, negligence or other tortious action,
arising out of or in connection with the use or performance of
this software.
*/
#include "3DPro.h"
#include "Joystick.h"
#include "ffb.h"
#include "usb_hid.h"
#include "debug.h"
//#define USE_FAKE_JOYSTICK
const uint8_t INPUT_REPORTID_ALL = 0xFF;
// Code from 3DPVert begins-->
//------------------------------------------------------------------------------
// Delay until reading stick after last report was sent
#define READ_DEL MS2TM( 4.5, 1024 )
#define IDLE_DEL MS2TM( 4 , 64 )
//------------------------------------------------------------------------------
uint8_t
sw_reportsz ; // Size of SW report in bytes
uint8_t
idle_rate, // idle rate in 4ms clicks, 0 for indefinite
idle_cnt, // idle timer (count down)
sw_repsav[SW_REPSZ_3DP + ADDED_REPORT_DATA_SIZE] ; // USB report data saved
//------------------------------------------------------------------------------
// Check if current report differs from the last saved one and save it.
static uint8_t sw_repchg ( void )
{
uint8_t
r = FALSE,
i = sw_reportsz ;
for ( ; i-- ; )
if ( sw_repsav[i] != sw_report[i] )
{
sw_repsav[i] = sw_report[i] ;
r = TRUE ;
}
return ( r ) ;
}
// <--- Code from 3DPVert ends
/** Configures the board hardware and chip peripherals for the joystick's functionality. */
void Joystick_Init(void)
{
uint8_t sw_sendrep ;
// Initialize..
init_hw() ; // hardware. Note: defined as naked !
sw_reportsz = SW_REPSZ_FFP + ADDED_REPORT_DATA_SIZE;
SetTMPS( 1, 64 ) ; // Set T1 prescaler to /64
SetTMPS( 0, 1024 ) ; // Set T0 prescaler to / 1024 for delay
sw_sendrep = sw_repchg() ; // Init send report flag, saved report
WaitMs(1000);
// Force feedback
FfbInitMidi();
// ADC for extra controls
DDRF = 0; // all inputs
// PORTF |= 0xff; // all pullups enabled
// Init and enable ADC
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
ADMUX |= (1 << REFS0); // ADC reference := Vcc
// ADCSRA |= (1 << ADFR); // ADATE
// ADCSRA |= (1 << ADATE); // free/continous mode
ADMUX |= (1 << ADLAR); // 8-bit resolution to HIGH
ADCSRA |= (1 << ADEN); // Enable ADC
ADCSRA |= (1 << ADIE); // Enable ADC Interrupt
ADCSRA |= (1 << ADSC); // Go ADC
}
/** Configures the board hardware and chip peripherals for the joystick's functionality. */
int Joystick_Connect(void)
{
return 1;
}
/** Fills the given HID report data structure with the next HID report to send to the host.
*
* \param[out] outReportData Pointer to a HID report data structure to be filled
*
* \return Boolean true if the new report differs from the last report, false otherwise
*/
typedef struct
{
int16_t position;
uint16_t buttons;
uint8_t others;
uint16_t scaler;
} JoystickData;
typedef struct
{
uint8_t sampledChannel;
uint8_t trim1, trim2, pedal1, pedal2;
} AddedControls_ADC_t;
static volatile AddedControls_ADC_t added_controls_adc;
static volatile JoystickData prev_joystick_data;
static volatile uint8_t ADC_is_ready = 0;
int Joystick_CreateInputReport(uint8_t inReportId, USB_JoystickReport_Data_t* const outReportData)
{
// Read the data from the FFP-joystick
int InputChanged = 1; // ???? TODO: check for actual changes to avoid unnecessary input reports
#ifndef USE_FAKE_JOYSTICK
// Code from 3DPVert begins-->
SetTMPS( 0, 64 ) ; // Set T0 prescaler to / 64 for query
getdata();
// -------------------------------------------------------------------------------
// *******************************************************************************
// Decode the raw FFP/PP data and store it into sw_report
//
// Input:
// Pointer to start of packet to copy
//
// FFP/PP data packet structure
// ============================
//
// 44444444 33333333 33222222 22221111 11111100 00000000
// 76543210 98765432 10987654 32109876 54321098 76543210
// -------0 -------1 -------2 -------3 -------4 -------5
// ppHHHHRR RRRRTTTT TTTYYYYY YYYYYXXX XXXXXXXB BBBBBBBB
// 321054 32106543 21098765 43210987 65432109 87654321
//
// USB report data structure
// =========================
//
// -------0 -------1 -------2 -------3 -------4 -------5
// XXXXXXXX YYYYYYXX HHHHYYYY BBRRRRRR TBBBBBBB 00TTTTTT
// 76543210 54321098 32109876 21543210 09876543 654321
//
// -------------------------------------------------------------------------------
// Copy the data to the USB report
// <--- Code from 3DPVert ends
#else
memset(sw_report, 0, sizeof(sw_report));
#endif
// ???? This could be done more directly by modifying the 3DPVert code
// ???? that generates its own USB report to the abovementioned format.
// Here we read the additional analog controls in
// rotation for each AD-channel at a time.
//
// Here,
// ADC0 - Trim 1
// ADC1 - Trim 2
// ADC4 - Left Pedal
// ADC5 - Right Pedal
if (ADC_is_ready)
{
ADC_is_ready = 0;
switch (added_controls_adc.sampledChannel)
{
case 0:
added_controls_adc.trim1 = ADCH;
added_controls_adc.sampledChannel = 1;
break;
case 1:
added_controls_adc.trim2 = ADCH;
added_controls_adc.sampledChannel = 4;
break;
case 4:
added_controls_adc.pedal1 = ADCH;
added_controls_adc.sampledChannel = 5;
break;
case 5:
added_controls_adc.pedal2 = ADCH;
added_controls_adc.sampledChannel = 0;
break;
default:
added_controls_adc.sampledChannel = 0; // just in case
break;
}
// Start AD-conversion for the next channel in the rotation
ADMUX &= 0b11111000;
ADMUX |= (0b111 & added_controls_adc.sampledChannel);
ADCSRA|=(1<<ADSC);
}
/*
5 wwwwwwww
4 aaaaaaww
3 BAbbbbbb
2 1FLZYXRC
1 p-------
0 --------
*/
// Convert the raw input data to USB report
outReportData->reportId = 1; // Input report ID 1
if (sw_id == SW_ID_FFPW)
{
outReportData->Button = ((sw_report[2] << 2) | (sw_report[3] >> 6)) ^ 0x00ff;
outReportData->X = ((sw_report[4] & 0x03) << 8) + sw_report[5];
outReportData->Y = (sw_report[4] & 0xfc) << 2;
/* actually break for wheel */
outReportData->Throttle = 63-(sw_report[3] & 0x3f);
}
else
{
// Convert data from Sidewinder Force Feedback Pro
outReportData->X = sw_report[0] + ((sw_report[1] & 0x03) << 8);
if (sw_report[1] & 0x02)
outReportData->X |= (0b11111100 << 8);
outReportData->Y = (sw_report[1] >> 2) + ((sw_report[2] & 0x0F) << 6);
if (sw_report[2] & 0x08)
outReportData->Y |= (0b11111100 << 8);
outReportData->Button = ((sw_report[4] & 0x7F) << 2) + ((sw_report[3] & 0xC0) >> 6);
outReportData->Hat = sw_report[2] >> 4;
outReportData->Rz = (sw_report[3] & 0x3f) - 32;
outReportData->Throttle = ((sw_report[5] & 0x3f) << 1) + (sw_report[4] >> 7);
if (sw_report[5] & 0x20)
outReportData->Throttle |= 0b11000000;
outReportData->Z = 0; // not used at the moment
// Get data from additional controls
outReportData->Rudder = (added_controls_adc.pedal2 - added_controls_adc.pedal1) / 2 - 128; // Combine two pedals into a single rudder
outReportData->Rx = added_controls_adc.trim2; // rudder trim
outReportData->Ry = added_controls_adc.trim1; // elevator trim
}
/*
// This test code generates ever changing position and button values
// to make it easy to see whether the position reports are working
// or not even if you don't have an actual joystick or other control
// connected.
prev_joystick_data.scaler++;
if (prev_joystick_data.scaler < 200)
return false;
prev_joystick_data.scaler = 0;
// "Read" the joystick
if (prev_joystick_data.buttons == 0)
prev_joystick_data.buttons = 1;
else
prev_joystick_data.buttons = prev_joystick_data.buttons << 1;
if (prev_joystick_data.position > 500)
prev_joystick_data.position = 0;
if (prev_joystick_data.position < 500)
prev_joystick_data.position++;
else
prev_joystick_data.position = -500;
LogData("Joy: ", 1, outReportData, sizeof(USB_JoystickReport_Data_t));
// Clear the report contents
memset(outReportData, 0, sizeof(USB_JoystickReport_Data_t));
outReportData->reportId = 1;
outReportData->Button = prev_joystick_data.buttons;
outReportData->Y = prev_joystick_data.position;
outReportData->X = prev_joystick_data.position;
outReportData->Z = prev_joystick_data.position;
outReportData->Rz = prev_joystick_data.position & 0x7F;
outReportData->Rx = prev_joystick_data.position & 0xFF;
outReportData->Ry = prev_joystick_data.position & 0xFF;
outReportData->Throttle = prev_joystick_data.position & 0xFF;
outReportData->Slider = prev_joystick_data.position & 0xFF;
outReportData->Hat = prev_joystick_data.position % 8;
*/
return InputChanged;
}
// AD-conversion-completed-interrupt handler.
// We only set a "ADC ready"-flag here so that
// the actual data can be read later when there
// is more time. Doing any heavier stuff here
// seems to cause problems reading data from FFP joystick.
ISR(ADC_vect)
{
ADC_is_ready = 1;
}