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

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

3天内不再提示

利用GPIO模拟I2C控制被控芯片的解决方案

电子设计 来源:电子设计 作者:电子设计 2022-01-13 14:15 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

引言

I2C作为一种简单的数字通讯方式,仅需要两根数据线就可以完成近距离主机(Master)与从机(Slave)之间的通讯,节省了MCU引脚以及额外的逻辑芯片,简化了PCB布板难度,因此得到了广泛的应用。近年来,TI也推出了越来越多支持I2C通讯功能的芯片,大大简化了芯片与MCU之间的通讯,方便了系统的设计。

但在实际应用中,针对性能要求较低的应用场合,通常选择外设较为简单的低端主控MCU,可能并不具备I2C接口。对于此类应用,可以通过MCU的IO口进行I2C模拟,与被控器件建立通讯,达到发送控制指令、读取内部寄存器的目的。即使在I2C接口缺失的情况下也能够充分发挥器件的全部功能。

本文基于C2000提供了一种利用GPIO模拟I2C控制被控芯片的解决方案,并附有完整例程。对于绝大多数采用标准I2C通信协议以及部分采用SMBus的芯片均具有参考意义。基于其它MCU的方案也可参考该例程进行移植。

一、I2C通讯协议与GPIO模拟

I2C总线由两条双向信号线构成,分别为数据线(SDA)以及时钟线(SCL),分别用电阻进行上拉,以实现高低电平之间的切换,进行设备之间的数据交交换。I2C允许的工作电压范围较为宽泛,典型电压基准为+3.3V或+5V。常见的I2C总线速率分为以下几种模式:标准模式(100Kbit/s)、快速模式(400Kbit/s)以及高速模式(3.4Mbit/s)等。如图为典型的I2C连接示意图:

图1 I2C连接示意图

如图2为典型的I2C通讯帧格式示意图。一帧完整的数据发送主要包括起始位、地址位、读/写位、ACK/NCK位、数据位等。下面对各部分进行简要的讲解,并介绍如何通过C2000进行实现。

图2 I2C连接示意图

1.1 起始及结束指令

当某个设备在I2C总线上被配置为主机(Master),该设备可以发送起始及结束信号用来发起或结束一次I2C通信,母线电平示意图如图2所示。

起始信号:在SCL为高电平期间,SDA由高电平转换为低电平。

结束信号:在SCL为高电平期间,SDA由低电平转换为高电平。


图3 I2C通讯起始及结束信号

在C2000中,可以通过以下代码实现起始信号的发送。其中SCL及SDA分别代表用C2000 GPIO模拟的SDA及SCL总线,具体定义请参考例程部分。

voidI2C_Start(void)

{

Delay(I2CDelay);

SCL_High();//SettheSCL

SDA_High();//SettheSDA

Delay(I2CDelay);

SDA_Low(); //CleartheSDAwhileSCLishighindicatesthestartsignal

Delay(I2CDelay);

SCL_Low(); //CleartheSCLtogetreadytotransmit

}

可以参考以下代码实现结束信号的发送:

voidI2C_Finish(void)

{

SDA_Low(); //CleartheSDA

SCL_Low(); //CleartheSCL

Delay(I2CDelay);

SCL_High();//SettheSCL

Delay(I2CDelay);

SDA_High();//SettheSDAwhileSCLishighindicatesthefinishsignal

}

1.2 数据位及地址位

I2C通讯的数据位通常由1-8的数据构成,在主机进行数据的发送以及读取期间,SCL总线时钟信号时钟仍由主机发出,每个SCL高电平期间对应一位数据。在SCL高电平期间,都应该保持SDA上的数据正确,因此在实际的应用中,通常使得SDA的高电平脉宽宽于SCL。

地址位的发送与数据位类似,实际的操作中可以将设备的7位地址位+1位读写位作为一个8位字节进行整体的发送。以BQ25703A为例,默认设备地址为0x6B(7bit)。则在进行读操作时,所要发送的字节为0xD7(1101011b+1b);进行写操作时,所要发送的整体字节为0XD6 (1101011b+0b)。

数据位及地址位的发送均可参考以下发送一个8位byte的实现方法:

voidI2C_Send_Byte(unsignedchartxd)

{

intt;

SDA_Output(); //Config SDA GPIO as output

SCL_Low(); //CleartheSCLtogetreadytotransmit

txd&=0X00FF; // Getthe lower8bits

for(t=0;t<8;t++)  

{

SDA_Data_Register=(txd&0x80)>>7;//SendtheLSB

txd<<=1;  

Delay(I2CDelay/2);

SCL_High();//SettheSCL

Delay(I2CDelay);

SCL_Low(); //CleartheSCL

Delay(I2CDelay/2);

}

}

1.3 ACK/NACK指令

Acknowledge(ACK)以及Not Acknowledge(NACK)指令通常发生在一个byte发送结束之后,用于标志一个byte发送的成功或失败。特别需要注意的是,即使是在ACK时钟周期期间,SCL总线时钟信号也是由主机产生的。

ACK: 当一次发送结束,主机释放SDA总线。若发送成功,从机在第9个时钟周期内拉低SDA总线,并在整个高电平期间保持。

NACK: 当一次发送结束,主机释放SDA总线。若发送失败,在第9个时钟周期内SDA始终处于高电平。

在通讯中作为主机的MCU通常只需要实现NACK的发送以及ACK信号的等待,具体可参考以下程序:

voidI2C_NAck(void)

{

SCL_Low(); //CleartheSCLtogetreadytotransmit

SDA_Low(); //CleartheSDA

Delay(I2CDelay);

SCL_High(); //SettheSCL

Delay(I2CDelay);

SCL_Low(); //CleartheSCL

Delay(I2CDelay);

}

Uint16I2C_Wait_Ack(void)

{

intErrTime=0;

intReadAck=0;

SDA_Input();//Config SDAGPIO as Input

Delay(I2CDelay);

SCL_High();//SettheSCLandwaitforACK

while(1)

{

ReadAck=SDA_Data_Register;//Readtheinput

if(ReadAck)

{

ErrTime++;

if(ErrTime>ErrLimit)

{

//Errorhandler:Seterrorflag,retryorstop.

//Definebyusers

return1;

}

}

if(ReadAck==0) //Receive a ACK

{

Delay(I2CDelay);

SCL_Low();//CleartheSCLforNextTransmit

return0;

}

}

}

基于以上几个基本的I2C通讯操作,就可以发送一个完整I2C数据帧,实现基本的I2C通讯功能,构建了利用GPIO口模拟I2C进行芯片控制的基础。

二、I2C模拟器件寄存器写入与读取

在构建了基本的I2C通讯功能之后,就可以利用I2C通讯对Slave进行控制或状态的读取,其本质就是对Slave的内部寄存器进行读写操作。下面以一个典型的带有I2C功能的8位寄存器芯片为例,介绍如何利用前文的基础I2C模拟函数对芯片的内部寄存器进行写入和读取。

I2C 写入:要进行一次I2C写入,MCU首先要发送一个起始位以及一个由7位slave地址位和读写位(0b)组成的8位硬件写地址,而后释放SDA总线。若地址正确,slave将拉低SDA发送一个ACK。此后,MCU发送写入寄存器的地址,并等待slave返回的ACK。响应后,MCU发送8位数据,并在收到ACK响应后发送停止位。

图4 I2C写入寄存器帧格式

具体实现方法可以参考以下代码:

voidI2C_Write_Register(unsignedcharDevice,unsignedcharRegister,unsignedcharValue)

{

I2C_Start();

I2C_Send_Byte(Device);//Sendthedeviceaddress

I2C_Wait_Ack(); //Waitfortheacksignal

I2C_Send_Byte(Register);//Sendtheregisteraddress

I2C_Wait_Ack(); //Waitfortheacksignal

I2C_Send_Byte(Value); //Sendregistervalue

I2C_Wait_Ack();

I2C_Finish();

}

