1 // *********************************************************************
    2 //   Custom DPA Handler code example - Standard Sensors - DDC-SE-01    *
    3 // *********************************************************************
    4 // Copyright (c) MICRORISC s.r.o.
    5 //
    6 // File:    $RCSfile: 0002_DDC-SE01.c,v $
    7 // Version: $Revision: 1.46 $
    8 // Date:    $Date: 2022/02/25 09:41:26 $
    9 //
   10 // Revision history:
   11 //   2022/02/24  Release for DPA 4.17
   12 //   2021/08/20  Release for DPA 4.16
   13 //   2020/01/02  Release for DPA 4.11
   14 //   2018/10/25  Release for DPA 3.03
   15 //   2017/11/16  Release for DPA 3.02
   16 //   2017/08/14  Release for DPA 3.01
   17 //
   18 // *********************************************************************
   19 
   20 // MCR-BuildStdHandler
   21 #message '+CC5X -bu'
   22 
   23 #define _HWPID_     HWPID_IQRF_TECH__DEMO_DDC_SE01
   24 #define _HWPIDver_  0x0001
   25 
   26 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   27 // IQRF Standards documentation https://doc.iqrf.org/
   28 
   29 // This example implements 4 sensors according to the IQRF Sensors standard
   30 // 1st sensor is on-board TR temperature sensor.
   31 // 2nd sensor is either Dallas 18B20 or MCP9802 temperature sensor at DDC-SE-01 board (according to the HW jumper position) chosen at the runtime based on the SW detection.
   32 // 3rd sensor is light intensity indicator at DDC-SE-01 board (value range is 0[max light]-127[max dark]).
   33 // 4th sensor is potentiometer value at DDC-SE-01 board (value range is 0[left stop]-127[right stop]).
   34 
   35 // Default IQRF include (modify the path according to your setup)
   36 #include "IQRF.h"
   37 
   38 // Default DPA header (modify the path according to your setup)
   39 #include "DPA.h"
   40 // Default Custom DPA Handler header (modify the path according to your setup)
   41 #include "DPAcustomHandler.h"
   42 // IQRF standards header (modify the path according to your setup)
   43 #include "IQRFstandard.h"
   44 #include "IQRF_HWPID.h"
   45 
   46 //############################################################################################
   47 
   48 // Number of implemented sensors
   49 #define SENSORS_COUNT 4
   50 
   51 // Variable to store sensor value at Get?_????() methods. This example implements sensors returning maximum 2 bytes of data.
   52 uns16 sensorValue @ param3;
   53 
   54 // Reads sensor value to the sensorValue variable and to responseFRCvalue(2B) variable
   55 bit Get0_TemperatureTR();
   56 bit Get1_Temperature();
   57 bit Get2_BinaryData_Light();
   58 bit Get3_BinaryData_Potentiometer();
   59 
   60 // Temperature sensors read routine for both DDC-SE-01 sensor types
   61 void GetTemperature();
   62 
   63 // Stores sensor value byte(s) to the FSR1[+1...], in case of PCMD_STD_SENSORS_READ_TYPES_AND_VALUES sensor type is stored before value byte(s)
   64 void StoreValue( uns8 sensorType );
   65 
   66 // Sensor connected to PORT C.3 (compatible with DDC-SE-01)
   67 #define OneWire_TRIS         TRISC.3
   68 #define OneWire_IO_IN        PORTC.3
   69 #define OneWire_IO_OUT       LATC.3
   70 
   71 // Writes sensor configuration (resolution)
   72 bit Ds18B20WriteConfig( uns8 value );
   73 
   74 // Resets OneWire
   75 bit OneWireReset();
   76 // Reads OneWire byte
   77 uns8 OneWireReadByte();
   78 // Writes OneWire byte
   79 void OneWireWriteByte( uns8 byte );
   80 
   81 // DS18B20 commands
   82 #define CMD_READROM       0x33
   83 #define CMD_CONVERTTEMP   0x44
   84 #define CMD_CPYSCRATCHPAD 0x48
   85 #define CMD_WSCRATCHPAD   0x4e
   86 #define CMD_MATCHROM      0x55
   87 #define CMD_RPWRSUPPLY    0xb4
   88 #define CMD_RECEEPROM     0xb8
   89 #define CMD_RSCRATCHPAD   0xbe
   90 #define CMD_SKIPROM       0xcc
   91 #define CMD_ALARMSEARCH   0xec
   92 #define CMD_SEARCHROM     0xf0
   93 
   94 // I2C SCL frequency [Hz]
   95 #define I2Cfrequency      50000
   96 
   97 // MCP9802 address
   98 #define I2C_ADR           0b1001.0110
   99 
  100 // TRUE if DS18B20 is enabled at runtime at startup, FALSE in case of MCP9802
  101 bit isDS18B20;
  102 // Final DS18B20 temperature value read by state machine
  103 uns16 temperature;
  104 
  105 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
  106 //############################################################################################
  107 bit CustomDpaHandler()
  108 //############################################################################################
  109 {
  110   // This forces CC5X to wisely use MOVLB instructions (doc says:  The 'default' bank is used by the compiler for loops and labels when the algorithm gives up finding the optimal choice)
  111 #pragma updateBank default = UserBank_01
  112 
  113   // Finite machine states
  114   typedef enum
  115   {
  116     S_ResetConvertT = 0,
  117     S_SkipRomConvertT,
  118     S_CmdConvertT,
  119 
  120     S_WaitConvertT,
  121 
  122     S_ResetReadTemp,
  123     S_SkipRomReadTemp,
  124     S_CmdReadTemp,
  125     S_Byte1ReadTemp,
  126     S_Byte2ReadTemp
  127   } TState;
  128 
  129   // Handler presence mark
  130   clrwdt();
  131 
  132   // Sleeping parameters, valid when Time != 0
  133   static TPerOSSleep_Request PerOSSleep_Request;
  134   // Finite machine state
  135   static uns8 state; // = S_ResetConvertT = 0
  136   // Pre-read lower temperature byte
  137   static uns8 temperatureByteLow;
  138   // Conversion timeout counter
  139   static uns8 timeout;
  140 
  141   // Detect DPA event to handle
  142   switch ( GetDpaEvent() )
  143   {
  144     // -------------------------------------------------
  145     case DpaEvent_Interrupt:
  146       // Do an extra quick background interrupt work
  147       // ! 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.
  148       // ! 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.
  149       // ! 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.
  150       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
  151       // ! Make sure race condition does not occur when accessing those variables at other places.
  152       // ! 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.
  153       // ! Do not call any OS functions except setINDFx().
  154       // ! Do not use any OS variables especially for writing access.
  155       // ! 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.
  156 
  157       // ms per TMR6 interrupt
  158 #define TICKS_LEN  10
  159 
  160       //  If TMR6 interrupt occurred
  161       if ( TMR6IF )
  162       {
  163         // Unmask interrupt
  164         TMR6IF = 0;
  165         // Decrement count
  166         if ( timeout != 0 )
  167           timeout--;
  168       }
  169       return Carry;
  170 
  171       // -------------------------------------------------
  172     case DpaEvent_Idle:
  173       // Do a quick background work when RF packet is not received
  174 
  175       // Should go to sleep?
  176       if ( PerOSSleep_Request.Time != 0 )
  177       {
  178         // Copy sleep parameters to the DPA request
  179         _DpaMessage.PerOSSleep_Request.Time = PerOSSleep_Request.Time;
  180         _DpaMessage.PerOSSleep_Request.Control = PerOSSleep_Request.Control;
  181         // Switch off sleeping time=flag
  182         PerOSSleep_Request.Time = 0;
  183         // Finalize OS Sleep DPA Request
  184         _DpaDataLength = sizeof( _DpaMessage.PerOSSleep_Request );
  185         _PNUM = PNUM_OS;
  186         _PCMD = CMD_OS_SLEEP;
  187         // I2C down
  188         if ( !isDS18B20 )
  189           _DpaApiI2Cshutdown();
  190         // Perform local DPA Request to go to sleep
  191         DpaApiLocalRequest();
  192         // I2C up
  193         if ( !isDS18B20 )
  194           _DpaApiI2Cinit( I2CcomputeFrequency( I2Cfrequency ) );
  195       }
  196 
  197       // Run finite state machine to read temperature from DS18B20 at background so the temperature value is immediately ready for FRC
  198       if ( !isDS18B20 )
  199         break;
  200 
  201       // Make sure 1Wire data pin at LATX.y is low as it might be set by another PORTX.? pin manipulation
  202       OneWire_IO_OUT = 0;
  203 
  204       skip( state );
  205 #pragma computedGoto 1
  206       goto _S_ResetConvertT;
  207       goto _S_SkipRomConvertT;
  208       goto _S_CmdConvertT;
  209       goto _S_WaitConvertT;
  210       goto _S_ResetReadTemp;
  211       goto _S_SkipRomReadTemp;
  212       goto _S_CmdReadTemp;
  213       goto _S_Byte1ReadTemp;
  214       goto _S_Byte2ReadTemp;
  215 #pragma computedGoto 0
  216       ;
  217       // --------------
  218 _S_Byte2ReadTemp:
  219       temperature.high8 = OneWireReadByte();
  220       temperature.low8 = temperatureByteLow;
  221 
  222 ResetMachine:
  223       state = S_ResetConvertT;
  224       goto ExitMachine;
  225 
  226       // --------------
  227 _S_ResetConvertT:
  228 _S_ResetReadTemp:
  229       if ( !OneWireReset() )
  230       {
  231 _S_Error_Reset:
  232         STD_SENSOR_TYPE_TEMPERATURE_SET_ERROR( temperature );
  233         goto ResetMachine;
  234       }
  235       goto NextState;
  236 
  237       // --------------
  238 _S_SkipRomConvertT:
  239 _S_SkipRomReadTemp:
  240       // OneWire: Skip ROM
  241       OneWireWriteByte( CMD_SKIPROM );
  242       goto NextState;
  243 
  244       // --------------
  245 _S_CmdConvertT:
  246       // OneWire: Convert temperature
  247       OneWireWriteByte( CMD_CONVERTTEMP );
  248       // Setup timeout for approx 750 ms (the longest conversion time)
  249       timeout = 2 + 750 / TICKS_LEN;
  250       goto NextState;
  251 
  252       // --------------
  253 _S_WaitConvertT:
  254       if ( OneWireReadByte() == 0xff )
  255         goto NextState;
  256 
  257       // Timeout?
  258       if ( timeout == 0 )
  259         goto _S_Error_Reset;
  260 
  261       goto ExitMachine;
  262 
  263       // --------------
  264 _S_CmdReadTemp:
  265       // OneWire: Read scratchpad
  266       OneWireWriteByte( CMD_RSCRATCHPAD );
  267       goto NextState;
  268 
  269       // --------------
  270 _S_Byte1ReadTemp:
  271       temperatureByteLow = OneWireReadByte();
  272       goto NextState;
  273 
  274       // --------------
  275 NextState:
  276       ++state;
  277 
  278 ExitMachine:
  279       break;
  280 
  281       // -------------------------------------------------
  282     case DpaEvent_Init:
  283       // Do a one time initialization before main loop starts
  284 
  285       // Initialize sensors
  286       // C5 (AN4) as input
  287       moduleInfo();
  288       // Connected TR pins?
  289       if ( !bufferINFO[5].7 )
  290       {
  291         TRISC.6 = 1;
  292         TRISB.4 = 1;
  293       }
  294       // C5 as input
  295       TRISA.5 = 1;
  296       // C1 as input
  297       TRISA.0 = 1;
  298 
  299       // Setup TMR6 to generate ticks on the background (ticks every 10ms)
  300       _PR6 = 250 - 1;
  301       // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms
  302 #if defined( TR7xG )
  303       TMR6MD = 0;
  304       T6CON = 0b1.100.1001;
  305       //  Timer2/4/6 Clock Select bits = FOSC/4
  306       T6CLKCON |= 0b0000.0001;
  307 #else
  308       T6CON = 0b0.1001.1.10;
  309 #endif
  310 
  311       TMR6IE = TRUE;
  312 
  313       // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet)
  314       if ( Ds18B20WriteConfig( 0b0.00.00000 ) )
  315         // DS18B20 is enabled
  316         isDS18B20 = TRUE;
  317       else
  318       {
  319         // Expect MCP9802 is enabled
  320         I2Ctimeout = 0xFF;
  321 
  322 #if defined( TR7xG )
  323         // Do PPS for I2C
  324         unlockPPS();
  325         SSP1CLKPPS = 0x13;  // RC3
  326         SSP1DATPPS = 0x14;  // RC4
  327         RC3PPS = 0x14;      // SCK1/SCL1
  328         RC4PPS = 0x15;      // SD01/SDA1
  329         lockPPS();
  330 #endif
  331 
  332         _DpaApiI2Cinit( I2CcomputeFrequency( I2Cfrequency ) );
  333       }
  334 
  335       break;
  336 
  337       // -------------------------------------------------
  338     case DpaEvent_AfterSleep:
  339       // Called after woken up after sleep
  340       if ( !isDS18B20 )
  341         _DpaApiI2Cinit( I2CcomputeFrequency( I2Cfrequency ) );
  342 
  343       // Called on wake-up from sleep
  344       TMR6IE = TRUE;
  345       _TMR6ON = TRUE;
  346       break;
  347 
  348       // -------------------------------------------------
  349     case DpaEvent_BeforeSleep:
  350       // Called before going to sleep
  351       if ( !isDS18B20 )
  352         _DpaApiI2Cshutdown();
  353 
  354       // -------------------------------------------------
  355     case DpaEvent_DisableInterrupts:
  356       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM)
  357       // Must not use TMR6 any more
  358       _TMR6ON = FALSE;
  359       TMR6IE = FALSE;
  360       break;
  361 
  362       // -------------------------------------------------
  363     case DpaEvent_DpaRequest:
  364       // Called to interpret DPA request for peripherals
  365       // -------------------------------------------------
  366       // Peripheral enumeration
  367       if ( IsDpaEnumPeripheralsRequest() )
  368       {
  369         // We implement 1 standard user peripheral
  370         _DpaMessage.EnumPeripheralsAnswer.UserPerNr |=  1;
  371         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_SENSORS );
  372         _DpaMessage.EnumPeripheralsAnswer.HWPID |= _HWPID_;
  373         _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= _HWPIDver_;
  374 
  375 DpaHandleReturnTRUE:
  376         return TRUE;
  377       }
  378       // -------------------------------------------------
  379       // Get information about peripheral
  380       else if ( IsDpaPeripheralInfoRequest() )
  381       {
  382         if ( _PNUM == PNUM_STD_SENSORS )
  383         {
  384           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_STD_SENSORS;
  385           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ;
  386           // Set standard version
  387           _DpaMessage.PeripheralInfoAnswer.Par1 = STD_SENSORS_VERSION;
  388           goto DpaHandleReturnTRUE;
  389         }
  390 
  391         break;
  392       }
  393       // -------------------------------------------------
  394       else
  395       {
  396         // Handle peripheral command
  397 
  398         // Supported peripheral number?
  399         if ( _PNUM == PNUM_STD_SENSORS )
  400         {
  401           // Supported commands?
  402           switch ( _PCMD )
  403           {
  404             // Invalid command
  405             default:
  406               // Return error
  407               DpaApiReturnPeripheralError( ERROR_PCMD );
  408 
  409               // Sensor enumeration
  410             case PCMD_STD_ENUMERATE:
  411               if ( _DpaDataLength != 0 )
  412                 goto _ERROR_DATA_LEN;
  413 
  414               // Then just enumerate their types
  415               _DpaMessage.Response.PData[0] = STD_SENSOR_TYPE_TEMPERATURE;
  416               _DpaMessage.Response.PData[1] = STD_SENSOR_TYPE_TEMPERATURE;
  417               _DpaMessage.Response.PData[2] = STD_SENSOR_TYPE_BINARYDATA7;
  418               _DpaMessage.Response.PData[3] = STD_SENSOR_TYPE_BINARYDATA7;
  419               W = SENSORS_COUNT;
  420               goto _W2_DpaDataLength;
  421 
  422               // Supported commands. They are handled the same way except one "if" at StoreValue() method
  423             case PCMD_STD_SENSORS_READ_VALUES:
  424             case PCMD_STD_SENSORS_READ_TYPES_AND_VALUES:
  425             {
  426               // No sensor bitmap specified? W = _DpaDataLength. Note: W is used to avoid MOVLB at next if
  427               W = _DpaDataLength;
  428               if ( W == 0 ) // Note: must not modify W
  429               {
  430                 // Actually clears the bitmap
  431 #if &_DpaMessage.Request.PData[0] != &bufferRF[0]
  432 #error
  433 #endif
  434                 clearBufferRF();
  435                 // Simulate 1st only sensor in the bitmap (states of the other unimplemented sensors do not care)
  436                 _DpaMessage.Request.PData[0].0 = 1;
  437                 // Bitmap is 32 bits long = 4
  438                 _DpaDataLength = W = sizeof( _DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap );
  439               }
  440 
  441               // Invalid bitmap (data) length (W = _DpaDataLength)?
  442               if ( W != sizeof( _DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap ) )
  443               {
  444 _ERROR_DATA_LEN:
  445                 // Return error
  446                 DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  447               }
  448 
  449               // Now read the sensors
  450 
  451               // Prepare pointer (minus 1, see below) to store sensor (types and) values to
  452               // Note: 4 sensors at this example cannot return more than DPA_MAX_DATA_LENGTH bytes of data, so it does not have to be checked...
  453               // ... If it would be the case, then ERROR_FAIL must be returned
  454               FSR1 = &_DpaMessage.Response.PData[-1];
  455 
  456               // Store bitmap of sensors to get values from
  457               uns8  sensorsBitmap = FSR1[1];
  458 
  459               // 1st sensor (index 0) selected?
  460               if ( sensorsBitmap.0 )
  461               {
  462                 Get0_TemperatureTR();
  463                 StoreValue( STD_SENSOR_TYPE_TEMPERATURE );
  464               }
  465 
  466               // 2nd sensor (index 1) selected?
  467               if ( sensorsBitmap.1 )
  468               {
  469                 Get1_Temperature();
  470                 StoreValue( STD_SENSOR_TYPE_TEMPERATURE );
  471               }
  472 
  473               // 3rd sensor (index 2) selected?
  474               if ( sensorsBitmap.2 )
  475               {
  476                 Get2_BinaryData_Light();
  477                 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 );
  478               }
  479 
  480               // 4th sensor (index 3) selected?
  481               if ( sensorsBitmap.3 )
  482               {
  483                 Get3_BinaryData_Potentiometer();
  484                 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 );
  485               }
  486 
  487               // Compute returned data bytes count
  488               W = FSR1L - ( (uns16)&_DpaMessage.Response.PData[0] & 0xFF ) + 1;
  489               // Optimization: return W long block of bytes at response
  490 _W2_DpaDataLength:
  491               _DpaDataLength = W;
  492               goto DpaHandleReturnTRUE;
  493             }
  494           }
  495         }
  496 
  497         break;
  498       }
  499 
  500       // -------------------------------------------------
  501     case DpaEvent_FrcValue:
  502       // Called to get FRC value
  503 
  504       // FSR1 for optimization purposes (avoid MOVLB) will be used to point to DataOutBeforeResponseFRC[0...]
  505       FSR1 = &DataOutBeforeResponseFRC[0];
  506       // Check for correct FRC user data
  507       if ( *FSR1++ /*DataOutBeforeResponseFRC[0]*/ == PNUM_STD_SENSORS )
  508       {
  509         // Actually used sensor index
  510         uns8 sensorIndex = FSR1[1] /*DataOutBeforeResponseFRC[2]*/ & 0x1f;
  511         // Test sensor type
  512         switch ( *FSR1++ /*DataOutBeforeResponseFRC[1]*/ )
  513         {
  514           default:
  515             goto DpaHandleReturnFALSE;
  516 
  517             // No type specified, use specified index value
  518           case 0x00:
  519             goto _KeepSensorIndex;
  520 
  521             // For other types make the index value based on the requested index value and sensor type
  522           case STD_SENSOR_TYPE_TEMPERATURE:
  523             if ( sensorIndex > 1 )
  524               goto DpaHandleReturnFALSE;
  525             W = 0 + sensorIndex;
  526             break;
  527 
  528           case STD_SENSOR_TYPE_BINARYDATA7:
  529             if ( sensorIndex > 1 )
  530               goto DpaHandleReturnFALSE;
  531             W = 2 + sensorIndex;
  532             break;
  533         }
  534 
  535         // New sensor index based on type and requested index
  536         sensorIndex = W;
  537 _KeepSensorIndex:
  538 
  539         // Test for supported FRC commands
  540         switch ( _PCMD )
  541         {
  542           default:
  543             goto DpaHandleReturnFALSE;
  544 
  545           case FRC_STD_SENSORS_BIT:
  546           case FRC_STD_SENSORS_1B:
  547           case FRC_STD_SENSORS_2B:
  548             switch ( sensorIndex )
  549             {
  550               default:
  551                 goto DpaHandleReturnFALSE;
  552 
  553               case 0:
  554                 Carry = Get0_TemperatureTR();
  555                 break;
  556 
  557               case 1:
  558                 Carry = Get1_Temperature();
  559                 break;
  560 
  561               case 2:
  562                 Carry = Get2_BinaryData_Light();
  563                 break;
  564 
  565               case 3:
  566                 Carry = Get3_BinaryData_Potentiometer();
  567                 break;
  568             }
  569 
  570             // This type of FRC is not valid for the specified sensor
  571             if ( !Carry )
  572               goto DpaHandleReturnFALSE;
  573 
  574             break;
  575         }
  576 
  577         // Some sensor was measured by FRC, check if there is a sleep request
  578         FSR1++;
  579         if ( INDF1.0 ) // Note: same as DataOutBeforeResponseFRC[3].0
  580         {
  581           // Remember sleep parameters to go to sleep at the Idle event later
  582           PerOSSleep_Request.Time.low8 = FSR1[4 - 3]; // Note: same as DataOutBeforeResponseFRC[4];
  583           PerOSSleep_Request.Time.high8 = FSR1[5 - 3]; // Note: same as DataOutBeforeResponseFRC[5];
  584           PerOSSleep_Request.Control = FSR1[6 - 3]; // Note: same as DataOutBeforeResponseFRC[6];
  585         }
  586       }
  587 
  588       break;
  589 
  590       // -------------------------------------------------
  591     case DpaEvent_FrcResponseTime:
  592       // Called to get FRC response time
  593 
  594       // In this example the FRC commands are fast
  595       switch ( DataOutBeforeResponseFRC[0] )
  596       {
  597         case FRC_STD_SENSORS_BIT:
  598         case FRC_STD_SENSORS_1B:
  599         case FRC_STD_SENSORS_2B:
  600           responseFRCvalue = _FRC_RESPONSE_TIME_40_MS;
  601           break;
  602       }
  603       break;
  604   }
  605 DpaHandleReturnFALSE:
  606   return FALSE;
  607 }
  608 
  609 //############################################################################################
  610 // Increases FSR1 and then stores the byte
  611 void setPlusPlusINDF1( uns8 data @ W )
  612 //############################################################################################
  613 {
  614   FSR1++; // Note: must not modify W
  615   setINDF1( data );
  616 }
  617 
  618 //############################################################################################
  619 // Stores measured sensor value byte(s) and optionally sensor type to the FSR[+1...]
  620 void StoreValue( uns8 sensorType )
  621 //############################################################################################
  622 {
  623   // Is the sensor type to be stored too?
  624   if ( _PCMD == PCMD_STD_SENSORS_READ_TYPES_AND_VALUES )
  625     setPlusPlusINDF1( sensorType );
  626 
  627   // Store lower value byte
  628   setPlusPlusINDF1( sensorValue.low8 );
  629 
  630   // No more value bytes to store?
  631   if ( sensorType.7 != 0 )
  632     return;
  633 
  634   // Store higher value byte
  635   setPlusPlusINDF1( sensorValue.high8 );
  636 
  637   // Note: this example implements sensors returning only 1 or 2 bytes of data. If another data widths are returned, then it must be implemented explicitly.
  638 }
  639 
  640 //############################################################################################
  641 bit setFRCerror()
  642 //############################################################################################
  643 {
  644   responseFRCvalue2B = FRC_STD_FRC_ERROR_VALUE;
  645   return TRUE;
  646 }
  647 
  648 //############################################################################################
  649 bit sensorError;
  650 bit AdjustFrcTemperature()
  651 //############################################################################################
  652 {
  653   // Test for supported FRC commands
  654   switch ( _PCMD )
  655   {
  656     default:
  657       return FALSE;
  658 
  659     case FRC_STD_SENSORS_1B:
  660       // Return sensor FRC value 1B
  661       // Check for out of limits
  662       if ( sensorError || (int16)sensorValue > (int16)( 105.5 * 16 ) || (int16)sensorValue < ( (int16)-20 * 16 ) )
  663         return setFRCerror();
  664 
  665       // Convert to the "F = ( T + 22 ) * 2 " from 1/16 resolution
  666       responseFRCvalue2B = sensorValue + 4; // Note: do rounding when /8
  667       responseFRCvalue2B /= 8;
  668       responseFRCvalue += 44;
  669       break;
  670 
  671     case FRC_STD_SENSORS_2B:
  672       // Return sensor FRC value 2B
  673       if ( sensorError )
  674         return setFRCerror();
  675 
  676       responseFRCvalue2B = sensorValue ^ 0x8000;
  677       break;
  678   }
  679 
  680   return TRUE;
  681 }
  682 
  683 //############################################################################################
  684 // Sensor index 0: measure temperature using the TR sensor
  685 bit Get0_TemperatureTR()
  686 //############################################################################################
  687 {
  688   // Make sure FSR1 is not modified
  689 
  690   // When error, then adjust the standard error values
  691   sensorError = FALSE;
  692   // Measure temperature using TR sensor
  693   if ( getTemperature() == -128 )
  694   {
  695     sensorError = TRUE;
  696     STD_SENSOR_TYPE_TEMPERATURE_SET_ERROR( sensorValue );
  697   }
  698   else
  699   {
  700     // Extent minus sign bit
  701     if ( param3.11 )
  702       param3 |= 0xF000;
  703     // Return sensor value
  704     sensorValue = param3;
  705   }
  706   // Handler FRC
  707   return AdjustFrcTemperature();
  708 }
  709 
  710 //############################################################################################
  711 // Sensor index 1: measure temperature using one of the DDC-SE-01 sensors
  712 bit Get1_Temperature()
  713 //############################################################################################
  714 {
  715   // Make sure FSR1 is not modified
  716 
  717   // Measure temperature using DDC-SE-01 sensors
  718   sensorError = FALSE;
  719   // Read temperature and check for an error
  720   GetTemperature();
  721   // When error, return standard (FRC) error value(s)
  722   if ( STD_SENSOR_TYPE_TEMPERATURE_IS_ERROR( sensorValue ) )
  723     sensorError = TRUE;
  724 
  725   // FrcValues
  726   return AdjustFrcTemperature();
  727 }
  728 
  729 //############################################################################################
  730 bit AdjustFrcBinaryData()
  731 //############################################################################################
  732 {
  733   // Test for supported FRC commands
  734   switch ( _PCMD )
  735   {
  736     default:
  737       return FALSE;
  738 
  739     case FRC_STD_SENSORS_BIT:
  740       // If there is a sensor error, 2-bit FRC cannot indicate it, it returns [01]
  741 
  742       // Number of shifts to get the bit out of the return value
  743       uns8 bitLoop = ( INDF1 >> 5 ) + 1;
  744       // Value to get the bit from
  745       W = sensorValue.low8;
  746       do
  747       {
  748         // Get the bit to Carry
  749         W = rr( W );
  750         // Next bit
  751       } while ( --bitLoop != 0 ); // Note: must not modify W and Carry
  752       // Current (prepared by DPA) FRC value is [01], change it to [11] (means bit is set)
  753       responseFRCvalue.1 = 1; // Note: must not modify Carry
  754       // Is bit set?
  755       if ( !Carry )
  756         // Bit is NOT set, return [10]
  757         responseFRCvalue.0 = 0;
  758       break;
  759 
  760     case FRC_STD_SENSORS_1B:
  761       responseFRCvalue = sensorValue.low8 + 4;
  762       break;
  763   }
  764   return TRUE;
  765 }
  766 
  767 #if defined( TR7xG )
  768 //############################################################################################
  769 // Might not be needed if ADC registers are kept default
  770 void resetADCregs()
  771 //############################################################################################
  772 {
  773   // Start reseting ADC registers from ADCON0 to ADPCH
  774   FSR0 = &ADCON0;
  775   do
  776   {
  777     setINDF0( 0 );
  778     FSR0++;
  779     // Stop reseting at 1st GPR register, ADPCH is the last implemented register before GPR
  780   } while ( !FSR0L.5 );
  781 }
  782 #endif
  783 
  784 //############################################################################################
  785 uns8 ReadAdc()
  786 //############################################################################################
  787 {
  788 #if defined( TR7xG )
  789   // ADC is enabled, ADGO is cleared upon completion, Clock supplied according to ADCLK register, left-justified
  790   ADCON0 = 0b1000.0000;
  791   // ADC Conversion Clock = FOSC/8
  792   ADCLK = 8 / 2 - 1;
  793 #else
  794   // ADC result - left justified, Fosc/8
  795   ADCON1 = 0b0001.0000;
  796 #endif
  797 
  798   // Short delay to stabilize
  799   updateCRC16( W );
  800 
  801   // start ADC
  802   _GO = 1;
  803   // wait for ADC finish
  804   while ( _GO );
  805   return ADRESH;
  806 }
  807 
  808 //############################################################################################
  809 // Sensor index 2: returns light intensity indicator value using DDC-SE-01
  810 bit Get2_BinaryData_Light()
  811 //############################################################################################
  812 {
  813   // Make sure FSR1 is not modified
  814 
  815 #if defined( TR7xG )
  816   // Enable ADC
  817   ADCMD = 0;
  818   // Resets all ADC registers
  819   // VREF- is connected to AVSS, VREF+ is connected to VDD
  820   resetADCregs();
  821   //  ADC Positive Input Channel = ANA0
  822   ADPCH = 0x00;
  823 #else
  824   // ADC setting (AN0 channel)
  825   ADCON0 = 0b0.00000.01;
  826 #endif
  827   // ADC initialization (for more info see PIC datasheet) pin C1 (AN0-D/ANA0-G) as analog input
  828   ANSELA.0 = 1;
  829   // Read ADC
  830   sensorValue.low8 = ReadAdc() / 2;
  831   // Return sensor FRC value
  832   return AdjustFrcBinaryData();
  833 }
  834 
  835 //############################################################################################
  836 // Sensor index 3: returns potentiometer value using DDC-SE-01
  837 bit Get3_BinaryData_Potentiometer()
  838 //############################################################################################
  839 {
  840   // Make sure FSR1 is not modified
  841 
  842 #if defined( TR7xG )
  843   // Enable ADC
  844   ADCMD = 0;
  845   // Resets all ADC registers
  846   // VREF- is connected to AVSS, VREF+ is connected to VDD
  847   resetADCregs();
  848   //  ADC Positive Input Channel = ANA5
  849   ADPCH = 0x05;
  850 #else
  851   // ADC setting (AN4 channel)
  852   ADCON0 = 0b0.00100.01;
  853 #endif
  854   // ADC initialization (for more info see PIC datasheet) pin C5 (AN4-D/ANA5-G) as analog input
  855   ANSELA.5 = 1;
  856   // Read ADC
  857   sensorValue.low8 = ReadAdc() / 2;
  858   // Return sensor FRC value
  859   return AdjustFrcBinaryData();
  860 }
  861 
  862 //############################################################################################
  863 // OneWire and Dallas 18B20 routines
  864 //############################################################################################
  865 
  866 //############################################################################################
  867 void Delay5us( uns8 val @ W ) // Absolutely precise timing but val != 0
  868 //############################################################################################
  869 {
  870   // 16 MHz
  871   // + 0.75us ( W=val, Call )
  872   for ( ;; )
  873   {         // loop time
  874     nop2(); // 0.50us
  875     nop2(); // 1.00us
  876     nop2(); // 1.50us
  877     nop2(); // 2.00us
  878     nop2(); // 2.50us
  879     nop2(); // 3.00us
  880     nop();  // 3.25us
  881     if ( --val == 0 ) // + 0.75us (W--, BTFS )
  882       return;         // + 0.25us
  883     nop2(); // 4.50us
  884   }         // 5.00us (Goto)
  885 }
  886 //############################################################################################
  887 
  888 #define OneWireData0()  { OneWire_TRIS = 0; }     // 0.5us @ 16MHz
  889 #define OneWireData1()  { OneWire_TRIS = 1; }     // 0.5us @ 16MHz
  890 
  891 //############################################################################################
  892 void OneWireWriteByte( uns8 byte )
  893 //############################################################################################
  894 {
  895   uns8 bitLoop = 8;
  896   do
  897   {
  898     // Next sequence is time precision critical
  899     GIE = FALSE;
  900 
  901     OneWireData0();
  902     nop2();         // 1 us [0.5 us]
  903     nop2();         // [1.0 us]
  904     if ( byte.0 )   // 2.5 us [1.75us]
  905       OneWireData1();
  906 
  907     // End of time precision critical sequence
  908     GIE = TRUE;
  909 
  910     // 60us minimum in total, does not have to be precise
  911     Delay5us( ( 60 - 3 ) / 5 + 1 );
  912 
  913     OneWireData1();
  914 
  915     byte >>= 1;
  916   } while ( --bitLoop != 0 );
  917 }
  918 
  919 //############################################################################################
  920 uns8 OneWireReadByte()
  921 //############################################################################################
  922 {
  923   uns8 result;
  924   uns8 bitLoop = 8;
  925   do
  926   {
  927     // Next sequence is time precision critical
  928     GIE = FALSE;
  929 
  930     OneWireData0();
  931     nop2();         // 1 us [0.5 us]
  932     nop2();         // [1.0 us]
  933     OneWireData1();         // 2 us [1.5 us]
  934     Delay5us( 15 / 5 );     // 17 us [16.5 us]
  935 
  936     Carry = 0;              // 17.5 us [16.75 us]
  937     if ( OneWire_IO_IN )    // 18.5 us [ 17.25 us] (condition must not modify Carry)
  938       Carry = 1;
  939 
  940     // End of time precision critical sequence
  941     GIE = TRUE;             // must not modify Carry
  942     result = rr( result );
  943 
  944     // 60us minimum in total, does not have to be precise
  945     Delay5us( ( 60 - 20 ) / 5 + 1 );
  946   } while ( --bitLoop != 0 );
  947 
  948   return result;
  949 }
  950 
  951 //############################################################################################
  952 bit OneWireReset()
  953 //############################################################################################
  954 {
  955   // Setting the pin once to low is enough
  956   OneWire_IO_OUT = 0;
  957   // Reset pulse
  958   OneWireData0();
  959   Delay5us( 500 / 5 );
  960   // Reset pulse end
  961   OneWireData1();
  962   // Next sequence is time precision critical
  963   GIE = FALSE;
  964   // Wait for presence pulse
  965   Delay5us( 70 / 5 );
  966   // End of time precision critical sequence
  967   GIE = TRUE;
  968   // Presence pulse?
  969   if ( OneWire_IO_IN )
  970   {
  971     // No presence, finish initialization sequence
  972     Delay5us( ( 500 - 70 ) / 5 );
  973     return FALSE;
  974   }
  975   else
  976   {
  977     // Presence OK, finish initialization sequence
  978     Delay5us( ( 500 - 70 ) / 5 );
  979     return TRUE;
  980   }
  981 }
  982 
  983 //############################################################################################
  984 void OneWireCmd( uns8 cmd )
  985 //############################################################################################
  986 {
  987   // OneWire: Skip ROM
  988   OneWireWriteByte( CMD_SKIPROM );
  989   // OneWire: Send command
  990   OneWireWriteByte( cmd );
  991 }
  992 
  993 //############################################################################################
  994 bit Ds18B20WriteConfig( uns8 value )
  995 //############################################################################################
  996 {
  997   if ( OneWireReset() )
  998   {
  999     // Write Scratchpad
 1000     OneWireCmd( CMD_WSCRATCHPAD );
 1001 
 1002     // Write TL = ? (we dot not care the value)
 1003     OneWireWriteByte( W );
 1004     // Write TH = ? (we dot not care the value)
 1005     OneWireWriteByte( W );
 1006     // Write Config byte
 1007     OneWireWriteByte( value );
 1008 
 1009     if ( OneWireReset() )
 1010     {
 1011       //  Copy Scratchpad
 1012       OneWireCmd( CMD_CPYSCRATCHPAD );
 1013       return TRUE;
 1014     }
 1015   }
 1016   return FALSE;
 1017 }
 1018 
 1019 //############################################################################################
 1020 void MCP9802GetTemp()
 1021 //############################################################################################
 1022 {
 1023   STD_SENSOR_TYPE_TEMPERATURE_SET_ERROR( sensorValue );
 1024 
 1025   // MCP9802 address
 1026   _DpaApiI2Cstart( I2C_ADR );
 1027   if ( I2CwasTimeout )
 1028     return;
 1029 
 1030   // pointer: 1 = configuration register
 1031   _DpaApiI2Cwrite( 0x01 );
 1032   // configuration: 9-bit ADC
 1033   _DpaApiI2CwriteAndStop( 0x00 );
 1034 
 1035   // MCP9802 address
 1036   _DpaApiI2Cstart( I2C_ADR );
 1037   // pointer: 0 = temperature
 1038   _DpaApiI2CwriteAndStop( 0 );
 1039 
 1040   // MCP9802 address + read
 1041   _DpaApiI2Cstart( I2C_ADR | 1 );
 1042   // store the result
 1043   sensorValue.high8 = _DpaApiI2Cread( TRUE );
 1044   sensorValue.low8 = _DpaApiI2Cread( FALSE );
 1045   _DpaApiI2Cstop();
 1046 }
 1047 
 1048 //############################################################################################
 1049 // Other routines
 1050 //############################################################################################
 1051 
 1052 //############################################################################################
 1053 void GetTemperature()
 1054 //############################################################################################
 1055 {
 1056   // Reads temperature from an enabled sensor
 1057   if ( isDS18B20 )
 1058     // Temperature is ready at the background
 1059     sensorValue = temperature;
 1060   else
 1061   {
 1062     // Temperature value must be read from I2C sensor
 1063     MCP9802GetTemp();
 1064     if ( !STD_SENSOR_TYPE_TEMPERATURE_IS_ERROR( sensorValue ) )
 1065     {
 1066       sensorValue += 0x08;
 1067       sensorValue /= 0x10;
 1068     }
 1069   }
 1070 }
 1071 
 1072 //############################################################################################
 1073 // 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)
 1074 #include "DPAcustomHandler.h"
 1075 //############################################################################################