exs_pcf8574 扩展库
作者:沈园园 | 最后修改:2026-08-14
一、概述
exs_pcf8574 是 NXP PCF8574 8 位 I2C GPIO 扩展芯片的 LuatOS 扩展库。PCF8574 通过 I2C 串行接口为主控提供 8 位并行 I/O 扩展,仅需 2 根线(SDA + SCL)即可扩展出 8 个准双向 GPIO 引脚。
PCF8574 提供 1 个 8 位端口(P0~P7),共 8 个 GPIO,采用准双向 IO 设计,无需单独配置方向寄存器,支持输入、输出和中断三种工作模式。
1.1 主要特性
- 8 个 I/O 引脚(P0~P7),准双向 IO 设计,无需方向配置
- 内置上拉电阻,输入模式时引脚默认高电平
- 支持 INT 中断引脚,输入电平变化时触发中断通知主控
- 支持批量读写所有 GPIO 端口数据(read_all / write_all)
- 同时支持 PCF8574T(地址 0x20~0x27)和 PCF8574AT(地址 0x38~0x3F)
- I2C 接口速率可达 100kHz(标准模式)
- 3 个硬件地址引脚(A0、A1、A2),每种芯片支持 8 种 I2C 地址
- 工作电压 1.8V~5.5V
- 灌电流驱动能力强(最大 25mA),可直接驱动 LED
- 拉电流驱动能力弱(约 10μA),高电平驱动需外接三极管
1.2 加载方式
local exs_pcf8574 = require "exs_pcf8574"
1.3 注意事项
- I2C 上拉电阻:SDA 和 SCL 需外接 4.7kΩ~10kΩ 上拉电阻到 VCC
- INT 引脚硬件要求:
- PCF8574 的 INT 引脚为开漏输出,需外接上拉电阻
- 重要:很多市售 PCF8574 模块(特别是 LCD I2C 转接板)未引出 INT 引脚,此类模块无法使用硬件中断功能
- 如模块无 INT 引脚,请使用轮询方式(
poll_int())检测引脚变化 - 准双向 IO 模式:PCF8574 没有独立的方向配置寄存器
- 写入
0:引脚输出低电平(强驱动,灌电流) - 写入
1:引脚变为高阻状态(内部上拉,可被外部驱动) - 读取:返回引脚实际电平(无论输出还是输入模式)
- 输入前置条件:任何引脚作为输入读取外部电平前,必须先向该位写入 1
- 输出驱动能力:灌电流(输出低)可达 25mA,可直接驱动 LED;拉电流(输出高)仅约 10μA,驱动 LED 需外接三极管或采用低电平驱动方式
- 中断模式:使用中断功能时,必须在
setup()中传入int_gpio参数 - 多设备支持:通过 A0、A1、A2 引脚配置不同 I2C 地址,同一条 I2C 总线最多可挂载 16 个 PCF8574(8 个 PCF8574T + 8 个 PCF8574AT)
- 地址自动探测:不传
addr参数时,扩展库自动扫描全部 16 个地址(0x20~0x27 + 0x38~0x3F)
1.4 硬件连接
┌──────────────┐ ┌──────────────────┐
│ 主控 │ │ PCF8574 │
│ │ │ GPIO 扩展芯片 │
│ I2C_SDA ─────┼────────────────────┼──→ SDA │
│ │ │ (需外接上拉电阻)│
│ I2C_SCL ─────┼────────────────────┼──→ SCL │
│ │ │ (需外接上拉电阻)│
│ GPIO_INT ←───┼────────────────────┼──→ INT │
│ │ │ (开漏输出, │
│ │ │ 需外接上拉电阻)│
│ │ │ │
│ 3.3V ────────┼────────────────────┼──→ VDD │
│ │ │ │
│ GND ─────────┼────────────────────┼──→ GND │
│ │ │ │
│ │ │ A0~A2 ──────────┼──→ 地址配置
│ │ │ │
│ │ │ P0~P7 ──────────┼──→ 扩展 GPIO 端口
└──────────────┘ └──────────────────┘
1.5 引脚说明
| PCF8574 引脚 | 功能 | 说明 |
|---|---|---|
| VDD | 电源 | 3.3V ~ 5.5V |
| GND | 地 | 电源地 |
| SDA | I2C 数据线 | 需外接上拉电阻 |
| SCL | I2C 时钟线 | 需外接上拉电阻 |
| INT | 中断输出 | 开漏输出,低电平有效,需外接上拉电阻 |
| A0, A1, A2 | 地址配置 | 接 VCC=1,接 GND=0 |
| P0 ~ P7 | 扩展 GPIO | 准双向 I/O |
1.6 地址配置
| 芯片型号 | A0 A1 A2 | 地址范围 | 基地址 |
|---|---|---|---|
| PCF8574T | 000 ~ 111 | 0x20 ~ 0x27 | 0x20 |
| PCF8574AT | 000 ~ 111 | 0x38 ~ 0x3F | 0x38 |
准双向 IO 说明: - 写入
0:引脚输出低电平(强驱动拉低,灌电流最大 25mA) - 写入1:引脚变为高阻状态(内部上拉,可被外部驱动) - 读取:返回引脚实际电平(无论输出还是输入模式)
二、核心示例
2.1 GPIO 输出示例
local exs_pcf8574 = require "exs_pcf8574"
local function output_demo()
-- 初始化(硬件 I2C1)
local result = exs_pcf8574.setup({i2c_id = 1})
if not result then
log.error("exs_pcf8574", "初始化失败")
return
end
-- 配置 P0 为输出模式,初始输出低电平
exs_pcf8574.pin_setup(0x00, 0)
-- 循环切换 P0 电平
while true do
exs_pcf8574.set(0x00, 0) -- 输出低电平(LED 亮)
sys.wait(1000) -- 等待 1 秒
exs_pcf8574.set(0x00, 1) -- 输出高电平(LED 灭)
sys.wait(1000) -- 等待 1 秒
end
end
sys.taskInit(output_demo)
2.2 GPIO 输入示例
local exs_pcf8574 = require "exs_pcf8574"
local function input_demo()
local result = exs_pcf8574.setup({i2c_id = 1})
if not result then return end
-- 配置 P1 为输出模式(用于产生测试信号)
exs_pcf8574.pin_setup(0x01, 0)
-- 配置 P2 为输入模式(需将 P1 和 P2 短接)
exs_pcf8574.pin_setup(0x02)
while true do
exs_pcf8574.set(0x01, 0)
sys.wait(500)
local level = exs_pcf8574.get(0x02)
log.info("exs_pcf8574", "P2 电平:", level)
exs_pcf8574.set(0x01, 1)
sys.wait(500)
level = exs_pcf8574.get(0x02)
log.info("exs_pcf8574", "P2 电平:", level)
end
end
sys.taskInit(input_demo)
2.3 GPIO 中断示例
local exs_pcf8574 = require "exs_pcf8574"
-- P3 中断回调函数
local function P3_int_cbfunc(pin, level)
log.info("exs_pcf8574", "P3 中断触发,pin:", pin, "电平:", level)
end
-- 中断监听任务:在协程上下文中处理中断
local function int_listener_task()
while true do
local msg = sys.waitUntil("exs_pcf8574_INT", 3000)
if msg then
exs_pcf8574.process_int() -- 在协程上下文中处理中断
end
end
end
local function int_demo()
-- 初始化,使用 GPIO2 作为中断引脚
local result = exs_pcf8574.setup({i2c_id = 1, int_gpio = 2})
if not result then return end
-- 启动中断监听任务
sys.taskInit(int_listener_task)
-- 配置 P2 为输出模式(用于触发 P3 中断,需短接 P2 和 P3)
exs_pcf8574.pin_setup(0x02, 0)
-- 配置 P3 为中断模式
exs_pcf8574.pin_setup(0x03, P3_int_cbfunc)
while true do
exs_pcf8574.set(0x02, 0)
sys.wait(1000)
exs_pcf8574.set(0x02, 1)
sys.wait(1000)
end
end
sys.taskInit(int_demo)
2.4 批量读写示例
local exs_pcf8574 = require "exs_pcf8574"
local function batch_demo()
local result = exs_pcf8574.setup({i2c_id = 1})
if not result then return end
while true do
-- P0~P3 输出低,P4~P7 输出高
exs_pcf8574.write_all(0xF0)
sys.wait(1000)
local data = exs_pcf8574.read_all()
if data ~= false then
log.info("exs_pcf8574", string.format("端口数据: 0x%02X", data))
end
-- 全部输出低
exs_pcf8574.write_all(0x00)
sys.wait(1000)
data = exs_pcf8574.read_all()
if data ~= false then
log.info("exs_pcf8574", string.format("端口数据: 0x%02X", data))
end
end
end
sys.taskInit(batch_demo)
三、常量解释
本扩展库无常量导出。
四、函数详解
4.1 固定接口
4.1.1 exs_pcf8574.setup(config)
功能
初始化 PCF8574,配置 I2C 通信参数,自动探测设备地址
参数
config
参数含义:初始化配置表
数据类型:table
取值说明:
- i2c_id (number):硬件 I2C 总线 id,如 0 或 1
- scl (number):软件 I2C 的 SCL 引脚号
- sda (number):软件 I2C 的 SDA 引脚号
- addr (number):指定设备地址(可选,不传则自动探测 PCF8574T/AT 全部 16 个地址)
- int_gpio (number):中断引脚 GPIO id(可选,不传则不使用中断)
是否必选:是
注意事项:i2c_id 与 scl/sda 二选一,同时传优先硬件 I2C
参数示例:{i2c_id = 1} 或 {scl = 67, sda = 66}
返回值
含义说明:初始化是否成功
数据类型:boolean
注意事项:初始化失败时请检查接线、供电和地址跳线配置
返回示例:true
示例
-- 基础初始化(硬件 I2C,不使用中断)
local result = exs_pcf8574.setup({i2c_id = 1})
-- 使用软件 I2C
local result = exs_pcf8574.setup({scl = 67, sda = 66})
-- 指定地址 + 中断功能
local result = exs_pcf8574.setup({i2c_id = 1, addr = 0x20, int_gpio = 2})
4.1.2 exs_pcf8574.get_data()
功能
读取所有 GPIO 引脚状态,返回结构化数据
参数
无
返回值
含义说明:所有引脚状态
数据类型:table
字段说明:
- p0~p7 (number):P0~P7 引脚电平,0=低电平,1=高电平
- raw (number):原始字节数据,bit0=P0, bit7=P7
注意事项:读取失败返回 nil
返回示例:{p0=1, p1=0, p2=1, ..., p7=0, raw=0x55}
示例
local data = exs_pcf8574.get_data()
if data then
log.info("exs_pcf8574", "引脚状态:", json.encode(data))
end
4.1.3 exs_pcf8574.close()
功能
关闭 PCF8574,释放所有资源(I2C、内部变量)
参数
无
返回值
含义说明:无返回值
注意事项:关闭后所有 GPIO 配置将失效,如需使用需重新 setup()
示例
exs_pcf8574.close()
4.1.4 exs_pcf8574.process_int()
功能
处理中断事件:读取所有引脚状态并分发用户回调函数
参数
无
返回值
含义说明:无返回值
注意事项:此函数必须在协程上下文中调用(如 sys.waitUntil 返回后)
使用场景:配合 setup() 中的 int_gpio 参数,在监听任务中调用
示例
-- 中断监听任务
local function int_listener_task()
while true do
local msg = sys.waitUntil("exs_pcf8574_INT", 3000)
if msg then
exs_pcf8574.process_int() -- 在协程上下文中处理中断
end
end
end
sys.taskInit(int_listener_task)
4.1.5 exs_pcf8574.poll_int()
功能
轮询检测引脚变化并分发用户回调(适用于无 INT 引脚的 PCF8574 模块)
参数
无
返回值
含义说明:无返回值
注意事项:此函数需要在定时器中周期性调用(如每 100ms)
使用场景:当 PCF8574 模块未引出 INT 引脚时,通过轮询检测引脚变化
工作原理:读取输入寄存器并与上次值比较,检测到变化时调用对应回调
示例
-- 启动轮询定时器(每 100ms 检测一次)
sys.timerLoopStart(function()
exs_pcf8574.poll_int()
end, 100)
4.1.6 exs_pcf8574.version()
功能
获取 exs_pcf8574 库的版本号
参数
无
返回值
含义说明:版本号字符串
数据类型:string
注意事项:格式为 "yyyymmddhhmm"
返回示例:"202608061000"
示例
local ver = exs_pcf8574.version()
log.info("exs_pcf8574", "版本号:", ver)
4.2 引脚配置接口
4.2.1 exs_pcf8574.pin_setup(pin, mode)
功能
配置 PCF8574 单个引脚的工作模式,支持输出、输入和中断三种模式
参数
pin
参数含义:PCF8574 的引脚 ID
数据类型:number
取值范围:0x00 ~ 0x07,对应 P0 ~ P7
是否必选:是
参数示例:0x00
mode
参数含义:引脚工作模式
数据类型:number | function | nil
取值说明:
- number (0):输出模式,输出低电平(灌电流驱动,可直接驱动 LED)
- number (1):输出模式,输出高电平(内部上拉,弱驱动)
- nil:输入模式(内部上拉,外部驱动)
- function:中断模式,参数为回调函数
回调函数格式:function cb_func(pin, level) end
- pin:触发中断的引脚 ID(number 类型)
- level:触发中断后读取到的电平(0=低,1=高)
是否必选:是
注意事项:中断模式需要在 setup() 中传入 int_gpio 参数
参数示例:0
返回值
含义说明:配置是否成功
数据类型:boolean
返回示例:true
示例
-- P0 配置为输出低电平
exs_pcf8574.pin_setup(0x00, 0)
-- P1 配置为输入模式
exs_pcf8574.pin_setup(0x01)
-- P4 配置为中断模式
local function P4_int_cbfunc(pin, level)
log.info("exs_pcf8574", "P4 中断:", pin, level)
end
exs_pcf8574.pin_setup(0x04, P4_int_cbfunc)
4.2.2 exs_pcf8574.pin_close(pin)
功能
关闭 PCF8574 单个引脚,清除中断回调,恢复为默认输入模式
参数
pin
参数含义:PCF8574 的引脚 ID
数据类型:number
取值范围:0x00 ~ 0x07
是否必选:是
参数示例:0x03
返回值
含义说明:关闭是否成功
数据类型:boolean
返回示例:true
示例
exs_pcf8574.pin_close(0x03)
4.3 单引脚读写接口
4.3.1 exs_pcf8574.set(pin, level)
功能
设置 PCF8574 单个引脚的输出电平
参数
pin
参数含义:PCF8574 的引脚 ID
数据类型:number
取值范围:0x00 ~ 0x07
是否必选:是
参数示例:0x03
level
参数含义:输出电平
数据类型:number
取值范围:0(低电平,灌电流驱动)或 1(高电平,内部上拉)
是否必选:是
注意事项:0=低电平可直接驱动 LED;1=高电平驱动能力极弱,需外接三极管
参数示例:0
返回值
含义说明:设置是否成功
数据类型:boolean
返回示例:true
示例
exs_pcf8574.set(0x03, 0) -- P3 输出低电平
exs_pcf8574.set(0x05, 1) -- P5 输出高电平
4.3.2 exs_pcf8574.get(pin)
功能
读取 PCF8574 单个引脚的输入电平
参数
pin
参数含义:PCF8574 的引脚 ID
数据类型:number
取值范围:0x00 ~ 0x07
是否必选:是
注意事项:引脚必须先通过 pin_setup() 配置为输入或中断模式
参数示例:0x02
返回值
含义说明:引脚输入电平
数据类型:number 或 boolean
取值范围:0(低电平),1(高电平);读取失败返回 false
返回示例:1
示例
local level = exs_pcf8574.get(0x02)
if level ~= false then
log.info("exs_pcf8574", "P2 电平:", level)
end
4.4 批量读写接口
4.4.1 exs_pcf8574.read_all()
功能
读取 PCF8574 所有引脚状态(字节),一次性读取 8 个引脚的状态
参数
无
返回值
含义说明:8 位端口数据
数据类型:number 或 boolean
取值范围:0x00 ~ 0xFF,bit0=P0, bit7=P7;读取失败返回 false
返回示例:0xF0
示例
local data = exs_pcf8574.read_all()
if data ~= false then
log.info("exs_pcf8574", string.format("端口数据: 0x%02X", data))
end
4.4.2 exs_pcf8574.write_all(data)
功能
写入 PCF8574 所有引脚状态(字节),一次性设置 8 个引脚的输出状态
参数
data
参数含义:8 位端口数据,每位对应一个引脚
数据类型:number
取值范围:0x00 ~ 0xFF,bit0=P0, bit7=P7
是否必选:是
注意事项:写入 0 的引脚输出低电平(灌电流驱动),写入 1 的引脚变为高阻(内部上拉)
参数示例:0xF0
返回值
含义说明:写入是否成功
数据类型:boolean
返回示例:true
示例
exs_pcf8574.write_all(0xF0) -- P0~P3 输出低,P4~P7 输出高
exs_pcf8574.write_all(0x00) -- 全部输出低
exs_pcf8574.write_all(0xFF) -- 全部输出高
五、版本更新说明
版本号:202608061000
- 更新时间:2026-08-06
- 更新内容:
- 初版实现
- 支持 PCF8574T(0x20~0x27)和 PCF8574AT(0x38~0x3F)地址自动探测
- 支持硬件 I2C 和软件 I2C
- 支持 GPIO 输入/输出/中断三种模式
- 支持批量读写接口(read_all / write_all)
- 支持 I2C 总线恢复
六、产品支持说明
所有支持 luatos 二次开发的模块,具体可以查看选型手册。