Linux I2C驱动详解

发布者:数字行者最新更新时间:2021-11-22 来源: eefocus关键字:Linux  I2C  驱动 手机看文章 扫描二维码
随时随地手机看文章

本文基于mini2440开发板,Linux版本号是:linux-2.6.32.2


一.IIC总线device 硬件信息

#define S3C2410_PA_IIC    (0x54000000)

static struct resource s3c_i2c_resource[] = {

[0] = {

.start = S3C_PA_IIC,

.end   = S3C_PA_IIC + SZ_4K - 1,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = IRQ_IIC,

.end   = IRQ_IIC,

.flags = IORESOURCE_IRQ,

},

};


struct platform_device s3c_device_i2c0 = {

.name   = "s3c2410-i2c",

#ifdef CONFIG_S3C_DEV_I2C1

.id   = 0,

#else

.id   = -1,

#endif

.num_resources   = ARRAY_SIZE(s3c_i2c_resource),

.resource   = s3c_i2c_resource,

};


static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {

.flags = 0,

.slave_addr = 0x10,

.frequency = 100*1000,

.sda_delay = 100,

};


其中,IIC寄存器的基地址为0x54000000。

s3c3440芯片的寄存器的地址如下:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二.IIC总线device注册

IIC总线device包含在mini2440_devices中,如下图所示:

在这里插入图片描述

将包含usb,lcd,i2c,nand等设备的mini2440_devices数组当成platform设备注册到内核


platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices))


三.IIC总线driver的注册

IIC总线driver的注册调用的是platform_driver_register函数,IIC总线driver注册是platform总线会去自动匹配相应的device,匹配的规则是:


先根据id_table来匹配

再根据device的名称来匹配

在这里插入图片描述

这个IICdriver可以匹配两种IIC device,如下所示:

在这里插入图片描述

四. IIC总线driver和device的匹配

IIC总线的driver和device匹配上后,会执行driver的probe函数,probe函数中执行了如下动作:


获取i2c时钟,并使能时钟

i2c->clk = clk_get(&pdev->dev, "i2c");

if (IS_ERR(i2c->clk)) {

    dev_err(&pdev->dev, "cannot get clockn");

    ret = -ENOENT;

    goto err_noclk;

}

clk_enable(i2c->clk);


获取IIC寄存器的虚拟地址

i2c->regs = ioremap(res->start, resource_size(res));


获取IIC中断,并申请IIC中断

i2c->irq = ret = platform_get_irq(pdev, 0);

if (ret <= 0) {

    dev_err(&pdev->dev, "cannot find IRQn");

    goto err_iomap;

}

ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,dev_name(&pdev->dev), i2c);


IIC初始化,设置时钟,使能中断,使能ask, 设置寄存器

static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)

{

unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;

struct s3c2410_platform_i2c *pdata;

unsigned int freq;


/* get the plafrom data :获取platform 设备数据

pdata = i2c->dev->platform_data;

/* inititalise the gpio */

if (pdata->cfg_gpio)

pdata->cfg_gpio(to_platform_device(i2c->dev));


/* write slave address */

writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);


        // IIC-bus acknowledge enable

        //IC-Bus Tx/Rx interrupt enable

writel(iicon, i2c->regs + S3C2410_IICCON);


/* we need to work out the divisors for the clock... */

        //设置时钟

if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {

writel(0, i2c->regs + S3C2410_IICCON);

dev_err(i2c->dev, "cannot meet bus frequency requiredn");

return -EINVAL;

}


/* todo - check that the i2c lines aren't being dragged anywhere */

dev_info(i2c->dev, "bus frequency set to %d KHzn", freq);

dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lxn", iicon);


return 0;

}


注册 IIC adapter,在注册adapter之前要设置adap的名字,算法,父节点等信息

strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));

i2c->adap.owner   = THIS_MODULE;

i2c->adap.algo    = &s3c24xx_i2c_algorithm;

i2c->adap.retries = 2;

i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;

