1 // ****************************************************************************
    2 //   Custom DPA Handler code example - Standard Lights - Template             *
    3 // ****************************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: 1002_Light-Template.c,v $
    7 // Version: $Revision: 1.18 $
    8 // Date:    $Date: 2019/04/03 09:27:58 $
    9 //
   10 // Revision history:
   11 //   2018/10/25  Release for DPA 3.03
   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 http://www.iqrf.org/DpaTechGuide/
   18 // IQRF Standards documentation https://www.iqrfalliance.org/techDocs/
   19 
   20 // This example implements 2 Lights according to the IQRF Lights standard
   21 //  Index 0: Red LED using PIC ADC, 31 ON levels
   22 //  Index 1: Green LED, only on/off, i.e. 2 levels (0 or 100 %)
   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 lights
   42 #define LIGHTS_COUNT 2
   43 
   44 // Light power levels array: every light has 2 bytes, 1st byte is actual power, 2nd byte is requested power
   45 uns16   Powers[LIGHTS_COUNT];
   46 
   47 // Sets power of the light, returns previous requested power and returns previous actual power at the global PrevActualPower variable
   48 uns8 SetLight( uns8 index, uns8 reqPower );
   49 static uns8 PrevActualPower;
   50 // Sets FSR0 to point to the 2 bytes of the light powers
   51 void FSR0PowerPointer( uns8 index );
   52 
   53 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   54 //############################################################################################
   55 bit CustomDpaHandler()
   56 //############################################################################################
   57 {
   58   // 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)
   59 #pragma updateBank default = UserBank_01
   60 
   61   // Timers for lights. The space must be long enough to fit them all. 2 bytes per one light.
   62   static uns16  Timers[LIGHTS_COUNT];
   63 
   64   // Handler presence mark
   65   clrwdt();
   66 
   67   // Detect DPA event to handle
   68   switch ( GetDpaEvent() )
   69   {
   70     // -------------------------------------------------
   71     case DpaEvent_Interrupt:
   72       // Do an extra quick background interrupt work
   73       // ! 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.
   74       // ! 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.
   75       // ! 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.
   76       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   77       // ! Make sure race condition does not occur when accessing those variables at other places.
   78       // ! 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.
   79       // ! Do not call any OS functions except setINDFx().
   80       // ! Do not use any OS variables especially for writing access.
   81       // ! 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.
   82 
   83       //  If TMR6 interrupt occurred, every 10 ms
   84       if ( TMR6IF )
   85       {
   86         // Unmask interrupt
   87         TMR6IF = 0;
   88 
   89         // Count 250 ms from 10 ms micro ticks
   90         static uns8 count250ms;
   91         if ( ++count250ms == ( 250 / 10 ) )
   92         {
   93           // 250 ms
   94           count250ms = 0;
   95 
   96           // Pointer to the timers array
   97           FSR1 = (uns16)&Timers[0];
   98           // Light index
   99           static uns8 index;
  100           index = 0;
  101           do
  102           {
  103             // Is timer running (is non-zero)?
  104             if ( ( FSR1[1] | INDF1 ) != 0 )
  105             {
  106               // Get time
  107               static uns16 time;
  108               time.low8 = *FSR1++;
  109               time.high8 = *FSR1;
  110               // Is timer over?
  111               if ( --time == 0 )
  112                 // Turn the light OFF
  113                 SetLight( index, 0 );
  114 
  115               // Store new time
  116               setINDF1( time.high8 );
  117               FSR1--;
  118               setINDF1( time.low8 );
  119             }
  120             // Next timer
  121             FSR1 += sizeof( Timers[0] );
  122             // Next index
  123           } while ( ++index < LIGHTS_COUNT );
  124         }
  125       }
  126       return Carry;
  127 
  128       // -------------------------------------------------
  129     case DpaEvent_DpaRequest:
  130       // Called to interpret DPA request for peripherals
  131       // -------------------------------------------------
  132       // Peripheral enumeration
  133       IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest()
  134       {
  135         // We implement 1 standard peripheral
  136         _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
  137         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_LIGHT );
  138         _DpaMessage.EnumPeripheralsAnswer.HWPID = HWPID_IQRF_TECH__DEMO_LIGHT;
  139         _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x0001;
  140 
  141 DpaHandleReturnTRUE:
  142         return TRUE;
  143       }
  144       // -------------------------------------------------
  145       // Get information about peripheral
  146       else
  147       {
  148         if ( _PNUM == PNUM_STD_LIGHT )
  149         {
  150           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_STD_LIGHT;
  151           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  152           // Set standard version
  153           _DpaMessage.PeripheralInfoAnswer.Par1 = 5;
  154           goto DpaHandleReturnTRUE;
  155         }
  156 
  157         break;
  158       }
  159       // -------------------------------------------------
  160       {
  161         // Handle peripheral command
  162 
  163         // Supported peripheral number?
  164         if ( _PNUM == PNUM_STD_LIGHT )
  165         {
  166           // Supported commands?
  167           switch ( _PCMD )
  168           {
  169             // Invalid command
  170             default:
  171               // Return error
  172               DpaApiReturnPeripheralError( ERROR_PCMD );
  173 
  174               // Light enumeration
  175             case PCMD_STD_ENUMERATE:
  176               if ( _DpaDataLength != 0 )
  177                 goto _ERROR_DATA_LEN;
  178 
  179               // Return just count of lights
  180               _DpaDataLength |= 1;  // Optimization = 1 (_DpaDataLength was zero for sure)
  181               _DpaMessage.Request.PData[0] = LIGHTS_COUNT;
  182               goto DpaHandleReturnTRUE;
  183 
  184               // Supported commands.
  185             case PCMD_STD_LIGHT_SET:
  186             case PCMD_STD_LIGHT_INC:
  187             case PCMD_STD_LIGHT_DEC:
  188             {
  189               // Invalid bitmap (data) length
  190               if ( _DpaDataLength < 4 )
  191               {
  192                 // Return error
  193 _ERROR_DATA_LEN:
  194                 DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  195               }
  196 
  197               // Store bitmap of lights
  198               uns8  lightsBitmap[4];
  199               // Copy bitmap
  200               lightsBitmap[0] = *FSR0++;
  201               lightsBitmap[1] = *FSR0++;
  202               lightsBitmap[2] = *FSR0++;
  203               lightsBitmap[3] = *FSR0++;
  204 
  205               // Remaining data counter + 1
  206               _DpaDataLength -= 3;
  207               // Light index
  208               uns8 index = 0;
  209               // Loop all optionally selected lights
  210               do
  211               {
  212                 // Light is selected?
  213                 if ( lightsBitmap[0].0 )
  214                 {
  215                   // Is there enough data for the light?
  216                   if ( --_DpaDataLength == 0 )
  217                     goto _ERROR_DATA_LEN;
  218 
  219                   // Get required power value
  220                   uns8 reqPower = *FSR0++;
  221                   // NULL required timer value
  222                   uns16 time = 0;
  223                   // Is there a timer value?
  224                   if ( reqPower.7 )
  225                   {
  226                     // Un-flag timer value
  227                     reqPower.7 = 0;
  228                     // Is there enough data for a timer value?
  229                     if ( --_DpaDataLength == 0 )
  230                       goto _ERROR_DATA_LEN;
  231 
  232                     // Get required time
  233                     time.low8 = *FSR0++;
  234                     // Valid timer value?
  235                     if ( ( time.low8 & 0x7F ) == 0 )
  236                     {
  237 _ERROR_DATA:
  238                       DpaApiReturnPeripheralError( ERROR_DATA );
  239                     }
  240 
  241                     // Conversion coefficient, ready for minutes to 250 ms
  242                     uns8 coef = 60 * ( 1000 / 250 );
  243                     // Get time in units s/min
  244                     if ( time.7 )
  245                     {
  246                       // Seconds
  247                       time.7 = 0;
  248                       // Convert from seconds
  249                       coef = 1000 / 250;
  250                     }
  251 
  252                     // Convert to 250 ms
  253                     time *= coef;
  254                   }
  255 
  256                   // Is the power parameter correct?
  257                   if ( reqPower > 100 && reqPower < 0x7F )
  258                     goto _ERROR_DATA;
  259 
  260                   // Process only implemented lights
  261                   if ( index < LIGHTS_COUNT )
  262                   {
  263                     // Increment or decrement?
  264                     if ( _PCMD != PCMD_STD_LIGHT_SET )
  265                     {
  266                       // Note: we do not report power if parameter for increment or decrement is 0x7F
  267                       // Get current power
  268                       uns8 curPower = SetLight( index, 0x7F );
  269                       // Increment?
  270                       if ( _PCMD == PCMD_STD_LIGHT_INC )
  271                       {
  272                         // Do increment and check for maximum
  273                         reqPower += curPower;
  274                         if ( reqPower > 100 )
  275                           reqPower = 100;
  276                       }
  277                       else
  278                       { // case PCMD_STD_LIGHT_DEC
  279                         // Do decrement and check for minimum
  280                         reqPower = curPower - reqPower;
  281                         if ( (int8)reqPower < 0 )
  282                           reqPower = 0;
  283                       }
  284                     }
  285 
  286                     // Disable timer, so there will be no background activity with the light
  287                     TMR6ON = FALSE;
  288                     // Is there a requirement for setting the power level? Then store the timer.
  289                     if ( reqPower != 0x7f )
  290                     {
  291                       // Write timer to the timer database
  292                       uns16 saveFSR0 = FSR0;
  293                       FSR0 = index * sizeof( uns16 );
  294                       FSR0 += (uns16)&Timers[0];
  295                       setINDF0( time.low8 );
  296                       FSR0++;
  297                       setINDF0( time.high8 );
  298                       FSR0 = saveFSR0;
  299                     }
  300 
  301                     // Set power and get previous actual power level
  302                     SetLight( index, reqPower );
  303                     W = PrevActualPower;
  304                     // Enable timer again
  305                     TMR6ON = TRUE;
  306                   }
  307                   else
  308                     // Selected light is not implemented, return 0 power
  309                     W = 0;
  310 
  311                   // Store previous power to the response
  312                   setINDF1( W );
  313                   // Move to the next response byte
  314                   FSR1++;
  315                 }
  316                 // Shift bitmap
  317                 lightsBitmap[3] = rr( lightsBitmap[3] );
  318                 lightsBitmap[2] = rr( lightsBitmap[2] );
  319                 lightsBitmap[1] = rr( lightsBitmap[1] );
  320                 lightsBitmap[0] = rr( lightsBitmap[0] );
  321                 // Next light index
  322               } while ( ++index < 32 );
  323 
  324               // Too much data?
  325               if ( --_DpaDataLength != 0 )
  326                 goto _ERROR_DATA_LEN;
  327 
  328               // Return data
  329               _DpaDataLength = FSR1L - ( (uns16)&_DpaMessage.Response.PData[0] & 0xFF );
  330               goto DpaHandleReturnTRUE;
  331             }
  332           }
  333         }
  334 
  335         break;
  336       }
  337 
  338       // -------------------------------------------------
  339     case DpaEvent_Init:
  340       // Do a one time initialization work before main loop starts
  341 
  342   // Setup TMR6 to generate 10 ms ticks
  343 #if F_OSC == 16000000
  344       PR6 = 250 - 1;
  345       T6CON = 0b0.1001.1.10;    // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms
  346 #else
  347 #error Unsupported oscillator frequency
  348 #endif
  349 
  350       break;
  351 
  352       // -------------------------------------------------
  353     case DpaEvent_AfterSleep:
  354       // Called after woken up after sleep
  355 
  356       TMR6IE = TRUE;
  357       TMR6ON = TRUE;
  358       break;
  359 
  360       // -------------------------------------------------
  361     case DpaEvent_BeforeSleep:
  362       // Called before going to sleep
  363 
  364       // -------------------------------------------------
  365     case DpaEvent_DisableInterrupts:
  366       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM)
  367 
  368       // Must not use TMR6 any more
  369       TMR6ON = FALSE;
  370       TMR6IE = FALSE;
  371       break;
  372 
  373     case DpaEvent_FrcValue:
  374       // Called to get FRC value
  375 
  376       // Check for correct FRC user data
  377       if ( DataOutBeforeResponseFRC[0] == PNUM_STD_LIGHT )
  378       {
  379         // Check for the correct light index and prepare pointer to the power array
  380         uns8 index = DataOutBeforeResponseFRC[1] & 0x1F;
  381         if ( index < LIGHTS_COUNT )
  382         {
  383           FSR0PowerPointer( index );
  384           // Check for FRC command
  385           switch ( _PCMD )
  386           {
  387             case FRC_STD_LIGHT_ONOFF:
  388               responseFRCvalue.1 = 1;
  389               // Is the light off?
  390               if ( FSR0[0] == 0 )
  391                 responseFRCvalue.0 = 0;
  392               break;
  393 
  394             case FRC_STD_LIGHT_ALARM:
  395               // !EXAMPLE! in this example return alarm if the light power is 100 %
  396               responseFRCvalue.1 = 1;
  397               if ( FSR0[0] != 100 )
  398                 responseFRCvalue.0 = 0;
  399               break;
  400           }
  401         }
  402       }
  403       goto DpaHandleReturnFALSE;
  404 
  405       // -------------------------------------------------
  406     case DpaEvent_FrcResponseTime:
  407       // Called to get FRC response time
  408 
  409       // In this example the FRC commands are fast 
  410       switch ( DataOutBeforeResponseFRC[0] )
  411       {
  412         case FRC_STD_LIGHT_ONOFF:
  413         case FRC_STD_LIGHT_ALARM:
  414           responseFRCvalue = _FRC_RESPONSE_TIME_40_MS;
  415           break;
  416       }
  417       break;
  418 
  419   }
  420 DpaHandleReturnFALSE:
  421   return FALSE;
  422 }
  423 
  424 //############################################################################################
  425 // Hand written multiplication using static variables because CC5X uses hidden "automatic" variables so it cannot be called from the interrupt
  426 static uns16 multiplier16;
  427 static uns8 multiplicand8;
  428 static uns16 mul16x8result;
  429 void mul16x8()
  430 //############################################################################################
  431 {
  432   mul16x8result = 0;
  433   static uns8 loop;
  434   loop = 8;
  435   do
  436   {
  437     if ( multiplicand8.0 )
  438       mul16x8result += multiplier16;
  439     multiplier16 <<= 1;
  440     multiplicand8 >>= 1;
  441   } while ( --loop != 0 );
  442 }
  443 
  444 
  445 //############################################################################################
  446 void FSR0PowerPointer( uns8 index @ W )
  447 //############################################################################################
  448 {
  449   // Prepare powers pointer
  450   FSR0 = index * sizeof( uns16 );
  451   FSR0 += (uns16)&Powers[0];
  452 }
  453 
  454 //############################################################################################
  455 // Note: This method is called in the interrupt too, so all variables and parameters must be static, also in called functions
  456 // IMPORTANT: !beware of the hidden variables generated by compiler e.g. in case of multiplication generated by CC5X
  457 uns8 static _index, _reqPower;
  458 uns8 SetLight( uns8 index @ _index, uns8 reqPower @ _reqPower )
  459 //############################################################################################
  460 {
  461   // Note: FSRs must not be modified
  462 
  463   // Save FRS0
  464   static uns16 saveFSR0;
  465   saveFSR0 = FSR0;
  466   // Prepare powers pointer
  467   FSR0PowerPointer( index );
  468   // Get previous actual power
  469   PrevActualPower = FSR0[0];
  470   static uns8 prevReqPower;
  471   // Get previous required power
  472   prevReqPower = FSR0[1];
  473   // If just asking for the power levels, return values, but do not set new power
  474   if ( reqPower == 0x7F )
  475     goto _returnNoSet;
  476 
  477   skip( index );
  478 #pragma computedGoto 1
  479   goto set0;
  480   goto set1;
  481 #pragma computedGoto 0
  482   ;
  483   // --------------------------------------
  484 set1: // Control LEDG by GPIO
  485   if ( reqPower != 0 )
  486   {
  487     // On @ 100%
  488     _LEDG = 1;
  489     W = 100;
  490     goto _return;
  491   }
  492   else
  493   {
  494     // Off @ 0 %
  495     _LEDG = 0;
  496     W = 0;
  497     goto _return;
  498   }
  499 
  500   // --------------------------------------
  501 set0: // Control LEDR using 5-bit PIC DAC
  502 
  503   // Initialize DAC: DAC on, output enabled, from VDD to VSS
  504   DACCON0 = 0b111.0.00.0.0;
  505 
  506   // 5-bit DAC has 31 ON levels, we have to spread it between 1-100 % 
  507   // 80 = round( 31 / 100 * 256 )
  508   multiplier16 = reqPower;
  509   multiplicand8 = 80;
  510   mul16x8();
  511   // Round it before / 256
  512   mul16x8result += 128;
  513   // Make sure that light is on always when non-zero power is requested
  514   if ( reqPower != 0 && mul16x8result.high8 == 0 )
  515     mul16x8result.8 = 1;
  516 
  517   // Set DAC and prepare the value for the back computation of the actual power
  518   DACCON1 = multiplicand8 = mul16x8result.high8;
  519 
  520   // Convert actually used 32 level DAC value back to real 0-100 %
  521   // 826 = 100 / 31 * 256
  522   multiplier16 = 826;
  523   mul16x8();
  524   // Round it before / 256
  525   mul16x8result += 128;
  526   W = mul16x8result.high8;
  527   // Return powers
  528   goto _return
  529     // --------------------------------------
  530     ;
  531 _return:
  532   // Store actual power
  533   setINDF0( W );
  534   // Store requested power
  535   FSR0 += 1;
  536   setINDF0( reqPower );
  537 
  538 _returnNoSet:
  539   FSR0 = saveFSR0;
  540   // Return required power
  541   return prevReqPower;
  542 }
  543 
  544 //############################################################################################
  545 // 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) 
  546 #include "DPAcustomHandler.h"
  547 //############################################################################################