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