rtthread-IO扩展PCA9539芯片

一、芯片介绍

        PCA9539是NXP推出用于扩展芯片引脚的芯片,当主芯片控制引脚不够时,可以通过添加PCA9539芯片来进行扩展,PCA9539 具有中断输出和复位配置的远程 16 位 I2C 和 SMBus 低功耗 I/O 扩展器 寄存器。PCA9539由两个8位配置(输入或输出选择),输入端口,输出端口和极性反转(高电平有效或低电平有效操作)寄存器组成。上电时,I /O配置为输入。系统主机可以通过写入I /O配置位将I /O用作输入或输出。每个输入或输出的数据保存在相应的输入或输出寄存器中。使用Polarity Inversion寄存器可以反转输入端口寄存器的极性。所有寄存器都可以由系统主机读取。当任何输入状态与其对应的输入端口寄存器状态不同并使用时,PCA9539漏极开路中断( INT )输出被激活向系统主机指示输入状态已更改。INT 可以连接到微控制器的中断输入。通过在该线路上发送中断信号,远程I /O可以通知微控制器其端口上是否有输入数据,而无需通过I 2 C总线进行通信。因此,PCA9539可以保持简单的从器件。

二、引脚定义

三、驱动流程

1、项目开发使用了RTT实时操作系统,因此会使用到模拟I2C驱动框架,首先注册I2C总线设备驱动。

1)使能I2C接口及定义I2C引脚号,初始化I2C引脚,注册I2C总线设备驱动
 

#define RT_USING_I2C
#define RT_USING_I2C_BITOPS
#define BSP_USING_I2C2
#define BSP_I2C2_SCL_PIN			GET_PIN(B,10)
#define BSP_I2C2_SDA_PIN			GET_PIN(B,11)
 
/* I2C initialization function */
int rt_hw_i2c_init(void)
{
    rt_size_t obj_num = sizeof(i2c_obj) / sizeof(struct stm32_i2c);
    rt_err_t result;
 
    for (int i = 0; i < obj_num; i++)
    {
        i2c_obj[i].ops = stm32_bit_ops_default;
        i2c_obj[i].ops.data = (void *)&soft_i2c_config[i];
        i2c_obj[i].i2c2_bus.priv = &i2c_obj[i].ops;
        stm32_i2c_gpio_init(&i2c_obj[i]);
        result = rt_i2c_bit_add_bus(&i2c_obj[i].i2c2_bus, soft_i2c_config[i].bus_name);
        RT_ASSERT(result == RT_EOK);
        stm32_i2c_bus_unlock(&soft_i2c_config[i]);
    }
 
    return RT_EOK;
}
INIT_BOARD_EXPORT(rt_hw_i2c_init);

2)将设备作为pin设备,注册到设备驱动框架中

int rt_hw_pca9539_init(char *name, struct rt_pca9539_config *cfg)
{
    rt_err_t ret = RT_EOK;

    if (cfg == RT_NULL)
    {
        LOG_E("%s cfg null!");
        return -RT_ERROR;
    }
    /* 申请pca io 设备空间*/
    pca9539_dev = rt_calloc(1, sizeof(struct rt_pca9539_device));
    if (pca9539_dev == RT_NULL)
    {
        LOG_E("Can't allocate memory for io '%s' ", name);
        return -RT_ERROR;
    }

    /* 找到I2C总线设备 */
    pca9539_dev->i2c_bus_dev = rt_i2c_bus_device_find(cfg->dev_name);
    if (pca9539_dev->i2c_bus_dev == RT_NULL)
    {
        LOG_E("i2c bus device %s not found!\r\n", cfg->dev_name);
        ret = -RT_ERROR;
    }
    /* 拷贝配置信息 */
    rt_memcpy(&pca9539_dev->config, cfg, sizeof(struct rt_pca9539_config));

    /* 注册pca9539 IO 驱动*/
    pca9539_dev->parent.parent.type         = RT_Device_Class_Miscellaneous;
    pca9539_dev->parent.parent.rx_indicate  = RT_NULL;
    pca9539_dev->parent.parent.tx_complete  = RT_NULL;

#ifdef RT_USING_DEVICE_OPS
    _hw_pin.parent.ops          = &pin_ops;
#else
    pca9539_dev->parent.parent.init         = RT_NULL;
    pca9539_dev->parent.parent.open         = RT_NULL;
    pca9539_dev->parent.parent.close        = RT_NULL;
    pca9539_dev->parent.parent.read         = pca9539_pin_read;
    pca9539_dev->parent.parent.write        = pca9539_pin_write;
    pca9539_dev->parent.parent.control      = pca9539_pin_control;
#endif

    pca9539_dev->parent.ops                 = &pca9539_pin_ops;
    pca9539_dev->parent.parent.user_data    = (void *)pca9539_dev;

    /* register a character device */
    ret = rt_device_register(&pca9539_dev->parent.parent, name, RT_DEVICE_FLAG_RDWR);
    if (ret != RT_EOK)
    {
        LOG_E("device register err code: %d", ret);
        if (pca9539_dev != RT_NULL)
            rt_free(pca9539_dev);
        return -RT_ERROR;
    }
    return ret;
}

