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