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