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