0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

I2C驱动学习看这一篇就够了

嵌入式悦翔园 来源:嵌入式悦翔园 2023-06-16 11:46 次阅读

一、前言

I2C协议是在开发中使用非常频繁的一种协议,相信大家在学习单片机的时候经常会用到支持I2C协议的模块,I2C 总线仅仅使用 SCL、SDA 这两根信号线就实现了设备之间的数据交互,极大地简化了对硬件资源和 PCB 板布线空间的占用。因此,I2C 总线被非常广泛地应用在 EEPROM、实时钟、小型 LCD 等设备与 CPU接口中。

但是与裸机开发不同的是在 Linux 系统中,I2C 驱动由 3 部分组成,即 I2C 核心、I2C 总线驱动和 I2C 设备驱动。今天就从这三个部分来给大家讲解一下Linux中的I2C驱动,以及我们应该如何为我们的开发板添加一个I2C设备。

二、Linux 的 I2C 体系结构

由上面分析可知,Linux驱动分为三部分:I2C 核心、I2C 总线驱动和 I2C 设备驱动

26b6b8b2-0bf8-11ee-962d-dac502259ad0.png

2.1 Linux I2C 核心

I2C 核心提供了 I2C 总线驱动和设备驱动的注册、注销方法,这部分主要是一些与硬件无关的的接口函数,这部分的代码一般不用我们普通开发者进行开发和修改,但是理解这部分的代码逻辑和接口还是非常必要的。

I2C 核心中的主要函数如下:

注册/注销适配器(adapter)
inti2c_add_adapter(structi2c_adapter*adap);
inti2c_del_adapter(structi2c_adapter*adap);

注册/注销I2C设备驱动程序
inti2c_register_driver(structmodule*owner,structi2c_driver*driver);
inti2c_del_driver(structi2c_driver*driver);
inlineinti2c_add_driver(structi2c_driver*driver);

创建并注册一个新的I2C设备
structi2c_client*i2c_new_device(structi2c_adapter*adap,structi2c_board_infoconst*info);

I2C传输、发送和接收
inti2c_transfer(structi2c_adapter*adap,structi2c_msg*msgs,intnum);
inti2c_master_send(structi2c_client*client,constchar*buf,intcount);
inti2c_master_recv(structi2c_client*client,char*buf,intcount);

上边三个函数用于实现与I2C设备之间的数据交换。i2c_transfer函数可以进行复杂的多消息传输,而i2c_master_send和i2c_master_recv函数用于单个数据消息的发送和接收。

这些函数提供了对于I2C总线读写操作的基本支持,简化了I2C设备驱动的开发,有了这些接口我们就不用关注I2C协议方面的代码了,只需要调用该接口即可完成数据的传输。

注意: i2c_transfer函数本身不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到 i2c_adapter 对应的 i2c_algorithm,并使用 i2c_algorithm 的 master_xfer函数真正驱动硬件流程。

2.2 Linux I2C 适配器驱动

通过上面的介绍我们知道了I2C驱动主要分为三个部分,上面我们已经介绍了I2C核心这一部分,现在我们来介绍一下I2C 适配器驱动,我们知道I2C驱动和其他的那些字符设备驱动有所不同,I2C驱动中维持着一套自己的总线。

I2C 适配器驱动是Linux内核中的一个核心模块,总线层负责管理所有注册到系统的I2C总线适配器和设备,并提供与设备通信的API函数。它提供了一些基本的操作函数,如启动总线、停止总线、发送起始信号、发送停止信号等。但是这部分是由Linux内核完成的,并不需要我们开发者进行修改或添加,所以了解即可。

下面我们用一张图来看一下上面描述的这个过程:

26f4966e-0bf8-11ee-962d-dac502259ad0.png

2.3 Linux I2C 设备驱动

I2C 设备驱动要使用 i2c_driver 和 i2c_client 数据结构并填充其中的成员函数。 i2c_client 一般被包含在设备的私有信息结构体 yyy_data 中,而 i2c_driver 则适合被定义为全局变量并初始化。

