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.c,v $ 7 // Version: $Revision: 1.30 $ 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/08/05 Release for DPA 2.20 14 // 2014/05/26 Release for DPA 2.11 15 // 2014/10/31 Release for DPA 2.10 16 // 2014/05/26 Release for DPA 2.01 17 // 18 // ********************************************************************* 19 20 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/ 21 22 // This example implements the user peripheral reading from Dallas 18B20 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 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 // If defined then CRC is checked 37 #define USE_ONEWIRE_CRC 38 39 // Special temperature value to indicate a sensor error 40 #define DS18B20_ERROR_TEMPERATURE 0xF800 41 42 // Sensor connected to PORT C.3 (compatible with DDC-SE-01) 43 #define OneWire_TRIS TRISC.3 44 #define OneWire_IO_IN PORTC.3 45 #define OneWire_IO_OUT LATC.3 46 47 // Reads temperature from sensor 48 uns16 Ds18B20GetTemp(); 49 // Writes sensor configuration (resolution) 50 bit Ds18B20WriteConfig( uns8 value ); 51 52 // DS18B20 commands 53 #define CMD_READROM 0x33 54 #define CMD_CONVERTTEMP 0x44 55 #define CMD_CPYSCRATCHPAD 0x48 56 #define CMD_WSCRATCHPAD 0x4e 57 #define CMD_MATCHROM 0x55 58 #define CMD_RPWRSUPPLY 0xb4 59 #define CMD_RECEEPROM 0xb8 60 #define CMD_RSCRATCHPAD 0xbe 61 #define CMD_SKIPROM 0xcc 62 #define CMD_ALARMSEARCH 0xec 63 #define CMD_SEARCHROM 0xf0 64 65 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 66 //############################################################################################ 67 bit CustomDpaHandler() 68 //############################################################################################ 69 { 70 // Handler presence mark 71 clrwdt(); 72 73 // Detect DPA event to handle 74 switch ( GetDpaEvent() ) 75 { 76 // ------------------------------------------------- 77 case DpaEvent_Interrupt: 78 // Do an extra quick background interrupt work 79 // ! 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. 80 // ! 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. 81 // ! 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. 82 // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy. 83 // ! Make sure race condition does not occur when accessing those variables at other places. 84 // ! 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. 85 // ! Do not call any OS functions except setINDFx(). 86 // ! Do not use any OS variables especially for writing access. 87 // ! 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. 88 89 DpaHandleReturnTRUE: 90 return TRUE; 91 92 // ------------------------------------------------- 93 case DpaEvent_Init: 94 // Do a one time initialization work before main loop starts 95 96 // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet) 97 //Ds18B20WriteConfig( 0b0.00.00000 ); 98 99 //// Setup DS18B20 for 12bit precision, conversion takes 750ms (see datasheet) 100 Ds18B20WriteConfig( 0b0.11.00000 ); 101 102 break; 103 104 // ------------------------------------------------- 105 case DpaEvent_DpaRequest: 106 // Called to interpret DPA request for peripherals 107 // ------------------------------------------------- 108 // Peripheral enumeration 109 if ( IsDpaEnumPeripheralsRequest() ) 110 { 111 // We implement 1 user peripheral 112 _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1; 113 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 ); 114 _DpaMessage.EnumPeripheralsAnswer.HWPID = 0x000F; 115 _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x9876; 116 117 goto DpaHandleReturnTRUE; 118 } 119 // ------------------------------------------------- 120 // Get information about peripheral 121 else if ( IsDpaPeripheralInfoRequest() ) 122 { 123 if ( _PNUM == PNUM_USER + 0 ) 124 { 125 _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA; 126 _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ; 127 goto DpaHandleReturnTRUE; 128 } 129 130 break; 131 } 132 // ------------------------------------------------- 133 else 134 { 135 // Handle peripheral command 136 if ( _PNUM == PNUM_USER + 0 ) 137 { 138 // Check command 139 switch ( _PCMD ) 140 { 141 case 0: 142 // ------------------------------------------------- 143 // Read temperature 144 if ( _DpaDataLength != 0 ) 145 DpaApiReturnPeripheralError( ERROR_DATA_LEN ); 146 147 uns16 temperature @ _DpaMessage.Response.PData; 148 temperature = Ds18B20GetTemp(); 149 if ( temperature == DS18B20_ERROR_TEMPERATURE ) 150 DpaApiReturnPeripheralError( ERROR_FAIL ); 151 152 _DpaDataLength = sizeof( temperature ); 153 goto DpaHandleReturnTRUE; 154 155 default: 156 // ------------------------------------------------- 157 // Invalid command 158 DpaApiReturnPeripheralError( ERROR_PCMD ); 159 } 160 } 161 } 162 } 163 164 return FALSE; 165 } 166 167 //############################################################################################ 168 // OneWire and Dallas 18B20 routines 169 // 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 170 //############################################################################################ 171 172 //############################################################################################ 173 void Delay5us( uns8 val @ W ) // Absolutely precise timing but val != 0 174 //############################################################################################ 175 { 176 #if F_OSC == 8000000 177 // 8 MHz 178 // + 1.5us ( W=val, Call ) 179 for ( ;; ) 180 { // loop time 181 nop2(); // 1us 182 nop(); // 1.5us 183 if ( --val == 0 ) // + 1.5us (W--, BTFS ) 184 return; // + 0.5us 185 nop2(); // 4us 186 } // 5us (Goto) 187 #elif F_OSC == 16000000 188 // 16 MHz 189 // + 0.75us ( W=val, Call ) 190 for ( ;; ) 191 { // loop time 192 nop2(); // 0.50us 193 nop2(); // 1.00us 194 nop2(); // 1.50us 195 nop2(); // 2.00us 196 nop2(); // 2.50us 197 nop2(); // 3.00us 198 nop(); // 3.25us 199 if ( --val == 0 ) // + 0.75us (W--, BTFS ) 200 return; // + 0.25us 201 nop2(); // 4.50us 202 } // 5.00us (Goto) 203 #else 204 #error Unsupported oscillator frequency 205 #endif 206 } 207 //############################################################################################ 208 209 #define OneWireData0() { OneWire_TRIS = 0; } // 1us [0.5us @ 16MHz] 210 #define OneWireData1() { OneWire_TRIS = 1; } // 1us [0.5us @ 16MHz] 211 212 //############################################################################################ 213 void OneWireWriteByte( uns8 byte ) 214 //############################################################################################ 215 { 216 uns8 bitLoop = 8; 217 do 218 { 219 // Next sequence is time precision critical 220 GIE = FALSE; 221 222 OneWireData0(); 223 nop2(); // 1 us [0.5 us] 224 #if F_OSC == 16000000 225 nop2(); // [1.0 us] 226 #endif 227 if ( byte & 1 ) // 2.5 us [1.75us] 228 OneWireData1(); 229 230 // End of time precision critical sequence 231 GIE = TRUE; 232 233 // 60us minimum in total, does not have to be precise 234 Delay5us( ( 60 - 3 ) / 5 + 1 ); 235 OneWireData1(); 236 237 byte >>= 1; 238 } while ( --bitLoop != 0 ); 239 } 240 241 //############################################################################################ 242 uns8 OneWireReadByte() 243 //############################################################################################ 244 { 245 uns8 result; 246 uns8 bitLoop = 8; 247 do 248 { 249 // Next sequence is time precision critical 250 GIE = FALSE; 251 252 OneWireData0(); 253 nop2(); // 1 us [0.5 us] 254 #if F_OSC == 16000000 255 nop2(); // [1.0 us] 256 #endif 257 OneWireData1(); // 2 us [1.5 us] 258 Delay5us( 15 / 5 ); // 17 us [16.5 us] 259 260 Carry = 0; // 17.5 us [16.75 us] 261 if ( OneWire_IO_IN ) // 18.5 us [ 17.25 us] (condition must not modify Carry) 262 Carry = 1; 263 264 // End of time precision critical sequence 265 GIE = TRUE; // must not modify Carry 266 result = rr( result ); 267 268 // 60us minimum in total, does not have to be precise 269 Delay5us( ( 60 - 20 ) / 5 + 1 ); 270 } while ( --bitLoop != 0 ); 271 272 return result; 273 } 274 275 //############################################################################################ 276 bit OneWireResetAndCmd( uns8 cmd ) 277 //############################################################################################ 278 { 279 // Setting the pin once to low is enough 280 OneWire_IO_OUT = 0; 281 // Reset pulse 282 OneWireData0(); 283 Delay5us( 500 / 5 ); 284 // Reset pulse end 285 OneWireData1(); 286 // Next sequence is time precision critical 287 GIE = FALSE; 288 // Wait for presence pulse 289 Delay5us( 70 / 5 ); 290 // End of time precision critical sequence 291 GIE = TRUE; 292 // Presence pulse? 293 if ( OneWire_IO_IN ) 294 { 295 // No presence, finish initialization sequence 296 Delay5us( ( 500 - 70 ) / 5 ); 297 return FALSE; 298 } 299 else 300 { 301 // Presence OK, finish initialization sequence 302 Delay5us( ( 500 - 70 ) / 5 ); 303 // OneWire: Skip ROM 304 OneWireWriteByte( CMD_SKIPROM ); 305 // OneWire: Send command 306 OneWireWriteByte( cmd ); 307 return TRUE; 308 } 309 } 310 311 //############################################################################################ 312 313 #ifdef USE_ONEWIRE_CRC 314 // One Wire CRC accumulator 315 static uns8 OneWireCrc; 316 317 //############################################################################################ 318 void UpdateOneWireCrc( uns8 value @ W ) 319 //############################################################################################ 320 { 321 // Based on http://www.dattalo.com/technical/software/pic/crc_8bit.c 322 OneWireCrc ^= W; 323 #pragma updateBank 0 /* OFF */ 324 value = 0; 325 if ( OneWireCrc.0 ) // 1 instruction 326 value ^= 0x5e; // 1 instruction 327 if ( OneWireCrc.1 ) // 1 instruction 328 value ^= 0xbc; // ... 329 if ( OneWireCrc.2 ) 330 value ^= 0x61; // ... 331 if ( OneWireCrc.3 ) 332 value ^= 0xc2; // ... 333 if ( OneWireCrc.4 ) 334 value ^= 0x9d; // ... 335 if ( OneWireCrc.5 ) 336 value ^= 0x23; // ... 337 if ( OneWireCrc.6 ) 338 value ^= 0x46; // ... 339 if ( OneWireCrc.7 ) 340 value ^= 0x8c; // 0x8C is reverse polynomial representation (normal is 0x31) 341 OneWireCrc = value; 342 #pragma updateBank 1 /* ON */ 343 } 344 345 #endif 346 347 //############################################################################################ 348 uns16 Ds18B20GetTemp() 349 //############################################################################################ 350 { 351 // OneWire: Convert T 352 if ( OneWireResetAndCmd( CMD_CONVERTTEMP ) ) 353 { 354 // Wait for conversion to finish 355 startCapture(); 356 for ( ;; ) 357 { 358 // 1s timeout 359 captureTicks(); 360 if ( param3.low8 > ( 1000 / 10 ) ) 361 { 362 // Timeout 363 Carry = FALSE; 364 break; 365 } 366 367 if ( OneWireReadByte() == 0xff ) 368 { 369 // Temperature ready to be read 370 Carry = TRUE; 371 break; 372 } 373 } 374 375 // OneWire: Read Scratchpad 376 if ( Carry && OneWireResetAndCmd( CMD_RSCRATCHPAD ) ) 377 { 378 // Read Scratchpad bytes 379 uns16 temperature; 380 381 #ifdef USE_ONEWIRE_CRC 382 // Initialize crc 383 OneWireCrc = 0; 384 // Will read later rest of scratchpad bytes and update CRC (initialization is here to avoid MOVLB later) 385 uns8 byteLoop = 9 - 2; 386 387 // Temperature LSB into result & CRC 388 UpdateOneWireCrc( temperature.low8 = OneWireReadByte() ); 389 // Temperature MSB into result & CRC 390 UpdateOneWireCrc( temperature.high8 = OneWireReadByte() ); 391 392 // Read rest of scratchpad 393 do 394 { 395 UpdateOneWireCrc( OneWireReadByte() ); 396 } while ( --byteLoop != 0 ); 397 398 // Check correct CRC 399 if ( OneWireCrc == 0 ) 400 return temperature; 401 #else 402 temperature.low8 = OneWireReadByte(); 403 temperature.high8 = OneWireReadByte(); 404 return temperature; 405 #endif 406 } 407 } 408 // Some error occurred 409 return DS18B20_ERROR_TEMPERATURE; 410 } 411 412 //############################################################################################ 413 bit Ds18B20WriteConfig( uns8 value ) 414 //############################################################################################ 415 { 416 // OneWire: Write Scratchpad 417 if ( OneWireResetAndCmd( CMD_WSCRATCHPAD ) ) 418 { 419 // Write TL = ? (we dot not care the value) 420 OneWireWriteByte( W ); 421 // Write TH = ? (we dot not care the value) 422 OneWireWriteByte( W ); 423 // Write Config byte 424 OneWireWriteByte( value ); 425 426 // OneWire: Copy Scratchpad 427 if ( OneWireResetAndCmd( CMD_CPYSCRATCHPAD ) ) 428 return TRUE; 429 } 430 return FALSE; 431 } 432 433 //############################################################################################ 434 // 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) 435 #include "DPAcustomHandler.h" 436 //############################################################################################