1. 程式人生 > >【STM32 IIC詳解】stm32 IIC從機模式(中斷方式收發資料)

【STM32 IIC詳解】stm32 IIC從機模式(中斷方式收發資料)

1、IIC簡介


第二節程式碼會用到該部分內容,對於IIC來說,從機是不能主動傳送資料的,開始條件都是由主機生成。

 
1.1、主機發送資料流程


  1) 主機在檢測到匯流排為“空閒狀態”(即 SDA、SCL 線均為高電平)時,傳送一個啟動訊號“S”,開始一次通訊的開始
  2) 主機接著傳送一個命令位元組。該位元組由 7 位的外圍器件地址和 1 位讀寫控制位 R/W組成(此時 R/W=0)
  3) 相對應的從機收到命令位元組後向主機回饋應答訊號 ACK(ACK=0)
  4) 主機收到從機的應答訊號後開始傳送第一個位元組的資料
  5) 從機收到資料後返回一個應答訊號 ACK
  6) 主機收到應答訊號後再發送下一個資料位元組
  7) 當主機發送最後一個數據位元組並收到從機的 ACK 後,通過向從機發送一個停止訊號P結束本次通訊並釋放匯流排。從機收到P訊號後也退出與主機之間的通訊

 
1.2、主機接收資料流程


  1) 主機發送啟動訊號後,接著傳送命令位元組(其中 R/W=1)
  2) 對應的從機收到地址位元組後,返回一個應答訊號並向主機發送資料
  3) 主機收到資料後向從機反饋一個應答訊號
  4) 從機收到應答訊號後再向主機發送下一個資料
  5) 當主機完成接收資料後,向從機發送一個“非應答訊號(ACK=1)”,從機收到ASK=1 的非應答訊號後便停止傳送
  6) 主機發送非應答訊號後,再發送一個停止訊號,釋放匯流排結束通訊

 
1.3、處理器的I2C模組會在如下所述的情況產生中斷訊號


  RX_UNDER   當處理器通過IC_DATA_CMD暫存器讀取接收緩衝器為空時置位
  RX_OVER    當接收緩衝器被填滿,而且還有資料從外設傳送過來時被置位;緩衝器被填滿後接收的資料將會丟失
  RX_FULL    當接收緩衝器達到或者超過IC_RX_TL暫存器中規定的閾值時被置位;當資料低於閾值時標誌位將被自動清除
  TX_OVER    當傳送緩衝器被填滿,而且處理器試圖傳送另外的命令寫IC_DATA_CMD暫存器時被置位
  TX_EMPTY   當傳送緩衝器等於或者低於IC_TX_TL暫存器中規定的閾值時被置位;當資料高於閾值時標誌位將被自動清除
  TX_ABRT    當i2c模組無法完成處理器下達的命令時被置位,有如下幾種原因:
                          * 傳送地址位元組後沒有從機應答
                          * 地址識別成功後主機發送的資料從機沒有應答
                          * 當i2c模組只能作為從機時試圖傳送主機命令
                          * 當模組的RESTART功能被關閉,而處理試圖完成的功能必須要RESTART功能開啟才能完成
                          * 高速模組主機程式碼被應答
                          * START BYTE被應答
                          * 模組仲裁失敗
                          無論標誌位什麼時候被置位,傳送緩衝器和接收緩衝器的內容都會被重新整理
  ACTIVITY   表明i2c模組正在活動,這個標誌位將會一直保持直到用以下4種方式清除:
                          * 關閉i2c
                          * 讀取IC_CLR_ACTIVITY暫存器
                          * 讀取IC_CLR_INTR暫存器
                          * 系統重啟
                          即使i2c模組是空閒的,這個標誌仍然需要被置位直到被清除,因為這表明i2c總線上有資料正在傳輸
 
需要用到的:
 
  RD_REQ     當i2c模組作為從機時並且另外的主機試圖從本模組讀取資料時被置位  
  RX_DONE    當i2c模組作為從機發送資料時,如果主機沒有應答則置位;這種情況發生在i2c模組傳送最後一個位元組資料時,表明傳輸結束
  STOP_DET   表明i2c總線上產生了STOP訊號,無論模組作為主機還是從機
  START_DET  表明i2c總線上產生了START訊號,無論模組作為主機還是從機

 
