News:

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

Main Menu

dsPIC33xxCK Serial USART1 Buffer

Started by top204, Nov 14, 2021, 05:01 PM

Previous topic - Next topic

top204

Below is a program to operate as a serial USART1 buffer on a dsPIC33xxCK device.

The code below is an include file, that should be named "USART1_Buffer33CK.inc":

$ifndef _USART1_Buffer33CK_inc_
$define _USART1_Buffer33CK_inc_
'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Buffered USART receiver that replaces the compiler's Hrsin1 command library routines, both with and without timeout
'
' Written for the Positron16 compiler by Les Johnson
'
' The compiler will produce the following hooks for use with #ifSym:
' __SYSCOM_HRSIN1_REQ_      for the HRSIn command without timeout
' __SYSCOM_HRSIN1_TO_REQ_   for the HRSIn command with timeout
'
    #Disable HRSIn1, HRsin1To                               ' Disable the compiler's Hrsin1 (with and without timeout) library routines
'
' Default size of the RX buffer
'
$ifndef cUSART_RXBufferSize
    $define cUSART_RXBufferSize 64
    $SendWarning "$define cUSART_RXBufferSize missing, so using an RX buffer size of 64 bytes"
$endif
'
' Create some variables for use with the interrupt RX buffer
'
    Dim _USART1_bRxBuffer[cUSART_RXBufferSize] As Byte          ' The RX buffer array
    Dim _USART1_wRxIndexIn  As Word                             ' RX Data in pointer
    Dim _USART1_wRxIndexOut As Word                             ' RX Data out pointer
    Dim USART1_tRxOverrun   As Bit                              ' RX Buffer Overrun flag

$define cHrsin1_TimeoutPadding $eval (((80 * _xtal) / 2) - 1)  ' Hrsin1 timeout delay padding

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

'-------------------------------------------------------------------------------------------
$define USART1_Isr_RxEnable()  IEC0bits_U1RXIE = 1  ' Enable the USART1 RX Interrupt
$define USART1_Isr_RxDisable() IEC0bits_U1RXIE = 0  ' Disable the USART1 RX interrupt

'-------------------------------------------------------------------------------------------
' IPC2 Bits
'
$ifndef IPC2bits_U1RXIP0
    $define IPC2bits_U1RXIP0 IPC2.12
    $define IPC2bits_U1RXIP1 IPC2.13
    $define IPC2bits_U1RXIP2 IPC2.14
$endif

'-------------------------------------------------------------------------------------------
' Reset the buffer variables
' Input     : None
' Output    : None
' Notes     : None
'
Proc USART1_BufferReset()
    USART1_Isr_RxDisable()                          ' Disable the USART1 RX interrupt
    USART1_tRxOverrun = False                       ' Reset the Overrun flag
    Clear _USART1_bRxBuffer                         ' Clear the RX buffer array
    _USART1_wRxIndexIn = 0                          ' \
    _USART1_wRxIndexOut = 0                         ' / Reset the RX data in and out pointers
    IFS0bits_U1RXIF = 0                             ' Clear the interrupt request flag
    USART1_Isr_RxEnable()                           ' Enable the USART1 RX interrupt
EndProc

'-------------------------------------------------------------------------------------------
' Initialise the buffer and the USART1 RX interrupt handler
' Input     : None
' Output    : None
' Notes     : The procedure also contains the interrupt handler
'
Proc USART1_Init()
    USART1_Isr_RxDisable()                          ' Disable the USART1 RX interrupt
    USART1_tRxOverrun = False                       ' Reset the RX Overrun flag
    Clear _USART1_bRxBuffer                         ' Clear the RX buffer array
    _USART1_wRxIndexIn = 0                          ' \
    _USART1_wRxIndexOut = 0                         ' / Reset the RX data in and out pointers
    IPC2bits_U1RXIP0 = 1                            ' \
    IPC2bits_U1RXIP1 = 1                            ' | USART1 RX interrupt priority
    IPC2bits_U1RXIP2 = 1                            ' /
    IFS0bits_U1RXIF = 0                             ' Clear the RX interrupt request flag
    USART1_Isr_RxEnable()                           ' Enable the USART1 RX interrupt
    ExitProc
'
' USART1 RX interrupt handler to buffer incoming data
'
Isr U1RXInterrupt                                   ' USART1 RX interrupt
    #ifSym __SYSCOM_HSERIAL1_CLEAR_REQ_             ' Has the Hserial_Clear declare been used in the program?
        If U1STAbits_OERR = 1 Then                  ' Yes. So is there an overrun error?
            U1STAbits_OERR = 0                      ' Yes. So clear it
            GoTo Exit_Interrupt                     ' And exit the interrupt
        EndIf
    #endIfSym
    _USART1_bRxBuffer[_USART1_wRxIndexIn] = U1RXREG ' Read a data byte from USART1
    Inc _USART1_wRxIndexIn                          ' Move up the buffer
    If _USART1_wRxIndexIn > cUSART_RXBufferSize Then    ' Has the buffer been filled?
        _USART1_wRxIndexIn = 0                      ' Yes. So reset the in pointer
    EndIf
