Positron16 - Interrupt Driven Serial Receive buffer on a dsPIC33CKxxMP202 device

Started by top204, Jan 26, 2026, 07:38 PM

Previous topic - Next topic

top204

I was in need of a reliable async serial receive mechanism for the LD06 LIDAR unit, when converting the 8-bit device's code I created a few weeks ago, to run on a dsPIC33xxCK device, operating at 200 MHz. However, I found out that I had not actually created a serial receive buffer library for the dsPIC33 devices, so set about looking in the datasheets for USART and interrupts on them.

Things looked pretty much standard, so I created some procedures to setup USART1, and subroutines to replace the compiler's HRSin1 and HSerin1 commands library routines. I wrote a simple demo program to receive data and echo it back, to make sure things were working as they should, but came up against the most odd behaviour on the dsPIC33CK device. It would not receive at all... In fact, after some tests with an LED, I discovered that it was not triggering the receive interrupt, even though everything was setup as it stated in the datasheet!

It worked if I did not rely on the interrupt, or changed the device to a PIC24 type (with a few code changes, but not too much change), and it received and transmitted OK, but not when using an interrupt to receive and store the data on a dsPIC33xxCK device!

Very strange behaviour, but not, exactly, unexpected with microchip these days. :-)

So I set about altering bits in SFRs and altering the SFR sequences, and eventually came up with a sequence that allowed the interrupt to trigger when USART1 receives a byte, but I cannot find any reason or errata for the dsPIC33CK devices.

The working SFR manipulation sequence is:

    USART1_RX_IntDisable()                                          ' Disable the USART1 interrupt
    USART1_tOverrun = 0                                             ' Reset the Overrun flag
    USART1_ClearBuffer()                                            ' Clear the serial buffer and the data in and out pointers
    IPC2bits_U1RXIP0 = 1                                            ' \
    IPC2bits_U1RXIP1 = 1                                            ' | USART1 interrupt priority
    IPC2bits_U1RXIP2 = 1                                            ' /
    USART1_RX_FlagClear()                                           ' Clear the interrupt request flag
    WREG2.0 = U1MODELbits_BRGH                                      ' Save the state of the BRGH bit that the compiler initially set with the 'Declare HRsin1_Baud'
    U1MODEL = $00                                                   ' \ Clear USART1 settings for now
    U1MODEH = $00                                                   ' /
    U1MODELbits_BRGH = WREG2.0                                      ' Restore the state of the BRGH bit that the compiler initially set with the 'Declare HRsin1_Baud'
    USART1_RX_IntEnable()                                           ' Enable the USART1 interrupt
    USART1_Enable()                                                 ' Enable USART1
    USART1_TX_Enable()                                              ' Enable USART1 TX
    USART1_RX_Enable()                                              ' Enable USART1 RX

The above SFR sequence made the receive interrupt fire correctly, every time.

So below is the code listing for the demo program, operating on an Amicus16B board, with a dsPIC33CK128MP202 plugged into it, and operating at 200 MHz with its internal oscillator.

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Demonstrate the buffered USART1 receive library that replaces the routines used by the HRsin1 and HSerial1 commands.
' The demo will receive bytes from a serial terminal and echo them back.
' The code listing has the USART1 pins setup for an Amicus16B development board.
'
' Written by Les Johnson for the Positron16 BASIC Compiler.
' https://sites.google.com/view/rosetta-tech/positron-compilers-experimenters-notebook
'
    Device = 33CK128MP202                                                       ' Tell the compiler what device to compile for
    Declare Xtal = 200                                                          ' Tell the compiler what frequency the device is operating at (in MHz)
'
' Set the USART1 pins for an Amicus16B board
'
    Declare Hserial1_Baud = 115200                                              ' Set the Baud rate of USART1 to 115200
    Declare HRSOut1_Pin   = PORTB.0                                             ' Set pin PORTB.0 for TX (PPS pin RP32)
    Declare HRSIn1_Pin    = PORTB.1                                             ' Set pin PORTB.1 for RX (PPS Pin RP33)

$define cUSART1_RXBufferSize 128                                                ' The size of the USART1 receive buffer (in bytes)
    Include "RX1_Buffer_CK.inc"                                                 ' Load the buffered HRSin1 routines into the program
'
' Create global variables here
'
    Dim ByteIn As Byte                                                          ' Holds the byte read from USART1