int pca9539_init(void)
{
    struct rt_pca9539_config config;
    config.dev_name = "i2c2";
    config.user_data = (void *)PCA9539_DEFALUT_I2C_ADDR;
    return rt_hw_pca9539_init("ex_pin", &config);
}
INIT_DEVICE_EXPORT(pca9539_init);

3)实现pin设备读写控制操作函数

static rt_size_t pca9539_pin_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    struct rt_device_pin_status *status;
    struct rt_device_pin *pin = (struct rt_device_pin *)dev;

    /* check parameters */
    RT_ASSERT(pin != RT_NULL);

    status = (struct rt_device_pin_status *) buffer;
    if (status == RT_NULL || size != sizeof(*status))
        return 0;

    status->status = pin->ops->pin_read(dev, status->pin);
    return size;
}

static rt_size_t pca9539_pin_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    struct rt_device_pin_status *status;
    struct rt_device_pin *pin = (struct rt_device_pin *)dev;

    /* check parameters */
    RT_ASSERT(pin != RT_NULL);

    status = (struct rt_device_pin_status *) buffer;
    if (status == RT_NULL || size != sizeof(*status))
        return 0;

    pin->ops->pin_write(dev, (rt_base_t)status->pin, (rt_base_t)status->status);

    return size;
}

static rt_err_t pca9539_pin_control(rt_device_t dev, int cmd, void *args)
{
    struct rt_device_pin_mode *mode;
    struct rt_device_pin *pin = (struct rt_device_pin *)dev;

    /* check parameters */
    RT_ASSERT(pin != RT_NULL);

    mode = (struct rt_device_pin_mode *) args;
    if (mode == RT_NULL)
        return -RT_ERROR;

    pin->ops->pin_mode(dev, (rt_base_t)mode->pin, (rt_base_t)mode->mode);

    return 0;
}

4)实现PCA9539设备I2C总线读写操作函数

int pca9539_write_regs(uint8_t reg, uint8_t *data, uint16_t data_size)
{
    struct rt_i2c_msg msg[2] = {0};
    uint8_t pca9539_i2c_addr = (((uint32_t)pca9539_dev->config.user_data) & 0xff);

    if (pca9539_dev->i2c_bus_dev == RT_NULL)
        return -RT_ERROR;
    /*msg[0]*/
    if (pca9539_i2c_addr == 0)
        msg[0].addr = PCA9539_DEFALUT_I2C_ADDR;
    else
        msg[0].addr	= pca9539_i2c_addr;
    msg[0].flags	= RT_I2C_WR;
    msg[0].len   	= 1;
    msg[0].buf   	= &reg;
    /*msg[1]*/
    if (pca9539_i2c_addr == 0)
        msg[1].addr = PCA9539_DEFALUT_I2C_ADDR;
    else
        msg[1].addr	= pca9539_i2c_addr;
    msg[1].flags	= RT_I2C_WR | RT_I2C_NO_START;
    msg[1].len   	= data_size;
    msg[1].buf   	= data;
    if (rt_i2c_transfer(pca9539_dev->i2c_bus_dev, msg, ITEM_NUM(msg)) == ITEM_NUM(msg))
    {
        return RT_EOK;
    }
    else
    {
        LOG_E("i2c bus write failed!");
        return -RT_ERROR;
    }
}

