引言
I2C作為一種簡單的數(shù)字通訊方式,僅需要兩根數(shù)據(jù)線就可以完成近距離主機(Master)與從機(Slave)之間的通訊,節(jié)省了MCU引腳以及額外的邏輯芯片,簡化了PCB布板難度,因此得到了廣泛的應用。近年來,TI也推出了越來越多支持I2C通訊功能的芯片,大大簡化了芯片與MCU之間的通訊,方便了系統(tǒng)的設計。
但在實際應用中,針對性能要求較低的應用場合,通常選擇外設較為簡單的低端主控MCU,可能并不具備I2C接口。對于此類應用,可以通過MCU的IO口進行I2C模擬,與被控器件建立通訊,達到發(fā)送控制指令、讀取內部寄存器的目的。即使在I2C接口缺失的情況下也能夠充分發(fā)揮器件的全部功能。
本文基于C2000提供了一種利用GPIO模擬I2C控制被控芯片的解決方案,并附有完整例程。對于絕大多數(shù)采用標準I2C通信協(xié)議以及部分采用SMBus的芯片均具有參考意義?;谄渌麺CU的方案也可參考該例程進行移植。
一、I2C通訊協(xié)議與GPIO模擬
I2C總線由兩條雙向信號線構成,分別為數(shù)據(jù)線(SDA)以及時鐘線(SCL),分別用電阻進行上拉,以實現(xiàn)高低電平之間的切換,進行設備之間的數(shù)據(jù)交交換。I2C允許的工作電壓范圍較為寬泛,典型電壓基準為+3.3V或+5V。常見的I2C總線速率分為以下幾種模式:標準模式(100Kbit/s)、快速模式(400Kbit/s)以及高速模式(3.4Mbit/s)等。如圖為典型的I2C連接示意圖:
圖1 I2C連接示意圖
如圖2為典型的I2C通訊幀格式示意圖。一幀完整的數(shù)據(jù)發(fā)送主要包括起始位、地址位、讀/寫位、ACK/NCK位、數(shù)據(jù)位等。下面對各部分進行簡要的講解,并介紹如何通過C2000進行實現(xiàn)。
圖2 I2C連接示意圖
1.1 起始及結束指令
當某個設備在I2C總線上被配置為主機(Master),該設備可以發(fā)送起始及結束信號用來發(fā)起或結束一次I2C通信,母線電平示意圖如圖2所示。
起始信號:在SCL為高電平期間,SDA由高電平轉換為低電平。
結束信號:在SCL為高電平期間,SDA由低電平轉換為高電平。
圖3 I2C通訊起始及結束信號
在C2000中,可以通過以下代碼實現(xiàn)起始信號的發(fā)送。其中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
}
可以參考以下代碼實現(xiàn)結束信號的發(fā)送:
voidI2C_Finish(void)
{
SDA_Low(); //CleartheSDA
SCL_Low(); //CleartheSCL
Delay(I2CDelay);
SCL_High();//SettheSCL
Delay(I2CDelay);
SDA_High();//SettheSDAwhileSCLishighindicatesthefinishsignal
}
1.2 數(shù)據(jù)位及地址位
I2C通訊的數(shù)據(jù)位通常由1-8的數(shù)據(jù)構成,在主機進行數(shù)據(jù)的發(fā)送以及讀取期間,SCL總線時鐘信號時鐘仍由主機發(fā)出,每個SCL高電平期間對應一位數(shù)據(jù)。在SCL高電平期間,都應該保持SDA上的數(shù)據(jù)正確,因此在實際的應用中,通常使得SDA的高電平脈寬寬于SCL。
地址位的發(fā)送與數(shù)據(jù)位類似,實際的操作中可以將設備的7位地址位+1位讀寫位作為一個8位字節(jié)進行整體的發(fā)送。以BQ25703A為例,默認設備地址為0x6B(7bit)。則在進行讀操作時,所要發(fā)送的字節(jié)為0xD7(1101011b+1b);進行寫操作時,所要發(fā)送的整體字節(jié)為0XD6 (1101011b+0b)。
數(shù)據(jù)位及地址位的發(fā)送均可參考以下發(fā)送一個8位byte的實現(xiàn)方法:
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)指令通常發(fā)生在一個byte發(fā)送結束之后,用于標志一個byte發(fā)送的成功或失敗。特別需要注意的是,即使是在ACK時鐘周期期間,SCL總線時鐘信號也是由主機產生的。
ACK: 當一次發(fā)送結束,主機釋放SDA總線。若發(fā)送成功,從機在第9個時鐘周期內拉低SDA總線,并在整個高電平期間保持。
NACK: 當一次發(fā)送結束,主機釋放SDA總線。若發(fā)送失敗,在第9個時鐘周期內SDA始終處于高電平。
在通訊中作為主機的MCU通常只需要實現(xiàn)NACK的發(fā)送以及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通訊操作,就可以發(fā)送一個完整I2C數(shù)據(jù)幀,實現(xiàn)基本的I2C通訊功能,構建了利用GPIO口模擬I2C進行芯片控制的基礎。
二、I2C模擬器件寄存器寫入與讀取
在構建了基本的I2C通訊功能之后,就可以利用I2C通訊對Slave進行控制或狀態(tài)的讀取,其本質就是對Slave的內部寄存器進行讀寫操作。下面以一個典型的帶有I2C功能的8位寄存器芯片為例,介紹如何利用前文的基礎I2C模擬函數(shù)對芯片的內部寄存器進行寫入和讀取。
I2C 寫入:要進行一次I2C寫入,MCU首先要發(fā)送一個起始位以及一個由7位slave地址位和讀寫位(0b)組成的8位硬件寫地址,而后釋放SDA總線。若地址正確,slave將拉低SDA發(fā)送一個ACK。此后,MCU發(fā)送寫入寄存器的地址,并等待slave返回的ACK。響應后,MCU發(fā)送8位數(shù)據(jù),并在收到ACK響應后發(fā)送停止位。
圖4 I2C寫入寄存器幀格式
具體實現(xiàn)方法可以參考以下代碼:
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讀?。阂x取Slave的內部寄存器,MCU首先要與Slave進行一次通信,告知Slave讀取的目標寄存器,該過程與進行寫入操作類似。MCU首先發(fā)送起始位、8位Slave寫地址,并在ACK信號后發(fā)送8位的目標寄存器地址。在Slave響應該地址后,MCU重新發(fā)送一次起始位,以及8位Slave讀地址(7位地址+1b),ACK響應后MCU釋放SDA總線,并繼續(xù)發(fā)送SCL時鐘信號讀取SDA上的內容。接收完成后,MCU 發(fā)送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通訊的頭文件以及函數(shù),下面對例程中的主要內容進行介紹,以方便讀者理解。
圖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文件依照所用器件實際情況修改硬件地址及各寄存器地址,就可以很方便地調用相關函數(shù)。
2)定義I2C通訊速率
#defineI2CDelay1//DefinetoconfigureI2Crate
| I2CDelay | I2C通訊時鐘高低電平時間 |
表2 I2C通訊速率
通過改變I2CDelay可以設置I2C通訊時鐘的高低電平持續(xù)時間,進而改變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設為輸入狀態(tài) |
| SDA_Output | 將SDA對應GPIO設為輸出狀態(tài) |
| SDA_Data_Register | SDA對應GPIO數(shù)據(jù)寄存器 |
| SCL_High() | 將SCL對應GPIO置1 |
| SCL_Low() | 將SCL對應GPIO置0 |
表3 IO口動作宏定義
將GPIO口的動作以宏定義的形式定義為SDA、SCL的動作,以增強代碼的可讀性。在進行程序移植時,只需要根據(jù)單片機實際情況將宏定義內的代碼更換成對應GPIO口動作的代碼,不需要對程序其他部分進行改動。其中EALLOW\EDIS語句是TI C2000產品改變GPIO口方向時需要解除相應的保護,請根據(jù)具體情況進行改動。
4)定義Delay函數(shù)
#defineDelay(A)DELAY_US(A)
Delay()函數(shù)用于進行程序中SDA、SCL的高低電平延時,在例程中實際被定義成DELAY_US()函數(shù)。在移植過程需要根據(jù)實際情況修改宏定義,更改成適用用戶MCU的延時函數(shù),不需要對后續(xù)程序進行修改。
3.2 I2C通訊功能函數(shù)
voidI2C_Start(void);
voidI2C_Finish(void);
Uint16I2C_Wait_Ack(void);
voidI2C_NAck(void);
voidI2C_Send_Byte(unsignedcharxtd);
unsignedcharI2C_Read_Byte(void);
| 函數(shù)名稱 | 功能描述 |
| void I2C_Start(void) | 發(fā)送I2C通訊起始信號 |
| void I2C_Finish(void) | 發(fā)送I2C通訊結束信號 |
| Uint16 I2C_Wait_Ack(void) | 等待Ack應答信號,返回接收狀態(tài) |
| void I2C_NAck(void) | 發(fā)送一個NAck信號,用于寄存器讀取 |
| void I2C_Send_Byte(unsigned char xtd) | 發(fā)送一個字節(jié) |
| unsigned char I2C_Read_Byte(void) | 讀取一個字節(jié) |
| void Gpio_setup(void) | GPIO口配置 |
| void I2C_Write_Register(unsigned char Device, unsigned char Register, unsigned char value) | I2C 寫寄存器函數(shù) |
| void I2C_Read_Register(unsigned char Device_Write, unsigned char Device_Read, unsigned char Register) | I2C 讀寄存器函數(shù) |
表4 I2C通訊函數(shù)
四、總結
針對由于MCU缺少I2C接口而不能直接使用I2C與外圍芯片進行通訊的問題,本文給出了使用IO模擬I2C接口的方法。首先,從I2C協(xié)議入手對數(shù)據(jù)幀中各個位的邏輯電平進行了詳細介紹,并給出基于C2000 GPIO的具體實現(xiàn)方法;在此基礎上,以常見的8位I2C通訊Slave為例介紹了內部寄存器的讀取邏輯,并給出了實現(xiàn)方法。最后,針對附帶的參考例程內容進行了介紹,方便讀者參考例程,其它MCU也可以在本例程上進行快速的移植。本文為使用IO模擬I2C需求給出了一種有效的解決方案。
審核編輯:何安
-
嵌入式處理
+關注
關注
0文章
341瀏覽量
10516
發(fā)布評論請先 登錄
RK平臺I2C開發(fā):從硬件原理到實戰(zhàn)排查
探索MAX7306:多功能I2C/SMBus接口GPIO與LED驅動器
MAX7304:集成ESD保護的I2C接口16端口GPIO與LED驅動器
ISO164x系列:增強EMC與GPIO功能的熱插拔雙向I2C隔離器
探索PCF8584:I2C總線控制器的卓越之選
深入解析 TCA9539:低電壓 16 位 I2C 和 SMBus I/O 擴展器
TCA8418:I2C控制的鍵盤掃描IC深度剖析
深入剖析LM8330:I2C兼容的鍵盤控制器與多功能拓展芯片
TCAL6408:8位轉換I2C總線/SMBus I/O擴展器的深度剖析
CW32單片機I2C接口來讀寫EEPROM芯片
I2C的優(yōu)點介紹
深入剖析I2C協(xié)議
利用GPIO模擬I2C控制被控芯片的解決方案
評論