News:

Let's find out together what makes a PIC Tick!

Main Menu

Interrupt for USART

Started by chris_cb_uk, Oct 27, 2021, 05:21 PM

Previous topic - Next topic

chris_cb_uk

Hi all, I'm establishing a 2 way communication between pics separated over fibre over quite a distance.  I have comms working of a fashion using timeout's to jump to the next sequence to avoid hanging i.e. using the wait function, however thinking that having the pics running on interrupts for their receive would be the best solution to make sure I'm not missing bytes from overflowing and or the programs are running efficiently.  I'm poking I2C sensors and Dallas devices to transmit their values and waiting for these to reply is causing some issues.

I'm using 18F4550 using hrsout on the hardware EUSART ports, I've never dealt with interrupts but understand the flags I'm looking for are RCIF 1 being full, 0 being empty.  I just need a little help in how to correctly assign the interrupt, calling the function and how to restore out of it.

Any help appreciated.

Chris

TimB

#1
Les in the samples has a USART interrupt example

I have included some I got from a bit of code I have. Loads of non useful stuff but it covers most of what you need.

I highly recommend you use a timed packet system. Where you time the period between data. If the data does not all come through then after it times out you reset your statemachine to ignore that packet and wait for the next one.

The Modbus system kind of works like that. With work you can also implement an ACK system so if the data sent has a CRC (it needs to) you can send that back to say OK got some data.
The RX code should also check the CRC is right before sending the ACK. The TX side can time for the ACK. If it does not get it within x ms then it resends. Perhaps for for x times. If after x retries it can signal data lost to slave and take action.

; Set up the usart to run at the right speed and interrupt on high level ints

;Usart 1
;Calculated Baudrate = 38462 @ Xtal 48MHz, Error = 0.16%
    RCSTA = 144 ; Enable continuous receive
    TXSTA = 36  ; Enable transmit, BRGH = 1
    SPBRG = 77  ; Baud Rate Generator Value

    ;Calculated Baudrate = 38462 @ Xtal 48MHz, Error = 0.16%
    RCSTA2 = 144 ; Enable continuous receive
    TXSTA2 = 36  ; Enable transmit, BRGH = 1
    SPBRG2 = 77  ; Baud Rate Generator Value

;Usart 2
; ENABLE USART RECEIVE INTERRUPTS
    RC1IP = 1                                                                     ; USART1 HAS HIGH PRIORITY
    RC2IP = 1                                                                     ; USART2 HAS HIGH PRIORITY
    RCIE = 1
    RCIE = 1
    RC2IE = 1
    CREN1 = 1
    CREN2 = 1
    SPEN1 = 1
    SPEN2 = 1
   
    TX1IP = 1                                                                     ; TX Usart Int is high priority
    TX2IP = 1                                                                     ; TX Usart Int is high priority
    TX1GenDataSent = True                                                         ; Info flags
    TX2DataSent = True
   
    TRISC.6 = 0                                                                   ' Set the TX ports as output so the data can come out
    TRISB.6 = 0


;-------------------------------------------------------------------------------

;*=============================================================*
;   Usart interrupt
;   Hanldes the RX and TX of data
;*=============================================================*


