News:

;) This forum is the property of Proton software developers

Main Menu

How to disable interrupts in critical section?

Started by marekf, Sep 12, 2023, 07:54 PM

Previous topic - Next topic

marekf

I am facing a peculiar problem on an old PIC18F6622 where ADC results data get corrupted possibly due to interrupt (ISR handler). The ADC result is multiplied and converted to float in a subroutine. Is there any way to temporary disable interrupts inside a subroutine to allow the calculation to be finished?

trastikata

Just clear the corresponding IE bit and then set it back after the critical section.

marekf

Is there one command that would disable all interrupts? I found an old reference to 'Disable' and 'Enable' compiler keywords, but not sure if it does anything. Would GIE=0 work?

trastikata

You can use GIE = 0/1. Keep in mind you still have to clear all interrupt flags prior to re-enabling the interrupts.

Normally the ISR does not interfere with any of the calculations done in the main program unless they are using the same variables, you should look for problems elsewhere.

John Drew

Quote from: trastikata on Sep 12, 2023, 09:26 PMYou can use GIE = 0/1. Keep in mind you still have to clear all interrupt flags prior to re-enabling the interrupts.

Normally the ISR does not interfere with any of the calculations done in the main program unless they are using the same variables, you should look for problems elsewhere.
I agree with Trastikata, you have a problem elsewhere. Interrupts do not interfere with code or peripherals to the best of my knowledge. The only time I had interference with an A/D conversion was when I connected a serial peripheral to a computer where the port was putting a high voltage (>5V) on the serial port peripheral and somehow finding it's way back into other peripherals. That was NOT an interrupt problem. I use interrupts a lot and find them very easy and safe to use, but I'm assuming you make them fast (minimum code) and completely avoid delays or systems that have delays such as writing to serial inside the interrupt.
Just follow Les's guidelines.
John

SCV

Les gave me some good advice a while ago, there are scenarios where GIE = 0 doesn't always work due to timing peculiarities, so his suggestion is to...

WHILE GIE = 1
GIE = 0
WEND

Tim

TimB


I suspect the issue is your interrupt code. Can you post it?


marekf

Thank you for all your inputs. It seems the problem is elsewhere...

My ISR is like this:
com2_isr:    'Interrupt Service Routine

             'These bits are set at the beginning of the program before "main"
             'bit GIE   (INTCON.7=1)  enables global interrupts
             'bit PEIE  (INTCON.6=1) enables peripheral interrupts
             'bit RC2IE (PIE3.5=1)    enables receive interrupt
            Context Save  'start ISR

            If RC2IF == 1 Then
                TMR4ON = 0    ' stop the Timer4
                TMR0ON = 0    ' stop TMR0
                TMR4IF = 0    ' clear TMR4 flag
                TMR0IF = 0    ' clear TMR0 flag

                If newFrame == 1 Or rx_len == 0 Then      'New frame start
                    For ix1=0 To 7
                        rx_bufr[ix1]=0
                    Next
                    newFrame = 0   ' new frame received
                    rx_len = 1     ' first byte received
                EndIf
                If rx_len <= RXBUFMAX Then
                    rx_bufr[rx_len-1] = HRSIn2,{5000,mb_timeout} 'read byte from COM2 and save in buffer
                    Inc rx_len
                Else
                    rx_len = 0    ' buffer is full discard all the data
                EndIf

                TMR0ON=0           ' Stop the Timer0, TMR0ON=0
                T0CON=TMR0SCALE    'set prescaler option
                T08BIT=1           'set 8-bit mode for Timer0
                T0CS=0             'internal clock
                TMR0L=T0VAL        ' time period = 255-T0VAL
                TMR0IF=0           ' clear TMR0 flag before starting the timer
                TMR0ON = 1         ' start the Timer0
               
                TMR4ON = 0         ' stop the Timer4
                PR4=T4PR           ' set TMR4 period register (4000/16=255)
                T4CON=TMR4SCALE    ' set prescale and postscale
                TMR4IF = 0         ' clear TMR4 flag
                TMR4ON = 1         ' start the Timer4

            EndIf

            If TMR0IF == 1 Then
                ' at least 1.5 characters time span has passed
                TMR0ON = 0    ' stop the Timer0
                TMR0IF = 0    ' clear TMR0 flag
                If rx_len < 8 Then
                    ' Not enough bytes received, so discard all
                    TMR4ON = 0    ' stop the Timer4
                    TMR4IF = 0    ' clear TMR4 flag
                    rx_len = 0    ' clear number of bytes received
                    newFrame = 1
                EndIf
            EndIf

            If TMR4IF == 1 Then
                TMR4ON = 0    ' stop the Timer4
                TMR4IF = 0    ' clear TMR4 flag
                TMR0ON = 0    ' stop the Timer0
                TMR0IF = 0    ' clear TMR0 flag
                ' ~ 4 ms without new char

                If rx_len < 8 Then 'Frame too short
                        rx_len = 0
                        newFrame = 1
                Else
                    Set flagRxRcvd
                EndIf
            EndIf

            Context Restore   'end ISR

