1 // *********************************************************************************** 2 // Custom DPA Handler code example - User peripheral implementation - Dallas 18B20 * 3 // *********************************************************************************** 4 // Copyright (c) MICRORISC s.r.o. 5 // 6 // File: $RCSfile: CustomDpaHandler-UserPeripheral-18B20.c,v $ 7 // Version: $Revision: 1.36 $ 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/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 https://doc.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 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 == 16000000 177 // 16 MHz 178 // + 0.75us ( W=val, Call ) 179 for ( ;; ) 180 { // loop time 181 nop2(); // 0.50us 182 nop2(); // 1.00us 183 nop2(); // 1.50us 184 nop2(); // 2.00us 185 nop2(); // 2.50us 186 nop2(); // 3.00us 187 nop(); // 3.25us 188 if ( --val == 0 ) // + 0.75us (W--, BTFS ) 189 return; // + 0.25us 190 nop2(); // 4.50us 191 } // 5.00us (Goto) 192 #else 193 #error Unsupported oscillator frequency 194 #endif 195 } 196 //############################################################################################ 197 198 #define OneWireData0() { OneWire_TRIS = 0; } // 0.5us @ 16MHz 199 #define OneWireData1() { OneWire_TRIS = 1; } // 0.5us @ 16MHz 200 201 //############################################################################################ 202 void OneWireWriteByte( uns8 byte ) 203 //############################################################################################ 204 { 205 uns8 bitLoop = 8; 206 do 207 { 208 // Next sequence is time precision critical 209 GIE = FALSE; 210 211 OneWireData0(); 212 nop2(); // 1 us [0.5 us] 213 #if F_OSC == 16000000 214 nop2(); // [1.0 us] 215 #endif 216 if ( byte & 1 ) // 2.5 us [1.75us] 217 OneWireData1(); 218 219 // End of time precision critical sequence 220 GIE = TRUE; 221 222 // 60us minimum in total, does not have to be precise 223 Delay5us( ( 60 - 3 ) / 5 + 1 ); 224 OneWireData1(); 225 226 byte >>= 1; 227 } while ( --bitLoop != 0 ); 228 } 229 230 //############################################################################################ 231 uns8 OneWireReadByte() 232 //############################################################################################ 233 { 234 uns8 result; 235 uns8 bitLoop = 8; 236 do 237 { 238 // Next sequence is time precision critical 239 GIE = FALSE; 240 241 OneWireData0(); 242 nop2(); // 1 us [0.5 us] 243 #if F_OSC == 16000000 244 nop2(); // [1.0 us] 245 #endif 246 OneWireData1(); // 2 us [1.5 us] 247 Delay5us( 15 / 5 ); // 17 us [16.5 us] 248 249 Carry = 0; // 17.5 us [16.75 us] 250 if ( OneWire_IO_IN ) // 18.5 us [ 17.25 us] (condition must not modify Carry) 251 Carry = 1; 252 253 // End of time precision critical sequence 254 GIE = TRUE; // must not modify Carry 255 result = rr( result ); 256 257 // 60us minimum in total, does not have to be precise 258 Delay5us( ( 60 - 20 ) / 5 + 1 ); 259 } while ( --bitLoop != 0 ); 260 261 return result; 262 } 263 264 //############################################################################################ 265 bit OneWireResetAndCmd( uns8 cmd ) 266 //############################################################################################ 267 { 268 // Setting the pin once to low is enough 269 OneWire_IO_OUT = 0; 270 // Reset pulse 271 OneWireData0(); 272 Delay5us( 500 / 5 ); 273 // Reset pulse end 274 OneWireData1(); 275 // Next sequence is time precision critical 276 GIE = FALSE; 277 // Wait for presence pulse 278 Delay5us( 70 / 5 ); 279 // End of time precision critical sequence 280 GIE = TRUE; 281 // Presence pulse? 282 if ( OneWire_IO_IN ) 283 { 284 // No presence, finish initialization sequence 285 Delay5us( ( 500 - 70 ) / 5 ); 286 return FALSE; 287 } 288 else 289 { 290 // Presence OK, finish initialization sequence 291 Delay5us( ( 500 - 70 ) / 5 ); 292 // OneWire: Skip ROM 293 OneWireWriteByte( CMD_SKIPROM ); 294 // OneWire: Send command 295 OneWireWriteByte( cmd ); 296 return TRUE; 297 } 298 } 299 300 //############################################################################################ 301 302 #ifdef USE_ONEWIRE_CRC 303 // One Wire CRC accumulator 304 static uns8 OneWireCrc; 305 306 //############################################################################################ 307 void UpdateOneWireCrc( uns8 value @ W ) 308 //############################################################################################ 309 { 310 // Based on http://www.dattalo.com/technical/software/pic/crc_8bit.c 311 OneWireCrc ^= W; 312 #pragma updateBank 0 /* OFF */ 313 value = 0; 314 if ( OneWireCrc.0 ) // 1 instruction 315 value ^= 0x5e; // 1 instruction 316 if ( OneWireCrc.1 ) // 1 instruction 317 value ^= 0xbc; // ... 318 if ( OneWireCrc.2 ) 319 value ^= 0x61; // ... 320 if ( OneWireCrc.3 ) 321 value ^= 0xc2; // ... 322 if ( OneWireCrc.4 ) 323 value ^= 0x9d; // ... 324 if ( OneWireCrc.5 ) 325 value ^= 0x23; // ... 326 if ( OneWireCrc.6 ) 327 value ^= 0x46; // ... 328 if ( OneWireCrc.7 ) 329 value ^= 0x8c; // 0x8C is reverse polynomial representation (normal is 0x31) 330 OneWireCrc = value; 331 #pragma updateBank 1 /* ON */ 332 } 333 334 #endif 335 336 //############################################################################################ 337 uns16 Ds18B20GetTemp() 338 //############################################################################################ 339 { 340 // OneWire: Convert T 341 if ( OneWireResetAndCmd( CMD_CONVERTTEMP ) ) 342 { 343 // Wait for conversion to finish 344 startCapture(); 345 for ( ;; ) 346 { 347 // 1s timeout 348 captureTicks(); 349 if ( param3.low8 > ( 1000 / 10 ) ) 350 { 351 // Timeout 352 Carry = FALSE; 353 break; 354 } 355 356 if ( OneWireReadByte() == 0xff ) 357 { 358 // Temperature ready to be read 359 Carry = TRUE; 360 break; 361 } 362 } 363 364 // OneWire: Read Scratchpad 365 if ( Carry && OneWireResetAndCmd( CMD_RSCRATCHPAD ) ) 366 { 367 // Read Scratchpad bytes 368 uns16 temperature; 369 370 #ifdef USE_ONEWIRE_CRC 371 // Initialize crc 372 OneWireCrc = 0; 373 // Will read later rest of scratchpad bytes and update CRC (initialization is here to avoid MOVLB later) 374 uns8 byteLoop = 9 - 2; 375 376 // Temperature LSB into result & CRC 377 UpdateOneWireCrc( temperature.low8 = OneWireReadByte() ); 378 // Temperature MSB into result & CRC 379 UpdateOneWireCrc( temperature.high8 = OneWireReadByte() ); 380 381 // Read rest of scratchpad 382 do 383 { 384 UpdateOneWireCrc( OneWireReadByte() ); 385 } while ( --byteLoop != 0 ); 386 387 // Check correct CRC 388 if ( OneWireCrc == 0 ) 389 return temperature; 390 #else 391 temperature.low8 = OneWireReadByte(); 392 temperature.high8 = OneWireReadByte(); 393 return temperature; 394 #endif 395 } 396 } 397 // Some error occurred 398 return DS18B20_ERROR_TEMPERATURE; 399 } 400 401 //############################################################################################ 402 bit Ds18B20WriteConfig( uns8 value ) 403 //############################################################################################ 404 { 405 // OneWire: Write Scratchpad 406 if ( OneWireResetAndCmd( CMD_WSCRATCHPAD ) ) 407 { 408 // Write TL = ? (we dot not care the value) 409 OneWireWriteByte( W ); 410 // Write TH = ? (we dot not care the value) 411 OneWireWriteByte( W ); 412 // Write Config byte 413 OneWireWriteByte( value ); 414 415 // OneWire: Copy Scratchpad 416 if ( OneWireResetAndCmd( CMD_CPYSCRATCHPAD ) ) 417 return TRUE; 418 } 419 return FALSE; 420 } 421 422 //############################################################################################ 423 // 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) 424 #include "DPAcustomHandler.h" 425 //############################################################################################