这次还是使用了idf老版本4.4.7,上次用了5.3,感觉不好用,官方的MCP23017芯片是英文版,真的很难读明白,可能是我英语水平不够吧。先看看每个寄存器的功能:
IODIRA 和 IODIRB: 输入/输出方向寄存器
IPOLA 和 IPOLB: 输入极性寄存器
GPINTENA 和 GPINTENB: GPIO 中断使能寄存器
DEFVALA 和 DEFVALB: 默认值寄存器
INTCONA 和 INTCONB: 中断控制寄存器
IOCONA 和 IOCONB: 配置寄存器
GPPUA 和 GPPUB: 上拉电阻寄存器
INTFA 和 INTFB: 中断标志寄存器
INTCAPA 和 INTCAPB: 中断捕获寄存器
GPIOA 和 GPIOB: 数据方向寄存器
OLATA 和 OLATB: 输出锁存器寄存器
寄存器说明
- 输入/输出方向寄存器 (IODIRA 和 IODIRB)
地址: IODIRA 的地址是 0x00,IODIRB 的地址是 0x01。
功能: 这些寄存器用于设置 Port A 和 Port B 上每个 GPIO 引脚的输入/输出方向。
0: 对应的引脚配置为输出。
1: 对应的引脚配置为输入。 - 输入极性寄存器 (IPOLA 和 IPOLB)
地址: IPOLA 的地址是 0x02,IPOLB 的地址是 0x03。
功能: 这些寄存器用于反转 Port A 和 Port B 上每个 GPIO 引脚的输入状态。
0: 对应的引脚保持原始输入状态。
1: 对应的引脚输入状态被反转。 - GPIO 中断使能寄存器 (GPINTENA 和 GPINTENB)
地址: GPINTENA 的地址是 0x04,GPINTENB 的地址是 0x05。
功能: 这些寄存器用于启用 Port A 和 Port B 上每个 GPIO 引脚的中断功能。
0: 对应的引脚的中断功能禁用。
1: 对应的引脚的中断功能启用。 - 默认值寄存器 (DEFVALA 和 DEFVALB)
地址: DEFVALA 的地址是 0x06,DEFVALB 的地址是 0x07。
功能: 这些寄存器用于设置 Port A 和 Port B 上每个 GPIO 引脚的默认比较值。当 GPIO 引脚的输入状态与默认值寄存器中的值不同时,会产生中断。 - 中断控制寄存器 (INTCONA 和 INTCONB)
地址: INTCONA 的地址是 0x08,INTCONB 的地址是 0x09。
功能: 这些寄存器用于配置 Port A 和 Port B 上每个 GPIO 引脚的中断触发方式(上升沿、下降沿或变化)。
0: 对应的引脚中断触发方式为变化。
1: 对应的引脚中断触发方式为上升沿或下降沿。 - 配置寄存器 (IOCONA 和 IOCONB)
地址: IOCONA 的地址是 0x0A,IOCONB 的地址是 0x0B。
功能: 这些寄存器用于配置全局设置,如中断行为、序列号等。 - 上拉电阻寄存器 (GPPUA 和 GPPUB)
地址: GPPUA 的地址是 0x0C,GPPUB 的地址是 0x0D。
功能: 这些寄存器用于启用 Port A 和 Port B 上每个 GPIO 引脚的内部上拉电阻。
0: 对应的引脚的上拉电阻禁用。
1: 对应的引脚的上拉电阻启用。 - 中断标志寄存器 (INTFA 和 INTFB)
地址: INTFA 的地址是 0x0E,INTFB 的地址是 0x0F。
功能: 这些寄存器用于指示 Port A 和 Port B 上每个 GPIO 引脚是否产生了中断。当某个引脚产生中断时,相应的位将被设置为 1。 - 中断捕获寄存器 (INTCAPA 和 INTCAPB)
地址: INTCAPA 的地址是 0x10,INTCAPB 的地址是 0x11。
功能: 这些寄存器用于存储 Port A 和 Port B 上每个 GPIO 引脚在产生中断时的输入状态。 - 数据方向寄存器 (GPIOA 和 GPIOB)
地址: GPIOA 的地址是 0x12,GPIOB 的地址是 0x13。
功能: 这些寄存器用于读取 Port A 和 Port B 上每个 GPIO 引脚的当前输入状态。 - 输出锁存器寄存器 (OLATA 和 OLATB)
地址: OLATA 的地址是 0x14,OLATB 的地址是 0x15。
功能: 这些寄存器用于设置 Port A 和 Port B 上每个 GPIO 引脚的输出状态。
代码,已经测试读写寄存器没有问题,输入可以检测(没用中断),也可以正常输出:
c
#include <driver/i2c.h>
#include <esp_log.h>
#define I2C_MASTER_SCL_IO 15 /*!< gpio number for I2C master clock */
#define I2C_MASTER_SDA_IO 16 /*!< gpio number for I2C master data */
#define I2C_MASTER_NUM I2C_NUM_0 /*!< I2C port number for master dev */
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
#define I2C_MASTER_FREQ_HZ 400000 /*!< I2C master clock frequency */
#define MCP23017_ADDRESS 0x20 /*!< I2C address of MCP23017 */
static const char *TAG = "I2C_MASTER";
void i2c_master_init() {
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = I2C_MASTER_SCL_IO,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
i2c_param_config(I2C_MASTER_NUM, &conf);
i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}
void read_register(uint8_t reg, uint8_t *value)
{
uint8_t data[1] = {reg};
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
// 写入寄存器地址
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MCP23017_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write(cmd, data, 1, true);
// 读取数据
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MCP23017_ADDRESS << 1) | I2C_MASTER_READ, true);
i2c_master_read(cmd, value, 1, I2C_MASTER_LAST_NACK);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
}
void write_register(uint8_t reg, uint8_t value) {
uint8_t data[2] = {reg, value};
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MCP23017_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write(cmd, data, sizeof(data), true);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
}
void set_output_direction(uint8_t port, uint8_t direction) {
// 设置输出方向
// 方向:0为输出,1为输入
write_register(port == 0 ? 0x00 : 0x01, direction);
}
void set_output_value(uint8_t port, uint8_t value) {
// 设置输出值
// value: 输出值
//write_register(port == 0 ? 0x12 : 0x13, value);
write_register(port == 0 ? 0x14 : 0x15, value);
}
void example() {
i2c_master_init();
// 设置PORTA为全输出
set_output_direction(0, 0x00);
// 设置PORTA的输出值为0xFF
set_output_value(0, 0x0f);
}
void app_main() {
i2c_master_init();
// 设置PORT A B为全输出
set_output_direction(0, 0x00);
set_output_direction(1, 0x00);
// 设置PORTA的输出值为0xf5 B为0xAF
set_output_value(0, 0xf5);
set_output_value(1, 0xAf);
// 设置PORT A B为输入
set_output_direction(0, 0xff);
set_output_direction(1, 0xff);
uint8_t data1[1] = {0};
uint8_t data2[1] = {0};
while (true)
{
read_register(0x12, data1);
read_register(0x13, data2);
ESP_LOGI(TAG, "read register 0x12: %02X 0x13: %02X", data1[0], data2[0]);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
希望对你有帮助。