PIC微控制器是微芯片为嵌入式项目提供的功能强大的平台,它的通用性使其能够找到进入许多应用程序的途径,并且该阶段仍在继续。如果您一直在关注我们的PIC教程,那么您会发现我们已经从基础知识开始涵盖了有关PIC微控制器的各种教程。从现在开始,我们已经涵盖了基础知识,我们可以进入更有趣的内容,例如通信门户。
在庞大的嵌入式应用系统中,没有微控制器可以独自执行所有活动。在某些时候它必须与其他设备进行通信以共享信息,有许多不同类型的通信协议可以共享这些信息,但是最常用的是USART,IIC,SPI和CAN。每个通信协议都有其自身的优点和缺点。现在让我们专注于IIC部分,因为这是我们将在本教程中学习的内容。
什么是I2C通信协议?
IIC一词代表“内部集成电路”。在某些地方,它通常表示为I2C或I平方C或什至表示为2线接口协议(TWI),但它们的含义相同。I2C是同步通信协议,这意味着共享信息的两个设备必须共享一个公共时钟信号。它只有两根导线可以共享信息,其中一根用于塞子信号,另一根用于发送和接收数据。
I2C通信如何工作?
I2C通信最初是由Phillips引入的。如前所述,它有两条线,这两条线将跨两个设备连接。在这里,一个设备称为主机,另一设备称为从机。通讯应该并且将始终在两个主机和一个从机之间进行。I2C通信的优势在于,一个主设备可以连接多个从设备。
完整的通信通过这两条线进行,即串行时钟(SCL)和串行数据(SDA)。
串行时钟(SCL):与从机共享主机产生的时钟信号
串行数据(SDA):在主机和从机之间发送数据。
在任何给定时间,只有主站才可以发起通信。由于总线中有多个从站,因此主站必须使用不同的地址引用每个从站。当被寻址时,只有具有该特定地址的从属才会回复该信息,而其他人则退出。这样,我们可以使用同一条总线与多个设备进行通信。
在哪里使用I2C通讯?
I2C通信仅用于短距离通信。它一定程度上是可靠的,因为它具有同步时钟脉冲以使其变得智能。该协议主要用于与必须向主机发送信息的传感器或其他设备进行通信。当微控制器必须使用最少的电线与许多其他从模块通信时,这非常方便。如果您要进行远程通信,则应尝试RS232;如果要寻找更可靠的通信,则应尝试SPI协议。
使用XC8编译器的PIC16F877a和I2C
足够的介绍,让我们开始学习,学习如何使用微控制器执行I2C通信。在我们开始明确本教程仅讨论使用XC8编译器的PIC16F877a中的I2C之前,该过程对于其他单片机将是相同的,但可能需要稍作更改。还要记住,对于像PIC18F系列这样的高级微控制器,编译器本身可能内置了一些库以使用I2C功能,但是对于PIC16F877A则不存在这样的库,因此让我们自己构建一个库。此处说明的库将作为头文件供下载,该文件可在底部下载,可用于PIC16F877A与其他I2C器件进行通信。
与往常一样,开始任何事情的最佳地点是我们的数据表。在数据表中查找有关I2C的详细信息,并检查必须配置的寄存器。我不会详细解释,因为数据表已经为您完成了。在下面的内容中,我将解释头文件中存在的不同功能及其在程序中的职责。
I2C_Initialize()
初始化函数用于告知微控制器我们将使用I2C协议。这可以通过将SSPCON和SSPCON2寄存器上的所需位置1来完成。第一步是将IIC引脚声明为输入引脚,这里RC3和RC4引脚应用于I2C通信,因此我们将它们声明为输入引脚。接下来,我们应该设置SSPCON和SSPCON2,这是MSSP控制寄存器。我们正在以ISC主模式运行PIC,时钟频率为FOSC /(4 *(SSPADD +1))。请参考下面注释行中提到的数据表的页码,以了解为何以这种方式设置特定寄存器。
因此,接下来我们必须设置时钟频率,不同应用的时钟频率可能会有所不同,因此我们通过变量 feq_k 从用户那里进行选择,并在公式中使用它来设置SSPADD寄存器。
void I2C_Initialize(const unsigned long feq_K)//开始将IIC作为主设备 { TRISC3 = 1; TRISC4 = 1; //将SDA和SCL引脚设置为输入引脚 SSPCON = 0b00101000; // pg84 / 234 SSPCON2 = 0b00000000; // pg85 / 234 SSPADD =(_XTAL_FREQ /(4 * feq_K * 100))-1; //设置时钟速度pg99 / 234 SSPSTAT = 0b00000000; // pg83 / 234 }
无效I2C_Hold()
下一个重要功能是 I2C_hold 函数,该函数用于保持设备的执行,直到当前I2C操作完成为止。在开始任何新操作之前,我们将必须检查是否必须保留I2C操作。这可以通过检查寄存器SSPSTAT和SSPCON2来完成。SSPSTAT包含有关I2C总线状态的信息。
该程序似乎有点复杂,因为它涉及“ and”和“ or”运算符。当你打破它
SSPSTAT和0b00000100 SSPCON2和0b00011111
这意味着我们要确保SSPSTAT的第二位为零,类似地,SSPCON2的从0到4的位为零。然后,我们将所有这些结合起来以检查结果是否为零。如果结果为零,则该程序将继续执行,直到不为零为止,因为它将在 while 循环中使用,所以它将保持在那里。
无效的I2C_Hold() { while((SSPCON2&0b00011111)-(SSPSTAT&0b00000100)); //检查寄存器上的this以确保IIC不在进行中 }
无效I2C_Begin()和无效I2C_End()
每当我们使用I2C总线写入或读取任何数据时,都应开始和结束I2C连接。要开始I2C通信,我们必须将SEN位置1,而要结束通信,我们必须将PEN状态位置1。在切换任何这些位之前,我们还应该通过使用函数I2C_Hold来检查I2C总线是否繁忙。
void I2C_Begin() { I2C_Hold(); //保持程序为I2C忙 SEN = 1; //开始IIC pg85 / 234 } void I2C_End() { I2C_Hold(); //保持程序为I2C忙 PEN = 1; //结束IIC pg85 / 234 }
虚空I2C_Write()
写功能用于将任何数据从主模块发送到从模块。该功能通常在I2C开始功能之后使用,然后是I2C结束功能。必须写入IIC总线的数据通过变量数据传递。然后,该数据被加载到SSPBUF缓冲寄存器中,以通过I2C总线发送。
通常,在写入数据之前,将写入一个地址,因此您必须使用两次写入功能,一次用于设置地址,另一次用于发送实际数据。
void I2C_Write(unsigned data) { I2C_Hold(); //保持程序为I2C忙 SSPBUF =数据;// pg82 / 234 }
无符号的简短I2C_Read()
我们必须知道的最后一个函数是 I2C_Read 函数。该功能用于读取I2C总线上当前的数据。在要求从机将一些值写入总线后使用。接收到的值将在 SSPBUF中, 我们可以将该值传输到用于我们操作的任何变量。
在I2C通讯期间,从机在发送主机请求的数据后将发送另一个位,即确认位,主机也应检查该位以确保通讯成功。在检查ACKDT位的确认后,应通过将ACKEN位置1来使能。
无符号短I2C_Read(无符号短ACK) { 无符号短传入; I2C_Hold(); RCEN = 1; I2C_Hold(); 传入= SSPBUF; //获取保存在SSPBUF I2C_Hold() 中的数据;ACKDT =(ack)?0:1; //检查确认位是否收到 ACKEN = 1; // pg 85/234 返回传入; }
就是说,这些功能应该足以建立I2C通信并从设备写入或读取数据。还要注意,I2C通信还可以执行许多其他功能,但是为了简单起见,我们在这里不讨论它们。您随时可以参考数据表以了解
可以从链接下载用于PIC16F877A I2C通信的带有头文件的完整代码。
使用I2C头文件进行编程:
现在,我们已经了解了I2C通信的工作原理以及如何使用为此创建的头文件,让我们编写一个简单的程序,在其中使用头文件并将一些值写入I2C行。然后,我们将模拟该程序,并检查这些值是否正在总线上写入。
程序一如既往地开始设置配置位并将时钟频率设置为20 MHz,如下所示
#pragma config FOSC = HS //振荡器选择位(HS振荡器) #pragma config WDTE = OFF //看门狗定时器使能位(禁止WDT) #pragma config PWRTE = ON //上电定时器使能位(使能PWRT) # pragma config BOREN = ON //欠压复位使能位(使能BOR) #pragma config LVP = OFF //低压(单电源)在线串行编程使能位(RB3为数字I / O,HV开启必须使用MCLR进行编程) #pragma config CPD = OFF //数据EEPROM存储器代码保护位(Data EEPROM代码保护已关闭) #pragma config WRT = OFF //闪存程序存储器写使能位(写保护已关闭;所有程序存储器可能由EECON控件写入) #pragma config CP = OFF //闪存程序存储器代码保护位(代码保护关闭) #define _XTAL_FREQ 20000000
下一步将添加我们刚刚讨论过的头文件。头文件名为 PIC16F877a_I2C.h ,可以从我们上面讨论的链接下载。确保将头文件添加到项目列表的头文件中,项目文件结构应如下所示
确保将头文件添加到您的项目文件后,将头文件包含在主C文件中
#包括
在 while 循环内,我们将开始I2C通信,向I2C总线写入一些随机值,然后结束I2C通信。我选择的随机值为D0、88和FF。您可以输入所需的任何值。但是请记住这些值,因为我们将在仿真中对其进行验证。
while(1) { I2C_Begin(); I2C_Write(0xD0); I2C_Write(0x88); I2C_Write(0xFF); I2C_End(); __delay_ms(1000); }
该完整的程序可以在页面的底部找到,你可以使用或从这里下载该程序的完整的zip文件。得到程序后,对其进行编译并准备进行仿真。
Proteus模拟:
Proteus有一个很好的工具,称为I2C调试器,可用于读取I2C总线上的数据,因此,让我们使用它构建电路并检查数据是否被成功写入。完整的电路图如下所示
双击微处理器,加载由我们的程序生成的十六进制文件。然后模拟程序。您会注意到一个弹出窗口,它将显示有关I2C总线的所有信息。我们程序的窗口如下所示。
如果仔细查看正在写入的数据,您会发现它们与我们在程序中写入的数据相同。值为D0、88和FF。这些值每1秒写入一次,因此时间也如下所示进行更新。蓝色箭头表示它是从主机写入从机的,否则将指向相反的方向。下面显示了正在发送的数据的详细信息。
这只是I2C可以做的事情的一瞥,它也可以将数据读取和写入多个设备。我们将通过与适用于I2C协议的各种模块进行接口连接,在即将到来的教程中介绍有关I2C的更多信息。
希望您理解该项目并从中学到一些有用的东西。如果您有任何疑问,请在下面的评论部分中发布它们,或使用论坛获取技术帮助。
完整的代码已在下面给出;您可以从此处下载包含所有代码的头文件。