'-----------------------------------------------------------------------------------------
' The main program starts here
' Receive bytes from a serial terminal and echo them back.
' Uses a large delay in the loop, so it relies on the serial buffer.
'
Main:
    Setup()                                                                     ' Setup the program and any peripherals

    HRSOut1Ln "Type some characters in the serial terminal as fast as possible"
    Do                                                                          ' Create a loop
        Do                                                                      ' Create a receiving loop
            ByteIn = HRSIn1, {5000, TimeOutLabel}                               ' Receive a serial byte with timeout
            DelayMS 100                                                         ' Create a large delay between reception and transmission to test the buffer
            HRSOut1 ByteIn                                                      ' Transmit what was received
        Loop                                                                    ' Close the loop
'
' Come here if a timeout occurs
'
TimeOutLabel:
        HRSOut1Ln "\r** Timed Out **\rLooking again for characters"
    Loop                                                                        ' Look for more serial bytes

'-----------------------------------------------------------------------------------------
' Setup the program and any peripherals
' Input    : None
' Output    : None
' Notes    : None
'
Proc Setup()
    Osc_200MHz()                                                                ' Initialise the internal oscillator to run the device at 200 MHz
    Clear                                                                       ' Clear all user RAM
    PPS_Unlock()                                                                ' Unlock the PPS
    PPS_Output(cOut_Pin_RP32, cOut_Fn_U1TX)                                     ' Make pin RB0 USART1 TX for an Amicus16B
    PPS_Input(cIn_Pin_RP33, cIn_Fn_U1RX)                                        ' Make pin RB1 USART1 RX for an Amicus16B
    USART1_Init()                                                               ' Initialise USART1 and its receive interrupt
EndProc

'-----------------------------------------------------------------------------------------
' Initialise the internal oscillator to run the device at 200MHz
' Input    : None
' Output    : None
' Notes    : For a dsPIC33CKxxMP202 device
'
Proc Osc_200MHz()
    CLKDIV    = %0011000000000001                                               ' RCDIV is FRC/1. PREPLL is 1:1
    PLLFBD    = %0000000000110010
    OSCTUN    = %0000000000000000
    PLLDIV    = %0000000000010001                                               ' POSTPLL is 1:1. FVCO/4. POST2DIV is 1:1
    ACLKCON1  = %0000000100000001                                               ' PLL disabled. FRC Oscillator. PREPLL is 1:1
    APLLFBD1  = %0000000010010110
    APLLDIV1  = %0000000001000001                                               ' APSTSCLR is 1:4. APOST2DIV is 1:1. AVCODIV is FVCO/4
    REFOCONL  = %0000000000000000
    REFOCONH  = %0000000000000000
    REFOTRIMH = %0000000000000000
    RPCON     = %0000000000000000
    Write_OSCCON(%0000000100000001)                                             ' Enable PLL
    DelayMS 100                                                                 ' Wait for the clock to stabilise
EndProc