'
' Check for a buffer over-run. Flag USART1_tRxOverrun must be cleared in the main program
'
    If _USART1_wRxIndexIn = _USART1_wRxIndexOut Then USART1_tRxOverrun = True
Exit_Interrupt:
    IFS0bits_U1RXIF = 0                             ' Clear the interrupt request flag
EndIsr                                              ' Exit the interrupt handler
EndProc

'----------------------------------------------------------------------------
' Jump over the replacement library subroutines (if they are enabled)
'
#ifSym __SYSCOM_HRSIN1_REQ_, __SYSCOM_HRSIN1_TO_REQ_
    GoTo _BufferedHrsin1_Main
#endIfSym

'----------------------------------------------------------------------------
' Hrsin1 replacement code with timeout
' Input     : PRTA1 = timeout value
' Output    : WREG0.Byte0
'           : Carry flag clear if timed out
' Notes     : Follows exactly the method used by the compiler's library routine
'
#ifSym __SYSCOM_HRSIN1_TO_REQ_
__hrsin1__to:
    PinInput __HRSIN1_Port.__HRSIN1_Pin         ' Make the Hrsin1 pin an input (just in case it is altered elsewhere)
    Push.d WREG2                                ' Save WREG2 and WREG3 on the stack
    WREG2 = PRTA1                               ' Move the timeout value (passed from the command) to WREG2
    WREG3 = 0                                   ' Clear the timeout loop counter
    Do                                          ' Create an infinite loop
        DelayCS 1                               ' Padding for 1 cycle
        If _USART1_wRxIndexIn <> _USART1_wRxIndexOut Then ' Is there any data in the buffer?
            Pop.d WREG2                         ' Yes. So restore WREG2 and WREG3
            GoTo __hrsin1__getbyte              ' Get the byte from USART1, and exit the routine from there
        EndIf
        WREG0 = 0
        Dec WREG3                               ' Decrement the inner loop counter
        If SRbits_C = 1 Then Continue           ' Continue if gone below zero
        Dec WREG2                               ' Decrement the outer loop counter
        If SRbits_C = 0 Then                    ' Is the carry flag clear?
            Pop.d WREG2                         ' Yes. So restore WREG2 and WREG3 from the stack
            Return                              ' Exit the subroutine
        EndIf
        WREG3 = cHrsin1_TimeoutPadding          ' Reload the timout padding into WREG3
    Loop                                        ' Look again
#endIfSym
'
'----------------------------------------------------------------------------
' Hrsin1 replacement code without timeout
' Input     : None
' Output    : WREG0.Byte0
' Notes     : None
'
#ifSym __SYSCOM_HRSIN1_REQ_
__hrsin1:
    PinInput __HRSIN1_Port.__HRSIN1_Pin                 ' Make the Hrsin1 pin an input (just in case it is altered elsewhere)
    While _USART1_wRxIndexIn = _USART1_wRxIndexOut      ' \ Wait for data in the buffer
    Wend                                                ' /
'
' Fall through to __hrsin1__getbyte
'
#endIfSym
'----------------------------------------------------------------------------
' Get a single byte from the buffer and place it into WREG0.Byte0
'
#ifSym __SYSCOM_HRSIN1_REQ_, __SYSCOM_HRSIN1_TO_REQ_
__hrsin1__getbyte:
    WREG2.Byte0 = _USART1_bRxBuffer[_USART1_wRxIndexOut]    ' Place a buffer byte into WREG2.Byte0
    Inc _USART1_wRxIndexOut                                 ' Move up the buffer
    If _USART1_wRxIndexOut > cUSART_RXBufferSize Then       ' Is the out index over-running the buffer size?
        _USART1_wRxIndexOut = 0                             ' Yes. So reset it
    EndIf
    WREG0.Byte0 = WREG2.Byte0                               ' Transfer the byte from the buffer to WREG0
    Set SRbits_C                                            ' Set the carry flag to indicate byte received
    Return                                                  ' Exit the subroutine
#endIfSym

'----------------------------------------------------------------------------
_BufferedHrsin1_Main:

$endif  ' _USART1_Buffer33CK_inc_

And below is a demo program using the above include file library:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Demonstrate the buffered USART library that replaces the routines used by the Hrsin command
' The demo will receive bytes from a serial terminal and echo them back
'
' Written for the Positron16 compiler by Les Johnson
'
    Device = 33CK128MP202
    Declare Xtal = 160
'
' USART1 setup
'
    Declare Hserial_Baud = 9600
    Declare HRSOut1_Pin = PORTB.0
    Declare HRSIn1_Pin  = PORTB.1

$define cUSART_RXBufferSize 128
   
    Include "USART1_Buffer33CK.inc"                   ' Load the buffered Hrsin routines into the program
