免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2634 | 回复: 1
打印 上一主题 下一主题

I2C总线驱动 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-12-20 09:44 |只看该作者 |倒序浏览
    转眼间,从事嵌入式系统开发已快三年了。回首三年走过的历程,发觉除了增加几行代码,没有留下什么。希望在chinaunix的博客,把"I2C总线驱动"做为第一篇博文,争取每周一篇,把之前和今后工作中的心得体会记录下来,有不当之处,还恳请指正。
 
$1 I2C总线概述
    I2C总线是一种由PHILIPS公司开发的两线式(数据线SDA和时钟线SCL)串行总线,其标准模式下传输速度可以100Kbps,快速模式下可达400Kbps.I2C总线上的每个器件都有一个唯一的地址识别,可以作为总线上的一个发送器件或接收器件。
$1.1 I2C总线的几种信号状态
    1.  空闲状态:SDA和SCL都为高电平。
    2.  开始条件(S):SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据(由主机产生)。
    3.  重复起始条件(Sr):SCL为高电平时,SDA由高电平向低电平跳变,总线处于忙状态(由主机产生)。
    4.  结束条件(P):SCL为低电平时,SDA由低电平向高电平跳变,结束传送数据(由主机产生)。
    5.  数据有效:在SCL的高电平期间, SDA保持稳定,数据有效。SDA的改变只能发生在SCL的低电平期间。
    6.  ACK信号: 数据传输的过程中,接收器件每接收一个字节数据要产生一个ACK信号,向发送器件发出特定的低电平脉冲,表示已经收到数据。
$1.2 总线仲裁
    主机只能在总线空闲的时候启动传输。当SCL线是高电平时,仲裁在SDA线发生,当有其它主机发送低电平时,发送高电平的主机将断开它的数据输出级,产生仲裁失败。
$1.3 用时钟同步机制作为握手
    在字节级的快速传输中,器件可以快速接收字节,但需要更多时间来处理和准备下一个要发送的字节。这时从机以一种握手过程,在接收和响应一个字节后使SCL线保持低电平,迫使主机进入等待状态,直到从机准备好下一个要传输的字节。
$1.4 设备地址
    设备地址分7位地址和10位地址,
10位地址的第一字节:11110xx, C语言代码:addr_1st = (0xf0 | ((addr & 0x300) >> 7)) & 0xff;
$1.5 传输时序
    标准传输时序见I2C协议手册,开发时需要注意某些从设备的芯片手册上的非标准的传输时序。
 
$2. I2C总线驱动模板
$2.1 板级注册
1. 预先注册I2C0上的I2C从设备信息
#ifdef CONFIG_I2C0_XXXX
static struct i2c_board_info __initdata board_i2c0_devices[] = {
#if defined (CONFIG_BATTERY_BQ27510)
 {
    .type      = "bq27510",
    .addr           = 0x55,
    .flags   = 0,
 },
#endif
#if defined (CONFIG_RTC_HYM8563)
 {
    .type      = "rtc_hym8563",
    .addr           = 0x51,
    .flags   = 0,
 },
#endif
};
#endif
i2c_register_board_info(0, board_i2c0_devices,中心ARRAY_SIZE(board_i2c0_devices));
$2.2 注册平台设备
static struct resource resources_i2c0[] = {
 {
    .start = IRQ_I2C0,
    .end = IRQ_I2C0,
    .flags = IORESOURCE_IRQ,
 },
 {
    .start = XXXX_I2C0_PHYS,
    .end = XXXX_I2C0_PHYS + SZ_4K - 1,
    .flags = IORESOURCE_MEM,
 },
};
struct platform_device xxxx_device_i2c0 = {
    .name = "xxxx_i2c",
    .id = 0,
    .num_resources = ARRAY_SIZE(resources_i2c0),
    .resource = resources_i2c0,
    .dev    = {
        //板级私有结构,总线驱动通过pdev->dev.platform_data获取此结构地址.
        .platform_data = xxxx_priv_data,    
    },
};
platform_device_register();
$2.3 总线驱动
//i2c_transfer函数调用master_xfer方法完成底层硬件操作
static int xxxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
    //在函数是需要特别注意对出错情况的处理(如:总线仲裁失败,传输超时,总线忙等等)!
    ....
}
//返回I2C所支持的协议
static u32 xxxx_i2c_func(struct i2c_adapter *adap)
{
     return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
}
static const struct i2c_algorithm xxxx_i2c_algorithm = {
    .master_xfer  = xxxx_i2c_xfer,
    .functionality  = xxxx_i2c_func,
};
static int xxxx_i2c_probe(struct platform_device *pdev)
{
    struct i2c_adapter adap;
    ....
    //注册适配器
    adap.owner    = THIS_MODULE;
    adap.algo     = &xxxx_i2c_algorithm;  //I2C总线通信方法
    adap.class    = I2C_CLASS_HWMON;
    ....
    i2c_add_numbered_adapter(&adap);
    ....
}
static int xxxx_i2c_remove(struct platform_device *pdev)
{
    ....
}
static struct platform_driver xxxx_i2c_driver = {
    .probe  = xxxx_i2c_probe,
    .remove  = xxxx_i2c_remove,
    .driver  = {
        .owner = THIS_MODULE,
        .name = "xxxx_i2c",
    },
};
static int __init xxxx_i2c_adap_init(void)
{
     return platform_driver_register(&xxxx_i2c_driver);
}
static void __exit xxxx_i2c_adap_exit(void)
{
     platform_driver_unregister(&xxxx_i2c_driver);
}
subsys_initcall(xxxx_i2c_adap_init);
module_exit(xxxx_i2c_adap_exit);
 
