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.c,v $
    7 // Version: $Revision: 1.43 $
    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 //   2016/09/12  Release for DPA 2.28
   14 //   2015/08/05  Release for DPA 2.20
   15 //   2014/05/26  Release for DPA 2.11
   16 //   2014/10/31  Release for DPA 2.10
   17 //   2014/05/26  Release for DPA 2.01
   18 //
   19 // *********************************************************************
   20 
   21 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   22 
   23 // This example implements the user peripheral reading from Dallas 18B20
   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 
   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 // If defined then CRC is checked
   38 #define USE_ONEWIRE_CRC
   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 // Reads temperature from sensor
   49 uns16 Ds18B20GetTemp();
   50 // Writes sensor configuration (resolution)
   51 bit Ds18B20WriteConfig( uns8 value );
   52 
   53 // DS18B20 commands
   54 #define CMD_READROM       0x33
   55 #define CMD_CONVERTTEMP   0x44
   56 #define CMD_CPYSCRATCHPAD 0x48
   57 #define CMD_WSCRATCHPAD   0x4e
   58 #define CMD_MATCHROM      0x55
   59 #define CMD_RPWRSUPPLY    0xb4
   60 #define CMD_RECEEPROM     0xb8
   61 #define CMD_RSCRATCHPAD   0xbe
   62 #define CMD_SKIPROM       0xcc
   63 #define CMD_ALARMSEARCH   0xec
   64 #define CMD_SEARCHROM     0xf0
   65 
   66 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   67 //############################################################################################
   68 bit CustomDpaHandler()
   69 //############################################################################################
   70 {
   71   // Handler presence mark
   72   clrwdt();
   73 
   74   // Detect DPA event to handle
   75   switch ( GetDpaEvent() )
   76   {
   77     // -------------------------------------------------
   78     case DpaEvent_Interrupt:
   79       // Do an extra quick background interrupt work
   80       // ! 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.
   81       // ! 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.
   82       // ! 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.
   83       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   84       // ! Make sure race condition does not occur when accessing those variables at other places.
   85       // ! 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.
   86       // ! Do not call any OS functions except setINDFx().
   87       // ! Do not use any OS variables especially for writing access.
   88       // ! 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.
   89 
   90 DpaHandleReturnTRUE:
   91       return TRUE;
   92 
   93       // -------------------------------------------------
   94     case DpaEvent_Init:
   95       // Do a one time initialization before main loop starts
   96 
   97       // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet)
   98       //Ds18B20WriteConfig( 0b0.00.00000 );
   99 
  100       //// Setup DS18B20 for 12bit precision, conversion takes 750ms (see datasheet)
  101       Ds18B20WriteConfig( 0b0.11.00000 );
  102 
  103       break;
  104 
  105       // -------------------------------------------------
  106     case DpaEvent_DpaRequest:
  107       // Called to interpret DPA request for peripherals
  108       // -------------------------------------------------
  109       // Peripheral enumeration
  110       if ( IsDpaEnumPeripheralsRequest() )
  111       {
  112         // We implement 1 user peripheral
  113         _DpaMessage.EnumPeripheralsAnswer.UserPerNr |= 1;
  114         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 );
  115         _DpaMessage.EnumPeripheralsAnswer.HWPID |= 0x000F;
  116         _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= 0x9876;
  117 
  118         goto DpaHandleReturnTRUE;
  119       }
  120       // -------------------------------------------------
  121       // Get information about peripheral
  122       else if ( IsDpaPeripheralInfoRequest() )
  123       {
  124         if ( _PNUM == PNUM_USER + 0 )
  125         {
  126           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA;
  127           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ;
  128           goto DpaHandleReturnTRUE;
  129         }
  130 
  131         break;
  132       }
  133       // -------------------------------------------------
  134       else
  135       {
  136         // Handle peripheral command
  137         if ( _PNUM == PNUM_USER + 0 )
  138         {
  139           // Check command
  140           switch ( _PCMD )
  141           {
  142             case 0:
  143               // -------------------------------------------------
  144               // Read temperature
  145               if ( _DpaDataLength != 0 )
  146                 DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  147 
  148               uns16 temperature  @ _DpaMessage.Response.PData;
  149               temperature = Ds18B20GetTemp();
  150               if ( temperature == DS18B20_ERROR_TEMPERATURE )
  151                 DpaApiReturnPeripheralError( ERROR_FAIL );
  152 
  153               _DpaDataLength = sizeof( temperature );
  154               goto DpaHandleReturnTRUE;
  155           }
  156         }
  157       }
  158   }
  159 
  160   return FALSE;
  161 }
  162 
  163 //############################################################################################
  164 // OneWire and Dallas 18B20 routines
  165 // 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
  166 //############################################################################################
  167 
  168 //############################################################################################
  169 void Delay5us( uns8 val @ W ) // Absolutely precise timing but val != 0
  170 //############################################################################################
  171 {
  172   // 16 MHz
  173   // + 0.75us ( W=val, Call )
  174   for ( ;; )
  175   {         // loop time
  176     nop2(); // 0.50us
  177     nop2(); // 1.00us
  178     nop2(); // 1.50us
  179     nop2(); // 2.00us
  180     nop2(); // 2.50us
  181     nop2(); // 3.00us
  182     nop();  // 3.25us
  183     if ( --val == 0 ) // + 0.75us (W--, BTFS )
  184       return;         // + 0.25us
  185     nop2(); // 4.50us
  186   }         // 5.00us (Goto)
  187 }
  188 //############################################################################################
  189 
  190 #define OneWireData0()  { OneWire_TRIS = 0; }     // 0.5us @ 16MHz
  191 #define OneWireData1()  { OneWire_TRIS = 1; }     // 0.5us @ 16MHz
  192 
  193 //############################################################################################
  194 void OneWireWriteByte( uns8 byte )
  195 //############################################################################################
  196 {
  197   uns8 bitLoop = 8;
  198   do
  199   {
  200     // Next sequence is time precision critical
  201     GIE = FALSE;
  202 
  203     OneWireData0();
  204     nop2();         // 1 us [0.5 us]
  205     nop2();         // [1.0 us]
  206     if ( byte & 1 ) // 2.5 us [1.75us]
  207       OneWireData1();
  208 
  209     // End of time precision critical sequence
  210     GIE = TRUE;
  211 
  212     // 60us minimum in total, does not have to be precise
  213     Delay5us( ( 60 - 3 ) / 5 + 1 );
  214     OneWireData1();
  215 
  216     byte >>= 1;
  217   } while ( --bitLoop != 0 );
  218 }
  219 
  220 //############################################################################################
  221 uns8 OneWireReadByte()
  222 //############################################################################################
  223 {
  224   uns8 result;
  225   uns8 bitLoop = 8;
  226   do
  227   {
  228     // Next sequence is time precision critical
  229     GIE = FALSE;
  230 
  231     OneWireData0();
  232     nop2();         // 1 us [0.5 us]
  233     nop2();         // [1.0 us]
  234     OneWireData1();         // 2 us [1.5 us]
  235     Delay5us( 15 / 5 );     // 17 us [16.5 us]
  236 
  237     Carry = 0;              // 17.5 us [16.75 us]
  238     if ( OneWire_IO_IN )    // 18.5 us [ 17.25 us] (condition must not modify Carry)
  239       Carry = 1;
  240 
  241     // End of time precision critical sequence
  242     GIE = TRUE;             // must not modify Carry
  243     result = rr( result );
  244 
  245     // 60us minimum in total, does not have to be precise
  246     Delay5us( ( 60 - 20 ) / 5 + 1 );
  247   } while ( --bitLoop != 0 );
  248 
  249   return result;
  250 }
  251 
  252 //############################################################################################
  253 bit OneWireResetAndCmd( uns8 cmd )
  254 //############################################################################################
  255 {
  256   // Setting the pin once to low is enough
  257   OneWire_IO_OUT = 0;
  258   // Reset pulse
  259   OneWireData0();
  260   Delay5us( 500 / 5 );
  261   // Reset pulse end
  262   OneWireData1();
  263   // Next sequence is time precision critical
  264   GIE = FALSE;
  265   // Wait for presence pulse
  266   Delay5us( 70 / 5 );
  267   // End of time precision critical sequence
  268   GIE = TRUE;
  269   // Presence pulse?
  270   if ( OneWire_IO_IN )
  271   {
  272     // No presence, finish initialization sequence
  273     Delay5us( ( 500 - 70 ) / 5 );
  274     return FALSE;
  275   }
  276   else
  277   {
  278     // Presence OK, finish initialization sequence
  279     Delay5us( ( 500 - 70 ) / 5 );
  280     // OneWire: Skip ROM
  281     OneWireWriteByte( CMD_SKIPROM );
  282     // OneWire: Send command
  283     OneWireWriteByte( cmd );
  284     return TRUE;
  285   }
  286 }
  287 
  288 //############################################################################################
  289 
  290 #ifdef USE_ONEWIRE_CRC
  291 // One Wire CRC accumulator
  292 uns8 OneWireCrc;
  293 
  294 //############################################################################################
  295 void UpdateOneWireCrc( uns8 value )
  296 //############################################################################################
  297 {
  298   OneWireCrc = DpaApiCrc8( OneWireCrc, value );
  299 }
  300 
  301 #endif
  302 
  303 //############################################################################################
  304 uns16 Ds18B20GetTemp()
  305 //############################################################################################
  306 {
  307   // OneWire: Convert T
  308   if ( OneWireResetAndCmd( CMD_CONVERTTEMP ) )
  309   {
  310     // Wait for conversion to finish
  311     startCapture();
  312     for ( ;; )
  313     {
  314       // 1s timeout
  315       captureTicks();
  316       if ( param3.low8 > ( 1000 / 10 ) )
  317       {
  318         // Timeout
  319         Carry = FALSE;
  320         break;
  321       }
  322 
  323       if ( OneWireReadByte() == 0xff )
  324       {
  325         // Temperature ready to be read
  326         Carry = TRUE;
  327         break;
  328       }
  329     }
  330 
  331     // OneWire: Read Scratchpad
  332     if ( Carry && OneWireResetAndCmd( CMD_RSCRATCHPAD ) )
  333     {
  334       // Read Scratchpad bytes
  335       uns16 temperature;
  336 
  337 #ifdef USE_ONEWIRE_CRC
  338       // Initialize CRC
  339       OneWireCrc = 0;
  340       // Will read later rest of scratchpad bytes and update CRC (initialization is here to avoid MOVLB later)
  341       uns8 byteLoop = 9 - 2;
  342 
  343       // Temperature LSB into result & CRC
  344       UpdateOneWireCrc( temperature.low8 = OneWireReadByte() );
  345       // Temperature MSB into result & CRC
  346       UpdateOneWireCrc( temperature.high8 = OneWireReadByte() );
  347 
  348       // Read rest of scratchpad
  349       do
  350       {
  351         UpdateOneWireCrc( OneWireReadByte() );
  352       } while ( --byteLoop != 0 );
  353 
  354       // Check correct CRC
  355       if ( OneWireCrc == 0 )
  356         return temperature;
  357 #else
  358       temperature.low8 = OneWireReadByte();
  359       temperature.high8 = OneWireReadByte();
  360       return temperature;
  361 #endif
  362     }
  363   }
  364   // Some error occurred
  365   return DS18B20_ERROR_TEMPERATURE;
  366 }
  367 
  368 //############################################################################################
  369 bit Ds18B20WriteConfig( uns8 value )
  370 //############################################################################################
  371 {
  372   // OneWire: Write Scratchpad
  373   if ( OneWireResetAndCmd( CMD_WSCRATCHPAD ) )
  374   {
  375     // Write TL = ? (we dot not care the value)
  376     OneWireWriteByte( W );
  377     // Write TH = ? (we dot not care the value)
  378     OneWireWriteByte( W );
  379     // Write Config byte
  380     OneWireWriteByte( value );
  381 
  382     // OneWire: Copy Scratchpad
  383     if ( OneWireResetAndCmd( CMD_CPYSCRATCHPAD ) )
  384       return TRUE;
  385   }
  386   return FALSE;
  387 }
  388 
  389 //############################################################################################
  390 // 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)
  391 #include "DPAcustomHandler.h"
  392 //############################################################################################