1 // **************************************************************************** 2 // Custom DPA Handler code example - Standard Binary Outputs - Template * 3 // **************************************************************************** 4 // Copyright (c) MICRORISC s.r.o. 5 // 6 // File: $RCSfile: 0C02_BinaryOutput-Template.c,v $ 7 // Version: $Revision: 1.33 $ 8 // Date: $Date: 2022/02/25 09:41:26 $ 9 // 10 // Revision history: 11 // 2022/02/24 Release for DPA 4.17 12 // 2021/08/20 Release for DPA 4.16 13 // 2020/01/09 Release for DPA 4.12 14 // 2017/11/16 Release for DPA 3.02 15 // 2017/08/14 Release for DPA 3.01 16 // 17 // ********************************************************************* 18 19 // MCR-BuildStdHandler 20 #message '+CC5X -bu' 21 22 #define _HWPID_ HWPID_IQRF_TECH__DEMO_BINARY_OUTPUT 23 #define _HWPIDver_ 0x0000 24 25 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/ 26 // IQRF Standards documentation https://doc.iqrf.org/ 27 28 // This example implements 8 binary outputs according to the IQRF Binary Outputs standard 29 // Index 0: Red LED 30 // Index 1: Green LED 31 // Index 2: GPIO C.2 = SIM C2 = C8 : Relay #1 @ DDC-RE-01 or C2 @ DDC-IO-01 32 // Index 3: GPIO C.5 = SIM C8 = C1 : Relay #2 @ DDC-RE-01 or C8 @ DDC-IO-01 33 // Index 4: GPIO C.4 = SIM C7 = C2 : C7 @ DDC-IO-01 34 // Index 5: GPIO C.3 = SIM C6 = C3 : C6 @ DDC-IO-01 35 // Index 6: GPIO A.5 = SIM C5 = C4 : C5 @ DDC-IO-01 36 // Index 7: GPIO A.0 = SIM C1 = C5 : C1 @ DDC-IO-01 37 38 // Default IQRF include (modify the path according to your setup) 39 #include "IQRF.h" 40 41 // Default DPA header (modify the path according to your setup) 42 #include "DPA.h" 43 // Default Custom DPA Handler header (modify the path according to your setup) 44 #include "DPAcustomHandler.h" 45 // IQRF standards header (modify the path according to your setup) 46 #include "IQRFstandard.h" 47 #include "IQRF_HWPID.h" 48 49 #if DPA_VERSION_MASTER < 0x0301 50 #error DPA version 3.01++ is required 51 #endif 52 53 //############################################################################################ 54 55 // Number of implemented binary outputs 56 #define OUTPUTS_COUNT 8 57 58 // Sets and Gets state of the indexed binary output 59 void SetOutput( uns8 state, uns8 index ); 60 bit GetOutput( uns8 index ); 61 62 // GPIO C.2 = SIM C2 = C8 : Relay #1 @ DDC-RE-01 or C2 @ DDC-IO-01 63 #define BINOUT2_LAT LATC.2 64 #define BINOUT2_TRIS TRISC.2 65 // GPIO C.5 = SIM C8 = C1 : Relay #2 @ DDC-RE-01 or C8 @ DDC-IO-01 66 #define BINOUT3_LAT LATC.5 67 #define BINOUT3_TRIS TRISC.5 68 // GPIO C.4 = SIM C7 = C2 : C7 @ DDC-IO-01 69 #define BINOUT4_LAT LATC.4 70 #define BINOUT4_TRIS TRISC.4 71 // GPIO C.3 = SIM C6 = C3 : C6 @ DDC-IO-01 72 #define BINOUT5_LAT LATC.3 73 #define BINOUT5_TRIS TRISC.3 74 // GPIO A.5 = SIM C5 = C4 : C5 @ DDC-IO-01 75 #define BINOUT6_LAT LATA.5 76 #define BINOUT6_TRIS TRISA.5 77 // GPIO A.0 = SIM C1 = C5 : C1 @ DDC-IO-01 78 #define BINOUT7_LAT LATA.0 79 #define BINOUT7_TRIS TRISA.0 80 81 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 82 //############################################################################################ 83 bit CustomDpaHandler() 84 //############################################################################################ 85 { 86 // 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) 87 #pragma updateBank default = UserBank_01 88 89 // Timers for outputs. The space must be long enough to fit them all. 2 bytes per one binary output. 90 static uns16 Timers[OUTPUTS_COUNT]; 91 92 // Handler presence mark 93 clrwdt(); 94 95 // Detect DPA event to handle 96 switch ( GetDpaEvent() ) 97 { 98 // ------------------------------------------------- 99 case DpaEvent_Interrupt: 100 // Do an extra quick background interrupt work 101 // ! 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. 102 // ! 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. 103 // ! 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. 104 // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy. 105 // ! Make sure race condition does not occur when accessing those variables at other places. 106 // ! 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. 107 // ! Do not call any OS functions except setINDFx(). 108 // ! Do not use any OS variables especially for writing access. 109 // ! 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. 110 111 // If TMR6 interrupt occurred, every 10 ms 112 if ( TMR6IF ) 113 { 114 // Unmask interrupt 115 TMR6IF = 0; 116 117 // Count 250 ms from 10 ms micro ticks 118 static uns8 count250ms; 119 if ( ++count250ms == ( 250 / 10 ) ) 120 { 121 // 250 ms 122 count250ms = 0; 123 124 // Pointer to the timers array 125 FSR1 = (uns16)&Timers[0]; 126 // Output index 127 static uns8 index; 128 index = 0; 129 do 130 { 131 // Is timer running (is non-zero)? 132 if ( ( FSR1[1] | INDF1 ) != 0 ) 133 { 134 // Get time 135 static uns16 time; 136 time.low8 = *FSR1++; 137 time.high8 = *FSR1; 138 // Is timer over? 139 if ( --time == 0 ) 140 // Set output to OFF 141 SetOutput( 0, index ); 142 143 // Store new time 144 setINDF1( time.high8 ); 145 FSR1--; 146 setINDF1( time.low8 ); 147 } 148 // Next timer 149 FSR1 += sizeof( Timers[0] ); 150 // Next index 151 } while ( ++index < OUTPUTS_COUNT ); 152 } 153 } 154 return Carry; 155 156 // ------------------------------------------------- 157 case DpaEvent_DpaRequest: 158 // Called to interpret DPA request for peripherals 159 // ------------------------------------------------- 160 // Peripheral enumeration 161 if ( IsDpaEnumPeripheralsRequest() ) 162 { 163 // We implement 1 standard peripheral 164 _DpaMessage.EnumPeripheralsAnswer.UserPerNr |= 1; 165 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_BINARY_OUTPUTS ); 166 _DpaMessage.EnumPeripheralsAnswer.HWPID |= _HWPID_; 167 _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= _HWPIDver_; 168 169 DpaHandleReturnTRUE: 170 return TRUE; 171 } 172 // ------------------------------------------------- 173 // Get information about peripheral 174 else if ( IsDpaPeripheralInfoRequest() ) 175 { 176 if ( _PNUM == PNUM_STD_BINARY_OUTPUTS ) 177 { 178 _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_STD_BINARY_OUTPUTS; 179 _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE; 180 // Set standard version 181 _DpaMessage.PeripheralInfoAnswer.Par1 = STD_BINARY_OUTPUTS_VERSION; 182 goto DpaHandleReturnTRUE; 183 } 184 185 break; 186 } 187 // ------------------------------------------------- 188 else 189 { 190 // Handle peripheral command 191 192 // Supported peripheral number? 193 if ( _PNUM == PNUM_STD_BINARY_OUTPUTS ) 194 { 195 // Supported commands? 196 switch ( _PCMD ) 197 { 198 // Invalid command 199 default: 200 { 201 // Return error 202 W = ERROR_PCMD; 203 ERROR_W: 204 DpaApiReturnPeripheralError( W ); 205 } 206 207 // Outputs enumeration 208 case PCMD_STD_ENUMERATE: 209 if ( _DpaDataLength != 0 ) 210 goto _ERROR_DATA_LEN; 211 212 // Return number of outputs 213 _DpaMessage.Response.PData[0] = OUTPUTS_COUNT; 214 W = 1; 215 goto _DpaDataLengthW; 216 217 // Supported commands. 218 case PCMD_STD_BINARY_OUTPUTS_SET: 219 { 220 // Pointers FSR01 to data are already set at the DPA 221 222 // As this template implements < 9 outputs the working bitmap is uns8, if more outputs are implemented then uns16, ..., uns32 must be used 223 #if OUTPUTS_COUNT < 9 224 uns8 inBitmap = *FSR0--; 225 uns8 outBitmap @ _DpaMessage.Response.PData[0]; 226 uns8 bitmapMask = 0b1; 227 #else 228 #error Not implemented 229 #endif 230 231 // Number of selected outputs + bitmap length 232 uns8 outputsCount = 4; 233 // Loop bitmap 234 uns8 index = 4; 235 do 236 { 237 // Count bits of next byte 238 uns8 byte = *++FSR0; 239 if ( byte != 0 ) 240 { 241 // Brian Kernighan's Algorithm for counting set bits 242 do 243 { 244 outputsCount++; 245 byte &= byte - 1; 246 } while ( byte != 0 ); 247 } 248 249 // Reset bitmap 250 setINDF0( 0 ); 251 } while ( --index != 0 ); 252 253 // Check data length 254 if ( _DpaDataLength != outputsCount ) 255 { 256 _ERROR_DATA_LEN: 257 W = ERROR_DATA_LEN; 258 goto ERROR_W; 259 } 260 261 // Pointer to the timers array 262 FSR1 = (uns16)&Timers[0]; 263 // Output index 264 index = 0; 265 do 266 { 267 // Output was set? 268 if ( GetOutput( index ) ) 269 // Yes, set in the output bitmap 270 outBitmap |= bitmapMask; 271 272 // Implemented output selected? Set the state. 273 if ( inBitmap.0 ) 274 { 275 // Default is timer off 276 uns16 time = 0; 277 // Desired state 278 uns8 state = *++FSR0; 279 if ( state > 1 ) 280 { 281 // Get time in units s/min 282 time = state & 0x7F; 283 if ( time == 0 ) 284 { 285 // Invalid time 286 W = ERROR_FAIL; 287 goto ERROR_W; 288 } 289 290 // Conversion coefficient, ready for minutes to 250 ms 291 uns8 coef = 60 * ( 1000 / 250 ); 292 if ( state.7 ) 293 // Convert from seconds 294 coef = 1000 / 250; 295 296 // Convert to 250 ms 297 time *= coef; 298 // Set ON 299 state = 1; 300 } 301 302 // Set output 303 SetOutput( state, index ); 304 305 // Set timer and preserve pointer 306 GIE = FALSE; 307 setINDF1( time.low8 ); 308 FSR1++; 309 setINDF1( time.high8 ); 310 FSR1--; 311 GIE = TRUE; 312 } 313 314 // Next pointer to the timer 315 FSR1 += sizeof( Timers[0] ); 316 // Next bits 317 bitmapMask <<= 1; 318 inBitmap >>= 1; 319 // Next index 320 } while ( ++index < OUTPUTS_COUNT ); 321 322 // Return bitmap 323 _DpaDataLength4: 324 W = 4; 325 _DpaDataLengthW: 326 _DpaDataLength = W; 327 goto DpaHandleReturnTRUE; 328 } 329 } 330 } 331 332 break; 333 } 334 335 // ------------------------------------------------- 336 case DpaEvent_Init: 337 // Do a one time initialization before main loop starts 338 339 // Initialize relays @ DDC-RE 340 BINOUT2_LAT = 0; 341 BINOUT3_LAT = 0; 342 BINOUT4_LAT = 0; 343 BINOUT5_LAT = 0; 344 BINOUT6_LAT = 0; 345 BINOUT7_LAT = 0; 346 347 BINOUT2_TRIS = 0; 348 BINOUT3_TRIS = 0; 349 BINOUT4_TRIS = 0; 350 BINOUT5_TRIS = 0; 351 BINOUT6_TRIS = 0; 352 BINOUT7_TRIS = 0; 353 354 // Setup TMR6 to generate ticks on the background (ticks every 10ms) 355 _PR6 = 250 - 1; 356 // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms 357 #if defined( TR7xG ) 358 TMR6MD = 0; 359 T6CON = 0b1.100.1001; 360 // Timer2/4/6 Clock Select bits = FOSC/4 361 T6CLKCON |= 0b0000.0001; 362 #else 363 T6CON = 0b0.1001.1.10; 364 #endif 365 366 367 break; 368 369 // ------------------------------------------------- 370 case DpaEvent_AfterSleep: 371 // Called after woken up after sleep 372 373 TMR6IE = TRUE; 374 _TMR6ON = TRUE; 375 break; 376 377 // ------------------------------------------------- 378 case DpaEvent_BeforeSleep: 379 // Called before going to sleep 380 381 // ------------------------------------------------- 382 case DpaEvent_DisableInterrupts: 383 // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM) 384 385 // Must not use TMR6 any more 386 _TMR6ON = FALSE; 387 TMR6IE = FALSE; 388 break; 389 } 390 DpaHandleReturnFALSE: 391 return FALSE; 392 } 393 394 //############################################################################################ 395 static uns8 _state; 396 void SetOutput( uns8 state @ _state, uns8 index @ W ) 397 //############################################################################################ 398 { 399 // Note: FSRs must not be modified 400 // Note: This method is called in the interrupt too! 401 402 skip( index ); 403 #pragma computedGoto 1 404 goto set0; 405 goto set1; 406 goto set2; 407 goto set3; 408 goto set4; 409 goto set5; 410 goto set6; 411 goto set7; 412 #pragma computedGoto 0 413 ; 414 // -------------------------------------- 415 set7: 416 if ( !state.0 ) 417 BINOUT7_LAT = 0; 418 else 419 BINOUT7_LAT = 1; 420 421 return; 422 // -------------------------------------- 423 set6: 424 if ( !state.0 ) 425 BINOUT6_LAT = 0; 426 else 427 BINOUT6_LAT = 1; 428 429 return; 430 // -------------------------------------- 431 set5: 432 if ( !state.0 ) 433 BINOUT5_LAT = 0; 434 else 435 BINOUT5_LAT = 1; 436 437 return; 438 // -------------------------------------- 439 set4: 440 if ( !state.0 ) 441 BINOUT4_LAT = 0; 442 else 443 BINOUT4_LAT = 1; 444 445 return; 446 // -------------------------------------- 447 set3: 448 if ( !state.0 ) 449 BINOUT3_LAT = 0; 450 else 451 BINOUT3_LAT = 1; 452 453 return; 454 // -------------------------------------- 455 set2: 456 if ( !state.0 ) 457 BINOUT2_LAT = 0; 458 else 459 BINOUT2_LAT = 1; 460 461 return; 462 // -------------------------------------- 463 set1: 464 if ( !state.0 ) 465 _LEDG = 0; 466 else 467 _LEDG = 1; 468 469 return; 470 // -------------------------------------- 471 set0: 472 if ( !state.0 ) 473 _LEDR = 0; 474 else 475 _LEDR = 1; 476 477 return; 478 } 479 480 //############################################################################################ 481 bit GetOutput( uns8 index @ W ) 482 //############################################################################################ 483 { 484 Carry = FALSE; // Note: must not modify W 485 486 // Note: all below must not modify Carry except when needed 487 skip( index ); 488 #pragma computedGoto 1 489 goto get0; 490 goto get1; 491 goto get2; 492 goto get3; 493 goto get4; 494 goto get5; 495 goto get6; 496 goto get7; 497 #pragma computedGoto 0 498 ; 499 // -------------------------------------- 500 get7: 501 if ( BINOUT7_LAT ) 502 Carry = TRUE; 503 goto _return; 504 // -------------------------------------- 505 get6: 506 if ( BINOUT6_LAT ) 507 Carry = TRUE; 508 goto _return; 509 // -------------------------------------- 510 get5: 511 if ( BINOUT5_LAT ) 512 Carry = TRUE; 513 goto _return; 514 // -------------------------------------- 515 get4: 516 if ( BINOUT4_LAT ) 517 Carry = TRUE; 518 goto _return; 519 // -------------------------------------- 520 get3: 521 if ( BINOUT3_LAT ) 522 Carry = TRUE; 523 goto _return; 524 // -------------------------------------- 525 get2: 526 if ( BINOUT2_LAT ) 527 Carry = TRUE; 528 goto _return; 529 // -------------------------------------- 530 get1: 531 if ( _LEDG ) 532 Carry = TRUE; 533 goto _return; 534 // -------------------------------------- 535 get0: 536 if ( _LEDR ) 537 Carry = TRUE; 538 goto _return; 539 // -------------------------------------- 540 541 _return: 542 return Carry; 543 } 544 545 //############################################################################################ 546 // 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) 547 #include "DPAcustomHandler.h" 548 //############################################################################################