This is an automated email from the ASF dual-hosted git repository. linguini pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 5bacc302eb3880ffc485ab1aa66c31f927d0b44b Author: raiden00pl <[email protected]> AuthorDate: Sat Apr 11 11:52:48 2026 +0200 arch/stm32f0l0g0: add qencoder support add QENCODER support for STM32F0/L0/G0/C0 Signed-off-by: raiden00pl <[email protected]> --- arch/arm/src/stm32f0l0g0/CMakeLists.txt | 4 + arch/arm/src/stm32f0l0g0/Kconfig | 152 +++ arch/arm/src/stm32f0l0g0/Make.defs | 4 + arch/arm/src/stm32f0l0g0/hardware/stm32c0_pinmap.h | 254 +++- arch/arm/src/stm32f0l0g0/stm32_qencoder.c | 1254 ++++++++++++++++++++ arch/arm/src/stm32f0l0g0/stm32_qencoder.h | 116 ++ 6 files changed, 1764 insertions(+), 20 deletions(-) diff --git a/arch/arm/src/stm32f0l0g0/CMakeLists.txt b/arch/arm/src/stm32f0l0g0/CMakeLists.txt index 2d0b6d9cb53..b2cf874c32e 100644 --- a/arch/arm/src/stm32f0l0g0/CMakeLists.txt +++ b/arch/arm/src/stm32f0l0g0/CMakeLists.txt @@ -124,4 +124,8 @@ if(CONFIG_STM32F0L0G0_FDCAN) endif() endif() +if(CONFIG_SENSORS_QENCODER) + list(APPEND SRCS stm32_qencoder.c) +endif() + target_sources(arch PRIVATE ${SRCS}) diff --git a/arch/arm/src/stm32f0l0g0/Kconfig b/arch/arm/src/stm32f0l0g0/Kconfig index 823254a704c..b4b529d90a1 100644 --- a/arch/arm/src/stm32f0l0g0/Kconfig +++ b/arch/arm/src/stm32f0l0g0/Kconfig @@ -2386,6 +2386,13 @@ endif # !STM32F0L0G0_PWM_MULTICHAN endif # STM32F0L0G0_TIM1_PWM +config STM32F0L0G0_TIM1_QE + bool "TIM1 Quadrature Encoder" + default n + depends on STM32F0L0G0_TIM1 + ---help--- + Reserve TIM1 for use by Quadrature Encoder. + config STM32F0L0G0_TIM2_PWM bool "TIM2 PWM" default n @@ -2540,6 +2547,13 @@ endif # !STM32F0L0G0_PWM_MULTICHAN endif # STM32F0L0G0_TIM2_PWM +config STM32F0L0G0_TIM2_QE + bool "TIM2 Quadrature Encoder" + default n + depends on STM32F0L0G0_TIM2 + ---help--- + Reserve TIM2 for use by Quadrature Encoder. + config STM32F0L0G0_TIM3_PWM bool "TIM3 PWM" default n @@ -2694,6 +2708,144 @@ endif # !STM32F0L0G0_PWM_MULTICHAN endif # STM32F0L0G0_TIM3_PWM +config STM32F0L0G0_TIM3_QE + bool "TIM3 Quadrature Encoder" + default n + depends on STM32F0L0G0_TIM3 + ---help--- + Reserve TIM3 for use by Quadrature Encoder. + +config STM32F0L0G0_TIM4_QE + bool "TIM3 Quadrature Encoder" + default n + depends on STM32F0L0G0_TIM4 + ---help--- + Reserve TIM4 for use by Quadrature Encoder. + +menu "STM32F0L0G0 QEncoder Driver" + depends on SENSORS_QENCODER + depends on STM32F0L0G0_TIM1 || STM32F0L0G0_TIM2 || STM32F0L0G0_TIM3 || STM32F0L0G0_TIM4 + +config STM32F0L0G0_TIM1_QEPSC + int "TIM1 QE pulse prescaler" + default 1 + depends on STM32F0L0G0_TIM1_QE + ---help--- + This prescaler divides the number of recorded encoder pulses, + limiting the count rate at the expense of resolution. + +config STM32F0L0G0_TIM2_QEPSC + int "TIM2 QE pulse prescaler" + default 1 + depends on STM32F0L0G0_TIM2_QE + ---help--- + This prescaler divides the number of recorded encoder pulses, + limiting the count rate at the expense of resolution. + +config STM32F0L0G0_TIM3_QEPSC + int "TIM3 QE pulse prescaler" + default 1 + depends on STM32F0L0G0_TIM3_QE + ---help--- + This prescaler divides the number of recorded encoder pulses, + limiting the count rate at the expense of resolution. + +config STM32F0L0G0_TIM4_QEPSC + int "TIM3 QE pulse prescaler" + default 1 + depends on STM32F0L0G0_TIM4_QE + ---help--- + This prescaler divides the number of recorded encoder pulses, + limiting the count rate at the expense of resolution. + +config STM32F0L0G0_QENCODER_DISABLE_EXTEND16BTIMERS + bool "Disable QEncoder timers extension from 16-bit to 32-bit" + default n + ---help--- + Disable the extension of 16-bit timers to 32-bit via interrupt-based + overflow tracking. When enabled, timers will use their native hardware + counter width (16-bit or 32-bit). This reduces interrupt overhead but + limits the position range for 16-bit timers. + +config STM32F0L0G0_QENCODER_INDEX_PIN + bool "Enable QEncoder timers support for index pin" + default n + ---help--- + Enable support for quadrature encoder index pin. The index pin can be + used to reset the encoder position to a known value when the index + pulse is detected. + +config STM32F0L0G0_QENCODER_FILTER + bool "Enable filtering on STM32F0L0G0 QEncoder input" + default y + ---help--- + Enable input filtering on quadrature encoder channels to reduce noise. + +choice + depends on STM32F0L0G0_QENCODER_FILTER + prompt "Input channel sampling frequency" + default STM32F0L0G0_QENCODER_SAMPLE_FDTS_4 + ---help--- + Select the sampling frequency for the input filter. + +config STM32F0L0G0_QENCODER_SAMPLE_FDTS + bool "fDTS" + +config STM32F0L0G0_QENCODER_SAMPLE_CKINT + bool "fCK_INT" + +config STM32F0L0G0_QENCODER_SAMPLE_FDTS_2 + bool "fDTS/2" + +config STM32F0L0G0_QENCODER_SAMPLE_FDTS_4 + bool "fDTS/4" + +config STM32F0L0G0_QENCODER_SAMPLE_FDTS_8 + bool "fDTS/8" + +config STM32F0L0G0_QENCODER_SAMPLE_FDTS_16 + bool "fDTS/16" + +config STM32F0L0G0_QENCODER_SAMPLE_FDTS_32 + bool "fDTS/32" + +endchoice + +choice + depends on STM32F0L0G0_QENCODER_FILTER + prompt "Input channel event count" + default STM32F0L0G0_QENCODER_SAMPLE_EVENT_6 + ---help--- + Select the number of consecutive events required to validate a transition. + +config STM32F0L0G0_QENCODER_SAMPLE_EVENT_1 + depends on STM32F0L0G0_QENCODER_SAMPLE_FDTS + bool "1" + +config STM32F0L0G0_QENCODER_SAMPLE_EVENT_2 + depends on STM32F0L0G0_QENCODER_SAMPLE_CKINT + bool "2" + +config STM32F0L0G0_QENCODER_SAMPLE_EVENT_4 + depends on STM32F0L0G0_QENCODER_SAMPLE_CKINT + bool "4" + +config STM32F0L0G0_QENCODER_SAMPLE_EVENT_5 + depends on STM32F0L0G0_QENCODER_SAMPLE_FDTS_16 || STM32F0L0G0_QENCODER_SAMPLE_FDTS_32 + bool "5" + +config STM32F0L0G0_QENCODER_SAMPLE_EVENT_6 + depends on !STM32F0L0G0_QENCODER_SAMPLE_FDTS && !STM32F0L0G0_QENCODER_SAMPLE_CKINT + bool "6" + +config STM32F0L0G0_QENCODER_SAMPLE_EVENT_8 + depends on !STM32F0L0G0_QENCODER_SAMPLE_FDTS + bool "8" + +endchoice + +endmenu # STM32F0L0G0 QEncoder Driver + config STM32F0L0G0_TIM14_PWM bool "TIM14 PWM" default n diff --git a/arch/arm/src/stm32f0l0g0/Make.defs b/arch/arm/src/stm32f0l0g0/Make.defs index 3b73b01180d..df47027944c 100644 --- a/arch/arm/src/stm32f0l0g0/Make.defs +++ b/arch/arm/src/stm32f0l0g0/Make.defs @@ -109,3 +109,7 @@ ifeq ($(CONFIG_STM32F0L0G0_FDCAN_SOCKET),y) CHIP_CSRCS += stm32_fdcan_sock.c endif endif + +ifeq ($(CONFIG_SENSORS_QENCODER),y) +CHIP_CSRCS += stm32_qencoder.c +endif diff --git a/arch/arm/src/stm32f0l0g0/hardware/stm32c0_pinmap.h b/arch/arm/src/stm32f0l0g0/hardware/stm32c0_pinmap.h index 476e3691447..4e10209f8a6 100644 --- a/arch/arm/src/stm32f0l0g0/hardware/stm32c0_pinmap.h +++ b/arch/arm/src/stm32f0l0g0/hardware/stm32c0_pinmap.h @@ -116,25 +116,239 @@ /* FDCAN1 */ -#define GPIO_FDCAN1_RX_1 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTA | GPIO_PIN11) -#define GPIO_FDCAN1_RX_2 (GPIO_ALT | GPIO_AF3 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN0) -#define GPIO_FDCAN1_RX_3 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN5) -#define GPIO_FDCAN1_RX_4 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN12) -#define GPIO_FDCAN1_RX_5 (GPIO_ALT | GPIO_AF8 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN8) -#define GPIO_FDCAN1_RX_6 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTC | GPIO_PIN2) -#define GPIO_FDCAN1_RX_7 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTC | GPIO_PIN4) -#define GPIO_FDCAN1_RX_8 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTD | GPIO_PIN0) - -#define GPIO_FDCAN1_TX_1 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTA | GPIO_PIN12) -#define GPIO_FDCAN1_TX_2 (GPIO_ALT | GPIO_AF3 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN1) -#define GPIO_FDCAN1_TX_3 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN13) -#define GPIO_FDCAN1_TX_4 (GPIO_ALT | GPIO_AF15 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN6) -#define GPIO_FDCAN1_TX_5 (GPIO_ALT | GPIO_AF8 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN9) -#define GPIO_FDCAN1_TX_6 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTC | GPIO_PIN3) -#define GPIO_FDCAN1_TX_7 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTC | GPIO_PIN5) -#define GPIO_FDCAN1_TX_8 (GPIO_ALT | GPIO_AF13 | GPIO_PUSHPULL | GPIO_PORTC | GPIO_PIN14) -#define GPIO_FDCAN1_TX_9 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTD | GPIO_PIN1) - -/* TODO: missing pinmaps */ +#define GPIO_FDCAN1_RX_1 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTA | GPIO_PIN11) +#define GPIO_FDCAN1_RX_2 (GPIO_ALT | GPIO_AF3 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN0) +#define GPIO_FDCAN1_RX_3 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN5) +#define GPIO_FDCAN1_RX_4 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN12) +#define GPIO_FDCAN1_RX_5 (GPIO_ALT | GPIO_AF8 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN8) +#define GPIO_FDCAN1_RX_6 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTC | GPIO_PIN2) +#define GPIO_FDCAN1_RX_7 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTC | GPIO_PIN4) +#define GPIO_FDCAN1_RX_8 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTD | GPIO_PIN0) + +#define GPIO_FDCAN1_TX_1 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTA | GPIO_PIN12) +#define GPIO_FDCAN1_TX_2 (GPIO_ALT | GPIO_AF3 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN1) +#define GPIO_FDCAN1_TX_3 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN13) +#define GPIO_FDCAN1_TX_4 (GPIO_ALT | GPIO_AF15 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN6) +#define GPIO_FDCAN1_TX_5 (GPIO_ALT | GPIO_AF8 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN9) +#define GPIO_FDCAN1_TX_6 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTC | GPIO_PIN3) +#define GPIO_FDCAN1_TX_7 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTC | GPIO_PIN5) +#define GPIO_FDCAN1_TX_8 (GPIO_ALT | GPIO_AF13 | GPIO_PUSHPULL | GPIO_PORTC | GPIO_PIN14) +#define GPIO_FDCAN1_TX_9 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTD | GPIO_PIN1) + +/* TIM1 */ + +#define GPIO_TIM1_BKIN_1 (GPIO_ALT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN6) +#define GPIO_TIM1_BKIN_2 (GPIO_ALT | GPIO_AF2 | GPIO_PORTB | GPIO_PIN12) +#define GPIO_TIM1_BKIN_3 (GPIO_ALT | GPIO_AF2 | GPIO_PORTC | GPIO_PIN13) +#define GPIO_TIM1_BKIN_4 (GPIO_ALT | GPIO_AF2 | GPIO_PORTD | GPIO_PIN5) +#define GPIO_TIM1_BKIN2_1 (GPIO_ALT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN11) +#define GPIO_TIM1_BKIN2_2 (GPIO_ALT | GPIO_AF1 | GPIO_PORTB | GPIO_PIN12) +#define GPIO_TIM1_BKIN2_3 (GPIO_ALT | GPIO_AF2 | GPIO_PORTC | GPIO_PIN14) +#define GPIO_TIM1_BKIN2_4 (GPIO_ALT | GPIO_AF2 | GPIO_PORTD | GPIO_PIN9) +#define GPIO_TIM1_CH1IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN0) +#define GPIO_TIM1_CH1IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN5) +#define GPIO_TIM1_CH1IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN8) +#define GPIO_TIM1_CH1IN_4 (GPIO_ALT | GPIO_FLOAT | GPIO_AF10 | GPIO_PORTA | GPIO_PIN14) +#define GPIO_TIM1_CH1IN_5 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN15) +#define GPIO_TIM1_CH1IN_6 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTC | GPIO_PIN8) +#define GPIO_TIM1_CH1OUT_1 (GPIO_ALT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN0) +#define GPIO_TIM1_CH1OUT_2 (GPIO_ALT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN5) +#define GPIO_TIM1_CH1OUT_3 (GPIO_ALT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN8) +#define GPIO_TIM1_CH1OUT_4 (GPIO_ALT | GPIO_AF10 | GPIO_PORTA | GPIO_PIN14) +#define GPIO_TIM1_CH1OUT_5 (GPIO_ALT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN15) +#define GPIO_TIM1_CH1OUT_6 (GPIO_ALT | GPIO_AF2 | GPIO_PORTC | GPIO_PIN8) +#define GPIO_TIM1_CH1NOUT_1 (GPIO_ALT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN3) +#define GPIO_TIM1_CH1NOUT_2 (GPIO_ALT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN7) +#define GPIO_TIM1_CH1NOUT_3 (GPIO_ALT | GPIO_AF2 | GPIO_PORTB | GPIO_PIN13) +#define GPIO_TIM1_CH1NOUT_4 (GPIO_ALT | GPIO_AF2 | GPIO_PORTD | GPIO_PIN2) +#define GPIO_TIM1_CH2IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN1) +#define GPIO_TIM1_CH2IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN9) +#define GPIO_TIM1_CH2IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF1 | GPIO_PORTB | GPIO_PIN3) +#define GPIO_TIM1_CH2IN_4 (GPIO_ALT | GPIO_FLOAT | GPIO_AF11 | GPIO_PORTB | GPIO_PIN6) +#define GPIO_TIM1_CH2IN_5 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTC | GPIO_PIN9) +#define GPIO_TIM1_CH2OUT_1 (GPIO_ALT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN1) +#define GPIO_TIM1_CH2OUT_2 (GPIO_ALT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN9) +#define GPIO_TIM1_CH2OUT_3 (GPIO_ALT | GPIO_AF1 | GPIO_PORTB | GPIO_PIN3) +#define GPIO_TIM1_CH2OUT_4 (GPIO_ALT | GPIO_AF11 | GPIO_PORTB | GPIO_PIN6) +#define GPIO_TIM1_CH2OUT_5 (GPIO_ALT | GPIO_AF2 | GPIO_PORTC | GPIO_PIN9) +#define GPIO_TIM1_CH2NOUT_1 (GPIO_ALT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN4) +#define GPIO_TIM1_CH2NOUT_2 (GPIO_ALT | GPIO_AF9 | GPIO_PORTA | GPIO_PIN8) +#define GPIO_TIM1_CH2NOUT_3 (GPIO_ALT | GPIO_AF2 | GPIO_PORTB | GPIO_PIN0) +#define GPIO_TIM1_CH2NOUT_4 (GPIO_ALT | GPIO_AF5 | GPIO_PORTB | GPIO_PIN1) +#define GPIO_TIM1_CH2NOUT_5 (GPIO_ALT | GPIO_AF2 | GPIO_PORTB | GPIO_PIN14) +#define GPIO_TIM1_CH2NOUT_6 (GPIO_ALT | GPIO_AF2 | GPIO_PORTD | GPIO_PIN3) +#define GPIO_TIM1_CH3IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN2) +#define GPIO_TIM1_CH3IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN10) +#define GPIO_TIM1_CH3IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF1 | GPIO_PORTB | GPIO_PIN6) +#define GPIO_TIM1_CH3IN_4 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTC | GPIO_PIN10) +#define GPIO_TIM1_CH3OUT_1 (GPIO_ALT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN2) +#define GPIO_TIM1_CH3OUT_2 (GPIO_ALT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN10) +#define GPIO_TIM1_CH3OUT_3 (GPIO_ALT | GPIO_AF1 | GPIO_PORTB | GPIO_PIN6) +#define GPIO_TIM1_CH3OUT_4 (GPIO_ALT | GPIO_AF2 | GPIO_PORTC | GPIO_PIN10) +#define GPIO_TIM1_CH3NOUT_1 (GPIO_ALT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN5) +#define GPIO_TIM1_CH3NOUT_2 (GPIO_ALT | GPIO_AF10 | GPIO_PORTA | GPIO_PIN8) +#define GPIO_TIM1_CH3NOUT_3 (GPIO_ALT | GPIO_AF2 | GPIO_PORTB | GPIO_PIN1) +#define GPIO_TIM1_CH3NOUT_4 (GPIO_ALT | GPIO_AF2 | GPIO_PORTB | GPIO_PIN15) +#define GPIO_TIM1_CH3NOUT_5 (GPIO_ALT | GPIO_AF2 | GPIO_PORTD | GPIO_PIN4) +#define GPIO_TIM1_CH4IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN3) +#define GPIO_TIM1_CH4IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN11) +#define GPIO_TIM1_CH4IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF1 | GPIO_PORTB | GPIO_PIN7) +#define GPIO_TIM1_CH4IN_4 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTC | GPIO_PIN11) +#define GPIO_TIM1_CH4IN_5 (GPIO_ALT | GPIO_FLOAT | GPIO_AF1 | GPIO_PORTF | GPIO_PIN2) +#define GPIO_TIM1_CH4OUT_1 (GPIO_ALT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN3) +#define GPIO_TIM1_CH4OUT_2 (GPIO_ALT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN11) +#define GPIO_TIM1_CH4OUT_3 (GPIO_ALT | GPIO_AF1 | GPIO_PORTB | GPIO_PIN7) +#define GPIO_TIM1_CH4OUT_4 (GPIO_ALT | GPIO_AF2 | GPIO_PORTC | GPIO_PIN11) +#define GPIO_TIM1_CH4OUT_5 (GPIO_ALT | GPIO_AF1 | GPIO_PORTF | GPIO_PIN2) + +/* TIM2 */ + +#define GPIO_TIM2_CH1IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTA | GPIO_PIN0) +#define GPIO_TIM2_CH1IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTA | GPIO_PIN5) +#define GPIO_TIM2_CH1IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN15) +#define GPIO_TIM2_CH1IN_4 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTC | GPIO_PIN4) +#define GPIO_TIM2_CH1OUT_1 (GPIO_ALT | GPIO_AF3 | GPIO_PORTA | GPIO_PIN0) +#define GPIO_TIM2_CH1OUT_2 (GPIO_ALT | GPIO_AF3 | GPIO_PORTA | GPIO_PIN5) +#define GPIO_TIM2_CH1OUT_3 (GPIO_ALT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN15) +#define GPIO_TIM2_CH1OUT_4 (GPIO_ALT | GPIO_AF3 | GPIO_PORTC | GPIO_PIN4) +#define GPIO_TIM2_CH2IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTA | GPIO_PIN1) +#define GPIO_TIM2_CH2IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF6 | GPIO_PORTB | GPIO_PIN3) +#define GPIO_TIM2_CH2IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTC | GPIO_PIN5) +#define GPIO_TIM2_CH2OUT_1 (GPIO_ALT | GPIO_AF3 | GPIO_PORTA | GPIO_PIN1) +#define GPIO_TIM2_CH2OUT_2 (GPIO_ALT | GPIO_AF6 | GPIO_PORTB | GPIO_PIN3) +#define GPIO_TIM2_CH2OUT_3 (GPIO_ALT | GPIO_AF3 | GPIO_PORTC | GPIO_PIN5) +#define GPIO_TIM2_CH3IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF6 | GPIO_PORTA | GPIO_PIN2) +#define GPIO_TIM2_CH3IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN10) +#define GPIO_TIM2_CH3IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTC | GPIO_PIN6) +#define GPIO_TIM2_CH3OUT_1 (GPIO_ALT | GPIO_AF6 | GPIO_PORTA | GPIO_PIN2) +#define GPIO_TIM2_CH3OUT_2 (GPIO_ALT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN10) +#define GPIO_TIM2_CH3OUT_3 (GPIO_ALT | GPIO_AF3 | GPIO_PORTC | GPIO_PIN6) +#define GPIO_TIM2_CH4IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTA | GPIO_PIN3) +#define GPIO_TIM2_CH4IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN11) +#define GPIO_TIM2_CH4IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTC | GPIO_PIN7) +#define GPIO_TIM2_CH4OUT_1 (GPIO_ALT | GPIO_AF3 | GPIO_PORTA | GPIO_PIN3) +#define GPIO_TIM2_CH4OUT_2 (GPIO_ALT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN11) +#define GPIO_TIM2_CH4OUT_3 (GPIO_ALT | GPIO_AF3 | GPIO_PORTC | GPIO_PIN7) + +/* TIM3 */ + +#define GPIO_TIM3_CH1IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF1 | GPIO_PORTA | GPIO_PIN6) +#define GPIO_TIM3_CH1IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF1 | GPIO_PORTB | GPIO_PIN4) +#define GPIO_TIM3_CH1IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF12 | GPIO_PORTB | GPIO_PIN6) +#define GPIO_TIM3_CH1IN_4 (GPIO_ALT | GPIO_FLOAT | GPIO_AF11 | GPIO_PORTB | GPIO_PIN7) +#define GPIO_TIM3_CH1IN_5 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN8) +#define GPIO_TIM3_CH1IN_6 (GPIO_ALT | GPIO_FLOAT | GPIO_AF1 | GPIO_PORTC | GPIO_PIN6) +#define GPIO_TIM3_CH1OUT_1 (GPIO_ALT | GPIO_AF1 | GPIO_PORTA | GPIO_PIN6) +#define GPIO_TIM3_CH1OUT_2 (GPIO_ALT | GPIO_AF1 | GPIO_PORTB | GPIO_PIN4) +#define GPIO_TIM3_CH1OUT_3 (GPIO_ALT | GPIO_AF12 | GPIO_PORTB | GPIO_PIN6) +#define GPIO_TIM3_CH1OUT_4 (GPIO_ALT | GPIO_AF11 | GPIO_PORTB | GPIO_PIN7) +#define GPIO_TIM3_CH1OUT_5 (GPIO_ALT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN8) +#define GPIO_TIM3_CH1OUT_6 (GPIO_ALT | GPIO_AF1 | GPIO_PORTC | GPIO_PIN6) +#define GPIO_TIM3_CH2IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF1 | GPIO_PORTA | GPIO_PIN7) +#define GPIO_TIM3_CH2IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN3) +#define GPIO_TIM3_CH2IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF1 | GPIO_PORTB | GPIO_PIN5) +#define GPIO_TIM3_CH2IN_4 (GPIO_ALT | GPIO_FLOAT | GPIO_AF13 | GPIO_PORTB | GPIO_PIN6) +#define GPIO_TIM3_CH2IN_5 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN9) +#define GPIO_TIM3_CH2IN_6 (GPIO_ALT | GPIO_FLOAT | GPIO_AF1 | GPIO_PORTC | GPIO_PIN7) +#define GPIO_TIM3_CH2IN_7 (GPIO_ALT | GPIO_FLOAT | GPIO_AF11 | GPIO_PORTC | GPIO_PIN14) +#define GPIO_TIM3_CH2OUT_1 (GPIO_ALT | GPIO_AF1 | GPIO_PORTA | GPIO_PIN7) +#define GPIO_TIM3_CH2OUT_2 (GPIO_ALT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN3) +#define GPIO_TIM3_CH2OUT_3 (GPIO_ALT | GPIO_AF1 | GPIO_PORTB | GPIO_PIN5) +#define GPIO_TIM3_CH2OUT_4 (GPIO_ALT | GPIO_AF13 | GPIO_PORTB | GPIO_PIN6) +#define GPIO_TIM3_CH2OUT_5 (GPIO_ALT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN9) +#define GPIO_TIM3_CH2OUT_6 (GPIO_ALT | GPIO_AF1 | GPIO_PORTC | GPIO_PIN7) +#define GPIO_TIM3_CH2OUT_7 (GPIO_ALT | GPIO_AF11 | GPIO_PORTC | GPIO_PIN14) +#define GPIO_TIM3_CH3IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF11 | GPIO_PORTA | GPIO_PIN8) +#define GPIO_TIM3_CH3IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF1 | GPIO_PORTB | GPIO_PIN0) +#define GPIO_TIM3_CH3IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN5) +#define GPIO_TIM3_CH3IN_4 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN6) +#define GPIO_TIM3_CH3IN_5 (GPIO_ALT | GPIO_FLOAT | GPIO_AF1 | GPIO_PORTC | GPIO_PIN8) +#define GPIO_TIM3_CH3IN_6 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTC | GPIO_PIN15) +#define GPIO_TIM3_CH3OUT_1 (GPIO_ALT | GPIO_AF11 | GPIO_PORTA | GPIO_PIN8) +#define GPIO_TIM3_CH3OUT_2 (GPIO_ALT | GPIO_AF1 | GPIO_PORTB | GPIO_PIN0) +#define GPIO_TIM3_CH3OUT_3 (GPIO_ALT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN5) +#define GPIO_TIM3_CH3OUT_4 (GPIO_ALT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN6) +#define GPIO_TIM3_CH3OUT_5 (GPIO_ALT | GPIO_AF1 | GPIO_PORTC | GPIO_PIN8) +#define GPIO_TIM3_CH3OUT_6 (GPIO_ALT | GPIO_AF3 | GPIO_PORTC | GPIO_PIN15) +#define GPIO_TIM3_CH4IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF12 | GPIO_PORTA | GPIO_PIN8) +#define GPIO_TIM3_CH4IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF1 | GPIO_PORTB | GPIO_PIN1) +#define GPIO_TIM3_CH4IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN7) +#define GPIO_TIM3_CH4IN_4 (GPIO_ALT | GPIO_FLOAT | GPIO_AF1 | GPIO_PORTC | GPIO_PIN9) +#define GPIO_TIM3_CH4OUT_1 (GPIO_ALT | GPIO_AF12 | GPIO_PORTA | GPIO_PIN8) +#define GPIO_TIM3_CH4OUT_2 (GPIO_ALT | GPIO_AF1 | GPIO_PORTB | GPIO_PIN1) +#define GPIO_TIM3_CH4OUT_3 (GPIO_ALT | GPIO_AF3 | GPIO_PORTB | GPIO_PIN7) +#define GPIO_TIM3_CH4OUT_4 (GPIO_ALT | GPIO_AF1 | GPIO_PORTC | GPIO_PIN9) + +/* TIM14 */ + +#define GPIO_TIM14_CH1IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF4 | GPIO_PORTA | GPIO_PIN4) +#define GPIO_TIM14_CH1IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF4 | GPIO_PORTA | GPIO_PIN7) +#define GPIO_TIM14_CH1IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF13 | GPIO_PORTA | GPIO_PIN8) +#define GPIO_TIM14_CH1IN_4 (GPIO_ALT | GPIO_FLOAT | GPIO_AF0 | GPIO_PORTB | GPIO_PIN1) +#define GPIO_TIM14_CH1IN_5 (GPIO_ALT | GPIO_FLOAT | GPIO_AF0 | GPIO_PORTC | GPIO_PIN12) +#define GPIO_TIM14_CH1IN_6 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTF | GPIO_PIN0) +#define GPIO_TIM14_CH1OUT_1 (GPIO_ALT | GPIO_AF4 | GPIO_PORTA | GPIO_PIN4) +#define GPIO_TIM14_CH1OUT_2 (GPIO_ALT | GPIO_AF4 | GPIO_PORTA | GPIO_PIN7) +#define GPIO_TIM14_CH1OUT_3 (GPIO_ALT | GPIO_AF13 | GPIO_PORTA | GPIO_PIN8) +#define GPIO_TIM14_CH1OUT_4 (GPIO_ALT | GPIO_AF0 | GPIO_PORTB | GPIO_PIN1) +#define GPIO_TIM14_CH1OUT_5 (GPIO_ALT | GPIO_AF0 | GPIO_PORTC | GPIO_PIN12) +#define GPIO_TIM14_CH1OUT_6 (GPIO_ALT | GPIO_AF2 | GPIO_PORTF | GPIO_PIN0) + +/* TIM15 */ + +#define GPIO_TIM15_BKIN_1 (GPIO_ALT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN9) +#define GPIO_TIM15_BKIN_2 (GPIO_ALT | GPIO_AF5 | GPIO_PORTB | GPIO_PIN8) +#define GPIO_TIM15_BKIN_3 (GPIO_ALT | GPIO_AF5 | GPIO_PORTB | GPIO_PIN12) +#define GPIO_TIM15_BKIN_4 (GPIO_ALT | GPIO_AF4 | GPIO_PORTC | GPIO_PIN15) +#define GPIO_TIM15_CH1IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF8 | GPIO_PORTA | GPIO_PIN2) +#define GPIO_TIM15_CH1IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF5 | GPIO_PORTB | GPIO_PIN14) +#define GPIO_TIM15_CH1IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTC | GPIO_PIN1) +#define GPIO_TIM15_CH1OUT_1 (GPIO_ALT | GPIO_AF8 | GPIO_PORTA | GPIO_PIN2) +#define GPIO_TIM15_CH1OUT_2 (GPIO_ALT | GPIO_AF5 | GPIO_PORTB | GPIO_PIN14) +#define GPIO_TIM15_CH1OUT_3 (GPIO_ALT | GPIO_AF2 | GPIO_PORTC | GPIO_PIN1) +#define GPIO_TIM15_CH1NOUT_1 (GPIO_ALT | GPIO_AF8 | GPIO_PORTA | GPIO_PIN1) +#define GPIO_TIM15_CH1NOUT_2 (GPIO_ALT | GPIO_AF5 | GPIO_PORTB | GPIO_PIN13) +#define GPIO_TIM15_CH1NOUT_3 (GPIO_ALT | GPIO_AF4 | GPIO_PORTB | GPIO_PIN15) +#define GPIO_TIM15_CH1NOUT_4 (GPIO_ALT | GPIO_AF2 | GPIO_PORTF | GPIO_PIN1) +#define GPIO_TIM15_CH2IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF8 | GPIO_PORTA | GPIO_PIN3) +#define GPIO_TIM15_CH2IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF5 | GPIO_PORTB | GPIO_PIN15) +#define GPIO_TIM15_CH2IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTC | GPIO_PIN2) +#define GPIO_TIM15_CH2OUT_1 (GPIO_ALT | GPIO_AF8 | GPIO_PORTA | GPIO_PIN3) +#define GPIO_TIM15_CH2OUT_2 (GPIO_ALT | GPIO_AF5 | GPIO_PORTB | GPIO_PIN15) +#define GPIO_TIM15_CH2OUT_3 (GPIO_ALT | GPIO_AF2 | GPIO_PORTC | GPIO_PIN2) + +/* TIM16 */ + +#define GPIO_TIM16_BKIN_1 (GPIO_ALT | GPIO_AF2 | GPIO_PORTB | GPIO_PIN5) +#define GPIO_TIM16_BKIN_2 GPIO_ALT | GPIO_AF14 | GPIO_PORTB | GPIO_PIN6) +#define GPIO_TIM16_CH1IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN0) +#define GPIO_TIM16_CH1IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN6) +#define GPIO_TIM16_CH1IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF10 | GPIO_PORTB | GPIO_PIN7) +#define GPIO_TIM16_CH1IN_4 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTB | GPIO_PIN8) +#define GPIO_TIM16_CH1IN_5 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTD | GPIO_PIN0) +#define GPIO_TIM16_CH1OUT_1 (GPIO_ALT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN0) +#define GPIO_TIM16_CH1OUT_2 (GPIO_ALT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN6) +#define GPIO_TIM16_CH1OUT_3 (GPIO_ALT | GPIO_AF10 | GPIO_PORTB | GPIO_PIN7) +#define GPIO_TIM16_CH1OUT_4 (GPIO_ALT | GPIO_AF2 | GPIO_PORTB | GPIO_PIN8) +#define GPIO_TIM16_CH1OUT_5 (GPIO_ALT | GPIO_AF2 | GPIO_PORTD | GPIO_PIN0) +#define GPIO_TIM16_CH1NOUT_1 (GPIO_ALT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN2) +#define GPIO_TIM16_CH1NOUT_2 (GPIO_ALT | GPIO_AF2 | GPIO_PORTB | GPIO_PIN6) + +/* TIM17 */ + +#define GPIO_TIM17_BKIN_1 (GPIO_ALT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN10) +#define GPIO_TIM17_BKIN_2 (GPIO_ALT | GPIO_AF5 | GPIO_PORTB | GPIO_PIN4) +#define GPIO_TIM17_CH1IN_1 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN1) +#define GPIO_TIM17_CH1IN_2 (GPIO_ALT | GPIO_FLOAT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN7) +#define GPIO_TIM17_CH1IN_3 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTB | GPIO_PIN9) +#define GPIO_TIM17_CH1IN_4 (GPIO_ALT | GPIO_FLOAT | GPIO_AF10 | GPIO_PORTC | GPIO_PIN14) +#define GPIO_TIM17_CH1IN_5 (GPIO_ALT | GPIO_FLOAT | GPIO_AF2 | GPIO_PORTD | GPIO_PIN1) +#define GPIO_TIM17_CH1OUT_1 (GPIO_ALT | GPIO_AF2 | GPIO_PORTA | GPIO_PIN1) +#define GPIO_TIM17_CH1OUT_2 (GPIO_ALT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN7) +#define GPIO_TIM17_CH1OUT_3 (GPIO_ALT | GPIO_AF2 | GPIO_PORTB | GPIO_PIN9) +#define GPIO_TIM17_CH1OUT_4 (GPIO_ALT | GPIO_AF10 | GPIO_PORTC | GPIO_PIN14) +#define GPIO_TIM17_CH1OUT_5 (GPIO_ALT | GPIO_AF2 | GPIO_PORTD | GPIO_PIN1) +#define GPIO_TIM17_CH1NOUT_1 (GPIO_ALT | GPIO_AF5 | GPIO_PORTA | GPIO_PIN4) +#define GPIO_TIM17_CH1NOUT_2 (GPIO_ALT | GPIO_AF2 | GPIO_PORTB | GPIO_PIN7) #endif /* __ARCH_ARM_SRC_STM32F0L0G0_HARDWARE_STM32C0_PINMAP_H */ diff --git a/arch/arm/src/stm32f0l0g0/stm32_qencoder.c b/arch/arm/src/stm32f0l0g0/stm32_qencoder.c new file mode 100644 index 00000000000..8f09d5596fb --- /dev/null +++ b/arch/arm/src/stm32f0l0g0/stm32_qencoder.c @@ -0,0 +1,1254 @@ +/**************************************************************************** + * arch/arm/src/stm32f0l0g0/stm32_qencoder.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 <nuttx/config.h> + +#include <stdint.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/irq.h> +#include <nuttx/spinlock.h> +#include <nuttx/sensors/qencoder.h> + +#include <arch/board/board.h> + +#include "chip.h" +#include "arm_internal.h" +#include "stm32.h" +#include "stm32_gpio.h" +#include "stm32_tim.h" +#include "stm32_qencoder.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Timers *******************************************************************/ + +#ifndef CONFIG_STM32F0L0G0_QENCODER_DISABLE_EXTEND16BTIMERS +# undef HAVE_32BIT_TIMERS +# undef HAVE_16BIT_TIMERS + +/* If TIM2 is enabled and is 32-bit, then we have 32-bit timers */ + +# if defined(CONFIG_STM32F0L0G0_TIM2_QE) && defined(HAVE_TIM2_32BIT) +# define HAVE_32BIT_TIMERS 1 +# endif + +/* If TIM1, TIM2 (16-bit variant), TIM3, or TIM4 are enabled, we have + * 16-bit timers + */ + +# if defined(CONFIG_STM32F0L0G0_TIM1_QE) || defined(CONFIG_STM32F0L0G0_TIM3_QE) || \ + defined(CONFIG_STM32F0L0G0_TIM4_QE) || \ + (defined(CONFIG_STM32F0L0G0_TIM2_QE) && defined(HAVE_TIM2_16BIT)) +# define HAVE_16BIT_TIMERS 1 +# endif + +/* The width in bits of each timer */ + +# define TIM1_BITWIDTH 16 +# ifdef HAVE_TIM2_16BIT +# define TIM2_BITWIDTH 16 +# else +# define TIM2_BITWIDTH 32 +# endif +# define TIM3_BITWIDTH 16 +# define TIM4_BITWIDTH 16 + +/* Do we need to support mixed 16- and 32-bit timers */ + +# undef HAVE_MIXEDWIDTH_TIMERS +# if defined(HAVE_16BIT_TIMERS) && defined(HAVE_32BIT_TIMERS) +# define HAVE_MIXEDWIDTH_TIMERS 1 +# endif +#endif + +/* Input filter *************************************************************/ + +#ifdef CONFIG_STM32F0L0G0_QENCODER_FILTER +# if defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_FDTS) +# if defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_1) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_NOFILT +# endif +# elif defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_CKINT) +# if defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_2) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_FCKINT2 +# elif defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_4) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_FCKINT4 +# elif defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_8) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_FCKINT8 +# endif +# elif defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_FDTS_2) +# if defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_6) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_FDTSd26 +# elif defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_8) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_FDTSd28 +# endif +# elif defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_FDTS_4) +# if defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_6) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_FDTSd46 +# elif defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_8) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_FDTSd48 +# endif +# elif defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_FDTS_8) +# if defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_6) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_FDTSd86 +# elif defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_8) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_FDTSd88 +# endif +# elif defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_FDTS_16) +# if defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_5) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_FDTSd165 +# elif defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_6) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_FDTSd166 +# elif defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_8) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_FDTSd168 +# endif +# elif defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_FDTS_32) +# if defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_5) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_FDTSd325 +# elif defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_6) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_FDTSd326 +# elif defined(CONFIG_STM32F0L0G0_QENCODER_SAMPLE_EVENT_8) +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_FDTSd328 +# endif +# endif + +# ifndef STM32F0L0G0_QENCODER_ICF +# warning "Invalid encoder filter combination, filter disabled" +# endif +#endif + +#ifndef STM32F0L0G0_QENCODER_ICF +# define STM32F0L0G0_QENCODER_ICF GTIM_CCMR_ICF_NOFILT +#endif + +#define STM32F0L0G0_GPIO_INPUT_FLOAT (GPIO_INPUT | GPIO_FLOAT) + +/* Debug ********************************************************************/ + +/* Non-standard debug that may be enabled just for testing the quadrature + * encoder + */ + +#ifndef CONFIG_DEBUG_FEATURES +# undef CONFIG_DEBUG_SENSORS +#endif + +#ifdef CONFIG_DEBUG_SENSORS +# ifdef CONFIG_DEBUG_INFO +# define qe_dumpgpio(p,m) stm32_dumpgpio(p,m) +# else +# define qe_dumpgpio(p,m) +# endif +#else +# define qe_dumpgpio(p,m) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Constant configuration structure that is retained in FLASH */ + +struct stm32_qeconfig_s +{ + uint8_t timid; /* Timer ID {1,2,3,4,5,8} */ + uint8_t irq; /* Timer update IRQ */ +#ifdef HAVE_MIXEDWIDTH_TIMERS + uint8_t width; /* Timer width (16- or 32-bits) */ +#endif + uint32_t ti1cfg; /* TI1 input pin configuration (20-bit encoding) */ + uint32_t ti2cfg; /* TI2 input pin configuration (20-bit encoding) */ + uint32_t base; /* Register base address */ + uint32_t psc; /* Encoder pulses prescaler */ +}; + +/* Overall, RAM-based state structure */ + +struct stm32_lowerhalf_s +{ + /* The first field of this state structure must be a pointer to the lower- + * half callback structure: + */ + + const struct qe_ops_s *ops; + + /* STM32 driver-specific fields: */ + + const struct stm32_qeconfig_s *config; + + bool inuse; /* True: The lower-half driver is in-use */ + +#ifdef CONFIG_STM32F0L0G0_QENCODER_INDEX_PIN + uint32_t index_pin; /* Index pin GPIO */ + bool index_use; /* True: Index pin is configured */ + int32_t index_offset; /* Index pin offset */ +#endif + +#ifndef CONFIG_STM32F0L0G0_QENCODER_DISABLE_EXTEND16BTIMERS + volatile int32_t position; /* The current position offset */ +#endif + spinlock_t lock; /* Spinlock */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Helper functions */ + +static uint16_t stm32_getreg16(struct stm32_lowerhalf_s *priv, int offset); +static void stm32_putreg16(struct stm32_lowerhalf_s *priv, int offset, + uint16_t value); +static uint32_t stm32_getreg32(struct stm32_lowerhalf_s *priv, int offset); +static void stm32_putreg32(struct stm32_lowerhalf_s *priv, int offset, + uint32_t value); + +#if defined(CONFIG_DEBUG_SENSORS) && defined(CONFIG_DEBUG_INFO) +static void stm32_dumpregs(struct stm32_lowerhalf_s *priv, + const char *msg); +#else +# define stm32_dumpregs(priv, msg) +#endif + +static struct stm32_lowerhalf_s *stm32_tim2lower(int tim); + +/* Interrupt handling */ + +#ifdef CONFIG_STM32F0L0G0_QENCODER_INDEX_PIN +static int stm32_qe_index_irq(int irq, void *context, void *arg); +#endif + +#ifndef CONFIG_STM32F0L0G0_QENCODER_DISABLE_EXTEND16BTIMERS +static int stm32_interrupt(int irq, void *context, void *arg); +#endif + +/* Lower-half Quadrature Encoder Driver Methods */ + +static int stm32_setup(struct qe_lowerhalf_s *lower); +static int stm32_shutdown(struct qe_lowerhalf_s *lower); +static int stm32_position(struct qe_lowerhalf_s *lower, int32_t *pos); +static int stm32_setposmax(struct qe_lowerhalf_s *lower, uint32_t pos); +static int stm32_reset(struct qe_lowerhalf_s *lower); +static int stm32_setindex(struct qe_lowerhalf_s *lower, uint32_t pos); +static int stm32_ioctl(struct qe_lowerhalf_s *lower, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The lower half callback structure */ + +static const struct qe_ops_s g_qecallbacks = +{ + .setup = stm32_setup, + .shutdown = stm32_shutdown, + .position = stm32_position, + .setposmax = stm32_setposmax, + .reset = stm32_reset, + .setindex = stm32_setindex, + .ioctl = stm32_ioctl, +}; + +/* Per-timer state structures */ + +#ifdef CONFIG_STM32F0L0G0_TIM1_QE +static const struct stm32_qeconfig_s g_tim1config = +{ + .timid = 1, + .irq = STM32_IRQ_TIM1_BRK, +#ifdef HAVE_MIXEDWIDTH_TIMERS + .width = TIM1_BITWIDTH, +#endif + .base = STM32_TIM1_BASE, + .psc = CONFIG_STM32F0L0G0_TIM1_QEPSC, + .ti1cfg = GPIO_TIM1_CH1IN, + .ti2cfg = GPIO_TIM1_CH2IN, +}; + +static struct stm32_lowerhalf_s g_tim1lower = +{ + .ops = &g_qecallbacks, + .config = &g_tim1config, + .inuse = false, + .lock = SP_UNLOCKED, +}; + +#endif + +#ifdef CONFIG_STM32F0L0G0_TIM2_QE +static const struct stm32_qeconfig_s g_tim2config = +{ + .timid = 2, + .irq = STM32_IRQ_TIM2, +#ifdef HAVE_MIXEDWIDTH_TIMERS + .width = TIM2_BITWIDTH, +#endif + .base = STM32_TIM2_BASE, + .psc = CONFIG_STM32F0L0G0_TIM2_QEPSC, + .ti1cfg = GPIO_TIM2_CH1IN, + .ti2cfg = GPIO_TIM2_CH2IN, +}; + +static struct stm32_lowerhalf_s g_tim2lower = +{ + .ops = &g_qecallbacks, + .config = &g_tim2config, + .inuse = false, + .lock = SP_UNLOCKED, +}; + +#endif + +#ifdef CONFIG_STM32F0L0G0_TIM3_QE +static const struct stm32_qeconfig_s g_tim3config = +{ + .timid = 3, + .irq = STM32_IRQ_TIM3, +#ifdef HAVE_MIXEDWIDTH_TIMERS + .width = TIM3_BITWIDTH, +#endif + .base = STM32_TIM3_BASE, + .psc = CONFIG_STM32F0L0G0_TIM3_QEPSC, + .ti1cfg = GPIO_TIM3_CH1IN, + .ti2cfg = GPIO_TIM3_CH2IN, +}; + +static struct stm32_lowerhalf_s g_tim3lower = +{ + .ops = &g_qecallbacks, + .config = &g_tim3config, + .inuse = false, + .lock = SP_UNLOCKED, +}; + +#endif + +#ifdef CONFIG_STM32F0L0G0_TIM4_QE +static const struct stm32_qeconfig_s g_tim4config = +{ + .timid = 4, + .irq = STM32_IRQ_TIM4, +#ifdef HAVE_MIXEDWIDTH_TIMERS + .width = TIM4_BITWIDTH, +#endif + .base = STM32_TIM4_BASE, + .psc = CONFIG_STM32F0L0G0_TIM4_QEPSC, + .ti1cfg = GPIO_TIM4_CH1IN, + .ti2cfg = GPIO_TIM4_CH2IN, +}; + +static struct stm32_lowerhalf_s g_tim4lower = +{ + .ops = &g_qecallbacks, + .config = &g_tim4config, + .inuse = false, + .lock = SP_UNLOCKED, +}; + +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_getreg16 + * + * Description: + * Read the value of a 16-bit timer register. + * + * Input Parameters: + * priv - A reference to the lower half status + * offset - The offset to the register to read + * + * Returned Value: + * The current contents of the specified register + * + ****************************************************************************/ + +static uint16_t stm32_getreg16(struct stm32_lowerhalf_s *priv, int offset) +{ + return getreg16(priv->config->base + offset); +} + +/**************************************************************************** + * Name: stm32_putreg16 + * + * Description: + * Write a value to a 16-bit timer register. + * + * Input Parameters: + * priv - A reference to the lower half status + * offset - The offset to the register to read + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_putreg16(struct stm32_lowerhalf_s *priv, int offset, + uint16_t value) +{ + putreg16(value, priv->config->base + offset); +} + +/**************************************************************************** + * Name: stm32_getreg32 + * + * Description: + * Read the value of a 32-bit timer register. + * This applies only for the STM32 F4 32-bit registers (CNT, ARR, CRR1-4) + * in the 32-bit timers TIM2-5 (but works OK with the 16-bit TIM1,8 + * and F1 registers as well). + * + * Input Parameters: + * priv - A reference to the lower half status + * offset - The offset to the register to read + * + * Returned Value: + * The current contents of the specified register + * + ****************************************************************************/ + +static uint32_t stm32_getreg32(struct stm32_lowerhalf_s *priv, int offset) +{ + return getreg32(priv->config->base + offset); +} + +/**************************************************************************** + * Name: stm32_putreg16 + * + * Description: + * Write a value to a 32-bit timer register. + * This applies only for the STM32 F4 32-bit registers (CNT, ARR, CRR1-4) + * in the 32-bit timers TIM2-5 (but works OK with the 16-bit TIM1,8 + * and F1 registers). + * + * Input Parameters: + * priv - A reference to the lower half status + * offset - The offset to the register to read + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_putreg32(struct stm32_lowerhalf_s *priv, int offset, + uint32_t value) +{ + putreg32(value, priv->config->base + offset); +} + +/**************************************************************************** + * Name: stm32_dumpregs + * + * Description: + * Dump all timer registers. + * + * Input Parameters: + * priv - A reference to the QENCODER block status + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if defined(CONFIG_DEBUG_SENSORS) && defined(CONFIG_DEBUG_INFO) +static void stm32_dumpregs(struct stm32_lowerhalf_s *priv, + const char *msg) +{ + sninfo("%s:\n", msg); + sninfo(" CR1: %04x CR2: %04x SMCR: %08" PRIx32 " DIER: %04x\n", + stm32_getreg16(priv, STM32_GTIM_CR1_OFFSET), + stm32_getreg16(priv, STM32_GTIM_CR2_OFFSET), + stm32_getreg32(priv, STM32_GTIM_SMCR_OFFSET), + stm32_getreg16(priv, STM32_GTIM_DIER_OFFSET)); + sninfo(" SR: %04x EGR: %04x CCMR1: %08" PRIx32 + " CCMR2: %08" PRIx32 "\n", + stm32_getreg16(priv, STM32_GTIM_SR_OFFSET), + stm32_getreg16(priv, STM32_GTIM_EGR_OFFSET), + stm32_getreg32(priv, STM32_GTIM_CCMR1_OFFSET), + stm32_getreg32(priv, STM32_GTIM_CCMR2_OFFSET)); + sninfo(" CCER: %04x CNT: %08" PRIx32 " PSC: %04x" + " ARR: %08" PRIx32 "\n", + stm32_getreg16(priv, STM32_GTIM_CCER_OFFSET), + stm32_getreg32(priv, STM32_GTIM_CNT_OFFSET), + stm32_getreg16(priv, STM32_GTIM_PSC_OFFSET), + stm32_getreg32(priv, STM32_GTIM_ARR_OFFSET)); + sninfo(" CCR1: %08" PRIx32 " CCR2: %08" PRIx32 "\n", + stm32_getreg32(priv, STM32_GTIM_CCR1_OFFSET), + stm32_getreg32(priv, STM32_GTIM_CCR2_OFFSET)); + sninfo(" CCR3: %08" PRIx32 " CCR4: %08" PRIx32 "\n", + stm32_getreg32(priv, STM32_GTIM_CCR3_OFFSET), + stm32_getreg32(priv, STM32_GTIM_CCR4_OFFSET)); +#if defined(CONFIG_STM32F0L0G0_TIM1_QE) + if (priv->config->timid == 1) + { + sninfo(" RCR: %04x BDTR: %04x DCR: %04x DMAR: %04x\n", + stm32_getreg16(priv, STM32_ATIM_RCR_OFFSET), + stm32_getreg16(priv, STM32_ATIM_BDTR_OFFSET), + stm32_getreg16(priv, STM32_ATIM_DCR_OFFSET), + stm32_getreg16(priv, STM32_ATIM_DMAR_OFFSET)); + } + else +#endif + { + sninfo(" DCR: %04x DMAR: %04x\n", + stm32_getreg16(priv, STM32_GTIM_DCR_OFFSET), + stm32_getreg16(priv, STM32_GTIM_DMAR_OFFSET)); + } +} +#endif + +/**************************************************************************** + * Name: stm32_tim2lower + * + * Description: + * Map a timer number to a device structure + * + ****************************************************************************/ + +static struct stm32_lowerhalf_s *stm32_tim2lower(int tim) +{ + switch (tim) + { +#ifdef CONFIG_STM32F0L0G0_TIM1_QE + case 1: + return &g_tim1lower; +#endif +#ifdef CONFIG_STM32F0L0G0_TIM2_QE + case 2: + return &g_tim2lower; +#endif +#ifdef CONFIG_STM32F0L0G0_TIM3_QE + case 3: + return &g_tim3lower; +#endif +#ifdef CONFIG_STM32F0L0G0_TIM4_QE + case 4: + return &g_tim4lower; +#endif + default: + return NULL; + } +} + +/**************************************************************************** + * Name: stm32_qe_index_irq + * + * Description: + * Common encoder index pin interrupt. + * + ****************************************************************************/ + +#ifdef CONFIG_STM32F0L0G0_QENCODER_INDEX_PIN +static int stm32_qe_index_irq(int irq, void *context, void *arg) +{ + struct stm32_lowerhalf_s *priv; + bool valid = false; + + DEBUGASSERT(arg); + + priv = (struct stm32_lowerhalf_s *)arg; + + valid = stm32_gpioread(priv->index_pin); + + if (valid == true) + { + stm32_putreg32(priv, STM32_GTIM_CNT_OFFSET, priv->index_offset); + } + + return OK; +} +#endif + +/**************************************************************************** + * Name: stm32_interrupt + * + * Description: + * Common timer interrupt handling. NOTE: Only 16-bit timers require timer + * interrupts. + * + ****************************************************************************/ + +#ifndef CONFIG_STM32F0L0G0_QENCODER_DISABLE_EXTEND16BTIMERS +static int stm32_interrupt(int irq, void *context, void *arg) +{ + struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)arg; + uint16_t regval; + + DEBUGASSERT(priv != NULL); + + /* Verify that this is an update interrupt. + * Nothing else is expected. + */ + + regval = stm32_getreg16(priv, STM32_GTIM_SR_OFFSET); + DEBUGASSERT((regval & ATIM_SR_UIF) != 0); + + /* Clear the UIF interrupt bit */ + + stm32_putreg16(priv, STM32_GTIM_SR_OFFSET, regval & ~GTIM_SR_UIF); + + /* Check the direction bit in the CR1 register and add or subtract the + * maximum value, as appropriate. + */ + + regval = stm32_getreg16(priv, STM32_GTIM_CR1_OFFSET); + if ((regval & ATIM_CR1_DIR) != 0) + { + priv->position -= (int32_t)0x0000ffff; + } + else + { + priv->position += (int32_t)0x0000ffff; + } + + return OK; +} +#endif + +/**************************************************************************** + * Name: stm32_setup + * + * Description: + * This method is called when the driver is opened. The lower half driver + * should configure and initialize the device so that it is ready for use. + * The initial position value should be zero. * + * + ****************************************************************************/ + +static int stm32_setup(struct qe_lowerhalf_s *lower) +{ + struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower; + uint16_t dier; + uint32_t smcr; + uint32_t ccmr1; + uint16_t ccer; + uint16_t cr1; +#ifndef CONFIG_STM32F0L0G0_QENCODER_DISABLE_EXTEND16BTIMERS + uint16_t regval; + int ret; +#endif + + /* NOTE: + * Clocking should have been enabled in the low-level RCC logic at boot-up + */ + + /* Timer base configuration */ + + cr1 = stm32_getreg16(priv, STM32_GTIM_CR1_OFFSET); + + /* Clear the direction bit (0=count up) and select the Counter Mode + * (0=Edge aligned) + * (Timers 2-5 and 1-8 only) + */ + + cr1 &= ~(GTIM_CR1_DIR | GTIM_CR1_CMS_MASK); + stm32_putreg16(priv, STM32_GTIM_CR1_OFFSET, cr1); + + /* Set the Autoreload value */ + +#if defined(HAVE_MIXEDWIDTH_TIMERS) + if (priv->config->width == 32) + { + stm32_putreg32(priv, STM32_GTIM_ARR_OFFSET, 0xffffffff); + } + else + { + stm32_putreg16(priv, STM32_GTIM_ARR_OFFSET, 0xffff); + } +#elif defined(HAVE_32BIT_TIMERS) + stm32_putreg32(priv, STM32_GTIM_ARR_OFFSET, 0xffffffff); +#else + stm32_putreg16(priv, STM32_GTIM_ARR_OFFSET, 0xffff); +#endif + + /* Set the timer prescaler value. */ + + stm32_putreg16(priv, + STM32_GTIM_PSC_OFFSET, (uint16_t)priv->config->psc); + +#if defined(CONFIG_STM32F0L0G0_TIM1_QE) + if (priv->config->timid == 1) + { + /* Clear the Repetition Counter value */ + + stm32_putreg16(priv, STM32_ATIM_RCR_OFFSET, 0); + } +#endif + + /* Generate an update event to reload the Prescaler + * and the repetition counter (only for TIM1) value immediately + */ + + stm32_putreg16(priv, STM32_GTIM_EGR_OFFSET, GTIM_EGR_UG); + + /* GPIO pin configuration */ + + stm32_configgpio(priv->config->ti1cfg); + stm32_configgpio(priv->config->ti2cfg); + + /* Set the encoder Mode 3 */ + + smcr = stm32_getreg32(priv, STM32_GTIM_SMCR_OFFSET); + smcr &= ~GTIM_SMCR_SMS_MASK; + smcr |= GTIM_SMCR_ENCMD3; + stm32_putreg32(priv, STM32_GTIM_SMCR_OFFSET, smcr); + + /* TI1 Channel Configuration */ + + /* Disable the Channel 1: Reset the CC1E Bit */ + + ccer = stm32_getreg16(priv, STM32_GTIM_CCER_OFFSET); + ccer &= ~GTIM_CCER_CC1E; + stm32_putreg16(priv, STM32_GTIM_CCER_OFFSET, ccer); + + ccmr1 = stm32_getreg32(priv, STM32_GTIM_CCMR1_OFFSET); + ccer = stm32_getreg16(priv, STM32_GTIM_CCER_OFFSET); + + /* Select the Input IC1=TI1 and set the filter fSAMPLING=fDTS/4, N=6 */ + + ccmr1 &= ~(GTIM_CCMR1_CC1S_MASK | GTIM_CCMR1_IC1F_MASK); + ccmr1 |= GTIM_CCMR_CCS_CCIN1 << GTIM_CCMR1_CC1S_SHIFT; + ccmr1 |= STM32F0L0G0_QENCODER_ICF << GTIM_CCMR1_IC1F_SHIFT; + + /* Select the Polarity=rising and set the CC1E Bit */ + + ccer &= ~(GTIM_CCER_CC1P | GTIM_CCER_CC1NP); + ccer |= GTIM_CCER_CC1E; + + /* Write to TIM CCMR1 and CCER registers */ + + stm32_putreg32(priv, STM32_GTIM_CCMR1_OFFSET, ccmr1); + stm32_putreg16(priv, STM32_GTIM_CCER_OFFSET, ccer); + + /* Set the Input Capture Prescaler value: Capture performed each time an + * edge is detected on the capture input. + */ + + ccmr1 = stm32_getreg32(priv, STM32_GTIM_CCMR1_OFFSET); + ccmr1 &= ~GTIM_CCMR1_IC1PSC_MASK; + ccmr1 |= (GTIM_CCMR_ICPSC_NOPSC << GTIM_CCMR1_IC1PSC_SHIFT); + stm32_putreg32(priv, STM32_GTIM_CCMR1_OFFSET, ccmr1); + + /* TI2 Channel Configuration */ + + /* Disable the Channel 2: Reset the CC2E Bit */ + + ccer = stm32_getreg16(priv, STM32_GTIM_CCER_OFFSET); + ccer &= ~GTIM_CCER_CC2E; + stm32_putreg16(priv, STM32_GTIM_CCER_OFFSET, ccer); + + ccmr1 = stm32_getreg32(priv, STM32_GTIM_CCMR1_OFFSET); + ccer = stm32_getreg16(priv, STM32_GTIM_CCER_OFFSET); + + /* Select the Input IC2=TI2 and set the filter fSAMPLING=fDTS/4, N=6 */ + + ccmr1 &= ~(GTIM_CCMR1_CC2S_MASK | GTIM_CCMR1_IC2F_MASK); + ccmr1 |= GTIM_CCMR_CCS_CCIN1 << GTIM_CCMR1_CC2S_SHIFT; + ccmr1 |= STM32F0L0G0_QENCODER_ICF << GTIM_CCMR1_IC2F_SHIFT; + + /* Select the Polarity=rising and set the CC2E Bit */ + + ccer &= ~(GTIM_CCER_CC2P | GTIM_CCER_CC2NP); + ccer |= GTIM_CCER_CC2E; + + /* Write to TIM CCMR1 and CCER registers */ + + stm32_putreg32(priv, STM32_GTIM_CCMR1_OFFSET, ccmr1); + stm32_putreg16(priv, STM32_GTIM_CCER_OFFSET, ccer); + + /* Set the Input Capture Prescaler value: Capture performed each time an + * edge is detected on the capture input. + */ + + ccmr1 = stm32_getreg32(priv, STM32_GTIM_CCMR1_OFFSET); + ccmr1 &= ~GTIM_CCMR1_IC2PSC_MASK; + ccmr1 |= (GTIM_CCMR_ICPSC_NOPSC << GTIM_CCMR1_IC2PSC_SHIFT); + stm32_putreg32(priv, STM32_GTIM_CCMR1_OFFSET, ccmr1); + + /* Disable the update interrupt */ + + dier = stm32_getreg16(priv, STM32_GTIM_DIER_OFFSET); + dier &= ~GTIM_DIER_UIE; + stm32_putreg16(priv, STM32_GTIM_DIER_OFFSET, dier); + + /* There is no need for interrupts with 32-bit timers */ + +#ifndef CONFIG_STM32F0L0G0_QENCODER_DISABLE_EXTEND16BTIMERS +#ifdef HAVE_MIXEDWIDTH_TIMERS + if (priv->config->width != 32) +#endif + { + /* Attach the interrupt handler */ + + ret = irq_attach(priv->config->irq, stm32_interrupt, priv); + if (ret < 0) + { + stm32_shutdown(lower); + return ret; + } + + /* Enable the update/global interrupt at the NVIC */ + + up_enable_irq(priv->config->irq); + } +#endif + + /* Reset the Update Disable Bit */ + + cr1 = stm32_getreg16(priv, STM32_GTIM_CR1_OFFSET); + cr1 &= ~GTIM_CR1_UDIS; + stm32_putreg16(priv, STM32_GTIM_CR1_OFFSET, cr1); + + /* Reset the URS Bit */ + + cr1 &= ~GTIM_CR1_URS; + stm32_putreg16(priv, STM32_GTIM_CR1_OFFSET, cr1); + + /* There is no need for interrupts with 32-bit timers */ + +#ifndef CONFIG_STM32F0L0G0_QENCODER_DISABLE_EXTEND16BTIMERS +#ifdef HAVE_MIXEDWIDTH_TIMERS + if (priv->config->width != 32) +#endif + { + /* Clear any pending update interrupts */ + + regval = stm32_getreg16(priv, STM32_GTIM_SR_OFFSET); + stm32_putreg16(priv, STM32_GTIM_SR_OFFSET, regval & ~GTIM_SR_UIF); + + /* Then enable the update interrupt */ + + dier = stm32_getreg16(priv, STM32_GTIM_DIER_OFFSET); + dier |= GTIM_DIER_UIE; + stm32_putreg16(priv, STM32_GTIM_DIER_OFFSET, dier); + } +#endif + +#ifdef CONFIG_STM32F0L0G0_QENCODER_INDEX_PIN + priv->index_offset = 0; +#endif + + /* Enable the TIM Counter */ + + cr1 = stm32_getreg16(priv, STM32_GTIM_CR1_OFFSET); + cr1 |= GTIM_CR1_CEN; + stm32_putreg16(priv, STM32_GTIM_CR1_OFFSET, cr1); + + stm32_dumpregs(priv, "After setup"); + + return OK; +} + +/**************************************************************************** + * Name: stm32_shutdown + * + * Description: + * This method is called when the driver is closed. The lower half driver + * should stop data collection, free any resources, disable timer hardware, + * and put the system into the lowest possible power usage state * + * + ****************************************************************************/ + +static int stm32_shutdown(struct qe_lowerhalf_s *lower) +{ + struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower; + irqstate_t flags; + uint32_t regaddr; + uint32_t regval; + uint32_t resetbit; + uint32_t pincfg; + + /* Disable the update/global interrupt at the NVIC */ + + flags = enter_critical_section(); + up_disable_irq(priv->config->irq); + + /* Detach the interrupt handler */ + + irq_detach(priv->config->irq); + + /* Disable interrupts momentary to stop any ongoing timer processing and + * to prevent any concurrent access to the reset register. + */ + + /* Disable further interrupts and stop the timer */ + + stm32_putreg16(priv, STM32_GTIM_DIER_OFFSET, 0); + stm32_putreg16(priv, STM32_GTIM_SR_OFFSET, 0); + + /* Determine which timer to reset */ + + switch (priv->config->timid) + { +#ifdef CONFIG_STM32F0L0G0_TIM1_QE + case 1: + regaddr = STM32_RCC_APB2RSTR; + resetbit = RCC_APB2RSTR_TIM1RST; + break; +#endif +#ifdef CONFIG_STM32F0L0G0_TIM2_QE + case 2: + regaddr = STM32_RCC_APB1RSTR; + resetbit = RCC_APB1RSTR_TIM2RST; + break; +#endif +#ifdef CONFIG_STM32F0L0G0_TIM3_QE + case 3: + regaddr = STM32_RCC_APB1RSTR; + resetbit = RCC_APB1RSTR_TIM3RST; + break; +#endif +#ifdef CONFIG_STM32F0L0G0_TIM4_QE + case 4: + regaddr = STM32_RCC_APB1RSTR; + resetbit = RCC_APB1RSTR_TIM4RST; + break; +#endif + default: + leave_critical_section(flags); + return -EINVAL; + } + + /* Reset the timer - stopping the output and putting the timer back + * into a state where stm32_start() can be called. + */ + + regval = getreg32(regaddr); + regval |= resetbit; + putreg32(regval, regaddr); + + regval &= ~resetbit; + putreg32(regval, regaddr); + leave_critical_section(flags); + + sninfo("regaddr: %08" PRIx32 " resetbit: %08" PRIx32 "\n", + regaddr, resetbit); + stm32_dumpregs(priv, "After stop"); + + /* Put the TI1 GPIO pin back to its default state */ + + pincfg = priv->config->ti1cfg & (GPIO_PORT_MASK | GPIO_PIN_MASK); + pincfg |= STM32F0L0G0_GPIO_INPUT_FLOAT; + + stm32_configgpio(pincfg); + + /* Put the TI2 GPIO pin back to its default state */ + + pincfg = priv->config->ti2cfg & (GPIO_PORT_MASK | GPIO_PIN_MASK); + pincfg |= STM32F0L0G0_GPIO_INPUT_FLOAT; + + stm32_configgpio(pincfg); + return OK; +} + +/**************************************************************************** + * Name: stm32_position + * + * Description: + * Return the current position measurement. + * + ****************************************************************************/ + +static int stm32_position(struct qe_lowerhalf_s *lower, int32_t *pos) +{ + struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower; +#ifndef CONFIG_STM32F0L0G0_QENCODER_DISABLE_EXTEND16BTIMERS + irqstate_t flags; + int32_t position; + int32_t verify; + uint32_t count; + + DEBUGASSERT(lower && priv->inuse); + + /* Loop until we are certain that no interrupt occurred between samples */ + + flags = spin_lock_irqsave(&priv->lock); + do + { + position = priv->position; + count = stm32_getreg32(priv, STM32_GTIM_CNT_OFFSET); + verify = priv->position; + } + while (position != verify); + spin_unlock_irqrestore(&priv->lock, flags); + + /* Return the position measurement */ + + *pos = position + (int32_t)count; +#else + /* Return the counter value */ + + *pos = (int32_t)stm32_getreg32(priv, STM32_GTIM_CNT_OFFSET); +#endif + return OK; +} + +/**************************************************************************** + * Name: stm32_reset + * + * Description: + * Reset the position measurement to zero. + * + ****************************************************************************/ + +static int stm32_reset(struct qe_lowerhalf_s *lower) +{ + struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower; +#ifndef CONFIG_STM32F0L0G0_QENCODER_DISABLE_EXTEND16BTIMERS + irqstate_t flags; + + sninfo("Resetting position to zero\n"); + DEBUGASSERT(lower && priv->inuse); + + /* Reset the timer and the counter. + * Interrupts are disabled to make this atomic (if possible) + */ + + flags = spin_lock_irqsave(&priv->lock); + stm32_putreg32(priv, STM32_GTIM_CNT_OFFSET, 0); + priv->position = 0; + spin_unlock_irqrestore(&priv->lock, flags); +#else + sninfo("Resetting position to zero\n"); + DEBUGASSERT(lower && priv->inuse); + + /* Reset the counter to zero */ + + stm32_putreg32(priv, STM32_GTIM_CNT_OFFSET, 0); +#endif + return OK; +} + +/**************************************************************************** + * Name: stm32_setposmax + * + * Description: + * Set the maximum encoder position. + * + ****************************************************************************/ + +static int stm32_setposmax(struct qe_lowerhalf_s *lower, uint32_t pos) +{ + struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower; + + DEBUGASSERT(lower && priv->inuse); + +#if defined(HAVE_MIXEDWIDTH_TIMERS) + if (priv->config->width == 32) + { + stm32_putreg32(priv, STM32_GTIM_ARR_OFFSET, pos); + } + else + { + stm32_putreg16(priv, STM32_GTIM_ARR_OFFSET, pos); + } +#elif defined(HAVE_32BIT_TIMERS) + stm32_putreg32(priv, STM32_GTIM_ARR_OFFSET, pos); +#else + stm32_putreg16(priv, STM32_GTIM_ARR_OFFSET, pos); +#endif + + return OK; +} + +/**************************************************************************** + * Name: stm32_setindex + * + * Description: + * Set the index pin position + * + ****************************************************************************/ + +static int stm32_setindex(struct qe_lowerhalf_s *lower, uint32_t pos) +{ +#ifdef CONFIG_STM32F0L0G0_QENCODER_INDEX_PIN + struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower; + int ret = OK; + + sninfo("Set QE TIM%d the index pin position %" PRIx32 "\n", + priv->config->timid, pos); + DEBUGASSERT(lower && priv->inuse); + + if (priv->index_use == false) + { + snerr("ERROR: QE TIM%d index not registered\n", + priv->config->timid); + ret = -EPERM; + goto errout; + } + + priv->index_offset = pos; + +errout: + return ret; +#else + return -ENOTTY; +#endif +} + +/**************************************************************************** + * Name: stm32_ioctl + * + * Description: + * Lower-half logic may support platform-specific ioctl commands + * + ****************************************************************************/ + +static int stm32_ioctl(struct qe_lowerhalf_s *lower, int cmd, + unsigned long arg) +{ + /* No ioctl commands supported */ + + /* TODO add an IOCTL to control the encoder pulse count prescaler */ + + return -ENOTTY; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_qeinitialize + * + * Description: + * Initialize a quadrature encoder interface. + * This function must be called from board-specific logic. + * + * Input Parameters: + * devpath - The full path to the driver to register. E.g., "/dev/qe0" + * tim - The timer number to used. 'tim' must be an element of + * {1,2,3,4} + * + * Returned Value: + * Zero on success; A negated errno value is returned on failure. + * + ****************************************************************************/ + +int stm32_qeinitialize(const char *devpath, int tim) +{ + struct stm32_lowerhalf_s *priv; + int ret; + + /* Find the pre-allocated timer state structure corresponding to this + * timer + */ + + priv = stm32_tim2lower(tim); + if (!priv) + { + snerr("ERROR: TIM%d support not configured\n", tim); + return -ENXIO; + } + + /* Make sure that it is available */ + + if (priv->inuse) + { + snerr("ERROR: TIM%d is in-use\n", tim); + return -EBUSY; + } + + /* Register the priv-half driver */ + + ret = qe_register(devpath, (struct qe_lowerhalf_s *)priv); + if (ret < 0) + { + snerr("ERROR: qe_register failed: %d\n", ret); + return ret; + } + + /* Make sure that the timer is in the shutdown state */ + + stm32_shutdown((struct qe_lowerhalf_s *)priv); + + /* The driver is now in-use */ + + priv->inuse = true; + return OK; +} + +#ifdef CONFIG_STM32F0L0G0_QENCODER_INDEX_PIN +/**************************************************************************** + * Name: stm32_qe_index_init + * + * Description: + * Register the encoder index pin to a given Qencoder timer + * + * Input Parameters: + * tim - The qenco timer number + * gpio - gpio pin configuration + * + * Returned Value: + * Zero on success; A negated errno value is returned on failure. + * + ****************************************************************************/ + +int stm32_qe_index_init(int tim, uint32_t gpio) +{ + struct stm32_lowerhalf_s *priv; + int ret = OK; + + priv = stm32_tim2lower(tim); + if (!priv) + { + snerr("ERROR: TIM%d support not configured\n", tim); + return -ENXIO; + } + + if (priv->inuse == false) + { + snerr("ERROR: TIM%d is not in-use\n", tim); + ret = -EINVAL; + } + + priv->index_pin = gpio; + stm32_configgpio(priv->index_pin); + + ret = stm32_gpiosetevent(gpio, true, false, true, + stm32_qe_index_irq, priv); + if (ret < 0) + { + snerr("ERROR: QE TIM%d failed register irq\n", tim); + goto errout; + } + + priv->index_use = true; + +errout: + return ret; +} +#endif diff --git a/arch/arm/src/stm32f0l0g0/stm32_qencoder.h b/arch/arm/src/stm32f0l0g0/stm32_qencoder.h new file mode 100644 index 00000000000..b6f1c4c9d9c --- /dev/null +++ b/arch/arm/src/stm32f0l0g0/stm32_qencoder.h @@ -0,0 +1,116 @@ +/**************************************************************************** + * arch/arm/src/stm32f0l0g0/stm32_qencoder.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 __ARCH_ARM_SRC_STM32F0L0G0_STM32_QENCODER_H +#define __ARCH_ARM_SRC_STM32F0L0G0_STM32_QENCODER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include "chip.h" + +#ifdef CONFIG_SENSORS_QENCODER + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Timer devices may be used for different purposes. One special purpose is + * as a quadrature encoder input device. If CONFIG_STM32F0L0G0_TIMn is + * defined then the CONFIG_STM32F0L0G0_TIMn_QE must also be defined to + * indicate that timer "n" is intended to be used for as a quadrature + * encoder. + */ + +#ifndef CONFIG_STM32F0L0G0_TIM1 +# undef CONFIG_STM32F0L0G0_TIM1_QE +#endif +#ifndef CONFIG_STM32F0L0G0_TIM2 +# undef CONFIG_STM32F0L0G0_TIM2_QE +#endif +#ifndef CONFIG_STM32F0L0G0_TIM3 +# undef CONFIG_STM32F0L0G0_TIM3_QE +#endif +#ifndef CONFIG_STM32F0L0G0_TIM4 +# undef CONFIG_STM32F0L0G0_TIM4_QE +#endif + +/* Only timers 1-4 can be used as a quadrature encoder (timers with + * encoder mode support). + * TIM6, TIM7, TIM14-17 are basic/general purpose timers without encoder + * capability. + */ + +#undef CONFIG_STM32F0L0G0_TIM6_QE +#undef CONFIG_STM32F0L0G0_TIM7_QE +#undef CONFIG_STM32F0L0G0_TIM14_QE +#undef CONFIG_STM32F0L0G0_TIM15_QE +#undef CONFIG_STM32F0L0G0_TIM16_QE +#undef CONFIG_STM32F0L0G0_TIM17_QE + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_qeinitialize + * + * Description: + * Initialize a quadrature encoder interface. + * This function must be called from board-specific logic. + * + * Input Parameters: + * devpath - The full path to the driver to register. E.g., "/dev/qe0" + * tim - The timer number to used. + * 'tim' must be an element of {1,2,3,4} + * + * Returned Value: + * Zero on success; A negated errno value is returned on failure. + * + ****************************************************************************/ + +int stm32_qeinitialize(const char *devpath, int tim); + +#ifdef CONFIG_STM32F0L0G0_QENCODER_INDEX_PIN +/**************************************************************************** + * Name: stm32_qe_index_init + * + * Description: + * Register the encoder index pin to a given Qencoder timer + * + * Input Parameters: + * tim - The qenco timer number + * gpio - gpio pin configuration + * + * Returned Value: + * Zero on success; A negated errno value is returned on failure. + * + ****************************************************************************/ + +int stm32_qe_index_init(int tim, uint32_t gpio); +#endif + +#endif /* CONFIG_SENSORS_QENCODER */ +#endif /* __ARCH_ARM_SRC_STM32F0L0G0_STM32_QENCODER_H */
