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