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