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.33 $
    8 // Date:    $Date: 2019/01/04 17:14:37 $
    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 == 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 //############################################################################################