cancel
Showing results for 
Search instead for 
Did you mean: 

STM320L0 I2C timing issue?

I'm working on a board with a STM32L010K.

For some reason, I can't get the I2C code to work properly. I mean it works (data gets read from the slave) but not if a lot of things are done in succession, then a hard fault is thrown.

Below is the block read code and irq, Tick is a variable that gets incremented by the systemtick,

I've checked that actual coms with a saleae logic analyser and it works as expected. But for some reason the end of the block read isn't detected properly isr wise (or so it seems). The only way so far I've managed to get it working is by keeping track of the amount of bytes received and waiting for that, but that can't be the way to go?

The strange thing is that if i debug step from line 43 to 44, the stopf bit gets set in the isr for some reason (that's why line 44 removes it again, not that it solved the issue).

My current gues is that the code doesn't properly wait for a transfer to finish to start another one but no idea why...

Secondary question, is the CR2 NBytes supposed to decrement by itself?

 

 

uint8_t I2C1_ReadData( uint8_t i2c_Address, uint8_t reg, uint8_t length){
	uint32_t tickstart;


	tickstart = Tick;
	while((I2C1->ISR & I2C_ISR_BUSY) != 0 ){ // Wait till busy flag is lowered
		if ((Tick - tickstart ) > I2C_TIMEOUT_VALUE){
			return ERROR_I2C_NO_BUSY_FLAG;
		}
	}

	tickstart = Tick;
	while((I2C1->ISR & I2C_ISR_TXE) == 0){  // Wait till transfer buffer is empty
		if ((Tick - tickstart ) > I2C_TIMEOUT_VALUE){
			return ERROR_I2C_TRANSFER_BUSY;
		}
	}
	I2C1->TXDR = reg;  		// Load the first byte

	// No auto-end, set amount of bytes to send and the address shifted for R/W bit
	I2C1->CR2 = (1 << I2C_CR2_NBYTES_Pos) | (i2c_Address << 1);

	/* Setting start bit causes the device to send start condition followed by the slave address */
	I2C1->CR2 |= I2C_CR2_START; // Send start condition

	// When the start+address is done, this sets the BUSY flag (and removed the start bit)
	tickstart = Tick;
	while((I2C1->ISR & I2C_ISR_BUSY) == 0 ){ // Wait till busy flag is raised
		if ((Tick - tickstart ) > I2C_TIMEOUT_VALUE){
			return ERROR_I2C_NO_BUSY_FLAG;
		}
	}
	// When the register byte has been transferred the TC bit is set high, so wait for this
	tickstart = Tick;
	while( (I2C1->ISR & I2C_ISR_TC)==0 ){
		if ((Tick - tickstart ) > I2C_TIMEOUT_VALUE){
			return ERROR_I2C_NO_TC_DETECT;
		}
	}
	// At this point the reg has been transferred to the slave, now ask for data

	// Enable auto-end, set amount of bytes to receive and the address shifted for R/W bit
	I2C1->CR2 = I2C_CR2_AUTOEND | (length<<I2C_CR2_NBYTES_Pos) | I2C_CR2_RD_WRN |(i2c_Address<<1);
	SET_BIT(I2C1->ICR, I2C_ICR_STOPCF); // Clear flag
	I2C1->CR2 |= I2C_CR2_START; // Send start condition

	// Wait for the START condition detection, maximum 10ms
	tickstart = Tick;
	while((I2C1->ISR & I2C_ISR_BUSY) == 0 ){ // Wait till busy flag is raised
		if ((Tick - tickstart ) > I2C_TIMEOUT_VALUE){
			return ERROR_I2C_NO_BUSY_FLAG2;
		}
	}

	// Then check when BUSY flags gets cleared (STOP detected), 10ms max
	tickstart = Tick;
	while((I2C1->ISR & I2C_ICR_STOPCF) == 0){
		if ((Tick - tickstart ) > 200){
			return ERROR_I2C_NO_STOP_DT;
		}
	}
	SET_BIT(I2C1->ICR, I2C_ICR_STOPCF); // Clear flag
	return I2C_OK;
}
// IRQ handler
void I2C1_IRQHandler(void){

  if(I2C1->ISR & I2C_ISR_RXNE){ // Receive buffer not empty
	  if( bits == 8 ){
		  *i2c_rxBuffer_8bit = I2C1->RXDR;
		  i2c_rxBuffer_8bit++;
		  rec--;
	  }else if( bits == 16 ){
		  if( rec%2==0 ){
			  *i2c_rxBuffer_16bit = I2C1->RXDR; /* Read receive register, will clear RXNE flag */
			  *i2c_rxBuffer_16bit = *i2c_rxBuffer_16bit <<8; //Receiving MSB first, so shift it to msb
		  }else{
			  *i2c_rxBuffer_16bit += I2C1->RXDR; // Add the LSB
			  i2c_rxBuffer_16bit++; // Increment the pointer
		  }
	  }
  }
  if(I2C1->ISR & I2C_ISR_TXIS){ /*  Ready to send the next byte */
	  I2C1 ->TXDR = *i2c_txBuffer++; // Put the next byte
  }
  if(I2C1->ISR & I2C_ISR_NACKF){ /* NACK Received*/
	  SET_BIT(I2C1->ICR, I2C_ICR_NACKCF); // Clear flag
  }
  if(I2C1->ISR & I2C_ISR_STOPF){
	  SET_BIT(I2C1->ICR, I2C_ICR_STOPCF); // Clear flag
  }
  if(I2C1->ISR & I2C_ISR_TC){ // Transfer complete?
	  test=0;
  }
}

 

 

 

 

4 REPLIES 4
Tinnagit
Senior II

why do you don't use regular HAL Library?

Want to keep things 'simple' and work bare metal. Thought that would be better to learn the hardware without an abstraction layer inbetween.

Likely Hard Faults because you don't bounds check any buffers or pointers.

The 16-bit path doesn't decrement the byte count.

Need to show all related code and variables. 

Focus on the code/instruction that faults.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
  • Attached source and header that purely checks received byte count (renamed rec to 'toReceive')
  • Added check to make sure it doesn't go beyond expected bounds.
  • That decrement was missing because I was testing the code without rec.

The codes works and is stable. But aren't I supposed to use the isr flags to determine when the full transfer is complete? As mentioned earlier, I thought that the stopf would only be raised after an actual stop condition. But this doesn't seem to be the case. The other one was TC (transfer complete) but that doesn't work either.

Updated attachments on 02 dec 23.