最近在用米尔的Z-TURN BOARD单板做小项目。顺便也加强学习I2C驱动,记一篇做记录。 
I2C总线知识非常简单,SDA,SCL,他们的时序规则是:I2C总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,最高传送速率100kbps。各种被控制电路均并联在这条总线上,但就像电话机一样只有拨通各自的号码才能工作,所以每个电路和模块都有唯一的地址,在信息的传输过程中,I2C总线上并接的每一模块电路既是主控器(或被控器),又是发送器(或接收器),这取决于它所要完成的功能。CPU发出的控制信号分为地址码和控制量两部分,地址码用来选址,即接通需要控制的电路,确定控制的种类;控制量决定该调整的类别(如对比度、亮度等)及需要调整的量。这样,各控制电路虽然挂在同一条总线上,却彼此独立,互不相关。 
I2C总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。 
开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。 
结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。 
应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。 
在LINUX系统初始化的过程中,通过 i2c_register_board_info,将所需要的I2C从设备加入一个名为_i2c_board_list双向循环链表,系统在成功加载I2C主设备adapt后,就会对这张链表里所有I2C从设备逐一地完成 i2c_client的注册。 
也就是说,i2c_client和i2c_adapter都是由i2c_core来维护的。 
在xilinx-linux中,i2c从设备是通过dts文件传递给内核的,内核通过zynq_init_machine函数注册所有的i2c从设备,i2c_client. 
I2C的linux必须知道4个结构体:i2c_adapter,i2c_algorithm,i2c_client,i2c_driver 
struct i2c_adapter { 
struct module owner; 
unsigned int class; / classes to allow probing for / 
const struct i2c_algorithm algo; / the algorithm to access the bus / 
void *algo_data; 
/ data fields that are valid for all devices / 
struct rt_mutex bus_lock; 
int timeout; / in jIFfies / 
int retries; 
struct device dev; / the adapter device / 
int nr; 
char name[48]; 
struct completion dev_released; 
struct mutex userspace_clients_lock; 
struct list_head userspace_clients; 
struct i2c_bus_recovery_info *bus_recovery_info; 
}; 
i2c总线控制器数据依附于algo_data,比如xi2cps,s3c24xx_i2c。 
struct device dev;成员表明i2c_adapter是一个硬件,对应SoC上的I2C控制器。而i2c_algorithm则是这个I2C控制器的底层驱动程序。 
同理: 
struct i2c_client { 
unsigned short flags; / div., see below / 
unsigned short addr; / chip address - NOTE: 7bit / 
/ addresses are stored in the / 
/ LOWER 7 bits / 
char name[I2C_NAME_SIZE]; 
struct i2c_adapter adapter; / the adapter we sit on / 
struct i2c_driver driver; / and our access routines / 
struct device dev; / the device structure / 
int irq; / irq issued by device / 
struct list_head detected; 
}; 
struct i2c_client代表一个挂载到i2c总线上的i2c从设备,该设备所需要的数据结构,其中包括 
该i2c从设备所依附的i2c主设备 struct i2c_adapter adapter 
该i2c从设备的驱动程序struct i2c_driver driver 
作为i2c从设备所通用的成员变量,比如addr, name等 
该i2c从设备驱动所特有的数据,依附于dev->driver_data下,在i2c_driver中的probe函数中设置这个结构体成员。比如eeprom的eeprom_data。 
所有i2c从设备组成的双向链表:detected 
struct device dev表明struct i2c_client代表的是一个硬件,比如eeprom芯片,或则rtc芯片,通过i2c总线连接到i2c_adapter硬件上。 
而i2c_driver则是这个i2c_client芯片硬件的驱动程序。 
我们一般会对每个I2C字符设备定义一个私有信息结构体,而i2c_client一般被包含在这个私有信息结构体中。看过LDR3源代码的hacker应该比较清楚。 
i2c_client依附于i2c_adapter,也就是I2C设备和I2C总线控制器的对应关系,一个i2c_adapter可以挂接多个i2c_client,i2c_adapter的struct list_head userspace_clients;结构成员就是所有client的链表。 
linux的最新版本基本上支持目前所有的I2C适配器硬件和I2C从设备,但是对于工程师来说,可能要面临各种情况:为i2c_adapter和i2c_client编写驱动程序。 
二、I2C核心 
I2C核心是源码位于drivers/i2c/i2c-core.c,它并不依赖于硬件平台的接口函数,是I2C总线驱动和设备驱动的纽带。 
增加/删除i2c_adapter 
int i2c_add_adapter(struct i2c_adapter adapter) //调用i2c_register_adapter() 
int i2c_del_adapter(struct i2c_adapter adapter) 
增加/删除i2c_driver 
int i2c_register_driver(struct module owner, struct i2c_driver driver) 
int i2c_add_driver(struct i2c_driver driver) //调用i2c_register_driver 
void i2c_del_driver(struct i2c_driver driver) 
增加/删除i2c_client 
struct i2c_client i2c_new_device(struct i2c_adapter adap, struct i2c_board_info const info) 
void i2c_unregister_device(struct i2c_client client) 
注:在2.6.30版本之前使用的是i2c_attach_client()和i2c_detach_client()函数。之后attach被merge到了i2c_new_device中,而detach直接被unregister取代。实际上这两个函数内部都是调用了device_register()和device_unregister() 
I2C传输、发送接收 
int i2c_transfer(struct i2c_adapter adap, struct i2c_msg msgs, int num) 
int i2c_master_send(struct i2c_client client,const char buf ,int count) 
int i2c_master_recv(struct i2c_client client, char buf ,int count) 
i2c_transfer()函数用于进行I2C 适配器和I2C 设备之间的一组消息交互,i2c_master_send()函数和i2c_master_recv()函数内部会调用i2c_transfer()函数分别完成一条写消息和一条读消息。 
i2c_transfer()本身不能和硬件完成消息交互,它寻找i2c_adapter对应的i2c_algorithm,要实现数据传送就要实现i2c_algorithm的master_xfer(),这个函数与具体的硬件有关,大部分时间由厂商完成。 
i2c_transfer()通过调用__i2c_transfer()完成I2C通讯: 
int __i2c_transfer(struct i2c_adapter adap, struct i2c_msg msgs, int num) 

