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