1 // *********************************************************************
    2 //   Custom DPA Handler code example - Standard Sensors - DDC-SE-01    *
    3 // *********************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: 0002_DDC-SE01.c,v $
    7 // Version: $Revision: 1.23 $
    8 // Date:    $Date: 2019/04/03 09:27:58 $
    9 //
   10 // Revision history:
   11 //   2018/10/25  Release for DPA 3.03
   12 //   2017/11/16  Release for DPA 3.02
   13 //   2017/08/14  Release for DPA 3.01
   14 //
   15 // *********************************************************************
   16 
   17 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/
   18 // IQRF Standards documentation https://www.iqrfalliance.org/techDocs/
   19 
   20 // This example implements 4 sensors according to the IQRF Sensors standard
   21 // 1st sensor is on-board TR temperature sensor.
   22 // 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.
   23 // 3rd sensor is light intensity indicator at DDC-SE-01 board (value range is 0[max light]-127[max dark]).
   24 // 4th sensor is potentiometer value at DDC-SE-01 board (value range is 0[left stop]-127[right stop]).
   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-SE-01 sensor types
   52 void GetTemperature();
   53 // Read preset PIC ADC for DDC-SE-01
   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, LoadCode, Remove bond, and Run 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_IQRF_TECH__DEMO_DDC_SE01;
  353         _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x0001;
  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 setFRCerror()
  622 //############################################################################################
  623 {
  624   responseFRCvalue2B = 2;
  625   return TRUE;
  626 }
  627 
  628 //############################################################################################
  629 bit sensorError;
  630 bit AdjustFrcTemperature()
  631 //############################################################################################
  632 {
  633   // Test for supported FRC commands
  634   switch ( _PCMD )
  635   {
  636     default:
  637       return FALSE;
  638 
  639     case FRC_STD_SENSORS_1B:
  640       // Return sensor FRC value 1B
  641       // Check for out of limits
  642       if ( sensorError || (int16)sensorValue > (int16)( 105.5 * 16 ) || (int16)sensorValue < ( (int16)-20 * 16 ) )
  643         return setFRCerror();
  644 
  645       // Convert to the "F = ( T + 22 ) * 2 " from 1/16 resolution
  646       uns16 _sensorValue = sensorValue + 4; // Note: do rounding when /8
  647       responseFRCvalue = (uns8)( _sensorValue / 8 ) + 44;
  648       break;
  649 
  650     case FRC_STD_SENSORS_2B:
  651       // Return sensor FRC value 2B
  652       if ( sensorError )
  653         return setFRCerror();
  654 
  655       responseFRCvalue2B = sensorValue ^ 0x8000;
  656       break;
  657   }
  658 
  659   return TRUE;
  660 }
  661 
  662 //############################################################################################
  663 // Sensor index 0: measure temperature using the TR sensor
  664 bit Get0_TemperatureTR()
  665 //############################################################################################
  666 {
  667   // Make sure FSR1 is not modified
  668 
  669   // When error, then adjust the standard error values
  670   sensorError = FALSE;
  671   // Measure temperature using TR sensor
  672   if ( getTemperature() == -128 )
  673   {
  674     sensorError = TRUE;
  675     sensorValue = 0x8000;
  676   }
  677 
  678   // Return sensor value
  679   sensorValue = param3;
  680   // Handler FRC
  681   return AdjustFrcTemperature();
  682 }
  683 
  684 //############################################################################################
  685 // Sensor index 1: measure temperature using one of the DDC-SE-01 sensors
  686 bit Get1_Temperature()
  687 //############################################################################################
  688 {
  689   // Make sure FSR1 is not modified
  690 
  691   // Measure temperature using DDC-SE-01 sensors
  692   sensorError = FALSE;
  693   // Read temperature and check for an error
  694   GetTemperature();
  695   // When error, return standard (FRC) error value(s)
  696   if ( sensorValue == ERROR_TEMPERATURE )
  697     sensorError = TRUE;
  698 
  699   // FrcValues
  700   return AdjustFrcTemperature();
  701 }
  702 
  703 //############################################################################################
  704 bit AdjustFrcBinaryData()
  705 //############################################################################################
  706 {
  707   // Test for supported FRC commands
  708   switch ( _PCMD )
  709   {
  710     default:
  711       return FALSE;
  712 
  713     case FRC_STD_SENSORS_BIT:
  714       // If there is a sensor error, 2-bit FRC cannot indicate it, it returns [01]
  715 
  716       // Number of shifts to get the bit out of the return value
  717       uns8 bitLoop = ( INDF1 >> 5 ) + 1;
  718       // Value to get the bit from
  719       W = sensorValue.low8;
  720       do
  721       {
  722         // Get the bit to Carry
  723         W = rr( W );
  724         // Next bit
  725       } while ( --bitLoop != 0 ); // Note: must not modify W and Carry
  726       // Current (prepared by DPA) FRC value is [01], change it to [11] (means bit is set)
  727       responseFRCvalue.1 = 1; // Note: must not modify Carry
  728       // Is bit set?
  729       if ( !Carry )
  730         // Bit is NOT set, return [10]
  731         responseFRCvalue.0 = 0;
  732       break;
  733 
  734     case FRC_STD_SENSORS_1B:
  735       responseFRCvalue = sensorValue.low8 + 4;
  736       break;
  737   }
  738   return TRUE;
  739 }
  740 
  741 //############################################################################################
  742 // Sensor index 2: returns light intensity indicator value using DDC-SE-01
  743 bit Get2_BinaryData_Light()
  744 //############################################################################################
  745 {
  746   // Make sure FSR1 is not modified
  747 
  748   // ADC initialization (for more info see PIC datasheet) pin C1 (AN0) as analog input 
  749   ANSELA.0 = 1;
  750   // ADC setting (AN0 channel)
  751   ADCON0 = 0b0.00000.01;
  752   // Read ADC
  753   sensorValue.low8 = ReadAdc() / 2;
  754   // Return sensor FRC value
  755   return AdjustFrcBinaryData();
  756 }
  757 
  758 //############################################################################################
  759 // Sensor index 3: returns potentiometer value using DDC-SE-01
  760 bit Get3_BinaryData_Potentiometer()
  761 //############################################################################################
  762 {
  763   // Make sure FSR1 is not modified
  764 
  765   // ADC initialization (for more info see PIC datasheet) pin C5 (AN4) as analog input 
  766   ANSELA.5 = 1;
  767   // ADC setting (AN4 channel)
  768   ADCON0 = 0b0.00100.01;
  769   // Read ADC
  770   sensorValue.low8 = ReadAdc() / 2;
  771   // Return sensor FRC value
  772   return AdjustFrcBinaryData();
  773 }
  774 
  775 //############################################################################################
  776 // OneWire and Dallas 18B20 routines
  777 //############################################################################################
  778 
  779 //############################################################################################
  780 void Delay5us( uns8 val @ W ) // Absolutely precise timing but val != 0
  781 //############################################################################################
  782 {
  783   // 16 MHz
  784   // + 0.75us ( W=val, Call )
  785   for ( ;; )
  786   {         // loop time
  787     nop2(); // 0.50us
  788     nop2(); // 1.00us
  789     nop2(); // 1.50us
  790     nop2(); // 2.00us
  791     nop2(); // 2.50us
  792     nop2(); // 3.00us
  793     nop();  // 3.25us
  794     if ( --val == 0 ) // + 0.75us (W--, BTFS ) 
  795       return;         // + 0.25us 
  796     nop2(); // 4.50us
  797   }         // 5.00us (Goto)
  798 }
  799 //############################################################################################
  800 
  801 #define OneWireData0()  { OneWire_TRIS = 0; }     // 0.5us @ 16MHz
  802 #define OneWireData1()  { OneWire_TRIS = 1; }     // 0.5us @ 16MHz
  803 
  804 //############################################################################################
  805 void OneWireWriteByte( uns8 byte )
  806 //############################################################################################
  807 {
  808   uns8 bitLoop = 8;
  809   do
  810   {
  811     // Next sequence is time precision critical
  812     GIE = FALSE;
  813 
  814     OneWireData0();
  815     nop2();         // 1 us [0.5 us]
  816     nop2();         // [1.0 us]
  817     if ( byte.0 )   // 2.5 us [1.75us]
  818       OneWireData1();
  819 
  820     // End of time precision critical sequence
  821     GIE = TRUE;
  822 
  823     // 60us minimum in total, does not have to be precise
  824     Delay5us( ( 60 - 3 ) / 5 + 1 );
  825 
  826     OneWireData1();
  827 
  828     byte >>= 1;
  829   } while ( --bitLoop != 0 );
  830 }
  831 
  832 //############################################################################################
  833 uns8 OneWireReadByte()
  834 //############################################################################################
  835 {
  836   uns8 result;
  837   uns8 bitLoop = 8;
  838   do
  839   {
  840     // Next sequence is time precision critical
  841     GIE = FALSE;
  842 
  843     OneWireData0();
  844     nop2();         // 1 us [0.5 us]
  845 #if F_OSC == 16000000
  846     nop2();         // [1.0 us]
  847 #endif
  848     OneWireData1();         // 2 us [1.5 us]
  849     Delay5us( 15 / 5 );     // 17 us [16.5 us]
  850 
  851     Carry = 0;              // 17.5 us [16.75 us]
  852     if ( OneWire_IO_IN )    // 18.5 us [ 17.25 us] (condition must not modify Carry)
  853       Carry = 1;
  854 
  855     // End of time precision critical sequence
  856     GIE = TRUE;             // must not modify Carry
  857     result = rr( result );
  858 
  859     // 60us minimum in total, does not have to be precise
  860     Delay5us( ( 60 - 20 ) / 5 + 1 );
  861   } while ( --bitLoop != 0 );
  862 
  863   return result;
  864 }
  865 
  866 //############################################################################################
  867 bit OneWireReset()
  868 //############################################################################################
  869 {
  870   // Setting the pin once to low is enough
  871   OneWire_IO_OUT = 0;
  872   // Reset pulse
  873   OneWireData0();
  874   Delay5us( 500 / 5 );
  875   // Reset pulse end
  876   OneWireData1();
  877   // Next sequence is time precision critical
  878   GIE = FALSE;
  879   // Wait for presence pulse
  880   Delay5us( 70 / 5 );
  881   // End of time precision critical sequence
  882   GIE = TRUE;
  883   // Presence pulse?
  884   if ( OneWire_IO_IN )
  885   {
  886     // No presence, finish initialization sequence
  887     Delay5us( ( 500 - 70 ) / 5 );
  888     return FALSE;
  889   }
  890   else
  891   {
  892     // Presence OK, finish initialization sequence
  893     Delay5us( ( 500 - 70 ) / 5 );
  894     return TRUE;
  895   }
  896 }
  897 
  898 //############################################################################################
  899 void OneWireCmd( uns8 cmd )
  900 //############################################################################################
  901 {
  902   // OneWire: Skip ROM
  903   OneWireWriteByte( CMD_SKIPROM );
  904   // OneWire: Send command
  905   OneWireWriteByte( cmd );
  906 }
  907 
  908 //############################################################################################
  909 bit Ds18B20WriteConfig( uns8 value )
  910 //############################################################################################
  911 {
  912   if ( OneWireReset() )
  913   {
  914     // Write Scratchpad
  915     OneWireCmd( CMD_WSCRATCHPAD );
  916 
  917     // Write TL = ? (we dot not care the value)
  918     OneWireWriteByte( W );
  919     // Write TH = ? (we dot not care the value)
  920     OneWireWriteByte( W );
  921     // Write Config byte
  922     OneWireWriteByte( value );
  923 
  924     if ( OneWireReset() )
  925     {
  926       //  Copy Scratchpad
  927       OneWireCmd( CMD_CPYSCRATCHPAD );
  928       return TRUE;
  929     }
  930   }
  931   return FALSE;
  932 }
  933 
  934 //############################################################################################
  935 void writeToSSPCON2( uns8 value )
  936 //############################################################################################
  937 {
  938   writeToRAM( &SSPCON2, value );
  939 }
  940 
  941 //############################################################################################
  942 void writeOredToSSPCON2( uns8 value )
  943 //############################################################################################
  944 {
  945   writeToSSPCON2( SSPCON2 | value );
  946 }
  947 
  948 //############################################################################################
  949 // I2C routines
  950 //############################################################################################
  951 
  952 bit i2cTimeout;
  953 
  954 //############################################################################################
  955 void i2c_init()
  956 //############################################################################################
  957 {
  958   // SCL as input (SIM C6)
  959   TRISC.3 = 1;
  960   // SDA as input (SIM C7)
  961   TRISC.4 = 1;
  962 
  963   // I2C master mode     SSPCON = 0b00111000
  964   writeToRAM( &SSPCON1, 0x38 );
  965   writeToSSPCON2( 0x00 );
  966 
  967   // 50 kHz SCL frequency
  968   SSPADD = ( F_OSC / 50000 / 4 ) - 2;
  969   // Disable slew rate control
  970   SMP = 1;
  971 }
  972 
  973 //############################################################################################
  974 void i2c_shutdown()
  975 //############################################################################################
  976 {
  977   // I2C master mode     SSPCON = 0
  978   writeToRAM( &SSPCON1, 0x00 );
  979 }
  980 
  981 //############################################################################################
  982 void i2c_waitForIdle()
  983 //############################################################################################
  984 {
  985   i2cTimeout = FALSE;
  986   uns8 timeout;
  987   // Wait for idle and not writing
  988   timeout = 0;
  989   while ( ( SSPCON2 & 0b0001.1111 ) != 0 || RW_ )
  990     if ( ++timeout == 0 )
  991     {
  992       i2cTimeout = TRUE;
  993       break;
  994     }
  995 }
  996 
  997 //############################################################################################
  998 void i2c_start()
  999 //############################################################################################
 1000 {
 1001   i2c_waitForIdle();
 1002   // SEN = 1
 1003   writeOredToSSPCON2( 0x01 );
 1004 }
 1005 
 1006 //############################################################################################
 1007 void i2c_repStart()
 1008 //############################################################################################
 1009 {
 1010   i2c_waitForIdle();
 1011   // RSEN = 1
 1012   writeOredToSSPCON2( 0x02 );
 1013 }
 1014 
 1015 //############################################################################################
 1016 void i2c_stop()
 1017 //############################################################################################
 1018 {
 1019   i2c_waitForIdle();
 1020   // PEN = 1
 1021   writeOredToSSPCON2( 0x04 );
 1022 }
 1023 
 1024 //############################################################################################
 1025 uns8 i2c_read( bit ack )
 1026 //############################################################################################
 1027 {
 1028   i2c_waitForIdle();
 1029   // RCEN = 1
 1030   writeOredToSSPCON2( 0x08 );
 1031 
 1032   i2c_waitForIdle();
 1033 
 1034   uns8 i2cReadData @ userReg0;
 1035   i2cReadData = SSPBUF;
 1036 
 1037   i2c_waitForIdle();
 1038 
 1039   if ( ack )
 1040     // Acknowledge, ACKDT = 0
 1041     writeToSSPCON2( SSPCON2 & 0xDF );
 1042   else
 1043     // Not acknowledge, ACKDT = 1 
 1044     writeOredToSSPCON2( 0x20 );
 1045 
 1046   // Send acknowledge sequence, ACKEN = 1 
 1047   writeOredToSSPCON2( 0x10 );
 1048   return i2cReadData;
 1049 }
 1050 
 1051 //############################################################################################
 1052 void i2c_write( uns8 i2cWriteData )
 1053 //############################################################################################
 1054 {
 1055   i2c_waitForIdle();
 1056   SSPBUF = i2cWriteData;
 1057 }
 1058 
 1059 //############################################################################################
 1060 void MCP9802GetTemp()
 1061 //############################################################################################
 1062 {
 1063   sensorValue = ERROR_TEMPERATURE;
 1064 
 1065   i2c_start();
 1066   if ( i2cTimeout )
 1067     return;
 1068 
 1069   // MCP9802 address
 1070   i2c_write( I2C_ADR );
 1071   // pointer: 1 = configuration register
 1072   i2c_write( 0x01 );
 1073   // configuration: 9-bit ADC
 1074   i2c_write( 0x00 );
 1075   i2c_stop();
 1076 
 1077   i2c_start();
 1078   // MCP9802 address
 1079   i2c_write( I2C_ADR );
 1080   // pointer: 0 = temperature
 1081   i2c_write( 0 );
 1082   i2c_stop();
 1083 
 1084   i2c_start();
 1085   // MCP9802 address + read
 1086   i2c_write( I2C_ADR | 1 );
 1087   // store the result
 1088   sensorValue.high8 = i2c_read( TRUE );
 1089   sensorValue.low8 = i2c_read( FALSE );
 1090   i2c_stop();
 1091 }
 1092 
 1093 //############################################################################################
 1094 // Other routines
 1095 //############################################################################################
 1096 
 1097 //############################################################################################
 1098 void GetTemperature()
 1099 //############################################################################################
 1100 {
 1101   // Reads temperature from an enabled sensor
 1102   if ( isDS18B20 )
 1103     // Temperature is ready at the background
 1104     sensorValue = temperature;
 1105   else
 1106   {
 1107     // Temperature value must be read from I2C sensor
 1108     MCP9802GetTemp();
 1109     if ( sensorValue != ERROR_TEMPERATURE )
 1110     {
 1111       sensorValue += 0x08;
 1112       sensorValue /= 0x10;
 1113     }
 1114   }
 1115 }
 1116 
 1117 //############################################################################################
 1118 uns8 ReadAdc()
 1119 //############################################################################################
 1120 {
 1121   // ADC result - left justified, Fosc/8
 1122   ADCON1 = 0b0001.0000;
 1123   // Do a smallest delay for ADC ACQUISITION TIME
 1124   waitMS( 1 );
 1125   // start ADC
 1126   GO = 1;
 1127   // wait for ADC finish
 1128   while ( GO );
 1129   return ADRESH;
 1130 }
 1131 
 1132 //############################################################################################
 1133 // 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) 
 1134 #include "DPAcustomHandler.h"
 1135 //############################################################################################