Usart_Int:
    Reset_Bank
    If OERR1 = 1 Then GoTo USART_ERR1                                           ; Check for usart1 overrun
    If OERR2 = 1 Then GoTo USART_ERR2                                           ; Check for usart1 overrun

    HiIntFSRSave = FSR_0                                                        ; Save the FSR Reg

    If RCIF = True Then                                                         ; If this is a RX1 interrupt
        HighIntTemp = RCREG1                                                    ; Make a working copy of the data
        RX1DataBuffer[RX1DataBufferPointer] = HighIntTemp                       ; Load the data into our buffer
        If RX1DataBufferPointer < RX1DataBufferSize Then                        ; make sure we are below our buffer size
            Inc RX1DataBufferPointer                                            ; Move our pointer up
            RX1FrameTimer = RX1FrameTimerReloadVal
        Else                                                                    ; too much data to quick reset needed
            RX1FrameTimer = RX1FrameTimerReloadVal
            RX1DataBufferPointer = 0                                            ; Reset the buffer pointers and timers
        EndIf
    EndIf

    If RC2IF = True Then                                                        ; If this is a RX2 interrupt
        HighIntTemp = RCREG2                                                    ; Make a working copy of the data
        RX2DataBuffer[RX2DataBufferPointer] = HighIntTemp                       ; Load the data into our buffer
        If RX2DataBufferPointer < RX2DataBufferSize Then                        ; make sure we are below our buffer size
            Inc RX2DataBufferPointer                                            ; Move our pointer up
        RX2FrameTimer = RX2FrameTimerReloadVal
        Else                                                                    ; too much data to quick reset needed
            RX2FrameTimer = RX2FrameTimerReloadVal
            RX2DataBufferPointer = 0                                            ; Reset the buffer pointers and timers
        EndIf
    EndIf

    ; Code to send the data from the TX buffer1
    If TX1IE = 1 Then                                                           ; Only do the rest if the TX1 interrupts are enabled, its our way to control the system
        If TX1IF = True Then                                                    ; If this is a TX interrupt
            If TX1PacketType = TX1PType_MainData Then
                If TX1GenDataBufferPointer >= TX1GenBufferCount Then            ; Check if there is more data to send
                    TX1IE = 0                                                   ; Buffer empty so turn of interrupts for now
                    TX1FrameTimer = TX1FrameTimerReloadVal
                Else
                    TXREG1 = TX1GenDataBuffer[TX1GenDataBufferPointer]          ; Send the data
                    Inc TX1GenDataBufferPointer                                 ; Move the pointer to the next byte
                    TX1GenDataSent = False                                      ; Mark there is still data to send
                    TX1FrameTimer = TX1FrameTimerReloadVal
                EndIf
            Else
                If TX1MsgReplyBufferPointer >= TX1MsgReplyPacket Then           ; Check if there is more data to send
                    TX1IE = 0                                                   ; Buffer empty so turn of interrupts for now
                    TX1FrameTimer = TX2FrameTimerReloadVal
                Else
                    TXREG1 = TX1MsgReplyBuffer[TX1MsgReplyBufferPointer]        ; Send the data
                    Inc TX1MsgReplyBufferPointer                                ; Move the pointer to the next byte
                    TX1GenDataSent = False                                      ; Mark there is still data to send
                    TX1FrameTimer = TX1FrameTimerReloadVal
                EndIf
            EndIf
        EndIf
    EndIf

    ; Code to send the data from the TX buffer2
    If TX2IE = 1 Then                                                           ; Only do the rest if the TX1 interrupts are enabled, its our way to control the system
        If TX2IF = True Then                                                    ; If this is a TX interrupt
            If TX2PacketType = TX2PType_MainData Then
                If TX2DataOutBufferPointer >= TX2BufferCount Then               ; Check if there is more data to send
                    TX2IE = 0                                                   ; Buffer empty so turn of interrupts for now
                    TX2FrameTimer = TX2FrameTimerReloadVal
                Else
                    TXREG2 = TX2GenDataBuffer[TX2DataOutBufferPointer]          ; Send the data
                    Inc TX2DataOutBufferPointer                                 ; Move the pointer to the next byte
                    TX2DataSent = False                                         ; Mark there is still data to send
                    TX2FrameTimer = TX2FrameTimerReloadVal
              EndIf
          Else
              If TX2MsgReplyBufferPointer >= TX2MsgReplyPacket Then           ; Check if there is more data to send
                    TX2IE = 0                                                   ; Buffer empty so turn of interrupts for now
                    TX2FrameTimer = TX2FrameTimerReloadVal
              Else
                    TXREG2 = TX2MsgReplyBuffer[TX2MsgReplyBufferPointer]        ; Send the data
                    Inc TX2MsgReplyBufferPointer                                ; Move the pointer to the next byte
                    TX2DataSent = False                                         ; Mark there is still data to send
                    TX2FrameTimer = TX2FrameTimerReloadVal
              EndIf
          EndIf
        EndIf
    EndIf

USART_EXIT:
    FSR_0 = HiIntFSRSave
    USART_EXIT2:
    Retfie Fast

USART_ERR1:                                                                     ; Handle a usart overrun error
    WREG = RCREG1                                                               ; Clear the regs
    CREN1 = 0
    CREN1 = 1
    TX1FrameTimer = 0                                                           ; Clear the regs
    RX1DataBufferPointer = 0
    GoTo Usart_Int                                                              ; Jump back to the start
   
USART_ERR2:                                                                     ; Handle a usart overrun error
    WREG = RCREG2                                                               ; Clear the regs
    CREN2 = 0
    CREN2 = 1
    TX2FrameTimer = 0                                                           ; Clear the regs
    RX2DataBufferPointer = 0                                                    ; Reset the buffer pointer
    GoTo Usart_Int



