EFM32 Happy Gecko Software Documentation  efm32hg-doc-5.1.2
tempdrv.c
Go to the documentation of this file.
1 /***************************************************************************/
33 #include "em_device.h"
34 #include "em_system.h"
35 #include "em_emu.h"
36 #include "string.h"
37 
38 #include "tempdrv.h"
39 
40 typedef struct
41 {
43  uint8_t temp;
45 
46 #if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
47 #define TEMPDRV_ERRATA_FIX
48 #endif
49 
50 #ifdef TEMPDRV_ERRATA_FIX
51 #define TEMPDRV_INT_CALLBACK_DEPTH 1
52 #define TEMPDRV_
53 #else
54 #define TEMPDRV_INT_CALLBACK_DEPTH 0
55 #endif
56 
57 #define TEMPDRV_CALLBACK_DEPTH (TEMPDRV_INT_CALLBACK_DEPTH + TEMPDRV_CUSTOM_CALLBACK_DEPTH)
58 #define TEMPDRV_CUSTOM_CALLBACK_INDEX TEMPDRV_INT_CALLBACK_DEPTH
59 
60 static TEMPDRV_CallbackSet_t tempdrvHighCallbacks[TEMPDRV_CALLBACK_DEPTH];
61 static TEMPDRV_CallbackSet_t tempdrvLowCallbacks[TEMPDRV_CALLBACK_DEPTH];
62 static TEMPDRV_CallbackSet_t nullCallback = {NULL, 0};
63 static TEMPDRV_CallbackSet_t *highCallback;
64 static TEMPDRV_CallbackSet_t *lowCallback;
65 
66 static bool TEMPDRV_InitState = false;
67 static bool TEMPDRV_EnableState = false;
68 
69 static int8_t convertToTemp(uint8_t emu);
70 static uint8_t convertToEmu(int8_t temp);
71 static void updateInterrupts(void);
72 
73 // Calibration values to be initialized in TEMPDRV_Init
74 static int32_t calibrationEMU;
75 static int32_t calibrationTEMP;
76 // Fallback calibration values in case DI calibration data not present
77 static uint8_t fallbackEMU = 0x90;
78 static uint8_t fallbackTEMP = 25;
79 
80 #if (EMU_CUSTOM_IRQ_HANDLER == false)
81 /***************************************************************************/
91 void EMU_IRQHandler(void)
92 {
94 }
95 #endif
96 
97 /***************************************************************************/
109 {
110  uint32_t flags = EMU_IntGetEnabled();
111  TEMPDRV_Callback_t activeCallback;
112  // High EMU value means a decreasing temperature
113  if (flags & EMU_IF_TEMPHIGH)
114  {
115  // High EMU interrupt = Low temp limit
116  if (lowCallback->callback != NULL)
117  {
118  activeCallback = lowCallback->callback;
119  memset(lowCallback, 0, sizeof(TEMPDRV_CallbackSet_t));
120  activeCallback(TEMPDRV_GetTemp(), TEMPDRV_LIMIT_LOW);
121  }
122  EMU_IntClear(EMU_IFC_TEMPHIGH);
123  }
124  else if (flags & EMU_IF_TEMPLOW)
125  {
126  // Low EMU interrupt = high temp limit
127  if (highCallback->callback != NULL)
128  {
129  activeCallback = highCallback->callback;
130  memset(highCallback, 0, sizeof(TEMPDRV_CallbackSet_t));
131  activeCallback(TEMPDRV_GetTemp(), TEMPDRV_LIMIT_HIGH);
132  }
133  EMU_IntClear(EMU_IFC_TEMPLOW);
134  }
136 }
137 
138 #if defined(TEMPDRV_ERRATA_FIX)
139 /* Errata */
140 typedef enum ErrataState
141 {
142  ERRATA_LOW = 0,
143  ERRATA_MID = 1,
144  ERRATA_HIGH = 2
145 } ErrataState_t;
146 
147 // These are the temperature limits in degrees Celsius
148 #define ERRATA_MID_LIMIT 65
149 #define ERRATA_HIGH_LIMIT 80
150 #define ERRATA_HYSTERESIS 8
151 
152 static void errataCallback(int8_t temp, TEMPDRV_LimitType_t limit);
153 static void errataStateUpdate(int8_t temp);
154 
155 static TEMPDRV_CallbackSet_t errataLowTemp[3]; // Temperature decrease thresholds
156 static TEMPDRV_CallbackSet_t errataHighTemp[3]; // Temperature increase thresholds
157 
158 /***************************************************************************/
166 static void errataCallback(int8_t temp, TEMPDRV_LimitType_t limit)
167 {
168  (void) limit; // unused
169  errataStateUpdate(temp);
170 }
171 
172 /***************************************************************************/
179 static void errataStateUpdate(int8_t temp)
180 {
181  ErrataState_t errataState;
182  bool emuLocked = (EMU->LOCK == EMU_LOCK_LOCKKEY_LOCKED);
183 
184  // Figure out the current state based on temperature
185  if (temp < ERRATA_MID_LIMIT)
186  {
187  errataState = ERRATA_LOW;
188  }
189  else if (temp < ERRATA_HIGH_LIMIT)
190  {
191  errataState = ERRATA_MID;
192  }
193  else
194  {
195  errataState = ERRATA_HIGH;
196  }
197 
198  // Activate callback and thresholds for the current state
199  tempdrvHighCallbacks[0] = errataHighTemp[errataState];
200  tempdrvLowCallbacks[0] = errataLowTemp[errataState];
201 
202  if (emuLocked)
203  {
205  }
206 
207  switch (errataState)
208  {
209  case ERRATA_LOW:
210  EMU_SetBiasMode(emuBiasMode_1KHz);
211  break;
212  case ERRATA_MID:
213  EMU_SetBiasMode(emuBiasMode_4KHz);
214  break;
215  case ERRATA_HIGH:
216  EMU_SetBiasMode(emuBiasMode_Continuous);
217  break;
218  }
219 
220  if (emuLocked)
221  {
222  EMU->LOCK = EMU_LOCK_LOCKKEY_LOCK;
223  }
224 }
225 
226 /***************************************************************************/
234 static void errataInit(void)
235 {
238 
239  /* Rev A temp errata handling */
240  if (rev.major == 0x01)
241  {
242  uint8_t limitLow;
243  uint8_t limitHigh;
244 
245  // Initialize Low temperature state [*, 65]
246  limitHigh = convertToEmu(ERRATA_MID_LIMIT);
247  errataHighTemp[ERRATA_LOW].temp = limitHigh;
248  errataHighTemp[ERRATA_LOW].callback = errataCallback;
249 
250  // Initialize Mid temperature state [57, 80]
251  limitLow = convertToEmu(ERRATA_MID_LIMIT - ERRATA_HYSTERESIS);
252  limitHigh = convertToEmu(ERRATA_HIGH_LIMIT);
253  errataLowTemp[ERRATA_MID].temp = limitLow;
254  errataLowTemp[ERRATA_MID].callback = errataCallback;
255  errataHighTemp[ERRATA_MID].temp = limitHigh;
256  errataHighTemp[ERRATA_MID].callback = errataCallback;
257 
258  // Initialize High temperature state [72, *]
259  limitLow = convertToEmu(ERRATA_HIGH_LIMIT - ERRATA_HYSTERESIS);
260  errataLowTemp[ERRATA_HIGH].temp = limitLow;
261  errataLowTemp[ERRATA_HIGH].callback = errataCallback;
262 
263  errataStateUpdate(TEMPDRV_GetTemp());
264  }
265 }
266 #else // TEMPDRV_ERRATA_FIX
267 
268 #define errataInit()
269 
270 #endif // TEMPDRV_ERRATA_FIX
271 
272 /* Internal Functions */
273 /***************************************************************************/
284 {
285  uint8_t index;
286  for (index = TEMPDRV_CUSTOM_CALLBACK_INDEX; index < TEMPDRV_CALLBACK_DEPTH; index++)
287  {
288  if (set[index].callback==NULL)
289  {
290  return index;
291  }
292  }
293  // no empty space, return -1
294  return -1;
295 }
296 
297 /***************************************************************************/
316  int8_t temp,
317  TEMPDRV_Callback_t callback)
318 {
319  int8_t index = findCallbackSpace(set);
320  if (index < 0)
321  {
323  }
324  set[index].temp = convertToEmu(temp);
325  set[index].callback = callback;
327  return ECODE_EMDRV_TEMPDRV_OK;
328 }
329 
330 /***************************************************************************/
344  TEMPDRV_Callback_t callback)
345 {
346  bool found = false;
347  uint8_t index;
348  for (index = TEMPDRV_CUSTOM_CALLBACK_INDEX; index < TEMPDRV_CALLBACK_DEPTH; index++)
349  {
350  if (set[index].callback == callback)
351  {
352  set[index].callback = NULL;
353  set[index].temp = 0;
354  found = true;
355  }
356  }
358  return found;
359 }
360 
361 /***************************************************************************/
374 static bool checkForDuplicates(TEMPDRV_CallbackSet_t *set, int8_t temp)
375 {
376  uint8_t index;
377  uint8_t emu = convertToEmu(temp);
378  for (index = TEMPDRV_CUSTOM_CALLBACK_INDEX; index < TEMPDRV_CALLBACK_DEPTH; index++)
379  {
380  // filter out only entries with valid callbacks
381  if (set[index].callback!=NULL)
382  {
383  // if duplicate temperature, return true
384  if (set[index].temp == emu)
385  {
386  return true;
387  }
388  }
389  }
390  // return false if no duplicate temperatures found
391  return false;
392 }
393 
394 /***************************************************************************/
404 static int8_t convertToTemp(uint8_t emu)
405 {
406  int32_t res = (int32_t) calibrationTEMP - ((emu * 8) / 5);
407  // Cap conversion results at int8_t bounds
408  if (res < -128)
409  {
410  res = -128;
411  }
412  else if (res > 127)
413  {
414  res = 127;
415  }
416 
417  return (int8_t) res;
418 }
419 
420 /***************************************************************************/
430 static uint8_t convertToEmu(int8_t temp)
431 {
432  int32_t res = (int32_t) calibrationEMU - ((temp * 5) >> 3);
433  // Cap conversion results at uint8_t bounds
434  if (res > 255)
435  {
436  res = 255;
437  }
438  else if (res < 0)
439  {
440  res = 0;
441  }
442  return (uint8_t) res;
443 }
444 
445 /***************************************************************************/
449 static void disableInterrupts(void)
450 {
451  EMU_IntClear(EMU_IFC_TEMPLOW | EMU_IFC_TEMPHIGH);
452  EMU_IntDisable(EMU_IFC_TEMPLOW | EMU_IFC_TEMPHIGH);
453 }
454 
455 /***************************************************************************/
466 static void updateInterrupts(void)
467 {
468  // Find lowest temperature active high callback
469  uint8_t index;
470  for (index = 0; index < TEMPDRV_CALLBACK_DEPTH; index++)
471  {
472  // filter out only entries with valid callbacks
473  if (tempdrvHighCallbacks[index].callback!=NULL)
474  {
475  if (highCallback->callback == NULL)
476  {
477  highCallback = &tempdrvHighCallbacks[index];
478  }
479  else
480  {
481  if (tempdrvHighCallbacks[index].temp>highCallback->temp)
482  {
483  highCallback = &tempdrvHighCallbacks[index];
484  }
485  }
486  }
487  }
488  // If active callback, set and enable interrupt
489  if (highCallback->callback != NULL)
490  {
491  // EMU and TEMP are inversely proportional
492  EMU->TEMPLIMITS &= ~_EMU_TEMPLIMITS_TEMPLOW_MASK;
493  EMU->TEMPLIMITS |= highCallback->temp << _EMU_TEMPLIMITS_TEMPLOW_SHIFT;
494  EMU_IntEnable(EMU_IEN_TEMPLOW);
495  }
496  else
497  {
498  EMU_IntDisable(EMU_IEN_TEMPLOW);
499  }
500 
501  // Find highest temperature active low callback
502  for (index = 0; index < TEMPDRV_CALLBACK_DEPTH; index++)
503  {
504  // filter out only entries with valid callbacks
505  if (tempdrvLowCallbacks[index].callback!=NULL)
506  {
507  if (lowCallback->callback == NULL)
508  {
509  lowCallback = &tempdrvLowCallbacks[index];
510  }
511  else
512  {
513  if (tempdrvLowCallbacks[index].temp<lowCallback->temp)
514  {
515  lowCallback = &tempdrvLowCallbacks[index];
516  }
517  }
518  }
519  }
520  // If active callback, set and enable interrupt
521  if (lowCallback->callback!=NULL)
522  {
523  // EMU and TEMP are inversely proportional
524  EMU->TEMPLIMITS &= ~_EMU_TEMPLIMITS_TEMPHIGH_MASK;
525  EMU->TEMPLIMITS |= lowCallback->temp << _EMU_TEMPLIMITS_TEMPHIGH_SHIFT;
526  EMU_IntEnable(EMU_IEN_TEMPHIGH);
527  }
528  else
529  {
530  EMU_IntDisable(EMU_IEN_TEMPHIGH);
531  }
532 }
533 
534 /* Official API */
535 /***************************************************************************/
548 {
549  uint32_t DItemp, DIemu;
550 
551  // Flag up
552  TEMPDRV_InitState = true;
553 
554  // reset stack state by erasing callbacks
555  memset(tempdrvHighCallbacks, 0, sizeof (TEMPDRV_CallbackSet_t) * TEMPDRV_CALLBACK_DEPTH);
556  memset(tempdrvLowCallbacks, 0, sizeof (TEMPDRV_CallbackSet_t) * TEMPDRV_CALLBACK_DEPTH);
557  highCallback = &nullCallback;
558  lowCallback = &nullCallback;
559 
560  // Retrieve calibration data from DI page
562  DIemu = ((DEVINFO->EMUTEMP & _DEVINFO_EMUTEMP_EMUTEMPROOM_MASK) >> _DEVINFO_EMUTEMP_EMUTEMPROOM_SHIFT);
563 
564  if ((DItemp == (_DEVINFO_CAL_TEMP_MASK >> _DEVINFO_CAL_TEMP_SHIFT))
565  || (DIemu == (_DEVINFO_EMUTEMP_EMUTEMPROOM_MASK >> _DEVINFO_EMUTEMP_EMUTEMPROOM_SHIFT)))
566  {
567  // Missing DI page calibration data, substitute fixed values
568  DItemp = fallbackTEMP;
569  DIemu = fallbackEMU;
570  }
571 
572  // calculate conversion offsets. Based on assumed slope of 5/8
573  calibrationEMU = (DIemu) + ((5*(DItemp))/8);
574  calibrationTEMP = (DItemp) + (8*(DIemu)/5);
575 
576  errataInit();
577 
579  NVIC_ClearPendingIRQ(EMU_IRQn);
580  NVIC_EnableIRQ(EMU_IRQn);
582 
583  return ECODE_EMDRV_TEMPDRV_OK;
584 }
585 
586 /***************************************************************************/
598 {
599  TEMPDRV_InitState = false;
600  NVIC_DisableIRQ(EMU_IRQn);
601  NVIC_ClearPendingIRQ(EMU_IRQn);
603  memset(tempdrvHighCallbacks, 0, sizeof (TEMPDRV_CallbackSet_t) * TEMPDRV_CALLBACK_DEPTH);
604  memset(tempdrvLowCallbacks, 0, sizeof (TEMPDRV_CallbackSet_t) * TEMPDRV_CALLBACK_DEPTH);
605  return ECODE_EMDRV_TEMPDRV_OK;
606 }
607 
608 /***************************************************************************/
619 {
620  if (TEMPDRV_EnableState != enable)
621  {
622  TEMPDRV_EnableState = enable;
623  if (enable)
624  {
626  }
627  else
628  {
630  }
631  }
632  return ECODE_EMDRV_TEMPDRV_OK;
633 }
634 
635 /***************************************************************************/
646 {
648 
649  if (limit == TEMPDRV_LIMIT_HIGH)
650  {
651  // Define callback set
652  set = tempdrvHighCallbacks;
653  }
654  else if (limit == TEMPDRV_LIMIT_LOW)
655  {
656  // Define callback set
657  set = tempdrvLowCallbacks;
658  }
659  else
660  {
661  // Invalid limit
662  return 0;
663  }
664  uint8_t index, count=0;
665  for (index = TEMPDRV_CUSTOM_CALLBACK_INDEX; index < TEMPDRV_CALLBACK_DEPTH; index++)
666  {
667  // filter out only entries with valid callbacks
668  if (set[index].callback!=NULL)
669  {
670  count++;
671  }
672  }
673  return count;
674 }
675 
676 /***************************************************************************/
683 int8_t TEMPDRV_GetTemp(void)
684 {
685  return convertToTemp(EMU->TEMP);
686 }
687 
688 /***************************************************************************/
751  TEMPDRV_LimitType_t limit,
752  TEMPDRV_Callback_t callback)
753 {
755  if (TEMPDRV_InitState == false)
756  {
758  }
759  // cannot register null callback
760  if (callback == NULL)
761  {
763  }
764  if (limit == TEMPDRV_LIMIT_HIGH)
765  {
766  // current temperature is already higher than requested temperature
767  if (TEMPDRV_GetTemp() > temp)
768  {
770  }
771  // Define callback set
772  set = tempdrvHighCallbacks;
773  }
774  else if (limit == TEMPDRV_LIMIT_LOW)
775  {
776  // current temperature is already lower than requested temperature
777  if (TEMPDRV_GetTemp() < temp)
778  {
780  }
781  // Define callback set
782  set = tempdrvLowCallbacks;
783  }
784  else
785  {
786  // Invalid limit
788  }
789 
790  // Cannot register duplicate temperature callback
791  if (checkForDuplicates(set, temp) == true)
792  {
794  }
795 
796  return addCallback(set, temp, callback);
797 }
798 
799 /***************************************************************************/
814 {
815  // cannot register null callback
816  if (callback == NULL)
817  {
819  }
820  if (removeCallback(tempdrvHighCallbacks,callback) == false &&
821  removeCallback(tempdrvLowCallbacks,callback) == false)
822  {
824  }
825  return ECODE_EMDRV_TEMPDRV_OK;
826 }
827 
828 /******** THE REST OF THE FILE IS DOCUMENTATION ONLY !**********************/
static int8_t convertToTemp(uint8_t emu)
Convert EMU value to degrees Celsius.
Definition: tempdrv.c:404
static void updateInterrupts(void)
Update interrupts based on active callbacks.
Definition: tempdrv.c:466
static bool removeCallback(TEMPDRV_CallbackSet_t *set, TEMPDRV_Callback_t callback)
Remove a callback from the set.
Definition: tempdrv.c:343
#define ECODE_EMDRV_TEMPDRV_DUP_TEMP
Requested temperature is a duplicate.
Definition: tempdrv.h:63
TEMPDRV_Callback_t callback
Callback function.
Definition: tempdrv.c:42
void SYSTEM_ChipRevisionGet(SYSTEM_ChipRevision_TypeDef *rev)
Get chip major/minor revision.
Definition: em_system.c:58
static bool checkForDuplicates(TEMPDRV_CallbackSet_t *set, int8_t temp)
Check if another callback has registered the same temperature.
Definition: tempdrv.c:374
static int8_t findCallbackSpace(TEMPDRV_CallbackSet_t *set)
Find an empty spot for callback in set.
Definition: tempdrv.c:283
void(* TEMPDRV_Callback_t)(int8_t temp, TEMPDRV_LimitType_t limit)
TEMPDRV temperature limit callback function.
Definition: tempdrv.h:91
#define _DEVINFO_CAL_TEMP_MASK
int8_t TEMPDRV_GetTemp(void)
Get the current temperature.
Definition: tempdrv.c:683
CMSIS Cortex-M Peripheral Access Layer for Silicon Laboratories microcontroller devices.
TEMPDRV API definition.
Ecode_t TEMPDRV_DeInit(void)
De-initialize the TEMP driver.
Definition: tempdrv.c:597
static uint8_t convertToEmu(int8_t temp)
Convert a temperature in °C to an EMU sensor value.
Definition: tempdrv.c:430
void TEMPDRV_IRQHandler(void)
TEMPDRV Interrupt Handler.
Definition: tempdrv.c:108
#define DEVINFO
static Ecode_t addCallback(TEMPDRV_CallbackSet_t *set, int8_t temp, TEMPDRV_Callback_t callback)
Attempt to add a callback to a set.
Definition: tempdrv.c:315
#define ECODE_EMDRV_TEMPDRV_BAD_LIMIT
Temperature mismatch with limit.
Definition: tempdrv.h:58
void EMU_IRQHandler(void)
EMU Interrupt Handler.
Definition: tempdrv.c:91
#define ECODE_EMDRV_TEMPDRV_NO_INIT
Function requires prior initialization.
Definition: tempdrv.h:56
uint32_t Ecode_t
Typedef for API function error code return values.
Definition: ecode.h:51
#define EMU_LOCK_LOCKKEY_UNLOCK
Definition: efm32hg_emu.h:93
uint8_t temp
Limit temperature (EMU value)
Definition: tempdrv.c:43
uint8_t TEMPDRV_GetActiveCallbacks(TEMPDRV_LimitType_t limit)
Get the number of active callbacks for a limit.
Definition: tempdrv.c:645
#define ECODE_EMDRV_TEMPDRV_PARAM_ERROR
Illegal input parameter.
Definition: tempdrv.h:57
#define EMU_LOCK_LOCKKEY_LOCK
Definition: efm32hg_emu.h:90
Ecode_t TEMPDRV_UnregisterCallback(TEMPDRV_Callback_t callback)
Unregister a callback in the TEMP driver.
Definition: tempdrv.c:813
#define ECODE_EMDRV_TEMPDRV_NO_SPACE
No more space to register.
Definition: tempdrv.h:60
Ecode_t TEMPDRV_Enable(bool enable)
Enable or disable the TEMP driver.
Definition: tempdrv.c:618
Energy management unit (EMU) peripheral API.
#define EMU_LOCK_LOCKKEY_LOCKED
Definition: efm32hg_emu.h:92
#define ECODE_EMDRV_TEMPDRV_NO_CALLBACK
Can't find callback.
Definition: tempdrv.h:59
static void disableInterrupts(void)
Turn off and clear EMU temperature related interrupts.
Definition: tempdrv.c:449
#define EMU
System API.
enum TEMPDRV_LimitType TEMPDRV_LimitType_t
#define ECODE_EMDRV_TEMPDRV_OK
Success return value.
Definition: tempdrv.h:55
Ecode_t TEMPDRV_RegisterCallback(int8_t temp, TEMPDRV_LimitType_t limit, TEMPDRV_Callback_t callback)
Register a callback in the TEMP driver.
Definition: tempdrv.c:750
Ecode_t TEMPDRV_Init(void)
Initialize the TEMP driver.
Definition: tempdrv.c:547
#define _DEVINFO_CAL_TEMP_SHIFT