Program to identify Message Timeout

nsaspook

Joined Aug 27, 2009
16,334
Differences of opinion make a horse race.

Besides, I expect that my code ultimately will be doing much more than controlling a single communication channel.
I think that low-resolution timing requirements are so common in programming that general abstraction for hardware timers is valuable. I use mine in almost every controller project for simple event timing, event timeouts and FSM event sequencing.
 

joeyd999

Joined Jun 6, 2011
6,331
I think that low-resolution timing requirements are so common in programming that general abstraction for hardware timers is valuable. I use mine in almost every controller project for simple event timing, event timeouts and FSM event sequencing.
They're especially useful for UI timing where a few us or ms here or there are not going to effect the user experience.

Oh, and for managing slow comm channels.

I reserve high-res or high-accuracy low jitter hardware timers for the functions that need it.
 

Irving

Joined Jan 30, 2016
5,139
Guys, all interesting stuff, and when the TS is ready we can address the timer issue; yes 2 hardware timers might be wasteful, but its not a big issue to deal with later. The dsPIC33EV MCU has 5 x16bit and 2 x 32 bit timers with ~15nS resolution (full clock speed).

@Vihaan@123 Coming back to your code, can I clarify that 'messages', apparently delimited by <CR>, <LF> are independent of 'frames' delimited by <SOF>, <EOF>, and you're not doing anything with the address or CRC fields at frame level? I don't understand why you are not processing all data in a frame.

