PX4-17-执行机构驱动

在之前的文章中我们讲了PX4的混动框架,控制信号由混控器输出执行机构的控制信号后,交由执行机构驱动器。

PX4支持多种驱动器类型,如fmu的pwmout驱动、io mcu驱动、dshot、DroneCAN(UAVCAN)、PCA9685等。

我们之前的分享中,以PX4 1.12版本为基础,今年6月发布的最新版本的1.13版本在混控和执行机构驱动做了较多的修改,比较重要的修改修改有

  • 取消io单片机的mixer功能,全部由fmu实现mixer

    相关修改说明:https://github.com/PX4/PX4-Autopilot/commit/089c962d9208b3616ba505ab927fa399554035d4

  • 支持控制动态分配

    相关修改说明:https://github.com/PX4/PX4-Autopilot/commit/089c962d9208b3616ba505ab927fa399554035d4

我们基于1.13版本,介绍一下PX4的执行机构驱动框架。

PX4启动时根据配置选择启动的执行机构驱动类型,px4io、pwm_out、dshot、uavcan_esc

	#
	# Start IO for PWM output or RC input if needed.
	#
	if [ $IO_PRESENT = yes ]
	then
		if ! px4io start
		then
			echo "PX4IO start failed"
			tune_control play -t 18 # PROG_PX4IO_ERR
		fi
	fi

	if [ $OUTPUT_MODE = $OUTPUT_CMD -o $OUTPUT_MODE = io ]
	then
		if param compare SYS_CTRL_ALLOC 1
		then
			pwm_out start
			dshot start
		else
			if ! $OUTPUT_CMD start
			then
				echo "$OUTPUT_CMD start failed"
				tune_control play error
			fi
		fi
	fi

使用系统命令工具mixer,向指定的执行机构驱动配置主混控文件

	set OUTPUT_DEV /dev/pwm_output0

	if [ $OUTPUT_MODE = uavcan_esc ]
	then
		set OUTPUT_DEV /dev/uavcan/esc
	fi

	if mixer load ${OUTPUT_DEV} ${MIXER_FILE}
	then
		echo "INFO  [init] Mixer: ${MIXER_FILE} on ${OUTPUT_DEV}"

	else
		echo "ERROR  [init] Failed loading mixer: ${MIXER_FILE}"
		tune_control play -t 18 # tune 18 = PROG_PX4IO_ERR
	fi

PX4支持main和aux两个混控文件,即可以同时使用两个执行机构驱动,如fmu的pwm_out和px4io,或者uavcan_esc,可以同时支持更多的控制通道输出。

    if [ -e $OUTPUT_AUX_DEV -a $OUTPUT_MODE != hil ]
    then
        if mixer load ${OUTPUT_AUX_DEV} ${MIXER_AUX_FILE}
        then
            echo "INFO  [init] Mixer: ${MIXER_AUX_FILE} on ${OUTPUT_AUX_DEV}"
        else
            echo "ERROR  [init] Failed loading mixer: ${MIXER_AUX_FILE}"
        fi
    else
        echo "INFO  [init] setting PWM_AUX_OUT none"
        set PWM_AUX_OUT none
    fi

启动执行机构驱动后,在驱动器任务中进行,混控更新和控制通道输出。以pwm_out驱动为例:

PX4可以非常灵活的配置PWM输出io,只需要在boards中配置即可,如boards/px4/fmu-v6x/src/timer_config.cpp

constexpr io_timers_t io_timers[MAX_IO_TIMERS] = {
	initIOTimer(Timer::Timer5, DMA{DMA::Index1}),
	initIOTimer(Timer::Timer4, DMA{DMA::Index1}),
	initIOTimer(Timer::Timer12),
	initIOTimer(Timer::Timer1),
	initIOTimer(Timer::Timer2),
};

constexpr timer_io_channels_t timer_io_channels[MAX_TIMER_IO_CHANNELS] = {
	initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel4}, {GPIO::PortI, GPIO::Pin0}),
	initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel3}, {GPIO::PortH, GPIO::Pin12}),
	initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel2}, {GPIO::PortH, GPIO::Pin11}),
	initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel1}, {GPIO::PortH, GPIO::Pin10}),
	initIOTimerChannel(io_timers, {Timer::Timer4, Timer::Channel2}, {GPIO::PortD, GPIO::Pin13}),
	initIOTimerChannel(io_timers, {Timer::Timer4, Timer::Channel3}, {GPIO::PortD, GPIO::Pin14}),
	initIOTimerChannel(io_timers, {Timer::Timer12, Timer::Channel1}, {GPIO::PortH, GPIO::Pin6}),
	initIOTimerChannel(io_timers, {Timer::Timer12, Timer::Channel2}, {GPIO::PortH, GPIO::Pin9}),
	initIOTimerChannelCapture(io_timers, {Timer::Timer1, Timer::Channel2}, {GPIO::PortE, GPIO::Pin11}),
};

constexpr io_timers_channel_mapping_t io_timers_channel_mapping =
	initIOTimerChannelMapping(io_timers, timer_io_channels);

我的微信公众号,文章同步更新,欢迎关注。

微信公众号