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.23 $ 8 // Date: $Date: 2019/04/03 09:27:58 $ 9 // 10 // Revision history: 11 // 2017/11/16 Release for DPA 3.02 12 // 2017/08/14 Release for DPA 3.01 13 // 14 // ********************************************************************* 15 16 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/ 17 // IQRF Standards documentation https://www.iqrfalliance.org/techDocs/ 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 4 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 // Index 2 i.e. 3rd output is Red LED 28 // Index 3 i.e. 4th output is Green LED 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 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 #ifdef DALLASnotMCP 80 // Sensor connected to PORT C.3 (compatible with DDC-SE-01) 81 #define OneWire_TRIS TRISC.3 82 #define OneWire_IO_IN PORTC.3 83 #define OneWire_IO_OUT LATC.3 84 85 // Writes sensor configuration (resolution) 86 bit Ds18B20WriteConfig( uns8 value ); 87 88 // Resets OneWire 89 bit OneWireReset(); 90 // Reads OneWire byte 91 uns8 OneWireReadByte(); 92 // Writes OneWire byte 93 void OneWireWriteByte( uns8 byte ); 94 95 // DS18B20 commands 96 #define CMD_READROM 0x33 97 #define CMD_CONVERTTEMP 0x44 98 #define CMD_CPYSCRATCHPAD 0x48 99 #define CMD_WSCRATCHPAD 0x4e 100 #define CMD_MATCHROM 0x55 101 #define CMD_RPWRSUPPLY 0xb4 102 #define CMD_RECEEPROM 0xb8 103 #define CMD_RSCRATCHPAD 0xbe 104 #define CMD_SKIPROM 0xcc 105 #define CMD_ALARMSEARCH 0xec 106 #define CMD_SEARCHROM 0xf0 107 108 // Final DS18B20 temperature value read by state machine 109 uns16 temperature; 110 111 #else // DALLASnotMCP 112 // I2C routines 113 void i2c_init(); 114 void i2c_shutdown(); 115 void i2c_waitForIdle(); 116 void i2c_start(); 117 void i2c_stop(); 118 uns8 i2c_read( bit ack ); 119 void i2c_write( uns8 i2cWriteData ); 120 121 bit i2cTimeout; 122 123 // MCP9802 address 124 #define I2C_ADR 0b10010110 125 // Power pin 126 #define PWR_SENSOR_TRIS TRISC.7 127 #define PWR_SENSOR_IO LATC.7 128 129 #endif 130 131 // Special temperature value to indicate a sensor error, compatible with IQRF sensor standard 132 #define ERROR_TEMPERATURE 0x8000 133 134 //############################################################################################ 135 136 // Number of implemented binary outputs 137 #define OUTPUTS_COUNT 4 138 139 // Sets and Gets state of the indexed binary output 140 void SetOutput( uns8 state, uns8 index ); 141 bit GetOutput( uns8 index ); 142 143 // DDC-RE-01 relay pins 144 // C.5 = C8 = Relay#1 145 #define RELAY1_LAT LATC.5 146 #define RELAY1_TRIS TRISC.5 147 // C.2 = C2 = Relay#2 148 #define RELAY2_LAT LATC.2 149 #define RELAY2_TRIS TRISC.2 150 151 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 152 //############################################################################################ 153 bit CustomDpaHandler() 154 //############################################################################################ 155 { 156 // 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) 157 #pragma updateBank default = UserBank_01 158 159 #ifdef DALLASnotMCP 160 // Finite machine states 161 typedef enum 162 { 163 S_ResetConvertT = 0, 164 S_SkipRomConvertT, 165 S_CmdConvertT, 166 167 S_WaitConvertT, 168 169 S_ResetReadTemp, 170 S_SkipRomReadTemp, 171 S_CmdReadTemp, 172 S_Byte1ReadTemp, 173 S_Byte2ReadTemp 174 } TState; 175 #endif 176 177 // Handler presence mark 178 clrwdt(); 179 180 // Sleeping parameters, valid when Time != 0 181 static TPerOSSleep_Request PerOSSleep_Request; 182 183 #ifdef DALLASnotMCP 184 // Finite machine state 185 static uns8 state; // = S_ResetConvertT = 0 186 // Pre-read lower temperature byte 187 static uns8 temperatureByteLow; 188 // Conversion timeout counter 189 static uns8 timeout; 190 #endif 191 192 // Timers for outputs. The space must be long enough to fit them all. 2 bytes per one binary output. 193 static uns16 Timers[OUTPUTS_COUNT]; 194 195 // Detect DPA event to handle 196 switch ( GetDpaEvent() ) 197 { 198 // ------------------------------------------------- 199 case DpaEvent_Interrupt: 200 // Do an extra quick background interrupt work 201 // ! 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. 202 // ! 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. 203 // ! 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. 204 // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy. 205 // ! Make sure race condition does not occur when accessing those variables at other places. 206 // ! 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. 207 // ! Do not call any OS functions except setINDFx(). 208 // ! Do not use any OS variables especially for writing access. 209 // ! 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. 210 211 // ms per TMR6 interrupt 212 #define TICKS_LEN 10 213 214 // If TMR6 interrupt occurred 215 if ( TMR6IF ) 216 { 217 // Unmask interrupt 218 TMR6IF = 0; 219 #ifdef DALLASnotMCP 220 // Decrement count 221 if ( timeout != 0 ) 222 timeout--; 223 #endif 224 225 // Count 250 ms from 10 ms micro ticks 226 static uns8 count250ms; 227 if ( ++count250ms == ( 250 / 10 ) ) 228 { 229 // 250 ms 230 count250ms = 0; 231 232 // Pointer to the timers array 233 FSR1 = (uns16)&Timers[0]; 234 // Output index 235 static uns8 index; 236 index = 0; 237 do 238 { 239 // Is timer running (is non-zero)? 240 if ( ( FSR1[1] | INDF1 ) != 0 ) 241 { 242 // Get time 243 static uns16 time; 244 time.low8 = *FSR1++; 245 time.high8 = *FSR1; 246 // Is timer over? 247 if ( --time == 0 ) 248 // Set output to OFF 249 SetOutput( 0, index ); 250 251 // Store new time 252 setINDF1( time.high8 ); 253 FSR1--; 254 setINDF1( time.low8 ); 255 } 256 // Next timer 257 FSR1 += sizeof( Timers[0] ); 258 // Next index 259 } while ( ++index < OUTPUTS_COUNT ); 260 } 261 } 262 return Carry; 263 264 // ------------------------------------------------- 265 case DpaEvent_Idle: 266 // Do a quick background work when RF packet is not received 267 268 // Should go to sleep? 269 if ( PerOSSleep_Request.Time != 0 ) 270 { 271 // Copy sleep parameters to the DPA request 272 _DpaMessage.PerOSSleep_Request.Time = PerOSSleep_Request.Time; 273 _DpaMessage.PerOSSleep_Request.Control = PerOSSleep_Request.Control; 274 // Finalize OS Sleep DPA Request 275 _DpaDataLength = sizeof( _DpaMessage.PerOSSleep_Request ); 276 _PNUM = PNUM_OS; 277 _PCMD = CMD_OS_SLEEP; 278 // Perform local DPA Request to go to sleep 279 DpaApiLocalRequest(); 280 // Switch off sleeping time=flag 281 PerOSSleep_Request.Time = 0; 282 } 283 284 #ifdef DALLASnotMCP 285 // Run finite state machine to read temperature from DS18B20 at background so the temperature value is immediately ready for FRC 286 287 // Make sure 1Wire data pin at LATX.y is low as it might be set by another PORTX.? pin manipulation 288 OneWire_IO_OUT = 0; 289 290 skip( state ); 291 #pragma computedGoto 1 292 goto _S_ResetConvertT; 293 goto _S_SkipRomConvertT; 294 goto _S_CmdConvertT; 295 goto _S_WaitConvertT; 296 goto _S_ResetReadTemp; 297 goto _S_SkipRomReadTemp; 298 goto _S_CmdReadTemp; 299 goto _S_Byte1ReadTemp; 300 goto _S_Byte2ReadTemp; 301 #pragma computedGoto 0 302 ; 303 // -------------- 304 _S_Byte2ReadTemp: 305 temperature.high8 = OneWireReadByte(); 306 temperature.low8 = temperatureByteLow; 307 308 ResetMachine: 309 state = S_ResetConvertT; 310 goto ExitMachine; 311 312 // -------------- 313 _S_ResetConvertT: 314 _S_ResetReadTemp: 315 if ( !OneWireReset() ) 316 { 317 _S_Error_Reset: 318 temperature = ERROR_TEMPERATURE; 319 goto ResetMachine; 320 } 321 goto NextState; 322 323 // -------------- 324 _S_SkipRomConvertT: 325 _S_SkipRomReadTemp: 326 // OneWire: Skip ROM 327 OneWireWriteByte( CMD_SKIPROM ); 328 goto NextState; 329 330 // -------------- 331 _S_CmdConvertT: 332 // OneWire: Convert temperature 333 OneWireWriteByte( CMD_CONVERTTEMP ); 334 // Setup timeout for approx 750 ms (the longest conversion time) 335 timeout = 2 + 750 / TICKS_LEN; 336 goto NextState; 337 338 // -------------- 339 _S_WaitConvertT: 340 if ( OneWireReadByte() == 0xff ) 341 goto NextState; 342 343 // Timeout? 344 if ( timeout == 0 ) 345 goto _S_Error_Reset; 346 347 goto ExitMachine; 348 349 // -------------- 350 _S_CmdReadTemp: 351 // OneWire: Read scratchpad 352 OneWireWriteByte( CMD_RSCRATCHPAD ); 353 goto NextState; 354 355 // -------------- 356 _S_Byte1ReadTemp: 357 temperatureByteLow = OneWireReadByte(); 358 goto NextState; 359 360 // -------------- 361 NextState: 362 ++state; 363 364 ExitMachine: 365 #endif 366 break; 367 368 // ------------------------------------------------- 369 case DpaEvent_Init: 370 // Do a one time initialization work before main loop starts 371 372 // Initialize relays @ DDC-RE 373 RELAY1_LAT = 0; 374 RELAY2_LAT = 0; 375 RELAY1_TRIS = 0; 376 RELAY2_TRIS = 0; 377 378 // Initialize sensors 379 // C5 (AN4) as input 380 moduleInfo(); 381 // Connected TR pins? 382 if ( !bufferINFO[5].7 ) 383 { 384 TRISC.6 = 1; 385 TRISB.4 = 1; 386 } 387 TRISA.5 = 1; 388 389 // C1 (AN0) as input 390 TRISA.0 = 1; 391 392 // Setup TMR6 to generate ticks on the background (ticks every 10ms) 393 #if F_OSC == 16000000 394 PR6 = 250 - 1; 395 T6CON = 0b0.1001.1.10; // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms 396 #else 397 #error Unsupported oscillator frequency 398 #endif 399 400 #ifdef DALLASnotMCP 401 // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet) 402 Ds18B20WriteConfig( 0b0.00.00000 ); 403 #else 404 // Expect MCP9802 is enabled 405 i2c_init(); 406 #endif 407 break; 408 409 // ------------------------------------------------- 410 case DpaEvent_AfterSleep: 411 // Called after woken up after sleep 412 #ifndef DALLASnotMCP 413 i2c_init(); 414 #endif 415 416 // Called on wake-up from sleep 417 TMR6IE = TRUE; 418 TMR6ON = TRUE; 419 break; 420 421 // ------------------------------------------------- 422 case DpaEvent_BeforeSleep: 423 // Called before going to sleep 424 #ifndef DALLASnotMCP 425 i2c_shutdown(); 426 #endif 427 428 // ------------------------------------------------- 429 case DpaEvent_DisableInterrupts: 430 // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM) 431 // Must not use TMR6 any more 432 TMR6ON = FALSE; 433 TMR6IE = FALSE; 434 break; 435 436 // ------------------------------------------------- 437 case DpaEvent_DpaRequest: 438 // Called to interpret DPA request for peripherals 439 // ------------------------------------------------- 440 // Peripheral enumeration 441 IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest() 442 { 443 // We implement 2 standard peripherals 444 _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 2; 445 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_SENSORS ); 446 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_BINARY_OUTPUTS ); 447 _DpaMessage.EnumPeripheralsAnswer.HWPID = HWPID_IQRF_TECH__DEMO_DDC_SE01_RE01; 448 _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x0002; 449 450 DpaHandleReturnTRUE: 451 return TRUE; 452 } 453 // ------------------------------------------------- 454 // Get information about peripherals 455 else 456 { 457 #if PERIPHERAL_TYPE_STD_SENSORS != PNUM_STD_SENSORS || PERIPHERAL_TYPE_STD_BINARY_OUTPUTS != PNUM_STD_BINARY_OUTPUTS 458 #error 459 #endif 460 switch ( _DpaMessage.PeripheralInfoAnswer.PerT = _PNUM ) 461 { 462 case PNUM_STD_SENSORS: 463 // Set standard version 464 W = 13; 465 goto Par1toVersion; 466 467 case PNUM_STD_BINARY_OUTPUTS: 468 // Set standard version 469 W = 4; 470 Par1toVersion: 471 _DpaMessage.PeripheralInfoAnswer.Par1 = W; 472 _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE; 473 goto DpaHandleReturnTRUE; 474 } 475 476 break; 477 } 478 479 { 480 // ------------------------------------------------- 481 // Handle peripheral command 482 483 // Supported peripheral number? 484 switch ( _PNUM ) 485 { 486 case PNUM_STD_SENSORS: 487 { 488 // Supported commands? 489 switch ( _PCMD ) 490 { 491 // Invalid command 492 default: 493 { 494 // Return error 495 _ERROR_PCMD: 496 W = ERROR_PCMD; 497 _ERROR_W: 498 DpaApiReturnPeripheralError( W ); 499 } 500 501 // Sensor enumeration 502 case PCMD_STD_ENUMERATE: 503 if ( _DpaDataLength != 0 ) 504 goto _ERROR_DATA_LEN; 505 506 _DpaMessage.Response.PData[0] = STD_SENSOR_TYPE_TEMPERATURE; 507 _DpaMessage.Response.PData[1] = STD_SENSOR_TYPE_BINARYDATA7; 508 _DpaMessage.Response.PData[2] = STD_SENSOR_TYPE_BINARYDATA7; 509 W = SENSORS_COUNT; 510 goto _W2_DpaDataLength; 511 512 // Supported commands. They are handled the same way except one "if" at StoreValue() method 513 case PCMD_STD_SENSORS_READ_VALUES: 514 case PCMD_STD_SENSORS_READ_TYPES_AND_VALUES: 515 { 516 // No sensor bitmap specified? W = _DpaDataLength. Note: W is used to avoid MOVLB at next if 517 W = _DpaDataLength; 518 if ( W == 0 ) // Note: must not modify W 519 { 520 // Actually clears the bitmap 521 #if &_DpaMessage.Request.PData[0] != &bufferRF[0] 522 #error 523 #endif 524 clearBufferRF(); 525 // Simulate 1st only sensor in the bitmap (states of the other unimplemented sensors do not care) 526 _DpaMessage.Request.PData[0].0 = 1; 527 // Bitmap is 32 bits long = 4 528 _DpaDataLength = W = 4; 529 } 530 531 // Invalid bitmap (data) length (W = _DpaDataLength)? 532 if ( W != 4 ) 533 goto _ERROR_DATA_LEN; 534 535 // Now read the sensors 536 537 // Prepare pointer (minus 1, see below) to store sensor (types and) values to 538 // 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... 539 // ... If it would be the case, then ERROR_FAIL must be returned 540 FSR1 = &_DpaMessage.Response.PData[-1]; 541 542 // Store bitmap of sensors to get values from 543 uns8 sensorsBitmap = FSR1[1]; 544 545 // 1st sensor (index 0) selected? 546 if ( sensorsBitmap.0 ) 547 { 548 Get0_Temperature(); 549 StoreValue( STD_SENSOR_TYPE_TEMPERATURE ); 550 } 551 552 // 2nd sensor (index 1) selected? 553 if ( sensorsBitmap.1 ) 554 { 555 Get1_BinaryData_Light(); 556 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 ); 557 } 558 559 // 3rd sensor (index 2) selected? 560 if ( sensorsBitmap.2 ) 561 { 562 Get2_BinaryData_Potentiometer(); 563 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 ); 564 } 565 566 // Compute returned data bytes count 567 W = FSR1L - ( (uns16)&_DpaMessage.Response.PData[0] & 0xFF ) + 1; 568 // Optimization: return W long block of bytes at response 569 _W2_DpaDataLength: 570 _DpaDataLength = W; 571 goto DpaHandleReturnTRUE; 572 } 573 } 574 } 575 576 case PNUM_STD_BINARY_OUTPUTS: 577 { 578 // Supported commands? 579 switch ( _PCMD ) 580 { 581 // Invalid command 582 default: 583 // Return error 584 goto _ERROR_PCMD; 585 586 // Outputs enumeration 587 case PCMD_STD_ENUMERATE: 588 if ( _DpaDataLength != 0 ) 589 goto _ERROR_DATA_LEN; 590 591 // Return number of outputs 592 _DpaMessage.Response.PData[0] = OUTPUTS_COUNT; 593 W = 1; 594 goto _W2_DpaDataLength; 595 596 // Supported commands. 597 case PCMD_STD_BINARY_OUTPUTS_SET: 598 { 599 // Pointers FSR01 to data are already set at the DPA 600 601 // As this template implements < 9 outputs the working bitmap is uns8, if more outputs are implemented then uns16, ..., uns32 must be used 602 #if OUTPUTS_COUNT < 9 603 uns8 inBitmap = *FSR0--; 604 uns8 outBitmap @ _DpaMessage.Response.PData[0]; 605 uns8 bitmapMask = 0b1; 606 #else 607 #error Not implemented 608 #endif 609 610 // Number of selected outputs + bitmap length 611 uns8 outputsCount = 4; 612 // Loop bitmap 613 uns8 index = 4; 614 do 615 { 616 // Count bits of next byte 617 uns8 byte = *++FSR0; 618 if ( byte != 0 ) 619 { 620 // Brian Kernighan's Algorithm for counting set bits 621 do 622 { 623 outputsCount++; 624 byte &= byte - 1; 625 } while ( byte != 0 ); 626 } 627 628 // Reset bitmap 629 setINDF0( 0 ); 630 } while ( --index != 0 ); 631 632 // Check data length 633 if ( _DpaDataLength != outputsCount ) 634 { 635 _ERROR_DATA_LEN: 636 W = ERROR_DATA_LEN; 637 goto _ERROR_W; 638 } 639 640 // Pointer to the timers array 641 FSR1 = (uns16)&Timers[0]; 642 // Output index 643 index = 0; 644 do 645 { 646 // Output was set? 647 if ( GetOutput( index ) ) 648 // Yes, set in the output bitmap 649 outBitmap |= bitmapMask; 650 651 // Implemented output selected? Set the state. 652 if ( inBitmap.0 ) 653 { 654 // Default is timer off 655 uns16 time = 0; 656 // Desired state 657 uns8 state = *++FSR0; 658 if ( state > 1 ) 659 { 660 // Get time in units s/min 661 time = state & 0x7F; 662 if ( time == 0 ) 663 { 664 // Invalid time 665 W = ERROR_FAIL; 666 goto _ERROR_W; 667 } 668 669 // Conversion coefficient, ready for minutes to 250 ms 670 uns8 coef = 60 * ( 1000 / 250 ); 671 if ( state.7 ) 672 // Convert from seconds 673 coef = 1000 / 250; 674 675 // Convert to 250 ms 676 time *= coef; 677 // Set ON 678 state = 1; 679 } 680 681 // Set output 682 SetOutput( state, index ); 683 684 // Set timer and preserve pointer 685 GIE = FALSE; 686 setINDF1( time.low8 ); 687 FSR1++; 688 setINDF1( time.high8 ); 689 FSR1--; 690 GIE = TRUE; 691 } 692 693 // Next pointer to the timer 694 FSR1 += sizeof( Timers[0] ); 695 // Next bits 696 bitmapMask <<= 1; 697 inBitmap >>= 1; 698 // Next index 699 } while ( ++index < OUTPUTS_COUNT ); 700 701 // Return bitmap 702 _DpaDataLength4: 703 W = 4; 704 goto _W2_DpaDataLength; 705 } 706 } 707 } 708 } 709 710 break; 711 } 712 713 // ------------------------------------------------- 714 case DpaEvent_FrcValue: 715 // Called to get FRC value 716 717 // FSR1 for optimization purposes (avoid MOVLB) will be used to point to DataOutBeforeResponseFRC[0...] 718 FSR1 = &DataOutBeforeResponseFRC[0]; 719 // Check for correct FRC user data 720 if ( *FSR1++ /*DataOutBeforeResponseFRC[0]*/ == PNUM_STD_SENSORS ) 721 { 722 // Actually used sensor index 723 uns8 sensorIndex = FSR1[1] /*DataOutBeforeResponseFRC[2]*/ & 0x1f; 724 // Test sensor type 725 switch ( *FSR1++ /*DataOutBeforeResponseFRC[1]*/ ) 726 { 727 default: 728 goto DpaHandleReturnFALSE; 729 730 // No type specified, use specified index value 731 case 0x00: 732 goto _KeepSensorIndex; 733 734 // For other types make the index value based on the requested index value and sensor type 735 case STD_SENSOR_TYPE_TEMPERATURE: 736 if ( sensorIndex > 0 ) 737 goto DpaHandleReturnFALSE; 738 W = 0 + sensorIndex; 739 break; 740 741 case STD_SENSOR_TYPE_BINARYDATA7: 742 if ( sensorIndex > 1 ) 743 goto DpaHandleReturnFALSE; 744 W = 1 + sensorIndex; 745 break; 746 } 747 748 // New sensor index based on type and requested index 749 sensorIndex = W; 750 _KeepSensorIndex: 751 752 // Test for supported FRC commands 753 switch ( _PCMD ) 754 { 755 default: 756 goto DpaHandleReturnFALSE; 757 758 case FRC_STD_SENSORS_BIT: 759 case FRC_STD_SENSORS_1B: 760 case FRC_STD_SENSORS_2B: 761 switch ( sensorIndex ) 762 { 763 default: 764 goto DpaHandleReturnFALSE; 765 766 case 0: 767 Carry = Get0_Temperature(); 768 break; 769 770 case 1: 771 Carry = Get1_BinaryData_Light(); 772 break; 773 774 case 2: 775 Carry = Get2_BinaryData_Potentiometer(); 776 break; 777 } 778 779 // This type of FRC is not valid for the specified sensor 780 if ( !Carry ) 781 goto DpaHandleReturnFALSE; 782 783 break; 784 } 785 786 // Some sensor was measured by FRC, check if there is a sleep request 787 FSR1++; 788 if ( INDF1.0 ) // Note: same as DataOutBeforeResponseFRC[3].0 789 { 790 // Remember sleep parameters to go to sleep at the Idle event later 791 PerOSSleep_Request.Time.low8 = FSR1[4 - 3]; // Note: same as DataOutBeforeResponseFRC[4]; 792 PerOSSleep_Request.Time.high8 = FSR1[5 - 3]; // Note: same as DataOutBeforeResponseFRC[5]; 793 PerOSSleep_Request.Control = FSR1[6 - 3]; // Note: same as DataOutBeforeResponseFRC[6]; 794 } 795 } 796 797 break; 798 799 // ------------------------------------------------- 800 case DpaEvent_FrcResponseTime: 801 // Called to get FRC response time 802 803 // In this example the FRC commands are fast 804 switch ( DataOutBeforeResponseFRC[0] ) 805 { 806 case FRC_STD_SENSORS_BIT: 807 case FRC_STD_SENSORS_1B: 808 case FRC_STD_SENSORS_2B: 809 responseFRCvalue = _FRC_RESPONSE_TIME_40_MS; 810 break; 811 } 812 break; 813 } 814 DpaHandleReturnFALSE: 815 return FALSE; 816 } 817 818 //############################################################################################ 819 bit returnTRUE() 820 //############################################################################################ 821 { 822 return TRUE; 823 } 824 825 //############################################################################################ 826 bit returnFALSE() 827 //############################################################################################ 828 { 829 return FALSE; 830 } 831 832 //############################################################################################ 833 // Increases FSR1 and then stores the byte 834 void setPlusPlusINDF1( uns8 data @ W ) 835 //############################################################################################ 836 { 837 FSR1++; // Note: must not modify W 838 setINDF1( data ); 839 } 840 841 //############################################################################################ 842 // Stores measured sensor value byte(s) and optionally sensor type to the FSR[+1...] 843 void StoreValue( uns8 sensorType ) 844 //############################################################################################ 845 { 846 // Is the sensor type to be stored too? 847 if ( _PCMD == PCMD_STD_SENSORS_READ_TYPES_AND_VALUES ) 848 setPlusPlusINDF1( sensorType ); 849 850 // Store lower value byte 851 setPlusPlusINDF1( sensorValue.low8 ); 852 853 // No more value bytes to store? 854 if ( sensorType.7 != 0 ) 855 return; 856 857 // Store higher value byte 858 setPlusPlusINDF1( sensorValue.high8 ); 859 860 // 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. 861 } 862 863 //############################################################################################ 864 bit setFRCerror() 865 //############################################################################################ 866 { 867 responseFRCvalue2B = 2; 868 return returnTRUE(); 869 } 870 871 //############################################################################################ 872 bit sensorError; 873 bit AdjustFrcTemperature() 874 //############################################################################################ 875 { 876 // Test for supported FRC commands 877 switch ( _PCMD ) 878 { 879 default: 880 return returnFALSE(); 881 882 case FRC_STD_SENSORS_1B: 883 // Return sensor FRC value 1B 884 // Check for out of limits 885 if ( sensorError || (int16)sensorValue > (int16)( 105.5 * 16 ) || (int16)sensorValue < ( (int16)-20 * 16 ) ) 886 return setFRCerror(); 887 888 // Convert to the "F = ( T + 22 ) * 2 " from 1/16 resolution 889 uns16 _sensorValue = sensorValue + 4; // Note: do rounding when /8 890 responseFRCvalue = (uns8)( _sensorValue / 8 ) + 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 ( sensorValue == ERROR_TEMPERATURE ) 922 sensorError = TRUE; 923 #else 924 sensorError = TRUE; 925 // Temperature value must be read from I2C sensor 926 sensorValue = ERROR_TEMPERATURE; 927 i2c_start(); 928 if ( !i2cTimeout ) 929 { 930 // MCP9802 address 931 i2c_write( I2C_ADR ); 932 // pointer: 1 = configuration register 933 i2c_write( 0x01 ); 934 // configuration: 9-bit ADC 935 i2c_write( 0x00 ); 936 i2c_stop(); 937 938 i2c_start(); 939 // MCP9802 address 940 i2c_write( I2C_ADR ); 941 // pointer: 0 = temperature 942 i2c_write( 0 ); 943 i2c_stop(); 944 945 i2c_start(); 946 // MCP9802 address + read 947 i2c_write( I2C_ADR | 1 ); 948 // store the result 949 sensorValue.high8 = i2c_read( TRUE ); 950 sensorValue.low8 = i2c_read( FALSE ); 951 i2c_stop(); 952 953 sensorValue += 0x10 / 2; 954 sensorValue /= 0x10; 955 956 sensorError = FALSE; 957 } 958 #endif 959 960 // FrcValues 961 return AdjustFrcTemperature(); 962 } 963 964 //############################################################################################ 965 // Sensor index 1: returns light intensity indicator value using DDC-SE-01 966 bit Get_BinaryData_Final( uns8 _ADCON0 @ W ) 967 //############################################################################################ 968 { 969 ADCON0 = _ADCON0; 970 // Read ADC 971 972 // ADC result - left justified, Fosc/8 973 ADCON1 = 0b0001.0000; 974 // Do a smallest delay for ADC ACQUISITION TIME 975 waitMS( 1 ); 976 // start ADC 977 GO = 1; 978 // wait for ADC finish 979 while ( GO ); 980 // Get ADC value 981 sensorValue.low8 = ADRESH / 2; 982 983 // Return sensor FRC value 984 985 // Test for supported FRC commands 986 switch ( _PCMD ) 987 { 988 default: 989 return returnFALSE(); 990 991 case FRC_STD_SENSORS_BIT: 992 // If there is a sensor error, 2-bit FRC cannot indicate it, it returns [01] 993 994 // Number of shifts to get the bit out of the return value 995 uns8 bitLoop = ( INDF1 >> 5 ) + 1; 996 // Value to get the bit from 997 W = sensorValue.low8; 998 do 999 { 1000 // Get the bit to Carry 1001 W = rr( W ); 1002 // Next bit 1003 } while ( --bitLoop != 0 ); // Note: must not modify W and Carry 1004 // Current (prepared by DPA) FRC value is [01], change it to [11] (means bit is set) 1005 responseFRCvalue.1 = 1; // Note: must not modify Carry 1006 // Is bit set? 1007 if ( !Carry ) 1008 // Bit is NOT set, return [10] 1009 responseFRCvalue.0 = 0; 1010 break; 1011 1012 case FRC_STD_SENSORS_1B: 1013 responseFRCvalue = sensorValue.low8 + 4; 1014 break; 1015 } 1016 1017 return returnTRUE(); 1018 } 1019 1020 //############################################################################################ 1021 // Sensor index 1: returns light intensity indicator value using DDC-SE-01 1022 bit Get1_BinaryData_Light() 1023 //############################################################################################ 1024 { 1025 // Make sure FSR1 is not modified 1026 1027 // ADC initialization (for more info see PIC datasheet) pin C1 (AN0) as analog input 1028 ANSELA.0 = 1; 1029 // ADC setting (AN0 channel) 1030 return Get_BinaryData_Final( 0b0.00000.01 ); 1031 } 1032 1033 //############################################################################################ 1034 // Sensor index 2: returns potentiometer value using DDC-SE-01 1035 bit Get2_BinaryData_Potentiometer() 1036 //############################################################################################ 1037 { 1038 // Make sure FSR1 is not modified 1039 1040 // ADC initialization (for more info see PIC datasheet) pin C5 (AN4) as analog input 1041 ANSELA.5 = 1; 1042 // ADC setting (AN4 channel) 1043 return Get_BinaryData_Final( 0b0.00100.01 ); 1044 } 1045 1046 #ifdef DALLASnotMCP 1047 //############################################################################################ 1048 // OneWire and Dallas 18B20 routines 1049 //############################################################################################ 1050 1051 //############################################################################################ 1052 void Delay5us( uns8 val @ W ) // Absolutely precise timing but val != 0 1053 //############################################################################################ 1054 { 1055 // 16 MHz 1056 // + 0.75us ( W=val, Call ) 1057 for ( ;; ) 1058 { // loop time 1059 nop2(); // 0.50us 1060 nop2(); // 1.00us 1061 nop2(); // 1.50us 1062 nop2(); // 2.00us 1063 nop2(); // 2.50us 1064 nop2(); // 3.00us 1065 nop(); // 3.25us 1066 if ( --val == 0 ) // + 0.75us (W--, BTFS ) 1067 return; // + 0.25us 1068 nop2(); // 4.50us 1069 } // 5.00us (Goto) 1070 } 1071 //############################################################################################ 1072 1073 #define OneWireData0() { OneWire_TRIS = 0; } // 0.5us @ 16MHz 1074 #define OneWireData1() { OneWire_TRIS = 1; } // 0.5us @ 16MHz 1075 1076 //############################################################################################ 1077 void OneWireWriteByte( uns8 byte ) 1078 //############################################################################################ 1079 { 1080 uns8 bitLoop = 8; 1081 do 1082 { 1083 // Next sequence is time precision critical 1084 GIE = FALSE; 1085 1086 OneWireData0(); 1087 nop2(); // 1 us [0.5 us] 1088 nop2(); // [1.0 us] 1089 if ( byte.0 ) // 2.5 us [1.75us] 1090 OneWireData1(); 1091 1092 // End of time precision critical sequence 1093 GIE = TRUE; 1094 1095 // 60us minimum in total, does not have to be precise 1096 Delay5us( ( 60 - 3 ) / 5 + 1 ); 1097 1098 OneWireData1(); 1099 1100 byte >>= 1; 1101 } while ( --bitLoop != 0 ); 1102 } 1103 1104 //############################################################################################ 1105 uns8 OneWireReadByte() 1106 //############################################################################################ 1107 { 1108 uns8 result; 1109 uns8 bitLoop = 8; 1110 do 1111 { 1112 // Next sequence is time precision critical 1113 GIE = FALSE; 1114 1115 OneWireData0(); 1116 nop2(); // 1 us [0.5 us] 1117 #if F_OSC == 16000000 1118 nop2(); // [1.0 us] 1119 #endif 1120 OneWireData1(); // 2 us [1.5 us] 1121 Delay5us( 15 / 5 ); // 17 us [16.5 us] 1122 1123 Carry = 0; // 17.5 us [16.75 us] 1124 if ( OneWire_IO_IN ) // 18.5 us [ 17.25 us] (condition must not modify Carry) 1125 Carry = 1; 1126 1127 // End of time precision critical sequence 1128 GIE = TRUE; // must not modify Carry 1129 result = rr( result ); 1130 1131 // 60us minimum in total, does not have to be precise 1132 Delay5us( ( 60 - 20 ) / 5 + 1 ); 1133 } while ( --bitLoop != 0 ); 1134 1135 return result; 1136 } 1137 1138 //############################################################################################ 1139 bit OneWireReset() 1140 //############################################################################################ 1141 { 1142 // Setting the pin once to low is enough 1143 OneWire_IO_OUT = 0; 1144 // Reset pulse 1145 OneWireData0(); 1146 Delay5us( 500 / 5 ); 1147 // Reset pulse end 1148 OneWireData1(); 1149 // Next sequence is time precision critical 1150 GIE = FALSE; 1151 // Wait for presence pulse 1152 Delay5us( 70 / 5 ); 1153 // End of time precision critical sequence 1154 GIE = TRUE; 1155 // Presence pulse? 1156 if ( OneWire_IO_IN ) 1157 { 1158 // No presence, finish initialization sequence 1159 Delay5us( ( 500 - 70 ) / 5 ); 1160 return returnFALSE(); 1161 } 1162 else 1163 { 1164 // Presence OK, finish initialization sequence 1165 Delay5us( ( 500 - 70 ) / 5 ); 1166 return returnTRUE(); 1167 } 1168 } 1169 1170 //############################################################################################ 1171 void OneWireCmd( uns8 cmd ) 1172 //############################################################################################ 1173 { 1174 // OneWire: Skip ROM 1175 OneWireWriteByte( CMD_SKIPROM ); 1176 // OneWire: Send command 1177 OneWireWriteByte( cmd ); 1178 } 1179 1180 //############################################################################################ 1181 bit Ds18B20WriteConfig( uns8 value ) 1182 //############################################################################################ 1183 { 1184 if ( OneWireReset() ) 1185 { 1186 // Write Scratchpad 1187 OneWireCmd( CMD_WSCRATCHPAD ); 1188 1189 // Write TL = ? (we dot not care the value) 1190 OneWireWriteByte( W ); 1191 // Write TH = ? (we dot not care the value) 1192 OneWireWriteByte( W ); 1193 // Write Config byte 1194 OneWireWriteByte( value ); 1195 1196 if ( OneWireReset() ) 1197 { 1198 // Copy Scratchpad 1199 OneWireCmd( CMD_CPYSCRATCHPAD ); 1200 return returnTRUE(); 1201 } 1202 } 1203 return returnFALSE(); 1204 } 1205 1206 #else // DALLASnotMCP 1207 1208 //############################################################################################ 1209 void writeToSSPCON2( uns8 value ) 1210 //############################################################################################ 1211 { 1212 writeToRAM( &SSPCON2, value ); 1213 } 1214 1215 //############################################################################################ 1216 void writeOredToSSPCON2( uns8 value ) 1217 //############################################################################################ 1218 { 1219 writeToSSPCON2( SSPCON2 | value ); 1220 } 1221 1222 //############################################################################################ 1223 // I2C routines 1224 //############################################################################################ 1225 1226 //############################################################################################ 1227 void i2c_init() 1228 //############################################################################################ 1229 { 1230 // SCL as input (SIM C6) 1231 TRISC.3 = 1; 1232 // SDA as input (SIM C7) 1233 TRISC.4 = 1; 1234 1235 // I2C master mode SSPCON = 0b00111000 1236 writeToRAM( &SSPCON1, 0x38 ); 1237 writeToSSPCON2( 0x00 ); 1238 1239 // 50 kHz SCL frequency 1240 SSPADD = ( F_OSC / 50000 / 4 ) - 2; 1241 // Disable slew rate control 1242 SMP = 1; 1243 } 1244 1245 //############################################################################################ 1246 void i2c_shutdown() 1247 //############################################################################################ 1248 { 1249 // I2C master mode SSPCON = 0 1250 writeToRAM( &SSPCON1, 0x00 ); 1251 } 1252 1253 //############################################################################################ 1254 void i2c_waitForIdle() 1255 //############################################################################################ 1256 { 1257 i2cTimeout = FALSE; 1258 uns8 timeout; 1259 // Wait for idle and not writing 1260 timeout = 0; 1261 while ( ( SSPCON2 & 0b0001.1111 ) != 0 || RW_ ) 1262 if ( ++timeout == 0 ) 1263 { 1264 i2cTimeout = TRUE; 1265 break; 1266 } 1267 } 1268 1269 //############################################################################################ 1270 void i2c_start() 1271 //############################################################################################ 1272 { 1273 i2c_waitForIdle(); 1274 // SEN = 1 1275 writeOredToSSPCON2( 0x01 ); 1276 } 1277 1278 //############################################################################################ 1279 void i2c_stop() 1280 //############################################################################################ 1281 { 1282 i2c_waitForIdle(); 1283 // PEN = 1 1284 writeOredToSSPCON2( 0x04 ); 1285 } 1286 1287 //############################################################################################ 1288 uns8 i2c_read( bit ack ) 1289 //############################################################################################ 1290 { 1291 i2c_waitForIdle(); 1292 // RCEN = 1 1293 writeOredToSSPCON2( 0x08 ); 1294 1295 i2c_waitForIdle(); 1296 1297 uns8 i2cReadData @ userReg0; 1298 i2cReadData = SSPBUF; 1299 1300 i2c_waitForIdle(); 1301 1302 if ( ack ) 1303 // Acknowledge, ACKDT = 0 1304 writeToSSPCON2( SSPCON2 & 0xDF ); 1305 else 1306 // Not acknowledge, ACKDT = 1 1307 writeOredToSSPCON2( 0x20 ); 1308 1309 // Send acknowledge sequence, ACKEN = 1 1310 writeOredToSSPCON2( 0x10 ); 1311 return i2cReadData; 1312 } 1313 1314 //############################################################################################ 1315 void i2c_write( uns8 i2cWriteData ) 1316 //############################################################################################ 1317 { 1318 i2c_waitForIdle(); 1319 SSPBUF = i2cWriteData; 1320 } 1321 1322 #endif 1323 1324 //############################################################################################ 1325 // Other routines 1326 //############################################################################################ 1327 1328 1329 //############################################################################################ 1330 static uns8 _state; 1331 void SetOutput( uns8 state @ _state, uns8 index @ W ) 1332 //############################################################################################ 1333 { 1334 // Note: FSRs must not be modified 1335 // Note: This method is called in the interrupt too! 1336 1337 skip( index ); 1338 #pragma computedGoto 1 1339 goto set0; 1340 goto set1; 1341 goto set2; 1342 goto set3; 1343 #pragma computedGoto 0 1344 ; 1345 // -------------------------------------- 1346 set3: 1347 if ( !state.0 ) 1348 _LEDG = 0; 1349 else 1350 _LEDG = 1; 1351 1352 return; 1353 // -------------------------------------- 1354 set2: 1355 if ( !state.0 ) 1356 _LEDR = 0; 1357 else 1358 _LEDR = 1; 1359 1360 return; 1361 // -------------------------------------- 1362 set1: 1363 if ( !state.0 ) 1364 RELAY2_LAT = 0; 1365 else 1366 RELAY2_LAT = 1; 1367 1368 return; 1369 // -------------------------------------- 1370 set0: 1371 if ( !state.0 ) 1372 RELAY1_LAT = 0; 1373 else 1374 RELAY1_LAT = 1; 1375 1376 return; 1377 // -------------------------------------- 1378 } 1379 1380 //############################################################################################ 1381 bit GetOutput( uns8 index @ W ) 1382 //############################################################################################ 1383 { 1384 Carry = FALSE; // Note: must not modify W 1385 1386 // Note: all below must not modify Carry except when needed 1387 skip( index ); 1388 #pragma computedGoto 1 1389 goto get0; 1390 goto get1; 1391 goto get2; 1392 goto get3; 1393 #pragma computedGoto 0 1394 ; 1395 // -------------------------------------- 1396 get3: 1397 if ( _LEDG ) 1398 Carry = TRUE; 1399 goto _return; 1400 // -------------------------------------- 1401 get2: 1402 if ( _LEDR ) 1403 Carry = TRUE; 1404 goto _return; 1405 // -------------------------------------- 1406 get1: 1407 if ( RELAY2_LAT ) 1408 Carry = TRUE; 1409 goto _return; 1410 // -------------------------------------- 1411 get0: 1412 if ( RELAY1_LAT ) 1413 Carry = TRUE; 1414 goto _return; 1415 // -------------------------------------- 1416 1417 _return: 1418 return Carry; 1419 } 1420 1421 //############################################################################################ 1422 // 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) 1423 #include "DPAcustomHandler.h" 1424 //############################################################################################