1 // ********************************************************************************
    2 //   Custom DPA Handler code example - Standard HW UART RX + SW TX                *
    3 // ********************************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-UartHwRxSwTx.c,v $
    7 // Version: $Revision: 1.15 $
    8 // Date:    $Date: 2020/02/20 17:18:58 $
    9 //
   10 // Revision history:
   11 //   2019/12/11  Release for DPA 4.11
   12 //   2018/10/25  Release for DPA 3.03
   13 //   2017/03/14  Release for DPA 3.00
   14 //
   15 // ********************************************************************************
   16 
   17 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   18 
   19 // Default IQRF include (modify the path according to your setup)
   20 #include "IQRF.h"
   21 
   22 // Default DPA header (modify the path according to your setup)
   23 #include "DPA.h"
   24 // Default Custom DPA Handler header (modify the path according to your setup)
   25 #include "DPAcustomHandler.h"
   26 // PIC instructions (header from CC5X)
   27 #include "hexcodes.h"
   28 
   29 // This example shows how to use standard DPA UART RX but SW TX in order to make HW TX pin free for other use (e.g. PWM).
   30 // This example works only at STD mode, not at LP mode.
   31 // !!! Make sure the UART peripheral baud rate set at the Node configuration matches BaudRateValue value from below
   32 
   33 // Fixed baud-rate
   34 #define BaudRateValue   57600L
   35 #define BaudRateDPA     DpaBaud_57600
   36 
   37 // SW TX pin
   38 #define SWTX      LATA.0
   39 #define SWTXtris  TRISA.0
   40 
   41 // Default HW TX pin, that will not be used for UART
   42 #define HWTX      LATC.6
   43 #define HWTXtris  TRISC.6
   44 
   45 // Division macro with rounding
   46 #define DIV(Dividend,Divisor) (((Dividend+((Divisor)>>1))/(Divisor)))
   47 // Instructions by one baud 
   48 #define INSTRperBAUDmul(mul)    ( DIV( ( (F_OSC) * (mul) ), ( 4L * (BaudRateValue) ) ) )
   49 #define INSTRperBAUD            INSTRperBAUDmul(1L)
   50 
   51 // Delay macro
   52 #define SwUartDelayInst(instr) \
   53 #if (instr) != 0 \
   54 #if (instr) < 0 \
   55 #error SwUartDelayInst is negative \
   56 #elif (instr) == 3 \
   57   nop(); \
   58   nop2(); \
   59 #elif (instr) == 4 \
   60   nop2(); \
   61   nop2(); \
   62 #elif (instr) == 5 \
   63   nop(); \
   64   nop2(); \
   65   nop2(); \
   66 #else \
   67 #if (instr) % 3 == 1 \
   68   nop(); \
   69 #elif (instr) % 3 == 2 \
   70   nop2(); \
   71 #endif /* (instr) % 3 == 1 */ \
   72 #if (instr) / 3 != 0 \
   73 #asm \
   74   DW  __MOVLW( (instr) / 3 ) \
   75   DW  __DECFSZ( __WREG, 1 ) \
   76   DW  __BRA( -2 ) \
   77 #endasm \
   78 #endif /* (instr) / 3 != 0 */ \
   79 #endif \
   80 #endif /* (instr) != 0 */ \
   81 
   82 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   83 //############################################################################################
   84 bit CustomDpaHandler()
   85 //############################################################################################
   86 {
   87   // Handler presence mark
   88   clrwdt();
   89 
   90   // Detect DPA event to handle
   91   switch ( GetDpaEvent() )
   92   {
   93     // -------------------------------------------------
   94     case DpaEvent_Interrupt:
   95       // Do an extra quick background interrupt work
   96       // ! 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.
   97       // ! 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.
   98       // ! 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.
   99       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
  100       // ! Make sure race condition does not occur when accessing those variables at other places.
  101       // ! 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.
  102       // ! Do not call any OS functions except setINDFx().
  103       // ! Do not use any OS variables especially for writing access.
  104       // ! 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.
  105       return Carry;
  106 
  107       // -------------------------------------------------
  108     case DpaEvent_Idle:
  109       // Do a quick background work when RF packet is not received
  110 
  111       // ####################################
  112       // Shows free use of normally HW TX pin
  113       HWTXtris = 0;
  114       HWTX = !HWTX;
  115       // ####################################
  116       break;
  117 
  118       // -------------------------------------------------
  119     case DpaEvent_Init:
  120       // Do a one time initialization before main loop starts
  121 
  122       // After UART was opened by DPA, make sure HW TX pin is free to use by other code but not by UART peripheral
  123       TXEN = 0;
  124       HWTXtris = 0;
  125 
  126       // Initialize SW TX pin
  127       SWTXtris = 0;
  128       SWTX = 1;
  129       break;
  130 
  131       // -------------------------------------------------
  132     case DpaEvent_ReceiveDpaRequest:
  133       // Called after DPA request was received
  134 
  135       if ( _PNUM == PNUM_UART )
  136         switch ( _PCMD )
  137         {
  138           // Not implemented
  139           case CMD_UART_CLEAR_WRITE_READ:
  140             DpaApiSetPeripheralError( ERROR_PCMD );
  141             _PCMD |= RESPONSE_FLAG;
  142             return TRUE;
  143 
  144             // Handle TX by SW solution, RX by default HW solution
  145           case CMD_UART_WRITE_READ:
  146           {
  147             // Optimization?
  148 #if &_DpaMessage.PerUartSpiWriteRead_Request.WrittenData[-1] == &bufferRF[0]
  149             setFSR0( _FSR_RF );
  150 #else
  151             FSR0 = &_DpaMessage.PerUartSpiWriteRead_Request.WrittenData[-1];
  152 #endif
  153             // Decrease size by ReadTimeout field but 1 byte
  154             _DpaDataLength -= sizeof( _DpaMessage.PerUartSpiWriteRead_Request.ReadTimeout ) - 1;
  155             // Send every TX byte by SW
  156             while ( --_DpaDataLength != 0 )
  157             {
  158               // Byte to TX (must be at common RAM to avoid MOVLB)
  159               userReg0 = *++FSR0;
  160               // Disable interrupts for precise timing
  161               GIE = FALSE;
  162               // Start bit
  163               SWTX = 0;
  164               // Bank update can be forced off as we know that only one bank (with SWTX pin) is used in the loop
  165 #pragma updateBank 0 /* OFF */
  166               SwUartDelayInst( INSTRperBAUD - 6 );
  167               // Bit loop variable (must be at common RAM to avoid MOVLB)
  168               uns8 loop @ userReg1;
  169               // 8 bits
  170               loop = 8;
  171               do
  172               {
  173                 if ( userReg0.0 )
  174                 {
  175                   userReg0 = rr( userReg0 );
  176                   SWTX = 1;
  177                 }
  178                 else
  179                 {
  180                   SWTX = 0;
  181                   userReg0 = rr( userReg0 );
  182                   nop();
  183                 }
  184 
  185                 SwUartDelayInst( INSTRperBAUD - 9 );
  186               } while ( --loop != 0 );
  187               SwUartDelayInst( 9 - 5 );
  188               // Stop bit
  189               SWTX = 1;
  190               // Relax timing
  191               GIE = TRUE;
  192               SwUartDelayInst( 3 * INSTRperBAUD );
  193 #pragma updateBank 1 /* ON */
  194             }
  195 
  196             // Pass the request to the default DPA but without TX data
  197             _DpaDataLength = sizeof( _DpaMessage.PerUartSpiWriteRead_Request.ReadTimeout );
  198             break;
  199           }
  200         }
  201 
  202       break;
  203 
  204     case DpaEvent_BeforeSendingDpaResponse:
  205       // Called before sending DPA response back to originator of DPA response
  206 
  207       // After UART was opened by DPA, make sure HW TX pin is free to use by other code but not by UART peripheral
  208       if ( _PNUM == PNUM_UART && _PCMD == CMD_UART_OPEN )
  209       {
  210         TXEN = 0;
  211         HWTXtris = 0;
  212       }
  213       break;
  214   }
  215 
  216   return FALSE;
  217 }
  218 
  219 //############################################################################################
  220 // 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) 
  221 #include "DPAcustomHandler.h"
  222 //############################################################################################