cancel
Showing results for 
Search instead for 
Did you mean: 

I'm getting HAL_I2C_STATE_BUSY_RX

Mabou.1
Associate II

Hello,

I have developed a code in FreeRTOS mode that contains two main tasks. The first one check if I have received an RX buffer on the I2C bus, and the second one manage the data that has been received.

However, after receiving the first buffer, I am getting HAL_I2C_STATE_BUSY_RX, and I cannot transmit a response to the master device but I'm able to receive another RX buffer . Can anyone please help me?

I'm using I2C in DMA normal mode.


_legacyfs_online_stmicro_images_0693W00000bkVdiQAE.png32-> HAL_I2C_STATE_READY
_legacyfs_online_stmicro_images_0693W00000bkVdJQAU.png34-> HAL_I2C_STATE_BUSY_RX
_legacyfs_online_stmicro_images_0693W00000bkVd4QAE.png

15 REPLIES 15
Bob S
Principal

Does your receive code re-call the HAL_I2C_Receive_DMA() after it gets the first buffer?

Are you relying on the hi2c structure flags to tell the 2nd task when data is ready? You should be using a semaphore or other RTOS construct to communicate between tasks. The HAL code is NOT thread safe by itself.

Show more of your code, and please paste into the forums using the "</>" button below, not as screen capture images.

Foued_KH
ST Employee

Hello @Mabou.1​ ,

I couldn't understand your issue but maybe you can check the end of the transfer :

  do
  {
    if(HAL_I2C_Master_Transmit_DMA(&I2cHandle, (uint16_t)I2C_ADDRESS, (uint8_t*)TxBuffer, TXBUFFERSIZE)!= HAL_OK)
    {
      Error_Handler();
    }
    /* Wait for the end of the transfer */  
    /* Check the current state of the peripheral; if it’s busy you need to wait for the end of current
        transfer before starting a new one.*/  
    while (HAL_I2C_GetState(&I2cHandle) != HAL_I2C_STATE_READY)
    {
    } 
  }

Foued

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Yes, that's correct. When my code recall HAL_I2C_Receive_DMA() once this function returns HAL_OK indicating that the reception is successful,

I proceed to send a notification to the second task to inform it about the detection of an RX buffer. Subsequently, I transmit this buffer to the second task by a queue.

< if(HAL_I2C_GetState(&hi2c1) == HAL_I2C_STATE_READY)

 {

 ret = HAL_I2C_Slave_Receive_DMA(&hi2c1, (uint8_t*)&RX_I2C_Buffer, sizeof(RX_I2C_Buffer));

 if(ret == HAL_OK)

 {

 QueueStatus = osMessageQueuePut(I2CBufferQueueHandle, &RX_I2C_Buffer, sizeof(RX_I2C_Buffer), 500);

 xTaskNotify( xTaskMannageCmdHandle, RX_I2C_SUCCESSFULLY_DETECTED, eSetBits);

 }

 }

>

Hello,

< while (1)

 {

 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);

 ret1 = HAL_I2C_GetState(&hi2c1);

 if(HAL_I2C_GetState(&hi2c1) == HAL_I2C_STATE_READY){

 ret = HAL_I2C_Slave_Receive_DMA(&hi2c1, rxbuffer, sizeof(rxbuffer));

 if(ret == HAL_OK){

 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);

 }

 }

 }

 /* USER CODE END 3 */

}

void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)

{

BufferReceived++;

}

>

In the first iteration of my while loop, the code detects an I2C RX bus, but it's not true because I'm not sending anything from the master. After that, if I set a breakpoint on the line ret1 = HAL_I2C_GetState(&hi2c1); , I get ret1 = HAL_I2C_BUSY_RX, but despite that, I am able to receive from other I2C buses, but I'm not able to transmit. This problem occur when I use DMA or IT mode

You can't have RX_DMA active and try to transmit. I2C can only do one at a time. Since you are the slave, there should not be any more incoming data until you have sent your response. So don't restart the receive until after you have sent your response.

And a curiosity: why do you use BOTH a queue and task notify? just posting to the queue should be sufficient.

Hello,

