I2C簡介
I2C總線是一個兩線串行接口,包含串行數據線(SDA)與串行時鐘線(SCL),能夠在連接到總線的器件間傳遞信息,每一個連接總線的設備都有獨立的地址,主機可以通過該地址選擇連接總線的設備并與之通信。I2C通過對SCL和SDA線高低電平時序的控制,來產生I2C總線協議所需要的信號,從而進行數據傳輸,微控制器可通過I2C總線接口實現芯片間串行互聯。
I2C的功能框圖如圖1所示,當I2C采用主模式進行數據傳輸時,主機先發送從機設備地址與讀寫位數據,在從機地址匹配時可進行數據傳輸;I2C采用從模式時,從設備等待接收由主機發送來的地址數據,地址匹配時可進行數據傳輸。
主機與從機
主機是初始化總線的數據傳輸并產生允許傳輸的時鐘信號的器件。此時,任何被尋址的器件都被認為是從機,在主模式下只支持一個主機。
設備地址
在進行數據傳輸前需要發送從設備地址,只有當主機的發送設備地址與從機的設備地址匹配時才能進行數據傳輸。總線的每個器件都有一個唯一的地址識別,而且都可以作為一個發送或接收器。
I2C信號
起始信號
當總線處于空閑狀態時,SCL和SDA同時被外部上拉電阻拉為高電平。當主機啟動數據傳輸時,必須先產生一個起始信號。在SCL線是高電平時, SDA線從高電平向低電平切換表示起始信號,如圖2所示。在起始信號產生后,總線處于忙碌狀態,除當前數據傳輸的主從設備外,其他I2C器件無法訪問總線。
停止信號
停止信號如圖2所示,當主機結束傳輸時要發送停止信號,在SCL線為高電平、SDA線由低電平向高電平切換時,產生停止信號,從而釋放總線,總線再次處于空閑狀態。
應答信號
I2C具有完善的應答機制,當主機發送完成地址與讀寫位或者主機發送一個字節的數據到從機上時,從機會回應一個應答信號(ACK),即在第9個SCL時鐘周期時拉低SDA,如圖3所示。
非應答信號
如圖3所示,當第9個SCL時鐘周期時拉高SDA,表示為非應答信號,主機或從機將產生一個停止信號中止傳輸。
讀寫位
在傳輸設備地址時會跟隨1bit的讀寫位數據,該讀寫位決定當前數據傳輸方向。如圖4所示,起始信號后發送從設備地址及讀寫位數據,當讀寫位為1時,執行讀操作;讀寫位為0時,執行寫操作。
I2C配置
I2C的配置包括傳輸速率、從設備地址、數據傳輸方向及主從模式選擇的配置,從而進行數據傳輸。
工作速率
I2C存在兩種工作速率模式:標準模式(數據傳輸速率為0 ~ 100Kbps),快速模式(數據傳輸速率最大為400Kbps);可通過I2C控制寄存器(I2C_CR)的SPEED位進行控制,由于I2C從機速率跟隨主機速率,因此只需在I2C為主模式下配置工作速率,如圖5所示。
地址格式
I2C有兩種地址格式:7位地址格式和10位地址格式。
7位地址
如圖6所示,在起始條件(S)后發送的一個字節的前7位(A6 ~ A0)為從機地址,最低位(R/W)是數據方向位,當R/W位為0,表示主機寫數據到從機,R/W位為1表示主機從從機讀數據。
10位地址
在10位的地址格式中,發送2個字節來傳輸10位地址。發送的第一個字節的起始位后的5位(bit7:3)用于告示從機接下來是10位的傳輸,第一個字節的后兩位(bit2:1)是從機地址的bit9:8,最低位(bit 0)是數據方向位(R/W)。傳輸的第二個字節為10位地址的低八位,如圖7所示。
寫操作
I2C進行寫操作時,先通過I2C目標地址寄存器(I2C_TAR)配置從設備地址,再配置讀寫位,令控制數據命令寄存器(I2C_DR)的CMD位為0,所需傳輸數據放入I2C_DR寄存器的DR位,進行寫操作。
讀操作
I2C進行讀操作時,先由主機寫從設備地址及設備內寄存器地址,當從設備地址及設備內寄存器地址發送完成后,將控制數據命令寄存器(I2C_DR)的CMD位為1,進行讀操作,讀出設備內寄存器地址中的數值。
主模式
I2C主模式配置需在I2C未使能狀態下進行,將I2C使能寄存器(I2C_IMR)的ENABLE位置0。通過配置控制寄存器(I2C_CR)的SPEED位選擇速率模式,MASTER10位選擇地址格式,DISSLAVE位為1使從模式禁止,MASTER位為1選擇主模式。向目標地址寄存器(I2C_TAR)寫入從設備地址,I2C_IMR寄存器的ENABLE位置1,使能I2C。
I2C 接口支持讀寫的動態切換,當發送數據時,寫數據到I2C的RX/TX數據緩沖及命令寄存器(DR)的低字節中,配置CMD位為0產生寫操作。接下來的讀命令,需要確保CMD位為1。如果發送FIFO為空,I2C模塊拉低SCL直到下個命令寫入到發送FIFO中。
從模式
I2C做從機時,等待其他主機的訪問,只有其他主機發送了I2C從機對應的設備地址,才能進行數據傳輸。配置I2C為從模式,將I2C使能寄存器(I2C_IMR)的ENABLE位置0,禁止I2C使能,將I2C自身從地址寫入從機地址寄存器(I2C_SAR),配置控制寄存器(I2C_CR)的SLAVE10選擇尋址格式,DISSLAVE位為0,選擇從模式,MASTER位為0,禁止主模式;I2C_IMR寄存器的ENABLE位置1,使能I2C。
當其他主機發送地址與 SAR 寄存器中的從機地址匹配,I2C接口響應發送的地址,并識別傳輸方向;當I2C產生讀請求,從機將數據發送至主機;當主機為寫操作時,I2C從機接收到主機的數據并將數據存儲在接收緩沖中,軟件通過讀DR寄存器中的bit7:0來獲得接收到的數據。
實驗
本實驗使用輪詢進行數據的寫操作與讀操作,初始化配置I2C為主模式,從設備地址為0x50,速率為100KHz,使用7位地址格式。I2C初始化后,按下一次任意按鍵,進行8字節數據的寫操作,其中第一字節寫入數據設置為設備內的寄存器地址,串口顯示寫入數據;寫操作完成后,再次按下任意按鍵,進行7位數據的讀操作,從設備的寄存器地址中讀出相關數據。
啟用I2C外設時鐘 enable_clock()
實驗使用I2C1,串口打印輸出結果,串口使用引腳屬于GPIOA組,I2C使用引腳屬于GPIOB組,因此需要啟用I2C1、UART1、GPIOA及GPIOB的外設時鐘。
voidenable_clock()
{
/*EnableI2C1clock.*/
RCC->APB1ENR|=RCC_APB1_PERIPH_I2C1;
/*EnableGPIOAclock.*/
RCC->AHB1ENR|=RCC_AHB1_PERIPH_GPIOA;
/*EnableGPIOBclock.*/
RCC->AHB1ENR|=RCC_AHB1_PERIPH_GPIOB;
/*EnableUART1clock.*/
RCC->APB2ENR|=RCC_APB2_PERIPH_UART1;
}
配置引腳 pin_init()
配置I2C1引腳,I2C1_SCL為PB6引腳,I2C1_SDA為PB7引腳,復用通道為AF1;由于實驗現象通過串口顯示,故配置UART的TX(PA9)與RX(PA10)引腳。
voidpin_init()
{
/*PB6-I2C1_SCL.*/
GPIOB->CRL&=~GPIO_CRL_MODE6_MASK;
GPIOB->CRL&=~GPIO_CRL_CNF6_MASK;
GPIOB->CRL|=GPIO_CRL_MODE6(GPIO_Speed_50MHz);
GPIOB->CRL|=GPIO_CRL_CNF6(GPIO_PinMode_AF_OpenDrain);
GPIOB->AFRL&=~GPIO_AFRL_AFRY_MASK;
GPIOB->AFRL|=(GPIO_AF_1<
/*PB7-I2C1_SDA.*/
GPIOB->CRL&=~GPIO_CRL_MODE7_MASK;
GPIOB->CRL&=~GPIO_CRL_CNF7_MASK;
GPIOB->CRL|=GPIO_CRL_MODE7(GPIO_Speed_50MHz);
GPIOB->CRL |= GPIO_CRL_CNF7(GPIO_PinMode_AF_OpenDrain);
GPIOB->AFRL|=(GPIO_AF_1<
/*PA9-UART_TX.*/
GPIOA->CRH&=~GPIO_CRH_MODE9_MASK;
GPIOA->CRH&=~GPIO_CRH_CNF9_MASK;
GPIOA->CRH|=GPIO_CRH_MODE9(GPIO_Speed_50MHz);
GPIOA->CRH|=GPIO_CRH_CNF9(GPIO_PinMode_AF_PushPull);
GPIOA->AFRH|=(GPIO_AF_1<
/*PA10-UART_RX.*/
GPIOA->CRH&=~GPIO_CRH_MODE10_MASK;
GPIOA->CRH&=~GPIO_CRH_CNF10_MASK;
GPIOA->CRH|=GPIO_CRH_CNF10(GPIO_PinMode_In_Floating);
GPIOA->AFRH|=(GPIO_AF_1<
}
UART初始化 uart_init()
初始化UART,配置時鐘頻率、波特率、數據長度、停止位、傳輸模式及是否使用校驗。
voiduart_init()
{
/*Clearthecorrespondingbittobeused.*/
UART1->CCR&=~(UART_CCR_PEN_MASK|UART_CCR_PSEL_MASK|UART_CCR_SPB0_MASK|UART_CCR_SPB1_MASK|UART_CCR_CHAR_MASK);
UART1->GCR&=~(UART_GCR_AUTOFLOWEN_MASK|UART_GCR_RXEN_MASK|UART_GCR_TXEN_MASK);
/*WordLength.*/
UART1->CCR|=UART_CCR_CHAR_MASK;
/*XferMode.*/
UART1->GCR|=(UART_XferMode_RxTx<
/*Setupbaudrate,BOARD_DEBUG_UART_FREQ=48000000u,BOARD_DEBUG_UART_BAUDRATE=9600u.*/
UART1->BRR=(BOARD_DEBUG_UART_FREQ/BOARD_DEBUG_UART_BAUDRATE)/16u;
UART1->FRA=(BOARD_DEBUG_UART_FREQ/BOARD_DEBUG_UART_BAUDRATE)%16u;
/*EnableUART1.*/
UART1->GCR|=UART_GCR_UARTEN_MASK;
}
I2C初始化 i2c_init()
I2C通過操作配置寄存器(I2C_CR)實現初始化,配置I2C為主模式、速率為100KHz、7位地址格式、時鐘頻率為60000000u,設置從設備地址為0x50,使能I2C。
voidi2c_init()
{
I2C1->ENR&=~I2C_ENR_ENABLE_MASK;
uint32_ttmp=BOARD_I2C_FREQ/I2C_BaudRate_100K;/*BOARD_I2C_FREQ=60000000u,I2C_BaudRate_100K=100000u.*/
I2C1->SSHR=tmp/2u-12u;/*Configurehighlevelcountinnormalspeed.*/
I2C1->SSLR=tmp/2u-1u; /*Configurelowlevelcountinnormalspeed.*/
I2C1->CR=I2C_CR_SPEED(1u);
I2C1->CR&=~I2C_CR_MASTER10_MASK; /*Addressformat.*/
I2C1->CR|=I2C_CR_RESTART_MASK /*Generaterestartsignal.*/
|I2C_CR_DISSLAVE_MASK /*Disableslavemodule.*/
|I2C_CR_REPEN_MASK /*Enablesendingrestartcondition.*/
|I2C_CR_EMPINT_MASK /*Controltx_emptyinterruptgeneration.*/
|I2C_CR_MASTER_MASK; /*Enablemastermodule.*/
I2C1->RXTLR=0u; /*Configurethesendingreceivevalue.*/
I2C1->TXTLR=0u; /*Configurethesendingthresholdvalue.*/
/*Setuptargetaddress.*/
I2C1->TAR=I2C_TAR_ADDR(APP_I2C_TARGET_ADDR);/*APP_I2C_TARGET_ADDR=0x50.*/
I2C1->ENR|=I2C_ENR_ENABLE_MASK; /*EnableI2C.*/
}
I2C寫操作 i2c_write()
設置I2C發送數組i2c_tx_buf及發送數據長度i2c_tx_len,將數組i2c_tx_buf的第一個數值設置為寄存器地址,發送8字節數據。數據發送后讀取狀態寄存器(I2C_SR),等待發送緩沖空,再進行下一次發送;數據傳輸完成,操作使能寄存器的ABORT位為1,使I2C停止數據傳輸;讀清除TX_ABRT中斷寄存器(I2C_TXABRT),將TX FIFO從刷新/復位狀態中釋放。
voidi2c_write()
{
I2C1->DR=I2C_DR_DAT(i2c_tx_buf[0]);
while(0u==(I2C1->SR&I2C_STATUS_TX_EMPTY))
{
}
for(uint32_ti=1u;i
{
I2C1->DR=I2C_DR_DAT(i2c_tx_buf[i]);
while(0u==(I2C1->SR&I2C_STATUS_TX_EMPTY)) /*Waittotxfifoempty.*/
{
}
}
I2C1->ENR|=I2C_ENR_ABORT_MASK; /*Prepareforthestop.*/
I2C1->TXABRT; /*Readregistertoreleasetxfifo.*/
while(I2C1->SR&I2C_STATUS_ACTIVE) /*WaittoI2Cnotactive,whichmeansstopistakingeffect.*/
{
}
}
I2C讀操作 i2c_read()
設置I2C接收數組i2c_rx_buf及接收長度i2c_rx_len,在進行數據讀取前,需要發送從設備地址及寄存器地址,本實驗設置發送數組的第一個數據為寄存器地址,發送完成后再將I2C_DR寄存器的讀寫位(CMD)置1,讀取I2C_DR寄存器進行讀操作。讀操作結束后,操作使能寄存器的ABORT位為1,使I2C停止數據傳輸。
voidi2c_read()
{
I2C1->DR=I2C_DR_DAT(i2c_tx_buf[0]);
while(0u==(I2C1->SR&I2C_STATUS_TX_EMPTY))
{
}
/*Readdatafromtargetdevice.*/
for(uint32_ti=0u;i
{
I2C1->DR=I2C_DR_CMD_MASK; /*Swichread-writebittoread,preparetogetdata.*/
while(0u==(I2C1->SR&I2C_STATUS_RX_NOTEMPTY)) /*Waittorxfifonotempty.*/
{
}
i2c_rx_buf[i]=(uint8_t)I2C1->DR;
}
I2C1->ENR|=I2C_ENR_ABORT_MASK; /*Prepareforthestop.*/
I2C1->TXABRT; /*Readregistertoreleasetxfifo.*/
while(I2C1->SR&I2C_STATUS_ACTIVE) /*WaittoI2Cnotactive,whichmeansstopistakingeffect.*/
{
}
}
main()函數
main()函數結合上述操作,串口打印“i2c_master_basic example”,初始化I2C后,將發送數組設置為0到7,按下一次任意按鍵,進行數據發送,串口顯示發送數據;再次按下任意按鍵,進行數據讀取,從寄存器地址中讀出數值,設置寄存器地址設置為發送數據的首個數據,串口顯示接收數據,實驗結果如圖9所示。
intmain()
{
enable_clock();
pin_init();
uart_init();
printf("\r\ni2c_master_basicexample\r\n");
i2c_init();
for(uint32_ti=0u;i
{
i2c_tx_buf[i]=i;
}
while(1)
{
printf("pressanykeytowritei2c-eeprom.\r\n");
getchar();
i2c_write();
printf("writedata:");
for(uint32_ti=0u;i
{
printf("0x%02X,",i2c_tx_buf[i]);
}
printf("\r\n");
printf("pressanykeytoreadi2c-eeprom.\r\n");
getchar();
i2c_read();
printf("readdatafromdeviceregisteraddress:0x%02X\r\n",i2c_tx_buf[0u]);
printf("readdata:");
for(uint32_ti=0u;i
{
printf("0x%02X,",i2c_rx_buf[i]);
}
printf("\r\n");
}
-
mcu
+關注
關注
146文章
17317瀏覽量
352631 -
I2C
+關注
關注
28文章
1495瀏覽量
124547 -
靈動微
+關注
關注
4文章
174瀏覽量
22720 -
MM32
+關注
關注
1文章
106瀏覽量
807
發布評論請先 登錄
相關推薦
評論