News:

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

Main Menu

PIC to PIC via USART

Started by midali, Mar 08, 2022, 06:18 PM

Previous topic - Next topic

midali

Hi,

I want to sent 5 byte variables from  PIC to PIC with interrupt USART receiver , but I have a trouble because I receive some random values on first byte, the rest of bytes are 0 . Its first time when I configure registries for Usart interrupts, so I'm sure that something is wrong in my code .
The both PICs have Vdd = 3,3V
Any sugestions where I'm wrong or another examples code to receive some bytes via interrupts, are welcome !

Master code :
Declare Hserial_Clear  = On
Declare Hserial_Baud   = 9600
   
Dim byte_1 As Byte =101
Dim byte_2 As Byte =102
Dim byte_3 As Byte =103
Dim byte_4 As Byte =104
Dim byte_5 As Byte = 105
   
Main:
    HSerOut [1, byte_1, 2,byte_2, 3,byte_3, 4,byte_4, 5,byte_5] 
    DelayMS 1000     
GoTo Main



Slave:
Device 18F26K20

Declare  Xtal = 32
set_reg()

Declare Hserial_Clear  = On
Declare Hserial_Baud   = 9600

INTCON.7 = 1    'GIE = 1   - Enable Global Interrupts
INTCON.6 = 1    'PEIE = 1  - Peripheral/Low-Priority Interrupt Enable bit
PIE1.5   = 1    'RC1IE     - EUSART1 Receive Interrupt Enable bit

RCSTA.7 = 1 'SPEN: Serial port Enable bit
RCSTA.2 = 0 'FERR: Framing Error bit clear
RCSTA.1 = 0 'OERR: Overrun Error bit clear
RCSTA.4 = 1  'Continuous Receive Enable bit - Enables receiver

SPBRGH = 0    'EUSARTx Baud Rate Generator Register High Byte
SPBRG = 25    'EUSARTx Baud Rate Generator Register Low Byte

TRISC  = %10000000

Dim byte_[5]     As Byte
Dim header       As Byte
Dim Tempbyte     As Byte
Dim RC1IF        As PIR1.5      'Eusart receive buffer is active

GoTo Main

    On_Interrupt GoTo Isr
Isr:
    Context Save
 
    If RC1IF = 1 Then
        Tempbyte = RCREG              'load received buffer in Tempbyte
       If Tempbyte < 6 Then       '\
           header = Tempbyte      '/ read "header" 's tempbyte
                  Else
               byte_[header] = Tempbyte 'attribute a value to byte_ from header
            EndIf   
    EndIf
    PIE1.5 = 1
    Context Restore

Main:
   Do:
        Print At 1,1, Dec byte_[0] , "    " ,Dec byte_[1]  , "    " ,Dec byte_[2]
        Print At 2,1, Dec byte_[3] , "    " ,Dec byte_[4]  , "    " ,Dec Tempbyte   
   Loop

Thank you !

top204

Place a 100K resistor on the both the TX and RX lines on one of the microcontrollers.

Without the weak pullup resistors, the RX pins are floating and the first start bits cannot be detected.

midali

#2
Thank you Les , I added a pullup 100k resistor, but problem persist .

For test, I added a index variable to verify if exist Tempbyte<6 loop

index is incremented , but extremly quicly ! The master transmit to every 1000ms , so I must receive something to each 1 second.  I think that the trouble is somewhere in registry setting

    If RC1IF = 1 Then
        Tempbyte = RCREG              'load received buffer in Tempbyte
       If Tempbyte < 6 Then       '\
           header = Tempbyte      '/ read "header" 's tempbyte
           Inc index              ' verify if exist Tempbyte<6 loop
                  Else
               byte_[header] = Tempbyte 'attribute a value to byte_ from header
            EndIf   
    EndIf


Yasin

Definitions on the slave side are meaningless. Serial communication declare 9600 baud. But then again, being a non-standard baud setting because you changed the SPBRG register.

Declare Hserial_Baud   = 9600
SPBRGH = 0    'EUSARTx Baud Rate Generator Register High Byte
SPBRG = 25    'EUSARTx Baud Rate Generator Register Low Byte

Pepe

#4
replace your code by this in slave

