1 // *********************************************************************
    2 //   Custom DPA Handler code example - Standard Sensors - Thermometer  *
    3 // *********************************************************************
    4 // Copyright (c) MICRORISC s.r.o.
    5 //
    6 // File:    $RCSfile: 0802_TrThermometer.c,v $
    7 // Version: $Revision: 1.36 $
    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 //   2018/10/25  Release for DPA 3.03
   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_TR_THERMOMETER
   23 #define _HWPIDver_  0x0001
   24 
   25 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   26 // IQRF Standards documentation https://doc.iqrf.org/
   27 
   28 // This example implements 1 temperature sensor according to the IQRF Sensors standard
   29 
   30 // Default IQRF include (modify the path according to your setup)
   31 #include "IQRF.h"
   32 
   33 // Uncomment to implement Custom DPA Handler for Coordinator
   34 //#define COORDINATOR_CUSTOM_HANDLER
   35 
   36 // Default DPA header (modify the path according to your setup)
   37 #include "DPA.h"
   38 // Default Custom DPA Handler header (modify the path according to your setup)
   39 #include "DPAcustomHandler.h"
   40 // IQRF standards header (modify the path according to your setup)
   41 #include "IQRFstandard.h"
   42 #include "IQRF_HWPID.h"
   43 
   44 //############################################################################################
   45 
   46 // Calibration value address in the EEPROM
   47 #define TEMP_CALIBRATION_ADDRESS  0
   48 
   49 // Initial calibration value is 0
   50 #pragma cdata[ __EESTART + TEMP_CALIBRATION_ADDRESS ] = 0, 0
   51 
   52 // Calibrates temperature at param3, that was read by getTemperature()
   53 void CalibrateParam3();
   54 
   55 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   56 //############################################################################################
   57 bit CustomDpaHandler()
   58 //############################################################################################
   59 {
   60   // 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)
   61 #pragma updateBank default = UserBank_01
   62 
   63   // Handler presence mark
   64   clrwdt();
   65 
   66   // Sleeping parameters, valid when Time != 0
   67   static TPerOSSleep_Request PerOSSleep_Request;
   68 
   69   // Detect DPA event to handle
   70   switch ( GetDpaEvent() )
   71   {
   72     // -------------------------------------------------
   73     case DpaEvent_Interrupt:
   74       // Do an extra quick background interrupt work
   75       // ! 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.
   76       // ! 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.
   77       // ! 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.
   78       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   79       // ! Make sure race condition does not occur when accessing those variables at other places.
   80       // ! 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.
   81       // ! Do not call any OS functions except setINDFx().
   82       // ! Do not use any OS variables especially for writing access.
   83       // ! 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.
   84 
   85       return Carry;
   86 
   87       // -------------------------------------------------
   88     case DpaEvent_Idle:
   89       // Do a quick background work when RF packet is not received
   90 
   91       // Should go to sleep?
   92       if ( PerOSSleep_Request.Time != 0 )
   93       {
   94         // Copy sleep parameters to the DPA request
   95         _DpaMessage.PerOSSleep_Request.Time = PerOSSleep_Request.Time;
   96         _DpaMessage.PerOSSleep_Request.Control = PerOSSleep_Request.Control;
   97         // Switch off sleeping time=flag
   98         PerOSSleep_Request.Time = 0;
   99         // Finalize OS Sleep DPA Request
  100         _DpaDataLength = sizeof( _DpaMessage.PerOSSleep_Request );
  101         _PNUM = PNUM_OS;
  102         _PCMD = CMD_OS_SLEEP;
  103         // Perform local DPA Request to go to sleep
  104         DpaApiLocalRequest();
  105       }
  106       break;
  107 
  108       // -------------------------------------------------
  109     case DpaEvent_DpaRequest:
  110       // Called to interpret DPA request for peripherals
  111       // -------------------------------------------------
  112       // Peripheral enumeration
  113       if ( IsDpaEnumPeripheralsRequest() )
  114       {
  115         // We implement 1 standard peripheral
  116         _DpaMessage.EnumPeripheralsAnswer.UserPerNr |=  1;
  117         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_SENSORS );
  118         _DpaMessage.EnumPeripheralsAnswer.HWPID |= _HWPID_;
  119         _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= _HWPIDver_;
  120 
  121 DpaHandleReturnTRUE:
  122         return TRUE;
  123       }
  124       // -------------------------------------------------
  125       // Get information about peripheral
  126       else if ( IsDpaPeripheralInfoRequest() )
  127       {
  128         if ( _PNUM == PNUM_STD_SENSORS )
  129         {
  130           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_STD_SENSORS;
  131           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ;
  132           // Set standard version
  133           _DpaMessage.PeripheralInfoAnswer.Par1 = STD_SENSORS_VERSION;
  134           goto DpaHandleReturnTRUE;
  135         }
  136 
  137         break;
  138       }
  139       // -------------------------------------------------
  140       else
  141       {
  142         // Handle peripheral command
  143 
  144         // Supported peripheral number?
  145         if ( _PNUM == PNUM_STD_SENSORS )
  146         {
  147           // Supported commands?
  148           switch ( _PCMD )
  149           {
  150             // Invalid command
  151             default:
  152               // Return error
  153               W = ERROR_PCMD;
  154               goto _ERROR_W;
  155 
  156               // Sensor enumeration
  157             case PCMD_STD_ENUMERATE:
  158               if ( _DpaDataLength != 0 )
  159               {
  160 _ERROR_DATA_LEN:
  161                 W = ERROR_DATA_LEN;
  162                 goto _ERROR_W;
  163               }
  164 
  165               _DpaDataLength |= 1; // = 1 (optimization as _DpaDataLength was 0 for sure)
  166               goto _Enumerate;
  167 
  168               // Supported commands. They are handled almost the same way
  169             case PCMD_STD_SENSORS_READ_VALUES:
  170             case PCMD_STD_SENSORS_READ_TYPES_AND_VALUES:
  171             {
  172               bit returnCalibration = FALSE;
  173 
  174               // No sensor bitmap specified? W = _DpaDataLength. Note: W is used to avoid MOVLB at next if
  175               W = _DpaDataLength;
  176               if ( W == 0 ) // Note: must not modify W
  177               {
  178                 // Bitmap is 32 bits long = 4 (Note: using here save MOVLB)
  179                 _DpaDataLength = W = sizeof( _DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap );
  180                 // Simulate 1st sensor in the bitmap (states of the other unimplemented sensors do not care)
  181                 _DpaMessage.Request.PData[0].0 = 1; // Note: must not modify W
  182               }
  183 
  184               // Invalid bitmap (data) length (W = _DpaDataLength)?
  185               switch ( W )
  186               {
  187                 // Yes!
  188                 default:
  189                   // Return error
  190                   goto _ERROR_DATA_LEN;
  191 
  192                   // No extra data written to sensor
  193                 case ( (uns8)( sizeof( _DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap ) ) ):
  194                   break;
  195 
  196                   // Extra data written to sensor
  197                 case ( (uns8)( sizeof( _DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap ) + 5 ) ):
  198                   // To the 1st i.e. temperature sensor?
  199                   if ( FSR1[offsetof( TPerStdSensorRead_Request, WrittenData ) + 0] != 0 )
  200                   {
  201                     // No, error
  202 _ERROR_DATA:
  203                     W = ERROR_DATA;
  204 _ERROR_W:
  205                     DpaApiReturnPeripheralError( W );
  206                   }
  207 
  208                   // Parse data content
  209                   switch ( FSR1[offsetof( TPerStdSensorRead_Request, WrittenData ) + 1] )
  210                   {
  211                     // Invalid 1st "header" byte
  212                     default:
  213                       goto _ERROR_DATA;
  214 
  215                       // Get calibration temperature
  216                     case STD_SENSOR_TYPE_TEMPERATURE_DATA_GET_1_POINT_CALIBRATION:
  217                       returnCalibration = TRUE;
  218                       break;
  219 
  220                       // Set calibration temperature
  221                     case STD_SENSOR_TYPE_TEMPERATURE_DATA_SET_1_POINT_CALIBRATION:
  222                       // Write calibration to the memory
  223                       bufferINFO[0] = FSR1[offsetof( TPerStdSensorRead_Request, WrittenData ) + 2];
  224                       bufferINFO[1] = FSR1[offsetof( TPerStdSensorRead_Request, WrittenData ) + 3];
  225                       eeWriteData( TEMP_CALIBRATION_ADDRESS, sizeof( uns16 ) );
  226                       break;
  227                   }
  228                   break;
  229               }
  230 
  231               // Get ready return data length for temperature data only (Note: optimization, 2 = 4/2 is used to save)
  232               _DpaDataLength = sizeof( uns16 );
  233 
  234               // Is my only sensor selected?
  235               if ( _DpaMessage.Request.PData[0].0 )
  236               {
  237                 // Measure temperature?
  238                 if ( !returnCalibration )
  239                 {
  240                   // Error reading temperature? Note: param3 holds temperature value after calling getTemperature()
  241                   if ( getTemperature() == -128 )
  242                     // Return standard error value
  243                     STD_SENSOR_TYPE_TEMPERATURE_SET_ERROR( param3 );
  244                   else
  245                   {
  246                     // Extent minus sign bit
  247                     if ( param3.11 )
  248                       param3 |= 0xF000;
  249 
  250                     CalibrateParam3();
  251                   }
  252                 }
  253                 else
  254                 {
  255                   // No, return calibration
  256                   param3 = 0;
  257                   CalibrateParam3();
  258                 }
  259 
  260                 // Return the sensor type too?
  261                 if ( _PCMD == PCMD_STD_SENSORS_READ_TYPES_AND_VALUES )
  262                 {
  263                   // 3 bytes (1 byte with type, 2 bytes with temperature value) will be returned
  264                   _DpaDataLength++;
  265 _Enumerate:
  266                   // 1st byte is sensor type
  267                   _DpaMessage.Response.PData[0] = STD_SENSOR_TYPE_TEMPERATURE;
  268                   // 3rd byte is higher byte of temperature value
  269                   _DpaMessage.Response.PData[2] = param3.high8;
  270                   // 2nd byte is lower byte of temperature value
  271                   W = param3.low8;
  272                 }
  273                 else
  274                 {
  275                   // 2 bytes to return
  276                   // 1st byte is lower byte of temperature value
  277                   _DpaMessage.Response.PData[0] = param3.low8;
  278                   // 2nd byte is higher byte of temperature value
  279                   W = param3.high8;
  280                 }
  281 
  282                 // Store 2nd byte for both supported commands
  283                 _DpaMessage.Response.PData[1] = W;
  284                 // Handled!
  285                 goto DpaHandleReturnTRUE;
  286               }
  287               else
  288               {
  289                 // My sensor not selected, so no data returned
  290                 _DpaDataLength = 0;
  291                 goto DpaHandleReturnTRUE;
  292               }
  293             }
  294           }
  295         }
  296 
  297         break;
  298       }
  299 
  300       // -------------------------------------------------
  301     case DpaEvent_FrcValue:
  302       // Called to get FRC value
  303 
  304       // Check for correct FRC and FRC user data (signature byte and sensor index == 0)
  305       switch ( _PCMD )
  306       {
  307         case FRC_STD_SENSORS_1B:
  308         case FRC_STD_SENSORS_2B:
  309           // FSR1 for optimization purposes (avoid MOVLB) will be used to point to DataOutBeforeResponseFRC[0...]
  310           FSR1 = &DataOutBeforeResponseFRC[0];
  311 
  312           if ( *FSR1++ /*DataOutBeforeResponseFRC[0]*/ == PNUM_STD_SENSORS &&
  313                ( *FSR1 /*DataOutBeforeResponseFRC[1]*/ == 0x00 || *FSR1 /*DataOutBeforeResponseFRC[1]*/ == STD_SENSOR_TYPE_TEMPERATURE ) &&
  314                ( *++FSR1 /*DataOutBeforeResponseFRC[2]*/ & 0x1f ) == 0 )
  315           {
  316             // Return error code
  317             responseFRCvalue2B = FRC_STD_FRC_ERROR_VALUE;
  318             // Get temperature and adjust it for FRC
  319             // Temperature OK?
  320             if ( getTemperature() != -128 )
  321             {
  322               // Extent minus sign bit
  323               if ( param3.11 )
  324                 param3 |= 0xF000;
  325 
  326               // Calibrate the temperatures
  327               CalibrateParam3();
  328 
  329               if ( _PCMD == FRC_STD_SENSORS_2B )
  330               {
  331 
  332                 // Return sensor FRC value 2B
  333                 responseFRCvalue2B = param3 ^ 0x8000;
  334               }
  335               else
  336               {
  337                 // Return sensor FRC value 1B
  338                 // Check for out of limits
  339                 if ( (int16)param3 <= (int16)( 105.5 * 16 ) && (int16)param3 >= ( (int16)-20 * 16 ) )
  340                 {
  341                   // Convert to the "F = ( T + 22 ) * 2 " from 1/16 resolution
  342                   param3 += 8 / 2 + (uns16)44 * 8; // Note: do rounding for /8
  343                   param3 /= 8;
  344                   responseFRCvalue = param3.low8;
  345                 }
  346               }
  347             }
  348 
  349             // The sensor was measured by FRC, check if there is a sleep request
  350             FSR1++;
  351             if ( INDF1.0 ) // Note: same as DataOutBeforeResponseFRC[3].0
  352             {
  353               // Remember sleep parameters to go to sleep at the Idle event later
  354               PerOSSleep_Request.Time.low8 = FSR1[4 - 3]; // Note: same as DataOutBeforeResponseFRC[4]
  355               PerOSSleep_Request.Time.high8 = FSR1[5 - 3]; // Note: same as DataOutBeforeResponseFRC[5]
  356               PerOSSleep_Request.Control = FSR1[6 - 3]; // Note: same as DataOutBeforeResponseFRC[6]
  357             }
  358           }
  359           break;
  360       }
  361       break;
  362 
  363       // -------------------------------------------------
  364     case DpaEvent_FrcResponseTime:
  365       // Called to get FRC response time
  366 
  367       // In this example the FRC command is a fast one
  368       switch ( DataOutBeforeResponseFRC[0] )
  369       {
  370         case FRC_STD_SENSORS_1B:
  371         case FRC_STD_SENSORS_2B:
  372           responseFRCvalue = _FRC_RESPONSE_TIME_40_MS;
  373           break;
  374       }
  375       break;
  376   }
  377 
  378 DpaHandleReturnFALSE:
  379   return FALSE;
  380 }
  381 
  382 
  383 //############################################################################################
  384 void CalibrateParam3()
  385 //############################################################################################
  386 {
  387   // Read calibration
  388   eeReadData( TEMP_CALIBRATION_ADDRESS, sizeof( uns16 ) );
  389   uns16 calib @ bufferINFO[0];
  390   param3 += calib;
  391 }
  392 
  393 //############################################################################################
  394 // 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)
  395 #include "DPAcustomHandler.h"
  396 //############################################################################################