ESP32通过I2C驱动PCA9557IO扩展芯片

前言

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引脚可引发相同的复位/初始化而无需使部件断电。
原理图如下:
pca9557原理图

代码编写

在 i2c_self_test的例程上修改

  1. 在 i2c_self_test–main文件夹下创建 bsp_pca9557.c和bsp_pca9557.h文件
    增加pca文件

增加文件后,然后将 CMakeList.txt 文件修改为:

idf_component_register(SRCS “i2c_example_main.c”“bsp_pca9557.c”
INCLUDE_DIRS “.”)

  1. 根据 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, &current_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, &current_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, &current_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, &current_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

  1. 屏蔽例程自带的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 查看打印信息:
在这里插入图片描述


纯手写文章,转载请注明出处,谢谢!
如有任何错误,欢迎留言指正!

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

荻夜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值