2015-06-19 42 views
3

我在開發ST微電子iNemo應用程序時遇到了一個很奇怪的問題。我的應用在於:嵌入式軟件程序塊,I2C?

  • 陀螺SPI讀取
  • 加速度計和磁力(在同一設備中)與I2C讀取
  • 姿態估計算法
  • PD功能
  • 與USART
  • 數據接收,以沒有DMA的中斷
  • 發送日誌包,USART

循環由100Hz的定時器觸發。這個程序運行良好(我用一些USART調試打印進行了測試)直到我開始用USART發送數據:我最初的猜測是,由於這個事實使得接收到中斷,它會導致I2C總線套利機制的問題。我的猜測是,當我用USART打印成功地調試問題(時間相關)時,我發現最後一次打印總是在磁力計打印的加速度計之前(我在代碼中調用的第一個打印)。 此外,如果我通過USART啓用了詳細的調試打印,我已經提到問題發生在更少的場合,而如果我禁用它並且僅發送日誌記錄數據包,則問題總是會立即發生。任何人都可以告訴我這個問題的原因是什麼?由於

編輯:我附上我的I2C代碼:

#define DMA_BUFFER_SIZE  196 
#define FORCE_CRITICAL_SEC 
/** 
* @brief DMA initialization structure variable definition. 
*/ 
DMA_InitTypeDef I2CDMA_InitStructure; 

/** 
* @brief Volatile variable definition for I2C direction. 
*/ 
__IO uint32_t I2CDirection = I2C_DIRECTION_TX; 
void iNemoI2CInit(I2C_TypeDef* I2Cx, uint32_t I2CxSpeed) 
{ 
    I2C_InitTypeDef I2C_InitStructure; 
    GPIO_InitTypeDef GPIO_InitStructure; 

    /* Enable GPIO clocks */ 
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE); 

/* Configure I2C pins: SCL and SDA */ 
if(I2Cx==I2C2) 
{ 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; 
    } 
    else 
    { 
    GPIO_PinRemapConfig(GPIO_Remap_I2C1,ENABLE); 

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; 
    } 

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; 
    GPIO_Init(GPIOB, &GPIO_InitStructure); 


    /* I2C configuration */ 
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; 
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; 
    I2C_InitStructure.I2C_OwnAddress1 = 0x00; 
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; 
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; 
    I2C_InitStructure.I2C_ClockSpeed = I2CxSpeed; 

    /* Apply I2C configuration after enabling it */ 
    I2C_Init(I2Cx, &I2C_InitStructure); 

    /* I2C Peripheral Enable */ 
    I2C_Cmd(I2Cx, ENABLE); 

    /* Enable DMA if required */ 
#if (defined(I2C1_USE_DMA_TX) || defined(I2C1_USE_DMA_RX)) 
if (I2Cx==I2C1) 
    iNemoI2CDMAInit(I2C1); 
#endif 

#if (defined(I2C2_USE_DMA_TX) || defined(I2C2_USE_DMA_RX)) 
if (I2Cx==I2C2) 
    iNemoI2CDMAInit(I2C2); 
#endif 


} 
void iNemoI2CDMAInit(I2C_TypeDef* I2Cx) 
{ 
    /* Enable the DMA1 clock */ 
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); 

    /* I2C TX DMA Channel configuration */  
    I2CDMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)0; /* This parameter will be configured durig communication */ 
    I2CDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; /* This parameter will be configured durig communication */ 
    I2CDMA_InitStructure.DMA_BufferSize = 0xFFFF;   /* This parameter will be configured durig communication */ 
    I2CDMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 
    I2CDMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; 
    I2CDMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_Byte; 
    I2CDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; 
    I2CDMA_InitStructure.DMA_Mode = DMA_Mode_Normal; 
    I2CDMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; 
    I2CDMA_InitStructure.DMA_M2M = DMA_M2M_Disable; 

    if(I2Cx==I2C2) 
    { 
    I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C2_DR_Address; 

#ifdef I2C2_USE_DMA_TX 
     DMA_DeInit(I2C2_DMA_CHANNEL_TX); 
     DMA_Init(I2C2_DMA_CHANNEL_TX, &I2CDMA_InitStructure); 
#endif 

#ifdef I2C2_USE_DMA_RX 
     /* I2C2 RX DMA Channel configuration */ 
     DMA_DeInit(I2C2_DMA_CHANNEL_RX); 
     DMA_Init(I2C2_DMA_CHANNEL_RX, &I2CDMA_InitStructure); 
#endif 
    } 

    if(I2Cx==I2C1) 
    { 
    I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C1_DR_Address; 

#ifdef I2C1_USE_DMA_TX 
     DMA_DeInit(I2C1_DMA_CHANNEL_TX); 
     DMA_Init(I2C1_DMA_CHANNEL_TX, &I2CDMA_InitStructure); 
#endif 

#ifdef I2C1_USE_DMA_RX 
     /* I2C1 RX DMA Channel configuration */ 
     DMA_DeInit(I2C1_DMA_CHANNEL_RX); 
     DMA_Init(I2C1_DMA_CHANNEL_RX, &I2CDMA_InitStructure); 
#endif 

    } 
