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.24 $ 8 // Date: $Date: 2019/01/04 17:14:37 $ 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 == 16000000 222 PR6 = 250 - 1; 223 T6CON = 0b0.1001.1.10; // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms 224 #else 225 #error Unsupported oscillator frequency 226 #endif 227 228 // Set error temperature 229 temperature = DS18B20_ERROR_TEMPERATURE; 230 // Do a one time initialization work before main loop starts 231 Ds18B20WriteConfig( 0b0.11.00000 ); // Setup DS18B20 for 12bit precision, conversion takes 750ms (see datasheet) 232 //Ds18B20WriteConfig( 0b0.00.00000 ); // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet) 233 break; 234 235 // ------------------------------------------------- 236 case DpaEvent_AfterSleep: 237 // Called after woken up after sleep 238 239 goto ResetMachine; 240 241 case DpaEvent_FrcValue: 242 // Called to get FRC value 243 244 if ( _PCMD == ( FRC_USER_BYTE_FROM + 0 ) ) 245 { 246 // Return temperature 247 // Dallas returns 12 bits, so we have to do >> 4 to return fixed part 248 249 W = swap( temperature.low8 ); // W = L3 L2 L1 L0 L7 L6 L5 L4 250 responseFRCvalue = W & 0x0F; // responseFRCvalue = 0 0 0 0 L7 L6 L5 L4 251 W = swap( temperature.high8 ); // W = H3 H2 H1 H0 H7 H6 H5 H4 252 W &= 0xF0; // W = H3 H2 H1 H0 0 0 0 0 253 responseFRCvalue |= W; // responseFRCvalue = H3 H2 H1 H0 L7 L6 L5 L4 254 // Avoid returning 0 255 if ( responseFRCvalue == 0 ) 256 responseFRCvalue = 127; 257 } 258 break; 259 260 // ------------------------------------------------- 261 case DpaEvent_FrcResponseTime: 262 // Called to get FRC response time 263 264 if ( DataOutBeforeResponseFRC[0] == ( FRC_USER_BYTE_FROM + 0 ) ) 265 responseFRCvalue = _FRC_RESPONSE_TIME_40_MS; 266 break; 267 268 // ------------------------------------------------- 269 case DpaEvent_DpaRequest: 270 // Called to interpret DPA request for peripherals 271 // ------------------------------------------------- 272 // Peripheral enumeration 273 if ( IsDpaEnumPeripheralsRequest() ) 274 { 275 // We implement 1 user peripheral 276 _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1; 277 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 ); 278 _DpaMessage.EnumPeripheralsAnswer.HWPID = 0x000F; 279 _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x2468; 280 281 goto DpaHandleReturnTRUE; 282 } 283 // ------------------------------------------------- 284 // Get information about peripheral 285 else if ( IsDpaPeripheralInfoRequest() ) 286 { 287 if ( _PNUM == PNUM_USER + 0 ) 288 { 289 _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA; 290 _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ; 291 goto DpaHandleReturnTRUE; 292 } 293 294 break; 295 } 296 // ------------------------------------------------- 297 else 298 { 299 // Handle peripheral command 300 if ( _PNUM == PNUM_USER + 0 ) 301 { 302 // Check command 303 switch ( _PCMD ) 304 { 305 case 0: 306 // ------------------------------------------------- 307 // Read temperature 308 if ( _DpaDataLength != 0 ) 309 DpaApiReturnPeripheralError( ERROR_DATA_LEN ); 310 311 uns16 temperatureStore @ _DpaMessage.Response.PData; 312 temperatureStore = temperature; 313 if ( temperatureStore == DS18B20_ERROR_TEMPERATURE ) 314 DpaApiReturnPeripheralError( ERROR_FAIL ); 315 316 _DpaDataLength = sizeof( temperature ); 317 goto DpaHandleReturnTRUE; 318 319 default: 320 // ------------------------------------------------- 321 // Invalid command 322 DpaApiReturnPeripheralError( ERROR_PCMD ); 323 } 324 } 325 } 326 } 327 328 return FALSE; 329 } 330 331 //############################################################################################ 332 // OneWire and Dallas 18B20 routines 333 //############################################################################################ 334 335 //############################################################################################ 336 void Delay5us( uns8 val @ W ) // Absolutely precise timing but val != 0 337 //############################################################################################ 338 { 339 #if F_OSC == 16000000 340 // 16 MHz 341 // + 0.75us ( W=val, Call ) 342 for ( ;; ) 343 { // loop time 344 nop2(); // 0.50us 345 nop2(); // 1.00us 346 nop2(); // 1.50us 347 nop2(); // 2.00us 348 nop2(); // 2.50us 349 nop2(); // 3.00us 350 nop(); // 3.25us 351 if ( --val == 0 ) // + 0.75us (W--, BTFS ) 352 return; // + 0.25us 353 nop2(); // 4.50us 354 } // 5.00us (Goto) 355 #else 356 #error Unsupported oscillator frequency 357 #endif 358 } 359 //############################################################################################ 360 361 #define OneWireData0() { OneWire_TRIS = 0; } // 0.5us @ 16MHz 362 #define OneWireData1() { OneWire_TRIS = 1; } // 0.5us @ 16MHz 363 364 //############################################################################################ 365 void OneWireWriteByte( uns8 byte ) 366 //############################################################################################ 367 { 368 uns8 bitLoop = 8; 369 do 370 { 371 // Next sequence is time precision critical 372 GIE = FALSE; 373 374 OneWireData0(); 375 nop2(); // 1 us [0.5 us] 376 #if F_OSC == 16000000 377 nop2(); // [1.0 us] 378 #endif 379 if ( byte.0 ) // 2.5 us [1.75us] 380 OneWireData1(); 381 382 // End of time precision critical sequence 383 GIE = TRUE; 384 385 // 60us minimum in total, does not have to be precise 386 Delay5us( ( 60 - 3 ) / 5 + 1 ); 387 388 OneWireData1(); 389 390 byte >>= 1; 391 } while ( --bitLoop != 0 ); 392 } 393 394 //############################################################################################ 395 uns8 OneWireReadByte() 396 //############################################################################################ 397 { 398 uns8 result; 399 uns8 bitLoop = 8; 400 do 401 { 402 // Next sequence is time precision critical 403 GIE = FALSE; 404 405 OneWireData0(); 406 nop2(); // 1 us [0.5 us] 407 #if F_OSC == 16000000 408 nop2(); // [1.0 us] 409 #endif 410 OneWireData1(); // 2 us [1.5 us] 411 Delay5us( 15 / 5 ); // 17 us [16.5 us] 412 413 Carry = 0; // 17.5 us [16.75 us] 414 if ( OneWire_IO_IN ) // 18.5 us [ 17.25 us] (condition must not modify Carry) 415 Carry = 1; 416 417 // End of time precision critical sequence 418 GIE = TRUE; // must not modify Carry 419 result = rr( result ); 420 421 // 60us minimum in total, does not have to be precise 422 Delay5us( ( 60 - 20 ) / 5 + 1 ); 423 } while ( --bitLoop != 0 ); 424 425 return result; 426 } 427 428 //############################################################################################ 429 bit OneWireReset() 430 //############################################################################################ 431 { 432 // Setting the pin once to low is enough 433 OneWire_IO_OUT = 0; 434 // Reset pulse 435 OneWireData0(); 436 Delay5us( 500 / 5 ); 437 // Reset pulse end 438 OneWireData1(); 439 // Next sequence is time precision critical 440 GIE = FALSE; 441 // Wait for presence pulse 442 Delay5us( 70 / 5 ); 443 // End of time precision critical sequence 444 GIE = TRUE; 445 // Presence pulse? 446 if ( OneWire_IO_IN ) 447 { 448 // No presence, finish initialization sequence 449 Delay5us( ( 500 - 70 ) / 5 ); 450 return FALSE; 451 } 452 else 453 { 454 // Presence OK, finish initialization sequence 455 Delay5us( ( 500 - 70 ) / 5 ); 456 return TRUE; 457 } 458 } 459 460 //############################################################################################ 461 void OneWireCmd( uns8 cmd ) 462 //############################################################################################ 463 { 464 // OneWire: Skip ROM 465 OneWireWriteByte( CMD_SKIPROM ); 466 // OneWire: Send command 467 OneWireWriteByte( cmd ); 468 } 469 470 //############################################################################################ 471 bit Ds18B20WriteConfig( uns8 value ) 472 //############################################################################################ 473 { 474 if ( OneWireReset() ) 475 { 476 // Write Scratchpad 477 OneWireCmd( CMD_WSCRATCHPAD ); 478 479 // Write TL = ? (we dot not care the value) 480 OneWireWriteByte( W ); 481 // Write TH = ? (we dot not care the value) 482 OneWireWriteByte( W ); 483 // Write Config byte 484 OneWireWriteByte( value ); 485 486 if ( OneWireReset() ) 487 { 488 // Copy Scratchpad 489 OneWireCmd( CMD_CPYSCRATCHPAD ); 490 return TRUE; 491 } 492 } 493 return FALSE; 494 } 495 496 //############################################################################################ 497 // 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) 498 #include "DPAcustomHandler.h" 499 //############################################################################################