cancel
Showing results for 
Search instead for 
Did you mean: 

Connecting Raspberry PI to STM32F105xx via I2C.

SSoko
Associate II

Hi,

I am trying to connect raspberry Pi with STM32 via I2C.. for 2 weeks now.. going to the specifics: RPI3 is a master, STM32 is a slave, I use HAL library and CubeMX. All communication should be the simplest possible. I want to send / receive one byte - nothing more. I have connected a logic analyzer and UART console for debugging purposes.

Maybe I'll show my code first.

Init I2C:

/* I2C2 init function */
void MX_I2C2_Init(void)
{
  hi2c2.Instance = I2C2;
  hi2c2.Init.ClockSpeed = 62500;
  hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c2.Init.OwnAddress1 = 32;
  hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c2.Init.OwnAddress2 = 0;
  hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_ENABLE;
  if (HAL_I2C_Init(&hi2c2) != HAL_OK)
  {
    Error_Handler();
  }
 
}
 
void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{
 
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(i2cHandle->Instance==I2C1)
  {
  /* USER CODE BEGIN I2C1_MspInit 0 */
 
  /* USER CODE END I2C1_MspInit 0 */
  
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**I2C1 GPIO Configuration    
    PB6     ------> I2C1_SCL
    PB7     ------> I2C1_SDA 
    */
    GPIO_InitStruct.Pin = SCL_EEPROM_Pin|SDA_EEPROM_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
 
    /* I2C1 clock enable */
    __HAL_RCC_I2C1_CLK_ENABLE();
  /* USER CODE BEGIN I2C1_MspInit 1 */
 
  /* USER CODE END I2C1_MspInit 1 */
  }
  else if(i2cHandle->Instance==I2C2)
  {
  /* USER CODE BEGIN I2C2_MspInit 0 */
 
  /* USER CODE END I2C2_MspInit 0 */
  
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**I2C2 GPIO Configuration    
    PB10     ------> I2C2_SCL
    PB11     ------> I2C2_SDA 
    */
    GPIO_InitStruct.Pin = SCL_RP3_Pin|SDA_RP3_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
 
    /* I2C2 clock enable */
    __HAL_RCC_I2C2_CLK_ENABLE();
 
    /* I2C2 interrupt Init */
    HAL_NVIC_SetPriority(I2C2_EV_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(I2C2_EV_IRQn);
    HAL_NVIC_SetPriority(I2C2_ER_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(I2C2_ER_IRQn);
  /* USER CODE BEGIN I2C2_MspInit 1 */
 
  /* USER CODE END I2C2_MspInit 1 */
  }
}

All Callbacks:

void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	debug_tx("HAL_I2C_AddrCallback\r\n");
 
	UNUSED(hi2c);
	//UNUSED(TransferDirection);
	UNUSED(AddrMatchCode);
 
	if(TransferDirection)
	{
		RPI_State = API_RPI_TO_STM;
		debug_tx("DIR: RPI_TO_STM\r\n");
	}
	else
	{
		RPI_State = API_STM_TO_RPI;
		debug_tx("DIR: STM_TO_RPI\r\n");
	}
}
 
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{
	/* Prevent unused argument(s) compilation warning */
	UNUSED(hi2c);
 
	debug_tx("HAL_I2C_ListenCpltCallback\r\n");
 
	if(RPI_State == API_RPI_TO_STM)
	{
		if (HAL_I2C_Slave_Receive_IT(&hi2c2, rx_buf, 1) != HAL_OK)
		{
			Error_Handler();
		}
	}
	else if(RPI_State == API_STM_TO_RPI)
	{
		lcd_string(4, 0, "SLAVE TX 1 ");
		if (HAL_I2C_Slave_Transmit_IT(&hi2c2, tx_buf, 1) != HAL_OK)
		{
			Error_Handler();
			lcd_string(5, 0, "SLAVE TX ER");
		}
	}
}
 
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	debug_tx("HAL_I2C_SlaveTxCpltCallback\r\n");
}
 
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	debug_tx("HAL_I2C_SlaveRxCpltCallback\r\n");
 
	lcd_hex(5, 0, rx_buf[0]);
	HAL_I2C_EnableListen_IT(&hi2c2); // Restart
}
 
/* USER CODE END 4 */
 
/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
	debug_tx("Error_Handler\r\n");
 
  /* USER CODE END Error_Handler_Debug */
}

When RPI sends:

0690X00000BvV84QAF.png

in the console I can see:

0690X00000BvV89QAF.png

Doesn't enter "HAL_I2C_SlaveRxCpltCallback". If RPI sends an identical frame a second time then it goes into "HAL_I2C_SlaveRxCpltCallback".

0690X00000BvV8EQAV.png

Byte reception works every second time.

When I want to send some byte to RPI3 it's even worse. The frame looks like this:

0690X00000BvV8JQAV.png

From RPI's point of view, I'm only sending an address and waiting for 1 byte. I have no idea why STM sends these 2 bytes back. When we look at the console:

0690X00000BvV8OQAV.png

You can see that it enters only the "HAL_I2C_AddrCallback". And it misreads the direction flag.

When I change "NoStretchMode" to DISABLE it's even worse. It is only better that it reads the direction flag correctly.

Do you have any idea what I'm doing wrong? Or maybe there are some bugs in HAL? I really got stuck. I will be veery grateful for any help.

Best regards,

Sebastian

10 REPLIES 10
KnarfB
Principal III

Are you aware aof the raspi clock stretching bug http://www.advamation.com/knowhow/raspberrypi/rpi-i2c-bug.html?

SSoko
Associate II

I wasn't aware of this, thank you very much. Fortunately, clock of uC is probably fast enough so I don't have to use stretching (after all MUXs/PLLs is 72 MHz).

However, this still doesn't solve my problem. Most of the time I tried to solve it without stretching. In STM32F105VCT6 is also a bug in I2C?

KnarfB
Principal III

Once I have implemented I2C slave comm for another board (NUCLEO-F401RE). The STM32 acts on i2C like a tiny I2C EPRROM/RAM.

To keep the timing, keep the printf's short or use GPIOs as debugging aid.

hth

KnarfB

// emulated I2C RAM
static uint8_t ram[256];
static uint8_t offset; 	// index of current RAM cell
static uint8_t first=1;	// first byte --> new offset
 
#define PRINTF(...) printf(__VA_ARGS__)
 
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{
	PRINTF("LCB\n");
	first = 1;
	HAL_I2C_EnableListen_IT(hi2c); // slave is ready again
}
 
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	PRINTF("ACB %s\n", TransferDirection==I2C_DIRECTION_RECEIVE ? "rx" : "tx" );
	HAL_GPIO_WritePin( LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET );
	if( TransferDirection==I2C_DIRECTION_TRANSMIT ) {
		if( first ) {
			HAL_I2C_Slave_Seq_Receive_IT(hi2c, &offset, 1, I2C_NEXT_FRAME);
		} else {
			HAL_I2C_Slave_Seq_Receive_IT(hi2c, &ram[offset], 1, I2C_NEXT_FRAME);
		}
	} else {
		HAL_I2C_Slave_Seq_Transmit_IT(hi2c, &ram[offset], 1, I2C_NEXT_FRAME);
	}
	HAL_GPIO_WritePin( LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET );
}
 
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	HAL_GPIO_WritePin( PA6_GPIO_Port, PA6_Pin, GPIO_PIN_SET );
	if(first) {
		PRINTF("RXCB: offset <== %3d\n", offset );
		first = 0;
	} else {
		PRINTF("RXCB: ram[%3d] <== %3d\n", offset,  ram[offset] );
		offset++;
	}
	HAL_I2C_Slave_Seq_Receive_IT(hi2c, &ram[offset], 1, I2C_NEXT_FRAME);
	HAL_GPIO_WritePin(PA6_GPIO_Port, PA6_Pin, GPIO_PIN_RESET );
}
 
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	HAL_GPIO_WritePin( PA7_GPIO_Port, PA7_Pin, GPIO_PIN_SET );
	PRINTF("TXCB: ram[%3d] ==> %3d\n", offset, ram[offset] );
	offset++;
	HAL_I2C_Slave_Seq_Transmit_IT(hi2c, &ram[offset], 1, I2C_NEXT_FRAME);
	HAL_GPIO_WritePin( PA7_GPIO_Port, PA7_Pin, GPIO_PIN_RESET );
}

SSoko
Associate II

Thank you,

I did the same on NUCLEO-F401RE (based on your code) and it works like a charm.

Both receiving and transmitting.

0690X00000BvYsjQAF.png

When I try to do it identical on my board with STM32F105VCT I get that ..

0690X00000BvYsoQAF.png

STM sends the address again instead of 0x44. The code is really the same.

To make it "funnier", receiving from STM works OK.

I really have no idea. Do you think this is a bug in uC? I have read the errata about this chip and found nothing to match this behavior.

Below I paste my absolutely simplified code to a minimum. Maybe someone had similar problems?

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "gpio.h"
 
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
 
/* USER CODE END Includes */
 
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
 
uint8_t tx_buf[1] = {0x44}, rx_buf[1];
 
/* USER CODE END PTD */
 
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
 
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
 
/* Private variables ---------------------------------------------------------*/
 
/* USER CODE BEGIN PV */
/* USER CODE END PV */
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
 
/* USER CODE END PFP */
 
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
 
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */
  
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_I2C2_Init();
  /* USER CODE BEGIN 2 */
 
  HAL_I2C_EnableListen_IT(&hi2c2);
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
 