'-----------------------------------------------------------------------------------------
' Setup the config fuses for an internal oscillator on a dsPIC33CKxxMP202 device.
' The OSC pins are general purpose I/O.
'
    Config FSEC = BWRP_OFF,_                                                    ' Boot Segment may be written
                  BSS_DISABLED,_                                                ' Boot Segment No Protection (other than BWRP)
                  BSEN_OFF,_                                                    ' No Boot Segment
                  GWRP_OFF,_                                                    ' General Segment may be written
                  GSS_DISABLED,_                                                ' General Segment No Protection (other than GWRP)
                  CWRP_OFF,_                                                    ' Configuration Segment may be written
                  CSS_DISABLED,_                                                ' Configuration Segment No Protection (other than CWRP)
                  AIVTDIS_OFF                                                   ' Alternate Interrupt Vector Table Disabled

    Config FOSCSEL = FNOSC_FRC,_                                                ' Oscillator Source is Internal Fast RC (FRC)
                     IESO_OFF                                                   ' Start up with user-selected oscillator source

    Config FOSC = POSCMD_NONE,_                                                 ' Primary Oscillator disabled
                  OSCIOFNC_ON,_                                                 ' OSC2 is general purpose digital I/O pin
                  FCKSM_CSECMD,_                                                ' Clock switching is enabled. Fail-safe Clock Monitor is disabled
                  PLLKEN_ON,_                                                   ' PLL clock output will be disabled if Lock is lost
                  XTCFG_G3,_                                                    ' XT Config is 24 to 32 MHz crystals
                  XTBST_ENABLE                                                  ' XT Boost the kick-start


    Config FWDT = RWDTPS_PS2147483648,_                                         ' Run Mode Watchdog Timer Post Scaler is 1:2147483648
                  RCLKSEL_LPRC,_                                                ' Watchdog Timer Clock is use LPRC
                  WINDIS_ON,_                                                   ' Watchdog Timer in Non-Window mode
                  WDTWIN_WIN25,_                                                ' Watchdog Timer Window is 25% of WDT period
                  SWDTPS_PS2147483648,_                                         ' Sleep Mode Watchdog Timer Post Scaler is 1:2147483648
                  FWDTEN_ON_SW                                                  ' Watchdog Timer controlled via software. Use WDTCON.ON bit

    Config FPOR = BISTDIS_DISABLED                                              ' Memory BIST on reset disabled

    Config FICD = ICS_PGD1,_                                                    ' ICD Communicate on PGC1 and PGD1
                  JTAGEN_OFF                                                    ' JTAG is disabled

    Config FDMT = DMTDIS_OFF                                                    ' Dead Man Timer is disabled and can be enabled by software

    Config FDEVOPT = ALTI2C1_OFF,_                                              ' I2C1 mapped to SDA1/SCL1 pins
                     ALTI2C2_OFF,_                                              ' I2C2 mapped to SDA2/SCL2 pins
                     SMBEN_STANDARD,_                                           ' I2C input buffer operation
                     SPI2PIN_PPS                                                ' SPI2 uses I/O remap (PPS) pins

    Config FALTREG = CTXT1_OFF,_                                                ' (IPL) Associated to Alternate Working Register Not Assigned
                     CTXT2_OFF,_                                                ' (IPL) Associated to Alternate Working Register Not Assigned
                     CTXT3_OFF,_                                                ' (IPL) Associated to Alternate Working Register Not Assigned
                     CTXT4_OFF                                                  ' (IPL) Associated to Alternate Working Register Not Assigned

The above code listing calls an include file named: "RX1_Buffer_CK.inc", and its code listing is below:

$ifndef USART1_BufferCK_inc_
$define USART1_BufferCK_inc_
'
'  /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\    \/\\\                                                /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\    /\\\\\\\\\\    /\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\    \/\\\        \/\\\        /\\\\\\\\\\
'      \/\\\    \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\    \/\\\ /\\  /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\  \//\\\\\\\\/\\
'        \///        \///    \/////    \//////////    \//////////      \/////        \/////    \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Buffered USART1 receiver that replaces the compiler's Hrsin1 and HSerin1 commands library routines, both with and without timeout.
' For use with a dsPIC33xxCK device.
'
' Written by Les Johnson for the Positron16 BASIC Compiler.
' https://sites.google.com/view/rosetta-tech/positron-compilers-experimenters-notebook
'
' The compiler will produce the following hooks for use with #ifSym, when a command type or declare has been used in the code listing:
' __SYSCOM_HRSIN1_REQ_        for the HRSIn command without timeout
' __SYSCOM_HRSIN1_TO_REQ_      for the HRSIn command with timeout
' __SYSCOM_HSERIAL1_CLEAR_REQ_ if the Declare Hserial1_Clear was used in the code listing
'
    #Disable HRSIn1, HRsin1To                                      ' Disable the compiler's Hrsin1 and HSerin1 (with and without timeout) library routines

$ifndef False
    $define False 0
$endif
$ifndef True
    $define True 1
$endif
'
' Default size of the USART1 receive buffer
'
$ifndef cUSART1_RXBufferSize                                        ' Has the buffer size been given in the main code listing?
    $define cUSART1_RXBufferSize 64                                 ' No. So set it to a default size of 64 bytes
    $SendWarning "cUSART1_RXBufferSize has not been used in the main code listing, so defaulting to a buffer size of 64 bytes"
$endif
'
' Create some variables for use with the interrupt buffer
'
    Dim USART1_bBuffer[cUSART1_RXBufferSize] As Byte                ' The USART1 receive buffer
    Dim USART1_wIndexIn      As Word                                ' Data in pointer
    Dim USART1_wIndexOut     As Word                                ' Data out pointer
    Dim USART1_tOverrun      As Bit                                 ' Buffer Overrun flag
    Dim USART1_tByteInBuffer As Bit                                 ' Set true if a byte has been received (must be reset in the main program)

