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