As discussed in previous posts comparing the 32F103 microcontrollers, there are several I²C issues on the MegaHunt MH32F103. Disclaimer: The parts are assumed to be MegaHunt MH32F103. I have been told the “STM32F103 China” Blue Pills from AliExpress contain a MegaHunt MH32F103. As these parts have been relabelled as STM parts, I cannot tell for sure these are MegaHunt parts.

Anyhow, let’s look at the issue. In the I²C tests I have done previously, I’ve excluded the HCD1080 humidity sensor from the MH32F103 tests, as attempting to read this sensor would trash the whole I²C communication. After a failed attempts to read the sensor, no further communication would be possible on the I²C bus.

When we look at the communication happening between the MH32F103 and HCD1080, we see the microcontroller addresses for sending data, received an ACK, and then sends 0x00. Then the microcontroller addresses for reading and received a NACK. After this the communication is stalled.

When we look at the same communication between an STM32F103 and HCD1080, we see the same, but then, after 10 ms, the microcontroller attempts to read again, received another NACK, and then, another 10 ms later, the HCD1080 ACKs and gives two bytes of data.

This behaviour corresponds with the following code I am using:

status = bshal_i2cm_send(hcd1080->p_i2c, hcd1080->addr, &cmd, 
		sizeof(cmd), false);
if (status)
	return status;

for (int i = 0; i < 3; i++) {
	status = bshal_i2cm_recv(hcd1080->p_i2c, hcd1080->addr, &value,
			sizeof(value), false);
	if (!status)
		break;
	bshal_delay_ms(10);
}

Now, if we replace the loop with a fixed delay of 30 ms, as the third attempt of reading succeeded on the STM32, thus running the following code:

status = bshal_i2cm_send(hcd1080->p_i2c, hcd1080->addr, &cmd, 
		sizeof(cmd), false);
if (status)
	return status;
bshal_delay_ms(30);
status = bshal_i2cm_recv(hcd1080->p_i2c, hcd1080->addr, &value,
			sizeof(value), false);

The read succeeds on the MH32. It seems, receiving a NACK when attempting to read throws the MH32 off. After more investigation, it appears to be the case that, once a NACK is recveived, the BUSY bit in the SR2 register is stuck set. Performing a software reset and re-initialisation of the I²C peripheral restores it in a working state.

Something like the following code seems to work. Performing the Software Reset is probably not needed as the HAL_I2C_Init will perform this as well.

int result = HAL_I2C_Master_Transmit(hi2c, address << 1, p_data,
		length, 10);
if (result) {
	if (hi2c->Instance->SR2 & I2C_SR2_BUSY) {
		// On the MH32 the BUSY bit is stuck when a NACK is received
		// We need to Software Reset the I²C peripheral
		hi2c->Instance->CR1 |= I2C_CR1_SWRST;
		hi2c->Instance->CR1 &= ~I2C_CR1_SWRST;
		HAL_I2C_Init(drv_specific);
	}
}

This is so far the case when running at 100 kHz. At 400 kHz the I²C peripheral gets stuck in a state not recoverable by a software reset. According to the documentation, one should configure the Duty Cycle to I2C_DUTYCYCLE_2 when running at 100 kHz, and configure the Duty Cycle to I2C_DUTYCYCLE_16_9 when running at 400 kHz. It seems, the Duty Cycle of I2C_DUTYCYCLE_16_9 is broken at the MH32F103. Configuring the Duty Cycle to I2C_DUTYCYCLE_2 appears to solve that issue running at 400 kHz.

This solves some of the problems with the MH32F103 I²C Peripheral, but not all issues are resolved yet. I’ve been emailing with Waldek Hebisch. Waldek told me about timing issues with the registers. The registers get read before they get updated. This might explain some of the remaining issues. Furthermore, I am compiling without optimisation at this point of investigation. I’d imagine having such timing issues might cause things to break when I enable optimisation.

So far: Running I²C on the MH32F103: re-initialise when receiving a NACK. Set Duty Cycle to I2C_DUTYCYCLE_2 regardless of the bus speed.