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