在为 ST Microelectronics iNemo 开发应用程序时,我遇到了一个非常奇怪的问题。我的申请包括:
- 使用 SPI 的陀螺仪读数
- 加速度计和磁力计(在同一设备中)使用 I2C 读取
- 姿态估计算法
- 局部放电功能
- 使用 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);
}