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