跳转至

13 exmux-总线上拉

作者:陈取德 | 最后修改:2026-04-10

一、概述

在实际开发过程中,时常会遇到 i2c 或者 spi 通讯失败的情况,测量总线的波形图时低于模组 IO 电平,必须要将同一路 i2c 或者 spi 总线上设备全部打开才能够正常通讯。按照 i2c 和 spi 的总线设计来说,理论上应该支持单总线同时挂载多台设备,并且多台设备之间不互相干扰的。为什么实际上却出现了干扰呢?同样的情况出现在了我们合宙对外公开售卖的开发板中,经过我们的测试发现,原因出在了两个地方:

  1. LDO:很多客户在使用i2c/spi设备时,参考设备的建议电路设计,会将i2c/spi总线上拉到设备供电的LDO上,这个逻辑是没有问题的,设备打开同时,拉高总线工作。但是问题就出在LDO上,以LP5907MFX为例,其在关闭时,LDO电源输出管脚会有下拉电阻到地,进而造成i2c电平拉低,比如Air780EHM电平设置为3.3V时,会被拉低到2.2V甚至2V以下,造成i2c通讯不正常。

  1. 设备芯片:类似ES8311或者CH390这类芯片,他们的芯片设计时每个管脚都接了下拉电阻,在设备关断时并不会进入高阻态,而是经过下拉电阻到了地,导致i2c/spi总线被拉低了,通讯不正常。

为解决以上的问题,我们在每个产品的硬件设计建议中增加了 i2c/spi 的说明(详情见各型号目录下”硬件开发资料“),并制作了 exmux 库,用于在单总线挂多设备的应用场景中,能够统一管理每个 i2c/spi 总线上所有设备的供电使能,确保总线不会因为 LDO 或者设备芯片原因被拉低,导致通讯失败。

注意事项

exmux 库已内置 Air780 系列、Air8000 系列开发板的配置,涵盖如下版本:

  • Air780系列的V1.2开发板 - "DEV_BOARD_780_V1.2"
  • Air780系列的V1.3开发板 - "DEV_BOARD_780_V1.3"
  • Air8000系列的V2.0开发板 - "DEV_BOARD_8000_V2.0"

在 exmux.setup()时填入对应开发板的型号字符串即可;

如果是使用自己的板子,则填入自定义的配置表;

二、核心示例

1、核心示例是指:使用本库文件提供的核心 API,开发的基础业务逻辑的演示代码;

2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;

3、更加完整和详细的 demo,请参考 LuatOS 仓库 中各个产品目录下的相关示例:demo/camera:

-- 使用开发板
exmux.setup("DEV_BOARD_8000_V2.0")
-- 打开i2c1总线上的设备
exmux.open("i2c1")
-- 关闭i2c1总线上的设备
exmux.close("i2c1")


-- 使用自定义配置
ex_device_param = {
    i2c1 = {
        pwr1 = 2,     -- 设备电源使能
        pwr2 = 20,    -- 设备电源使能
        pwr3 = 23     -- 设备电源使能
    },
    spi1 = {
        pwr1 = 1,     -- 设备电源使能
        cs1 = 8,      -- 片选引脚
        pwr2 = 3,     -- 设备电源使能
        cs2 = 9       -- 片选引脚
    }
}
exmux.setup(ex_device_param)

-- 打开i2c1分组
exmux.open("i2c1")

-- 关闭spi1分组
exmux.close("spi1")

三、常量详解

核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;

合宙 LuatOS 内核固件中定义的核心库常量,和此扩展库功能有关,所以也会放到本文描述。

该函数库无常量

四、函数详解

4.1 exmux.setup(ex_device_param)

功能

初始化 IO 管理配置表;

注意事项

根据实际硬件设计中的 i2c 和 spi 填写,并按照总线分组,初始化时使用 i2c1 则填在 i2c1 的表格中,spi 同理;

增加管脚时按照表格的格式增加,勿修改表格格式和名称;

参数

ex_device_param

