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 //############################################################################################