int pca9539_read_regs(uint8_t reg, uint8_t *data, uint16_t data_size)
{
    struct rt_i2c_msg msg[2] = {0};
    uint8_t pca9539_i2c_addr = (((uint32_t)pca9539_dev->config.user_data) & 0xff);

    if (pca9539_dev->i2c_bus_dev == RT_NULL)
        return -RT_ERROR;

    /*msg[0]*/
    if (pca9539_i2c_addr == 0)
        msg[0].addr = PCA9539_DEFALUT_I2C_ADDR;
    else
        msg[0].addr	= pca9539_i2c_addr;
    msg[0].flags = RT_I2C_WR;
    msg[0].len   = 1;
    msg[0].buf   = &reg;
    /*msg[1]*/
    if (pca9539_i2c_addr == 0)
        msg[1].addr = PCA9539_DEFALUT_I2C_ADDR;
    else
        msg[1].addr	= pca9539_i2c_addr;
    msg[1].flags = RT_I2C_RD;
    msg[1].len   = data_size;
    msg[1].buf   = data;

    if (rt_i2c_transfer(pca9539_dev->i2c_bus_dev, msg, ITEM_NUM(msg)) == ITEM_NUM(msg))
    {
        return RT_EOK;
    }
    else
    {
        //LOG_E("i2c bus read failed!");
        return -RT_ERROR;
    }
}

5)实现PIN设备驱动模式设置,读写操作函数

void pca9539_mode(struct rt_device *device, rt_base_t pin, rt_base_t mode)
{
    uint8_t cmd = 0;
    uint8_t reg_value[2] = {0};
    if (pin > 15)
        return;
    if (mode == PIN_MODE_OUTPUT || mode == PIN_MODE_OUTPUT_OD)
    {
        cmd = PORT_0_CONFIG_CMD;
        if (pca9539_read_regs(cmd, reg_value, ITEM_NUM(reg_value)) == RT_EOK)
        {
            reg_value[pin / 8] &= ~(1 << (pin % 8));
            if (pca9539_write_regs(cmd, reg_value, ITEM_NUM(reg_value)) != RT_EOK)
            {
                LOG_E("set output failed!");
            }
        }
    }
    else if (mode == PIN_MODE_INPUT || mode == PIN_MODE_INPUT_PULLUP || mode == PIN_MODE_INPUT_PULLDOWN)
    {
        cmd = PORT_0_CONFIG_CMD;
        if (pca9539_read_regs(cmd, reg_value, ITEM_NUM(reg_value)) == RT_EOK)
        {
            reg_value[pin / 8] &= ~(1 << (pin % 8));
            reg_value[pin / 8] |= (1 << (pin % 8));
            if (pca9539_write_regs(cmd, reg_value, ITEM_NUM(reg_value)) != RT_EOK)
            {
                LOG_E("set input failed!");
            }
        }
    }
    return;
}

void pca9539_write(struct rt_device *device, rt_base_t pin, rt_base_t value)
{
    uint8_t cmd = 0;
    uint8_t reg_value[2] = {0};
    if (pin > 15)
        return;

    cmd = PORT_0_OUTPUT_CMD;
    if (pca9539_read_regs(cmd, reg_value, ITEM_NUM(reg_value)) == RT_EOK)
    {
        reg_value[pin / 8] &= ~(1 << (pin % 8));
        reg_value[pin / 8] |= (value << (pin % 8));
        if (pca9539_write_regs(cmd, reg_value, ITEM_NUM(reg_value)) != RT_EOK)
        {
            LOG_E("write failed!");
        }
    }
    return;
}

int pca9539_read(struct rt_device *device, rt_base_t pin)
{

    uint8_t cmd = 0;
    uint8_t reg_value[2] = {0};
    uint8_t value = 0;
    if (pin < 15)
    {
        cmd = PORT_0_INPUT_CMD;
        if (pca9539_read_regs(cmd, reg_value, ITEM_NUM(reg_value)) == RT_EOK)
        {
            value = (reg_value[pin / 8] >> (pin % 8)) & 0x01;
        }
    }
    return value;
}
const static struct rt_pin_ops pca9539_pin_ops =
{
    pca9539_mode,
    pca9539_write,
    pca9539_read,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
};

6)头文件定义

#define PCA9539_DEFALUT_I2C_ADDR	0x74
#define PCA9539_GET_PIN(PORTx,PIN) 	(rt_base_t)(8 * (PORTx)  + PIN)

#define PORT_0_INPUT_CMD		0x00
#define PORT_1_INPUT_CMD		0x01
#define PORT_0_OUTPUT_CMD		0x02
#define PORT_1_OUTPUT_CMD		0x03
#define PORT_0_POLARITY_CMD		0x04
#define PORT_1_POLARITY_CMD		0x05
#define PORT_0_CONFIG_CMD		0x06
#define PORT_1_CONFIG_CMD		0x07

struct rt_pca9539_config
{
    char                       	*dev_name;  	/* The name of the communication device */
    rt_uint8_t                  type;       	/* Communication interface type */
    void                       	*user_data; 	/* Private data for the sensor. ex. i2c addr,spi cs,control I/O */
};

