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