【Linux驱动】十二、I2C编程
【Linux驱动】十二、I2C编程
本文最后更新于320 天前,其中的信息可能已经过时,如有错误请发送邮件到273925452@qq.com

I2C(Inter-Integrated Circuit BUS)是 I2C BUS 简称,中文为集成电路总线,是目前应用最广泛的总线之一。和 IMX6ULL 有些相关的是,刚好该总线是 NXP 前身的 PHILIPS 设计。


I2C 协议

概述:

    I2C 是一种串行通信总线,使用多主从架构,最初设计目的为了让主板、嵌入式系统或手机用来连接低速周边设备。多用于小数据量的场合,有传输距离短,任意时刻只能有一个主机等特性。严格意义上讲,I2C 应该是软硬件结合体,所以我们将分物理层和协议层来介绍该总线。
I2C 总线结构如下图:
      传输数据时,我们需要发数据,从主设备发送到从设备上去;也需要把数据从从设备传送到主设备上去,数据涉及到双向传输。
      对于 I2C 通信的过程,下面使用一个形象的生活例子进行类比。
 
 

体育老师:可以把球发给学生,也可以把球从学生中接过来。

① 发球:

a) 老师说:注意了(start);

b) 老师对 A 学生说,我要球发给你(A 就是地址);

c) 老师就把球发出去了(传输);

d) A 收到球之后,应该告诉老师一声(回应);

e) 老师说下课(停止)。

② 接球:

a) 老师说注意了(start);

b) 老师说:B 把球发给我(B 是地址);

c) B 就把球发给老师(传输);

d) 老师收到球之后,给 B 说一声,表示收到球了(回应);

e) 老师说下课(停止)。

我们就使用这个简单的例子,来解释一下 IIC 的传输协议

① 老师说注意了,表示开始信号(start)

② 老师告诉某个学生,表示发送地址(address)

③ 老师发球/接球,表示数据的传输

④ 老师/学生收到球,回应表示:回应信号(ACK)

⑤ 老师说下课,表示 IIC 传输接受(P)


物理层:

➢ 特性 1:半双工(非全双工)

I2C 总线中只使用两条线路:SDA、SCL。

① SDA(串行数据线):

主芯片通过一根 SDA 线既可以把数据发给从设备,也可以从 SDA 上读取数据。在 I2C 设备内部有两个引脚(发送引脚/接受引脚),它们都连接到外部的SDA 线上。

② SCL(串行时钟线):

I2C 主设备发出时钟,从设备接收时钟。

SDA 和 SCL 引脚的内部电路结构一致,引脚的输出驱动与输入缓冲连在一起。其中输出为漏极开路的场效应管、输入缓冲为一只高输入阻抗的同相器。这

样结构有如下特性:

⚫ 由于 SDA、SCL 为漏极开路结构,借助于外部的上拉电阻实现了信号的“线与”逻辑;

⚫ 引脚在输出信号的同时还作用输入信号供内部进行检测,当输出与输入不一致时,就表示有问题发生了。这为 “时钟同步”和“总线仲裁”提供硬件基础。SDA 和 CLK 连接线上连有两个上拉电阻,当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低。

物理层连接如下图所示:

➢ 特性 2:地址和角色可配置

每个连接到总线的器件都可以通过唯一的地址和其它器件通信,主机/从机角色和地址可配置,主机可以作为主机发送器和主机接收器。

➢ 特性 3:多主机

IIC 是真正的多主机总线,I2C 设备可以在通讯过程转变成主机。如果两个或更多的主机同时请求总线,可以通过冲突检测和仲裁防止总线数据被破坏。

➢ 特性 4:传输速率

传输速率在标准模式下可以达到 100kb/s,快速模式下可以达到 400kb/s。

➢ 特性 5:负载和距离

节点的最大数量受限于地址空间以及总线电容决定,另外总电容也限制了实际通信距离只有几米。


协议层:

➢ 数据有效性

I2C 协议的数据有效性是靠时钟来保证的,在时钟的高电平周期内,SDA 线上的数据必须保持稳定。数据线仅可以在时钟 SCL 为低电平时改变。

➢ 起始和结束条件

⚫ 起始条件:当 SCL 为高电平的时候,SDA 线上由高到低的跳变被定义为起始条件。

⚫ 结束条件:当 SCL 为高电平的时候,SDA 线上由低到高的跳变被定义为停止条件。

要注意起始和终止信号都是由主机发出的,连接到 I2C 总线上的器件,若具有 I2C 总线的硬件接口,则很容易检测到起始和终止信号。

总线在起始条件之后,视为忙状态,在停止条件之后被视为空闲状态。

➢ 应答

每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据,从机应答主机所需要的时钟仍是主机提供的,应答出现在每一次主机完成 8 个数据位传输后紧跟着的时钟周期,低电平 0 表示应答,1 表示非应答。

➢ 数据帧格式

SDA 线上每个字节必须是 8 位长,在每个传输(transfer)中所传输字节数没有限制,每个字节后面必须跟一个 ACK。8 位数据中,先传输最高有效位(MSB)传输。


IMX6ULL的I2C控制器操作与寄存器介绍

本部分在IMX6ULL裸机开发手册里面有详细介绍


I2C 读写标准流程