typedef struct rt_pca9539_device
{
    struct rt_device_pin        parent;    		/* The standard device */
    struct rt_i2c_bus_device	*i2c_bus_dev;	/* i2c bus dev*/
    struct rt_pca9539_config	config;			/* pca9539 config */
    struct rt_device_pin_mode	irq_pin;   		/* Interrupt pin, The purpose of this pin is to notification read data */
} pca9539_device_t;

int rt_hw_pca9539_init(char *name, struct rt_pca9539_config *cfg);
#endif

5)对驱动继续进行封装,跟换引脚只要修改此处就行

gpio_in_obj_st gpio_in_objs[] =
{
    //6-----ON OFF
    {
        .dev_name = "ex_pin",
        .pin_index = PCA9539_GET_PIN(1, 1),
        .mode = PIN_MODE_INPUT,
        .used_flag = GPIO_IN_OBJ_FLAG_USED,
    },
    //7-----OPS MODE
    {
        .dev_name = "ex_pin",
        .pin_index = PCA9539_GET_PIN(1, 0),
        .mode = PIN_MODE_INPUT,
        .used_flag = GPIO_IN_OBJ_FLAG_USED,
    },
    //8-----WORK MODE
    {
        .dev_name = "ex_pin",
        .pin_index = PCA9539_GET_PIN(0, 7),
        .mode = PIN_MODE_INPUT,
        .used_flag = GPIO_IN_OBJ_FLAG_USED,
    },
    //9-----START SUSPEND
    {
        .dev_name = "ex_pin",
        .pin_index = PCA9539_GET_PIN(0, 6),
        .mode = PIN_MODE_INPUT,
        .used_flag = GPIO_IN_OBJ_FLAG_USED,
    }
};

int gpio_in_init(void)
{
    uint8_t i = 0;
    struct rt_device_pin_mode mode_data;
    for (; i < ITEM_NUM(gpio_in_objs); i++)
    {
        if ((gpio_in_objs[i].used_flag & GPIO_IN_OBJ_FLAG_USED) == 0)
            continue;

        gpio_in_objs[i].dev_handle = rt_device_find(gpio_in_objs[i].dev_name);
        if (gpio_in_objs[i].dev_handle == RT_NULL)
            continue;

        mode_data.pin = gpio_in_objs[i].pin_index;
        mode_data.mode = gpio_in_objs[i].mode;

        rt_device_control(gpio_in_objs[i].dev_handle, mode_data.pin, &mode_data);
        rt_device_open(gpio_in_objs[i].dev_handle, RT_DEVICE_FLAG_RDWR);
    }
    return RT_EOK;
}
INIT_ENV_EXPORT(gpio_in_init);

int32_t gpio_in_status_read(uint8_t gpio_in_id)
{
    struct rt_device_pin_status pin_status;

    //参数校验
    if (gpio_in_id >= ITEM_NUM(gpio_in_objs))
        return -RT_ERROR;
    if (gpio_in_objs[gpio_in_id].dev_handle == RT_NULL)
        return -RT_ERROR;

    //读取状态
    pin_status.pin = gpio_in_objs[gpio_in_id].pin_index;
    rt_device_read(gpio_in_objs[gpio_in_id].dev_handle, 0, &pin_status, sizeof(pin_status));
    return pin_status.status;
}

6)应用,应用层配合按键驱动框架,调用封装接口实现IO状态读取

static int ops_mode_status_read(void)
{
    return gpio_in_status_read(ID_KEY_OPS_MODE);
}

static void ops_mode_press_handler(void *param)
{
    /* 自动手动按键,按键只有在开机状态下按案件才有作用 */
    struct key_status_info key_info = {0};
    if (func_button_status[SOFT_START_BUTTON])
    {
        func_button_status[OPS_MODE_BUTTON] = !func_button_status[OPS_MODE_BUTTON];
        key_info.key_status = func_button_status[OPS_MODE_BUTTON];
        orb_publish(ORB_ID(auto_manual_key), &key_info);
    }
    LOG_D("ops mode: %s", (func_button_status[OPS_MODE_BUTTON] ? "manual" : "auto"));
}

static void ops_mode_button_init(void)
{
    button_create("ops mode", &func_button[OPS_MODE_BUTTON], ops_mode_status_read, PIN_LOW);
    button_attach(&func_button[OPS_MODE_BUTTON], BUTTON_DOWM, ops_mode_press_handler);
}

7)实验结果

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值