1 /*
2  * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdlib.h>
8 #include <zephyr/kernel.h>
9 
10 #include "soc/soc_caps.h"
11 #include "esp_private/sar_periph_ctrl.h"
12 #include "esp_log.h"
13 
14 #if SOC_TEMP_SENSOR_SUPPORTED
15 #include "hal/temperature_sensor_ll.h"
16 #include "soc/temperature_sensor_periph.h"
17 #include "soc/periph_defs.h"
18 #include "esp_private/periph_ctrl.h"
19 #include "esp_private/adc_share_hw_ctrl.h"
20 
21 extern int rtc_spinlock;
22 
23 #define RTC_ENTER_CRITICAL()    do { rtc_spinlock = irq_lock(); } while(0)
24 #define RTC_EXIT_CRITICAL()    irq_unlock(rtc_spinlock);
25 
26 
27 /*------------------------------------------------------------------------------------------------------------
28 -----------------------------------------Temperature Sensor---------------------------------------------------
29 ------------------------------------------------------------------------------------------------------------*/
30 static const char *TAG_TSENS = "temperature_sensor";
31 
32 #define INT_NOT_USED 999999
33 
34 static int s_record_min = INT_NOT_USED;
35 static int s_record_max = INT_NOT_USED;
36 static int s_temperature_sensor_power_cnt;
37 
38 static uint8_t s_tsens_idx = 2; // Index for temperature attribute, set 2(middle) as default value
39 
temperature_sensor_power_acquire(void)40 void temperature_sensor_power_acquire(void)
41 {
42     RTC_ENTER_CRITICAL();
43     s_temperature_sensor_power_cnt++;
44     if (s_temperature_sensor_power_cnt == 1) {
45         adc_apb_periph_claim();
46         periph_module_enable(PERIPH_TEMPSENSOR_MODULE);
47         periph_module_reset(PERIPH_TEMPSENSOR_MODULE);
48         regi2c_saradc_enable();
49         temperature_sensor_ll_clk_enable(true);
50         temperature_sensor_ll_enable(true);
51     }
52     RTC_EXIT_CRITICAL();
53 }
54 
temperature_sensor_power_release(void)55 void temperature_sensor_power_release(void)
56 {
57     RTC_ENTER_CRITICAL();
58     s_temperature_sensor_power_cnt--;
59     /* Sanity check */
60     if (s_temperature_sensor_power_cnt < 0) {
61         RTC_EXIT_CRITICAL();
62         ESP_LOGE(TAG_TSENS, "%s called, but s_temperature_sensor_power_cnt == 0", __func__);
63         abort();
64     } else if (s_temperature_sensor_power_cnt == 0) {
65         temperature_sensor_ll_clk_enable(false);
66         temperature_sensor_ll_enable(false);
67         regi2c_saradc_disable();
68         periph_module_disable(PERIPH_TEMPSENSOR_MODULE);
69         adc_apb_periph_free();
70     }
71     RTC_EXIT_CRITICAL();
72 }
73 
temperature_sensor_get_raw_value(void)74 static int temperature_sensor_get_raw_value(void)
75 {
76     int raw_value  = temperature_sensor_ll_get_raw_value();
77     return (TEMPERATURE_SENSOR_LL_ADC_FACTOR * raw_value - TEMPERATURE_SENSOR_LL_DAC_FACTOR * temperature_sensor_attributes[s_tsens_idx].offset - TEMPERATURE_SENSOR_LL_OFFSET_FACTOR);
78 }
79 
temp_sensor_sync_tsens_idx(int tsens_idx)80 void temp_sensor_sync_tsens_idx(int tsens_idx)
81 {
82     s_tsens_idx = tsens_idx;
83 }
84 
temp_sensor_get_raw_value(bool * range_changed)85 int16_t temp_sensor_get_raw_value(bool *range_changed)
86 {
87     RTC_ENTER_CRITICAL();
88 
89     int degree = temperature_sensor_get_raw_value();
90     uint8_t temperature_dac;
91 
92     // 1. Check whether temperature value is in range
93     if (s_record_min != INT_NOT_USED && degree >= s_record_min && degree <= s_record_max) {
94         // If degree is in range, not needed to do any check to save time. Otherwise, choose proper range and record.
95         if (range_changed != NULL) {
96             *range_changed = false;
97         }
98         RTC_EXIT_CRITICAL();
99         return degree;
100     }
101 
102     // 2. If temperature value is not in range, adjust to proper range
103     if (degree >= temperature_sensor_attributes[1].range_max) {
104         s_tsens_idx = 0;
105     } else if (degree >= temperature_sensor_attributes[2].range_max && degree < temperature_sensor_attributes[1].range_max) {
106         s_tsens_idx = 1;
107     } else if (degree <= temperature_sensor_attributes[2].range_min && degree > temperature_sensor_attributes[3].range_min) {
108         s_tsens_idx = 3;
109     } else if (degree <= temperature_sensor_attributes[3].range_min) {
110         s_tsens_idx = 4;
111     } else {
112         s_tsens_idx = 2;
113     }
114     ESP_EARLY_LOGD(TAG_TSENS, "range changed, change to index %d", s_tsens_idx);
115     temperature_dac = temperature_sensor_attributes[s_tsens_idx].reg_val;
116     s_record_min = temperature_sensor_attributes[s_tsens_idx].range_min;
117     s_record_max = temperature_sensor_attributes[s_tsens_idx].range_max;
118 
119     temperature_sensor_ll_set_range(temperature_dac);
120 
121     // 3. Then, read value again
122     // Before reading the temperature value, ticks need to be delayed, otherwise a wrong value will be returned.
123     // As what has been recommended and tested, 300us is a good interval to get the correct value after adjust range.
124     esp_rom_delay_us(300);
125     degree = temperature_sensor_get_raw_value();
126     if (range_changed != NULL) {
127         *range_changed = true;
128     }
129 
130     RTC_EXIT_CRITICAL();
131     return degree;
132 }
133 
134 #endif
135