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