Linux 下 i2c switch pca9548驱动

Linux 下 i2c switch pca9548驱动

作者: 韩大卫 @吉林师范大学


    现有的关于 i2c switch 资料非常少。即使阅读完官方的datasheet.也不能写出完全正确的操作。

因为内核中的驱动本身不是那么完善的。还有一些资料是单片机编程的,可惜在linux上并不能成功执行。

    pca954x 系列是一种 i2c switch 芯片,比如 pca9548 可实现8个开关, 进而实现多了8条i2c通道。

这样可以在有限的i2c 资源上扩展出足够多的接口。解决了在使用 i2c总线容量的问题。


   Pca954x 内部只有一个控制寄存器。属于无子地址设备。做I/O 访问时,只需要向0x00 地址处做写操作即可,

这就可实现pca954x 下挂设备 i2c bus 的选路。


    但是在现有的pca954x 驱动函数中,没有实现自动对内部控制寄存器进行相应配置,这样的话就需要额外的写一个附加的配置函数,实现这种功能。

如果没有这种配置函数,只是使用现有的内核代码,那么会出现有一些难以发现的问题,但是还是被我遇到了。

在我看来,这种问题暂且不能算bug,但至少应该去优化,毕竟,如果每次在访问不同的i2c bus 时,

需要我们手动的去操作i2c switch 的开关,这多少会影响执行效率,代码量变大。还有一点,

我们自己编写的配置函数,是严重依赖于硬件的,即我们的开关位置打开的位置需要自己判断,

在代码上固定写出, 可移植性差。稍后可以在我的代码中看到这种缺陷。

    基于以上原因, 我认为pca954x 的驱动应该修改。有时间的话我会整理出自己的代码,融入到内核代码中去,再提供出API 供大家使用。


I2C 1 地址 0x71,0x72,0x73 上都是pca9548 , 每个pca9548上 挂了 8 个 千兆以太网光模块sfp。 这样 我们系统上就可以同时挂载 24 个 千兆以太网光模块sfp。

I2C 0 地址  0x70 也是pca9548, 挂了2个万兆以太网光模块XFP,还有3个温度传感器TMP411.

*********** ***************

下面的内容是i2c bus 选路函数。之后是从内核代码入手的分析过程,以证明我的判断,阅读起来肯定是

有些难度,因为驱动的工作本身亦如此。如果不是从事嵌入式linux驱动的,就不必深究。

阅读本文前提是在linux的用户层和内核层要有所了解,请参考其他资料。
*********** ************************
如果需要完整的代码,请联系我:[email protected]

转载请务必表明出处。
******* ****************************

// 这是需要我们自己添加的函数。使用它来控制 i2c bus 的选路。

//   0x70  在 i2c 0上 , 0x71 0x72 0x73   在 i2c 1 上。

//  如果是操作的 i2c bus 是/dev/i2c-10 ,程序根据 10 来判断i2c bus 的选路。

// /dev/i2c-2 到 /dev/i2c-9 属于 0x70 的pca9548

// /dev/i2c-10 到 /dev/i2c-17 属于 0x71 的pca9548

// /dev/i2c-18 到 /dev/i2c-25 属于0x72 的pca9548

// /dev/i2c-26 到 /dev/i2c-33 属于 0x73 的pca9548

inline int i2c_bus_chan_enable(char* argv,int flag){
                  
    int ret,tmp;        
    unsigned char val = 0;
    unsigned short addr;
    char *s = argv;     
                        
    while(*s++ != '-' && *s);
                  
    if(*s)              
        ret  = atoi(s);
                        
    if(ret < 10 && ret != 1)
        addr = 0x70;    
    else                
        addr = ret < 18 ? 0x71 : (ret > 25 ? 0x73 : 0x72);
                                    
    if(addr != 0x70){   
        tmp = ( addr == 0x71 ? 10 : (addr == 0x72 ? 18 : 25));     
        val = 1 << ((ret - tmp) % 8 ) ;
    }                   
    else{               
             
        // 给相应的 i2c  bus 置1             
        if( ret == 2 )  
            val = 1 << 1;
        else if( ret == 3 )
            val = 1 << 2;
        else if( ret == 4 )
            val = 1 << 3;
        else if( ret == 9 )
            val = 1 << 7;
        else if( ret == 8 )
            val = 1 << 6;
    }             
                        
    // 先向 pca9548 的 i2c 地址 写相应的数值,打开相应的i2c bus
    ret = i2c_write_data(addr,0x00,val);
    if(ret < 0){        
        printf("i2c switch init error!\n");
        return -1;                                                 
   }       
    return 0;   
}           
 


********* *******************

下面是在此函数的使用:

main.c{
…..
                 int ret,tmp;
                    unsigned char val   = 0;
                    char cmd_buf[1024]  = {0};
                    unsigned short addr ;
                         
                    i2c_path(argv[2],0);                                                                                             
                    i2c_bus_chan_enable(argv[2],1);
                         
                    printf("offset = 0x%x\n",offset);
                    for(addr = 0x00; addr < 0xff ;addr++){
                        ret = i2c_read_data(addr,0x00,&val);
                        if(!ret)
                            printf("addr = %x,val = %x\n",addr,val);
                    }    
                }else    
                        error_info();
        }         


…..
}


inline int
i2c_read_data(u16 addr, u8 offset, u8 *val)
{
    int ret = 0;
   
    struct i2c_rdwr_ioctl_data *data;
   
    if ((data = (struct i2c_rdwr_ioctl_data *)malloc(sizeof(struct i2c_rdwr_ioctl_data))) == NULL)
    return -1;
   
    data->nmsgs = 2;
    if ((data->msgs = (struct i2c_msg *)malloc(data->nmsgs * sizeof(struct i2c_msg))) == NULL) {
        ret = -1;
        goto errexit3;
    }
    if ((data->msgs[0].buf = (unsigned char *)malloc(sizeof(unsigned char))) == NULL) {
        ret = -1;
        goto errexit2;
    }
    if ((data->msgs[1].buf = (unsigned char *)malloc(sizeof(unsigned char))) == NULL) {
        ret = -1;
        goto errexit1;
    }
   
    data->msgs[0].addr      = addr;
    data->msgs[0].flags     = 0;
    data->msgs[0].len       = 1;
    data->msgs[0].buf[0]    = offset;
   
    data->msgs[1].addr      = addr;
    data->msgs[1].flags     = I2C_M_RD;
    data->msgs[1].len       = 1;        
    data->msgs[1].buf[0]    = 0;
   
    if ((ret = __i2c_send(fd, data)) < 0)
        goto errexit0;

    *val = data->msgs[1].buf[0];
    
errexit0:
    free(data->msgs[1].buf);
errexit1:
    free(data->msgs[0].buf);
errexit2:
    free(data->msgs);
errexit3:
    free(data);
    
    return ret;
}   
   
static int
__i2c_send(int fd, struct i2c_rdwr_ioctl_data *data)
{  
    int ret;
   
    if (fd < 0)
        return -1;
   
    if (data == NULL)
        retu
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值