void iNemoI2CDMAConfig(I2C_TypeDef* I2Cx, uint8_t* pBuffer, uint32_t lBufferSize, uint32_t lDirection) 
{ 
    /* Initialize the DMA with the new parameters */ 
    if (lDirection == I2C_DIRECTION_TX) 
    { 
    /* Configure the DMA Tx Channel with the buffer address and the buffer size */ 
    I2CDMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)pBuffer; 
    I2CDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; 
    I2CDMA_InitStructure.DMA_BufferSize = (uint32_t)lBufferSize; 
    if(I2Cx==I2C2) 
    { 
     I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C2_DR_Address; 
     DMA_Cmd(I2C2_DMA_CHANNEL_TX, DISABLE); 
     DMA_Init(I2C2_DMA_CHANNEL_TX, &I2CDMA_InitStructure); 
     DMA_Cmd(I2C2_DMA_CHANNEL_TX, ENABLE); 
    } 
    else 
    { 
     I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C1_DR_Address; 
     DMA_Cmd(I2C1_DMA_CHANNEL_TX, DISABLE); 
     DMA_Init(I2C1_DMA_CHANNEL_TX, &I2CDMA_InitStructure); 
     DMA_Cmd(I2C1_DMA_CHANNEL_TX, ENABLE); 
    } 
    } 
    else /* Reception */ 
    { 
    /* Configure the DMA Rx Channel with the buffer address and the buffer size */ 
    I2CDMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)pBuffer; 
    I2CDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; 
    I2CDMA_InitStructure.DMA_BufferSize = (uint32_t)lBufferSize; 

    if(I2Cx==I2C2) 
    { 
     I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C2_DR_Address; 
     DMA_Cmd(I2C2_DMA_CHANNEL_RX, DISABLE); 
     DMA_Init(I2C2_DMA_CHANNEL_RX, &I2CDMA_InitStructure); 
     DMA_Cmd(I2C2_DMA_CHANNEL_RX, ENABLE); 
    } 
    else 
    { 
     I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C1_DR_Address; 
     DMA_Cmd(I2C1_DMA_CHANNEL_RX, DISABLE); 
     DMA_Init(I2C1_DMA_CHANNEL_RX, &I2CDMA_InitStructure); 
     DMA_Cmd(I2C1_DMA_CHANNEL_RX, ENABLE); 
    } 
    } 
} 

void iNemoI2CBufferReadDma(I2C_TypeDef* I2Cx, uint8_t cAddr, uint8_t* pcBuffer, uint8_t cReadAddr, uint8_t cNumByteToRead) 
{ 

__IO uint32_t temp = 0; 
__IO uint32_t Timeout = 0; 

/* Enable I2C errors interrupts */ 
I2Cx->CR2 |= I2C_IT_ERR; 

/* Set the MSb of the register address in case of multiple readings */ 
if(cNumByteToRead>1) 
    cReadAddr |= 0x80; 

#ifdef FORCE_CRITICAL_SEC 
    __disable_irq(); 
#endif  

#ifdef USART_DEBUG2 
    USART1_Printf("FLAG BUSY\r\n"); 
#endif 

Timeout = 0xFFFF; 
/* While the bus is busy */ 
while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY)){ 
    if (Timeout-- == 0) 
     return; 
} 

/* Send START condition */ 
I2C_GenerateSTART(I2Cx, ENABLE); 

#ifdef USART_DEBUG2 
    USART1_Printf("MASTER MODE\r\n"); 
