Skip to content

Commit

Permalink
Merge branch 'feature/temperature_intr' into 'master'
Browse files Browse the repository at this point in the history
temperature sensor: Add high/low value threshold interrupt support

Closes IDF-5786

See merge request espressif/esp-idf!22331
  • Loading branch information
mythbuster5 committed Mar 3, 2023
2 parents 40ca803 + 3526ff3 commit 679dae7
Show file tree
Hide file tree
Showing 26 changed files with 649 additions and 66 deletions.
8 changes: 8 additions & 0 deletions components/driver/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,14 @@ menu "Driver Configurations"
Wether to enable the debug log message for temperature sensor driver.
Note that, this option only controls the temperature sensor driver log, won't affect other drivers.

config TEMP_SENSOR_ISR_IRAM_SAFE
depends on SOC_TEMPERATURE_SENSOR_INTR_SUPPORT
bool "Temperature sensor ISR IRAM-Safe"
default n
help
Ensure the Temperature Sensor interrupt is IRAM-Safe by allowing the interrupt handler to be
executable when the cache is disabled (e.g. SPI Flash write).

endmenu # TEMP_SENSOR Configuration

menu "UART Configuration"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/*
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "hal/temperature_sensor_types.h"

Expand Down Expand Up @@ -94,6 +95,92 @@ esp_err_t temperature_sensor_disable(temperature_sensor_handle_t tsens);
*/
esp_err_t temperature_sensor_get_celsius(temperature_sensor_handle_t tsens, float *out_celsius);

#if SOC_TEMPERATURE_SENSOR_INTR_SUPPORT

/**
* @brief Temperature sensor event data
*/
typedef struct {
int celsius_value; /**< Celsius value in interrupt callback. */
} temperature_sensor_threshold_event_data_t;

/**
* @brief Callback for temperature sensor threshold interrupt.
*
* @param[in] tsens The handle created by `temperature_sensor_install()`.
* @param[in] edata temperature sensor event data, fed by driver.
* @param[in] user_data User data, set in `temperature_sensor_register_callbacks()`.
* @return Whether a high priority task has been waken up by this function.
*/
typedef bool (*temperature_thres_cb_t)(temperature_sensor_handle_t tsens, const temperature_sensor_threshold_event_data_t *edata, void *user_data);

/**
* @brief Group of temperature sensor callback functions, all of them will be run in ISR.
*/
typedef struct {
temperature_thres_cb_t on_threshold; /**< Temperature value interrupt callback */
} temperature_sensor_event_callbacks_t;

/**
* @brief Config options for temperature value absolute interrupt.
*/
typedef struct {
float high_threshold; /**< High threshold value(Celsius). Interrupt will be triggered if temperature value is higher than this value */
float low_threshold; /**< Low threshold value(Celsius). Interrupt will be triggered if temperature value is lower than this value */
} temperature_sensor_abs_threshold_config_t;

/**
* @brief Set temperature sensor absolute mode automatic monitor.
*
* @param tsens The handle created by `temperature_sensor_install()`.
* @param abs_cfg Configuration of temperature sensor absolute mode interrupt, see `temperature_sensor_abs_threshold_config_t`.
* @note This function should not be called with `temperature_sensor_set_delta_threshold`.
*
* @return
* - ESP_OK: Set absolute threshold successfully.
* - ESP_ERR_INVALID_STATE: Set absolute threshold failed because of wrong state.
* - ESP_ERR_INVALID_ARG: Set absolute threshold failed because of invalid argument.
*/
esp_err_t temperature_sensor_set_absolute_threshold(temperature_sensor_handle_t tsens, const temperature_sensor_abs_threshold_config_t *abs_cfg);

/**
* @brief Config options for temperature value delta interrupt.
*/
typedef struct {
float increase_delta; /**< Interrupt will be triggered if the temperature increment of two consecutive samplings if larger than `increase_delta` */
float decrease_delta; /**< Interrupt will be triggered if the temperature decrement of two consecutive samplings if smaller than `decrease_delta` */
} temperature_sensor_delta_threshold_config_t;

/**
* @brief Set temperature sensor differential mode automatic monitor.
*
* @param tsens The handle created by `temperature_sensor_install()`.
* @param delta_cfg Configuration of temperature sensor delta mode interrupt, see `temperature_sensor_delta_threshold_config_t`.
* @note This function should not be called with `temperature_sensor_set_absolute_threshold`
*
* @return
* - ESP_OK: Set differential value threshold successfully.
* - ESP_ERR_INVALID_STATE: Set absolute threshold failed because of wrong state.
* - ESP_ERR_INVALID_ARG: Set differential value threshold failed because of invalid argument.
*/
esp_err_t temperature_sensor_set_delta_threshold(temperature_sensor_handle_t tsens, const temperature_sensor_delta_threshold_config_t *delta_cfg);

