This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 7091ee3f5beccea0d64665b0c295a220efa30267
Author: Alan Carvalho de Assis <[email protected]>
AuthorDate: Mon Oct 12 15:27:38 2026 -0300

    boards/stm32f1: Add Digital Dream NVMPG pendant board.
    
    Add support for the Digital Dream NVMPG CNC pendant (nvmpg-stm32f1),
    used with the NVUM/NVEM/EC300/EC500 motion controllers.  It is based
    on an STM32F103RCT6 with an ILI9225 176x220 TFT on a 16-bit GPIO bus,
    10 keys and a USART1 link to the controller.
    
    The board provides:
    
    - An lcd_dev_s driver for the ILI9225 on the bit-banged 8080 16-bit
      bus (the upstream ILI9225 driver only supports SPI), registered as
      /dev/lcd0 in landscape mode (220x176).
    - The keys exported as /dev/gpio0..9.
    - The "dro" configuration, running the industry/cnc/nvmpg-dro
      application as replacement firmware for the pendant.
    
    Signed-off-by: Alan Carvalho de Assis <[email protected]>
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 boards/Kconfig                                     |  12 +
 boards/arm/stm32f1/nvmpg-stm32f1/CMakeLists.txt    |  23 +
 boards/arm/stm32f1/nvmpg-stm32f1/Kconfig           |  17 +
 .../stm32f1/nvmpg-stm32f1/configs/dro/defconfig    |  35 ++
 boards/arm/stm32f1/nvmpg-stm32f1/include/board.h   | 131 +++++
 boards/arm/stm32f1/nvmpg-stm32f1/scripts/Make.defs |  37 ++
 boards/arm/stm32f1/nvmpg-stm32f1/scripts/ld.script | 127 +++++
 .../arm/stm32f1/nvmpg-stm32f1/src/CMakeLists.txt   |  35 ++
 boards/arm/stm32f1/nvmpg-stm32f1/src/Make.defs     |  37 ++
 .../arm/stm32f1/nvmpg-stm32f1/src/nvmpg-stm32f1.h  | 105 ++++
 boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_boot.c  |  75 +++
 .../arm/stm32f1/nvmpg-stm32f1/src/stm32_bringup.c  |  86 ++++
 boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_gpio.c  | 123 +++++
 .../arm/stm32f1/nvmpg-stm32f1/src/stm32_ili9225.c  | 553 +++++++++++++++++++++
 14 files changed, 1396 insertions(+)

diff --git a/boards/Kconfig b/boards/Kconfig
index 045d03e09ee..fbcc1c550e1 100644
--- a/boards/Kconfig
+++ b/boards/Kconfig
@@ -1967,6 +1967,14 @@ config ARCH_BOARD_NUCLEO_F103RB
        ---help---
                STMicro Nucleo F103RB board based on the STMicro STM32F103RBT6 
MCU.
 
+config ARCH_BOARD_NVMPG_STM32F1
+       bool "Digital Dream NVMPG CNC pendant (STM32F103RC)"
+       depends on ARCH_CHIP_STM32F103RC
+       ---help---
+               Digital Dream NVMPG pendant for NVUM/NVEM/EC300/EC500 CNC motion
+               controllers: STM32F103RCT6, ILI9225 176x220 TFT on a 16-bit GPIO
+               bus, 10 keys and a USART1 link to the controller.
+
 config ARCH_BOARD_NUCLEO_F207ZG
        bool "STM32F207 Nucleo F207ZG"
        depends on ARCH_CHIP_STM32F207ZG
@@ -4211,6 +4219,7 @@ config ARCH_BOARD
        default "nucleo-f072rb"                if ARCH_BOARD_NUCLEO_F072RB
        default "nucleo-f091rc"                if ARCH_BOARD_NUCLEO_F091RC
        default "nucleo-f103rb"                if ARCH_BOARD_NUCLEO_F103RB
+       default "nvmpg-stm32f1"                if ARCH_BOARD_NVMPG_STM32F1
        default "nucleo-f207zg"                if ARCH_BOARD_NUCLEO_F207ZG
        default "nucleo-f302r8"                if ARCH_BOARD_NUCLEO_F302R8
        default "nucleo-f303re"                if ARCH_BOARD_NUCLEO_F303RE
@@ -5180,6 +5189,9 @@ endif
 if ARCH_BOARD_NUCLEO_F103RB
 source "boards/arm/stm32f1/nucleo-f103rb/Kconfig"
 endif
+if ARCH_BOARD_NVMPG_STM32F1
+source "boards/arm/stm32f1/nvmpg-stm32f1/Kconfig"
+endif
 if ARCH_BOARD_NUCLEO_F207ZG
 source "boards/arm/stm32f2/nucleo-f207zg/Kconfig"
 endif