看到I2C设备驱动的这两个结构体大家是不是很熟悉了,I2C设备驱动是针对特定类型的I2C设备编写的驱动程序。它包含了对具体设备的操作和控制逻辑,通过调用I2C总线核心驱动提供的API函数与设备进行通信。设备驱动的主要任务包括初始化设备、读写数据、配置设备参数等。

因为这部分是针对特定类型的I2C设备编写的驱动程序,所以这部分才是要我们开发人员来完成编写的,我们如果需要在自己的开发板上添加一个新的I2C模块,我们就要首先编写I2C设备驱动这部分,这部分的编写需要调用上面我们介绍的I2C核心和I2C总线中接口函数来完成模块的初始化。

关于I2C设备驱动我们这里先做一个了解即可,后面会详细介绍这部分的内容,也是我们学习I2C驱动的重点内容。

2.4 Linux I2C驱动总结

I2C总线核心驱动(I2C Core Driver):【系统厂编写】I2C总线核心驱动是Linux内核中的一个核心模块,负责管理所有注册到系统的I2C总线适配器和设备,并提供与设备通信的API函数。它提供了一些基本的操作函数,如启动总线、停止总线、发送起始信号、发送停止信号等。

I2C适配器驱动(I2C Adapter Driver):【芯片厂提供】I2C适配器驱动负责与硬件的I2C控制器进行交互,完成硬件层面的初始化、配置和操作。它将底层硬件的特定接口与I2C总线核心驱动进行连接,使得核心驱动能够通过适配器驱动来访问硬件。

I2C设备驱动(I2C Device Driver):【开发者编写】I2C设备驱动是针对特定类型的I2C设备编写的驱动程序。它包含了对具体设备的操作和控制逻辑,通过调用I2C总线核心驱动提供的API函数与设备进行通信。设备驱动的主要任务包括初始化设备、读写数据、配置设备参数等。

三部分之间的关系如下:

I2C核心层驱动作为顶层驱动,管理整个I2C子系统,并提供了基本的I2C操作接口。

I2C适配器驱动负责与底层硬件的I2C控制器进行交互,通过适配器驱动,I2C总线核心驱动能够与硬件进行通信。

I2C设备驱动则针对具体的I2C设备编写,实现了对设备的初始化、读写数据等操作。

27044c62-0bf8-11ee-962d-dac502259ad0.png

三、具体设备驱动分析

由于作为开发者我们需要关注并且需要我们亲自编写的部分就只有设备驱动了,所以我们今天就详细介绍一下设备驱动这部分。

当我们需要编写具体的I2C设备驱动程序时,我们需要编写以下内容:**probe函数、remove函数、操作函数以及数据传输与处理**,下面将对每部分进行详细介绍。

3.1 Probe函数

具体设备中的probe函数是I2C设备驱动中最重要的函数之一,用于在I2C设备与驱动匹配成功后进行初始化和注册设备。在probe函数中,可以执行以下任务:

进行设备的特定初始化操作,例如配置设备寄存器、申请内存资源等。

注册字符设备、输入设备或其他设备类别,使系统能够识别和使用该设备。

存储设备私有数据,通常使用i2c_set_clientdata函数将私有数据与i2c_client相关联,方便后续的操作函数访问。

我们在学习其他设备驱动的时候就知道了probe函数是设备与驱动匹配成功后被调用执行的。它的原型通常如下所示:

staticinti2c_device_probe(structi2c_client*client,conststructi2c_device_id*id);

下面我们就找一个设备驱动来分析一下我们应该如何编写:

