概述
CPU上的GPIO口特别有限,所以想着给现有的单板做一个I/O扩展。方案是QCA9563通过I2C,外接MCP23017,可以外出16路I/O口。配置I/O口为固定方向,8个为一组,一组输出一组输入。
实现
刚开始打算直接通过QCA9563自带的I2C口控制器进行控制MCP23017芯片,但是在开发的过程中才发现没有I2C控制器驱动代码。可能由于这款芯片太新了,开源代码还没有适配,最后想着用GPIO模拟I2C的方式进行控制的。
- 首先选中GPIO模拟I2C驱动部分
- 注册I2C设备
驱动跑起来之后,会创建一个i2c设备/dev/i2c-0.static struct ath79_i2c_platform_data ath79_i2c_pdata = { .sda_pin = 5,//选中的GPIO口,串行数据线 .scl_pin = 6,//选中的GPIO口,串行时钟线 .udelay = 5, .timeout = 100, .sda_is_open_drain = 1, .scl_is_open_drain = 1, }; static struct resource ath79_i2c_resources[] = { { .flags = IORESOURCE_MEM, .start = AR71XX_GPIO_BASE, .end = AR71XX_GPIO_BASE + AR71XX_GPIO_SIZE - 1, }, { .start = ATH79_MISC_IRQ(8), .end = ATH79_MISC_IRQ(8), .flags = IORESOURCE_IRQ, }, }; static struct platform_device ath79_i2c_device = { .name = "i2c-gpio", .id = -1, .resource = ath79_i2c_resources, .num_resources = ARRAY_SIZE(ath79_i2c_resources), .dev = { .platform_data = &ath79_i2c_pdata }, }; platform_device_register(&ath79_i2c_device);
- 应用层开发
提供读写接口:#define MCP23017_ADDRESS 0x20 /* mcp23017 address 通过查手册和硬件走线即可确定*/ //通过/dev/i2c-0设备进行读 int mcp23017_read(unsigned char regaddr, unsigned char *value) { int ret = 0; /* read data from mcp23017 */ /* we used to start singnal */ memset(buf1, 0, sizeof(buf1)); memset(buf2, 0, sizeof(buf2)); i2c_data.nmsgs = 2; i2c_data.msgs[0].len = 1; i2c_data.msgs[0].addr = MCP23017_ADDRESS; i2c_data.msgs[0].flags = 0; /* write */ i2c_data.msgs[0].buf = buf1; buf1[0] = regaddr; /* regaddr */ i2c_data.msgs[1].len = 1; /* test read twice ,2 */ i2c_data.msgs[1].addr = 0x20; i2c_data.msgs[1].flags = I2C_M_RD; i2c_data.msgs[1].buf = buf2; /* read buff */ ret = ioctl (i2c_fd, I2C_RDWR, (unsigned long)&i2c_data); if (ret < 0) { #ifdef IOEXTEND_DEBUG print_file("[%s %d]ioctl read error\n", __FILE__, __LINE__); #endif return RET_ERR; } *value = buf2[0]; #ifdef IOEXTEND_DEBUG print_file("[%s %d]%s value:0x%2x\n", __FILE__, __LINE__, register_to_string[regaddr], buf2[0]); #endif return RET_OK; }
//通过/dev/i2c-0设备进行写 int mcp23017_write(unsigned char regaddr, unsigned char value) { int ret = 0; /* write data to mcp23017 */ i2c_data.nmsgs = 1; i2c_data.msgs[0].len = 2; i2c_data.msgs[0].addr = MCP23017_ADDRESS; /* 0100 A2A1A0 R/W 7-bit i2c addresses */ i2c_data.msgs[0].flags = 0; /* write */ i2c_data.msgs[0].buf = buf1; buf1[0] = regaddr; /* regaddr*/ buf1[1] = value; /* write value */ ret = ioctl (i2c_fd, I2C_RDWR, (unsigned long)&i2c_data); if (ret < 0) { #ifdef IOEXTEND_DEBUG print_file("[%s %d]ioctl read error\n", __FILE__, __LINE__); #endif return RET_ERR; } return RET_OK; }
//初始化/dev/i2c-0 int mcp23017_init() { i2c_fd = open("/dev/i2c-0", O_RDWR); if (i2c_fd < 0) { #ifdef IOEXTEND_DEBUG print_file("[%s %d]Error on opening the /dev/i2c-0 file i2c_fd:%d\n", __FILE__, __LINE__, i2c_fd); #endif return RET_ERR; } ioctl(i2c_fd, I2C_TIMEOUT, 2); ioctl(i2c_fd, I2C_RETRIES, 1); i2c_data.nmsgs = 2; i2c_data.msgs = (struct i2c_msg *)malloc(i2c_data.nmsgs * sizeof(struct i2c_msg)); if (!i2c_data.msgs) { #ifdef IOEXTEND_DEBUG print_file("[%s %d]Memory alloc error\n", __FILE__, __LINE__); #endif close(i2c_fd); return RET_ERR; } mcp23017_write(REG_IODIRA, 0x00); /* out put module */ mcp23017_write(REG_IODIRB, 0xff); /* in put module */ mcp23017_write(REG_GPIOA, 0xff); return RET_OK; }
总结
通过read、write接口就可以控制MCP23017芯片,如果想要实时获取芯片上的状态,需要把芯片上的中断引脚解出来,根据中断来读。或者采用轮询模式进行读状态。