chris_cb_uk

Thanks Tim, I've picked out some key points in your code that I was lacking understanding of for interrupts. 
Interesting about modbus, I have experience of this from PLC backgrounds in a previous life programming S7 Siemens Ladder.  I think this will be the most robust solution for avoiding clashes, time outs and enabling everything to be clocked in, and out with certainty backwards and fourth.

Teo

Hi Tim,
How can I use interrupts for USART 1 on the 18F26K22?
I used:
PIE1 = %00100000
If PIR1.5 = 1 Then ....
and it doesn't work.
Thanks for the help,
Teo

charliecoutas

Hi Teo

I'm sure Tim won't mind me helping you a little with this. What else have you set up? You need to set some more things up, enable interrupts, baud rate, set the flag RC1IE etc.

Charlie

Teo

Hi Charlie,

    INTCON.6 = 1        ' Enable Peripheral Interrupt
    INTCON.4 = 1        ' Enable PORTB IOC
    INTCON.7 = 1        ' Global interrupt enable
    INTCON2.6 = 0       ' Interrupt on falling edge
    PIE1.5 = 1

Thanks in advance,
Teo

RGV250

#6
Hi Teo,
How about posting whole but (not) working code so we can see everything. From memory you need to clear the interrupt flag in the interrupt or it will only occur once.

Have you looked at the "Buffered_Hserin_Test" in the new samples folder.

Regards,
Bob

Teo

Thank you,
I am looking now.
Have a pleasant evening,
Teo

CPR

Here is what I used to receive bytes on UART1 from a Nextion display. This code is within my interrupt routine.
PIC 18F26K22 @ 64Mhz

'//---------------------    RX UART1

            If RC1IE = 1 Then
            If RC1IF = 1 Then                                   '// There are Byte(s) received from local Nextion on UART1

                If RCSTA1.2 = 1 Then
                        dummy1 = RCREG1                         '// Note: Reading RCREG1 resets RC1IF
                    ElseIf RCSTA1.1 = 1 Then
                        RCSTA1.4 = 0
                        RCSTA1.4 = 1
                    Else
                        dummy1 = RCREG1
                        inComing1[inPointer1] = dummy1
                        nextFlag = 1                            '// Byte(s) have arrived/ing from Nextion to PIC
                       
                        Inc inPointer1
                    If  inPointer1 > SS Then
                        inPointer1 = 0
                    EndIf
                EndIf

            EndIf
            EndIf


Make sure you've set PMD0.6 to "0" (to power on the module) That was a "gotcha" that had me for a while...

'// UART1
        Symbol UART1MD  = PMD0.6
        UART1MD         = 0                '// "0" Power to module is enabled

To write a byte(s) to UART1 I used -
                                  '// UART1 WRITE ONE BYTE
Proc writeUart1(toSend1 As Byte)
   
        TXREG1 = toSend1
        While TRMT1 = 0
            Nop
        Wend

EndProc

Hope that helps

Teo

Hi,
Thank you all for the help.
I had made a wrong setting. Now it's OK.
Have a nice Sunday,
Teo

Teo

Hi,
I don't understand why this is happening .

   Main:   
     Setup()     
     Do       
        If x = 1 Then
           If Date_Rec = $1B  Then
              Comanda()             
           EndIf
        EndIf
     Loop
   
    ISR_HANDLER:
       Context Save   
       If PIR1.5 = 1 Then
          x=1
          Date_Rec = RC1REG         
       EndIf   
       Context Restore

'---------------------------------------------------------------------------------------------   
   Proc Comanda()
      If x=1 Then
         If Date_Rec = $4B Then
            'HRSOut "K"
            HRSOut2 "R"
            x=0
         EndIf
      EndIf
   EndProc

If I have
HRSOut "K"
HRSOut2 "R"
both USARTs will work.
If I only have
HRSOut2 "R"
it does not work.
Does anyone have any idea and how I can solve the problem?
Thanks in advance,
Teo
 

charliecoutas

Are you sure that you have the values right Teo? When you check what has been received, you only accept $1B (Escape in ASACII):

     Do       
        If x = 1 Then
           If Date_Rec = $1B  Then
              Comanda()             
           EndIf
        EndIf
     Loop

Charlie

Mapo

Hi Teo,
are you sure you have set the 2 UART ports correctly?

Teo

Hi,
It seems like there is some interference between the USART1 and USART2 settings.
Thanks in advance,
Teo