News:

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

Main Menu

Rx usart synchronism through interrupt

Started by Giuseppe, Sep 04, 2021, 09:32 PM

Previous topic - Next topic

Giuseppe

Greetings
I am using 2 hc-12 modules controlled in hardware serial. Now what is the best method for you to synchronize interrupt reception?
I put under a piece of code that sometimes fails to synchronize reception with transmission


int_routine:

Context Save

If RCIF = 1 Then     'Interrupt occurred for rx
   Clear RCIF        'Gate flag interrupt rx 

 buf[buft] = RCREG  'Load RCREG in the bytes array                                         
 Inc buft           'Array index increment because I have to receive 8 bytes
 
 If start_buf = 0 Then
  If buft > 1 Then                   'I received 2 bytes I'm going to analyze if they are correct
   If buf[0] = 8 And buf[1] = 5 Then 'If I receive 8 as the first byte and 5 as the second I continue to receive
     start_buf = 1
    Else
    Clear buf   'Delete the 2 bytes received incorrect
    Clear buft  'Reset index of array bytes
   EndIf
  EndIf
 EndIf
 
  If buft > buft_end Then    '
   rx_end = 1
   flag.2 = 0
   buft = 0
   buft_end = 7
   start_buf = 0
  End If
EndIf

Context Restore 



I am thinking that it is a code with problems because when I meet to receive the 7 byte as buf
  • and then the 0 byte as buf [1] I lose the synchronism what do you think?





trastikata

Hello Giuseppe,

could you post the USART settings, what is the transmission rate? Maybe it is the baud rate error that kicks in from time to time due to clock variations if it is the 8th byte where you loose the sync - have you calculated the baud rate error?

Just as a side note, I'd also add in the interrupt handler for the Overrun Error Bit to reset CREN.

 

Giuseppe

'-----Set serial hardware-------
Declare Hserin_Pin PORTB.1    'PIN 7 Rx 16f1827
Declare Hserout_Pin PORTB.2   'PIN 8 Tx 16f1827
Declare Hserial_Baud 9600
Declare  Hserial_TXSTA = %00100100 ' tx 8bit(bit6) high speed on (bit2)
Declare  Hserial_RCSTA = %10010000 '
Declare  Hserial_Clear = On
'--------Set  Interrupt----------
INTCON = %00000000  '
PIE1 = %00000000    '
PIR1 = %00000000    '

Symbol GIE = INTCON.7
Symbol PEIE = INTCON.6
Symbol RCIE = PIE1.5
Symbol RCIF = PIR1.5

Set PEIE    'on interrupt peripherals
Set RCIE    'on interrupt rx usart
Set GIE     'on interrupt global


In the past when I couldn't synchronize the transmission with the reception I used the Hserin Wait function. Then once synchronized I used the interrupt to receive the bytes and did not miss a beat. Now I wanted to improve the system and do everything with the interrupt

trastikata

I took a glance at those hc-12 modules - they seem to use "AT" commands, can I assume the 8 & 5 preamble is sent only one time? Make sure you are holding the module in reset till the MCU is ready to receive.

Also you said you are connecting two modules - how do you arrange for the communication to a single pin?

Giuseppe

Preamble 8 and 5 send each transmission and then subsequently send other data for a total of 8 bytes.
Already not sending data before the module is ready also wait 200mS at program start before enabling global interrupt.
The communication is not single pin but there is a master card with an hc-12 module connected in hardware serial and a slave card with another hc-12 module connected in hardware serial with 2 pins tx and rx

trastikata

#5
Quote from: Giuseppe on Sep 05, 2021, 08:44 AMPreamble 8 and 5 send each transmission and then subsequently send other data for a total of 8 bytes.

And the rest of the data is all ASCII code or the combination of 8 and 5 can't occur in the middle of the byte array?

Anyway I think I see the problem - you are not using a sliding window buffer to check for the preamble byte by byte but rather you are checking the entire 2 byte preamble - so I think this logic expression here causes the problem "If buf[0] = 8 And buf[1] = 5 Then".

See, what happens if the MCU starts receiving while the hc-12 is already transmitting? For example your very first byte to arrive in the USART buffer is the 8th byte of the transmission, then because you are looking for two bytes preamble at the same time and then reset, it will never come in sync because 8 and 5 will never fall in the same two-byte window at the same time.

