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