$define cHrsin1_TimeoutPadding $eval ((50 * _xtal) / 2)             ' HRSin1 and HSerin1 timeout delay padding

'-----------------------------------------------------------------------------------------------------------------------------
' USART1 Meta-Macros for a dsPIC33CK128MP202 device
'
$define USART1_RX_Reg          U1RXREG                              ' The SFR used to hold the byte received
$define USART1_OverRun_Flag    U1STAbits_OERR                       ' The USART1 over-run flag
$define USART1_RX_Flag         IFS0bits_U1RXIF                      ' The USART1 byte received flag
$define USART1_RX_IntBit       IEC0bits_U1RXIE                      ' The USART1 RX interrupt bit
$define USART1_RX_FlagClear()  USART1_RX_Flag = 0                   ' Reset the USART1 RX flag
$define USART1_RX_IntEnable()  USART1_RX_IntBit = 1                 ' Enable the USART1 Interrupt
$define USART1_RX_IntDisable() USART1_RX_IntBit = 0                 ' Disable the USART1 interrupt
$define USART1_Enable()        U1MODEbits_UARTEN = 1                ' Enable USART1
$define USART1_Disable()       U1MODEbits_UARTEN = 0                ' Disable USART1
$define USART1_TX_Enable()     U1MODEbits_UTXEN = 1                 ' Enable USART1 TX
$define USART1_TX_Disable()    U1MODEbits_UTXEN = 0                 ' Disable USART1 TX
$define USART1_RX_Enable()     U1MODEbits_URXEN = 1                 ' Enable USART1 RX
$define USART1_RX_Disable()    U1MODEbits_URXEN = 0                 ' Disable USART1 RX

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

'-----------------------------------------------------------------------------------------------------------------------------
' Test if there is any data in the serial buffer
' Input    : None
' Output    : Returns true if data is available
' Notes    : None
'
Proc USART1_DataAvailable(), Bit
    Result = False
    If USART1_wIndexIn <> USART1_wIndexOut Then                    ' Is there any data in the buffer?
        Result = True                                              ' Yes. So return true
    EndIf
EndProc

'-----------------------------------------------------------------------------------------------------------------------------
' Clear the serial buffer
' Input    : None
' Output    : None
' Notes    : None
'
Proc USART1_ClearBuffer()
    Clear USART1_bBuffer                                            ' Clear the buffer array
    USART1_wIndexIn = 0                                             ' \
    USART1_wIndexOut = 0                                            ' / Reset the data in and out pointers
    USART1_tByteInBuffer = False                                    ' Reset the USART1_tByteInBuffer flag
EndProc

'-----------------------------------------------------------------------------------------------------------------------------
' Reset the buffer variables
' Input    : None
' Output    : None
' Notes    : None
'
Proc USART1_BufferReset()
    USART1_RX_IntDisable()                                          ' Disable the USART1 interrupt
    USART1_tOverrun = 0                                             ' Reset the Overrun flag
    Clear USART1_bBuffer                                            ' Clear the buffer array
    USART1_wIndexIn = 0                                             ' \
    USART1_wIndexOut = 0                                            ' / Reset the data in and out pointers
    USART1_tByteInBuffer = False                                    ' Reset the USART1_tByteInBuffer flag
    USART1_RX_FlagClear()                                           ' Clear the USART1 receive interrupt request flag
    USART1_RX_IntEnable()                                           ' Enable the USART1 interrupt
EndProc

'-----------------------------------------------------------------------------------------------------------------------------
' Initialise the buffer and the USART1 interrupt handler
' Input    : None
' Output    : Fills the serial buffer with data
'          : USART1_tOverrun is true if a buffer over run occurs. Must be reset in the main program
' Notes    : The Interrupt routine is also held in the 'USART1_Init' Procedure, so is not included, if not applied in the main code listing
'          : The initialisation sequence of USART1 is important with dsPICxx33CK devices, otherwise, no interrupt is generated.
'
Proc USART1_Init()
    USART1_RX_IntDisable()                                          ' Disable the USART1 interrupt
    USART1_tOverrun = 0                                             ' Reset the Overrun flag
    USART1_ClearBuffer()                                            ' Clear the serial buffer and the data in and out pointers
    IPC2bits_U1RXIP0 = 1                                            ' \
    IPC2bits_U1RXIP1 = 1                                            ' | USART1 interrupt priority
    IPC2bits_U1RXIP2 = 1                                            ' /
    USART1_RX_FlagClear()                                           ' Clear the interrupt request flag
    WREG2.0 = U1MODELbits_BRGH                                      ' Save the state of the BRGH bit that the compiler initially set with the 'Declare HRsin1_Baud'
    U1MODEL = $00                                                   ' \ Clear USART1 settings for now
    U1MODEH = $00                                                   ' /
    U1MODELbits_BRGH = WREG2.0                                      ' Restore the state of the BRGH bit that the compiler initially set with the 'Declare HRsin1_Baud'
    USART1_RX_IntEnable()                                           ' Enable the USART1 interrupt
    USART1_Enable()                                                 ' Enable USART1
    USART1_TX_Enable()                                              ' Enable USART1 TX
    USART1_RX_Enable()                                              ' Enable USART1 RX