< while (1)

 {

 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);

 ret1 = HAL_I2C_GetState(&hi2c1);

 if(HAL_I2C_GetState(&hi2c1) == HAL_I2C_STATE_READY){

 ret = HAL_I2C_Slave_Receive_DMA(&hi2c1, rxbuffer, sizeof(rxbuffer));

 if(ret == HAL_OK){

 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);

 }

 }

 }

 /* USER CODE END 3 */

}

void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)

{

BufferReceived++;

}

>

In the first iteration of my while loop, the code detects an I2C RX bus, but it's not true because I'm not sending anything from the master. After that, if I set a breakpoint on the line ret1 = HAL_I2C_GetState(&hi2c1); , I get ret1 = HAL_I2C_BUSY_RX, but despite that, I am able to receive from other I2C buses, but I'm not able to transmit. This problem occur when I use DMA or IT mode.

And if I add the lines of code below I enter in a blocking loop.

<

while(HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)

{

}

>

(1) When posting code, don't use the literal "<" and ">" characters, use the BUTTON below that looks like "</>" (3rd button from the right)

(2) "the code detects an I2C RX bus" - What the heck does THAT mean? Do you mean that HAL_I2C_Slave_Receive_DMA() returns HAL_OK? That only means that it STARTED the receive process, not that it actually has received data. That is what the HAL_I2C_SlaveRxCpltCallback() is for.

(3) Despite that I am able to receive from other I2C buses" - OTHER buses??? What does that have to do the THIS (hi2c1) bus?

(5) Please show ALL of your code from both tasks that relates to receiving data and transmitting data.

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
 
/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "cmsis_os.h"
#include "main.h"
#include "string.h"
 
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "A3941.h"
#include "i2c.h"
#include "FM24W256.h"
#include "Motor.h"
/* USER CODE END Includes */
 
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
 
/* USER CODE END PTD */
 
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
 
/* USER CODE END PD */
 
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
 
/* USER CODE END PM */
 
/* Global Variables ------------------------------------------------------------*/
/* USER CODE BEGIN */
extern I2C_HandleTypeDef hi2c1;
/* USER CODE END PD */
 
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
 
/* USER CODE END Variables */
 
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 256 * 4,
  .priority = (osPriority_t) osPriorityRealtime,
};
/* Definitions for xTaskUpdateI2C */
osThreadId_t xTaskUpdateI2CHandle;
const osThreadAttr_t xTaskUpdateI2C_attributes = {
  .name = "xTaskUpdateI2C",
  .stack_size = 256 * 4,
  .priority = (osPriority_t) osPriorityHigh,
};
/* Definitions for xTaskMannageCmd */
osThreadId_t xTaskMannageCmdHandle;
const osThreadAttr_t xTaskMannageCmd_attributes = {
  .name = "xTaskMannageCmd",
  .stack_size = 256 * 4,
  .priority = (osPriority_t) osPriorityHigh,
};
/* Definitions for xTaskDivers */
osThreadId_t xTaskDiversHandle;
const osThreadAttr_t xTaskDivers_attributes = {
  .name = "xTaskDivers",
  .stack_size = 256 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for I2CBufferQueue */
osMessageQueueId_t I2CBufferQueueHandle;
const osMessageQueueAttr_t I2CBufferQueue_attributes = {
  .name = "I2CBufferQueue"
};
 
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
void MX_FreeRTOS_Init(void);
void StartDefaultTask(void *argument);
void StartTaskI2CCommunication(void *argument);
void StartTaskManageCmd(void *argument);
void StartTaskDivers(void *argument);
 
void MX_FreeRTOS_Init(void)
{
	/* Creation of default task */
	defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
 
	/* Creation of I2c communication task */
	xTaskUpdateI2CHandle = osThreadNew(StartTaskI2CCommunication, NULL, &xTaskUpdateI2C_attributes);
 
	/* Creation of Manage I2C command task */
	xTaskMannageCmdHandle = osThreadNew(StartTaskManageCmd, NULL, &xTaskMannageCmd_attributes);
 
	/* Creation of divers task */
	xTaskDiversHandle = osThreadNew(StartTaskDivers, NULL, &xTaskDivers_attributes);
 
	/* Creation of I2C buffer queue task */
	I2CBufferQueueHandle = osMessageQueueNew(1, 8, &I2CBufferQueue_attributes);
}
 
/* USER CODE BEGIN Header_StartDefaultTask */
/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
	uint32_t ulNotifiedValue;
  for(;;)
  {
	  if( xTaskNotifyWait( pdFALSE,    /* Don't clear bits on entry. */
			  0xFFFFFFFF,        /* Clear all bits on exit. */
			  &ulNotifiedValue, /* Stores the notified value. */
			  500 )	//Wait 500ms before generate error
			  == pdPASS ) {			/* A notification was received.  See which bits were set. */
 
			  if( ( ulNotifiedValue & 0x01 ) != 0 ) {
				  //count++;
			  }
	  }
  }
}
 
