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