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