i2c->tx_setup     = 50;

i2c->adap.algo_data = i2c;

i2c->adap.dev.parent = &pdev->dev;

i2c->adap.nr = pdata->bus_num;

ret = i2c_add_numbered_adapter(&i2c->adap);


五.使能IIC总线应答

在这里插入图片描述

需要把 IICCON寄存器的第7位设置为1。


#define S3C2410_IICCON_ACKEN (1<<7)

static inline void s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c)

{

unsigned long tmp;

tmp = readl(i2c->regs + S3C2410_IICCON);

writel(tmp | S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON);

}


六. 使能IIC总线中断

在这里插入图片描述

需要把 IICCON寄存器的第5位设置为1。


#define S3C2410_IICCON_IRQEN (1<<5)

static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c)

{

unsigned long tmp;

tmp = readl(i2c->regs + S3C2410_IICCON);

writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);

}


七.获取IIC总线的忙状态

在这里插入图片描述

读 IICSTAT寄存器的第5位,1表示忙,0表示空闲


#define S3C2410_IICSTAT_BUSBUSY (1<<5)


iicstat = readl(i2c->regs + S3C2410_IICSTAT);

return (iicstat & S3C2410_IICSTAT_BUSBUSY);


八. IIC硬件中断

IIC中断产生的条件

①一个字节的数据发送或者接收完成产生硬件中断。

②发出从设备地址,成功匹配到从设备后产生硬件中断。

总线仲裁失败产生中断。

正常情况下IIC中断产生的时机

发送数据时,IICDS移位寄存器向SDA一位一位发出电平信号,先发高位,循环8次后,数据发送完成, 接收到ASK信号后产生中断。

读数据时,IICDS移位寄存器从SDA线一位一位读入电平信号,先读高位,循环8次后,数据接收完成,产生中断。


3.中断处理过程中又来了新的IIC数据

产生硬件中断后,会设置中断挂起flag,同时将SCL拉低,IIC传输暂停,以防在中断处理过程中又来了新的IIC信号。

等待中断函数处理完成后,清除中断挂起flag,释放SCL,IIC继续发送和接收数据。

在这里插入图片描述

清除中断挂起flag,设置 IICCON寄存器的第4位为0。


#define S3C2410_IICCON_IRQPEND (1<<4)

tmp = readl(i2c->regs + S3C2410_IICCON);

tmp &= ~S3C2410_IICCON_IRQPEND;

writel(tmp, i2c->regs + S3C2410_IICCON);


九. IIC总线启动

使能IIC应答

s3c24xx_i2c_enable_ack(i2c);


2.使能IIC数据输入输出


#define S3C2410_IICSTAT_TXRXEN (1<<4)

stat |=  S3C2410_IICSTAT_TXRXEN;


3.配置主机读写模式,若是读,设备地址最低位或上1

在这里插入图片描述

#define S3C2410_IICSTAT_MASTER_RX (2<<6)

#define S3C2410_IICSTAT_MASTER_TX (3<<6)

if (msg->flags & I2C_M_RD)

{

stat |= S3C2410_IICSTAT_MASTER_RX;

addr |= 1;

else

stat |= S3C2410_IICSTAT_MASTER_TX;


writel(stat, i2c->regs + S3C2410_IICSTAT);


4.写从设备地址到IICDS寄存器


writeb(addr, i2c->regs + S3C2410_IICDS);


5.开始IIC传输


stat |= S3C2410_IICSTAT_START;

writel(stat, i2c->regs + S3C2410_IICSTAT);

在这里插入图片描述

十. IIC总线停止传输

1.需要向IICSTART寄存器的第5位写0

在这里插入图片描述

unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT);

/* stop the transfer */

iicstat &= ~S3C2410_IICSTAT_START;

writel(iicstat, i2c->regs + S3C2410_IICSTAT);


2.禁止IIC中断


s3c24xx_i2c_disable_irq(i2c);