➢ 写寄存器的标准流程如下图:

a) Master 发起 START

b) Master 发送 I2C addr(7bit)和 w 操作 0(1bit),等待 ACK

c) Slave 发送 ACK

d) Master 发送 reg addr(8bit),等待 ACK

e) Slave 发送 ACK

f) Master 发送 data(8bit),即要写入寄存器中的数据,等待 ACK

g) Slave 发送 ACK

h) 第 6 步和第 7 步可以重复多次,即顺序写多个寄存器

i) Master 发起 STOP

➢ 读寄存器的标准流程如下图:

a) Master 发送 I2C addr(7bit)和 w 操作 1(1bit),等待 ACK

b) Slave 发送 ACK

c) Master 发送 reg addr(8bit),等待 ACK

d) Slave 发送 ACK

e) Master 发起 RESTART

f) Master 发送 I2C addr(7bit)和 r 操作 1(1bit),等待 ACK

g) Slave 发送 ACK

h) Slave 发送 data(8bit),即寄存器里的值

i) Master 发送 ACK

j) 第 8 步和第 9 步可以重复多次,即顺序读多个寄存器

k) Master 发送 NO ACK 表示读取完成,从机也不用发送 ACK

l) Master 发送 STOP


I2C总线驱动模型


I2C设备驱动程序模板

i2c_drv.c

#include "linux/i2c.h"
#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>

/* 主设备号                                                                 */
static int major = 0;
static struct class *my_i2c_class;

/* i2c设备结构体数据指针,指向I2C客户端的结构体 */
struct i2c_client *g_client;

static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);

/* i2c异步通知结构体指针  */
struct fasync_struct *i2c_fasync;


/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t i2c_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
    int err;

    /* 实例i2c_msg, 用于表示I2C通信中的一个消息,可以包含消息的地址、数据方向以及要传输的数据*/
    struct i2c_msg msgs[2];

    /* 初始化i2c_msg 
        adapter:是I2C适配器的结构体指针

    */
    err = i2c_transfer(g_client->adapter, msgs, 2);  //传输2个消息
    /* copy_to_user  */
    
    return 0;
}

static ssize_t i2c_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    int err;

    /* copy_from_user  */


    struct i2c_msg msgs[2];

    /* 初始化i2c_msg */
    err = i2c_transfer(g_client->adapter, msgs, 2);

    
    return 0;    
}


static unsigned int i2c_drv_poll(struct file *fp, poll_table * wait)
{
    //printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    poll_wait(fp, &gpio_wait, wait);
    //return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
    return 0;
}

static int i2c_drv_fasync(int fd, struct file *file, int on)
{
    if (fasync_helper(fd, file, on, &i2c_fasync) >= 0)
        return 0;
    else
        return -EIO;
}


/* 定义自己的file_operations结构体                                              */
static struct file_operations i2c_drv_fops = {
    .owner     = THIS_MODULE,
    .read    = i2c_drv_read,
    .write   = i2c_drv_write,
    .poll    = i2c_drv_poll,
    .fasync  = i2c_drv_fasync,
};


static int i2c_drv_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    // struct device_node *np = client->dev.of_node;
    // struct i2c_adapter *adapter = client->adapter;

    /* 记录client */
    g_client = client;

    /* 注册字符设备 */
    /* 注册file_operations     */
    major = register_chrdev(0, "100ask_i2c", &i2c_drv_fops);  /* /dev/gpio_desc */

    my_i2c_class = class_create(THIS_MODULE, "100ask_i2c_class");
    if (IS_ERR(my_i2c_class)) {
        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
        unregister_chrdev(major, "100ask_i2c");
        return PTR_ERR(my_i2c_class);
    }

    device_create(my_i2c_class, NULL, MKDEV(major, 0), NULL, "myi2c"); /* /dev/myi2c */
    
    return 0;
}

static int i2c_drv_remove(struct i2c_client *client)
{
    /* 反注册字符设备 */
    device_destroy(my_i2c_class, MKDEV(major, 0));
    class_destroy(my_i2c_class);
    unregister_chrdev(major, "100ask_i2c");

    return 0;
}

/* 设备树匹配表 */
static const struct of_device_id myi2c_dt_match[] = {
    { .compatible = "100ask,i2cdev" },
    {},
};

/* i2c_driver驱动结构体 */
static struct i2c_driver my_i2c_driver = {
    .driver = {
           .name = "100ask_i2c_drv",
           .owner = THIS_MODULE,
           .of_match_table = myi2c_dt_match,     //设备树匹配表
    },
    .probe = i2c_drv_probe,
    .remove = i2c_drv_remove,
};


static int __init i2c_drv_init(void)
{
    /* 注册i2c_driver */
    return i2c_add_driver(&my_i2c_driver);
}

static void __exit i2c_drv_exit(void)
{
    /* 删除i2c_driver */
    i2c_del_driver(&my_i2c_driver);
}

/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(i2c_drv_init);
module_exit(i2c_drv_exit);

MODULE_LICENSE("GPL");


💡商业转载请联系作者获得授权,非商业转载请注明出处。
协议(License):署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)。
使用这些免费资源的时候应遵守版权意识,切勿非法利用,售卖,尊重原创内容知识产权。未经允许严禁转载。

评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