'
' USART1 interrupt handler code to buffer incoming data
'
Isr U1RXInterrupt, UnHandled                                        ' An unhandled interrupt, because not a lot happens in it to alter SFRs and compiler system variables
    Push STATUS                                                     ' Push the STATUS SFR onto the stack
    Push All_WREG                                                   ' Push all the WREG SFRs onto the stack
#ifSym __SYSCOM_HSERIAL1_CLEAR_REQ_                                 ' Has the 'Declare HSerial1_Clear' been used in the program?
    If USART1_OverRun_Flag = 1 Then                                 ' Yes. So is there an overrun error?
        USART1_OverRun_Flag = 0                                     ' Yes. So clear it
        GoTo Exit_Interrupt                                         ' And exit the interrupt
    EndIf
#endIfSym
    USART1_bBuffer[USART1_wIndexIn] = U1RXREG                       ' Read a data byte from USART1
    USART1_tByteInBuffer =  True                                    ' Signal that their is a byte in the buffer
    Inc USART1_wIndexIn                                             ' Move up the buffer
    If USART1_wIndexIn >= cUSART1_RXBufferSize Then                 ' Has the buffer been filled?
        USART1_wIndexIn = 0                                         ' Yes. So reset the in pointer
    EndIf
'
' Check for a buffer overrun. Flag USART1_tOverrun must be cleared in the main program
'
    If USART1_wIndexIn = USART1_wIndexOut Then USART1_tOverrun = 1
Exit_Interrupt:
    USART1_RX_FlagClear()                                           ' Clear the USART1 receive interrupt request flag
    Pop All_WREG                                                    ' Pop all the WREG SFRs from the stack
    Pop STATUS                                                      ' Pop the STATUS SFR from the stack
EndIsr                                                              ' Exit the interrupt handler
EndProc

'-----------------------------------------------------------------------------------------------------------------------------
#ifSym __SYSCOM_HRSIN1_REQ_, __SYSCOM_HRSIN1_TO_REQ_
    GoTo _BufferedHrsin1_Main                                       ' Jump over the HRsin1 subroutines
#endIfSym

'-----------------------------------------------------------------------------------------------------------------------------
' HRSIn1 replacement code with timeout
' Input    : PRTA1 = Timeout value
' Output    : WREG0.Byte0
'          : Carry flag clear if timed out
' Notes    : Follows the method used by the compiler's library routine
'          : Preserves WREG1, WREG2 and WREG3
'
#ifSym __SYSCOM_HRSIN1_TO_REQ_
__hrsin1__to:
#ifSym __HRSIN1_PORT                                                ' Has the 'Declare HRSIn1_Pin' been issued in the main code listing?
    PinInput __HRSIN1_PORT.__HRSIN1_PIN                             ' Yes. So make the HRSin1 pin an input (just in case it is altered elsewhere)
#endIfSym
    Push.w WREG1                                                    ' Save WREG1
    Push.d WREG2                                                    ' Save WREG2 and WREG3
    WREG2 = PRTA1                                                   ' Move the timeout value (passed from the command) to WREG2
    WREG3 = 0                                                       ' Clear the inner loop counter
    Do                                                              ' Create a loop
        DelayCS 1                                                   ' Padding for 1 clock cycle
        If USART1_wIndexIn <> USART1_wIndexOut Then                 ' Is there any data in the buffer?
            Pop.d WREG2                                             ' Yes. So restore WREG2 and WREG3
            Pop.w WREG1                                             ' Restore WREG1
            GoTo __hrsin1__getbyte                                  ' Get the byte from USART1, and exit the routine from there
        EndIf
        WREG0 = 0                                                   ' Clear WREG0
        Dec WREG3                                                   ' Decrement the inner loop counter. So that it will trigger a Carry change
        If SRbits_C = 1 Then Continue                               ' Continue if gone below zero?
        Dec WREG2                                                   ' Decrement the outer loop counter. So that it will trigger a Carry change
        If SRbits_C = 0 Then                                        ' Is the carry flag clear?
            Pop.d WREG2                                             ' Yes. So restore WREG2 and WREG3
            Pop.w WREG1                                             ' Restore WREG1
            Return                                                  ' Exit the routine
        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:
