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