/* USER CODE BEGIN Header_StartTaskI2CCommunication */
/**
* @brief Function implementing the xTaskUpdateI2CC thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskI2CCommunication */
void StartTaskI2CCommunication(void *argument)
{
  /* USER CODE BEGIN StartTaskI2CCommunication */
  /* Infinite loop */
	HAL_StatusTypeDef ret;
	osStatus_t QueueStatus;
	uint32_t ulNotifiedValue;
	RX_I2C_Buffer_t RX_I2C_Buffer;
 
  for(;;)
  {
	  if(HAL_I2C_GetState(&hi2c1) == HAL_I2C_STATE_READY)
	  {
		  ret = HAL_I2C_Slave_Receive_DMA(&hi2c1, (uint8_t*)&RX_I2C_Buffer, sizeof(RX_I2C_Buffer));
		  if(ret == HAL_OK)
		  {
			  QueueStatus = osMessageQueuePut(I2CBufferQueueHandle, &RX_I2C_Buffer, sizeof(RX_I2C_Buffer), 500);
			  xTaskNotify( xTaskMannageCmdHandle, RX_I2C_SUCCESSFULLY_DETECTED, eSetBits);
		  }
	  }
  }
  /* USER CODE END StartTaskI2CCommunication */
}
 
/* USER CODE BEGIN Header_StartTaskManageCmd */
/**
* @brief Function implementing the xTaskMannageCmd thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskManageCmd */
void StartTaskManageCmd(void *argument)
{
  /* USER CODE BEGIN StartTaskManageCmd */
  /* Infinite loop */
	HAL_StatusTypeDef ret;
	osStatus_t QueueStatus;
	uint8_t status = 0;
	RX_I2C_Buffer_t RX_I2C_Buffer;
	uint32_t ulNotifiedValue;
  for(;;)
  {
	  if( xTaskNotifyWait( pdFALSE,    /* Don't clear bits on entry. */
			  0xFFFFFFFF,        /* Clear all bits on exit. */
			  &ulNotifiedValue, /* Stores the notified value. */
			  500 )	//Wait 500ms before generate error
			  == pdPASS ) {			/* A notification was received.  See which bits were set. */
 
			  if( ( ulNotifiedValue & RX_I2C_SUCCESSFULLY_DETECTED ) != 0 ) {
				  QueueStatus = osMessageQueueGet (I2CBufferQueueHandle, &RX_I2C_Buffer, NULL, 100);
 
				  switch (RX_I2C_Buffer.CmdId){
				  case RESET_STM32F411RE:
					  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
					  HAL_Delay(1000);
					  NVIC_SystemReset();
					  break;
 
				  case SET_HIGH_POSITION:
					  Motor_SetHighPosition();
					  break;
 
				  case SET_LOW_POSITION:
					  Motor_SetLowPosition();
					  break;
 
				  case GET_LOW_POSITION_VALUE:
					  ret = Motor_SendLowPositionToMaster();
					  break;
 
				  case INITIALIZE_THE_MOTOR:
					  A3941_MotorInitialization();
					  break;
 
				  case MOVE_THE_MOTOR_UP:
					  break;
 
				  case MOVE_THE_MOTOR_DOWN:
					  break;
 
				  case MOVE_ALL_MOTORS_UP:
					  break;
 
				  case MOVE_ALL_MOTORS_DOWN:
					  break;
 
				  case SET_MOTOR_DIRECTION:
					  break;
 
				  case GET_MOTOR_DIRECTION:
					  break;
 
				  case GET_MOTOR_SENSOR_VALUE:
					  break;
 
				  case GET_CURRENT_SENSOR_VALUE:
					  break;
 
				  case A3941_SLEEP_MODE_ENABLE:
					  break;
 
				  case A3941_SLEEP_MODE_DISABLE:
					  break;
 
				  case A3941_GET_DRIVER_FAULT:
					  break;
 
				  case A3941_RESET_DRIVER_FAULT:
					  break;
 
				  case DOWNLOAD_STANDARD_MEMORY_TABLE:
					  ret = FM24W256_DownloadStandardMemoryTable();
					  break;
 
				  default:
					  break;
				  }
 
				  RX_I2C_Buffer.Addr = 0;
				  RX_I2C_Buffer.I2C_ID = 0;
				  RX_I2C_Buffer.CmdId = 0;
				  memset(RX_I2C_Buffer.Data, 0, sizeof(RX_I2C_Buffer.Data));
 
			  }
	  }
  }
  /* USER CODE END StartTaskManageCmd */
}
 
 
/* USER CODE BEGIN Header_StartTaskDivers */
/**
* @brief Function implementing the xTaskDivers thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskDivers */
void StartTaskDivers(void *argument)
{
  /* USER CODE BEGIN StartTaskDivers */
  /* Infinite loop */
  for(;;)
  {
	  HAL_GPIO_TogglePin(LED_1_GPIO_Port, LED_1_Pin);
	  HAL_GPIO_TogglePin(LED_2_GPIO_Port, LED_2_Pin);
	  HAL_GPIO_TogglePin(LED_3_GPIO_Port, LED_3_Pin);
	  osDelay(100);
  }
  /* USER CODE END StartTaskDivers */
}
 
