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