Therefore I'd rather look for the preamble byte by byte and reset the counter as soon as wrong byte is received. Thus, look first for 8, reset as soon as it is wrong, however if it is correct, now look for 5 as next byte, if it is wrong - reset, if it is correct - sync is done and can start reception.

Giuseppe

#6
Select start_buf
Case 0
   If buf[0] = 8 Then
      start_buf = 1
   Else
    Clear buf   '
    Clear buft  '
   EndIf
Case 1
   If buf[1] = 5 Then
       start_buf = 2
    Else
    Clear buf   '
    Clear buft  '
    start_buf = 0
   EndIf
EndSelect

Doing with a case you can analyze first the buf
  • then the buf [1]
Was this your idea?

trastikata

Quote from: Giuseppe on Sep 05, 2021, 02:37 PMWas this your idea?

No, you are still waiting to receive two bytes and then analyze them at the same time. This is what I meant:

    buf[buft] = RCREG                           'Load RCREG in the bytes array                                         
    Inc buft                                    'Array index increment because I have to receive 8 bytes
 
    If start_buf = 0 Then
        If buft = 1 Then                        '1'st byte received
            If buf[0] <> 8 Then                 'If it doesn't match
                Clear buf                       'Delete byte
                Clear buft                      'Reset index of array bytes
            EndIf                               'If it matches do nothing
        EndIf
        If buft = 2 Then                        '2'nd byte received
            If buf[1] <> 5 Then                 'If it doesn't match
                Clear buf                       'Delete byte
                Clear buft                      'Reset index of array bytes
            Else
                start_buf = 1                   'If it matches then sync is done
            EndIf                               
        EndIf
    EndIf
 
    If buft > buft_end Then   
        rx_end = 1
        flag.2 = 0
        buft = 0
        buft_end = 7
        start_buf = 0
    End If

Giuseppe

Excellent trastikata solution.
Also rereading my code that I posted above I forgot to tell you that start_buf is a byte so at the first reception of buf
  • only case 0 will be enabled on the second reception case 1 I don't know if you noticed this. I am adding a more complete version of the code below so you can see if it is valid for you.
I thought about using a select end select to speed up the interrupt routine

int_routine:

Context Save

If RCIF = 1 Then     '
   Clear RCIF         
   buf[buft] = RCREG '                                     
   Inc buft
 
 Select start_buf
 Case 0
   If buf[0] = 8 Then
      start_buf = 1
   Else
    Clear buf   '
    Clear buft  '
   EndIf
 Case 1
   If buf[1] = 5 Then
       start_buf = 2
    Else
    Clear buf   '
    Clear buft  '
    start_buf = 0
   EndIf
 Case 2
  If buft > buft_end Then    'buft_end = 7
   rx_end = 1
   flag.2 = 0
   buft = 0
   buft_end = 7
   start_buf = 0
  End If
 EndSelect
EndIf

Context Restore

TimB

This is just for info, probably not answering your questions

This is what I do

1 Interrupt for USART
2 Interrupt for a timer

In Usart interrupt you have a state machine. 0 is check for first char. Every time you get a char you increment the state machine. You can have just 2 states, start and data. Within a state you can have sub states eg counting number of bytes your expecting.
With this you can at any time reset the state to 0

Also in the Usart routing every time you get a char you reset a Reset timer.

In the timer Interrupt you increment the reset timer. If it gets > x then stop incrementing and clear the Usart State var.

Basically what you are doing is resetting the Usart state if the gap between bytes is too long. This way you can be sure that you sync with the sending device. As long as the sending device is sending data in bulk.

Tim

trastikata

Quote from: TimB on Sep 07, 2021, 07:24 AMIn the timer Interrupt you increment the reset timer. If it gets > x then stop incrementing and clear the Usart State var.

Hello Tim,

Why not monitoring the Framing Error bit (FERR) and as long as it is not set, then all is fine.

Giuseppe

In my case, whoever transmits every time sends 8 bytes.
TimB can you do a code example to understand better.

TimB

#12
Quote from: trastikata on Sep 07, 2021, 08:46 AMHello Tim,

Why not monitoring the Framing Error bit (FERR) and as long as it is not set, then all is fine.

If your interrupt system is running then you should receive all the data. The issue is that you do not know when the data starts. That is where the timer comes in. It will reset the state machine to keep it all aligned.




