mini2440 I2C驱动的分析与学习(二)

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

接着分析i2c的数据传输流程。


首先是打开i2c设备,比如open("/dev/i2c/0"),在内核调用下面函数


static int i2cdev_open(struct inode *inode, struct file *file)

{

unsigned int minor = iminor(inode);

struct i2c_client *client;

struct i2c_adapter *adap;

struct i2c_dev *i2c_dev;

int ret = 0;

 

lock_kernel();

i2c_dev = i2c_dev_get_by_minor(minor);

if (!i2c_dev) {

ret = -ENODEV;

goto out;

}

 

adap = i2c_get_adapter(i2c_dev->adap->nr);

if (!adap) {

ret = -ENODEV;

goto out;

}

 

/* This creates an anonymous i2c_client, which may later be

* pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.

*

* This client is ** NEVER REGISTERED ** with the driver model

* or I2C core code!!  It just holds private copies of addressing

* information and maybe a PEC flag.

*/

client = kzalloc(sizeof(*client), GFP_KERNEL);

if (!client) {

i2c_put_adapter(adap);

ret = -ENOMEM;

goto out;

}

snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);

client->driver = &i2cdev_driver;

 

client->adapter = adap;

file->private_data = client;

 

out:

unlock_kernel();

return ret;

}

这里就是根据设备的minor号,生成了i2c_client结构体。以后在操作文件的时候,就可以把对i2c_client进行操作了。在i2c_client中有device, 也有device_driver。因此,i2c_client是更高层次的抽象。


 


先看设备的写函数:


static ssize_t i2cdev_write (struct file *file, const char __user *buf, size_t count,

                             loff_t *offset)

{

int ret;

char *tmp;

struct i2c_client *client = (struct i2c_client *)file->private_data;

 

if (count > 8192)

count = 8192;

 

tmp = kmalloc(count,GFP_KERNEL);

if (tmp==NULL)

return -ENOMEM;

if (copy_from_user(tmp,buf,count)) {

kfree(tmp);

return -EFAULT;

}

 

pr_debug("i2c-dev: i2c-%d writing %zu bytes.n",

iminor(file->f_path.dentry->d_inode), count);

 

ret = i2c_master_send(client,tmp,count);

kfree(tmp);

return ret;

}


在这里调用i2c_master_send函数。


 


int i2c_master_send(struct i2c_client *client,const char *buf ,int count)

{

int ret;

struct i2c_adapter *adap=client->adapter;

struct i2c_msg msg;

 

msg.addr = client->addr;

msg.flags = client->flags & I2C_M_TEN;

msg.len = count;

msg.buf = (char *)buf;

 

ret = i2c_transfer(adap, &msg, 1);

 

/* If everything went ok (i.e. 1 msg transmitted), return #bytes

   transmitted, else error code. */

return (ret == 1) ? count : ret;

}

这样又调用了i2c_transfer函数。不论读写,最后都是通过这个函数进行操作的。


int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

{

unsigned long orig_jiffies;

int ret, try;

if (adap->algo->master_xfer) {

if (in_atomic() || irqs_disabled()) {

ret = mutex_trylock(&adap->bus_lock);

if (!ret)

/* I2C activity is ongoing. */

return -EAGAIN;

} else {

mutex_lock_nested(&adap->bus_lock, adap->level);

}

 

/* Retry automatically on arbitration loss */

orig_jiffies = jiffies;

for (ret = 0, try = 0; try <= adap->retries; try++) {

ret = adap->algo->master_xfer(adap, msgs, num);

if (ret != -EAGAIN)

break;

if (time_after(jiffies, orig_jiffies + adap->timeout))

break;

}

mutex_unlock(&adap->bus_lock);

 

return ret;

} else {

dev_dbg(&adap->dev, "I2C level transfers not supportedn");

return -EOPNOTSUPP;

}

}

利用adap->algo->master_xfer进行传输。这个函数是在我们自己的文件中实现的。


最终调用了s3c24xx_i2c_doxfer函数。在这个函数中,只是传输i2c协议中的第一个byte。每传输完一个byte之后,会产生一个中断,其余的数据都是在中断函数中进行传输的。具体的关于i2c的内容,可以在arm的芯片手册和eeprom的芯片手册中看到。


static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)

{

unsigned long tmp;

unsigned char byte;

int ret = 0;

 

switch (i2c->state) {

 

case STATE_IDLE:

dev_err(i2c->dev, "%s: called in STATE_IDLEn", __func__);

goto out;

break;

 

case STATE_STOP:

dev_err(i2c->dev, "%s: called in STATE_STOPn", __func__);

s3c24xx_i2c_disable_irq(i2c);

goto out_ack;

 

case STATE_START:

/* last thing we did was send a start condition on the

* bus, or started a new i2c message

*/

 

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;

}

 

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

i2c->state = STATE_READ;

else

i2c->state = STATE_WRITE;

 

/* terminate the transfer if there is nothing to do

* as this is used by the i2c probe to find devices. */

 

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

s3c24xx_i2c_stop(i2c, 0);

goto out_ack;

}

 