2、IIC從機中斷收發函式


// 從機收發函式處理
void I2C1_EV_IRQHandler(void)
{
  __IO uint16_t SR1Register =0;
  __IO uint16_t SR2Register =0;


  SR1Register = I2C1->SR1;           // 通過讀取 SR1/2 獲取 IIC 狀態
  SR2Register = I2C1->SR2;


  // 從機發送
  // 判斷IIC是從機模式 - 最低位(MSL = 0)
  if((SR2Register & 0x0001) != 0x0001)
  {
    // ADDR:根據狀態判斷獲取從機IIC地址成功
    if((SR1Register & 0x0002) == 0x0002)
    {
      // 清除標誌,準備接收資料
      SR1Register = 0;
      SR2Register = 0;
 TrCount= 0;
    }


//TxE:根據狀態標誌可以傳送資料
if((SR1Register & 0x0080) == 0x0080)
{
 SR1Register = 0;
      SR2Register = 0;
 I2C1->DR =TrCount ++;
}

//STOPF:監測停止標誌
if((SR1Register & 0x0010) == 0x0010)
{
 I2C1->CR1 |= CR1_PE_Set;
      SR1Register = 0;
      SR2Register = 0;
 TrCount= 5;
}

//TIME_OUT
if((SR1Register & 0x4000) == 0x4000)
{
 I2C1->CR1 |= CR1_PE_Set;
 SR1Register = 0;
      SR2Register = 0;
}
  }  
 
  // IIC從機接收
  // 判斷IIC是從機模式 - 最低位(MSL = 0)
  if((SR2Register &0x0001) != 0x0001)
  {
    // 收到主機發送的地址:(ADDR = 1: EV1)
    if((SR1Register & 0x0002) == 0x0002)
    {
      // 清除標誌,準備接受資料
      SR1Register = 0;
      SR2Register = 0;
      Rx_Idx = 0;
    }
    
    // 接收資料 (RXNE = 1: EV2)
    if((SR1Register & 0x0040) == 0x0040)
    {
      Buffer_Rx[Rx_Idx++] = I2C1->DR;
      SR1Register = 0;
      SR2Register = 0;
    }
    
    // 監測停止條件 (STOPF =1: EV4)
    if(( SR1Register & 0x0010) == 0x0010)
    {
      I2C1->CR1 |= CR1_PE_Set;
      SR1Register = 0;
      SR2Register = 0;
      Flag_RcvOK = 1;                         
    }
  }
}

 
3、程式碼參考例項


//stm32f10x_it.c


#include "stm32f10x_it.h"
#include "stdio.h"


extern u32 BufferSize ;
extern u8 I2C1_ADDRESS ;
extern u8 I2C2_ADDRESS ;
extern vu8 I2C1_Buffer_Tx[];
extern vu8 I2C2_Buffer_Rx[];
vu32 Tx_Counter = 0;
vu32 Rx_Counter = 0;
vu32 show_counter1 = 0;
vu32 show_counter2 = 0;


// I2C1 作為主機,用於中斷接收從機資料
void I2C1_EV_IRQHandler(void)
{
  show_counter1++;
  if(show_counter1 > 1000000)
  {
    show_counter1 = 0;
    printf("\r\n The I2C1 LastEvent is %x \r\n", I2C_GetLastEvent(I2C1));
  }
  switch(I2C_GetLastEvent(I2C1))
  {
    case I2C_EVENT_MASTER_MODE_SELECT: // 已傳送啟始條件
    {
 // 七位地址傳送
      I2C_Send7bitAddress(I2C1, I2C2_ADDRESS, I2C_Direction_Receiver);
      printf("\r\n The I2C1 is ready \r\n");
      break;
    }
    case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED: // 已傳送從機地址
    {
      printf("\r\n The slave address is %x \r\n", I2C_ReceiveData(I2C1));
      break;
    }
    case (I2C_EVENT_MASTER_BYTE_RECEIVED | (I2C_FLAG_BTF & 0x0f)): // 第一個資料已接收
    {
      // 要接收最後一個位元組前先關匯流排,不然匯流排鎖死
      I2C_GenerateSTOP(I2C1,ENABLE);
      printf("\r\n The I2C1 has received data2 %x \r\n", I2C_ReceiveData(I2C1));
      printf("\r\n The I2C1 is finish \r\n");
      break;
    }
    case 0x40:
    {
      // 接收了兩個同樣的資料,沒有這個釋放不了 RXNE
      I2C_ReceiveData(I2C1);
    }
default: {break;}
  }
}