/* USER CODE END FunctionPrototypes */
 
/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
 
/* USER CODE END Application */
 
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
 
}
 
 
 
 
HAL_StatusTypeDef Motor_SendLowPositionToMaster(void)
{
	HAL_StatusTypeDef ret;
	uint8_t *pData = NULL;
	TX_I2C_Buffer_t TX_I2C_Buffer;
 
	//pData = FM24W256_ReadMultiByte(LOW_POSITION_1_MEMORY_ADDRESS, 2);
	TX_I2C_Buffer.Addr = I2C_Board_Address;
	TX_I2C_Buffer.I2C_ID = I2C_Slave_TX;
	TX_I2C_Buffer.CmdId = GET_LOW_POSITION_VALUE;
	TX_I2C_Buffer.Data[0] = pData[0];
	TX_I2C_Buffer.Data[1] = pData[1];
	TX_I2C_Buffer.Data[2] = 0xFF;
	TX_I2C_Buffer.Data[3] = 0xFF;
	TX_I2C_Buffer.Data[4] = 0xFF;
 
 
 
	while(HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
	{
		if(xTaskGetTickCount() >= 1000)
		{
			break;
		}
	}
 
	ret = HAL_I2C_Slave_Transmit_DMA(&hi2c1, (uint8_t *)&TX_I2C_Buffer, sizeof(TX_I2C_Buffer));
 
	return ret;
}

Should I stop the DMA receive process before transmitting data to ensure proper operation? If so, how can I accomplish that?

As per both of my most recent posts:

  • Don't rely on HAL I2C states to tell tasks when to run or access the I2C bus. HAL code is NOT thread/RTOS compliant. Use a semaphore, notifications or other RTOS constructs.
  • HAL_I2C_Slave_Receive_DMA() returns immediately, NOT when it has data. See StartTaskI2CCommunication. Use the "rx complete" callback to post data to the queue. Then use "tx complete" callback to notify the other task when it is OK to try receiving again
  • Why are you using BOTH a queue and notifications? One would be sufficient for handling RX data (i.e. the queue).
  • NEW ISSUE: You call to osMessageQueuePut() is wrong. The 3rd param is NOT length, but priority (which is not used). All queue messages are presumed to be the same size - whatever was declared when you created the queue. You declare the queue with "message size"=8 bytes and queue size 1 entry.

There may be other issues that I did not repeat here, or have not yet noticed in your code. Address these issues, then debug your code and trace the flow around the HAL I2C calls.