Main:
    HSerOut [1,byte_1, byte_2,byte_3,byte_4,byte_5]
    DelayMS 1000   
GoTo Main





replace your code by this in master



Dim ind As Byte




    On_Interrupt GoTo Isr
Isr:
    Context Save

    If RC1IF = 1 Then
        Tempbyte = RCREG              'load received buffer in Tempbyte
        If Tempbyte = 1 Or ind > 4 Then
                              ind = 0
                        Else
                          byte_[ind] = Tempbyte 'attribute a value to byte_ from header
                              Inc ind
      End If
    PIE1.5 = 1
    EndIf
   
    Context Restore

Pepe

declare the crystal in 16mhz in slave
for
SPBRGH = 0 'EUSARTx Baud Rate Generator Register High Byte
SPBRG = 25 'EUSARTx Baud Rate Generator Register Low Byte

Pepe

simulation in proteus

midali

Thank you Yasin , you are right ,declares was meaningless .

Thank you Pepe for your help, all are done, the slave receive correct all 5 bytes! Your code is beter than mine !

Pepe

I'm glad it has worked.
It is always good to give without asking for anything in return.

midali

I included in programm servo.inc , I receive data on USART but no servo signal on outputs . If I delete On_Interrupt GoTo Isr ( and ISR ) then I have servo signal on ouputs but no received the serial data, of course , even if the interrupt settings remain the same .

Slave code :
Device 18F26K20

set_reg()
Declare  Xtal = 16
Include "Servos.inc"

INTCON.7 = 1    'GIE = 1   - Enable Global Interrupts
INTCON.6 = 1    'PEIE = 1  - Peripheral/Low-Priority Interrupt Enable bit
PIE1.5   = 1    'RC1IE     - EUSART1 Receive Interrupt Enable bit

RCSTA.7 = 1 'SPEN: Serial port Enable bit
RCSTA.2 = 0 'FERR: Framing Error bit clear
RCSTA.1 = 0 'OERR: Overrun Error bit clear
RCSTA.4 = 1  'Continuous Receive Enable bit - Enables receiver

SPBRGH = 0    'EUSARTx Baud Rate Generator Register High Byte
SPBRG = 25    'EUSARTx Baud Rate Generator Register Low Byte


'------------------------------------------------------------
    $define Servo_NumberOfServos 8              ' Amount of servos attached
    $define Servo_Priority ipHigh               ' Choose a high priority interrupt
                           
   
Dim byte_[5]     As Byte
Dim Tempbyte     As Byte
Dim RC1IF        As PIR1.5      'Eusart receive buffer is active
Dim ind          As Byte     
Dim CountVar     As Word  = 1500
'------------------------------------------------------------

 GoTo Main
 On_Interrupt GoTo Isr
Isr:
    Context Save
   
    If RC1IF = 1 Then
        Tempbyte = RCREG              'load received buffer in Tempbyte
        If Tempbyte = 1 Or ind > 5 Then
                              ind = 0
                        Else
                          byte_[ind] = Tempbyte 'attribute a value to byte_ from header
                              Inc ind
      End If

    EndIf
        PIE1.5 = 1
    Context Restore
   
'------------------------------------------------------------

Main:
Servo_On() 
            Servo1_Position(1500)
            Servo2_Position(1500)
            Servo3_Position(1500)
            Servo4_Position(1500)
            Servo5_Position(1500)
            Servo6_Position(1500)
         
        Print At 1,1, Dec byte_[0] , "   " ,Dec byte_[1]  , "   " ,Dec byte_[2]
        Print At 2,1, Dec byte_[3] , "   " ,Dec byte_[4] 
                   
GoTo Main