// I2C2 用於從機發送資料到主機
void I2C2_EV_IRQHandler(void)
{
  show_counter2++;
  if(show_counter2 > 100000)
  {
    show_counter2 = 0;
    printf("\r\n The I2C2 LastEvent is %x \r\n", I2C_GetLastEvent(I2C2));
  }
  switch(I2C_GetLastEvent(I2C2))
  {
    // 收到匹配的地址資料
    case I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED:
    {
      printf("\r\n The I2C2 is ready \r\n");
      I2C_GenerateSTOP(I2C2, DISABLE);
      break;
    }
    case I2C_EVENT_SLAVE_BYTE_TRANSMITTING: //傳送資料
    {
      printf("\r\n The I2C2 transmits is transmitting \r\n");
      I2C_SendData(I2C2, 0xb6 + Rx_Counter);
      break;

    }

   // 傳送資料,要傳送,不然鎖死,不過 master 沒收到

    case I2C_EVENT_SLAVE_BYTE_TRANSMITTED:
    {

      printf("\r\n The I2C2 transmits one byte \r\n");

     I2C_SendData(I2C2, 0xb6 + (Rx_Counter++));

      break;
    }
    case I2C_EVENT_SLAVE_STOP_DETECTED: //收到結束條件
    {
      printf("\r\n The I2C2 is finish \r\n");
      I2C_ClearFlag(I2C2,I2C_FLAG_STOPF);
      I2C_GenerateSTOP(I2C2, ENABLE);
      break;
    }
    default: {break;}
  }
}


