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