1. 程式人生 > >STM32F103使用硬體i2c作為從機模式

STM32F103使用硬體i2c作為從機模式

一、簡單說明

本例子參考了ST官方歷程,官方歷程的連結如下

關於i2c的協議這裡就不做描述了

關於STM32 i2c的模式可以在中文資料手冊中檢視

手冊中已經描述,該模組預設工作在從模式,要想變為主模式,主要生產一個起始條件。(主模式的程式碼可以參考野火開發板的硬體i2c歷程,本例子中也是使用野火開發板硬體i2c作為主機的)

二、i2c從機的配置

    I2C_DeInit(I2C1);
    /* I2C1 configuration ------------------------------------------------------*/
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//模式
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 = I2C_SLAVE_ADDRESS7;//這個就是作為從機的地址,一定要配置正確
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//7位的地址
    I2C_InitStructure.I2C_ClockSpeed = ClockSpeed;
    I2C_Init(I2C1, &I2C_InitStructure);

上面配置注意的就是從機地址,這就是主機要查詢的從機地址

三、i2c從機中斷的配置

    /* Configure and enable I2Cx event interrupt -------------------------------*/
    NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* Configure and enable I2C1 error interrupt -------------------------------*/
    NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
    NVIC_Init(&NVIC_InitStructure);
標準庫中的i2c一共有兩個中斷 一個是事件中斷(EV_IRQ)和一個錯誤中斷(ER_IRQ) EV_IRQ的中斷只要響應EV1 EV2 EV4 之類的,後面會說明 ER_IRQ的中斷只要響應沒有應答和起始和停止條件出錯等

四、使能中斷

/* Enable I2C1 event and buffer interrupts */

I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, ENABLE);

/* Enable I2C1 Error interrupts */

I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE);
使能了I2C1的這三個中斷,每個中斷的作用下面說明

五、中斷處理函式

void I2C1_EV_IRQHandler(void)     

//事件中斷處理函式 

{ 

 switch (I2C_GetLastEvent(I2C1))

 //獲取i2c1的中斷事件 

 { 

  /* Slave Transmitter ---------------------------------------------------*/  

 case I2C_EVENT_SLAVE_BYTE_TRANSMITTED: 

  /* 這個和下面那個都是從傳送模式下發送資料的,具體兩個的區別我也不是很明白,感覺就是移位暫存器空與非  空的區別,準備好資料傳送吧 */ 

  I2C_SendData(I2C1, I2C1_Buffer_Tx[Tx_Idx++]);

   break; 

 case I2C_EVENT_SLAVE_BYTE_TRANSMITTING:             /* EV3 */    

  /* Transmit I2C1 data */

  I2C_SendData(I2C1, I2C1_Buffer_Tx[Tx_Idx++]); 

   break; 

 /* Slave Receiver ------------------------------------------------------*/ 

 case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED:     /* EV1 */ 

  /* 地址匹配中斷,不管從傳送和接收都要匹配地址,如下圖244、243傳送地址之後都會響應EV1 */ 

  break; 

 case I2C_EVENT_SLAVE_BYTE_RECEIVED:                /* EV2 */ 

  /* Store I2C1 received data */ 

  /* 這個中斷就是響應EV2中斷,如下圖244,每次主機發送完一個數據就會產生一個EV2的中斷 */ 

  I2C1_Buffer_Rx[Rx_Idx++] = I2C_ReceiveData(I2C1);

  /* 把接收到的中斷填充到陣列中 */ 

  /* 注意:地址不會填充進來的 */

   break; 

 case I2C_EVENT_SLAVE_STOP_DETECTED:                /* EV4 */

   /* Clear I2C1 STOPF flag */ 

  /* 這個就是正常停止的時候產生的一個停止訊號 */ 

  I2C_Cmd(I2C1, ENABLE); 

  /* 我也不清楚這個為什麼要這樣,如果接收完一串資料之後,不響應主機的情況可以 關閉i2c,然後在處理完資料後再  從新配置i2c,記得是從新配置 */ 

  Rx_Idx=0; 

  i2c_event = EVENT_OPCOD_NOTYET_READ; 

  break; 

 default: 

  break;    

  } 

} 

void I2C1_ER_IRQHandler(void) 

{ 

 /* Check on I2C1 AF flag and clear it */ 

 if (I2C_GetITStatus(I2C1, I2C_IT_AF)) 

 { 

  /* 這個就是圖243中最後那個沒有應答的中斷,也就是傳送了一串資料後的中斷,可以做清零工作 */  

  I2C_ClearITPendingBit(I2C1, I2C_IT_AF); 

  Tx_Idx = 0; 

  i2c_event = EVENT_OPCOD_NOTYET_READ; 

 } 

 /* Check on I2C1 AF flag and clear it */ 

 if (I2C_GetITStatus(I2C1, I2C_IT_BERR))   //這個就是起始和停止條件出錯了

 { 

  I2C_ClearITPendingBit(I2C1, I2C_IT_BERR);

  }

 }




注意:i2c中斷中不要printf列印資訊, 則會出錯

主要是參考了官方的歷程,然後自己再調整一下邏輯即可使用。


最後的效果感覺還是挺穩定的,這篇文章主要是給大家拋磚引玉的作用,也記錄一下自己除錯幾天的成果。 ST官網的歷程還是挺多的,沒遇到的知識可以去官網找找。