#ifSym __HRSIN1_PORT                                                ' Has the 'Declare HRSIn1_Pin' been issued in the main code listing?
    PinInput __HRSIN1_PORT.__HRSIN1_PIN                             ' Yes. So make the HRSin1 pin an input (just in case it is altered elsewhere)
#endIfSym
    Clrwdt
    Push.w WREG1                                                    ' Save WREG1
    While USART1_wIndexIn = USART1_wIndexOut: Wend                  ' Wait for data in the buffer
    Pop.w WREG1                                                     ' Restore WREG1
'
' Fall through to __hrsin1__getbyte
'
#endIfSym
'
' Get a single byte from the buffer and place it into WREG0.Byte0
' Input    : USART1_bBuffer holds the serial data
' Output    : WREG0.Byte0
' Notes    : Preserves WREG1
'
#ifSym __SYSCOM_HRSIN1_REQ_, __SYSCOM_HRSIN1_TO_REQ_
__hrsin1__getbyte:
    Push.w WREG1                                                    ' Save WREG1
    WREG2.Byte0 = USART1_bBuffer[USART1_wIndexOut]                  ' Place a buffer byte into WREG2.Byte0
    Inc USART1_wIndexOut                                            ' Move up the buffer
    If USART1_wIndexOut >= cUSART1_RXBufferSize Then                ' Is the out index over running the buffer size?
        USART1_wIndexOut = 0                                        ' Yes. So reset it
    EndIf
    Pop.w WREG1                                                     ' Restore WREG1
    WREG0.Byte0 = WREG2.Byte0                                       ' Transfer the byte from the buffer to WREG0
    SRbits_C = 1                                                    ' Set the carry flag to indicate byte received
    Return                                                          ' Exit the routine
#endIfSym

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

$endif  ' USART1_BufferCK_inc_

The demo program will receive text at 115200 Baud, and echo it back, or time out. It has a large delay in the receiving loop, so that it shows the interrupt buffer working, and receiving data when the main program has come to a halt.

The dsPIC33xxCK devices are really nice for their price, and operate extremely fast.

Attached below, are the firmware source codes for the demo program and the buffered RX library. It is named: "HRSin1_Buffered_Demo_CK.zip"

Regards
Les

flosigud

I look forward to to try this out with my Positron16. I will have to wait a bit as the PICs I ordered left China this morning. Two dsPIC33CK256MP502.

top204

Many thanks for your kind words.

In my 64x64 P3 RGB LED panel interface code, I was getting some very strange operations from the dsPIC33CK128MP502 device, and I just could not work out why! I went through everything I could, and even went through the config fuse values, because I know the dsPIC33xxCK devices once had a problem with fuses, and would not allow the higher clock rate if a certain, unused, bit was set in one of them (worked out by one of our excellent users, and taken care of by the compiler)

It turned out to be a config fuse issue again, but I do not know if it is the device or the PICkit4 programmer I use. Both are microchip, so one of them is screwed, or even both. :-) But only on the dsPIC33CK128MP50x devices, and none of the other MP range?

The fuses I used were:

