1 // *********************************************************************
    2 //   Custom DPA Handler code example - Standard Sensors - DDC-SE01     *
    3 // *********************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: 0002_DDC-SE01.c,v $
    7 // Version: $Revision: 1.10 $
    8 // Date:    $Date: 2017/12/11 19:26:49 $
    9 //
   10 // Revision history:
   11 //   2017/11/16  Release for DPA 3.02
   12 //   2017/08/14  Release for DPA 3.01
   13 //
   14 // *********************************************************************
   15 
   16 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/
   17 
   18 // This example implements 4 sensors according to the IQRF Sensors standard
   19 // 1st sensor is on-board TR temperature sensor.
   20 // 2nd sensor is either Dallas 18B20 or MCP9802 temperature sensor at DDC-SE01 board (according to the HW jumper position) chosen at the runtime based on the SW detection.
   21 // 3rd sensor is light intensity indicator at DDC-SE01 board (value range is 0[max light]-127[max dark]).
   22 // 4th sensor is potentiometer value at DDC-SE01 board (value range is 0[left stop]-127[right stop]).
   23 
   24 // The example will not work at demo DPA version because demo DPA does not support PCMD != 0
   25 
   26 // Default IQRF include (modify the path according to your setup)
   27 #include "IQRF.h"
   28 
   29 // Default DPA header (modify the path according to your setup)
   30 #include "DPA.h"
   31 // Default Custom DPA Handler header (modify the path according to your setup)
   32 #include "DPAcustomHandler.h"
   33 // IQRF standards header (modify the path according to your setup)
   34 #include "IQRFstandard.h"
   35 #include "IQRF_HWPID.h"
   36 
   37 //############################################################################################
   38 
   39 // Number of implemented sensors
   40 #define SENSORS_COUNT 4
   41 
   42 // Variable to store sensor value at Get?_????() methods. This example implements sensors returning maximum 2 bytes of data.
   43 uns16 sensorValue @ param3;
   44 
   45 // Reads sensor value to the sensorValue variable and to responseFRCvalue(2B) variable
   46 bit Get0_TemperatureTR();
   47 bit Get1_Temperature();
   48 bit Get2_BinaryData_Light();
   49 bit Get3_BinaryData_Potentiometer();
   50 
   51 // Temperature sensors read routine for both DDC-SE01 sensor types
   52 void GetTemperature();
   53 // Read preset PIC ADC for DDC-SE01
   54 uns8 ReadAdc();
   55 
   56 // 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)
   57 void StoreValue( uns8 sensorType );
   58 
   59 // Sensor connected to PORT C.3 (compatible with DDC-SE-01)
   60 #define OneWire_TRIS         TRISC.3
   61 #define OneWire_IO_IN        PORTC.3
   62 #define OneWire_IO_OUT       LATC.3
   63 
   64 // Writes sensor configuration (resolution)
   65 bit Ds18B20WriteConfig( uns8 value );
   66 
   67 // Resets OneWire
   68 bit OneWireReset();
   69 // Reads OneWire byte
   70 uns8 OneWireReadByte();
   71 // Writes OneWire byte
   72 void OneWireWriteByte( uns8 byte );
   73 
   74 // DS18B20 commands
   75 #define CMD_READROM       0x33
   76 #define CMD_CONVERTTEMP   0x44
   77 #define CMD_CPYSCRATCHPAD 0x48
   78 #define CMD_WSCRATCHPAD   0x4e
   79 #define CMD_MATCHROM      0x55
   80 #define CMD_RPWRSUPPLY    0xb4
   81 #define CMD_RECEEPROM     0xb8
   82 #define CMD_RSCRATCHPAD   0xbe
   83 #define CMD_SKIPROM       0xcc
   84 #define CMD_ALARMSEARCH   0xec
   85 #define CMD_SEARCHROM     0xf0
   86 
   87 // I2C routines
   88 void i2c_init();
   89 void i2c_shutdown();
   90 void i2c_waitForIdle();
   91 void i2c_start();
   92 void i2c_repStart();
   93 void i2c_stop();
   94 uns8 i2c_read( bit ack );
   95 void i2c_write( uns8 i2cWriteData );
   96 
   97 // MCP9802 address
   98 #define I2C_ADR             0b10010110
   99 // Power pin
  100 #define PWR_SENSOR_TRIS     TRISC.7
  101 #define PWR_SENSOR_IO       LATC.7
  102 
  103 // Special temperature value to indicate a sensor error, compatible with IQRF sensor standard
  104 #define ERROR_TEMPERATURE 0x8000
  105 
  106 // TRUE if DS18B20 is enabled at runtime at startup, FALSE in case of MCP9802
  107 bit isDS18B20;
  108 // Final DS18B20 temperature value read by state machine
  109 uns16 temperature;
  110 
  111 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
  112 //############################################################################################
  113 bit CustomDpaHandler()
  114 //############################################################################################
  115 {
  116   // 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)
  117 #pragma updateBank default = UserBank_01
  118 
  119   // Finite machine states
  120   typedef enum
  121   {
  122     S_ResetConvertT = 0,
  123     S_SkipRomConvertT,
  124     S_CmdConvertT,
  125 
  126     S_WaitConvertT,
  127 
  128     S_ResetReadTemp,
  129     S_SkipRomReadTemp,
  130     S_CmdReadTemp,
  131     S_Byte1ReadTemp,
  132     S_Byte2ReadTemp
  133   } TState;
  134 
  135   // Handler presence mark
  136   clrwdt();
  137 
  138   // Sleeping parameters, valid when Time != 0
  139   static TPerOSSleep_Request PerOSSleep_Request;
  140   // Finite machine state
  141   static uns8 state; // = S_ResetConvertT = 0
  142   // Pre-read lower temperature byte
  143   static uns8 temperatureByteLow;
  144   // Conversion timeout counter
  145   static uns8 timeout;
  146 
  147   // Detect DPA event to handle
  148   switch ( GetDpaEvent() )
  149   {
  150     // -------------------------------------------------
  151     case DpaEvent_Interrupt:
  152       // Do an extra quick background interrupt work
  153       // ! 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.
  154       // ! 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.
  155       // ! 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.
  156       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
  157       // ! Make sure race condition does not occur when accessing those variables at other places.
  158       // ! 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.
  159       // ! Do not call any OS functions except setINDFx().
  160       // ! Do not use any OS variables especially for writing access.
  161       // ! 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.
  162 
  163       // ms per TMR6 interrupt
  164 #define TICKS_LEN  10
  165 
  166       //  If TMR6 interrupt occurred
  167       if ( TMR6IF )
  168       {
  169         // Unmask interrupt
  170         TMR6IF = 0;
  171         // Decrement count
  172         if ( timeout != 0 )
  173           timeout--;
  174       }
  175       return Carry;
  176 
  177       // -------------------------------------------------
  178     case DpaEvent_Idle:
  179       // Do a quick background work when RF packet is not received
  180 
  181       // Should go to sleep?
  182       if ( PerOSSleep_Request.Time != 0 )
  183       {
  184         // Copy sleep parameters to the DPA request
  185         _DpaMessage.PerOSSleep_Request.Time = PerOSSleep_Request.Time;
  186         _DpaMessage.PerOSSleep_Request.Control = PerOSSleep_Request.Control;
  187         // Switch off sleeping time=flag
  188         PerOSSleep_Request.Time = 0;
  189         // Finalize OS Sleep DPA Request
  190         _DpaDataLength = sizeof( _DpaMessage.PerOSSleep_Request );
  191         _PNUM = PNUM_OS;
  192         _PCMD = CMD_OS_SLEEP;
  193         // Perform local DPA Request to go to sleep
  194         DpaApiLocalRequest();
  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         temperature = 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 work 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       TRISA.5 = 1;
  295 
  296       // C1 (AN0) as input 
  297       TRISA.0 = 1;
  298 
  299       // Setup TMR6 to generate ticks on the background (ticks every 10ms)
  300 #if F_OSC == 16000000
  301       PR6 = 250 - 1;
  302       T6CON = 0b0.1001.1.10;    // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms
  303 #else
  304 #error Unsupported oscillator frequency
  305 #endif
  306 
  307       // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet)
  308       if ( Ds18B20WriteConfig( 0b0.00.00000 ) )
  309         // DS18B20 is enabled
  310         isDS18B20 = TRUE;
  311       else
  312         // Expect MCP9802 is enabled
  313         i2c_init();
  314 
  315       break;
  316 
  317       // -------------------------------------------------
  318     case DpaEvent_AfterSleep:
  319       // Called after woken up after sleep
  320       if ( !isDS18B20 )
  321         i2c_init();
  322 
  323       // Called on wake-up from sleep
  324       TMR6IE = TRUE;
  325       TMR6ON = TRUE;
  326       break;
  327 
  328       // -------------------------------------------------
  329     case DpaEvent_BeforeSleep:
  330       // Called before going to sleep
  331       if ( !isDS18B20 )
  332         i2c_shutdown();
  333 
  334       // -------------------------------------------------
  335     case DpaEvent_DisableInterrupts:
  336       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart and RFPGM)
  337       // Must not use TMR6 any more
  338       TMR6ON = FALSE;
  339       TMR6IE = FALSE;
  340       break;
  341 
  342       // -------------------------------------------------
  343     case DpaEvent_DpaRequest:
  344       // Called to interpret DPA request for peripherals
  345       // -------------------------------------------------
  346       // Peripheral enumeration
  347       if ( IsDpaEnumPeripheralsRequest() )
  348       {
  349         // We implement 1 standard user peripheral
  350         _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
  351         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_SENSORS );
  352         _DpaMessage.EnumPeripheralsAnswer.HWPID = HWPID_MICRORISC__DEMO_DDC_SE01;
  353         _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x0000;
  354 
  355 DpaHandleReturnTRUE:
  356         return TRUE;
  357       }
  358       // -------------------------------------------------
  359       // Get information about peripheral
  360       else if ( IsDpaPeripheralInfoRequest() )
  361       {
  362         if ( _PNUM == PNUM_STD_SENSORS )
  363         {
  364           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_STD_SENSORS;
  365           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  366           // Set standard version
  367           _DpaMessage.PeripheralInfoAnswer.Par1 = 13;
  368           goto DpaHandleReturnTRUE;
  369         }
  370 
  371         break;
  372       }
  373       // -------------------------------------------------
  374       else
  375       {
  376         // Handle peripheral command
  377 
  378         // Supported peripheral number?
  379         if ( _PNUM == PNUM_STD_SENSORS )
  380         {
  381           // Supported commands?
  382           switch ( _PCMD )
  383           {
  384             // Invalid command
  385             default:
  386               // Return error
  387               DpaApiReturnPeripheralError( ERROR_PCMD );
  388 
  389               // Sensor enumeration
  390             case PCMD_STD_ENUMERATE:
  391               if ( _DpaDataLength != 0 )
  392                 goto _ERROR_DATA_LEN;
  393 
  394               // Then just enumerate their types
  395               _DpaMessage.Response.PData[0] = STD_SENSOR_TYPE_TEMPERATURE;
  396               _DpaMessage.Response.PData[1] = STD_SENSOR_TYPE_TEMPERATURE;
  397               _DpaMessage.Response.PData[2] = STD_SENSOR_TYPE_BINARYDATA7;
  398               _DpaMessage.Response.PData[3] = STD_SENSOR_TYPE_BINARYDATA7;
  399               W = SENSORS_COUNT;
  400               goto _W2_DpaDataLength;
  401 
  402               // Supported commands. They are handled the same way except one "if" at StoreValue() method
  403             case PCMD_STD_SENSORS_READ_VALUES:
  404             case PCMD_STD_SENSORS_READ_TYPES_AND_VALUES:
  405             {
  406               // No sensor bitmap specified? W = _DpaDataLength. Note: W is used to avoid MOVLB at next if
  407               W = _DpaDataLength;
  408               if ( W == 0 ) // Note: must not modify W
  409               {
  410                 // Actually clears the bitmap
  411 #if &_DpaMessage.Request.PData[0] != &bufferRF[0]
  412 #error
  413 #endif
  414                 clearBufferRF();
  415                 // Simulate 1st only sensor in the bitmap (states of the other unimplemented sensors do not care)
  416                 _DpaMessage.Request.PData[0].0 = 1;
  417                 // Bitmap is 32 bits long = 4
  418                 _DpaDataLength = W = 4;
  419             }
  420 
  421               // Invalid bitmap (data) length (W = _DpaDataLength)?
  422               if ( W != 4 )
  423               {
  424 _ERROR_DATA_LEN:
  425                 // Return error
  426                 DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  427               }
  428 
  429               // Now read the sensors
  430 
  431               // Prepare pointer (minus 1, see below) to store sensor (types and) values to
  432               // 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...
  433               // ... If it would be the case, then ERROR_FAIL must be returned
  434               FSR1 = &_DpaMessage.Response.PData[-1];
  435 
  436               // Store bitmap of sensors to get values from
  437               uns8  sensorsBitmap = FSR1[1];
  438 
  439               // 1st sensor (index 0) selected?
  440               if ( sensorsBitmap.0 )
  441               {
  442                 Get0_TemperatureTR();
  443                 StoreValue( STD_SENSOR_TYPE_TEMPERATURE );
  444               }
  445 
  446               // 2nd sensor (index 1) selected?
  447               if ( sensorsBitmap.1 )
  448               {
  449                 Get1_Temperature();
  450                 StoreValue( STD_SENSOR_TYPE_TEMPERATURE );
  451               }
  452 
  453               // 3rd sensor (index 2) selected?
  454               if ( sensorsBitmap.2 )
  455               {
  456                 Get2_BinaryData_Light();
  457                 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 );
  458               }
  459 
  460               // 4th sensor (index 3) selected?
  461               if ( sensorsBitmap.3 )
  462               {
  463                 Get3_BinaryData_Potentiometer();
  464                 StoreValue( STD_SENSOR_TYPE_BINARYDATA7 );
  465               }
  466 
  467               // Compute returned data bytes count
  468               W = FSR1L - ( (uns16)&_DpaMessage.Response.PData[0] & 0xFF ) + 1;
  469               // Optimization: return W long block of bytes at response
  470 _W2_DpaDataLength:
  471               _DpaDataLength = W;
  472               goto DpaHandleReturnTRUE;
  473           }
  474         }
  475       }
  476 
  477         break;
  478   }
  479 
  480       // -------------------------------------------------
  481     case DpaEvent_FrcValue:
  482       // Called to get FRC value
  483 
  484       // FSR1 for optimization purposes (avoid MOVLB) will be used to point to DataOutBeforeResponseFRC[0...]
  485       FSR1 = &DataOutBeforeResponseFRC[0];
  486       // Check for correct FRC user data
  487       if ( *FSR1++ /*DataOutBeforeResponseFRC[0]*/ == PNUM_STD_SENSORS )
  488       {
  489         // Actually used sensor index
  490         uns8 sensorIndex = FSR1[1] /*DataOutBeforeResponseFRC[2]*/ & 0x1f;
  491         // Test sensor type
  492         switch ( *FSR1++ /*DataOutBeforeResponseFRC[1]*/ )
  493         {
  494           default:
  495             goto DpaHandleReturnFALSE;
  496 
  497             // No type specified, use specified index value
  498           case 0x00:
  499             goto _KeepSensorIndex;
  500 
  501             // For other types make the index value based on the requested index value and sensor type
  502           case STD_SENSOR_TYPE_TEMPERATURE:
  503             if ( sensorIndex > 1 )
  504               goto DpaHandleReturnFALSE;
  505             W = 0 + sensorIndex;
  506             break;
  507 
  508           case STD_SENSOR_TYPE_BINARYDATA7:
  509             if ( sensorIndex > 1 )
  510               goto DpaHandleReturnFALSE;
  511             W = 2 + sensorIndex;
  512             break;
  513         }
  514 
  515         // New sensor index based on type and requested index
  516         sensorIndex = W;
  517 _KeepSensorIndex:
  518 
  519         // Test for supported FRC commands
  520         switch ( _PCMD )
  521         {
  522           default:
  523             goto DpaHandleReturnFALSE;
  524 
  525           case FRC_STD_SENSORS_BIT:
  526           case FRC_STD_SENSORS_1B:
  527           case FRC_STD_SENSORS_2B:
  528             switch ( sensorIndex )
  529             {
  530               default:
  531                 goto DpaHandleReturnFALSE;
  532 
  533               case 0:
  534                 Carry = Get0_TemperatureTR();
  535                 break;
  536 
  537               case 1:
  538                 Carry = Get1_Temperature();
  539                 break;
  540 
  541               case 2:
  542                 Carry = Get2_BinaryData_Light();
  543                 break;
  544 
  545               case 3:
  546                 Carry = Get3_BinaryData_Potentiometer();
  547                 break;
  548             }
  549 
  550             // This type of FRC is not valid for the specified sensor
  551             if ( !Carry )
  552               goto DpaHandleReturnFALSE;
  553 
  554             break;
  555         }
  556 
  557         // Some sensor was measured by FRC, check if there is a sleep request
  558         FSR1++;
  559         if ( INDF1.0 ) // Note: same as DataOutBeforeResponseFRC[3].0
  560         {
  561           // Remember sleep parameters to go to sleep at the Idle event later
  562           PerOSSleep_Request.Time.low8 = FSR1[4 - 3]; // Note: same as DataOutBeforeResponseFRC[4];
  563           PerOSSleep_Request.Time.high8 = FSR1[5 - 3]; // Note: same as DataOutBeforeResponseFRC[5];
  564           PerOSSleep_Request.Control = FSR1[6 - 3]; // Note: same as DataOutBeforeResponseFRC[6];
  565         }
  566       }
  567 
  568       break;
  569 
  570       // -------------------------------------------------
  571     case DpaEvent_FrcResponseTime:
  572       // Called to get FRC response time
  573 
  574       // In this example the FRC commands are fast 
  575       switch ( DataOutBeforeResponseFRC[0] )
  576       {
  577         case FRC_STD_SENSORS_BIT:
  578         case FRC_STD_SENSORS_1B:
  579         case FRC_STD_SENSORS_2B:
  580           responseFRCvalue = _FRC_RESPONSE_TIME_40_MS;
  581           break;
  582       }
  583       break;
  584 }
  585 DpaHandleReturnFALSE:
  586   return FALSE;
  587 }
  588 
  589 //############################################################################################
  590 // Increases FSR1 and then stores the byte
  591 void setPlusPlusINDF1( uns8 data @ W )
  592 //############################################################################################
  593 {
  594   FSR1++; // Note: must not modify W
  595   setINDF1( data );
  596 }
  597 
  598 //############################################################################################
  599 // Stores measured sensor value byte(s) and optionally sensor type to the FSR[+1...]
  600 void StoreValue( uns8 sensorType )
  601 //############################################################################################
  602 {
  603   // Is the sensor type to be stored too?
  604   if ( _PCMD == PCMD_STD_SENSORS_READ_TYPES_AND_VALUES )
  605     setPlusPlusINDF1( sensorType );
  606 
  607   // Store lower value byte
  608   setPlusPlusINDF1( sensorValue.low8 );
  609 
  610   // No more value bytes to store?
  611   if ( sensorType.7 != 0 )
  612     return;
  613 
  614   // Store higher value byte
  615   setPlusPlusINDF1( sensorValue.high8 );
  616 
  617   // 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.
  618 }
  619 
  620 //############################################################################################
  621 bit setFRCerror1B()
  622 //############################################################################################
  623 {
  624   responseFRCvalue = 2;
  625   return TRUE;
  626 }
  627 
  628 //############################################################################################
  629 bit setFRCerror2B()
  630 //############################################################################################
  631 {
  632   responseFRCvalue2B = 2;
  633   return TRUE;
  634 }
  635 
  636 //############################################################################################
  637 bit sensorError;
  638 bit AdjustFrcTemperature()
  639 //############################################################################################
  640 {
  641   // Test for supported FRC commands
  642   switch ( _PCMD )
  643   {
  644     default:
  645       return FALSE;
  646 
  647     case FRC_STD_SENSORS_1B:
  648       // Return sensor FRC value 1B
  649       // Check for out of limits
  650       if ( sensorError || (int16)sensorValue > (int16)( 105.5 * 16 ) || (int16)sensorValue < ( (int16)-20 * 16 ) )
  651         return setFRCerror1B();
  652 
  653       // Convert to the "F = ( T + 22 ) * 2 " from 1/16 resolution
  654       uns16 _sensorValue = sensorValue + 4; // Note: do rounding when /8
  655       responseFRCvalue = (uns8)( _sensorValue / 8 ) + 44;
  656       break;
  657 
  658     case FRC_STD_SENSORS_2B:
  659       // Return sensor FRC value 2B
  660       if ( sensorError )
  661         return setFRCerror2B();
  662 
  663       responseFRCvalue2B = sensorValue ^ 0x8000;
  664       break;
  665   }
  666 
  667   return TRUE;
  668 }
  669 
  670 //############################################################################################
  671 // Sensor index 0: measure temperature using the TR sensor
  672 bit Get0_TemperatureTR()
  673 //############################################################################################
  674 {
  675   // Make sure FSR1 is not modified
  676 
  677   // When error, then adjust the standard error values
  678   sensorError = FALSE;
  679   // Measure temperature using TR sensor
  680   if ( getTemperature() == -128 )
  681   {
  682     sensorError = TRUE;
  683     sensorValue = 0x8000;
  684   }
  685 
  686   // Return sensor value
  687   sensorValue = param3;
  688   // Handler FRC
  689   return AdjustFrcTemperature();
  690 }
  691 
  692 //############################################################################################
  693 // Sensor index 1: measure temperature using one of the DDC-SE01 sensors
  694 bit Get1_Temperature()
  695 //############################################################################################
  696 {
  697   // Make sure FSR1 is not modified
  698 
  699   // Measure temperature using DDC-SE01 sensors
  700   sensorError = FALSE;
  701   // Read temperature and check for an error
  702   GetTemperature();
  703   // When error, return standard (FRC) error value(s)
  704   if ( sensorValue == ERROR_TEMPERATURE )
  705     sensorError = TRUE;
  706 
  707   // FrcValues
  708   return AdjustFrcTemperature();
  709 }
  710 
  711 //############################################################################################
  712 bit AdjustFrcBinaryData()
  713 //############################################################################################
  714 {
  715   // Test for supported FRC commands
  716   switch ( _PCMD )
  717   {
  718     default:
  719       return FALSE;
  720 
  721     case FRC_STD_SENSORS_BIT:
  722       // If there is a sensor error, 2-bit FRC cannot indicate it, it returns [01]
  723 
  724       // Number of shifts to get the bit out of the return value
  725       uns8 bitLoop = ( INDF1 >> 5 ) + 1;
  726       // Value to get the bit from
  727       W = sensorValue.low8;
  728       do
  729       {
  730         // Get the bit to Carry
  731         W = rr( W );
  732         // Next bit
  733       } while ( --bitLoop != 0 ); // Note: must not modify W and Carry
  734       // Current (prepared by DPA) FRC value is [01], change it to [11] (means bit is set)
  735       responseFRCvalue.1 = 1; // Note: must not modify Carry
  736       // Is bit set?
  737       if ( !Carry )
  738         // Bit is NOT set, return [10]
  739         responseFRCvalue.0 = 0;
  740       break;
  741 
  742     case FRC_STD_SENSORS_1B:
  743       responseFRCvalue = sensorValue.low8 + 4;
  744       break;
  745   }
  746   return TRUE;
  747 }
  748 
  749 //############################################################################################
  750 // Sensor index 2: returns light intensity indicator value using DDC-SE01
  751 bit Get2_BinaryData_Light()
  752 //############################################################################################
  753 {
  754   // Make sure FSR1 is not modified
  755 
  756   // ADC initialization (for more info see PIC datasheet) pin C1 (AN0) as analog input 
  757   ANSELA.0 = 1;
  758   // ADC setting (AN0 channel)
  759   ADCON0 = 0b0.00000.01;
  760   // Read ADC
  761   sensorValue.low8 = ReadAdc() / 2;
  762   // Return sensor FRC value
  763   return AdjustFrcBinaryData();
  764 }
  765 
  766 //############################################################################################
  767 // Sensor index 3: returns potentiometer value using DDC-SE01
  768 bit Get3_BinaryData_Potentiometer()
  769 //############################################################################################
  770 {
  771   // Make sure FSR1 is not modified
  772 
  773   // ADC initialization (for more info see PIC datasheet) pin C5 (AN4) as analog input 
  774   ANSELA.5 = 1;
  775   // ADC setting (AN4 channel)
  776   ADCON0 = 0b0.00100.01;
  777   // Read ADC
  778   sensorValue.low8 = ReadAdc() / 2;
  779   // Return sensor FRC value
  780   return AdjustFrcBinaryData();
  781 }
  782 
  783 //############################################################################################
  784 // OneWire and Dallas 18B20 routines
  785 //############################################################################################
  786 
  787 //############################################################################################
  788 void Delay5us( uns8 val @ W ) // Absolutely precise timing but val != 0
  789 //############################################################################################
  790 {
  791   // 16 MHz
  792   // + 0.75us ( W=val, Call )
  793   for ( ;; )
  794   {         // loop time
  795     nop2(); // 0.50us
  796     nop2(); // 1.00us
  797     nop2(); // 1.50us
  798     nop2(); // 2.00us
  799     nop2(); // 2.50us
  800     nop2(); // 3.00us
  801     nop();  // 3.25us
  802     if ( --val == 0 ) // + 0.75us (W--, BTFS ) 
  803       return;         // + 0.25us 
  804     nop2(); // 4.50us
  805   }         // 5.00us (Goto)
  806 }
  807 //############################################################################################
  808 
  809 #define OneWireData0()  { OneWire_TRIS = 0; }     // 1us [0.5us @ 16MHz]
  810 #define OneWireData1()  { OneWire_TRIS = 1; }     // 1us [0.5us @ 16MHz]
  811 
  812 //############################################################################################
  813 void OneWireWriteByte( uns8 byte )
  814 //############################################################################################
  815 {
  816   uns8 bitLoop = 8;
  817   do
  818   {
  819     // Next sequence is time precision critical
  820     GIE = FALSE;
  821 
  822     OneWireData0();
  823     nop2();         // 1 us [0.5 us]
  824     nop2();         // [1.0 us]
  825     if ( byte.0 )   // 2.5 us [1.75us]
  826       OneWireData1();
  827 
  828     // End of time precision critical sequence
  829     GIE = TRUE;
  830 
  831     // 60us minimum in total, does not have to be precise
  832     Delay5us( ( 60 - 3 ) / 5 + 1 );
  833 
  834     OneWireData1();
  835 
  836     byte >>= 1;
  837   } while ( --bitLoop != 0 );
  838 }
  839 
  840 //############################################################################################
  841 uns8 OneWireReadByte()
  842 //############################################################################################
  843 {
  844   uns8 result;
  845   uns8 bitLoop = 8;
  846   do
  847   {
  848     // Next sequence is time precision critical
  849     GIE = FALSE;
  850 
  851     OneWireData0();
  852     nop2();         // 1 us [0.5 us]
  853 #if F_OSC == 16000000
  854     nop2();         // [1.0 us]
  855 #endif
  856     OneWireData1();         // 2 us [1.5 us]
  857     Delay5us( 15 / 5 );     // 17 us [16.5 us]
  858 
  859     Carry = 0;              // 17.5 us [16.75 us]
  860     if ( OneWire_IO_IN )    // 18.5 us [ 17.25 us] (condition must not modify Carry)
  861       Carry = 1;
  862 
  863     // End of time precision critical sequence
  864     GIE = TRUE;             // must not modify Carry
  865     result = rr( result );
  866 
  867     // 60us minimum in total, does not have to be precise
  868     Delay5us( ( 60 - 20 ) / 5 + 1 );
  869   } while ( --bitLoop != 0 );
  870 
  871   return result;
  872 }
  873 
  874 //############################################################################################
  875 bit OneWireReset()
  876 //############################################################################################
  877 {
  878   // Setting the pin once to low is enough
  879   OneWire_IO_OUT = 0;
  880   // Reset pulse
  881   OneWireData0();
  882   Delay5us( 500 / 5 );
  883   // Reset pulse end
  884   OneWireData1();
  885   // Next sequence is time precision critical
  886   GIE = FALSE;
  887   // Wait for presence pulse
  888   Delay5us( 70 / 5 );
  889   // End of time precision critical sequence
  890   GIE = TRUE;
  891   // Presence pulse?
  892   if ( OneWire_IO_IN )
  893   {
  894     // No presence, finish initialization sequence
  895     Delay5us( ( 500 - 70 ) / 5 );
  896     return FALSE;
  897   }
  898   else
  899   {
  900     // Presence OK, finish initialization sequence
  901     Delay5us( ( 500 - 70 ) / 5 );
  902     return TRUE;
  903   }
  904 }
  905 
  906 //############################################################################################
  907 void OneWireCmd( uns8 cmd )
  908 //############################################################################################
  909 {
  910   // OneWire: Skip ROM
  911   OneWireWriteByte( CMD_SKIPROM );
  912   // OneWire: Send command
  913   OneWireWriteByte( cmd );
  914 }
  915 
  916 //############################################################################################
  917 bit Ds18B20WriteConfig( uns8 value )
  918 //############################################################################################
  919 {
  920   if ( OneWireReset() )
  921   {
  922     // Write Scratchpad
  923     OneWireCmd( CMD_WSCRATCHPAD );
  924 
  925     // Write TL = ? (we dot not care the value)
  926     OneWireWriteByte( W );
  927     // Write TH = ? (we dot not care the value)
  928     OneWireWriteByte( W );
  929     // Write Config byte
  930     OneWireWriteByte( value );
  931 
  932     if ( OneWireReset() )
  933     {
  934       //  Copy Scratchpad
  935       OneWireCmd( CMD_CPYSCRATCHPAD );
  936       return TRUE;
  937     }
  938   }
  939   return FALSE;
  940 }
  941 
  942 //############################################################################################
  943 void writeToSSPCON2( uns8 value @ param4.high8 )
  944 //############################################################################################
  945 {
  946   writeToRAM( &SSPCON2, value );
  947 }
  948 
  949 //############################################################################################
  950 void writeOredToSSPCON2( uns8 value @ param4.high8 )
  951 //############################################################################################
  952 {
  953   writeToSSPCON2( SSPCON2 | value );
  954 }
  955 
  956 //############################################################################################
  957 // I2C routines
  958 //############################################################################################
  959 
  960 bit i2cTimeout;
  961 
  962 //############################################################################################
  963 void i2c_init()
  964 //############################################################################################
  965 {
  966   // SCL as input (SIM C6)
  967   TRISC.3 = 1;
  968   // SDA as input (SIM C7)
  969   TRISC.4 = 1;
  970 
  971   // I2C master mode     SSPCON = 0b00111000
  972   writeToRAM( &SSPCON1, 0x38 );
  973   writeToSSPCON2( 0x00 );
  974 
  975   // 50 kHz SCL frequency
  976   SSPADD = ( F_OSC / 50000 / 4 ) - 2;
  977   // Disable slew rate control
  978   SMP = 1;
  979 }
  980 
  981 //############################################################################################
  982 void i2c_shutdown()
  983 //############################################################################################
  984 {
  985   // I2C master mode     SSPCON = 0
  986   writeToRAM( &SSPCON1, 0x00 );
  987 }
  988 
  989 //############################################################################################
  990 void i2c_waitForIdle()
  991 //############################################################################################
  992 {
  993   i2cTimeout = FALSE;
  994   uns8 timeout;
  995   // Wait for idle and not writing
  996   timeout = 0;
  997   while ( ( SSPCON2 & 0b0001.1111 ) != 0 || RW_ )
  998     if ( ++timeout == 0 )
  999     {
 1000       i2cTimeout = TRUE;
 1001       break;
 1002     }
 1003 }
 1004 
 1005 //############################################################################################
 1006 void i2c_start()
 1007 //############################################################################################
 1008 {
 1009   i2c_waitForIdle();
 1010   // SEN = 1
 1011   writeOredToSSPCON2( 0x01 );
 1012 }
 1013 
 1014 //############################################################################################
 1015 void i2c_repStart()
 1016 //############################################################################################
 1017 {
 1018   i2c_waitForIdle();
 1019   // RSEN = 1
 1020   writeOredToSSPCON2( 0x02 );
 1021 }
 1022 
 1023 //############################################################################################
 1024 void i2c_stop()
 1025 //############################################################################################
 1026 {
 1027   i2c_waitForIdle();
 1028   // PEN = 1
 1029   writeOredToSSPCON2( 0x04 );
 1030 }
 1031 
 1032 //############################################################################################
 1033 uns8 i2c_read( bit ack )
 1034 //############################################################################################
 1035 {
 1036   i2c_waitForIdle();
 1037   // RCEN = 1
 1038   writeOredToSSPCON2( 0x08 );
 1039 
 1040   i2c_waitForIdle();
 1041 
 1042   uns8 i2cReadData @ userReg0;
 1043   i2cReadData = SSPBUF;
 1044 
 1045   i2c_waitForIdle();
 1046 
 1047   if ( ack )
 1048     // Acknowledge, ACKDT = 0
 1049     writeToSSPCON2( SSPCON2 & 0xDF );
 1050   else
 1051     // Not acknowledge, ACKDT = 1 
 1052     writeOredToSSPCON2( 0x20 );
 1053 
 1054   // Send acknowledge sequence, ACKEN = 1 
 1055   writeOredToSSPCON2( 0x10 );
 1056   return i2cReadData;
 1057 }
 1058 
 1059 //############################################################################################
 1060 void i2c_write( uns8 i2cWriteData @ param2 )
 1061 //############################################################################################
 1062 {
 1063   i2c_waitForIdle();
 1064   SSPBUF = i2cWriteData;
 1065 }
 1066 
 1067 //############################################################################################
 1068 void MCP9802GetTemp()
 1069 //############################################################################################
 1070 {
 1071   sensorValue = ERROR_TEMPERATURE;
 1072 
 1073   i2c_start();
 1074   if ( i2cTimeout )
 1075     return;
 1076 
 1077   // MCP9802 address
 1078   i2c_write( I2C_ADR );
 1079   // pointer: 1 = configuration register
 1080   i2c_write( 0x01 );
 1081   // configuration: 9-bit ADC
 1082   i2c_write( 0x00 );
 1083   i2c_stop();
 1084 
 1085   i2c_start();
 1086   // MCP9802 address
 1087   i2c_write( I2C_ADR );
 1088   // pointer: 0 = temperature
 1089   i2c_write( 0 );
 1090   i2c_stop();
 1091 
 1092   i2c_start();
 1093   // MCP9802 address + read
 1094   i2c_write( I2C_ADR | 1 );
 1095   // store the result
 1096   sensorValue.high8 = i2c_read( TRUE );
 1097   sensorValue.low8 = i2c_read( FALSE );
 1098   i2c_stop();
 1099 }
 1100 
 1101 //############################################################################################
 1102 // Other routines
 1103 //############################################################################################
 1104 
 1105 //############################################################################################
 1106 void GetTemperature()
 1107 //############################################################################################
 1108 {
 1109   // Reads temperature from an enabled sensor
 1110   if ( isDS18B20 )
 1111     // Temperature is ready at the background
 1112     sensorValue = temperature;
 1113   else
 1114   {
 1115     // Temperature value must be read from I2C sensor
 1116     MCP9802GetTemp();
 1117     if ( sensorValue != ERROR_TEMPERATURE )
 1118     {
 1119       sensorValue += 0x08;
 1120       sensorValue /= 0x10;
 1121     }
 1122   }
 1123 }
 1124 
 1125 //############################################################################################
 1126 uns8 ReadAdc()
 1127 //############################################################################################
 1128 {
 1129   // ADC result - left justified, Fosc/8
 1130   ADCON1 = 0b0001.0000;
 1131   // Do a smallest delay for ADC ACQUISITION TIME
 1132   waitMS( 1 );
 1133   // start ADC
 1134   GO = 1;
 1135   // wait for ADC finish
 1136   while ( GO );
 1137   return ADRESH;
 1138 }
 1139 
 1140 //############################################################################################
 1141 // Default Custom DPA Handler header; 2nd include to implement Code bumper to detect too long code of the Custom DPA Handler (modify the path according to your setup) 
 1142 #include "DPAcustomHandler.h"
 1143 //############################################################################################