cancel
Showing results for 
Search instead for 
Did you mean: 

First ISR of the timer is exected even after it is reset

Anesh
Associate II

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.

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Guru

ARR and PSC are preloaded. Generate an update event (and ignore/clear resulting update flag) to have them take effect immediately.

 

TDK_0-1745104805176.png

 

If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

3 REPLIES 3
TDK
Guru

ARR and PSC are preloaded. Generate an update event (and ignore/clear resulting update flag) to have them take effect immediately.

 

TDK_0-1745104805176.png

 

If you feel a post has answered your question, please click "Accept as Solution".
Anesh
Associate II

Thank you for your hint. Now I modified my code as below. It is working great. Still if you feel that I overdo something, please let me know. Thank you so much for your hint.

 

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
        TIM3->CR1 &= ~TIM_CR1_CEN; // Stop
        TIM3->SR  &= ~TIM_SR_UIF;          // <-- Critical: Clear pending flag
        TIM3->SR &= ~TIM_SR_UIF; // Clear update flag
        TIM3->PSC = 7;
        TIM3->ARR = 3640;
        TIM3->CNT = 0;              // Reset counter
 
        TIM3->EGR = TIM_EGR_UG; // Force update to apply ARR/PSC
        TIM3->SR &= ~TIM_SR_UIF; // Clear again (critical)
        TIM3->DIER |= TIM_DIER_UIE; // Re-enable interrupt
        TIM3->CR1 |= TIM_CR1_CEN; // Start timer
 
        Switch_Status_LED_OFF;
    }
}
 
 
void Init_Tim3_Eo_Frame_Detection (void)
{
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
 
TIM3->CR1 = 0;
TIM3->PSC = 7;
TIM3->ARR = 3640;
TIM3->CNT = 0;
 
TIM3->SR = 0;
TIM3->DIER = 0; // No interrupt yet
TIM3->EGR = TIM_EGR_UG; // Update event to preload
TIM3->SR &= ~TIM_SR_UIF; // Clear update flag
TIM3->DIER |= TIM_DIER_UIE; // Now enable 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;
    }
}

 

Looks good.

To clear UIF, you should write 0 only to that byte. Doing a read-modify-write (&=) can clear other flags. Not an issue here but could be if you were using them.

TIM3->SR = ~TIM_SR_UIF;  // Clear UIF

 

If you feel a post has answered your question, please click "Accept as Solution".