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> 56 #include <sys/ioctl.h> 60 #include <nuttx/fs/ioctl.h> 63 #include <arch/board/board.h> 69 static void usage(
const char *reason);
78 if (reason !=
nullptr) {
79 PX4_WARN(
"%s", reason);
82 PRINT_MODULE_DESCRIPTION(
85 This command is used to configure PWM outputs for servo and ESC control. 87 The default device `/dev/pwm_output0` are the Main channels, AUX channels are on `/dev/pwm_output1` (`-d` parameter). 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`). 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. 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. 99 The parameters `-p` and `-r` can be set to a parameter instead of specifying an integer: use -p p:PWM_MIN for example. 101 Note that in OneShot mode, the PWM range [1000, 2000] is automatically mapped to [125, 250]. 104 Set the PWM rate for all channels to 400 Hz: 107 Test the outputs of eg. channels 1 and 3, and set the PWM value to 1200 us: 109 $ pwm test -c 13 -p 1200 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");
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);
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);
130 PRINT_MODULE_USAGE_COMMAND_DESCR(
"oneshot",
"Configure Oneshot125 (rate is set to 0)");
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");
138 PRINT_MODULE_USAGE_COMMAND_DESCR(
"steps",
"Run 5 steps from 0 to 100%");
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);
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)",
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)",
151 PRINT_MODULE_USAGE_PARAM_FLAG(
'a',
"Select all channels",
true);
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);
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;
174 uint32_t set_mask = 0;
176 unsigned long channels;
177 unsigned single_ch = 0;
186 const char *myoptarg =
nullptr;
188 while ((ch = px4_getopt(argc, argv,
"d:vec:g:m:ap:r:", &myoptind, &myoptarg)) != EOF) {
192 if (
nullptr == strstr(myoptarg,
"/dev/")) {
193 PX4_WARN(
"device %s not valid", myoptarg);
202 print_verbose =
true;
206 error_on_warn =
true;
211 channels = strtoul(myoptarg, &ep, 0);
213 while ((single_ch = channels % 10)) {
215 set_mask |= 1 << (single_ch - 1);
222 group = strtoul(myoptarg, &ep, 0);
224 if ((*ep !=
'\0') || (group >= 32)) {
225 usage(
"bad channel_group value");
229 alt_channel_groups |= (1 << group);
230 alt_channels_set =
true;
235 set_mask = strtoul(myoptarg, &ep, 0);
238 usage(
"BAD set_mask VAL");
252 if (px4_get_parameter_value(myoptarg, pwm_value) != 0) {
253 PX4_ERR(
"CLI argument parsing for PWM value failed");
259 if (px4_get_parameter_value(myoptarg, alt_rate) != 0) {
260 PX4_ERR(
"CLI argument parsing for PWM rate failed");
271 if (myoptind >= argc) {
276 const char *
command = argv[myoptind];
278 if (print_verbose && set_mask > 0) {
279 PX4_INFO(
"Channels: ");
283 if (set_mask & 1 << i) {
284 printf(
"%d ", i + 1);
295 PX4_ERR(
"can't open %s", dev);
300 unsigned servo_count;
304 PX4_ERR(
"PWM_SERVO_GET_COUNT");
305 return error_on_warn;
308 oneshot = !strcmp(command,
"oneshot");
310 if (!strcmp(command,
"arm")) {
315 err(1,
"PWM_SERVO_SET_ARM_OK");
322 err(1,
"PWM_SERVO_ARM");
326 PX4_INFO(
"Outputs armed");
331 }
else if (!strcmp(command,
"disarm")) {
336 err(1,
"PWM_SERVO_DISARM");
340 PX4_INFO(
"Outputs disarmed");
345 }
else if (oneshot || !strcmp(command,
"rate")) {
354 if (oneshot || alt_rate >= 0) {
358 PX4_ERR(
"PWM_SERVO_SET_UPDATE_RATE (check rate for sanity)");
359 return error_on_warn;
368 PX4_ERR(
"PWM_SERVO_SET_SELECT_UPDATE_RATE");
369 return error_on_warn;
374 if (alt_channels_set) {
377 for (group = 0; group < 32; group++) {
378 if ((1 << group) & alt_channel_groups) {
384 PX4_ERR(
"PWM_SERVO_GET_RATEGROUP(%u)", group);
385 return error_on_warn;
395 PX4_ERR(
"PWM_SERVO_SET_SELECT_UPDATE_RATE");
396 return error_on_warn;
402 }
else if (!strcmp(command,
"min")) {
405 usage(
"min: no channels set");
413 if (pwm_value == 0) {
414 usage(
"min: no PWM value provided");
426 PX4_ERR(
"failed get min values");
430 for (
unsigned i = 0; i < servo_count; i++) {
431 if (set_mask & 1 << i) {
432 pwm_values.values[i] = pwm_value;
435 PX4_INFO(
"Channel %d: min PWM: %d", i + 1, pwm_value);
440 if (pwm_values.channel_count == 0) {
441 usage(
"min: no channels provided");
449 PX4_ERR(
"failed setting min values (%d)", ret);
450 return error_on_warn;
456 }
else if (!strcmp(command,
"max")) {
459 usage(
"no channels set");
467 if (pwm_value == 0) {
468 usage(
"no PWM value provided");
480 PX4_ERR(
"failed get max values");
484 for (
unsigned i = 0; i < servo_count; i++) {
485 if (set_mask & 1 << i) {
486 pwm_values.values[i] = pwm_value;
489 PX4_INFO(
"Channel %d: max PWM: %d", i + 1, pwm_value);
494 if (pwm_values.channel_count == 0) {
495 usage(
"max: no PWM channels");
503 PX4_ERR(
"failed setting max values (%d)", ret);
504 return error_on_warn;
510 }
else if (!strcmp(command,
"disarmed")) {
513 usage(
"no channels set");
521 if (pwm_value == 0) {
522 PX4_WARN(
"reading disarmed value of zero, disabling disarmed PWM");
533 PX4_ERR(
"failed get disarmed values");
537 for (
unsigned i = 0; i < servo_count; i++) {
538 if (set_mask & 1 << i) {
539 pwm_values.values[i] = pwm_value;
542 PX4_INFO(
"chan %d: disarmed PWM: %d", i + 1, pwm_value);
547 if (pwm_values.channel_count == 0) {
548 usage(
"disarmed: no PWM channels");
556 PX4_ERR(
"failed setting disarmed values (%d)", ret);
557 return error_on_warn;
563 }
else if (!strcmp(command,
"failsafe")) {
566 usage(
"no channels set");
574 if (pwm_value == 0) {
575 usage(
"failsafe: no PWM provided");
587 PX4_ERR(
"failed get failsafe values");
591 for (
unsigned i = 0; i < servo_count; i++) {
592 if (set_mask & 1 << i) {
593 pwm_values.values[i] = pwm_value;
596 PX4_INFO(
"Channel %d: failsafe PWM: %d", i + 1, pwm_value);
601 if (pwm_values.channel_count == 0) {
602 usage(
"failsafe: no PWM channels");
610 PX4_ERR(
"BAD input VAL");
617 }
else if (!strcmp(command,
"test")) {
620 usage(
"no channels set");
624 if (pwm_value == 0) {
625 usage(
"no PWM provided");
632 for (
unsigned i = 0; i < servo_count; i++) {
638 PX4_ERR(
"PWM_SERVO_GET(%d)", i);
651 PX4_ERR(
"Failed to Enter pwm test mode");
652 goto err_out_no_test;
655 PX4_INFO(
"Press CTRL-C or 'c' to abort.");
658 for (
unsigned i = 0; i < servo_count; i++) {
659 if (set_mask & 1 << i) {
663 PX4_ERR(
"PWM_SERVO_SET(%d)", i);
671 ret = poll(&fds, 1, 0);
675 ret =
read(0, &c, 1);
677 if (c == 0x03 || c == 0x63 || c ==
'q') {
679 for (
unsigned i = 0; i < servo_count; i++) {
680 if (set_mask & 1 << i) {
684 PX4_ERR(
"PWM_SERVO_SET(%d)", i);
690 PX4_INFO(
"User abort\n");
712 PX4_ERR(
"Failed to Exit pwm test mode");
719 }
else if (!strcmp(command,
"steps")) {
722 usage(
"no channels set");
729 for (
unsigned i = 0; i < servo_count; i++) {
734 PX4_ERR(
"PWM_SERVO_GET(%d)", i);
746 PX4_WARN(
"Running 5 steps. WARNING! Motors will be live in 5 seconds\nPress any key to abort now.");
750 PX4_ERR(
"Failed to Enter pwm test mode");
751 goto err_out_no_test;
755 unsigned idle = 1300;
756 unsigned full = 2000;
757 unsigned steps_timings_us[] = {2000, 5000, 20000, 50000};
760 unsigned phase_counter = 0;
761 unsigned const phase_maxcount = 20;
763 for (
unsigned steps_timing_index = 0;
764 steps_timing_index <
sizeof(steps_timings_us) /
sizeof(steps_timings_us[0]);
765 steps_timing_index++) {
767 PX4_INFO(
"Step input (0 to 100%%) over %u us ramp", steps_timings_us[steps_timing_index]);
770 for (
unsigned i = 0; i < servo_count; i++) {
771 if (set_mask & 1 << i) {
778 }
else if (phase == 1) {
780 val = idle + (full -
idle) * ((
float)phase_counter / phase_maxcount);
789 PX4_ERR(
"PWM_SERVO_SET(%d)", i);
797 ret = poll(&fds, 1, 0);
801 ret =
read(0, &c, 1);
805 for (
unsigned i = 0; i < servo_count; i++) {
806 if (set_mask & 1 << i) {
810 PX4_ERR(
"PWM_SERVO_SET(%d)", i);
816 PX4_INFO(
"User abort\n");
823 px4_usleep(steps_timings_us[steps_timing_index] / phase_maxcount);
825 }
else if (phase == 0) {
828 }
else if (phase == 2) {
837 if (phase_counter > phase_maxcount) {
848 }
else if (!strcmp(command,
"info")) {
850 printf(
"device: %s\n", dev);
852 uint32_t info_default_rate;
853 uint32_t info_alt_rate;
854 uint32_t info_alt_rate_mask;
859 PX4_ERR(
"PWM_SERVO_GET_DEFAULT_UPDATE_RATE");
866 PX4_ERR(
"PWM_SERVO_GET_UPDATE_RATE");
873 PX4_ERR(
"PWM_SERVO_GET_SELECT_UPDATE_RATE");
890 PX4_ERR(
"PWM_SERVO_GET_FAILSAFE_PWM");
897 PX4_ERR(
"PWM_SERVO_GET_DISARMED_PWM");
904 PX4_ERR(
"PWM_SERVO_GET_MIN_PWM");
911 PX4_ERR(
"PWM_SERVO_GET_MAX_PWM");
918 PX4_ERR(
"PWM_SERVO_GET_TRIM_PWM");
923 for (
unsigned i = 0; i < servo_count; i++) {
929 printf(
"channel %u: %u us", i + 1, spos);
931 if (info_alt_rate_mask & (1 << i)) {
932 printf(
" (alternative rate: %d Hz", info_alt_rate);
935 printf(
" (default rate: %d Hz", info_default_rate);
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));
945 printf(
"%u: ERROR\n", i);
950 for (
unsigned i = 0; i < servo_count; i++) {
959 if (group_mask != 0) {
960 printf(
"channel group %u: channels", i);
962 for (
unsigned j = 0; j < 32; j++) {
963 if (group_mask & (1 << j)) {
964 printf(
" %u", j + 1);
974 }
else if (!strcmp(command,
"forcefail")) {
977 PX4_ERR(
"arg missing [on|off]");
982 if (!strcmp(argv[2],
"on")) {
992 PX4_ERR(
"FAILED setting forcefail %s", argv[2]);
998 }
else if (!strcmp(command,
"terminatefail")) {
1001 PX4_ERR(
"arg missing [on|off]");
1006 if (!strcmp(argv[2],
"on")) {
1016 PX4_ERR(
"FAILED setting termination failsafe %s", argv[2]);
#define PWM_SERVO_GET_SELECT_UPDATE_RATE
check the selected update rates
#define PWM_SERVO_SET_ARM_OK
set the 'ARM ok' 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 PWM_SERVO_GET_COUNT
get the number of servos in *(unsigned *)arg
#define PWM_SERVO_ARM
arm all servo outputs handle by this driver
static void usage(const char *reason)
#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[])
#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 PWM_SERVO_ENTER_TEST_MODE
#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'th rate group's channels; *(uint32_t *)arg returns a bitmap of channels whose update rates...
#define PWM_SERVO_GET(_servo)
get a single specific servo value
#define PWM_SERVO_SET_MIN_PWM
set the minimum PWM value the output will send
#define PWM_SERVO_SET_MAX_PWM
set the maximum PWM value the output will send
#define PWM_SERVO_SET_MODE
#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 PWM_SERVO_GET_FAILSAFE_PWM
get the PWM value for failsafe
__EXPORT void up_pwm_update(void)
Trigger all timer'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