I2C读取:要读取Slave的内部寄存器,MCU首先要与Slave进行一次通信,告知Slave读取的目标寄存器,该过程与进行写入操作类似。MCU首先发送起始位、8位Slave写地址,并在ACK信号后发送8位的目标寄存器地址。在Slave响应该地址后,MCU重新发送一次起始位,以及8位Slave读地址(7位地址+1b),ACK响应后MCU释放SDA总线,并继续发送SCL时钟信号读取SDA上的内容。接收完成后,MCU 发送NACK位以及STOP位结束一次寄存器读取操作。

图5 I2C读取寄存器帧格式

8位Byte的读方法可以参考以下代码:

unsignedcharI2C_Read_Byte(void)

{

intt,rxData;

unsignedcharreceive;

SDA_Input();

for(t=0;t<8;t++)  

{

SCL_Low();//CleartheSCL

Delay(I2CDelay);

SCL_High();//SettheSCL

receive<<=1;  

rxData=SDA_Data_Register;

if(rxData)

{

receive++;

}

Delay(I2CDelay);

}

returnreceive;

}

寄存器的读方法可以参考以下代码:

unsignedcharI2C_Read_Register(unsignedcharDevice_Write,unsignedcharDevice_Read,unsignedcharRegister)

{

unsignedcharReadData;

I2C_Start();

I2C_Send_Byte(Device_Write);//Sendthedeviceaddress

I2C_Wait_Ack(); //Waitfortheacksignal

I2C_Send_Byte(Register); //Sendtheregisteraddress

I2C_Wait_Ack(); //Waitfortheacksignal

I2C_Start();

I2C_Send_Byte(Device_Read); //Sendregistervalue

I2C_Wait_Ack();

SDA_High(); //SettheSDA

ReadData=I2C_Read_Byte();

I2C_NAck();

Delay(1);

I2C_Finish();

returnReadData;

}

三、参考例程

本文附带的例程中包含了完整GPIO模拟I2C通讯的头文件以及函数,下面对例程中的主要内容进行介绍,以方便读者理解。

图6 I2C通讯程序架构

3.1宏定义

1)定义硬件通讯通讯地址及寄存器地址:

#defineDevice_Address_Write0xC0

#defineDevice_Address_Read0xC1

#defineREG_1 0x01

#defineREG_2 0x02

#defineREG_3 0x03

#defineREG_4 0x04

Device_Address_Write 硬件写地址:默认地址0x60(7bit)+0b
Device_Address_Read 硬件读地址:默认地址0x60(7bit)+0b
REG_1 - 4 硬件内部寄存器地址

表1 硬件读写地址及寄存器地址

在调用此代码时,只需在.h文件依照所用器件实际情况修改硬件地址及各寄存器地址,就可以很方便地调用相关函数。

2)定义I2C通讯速率

#defineI2CDelay1//DefinetoconfigureI2Crate

I2CDelay I2C通讯时钟高低电平时间

表2 I2C通讯速率

通过改变I2CDelay可以设置I2C通讯时钟的高低电平持续时间,进而改变I2C的通讯速率。实际应用中,该值可以通过实际测试进行调整,以达到理想的通讯速率。

3)定义IO口动作

#defineSDA_High(){GpioDataRegs.GPASET.bit.GPIO7=1;EALLOW;GpioCtrlRegs.GPADIR.bit.GPIO7=1;EDIS;}

#defineSDA_Low(){GpioDataRegs.GPACLEAR.bit.GPIO7=1;EALLOW;GpioCtrlRegs.GPADIR.bit.GPIO7=1;EDIS;}//TocleartheSDAline.Disableprotectionforwritingregister

#defineSDA_Input(){EALLOW;GpioCtrlRegs.GPADIR.bit.GPIO7=0;EDIS;}//SDADIR=Input

#defineSDA_Output(){EALLOW;GpioCtrlRegs.GPADIR.bit.GPIO7=1;EDIS;}//SDADIR=Output

#defineSDA_Data_RegisterGpioDataRegs.GPADAT.bit.GPIO7

#defineSCL_High(){GpioDataRegs.GPASET.bit.GPIO6=1;}//SettheSCLline

#defineSCL_Low(){GpioDataRegs.GPACLEAR.bit.GPIO6=1;}//CleartheSCLline

