Hello,
I am trying to implment MODBUS Slave RTU with STM32F030C8T6. I havce used RS485 for physical layer for communication.
As per MODBUS protocol, a stream of 8 bit data will be sent from Master. As a first step slave must capture all the data in an array, then process the data, do the required action and send back data.
In MODBUS if there is silence of more than 3.65 milli seconds (for Baudrate of 9600) slave can decice that Masfet has finished the data transfer.
I have used UART1 in Interrupt mode (RXNE) to receive the UART data and fill it in the array. Whenever a character is recived by UART, I reset the timer 3 to restart the timing of 3.65mS. If no data comes from UART for more than 3.65ms, slave will decide as teh frame is completed and start next actions.
As of now, to analyze the received data, I used conditional statements and digital outputs inside while loop. I have oscilloscope too.
Now the issue is.
When ever board is restarted and, first frame is sent, timer3 ISR is executed during the frame is being transmitted. WHich means timer is not reset inside the UART ISR. but this issue is present only during the first frame transfer. FFor all the later frames, timer3 ISR is exceuted perfectly after the set time is elapsed. Also the data filled in the array is also corrcet.. For your reference I have provided my full code below
/**
******************************************************************************
* @file : main.c
* @author : Auto-generated by STM32CubeIDE
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 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.
*
******************************************************************************
*/
#include <stdint.h>
#include "stm32f030x8.h"
#include "GPIOs_Definitions.h"
#define MB_Buff_Size 25
#define BAUD_RATE 9600
#define SYSCLK 8000000
#define USARTDIV (SYSCLK / BAUD_RATE)
volatile uint8_t rx_data = 0;
volatile uint8_t rx_ready = 0;
volatile uint8_t MB_RX_Buffer[MB_Buff_Size];
volatile uint8_t MB_RX_Buff_Index = 0;
void Init_Tim3_Eo_Frame_Detection (void);
void Init_Tim14_1ms(void);
void delay_ms(uint32_t);
void USART1_Init(void);
void USART1_Send_Char(uint8_t c);
void Enable_HSE_8MHz(void);
volatile uint32_t ms_Ticks = 0;
int main(void)
{
Enable_HSE_8MHz();
Init_Tim14_1ms();
delay_ms(100);
Init_GPIOs();
Switch_All_Relays_OFF;
Init_Tim3_Eo_Frame_Detection();
USART1_Init();
delay_ms(250);
for (uint16_t i = 0; i < MB_Buff_Size; i++) MB_RX_Buffer[i] = 0; // Clear buffer
MB_RX_Buff_Index = 0;
while (1)
{
if (rx_ready)
{
if (MB_RX_Buffer[0] == 0x01) { Switch_Relay_1_ON; } else { Switch_Relay_1_OFF; }
if (MB_RX_Buffer[1] == 0x05) { Switch_Relay_2_ON; } else { Switch_Relay_2_OFF; }
if (MB_RX_Buffer[2] == 0x00) { Switch_Relay_3_ON; } else { Switch_Relay_3_OFF; }
if (MB_RX_Buffer[3] == 0x00) { Switch_Relay_4_ON; } else { Switch_Relay_4_OFF; }
if (MB_RX_Buffer[4] == 0xFF) { Switch_Relay_5_ON; } else { Switch_Relay_5_OFF; }
if (MB_RX_Buffer[5] == 0x00) { Switch_Relay_6_ON; } else { Switch_Relay_6_OFF; }
if (MB_RX_Buffer[6] == 0x8C) { Switch_Relay_7_ON; } else { Switch_Relay_7_OFF; }
if (MB_RX_Buffer[7] == 0x3A) { Switch_Relay_8_ON; } else { Switch_Relay_8_OFF; }
rx_ready = 0;
MB_RX_Buff_Index = 0;
}
}
}
void USART1_Init(void)
{
// Enable GPIOB and USART1 clocks
RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
// Set PB6 (TX) and PB7 (RX) to AF mode
GPIOB->MODER &= ~((3 << (6 * 2)) | (3 << (7 * 2))); // Clear MODER
GPIOB->MODER |= ((2 << (6 * 2)) | (2 << (7 * 2))); // Set to AF
// Set AF0 (USART1) for PB6 and PB7
GPIOB->AFR[0] &= ~((0xF << (6 * 4)) | (0xF << (7 * 4)));
GPIOB->AFR[0] |= ((0x0 << (6 * 4)) | (0x0 << (7 * 4)));
// Optional: set high speed for TX/RX pins
GPIOB->OSPEEDR |= (3 << (6 * 2)) | (3 << (7 * 2));
// --- PB5 as DE (Driver Enable) ---
GPIOB->MODER &= ~(3 << (5 * 2)); // Clear PB5 mode
GPIOB->MODER |= (1 << (5 * 2)); // Set PB5 as output
GPIOB->OTYPER &= ~(1 << 5); // Push-pull
GPIOB->OSPEEDR |= (3 << (5 * 2)); // High speed
GPIOB->PUPDR &= ~(3 << (5 * 2)); // No pull-up/down
GPIOB->ODR &= ~(1 << 5); // Set PB5 LOW (receive mode by default)
// --- USART1 Config ---
USART1->BRR = 8000000 / 9600; // Baud = 9600 (assuming 8 MHz HSE)
USART1->CR1 &= ~(USART_CR1_M | USART_CR1_PCE); // 8-bit, no parity
USART1->CR2 &= ~USART_CR2_STOP; // 1 stop bit
USART1->CR1 |= USART_CR1_TE | USART_CR1_RE; // Enable TX and RX
USART1->CR1 |= USART_CR1_RXNEIE; // Enable RXNE interrupt
USART1->CR1 |= USART_CR1_UE; // Enable USART
// Clear RX data register to avoid junk
volatile uint8_t dummy = USART1->RDR;
// Enable USART1 IRQ
NVIC_EnableIRQ(USART1_IRQn);
}
void USART1_IRQHandler(void)
{
if (USART1->ISR & USART_ISR_RXNE) // RX not empty
{
Switch_Status_LED_ON;
uint8_t rx_data = USART1->RDR; // Read received byte (clears RXNE flag)
MB_RX_Buffer[MB_RX_Buff_Index++] = rx_data;
if (MB_RX_Buff_Index >= MB_Buff_Size)
{
MB_RX_Buff_Index = 0; // Prevent buffer overrun
}
// Restart TIM3 for end-of-frame detection (Modbus RTU style)
TIM3->CR1 &= ~TIM_CR1_CEN; // Stop
TIM3->SR &= ~TIM_SR_UIF; // <-- Critical: Clear pending flag
TIM3->PSC = 7;
TIM3->ARR = 3640;
TIM3->CNT = 0; // Reset counter
TIM3->CR1 |= TIM_CR1_CEN; // Start
Switch_Status_LED_OFF;
}
}
void Init_Tim3_Eo_Frame_Detection (void)
{
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // Enable TIM3 clock
TIM3->CR1 = 0; // Clean slate
TIM3->PSC = 7; // Prescaler: 8 MHz / (7+1) = 1 MHz → 1 µs tick
TIM3->ARR = 3640; // Auto-reload: 3640 µs (3.64 ms)
TIM3->CNT = 0; // Reset counter
TIM3->SR &= ~TIM_SR_UIF; // Clear update flag
TIM3->DIER |= TIM_DIER_UIE; // Enable update interrupt
TIM3->CR1 |= TIM_CR1_OPM; // One Pulse Mode
NVIC_EnableIRQ(TIM3_IRQn);
// Don't start timer here; it is started/reset on each byte in USART1_IRQHandler
}
void TIM3_IRQHandler(void)
{
if (TIM3->SR & TIM_SR_UIF)
{
Switch_RXD_LED_ON;
TIM3->SR &= ~TIM_SR_UIF;
TIM3->CR1 &= ~TIM_CR1_CEN; // Stop timer
rx_ready = 1; // Signal frame complete
Switch_RXD_LED_OFF;
}
}
void USART1_Send_Char(uint8_t data)
{
while (!(USART1->ISR & USART_ISR_TXE));
GPIOB->ODR |= (1 << 5);
USART1->TDR = data;
while (!(USART1->ISR & USART_ISR_TC));
GPIOB->ODR &= ~(1 << 5);
}
void Init_Tim14_1ms(void)
{
RCC->APB1ENR |= RCC_APB1ENR_TIM14EN;
TIM14->PSC = 7;
TIM14->ARR = 999;
TIM14->CNT = 0;
TIM14->DIER |= TIM_DIER_UIE;
TIM14->CR1 |= TIM_CR1_CEN;
NVIC_EnableIRQ(TIM14_IRQn);
}
void TIM14_IRQHandler(void)
{
if (TIM14->SR & TIM_SR_UIF)
{
TIM14->SR &= ~TIM_SR_UIF;
ms_Ticks++;
}
}
void delay_ms(uint32_t ms)
{
uint32_t start = ms_Ticks;
while ((ms_Ticks - start) < ms);
}
void Enable_HSE_8MHz(void)
{
// Enable HSE
RCC->CR |= RCC_CR_HSEON;
// Wait until HSE is ready
while (!(RCC->CR & RCC_CR_HSERDY));
// Switch system clock to HSE
RCC->CFGR &= ~RCC_CFGR_SW; // Clear SW bits
RCC->CFGR |= RCC_CFGR_SW_HSE; // Set SW to HSE
// Wait till HSE is used as the system clock
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSE);
}
Please let me know what is wrong with my code.
Thank you, WIth best regards, Anesh S.