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