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 //############################################################################################