2020-02-18 05:31 AM
Hello,
I am trying to write software to send and receive data using I2C - communication between STM and BNO055.
I have problem with that my software hangs on loops checking states on BUSY or START flags.
Based on: https://www.st.com/en/embedded-software/stsw-stm32020.html
Init:
void i2c_Init(uint16_t bus_speed)
{
gpio_pin_cfg(GPIOC, 9, GPIO_AF4_OD_50MHz_PULL_UP); //sda
gpio_pin_cfg(GPIOA, 8, GPIO_AF4_OD_50MHz_PULL_UP); //scl
RCC->APB1ENR |= RCC_APB1ENR_I2C3EN;
I2C3->CR1 |= I2C_CR1_SWRST; //Software reset 1
I2C3->CR1 &= ~ I2C_CR1_SWRST; //Software reset 0
I2C3->CR1 = 0;
I2C3->CR2 = APB1_CLOCK_FREQ/1000U; //Value in Mhz ex. 42Mhz
I2C3->CCR = (APB1_CLOCK_FREQ / (4U*1000U * bus_speed)); //APB1/(i2c_freq*2) - 400 khz;
I2C3->TRISE = 43; //1000ns ((max t_rise[ns]) / (1/APB1_CLOCK_FREQ))+1
I2C3->CR1 = I2C_CR1_PE;
/* 1 bit for pre-emption priority, 3 bits for subpriority */
//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
// NVIC_SetPriority(I2C3_EV_IRQn, 0x00);
NVIC_EnableIRQ(I2C3_EV_IRQn);
//NVIC_SetPriority(I2C3_ER_IRQn, 0x01);
NVIC_EnableIRQ(I2C3_ER_IRQn);
}
Function for receiving data:
void i2cReadInterrupt(I2C_TypeDef *I2Cx, uint8_t *data_buffer, uint8_t slave_address, uint8_t register_address, uint32_t lenght)
{
//Enable I2C errors interrupts
I2Cx->CR2 |= I2C_CR2_ITERREN;
/* Enable EVT IT*/
I2Cx->CR2 |= I2C_CR2_ITEVTEN;
/* Enable BUF IT */
I2Cx->CR2 |= I2C_CR2_ITBUFEN;
/* Set the I2C direction to reception */
I2CDirection = I2C_DIRECTION_RX;
slave_address |= I2CDirection;
Address = slave_address;
NumbOfBytes1 = lenght;
//Send START condition */
I2Cx->CR1 |= I2C_CR1_START;
/* Wait until the START condition is generated on the bus: START bit is cleared by hardware */
while ((I2Cx->CR1 & I2C_CR1_START));
/* Wait until BUSY flag is reset (until a STOP is generated) */
while (!(I2Cx->SR2 & I2C_SR2_BUSY));
/* Enable Acknowledgement to be ready for another reception */
I2Cx->CR1 |= I2C_CR1_ACK;
}
function for sending data:
void i2cWriteInterrupt(I2C_TypeDef *I2Cx,uint8_t slave_adress, uint8_t *data, uint32_t lenght)
{
//Enable Error IT
I2Cx->CR2 |= I2C_CR2_ITERREN;
/* Enable EVT IT*/
I2Cx->CR2 |= I2C_CR2_ITEVTEN;
/* Enable BUF IT */
I2Cx->CR2 |= I2C_CR2_ITBUFEN;
/* Set the I2C direction to Transmission */
I2CDirection = I2C_DIRECTION_TX;
slave_adress &= I2C_OAR1_ADD0_Msk;
Address = slave_adress;
/*if (I2Cx == I2C1) */ NumbOfBytes1 = lenght;
/*else*//* NumbOfBytes2 = lenght;*/
/* Send START condition */
I2Cx->CR1 |= I2C_CR1_START;
/* Wait until the START condition is generated on the bus: the START bit is cleared by hardware */
while ((I2Cx->CR1 & I2C_CR1_START));
/* Wait until BUSY flag is reset: a STOP has been generated on the bus signaling the end
of transmission */
while (!(I2Cx->SR2 & I2C_SR2_BUSY));
}
Interrupts:
void I2C3_EV_IRQHandler(void)
{
/* If SB = 1, I2C3 master sent a START on the bus: EV5) */
if ((I2C3->SR1 & I2C_SR1_SB))
{
/* Send the slave address for transmssion or for reception (according to the configured value
in the write master write routine */
I2C3->DR = Address;
I2C3->SR1 = 0;
I2C3->SR2 = 0;
}
/* If I2C3 is Master (MSL flag = 1) */
if (I2C3->SR2 & I2C_SR2_MSL)
{
/* If ADDR = 1, EV6 */
if ((I2C3->SR1 & I2C_SR1_ADDR))
{
/* Write the first data in case the Master is Transmitter */
if (I2CDirection == I2C_DIRECTION_TX)
{
/* Initialize the Transmit counter */
Tx_Idx1 = 0;
/* Write the first data in the data register */
I2C3->DR = Buffer_Tx1[Tx_Idx1++];
/* Decrement the number of bytes to be written */
NumbOfBytes1--;
/* If no further data to be sent, disable the I2C BUF IT
in order to not have a TxE interrupt */
if (NumbOfBytes1 == 0)
{
I2C3->CR2 &= (uint16_t)~I2C_CR2_ITBUFEN;
}
}
/* Master Receiver */
else
{
/* Initialize Receive counter */
Rx_Idx1 = 0;
/* At this stage, ADDR is cleared because both SR1 and SR2 were read.*/
/* EV6_1: used for single byte reception. The ACK disable and the STOP
Programming should be done just after ADDR is cleared. */
if (NumbOfBytes1 == 1)
{
/* Clear ACK */
I2C3->CR1 &=~ I2C_CR1_ACK;
/* Program the STOP */
I2C3->CR1 |= I2C_CR1_STOP;
}
}
I2C3->SR1 = 0;
I2C3->SR2 = 0;
}
/* Master transmits the remaing data: from data2 until the last one. */
/* If TXE is set */
if ((I2C3->SR1 & (I2C_SR1_TXE | I2C_SR1_BTF)))
{
/* If there is still data to write */
if (NumbOfBytes1!=0)
{
/* Write the data in DR register */
I2C3->DR = Buffer_Tx1[Tx_Idx1++];
/* Decrment the number of data to be written */
NumbOfBytes1--;
/* If no data remains to write, disable the BUF IT in order
to not have again a TxE interrupt. */
if (NumbOfBytes1 == 0)
{
/* Disable the BUF IT */
I2C3->CR2 &= (uint16_t)~I2C_CR2_ITBUFEN;
}
}
I2C3->SR1 = 0;
I2C3->SR2 = 0;
}
/* If BTF and TXE are set (EV8_2), program the STOP */
if ((I2C3->SR1 & I2C_SR1_BTF) && (I2C3->SR1 & I2C_SR1_TXE))
{
/* Program the STOP */
I2C3->CR1 |= I2C_CR1_STOP;
/* Disable EVT IT In order to not have again a BTF IT */
I2C3->CR2 &= (uint16_t)~I2C_CR2_ITEVTEN;
I2C3->SR1 = 0;
I2C3->SR2 = 0;
}
/* If RXNE is set */
if ((I2C3->SR1 & I2C_SR1_RXNE))
{
/* Read the data register */
Buffer_Rx1[Rx_Idx1++] = I2C3->DR;
/* Decrement the number of bytes to be read */
NumbOfBytes1--;
/* If it remains only one byte to read, disable ACK and program the STOP (EV7_1) */
if (NumbOfBytes1 == 1)
{
/* Clear ACK */
I2C3->CR1 &=~ I2C_CR1_ACK;
/* Program the STOP */
I2C3->CR1 |= I2C_CR1_STOP;
}
I2C3->SR1 = 0;
I2C3->SR2 = 0;
}
}
}
void I2C3_ER_IRQHandler(void)
{
/* Read the I2C3 status register */
I2C3->SR1 = I2C3->SR1;
/* If AF = 1 */
if ((I2C3->SR1 & I2C_SR1_AF) )
{
I2C3->SR1 &= 0xFBFF;
I2C3->SR1 = 0;
}
/* If ARLO = 1 */
if ((I2C3->SR1 & I2C_SR1_ARLO))
{
I2C3->SR1 &= 0xFBFF;
I2C3->SR1 = 0;
}
/* If BERR = 1 */
if ((I2C3->SR1 & I2C_SR1_BERR))
{
I2C3->SR1 &= 0xFEFF;
I2C3->SR1 = 0;
}
/* If OVR = 1 */
if ((I2C3->SR1 & I2C_SR1_OVR))
{
I2C3->SR1 &= 0xF7FF;
I2C3->SR1 = 0;
}
}
Executing function for sending and receiving data in BNO055 API:
s8 BNO055_I2C_bus_read(u8 dev_addr, u8 reg_addr, u8 *reg_data, u8 cnt)
{
s32 BNO055_iERROR = BNO055_INIT_VALUE;
//u8 array[I2C_BUFFER_LEN] = { BNO055_INIT_VALUE };
// Buffer_Rx1[0] = { BNO055_INIT_VALUE };
u8 stringpos = BNO055_INIT_VALUE;
Buffer_Rx1[BNO055_INIT_VALUE] = reg_addr;
i2cReadInterrupt(I2C3,0,dev_addr,reg_addr,cnt);
/* Please take the below API as your reference
* for read the data using I2C communication
* add your I2C read API here.
* "BNO055_iERROR = I2C_WRITE_READ_STRING(DEV_ADDR,
* ARRAY, ARRAY, 1, CNT)"
* BNO055_iERROR is an return value of SPI write API
* Please select your valid return value
* In the driver BNO055_SUCCESS defined as 0
* and FAILURE defined as -1
*/
for (stringpos = BNO055_INIT_VALUE; stringpos < cnt; stringpos++)
{
*(reg_data + stringpos) = Buffer_Rx1[stringpos];
}
return (s8)BNO055_iERROR;
}
s8 BNO055_I2C_bus_write(u8 dev_addr, u8 reg_addr, u8 *reg_data, u8 cnt)
{
s32 BNO055_iERROR = BNO055_INIT_VALUE;
// u8 array[I2C_BUFFER_LEN];
u8 stringpos = BNO055_INIT_VALUE;
Buffer_Tx1[BNO055_INIT_VALUE] = reg_addr;
for (stringpos = BNO055_INIT_VALUE; stringpos < cnt; stringpos++)
{
Buffer_Tx1[stringpos + BNO055_I2C_BUS_WRITE_ARRAY_INDEX] = *(reg_data + stringpos);
}
i2cWriteInterrupt(I2C3,dev_addr, 0,cnt);
return (s8)BNO055_iERROR;
}
2020-02-18 12:28 PM
Just a remark: Be aware that the BNO055 makes use of the I2C clock stretching feature.
2020-02-18 01:43 PM
Observe SCL and SDA using oscilloscope/logic analyzer.
JW