'-----------------------------------------------------------------------------------------
' Setup the config fuses for an internal oscillator on a dsPIC33CK128MP502 device.
' The OSC pins are general purpose I/O.

    Config FSEC = BWRP_OFF,_                                                    ' Boot Segment may be written
                  BSS_DISABLED,_                                                ' Boot Segment No Protection (other than BWRP)
                  BSEN_OFF,_                                                    ' No Boot Segment
                  GWRP_OFF,_                                                    ' General Segment may be written
                  GSS_DISABLED,_                                                ' General Segment No Protection (other than GWRP)
                  CWRP_OFF,_                                                    ' Configuration Segment may be written
                  CSS_DISABLED,_                                                ' Configuration Segment No Protection (other than CWRP)
                  AIVTDIS_OFF                                                   ' Alternate Interrupt Vector Table Disabled

    Config FOSCSEL = FNOSC_FRC,_                                                ' Oscillator Source is Internal Fast RC (FRC)
                     IESO_OFF                                                   ' Start up with user-selected oscillator source

    Config FOSC = POSCMD_NONE,_                                                 ' Primary Oscillator disabled
                  OSCIOFNC_ON,_                                                 ' OSC2 is general purpose digital I/O pin
                  FCKSM_CSECMD,_                                                ' Clock switching is enabled. Fail-safe Clock Monitor is disabled
                  PLLKEN_ON,_                                                   ' PLL clock output will be disabled if Lock is lost
                  XTCFG_G3,_                                                    ' XT Config is 24 to 32 MHz crystals
                  XTBST_ENABLE                                                  ' XT Boost the kick-start


    Config FWDT = RWDTPS_PS2147483648,_                                         ' Run Mode Watchdog Timer Post Scaler is 1:2147483648
                  RCLKSEL_LPRC,_                                                ' Watchdog Timer Clock is use LPRC
                  WINDIS_ON,_                                                   ' Watchdog Timer in Non-Window mode
                  WDTWIN_WIN25,_                                                ' Watchdog Timer Window is 25% of WDT period
                  SWDTPS_PS2147483648,_                                         ' Sleep Mode Watchdog Timer Post Scaler is 1:S2147483648
                  FWDTEN_ON_SW                                                  ' Watchdog Timer controlled via software. Use WDTCON.ON bit

    Config FPOR = BISTDIS_DISABLED                                              ' Memory BIST on reset disabled

    Config FICD = ICS_PGD1,_                                                    ' ICD Communicate on PGC1 and PGD1
                  JTAGEN_OFF                                                    ' JTAG is disabled

    Config FDMT = DMTDIS_OFF                                                    ' Dead Man Timer is Disabled and can be enabled by software
'
' *** Note. The FDEVOPT settings stop the device from operating, if set and programmed into it
'
    'Config FDEVOPT = ALTI2C1_OFF,_                                              ' I2C1 mapped to SDA1/SCL1 pins
    '                 ALTI2C2_OFF,_                                              ' I2C2 mapped to SDA2/SCL2 pins
    '                 ALTI2C3_OFF,_                                              ' I2C3 mapped to SDA3/SCL3 pins
    '                 SMBEN_STANDARD,_                                           ' Standard I2C input threshold operation
    '                 SPI2PIN_PPS                                                ' SPI2 uses I/O remap (PPS) pins

    Config FALTREG = CTXT1_OFF,_                                                ' (IPL) Associated to Alternate Working Register Not Assigned
                     CTXT2_OFF,_                                                ' (IPL) Associated to Alternate Working Register Not Assigned
                     CTXT3_OFF,_                                                ' (IPL) Associated to Alternate Working Register Not Assigned
                     CTXT4_OFF                                                  ' (IPL) Associated to Alternate Working Register Not Assigned

And the setup procedure was:

'-----------------------------------------------------------------------------------------
' Initialise the internal oscillator to run the device at 200MHz
' Input     : None
' Output    : None
' Notes     : For a dsPIC33CK128MP502 device
'
Proc Osc_200MHz()
    CLKDIV    = %0011000000000001                                               ' RCDIV is FRC/1. PREPLL is 1:1
    PLLFBD    = %0000000000110010
    OSCTUN    = %0000000000000000
    PLLDIV    = %0000000000010001                                               ' POSTPLL is 1:1. FVCO/4. POST2DIV is 1:1
    ACLKCON1  = %0000000100000001                                               ' PLL disabled. FRC Oscillator. PREPLL is 1:1
    APLLFBD1  = %0000000010010110
    APLLDIV1  = %0000000001000001                                               ' APSTSCLR is 1:4. APOST2DIV is 1:1. AVCODIV is FVCO/4
    REFOCONL  = %0000000000000000
    REFOCONH  = %0000000000000000
    REFOTRIMH = %0000000000000000
    RPCON     = %0000000000000000
    Write_OSCCON(%0000000100000001)                                             ' Enable PLL
    DelayMS 100                                                                 ' Wait for clock switching to stabilise
EndProc

Thankfully, there is a bit more of a standard with the 16-bit devices. So the same setups work on most of the dsPIC33xxCK devices.

Regards
Les