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