if (i2c->state == STATE_READ)

goto prepare_read;

 

/* fall through to the write state, as we will need to

* send a byte as well */

 

case STATE_WRITE:

/* we are writing data to the device... check for the

* end of the message, and if so, work out what to do

*/

 

if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {

if (iicstat & S3C2410_IICSTAT_LASTBIT) {

dev_dbg(i2c->dev, "WRITE: No Ackn");

 

s3c24xx_i2c_stop(i2c, -ECONNREFUSED);

goto out_ack;

}

}

 

 retry_write:

 

if (!is_msgend(i2c)) {

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

printk( "%02x ", byte);

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

 

/* delay after writing the byte to allow the

* data setup time on the bus, as writing the

* data to the register causes the first bit

* to appear on SDA, and SCL will change as

* soon as the interrupt is acknowledged */

 

ndelay(i2c->tx_setup);

 

} 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;

}

 

} else {

/* send stop */

 

s3c24xx_i2c_stop(i2c, 0);

}

break;

 

case STATE_READ:

/* we have a byte of data in the data register, do

* something with it, and then work out wether we are

* going to do any more read/write

*/

 

byte = readb(i2c->regs + S3C2410_IICDS);

printk( "%02x ", byte);

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

 

 prepare_read:

if (is_msglast(i2c)) {

/* last byte of buffer */

 

if (is_lastmsg(i2c))

s3c24xx_i2c_disable_ack(i2c);   

/* NO ACK  means the ernd of read. Is it possible here wusq */

 

} else if (is_msgend(i2c)) {

/* ok, we've read the entire buffer, see if there

* is anything else we need to do */

 

if (is_lastmsg(i2c)) {

/* last message, send stop and complete */

dev_dbg(i2c->dev, "READ: Send Stopn");

 

s3c24xx_i2c_stop(i2c, 0);

} else {

/* go to the next transfer */

dev_dbg(i2c->dev, "READ: Next Transfern");

 

i2c->msg_ptr = 0;

i2c->msg_idx++;

i2c->msg++;

}

}

 

break;

}

 

/* acknowlegde the IRQ and get back on with the work */

 

 out_ack:

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

tmp &= ~S3C2410_IICCON_IRQPEND;

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

 out:

return ret;

}

 

/* s3c24xx_i2c_irq

 *

 * top level IRQ servicing routine

*/

 

static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)

{

struct s3c24xx_i2c *i2c = dev_id;

unsigned long status;

unsigned long tmp;

 

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

 

if (status & S3C2410_IICSTAT_ARBITR) {

/* deal with arbitration loss */

dev_err(i2c->dev, "deal with arbitration lossn");

}

 

if (i2c->state == STATE_IDLE) {

dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLEn");

 

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

tmp &= ~S3C2410_IICCON_IRQPEND;

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

goto out;

}

 

/* pretty much this leaves us with the fact that we've

* transmitted or received whatever byte we last sent */

 

i2s_s3c_irq_nextbyte(i2c, status);

 

 out:

return IRQ_HANDLED;

}

关键字:mini2440  I2C驱动  数据传输 引用地址:mini2440 I2C驱动的分析与学习(二)

上一篇:Linux I2C驱动详解
下一篇:MINI2440i2c驱动学习一

推荐阅读最新更新时间:2024-11-11 16:49

