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