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