#endif 

Timeout = 0xFFFF; 
/* Test on EV5 and clear it */ 
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)){ 
    if (Timeout-- == 0) 
     return; 
} 

/* Send LSM303DLH address for read */ 
I2C_Send7bitAddress(I2Cx, cAddr, I2C_Direction_Transmitter); 

Timeout = 0xFFFF; 
/* Test on EV6 and clear it */ 
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){ 
    if (Timeout-- == 0) 
     return; 
} 

/* Clear EV6 by setting again the PE bit */ 
I2C_Cmd(I2Cx, ENABLE); 

/* Send the LSM303DLH_Magn's internal address to write to */ 
I2C_SendData(I2Cx, cReadAddr); 

#ifdef USART_DEBUG2 
    USART1_Printf("BYTE TRANSMITTED\r\n"); 
#endif 

Timeout = 0xFFFF; 

/* Test on EV8 and clear it */ 
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){ 
    if (Timeout-- == 0) 
     return; 
} 

/* Configure I2Cx DMA channel */ 
iNemoI2CDMAConfig(I2Cx, pcBuffer, cNumByteToRead, I2C_DIRECTION_RX); 

/* Set Last bit to have a NACK on the last received byte */ 
I2Cx->CR2 |= 0x1000; 

/* Enable I2C DMA requests */ 
I2C_DMACmd(I2Cx, ENABLE); 
Timeout = 0xFFFF; 

/* Send START condition */ 
I2C_GenerateSTART(I2Cx, ENABLE); 

Timeout = 0xFFFF; 

/* Wait until SB flag is set: EV5 */ 
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)) 
{ 
    if (Timeout-- == 0) 
    return; 
} 
Timeout = 0xFFFF; 

/* Send LSM303DLH address for read */ 
I2C_Send7bitAddress(I2Cx, cAddr, I2C_Direction_Receiver); 

Timeout = 0xFFFF; 

    /* Wait until ADDR is set: EV6 */ 
    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) 
    { 
     if (Timeout-- == 0) 
     return; 
    } 
    /* Clear ADDR flag by reading SR2 register */ 
    temp = I2Cx->SR2; 



    if(I2Cx == I2C2) 
    { 
     Timeout = 0xFFFF; 
     /* Wait until DMA end of transfer */ 
     while (!DMA_GetFlagStatus(DMA1_FLAG_TC5)){ 
     if (Timeout-- == 0) 
      return; 
    } 
     /* Disable DMA Channel */ 
     DMA_Cmd(I2C2_DMA_CHANNEL_RX, DISABLE); 

     /* Clear the DMA Transfer Complete flag */ 
     DMA_ClearFlag(DMA1_FLAG_TC5); 
    } 
    else 
    { 
     /* Wait until DMA end of transfer */ 
    #ifdef USART_DEBUG2 
     USART1_Printf("END TRANSFER\r\n"); 
    #endif 
     Timeout = 0xFFFF; 
     while (!DMA_GetFlagStatus(DMA1_FLAG_TC7)){ 
     if (Timeout-- == 0) 
      return; 
    } 
     /* Disable DMA Channel */ 
     DMA_Cmd(I2C1_DMA_CHANNEL_RX, DISABLE); 

     /* Clear the DMA Transfer Complete flag */ 
     DMA_ClearFlag(DMA1_FLAG_TC7); 
    } 


    /* Disable Ack for the last byte */ 
    I2C_AcknowledgeConfig(I2Cx, DISABLE); 

    /* Send STOP Condition */ 
    I2C_GenerateSTOP(I2Cx, ENABLE); 

    #ifdef USART_DEBUG2 
     USART1_Printf("STOP BIT\r\n"); 
    #endif 
    Timeout = 0xFFFF; 
    /* Make sure that the STOP bit is cleared by Hardware before CR1 write access */ 
    while ((I2Cx->CR1 & 0x0200) == 0x0200){ 
     if (Timeout-- == 0) 
     return; 
    } 

    /* Enable Acknowledgement to be ready for another reception */ 
    I2C_AcknowledgeConfig(I2Cx, ENABLE); 

#ifdef FORCE_CRITICAL_SEC 
    __enable_irq(); 