TimB

#13
Quote from: Giuseppe on Sep 07, 2021, 12:16 PMIn my case, whoever transmits every time sends 8 bytes.
TimB can you do a code example to understand better.


In this lot there is all the code I have on hand

Note that there is more going on in it. EG it will work with variable packet sizes without having to know the packet size in advance. If its collected the minimum data it will let you know. It was written 10 years back so going of a cursory glance not memory. Modify to suit. You really need to understand what your doing and this is just a guide.







'****************************************************************
'*  Name    : UNTITLED.BAS                                      *
'*  Author  : [select VIEW...EDITOR OPTIONS]                    *
'*  Notice  : Copyright (c) 2011 [select VIEW...EDITOR OPTIONS] *
'*          : All Rights Reserved                               *
'*  Date    : 12/12/2011                                        *
'*  Version : 1.0                                               *
'*  Notes   :                                                   *
'*          :                                                   *
'****************************************************************



    Device 18F25K20
   
    Xtal = 16
   
    Symbol GIE = INTCON.7       ' Global Interrupt Enable Bit
    Symbol PEIE = INTCON.6      ' Peripheral Interrupt Enable Bit
    Symbol TMR0IE = INTCON.5    ' TMR0 Overflow Interrupt Enable Bit
    Symbol INT0IE = INTCON.4    ' INT0 External Interrupt Enable Bit
    Symbol RABIE = INTCON.3     ' RA And RB Port Change Interrupt Enable Bit
    Symbol TMR0IF = INTCON.2    ' TMR0 Overflow Interrupt Flag Bit
    Symbol INT0IF = INTCON.1    ' INT0 External Interrupt Flag Bit
    Symbol RABIF =  INTCON.0    ' RA And RB Port Change Interrupt Flag Bit(1)
   
    Symbol RX9D = RCSTA.0  ' 9th bit of received data (Can be parity bit) (Usart 1)
    Symbol OERR = RCSTA.1  ' Overrun Error (Usart 1)
    Symbol FERR = RCSTA.2  ' Framing Error (Usart 1)
    Symbol ADDEN = RCSTA.3 ' Address Detect Enable (Usart 1)
    Symbol CREN = RCSTA.4  ' Continuous Receive Enable (Usart 1)
    Symbol SREN = RCSTA.5  ' Single Receive Enable (Usart 1)
    Symbol RX9 = RCSTA.6   ' 9-bit Receive Enable (Usart 1)
    Symbol SPEN = RCSTA.7  ' Serial Port Enable (Usart 1)
   
    Symbol TXEN = TXSTA.5  ' Tx enable USART 1

    Symbol TMR1IF = PIR1.0 ' TMR1 Overflow Interrupt Flag bit
    Symbol TMR2IF = PIR1.1 ' TMR2 to PR2 Match Interrupt Flag bit
    Symbol CCP1IF = PIR1.2 ' CCP1 Interrupt Flag bit
    Symbol SSPIF = PIR1.3  ' Master Synchronous Serial Port Interrupt Flag bit
    Symbol TXIF = PIR1.4   ' EUSART Transmit Interrupt Flag bit
    Symbol RCIF = PIR1.5   ' EUSART Receive Interrupt Flag bit
    Symbol ADIF = PIR1.6   ' A/D Converter Interrupt Flag bit
   
    Symbol TMR1ON = T1CON.0     ' Timer1 ON
    Symbol TMR1CS = T1CON.1     ' Timer1 Clock Source Select
    Symbol NOT_T1SYNC = T1CON.2 ' Timer1 External Clock Input Synchronization Control
    Symbol T1OSCEN = T1CON.3    ' Timer1 Oscillator Enable Control
    Symbol T1CKPS0 = T1CON.4    ' Timer1 Input Clock Prescale Select bits
    Symbol T1CKPS1 = T1CON.5    ' Timer1 Input Clock Prescale Select bits
    Symbol T1RUN = T1CON.6      ' Timer1 System Clock Status bit
    Symbol RD16 = T1CON.7       ' 16-bit Read/Write Mode Enable bit
   
    Symbol IPEN = RCON.7
   
    Symbol TMR1IP = IPR1.0 ' TMR1 Overflow Interrupt Priority bit
    Symbol TMR2IP = IPR1.1 ' TMR2 to PR2 Match Interrupt Priority bit
    Symbol CCP1IP = IPR1.2 ' CCP1 Interrupt Priority bit
    Symbol SSPIP = IPR1.3  ' Master Synchronous Serial Port Interrupt Priority bit
    Symbol TXIP = IPR1.4   ' EUSART Transmit Interrupt Priority bit
    Symbol RCIP = IPR1.5   ' EUSART Receive Interrupt Priority bit
    Symbol ADIP = IPR1.6   ' A/D Converter Interrupt Priority bit
   
    Symbol TMR1IE = PIE1.0   ' TMR1 Overflow Interrupt Enable bit
    Symbol TMR2IE = PIE1.1   ' TMR2 to PR2 Match Interrupt Enable bit
    Symbol CCP1IE = PIE1.2   ' CCP1 Interrupt Enable bit
    Symbol SSPIE = PIE1.3    ' Master Synchronous Serial Port Interrupt Enable bit
    Symbol TXIE = PIE1.4     ' EUSART Transmit Interrupt Enable bit
    Symbol RCIE = PIE1.5     ' EUSART Receive Interrupt Enable bit
    Symbol ADIE = PIE1.6     ' A/D Converter Interrupt Enable bit
    Symbol reserved = PIE1.7 ' Maintain this bit clear   
   
    Dim FSR_0 As FSR0L.Word
    Dim FSR_1 As FSR1L.Word
    Dim FSR_2 As FSR2L.Word   
   
    Dim TIMER1REG As TMR1L.Word
   
   
   
   
   
   
   
