1 // *********************************************************************
    2 //   Custom DPA Handler code example - Multiple response example       *
    3 // *********************************************************************
    4 // Copyright (c) MICRORISC s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-MultiResponse.c,v $
    7 // Version: $Revision: 1.42 $
    8 // Date:    $Date: 2022/02/25 09:41:25 $
    9 //
   10 // Revision history:
   11 //   2022/02/24  Release for DPA 4.17
   12 //   2020/09/03  Release for DPA 4.15
   13 //   2019/01/10  Release for DPA 4.00
   14 //   2017/03/13  Release for DPA 3.00
   15 //
   16 // *********************************************************************
   17 
   18 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   19 
   20 // !!! This is an !EXPERIMENTAL EXAMPLE! that shows how to receive responses from more nodes to the one request. !!!
   21 // This example works only at STD mode.
   22 
   23 // The example allows to send DPA request to multiple selected nodes (addressed by VRN but NOT by logical address).
   24 // Addressed nodes then send one by one their responses.
   25 // Please see TRequest structure below. Just broadcast this structure at PDATA to the PNUM=0x20 and PCMD=0x00.
   26 // Only addressed, discovered and routing nodes will respond.
   27 
   28 // Default IQRF include (modify the path according to your setup)
   29 #include "IQRF.h"
   30 // Default DPA header (modify the path according to your setup)
   31 #include "DPA.h"
   32 // Default Custom DPA Handler header (modify the path according to your setup)
   33 #include "DPAcustomHandler.h"
   34 
   35 //############################################################################################
   36 
   37 typedef struct
   38 {
   39   // Length of the response slot in 10 ms units. The length must be big enough to fit the longest response from any addressed node.
   40   // Please see MIN_TIMESLOT and MAX_TIMESLOT.
   41   uns8  ResponseSlot;
   42   // Bitmap of the addressed nodes. Each bit represent one node. VrnBitmap[0].1 is VRN=1, VrnBitmap[0].2 is VRN=2, ..., VrnBitmap[29].7 is VRN=239
   43   // Mapping from logical address to VRN is stored at Coordinator external EEPROM table at address 0x5000. See IQRF OS documentation for more information.
   44   uns8  VrnBitmap[( MAX_ADDRESS + 7 ) / 8];
   45   // PNUM of the request
   46   uns8  PNUM;
   47   // PCMD of the request
   48   uns8  PCMD;
   49   // Optional PDATA
   50   uns8  PDATA[0];
   51 } TMultiRequest;
   52 
   53 // Request stored at bufferAUX. We can use bufferAUX because the Batch, that internally uses bufferAUX, is not used.
   54 TMultiRequest MultiRequest @ bufferAUX;
   55 
   56 #if &_DpaMessage.Request.PData[0] != &bufferRF[0] || &_DpaMessage.Response.PData[0] != &bufferRF[0]
   57 #error We assume DPA data @ bufferRF
   58 #endif
   59 
   60 // Indicate response will be sent back
   61 static bit sendResponse;
   62 
   63 void CancelResponse();
   64 
   65 #define   myHWPID 0xaAaF
   66 
   67 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   68 //############################################################################################
   69 bit CustomDpaHandler()
   70 //############################################################################################
   71 {
   72   // Handler presence mark
   73   clrwdt();
   74 
   75   // Saved DPA request/response parameters
   76   static uns8 saveDpaDataLength, savePNUM, savePCMD;
   77   // Indicate multi-request was send
   78   static bit multiResponseRequested;
   79   // Delay to send the response back
   80   static uns24 responseDelay;
   81 
   82   // Detect DPA event to handle (unused event handlers can be commented out or even deleted)
   83   switch ( GetDpaEvent() )
   84   {
   85     // -------------------------------------------------
   86     case DpaEvent_Interrupt:
   87       // Do an extra quick background interrupt work
   88       // ! 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.
   89       // ! 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.
   90       // ! 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.
   91       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   92       // ! Make sure race condition does not occur when accessing those variables at other places.
   93       // ! 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.
   94       // ! Do not call any OS functions except setINDFx().
   95       // ! Do not use any OS variables especially for writing access.
   96       // ! 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.
   97 
   98       //  If TMR6 interrupt occurred
   99       if ( TMR6IF )
  100       {
  101         // Unmask interrupt
  102         TMR6IF = 0;
  103         // Decrement tick count
  104 #ifndef __CC5XFREE__
  105         if ( responseDelay != 0 )
  106           responseDelay--;
  107 #else
  108         if ( ( responseDelay.low8 | responseDelay.mid8 | responseDelay.high8 ) != 0 )
  109         {
  110           W = 1;
  111           responseDelay.low8 -= W;
  112           W = 0;
  113           responseDelay.mid8 = subWFB( responseDelay.mid8 );
  114           responseDelay.high8 = subWFB( responseDelay.high8 );
  115         }
  116 #endif
  117         else
  118           _TMR6ON = FALSE;
  119       }
  120 
  121       return Carry;
  122 
  123       // -------------------------------------------------
  124     case DpaEvent_Idle:
  125       // Do a quick background work when RF packet is not received
  126 
  127       // Ready to send the response?
  128       if ( sendResponse && !_TMR6ON )
  129       {
  130         // Cancel response
  131         CancelResponse();
  132         // Number of hops = my VRN
  133         RTHOPS = ntwVRN;
  134         // Just generate new PID
  135         ++PID;
  136         // No DPA Params used
  137         _DpaParams = 0;
  138         // Send response to the coordinator
  139         _NADR = COORDINATOR_ADDRESS;
  140         // Recall stored parameters
  141         _PNUM = savePNUM;
  142         _PCMD = savePCMD;
  143         // Copy stored response data to the right place
  144         swapBufferINFO();
  145         copyBufferINFO2RF();
  146         // Prepare data length
  147         _DpaDataLength = saveDpaDataLength;
  148         // My HWPID
  149         _HWPID = myHWPID;
  150         // And finally send the response
  151         DpaApiRfTxDpaPacket( lastRSSI, 0xff );
  152       }
  153       break;
  154 
  155       // -------------------------------------------------
  156     case DpaEvent_Init:
  157       // Do a one time initialization before main loop starts
  158 
  159       // Setup TMR6 to generate ticks on the background (ticks every 10ms)
  160       _PR6 = 250 - 1;
  161       // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms
  162 #if defined( TR7xG )
  163       TMR6MD = 0;
  164       T6CON = 0b1.100.1001;
  165       //  Timer2/4/6 Clock Select bits = FOSC/4
  166       T6CLKCON |= 0b0000.0001;
  167 #else
  168       T6CON = 0b0.1001.1.10;
  169 #endif
  170       break;
  171 
  172       // -------------------------------------------------
  173     case DpaEvent_AfterSleep:
  174       // Called on wake-up from sleep
  175 
  176       // "Start" timer
  177       TMR6IE = TRUE;
  178       break;
  179 
  180       // -------------------------------------------------
  181     case DpaEvent_BeforeSleep:
  182       // Called before going to sleep   (the same handling as DpaEvent_DisableInterrupts event)
  183     case DpaEvent_DisableInterrupts:
  184       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM)
  185 
  186       // Must not use TMR6 any more
  187       _TMR6ON = FALSE;
  188       TMR6IE = FALSE;
  189 
  190       // -------------------------------------------------
  191     case DpaEvent_FrcResponseTime:
  192       // Called to get FRC response time
  193     case DpaEvent_FrcValue:
  194       // Called to get FRC value
  195     case DpaEvent_ReceiveDpaRequest:
  196       // Called after DPA request was received
  197 
  198       // Events that will cancel sending response
  199       CancelResponse();
  200       break;
  201 
  202       // -------------------------------------------------
  203     case DpaEvent_AfterRouting:
  204       // Called after Notification and after routing of the DPA response was finished
  205 
  206       // Is there a multi-response?
  207       if ( multiResponseRequested )
  208       {
  209         // Reset the flag
  210         multiResponseRequested = FALSE;
  211         // Delay for the 1st VRN
  212         responseDelay = MIN_STD_TIMESLOT;
  213         // Bitmap bit mask
  214         uns8 mask = 0;
  215         // Bitmap byte pointer
  216 #if &MultiRequest.VrnBitmap[-1] != &bufferAUX[0]
  217         FSR0 = &MultiRequest.VrnBitmap[-1];
  218 #else
  219         setFSR0( _FSR_AUX );
  220 #endif
  221         // Checked VRN
  222         uns8 vrn;
  223         for ( vrn = 0; vrn <= ntwVRN; vrn++ )
  224         {
  225           // Next mask
  226           mask <<= 1;
  227           // Start with new mask and next byte?
  228           if ( mask == 0 )
  229           {
  230             // New mask
  231             mask.0 = 1;
  232             // Next byte
  233             FSR0++;
  234           }
  235 
  236           // Is the VRN addressed?
  237           if ( ( *FSR0 & mask ) != 0 )
  238           {
  239             // Is it me?
  240             if ( vrn == ntwVRN )
  241             {
  242               // I am addressed :-), so I will run the request and store a response for later sending
  243               // Prepare the request
  244               _PNUM = MultiRequest.PNUM;
  245               _PCMD = MultiRequest.PCMD;
  246               setFSR1( _FSR_RF );
  247               copyMemoryBlock( MultiRequest.PDATA, FSR1, _DpaDataLength = saveDpaDataLength - sizeof( MultiRequest ) );
  248               // Run the request. It should take the same time at all nodes.
  249               DpaApiLocalRequest();
  250               // Store the response parameters
  251               savePNUM = _PNUM;
  252               savePCMD = _PCMD;
  253               saveDpaDataLength = _DpaDataLength;
  254               // Store the response data to bufferAUX
  255               copyBufferRF2INFO();
  256               swapBufferINFO();
  257               // Delay to send the response is on
  258               sendResponse = TRUE;
  259               // Synchronize and start timer
  260               _TMR6 = 0;
  261               _TMR6ON = TRUE;
  262               break;
  263             }
  264             else
  265             {
  266               // Delay for the current VRN
  267               uns8 loop = MultiRequest.ResponseSlot;
  268               // Loop is faster and shorter than multiplication
  269               do
  270               {
  271 #ifndef __CC5XFREE__
  272                 responseDelay += vrn;
  273 #else
  274                 W = vrn;
  275                 responseDelay.low8 += W;
  276                 W = 0;
  277                 responseDelay.mid8 = addWFC( responseDelay.mid8 );
  278                 responseDelay.high8 = addWFC( responseDelay.high8 );
  279 #endif
  280               } while ( --loop != 0 );
  281               // Add some extra time for every addressed VRN before me
  282 #ifndef __CC5XFREE__
  283               responseDelay += MIN_STD_TIMESLOT;
  284 #else
  285               W = MIN_STD_TIMESLOT;
  286               responseDelay.low8 += W;
  287               W = 0;
  288               responseDelay.mid8 = addWFC( responseDelay.mid8 );
  289               responseDelay.high8 = addWFC( responseDelay.high8 );
  290 #endif
  291             }
  292           }
  293         }
  294         // Delay is computed, but I was not addressed :-(
  295       }
  296       break;
  297 
  298       // -------------------------------------------------
  299     case DpaEvent_DpaRequest:
  300       // Called to interpret DPA request for peripherals
  301 
  302       // Cancel sending multi response
  303       CancelResponse();
  304 
  305       // -------------------------------------------------
  306       // Peripheral enumeration
  307       if ( IsDpaEnumPeripheralsRequest() )
  308       {
  309         _DpaMessage.EnumPeripheralsAnswer.UserPerNr |=  1;
  310         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 );
  311         _DpaMessage.EnumPeripheralsAnswer.HWPID |= myHWPID;
  312 
  313 DpaHandleReturnTRUE:
  314         return TRUE;
  315       }
  316       // -------------------------------------------------
  317       // Get information about peripheral
  318       else if ( IsDpaPeripheralInfoRequest() )
  319       {
  320         if ( _PNUM == PNUM_USER + 0 )
  321         {
  322           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA;
  323           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  324           goto DpaHandleReturnTRUE;
  325         }
  326         break;
  327       }
  328       // -------------------------------------------------
  329       else
  330       {
  331         // Handle peripheral command
  332         if ( _PNUM == PNUM_USER + 0 )
  333         {
  334           // Check the command value and other stuff
  335           if ( _PCMD == 0 &&
  336                // Data length is enough?
  337                _DpaDataLength >= sizeof( MultiRequest ) &&
  338                // Broadcast?
  339                _NADR == BROADCAST_ADDRESS &&
  340                // Am I routing?
  341                ( DpaApiReadConfigByte( CFGIND_DPA_FLAGS0 ) & 0b1000 ) == 0 &&
  342                // I do not have temporary address (i.e. no VRN)?
  343                ntwADDR != TEMPORARY_ADDRESS &&
  344                // I have a non-zero VRN (I am or I was discovered)
  345                ntwVRN != 0 &&
  346                // I am discovered with the last discovery?
  347                ntwDID == RTDID )
  348             // Then I can response back
  349           {
  350             // Save data length
  351             saveDpaDataLength = _DpaDataLength;
  352             // We will process the request after routing is over
  353             multiResponseRequested = TRUE;
  354             // Store TMultiRequest to the bufferAUX for lated processing
  355             copyBufferRF2INFO();
  356             swapBufferINFO();
  357             // Request was just OK (because of broadcast the response is not sent)
  358             goto DpaHandleReturnTRUE;
  359           }
  360 
  361           // Invalid command for some reason from above
  362           DpaApiReturnPeripheralError( ERROR_FAIL );
  363         }
  364       }
  365 
  366       break;
  367           }
  368 
  369   return FALSE;
  370       }
  371 //############################################################################################
  372 void CancelResponse()
  373 //############################################################################################
  374 {
  375   _TMR6ON = FALSE;
  376   sendResponse = FALSE;
  377 }
  378 //############################################################################################
  379 // 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)
  380 #include "DPAcustomHandler.h"
  381 //############################################################################################