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