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或者芯片厂封装好了,我们需要做的就是使用他们提供的这些接口完成指定设备的读写操作,但是我们的学习不能止步于此,所以我们不但要会用,还要知其然知其所以然。





审核编辑:刘清

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

    关注

    52

    文章

    8241

    浏览量

    146399
  • SDA
    SDA
    +关注

    关注

    0

    文章

    124

    浏览量

    28140
  • I2C协议
    +关注

    关注

    0

    文章

    26

    浏览量

    8475
  • Linux驱动
    +关注

    关注

    0

    文章

    43

    浏览量

    9965

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

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

收藏 人收藏

    评论

    相关推荐

    GPIO模拟I2C总线的驱动设计与实现

    I2C总线简单方便,是我们经常使用的种总线。但有时候我们的MCU没有足够多的I2C控制器来实现我们的应用,所幸我可以使用普通的GPIO引脚来模拟低速的I2C总线通信。
    发表于 12-14 14:19 5395次阅读
    GPIO模拟<b class='flag-5'>I2C</b>总线的<b class='flag-5'>驱动</b>设计与实现

    I2C总线驱动程序的实现

    I2C总线驱动程序的实现 I2C 驱动程序的简介本驱动程序为标准的51 系列CPU 编写,让CPU 模拟成
    发表于 09-26 17:25 5749次阅读
    <b class='flag-5'>I2C</b>总线<b class='flag-5'>驱动</b>程序的实现

    基于51的I2c总线

    I2c总线,基于51的I2c总线,程序学习。快来下载学习
    发表于 01-13 11:49 34次下载

    I2C LCD 器件通过驱动带有 I2C 接口的 2 线式 16 字符 LCD

    I2C LCD 器件通过驱动带有 I2C 接口的 2 线式 16 字符 LCD
    发表于 10-10 08:22 13次下载
    <b class='flag-5'>I2C</b> LCD 器件通过<b class='flag-5'>驱动</b>带有 <b class='flag-5'>I2C</b> 接口的 <b class='flag-5'>2</b> 线式 16 字符 LCD

    Linux的I2C驱动架构

    控制器的差异,不考虑其实现细节地与硬件设备通讯。1.1 总线驱动在系统开机时,首先装载的是I2C总线驱动个总线驱动用于支持
    发表于 04-02 14:38 680次阅读

    I2C通信协议应该如何学习

    我最近刚做完I2C通信协议的编写与调试,下面介绍下我从开始理解夏老师的程序,修改程序,直到下板调试整个的学习过程,希望对大家学习
    发表于 04-28 08:00 21次下载
    <b class='flag-5'>I2C</b>通信协议应该如何<b class='flag-5'>学习</b>

    I2C系列的合集,可以系统学习I2C协议

    这篇文章给大家带来了I2C系列的合集,可以系统学习I2C协议。大家赶紧看看吧! 1、I2C总线:何时使用I2C缓冲器 本文讨论了使用
    的头像 发表于 09-23 15:28 3155次阅读

    STM32学习I2C协议(读写EEPROM)

    关于STM32学习分享第七章 I2C协议(读写EEPROM)文章目录关于STM32学习分享前言二、代码1.i2c.c2.i2c.h3.main.c总结前言开始!开始!单片机的
    发表于 11-30 15:21 32次下载
    STM32<b class='flag-5'>学习</b>之<b class='flag-5'>I2C</b>协议(读写EEPROM)

    Linux驱动I2C设备驱动(基于Freescale i.MX6ULL平台了解I2C驱动框架,顺便写个简陋的MPU6050驱动

    文章目录1、简介2I2C总线、设备和驱动的结构体定义2.1 结构体定义--I2C总线2.2 结构体定义--I2C设备2.3 结构体定义--
    发表于 12-06 13:51 8次下载
    Linux<b class='flag-5'>驱动</b>:<b class='flag-5'>I2C</b>设备<b class='flag-5'>驱动</b>(基于Freescale <b class='flag-5'>i</b>.MX6ULL平台了解<b class='flag-5'>I2C</b>的<b class='flag-5'>驱动</b>框架,顺便写个简陋的MPU6050<b class='flag-5'>驱动</b>)

    嵌入式内核及驱动开发-09IIC子系统框架使用(I2C协议和时序,I2C驱动框架,I2C从设备驱动开发,MPU6050硬件连接

    文章目录I2c协议和时序I2c介绍I2c硬件连接I2c总线的信号I2c总线写时序I2c总线读时序
    发表于 12-06 14:06 17次下载
    嵌入式内核及<b class='flag-5'>驱动</b>开发-09IIC子系统框架使用(<b class='flag-5'>I2C</b>协议和时序,<b class='flag-5'>I2C</b><b class='flag-5'>驱动</b>框架,<b class='flag-5'>I2C</b>从设备<b class='flag-5'>驱动</b>开发,MPU6050硬件连接

    硬件I2C与模拟I2C

    硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的,因而效率要远高于软件模拟的
    发表于 12-28 19:14 81次下载
    硬件<b class='flag-5'>I2C</b>与模拟<b class='flag-5'>I2C</b>

    ESP32 之 ESP-IDF 教学(六)——I2C数据总线(I²C

    ESP32 之 ESP-IDF 学习笔记(六)【I2C数据总线(I²C)】文章目录ESP32 之 ESP-IDF 学习笔记(六)【
    发表于 12-28 19:25 22次下载
    ESP32 之 ESP-IDF 教学(六)——<b class='flag-5'>I2C</b>数据总线(<b class='flag-5'>I</b>²<b class='flag-5'>C</b>)

    Linux I2C驱动入门知识科普

    I2C 总线驱动I2C总线驱动就是SOC的 I2C控制器驱动,也叫做
    的头像 发表于 12-29 13:59 1692次阅读

    I2C控制器驱动介绍

    (控制器)抽象成 i2c_adapter。 对于I2C 适配器,肯定要对外提供读写 API 函数,设备驱动程序可以使用这些 API 函数来完成读写操作。
    的头像 发表于 07-22 15:38 1495次阅读
    <b class='flag-5'>I2C</b>控制器<b class='flag-5'>驱动</b>介绍

    I2C子系统SW Architecture

    I2C SW Architecture 【driver 驱动层】由普通驱动工程师负责,【i2c 核心层】由 Linux 提供,【i2c 核心
    的头像 发表于 07-22 16:01 957次阅读
    <b class='flag-5'>I2C</b>子系统SW Architecture