/**
* @brief Install temperature sensor interrupt callback. Temperature sensor interrupt will be enabled at same time
*
* @param tsens The handle created by `temperature_sensor_install()`.
* @param cbs Pointer to the group of temperature sensor interrupt callbacks.
* @param user_arg Callback argument.
*
* @return
* - ESP_OK: Set event callbacks successfully
* - ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument
* - ESP_FAIL: Set event callbacks failed because of other error
*/
esp_err_t temperature_sensor_register_callbacks(temperature_sensor_handle_t tsens, const temperature_sensor_event_callbacks_t *cbs, void *user_arg);

#endif // SOC_TEMPERATURE_SENSOR_INTR_SUPPORT

#ifdef __cplusplus
}
#endif
142 changes: 128 additions & 14 deletions components/driver/temperature_sensor/temperature_sensor.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,18 @@
#include "driver/temperature_sensor.h"
#include "esp_efuse_rtc_calib.h"
#include "esp_private/periph_ctrl.h"
#include "temperature_sensor_private.h"
#include "hal/temperature_sensor_ll.h"
#include "soc/temperature_sensor_periph.h"
#include "esp_memory_utils.h"

static const char *TAG = "temperature_sensor";

typedef enum {
TEMP_SENSOR_FSM_INIT,
TEMP_SENSOR_FSM_ENABLE,
} temp_sensor_fsm_t;

static float s_deltaT = NAN; // unused number

typedef struct temperature_sensor_obj_t temperature_sensor_obj_t;

struct temperature_sensor_obj_t {
const temperature_sensor_attribute_t *tsens_attribute;
temp_sensor_fsm_t fsm;
temperature_sensor_clk_src_t clk_src;
};
#if SOC_TEMPERATURE_SENSOR_INTR_SUPPORT
static int8_t s_temperature_regval_2_celsius(temperature_sensor_handle_t tsens, uint8_t regval);
#endif // SOC_TEMPERATURE_SENSOR_INTR_SUPPORT

static temperature_sensor_attribute_t *s_tsens_attribute_copy;

Expand All @@ -53,7 +46,7 @@ static int inline accuracy_compare(const void *p1, const void *p2)

