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