1 // ********************************************************************* 2 // Custom DPA Handler code example - Standard Sensors - DDC-SE-01 * 3 // ********************************************************************* 4 // Copyright (c) IQRF Tech s.r.o. 5 // 6 // File: $RCSfile: 0002_DDC-SE01.c,v $ 7 // Version: $Revision: 1.19 $ 8 // Date: $Date: 2018/10/25 09:51:28 $ 9 // 10 // Revision history: 11 // 2018/10/25 Release for DPA 3.03 12 // 2017/11/16 Release for DPA 3.02 13 // 2017/08/14 Release for DPA 3.01 14 // 15 // ********************************************************************* 16 17 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/ 18 19 // This example implements 4 sensors according to the IQRF Sensors standard 20 // 1st sensor is on-board TR temperature sensor. 21 // 2nd sensor is either Dallas 18B20 or MCP9802 temperature sensor at DDC-SE-01 board (according to the HW jumper position) chosen at the runtime based on the SW detection. 22 // 3rd sensor is light intensity indicator at DDC-SE-01 board (value range is 0[max light]-127[max dark]). 23 // 4th sensor is potentiometer value at DDC-SE-01 board (value range is 0[left stop]-127[right stop]). 24 25 // Default IQRF include (modify the path according to your setup) 26 #include "IQRF.h" 27 28 // Default DPA header (modify the path according to your setup) 29 #include "DPA.h" 30 // Default Custom DPA Handler header (modify the path according to your setup) 31 #include "DPAcustomHandler.h" 32 // IQRF standards header (modify the path according to your setup) 33 #include "IQRFstandard.h" 34 #include "IQRF_HWPID.h" 35 36 //############################################################################################ 37 38 // Number of implemented sensors 39 #define SENSORS_COUNT 4 40 41 // Variable to store sensor value at Get?_????() methods. This example implements sensors returning maximum 2 bytes of data. 42 uns16 sensorValue @ param3; 43 44 // Reads sensor value to the sensorValue variable and to responseFRCvalue(2B) variable 45 bit Get0_TemperatureTR(); 46 bit Get1_Temperature(); 47 bit Get2_BinaryData_Light(); 48 bit Get3_BinaryData_Potentiometer(); 49 50 // Temperature sensors read routine for both DDC-SE-01 sensor types 51 void GetTemperature(); 52 // Read preset PIC ADC for DDC-SE-01 53 uns8 ReadAdc(); 54 55 // 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) 56 void StoreValue( uns8 sensorType ); 57 58 // Sensor connected to PORT C.3 (compatible with DDC-SE-01) 59 #define OneWire_TRIS TRISC.3 60 #define OneWire_IO_IN PORTC.3 61 #define OneWire_IO_OUT LATC.3 62 63 // Writes sensor configuration (resolution) 64 bit Ds18B20WriteConfig( uns8 value ); 65 66 // Resets OneWire 67 bit OneWireReset(); 68 // Reads OneWire byte 69 uns8 OneWireReadByte(); 70 // Writes OneWire byte 71 void OneWireWriteByte( uns8 byte ); 72 73 // DS18B20 commands 74 #define CMD_READROM 0x33 75 #define CMD_CONVERTTEMP 0x44 76 #define CMD_CPYSCRATCHPAD 0x48 77 #define CMD_WSCRATCHPAD 0x4e 78 #define CMD_MATCHROM 0x55 79 #define CMD_RPWRSUPPLY 0xb4 80 #define CMD_RECEEPROM 0xb8 81 #define CMD_RSCRATCHPAD 0xbe 82 #define CMD_SKIPROM 0xcc 83 #define CMD_ALARMSEARCH 0xec 84 #define CMD_SEARCHROM 0xf0 85 86 // I2C routines 87 void i2c_init(); 88 void i2c_shutdown(); 89 void i2c_waitForIdle(); 90 void i2c_start(); 91 void i2c_repStart(); 92 void i2c_stop(); 93 uns8 i2c_read( bit ack ); 94 void i2c_write( uns8 i2cWriteData ); 95 96 // MCP9802 address 97 #define I2C_ADR 0b10010110 98 // Power pin 99 #define PWR_SENSOR_TRIS TRISC.7 100 #define PWR_SENSOR_IO LATC.7 101 102 // Special temperature value to indicate a sensor error, compatible with IQRF sensor standard 103 #define ERROR_TEMPERATURE 0x8000 104 105 // TRUE if DS18B20 is enabled at runtime at startup, FALSE in case of MCP9802 106 bit isDS18B20; 107 // Final DS18B20 temperature value read by state machine 108 uns16 temperature; 109 110 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 111 //############################################################################################ 112 bit CustomDpaHandler() 113 //############################################################################################ 114 { 115 // 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) 116 #pragma updateBank default = UserBank_01 117 118 // Finite machine states 119 typedef enum 120 { 121 S_ResetConvertT = 0, 122 S_SkipRomConvertT, 123 S_CmdConvertT, 124 125 S_WaitConvertT, 126 127 S_ResetReadTemp, 128 S_SkipRomReadTemp, 129 S_CmdReadTemp, 130 S_Byte1ReadTemp, 131 S_Byte2ReadTemp 132 } TState; 133 134 // Handler presence mark 135 clrwdt(); 136 137 // Sleeping parameters, valid when Time != 0 138 static TPerOSSleep_Request PerOSSleep_Request; 139 // Finite machine state 140 static uns8 state; // = S_ResetConvertT = 0 141 // Pre-read lower temperature byte 142 static uns8 temperatureByteLow; 143 // Conversion timeout counter 144 static uns8 timeout; 145 146 // Detect DPA event to handle 147 switch ( GetDpaEvent() ) 148 { 149 // ------------------------------------------------- 150 case DpaEvent_Interrupt: 151 // Do an extra quick background interrupt work 152 // ! 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. 153 // ! 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. 154 // ! 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. 155 // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy. 156 // ! Make sure race condition does not occur when accessing those variables at other places. 157 // ! 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. 158 // ! Do not call any OS functions except setINDFx(). 159 // ! Do not use any OS variables especially for writing access. 160 // ! 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. 161 162 // ms per TMR6 interrupt 163 #define TICKS_LEN 10 164 165 // If TMR6 interrupt occurred 166 if ( TMR6IF ) 167 { 168 // Unmask interrupt 169 TMR6IF = 0; 170 // Decrement count 171 if ( timeout != 0 ) 172 timeout--; 173 } 174 return Carry; 175 176 // ------------------------------------------------- 177 case DpaEvent_Idle: 178 // Do a quick background work when RF packet is not received 179 180 // Should go to sleep? 181 if ( PerOSSleep_Request.Time != 0 ) 182 { 183 // Copy sleep parameters to the DPA request 184 _DpaMessage.PerOSSleep_Request.Time = PerOSSleep_Request.Time; 185 _DpaMessage.PerOSSleep_Request.Control = PerOSSleep_Request.Control; 186 // Switch off sleeping time=flag 187 PerOSSleep_Request.Time = 0; 188 // Finalize OS Sleep DPA Request 189 _DpaDataLength = sizeof( _DpaMessage.PerOSSleep_Request ); 190 _PNUM = PNUM_OS; 191 _PCMD = CMD_OS_SLEEP; 192 // Perform local DPA Request to go to sleep 193 DpaApiLocalRequest(); 194 } 195 196 // Run finite state machine to read temperature from DS18B20 at background so the temperature value is immediately ready for FRC 197 if ( !isDS18B20 ) 198 break; 199 200 // Make sure 1Wire data pin at LATX.y is low as it might be set by another PORTX.? pin manipulation 201 OneWire_IO_OUT = 0; 202 203 skip( state ); 204 #pragma computedGoto 1 205 goto _S_ResetConvertT; 206 goto _S_SkipRomConvertT; 207 goto _S_CmdConvertT; 208 goto _S_WaitConvertT; 209 goto _S_ResetReadTemp; 210 goto _S_SkipRomReadTemp; 211 goto _S_CmdReadTemp; 212 goto _S_Byte1ReadTemp; 213 goto _S_Byte2ReadTemp; 214 #pragma computedGoto 0 215 ; 216 // -------------- 217 _S_Byte2ReadTemp: 218 temperature.high8 = OneWireReadByte(); 219 temperature.low8 = temperatureByteLow; 220 221 ResetMachine: 222 state = S_ResetConvertT; 223 goto ExitMachine; 224 225 // -------------- 226 _S_ResetConvertT: 227 _S_ResetReadTemp: 228 if ( !OneWireReset() ) 229 { 230 _S_Error_Reset: 231 temperature = ERROR_TEMPERATURE; 232 goto ResetMachine; 233 } 234 goto NextState; 235 236 // -------------- 237 _S_SkipRomConvertT: 238 _S_SkipRomReadTemp: 239 // OneWire: Skip ROM 240 OneWireWriteByte( CMD_SKIPROM ); 241 goto NextState; 242 243 // -------------- 244 _S_CmdConvertT: 245 // OneWire: Convert temperature 246 OneWireWriteByte( CMD_CONVERTTEMP ); 247 // Setup timeout for approx 750 ms (the longest conversion time) 248 timeout = 2 + 750 / TICKS_LEN; 249 goto NextState; 250 251 // -------------- 252 _S_WaitConvertT: 253 if ( OneWireReadByte() == 0xff ) 254 goto NextState; 255 256 // Timeout? 257 if ( timeout == 0 ) 258 goto _S_Error_Reset; 259 260 goto ExitMachine; 261 262 // -------------- 263 _S_CmdReadTemp: 264 // OneWire: Read scratchpad 265 OneWireWriteByte( CMD_RSCRATCHPAD ); 266 goto NextState; 267 268 // -------------- 269 _S_Byte1ReadTemp: 270 temperatureByteLow = OneWireReadByte(); 271 goto NextState; 272 273 // -------------- 274 NextState: 275 ++state; 276 277 ExitMachine: 278 break; 279 280 // ------------------------------------------------- 281 case DpaEvent_Init: 282 // Do a one time initialization work before main loop starts 283 284 // Initialize sensors 285 // C5 (AN4) as input 286 moduleInfo(); 287 // Connected TR pins? 288 if ( !bufferINFO[5].7 ) 289 { 290 TRISC.6 = 1; 291 TRISB.4 = 1; 292 } 293 TRISA.5 = 1; 294 295 // C1 (AN0) as input 296 TRISA.0 = 1; 297 298 // Setup TMR6 to generate ticks on the background (ticks every 10ms) 299 #if F_OSC == 16000000 300 PR6 = 250 - 1; 301 T6CON = 0b0.1001.1.10; // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms 302 #else 303 #error Unsupported oscillator frequency 304 #endif 305 306 // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet) 307 if ( Ds18B20WriteConfig( 0b0.00.00000 ) ) 308 // DS18B20 is enabled 309 isDS18B20 = TRUE; 310 else 311 // Expect MCP9802 is enabled 312 i2c_init(); 313 314 break; 315 316 // ------------------------------------------------- 317 case DpaEvent_AfterSleep: 318 // Called after woken up after sleep 319 if ( !isDS18B20 ) 320 i2c_init(); 321 322 // Called on wake-up from sleep 323 TMR6IE = TRUE; 324 TMR6ON = TRUE; 325 break; 326 327 // ------------------------------------------------- 328 case DpaEvent_BeforeSleep: 329 // Called before going to sleep 330 if ( !isDS18B20 ) 331 i2c_shutdown(); 332 333 // ------------------------------------------------- 334 case DpaEvent_DisableInterrupts: 335 // Called when device needs all hardware interrupts to be disabled (before Reset, Restart and RFPGM) 336 // Must not use TMR6 any more 337 TMR6ON = FALSE; 338 TMR6IE = FALSE; 339 break; 340 341 // ------------------------------------------------- 342 case DpaEvent_DpaRequest: 343 // Called to interpret DPA request for peripherals 344 // ------------------------------------------------- 345 // Peripheral enumeration 346 if ( IsDpaEnumPeripheralsRequest() ) 347 { 348 // We implement 1 standard user peripheral 349 _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1; 350 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_SENSORS ); 351 _DpaMessage.EnumPeripheralsAnswer.HWPID = HWPID_IQRF_TECH__DEMO_DDC_SE01; 352 _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x0001; 353 354 DpaHandleReturnTRUE: 355 return TRUE; 356 } 357 // ------------------------------------------------- 358 // Get information about peripheral 359 else if ( IsDpaPeripheralInfoRequest() ) 360 { 361 if ( _PNUM == PNUM_STD_SENSORS ) 362 { 363 _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_STD_SENSORS; 364 _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE; 365 // Set standard version 366 _DpaMessage.PeripheralInfoAnswer.Par1 = 13; 367 goto DpaHandleReturnTRUE; 368 } 369 370 break; 371 } 372 // ------------------------------------------------- 373 else 374 { 375 // Handle peripheral command 376 377 // Supported peripheral number? 378 if ( _PNUM == PNUM_STD_SENSORS ) 379 { 380 // Supported commands? 381 switch ( _PCMD ) 382 { 383 // Invalid command 384 default: 385 // Return error 386 DpaApiReturnPeripheralError( ERROR_PCMD ); 387 388 // Sensor enumeration 389 case PCMD_STD_ENUMERATE: 390 if ( _DpaDataLength != 0 ) 391 goto _ERROR_DATA_LEN; 392 393 // Then just enumerate their types 394 _DpaMessage.Response.PData[0] = STD_SENSOR_TYPE_TEMPERATURE; 395 _DpaMessage.Response.PData[1] = STD_SENSOR_TYPE_TEMPERATURE; 396 _DpaMessage.Response.PData[2] = STD_SENSOR_TYPE_BINARYDATA7; 397 _DpaMessage.Response.PData[3] = STD_SENSOR_TYPE_BINARYDATA7; 398 W = SENSORS_COUNT; 399 goto _W2_DpaDataLength; 400 401 // Supported commands. They are handled the same way except one "if" at StoreValue() method 402 case PCMD_STD_SENSORS_READ_VALUES: 403 case PCMD_STD_SENSORS_READ_TYPES_AND_VALUES: 404 { 405 // No sensor bitmap specified? W = _DpaDataLength. Note: W is used to avoid MOVLB at next if 406 W = _DpaDataLength; 407 if ( W == 0 ) // Note: must not modify W 408 { 409 // Actually clears the bitmap 410 #if &_DpaMessage.Request.PData[0] != &bufferRF[0] 411 #error 412 #endif 413 clearBufferRF(); 414 // Simulate 1st only sensor in the bitmap (states of the other unimplemented sensors do not care) 415 _DpaMessage.Request.PData[0].0 = 1; 416 // Bitmap is 32 bits long = 4 417 _DpaDataLength = W = 4; 418 } 419 420 // Invalid bitmap (data) length (W = _DpaDataLength)? 421 if ( W != 4 ) 422 { 423 _ERROR_DATA_LEN: 424 // Return error 425 DpaApiReturnPeripheralError( ERROR_DATA_LEN ); 426 } 427 428 // Now read the sensors 429 430 // Prepare pointer (minus 1, see below) to store sensor (types and) values to 431 // 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... 432 // ... If it would be the case, then ERROR_FAIL must be returned 433 FSR1 = &_DpaMessage.Response.PData[-1]; 434 435 // Store bitmap of sensors to get values from 436 uns8 sensorsBitmap = FSR1[1]; 437 438 // 1st sensor (index 0) selected? 439 if ( sensorsBitmap.0 ) 440 { 441 Get0_TemperatureTR(); 442 StoreValue( STD_SENSOR_TYPE_TEMPERATURE ); 443 } 444 445 // 2nd sensor (index 1) selected? 446 if ( sensorsBitmap.1 ) 447 { 448 Get1_Temperature(); 449 StoreValue( STD_SENSOR_TYPE_TEMPERATURE ); 450 } 451 452 // 3rd sensor (index 2) selected? 453 if ( sensorsBitmap.2 ) 454 { 455 Get2_BinaryData_Light(); 456 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 ); 457 } 458 459 // 4th sensor (index 3) selected? 460 if ( sensorsBitmap.3 ) 461 { 462 Get3_BinaryData_Potentiometer(); 463 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 ); 464 } 465 466 // Compute returned data bytes count 467 W = FSR1L - ( (uns16)&_DpaMessage.Response.PData[0] & 0xFF ) + 1; 468 // Optimization: return W long block of bytes at response 469 _W2_DpaDataLength: 470 _DpaDataLength = W; 471 goto DpaHandleReturnTRUE; 472 } 473 } 474 } 475 476 break; 477 } 478 479 // ------------------------------------------------- 480 case DpaEvent_FrcValue: 481 // Called to get FRC value 482 483 // FSR1 for optimization purposes (avoid MOVLB) will be used to point to DataOutBeforeResponseFRC[0...] 484 FSR1 = &DataOutBeforeResponseFRC[0]; 485 // Check for correct FRC user data 486 if ( *FSR1++ /*DataOutBeforeResponseFRC[0]*/ == PNUM_STD_SENSORS ) 487 { 488 // Actually used sensor index 489 uns8 sensorIndex = FSR1[1] /*DataOutBeforeResponseFRC[2]*/ & 0x1f; 490 // Test sensor type 491 switch ( *FSR1++ /*DataOutBeforeResponseFRC[1]*/ ) 492 { 493 default: 494 goto DpaHandleReturnFALSE; 495 496 // No type specified, use specified index value 497 case 0x00: 498 goto _KeepSensorIndex; 499 500 // For other types make the index value based on the requested index value and sensor type 501 case STD_SENSOR_TYPE_TEMPERATURE: 502 if ( sensorIndex > 1 ) 503 goto DpaHandleReturnFALSE; 504 W = 0 + sensorIndex; 505 break; 506 507 case STD_SENSOR_TYPE_BINARYDATA7: 508 if ( sensorIndex > 1 ) 509 goto DpaHandleReturnFALSE; 510 W = 2 + sensorIndex; 511 break; 512 } 513 514 // New sensor index based on type and requested index 515 sensorIndex = W; 516 _KeepSensorIndex: 517 518 // Test for supported FRC commands 519 switch ( _PCMD ) 520 { 521 default: 522 goto DpaHandleReturnFALSE; 523 524 case FRC_STD_SENSORS_BIT: 525 case FRC_STD_SENSORS_1B: 526 case FRC_STD_SENSORS_2B: 527 switch ( sensorIndex ) 528 { 529 default: 530 goto DpaHandleReturnFALSE; 531 532 case 0: 533 Carry = Get0_TemperatureTR(); 534 break; 535 536 case 1: 537 Carry = Get1_Temperature(); 538 break; 539 540 case 2: 541 Carry = Get2_BinaryData_Light(); 542 break; 543 544 case 3: 545 Carry = Get3_BinaryData_Potentiometer(); 546 break; 547 } 548 549 // This type of FRC is not valid for the specified sensor 550 if ( !Carry ) 551 goto DpaHandleReturnFALSE; 552 553 break; 554 } 555 556 // Some sensor was measured by FRC, check if there is a sleep request 557 FSR1++; 558 if ( INDF1.0 ) // Note: same as DataOutBeforeResponseFRC[3].0 559 { 560 // Remember sleep parameters to go to sleep at the Idle event later 561 PerOSSleep_Request.Time.low8 = FSR1[4 - 3]; // Note: same as DataOutBeforeResponseFRC[4]; 562 PerOSSleep_Request.Time.high8 = FSR1[5 - 3]; // Note: same as DataOutBeforeResponseFRC[5]; 563 PerOSSleep_Request.Control = FSR1[6 - 3]; // Note: same as DataOutBeforeResponseFRC[6]; 564 } 565 } 566 567 break; 568 569 // ------------------------------------------------- 570 case DpaEvent_FrcResponseTime: 571 // Called to get FRC response time 572 573 // In this example the FRC commands are fast 574 switch ( DataOutBeforeResponseFRC[0] ) 575 { 576 case FRC_STD_SENSORS_BIT: 577 case FRC_STD_SENSORS_1B: 578 case FRC_STD_SENSORS_2B: 579 responseFRCvalue = _FRC_RESPONSE_TIME_40_MS; 580 break; 581 } 582 break; 583 } 584 DpaHandleReturnFALSE: 585 return FALSE; 586 } 587 588 //############################################################################################ 589 // Increases FSR1 and then stores the byte 590 void setPlusPlusINDF1( uns8 data @ W ) 591 //############################################################################################ 592 { 593 FSR1++; // Note: must not modify W 594 setINDF1( data ); 595 } 596 597 //############################################################################################ 598 // Stores measured sensor value byte(s) and optionally sensor type to the FSR[+1...] 599 void StoreValue( uns8 sensorType ) 600 //############################################################################################ 601 { 602 // Is the sensor type to be stored too? 603 if ( _PCMD == PCMD_STD_SENSORS_READ_TYPES_AND_VALUES ) 604 setPlusPlusINDF1( sensorType ); 605 606 // Store lower value byte 607 setPlusPlusINDF1( sensorValue.low8 ); 608 609 // No more value bytes to store? 610 if ( sensorType.7 != 0 ) 611 return; 612 613 // Store higher value byte 614 setPlusPlusINDF1( sensorValue.high8 ); 615 616 // 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. 617 } 618 619 //############################################################################################ 620 bit setFRCerror() 621 //############################################################################################ 622 { 623 responseFRCvalue2B = 2; 624 return TRUE; 625 } 626 627 //############################################################################################ 628 bit sensorError; 629 bit AdjustFrcTemperature() 630 //############################################################################################ 631 { 632 // Test for supported FRC commands 633 switch ( _PCMD ) 634 { 635 default: 636 return FALSE; 637 638 case FRC_STD_SENSORS_1B: 639 // Return sensor FRC value 1B 640 // Check for out of limits 641 if ( sensorError || (int16)sensorValue > (int16)( 105.5 * 16 ) || (int16)sensorValue < ( (int16)-20