; general programming defines

    Symbol True = 1
    Symbol False = 0   
   
   
   
   
;*=============================================================*
;* Description  Interrupt based defines                        *
;*=============================================================*
    Dim HiIntFSRSave As Word System
   
       
    On_Interrupt GoTo Usart_Int
   
    On_Low_Interrupt GoTo TMR_INTERRUPT





    Symbol FUDGE_FACTOR = 5
    Symbol TMR1_VAL = 63541                                         ' VALUE FOR 1000us INTERRUPTS




; Main variables
   
    ; interrupt related
    Dim HighIntTemp As Byte                                         ; temp var
    Dim PacketFrameState As Byte                                    ; State Machine
    Dim LowIntTemp As Byte
   
   
    Dim FrameCounter As Byte                                        ; Our inter packet timer
   
    Symbol NodeAddress = 0
    Symbol DataCollection = 1
   
   
   
    ; Buffer
    Symbol DataBufferSize = 32                                      ; Or packet buffer is this big
    Dim DataBuffer[DataBufferSize] As Byte                          ; Our main buffer
    Dim DataBufferPointer As Byte

    ; Packet info
    Dim DataReady As Bit                                            ; Flag to say there is data ready to puLL out of the buffer
    Dim BufferFull As Bit
   
   
    ; Safty reset timer
    Dim WatchDogTimer As Byte                                       ; Main code needs to keep resetting this or the whole pic is reset

; Temp stuff!

    Dim FrameTime As Byte
     
    Dim OurNodeAddress As $12

    Dim MinPacketSize As 3
   
    GoTo OverSubs1                                                  ; Jump over these routines
   
; commands
    $define ResetFrameTimer GoSub ResetFramSub
    $define ClrWdtimr WatchDogTimer = 0
   
   
   
ResetFramSub:
    FrameCounter = FrameTime
    Return   
     
;*=============================================================*
;   Usart interrupt
;   Hanldes the RX and TX of data
;*=============================================================*     
       
   
Usart_Int:
    Reset_Bank
    If OERR = 1 Then GoTo USART_ERR                                 ' Check for usart overrun
   
    HiIntFSRSave = FSR_0                                            ' Save the FRS Reg
   
       
    If RCIF = 1 Then
        HighIntTemp = RCREG                                         ' Make a working copy of the data
        Select PacketFrameState                                     ' What stage of the data collection are we?
       
        Case NodeAddress                                            ' We are waiting for the nodeAddress
            If HighIntTemp = OurNodeAddress Then                    ' Is this address for us?
                Inc PacketFrameState                                ' Yes so we move onto the next stage and collect the data
                ResetFrameTimer                                     ' Reset the Frame timer as we have just recieved an address
                DataBufferPointer = 0                               ' reset the buffer pointer
                BufferFull = False                                  ' Buffer cannot be full at this point
            EndIf
           
       
        Case DataCollection
            DataBuffer[DataBufferPointer] = HighIntTemp             ' Load the data into our buffer
            If DataBufferPointer < DataBufferSize Then              ' make sure we are below our buffer size
                Inc DataBufferPointer                               ' Move our pointer up
                BufferFull = False                                  ' Buffer cannot be full at this point
            Else
                BufferFull = True
            EndIf
       
        EndSelect
       
     ElseIf RCIF = 1 Then
     
     
     EndIf
     
     
     
     
   
