zoukankan      html  css  js  c++  java
  • PX4 IO [15] mixer

    PX4 IO [15] mixer

    PX4 IO [15] mixer
                                                                                 -------- 转载请注明出处 
                                                                                 -------- 更多笔记请访问我的博客:merafour.blog.163.com 

                                                                                 -------- 2015-1-5.冷月追风

                                                                                 -------- email:merafour@163.com 



        看过了 IO的输入,我们现在来看看 IO中是怎么把信号输出的。 
        这个我们当然得先知道在 fmu中是怎么往 IO发这些输出数据的。 
        回过头去看第十一篇笔记,我们会看到 " AP_MotorsMatrix::output_test"函数的源码如下: 

    // output_test - spin a motor at the pwm value specified
    //  motor_seq is the motor's sequence number from 1 to the number of motors on the frame
    //  pwm value is an actual pwm value that will be output, normally in the range of 1000 ~ 2000
    void AP_MotorsMatrix::output_test(uint8_t motor_seq, int16_t pwm)
    {
        // exit immediately if not armed
        if (!_flags.armed) {
            return;
        }

        // loop through all the possible orders spinning any motors that match that description
        for (uint8_t i=0; i<AP_MOTORS_MAX_NUM_MOTORS; i++) {
            if (motor_enabled[i] && _test_order[i] == motor_seq) {
                // turn on this motor
                hal.rcout->write(pgm_read_byte(&_motor_to_channel_map[i]), pwm);
            }
        }
    }


    也就是说通过 " hal.rcout->write"接口下发数据。" rcout"类型为 "PX4RCOutput"。从前面的分析中我们知道 "PX4RCOutput"其实是通过设备文件对 px4io进行调用,最终调用的是 "PX4IO::write"函数,源码如下: 

    ssize_t PX4IO::write(file * /*filp*/, const char *buffer, size_t len)
    /* Make it obvious that file * isn't used here */
    {
        unsigned count = len / 2;

        if (count > _max_actuators)
            count = _max_actuators;

        if (count > 0) {

            perf_begin(_perf_write);
            int ret = io_reg_set(PX4IO_PAGE_DIRECT_PWM, 0, (uint16_t *)buffer, count);
            perf_end(_perf_write);

            if (ret != OK)
                return ret;
        }

        return count * 2;
    }


    px4io在我们的 V2版本中通过串口跟 io板通信。所以两者通信的关键在于 "PX4IO_PAGE_DIRECT_PWM"这个宏, 

    radiolink@ubuntu:~/apm$ grep -nr PX4IO_PAGE_DIRECT_PWM ./PX4Firmware/src/
    ./PX4Firmware/src/modules/px4iofirmware/protocol.h:258:#definePX4IO_PAGE_DIRECT_PWM                    54              /**< 0..CONFIG_ACTUATOR_COUNT-1 */
    ./PX4Firmware/src/modules/px4iofirmware/registers.c:284:        casePX4IO_PAGE_DIRECT_PWM:
    ./PX4Firmware/src/modules/px4iofirmware/registers.c:883:        casePX4IO_PAGE_DIRECT_PWM:
    ./PX4Firmware/src/drivers/px4io/px4io.cpp:2366:                         ret = io_reg_set(PX4IO_PAGE_DIRECT_PWM, channel, arg);
    ./PX4Firmware/src/drivers/px4io/px4io.cpp:2649:         int ret = io_reg_set(PX4IO_PAGE_DIRECT_PWM, 0, (uint16_t *)buffer, count);
    radiolink@ubuntu:~/apm$

    因为我们现在关注的是 io固件,所以我们现在要找的源码在 "registers.c"源文件中。可能我们觉得这里值应该出现一个 "case"语句,但这里却出现了两个,为什么?如果你去看源码你会看到实际上有一个 "registers_set"和一个 "registers_get"函数。当然 get函数我们没有使用,就不去关心。下面我们就来看看 set函数。

    int registers_set(uint8_t page, uint8_t offset, const uint16_t *values, unsignednum_values)
    {
        switch (page) {
      /* ... */
            /* handle raw PWM input */
        case PX4IO_PAGE_DIRECT_PWM:
            /* copy channel data */
            while ((offset < PX4IO_CONTROL_CHANNELS) && (num_values > 0)) {
                /* XXX range-check value? */
                r_page_servos[offset] = *values;
                offset++;
                num_values--;
                values++;
            }
            system_state.fmu_data_received_time = hrt_absolute_time();
            r_status_flags |= PX4IO_P_STATUS_FLAGS_FMU_OK | PX4IO_P_STATUS_FLAGS_RAW_PWM;
            break;

    所以我们看到,在这里 set主要是数据拷贝动作。而我们现在要解决的是在把数据放到 "r_page_servos"数组中之后数据是怎样到大寄存器的。

    radiolink@ubuntu:~/apm$ grep -nr r_page_servos ./PX4Firmware/src/modules/px4iofirmware/
    ./PX4Firmware/src/modules/px4iofirmware/px4io.h:76:externuint16_t                      r_page_servos[];        /* PX4IO_PAGE_SERVOS */
    ./PX4Firmware/src/modules/px4iofirmware/mixer.cpp:217:                  r_page_servos[i] = r_page_servo_failsafe[i];
    ./PX4Firmware/src/modules/px4iofirmware/mixer.cpp:220:                  r_page_actuators[i] = FLOAT_TO_REG((r_page_servos[i] - 1500) / 600.0f);
    ./PX4Firmware/src/modules/px4iofirmware/mixer.cpp:237:          pwm_limit_calc(should_arm, mixed, r_page_servo_disarmed, r_page_servo_control_min, r_page_servo_control_max, outputs, r_page_servos, &pwm_limit);
    ./PX4Firmware/src/modules/px4iofirmware/mixer.cpp:240:                  r_page_servos[i] = 0;
    ./PX4Firmware/src/modules/px4iofirmware/mixer.cpp:276:                  up_pwm_servo_set(i, r_page_servos[i]);
    ./PX4Firmware/src/modules/px4iofirmware/registers.c:107:uint16_t                r_page_servos[PX4IO_SERVO_COUNT];
    ./PX4Firmware/src/modules/px4iofirmware/registers.c:221: * PAGE 104 uses r_page_servos.
    ./PX4Firmware/src/modules/px4iofirmware/registers.c:291:                                r_page_servos[offset] = *values;
    ./PX4Firmware/src/modules/px4iofirmware/registers.c:864:                SELECT_PAGE(r_page_servos);
    ./PX4Firmware/src/modules/px4iofirmware/registers.c:884:                SELECT_PAGE(r_page_servos);
    radiolink@ubuntu:~/apm$

    去阅读源码我们就会发现, mixer.cpp中的结果均来自同一个函数: mixer_tick。该函数同样是在 Px4io.c中由 user_start调用。代码如下:

    int user_start(int argc, char *argv[])
    {
        /* ... */
        for (;;) {
            /* track the rate at which the loop is running */
            perf_count(loop_perf);
            /* kick the mixer */
            perf_begin(mixer_perf);
            mixer_tick();
            perf_end(mixer_perf);
            /* kick the control inputs */
            perf_begin(controls_perf);
            controls_tick();
            perf_end(controls_perf);

    这样我们就只需要关心 mixer_tick函数即可。而该函数从头到尾超过 150行代码,我们在看源码的时候要将它拆成几段。

    void mixer_tick(void)
    {
        /* check that we are receiving fresh data from the FMU */
        if (hrt_elapsed_time(&system_state.fmu_data_received_time) > FMU_INPUT_DROP_LIMIT_US) {
            /* too long without FMU input, time to go to failsafe */
            if (r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_OK) {
                isr_debug(1, "AP RX timeout");
            }
            r_status_flags &= ~(PX4IO_P_STATUS_FLAGS_FMU_OK);
            r_status_alarms |= PX4IO_P_STATUS_ALARMS_FMU_LOST;
        } else {
            r_status_flags |= PX4IO_P_STATUS_FLAGS_FMU_OK;
        }
    #define FMU_INPUT_DROP_LIMIT_US        200000

    这段代码并不难理解,是用来检测超时的。超时时间为 200ms。可能我们都会奇怪, user_start中的主循环执行一次会消耗 200ms吗?关于这点,我只能说目前我还没有去研究它是怎么控制循环的,暂时不予讨论。

        /* default to failsafe mixing */
        source = MIX_FAILSAFE;
        /*
         * Decide which set of controls we're using.
         */
        /* do not mix if RAW_PWM mode is on and FMU is good */
        if ((r_status_flags & PX4IO_P_STATUS_FLAGS_RAW_PWM) &&
                (r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_OK)) {
            /* don't actually mix anything - we already have raw PWM values */
            source = MIX_NONE;
        } else {
            if (!(r_status_flags & PX4IO_P_STATUS_FLAGS_OVERRIDE) &&
                 (r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_OK) &&
                 (r_status_flags & PX4IO_P_STATUS_FLAGS_MIXER_OK)) {
                /* mix from FMU controls */
                source = MIX_FMU;
            }
            if ( (r_status_flags & PX4IO_P_STATUS_FLAGS_OVERRIDE) &&
                 (r_status_flags & PX4IO_P_STATUS_FLAGS_RC_OK) &&
                 (r_status_flags & PX4IO_P_STATUS_FLAGS_MIXER_OK) &&
                 !(r_setup_arming & PX4IO_P_SETUP_ARMING_RC_HANDLING_DISABLED) &&
                 !(r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_OK)) {
                 /* if allowed, mix from RC inputs directly */
                source = MIX_OVERRIDE;
            } else     if ( (r_status_flags & PX4IO_P_STATUS_FLAGS_OVERRIDE) &&
                 (r_status_flags & PX4IO_P_STATUS_FLAGS_RC_OK) &&
                 (r_status_flags & PX4IO_P_STATUS_FLAGS_MIXER_OK) &&
                 !(r_setup_arming & PX4IO_P_SETUP_ARMING_RC_HANDLING_DISABLED) &&
                 (r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_OK)) {
                /* if allowed, mix from RC inputs directly up to available rc channels */
                source = MIX_OVERRIDE_FMU_OK;
            }
        }
        /*
         * Set failsafe status flag depending on mixing source
         */
        if (source == MIX_FAILSAFE) {
            r_status_flags |= PX4IO_P_STATUS_FLAGS_FAILSAFE;
        } else {
            r_status_flags &= ~(PX4IO_P_STATUS_FLAGS_FAILSAFE);
        }

    r_status_flags就是前面我们在 set函数中设置的标志,如果set函数被正常调用,那么到这里 source的值为 MIX_NONE。关于失控保护,正常情况下是可以忽略的。

        /*
         * Decide whether the servos should be armed right now.
         *
         * We must be armed, and we must have a PWM source; either raw from
         * FMU or from the mixer.
         *
         * XXX correct behaviour for failsafe may require an additional case
         * here.
         */
        should_arm = (
            /* IO initialised without error */   (r_status_flags & PX4IO_P_STATUS_FLAGS_INIT_OK)
            /* and IO is armed */           && (r_status_flags & PX4IO_P_STATUS_FLAGS_SAFETY_OFF)
            /* and FMU is armed */           && (
                                    ((r_setup_arming & PX4IO_P_SETUP_ARMING_FMU_ARMED)
            /* and there is valid input via or mixer */         &&   (r_status_flags & PX4IO_P_STATUS_FLAGS_MIXER_OK) )
            /* or direct PWM is set */               || (r_status_flags & PX4IO_P_STATUS_FLAGS_RAW_PWM)
            /* or failsafe was set manually */     || ((r_setup_arming & PX4IO_P_SETUP_ARMING_FAILSAFE_CUSTOM) && !(r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_OK))
                                 )
        );
        should_always_enable_pwm = (r_setup_arming & PX4IO_P_SETUP_ARMING_ALWAYS_PWM_ENABLE)
                            && (r_status_flags & PX4IO_P_STATUS_FLAGS_INIT_OK)
                            && (r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_OK);

    这段代码注释说的比较清楚,是进行解锁检查的。说到解锁,我们都还没看 fmu是怎么解锁的,待会还真得看看。飞控通常都是要解锁之后才能飞的,这是出于安全考虑。我想 fmu解锁了应该也会通过串口发送一个信息给 io并最终调用 set函。

        /*
         * Run the mixers.
         */
        if (source == MIX_FAILSAFE) {
            /* copy failsafe values to the servo outputs */
            for (unsigned i = 0; i < PX4IO_SERVO_COUNT; i++) {
                r_page_servos[i] = r_page_servo_failsafe[i];
                /* safe actuators for FMU feedback */
                r_page_actuators[i] = FLOAT_TO_REG((r_page_servos[i] - 1500) / 600.0f);
            }
        } else if (source != MIX_NONE && (r_status_flags & PX4IO_P_STATUS_FLAGS_MIXER_OK)) {
            float    outputs[PX4IO_SERVO_COUNT];
            unsigned mixed;
            /* mix */
            /* poor mans mutex */
            in_mixer = true;
            mixed = mixer_group.mix(&outputs[0], PX4IO_SERVO_COUNT);
            in_mixer = false;
            pwm_limit_calc(should_arm, mixed, r_page_servo_disarmed, r_page_servo_control_min, r_page_servo_control_max, outputs, r_page_servos, &pwm_limit);
            for (unsigned i = mixed; i < PX4IO_SERVO_COUNT; i++)
                r_page_servos[i] = 0;
            for (unsigned i = 0; i < PX4IO_SERVO_COUNT; i++) {
                r_page_actuators[i] = FLOAT_TO_REG(outputs[i]);
            }
        }

    从前面的代码中我们知道 source的值为 MIX_NONE,所以这一段不会执行。这段代码只是给 r_page_servos一个特定的值,前面是失控保护,而后面应该是关闭电机。其实如果 fmu发过来的数据是 1ms的脉宽,电机也是关闭的。

        /* set arming */
        bool needs_to_arm = (should_arm || should_always_enable_pwm);
        /* check any conditions that prevent arming */
        if (r_setup_arming & PX4IO_P_SETUP_ARMING_LOCKDOWN) {
            needs_to_arm = false;
        }
        if (!should_arm && !should_always_enable_pwm) {
            needs_to_arm = false;
        }
        if (needs_to_arm && !mixer_servos_armed) {
            /* need to arm, but not armed */
            up_pwm_servo_arm(true);
            mixer_servos_armed = true;
            r_status_flags |= PX4IO_P_STATUS_FLAGS_OUTPUTS_ARMED;
            isr_debug(5, "> PWM enabled");
        } else if (!needs_to_arm && mixer_servos_armed) {
            /* armed but need to disarm */
            up_pwm_servo_arm(false);
            mixer_servos_armed = false;
            r_status_flags &= ~(PX4IO_P_STATUS_FLAGS_OUTPUTS_ARMED);
            isr_debug(5, "> PWM disabled");
        }
        if (mixer_servos_armed && should_arm) {
            /* update the servo outputs. */
            for (unsigned i = 0; i < PX4IO_SERVO_COUNT; i++)
                up_pwm_servo_set(i, r_page_servos[i]);
        } else if (mixer_servos_armed && should_always_enable_pwm) {
            /* set the disarmed servo outputs. */
            for (unsigned i = 0; i < PX4IO_SERVO_COUNT; i++)
                up_pwm_servo_set(i, r_page_servo_disarmed[i]);
        }
    int up_pwm_servo_set(unsigned channel, servo_position_t value)
    {
        if (channel >= PWM_SERVO_MAX_CHANNELS)
            return -1;

        unsigned timer = pwm_channels[channel].timer_index;

        /* test timer for validity */
        if ((pwm_timers[timer].base == 0) ||
            (pwm_channels[channel].gpio == 0))
            return -1;

        /* configure the channel */
        if (value > 0)
            value--;

        switch (pwm_channels[channel].timer_channel) {
        case 1:
            rCCR1(timer) = value;
            break;

        case 2:
            rCCR2(timer) = value;
            break;

        case 3:
            rCCR3(timer) = value;
            break;

        case 4:
            rCCR4(timer) = value;
            break;

        default:
            return -1;
        }

        return 0;
    }

    所以最终是通过 up_pwm_servo_set函数最终将 PWM信号输出的。其中 r_page_servos这组数据是 fmu发送过来的,而 r_page_servo_disarmed这组数据是前面 pwm_limit_calc函数计算得到的。

        而其中具体每一个参数是做什么的,这个就要搞懂了流程以后详细去分析了。

  • 相关阅读:
    oracle 数据库服务名怎么查
    vmware vsphere 6.5
    vSphere虚拟化之ESXi的安装及部署
    ArcMap中无法添加ArcGIS Online底图的诊断方法
    ArcGIS中字段计算器(高级计算VBScript、Python)
    Bad habits : Putting NOLOCK everywhere
    Understanding the Impact of NOLOCK and WITH NOLOCK Table Hints in SQL Server
    with(nolock) or (nolock)
    What is “with (nolock)” in SQL Server?
    Changing SQL Server Collation After Installation
  • 原文地址:https://www.cnblogs.com/eastgeneral/p/10879608.html
Copyright © 2011-2022 走看看