前言
ESP32自带的IO管脚比较有限,这个时候我们就需要使用一些IO扩展芯片扩展我们的IO,今天就介绍一款使用I2C接口扩展8个IO的芯片 PCA9557
PCA 9557芯片介绍
PCA9557是一款硅CMOS电路,为SMBus和I²C总线应用提供并行输入/输出扩展。PCA9557由8位输入端口寄存器、8位输出端口寄存器和I²C总线/SMBus接口组成。具有低电流消耗和高阻抗开漏输出引脚IO0。
通过写入I/O配置寄存器,系统主器件可将PCA9557的I/O用作输入或输出。通过写入高电平有效的极性反转寄存器,系统主器件还可反转PCA9557输入。最后,通过在复位输入中加入低电平,系统主器件可在超时时使PCA9557复位。
上电复位会将寄存器设为其默认状态并初始化I²C总线/SMBus状态机。1引脚可引发相同的复位/初始化而无需使部件断电。
原理图如下:
代码编写
在 i2c_self_test的例程上修改
- 在 i2c_self_test–main文件夹下创建 bsp_pca9557.c和bsp_pca9557.h文件
增加文件后,然后将 CMakeList.txt 文件修改为:
idf_component_register(SRCS “i2c_example_main.c”“bsp_pca9557.c”
INCLUDE_DIRS “.”)
- 根据 pca9557 数据手册上读写的要求,编写pca9557读写寄存器的函数,并在读写寄存器函数的基础上封装设置IO,读取IO的函数,具体如下:
- bsp_pca9557.c
#include "bsp_pca9557.h"
#include <stdio.h>
#include "esp_log.h"
#include "driver/i2c.h"
#define PCA9557_I2C_MASTER_SCL_IO 2 /*!< gpio number for I2C master clock */
#define PCA9557_I2C_MASTER_SDA_IO 15 /*!< gpio number for I2C master data */
#define PCA9557_I2C_MASTER_NUM I2C_NUM_1 /*!< I2C port number for master dev */
#define PCA9557_I2C_MASTER_FREQ_HZ 200000 /*!< I2C master clock frequency */
#define PCA9557_I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define PCA9557_I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
/*******************************************************************************
* 名 称: bsp_Pca9557Init
* 功 能: PCA9577 I2C初始化函数
* 入口参数: 无
* 出口参数: 无
* 作 者: Roger-WY
* 创建日期: 2021-05-08
* 修 改:
* 修改日期:
* 备 注:
*******************************************************************************/
esp_err_t bsp_Pca9557Init(void)
{
int i2c_master_port = PCA9557_I2C_MASTER_NUM;
i2c_config_t conf;
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = PCA9557_I2C_MASTER_SDA_IO;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.scl_io_num = PCA9557_I2C_MASTER_SCL_IO;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = PCA9557_I2C_MASTER_FREQ_HZ;
i2c_param_config(i2c_master_port, &conf);
return i2c_driver_install(i2c_master_port, conf.mode, PCA9557_I2C_MASTER_RX_BUF_DISABLE, PCA9557_I2C_MASTER_TX_BUF_DISABLE, 0);
}
/*******************************************************************************
* 名 称: bsp_Pca9557WriterReg
* 功 能: PCA9577 写寄存器
* 入口参数: u8I2cSlaveAddr : PCA9557的I2C地址
u8Cmd : 命令寄存器
u8Value : 写入寄存器的值
* 出口参数: esp_err_t
* 作 者: Roger-WY
* 创建日期: 2021-05-08
* 修 改:
* 修改日期:
* 备 注:
*******************************************************************************/
esp_err_t bsp_Pca9557WriterReg(uint8_t u8I2cSlaveAddr,uint8_t u8Cmd,uint8_t u8Value)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (u8I2cSlaveAddr << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
i2c_master_write_byte(cmd, u8Cmd, ACK_CHECK_EN);
i2c_master_write_byte(cmd, u8Value, ACK_CHECK_EN);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(PCA9557_I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
/*******************************************************************************
* 名 称: bsp_Pca9557ReadReg
* 功 能: PCA9577 读寄存器
* 入口参数: u8I2cSlaveAddr : PCA9557的I2C地址
u8Cmd : 命令寄存器
pBuff : 寄存器值的缓存数组指针
u8Cnt : 读取的寄存器个数
* 出口参数: esp_err_t
* 作 者: Roger-WY
* 创建日期: 2021-05-08
* 修 改:
* 修改日期:
* 备 注:
*******************************************************************************/
esp_err_t bsp_Pca9557ReadReg(uint8_t u8I2cSlaveAddr,uint8_t u8Cmd,uint8_t *pBuff,uint8_t u8Cnt)
{
esp_err_t ret ;
i2c_cmd_handle_t wr_cmd = i2c_cmd_link_create();
i2c_master_start(wr_cmd);
i2c_master_write_byte(wr_cmd, (u8I2cSlaveAddr << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
i2c_master_write_byte(wr_cmd, u8Cmd, ACK_CHECK_EN);
i2c_master_stop(wr_cmd);
ret = i2c_master_cmd_begin(PCA9557_I2C_MASTER_NUM, wr_cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(wr_cmd);
if (ret != ESP_OK) {
return ret;
}
//vTaskDelay(30 / portTICK_RATE_MS);
i2c_cmd_handle_t rd_cmd = i2c_cmd_link_create();
i2c_master_start(rd_cmd);
i2c_master_write_byte(rd_cmd, (u8I2cSlaveAddr << 1) | I2C_MASTER_READ, ACK_CHECK_EN);
i2c_master_read(rd_cmd, pBuff, u8Cnt, I2C_MASTER_LAST_NACK);
i2c_master_stop(rd_cmd);
ret = i2c_master_cmd_begin(PCA9557_I2C_MASTER_NUM, rd_cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(rd_cmd);
return ret;
}
/*******************************************************************************
* 名 称: bsp_PcaSetIoDirection
* 功 能: PCA9577 设置IO方向
* 入口参数: pinx : pin脚名称
newMode : 输入或者输出
* 出口参数: esp_err_t
* 作 者: Roger-WY
* 创建日期: 2021-05-08
* 修 改:
* 修改日期:
* 备 注:
*******************************************************************************/
esp_err_t bsp_PcaSetIoDirection(snPinName_t pinx,snPinMode_t newMode)
{
esp_err_t ret ;
uint8_t current_mode = 0;
ret = bsp_Pca9557ReadReg(PCA9557_I2C_SLAVE_ADDR, PCA9557_CONTROL_REG_3, ¤t_mode, 1);
if (ret != ESP_OK) {
return ret;
}
if(newMode == IO_OUTPUT)
{
current_mode &= ~(1 << pinx);
}
else
{
current_mode |= (1 << pinx);
}
ret = bsp_Pca9557WriterReg(PCA9557_I2C_SLAVE_ADDR, PCA9557_CONTROL_REG_3, current_mode);
return ret;
}
/*******************************************************************************
* 名 称: bsp_PcaSetIoStatus
* 功 能: PCA9577 设置IO状态
* 入口参数: pinx : pin脚名称
snPinState_t : 高电平或者低电平
* 出口参数: esp_err_t
* 作 者: Roger-WY
* 创建日期: 2021-05-08
* 修 改:
* 修改日期:
* 备 注:
*******************************************************************************/
esp_err_t bsp_PcaSetIoStatus(snPinName_t pinx,snPinState_t newState)
{
esp_err_t ret ;
uint8_t current_state = 0;
ret = bsp_Pca9557ReadReg(PCA9557_I2C_SLAVE_ADDR, PCA9557_CONTROL_REG_1, ¤t_state, 1);
if (ret != ESP_OK) {
return ret;
}
if(newState == IO_LOW)
{
current_state &= ~(1 << pinx);
}
else
{
current_state |= (1 << pinx);
}
ret = bsp_Pca9557WriterReg(PCA9557_I2C_SLAVE_ADDR, PCA9557_CONTROL_REG_1, current_state);
return ret;
}
/*******************************************************************************
* 名 称: bsp_PcaSetInputPolarity
* 功 能: PCA9577 设置IO输入的极性是否翻转
* 入口参数: pinx : pin脚名称
newPolarity : 极性是否翻转
* 出口参数: esp_err_t
* 作 者: Roger-WY
* 创建日期: 2021-05-08
* 修 改:
* 修改日期:
* 备 注:默认bit0-bit3极性不翻转 bit4-bit7极性翻转
*******************************************************************************/
esp_err_t bsp_PcaSetInputPolarity(snPinName_t pinx,snPinPolarity_t newPolarity)
{
esp_err_t ret ;
uint8_t current_state = 0;
ret = bsp_Pca9557ReadReg(PCA9557_I2C_SLAVE_ADDR, PCA9557_CONTROL_REG_2, ¤t_state, 1);
if (ret != ESP_OK) {
return ret;
}
if(newPolarity == IO_NON_INVERTED)
{
current_state &= ~(1 << pinx);
}
else
{
current_state |= (1 << pinx);
}
ret = bsp_Pca9557WriterReg(PCA9557_I2C_SLAVE_ADDR, PCA9557_CONTROL_REG_2, current_state);
return ret;
}
/*******************************************************************************
* 名 称: bsp_PcaGetIoStatus
* 功 能: PCA9577 设置IO输入的状态
* 入口参数: pinx : pin脚名称
* 出口参数: snPinState_t 引脚的电平(0/1)
* 作 者: Roger-WY
* 创建日期: 2021-05-08
* 修 改:
* 修改日期:
* 备 注:注意是否在极性翻转寄存器中设置了翻转极性!!!
*******************************************************************************/
snPinState_t bsp_PcaGetIoStatus(snPinName_t pinx)
{
esp_err_t ret ;
uint8_t current_state = 0;
ret = bsp_Pca9557ReadReg(PCA9557_I2C_SLAVE_ADDR, PCA9557_CONTROL_REG_0, ¤t_state, 1);
if(ret == ESP_OK)
{
if(current_state & (1 << pinx))
{
return IO_HIGH;
}
else
{
return IO_LOW;
}
}
else
{
return IO_UNKNOW; //返回未知状态
}
}
- bsp_pca9557.h
#ifndef __BSP_PCA9557_H__
#define __BSP_PCA9557_H__
#include "esp_err.h"
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0 /*!< I2C ack value */
#define NACK_VAL 0x1 /*!< I2C nack value */
/*
0011 A2 A1 A0 R/W
0011 0 0 0 X // w: 0x30 r:0x31 (0x18 << 1)
真实使用i2c地址 ((PCA9557_I2C_SLAVE_ADDR << 1) | R/W)
*/
#define PCA9557_I2C_SLAVE_ADDR 0x18 //(0001 1 A2 A1 A0)
/* 控制寄存器 (CMD)*/
#define PCA9557_CONTROL_REG_0 0x00 // Input Port Register (R) BXXXXXXXX (Default)
#define PCA9557_CONTROL_REG_1 0x01 // Output Port Register (R/W) B00000000
#define PCA9557_CONTROL_REG_2 0x02 // Polarity Inversion Register (R/W) B11110000
#define PCA9557_CONTROL_REG_3 0x03 // Configuration Register (R/W) B11111111
typedef enum __pinname
{
PIN_IO0 = 0, //BIT 0
PIN_IO1, //BIT 1
PIN_IO2, //BIT 2
PIN_IO3, //BIT 3
PIN_IO4, //BIT 4
PIN_IO5, //BIT 5
PIN_IO6, //BIT 6
PIN_IO7, //BIT 7
} snPinName_t;
typedef enum __pinstate {
IO_LOW = 0,
IO_HIGH = 1,
IO_UNKNOW,
} snPinState_t;
typedef enum __pinmode {
IO_OUTPUT = 0,
IO_INPUT = 1
} snPinMode_t;
typedef enum __pinpolarity{
IO_NON_INVERTED = 0,
IO_INVERTED = 1
} snPinPolarity_t;
/* 外部函数引用 */
esp_err_t bsp_Pca9557Init(void);
esp_err_t bsp_PcaSetIoDirection(snPinName_t pinx, snPinMode_t newMode);
esp_err_t bsp_PcaSetIoStatus(snPinName_t pinx, snPinState_t newState);
esp_err_t bsp_PcaSetInputPolarity(snPinName_t pinx, snPinPolarity_t newPolarity);
snPinState_t bsp_PcaGetIoStatus(snPinName_t pinx);
#endif
- 屏蔽例程自带的i2c自身读取的例程,编写测试pca9557的函数
/* 每隔 10s 翻转一次 IO0-IO6 的状态,并读取一次 IO7 的输入电平*/
void pca9557_test_task(void *arg)
{
esp_err_t ret ;
snPinState_t state = IO_LOW;
snPinState_t pin7state = IO_LOW;
while (1) {
printf("PCA9557 IO0-IO6 SET %d!\n",state);
for (uint8_t i = 0; i <= 6; i++)
{
ret = bsp_PcaSetIoStatus(i, state);
printf("set io state ret:%d\n",ret);
}
if(state == IO_LOW)
{
state = IO_HIGH;
}
else
{
state = IO_LOW;
}
pin7state = bsp_PcaGetIoStatus(PIN_IO7);
printf("pin7state:%d\n",pin7state);
vTaskDelay((10000) / portTICK_RATE_MS);
}
}
void app_main(void)
{
esp_err_t ret ;
//print_mux = xSemaphoreCreateMutex();
//ESP_ERROR_CHECK(i2c_slave_init());
//ESP_ERROR_CHECK(i2c_master_init());
//xTaskCreate(i2c_test_task, "i2c_test_task_0", 1024 * 2, (void *)0, 10, NULL);
//xTaskCreate(i2c_test_task, "i2c_test_task_1", 1024 * 2, (void *)1, 10, NULL);
printf("PCA9557 IC Test!\n");
bsp_Pca9557Init();
printf("PCA9557 SET IO DIRECTION IO0-IO6 IS OUTPUT/IO7 IS INPUT!\n");
ret=bsp_PcaSetIoDirection(PIN_IO0,IO_OUTPUT);
printf("IO0 set dir is %d\n",ret);
ret=bsp_PcaSetIoDirection(PIN_IO1, IO_OUTPUT);
printf("IO1 set dir is %d\n",ret);
ret=bsp_PcaSetIoDirection(PIN_IO2,IO_OUTPUT);
printf("IO2 set dir is %d\n",ret);
ret=bsp_PcaSetIoDirection(PIN_IO3,IO_OUTPUT);
printf("IO3 set is %d\n",ret);
ret=bsp_PcaSetIoDirection(PIN_IO4,IO_OUTPUT);
printf("IO4 set dir is %d\n",ret);
ret=bsp_PcaSetIoDirection(PIN_IO5,IO_OUTPUT);
printf("IO5 set dir is %d\n",ret);
ret=bsp_PcaSetIoDirection(PIN_IO6,IO_OUTPUT);
printf("IO6 set dir is %d\n",ret);
ret=bsp_PcaSetIoDirection(PIN_IO7,IO_INPUT);
printf("IO7 set dir is %d\n",ret);
/* 设置IO7的输入电平极性翻转 */
bsp_PcaSetInputPolarity(PIN_IO7, IO_NON_INVERTED);
xTaskCreate(pca9557_test_task, "pca9557_test_task", 1024 * 2, (void *)1, 10, NULL);
}
实验结果
编译下载程序后,可通过 Monitor 查看打印信息:
纯手写文章,转载请注明出处,谢谢!
如有任何错误,欢迎留言指正!