servos.inc code:
$ifndef _SERVOS_INC_
$define _SERVOS_INC_
(*
 Controls 1 to 8 hobby servos connected to pins of PortB:
 Servo 0 connects to PortB.0, Servo 1 connects to PortB.1, etc.

 Servos are positioned by sending pulses between 600uS and 2400uS long every 20mS.
 Pulse length of 1500uS = approximate halfway point.

 Set servo positions by loading the desired pulse length (in uS) into the wServo_Position array:-
 wServo_Position[3] = 2400 sets servo 3 to its maximum point.

 Use Servo_On() to enable Interrupt and start sending pulses to servos.
 Use Servo_Off() to stop Interrupt firing and stop sending pulses to servos.

 Flag tServo_InterruptComplete is set to true when the Interrupt code has just finished.
 The main program may poll this flag to know when it is safe to run timing-sensitive code.

 Uses the Timer1 and Compare peripherals to trigger an interrupt at the correct intervals for servo pulses.
 Works with clock speeds of 4, 8, 16, and 32MHz.
*)
'
' Written by Les Johnson for the Positron8 BASIC compiler.
' https://sites.google.com/view/rosetta-tech/home
'

$ifndef ipHigh
    $define ipHigh 1
$endif
$ifndef ipLow
    $define ipLow 0
$endif

$ifndef False
    $define False 0
$endif
$ifndef True
    $define True 1
$endif

$ifndef Servo_NumberOfServos
    $define Servo_NumberOfServos 8              ' Default to 8 servos
$endif

$ifndef Servo_Priority
    $define Servo_Priority ipHigh               ' Default to Servo interrupt priority high
$endif

$if Servo_Priority = ipHigh
    On_Hardware_Interrupt GoTo OnTimer_ISR
$else
    On_Low_Interrupt GoTo OnTimer_ISR
$endif
'===============================================================================
' Variable and Alias Declarations
'-------------------------------------------------------------------------------

$if (Servo_NumberOfServos > 0) And (Servo_NumberOfServos < 9)
    $if Servo_NumberOfServos = 1                    ' \
        $define cOffMask %11111110                  ' |
    $elseif Servo_NumberOfServos = 2                ' |
        $define cOffMask %11111100                  ' |
    $elseif Servo_NumberOfServos = 3                ' | According to how many
        $define cOffMask %11111000                  ' | servos are connected, set
    $elseif Servo_NumberOfServos = 4                ' | the mask which is AND'd
        $define cOffMask %11110000                  ' | with PortB to turn off the
    $elseif Servo_NumberOfServos = 5                ' | servo pins.
        $define cOffMask %11100000                  ' |
    $elseif Servo_NumberOfServos = 6                ' |
        $define cOffMask %11000000                  ' |
    $elseif Servo_NumberOfServos = 7                ' |
        $define cOffMask %10000000                  ' |
    $else                                           ' |
        $define cOffMask %00000000                  ' /
    $endif
$else
     $error "Servo_NumberOfServos $define value must be in the range of 1 to 8"
$endif

$if _xtal = 4                                       ' \
    $define cTimer1Config %00000000                 ' |
$elseif _xtal = 8                                   ' | According to clock speed,
    $define cTimer1Config %00010000                 ' | set Timer1 pre-scaler to
$elseif _xtal = 16                                  ' | give 1 tick every 1 uS.
    $define cTimer1Config %00100000                 ' |
$elseif _xtal = 32                                  ' |
    $define cTimer1Config %00110000                 ' /
$else
    $define cTimer1Config %00000000
    $error "Servo code only works correctly with a clock of 4, 8, 16 or 32MHz"
$endif
'
' Aliases for Timer1 & CCP Module Registers
'
    Dim wServo_Timer1 As TMR1L.Word
    Dim wServo_CompareValue As CCPR1L.Word
    Dim wServo_FSR0 As FSR0L.Word

    Symbol tServo_Timer1On          = T1CONbits_TMR1ON
    Symbol tCompare_InterruptFlag   = PIR1bits_CCP1IF
    Symbol tCompare_InterruptEnable = PIE1bits_CCP1IE
    Symbol tCompare_UsingTimer3     = T3CONbits_T3CCP1
'
' Variables
'
    Dim wFSR0_Save As Word System                       ' Container for FSR0L\H within the interrupt
    Dim bServo_Flags As Byte System
    Dim tServo_InterruptComplete As bServo_Flags.0      ' Flag to indicate that the interrupt has finished
    Dim wServo_DelayTime As Word System                 ' Delay (in uS) between end of last servo
                                                        ' Pulse and start of first (set so servos are refreshed at 50Hz)
    Dim bServo_OnMask As Byte System                    ' OR'd with PortB to turn on current servo pin
    Dim bServo_Index As Byte System                     ' Holds index number of current servo
    Dim wServo_Position[Servo_NumberOfServos] As Word   ' Holds pulse length in uS for servos
