add BF spektrum RSSI patch
[inav.git] / src / main / rx / spektrum.c
blob96d52aa863cf14f1792c31cae4d3220e84cefd88
1 /*
2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <stdlib.h>
22 #include "platform.h"
24 #ifdef USE_SERIALRX_SPEKTRUM
26 #include "build/debug.h"
28 #include "drivers/io.h"
29 #include "drivers/io_impl.h"
30 #include "drivers/light_led.h"
31 #include "drivers/serial.h"
32 #include "drivers/system.h"
34 #include "fc/config.h"
36 #include "io/serial.h"
38 #ifdef TELEMETRY
39 #include "telemetry/telemetry.h"
40 #endif
42 #include "rx/rx.h"
43 #include "rx/spektrum.h"
45 // driver for spektrum satellite receiver / sbus
47 #define SPEKTRUM_MAX_SUPPORTED_CHANNEL_COUNT 12
48 #define SPEKTRUM_2048_CHANNEL_COUNT 12
49 #define SPEKTRUM_1024_CHANNEL_COUNT 7
51 #define SPEK_FRAME_SIZE 16
52 #define SPEKTRUM_NEEDED_FRAME_INTERVAL 5000
54 #define SPEKTRUM_BAUDRATE 115200
56 #define SPEKTRUM_MAX_FADE_PER_SEC 40
57 #define SPEKTRUM_FADE_REPORTS_PER_SEC 2
59 static uint8_t spek_chan_shift;
60 static uint8_t spek_chan_mask;
61 static bool rcFrameComplete = false;
62 static bool spekHiRes = false;
64 // Variables used for calculating a signal strength from satellite fade.
65 // This is time-variant and computed every second based on the fade
66 // count over the last second.
67 static uint32_t spek_fade_last_sec = 0; // Stores the timestamp of the last second.
68 static uint16_t spek_fade_last_sec_count = 0; // Stores the fade count at the last second.
69 static uint8_t rssi_channel; // Stores the RX RSSI channel.
71 static volatile uint8_t spekFrame[SPEK_FRAME_SIZE];
73 static rxRuntimeConfig_t *rxRuntimeConfigPtr;
75 #ifdef SPEKTRUM_BIND
76 static IO_t BindPin = DEFIO_IO(NONE);
77 #endif
78 #ifdef HARDWARE_BIND_PLUG
79 static IO_t BindPlug = DEFIO_IO(NONE);
80 #endif
83 // Receive ISR callback
84 static void spektrumDataReceive(uint16_t c)
86 timeUs_t spekTime;
87 timeDelta_t spekTimeInterval;
88 static timeUs_t spekTimeLast = 0;
89 static uint8_t spekFramePosition = 0;
91 spekTime = micros();
92 spekTimeInterval = cmpTimeUs(spekTime, spekTimeLast);
93 spekTimeLast = spekTime;
95 if (spekTimeInterval > SPEKTRUM_NEEDED_FRAME_INTERVAL) {
96 spekFramePosition = 0;
99 if (spekFramePosition < SPEK_FRAME_SIZE) {
100 spekFrame[spekFramePosition++] = (uint8_t)c;
101 if (spekFramePosition == SPEK_FRAME_SIZE) {
102 rcFrameComplete = true;
103 } else {
104 rcFrameComplete = false;
109 static uint32_t spekChannelData[SPEKTRUM_MAX_SUPPORTED_CHANNEL_COUNT];
111 uint8_t spektrumFrameStatus(void)
113 uint8_t b;
114 uint16_t fade;
115 uint32_t current_secs;
117 if (!rcFrameComplete) {
118 return RX_FRAME_PENDING;
121 rcFrameComplete = false;
123 // Fetch the fade count
124 fade = (spekFrame[0] << 8) + spekFrame[1];
125 current_secs = micros() / 1000 / (1000 / SPEKTRUM_FADE_REPORTS_PER_SEC);
127 if (spek_fade_last_sec == 0) {
128 // This is the first frame status received.
129 spek_fade_last_sec_count = fade;
130 spek_fade_last_sec = current_secs;
131 } else if(spek_fade_last_sec != current_secs) {
132 // If the difference is > 1, then we missed several seconds worth of frames and
133 // should just throw out the fade calc (as it's likely a full signal loss).
134 if((current_secs - spek_fade_last_sec) == 1) {
135 if(rssi_channel != 0) {
136 if (spekHiRes)
137 spekChannelData[rssi_channel] = 2048 - ((fade - spek_fade_last_sec_count) * 2048 / (SPEKTRUM_MAX_FADE_PER_SEC / SPEKTRUM_FADE_REPORTS_PER_SEC));
138 else
139 spekChannelData[rssi_channel] = 1024 - ((fade - spek_fade_last_sec_count) * 1024 / (SPEKTRUM_MAX_FADE_PER_SEC / SPEKTRUM_FADE_REPORTS_PER_SEC));
142 spek_fade_last_sec_count = fade;
143 spek_fade_last_sec = current_secs;
147 for (b = 3; b < SPEK_FRAME_SIZE; b += 2) {
148 uint8_t spekChannel = 0x0F & (spekFrame[b - 1] >> spek_chan_shift);
149 if (spekChannel < rxRuntimeConfigPtr->channelCount && spekChannel < SPEKTRUM_MAX_SUPPORTED_CHANNEL_COUNT) {
150 if(rssi_channel != 0 && spekChannel != rssi_channel) {
151 spekChannelData[spekChannel] = ((uint32_t)(spekFrame[b - 1] & spek_chan_mask) << 8) + spekFrame[b];
156 return RX_FRAME_COMPLETE;
159 static uint16_t spektrumReadRawRC(const rxRuntimeConfig_t *rxRuntimeConfig, uint8_t chan)
161 uint16_t data;
163 if (chan >= rxRuntimeConfig->channelCount) {
164 return 0;
167 if (spekHiRes)
168 data = 988 + (spekChannelData[chan] >> 1); // 2048 mode
169 else
170 data = 988 + spekChannelData[chan]; // 1024 mode
172 return data;
175 #ifdef SPEKTRUM_BIND
177 bool spekShouldBind(uint8_t spektrum_sat_bind)
179 #ifdef HARDWARE_BIND_PLUG
180 BindPlug = IOGetByTag(IO_TAG(BINDPLUG_PIN));
181 IOInit(BindPlug, OWNER_RX, RESOURCE_INPUT, 0);
182 IOConfigGPIO(BindPlug, IOCFG_IPU);
184 // Check status of bind plug and exit if not active
185 delayMicroseconds(10); // allow configuration to settle
186 if (IORead(BindPlug)) {
187 return false;
189 #endif
191 return !(
192 isMPUSoftReset() ||
193 spektrum_sat_bind == SPEKTRUM_SAT_BIND_DISABLED ||
194 spektrum_sat_bind > SPEKTRUM_SAT_BIND_MAX
197 /* spektrumBind function ported from Baseflight. It's used to bind satellite receiver to TX.
198 * Function must be called immediately after startup so that we don't miss satellite bind window.
199 * Known parameters. Tested with DSMX satellite and DX8 radio. Framerate (11ms or 22ms) must be selected from TX.
200 * 9 = DSMX 11ms / DSMX 22ms
201 * 5 = DSM2 11ms 2048 / DSM2 22ms 1024
203 void spektrumBind(rxConfig_t *rxConfig)
205 int i;
206 if (!spekShouldBind(rxConfig->spektrum_sat_bind)) {
207 return;
210 LED1_ON;
212 BindPin = IOGetByTag(IO_TAG(BIND_PIN));
213 IOInit(BindPin, OWNER_RX, RESOURCE_OUTPUT, 0);
214 IOConfigGPIO(BindPin, IOCFG_OUT_PP);
216 // RX line, set high
217 IOWrite(BindPin, true);
219 // Bind window is around 20-140ms after powerup
220 delay(60);
221 LED1_OFF;
223 for (i = 0; i < rxConfig->spektrum_sat_bind; i++) {
225 LED0_OFF;
226 LED2_OFF;
227 // RX line, drive low for 120us
228 IOWrite(BindPin, false);
229 delayMicroseconds(120);
231 LED0_ON;
232 LED2_ON;
233 // RX line, drive high for 120us
234 IOWrite(BindPin, true);
235 delayMicroseconds(120);
239 #ifndef HARDWARE_BIND_PLUG
240 // If we came here as a result of hard reset (power up, with spektrum_sat_bind set), then reset it back to zero and write config
241 // Don't reset if hardware bind plug is present
242 // Reset only when autoreset is enabled
243 if (rxConfig->spektrum_sat_bind_autoreset == 1 && !isMPUSoftReset()) {
244 rxConfig->spektrum_sat_bind = 0;
245 saveConfigAndNotify();
247 #endif
250 #endif // SPEKTRUM_BIND
252 bool spektrumInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
254 rxRuntimeConfigPtr = rxRuntimeConfig;
256 switch (rxConfig->serialrx_provider) {
257 case SERIALRX_SPEKTRUM2048:
258 // 11 bit frames
259 spek_chan_shift = 3;
260 spek_chan_mask = 0x07;
261 spekHiRes = true;
262 rxRuntimeConfig->channelCount = SPEKTRUM_2048_CHANNEL_COUNT;
263 rxRuntimeConfig->rxRefreshRate = 11000;
264 break;
265 case SERIALRX_SPEKTRUM1024:
266 // 10 bit frames
267 spek_chan_shift = 2;
268 spek_chan_mask = 0x03;
269 spekHiRes = false;
270 rxRuntimeConfig->channelCount = SPEKTRUM_1024_CHANNEL_COUNT;
271 rxRuntimeConfig->rxRefreshRate = 22000;
272 break;
275 rxRuntimeConfig->rcReadRawFn = spektrumReadRawRC;
276 rxRuntimeConfig->rcFrameStatusFn = spektrumFrameStatus;
278 const serialPortConfig_t *portConfig = findSerialPortConfig(FUNCTION_RX_SERIAL);
279 if (!portConfig) {
280 return false;
283 #ifdef TELEMETRY
284 bool portShared = telemetryCheckRxPortShared(portConfig);
285 #else
286 bool portShared = false;
287 #endif
289 serialPort_t *spektrumPort = openSerialPort(portConfig->identifier, FUNCTION_RX_SERIAL, spektrumDataReceive, SPEKTRUM_BAUDRATE, portShared ? MODE_RXTX : MODE_RX, SERIAL_NOT_INVERTED);
291 #ifdef TELEMETRY
292 if (portShared) {
293 telemetrySharedPort = spektrumPort;
295 #endif
297 rssi_channel = rxConfig->rssi_channel - 1; // -1 because rxConfig->rssi_channel is 1-based and rssi_channel is 0-based.
298 if (rssi_channel >= rxRuntimeConfig->channelCount) {
299 rssi_channel = 0;
302 return spektrumPort != NULL;
304 #endif // USE_SERIALRX_SPEKTRUM