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