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.34 $
    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 //   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 https://doc.iqrf.org/DpaTechGuide/
   21 
   22 // This example implements the user peripheral reading from Dallas 18B20
   23 // The sensor is continuously read on the background at Idle event
   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 // Also user FRC command is implemented
   27 
   28 // Default IQRF include (modify the path according to your setup)
   29 #include "IQRF.h"
   30 
   31 // Default DPA header (modify the path according to your setup)
   32 #include "DPA.h"
   33 // Default Custom DPA Handler header (modify the path according to your setup)
   34 #include "DPAcustomHandler.h"
   35 
   36 //############################################################################################
   37 
   38 // Special temperature value to indicate a sensor error
   39 #define DS18B20_ERROR_TEMPERATURE 0xF800
   40 
   41 // Sensor connected to PORT C.3 (compatible with DDC-SE-01)
   42 #define OneWire_TRIS         TRISC.3
   43 #define OneWire_IO_IN        PORTC.3
   44 #define OneWire_IO_OUT       LATC.3
   45 
   46 // Writes sensor configuration (resolution)
   47 bit Ds18B20WriteConfig( uns8 value );
   48 
   49 // Resets OneWire
   50 bit OneWireReset();
   51 // Reads OneWire byte
   52 uns8 OneWireReadByte();
   53 // Writes OneWire byte
   54 void OneWireWriteByte( uns8 byte );
   55 
   56 // DS18B20 commands
   57 #define CMD_READROM       0x33
   58 #define CMD_CONVERTTEMP   0x44
   59 #define CMD_CPYSCRATCHPAD 0x48
   60 #define CMD_WSCRATCHPAD   0x4e
   61 #define CMD_MATCHROM      0x55
   62 #define CMD_RPWRSUPPLY    0xb4
   63 #define CMD_RECEEPROM     0xb8
   64 #define CMD_RSCRATCHPAD   0xbe
   65 #define CMD_SKIPROM       0xcc
   66 #define CMD_ALARMSEARCH   0xec
   67 #define CMD_SEARCHROM     0xf0
   68 
   69 #define TICKS_LEN  10
   70 
   71 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   72 //############################################################################################
   73 bit CustomDpaHandler()
   74 //############################################################################################
   75 {
   76   // Finite machine states
   77   typedef enum
   78   {
   79     S_ResetConvertT = 0,
   80     S_SkipRomConvertT,
   81     S_CmdConvertT,
   82 
   83     S_WaitConvertT,
   84 
   85     S_ResetReadTemp,
   86     S_SkipRomReadTemp,
   87     S_CmdReadTemp,
   88     S_Byte1ReadTemp,
   89     S_Byte2ReadTemp
   90   } TState;
   91 
   92   // Handler presence mark
   93   clrwdt();
   94 
   95   // Finite machine state
   96   static uns8 state; // = S_ResetConvertT = 0
   97 
   98   // Pre-read lower temperature byte
   99   static uns8 temperatureByteLow;
  100   // Final temperature value
  101   static uns16 temperature;
  102   // Conversion timeout counter
  103   static uns8 timeout;
  104 
  105   // Detect DPA event to handle
  106   switch ( GetDpaEvent() )
  107   {
  108     // -------------------------------------------------
  109     case DpaEvent_Interrupt:
  110       // Do an extra quick background interrupt work
  111       // ! 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.
  112       // ! 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.
  113       // ! 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.
  114       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
  115       // ! Make sure race condition does not occur when accessing those variables at other places.
  116       // ! 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.
  117       // ! Do not call any OS functions except setINDFx().
  118       // ! Do not use any OS variables especially for writing access.
  119       // ! 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.
  120 
  121       //  If TMR6 interrupt occurred
  122       if ( TMR6IF )
  123       {
  124         // Unmask interrupt
  125         TMR6IF = 0;
  126         // Decrement count
  127         if ( timeout != 0 )
  128           timeout--;
  129       }
  130 
  131 DpaHandleReturnTRUE:
  132       return TRUE;
  133 
  134       // -------------------------------------------------
  135     case DpaEvent_Idle:
  136       // Do a quick background work when RF packet is not received
  137 
  138       // Make sure 1Wire data pin at LATX.y is low as it might be set by another PORTX.? pin manipulation
  139       OneWire_IO_OUT = 0;
  140 
  141       // Run finite state machine
  142       skip( state );
  143 #pragma computedGoto 1
  144       goto _S_ResetConvertT;
  145       goto _S_SkipRomConvertT;
  146       goto _S_CmdConvertT;
  147       goto _S_WaitConvertT;
  148       goto _S_ResetReadTemp;
  149       goto _S_SkipRomReadTemp;
  150       goto _S_CmdReadTemp;
  151       goto _S_Byte1ReadTemp;
  152       goto _S_Byte2ReadTemp;
  153 #pragma computedGoto 0
  154       ;
  155       // --------------
  156 _S_Byte2ReadTemp:
  157       temperature.high8 = OneWireReadByte();
  158       temperature.low8 = temperatureByteLow;
  159 
  160 ResetMachine:
  161       state = S_ResetConvertT;
  162       goto ExitMachine;
  163 
  164       // --------------
  165 _S_ResetConvertT:
  166 _S_ResetReadTemp:
  167       if ( !OneWireReset() )
  168       {
  169 _S_Error_Reset:
  170         temperature = DS18B20_ERROR_TEMPERATURE;
  171         goto ResetMachine;
  172       }
  173       goto NextState;
  174 
  175       // --------------
  176 _S_SkipRomConvertT:
  177 _S_SkipRomReadTemp:
  178       // OneWire: Skip ROM
  179       OneWireWriteByte( CMD_SKIPROM );
  180       goto NextState;
  181 
  182       // --------------
  183 _S_CmdConvertT:
  184       // OneWire: Convert temperature
  185       OneWireWriteByte( CMD_CONVERTTEMP );
  186       // Setup timeout for approx 750 ms (the longest conversion time)
  187       timeout = 2 + 750 / TICKS_LEN;
  188       goto NextState;
  189 
  190       // --------------
  191 _S_WaitConvertT:
  192       if ( OneWireReadByte() == 0xff )
  193         goto NextState;
  194 
  195       // Timeout?
  196       if ( timeout == 0 )
  197         goto _S_Error_Reset;
  198 
  199       goto ExitMachine;
  200 
  201       // --------------
  202 _S_CmdReadTemp:
  203       // OneWire: Read scratchpad
  204       OneWireWriteByte( CMD_RSCRATCHPAD );
  205       goto NextState;
  206 
  207       // --------------
  208 _S_Byte1ReadTemp:
  209       temperatureByteLow = OneWireReadByte();
  210       goto NextState;
  211 
  212       // --------------
  213 NextState:
  214       ++state;
  215 
  216 ExitMachine:
  217       break;
  218 
  219       // -------------------------------------------------
  220     case DpaEvent_Init:
  221       // Setup TMR6 to generate ticks on the background (ticks every 10ms)
  222       _PR6 = 250 - 1;
  223       // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms
  224 #if defined( TR7xG )
  225       TMR6MD = 0;
  226       T6CON = 0b1.100.1001;
  227       //  Timer2/4/6 Clock Select bits = FOSC/4
  228       T6CLKCON |= 0b0000.0001;
  229 #else
  230       T6CON = 0b0.1001.1.10;
  231 #endif
  232 
  233       // Set error temperature
  234       temperature = DS18B20_ERROR_TEMPERATURE;
  235       // Do a one time initialization before main loop starts
  236       Ds18B20WriteConfig( 0b0.11.00000 ); // Setup DS18B20 for 12bit precision, conversion takes 750ms (see datasheet)
  237       //Ds18B20WriteConfig( 0b0.00.00000 ); // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet)
  238       break;
  239 
  240       // -------------------------------------------------
  241     case DpaEvent_AfterSleep:
  242       // Called after woken up after sleep
  243 
  244       goto ResetMachine;
  245 
  246     case DpaEvent_FrcValue:
  247       // Called to get FRC value
  248 
  249       if ( _PCMD == ( FRC_USER_BYTE_FROM + 0 ) )
  250       {
  251         // Return temperature
  252         // Dallas returns 12 bits, so we have to do >> 4 to return fixed part
  253 
  254         W = swap( temperature.low8 );   //                W = L3 L2 L1 L0 L7 L6 L5 L4
  255         responseFRCvalue = W & 0x0F;    // responseFRCvalue = 0  0  0  0  L7 L6 L5 L4
  256         W = swap( temperature.high8 );  //                W = H3 H2 H1 H0 H7 H6 H5 H4
  257         W &= 0xF0;                      //                W = H3 H2 H1 H0 0  0  0  0
  258         responseFRCvalue |= W;          // responseFRCvalue = H3 H2 H1 H0 L7 L6 L5 L4
  259         // Avoid returning 0
  260         if ( responseFRCvalue == 0 )
  261           responseFRCvalue = 127;
  262       }
  263       break;
  264 
  265       // -------------------------------------------------
  266     case DpaEvent_FrcResponseTime:
  267       // Called to get FRC response time
  268 
  269       if ( DataOutBeforeResponseFRC[0] == ( FRC_USER_BYTE_FROM + 0 ) )
  270         responseFRCvalue = _FRC_RESPONSE_TIME_40_MS;
  271       break;
  272 
  273       // -------------------------------------------------
  274     case DpaEvent_DpaRequest:
  275       // Called to interpret DPA request for peripherals
  276       // -------------------------------------------------
  277       // Peripheral enumeration
  278       if ( IsDpaEnumPeripheralsRequest() )
  279       {
  280         // We implement 1 user peripheral
  281         _DpaMessage.EnumPeripheralsAnswer.UserPerNr |= 1;
  282         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 );
  283         _DpaMessage.EnumPeripheralsAnswer.HWPID |= 0x000F;
  284         _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= 0x2468;
  285 
  286         goto DpaHandleReturnTRUE;
  287       }
  288       // -------------------------------------------------
  289       // Get information about peripheral
  290       else if ( IsDpaPeripheralInfoRequest() )
  291       {
  292         if ( _PNUM == PNUM_USER + 0 )
  293         {
  294           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA;
  295           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ;
  296           goto DpaHandleReturnTRUE;
  297         }
  298 
  299         break;
  300       }
  301       // -------------------------------------------------
  302       else
  303       {
  304         // Handle peripheral command
  305         if ( _PNUM == PNUM_USER + 0 )
  306         {
  307           // Check command
  308           switch ( _PCMD )
  309           {
  310             case 0:
  311               // -------------------------------------------------
  312               // Read temperature
  313               if ( _DpaDataLength != 0 )
  314                 DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  315 
  316               uns16 temperatureStore  @ _DpaMessage.Response.PData;
  317               temperatureStore = temperature;
  318               if ( temperatureStore == DS18B20_ERROR_TEMPERATURE )
  319                 DpaApiReturnPeripheralError( ERROR_FAIL );
  320 
  321               _DpaDataLength = sizeof( temperature );
  322               goto DpaHandleReturnTRUE;
  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   // 16 MHz
  340   // + 0.75us ( W=val, Call )
  341   for ( ;; )
  342   {         // loop time
  343     nop2(); // 0.50us
  344     nop2(); // 1.00us
  345     nop2(); // 1.50us
  346     nop2(); // 2.00us
  347     nop2(); // 2.50us
  348     nop2(); // 3.00us
  349     nop();  // 3.25us
  350     if ( --val == 0 ) // + 0.75us (W--, BTFS )
  351       return;         // + 0.25us
  352     nop2(); // 4.50us
  353   }         // 5.00us (Goto)
  354 }
  355 //############################################################################################
  356 
  357 #define OneWireData0()  { OneWire_TRIS = 0; }     // 0.5us @ 16MHz
  358 #define OneWireData1()  { OneWire_TRIS = 1; }     // 0.5us @ 16MHz
  359 
  360 //############################################################################################
  361 void OneWireWriteByte( uns8 byte )
  362 //############################################################################################
  363 {
  364   uns8 bitLoop = 8;
  365   do
  366   {
  367     // Next sequence is time precision critical
  368     GIE = FALSE;
  369 
  370     OneWireData0();
  371     nop2();         // 1 us [0.5 us]
  372     nop2();         // [1.0 us]
  373     if ( byte.0 )   // 2.5 us [1.75us]
  374       OneWireData1();
  375 
  376     // End of time precision critical sequence
  377     GIE = TRUE;
  378 
  379     // 60us minimum in total, does not have to be precise
  380     Delay5us( ( 60 - 3 ) / 5 + 1 );
  381 
  382     OneWireData1();
  383 
  384     byte >>= 1;
  385   } while ( --bitLoop != 0 );
  386 }
  387 
  388 //############################################################################################
  389 uns8 OneWireReadByte()
  390 //############################################################################################
  391 {
  392   uns8 result;
  393   uns8 bitLoop = 8;
  394   do
  395   {
  396     // Next sequence is time precision critical
  397     GIE = FALSE;
  398 
  399     OneWireData0();
  400     nop2();         // 1 us [0.5 us]
  401     nop2();         // [1.0 us]
  402     OneWireData1();         // 2 us [1.5 us]
  403     Delay5us( 15 / 5 );     // 17 us [16.5 us]
  404 
  405     Carry = 0;              // 17.5 us [16.75 us]
  406     if ( OneWire_IO_IN )    // 18.5 us [ 17.25 us] (condition must not modify Carry)
  407       Carry = 1;
  408 
  409     // End of time precision critical sequence
  410     GIE = TRUE;             // must not modify Carry
  411     result = rr( result );
  412 
  413     // 60us minimum in total, does not have to be precise
  414     Delay5us( ( 60 - 20 ) / 5 + 1 );
  415   } while ( --bitLoop != 0 );
  416 
  417   return result;
  418 }
  419 
  420 //############################################################################################
  421 bit OneWireReset()
  422 //############################################################################################
  423 {
  424   // Setting the pin once to low is enough
  425   OneWire_IO_OUT = 0;
  426   // Reset pulse
  427   OneWireData0();
  428   Delay5us( 500 / 5 );
  429   // Reset pulse end
  430   OneWireData1();
  431   // Next sequence is time precision critical
  432   GIE = FALSE;
  433   // Wait for presence pulse
  434   Delay5us( 70 / 5 );
  435   // End of time precision critical sequence
  436   GIE = TRUE;
  437   // Presence pulse?
  438   if ( OneWire_IO_IN )
  439   {
  440     // No presence, finish initialization sequence
  441     Delay5us( ( 500 - 70 ) / 5 );
  442     return FALSE;
  443   }
  444   else
  445   {
  446     // Presence OK, finish initialization sequence
  447     Delay5us( ( 500 - 70 ) / 5 );
  448     return TRUE;
  449   }
  450 }
  451 
  452 //############################################################################################
  453 void OneWireCmd( uns8 cmd )
  454 //############################################################################################
  455 {
  456   // OneWire: Skip ROM
  457   OneWireWriteByte( CMD_SKIPROM );
  458   // OneWire: Send command
  459   OneWireWriteByte( cmd );
  460 }
  461 
  462 //############################################################################################
  463 bit Ds18B20WriteConfig( uns8 value )
  464 //############################################################################################
  465 {
  466   if ( OneWireReset() )
  467   {
  468     // Write Scratchpad
  469     OneWireCmd( CMD_WSCRATCHPAD );
  470 
  471     // Write TL = ? (we dot not care the value)
  472     OneWireWriteByte( W );
  473     // Write TH = ? (we dot not care the value)
  474     OneWireWriteByte( W );
  475     // Write Config byte
  476     OneWireWriteByte( value );
  477 
  478     if ( OneWireReset() )
  479     {
  480       //  Copy Scratchpad
  481       OneWireCmd( CMD_CPYSCRATCHPAD );
  482       return TRUE;
  483     }
  484   }
  485   return FALSE;
  486 }
  487 
  488 //############################################################################################
  489 // 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)
  490 #include "DPAcustomHandler.h"
  491 //############################################################################################