十一. IIC总线的algo

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {

.master_xfer = s3c24xx_i2c_xfer,

.functionality = s3c24xx_i2c_func,

};


s3c24xx_i2c_algorithm赋值给adap.algo


i2c->adap.algo    = &s3c24xx_i2c_algorithm;


该adap添加进内核,后面内核通过该adap控制这个IIC总线的数据传输。


i2c_add_numbered_adapter(&i2c->adap);


十二. IIC的数据传输函数s3c24xx_i2c_xfer

配置IIC引脚

if (pdata->cfg_gpio)

pdata->cfg_gpio(to_platform_device(i2c->dev));


2.调用s3c24xx_i2c_doxfer函数传输数据,最多重复操作2次,成功了就退出,失败了就继续操作。

adap->retries = 2。


for (retry = 0; retry < adap->retries; retry++) 

{

ret = s3c24xx_i2c_doxfer(i2c, msgs, num);

if (ret != -EAGAIN)

return ret;

udelay(100);

}


十三. IIC 总线发数据

IIC总线调用s3c24xx_i2c_xfer发送数据,s3c24xx_i2c_xfer函数又调用s3c24xx_i2c_doxfer。

1.等待IIC不忙。


ret = s3c24xx_i2c_set_master(i2c);


2.设置IIC状态标志位


i2c->state   = STATE_START;


3.使能IIC中断


s3c24xx_i2c_enable_irq(i2c);


IIC总线启动传输

s3c24xx_i2c_message_start(i2c, msgs);


5.进入睡眠,等待IIC的等待队列唤醒。


timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);


6.等待中断发生,中断函数在s3c24xx_i2c_probe里面注册。


ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c);


7.中断发生,进入中断

在这里插入图片描述

8.进入case STATE_START,判断最后接收到1bit数据是不是ASK, 没有收到ask,退出IIC传输(前提是没有设置I2C_M_IGNORE_NAK模式)


if (iicstat & S3C2410_IICSTAT_LASTBIT && !(i2c->msg->flags & I2C_M_IGNORE_NAK)) 

{

        /* ack was not received... */

        dev_dbg(i2c->dev, "ack was not receivedn");

        s3c24xx_i2c_stop(i2c, -ENXIO);

        goto out_ack;

}


9.判断是读IIC消息还是写IIC消息


if (i2c->msg->flags & I2C_M_RD)

        i2c->state = STATE_READ;

else

        i2c->state = STATE_WRITE;


在这里是写IIC,i2c->state = STATE_WRITE;


10.判断该消息是不是最后一个消息,若是,停止IIC传输,否则,进入case STATE_WRITE。这里通常是i2c匹配设备的时候使用。


if (is_lastmsg(i2c) && i2c->msg->len == 0) 

{

        s3c24xx_i2c_stop(i2c, 0);

        goto out_ack;

}


11.若这个msg的数据没有发完,继续发,每次发一个byte,就是给寄存器S3C2410_IICDS赋值。


if (!is_msgend(i2c))

{

        byte = i2c->msg->buf[i2c->msg_ptr++];

        writeb(byte, i2c->regs + S3C2410_IICDS);

        ndelay(i2c->tx_setup);

}