Are there multiple <LF> allowed in a message but only one <CR>? I noticed you check on <LF> to see if previous char was a <CR> and if so end the message, but if not you carry on but, I think, throw away the <LF> (you decrement charcounter to check for previous <CR> but don't increment it again if not) or is this a potential bug?
 

joeyd999

Joined Jun 6, 2011
6,331
Guys, all interesting stuff, and when the TS is ready we can address the timer issue; yes 2 hardware timers might be wasteful, but its not a big issue to deal with later.
So, rather than offer helpful, useful free advice today, we should wait till next year to help him solve the bigger problem of "I don't have any timers left?"

Part of education is filling in blanks the learner doesn't even know exist.

There's that, and the School of Hard Knocks.
 

Irving

Joined Jan 30, 2016
5,139
So, rather than offer helpful, useful free advice today, we should wait till next year to help him solve the bigger problem of "I don't have any timers left?"

Part of education is filling in blanks the learner doesn't even know exist.

There's that, and the School of Hard Knocks.
Joey, I do get your point. I agree its not trivial to deal with that issue, but we know very little about the TS' project's scale and purpose and I feel there are equally big issues elsewhere to deal with sooner.
 

Thread Starter

Vihaan@123

Joined Oct 7, 2025
254
@Vihaan@123 Coming back to your code, can I clarify that 'messages', apparently delimited by <CR>, <LF> are independent of 'frames' delimited by <SOF>, <EOF>, and you're not doing anything with the address or CRC fields at frame level? I don't understand why you are not processing all data in a frame.

Are there multiple <LF> allowed in a message but only one <CR>? I noticed you check on <LF> to see if previous char was a <CR> and if so end the message, but if not you carry on but, I think, throw away the <LF> (you decrement charcounter to check for previous <CR> but don't increment it again if not) or is this a potential bug?
Yes I have not yet written the complete code, there are CRC fields and slave address, it is RS485 Modbus communication with ASCII format which i planned to implement.
As a first step i had concerns of a. Using the global variables b. Logic of detecting the timeout of characters and messages. c. Issues of handling UART data or flags between the Uart Interrupt and the main loop.
Once i get enough confidence and logic i wanted to proceed with further steps of CRC, slave address and then send the response packet.
The application code is not very huge i need to read around 5 ADC channels check for valid ranges and switch On relays and in parallel perform the RS485 communication.
1776833128488.png

Messages and frames are both same. There could be some logical mistakes in the code related to increment and decrement that i have not yet fine-tuned.
 

Thread Starter

Vihaan@123

Joined Oct 7, 2025
254
I am just rewriting the algorithm based on suggestions form @MrChips
a. UART interrupt i will make it small with only data capture and EOF detection, i will not check for SOF.
b. The entire packet will process in the main loop.
c. Regarding the timeout mechanism using Timers i am still working out.
I can post the logic once i have completed.
 

Thread Starter

Vihaan@123

Joined Oct 7, 2025
254
I have come up with the flow
Code:
UartInterrupt(void) {
    static uint8_t msgcounter=0;
    
    MessageData[msgcounter] = TxBuf;
    if(MessageData[msgcounter] == SOF) {
        startofframe = TRUE;
    }
    if(startofframe == TRUE && MessageData[msgcounter] == EOF) {
            data_avaialable_flag = TRUE;
     StartTimer1();   
     msgcounter=0;
      } else{
         msgcounter++;
     }         
}
    
    
    Timer1Interrupt(void){
        msgtimeout_flag = TRUE;
    }
    
    int main(void) {
        uint8_t localmsgdata[20];
while(1) {
        if(msgtimeout_flag == TRUE){
            StopTimer1();                     // Stop Timer
            /*Perform some actions */
        }
       if(data_avaialable_flag == TRUE) {  // should i disable the uart interrupts and check for the flag ?
            data_avaialable_flag = FALSE;            // should i disable the uart interrupts and update the flag ?
            CopyDataToLocalBuffer(MessageData,localmsgdata); // should i disable the uart interrupts and copy the data ?
            VerifyMessageIsValid();
            ProcessMessage(localmsgdata);
        }
    }
}
My real concerns now are can I read the flags and copy the data which is shared between the interrrupts and while(1);
The other issue which i am thinking is in case there are multiple slaves and want to identify the timeouts of each slave, then the start and stop timer may not work.
 

Thread Starter

Vihaan@123

Joined Oct 7, 2025
254
I use one free-running global system timer -- a heartbeat -- which can be quickly bit tested to time low-resolution events:


system_timer.c:
#include <xc.h>
#include "system_timer.h"
#include "system_timer_definitions.h"
#include "interrupts.h"
#include "power.h"

void __interrupt(irq(IRQ_TMR0), base(IVT_Base)) TMR0_ISR(void)
{
    TMR0IF = 0;                             //clear interrupt flag
    st_vtmrchg.bytev |= st_vtimer.bytev+1^(st_vtimer.bytev);  //accumlate changed bits
    st_vtimer.bytev++;                            //increment volatile timer
}

void ST_start(void)
{
    T0CON0bits.EN = 0;                      //turn off timer if running

    st_systim.wordv = 0;                    //reset timer registers
    st_tmrchg.wordv = 0;
    st_vtimer.bytev = 0;
    st_vtmrchg.bytev = 0;

    T0CON1 = system_timer_t0con1;           //set up oscillator/ sync / prescale
    T0CON0 = system_timer_t0con0;           //enable 8 bit timer and postscale
    TMR0H = system_timer_period;            //set tmr0 period
    TMR0L = 0;                              //clear any previous counts

    TMR0IP = 1;                             //set hi priority (for fast pid)
    TMR0IF = 0;                             //clear pending ints
    TMR0IE = 1;                             //and enable interrupts
}

void ST_stop(void)
{
    T0CON0bits.EN = 0;                      //turn on timer
    TMR0IE = 0;                             //disable interrupts

}

void ST_continue(void)
{
    T0CON0bits.EN = 1;                      //turn on timer 
    TMR0IE = 1;                             //and enable interrupts
}

void ST_poll(void)
{
    st_tmrchg.bytev.hi = 0;

#ifdef TIMER0_SLEEP

//Sleep between interrupts

    di();

    if (PPFLAG_CAN_SLEEP && !st_vtmrchg.bytev)
    {
        Sleep();
        Nop();
    }

    PPFLAG_CAN_SLEEP = 1;

    ei();

#endif

// Critical Section

    TMR0IE = 0;

    st_systim.bytev.lo = st_vtimer.bytev;
    st_tmrchg.bytev.lo = st_vtmrchg.bytev;
    st_vtmrchg.bytev = 0;

    TMR0IE = 1;

// End Critical Section

    if (st_tmrchg.bitv.b7 && !st_systim.bitv.b7)
       st_tmrchg.bytev.hi = st_systim.bytev.hi^((st_systim.bytev.hi++)+1);
}
system_timer_h:
#ifndef SYSTEM_TIMER_H
#define    SYSTEM_TIMER_H

#ifdef    __cplusplus
extern "C" {
#endif

#include <xc.h>
#include "custom_types.h"
    
#define T0CON0_EN_Y             (0b1<<7)
#define T0CON0_EN_N             (0b0<<7)

#define T0CON0_MD16_16          (0b1<<4)
#define T0CON0_MD16_8           (0b0<<4)

#define T0CON0_POSTSCALE_16     0b1111
#define T0CON0_POSTSCALE_15     0b1110
#define T0CON0_POSTSCALE_14     0b1101
#define T0CON0_POSTSCALE_13     0b1100
#define T0CON0_POSTSCALE_12     0b1011
#define T0CON0_POSTSCALE_11     0b1010
#define T0CON0_POSTSCALE_10     0b1001
#define T0CON0_POSTSCALE_9      0b1000
#define T0CON0_POSTSCALE_8      0b0111
#define T0CON0_POSTSCALE_7      0b0110
#define T0CON0_POSTSCALE_6      0b0101
#define T0CON0_POSTSCALE_5      0b0100
#define T0CON0_POSTSCALE_4      0b0011
#define T0CON0_POSTSCALE_3      0b0010
#define T0CON0_POSTSCALE_2      0b0001
#define T0CON0_POSTSCALE_1      0b0000

#define T0CON1_CS_CLC1          (0b111<<5)
#define T0CON1_CS_SOSC          (0b110<<5)
#define T0CON1_CS_MFINTOSC      (0b101<<5)
#define T0CON1_CS_LFINTOSC      (0b100<<5)
#define T0CON1_CS_HFINTOSC      (0b011<<5)
#define T0CON1_CS_FOSC4         (0b010<<5)
#define T0CON1_CS_PIN_I         (0b001<<5)
#define T0CON1_CS_PINN_I        (0b000<<5)

#define T0CON1_ASYNC_N          (0b1<<4)
#define T0CON1_ASYNC_Y          (0b0<<4)

#define T0CON1_PRESCALE_32768   0b1111
#define T0CON1_PRESCALE_16384   0b1110
#define T0CON1_PRESCALE_8192    0b1101
#define T0CON1_PRESCALE_4096    0b1100
#define T0CON1_PRESCALE_2048    0b1011
#define T0CON1_PRESCALE_1024    0b1010
#define T0CON1_PRESCALE_512     0b1001
#define T0CON1_PRESCALE_256     0b1000
#define T0CON1_PRESCALE_128     0b0111
#define T0CON1_PRESCALE_64      0b0110
#define T0CON1_PRESCALE_32      0b0101
#define T0CON1_PRESCALE_16      0b0100
#define T0CON1_PRESCALE_8       0b0011
#define T0CON1_PRESCALE_4       0b0010
#define T0CON1_PRESCALE_2       0b0001
#define T0CON1_PRESCALE_1       0b0000
    
    
volatile ct_bitmap8 st_vtimer,st_vtmrchg;
ct_bitmap16      st_systim, st_tmrchg;

void ST_interrupt(void);  //Timer 0 interrupt handler
void ST_start(void);      //start system timer
void ST_stop(void);       //stop system timer
void ST_continue(void);   //continue timer after stop
void ST_poll(void);       //Main loop timer 0 polling call

#ifdef    __cplusplus
}
#endif

#endif    /* SYSTEM_TIMER_H */
system_timer_definitions.h:
#ifndef SYSTEM_TIMER_DEFINITIONS_H
#define    SYSTEM_TIMER_DEFINITIONS_H

#ifdef    __cplusplus
extern "C" {
#endif

#include "system_timer.h"

#define TIMER0_SLEEP  //undefine to sleep between timer interrupts for power save

// LFINTOSC -- 31Khz Base -- mod 31 -- 1ms time base

#define system_timer_t0con0 (T0CON0_EN_Y + T0CON0_MD16_8 + T0CON0_POSTSCALE_1)
#define system_timer_t0con1 (T0CON1_CS_LFINTOSC + T0CON1_ASYNC_N + T0CON1_PRESCALE_1)
#define system_timer_period (31-1);     //30 + 1 counts per ms

//To calculate time constants

#define clctim(ms,msbase) ((unsigned long) ms*1000 / (unsigned long) msbase)


//Time Base Definitions [uS]

#define T1ms    1000
#define T2ms    2000
#define T4ms    4000
#define T8ms    8000
#define T16ms    16000
#define T32ms    32000
#define T64ms    64000
#define T128ms    128000

#define T256ms    256000
#define T512ms    512000
#define T1s     1024000
#define T2s     2048000
#define T4s     4096000
#define T8s     8192000
#define T16s    16384000
#define T32s    32768000


//Timer 0 Bit Constant Definitions

#define TB2ms    st_systim.bitv.b0
#define TB4ms    st_systim.bitv.b1
#define TB8ms    st_systim.bitv.b2
#define TB16ms    st_systim.bitv.b3
#define TB32ms    st_systim.bitv.b4
#define TB64ms    st_systim.bitv.b5
#define TB128ms st_systim.bitv.b6
#define TB256ms    st_systim.bitv.b7

#define TB512ms    st_systim.bitv.b8
#define TB1s    st_systim.bitv.b9
#define TB2s    st_systim.bitv.b10
#define TB4s    st_systim.bitv.b11
#define TB8s    st_systim.bitv.b12
#define TB16s    st_systim.bitv.b13
#define TB32s    st_systim.bitv.b14
#define TB64s    st_systim.bitv.b15

//Timer 0 Change Constant Definitions

#define TC1ms    st_tmrchg.bitv.b0
#define TC2ms    st_tmrchg.bitv.b1
#define TC4ms    st_tmrchg.bitv.b2
#define TC8ms    st_tmrchg.bitv.b3
#define TC16ms    st_tmrchg.bitv.b4
#define TC32ms    st_tmrchg.bitv.b5
#define TC64ms    st_tmrchg.bitv.b6
#define TC128ms st_tmrchg.bitv.b7

#define TC256ms    st_tmrchg.bitv.b8
#define TC512ms    st_tmrchg.bitv.b9
#define TC1s    st_tmrchg.bitv.b10
#define TC2s    st_tmrchg.bitv.b11
#define TC4s    st_tmrchg.bitv.b12
#define TC8s    st_tmrchg.bitv.b13
#define TC16s    st_tmrchg.bitv.b14
#define TC32s    st_tmrchg.bitv.b15


#ifdef    __cplusplus
}
#endif

#endif    /* SYSTEM_TIMER_DEFINITIONS_H */
Sorry i do not understand the program, my assumption is i need to include these .c files in my project, compile them and use some of the #defines to identify the timeout. Is it correct?
A slave device transmits only when prompted by the master device. Hence no two devices can transmit at the same time.
Ok now i can use one timer that is big relief. Can you help how do i access the data which is shared between the interrupt and the main routine?
 

joeyd999

Joined Jun 6, 2011
6,331
Sorry i do not understand the program, my assumption is i need to include these .c files in my project, compile them and use some of the #defines to identify the timeout. Is it correct?

Ok now i can use one timer that is big relief. Can you help how do i access the data which is shared between the interrupt and the main routine?
I gave you an example of my code, written for a particular processor (in this case, a Microchip PIC18F57Q84), so you (or other readers) can understand what I do.

If you want to use my method, you will need to modify my code so that it is compatible with whatever MCU/CPU you're using. You will also need to understand the major limitation (not really a limitation, though) of my method: blocking code will cause you major headaches. You may need to change your coding philosophy (and, possibly, some actual code).

I am sure others will chime in to tell you that my way is wrong (whatever.) and that their way is better.

Your job is to understand the problem, choose a solution, and implement it by writing and testing some code.
 

Thread Starter

Vihaan@123

Joined Oct 7, 2025
254
Create a global char array. The array is accessible from any function or subroutine in the program.
But for example
Code:
uint8_ data_avaialable_flag
I am setting in the interrupt and clearing in the main loop, so in case if both the interrupt and main are accessing at the same time how do i handle? Shall i disable interrupts before clearing the flag in main routine, will i not loose messages?
 

MrChips

Joined Oct 2, 2009
34,866
In master-slave communications, sending and receiving messages are synchronized.
The data_available_flag will not get set because the master has not yet given permission to transmit.
 

Irving

Joined Jan 30, 2016
5,139
In master-slave communications, sending and receiving messages are synchronized.
The data_available_flag will not get set because the master has not yet given permission to transmit.
In the TS's requirement the dataAvailable flag is for some other process to take the packet and forward it to another consumer (RS232?) while the first process is sending the acknowledgement back to the slave. There may be a need to synchronize depending on relative flow rates. We don't yet have enough info.
 

nsaspook

Joined Aug 27, 2009
16,334
I hope FSM is finite state machine, yes i plan to use for example as below
View attachment 366277
whenever i receive a message based on the data received, i send response and change state accordingly.
Cool. For one protocol project I used three to handle the process.

1777042314520.png

RX
TX
Message
Code:
typedef enum {
        LINK_STATE_IDLE = 0,
        LINK_STATE_ENQ,
        LINK_STATE_EOT,
        LINK_STATE_ACK,
        LINK_STATE_DONE,
        LINK_STATE_NAK,
        LINK_STATE_ERROR
    } LINK_STATES;
    typedef enum {
        LINK_ERROR_NONE = 10,
        LINK_ERROR_T1,
        LINK_ERROR_T2,
        LINK_ERROR_T3,
        LINK_ERROR_T4,
        LINK_ERROR_CHECKSUM,
        LINK_ERROR_NAK,
        LINK_ERROR_ABORT,
        LINK_ERROR_SEND
    } LINK_ERRORS;

    LINK_STATES m_protocol(LINK_STATES *);
    LINK_STATES r_protocol(LINK_STATES *);
    LINK_STATES t_protocol(LINK_STATES *);
1777042189395.png
1777042217551.png

https://forum.allaboutcircuits.com/...c18f57k42-and-pic18f47q84.157503/post-1954650
Original prototype testing
 
Last edited:
Top