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