• iNavFlight之MSP DJI协议分析


    MSP DJI协议主要是为了解决如何将飞控内部信息传送到DJI数字图传,进而在DJI的数字系统的视频上叠加飞控OSD信息。

    注:目前模拟图传的做法是通过MAX7456芯片将模拟VI和飞控

    1. iNav串行口通信

    1.1 iNav 串口任务

    taskHandleSerial是STM32任务之一。

        [TASK_SERIAL] = {
            .taskName = "SERIAL",
            .taskFunc = taskHandleSerial,
            .desiredPeriod = TASK_PERIOD_HZ(100),     // 100 Hz should be enough to flush up to 115 bytes @ 115200 baud
            .staticPriority = TASK_PRIORITY_LOW,
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.2 调用逻辑

    taskHandleSerial是专门集中处理串口的任务。其中djiOsdSerialProcess是与DJI天空端通信的实现代码入口。

    void taskHandleSerial(timeUs_t currentTimeUs)
    {
        UNUSED(currentTimeUs);
        // in cli mode, all serial stuff goes to here. enter cli mode by sending #
        if (cliMode) {
            cliProcess();
        }
    
        // Allow MSP processing even if in CLI mode
        mspSerialProcess(ARMING_FLAG(ARMED) ? MSP_SKIP_NON_MSP_DATA : MSP_EVALUATE_NON_MSP_DATA, mspFcProcessCommand);
    
    #if defined(USE_DJI_HD_OSD)
        // DJI OSD uses a special flavour of MSP (subset of Betaflight 4.1.1 MSP) - process as part of serial task
        djiOsdSerialProcess();
    #endif
    
    #ifdef USE_MSP_OSD
    	// Capture MSP Displayport messages to determine if VTX is connected
        mspOsdSerialProcess(mspFcProcessCommand);
    #endif
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    mspSerialProcess是正常的MSP控制协议函数实现。

    void mspSerialProcess(mspEvaluateNonMspData_e evaluateNonMspData, mspProcessCommandFnPtr mspProcessCommandFn)
    {
        for (uint8_t portIndex = 0; portIndex < MAX_MSP_PORT_COUNT; portIndex++) {
            mspPort_t * const mspPort = &mspPorts[portIndex];
            if (mspPort->port) {
                mspSerialProcessOnePort(mspPort, evaluateNonMspData, mspProcessCommandFn);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    djiOsdSerialProcess是专门用于处理与DJI天空端通信的函数实现。

    void djiOsdSerialProcess(void)
    {
        // Check if DJI OSD is configured
        if (!djiMspPort.port) {
            return;
        }
    
        // Piggyback on existing MSP protocol stack, but pass our special command processing function
        mspSerialProcessOnePort(&djiMspPort, MSP_SKIP_NON_MSP_DATA, djiProcessMspCommand);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这段代码据说没有使能,详见:how to open MSP_DISPLAYPORT #6415。但是从代码上看是有编译到的,详见:#define USE_MSP_OSD,因此应该是特殊配置的FUNCTION_MSP_OSD端口(且不是DJI的)。

    #ifdef USE_MSP_OSD
    	// Capture MSP Displayport messages to determine if VTX is connected
        mspOsdSerialProcess(mspFcProcessCommand);
    #endif
    
    • 1
    • 2
    • 3
    • 4

    2. iNav串行抽象

    从该代码实现角度看,是典型的C/S模型。

    通用框架mspSerialProcessOnePort主要是提供收包,异常处理,解析,调用命令处理流程和报文反馈,其主要差异在于命令处理流程部分。

    2.1 框架代码

    mspSerialProcessOnePort框架代码是MSP协议的通用实现,其主要差异在于命令码处理的mspProcessCommandFn函数。

    1. 当MSP_SKIP_NON_MSP_DATA,用mspProcessCommandFn处理;

    a) djiProcessMspCommand: MSP(DJI)协议处理
    b) mspFcProcessCommand: MSP(Control)协议处理

    1. 当MSP_EVALUATE_NON_MSP_DATA,mspEvaluateNonMspData;

    命令行模式

    void mspSerialProcessOnePort(mspPort_t * const mspPort, mspEvaluateNonMspData_e evaluateNonMspData, mspProcessCommandFnPtr mspProcessCommandFn)
    {
        mspPostProcessFnPtr mspPostProcessFn = NULL;
    
        if (serialRxBytesWaiting(mspPort->port)) {
            // There are bytes incoming - abort pending request
            mspPort->lastActivityMs = millis();
            mspPort->pendingRequest = MSP_PENDING_NONE;
    
            // Process incoming bytes
            while (serialRxBytesWaiting(mspPort->port)) {
                const uint8_t c = serialRead(mspPort->port);
                const bool consumed = mspSerialProcessReceivedData(mspPort, c);
    
                if (!consumed && evaluateNonMspData == MSP_EVALUATE_NON_MSP_DATA) {
                    mspEvaluateNonMspData(mspPort, c);
                }
    
                if (mspPort->c_state == MSP_COMMAND_RECEIVED) {
                    mspPostProcessFn = mspSerialProcessReceivedCommand(mspPort, mspProcessCommandFn);
                    break; // process one command at a time so as not to block.
                }
            }
    
            if (mspPostProcessFn) {
                waitForSerialPortToFinishTransmitting(mspPort->port);
                mspPostProcessFn(mspPort->port);
            }
        }
        else {
            mspProcessPendingRequest(mspPort);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    2.2 MSP(DJI)协议处理

    DJI天空端发出请求,FC接收到请求后反馈请求结果,详见:djiProcessMspCommand

    static mspResult_e djiProcessMspCommand(mspPacket_t *cmd, mspPacket_t *reply, mspPostProcessFnPtr *mspPostProcessFn)
    {
        UNUSED(mspPostProcessFn);
    
        sbuf_t *dst = &reply->buf;
        sbuf_t *src = &cmd->buf;
    
        // Start initializing the reply message
        reply->cmd = cmd->cmd;
        reply->result = MSP_RESULT_ACK;
    
        switch (cmd->cmd) {
            case DJI_MSP_API_VERSION:
                sbufWriteU8(dst, MSP_PROTOCOL_VERSION);
                sbufWriteU8(dst, DJI_API_VERSION_MAJOR);
                sbufWriteU8(dst, DJI_API_VERSION_MINOR);
                break;
    
            case DJI_MSP_FC_VARIANT:
                {
                    const char * const flightControllerIdentifier = INAV_IDENTIFIER;
                    sbufWriteData(dst, flightControllerIdentifier, FLIGHT_CONTROLLER_IDENTIFIER_LENGTH);
                }
                break;
    
            case DJI_MSP_FC_VERSION:
                sbufWriteU8(dst, 4);
                sbufWriteU8(dst, 1);
                sbufWriteU8(dst, 0);
                break;
    
            case DJI_MSP_NAME:
                {
    #if defined(USE_OSD)
                    if (djiOsdConfig()->use_name_for_messages)  {
                        djiSerializeCraftNameOverride(dst);
                    } else {
    #endif
                        sbufWriteData(dst, systemConfig()->name, (int)strlen(systemConfig()->name));
    #if defined(USE_OSD)
                    }
    #endif
    
                    break;
                }
                break;
    
            case DJI_MSP_STATUS:
            case DJI_MSP_STATUS_EX:
                {
                    // DJI OSD relies on a statically defined bit order and doesn't use MSP_BOXIDS
                    // to get actual BOX order. We need a special packBoxModeFlags()
                    boxBitmask_t flightModeBitmask;
                    djiPackBoxModeBitmask(&flightModeBitmask);
    
                    sbufWriteU16(dst, (uint16_t)cycleTime);
                    sbufWriteU16(dst, 0);
                    sbufWriteU16(dst, packSensorStatus());
                    sbufWriteData(dst, &flightModeBitmask, 4);        // unconditional part of flags, first 32 bits
                    sbufWriteU8(dst, getConfigProfile());
    
                    sbufWriteU16(dst, constrain(averageSystemLoadPercent, 0, 100));
                    if (cmd->cmd == MSP_STATUS_EX) {
                        sbufWriteU8(dst, 3);            // PID_PROFILE_COUNT
                        sbufWriteU8(dst, 1);            // getCurrentControlRateProfileIndex()
                    } else {
                        sbufWriteU16(dst, cycleTime);   // gyro cycle time
                    }
    
                    // Cap BoxModeFlags to 32 bits
                    // write flightModeFlags header. Lowest 4 bits contain number of bytes that follow
                    sbufWriteU8(dst, 0);
                    // sbufWriteData(dst, ((uint8_t*)&flightModeBitmask) + 4, byteCount);
    
                    // Write arming disable flags
                    sbufWriteU8(dst, DJI_ARMING_DISABLE_FLAGS_COUNT);
                    sbufWriteU32(dst, djiPackArmingDisabledFlags());
    
                    // Extra flags
                    sbufWriteU8(dst, 0);
                }
                break;
    
            case DJI_MSP_RC:
                // Only send sticks (first 4 channels)
                for (int i = 0; i < STICK_CHANNEL_COUNT; i++) {
                    sbufWriteU16(dst, rxGetChannelValue(i));
                }
                break;
    
            case DJI_MSP_RAW_GPS:
                sbufWriteU8(dst, gpsSol.fixType);
                sbufWriteU8(dst, gpsSol.numSat);
                sbufWriteU32(dst, gpsSol.llh.lat);
                sbufWriteU32(dst, gpsSol.llh.lon);
                sbufWriteU16(dst, gpsSol.llh.alt / 100);
                sbufWriteU16(dst, osdGetSpeedFromSelectedSource());
                sbufWriteU16(dst, gpsSol.groundCourse);
                break;
    
            case DJI_MSP_COMP_GPS:
                sbufWriteU16(dst, GPS_distanceToHome);
                sbufWriteU16(dst, GPS_directionToHome);
                sbufWriteU8(dst, gpsSol.flags.gpsHeartbeat ? 1 : 0);
                break;
    
            case DJI_MSP_ATTITUDE:
                sbufWriteU16(dst, attitude.values.roll);
                sbufWriteU16(dst, attitude.values.pitch);
                sbufWriteU16(dst, DECIDEGREES_TO_DEGREES(attitude.values.yaw));
                break;
    
            case DJI_MSP_ALTITUDE:
                sbufWriteU32(dst, lrintf(getEstimatedActualPosition(Z)));
                sbufWriteU16(dst, lrintf(getEstimatedActualVelocity(Z)));
                break;
    
            case DJI_MSP_ANALOG:
                sbufWriteU8(dst,  constrain(getBatteryVoltage() / 10, 0, 255));
                sbufWriteU16(dst, constrain(getMAhDrawn(), 0, 0xFFFF)); // milliamp hours drawn from battery
    #ifdef USE_SERIALRX_CRSF
                // Range of RSSI field: 0-99: 99 = 150 hz , 0 - 98 50 hz / 4 hz
                if (djiOsdConfig()->rssi_source == DJI_CRSF_LQ) {
                    uint16_t scaledLq = 0;
                    if (rxLinkStatistics.rfMode >= 2) {
                        scaledLq = RSSI_MAX_VALUE;
                    } else {
                        scaledLq = scaleRange(constrain(rxLinkStatistics.uplinkLQ, 0, 100), 0, 100, 0, RSSI_BOUNDARY(98));
                    }
                    sbufWriteU16(dst, scaledLq);
                } else {
    #endif
                    sbufWriteU16(dst, getRSSI());
    #ifdef USE_SERIALRX_CRSF
                }
    #endif
                sbufWriteU16(dst, constrain(getAmperage(), -0x8000, 0x7FFF)); // send amperage in 0.01 A steps, range is -320A to 320A
                sbufWriteU16(dst, getBatteryVoltage());
                break;
    
            case DJI_MSP_PID:
                for (unsigned i = 0; i < ARRAYLEN(djiPidIndexMap); i++) {
                    sbufWriteU8(dst, pidBank()->pid[djiPidIndexMap[i]].P);
                    sbufWriteU8(dst, pidBank()->pid[djiPidIndexMap[i]].I);
                    sbufWriteU8(dst, pidBank()->pid[djiPidIndexMap[i]].D);
                }
                break;
    
            case DJI_MSP_BATTERY_STATE:
                // Battery characteristics
                sbufWriteU8(dst, constrain(getBatteryCellCount(), 0, 255));
                sbufWriteU16(dst, currentBatteryProfile->capacity.value);
    
                // Battery state
                sbufWriteU8(dst, constrain(getBatteryVoltage() / 10, 0, 255)); // in 0.1V steps
                sbufWriteU16(dst, constrain(getMAhDrawn(), 0, 0xFFFF));
                sbufWriteU16(dst, constrain(getAmperage(), -0x8000, 0x7FFF));
    
                // Battery alerts - used values match Betaflight's/DJI's
                sbufWriteU8(dst,  getBatteryState());
    
                // Additional battery voltage field (in 0.01V steps)
                sbufWriteU16(dst, getBatteryVoltage());
                break;
    
            case DJI_MSP_RTC:
                {
                    dateTime_t datetime;
    
                    // We don't care about validity here - dt will be always set to a sane value
                    // rtcGetDateTime() will call rtcGetDefaultDateTime() internally
                    rtcGetDateTime(&datetime);
    
                    sbufWriteU16(dst, datetime.year);
                    sbufWriteU8(dst, datetime.month);
                    sbufWriteU8(dst, datetime.day);
                    sbufWriteU8(dst, datetime.hours);
                    sbufWriteU8(dst, datetime.minutes);
                    sbufWriteU8(dst, datetime.seconds);
                    sbufWriteU16(dst, datetime.millis);
                }
                break;
    
            case DJI_MSP_ESC_SENSOR_DATA:
                if (djiOsdConfig()->proto_workarounds & DJI_OSD_USE_NON_STANDARD_MSP_ESC_SENSOR_DATA) {
                    // Version 1.00.06 of DJI firmware is not using the standard MSP_ESC_SENSOR_DATA
                    uint16_t protoRpm = 0;
                    int16_t protoTemp = 0;
    
    #if defined(USE_ESC_SENSOR)
                    if (STATE(ESC_SENSOR_ENABLED) && getMotorCount() > 0) {
                        uint32_t motorRpmAcc = 0;
                        int32_t motorTempAcc = 0;
    
                        for (int i = 0; i < getMotorCount(); i++) {
                            const escSensorData_t * escSensor = getEscTelemetry(i);
                            motorRpmAcc += escSensor->rpm;
                            motorTempAcc += escSensor->temperature;
                        }
    
                        protoRpm = motorRpmAcc / getMotorCount();
                        protoTemp = motorTempAcc / getMotorCount();
                    }
    #endif
    
                    switch (djiOsdConfig()->esc_temperature_source) {
                        // This is ESC temperature (as intended)
                        case DJI_OSD_TEMP_ESC:
                            // No-op, temperature is already set to ESC
                            break;
    
                        // Re-purpose the field for core temperature
                        case DJI_OSD_TEMP_CORE:
                            getIMUTemperature(&protoTemp);
                            protoTemp = protoTemp / 10;
                            break;
    
                        // Re-purpose the field for baro temperature
                        case DJI_OSD_TEMP_BARO:
                            getBaroTemperature(&protoTemp);
                            protoTemp = protoTemp / 10;
                            break;
                    }
    
                    // No motor count, just raw temp and RPM data
                    sbufWriteU8(dst, protoTemp);
                    sbufWriteU16(dst, protoRpm);
                }
                else {
                    // Use standard MSP_ESC_SENSOR_DATA message
                    sbufWriteU8(dst, getMotorCount());
                    for (int i = 0; i < getMotorCount(); i++) {
                        uint16_t motorRpm = 0;
                        int16_t motorTemp = 0;
    
                        // If ESC_SENSOR is enabled, pull the telemetry data and get motor RPM
    #if defined(USE_ESC_SENSOR)
                        if (STATE(ESC_SENSOR_ENABLED)) {
                            const escSensorData_t * escSensor = getEscTelemetry(i);
                            motorRpm = escSensor->rpm;
                            motorTemp = escSensor->temperature;
                        }
    #endif
    
                        // Now populate temperature field (which we may override for different purposes)
                        switch (djiOsdConfig()->esc_temperature_source) {
                            // This is ESC temperature (as intended)
                            case DJI_OSD_TEMP_ESC:
                                // No-op, temperature is already set to ESC
                                break;
    
                            // Re-purpose the field for core temperature
                            case DJI_OSD_TEMP_CORE:
                                getIMUTemperature(&motorTemp);
                                motorTemp = motorTemp / 10;
                                break;
    
                            // Re-purpose the field for baro temperature
                            case DJI_OSD_TEMP_BARO:
                                getBaroTemperature(&motorTemp);
                                motorTemp = motorTemp / 10;
                                break;
                        }
    
                        // Add data for this motor to the packet
                        sbufWriteU8(dst, motorTemp);
                        sbufWriteU16(dst, motorRpm);
                    }
                }
                break;
    
            case DJI_MSP_OSD_CONFIG:
    #if defined(USE_OSD)
                // This involved some serious magic, better contain in a separate function for readability
                djiSerializeOSDConfigReply(dst);
    #else
                sbufWriteU8(dst, 0);
    #endif
                break;
    
            case DJI_MSP_FILTER_CONFIG:
                sbufWriteU8(dst, gyroConfig()->gyro_main_lpf_hz);           // BF: gyroConfig()->gyro_lowpass_hz
                sbufWriteU16(dst, pidProfile()->dterm_lpf_hz);              // BF: currentPidProfile->dterm_lowpass_hz
                sbufWriteU16(dst, pidProfile()->yaw_lpf_hz);                // BF: currentPidProfile->yaw_lowpass_hz
                sbufWriteU16(dst, 0);                                       // BF: gyroConfig()->gyro_soft_notch_hz_1
                sbufWriteU16(dst, 1);                                       // BF: gyroConfig()->gyro_soft_notch_cutoff_1
                sbufWriteU16(dst, 0);                                       // BF: currentPidProfile->dterm_notch_hz
                sbufWriteU16(dst, 1);                                       // BF: currentPidProfile->dterm_notch_cutoff
                sbufWriteU16(dst, 0);                                       // BF: gyroConfig()->gyro_soft_notch_hz_2
                sbufWriteU16(dst, 1);                                       // BF: gyroConfig()->gyro_soft_notch_cutoff_2
                sbufWriteU8(dst, 0);                                        // BF: currentPidProfile->dterm_filter_type
                sbufWriteU8(dst, gyroConfig()->gyro_lpf);                   // BF: gyroConfig()->gyro_hardware_lpf);
                sbufWriteU8(dst, 0);                                        // BF: DEPRECATED: gyro_32khz_hardware_lpf
                sbufWriteU16(dst, gyroConfig()->gyro_main_lpf_hz);          // BF: gyroConfig()->gyro_lowpass_hz);
                sbufWriteU16(dst, 0);                                       // BF: gyroConfig()->gyro_lowpass2_hz);
                sbufWriteU8(dst, 0);                                        // BF: gyroConfig()->gyro_lowpass_type);
                sbufWriteU8(dst, 0);                                        // BF: gyroConfig()->gyro_lowpass2_type);
                sbufWriteU16(dst, 0);                                       // BF: currentPidProfile->dterm_lowpass2_hz);
                sbufWriteU8(dst, 0);                                        // BF: currentPidProfile->dterm_filter2_type);
                break;
    
            case DJI_MSP_RC_TUNING:
                sbufWriteU8(dst, 100);                                      // INAV doesn't use rcRate
                sbufWriteU8(dst, currentControlRateProfile->stabilized.rcExpo8);
                for (int i = 0 ; i < 3; i++) {
                    // R,P,Y rates see flight_dynamics_index_t
                    sbufWriteU8(dst, currentControlRateProfile->stabilized.rates[i]);
                }
                sbufWriteU8(dst, currentControlRateProfile->throttle.dynPID);
                sbufWriteU8(dst, currentControlRateProfile->throttle.rcMid8);
                sbufWriteU8(dst, currentControlRateProfile->throttle.rcExpo8);
                sbufWriteU16(dst, currentControlRateProfile->throttle.pa_breakpoint);
                sbufWriteU8(dst, currentControlRateProfile->stabilized.rcYawExpo8);
                sbufWriteU8(dst, 100);                                      // INAV doesn't use rcRate
                sbufWriteU8(dst, 100);                                      // INAV doesn't use rcRate
                sbufWriteU8(dst, currentControlRateProfile->stabilized.rcExpo8);
    
                // added in 1.41
                sbufWriteU8(dst, 0);
                sbufWriteU8(dst, currentControlRateProfile->throttle.dynPID);
                break;
    
            case DJI_MSP_SET_PID:
                // Check if we have enough data for all PID coefficients
                if ((unsigned)sbufBytesRemaining(src) >= ARRAYLEN(djiPidIndexMap) * 3) {
                    for (unsigned i = 0; i < ARRAYLEN(djiPidIndexMap); i++) {
                        pidBankMutable()->pid[djiPidIndexMap[i]].P = sbufReadU8(src);
                        pidBankMutable()->pid[djiPidIndexMap[i]].I = sbufReadU8(src);
                        pidBankMutable()->pid[djiPidIndexMap[i]].D = sbufReadU8(src);
                    }
                    schedulePidGainsUpdate();
                }
                else {
                    reply->result = MSP_RESULT_ERROR;
                }
                break;
    
            case DJI_MSP_PID_ADVANCED:
                // TODO
                reply->result = MSP_RESULT_ERROR;
                break;
    
            case DJI_MSP_SET_FILTER_CONFIG:
            case DJI_MSP_SET_PID_ADVANCED:
            case DJI_MSP_SET_RC_TUNING:
                // TODO
                reply->result = MSP_RESULT_ERROR;
                break;
    
            default:
                // debug[1]++;
                // debug[2] = cmd->cmd;
                reply->result = MSP_RESULT_ERROR;
                break;
        }
    
        // Process DONT_REPLY flag
        if (cmd->flags & MSP_FLAG_DONT_REPLY) {
            reply->result = MSP_RESULT_NO_REPLY;
        }
    
        return reply->result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363

    3. DJI协议相关实现

    3.1 DJI串口初始化

    main
     └──> init
         └──> djiOsdSerialInit
    
    • 1
    • 2
    • 3

    3.2 DJI命令集

    最新DJI命令集请查阅这里

    #define DJI_MSP_API_VERSION             1       // INAV: Implemented     | DSI: ???             | 
    #define DJI_MSP_FC_VARIANT              2       // INAV: Implemented     | DSI: ???             | 
    #define DJI_MSP_FC_VERSION              3       // INAV: Implemented     | DSI: ???             | 
    #define DJI_MSP_NAME                    10      // INAV: Implemented     | DSI: Implemented     | For OSD 'Craft Name'
    #define DJI_MSP_OSD_CONFIG              84      // INAV: Implemented     | DSI: Implemented     | OSD item count + positions
    #define DJI_MSP_FILTER_CONFIG           92      // INAV: Not implemented | DSI: Implemented     |
    #define DJI_MSP_PID_ADVANCED            94      // INAV: Not implemented | DSI: Implemented     |
    #define DJI_MSP_STATUS                  101     // INAV: Implemented     | DSI: Implemented     | For OSD ‘armingTime’, Flight controller arming status
    #define DJI_MSP_RC                      105     // INAV: Implemented     | DSI: Implemented     |
    #define DJI_MSP_RAW_GPS                 106     // INAV: Implemented     | DSI: Implemented     | For OSD ‘GPS Sats’ + coordinates
    #define DJI_MSP_COMP_GPS                107     // INAV: Implemented     | DSI: Not implemented | GPS direction to home & distance to home
    #define DJI_MSP_ATTITUDE                108     // INAV: Implemented     | DSI: Implemented     | For OSD ‘Angle: roll & pitch’
    #define DJI_MSP_ALTITUDE                109     // INAV: Implemented     | DSI: Implemented     | For OSD ‘Numerical Vario’
    #define DJI_MSP_ANALOG                  110     // INAV: Implemented     | DSI: Implemented     | For OSD ‘RSSI Value’, For OSD ‘Battery voltage’ etc
    #define DJI_MSP_RC_TUNING               111     // INAV: Not implemented | DSI: Implemented     |
    #define DJI_MSP_PID                     112     // INAV: Implemented     | DSI: Implemented     | For OSD ‘PID roll, yaw, pitch'
    #define DJI_MSP_BATTERY_STATE           130     // INAV: Implemented     | DSI: Implemented     | For OSD ‘Battery current mAh drawn’ etc
    #define DJI_MSP_ESC_SENSOR_DATA         134     // INAV: Implemented     | DSI: Implemented     | For OSD ‘ESC temperature’
    #define DJI_MSP_STATUS_EX               150     // INAV: Implemented     | DSI: Implemented     | For OSD ‘Fly mode', For OSD ‘Disarmed’
    #define DJI_MSP_RTC                     247     // INAV: Implemented     | DSI: Implemented     | For OSD ‘RTC date time’
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.3. DJI相关函数

    static void djiPackBoxModeBitmask(boxBitmask_t * flightModeBitmask)
    static uint32_t djiPackArmingDisabledFlags(void)
    static uint32_t djiEncodeOSDEnabledWarnings(void)  //TODO
    static void djiSerializeOSDConfigReply(sbuf_t *dst)
    static char * osdArmingDisabledReasonMessage(void)
    static char * osdFailsafePhaseMessage(void)
    static char * osdFailsafeInfoMessage(void)
    static char * navigationStateMessage(void)
    static int32_t osdConvertVelocityToUnit(int32_t vel)
    void osdDJIFormatVelocityStr(char* buff)
    static void osdDJIFormatThrottlePosition(char *buff, bool autoThr )
    static void osdDJIFormatDistanceStr(char *buff, int32_t dist)
    static void osdDJIEfficiencyMahPerKM(char *buff)
    static void osdDJIAdjustmentMessage(char *buff, uint8_t adjustmentFunction)
    static bool osdDJIFormatAdjustments(char *buff)
    static bool djiFormatMessages(char *buff)
    static void djiSerializeCraftNameOverride(sbuf_t *dst)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    4. 协议格式

      +---+---+--------+---------+--------+------+---------+------------------------------+-------------+
      |                            Multiwii Serial Protocol V2                length = 9 + payload size |
      +---+---+--------+---------+--------+------+---------+------------------------------+-------------+
      | $ | X | < ! >  | flag(1) | cmd(2)        | size(2) | payload(16bit len)           | checksum_v2 |
      +---+---+--------+---------+--------+------+---------+------------------------------+-------------+
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • ‘$’:表示SOF(Start Of a frame)
    • ‘X’:表示V2
    • ‘<’: 表示request
    • ‘>’:表示response
    • ‘!’:表示error

    5. 参考资料

    【1】iNav 2.4.0 Release Notes – Support for DJI HD FPV in Feb 6, 2020
    【2】Multiwii Serial Protocol
    【3】Multiwii Serial Protocol (MSP)
    【4】BetaFlight模块设计之三十二:MSP协议模块分析
    【5】iNavFlight之MSP DJI协议天空端请求报文
    【6】iNavFlight之MSP DJI协议飞控端请求应答

  • 相关阅读:
    软件设计师_数据库——关系代数
    每日一题2609. 最长平衡子字符串
    Linux入侵排查
    自定义MVC框架02
    题目:Java 程序员的 10 道 XML 面试题
    SMART PLC星三角延时启动功能块(梯形图FC)
    【目标检测】44、YOLOv2 | 更快 更好 更强的 YOLO 结构
    vue - Vue脚手架/TodoList案例
    编程前置:句子联想游戏
    老夫的正则表达式大成了,桀桀桀桀!!!【Python 正则表达式笔记】
  • 原文地址:https://blog.csdn.net/lida2003/article/details/127835527