'
' PPS output defines
'
$define PPS_OutPin_RB0  cOut_Pin_RP32
$define PPS_OutPin_RB1  cOut_Pin_RP33
$define PPS_OutPin_RB2  cOut_Pin_RP34
$define PPS_OutPin_RB3  cOut_Pin_RP35
$define PPS_OutPin_RB4  cOut_Pin_RP36
$define PPS_OutPin_RB5  cOut_Pin_RP37
$define PPS_OutPin_RB6  cOut_Pin_RP38
$define PPS_OutPin_RB7  cOut_Pin_RP39
$define PPS_OutPin_RB8  cOut_Pin_RP40
$define PPS_OutPin_RB9  cOut_Pin_RP41
$define PPS_OutPin_RB10 cOut_Pin_RP42
$define PPS_OutPin_RB11 cOut_Pin_RP43
$define PPS_OutPin_RB12 cOut_Pin_RP44
$define PPS_OutPin_RB13 cOut_Pin_RP45
$define PPS_OutPin_RB14 cOut_Pin_RP46
$define PPS_OutPin_RB15 cOut_Pin_RP47
'
' PPS input defines
'
$define PPS_InPin_RB0  cIn_Pin_RP32
$define PPS_InPin_RB1  cIn_Pin_RP33
$define PPS_InPin_RB2  cIn_Pin_RP34
$define PPS_InPin_RB3  cIn_Pin_RP35
$define PPS_InPin_RB4  cIn_Pin_RP36
$define PPS_InPin_RB5  cIn_Pin_RP37
$define PPS_InPin_RB6  cIn_Pin_RP38                                                                                                                                             
$define PPS_InPin_RB7  cIn_Pin_RP39
$define PPS_InPin_RB8  cIn_Pin_RP40
$define PPS_InPin_RB9  cIn_Pin_RP41
$define PPS_InPin_RB10 cIn_Pin_RP42
$define PPS_InPin_RB11 cIn_Pin_RP43
$define PPS_InPin_RB12 cIn_Pin_RP44
$define PPS_InPin_RB13 cIn_Pin_RP45
$define PPS_InPin_RB14 cIn_Pin_RP46
$define PPS_InPin_RB15 cIn_Pin_RP47
'
' Create a variable for the demo
'   
    Dim MyByte As Byte

'----------------------------------------------------------------------------
Main: 
    IntOsc_160MHz()                                     ' Set the device to operate at 160MHz with its internal oscillator
   
    USART1_Init()                                       ' Initialise the USART1 buffer interrupt
   
    PPS_Unlock()                                        ' Unlock the PPS peripheral
    PPS_Output(PPS_OutPin_RB0, cOut_Fn_U1TX)            ' Set the appropriate PPS pin for the USART1 peripheral TX
    PPS_Input(PPS_InPin_RB1, cIn_Fn_U1RX)               ' Set the appropriate PPS pin for the USART1 peripheral RX
   
    HRSOutLn "Start"
       
    Do                                                  ' Create a loop
        Do                                              ' Create a loop
            MyByte = HRSIn1, {5000, TimeOutLabel}       ' Receive a serial byte with timeout
            DelayMS 100                                 ' Create a large delay between reception and transmission to test the buffer
            HRSOut1 MyByte                              ' Transmit what was received
        Loop                                            ' Close the loop
'
' Come here if a timeout occurs
TimeOutLabel: 
        HRSOutLn "\r** Timed Out **"
    Loop                                            ' Look for more serial bytes

'----------------------------------------------------------------------------------
' Set the internal oscillator to 160MHz
' Input     : None
' Output    : None
' Notes     : None
'
Proc IntOsc_160MHz()
    CLKDIV = %0011000000000001
    PLLFBD = 160                   
    OSCTUN = $00                   
    PLLDIV = %0000000001000001
    ACLKCON1 = $0101               
    APLLFBD1 = 150
    APLLDIV1 = $41
    Write_OSCCON($0101)
'
' Wait for clock switch to finish
'
    While OSCCONbits_OSWEN <> 0 : Wend
    While OSCCONbits_LOCK <> 1 : Wend
EndProc

'----------------------------------------------------------------------------------     
' Set the fuses for internal oscillator on a dsPIC33CK128MP202 Device
'   
    Config FSEC     = BWRP_OFF, BSS_DISABLED, BSEN_OFF, GWRP_OFF, GSS_DISABLED, CWRP_OFF, CSS_DISABLED, AIVTDIS_OFF   
    Config FOSCSEL  = FNOSC_FRC, IESO_OFF   
    Config FOSC     = POSCMD_NONE, OSCIOFNC_ON, FCKSM_CSECMD, PLLKEN_OFF, XTCFG_G0, XTBST_ENABLE   
    Config FWDT     = SWDTPS_PS2147483648, RCLKSEL_LPRC   
    Config FPOR     = BISTDIS_DISABLED   
    Config FICD     = ICS_PGD1, JTAGEN_OFF, NOBTSWP_DISABLED
    Config FDMT     = DMTDIS_OFF
    Config FDEVOPT  = ALTI2C1_ON, ALTI2C2_ON, ALTI2C3_ON, SMBEN_STANDARD, SPI2PIN_PPS
    Config FALTREG  = CTXT1_OFF, CTXT2_OFF, CTXT3_OFF, CTXT4_OFF