基于zynq的IIC驱动的内容和机制

本文主要介绍基于zynq的iic的驱动架构,通过代码编写来深入了解iic驱动的内容和机制。
1. iic驱动架构
iic驱动包含两部分:iic总线驱动和设备驱动。总线驱动是对硬件设备适配器端的实现,主要包含i2c_adapter,i2c_algorithm和控制i2c适配器产生通信信号的函数。i2c_adapter和一套i2c读写algorithm绑定,i2c_algorithm包含对i2c总线的访问方式。iic总线驱动完成i2c_driver的注册,io空间分配,iic硬件设备时钟的设定,主从模式以及收发模式的设定等。通过i2c_adapter可以控制i2c总线上开始,结束等信号的产生。
iic设备驱动提供用户空间和i2c总线驱动的交互,其定义了i2c_client数据结构以及文件操作ioctrl,open,write等。i2c_client依附于i2c_adapter,用户通过i2c_client结构来调用相应的i2c_adapter,来实现iic控制。具体层次结构如下:
user ioctrl--------->
i2c_client------------>
i2c_adapter------------>
i2c_algorithm--------------->
iic硬件
2. zynq上iic驱动试验
基于黑金的zynq7020开发板,通过iic来读写eeprom,首先在ps端选择iic端口,导出到pl端,分配引脚。
代码分析:
首先定义一个i2c_dev,用于绑定到platfrom总线上。结构体中包含i2c_adapter,以及用于收发数据的buffer(p_send_buf和p_recv_buf),completion用于等待数据发送过程。
struct eeprom_i2c_dev{
struct device *dev;
struct i2c_adapter adapter;
unsigned long size;
void __iomem *baddr;
struct i2c_msg *p_msg;
unsigned char *p_send_buf;
unsigned char *p_recv_buf;
unsigned int recv_cnt;
unsigned int send_cnt;
int hold_falg;
int irq;
struct completion msg_completion;
spinlock_t xfer_lock;
u32 i2c_clk;
u32 input_clk;
struct clk *clk;
u32 ctrl_reg;
};
i2c_driver中主要包含了驱动注册和卸载函数。
static struct platform_driver eeprom_i2c_driver = {
.driver={
.name=driver_name,
.owner=this_module,
.of_match_table=eeprom_i2c_of_match,
},
.probe=eeprom_i2c_probe,
.remove=eeprom_i2c_remove,
};
现在分析probe函数:
首先从设备树中获得内存地址以及中断号,并分配io虚拟内存。
static int eeprom_i2c_probe(struct platform_device *pdev){
……
res=platform_get_resource(pdev, ioresource_mem, 0);
……
base=ioremap(res->start, resource_size(res));
……
res=platform_get_resource(pdev, ioresource_irq, 0);
指定i2c_adapter的i2c_algorithm。
idev->adapter.owner = this_module;
idev->adapter.dev.of_node = pdev->dev.of_node;
idev->baddr=base;
idev->irq=irq;
idev->size=size;
idev->dev=&pdev->dev;
idev->adapter.timeout=msecs_to_jiffies(1000);
idev->adapter.algo=&eeprom_i2c_algo;
idev->adapter.algo_data = idev;
idev->adapter.dev.parent = &pdev->dev;
snprintf(idev->adapter.name, sizeof(idev->adapter.name), eeprom i2c at %08lx, strt_addr);
根据设备树中时钟配置iic硬件中的时钟寄存器。
idev->clk = devm_clk_get(&pdev->dev, null);
if (is_err(idev->clk)) {
dev_err(&pdev->dev, input clock not found./n);
return ptr_err(idev->clk);
} ret=clk_prepare_enable(idev->clk);
if(ret)
dev_err(&pdev->dev, unable to enable clcok/n);
idev->input_clk=clk_get_rate(idev->clk);
idev->ctrl_reg = eeprom_i2c_cr_ack_en | eeprom_i2c_cr_nea | eeprom_i2c_cr_ms;
ret=eeprom_i2c_setclk(idev->input_clk, idev);
iic接收数据要申请中断。
ret=devm_request_irq(&pdev->dev, irq, eeprom_i2c_irq, 0, driver_name, idev);
i2c_add_adapter用于将i2c_adapter加入到总线中。这个时候i2c_dev.c是可以通过通知连来检测到这个i2c_adapter的,并能为其分配设备号。然后用户就可以通过直接操作这个设备来实现iic读写了。
接下来看i2c_algorithm:
static const struct i2c_algorithm eeprom_i2c_algo = {
.master_xfer=eeprom_i2c_master_xfer,
.functionality=eeprom_i2c_func,
};
eeprom_i2c_master_xfer是通信函数,其中有:
eeprom_i2c_mrcv用于处理接收到iic设备数据,eeprom_i2c_msend用于处理发送数据,函数主要通过配置iic硬件设备中寄存器来实现。
现在来看用户如何操作iic:
i2c_rdwr_ioctl_data是i2c_dev.c中用于存储的收发数据的结构,通过它可以传递数据。在work_queue中的msgs中设定好slave_addr,len等值,然后通过ioctrl可以读写iic了。


选用视频会议设备要注意的事项有哪些?
采用N32G020核心控制板实现热敏式微型打印机的设计
国庆必看蓝牙耳机推荐:学生平价蓝牙耳机推荐
通过轻型线程提高多核设备中的Linux实时性能
西门子如何看待经济复苏和EDA未来?
基于zynq的IIC驱动的内容和机制
LED灯条调光应用的驱动电源应该如何选择
如何使用Power Design Manager(PDM)进行功耗评估?
S7-1200:DB_ANY类型
DDoS攻击风暴来袭 Memcached成攻击新宠
低耗能、低成本、高质量 灵鸽科技赋能钠电匀浆工序
TD-LTE电路域回落多模单待终端实现
全球车企10年市值变化:特斯拉取代丰田夺得第一
因为这个,国产首艘航母下水没多久居然大冒黑烟!这个解释你满意吗?
华为海思原来这么厉害,跪了
荣耀V40官方信息配置曝光
蔚来计划自主研发自动驾驶计算芯片 激进还是保守?
康普携手Resonai推进AR技术在智能楼宇领域的应用
三星C7 Pro全面体验评测:细节上做到了大而全
地平线与一汽智能网联开发院签署战略合作协议