unsigned long orig_jiffies; 
int ret, try; 
/ 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; 

return ret; 

可见retries为重传尝试次数,timeout为超时时间。 
三、Linux I2C总线驱动 
1、I2C适配器的加载和卸除 
加载:申请硬件资源,比如IO地址,中断号,调用i2c_add_adapter加载适配器 
i2c_add_adapter中会调用i2c_register_adapter函数 
static int i2c_register_adapter(struct i2c_adapter *adap) 

device_register(&adap->dev); //完成I2C主设备adapter的注册,即注册object和发送uevent等 
i2c_scan_static_board_info(adap); //注册i2c_clienlt 

static void i2c_scan_static_board_info(struct i2c_adapter adapter) 

struct i2c_devinfo devinfo; 
down_read(&__i2c_board_lock); 
list_for_each_entry(devinfo, &i2c_board_list, list) { 
if (devinfo->busnum == adapter->nr 
&& !i2c_new_device(adapter, 
&devinfo->board_info)) 
dev_err(&adapter->dev, 
“Can’t create device at 0x%02x\n”, 
devinfo->board_info.addr); 

up_read(&i2c_board_lock); 

i2c_new_device调用device_register注册i2c从设备。 
那么,这个I2C从设备组成的双向循环链表,是什么时候通过什么方式建立起来的呢? 
以 /arch/ARM/mach-pxa/saar.c 为例 
static void __init saar_init(void) 

saar_init_i2c(); 

static void __init saar_init_i2c(void) 

pxa_set_i2c_info(NULL); 
i2c_register_board_info(0, ARRAY_AND_SIZE(saar_i2c_info)); 

static struct i2c_board_info saar_i2c_info[] = { 
[0] = { 
.type = “da9034”, 
.addr = 0x34, 
.platform_data = &saar_da9034_info, 
.irq = PXA_GPIO_TO_IRQ(mfp_to_gpio(MFP_PIN_GPIO83)), 
}, 
}; 
/ drivers/i2c/i2c-boardinfo.c / 
int __init i2c_register_board_info(int busnum, structi2c_board_info const info, unsigned len) 

struct i2c_devinfo devinfo; 
devinfo->board_info = *info; 
list_add_tail(&devinfo->list, &__i2c_board_list); //将I2C从设备加入该链表中

为满足不同企业的网络连接需求,海外专线服务提供多种带宽速率选项,通过灵活、便捷的接入方式,直连运营商骨干互联网,再透传访问全球互联网内容,助力高质量、高安全的实现全球互联互通。

Zynq linux的I2C驱动学习笔记相关推荐

  1. 【嵌入式环境下linux内核及驱动学习笔记-(16)linux总线、设备、驱动模型之input框架】

    目录 1.Linux内核输入子系统概念导入 1.1 输入设备工作机制 1.2 运行框架 1.3 分层思想 2.驱动开发步骤 2.1 在init()或probe()函数中 2.2 在exit()或rem ...

  2. 【嵌入式环境下linux内核及驱动学习笔记-(15-1)例程】

    目录 1.在APP直接调用标准文件IO操作I2C(针对学习笔记-15的15.3节) 1.1 mail.c 1.2 mpu6050.h 1.3 mpu6050.c 1.4 Makefile 2.以外称i ...

  3. 【嵌入式环境下linux内核及驱动学习笔记-(11-设备树)】

    目录 1.设备树体系 1.1 DTS /DTSI / DTC / DTB 2.基础语法 2.1 节点语法 2.1.1 通用名称建议 2.2 属性语法 2.2.1 属性值 2.3 关于label 2.4 ...

  4. zynq+linux固化程序,MiZ702学习笔记6——ZYNQ如何固化程序

    上次,我们讲到了EMIO的使用,其实那就是一个最简的"PS + PL"运用的体现.我们之前是通过JTAG先下载bit流文件,再下载elf文件,之后点击Run As来运行的程序.但是 ...

  5. Linux驱动学习笔记

    驱动学习笔记 1.字符设备驱动 Linux 驱动有两种运行方式 第一种就是将驱动编译进 Linux 内核中,这样当 Linux 内核启 动的时候就会自动运行驱动程序. 第二种就是将驱动编译成模块(Li ...

  6. Linux 驱动学习笔记 - beep(九)

    Linux 驱动学习笔记 - beep(九) 本系列均为正点原子 Linux 驱动的学习笔记, 以便加深笔者记忆.如读者想进一步学习,可以到正点原子官网中下载资料进行学习. 添加 pinctrl 节点 ...

  7. linux驱动--i2c驱动学习

    转至:http://blog.csdn.net/ghostyu/article/details/8094049 预备知识 在阅读本文最好先熟悉一种i2c设备的驱动程序,并且浏览一下i2c-core.c ...

  8. 【驱动】linux下I2C驱动架构全面分析

    I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...

  9. 九十分钟极速入门Linux——Linux Guide for Developments 学习笔记

    转载自: 九十分钟极速入门Linux--Linux Guide for Developments 学习笔记 http://mp.weixin.qq.com/s?__biz=MzAwNTMxMzg1MA ...

最新文章

  1. 大话:边缘计算、雾计算、云计算
  2. Oracle TNS 不能启动
  3. hashMap与arrayList,linkedList,hashTable的区别
  4. [SCOI2005]扫雷
  5. 数据仓库分层和元数据管理
  6. [蓝桥杯2019初赛]最大降雨量-模拟
  7. jeewx-qywx-api 1.0版发布,微信企业号Java SDK
  8. liferay 指定默认首页
  9. 读书日记 莫雨 《一个程序员的奋斗史》Java 面试 感悟 程序员
  10. 中国农业机械用橡胶履带行业市场供需与战略研究报告
  11. pyspider all 只启动了_Python 爬虫:Pyspider 安装与测试
  12. AlexNet做文档布局分析 (版面分析)数据集
  13. 在access中一列称为_ACCESS考试_笔试
  14. MOSFET 导通条件
  15. sit是什么环境_测试理论——SIT测试 和 UAT测试概念
  16. MSP430F149的看门狗定时器
  17. Android手机玩8086汇编
  18. namecheap ssr_如何将SSL证书和自定义Namecheap域添加到GitLab Pages站点
  19. vue 高德地图搜索功能_vue高德地图搜索功能
  20. 《大学之路》读后感(1)

热门文章

  1. 内容营销的21条黄金法则
  2. 用泛型实现参数化类型
  3. nagios中自己写的监控mysql主从复制的插件
  4. 磁盘阵列上的文件访问不了--原因是lvm不能activate,解决办法
  5. 欧盟正研究用三种标准技术应对DNS漏洞
  6. mysql使用数据库预处理_php中对MYSQL操作之预处理技术(2)数据库dql查询语句
  7. mysql 垂直拆分 原因_mysql的水平拆分和垂直拆分 (转)
  8. python使用requests+xpath爬取小说并下载
  9. 44 Wild card Matching
  10. 360浏览器兼容模式下IE内核版本