2025-09-27 9:07 AM
Hello ST,
I have been working on trying to get the FreeMODBUS library working on the NUCLEO-F446RE development board.
The FreeMODBUS library can be found here,
https://github.com/cwalter-at/freemodbus
I am having trouble getting the USART port working correctly,
So far I am able send packets to the microcontroller and get interrupt calls to the USART2_IRQHandler();
However calling HAL_USART_Receive_IT(&uart_mb, (uint8_t *)byte, 1) only returns zeros in byte.
BOOL xMBPortSerialGetByte(CHAR *byte)
{
//*byte = MB_USART->RDR;
HAL_USART_Receive_IT(&uart_mb, (uint8_t *)byte, 1);
//HAL_UART_StateTypeDef state = HAL_UART_GetState(&uart_mb);
//*byte = MB_USART->DR;
return TRUE;
}
I am configuring the USART port in the following manner,
BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity, UCHAR stopBit)
{
HAL_Delay(10);
// Enable UART peripheral clock
MB_USART_CLK_ENABLE();
HAL_Delay(10);
// Enable GPIO clocks
MB_TX_GPIO_CLK_ENABLE();
MB_RX_GPIO_CLK_ENABLE();
HAL_Delay(10);
__HAL_UART_ENABLE_IT(&uart_mb, UART_IT_RXNE);
__HAL_UART_ENABLE_IT(&uart_mb, UART_IT_TC);
// Configure NVIC for UART interrupt
HAL_NVIC_SetPriority(MB_USART_IRQn, MB_USART_IRQ_priority, MB_USART_IRQ_subpriority);
//HAL_NVIC_DisableIRQ(MB_USART_IRQn);
// Configure UART for Modbus communication
uart_mb.Instance = MB_USART;
uart_mb.Init.BaudRate = ulBaudRate;
uart_mb.Init.StopBits = UART_STOPBITS_1; // Always use 1 stop bit
uart_mb.Init.Mode = UART_MODE_TX_RX;
uart_mb.Init.HwFlowCtl = UART_HWCONTROL_NONE;
uart_mb.Init.OverSampling = UART_OVERSAMPLING_16;
// Configure WordLength and Parity based on data bits and parity
if( ucDataBits == 8 )
{
if( eParity == MB_PAR_NONE )
{
uart_mb.Init.WordLength = UART_WORDLENGTH_8B;
uart_mb.Init.Parity = UART_PARITY_NONE;
}
else
{
uart_mb.Init.WordLength = UART_WORDLENGTH_9B; // 8 data bits + parity
uart_mb.Init.Parity = (eParity == MB_PAR_ODD) ? UART_PARITY_ODD : UART_PARITY_EVEN;
}
}
else if( ucDataBits == 7 )
{
if( eParity == MB_PAR_NONE )
{
uart_mb.Init.WordLength = UART_WORDLENGTH_8B;
uart_mb.Init.Parity = UART_PARITY_NONE;
}
else
{
uart_mb.Init.WordLength = UART_WORDLENGTH_8B; // 7 data bits + parity
uart_mb.Init.Parity = (eParity == MB_PAR_ODD) ? UART_PARITY_ODD : UART_PARITY_EVEN;
}
}
else
{
return FALSE; // Unsupported data bits configuration
}
if( HAL_UART_Init( &uart_mb ) != HAL_OK )
{
return FALSE; // UART initialization failed
}
//Critical Delay Flush Everything
//Let hardware settle.
HAL_Delay(10);
//Read and discard any garbage data
while(__HAL_UART_GET_FLAG(&uart_mb, UART_FLAG_RXNE))
{
uint8_t dummy = (uint8_t)uart_mb.Instance->DR;
(void)dummy;
}
//Clear ALL possible flags
__HAL_UART_CLEAR_PEFLAG(&uart_mb);
__HAL_UART_CLEAR_FEFLAG(&uart_mb);
__HAL_UART_CLEAR_NEFLAG(&uart_mb);
__HAL_UART_CLEAR_OREFLAG(&uart_mb);
HAL_Delay(10);
for (;;)
{
HAL_USART_Transmit_IT(&uart_mb, (uint8_t *)"hi", 2);
HAL_Delay(10);
}
// Disable RX and TX interrupts initially
//__HAL_UART_DISABLE_IT(&uart_mb, UART_IT_RXNE);
//__HAL_UART_DISABLE_IT(&uart_mb, UART_IT_TXE);
//HAL_NVIC_EnableIRQ(MB_USART_IRQn);
return TRUE;
}
I have found that uncommenting,
HAL_NVIC_DisableIRQ(MB_USART_IRQn);
//and
HAL_NVIC_EnableIRQ(MB_USART_IRQn);
Will prevent the HAL_Delay(10) function from working properly.
main() initializes HAL, configures the system clock, pin multiplexing, and timer 7.
int main(void)
{
//Vars
eMBErrorCode eStatus;
//0. Initialize HAL
HAL_Init();
//1. System Clock Config
SystemClock_Config();
//2. GPIO Config
MX_GPIO_Init();
HAL_Delay(10);
MX_TIM7_Init();
HAL_Delay(10);
//3. MODBUS
//a. Initialize
eStatus = eMBInit(MB_RTU, 0x0A, 0, 38400, MB_PAR_NONE, 1);
usRegInputBuf[0] = 0; /* Initialize counter */
usRegInputBuf[1] = 0;
usRegInputBuf[2] = 0;
usRegInputBuf[3] = 0;
//b. Enable the protocol stack.
eStatus = eMBEnable();
//c. Handle Errors
if( eStatus != MB_ENOERR )
{
Error_Handler();
}
/* Loop forever */
for(;;)
{
(void)eMBPoll();
}
}
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 = 8;
RCC_OscInitStruct.PLL.PLLN = 84;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
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();
}
}
static void MX_TIM7_Init(void)
{
/* USER CODE BEGIN TIM7_Init 0 */
/* USER CODE END TIM7_Init 0 */
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM7_Init 1 */
/* USER CODE END TIM7_Init 1 */
htim7.Instance = TIM7;
htim7.Init.Prescaler = 0;
htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
htim7.Init.Period = 65535;
htim7.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim7) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim7, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM7_Init 2 */
/* USER CODE END TIM7_Init 2 */
}
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* 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(LD2_GPIO_Port, 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 : PA2 PA3 */
GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pin : LD2_Pin */
GPIO_InitStruct.Pin = LD2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
I have tried adding,
for (;;)
{
HAL_USART_Transmit_IT(&uart_mb, (uint8_t *)"hi", 2);
HAL_Delay(10);
}
to BOOL xMBPortSerialInit(), but this has really strange behavior.
The SysTick interrupt will stop being called and execution will hang.
Does anyone have any suggestions on how to get the USART port working reliably?
The complete project is attached.
Thanks,
Allan
Solved! Go to Solution.
2025-09-28 12:46 PM
I was able to fix this issue by using the correct function call.
BOOL xMBPortSerialPutByte(CHAR byte)
{
//MB_USART->TDR = byte;
HAL_UART_Transmit(&usart2_hndl, (uint8_t *)&byte, 1, 100);
//USART2->DR = byte;
return TRUE;
}
BOOL xMBPortSerialGetByte(CHAR *byte)
{
//*byte = USART2->DR;
HAL_UART_Receive(&usart2_hndl, (uint8_t *)byte, 1, 100);
//HAL_UART_StateTypeDef state = HAL_UART_GetState(&uart_mb);
//*byte = MB_USART->DR;
return TRUE;
}
Thanks,
Allan
2025-09-28 5:55 AM
I can recommend this library we developed our stm32 products. It is very easy to integrate into any platform.
https://github.com/SiemensEnergy/c-modbus-slave
Here is an example RTU:
extern void modbus_proc(void)
{
static uint8_t s_rx[MBADU_SIZE_MAX];
static uint8_t s_tx[MBADU_SIZE_MAX];
size_t n_rx, n_tx;
if (uart_is_tx(&s_uart_dev) || uart_is_recv_mode(&s_uart_dev)) {
return;
}
n_rx = uart_recv_block_size(&s_uart_dev);
if (n_rx > 0) {
n_tx = mbadu_handle_req(modbus_get(), s_rx, n_rx, s_tx); // Library entrypoint
if (n_tx > 0) {
(void)uart_transmit_it(&s_uart_dev, s_tx, n_tx);
}
}
uart_start_recv_it(&s_uart_dev, s_rx, sizeof s_rx);
}
Some support functions used here:
struct uart_device_s {
UART_HandleTypeDef *huart;
/* RTS is optional */
GPIO_TypeDef *rts_port; /* Request to send port */
uint16_t rts_pin; /* Request to send pin */
};
/////////
extern int uart_is_recv_mode(struct uart_device_s *uart_dev)
{
return (uart_dev->huart->RxState & 2) != 0;
}
extern int uart_is_tx(struct uart_device_s *uart_dev)
{
return (uart_dev->huart->TxXferCount != 0U) || (uart_dev->huart->gState & 1) != 0;
}
extern int uart_recv_block_size(struct uart_device_s *uart_dev)
{
return (int)(uart_dev->huart->RxXferSize - uart_dev->huart->RxXferCount);
}
extern int uart_start_recv_it(struct uart_device_s *uart_dev, uint8_t *buf, int buf_size)
{
/* Restart listening for data */
return HAL_UART_Receive_IT(uart_dev->huart, buf, (uint16_t)buf_size) == HAL_OK;
}
extern int uart_transmit_it(struct uart_device_s *uart_dev, uint8_t *buf, int size)
{
return HAL_UART_Transmit_IT(uart_dev->huart, buf, (uint16_t)size) == HAL_OK;
}
Example of uart setup ++:
static UART_HandleTypeDef s_huart = {0};
/**
* @brief This function handles USART3 global interrupt.
*/
void USART3_IRQHandler(void)
{
HAL_UART_IRQHandler(&s_huart);
}
static void msp_init(UART_HandleTypeDef *huart)
{
(void)huart;
GPIO_InitTypeDef gpio_cfg = {0};
RCC_PeriphCLKInitTypeDef periph_clk_cfg = {0};
/* Initializes the peripherals clock */
periph_clk_cfg.PeriphClockSelection = RCC_PERIPHCLK_USART3;
periph_clk_cfg.Usart3ClockSelection = RCC_USART3CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&periph_clk_cfg) != HAL_OK) {
__disable_irq();
while (1); /* Allow the watchdog to reboot */
}
/* Peripheral clock enable */
__HAL_RCC_USART3_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART3 GPIO Configuration
PB10 ------> USART3_TX
PB11 ------> USART3_RX
PA15 (JTDI) ------> USART3_DE
*/
gpio_cfg.Pin = GPIO_PIN_10|GPIO_PIN_11;
gpio_cfg.Mode = GPIO_MODE_AF_PP;
gpio_cfg.Pull = GPIO_NOPULL;
gpio_cfg.Speed = GPIO_SPEED_FREQ_LOW;
gpio_cfg.Alternate = GPIO_AF7_USART3;
HAL_GPIO_Init(GPIOB, &gpio_cfg);
gpio_cfg.Pin = GPIO_PIN_15;
gpio_cfg.Mode = GPIO_MODE_AF_PP;
gpio_cfg.Pull = GPIO_NOPULL;
gpio_cfg.Speed = GPIO_SPEED_FREQ_LOW;
gpio_cfg.Alternate = GPIO_AF7_USART3;
HAL_GPIO_Init(GPIOA, &gpio_cfg);
/* USART3 interrupt Init */
HAL_NVIC_SetPriority(USART3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART3_IRQn);
}
static void msp_de_init(UART_HandleTypeDef *huart)
{
(void)huart;
/* Peripheral clock disable */
__HAL_RCC_USART3_CLK_DISABLE();
/**USART3 GPIO Configuration
PB10 ------> USART3_TX
PB11 ------> USART3_RX
PA15 (JTDI) ------> USART3_DE
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_11);
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_15);
/* USART3 interrupt DeInit */
HAL_NVIC_DisableIRQ(USART3_IRQn);
}
extern int uart3_init(uint32_t baudrate, int two_stop_bits)
{
GPIO_InitTypeDef gpio_cfg = {0};
/* USART3_5V0_OK */
gpio_cfg.Pin = GPIO_PIN_2;
gpio_cfg.Mode = GPIO_MODE_INPUT;
gpio_cfg.Pull = GPIO_PULLUP;
gpio_cfg.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOD, &gpio_cfg);
s_huart.Instance = USART3;
s_huart.Init.BaudRate = baudrate;
s_huart.Init.WordLength = UART_WORDLENGTH_8B;
s_huart.Init.StopBits = two_stop_bits ? UART_STOPBITS_2 : UART_STOPBITS_1;
s_huart.Init.Parity = UART_PARITY_NONE;
s_huart.Init.Mode = UART_MODE_TX_RX;
s_huart.Init.HwFlowCtl = UART_HWCONTROL_NONE;
s_huart.Init.OverSampling = UART_OVERSAMPLING_16;
s_huart.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
/*s_huart.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_SWAP_INIT;
s_huart.AdvancedInit.Swap = UART_ADVFEATURE_SWAP_ENABLE;*/
s_huart.MspInitCallback = &msp_init;
s_huart.MspDeInitCallback = &msp_de_init;
if (HAL_RS485Ex_Init(&s_huart, UART_DE_POLARITY_HIGH, 0, 0) != HAL_OK) {
return 0;
}
HAL_UART_ReceiverTimeout_Config(&s_huart, 28); /* 3.5 8-bit characters */
if (HAL_UART_EnableReceiverTimeout(&s_huart) != HAL_OK) {
return 0;
}
__HAL_LOCK(&s_huart); /* this macro may return HAL_BUSY */
SET_BIT(s_huart.Instance->CR1, USART_CR1_RTOIE);
__HAL_UNLOCK(&s_huart);
return 1;
}
extern UART_HandleTypeDef *uart3_get(void)
{
return &s_huart;
}
2025-09-28 12:46 PM
I was able to fix this issue by using the correct function call.
BOOL xMBPortSerialPutByte(CHAR byte)
{
//MB_USART->TDR = byte;
HAL_UART_Transmit(&usart2_hndl, (uint8_t *)&byte, 1, 100);
//USART2->DR = byte;
return TRUE;
}
BOOL xMBPortSerialGetByte(CHAR *byte)
{
//*byte = USART2->DR;
HAL_UART_Receive(&usart2_hndl, (uint8_t *)byte, 1, 100);
//HAL_UART_StateTypeDef state = HAL_UART_GetState(&uart_mb);
//*byte = MB_USART->DR;
return TRUE;
}
Thanks,
Allan