1 // *********************************************************************************** 2 // Custom DPA Handler code example - User peripheral implementation - Dallas 18B20 * 3 // *********************************************************************************** 4 // Copyright (c) IQRF Tech s.r.o. 5 // 6 // File: $RCSfile: CustomDpaHandler-UserPeripheral-18B20-Idle.c,v $ 7 // Version: $Revision: 1.21 $ 8 // Date: $Date: 2017/08/31 08:32:32 $ 9 // 10 // Revision history: 11 // 2017/03/13 Release for DPA 3.00 12 // 2015/08/05 Release for DPA 2.20 13 // 2014/05/26 Release for DPA 2.11 14 // 2014/10/31 Release for DPA 2.10 15 // 2014/05/26 Release for DPA 2.01 16 // 17 // ********************************************************************* 18 19 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/ 20 21 // This example implements the user peripheral reading from Dallas 18B20 22 // The sensor is continuously read on the background at Idle event 23 // PNUM = 0x20 and PCMD = 0 returns 2 bytes with result read from Dallas 18B20 24 // Dallas 18B20 is connected to MCU pin (see below) with 10k pull-up resistor and not using parasite power 25 // Also user FRC command is implemented 26 27 // Default IQRF include (modify the path according to your setup) 28 #include "IQRF.h" 29 30 // Default DPA header (modify the path according to your setup) 31 #include "DPA.h" 32 // Default Custom DPA Handler header (modify the path according to your setup) 33 #include "DPAcustomHandler.h" 34 35 //############################################################################################ 36 37 // Special temperature value to indicate a sensor error 38 #define DS18B20_ERROR_TEMPERATURE 0xF800 39 40 // Sensor connected to PORT C.3 (compatible with DDC-SE-01) 41 #define OneWire_TRIS TRISC.3 42 #define OneWire_IO_IN PORTC.3 43 #define OneWire_IO_OUT LATC.3 44 45 // Writes sensor configuration (resolution) 46 bit Ds18B20WriteConfig( uns8 value ); 47 48 // Resets OneWire 49 bit OneWireReset(); 50 // Reads OneWire byte 51 uns8 OneWireReadByte(); 52 // Writes OneWire byte 53 void OneWireWriteByte( uns8 byte ); 54 55 // DS18B20 commands 56 #define CMD_READROM 0x33 57 #define CMD_CONVERTTEMP 0x44 58 #define CMD_CPYSCRATCHPAD 0x48 59 #define CMD_WSCRATCHPAD 0x4e 60 #define CMD_MATCHROM 0x55 61 #define CMD_RPWRSUPPLY 0xb4 62 #define CMD_RECEEPROM 0xb8 63 #define CMD_RSCRATCHPAD 0xbe 64 #define CMD_SKIPROM 0xcc 65 #define CMD_ALARMSEARCH 0xec 66 #define CMD_SEARCHROM 0xf0 67 68 #define TICKS_LEN 10 69 70 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 71 //############################################################################################ 72 bit CustomDpaHandler() 73 //############################################################################################ 74 { 75 // Finite machine states 76 typedef enum 77 { 78 S_ResetConvertT = 0, 79 S_SkipRomConvertT, 80 S_CmdConvertT, 81 82 S_WaitConvertT, 83 84 S_ResetReadTemp, 85 S_SkipRomReadTemp, 86 S_CmdReadTemp, 87 S_Byte1ReadTemp, 88 S_Byte2ReadTemp 89 } TState; 90 91 // Handler presence mark 92 clrwdt(); 93 94 // Finite machine state 95 static uns8 state; // = S_ResetConvertT = 0 96 97 // Pre-read lower temperature byte 98 static uns8 temperatureByteLow; 99 // Final temperature value 100 static uns16 temperature; 101 // Conversion timeout counter 102 static uns8 timeout; 103 104 // Detect DPA event to handle 105 switch ( GetDpaEvent() ) 106 { 107 // ------------------------------------------------- 108 case DpaEvent_Interrupt: 109 // Do an extra quick background interrupt work 110 // ! 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. 111 // ! 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. 112 // ! 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. 113 // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy. 114 // ! Make sure race condition does not occur when accessing those variables at other places. 115 // ! 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. 116 // ! Do not call any OS functions except setINDFx(). 117 // ! Do not use any OS variables especially for writing access. 118 // ! 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. 119 120 // If TMR6 interrupt occurred 121 if ( TMR6IF ) 122 { 123 // Unmask interrupt 124 TMR6IF = 0; 125 // Decrement count 126 if ( timeout != 0 ) 127 timeout--; 128 } 129 130 DpaHandleReturnTRUE: 131 return TRUE; 132 133 // ------------------------------------------------- 134 case DpaEvent_Idle: 135 // Do a quick background work when RF packet is not received 136 137 // Make sure 1Wire data pin at LATX.y is low as it might be set by another PORTX.? pin manipulation 138 OneWire_IO_OUT = 0; 139 140 // Run finite state machine 141 skip( state ); 142 #pragma computedGoto 1 143 goto _S_ResetConvertT; 144 goto _S_SkipRomConvertT; 145 goto _S_CmdConvertT; 146 goto _S_WaitConvertT; 147 goto _S_ResetReadTemp; 148 goto _S_SkipRomReadTemp; 149 goto _S_CmdReadTemp; 150 goto _S_Byte1ReadTemp; 151 goto _S_Byte2ReadTemp; 152 #pragma computedGoto 0 153 ; 154 // -------------- 155 _S_Byte2ReadTemp: 156 temperature.high8 = OneWireReadByte(); 157 temperature.low8 = temperatureByteLow; 158 159 ResetMachine: 160 state = S_ResetConvertT; 161 goto ExitMachine; 162 163 // -------------- 164 _S_ResetConvertT: 165 _S_ResetReadTemp: 166 if ( !OneWireReset() ) 167 { 168 _S_Error_Reset: 169 temperature = DS18B20_ERROR_TEMPERATURE; 170 goto ResetMachine; 171 } 172 goto NextState; 173 174 // -------------- 175 _S_SkipRomConvertT: 176 _S_SkipRomReadTemp: 177 // OneWire: Skip ROM 178 OneWireWriteByte( CMD_SKIPROM ); 179 goto NextState; 180 181 // -------------- 182 _S_CmdConvertT: 183 // OneWire: Convert temperature 184 OneWireWriteByte( CMD_CONVERTTEMP ); 185 // Setup timeout for approx 750 ms (the longest conversion time) 186 timeout = 2 + 750 / TICKS_LEN; 187 goto NextState; 188 189 // -------------- 190 _S_WaitConvertT: 191 if ( OneWireReadByte() == 0xff ) 192 goto NextState; 193 194 // Timeout? 195 if ( timeout == 0 ) 196 goto _S_Error_Reset; 197 198 goto ExitMachine; 199 200 // -------------- 201 _S_CmdReadTemp: 202 // OneWire: Read scratchpad 203 OneWireWriteByte( CMD_RSCRATCHPAD ); 204 goto NextState; 205 206 // -------------- 207 _S_Byte1ReadTemp: 208 temperatureByteLow = OneWireReadByte(); 209 goto NextState; 210 211 // -------------- 212 NextState: 213 ++state; 214 215 ExitMachine: 216 break; 217 218 // ------------------------------------------------- 219 case DpaEvent_Init: 220 // Setup TMR6 to generate ticks on the background (ticks every 10ms) 221 #if F_OSC == 8000000 222 PR6 = 125 - 1; 223 T6CON = 0b0.1001.1.10; // Prescaler 16, Postscaler 10, 16 * 10 * 125 = 20000 = 2MHz * 10ms 224 #elif F_OSC == 16000000 225 PR6 = 250 - 1; 226 T6CON = 0b0.1001.1.10; // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms 227 #else 228 #error Unsupported oscillator frequency 229 #endif 230 231 // Set error temperature 232 temperature = DS18B20_ERROR_TEMPERATURE; 233 // Do a one time initialization work before main loop starts 234 Ds18B20WriteConfig( 0b0.11.00000 ); // Setup DS18B20 for 12bit precision, conversion takes 750ms (see datasheet) 235 //Ds18B20WriteConfig( 0b0.00.00000 ); // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet) 236 break; 237 238 // ------------------------------------------------- 239 case DpaEvent_AfterSleep: 240 // Called after woken up after sleep 241 242 goto ResetMachine; 243 244 case DpaEvent_FrcValue: 245 // Called to get FRC value 246 247 if ( _PCMD == ( FRC_USER_BYTE_FROM + 0 ) ) 248 { 249 // Return temperature 250 // Dallas returns 12 bits, so we have to do >> 4 to return fixed part 251 252 W = swap( temperature.low8 ); // W = L3 L2 L1 L0 L7 L6 L5 L4 253 responseFRCvalue = W & 0x0F; // responseFRCvalue = 0 0 0 0 L7 L6 L5 L4 254 W = swap( temperature.high8 ); // W = H3 H2 H1 H0 H7 H6 H5 H4 255 W &= 0xF0; // W = H3 H2 H1 H0 0 0 0 0 256 responseFRCvalue |= W; // responseFRCvalue = H3 H2 H1 H0 L7 L6 L5 L4 257 // Avoid returning 0 258 if ( responseFRCvalue == 0 ) 259 responseFRCvalue = 127; 260 } 261 break; 262 263 // ------------------------------------------------- 264 case DpaEvent_FrcResponseTime: 265 // Called to get FRC response time 266 267 if ( DataOutBeforeResponseFRC[0] == ( FRC_USER_BYTE_FROM + 0 ) ) 268 responseFRCvalue = _FRC_RESPONSE_TIME_40_MS; 269 break; 270 271 // ------------------------------------------------- 272 case DpaEvent_DpaRequest: 273 // Called to interpret DPA request for peripherals 274 // ------------------------------------------------- 275 // Peripheral enumeration 276 if ( IsDpaEnumPeripheralsRequest() ) 277 { 278 // We implement 1 user peripheral 279 _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1; 280 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 ); 281 _DpaMessage.EnumPeripheralsAnswer.HWPID = 0x000F; 282 _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x2468; 283 284 goto DpaHandleReturnTRUE; 285 } 286 // ------------------------------------------------- 287 // Get information about peripheral 288 else if ( IsDpaPeripheralInfoRequest() ) 289 { 290 if ( _PNUM == PNUM_USER + 0 ) 291 { 292 _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA; 293 _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ; 294 goto DpaHandleReturnTRUE; 295 } 296 297 break; 298 } 299 // ------------------------------------------------- 300 else 301 { 302 // Handle peripheral command 303 if ( _PNUM == PNUM_USER + 0 ) 304 { 305 // Check command 306 switch ( _PCMD ) 307 { 308 case 0: 309 // ------------------------------------------------- 310 // Read temperature 311 if ( _DpaDataLength != 0 ) 312 DpaApiReturnPeripheralError( ERROR_DATA_LEN ); 313 314 uns16 temperatureStore @ _DpaMessage.Response.PData; 315 temperatureStore = temperature; 316 if ( temperatureStore == DS18B20_ERROR_TEMPERATURE ) 317 DpaApiReturnPeripheralError( ERROR_FAIL ); 318 319 _DpaDataLength = sizeof( temperature ); 320 goto DpaHandleReturnTRUE; 321 322 default: 323 // ------------------------------------------------- 324 // Invalid command 325 DpaApiReturnPeripheralError( ERROR_PCMD ); 326 } 327 } 328 } 329 } 330 331 return FALSE; 332 } 333 334 //############################################################################################ 335 // OneWire and Dallas 18B20 routines 336 //############################################################################################ 337 338 //############################################################################################ 339 void Delay5us( uns8 val @ W ) // Absolutely precise timing but val != 0 340 //############################################################################################ 341 { 342 #if F_OSC == 8000000 343 // 8 MHz 344 // + 1.5us ( W=val, Call ) 345 for ( ;; ) 346 { // loop time 347 nop2(); // 1us 348 nop(); // 1.5us 349 if ( --val == 0 ) // + 1.5us (W--, BTFS ) 350 return; // + 0.5us 351 nop2(); // 4us 352 } // 5us (Goto) 353 #elif F_OSC == 16000000 354 // 16 MHz 355 // + 0.75us ( W=val, Call ) 356 for ( ;; ) 357 { // loop time 358 nop2(); // 0.50us 359 nop2(); // 1.00us 360 nop2(); // 1.50us 361 nop2(); // 2.00us 362 nop2(); // 2.50us 363 nop2(); // 3.00us 364 nop(); // 3.25us 365 if ( --val == 0 ) // + 0.75us (W--, BTFS ) 366 return; // + 0.25us 367 nop2(); // 4.50us 368 } // 5.00us (Goto) 369 #else 370 #error Unsupported oscillator frequency 371 #endif 372 } 373 //############################################################################################ 374 375 #define OneWireData0() { OneWire_TRIS = 0; } // 1us [0.5us @ 16MHz] 376 #define OneWireData1() { OneWire_TRIS = 1; } // 1us [0.5us @ 16MHz] 377 378 //############################################################################################ 379 void OneWireWriteByte( uns8 byte ) 380 //############################################################################################ 381 { 382 uns8 bitLoop = 8; 383 do 384 { 385 // Next sequence is time precision critical 386 GIE = FALSE; 387 388 OneWireData0(); 389 nop2(); // 1 us [0.5 us] 390 #if F_OSC == 16000000 391 nop2(); // [1.0 us] 392 #endif 393 if ( byte.0 ) // 2.5 us [1.75us] 394 OneWireData1(); 395 396 // End of time precision critical sequence 397 GIE = TRUE; 398 399 // 60us minimum in total, does not have to be precise 400 Delay5us( ( 60 - 3 ) / 5 + 1 ); 401 402 OneWireData1(); 403 404 byte >>= 1; 405 } while ( --bitLoop != 0 ); 406 } 407 408 //############################################################################################ 409 uns8 OneWireReadByte() 410 //############################################################################################ 411 { 412 uns8 result; 413 uns8 bitLoop = 8; 414 do 415 { 416 // Next sequence is time precision critical 417 GIE = FALSE; 418 419 OneWireData0(); 420 nop2(); // 1 us [0.5 us] 421 #if F_OSC == 16000000 422 nop2(); // [1.0 us] 423 #endif 424 OneWireData1(); // 2 us [1.5 us] 425 Delay5us( 15 / 5 ); // 17 us [16.5 us] 426 427 Carry = 0; // 17.5 us [16.75 us] 428 if ( OneWire_IO_IN ) // 18.5 us [ 17.25 us] (condition must not modify Carry) 429 Carry = 1; 430 431 // End of time precision critical sequence 432 GIE = TRUE; // must not modify Carry 433 result = rr( result ); 434 435 // 60us minimum in total, does not have to be precise 436 Delay5us( ( 60 - 20 ) / 5 + 1 ); 437 } while ( --bitLoop != 0 ); 438 439 return result; 440 } 441 442 //############################################################################################ 443 bit OneWireReset() 444 //############################################################################################ 445 { 446 // Setting the pin once to low is enough 447 OneWire_IO_OUT = 0; 448 // Reset pulse 449 OneWireData0(); 450 Delay5us( 500 / 5 ); 451 // Reset pulse end 452 OneWireData1(); 453 // Next sequence is time precision critical 454 GIE = FALSE; 455 // Wait for presence pulse 456 Delay5us( 70 / 5 ); 457 // End of time precision critical sequence 458 GIE = TRUE; 459 // Presence pulse? 460 if ( OneWire_IO_IN ) 461 { 462 // No presence, finish initialization sequence 463 Delay5us( ( 500 - 70 ) / 5 ); 464 return FALSE; 465 } 466 else 467 { 468 // Presence OK, finish initialization sequence 469 Delay5us( ( 500 - 70 ) / 5 ); 470 return TRUE; 471 } 472 } 473 474 //############################################################################################ 475 void OneWireCmd( uns8 cmd ) 476 //############################################################################################ 477 { 478 // OneWire: Skip ROM 479 OneWireWriteByte( CMD_SKIPROM ); 480 // OneWire: Send command 481 OneWireWriteByte( cmd ); 482 } 483 484 //############################################################################################ 485 bit Ds18B20WriteConfig( uns8 value ) 486 //############################################################################################ 487 { 488 if ( OneWireReset() ) 489 { 490 // Write Scratchpad 491 OneWireCmd( CMD_WSCRATCHPAD ); 492 493 // Write TL = ? (we dot not care the value) 494 OneWireWriteByte( W ); 495 // Write TH = ? (we dot not care the value) 496 OneWireWriteByte( W ); 497 // Write Config byte 498 OneWireWriteByte( value ); 499 500 if ( OneWireReset() ) 501 { 502 // Copy Scratchpad 503 OneWireCmd( CMD_CPYSCRATCHPAD ); 504 return TRUE; 505 } 506 } 507 return FALSE; 508 } 509 510 //############################################################################################ 511 // 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) 512 #include "DPAcustomHandler.h" 513 //############################################################################################