1 // **************************************************************************** 2 // Custom DPA Handler code example - Standard Lights - Template * 3 // **************************************************************************** 4 // Copyright (c) IQRF Tech s.r.o. 5 // 6 // File: $RCSfile: 1002_Light-Template.c,v $ 7 // Version: $Revision: 1.18 $ 8 // Date: $Date: 2019/04/03 09:27:58 $ 9 // 10 // Revision history: 11 // 2018/10/25 Release for DPA 3.03 12 // 2017/11/16 Release for DPA 3.02 13 // 2017/08/14 Release for DPA 3.01 14 // 15 // ********************************************************************* 16 17 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/ 18 // IQRF Standards documentation https://www.iqrfalliance.org/techDocs/ 19 20 // This example implements 2 Lights according to the IQRF Lights standard 21 // Index 0: Red LED using PIC ADC, 31 ON levels 22 // Index 1: Green LED, only on/off, i.e. 2 levels (0 or 100 %) 23 24 // Default IQRF include (modify the path according to your setup) 25 #include "IQRF.h" 26 27 // Default DPA header (modify the path according to your setup) 28 #include "DPA.h" 29 // Default Custom DPA Handler header (modify the path according to your setup) 30 #include "DPAcustomHandler.h" 31 // IQRF standards header (modify the path according to your setup) 32 #include "IQRFstandard.h" 33 #include "IQRF_HWPID.h" 34 35 #if DPA_VERSION_MASTER < 0x0301 36 #error DPA version 3.01++ is required 37 #endif 38 39 //############################################################################################ 40 41 // Number of implemented lights 42 #define LIGHTS_COUNT 2 43 44 // Light power levels array: every light has 2 bytes, 1st byte is actual power, 2nd byte is requested power 45 uns16 Powers[LIGHTS_COUNT]; 46 47 // Sets power of the light, returns previous requested power and returns previous actual power at the global PrevActualPower variable 48 uns8 SetLight( uns8 index, uns8 reqPower ); 49 static uns8 PrevActualPower; 50 // Sets FSR0 to point to the 2 bytes of the light powers 51 void FSR0PowerPointer( uns8 index ); 52 53 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 54 //############################################################################################ 55 bit CustomDpaHandler() 56 //############################################################################################ 57 { 58 // This forces CC5X to wisely use MOVLB instructions (doc says: The 'default' bank is used by the compiler for loops and labels when the algorithm gives up finding the optimal choice) 59 #pragma updateBank default = UserBank_01 60 61 // Timers for lights. The space must be long enough to fit them all. 2 bytes per one light. 62 static uns16 Timers[LIGHTS_COUNT]; 63 64 // Handler presence mark 65 clrwdt(); 66 67 // Detect DPA event to handle 68 switch ( GetDpaEvent() ) 69 { 70 // ------------------------------------------------- 71 case DpaEvent_Interrupt: 72 // Do an extra quick background interrupt work 73 // ! 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. 74 // ! 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. 75 // ! 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. 76 // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy. 77 // ! Make sure race condition does not occur when accessing those variables at other places. 78 // ! 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. 79 // ! Do not call any OS functions except setINDFx(). 80 // ! Do not use any OS variables especially for writing access. 81 // ! 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. 82 83 // If TMR6 interrupt occurred, every 10 ms 84 if ( TMR6IF ) 85 { 86 // Unmask interrupt 87 TMR6IF = 0; 88 89 // Count 250 ms from 10 ms micro ticks 90 static uns8 count250ms; 91 if ( ++count250ms == ( 250 / 10 ) ) 92 { 93 // 250 ms 94 count250ms = 0; 95 96 // Pointer to the timers array 97 FSR1 = (uns16)&Timers[0]; 98 // Light index 99 static uns8 index; 100 index = 0; 101 do 102 { 103 // Is timer running (is non-zero)? 104 if ( ( FSR1[1] | INDF1 ) != 0 ) 105 { 106 // Get time 107 static uns16 time; 108 time.low8 = *FSR1++; 109 time.high8 = *FSR1; 110 // Is timer over? 111 if ( --time == 0 ) 112 // Turn the light OFF 113 SetLight( index, 0 ); 114 115 // Store new time 116 setINDF1( time.high8 ); 117 FSR1--; 118 setINDF1( time.low8 ); 119 } 120 // Next timer 121 FSR1 += sizeof( Timers[0] ); 122 // Next index 123 } while ( ++index < LIGHTS_COUNT ); 124 } 125 } 126 return Carry; 127 128 // ------------------------------------------------- 129 case DpaEvent_DpaRequest: 130 // Called to interpret DPA request for peripherals 131 // ------------------------------------------------- 132 // Peripheral enumeration 133 IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest() 134 { 135 // We implement 1 standard peripheral 136 _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1; 137 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_LIGHT ); 138 _DpaMessage.EnumPeripheralsAnswer.HWPID = HWPID_IQRF_TECH__DEMO_LIGHT; 139 _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x0001; 140 141 DpaHandleReturnTRUE: 142 return TRUE; 143 } 144 // ------------------------------------------------- 145 // Get information about peripheral 146 else 147 { 148 if ( _PNUM == PNUM_STD_LIGHT ) 149 { 150 _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_STD_LIGHT; 151 _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE; 152 // Set standard version 153 _DpaMessage.PeripheralInfoAnswer.Par1 = 5; 154 goto DpaHandleReturnTRUE; 155 } 156 157 break; 158 } 159 // ------------------------------------------------- 160 { 161 // Handle peripheral command 162 163 // Supported peripheral number? 164 if ( _PNUM == PNUM_STD_LIGHT ) 165 { 166 // Supported commands? 167 switch ( _PCMD ) 168 { 169 // Invalid command 170 default: 171 // Return error 172 DpaApiReturnPeripheralError( ERROR_PCMD ); 173 174 // Light enumeration 175 case PCMD_STD_ENUMERATE: 176 if ( _DpaDataLength != 0 ) 177 goto _ERROR_DATA_LEN; 178 179 // Return just count of lights 180 _DpaDataLength |= 1; // Optimization = 1 (_DpaDataLength was zero for sure) 181 _DpaMessage.Request.PData[0] = LIGHTS_COUNT; 182 goto DpaHandleReturnTRUE; 183 184 // Supported commands. 185 case PCMD_STD_LIGHT_SET: 186 case PCMD_STD_LIGHT_INC: 187 case PCMD_STD_LIGHT_DEC: 188 { 189 // Invalid bitmap (data) length 190 if ( _DpaDataLength < 4 ) 191 { 192 // Return error 193 _ERROR_DATA_LEN: 194 DpaApiReturnPeripheralError( ERROR_DATA_LEN ); 195 } 196 197 // Store bitmap of lights 198 uns8 lightsBitmap[4]; 199 // Copy bitmap 200 lightsBitmap[0] = *FSR0++; 201 lightsBitmap[1] = *FSR0++; 202 lightsBitmap[2] = *FSR0++; 203 lightsBitmap[3] = *FSR0++; 204 205 // Remaining data counter + 1 206 _DpaDataLength -= 3; 207 // Light index 208 uns8 index = 0; 209 // Loop all optionally selected lights 210 do 211 { 212 // Light is selected? 213 if ( lightsBitmap[0].0 ) 214 { 215 // Is there enough data for the light? 216 if ( --_DpaDataLength == 0 ) 217 goto _ERROR_DATA_LEN; 218 219 // Get required power value 220 uns8 reqPower = *FSR0++; 221 // NULL required timer value 222 uns16 time = 0; 223 // Is there a timer value? 224 if ( reqPower.7 ) 225 { 226 // Un-flag timer value 227 reqPower.7 = 0; 228 // Is there enough data for a timer value? 229 if ( --_DpaDataLength == 0 ) 230 goto _ERROR_DATA_LEN; 231 232 // Get required time 233 time.low8 = *FSR0++; 234 // Valid timer value? 235 if ( ( time.low8 & 0x7F ) == 0 ) 236 { 237 _ERROR_DATA: 238 DpaApiReturnPeripheralError( ERROR_DATA ); 239 } 240 241 // Conversion coefficient, ready for minutes to 250 ms 242 uns8 coef = 60 * ( 1000 / 250 ); 243 // Get time in units s/min 244 if ( time.7 ) 245 { 246 // Seconds 247 time.7 = 0; 248 // Convert from seconds 249 coef = 1000 / 250; 250 } 251 252 // Convert to 250 ms 253 time *= coef; 254 } 255 256 // Is the power parameter correct? 257 if ( reqPower > 100 && reqPower < 0x7F ) 258 goto _ERROR_DATA; 259 260 // Process only implemented lights 261 if ( index < LIGHTS_COUNT ) 262 { 263 // Increment or decrement? 264 if ( _PCMD != PCMD_STD_LIGHT_SET ) 265 { 266 // Note: we do not report power if parameter for increment or decrement is 0x7F 267 // Get current power 268 uns8 curPower = SetLight( index, 0x7F ); 269 // Increment? 270 if ( _PCMD == PCMD_STD_LIGHT_INC ) 271 { 272 // Do increment and check for maximum 273 reqPower += curPower; 274 if ( reqPower > 100 ) 275 reqPower = 100; 276 } 277 else 278 { // case PCMD_STD_LIGHT_DEC 279 // Do decrement and check for minimum 280 reqPower = curPower - reqPower; 281 if ( (int8)reqPower < 0 ) 282 reqPower = 0; 283 } 284 } 285 286 // Disable timer, so there will be no background activity with the light 287 TMR6ON = FALSE; 288 // Is there a requirement for setting the power level? Then store the timer. 289 if ( reqPower != 0x7f ) 290 { 291 // Write timer to the timer database 292 uns16 saveFSR0 = FSR0; 293 FSR0 = index * sizeof( uns16 ); 294 FSR0 += (uns16)&Timers[0]; 295 setINDF0( time.low8 ); 296 FSR0++; 297 setINDF0( time.high8 ); 298 FSR0 = saveFSR0; 299 } 300 301 // Set power and get previous actual power level 302 SetLight( index, reqPower ); 303 W = PrevActualPower; 304 // Enable timer again 305 TMR6ON = TRUE; 306 } 307 else 308 // Selected light is not implemented, return 0 power 309 W = 0; 310 311 // Store previous power to the response 312 setINDF1( W ); 313 // Move to the next response byte 314 FSR1++; 315 } 316 // Shift bitmap 317 lightsBitmap[3] = rr( lightsBitmap[3] ); 318 lightsBitmap[2] = rr( lightsBitmap[2] ); 319 lightsBitmap[1] = rr( lightsBitmap[1] ); 320 lightsBitmap[0] = rr( lightsBitmap[0] ); 321 // Next light index 322 } while ( ++index < 32 ); 323 324 // Too much data? 325 if ( --_DpaDataLength != 0 ) 326 goto _ERROR_DATA_LEN; 327 328 // Return data 329 _DpaDataLength = FSR1L - ( (uns16)&_DpaMessage.Response.PData[0] & 0xFF ); 330 goto DpaHandleReturnTRUE; 331 } 332 } 333 } 334 335 break; 336 } 337 338 // ------------------------------------------------- 339 case DpaEvent_Init: 340 // Do a one time initialization work before main loop starts 341 342 // Setup TMR6 to generate 10 ms ticks 343 #if F_OSC == 16000000 344 PR6 = 250 - 1; 345 T6CON = 0b0.1001.1.10; // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms 346 #else 347 #error Unsupported oscillator frequency 348 #endif 349 350 break; 351 352 // ------------------------------------------------- 353 case DpaEvent_AfterSleep: 354 // Called after woken up after sleep 355 356 TMR6IE = TRUE; 357 TMR6ON = TRUE; 358 break; 359 360 // ------------------------------------------------- 361 case DpaEvent_BeforeSleep: 362 // Called before going to sleep 363 364 // ------------------------------------------------- 365 case DpaEvent_DisableInterrupts: 366 // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM) 367 368 // Must not use TMR6 any more 369 TMR6ON = FALSE; 370 TMR6IE = FALSE; 371 break; 372 373 case DpaEvent_FrcValue: 374 // Called to get FRC value 375 376 // Check for correct FRC user data 377 if ( DataOutBeforeResponseFRC[0] == PNUM_STD_LIGHT ) 378 { 379 // Check for the correct light index and prepare pointer to the power array 380 uns8 index = DataOutBeforeResponseFRC[1] & 0x1F; 381 if ( index < LIGHTS_COUNT ) 382 { 383 FSR0PowerPointer( index ); 384 // Check for FRC command 385 switch ( _PCMD ) 386 { 387 case FRC_STD_LIGHT_ONOFF: 388 responseFRCvalue.1 = 1; 389 // Is the light off? 390 if ( FSR0[0] == 0 ) 391 responseFRCvalue.0 = 0; 392 break; 393 394 case FRC_STD_LIGHT_ALARM: 395 // !EXAMPLE! in this example return alarm if the light power is 100 % 396 responseFRCvalue.1 = 1; 397 if ( FSR0[0] != 100 ) 398 responseFRCvalue.0 = 0; 399 break; 400 } 401 } 402 } 403 goto DpaHandleReturnFALSE; 404 405 // ------------------------------------------------- 406 case DpaEvent_FrcResponseTime: 407 // Called to get FRC response time 408 409 // In this example the FRC commands are fast 410 switch ( DataOutBeforeResponseFRC[0] ) 411 { 412 case FRC_STD_LIGHT_ONOFF: 413 case FRC_STD_LIGHT_ALARM: 414 responseFRCvalue = _FRC_RESPONSE_TIME_40_MS; 415 break; 416 } 417 break; 418 419 } 420 DpaHandleReturnFALSE: 421 return FALSE; 422 } 423 424 //############################################################################################ 425 // Hand written multiplication using static variables because CC5X uses hidden "automatic" variables so it cannot be called from the interrupt 426 static uns16 multiplier16; 427 static uns8 multiplicand8; 428 static uns16 mul16x8result; 429 void mul16x8() 430 //############################################################################################ 431 { 432 mul16x8result = 0; 433 static uns8 loop; 434 loop = 8; 435 do 436 { 437 if ( multiplicand8.0 ) 438 mul16x8result += multiplier16; 439 multiplier16 <<= 1; 440 multiplicand8 >>= 1; 441 } while ( --loop != 0 ); 442 } 443 444 445 //############################################################################################ 446 void FSR0PowerPointer( uns8 index @ W ) 447 //############################################################################################ 448 { 449 // Prepare powers pointer 450 FSR0 = index * sizeof( uns16 ); 451 FSR0 += (uns16)&Powers[0]; 452 } 453 454 //############################################################################################ 455 // Note: This method is called in the interrupt too, so all variables and parameters must be static, also in called functions 456 // IMPORTANT: !beware of the hidden variables generated by compiler e.g. in case of multiplication generated by CC5X 457 uns8 static _index, _reqPower; 458 uns8 SetLight( uns8 index @ _index, uns8 reqPower @ _reqPower ) 459 //############################################################################################ 460 { 461 // Note: FSRs must not be modified 462 463 // Save FRS0 464 static uns16 saveFSR0; 465 saveFSR0 = FSR0; 466 // Prepare powers pointer 467 FSR0PowerPointer( index ); 468 // Get previous actual power 469 PrevActualPower = FSR0[0]; 470 static uns8 prevReqPower; 471 // Get previous required power 472 prevReqPower = FSR0[1]; 473 // If just asking for the power levels, return values, but do not set new power 474 if ( reqPower == 0x7F ) 475 goto _returnNoSet; 476 477 skip( index ); 478 #pragma computedGoto 1 479 goto set0; 480 goto set1; 481 #pragma computedGoto 0 482 ; 483 // -------------------------------------- 484 set1: // Control LEDG by GPIO 485 if ( reqPower != 0 ) 486 { 487 // On @ 100% 488 _LEDG = 1; 489 W = 100; 490 goto _return; 491 } 492 else 493 { 494 // Off @ 0 % 495 _LEDG = 0; 496 W = 0; 497 goto _return; 498 } 499 500 // -------------------------------------- 501 set0: // Control LEDR using 5-bit PIC DAC 502 503 // Initialize DAC: DAC on, output enabled, from VDD to VSS 504 DACCON0 = 0b111.0.00.0.0; 505 506 // 5-bit DAC has 31 ON levels, we have to spread it between 1-100 % 507 // 80 = round( 31 / 100 * 256 ) 508 multiplier16 = reqPower; 509 multiplicand8 = 80; 510 mul16x8(); 511 // Round it before / 256 512 mul16x8result += 128; 513 // Make sure that light is on always when non-zero power is requested 514 if ( reqPower != 0 && mul16x8result.high8 == 0 ) 515 mul16x8result.8 = 1; 516 517 // Set DAC and prepare the value for the back computation of the actual power 518 DACCON1 = multiplicand8 = mul16x8result.high8; 519 520 // Convert actually used 32 level DAC value back to real 0-100 % 521 // 826 = 100 / 31 * 256 522 multiplier16 = 826; 523 mul16x8(); 524 // Round it before / 256 525 mul16x8result += 128; 526 W = mul16x8result.high8; 527 // Return powers 528 goto _return 529 // -------------------------------------- 530 ; 531 _return: 532 // Store actual power 533 setINDF0( W ); 534 // Store requested power 535 FSR0 += 1; 536 setINDF0( reqPower ); 537 538 _returnNoSet: 539 FSR0 = saveFSR0; 540 // Return required power 541 return prevReqPower; 542 } 543 544 //############################################################################################ 545 // 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) 546 #include "DPAcustomHandler.h" 547 //############################################################################################