$3. I2C从设备驱动模板
static int __devinit  xxxx_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
    ....
}
static int __devexit xxxx_remove(struct i2c_client *client)
{
    ....
}
static struct i2c_driver xxxx_driver = {
 .driver  = {
    .name = "xxxxxxxx",
    .owner = THIS_MODULE,
 },
    .probe  = xxxx_probe,
    .remove  = __devexit_p(xxxx_remove),
};
static int __init xxxx_init(void)
{
    return i2c_add_driver(&xxxx_driver);
}
static void __exit xxxx_exit(void)
{
    i2c_del_driver(&xxxx_driver);
}
module_init(xxxx_init);
module_exit(xxxx_exit);
 
$4. I2C设备数据传输(8位寄存器地址读写例子)
static int xxxx_i2c_write(struct i2c_client *client,char *data,char reg,int len)
{
    struct i2c_msg msg;
    char buf[len + 1];
    buf[0] = reg;
    memcpy(buf+1, data, len);
    msg.addr = client->addr;
    msg.flag = 0;
    msg.len = len + 1;
    msg.buf = buf;
   
    return i2c_transfer(client->adapter, &msg, 1);
}
static int xxxx_i2c_read(struct i2c_client *client, char *buf, char reg, int len)
{
    struct i2c_msg msgs[2];
    //发送从设备寄存器地址(有些从设备芯片手册称之为命令号)
    msgs[0].addr = client->addr;
    msgs[0].flag = 0;
    msgs[0].len = len;
    msgs[0].buf = ®
    //读数据
    msgs[1].addr = client->addr;
    msgs[1].flag = I2C_M_RD;
    msgs[1].len = len;
    msgs[1].buf = buf;
   
    return i2c_transfer(client->adapter, msgs, 2);
}
 
$5. 调试过程中常见的出错原因
1. 总线上拉电阻不够
2. 总线长度太长
    注意:如果总线长度超过10cm,则总线线路的配线方式为:
    SDA-------------
    VDD-------------
    VSS-------------
    SCL-------------
3. 示波上显示的波形不符合从设备芯片上描述的传输时序(有些厂商家为了绕开PHILIPS的I2C协议,芯片手册上的传输时序跟标准的协议描述有点出入,需要驱动的额外支持)
4. 有些从设备在传输一个字节后会把SCL总线拉低一段时间处理数据,导致主机认为总线仲裁失败.
  

论坛徽章:
0
2 [报告]
发表于 2012-10-16 09:54 |只看该作者
你这写的东西baidu.com就能搜出来,是你的东西吗?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP