1 // ****************************************************************************
    2 //   Custom DPA Handler code example - Standard Binary Outputs - Template     *
    3 // ****************************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: 0C02_BinaryOutput-Template.c,v $
    7 // Version: $Revision: 1.15 $
    8 // Date:    $Date: 2019/04/03 09:27:58 $
    9 //
   10 // Revision history:
   11 //   2017/11/16  Release for DPA 3.02
   12 //   2017/08/14  Release for DPA 3.01
   13 //
   14 // *********************************************************************
   15 
   16 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/
   17 // IQRF Standards documentation https://www.iqrfalliance.org/techDocs/
   18 
   19 // This example implements 4 binary outputs according to the IQRF Binary Outputs standard
   20 // Index 0: Red LED
   21 // Index 1: Green LED
   22 // Index 2: Relay #1 @ DDC-RE-01
   23 // Index 3: Relay #2 @ DDC-RE-01
   24 
   25 // Default IQRF include (modify the path according to your setup)
   26 #include "IQRF.h"
   27 
   28 // Default DPA header (modify the path according to your setup)
   29 #include "DPA.h"
   30 // Default Custom DPA Handler header (modify the path according to your setup)
   31 #include "DPAcustomHandler.h"
   32 // IQRF standards header (modify the path according to your setup)
   33 #include "IQRFstandard.h"
   34 #include "IQRF_HWPID.h"
   35 
   36 #if DPA_VERSION_MASTER  < 0x0301
   37 #error DPA version 3.01++ is required
   38 #endif
   39 
   40 //############################################################################################
   41 
   42 // Number of implemented binary outputs
   43 #define OUTPUTS_COUNT 4
   44 
   45 // Sets and Gets state of the indexed binary output
   46 void SetOutput( uns8 state, uns8 index );
   47 bit GetOutput( uns8 index );
   48 
   49 // DDC-RE-01 relay pins
   50 //  C.5 = C8 = Relay#1
   51 #define RELAY1_LAT  LATC.5 
   52 #define RELAY1_TRIS TRISC.5
   53 //  C.2 = C2 = Relay#2
   54 #define RELAY2_LAT  LATC.2 
   55 #define RELAY2_TRIS TRISC.2
   56 
   57 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   58 //############################################################################################
   59 bit CustomDpaHandler()
   60 //############################################################################################
   61 {
   62   // 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)
   63 #pragma updateBank default = UserBank_01
   64 
   65   // Timers for outputs. The space must be long enough to fit them all. 2 bytes per one binary output.
   66   static uns16  Timers[OUTPUTS_COUNT];
   67 
   68   // Handler presence mark
   69   clrwdt();
   70 
   71   // Detect DPA event to handle
   72   switch ( GetDpaEvent() )
   73   {
   74     // -------------------------------------------------
   75     case DpaEvent_Interrupt:
   76       // Do an extra quick background interrupt work
   77       // ! 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.
   78       // ! 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.
   79       // ! 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.
   80       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   81       // ! Make sure race condition does not occur when accessing those variables at other places.
   82       // ! 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.
   83       // ! Do not call any OS functions except setINDFx().
   84       // ! Do not use any OS variables especially for writing access.
   85       // ! 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.
   86 
   87       //  If TMR6 interrupt occurred, every 10 ms
   88       if ( TMR6IF )
   89       {
   90         // Unmask interrupt
   91         TMR6IF = 0;
   92 
   93         // Count 250 ms from 10 ms micro ticks
   94         static uns8 count250ms;
   95         if ( ++count250ms == ( 250 / 10 ) )
   96         {
   97           // 250 ms
   98           count250ms = 0;
   99 
  100           // Pointer to the timers array
  101           FSR1 = (uns16)&Timers[0];
  102           // Output index
  103           static uns8 index;
  104           index = 0;
  105           do
  106           {
  107             // Is timer running (is non-zero)?
  108             if ( ( FSR1[1] | INDF1 ) != 0 )
  109             {
  110               // Get time
  111               static uns16 time;
  112               time.low8 = *FSR1++;
  113               time.high8 = *FSR1;
  114               // Is timer over?
  115               if ( --time == 0 )
  116                 // Set output to OFF
  117                 SetOutput( 0, index );
  118 
  119               // Store new time
  120               setINDF1( time.high8 );
  121               FSR1--;
  122               setINDF1( time.low8 );
  123             }
  124             // Next timer
  125             FSR1 += sizeof( Timers[0] );
  126             // Next index
  127           } while ( ++index < OUTPUTS_COUNT );
  128         }
  129       }
  130       return Carry;
  131 
  132       // -------------------------------------------------
  133     case DpaEvent_DpaRequest:
  134       // Called to interpret DPA request for peripherals
  135       // -------------------------------------------------
  136       // Peripheral enumeration
  137       if ( IsDpaEnumPeripheralsRequest() )
  138       {
  139         // We implement 1 standard peripheral
  140         _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
  141         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_BINARY_OUTPUTS );
  142         _DpaMessage.EnumPeripheralsAnswer.HWPID = HWPID_IQRF_TECH__DEMO_BINARY_OUTPUT;
  143         _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x0000;
  144 
  145 DpaHandleReturnTRUE:
  146         return TRUE;
  147       }
  148       // -------------------------------------------------
  149       // Get information about peripheral
  150       else if ( IsDpaPeripheralInfoRequest() )
  151       {
  152         if ( _PNUM == PNUM_STD_BINARY_OUTPUTS )
  153         {
  154           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_STD_BINARY_OUTPUTS;
  155           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  156           // Set standard version
  157           _DpaMessage.PeripheralInfoAnswer.Par1 = 4;
  158           goto DpaHandleReturnTRUE;
  159         }
  160 
  161         break;
  162       }
  163       // -------------------------------------------------
  164       else
  165       {
  166         // Handle peripheral command
  167 
  168         // Supported peripheral number?
  169         if ( _PNUM == PNUM_STD_BINARY_OUTPUTS )
  170         {
  171           // Supported commands?
  172           switch ( _PCMD )
  173           {
  174             // Invalid command
  175             default:
  176             {
  177               // Return error
  178               W = ERROR_PCMD;
  179 ERROR_W:
  180               DpaApiReturnPeripheralError( W );
  181             }
  182 
  183             // Outputs enumeration
  184             case PCMD_STD_ENUMERATE:
  185               if ( _DpaDataLength != 0 )
  186                 goto _ERROR_DATA_LEN;
  187 
  188               // Return number of outputs
  189               _DpaMessage.Response.PData[0] = OUTPUTS_COUNT;
  190               W = 1;
  191               goto _DpaDataLengthW;
  192 
  193               // Supported commands.
  194             case PCMD_STD_BINARY_OUTPUTS_SET:
  195             {
  196               // Pointers FSR01 to data are already set at the DPA
  197 
  198               // As this template implements < 9 outputs the working bitmap is uns8, if more outputs are implemented then uns16, ..., uns32 must be used
  199 #if OUTPUTS_COUNT < 9
  200               uns8 inBitmap = *FSR0--;
  201               uns8 outBitmap @ _DpaMessage.Response.PData[0];
  202               uns8 bitmapMask = 0b1;
  203 #else
  204 #error Not implemented
  205 #endif
  206 
  207               // Number of selected outputs + bitmap length
  208               uns8 outputsCount = 4;
  209               // Loop bitmap
  210               uns8 index = 4;
  211               do
  212               {
  213                 // Count bits of next byte
  214                 uns8 byte = *++FSR0;
  215                 if ( byte != 0 )
  216                 {
  217                   // Brian Kernighan's Algorithm for counting set bits 
  218                   do
  219                   {
  220                     outputsCount++;
  221                     byte &= byte - 1;
  222                   } while ( byte != 0 );
  223                 }
  224 
  225                 // Reset bitmap
  226                 setINDF0( 0 );
  227               } while ( --index != 0 );
  228 
  229               // Check data length
  230               if ( _DpaDataLength != outputsCount )
  231               {
  232 _ERROR_DATA_LEN:
  233                 W = ERROR_DATA_LEN;
  234                 goto ERROR_W;
  235               }
  236 
  237               // Pointer to the timers array
  238               FSR1 = (uns16)&Timers[0];
  239               // Output index
  240               index = 0;
  241               do
  242               {
  243                 // Output was set?
  244                 if ( GetOutput( index ) )
  245                   // Yes, set in the output bitmap
  246                   outBitmap |= bitmapMask;
  247 
  248                 // Implemented output selected? Set the state.
  249                 if ( inBitmap.0 )
  250                 {
  251                   // Default is timer off
  252                   uns16 time = 0;
  253                   // Desired state
  254                   uns8 state = *++FSR0;
  255                   if ( state > 1 )
  256                   {
  257                     // Get time in units s/min
  258                     time = state & 0x7F;
  259                     if ( time == 0 )
  260                     {
  261                       // Invalid time
  262                       W = ERROR_FAIL;
  263                       goto ERROR_W;
  264                     }
  265 
  266                     // Conversion coefficient, ready for minutes to 250 ms
  267                     uns8 coef = 60 * ( 1000 / 250 );
  268                     if ( state.7 )
  269                       // Convert from seconds
  270                       coef = 1000 / 250;
  271 
  272                     // Convert to 250 ms
  273                     time *= coef;
  274                     // Set ON
  275                     state = 1;
  276                   }
  277 
  278                   // Set output
  279                   SetOutput( state, index );
  280 
  281                   // Set timer and preserve pointer
  282                   GIE = FALSE;
  283                   setINDF1( time.low8 );
  284                   FSR1++;
  285                   setINDF1( time.high8 );
  286                   FSR1--;
  287                   GIE = TRUE;
  288                 }
  289 
  290                 // Next pointer to the timer
  291                 FSR1 += sizeof( Timers[0] );
  292                 // Next bits
  293                 bitmapMask <<= 1;
  294                 inBitmap >>= 1;
  295                 // Next index
  296               } while ( ++index < OUTPUTS_COUNT );
  297 
  298               // Return bitmap
  299 _DpaDataLength4:
  300               W = 4;
  301 _DpaDataLengthW:
  302               _DpaDataLength = W;
  303               goto DpaHandleReturnTRUE;
  304             }
  305           }
  306         }
  307 
  308         break;
  309       }
  310 
  311       // -------------------------------------------------
  312     case DpaEvent_Init:
  313       // Do a one time initialization work before main loop starts
  314 
  315       // Initialize relays @ DDC-RE
  316       RELAY1_LAT = 0;
  317       RELAY2_LAT = 0;
  318       RELAY1_TRIS = 0;
  319       RELAY2_TRIS = 0;
  320 
  321       // Setup TMR6 to generate 10 ms ticks
  322 #if F_OSC == 16000000
  323       PR6 = 250 - 1;
  324       T6CON = 0b0.1001.1.10;    // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms
  325 #else
  326 #error Unsupported oscillator frequency
  327 #endif
  328 
  329       break;
  330 
  331       // -------------------------------------------------
  332     case DpaEvent_AfterSleep:
  333       // Called after woken up after sleep
  334 
  335       TMR6IE = TRUE;
  336       TMR6ON = TRUE;
  337       break;
  338 
  339       // -------------------------------------------------
  340     case DpaEvent_BeforeSleep:
  341       // Called before going to sleep
  342 
  343       // -------------------------------------------------
  344     case DpaEvent_DisableInterrupts:
  345       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM)
  346 
  347       // Must not use TMR6 any more
  348       TMR6ON = FALSE;
  349       TMR6IE = FALSE;
  350       break;
  351   }
  352 DpaHandleReturnFALSE:
  353   return FALSE;
  354 }
  355 
  356 //############################################################################################
  357 static uns8 _state;
  358 void SetOutput( uns8 state @ _state, uns8 index @ W )
  359 //############################################################################################
  360 {
  361   // Note: FSRs must not be modified
  362   // Note: This method is called in the interrupt too!
  363 
  364   skip( index );
  365 #pragma computedGoto 1
  366   goto set0;
  367   goto set1;
  368   goto set2;
  369   goto set3;
  370 #pragma computedGoto 0
  371   ;
  372   // --------------------------------------
  373 set3:
  374   if ( !state.0 )
  375     RELAY2_LAT = 0;
  376   else
  377     RELAY2_LAT = 1;
  378 
  379   return;
  380   // --------------------------------------
  381 set2:
  382   if ( !state.0 )
  383     RELAY1_LAT = 0;
  384   else
  385     RELAY1_LAT = 1;
  386 
  387   return;
  388   // --------------------------------------
  389 set1:
  390   if ( !state.0 )
  391     _LEDG = 0;
  392   else
  393     _LEDG = 1;
  394 
  395   return;
  396   // --------------------------------------
  397 set0:
  398   if ( !state.0 )
  399     _LEDR = 0;
  400   else
  401     _LEDR = 1;
  402 
  403   return;
  404 }
  405 
  406 //############################################################################################
  407 bit GetOutput( uns8 index @ W )
  408 //############################################################################################
  409 {
  410   Carry = FALSE; // Note: must not modify W
  411 
  412   // Note: all below must not modify Carry except when needed
  413   skip( index );
  414 #pragma computedGoto 1
  415   goto get0;
  416   goto get1;
  417   goto get2;
  418   goto get3;
  419 #pragma computedGoto 0
  420   ;
  421   // --------------------------------------
  422 get3:
  423   if ( RELAY2_LAT )
  424     Carry = TRUE;
  425   goto _return;
  426   // --------------------------------------
  427 get2:
  428   if ( RELAY1_LAT )
  429     Carry = TRUE;
  430   goto _return;
  431   // --------------------------------------
  432 get1:
  433   if ( _LEDG )
  434     Carry = TRUE;
  435   goto _return;
  436   // --------------------------------------
  437 get0:
  438   if ( _LEDR )
  439     Carry = TRUE;
  440   goto _return;
  441   // --------------------------------------
  442 
  443 _return:
  444   return Carry;
  445 }
  446 
  447 //############################################################################################
  448 // 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) 
  449 #include "DPAcustomHandler.h"
  450 //############################################################################################