/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  RCC_OscInitStruct.PLL2.PLL2State = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure the Systick interrupt time 
  */
  __HAL_RCC_PLLI2S_ENABLE();
}
 
/* USER CODE BEGIN 4 */
 
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{
	HAL_I2C_EnableListen_IT(&hi2c2); // Restart
}
 
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	UNUSED(AddrMatchCode);
 
	if(TransferDirection == I2C_DIRECTION_TRANSMIT)
		HAL_I2C_Slave_Sequential_Receive_IT(&hi2c2, &rx_buf, 1, I2C_LAST_FRAME);
	else
		HAL_I2C_Slave_Sequential_Transmit_IT(&hi2c2, &tx_buf[0], 1, I2C_LAST_FRAME);
	
}
 
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
}
 
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
}
 
/* USER CODE END 4 */
 
/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
 
  /* USER CODE END Error_Handler_Debug */
}
 
#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{ 
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

KnarfB
Principal III

Glad to hear it works somehow. &rx_buf looks wrong in your code, shouldn't it be &rx_buf[0]?. I have no experience with F1, sorry.

Piranha
Chief II

> Or maybe there are some bugs in HAL?

> STM sends the address again instead of 0x44. The code is really the same.

HAL code is not the same, because the MCU series and I2C peripheral are different. And with such a behavior, it's almost certainly a HAL bug. HAL and CubeMX generated bloatware are so full of bugs, that it can't work reliably!

> &rx_buf looks wrong in your code, shouldn't it be &rx_buf[0]

For arrays rx_buf, &rx_buf and &rx_buf[0] are all the same pointer.

debugging
Senior III

I have an RPI3 with and F103 and have exactly the same problem SSoko describes in the 1st post. All transaction on ths bus are ACKEdl but upon the 1st transaction the address and address complete callback are called and state moves from 0x28 to 0x22 upon the second the rxCallback is called and state changed from 0x22 to 0x28. This is no matter how many bytes the master sends or specified in the Receive IT call. hopefully someone from ST can look at this . . P.S: I had to disabled clock stretching otherwise it cuses many other problems. Also enabling the ER interrupt causes transaction to fail so only enabled the EV interrupt.

https://community.st.com/s/profile/0050X000008AxIY

HAL and CubeMX generated bloatware are so full of bugs, that it can't work reliably!

This is worrysome and voiced in many other post, but is this true and how can this be after so many years, and how are developers supposed to create product solutions if the libraries are unreliable ? What is the alternative ST suggests ? The Low Level library or just every developer has to to go rewrite custom code from scratch at the register level ? that would be non sense. 30 years back I wrote I2C reliable code in assembly in a few hundred bytes and now with C/C++ and stuff and many K's available this result is unreliable libraries ?

P.S: I could not find any example code from ST in where the callbacks are used.

VThan.2
Associate III

Hello all, Thank you for this post. I got this working with the Stm32l series too. Works like a charm.

I may be wrong, but why HAL_I2C_Slave_Seq_Receive_IT/HAL_I2C_Slave_Seq_Transmit_IT are called second time in HAL_I2C_SlaveRxCpltCallback/HAL_I2C_SlaveTxCpltCallback? This example works even if I comment out these two lines and leave HAL_I2C_AddrCallback unchanged:

void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	UNUSED(AddrMatchCode);
 
	if(TransferDirection == I2C_DIRECTION_TRANSMIT)
		HAL_I2C_Slave_Sequential_Receive_IT(&hi2c2, &rx_buf, 1, I2C_LAST_FRAME);
	else
		HAL_I2C_Slave_Sequential_Transmit_IT(&hi2c2, &tx_buf[0], 1, I2C_LAST_FRAME);
	
}

 

void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	HAL_GPIO_WritePin( PA6_GPIO_Port, PA6_Pin, GPIO_PIN_SET );
	if(first) {
		PRINTF("RXCB: offset <== %3d\n", offset );
		first = 0;
	} else {
		PRINTF("RXCB: ram[%3d] <== %3d\n", offset,  ram[offset] );
		offset++;
	}
	// HAL_I2C_Slave_Seq_Receive_IT(hi2c, &ram[offset], 1, I2C_NEXT_FRAME);
	HAL_GPIO_WritePin(PA6_GPIO_Port, PA6_Pin, GPIO_PIN_RESET );
}
 
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	HAL_GPIO_WritePin( PA7_GPIO_Port, PA7_Pin, GPIO_PIN_SET );
	PRINTF("TXCB: ram[%3d] ==> %3d\n", offset, ram[offset] );
	offset++;
	// HAL_I2C_Slave_Seq_Transmit_IT(hi2c, &ram[offset], 1, I2C_NEXT_FRAME);
	HAL_GPIO_WritePin( PA7_GPIO_Port, PA7_Pin, GPIO_PIN_RESET );
}