Linux的I2C子系统

    xiaoxiao2023-10-06  203

    目录

    i2c_adapter、i2c_client等结构体分析 

    结构体之间的包含关系

    i2c_adapter结构体

    i2c_client结构体

    I2C系统调用过程

    整体调用过程

    调用过程路径1

    调用过程路径2

    ​I2C底层框架

     底层调用流程例1

    ​底层调用流程例2

    底层调用流程例3

    用到mux时的流程

    驱动程序使用流程

    mux驱动流程

    ak4954操作流程

    示例源码(应用调用控制器)


    i2c_adapter、i2c_client等结构体分析 

    结构体之间的包含关系

    i2c_adapter结构体

    i2c_adapter的重要成员     struct i2c_algorithm *algo    // 主控的数据传输的算法     struct device dev                   // 主控分配的device结构体,可以通过dev获得i2c_adapter等操作。     int nr                                       // 适配器编号,即:i2c通道编号。驱动可以通过这个编号获得i2c_adapter i2c_adapter的内存分配     在主控程序的probe函数中会分配i2c_adapter的内存i2c_adapter的使用     驱动可以直接通过i2c_get_adapter函数,以adapter编号为参数获得i2c_adapter结构体,进而调用其相关函数完成数据传输。

    注意: i2c_get_adapter函数会调用idr_find(&i2c_adapter_idr, nr); i2c_add_numbered_adapter和i2c_add_adapter函数中都会调用idr_alloc向i2c_adapter添加adapter i2c_get_adapter; 调用后要使用i2c_put_adapter释放掉adapter。

    i2c_client结构体

    i2c_client的重要成员     unsigned short addr                 // 外设的地址(7bit)     struct i2c_adapter *adapter     // 本外设所关联的主控适配器     struct device dev                      // device结构体

    i2c_client的内存分配和使用 (1)i2c-dev.c     open函数中分配i2c_client结构体,设置adapter等成员,将file->private_data指向本结构体; write,read,ioctl等函数中通过file->private_data获得i2c_client结构体,进行其他操作。(2)外设驱动     分配i2c_client结构体,设置其adapter等成员,驱动中可以将i2c_client作为参数调用i2c_transfer、i2c_master_send、i2c_master_recv等函数。(3)主控、mux驱动层      如果主控设备(i2c_add_adapter)和mux驱动(i2c_add_driver)都存在,则最终会分配i2c_client结构体,并将其挂到mux驱动(i2c_driver)的clients链表上边。

    I2C系统调用过程

    整体调用过程

    调用过程路径1

    调用过程路径2

    I2C底层框架

     底层调用流程例1

    (i2c-hisilicon.c)

    底层调用流程例2

    (i2c-hisi-v110.c)

    底层调用流程例3

    用到mux时的流程

    (一般不用mux)

    驱动程序使用流程

    (ipc_stm8.c)

    注意:     i2c_put_adapter最好在初始化或者打开时 进行,不能放在module_exit修饰的退出函数中。如果放在了module_exit函数修饰的 出函 数中,会导致rmmod时失败。             原因:i2c_get_adapter会调用 try_module_get函数使i2c_adapter使用计数加一, rmmod函数会先检查模块使用计数,再调用自己 定义的函数,如果i2c_put_adapter在退出函数中, 那么在rmmod检查时使用计数还是1,会导致 rmmod失败,无法卸载。

    mux驱动流程

    (一般不用mux)

    (pca9541.c)

    ak4954操作流程

     

    示例源码(应用调用控制器)

    #include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <linux/types.h> #include <linux/i2c.h> #include <linux/i2c-dev.h> #define I2C_DEV "/dev/i2c-1" #define MEM_DEV "/dev/mem" #define PAGE_SIZE_MASK 0xfffff000 #define PAGE_SIZE 4096 static int g_fd = -1; static int g_slave_addr; struct i2c_rdwr_ioctl_data g_i2c_data; static u8 g_i2c_addr[] = { 0x12,0x13,0x14,0x15,0x16 }; static u8 g_i2c_reg_val[] = { 0x08,0x05,0x11,0x03,0x30 }; int test_i2c_write(unsigned short reg, unsigned char addr_byte_num,unsigned char val) { unsigned char buf[4]; int ret; g_i2c_data.nmsgs = 1; buf[0] = reg & 0xff; buf[1] = val; (g_i2c_data.msgs[0]).len = 2; (g_i2c_data.msgs[0]).addr = g_slave_addr; (g_i2c_data.msgs[0]).buf = buf; (g_i2c_data.msgs[0]).flags = 0; ret = ioctl(g_fd, I2C_RDWR, &g_i2c_data); if(ret < 0) { printf("i2c ioctl error(write)\n"); return -1; } return 0; } int test_i2c_read(unsigned short reg, unsigned char addr_byte_num,unsigned char *val) { int ret; unsigned char reg_buf[4] = {0}; unsigned char val_buf[4] = {0}; if(addr_byte_num == 2) { reg_buf[0] = (reg >> 8) & 0xFF; reg_buf[1] = reg & 0xFF; } else if(addr_byte_num == 1){ reg_buf[0] = reg & 0xff; } (g_i2c_data.msgs[0]).len = addr_byte_num; (g_i2c_data.msgs[0]).addr = g_slave_addr; (g_i2c_data.msgs[0]).flags = 0; (g_i2c_data.msgs[0]).buf = reg_buf; (g_i2c_data.msgs[1]).len = 1; (g_i2c_data.msgs[1]).addr = g_slave_addr; (g_i2c_data.msgs[1]).flags = I2C_M_RD; (g_i2c_data.msgs[1]).buf = val_buf; g_i2c_data.nmsgs = 2; ret = ioctl(g_fd, I2C_RDWR, &g_i2c_data); if(ret < 0) { printf("i2c ioctl error(read)\n"); return -1; } *val = val_buf[0]; return 0; } int test_i2c_init(void) { int ret = -1; g_fd = open(I2C_DEV, O_RDWR, 0); if(g_fd < 0) { printf("open %s error\n",I2C_DEV); return -1; } ret = ioctl(g_fd, I2C_TENBIT, 0); if(ret < 0) { printf("set 7 bit error\n"); goto out; } ret = ioctl(g_fd, I2C_SLAVE_FORCE, g_slave_addr); if (ret < 0) { printf("set slave address err!\n"); goto out; } g_i2c_data.msgs = (struct i2c_msg *)malloc(g_i2c_data.nmsgs * sizeof(struct i2c_msg)); if(g_i2c_data.msgs == NULL) { ret = -1; goto out; } return 0; out: if(g_fd > 0) close(g_fd); if(g_i2c_data.msgs != NULL) free(g_i2c_data.msgs); return ret; } static void *memmap(unsigned long phy_addr) { int memfd = -1; unsigned long phy_addr_in_page, page_diff; unsigned long size_in_page = PAGE_SIZE; void *addr = NULL; memfd = open (MEM_DEV, O_RDWR | O_SYNC); if (memfd < 0) { printf("memmap():open %s error!\n", MEM_DEV); return NULL; } phy_addr_in_page = phy_addr & PAGE_SIZE_MASK; page_diff = phy_addr - phy_addr_in_page; addr = mmap ((void *)0, size_in_page, PROT_READ|PROT_WRITE, MAP_SHARED, memfd, phy_addr_in_page); if (addr == MAP_FAILED) { printf("memmap():mmap @ 0x%lx error\n", phy_addr_in_page); close(memfd); return NULL; } if(memfd > 0) close(memfd); return (void *)(addr + page_diff); } int write_reg(u32 phy_addr, u32 value) { void *vir_addr = NULL; vir_addr = memmap(phy_addr); if(vir_addr == NULL) { return -1; } *(unsigned int *)vir_addr = value; return 0; } int read_reg(u32 phy_addr, u32 *value) { void *vir_addr = NULL; vir_addr = memmap(phy_addr); if(vir_addr == NULL) { return -1; } *value = *(unsigned int *)vir_addr; return 0; } void peripheral_reset(void) { unsigned int val; /* reset */ read_reg(0x10200000, &val); val &= ~(0x1 << 0); write_reg(0x10200000, val); usleep(10000); val |= (0x1 << 0); write_reg(0x10200000, val); } static void memcmp_print(const void * src, const void * dest, unsigned int cnt) { unsigned int i = 0, diff_index = 0, diff_flag = 0; const unsigned char *buf1, *buf2; if(!src || !dest){ printf("address is NULL\n"); return; } for(i = 0, buf1 = src, buf2 = dest; i < cnt; i++){ if(*(buf1 + i) != *(buf2 + i)){ diff_flag = 1; diff_index = i; break; } } if(diff_flag){ printf("Compare result : error\n" "The first difference is : \n" "src[%u] = %#x\n" "dest[%u] = %#x\n", diff_index, *(buf1 + diff_index), diff_index, *(buf2 + diff_index) ); }else{ printf("Compare result : ok\n"); } } int main(int argc, char *argv[]) { int i = 0, ret = -1; unsigned char r_buf[5] = {0}; g_slave_addr = 0x29; peripheral_reset(); test_i2c_init(); for(i = 0; i < sizeof(g_i2c_addr)/sizeof(g_i2c_addr[0]); i++){ test_i2c_write( g_i2c_addr[i], 1, g_i2c_reg_val[i]); } memset(r_buf, 0, 5); for(i = 0; i < sizeof(g_i2c_addr)/sizeof(g_i2c_addr[0]); i++){ test_i2c_read( g_i2c_addr[i], 1, &r_buf[i]); } memcmp_print(g_i2c_reg_val, r_buf, 5); close(g_fd); free(g_i2c_data.msgs); return 0; }

     


    最新回复(0)