Skip to content

drivers: sensor: Add LPC internal temperature sensor #90418

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion drivers/adc/adc_mcux_lpadc.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,16 @@ static int mcux_lpadc_channel_setup(const struct device *dev,
}
} else if (channel_cfg->reference == ADC_REF_EXTERNAL0) {
LOG_DBG("ref external0");
# if defined(FSL_FEATURE_LPADC_HAS_INTERNAL_TEMP_SENSOR)
} else if (channel_cfg->reference == ADC_REF_INTERNAL) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use IS_ENABLED(FSL_FEATURE_LPADC_HAS_INTERNAL_TEMP_SENSOR) instead of #if

/*
* We are assuming that if the reference has been set to
* ADC_REF_INTERNAL that the channel is being used to sample the
* internal temperature sensor.
*/
LOG_DBG("ref internal");
cmd->loopCount = FSL_FEATURE_LPADC_TEMP_SENS_BUFFER_SIZE - 1U;
#endif
} else {
LOG_DBG("ref not support");
return -EINVAL;
Expand Down Expand Up @@ -477,8 +487,12 @@ static int mcux_lpadc_init(const struct device *dev)
lpadc_config_t adc_config;
int err;

/*
* pinctrl_apply_state() returns -ENOENT if pinctrl is not provided. This is
* expected if only using the internal channels.
*/
err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
if (err) {
if (err && err != -ENOENT) {
return err;
}

Expand Down
1 change: 1 addition & 0 deletions drivers/sensor/nxp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ add_subdirectory_ifdef(CONFIG_QDEC_NXP_S32 qdec_nxp_s32)
add_subdirectory_ifdef(CONFIG_QDEC_TPM qdec_tpm)
add_subdirectory_ifdef(CONFIG_SENSOR_MCUX_ACMP mcux_acmp)
add_subdirectory_ifdef(CONFIG_TEMP_KINETIS nxp_kinetis_temp)
add_subdirectory_ifdef(CONFIG_TEMP_LPC nxp_lpc_temp)
# zephyr-keep-sorted-stop
1 change: 1 addition & 0 deletions drivers/sensor/nxp/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ source "drivers/sensor/nxp/fxos8700/Kconfig"
source "drivers/sensor/nxp/mcux_acmp/Kconfig"
source "drivers/sensor/nxp/mcux_lpcmp/Kconfig"
source "drivers/sensor/nxp/nxp_kinetis_temp/Kconfig"
source "drivers/sensor/nxp/nxp_lpc_temp/Kconfig"
source "drivers/sensor/nxp/nxp_tempmon/Kconfig"
source "drivers/sensor/nxp/p3t1755/Kconfig"
source "drivers/sensor/nxp/qdec_mcux/Kconfig"
Expand Down
6 changes: 6 additions & 0 deletions drivers/sensor/nxp/nxp_lpc_temp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copyright (c) 2025 Nova Dynamics LLC
# SPDX-License-Identifier: Apache-2.0

zephyr_library()

zephyr_library_sources(temp_lpc.c)
9 changes: 9 additions & 0 deletions drivers/sensor/nxp/nxp_lpc_temp/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2025 Nova Dynamics LLC
# SPDX-License-Identifier: Apache-2.0

config TEMP_LPC
bool "NXP LPC Temperature Sensor"
default y
depends on ADC_MCUX_LPADC && DT_HAS_NXP_LPC_TEMPERATURE_ENABLED
help
Enable driver for NXP LPC internal temperature sensor.
142 changes: 142 additions & 0 deletions drivers/sensor/nxp/nxp_lpc_temp/temp_lpc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Copyright (c) 2025 Nova Dynamics LLC
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT nxp_lpc_temperature

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/adc.h>

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(temp_lpc, CONFIG_SENSOR_LOG_LEVEL);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does clang-format not sort all the includes? Why is the LOG_MODULE_REGISTER needed to be between the includes?


#include <fsl_lpadc.h>

BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) <= 1,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You already filtered building this file in CMake, shouldn't this be == 1?

"only one instance is supported");

struct temp_lpc_config {
const struct device *adc;
uint8_t sensor_adc_ch;
struct adc_sequence adc_seq;
const struct adc_channel_cfg adc_ch_cfg[];
};

struct temp_lpc_data {
uint16_t buffer[FSL_FEATURE_LPADC_TEMP_SENS_BUFFER_SIZE];
};

static struct temp_lpc_data inst_data;

static const struct temp_lpc_config inst_config = {
.adc = DEVICE_DT_GET(DT_INST_IO_CHANNELS_CTLR(0)),
.sensor_adc_ch = DT_INST_IO_CHANNELS_INPUT_BY_IDX(0, 0),
.adc_seq = {
.options = NULL,
.channels = BIT(DT_INST_IO_CHANNELS_INPUT_BY_IDX(0, 0)),
.buffer = &inst_data.buffer,
.buffer_size = sizeof(inst_data.buffer),
.resolution = 16,
.oversampling = 7,
.calibrate = false,
},
.adc_ch_cfg = {
DT_FOREACH_CHILD_SEP(DT_INST_IO_CHANNELS_CTLR(0), ADC_CHANNEL_CFG_DT, (,))
},
};

static int temp_lpc_sample_fetch(const struct device *dev,
enum sensor_channel chan)
{
const struct temp_lpc_config *config = dev->config;

if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_DIE_TEMP) {
return -ENOTSUP;
}

