Line data Source code
1 : #pragma once
2 :
3 : #include "AP_HAL_Namespace.h"
4 : #include <stdint.h>
5 :
6 : #define RC_OUTPUT_MIN_PULSEWIDTH 400
7 : #define RC_OUTPUT_MAX_PULSEWIDTH 2100
8 :
9 : /* Define the CH_n names, indexed from 1, if we don't have them already */
10 : #ifndef CH_1
11 : #define CH_1 0
12 : #define CH_2 1
13 : #define CH_3 2
14 : #define CH_4 3
15 : #define CH_5 4
16 : #define CH_6 5
17 : #define CH_7 6
18 : #define CH_8 7
19 : #define CH_9 8
20 : #define CH_10 9
21 : #define CH_11 10
22 : #define CH_12 11
23 : #define CH_13 12
24 : #define CH_14 13
25 : #define CH_15 14
26 : #define CH_16 15
27 : #define CH_17 16
28 : #define CH_18 17
29 : #define CH_19 18
30 : #define CH_20 19
31 : #define CH_21 20
32 : #define CH_22 21
33 : #define CH_23 22
34 : #define CH_24 23
35 : #define CH_25 24
36 : #define CH_26 25
37 : #define CH_27 26
38 : #define CH_28 27
39 : #define CH_29 28
40 : #define CH_30 29
41 : #define CH_31 30
42 : #define CH_32 31
43 : #define CH_NONE 255
44 : #endif
45 :
46 : class ByteBuffer;
47 :
48 : class ExpandingString;
49 :
50 : class AP_HAL::RCOutput {
51 : public:
52 : virtual void init() = 0;
53 :
54 : /* Output freq (1/period) control */
55 : virtual void set_freq(uint32_t chmask, uint16_t freq_hz) = 0;
56 : virtual uint16_t get_freq(uint8_t chan) = 0;
57 :
58 : /* Output active/highZ control, either by single channel at a time
59 : * or a mask of channels */
60 : virtual void enable_ch(uint8_t chan) = 0;
61 : virtual void disable_ch(uint8_t chan) = 0;
62 :
63 : /*
64 : * Output a single channel, possibly grouped with previous writes if
65 : * cork() has been called before.
66 : */
67 : virtual void write(uint8_t chan, uint16_t period_us) = 0;
68 :
69 : /*
70 : * mark the channels in chanmask as reversible. This is needed for some ESC types (such as DShot)
71 : * so that output scaling can be performed correctly. The chanmask passed is added (ORed) into any existing mask.
72 : * The mask uses servo channel numbering
73 : */
74 2086 : virtual void set_reversible_mask(uint32_t chanmask) {}
75 :
76 : /*
77 : * mark the channels in chanmask as reversed.
78 : * The chanmask passed is added (ORed) into any existing mask.
79 : * The mask uses servo channel numbering
80 : */
81 2086 : virtual void set_reversed_mask(uint32_t chanmask) {}
82 0 : virtual uint32_t get_reversed_mask() { return 0; }
83 :
84 : /*
85 : * Update channel masks at 1Hz allowing for actions such as dshot commands to be sent
86 : */
87 151226 : virtual void update_channel_masks() {}
88 :
89 : /*
90 : * Allow channel mask updates to be temporarily suspended
91 : */
92 0 : virtual void disable_channel_mask_updates() {}
93 0 : virtual void enable_channel_mask_updates() {}
94 :
95 : /*
96 : * Delay subsequent calls to write() going to the underlying hardware in
97 : * order to group related writes together. When all the needed writes are
98 : * done, call push() to commit the changes.
99 : */
100 : virtual void cork() = 0;
101 :
102 : /*
103 : * Push pending changes to the underlying hardware. All changes between a
104 : * call to cork() and push() are pushed together in a single transaction.
105 : */
106 : virtual void push() = 0;
107 :
108 : /* Read back current output state, as either single channel or
109 : * array of channels. On boards that have a separate IO controller,
110 : * this returns the latest output value that the IO controller has
111 : * reported */
112 : virtual uint16_t read(uint8_t chan) = 0;
113 : virtual void read(uint16_t* period_us, uint8_t len) = 0;
114 :
115 : /* Read the current input state. This returns the last value that was written. */
116 0 : virtual uint16_t read_last_sent(uint8_t chan) { return read(chan); }
117 0 : virtual void read_last_sent(uint16_t* period_us, uint8_t len) { read(period_us, len); };
118 :
119 : /*
120 : set PWM to send to a set of channels if the FMU firmware dies
121 : */
122 33251 : virtual void set_failsafe_pwm(uint32_t chmask, uint16_t period_us) {}
123 :
124 : /*
125 : force the safety switch on, disabling PWM output from the IO board
126 : return false (indicating failure) by default so that boards with no safety switch
127 : do not need to implement this method
128 : */
129 0 : virtual bool force_safety_on(void) { return false; }
130 :
131 : /*
132 : force the safety switch off, enabling PWM output from the IO board
133 : */
134 0 : virtual void force_safety_off(void) {}
135 :
136 : /*
137 : setup scaling of ESC output for ESCs that can output a
138 : percentage of power (such as UAVCAN ESCs). The values are in
139 : microseconds, and represent minimum and maximum PWM values which
140 : will be used to convert channel writes into a percentage
141 : */
142 82902 : void set_esc_scaling(uint16_t min_pwm, uint16_t max_pwm) {
143 82902 : _esc_pwm_min = min_pwm;
144 82902 : _esc_pwm_max = max_pwm;
145 82902 : }
146 :
147 : /*
148 : returns the pwm value scaled to [-1;1] regrading to set_esc_scaling ranges range without constraints.
149 : */
150 : float scale_esc_to_unity(uint16_t pwm) const;
151 :
152 : /*
153 : return the erpm and error rate for a channel if available
154 : */
155 0 : virtual uint16_t get_erpm(uint8_t chan) const { return 0; }
156 0 : virtual float get_erpm_error_rate(uint8_t chan) const { return 100.0f; }
157 : /*
158 : allow all erpm values to be read and for new updates to be detected - primarily for IOMCU
159 : */
160 0 : virtual bool new_erpm() { return false; }
161 0 : virtual uint32_t read_erpm(uint16_t* erpm, uint8_t len) { return 0; }
162 :
163 : /*
164 : enable PX4IO SBUS out at the given rate
165 : */
166 0 : virtual bool enable_px4io_sbus_out(uint16_t rate_hz) { return false; }
167 :
168 : /*
169 : * Optional method to control the update of the motors. Derived classes
170 : * can implement it if their HAL layer requires.
171 : */
172 0 : virtual void timer_tick(void) { }
173 :
174 : /*
175 : setup for serial output to an ESC using the given
176 : baudrate. Assumes 1 start bit, 1 stop bit, LSB first and 8
177 : databits. This is used for passthrough ESC configuration and
178 : firmware flashing
179 :
180 : While serial output is active normal output to all channels in
181 : the chanmask is suspended. Output to some other channels (such
182 : as those in the same channel timer groups) may also be stopped,
183 : depending on the implementation
184 : */
185 0 : virtual bool serial_setup_output(uint8_t chan, uint32_t baudrate, uint32_t chanmask) { return false; }
186 :
187 : /*
188 : write a set of bytes to an ESC, using settings from
189 : serial_setup_output. This is a blocking call
190 : */
191 0 : virtual bool serial_write_bytes(const uint8_t *bytes, uint16_t len) { return false; }
192 :
193 : /*
194 : read a series of bytes from a port, using serial parameters from serial_setup_output()
195 : return the number of bytes read. This is a blocking call
196 : */
197 0 : virtual uint16_t serial_read_bytes(uint8_t *buf, uint16_t len, uint32_t timeout_us) { return 0; }
198 :
199 : /*
200 : stop serial output. This restores the previous output mode for
201 : the channel and any other channels that were stopped by
202 : serial_setup_output()
203 : */
204 0 : virtual void serial_end(uint32_t chanmask) {}
205 :
206 : /*
207 : reset serial output. This re-initializes the DMA configuration to that configured by
208 : serial_setup_output()
209 : */
210 0 : virtual void serial_reset(uint32_t chanmask) {}
211 :
212 : /*
213 : output modes. Allows for support of PWM, oneshot and dshot
214 : */
215 : // this enum is used by BLH_OTYPE and ESC_PWM_TYPE on AP_Periph
216 : // double check params are still correct when changing
217 : enum output_mode {
218 : MODE_PWM_NONE,
219 : MODE_PWM_NORMAL,
220 : MODE_PWM_ONESHOT,
221 : MODE_PWM_ONESHOT125,
222 : MODE_PWM_BRUSHED,
223 : MODE_PWM_DSHOT150,
224 : MODE_PWM_DSHOT300,
225 : MODE_PWM_DSHOT600,
226 : MODE_PWM_DSHOT1200,
227 : MODE_NEOPIXEL, // same as MODE_PWM_DSHOT at 800kHz but it's an LED
228 : MODE_PROFILED, // same as MODE_PWM_DSHOT using separate clock and data
229 : MODE_NEOPIXELRGB, // same as MODE_NEOPIXEL but RGB ordering
230 : };
231 : // true when the output mode is of type dshot
232 : // static to allow use in the ChibiOS thread stuff
233 : static bool is_dshot_protocol(const enum output_mode mode);
234 :
235 : static bool is_led_protocol(const enum output_mode mode) {
236 : switch (mode) {
237 : case MODE_NEOPIXEL:
238 : case MODE_NEOPIXELRGB:
239 : case MODE_PROFILED:
240 : return true;
241 : default:
242 : return false;
243 : }
244 : }
245 :
246 : // BLHeli32: https://github.com/bitdump/BLHeli/blob/master/BLHeli_32%20ARM/BLHeli_32%20Firmware%20specs/Digital_Cmd_Spec.txt
247 : // BLHeli_S: https://github.com/bitdump/BLHeli/blob/master/BLHeli_S%20SiLabs/Dshotprog%20spec%20BLHeli_S.txt
248 : enum BLHeliDshotCommand : uint8_t {
249 : DSHOT_RESET = 0,
250 : DSHOT_BEEP1 = 1,
251 : DSHOT_BEEP2 = 2,
252 : DSHOT_BEEP3 = 3,
253 : DSHOT_BEEP4 = 4,
254 : DSHOT_BEEP5 = 5,
255 : DSHOT_ESC_INFO = 6,
256 : DSHOT_ROTATE = 7,
257 : DSHOT_ROTATE_ALTERNATE = 8,
258 : DSHOT_3D_OFF = 9,
259 : DSHOT_3D_ON = 10,
260 : DSHOT_SAVE = 12,
261 : DSHOT_EXTENDED_TELEMETRY_ENABLE = 13,
262 : DSHOT_EXTENDED_TELEMETRY_DISABLE = 14,
263 : DSHOT_NORMAL = 20,
264 : DSHOT_REVERSE = 21,
265 : // The following options are only available on BLHeli32
266 : DSHOT_LED0_ON = 22,
267 : DSHOT_LED1_ON = 23,
268 : DSHOT_LED2_ON = 24,
269 : DSHOT_LED3_ON = 25,
270 : DSHOT_LED0_OFF = 26,
271 : DSHOT_LED1_OFF = 27,
272 : DSHOT_LED2_OFF = 28,
273 : DSHOT_LED3_OFF = 29,
274 : };
275 :
276 : static constexpr uint8_t DSHOT_ZERO_THROTTLE = 48;
277 :
278 : enum DshotEscType {
279 : DSHOT_ESC_NONE = 0,
280 : DSHOT_ESC_BLHELI = 1,
281 : DSHOT_ESC_BLHELI_S = 2,
282 : DSHOT_ESC_BLHELI_EDT = 3,
283 : DSHOT_ESC_BLHELI_EDT_S = 4
284 : };
285 :
286 2565 : virtual void set_output_mode(uint32_t mask, enum output_mode mode) {}
287 :
288 0 : virtual enum output_mode get_output_mode(uint32_t& mask) {
289 0 : mask = 0;
290 0 : return MODE_PWM_NORMAL;
291 : }
292 :
293 :
294 : /*
295 : * get output mode banner to inform user of how outputs are configured
296 : */
297 5104 : virtual bool get_output_mode_banner(char banner_msg[], uint8_t banner_msg_len) const { return false; }
298 :
299 : /*
300 : * return mask of channels that must be disabled because they share a group with a digital channel
301 : */
302 2103 : virtual uint32_t get_disabled_channels(uint32_t digital_mask) { return 0; }
303 :
304 : /*
305 : set default update rate
306 : */
307 151226 : virtual void set_default_rate(uint16_t rate_hz) {}
308 :
309 : /*
310 : enable telemetry request for a mask of channels. This is used
311 : with DShot to get telemetry feedback
312 : */
313 0 : virtual void set_telem_request_mask(uint32_t mask) {}
314 :
315 : /*
316 : enable bi-directional telemetry request for a mask of channels. This is used
317 : with DShot to get telemetry feedback
318 : */
319 0 : virtual void set_bidir_dshot_mask(uint32_t mask) {}
320 :
321 : /*
322 : mark escs as active for the purpose of sending dshot commands
323 : */
324 0 : virtual void set_active_escs_mask(uint32_t mask) {}
325 :
326 : /*
327 : Set the dshot rate as a multiple of the loop rate
328 : */
329 2093 : virtual void set_dshot_rate(uint8_t dshot_rate, uint16_t loop_rate_hz) {}
330 :
331 : /*
332 : Set the dshot period in us, only for use by the IOMCU
333 : */
334 0 : virtual void set_dshot_period(uint32_t period_us, uint8_t dshot_rate) {}
335 0 : virtual uint32_t get_dshot_period_us() const { return 0; }
336 :
337 : /*
338 : Set the dshot ESC type
339 : */
340 5625 : virtual void set_dshot_esc_type(DshotEscType esc_type) {}
341 :
342 0 : virtual DshotEscType get_dshot_esc_type() const { return DSHOT_ESC_NONE; }
343 :
344 : const static uint32_t ALL_CHANNELS = 255;
345 : /*
346 : Send a dshot command, if command timeout is 0 then 10 commands are sent
347 : chan is the servo channel to send the command to
348 : */
349 0 : virtual void send_dshot_command(uint8_t command, uint8_t chan = ALL_CHANNELS, uint32_t command_timeout_ms = 0, uint16_t repeat_count = 10, bool priority = false) {}
350 :
351 : /*
352 : set the number of motor poles to be used in rpm calculations
353 : */
354 0 : virtual void set_motor_poles(uint8_t poles) {}
355 :
356 : /*
357 : setup serial led output for a given channel number, with
358 : the given max number of LEDs in the chain.
359 : */
360 0 : virtual bool set_serial_led_num_LEDs(const uint16_t chan, uint8_t num_leds, output_mode mode = MODE_PWM_NONE, uint32_t clock_mask = 0) { return false; }
361 :
362 : /*
363 : setup serial led output data for a given output channel
364 : and led number. A led number of -1 means all LEDs. LED 0 is the first LED
365 : */
366 0 : virtual bool set_serial_led_rgb_data(const uint16_t chan, int8_t led, uint8_t red, uint8_t green, uint8_t blue) { return false; }
367 :
368 : /*
369 : trigger send of serial led
370 : */
371 0 : virtual bool serial_led_send(const uint16_t chan) { return false; }
372 :
373 0 : virtual void timer_info(ExpandingString &str) {}
374 :
375 : /*
376 : Can this driver handle gpio as well as RC
377 : */
378 0 : virtual bool supports_gpio() { return false; };
379 :
380 : /*
381 : Writes gpio state to a channel
382 : */
383 0 : virtual void write_gpio(uint8_t chan, bool active) {};
384 :
385 : /*
386 : Force group trigger from all callers rather than just from the main thread
387 : */
388 4 : virtual void force_trigger_groups(bool onoff) {};
389 :
390 : /*
391 : * calculate the prescaler required to achieve the desire bitrate
392 : */
393 : static uint32_t calculate_bitrate_prescaler(uint32_t timer_clock, uint32_t target_frequency, bool at_least_freq = false);
394 :
395 : /*
396 : * bit width values for different protocols
397 : */
398 : /*
399 : * It seems ESCs are quite sensitive to the DSHOT duty cycle.
400 : * Options are (ticks, percentage):
401 : * 20/7/14, 35/70
402 : * 11/4/8, 36/72
403 : * 8/3/6, 37/75 <-- this is the preferred duty cycle and has some support on the interwebs
404 : */
405 : // bitwidths: 8/3/6 == 37%/75%
406 : static constexpr uint32_t DSHOT_BIT_WIDTH_TICKS_DEFAULT = 8;
407 : static constexpr uint32_t DSHOT_BIT_0_TICKS_DEFAULT = 3;
408 : static constexpr uint32_t DSHOT_BIT_1_TICKS_DEFAULT = 6;
409 : // bitwidths: 11/4/8 == 36%/72%
410 : static constexpr uint32_t DSHOT_BIT_WIDTH_TICKS_S = 11;
411 : static constexpr uint32_t DSHOT_BIT_0_TICKS_S = 4;
412 : static constexpr uint32_t DSHOT_BIT_1_TICKS_S = 8;
413 :
414 : static uint32_t DSHOT_BIT_WIDTH_TICKS;
415 : static uint32_t DSHOT_BIT_0_TICKS;
416 : static uint32_t DSHOT_BIT_1_TICKS;
417 :
418 : // See WS2812B spec for expected pulse widths
419 : static constexpr uint32_t NEOP_BIT_WIDTH_TICKS = 8;
420 : static constexpr uint32_t NEOP_BIT_0_TICKS = 2;
421 : static constexpr uint32_t NEOP_BIT_1_TICKS = 6;
422 : // neopixel does not use pulse widths at all
423 : static constexpr uint32_t PROFI_BIT_0_TICKS = 7;
424 : static constexpr uint32_t PROFI_BIT_1_TICKS = 14;
425 : static constexpr uint32_t PROFI_BIT_WIDTH_TICKS = 20;
426 :
427 : // suitably long LED output period to support high LED counts
428 : static constexpr uint32_t LED_OUTPUT_PERIOD_US = 10000;
429 :
430 : protected:
431 :
432 : // helper functions for implementation of get_output_mode_banner
433 : void append_to_banner(char banner_msg[], uint8_t banner_msg_len, output_mode out_mode, uint8_t low_ch, uint8_t high_ch) const;
434 : const char* get_output_mode_string(enum output_mode out_mode) const;
435 :
436 : uint16_t _esc_pwm_min = 1000;
437 : uint16_t _esc_pwm_max = 2000;
438 : };
|