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