; Thats it! End of the data collection routine   
   
   
USART_RECEIVE_EXIT:
     FSR_0 = HiIntFSRSave
USART_RECEIVE_EXIT2:                                               
     Retfie Fast
   
USART_ERR:                                                          ; Handle a usart overrun error
     WREG = RCREG
     CREN = 0
     CREN = 1
     PacketFrameState = 0                                           ; Packet is empty
     ResetFrameTimer                                                ; We need to wait for the next packet so
     BufferFull = False                                             ; Buffer cannot be full at this point
     GoTo USART_RECEIVE_EXIT2   
   
   
   
; Timer based interrupts low Level
   
TMR_INTERRUPT:       
    Context Save   
    Reset_Bank
   
    ; Reload our timer
    If TMR1IF = True Then   
        Clear TMR1ON                                                ' STOP THE TIMER
        TIMER1REG = TIMER1REG + TMR1_VAL                            ' LOAD TMR1
        Set TMR1ON                                                  ' START THE TIMER AGAIN   
   
    ; Check our Frame timer and controler
        LowIntTemp = DataBufferPointer                              ; Make a temp copy of this var
        If FrameCounter > 0 Then
            Dec FrameCounter                                       
        Else FrameCounter = 0                                       ; Frame counter is timed out so is there any data?
            If LowIntTemp > MinPacketSize Then                      ; We have to be larger than the min packet size           
                If DataReady = False Then                           ; flag there is data ready if its not been flaged already 
                    DataReady = True
                EndIf
            EndIf
        EndIf
           
    EndIf
   
   
LowIntExit:
     Inc WatchDogTimer
     If WatchDogTimer > 250 Then
         GoTo Restart_Routine
     EndIf
    Clear TMR1IF                                                    ; Clear TMR1 interrupt flag     
    Context Restore
   
   
   

Initialise:

'----- SET UP TIMER 1 INTERRUPT ----------------------------

    IPEN = 1                                                        ' Enable priority interrupts.
   
    TMR1IP = 0                                                      ' Timer1 has low priority
    TIMER1REG = TMR1_VAL
    T1CON = %00000000                                               ' Set up Tmr1 to have 1:1 prescaler and act as a timer
    TMR1IF = 0                                                      ' Clear Tmr1 interrupt flag

    TMR1IE = 0                                                      ' Do not Enable Tmr1 as peripheral interrupt source yet
    TMR1ON = 1
    TXIF = 0
    TMR1IE = 1
   

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

    If BaudRate = 192 Then       
        RCSTA = $90                                                 ' Enable serial port & continuous receive
        TXSTA = $20                                                 ' Enable transmit, BRGH = 0
        SPBRG = 12                                                  ' 19200 Baud @ 16MHz, 0.16%
        FrameTime =                                             
    Else
        RCSTA = $90                                                 ' Enable serial port & continuous receive
        TXSTA = $20                                                 ' Enable transmit, BRGH = 0
        SPBRG = 25                                                  ' 9600 Baud @ 16MHz, 0.16%
    EndIf
   
    ' ENABLE USART RECEIVE INTERRUPTS
    RCIP = 1                                                        ' USART HAS HIGH PRIORITY
    RCIE = 1     
    CREN = 1
    SPEN = 1

; Turn on interrupts                                               
    PEIE = 1
    GIE = 1   
   
    Return   
   


Restart_Routine:
    While 1 = 1
        Nop
    Wend   
OverSubs1:

    Clear


    GoSub Initialise
   
   
    While 1 = 1

        Nop
        If DataReady = True Then
            Nop           
        EndIf
        ClrWdtimr
    Wend


Giuseppe