#endif 

} 
void iNemoI2CBufferWriteDma(I2C_TypeDef* I2Cx, uint8_t cAddr, uint8_t* pcBuffer, uint8_t cWriteAddr, uint8_t cNumByteToWrite) 
{ 

    __IO uint32_t temp = 0; 
    __IO uint32_t Timeout = 0; 

    static uint8_t pcDmaBuffer[DMA_BUFFER_SIZE+1]; 

    /* Set to 1 the MSb of the register address in case of multiple byte writing */ 
    if(cNumByteToWrite>1) 
    cWriteAddr |= 0x80; 

    pcDmaBuffer[0]=cWriteAddr; 
    memcpy(&pcDmaBuffer[1],pcBuffer,cNumByteToWrite); 

    /* Enable Error IT */ 
    I2Cx->CR2 |= I2C_IT_ERR; 

    Timeout = 0xFFFF; 
    /* Configure the DMA channel for I2Cx transmission */ 
    iNemoI2CDMAConfig(I2Cx, pcDmaBuffer, cNumByteToWrite+1, I2C_DIRECTION_TX); 

    /* Enable DMA for I2C */ 
    I2C_DMACmd(I2Cx, ENABLE); 

    /* Send START condition */ 
    I2C_GenerateSTART(I2Cx, ENABLE); 


    /* Wait until SB flag is set: EV5 */ 
    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)) 
    { 
    if (Timeout-- == 0) 
     return; 
    } 

    Timeout = 0xFFFF; 

    /* Send LSM303DLH address for write */ 
    I2C_Send7bitAddress(I2Cx, cAddr, I2C_Direction_Transmitter); 

    /* Wait until ADDR is set: EV6 */ 
    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) 
    { 
    if (Timeout-- == 0) 
     return; 
    } 

    /* Clear ADDR flag by reading SR2 register */ 
    temp = I2Cx->SR2; 


    /* Disable the DMA1 channel */ 
    if(I2Cx == I2C2) 
    { 
    /* Wait until DMA end of transfer */ 
    while (!DMA_GetFlagStatus(DMA1_FLAG_TC4)); 
    /* Disable DMA Channel */ 
    DMA_Cmd(I2C2_DMA_CHANNEL_TX, DISABLE); 

    /* Clear the DMA Transfer complete flag */ 
    DMA_ClearFlag(DMA1_FLAG_TC4); 
    } 
    else 
    { 
    /* Wait until DMA end of transfer */ 
    while (!DMA_GetFlagStatus(DMA1_FLAG_TC6)); 
    /* Disable DMA Channel */ 
    DMA_Cmd(I2C1_DMA_CHANNEL_TX, DISABLE); 

    /* Clear the DMA Transfer complete flag */ 
    DMA_ClearFlag(DMA1_FLAG_TC6); 
    } 


    /* EV8_2: Wait until BTF is set before programming the STOP */ 
    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); 

    /* Send STOP Condition */ 
    I2C_GenerateSTOP(I2Cx, ENABLE); 

    /* Make sure that the STOP bit is cleared by Hardware before CR1 write access */ 
    while ((I2Cx->CR1 & 0x0200) == 0x0200); 

} 
+0

您的I2C讀取卡住了。要麼你不以正確的方式使用I2C API,否則硬件設置不當。 –

+1

檢查您的DSO上的I2C波形,無論您的位模式是否正確 –

+0

@EugeneSh。我已經添加了代碼,我認爲它是可以的,因爲它是從ST示例中獲取的,但是我已附加到此處,您能否就此對我發表意見? – Daniel

回答

3

我看到了一會兒循環你有超時,但對於一些你不這樣做:

while ((I2Cx->CR1 & 0x0200) == 0x0200);

製作所有循環都會超時,並且還會記錄發生錯誤情況的位置(需要調查 - 如果您不知道原因,它會在以後再次出現)。

有時候硬件可能有點bug,所以它完全有可能你正在做的一切,但它仍然無法正常工作。檢查勘誤表(針對STM32 I2C和您的I2C從器件)以查找已記錄的錯誤。

幾年前,我遇到了一個I2C線路會保持低電平的問題,我不得不將引腳重新配置爲GPIO,稍微反覆位,然後我可以切換回I2C操作。

+1

另外,在同一行寫這樣的循環的分號是不好的做法。讀者將無法確定缺乏循環體是故意的,還是僅僅是將分號放在那裏的一根手指。 – Lundin