diff --git a/Documentation/components/drivers/character/quadrature.rst b/Documentation/components/drivers/character/quadrature.rst index d19886e244dea..af4e926253dfe 100644 --- a/Documentation/components/drivers/character/quadrature.rst +++ b/Documentation/components/drivers/character/quadrature.rst @@ -2,6 +2,16 @@ Quadrature Encoder Drivers ========================== +A Quadrature Encoder (QE) is a kind of sensor normally used to read +angular rotation of a motor or other turning device. + +NuttX supports internal QE peripheral that exists in some microcontrollers +like ESP32, iMXRT, STM32, nRF5x, TIVA, and others, and also supports +Magnetic Rotary Encoders like AS5048, MT6816, etc. + +Internal Peripheral Quadrature Encoder +====================================== + NuttX supports a low-level, two-part Quadrature Encoder driver. #. An "upper half", generic driver that provides the common @@ -28,6 +38,46 @@ following locations: for the specific processor ```` and for the specific ```` Quadrature Encoder peripheral devices. +Magnetic Rotary Encoder +======================= + +Although technically a Magnetic Rotary Encoder is not a Quadrature Encoder, +usually uses the QE Lower Half driver to export a device compatible with +quadrature encoder. This way an application using an ordinary QE encoder +could use a Magnetic Rotary Encoder with any modification, just need to +enable and initialize the Magnetic Rotary Encoder on their board. + +This is how a board powered by STM32 will initialize a MT6816 Magnetic +Rotary Encoder: + +.. code-block:: c + + /* Initialize the SPI bus connected to MT6816 */ + + spi = stm32_spibus_initialize(spi_busno); + if (spi == NULL) + { + return -ENODEV; + } + + /* Initialize MT6816 using `spi` and a `device number` starting from 0 */ + + dev = mt6816_initialize(spi, (uint16_t) devno); + if (dev == NULL) + { + return -ENODEV; + } + + /* Use the returned qe lower half to register /dev/qe# (# => devno) */ + + ret = qe_register(qe_path, dev); + if (ret < 0) + { + snerr("ERROR: Failed to register MT6816 qe%d driver: %d\n", + devno, ret); + ret = -ENODEV; + } + Application Programming Interface ================================= diff --git a/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst b/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst index 1c2197aa35dca..aabfd73af54aa 100644 --- a/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst +++ b/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/index.rst @@ -1523,6 +1523,61 @@ After compiling and flashing the firmware in our board, run kbd command. code : 49 type : 1 +mt6816 +------ + +This board config enables the MagTek MT6816 Magnetic Rotary Encoder connected +to STM32F4Discovery board SPI1 this way: + +================ ====== +STM32F4Discovery MT6816 +================ ====== +3V [1] VCC +GND GND +PE3 CSN +SPI1 MOSI (PA7) MOSI +SPI1 MISO (PA6) MISO +SPI1 SCK (PA5) SCK +================ ====== + +1: You need to remove the diode D3 and short-circuit the PADs in the +board to get 3.3V. Be aware: although my board works fine, it could +damage something that expects 3V in our board (double check). + +IMPORTANT: You need to connect the HVPP (pin 2) to VCC in order to get +MT6816 working in SPI mode. Just short-circuit R3 pads will work: + +.. figure:: mt6816.png + :align: center + +After compiling and flashing the firmware in our board, run qe command: + +.. code:: console + + NuttShell (NSH) NuttX-12.13.0 + nsh> ls /dev + /dev: + console + null + qe0 + ttyS0 + zero + nsh> qe + qe_main: Hardware initialized. Opening the encoder device: /dev/qe0 + qe_main: Number of samples: 0 + qe_main: 1. 6546 + qe_main: 2. 6620 + qe_main: 3. 7384 + qe_main: 4. 7808 + qe_main: 5. 7900 + qe_main: 6. 7984 + qe_main: 7. 7989 + qe_main: 8. 7993 + qe_main: 9. 7998 + qe_main: 10. 8008 + qe_main: 11. 8052 + qe_main: 12. 8064 + netnsh ------ diff --git a/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/mt6816.png b/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/mt6816.png new file mode 100644 index 0000000000000..3a937cf7dff57 Binary files /dev/null and b/Documentation/platforms/arm/stm32f4/boards/stm32f4discovery/mt6816.png differ diff --git a/arch/arm/src/stm32/CMakeLists.txt b/arch/arm/src/stm32/CMakeLists.txt index e193c424b57f2..9727b096e3ba5 100644 --- a/arch/arm/src/stm32/CMakeLists.txt +++ b/arch/arm/src/stm32/CMakeLists.txt @@ -205,7 +205,9 @@ if(CONFIG_STM32_CAP) endif() if(CONFIG_SENSORS_QENCODER) - list(APPEND SRCS stm32_qencoder.c) + if(CONFIG_STM32_QE) + list(APPEND SRCS stm32_qencoder.c) + endif() endif() if(CONFIG_SENSORS_HALL3PHASE) diff --git a/arch/arm/src/stm32/Kconfig b/arch/arm/src/stm32/Kconfig index 67ec8612c9d84..d811530b5b4b6 100644 --- a/arch/arm/src/stm32/Kconfig +++ b/arch/arm/src/stm32/Kconfig @@ -12004,6 +12004,10 @@ config STM32_DMA2D_REGDEBUG endmenu endif # STM32_DMA2D +config STM32_QE + bool + default n + menu "STM32 QEncoder Driver" depends on SENSORS_QENCODER depends on STM32_TIM1 || STM32_TIM2 || STM32_TIM3 || STM32_TIM4 || STM32_TIM5 || STM32_TIM8 @@ -12020,6 +12024,7 @@ config STM32_TIM1_QE bool "TIM1 QE" default n depends on STM32_TIM1 + select STM32_QE ---help--- Reserve TIM1 for use by QEncoder. @@ -12038,6 +12043,7 @@ config STM32_TIM2_QE bool "TIM2 QE" default n depends on STM32_TIM2 + select STM32_QE ---help--- Reserve TIM2 for use by QEncoder. @@ -12056,6 +12062,7 @@ config STM32_TIM3_QE bool "TIM3 QE" default n depends on STM32_TIM3 + select STM32_QE ---help--- Reserve TIM3 for use by QEncoder. @@ -12074,6 +12081,7 @@ config STM32_TIM4_QE bool "TIM4 QE" default n depends on STM32_TIM4 + select STM32_QE ---help--- Reserve TIM4 for use by QEncoder. @@ -12092,6 +12100,7 @@ config STM32_TIM5_QE bool "TIM5 QE" default n depends on STM32_TIM5 + select STM32_QE ---help--- Reserve TIM5 for use by QEncoder. @@ -12110,6 +12119,7 @@ config STM32_TIM8_QE bool "TIM8 QE" default n depends on STM32_TIM8 + select STM32_QE ---help--- Reserve TIM8 for use by QEncoder. diff --git a/arch/arm/src/stm32/Make.defs b/arch/arm/src/stm32/Make.defs index fe8e29dfe333c..09f2cf8f3bd26 100644 --- a/arch/arm/src/stm32/Make.defs +++ b/arch/arm/src/stm32/Make.defs @@ -189,7 +189,9 @@ CHIP_CSRCS += stm32_capture_lowerhalf.c endif ifeq ($(CONFIG_SENSORS_QENCODER),y) -CHIP_CSRCS += stm32_qencoder.c + ifeq ($(CONFIG_STM32_QE),y) + CHIP_CSRCS += stm32_qencoder.c + endif endif ifeq ($(CONFIG_SENSORS_HALL3PHASE),y) diff --git a/boards/arm/stm32/common/include/stm32_mt6816.h b/boards/arm/stm32/common/include/stm32_mt6816.h new file mode 100644 index 0000000000000..2497d13aa9ad1 --- /dev/null +++ b/boards/arm/stm32/common/include/stm32_mt6816.h @@ -0,0 +1,75 @@ +/**************************************************************************** + * boards/arm/stm32/common/include/stm32_mt6816.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 __BOARDS_ARM_STM32_COMMON_INCLUDE_STM32_MT6816_H +#define __BOARDS_ARM_STM32_COMMON_INCLUDE_STM32_MT6816_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: board_mt6816_initialize + * + * Description: + * Initialize the MT6816 encoder driver + * + ****************************************************************************/ + +int board_mt6816_initialize(int devno, int spi_busno); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __BOARDS_ARM_STM32_COMMON_INCLUDE_STM32_MT6816_H */ diff --git a/boards/arm/stm32/common/src/CMakeLists.txt b/boards/arm/stm32/common/src/CMakeLists.txt index 6883bfaea511a..c7d9426af29e7 100644 --- a/boards/arm/stm32/common/src/CMakeLists.txt +++ b/boards/arm/stm32/common/src/CMakeLists.txt @@ -82,12 +82,18 @@ if(CONFIG_SENSORS_APDS9960) list(APPEND SRCS stm32_apds9960.c) endif() +if(CONFIG_SENSORS_MT6816) + list(APPEND SRCS stm32_mt6816.c) +endif() + if(CONFIG_SENSORS_ZEROCROSS) list(APPEND SRCS stm32_zerocross.c) endif() if(CONFIG_SENSORS_QENCODER) - list(APPEND SRCS stm32_qencoder.c) + if(CONFIG_STM32_QE) + list(APPEND SRCS stm32_qencoder.c) + endif() endif() if(CONFIG_SENSORS_INA219) diff --git a/boards/arm/stm32/common/src/Make.defs b/boards/arm/stm32/common/src/Make.defs index bacc31b1dcf33..5b1f8556d767f 100644 --- a/boards/arm/stm32/common/src/Make.defs +++ b/boards/arm/stm32/common/src/Make.defs @@ -86,6 +86,10 @@ ifeq ($(CONFIG_SENSORS_APDS9960),y) CSRCS += stm32_apds9960.c endif +ifeq ($(CONFIG_SENSORS_MT6816),y) + CSRCS += stm32_mt6816.c +endif + ifeq ($(CONFIG_INPUT_MPR121_KEYPAD),y) CSRCS += stm32_mpr121.c endif @@ -95,7 +99,9 @@ ifeq ($(CONFIG_SENSORS_ZEROCROSS),y) endif ifeq ($(CONFIG_SENSORS_QENCODER),y) - CSRCS += stm32_qencoder.c + ifeq ($(CONFIG_STM32_QE),y) + CSRCS += stm32_qencoder.c + endif endif ifeq ($(CONFIG_SENSORS_HALL3PHASE),y) diff --git a/boards/arm/stm32/common/src/stm32_mt6816.c b/boards/arm/stm32/common/src/stm32_mt6816.c new file mode 100644 index 0000000000000..834e120385036 --- /dev/null +++ b/boards/arm/stm32/common/src/stm32_mt6816.c @@ -0,0 +1,91 @@ +/**************************************************************************** + * boards/arm/stm32/common/src/stm32_mt6816.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 "chip.h" +#include "stm32.h" +#include "stm32_spi.h" +#include "arm_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_mt6816_initialize + * + * Description: + * Initialize the MT6816 encoder driver + * + ****************************************************************************/ + +int board_mt6816_initialize(int devno, int spi_busno) +{ + struct spi_dev_s *spi; + struct qe_lowerhalf_s *dev; + char qe_path[12]; + int ret = OK; + + spi = stm32_spibus_initialize(spi_busno); + if (!spi) + { + return -ENODEV; + } + + dev = mt6816_initialize(spi, (uint16_t) devno); + if (dev == NULL) + { + snerr("ERROR: Failed to initialize MT6816 at SPI%d\n", spi_busno); + ret = -ENODEV; + } + else + { + snprintf(qe_path, sizeof(qe_path), "/dev/qe%d", devno); + ret = qe_register(qe_path, dev); + if (ret < 0) + { + snerr("ERROR: Failed to register MT6816 qe%d driver: %d\n", + devno, ret); + ret = -ENODEV; + } + } + + return ret; +} diff --git a/boards/arm/stm32/stm32f4discovery/configs/mt6816/defconfig b/boards/arm/stm32/stm32f4discovery/configs/mt6816/defconfig new file mode 100644 index 0000000000000..722c5b12b9fbe --- /dev/null +++ b/boards/arm/stm32/stm32f4discovery/configs/mt6816/defconfig @@ -0,0 +1,56 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_FPU is not set +# CONFIG_DEBUG_ERROR is not set +# CONFIG_NSH_ARGCAT is not set +# CONFIG_NSH_CMDOPT_HEXDUMP is not set +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="stm32f4discovery" +CONFIG_ARCH_BOARD_COMMON=y +CONFIG_ARCH_BOARD_STM32F4_DISCOVERY=y +CONFIG_ARCH_BUTTONS=y +CONFIG_ARCH_CHIP="stm32" +CONFIG_ARCH_CHIP_STM32=y +CONFIG_ARCH_CHIP_STM32F407VG=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_BOARD_LATE_INITIALIZE=y +CONFIG_BOARD_LOOPSPERMSEC=16717 +CONFIG_BUILTIN=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_SENSORS=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_EXAMPLES_QENCODER=y +CONFIG_FS_PROCFS=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INTELHEX_BINARY=y +CONFIG_LINE_MAX=64 +CONFIG_MM_REGIONS=2 +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_READLINE=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_RAM_SIZE=114688 +CONFIG_RAM_START=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_WAITPID=y +CONFIG_SENSORS=y +CONFIG_SENSORS_MT6816=y +CONFIG_START_DAY=6 +CONFIG_START_MONTH=12 +CONFIG_START_YEAR=2011 +CONFIG_STM32_JTAG_SW_ENABLE=y +CONFIG_STM32_PWR=y +CONFIG_STM32_SPI1=y +CONFIG_STM32_USART2=y +CONFIG_SYSTEM_NSH=y +CONFIG_USART2_RXBUFSIZE=128 +CONFIG_USART2_SERIAL_CONSOLE=y +CONFIG_USART2_TXBUFSIZE=128 diff --git a/boards/arm/stm32/stm32f4discovery/include/board.h b/boards/arm/stm32/stm32f4discovery/include/board.h index 4f850570856aa..2b2d37418c40d 100644 --- a/boards/arm/stm32/stm32f4discovery/include/board.h +++ b/boards/arm/stm32/stm32f4discovery/include/board.h @@ -455,6 +455,11 @@ #define BOARD_MPR121_GPIO_INT GPIO_MPR121_INT +/* Pin for Magnetic Encoder MT6816 */ + +#define GPIO_CS_MT6816 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\ + GPIO_OUTPUT_SET|GPIO_PORTE|GPIO_PIN3) + /* LIS3DSH */ #define GPIO_LIS3DSH_EXT0 \ diff --git a/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c b/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c index a49ce53519d1e..272cc5206efc5 100644 --- a/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c +++ b/boards/arm/stm32/stm32f4discovery/src/stm32_bringup.c @@ -60,6 +60,10 @@ #include "stm32_apds9960.h" #endif +#ifdef CONFIG_SENSORS_MT6816 +#include "stm32_mt6816.h" +#endif + #ifdef CONFIG_INPUT_MPR121_KEYPAD #include "stm32_mpr121.h" #endif @@ -268,6 +272,16 @@ int stm32_bringup(void) board_zerocross_initialize(0); #endif +#ifdef CONFIG_SENSORS_MT6816 + /* Initialize MT6816 as /dev/qe0 on SPI1 */ + + ret = board_mt6816_initialize(0, 1); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: board_mt6816_initialize failed: %d\n", ret); + } +#endif + #ifdef CONFIG_LEDS_MAX7219 ret = stm32_max7219init("/dev/numdisp0"); if (ret < 0) @@ -476,7 +490,7 @@ int stm32_bringup(void) } #endif -#ifdef CONFIG_SENSORS_QENCODER +#if defined(CONFIG_STM32_QE) && defined(CONFIG_SENSORS_QENCODER) /* Initialize and register the qencoder driver */ ret = board_qencoder_initialize(0, CONFIG_STM32F4DISCO_QETIMER); diff --git a/boards/arm/stm32/stm32f4discovery/src/stm32_spi.c b/boards/arm/stm32/stm32f4discovery/src/stm32_spi.c index 3dad8ada29d23..a1026359b9485 100644 --- a/boards/arm/stm32/stm32f4discovery/src/stm32_spi.c +++ b/boards/arm/stm32/stm32f4discovery/src/stm32_spi.c @@ -77,6 +77,10 @@ void weak_function stm32_spidev_initialize(void) stm32_configgpio(GPIO_CS_MFRC522); /* MFRC522 chip select */ #endif +#if defined(CONFIG_STM32_SPI1) && defined(CONFIG_SENSORS_MT6816) + stm32_configgpio(GPIO_CS_MT6816); +#endif + #if defined(CONFIG_STM32_SPI2) && defined(CONFIG_SENSORS_MAX31855) stm32_configgpio(GPIO_MAX31855_CS); /* MAX31855 chip select */ #endif @@ -192,6 +196,13 @@ void stm32_spi1select(struct spi_dev_s *dev, uint32_t devid, } #endif +#if defined (CONFIG_SENSORS_MT6816) + if (devid == SPIDEV_MAG_ENCODER(0)) + { + stm32_gpiowrite(GPIO_CS_MT6816, !selected); + } +#endif + #if defined(CONFIG_CL_MFRC522) if (devid == SPIDEV_CONTACTLESS(0)) { diff --git a/drivers/sensors/CMakeLists.txt b/drivers/sensors/CMakeLists.txt index 7e9d91209e630..00b6a1df2ab84 100644 --- a/drivers/sensors/CMakeLists.txt +++ b/drivers/sensors/CMakeLists.txt @@ -107,6 +107,10 @@ if(CONFIG_SENSORS) list(APPEND SRCS ak09912.c) endif() + if(CONFIG_SENSORS_MT6816) + list(APPEND SRCS mt6816.c) + endif() + if(CONFIG_SENSORS_AS5048B) list(APPEND SRCS as5048b.c) endif() diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig index 587c75ee6353c..26d154e344c5c 100644 --- a/drivers/sensors/Kconfig +++ b/drivers/sensors/Kconfig @@ -143,6 +143,15 @@ config SENSORS_AK09912 ---help--- Enable driver for AK09911/AK09912 Compass sensor. +config SENSORS_MT6816 + bool "MagTek MT6816 Magnetic Angle Position support" + default n + select SPI + select SENSORS_QENCODER + ---help--- + Enable driver support for the MagTek MT6816 magnetic angle + position (magnetic rotary encoder). + config SENSORS_AS5048B bool "AMS AS5048B Magnetic Rotary Encoder support" default n diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs index cc8cf42605690..fa0d02b69c05c 100644 --- a/drivers/sensors/Make.defs +++ b/drivers/sensors/Make.defs @@ -388,6 +388,10 @@ ifeq ($(CONFIG_SENSORS_MPL115A),y) CSRCS += mpl115a.c endif +ifeq ($(CONFIG_SENSORS_MT6816),y) + CSRCS += mt6816.c +endif + ifeq ($(CONFIG_SENSORS_LIS3MDL),y) CSRCS += lis3mdl.c endif diff --git a/drivers/sensors/mt6816.c b/drivers/sensors/mt6816.c new file mode 100644 index 0000000000000..a69432ef28e95 --- /dev/null +++ b/drivers/sensors/mt6816.c @@ -0,0 +1,396 @@ +/**************************************************************************** + * drivers/sensors/mt6816.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 + +#if defined(CONFIG_SENSORS_MT6816) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MT6816_REG_ANGLE_LSB 0x03 +#define MT6816_REG_ANGLE_MSB 0x04 +#define MT6816_CMD_READ 0x80 +#define MT6816_NO_MAG_WARN 0x02 +#define MT6816_PARITY_CHECK 0x01 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct mt6816_dev_s +{ + struct qe_lowerhalf_s lower; /* MT6816 quadrature encoder lower half */ + FAR struct spi_dev_s *spi; /* SPI interface */ + + /* Since multiple MT6816 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 MT6816 chip via their chip select inputs. + */ + + int devid; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int mt6816_exchange(FAR struct mt6816_dev_s *priv, + uint8_t regaddr, FAR uint8_t *regval); +static uint16_t calc_even_parity(uint16_t value); +static int mt6816_readang(FAR struct mt6816_dev_s *priv, + FAR uint16_t *ang); + +/* Character Driver Methods */ + +static int mt6816_setup(FAR struct qe_lowerhalf_s *lower); +static int mt6816_shutdown(FAR struct qe_lowerhalf_s *lower); +static int mt6816_position(FAR struct qe_lowerhalf_s *lower, + FAR int32_t *pos); +static int mt6816_reset(FAR struct qe_lowerhalf_s *lower); +static int mt6816_ioctl(FAR struct qe_lowerhalf_s *lower, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct qe_ops_s g_qeops = +{ + mt6816_setup, /* setup */ + mt6816_shutdown, /* shutdown */ + mt6816_position, /* position */ + NULL, /* setposmax */ + mt6816_reset, /* reset */ + NULL, /* setindex */ + mt6816_ioctl /* ioctl */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mt6816_configspi + * + * Description: + * + ****************************************************************************/ + +static inline void mt6816_configspi(FAR struct spi_dev_s *spi) +{ + /* Configure SPI for the MT6816 */ + + SPI_SETMODE(spi, MT6816_SPI_MODE); + SPI_SETBITS(spi, 8); + SPI_HWFEATURES(spi, 0); + SPI_SETFREQUENCY(spi, MT6816_SPI_MAXFREQUENCY); +} + +/**************************************************************************** + * Name: mt6816_exchange + * + * Description: + * Read from 8-bit registers + * + ****************************************************************************/ + +static int mt6816_exchange(FAR struct mt6816_dev_s *priv, + uint8_t regaddr, FAR uint8_t *regval) +{ + uint8_t dat; + + /* If SPI bus is shared then lock and configure it */ + + SPI_LOCK(priv->spi, true); + mt6816_configspi(priv->spi); + + /* Select the MT6816 */ + + SPI_SELECT(priv->spi, SPIDEV_MAG_ENCODER(priv->devid), true); + + /* Send READ command. Received data is thrown away + * this data comes from the previous command (unknown) + */ + + dat = MT6816_CMD_READ | regaddr; + + /* Send register to read and get the next byte */ + + SPI_SEND(priv->spi, dat); + + /* Send NOP command. Received data is the value of regaddr + * from the previous command + */ + + dat = 0x00; /* NOP command */ + *regval = SPI_SEND(priv->spi, dat); + + /* Deselect the MT6816 */ + + SPI_SELECT(priv->spi, SPIDEV_MAG_ENCODER(priv->devid), false); + + /* Unlock bus */ + + SPI_LOCK(priv->spi, false); + + sninfo("addr: %02x value: %02x\n", regaddr, *regval); + return OK; +} + +/**************************************************************************** + * Name: calc_even_parity + * + * Description: + * get the even parity of input value + * + ****************************************************************************/ + +static uint16_t 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: mt6816_readang + * + * Description: + * Read from the angle position registers + * + ****************************************************************************/ + +static int mt6816_readang(FAR struct mt6816_dev_s *priv, + FAR uint16_t *ang) +{ + uint8_t hi; + uint8_t lo; + uint16_t value; + bool no_mag; + bool parity; + int ret; + + /* Read the high 8 bits of the 14-bit value */ + + ret = mt6816_exchange(priv, MT6816_REG_ANGLE_LSB, &hi); + if (ret < 0) + { + snerr("ERROR: mt6816_exchange failed: %d\n", ret); + return ret; + } + + /* Read the low 6 bits of the 14-bit value */ + + ret = mt6816_exchange(priv, MT6816_REG_ANGLE_MSB, &lo); + if (ret < 0) + { + snerr("ERROR: mt6816_exchange failed: %d\n", ret); + return ret; + } + + /* Read No Magnetic Error and Parity Check bits first */ + + no_mag = (lo & MT6816_NO_MAG_WARN) >> 1; + parity = (lo & MT6816_PARITY_CHECK); + + value = ((uint16_t)hi << 8 | (uint16_t)lo); + + /* Check if parity is right: + * - Clear the parity bit + * - Check if the calculated parity match with internal parity + */ + + value &= ~MT6816_PARITY_CHECK; + + if (calc_even_parity(value) == parity) + { + sninfo("Parity is correct!\n"); + } + else + { + snerr("Parity error: expected %d got %d", !parity, parity); + } + + /* Save only the angle value */ + + *ang = value >> 2; + + sninfo("angle: %04d, NO_MAG=%d, PC=%d ret: %d\n", + *ang, no_mag, parity, ret); + + return ret; +} + +/**************************************************************************** + * Name: mt6816_setup + * + * Description: + * This method is called when the driver is opened + * + ****************************************************************************/ + +static int mt6816_setup(FAR struct qe_lowerhalf_s *lower) +{ + return OK; +} + +/**************************************************************************** + * Name: mt6816_shutdown + * + * Description: + * This method is called when the driver is closed + * + ****************************************************************************/ + +static int mt6816_shutdown(FAR struct qe_lowerhalf_s *lower) +{ + return OK; +} + +/**************************************************************************** + * Name: mt6816_position + * + * Description: + * Return the current position measurement + * + ****************************************************************************/ + +static int mt6816_position(FAR struct qe_lowerhalf_s *lower, + FAR int32_t *pos) +{ + FAR struct mt6816_dev_s *priv = (FAR struct mt6816_dev_s *)lower; + uint16_t ang; + int ret; + + ret = mt6816_readang(priv, &ang); + if (ret < 0) + { + snerr("ERROR: mt6816_readang failed: %d\n", ret); + return ret; + } + + *pos = (int32_t)ang; + + return ret; +} + +/**************************************************************************** + * Name: mt6816_reset + * + * Description: + * Reset the position measurement to zero + * + ****************************************************************************/ + +static int mt6816_reset(FAR struct qe_lowerhalf_s *lower) +{ + /* Seems like the MT6816 doesn't have a SPI writable register + * to define the zero position. However it has a register to + * flash the zero position using its memory programming (MTP) + * but it requires supply high voltage (7V) to HVPP pin. + */ + + return OK; +} + +/**************************************************************************** + * Name: mt6816_ioctl + ****************************************************************************/ + +static int mt6816_ioctl(FAR struct qe_lowerhalf_s *lower, int cmd, + unsigned long arg) +{ + /* There is nothing we can('t) do! */ + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mt6816_initialize + * + * Description: + * Initialize the MT6816 device. + * + * Input Parameters: + * spi - An SPI driver instance. + * + * Returned Value: + * A new lower half encoder interface for the MT6816 on success; + * NULL on failure. + * + ****************************************************************************/ + +FAR struct qe_lowerhalf_s *mt6816_initialize(FAR struct spi_dev_s *spi, + uint16_t devid) +{ + FAR struct mt6816_dev_s *priv; + + DEBUGASSERT(spi != NULL); + + /* Initialize the device's structure */ + + priv = kmm_malloc(sizeof(*priv)); + if (priv == NULL) + { + snerr("ERROR: Failed to allocate instance\n"); + return NULL; + } + + priv->lower.ops = &g_qeops; + priv->spi = spi; + priv->devid = devid; + + return &priv->lower; +} + +#endif /* CONFIG_SENSORS_MT6816 */ diff --git a/include/nuttx/sensors/mt6816.h b/include/nuttx/sensors/mt6816.h new file mode 100644 index 0000000000000..d75c63ed5e681 --- /dev/null +++ b/include/nuttx/sensors/mt6816.h @@ -0,0 +1,102 @@ +/**************************************************************************** + * include/nuttx/sensors/mt6816.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_MT6816_H +#define __INCLUDE_NUTTX_SENSORS_MT6816_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#if defined(CONFIG_SENSORS_MT6816) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************ + * Prerequisites: + * + * CONFIG_SPI + * Enables support for SPI drivers + * CONFIG_SENSORS_MT6816 + * Enables support for the MT6816 driver + */ + +/* The device always operates in mode 3 */ + +#define MT6816_SPI_MODE SPIDEV_MODE3 /* Mode 3 */ + +/* SPI frequency */ + +#define MT6816_SPI_MAXFREQUENCY 1000000 /* 1MHz */ + +/* Resolution ***************************************************************/ + +#define MT6816_MAX 0x3fff /* Maximum value (14 bits) */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: mt6816_initialize + * + * Description: + * Initialize the MT6816 device. + * + * Input Parameters: + * spi - An SPI driver instance. + * + * Returned Value: + * A new lower half encoder interface for the MT6816 on success; + * NULL on failure. + * + ****************************************************************************/ + +FAR struct qe_lowerhalf_s *mt6816_initialize(FAR struct spi_dev_s *spi, + uint16_t devid); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_SENSORS_MT6816 */ +#endif /* __INCLUDE_NUTTX_SENSORS_MT6816_H */ diff --git a/include/nuttx/spi/spi.h b/include/nuttx/spi/spi.h index a8ff32c04d1e7..574d733fd4f95 100644 --- a/include/nuttx/spi/spi.h +++ b/include/nuttx/spi/spi.h @@ -478,6 +478,7 @@ #define SPIDEV_ADC(n) SPIDEV_ID(SPIDEVTYPE_ADC, (n)) #define SPIDEV_MOTOR(n) SPIDEV_ID(SPIDEVTYPE_MOTOR, (n)) #define SPIDEV_IMU(n) SPIDEV_ID(SPIDEVTYPE_IMU, (n)) +#define SPIDEV_MAG_ENCODER(n) SPIDEV_ID(SPIDEVTYPE_MAG_ENCODER, (n)) #define SPIDEV_USER(n) SPIDEV_ID(SPIDEVTYPE_USER, (n)) /**************************************************************************** @@ -519,6 +520,7 @@ enum spi_devtype_e SPIDEVTYPE_ADC, /* Select SPI ADC device */ SPIDEVTYPE_MOTOR, /* Select SPI motor device */ SPIDEVTYPE_IMU, /* Select SPI IMU device */ + SPIDEVTYPE_MAG_ENCODER, /* Select SPI Magnetic Encoder device */ SPIDEVTYPE_USER /* Board-specific values start here * This must always be the last definition. */ };