Thomas Buck 3 лет назад
Родитель
Сommit
5b1c1554d9
8 измененных файлов: 56 добавлений и 20 удалений
  1. 4
    2
      README.md
  2. 9
    7
      Saitek-X52-PPM.ino
  3. 11
    2
      config.cpp
  4. 3
    3
      config.h
  5. 13
    4
      cppm.cpp
  6. 6
    1
      cppm.h
  7. 1
    0
      events.h
  8. 9
    1
      events_buttons.cpp

+ 4
- 2
README.md Просмотреть файл

@@ -2,12 +2,14 @@
2 2
 
3 3
 This sketch allows connecting a [Saitek X52](http://www.saitek.com/uk/prod/x52.html) or [Saitek X52 Pro](http://www.saitek.com/uk/prod/x52pro.html) to an [Arduino](https://www.arduino.cc/en/Main/ArduinoBoardUno) with a [USB Host Shield](https://www.arduino.cc/en/Main/ArduinoUSBHostShield). It uses the [USB_Host_Shield_2.0 Library](https://github.com/felis/USB_Host_Shield_2.0).
4 4
 
5
-A CPPM signal is generated on a configurable pin (4 by default, can be changed in `cppm.h`).
5
+A CPPM signal is generated on a configurable pin and can be fed into a transmitter module or directly into a flight controller.
6 6
 
7
-It includes code to interface with the Multi-Function-Display on the Joystick to display text and change the LED and background lighting.
7
+Using the Multi-Function-Display on the Joystick, every config option can be changed. These values can be stored on the EEPROM and will be loaded on every start.
8 8
 
9 9
 I'm connecting the Arduino to the [FrSky DHT module](http://www.frsky-rc.com/product/pro.php?pro_id=7) in my modified transmitter to control my Tricopter.
10 10
 
11
+A modified (ie. non-inverted) FrSky Host Telemetry Port (D-Port) can be connected to the hardware serial port of the Arduino so the Telemetry data (link quality and voltages) will be displayed on the Multi-Function-Display of the joystick.
12
+
11 13
 ## License
12 14
 
13 15
 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.

+ 9
- 7
Saitek-X52-PPM.ino Просмотреть файл

@@ -22,10 +22,12 @@
22 22
 #include "frsky.h"
23 23
 #include "config.h"
24 24
 
25
-#define ENABLE_SERIAL_PORT
25
+#define ENABLE_SERIAL_PORT 9600
26 26
 //#define DEBUG_OUTPUT Serial
27 27
 //#define DEBUG_MFD_UPTIME
28 28
 
29
+#define LED_STATUS_PIN 13
30
+
29 31
 USB usb;
30 32
 USBHub hub(&usb);
31 33
 HIDUniversal hid(&usb);
@@ -65,27 +67,27 @@ void statusCallback(uint8_t a1, uint8_t a2, uint8_t q1, uint8_t q2) {
65 67
 
66 68
 void setup() {
67 69
 #ifdef ENABLE_SERIAL_PORT
68
-    Serial.begin(115200);
70
+    Serial.begin(ENABLE_SERIAL_PORT);
69 71
 #endif
70 72
 
71 73
 #ifdef DEBUG_OUTPUT
72 74
     DEBUG_OUTPUT.println("Start");
73 75
 #endif
74 76
 
75
-    pinMode(13, OUTPUT);
76
-    digitalWrite(13, LOW);
77
+    pinMode(LED_STATUS_PIN, OUTPUT);
78
+    digitalWrite(LED_STATUS_PIN, LOW);
77 79
 
78 80
     eepromRead();
79 81
 
80 82
     if (usb.Init() == -1) {
81
-        digitalWrite(13, HIGH);
83
+        digitalWrite(LED_STATUS_PIN, HIGH);
82 84
 #ifdef DEBUG_OUTPUT
83 85
         DEBUG_OUTPUT.println("OSC did not start.");
84 86
 #endif
85 87
     }
86 88
 
87 89
     if (!hid.SetReportParser(0, &joy)) {
88
-        digitalWrite(13, HIGH);
90
+        digitalWrite(LED_STATUS_PIN, HIGH);
89 91
 #ifdef DEBUG_OUTPUT
90 92
         DEBUG_OUTPUT.println("Error adding report parser.");
91 93
 #endif
@@ -93,7 +95,7 @@ void setup() {
93 95
 
94 96
     CPPM::instance().init();
95 97
     frsky.setDataHandler(&statusCallback);
96
-    wdt_enable(WDTO_500MS);
98
+    wdt_enable(WDTO_1S);
97 99
 }
98 100
 
99 101
 void init_joystick() {

+ 11
- 2
config.cpp Просмотреть файл

@@ -6,6 +6,7 @@
6 6
  * modify it under the terms of the GNU General Public License as
7 7
  * published by the Free Software Foundation, version 2.
8 8
  */
9
+
9 10
 #include <EEPROM.h>
10 11
 #include "cppm.h"
11 12
 #include "events.h"
@@ -67,11 +68,13 @@ static uint8_t fromEEPROM(ConfigData& data) {
67 68
 
68 69
 void eepromRead() {
69 70
     ConfigData data;
71
+
70 72
     if (fromEEPROM(data) != 0) {
71 73
         data.channels = DEFAULT_CHANNELS;
72 74
         data.frameLength = DEFAULT_FRAME_LENGTH;
73 75
         data.pulseLength = DEFAULT_PULSE_LENGTH;
74 76
         data.inverted = DEFAULT_INVERT_STATE;
77
+        data.cppmPin = CPPM_OUTPUT_PIN_DEFAULT;
75 78
         for (uint8_t i = 0; i < CHANNELS_MAX; i++) {
76 79
             data.invert[i] = 0;
77 80
             data.minimum[i] = CHANNEL_MINIMUM_VALUE;
@@ -79,12 +82,14 @@ void eepromRead() {
79 82
             data.trim[i] = 0;
80 83
         }
81 84
 
85
+        // Should be correct for every device
86
+        data.invert[CHANNEL_THROTTLE] = 1;
87
+        data.invert[CHANNEL_PITCH] = 1;
88
+
82 89
         /*
83 90
          * Default values to match my personal setup.
84 91
          * Can be changed using the on-screen menu.
85 92
          */
86
-        data.invert[CHANNEL_THROTTLE] = 1;
87
-        data.invert[CHANNEL_PITCH] = 1;
88 93
         data.minimum[CHANNEL_THROTTLE] = 1010;
89 94
         data.maximum[CHANNEL_THROTTLE] = 1950;
90 95
         data.minimum[CHANNEL_ROLL] = 1050;
@@ -103,6 +108,7 @@ void eepromRead() {
103 108
     CPPM::instance().setFrameLength(data.frameLength);
104 109
     CPPM::instance().setPulseLength(data.pulseLength);
105 110
     CPPM::instance().setInvert(data.inverted);
111
+    CPPM::instance().setOutput(data.cppmPin);
106 112
     for (uint8_t i = 0; i < CHANNELS_MAX; i++) {
107 113
         joyCPPM.setInvert(i, data.invert[i]);
108 114
         joyCPPM.setMinimum(i, data.minimum[i]);
@@ -113,16 +119,19 @@ void eepromRead() {
113 119
 
114 120
 void eepromWrite() {
115 121
     ConfigData data;
122
+
116 123
     data.channels = CPPM::instance().getChannels();
117 124
     data.frameLength = CPPM::instance().getFrameLength();
118 125
     data.pulseLength = CPPM::instance().getPulseLength();
119 126
     data.inverted = CPPM::instance().getInvert();
127
+    data.cppmPin = CPPM::instance().getOutput();
120 128
     for (uint8_t i = 0; i < CHANNELS_MAX; i++) {
121 129
         data.invert[i] = joyCPPM.getInvert(i);
122 130
         data.minimum[i] = joyCPPM.getMinimum(i);
123 131
         data.maximum[i] = joyCPPM.getMaximum(i);
124 132
         data.trim[i] = joyCPPM.getTrim(i);
125 133
     }
134
+
126 135
     toEEPROM(data);
127 136
 }
128 137
 

+ 3
- 3
config.h Просмотреть файл

@@ -12,7 +12,7 @@
12 12
 
13 13
 #include <stdint.h>
14 14
 
15
-#define CPPM_OUTPUT_PIN 4
15
+#define CPPM_OUTPUT_PIN_DEFAULT 4
16 16
 #define CHANNEL_MINIMUM_VALUE 1000
17 17
 #define CHANNEL_DEFAULT_VALUE 1500
18 18
 #define CHANNEL_MAXIMUM_VALUE 2000
@@ -32,11 +32,11 @@ enum RxChannels {
32 32
 };
33 33
 
34 34
 // Increase string number when struct changes!
35
-#define CONFIG_VERSION "USBCPPM-01"
35
+#define CONFIG_VERSION "USBCPPM-02"
36 36
 #define CONFIG_VERSION_LENGTH (sizeof(CONFIG_VERSION) - 1)
37 37
 
38 38
 struct ConfigData {
39
-    uint16_t channels, frameLength, pulseLength, inverted;
39
+    uint16_t channels, frameLength, pulseLength, inverted, cppmPin;
40 40
     uint16_t minimum[CHANNELS_MAX];
41 41
     uint16_t maximum[CHANNELS_MAX];
42 42
     uint16_t invert[CHANNELS_MAX];

+ 13
- 4
cppm.cpp Просмотреть файл

@@ -11,6 +11,7 @@
11 11
  * modify it under the terms of the GNU General Public License as
12 12
  * published by the Free Software Foundation, version 2.
13 13
  */
14
+
14 15
 #include <Arduino.h>
15 16
 #include "cppm.h"
16 17
 
@@ -30,8 +31,8 @@ void CPPM::init(void) {
30 31
         data[i] = CHANNEL_DEFAULT_VALUE;
31 32
     }
32 33
 
33
-    pinMode(CPPM_OUTPUT_PIN, OUTPUT);
34
-    digitalWrite(CPPM_OUTPUT_PIN, CPPM::inst->onState ? LOW : HIGH);
34
+    pinMode(output, OUTPUT);
35
+    digitalWrite(output, CPPM::inst->onState ? LOW : HIGH);
35 36
 
36 37
     cli();
37 38
     TCCR1A = 0; // set entire TCCR1 register to 0
@@ -43,6 +44,14 @@ void CPPM::init(void) {
43 44
     sei();
44 45
 }
45 46
 
47
+void CPPM::setOutput(uint8_t i) {
48
+    digitalWrite(output, LOW);
49
+    pinMode(output, INPUT);
50
+    output = i;
51
+    pinMode(output, OUTPUT);
52
+    digitalWrite(output, CPPM::inst->onState ? LOW : HIGH);
53
+}
54
+
46 55
 void CPPM::copy(uint16_t* d) {
47 56
 #ifdef DEBUG_OUTPUT
48 57
     DEBUG_OUTPUT.println("New CPPM data!");
@@ -63,12 +72,12 @@ ISR(TIMER1_COMPA_vect) {
63 72
     TCNT1 = 0;
64 73
     if (CPPM::inst->state) {
65 74
         // start pulse
66
-        digitalWrite(CPPM_OUTPUT_PIN, CPPM::inst->onState ? HIGH : LOW);
75
+        digitalWrite(CPPM::inst->output, CPPM::inst->onState ? HIGH : LOW);
67 76
         OCR1A = CPPM::inst->pulseLength << 1;
68 77
         CPPM::inst->state = 0;
69 78
     } else {
70 79
         // end pulse and calculate when to start the next pulse
71
-        digitalWrite(CPPM_OUTPUT_PIN, CPPM::inst->onState ? LOW : HIGH);
80
+        digitalWrite(CPPM::inst->output, CPPM::inst->onState ? LOW : HIGH);
72 81
         CPPM::inst->state = 1;
73 82
         if (CPPM::inst->currentChannel >= CPPM::inst->channels) {
74 83
             CPPM::inst->currentChannel = 0;

+ 6
- 1
cppm.h Просмотреть файл

@@ -44,15 +44,20 @@ class CPPM {
44 44
     inline uint8_t getInvert() { return !onState; }
45 45
     inline void setInvert(uint8_t i) { onState = !i; }
46 46
 
47
+    inline uint8_t getOutput() { return output; }
48
+    void setOutput(uint8_t i);
49
+
47 50
   private:
48 51
     CPPM() : channels(DEFAULT_CHANNELS), frameLength(DEFAULT_FRAME_LENGTH),
49
-            pulseLength(DEFAULT_PULSE_LENGTH), onState(!DEFAULT_INVERT_STATE) { }
52
+            pulseLength(DEFAULT_PULSE_LENGTH), onState(!DEFAULT_INVERT_STATE),
53
+            output(CPPM_OUTPUT_PIN_DEFAULT) { }
50 54
     CPPM(CPPM&) { }
51 55
 
52 56
     volatile uint16_t channels;
53 57
     volatile uint16_t frameLength; // PPM frame length in microseconds (1ms = 1000µs)
54 58
     volatile uint16_t pulseLength;
55 59
     volatile uint8_t onState; // polarity of the pulses: 1 is positive, 0 is negative
60
+    volatile uint8_t output;
56 61
 
57 62
     volatile uint16_t data[CHANNELS_MAX];
58 63
     volatile uint8_t state;

+ 1
- 0
events.h Просмотреть файл

@@ -118,6 +118,7 @@ class JoystickEventsButtons : public JoystickEvents {
118 118
         EDIT_FRAME_LENGTH,
119 119
         EDIT_PULSE_LENGTH,
120 120
         EDIT_INVERT,
121
+        EDIT_CPPM_PIN,
121 122
         EDIT_MIN_ROLL,
122 123
         EDIT_MAX_ROLL,
123 124
         EDIT_MIN_PITCH,

+ 9
- 1
events_buttons.cpp Просмотреть файл

@@ -54,7 +54,7 @@ void JoystickEventsButtons::printMenu() {
54 54
     static const uint8_t mainMenuCount = sizeof(mainMenu) / sizeof(mainMenu[0]);
55 55
 
56 56
     static const char* cppmMenu[] = {
57
-        "Channels", "Frame Length", "Pulse Length", "Invert", "Main Menu"
57
+        "Channels", "Frame Length", "Pulse Length", "Invert", "Output Pin", "Main Menu"
58 58
     };
59 59
     static const uint8_t cppmMenuCount = sizeof(cppmMenu) / sizeof(cppmMenu[0]);
60 60
 
@@ -90,6 +90,8 @@ void JoystickEventsButtons::printMenu() {
90 90
         printValue(MIN_PULSE_LENGTH, MAX_PULSE_LENGTH, cppmMenu[2]);
91 91
     } else if (state == EDIT_INVERT) {
92 92
         printValue(0, 1, cppmMenu[3]);
93
+    } else if (state == EDIT_CPPM_PIN) {
94
+        printValue(0, 13, cppmMenu[4]);
93 95
     } else if ((state >= EDIT_INVERT_ROLL) && (state <= EDIT_INVERT_AUX2)) {
94 96
         uint8_t index = state - EDIT_INVERT_ROLL;
95 97
         printValue(0, 1, (String("Invert ") + axisMenu[index]).c_str());
@@ -252,6 +254,9 @@ void JoystickEventsButtons::OnButtonDown(uint8_t but_id) {
252 254
                 state = EDIT_INVERT;
253 255
                 value = CPPM::instance().getInvert();
254 256
             } else if (index == 4) {
257
+                state = EDIT_CPPM_PIN;
258
+                value = CPPM::instance().getOutput();
259
+            } else if (index == 5) {
255 260
                 state = MAINMENU;
256 261
                 index = 0;
257 262
             }
@@ -267,6 +272,9 @@ void JoystickEventsButtons::OnButtonDown(uint8_t but_id) {
267 272
         } else if (state == EDIT_INVERT) {
268 273
             CPPM::instance().setInvert(value);
269 274
             state = CPPMMENU;
275
+        } else if (state == EDIT_CPPM_PIN) {
276
+            CPPM::instance().setOutput(value);
277
+            state = CPPMMENU;
270 278
         } else if (state == EDIT_INVERT_ROLL) {
271 279
             joyCPPM.setInvert(CHANNEL_ROLL, value);
272 280
             state = INVERTAXISMENU;

Загрузка…
Отмена
Сохранить