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