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