1、簡(jiǎn)介
之所以叫“I2C硬件控制方式”是與“軟件控制方式”相對(duì)。I2C軟件控制,就是寫程序直接操作兩個(gè)GPIO引腳,分別作為時(shí)鐘線SCL和數(shù)據(jù)線SDA,按照I2C協(xié)議的時(shí)序要求,操作GPIO輸入、輸出、高電平、低電平。
聽著就很復(fù)雜,好在STM32中有I2C的硬件實(shí)現(xiàn),即通過簡(jiǎn)單的操作寄存器即可實(shí)現(xiàn)收發(fā)數(shù)據(jù)。
2、手冊(cè)
2.1 寄存器功能框圖
2.2 I2C引腳
STM32F407ZGT6中有3個(gè)I2C總線,對(duì)應(yīng)的引腳如下圖所示。
其中I2C1默認(rèn)引腳是PB6、PB7,可以重映射到PB8、PB9上。
2.3 寄存器
寄存器地址:
3、代碼詳解
I2C基本編程步驟:初始化時(shí)鐘、配置引腳、起始信號(hào)、讀、寫、終止信號(hào)
3.1 I2C初始化
3.1.1 初始化時(shí)鐘
I2C在APB1總線上,并且I2C1的引腳位PB6、PB7,因此使能使能GPIOB時(shí)鐘,以及I2C的時(shí)鐘
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
3.1.2 配置引腳位開漏輸出
static void I2C_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
#a)使能與 I2C 有關(guān)的時(shí)鐘
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
#b)配置SCL和SDA引腳位開漏輸出GPIO_Mode_AF_OD
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
3.1.3 設(shè)置I2C工作模式
設(shè)置工作模式、占空比、地址7位或10位、通信速率等
工作模式有三種:I2C、SMBusDevice、SMBusHost。
SMBus (System Management Bus,系統(tǒng)管理總線)和I2C類似,但是在時(shí)序特性上有一些差異:
首先,SMBus需要一定數(shù)據(jù)保持時(shí)間,而 I2C總線則是從內(nèi)部延長(zhǎng)數(shù)據(jù)保持時(shí)間。
SMBus具有超時(shí)功能,因此當(dāng)SCL太低而超過35 ms時(shí),從器件將復(fù)位正在進(jìn)行的通信。相反,I2C采用硬件復(fù)位。
SMBus具有一種警報(bào)響應(yīng)地址(ARA),因此當(dāng)從器件產(chǎn)生一個(gè)中斷時(shí),
它不會(huì)馬上清除中斷,而是一直保持到其收到一個(gè)由主器件發(fā)送的含有其地址的ARA為止。
SMBus只工作在從10kHz到最高100kHz。最低工作頻率10kHz是由SMBus超時(shí)功能決定的。
static void I2C_Mode_Configu(void)
{
I2C_InitTypeDef I2C_InitStructure;
#a)設(shè)置位I2C模式
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
#b)設(shè)置高低電平占空比,這個(gè)不用糾結(jié),可以隨意設(shè)置,一般設(shè)備兼容性都沒問題
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
#c)設(shè)置自己的地址為7位
I2C_InitStructure.I2C_OwnAddress1 =I2Cx_OWN_ADDRESS7;
#d)默認(rèn)使能ACK,當(dāng)需要發(fā)送NACK時(shí),再重新設(shè)置
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;
#e)設(shè)置I2C的尋址地址為7位
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
#f)設(shè)置時(shí)鐘為:400000
I2C_InitStructure.I2C_ClockSpeed = 400000;
#g)初始化I2C1:(APB1PERIPH_BASE + 0x5400)
I2C_Init(I2C1, &I2C_InitStructure);
#h)使能 I2C1
I2C_Cmd(I2C1, ENABLE);
}
3.2 寫操作
uint32_t I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr)
{
#a)發(fā)送起始信號(hào)“S”
I2C_GenerateSTART(I2C1, ENABLE);
I2CTimeout = I2CT_FLAG_TIMEOUT;
#b)等待起始信號(hào)發(fā)送成功:測(cè)試 EV5 即可
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0);
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
#c)發(fā)送從地址
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
#d)等待從地址發(fā)送成功:測(cè)試 EV6 即可
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1);
}
#e)發(fā)送需要寫入EEPROM的地址(本質(zhì)也是數(shù)據(jù))
I2C_SendData(I2C1, WriteAddr);
I2CTimeout = I2CT_FLAG_TIMEOUT;
#f)等待數(shù)據(jù)發(fā)送成功:測(cè)試 EV8 即可
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2);
}
#g)發(fā)送需要寫入的數(shù)據(jù)
I2C_SendData(I2C1, *pBuffer);
I2CTimeout = I2CT_FLAG_TIMEOUT;
#h)等待數(shù)據(jù)發(fā)送成功:測(cè)試 EV8 即可
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
}
#i)發(fā)送停止信號(hào)“P”
I2C_GenerateSTOP(I2C1, ENABLE);
return 1;
}
3.3 讀操作
uint32_t I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)
{
I2CTimeout = I2CT_LONG_TIMEOUT;
#a)等待是否可以讀
//*((u8 *)0x4001080c) |=0x80;
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(9);
}
#b)發(fā)送起始信號(hào)“S”
I2C_GenerateSTART(I2C1, ENABLE);
//*((u8 *)0x4001080c) &=~0x80;
I2CTimeout = I2CT_FLAG_TIMEOUT;
#c)等待起始信號(hào)發(fā)送成功:測(cè)試 EV5 即可
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10);
}
#d)發(fā)送從地址
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
I2CTimeout = I2CT_FLAG_TIMEOUT;
#e)等待從地址發(fā)送成功:測(cè)試 EV6 即可
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(11);
}
#f)EV6后執(zhí)行EV6_1:清除EV6 (再次設(shè)置PE位)
I2C_Cmd(I2C1, ENABLE);
#g)發(fā)送需要讀取的地址
I2C_SendData(I2C1, ReadAddr);
I2CTimeout = I2CT_FLAG_TIMEOUT;
#h)等待數(shù)據(jù)發(fā)送成功:測(cè)試 EV8 即可
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(12);
}
#i)復(fù)合命令:再次發(fā)送起始信號(hào)“S”
I2C_GenerateSTART(I2C1, ENABLE);
I2CTimeout = I2CT_FLAG_TIMEOUT;
#j)等待起始信號(hào)發(fā)送成功:測(cè)試 EV5 即可
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(13);
}
#k)發(fā)送從地址
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Receiver);
I2CTimeout = I2CT_FLAG_TIMEOUT;
#l)等待從地址發(fā)送成功:測(cè)試 EV6 即可
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(14);
}
#m)循環(huán)讀
while(NumByteToRead)
{
if(NumByteToRead == 1)
{
#n)讀取完畢,發(fā)送NACK
I2C_AcknowledgeConfig(I2C1, DISABLE);
#o)發(fā)送停止信號(hào)“P”
I2C_GenerateSTOP(I2C1, ENABLE);
}
#p)每次讀取一個(gè)字節(jié)前,先測(cè)試 EV7
I2CTimeout = I2CT_LONG_TIMEOUT;
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)==0)
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
}
{
#q)讀取一字節(jié)
*pBuffer = I2C_ReceiveData(I2C1);
pBuffer++;
NumByteToRead--;
}
}
#r)為下一次讀取做準(zhǔn)備,即將ACK設(shè)置為1
I2C_AcknowledgeConfig(I2C1, ENABLE);
return 1;
}
3.4 待機(jī)狀態(tài)
向EEPROM寫入數(shù)據(jù)后,調(diào)用這個(gè)函數(shù)等待EEPROM 內(nèi)部擦寫完畢。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-400824.html
這個(gè)函數(shù)主要實(shí)現(xiàn)是向EEPROM 發(fā)送它設(shè)備地址,檢測(cè)EEPROM 的響應(yīng),若EEPROM 接收到地址后返回應(yīng)答信號(hào),則表示EEPROM 已經(jīng)準(zhǔn)備好,可以開始下一次通訊。函數(shù)中檢測(cè)響應(yīng)是通過讀取STM32 的SR1 寄存器的ADDR 位及AF 位來(lái)實(shí)現(xiàn)的,當(dāng)I2C 設(shè)備響應(yīng)了地址的時(shí)候,ADDR 會(huì)置1,若應(yīng)答失敗,AF 位會(huì)置1。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-400824.html
void I2C_EE_WaitEepromStandbyState(void)
{
vu16 SR1_Tmp = 0;
do
{
#a)發(fā)送起始信號(hào)“S”
I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
#b)讀取I2C1 SR1 寄存器
SR1_Tmp = I2C_ReadRegister(EEPROM_I2Cx, I2C_Register_SR1);
#c)發(fā)送從地址
I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);
}while(!(I2C_ReadRegister(EEPROM_I2Cx, I2C_Register_SR1) & 0x0002));
#d)清除AF信號(hào)
I2C_ClearFlag(EEPROM_I2Cx, I2C_FLAG_AF);
#e)發(fā)送停止信號(hào)“P”
I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
}
到了這里,關(guān)于【STM32】入門(七):I2C硬件控制方式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!