聊城冠縣網(wǎng)站建設(shè)班級(jí)優(yōu)化大師怎么用
單片機(jī)學(xué)習(xí)!
目錄
前言
一、軟件I2C讀寫代碼框架
二、I2C初始化
三、六個(gè)時(shí)序基本單元
3.1 引腳操作的封裝和改名? ?
3.2 起始條件執(zhí)行邏輯
3.3 終止條件執(zhí)行邏輯
3.4 發(fā)送一個(gè)字節(jié)
3.5 接收一個(gè)字節(jié)
3.5 發(fā)送應(yīng)答&接收應(yīng)答
3.5.1 發(fā)送應(yīng)答
3.5.2 接收應(yīng)答
總結(jié)
前言
????????本文介紹了軟件I2C讀寫代碼,I2C協(xié)議層重點(diǎn)關(guān)注的是使用的兩個(gè)引腳、I2C配置、時(shí)序的高低電平等協(xié)議相關(guān)的內(nèi)容。
一、軟件I2C讀寫代碼框架
????????代碼整體框架:首先建立I2C通信層的.c和.h模塊,在通信層里寫好I2C底層的GPIO初始化和6個(gè)時(shí)序基本單元,也就是起始、終止、發(fā)送一個(gè)字節(jié)、接收一個(gè)字節(jié)、發(fā)送應(yīng)答和接收應(yīng)答。
????????由于本代碼使用軟件I2C,所以I2C的庫函數(shù)暫時(shí)不用看,軟件I2C只需要用GPIO的讀寫函數(shù)就行了。
軟件I2C初始化要做兩個(gè)任務(wù):
- 第一個(gè)任務(wù),把SCL和SDA都初始化為開漏輸出模式;
- 第二個(gè)任務(wù),把SCL和SDA置高電平。
?? ?
二、I2C初始化
????????當(dāng)前接線SCL是PB10,SDA是PB11。所以要開啟GPIOB,PB10和PB11都要配置成開漏輸出的模式。雖然開漏輸出名字上帶了個(gè)輸出,但并不代表它只能輸出,開漏輸出模式仍然可以輸入。輸入時(shí)先輸出1再直接讀取輸入數(shù)據(jù)寄存器就行了。這個(gè)過程在之前博文講I2C硬件規(guī)定時(shí)介紹過。
代碼示例:
void MyI2C_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//開啟時(shí)鐘GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode= GPIO_Mode_Out_OD;GPIO_InitStruct.GPIO_Pin= GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);GPIO_SetBits(GPIOB,GPIO_Pin_10 | GPIO_Pin_11);//把GPIOB的PB10和PB11都置高電平}
????????調(diào)用MyI2C_Init函數(shù),PB10和PB11兩個(gè)端口就被初始化為開漏輸出模式,然后釋放總線。SCL和SDA處于高電平,此時(shí)I2C總線處于空閑狀態(tài)。
三、六個(gè)時(shí)序基本單元
3.1 引腳操作的封裝和改名? ?
????????第一個(gè)基本單元,起始條件。根據(jù)波形圖,首先把SCL和SDA都確保釋放。然后先拉低SDA,再拉低SCL,這樣就能產(chǎn)生起始條件了。
????????在這里可以不斷地調(diào)用SetBits和ResetBit函數(shù),來手動(dòng)翻轉(zhuǎn)高低電平。但是這樣做會(huì)在后面的程序中出現(xiàn)非常多的地方來指定這個(gè)GPIO端口號(hào)。一方面,這樣做語義不是很明顯;另一方面,如果之后需要換一個(gè)端口那就需要改動(dòng)非常多的地方。所以這時(shí)就需要在上面做個(gè)定義,把端口號(hào)統(tǒng)一替換一個(gè)名字,這樣無論是語義還是端口的修改都會(huì)非常方便。
????????給端口號(hào)換一個(gè)名字有很多方法都能實(shí)現(xiàn)功能,一種簡(jiǎn)單的替換方法就是宏定義,
?
#define SCL_PORT ? ? ? GPIOB ? ?
#define SCL_PIN ? ? ? ?GPIO_Pin_10
????????之后如果想釋放SCL,就調(diào)用SetBits函數(shù)將GPIOB替換為SCL_PORT;將GPIO_Pin_10替換為SCL_PIN。
GPIO_SetBits(GPIOB,GPIO_Pin_10);
GPIO_SetBits(SCL_PORT,SCL_PIN);
????????這樣語義比較明確,而且修改引腳的時(shí)候直接在上面修改一下宏定義,下面所有引用宏定義的地方都會(huì)自動(dòng)更改。但是這樣宏定義的方法如果換到一個(gè)主頻很高的單片機(jī)中,需要對(duì)軟件時(shí)序進(jìn)行延時(shí)操作的時(shí)候,也不太方便進(jìn)一步修改。所以這里也可以直接一點(diǎn),定義函數(shù)對(duì)操作端口的庫函數(shù)進(jìn)行封裝,這樣既容易理解,又方便加軟件延時(shí)。
定義函數(shù)對(duì)操作端口的庫函數(shù)進(jìn)行封裝代碼示例:
void MyI2C_W_SCL(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);
}
????????MyI2C_W_SCL這個(gè)W代表寫的意思,函數(shù)里調(diào)用GPIO_WriteBit函數(shù),第三個(gè)參數(shù)給BitValue強(qiáng)轉(zhuǎn)為BitAction類型。
????????這樣套一個(gè)函數(shù)替換之后,后面再調(diào)用MyI2C_W_SCL函數(shù),參數(shù)給1或0,就可以釋放或拉低SCL了。
????????如果要把這個(gè)程序移植到別的單片機(jī),就可以把這個(gè)函數(shù)里的操作替換為其他單片機(jī)對(duì)應(yīng)的操作。
比如SCL是51單片機(jī)的P10口,就可以把
GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);
上面這句替換為下面這句
P10=BitValue;
????????另外如果單片機(jī)主頻比較快,函數(shù)里也非常方便加一些延時(shí),比如這里要求每次操作引腳之后都要延時(shí)10us,就可以在引腳操作之后調(diào)用延時(shí)函數(shù)進(jìn)行引腳延時(shí)操作了。I2C可以慢一些,多慢都行,但是快的話還是要看一下手冊(cè)里對(duì)時(shí)序時(shí)間的要求。
void MyI2C_W_SCL(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);Delay_us(10);
}
同理封裝一下SDA:
//SDA封裝
void MyI2C_W_SDA(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);Delay_us(10);
}
????????另外還要再來個(gè)讀SDA的函數(shù),因?yàn)镾TM32庫函數(shù)中,讀和寫不是同一個(gè)寄存器
uint8_t MyI2C_R_SDA(void)
{uint8_t BitValue;BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);Delay_us(10);return BitValue;
}
? ? ? ? 函數(shù)MyI2C_R_SDA中R代表讀的意思,讀出SDA之后也延時(shí)10us,返回讀到SDA線的電平。
? ? ? ? 有了以上三個(gè)函數(shù)的封裝就實(shí)現(xiàn)了函數(shù)名稱、端口號(hào)的替換。同時(shí)也可以很方便地修改時(shí)序的延時(shí)。當(dāng)需要替換端口時(shí),或者把這個(gè)程序移植到別的單片機(jī)中時(shí)就只需要對(duì)這前4個(gè)函數(shù)里的操作對(duì)應(yīng)更改,后面的函數(shù)都調(diào)用這里封裝的新名稱進(jìn)行操作,這樣在移植的時(shí)候后面的部分就不需要再進(jìn)行修改了。
? ? ? ? 以上關(guān)于引腳操作的封裝和改名就完成了。
3.2 起始條件執(zhí)行邏輯
????????在起始條件里需要先把SCL和SDA都釋放,也就是都輸出1.然后先拉低SDA,再拉低SCL。
代碼示例:
void MyI2C_Start(void)
{MyI2C_W_SDA(1);MyI2C_W_SCL(1);MyI2C_W_SDA(0);MyI2C_W_SCL(0);
}
????????代碼中需注意,最好將釋放SDA的放在最前面,這樣更符合起始條件的波形,這樣保險(xiǎn)一些。
????????如果起始條件之前的SCL和SDA已經(jīng)是高電平了,那不管先釋放哪一個(gè)都是一樣的效果。但是看下圖中Sr這里,Start還要兼容這里的重復(fù)起始條件Sr,Sr最開始SCL是低電平,SDA電平不敢確定。所以保險(xiǎn)起見,趁著SCL是低電平,先釋放SDA;再釋放SCL。這時(shí)SDA和SCL都是高電平。
????????然后再拉低SDA,拉低SCL。這樣Start就可以兼容起始條件和重復(fù)起始條件了。
?
3.3 終止條件執(zhí)行邏輯
? ? ? ? 當(dāng)Stop開始,如果SDA和SCL都已經(jīng)是低電平了,那就先釋放SCL,再釋放SDA就行了。但是在時(shí)序單元開始時(shí),SDA并不一定是低電平。所以為了確保之后釋放SDA能產(chǎn)生上升沿,要在時(shí)序單元開始時(shí),先拉低SDA,然后再釋放SCL、釋放SDA。
????????所以在程序里Stop的執(zhí)行邏輯是:先拉低SDA,再釋放SCL,再釋放SDA。
void MyI2C_Stop(void)
{MyI2C_W_SDA(0);MyI2C_W_SCL(1);MyI2C_W_SDA(1);
}
? ? ? ? 終止條件后,SCL和SDA都回歸到高電平。
3.4 發(fā)送一個(gè)字節(jié)
? ? ? ? 函數(shù)的參數(shù)是要發(fā)送的一個(gè)字節(jié)。發(fā)送一個(gè)字節(jié)時(shí)序開始時(shí),SCL是低電平。實(shí)際上除了終止條件,SCL以高電平結(jié)束,所有的單元都會(huì)保證SCL以低電平結(jié)束,這樣方便各個(gè)單元的拼接。
????????
????????上圖所示,SCL低電平,變換數(shù)據(jù);SCL高電平,保持?jǐn)?shù)據(jù)穩(wěn)定。由于是高位先行,所以變換數(shù)據(jù)的時(shí)候按照先放最高位,再放次高位,最后最低位,這樣的順序依次把一個(gè)字節(jié)的每一位放在SDA線上。每放完一位后,執(zhí)行釋放SCL,拉低SCL的操作,驅(qū)動(dòng)時(shí)鐘運(yùn)轉(zhuǎn)。
????????在程序中的操作就是,首先趁SCL低電平,先把Byte的最高位放在SDA線上,寫SDA,寫1還是寫0取決于Byte的最高位。這里需要取出Byte的最高位,可以用 (Byte & 0x80) ,這是一個(gè)單片機(jī)中非常常見的操作。就是用按位與的方式取出數(shù)據(jù)的某一位或某幾位。
按位與取出數(shù)據(jù)某一位的運(yùn)行邏輯:
- Byte可以是任意的數(shù)據(jù)? ?xxxx xxxx
- 0x80就是? ? ? ? ? ? ? ? ? ? ? ?1000 0000
- Byte與0x80按位與結(jié)果? x000 0000
低7位因?yàn)楹?相與,所以結(jié)果不受Byte數(shù)據(jù)的影響,始終是0;
最高位和1相與,所以結(jié)果取決于Byte的最高位。
- 如果Byte的最高位是1,結(jié)果就是1000 0000,也就是0x80;
- 如果Byte最高位是0,結(jié)果就是0000 0000,也就是0x00.
這就相當(dāng)于把Byte的最高位取出來了。但是注意,Byte & 0x80 這個(gè)式子計(jì)算結(jié)果是0x80或0x00,而不是1或0.不過上文函數(shù)將參數(shù)BitValue強(qiáng)轉(zhuǎn)為BitAction類型,就是非0即1,所以即使傳入0x80也相當(dāng)于傳入了1,代碼中可以直接寫 (Byte & 0x80)。
????????上面方法步驟將最高位數(shù)據(jù)放好后,再釋放SCL,再拉低SCL,驅(qū)動(dòng)時(shí)鐘走一個(gè)脈沖。
MyI2C_W_SDA(Byte & 0x80);MyI2C_W_SCL(1);MyI2C_W_SCL(0);
????????當(dāng)釋放SCL之后,從機(jī)就會(huì)立刻把放好在SDA的數(shù)據(jù)讀走,再拉低SCL,然后就可以放下一個(gè)數(shù)據(jù)了,下一位是次高位。
?? ?MyI2C_W_SDA(Byte & 0x40);MyI2C_W_SCL(1);MyI2C_W_SCL(0);?? ?
????????寫SDA,數(shù)據(jù)與0x40,取出次高位,再驅(qū)動(dòng)SCL,來一個(gè)時(shí)鐘。
????????之后繼續(xù),寫SDA,數(shù)據(jù)與0x20,取出再下一位,再驅(qū)動(dòng)SCL,來一個(gè)時(shí)鐘。
?? ?MyI2C_W_SDA(Byte & 0x20);MyI2C_W_SCL(1);MyI2C_W_SCL(0);??
? ? ? ? 這樣來8次這個(gè)操作,就可以寫入一個(gè)字節(jié)。不過可以套個(gè)for循環(huán),循環(huán)8次減少代碼量。
void MyI2C_SendByte(uint8_t Byte);
{uint8_t i;for(i=0;i<8;i++){MyI2C_W_SDA(Byte & (0x80 >>i));MyI2C_W_SCL(1);MyI2C_W_SCL(0);}
}
定義一個(gè)迭代變量i,循環(huán)八次,然后把上述重復(fù)的操作單元放在里面。
第一次循環(huán) i=0,需要 (Byte & 0x80)?
第二次循環(huán) i=1,需要 (Byte & 0x40)?
第三次循環(huán) i=2,需要 (Byte & 0x20)?
...
所以這里的通式就是(Byte & (0x80 >>i)) 注意要加個(gè)括號(hào),確保優(yōu)先級(jí)。
3.5 接收一個(gè)字節(jié)
? ? ? ? 接收一個(gè)字節(jié)時(shí)序開始時(shí),SCL低電平,此時(shí)從機(jī)需要把數(shù)據(jù)放到SDA上,為了防止主機(jī)干擾從機(jī)寫入數(shù)據(jù),主機(jī)需要先釋放SDA,釋放SDA也相當(dāng)于切換為輸入模式。那在SCL低電平時(shí),從機(jī)會(huì)把數(shù)據(jù)放到SDA。
- 如果從機(jī)想發(fā)1,就釋放SDA;
- 如果從機(jī)想發(fā)0,就拉低SDA;
????????然后主機(jī)釋放SCL,在SCL高電平期間,讀取SDA,再拉低SCL。SCL低電平期間從機(jī)就會(huì)把下一位數(shù)據(jù)放到SDA上。這樣重復(fù)八次主機(jī)就能得到一個(gè)字節(jié)了。
????????在這里可以發(fā)現(xiàn),SCL低電平變換數(shù)據(jù),高電平讀取數(shù)據(jù)。實(shí)際上就是一種讀寫分離的設(shè)計(jì):
- 低電平時(shí)間定義為寫的時(shí)間;
- 高電平時(shí)間定義為讀的時(shí)間。
????????那在SCL高電平期間,如果非要?jiǎng)覵DA來破壞讀寫規(guī)則的話,那這個(gè)信號(hào)就是起始條件和終止條件。SCL高電平時(shí),SDA下降沿為起始條件,SDA上升沿為終止條件。這個(gè)設(shè)計(jì)也保證了起始條件和終止條件的特異性,能夠在連續(xù)不斷的波形中快速地定位起始和終止。因?yàn)槠鹗冀K止與數(shù)據(jù)傳輸?shù)牟ㄐ斡斜举|(zhì)區(qū)別:
- 數(shù)據(jù)傳輸SCL高電平不許動(dòng)SDA;
- 起始終止SCL高電平必須動(dòng)SDA。
這就是這個(gè)設(shè)計(jì)的巧妙之處。
進(jìn)接收一個(gè)字節(jié)的時(shí)序之后,SCL是低電平,主機(jī)釋放SDA。從機(jī)把數(shù)據(jù)放到SDA時(shí),主機(jī)釋放SCL,SCL高電平時(shí),主機(jī)就能讀取數(shù)據(jù)了。
uint8_t MyI2C_ReceiveByte(void)
{uint8_t Byte = 0x00;MyI2C_W_SDA(1);MyI2C_W_SCL(1);if(MyI2C_R_SDA() == 1){Byte |= 0x80;}MyI2C_W_SCL(0);}
????????讀取數(shù)據(jù)用MyI2C_R_SDA函數(shù),套個(gè)if,如果讀SDA為1,if成立,就知道接收這一位為1了。先定義一個(gè)數(shù)據(jù)Byte,給初始值0x00.
- 如果第一次讀SDA為1,就Byte |= 0x80; 把Byte最高位置1;
- 如果第一次讀SDA為0,if條件不成立,Byte默認(rèn)為0x00,就相當(dāng)于寫如0了。
????????讀取一位之后,再把SCL拉低。這時(shí)從機(jī)就會(huì)把下一位數(shù)據(jù)放到SDA上。再執(zhí)行下方代碼相同的流程8次就能接收一個(gè)字節(jié)了。
MyI2C_W_SCL(1);if(MyI2C_R_SDA() == 1){Byte |= 0x80;}MyI2C_W_SCL(0);
????????可以用個(gè)for循環(huán),把上方代碼放進(jìn)去,循環(huán)8次,依次從高位到低位進(jìn)行判斷。所以在寫個(gè)和發(fā)送一個(gè)字節(jié)一樣的移位操作。就可以接收一個(gè)字節(jié)了。最后return Byte; 把接收的Byte返回去。
3.5 發(fā)送應(yīng)答&接收應(yīng)答
????????發(fā)送應(yīng)答和接收應(yīng)答其實(shí)就是發(fā)送一個(gè)字節(jié)和接收一個(gè)字節(jié)的簡(jiǎn)化版:
- 發(fā)送一個(gè)字節(jié)是發(fā)8位,發(fā)送應(yīng)答是發(fā)1位;
- 接收一個(gè)字節(jié)是收8位,接收應(yīng)答是收1位。
所以程序這里可以參照發(fā)送一個(gè)字節(jié)和接收一個(gè)字節(jié)來修改。
3.5.1 發(fā)送應(yīng)答
? ? ? ??將發(fā)送一個(gè)字節(jié)的代碼中的for循環(huán)去掉,修改一下。
void MyI2C_SendAck(uint8_t AckBit)
{MyI2C_W_SDA(AckBit);MyI2C_W_SCL(1);MyI2C_W_SCL(0);
}
? ? ? ? 現(xiàn)在的邏輯是:函數(shù)進(jìn)來時(shí),SCL低電平。
- 主機(jī)把AckBit放到SDA上。
- SCL高電平,從機(jī)讀取應(yīng)答。
- SCL低電平,進(jìn)入下一個(gè)時(shí)序單元。
3.5.2 接收應(yīng)答
????????將接收一個(gè)字節(jié)的代碼中的for循環(huán)去掉,修改一下。讀SDA時(shí),直接把讀到的值,賦值給AckBit就行了。最后返回讀AckBit。
uint8_t MyI2C_ReceiveAck(void)
{uint8_t AckBit;MyI2C_W_SDA(1);MyI2C_W_SCL(1);AckBit = MyI2C_R_SDA();MyI2C_W_SCL(0);return AckBit;
}
????????現(xiàn)在的邏輯是:函數(shù)進(jìn)來時(shí),SCL低電平。
- 主機(jī)釋放SDA,防止干擾從機(jī),同時(shí)從機(jī)把應(yīng)答位放在SDA上。
- SCL高電平,主機(jī)讀取應(yīng)答位。
- SCL低電平,進(jìn)入下一個(gè)時(shí)序單元。
代碼疑問:
? ? ? ? 1.程序里主機(jī)先把SDA置1了,然后再讀取SDA,應(yīng)答位就肯定是1嗎?
可以從兩點(diǎn)分析:
- 第一,I2C的引腳都是開漏輸出+弱上拉的配置,主機(jī)輸出1并不是強(qiáng)置SDA為高電平,而是釋放SDA。
- 第二,I2C是在進(jìn)行通信,主機(jī)釋放了SDA,那從機(jī)如果在的話,從機(jī)是有義務(wù)把SDA再拉低的。
所以即使主機(jī)在前面把SDA置1了,之后再讀取SDA,讀到的值也可能是0.
- 讀到0代表從機(jī)給了應(yīng)答。
- 讀到1代表從機(jī)沒給應(yīng)答。
? ? ? ? 2.接收一個(gè)字節(jié)的代碼里不斷讀取SDA,但是for循環(huán)中又沒寫過SDA,那SDA讀出來應(yīng)該始終是一個(gè)值嗎?
? ? ? ? I2C進(jìn)行通信是有從機(jī)的,當(dāng)主機(jī)不斷驅(qū)動(dòng)SCL時(shí)鐘時(shí),從機(jī)就有義務(wù)去改變SDA的電平。所以主機(jī)每次循環(huán)讀取SDA的時(shí)候,這個(gè)讀取到的數(shù)據(jù)是從機(jī)控制的,這個(gè)讀取到的數(shù)據(jù)也正是從機(jī)想要給我們發(fā)送的數(shù)據(jù)。這也就是這個(gè)時(shí)序叫做接收一個(gè)字節(jié)。
? ? ? ? 通信是有時(shí)序的,有些引腳的電平之前讀和之后讀,讀的值就是不一樣的。
四、總代碼示例
.c代碼示例:
#include "stm32f10x.h" // Device header
#include "Delay.h" //CL封裝
void MyI2C_W_SCL(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);Delay_us(10);
}//SDA封裝
void MyI2C_W_SDA(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);Delay_us(10);
}//讀SDA的函數(shù)
uint8_t MyI2C_R_SDA(void)
{uint8_t BitValue;BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);Delay_us(10);return BitValue;
}void MyI2C_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//開啟時(shí)鐘GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode= GPIO_Mode_Out_OD;GPIO_InitStruct.GPIO_Pin= GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);GPIO_SetBits(GPIOB,GPIO_Pin_10 | GPIO_Pin_11);//把GPIOB的PB10和PB11都置高電平}//六個(gè)時(shí)序基本單元//起始條件
void MyI2C_Start(void)
{MyI2C_W_SDA(1);MyI2C_W_SCL(1);MyI2C_W_SDA(0);MyI2C_W_SCL(0);
}//終止條件
void MyI2C_Stop(void)
{MyI2C_W_SDA(0);MyI2C_W_SCL(1);MyI2C_W_SDA(1);
}//發(fā)送一個(gè)字節(jié)
void MyI2C_SendByte(uint8_t Byte)
{uint8_t i;for(i=0;i<8;i++){MyI2C_W_SDA(Byte & (0x80 >>i));MyI2C_W_SCL(1);MyI2C_W_SCL(0);}
}//接收一個(gè)字節(jié)
uint8_t MyI2C_ReceiveByte(void)
{uint8_t i, Byte = 0x00;MyI2C_W_SDA(1);for(i = 0 ; i < 8 ; i++){MyI2C_W_SCL(1);if(MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}MyI2C_W_SCL(0);}return Byte;
}//發(fā)送應(yīng)答
void MyI2C_SendAck(uint8_t AckBit)
{MyI2C_W_SDA(AckBit);MyI2C_W_SCL(1);MyI2C_W_SCL(0);
}//接收應(yīng)答
uint8_t MyI2C_ReceiveAck(void)
{uint8_t AckBit;MyI2C_W_SDA(1);MyI2C_W_SCL(1);AckBit = MyI2C_R_SDA();MyI2C_W_SCL(0);return AckBit;
}
.h代碼示例:
#ifndef __MYI2C_H__
#define __MYI2C_H__void MyI2C_Init(void);//起始條件
void MyI2C_Start(void);//終止條件
void MyI2C_Stop(void);//發(fā)送一個(gè)字節(jié)
void MyI2C_SendByte(uint8_t Byte);//接收一個(gè)字節(jié)
uint8_t MyI2C_ReceiveByte(void);//發(fā)送應(yīng)答
void MyI2C_SendAck(uint8_t AckBit);//接收應(yīng)答
uint8_t MyI2C_ReceiveAck(void);#endif
總結(jié)
????????以上就是今天要講的內(nèi)容,本文僅僅簡(jiǎn)單介紹了軟件I2C讀寫代碼。其中有使用的兩個(gè)引腳、I2C配置、時(shí)序的高低電平等協(xié)議相關(guān)內(nèi)容的代碼配置細(xì)節(jié)。