ArduPilot开源代码之AP_IOMCU

1. 源由

之前我们讨论了FMU + IMU的硬件架构,该设计主要是由于STM32外设IO、MCU计算资源不够应付外设的情况下,采用的一种折中的方案。

-ArduPilot开源代码之FMU+IOMCU设计
-Ardupilot开源飞控之IOMCU设计细节

随着技术的日益发展,更高效,更多的IO资源的MCU芯片已经具备。当然在特殊应用领域或者方案,仍然可以考虑采用这一方案。

为此,我们研究下AP_IOMCU应用类:FMU与IOMCU通信实现。

注:参考Pixhawk4硬件飞控板。

2. 框架设计

hwdef.h头文件定义FMU端通信端口UART8.

#define HAL_UART_IOMCU_IDX 8
#define HAL_UART_IO_DRIVER ChibiOS::UARTDriver uart_io(HAL_UART_IOMCU_IDX)

libraries/AP_HAL_ChibiOS/HAL_ChibiOS_Class.cpp初始化FMU串口实例.

#if HAL_WITH_IO_MCU
HAL_UART_IO_DRIVER;
#include <AP_IOMCU/AP_IOMCU.h>
AP_IOMCU iomcu(uart_io);
#endif

2.1 启动代码

AP_Vehicle::setup
 └──> AP_BoardConfig::init
     └──> AP_BoardConfig::board_setup
         └──> hal.rcout->init();
             └──> AP_IOMCU::init
                 └──> !hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_IOMCU::thread_main, void)

2.2 初始化

采用1.5Mbps,默认(测试)确保,当前连接在无CTS/RTS的方式下进行通信无异常。

void AP_IOMCU::init(void)
{
    // uart runs at 1.5MBit, 不知道为何,因为后面`thread_main`里面仍然要初始化一遍。难道是为了`check_crc`
    uart.begin(1500*1000, 128, 128);
    uart.set_unbuffered_writes(true);

#if IOMCU_DEBUG_ENABLE
    crc_is_ok = true;
#else
    AP_BoardConfig *boardconfig = AP_BoardConfig::get_singleton();
    if ((!boardconfig || boardconfig->io_enabled() == 1) && !hal.util->was_watchdog_reset()) {
        check_crc();
    } else {
        crc_is_ok = true;
    }
#endif

    if (!hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_IOMCU::thread_main, void), "IOMCU",
                                      1024, AP_HAL::Scheduler::PRIORITY_BOOST, 1)) {
        AP_HAL::panic("Unable to allocate IOMCU thread");
    }
    initialised = true;
}

2.3 动态过程

AP_IOMCU::thread_main(void)
|
|-- 初始化部分
|   |-- thread_ctx = chThdGetSelfX()
|   |-- chEvtSignal(thread_ctx, initial_event_mask)
|   |-- uart.begin(1500*1000, 128, 128)
|   |-- uart.set_unbuffered_writes(true)
|
|-- 条件编译(DSHOT 相关配置)
|   |-- HAL_WITH_IO_MCU_BIDIR_DSHOT
|       |-- erpm_period_ms = 10
|       |-- HAVE_AP_BLHELI_SUPPORT
|           |-- blh = AP_BLHeli::get_singleton()
|           |-- erpm_period_ms = constrain_int16(1000 / blh->get_telemetry_rate(), 1, 1000)
|
|-- 触发初始化事件
|   |-- trigger_event(IOEVENT_INIT)
|
|-- 主循环
    |
    |-- 检查与 IOMCU 的通信
    |   |-- now_ms = AP_HAL::millis()
    |   |-- last_reg_access_ms 检查
    |       |-- INTERNAL_ERROR(AP_InternalError::error_t::iomcu_reset)
    |
    |-- 等待事件
    |   |-- mask = chEvtWaitAnyTimeout(~0, chTimeMS2I(10))
    |
    |-- 处理各类 IO 事件
        |
        |-- IOEVENT_SEND_PWM_OUT
        |   |-- send_servo_out()
        |
        |-- IOEVENT_INIT
        |   |-- 读取协议版本
        |       |-- read_registers()
        |       |-- is_chibios_backend 赋值
        |       |-- DEV_PRINTF
        |       |-- modify_register()
        |       |-- AP_IOMCU_FORCE_ENABLE_HEATER
        |           |-- modify_register()
        |
        |-- IOEVENT_MIXING
        |   |-- write_registers(PAGE_MIXING, ...)
        |
        |-- IOEVENT_FORCE_S
