2 ******************************************************************************
4 * @author The LibrePilot Project, http://www.librepilot.org, Copyright (c) 2015
5 * @author Tau Labs, http://taulabs.org, Copyright (C) 2013-2014
6 * @addtogroup PIOS PIOS Core hardware abstraction layer
8 * @addtogroup PIOS_HOTT Graupner HoTT receiver functions
10 * @brief Graupner HoTT receiver functions for SUMD/H
11 *****************************************************************************/
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 /* Project Includes */
28 #include "pios_hott_priv.h"
30 #if defined(PIOS_INCLUDE_HOTT)
32 #if !defined(PIOS_INCLUDE_RTC)
33 #error PIOS_INCLUDE_RTC must be used to use HOTT
37 * HOTT protocol documentation
39 * Currently known Graupner HoTT serial port settings:
40 * 115200bps serial stream, 8 bits, no parity, 1 stop bit
41 * size of each frame: 11..37 bytes
42 * data resolution: 14 bit
43 * frame period: 11ms or 22ms
45 * Currently known SUMD/SUMH frame structure:
46 * Section Byte_Number Byte_Name Byte_Value Remark
47 * Header 0 Vendor_ID 0xA8 Graupner
48 * Header 1 Status 0x00 valid and live SUMH data frame
49 * 0x01 valid and live SUMD data frame
50 * 0x81 valid SUMD/H data frame with
51 * transmitter in fail safe condition
52 * others invalid frame
53 * Header 2 N_Channels 0x02..0x20 number of transmitted channels
54 * Data n*2+1 Channel n MSB 0x00..0xff High Byte of channel n data
55 * Data n*2+2 Channel n LSB 0x00..0xff Low Byte of channel n data
56 * SUMD_CRC (N_Channels+1)*2+1 CRC High Byte 0x00..0xff High Byte of 16 Bit CRC
57 * SUMD_CRC (N_Channels+1)*2+2 CRC Low Byte 0x00..0xff Low Byte of 16 Bit CRC
58 * SUMH_Telemetry (N_Channels+1)*2+1 Telemetry_Req 0x00..0xff 0x00 no telemetry request
59 * SUMH_CRC (N_Channels+1)*2+2 CRC Byte 0x00..0xff Low Byte of all added data bytes
61 * Channel Data Interpretation
62 * Stick Positon Channel Data Remark
63 * ext. low (-150%) 0x1c20 900µs
64 * low (-100%) 0x2260 1100µs
65 * neutral (0%) 0x2ee0 1500µs
66 * high (100%) 0x3b60 1900µs
67 * ext. high(150%) 0x41a0 2100µs
69 * Channel Mapping (not sure)
79 /* HOTT frame size and contents definitions */
80 #define HOTT_HEADER_LENGTH 3
81 #define HOTT_CRC_LENGTH 2
82 #define HOTT_MAX_CHANNELS_PER_FRAME 32
83 #define HOTT_OVERHEAD_LENGTH (HOTT_HEADER_LENGTH + HOTT_CRC_LENGTH)
84 #define HOTT_MAX_FRAME_LENGTH (HOTT_MAX_CHANNELS_PER_FRAME * 2 + HOTT_OVERHEAD_LENGTH)
86 #define HOTT_GRAUPNER_ID 0xA8
87 #define HOTT_STATUS_LIVING_SUMH 0x00
88 #define HOTT_STATUS_LIVING_SUMD 0x01
89 #define HOTT_STATUS_FAILSAFE 0x81
90 #define HOTT_FRAME_TIMEOUT 4
91 #define HOTT_FAILSAFE_TIMEOUT 64
93 /* With an Ex.Bus frame rate of 11/22ms (90/45Hz) averaging over 15 samples
94 * gives about a 165/330ms response.
96 #define HOTT_FL_WEIGHTED_AVERAGE 20
99 /* Forward Declarations */
100 static int32_t PIOS_HOTT_Get(uint32_t rcvr_id
, uint8_t channel
);
101 static uint16_t PIOS_HOTT_RxInCallback(uint32_t context
,
106 static void PIOS_HOTT_Supervisor(uint32_t hott_id
);
107 static uint8_t PIOS_HOTT_Quality_Get(uint32_t rcvr_id
);
109 /* Local Variables */
110 const struct pios_rcvr_driver pios_hott_rcvr_driver
= {
111 .read
= PIOS_HOTT_Get
,
112 .get_quality
= PIOS_HOTT_Quality_Get
,
115 enum pios_hott_dev_magic
{
116 PIOS_HOTT_DEV_MAGIC
= 0x4853554D,
119 struct pios_hott_state
{
120 uint16_t channel_data
[PIOS_HOTT_NUM_INPUTS
];
121 uint8_t received_data
[HOTT_MAX_FRAME_LENGTH
];
122 uint8_t receive_timer
;
123 uint8_t failsafe_timer
;
125 uint8_t tx_connected
;
127 uint8_t frame_length
;
131 struct pios_hott_dev
{
132 enum pios_hott_dev_magic magic
;
133 const struct pios_hott_cfg
*cfg
;
134 enum pios_hott_proto proto
;
135 struct pios_hott_state state
;
138 /* Allocate HOTT device descriptor */
139 static struct pios_hott_dev
*PIOS_HOTT_Alloc(void)
141 struct pios_hott_dev
*hott_dev
;
143 hott_dev
= (struct pios_hott_dev
*)pios_malloc(sizeof(*hott_dev
));
148 hott_dev
->magic
= PIOS_HOTT_DEV_MAGIC
;
152 /* Validate HOTT device descriptor */
153 static bool PIOS_HOTT_Validate(struct pios_hott_dev
*hott_dev
)
155 return hott_dev
->magic
== PIOS_HOTT_DEV_MAGIC
;
158 /* Reset channels in case of lost signal or explicit failsafe receiver flag */
159 static void PIOS_HOTT_ResetChannels(struct pios_hott_state
*state
)
161 for (int i
= 0; i
< PIOS_HOTT_NUM_INPUTS
; i
++) {
162 state
->channel_data
[i
] = PIOS_RCVR_TIMEOUT
;
166 /* Reset HOTT receiver state */
167 static void PIOS_HOTT_ResetState(struct pios_hott_state
*state
)
169 state
->receive_timer
= 0;
170 state
->failsafe_timer
= 0;
171 state
->frame_found
= 0;
172 state
->tx_connected
= 0;
173 state
->quality
= 0.0f
;
174 PIOS_HOTT_ResetChannels(state
);
178 * Check and unroll complete frame data.
179 * \output 0 frame data accepted
180 * \output -1 frame error found
182 static int PIOS_HOTT_UnrollChannels(struct pios_hott_dev
*hott_dev
)
184 struct pios_hott_state
*state
= &(hott_dev
->state
);
186 /* check the header and crc for a valid HoTT SUM stream */
187 uint8_t vendor
= state
->received_data
[0];
188 uint8_t status
= state
->received_data
[1];
190 if (vendor
!= HOTT_GRAUPNER_ID
) {
191 /* Graupner ID was expected */
196 case HOTT_STATUS_LIVING_SUMH
:
197 case HOTT_STATUS_LIVING_SUMD
:
198 case HOTT_STATUS_FAILSAFE
:
199 /* check crc before processing */
200 if (hott_dev
->proto
== PIOS_HOTT_PROTO_SUMD
) {
201 /* SUMD has 16 bit CCITT CRC */
203 uint8_t *s
= &(state
->received_data
[0]);
204 int len
= state
->byte_count
- 2;
205 for (int n
= 0; n
< len
; n
++) {
206 crc
^= (uint16_t)s
[n
] << 8;
207 for (int i
= 0; i
< 8; i
++) {
208 crc
= (crc
& 0x8000) ? (crc
<< 1) ^ 0x1021 : (crc
<< 1);
211 if (crc
^ (((uint16_t)s
[len
] << 8) | s
[len
+ 1])) {
212 /* wrong crc checksum found */
216 if (hott_dev
->proto
== PIOS_HOTT_PROTO_SUMH
) {
217 /* SUMH has only 8 bit added CRC */
219 uint8_t *s
= &(state
->received_data
[0]);
220 int len
= state
->byte_count
- 1;
221 for (int n
= 0; n
< len
; n
++) {
225 /* wrong crc checksum found */
229 /* check for a living connect */
230 state
->tx_connected
|= (status
!= HOTT_STATUS_FAILSAFE
);
233 /* wrong header format */
237 /* check initial connection since reset or timeout */
238 if (!(state
->tx_connected
)) {
239 /* these are failsafe data without a first connect. ignore it */
240 PIOS_HOTT_ResetChannels(state
);
244 /* unroll channels */
245 uint8_t n_channels
= state
->received_data
[2];
246 uint8_t *s
= &(state
->received_data
[3]);
249 for (int i
= 0; i
< HOTT_MAX_CHANNELS_PER_FRAME
; i
++) {
250 if (i
< n_channels
) {
251 word
= ((uint16_t)s
[0] << 8) | s
[1];
252 s
+= sizeof(uint16_t);
253 /* save the channel value */
254 if (i
< PIOS_HOTT_NUM_INPUTS
) {
255 /* floating version. channel limits from -100..+100% are mapped to 1000..2000 */
256 state
->channel_data
[i
] = (uint16_t)(word
/ 6.4f
- 375);
259 /* this channel was not received */
260 state
->channel_data
[i
] = PIOS_RCVR_INVALID
;
264 /* all channels processed */
268 /* either SUMD selected with SUMH stream found, or vice-versa */
272 /* Update decoder state processing input byte from the HoTT stream */
273 static void PIOS_HOTT_UpdateState(struct pios_hott_dev
*hott_dev
, uint8_t byte
)
275 struct pios_hott_state
*state
= &(hott_dev
->state
);
277 if (state
->frame_found
) {
278 /* receiving the data frame */
279 if (state
->byte_count
< HOTT_MAX_FRAME_LENGTH
) {
280 /* store next byte */
281 state
->received_data
[state
->byte_count
++] = byte
;
282 if (state
->byte_count
== HOTT_HEADER_LENGTH
) {
283 /* 3rd byte contains the number of channels. calculate frame size */
284 state
->frame_length
= HOTT_OVERHEAD_LENGTH
+ 2 * byte
;
286 if (state
->byte_count
== state
->frame_length
) {
287 uint8_t quality_trend
= 0;
288 /* full frame received - process and wait for new one */
289 if (!PIOS_HOTT_UnrollChannels(hott_dev
)) {
290 /* data looking good */
291 state
->failsafe_timer
= 0;
294 // Calculate quality trend using weighted average of good frames
295 state
->quality
= ((state
->quality
* (HOTT_FL_WEIGHTED_AVERAGE
- 1)) +
296 quality_trend
) / HOTT_FL_WEIGHTED_AVERAGE
;
298 /* prepare for the next frame */
299 state
->frame_found
= 0;
305 /* Initialise HoTT receiver interface */
306 int32_t PIOS_HOTT_Init(uint32_t *hott_id
,
307 const struct pios_com_driver
*driver
,
309 enum pios_hott_proto proto
)
311 PIOS_DEBUG_Assert(hott_id
);
312 PIOS_DEBUG_Assert(driver
);
314 struct pios_hott_dev
*hott_dev
;
316 hott_dev
= (struct pios_hott_dev
*)PIOS_HOTT_Alloc();
321 /* Bind the configuration to the device instance */
322 hott_dev
->proto
= proto
;
324 PIOS_HOTT_ResetState(&(hott_dev
->state
));
326 *hott_id
= (uint32_t)hott_dev
;
328 /* Set comm driver callback */
329 (driver
->bind_rx_cb
)(lower_id
, PIOS_HOTT_RxInCallback
, *hott_id
);
331 if (!PIOS_RTC_RegisterTickCallback(PIOS_HOTT_Supervisor
, *hott_id
)) {
332 PIOS_DEBUG_Assert(0);
338 /* Comm byte received callback */
339 static uint16_t PIOS_HOTT_RxInCallback(uint32_t context
,
345 struct pios_hott_dev
*hott_dev
= (struct pios_hott_dev
*)context
;
347 bool valid
= PIOS_HOTT_Validate(hott_dev
);
351 /* process byte(s) and clear receive timer */
352 for (uint8_t i
= 0; i
< buf_len
; i
++) {
353 PIOS_HOTT_UpdateState(hott_dev
, buf
[i
]);
354 hott_dev
->state
.receive_timer
= 0;
357 /* Always signal that we can accept more data */
359 *headroom
= HOTT_MAX_FRAME_LENGTH
;
362 /* We never need a yield */
365 /* Always indicate that all bytes were consumed */
370 * Get the value of an input channel
371 * \param[in] channel Number of the channel desired (zero based)
372 * \output PIOS_RCVR_INVALID channel not available
373 * \output PIOS_RCVR_TIMEOUT failsafe condition or missing receiver
374 * \output >=0 channel value
376 static int32_t PIOS_HOTT_Get(uint32_t rcvr_id
, uint8_t channel
)
378 struct pios_hott_dev
*hott_dev
= (struct pios_hott_dev
*)rcvr_id
;
380 if (!PIOS_HOTT_Validate(hott_dev
)) {
381 return PIOS_RCVR_INVALID
;
384 /* return error if channel is not available */
385 if (channel
>= PIOS_HOTT_NUM_INPUTS
) {
386 return PIOS_RCVR_INVALID
;
389 /* may also be PIOS_RCVR_TIMEOUT set by other function */
390 return hott_dev
->state
.channel_data
[channel
];
393 static uint8_t PIOS_HOTT_Quality_Get(uint32_t hott_id
)
395 struct pios_hott_dev
*hott_dev
= (struct pios_hott_dev
*)hott_id
;
397 bool valid
= PIOS_HOTT_Validate(hott_dev
);
401 struct pios_hott_state
*state
= &(hott_dev
->state
);
403 return (uint8_t)(state
->quality
+ 0.5f
);
407 * Input data supervisor is called periodically and provides
408 * two functions: frame syncing and failsafe triggering.
410 * HOTT frames come at 11ms or 22ms rate at 115200bps.
411 * RTC timer is running at 625Hz (1.6ms). So with divider 5 it gives
412 * 8ms pause between frames which is good for both HOTT frame rates.
414 * Data receive function must clear the receive_timer to confirm new
415 * data reception. If no new data received in 100ms, we must call the
416 * failsafe function which clears all channels.
418 static void PIOS_HOTT_Supervisor(uint32_t hott_id
)
420 struct pios_hott_dev
*hott_dev
= (struct pios_hott_dev
*)hott_id
;
422 bool valid
= PIOS_HOTT_Validate(hott_dev
);
426 struct pios_hott_state
*state
= &(hott_dev
->state
);
428 /* waiting for new frame if no bytes were received in 8ms */
429 if (++state
->receive_timer
> HOTT_FRAME_TIMEOUT
) {
430 state
->frame_found
= 1;
431 state
->byte_count
= 0;
432 state
->receive_timer
= 0;
433 state
->frame_length
= HOTT_MAX_FRAME_LENGTH
;
436 /* activate failsafe if no frames have arrived in 102.4ms */
437 if (++state
->failsafe_timer
> HOTT_FAILSAFE_TIMEOUT
) {
438 PIOS_HOTT_ResetChannels(state
);
439 state
->failsafe_timer
= 0;
440 state
->tx_connected
= 0;
441 state
->quality
= 0.0f
;
445 #endif /* PIOS_INCLUDE_HOTT */