使用gpio模拟i2c读写16位位宽寄存器
芯片i2c时序如下
#include <linux/module.h> #include <linux/errno.h> #include <linux/miscdevice.h> #include <linux/fcntl.h> #include <linux/uaccess.h> #include <linux/io.h> #include <linux/gpio/consumer.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/proc_fs.h> #include <linux/workqueue.h> #define I2C_SCL 46 #define I2C_SDA 45 struct gpio_desc *i2c_scl_desc; struct gpio_desc *i2c_sda_desc; #define GPIO_I2C_READ 0x01 #define GPIO_I2C_WRITE 0x02 #define GPIO_I2C_READ16 0x03 #define GPIO_I2C_WRITE16 0x04 typedef struct _mul_i2c { int addr; int val; }multi_i2c_s; void gpio_i2c_multi_write16(unsigned char devaddress, unsigned short address, unsigned short *data, unsigned short bytes); unsigned short gpio_i2c_read16(unsigned char devaddress, unsigned short address); void gpio_i2c_write16(unsigned char devaddress, unsigned short address, unsigned short data); void gpio_i2c_multi_read16(unsigned char devaddress, unsigned short address, unsigned short *data, unsigned short bytes); void i2c_start(void) { gpiod_direction_output(i2c_scl_desc, 1); gpiod_direction_output(i2c_sda_desc, 1); udelay(2); gpiod_direction_output(i2c_sda_desc, 0); udelay(2); gpiod_direction_output(i2c_scl_desc, 0); udelay(2); } void i2c_stop(void) { gpiod_direction_output(i2c_scl_desc, 0); gpiod_direction_output(i2c_sda_desc, 0); udelay(2); gpiod_direction_output(i2c_scl_desc, 1); udelay(2); gpiod_direction_output(i2c_sda_desc, 1); udelay(2); } void i2c_send_ack(int ack) { gpiod_direction_output(i2c_sda_desc, 0); if (ack) { gpiod_direction_output(i2c_sda_desc, 0); } else { gpiod_direction_output(i2c_sda_desc, 1); } gpiod_direction_output(i2c_scl_desc, 1); udelay(2); gpiod_direction_output(i2c_scl_desc, 0); } int i2c_recv_ack(void) { int value = 0; gpiod_direction_input(i2c_sda_desc); gpiod_direction_output(i2c_scl_desc, 1); udelay(2); if (gpiod_get_value(i2c_sda_desc)) { value = 1; } else { value = 0; } gpiod_direction_output(i2c_scl_desc, 0); gpiod_direction_output(i2c_sda_desc, 1); return value; } void i2c_send_data(int data) { int i; int value; gpiod_direction_output(i2c_scl_desc, 0); for (i = 0; i < 8; i++) { value = (data << i) & 0x80; if (value) { gpiod_direction_output(i2c_sda_desc, 1); } else { gpiod_direction_output(i2c_sda_desc, 0); } gpiod_direction_output(i2c_scl_desc, 1); udelay(2); gpiod_direction_output(i2c_scl_desc, 0); udelay(2); } } int i2c_recv_data(void) { int i; int temp = 0; int data = 0; gpiod_direction_input(i2c_sda_desc); udelay(2); for (i = 0; i < 8; i++) { gpiod_direction_output(i2c_scl_desc, 0); udelay(2); gpiod_direction_output(i2c_scl_desc, 1); udelay(2); data = gpiod_get_value(i2c_sda_desc); if (data) { temp = (temp << 1) | data; } else { temp = (temp << 1) & ~data; } } gpiod_direction_output(i2c_scl_desc, 0); udelay(2); gpiod_direction_output(i2c_sda_desc, 1); return temp; } EXPORT_SYMBOL(gpio_i2c_multi_read16); void gpio_i2c_multi_read16(unsigned char devaddress, unsigned short address, unsigned short *data, unsigned short bytes) { unsigned short i; unsigned short rxdata; unsigned char high,low; if (bytes < 2) return; devaddress &= 0xFE; i2c_start(); i2c_send_data(devaddress); i2c_recv_ack(); high = (address&0xff00)>>8; low = address&0xff; i2c_send_data(high); i2c_recv_ack(); i2c_send_data(low); i2c_recv_ack(); i2c_start(); i2c_send_data(devaddress | 1); i2c_recv_ack(); for(i=0; i<(bytes-2)/2; i++) { high= i2c_recv_data(); i2c_send_ack(1); low = i2c_recv_data(); i2c_send_ack(1); data[i] = (high<<8)|low; } high= i2c_recv_data(); i2c_send_ack(1); low = i2c_recv_data(); i2c_send_ack(0); data[i] = (high<<8)|low; i2c_stop(); // rxdata=(high<<8)|low; // return rxdata; } EXPORT_SYMBOL(gpio_i2c_multi_write16); void gpio_i2c_multi_write16(unsigned char devaddress, unsigned short address, unsigned short *data, unsigned short bytes) { unsigned short i; unsigned char high, low; if (bytes < 2) return; i2c_start(); devaddress &= 0xFE; i2c_send_data(devaddress); i2c_recv_ack(); high = (address&0xff00)>>8; low = address&0xff; i2c_send_data(high); i2c_recv_ack(); i2c_send_data(low); i2c_recv_ack(); i2c_start(); i2c_send_data(devaddress); i2c_recv_ack(); for(i=0; i<bytes/2; i++) { high = (data[i]&0xff00)>>8; low = data[i]&0xff; i2c_send_data(high); i2c_recv_ack(); i2c_send_data(low); i2c_recv_ack(); } i2c_stop(); } EXPORT_SYMBOL(gpio_i2c_read16); unsigned short gpio_i2c_read16(unsigned char devaddress, unsigned short address) { unsigned short rxdata; unsigned char high,low; devaddress &= 0xFE; i2c_start(); i2c_send_data(devaddress); i2c_recv_ack(); high = (address&0xff00)>>8; low = address&0xff; i2c_send_data(high); i2c_recv_ack(); i2c_send_data(low); i2c_recv_ack(); i2c_start(); i2c_send_data(devaddress | 1); i2c_recv_ack(); high= i2c_recv_data(); i2c_send_ack(1); low = i2c_recv_data(); i2c_send_ack(0);//nack // printk("hi=0x%x,low=0x%x\n",high,low); i2c_stop(); // rxdata=0; rxdata=(high<<8)|low; return rxdata; } EXPORT_SYMBOL(gpio_i2c_write16); void gpio_i2c_write16(unsigned char devaddress, unsigned short address, unsigned short data) { unsigned char high,low; devaddress &= 0xFE; i2c_start(); i2c_send_data(devaddress); i2c_recv_ack(); high = (address&0xff00)>>8; low = address&0xff; i2c_send_data(high); i2c_recv_ack(); i2c_send_data(low); i2c_recv_ack(); i2c_start(); i2c_send_data(devaddress); i2c_recv_ack(); high = (data&0xff00)>>8; low = data&0xff; i2c_send_data(high); i2c_recv_ack(); i2c_send_data(low); i2c_recv_ack(); i2c_stop(); } int gpioi2c_open(struct inode * inode, struct file * file) { return 0; } int gpioi2c_close(struct inode * inode, struct file * file) { return 0; } long gpioi2c_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { unsigned int val; unsigned int __user *argp = (unsigned int __user *)arg; unsigned char device_addr; int reg_addr16; unsigned short reg_val; multi_i2c_s multi_i2c_arg; switch(cmd) { case GPIO_I2C_READ16: if (copy_from_user(&val, argp, sizeof(val))) { printk("GPIO_I2C_WRITE16: WRONG cpy multi_i2c_arg \n"); return -1; } //printk("ioctl read val=0x%x\n",val); device_addr = (val&0xff0000)>>16; reg_addr16 = (val&0xffff); reg_val = gpio_i2c_read16(device_addr, reg_addr16); //*(unsigned int *)arg = reg_val; val = reg_val; if (copy_to_user(argp, &val, sizeof(val))) { return -1; } break; case GPIO_I2C_WRITE16: if (copy_from_user(&multi_i2c_arg, argp, sizeof(multi_i2c_arg))) { printk("GPIO_I2C_WRITE16: WRONG cpy multi_i2c_arg \n"); return -1; } device_addr = (multi_i2c_arg.addr&0xff0000)>>16; reg_addr16 = (multi_i2c_arg.addr&0xffff); reg_val = multi_i2c_arg.val&0xffff; gpio_i2c_write16(device_addr, reg_addr16, reg_val); break; } return 0; } static struct file_operations gpioi2c_fops = { .owner = THIS_MODULE, .unlocked_ioctl = gpioi2c_ioctl, .open = gpioi2c_open, .release = gpioi2c_close }; static struct miscdevice gpioi2c_dev = { .minor = MISC_DYNAMIC_MINOR, .name = "gpioi2c", .fops = &gpioi2c_fops, }; static int gpio_i2c_init(void) { int ret; ret = misc_register(&gpioi2c_dev); if(0 != ret) return -1; i2c_scl_desc = gpio_to_desc(I2C_SCL); if (i2c_scl_desc == NULL) { printk("gpio_to_desc error for SCL pin\n"); return -1; } i2c_sda_desc = gpio_to_desc(I2C_SDA); if (i2c_sda_desc == NULL) { printk("gpio_to_desc error for SDA pin\n"); return -1; } gpiod_direction_output(i2c_scl_desc, 1); gpiod_direction_output(i2c_sda_desc, 1); //ft5x06_write_reg(0x38,0x80,0x33); //ft5x06_read_reg(0x38,0x80); return 0; } static void gpio_i2c_exit(void) { misc_deregister(&gpioi2c_dev); gpiod_put(i2c_scl_desc); gpiod_put(i2c_sda_desc); } module_init(gpio_i2c_init); module_exit(gpio_i2c_exit); MODULE_LICENSE("GPL");