基于CY7C68013与GPIF模式的USB2.0数据传输系统的设计
1 引 言 USB支持主计算机与许多可同时访问的外设之间进行数据交换,使外设的连接具有单一化、即插即用、热插拔等特点,已成为个人笔记本电脑和台式机的标准配置接口。Cypress公司的EZ-USB FX2是一款集成 USB2.0的微处理器,它集成了USB2.0收发器、SIE(串行接口引擎)、增强的8051微控 制器和可编程的外围接口。每条指令占四个时钟周期,在48M晶振下工作时,单指令周 期为83.3ns,执行速度远快于标准的8051单片机。本文的数据传输模块采用CY7C68013 高速芯片设计的USB接口可以实现外部的存储测试电路数据的快速下载,并且上传至计 算机保存,显示,处理。这里介绍CY7C68013的GPIF接口功能及其在
[单片机]
基于CY7C68013与GPIF模式的USB2.0<font color='red'>数据传输</font>系统的设计
Linux I2C驱动完全分析(一)
其实老早就想写这个I2C的了,期间有各种各样的事情给耽误了。借着五一放假的时间把这个写出来,供同志们参考。以后会花一些时间深入研究下内核,虽然以前对内核也有所了解,但是还不系统。I2C的硬件结构并不复杂,一个适配器加几个设备而已。Linux下驱动的体系结构看着挺复杂,实际也是比较简单的。在本文中我还是使用实际的例子,结合硬件和软件两个方面来介绍。希望能给初学的同志们一些帮助,另外抛砖引玉,希望高手能给一些指点。话不多说,开整!~ 本文用到的一些资源: 1. Source Insight软件 2. mini2440原理图。 下载地址http://wenku.baidu.com/view/0521ab8da0116c1
[单片机]
linux-2.6.32在mini2440开发板上移植 移植UDA1341 音频驱动
1 在初始化文件中加入UDA1341 设备结构 Linux-2.6.32.2 已经完美的支持UDA1341 音频芯片的驱动, 我们只要在arch/arm/mach-s3c2440/mach-mini2440.c 文件中注册UDA1341 平台设备的控制端口就可以了,打开mach-mini2440.c,添加如下内容: ;在文件首部添加头文件 #include sound/s3c24xx_uda134x.h ;在LCD 平台设备后面添加UDA1341 设备结构 static struct s3c24xx_uda134x_platform_data s3c24xx_uda134x_data = { .l3_clk =
[单片机]
STM32F030 硬件I2C驱动 AT24C16
I2C 的配置 static void InitI2C() { I2C_InitTypeDef I2C_InitStructure; GPIO_InitTypeDef GPIO_InitA; RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//使能I2C1,I2C2的时钟 RCC_I2CCLKConfig(RCC_I2C1CLK_SYSCLK);//时钟源设定 GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_1); //配置PB8 成第二功能引脚 I2C1_SCL GPIO_PinAFConfig(GPIOB,
[单片机]
STM32F030 硬件<font color='red'>I2C</font><font color='red'>驱动</font> AT24C16
mini2440系统移植篇之kernel启动流程
1.1. 第1阶段 arch/arm/kernel/vmlinux.lds --------------------Makefile 2.1 arch/arm/boot/compressed/start.S 解压代码 2.2 arch/arm/kernel/head.S 2.2.1 __lookup_machine_type 机器ID àMACH_START àmachine_desc //放在__arch_info_begin…__arch_info_end 2.2.2 __create_page_tables 创建页表 2.2.3 使能MMU 2.2.4 __switch_data àb start_kerne
[单片机]
<font color='red'>mini2440</font>系统移植篇之kernel启动流程
mini2440硬件篇之RTC
硬件原理 实时时钟(RTC)的主要功能是在系统掉电的情况下,利用后备电源使时钟继续运行,从而不会丢失时间信息。 1.1. 时间的设置和获取 s3c2440内部集成了RTC模块,而且用起来也十分简单。其内部的寄存器BCDSEC,BCDMIN,BCDHOUR,BCDDAY,BCDDATE,BCDMON和BCDYEAR分别存储了当前的秒,分,小时,星期,日,月和年,表示时间的数值都是BCD码。这些寄存器的内容可读可写,并且只有在寄存器RTCCON的第0位为1时才能进行读写操作。为了防止误操作,当不进行读写时,要把该位清零。当读取这些寄存器时,能够获知当前的时间;当写入这些寄存器时,能够改变当前的时间。另外需要注意的是,因为有
[单片机]
Molex zCD互连产品满足下一代数据传输需求
(新加坡 – 2014年2月11日) 全球领先的电子元器件企业Molex公司开发紧凑型zCD™ 互连系统以支持电信、联网和企业计算环境中的下一代应用。在2014年1月29至30日在美国加利福尼亚州圣克拉拉举办的DesignCon 2014展会上,Molex将在117号展台上展示zCD互连系统。Molex zCD互连系统将传输400 Gbps数据速率 (在16个通道上达到25 Gbps),具有出色的信号完整性、电磁干扰(EMI)防护和散热冷却特性。 Molex全球新产品开发经理Joe Dambach表示:“随着网络带宽继续增加,满足高速应用的需求已经成为业界共同的优先考虑事项。zCD互连产品具有优胜的外形尺寸,有助于实现400 Gbp
[模拟电子]
Molex zCD互连产品满足下一代<font color='red'>数据传输</font>需求
Mini2440裸机程序之ADC
源代码及注释 #define GLOBAL_CLK 1 #include stdlib.h #include string.h #include def.h #include option.h #include 2440addr.h #include 2440lib.h #include 2440slib.h #include mmu.h #include memtest.h #include Mylib.h #define LED1 (1 (5 * 2)) #define LED2 (1 (6 * 2)) #define LED3 (1 (7 * 2)) #define LED4 (1 (8 * 2)) #defi
[单片机]
<font color='red'>Mini2440</font>裸机程序之ADC
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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