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