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