一、芯片介绍
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 ini tialization 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 .ops = stm32_bit_ops_default;
i2c_obj.ops.data = (void *)&soft_i2c_config;
i2c_obj.i2c2_bus.priv = &i2c_obj.ops;
stm32_i2c_gpio_init(&i2c_obj);
result = rt_i2c_bit_add_bus(&i2c_obj.i2c2_bus, soft_i2c_config.bus_name);
RT_ASSERT(result == RT_EOK);
stm32_i2c_bus_unlock(&soft_i2c_config);
}
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!rn", 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 = ®
/*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 = ®
/*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.used_flag & GPIO_IN_OBJ_FLAG_USED) == 0)
continue;
gpio_in_objs.dev_handle = rt_device_find(gpio_in_objs.dev_name);
if (gpio_in_objs.dev_handle == RT_NULL)
continue;
mode_data.pin = gpio_in_objs.pin_index;
mode_data.mode = gpio_in_objs.mode;
rt_device_control(gpio_in_objs.dev_handle, mode_data.pin, &mode_data);
rt_device_open(gpio_in_objs.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)实验结果
|