'
' Create context saving variable if a low priority interrupt is used
'
$if Servo_Priority <> ipHigh
    Dim Wreg_Save As Byte System
    Dim Status_Save As Byte System
    Dim BSR_Save As Byte System
$endif

'----------------------------------------------------------------------------------
    GoTo _Servo_Main                                    ' Jump over the subroutines to the initialising code
'----------------------------------------------------------------------------------
' Interrupt triggered when Timer1 reaches CCPR1 register value (wServo_CompareValue).
' Input     : None
' Output    : tServo_InterruptComplete is true when compare interrupt has finished (must be reset in the main code)
' Notes:    : Puts servo pin high and then sets CCPR1 to desired pulse length for that servo.
'             Next time through interrupt, next servo pin is set high and CCPR1 is set to new pulse length.
'             Process repeats until all servo pins have been pulsed.
'             Then all pins off and CCPR1 set to time required to wait before starting again to give 50Hz refresh.
'
OnTimer_ISR:
$if Servo_Priority <> ipHigh                                ' Are we using a low priority interrupt?
    Status_Save = STATUS                                    ' \
    Wreg_Save = WREG                                        ' | Yes. So save the STATUS, WREG and BSR registers
    BSR_Save = BSR                                          ' /
$endif
    If tCompare_InterruptFlag = True Then                   ' Is it a CCP1 compare that has triggered the interrupt?
        wFSR0_Save = wServo_FSR0                            ' Yes. So save registers FSR0L\H
        LATB = PORTB & cOffMask                             ' Turn off all servo pins
        If bServo_Index >= Servo_NumberOfServos Then        ' Have all servo pulses been accomplished ?
            wServo_CompareValue = wServo_DelayTime          ' Time until next interrupt = time between cycles
            wServo_DelayTime = 20000                        ' \
            bServo_Index = 0                                ' | Reset all variables for next cycle
            bServo_OnMask = %00000001                       ' /
        Else                                                ' Otherwise. Not all servo pulses have been sent
            LATB = PORTB | bServo_OnMask                    ' Turn on the next servo pin
            wServo_CompareValue = wServo_Position[bServo_Index]         ' Time until next interrupt = pulse length of servo
            wServo_DelayTime = wServo_DelayTime - wServo_CompareValue   ' Update time between cycles to maintain 50Hz refresh
            Inc bServo_Index                                ' Ready for next servo
            bServo_OnMask = bServo_OnMask << 1              ' Move to the next bit of PortB
        EndIf
        tCompare_InterruptFlag = False                      ' Clear the compare interrupt flag
        tServo_InterruptComplete = True                     ' Set flag to let main program know interrupt has finished
        wServo_FSR0 = wFSR0_Save                            ' Restore registers FSR0L\H
    EndIf
'
' *** Other Interrupt routines can be placed here ***
'
$if Servo_Priority <> ipHigh                                ' Are we using a low priority interrupt?
    BSR = BSR_Save                                          ' \
    WREG = Wreg_Save                                        ' | Yes. So restore the STATUS, WREG And BSR registers and exit the interrupt
    STATUS = Status_Save                                    ' |
    Retfie                                                  ' /
$else                                                       ' Otherwise...
    Retfie Fast                                             ' Exit the interrupt, auto restoring the STATUS, WREG and BSR registers
$endif

'----------------------------------------------------------------------------------
' Start sending servo control pulses
' Input     : None
' Output    : None
' Notes     : Resets Timer1 and enables it, allowing it to trigger the Interrupt
'
Proc Servo_On()
    LATB = PORTB & cOffMask                 ' Turn off all servo pins
    wServo_DelayTime = 20000                ' \
    bServo_Index = 0                        ' | Set initial values for variables
    bServo_OnMask = %00000001               ' /
    wServo_CompareValue = wServo_DelayTime  ' Set time in uS before interrupt fires
    wServo_Timer1 = 0                       ' Reset Timer1
    tServo_Timer1On = True                  ' Start Timer1
EndProc