diff --git a/boards/arm/stm32f1/nvmpg-stm32f1/CMakeLists.txt 
b/boards/arm/stm32f1/nvmpg-stm32f1/CMakeLists.txt
new file mode 100644
index 00000000000..b2674c97fe4
--- /dev/null
+++ b/boards/arm/stm32f1/nvmpg-stm32f1/CMakeLists.txt
@@ -0,0 +1,23 @@
+# 
##############################################################################
+# boards/arm/stm32f1/nvmpg-stm32f1/CMakeLists.txt
+#
+# 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.
+#
+# 
##############################################################################
+
+add_subdirectory(src)
diff --git a/boards/arm/stm32f1/nvmpg-stm32f1/Kconfig 
b/boards/arm/stm32f1/nvmpg-stm32f1/Kconfig
new file mode 100644
index 00000000000..1a8751f85ad
--- /dev/null
+++ b/boards/arm/stm32f1/nvmpg-stm32f1/Kconfig
@@ -0,0 +1,17 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+if ARCH_BOARD_NVMPG_STM32F1
+
+config NVMPG_STM32F1_HSE_8MHZ
+       bool "Clock from an 8MHz HSE crystal (72MHz)"
+       default n
+       ---help---
+               The original NVMPG firmware clocks the PLL from HSI/2, so the
+               crystal fitted on the board is not known.  By default NuttX does
+               the same (HSI/2 x 16 = 64MHz).  Select this if the board has an
+               8MHz crystal to run at 72MHz from the HSE instead.
+
+endif # ARCH_BOARD_NVMPG_STM32F1
diff --git a/boards/arm/stm32f1/nvmpg-stm32f1/configs/dro/defconfig 
b/boards/arm/stm32f1/nvmpg-stm32f1/configs/dro/defconfig
new file mode 100644
index 00000000000..3679dc66d44
--- /dev/null
+++ b/boards/arm/stm32f1/nvmpg-stm32f1/configs/dro/defconfig
@@ -0,0 +1,35 @@
+#
+# 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_DISABLE_PTHREAD is not set
+CONFIG_ARCH="arm"
+CONFIG_ARCH_BOARD="nvmpg-stm32f1"
+CONFIG_ARCH_BOARD_NVMPG_STM32F1=y
+CONFIG_ARCH_CHIP="stm32f1"
+CONFIG_ARCH_CHIP_STM32=y
+CONFIG_ARCH_CHIP_STM32F103RC=y
+CONFIG_ARCH_CHIP_STM32F1=y
+CONFIG_BOARD_LOOPSPERMSEC=5483
+CONFIG_DEFAULT_SMALL=y
+CONFIG_DEV_GPIO=y
+CONFIG_INDUSTRY_CNC_NVMPG_DRO=y
+CONFIG_INIT_ENTRYPOINT="nvmpg_dro_main"
+CONFIG_INTELHEX_BINARY=y
+CONFIG_LCD=y
+CONFIG_LCD_DEV=y
+CONFIG_LCD_NOGETRUN=y
+CONFIG_RAM_SIZE=49152
+CONFIG_RAM_START=0x20000000
+CONFIG_RAW_BINARY=y
+CONFIG_SERIAL_TERMIOS=y
+CONFIG_START_DAY=12
+CONFIG_START_MONTH=10
+CONFIG_START_YEAR=2026
+CONFIG_STM32_JTAG_SW_ENABLE=y
+CONFIG_STM32_PWR=y
+CONFIG_STM32_USART1=y
+CONFIG_TASK_NAME_SIZE=0
diff --git a/boards/arm/stm32f1/nvmpg-stm32f1/include/board.h 
b/boards/arm/stm32f1/nvmpg-stm32f1/include/board.h
new file mode 100644
index 00000000000..2a0565d2832
--- /dev/null
+++ b/boards/arm/stm32f1/nvmpg-stm32f1/include/board.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+ * boards/arm/stm32f1/nvmpg-stm32f1/include/board.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_STM32F1_NVMPG_STM32F1_INCLUDE_BOARD_H
+#define __BOARDS_ARM_STM32F1_NVMPG_STM32F1_INCLUDE_BOARD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#ifndef __ASSEMBLY__
+#  include <stdint.h>
+#  include <stdbool.h>
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Clocking *****************************************************************/
+
+/* The original NVMPG firmware runs the PLL from HSI/2 even though it starts
+ * the HSE, so the crystal value is not known for sure.  By default we do the
+ * same thing (HSI/2 x 16 = 64MHz), which works whatever crystal is fitted.
+ * Select CONFIG_NVMPG_STM32F1_HSE_8MHZ if the board has an 8MHz crystal to
+ * run at 72MHz.
+ */
+
+#define STM32_HSI_FREQUENCY     8000000ul
+#define STM32_LSI_FREQUENCY     40000            /* Between 30kHz and 60kHz */
+#define STM32_LSE_FREQUENCY     32768
+
+#ifdef CONFIG_NVMPG_STM32F1_HSE_8MHZ
+#  define STM32_BOARD_XTAL      8000000ul
+#  define STM32_HSE_FREQUENCY   STM32_BOARD_XTAL
+
+/* PLL source is HSE/1, PLL multiplier is 9: 8MHz x 9 = 72MHz */
+
+#  define STM32_CFGR_PLLSRC     RCC_CFGR_PLLSRC
+#  define STM32_CFGR_PLLXTPRE   0
+#  define STM32_CFGR_PLLMUL     RCC_CFGR_PLLMUL_CLKx9
+#  define STM32_PLL_FREQUENCY   (9*STM32_BOARD_XTAL)
+#else
+#  define STM32_HSE_FREQUENCY   0
+
+/* PLL source is HSI/2, PLL multiplier is 16: 4MHz x 16 = 64MHz */
+
+#  define STM32_CFGR_PLLSRC     0
+#  define STM32_CFGR_PLLXTPRE   0
+#  define STM32_CFGR_PLLMUL     RCC_CFGR_PLLMUL_CLKx16
+#  define STM32_PLL_FREQUENCY   (16*STM32_HSI_FREQUENCY/2)
+#endif
+
+/* Use the PLL and set the SYSCLK source to be the PLL */
+
+#define STM32_SYSCLK_SW         RCC_CFGR_SW_PLL
+#define STM32_SYSCLK_SWS        RCC_CFGR_SWS_PLL
+#define STM32_SYSCLK_FREQUENCY  STM32_PLL_FREQUENCY
+
+/* AHB clock (HCLK) is SYSCLK */
+
+#define STM32_RCC_CFGR_HPRE     RCC_CFGR_HPRE_SYSCLK
+#define STM32_HCLK_FREQUENCY    STM32_SYSCLK_FREQUENCY
+
+/* APB2 clock (PCLK2) is HCLK */
+
+#define STM32_RCC_CFGR_PPRE2    RCC_CFGR_PPRE2_HCLK
+#define STM32_PCLK2_FREQUENCY   STM32_HCLK_FREQUENCY
+#define STM32_APB2_CLKIN        (STM32_PCLK2_FREQUENCY)
+
+/* APB1 clock (PCLK1) is HCLK/2 */
+
+#define STM32_RCC_CFGR_PPRE1    RCC_CFGR_PPRE1_HCLKd2
+#define STM32_PCLK1_FREQUENCY   (STM32_HCLK_FREQUENCY/2)
+
+/* Timers */
+
+#define STM32_TIM1_CLKIN        (STM32_PCLK2_FREQUENCY)
+#define STM32_TIM8_CLKIN        (STM32_PCLK2_FREQUENCY)
+#define STM32_TIM2_CLKIN        (2*STM32_PCLK1_FREQUENCY)
+#define STM32_TIM3_CLKIN        (2*STM32_PCLK1_FREQUENCY)
+#define STM32_TIM4_CLKIN        (2*STM32_PCLK1_FREQUENCY)
+#define STM32_TIM5_CLKIN        (2*STM32_PCLK1_FREQUENCY)
+#define STM32_TIM6_CLKIN        (2*STM32_PCLK1_FREQUENCY)
+#define STM32_TIM7_CLKIN        (2*STM32_PCLK1_FREQUENCY)
+
+/* LCD **********************************************************************/
+
+/* ILI9225 176x220 TFT used in landscape: 220 columns x 176 rows */
+
+#define NVMPG_LCD_XRES          220
+#define NVMPG_LCD_YRES          176
+
+/* Keys *********************************************************************/
+
+/* Ten push buttons, active low.  The original firmware sends key index + 2
+ * to the controller when a key is pressed, and (index + 2) | 0x80 when it is
+ * released.  They are exported as /dev/gpio0 .. /dev/gpio9.
+ */
+
+#define NVMPG_NKEYS             10
+
+/* Alternate function pin selections ****************************************/
+
+/* USART1: link to the NVUM/NVEM controller (PA9 = TX, PA10 = RX) */
+
+#define GPIO_USART1_TX          GPIO_ADJUST_MODE(GPIO_USART1_TX_0, 
GPIO_MODE_50MHz)
+#define GPIO_USART1_RX          GPIO_USART1_RX_0
+
+#endif /* __BOARDS_ARM_STM32F1_NVMPG_STM32F1_INCLUDE_BOARD_H */
diff --git a/boards/arm/stm32f1/nvmpg-stm32f1/scripts/Make.defs 
b/boards/arm/stm32f1/nvmpg-stm32f1/scripts/Make.defs
new file mode 100644
index 00000000000..297dfb9c8d9
--- /dev/null
+++ b/boards/arm/stm32f1/nvmpg-stm32f1/scripts/Make.defs
@@ -0,0 +1,37 @@
+############################################################################
+# boards/arm/stm32f1/nvmpg-stm32f1/scripts/Make.defs
+#
+# 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.
+#
+############################################################################
+
+include $(TOPDIR)/.config
+include $(TOPDIR)/tools/Config.mk
+include $(TOPDIR)/arch/arm/src/armv7-m/Toolchain.defs
+
+LDSCRIPT = ld.script
+ARCHSCRIPT += $(BOARD_DIR)$(DELIM)scripts$(DELIM)$(LDSCRIPT)
+
+CFLAGS := $(ARCHCFLAGS) $(ARCHOPTIMIZATION) $(ARCHCPUFLAGS) $(ARCHINCLUDES) 
$(ARCHDEFINES) $(EXTRAFLAGS)
+CXXFLAGS := $(ARCHCXXFLAGS) $(ARCHOPTIMIZATION) $(ARCHCPUFLAGS) 
$(ARCHXXINCLUDES) $(ARCHDEFINES) $(EXTRAFLAGS)
+CPPFLAGS := $(ARCHINCLUDES) $(ARCHDEFINES) $(EXTRAFLAGS)
+AFLAGS := $(CFLAGS) -D__ASSEMBLY__
+
+NXFLATLDFLAGS1 = -r -d -warn-common
+NXFLATLDFLAGS2 = $(NXFLATLDFLAGS1) 
-T$(TOPDIR)/binfmt/libnxflat/gnu-nxflat-pcrel.ld -no-check-sections
+LDNXFLATFLAGS = -e main -s 2048
diff --git a/boards/arm/stm32f1/nvmpg-stm32f1/scripts/ld.script 
b/boards/arm/stm32f1/nvmpg-stm32f1/scripts/ld.script
new file mode 100644
index 00000000000..2da7a68ed10
--- /dev/null
+++ b/boards/arm/stm32f1/nvmpg-stm32f1/scripts/ld.script
@@ -0,0 +1,127 @@
+/****************************************************************************
+ * boards/arm/stm32f1/nvmpg-stm32f1/scripts/ld.script
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/* The STM32F103RCT6 has 256Kb of FLASH beginning at address 0x0800:0000 and
+ * 48Kb of SRAM.
+ *
+ * When booting from FLASH, FLASH memory is aliased to address 0x0000:0000
+ * where the code expects to begin execution by jumping to the entry point in
+ * the 0x0800:0000 address range.
+ */
+
+MEMORY
+{
+    flash (rx) : ORIGIN = 0x08000000, LENGTH = 256K
+    sram (rwx) : ORIGIN = 0x20000000, LENGTH = 48K
+}
+
+OUTPUT_ARCH(arm)
+EXTERN(_vectors)
+ENTRY(_stext)
+SECTIONS
+{
+    .text : {
+    _stext = ABSOLUTE(.);
+        *(.vectors)
+        *(.text .text.*)
+        *(.fixup)
+        *(.gnu.warning)
+        *(.rodata .rodata.*)
+        *(.gnu.linkonce.t.*)
+        *(.glue_7)
+        *(.glue_7t)
+        *(.got)
+        *(.gcc_except_table)
+        *(.gnu.linkonce.r.*)
+        _etext = ABSOLUTE(.);
+    } > flash
+
+    .init_section : ALIGN(4) {
+        _sinit = ABSOLUTE(.);
+        KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) 
SORT_BY_INIT_PRIORITY(.ctors.*)))
+        KEEP(*(.init_array EXCLUDE_FILE(*crtbegin.o *crtbegin?.o *crtend.o 
*crtend?.o) .ctors))
+        _einit = ABSOLUTE(.);
+    } > flash
+
+    .ARM.extab : ALIGN(4) {
+        *(.ARM.extab*)
+    } > flash
+
+    .ARM.exidx : ALIGN(4) {
+        __exidx_start = ABSOLUTE(.);
+        *(.ARM.exidx*)
+        __exidx_end = ABSOLUTE(.);
+    } > flash
+
+    .tdata : {
+        _stdata = ABSOLUTE(.);
+        *(.tdata .tdata.* .gnu.linkonce.td.*);
+        _etdata = ABSOLUTE(.);
+    } > flash
+
+    .tbss : {
+        _stbss = ABSOLUTE(.);
+        *(.tbss .tbss.* .gnu.linkonce.tb.* .tcommon);
+        _etbss = ABSOLUTE(.);
+    } > flash
+
+    _eronly = ABSOLUTE(.);
+
+    /* The RAM vector table (if present) should lie at the beginning of SRAM */
+
+    .ram_vectors : {
+        *(.ram_vectors)
+    } > sram
+
+    .data : ALIGN(4) {
+        _sdata = ABSOLUTE(.);
+        *(.data .data.*)
+        *(.gnu.linkonce.d.*)
+        CONSTRUCTORS
+        . = ALIGN(4);
+        _edata = ABSOLUTE(.);
+    } > sram AT > flash
+
+    .bss : ALIGN(4) {
+        _sbss = ABSOLUTE(.);
+        *(.bss .bss.*)
+        *(.gnu.linkonce.b.*)
+        *(COMMON)
+        . = ALIGN(4);
+        _ebss = ABSOLUTE(.);
+    } > sram
+
+    /* Stabs debugging sections. */
+
+    .stab 0 : { *(.stab) }
+    .stabstr 0 : { *(.stabstr) }
+    .stab.excl 0 : { *(.stab.excl) }
+    .stab.exclstr 0 : { *(.stab.exclstr) }
+    .stab.index 0 : { *(.stab.index) }
+    .stab.indexstr 0 : { *(.stab.indexstr) }
+    .comment 0 : { *(.comment) }
+    .debug_abbrev 0 : { *(.debug_abbrev) }
+    .debug_info 0 : { *(.debug_info) }
+    .debug_line 0 : { *(.debug_line) }
+    .debug_pubnames 0 : { *(.debug_pubnames) }
+    .debug_aranges 0 : { *(.debug_aranges) }
+}
diff --git a/boards/arm/stm32f1/nvmpg-stm32f1/src/CMakeLists.txt 
b/boards/arm/stm32f1/nvmpg-stm32f1/src/CMakeLists.txt
new file mode 100644
index 00000000000..d8190dfe88e
--- /dev/null
+++ b/boards/arm/stm32f1/nvmpg-stm32f1/src/CMakeLists.txt
@@ -0,0 +1,35 @@
+# 
##############################################################################
+# boards/arm/stm32f1/nvmpg-stm32f1/src/CMakeLists.txt
+#
+# 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.
+#
+# 
##############################################################################
+
+set(SRCS stm32_boot.c stm32_bringup.c)
+
+if(CONFIG_DEV_GPIO)
+  list(APPEND SRCS stm32_gpio.c)
+endif()
+
+if(CONFIG_LCD)
+  list(APPEND SRCS stm32_ili9225.c)
+endif()
+
+target_sources(board PRIVATE ${SRCS})
+
+set_property(GLOBAL PROPERTY LD_SCRIPT "${NUTTX_BOARD_DIR}/scripts/ld.script")
diff --git a/boards/arm/stm32f1/nvmpg-stm32f1/src/Make.defs 
b/boards/arm/stm32f1/nvmpg-stm32f1/src/Make.defs
new file mode 100644
index 00000000000..3bbd98f4d8c
--- /dev/null
+++ b/boards/arm/stm32f1/nvmpg-stm32f1/src/Make.defs
@@ -0,0 +1,37 @@
+############################################################################
+# boards/arm/stm32f1/nvmpg-stm32f1/src/Make.defs
+#
+# 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.
+#
+############################################################################
+
+include $(TOPDIR)/Make.defs
+
+CSRCS = stm32_boot.c stm32_bringup.c
+
+ifeq ($(CONFIG_DEV_GPIO),y)
+CSRCS += stm32_gpio.c
+endif
+
+ifeq ($(CONFIG_LCD),y)
+CSRCS += stm32_ili9225.c
+endif
+
+DEPPATH += --dep-path board
+VPATH += :board
+CFLAGS += 
${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)board$(DELIM)board
diff --git a/boards/arm/stm32f1/nvmpg-stm32f1/src/nvmpg-stm32f1.h 
b/boards/arm/stm32f1/nvmpg-stm32f1/src/nvmpg-stm32f1.h
new file mode 100644
index 00000000000..97ef05da013
--- /dev/null
+++ b/boards/arm/stm32f1/nvmpg-stm32f1/src/nvmpg-stm32f1.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+ * boards/arm/stm32f1/nvmpg-stm32f1/src/nvmpg-stm32f1.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_STM32F1_NVMPG_STM32F1_SRC_NVMPG_STM32F1_H
+#define __BOARDS_ARM_STM32F1_NVMPG_STM32F1_SRC_NVMPG_STM32F1_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <arch/board/board.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* LCD: ILI9225 on a bit-banged 8080 16-bit bus *****************************
+ *
+ *   D0..D15  PB0..PB15  (needs JTAG disabled to free PB3/PB4; SWD is kept)
+ *   /WR      PC10
+ *   RS       PC11       (0 = index register, 1 = data)
+ *   /CS      PC12
+ *   /RD      PA15
+ *   /RESET   not connected to the MCU
+ */
+
+#define GPIO_LCD_DATA   (GPIO_OUTPUT|GPIO_CNF_OUTPP|GPIO_MODE_50MHz|\
+                         GPIO_OUTPUT_SET|GPIO_PORTB)
+#define GPIO_LCD_WR     (GPIO_OUTPUT|GPIO_CNF_OUTPP|GPIO_MODE_50MHz|\
+                         GPIO_OUTPUT_SET|GPIO_PORTC|GPIO_PIN10)
+#define GPIO_LCD_RS     (GPIO_OUTPUT|GPIO_CNF_OUTPP|GPIO_MODE_50MHz|\
+                         GPIO_OUTPUT_SET|GPIO_PORTC|GPIO_PIN11)
+#define GPIO_LCD_CS     (GPIO_OUTPUT|GPIO_CNF_OUTPP|GPIO_MODE_50MHz|\
+                         GPIO_OUTPUT_SET|GPIO_PORTC|GPIO_PIN12)
+#define GPIO_LCD_RD     (GPIO_OUTPUT|GPIO_CNF_OUTPP|GPIO_MODE_50MHz|\
+                         GPIO_OUTPUT_SET|GPIO_PORTA|GPIO_PIN15)
+
+/* Keys: active low, external pull-ups (floating inputs).  The order below
+ * matches the key index used by the original firmware (code = index + 2).
+ */
+
+#define GPIO_KEY(port, pin) (GPIO_INPUT|GPIO_CNF_INFLOAT|GPIO_MODE_INPUT|\
+                             (port)|(pin))
+
+#define GPIO_KEY0       GPIO_KEY(GPIO_PORTA, GPIO_PIN2)   /* Axis Up    0x02 */
+#define GPIO_KEY1       GPIO_KEY(GPIO_PORTA, GPIO_PIN1)   /* Axis Down  0x03 */
+#define GPIO_KEY2       GPIO_KEY(GPIO_PORTA, GPIO_PIN0)   /* Spindle    0x04 */
+#define GPIO_KEY3       GPIO_KEY(GPIO_PORTC, GPIO_PIN3)   /* Step x1..  0x05 */
+#define GPIO_KEY4       GPIO_KEY(GPIO_PORTC, GPIO_PIN2)   /* Home       0x06 */
+#define GPIO_KEY5       GPIO_KEY(GPIO_PORTC, GPIO_PIN1)   /* GOTOZ      0x07 */
+#define GPIO_KEY6       GPIO_KEY(GPIO_PORTC, GPIO_PIN0)   /* Zero       0x08 */
+#define GPIO_KEY7       GPIO_KEY(GPIO_PORTC, GPIO_PIN15)  /* 1/2        0x09 */
+#define GPIO_KEY8       GPIO_KEY(GPIO_PORTC, GPIO_PIN14)  /* Side right 0x0a */
+#define GPIO_KEY9       GPIO_KEY(GPIO_PORTA, GPIO_PIN3)   /* Side left  0x0b */
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_bringup
+ *
+ * Description:
+ *   Perform architecture-specific initialization.  Called from
+ *   board_late_initialize() (CONFIG_BOARD_LATE_INITIALIZE=y) or from
+ *   board_app_initialize().
+ *
+ ****************************************************************************/
+
+int stm32_bringup(void);
+
+/****************************************************************************
+ * Name: stm32_gpio_initialize
+ *
+ * Description:
+ *   Register the keys as /dev/gpio0 .. /dev/gpio9.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEV_GPIO
+int stm32_gpio_initialize(void);
+#endif
+
+#endif /* __BOARDS_ARM_STM32F1_NVMPG_STM32F1_SRC_NVMPG_STM32F1_H */
diff --git a/boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_boot.c 
b/boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_boot.c
new file mode 100644
index 00000000000..09280744cd3
--- /dev/null
+++ b/boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_boot.c
@@ -0,0 +1,75 @@
+/****************************************************************************
+ * boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_boot.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 <nuttx/board.h>
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "stm32_dbgmcu.h"
+#include "nvmpg-stm32f1.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_boardinitialize
+ *
+ * Description:
+ *   All STM32 architectures must provide the following entry point.  This
+ *   entry point is called early in the initialization -- after all memory
+ *   has been configured and mapped but before any devices have been
+ *   initialized.
+ *
+ ****************************************************************************/
+
+void stm32_boardinitialize(void)
+{
+  /* Keep the debug clocks running while the idle loop sleeps in WFI, so
+   * that SWD can attach to the running firmware.  The pendant is always
+   * externally powered, so the extra current does not matter.
+   */
+
+  modifyreg32(STM32_DBGMCU_CR, 0, DBGMCU_CR_SLEEP);
+}
+
+/****************************************************************************
+ * Name: board_late_initialize
+ *
+ * Description:
+ *   Called immediately after up_initialize() and just before the initial
+ *   application is started (CONFIG_BOARD_LATE_INITIALIZE=y).
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_BOARD_LATE_INITIALIZE
+void board_late_initialize(void)
+{
+  stm32_bringup();
+}
+#endif
diff --git a/boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_bringup.c 
b/boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_bringup.c
new file mode 100644
index 00000000000..a196f72a651
--- /dev/null
+++ b/boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_bringup.c
@@ -0,0 +1,86 @@
+/****************************************************************************
+ * boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_bringup.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 <sys/types.h>
+#include <syslog.h>
+
+#include <nuttx/board.h>
+
+#ifdef CONFIG_LCD_DEV
+#  include <nuttx/lcd/lcd_dev.h>
+#endif
+
+#include "nvmpg-stm32f1.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_bringup
+ *
+ * Description:
+ *   Perform architecture-specific initialization
+ *
+ ****************************************************************************/
+
+int stm32_bringup(void)
+{
+  int ret = OK;
+
+#ifdef CONFIG_DEV_GPIO
+  /* Keys as /dev/gpio0 .. /dev/gpio9 */
+
+  ret = stm32_gpio_initialize();
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: stm32_gpio_initialize() failed: %d\n", ret);
+    }
+#endif
+
+#ifdef CONFIG_LCD
+  ret = board_lcd_initialize();
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: board_lcd_initialize() failed: %d\n", ret);
+    }
+
+#  ifdef CONFIG_LCD_DEV
+  /* LCD character driver as /dev/lcd0 */
+
+  ret = lcddev_register(0);
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: lcddev_register() failed: %d\n", ret);
+    }
+#  endif
+#endif
+
+  UNUSED(ret);
+  return OK;
+}
diff --git a/boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_gpio.c 
b/boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_gpio.c
new file mode 100644
index 00000000000..64982444108
--- /dev/null
+++ b/boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_gpio.c
@@ -0,0 +1,123 @@
+/****************************************************************************
+ * boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_gpio.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.
+ *
+ ****************************************************************************/
+
+/* The ten pendant keys exported as /dev/gpio0 .. /dev/gpio9 (inputs, true
+ * means released).  PA0..PA3 and PC0..PC3 share EXTI lines, so the keys
+ * cannot all be IRQ buttons and the application polls them instead.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <assert.h>
+
+#include <nuttx/ioexpander/gpio.h>
+
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "stm32.h"
+#include "nvmpg-stm32f1.h"
+
+#if defined(CONFIG_DEV_GPIO) && !defined(CONFIG_GPIO_LOWER_HALF)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32gpio_dev_s
+{
+  struct gpio_dev_s gpio;
+  uint8_t id;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int gpin_read(FAR struct gpio_dev_s *dev, FAR bool *value);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct gpio_operations_s gpin_ops =
+{
+  .go_read   = gpin_read,
+  .go_write  = NULL,
+  .go_attach = NULL,
+  .go_enable = NULL,
+};
+
+static const uint32_t g_keys[NVMPG_NKEYS] =
+{
+  GPIO_KEY0, GPIO_KEY1, GPIO_KEY2, GPIO_KEY3, GPIO_KEY4,
+  GPIO_KEY5, GPIO_KEY6, GPIO_KEY7, GPIO_KEY8, GPIO_KEY9,
+};
+
+static struct stm32gpio_dev_s g_gpin[NVMPG_NKEYS];
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int gpin_read(FAR struct gpio_dev_s *dev, FAR bool *value)
+{
+  FAR struct stm32gpio_dev_s *stm32gpio = (FAR struct stm32gpio_dev_s *)dev;
+
+  DEBUGASSERT(stm32gpio != NULL && value != NULL);
+  DEBUGASSERT(stm32gpio->id < NVMPG_NKEYS);
+
+  *value = stm32_gpioread(g_keys[stm32gpio->id]);
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_gpio_initialize
+ ****************************************************************************/
+
+int stm32_gpio_initialize(void)
+{
+  int i;
+
+  for (i = 0; i < NVMPG_NKEYS; i++)
+    {
+      stm32_configgpio(g_keys[i]);
+
+      g_gpin[i].gpio.gp_pintype = GPIO_INPUT_PIN;
+      g_gpin[i].gpio.gp_ops     = &gpin_ops;
+      g_gpin[i].id              = i;
+      gpio_pin_register(&g_gpin[i].gpio, i);
+    }
+
+  return OK;
+}
+
+#endif /* CONFIG_DEV_GPIO && !CONFIG_GPIO_LOWER_HALF */
diff --git a/boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_ili9225.c 
b/boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_ili9225.c
new file mode 100644
index 00000000000..2bef0750862
--- /dev/null
+++ b/boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_ili9225.c
@@ -0,0 +1,553 @@
+/****************************************************************************
+ * boards/arm/stm32f1/nvmpg-stm32f1/src/stm32_ili9225.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.
+ *
+ ****************************************************************************/
+
+/* ILI9225 176x220 TFT on a GPIO bit-banged 8080 16-bit bus.
+ *
+ * The upstream drivers/lcd/ili9225.c only supports SPI, so this board
+ * provides its own lcd_dev_s.  The panel is used in landscape (220x176):
+ *
+ *   GRAM vertical address (0..219)   = screen column (x)
+ *   GRAM horizontal address (0..175) = 175 - screen row (y)
+ *
+ * which is the same orientation as the original NVMPG firmware.  Entry mode
+ * AM=1 makes the address counter move along the vertical (x) direction
+ * first, so a screen raster line is one contiguous GRAM write.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/board.h>
+#include <nuttx/irq.h>
+#include <sys/param.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/video/fb.h>
+
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "stm32.h"
+#include "nvmpg-stm32f1.h"
+
+#ifdef CONFIG_LCD
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define LCD_XRES        NVMPG_LCD_XRES
+#define LCD_YRES        NVMPG_LCD_YRES
+#define LCD_BPP         16
+
+/* Control pin masks (GPIOA/GPIOC BSRR/BRR) */
+
+#define LCD_WR_BIT      (1 << 10)  /* PC10 */
+#define LCD_RS_BIT      (1 << 11)  /* PC11 */
+#define LCD_CS_BIT      (1 << 12)  /* PC12 */
+#define LCD_RD_BIT      (1 << 15)  /* PA15 */
+
+/* ILI9225 registers used here */
+
+#define ILI9225_DRIVER_OUTPUT   0x01
+#define ILI9225_LCD_AC_DRIVING  0x02
+#define ILI9225_ENTRY_MODE      0x03
+#define ILI9225_DISP_CTRL1      0x07
+#define ILI9225_BLANK_PERIOD    0x08
+#define ILI9225_FRAME_CYCLE     0x0b
+#define ILI9225_INTERFACE_CTRL  0x0c
+#define ILI9225_OSC_CTRL        0x0f
+#define ILI9225_POWER_CTRL1     0x10
+#define ILI9225_POWER_CTRL2     0x11
+#define ILI9225_POWER_CTRL3     0x12
+#define ILI9225_POWER_CTRL4     0x13
+#define ILI9225_POWER_CTRL5     0x14
+#define ILI9225_VCI_RECYCLING   0x15
+#define ILI9225_RAM_ADDR_H      0x20
+#define ILI9225_RAM_ADDR_V      0x21
+#define ILI9225_GRAM_DATA       0x22
+#define ILI9225_HWIN_END        0x36
+#define ILI9225_HWIN_START      0x37
+#define ILI9225_VWIN_END        0x38
+#define ILI9225_VWIN_START      0x39
+
+/* Entry mode: BGR=1, I/D=10 (V increment, H decrement), AM=1 */
+
+#define ILI9225_ENTRY_LANDSCAPE 0x1028
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct nvmpg_lcd_s
+{
+  struct lcd_dev_s dev;   /* Publicly visible device structure */
+  uint8_t power;          /* Current power setting */
+  bool initialized;
+  uint16_t runbuffer[LCD_XRES];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int lcd_putrun(FAR struct lcd_dev_s *dev, fb_coord_t row,
+                      fb_coord_t col, FAR const uint8_t *buffer,
+                      size_t npixels);
+static int lcd_putarea(FAR struct lcd_dev_s *dev, fb_coord_t row_start,
+                       fb_coord_t row_end, fb_coord_t col_start,
+                       fb_coord_t col_end, FAR const uint8_t *buffer,
+                       fb_coord_t stride);
+static int lcd_getvideoinfo(FAR struct lcd_dev_s *dev,
+                            FAR struct fb_videoinfo_s *vinfo);
+static int lcd_getplaneinfo(FAR struct lcd_dev_s *dev, unsigned int planeno,
+                            FAR struct lcd_planeinfo_s *pinfo);
+static int lcd_getpower(FAR struct lcd_dev_s *dev);
+static int lcd_setpower(FAR struct lcd_dev_s *dev, int power);
+static int lcd_getcontrast(FAR struct lcd_dev_s *dev);
+static int lcd_setcontrast(FAR struct lcd_dev_s *dev,
+                           unsigned int contrast);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct nvmpg_lcd_s g_lcd =
+{
+  .dev =
+  {
+    .getvideoinfo = lcd_getvideoinfo,
+    .getplaneinfo = lcd_getplaneinfo,
+    .getpower     = lcd_getpower,
+    .setpower     = lcd_setpower,
+    .getcontrast  = lcd_getcontrast,
+    .setcontrast  = lcd_setcontrast,
+  },
+};
+
+/* Power-on sequence taken from the original NVMPG firmware */
+
+static const uint16_t g_init_power[][2] =
+{
+  { ILI9225_POWER_CTRL2, 0x001a },
+  { ILI9225_POWER_CTRL3, 0x3121 },
+  { ILI9225_POWER_CTRL4, 0x006c },
+  { ILI9225_POWER_CTRL5, 0x4249 },
+  { ILI9225_POWER_CTRL1, 0x0800 },
+};
+
+static const uint16_t g_init_ramp[] =
+{
+  0x011a, 0x031a, 0x071a, 0x0f1a, 0x0f3a
+};
+
+static const uint16_t g_init_regs[][2] =
+{
+  { ILI9225_DRIVER_OUTPUT,  0x011c },
+  { ILI9225_LCD_AC_DRIVING, 0x0100 },
+  { ILI9225_ENTRY_MODE,     ILI9225_ENTRY_LANDSCAPE },
+  { ILI9225_DISP_CTRL1,     0x0000 },
+  { ILI9225_BLANK_PERIOD,   0x0808 },
+  { ILI9225_FRAME_CYCLE,    0x1100 },
+  { ILI9225_INTERFACE_CTRL, 0x0000 },
+  { ILI9225_OSC_CTRL,       0x1401 },
+  { ILI9225_VCI_RECYCLING,  0x0000 },
+  { ILI9225_RAM_ADDR_H,     0x0000 },
+  { ILI9225_RAM_ADDR_V,     0x0000 },
+  { 0x50, 0x0001 },                 /* Gamma 0x50..0x59 */
+  { 0x51, 0x020b },
+  { 0x52, 0x0805 },
+  { 0x53, 0x0404 },
+  { 0x54, 0x0c0c },
+  { 0x55, 0x000c },
+  { 0x56, 0x0101 },
+  { 0x57, 0x0400 },
+  { 0x58, 0x1108 },
+  { 0x59, 0x050c },
+  { ILI9225_HWIN_END,       LCD_YRES - 1 },  /* Full window */
+  { ILI9225_HWIN_START,     0x0000 },
+  { ILI9225_VWIN_END,       LCD_XRES - 1 },
+  { ILI9225_VWIN_START,     0x0000 },
+  { ILI9225_OSC_CTRL,       0x0b01 },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/* Bus primitives.  These are written against the registers directly: going
+ * through stm32_gpiowrite() for every edge would make a full screen redraw
+ * several times slower.
+ */
+
+static inline void lcd_wrbus(uint16_t value)
+{
+  putreg32(value, STM32_GPIOB_ODR);
+  putreg32(LCD_WR_BIT, STM32_GPIOC_BRR);
+  putreg32(LCD_WR_BIT, STM32_GPIOC_BSRR);
+}
+
+static inline void lcd_select(void)
+{
+  putreg32(LCD_CS_BIT, STM32_GPIOC_BRR);
+}
+
+static inline void lcd_deselect(void)
+{
+  putreg32(LCD_CS_BIT, STM32_GPIOC_BSRR);
+}
+
+static inline void lcd_index(uint16_t index)
+{
+  putreg32(LCD_RS_BIT, STM32_GPIOC_BRR);
+  lcd_wrbus(index);
+  putreg32(LCD_RS_BIT, STM32_GPIOC_BSRR);
+}
+
+static void lcd_writereg(uint16_t reg, uint16_t value)
+{
+  lcd_select();
+  lcd_index(reg);
+  lcd_wrbus(value);
+  lcd_deselect();
+}
+
+/* Reads only the ID register, so the slow path through stm32_configgpio()
+ * to turn PB0..15 around is fine here.
+ */
+
+static uint16_t lcd_readreg(uint16_t reg)
+{
+  uint16_t value;
+  int i;
+
+  lcd_select();
+  lcd_index(reg);
+
+  for (i = 0; i < 16; i++)
+    {
+      stm32_configgpio(GPIO_INPUT | GPIO_CNF_INFLOAT | GPIO_MODE_INPUT |
+                       GPIO_PORTB | (i << GPIO_PIN_SHIFT));
+    }
+
+  putreg32(LCD_RD_BIT, STM32_GPIOA_BRR);
+  up_udelay(5);
+  value = (uint16_t)getreg32(STM32_GPIOB_IDR);
+  putreg32(LCD_RD_BIT, STM32_GPIOA_BSRR);
+
+  for (i = 0; i < 16; i++)
+    {
+      stm32_configgpio(GPIO_LCD_DATA | (i << GPIO_PIN_SHIFT));
+    }
+
+  lcd_deselect();
+  return value;
+}
+
+/* Select the screen rectangle [x0..x1] x [y0..y1] and leave the controller
+ * ready to receive pixels in raster order (left to right, top to bottom).
+ */
+
+static void lcd_setarea(fb_coord_t x0, fb_coord_t y0,
+                        fb_coord_t x1, fb_coord_t y1)
+{
+  lcd_writereg(ILI9225_HWIN_END,   LCD_YRES - 1 - y0);
+  lcd_writereg(ILI9225_HWIN_START, LCD_YRES - 1 - y1);
+  lcd_writereg(ILI9225_VWIN_END,   x1);
+  lcd_writereg(ILI9225_VWIN_START, x0);
+  lcd_writereg(ILI9225_RAM_ADDR_H, LCD_YRES - 1 - y0);
+  lcd_writereg(ILI9225_RAM_ADDR_V, x0);
+}
+
+static void lcd_fill(uint16_t color)
+{
+  int i;
+
+  lcd_setarea(0, 0, LCD_XRES - 1, LCD_YRES - 1);
+  lcd_select();
+  lcd_index(ILI9225_GRAM_DATA);
+  putreg32(color, STM32_GPIOB_ODR);
+
+  for (i = 0; i < LCD_XRES * LCD_YRES; i++)
+    {
+      putreg32(LCD_WR_BIT, STM32_GPIOC_BRR);
+      putreg32(LCD_WR_BIT, STM32_GPIOC_BSRR);
+    }
+
+  lcd_deselect();
+}
+
+/****************************************************************************
+ * Name: lcd_putrun
+ ****************************************************************************/
+
+static int lcd_putrun(FAR struct lcd_dev_s *dev, fb_coord_t row,
+                      fb_coord_t col, FAR const uint8_t *buffer,
+                      size_t npixels)
+{
+  FAR const uint16_t *src = (FAR const uint16_t *)buffer;
+
+  DEBUGASSERT(buffer != NULL && ((uintptr_t)buffer & 1) == 0);
+  DEBUGASSERT(row < LCD_YRES && col + npixels <= LCD_XRES);
+
+  lcd_setarea(col, row, col + npixels - 1, row);
+  lcd_select();
+  lcd_index(ILI9225_GRAM_DATA);
+
+  while (npixels-- > 0)
+    {
+      lcd_wrbus(*src++);
+    }
+
+  lcd_deselect();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: lcd_putarea
+ ****************************************************************************/
+
+static int lcd_putarea(FAR struct lcd_dev_s *dev, fb_coord_t row_start,
+                       fb_coord_t row_end, fb_coord_t col_start,
+                       fb_coord_t col_end, FAR const uint8_t *buffer,
+                       fb_coord_t stride)
+{
+  size_t width = col_end - col_start + 1;
+  fb_coord_t row;
+  size_t i;
+
+  DEBUGASSERT(buffer != NULL && ((uintptr_t)buffer & 1) == 0);
+  DEBUGASSERT(row_end < LCD_YRES && col_end < LCD_XRES);
+
+  if (stride == 0)
+    {
+      stride = width * sizeof(uint16_t);
+    }
+
+  lcd_setarea(col_start, row_start, col_end, row_end);
+  lcd_select();
+  lcd_index(ILI9225_GRAM_DATA);
+
+  for (row = row_start; row <= row_end; row++)
+    {
+      FAR const uint16_t *src = (FAR const uint16_t *)buffer;
+
+      for (i = 0; i < width; i++)
+        {
+          lcd_wrbus(src[i]);
+        }
+
+      buffer += stride;
+    }
+
+  lcd_deselect();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: lcd_getvideoinfo
+ ****************************************************************************/
+
+static int lcd_getvideoinfo(FAR struct lcd_dev_s *dev,
+                            FAR struct fb_videoinfo_s *vinfo)
+{
+  DEBUGASSERT(vinfo != NULL);
+
+  vinfo->fmt     = FB_FMT_RGB16_565;
+  vinfo->xres    = LCD_XRES;
+  vinfo->yres    = LCD_YRES;
+  vinfo->nplanes = 1;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: lcd_getplaneinfo
+ ****************************************************************************/
+
+static int lcd_getplaneinfo(FAR struct lcd_dev_s *dev, unsigned int planeno,
+                            FAR struct lcd_planeinfo_s *pinfo)
+{
+  FAR struct nvmpg_lcd_s *priv = (FAR struct nvmpg_lcd_s *)dev;
+
+  DEBUGASSERT(pinfo != NULL && planeno == 0);
+
+  pinfo->putrun  = lcd_putrun;
+  pinfo->putarea = lcd_putarea;
+  pinfo->buffer  = (FAR uint8_t *)priv->runbuffer;
+  pinfo->bpp     = LCD_BPP;
+  pinfo->dev     = dev;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: lcd_getpower / lcd_setpower
+ ****************************************************************************/
+
+static int lcd_getpower(FAR struct lcd_dev_s *dev)
+{
+  FAR struct nvmpg_lcd_s *priv = (FAR struct nvmpg_lcd_s *)dev;
+
+  return priv->power;
+}
+
+static int lcd_setpower(FAR struct lcd_dev_s *dev, int power)
+{
+  FAR struct nvmpg_lcd_s *priv = (FAR struct nvmpg_lcd_s *)dev;
+
+  if (power > 0)
+    {
+      lcd_writereg(ILI9225_DISP_CTRL1, 0x0016);
+      up_mdelay(2);
+      lcd_writereg(ILI9225_DISP_CTRL1, 0x0017);
+      priv->power = 1;
+    }
+  else
+    {
+      lcd_writereg(ILI9225_DISP_CTRL1, 0x0000);
+      priv->power = 0;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: lcd_getcontrast / lcd_setcontrast
+ ****************************************************************************/
+
+static int lcd_getcontrast(FAR struct lcd_dev_s *dev)
+{
+  return -ENOSYS;
+}
+
+static int lcd_setcontrast(FAR struct lcd_dev_s *dev, unsigned int contrast)
+{
+  return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: lcd_hwinit
+ ****************************************************************************/
+
+static void lcd_hwinit(void)
+{
+  uint16_t id;
+  int i;
+
+  /* PB3/PB4 are only usable as GPIO with JTAG-DP disabled
+   * (CONFIG_STM32_JTAG_SW_ENABLE keeps SWD available for debugging).
+   */
+
+  for (i = 0; i < 16; i++)
+    {
+      stm32_configgpio(GPIO_LCD_DATA | (i << GPIO_PIN_SHIFT));
+    }
+
+  stm32_configgpio(GPIO_LCD_WR);
+  stm32_configgpio(GPIO_LCD_RS);
+  stm32_configgpio(GPIO_LCD_CS);
+  stm32_configgpio(GPIO_LCD_RD);
+
+  up_mdelay(50);
+  lcd_writereg(0x00, 0x0001);   /* Start oscillator */
+  up_mdelay(50);
+
+  id = lcd_readreg(0x00);
+  lcdinfo("LCD ID: %04x\n", id);
+  UNUSED(id);
+
+  for (i = 0; i < nitems(g_init_power); i++)
+    {
+      lcd_writereg(g_init_power[i][0], g_init_power[i][1]);
+    }
+
+  up_mdelay(10);
+
+  for (i = 0; i < nitems(g_init_ramp); i++)
+    {
+      lcd_writereg(ILI9225_POWER_CTRL2, g_init_ramp[i]);
+      up_mdelay(i < 3 ? 10 : 20);
+    }
+
+  up_mdelay(30);
+
+  for (i = 0; i < nitems(g_init_regs); i++)
+    {
+      lcd_writereg(g_init_regs[i][0], g_init_regs[i][1]);
+    }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: board_lcd_initialize
+ ****************************************************************************/
+
+int board_lcd_initialize(void)
+{
+  FAR struct nvmpg_lcd_s *priv = &g_lcd;
+
+  if (!priv->initialized)
+    {
+      lcd_hwinit();
+      lcd_fill(0x0000);
+      lcd_setpower(&priv->dev, CONFIG_LCD_MAXPOWER);
+      priv->initialized = true;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: board_lcd_getdev
+ ****************************************************************************/
+
+FAR struct lcd_dev_s *board_lcd_getdev(int lcddev)
+{
+  if (lcddev != 0)
+    {
+      return NULL;
+    }
+
+  return &g_lcd.dev;
+}
+
+/****************************************************************************
+ * Name: board_lcd_uninitialize
+ ****************************************************************************/
+
+void board_lcd_uninitialize(void)
+{
+  lcd_setpower(&g_lcd.dev, 0);
+}
+
+#endif /* CONFIG_LCD */

Reply via email to