/*----------------------------------------------------------------------------------------------------
名稱: I2C 測試 24C02 測試
編寫: mingzhang.zhao
內容:測試 stm32f103vct6 的硬體 I2C 實現中斷收發資料
注意事項:
1.USART1: PA9 為 TX, PA10 為 RX
I2C1: PB6 為 SCL, PB7 為 SDA
I2C2: PB10 為 SCL, PB11 為 SDA
----------------------------------------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "stdio.h"
#define PRINTF_ON 1
void RCC_Configuration(void);void GPIO_Configuration(void);
void USART_Configuration(void);
void I2C_Configuration(void);
void NVIC_Configuration(void);
void Delay(__IO uint32_t t);
u8 I2C1_ADDRESS = 0x30; //7 位 I2C 地址
u8 I2C2_ADDRESS = 0x31;
#define Size 4
vu8 I2C1_Buffer_Tx[Size] = {1,2,3,4};
vu8 I2C2_Buffer_Rx[Size] = {0};
u32 BufferSize = Size ;


int main(void)
{
  RCC_Configuration();
  GPIO_Configuration();
  USART_Configuration();
  I2C_Configuration();
  NVIC_Configuration();
  I2C_GenerateSTART(I2C1,ENABLE);
  while(1)
  {
    Delay(1000);
    I2C_GenerateSTART(I2C1,ENABLE); //I2C1 迴圈讀取資料
  }
}


// 初始化和配置相關
void I2C_Configuration(void)
{
  I2C_InitTypeDef I2C_InitStructure;
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  I2C_InitStructure.I2C_OwnAddress1 = I2C1_ADDRESS;
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  I2C_InitStructure.I2C_ClockSpeed = 200000;I2C_Init(I2C1,&I2C_InitStructure);
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  I2C_InitStructure.I2C_OwnAddress1 = I2C2_ADDRESS;
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  I2C_InitStructure.I2C_ClockSpeed = 200000;
  I2C_Init(I2C2,&I2C_InitStructure);
  I2C_ITConfig(I2C1,I2C_IT_EVT|I2C_IT_BUF,ENABLE);
  I2C_ITConfig(I2C2,I2C_IT_EVT|I2C_IT_BUF,ENABLE);
  I2C_Cmd(I2C1,ENABLE);
  I2C_Cmd(I2C2,ENABLE);
}


void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}


void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  //初始化 I2C1GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
  GPIO_Init(GPIOB , &GPIO_InitStructure);
  //初始化 I2C2
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
  GPIO_Init(GPIOB , &GPIO_InitStructure);
  //初始化 USART1
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOA , &GPIO_InitStructure);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA , &GPIO_InitStructure);
}


void RCC_Configuration(void)
{
  /* 定義列舉型別變數 HSEStartUpStatus */
  ErrorStatus HSEStartUpStatus;
  /* 復位系統時鐘設定*/
  RCC_DeInit();
  /* 開啟 HSE*/
  RCC_HSEConfig(RCC_HSE_ON);
  /* 等待 HSE 起振並穩定*/
  HSEStartUpStatus = RCC_WaitForHSEStartUp();
  /* 判斷 HSE 起是否振成功,是則進入 if()內部 */
  if(HSEStartUpStatus == SUCCESS)
  {
  /* 選擇 HCLK(AHB)時鐘源為 SYSCLK 1 分頻 */
  RCC_HCLKConfig(RCC_SYSCLK_Div1);
  /* 選擇 PCLK2 時鐘源為 HCLK(AHB) 1 分頻 */
  RCC_PCLK2Config(RCC_HCLK_Div1);
  /* 選擇 PCLK1 時鐘源為 HCLK(AHB) 2 分頻 */RCC_PCLK1Config(RCC_HCLK_Div2);
  /* 設定 FLASH 延時週期數為 2 */
  FLASH_SetLatency(FLASH_Latency_2);
  /* 使能 FLASH 預取快取 */
  FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
  /* 選擇鎖相環(PLL)時鐘源為 HSE 1 分頻, 倍頻數為 9,則 PLL 輸出頻率為 8MHz
  * 9 = 72MHz */
  RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
  /* 使能 PLL */
  RCC_PLLCmd(ENABLE);
  /* 等待 PLL 輸出穩定 */
  while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
  /* 選擇 SYSCLK 時鐘源為 PLL */
  RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
  /* 等待 PLL 成為 SYSCLK 時鐘源 */
  while(RCC_GetSYSCLKSource() != 0x08);
  }
  /* 開啟 APB2 總線上的 GPIOA 時鐘*/
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB
  2Periph_USART1, ENABLE);
  //RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1|RCC_APB1Periph_I2C2,ENABLE);
  //RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP|RCC_APB1Pe
  riph_WWDG|RCC_APB1Periph_SPI2, ENABLE);
}


void USART_Configuration(void)
{
  USART_InitTypeDef USART_InitStructure;
  USART_ClockInitTypeDef USART_ClockInitStructure;USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;
  USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;
  USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;
  USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
  USART_ClockInit(USART1 , &USART_ClockInitStructure);
  USART_InitStructure.USART_BaudRate = 9600;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl =
  USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
  USART_Init(USART1,&USART_InitStructure);
  USART_Cmd(USART1,ENABLE);
}


void Delay(__IO uint32_t t)
{
  while(t--);
}
#if PRINTF_ON
int fputc(int ch,FILE *f)
{
  USART_SendData(USART1,(u8) ch);
  while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
  return ch;
}
#endif


refer:http://blog.csdn.net/g_salamander/article/details/8016698
---------------------
作者:liwei16611
來源:CSDN
原文:https://blog.csdn.net/liwei16611/article/details/75258222
版權宣告:本文為博主原創文章,轉載請附上博文連結!