'----------------------------------------------------------------------------------
' Stop sending servo control pulses
' Input     : None
' Output    : None
' Notes     : Turns off Timer1, preventing Interrupt from firing.
'           : Turns off all servo pins
'
$define Servo_Off()         '
    tServo_Timer1On = False '
    LATB = PORTB & cOffMask

'----------------------------------------------------------------------------------
' Adjust the position value for Servo 1
' Input     : wServo_Position[0] holds the position value
' Output    : None
' Notes     : None
'
$define Servo1_Position(pPosition) '
$if (Servo_NumberOfServos >= 1)    '
    wServo_Position[0] = pPosition '
$endif

'----------------------------------------------------------------------------------
' Adjust the position value for Servo 2 (if available)
' Input     : wServo_Position[1] holds the position value
' Output    : None
' Notes     : None
'
$define Servo2_Position(pPosition) '
$if (Servo_NumberOfServos >= 2)    '
    wServo_Position[1] = pPosition '
$endif

'----------------------------------------------------------------------------------
' Adjust the position value for Servo 3 (if available)
' Input     : wServo_Position[2] holds the position value
' Output    : None
' Notes     : None
'
$define Servo3_Position(pPosition) '
$if (Servo_NumberOfServos >= 3)    '
    wServo_Position[2] = pPosition '
$endif

'----------------------------------------------------------------------------------
' Adjust the position value for Servo 4 (if available)
' Input     : wServo_Position[3] holds the position value
' Output    : None
' Notes     : None
'
$define Servo4_Position(pPosition) '
$if (Servo_NumberOfServos >= 4)    '
    wServo_Position[3] = pPosition '
$endif

'----------------------------------------------------------------------------------
' Adjust the position value for Servo 5 (if available)
' Input     : wServo_Position[4] holds the position value
' Output    : None
' Notes     : None
'
$define Servo5_Position(pPosition) '
$if (Servo_NumberOfServos >= 5)    '
    wServo_Position[4] = pPosition '
$endif

'----------------------------------------------------------------------------------
' Adjust the position value for Servo 6 (if available)
' Input     : wServo_Position[5] holds the position value
' Output    : None
' Notes     : None
'
$define Servo6_Position(pPosition) '
$if (Servo_NumberOfServos >= 6)    '
    wServo_Position[5] = pPosition '
$endif

'----------------------------------------------------------------------------------
' Adjust the position value for Servo 7 (if available)
' Input     : wServo_Position[6] holds the position value
' Output    : None
' Notes     : None
'
$define Servo7_Position(pPosition) '
$if (Servo_NumberOfServos >= 7)    '
    wServo_Position[6] = pPosition '
$endif

'----------------------------------------------------------------------------------
' Adjust the position value for Servo 8 (if available)
' Input     : wServo_Position[7] holds the position value
' Output    : None
' Notes     : None
'
$define Servo8_Position(pPosition) '
$if (Servo_NumberOfServos >= 8)    '
    wServo_Position[7] = pPosition '
$endif

'----------------------------------------------------------------------------------
' Code Initialisation
'
_Servo_Main:
    bServo_Index = Servo_NumberOfServos         ' \
    Repeat                                      ' |
        Dec bServo_Index                        ' | Set all servos to mid-point (pulse length = 1500uS)
        wServo_Position[bServo_Index] = 1500    ' |
    Until bServo_Index = 0                      ' /
    TRISB = TRISB & cOffMask                    ' Set used servo pins to outputs
'
' Initialise CCP and Timer1 peripherals:
'
    tCompare_UsingTimer3 = False                ' Compare peripheral uses Timer1
    T1CON = cTimer1Config                       ' Set pre-scaler, Timer1 off
    CCP1CON = %00001011                         ' Set special event trigger
                                                ' (Resets Timer1 and triggers interrupt when Timer1 value reaches CCPR1 value)
    tCompare_InterruptEnable = True             ' Enable interrupt on Timer1 = CCPR1
'
' Enable interrupts
'
$if Servo_Priority = ipHigh                     ' Are we using a high priority interrupt?
    IPR1bits_TMR2IP = 1                         ' Yes. So set compare peripheral to high priority
    RCONbits_IPEN = 0                           ' Disable priority levels on interrupts
