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