SDA_High() 将SDA对应GPIO置1
SDA_Low() 将SDA对应GPIO置1
SDA_Input 将SDA对应GPIO设为输入状态
SDA_Output 将SDA对应GPIO设为输出状态
SDA_Data_Register SDA对应GPIO数据寄存器
SCL_High() 将SCL对应GPIO置1
SCL_Low() 将SCL对应GPIO置0

表3 IO口动作宏定义

将GPIO口的动作以宏定义的形式定义为SDA、SCL的动作,以增强代码的可读性。在进行程序移植时,只需要根据单片机实际情况将宏定义内的代码更换成对应GPIO口动作的代码,不需要对程序其他部分进行改动。其中EALLOW\EDIS语句是TI C2000产品改变GPIO口方向时需要解除相应的保护,请根据具体情况进行改动。

4)定义Delay函数

#defineDelay(A)DELAY_US(A)

Delay()函数用于进行程序中SDA、SCL的高低电平延时,在例程中实际被定义成DELAY_US()函数。在移植过程需要根据实际情况修改宏定义,更改成适用用户MCU的延时函数,不需要对后续程序进行修改。

3.2 I2C通讯功能函数

voidI2C_Start(void);

voidI2C_Finish(void);

Uint16I2C_Wait_Ack(void);

voidI2C_NAck(void);

voidI2C_Send_Byte(unsignedcharxtd);

unsignedcharI2C_Read_Byte(void);

函数名称 功能描述
void I2C_Start(void) 发送I2C通讯起始信号
void I2C_Finish(void) 发送I2C通讯结束信号
Uint16 I2C_Wait_Ack(void) 等待Ack应答信号,返回接收状态
void I2C_NAck(void) 发送一个NAck信号,用于寄存器读取
void I2C_Send_Byte(unsigned char xtd) 发送一个字节
unsigned char I2C_Read_Byte(void) 读取一个字节
void Gpio_setup(void) GPIO口配置
void I2C_Write_Register(unsigned char Device, unsigned char Register, unsigned char value) I2C 写寄存器函数
void I2C_Read_Register(unsigned char Device_Write, unsigned char Device_Read, unsigned char Register) I2C 读寄存器函数

表4 I2C通讯函数

四、总结

针对由于MCU缺少I2C接口而不能直接使用I2C与外围芯片进行通讯的问题,本文给出了使用IO模拟I2C接口的方法。首先,从I2C协议入手对数据帧中各个位的逻辑电平进行了详细介绍,并给出基于C2000 GPIO的具体实现方法;在此基础上,以常见的8位I2C通讯Slave为例介绍了内部寄存器的读取逻辑,并给出了实现方法。最后,针对附带的参考例程内容进行了介绍,方便读者参考例程,其它MCU也可以在本例程上进行快速的移植。本文为使用IO模拟I2C需求给出了一种有效的解决方案。

