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