1 // ******************************************************************************************** 2 // Custom DPA Handler code example - User peripheral implementation - Multiple Dallas 18B20 * 3 // ******************************************************************************************** 4 // Copyright (c) IQRF Tech s.r.o. 5 // 6 // File: $RCSfile: CustomDpaHandler-UserPeripheral-18B20-Multiple.c,v $ 7 // Version: $Revision: 1.15 $ 8 // Date: $Date: 2017/08/31 08:32:32 $ 9 // 10 // Revision history: 11 // 2017/03/13 Release for DPA 3.00 12 // 2016/09/12 Release for DPA 2.28 13 // 2015/12/16 Release for DPA 2.24 14 // 15 // ********************************************************************* 16 17 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/ 18 19 // This example implements the user peripheral reading temperature from one Dallas 18B20 temperature sensor 20 // There are more such sensors connected to the 1-Wire bus 21 // * PNUM = 0x20, PCMD = 0 and PDATA="8 byte ROM code" of the sensor, returns 2 bytes with temperature value read from the selected Dallas 18B20 22 // * PNUM = 0x20, PCMD = 1 returns list of up to seven "8 byte ROM codes" of connected 1-Wire devices 23 // Dallas 18B20 sensors are connected to MCU pin (see below) with 10k pull-up resistor and not using parasite power 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 33 //############################################################################################ 34 35 // Special temperature value to indicate a sensor error 36 #define DS18B20_ERROR_TEMPERATURE 0xF800 37 38 // Sensor connected to PORT C.3 (compatible with multiple DDC-SE-01) 39 #define OneWire_TRIS TRISC.3 40 #define OneWire_IO_IN PORTC.3 41 #define OneWire_IO_OUT LATC.3 42 43 // Reads temperature from sensor 44 uns16 Ds18B20GetTemp(); 45 // Writes sensor configuration (resolution) 46 bit Ds18B20WriteConfig( uns8 value ); 47 // Searches 1-Wire bus device IDs 48 uns8 OneWireSearch(); 49 50 // DS18B20 commands 51 #define CMD_READROM 0x33 52 #define CMD_CONVERTTEMP 0x44 53 #define CMD_CPYSCRATCHPAD 0x48 54 #define CMD_WSCRATCHPAD 0x4e 55 #define CMD_MATCHROM 0x55 56 #define CMD_RPWRSUPPLY 0xb4 57 #define CMD_RECEEPROM 0xb8 58 #define CMD_RSCRATCHPAD 0xbe 59 #define CMD_SKIPROM 0xcc 60 #define CMD_ALARMSEARCH 0xec 61 #define CMD_SEARCHROM 0xf0 62 63 // What to send to 1-Wire bus after Reset condition and before sending command 64 #define ROM_SKIP 0 65 #define ROM_MATCH 1 66 #define ROM_none 2 67 68 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 69 //############################################################################################ 70 bit CustomDpaHandler() 71 //############################################################################################ 72 { 73 // Handler presence mark 74 clrwdt(); 75 76 // Detect DPA event to handle 77 switch ( GetDpaEvent() ) 78 { 79 // ------------------------------------------------- 80 case DpaEvent_Interrupt: 81 // Do an extra quick background interrupt work 82 // ! 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. 83 // ! 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. 84 // ! 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. 85 // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy. 86 // ! Make sure race condition does not occur when accessing those variables at other places. 87 // ! 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. 88 // ! Do not call any OS functions except setINDFx(). 89 // ! Do not use any OS variables especially for writing access. 90 // ! 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. 91 92 DpaHandleReturnTRUE: 93 return TRUE; 94 95 // ------------------------------------------------- 96 case DpaEvent_Init: 97 // Do a one time initialization work before main loop starts 98 99 // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet) 100 Ds18B20WriteConfig( 0b0.00.00000 ); 101 102 //// Setup DS18B20 for 12bit precision, conversion takes 750ms (see datasheet) 103 //Ds18B20WriteConfig( 0b0.11.00000 ); 104 105 break; 106 107 // ------------------------------------------------- 108 case DpaEvent_DpaRequest: 109 // Called to interpret DPA request for peripherals 110 // ------------------------------------------------- 111 // Peripheral enumeration 112 if ( IsDpaEnumPeripheralsRequest() ) 113 { 114 // We implement 1 user peripheral 115 _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1; 116 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 ); 117 _DpaMessage.EnumPeripheralsAnswer.HWPID = 0x000F; 118 _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0xaabb; 119 120 goto DpaHandleReturnTRUE; 121 } 122 // ------------------------------------------------- 123 // Get information about peripheral 124 else if ( IsDpaPeripheralInfoRequest() ) 125 { 126 if ( _PNUM == PNUM_USER + 0 ) 127 { 128 _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA; 129 _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ; 130 goto DpaHandleReturnTRUE; 131 } 132 133 break; 134 } 135 // ------------------------------------------------- 136 else 137 { 138 // Handle peripheral command 139 if ( _PNUM == PNUM_USER + 0 ) 140 { 141 // Check command 142 switch ( _PCMD ) 143 { 144 case 0: 145 // ------------------------------------------------- 146 // Read temperature 147 if ( _DpaDataLength != 8 ) 148 DpaApiReturnPeripheralError( ERROR_DATA_LEN ); 149 150 uns16 temperature @ _DpaMessage.Response.PData; 151 temperature = Ds18B20GetTemp(); 152 if ( temperature == DS18B20_ERROR_TEMPERATURE ) 153 DpaApiReturnPeripheralError( ERROR_FAIL ); 154 155 _DpaDataLength = sizeof( temperature ); 156 goto DpaHandleReturnTRUE; 157 158 case 1: 159 // ------------------------------------------------- 160 // Read 1-Wire ROM codes 161 if ( _DpaDataLength != 0 ) 162 DpaApiReturnPeripheralError( ERROR_DATA_LEN ); 163 164 _DpaDataLength = OneWireSearch(); 165 goto DpaHandleReturnTRUE; 166 167 default: 168 // ------------------------------------------------- 169 // Invalid command 170 DpaApiReturnPeripheralError( ERROR_PCMD ); 171 } 172 } 173 } 174 } 175 176 return FALSE; 177 } 178 179 //############################################################################################ 180 // OneWire and Dallas 18B20 routines 181 // If there is an interrupt running that consumes a lot of MCU time (>450us once per 1ms) then use GIE=1/GIE=0 at whole OneWireResetAndCmd() for sure 182 //############################################################################################ 183 184 //############################################################################################ 185 void Delay5us( uns8 val @ W ) // Absolutely precise timing but val != 0 186 //############################################################################################ 187 { 188 #if F_OSC == 8000000 189 // 8 MHz 190 // + 1.5us ( W=val, Call ) 191 for ( ;; ) 192 { // loop time 193 nop2(); // 1us 194 nop(); // 1.5us 195 if ( --val == 0 ) // + 1.5us (W--, BTFS ) 196 return; // + 0.5us 197 nop2(); // 4us 198 } // 5us (Goto) 199 #elif F_OSC == 16000000 200 // 16 MHz 201 // + 0.75us ( W=val, Call ) 202 for ( ;; ) 203 { // loop time 204 nop2(); // 0.50us 205 nop2(); // 1.00us 206 nop2(); // 1.50us 207 nop2(); // 2.00us 208 nop2(); // 2.50us 209 nop2(); // 3.00us 210 nop(); // 3.25us 211 if ( --val == 0 ) // + 0.75us (W--, BTFS ) 212 return; // + 0.25us 213 nop2(); // 4.50us 214 } // 5.00us (Goto) 215 #else 216 #error Unsupported oscillator frequency 217 #endif 218 } 219 //############################################################################################ 220 221 #define OneWireData0() { OneWire_TRIS = 0; } // 1us [0.5us @ 16MHz] 222 #define OneWireData1() { OneWire_TRIS = 1; } // 1us [0.5us @ 16MHz] 223 224 //############################################################################################ 225 void OneWireWriteBit( bit value ) 226 //############################################################################################ 227 { 228 // Next sequence is time precision critical 229 GIE = FALSE; 230 231 OneWireData0(); 232 nop2(); // 1 us [0.5 us] 233 #if F_OSC == 16000000 234 nop2(); // [1.0 us] 235 #endif 236 if ( value ) // 2.5 us [1.75us] 237 OneWireData1(); 238 239 // End of time precision critical sequence 240 GIE = TRUE; 241 242 // 60us minimum in total, does not have to be precise 243 Delay5us( ( 60 - 3 ) / 5 + 1 ); 244 OneWireData1(); 245 } 246 247 //############################################################################################ 248 void OneWireWriteByte( uns8 byte ) 249 //############################################################################################ 250 { 251 uns8 bitLoop = 8; 252 do 253 { 254 byte = rr( byte ); 255 OneWireWriteBit( Carry ); 256 } while ( --bitLoop != 0 ); 257 } 258 259 //############################################################################################ 260 bit OneWireReadBit() 261 //############################################################################################ 262 { 263 // Next sequence is time precision critical 264 GIE = FALSE; 265 266 OneWireData0(); 267 nop2(); // 1 us [0.5 us] 268 #if F_OSC == 16000000 269 nop2(); // [1.0 us] 270 #endif 271 OneWireData1(); // 2 us [1.5 us] 272 Delay5us( 15 / 5 ); // 17 us [16.5 us] 273 274 nop(); // 17.5 us [16.75 us] 275 if ( OneWire_IO_IN ) // 18.5 us [ 17.25 us] 276 { 277 GIE = TRUE; 278 // 60us minimum in total, does not have to be precise 279 Delay5us( ( 60 - 20 ) / 5 + 1 ); 280 return TRUE; 281 } 282 else 283 { 284 GIE = TRUE; 285 // 60us minimum in total, does not have to be precise 286 Delay5us( ( 60 - 20 ) / 5 + 1 ); 287 return FALSE; 288 } 289 } 290 291 //############################################################################################ 292 uns8 OneWireReadByte() 293 //############################################################################################ 294 { 295 uns8 result; 296 uns8 bitLoop = 8; 297 do 298 { 299 // Read one bit 300 Carry = OneWireReadBit(); 301 result = rr( result ); 302 } while ( --bitLoop != 0 ); 303 304 return result; 305 } 306 307 //############################################################################################ 308 bit OneWireResetAndCmd( uns8 ROM, uns8 cmd ) 309 //############################################################################################ 310 { 311 // Setting the pin once to low is enough 312 OneWire_IO_OUT = 0; 313 // Reset pulse 314 OneWireData0(); 315 Delay5us( 500 / 5 ); 316 // Reset pulse end 317 OneWireData1(); 318 // Next sequence is time precision critical 319 GIE = FALSE; 320 // Wait for presence pulse 321 Delay5us( 70 / 5 ); 322 // End of time precision critical sequence 323 GIE = TRUE; 324 // Presence pulse? 325 if ( OneWire_IO_IN ) 326 { 327 // No presence, finish initialization sequence 328 Delay5us( ( 500 - 70 ) / 5 ); 329 return FALSE; 330 } 331 else 332 { 333 // Presence OK, finish initialization sequence 334 Delay5us( ( 500 - 70 ) / 5 ); 335 switch ( ROM ) 336 { 337 case ROM_SKIP: 338 // OneWire: Skip ROM 339 OneWireWriteByte( CMD_SKIPROM ); 340 break; 341 342 case ROM_MATCH: 343 // OneWire: Match ROM (must not be used during search because usage o FSR0) 344 OneWireWriteByte( CMD_MATCHROM ); 345 FSR0 = _DpaMessage.Request.PData; 346 do 347 { 348 OneWireWriteByte( *FSR0++ ); 349 } while ( FSR0L != ( &_DpaMessage.Request.PData[8] & 0xFF ) ); 350 break; 351 352 case ROM_none: 353 break; 354 } 355 356 OneWireWriteByte( cmd ); 357 return TRUE; 358 } 359 } 360 361 //############################################################################################ 362 363 // One Wire CRC accumulator 364 static uns8 OneWireCrc; 365 366 //############################################################################################ 367 void UpdateOneWireCrc( uns8 value @ W ) 368 //############################################################################################ 369 { 370 // Based on http://www.dattalo.com/technical/software/pic/crc_8bit.c 371 OneWireCrc ^= W; 372 #pragma updateBank 0 /* OFF */ 373 value = 0; 374 if ( OneWireCrc.0 ) // 1 instruction 375 value ^= 0x5e; // 1 instruction 376 if ( OneWireCrc.1 ) // 1 instruction 377 value ^= 0xbc; // ... 378 if ( OneWireCrc.2 ) 379 value ^= 0x61; // ... 380 if ( OneWireCrc.3 ) 381 value ^= 0xc2; // ... 382 if ( OneWireCrc.4 ) 383 value ^= 0x9d; // ... 384 if ( OneWireCrc.5 ) 385 value ^= 0x23; // ... 386 if ( OneWireCrc.6 ) 387 value ^= 0x46; // ... 388 if ( OneWireCrc.7 ) 389 value ^= 0x8c; // 0x8C is reverse polynomial representation (normal is 0x31) 390 OneWireCrc = value; 391 #pragma updateBank 1 /* ON */ 392 } 393 394 395 //############################################################################################ 396 uns16 Ds18B20GetTemp() 397 //############################################################################################ 398 { 399 // OneWire: Convert T 400 if ( OneWireResetAndCmd( ROM_MATCH, CMD_CONVERTTEMP ) ) 401 { 402 // Wait for conversion to finish 403 startCapture(); 404 for ( ;; ) 405 { 406 // 1s timeout 407 captureTicks(); 408 if ( param3.low8 > ( 1000 / 10 ) ) 409 { 410 // Timeout 411 Carry = FALSE; 412 break; 413 } 414 415 if ( OneWireReadByte() == 0xff ) 416 { 417 // Temperature ready to be read 418 Carry = TRUE; 419 break; 420 } 421 } 422 423 // OneWire: Read Scratchpad 424 if ( Carry && OneWireResetAndCmd( ROM_MATCH, CMD_RSCRATCHPAD ) ) 425 { 426 // Read Scratchpad bytes 427 uns16 temperature; 428 429 // Initialize CRC 430 OneWireCrc = 0; 431 // Will read later rest of scratchpad bytes and update CRC (initialization is here to avoid MOVLB later) 432 uns8 byteLoop = 9 - 2; 433 434 // Temperature LSB into result & CRC 435 UpdateOneWireCrc( temperature.low8 = OneWireReadByte() ); 436 // Temperature MSB into result & CRC 437 UpdateOneWireCrc( temperature.high8 = OneWireReadByte() ); 438 439 // Read rest of Scratchpad 440 do 441 { 442 UpdateOneWireCrc( OneWireReadByte() ); 443 } while ( --byteLoop != 0 ); 444 445 // Check correct CRC 446 if ( OneWireCrc == 0 ) 447 return temperature; 448 } 449 } 450 // Some error occurred 451 return DS18B20_ERROR_TEMPERATURE; 452 } 453 454 //############################################################################################ 455 bit Ds18B20WriteConfig( uns8 value ) 456 //############################################################################################ 457 { 458 // OneWire: Write Scratchpad 459 if ( OneWireResetAndCmd( ROM_SKIP, CMD_WSCRATCHPAD ) ) 460 { 461 // Write TL = ? (we dot not care the value) 462 OneWireWriteByte( W ); 463 // Write TH = ? (we dot not care the value) 464 OneWireWriteByte( W ); 465 // Write configuration byte 466 OneWireWriteByte( value ); 467 468 // OneWire: Copy Scratchpad 469 if ( OneWireResetAndCmd( ROM_SKIP, CMD_CPYSCRATCHPAD ) ) 470 return TRUE; 471 } 472 return FALSE; 473 } 474 475 //############################################################################################ 476 uns8 OneWireSearch() 477 //############################################################################################ 478 { 479 // Based on Maxim Integrated APPLICATION NOTE 187 480 481 // reset the search state 482 uns8 LastDiscrepancy = 0; 483 // FSR0 points to the current ROM id 484 FSR0 = _DpaMessage.Response.PData; 485 // FSR1 points to the last read ROM id 486 // Start loop only if Search command is confirmed 487 for ( FSR1 = FSR0; OneWireResetAndCmd( ROM_none, CMD_SEARCHROM ); ) 488 { 489 // initialize for search 490 uns8 rom_byte_number = 0; 491 uns8 last_zero = 0; 492 OneWireCrc = 0; 493 uns8 id_bit_number = 1; 494 uns8 rom_byte_mask = 0x01; 495 496 // loop to do the search 497 do 498 { 499 // read a bit and its complement 500 bit id_bit = OneWireReadBit(); 501 bit cmp_id_bit = OneWireReadBit(); 502 503 // check for no devices on 1-wire 504 if ( id_bit && cmp_id_bit ) 505 break; 506 else 507 { 508 bit search_direction; 509 510 // all devices coupled have 0 or 1 511 if ( id_bit != cmp_id_bit ) 512 search_direction = id_bit; // bit write value for search 513 else 514 { 515 // if this discrepancy is before the Last Discrepancy on a previous next then pick the same as last time 516 if ( id_bit_number < LastDiscrepancy ) 517 search_direction = ( *FSR1 & rom_byte_mask ) != 0; 518 else 519 // if equal to last pick 1, if not then pick 0 520 search_direction = id_bit_number == LastDiscrepancy; 521 522 // if 0 was picked then record its position in LastZero 523 if ( !search_direction ) 524 last_zero = id_bit_number; 525 } 526 527 // set or clear the bit in the ROM byte rom_byte_number with mask rom_byte_mask 528 if ( search_direction ) 529 setINDF0( *FSR0 | rom_byte_mask ); 530 else 531 setINDF0( *FSR0 & ~rom_byte_mask ); 532 533 // serial number search direction write bit 534 OneWireWriteBit( search_direction ); 535 536 // increment the byte counter id_bit_number 537 id_bit_number++; 538 // and shift the mask rom_byte_mask 539 rom_byte_mask <<= 1; 540 541 // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask 542 if ( rom_byte_mask == 0 ) 543 { 544 // Check for result overflow 545 if ( FSR0L == ( &_DpaMessage.Response.PData[sizeof( _DpaMessage.Response.PData )] & 0xff ) ) 546 break; 547 548 // compute CRC, move pointers, increment index and reset byte mask 549 UpdateOneWireCrc( *FSR0++ ); 550 FSR1++; 551 rom_byte_number++; 552 rom_byte_mask.0 = 1; 553 } 554 } 555 } while ( !rom_byte_number.3 /* !=8 */ ); // loop until through all ROM bytes 0-7 556 557 // if the search was successful then 558 if ( rom_byte_number.3 /* ==8 */ && OneWireCrc == 0 ) 559 { 560 // search successful so set LastDiscrepancy 561 LastDiscrepancy = last_zero; 562 563 // check for last device 564 if ( LastDiscrepancy == 0 ) 565 break; 566 } 567 else 568 break; 569 570 // FSR1 points to the last ROM ID read (optimized against FSR1 = FSR0 - 8) 571 FSR1 = FSR0; 572 FSR1 -= 8; 573 } 574 575 // return length of read bytes 576 return FSR0L - ( &_DpaMessage.Response.PData[0] & 0xff ); 577 } 578 579 //############################################################################################ 580 // 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) 581 #include "DPAcustomHandler.h" 582 //############################################################################################