diff --git a/drivers/sensor/ams/CMakeLists.txt b/drivers/sensor/ams/CMakeLists.txt index 6561af6659ea9..f90034d80d970 100644 --- a/drivers/sensor/ams/CMakeLists.txt +++ b/drivers/sensor/ams/CMakeLists.txt @@ -2,6 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # zephyr-keep-sorted-start +add_subdirectory_ifdef(CONFIG_AMS_AS5048 ams_as5048) add_subdirectory_ifdef(CONFIG_AMS_AS5600 ams_as5600) add_subdirectory_ifdef(CONFIG_AMS_IAQ_CORE ams_iAQcore) add_subdirectory_ifdef(CONFIG_CCS811 ccs811) diff --git a/drivers/sensor/ams/Kconfig b/drivers/sensor/ams/Kconfig index 5aa40b944a81b..92df421a1bd78 100644 --- a/drivers/sensor/ams/Kconfig +++ b/drivers/sensor/ams/Kconfig @@ -2,6 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # zephyr-keep-sorted-start +source "drivers/sensor/ams/ams_as5048/Kconfig" source "drivers/sensor/ams/ams_as5600/Kconfig" source "drivers/sensor/ams/ams_iAQcore/Kconfig" source "drivers/sensor/ams/ccs811/Kconfig" diff --git a/drivers/sensor/ams/ams_as5048/CMakeLists.txt b/drivers/sensor/ams/ams_as5048/CMakeLists.txt new file mode 100644 index 0000000000000..25eb20db43d52 --- /dev/null +++ b/drivers/sensor/ams/ams_as5048/CMakeLists.txt @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources(ams_as5048.c) diff --git a/drivers/sensor/ams/ams_as5048/Kconfig b/drivers/sensor/ams/ams_as5048/Kconfig new file mode 100644 index 0000000000000..55b3d2c3b7c36 --- /dev/null +++ b/drivers/sensor/ams/ams_as5048/Kconfig @@ -0,0 +1,13 @@ +# AS5048 Angular position sensor configuration option + +# Copyright (c) 2025, Felipe Neves. +# SPDX-License-Identifier: Apache-2.0 + +config AMS_AS5048 + bool "AMS AS5048 magnetic angle sensor" + default y + depends on DT_HAS_AMS_AS5048_ENABLED + select SPI + help + Enable driver for the AMS AS5048 magnetic rotary encoder. The driver + supports reading the absolute angle via the SPI interface. diff --git a/drivers/sensor/ams/ams_as5048/ams_as5048.c b/drivers/sensor/ams/ams_as5048/ams_as5048.c new file mode 100644 index 0000000000000..b570318f132af --- /dev/null +++ b/drivers/sensor/ams/ams_as5048/ams_as5048.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2025, Felipe Neves + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ams_as5048 + +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(ams_as5048, CONFIG_SENSOR_LOG_LEVEL); + +#define AS5048_REG_ANGLE 0x3FFF +#define AS5048_READ_BIT BIT(14) +#define AS5048_PARITY_BIT BIT(15) +#define AS5048_DATA_MASK GENMASK(13, 0) +#define AS5048_ERROR_BIT BIT(14) +#define AS5048_MAX_STEPS 16384 +#define AS5048_FULL_ANGLE_DEG 360 +#define AS5048_MICRO_DEGREE 1000000 + +struct as5048_config { + struct spi_dt_spec spi; +}; + +struct as5048_data { + uint16_t angle_raw; +}; + +static uint16_t as5048_build_read_command(uint16_t reg) +{ + uint16_t cmd = reg & AS5048_DATA_MASK; + + cmd |= AS5048_READ_BIT; + + /* Even parity across bits 0..14 (read bit + address). */ + if (__builtin_parity(cmd)) { + cmd |= AS5048_PARITY_BIT; + } + + return cmd; +} + +static int as5048_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + struct as5048_data *data = dev->data; + const struct as5048_config *cfg = dev->config; + uint16_t tx_buf[2]; + uint16_t rx_buf[2]; + struct spi_buf tx[] = { + { + .buf = &tx_buf[0], + .len = sizeof(tx_buf[0]), + }, + { + .buf = &tx_buf[1], + .len = sizeof(tx_buf[1]), + }, + }; + + const struct spi_buf_set tx_set = { + .buffers = tx, + .count = ARRAY_SIZE(tx), + }; + + struct spi_buf rx[] = { + { + .buf = &rx_buf[0], + .len = sizeof(rx_buf[0]), + }, + { + .buf = &rx_buf[1], + .len = sizeof(rx_buf[1]), + }, + }; + + const struct spi_buf_set rx_set = { + .buffers = rx, + .count = ARRAY_SIZE(rx), + }; + + int ret; + + if ((chan != SENSOR_CHAN_ALL) && (chan != SENSOR_CHAN_ROTATION)) { + return -ENOTSUP; + } + + tx_buf[0] = sys_cpu_to_be16(as5048_build_read_command(AS5048_REG_ANGLE)); + tx_buf[1] = sys_cpu_to_be16(0x0000); + + ret = spi_transceive_dt(&cfg->spi, &tx_set, &rx_set); + if (ret < 0) { + LOG_ERR("spi transceive failed (%d)", ret); + return ret; + } + + uint16_t raw = sys_be16_to_cpu(rx_buf[1]); + + if (raw & AS5048_ERROR_BIT) { + LOG_ERR("AS5048 reported error (0x%04x)", raw); + return -EIO; + } + + data->angle_raw = raw & AS5048_DATA_MASK; + + return 0; +} + +static int as5048_channel_get(const struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + struct as5048_data *data = dev->data; + + if (chan != SENSOR_CHAN_ROTATION) { + return -ENOTSUP; + } + + int32_t scaled = (int32_t)data->angle_raw * AS5048_FULL_ANGLE_DEG; + + val->val1 = scaled / AS5048_MAX_STEPS; + val->val2 = ((scaled % AS5048_MAX_STEPS) * AS5048_MICRO_DEGREE) / AS5048_MAX_STEPS; + + return 0; +} + +static int as5048_init(const struct device *dev) +{ + const struct as5048_config *cfg = dev->config; + + if (!spi_is_ready_dt(&cfg->spi)) { + LOG_ERR("SPI device not ready"); + return -ENODEV; + } + + return 0; +} + +static DEVICE_API(sensor, as5048_api) = { + .sample_fetch = as5048_sample_fetch, + .channel_get = as5048_channel_get, +}; + +#define AS5048_INIT(inst) \ + static struct as5048_data as5048_data_##inst; \ + static const struct as5048_config as5048_config_##inst = { \ + .spi = SPI_DT_SPEC_INST_GET(inst, \ + SPI_WORD_SET(16) | SPI_TRANSFER_MSB), \ + }; \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, as5048_init, NULL, \ + &as5048_data_##inst, &as5048_config_##inst, \ + POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ + &as5048_api); + +DT_INST_FOREACH_STATUS_OKAY(AS5048_INIT) diff --git a/dts/bindings/sensor/ams,as5048.yaml b/dts/bindings/sensor/ams,as5048.yaml new file mode 100644 index 0000000000000..53d9778410fb2 --- /dev/null +++ b/dts/bindings/sensor/ams,as5048.yaml @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: Apache-2.0 + +description: | + AMS AS5048 magnetic rotary sensor + + &spi3 { + status = "okay"; + + as5048@0 { + compatible = "ams,as5048"; + reg = <0>; + spi-max-frequency = <4000000>; + }; + }; + + +compatible: "ams,as5048" + +include: [sensor-device.yaml, spi-device.yaml] + +properties: + reg: + required: true + + spi-max-frequency: + required: true diff --git a/tests/drivers/build_all/sensor/app.overlay b/tests/drivers/build_all/sensor/app.overlay index c1a5a3c1dacd1..dbd9c4894e464 100644 --- a/tests/drivers/build_all/sensor/app.overlay +++ b/tests/drivers/build_all/sensor/app.overlay @@ -156,7 +156,8 @@ <&test_gpio 0 0>, <&test_gpio 0 0>, <&test_gpio 0 0>, - <&test_gpio 0 0>; /* 0x39 */ + <&test_gpio 0 0>, + <&test_gpio 0 0>; /* 0x3A */ #include "spi.dtsi" }; diff --git a/tests/drivers/build_all/sensor/spi.dtsi b/tests/drivers/build_all/sensor/spi.dtsi index 1aa73779cf18c..2f22e7a09f179 100644 --- a/tests/drivers/build_all/sensor/spi.dtsi +++ b/tests/drivers/build_all/sensor/spi.dtsi @@ -489,3 +489,9 @@ test_spi_adxl345: adxl345@3a { int1-gpios = <&test_gpio 0 0>; fifo-watermark = <1>; }; + +test_spi_as5048: as5048@3b { + compatible = "ams,as5048"; + reg = <0x3b>; + spi-max-frequency = <0>; +};