2023-11-26 12:49 AM - edited 2023-11-26 12:53 AM
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;
}
}
2023-11-26 04:10 AM
why do you don't use regular HAL Library?
2023-11-26 04:59 AM
Want to keep things 'simple' and work bare metal. Thought that would be better to learn the hardware without an abstraction layer inbetween.
2023-11-26 05:32 AM
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.
2023-11-29 08:56 AM - edited 2023-12-01 04:35 PM
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.