Zynq linux的I2C驱动学习笔记
最近在用米尔的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驱动学习笔记相关推荐
- 【嵌入式环境下linux内核及驱动学习笔记-(16)linux总线、设备、驱动模型之input框架】
目录 1.Linux内核输入子系统概念导入 1.1 输入设备工作机制 1.2 运行框架 1.3 分层思想 2.驱动开发步骤 2.1 在init()或probe()函数中 2.2 在exit()或rem ...
- 【嵌入式环境下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 ...
- 【嵌入式环境下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 ...
- zynq+linux固化程序,MiZ702学习笔记6——ZYNQ如何固化程序
上次,我们讲到了EMIO的使用,其实那就是一个最简的"PS + PL"运用的体现.我们之前是通过JTAG先下载bit流文件,再下载elf文件,之后点击Run As来运行的程序.但是 ...
- Linux驱动学习笔记
驱动学习笔记 1.字符设备驱动 Linux 驱动有两种运行方式 第一种就是将驱动编译进 Linux 内核中,这样当 Linux 内核启 动的时候就会自动运行驱动程序. 第二种就是将驱动编译成模块(Li ...
- Linux 驱动学习笔记 - beep(九)
Linux 驱动学习笔记 - beep(九) 本系列均为正点原子 Linux 驱动的学习笔记, 以便加深笔者记忆.如读者想进一步学习,可以到正点原子官网中下载资料进行学习. 添加 pinctrl 节点 ...
- linux驱动--i2c驱动学习
转至:http://blog.csdn.net/ghostyu/article/details/8094049 预备知识 在阅读本文最好先熟悉一种i2c设备的驱动程序,并且浏览一下i2c-core.c ...
- 【驱动】linux下I2C驱动架构全面分析
I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...
- 九十分钟极速入门Linux——Linux Guide for Developments 学习笔记
转载自: 九十分钟极速入门Linux--Linux Guide for Developments 学习笔记 http://mp.weixin.qq.com/s?__biz=MzAwNTMxMzg1MA ...
最新文章
- 大话:边缘计算、雾计算、云计算
- Oracle TNS 不能启动
- hashMap与arrayList,linkedList,hashTable的区别
- [SCOI2005]扫雷
- 数据仓库分层和元数据管理
- [蓝桥杯2019初赛]最大降雨量-模拟
- jeewx-qywx-api 1.0版发布,微信企业号Java SDK
- liferay 指定默认首页
- 读书日记 莫雨 《一个程序员的奋斗史》Java 面试 感悟 程序员
- 中国农业机械用橡胶履带行业市场供需与战略研究报告
- pyspider all 只启动了_Python 爬虫:Pyspider 安装与测试
- AlexNet做文档布局分析 (版面分析)数据集
- 在access中一列称为_ACCESS考试_笔试
- MOSFET 导通条件
- sit是什么环境_测试理论——SIT测试 和 UAT测试概念
- MSP430F149的看门狗定时器
- Android手机玩8086汇编
- namecheap ssr_如何将SSL证书和自定义Namecheap域添加到GitLab Pages站点
- vue 高德地图搜索功能_vue高德地图搜索功能
- 《大学之路》读后感(1)