my ADC subroutine looks like this:

Dim amzero As Float   'variable to store ammeter zero offset
Dim amcal  As Word    'variable to store ammeter gain factor
Dim ampav  As Float   'variable to store scaled output current
Dim amps   As Word    'variable to store output current for averaging
Dim an7val As Word    'storage for ADC result


getamps:
amps=0
For i=1 To 10
    an7val = ADIn 7
    DelayUS 1
    amps= amps + an7val   'sum the samples
Next i
ampav=(amps/10)+amzero  'calculate the average of 10 current reads
ampav=(ampav*amcal)/10000
If ampav < -.0 Then ampav = 0.0
Return

Unfortunately I cannot share the code in full.

trastikata

Depending on the impedance, 1us might not be enough to charge/discharge the S&H caps.

TimB

#9
Looking at your interrupt code I see some code that has issues


 rx_bufr[rx_len-1] = HRSIn2,{5000,mb_timeout} 'read byte from COM2 and save in buffer

You by the looks of it correctly generate an interrupt on the USART2 check the int flag RCIF2 then rather than just copy the data from RCREG2 into the array you start using non interrupt routines HRSIN with a time out!

If you by the looks of it you are trying to have a 1.5 x byte time then I really recommend you do it using better code


Here is my code doing the same


'*=============================================================*
'   Usart interrupt
'   Hanldes the TXing and Rxing of data
'*=============================================================*



Usart_Int_Handler:
    Context Save

    If RC1STAbits_OERR = 1 Then
         GoTo USART_ERR1                                 ' Check for usart1 overrun
    Endif

    If RC2STAbits_OERR = 1 Then
         GoTo USART_ERR1                                 ' Check for usart2 overrun
    Endif
    ' Received



    If PIR3bits_RC2IF = cTrue Then                                              ' If this is a RX2 interrupt
        bRXPacketTout = cRXPacketTmrValue
        aRX2DataBuffer[bRX2DataBufferPntr] = RCreg2                             ' Place that data in the array Note it is not a ring buffer!
        if bRX2DataBufferPntr < RX2DataBufferSize - 1 then                      ' Prevent the pointer going over the end of the array
            inc bRX2DataBufferPntr
        Endif

    Endif





    Context Restore

USART_ERR1:                                                                 ' Handle a usart overrun error
    WREG = RCREG1                                                               ' Clear the regs
    RC1STAbits_CREN = 0
    RC1STAbits_CREN = 1
    bRX2DataBufferPntr = 0

    WREG = RCREG2                                                               ' Clear the regs
    RC2STAbits_CREN = 0
    RC2STAbits_CREN = 1
    'bRX2DataBufferPntr = 0

    GoTo Usart_Int_Handler                                                      ' Jump back to the start


Notice the line bRXPacketTout = cRXPacketTmrValue

That loads a counter that an few lines of code in a timer interrupt processes

'------------------------------------------------------------------------------------
' Interrupt handler for
' Input     : None
' Output    : None
' Notes     : None
''
Timer_Int_Handler:

    Context Save


    If PIR1bits_TMR1IF = cTrue Then
        T1CONBits_TMR1ON = cFalse
        wrTimer1 = wrTimer1 + cTimer1_ms_Reload
        T1CONBits_TMR1ON = cTrue

        ' Here we handle the packet timer. If we RX a byte the timer is loaded and when it reaches 0 as we have no more data then
        ' we set a flag so the data can be parsed

        If bRXPacketTout > 0 then
            Dec bRXPacketTout
            If bRXPacketTout = 0 then
                xRX2PacketReady = Ctrue
            Endif
        Endif



        PIR1bits_TMR1IF = 0

        EndIf

    Context Restore

I'm not saying that the changes will fix the ADC read issues but it will defiantly prevent other issues

marekf

Thank you Tim.
This helps.
I am glad I joined this forum.