1 // ******************************************************************************************** 2 // Custom DPA Handler code example - User peripheral implementation - Multiple Dallas 18B20 * 3 // ******************************************************************************************** 4 // Copyright (c) MICRORISC s.r.o. 5 // 6 // File: $RCSfile: CustomDpaHandler-UserPeripheral-18B20-Multiple.c,v $ 7 // Version: $Revision: 1.22 $ 8 // Date: $Date: 2021/04/26 15:13:51 $ 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 https://doc.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 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 == 16000000 189 // 16 MHz 190 // + 0.75us ( W=val, Call ) 191 for ( ;; ) 192 { // loop time 193 nop2(); // 0.50us 194 nop2(); // 1.00us 195 nop2(); // 1.50us 196 nop2(); // 2.00us 197 nop2(); // 2.50us 198 nop2(); // 3.00us 199 nop(); // 3.25us 200 if ( --val == 0 ) // + 0.75us (W--, BTFS ) 201 return; // + 0.25us 202 nop2(); // 4.50us 203 } // 5.00us (Goto) 204 #else 205 #error Unsupported oscillator frequency 206 #endif 207 } 208 //############################################################################################ 209 210 #define OneWireData0() { OneWire_TRIS = 0; } // 0.5us @ 16MHz 211 #define OneWireData1() { OneWire_TRIS = 1; } // 0.5us @ 16MHz 212 213 //############################################################################################ 214 void OneWireWriteBit( bit value ) 215 //############################################################################################ 216 { 217 // Next sequence is time precision critical 218 GIE = FALSE; 219 220 OneWireData0(); 221 nop2(); // 1 us [0.5 us] 222 #if F_OSC == 16000000 223 nop2(); // [1.0 us] 224 #endif 225 if ( value ) // 2.5 us [1.75us] 226 OneWireData1(); 227 228 // End of time precision critical sequence 229 GIE = TRUE; 230 231 // 60us minimum in total, does not have to be precise 232 Delay5us( ( 60 - 3 ) / 5 + 1 ); 233 OneWireData1(); 234 } 235 236 //############################################################################################ 237 void OneWireWriteByte( uns8 byte ) 238 //############################################################################################ 239 { 240 uns8 bitLoop = 8; 241 do 242 { 243 byte = rr( byte ); 244 OneWireWriteBit( Carry ); 245 } while ( --bitLoop != 0 ); 246 } 247 248 //############################################################################################ 249 bit OneWireReadBit() 250 //############################################################################################ 251 { 252 // Next sequence is time precision critical 253 GIE = FALSE; 254 255 OneWireData0(); 256 nop2(); // 1 us [0.5 us] 257 #if F_OSC == 16000000 258 nop2(); // [1.0 us] 259 #endif 260 OneWireData1(); // 2 us [1.5 us] 261 Delay5us( 15 / 5 ); // 17 us [16.5 us] 262 263 nop(); // 17.5 us [16.75 us] 264 if ( OneWire_IO_IN ) // 18.5 us [ 17.25 us] 265 { 266 GIE = TRUE; 267 // 60us minimum in total, does not have to be precise 268 Delay5us( ( 60 - 20 ) / 5 + 1 ); 269 return TRUE; 270 } 271 else 272 { 273 GIE = TRUE; 274 // 60us minimum in total, does not have to be precise 275 Delay5us( ( 60 - 20 ) / 5 + 1 ); 276 return FALSE; 277 } 278 } 279 280 //############################################################################################ 281 uns8 OneWireReadByte() 282 //############################################################################################ 283 { 284 uns8 result; 285 uns8 bitLoop = 8; 286 do 287 { 288 // Read one bit 289 Carry = OneWireReadBit(); 290 result = rr( result ); 291 } while ( --bitLoop != 0 ); 292 293 return result; 294 } 295 296 //############################################################################################ 297 bit OneWireResetAndCmd( uns8 ROM, uns8 cmd ) 298 //############################################################################################ 299 { 300 // Setting the pin once to low is enough 301 OneWire_IO_OUT = 0; 302 // Reset pulse 303 OneWireData0(); 304 Delay5us( 500 / 5 ); 305 // Reset pulse end 306 OneWireData1(); 307 // Next sequence is time precision critical 308 GIE = FALSE; 309 // Wait for presence pulse 310 Delay5us( 70 / 5 ); 311 // End of time precision critical sequence 312 GIE = TRUE; 313 // Presence pulse? 314 if ( OneWire_IO_IN ) 315 { 316 // No presence, finish initialization sequence 317 Delay5us( ( 500 - 70 ) / 5 ); 318 return FALSE; 319 } 320 else 321 { 322 // Presence OK, finish initialization sequence 323 Delay5us( ( 500 - 70 ) / 5 ); 324 switch ( ROM ) 325 { 326 case ROM_SKIP: 327 // OneWire: Skip ROM 328 OneWireWriteByte( CMD_SKIPROM ); 329 break; 330 331 case ROM_MATCH: 332 // OneWire: Match ROM (must not be used during search because usage o FSR0) 333 OneWireWriteByte( CMD_MATCHROM ); 334 FSR0 = _DpaMessage.Request.PData; 335 do 336 { 337 OneWireWriteByte( *FSR0++ ); 338 } while ( FSR0L != ( &_DpaMessage.Request.PData[8] & 0xFF ) ); 339 break; 340 341 case ROM_none: 342 break; 343 } 344 345 OneWireWriteByte( cmd ); 346 return TRUE; 347 } 348 } 349 350 //############################################################################################ 351 352 // One Wire CRC accumulator 353 static uns8 OneWireCrc; 354 355 //############################################################################################ 356 void UpdateOneWireCrc( uns8 value @ W ) 357 //############################################################################################ 358 { 359 // Based on http://www.dattalo.com/technical/software/pic/crc_8bit.c 360 OneWireCrc ^= W; 361 #pragma updateBank 0 /* OFF */ 362 value = 0; 363 if ( OneWireCrc.0 ) // 1 instruction 364 value ^= 0x5e; // 1 instruction 365 if ( OneWireCrc.1 ) // 1 instruction 366 value ^= 0xbc; // ... 367 if ( OneWireCrc.2 ) 368 value ^= 0x61; // ... 369 if ( OneWireCrc.3 ) 370 value ^= 0xc2; // ... 371 if ( OneWireCrc.4 ) 372 value ^= 0x9d; // ... 373 if ( OneWireCrc.5 ) 374 value ^= 0x23; // ... 375 if ( OneWireCrc.6 ) 376 value ^= 0x46; // ... 377 if ( OneWireCrc.7 ) 378 value ^= 0x8c; // 0x8C is reverse polynomial representation (normal is 0x31) 379 OneWireCrc = value; 380 #pragma updateBank 1 /* ON */ 381 } 382 383 384 //############################################################################################ 385 uns16 Ds18B20GetTemp() 386 //############################################################################################ 387 { 388 // OneWire: Convert T 389 if ( OneWireResetAndCmd( ROM_MATCH, CMD_CONVERTTEMP ) ) 390 { 391 // Wait for conversion to finish 392 startCapture(); 393 for ( ;; ) 394 { 395 // 1s timeout 396 captureTicks(); 397 if ( param3.low8 > ( 1000 / 10 ) ) 398 { 399 // Timeout 400 Carry = FALSE; 401 break; 402 } 403 404 if ( OneWireReadByte() == 0xff ) 405 { 406 // Temperature ready to be read 407 Carry = TRUE; 408 break; 409 } 410 } 411 412 // OneWire: Read Scratchpad 413 if ( Carry && OneWireResetAndCmd( ROM_MATCH, CMD_RSCRATCHPAD ) ) 414 { 415 // Read Scratchpad bytes 416 uns16 temperature; 417 418 // Initialize CRC 419 OneWireCrc = 0; 420 // Will read later rest of scratchpad bytes and update CRC (initialization is here to avoid MOVLB later) 421 uns8 byteLoop = 9 - 2; 422 423 // Temperature LSB into result & CRC 424 UpdateOneWireCrc( temperature.low8 = OneWireReadByte() ); 425 // Temperature MSB into result & CRC 426 UpdateOneWireCrc( temperature.high8 = OneWireReadByte() ); 427 428 // Read rest of Scratchpad 429 do 430 { 431 UpdateOneWireCrc( OneWireReadByte() ); 432 } while ( --byteLoop != 0 ); 433 434 // Check correct CRC 435 if ( OneWireCrc == 0 ) 436 return temperature; 437 } 438 } 439 // Some error occurred 440 return DS18B20_ERROR_TEMPERATURE; 441 } 442 443 //############################################################################################ 444 bit Ds18B20WriteConfig( uns8 value ) 445 //############################################################################################ 446 { 447 // OneWire: Write Scratchpad 448 if ( OneWireResetAndCmd( ROM_SKIP, CMD_WSCRATCHPAD ) ) 449 { 450 // Write TL = ? (we dot not care the value) 451 OneWireWriteByte( W ); 452 // Write TH = ? (we dot not care the value) 453 OneWireWriteByte( W ); 454 // Write configuration byte 455 OneWireWriteByte( value ); 456 457 // OneWire: Copy Scratchpad 458 if ( OneWireResetAndCmd( ROM_SKIP, CMD_CPYSCRATCHPAD ) ) 459 return TRUE; 460 } 461 return FALSE; 462 } 463 464 //############################################################################################ 465 uns8 OneWireSearch() 466 //############################################################################################ 467 { 468 // Based on Maxim Integrated APPLICATION NOTE 187 469 470 // reset the search state 471 uns8 LastDiscrepancy = 0; 472 // FSR0 points to the current ROM id 473 FSR0 = _DpaMessage.Response.PData; 474 // FSR1 points to the last read ROM id 475 // Start loop only if Search command is confirmed 476 for ( FSR1 = FSR0; OneWireResetAndCmd( ROM_none, CMD_SEARCHROM ); ) 477 { 478 // initialize for search 479 uns8 rom_byte_number = 0; 480 uns8 last_zero = 0; 481 OneWireCrc = 0; 482 uns8 id_bit_number = 1; 483 uns8 rom_byte_mask = 0x01; 484 485 // loop to do the search 486 do 487 { 488 // read a bit and its complement 489 bit id_bit = OneWireReadBit(); 490 bit cmp_id_bit = OneWireReadBit(); 491 492 // check for no devices on 1-wire 493 if ( id_bit && cmp_id_bit ) 494 break; 495 else 496 { 497 bit search_direction; 498 499 // all devices coupled have 0 or 1 500 if ( id_bit != cmp_id_bit ) 501 search_direction = id_bit; // bit write value for search 502 else 503 { 504 // if this discrepancy is before the Last Discrepancy on a previous next then pick the same as last time 505 if ( id_bit_number < LastDiscrepancy ) 506 search_direction = ( *FSR1 & rom_byte_mask ) != 0; 507 else 508 // if equal last pick 1, if not then pick 0 509 search_direction = id_bit_number == LastDiscrepancy; 510 511 // if 0 was picked then record its position in LastZero 512 if ( !search_direction ) 513 last_zero = id_bit_number; 514 } 515 516 // set or clear the bit in the ROM byte rom_byte_number with mask rom_byte_mask 517 if ( search_direction ) 518 setINDF0( *FSR0 | rom_byte_mask ); 519 else 520 setINDF0( *FSR0 & ~rom_byte_mask ); 521 522 // serial number search direction write bit 523 OneWireWriteBit( search_direction ); 524 525 // increment the byte counter id_bit_number 526 id_bit_number++; 527 // and shift the mask rom_byte_mask 528 rom_byte_mask <<= 1; 529 530 // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask 531 if ( rom_byte_mask == 0 ) 532 { 533 // Check for result overflow 534 if ( FSR0L == ( &_DpaMessage.Response.PData[sizeof( _DpaMessage.Response.PData )] & 0xff ) ) 535 break; 536 537 // compute CRC, move pointers, increment index and reset byte mask 538 UpdateOneWireCrc( *FSR0++ ); 539 FSR1++; 540 rom_byte_number++; 541 rom_byte_mask.0 = 1; 542 } 543 } 544 } while ( !rom_byte_number.3 /* !=8 */ ); // loop until through all ROM bytes 0-7 545 546 // if the search was successful then 547 if ( rom_byte_number.3 /* ==8 */ && OneWireCrc == 0 ) 548 { 549 // search successful so set LastDiscrepancy 550 LastDiscrepancy = last_zero; 551 552 // check for last device 553 if ( LastDiscrepancy == 0 ) 554 break; 555 } 556 else 557 break; 558 559 // FSR1 points to the last ROM ID read (optimized against FSR1 = FSR0 - 8) 560 FSR1 = FSR0; 561 FSR1 -= 8; 562 } 563 564 // return length of read bytes 565 return FSR0L - ( &_DpaMessage.Response.PData[0] & 0xff ); 566 } 567 568 //############################################################################################ 569 // 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) 570 #include "DPAcustomHandler.h" 571 //############################################################################################