常量含义:IO管理配置信息
数据类型:string or table
是否必选:必须传入此参数;
注意事项:如i2c或者spi总线仅挂单个设备时无需配置,该功能仅对单路总线多设备场景;
示例代码:string格式是合宙公开售卖的开发板Air780系列所有型号均通用一个配置表,只有版本差异,目前支持以下三种型号的配置表,直接复制填入即可:
        "DEV_BOARD_780_V1.2"
        "DEV_BOARD_780_V1.3"
        "DEV_BOARD_8000_V2.0"

         table格式是自定义配置表,根据硬件设计中所使用的i2c/spi分组填写对应表格,如果硬件设计上没有供电控制,开机后i2c/spi设备一直处于上电状态则不需要填入配置表
         ex_device_param = {

            参数含义:配置i2c1分组参数
            数据类型:table
            取值范围:i2c1i2c0
            是否必选:可选;
            注意事项:使用了哪路i2c就填哪路i2c总线号,没使用i2c时不填
            参数示例: 
            i2c1 = {

                参数含义:i2c1总线上设备的供电使能管脚
                数据类型:munber
                取值范围:产品支持的 GPIO ID,可以查询具体产品的硬件手册;
                是否必选:必选;
                注意事项:需是挂载在i2c1总线上的设备,如果硬件设计上没有供电控制,开机后i2c设备一直有电时不需要添加到配置表中
                参数示例:
                pwr1 = 2,

                参数含义:i2c1总线上设备的供电使能管脚
                数据类型:munber
                取值范围:产品支持的 GPIO,可以查询具体产品的硬件手册;
                是否必选:必选;
                注意事项:需是挂载在i2c1总线上的设备,如果硬件设计上没有供电控制,开机后i2c设备一直有电时不需要添加到配置表中
                参数示例:
                pwr2 = 20,

                ...
            },

            参数含义:配置spi1分组参数
            数据类型:table
            取值范围:spi1spi0
            是否必选:可选;
            注意事项:使用了哪路i2c就填哪路i2c总线号,没使用i2c时不填
            参数示例:
            spi1 = {
                参数含义:spi1总线上设备的供电使能管脚
                数据类型:munber
                取值范围:产品支持的 GPIO,可以查询具体产品的硬件手册;
                是否必选:必选;
                注意事项:需是挂载在spi1总线上的设备,如果硬件设计上没有供电控制,开机后spi设备一直有电时不需要添加到配置表中
                参数示例:
                pwr1 = 1,

                参数含义:spi1总线上设备的片选管脚
                数据类型:munber
                取值范围:产品支持的 GPIO,可以查询具体产品的硬件手册;
                是否必选:必选;
                注意事项:需是挂载在spi1总线上的设备,且是ex_device_param.spi1.pwr1控制设备的CS管脚,如果硬件设计上没有供电控制,开机后spi设备一直有电时不需要添加到配置表中
                参数示例:
                cs1 = 8,
                ...
            }
        }

返回值

local result = exmux.setup(ex_device_param)

此处有一个返回值 result

result

参数含义:初始化i2c/spi各分组配置表结果
数据类型:boolean
取值范围:true or false;
注意事项:true则初始化成功false则初始化失败,失败原因见打印;
返回示例:true

示例

-- 使用开发板版本初始化
exmux.setup("DEV_BOARD_8000_V2.0")

-- 或使用自定义配置初始化
ex_device_param = {
    i2c1 = {
        pwr1 = 2,     -- 设备电源使能
        pwr2 = 20,    -- 设备电源使能
        pwr3 = 23     -- 设备电源使能
    },
    spi1 = {
        pwr1 = 1,     -- 设备电源使能
        cs1 = 8,      -- 片选引脚
        pwr2 = 3,     -- 设备电源使能
        cs2 = 9       -- 片选引脚
    }
}
exmux.setup(ex_device_param)

4.2 exmux.open(bus_id)

功能

i2c/spi 分组设备供电上拉,在设备需要工作前调用;

注意事项

请注意区分 i2c/spi 总线号,是字符串形式;

参数

bus_id

参数含义:i2c/spi的总线号
数据类型:string
取值范围:"i2c0""i2c1""spi0""spi1";
是否必选:必选;
注意事项:函数内已经自动处理了对应总线号的表格,所以只需要填写取值范围中的四个字符串即可;
示例代码:打开i2c0总线的所有设备
         exmux.open("i2c0")

返回值

local result = exmux.open(bus_id)

此处有一个返回值 result

result

参数含义:对应分组的打开结果;
数据类型:boolean
取值范围:true or false;
注意事项:true的含义为打开成功或者此前已经打开过,不需要再打开;
         false的含义为未找到对应的总线号,此前未设置或填写错误;
返回示例:true

4.3 exmux.close(bus_id)

功能

i2c/spi 分组设备供电下拉,在关闭设备后调用;

注意事项

请注意区分 i2c/spi 总线号,是字符串形式;

参数

bus_id

参数含义:i2c/spi的总线号
数据类型:string
取值范围:"i2c0""i2c1""spi0""spi1";
是否必选:必选;
注意事项:函数内已经自动处理了对应总线号的表格,所以只需要填写取值范围中的四个字符串即可;
示例代码:关闭i2c0总线的所有设备
         exmux.close("i2c0")

返回值

local result = exmux.close(bus_id)

此处有一个返回值 result

result

参数含义:对应分组的关闭结果;
数据类型:boolean
取值范围:true or false;
注意事项:true的含义为关闭成功或者此前已经关闭过,不需要再关闭;
         false的含义为未找到对应的总线号,此前未设置或填写错误;
返回示例:true

五、产品支持说明

支持 LuatOS 开发的 Air780 系列,Air8000 系列,Air8101 系列 产品都支持 exmux 库。