1.03日更新 在使用I2C2的过程中发现读取数据下图的问题。 这是读取bno055的结果,如果摆动传感器会获得数据,但是如果静止或者过了一会就只剩下yaw方向的结果。同时pitch 方向的数据基本上的读不到的。经常出现-1的结果。
而使用I2C1的结果如下:
在尝试使用软件I2C与BNO055通讯失败后(该软件I2C代码此前与MPU6050或其他传感器通讯均没问题,但是用在BNO055 上在发送从地址并收到应答后时序就乱了,不接传感器下测出来的时序是正确的),使用STM32 自带的I2C,并且使用的是库函数操作。当读函数在main函数内是正常的,但是一旦在把它封装到别的函数,时序就乱了,并且卡在EV6事件的循环等待。 此前一直没使用过硬件的I2C,所有这次认真查看参考手册再比较库函数的实现,其实在每个事件的等待中,库函数比较的不止一个寄存器的位,基本上每个事件都是几个位的比较 下列是事件掩码的定义
/* --EV6 */ #define I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082) /* BUSY, MSL, ADDR, TXE and TRA flags */ #define I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ((uint32_t)0x00030002) /* BUSY, MSL and ADDR flags */ /* --EV9 */ #define I2C_EVENT_MASTER_MODE_ADDRESS10 ((uint32_t)0x00030008) /* BUSY, MSL and ADD10 flags */ /* --EV5 */ #define I2C_EVENT_MASTER_MODE_SELECT ((uint32_t)0x00030001) /* BUSY, MSL and SB flag */使用库函数的代码很容易就卡在死循环了,而且也不知道是哪里出错。所以果断使用另外一个官方的实现。 根据参考手册的介绍,其实只要检查某一位标志就可以确定外设的状态。 下面这个实现根据参考手册去实现。并且在文档中也对代码进行了阐述:
Status I2C_Master_BufferRead(I2C_TypeDef* I2Cx, uint8_t* pBuffer, uint32_t NumByteToRead, I2C_ProgrammingModel Mode, uint8_t SlaveAddress) { __IO uint32_t temp = 0; __IO uint32_t Timeout = 0; /* Enable I2C errors interrupts (used in all modes: Polling, DMA and Interrupts */ I2Cx->CR2 |= I2C_IT_ERR; ...... else if (Mode == Polling) /* I2Cx Master Reception using Polling */ { if (NumByteToRead == 1) { Timeout = 0xFFFF; /* Send START condition */ I2Cx->CR1 |= CR1_START_Set; /* Wait until SB flag is set: EV5 */ while ((I2Cx->SR1&0x0001) != 0x0001) { if (Timeout-- == 0) return Error; } /* Send slave address */ /* Reset the address bit0 for read */ SlaveAddress |= OAR1_ADD0_Set; Address = SlaveAddress; /* Send the slave address */ I2Cx->DR = Address; /* Wait until ADDR is set: EV6_3, then program ACK = 0, clear ADDR and program the STOP just after ADDR is cleared. The EV6_3 software sequence must complete before the current byte end of transfer.*/ /* Wait until ADDR is set */ Timeout = 0xFFFF; while ((I2Cx->SR1&0x0002) != 0x0002) { if (Timeout-- == 0) return Error; } /* Clear ACK bit */ I2Cx->CR1 &= CR1_ACK_Reset; /* Disable all active IRQs around ADDR clearing and STOP programming because the EV6_3 software sequence must complete before the current byte end of transfer */ __disable_irq(); /* Clear ADDR flag */ temp = I2Cx->SR2; /* Program the STOP */ I2Cx->CR1 |= CR1_STOP_Set; /* Re-enable IRQs */ __enable_irq(); /* Wait until a data is received in DR register (RXNE = 1) EV7 */ while ((I2Cx->SR1 & 0x00040) != 0x000040); /* Read the data */ *pBuffer = I2Cx->DR; /* Make sure that the STOP bit is cleared by Hardware before CR1 write access */ while ((I2Cx->CR1&0x200) == 0x200); /* Enable Acknowledgement to be ready for another reception */ I2Cx->CR1 |= CR1_ACK_Set; } else if (NumByteToRead == 2) { /* Set POS bit */ I2Cx->CR1 |= CR1_POS_Set; Timeout = 0xFFFF; /* Send START condition */ I2Cx->CR1 |= CR1_START_Set; /* Wait until SB flag is set: EV5 */ while ((I2Cx->SR1&0x0001) != 0x0001) { if (Timeout-- == 0) return Error; } Timeout = 0xFFFF; /* Send slave address */ /* Set the address bit0 for read */ SlaveAddress |= OAR1_ADD0_Set; Address = SlaveAddress; /* Send the slave address */ I2Cx->DR = Address; /* Wait until ADDR is set: EV6 */ while ((I2Cx->SR1&0x0002) != 0x0002) { if (Timeout-- == 0) return Error; } /* EV6_1: The acknowledge disable should be done just after EV6, that is after ADDR is cleared, so disable all active IRQs around ADDR clearing and ACK clearing */ __disable_irq(); /* Clear ADDR by reading SR2 register */ temp = I2Cx->SR2; /* Clear ACK */ I2Cx->CR1 &= CR1_ACK_Reset; /*Re-enable IRQs */ __enable_irq(); /* Wait until BTF is set */ while ((I2Cx->SR1 & 0x00004) != 0x000004); /* Disable IRQs around STOP programming and data reading because of the limitation ?*/ __disable_irq(); /* Program the STOP */ I2C_GenerateSTOP(I2Cx, ENABLE); /* Read first data */ *pBuffer = I2Cx->DR; /* Re-enable IRQs */ __enable_irq(); /**/ pBuffer++; /* Read second data */ *pBuffer = I2Cx->DR; /* Make sure that the STOP bit is cleared by Hardware before CR1 write access */ while ((I2Cx->CR1&0x200) == 0x200); /* Enable Acknowledgement to be ready for another reception */ I2Cx->CR1 |= CR1_ACK_Set; /* Clear POS bit */ I2Cx->CR1 &= CR1_POS_Reset; } else { Timeout = 0xFFFF; /* Send START condition */ I2Cx->CR1 |= CR1_START_Set; /* Wait until SB flag is set: EV5 */ while ((I2Cx->SR1&0x0001) != 0x0001) { if (Timeout-- == 0) return Error; } Timeout = 0xFFFF; /* Send slave address */ /* Reset the address bit0 for write */ SlaveAddress |= OAR1_ADD0_Set;; Address = SlaveAddress; /* Send the slave address */ I2Cx->DR = Address; /* Wait until ADDR is set: EV6 */ while ((I2Cx->SR1&0x0002) != 0x0002) { if (Timeout-- == 0) return Error; } /* Clear ADDR by reading SR2 status register */ temp = I2Cx->SR2; /* While there is data to be read */ while (NumByteToRead) { /* Receive bytes from first byte until byte N-3 */ if (NumByteToRead != 3) { /* Poll on BTF to receive data because in polling mode we can not guarantee the EV7 software sequence is managed before the current byte transfer completes */ while ((I2Cx->SR1 & 0x00004) != 0x000004); /* Read data */ *pBuffer = I2Cx->DR; /* */ pBuffer++; /* Decrement the read bytes counter */ NumByteToRead--; } /* it remains to read three data: data N-2, data N-1, Data N */ if (NumByteToRead == 3) { /* Wait until BTF is set: Data N-2 in DR and data N -1 in shift register */ while ((I2Cx->SR1 & 0x00004) != 0x000004); /* Clear ACK */ I2Cx->CR1 &= CR1_ACK_Reset; /* Disable IRQs around data reading and STOP programming because of the limitation ? */ __disable_irq(); /* Read Data N-2 */ *pBuffer = I2Cx->DR; /* Increment */ pBuffer++; /* Program the STOP */ I2Cx->CR1 |= CR1_STOP_Set; /* Read DataN-1 */ *pBuffer = I2Cx->DR; /* Re-enable IRQs */ __enable_irq(); /* Increment */ pBuffer++; /* Wait until RXNE is set (DR contains the last data) */ while ((I2Cx->SR1 & 0x00040) != 0x000040); /* Read DataN */ *pBuffer = I2Cx->DR; /* Reset the number of bytes to be read by master */ NumByteToRead = 0; } } /* Make sure that the STOP bit is cleared by Hardware before CR1 write access */ while ((I2Cx->CR1&0x200) == 0x200); /* Enable Acknowledgement to be ready for another reception */ I2Cx->CR1 |= CR1_ACK_Set; } } else /* I2Cx Master Reception using Interrupts with highest priority in an application */ { /* Enable EVT IT*/ I2Cx->CR2 |= I2C_IT_EVT; /* Enable BUF IT */ I2Cx->CR2 |= I2C_IT_BUF; /* Set the I2C direction to reception */ I2CDirection = I2C_DIRECTION_RX; SlaveAddress |= OAR1_ADD0_Set; Address = SlaveAddress; if (I2Cx == I2C1) NumbOfBytes1 = NumByteToRead; else NumbOfBytes2 = NumByteToRead; /* Send START condition */ I2Cx->CR1 |= CR1_START_Set; /* Wait until the START condition is generated on the bus: START bit is cleared by hardware */ while ((I2Cx->CR1&0x100) == 0x100); /* Wait until BUSY flag is reset (until a STOP is generated) */ while ((I2Cx->SR2 &0x0002) == 0x0002); /* Enable Acknowledgement to be ready for another reception */ I2Cx->CR1 |= CR1_ACK_Set; } return Success; }此代码实现了中断或者轮训方式的I2C操作。 由于软件操作的时间问题,例如在主接收模式中(Master receiver)为了保证在最后一个接受到的数据之后发送NACK,必须在最后一次 RxNE 位被设置之前将ACK位clear。因此针对接受的数据长度不同,代码的实现均不一样。
下面是官方描述 When 3 bytes remain to be read: – RxNE = 1 => Nothing (DataN-2 not read). – DataN-1 received – BTF = 1 because both shift and data registers are full: DataN-2 in DR and DataN-1 in the shift register => SCL tied low: no other data will be received on the bus. – Clear ACK bit – Read DataN-2 in DR => This starts DataN reception in the shift register. – DataN received (with a NACK) – Program START/STOP – Read DataN-1 – RxNE = 1 – Read DataN
Case of a single byte to be received: – In the ADDR event, clear the ACK bit. – Clear ADDR – Program the STOP/START bit. – Read the data after the RxNE flag is set.
Case of two bytes to be received: – Set POS and ACK – Wait for the ADDR flag to be set – Clear ADDR – Clear ACK – Wait for BTF to be set – Program STOP – Read DataN-1 – Read DataN
有关资料在这
