2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
27 #ifdef USE_SERIALRX_XBUS
29 #include "common/crc.h"
31 #include "drivers/time.h"
33 #include "io/serial.h"
36 #include "telemetry/telemetry.h"
45 // Serial driver for JR's XBus (MODE B) receiver
48 #define XBUS_CHANNEL_COUNT 12
49 #define XBUS_RJ01_CHANNEL_COUNT 12
51 // Frame is: ID(1 byte) + 12*channel(2 bytes) + CRC(2 bytes) = 27
52 #define XBUS_FRAME_SIZE_A1 27
53 #define XBUS_FRAME_SIZE_A2 35
55 #define XBUS_RJ01_FRAME_SIZE 33
56 #define XBUS_RJ01_MESSAGE_LENGTH 30
57 #define XBUS_RJ01_OFFSET_BYTES 3
59 #define XBUS_BAUDRATE 115200
60 #define XBUS_RJ01_BAUDRATE 250000
61 #define XBUS_MAX_FRAME_TIME 8000
64 // This is actually based on ID+LENGTH (nibble each)
65 // 0xA - Multiplex ID (also used by JR, no idea why)
68 // However, the JR XG14 that is used for test at the moment
69 // does only use 0xA1 as its output. This is why the implementation
70 // is based on these numbers only. Maybe update this in the future?
71 #define XBUS_START_OF_FRAME_BYTE_A1 (0xA1) //12 channels
72 #define XBUS_START_OF_FRAME_BYTE_A2 (0xA2) //16 channels transfare, but only 12 channels use for
74 // Pulse length convertion from [0...4095] to µs:
78 // Total range is: 2200 - 800 = 1400 <==> 4095
79 // Use formula: 800 + value * 1400 / 4096 (i.e. a shift by 12)
80 #define XBUS_CONVERT_TO_USEC(V) (800 + ((V * 1400) >> 12))
82 static bool xBusFrameReceived
= false;
83 static bool xBusDataIncoming
= false;
84 static uint8_t xBusFramePosition
;
85 static uint8_t xBusFrameLength
;
86 static uint8_t xBusChannelCount
;
87 static uint8_t xBusProvider
;
89 // Use max values for ram areas
90 static volatile uint8_t xBusFrame
[XBUS_FRAME_SIZE_A2
]; //size 35 for 16 channels in xbus_Mode_B
91 static uint16_t xBusChannelData
[XBUS_RJ01_CHANNEL_COUNT
];
93 // Full RJ01 message CRC calculations
94 static uint8_t xBusRj01CRC8(uint8_t inData
, uint8_t seed
)
96 for (uint8_t bitsLeft
= 8; bitsLeft
> 0; bitsLeft
--) {
97 const uint8_t temp
= ((seed
^ inData
) & 0x01);
113 static void xBusUnpackModeBFrame(uint8_t offsetBytes
)
115 // Calculate the CRC of the incoming frame
116 // Calculate on all bytes except the final two CRC bytes
117 const uint16_t inCrc
= crc16_ccitt_update(0, (uint8_t*)&xBusFrame
[offsetBytes
], xBusFrameLength
- 2);
119 // Get the received CRC
120 const uint16_t crc
= (((uint16_t)xBusFrame
[offsetBytes
+ xBusFrameLength
- 2]) << 8) + ((uint16_t)xBusFrame
[offsetBytes
+ xBusFrameLength
- 1]);
123 // Unpack the data, we have a valid frame, only 12 channel unpack also when receive 16 channel
124 for (int i
= 0; i
< xBusChannelCount
; i
++) {
126 const uint8_t frameAddr
= offsetBytes
+ 1 + i
* 2;
127 uint16_t value
= ((uint16_t)xBusFrame
[frameAddr
]) << 8;
128 value
= value
+ ((uint16_t)xBusFrame
[frameAddr
+ 1]);
130 // Convert to internal format
131 xBusChannelData
[i
] = XBUS_CONVERT_TO_USEC(value
);
134 xBusFrameReceived
= true;
138 static void xBusUnpackRJ01Frame(void)
140 // Calculate the CRC of the incoming frame
141 uint8_t outerCrc
= 0;
144 // When using the Align RJ01 receiver with
145 // a MODE B setting in the radio (XG14 tested)
146 // the MODE_B -frame is packed within some
147 // at the moment unknown bytes before and after:
148 // 0xA1 LEN __ 0xA1 12*(High + Low) CRC1 CRC2 + __ __ CRC_OUTER
149 // Compared to a standard MODE B frame that only
150 // contains the "middle" package.
151 // Hence, at the moment, the unknown header and footer
152 // of the RJ01 MODEB packages are discarded.
153 // However, the LAST byte (CRC_OUTER) is infact an 8-bit
154 // CRC for the whole package, using the Dallas-One-Wire CRC
156 // So, we check both these values as well as the provided length
157 // of the outer/full message (LEN)
160 // Check we have correct length of message
162 if (xBusFrame
[1] != XBUS_RJ01_MESSAGE_LENGTH
)
164 // Unknown package as length is not ok
169 // CRC calculation & check for full message
171 for (i
= 0; i
< xBusFrameLength
- 1; i
++) {
172 outerCrc
= xBusRj01CRC8(outerCrc
, xBusFrame
[i
]);
175 if (outerCrc
!= xBusFrame
[xBusFrameLength
- 1])
177 // CRC does not match, skip this frame
181 // Now unpack the "embedded MODE B frame"
182 xBusUnpackModeBFrame(XBUS_RJ01_OFFSET_BYTES
);
185 // Receive ISR callback
186 static void xBusDataReceive(uint16_t c
, void *data
)
191 static uint32_t xBusTimeLast
, xBusTimeInterval
;
193 // Check if we shall reset frame position due to time
195 xBusTimeInterval
= now
- xBusTimeLast
;
197 if (xBusTimeInterval
> XBUS_MAX_FRAME_TIME
) {
198 xBusFramePosition
= 0;
199 xBusDataIncoming
= false;
202 // Check if we shall start a frame?
203 if (xBusFramePosition
== 0) {
204 if (c
== XBUS_START_OF_FRAME_BYTE_A1
) {
205 xBusDataIncoming
= true;
206 xBusFrameLength
= XBUS_FRAME_SIZE_A1
; //decrease framesize (when receiver change, otherwise board must reboot)
207 } else if (c
== XBUS_START_OF_FRAME_BYTE_A2
) {//16channel packet
208 xBusDataIncoming
= true;
209 xBusFrameLength
= XBUS_FRAME_SIZE_A2
; //increase framesize
213 // Only do this if we are receiving to a frame
214 if (xBusDataIncoming
== true) {
215 // Store in frame copy
216 xBusFrame
[xBusFramePosition
] = (uint8_t)c
;
221 if (xBusFramePosition
== xBusFrameLength
) {
222 switch (xBusProvider
) {
223 case SERIALRX_XBUS_MODE_B
:
224 xBusUnpackModeBFrame(0);
225 FALLTHROUGH
; //!!TODO - check this fall through is correct
226 case SERIALRX_XBUS_MODE_B_RJ01
:
227 xBusUnpackRJ01Frame();
229 xBusDataIncoming
= false;
230 xBusFramePosition
= 0;
234 // Indicate time to read a frame from the data...
235 static uint8_t xBusFrameStatus(rxRuntimeState_t
*rxRuntimeState
)
237 UNUSED(rxRuntimeState
);
239 if (!xBusFrameReceived
) {
240 return RX_FRAME_PENDING
;
243 xBusFrameReceived
= false;
245 return RX_FRAME_COMPLETE
;
248 static float xBusReadRawRC(const rxRuntimeState_t
*rxRuntimeState
, uint8_t chan
)
252 // Deliver the data wanted
253 if (chan
>= rxRuntimeState
->channelCount
) {
257 data
= xBusChannelData
[chan
];
262 bool xBusInit(const rxConfig_t
*rxConfig
, rxRuntimeState_t
*rxRuntimeState
)
266 switch (rxRuntimeState
->serialrxProvider
) {
267 case SERIALRX_XBUS_MODE_B
:
268 rxRuntimeState
->channelCount
= XBUS_CHANNEL_COUNT
;
269 xBusFrameReceived
= false;
270 xBusDataIncoming
= false;
271 xBusFramePosition
= 0;
272 baudRate
= XBUS_BAUDRATE
;
273 xBusFrameLength
= XBUS_FRAME_SIZE_A1
;
274 xBusChannelCount
= XBUS_CHANNEL_COUNT
;
275 xBusProvider
= SERIALRX_XBUS_MODE_B
;
277 case SERIALRX_XBUS_MODE_B_RJ01
:
278 rxRuntimeState
->channelCount
= XBUS_RJ01_CHANNEL_COUNT
;
279 xBusFrameReceived
= false;
280 xBusDataIncoming
= false;
281 xBusFramePosition
= 0;
282 baudRate
= XBUS_RJ01_BAUDRATE
;
283 xBusFrameLength
= XBUS_RJ01_FRAME_SIZE
;
284 xBusChannelCount
= XBUS_RJ01_CHANNEL_COUNT
;
285 xBusProvider
= SERIALRX_XBUS_MODE_B_RJ01
;
292 rxRuntimeState
->rcReadRawFn
= xBusReadRawRC
;
293 rxRuntimeState
->rcFrameStatusFn
= xBusFrameStatus
;
295 const serialPortConfig_t
*portConfig
= findSerialPortConfig(FUNCTION_RX_SERIAL
);
301 bool portShared
= telemetryCheckRxPortShared(portConfig
, rxRuntimeState
->serialrxProvider
);
303 bool portShared
= false;
306 serialPort_t
*xBusPort
= openSerialPort(portConfig
->identifier
,
311 portShared
? MODE_RXTX
: MODE_RX
,
312 (rxConfig
->serialrx_inverted
? SERIAL_INVERTED
: 0) | (rxConfig
->halfDuplex
? SERIAL_BIDIR
: 0)
317 telemetrySharedPort
= xBusPort
;
321 return xBusPort
!= NULL
;