若这个msg发完了,看看是不是最后一个msg。如果不是,指向下一个msg。判断下一个msg是不是变成读了,就是msg改变方向了。如果是读,要设置I2C_M_NOSTART标志,表示IIC重新发起一个起始信号,否则IIC停止传输。如果还是写,继续写下一个消息。

 else if (!is_lastmsg(i2c)) 

 {

        /* we need to go to the next i2c message */

        dev_dbg(i2c->dev, "WRITE: Next Messagen");

        i2c->msg_ptr = 0;

        i2c->msg_idx++;

        i2c->msg++;

        /* check to see if we need to do another message */

        if (i2c->msg->flags & I2C_M_NOSTART)

        {

                if (i2c->msg->flags & I2C_M_RD) 

                {

                        /* cannot do this, the controller

                         * forces us to send a new START

                         * when we change direction */

                        s3c24xx_i2c_stop(i2c, -EINVAL);

                }

                goto retry_write;

        } 

        else 

        {

                /* send the new start */

                s3c24xx_i2c_message_start(i2c, i2c->msg);

                i2c->state = STATE_START;

        }


若是所有的msg都发送完了,停止IIC传输

 else 

 {

        /* send stop */

        s3c24xx_i2c_stop(i2c, 0);

}


写寄存器停止IIC传输

iicstat &= ~S3C2410_IICSTAT_START;

writel(iicstat, i2c->regs + S3C2410_IICSTAT);


IIC msg 清零

i2c->msg_ptr = 0;

i2c->msg = NULL;

i2c->msg_idx++;

i2c->msg_num = 0;


唤醒等待队列

wake_up(&i2c->wait);


17.禁止中断产生。


s3c24xx_i2c_disable_irq(i2c);

[1] [2]
关键字:Linux  I2C  驱动 引用地址:Linux I2C驱动详解

上一篇:mini2440 linuxi2c驱动
下一篇:mini2440 I2C驱动的分析与学习(二)

推荐阅读最新更新时间:2024-11-07 13:36

芯昇科技王斌:中移动首款RISC-V MCU如何驱动万物互联
2021年7月,中国移动旗下中移物联网全资子公司芯昇科技有限公司正式独立运营,彼时,中移物联网公司党委委员、副总经理刘春阳表示,芯昇科技在2020年底完成注册,希望芯昇科技未来在芯片领域可以做出新的规模、锻造新的能力、开创新的机制,并希望未来3-5年,芯昇科技领导班子要有明确的规划。 芯昇科技总经理肖青曾表示,芯昇科技以“创芯驱动万物互联,加速社会数智化转型”为使命,致力于成为“最具创新力的物联网芯片及应用领航者”。 日前,在中国RISC-V产业联盟、芯原微电子和上海集成电路产业集群发展促进机构共同主办的首届滴水湖中国RISC-V产业论坛上,芯昇科技有限公司MCU产品经理王斌结合芯昇科技首款基于RISC-V内核的MCU,详
[单片机]
芯昇科技王斌:中移动首款RISC-V MCU如何<font color='red'>驱动</font>万物互联
基于2.6.19内核的小型Linux系统制作与移植
引言 ARM9 S3C2410微处理器与Linux的结合越来越紧密,逐渐在嵌入式领域得到广范的应用。目前,在便携式消费类电子产品、无线设备、汽车、网络、存储产品等都可以看到S3C2410与Linux相结合的身影。 S3C2410微处理器是一款由Samsung公司为手持终端设计的低价格、低功耗、高性能,基于ARM920T核的微处理器。它带有内存管理单元(MMU),采用0.18mm工艺和AMBA新型总线结构,主频可达203MHz。同时,它支持Thumb 16位压缩指令集,从而能以较小的存储空间获得32位的系统性能。 在众多嵌入式操作系统中,Linux目前发展最快、应用最为广泛 。性能优良、源码开放的Linux具有体积小、内核可裁
[嵌入式]
【STM32】实战1—用STM32与ULN2003驱动步进电机28BYJ-48(一)
1 实验预期效果 完成步进电机的正转(不通过串口控制)。 2 硬件学习 2.2 28BYJ-48步进电机 2.2.1 规格书 2.2.2 原理认识 【该部分为视频第20讲-步进电机控制_哔哩哔哩_bilibili的学习记录】【强推看原视频】 电机参数: 1~4为相线,5为电源线 同一根绕线,面对面通电励磁,吸引转子转动 : 该图中,定子8个齿,平均相间45°(即360°/8);转子6个齿,平均相间60°(即360°/6)。所以相差15°。根据系列计算可知,得出的结果与规格书并不相符。 所以:货不对板!!!!!!!!!!! (即上面那张图片中的齿数与
[单片机]
【STM32】实战1—用STM32与ULN2003<font color='red'>驱动</font>步进电机28BYJ-48(一)
ARM Linux bootloader笔记
1 .text //指定了后续编译出来的内容放在代码段【可执行】 2 .global //告诉编译器后续跟的是一个全局可见的名字【可能是变量,也可以是函数名】 3 _start /*函数的其实地址,也是编译、链接够程序的起始地址。由于程序是通过加载器来加载的,           必须找到_start名字的函数,因此_start必须定义成全局的,以便存在于编译后的全局符合表中,           供其他程序【如加载器】寻找到。*/ 4 _start: 5 /*1.关看门狗*/ 6 ldr r0,=53000000 7 /*2.设置时钟*/ 8 9 /*3.初始化SDRAM*
[单片机]
通过驱动器IC及输入检测机制来降低能耗方案
空间及成本限制一直是消费性电子设计的重要因素,如此才可令进入市场的产品造型优美诱人,且提供对公众具有吸引力的价格竞争优势。除了满足此两项要求,对环境的影响也越来越受关注,这表示能耗已经成为设计工程师的重要考虑因素。美国“能源之星”(Energy Start) 指令已经作出的最新变化彰显了此项事实。 如今,大多数视频娱乐系统仍要求模拟视频信号。最常用的就是单通道组合视频消隐同步(CVBS)信号,常见于视频输出接口,用于维持标准分辨率功能作为备份输出。通常情况下,1080i高分辨率(HD)模拟视频内容需要3通道YPbPr信号。为了遵从“能源之星”指令,任何DVD/蓝光播放器或机顶盒(STB)在休眠模式下的能耗必须低于1 W,详见图
[电源管理]
通过<font color='red'>驱动</font>器IC及输入检测机制来降低能耗方案
89C51编程器端驱动程序的例子
简介:本文提供给大家一个89C51编程器端驱动程序的例子 /* 89C51系列CPU编程器接收CPU程序*/ #include reg51.h #include intrins.h #include absacc.h #define e 8 #define p 9 #define l 10 sbit led=P3^2; sbit p27=P2^7; sbit p26=P2^6; sbit p36=P3^6; sbit p37=P3^7; sbit rst=P3^3; sbit ale=P3^5; sbit vpp=P3^4; bit b_break; unsigned int adds; // // 13.8mS voi
[单片机]
基于集成芯片TLE621O和L9349的ABS驱动电路设计
  ABS作为如今汽车上必备的安全电子设备,其功能越来越受到人们的重视。ABS系统通过电磁阀和回油泵来完成对制动器中轮缸压力的精细调节,以防止过度制动使车轮抱死。由于ABS工作环境十分恶劣,为保证电磁阀和电机响应的高效性和可靠性,除了与执行机构本身的参数相关外,对驱动电路的设计也直接决定了驱动的品质。   当今汽车电子市场异常火热,竞争十分激烈。各大集成芯片公司,如ST,Freescale,Infineon均设计ABS的专用集成芯片,提出了自己的ABS解决方案。该芯片就像一个黑匣子,方便了电路的设计过程,并且由于其高度集成性,使电路更简明,可靠性更高,代表了未来电路设计的方向。 1 ABS驱动电路的集成化方案   ABS驱动电路
[汽车电子]
基于集成芯片TLE621O和L9349的ABS<font color='red'>驱动</font>电路设计
Linux异常处理体系结构
在ARM V4及V4T以后的大部分处理器中,中断向量表的位置可以有两个位置:一个是0,另一个是0xffff0000。可以通过CP15协处理器c1寄存器中V位(bit )控制。V和中断向量表的对应关系如下: 0x00000000~0x0000001C / 0xffff0000~0xffff001C 。Linux内核使用0xffff0000。 异常向量的代码很简单,只是一些跳转指令。发生异常时,cpu自动执行这些指令,跳转到更复杂得代码。 地址__vectors_start~__vectors_end间的代码就是异常向量,在arch/arm/kernel/entry-armv.S中定义,这些异常向量会被复制到0xffff00
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved