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