return adc_read(config->adc, &config->adc_seq);
}

static int temp_lpc_channel_get(const struct device *dev,
enum sensor_channel chan, struct sensor_value *val)
{
struct temp_lpc_data *data = dev->data;

uint16_t Vbe1;
uint16_t Vbe8;
float slope;
float offset;
float temperature;
uint32_t cal_slope;
uint32_t cal_offset;

const float alpha = FSL_FEATURE_LPADC_TEMP_PARAMETER_ALPHA;
const uintptr_t slope_addr = FSL_FEATURE_FLASH_NMPA_TEMP_SLOPE_ADDRS;
const uintptr_t offset_addr = FSL_FEATURE_FLASH_NMPA_TEMP_OFFSET_ADDRS;

if (chan != SENSOR_CHAN_VOLTAGE && chan != SENSOR_CHAN_DIE_TEMP) {
return -ENOTSUP;
}

/* Read the calibration from flash. */
cal_slope = (*((volatile uint32_t *)(slope_addr)));
cal_offset = (*((volatile uint32_t *)(offset_addr)));

if (((cal_slope & 0x1UL) != 0UL) && ((cal_offset & 0x1UL) != 0UL)) {
/* Re-justify based on the calibration values. */
slope = ((float)(uint32_t)(cal_slope >> 1UL) / 1024.0f);
offset = ((float)(uint32_t)(cal_offset >> 1UL) / 1024.0f);
} else {
/* Otherwise, use the default slope and offset value. */
slope = FSL_FEATURE_LPADC_TEMP_PARAMETER_A;
offset = FSL_FEATURE_LPADC_TEMP_PARAMETER_B;
}

/* Read the 2 temperature sensor result. */
#if (FSL_FEATURE_LPADC_TEMP_SENS_BUFFER_SIZE == 4U)
Vbe1 = data->buffer[2];
Vbe8 = data->buffer[3];
#else
Vbe1 = data->buffer[0];
Vbe8 = data->buffer[1];
#endif

/* T = A*[alpha*(Vbe8-Vbe1)/(Vbe8 + alpha*(Vbe8-Vbe1))] - B. */
temperature = slope * (alpha * ((float)Vbe8 - (float)Vbe1) /
((float)Vbe8 + alpha * ((float)Vbe8 - (float)Vbe1))) - offset;

return sensor_value_from_float(val, temperature);
}

static DEVICE_API(sensor, temp_lpc_driver_api) = {
.sample_fetch = temp_lpc_sample_fetch,
.channel_get = temp_lpc_channel_get,
};

static int temp_lpc_init(const struct device *dev)
{
const struct temp_lpc_config *config = dev->config;
const struct adc_channel_cfg *channels = config->adc_ch_cfg;
int ret;

if (!device_is_ready(config->adc)) {
LOG_ERR("ADC device is not ready");
return -EINVAL;
}

ret = adc_channel_setup(config->adc, &channels[config->sensor_adc_ch]);
if (ret) {
LOG_ERR("failed to configure ADC channel (%d)", ret);
return ret;
}

return 0;
}

SENSOR_DEVICE_DT_INST_DEFINE(0, temp_lpc_init, NULL, &inst_data,
&inst_config, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,
&temp_lpc_driver_api);
17 changes: 17 additions & 0 deletions dts/arm/nxp/nxp_lpc55S1x_common.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

#include <arm/armv8-m.dtsi>
#include <zephyr/dt-bindings/adc/adc.h>
#include <zephyr/dt-bindings/clock/mcux_lpc_syscon_clock.h>
#include <zephyr/dt-bindings/gpio/gpio.h>
#include <zephyr/dt-bindings/i2c/i2c.h>
Expand Down Expand Up @@ -319,6 +320,22 @@
status = "okay";
};

adc0: adc@a0000 {
compatible = "nxp,lpc-lpadc";
reg = <0xa0000 0x1000>;
interrupts = <22 0>;
status = "disabled";
clk-divider = <8>;
clk-source = <0>;
voltage-ref= <1>;
calibration-average = <128>;
power-level = <0>;
offset-value-a = <10>;
offset-value-b = <10>;
#io-channel-cells = <1>;
clocks = <&syscon MCUX_LPADC1_CLK>;
};

usbhs: usbhs@94000 {
compatible = "nxp,lpcip3511";
reg = <0x94000 0x1000>;
Expand Down
14 changes: 14 additions & 0 deletions dts/bindings/sensor/nxp,lpc-temperature.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (c) 2025 Nova Dynamics LLC
# SPDX-License-Identifier: Apache-2.0

description: NXP LPC temperature sensor

compatible: "nxp,lpc-temperature"

include: sensor-device.yaml

properties:
io-channels:
required: true
description: This should point to an ADC channel (e.g., <&adc0 0>). That
will be used for reading from the internal temperature sensor.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_TEMP_LPC=y
35 changes: 35 additions & 0 deletions samples/sensor/die_temp_polling/boards/lpcxpresso55s16.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2025 Nova Dynamics LLC
*/

#include <zephyr/dt-bindings/adc/mcux-lpadc.h>

/ {
aliases {
die-temp0 = &temp;
};

temp: temp {
status = "okay";
compatible = "nxp,lpc-temperature";
io-channels = <&adc0 0>;
};
};

&adc0 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;

channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_TICKS, 131)>;
zephyr,vref-mv = <1000>;
zephyr,input-positive = <26>;
zephyr,input-negative = <26>;
};
};