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