PX4 Firmware
PX4 Autopilot Software http://px4.io
pwm.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2013, 2014, 2017 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file pwm.cpp
36  *
37  * PWM servo output configuration and monitoring tool.
38  */
39 
40 #include <px4_platform_common/px4_config.h>
41 #include <px4_platform_common/tasks.h>
42 #include <px4_platform_common/posix.h>
43 #include <px4_platform_common/getopt.h>
44 #include <px4_platform_common/defines.h>
45 #include <px4_platform_common/log.h>
46 #include <px4_platform_common/module.h>
47 #include <px4_platform_common/cli.h>
48 
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <stdbool.h>
53 #include <unistd.h>
54 #include <fcntl.h>
55 #include <poll.h>
56 #include <sys/ioctl.h>
57 #include <sys/stat.h>
58 
59 #ifdef __PX4_NUTTX
60 #include <nuttx/fs/ioctl.h>
61 #endif
62 
63 #include <arch/board/board.h>
64 
65 #include "systemlib/err.h"
66 #include <parameters/param.h>
67 #include "drivers/drv_pwm_output.h"
68 
69 static void usage(const char *reason);
71 __EXPORT int pwm_main(int argc, char *argv[]);
73 
74 
75 static void
76 usage(const char *reason)
77 {
78  if (reason != nullptr) {
79  PX4_WARN("%s", reason);
80  }
81 
82  PRINT_MODULE_DESCRIPTION(
83  R"DESCR_STR(
84 ### Description
85 This command is used to configure PWM outputs for servo and ESC control.
86 
87 The default device `/dev/pwm_output0` are the Main channels, AUX channels are on `/dev/pwm_output1` (`-d` parameter).
88 
89 It is used in the startup script to make sure the PWM parameters (`PWM_*`) are applied (or the ones provided
90 by the airframe config if specified). `pwm info` shows the current settings (the trim value is an offset
91 and configured with `PWM_MAIN_TRIMx` and `PWM_AUX_TRIMx`).
92 
93 The disarmed value should be set such that the motors don't spin (it's also used for the kill switch), at the
94 minimum value they should spin.
95 
96 Channels are assigned to a group. Due to hardware limitations, the update rate can only be set per group. Use
97 `pwm info` to display the groups. If the `-c` argument is used, all channels of any included group must be included.
98 
99 The parameters `-p` and `-r` can be set to a parameter instead of specifying an integer: use -p p:PWM_MIN for example.
100 
101 Note that in OneShot mode, the PWM range [1000, 2000] is automatically mapped to [125, 250].
102 
103 ### Examples
104 Set the PWM rate for all channels to 400 Hz:
105 $ pwm rate -a -r 400
106 
107 Test the outputs of eg. channels 1 and 3, and set the PWM value to 1200 us:
108 $ pwm arm
109 $ pwm test -c 13 -p 1200
110 
111 )DESCR_STR");
112 
113 
114  PRINT_MODULE_USAGE_NAME("pwm", "command");
115  PRINT_MODULE_USAGE_COMMAND_DESCR("arm", "Arm output");
116  PRINT_MODULE_USAGE_COMMAND_DESCR("disarm", "Disarm output");
117 
118  PRINT_MODULE_USAGE_COMMAND_DESCR("info", "Print current configuration of all channels");
119  PRINT_MODULE_USAGE_COMMAND_DESCR("forcefail", "Force Failsafe mode. "
120  "PWM outputs are set to failsafe values.");
121  PRINT_MODULE_USAGE_ARG("on|off", "Turn on or off", false);
122  PRINT_MODULE_USAGE_COMMAND_DESCR("terminatefail", "Enable Termination Failsafe mode. "
123  "While this is true, "
124  "any failsafe that occurs will be unrecoverable (even if recovery conditions are met).");
125  PRINT_MODULE_USAGE_ARG("on|off", "Turn on or off", false);
126 
127  PRINT_MODULE_USAGE_COMMAND_DESCR("rate", "Configure PWM rates");
128  PRINT_MODULE_USAGE_PARAM_INT('r', -1, 50, 400, "PWM Rate in Hz (0 = Oneshot, otherwise 50 to 400Hz)", false);
129 
130  PRINT_MODULE_USAGE_COMMAND_DESCR("oneshot", "Configure Oneshot125 (rate is set to 0)");
131 
132  PRINT_MODULE_USAGE_COMMAND_DESCR("failsafe", "Set Failsafe PWM value");
133  PRINT_MODULE_USAGE_COMMAND_DESCR("disarmed", "Set Disarmed PWM value");
134  PRINT_MODULE_USAGE_COMMAND_DESCR("min", "Set Minimum PWM value");
135  PRINT_MODULE_USAGE_COMMAND_DESCR("max", "Set Maximum PWM value");
136  PRINT_MODULE_USAGE_COMMAND_DESCR("test", "Set Output to a specific value until 'q' or 'c' or 'ctrl-c' pressed");
137 
138  PRINT_MODULE_USAGE_COMMAND_DESCR("steps", "Run 5 steps from 0 to 100%");
139 
140 
141  PRINT_MODULE_USAGE_PARAM_COMMENT("The commands 'failsafe', 'disarmed', 'min', 'max' and 'test' require a PWM value:");
142  PRINT_MODULE_USAGE_PARAM_INT('p', -1, 0, 4000, "PWM value (eg. 1100)", false);
143 
144  PRINT_MODULE_USAGE_PARAM_COMMENT("The commands 'rate', 'oneshot', 'failsafe', 'disarmed', 'min', 'max', 'test' and 'steps' "
145  "additionally require to specify the channels with one of the following commands:");
146  PRINT_MODULE_USAGE_PARAM_STRING('c', nullptr, nullptr, "select channels in the form: 1234 (1 digit per channel, 1=first)",
147  true);
148  PRINT_MODULE_USAGE_PARAM_INT('m', -1, 0, 4096, "Select channels via bitmask (eg. 0xF, 3)", true);
149  PRINT_MODULE_USAGE_PARAM_INT('g', -1, 0, 10, "Select channels by group (eg. 0, 1, 2. use 'pwm info' to show groups)",
150  true);
151  PRINT_MODULE_USAGE_PARAM_FLAG('a', "Select all channels", true);
152 
153  PRINT_MODULE_USAGE_PARAM_COMMENT("These parameters apply to all commands:");
154  PRINT_MODULE_USAGE_PARAM_STRING('d', "/dev/pwm_output0", "<file:dev>", "Select PWM output device", true);
155  PRINT_MODULE_USAGE_PARAM_FLAG('v', "Verbose output", true);
156  PRINT_MODULE_USAGE_PARAM_FLAG('e', "Exit with 1 instead of 0 on error", true);
157 
158 }
159 
160 int
161 pwm_main(int argc, char *argv[])
162 {
163  const char *dev = PWM_OUTPUT0_DEVICE_PATH;
164  int alt_rate = -1; // Default to indicate not set.
165  uint32_t alt_channel_groups = 0;
166  bool alt_channels_set = false;
167  bool print_verbose = false;
168  bool error_on_warn = false;
169  bool oneshot = false;
170  int ch;
171  int ret;
172  int rv = 1;
173  char *ep;
174  uint32_t set_mask = 0;
175  unsigned group;
176  unsigned long channels;
177  unsigned single_ch = 0;
178  int pwm_value = 0;
179 
180  if (argc < 2) {
181  usage(nullptr);
182  return 1;
183  }
184 
185  int myoptind = 1;
186  const char *myoptarg = nullptr;
187 
188  while ((ch = px4_getopt(argc, argv, "d:vec:g:m:ap:r:", &myoptind, &myoptarg)) != EOF) {
189  switch (ch) {
190 
191  case 'd':
192  if (nullptr == strstr(myoptarg, "/dev/")) {
193  PX4_WARN("device %s not valid", myoptarg);
194  usage(nullptr);
195  return 1;
196  }
197 
198  dev = myoptarg;
199  break;
200 
201  case 'v':
202  print_verbose = true;
203  break;
204 
205  case 'e':
206  error_on_warn = true;
207  break;
208 
209  case 'c':
210  /* Read in channels supplied as one int and convert to mask: 1234 -> 0xF */
211  channels = strtoul(myoptarg, &ep, 0);
212 
213  while ((single_ch = channels % 10)) {
214 
215  set_mask |= 1 << (single_ch - 1);
216  channels /= 10;
217  }
218 
219  break;
220 
221  case 'g':
222  group = strtoul(myoptarg, &ep, 0);
223 
224  if ((*ep != '\0') || (group >= 32)) {
225  usage("bad channel_group value");
226  return 1;
227  }
228 
229  alt_channel_groups |= (1 << group);
230  alt_channels_set = true;
231  break;
232 
233  case 'm':
234  /* Read in mask directly */
235  set_mask = strtoul(myoptarg, &ep, 0);
236 
237  if (*ep != '\0') {
238  usage("BAD set_mask VAL");
239  return 1;
240  }
241 
242  break;
243 
244  case 'a':
245  for (unsigned i = 0; i < PWM_OUTPUT_MAX_CHANNELS; i++) {
246  set_mask |= 1 << i;
247  }
248 
249  break;
250 
251  case 'p':
252  if (px4_get_parameter_value(myoptarg, pwm_value) != 0) {
253  PX4_ERR("CLI argument parsing for PWM value failed");
254  return 1;
255  }
256  break;
257 
258  case 'r':
259  if (px4_get_parameter_value(myoptarg, alt_rate) != 0) {
260  PX4_ERR("CLI argument parsing for PWM rate failed");
261  return 1;
262  }
263  break;
264 
265  default:
266  usage(nullptr);
267  return 1;
268  }
269  }
270 
271  if (myoptind >= argc) {
272  usage(nullptr);
273  return 1;
274  }
275 
276  const char *command = argv[myoptind];
277 
278  if (print_verbose && set_mask > 0) {
279  PX4_INFO("Channels: ");
280  printf(" ");
281 
282  for (unsigned i = 0; i < PWM_OUTPUT_MAX_CHANNELS; i++) {
283  if (set_mask & 1 << i) {
284  printf("%d ", i + 1);
285  }
286  }
287 
288  printf("\n");
289  }
290 
291  /* open for ioctl only */
292  int fd = px4_open(dev, 0);
293 
294  if (fd < 0) {
295  PX4_ERR("can't open %s", dev);
296  return 1;
297  }
298 
299  /* get the number of servo channels */
300  unsigned servo_count;
301  ret = px4_ioctl(fd, PWM_SERVO_GET_COUNT, (unsigned long)&servo_count);
302 
303  if (ret != OK) {
304  PX4_ERR("PWM_SERVO_GET_COUNT");
305  return error_on_warn;
306  }
307 
308  oneshot = !strcmp(command, "oneshot");
309 
310  if (!strcmp(command, "arm")) {
311  /* tell safety that its ok to disable it with the switch */
312  ret = px4_ioctl(fd, PWM_SERVO_SET_ARM_OK, 0);
313 
314  if (ret != OK) {
315  err(1, "PWM_SERVO_SET_ARM_OK");
316  }
317 
318  /* tell IO that the system is armed (it will output values if safety is off) */
319  ret = px4_ioctl(fd, PWM_SERVO_ARM, 0);
320 
321  if (ret != OK) {
322  err(1, "PWM_SERVO_ARM");
323  }
324 
325  if (print_verbose) {
326  PX4_INFO("Outputs armed");
327  }
328 
329  return 0;
330 
331  } else if (!strcmp(command, "disarm")) {
332  /* disarm, but do not revoke the SET_ARM_OK flag */
333  ret = px4_ioctl(fd, PWM_SERVO_DISARM, 0);
334 
335  if (ret != OK) {
336  err(1, "PWM_SERVO_DISARM");
337  }
338 
339  if (print_verbose) {
340  PX4_INFO("Outputs disarmed");
341  }
342 
343  return 0;
344 
345  } else if (oneshot || !strcmp(command, "rate")) {
346 
347  /* Change alternate PWM rate or set oneshot
348  * Either the "oneshot" command was used
349  * and/OR -r was provided on command line and has changed the alt_rate
350  * to the non default of -1, so we will issue the PWM_SERVO_SET_UPDATE_RATE
351  * ioctl
352  */
353 
354  if (oneshot || alt_rate >= 0) {
355  ret = px4_ioctl(fd, PWM_SERVO_SET_UPDATE_RATE, oneshot ? 0 : alt_rate);
356 
357  if (ret != OK) {
358  PX4_ERR("PWM_SERVO_SET_UPDATE_RATE (check rate for sanity)");
359  return error_on_warn;
360  }
361  }
362 
363  /* directly supplied channel mask */
364  if (set_mask > 0) {
365  ret = px4_ioctl(fd, PWM_SERVO_SET_SELECT_UPDATE_RATE, set_mask);
366 
367  if (ret != OK) {
368  PX4_ERR("PWM_SERVO_SET_SELECT_UPDATE_RATE");
369  return error_on_warn;
370  }
371  }
372 
373  /* assign alternate rate to channel groups */
374  if (alt_channels_set) {
375  uint32_t mask = 0;
376 
377  for (group = 0; group < 32; group++) {
378  if ((1 << group) & alt_channel_groups) {
379  uint32_t group_mask;
380 
381  ret = px4_ioctl(fd, PWM_SERVO_GET_RATEGROUP(group), (unsigned long)&group_mask);
382 
383  if (ret != OK) {
384  PX4_ERR("PWM_SERVO_GET_RATEGROUP(%u)", group);
385  return error_on_warn;
386  }
387 
388  mask |= group_mask;
389  }
390  }
391 
393 
394  if (ret != OK) {
395  PX4_ERR("PWM_SERVO_SET_SELECT_UPDATE_RATE");
396  return error_on_warn;
397  }
398  }
399 
400  return 0;
401 
402  } else if (!strcmp(command, "min")) {
403 
404  if (set_mask == 0) {
405  usage("min: no channels set");
406  return 1;
407  }
408 
409  if (pwm_value < 0) {
410  return 0;
411  }
412 
413  if (pwm_value == 0) {
414  usage("min: no PWM value provided");
415  return 1;
416  }
417 
418  struct pwm_output_values pwm_values {};
419 
420  pwm_values.channel_count = servo_count;
421 
422  /* first get current state before modifying it */
423  ret = px4_ioctl(fd, PWM_SERVO_GET_MIN_PWM, (long unsigned int)&pwm_values);
424 
425  if (ret != OK) {
426  PX4_ERR("failed get min values");
427  return 1;
428  }
429 
430  for (unsigned i = 0; i < servo_count; i++) {
431  if (set_mask & 1 << i) {
432  pwm_values.values[i] = pwm_value;
433 
434  if (print_verbose) {
435  PX4_INFO("Channel %d: min PWM: %d", i + 1, pwm_value);
436  }
437  }
438  }
439 
440  if (pwm_values.channel_count == 0) {
441  usage("min: no channels provided");
442  return 1;
443 
444  } else {
445 
446  ret = px4_ioctl(fd, PWM_SERVO_SET_MIN_PWM, (long unsigned int)&pwm_values);
447 
448  if (ret != OK) {
449  PX4_ERR("failed setting min values (%d)", ret);
450  return error_on_warn;
451  }
452  }
453 
454  return 0;
455 
456  } else if (!strcmp(command, "max")) {
457 
458  if (set_mask == 0) {
459  usage("no channels set");
460  return 1;
461  }
462 
463  if (pwm_value < 0) {
464  return 0;
465  }
466 
467  if (pwm_value == 0) {
468  usage("no PWM value provided");
469  return 1;
470  }
471 
472  struct pwm_output_values pwm_values {};
473 
474  pwm_values.channel_count = servo_count;
475 
476  /* first get current state before modifying it */
477  ret = px4_ioctl(fd, PWM_SERVO_GET_MAX_PWM, (long unsigned int)&pwm_values);
478 
479  if (ret != OK) {
480  PX4_ERR("failed get max values");
481  return 1;
482  }
483 
484  for (unsigned i = 0; i < servo_count; i++) {
485  if (set_mask & 1 << i) {
486  pwm_values.values[i] = pwm_value;
487 
488  if (print_verbose) {
489  PX4_INFO("Channel %d: max PWM: %d", i + 1, pwm_value);
490  }
491  }
492  }
493 
494  if (pwm_values.channel_count == 0) {
495  usage("max: no PWM channels");
496  return 1;
497 
498  } else {
499 
500  ret = px4_ioctl(fd, PWM_SERVO_SET_MAX_PWM, (long unsigned int)&pwm_values);
501 
502  if (ret != OK) {
503  PX4_ERR("failed setting max values (%d)", ret);
504  return error_on_warn;
505  }
506  }
507 
508  return 0;
509 
510  } else if (!strcmp(command, "disarmed")) {
511 
512  if (set_mask == 0) {
513  usage("no channels set");
514  return 1;
515  }
516 
517  if (pwm_value < 0) {
518  return 0;
519  }
520 
521  if (pwm_value == 0) {
522  PX4_WARN("reading disarmed value of zero, disabling disarmed PWM");
523  }
524 
525  struct pwm_output_values pwm_values {};
526 
527  pwm_values.channel_count = servo_count;
528 
529  /* first get current state before modifying it */
530  ret = px4_ioctl(fd, PWM_SERVO_GET_DISARMED_PWM, (long unsigned int)&pwm_values);
531 
532  if (ret != OK) {
533  PX4_ERR("failed get disarmed values");
534  return ret;
535  }
536 
537  for (unsigned i = 0; i < servo_count; i++) {
538  if (set_mask & 1 << i) {
539  pwm_values.values[i] = pwm_value;
540 
541  if (print_verbose) {
542  PX4_INFO("chan %d: disarmed PWM: %d", i + 1, pwm_value);
543  }
544  }
545  }
546 
547  if (pwm_values.channel_count == 0) {
548  usage("disarmed: no PWM channels");
549  return 1;
550 
551  } else {
552 
553  ret = px4_ioctl(fd, PWM_SERVO_SET_DISARMED_PWM, (long unsigned int)&pwm_values);
554 
555  if (ret != OK) {
556  PX4_ERR("failed setting disarmed values (%d)", ret);
557  return error_on_warn;
558  }
559  }
560 
561  return 0;
562 
563  } else if (!strcmp(command, "failsafe")) {
564 
565  if (set_mask == 0) {
566  usage("no channels set");
567  return 1;
568  }
569 
570  if (pwm_value < 0) {
571  return 0;
572  }
573 
574  if (pwm_value == 0) {
575  usage("failsafe: no PWM provided");
576  return 1;
577  }
578 
579  struct pwm_output_values pwm_values {};
580 
581  pwm_values.channel_count = servo_count;
582 
583  /* first get current state before modifying it */
584  ret = px4_ioctl(fd, PWM_SERVO_GET_FAILSAFE_PWM, (long unsigned int)&pwm_values);
585 
586  if (ret != OK) {
587  PX4_ERR("failed get failsafe values");
588  return 1;
589  }
590 
591  for (unsigned i = 0; i < servo_count; i++) {
592  if (set_mask & 1 << i) {
593  pwm_values.values[i] = pwm_value;
594 
595  if (print_verbose) {
596  PX4_INFO("Channel %d: failsafe PWM: %d", i + 1, pwm_value);
597  }
598  }
599  }
600 
601  if (pwm_values.channel_count == 0) {
602  usage("failsafe: no PWM channels");
603  return 1;
604 
605  } else {
606 
607  ret = px4_ioctl(fd, PWM_SERVO_SET_FAILSAFE_PWM, (long unsigned int)&pwm_values);
608 
609  if (ret != OK) {
610  PX4_ERR("BAD input VAL");
611  return 1;
612  }
613  }
614 
615  return 0;
616 
617  } else if (!strcmp(command, "test")) {
618 
619  if (set_mask == 0) {
620  usage("no channels set");
621  return 1;
622  }
623 
624  if (pwm_value == 0) {
625  usage("no PWM provided");
626  return 1;
627  }
628 
629  /* get current servo values */
630  struct pwm_output_values last_spos;
631 
632  for (unsigned i = 0; i < servo_count; i++) {
633 
634 
635  ret = px4_ioctl(fd, PWM_SERVO_GET(i), (unsigned long)&last_spos.values[i]);
636 
637  if (ret != OK) {
638  PX4_ERR("PWM_SERVO_GET(%d)", i);
639  return 1;
640  }
641  }
642 
643  /* perform PWM output */
644 
645  /* Open console directly to grab CTRL-C signal */
646  struct pollfd fds;
647  fds.fd = 0; /* stdin */
648  fds.events = POLLIN;
649 
650  if (::ioctl(fd, PWM_SERVO_SET_MODE, PWM_SERVO_ENTER_TEST_MODE) < 0) {
651  PX4_ERR("Failed to Enter pwm test mode");
652  goto err_out_no_test;
653  }
654 
655  PX4_INFO("Press CTRL-C or 'c' to abort.");
656 
657  while (1) {
658  for (unsigned i = 0; i < servo_count; i++) {
659  if (set_mask & 1 << i) {
660  ret = px4_ioctl(fd, PWM_SERVO_SET(i), pwm_value);
661 
662  if (ret != OK) {
663  PX4_ERR("PWM_SERVO_SET(%d)", i);
664  goto err_out;
665  }
666  }
667  }
668 
669  /* abort on user request */
670  char c;
671  ret = poll(&fds, 1, 0);
672 
673  if (ret > 0) {
674 
675  ret = read(0, &c, 1);
676 
677  if (c == 0x03 || c == 0x63 || c == 'q') {
678  /* reset output to the last value */
679  for (unsigned i = 0; i < servo_count; i++) {
680  if (set_mask & 1 << i) {
681  ret = px4_ioctl(fd, PWM_SERVO_SET(i), last_spos.values[i]);
682 
683  if (ret != OK) {
684  PX4_ERR("PWM_SERVO_SET(%d)", i);
685  goto err_out;
686  }
687  }
688  }
689 
690  PX4_INFO("User abort\n");
691  rv = 0;
692  goto err_out;
693  }
694  }
695 
696  /* Delay longer than the max Oneshot duration */
697 
698  px4_usleep(2542);
699 
700 #ifdef __PX4_NUTTX
701  /* Trigger all timer's channels in Oneshot mode to fire
702  * the oneshots with updated values.
703  */
704 
705  up_pwm_update();
706 #endif
707  }
708  rv = 0;
709 err_out:
710  if (::ioctl(fd, PWM_SERVO_SET_MODE, PWM_SERVO_EXIT_TEST_MODE) < 0) {
711  rv = 1;
712  PX4_ERR("Failed to Exit pwm test mode");
713  }
714 
715 err_out_no_test:
716  return rv;
717 
718 
719  } else if (!strcmp(command, "steps")) {
720 
721  if (set_mask == 0) {
722  usage("no channels set");
723  return 1;
724  }
725 
726  /* get current servo values */
727  struct pwm_output_values last_spos;
728 
729  for (unsigned i = 0; i < servo_count; i++) {
730 
731  ret = px4_ioctl(fd, PWM_SERVO_GET(i), (unsigned long)&last_spos.values[i]);
732 
733  if (ret != OK) {
734  PX4_ERR("PWM_SERVO_GET(%d)", i);
735  return 1;
736  }
737  }
738 
739  /* perform PWM output */
740 
741  /* Open console directly to grab CTRL-C signal */
742  struct pollfd fds;
743  fds.fd = 0; /* stdin */
744  fds.events = POLLIN;
745 
746  PX4_WARN("Running 5 steps. WARNING! Motors will be live in 5 seconds\nPress any key to abort now.");
747  px4_sleep(5);
748 
749  if (::ioctl(fd, PWM_SERVO_SET_MODE, PWM_SERVO_ENTER_TEST_MODE) < 0) {
750  PX4_ERR("Failed to Enter pwm test mode");
751  goto err_out_no_test;
752  }
753 
754  unsigned off = 900;
755  unsigned idle = 1300;
756  unsigned full = 2000;
757  unsigned steps_timings_us[] = {2000, 5000, 20000, 50000};
758 
759  unsigned phase = 0;
760  unsigned phase_counter = 0;
761  unsigned const phase_maxcount = 20;
762 
763  for (unsigned steps_timing_index = 0;
764  steps_timing_index < sizeof(steps_timings_us) / sizeof(steps_timings_us[0]);
765  steps_timing_index++) {
766 
767  PX4_INFO("Step input (0 to 100%%) over %u us ramp", steps_timings_us[steps_timing_index]);
768 
769  while (1) {
770  for (unsigned i = 0; i < servo_count; i++) {
771  if (set_mask & 1 << i) {
772 
773  unsigned val;
774 
775  if (phase == 0) {
776  val = idle;
777 
778  } else if (phase == 1) {
779  /* ramp - depending how steep it is this ramp will look instantaneous on the output */
780  val = idle + (full - idle) * ((float)phase_counter / phase_maxcount);
781 
782  } else {
783  val = off;
784  }
785 
786  ret = px4_ioctl(fd, PWM_SERVO_SET(i), val);
787 
788  if (ret != OK) {
789  PX4_ERR("PWM_SERVO_SET(%d)", i);
790  goto err_out;
791  }
792  }
793  }
794 
795  /* abort on user request */
796  char c;
797  ret = poll(&fds, 1, 0);
798 
799  if (ret > 0) {
800 
801  ret = read(0, &c, 1);
802 
803  if (ret > 0) {
804  /* reset output to the last value */
805  for (unsigned i = 0; i < servo_count; i++) {
806  if (set_mask & 1 << i) {
807  ret = px4_ioctl(fd, PWM_SERVO_SET(i), last_spos.values[i]);
808 
809  if (ret != OK) {
810  PX4_ERR("PWM_SERVO_SET(%d)", i);
811  goto err_out;
812  }
813  }
814  }
815 
816  PX4_INFO("User abort\n");
817  rv = 0;
818  goto err_out;
819  }
820  }
821 
822  if (phase == 1) {
823  px4_usleep(steps_timings_us[steps_timing_index] / phase_maxcount);
824 
825  } else if (phase == 0) {
826  px4_usleep(50000);
827 
828  } else if (phase == 2) {
829  px4_usleep(50000);
830 
831  } else {
832  break;
833  }
834 
835  phase_counter++;
836 
837  if (phase_counter > phase_maxcount) {
838  phase++;
839  phase_counter = 0;
840  }
841  }
842  }
843 
844  rv = 0;
845  goto err_out;
846 
847 
848  } else if (!strcmp(command, "info")) {
849 
850  printf("device: %s\n", dev);
851 
852  uint32_t info_default_rate;
853  uint32_t info_alt_rate;
854  uint32_t info_alt_rate_mask;
855 
856  ret = px4_ioctl(fd, PWM_SERVO_GET_DEFAULT_UPDATE_RATE, (unsigned long)&info_default_rate);
857 
858  if (ret != OK) {
859  PX4_ERR("PWM_SERVO_GET_DEFAULT_UPDATE_RATE");
860  return 1;
861  }
862 
863  ret = px4_ioctl(fd, PWM_SERVO_GET_UPDATE_RATE, (unsigned long)&info_alt_rate);
864 
865  if (ret != OK) {
866  PX4_ERR("PWM_SERVO_GET_UPDATE_RATE");
867  return 1;
868  }
869 
870  ret = px4_ioctl(fd, PWM_SERVO_GET_SELECT_UPDATE_RATE, (unsigned long)&info_alt_rate_mask);
871 
872  if (ret != OK) {
873  PX4_ERR("PWM_SERVO_GET_SELECT_UPDATE_RATE");
874  return 1;
875  }
876 
877  struct pwm_output_values failsafe_pwm;
878 
879  struct pwm_output_values disarmed_pwm;
880 
881  struct pwm_output_values min_pwm;
882 
883  struct pwm_output_values max_pwm;
884 
885  struct pwm_output_values trim_pwm;
886 
887  ret = px4_ioctl(fd, PWM_SERVO_GET_FAILSAFE_PWM, (unsigned long)&failsafe_pwm);
888 
889  if (ret != OK) {
890  PX4_ERR("PWM_SERVO_GET_FAILSAFE_PWM");
891  return 1;
892  }
893 
894  ret = px4_ioctl(fd, PWM_SERVO_GET_DISARMED_PWM, (unsigned long)&disarmed_pwm);
895 
896  if (ret != OK) {
897  PX4_ERR("PWM_SERVO_GET_DISARMED_PWM");
898  return 1;
899  }
900 
901  ret = px4_ioctl(fd, PWM_SERVO_GET_MIN_PWM, (unsigned long)&min_pwm);
902 
903  if (ret != OK) {
904  PX4_ERR("PWM_SERVO_GET_MIN_PWM");
905  return 1;
906  }
907 
908  ret = px4_ioctl(fd, PWM_SERVO_GET_MAX_PWM, (unsigned long)&max_pwm);
909 
910  if (ret != OK) {
911  PX4_ERR("PWM_SERVO_GET_MAX_PWM");
912  return 1;
913  }
914 
915  ret = px4_ioctl(fd, PWM_SERVO_GET_TRIM_PWM, (unsigned long)&trim_pwm);
916 
917  if (ret != OK) {
918  PX4_ERR("PWM_SERVO_GET_TRIM_PWM");
919  return 1;
920  }
921 
922  /* print current servo values */
923  for (unsigned i = 0; i < servo_count; i++) {
924  servo_position_t spos;
925 
926  ret = px4_ioctl(fd, PWM_SERVO_GET(i), (unsigned long)&spos);
927 
928  if (ret == OK) {
929  printf("channel %u: %u us", i + 1, spos);
930 
931  if (info_alt_rate_mask & (1 << i)) {
932  printf(" (alternative rate: %d Hz", info_alt_rate);
933 
934  } else {
935  printf(" (default rate: %d Hz", info_default_rate);
936  }
937 
938 
939  printf(" failsafe: %d, disarmed: %d us, min: %d us, max: %d us, trim: %5.2f)",
940  failsafe_pwm.values[i], disarmed_pwm.values[i], min_pwm.values[i], max_pwm.values[i],
941  (double)((int16_t)(trim_pwm.values[i]) / 10000.0f));
942  printf("\n");
943 
944  } else {
945  printf("%u: ERROR\n", i);
946  }
947  }
948 
949  /* print rate groups */
950  for (unsigned i = 0; i < servo_count; i++) {
951  uint32_t group_mask;
952 
953  ret = px4_ioctl(fd, PWM_SERVO_GET_RATEGROUP(i), (unsigned long)&group_mask);
954 
955  if (ret != OK) {
956  break;
957  }
958 
959  if (group_mask != 0) {
960  printf("channel group %u: channels", i);
961 
962  for (unsigned j = 0; j < 32; j++) {
963  if (group_mask & (1 << j)) {
964  printf(" %u", j + 1);
965  }
966  }
967 
968  printf("\n");
969  }
970  }
971 
972  return 0;
973 
974  } else if (!strcmp(command, "forcefail")) {
975 
976  if (argc < 3) {
977  PX4_ERR("arg missing [on|off]");
978  return 1;
979 
980  } else {
981 
982  if (!strcmp(argv[2], "on")) {
983  /* force failsafe */
985 
986  } else {
987  /* disable failsafe */
989  }
990 
991  if (ret != OK) {
992  PX4_ERR("FAILED setting forcefail %s", argv[2]);
993  }
994  }
995 
996  return 0;
997 
998  } else if (!strcmp(command, "terminatefail")) {
999 
1000  if (argc < 3) {
1001  PX4_ERR("arg missing [on|off]");
1002  return 1;
1003 
1004  } else {
1005 
1006  if (!strcmp(argv[2], "on")) {
1007  /* force failsafe */
1009 
1010  } else {
1011  /* disable failsafe */
1013  }
1014 
1015  if (ret != OK) {
1016  PX4_ERR("FAILED setting termination failsafe %s", argv[2]);
1017  }
1018  }
1019 
1020  return 0;
1021  }
1022 
1023  usage(nullptr);
1024  return 0;
1025 }
#define PWM_SERVO_GET_SELECT_UPDATE_RATE
check the selected update rates
#define PWM_SERVO_SET_ARM_OK
set the &#39;ARM ok&#39; bit, which activates the safety switch
uint16_t servo_position_t
Servo output signal type, value is actual servo output pulse width in microseconds.
#define __END_DECLS
Definition: visibility.h:59
#define PWM_SERVO_GET_COUNT
get the number of servos in *(unsigned *)arg
#define PWM_SERVO_ARM
arm all servo outputs handle by this driver
Definition: I2C.hpp:51
static void usage(const char *reason)
Definition: pwm.cpp:76
#define PWM_SERVO_GET_UPDATE_RATE
get alternate servo update rate
#define PWM_SERVO_GET_DISARMED_PWM
get the PWM value when disarmed
#define PWM_SERVO_SET_SELECT_UPDATE_RATE
selects servo update rates, one bit per servo.
Global flash based parameter store.
#define PWM_SERVO_SET(_servo)
set a single servo to a specific value
static void read(bootloader_app_shared_t *pshared)
#define PWM_SERVO_GET_DEFAULT_UPDATE_RATE
get default servo update rate
#define PWM_OUTPUT0_DEVICE_PATH
__BEGIN_DECLS __EXPORT int pwm_main(int argc, char *argv[])
Definition: pwm.cpp:161
#define PWM_SERVO_SET_FAILSAFE_PWM
set the PWM value for failsafe
uint16_t values[PWM_OUTPUT_MAX_CHANNELS]
#define PWM_SERVO_SET_DISARMED_PWM
set the PWM value when disarmed - should be no PWM (zero) by default
#define PWM_SERVO_SET_TERMINATION_FAILSAFE
make failsafe non-recoverable (termination) if it occurs
#define __BEGIN_DECLS
Definition: visibility.h:58
#define PWM_SERVO_ENTER_TEST_MODE
uint32_t channel_count
#define PWM_SERVO_GET_TRIM_PWM
get the TRIM value the output will send
#define PWM_OUTPUT_MAX_CHANNELS
Simple error/warning functions, heavily inspired by the BSD functions of the same names...
#define PWM_SERVO_GET_RATEGROUP(_n)
get the _n&#39;th rate group&#39;s channels; *(uint32_t *)arg returns a bitmap of channels whose update rates...
#define PWM_SERVO_GET(_servo)
get a single specific servo value
int fd
Definition: dataman.cpp:146
#define PWM_SERVO_SET_MIN_PWM
set the minimum PWM value the output will send
#define err(eval,...)
Definition: err.h:83
#define PWM_SERVO_SET_MAX_PWM
set the maximum PWM value the output will send
#define PWM_SERVO_SET_MODE
int px4_open(const char *path, int flags,...)
#define PWM_SERVO_SET_FORCE_FAILSAFE
force failsafe mode (failsafe values are set immediately even if failsafe condition not met) ...
#define PWM_SERVO_GET_MIN_PWM
get the minimum PWM value the output will send
#define OK
Definition: uavcan_main.cpp:71
#define PWM_SERVO_GET_FAILSAFE_PWM
get the PWM value for failsafe
__EXPORT void up_pwm_update(void)
Trigger all timer&#39;s channels in Oneshot mode to fire the oneshot with updated values.
#define PWM_SERVO_DISARM
disarm all servo outputs (stop generating pulses)
#define PWM_SERVO_GET_MAX_PWM
get the maximum PWM value the output will send
#define PWM_SERVO_SET_UPDATE_RATE
set alternate servo update rate
#define PWM_SERVO_EXIT_TEST_MODE
int px4_ioctl(int fd, int cmd, unsigned long arg)