1 // *************************************************************************************
    2 //   Custom DPA Handler code example - Standard Binary output - DDC-RE-01 - LP version *
    3 // *************************************************************************************
    4 // Copyright (c) MICRORISC s.r.o.
    5 //
    6 // File:    $RCSfile: 4802_DDC-RE_LP.c,v $
    7 // Version: $Revision: 1.8 $
    8 // Date:    $Date: 2021/04/26 15:13:51 $
    9 //
   10 // Revision history:
   11 //   2019/03/07  Release for DPA 4.01
   12 //
   13 // *********************************************************************
   14 
   15 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   16 // IQRF Standards documentation https://doc.iqrf.org/
   17 
   18 // This example also implements 2 binary outputs according to the IQRF Binary Outputs standard
   19 // Index 0 i.e. 1st output is Relay #1 @ DDC-RE-01
   20 // Index 1 i.e. 2nd output is Relay #2 @ DDC-RE-01
   21 
   22 // This example must be compiled without a "-bu" compiler switch in order to fit into available Flash memory
   23 
   24 // Default IQRF include (modify the path according to your setup)
   25 #include "IQRF.h"
   26 
   27 // We can save more instructions if needed by the symbol below
   28 // #define  PARAM_CHECK_LEVEL 1
   29 
   30 // Default DPA header (modify the path according to your setup)
   31 #include "DPA.h"
   32 // Default Custom DPA Handler header (modify the path according to your setup)
   33 #include "DPAcustomHandler.h"
   34 // IQRF standards header (modify the path according to your setup)
   35 #include "IQRFstandard.h"
   36 #include "IQRF_HWPID.h"
   37 
   38 //############################################################################################
   39 
   40 // Define useful macro that saves some code but not preset at DPA < 3.01
   41 #if DPA_VERSION_MASTER  < 0x0301
   42 // Optimized macro for both testing enumeration peripherals ELSE peripherals information. See examples
   43 #define IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequestNoSize() if ( _PCMD == CMD_GET_PER_INFO ) if ( _PNUM == PNUM_ENUMERATION )
   44 
   45 #if PARAM_CHECK_LEVEL >= 2
   46 #define IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest() if ( _DpaDataLength == 0 && _PCMD == CMD_GET_PER_INFO ) if ( _PNUM == PNUM_ENUMERATION )
   47 #else
   48 #define IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest() IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequestNoSize()
   49 #endif
   50 #endif
   51 
   52 //############################################################################################
   53 
   54 // ms per ticks
   55 #define TICKS_LEN  10
   56 
   57 // Number of implemented binary outputs
   58 #define OUTPUTS_COUNT 2
   59 
   60 // Sets and Gets state of the indexed binary output
   61 void SetOutput( uns8 state, uns8 index );
   62 bit GetOutput( uns8 index );
   63 
   64 // DDC-RE-01 relay pins
   65 //  C.5 = C8 = Relay#1
   66 #define RELAY1_LAT  LATC.5 
   67 #define RELAY1_TRIS TRISC.5
   68 //  C.2 = C2 = Relay#2
   69 #define RELAY2_LAT  LATC.2 
   70 #define RELAY2_TRIS TRISC.2
   71 
   72 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   73 //############################################################################################
   74 bit CustomDpaHandler()
   75 //############################################################################################
   76 {
   77   // 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)
   78 #pragma updateBank default = UserBank_01
   79 
   80   // Handler presence mark
   81   clrwdt();
   82 
   83   // Timers for outputs. The space must be long enough to fit them all. 2+2 bytes per one binary output. 
   84   //  2B timeout
   85   //  2B startTicks
   86   static uns16  Timers[OUTPUTS_COUNT * 2];
   87 
   88   // Detect DPA event to handle
   89   switch ( GetDpaEvent() )
   90   {
   91     // -------------------------------------------------
   92     case DpaEvent_Interrupt:
   93       // Do an extra quick background interrupt work
   94 
   95       return Carry;
   96 
   97       // -------------------------------------------------
   98     case DpaEvent_Idle:
   99       // Do a quick background work when RF packet is not received
  100 
  101       // Check binary output timers
  102     {
  103       // Pointer to the timers array
  104       FSR1 = (uns16)&Timers[0];
  105       // Output index
  106       uns8 index;
  107       index = 0;
  108       do
  109       {
  110         // Is timer running (is non-zero)?
  111         if ( ( FSR1[1] | INDF1 ) != 0 )
  112         {
  113           // Get timer value
  114           uns16 timer;
  115           timer.low8 = FSR1[0];
  116           timer.high8 = FSR1[1];
  117           // Get start time
  118           uns16 timerStart;
  119           timerStart.low8 = FSR1[2];
  120           timerStart.high8 = FSR1[3];
  121           // Measure elapsed time
  122           captureTicks(); // Note: must not modify FSR1
  123           param3 -= timerStart;
  124           // It time over?
  125           if ( param3 > timer )
  126           {
  127             // Set output to OFF
  128             SetOutput( 0, index );
  129             // Reset timer
  130             setINDF1( 0 );
  131             FSR1++;
  132             setINDF1( 0 );
  133             FSR1--;
  134           }
  135         }
  136         // Next timer
  137         FSR1 += 2 * sizeof( Timers[0] );
  138         // Next index
  139       } while ( ++index < OUTPUTS_COUNT );
  140     }
  141     break;
  142 
  143     // -------------------------------------------------
  144     case DpaEvent_Init:
  145       // Do a one time initialization before main loop starts
  146 
  147       // Initialize ticks
  148       startCapture();
  149 
  150       // Initialize relays @ DDC-RE
  151       RELAY1_LAT = 0;
  152       RELAY2_LAT = 0;
  153       RELAY1_TRIS = 0;
  154       RELAY2_TRIS = 0;
  155 
  156       break;
  157 
  158       // -------------------------------------------------
  159     case DpaEvent_DpaRequest:
  160       // Called to interpret DPA request for peripherals
  161       // -------------------------------------------------
  162       // Peripheral enumeration
  163       IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest()
  164       {
  165         // We implement 2 standard peripherals
  166         _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
  167         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_BINARY_OUTPUTS );
  168         _DpaMessage.EnumPeripheralsAnswer.HWPID = HWPID_IQRF_TECH__DEMO_DDC_RE01_LP;
  169         _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= 0x0000;
  170 
  171 DpaHandleReturnTRUE:
  172         return TRUE;
  173       }
  174       // -------------------------------------------------
  175       // Get information about peripherals
  176 else
  177       {
  178       switch ( _DpaMessage.PeripheralInfoAnswer.PerT = _PNUM )
  179       {
  180         case PNUM_STD_BINARY_OUTPUTS:
  181           // Set standard version
  182           _DpaMessage.PeripheralInfoAnswer.Par1 = STD_BINARY_OUTPUTS_VERSION;
  183           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  184           goto DpaHandleReturnTRUE;
  185       }
  186 
  187       break;
  188       }
  189 
  190       {
  191       // -------------------------------------------------
  192       // Handle peripheral command
  193 
  194       // Supported peripheral number?
  195       switch ( _PNUM )
  196       {
  197         case PNUM_STD_BINARY_OUTPUTS:
  198         {
  199           // Supported commands?
  200           switch ( _PCMD )
  201           {
  202             // Invalid command
  203             default:
  204               // Return error
  205 _ERROR_PCMD:
  206               W = ERROR_PCMD;
  207               goto _ERROR_W;
  208 
  209               // Outputs enumeration
  210             case PCMD_STD_ENUMERATE:
  211               if ( _DpaDataLength != 0 )
  212                 goto _ERROR_DATA_LEN;
  213 
  214               // Return number of outputs
  215               _DpaMessageIqrfStd.PerStdBinaryOutputEnumerate_Response.Count = OUTPUTS_COUNT;
  216               W = sizeof( _DpaMessageIqrfStd.PerStdBinaryOutputEnumerate_Response );
  217 _W2_DpaDataLength:
  218               _DpaDataLength = W;
  219               goto DpaHandleReturnTRUE;
  220 
  221               // Supported commands.
  222             case PCMD_STD_BINARY_OUTPUTS_SET:
  223             {
  224               // Pointers FSR01 to data are already set at the DPA
  225 
  226               // As this template implements < 9 outputs the working bitmap is uns8, if more outputs are implemented then uns16, ..., uns32 must be used
  227 #if OUTPUTS_COUNT < 9
  228               uns8 inBitmap = *FSR0--;
  229               uns8 outBitmap @ _DpaMessageIqrfStd.PerStdBinaryOutputSet_Request.Bitmap[0];
  230               uns8 bitmapMask = 0b1;
  231 #else
  232 #error Not implemented
  233 #endif
  234 
  235               // Number of selected outputs + bitmap length
  236               uns8 outputsCount = sizeof( _DpaMessageIqrfStd.PerStdBinaryOutputSet_Request.Bitmap );
  237               // Loop bitmap
  238               uns8 index = sizeof( _DpaMessageIqrfStd.PerStdBinaryOutputSet_Request.Bitmap );
  239               do
  240               {
  241                 // Count bits of next byte
  242                 uns8 byte = *++FSR0;
  243                 if ( byte != 0 )
  244                 {
  245                   // Brian Kernighan's Algorithm for counting set bits 
  246                   do
  247                   {
  248                     outputsCount++;
  249                     byte &= byte - 1;
  250                   } while ( byte != 0 );
  251                 }
  252 
  253                 // Reset bitmap
  254                 setINDF0( 0 );
  255               } while ( --index != 0 );
  256 
  257               // Check data length
  258               if ( _DpaDataLength != outputsCount )
  259               {
  260 _ERROR_DATA_LEN:
  261                 W = ERROR_DATA_LEN;
  262 _ERROR_W:
  263                 DpaApiReturnPeripheralError( W );
  264               }
  265 
  266               // Pointer to the timers array
  267               FSR1 = (uns16)&Timers[0];
  268               // Output index
  269               index = 0;
  270               do
  271               {
  272                 // Output was set?
  273                 if ( GetOutput( index ) )
  274                   // Yes, set in the output bitmap
  275                   outBitmap |= bitmapMask;
  276 
  277                 // Implemented output selected? Set the state.
  278                 if ( inBitmap.0 )
  279                 {
  280                   // Default is timer off
  281                   uns16 time = 0;
  282                   // Desired state
  283                   uns8 state = *++FSR0;
  284                   if ( state > 1 )
  285                   {
  286                     // Get time in units s/min
  287                     time = state & 0x7F;
  288                     if ( time == 0 )
  289                     {
  290                       // Invalid time
  291                       W = ERROR_FAIL;
  292 _ERROR_FAIL:
  293                       goto _ERROR_W;
  294                     }
  295 
  296                     // Conversion coefficient, ready for seconds
  297                     uns16 coef = 1000 / TICKS_LEN;
  298                     if ( !state.7 )
  299                     {
  300                       // Check for the maximum supported time because of captureTicks method
  301                       if ( time.low8 > ( (uns24)0xFFFF * TICKS_LEN / 1000 / 60 ) )
  302                         goto _ERROR_FAIL;
  303 
  304                       // Convert from minutes
  305                       uns16 coef = 60 * ( 1000 / TICKS_LEN );
  306                     }
  307 
  308                     // Convert to 250 ms
  309                     time *= coef;
  310                     // Set ON
  311                     state = 1;
  312                   }
  313 
  314                   // Set output
  315                   SetOutput( state, index );
  316 
  317                   // Set timer but preserve pointer
  318                   setINDF1( time.low8 );
  319                   FSR1++;
  320                   setINDF1( time.high8 );
  321                   FSR1++;
  322                   // Get start time
  323                   captureTicks(); //Note: must not destroy FSR1
  324                   setINDF1( param3.low8 );
  325                   FSR1++;
  326                   setINDF1( param3.high8 );
  327                   FSR1 -= 3;
  328                 }
  329 
  330                 // Pointer to the next timer
  331                 FSR1 += 2 * sizeof( Timers[0] );
  332                 // Next bits
  333                 bitmapMask <<= 1;
  334                 inBitmap >>= 1;
  335                 // Next index
  336               } while ( ++index < OUTPUTS_COUNT );
  337 
  338               // Return bitmap
  339 _DpaDataLength4:
  340               W = sizeof( _DpaMessageIqrfStd.PerStdBinaryOutputSet_Response.PreviousStates );
  341               goto _W2_DpaDataLength;
  342             }
  343           }
  344         }
  345       }
  346 
  347       break;
  348       }
  349   }
  350 DpaHandleReturnFALSE:
  351   return FALSE;
  352 }
  353 
  354 //############################################################################################
  355 void SetOutput( uns8 state, uns8 index @ W )
  356 //############################################################################################
  357 {
  358   // Note: FSRs must not be modified
  359   // Note: This method is called in the interrupt too!
  360 
  361   skip( index );
  362 #pragma computedGoto 1
  363   goto set0;
  364   goto set1;
  365 #pragma computedGoto 0
  366   ;
  367   // --------------------------------------
  368 set1:
  369   if ( !state.0 )
  370     RELAY2_LAT = 0;
  371   else
  372     RELAY2_LAT = 1;
  373 
  374   return;
  375   // --------------------------------------
  376 set0:
  377   if ( !state.0 )
  378     RELAY1_LAT = 0;
  379   else
  380     RELAY1_LAT = 1;
  381 
  382   return;
  383   // --------------------------------------
  384 }
  385 
  386 //############################################################################################
  387 bit GetOutput( uns8 index @ W )
  388 //############################################################################################
  389 {
  390   Carry = FALSE; // Note: must not modify W
  391 
  392   // Note: all below must not modify Carry except when needed
  393   skip( index );
  394 #pragma computedGoto 1
  395   goto get0;
  396   goto get1;
  397 #pragma computedGoto 0
  398   ;
  399   // --------------------------------------
  400 get1:
  401   if ( RELAY2_LAT )
  402     Carry = TRUE;
  403   goto _return;
  404   // --------------------------------------
  405 get0:
  406   if ( RELAY1_LAT )
  407     Carry = TRUE;
  408   goto _return;
  409   // --------------------------------------
  410 
  411 _return:
  412   return Carry;
  413 }
  414 
  415 //############################################################################################
  416 // 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) 
  417 #include "DPAcustomHandler.h"
  418 //############################################################################################