cancel
Showing results for 
Search instead for 
Did you mean: 

MODBUS RTU with NUCLEO-F446RE

tnishanthraj
Associate II

Hello everyone,

I’m working on a project where I’m using a NUCLEO-F446RE board to control an AC servo drive via MODBUS RTU. I’m using an RS485 board converter to handle the communication. The problem I’m facing is that I'm not getting the echo of data i send.

Here’s the setup I'm using:

  • Controller: NUCLEO-F446RE
  • Converter:  RS485 board
  • MODBUS Communication: USART2 (PA2 Rx, PA3 Tx, PA1 RSE)
  • Servo Drive Baud Rate: 38400, no parity, 2 stop bits

I’ve attached my program below, and I’d appreciate it if anyone could help me identify where the issue might be.

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 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 "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#include "stm32f4xx_hal.h"
#include "core_cm4.h"  // ITM functions for SWV
/* USER CODE END Includes */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
// Define the GPIO port and pins for DE/RE control
#define RSE_GPIO_PORT GPIOA   // GPIO Port for RSE pin
#define RSE_PIN GPIO_PIN_1    // GPIO Pin for RSE
/* USER CODE END PD */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */
uint8_t modbus_frames[][8] = {
  {0x01, 0x06, 0x60, 0x02, 0x00, 0x40, 0x37, 0xFA},
  {0x01, 0x06, 0x62, 0x00, 0x00, 0x01, 0x57, 0xB2},
  {0x01, 0x06, 0x62, 0x01, 0x00, 0x03, 0x87, 0xB3},
  {0x01, 0x06, 0x62, 0x02, 0x0D, 0x40, 0x32, 0xD2},
  {0x01, 0x06, 0x62, 0x03, 0x02, 0x58, 0x66, 0xE8},
  {0x01, 0x06, 0x62, 0x04, 0x00, 0x32, 0x56, 0x66},
  {0x01, 0x06, 0x62, 0x05, 0x00, 0x32, 0x07, 0xA6},
  {0x01, 0x06, 0x60, 0x02, 0x00, 0x10, 0x37, 0xC6}
};
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */
void MODBUS_Transmit(uint8_t *data, uint16_t size);
void Set_RSE_High(void);
void Set_RSE_Low(void);
int __io_putchar(int ch);  // for SWV
/* USER CODE END PFP */

/* 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_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    // Loop through and transmit each MODBUS frame
    for (int i = 0; i < sizeof(modbus_frames) / sizeof(modbus_frames[0]); i++)
    {
      // Transmit MODBUS frame
      MODBUS_Transmit(modbus_frames[i], 8);

      // Delay for receiving response
      HAL_Delay(2000);

      uint8_t rx_buffer[40];  // Buffer to hold incoming data
      int receivedBytes = HAL_UART_Receive(&huart2, rx_buffer, sizeof(rx_buffer), 5000);  // 5-second timeout
      if (receivedBytes > 0)
      {
          printf("Received: ");
          for (int j = 0; j < receivedBytes; j++)
          {
              printf("%02X ", rx_buffer[j]);
          }
          printf("\r\n");
      }
      else
      {
          printf("No response received.\r\n");
      }

      HAL_Delay(2000);  // Delay before sending next frame
    }
    /* 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};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 16;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  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_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART2 Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 38400;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_2;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief GPIO Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1|LD2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : B1_Pin */
  GPIO_InitStruct.Pin = B1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pins : PA1 LD2_Pin */
  GPIO_InitStruct.Pin = GPIO_PIN_1|LD2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

/* USER CODE BEGIN 4 */

// MODBUS Transmit function
void MODBUS_Transmit(uint8_t *data, uint16_t size)
{
  Set_RSE_High();  // Enable transmission (RSE=High)

  // Transmit data over UART
  HAL_UART_Transmit(&huart2, data, size, HAL_MAX_DELAY);
  HAL_Delay(5);  // Small delay

  Set_RSE_Low();   // Enable reception (RSE=Low)

  printf("Transmitted: ");
  for (int i = 0; i < size; i++)
  {
    printf("%02X ", data[i]);
  }
  printf("\r\n");
}

// Set RSE high to enable transmission
void Set_RSE_High(void)
{
  HAL_GPIO_WritePin(RSE_GPIO_PORT, RSE_PIN, GPIO_PIN_SET);
}

// Set RSE low to enable reception
void Set_RSE_Low(void)
{
  HAL_GPIO_WritePin(RSE_GPIO_PORT, RSE_PIN, GPIO_PIN_RESET);
}

// Redirect printf to SWV (ITM)
int __io_putchar(int ch)
{
  ITM_SendChar(ch);  // Use ITM to send data via SWV
  return ch;
}

/* 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 */
  __disable_irq();
  while (1)
  {
  }
  /* 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 can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
}
#endif /* USE_FULL_ASSERT */

 

2 REPLIES 2
SofLit
ST Employee

Hello @tnishanthraj ,

Why there is this delay of 2 second between the transmit and the receive? there is a big risk to lose the response from the AC servo mainly if it answers to your request in less than 2 seconds.

And I suggest to remove this "big" delay and use Rx interrupt instead.

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.

Hello! I used Rx interrupt and I've attached the output I got. I'm still not getting the desired output. But I did some troubleshooting and found out that the PA1[RSE] pin isn't getting HIGH. I used a multimeter. But in general, I wrote a program to toggle PA1 and it worked just fine.