审核编辑:何安

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

    关注

    0

    文章

    341

    浏览量

    10559
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    基于 RT-Thread 的软 I2C:比你想象的更简单

    ,对性能和带宽的要求并不高。在这种情况下,使用GPIO模拟I2C(即软I2C)就显得非常有价值:既能节省宝贵的硬件资源,又能满足功能需求,还能提升系统的灵活性。本文
    的头像 发表于 05-19 22:01 3302次阅读
    基于 RT-Thread 的软 <b class='flag-5'>I2C</b>:比你想象的更简单

    Adafruit TCA9548A 1-to-8 I2C 多路复用器:解决 I2C 设备地址冲突的利器

    ,Adafruit TCA9548A 1-to-8 I2C 多路复用器的出现,为我们提供了一个完美的解决方案。 文件下载: 2717.pdf 一、概述 在使用 I2C 传感器时,我们可能会遇到这样
    的头像 发表于 05-11 12:05 313次阅读

    如何在 Yocto 中集成由 HID 设备控制I2C 触摸驱动程序?

    我使用的是汇顶科技触摸,它通过 hid-ft260 等 hid 设备连接到 imx 处理器。 如何在 Yocto 中集成由 HID 设备控制I2C 触摸驱动程序。目前,我正在 HID 设备创建
    发表于 04-23 07:22

    RK平台I2C开发:从硬件原理到实战排查

    在嵌入式开发中,I2C 总线是连接外设的 “桥梁”—— 小到传感器、EEPROM,大到 LCD 驱动器、音频芯片,都离不开它的控制。而瑞芯微(Rockchip)系列芯片作为主流嵌入式
    的头像 发表于 02-05 13:42 3155次阅读
    RK平台<b class='flag-5'>I2C</b>开发:从硬件原理到实战排查

    探索MAX7306:多功能I2C/SMBus接口GPIO与LED驱动器

    探索MAX7306/MAX7307:多功能I2C/SMBus接口GPIO与LED驱动器 在电子设计领域,对于高效、灵活的GPIO扩展和LED驱动方案的需求从未停止。今天,我们将深入探讨
    的头像 发表于 02-02 15:55 307次阅读

    MAX7304:集成ESD保护的I2C接口16端口GPIO与LED驱动器

    MAX7304:集成ESD保护的I2C接口16端口GPIO与LED驱动器 在电子设备的设计中,GPIO扩展器和LED驱动器是常见的组件,它们对于实现设备的各种功能起着关键作用。今天我们要介绍
    的头像 发表于 02-02 15:50 348次阅读

    利用I2C接口采用轮询方式读写EEPROM芯片

    利用I2C接口,采用轮询方式读写EEPROM芯片 #define TESTI2C2 //I2C1 = 1
    发表于 01-23 06:00

    ISO164x系列:增强EMC与GPIO功能的热插拔双向I2C隔离器

    ISO164x系列:增强EMC与GPIO功能的热插拔双向I2C隔离器 在电子设计领域,I2C总线的应用广泛,但实现其可靠的隔离通信一直是个挑战。ISO1640、ISO1641、ISO1642
    的头像 发表于 01-21 14:40 545次阅读

    探索PCF8584:I2C总线控制器的卓越之选

    探索PCF8584:I2C总线控制器的卓越之选 在电子工程师的日常工作中,选择合适的芯片来实现特定功能至关重要。今天,我们就来深入探讨一款功能强大的I2C总线
    的头像 发表于 12-28 15:40 875次阅读

    基于FPGA的I2C控制模块设计

    I2C_WRITE_WDATA.v模块实现I2C写时序,I2C_Controller (I2C控制器)例化了
    的头像 发表于 12-26 09:48 6441次阅读
    基于FPGA的<b class='flag-5'>I2C</b><b class='flag-5'>控制</b>模块设计

    TCA8418:I2C控制的键盘扫描IC深度剖析

    TCA8418:I2C控制的键盘扫描IC深度剖析 在电子设备的设计中,键盘扫描IC是实现人机交互的关键组件之一。德州仪器(TI)的TCA8418就是这样一款具有卓越性能的I2C控制键盘
    的头像 发表于 12-25 09:20 586次阅读

    深入剖析LM8330:I2C兼容的键盘控制器与多功能拓展芯片

    深入剖析LM8330:I2C兼容的键盘控制器与多功能拓展芯片 在电子设计领域,一款功能强大且性能稳定的芯片往往能为产品带来质的飞跃。今天,我们就来详细探讨一下德州仪器(TI)推出的LM
    的头像 发表于 12-23 15:25 688次阅读

    CW32单片机I2C接口来读写EEPROM芯片

    .功能框图 I2C 模块主要包括时钟发生器、输入滤波器、地址比较器、协议控制逻辑、仲裁和同步逻辑、以及相关寄存器等。 CW32L083 支持用户灵活选择 GPIO 作为 I2C
    发表于 12-09 07:43

    I2C的优点介绍

    简单性和线路效率: I2C 仅使用两条线路(SDA(串行数据线)和 SCL(串行时钟线))提供简单而高效的通信解决方案,而与总线上的设备数量无关。这种简单性降低了电路设计的复杂性,最大限度地减少
    发表于 11-27 07:49

    深入剖析I2C协议

    I2C也是一种可以多主设备,多从设备的总线协议,通过地址索引,I2C可以使能所需从设备,I2C的出现主要是用来实现不同集成电路组件之间的控制功能,比如通过
    的头像 发表于 08-21 15:10 4061次阅读
    深入剖析<b class='flag-5'>I2C</b>协议