$else                                           ' Otherwise...
    IPR1bits_TMR2IP = 0                         ' Set compare peripheral to low priority
    RCONbits_IPEN = 1                           ' Enable priority levels on interrupts
$endif
    INTCONbits_GIEL = 1                         ' Enable peripheral and priority interrupts
    INTCONbits_GIE = 1                          ' Enable global interrupts

$endif

If I move procedure Servo_On()  before to start the usart interrupt , then I receive one time a data serial, then nothing received, nothing on servo outputs .
Dim byte_[5]     As Byte
Dim Tempbyte     As Byte
Dim RC1IF        As PIR1.5      'Eusart receive buffer is active
Dim ind          As Byte     
Dim CountVar     As Word  = 1500
'------------------------------------------------------------
 Servo_On()
 GoTo Main
 On_Interrupt GoTo Isr
Isr:
Looks like somewhere usart interrupt disabled some interrupt or vice versa .

Any suggestion is welcome .

Thank you


top204

Witout going through all the code, I can see that you have 2 interrupt handlers. One in teh Servo include file and one in the main program.

The PIC microcontrollers only have one interrupt handling mechanism, so you need to move the code within on of the interrupt handlers into the other, then have an If-Then to see what peripheral interrupt caused the interrupt to occur.


Pepe

Put $define Servo_Priority ipLow

And Replace On_Interrupt GoTo Isr

With On_Hardware_Interrupt Isr

top204

I never noticed that.

You need to take it in steps, so you know which part is not working. So first make sure the Servo routines are working with the low priority interrupt, then in the main program, make sure your high level interrupt is working correctly, and receive bytes and display them to make sure they are being received etc...

When creating a program, always do it in steps, instead of all in one, which makes it difficult to find a culprit. So start with the servos, then add another interrupt, then receiver etc, and test each part as it is created. That way, when something stops working as it should, you will know that it is something to do with the last block of code that was added.


Pepe

remove in your program

 On_interrupt goto isr:

isr:

 If RC1IF = 1 Then
        Tempbyte = RCREG              'load received buffer in Tempbyte
        If Tempbyte = 1 Or ind > 5 Then
                              ind = 0
                        Else
                          byte_[ind] = Tempbyte 'attribute a value to byte_ from header
                              Inc ind
      End If
context restore



and add this in servo.inc

'
' *** Other Interrupt routines can be placed here ***
'
   
    If RC1IF = 1 Then
        Tempbyte = RCREG              'load received buffer in Tempbyte
        If Tempbyte = 1 Or ind > 5 Then
                              ind = 0
                        Else
                          byte_[ind] = Tempbyte 'attribute a value to byte_ from header
                              Inc ind
      End If

    EndIf

midali

Servo routines working with the low priority interrupt.  On_Hardware_Interrupt Isr  have no effect . USART interrupt stop the servo routine and received is ok . In servos.inc I placed here my usart routine :

' *** Other Interrupt routines can be placed here ***
'-----------------------USART ROUTINE-----------------------------------
    If RC1IF = 1 Then
        Tempbyte = RCREG              'load received buffer in Tempbyte
        If Tempbyte = 1 Or ind > 5 Then
                              ind = 0
                        Else
                          byte_[ind] = Tempbyte 'attribute a value to byte_ from header
                              Inc ind
      End If

    EndIf
'------------------------------------------------------------------------


In main programm I think that I set right the high level interrupt correctly .

Les and Pepe thank you very, very much for your spend time !!

midali

#15
Back to post, now I solved .
I changed in servo routines  from high to low priority interrupt and USART interrupt I placed in servos.ini like posted above.
The problem was where procedure Servo_On() was placed , so I moved it before main loop .

Again thank you Pepe and Les !!

Pepe

#16
I have modified the communication routine
for better performance as seen in the proteus demo

midali

Pepe, can you attach the modified routine code, please ? Another questions : in the Main programm, if I add a pause in loop , the servo output signal is delayed, so its normal or something is wrong ?

Pepe

that is the example with de servo.inc modified

midali

Hi Pepe,

Nice optimized code ,I tested it and doesn't work on table .The values of received bytes are not correct .