From 9f565d286ac20440427f70c2af4a8df75d8a912d Mon Sep 17 00:00:00 2001 From: Dheeraj Singh Date: Fri, 24 Apr 2026 16:42:46 +0530 Subject: [PATCH] drivers/sensors: Add AS5047D magnetic encoder driver This adds support for the AMS AS5047D SPI magnetic encoder. Features: - 14-bit angle read - SPI interface - basic error handling Tested on STM32F4 with SPI3. Signed-off-by: Dheeraj Singh --- .../drivers/character/quadrature.rst | 35 ++ .../stm32f4/boards/nucleo-f446re/index.rst | 30 ++ .../nucleo-f446re/configs/qenco/defconfig | 6 +- .../arm/stm32/nucleo-f446re/include/board.h | 32 +- .../stm32/nucleo-f446re/src/stm32_bringup.c | 41 +- .../arm/stm32/nucleo-f446re/src/stm32_spi.c | 4 + drivers/sensors/CMakeLists.txt | 4 + drivers/sensors/Kconfig | 8 + drivers/sensors/Make.defs | 4 + drivers/sensors/as5047d.c | 417 ++++++++++++++++++ include/nuttx/sensors/as5047d.h | 138 ++++++ include/nuttx/sensors/qencoder.h | 5 + 12 files changed, 716 insertions(+), 8 deletions(-) create mode 100644 drivers/sensors/as5047d.c create mode 100644 include/nuttx/sensors/as5047d.h diff --git a/Documentation/components/drivers/character/quadrature.rst b/Documentation/components/drivers/character/quadrature.rst index d19886e244dea..e3cd51ba5670b 100644 --- a/Documentation/components/drivers/character/quadrature.rst +++ b/Documentation/components/drivers/character/quadrature.rst @@ -78,6 +78,36 @@ This ioctl sets the index position of the encoder. An argument is an This ioctl gets the index position of the encoder. An argument is a pointer to ``qe_index_s`` structure. +AS5047D Specific Interface +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The AS5047D magnetic rotary encoder is supported through the quadrature +encoder upper-half driver by ``drivers/sensors/as5047d.c``. + +Include the AS5047D header for device-specific ioctls and bit definitions: + +.. code-block:: c + + #include + +The AS5047D implements additional ``ioctl`` commands: + + * :c:macro:`QEIOC_AS5047D_DIAGNOSTICS` + * :c:macro:`QEIOC_AS5047D_MAGNITUDE` + +.. c:macro:: QEIOC_AS5047D_DIAGNOSTICS + +Reads the AS5047D ``DIAAGC`` register. The argument is a pointer to +``uint16_t`` where the register value is returned. + +.. c:macro:: QEIOC_AS5047D_MAGNITUDE + +Reads the AS5047D ``MAG`` register. The argument is a pointer to +``uint16_t`` where the register value is returned. + +The position value returned by :c:macro:`QEIOC_POSITION` is a 14-bit +angle sample in the range ``0`` to ``16383``. + .. c:struct:: qe_index_s .. code-block:: c @@ -124,3 +154,8 @@ reader should refer to target documentation for target specific configuration. The ``CONFIG_SENSORS`` option has to be enabled in order to use the qencoder peripheral. The peripheral itself is enabled by ``CONFIG_SENSORS_QENCODER`` option. + +For AS5047D, enable these options: + +- ``CONFIG_SPI`` +- ``CONFIG_SENSORS_AS5047D`` diff --git a/Documentation/platforms/arm/stm32f4/boards/nucleo-f446re/index.rst b/Documentation/platforms/arm/stm32f4/boards/nucleo-f446re/index.rst index 5c898c5c71620..9baa3d8db0cff 100644 --- a/Documentation/platforms/arm/stm32f4/boards/nucleo-f446re/index.rst +++ b/Documentation/platforms/arm/stm32f4/boards/nucleo-f446re/index.rst @@ -408,6 +408,36 @@ testing purposes:: PB_6 - GPIO_OUTPUT PC_7 - GPIO_INPUT_INTERRUPT +as5047d +------- + +This is basically an ``nsh`` configuration (see above) with added support +for the AMS AS5047D magnetic rotary encoder using the quadrature encoder +framework. + +Board bring-up initializes AS5047D on SPI3 and registers it as +``/dev/qe0``. + +AS5047D connection (SPI3):: + + AS5047D Signal Nucleo-F446RE Pin + -------------- ----------------- + SCK PC10 + MISO PC11 + MOSI PC12 + CS PA15 (D8, GPIO_SPI3_CS_USER) + +Relevant configuration options:: + + CONFIG_SPI=y + CONFIG_STM32_SPI3=y + CONFIG_SENSORS=y + CONFIG_SENSORS_QENCODER=y + CONFIG_SENSORS_AS5047D=y + +You can verify encoder operation from NuttShell with the qencoder example +application (``qe``), which reads position data from ``/dev/qe0``. + ihm08m1_f32 and ihm08m1_b16 --------------------------- diff --git a/boards/arm/stm32/nucleo-f446re/configs/qenco/defconfig b/boards/arm/stm32/nucleo-f446re/configs/qenco/defconfig index fdf0171833183..bb49647fe2ca3 100644 --- a/boards/arm/stm32/nucleo-f446re/configs/qenco/defconfig +++ b/boards/arm/stm32/nucleo-f446re/configs/qenco/defconfig @@ -19,8 +19,7 @@ CONFIG_BOARD_LOOPSPERMSEC=8499 CONFIG_BUILTIN=y CONFIG_DEBUG_SYMBOLS=y CONFIG_EXAMPLES_QENCODER=y -CONFIG_EXAMPLES_QENCODER_HAVE_MAXPOS=y -CONFIG_EXAMPLES_QENCODER_MAXPOS=8192 +CONFIG_EXAMPLES_QENCODER_NSAMPLES=10 CONFIG_INIT_ENTRYPOINT="nsh_main" CONFIG_INTELHEX_BINARY=y CONFIG_MQ_MAXMSGSIZE=5 @@ -35,13 +34,14 @@ CONFIG_RAW_BINARY=y CONFIG_RR_INTERVAL=200 CONFIG_SCHED_WAITPID=y CONFIG_SENSORS=y -CONFIG_SENSORS_QENCODER=y +CONFIG_SENSORS_AS5047D=y CONFIG_START_DAY=14 CONFIG_START_MONTH=10 CONFIG_START_YEAR=2014 CONFIG_STM32_JTAG_SW_ENABLE=y CONFIG_STM32_QENCODER_DISABLE_EXTEND16BTIMERS=y CONFIG_STM32_QENCODER_SAMPLE_FDTS_2=y +CONFIG_STM32_SPI3=y CONFIG_STM32_TIM2=y CONFIG_STM32_TIM2_QE=y CONFIG_STM32_TIM2_QEPSC=0 diff --git a/boards/arm/stm32/nucleo-f446re/include/board.h b/boards/arm/stm32/nucleo-f446re/include/board.h index 27fc810d30c28..a07dbbda12f73 100644 --- a/boards/arm/stm32/nucleo-f446re/include/board.h +++ b/boards/arm/stm32/nucleo-f446re/include/board.h @@ -304,9 +304,35 @@ #define GPIO_SPI2_MOSI GPIO_SPI2_MOSI_1 #define GPIO_SPI2_SCK GPIO_SPI2_SCK_2 -#define GPIO_SPI3_MISO GPIO_SPI3_MISO_1 -#define GPIO_SPI3_MOSI GPIO_SPI3_MOSI_1 -#define GPIO_SPI3_SCK GPIO_SPI3_SCK_1 +/* SPI3 has two alternate pin mappings on this board: + * + * - LCD path uses PB3/PB4/PB5 + * - AS5047D path uses PC10/PC11/PC12 + * + * Select pinmux by feature so both use-cases are supported in-tree. + */ + +#if defined(CONFIG_LCD_ILI9225) && defined(CONFIG_SENSORS_AS5047D) +# error "LCD (ILI9225) and AS5047D require different SPI3 pin mappings" +#elif defined(CONFIG_SENSORS_AS5047D) +# define GPIO_SPI3_MISO GPIO_SPI3_MISO_2 +# define GPIO_SPI3_MOSI GPIO_SPI3_MOSI_2 +# define GPIO_SPI3_SCK GPIO_SPI3_SCK_2 +#else +# define GPIO_SPI3_MISO GPIO_SPI3_MISO_1 +# define GPIO_SPI3_MOSI GPIO_SPI3_MOSI_1 +# define GPIO_SPI3_SCK GPIO_SPI3_SCK_1 +#endif + +/* Encoder SPI CS from your reference: + * #define ENC_CS GPIOA, GPIO_PIN_15 + */ + +#define ENC_CS_PORT GPIO_PORTA +#define ENC_CS_PIN GPIO_PIN15 +#define GPIO_SPI3_CS_USER \ + (GPIO_OUTPUT | GPIO_PUSHPULL | GPIO_SPEED_50MHz | GPIO_OUTPUT_SET \ + | ENC_CS_PORT | ENC_CS_PIN) /* CAN */ diff --git a/boards/arm/stm32/nucleo-f446re/src/stm32_bringup.c b/boards/arm/stm32/nucleo-f446re/src/stm32_bringup.c index 114ff1ca7c518..8ca0570da05ee 100644 --- a/boards/arm/stm32/nucleo-f446re/src/stm32_bringup.c +++ b/boards/arm/stm32/nucleo-f446re/src/stm32_bringup.c @@ -60,6 +60,10 @@ # include #endif +#ifdef CONFIG_SENSORS_AS5047D +#include +#endif + #include "stm32_romfs.h" #include "nucleo-f446re.h" @@ -84,6 +88,10 @@ int stm32_bringup(void) { int ret = OK; +#if defined(CONFIG_SENSORS_QENCODER) && defined(CONFIG_SENSORS_AS5047D) + FAR struct spi_dev_s *spi; + FAR struct qe_lowerhalf_s *lower; +#endif #ifdef CONFIG_FS_PROCFS /* Mount the procfs file system */ @@ -197,16 +205,45 @@ int stm32_bringup(void) #endif #ifdef CONFIG_SENSORS_QENCODER - /* Initialize and register the qencoder driver */ +#ifdef CONFIG_SENSORS_AS5047D + /* Initialize and register the AS5047D qencoder driver */ + + spi = stm32_spibus_initialize(3); + if (spi == NULL) + { + syslog(LOG_ERR, "ERROR: Failed to initialize SPI3 for AS5047D\n"); + return -ENODEV; + } + +#ifdef GPIO_SPI3_CS_USER + stm32_configgpio(GPIO_SPI3_CS_USER); +#endif + + lower = as5047d_initialize(spi, SPIDEV_USER(0)); + if (lower == NULL) + { + syslog(LOG_ERR, "ERROR: as5047d_initialize() failed\n"); + return -ENODEV; + } + + ret = qe_register("/dev/qe0", lower); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: qe_register(/dev/qe0) failed: %d\n", ret); + return ret; + } +#else + /* Initialize and register the STM32 timer qencoder driver */ ret = board_qencoder_initialize(0, CONFIG_NUCLEO_F446RE_QETIMER); if (ret != OK) { syslog(LOG_ERR, - "ERROR: Failed to register the qencoder: %d\n", + "ERROR: Failed to register timer qencoder: %d\n", ret); return ret; } +# endif #endif #ifdef CONFIG_SENSORS_HALL3PHASE diff --git a/boards/arm/stm32/nucleo-f446re/src/stm32_spi.c b/boards/arm/stm32/nucleo-f446re/src/stm32_spi.c index e713d51ed0ca6..ac24072289ce5 100644 --- a/boards/arm/stm32/nucleo-f446re/src/stm32_spi.c +++ b/boards/arm/stm32/nucleo-f446re/src/stm32_spi.c @@ -175,6 +175,10 @@ void stm32_spi3select(struct spi_dev_s *dev, uint32_t devid, spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); +#ifdef GPIO_SPI3_CS_USER + stm32_gpiowrite(GPIO_SPI3_CS_USER, !selected); +#endif + #ifdef HAVE_LCD stm32_gpiowrite(GPIO_LCD_CS, !selected); #endif diff --git a/drivers/sensors/CMakeLists.txt b/drivers/sensors/CMakeLists.txt index 7e9d91209e630..0a1c1e64ed070 100644 --- a/drivers/sensors/CMakeLists.txt +++ b/drivers/sensors/CMakeLists.txt @@ -412,6 +412,10 @@ if(CONFIG_SENSORS) list(APPEND SRCS adt7320.c) endif() + if(CONFIG_SENSORS_AS5047D) + list(APPEND SRCS as5047d.c) + endif() + if(CONFIG_SENSORS_AS5048A) list(APPEND SRCS as5048a.c) endif() diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig index 587c75ee6353c..a3528a9849ce1 100644 --- a/drivers/sensors/Kconfig +++ b/drivers/sensors/Kconfig @@ -159,6 +159,14 @@ config SENSORS_AS5048A ---help--- Enable driver support for the AMS AS5048A magnetic rotary encoder. +config SENSORS_AS5047D + bool "AMS AS5047D Magnetic Rotary Encoder support" + default n + select SPI + select SENSORS_QENCODER + ---help--- + Enable driver support for the AMS AS5047D magnetic rotary encoder. + config SENSORS_AS726X bool "AMS AS726X Spetral sensor support" default n diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs index cc8cf42605690..b9c501a18b6fa 100644 --- a/drivers/sensors/Make.defs +++ b/drivers/sensors/Make.defs @@ -408,6 +408,10 @@ ifeq ($(CONFIG_SENSORS_AS5048A),y) CSRCS += as5048a.c endif +ifeq ($(CONFIG_SENSORS_AS5047D),y) + CSRCS += as5047d.c +endif + endif # CONFIG_SPI # These drivers depend on 1WIRE support diff --git a/drivers/sensors/as5047d.c b/drivers/sensors/as5047d.c new file mode 100644 index 0000000000000..d656aa2fdba5c --- /dev/null +++ b/drivers/sensors/as5047d.c @@ -0,0 +1,417 @@ +/**************************************************************************** + * drivers/sensors/as5047d.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(CONFIG_SENSORS_AS5047D) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct as5047d_dev_s +{ + struct qe_lowerhalf_s lower; /* AS5047D quadrature encoder lower half */ + FAR struct spi_dev_s *spi; /* SPI interface */ + + /* Since multiple AS5047D can be connected to the same SPI bus we need + * to use multiple spi device ids which are employed by NuttX to select/ + * deselect the desired AS5047D chip via their chip select inputs. + */ + + int spi_devid; + + /* AS5047D returns the response of the previous frame. Track which + * register is currently staged in the pipeline. + */ + + bool pipeline_valid; + uint16_t pipeline_reg; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int as5047d_exchange(FAR struct as5047d_dev_s *priv, + uint16_t regaddr, FAR uint16_t *regval); +static uint16_t as5047d_calc_even_parity(uint16_t value); +static int as5047d_readang(FAR struct as5047d_dev_s *priv, + FAR uint16_t *ang); +static int as5047d_readmag(FAR struct as5047d_dev_s *priv, + FAR uint16_t *mag); +static int as5047d_readdiag(FAR struct as5047d_dev_s *priv, + FAR uint16_t *diag); + +/* Character Driver Methods */ + +static int as5047d_setup(FAR struct qe_lowerhalf_s *lower); +static int as5047d_shutdown(FAR struct qe_lowerhalf_s *lower); +static int as5047d_position(FAR struct qe_lowerhalf_s *lower, + FAR int32_t *pos); +static int as5047d_reset(FAR struct qe_lowerhalf_s *lower); +static int as5047d_ioctl(FAR struct qe_lowerhalf_s *lower, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct qe_ops_s g_qeops = +{ + as5047d_setup, /* setup */ + as5047d_shutdown, /* shutdown */ + as5047d_position, /* position */ + NULL, /* setposmax */ + as5047d_reset, /* reset */ + NULL, /* setindex */ + as5047d_ioctl /* ioctl */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: as5047d_configspi + * + * Description: + * Configure SPI for the AS5047D. + * + ****************************************************************************/ + +static inline void as5047d_configspi(FAR struct spi_dev_s *spi) +{ + SPI_SETMODE(spi, AS5047D_SPI_MODE); + SPI_SETBITS(spi, 16); + SPI_HWFEATURES(spi, 0); + SPI_SETFREQUENCY(spi, AS5047D_SPI_MAXFREQUENCY); +} + +/**************************************************************************** + * Name: as5047d_calc_even_parity + * + * Description: + * Get the even parity of input value. + * + ****************************************************************************/ + +static uint16_t as5047d_calc_even_parity(uint16_t value) +{ + uint16_t cnt = 0; + uint8_t i; + + for (i = 0; i < 16; i++) + { + if (value & 0x1) + { + cnt++; + } + + value >>= 1; + } + + return cnt & 0x1; +} + +/**************************************************************************** + * Name: as5047d_exchange + * + * Description: + * Read from 16-bit registers. + * + ****************************************************************************/ + +static int as5047d_exchange(FAR struct as5047d_dev_s *priv, + uint16_t regaddr, FAR uint16_t *regval) +{ + uint16_t cmd; + uint16_t tx; + uint16_t rx; + uint16_t sample; + + DEBUGASSERT(priv != NULL && regval != NULL); + + SPI_LOCK(priv->spi, true); + as5047d_configspi(priv->spi); + + cmd = AS5047D_CMD_READ | (regaddr & AS5047D_VALUE_MASK); + cmd |= as5047d_calc_even_parity(cmd) << 15; + + /* AS5047D expects framed 16-bit transfers. Keep the SPI lock but toggle + * CS for each command/response frame. + */ + + SPI_SELECT(priv->spi, priv->spi_devid, true); + tx = cmd; + SPI_EXCHANGE(priv->spi, &tx, &rx, 1); + SPI_SELECT(priv->spi, priv->spi_devid, false); + + if (priv->pipeline_valid && priv->pipeline_reg == regaddr) + { + sample = rx; + } + else + { + /* Pipeline is empty or points to a different register. Send one more + * READ frame so received data corresponds to regaddr. + */ + + SPI_SELECT(priv->spi, priv->spi_devid, true); + tx = cmd; + SPI_EXCHANGE(priv->spi, &tx, &sample, 1); + SPI_SELECT(priv->spi, priv->spi_devid, false); + } + + priv->pipeline_valid = true; + priv->pipeline_reg = regaddr; + + SPI_LOCK(priv->spi, false); + + if ((sample & AS5047D_FLAG_ERR) != 0) + { + snerr("ERROR: as5047d error flag set: %04x reg: %04x\n", + sample, regaddr); + return -EIO; + } + + *regval = sample & AS5047D_VALUE_MASK; + return OK; +} + +/**************************************************************************** + * Name: as5047d_readang + ****************************************************************************/ + +static int as5047d_readang(FAR struct as5047d_dev_s *priv, FAR uint16_t *ang) +{ + int ret; + + ret = as5047d_exchange(priv, AS5047D_REG_ANGLEUNC, ang); + if (ret < 0) + { + snerr("ERROR: as5047d_exchange failed: %d\n", ret); + return ret; + } + + return ret; +} + +/**************************************************************************** + * Name: as5047d_readmag + ****************************************************************************/ + +static int as5047d_readmag(FAR struct as5047d_dev_s *priv, FAR uint16_t *mag) +{ + int ret; + + ret = as5047d_exchange(priv, AS5047D_REG_MAG, mag); + if (ret < 0) + { + snerr("ERROR: as5047d_exchange failed: %d\n", ret); + return ret; + } + + return ret; +} + +/**************************************************************************** + * Name: as5047d_readdiag + ****************************************************************************/ + +static int as5047d_readdiag(FAR struct as5047d_dev_s *priv, + FAR uint16_t *diag) +{ + int ret; + + ret = as5047d_exchange(priv, AS5047D_REG_DIAAGC, diag); + if (ret < 0) + { + snerr("ERROR: as5047d_exchange failed: %d\n", ret); + return ret; + } + + return ret; +} + +/**************************************************************************** + * Name: as5047d_setup + ****************************************************************************/ + +static int as5047d_setup(FAR struct qe_lowerhalf_s *lower) +{ + return OK; +} + +/**************************************************************************** + * Name: as5047d_shutdown + ****************************************************************************/ + +static int as5047d_shutdown(FAR struct qe_lowerhalf_s *lower) +{ + return OK; +} + +/**************************************************************************** + * Name: as5047d_position + * + * Description: + * Return the current position measurement relative to reset position. + * + ****************************************************************************/ + +static int as5047d_position(FAR struct qe_lowerhalf_s *lower, + FAR int32_t *pos) +{ + FAR struct as5047d_dev_s *priv = (FAR struct as5047d_dev_s *)lower; + uint16_t rawang; + int ret; + + ret = as5047d_readang(priv, &rawang); + if (ret < 0) + { + snerr("ERROR: as5047d_readang failed: %d\n", ret); + return ret; + } + + *pos = (int32_t)(rawang & AS5047D_VALUE_MASK); + return ret; +} + +/**************************************************************************** + * Name: as5047d_reset + * + * Description: + * Reset the position measurement to zero. + * + ****************************************************************************/ + +static int as5047d_reset(FAR struct qe_lowerhalf_s *lower) +{ + FAR struct as5047d_dev_s *priv = (FAR struct as5047d_dev_s *)lower; + uint16_t rawang; + int ret; + + ret = as5047d_readang(priv, &rawang); + if (ret < 0) + { + snerr("ERROR: as5047d_readang failed: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: as5047d_ioctl + ****************************************************************************/ + +static int as5047d_ioctl(FAR struct qe_lowerhalf_s *lower, int cmd, + unsigned long arg) +{ + FAR struct as5047d_dev_s *priv = (FAR struct as5047d_dev_s *)lower; + int ret = OK; + + switch (cmd) + { + case QEIOC_AS5047D_DIAGNOSTICS: + { + FAR uint16_t *ptr = (FAR uint16_t *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + ret = as5047d_readdiag(priv, ptr); + } + break; + + case QEIOC_AS5047D_MAGNITUDE: + { + FAR uint16_t *ptr = (FAR uint16_t *)((uintptr_t)arg); + DEBUGASSERT(ptr != NULL); + ret = as5047d_readmag(priv, ptr); + } + break; + + default: + snerr("ERROR: Unrecognized cmd: %d arg: %ld\n", cmd, arg); + ret = -ENOTTY; + break; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: as5047d_initialize + * + * Description: + * Initialize the AS5047D device. + * + * Input Parameters: + * spi - An SPI driver instance. + * + * Returned Value: + * A new lower half encoder interface for the AS5047D on success; + * NULL on failure. + * + ****************************************************************************/ + +FAR struct qe_lowerhalf_s *as5047d_initialize(FAR struct spi_dev_s *spi, + int spi_devid) +{ + FAR struct as5047d_dev_s *priv; + + DEBUGASSERT(spi != NULL); + + priv = kmm_zalloc(sizeof(*priv)); + if (priv == NULL) + { + snerr("ERROR: Failed to allocate instance\n"); + return NULL; + } + + priv->lower.ops = &g_qeops; + priv->spi = spi; + priv->spi_devid = spi_devid; + + return &priv->lower; +} + +#endif /* CONFIG_SENSORS_AS5047D */ diff --git a/include/nuttx/sensors/as5047d.h b/include/nuttx/sensors/as5047d.h new file mode 100644 index 0000000000000..b97c2148c5d27 --- /dev/null +++ b/include/nuttx/sensors/as5047d.h @@ -0,0 +1,138 @@ +/**************************************************************************** + * include/nuttx/sensors/as5047d.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_SENSORS_AS5047D_H +#define __INCLUDE_NUTTX_SENSORS_AS5047D_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#if defined(CONFIG_SENSORS_AS5047D) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************ + * Prerequisites: + * + * CONFIG_SPI + * Enables support for SPI drivers + * CONFIG_SENSORS_AS5047D + * Enables support for the AS5047D driver + */ + +/* The device always operates in mode 1 */ + +#define AS5047D_SPI_MODE SPIDEV_MODE1 /* Mode 1 */ + +/* SPI frequency */ + +#define AS5047D_SPI_MAXFREQUENCY 1000000 /* 1MHz */ + +/* IOCTL Commands ***********************************************************/ + +/* Arg: uint16_t* pointer */ + +#define QEIOC_AS5047D_DIAGNOSTICS _QEIOC(QE_AS5047D_FIRST + 0) + +/* Arg: uint16_t* pointer */ + +#define QEIOC_AS5047D_MAGNITUDE _QEIOC(QE_AS5047D_FIRST + 1) + +/* Resolution ***************************************************************/ + +#define AS5047D_RESOLUTION 16384 /* Resolution (14 bits) */ +#define AS5047D_VALUE_MASK 0x3fff /* Maximum value (14 bits) */ + +/* Register Definitions *****************************************************/ + +/* Register Addresses */ + +#define AS5047D_CMD_READ 0x4000 /* Flag indicating read attempt */ +#define AS5047D_CMD_WRITE 0x0000 /* Flag indicating write attempt */ +#define AS5047D_FLAG_ERR 0x4000 /* Error flag in the response */ +#define AS5047D_REG_NOP 0x0000 /* NOP Register */ +#define AS5047D_REG_ERRFL 0x0001 /* Error Register */ +#define AS5047D_REG_PROG 0x0003 /* Programming Register */ +#define AS5047D_REG_DIAAGC 0x3ffc /* Diagnostics/AGC Register */ +#define AS5047D_REG_MAG 0x3ffd /* Magnitude Register */ +#define AS5047D_REG_ANGLEUNC 0x3ffe /* Uncompensated Angle Register */ +#define AS5047D_REG_ANGLE 0x3fff /* Angle Register */ + +/* Error Flags (ERRFL register bits) */ + +#define AS5047D_ERR_PARITY (1 << 2) +#define AS5047D_ERR_INV_CMD (1 << 1) +#define AS5047D_ERR_FRAMING (1 << 0) + +/* DIAAGC Register Bits */ + +#define AS5047D_DIAAGC_MAGL (1 << 11) +#define AS5047D_DIAAGC_MAGH (1 << 10) +#define AS5047D_DIAAGC_COF (1 << 9) +#define AS5047D_DIAAGC_OCF (1 << 8) + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: as5047d_initialize + * + * Description: + * Initialize the AS5047D device. + * + * Input Parameters: + * spi - An SPI driver instance. + * spi_devid - The SPI device ID used by the board specific logic. + * + * Returned Value: + * A new lower half encoder interface for the AS5047D on success; + * NULL on failure. + * + ****************************************************************************/ + +FAR struct qe_lowerhalf_s *as5047d_initialize(FAR struct spi_dev_s *spi, + int spi_devid); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_SENSORS_AS5047D */ +#endif /* __INCLUDE_NUTTX_SENSORS_AS5047D_H */ diff --git a/include/nuttx/sensors/qencoder.h b/include/nuttx/sensors/qencoder.h index 6c78d224eecc2..4bb40f345e3d4 100644 --- a/include/nuttx/sensors/qencoder.h +++ b/include/nuttx/sensors/qencoder.h @@ -98,6 +98,11 @@ #define QE_AS5048A_FIRST (QE_IMXRT_FIRST + QE_IMXRT_NCMDS) #define QE_AS5048A_NCMDS 4 +/* See include/nuttx/sensors/as5047d.h */ + +#define QE_AS5047D_FIRST (QE_AS5048A_FIRST + QE_AS5048A_NCMDS) +#define QE_AS5047D_NCMDS 2 + /**************************************************************************** * Public Types ****************************************************************************/