1 // *********************************************************************************************************** 2 // Custom DPA Handler code example - Standard Sensors + Binary output - DDC-SE-01 + DDC-RE-01 - LP version * 3 // *********************************************************************************************************** 4 // Copyright (c) MICRORISC s.r.o. 5 // 6 // File: $RCSfile: 4402_DDC-SE+RE_LP.c,v $ 7 // Version: $Revision: 1.30 $ 8 // Date: $Date: 2024/01/29 14:20:03 $ 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/02 Release for DPA 4.11 14 // 2019/03/07 Release for DPA 4.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 3 sensors according to the IQRF Sensors standard 22 // Index 0 i.e. 1st sensor is either Dallas 18B20 or MCP9802 temperature sensor at DDC-SE-01 board according to the HW jumper position and symbol DALLASnotMCP. 23 // Index 1 i.e. 2nd sensor is light intensity indicator at DDC-SE-01 board (value range is 0[max light]-127[max dark]). 24 // Index 2 i.e. 3rd sensor is potentiometer value at DDC-SE-01 board (value range is 0[left stop]-127[right stop]). 25 26 // This example also implements 2 binary outputs according to the IQRF Binary Outputs standard 27 // Index 0 i.e. 1st output is Relay #1 @ DDC-RE-01 28 // Index 1 i.e. 2nd output is Relay #2 @ DDC-RE-01 29 30 // This example must be compiled without a "-bu" compiler switch in order to fit into available Flash memory 31 32 // Default IQRF include (modify the path according to your setup) 33 #include "IQRF.h" 34 35 // We can save more instructions if needed by the symbol below 36 // #define PARAM_CHECK_LEVEL 1 37 38 // Default DPA header (modify the path according to your setup) 39 #include "DPA.h" 40 // Default Custom DPA Handler header (modify the path according to your setup) 41 #include "DPAcustomHandler.h" 42 // IQRF standards header (modify the path according to your setup) 43 #include "IQRFstandard.h" 44 #include "IQRF_HWPID.h" 45 46 // If defined then the handler is compiled for Dallas otherwise for MCP9802 47 //#define DALLASnotMCP 48 // !!! Important: with free C5XX compiler, the symbol DALLASnotMCP must not be defined, otherwise the code does not fit into available program memory @ TR7xD. 49 50 //############################################################################################ 51 52 // Define useful macro that saves some code but not preset at DPA < 3.01 53 #if DPA_VERSION_MASTER < 0x0301 54 // Optimized macro for both testing enumeration peripherals ELSE peripherals information. See examples 55 #define IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequestNoSize() if ( _PCMD == CMD_GET_PER_INFO ) if ( _PNUM == PNUM_ENUMERATION ) 56 57 #if PARAM_CHECK_LEVEL >= 2 58 #define IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest() if ( _DpaDataLength == 0 && _PCMD == CMD_GET_PER_INFO ) if ( _PNUM == PNUM_ENUMERATION ) 59 #else 60 #define IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest() IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequestNoSize() 61 #endif 62 #endif 63 64 //############################################################################################ 65 66 // Number of implemented sensors 67 #define SENSORS_COUNT 3 68 69 // Variable to store sensor value at Get?_????() methods. This example implements sensors returning maximum 2 bytes of data. 70 uns16 sensorValue @ param3; 71 72 // Reads sensor value to the sensorValue variable and to responseFRCvalue(2B) variable 73 bit Get0_Temperature(); 74 bit Get1_BinaryData_Light(); 75 bit Get2_BinaryData_Potentiometer(); 76 77 // Stores sensor value byte(s) to the FSR1[+1...], in case of PCMD_STD_SENSORS_READ_TYPES_AND_VALUES sensor type is stored before value byte(s) 78 void StoreValue( uns8 sensorType ); 79 80 // ms per ticks 81 #define TICKS_LEN 10 82 83 #ifdef DALLASnotMCP 84 // Sensor connected to PORT C.3 (compatible with DDC-SE-01) 85 #define OneWire_TRIS TRISC.3 86 #define OneWire_IO_IN PORTC.3 87 #define OneWire_IO_OUT LATC.3 88 89 // Writes sensor configuration (resolution) 90 bit Ds18B20WriteConfig( uns8 value ); 91 92 // Resets OneWire 93 bit OneWireReset(); 94 // Reads OneWire byte 95 uns8 OneWireReadByte(); 96 // Writes OneWire byte 97 void OneWireWriteByte( uns8 byte ); 98 99 // DS18B20 commands 100 #define CMD_READROM 0x33 101 #define CMD_CONVERTTEMP 0x44 102 #define CMD_CPYSCRATCHPAD 0x48 103 #define CMD_WSCRATCHPAD 0x4e 104 #define CMD_MATCHROM 0x55 105 #define CMD_RPWRSUPPLY 0xb4 106 #define CMD_RECEEPROM 0xb8 107 #define CMD_RSCRATCHPAD 0xbe 108 #define CMD_SKIPROM 0xcc 109 #define CMD_ALARMSEARCH 0xec 110 #define CMD_SEARCHROM 0xf0 111 112 // Final DS18B20 temperature value read by state machine 113 uns16 temperature; 114 115 #else // DALLASnotMCP 116 117 // I2C SCL frequency [Hz] 118 #define I2Cfrequency 50000 119 120 // MCP9802 address 121 #define I2C_ADR 0b1001.0110 122 123 #endif 124 125 //############################################################################################ 126 127 // Number of implemented binary outputs 128 #define OUTPUTS_COUNT 2 129 130 // Sets and Gets state of the indexed binary output 131 void SetOutput( uns8 state, uns8 index ); 132 bit GetOutput( uns8 index ); 133 134 // DDC-RE-01 relay pins 135 // C.5 = C8 = Relay#1 136 #define RELAY1_LAT LATC.5 137 #define RELAY1_TRIS TRISC.5 138 // C.2 = C2 = Relay#2 139 #define RELAY2_LAT LATC.2 140 #define RELAY2_TRIS TRISC.2 141 142 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 143 //############################################################################################ 144 bit CustomDpaHandler() 145 //############################################################################################ 146 { 147 // 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) 148 #pragma updateBank default = UserBank_01 149 150 #ifdef DALLASnotMCP 151 // Finite machine states 152 typedef enum 153 { 154 S_ResetConvertT = 0, 155 S_SkipRomConvertT, 156 S_CmdConvertT, 157 158 S_WaitConvertT, 159 160 S_ResetReadTemp, 161 S_SkipRomReadTemp, 162 S_CmdReadTemp, 163 S_Byte1ReadTemp, 164 S_Byte2ReadTemp 165 } TState; 166 #endif 167 168 // Handler presence mark 169 clrwdt(); 170 171 // Sleeping parameters, valid when Time != 0 172 static TPerOSSleep_Request PerOSSleep_Request; 173 174 #ifdef DALLASnotMCP 175 // Finite machine state 176 static uns8 state; // = S_ResetConvertT = 0 177 // Pre-read lower temperature byte 178 static uns8 temperatureByteLow; 179 // Conversion timeout counter 180 static uns16 timeoutStart; 181 #endif 182 183 // Timers for outputs. The space must be long enough to fit them all. 2+2 bytes per one binary output. 184 // 2B timeout 185 // 2B startTicks 186 static uns16 Timers[OUTPUTS_COUNT * 2]; 187 188 // Detect DPA event to handle 189 switch ( GetDpaEvent() ) 190 { 191 // ------------------------------------------------- 192 case DpaEvent_Interrupt: 193 // Do an extra quick background interrupt work 194 195 return Carry; 196 197 // ------------------------------------------------- 198 case DpaEvent_Idle: 199 // Do a quick background work when RF packet is not received 200 201 // Should go to sleep? 202 if ( PerOSSleep_Request.Time != 0 ) 203 { 204 // Copy sleep parameters to the DPA request 205 _DpaMessage.PerOSSleep_Request.Time = PerOSSleep_Request.Time; 206 _DpaMessage.PerOSSleep_Request.Control = PerOSSleep_Request.Control; 207 // Finalize OS Sleep DPA Request 208 _DpaDataLength = sizeof( _DpaMessage.PerOSSleep_Request ); 209 _PNUM = PNUM_OS; 210 _PCMD = CMD_OS_SLEEP; 211 #ifndef DALLASnotMCP 212 // I2C down 213 _DpaApiI2Cshutdown(); 214 #endif 215 // Perform local DPA Request to go to sleep 216 DpaApiLocalRequest(); 217 #ifndef DALLASnotMCP 218 // I2C up 219 _DpaApiI2Cinit( I2CcomputeFrequency( I2Cfrequency ) ); 220 #endif 221 // Switch off sleeping time=flag 222 PerOSSleep_Request.Time = 0; 223 } 224 225 // Check binary output timers 226 { 227 // Pointer to the timers array 228 FSR1 = (uns16)&Timers[0]; 229 // Output index 230 uns8 index; 231 index = 0; 232 do 233 { 234 // Is timer running (is non-zero)? 235 if ( ( FSR1[1] | INDF1 ) != 0 ) 236 { 237 // Get timer value 238 uns16 timer; 239 timer.low8 = FSR1[0]; 240 timer.high8 = FSR1[1]; 241 // Get start time 242 uns16 timerStart; 243 timerStart.low8 = FSR1[2]; 244 timerStart.high8 = FSR1[3]; 245 // Measure elapsed time 246 captureTicks(); // Note: must not modify FSR1 247 param3 -= timerStart; 248 // It time over? 249 if ( param3 > timer ) 250 { 251 // Set output to OFF 252 SetOutput( 0, index ); 253 // Reset timer 254 setINDF1( 0 ); 255 FSR1++; 256 setINDF1( 0 ); 257 FSR1--; 258 } 259 } 260 // Next timer 261 FSR1 += 2 * sizeof( Timers[0] ); 262 // Next index 263 } while ( ++index < OUTPUTS_COUNT ); 264 } 265 266 #ifdef DALLASnotMCP 267 // Run finite state machine to read temperature from DS18B20 at background so the temperature value is immediately ready for FRC 268 269 // Make sure 1Wire data pin at LATX.y is low as it might be set by another PORTX.? pin manipulation 270 OneWire_IO_OUT = 0; 271 272 skip( state ); 273 #pragma computedGoto 1 274 goto _S_ResetConvertT; 275 goto _S_SkipRomConvertT; 276 goto _S_CmdConvertT; 277 goto _S_WaitConvertT; 278 goto _S_ResetReadTemp; 279 goto _S_SkipRomReadTemp; 280 goto _S_CmdReadTemp; 281 goto _S_Byte1ReadTemp; 282 goto _S_Byte2ReadTemp; 283 #pragma computedGoto 0 284 ; 285 // -------------- 286 _S_Byte2ReadTemp: 287 temperature.high8 = OneWireReadByte(); 288 temperature.low8 = temperatureByteLow; 289 290 ResetMachine: 291 state = S_ResetConvertT; 292 goto ExitMachine; 293 294 // -------------- 295 _S_ResetConvertT: 296 _S_ResetReadTemp: 297 if ( !OneWireReset() ) 298 { 299 _S_Error_Reset: 300 STD_SENSOR_TYPE_TEMPERATURE_SET_ERROR( temperature ); 301 goto ResetMachine; 302 } 303 goto NextState; 304 305 // -------------- 306 _S_SkipRomConvertT: 307 _S_SkipRomReadTemp: 308 // OneWire: Skip ROM 309 OneWireWriteByte( CMD_SKIPROM ); 310 goto NextState; 311 312 // -------------- 313 _S_CmdConvertT: 314 // OneWire: Convert temperature 315 OneWireWriteByte( CMD_CONVERTTEMP ); 316 // Start timeout for approx 750 ms (the longest conversion time) 317 captureTicks(); 318 // Remember start time 319 timeoutStart = param3; 320 goto NextState; 321 322 // -------------- 323 _S_WaitConvertT: 324 // Measured? 325 if ( OneWireReadByte() == 0xff ) 326 goto NextState; 327 328 // Timeout? 329 captureTicks(); 330 param3 -= timeoutStart; 331 // Yes! 332 if ( param3 > ( 2 + 750 / TICKS_LEN ) ) 333 goto _S_Error_Reset; 334 335 goto ExitMachine; 336 337 // -------------- 338 _S_CmdReadTemp: 339 // OneWire: Read scratchpad 340 OneWireWriteByte( CMD_RSCRATCHPAD ); 341 goto NextState; 342 343 // -------------- 344 _S_Byte1ReadTemp: 345 temperatureByteLow = OneWireReadByte(); 346 goto NextState; 347 348 // -------------- 349 NextState: 350 ++state; 351 352 ExitMachine: 353 #endif 354 break; 355 356 // ------------------------------------------------- 357 case DpaEvent_Init: 358 // Do a one time initialization before main loop starts 359 360 // Initialize ticks 361 startCapture(); 362 363 // Initialize relays @ DDC-RE 364 RELAY1_LAT = 0; 365 RELAY2_LAT = 0; 366 RELAY1_TRIS = 0; 367 RELAY2_TRIS = 0; 368 369 // Initialize sensors 370 // C5 (AN4) as input 371 moduleInfo(); 372 // Connected TR pins? 373 if ( !bufferINFO[5].7 ) 374 { 375 TRISC.6 = 1; 376 TRISB.4 = 1; 377 } 378 TRISA.5 = 1; 379 380 // C1 (AN0) as input 381 TRISA.0 = 1; 382 383 #ifdef DALLASnotMCP 384 // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet) 385 Ds18B20WriteConfig( 0b0.00.00000 ); 386 #else 387 // Expect MCP9802 is enabled 388 I2Ctimeout = 0xFF; 389 390 #if defined( TR7xG ) 391 // Do PPS for I2C 392 unlockPPS(); 393 SSP1CLKPPS = 0x13; // RC3 394 SSP1DATPPS = 0x14; // RC4 395 RC3PPS = 0x14; // SCK1/SCL1 396 RC4PPS = 0x15; // SD01/SDA1 397 lockPPS(); 398 #endif 399 400 _DpaApiI2Cinit( I2CcomputeFrequency( I2Cfrequency ) ); 401 #endif 402 break; 403 404 // ------------------------------------------------- 405 case DpaEvent_AfterSleep: 406 // Called after woken up after sleep 407 #ifndef DALLASnotMCP 408 _DpaApiI2Cinit( I2CcomputeFrequency( I2Cfrequency ) ); 409 #endif 410 411 break; 412 413 // ------------------------------------------------- 414 case DpaEvent_BeforeSleep: 415 // Called before going to sleep 416 #ifndef DALLASnotMCP 417 _DpaApiI2Cshutdown(); 418 #endif 419 break; 420 421 // ------------------------------------------------- 422 case DpaEvent_DpaRequest: 423 // Called to interpret DPA request for peripherals 424 // ------------------------------------------------- 425 // Peripheral enumeration 426 IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest() 427 { 428 // We implement 2 standard peripherals 429 _DpaMessage.EnumPeripheralsAnswer.UserPerNr |= 2; 430 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_SENSORS ); 431 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_BINARY_OUTPUTS ); 432 _DpaMessage.EnumPeripheralsAnswer.HWPID |= HWPID_IQRF_TECH__DEMO_DDC_SE01_RE01_LP; 433 _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= 0x0000; 434 435 DpaHandleReturnTRUE: 436 return TRUE; 437 } 438 // ------------------------------------------------- 439 // Get information about peripherals 440 else 441 { 442 #if PERIPHERAL_TYPE_STD_SENSORS != PNUM_STD_SENSORS || PERIPHERAL_TYPE_STD_BINARY_OUTPUTS != PNUM_STD_BINARY_OUTPUTS 443 #error 444 #endif 445 switch ( _DpaMessage.PeripheralInfoAnswer.PerT = _PNUM ) 446 { 447 case PNUM_STD_SENSORS: 448 // Set standard version 449 W = STD_SENSORS_VERSION; 450 goto Par1toVersion; 451 452 case PNUM_STD_BINARY_OUTPUTS: 453 // Set standard version 454 W = STD_BINARY_OUTPUTS_VERSION; 455 Par1toVersion: 456 _DpaMessage.PeripheralInfoAnswer.Par1 = W; 457 _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ; 458 goto DpaHandleReturnTRUE; 459 } 460 461 break; 462 } 463 464 { 465 // ------------------------------------------------- 466 // Handle peripheral command 467 468 // Supported peripheral number? 469 switch ( _PNUM ) 470 { 471 case PNUM_STD_SENSORS: 472 { 473 // Supported commands? 474 switch ( _PCMD ) 475 { 476 // Invalid command 477 default: 478 { 479 // Return error 480 _ERROR_PCMD: 481 W = ERROR_PCMD; 482 _ERROR_W: 483 DpaApiReturnPeripheralError( W ); 484 } 485 486 // Sensor enumeration 487 case PCMD_STD_ENUMERATE: 488 if ( _DpaDataLength != 0 ) 489 goto _ERROR_DATA_LEN; 490 491 _DpaMessage.Response.PData[0] = STD_SENSOR_TYPE_TEMPERATURE; 492 _DpaMessage.Response.PData[1] = STD_SENSOR_TYPE_BINARYDATA7; 493 _DpaMessage.Response.PData[2] = STD_SENSOR_TYPE_BINARYDATA7; 494 W = SENSORS_COUNT; 495 goto _W2_DpaDataLength; 496 497 // Supported commands. They are handled the same way except one "if" at StoreValue() method 498 case PCMD_STD_SENSORS_READ_VALUES: 499 case PCMD_STD_SENSORS_READ_TYPES_AND_VALUES: 500 { 501 // No sensor bitmap specified? W = _DpaDataLength. Note: W is used to avoid MOVLB at next if 502 W = _DpaDataLength; 503 if ( W == 0 ) // Note: must not modify W 504 { 505 // Actually clears the bitmap 506 #if &_DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap[0] != &bufferRF[0] 507 #error Cannot use clearBufferRF for clearing bitmap 508 #endif 509 clearBufferRF(); 510 // Simulate 1st only sensor in the bitmap (states of the other unimplemented sensors do not care) 511 _DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap[0].0 = 1; 512 // Bitmap is 32 bits long 513 _DpaDataLength = W = sizeof( _DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap ); 514 } 515 516 // Invalid bitmap (data) length (W = _DpaDataLength)? 517 if ( W != sizeof( _DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap ) ) 518 goto _ERROR_DATA_LEN; 519 520 // Now read the sensors 521 522 // Prepare pointer (minus 1, see below) to store sensor (types and) values to 523 // Note: 3 sensors at this example cannot return more than DPA_MAX_DATA_LENGTH bytes of data, so it does not have to be checked... 524 // ... If it would be the case, then ERROR_FAIL must be returned 525 FSR1 = &_DpaMessage.Response.PData[-1]; 526 527 // Store bitmap of sensors to get values from 528 uns8 sensorsBitmap = FSR1[1 + offsetof( TPerStdSensorRead_Request, Bitmap )]; 529 530 // 1st sensor (index 0) selected? 531 if ( sensorsBitmap.0 ) 532 { 533 Get0_Temperature(); 534 StoreValue( STD_SENSOR_TYPE_TEMPERATURE ); 535 } 536 537 // 2nd sensor (index 1) selected? 538 if ( sensorsBitmap.1 ) 539 { 540 Get1_BinaryData_Light(); 541 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 ); 542 } 543 544 // 3rd sensor (index 2) selected? 545 if ( sensorsBitmap.2 ) 546 { 547 Get2_BinaryData_Potentiometer(); 548 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 ); 549 } 550 551 // Compute returned data bytes count 552 W = FSR1L - ( (uns16)&_DpaMessageIqrfStd & 0xFF ) + 1; 553 // Optimization: return W long block of bytes at response 554 _W2_DpaDataLength: 555 _DpaDataLength = W; 556 goto DpaHandleReturnTRUE; 557 } 558 } 559 } 560 561 case PNUM_STD_BINARY_OUTPUTS: 562 { 563 // Supported commands? 564 switch ( _PCMD ) 565 { 566 // Invalid command 567 default: 568 // Return error 569 goto _ERROR_PCMD; 570 571 // Outputs enumeration 572 case PCMD_STD_ENUMERATE: 573 if ( _DpaDataLength != 0 ) 574 goto _ERROR_DATA_LEN; 575 576 // Return number of outputs 577 _DpaMessageIqrfStd.PerStdBinaryOutputEnumerate_Response.Count = OUTPUTS_COUNT; 578 W = sizeof( _DpaMessageIqrfStd.PerStdBinaryOutputEnumerate_Response ); 579 goto _W2_DpaDataLength; 580 581 // Supported commands. 582 case PCMD_STD_BINARY_OUTPUTS_SET: 583 { 584 // Pointers FSR01 to data are already set at the DPA 585 586 // As this template implements < 9 outputs the working bitmap is uns8, if more outputs are implemented then uns16, ..., uns32 must be used 587 #if OUTPUTS_COUNT < 9 588 uns8 inBitmap = *FSR0--; 589 uns8 outBitmap @ _DpaMessageIqrfStd.PerStdBinaryOutputSet_Request.Bitmap[0]; 590 uns8 bitmapMask = 0b1; 591 #else 592 #error Not implemented 593 #endif 594 595 // Number of selected outputs + bitmap length 596 uns8 outputsCount = sizeof( _DpaMessageIqrfStd.PerStdBinaryOutputSet_Request.Bitmap ); 597 // Loop bitmap 598 uns8 index = sizeof( _DpaMessageIqrfStd.PerStdBinaryOutputSet_Request.Bitmap ); 599 do 600 { 601 // Count bits of next byte 602 uns8 byte = *++FSR0; 603 if ( byte != 0 ) 604 { 605 // Brian Kernighan's Algorithm for counting set bits 606 do 607 { 608 outputsCount++; 609 byte &= byte - 1; 610 } while ( byte != 0 ); 611 } 612 613 // Reset bitmap 614 setINDF0( 0 ); 615 } while ( --index != 0 ); 616 617 // Check data length 618 if ( _DpaDataLength != outputsCount ) 619 { 620 _ERROR_DATA_LEN: 621 W = ERROR_DATA_LEN; 622 goto _ERROR_W; 623 } 624 625 // Pointer to the timers array 626 FSR1 = (uns16)&Timers[0]; 627 // Output index 628 index = 0; 629 do 630 { 631 // Output was set? 632 if ( GetOutput( index ) ) 633 // Yes, set in the output bitmap 634 outBitmap |= bitmapMask; 635 636 // Implemented output selected? Set the state. 637 if ( inBitmap.0 ) 638 { 639 // Default is timer off 640 uns16 time = 0; 641 // Desired state 642 uns8 state = *++FSR0; 643 if ( state > 1 ) 644 { 645 // Get time in units s/min 646 time = state & 0x7F; 647 if ( time == 0 ) 648 { 649 // Invalid time 650 W = ERROR_FAIL; 651 _ERROR_FAIL: 652 goto _ERROR_W; 653 } 654 655 // Conversion coefficient, ready for seconds 656 uns16 coef = 1000 / TICKS_LEN; 657 if ( !state.7 ) 658 { 659 // Check for the maximum supported time because of captureTicks method 660 if ( time.low8 > ( (uns24)0xFFFF * TICKS_LEN / 1000 / 60 ) ) 661 goto _ERROR_FAIL; 662 663 // Convert from minutes 664 uns16 coef = 60 * ( 1000 / TICKS_LEN ); 665 } 666 667 // Convert to 250 ms 668 time *= coef; 669 // Set ON 670 state = 1; 671 } 672 673 // Set output 674 SetOutput( state, index ); 675 676 // Set timer but preserve pointer 677 setINDF1( time.low8 ); 678 FSR1++; 679 setINDF1( time.high8 ); 680 FSR1++; 681 // Get start time 682 captureTicks(); //Note: must not destroy FSR1 683 setINDF1( param3.low8 ); 684 FSR1++; 685 setINDF1( param3.high8 ); 686 FSR1 -= 3; 687 } 688 689 // Pointer to the next timer 690 FSR1 += 2 * sizeof( Timers[0] ); 691 // Next bits 692 bitmapMask <<= 1; 693 inBitmap >>= 1; 694 // Next index 695 } while ( ++index < OUTPUTS_COUNT ); 696 697 // Return bitmap 698 _DpaDataLength4: 699 W = sizeof( _DpaMessageIqrfStd.PerStdBinaryOutputSet_Response.PreviousStates ); 700 goto _W2_DpaDataLength; 701 } 702 } 703 } 704 } 705 706 break; 707 } 708 709 // ------------------------------------------------- 710 case DpaEvent_FrcValue: 711 // Called to get FRC value 712 713 // FSR1 for optimization purposes (avoid MOVLB) will be used to point to DataOutBeforeResponseFRC[0...] 714 FSR1 = (uns16)&PerStdSensorFrc; 715 #if offsetof( TPerStdSensorFrc, Header ) != 0 || offsetof( TPerStdSensorFrc, SensorType ) != 1 || offsetof( TPerStdSensorFrc, Options ) != 3 716 #error Cannot optimize 717 #endif 718 // Check for correct FRC user data 719 if ( *FSR1++ /* PerStdSensorFrc.Header */ == PNUM_STD_SENSORS ) 720 { 721 // Actually used sensor index 722 uns8 sensorIndex = FSR1[offsetof( TPerStdSensorFrc, SensorIndex ) - 1] & 0x1f; 723 // Test sensor type 724 switch ( *FSR1++ /* PerStdSensorFrc.SensorType */ ) 725 { 726 default: 727 goto DpaHandleReturnFALSE; 728 729 // No type specified, use specified index value 730 case 0x00: 731 goto _KeepSensorIndex; 732 733 // For other types make the index value based on the requested index value and sensor type 734 case STD_SENSOR_TYPE_TEMPERATURE: 735 if ( sensorIndex > 0 ) 736 goto DpaHandleReturnFALSE; 737 W = 0 + sensorIndex; 738 break; 739 740 case STD_SENSOR_TYPE_BINARYDATA7: 741 if ( sensorIndex > 1 ) 742 goto DpaHandleReturnFALSE; 743 W = 1 + sensorIndex; 744 break; 745 } 746 747 // New sensor index based on type and requested index 748 sensorIndex = W; 749 _KeepSensorIndex: 750 751 // Test for supported FRC commands 752 switch ( _PCMD ) 753 { 754 default: 755 goto DpaHandleReturnFALSE; 756 757 case FRC_STD_SENSORS_BIT: 758 case FRC_STD_SENSORS_1B: 759 case FRC_STD_SENSORS_2B: 760 switch ( sensorIndex ) 761 { 762 default: 763 goto DpaHandleReturnFALSE; 764 765 case 0: 766 Carry = Get0_Temperature(); 767 break; 768 769 case 1: 770 Carry = Get1_BinaryData_Light(); 771 break; 772 773 case 2: 774 Carry = Get2_BinaryData_Potentiometer(); 775 break; 776 } 777 778 // This type of FRC is not valid for the specified sensor 779 if ( !Carry ) 780 goto DpaHandleReturnFALSE; 781 782 break; 783 } 784 785 // Some sensor was measured by FRC, check if there is a sleep request 786 FSR1++; 787 if ( INDF1.0 ) // Note: same as PerStdSensorFrc.Options.0 788 { 789 // Remember sleep parameters to go to sleep at the Idle event later 790 PerOSSleep_Request.Time.low8 = FSR1[offsetof( TPerOSSleep_Request, Time ) + 0 + offsetof( TPerStdSensorFrc, SleepParameters ) - 3]; 791 PerOSSleep_Request.Time.high8 = FSR1[offsetof( TPerOSSleep_Request, Time ) + 1 + offsetof( TPerStdSensorFrc, SleepParameters ) - 3]; 792 PerOSSleep_Request.Control = FSR1[offsetof( TPerOSSleep_Request, Control ) + offsetof( TPerStdSensorFrc, SleepParameters ) - 3]; 793 } 794 } 795 796 break; 797 798 // ------------------------------------------------- 799 case DpaEvent_FrcResponseTime: 800 // Called to get FRC response time 801 802 // In this example the FRC commands are fast 803 switch ( DataOutBeforeResponseFRC[0] ) 804 { 805 case FRC_STD_SENSORS_BIT: 806 case FRC_STD_SENSORS_1B: 807 case FRC_STD_SENSORS_2B: 808 responseFRCvalue = _FRC_RESPONSE_TIME_40_MS; 809 break; 810 } 811 break; 812 } 813 DpaHandleReturnFALSE: 814 return FALSE; 815 } 816 817 //############################################################################################ 818 bit returnTRUE() 819 //############################################################################################ 820 { 821 return TRUE; 822 } 823 824 //############################################################################################ 825 bit returnFALSE() 826 //############################################################################################ 827 { 828 return FALSE; 829 } 830 831 //############################################################################################ 832 // Increases FSR1 and then stores the byte 833 void setPlusPlusINDF1( uns8 data @ W ) 834 //############################################################################################ 835 { 836 FSR1++; // Note: must not modify W 837 setINDF1( data ); 838 } 839 840 //############################################################################################ 841 // Stores measured sensor value byte(s) and optionally sensor type to the FSR[+1...] 842 void StoreValue( uns8 sensorType ) 843 //############################################################################################ 844 { 845 // Is the sensor type to be stored too? 846 if ( _PCMD == PCMD_STD_SENSORS_READ_TYPES_AND_VALUES ) 847 setPlusPlusINDF1( sensorType ); 848 849 // Store lower value byte 850 setPlusPlusINDF1( sensorValue.low8 ); 851 852 // No more value bytes to store? 853 if ( sensorType.7 != 0 ) 854 return; 855 856 // Store higher value byte 857 setPlusPlusINDF1( sensorValue.high8 ); 858 859 // Note: this example implements sensors returning only 1 or 2 bytes of data. If another data widths are returned, then it must be implemented explicitly. 860 } 861 862 //############################################################################################ 863 bit setFRCerror() 864 //############################################################################################ 865 { 866 responseFRCvalue2B = FRC_STD_FRC_ERROR_VALUE; 867 return returnTRUE(); 868 } 869 870 //############################################################################################ 871 bit sensorError; 872 bit AdjustFrcTemperature() 873 //############################################################################################ 874 { 875 // Test for supported FRC commands 876 switch ( _PCMD ) 877 { 878 default: 879 return returnFALSE(); 880 881 case FRC_STD_SENSORS_1B: 882 // Return sensor FRC value 1B 883 // Check for out of limits 884 if ( sensorError || (int16)sensorValue > (int16)( 105.5 * 16 ) || (int16)sensorValue < ( (int16)-20 * 16 ) ) 885 return setFRCerror(); 886 887 // Convert to the "F = ( T + 22 ) * 2 " from 1/16 resolution 888 responseFRCvalue2B = sensorValue + 4; // Note: do rounding when /8 889 responseFRCvalue2B /= 8; 890 responseFRCvalue += 44; 891 break; 892 893 case FRC_STD_SENSORS_2B: 894 // Return sensor FRC value 2B 895 if ( sensorError ) 896 return setFRCerror(); 897 898 responseFRCvalue2B = sensorValue ^ 0x8000; 899 break; 900 } 901 902 return returnTRUE(); 903 } 904 905 //############################################################################################ 906 // Sensor index 1: measure temperature using one of the DDC-SE-01 sensors 907 bit Get0_Temperature() 908 //############################################################################################ 909 { 910 // Make sure FSR1 is not modified 911 912 // Measure temperature using DDC-SE-01 sensors 913 // Read temperature and check for an error 914 915 // Reads temperature from an enabled sensor 916 #ifdef DALLASnotMCP 917 sensorError = FALSE; 918 // Temperature is ready at the background 919 sensorValue = temperature; 920 // When error, return standard (FRC) error value(s) 921 if ( STD_SENSOR_TYPE_TEMPERATURE_IS_ERROR( sensorValue ) ) 922 sensorError = TRUE; 923 #else 924 sensorError = TRUE; 925 // Temperature value must be read from I2C sensor 926 STD_SENSOR_TYPE_TEMPERATURE_SET_ERROR( sensorValue ); 927 // MCP9802 address 928 _DpaApiI2Cstart( I2C_ADR ); 929 if ( !I2CwasTimeout ) 930 { 931 // pointer: 1 = configuration register 932 _DpaApiI2Cwrite( 0x01 ); 933 // configuration: 9-bit ADC 934 _DpaApiI2CwriteAndStop( 0x00 ); 935 936 // MCP9802 address 937 _DpaApiI2Cstart( I2C_ADR ); 938 // pointer: 0 = temperature 939 _DpaApiI2CwriteAndStop( 0 ); 940 941 // MCP9802 address + read 942 _DpaApiI2Cstart( I2C_ADR | 1 ); 943 // store the result 944 sensorValue.high8 = _DpaApiI2Cread( TRUE ); 945 sensorValue.low8 = _DpaApiI2Cread( FALSE ); 946 _DpaApiI2Cstop(); 947 948 sensorValue += 0x10 / 2; 949 sensorValue /= 0x10; 950 951 sensorError = FALSE; 952 } 953 #endif 954 955 // FrcValues 956 return AdjustFrcTemperature(); 957 } 958 959 //############################################################################################ 960 bit Get_BinaryData_Final( uns8 _ADCcfg ) 961 //############################################################################################ 962 { 963 #if defined( TR7xG ) 964 setADPCH( _ADCcfg ); 965 #else 966 ADCON0 = _ADCcfg; 967 #endif 968 969 // Read ADC 970 971 #if defined( TR7xG ) 972 // ADC is enabled, ADGO is cleared upon completion, Clock supplied according to ADCLK register, left-justified 973 ADCON0 = 0b1000.0000; 974 // ADC Conversion Clock = FOSC/8 975 ADCLK = 8 / 2 - 1; 976 #else 977 // ADC result - left justified, Fosc/8 978 ADCON1 = 0b0001.0000; 979 #endif 980 981 // Short delay to stabilize 982 updateCRC16( W ); 983 984 // start ADC 985 _GO = 1; 986 // wait for ADC finish 987 while ( _GO ); 988 // Get ADC value 989 sensorValue.low8 = ADRESH / 2; 990 991 // Return sensor FRC value 992 993 // Test for supported FRC commands 994 switch ( _PCMD ) 995 { 996 default: 997 return returnFALSE(); 998 999 case FRC_STD_SENSORS_BIT: 1000 // If there is a sensor error, 2-bit FRC cannot indicate it, it returns [01] 1001 1002 // Number of shifts to get the bit out of the return value 1003 uns8 bitLoop = ( INDF1 >> 5 ) + 1; 1004 // Value to get the bit from 1005 W = sensorValue.low8; 1006 do 1007 { 1008 // Get the bit to Carry 1009 W = rr( W ); 1010 // Next bit 1011 } while ( --bitLoop != 0 ); // Note: must not modify W and Carry 1012 // Carry = bit = x 1013 // Current (prepared by DPA) FRC value is [01], change it to [1x] 1014 responseFRCvalue = rl( responseFRCvalue ); 1015 break; 1016 1017 case FRC_STD_SENSORS_1B: 1018 responseFRCvalue = sensorValue.low8 + 4; 1019 break; 1020 } 1021 1022 return returnTRUE(); 1023 } 1024 1025 #if defined( TR7xG ) 1026 //############################################################################################ 1027 // Might not be needed if ADC registers are kept default 1028 void resetADCregs() 1029 //############################################################################################ 1030 { 1031 // Start reseting ADC registers from ADCON0 to ADPCH 1032 FSR0 = &ADCON0; 1033 do 1034 { 1035 setINDF0( 0 ); 1036 FSR0++; 1037 // Stop reseting at 1st GPR register, ADPCH is the last implemented register before GPR 1038 } while ( !FSR0L.5 ); 1039 } 1040 #endif 1041 1042 //############################################################################################ 1043 // Sensor index 1: returns light intensity indicator value using DDC-SE-01 1044 bit Get1_BinaryData_Light() 1045 //############################################################################################ 1046 { 1047 // Make sure FSR1 is not modified 1048 1049 // ADC initialization (for more info see PIC datasheet) pin C1 (AN0-D/ANA0-G) as analog input 1050 ANSELA.0 = 1; 1051 #if defined( TR7xG ) 1052 // Enable ADC 1053 ADCMD = 0; 1054 // Resets all ADC registers 1055 // VREF- is connected to AVSS, VREF+ is connected to VDD 1056 resetADCregs(); 1057 // ADC Positive Input Channel = ANA0 1058 return Get_BinaryData_Final( 0x00 ); 1059 #else 1060 // ADC setting (AN0 channel) 1061 return Get_BinaryData_Final( 0b0.00000.01 ); 1062 #endif 1063 } 1064 1065 //############################################################################################ 1066 // Sensor index 2: returns potentiometer value using DDC-SE-01 1067 bit Get2_BinaryData_Potentiometer() 1068 //############################################################################################ 1069 { 1070 // Make sure FSR1 is not modified 1071 1072 // ADC initialization (for more info see PIC datasheet) pin C5 (AN4-D/ANA5-G) as analog input 1073 ANSELA.5 = 1; 1074 #if defined( TR7xG ) 1075 // Enable ADC 1076 ADCMD = 0; 1077 // Resets all ADC registers 1078 // VREF- is connected to AVSS, VREF+ is connected to VDD 1079 resetADCregs(); 1080 // ADC Positive Input Channel = ANA5 1081 return Get_BinaryData_Final( 0x05 ); 1082 #else 1083 // ADC setting (AN4 channel) 1084 return Get_BinaryData_Final( 0b0.00100.01 ); 1085 #endif 1086 } 1087 1088 #ifdef DALLASnotMCP 1089 //############################################################################################ 1090 // OneWire and Dallas 18B20 routines 1091 //############################################################################################ 1092 1093 //############################################################################################ 1094 void Delay5us( uns8 val @ W ) // Absolutely precise timing but val != 0 1095 //############################################################################################ 1096 { 1097 // 16 MHz 1098 // + 0.75us ( W=val, Call ) 1099 for ( ;; ) 1100 { // loop time 1101 nop2(); // 0.50us 1102 nop2(); // 1.00us 1103 nop2(); // 1.50us 1104 nop2(); // 2.00us 1105 nop2(); // 2.50us 1106 nop2(); // 3.00us 1107 nop(); // 3.25us 1108 if ( --val == 0 ) // + 0.75us (W--, BTFS ) 1109 return; // + 0.25us 1110 nop2(); // 4.50us 1111 } // 5.00us (Goto) 1112 } 1113 //############################################################################################ 1114 1115 #define OneWireData0() { OneWire_TRIS = 0; } // 0.5us @ 16MHz 1116 #define OneWireData1() { OneWire_TRIS = 1; } // 0.5us @ 16MHz 1117 1118 //############################################################################################ 1119 void OneWireWriteByte( uns8 byte ) 1120 //############################################################################################ 1121 { 1122 uns8 bitLoop = 8; 1123 do 1124 { 1125 // Next sequence is time precision critical 1126 GIE = FALSE; 1127 1128 OneWireData0(); 1129 nop2(); // 1 us [0.5 us] 1130 nop2(); // [1.0 us] 1131 if ( byte.0 ) // 2.5 us [1.75us] 1132 OneWireData1(); 1133 1134 // End of time precision critical sequence 1135 GIE = TRUE; 1136 1137 // 60us minimum in total, does not have to be precise 1138 Delay5us( ( 60 - 3 ) / 5 + 1 ); 1139 1140 OneWireData1(); 1141 1142 byte >>= 1; 1143 } while ( --bitLoop != 0 ); 1144 } 1145 1146 //############################################################################################ 1147 uns8 OneWireReadByte() 1148 //############################################################################################ 1149 { 1150 uns8 result; 1151 uns8 bitLoop = 8; 1152 do 1153 { 1154 // Next sequence is time precision critical 1155 GIE = FALSE; 1156 1157 OneWireData0(); 1158 nop2(); // 1 us [0.5 us] 1159 nop2(); // [1.0 us] 1160 OneWireData1(); // 2 us [1.5 us] 1161 Delay5us( 15 / 5 ); // 17 us [16.5 us] 1162 1163 Carry = 0; // 17.5 us [16.75 us] 1164 if ( OneWire_IO_IN ) // 18.5 us [ 17.25 us] (condition must not modify Carry) 1165 Carry = 1; 1166 1167 // End of time precision critical sequence 1168 GIE = TRUE; // must not modify Carry 1169 result = rr( result ); 1170 1171 // 60us minimum in total, does not have to be precise 1172 Delay5us( ( 60 - 20 ) / 5 + 1 ); 1173 } while ( --bitLoop != 0 ); 1174 1175 return result; 1176 } 1177 1178 //############################################################################################ 1179 bit OneWireReset() 1180 //############################################################################################ 1181 { 1182 // Setting the pin once to low is enough 1183 OneWire_IO_OUT = 0; 1184 // Reset pulse 1185 OneWireData0(); 1186 Delay5us( 500 / 5 ); 1187 // Reset pulse end 1188 OneWireData1(); 1189 // Next sequence is time precision critical 1190 GIE = FALSE; 1191 // Wait for presence pulse 1192 Delay5us( 70 / 5 ); 1193 // End of time precision critical sequence 1194 GIE = TRUE; 1195 // Presence pulse? 1196 if ( OneWire_IO_IN ) 1197 { 1198 // No presence, finish initialization sequence 1199 Delay5us( ( 500 - 70 ) / 5 ); 1200 return returnFALSE(); 1201 } 1202 else 1203 { 1204 // Presence OK, finish initialization sequence 1205 Delay5us( ( 500 - 70 ) / 5 ); 1206 return returnTRUE(); 1207 } 1208 } 1209 1210 //############################################################################################ 1211 void OneWireCmd( uns8 cmd ) 1212 //############################################################################################ 1213 { 1214 // OneWire: Skip ROM 1215 OneWireWriteByte( CMD_SKIPROM ); 1216 // OneWire: Send command 1217 OneWireWriteByte( cmd ); 1218 } 1219 1220 //############################################################################################ 1221 bit Ds18B20WriteConfig( uns8 value ) 1222 //############################################################################################ 1223 { 1224 if ( OneWireReset() ) 1225 { 1226 // Write Scratchpad 1227 OneWireCmd( CMD_WSCRATCHPAD ); 1228 1229 // Write TL = ? (we dot not care the value) 1230 OneWireWriteByte( W ); 1231 // Write TH = ? (we dot not care the value) 1232 OneWireWriteByte( W ); 1233 // Write Config byte 1234 OneWireWriteByte( value ); 1235 1236 if ( OneWireReset() ) 1237 { 1238 // Copy Scratchpad 1239 OneWireCmd( CMD_CPYSCRATCHPAD ); 1240 return returnTRUE(); 1241 } 1242 } 1243 return returnFALSE(); 1244 } 1245 1246 #endif // DALLASnotMCP 1247 //############################################################################################ 1248 // Other routines 1249 //############################################################################################ 1250 1251 //############################################################################################ 1252 void SetOutput( uns8 state, uns8 index @ W ) 1253 //############################################################################################ 1254 { 1255 // Note: FSRs must not be modified 1256 // Note: This method is called in the interrupt too! 1257 1258 skip( index ); 1259 #pragma computedGoto 1 1260 goto set0; 1261 goto set1; 1262 #pragma computedGoto 0 1263 ; 1264 // -------------------------------------- 1265 set1: 1266 if ( !state.0 ) 1267 RELAY2_LAT = 0; 1268 else 1269 RELAY2_LAT = 1; 1270 1271 return; 1272 // -------------------------------------- 1273 set0: 1274 if ( !state.0 ) 1275 RELAY1_LAT = 0; 1276 else 1277 RELAY1_LAT = 1; 1278 1279 return; 1280 // -------------------------------------- 1281 } 1282 1283 //############################################################################################ 1284 bit GetOutput( uns8 index @ W ) 1285 //############################################################################################ 1286 { 1287 Carry = FALSE; // Note: must not modify W 1288 1289 // Note: all below must not modify Carry except when needed 1290 skip( index ); 1291 #pragma computedGoto 1 1292 goto get0; 1293 goto get1; 1294 #pragma computedGoto 0 1295 ; 1296 // -------------------------------------- 1297 get1: 1298 if ( RELAY2_LAT ) 1299 Carry = TRUE; 1300 goto _return; 1301 // -------------------------------------- 1302 get0: 1303 if ( RELAY1_LAT ) 1304 Carry = TRUE; 1305 goto _return; 1306 // -------------------------------------- 1307 1308 _return: 1309 return Carry; 1310 } 1311 1312 //############################################################################################ 1313 // 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) 1314 #include "DPAcustomHandler.h" 1315 //############################################################################################