cancel
Showing results for 
Search instead for 
Did you mean: 

HAL for I2C is horribly broken

etheory
Senior

Hi STM Community,

I would like an answer from STM about the status of I2C in the HAL drivers.

Basically, they are broken, and still broken in STM32CubeIDE-1.7.0.

Of the many issues, there is still an open one here: https://github.com/STMicroelectronics/stm32l0xx_hal_driver/issues/5 and in fact even the examples provided don't actually work either (due to these issues with the _IT and _DMA functions).

For instance:

  • HAL_I2C_Slave_Receive_IT and HAL_I2C_Slave_Receive_DMA never call HAL_I2C_SlaveRxCpltCallback or any other callbacks for that matter (I've tested this exhaustively), ever, making them unusable in practise. This desperately needs to be fixed.
  • Confusingly HAL_I2C_Master_Transmit_IT and HAL_I2C_Master_Transmit_DMA DO work, and DO call HAL_I2C_MasterTxCpltCallback.

Is there an actual effort on STM's behalf to fix these functions, and actually test their OWN API's with hardware?

If so, is there a time-frame for these glaring issues to be fixed?

Has anyone ACTUALLY been able to get these simple functions working?

Also, is there any ACTUAL documentation for the usages of:

  • HAL_I2C_Mem_Write
  • HAL_I2C_Mem_Read
  • HAL_I2C_Master_Seq_Transmit_*
  • HAL_I2C_Master_Seq_Receive_*

Since the provided documentation is woefully inadequate, and there are zero examples provided with the Example folders on how to use these in context (which probably means they are broken, like a lot of HAL is).

It'd just be good to know what the plans are from STM themselves, since at this point I don't want to have to write my own drivers, because that's a huge undertaking, and these things should "just work".

Also, are STM open to collaborations on fixing HAL? If so how do we do that?

Regards,

etheory.

1 ACCEPTED SOLUTION

Accepted Solutions
T J
Lead

Try very High speed on the pins, usually fixes it...

View solution in original post

9 REPLIES 9
T J
Lead

Try very High speed on the pins, usually fixes it...

@Community member​ Thanks, but since the blocking functions work perfectly, I'm pretty sure that your suggestion will neither fix the issue, nor is the cause of the issue. I've already provided links to other people's well-shown bugs with HAL, so I can't see how you suggestion is related to the above.

Have you ever gotten the above functions to work and fire the callbacks yourself?

@Community member​ - I take it back, you are right, that absolutely fixed the issue.... I can't believe how stupid that is.... THANK YOU!

MMAST.1
Associate II

If the configuration of the IO speed leads to a different behavior I would say most likely , the issue is coming from a bad I2C timing configuration (So maybe it would be better to follow this trail.

Pavel A.
Evangelist III

Well I don't know about slave mode, but these two APIs are widely used, for I2C memories and many other devices.

  • HAL_I2C_Mem_Write
  • HAL_I2C_Mem_Read

They do transactions with repeated start. The 1st part is write (offset of memory or command to other devices) and the 2nd part is write or read data.

The 1st part (command) can be one or two bytes. This suffices for many devices.

For example the next line reads EEPROM with address 0xA2, 8 bytes from offset 0x10:

HAL_I2C_Mem_Read(&hi2c, 0xA2, 0x10, I2C_MEMADD_SIZE_8BIT, buf, 8, 10 /*ms*/);

When the address/command is I2C_MEMADD_SIZE_16BIT, the high byte goes first - because big endian.

Garnett.Robert
Senior III

Hi,

I've used the HAL I2C Drivers with a couple of different I2C devices namely the Bosch BME280 Barometric Pressure sensor and the Vishay VCNL4010 Proximity and Light Intensity sensor.

I always use DMA along with FreeRTOS using deferred interrupts, i.e. binary semaphores.

I always set the pins to high speed.

I have had no problems at all although I never use the callbacks embedded in the HAL Interrupt handlers I put anything I need in the interrupt handler directly. This avoids spaghetti code with unnecessary pointer etc. I often delete the call back code from the HAL drivers. I know this makes them non-standard, but I don't care I just want the project to work efficiently. I like the HAL Drivers, but I am always prepared to modify them to meet my needs. I do think they need to do a lot more testing and they desperately need a decent bug reporting and followup system. It also annoys me the version numbers aren't in the file headers.

I use an I2C Re-drivers: PCA9601 and am driving the VCN410 with a clock frequency of 833 KHz over a 1 meter piece of flat phone cable.

Bosch and Vishay both supply drivers for there devices which only require pointers to the I2C low level driver commands, read write etc.

I wrote a driver that can use blocking commands or DMA deferred interrupt processing. I have never used the blocking commands as I always use FeeRTOS unless the project is a trivial real time system where speed is the priority and there are no complex comms.

I use the same low level HAL driver code with both devices.

The HAL Section of the I2C Driver for the the VCNL4010 is shown below:

#ifdef USE_DMA_I2C_1_ON
/**************************** I2C Helper Functions *****************************/
 
/* Non Blocking functions for use with FreeRTOS */
/********************************* Write To Prox Probe *****************************/
 /*	\Brief: The function is used as I2C bus write
 *	\Return : Status of the I2C write
 *	\param dev_addr : The device address of the sensor
 *	\param reg_addr : Address of the first register, will data is going to be written
 *	\param reg_data : Data to be written
 *	\param cnt : The no of bytes of data to write
 */
__attribute__((section(".Tim2itcm"))) void I2C_Write_DMA(uint8_t i2C_address, char *txBuff, uint16_t Size)
{
	osSemaphoreAcquire(i2c_Prox_Tx_IRQSemHandle, 0);
	#if PRINTF_ON == 1 & INC_PROXTASKHANDLER_H_DEBUG == 1
	c1++;
  printf("I2C_Write_DMA: c1 = %d\n", c1);
	#endif
 
	while (__HAL_I2C_GET_FLAG(&I2C_HAND_1, I2C_FLAG_BUSY) == SET)
	{
		i2cWriteDelayCount++;
	}
 
	
	#if USE_DCACHE_ON == 1 && USE_DCACHE_WITH_I2C1 == 1
	SCB_CleanDCache_by_Addr((uint32_t *)&txBuff, Size);
	#endif
	
 
	/* Write I2C data */
	if (HAL_I2C_Master_Transmit_DMA(&I2C_HAND_1, (uint16_t)i2C_address, (uint8_t*)txBuff, (uint16_t)Size) != HAL_OK)
	{
		return; // HAL_I2C_ERR;
	}		
	
	if (osSemaphoreAcquire(i2c_Prox_Tx_IRQSemHandle, 10 * portTICK_PERIOD_MS) == osErrorTimeout)
	{
		return; // I2C_Error;
	}
}
 
 
/********************************* Read From VCNL40x0 *****************************/
 /*	\Brief: The function is used as I2C bus read
 *	\Return : Status of the I2C read
 *	\param dev_addr : The device address of the sensor
 *	\param reg_addr : Address of the first register, will data is going to be read
 *	\param reg_data : Pointer to data buffer
 *	\param cnt : Size Amount of data to be read
 */
__attribute__((section(".Tim2itcm"))) void I2C_Read_DMA(uint8_t i2C_address,  char *rxBuff, uint16_t Size)
{
 
	osSemaphoreAcquire(i2c_Prox_Rx_IRQSemHandle, 0);
	
	#if PRINTF_ON == 1
  printf("Start I2C Read\n");
	#endif
	
	I2C_TimeoutCount = 2;
	I2C_TimeoutError = 0;
 
 
	while (__HAL_I2C_GET_FLAG(&I2C_HAND_1, I2C_FLAG_BUSY) == SET)
	{
		i2cReadDelayCount++;
	}
 
	/* Read from Prox Probe */
	if (HAL_I2C_Master_Receive_DMA(&I2C_HAND_1, (uint16_t)i2C_address, (uint8_t*)rxBuff, (uint16_t)Size) != HAL_OK)
	{
		#if PRINTF_ON == 1
	  printf("HAL Master Receive Error\n");
		#endif
		return; // DEVICE_E_COMM_FAIL;
	}
	
	
	if (osSemaphoreAcquire(i2c_Prox_Rx_IRQSemHandle, 1000 * portTICK_PERIOD_MS) == osErrorTimeout)
	{
		#if PRINTF_ON == 1
		printf("i2c_Prox_Rx_IRQSemHandle Sem Timeout\n");
		#endif
		return; // I2C_Error;
	}
	#if PRINTF_ON == 1
	printf("Start I2C Read\n");
	#endif
}
#else

I have attached my source files for both devices as zips.

I am in the process of removing the CMSIS OS calls and moving to native FreeRTOS so there's a bit of a mixture in the example above.

Anyway I hope this helps.

Best regards

Rob

S.Ma
Principal

Please specify which stm32 as there are several generation of i2c macrocells.

etheory
Senior

Hi all!

I just thought I'd provide an update on this situation.

It turns out that the suggestion to set the GPIO pin speed to high speed was the fix. If you don't basically nothing works, and it looks like API issues, but it's not. The STM32 API's are actually working great now that I've sorted that out.

So I take back what I said before about the broken API's and will say that instead the documentation is terrible and doesn't explain the ins and outs you need to know to get it working.

Also, for completeness, the series is the STM32G4 series, specifically I got the STM32G431KB talking to the STM32G474RE over I2C, and now that the GPIO speed is set correctly, I'm using the I2C HAL functions without issue.

Thanks to everyone who replied, it's much appreciated!

I ended up deciding to write my own. These functions are horribly inefficient as they busy-wait on the first transfer before continuing the second. I created my own code that can use interrupts such that the first address is sent, without busy waiting for the response, so my code can continue whilst that happens. It seems like HAL_I2C_Mem_Read and HAL_I2C_Mem_Write both busy-wait, which for me, is simply not an option.