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