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