static esp_err_t temperature_sensor_attribute_table_sort(void)
{
s_tsens_attribute_copy = (temperature_sensor_attribute_t *)heap_caps_malloc(sizeof(temperature_sensor_attributes), MALLOC_CAP_DEFAULT);
s_tsens_attribute_copy = (temperature_sensor_attribute_t *)heap_caps_malloc(sizeof(temperature_sensor_attributes), TEMPERATURE_SENSOR_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(s_tsens_attribute_copy != NULL, ESP_ERR_NO_MEM, TAG, "No space for s_tsens_attribute_copy");
for (int i = 0 ; i < TEMPERATURE_SENSOR_ATTR_RANGE_NUM; i++) {
s_tsens_attribute_copy[i] = temperature_sensor_attributes[i];
Expand All @@ -75,6 +68,27 @@ static esp_err_t temperature_sensor_choose_best_range(temperature_sensor_handle_
return ESP_OK;
}

#if SOC_TEMPERATURE_SENSOR_INTR_SUPPORT
static void IRAM_ATTR temperature_sensor_isr(void *arg)
{
temperature_sensor_ll_clear_intr();
bool cbs_yield = false;
temperature_sensor_handle_t tsens = (temperature_sensor_handle_t) arg;
temperature_sensor_threshold_event_data_t data = {
.celsius_value = s_temperature_regval_2_celsius(tsens, temperature_sensor_ll_get_raw_value()),
};
if (tsens->threshold_cbs) {
if (tsens->threshold_cbs(tsens, &data, tsens->cb_user_arg)) {
cbs_yield = true;
}
}

if (cbs_yield) {
portYIELD_FROM_ISR();
}
}
#endif // SOC_TEMPERATURE_SENSOR_INTR_SUPPORT

esp_err_t temperature_sensor_install(const temperature_sensor_config_t *tsens_config, temperature_sensor_handle_t *ret_tsens)
{
#if CONFIG_TEMP_SENSOR_ENABLE_DEBUG_LOG
Expand Down Expand Up @@ -121,6 +135,13 @@ esp_err_t temperature_sensor_uninstall(temperature_sensor_handle_t tsens)
s_tsens_attribute_copy = NULL;
regi2c_saradc_disable();

#if SOC_TEMPERATURE_SENSOR_INTR_SUPPORT
temperature_sensor_ll_enable_intr(false);
if (tsens->temp_sensor_isr_handle) {
ESP_RETURN_ON_ERROR(esp_intr_free(tsens->temp_sensor_isr_handle), TAG, "uninstall interrupt service failed");
}
#endif // SOC_TEMPERATURE_SENSOR_INTR_SUPPORT

periph_module_disable(PERIPH_TEMPSENSOR_MODULE);
free(tsens);
return ESP_OK;
Expand All @@ -137,6 +158,11 @@ esp_err_t temperature_sensor_enable(temperature_sensor_handle_t tsens)
}
#endif

#if SOC_TEMPERATURE_SENSOR_INTR_SUPPORT
temperature_sensor_ll_wakeup_enable(true);
temperature_sensor_ll_sample_enable(true);
#endif // SOC_TEMPERATURE_SENSOR_INTR_SUPPORT

temperature_sensor_ll_clk_enable(true);
temperature_sensor_ll_clk_sel(tsens->clk_src);
temperature_sensor_ll_enable(true);
Expand All @@ -149,6 +175,11 @@ esp_err_t temperature_sensor_disable(temperature_sensor_handle_t tsens)
ESP_RETURN_ON_FALSE(tsens, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(tsens->fsm == TEMP_SENSOR_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "tsens not enabled yet");

#if SOC_TEMPERATURE_SENSOR_INTR_SUPPORT
temperature_sensor_ll_wakeup_enable(false);
temperature_sensor_ll_sample_enable(false);
#endif

temperature_sensor_ll_enable(false);
#if SOC_TEMPERATURE_SENSOR_SUPPORT_FAST_RC
if (tsens->clk_src == TEMPERATURE_SENSOR_CLK_SRC_RC_FAST) {
Expand Down Expand Up @@ -185,7 +216,7 @@ esp_err_t temperature_sensor_get_celsius(temperature_sensor_handle_t tsens, floa
ESP_RETURN_ON_FALSE(tsens->fsm == TEMP_SENSOR_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "tsens not enabled yet");

uint32_t tsens_out = temperature_sensor_ll_get_raw_value();
ESP_LOGV(TAG, "tsens_out %"PRIu32, tsens_out);
ESP_LOGD(TAG, "tsens_out %"PRIu32, tsens_out);

*out_celsius = parse_temp_sensor_raw_value(tsens_out, tsens->tsens_attribute->offset);
if (*out_celsius < tsens->tsens_attribute->range_min || *out_celsius > tsens->tsens_attribute->range_max) {
Expand All @@ -194,3 +225,86 @@ esp_err_t temperature_sensor_get_celsius(temperature_sensor_handle_t tsens, floa
}
return ESP_OK;
}

#if SOC_TEMPERATURE_SENSOR_INTR_SUPPORT

static uint8_t s_temperature_celsius_2_regval(temperature_sensor_handle_t tsens, int8_t celsius)
{
return (uint8_t)((celsius + TEMPERATURE_SENSOR_LL_OFFSET_FACTOR + TEMPERATURE_SENSOR_LL_DAC_FACTOR * tsens->tsens_attribute->offset)/TEMPERATURE_SENSOR_LL_ADC_FACTOR);
}

IRAM_ATTR static int8_t s_temperature_regval_2_celsius(temperature_sensor_handle_t tsens, uint8_t regval)
{
return TEMPERATURE_SENSOR_LL_ADC_FACTOR * regval - TEMPERATURE_SENSOR_LL_DAC_FACTOR * tsens->tsens_attribute->offset - TEMPERATURE_SENSOR_LL_OFFSET_FACTOR;
}

esp_err_t temperature_sensor_set_absolute_threshold(temperature_sensor_handle_t tsens, const temperature_sensor_abs_threshold_config_t *abs_cfg)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE((tsens != NULL), ESP_ERR_INVALID_ARG, TAG, "Temperature sensor has not been installed");
ESP_RETURN_ON_FALSE(tsens->fsm == TEMP_SENSOR_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "temperature sensor is not in init state");
ESP_RETURN_ON_FALSE(abs_cfg, ESP_ERR_INVALID_ARG, TAG, "Invalid callback configuration");

temperature_sensor_ll_set_sample_rate(0xffff);
temperature_sensor_ll_wakeup_mode(TEMPERATURE_SENSOR_LL_WAKE_ABSOLUTE);
temperature_sensor_ll_set_th_high_val(s_temperature_celsius_2_regval(tsens, abs_cfg->high_threshold));
temperature_sensor_ll_set_th_low_val(s_temperature_celsius_2_regval(tsens, abs_cfg->low_threshold));

return ret;
}

esp_err_t temperature_sensor_set_delta_threshold(temperature_sensor_handle_t tsens, const temperature_sensor_delta_threshold_config_t *delta_cfg)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE((tsens != NULL), ESP_ERR_INVALID_ARG, TAG, "Temperature sensor has not been installed");
ESP_RETURN_ON_FALSE(tsens->fsm == TEMP_SENSOR_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "temperature sensor is not in init state");
ESP_RETURN_ON_FALSE(delta_cfg, ESP_ERR_INVALID_ARG, TAG, "Invalid callback configuration");

temperature_sensor_ll_set_sample_rate(0xffff);
temperature_sensor_ll_wakeup_mode(TEMPERATURE_SENSOR_LL_WAKE_DELTA);
temperature_sensor_ll_set_th_high_val((uint8_t)(delta_cfg->increase_delta / TEMPERATURE_SENSOR_LL_ADC_FACTOR));
temperature_sensor_ll_set_th_low_val((uint8_t)(delta_cfg->decrease_delta / TEMPERATURE_SENSOR_LL_ADC_FACTOR));

return ret;
}

esp_err_t temperature_sensor_register_callbacks(temperature_sensor_handle_t tsens, const temperature_sensor_event_callbacks_t *cbs, void *user_arg)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE((tsens != NULL), ESP_ERR_INVALID_ARG, TAG, "Temperature sensor has not been installed");
ESP_RETURN_ON_FALSE(tsens->fsm == TEMP_SENSOR_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "temperature sensor is not in init state");
ESP_RETURN_ON_FALSE(cbs, ESP_ERR_INVALID_ARG, TAG, "callback group pointer is invalid");

#if CONFIG_TEMP_SENSOR_ISR_IRAM_SAFE
if (cbs->on_threshold) {
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_threshold), ESP_ERR_INVALID_ARG, TAG, "threshold callback not in IRAM");
}
if (user_arg) {
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_arg), ESP_ERR_INVALID_ARG, TAG, "user argument not in internal RAM");
}
#endif

