GPIO口模拟I2C操作
/*
? ?? ???作者:天空
? ?? ???日期:2014.5.12
? ?? ???功能:利用GPIO口模擬I2C總線,對傳感器寄存器讀取數(shù)據(jù)
? ?? ???注意:如果需要移植些文件到其他設(shè)備上,需要更改的函數(shù)地方
? ?? ???1、利用GPIO口模擬的數(shù)據(jù)線SDA和時鐘線SCL,注意需要更改GPIO口
? ?? ???2、從設(shè)備地址。不同的從設(shè)備,地址不一樣,注意修改。(在I2C寫寄存器和讀寄存器的函數(shù)中,如果是寫,在讀的地址上加1.)
? ?? ???經(jīng)驗:利用GPIO口模擬I2C總線時,需要注意的是,在應(yīng)答信號和讀數(shù)據(jù)的時候,一定要將SDA數(shù)據(jù)線配置為輸入。
*/
#define SCL P0_5//定義P0.5口為時鐘線
#define SDA P0_4//定義P0.4口為數(shù)據(jù)線
typedef unsigned int uint;
typedef unsigned char uchar;
typedef unsigned short ushort;
/*延時函數(shù)*/
void Delay(void)
{
}
/*I2C總線實始化,將P0.4和P0.5配置為輸出口,并且拉高*/
/*
? ?? ???I2C總線的開始時序:
? ?? ???1、將數(shù)據(jù)線SDA和時鐘線SCL全部拉高
*/
void I2C_Init(void)
{
? ? P0SEL &= ~0x30;//將P0.4和P0.5配置為GPIO口
? ? P0DIR |= 0x30;//將P0.4和P0.5配置為輸出
? ? P0 |= 0x30;//將P0.4和P0.5設(shè)置為高電平
}
/*I2C總線的開始信號*/
/*
? ?? ???I2C總線的開始時序:
? ?? ???1、將SDA數(shù)據(jù)線拉高
? ?? ???2、將SCL時鐘線拉高,延時一段時間
? ?? ???3、在SCL為高電平時,拉低SDA,延時一段時間
? ?? ???總結(jié):就是在SCL為高電平時,在SDA上給一個下降沿,表示開始信號。
*/
void I2C_Start(void)
{
? ? SDA = 1;
? ? SCL = 1;
? ? Delay();
? ? SDA = 0;
? ? Delay();
}
/*I2C總線的停止信號*/
/*
? ?? ???I2C總線的停止時序:
? ?? ???1、將SDA數(shù)據(jù)線拉低
? ?? ???2、將SCL數(shù)據(jù)線拉高,延時一段時間
? ?? ???3、在SCL為高電平時,將SDA數(shù)據(jù)線拉高,延時一段時間
? ?? ???總結(jié):在SCL為高電平時,在SDA上給一下上升沿,表示停止信號。
*/
void I2C_Stop(void)
{
? ? SDA = 0;
? ? SCL = 1;
? ? Delay();
? ? SDA = 1;
? ? Delay();
}
/*I2C總線的應(yīng)答信號*/
/*
? ?? ???I2C總線應(yīng)答時序:
? ?? ???1、首先是需要將模擬數(shù)據(jù)線的SDA口配置為輸入口。
? ?? ???2、將時鐘線SCL拉高,延時一段時間
? ?? ???3、等待從設(shè)備應(yīng)答,從設(shè)備應(yīng)答時,SDA上會產(chǎn)生一個高電平,主設(shè)備的GPIO口檢測到高電平,則表示應(yīng)答成功。
? ?? ???4、如果檢測到應(yīng)答信號,則拉SCL時鐘線拉低。延時一段時間。
? ?? ???5、將模擬SDA口的GPIO口配置為輸出。
*/
void I2C_Ack(void)
{
? ? P0DIR &= ~0x10;//注意這里,數(shù)據(jù)線應(yīng)該設(shè)置為輸入
? ? uint i = 0;
? ? SCL = 1;
? ? Delay();
? ? //等待應(yīng)答
? ? while((SDA == 1)&&(i<255))
? ?? ?i++;
? ? SCL = 0;
? ? Delay();
? ? P0DIR |= 0x10;//將數(shù)據(jù)線SDA設(shè)置為輸出
}
/*I2C總線寫數(shù)據(jù)函數(shù),將要寫的數(shù)據(jù)傳遞給形參I2CBuf*/
/*
? ?? ???I2C寫數(shù)據(jù)時序:
? ?? ???注意:由于I2C屬于串行總線,所以數(shù)據(jù)都是一位一位的傳輸
? ?? ???1、將SCL時鐘線拉高,準(zhǔn)備傳輸數(shù)據(jù),延時一段時間
? ?? ???2、將I2CBuf的最高位傳輸?shù)絊DA數(shù)據(jù)線上
? ?? ???3、拉低SCL時鐘線,延時一段時間
? ?? ???小結(jié):當(dāng)SCL有一個上升沿時,傳輸數(shù)據(jù)。
? ?? ???以上三步表示傳輸完一位數(shù)據(jù)。然后將I2CBuf左移一位,再重復(fù)以上三步。
? ?? ???最后,將SCL位低,延時一段時間,再將SDA位高,延時一段時間。此操作是為了釋放時鐘線SCL和數(shù)據(jù)線SDA.
*/
void I2C_Write(uint I2CBuf)
{
? ? uint iwi;
? ? for(iwi = 0; iwi < 8; iwi++)
? ? {
? ?? ???SCL = 0;
? ?? ???Delay();
? ?? ???if(I2CBuf & 0x80)
? ?? ?? ? SDA = 1;
? ?? ???else?
? ?? ?? ? SDA = 0;
? ?? ???SCL = 1;
? ?? ???Delay();
? ?? ???I2CBuf = I2CBuf << 1;
? ? }
? ? SCL = 0;
? ? Delay();
? ? SDA = 1;
? ? Delay();
}
/*I2C總線讀數(shù)據(jù)函數(shù),該函數(shù)具有返回值I2CBuf*/
/*
? ?? ???I2C讀數(shù)據(jù)函數(shù)的時序:
? ?? ???注意:首先需要將模擬SDA數(shù)據(jù)線的GPIO口配置為輸入
? ?? ???1、將時鐘線SCL拉高,延時一段時間
? ?? ???2、讀取數(shù)據(jù)線SDA上的電平,然后寫入I2CBuf的最低位。
? ?? ???3、拉低SCL時鐘線,延時一段時間
? ?? ???小結(jié):當(dāng)SCL有一個下降沿時,讀取數(shù)據(jù)。
? ?? ???以上三步表示讀取一位數(shù)據(jù),讀一個字節(jié),需要重復(fù)以上操作步驟8次。
*/
uint I2C_Read(void)
{
? ? uint iri;
? ? uint I2CBuf = 0;
? ? P0DIR &= ~0x10;//注意這里,需要將SDA配置為輸入。
? ? Delay();
? ? for(iri = 0; iri < 8; iri++)
? ? {
? ?? ???I2CBuf = I2CBuf << 1;//此步不可少
? ?? ???SCL = 1;
? ?? ???Delay();
? ?? ???if(SDA == 1)
? ?? ?? ? I2CBuf |= 0x01;
? ?? ???else
? ?? ?? ? I2CBuf &= 0xfe;
? ?? ???SCL = 0;
? ?? ???Delay();
? ? }
? ? P0DIR |= 0X10;//將SDA配置為輸出
? ? return I2CBuf;//將讀到的數(shù)據(jù)存入I2CBuf中,然后返回給主調(diào)函數(shù)
}
/*
I2C總線寫寄存器函數(shù),將寄存器地址傳給形參RegAdd,將要寫入的數(shù)據(jù)傳遞給RegValue。
注意:地址和數(shù)據(jù)最好都采用十六進制
*/
/*
? ?? ???I2C寫寄存器函數(shù)的時序:
? ?? ???1、主設(shè)備先發(fā)出一個開始信號
? ?? ???2、然后寫入從設(shè)備地址。每一個從設(shè)備的地址是唯一的。
? ?? ???3、主設(shè)備接收應(yīng)答信號
? ?? ???4、主設(shè)備向從設(shè)備寫入寄存器的地址
? ?? ???5、主設(shè)備接收應(yīng)答信號
? ?? ???6、主設(shè)備向從設(shè)備寫入數(shù)據(jù)
? ?? ???7、主設(shè)備接收應(yīng)答信號
? ?? ???8、主設(shè)備發(fā)出停止信號
*/
void I2C_WriteReg(uint RegAdd, uint RegValue)
{
? ? I2C_Start();
? ? I2C_Write(0xd0);//0xd0是從設(shè)置的地址,不同的設(shè)備,地址不同。所以移植些函數(shù)時,需要更改此處。
? ? I2C_Ack();
? ? I2C_Write(RegAdd);//先寫地址,此地址為需要寫入數(shù)據(jù)的寄存器地址
? ? I2C_Ack();
? ? I2C_Write(RegValue);//寫入數(shù)據(jù)
? ? I2C_Ack();
? ? I2C_Stop();
}
/*I2C總線讀寄存器函數(shù),將需要讀取數(shù)據(jù)的寄存器的地址傳遞給形參RegAdd,此函數(shù)有返回值*/
/*
? ?? ???I2C讀寄存器函數(shù)的時序:
? ?? ???1、主設(shè)備發(fā)出開始信號
? ?? ???2、寫入從設(shè)備地址
? ?? ???3、主設(shè)備接收從設(shè)備發(fā)來的應(yīng)答信號
? ?? ???4、寫入寄存器地址
? ?? ???5、主設(shè)備接收應(yīng)答信號
? ?? ???6、主設(shè)備再次發(fā)出開始信號
? ?? ???7、主設(shè)備向從設(shè)備寫入(從設(shè)備的地址+1),此時加1表示要從寄存器中讀數(shù)據(jù)
? ?? ???8、從設(shè)備給出應(yīng)答信號
? ?? ???9、從從設(shè)備的寄存器中讀出數(shù)據(jù)傳給變量
? ?? ???10、主設(shè)備發(fā)出停止信號
? ?? ???11、返回讀出的數(shù)據(jù)
*/
uint I2C_ReadReg(uint RegAdd)
{
? ? uchar date;
? ? I2C_Start();
? ? I2C_Write(0xd0);//先寫入從設(shè)備的地址。表示要向從設(shè)備中寫地址和數(shù)據(jù)
? ? I2C_Ack();
? ? I2C_Write(RegAdd);//寫入寄存器的地址,數(shù)據(jù)將從些地址中讀出
? ? I2C_Ack();
? ? I2C_Start();
? ? I2C_Write(0xd1);//從設(shè)備的地址加1,表示要讀從設(shè)備中寄存器的數(shù)據(jù)
? ? I2C_Ack();
? ? date = I2C_Read();//從地址RegAdd中讀出數(shù)據(jù)
? ? I2C_Stop();
? ? return date;//將讀出的數(shù)據(jù)存入date中,然后返回給主調(diào)函數(shù)。
}
?
總結(jié)
以上是生活随笔為你收集整理的GPIO口模拟I2C操作的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【第十一讲】TMS320F28335开发
- 下一篇: 计算机视觉、机器学习、人工智能领域知识汇