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