这里以rk3x_i2c_probe为例给大家进行分析:
staticintrk3x_i2c_probe(structplatform_device*pdev)
{
structdevice_node*np=pdev->dev.of_node;
conststructof_device_id*match;
structrk3x_i2c*i2c;
structresource*mem;
intret=0;
intbus_nr;
u32value;
intirq;
unsignedlongclk_rate;

i2c=devm_kzalloc(&pdev->dev,sizeof(structrk3x_i2c),GFP_KERNEL);
if(!i2c)
return-ENOMEM;

match=of_match_node(rk3x_i2c_match,np);
i2c->soc_data=(structrk3x_i2c_soc_data*)match->data;

/*usecommoninterfacetogetI2Ctimingproperties*/
i2c_parse_fw_timings(&pdev->dev,&i2c->t,true);

strlcpy(i2c->adap.name,"rk3x-i2c",sizeof(i2c->adap.name));
i2c->adap.owner=THIS_MODULE;
i2c->adap.algo=&rk3x_i2c_algorithm;
i2c->adap.retries=3;
i2c->adap.dev.of_node=np;
i2c->adap.algo_data=i2c;
i2c->adap.dev.parent=&pdev->dev;

i2c->dev=&pdev->dev;

spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait);

i2c->i2c_restart_nb.notifier_call=rk3x_i2c_restart_notify;
i2c->i2c_restart_nb.priority=128;
ret=register_i2c_restart_handler(&i2c->i2c_restart_nb);
if(ret){
dev_err(&pdev->dev,"failedtosetupi2crestarthandler.
");
returnret;
}

mem=platform_get_resource(pdev,IORESOURCE_MEM,0);
i2c->regs=devm_ioremap_resource(&pdev->dev,mem);
if(IS_ERR(i2c->regs))
returnPTR_ERR(i2c->regs);

/*TrytosettheI2Cadapternumberfromdt*/
bus_nr=of_alias_get_id(np,"i2c");

/*
*SwitchtonewinterfaceiftheSoCalsoofferstheoldone.
*ThecontrolbitislocatedintheGRFregisterspace.
*/
if(i2c->soc_data->grf_offset>=0){
structregmap*grf;

grf=syscon_regmap_lookup_by_phandle(np,"rockchip,grf");
if(IS_ERR(grf)){
dev_err(&pdev->dev,
"rk3x-i2cneeds'rockchip,grf'property
");
returnPTR_ERR(grf);
}

if(bus_nr< 0) {
   dev_err(&pdev->dev,"rk3x-i2cneedsi2cXalias");
return-EINVAL;
}

/*27+i:writemask,11+i:value*/
value=BIT(27+bus_nr)|BIT(11+bus_nr);

ret=regmap_write(grf,i2c->soc_data->grf_offset,value);
if(ret!=0){
dev_err(i2c->dev,"CouldnotwritetoGRF:%d
",ret);
returnret;
}
}

/*IRQsetup*/
irq=platform_get_irq(pdev,0);
if(irq< 0) {
  dev_err(&pdev->dev,"cannotfindrk3xIRQ
");
returnirq;
}

ret=devm_request_irq(&pdev->dev,irq,rk3x_i2c_irq,
0,dev_name(&pdev->dev),i2c);
if(ret< 0) {
  dev_err(&pdev->dev,"cannotrequestIRQ
");
returnret;
}

platform_set_drvdata(pdev,i2c);

if(i2c->soc_data->calc_timings==rk3x_i2c_v0_calc_timings){
/*Onlyoneclocktouseforbusclockandperipheralclock*/
i2c->clk=devm_clk_get(&pdev->dev,NULL);
i2c->pclk=i2c->clk;
}else{
i2c->clk=devm_clk_get(&pdev->dev,"i2c");
i2c->pclk=devm_clk_get(&pdev->dev,"pclk");
}

if(IS_ERR(i2c->clk)){
ret=PTR_ERR(i2c->clk);
if(ret!=-EPROBE_DEFER)
dev_err(&pdev->dev,"Can'tgetbusclk:%d
",ret);
returnret;
}
if(IS_ERR(i2c->pclk)){
ret=PTR_ERR(i2c->pclk);
if(ret!=-EPROBE_DEFER)
dev_err(&pdev->dev,"Can'tgetperiphclk:%d
",ret);
returnret;
}

ret=clk_prepare(i2c->clk);
if(ret< 0) {
  dev_err(&pdev->dev,"Can'tpreparebusclk:%d
",ret);
returnret;
}
ret=clk_prepare(i2c->pclk);
if(ret< 0) {
  dev_err(&pdev->dev,"Can'tprepareperiphclock:%d
",ret);
gotoerr_clk;
}

i2c->clk_rate_nb.notifier_call=rk3x_i2c_clk_notifier_cb;
ret=clk_notifier_register(i2c->clk,&i2c->clk_rate_nb);
if(ret!=0){
dev_err(&pdev->dev,"Unabletoregisterclocknotifier
");
gotoerr_pclk;
}

clk_rate=clk_get_rate(i2c->clk);
rk3x_i2c_adapt_div(i2c,clk_rate);

ret=i2c_add_adapter(&i2c->adap);
if(ret< 0) {
  dev_err(&pdev->dev,"Couldnotregisteradapter
");
gotoerr_clk_notifier;
}

dev_info(&pdev->dev,"InitializedRK3xxxI2Cbusat%p
",i2c->regs);

return0;

err_clk_notifier:
clk_notifier_unregister(i2c->clk,&i2c->clk_rate_nb);
err_pclk:
clk_unprepare(i2c->pclk);
err_clk:
clk_unprepare(i2c->clk);
returnret;
}

从上面的代码我们可以发现rk3x_i2c_probe主要做了以下几件事情:

1、通过devm_kzalloc函数为rk3x_i2c结构体分配内存空间;
2、从设备树中获取I2C设备信息并填充rk3x_i2c结构体;
3、使用devm_platform_ioremap_resource函数来映射设备的寄存器资源到内存中;
4、获取并配置中断;
5、使用i2c_add_adapter注册设备

基本上这个驱动就是一个比较完整的I2C设备初始化流程了,我们如果想要编写其他设备的驱动可以参考该驱动初始化来进行编写。

3.2 读写函数

由于rk3x_i2c中的读写函数和该设备关联性较大,不具备通用性,这里以sx1_i2c_write_byte和sx1_i2c_read_byte来给大家进行分析,该函数更具有通用性。

/*WritetoI2Cdevice*/
intsx1_i2c_write_byte(u8devaddr,u8regoffset,u8value)
{
structi2c_adapter*adap;
interr;
structi2c_msgmsg[1];
unsignedchardata[2];

adap=i2c_get_adapter(0);
if(!adap)
return-ENODEV;
msg->addr=devaddr;/*I2Caddressofchip*/
msg->flags=0;
msg->len=2;
msg->buf=data;
data[0]=regoffset;/*registernum*/
data[1]=value;/*registerdata*/
err=i2c_transfer(adap,msg,1);
i2c_put_adapter(adap);
if(err>=0)
return0;
returnerr;
}

/*ReadfromI2Cdevice*/
intsx1_i2c_read_byte(u8devaddr,u8regoffset,u8*value)
{
structi2c_adapter*adap;
interr;
structi2c_msgmsg[1];
unsignedchardata[2];

adap=i2c_get_adapter(0);
if(!adap)
return-ENODEV;

msg->addr=devaddr;/*I2Caddressofchip*/
msg->flags=0;
msg->len=1;
msg->buf=data;
data[0]=regoffset;/*registernum*/
err=i2c_transfer(adap,msg,1);

msg->addr=devaddr;/*I2Caddress*/
msg->flags=I2C_M_RD;
msg->len=1;
msg->buf=data;
err=i2c_transfer(adap,msg,1);
*value=data[0];
i2c_put_adapter(adap);

if(err>=0)
return0;
returnerr;
}

从上面的代码可以看出,sx1_i2c_write_byte主要完成了以下功能:

1、通过调用i2c_get_adapter(0)函数获取指定索引的I2C适配器对象并赋值给adap变量。
2、初始化一个structi2c_msg类型的数组msg,该数组包含一个元素用于I2C消息的传输。
3、设置msg结构体中的字段:
addr:设备的I2C地址。
flags:传输标志位,此处为0表示写操作。
len:要传输的字节数,此处设置为2,即寄存器地址和寄存器数据两个字节。
buf:数据缓冲区的指针,用于存储要发送的数据。
4、将要写入的设备寄存器地址和数据分别存储在data数组的第一个和第二个元素中,即data[0]=regoffset;和data[1]=value;。
5、调用i2c_transfer()函数进行I2C消息传输,将数据写入设备寄存器。
6、使用i2c_put_adapter()函数释放先前获取的I2C适配器对象。

sx1_i2c_read_byte主要完成了以下功能:

1、通过调用i2c_get_adapter(0)函数获取指定索引的I2C适配器对象并赋值给adap变量。
2、初始化一个structi2c_msg类型的数组msg,该数组包含一个元素用于I2C消息的传输。
3、设置msg结构体中的字段:
addr:设备的I2C地址。
flags:传输标志位,此处为0表示写操作。
len:要传输或接收的字节数。
buf:数据缓冲区的指针,用于存储要发送或接收的数据。
4、将要读取的设备寄存器地址存储在data数组的第一个元素中,即data[0]=regoffset;。
5、调用i2c_transfer()函数进行I2C消息传输,将数据写入设备寄存器。
6、更改flags字段为I2C_M_RD,表示接收模式(读操作)。
7、再次调用i2c_transfer()函数进行I2C消息传输,从设备中读取数据。
8、将读取到的数据存储在data数组的第一个元素中,即*value=data[0];。
9、使用i2c_put_adapter()函数释放先前获取的I2C适配器对象。

对比I2C读和写的过程大家可能会发现I2C读的过程为什么调用了两次i2c_transfer函数呢?多调用了一次i2c_transfer函数是因为我们在调用i2c_transfer读取数据时,需要先发送要读取的寄存器地址给设备,然后再从设备读取实际的数据。所以第一次使用i2c_transfer发送的信息为需要读取的地址信息,第二次将标志位改为读,然后使用i2c_transfer将从设备返回的信息存储到i2c_adapter中。

四、I2C驱动中几个重要的结构体

在I2C驱动中,有三个比较重要的结构体用于描述和管理I2C设备和传输操作。下面就这三个结构体的成员以及作用来给大家讲解一下:

4.1 i2c_adapter 结构体

定义位置:i2c.h结构体原型:

structi2c_adapter{
structmodule*owner;
unsignedintclass;/*classestoallowprobingfor*/
conststructi2c_algorithm*algo;/*thealgorithmtoaccessthebus*/
void*algo_data;

/*datafieldsthatarevalidforalldevices*/
conststructi2c_lock_operations*lock_ops;
structrt_mutexbus_lock;
structrt_mutexmux_lock;

inttimeout;/*injiffies*/
intretries;
structdevicedev;/*theadapterdevice*/
unsignedlonglocked_flags;/*ownedbytheI2Ccore*/
#defineI2C_ALF_IS_SUSPENDED0
#defineI2C_ALF_SUSPEND_REPORTED1

intnr;
charname[48];
structcompletiondev_released;

structmutexuserspace_clients_lock;
structlist_headuserspace_clients;

structi2c_bus_recovery_info*bus_recovery_info;
conststructi2c_adapter_quirks*quirks;

structirq_domain*host_notify_domain;
structregulator*bus_regulator;
};

几个重要的成员:

name:适配器的名称。
nr:适配器的编号。
bus_lock和bus_unlock:用于保护对适配器的并发访问的锁机制。
algo:指向I2C算法结构体的指针,包含了适配器的通信算法,如标准模式、快速模式、高速模式等。

4.2 i2c_client 结构体

定义位置:i2c.h结构体原型:

structi2c_client{
unsignedshortflags;/*div.,seebelow*/
#defineI2C_CLIENT_PEC0x04/*UsePacketErrorChecking*/
#defineI2C_CLIENT_TEN0x10/*wehaveatenbitchipaddress*/
/*MustequalI2C_M_TENbelow*/
#defineI2C_CLIENT_SLAVE0x20/*wearetheslave*/
#defineI2C_CLIENT_HOST_NOTIFY0x40/*WewanttouseI2Chostnotify*/
#defineI2C_CLIENT_WAKE0x80/*forboard_info;trueiffcanwake*/
#defineI2C_CLIENT_SCCB0x9000/*UseOmnivisionSCCBprotocol*/
/*MustmatchI2C_M_STOP|IGNORE_NAK*/

unsignedshortaddr;/*chipaddress-NOTE:7bit*/
/*addressesarestoredinthe*/
/*_LOWER_7bits*/
charname[I2C_NAME_SIZE];
structi2c_adapter*adapter;/*theadapterwesiton*/
structdevicedev;/*thedevicestructure*/
intinit_irq;/*irqsetatinitialization*/
intirq;/*irqissuedbydevice*/
structlist_headdetected;
#ifIS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_tslave_cb;/*callbackforslavemode*/
#endif
void*devres_group_id;/*IDofprobedevresgroup*/
};

几个重要的成员:

flags:标志位,用于指定设备的特性和行为。
addr:设备的I2C地址。
adapter:指向i2c_adapter的指针,表示所属的I2C适配器。
driver:指向设备驱动程序的指针,表示设备所使用的驱动。

4.3 i2c_driver 结构体

定义位置:i2c.h结构体原型:

structi2c_driver{
unsignedintclass;

union{
/*Standarddrivermodelinterfaces*/
int(*probe)(structi2c_client*client);
/*
*Legacycallbackthatwaspartofaconversionof.probe().
*Todayithasthesamesemanticas.probe().Don'tusefornew
*code.
*/
int(*probe_new)(structi2c_client*client);
};
void(*remove)(structi2c_client*client);


/*drivermodelinterfacesthatdon'trelatetoenumeration*/
void(*shutdown)(structi2c_client*client);

/*Alertcallback,forexamplefortheSMBusalertprotocol.
*Theformatandmeaningofthedatavaluedependsontheprotocol.
*FortheSMBusalertprotocol,thereisasinglebitofdatapassed
*asthealertresponse'slowbit("eventflag").
*FortheSMBusHostNotifyprotocol,thedatacorrespondstothe
*16-bitpayloaddatareportedbytheslavedeviceactingasmaster.
*/
void(*alert)(structi2c_client*client,enumi2c_alert_protocolprotocol,
unsignedintdata);

/*aioctllikecommandthatcanbeusedtoperformspecificfunctions
*withthedevice.
*/
int(*command)(structi2c_client*client,unsignedintcmd,void*arg);

structdevice_driverdriver;
conststructi2c_device_id*id_table;

/*Devicedetectioncallbackforautomaticdevicecreation*/
int(*detect)(structi2c_client*client,structi2c_board_info*info);
constunsignedshort*address_list;
structlist_headclients;

u32flags;
};

几个重要的成员:

driver:是一个structdevice_driver结构体,用于向Linux设备模型注册驱动程序。
probe和remove:指向探测和移除设备的函数指针,通过这两个函数,驱动程序可以在发现匹配的设备时执行初始化操作,并在设备被移除时执行清理操作。
id_table:用于指定驱动程序支持的I2C设备ID列表,以便匹配对应的设备。

这些结构体共同构成了Linux内核中的I2C驱动框架,提供了对I2C总线、适配器和设备的抽象和管理功能。开发者可以基于这些结构体来编写自己的I2C驱动程序,并实现与I2C设备的通信和控制。所以我们的工作就是填充这些结构体然后调用对应的接口把我们填充好的结构体传递给I2C设备器驱动和核心驱动从而完成设备的初始化和读写操作。

五、总结

I2C驱动的学习有一个特点:弄懂比较难,会用比较简单,这是因为有很多的有难度的内容以及和协议相关的内容都已经被Linux或者芯片厂封装好了,我们需要做的就是使用他们提供的这些接口完成指定设备的读写操作,但是我们的学习不能止步于此,所以我们不但要会用,还要知其然知其所以然。





审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 驱动器
    +关注

    关注

    51

    文章

    7310

    浏览量

    142972
  • SDA
    SDA
    +关注

    关注

    0

    文章

    122

    浏览量

    27556
  • I2C协议
    +关注

    关注

    0

    文章

    25

    浏览量

    8389
  • Linux驱动
    +关注

    关注

    0

    文章

    43

    浏览量

    9881

原文标题:Linux驱动:I2C驱动学习看这一篇就够了

文章出处:【微信号:嵌入式悦翔园,微信公众号:嵌入式悦翔园】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    【Z-turn Board试用体验】+ Zynq linux的I2C驱动学习笔记(

    苦于自己水平差些,就直没有去看,现在学习段时间,再看,感觉就比较轻松了。于是移植到Z-TURN BOARD上面,就记
    发表于 06-21 22:10

    【Z-turn Board试用体验】+ Zynq linux的I2C驱动学习笔记(二)

    是他们配备的依据之。id_table 是i2c_device_id结构体的个对象,里面定义了i2c驱动对应设备的
    发表于 06-21 22:25

    【DragonBoard 410c试用体验】i2c驱动TCS3414

    本帖最后由 终结者之梦 于 2016-10-21 00:05 编辑 上报告中我们已经安装mraa库并且能够操作GPIO,现在我们使用mraa的i2c函数,驱动TCS3414颜
    发表于 10-20 23:53

    到底什么是I2C

    之间的通信链路。这一解释已经很清楚了,但我还有更多的问题:它究竟是什么?什么类型的设备使用I2CI2C如何帮我解决系统中遇到的实际问题?I2C是使主设备(例如处理器,微控制器(MCU
    发表于 07-23 04:45

    如何驱动I2C总线?

    大家好,我使用的是KC705 Kintex主板。 Iwantto仅在不使用处理器的情况下将HDMI用于FPGA。我知道我必须通过I2C写入ADV7511。我有些代码用于示例屏幕,除了I2C信号之外
    发表于 09-20 07:25

    使用I2C和声驱动如何来解决各种问题

    我正在使用I2C和声驱动来解决各种问题。具体来说,我使用的是PIC32MZ2048ECH100I,我试图在I2C总线上与两个设备进行通信,
    发表于 05-05 15:57

    什么是软件I2C和硬件I2C

    学习I2C总线通信协议,完成基于I2C硬件协议的AHT20温湿度传感器的数据采集,并将采集的温度-湿度值通过串口输出。具体任务:1)解释什么是“软件I2C”和“硬件
    发表于 08-23 06:19

    使用I2C来对EEPROM进行操作

    I2C协议详解》我们了解了I2C的操作流程,这一,我们就使用
    发表于 12-13 08:19

    I2C总线驱动和设备驱动

    为400kbit/s常见iic设备eeprom触摸芯片温湿度传感器mpu6050(姿态传感器)…框架图I2C核心提供I2C总线驱动和设备驱动的注册方法、注销方法、
    发表于 12-13 06:18

    I2C读写与使用

    这一届爽是真的爽,hal库和cubemx节省半时间工作量23333终于学到I2C了,单片机的时候就没把I2C学好,现在来恶补,,,这个博主把为什么
    发表于 01-11 07:12

    分析rt-thread的I2C设备驱动框架

    简要上分析了RTT的PIN驱动,得到了很多网友的认可,很开心。很多人跟我反映写些u***,wlan等框架,这个
    发表于 01-12 06:23

    I2C总线的学习资料分享

    、概述  1、I2C总线只有两根双向信号线。根是数据线SDA,另根是时钟线SCL。   SCL:上升沿将数据输入到每个器件中;下降沿驱动
    发表于 01-19 08:05

    STM32核心板I2C通信驱动oled代码学习过程记录

    STM32核心板I2C通信驱动oled代码学习过程记录
    发表于 01-21 07:24

    I2C设备驱动的相关资料下载

    文章目录1、简介2I2C总线、设备和驱动的结构体定义2.1 结构体定义--I2C总线2.2 结构体定义--I2C设备2.3 结构体定义--
    发表于 02-10 06:39

    I2C通信详解

    救!《【STM32-I2C学习总结】STM32:硬件-IIC详解 , 固件库编程 , 手把手教你实现IIC》这里是我以前写的,硬件实现I2C个文章,文章末尾有软件实现
    发表于 02-28 11:24