电子说
步骤1:
I2C总线是一种简单的两线连接,可以将多个设备链接在一起,并允许它们交换数据。在最简单的形式中,有一台主设备与多个从设备进行通信。所有设备都并行连接到I2C总线的两条线。这两条线称为SCL和SDA。 SCL是时钟线,由主设备控制。 SDA是双向数据线。为了传输数据,主机发送一个从机地址并结合一位读/写标志。如果需要写操作,则主机将继续向被寻址的从机发送数据。如果请求读取,则从站将响应数据。为了协调事务,SCL和SDA线由主机和从机操纵,以发出几种情况的信号。其中包括START,STOP,ACK(确认)和NAK(未确认)。这些条件的详细信息由驱动程序处理。真正的极客可以在此Instructable末尾提供的链接中了解所有详细信息。
电气要求非常简单。主机和从机必须对Vcc使用相同的电平,必须接地,并且SCL和SDA线必须上拉至Vcc。上拉电阻的值是通过基于总线上总电容的计算精确确定的,但实际上几乎可以是1.8K到10K之间的任何值。我从5.1K开始使用较低的值,直到它起作用为止。除非您有很多设备或设备之间的电线很长,否则通常这不是问题。
I2C总线上的标称数据速率为100Kbits/秒。也可以达到400Kbit/s,1Mbits/s或更高的速率,但是此Instructable中的驱动程序不支持这些速率。所有I2C器件都将以100Kbit/s的速度工作。
ATtiny2313和ATmega168各自实现I2C总线的方式有所不同。 ATtiny2313使用通用串行接口(USI)硬件-也可以用于SPI总线。 ATmega168具有用于I2C总线的专用硬件,称为双线接口(TWI)。编写完驱动程序后,这些差异对用户几乎都是透明的。软件上的一个重要区别是:ATmega168 I2C驱动程序是由中断驱动的,而ATtiny2313则不是。这意味着ATmega168程序不必等待I2C数据传输发生,而只需要在启动另一个传输之前等待,或者直到数据从读取操作到达为止。 I2C地址的长度为7位,因此每个示例都有一个唯一的地址,因此总线上最多可以有127个设备。如图所示,此7位地址左移一位,最低有效位用于在该地址标记对设备的读取或写入。因此,完整的从机地址是一个8位字节。实际地址部分地在设备内部确定,并且不能更改(4个最高有效位),部分地由可以连接至设备引脚的位(3个最低有效位)确定,可以将其设置为高电平或低电平以进行设置一个特定的地址。
声音令人困惑,但是举个例子可以清楚地说明这一点。 PCA8574A数据表显示,I2C地址的四个最高有效位始终为0111。接下来的三位由引脚AD0,AD1和AD2的设置确定。这些引脚可以接地或连接到正电源(5伏),分别代表0或1。因此,可能的地址范围是38到3F十六进制,如PCA8574数据表的另一幅图所示。因此,通过更改地址位设置,可以同时在I2C总线上最多连接8个PCA8574A。每个都将仅响应其特定的从站地址。如果需要更多的I/O端口,则可以使用PCA8574。 PCA8574和PCA8574A之间的唯一区别是PCA8574的I2C从设备地址范围是20到27个十六进制。
确定给定设备的地址可能会造成混淆,因为某些数据手册认为读/写位是地址的一部分。仔细阅读数据手册,并牢记从机地址的长度为7位。读/写位应分开处理。同样,一个例子会有所帮助。我们将试验的24C16 EEPROM数据表中说,从机地址的前四个(最高有效位)是1010。接下来的三个位可以由A0,A1和A2确定。但请注意,数据手册还涵盖了尺寸较小的EEPROM 24C01至24C08。数据表中的图显示,这些地址位的设置随着大小的增加而被忽略,而对于24C16则被完全忽略。也就是说,最后三位无关紧要,而24C16实际上使用所有I2C从地址50至57十六进制。从机地址范围实际上将寻址24C16中的不同部分。前256个字节位于地址50h,下一个256位于地址51h,依此类推,直到最后一个256位于57h-总共2K字节。由于我们也尝试过的PCF8570 RAM的地址在此范围内,因此24C16和PCF8570不能一起使用。
第2步:订购一些I2C设备
现在您已经对I2C总线有所了解,并想使用它,为什么不订购
一些合适的设备包括I/O接口扩展器(我最喜欢的),静态Ram和EEPROM。还有很多,但是这是一个很好的开始。我们将使用的AVR处理器是ATtiny2313和Atmega168(在Arduino中使用)。如果您不熟悉这些,请看看这个功能强大的Instructable,以了解它们并构建您的Ghetto开发系统。本Instructable中ATmega168的示意图显示了如何为该处理器实现Ghetto开发系统。并行端口电缆与ATtiny2313的电缆相同。 (我尚未尝试使用Ghetto开发系统的USB版本,因此我不确定如何在其上访问I2C总线。与Arduino相同。)
这是Digikey的部件号。
Port Expander :
IC I2C I/O EXPANDER568-4236-5-ND
Ram:
IC SRAM 256X8 W/I2C568-1071-5-ND
EEPROM:
IC EEPROM串行16KCAT24C16LI-G -ND
步骤3:I2C驱动程序
以下是I2C总线驱动程序功能的说明。这些是使用面向初学者的Atmel Apps Notes开发的。没有他们,我无法做得到这一基础。使用WinAVR和gcc C编译器进行了开发。
下面对每个处理器的时钟速率限制进行了说明。由于我无法测试所有可能的处理器风格/时钟速率组合,因此我将坚持实际测试并尝试指出限制和限制。
以下是驱动程序功能以及如何使用它们。请查看示例以获取更多详细信息,并查看完整程序中使用的功能。
对于ATtiny2313:
时钟要求:
驱动程序的设计时钟频率为1MHz(默认频率)对于ATtiny2313。如果要以其他速率运行,则必须调整驱动程序中的常数。如果需要帮助,请给我发电子邮件。您还可以从“资源”步骤中的链接中的Atmel应用程序注释中获得一些提示。
USI_TWI_Master_Initialise()
此函数为I2C模式操作初始化USI硬件。在程序开始时调用一次。它返回void并且没有参数。
USI_TWI_Get_State_Info()
此函数返回I2C错误信息,如果I2C事务期间发生错误,则使用此函数。由于此函数仅返回错误代码,因此我使用函数TWI_Act_On_Failure_In_Last_Transmission(TWIerrorMsg)来闪烁错误LED。错误代码在USI_TWI_Master.h中定义。调用方法如下:
TWI_Act_On_Failure_In_Last_Transmission(USI_TWI_Get_State_Info())
USI_TWI_Start_Read_Write()
此函数用于向I2C器件读取和写入单个字节。它还用于写入多个字节。使用此功能有6个步骤。
1)在程序中声明一个消息缓冲区,以保存从站地址和要发送或接收的数据字节。
unsigned char messageBuf(MESSAGEBUF_SIZE);
2)将从地址作为缓冲区的第一个字节。向左移动一位,然后在“读/写”位中进行“或”操作。请注意,对于读操作,读/写位将为1,对于写操作将为0。本示例适用于读取。
messageBuf(0)=(TWI_targetSlaveAddress 《 3)进行写操作时,将要写入的字节放入缓冲区的下一个位置。
4)以消息缓冲区和消息大小作为参数调用USI_TWI_Start_Read_Write函数。
temp = USI_TWI_Start_Read_Write (messageBuf,2);
5)可以测试返回值(在这种情况下为温度)以查看是否发生错误。如果是这样,则如上所述进行处理。请参阅程序中的示例。
6)如果请求读取,则读取的字节将位于缓冲区的第二个位置。
如果要写入多个字节(例如,写入存储设备),则此可以使用相同的例程。设置缓冲区和调用例程略有不同。缓冲区中的第二个字节将是要写入的起始存储器地址。要写入的数据将在后续字节中。消息大小将是包括所有有效数据的大小。因此,如果要写入6个字节,则消息大小将为8(从站地址+内存地址+ 6个数据字节)。
USI_TWI_Start_Random_Read()
此函数用于从I2C设备读取多个字节。 ,通常仅对某种内存有意义。使用此例程与上一个例程非常相似,但有两个例外。
读/写位的设置无关紧要。调用此例程将始终导致读取操作。
messageSize应该为2加上要读取的字节数。
如果未发生错误,则数据将从第二个位置开始在缓冲区中。 》对于ATmega168:
时钟要求:
驱动程序设计用于ATmega168的4MHz时钟速率。示例代码显示了如何设置此时钟速率。如果要以其他速率运行,则必须调整驱动程序中的常数。如果需要这样做,请给我发送电子邮件。
TWI_Master_Initialise()
此函数将初始化TWI硬件以进行I2C模式操作。在程序开始时调用一次。它返回void并且没有参数。确保在初始化后通过调用swi()来启用中断。
TWI_Get_State_Info()
此函数返回I2C错误信息,如果I2C事务期间发生错误,则使用此函数。由于此函数仅返回错误代码,因此我使用函数TWI_Act_On_Failure_In_Last_Transmission(TWIerrorMsg)来闪烁错误LED。错误代码在TWI_Master.h中定义,但已修改以在错误LED上发出信号。有关详细信息,请参见示例代码。调用方法如下:
TWI_Act_On_Failure_In_Last_Transmission(TWI_Get_State_Info())
请注意,通过确保I2C事务完成(如下所述),然后在全局状态字中进行一点测试来完成错误检查。 br》 TWI_Start_Read_Write()
TWI_Start_Random_Read()
这两个功能与上述相应功能相同,但有一些例外。
它们不返回任何错误值。
读取的数据为没有转移到缓冲区。这样做将通过下面描述的功能完成。
调用TWI_Start_Random_Read时,messageSize应该是请求的数据字节数加1,而不是2。
ATmega168的I2C驱动程序是中断驱动的。也就是说,启动I2C事务,然后在主例程继续运行时独立执行。当主例程要从它启动的I2C事务中获取数据时,它必须检查该数据是否可用。错误检查的情况相同。在检查错误之前,主例程必须确保I2C事务已完成。接下来的两个函数用于这些目的。
TWI_Transceiver_Busy()
在检查错误之前,调用此函数以查看I2C事务是否已完成。示例程序演示了如何使用此方法。
TWI_Read_Data_From_Buffer()
调用此函数可将数据从I2C驱动程序的接收缓冲区传输到消息缓冲区。此功能将确保在传输数据之前完成I2C事务。当此函数返回一个值时,我发现直接检查错误位更加可靠。调用方法如下。消息大小应比所需的数据位数大一。数据将从第二个位置开始在messageBuf中。
temp = TWI_Read_Data_From_Buffer(messageBuf,messageSize);
第4步:构建!
首先下载文件I2C Schematics.zip。您可能需要在工作区中创建一个I2C文件夹,以保存原理图和示例程序文件。将原理图解压缩到该目录中。您会找到一个名为I2C Schematics的文件夹。打开名为tiny I2C.pdf的文件。此示意图显示了ATtiny2313 Ghetto开发系统和PCA8574A I/O端口扩展器(周围带有大虚线框)。端口扩展器电路建立在面包板上。看看照片,看看这些电路是什么样的。它们确实非常简单。
该原理图的ATtiny2313部分只是带有三个闪烁指示灯(LED1、2和3,以及R4、5和6)和一个挂钩的按钮(S1)的Ghetto开发系统。 ,以及其他一些细节。该细节是增加了跳线(JP4、5和6),可以将其删除以允许连接I2C总线SCL和SDA线。跳线必须在适当的位置进行编程,然后将其卸下,以便可以连接SCL和SDA。照片显示了跳线到位并被移除。这些跳线的位置由您决定,如果要使用I2C总线,只需将它们放在Ghetto开发系统上。必须断开I2C总线,并设置跳线以进行编程。请注意,对于I2C总线,您只需要真正关心JP4和JP6。如果您想使用SPI总线,请放入JP5。PCA8574AI/O端口扩展器的面包板非常简单。提供Vcc(+5伏)和Gnd(接地)连接,并将AD0、1和2接地(使I2C从地址38十六进制)。然后连接4个指示灯和4个DIP开关。 (如果没有DIP开关,则可以使用电线。分别接地或悬空以分别打开或关闭信号。)最后,将上拉电阻(R11和12)从SDA和SCL连接到Vcc。这些显示为3.3K,但是从1.8K到5.1K的任何值都可以工作(也许可以达到10K,但我没有尝试过)。对ATtiny2313进行编程后,就可以删除跳线,并连接SDA和SCL进行测试。
现在用于ATmega168。这里唯一的麻烦是您可能尚未为此处理器构建Ghetto开发系统。如果是这样,那么我提供的原理图(MEGA I2C.pdf)将向您展示如何。这只是ATtiny2313版本的排列。如果您提前计划,可以确保您的编程电缆可同时适用于两个系统。主要区别在于添加了C2和C3。请参阅图片中的这些位置,它们应该非常靠近芯片;其中之一实际上在芯片之下。这些特别有助于将噪声排除在模数转换器之外。除非计划使用SPI总线,否则不需要插入跳线,因为该芯片上的I2C总线不需要它们。请注意,PCA8754A面包板将保持不变。您只需将SDA和SCL连接起来,就可以使用!容易吗?
第5步:我们进行编码和测试!
是时候构建驱动程序和示例程序了。我们将从刚构建的ATtiny2313和PCA8574A面包板开始。将文件I2C.zip下载到您的I2C工作目录中并解压缩。您将拥有一个名为I2C的新文件夹。在其中,您将找到USI I2C(用于ATtiny2313)和TWI I2C(用于ATmega168)。在USI I2C中,您会找到I_O端口文件夹。该文件夹包含我们第一个示例程序的代码以及USI I2C驱动程序。
使用WinAVR将代码编译并加载到ATtiny2313中。深呼吸并打开电源。预期结果如下:
上电时,ATtiny2313的PD6端口上的LED 1闪烁两次。
在按下按钮(S1)之前,什么都不会发生。每次按下按钮,将读取开关,并且其设置将显示在连接到PCA8574A的LED上。更改开关的值,按按钮,LED将会改变。继续这样做,直到您克服了看到它起作用的快感。如果(上帝禁止!)事情没有按预期进行,请仔细检查接线。 I2C错误将通过LED3(PD4)闪烁来指示,这可能意味着您需要检查SDA和SCL是否已连接到正确的引脚并被正确上拉。如果仍然无法解决问题,请阅读本节的其余部分以了解有关调试的信息。
现在返回并让我们看一下代码。打开文件USI_I2C_Port.c。这是示例程序的代码。 (USI_TWI_Master.c和USI_TWI_Master.h包含驱动程序-除非感到好奇,否则可以忽略它们。)使用该示例指导自己的I2C应用程序。
该程序通常向您展示如何初始化和使用I2C驱动程序。 ,包括设置从站地址和消息缓冲区的其余部分,以及从中获取数据。您还将看到我如何反跳按钮并设置while循环。该程序有一些细节值得一提。请注意,来自交换机的数据在写入端口扩展器上的LED之前必须先反转。另请注意,必须将端口扩展器上的输入端口写为高电平,以使其正常工作。这些细节在PCA8574A数据手册中进行了描述。请务必仔细阅读数据表!
更感兴趣的是使用条件调试。在程序文件的开始附近是语句//#define DEBUG,并且在整个代码中散布的是#ifdef DEBUG语句。只要未定义DEBUG(两个斜杠使该行成为注释并阻止其定义),就不会编译#ifdef至#endif语句中的代码。但是,如果事情没有按您预期的那样进行,请使用未注释的#define DEBUG重新编译并重新加载代码。您将获得更多的LED闪烁,您可以对其进行解码,以跟随程序的执行,并帮助您准确地找出问题所在。实际上,我建议您尝试此操作以查看会发生什么。
您会看到随着程序执行的进行,LED 2(PD5上的LED)将闪烁。从开关读取的值在端口扩展器LED上显示之前,将在LED 1(PD6)上闪烁。通过使用这些LED,您应该能够在程序运行时对其进行跟踪。
接下来,我们将与ATmega168一起工作。如果您仅对ATtiny2313感兴趣,请跳过此部分。还在我这儿?好。移至TWI_I2C文件夹,将工作目录更改为IO_Port,然后编译并将TWI_I2C_Port.c加载到ATmega168中。从ATtiny2313断开SDA和SCL线,然后将它们连接到ATmega168。连接电源和地面,然后加电。操作应该是一样的!播放直到刺激消退,然后看一下代码。
打开TWI_I2C_Port.c。该代码几乎完全相同,除了错误处理和容纳中断驱动的驱动程序。区别在于:
请注意,必须将时钟设置为4MHz才能使I2C总线正常工作。
sei(); I2C驱动程序初始化后,该语句打开中断。
要检查错误,将测试特定的状态位。
在读取期间,必须调用TWI_Read_Data_From_Buffer函数将读取的数据传输到消息缓冲区中。
在写入期间,必须使用(TWI_Transceiver_Busy())来确保在检查错误之前传输已完成。
最后两个功能已在上面的驱动程序说明中进行了描述。除此之外,代码与ATtiny2313几乎相同。如果您想进行调试,则DEBUG的工作原理也相同。
步骤6:使用I2C内存
现在,我们已经学会了使用I2C总线读写I/O端口扩展器,让我们继续使用RAM和I2C存储器EEPROM。主要区别在于可以使用单个I2C命令从存储器读取或写入多个字节。
为准备好进行这些实验,我们需要稍微修改硬件并在试验板上建立几个新电路。保留端口扩展器电路,因为我们将使用它来显示一些内存值。从PCA8574A上卸下DIP开关,然后在那些引脚上放置闪烁指示灯。如果您没有足够的眨眼指示灯,请将P4到P7的指示灯移动到P0到P3。 (要显示的值足够小。)
现在看原理图I2C Ram.pdf并将PCF8570连接到试验板上。也看一下图片。确保将引脚7连接到Vcc。从PCA8574A为SDA和SCL布线。不需要额外的上拉电阻。
如果您也对EEPROM感兴趣,也可以使用24C16的I2C EEPROM.pdf构建该电路,但请注意,该示例使用ATmega168。这个电路真的很简单。如上所述,应该忽略地址位。只需接通电源并接地即可。由于我们还没有完成对Ram的实验,因此请不要连接SDA和SCL。我们将通过连接到PCA8574A端口扩展器和PCF8570 Ram的ATtiny2313开始内存实验。该程序将一些数字写入Ram,然后将其读回并显示在Port Expander上。
将工作目录更改为USI I2C下的RAM。使用生成文件来编译和下载USI_I2C_RAM.c。请注意,I2C驱动程序文件与我们先前使用的文件相同。接通电源,LED 1(PD6)上将闪烁一次。数据将被写入内存的前4个字节。按下按钮,将读取并显示两个字节。您应该在端口扩展器(P0)上看到一个LED指示灯,停顿两秒钟,然后在两个LED指示灯(P0和P1)上点亮。再暂停两秒钟,指示灯应熄灭。再按一次按钮可重新开始序列。调试与上述方法相似。
让我们看一下代码。打开USI_I2C_RAM.c。它看起来应该与之前的代码非常相似。主要区别在于读取和写入内存的细节。在实际执行写操作的调用之前,先查看消息缓冲区的加载方式。第一个字节是从机地址,其读/写位已适当设置。但是下一个字节是开始写入数据的内存地址。然后是实际的数据字节,这些字节将从我们指定的地址开始依次加载到内存中。我们将消息大小指定为6。因此,我们从地址00开始写入,并将值01、03、02和06写入内存位置00至03。
要从内存中读取数据,我们必须使用USI_TWI_Start_Random_Read。功能。消息缓冲区在第一个字节中获取从机地址,在第二个字节中获取起始地址。然后调用消息大小设置为要读取的字节数加上2的函数。请注意,读/写位无关紧要,因为无论如何都将进行读取。返回的数据将从消息缓冲区的第二个位置开始。读入数据后,将其反转以显示在端口扩展器上,并一次向其写入一个字节,并在两个值之间暂停。最后,端口扩展器LED熄灭。对端口扩展器的写入与前面的示例相同。有趣的是,您可以取消上面的#define DEBUG语句的注释,并看到许多闪烁的LED。
在另一个成功的实验后兴奋不已,让我们转到ATmega168和EEPROM。将工作目录更改为TWI I2C下的EEPROM。使用生成文件来编译和下载TWI_I2C_EEPROM.c。请注意,I2C驱动程序文件与我们先前用于PCA8574A的文件相同。要测试程序,请断开ATtiny2313的连接并连接ATmega168。将I2C总线挂在Ram上并加电。结果不同,因为我们现在正在写入和读取更多数据。初始化时,PD7上的LED 1应该闪烁。按下按钮,数据将从存储器中读取并显示。 PCA8574上的LED应按以下顺序闪烁:P1,P0和P2(全部熄灭),P0和P1,P1和P2。最后,端口LED均应熄灭。再按一次按钮即可重复。
哦,等一下,你说。该程序不是用于EEPROM吗?由于我们正在访问具有相同I2C地址的存储设备,因此同一程序对Ram和EEPROM均有效。断电,然后将SDA和SCL从Ram移至EEPROM,然后再次运行程序。它应该工作完全相同。请注意,由于EEPROM和Ram共享相同的地址,因此它们不能同时连接到I2C总线。 (其中一些聪明的人可能会考虑更改Ram上的可编程地址位,但这仍然行不通。24C16使用了可以为Ram编程的整个地址块。)
好,让我们来看一下最后一个程序。打开TWI_I2C_EEPROM.c。首先要注意的是,我已经说明了如何寻址完整的24C16 EEPROM。可以在8个不同的I2C从设备地址上以256字节块的形式对其进行访问。查看如何将MEMORY_ADDR定义为十六进制50的起始地址;这就是公羊工作的原因。如果要访问24C16的其他块,请使用我已指出的其他地址。看看我如何设置写入内存。首先将设置了读/写位的从机地址放入缓冲区,然后将起始地址00放入,然后是16字节数据。调用函数TWI_Start_Read_Write以将消息大小设置为18写入数据(如前所述)。按下按钮时,我们使用TWI_Start_Random_Read和TWI_Read_Data_From_Buffer读回数据。每第三个字节显示在端口扩展器LED上。最后,LED熄灭以等待下一次按钮按下。
您可能想知道为什么我选择写16个字节。如果仔细阅读数据表,您会发现24C16只要接收到16个字节就执行写周期,即使发送了更多字节也是如此。因此,这似乎是一个不错的数字。如果选择增加此值,则必须更改MESSAGEBUF_SIZE的大小。您还必须在TWI_Master.h中更改值TWI_BUFFER_SIZE。这是因为驱动程序从消息缓冲区复制了数据,以供中断服务程序使用。结果恭喜!现在您可以在自己的项目中使用I2C总线了!
第7步:网络资源
以下是用于实验的零件的数据表链接。如果您什么都没有,那么您肯定应该得到这些。端口扩展器,Ram,EEPROM和NXP作为I2C的创建者,NXP(Philips)有很多很棒的东西。 (他们喜欢在URL中使用方括号,所以我不能在此处正确包括它们。抱歉。)要进入I2C区域,请从“产品”列表中选择“接口”。访问它们提供的所有数据表和应用笔记,尤其是I2C总线描述和技术细节。
从Atmel获得ATtiny2313和ATmega168数据表(数据手册?)。
Atmel应用笔记在这里。看一下AVR310和AVR315,也获取代码。
在这里看看更多I2C内容。
步骤8:极客注意事项
对于想要了解详细信息的真正极客,如果您查看Atmel Apps Notes和驱动程序代码,请牢记以下几点:
-寻址和命令I2C设备的方法不是规范的一部分!除了从机地址和读/写位以外,没有指定命令,模式等,这些命令,模式等特定于给定设备。为清楚起见,请注意Atme中使用的方案
全部0条评论
快来发表一下你的评论吧 !