1 // ********************************************************************************************** 2 // Custom DPA Handler code example - Standard Sensors + Binary output - DDC-SE-01 + DDC-RE-01 * 3 // ********************************************************************************************** 4 // Copyright (c) IQRF Tech s.r.o. 5 // 6 // File: $RCSfile: 0402_DDC-SE+RE.c,v $ 7 // Version: $Revision: 1.30 $ 8 // Date: $Date: 2020/03/20 13:25:58 $ 9 // 10 // Revision history: 11 // 2020/01/02 Release for DPA 4.11 12 // 2017/11/16 Release for DPA 3.02 13 // 2017/08/14 Release for DPA 3.01 14 // 15 // ********************************************************************* 16 17 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/ 18 // IQRF Standards documentation https://www.iqrfalliance.org/iqrf-interoperability/ 19 20 // This example implements 3 sensors according to the IQRF Sensors standard 21 // 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. 22 // Index 1 i.e. 2nd sensor is light intensity indicator at DDC-SE-01 board (value range is 0[max light]-127[max dark]). 23 // Index 2 i.e. 3rd sensor is potentiometer value at DDC-SE-01 board (value range is 0[left stop]-127[right stop]). 24 25 // This example also implements 4 binary outputs according to the IQRF Binary Outputs standard 26 // Index 0 i.e. 1st output is Relay #1 @ DDC-RE-01 27 // Index 1 i.e. 2nd output is Relay #2 @ DDC-RE-01 28 // Index 2 i.e. 3rd output is Red LED 29 // Index 3 i.e. 4th output is Green LED 30 31 // This example must be compiled without a "-bu" compiler switch in order to fit into available Flash memory 32 33 // Default IQRF include (modify the path according to your setup) 34 #include "IQRF.h" 35 36 // We can save more instructions if needed by the symbol below 37 // #define PARAM_CHECK_LEVEL 1 38 39 // Default DPA header (modify the path according to your setup) 40 #include "DPA.h" 41 // Default Custom DPA Handler header (modify the path according to your setup) 42 #include "DPAcustomHandler.h" 43 // IQRF standards header (modify the path according to your setup) 44 #include "IQRFstandard.h" 45 #include "IQRF_HWPID.h" 46 // I2C Master library 47 #include "lib/I2Cmaster.c" 48 49 // If defined then the handler is compiled for Dallas otherwise for MCP9802 50 #define DALLASnotMCP 51 52 //############################################################################################ 53 54 // Define useful macro that saves some code but not preset at DPA < 3.01 55 #if DPA_VERSION_MASTER < 0x0301 56 // Optimized macro for both testing enumeration peripherals ELSE peripherals information. See examples 57 #define IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequestNoSize() if ( _PCMD == CMD_GET_PER_INFO ) if ( _PNUM == PNUM_ENUMERATION ) 58 59 #if PARAM_CHECK_LEVEL >= 2 60 #define IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest() if ( _DpaDataLength == 0 && _PCMD == CMD_GET_PER_INFO ) if ( _PNUM == PNUM_ENUMERATION ) 61 #else 62 #define IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest() IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequestNoSize() 63 #endif 64 #endif 65 66 //############################################################################################ 67 68 // Number of implemented sensors 69 #define SENSORS_COUNT 3 70 71 // Variable to store sensor value at Get?_????() methods. This example implements sensors returning maximum 2 bytes of data. 72 uns16 sensorValue @ param3; 73 74 // Reads sensor value to the sensorValue variable and to responseFRCvalue(2B) variable 75 bit Get0_Temperature(); 76 bit Get1_BinaryData_Light(); 77 bit Get2_BinaryData_Potentiometer(); 78 79 // 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) 80 void StoreValue( uns8 sensorType ); 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 4 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 uns8 timeout; 189 #endif 190 191 // Timers for outputs. The space must be long enough to fit them all. 2 bytes per one binary output. 192 static uns16 Timers[OUTPUTS_COUNT]; 193 194 // Detect DPA event to handle 195 switch ( GetDpaEvent() ) 196 { 197 // ------------------------------------------------- 198 case DpaEvent_Interrupt: 199 // Do an extra quick background interrupt work 200 // ! 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. 201 // ! 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. 202 // ! 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. 203 // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy. 204 // ! Make sure race condition does not occur when accessing those variables at other places. 205 // ! 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. 206 // ! Do not call any OS functions except setINDFx(). 207 // ! Do not use any OS variables especially for writing access. 208 // ! 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. 209 210 // ms per TMR6 interrupt 211 #define TICKS_LEN 10 212 213 // If TMR6 interrupt occurred 214 if ( TMR6IF ) 215 { 216 // Unmask interrupt 217 TMR6IF = 0; 218 #ifdef DALLASnotMCP 219 // Decrement count 220 if ( timeout != 0 ) 221 timeout--; 222 #endif 223 224 // Count 250 ms from 10 ms micro ticks 225 static uns8 count250ms; 226 if ( ++count250ms == ( 250 / 10 ) ) 227 { 228 // 250 ms 229 count250ms = 0; 230 231 // Pointer to the timers array 232 FSR1 = (uns16)&Timers[0]; 233 // Output index 234 static uns8 index; 235 index = 0; 236 do 237 { 238 // Is timer running (is non-zero)? 239 if ( ( FSR1[1] | INDF1 ) != 0 ) 240 { 241 // Get time 242 static uns16 time; 243 time.low8 = *FSR1++; 244 time.high8 = *FSR1; 245 // Is timer over? 246 if ( --time == 0 ) 247 // Set output to OFF 248 SetOutput( 0, index ); 249 250 // Store new time 251 setINDF1( time.high8 ); 252 FSR1--; 253 setINDF1( time.low8 ); 254 } 255 // Next timer 256 FSR1 += sizeof( Timers[0] ); 257 // Next index 258 } while ( ++index < OUTPUTS_COUNT ); 259 } 260 } 261 return Carry; 262 263 // ------------------------------------------------- 264 case DpaEvent_Idle: 265 // Do a quick background work when RF packet is not received 266 267 // Should go to sleep? 268 if ( PerOSSleep_Request.Time != 0 ) 269 { 270 // Copy sleep parameters to the DPA request 271 _DpaMessage.PerOSSleep_Request.Time = PerOSSleep_Request.Time; 272 _DpaMessage.PerOSSleep_Request.Control = PerOSSleep_Request.Control; 273 // Finalize OS Sleep DPA Request 274 _DpaDataLength = sizeof( _DpaMessage.PerOSSleep_Request ); 275 _PNUM = PNUM_OS; 276 _PCMD = CMD_OS_SLEEP; 277 // Perform local DPA Request to go to sleep 278 DpaApiLocalRequest(); 279 // Switch off sleeping time=flag 280 PerOSSleep_Request.Time = 0; 281 } 282 283 #ifdef DALLASnotMCP 284 // Run finite state machine to read temperature from DS18B20 at background so the temperature value is immediately ready for FRC 285 286 // Make sure 1Wire data pin at LATX.y is low as it might be set by another PORTX.? pin manipulation 287 OneWire_IO_OUT = 0; 288 289 skip( state ); 290 #pragma computedGoto 1 291 goto _S_ResetConvertT; 292 goto _S_SkipRomConvertT; 293 goto _S_CmdConvertT; 294 goto _S_WaitConvertT; 295 goto _S_ResetReadTemp; 296 goto _S_SkipRomReadTemp; 297 goto _S_CmdReadTemp; 298 goto _S_Byte1ReadTemp; 299 goto _S_Byte2ReadTemp; 300 #pragma computedGoto 0 301 ; 302 // -------------- 303 _S_Byte2ReadTemp: 304 temperature.high8 = OneWireReadByte(); 305 temperature.low8 = temperatureByteLow; 306 307 ResetMachine: 308 state = S_ResetConvertT; 309 goto ExitMachine; 310 311 // -------------- 312 _S_ResetConvertT: 313 _S_ResetReadTemp: 314 if ( !OneWireReset() ) 315 { 316 _S_Error_Reset: 317 STD_SENSOR_TYPE_TEMPERATURE_SET_ERROR( temperature ); 318 goto ResetMachine; 319 } 320 goto NextState; 321 322 // -------------- 323 _S_SkipRomConvertT: 324 _S_SkipRomReadTemp: 325 // OneWire: Skip ROM 326 OneWireWriteByte( CMD_SKIPROM ); 327 goto NextState; 328 329 // -------------- 330 _S_CmdConvertT: 331 // OneWire: Convert temperature 332 OneWireWriteByte( CMD_CONVERTTEMP ); 333 // Setup timeout for approx 750 ms (the longest conversion time) 334 timeout = 2 + 750 / TICKS_LEN; 335 goto NextState; 336 337 // -------------- 338 _S_WaitConvertT: 339 if ( OneWireReadByte() == 0xff ) 340 goto NextState; 341 342 // Timeout? 343 if ( timeout == 0 ) 344 goto _S_Error_Reset; 345 346 goto ExitMachine; 347 348 // -------------- 349 _S_CmdReadTemp: 350 // OneWire: Read scratchpad 351 OneWireWriteByte( CMD_RSCRATCHPAD ); 352 goto NextState; 353 354 // -------------- 355 _S_Byte1ReadTemp: 356 temperatureByteLow = OneWireReadByte(); 357 goto NextState; 358 359 // -------------- 360 NextState: 361 ++state; 362 363 ExitMachine: 364 #endif 365 break; 366 367 // ------------------------------------------------- 368 case DpaEvent_Init: 369 // Do a one time initialization before main loop starts 370 371 // Initialize relays @ DDC-RE 372 RELAY1_LAT = 0; 373 RELAY2_LAT = 0; 374 RELAY1_TRIS = 0; 375 RELAY2_TRIS = 0; 376 377 // Initialize sensors 378 // C5 (AN4) as input 379 moduleInfo(); 380 // Connected TR pins? 381 if ( !bufferINFO[5].7 ) 382 { 383 TRISC.6 = 1; 384 TRISB.4 = 1; 385 } 386 TRISA.5 = 1; 387 388 // C1 (AN0) as input 389 TRISA.0 = 1; 390 391 // Setup TMR6 to generate ticks on the background (ticks every 10ms) 392 #if F_OSC == 16000000 393 PR6 = 250 - 1; 394 T6CON = 0b0.1001.1.10; // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms 395 #else 396 #error Unsupported oscillator frequency 397 #endif 398 399 #ifdef DALLASnotMCP 400 // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet) 401 Ds18B20WriteConfig( 0b0.00.00000 ); 402 #else 403 // Expect MCP9802 is enabled 404 i2c_init(); 405 #endif 406 break; 407 408 // ------------------------------------------------- 409 case DpaEvent_AfterSleep: 410 // Called after woken up after sleep 411 #ifndef DALLASnotMCP 412 i2c_init(); 413 #endif 414 415 // Called on wake-up from sleep 416 TMR6IE = TRUE; 417 TMR6ON = TRUE; 418 break; 419 420 // ------------------------------------------------- 421 case DpaEvent_BeforeSleep: 422 // Called before going to sleep 423 #ifndef DALLASnotMCP 424 i2c_shutdown(); 425 #endif 426 427 // ------------------------------------------------- 428 case DpaEvent_DisableInterrupts: 429 // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM) 430 // Must not use TMR6 any more 431 TMR6ON = FALSE; 432 TMR6IE = FALSE; 433 break; 434 435 // ------------------------------------------------- 436 case DpaEvent_DpaRequest: 437 // Called to interpret DPA request for peripherals 438 // ------------------------------------------------- 439 // Peripheral enumeration 440 IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest() 441 { 442 // We implement 2 standard peripherals 443 _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 2; 444 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_SENSORS ); 445 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_BINARY_OUTPUTS ); 446 _DpaMessage.EnumPeripheralsAnswer.HWPID = HWPID_IQRF_TECH__DEMO_DDC_SE01_RE01; 447 _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x0002; 448 449 DpaHandleReturnTRUE: 450 return TRUE; 451 } 452 // ------------------------------------------------- 453 // Get information about peripherals 454 else 455 { 456 #if PERIPHERAL_TYPE_STD_SENSORS != PNUM_STD_SENSORS || PERIPHERAL_TYPE_STD_BINARY_OUTPUTS != PNUM_STD_BINARY_OUTPUTS 457 #error 458 #endif 459 switch ( _DpaMessage.PeripheralInfoAnswer.PerT = _PNUM ) 460 { 461 case PNUM_STD_SENSORS: 462 // Set standard version 463 W = STD_SENSORS_VERSION; 464 goto Par1toVersion; 465 466 case PNUM_STD_BINARY_OUTPUTS: 467 // Set standard version 468 W = STD_BINARY_OUTPUTS_VERSION; 469 Par1toVersion: 470 _DpaMessage.PeripheralInfoAnswer.Par1 = W; 471 _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE; 472 goto DpaHandleReturnTRUE; 473 } 474 475 break; 476 } 477 478 { 479 // ------------------------------------------------- 480 // Handle peripheral command 481 482 // Supported peripheral number? 483 switch ( _PNUM ) 484 { 485 case PNUM_STD_SENSORS: 486 { 487 // Supported commands? 488 switch ( _PCMD ) 489 { 490 // Invalid command 491 default: 492 { 493 // Return error 494 _ERROR_PCMD: 495 W = ERROR_PCMD; 496 _ERROR_W: 497 DpaApiReturnPeripheralError( W ); 498 } 499 500 // Sensor enumeration 501 case PCMD_STD_ENUMERATE: 502 if ( _DpaDataLength != 0 ) 503 goto _ERROR_DATA_LEN; 504 505 _DpaMessage.Response.PData[0] = STD_SENSOR_TYPE_TEMPERATURE; 506 _DpaMessage.Response.PData[1] = STD_SENSOR_TYPE_BINARYDATA7; 507 _DpaMessage.Response.PData[2] = STD_SENSOR_TYPE_BINARYDATA7; 508 W = SENSORS_COUNT; 509 goto _W2_DpaDataLength; 510 511 // Supported commands. They are handled the same way except one "if" at StoreValue() method 512 case PCMD_STD_SENSORS_READ_VALUES: 513 case PCMD_STD_SENSORS_READ_TYPES_AND_VALUES: 514 { 515 // No sensor bitmap specified? W = _DpaDataLength. Note: W is used to avoid MOVLB at next if 516 W = _DpaDataLength; 517 if ( W == 0 ) // Note: must not modify W 518 { 519 // Actually clears the bitmap 520 #if &_DpaMessage.Request.PData[0] != &bufferRF[0] 521 #error 522 #endif 523 clearBufferRF(); 524 // Simulate 1st only sensor in the bitmap (states of the other unimplemented sensors do not care) 525 _DpaMessage.Request.PData[0].0 = 1; 526 // Bitmap is 32 bits long = 4 527 _DpaDataLength = W = 4; 528 } 529 530 // Invalid bitmap (data) length (W = _DpaDataLength)? 531 if ( W != 4 ) 532 goto _ERROR_DATA_LEN; 533 534 // Now read the sensors 535 536 // Prepare pointer (minus 1, see below) to store sensor (types and) values to 537 // Note: 4 sensors at this example cannot return more than DPA_MAX_DATA_LENGTH bytes of data, so it does not have to be checked... 538 // ... If it would be the case, then ERROR_FAIL must be returned 539 FSR1 = &_DpaMessage.Response.PData[-1]; 540 541 // Store bitmap of sensors to get values from 542 uns8 sensorsBitmap = FSR1[1]; 543 544 // 1st sensor (index 0) selected? 545 if ( sensorsBitmap.0 ) 546 { 547 Get0_Temperature(); 548 StoreValue( STD_SENSOR_TYPE_TEMPERATURE ); 549 } 550 551 // 2nd sensor (index 1) selected? 552 if ( sensorsBitmap.1 ) 553 { 554 Get1_BinaryData_Light(); 555 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 ); 556 } 557 558 // 3rd sensor (index 2) selected? 559 if ( sensorsBitmap.2 ) 560 { 561 Get2_BinaryData_Potentiometer(); 562 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 ); 563 } 564 565 // Compute returned data bytes count 566 W = FSR1L - ( (uns16)&_DpaMessage.Response.PData[0] & 0xFF ) + 1; 567 // Optimization: return W long block of bytes at response 568 _W2_DpaDataLength: 569 _DpaDataLength = W; 570 goto DpaHandleReturnTRUE; 571 } 572 } 573 } 574 575 case PNUM_STD_BINARY_OUTPUTS: 576 { 577 // Supported commands? 578 switch ( _PCMD ) 579 { 580 // Invalid command 581 default: 582 // Return error 583 goto _ERROR_PCMD; 584 585 // Outputs enumeration 586 case PCMD_STD_ENUMERATE: 587 if ( _DpaDataLength != 0 ) 588 goto _ERROR_DATA_LEN; 589 590 // Return number of outputs 591 _DpaMessage.Response.PData[0] = OUTPUTS_COUNT; 592 W = 1; 593 goto _W2_DpaDataLength; 594 595 // Supported commands. 596 case PCMD_STD_BINARY_OUTPUTS_SET: 597 { 598 // Pointers FSR01 to data are already set at the DPA 599 600 // As this template implements < 9 outputs the working bitmap is uns8, if more outputs are implemented then uns16, ..., uns32 must be used 601 #if OUTPUTS_COUNT < 9 602 uns8 inBitmap = *FSR0--; 603 uns8 outBitmap @ _DpaMessage.Response.PData[0]; 604 uns8 bitmapMask = 0b1; 605 #else 606 #error Not implemented 607 #endif 608 609 // Number of selected outputs + bitmap length 610 uns8 outputsCount = 4; 611 // Loop bitmap 612 uns8 index = 4; 613 do 614 { 615 // Count bits of next byte 616 uns8 byte = *++FSR0; 617 if ( byte != 0 ) 618 { 619 // Brian Kernighan's Algorithm for counting set bits 620 do 621 { 622 outputsCount++; 623 byte &= byte - 1; 624 } while ( byte != 0 ); 625 } 626 627 // Reset bitmap 628 setINDF0( 0 ); 629 } while ( --index != 0 ); 630 631 // Check data length 632 if ( _DpaDataLength != outputsCount ) 633 { 634 _ERROR_DATA_LEN: 635 W = ERROR_DATA_LEN; 636 goto _ERROR_W; 637 } 638 639 // Pointer to the timers array 640 FSR1 = (uns16)&Timers[0]; 641 // Output index 642 index = 0; 643 do 644 { 645 // Output was set? 646 if ( GetOutput( index ) ) 647 // Yes, set in the output bitmap 648 outBitmap |= bitmapMask; 649 650 // Implemented output selected? Set the state. 651 if ( inBitmap.0 ) 652 { 653 // Default is timer off 654 uns16 time = 0; 655 // Desired state 656 uns8 state = *++FSR0; 657 if ( state > 1 ) 658 { 659 // Get time in units s/min 660 time = state & 0x7F; 661 if ( time == 0 ) 662 { 663 // Invalid time 664 W = ERROR_FAIL; 665 goto _ERROR_W; 666 } 667 668 // Conversion coefficient, ready for minutes to 250 ms 669 uns8 coef = 60 * ( 1000 / 250 ); 670 if ( state.7 ) 671 // Convert from seconds 672 coef = 1000 / 250; 673 674 // Convert to 250 ms 675 time *= coef; 676 // Set ON 677 state = 1; 678 } 679 680 // Set output 681 SetOutput( state, index ); 682 683 // Set timer and preserve pointer 684 GIE = FALSE; 685 setINDF1( time.low8 ); 686 FSR1++; 687 setINDF1( time.high8 ); 688 FSR1--; 689 GIE = TRUE; 690 } 691 692 // Next pointer to the timer 693 FSR1 += sizeof( Timers[0] ); 694 // Next bits 695 bitmapMask <<= 1; 696 inBitmap >>= 1; 697 // Next index 698 } while ( ++index < OUTPUTS_COUNT ); 699 700 // Return bitmap 701 _DpaDataLength4: 702 W = 4; 703 goto _W2_DpaDataLength; 704 } 705 } 706 } 707 } 708 709 break; 710 } 711 712 // ------------------------------------------------- 713 case DpaEvent_FrcValue: 714 // Called to get FRC value 715 716 // FSR1 for optimization purposes (avoid MOVLB) will be used to point to DataOutBeforeResponseFRC[0...] 717 FSR1 = &DataOutBeforeResponseFRC[0]; 718 // Check for correct FRC user data 719 if ( *FSR1++ /*DataOutBeforeResponseFRC[0]*/ == PNUM_STD_SENSORS ) 720 { 721 // Actually used sensor index 722 uns8 sensorIndex = FSR1[1] /*DataOutBeforeResponseFRC[2]*/ & 0x1f; 723 // Test sensor type 724 switch ( *FSR1++ /*DataOutBeforeResponseFRC[1]*/ ) 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 DataOutBeforeResponseFRC[3].0 788 { 789 // Remember sleep parameters to go to sleep at the Idle event later 790 PerOSSleep_Request.Time.low8 = FSR1[4 - 3]; // Note: same as DataOutBeforeResponseFRC[4]; 791 PerOSSleep_Request.Time.high8 = FSR1[5 - 3]; // Note: same as DataOutBeforeResponseFRC[5]; 792 PerOSSleep_Request.Control = FSR1[6 - 3]; // Note: same as DataOutBeforeResponseFRC[6]; 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 = 2; 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 i2c_start( I2C_ADR ); 929 if ( !i2cTimeout ) 930 { 931 // pointer: 1 = configuration register 932 i2c_write( 0x01 ); 933 // configuration: 9-bit ADC 934 i2c_write( 0x00 ); 935 i2c_stop(); 936 937 // MCP9802 address 938 i2c_start( I2C_ADR ); 939 // pointer: 0 = temperature 940 i2c_write( 0 ); 941 i2c_stop(); 942 943 // MCP9802 address + read 944 i2c_start( I2C_ADR | 1 ); 945 // store the result 946 sensorValue.high8 = i2c_read( TRUE ); 947 sensorValue.low8 = i2c_read( FALSE ); 948 i2c_stop(); 949 950 sensorValue += 0x10 / 2; 951 sensorValue /= 0x10; 952 953 sensorError = FALSE; 954 } 955 #endif 956 957 // FrcValues 958 return AdjustFrcTemperature(); 959 } 960 961 //############################################################################################ 962 // Sensor index 1: returns light intensity indicator value using DDC-SE-01 963 bit Get_BinaryData_Final( uns8 _ADCON0 @ W ) 964 //############################################################################################ 965 { 966 ADCON0 = _ADCON0; 967 // Read ADC 968 969 // ADC result - left justified, Fosc/8 970 ADCON1 = 0b0001.0000; 971 // Do a smallest delay for ADC ACQUISITION TIME 972 waitMS( 1 ); 973 // start ADC 974 GO = 1; 975 // wait for ADC finish 976 while ( GO ); 977 // Get ADC value 978 sensorValue.low8 = ADRESH / 2; 979 980 // Return sensor FRC value 981 982 // Test for supported FRC commands 983 switch ( _PCMD ) 984 { 985 default: 986 return returnFALSE(); 987 988 case FRC_STD_SENSORS_BIT: 989 // If there is a sensor error, 2-bit FRC cannot indicate it, it returns [01] 990 991 // Number of shifts to get the bit out of the return value 992 uns8 bitLoop = ( INDF1 >> 5 ) + 1; 993 // Value to get the bit from 994 W = sensorValue.low8; 995 do 996 { 997 // Get the bit to Carry 998 W = rr( W ); 999 // Next bit 1000 } while ( --bitLoop != 0 ); // Note: must not modify W and Carry 1001 // Current (prepared by DPA) FRC value is [01], change it to [11] (means bit is set) 1002 responseFRCvalue.1 = 1; // Note: must not modify Carry 1003 // Is bit set? 1004 if ( !Carry ) 1005 // Bit is NOT set, return [10] 1006 responseFRCvalue.0 = 0; 1007 break; 1008 1009 case FRC_STD_SENSORS_1B: 1010 responseFRCvalue = sensorValue.low8 + 4; 1011 break; 1012 } 1013 1014 return returnTRUE(); 1015 } 1016 1017 //############################################################################################ 1018 // Sensor index 1: returns light intensity indicator value using DDC-SE-01 1019 bit Get1_BinaryData_Light() 1020 //############################################################################################ 1021 { 1022 // Make sure FSR1 is not modified 1023 1024 // ADC initialization (for more info see PIC datasheet) pin C1 (AN0) as analog input 1025 ANSELA.0 = 1; 1026 // ADC setting (AN0 channel) 1027 return Get_BinaryData_Final( 0b0.00000.01 ); 1028 } 1029 1030 //############################################################################################ 1031 // Sensor index 2: returns potentiometer value using DDC-SE-01 1032 bit Get2_BinaryData_Potentiometer() 1033 //############################################################################################ 1034 { 1035 // Make sure FSR1 is not modified 1036 1037 // ADC initialization (for more info see PIC datasheet) pin C5 (AN4) as analog input 1038 ANSELA.5 = 1; 1039 // ADC setting (AN4 channel) 1040 return Get_BinaryData_Final( 0b0.00100.01 ); 1041 } 1042 1043 #ifdef DALLASnotMCP 1044 //############################################################################################ 1045 // OneWire and Dallas 18B20 routines 1046 //############################################################################################ 1047 1048 //############################################################################################ 1049 void Delay5us( uns8 val @ W ) // Absolutely precise timing but val != 0 1050 //############################################################################################ 1051 { 1052 // 16 MHz 1053 // + 0.75us ( W=val, Call ) 1054 for ( ;; ) 1055 { // loop time 1056 nop2(); // 0.50us 1057 nop2(); // 1.00us 1058 nop2(); // 1.50us 1059 nop2(); // 2.00us 1060 nop2(); // 2.50us 1061 nop2(); // 3.00us 1062 nop(); // 3.25us 1063 if ( --val == 0 ) // + 0.75us (W--, BTFS ) 1064 return; // + 0.25us 1065 nop2(); // 4.50us 1066 } // 5.00us (Goto) 1067 } 1068 //############################################################################################ 1069 1070 #define OneWireData0() { OneWire_TRIS = 0; } // 0.5us @ 16MHz 1071 #define OneWireData1() { OneWire_TRIS = 1; } // 0.5us @ 16MHz 1072 1073 //############################################################################################ 1074 void OneWireWriteByte( uns8 byte ) 1075 //############################################################################################ 1076 { 1077 uns8 bitLoop = 8; 1078 do 1079 { 1080 // Next sequence is time precision critical 1081 GIE = FALSE; 1082 1083 OneWireData0(); 1084 nop2(); // 1 us [0.5 us] 1085 nop2(); // [1.0 us] 1086 if ( byte.0 ) // 2.5 us [1.75us] 1087 OneWireData1(); 1088 1089 // End of time precision critical sequence 1090 GIE = TRUE; 1091 1092 // 60us minimum in total, does not have to be precise 1093 Delay5us( ( 60 - 3 ) / 5 + 1 ); 1094 1095 OneWireData1(); 1096 1097 byte >>= 1; 1098 } while ( --bitLoop != 0 ); 1099 } 1100 1101 //############################################################################################ 1102 uns8 OneWireReadByte() 1103 //############################################################################################ 1104 { 1105 uns8 result; 1106 uns8 bitLoop = 8; 1107 do 1108 { 1109 // Next sequence is time precision critical 1110 GIE = FALSE; 1111 1112 OneWireData0(); 1113 nop2(); // 1 us [0.5 us] 1114 #if F_OSC == 16000000 1115 nop2(); // [1.0 us] 1116 #endif 1117 OneWireData1(); // 2 us [1.5 us] 1118 Delay5us( 15 / 5 ); // 17 us [16.5 us] 1119 1120 Carry = 0; // 17.5 us [16.75 us] 1121 if ( OneWire_IO_IN ) // 18.5 us [ 17.25 us] (condition must not modify Carry) 1122 Carry = 1; 1123 1124 // End of time precision critical sequence 1125 GIE = TRUE; // must not modify Carry 1126 result = rr( result ); 1127 1128 // 60us minimum in total, does not have to be precise 1129 Delay5us( ( 60 - 20 ) / 5 + 1 ); 1130 } while ( --bitLoop != 0 ); 1131 1132 return result; 1133 } 1134 1135 //############################################################################################ 1136 bit OneWireReset() 1137 //############################################################################################ 1138 { 1139 // Setting the pin once to low is enough 1140 OneWire_IO_OUT = 0; 1141 // Reset pulse 1142 OneWireData0(); 1143 Delay5us( 500 / 5 ); 1144 // Reset pulse end 1145 OneWireData1(); 1146 // Next sequence is time precision critical 1147 GIE = FALSE; 1148 // Wait for presence pulse 1149 Delay5us( 70 / 5 ); 1150 // End of time precision critical sequence 1151 GIE = TRUE; 1152 // Presence pulse? 1153 if ( OneWire_IO_IN ) 1154 { 1155 // No presence, finish initialization sequence 1156 Delay5us( ( 500 - 70 ) / 5 ); 1157 return returnFALSE(); 1158 } 1159 else 1160 { 1161 // Presence OK, finish initialization sequence 1162 Delay5us( ( 500 - 70 ) / 5 ); 1163 return returnTRUE(); 1164 } 1165 } 1166 1167 //############################################################################################ 1168 void OneWireCmd( uns8 cmd ) 1169 //############################################################################################ 1170 { 1171 // OneWire: Skip ROM 1172 OneWireWriteByte( CMD_SKIPROM ); 1173 // OneWire: Send command 1174 OneWireWriteByte( cmd ); 1175 } 1176 1177 //############################################################################################ 1178 bit Ds18B20WriteConfig( uns8 value ) 1179 //############################################################################################ 1180 { 1181 if ( OneWireReset() ) 1182 { 1183 // Write Scratchpad 1184 OneWireCmd( CMD_WSCRATCHPAD ); 1185 1186 // Write TL = ? (we dot not care the value) 1187 OneWireWriteByte( W ); 1188 // Write TH = ? (we dot not care the value) 1189 OneWireWriteByte( W ); 1190 // Write Config byte 1191 OneWireWriteByte( value ); 1192 1193 if ( OneWireReset() ) 1194 { 1195 // Copy Scratchpad 1196 OneWireCmd( CMD_CPYSCRATCHPAD ); 1197 return returnTRUE(); 1198 } 1199 } 1200 return returnFALSE(); 1201 } 1202 1203 #else // DALLASnotMCP 1204 1205 //############################################################################################ 1206 void i2c_waitForIdle() 1207 //############################################################################################ 1208 { 1209 i2cTimeout = FALSE; 1210 uns8 timeout; 1211 // Wait for idle and not writing 1212 timeout = 0; 1213 while ( ( SSPCON2 & 0b0001.1111 ) != 0 || RW_ ) 1214 if ( ++timeout == 0 ) 1215 { 1216 i2cTimeout = TRUE; 1217 break; 1218 } 1219 } 1220 1221 #endif 1222 1223 //############################################################################################ 1224 // Other routines 1225 //############################################################################################ 1226 1227 //############################################################################################ 1228 static uns8 _state; 1229 void SetOutput( uns8 state @ _state, uns8 index @ W ) 1230 //############################################################################################ 1231 { 1232 // Note: FSRs must not be modified 1233 // Note: This method is called in the interrupt too! 1234 1235 skip( index ); 1236 #pragma computedGoto 1 1237 goto set0; 1238 goto set1; 1239 goto set2; 1240 goto set3; 1241 #pragma computedGoto 0 1242 ; 1243 // -------------------------------------- 1244 set3: 1245 if ( !state.0 ) 1246 _LEDG = 0; 1247 else 1248 _LEDG = 1; 1249 1250 return; 1251 // -------------------------------------- 1252 set2: 1253 if ( !state.0 ) 1254 _LEDR = 0; 1255 else 1256 _LEDR = 1; 1257 1258 return; 1259 // -------------------------------------- 1260 set1: 1261 if ( !state.0 ) 1262 RELAY2_LAT = 0; 1263 else 1264 RELAY2_LAT = 1; 1265 1266 return; 1267 // -------------------------------------- 1268 set0: 1269 if ( !state.0 ) 1270 RELAY1_LAT = 0; 1271 else 1272 RELAY1_LAT = 1; 1273 1274 return; 1275 // -------------------------------------- 1276 } 1277 1278 //############################################################################################ 1279 bit GetOutput( uns8 index @ W ) 1280 //############################################################################################ 1281 { 1282 Carry = FALSE; // Note: must not modify W 1283 1284 // Note: all below must not modify Carry except when needed 1285 skip( index ); 1286 #pragma computedGoto 1 1287 goto get0; 1288 goto get1; 1289 goto get2; 1290 goto get3; 1291 #pragma computedGoto 0 1292 ; 1293 // -------------------------------------- 1294 get3: 1295 if ( _LEDG ) 1296 Carry = TRUE; 1297 goto _return; 1298 // -------------------------------------- 1299 get2: 1300 if ( _LEDR ) 1301 Carry = TRUE; 1302 goto _return; 1303 // -------------------------------------- 1304 get1: 1305 if ( RELAY2_LAT ) 1306 Carry = TRUE; 1307 goto _return; 1308 // -------------------------------------- 1309 get0: 1310 if ( RELAY1_LAT ) 1311 Carry = TRUE; 1312 goto _return; 1313 // -------------------------------------- 1314 1315 _return: 1316 return Carry; 1317 } 1318 1319 //############################################################################################ 1320 #ifndef DALLASnotMCP 1321 // I2C Master library 1322 #include "lib/I2Cmaster.c" 1323 #endif 1324 1325 // 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) 1326 #include "DPAcustomHandler.h" 1327 //############################################################################################