1 // **************************************************************************
    2 //   Custom DPA Handler code example - User peripheral supporting UART HW   *
    3 // **************************************************************************
    4 // Copyright (c) MICRORISC s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-UserPeripheral-HW-UART.c,v $
    7 // Version: $Revision: 1.31 $
    8 // Date:    $Date: 2025/09/22 15:45:41 $
    9 //
   10 // Revision history:
   11 //   2023/03/07  Release for DPA 4.30
   12 //   2022/10/05  Release for DPA 4.18
   13 //   2022/02/24  Release for DPA 4.17
   14 //   2017/08/14  Release for DPA 3.01
   15 //
   16 // *********************************************************************
   17 
   18 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   19 
   20 // This example implements the user peripheral supporting HW UART of the PIC
   21 // This example works only at STD mode, not at LP mode
   22 // RX = RC7
   23 // TX = RC6
   24 
   25 // Default IQRF include (modify the path according to your setup)
   26 #include "IQRF.h"
   27 
   28 // Default DPA header (modify the path according to your setup)
   29 #include "DPA.h"
   30 // Default Custom DPA Handler header (modify the path according to your setup)
   31 #include "DPAcustomHandler.h"
   32 
   33 //############################################################################################
   34 
   35 // TX byte to UART
   36 void TxUART( uns8 data );
   37 // RX byte from UART; W = read byte, Carry = byte was read
   38 bit RxUART();
   39 
   40 //############################################################################################
   41 
   42 // UART baud rate
   43 #define UART_BAUD 19200
   44 
   45 // Number of UART received bytes
   46 uns8 RxDataLengthUART;
   47 
   48 // Length of RX and TX buffers, must be power of 2
   49 #define UART_BUFFER_LENGTH     16
   50 
   51 #if 0 != ( UART_BUFFER_LENGTH & ( UART_BUFFER_LENGTH - 1 ) )
   52 #error UART_BUFFER_LENGTH is not power of 2
   53 #endif
   54 
   55 // Rx
   56 // Circular RX UART buffer pointers
   57 uns8 RxBufferPointerStartUART;
   58 uns8 RxBufferPointerEndUART;
   59 uns8 RxBufferUART[UART_BUFFER_LENGTH] /*@ PeripheralRam[0]*/;
   60 
   61 // TX
   62 // Circular TX UART buffer pointers
   63 uns8 TxBufferPointerStartUART;
   64 uns8 TxBufferPointerEndUART;
   65 uns8 TxBufferUART[UART_BUFFER_LENGTH] /*@ PeripheralRam[UART_BUFFER_LENGTH]*/;
   66 
   67 // Division macro with rounding
   68 #define DIV(Dividend,Divisor) (((Dividend+((Divisor)>>1))/(Divisor)))
   69 // PIC baud register computation
   70 #define UART_SPBRG_VALUE( Baud )  ( DIV( F_OSC, ( ( ( uns24 )4  ) * ( Baud ) ) ) - 1 )
   71 
   72 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   73 //############################################################################################
   74 bit CustomDpaHandler()
   75 //############################################################################################
   76 {
   77   // Handler presence mark
   78   clrwdt();
   79 
   80   // TXIE state before sleep
   81   static bit wasTXIE;
   82 
   83   // Detect DPA event to handle
   84   switch ( GetDpaEvent() )
   85   {
   86     // -------------------------------------------------
   87     case DpaEvent_Interrupt:
   88       // Do an extra quick background interrupt work
   89       // ! The time spent handling this event is critical.If there is no interrupt to handle return immediately otherwise keep the code as fast as possible.
   90       // ! Make sure the event is the 1st case in the main switch statement at the handler routine.This ensures that the event is handled as the 1st one.
   91       // ! It is desirable that this event is handled with immediate return even if it is not used by the custom handler because the Interrupt event is raised on every MCU interrupt and the “empty” return handler ensures the shortest possible interrupt routine response time.
   92       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   93       // ! Make sure race condition does not occur when accessing those variables at other places.
   94       // ! Make sure( inspect.lst file generated by C compiler ) compiler does not create any hidden temporary local variable( occurs when using division, multiplication or bit shifts ) at the event handler code.The name of such variable is usually Cnumbercnt.
   95       // ! Do not call any OS functions except setINDFx().
   96       // ! Do not use any OS variables especially for writing access.
   97       // ! All above rules apply also to any other function being called from the event handler code, although calling any function from Interrupt event is not recommended because of additional MCU stack usage.
   98 
   99       // -----------------------------------------------------------------------------------------
  100       // UART Receive
  101       //
  102       if ( RCIF )
  103       {
  104         // We ignore FERR
  105         FSR1L = _RCREG;
  106         // Put the received byte in circular buffer
  107         if ( RxDataLengthUART < UART_BUFFER_LENGTH )
  108         {
  109           // One more byte received
  110           RxDataLengthUART++;
  111           // Prepare pointer
  112           FSR0 = RxBufferUART + RxBufferPointerEndUART;
  113           // Recalculate tail pointer
  114           // Optimization: Same as (UART_BUFFER_LENGTH is power of 2) : RxBufferPointerEndUART = ( RxBufferPointerEndUART + 1 ) % UART_BUFFER_LENGTH;
  115           RxBufferPointerEndUART++;
  116           RxBufferPointerEndUART &= ~UART_BUFFER_LENGTH;
  117           // Store byte
  118           setINDF0( FSR1L );
  119         }
  120       }
  121 
  122       // Overrun recovery (we do it after receiving UART byte in order to receive it as soon as possible)
  123       if ( OERR )
  124         CREN = 0;
  125 
  126       // Seems excess, but at the end it is shorted and faster than having this statement at else branch
  127       CREN = 1;
  128 
  129       // -----------------------------------------------------------------------------------------
  130       // UART Transmit
  131       //
  132       if ( TXIF && TXIE )
  133       {
  134         // Send byte from circular buffer to the UART
  135         FSR0 = TxBufferUART + TxBufferPointerStartUART;
  136         TxBufferPointerStartUART = ( TxBufferPointerStartUART + 1 ) % UART_BUFFER_LENGTH;
  137         // Buffer empty?
  138         if ( TxBufferPointerStartUART == TxBufferPointerEndUART )
  139           TXIE = FALSE;
  140         // TX the byte
  141         _TXREG = *FSR0;
  142       }
  143 
  144       // Return value does not matter
  145       return Carry;
  146 
  147 
  148       // -------------------------------------------------
  149     case DpaEvent_DpaRequest:
  150       // Called to interpret DPA request for peripherals
  151       // -------------------------------------------------
  152       // Peripheral enumeration
  153       if ( IsDpaEnumPeripheralsRequest() )
  154       {
  155         // We implement 1 user peripheral
  156         _DpaMessage.EnumPeripheralsAnswer.UserPerNr |= 1;
  157         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 );
  158         _DpaMessage.EnumPeripheralsAnswer.HWPID |= 0x000F;
  159         _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= 0x0000;
  160 
  161 DpaHandleReturnTRUE:
  162         return TRUE;
  163       }
  164       // -------------------------------------------------
  165       // Get information about peripheral
  166       else if ( IsDpaPeripheralInfoRequest() )
  167       {
  168         if ( _PNUM == PNUM_USER + 0 )
  169         {
  170           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA;
  171           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  172           goto DpaHandleReturnTRUE;
  173         }
  174 
  175         break;
  176       }
  177       // -------------------------------------------------
  178       else
  179       {
  180         // Handle peripheral command
  181         if ( _PNUM == PNUM_USER + 0 )
  182         {
  183           // Check command
  184           switch ( _PCMD )
  185           {
  186             // ------------------------------
  187             // Write to UART
  188             case 0:
  189               // There must be some data to write
  190               if ( _DpaDataLength == 0 )
  191               {
  192 _ERROR_DATA_LEN:
  193                 DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  194               }
  195 
  196               // Pointer to data (setFSR1( _FSR_RF ) can be used too to save code)
  197               FSR1 = &_DpaMessage.Request.PData[0];
  198               do
  199               {
  200                 // Send one byte
  201                 TxUART( *FSR1++ );
  202                 // Loop all bytes
  203               } while ( --_DpaDataLength != 0 );
  204 
  205               // _DpaDataLength = 0  => no data returned
  206               goto DpaHandleReturnTRUE;
  207 
  208               // ------------------------------
  209               // Read from UART
  210             case 1:
  211               // No data must be sent
  212               if ( _DpaDataLength != 0 )
  213                 goto _ERROR_DATA_LEN;
  214 
  215               // Pointer to the output buffer  (setFSR1( _FSR_RF ) can be used too to save code)
  216               FSR1 = &_DpaMessage.Response.PData[0];
  217               // Initial count of received bytes
  218               _DpaDataLength = 0;
  219               // Loop while there is enough space and some byte to read
  220               while ( _DpaDataLength < sizeof( _DpaMessage.Response.PData ) && RxUART() )
  221               {
  222                 // Store read byte
  223                 setINDF1( W );
  224                 // Move pointer
  225                 FSR1++;
  226                 // Count next byte
  227                 _DpaDataLength++;
  228               }
  229 
  230               // Return number of read bytes
  231               goto DpaHandleReturnTRUE;
  232           }
  233         }
  234       }
  235       break;
  236 
  237       // -------------------------------------------------
  238     case DpaEvent_Init:
  239       // Do a one time initialization before main loop starts
  240 
  241       // Connected TR pins (e.g. TR72D)?
  242       moduleInfo();
  243       if ( !bufferINFO[5].7 )
  244       {
  245         // Set them as inputs
  246         TRISC.5 = 1;
  247         TRISA.5 = 1;
  248         TRISB.4 = 1;
  249       }
  250 
  251       // RX input
  252       TRISC.7 = 1;
  253       // TX output
  254       TRISC.6 = 0;
  255 
  256 #if !defined( TR7xD )
  257       UART1MD = 0;
  258       unlockPPS();
  259       RC6PPS = 0x10;  // TX (Note: RX does not have to be set, use default)
  260       lockPPS();
  261 #endif
  262 
  263       // Set baud rate
  264       _SPBRGL = UART_SPBRG_VALUE( UART_BAUD ) & 0xff;
  265       _SPBRGH = UART_SPBRG_VALUE( UART_BAUD ) >> 8;
  266       // baud rate control setup: BRG16 = 1
  267 #if !defined( TR7xD )
  268       setBAUD1CON( 0b0000.1.000 );
  269 #else
  270       BAUDCON = 0b0000.1.000;
  271 #endif
  272 
  273       // CSRC TX9 TXEN SYNC SENDB BRGH TRMT TX9D
  274       // TXEN = 1
  275       // BRGH = 1
  276       // async UART, high speed, 8 bit, TX enabled
  277 #if !defined( TR7xD )
  278       setTX1STA( 0b0010.0100 );
  279 #else
  280       TXSTA = 0b0010.0100;
  281 #endif
  282 
  283       // SPEN RX9 SREN CREN ADDEN FERR OERR RX9D
  284       // SPEN = 1
  285       // CREN = 1
  286       // Continuous receive, enable port, 8 bit
  287       _RCSTA = 0b1001.0000;
  288 
  289       // Enable UART RX interrupt
  290       RCIE = TRUE;
  291 
  292       break;
  293 
  294       // -------------------------------------------------
  295     case DpaEvent_AfterSleep:
  296       // Called after woken up after sleep
  297 
  298       TXIE = wasTXIE;
  299       RCIE = TRUE;
  300       break;
  301 
  302       // -------------------------------------------------
  303     case DpaEvent_BeforeSleep:
  304       // Called before going to sleep
  305 
  306       // -------------------------------------------------
  307     case DpaEvent_DisableInterrupts:
  308       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM)
  309 
  310       wasTXIE = TXIE;
  311       TXIE = FALSE;
  312       RCIE = FALSE;
  313       break;
  314   }
  315 
  316   return FALSE;
  317 }
  318 
  319 //############################################################################################
  320 // Note: make sure the parameter does not overlap another variable as the function is ready to be called from (timer) interrupt too
  321 static uns8 _data;
  322 void TxUART( uns8 data @ _data )
  323 //############################################################################################
  324 {
  325   // Wait for a space in the buffer
  326   while ( TXIE && TxBufferPointerStartUART == TxBufferPointerEndUART );
  327 
  328   // Disable TX interrupt
  329   TXIE = FALSE;
  330   // Compute pointer
  331   FSR0 = TxBufferUART + TxBufferPointerEndUART;
  332   // Optimization: TxBufferPointerEndUART = ( TxBufferPointerEndUART + 1 ) % UART_BUFFER_LENGTH;
  333   TxBufferPointerEndUART++;
  334   TxBufferPointerEndUART &= ~UART_BUFFER_LENGTH;
  335   // Store byte
  336   setINDF0( data );
  337   // Start transmitting
  338   TXIE = TRUE;
  339 }
  340 
  341 //############################################################################################
  342 // W = read byte, Carry = result
  343 bit RxUART()
  344 //############################################################################################
  345 {
  346   // Buffer empty?
  347   if ( RxDataLengthUART == 0 )
  348     return FALSE;
  349 
  350   // Disable RX interrupt
  351   RCIE = FALSE;
  352   // Get byte from the circular buffer
  353   FSR0 = RxBufferUART + RxBufferPointerStartUART;
  354   // Optimization: RxBufferPointerStartUART = ( RxBufferPointerStartUART + 1 ) % UART_BUFFER_LENGTH;
  355   RxBufferPointerStartUART++;
  356   RxBufferPointerStartUART &= ~UART_BUFFER_LENGTH;
  357   // One byte less
  358   RxDataLengthUART--;
  359   // Returned byte
  360   W = *FSR0;
  361   // Enable RX interrupt
  362   RCIE = TRUE;
  363   // TRUE => byte was read
  364   return TRUE;
  365 }
  366 
  367 //############################################################################################
  368 // Default Custom DPA Handler header; 2nd include implementing a Code bumper to detect too long code of the Custom DPA Handler (modify the path according to your setup)
  369 #include "DPAcustomHandler.h"
  370 //############################################################################################