int isr_flags = TEMPERATURE_SENSOR_INTR_ALLOC_FLAGS;
#if SOC_ADC_TEMPERATURE_SHARE_INTR
isr_flags |= ESP_INTR_FLAG_SHARED;
#endif

// lazy install interrupt service.
if (!tsens->temp_sensor_isr_handle) {
ret = esp_intr_alloc_intrstatus(ETS_APB_ADC_INTR_SOURCE, isr_flags,
(uint32_t)temperature_sensor_ll_get_intr_status(),
TEMPERATURE_SENSOR_LL_INTR_MASK, temperature_sensor_isr, tsens, &tsens->temp_sensor_isr_handle);
}

if (cbs->on_threshold != NULL) {
temperature_sensor_ll_enable_intr(true);
temperature_sensor_ll_clear_intr();
tsens->threshold_cbs = cbs->on_threshold;
tsens->cb_user_arg = user_arg;
} else {
temperature_sensor_ll_enable_intr(false);
}
return ret;
}

#endif // SOC_TEMPERATURE_SENSOR_INTR_SUPPORT
49 changes: 49 additions & 0 deletions components/driver/temperature_sensor/temperature_sensor_private.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <stdlib.h>
#include "soc/temperature_sensor_periph.h"
#include "hal/temperature_sensor_types.h"
#include "driver/temperature_sensor.h"
#include "esp_intr_alloc.h"
#include "sdkconfig.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef enum {
TEMP_SENSOR_FSM_INIT,
TEMP_SENSOR_FSM_ENABLE,
} temp_sensor_fsm_t;

#if CONFIG_TEMP_SENSOR_ISR_IRAM_SAFE
#define TEMPERATURE_SENSOR_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED)
#define TEMPERATURE_SENSOR_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define TEMPERATURE_SENSOR_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED)
#define TEMPERATURE_SENSOR_MEM_ALLOC_CAPS (MALLOC_CAP_DEFAULT)
#endif

typedef struct temperature_sensor_obj_t temperature_sensor_obj_t;

struct temperature_sensor_obj_t {
const temperature_sensor_attribute_t *tsens_attribute;
temp_sensor_fsm_t fsm;
temperature_sensor_clk_src_t clk_src;
#if SOC_TEMPERATURE_SENSOR_INTR_SUPPORT
intr_handle_t temp_sensor_isr_handle;
temperature_thres_cb_t threshold_cbs;
void *cb_user_arg;
#endif // SOC_TEMPERATURE_SENSOR_INTR_SUPPORT
};


#ifdef __cplusplus
}
#endif
Loading

0 comments on commit 679dae7

Please sign in to comment.