<think>嗯,用户遇到了一个C++编译错误,说声明不兼容。错误信息显示在My_AP_Notify.cpp的第243行,函数add_backend_helper的声明和My_AP_Notify.h的第230行的声明不一致。我需要帮他分析可能的原因。 首先,声明不兼容通常是因为头文件中的函数声明和源文件中的定义不一致。比如参数类型、数量或者const修饰符不同。用户应该检查两个地方的函数签名是否完全一致。比如,头文件里的函数参数是NotifyDevice* backend,而源文件里的定义是否多了一个参数,或者参数类型不同?或者有没有const的问题? 另外,检查作用域是否正确。函数是否在My_AP_Notify类的内部声明,而在定义的时候是否漏掉了类名作用域?比如源文件中的函数定义是否写成void add_backend_helper(NotifyDevice* backend),而没有加上My_AP_Notify::,这会导致编译器认为是一个普通函数,而不是类的成员函数,从而引发声明不匹配的错误。 还有可能,头文件和源文件是否不同步?比如修改了头文件后没有重新编译相关部分,导致编译器使用了旧的声明。这时候清理项目重新构建可能会有帮助。 用户提供的错误信息里,源文件的路径有一个空格,可能是“AP_My_Notify /My_AP_Notify.h”,这里的空格是不是实际存在的?如果路径中有空格,可能导致编译器找不到头文件,或者包含错误的文件,进而引发声明不一致的问题。需要确认文件路径是否正确,是否有意外的空格或拼写错误。 总结一下,用户应该按照以下步骤检查:1. 对比头文件和源文件中的函数声明,确保完全一致。2. 检查作用域是否正确使用了类名限定。3. 确认文件路径和名称正确,没有空格或拼写错误。4. 清理项目并重新编译。如果问题依旧,可能需要查看具体的代码差异。</think>这个错误属于C/C++编译器的类型检查错误,具体原因是函数声明与实现不匹配。以下是分步骤解析和解决方案: ✦ **错误诊断步骤:** 1. **定位冲突位置**: - 头文件声明位置:$\text{My\_AP\_Notify.h}$ 第230行 - 源文件实现位置:$\text{My\_AP\_Notify.cpp}$ 第243行 2. **对比函数签名**(关键排查点): ```cpp // 检查头文件声明 class My_AP_Notify { void add_backend_helper(NotifyDevice *backend); // 声明版本 }; // 检查源文件实现 void My_AP_Notify::add_backend_helper(NotifyDevice *backend) { // 实现版本 //... } ``` - 需验证以下要素是否完全一致: - 返回值类型(包括`void`) - 函数名大小写(区分`Add_Backend_Helper`与`add_backend_helper`) - 参数类型(`NotifyDevice*`是否匹配) - `const`修饰符(是否缺少`const`关键字) 3. **常见不匹配情形**: - ❌ 参数列表差异(如多/少参数) - ❌ 作用域错误(未使用`My_AP_Notify::`限定符) - ❌ 头文件未同步(修改声明后未重新编译) ✦ **典型修复方案:** 1. **强制签名对齐**: ```diff // My_AP_Notify.cpp - void add_backend_helper(NotifyDevice *backend) + void My_AP_Notify::add_backend_helper(NotifyDevice *backend) ``` 2. **检查头文件包含链**: - 确认$\text{My\_AP\_Notify.cpp}$包含的是最新版本的$\text{My\_AP\_Notify.h}$ - 验证文件路径中的空格问题:原错误信息中路径`/AP_My_Notify /My_AP_Notify.h`含可疑空格,需检查实际路径是否应为`/AP_My_Notify/My_AP_Notify.h` 3. **编译器清理操作**: ```bash make clean # 清理旧编译结果 make -j8 # 重新完整编译 ``` ✦ **附加验证技巧**: - 使用IDE的`Go to Definition`功能直接跳转对比声明/实现 - 在编译命令中添加`-Wall -Werror`加强类型检查 - 检查头文件防护宏(`#ifndef HEADER_H`)是否冲突 若问题持续,建议提供实际代码片段以进一步分析函数签名的二进制层面差异。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值