diff --git a/.github/ALL_BSP_COMPILE.json b/.github/ALL_BSP_COMPILE.json index fc3600e713a9..5587bea24dda 100644 --- a/.github/ALL_BSP_COMPILE.json +++ b/.github/ALL_BSP_COMPILE.json @@ -331,6 +331,7 @@ "gd32/arm/gd32470z-lckfb", "gd32/arm/gd32h759i-start", "gd32/arm/gd32h759i-eval", + "gd32/arm/gd32h77d-ginopi", "gd32/arm/gd32e503v-eval", "gd32/arm/gd32527I-eval", "gd32/arm/gd32e230-lckfb", diff --git a/bsp/gd32/arm/gd32h77d-ginopi/.ci/attachconfig/ci.attachconfig.yml b/bsp/gd32/arm/gd32h77d-ginopi/.ci/attachconfig/ci.attachconfig.yml new file mode 100644 index 000000000000..c58b6cd2bb38 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/.ci/attachconfig/ci.attachconfig.yml @@ -0,0 +1,200 @@ +# Copyright (c) 2026, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 + +# Each project extends the minimal UART1/PIN default configuration. +# Serial V2 and UART DMA are not supported by this SDK port. +peripheral.uart_v1: + kconfig: + - CONFIG_BSP_USING_SERIAL_V1=y + - CONFIG_BSP_USING_UART0=y + - CONFIG_BSP_USING_UART2=y + - CONFIG_BSP_USING_UART3=y + - CONFIG_BSP_USING_UART4=y + +peripheral.ethernet: + kconfig: + - CONFIG_BSP_USING_ETH=y + +peripheral.usb_device_hs: + kconfig: + - CONFIG_BSP_USING_USB=y + - CONFIG_BSP_USB_USING_DEVICE=y + - CONFIG_BSP_USB_DEVICE_USING_HS=y + +peripheral.usb_device_fs: + kconfig: + - CONFIG_BSP_USING_USB=y + - CONFIG_BSP_USB_USING_DEVICE=y + - CONFIG_BSP_USB_DEVICE_USING_FS=y + +peripheral.usb_host: + kconfig: + - CONFIG_BSP_USING_USB=y + - CONFIG_BSP_USB_USING_DEVICE=n + - CONFIG_BSP_USB_USING_HOST=y + +peripheral.sdram: + kconfig: + - CONFIG_BSP_USING_SDRAM=y + +peripheral.i2c1: + kconfig: + - CONFIG_BSP_USING_I2C=y + - CONFIG_BSP_USING_HARD_I2C=y + - CONFIG_BSP_USING_HARD_I2C1=y + +peripheral.spi3: + kconfig: + - CONFIG_BSP_USING_SPI=y + - CONFIG_BSP_USING_SPI3=y + +peripheral.pwm: + kconfig: + - CONFIG_BSP_USING_PWM=y + - CONFIG_BSP_USING_PWM1=y + - CONFIG_BSP_USING_PWM2=y + - CONFIG_BSP_USING_PWM30=y + +peripheral.sdio1: + kconfig: + - CONFIG_BSP_SDIO_USING_SDIO1=y + +peripheral.sdcard_fatfs: + depends: + - peripheral.sdio1 + kconfig: + - CONFIG_BSP_USING_FS=y + - CONFIG_BSP_USING_SDCARD_FATFS=y + - CONFIG_RT_USING_DFS_V1=y + +peripheral.rtc_alarm0: + kconfig: + - CONFIG_BSP_USING_ONCHIP_RTC=y + - CONFIG_BSP_USING_ALARM=y + - CONFIG_BSP_USING_ALARM0=y + +peripheral.rtc_alarm1: + kconfig: + - CONFIG_BSP_USING_ONCHIP_RTC=y + - CONFIG_BSP_USING_ALARM=y + - CONFIG_BSP_USING_ALARM1=y + +peripheral.watchdog: + kconfig: + - CONFIG_RT_USING_WDT=y + +peripheral.can1: + kconfig: + - CONFIG_BSP_USING_CAN=y + - CONFIG_BSP_USING_CAN1=y + +peripheral.canfd1: + depends: + - peripheral.can1 + kconfig: + - CONFIG_RT_CAN_USING_CANFD=y + +peripheral.ospi0: + kconfig: + - CONFIG_BSP_USING_OSPI0=y + +peripheral.ospi0_mdma: + depends: + - peripheral.ospi0 + kconfig: + - CONFIG_BSP_OSPI0_USING_MDMA=y + +peripheral.flash_fatfs: + depends: + - peripheral.ospi0 + kconfig: + - CONFIG_BSP_USING_FS=y + - CONFIG_BSP_USING_FLASH_FATFS=y + - CONFIG_RT_USING_DFS_V1=y + - CONFIG_RT_DFS_ELM_MAX_SECTOR_SIZE=4096 + +peripheral.lcd_mipi: + kconfig: + - CONFIG_BSP_USING_LCD_MIPI=y + +peripheral.touch_gt911: + depends: + - peripheral.lcd_mipi + kconfig: + - CONFIG_BSP_USING_TOUCH_GT911=y + +peripheral.camera: + kconfig: + - CONFIG_BSP_USING_OV7670=y + +package.lvgl: + depends: + - peripheral.touch_gt911 + kconfig: + - CONFIG_PKG_USING_LVGL=y + - CONFIG_PKG_LVGL_USING_LATEST=y + +package.gd32vw553_wifi: + kconfig: + - CONFIG_BSP_USING_GD32VW553_AT=y + - CONFIG_BSP_USING_GD32VW553_MQTT_DEMO=n + +package.gd32vw553_mqtt: + depends: + - package.gd32vw553_wifi + kconfig: + - CONFIG_BSP_USING_GD32VW553_MQTT_DEMO=y + +application.board_demo: + depends: + - package.lvgl + - peripheral.flash_fatfs + kconfig: + - CONFIG_BSP_USING_LVGL_BOARD_DEMO=y + - CONFIG_PKG_LVGL_THREAD_STACK_SIZE=8192 + +application.usb_cdc: + depends: + - peripheral.usb_device_hs + kconfig: + - CONFIG_BSP_USING_USB_CDC_EXAMPLE=y + +application.sdram_heap_test: + depends: + - peripheral.sdram + kconfig: + - CONFIG_RT_USING_MEMHEAP_AS_HEAP=y + - CONFIG_RT_USING_MEMHEAP_AUTO_BINDING=y + - CONFIG_BSP_USING_SDRAM_HEAP_TEST=y + +application.filesystem_test: + depends: + - peripheral.flash_fatfs + kconfig: + - CONFIG_BSP_USING_FILESYSTEM_TEST=y + +application.pwm_dual_test: + depends: + - peripheral.pwm + kconfig: + - CONFIG_BSP_USING_PWM_DUAL_TEST=y + +application.rtc_alarm0: + depends: + - peripheral.rtc_alarm0 + kconfig: + - CONFIG_BSP_USING_RTC_ALARM_EXAMPLE=y + +application.rtc_alarm1: + depends: + - peripheral.rtc_alarm1 + kconfig: + - CONFIG_BSP_USING_RTC_ALARM_EXAMPLE=y + +application.ov7670_preview: + depends: + - package.lvgl + - peripheral.camera + kconfig: + - CONFIG_BSP_USING_OV7670_PREVIEW=y diff --git a/bsp/gd32/arm/gd32h77d-ginopi/.config b/bsp/gd32/arm/gd32h77d-ginopi/.config new file mode 100644 index 000000000000..b90d307bd01a --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/.config @@ -0,0 +1,1519 @@ + +# +# RT-Thread Kernel +# + +# +# klibc options +# + +# +# rt_vsnprintf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSNPRINTF is not set +# CONFIG_RT_KLIBC_USING_VSNPRINTF_LONGLONG is not set +# CONFIG_RT_KLIBC_USING_VSNPRINTF_STANDARD is not set +# end of rt_vsnprintf options + +# +# rt_vsscanf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSSCANF is not set +# end of rt_vsscanf options + +# +# rt_memset options +# +# CONFIG_RT_KLIBC_USING_USER_MEMSET is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMSET is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMSET is not set +# end of rt_memset options + +# +# rt_memcpy options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMCPY is not set +# end of rt_memcpy options + +# +# rt_memmove options +# +# CONFIG_RT_KLIBC_USING_USER_MEMMOVE is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMMOVE is not set +# end of rt_memmove options + +# +# rt_memcmp options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCMP is not set +# end of rt_memcmp options + +# +# rt_strstr options +# +# CONFIG_RT_KLIBC_USING_USER_STRSTR is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRSTR is not set +# end of rt_strstr options + +# +# rt_strcasecmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCASECMP is not set +# end of rt_strcasecmp options + +# +# rt_strncpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCPY is not set +# end of rt_strncpy options + +# +# rt_strcpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCPY is not set +# end of rt_strcpy options + +# +# rt_strncmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCMP is not set +# end of rt_strncmp options + +# +# rt_strcmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCMP is not set +# end of rt_strcmp options + +# +# rt_strlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRLEN is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRLEN is not set +# end of rt_strlen options + +# +# rt_strnlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRNLEN is not set +# end of rt_strnlen options +# end of klibc options + +CONFIG_RT_NAME_MAX=12 +# CONFIG_RT_USING_ARCH_DATA_TYPE is not set +# CONFIG_RT_USING_NANO is not set +# CONFIG_RT_USING_SMART is not set +# CONFIG_RT_USING_AMP is not set +# CONFIG_RT_USING_SMP is not set +CONFIG_RT_CPUS_NR=1 +CONFIG_RT_ALIGN_SIZE=8 +# CONFIG_RT_THREAD_PRIORITY_8 is not set +CONFIG_RT_THREAD_PRIORITY_32=y +# CONFIG_RT_THREAD_PRIORITY_256 is not set +CONFIG_RT_THREAD_PRIORITY_MAX=32 +CONFIG_RT_TICK_PER_SECOND=1000 +CONFIG_RT_USING_OVERFLOW_CHECK=y +CONFIG_RT_USING_HOOK=y +CONFIG_RT_HOOK_USING_FUNC_PTR=y +# CONFIG_RT_USING_HOOKLIST is not set +CONFIG_RT_USING_IDLE_HOOK=y +CONFIG_RT_IDLE_HOOK_LIST_SIZE=4 +CONFIG_IDLE_THREAD_STACK_SIZE=256 +CONFIG_RT_USING_TIMER_SOFT=y +CONFIG_RT_TIMER_THREAD_PRIO=4 +CONFIG_RT_TIMER_THREAD_STACK_SIZE=512 +# CONFIG_RT_USING_TIMER_ALL_SOFT is not set +# CONFIG_RT_USING_CPU_USAGE_TRACER is not set + +# +# kservice options +# +# CONFIG_RT_USING_TINY_FFS is not set +# end of kservice options + +CONFIG_RT_USING_DEBUG=y +CONFIG_RT_DEBUGING_ASSERT=y +CONFIG_RT_DEBUGING_COLOR=y +CONFIG_RT_DEBUGING_CONTEXT=y +# CONFIG_RT_DEBUGING_AUTO_INIT is not set +# CONFIG_RT_USING_CI_ACTION is not set + +# +# Inter-Thread communication +# +CONFIG_RT_USING_SEMAPHORE=y +CONFIG_RT_USING_MUTEX=y +CONFIG_RT_USING_EVENT=y +CONFIG_RT_USING_MAILBOX=y +CONFIG_RT_USING_MESSAGEQUEUE=y +# CONFIG_RT_USING_MESSAGEQUEUE_PRIORITY is not set +# CONFIG_RT_USING_SIGNALS is not set +# end of Inter-Thread communication + +# +# Memory Management +# +CONFIG_RT_USING_MEMPOOL=y +CONFIG_RT_USING_SMALL_MEM=y +# CONFIG_RT_USING_SLAB is not set +# CONFIG_RT_USING_MEMHEAP is not set +CONFIG_RT_USING_SMALL_MEM_AS_HEAP=y +# CONFIG_RT_USING_MEMHEAP_AS_HEAP is not set +# CONFIG_RT_USING_SLAB_AS_HEAP is not set +# CONFIG_RT_USING_USERHEAP is not set +# CONFIG_RT_USING_NOHEAP is not set +# CONFIG_RT_USING_MEMTRACE is not set +# CONFIG_RT_USING_HEAP_ISR is not set +CONFIG_RT_USING_HEAP=y +# end of Memory Management + +CONFIG_RT_USING_DEVICE=y +# CONFIG_RT_USING_DEVICE_OPS is not set +# CONFIG_RT_USING_INTERRUPT_INFO is not set +# CONFIG_RT_USING_THREADSAFE_PRINTF is not set +CONFIG_RT_USING_CONSOLE=y +CONFIG_RT_CONSOLEBUF_SIZE=128 +CONFIG_RT_CONSOLE_DEVICE_NAME="uart1" +CONFIG_RT_USING_CONSOLE_OUTPUT_CTL=y +CONFIG_RT_VER_NUM=0x50301 +# CONFIG_RT_USING_STDC_ATOMIC is not set +CONFIG_RT_BACKTRACE_LEVEL_MAX_NR=32 +# end of RT-Thread Kernel + +CONFIG_RT_USING_CACHE=y +CONFIG_RT_USING_HW_ATOMIC=y +CONFIG_ARCH_USING_HW_ATOMIC_8=y +CONFIG_ARCH_USING_HW_ATOMIC_16=y +CONFIG_RT_USING_CPU_FFS=y +CONFIG_ARCH_ARM=y +CONFIG_ARCH_ARM_CORTEX_M=y +CONFIG_ARCH_ARM_CORTEX_M7=y + +# +# RT-Thread Components +# +CONFIG_RT_USING_COMPONENTS_INIT=y +CONFIG_RT_USING_USER_MAIN=y +CONFIG_RT_MAIN_THREAD_STACK_SIZE=2048 +CONFIG_RT_MAIN_THREAD_PRIORITY=10 +# CONFIG_RT_USING_LEGACY is not set +CONFIG_RT_USING_MSH=y +CONFIG_RT_USING_FINSH=y +CONFIG_FINSH_USING_MSH=y +CONFIG_FINSH_THREAD_NAME="tshell" +CONFIG_FINSH_THREAD_PRIORITY=20 +CONFIG_FINSH_THREAD_STACK_SIZE=4096 +CONFIG_FINSH_USING_HISTORY=y +CONFIG_FINSH_HISTORY_LINES=5 +# CONFIG_FINSH_USING_WORD_OPERATION is not set +# CONFIG_FINSH_USING_FUNC_EXT is not set +CONFIG_FINSH_USING_SYMTAB=y +CONFIG_FINSH_CMD_SIZE=80 +CONFIG_MSH_USING_BUILT_IN_COMMANDS=y +CONFIG_FINSH_USING_DESCRIPTION=y +# CONFIG_FINSH_ECHO_DISABLE_DEFAULT is not set +# CONFIG_FINSH_USING_AUTH is not set +CONFIG_FINSH_ARG_MAX=10 +CONFIG_FINSH_USING_OPTION_COMPLETION=y + +# +# DFS: device virtual file system +# +# CONFIG_RT_USING_DFS is not set +# end of DFS: device virtual file system + +# CONFIG_RT_USING_FAL is not set + +# +# Device Drivers +# +# CONFIG_RT_USING_DM is not set +# CONFIG_RT_USING_DEV_BUS is not set +CONFIG_RT_USING_DEVICE_IPC=y +CONFIG_RT_UNAMED_PIPE_NUMBER=64 +# CONFIG_RT_USING_SYSTEM_WORKQUEUE is not set +CONFIG_RT_USING_SERIAL=y +CONFIG_RT_USING_SERIAL_V1=y +# CONFIG_RT_USING_SERIAL_V2 is not set +# CONFIG_RT_SERIAL_USING_DMA is not set +CONFIG_RT_SERIAL_RB_BUFSZ=64 +# CONFIG_RT_USING_SERIAL_BYPASS is not set +# CONFIG_RT_USING_CAN is not set +# CONFIG_RT_USING_ADC is not set +# CONFIG_RT_USING_CLOCK_TIME is not set +# CONFIG_RT_USING_I2C is not set +# CONFIG_RT_USING_PHY is not set +# CONFIG_RT_USING_PHY_V2 is not set +# CONFIG_RT_USING_DAC is not set +# CONFIG_RT_USING_NULL is not set +# CONFIG_RT_USING_ZERO is not set +# CONFIG_RT_USING_RANDOM is not set +# CONFIG_RT_USING_PWM is not set +# CONFIG_RT_USING_PULSE_ENCODER is not set +# CONFIG_RT_USING_INPUT_CAPTURE is not set +# CONFIG_RT_USING_MTD_NOR is not set +# CONFIG_RT_USING_MTD_NAND is not set +# CONFIG_RT_USING_PM is not set +# CONFIG_RT_USING_RTC is not set +# CONFIG_RT_USING_SDIO is not set +# CONFIG_RT_USING_SPI is not set +# CONFIG_RT_USING_WDT is not set +# CONFIG_RT_USING_AUDIO is not set +# CONFIG_RT_USING_SENSOR is not set +# CONFIG_RT_USING_TOUCH is not set +# CONFIG_RT_USING_LCD is not set +# CONFIG_RT_USING_HWCRYPTO is not set +# CONFIG_RT_USING_WIFI is not set +# CONFIG_RT_USING_RPMSG is not set +# CONFIG_RT_USING_BLK is not set +# CONFIG_RT_USING_REGULATOR is not set +# CONFIG_RT_USING_POWER_SUPPLY is not set +CONFIG_RT_USING_PIN=y +# CONFIG_RT_USING_CHERRYUSB is not set +# end of Device Drivers + +# +# C/C++ and POSIX layer +# + +# +# ISO-ANSI C layer +# + +# +# Timezone and Daylight Saving Time +# +# CONFIG_RT_LIBC_USING_FULL_TZ_DST is not set +CONFIG_RT_LIBC_USING_LIGHT_TZ_DST=y +CONFIG_RT_LIBC_TZ_DEFAULT_HOUR=8 +CONFIG_RT_LIBC_TZ_DEFAULT_MIN=0 +CONFIG_RT_LIBC_TZ_DEFAULT_SEC=0 +# end of Timezone and Daylight Saving Time +# end of ISO-ANSI C layer + +# +# POSIX (Portable Operating System Interface) layer +# +# CONFIG_RT_USING_POSIX_FS is not set +# CONFIG_RT_USING_POSIX_DELAY is not set +# CONFIG_RT_USING_POSIX_CLOCK is not set +# CONFIG_RT_USING_POSIX_TIMER is not set +# CONFIG_RT_USING_PTHREADS is not set +# CONFIG_RT_USING_MODULE is not set + +# +# Interprocess Communication (IPC) +# +# CONFIG_RT_USING_POSIX_PIPE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_QUEUE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_SEMAPHORE is not set + +# +# Socket is in the 'Network' category +# +# end of Interprocess Communication (IPC) +# end of POSIX (Portable Operating System Interface) layer + +# CONFIG_RT_USING_CPLUSPLUS is not set +# end of C/C++ and POSIX layer + +# +# Network +# +# CONFIG_RT_USING_SAL is not set +# CONFIG_RT_USING_NETDEV is not set +# CONFIG_RT_USING_LWIP is not set +# CONFIG_RT_USING_AT is not set +# end of Network + +# +# Memory protection +# +# CONFIG_RT_USING_MEM_PROTECTION is not set +# CONFIG_RT_USING_HW_STACK_GUARD is not set +# end of Memory protection + +# +# Utilities +# +# CONFIG_RT_USING_RYM is not set +# CONFIG_RT_USING_ULOG is not set +# CONFIG_RT_USING_UTEST is not set +# CONFIG_RT_USING_VAR_EXPORT is not set +# CONFIG_RT_USING_RESOURCE_ID is not set +# CONFIG_RT_USING_ADT is not set +# CONFIG_RT_USING_RT_LINK is not set +# end of Utilities + +# +# Using USB legacy version +# +# CONFIG_RT_USING_USB_HOST is not set +# CONFIG_RT_USING_USB_DEVICE is not set +# end of Using USB legacy version + +# CONFIG_RT_USING_FDT is not set +# CONFIG_RT_USING_RUST is not set +# end of RT-Thread Components + +# +# RT-Thread Utestcases +# +# CONFIG_RT_USING_UTESTCASES is not set +# end of RT-Thread Utestcases + +# +# RT-Thread online packages +# + +# +# IoT - internet of things +# +# CONFIG_PKG_USING_LORAWAN_DRIVER is not set +# CONFIG_PKG_USING_PAHOMQTT is not set +# CONFIG_PKG_USING_UMQTT is not set +# CONFIG_PKG_USING_WEBCLIENT is not set +# CONFIG_PKG_USING_WEBNET is not set +# CONFIG_PKG_USING_MONGOOSE is not set +# CONFIG_PKG_USING_MYMQTT is not set +# CONFIG_PKG_USING_KAWAII_MQTT is not set +# CONFIG_PKG_USING_BC28_MQTT is not set +# CONFIG_PKG_USING_WEBTERMINAL is not set +# CONFIG_PKG_USING_FREEMODBUS is not set +# CONFIG_PKG_USING_NANOPB is not set +# CONFIG_PKG_USING_WIFI_HOST_DRIVER is not set +# CONFIG_PKG_USING_ESP_HOSTED is not set + +# +# Wi-Fi +# + +# +# Marvell WiFi +# +# CONFIG_PKG_USING_WLANMARVELL is not set +# end of Marvell WiFi + +# +# Wiced WiFi +# +# CONFIG_PKG_USING_WLAN_WICED is not set +# end of Wiced WiFi + +# CONFIG_PKG_USING_RW007 is not set + +# +# CYW43012 WiFi +# +# CONFIG_PKG_USING_WLAN_CYW43012 is not set +# end of CYW43012 WiFi + +# +# BL808 WiFi +# +# CONFIG_PKG_USING_WLAN_BL808 is not set +# end of BL808 WiFi + +# +# CYW43439 WiFi +# +# CONFIG_PKG_USING_WLAN_CYW43439 is not set +# end of CYW43439 WiFi +# end of Wi-Fi + +# CONFIG_PKG_USING_COAP is not set +# CONFIG_PKG_USING_NOPOLL is not set +# CONFIG_PKG_USING_NETUTILS is not set +# CONFIG_PKG_USING_CMUX is not set +# CONFIG_PKG_USING_PPP_DEVICE is not set +# CONFIG_PKG_USING_AT_DEVICE is not set +# CONFIG_PKG_USING_ATSRV_SOCKET is not set +# CONFIG_PKG_USING_WIZNET is not set +# CONFIG_PKG_USING_ZB_COORDINATOR is not set + +# +# IoT Cloud +# +# CONFIG_PKG_USING_ONENET is not set +# CONFIG_PKG_USING_GAGENT_CLOUD is not set +# CONFIG_PKG_USING_ALI_IOTKIT is not set +# CONFIG_PKG_USING_AZURE is not set +# CONFIG_PKG_USING_TENCENT_IOT_EXPLORER is not set +# CONFIG_PKG_USING_JIOT-C-SDK is not set +# CONFIG_PKG_USING_UCLOUD_IOT_SDK is not set +# CONFIG_PKG_USING_JOYLINK is not set +# CONFIG_PKG_USING_IOTSHARP_SDK is not set +# end of IoT Cloud + +# CONFIG_PKG_USING_NIMBLE is not set +# CONFIG_PKG_USING_LLSYNC_SDK_ADAPTER is not set +# CONFIG_PKG_USING_OTA_DOWNLOADER is not set +# CONFIG_PKG_USING_IPMSG is not set +# CONFIG_PKG_USING_LSSDP is not set +# CONFIG_PKG_USING_AIRKISS_OPEN is not set +# CONFIG_PKG_USING_LIBRWS is not set +# CONFIG_PKG_USING_TCPSERVER is not set +# CONFIG_PKG_USING_PROTOBUF_C is not set +# CONFIG_PKG_USING_DLT645 is not set +# CONFIG_PKG_USING_QXWZ is not set +# CONFIG_PKG_USING_SMTP_CLIENT is not set +# CONFIG_PKG_USING_ABUP_FOTA is not set +# CONFIG_PKG_USING_LIBCURL2RTT is not set +# CONFIG_PKG_USING_CAPNP is not set +# CONFIG_PKG_USING_ERPC is not set +# CONFIG_PKG_USING_AGILE_TELNET is not set +# CONFIG_PKG_USING_NMEALIB is not set +# CONFIG_PKG_USING_PDULIB is not set +# CONFIG_PKG_USING_BTSTACK is not set +# CONFIG_PKG_USING_BT_CYW43012 is not set +# CONFIG_PKG_USING_CYW43XX is not set +# CONFIG_PKG_USING_LORAWAN_ED_STACK is not set +# CONFIG_PKG_USING_WAYZ_IOTKIT is not set +# CONFIG_PKG_USING_MAVLINK is not set +# CONFIG_PKG_USING_BSAL is not set +# CONFIG_PKG_USING_AGILE_MODBUS is not set +# CONFIG_PKG_USING_AGILE_FTP is not set +# CONFIG_PKG_USING_EMBEDDEDPROTO is not set +# CONFIG_PKG_USING_RT_LINK_HW is not set +# CONFIG_PKG_USING_RYANMQTT is not set +# CONFIG_PKG_USING_RYANW5500 is not set +# CONFIG_PKG_USING_LORA_PKT_FWD is not set +# CONFIG_PKG_USING_LORA_GW_DRIVER_LIB is not set +# CONFIG_PKG_USING_LORA_PKT_SNIFFER is not set +# CONFIG_PKG_USING_HM is not set +# CONFIG_PKG_USING_SMALL_MODBUS is not set +# CONFIG_PKG_USING_NET_SERVER is not set +# CONFIG_PKG_USING_ZFTP is not set +# CONFIG_PKG_USING_WOL is not set +# CONFIG_PKG_USING_ZEPHYR_POLLING is not set +# CONFIG_PKG_USING_MATTER_ADAPTATION_LAYER is not set +# CONFIG_PKG_USING_LHC_MODBUS is not set +# CONFIG_PKG_USING_QMODBUS is not set +# CONFIG_PKG_USING_PNET is not set +# CONFIG_PKG_USING_OPENER is not set +# CONFIG_PKG_USING_FREEMQTT is not set +# end of IoT - internet of things + +# +# security packages +# +# CONFIG_PKG_USING_MBEDTLS is not set +# CONFIG_PKG_USING_LIBSODIUM is not set +# CONFIG_PKG_USING_LIBHYDROGEN is not set +# CONFIG_PKG_USING_TINYCRYPT is not set +# CONFIG_PKG_USING_TFM is not set +# CONFIG_PKG_USING_YD_CRYPTO is not set +# end of security packages + +# +# language packages +# + +# +# JSON: JavaScript Object Notation, a lightweight data-interchange format +# +# CONFIG_PKG_USING_CJSON is not set +# CONFIG_PKG_USING_LJSON is not set +# CONFIG_PKG_USING_RT_CJSON_TOOLS is not set +# CONFIG_PKG_USING_RAPIDJSON is not set +# CONFIG_PKG_USING_JSMN is not set +# CONFIG_PKG_USING_AGILE_JSMN is not set +# CONFIG_PKG_USING_PARSON is not set +# CONFIG_PKG_USING_RYAN_JSON is not set +# end of JSON: JavaScript Object Notation, a lightweight data-interchange format + +# +# XML: Extensible Markup Language +# +# CONFIG_PKG_USING_SIMPLE_XML is not set +# CONFIG_PKG_USING_EZXML is not set +# end of XML: Extensible Markup Language + +# CONFIG_PKG_USING_LUATOS_SOC is not set +# CONFIG_PKG_USING_LUA is not set +# CONFIG_PKG_USING_JERRYSCRIPT is not set +# CONFIG_PKG_USING_MICROPYTHON is not set +# CONFIG_PKG_USING_PIKASCRIPT is not set +# CONFIG_PKG_USING_RTT_RUST is not set +# end of language packages + +# +# multimedia packages +# + +# +# LVGL: powerful and easy-to-use embedded GUI library +# +# CONFIG_PKG_USING_LVGL is not set +# CONFIG_PKG_USING_LV_MUSIC_DEMO is not set +# CONFIG_PKG_USING_GUI_GUIDER_DEMO is not set +# end of LVGL: powerful and easy-to-use embedded GUI library + +# +# u8g2: a monochrome graphic library +# +# CONFIG_PKG_USING_U8G2_OFFICIAL is not set +# CONFIG_PKG_USING_U8G2 is not set +# end of u8g2: a monochrome graphic library + +# CONFIG_PKG_USING_NES_SIMULATOR is not set +# CONFIG_PKG_USING_OPENMV is not set +# CONFIG_PKG_USING_MUPDF is not set +# CONFIG_PKG_USING_STEMWIN is not set +# CONFIG_PKG_USING_WAVPLAYER is not set +# CONFIG_PKG_USING_TJPGD is not set +# CONFIG_PKG_USING_PDFGEN is not set +# CONFIG_PKG_USING_HELIX is not set +# CONFIG_PKG_USING_AZUREGUIX is not set +# CONFIG_PKG_USING_TOUCHGFX2RTT is not set +# CONFIG_PKG_USING_NUEMWIN is not set +# CONFIG_PKG_USING_MP3PLAYER is not set +# CONFIG_PKG_USING_TINYJPEG is not set +# CONFIG_PKG_USING_UGUI is not set +# CONFIG_PKG_USING_MCURSES is not set +# CONFIG_PKG_USING_TERMBOX is not set +# CONFIG_PKG_USING_VT100 is not set +# CONFIG_PKG_USING_QRCODE is not set +# CONFIG_PKG_USING_GUIENGINE is not set +# CONFIG_PKG_USING_PERSIMMON is not set +# CONFIG_PKG_USING_3GPP_AMRNB is not set +# end of multimedia packages + +# +# tools packages +# +# CONFIG_PKG_USING_VECTOR is not set +# CONFIG_PKG_USING_SORCH is not set +# CONFIG_PKG_USING_DICT is not set +# CONFIG_PKG_USING_CMBACKTRACE is not set +# CONFIG_PKG_USING_MCOREDUMP is not set +# CONFIG_PKG_USING_EASYFLASH is not set +# CONFIG_PKG_USING_EASYLOGGER is not set +# CONFIG_PKG_USING_SYSTEMVIEW is not set +# CONFIG_PKG_USING_SEGGER_RTT is not set +# CONFIG_PKG_USING_RTT_AUTO_EXE_CMD is not set +# CONFIG_PKG_USING_RDB is not set +# CONFIG_PKG_USING_ULOG_EASYFLASH is not set +# CONFIG_PKG_USING_LOGMGR is not set +# CONFIG_PKG_USING_ADBD is not set +# CONFIG_PKG_USING_COREMARK is not set +# CONFIG_PKG_USING_DHRYSTONE is not set +# CONFIG_PKG_USING_MEMORYPERF is not set +# CONFIG_PKG_USING_NR_MICRO_SHELL is not set +# CONFIG_PKG_USING_CHINESE_FONT_LIBRARY is not set +# CONFIG_PKG_USING_LUNAR_CALENDAR is not set +# CONFIG_PKG_USING_BS8116A is not set +# CONFIG_PKG_USING_GPS_RMC is not set +# CONFIG_PKG_USING_URLENCODE is not set +# CONFIG_PKG_USING_UMCN is not set +# CONFIG_PKG_USING_LWRB2RTT is not set +# CONFIG_PKG_USING_CPU_USAGE is not set +# CONFIG_PKG_USING_GBK2UTF8 is not set +# CONFIG_PKG_USING_VCONSOLE is not set +# CONFIG_PKG_USING_KDB is not set +# CONFIG_PKG_USING_WAMR is not set +# CONFIG_PKG_USING_MICRO_XRCE_DDS_CLIENT is not set +# CONFIG_PKG_USING_LWLOG is not set +# CONFIG_PKG_USING_ANV_TRACE is not set +# CONFIG_PKG_USING_ANV_MEMLEAK is not set +# CONFIG_PKG_USING_ANV_TESTSUIT is not set +# CONFIG_PKG_USING_ANV_BENCH is not set +# CONFIG_PKG_USING_DEVMEM is not set +# CONFIG_PKG_USING_REGEX is not set +# CONFIG_PKG_USING_MEM_SANDBOX is not set +# CONFIG_PKG_USING_SOLAR_TERMS is not set +# CONFIG_PKG_USING_GAN_ZHI is not set +# CONFIG_PKG_USING_FDT is not set +# CONFIG_PKG_USING_CBOX is not set +# CONFIG_PKG_USING_SNOWFLAKE is not set +# CONFIG_PKG_USING_HASH_MATCH is not set +# CONFIG_PKG_USING_ARMV7M_DWT_TOOL is not set +# CONFIG_PKG_USING_VOFA_PLUS is not set +# CONFIG_PKG_USING_RT_TRACE is not set +# CONFIG_PKG_USING_ZDEBUG is not set +# CONFIG_PKG_USING_RVBACKTRACE is not set +# CONFIG_PKG_USING_HPATCHLITE is not set +# CONFIG_PKG_USING_THREAD_METRIC is not set +# CONFIG_PKG_USING_UORB is not set +# CONFIG_PKG_USING_RT_TUNNEL is not set +# CONFIG_PKG_USING_VIRTUAL_TERMINAL is not set +# end of tools packages + +# +# system packages +# + +# +# enhanced kernel services +# +# CONFIG_PKG_USING_RT_MEMCPY_CM is not set +# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set +# end of enhanced kernel services + +# CONFIG_PKG_USING_AUNITY is not set + +# +# acceleration: Assembly language or algorithmic acceleration packages +# +# CONFIG_PKG_USING_QFPLIB_M0_FULL is not set +# CONFIG_PKG_USING_QFPLIB_M0_TINY is not set +# CONFIG_PKG_USING_QFPLIB_M3 is not set +# end of acceleration: Assembly language or algorithmic acceleration packages + +# +# CMSIS: ARM Cortex-M Microcontroller Software Interface Standard +# +# CONFIG_PKG_USING_CMSIS_5 is not set +# CONFIG_PKG_USING_CMSIS_CORE is not set +# CONFIG_PKG_USING_CMSIS_NN is not set +# CONFIG_PKG_USING_CMSIS_RTOS1 is not set +# CONFIG_PKG_USING_CMSIS_RTOS2 is not set +# end of CMSIS: ARM Cortex-M Microcontroller Software Interface Standard + +# +# Micrium: Micrium software products porting for RT-Thread +# +# CONFIG_PKG_USING_UCOSIII_WRAPPER is not set +# CONFIG_PKG_USING_UCOSII_WRAPPER is not set +# CONFIG_PKG_USING_UC_CRC is not set +# CONFIG_PKG_USING_UC_CLK is not set +# CONFIG_PKG_USING_UC_COMMON is not set +# CONFIG_PKG_USING_UC_MODBUS is not set +# end of Micrium: Micrium software products porting for RT-Thread + +# CONFIG_PKG_USING_FREERTOS_WRAPPER is not set +# CONFIG_PKG_USING_LITEOS_SDK is not set +# CONFIG_PKG_USING_TZ_DATABASE is not set +# CONFIG_PKG_USING_CAIRO is not set +# CONFIG_PKG_USING_PIXMAN is not set +# CONFIG_PKG_USING_PARTITION is not set +# CONFIG_PKG_USING_PERF_COUNTER is not set +# CONFIG_PKG_USING_FILEX is not set +# CONFIG_PKG_USING_LEVELX is not set +# CONFIG_PKG_USING_FLASHDB is not set +# CONFIG_PKG_USING_SQLITE is not set +# CONFIG_PKG_USING_RTI is not set +# CONFIG_PKG_USING_DFS_YAFFS is not set +# CONFIG_PKG_USING_LITTLEFS is not set +# CONFIG_PKG_USING_DFS_JFFS2 is not set +# CONFIG_PKG_USING_DFS_UFFS is not set +# CONFIG_PKG_USING_LWEXT4 is not set +# CONFIG_PKG_USING_THREAD_POOL is not set +# CONFIG_PKG_USING_ROBOTS is not set +# CONFIG_PKG_USING_EV is not set +# CONFIG_PKG_USING_SYSWATCH is not set +# CONFIG_PKG_USING_SYS_LOAD_MONITOR is not set +# CONFIG_PKG_USING_PLCCORE is not set +# CONFIG_PKG_USING_RAMDISK is not set +# CONFIG_PKG_USING_MININI is not set +# CONFIG_PKG_USING_QBOOT is not set +# CONFIG_PKG_USING_PPOOL is not set +# CONFIG_PKG_USING_OPENAMP is not set +# CONFIG_PKG_USING_RPMSG_LITE is not set +# CONFIG_PKG_USING_LPM is not set +# CONFIG_PKG_USING_TLSF is not set +# CONFIG_PKG_USING_EVENT_RECORDER is not set +# CONFIG_PKG_USING_ARM_2D is not set +# CONFIG_PKG_USING_MCUBOOT is not set +# CONFIG_PKG_USING_TINYUSB is not set +# CONFIG_PKG_USING_KMULTI_RTIMER is not set +# CONFIG_PKG_USING_TFDB is not set +# CONFIG_PKG_USING_QPC is not set +# CONFIG_PKG_USING_AGILE_UPGRADE is not set +# CONFIG_PKG_USING_FLASH_BLOB is not set +# CONFIG_PKG_USING_MLIBC is not set +# CONFIG_PKG_USING_TASK_MSG_BUS is not set +# CONFIG_PKG_USING_UART_FRAMEWORK is not set +# CONFIG_PKG_USING_SFDB is not set +# CONFIG_PKG_USING_RTP is not set +# CONFIG_PKG_USING_REB is not set +# CONFIG_PKG_USING_RMP is not set +# CONFIG_PKG_USING_R_RHEALSTONE is not set +# CONFIG_PKG_USING_HEARTBEAT is not set +# CONFIG_PKG_USING_MICRO_ROS_RTTHREAD_PACKAGE is not set +# CONFIG_PKG_USING_CHERRYECAT is not set +# CONFIG_PKG_USING_EVENT_LOOP is not set +# CONFIG_PKG_USING_THREAD_MANAGER is not set +# end of system packages + +# +# peripheral libraries and drivers +# + +# +# HAL & SDK Drivers +# + +# +# STM32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_STM32F0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F1_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F1_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F2_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F2_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F3_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F3_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F7_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F7_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32G0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32G0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32G4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32G4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H7_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H7_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H7RS_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H7RS_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32U5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32U5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32WB55_SDK is not set +# CONFIG_PKG_USING_STM32_SDIO is not set +# CONFIG_PKG_USING_STM32WL_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32WL_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32WB_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32WB_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32MP1_M4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32MP1_M4_CMSIS_DRIVER is not set +# end of STM32 HAL & SDK Drivers + +# +# Infineon HAL Packages +# +# CONFIG_PKG_USING_INFINEON_CAT1CM0P is not set +# CONFIG_PKG_USING_INFINEON_CMSIS is not set +# CONFIG_PKG_USING_INFINEON_CORE_LIB is not set +# CONFIG_PKG_USING_INFINEON_MTB_HAL_CAT1 is not set +# CONFIG_PKG_USING_INFINEON_MTB_PDL_CAT1 is not set +# CONFIG_PKG_USING_INFINEON_RETARGET_IO is not set +# CONFIG_PKG_USING_INFINEON_CAPSENSE is not set +# CONFIG_PKG_USING_INFINEON_CSDIDAC is not set +# CONFIG_PKG_USING_INFINEON_SERIAL_FLASH is not set +# CONFIG_PKG_USING_INFINEON_USBDEV is not set +# end of Infineon HAL Packages + +# CONFIG_PKG_USING_BLUETRUM_SDK is not set +# CONFIG_PKG_USING_EMBARC_BSP is not set +# CONFIG_PKG_USING_ESP_IDF is not set + +# +# Kendryte SDK +# +# CONFIG_PKG_USING_K210_SDK is not set +# CONFIG_PKG_USING_KENDRYTE_SDK is not set +# end of Kendryte SDK + +# CONFIG_PKG_USING_NRF5X_SDK is not set +# CONFIG_PKG_USING_NRFX is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_RP2350_SDK is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_SDK is not set + +# +# MM32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_MM32F103XX_CMSIS is not set +# CONFIG_PKG_USING_MM32F3270X_CMSIS is not set +# CONFIG_PKG_USING_MM32F5260X_CMSIS is not set +# CONFIG_PKG_USING_MM32L0XX_CMSIS is not set +# CONFIG_PKG_USING_MM32L3XX_CMSIS is not set +# CONFIG_PKG_USING_MM32F103XX_HAL_DRIVER is not set +# CONFIG_PKG_USING_MM32F3270X_HAL_DRIVER is not set +# CONFIG_PKG_USING_MM32F5260X_HAL_DRIVER is not set +# CONFIG_PKG_USING_MM32L0XX_HAL_DRIVER is not set +# CONFIG_PKG_USING_MM32L3XX_HAL_DRIVER is not set +# end of MM32 HAL & SDK Drivers + +# +# WCH HAL & SDK Drivers +# +# CONFIG_PKG_USING_CH32V20x_SDK is not set +# CONFIG_PKG_USING_CH32V307_SDK is not set +# end of WCH HAL & SDK Drivers + +# +# AT32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_AT32A403A_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32A403A_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32A423_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32A423_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F45x_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F45x_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F402_405_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F402_405_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F403A_407_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F403A_407_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F413_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F413_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F415_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F415_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F421_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F421_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F423_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F423_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F425_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F425_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F435_437_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F435_437_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32M412_416_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32M412_416_CMSIS_DRIVER is not set +# end of AT32 HAL & SDK Drivers + +# +# HC32 DDL Drivers +# +# CONFIG_PKG_USING_HC32F3_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_HC32F3_SERIES_DRIVER is not set +# CONFIG_PKG_USING_HC32F4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_HC32F4_SERIES_DRIVER is not set +# end of HC32 DDL Drivers + +# +# NXP HAL & SDK Drivers +# +# CONFIG_PKG_USING_NXP_MCX_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_NXP_MCX_SERIES_DRIVER is not set +# CONFIG_PKG_USING_NXP_LPC_DRIVER is not set +# CONFIG_PKG_USING_NXP_LPC55S_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMX6SX_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMX6UL_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMXRT_DRIVER is not set +# end of NXP HAL & SDK Drivers + +# +# NUVOTON Drivers +# +# CONFIG_PKG_USING_NUVOTON_SERIES_DRIVER is not set +# end of NUVOTON Drivers + +# +# GD32 Drivers +# +CONFIG_PKG_USING_GD32_ARM_CMSIS_DRIVER=y +CONFIG_PKG_GD32_ARM_CMSIS_DRIVER_PATH="/packages/peripherals/hal-sdk/gd32/gd32-arm-cmsis" +CONFIG_PKG_USING_GD32_ARM_CMSIS_DRIVER_LATEST_VERSION=y +CONFIG_PKG_GD32_ARM_CMSIS_DRIVER_VER="latest" +CONFIG_PKG_USING_GD32_ARM_SERIES_DRIVER=y +CONFIG_PKG_GD32_ARM_SERIES_DRIVER_PATH="/packages/peripherals/hal-sdk/gd32/gd32-arm-series" +CONFIG_PKG_USING_GD32_ARM_SERIES_DRIVER_LATEST_VERSION=y +CONFIG_PKG_GD32_ARM_SERIES_DRIVER_VER="latest" +# CONFIG_PKG_USING_GD32_RISCV_SERIES_DRIVER is not set +# CONFIG_PKG_USING_GD32VW55X_WIFI is not set +# end of GD32 Drivers + +# +# HPMicro SDK +# +# CONFIG_PKG_USING_HPM_SDK is not set +# end of HPMicro SDK + +# +# FT32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_FT32F0_STD_DRIVER is not set +# CONFIG_PKG_USING_FT32F0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_FT32F4_STD_DRIVER is not set +# CONFIG_PKG_USING_FT32F4_CMSIS_DRIVER is not set +# end of FT32 HAL & SDK Drivers + +# +# NOVOSNS Drivers +# +# CONFIG_PKG_USING_NOVOSNS_SERIES_DRIVER is not set +# end of NOVOSNS Drivers +# end of HAL & SDK Drivers + +# +# sensors drivers +# +# CONFIG_PKG_USING_LSM6DSM is not set +# CONFIG_PKG_USING_LSM6DSL is not set +# CONFIG_PKG_USING_LPS22HB is not set +# CONFIG_PKG_USING_HTS221 is not set +# CONFIG_PKG_USING_LSM303AGR is not set +# CONFIG_PKG_USING_BME280 is not set +# CONFIG_PKG_USING_BME680 is not set +# CONFIG_PKG_USING_BMA400 is not set +# CONFIG_PKG_USING_BMI160_BMX160 is not set +# CONFIG_PKG_USING_SPL0601 is not set +# CONFIG_PKG_USING_MS5805 is not set +# CONFIG_PKG_USING_DA270 is not set +# CONFIG_PKG_USING_DF220 is not set +# CONFIG_PKG_USING_HSHCAL001 is not set +# CONFIG_PKG_USING_BH1750 is not set +# CONFIG_PKG_USING_MPU6XXX is not set +# CONFIG_PKG_USING_AHT10 is not set +# CONFIG_PKG_USING_AP3216C is not set +# CONFIG_PKG_USING_TSL4531 is not set +# CONFIG_PKG_USING_DS18B20 is not set +# CONFIG_PKG_USING_DHT11 is not set +# CONFIG_PKG_USING_DHTXX is not set +# CONFIG_PKG_USING_GY271 is not set +# CONFIG_PKG_USING_GP2Y10 is not set +# CONFIG_PKG_USING_SGP30 is not set +# CONFIG_PKG_USING_HDC1000 is not set +# CONFIG_PKG_USING_BMP180 is not set +# CONFIG_PKG_USING_BMP280 is not set +# CONFIG_PKG_USING_SHTC1 is not set +# CONFIG_PKG_USING_BMI088 is not set +# CONFIG_PKG_USING_HMC5883 is not set +# CONFIG_PKG_USING_MAX6675 is not set +# CONFIG_PKG_USING_MAX31855 is not set +# CONFIG_PKG_USING_TMP1075 is not set +# CONFIG_PKG_USING_SR04 is not set +# CONFIG_PKG_USING_CCS811 is not set +# CONFIG_PKG_USING_PMSXX is not set +# CONFIG_PKG_USING_RT3020 is not set +# CONFIG_PKG_USING_MLX90632 is not set +# CONFIG_PKG_USING_MLX90382 is not set +# CONFIG_PKG_USING_MLX90384 is not set +# CONFIG_PKG_USING_MLX90393 is not set +# CONFIG_PKG_USING_MLX90392 is not set +# CONFIG_PKG_USING_MLX90394 is not set +# CONFIG_PKG_USING_MLX90396 is not set +# CONFIG_PKG_USING_MLX90397 is not set +# CONFIG_PKG_USING_MS5611 is not set +# CONFIG_PKG_USING_MAX31865 is not set +# CONFIG_PKG_USING_VL53L0X is not set +# CONFIG_PKG_USING_INA260 is not set +# CONFIG_PKG_USING_MAX30102 is not set +# CONFIG_PKG_USING_INA226 is not set +# CONFIG_PKG_USING_LIS2DH12 is not set +# CONFIG_PKG_USING_HS300X is not set +# CONFIG_PKG_USING_ZMOD4410 is not set +# CONFIG_PKG_USING_ISL29035 is not set +# CONFIG_PKG_USING_MMC3680KJ is not set +# CONFIG_PKG_USING_QMP6989 is not set +# CONFIG_PKG_USING_BALANCE is not set +# CONFIG_PKG_USING_SHT2X is not set +# CONFIG_PKG_USING_SHT3X is not set +# CONFIG_PKG_USING_SHT4X is not set +# CONFIG_PKG_USING_AD7746 is not set +# CONFIG_PKG_USING_ADT74XX is not set +# CONFIG_PKG_USING_MAX17048 is not set +# CONFIG_PKG_USING_AS7341 is not set +# CONFIG_PKG_USING_CW2015 is not set +# CONFIG_PKG_USING_ICM20608 is not set +# CONFIG_PKG_USING_PAJ7620 is not set +# CONFIG_PKG_USING_STHS34PF80 is not set +# CONFIG_PKG_USING_P3T1755 is not set +# CONFIG_PKG_USING_QMI8658 is not set +# CONFIG_PKG_USING_ICM20948 is not set +# CONFIG_PKG_USING_SCD4X is not set +# end of sensors drivers + +# +# touch drivers +# +# CONFIG_PKG_USING_GT9147 is not set +# CONFIG_PKG_USING_GT1151 is not set +# CONFIG_PKG_USING_GT917S is not set +# CONFIG_PKG_USING_GT911 is not set +# CONFIG_PKG_USING_FT6206 is not set +# CONFIG_PKG_USING_FT5426 is not set +# CONFIG_PKG_USING_FT6236 is not set +# CONFIG_PKG_USING_XPT2046_TOUCH is not set +# CONFIG_PKG_USING_CST816X is not set +# CONFIG_PKG_USING_CST812T is not set +# end of touch drivers + +# CONFIG_PKG_USING_LCD_SPI_DRIVER is not set +# CONFIG_PKG_USING_REALTEK_AMEBA is not set +# CONFIG_PKG_USING_BUTTON is not set +# CONFIG_PKG_USING_PCF8574 is not set +# CONFIG_PKG_USING_SX12XX is not set +# CONFIG_PKG_USING_SIGNAL_LED is not set +# CONFIG_PKG_USING_LEDBLINK is not set +# CONFIG_PKG_USING_LITTLED is not set +# CONFIG_PKG_USING_LKDGUI is not set +# CONFIG_PKG_USING_INFRARED is not set +# CONFIG_PKG_USING_MULTI_INFRARED is not set +# CONFIG_PKG_USING_AGILE_BUTTON is not set +# CONFIG_PKG_USING_AGILE_LED is not set +# CONFIG_PKG_USING_AT24CXX is not set +# CONFIG_PKG_USING_MOTIONDRIVER2RTT is not set +# CONFIG_PKG_USING_PCA9685 is not set +# CONFIG_PKG_USING_ILI9341 is not set +# CONFIG_PKG_USING_I2C_TOOLS is not set +# CONFIG_PKG_USING_NRF24L01 is not set +# CONFIG_PKG_USING_RPLIDAR is not set +# CONFIG_PKG_USING_AS608 is not set +# CONFIG_PKG_USING_RC522 is not set +# CONFIG_PKG_USING_WS2812B is not set +# CONFIG_PKG_USING_EXTERN_RTC_DRIVERS is not set +# CONFIG_PKG_USING_MULTI_RTIMER is not set +# CONFIG_PKG_USING_MAX7219 is not set +# CONFIG_PKG_USING_BEEP is not set +# CONFIG_PKG_USING_EASYBLINK is not set +# CONFIG_PKG_USING_PMS_SERIES is not set +# CONFIG_PKG_USING_CAN_YMODEM is not set +# CONFIG_PKG_USING_LORA_RADIO_DRIVER is not set +# CONFIG_PKG_USING_QLED is not set +# CONFIG_PKG_USING_AGILE_CONSOLE is not set +# CONFIG_PKG_USING_LD3320 is not set +# CONFIG_PKG_USING_WK2124 is not set +# CONFIG_PKG_USING_LY68L6400 is not set +# CONFIG_PKG_USING_DM9051 is not set +# CONFIG_PKG_USING_SSD1306 is not set +# CONFIG_PKG_USING_QKEY is not set +# CONFIG_PKG_USING_RS485 is not set +# CONFIG_PKG_USING_RS232 is not set +# CONFIG_PKG_USING_NES is not set +# CONFIG_PKG_USING_VIRTUAL_SENSOR is not set +# CONFIG_PKG_USING_VDEVICE is not set +# CONFIG_PKG_USING_SGM706 is not set +# CONFIG_PKG_USING_RDA58XX is not set +# CONFIG_PKG_USING_LIBNFC is not set +# CONFIG_PKG_USING_MFOC is not set +# CONFIG_PKG_USING_TMC51XX is not set +# CONFIG_PKG_USING_TCA9534 is not set +# CONFIG_PKG_USING_KOBUKI is not set +# CONFIG_PKG_USING_ROSSERIAL is not set +# CONFIG_PKG_USING_MICRO_ROS is not set +# CONFIG_PKG_USING_MCP23008 is not set +# CONFIG_PKG_USING_MISAKA_AT24CXX is not set +# CONFIG_PKG_USING_MISAKA_RGB_BLING is not set +# CONFIG_PKG_USING_LORA_MODEM_DRIVER is not set +# CONFIG_PKG_USING_SOFT_SERIAL is not set +# CONFIG_PKG_USING_MB85RS16 is not set +# CONFIG_PKG_USING_RFM300 is not set +# CONFIG_PKG_USING_IO_INPUT_FILTER is not set +# CONFIG_PKG_USING_LRF_NV7LIDAR is not set +# CONFIG_PKG_USING_AIP650 is not set +# CONFIG_PKG_USING_FINGERPRINT is not set +# CONFIG_PKG_USING_BT_ECB02C is not set +# CONFIG_PKG_USING_UAT is not set +# CONFIG_PKG_USING_ST7789 is not set +# CONFIG_PKG_USING_VS1003 is not set +# CONFIG_PKG_USING_X9555 is not set +# CONFIG_PKG_USING_SYSTEM_RUN_LED is not set +# CONFIG_PKG_USING_BT_MX01 is not set +# CONFIG_PKG_USING_RGPOWER is not set +# CONFIG_PKG_USING_BT_MX02 is not set +# CONFIG_PKG_USING_GC9A01 is not set +# CONFIG_PKG_USING_IK485 is not set +# CONFIG_PKG_USING_SERVO is not set +# CONFIG_PKG_USING_SEAN_WS2812B is not set +# CONFIG_PKG_USING_IC74HC165 is not set +# CONFIG_PKG_USING_IST8310 is not set +# CONFIG_PKG_USING_ST7789_SPI is not set +# CONFIG_PKG_USING_CAN_UDS is not set +# CONFIG_PKG_USING_ISOTP_C is not set +# CONFIG_PKG_USING_IKUNLED is not set +# CONFIG_PKG_USING_INS5T8025 is not set +# CONFIG_PKG_USING_IRUART is not set +# CONFIG_PKG_USING_ST7305 is not set +# CONFIG_PKG_USING_TM1668 is not set +# CONFIG_PKG_USING_TCA9548A is not set +# CONFIG_PKG_USING_SPI_TOOLS is not set +# end of peripheral libraries and drivers + +# +# AI packages +# +# CONFIG_PKG_USING_LIBANN is not set +# CONFIG_PKG_USING_NNOM is not set +# CONFIG_PKG_USING_ONNX_BACKEND is not set +# CONFIG_PKG_USING_ONNX_PARSER is not set +# CONFIG_PKG_USING_TENSORFLOWLITEMICRO is not set +# CONFIG_PKG_USING_ELAPACK is not set +# CONFIG_PKG_USING_ULAPACK is not set +# CONFIG_PKG_USING_QUEST is not set +# CONFIG_PKG_USING_NAXOS is not set +# CONFIG_PKG_USING_R_TINYMAIX is not set +# CONFIG_PKG_USING_LLMCHAT is not set +# end of AI packages + +# +# Signal Processing and Control Algorithm Packages +# +# CONFIG_PKG_USING_APID is not set +# CONFIG_PKG_USING_FIRE_PID_CURVE is not set +# CONFIG_PKG_USING_QPID is not set +# CONFIG_PKG_USING_UKAL is not set +# CONFIG_PKG_USING_DIGITALCTRL is not set +# CONFIG_PKG_USING_KISSFFT is not set +# CONFIG_PKG_USING_CMSIS_DSP is not set +# end of Signal Processing and Control Algorithm Packages + +# +# miscellaneous packages +# + +# +# project laboratory +# +# end of project laboratory + +# +# samples: kernel and components samples +# +# CONFIG_PKG_USING_KERNEL_SAMPLES is not set +# CONFIG_PKG_USING_FILESYSTEM_SAMPLES is not set +# CONFIG_PKG_USING_NETWORK_SAMPLES is not set +# CONFIG_PKG_USING_PERIPHERAL_SAMPLES is not set +# end of samples: kernel and components samples + +# +# entertainment: terminal games and other interesting software packages +# +# CONFIG_PKG_USING_CMATRIX is not set +# CONFIG_PKG_USING_SL is not set +# CONFIG_PKG_USING_CAL is not set +# CONFIG_PKG_USING_ACLOCK is not set +# CONFIG_PKG_USING_THREES is not set +# CONFIG_PKG_USING_2048 is not set +# CONFIG_PKG_USING_SNAKE is not set +# CONFIG_PKG_USING_TETRIS is not set +# CONFIG_PKG_USING_DONUT is not set +# CONFIG_PKG_USING_COWSAY is not set +# CONFIG_PKG_USING_MORSE is not set +# CONFIG_PKG_USING_TINYSQUARE is not set +# end of entertainment: terminal games and other interesting software packages + +# CONFIG_PKG_USING_LIBCSV is not set +# CONFIG_PKG_USING_OPTPARSE is not set +# CONFIG_PKG_USING_FASTLZ is not set +# CONFIG_PKG_USING_MINILZO is not set +# CONFIG_PKG_USING_QUICKLZ is not set +# CONFIG_PKG_USING_LZMA is not set +# CONFIG_PKG_USING_RALARAM is not set +# CONFIG_PKG_USING_MULTIBUTTON is not set +# CONFIG_PKG_USING_FLEXIBLE_BUTTON is not set +# CONFIG_PKG_USING_CANFESTIVAL is not set +# CONFIG_PKG_USING_ZLIB is not set +# CONFIG_PKG_USING_MINIZIP is not set +# CONFIG_PKG_USING_HEATSHRINK is not set +# CONFIG_PKG_USING_DSTR is not set +# CONFIG_PKG_USING_TINYFRAME is not set +# CONFIG_PKG_USING_KENDRYTE_DEMO is not set +# CONFIG_PKG_USING_UPACKER is not set +# CONFIG_PKG_USING_UPARAM is not set +# CONFIG_PKG_USING_HELLO is not set +# CONFIG_PKG_USING_VI is not set +# CONFIG_PKG_USING_KI is not set +# CONFIG_PKG_USING_ARMv7M_DWT is not set +# CONFIG_PKG_USING_CRCLIB is not set +# CONFIG_PKG_USING_LIBCRC is not set +# CONFIG_PKG_USING_LWGPS is not set +# CONFIG_PKG_USING_STATE_MACHINE is not set +# CONFIG_PKG_USING_DESIGN_PATTERN is not set +# CONFIG_PKG_USING_CONTROLLER is not set +# CONFIG_PKG_USING_PHASE_LOCKED_LOOP is not set +# CONFIG_PKG_USING_MFBD is not set +# CONFIG_PKG_USING_SLCAN2RTT is not set +# CONFIG_PKG_USING_SOEM is not set +# CONFIG_PKG_USING_QPARAM is not set +# CONFIG_PKG_USING_CorevMCU_CLI is not set +# CONFIG_PKG_USING_DRMP is not set +# CONFIG_PKG_USING_AUTOGEN_PARAMETER_MANAGER is not set +# end of miscellaneous packages + +# +# Arduino libraries +# +# CONFIG_PKG_USING_RTDUINO is not set + +# +# Projects and Demos +# +# CONFIG_PKG_USING_ARDUINO_MSGQ_C_CPP_DEMO is not set +# CONFIG_PKG_USING_ARDUINO_SKETCH_LOADER_DEMO is not set +# CONFIG_PKG_USING_ARDUINO_ULTRASOUND_RADAR is not set +# CONFIG_PKG_USING_ARDUINO_RTDUINO_SENSORFUSION_SHIELD is not set +# CONFIG_PKG_USING_ARDUINO_NINEINONE_SENSOR_SHIELD is not set +# CONFIG_PKG_USING_ARDUINO_SENSOR_KIT is not set +# CONFIG_PKG_USING_ARDUINO_MATLAB_SUPPORT is not set +# end of Projects and Demos + +# +# Sensors +# +# CONFIG_PKG_USING_ARDUINO_SENSOR_DEVICE_DRIVERS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSOR is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSORLAB is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL375 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L0X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L1X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL6180X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31855 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31865 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31856 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX6675 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90614 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS1 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AHTX0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADT7410 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME680 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9808 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4728 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA219 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR390 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL345 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DHT is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9600 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM6DS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO055 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX1704X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMC56X3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90393 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90395 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ICM20X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DPS310 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTS221 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT4X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT31 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL343 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS726X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AMG88XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2320 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2315 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR329_LTR303 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP3XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MS8607 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90640 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMA8451 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MSA301 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X_RVC is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS2MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303DLH_MAG is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LC709203F is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CAP1188 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CCS811 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_NAU7802 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS331 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS2X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS35HW is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303_ACCEL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3DH is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8591 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL3115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPR121 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPRLS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPU6050 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCT2075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PM25AQI is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_EMC2101 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXAS21002C is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SCD30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXOS8700 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HMC5883_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP006 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TLA202X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCS34725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI7021 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI1145 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP40 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHTC3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HDC1000 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU21DF is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS7341 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU31D is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA260 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP007_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_L3GD20 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP117 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSC2007 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2561 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2591_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VCNL4040 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6070 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML7700 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LIS3DHTR is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_DHT is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ADXL335 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ADXL345 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BME280 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_H3LIS331DL is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MMA7660 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_TSL2561 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_PAJ7620 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_VL53L0X is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ITG3200 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SHT31 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HP20X is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_DRV2605L is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BBM150 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HMC5883L is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LSM303DLH is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_TCS3414CS is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MP503 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BMP085 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HIGHTEMP is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_VEML6070 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SI1145 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SHT35 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_AT42QT1070 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LSM6DS3 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HDC1000 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HM3301 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MCP9600 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LTC2941 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LDC1612 is not set +# CONFIG_PKG_USING_ARDUINO_CAPACITIVESENSOR is not set +# CONFIG_PKG_USING_ARDUINO_JARZEBSKI_MPU6050 is not set +# end of Sensors + +# +# Display +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_GFX_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_U8G2 is not set +# CONFIG_PKG_USING_ARDUINO_TFT_ESPI is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ST7735 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SSD1306 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ILI9341 is not set +# CONFIG_PKG_USING_SEEED_TM1637 is not set +# end of Display + +# +# Timing +# +# CONFIG_PKG_USING_ARDUINO_RTCLIB is not set +# CONFIG_PKG_USING_ARDUINO_MSTIMER2 is not set +# CONFIG_PKG_USING_ARDUINO_TICKER is not set +# CONFIG_PKG_USING_ARDUINO_TASKSCHEDULER is not set +# end of Timing + +# +# Data Processing +# +# CONFIG_PKG_USING_ARDUINO_KALMANFILTER is not set +# CONFIG_PKG_USING_ARDUINO_ARDUINOJSON is not set +# CONFIG_PKG_USING_ARDUINO_TENSORFLOW_LITE_MICRO is not set +# CONFIG_PKG_USING_ARDUINO_RUNNINGMEDIAN is not set +# end of Data Processing + +# +# Data Storage +# + +# +# Communication +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PN532 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI4713 is not set +# end of Communication + +# +# Device Control +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8574 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCA9685 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TPA2016 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DRV2605 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DS1841 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DS3502 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_PCF85063TP is not set +# end of Device Control + +# +# Other +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MFRC630 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI5351 is not set +# end of Other + +# +# Signal IO +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BUSIO is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCA8418 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP23017 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADS1X15 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AW9523 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP3008 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BD3491FS is not set +# end of Signal IO + +# +# Uncategorized +# +# end of Arduino libraries +# end of RT-Thread online packages + +CONFIG_SOC_FAMILY_GD32=y +CONFIG_SOC_SERIES_GD32H77x_H78X=y + +# +# Hardware Drivers Config +# +CONFIG_SOC_GD32H77DIW=y + +# +# Onboard Peripheral Drivers +# +# CONFIG_BSP_USING_FS is not set +# CONFIG_BSP_USING_ETH is not set +# CONFIG_BSP_USING_SDRAM is not set +# CONFIG_BSP_USING_LCD_MIPI is not set +# CONFIG_BSP_USING_OV7670 is not set +# CONFIG_BSP_USING_USB is not set +# end of Onboard Peripheral Drivers + +# +# On-chip Peripheral Drivers +# +CONFIG_BSP_USING_GPIO=y +CONFIG_BSP_USING_UART=y +CONFIG_BSP_USING_SERIAL_V1=y +# CONFIG_BSP_USING_SERIAL_V2 is not set +# CONFIG_BSP_USING_UART0 is not set +CONFIG_BSP_USING_UART1=y +CONFIG_BSP_UART1_TX_PIN="PA2" +CONFIG_BSP_UART1_RX_PIN="PA3" +CONFIG_BSP_UART1_AFIO="AF7" +# CONFIG_BSP_USING_UART2 is not set +# CONFIG_BSP_USING_UART3 is not set +# CONFIG_BSP_USING_UART4 is not set +# CONFIG_BSP_USING_I2C is not set +# CONFIG_BSP_USING_SPI is not set +# CONFIG_BSP_USING_OSPI0 is not set +# CONFIG_BSP_SDIO_USING_SDIO1 is not set +# CONFIG_BSP_USING_CAN is not set +# CONFIG_BSP_USING_PWM is not set +# CONFIG_BSP_USING_ONCHIP_RTC is not set +# end of On-chip Peripheral Drivers + +# +# Board extended module Drivers +# +# CONFIG_BSP_USING_GD32VW553_AT is not set +# end of Board extended module Drivers +# end of Hardware Drivers Config + +# +# Applications +# +# end of Applications diff --git a/bsp/gd32/arm/gd32h77d-ginopi/.gitignore b/bsp/gd32/arm/gd32h77d-ginopi/.gitignore new file mode 100644 index 000000000000..cba70a2178dc --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/.gitignore @@ -0,0 +1,32 @@ +*.pyc +*.map +*.dblite +*.elf +*.bin +*.hex +*.axf +*.old +*.o +*.obj +*.out +*.bak +*.dep +*.lib +*.i +*.d +*.uvguix* +*.scvd +*.ini +.sconsign.dblite +.vscode/ +build/ +Debug/ +Listings/ +Objects/ +packages/ +RTE/ +__pycache__/ +JLinkLog.txt +JLinkSettings.ini +/project.uvoptx +/project.uvprojx diff --git a/bsp/gd32/arm/gd32h77d-ginopi/Kconfig b/bsp/gd32/arm/gd32h77d-ginopi/Kconfig new file mode 100644 index 000000000000..c2de19e5b68c --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/Kconfig @@ -0,0 +1,16 @@ +mainmenu "RT-Thread Configuration" + +BSP_DIR := . + +RTT_DIR := ../../../.. + +PKGS_DIR := packages + +source "$(RTT_DIR)/Kconfig" +osource "$PKGS_DIR/Kconfig" +rsource "../libraries/Kconfig" + +if !RT_USING_NANO +rsource "board/Kconfig" +rsource "applications/Kconfig" +endif diff --git a/bsp/gd32/arm/gd32h77d-ginopi/README.md b/bsp/gd32/arm/gd32h77d-ginopi/README.md new file mode 100644 index 000000000000..066955213b68 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/README.md @@ -0,0 +1,172 @@ +# GD32H77D GinoPi BSP 说明 + +## 简介 + +本 BSP 面向搭载 GD32H77DIW 的 GD32H77D GinoPi 开发板,处理器内核为 Arm Cortex-M7,最高主频为 600 MHz。 + +当前 BSP 支持 GCC 和 MDK5 工具链,默认启用 GPIO、UART1 控制台和 RT-Thread FinSH/MSH。示例程序每隔 500 ms 翻转一次 PC4;其他板载外设均可按需通过 menuconfig 开启。 + +默认 `.config` 仅启用串口和 PIN 外设,LVGL、文件系统、网络、串口 DMA 及其他外设均关闭,仅保留启动必需的 CMSIS 和厂家外设库软件包。 + +## 外设支持 + +| 外设 | 支持情况 | 说明 | +| :----------------- | :------: | :------------------------------------------------------------------------------------------- | +| GPIO | 支持 | 默认启用,使用`GET_PIN()` 访问引脚 | +| UART0 | 支持 | 默认引脚 PF4/PF5,复用功能 AF4 | +| UART1 | 支持 | 默认控制台,PA2/PA3,复用功能 AF7 | +| UART2 | 支持 | 默认引脚 PB10/PB11,复用功能 AF7 | +| UART3 | 支持 | 默认引脚 PC10/PC11,复用功能 AF8 | +| UART4 | 支持 | 默认连接 GD32VW553 AT Wi-Fi,TX: PB12/AF14、RX: PB13/AF14 | +| I2C1 | 支持 | 默认未启用,SCL: PH4、SDA: PB11,复用功能 AF4 | +| CAN1 | 支持 | 默认未启用,TX: PB4/AF4、RX: PB5/AF9 | +| OSPI0/QSPI Flash | 支持 | 默认未启用,板载 8 MiB GD25Q64ESIG,Quad SPI 模式 | +| GD32VW553 AT Wi-Fi | 支持 | 默认未启用,基于 RT-Thread at_device、SAL 和 AT socket,设备名`wifi0`,AT 串口为 `uart4` | +| ENET1 | 支持 | 默认未启用,RMII 接口,PHY 默认地址为 2 | +| SDRAM | 支持 | 默认未启用,板载 32 MiB、16 位 SDRAM | +| USBHS0 Device | 支持 | 默认未启用,Type-C 接口,PA11/PA12,支持高速和全速模式 | +| USBHS1 Host | 支持 | 默认未启用,USB-A 接口,PB14/PB15,全速模式 | +| RTC | 支持 | 默认未启用,可通过 menuconfig 开启 | + +UART 引脚可在 `Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable UART` 中修改。 +I2C1 引脚和复用功能可在 `Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable I2C` 中修改。 +CAN1 引脚和 TX/RX 复用功能可在 `Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable CAN` 中分别修改。 +OSPI0 控制器以 QSPI 模式使用,CS、CLK 和 IO0-IO3 引脚及复用功能可在 `Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable OSPI0 (QSPI)` 中分别修改。 +QSPI Flash 文件系统可在 `Hardware Drivers Config -> Onboard Peripheral Drivers -> Enable File System -> Enable FAL filesystem partition based on GD25Q64ESIG` 中配置。 +GD32VW553 AT Wi-Fi 可在 `Hardware Drivers Config -> Board extended module Drivers -> Enable GD32VW553 AT Wi-Fi module` 中配置。 +ENET、SDRAM 和 USB 可在 `Hardware Drivers Config -> Onboard Peripheral Drivers` 中开启。 + +## 默认配置 + +| 项目 | 配置 | +| :----------- | :--------------- | +| 芯片 | GD32H77DIW | +| 控制台设备 | `uart1` | +| 控制台引脚 | TX: PA2,RX: PA3 | +| 串口参数 | 115200-8-N-1 | +| 示例输出引脚 | PC4 | +| 系统时钟 | 600 MHz | + +## 存储器布局 + +启动表位于 `0x08000000`,保存栈顶和复位入口;应用中断向量表与 VTOR 位于 `0x08010000`。GCC 与 SDK 一致,将启动代码、只读代码和数据加载映像放入 CNVM。MDK 的散装文件保留 ECNVM 作为额外代码区。 + +| 区域 | 起始地址 | 长度 | 用途 | +| :---------- | :------------- | :------- | :--------------------- | +| CNVM 启动表 | `0x08000000` | 8 B | 初始 SP 和复位入口 | +| CNVM 应用区 | `0x08010000` | 1984 KiB | 向量表、代码和只读数据 | +| ECNVM | `0x08200000` | 7680 KiB | MDK 额外代码区 | +| AXI SRAM | `0x24000000` | 768 KiB | 数据、栈和堆 | +| SRAM1 | `0x30004000` | 16 KiB | ENET DMA | +| ITCM RAM | `0x00000000` | 128 KiB | ITCM 段 | +| DTCM RAM | `0x20000000` | 256 KiB | DTCM 段 | + +## 使用说明 + +### 准备软件包 + +系列宏使用 `SOC_SERIES_GD32H77x_H78X`,芯片型号使用 `SOC_GD32H77DIW`。需要支持 `GD32H77x_78x` 的 `gd32-arm-cmsis` 和 `gd32-arm-series` 软件包,源码位于 `../libraries/`。CMSIS 软件包负责启动文件与系统时钟,series 软件包负责厂家外设源码及其构建选择;RT-Thread 驱动仅构建适配层。 + +首次编译或软件包配置发生变化后,在 BSP 目录执行: + +```bash +pkgs --update +``` + +### GCC 编译 + +SDK 链接脚本使用 `READONLY` 段属性,建议使用 Arm GNU Toolchain 13.3。已用该版本检查启动汇编及启动表、向量表、LVGL DTCM 内存池和 ENET DMA 的链接布局。 + +确保 `arm-none-eabi-gcc` 所在目录已经加入 `PATH`,或者通过 `RTT_EXEC_PATH` 指定工具链目录,然后执行: + +```bash +scons -j8 +``` + +构建成功后生成: + +| 文件 | 说明 | +| :---------------- | :------------------------------------------- | +| `rt-thread.elf` | 包含两个加载区域和调试信息,推荐用于调试下载 | +| `rt-thread.bin` | 包含启动表和 CNVM 应用区的镜像 | +| `rtthread.map` | GCC 链接映射文件 | + +使用调试器下载 `rt-thread.elf` 时,调试器会根据 ELF 加载段写入对应存储区。 + +直接下载 `rt-thread.bin` 时,下载起始地址必须设置为 `0x08000000`。程序代码位于该文件偏移 `0x00200000`,对应 MCU 地址 `0x08200000`。 + +### MDK5 编译 + +可以重新生成 MDK5 工程: + +```bash +scons --target=mdk5 +``` + +然后使用 MDK5 打开 `project.uvprojx`,编译并通过 SWD 调试器下载 `rt-thread.axf`。 + +### 硬件连接 + +使用 SWD 调试器连接开发板并供电。查看控制台时,将串口适配器连接到以下引脚: + +| 串口适配器 | 开发板 | +| :--------- | :-------------- | +| RX | PA2(UART1 TX) | +| TX | PA3(UART1 RX) | +| GND | GND | + +### 运行结果 + +复位开发板后,PC4 每隔 500 ms 翻转一次;启用 MIPI DSI LCD 时,屏幕会显示一个文本框和触摸虚拟键盘。打开串口终端可以看到 RT-Thread 启动信息并进入 MSH: + +```text + \ | / +- RT - Thread Operating System + / | \ +msh /> +``` + +### QSPI Flash 文件系统 + +板载 GD25Q64ESIG 注册为 FAL Flash 设备 `gd25q64e`,整个 8 MiB 空间配置为 `filesystem` 分区。系统会创建同名块设备,并尝试将 Elm-FAT 挂载到 `/flash`。 + +新 Flash 尚未格式化时,首次自动挂载会失败。确认分区中没有需要保留的数据后执行以下命令,`mkfs` 会清空整个 `filesystem` 分区: + +```text +fal probe filesystem +fal read 0 16 +mkfs -t elm filesystem +mount filesystem /flash elm +echo qspi-ok > /flash/qspi.txt +cat /flash/qspi.txt +``` + +复位后自动挂载成功,再执行 `cat /flash/qspi.txt`,可验证文件系统数据是否能够持久保存。FAL 块大小和 Elm-FAT 最大扇区大小均为 4096 字节。 + +### GD32VW553 MQTT 示例 + +GD32VW553 模块默认通过 `uart4` 接入 `at_device`,MQTT 应用通过 kawaii-mqtt、SAL socket 和 AT socket 访问网络。Wi-Fi 与 MQTT 参数在 `menuconfig` 中配置,MSH 命令如下: + +```text +gd32vw553_mqtt_start +gd32vw553_mqtt_pub +gd32vw553_mqtt_stop +``` + +## 配置 BSP + +在 BSP 目录打开 RT-Thread Env,执行: + +```bash +menuconfig +pkgs --update +scons -j8 +``` + +修改配置后应由 `.config` 重新生成 `rtconfig.h`,不要直接编辑 `rtconfig.h`。 + +## 注意事项 + +- 启动表位于 `0x08000000`,应用向量表位于 `0x08010000`,下载时必须包含启动表。 +- 当前 SDK 使用 Serial V1,未适配此芯片的 Serial V2 和串口 DMA,不应切换到这些配置。 +- 如果修改 CNVM 或 ECNVM 起始地址,需要同时更新链接脚本、VTOR 和下载配置。 diff --git a/bsp/gd32/arm/gd32h77d-ginopi/SConscript b/bsp/gd32/arm/gd32h77d-ginopi/SConscript new file mode 100644 index 000000000000..c7ef7659ecea --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/SConscript @@ -0,0 +1,14 @@ +# for module compiling +import os +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) + +Return('objs') diff --git a/bsp/gd32/arm/gd32h77d-ginopi/SConstruct b/bsp/gd32/arm/gd32h77d-ginopi/SConstruct new file mode 100644 index 000000000000..63da5bfe77e5 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/SConstruct @@ -0,0 +1,94 @@ +import os +import sys +import rtconfig + +if os.getenv('RTT_ROOT'): + RTT_ROOT = os.getenv('RTT_ROOT') +else: + RTT_ROOT = os.path.normpath(os.getcwd() + '/../../../..') + +sys.path = sys.path + [os.path.join(RTT_ROOT, 'tools')] +try: + from building import * +except: + print('Cannot found RT-Thread root directory, please check RTT_ROOT') + print(RTT_ROOT) + exit(-1) + +def bsp_pkg_check(): + import subprocess + + check_paths = [ + os.path.join("packages", "gd32-arm-cmsis-latest"), + os.path.join("packages", "gd32-arm-series-latest") + ] + + need_update = not all(os.path.exists(p) for p in check_paths) + + if need_update: + print("\n===============================================================================") + print("Dependency packages missing, please running 'pkgs --update'...") + print("If no packages are fetched, run 'pkgs --upgrade' first, then 'pkgs --update'...") + print("===============================================================================") + exit(1) + +RegisterPreBuildingAction(bsp_pkg_check) + +TARGET = 'rt-thread.' + rtconfig.TARGET_EXT + +DefaultEnvironment(tools=[]) +env = Environment(tools = ['mingw'], + AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS, + CC = rtconfig.CC, CFLAGS = rtconfig.CFLAGS, + AR = rtconfig.AR, ARFLAGS = '-rc', + CXX = rtconfig.CXX, CXXFLAGS = rtconfig.CXXFLAGS, + LINK = rtconfig.LINK, LINKFLAGS = rtconfig.LFLAGS) +env.PrependENVPath('PATH', rtconfig.EXEC_PATH) + +if rtconfig.PLATFORM in ['iccarm']: + env.Replace(CCCOM = ['$CC $CFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -o $TARGET $SOURCES']) + env.Replace(ARFLAGS = ['']) + env.Replace(LINKCOM = env["LINKCOM"] + ' --map rt-thread.map') + +Export('env') +Export('RTT_ROOT') +Export('rtconfig') + +SDK_ROOT = os.path.abspath('./') + +if os.path.exists(SDK_ROOT + '/libraries'): + libraries_path_prefix = SDK_ROOT + '/libraries' +else: + libraries_path_prefix = os.path.dirname(SDK_ROOT) + '/libraries' + +SDK_LIB = libraries_path_prefix +Export('SDK_LIB') + +# prepare building environment +objs = PrepareBuilding(env, RTT_ROOT, has_libcpu=False) + +if GetDepend('PKG_LVGL_USING_LATEST'): + def is_lvgl_frogfs_source(item): + path = os.path.normcase(os.path.normpath(str(item))) + frogfs_path = os.path.normcase(os.path.normpath( + os.path.join('packages', 'LVGL-latest', 'src', 'libs', 'frogfs'))) + return frogfs_path in path + + # FrogFS is disabled, but the upstream RT-Thread SConscript still lists its sources. + objs = [obj for obj in objs if not is_lvgl_frogfs_source(obj)] + for group in Projects: + group['src'] = [src for src in group['src'] + if not is_lvgl_frogfs_source(src)] + +if GetDepend('PKG_USING_GT911'): + for group in Projects: + if group['name'] == 'gt911': + local_defines = group.get('LOCAL_CPPDEFINES', []) + if 'memcpy=rt_memcpy' not in local_defines: + group['LOCAL_CPPDEFINES'] = local_defines + ['memcpy=rt_memcpy'] + +# include libraries +objs.extend(SConscript(os.path.join(libraries_path_prefix, 'gd32_drivers', 'SConscript'),variant_dir='build/libraries/gd32_drivers', duplicate=0)) + +# make a building +DoBuilding(TARGET, objs) diff --git a/bsp/gd32/arm/gd32h77d-ginopi/applications/Kconfig b/bsp/gd32/arm/gd32h77d-ginopi/applications/Kconfig new file mode 100644 index 000000000000..fcd511fe2a30 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/applications/Kconfig @@ -0,0 +1,131 @@ +menu "Applications" + + config BSP_USING_USB_CDC_EXAMPLE + bool "Enable USB CDC heartbeat in main" + depends on BSP_USB_USING_DEVICE && RT_USB_DEVICE_CDC + default n + help + Send one heartbeat per second on vcom from the LED loop. + + config BSP_USING_SDRAM_HEAP_TEST + bool "Enable SDRAM heap test command" + depends on BSP_USING_SDRAM && RT_USING_HEAP && RT_USING_FINSH + depends on RT_USING_MEMHEAP_AS_HEAP && RT_USING_MEMHEAP_AUTO_BINDING + default n + help + Register sdram_heap_test to verify an allocated SDRAM buffer. + Use memheap objects as the system heap with automatic binding + so allocations can use the external SDRAM heap. + + config BSP_USING_FILESYSTEM_TEST + bool "Enable filesystem read/write benchmark commands" + depends on RT_USING_DFS && RT_USING_HEAP && RT_USING_FINSH + default n + help + Register readspeed and writespeed commands. + + config BSP_USING_PWM_DUAL_TEST + bool "Enable PWM2/PWM30 test command" + depends on BSP_USING_PWM2 && BSP_USING_PWM30 && RT_USING_FINSH + default n + help + Register pwm_dual_test to control the two PWM outputs. + + config BSP_USING_RTC_ALARM_EXAMPLE + bool "Enable RTC alarm example command" + depends on BSP_USING_ALARM && RT_USING_FINSH + default n + help + Register rtc_alarm to configure and inspect a one-shot alarm. + + config BSP_USING_OV7670_PREVIEW + bool "Enable OV7670 LVGL preview" + depends on PKG_LVGL_USING_LATEST && BSP_USING_LCD_MIPI && BSP_USING_OV7670 + default n + help + Add the camera preview UI and its optional MSH control command. + + config BSP_USING_GD32VW553_MQTT_DEMO + bool "Enable GD32VW553 MQTT demo" + depends on BSP_USING_GD32VW553_AT + select PKG_USING_KAWAII_MQTT + select KAWAII_MQTT_NETSOCKET_USE_SAL + default n + help + Enable a GD32VW553 MQTT demo based on the RT-Thread + kawaii-mqtt package. The MQTT network path is + kawaii-mqtt, SAL socket, at_device, and GD32VW553 AT. + + if BSP_USING_GD32VW553_MQTT_DEMO + + config BSP_GD32VW553_MQTT_HOST + string "MQTT broker address" + default "broker.emqx.io" + help + Use a numeric IPv4 address when the module firmware + does not support AT+CIPDOMAIN. + + config BSP_GD32VW553_MQTT_PORT + int "MQTT broker port" + range 1 65535 + default 1883 + + config BSP_GD32VW553_MQTT_SUB_TOPIC + string "MQTT subscribe topic" + default "rtt/gd32vw553/sub" + + config BSP_GD32VW553_MQTT_PUB_TOPIC + string "MQTT publish topic" + default "rtt/gd32vw553/pub" + + config BSP_GD32VW553_MQTT_MESSAGE + string "MQTT publish message" + default "hello from GD32VW553" + + config BSP_GD32VW553_MQTT_CLIENT_ID + string "MQTT client id" + default "gd32vw553" + + config BSP_GD32VW553_MQTT_USERNAME + string "MQTT username" + default "" + + config BSP_GD32VW553_MQTT_PASSWORD + string "MQTT password" + default "" + + endif + + config BSP_USING_LVGL_BOARD_DEMO + bool "Enable LVGL board demonstration" + depends on PKG_LVGL_USING_LATEST + select BSP_USING_LCD_MIPI + select BSP_USING_TOUCH_GT911 + select BSP_USING_OV7670 + select BSP_USING_OV7670_PREVIEW + select BSP_USING_FS + select BSP_USING_FLASH_FATFS + select BSP_USING_USB + select BSP_USB_USING_HOST + select RT_USBH_MSTORAGE + select BSP_USING_CAN + select BSP_USING_CAN1 + select BSP_USING_PWM + select BSP_USING_PWM2 + select BSP_USING_PWM30 + select BSP_USING_SPI + select BSP_USING_SPI3 + select BSP_USING_I2C + select BSP_USING_HARD_I2C + select BSP_USING_HARD_I2C1 + select BSP_USING_GD32VW553_AT + select BSP_USING_GD32VW553_MQTT_DEMO + default n + help + Enable the LVGL board console for storage, CAN, MQTT, dual PWM, + OV7670 camera preview, SPI3 loopback, and I2C address scanning. + + config PKG_LVGL_THREAD_STACK_SIZE + range 8192 2147483647 if BSP_USING_LVGL_BOARD_DEMO + +endmenu diff --git a/bsp/gd32/arm/gd32h77d-ginopi/applications/SConscript b/bsp/gd32/arm/gd32h77d-ginopi/applications/SConscript new file mode 100644 index 000000000000..130142ef7480 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/applications/SConscript @@ -0,0 +1,29 @@ +from building import * + +cwd = GetCurrentDir() +src = ['main.c'] + +if GetDepend('BSP_USING_SDRAM_HEAP_TEST'): + src += ['sdram_heap_test.c'] + +if GetDepend('BSP_USING_FILESYSTEM_TEST'): + src += ['filesystem_test.c'] + +if GetDepend('BSP_USING_PWM_DUAL_TEST'): + src += ['pwm_dual_test.c'] + +if GetDepend('BSP_USING_RTC_ALARM_EXAMPLE'): + src += ['rtc_alarm_example.c'] + +if GetDepend('BSP_USING_OV7670_PREVIEW'): + src += ['ov7670_preview.c'] + +if GetDepend(['BSP_USING_GD32VW553_MQTT_DEMO', 'PKG_USING_KAWAII_MQTT']): + src += ['gd32vw553_mqtt_demo.c'] + +if GetDepend(['BSP_USING_LVGL_BOARD_DEMO', 'PKG_USING_LVGL']): + src += ['board_demo.c', 'board_demo_backend.c'] + +group = DefineGroup('Applications', src, depend = [''], CPPPATH = [cwd]) + +Return('group') diff --git a/bsp/gd32/arm/gd32h77d-ginopi/applications/board_demo.c b/bsp/gd32/arm/gd32h77d-ginopi/applications/board_demo.c new file mode 100644 index 000000000000..51e9b7918891 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/applications/board_demo.c @@ -0,0 +1,1788 @@ +/* + * Copyright (c) 2006-2026 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-09-08 CYFS implement the board dashboard for peripherals, storage, camera, and MQTT + */ + +#include + +#if defined(PKG_USING_LVGL) && defined(BSP_USING_LVGL_BOARD_DEMO) + +#include +#include +#include +#include + +#include "board_demo_backend.h" + +#ifdef BSP_USING_OV7670_PREVIEW +#include "ov7670_preview.h" +#endif + +#define DBG_TAG "app.board.demo" +#define DBG_LVL DBG_INFO +#include + +#ifndef BSP_GD32VW553_MQTT_HOST +#define BSP_GD32VW553_MQTT_HOST "broker.emqx.io" +#endif + +#ifndef BSP_GD32VW553_MQTT_SUB_TOPIC +#define BSP_GD32VW553_MQTT_SUB_TOPIC "rtt/gd32vw553/sub" +#endif + +#ifndef BSP_GD32VW553_MQTT_PUB_TOPIC +#define BSP_GD32VW553_MQTT_PUB_TOPIC "rtt/gd32vw553/pub" +#endif + +#define BOARD_DEMO_WIDTH 720 +#define BOARD_DEMO_HEIGHT 720 +#define BOARD_DEMO_SIDEBAR_WIDTH 132 +#define BOARD_DEMO_HEADER_HEIGHT 64 +#define BOARD_DEMO_CONTENT_WIDTH (BOARD_DEMO_WIDTH - BOARD_DEMO_SIDEBAR_WIDTH) +#define BOARD_DEMO_CONTENT_HEIGHT (BOARD_DEMO_HEIGHT - BOARD_DEMO_HEADER_HEIGHT) +#define BOARD_DEMO_PAGE_INNER_WIDTH (BOARD_DEMO_CONTENT_WIDTH - 32) +#define BOARD_DEMO_REFRESH_MS 150U +#define BOARD_DEMO_STORAGE_POLL_MS 1000U +#define BOARD_DEMO_KEYBOARD_HEIGHT 260 +#define BOARD_DEMO_LCD_DEVICE_NAME "lcd" + +#define COLOR_NAV lv_color_hex(0x242A2D) +#define COLOR_NAV_ACTIVE lv_color_hex(0x365B56) +#define COLOR_PAGE lv_color_hex(0xF1F4F5) +#define COLOR_SURFACE lv_color_hex(0xFFFFFF) +#define COLOR_TEXT lv_color_hex(0x1F2933) +#define COLOR_MUTED lv_color_hex(0x64727D) +#define COLOR_BORDER lv_color_hex(0xD6DEE2) +#define COLOR_ACCENT lv_color_hex(0x0F766E) +#define COLOR_INFO lv_color_hex(0x2563A6) +#define COLOR_ACCENT_SOFT lv_color_hex(0xE1F0EB) +#define COLOR_WARNING lv_color_hex(0xB45309) +#define COLOR_DANGER lv_color_hex(0xB42318) + +enum board_demo_page_id +{ + BOARD_DEMO_PAGE_OVERVIEW = 0, + BOARD_DEMO_PAGE_FILES, + BOARD_DEMO_PAGE_CAN, + BOARD_DEMO_PAGE_MQTT, + BOARD_DEMO_PAGE_PWM, + BOARD_DEMO_PAGE_CAMERA, + BOARD_DEMO_PAGE_SPI, + BOARD_DEMO_PAGE_I2C, + BOARD_DEMO_PAGE_COUNT, +}; + +static const char *const board_demo_page_names[BOARD_DEMO_PAGE_COUNT] = +{ + "Overview", "Files", "CAN", "MQTT", + "PWM", "Camera", "SPI", "I2C", +}; + +static const char *const board_demo_page_symbols[BOARD_DEMO_PAGE_COUNT] = +{ + LV_SYMBOL_HOME, LV_SYMBOL_DIRECTORY, LV_SYMBOL_LOOP, LV_SYMBOL_WIFI, + LV_SYMBOL_SETTINGS, LV_SYMBOL_IMAGE, LV_SYMBOL_SHUFFLE, LV_SYMBOL_LIST, +}; + +static lv_obj_t *demo_screen; +static lv_obj_t *demo_pages[BOARD_DEMO_PAGE_COUNT]; +static lv_obj_t *demo_nav_buttons[BOARD_DEMO_PAGE_COUNT]; +static lv_obj_t *demo_header_title; +static lv_obj_t *demo_header_status; +static lv_obj_t *demo_keyboard; +static lv_timer_t *demo_refresh_timer; +static enum board_demo_page_id demo_active_page; + +static lv_obj_t *overview_status[BOARD_DEMO_PAGE_COUNT]; +static lv_obj_t *overview_uptime; + +static lv_obj_t *files_source_buttons[BOARD_DEMO_STORAGE_COUNT]; +static lv_obj_t *files_source_status[BOARD_DEMO_STORAGE_COUNT]; +static lv_obj_t *files_up_button; +static lv_obj_t *files_path_label; +static lv_obj_t *files_status_label; +static lv_obj_t *files_table; +static lv_obj_t *files_empty_label; +static lv_obj_t *files_preview_title; +static lv_obj_t *files_preview_panel; +static lv_obj_t *files_preview_label; + +static lv_obj_t *can_status_label; +static lv_obj_t *can_baud_dropdown; +static lv_obj_t *can_id_textarea; +static lv_obj_t *can_data_textarea; +static lv_obj_t *can_extended_switch; +static lv_obj_t *can_table; + +static lv_obj_t *mqtt_status_label; +static lv_obj_t *mqtt_topic_textarea; +static lv_obj_t *mqtt_payload_textarea; +static lv_obj_t *mqtt_rx_topic_label; +static lv_obj_t *mqtt_rx_payload_label; + +static lv_obj_t *pwm_status_label; +static lv_obj_t *brightness_slider; +static lv_obj_t *brightness_value_label; +static lv_obj_t *pwm_frequency_dropdown; +static lv_obj_t *pwm2_switch; +static lv_obj_t *pwm2_slider; +static lv_obj_t *pwm2_value_label; +static lv_obj_t *pwm30_switch; +static lv_obj_t *pwm30_slider; +static lv_obj_t *pwm30_value_label; + +static lv_obj_t *camera_status_label; +static lv_obj_t *camera_counter_label; + +static lv_obj_t *spi_status_label; +static lv_obj_t *spi_frequency_dropdown; +static lv_obj_t *spi_length_dropdown; +static lv_obj_t *spi_run_button; +static lv_obj_t *spi_log_label; + +static lv_obj_t *i2c_status_label; +static lv_obj_t *i2c_result_table; +static lv_obj_t *i2c_scan_button; +static lv_obj_t *i2c_log_label; + +static rt_device_t board_demo_lcd_device; + +static struct board_demo_snapshot demo_snapshot; +static rt_uint32_t files_sequence; +static rt_uint32_t file_sequence; +static rt_uint32_t can_sequence; +static rt_uint32_t mqtt_sequence; +static rt_uint32_t pwm_sequence; +static rt_uint32_t spi_sequence; +static rt_uint32_t i2c_sequence; +static rt_uint8_t storage_mounts; +static rt_uint32_t storage_poll_tick; +static rt_bool_t storage_polled; + +static void board_demo_show_page(enum board_demo_page_id page_id); +static void board_demo_update_files(void); +static void board_demo_update_storage(void); +static void board_demo_update_runtime(void); + +static void board_demo_files_request_path(const char *path) +{ + char status[64]; + int result; + + lv_label_set_text(files_path_label, path); + lv_label_set_text(files_status_label, "Loading..."); + result = board_demo_request_fs_list(path); + if (result == RT_EOK) + { + result = board_demo_backend_get_snapshot(&demo_snapshot); + if (result == RT_EOK) + { + board_demo_update_files(); + LOG_I("storage list completed: %s", path); + } + else + { + lv_label_set_text(files_status_label, "Storage state unavailable"); + LOG_E("cannot read storage state for %s: %d", path, result); + } + } + else + { + rt_snprintf(status, sizeof(status), "Storage scan failed: %d", result); + lv_label_set_text(files_status_label, status); + LOG_E("cannot scan storage path %s: %d", path, result); + } +} + +static void board_demo_copy_string(char *destination, const char *source, + rt_size_t destination_size) +{ + if (destination_size == 0U) + { + return; + } + + rt_strncpy(destination, source, destination_size - 1U); + destination[destination_size - 1U] = '\0'; +} + +static void board_demo_set_label_text(lv_obj_t *label, const char *text) +{ + if (rt_strcmp(lv_label_get_text(label), text) != 0) + { + lv_label_set_text(label, text); + } +} + +static lv_obj_t *board_demo_create_label(lv_obj_t *parent, const char *text, + lv_coord_t x, lv_coord_t y, + lv_coord_t width) +{ + lv_obj_t *label; + + label = lv_label_create(parent); + lv_label_set_text(label, text); + lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP); + lv_obj_set_pos(label, x, y); + lv_obj_set_width(label, width); + lv_obj_set_style_text_color(label, COLOR_TEXT, 0); + return label; +} + +static lv_obj_t *board_demo_create_line(lv_obj_t *parent, const char *text, + lv_coord_t x, lv_coord_t y, + lv_coord_t width) +{ + lv_obj_t *label = board_demo_create_label(parent, text, x, y, width); + + lv_label_set_long_mode(label, LV_LABEL_LONG_MODE_DOTS); + lv_obj_set_height(label, 22); + return label; +} + +static void board_demo_style_table(lv_obj_t *table) +{ + lv_obj_set_style_radius(table, 6, 0); + lv_obj_set_style_border_width(table, 1, 0); + lv_obj_set_style_border_color(table, COLOR_BORDER, 0); + lv_obj_set_style_bg_color(table, COLOR_SURFACE, 0); + lv_obj_set_style_pad_all(table, 0, 0); + lv_obj_set_style_pad_hor(table, 10, LV_PART_ITEMS); + lv_obj_set_style_pad_ver(table, 12, LV_PART_ITEMS); + lv_obj_set_style_border_color(table, COLOR_BORDER, LV_PART_ITEMS); + lv_obj_set_style_border_side(table, LV_BORDER_SIDE_BOTTOM, LV_PART_ITEMS); + lv_obj_set_style_bg_color(table, COLOR_ACCENT_SOFT, + LV_PART_ITEMS | LV_STATE_PRESSED); + lv_obj_set_scroll_dir(table, LV_DIR_VER); +} + +static lv_obj_t *board_demo_create_button(lv_obj_t *parent, const char *text, + lv_coord_t x, lv_coord_t y, + lv_coord_t width, lv_coord_t height, + lv_event_cb_t event_callback) +{ + lv_obj_t *button; + lv_obj_t *label; + + button = lv_button_create(parent); + lv_obj_set_pos(button, x, y); + lv_obj_set_size(button, width, height); + lv_obj_set_style_radius(button, 6, 0); + lv_obj_set_style_bg_color(button, COLOR_ACCENT, 0); + lv_obj_set_style_shadow_width(button, 0, 0); + lv_obj_set_style_pad_all(button, 6, 0); + lv_obj_set_style_text_color(button, lv_color_white(), 0); + if (event_callback != RT_NULL) + { + lv_obj_add_event_cb(button, event_callback, LV_EVENT_CLICKED, RT_NULL); + } + + label = lv_label_create(button); + lv_label_set_text(label, text); + lv_obj_set_width(label, lv_pct(100)); + lv_obj_set_height(label, 22); + lv_label_set_long_mode(label, LV_LABEL_LONG_MODE_DOTS); + lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_center(label); + return button; +} + +static lv_obj_t *board_demo_create_page(enum board_demo_page_id page_id) +{ + lv_obj_t *page; + lv_obj_t *title; + + page = lv_obj_create(demo_screen); + lv_obj_set_pos(page, BOARD_DEMO_SIDEBAR_WIDTH, BOARD_DEMO_HEADER_HEIGHT); + lv_obj_set_size(page, BOARD_DEMO_CONTENT_WIDTH, BOARD_DEMO_CONTENT_HEIGHT); + lv_obj_set_style_bg_color(page, COLOR_PAGE, 0); + lv_obj_set_style_bg_opa(page, LV_OPA_COVER, 0); + lv_obj_set_style_border_width(page, 0, 0); + lv_obj_set_style_radius(page, 0, 0); + lv_obj_set_style_pad_all(page, 16, 0); + lv_obj_set_scroll_dir(page, LV_DIR_VER); + + title = board_demo_create_label(page, board_demo_page_names[page_id], + 0, 0, BOARD_DEMO_PAGE_INNER_WIDTH); + lv_obj_set_style_text_font(title, &lv_font_montserrat_16, 0); + return page; +} + +static void board_demo_hide_keyboard(void) +{ + lv_keyboard_set_textarea(demo_keyboard, RT_NULL); + lv_obj_set_hidden(demo_keyboard, true); + lv_obj_set_height(demo_pages[demo_active_page], BOARD_DEMO_CONTENT_HEIGHT); + lv_obj_scroll_to_y(demo_pages[demo_active_page], 0, LV_ANIM_OFF); +} + +static void board_demo_keyboard_event(lv_event_t *event) +{ + lv_event_code_t code; + + code = lv_event_get_code(event); + if ((code == LV_EVENT_READY) || (code == LV_EVENT_CANCEL)) + { + board_demo_hide_keyboard(); + } +} + +static void board_demo_textarea_event(lv_event_t *event) +{ + lv_obj_t *textarea; + + if ((lv_event_get_code(event) != LV_EVENT_FOCUSED) && + (lv_event_get_code(event) != LV_EVENT_CLICKED)) + { + return; + } + + textarea = lv_event_get_target_obj(event); + lv_keyboard_set_textarea(demo_keyboard, textarea); + lv_obj_set_hidden(demo_keyboard, false); + lv_obj_move_foreground(demo_keyboard); + lv_obj_set_height(demo_pages[demo_active_page], + BOARD_DEMO_CONTENT_HEIGHT - BOARD_DEMO_KEYBOARD_HEIGHT); + lv_obj_update_layout(demo_pages[demo_active_page]); + lv_obj_scroll_to_view(textarea, LV_ANIM_OFF); +} + +static void board_demo_nav_event(lv_event_t *event) +{ + enum board_demo_page_id page_id; + + page_id = (enum board_demo_page_id)(rt_ubase_t)lv_event_get_user_data(event); + board_demo_show_page(page_id); +} + +static void board_demo_show_page(enum board_demo_page_id page_id) +{ + enum board_demo_page_id index; + + if (page_id >= BOARD_DEMO_PAGE_COUNT) + { + return; + } + + board_demo_hide_keyboard(); + demo_active_page = page_id; + for (index = BOARD_DEMO_PAGE_OVERVIEW; + index < BOARD_DEMO_PAGE_COUNT; index++) + { + if (index == page_id) + { + lv_obj_set_hidden(demo_pages[index], false); + lv_obj_set_style_bg_color(demo_nav_buttons[index], COLOR_NAV_ACTIVE, 0); + } + else + { + lv_obj_set_hidden(demo_pages[index], true); + lv_obj_set_style_bg_color(demo_nav_buttons[index], COLOR_NAV, 0); + } + } + + if (page_id == BOARD_DEMO_PAGE_FILES) + { + board_demo_files_request_path(demo_snapshot.fs_path[0] == '\0' ? + "/flash" : demo_snapshot.fs_path); + } +} + +static void board_demo_storage_source_event(lv_event_t *event) +{ + const char *path; + + path = (const char *)lv_event_get_user_data(event); + board_demo_files_request_path(path); +} + +static void board_demo_files_refresh_event(lv_event_t *event) +{ + RT_UNUSED(event); + board_demo_files_request_path(demo_snapshot.fs_path[0] == '\0' ? + "/flash" : demo_snapshot.fs_path); +} + +static void board_demo_files_up_event(lv_event_t *event) +{ + char path[BOARD_DEMO_FS_PATH_MAX]; + char *separator = RT_NULL; + char *cursor; + rt_size_t root_length; + int source; + + RT_UNUSED(event); + board_demo_copy_string(path, demo_snapshot.fs_path, sizeof(path)); + source = board_demo_fs_source(path); + if (source < 0) + { + return; + } + root_length = rt_strlen(board_demo_storage_roots[source]); + if (rt_strlen(path) <= root_length) + { + return; + } + + for (cursor = path; *cursor != '\0'; cursor++) + { + if (*cursor == '/') + { + separator = cursor; + } + } + if ((separator != RT_NULL) && + ((rt_size_t)(separator - path) >= root_length)) + { + *separator = '\0'; + board_demo_files_request_path(path); + } +} + +static void board_demo_files_table_event(lv_event_t *event) +{ + char path[BOARD_DEMO_FS_PATH_MAX]; + const struct board_demo_fs_entry *entry; + uint32_t row; + uint32_t column; + int length; + int result; + + RT_UNUSED(event); + lv_table_get_selected_cell(files_table, &row, &column); + RT_UNUSED(column); + if ((row == 0U) || (row > demo_snapshot.fs_entry_count)) + { + return; + } + + entry = &demo_snapshot.fs_entries[row - 1U]; + length = rt_snprintf(path, sizeof(path), "%s/%s", + demo_snapshot.fs_path, entry->name); + if ((length <= 0) || ((rt_size_t)length >= sizeof(path))) + { + return; + } + + if (entry->is_directory) + { + board_demo_files_request_path(path); + } + else + { + lv_label_set_text(files_preview_title, entry->name); + lv_label_set_text(files_preview_label, "Reading..."); + result = board_demo_request_file_preview(path); + if (result == RT_EOK) + { + result = board_demo_backend_get_snapshot(&demo_snapshot); + if (result == RT_EOK) + { + board_demo_update_files(); + } + } + if (result != RT_EOK) + { + lv_label_set_text(files_preview_label, "Cannot read file preview"); + LOG_E("cannot read file preview for %s: %d", path, result); + } + } +} + +static int board_demo_hex_digit(char character) +{ + if ((character >= '0') && (character <= '9')) + { + return character - '0'; + } + if ((character >= 'a') && (character <= 'f')) + { + return character - 'a' + 10; + } + if ((character >= 'A') && (character <= 'F')) + { + return character - 'A' + 10; + } + return -1; +} + +static int board_demo_parse_can_data(const char *text, rt_uint8_t data[8], + rt_uint8_t *length) +{ + int high; + int low; + rt_uint8_t count = 0U; + + while (*text != '\0') + { + while ((*text == ' ') || (*text == ',') || (*text == ':') || + (*text == '-')) + { + text++; + } + if (*text == '\0') + { + break; + } + if (count >= 8U) + { + return -RT_EFULL; + } + + high = board_demo_hex_digit(*text++); + if (high < 0) + { + return -RT_EINVAL; + } + low = board_demo_hex_digit(*text++); + if (low < 0) + { + return -RT_EINVAL; + } + data[count++] = (rt_uint8_t)((high << 4) | low); + + if ((*text != '\0') && (*text != ' ') && (*text != ',') && + (*text != ':') && (*text != '-')) + { + return -RT_EINVAL; + } + } + + *length = count; + return RT_EOK; +} + +static void board_demo_can_baud_event(lv_event_t *event) +{ + static const rt_uint32_t baud_rates[] = + { + CAN125kBaud, CAN250kBaud, CAN500kBaud, CAN1MBaud, + }; + uint32_t selected; + + RT_UNUSED(event); + selected = lv_dropdown_get_selected(can_baud_dropdown); + if (selected < sizeof(baud_rates) / sizeof(baud_rates[0])) + { + board_demo_request_can_baud(baud_rates[selected]); + } +} + +static void board_demo_can_send_event(lv_event_t *event) +{ + const char *id_text; + const char *data_text; + char *end; + rt_uint32_t id; + rt_uint8_t data[8]; + rt_uint8_t length; + rt_bool_t extended; + int result; + + RT_UNUSED(event); + id_text = lv_textarea_get_text(can_id_textarea); + data_text = lv_textarea_get_text(can_data_textarea); + id = (rt_uint32_t)strtoul(id_text, &end, 16); + if ((id_text[0] == '\0') || (*end != '\0')) + { + lv_label_set_text(can_status_label, "Invalid CAN identifier"); + return; + } + + result = board_demo_parse_can_data(data_text, data, &length); + if (result != RT_EOK) + { + lv_label_set_text(can_status_label, "Use hexadecimal byte pairs, up to 8 bytes"); + return; + } + + extended = lv_obj_has_state(can_extended_switch, LV_STATE_CHECKED) ? + RT_TRUE : RT_FALSE; + result = board_demo_request_can_send(id, extended, data, length); + if (result != RT_EOK) + { + lv_label_set_text(can_status_label, "CAN frame is outside the valid range"); + } +} + +static void board_demo_mqtt_start_event(lv_event_t *event) +{ + char status[64]; + int result; + + RT_UNUSED(event); + result = board_demo_request_mqtt_start(); + if (result == RT_EOK) + { + lv_label_set_text(mqtt_status_label, "MQTT worker started"); + LOG_I("MQTT start command accepted from the UI"); + } + else if (result == -RT_EBUSY) + { + lv_label_set_text(mqtt_status_label, "MQTT is already running"); + } + else + { + rt_snprintf(status, sizeof(status), "MQTT start request failed: %d", result); + lv_label_set_text(mqtt_status_label, status); + LOG_E("cannot start MQTT: %d", result); + } +} + +static void board_demo_mqtt_stop_event(lv_event_t *event) +{ + char status[64]; + int result; + + RT_UNUSED(event); + result = board_demo_request_mqtt_stop(); + if (result == RT_EOK) + { + lv_label_set_text(mqtt_status_label, "MQTT stopping"); + LOG_I("MQTT stop command accepted from the UI"); + } + else + { + rt_snprintf(status, sizeof(status), "MQTT stop request failed: %d", result); + lv_label_set_text(mqtt_status_label, status); + LOG_E("cannot stop MQTT: %d", result); + } +} + +static void board_demo_mqtt_publish_event(lv_event_t *event) +{ + const char *topic; + const char *payload; + char status[64]; + int result; + + RT_UNUSED(event); + topic = lv_textarea_get_text(mqtt_topic_textarea); + payload = lv_textarea_get_text(mqtt_payload_textarea); + result = board_demo_request_mqtt_publish(topic, payload); + if (result == RT_EOK) + { + lv_label_set_text(mqtt_status_label, "MQTT publish requested"); + LOG_I("MQTT publish requested from the UI: %s", topic); + } + else if ((topic[0] == '\0') || (payload[0] == '\0')) + { + lv_label_set_text(mqtt_status_label, "Topic and payload are required"); + } + else + { + rt_snprintf(status, sizeof(status), "MQTT publish request failed: %d", result); + lv_label_set_text(mqtt_status_label, status); + LOG_E("cannot publish MQTT message: %d", result); + } +} + +static void board_demo_pwm_slider_event(lv_event_t *event) +{ + lv_obj_t *slider; + lv_obj_t *value_label; + char text[16]; + + slider = lv_event_get_target_obj(event); + value_label = (lv_obj_t *)lv_event_get_user_data(event); + rt_snprintf(text, sizeof(text), "%d%%", (int)lv_slider_get_value(slider)); + lv_label_set_text(value_label, text); +} + +static void board_demo_brightness_event(lv_event_t *event) +{ + lv_obj_t *slider; + rt_uint8_t brightness; + char text[16]; + + slider = lv_event_get_target_obj(event); + brightness = (rt_uint8_t)lv_slider_get_value(slider); + rt_snprintf(text, sizeof(text), "%u%%", brightness); + lv_label_set_text(brightness_value_label, text); + + if (board_demo_lcd_device == RT_NULL) + { + board_demo_lcd_device = rt_device_find(BOARD_DEMO_LCD_DEVICE_NAME); + } + if ((board_demo_lcd_device == RT_NULL) || + (rt_device_control(board_demo_lcd_device, + RTGRAPHIC_CTRL_SET_BRIGHTNESS, + &brightness) != RT_EOK)) + { + LOG_E("cannot set display brightness to %u%%", brightness); + } +} + +static void board_demo_pwm_apply_event(lv_event_t *event) +{ + static const rt_uint32_t frequencies[] = {1000U, 10000U, 20000U}; + uint32_t selected; + + RT_UNUSED(event); + selected = lv_dropdown_get_selected(pwm_frequency_dropdown); + if (selected >= sizeof(frequencies) / sizeof(frequencies[0])) + { + return; + } + + board_demo_request_pwm(frequencies[selected], + lv_obj_has_state(pwm2_switch, LV_STATE_CHECKED), + (rt_uint8_t)lv_slider_get_value(pwm2_slider), + lv_obj_has_state(pwm30_switch, LV_STATE_CHECKED), + (rt_uint8_t)lv_slider_get_value(pwm30_slider)); +} + +static void board_demo_camera_open_event(lv_event_t *event) +{ + RT_UNUSED(event); +#ifdef BSP_USING_OV7670_PREVIEW + if (ov7670_preview_request_start() != RT_EOK) + { + lv_label_set_text(camera_status_label, "Camera preview is not ready"); + } +#endif +} + +static void board_demo_camera_stop_event(lv_event_t *event) +{ + RT_UNUSED(event); +#ifdef BSP_USING_OV7670_PREVIEW + ov7670_preview_request_stop(); +#endif +} + +static void board_demo_bus_request_feedback(lv_obj_t *status_label, + lv_obj_t *log_label, int result) +{ + const char *reason; + + if (result == RT_EOK) + { + if (board_demo_backend_get_snapshot(&demo_snapshot) == RT_EOK) + { + board_demo_update_runtime(); + } + return; + } + + reason = result == -RT_ENOSYS ? "Feature disabled" : + (result == -RT_EBUSY ? "Test already running" : "Request failed"); + lv_label_set_text_fmt(status_label, "%s: %d", reason, result); + lv_obj_set_style_text_color(status_label, COLOR_DANGER, 0); + lv_label_set_text_fmt(log_label, "FAIL %s: %d", reason, result); + LOG_E("bus test request: %s (%d)", reason, result); +} + +static void board_demo_spi_run_event(lv_event_t *event) +{ + static const rt_uint32_t frequencies[] = {1000000U, 5000000U, 10000000U}; + static const rt_uint16_t lengths[] = {32U, 64U, 128U, 256U}; + uint32_t frequency_index; + uint32_t length_index; + int result; + + RT_UNUSED(event); + frequency_index = lv_dropdown_get_selected(spi_frequency_dropdown); + length_index = lv_dropdown_get_selected(spi_length_dropdown); + if ((frequency_index < sizeof(frequencies) / sizeof(frequencies[0])) && + (length_index < sizeof(lengths) / sizeof(lengths[0]))) + { + result = board_demo_request_spi_loopback(frequencies[frequency_index], + lengths[length_index]); + board_demo_bus_request_feedback(spi_status_label, spi_log_label, result); + } +} + +static void board_demo_i2c_scan_event(lv_event_t *event) +{ + int result; + + RT_UNUSED(event); + result = board_demo_request_i2c_scan(BOARD_DEMO_I2C_BUS_NAME); + board_demo_bus_request_feedback(i2c_status_label, i2c_log_label, result); +} + +static void board_demo_create_sidebar(void) +{ + lv_obj_t *sidebar; + lv_obj_t *brand; + lv_obj_t *button; + lv_obj_t *label; + enum board_demo_page_id index; + + sidebar = lv_obj_create(demo_screen); + lv_obj_set_pos(sidebar, 0, 0); + lv_obj_set_size(sidebar, BOARD_DEMO_SIDEBAR_WIDTH, BOARD_DEMO_HEIGHT); + lv_obj_set_style_bg_color(sidebar, COLOR_NAV, 0); + lv_obj_set_style_bg_opa(sidebar, LV_OPA_COVER, 0); + lv_obj_set_style_border_width(sidebar, 0, 0); + lv_obj_set_style_radius(sidebar, 0, 0); + lv_obj_set_style_pad_all(sidebar, 0, 0); + lv_obj_set_scrollable(sidebar, false); + + brand = lv_label_create(sidebar); + lv_label_set_text(brand, "Gino"); + lv_obj_set_style_text_color(brand, lv_color_white(), 0); + lv_obj_set_style_text_font(brand, &lv_font_montserrat_16, 0); + lv_obj_align(brand, LV_ALIGN_TOP_MID, 0, 24); + + for (index = BOARD_DEMO_PAGE_OVERVIEW; + index < BOARD_DEMO_PAGE_COUNT; index++) + { + button = lv_button_create(sidebar); + lv_obj_set_pos(button, 8, 82 + (lv_coord_t)index * 70); + lv_obj_set_size(button, BOARD_DEMO_SIDEBAR_WIDTH - 16, 62); + lv_obj_set_style_radius(button, 6, 0); + lv_obj_set_style_pad_all(button, 0, 0); + lv_obj_set_style_bg_color(button, COLOR_NAV, 0); + lv_obj_set_style_shadow_width(button, 0, 0); + lv_obj_add_event_cb(button, board_demo_nav_event, LV_EVENT_CLICKED, + (void *)(rt_ubase_t)index); + demo_nav_buttons[index] = button; + + label = lv_label_create(button); + lv_label_set_text(label, board_demo_page_symbols[index]); + lv_obj_set_style_text_color(label, lv_color_white(), 0); + lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 9); + label = lv_label_create(button); + lv_label_set_text(label, board_demo_page_names[index]); + lv_obj_set_style_text_color(label, lv_color_white(), 0); + lv_obj_align(label, LV_ALIGN_BOTTOM_MID, 0, -9); + } + + label = lv_label_create(sidebar); + lv_label_set_text(label, "RT-Thread"); + lv_obj_set_style_text_color(label, lv_color_hex(0xAAB6BF), 0); + lv_obj_align(label, LV_ALIGN_BOTTOM_MID, 0, -18); +} + +static void board_demo_create_header(void) +{ + lv_obj_t *header; + + header = lv_obj_create(demo_screen); + lv_obj_set_pos(header, BOARD_DEMO_SIDEBAR_WIDTH, 0); + lv_obj_set_size(header, BOARD_DEMO_CONTENT_WIDTH, BOARD_DEMO_HEADER_HEIGHT); + lv_obj_set_style_bg_color(header, COLOR_SURFACE, 0); + lv_obj_set_style_bg_opa(header, LV_OPA_COVER, 0); + lv_obj_set_style_border_width(header, 1, 0); + lv_obj_set_style_border_color(header, COLOR_BORDER, 0); + lv_obj_set_style_border_side(header, LV_BORDER_SIDE_BOTTOM, 0); + lv_obj_set_style_radius(header, 0, 0); + lv_obj_set_style_pad_all(header, 0, 0); + lv_obj_set_scrollable(header, false); + + demo_header_title = board_demo_create_label(header, "GD32H77D", 16, 23, 180); + lv_obj_set_style_text_font(demo_header_title, &lv_font_montserrat_16, 0); + + demo_header_status = board_demo_create_label(header, "Starting services", + 200, 12, BOARD_DEMO_CONTENT_WIDTH - 216); + lv_obj_set_style_text_align(demo_header_status, LV_TEXT_ALIGN_RIGHT, 0); + lv_obj_set_style_text_color(demo_header_status, COLOR_MUTED, 0); +} + +static void board_demo_build_overview_page(void) +{ + lv_obj_t *page; + lv_obj_t *button; + lv_obj_t *label; + enum board_demo_page_id index; + + page = board_demo_create_page(BOARD_DEMO_PAGE_OVERVIEW); + demo_pages[BOARD_DEMO_PAGE_OVERVIEW] = page; + + for (index = BOARD_DEMO_PAGE_FILES; index < BOARD_DEMO_PAGE_COUNT; index++) + { + button = lv_button_create(page); + lv_obj_set_pos(button, 0, 40 + (index - BOARD_DEMO_PAGE_FILES) * 76); + lv_obj_set_size(button, BOARD_DEMO_PAGE_INNER_WIDTH, 66); + lv_obj_set_style_radius(button, 6, 0); + lv_obj_set_style_pad_all(button, 0, 0); + lv_obj_set_style_shadow_width(button, 0, 0); + lv_obj_set_style_bg_color(button, COLOR_SURFACE, 0); + lv_obj_add_event_cb(button, board_demo_nav_event, LV_EVENT_CLICKED, + (void *)(rt_ubase_t)index); + label = board_demo_create_label(button, board_demo_page_symbols[index], + 14, 24, 26); + lv_obj_set_style_text_color(label, + (index == BOARD_DEMO_PAGE_CAN || index == BOARD_DEMO_PAGE_SPI || + index == BOARD_DEMO_PAGE_I2C) ? COLOR_INFO : COLOR_ACCENT, 0); + board_demo_create_line(button, board_demo_page_names[index], 52, 10, 200); + overview_status[index] = board_demo_create_line(button, "-", 52, 36, + BOARD_DEMO_PAGE_INNER_WIDTH - 98); + lv_obj_set_style_text_color(overview_status[index], COLOR_MUTED, 0); + label = board_demo_create_label(button, LV_SYMBOL_RIGHT, + BOARD_DEMO_PAGE_INNER_WIDTH - 30, 24, 18); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + } + + overview_uptime = board_demo_create_line(page, "Uptime 0 s", 0, 592, + BOARD_DEMO_PAGE_INNER_WIDTH); + lv_obj_set_style_text_color(overview_uptime, COLOR_MUTED, 0); +} + +static void board_demo_build_files_page(void) +{ + lv_obj_t *page; + lv_obj_t *preview_panel; + lv_obj_t *button; + lv_obj_t *label; + int source; + static const char *const source_names[BOARD_DEMO_STORAGE_COUNT] = + { + LV_SYMBOL_DRIVE " Flash", LV_SYMBOL_SD_CARD " SD card", LV_SYMBOL_USB " USB Host", + }; + + page = board_demo_create_page(BOARD_DEMO_PAGE_FILES); + demo_pages[BOARD_DEMO_PAGE_FILES] = page; + + for (source = 0; source < BOARD_DEMO_STORAGE_COUNT; source++) + { + button = lv_button_create(page); + lv_obj_set_pos(button, source * 188, 38); + lv_obj_set_size(button, 180, 64); + lv_obj_set_style_radius(button, 6, 0); + lv_obj_set_style_pad_all(button, 0, 0); + lv_obj_set_style_shadow_width(button, 0, 0); + lv_obj_set_style_border_width(button, 1, 0); + lv_obj_set_style_border_color(button, COLOR_BORDER, 0); + lv_obj_set_style_bg_color(button, COLOR_SURFACE, 0); + lv_obj_add_event_cb(button, board_demo_storage_source_event, + LV_EVENT_CLICKED, (void *)board_demo_storage_roots[source]); + files_source_buttons[source] = button; + label = board_demo_create_line(button, source_names[source], 12, 9, 156); + lv_obj_set_style_text_color(label, COLOR_TEXT, 0); + files_source_status[source] = board_demo_create_line(button, "Not mounted", + 12, 35, 156); + lv_obj_set_style_text_color(files_source_status[source], COLOR_MUTED, 0); + } + files_up_button = board_demo_create_button(page, LV_SYMBOL_UP, + 456, 114, 44, 44, + board_demo_files_up_event); + board_demo_create_button(page, LV_SYMBOL_REFRESH, 512, 114, 44, 44, + board_demo_files_refresh_event); + + files_path_label = board_demo_create_line(page, "/flash", 0, 124, 440); + files_status_label = board_demo_create_line(page, "Not scanned", 0, 168, + BOARD_DEMO_PAGE_INNER_WIDTH); + lv_obj_set_style_text_color(files_status_label, COLOR_MUTED, 0); + + files_table = lv_table_create(page); + lv_obj_set_pos(files_table, 0, 198); + lv_obj_set_size(files_table, BOARD_DEMO_PAGE_INNER_WIDTH, 228); + board_demo_style_table(files_table); + lv_table_set_column_count(files_table, 3U); + lv_table_set_row_count(files_table, 1U); + lv_table_set_column_width(files_table, 0U, 44); + lv_table_set_column_width(files_table, 1U, 398); + lv_table_set_column_width(files_table, 2U, 112); + lv_table_set_cell_value(files_table, 0U, 0U, ""); + lv_table_set_cell_value(files_table, 0U, 1U, "Name"); + lv_table_set_cell_value(files_table, 0U, 2U, "Size"); + lv_obj_add_event_cb(files_table, board_demo_files_table_event, + LV_EVENT_VALUE_CHANGED, RT_NULL); + + files_empty_label = board_demo_create_line(page, "No files", 16, 300, + BOARD_DEMO_PAGE_INNER_WIDTH - 32); + lv_obj_set_style_text_align(files_empty_label, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_style_text_color(files_empty_label, COLOR_MUTED, 0); + lv_obj_set_hidden(files_empty_label, true); + + files_preview_title = board_demo_create_line(page, "File preview", 0, 440, + BOARD_DEMO_PAGE_INNER_WIDTH); + preview_panel = lv_obj_create(page); + files_preview_panel = preview_panel; + lv_obj_set_pos(preview_panel, 0, 472); + lv_obj_set_size(preview_panel, BOARD_DEMO_PAGE_INNER_WIDTH, 144); + lv_obj_set_style_radius(preview_panel, 6, 0); + lv_obj_set_style_border_width(preview_panel, 1, 0); + lv_obj_set_style_border_color(preview_panel, COLOR_BORDER, 0); + lv_obj_set_style_bg_color(preview_panel, COLOR_SURFACE, 0); + lv_obj_set_style_pad_all(preview_panel, 10, 0); + lv_obj_set_scroll_dir(preview_panel, LV_DIR_VER); + + files_preview_label = board_demo_create_label(preview_panel, + "No file selected", + 0, 0, BOARD_DEMO_PAGE_INNER_WIDTH - 24); +} + +static void board_demo_build_can_page(void) +{ + lv_obj_t *page; + lv_obj_t *label; + + page = board_demo_create_page(BOARD_DEMO_PAGE_CAN); + demo_pages[BOARD_DEMO_PAGE_CAN] = page; + + can_status_label = board_demo_create_line(page, "CAN initializing", 0, 36, + BOARD_DEMO_PAGE_INNER_WIDTH); + lv_obj_set_style_text_color(can_status_label, COLOR_MUTED, 0); + + can_baud_dropdown = lv_dropdown_create(page); + lv_obj_set_pos(can_baud_dropdown, 0, 68); + lv_obj_set_size(can_baud_dropdown, 150, 42); + lv_dropdown_set_options(can_baud_dropdown, "125 kbit/s\n250 kbit/s\n500 kbit/s\n1 Mbit/s"); + lv_dropdown_set_selected(can_baud_dropdown, 3U); + board_demo_create_button(page, "Apply", 162, 68, 82, 42, + board_demo_can_baud_event); + + label = board_demo_create_label(page, "ID", 0, 122, 90); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + can_id_textarea = lv_textarea_create(page); + lv_obj_set_pos(can_id_textarea, 0, 142); + lv_obj_set_size(can_id_textarea, 120, 44); + lv_textarea_set_one_line(can_id_textarea, true); + lv_textarea_set_max_length(can_id_textarea, 8U); + lv_textarea_set_text(can_id_textarea, "321"); + lv_obj_add_event_cb(can_id_textarea, board_demo_textarea_event, + LV_EVENT_ALL, RT_NULL); + + label = board_demo_create_label(page, "Data", 134, 122, 240); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + can_data_textarea = lv_textarea_create(page); + lv_obj_set_pos(can_data_textarea, 134, 142); + lv_obj_set_size(can_data_textarea, 270, 44); + lv_textarea_set_one_line(can_data_textarea, true); + lv_textarea_set_max_length(can_data_textarea, 32U); + lv_textarea_set_text(can_data_textarea, "01 02 03 04"); + lv_obj_add_event_cb(can_data_textarea, board_demo_textarea_event, + LV_EVENT_ALL, RT_NULL); + + can_extended_switch = lv_switch_create(page); + lv_obj_set_pos(can_extended_switch, 424, 147); + lv_obj_set_size(can_extended_switch, 48, 26); + label = board_demo_create_label(page, "EXT", 484, 150, 60); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + board_demo_create_button(page, LV_SYMBOL_UPLOAD " Send", 444, 68, 112, 42, + board_demo_can_send_event); + + can_table = lv_table_create(page); + lv_obj_set_pos(can_table, 0, 204); + lv_obj_set_size(can_table, BOARD_DEMO_PAGE_INNER_WIDTH, 412); + board_demo_style_table(can_table); + lv_table_set_column_count(can_table, 4U); + lv_table_set_row_count(can_table, 1U); + lv_table_set_column_width(can_table, 0U, 52); + lv_table_set_column_width(can_table, 1U, 110); + lv_table_set_column_width(can_table, 2U, 52); + lv_table_set_column_width(can_table, 3U, 340); + lv_table_set_cell_value(can_table, 0U, 0U, "Dir"); + lv_table_set_cell_value(can_table, 0U, 1U, "ID"); + lv_table_set_cell_value(can_table, 0U, 2U, "Len"); + lv_table_set_cell_value(can_table, 0U, 3U, "Data"); +} + +static void board_demo_build_mqtt_page(void) +{ + lv_obj_t *page; + lv_obj_t *label; + lv_obj_t *rx_panel; + + page = board_demo_create_page(BOARD_DEMO_PAGE_MQTT); + demo_pages[BOARD_DEMO_PAGE_MQTT] = page; + + mqtt_status_label = board_demo_create_line(page, "MQTT stopped", 0, 36, + BOARD_DEMO_PAGE_INNER_WIDTH); + lv_obj_set_style_text_color(mqtt_status_label, COLOR_MUTED, 0); + + label = board_demo_create_line(page, "Broker: " BSP_GD32VW553_MQTT_HOST, + 0, 76, 326); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + label = board_demo_create_line(page, "Subscribe: " BSP_GD32VW553_MQTT_SUB_TOPIC, + 0, 116, BOARD_DEMO_PAGE_INNER_WIDTH); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + board_demo_create_button(page, LV_SYMBOL_PLAY " Start", 344, 68, 100, 40, + board_demo_mqtt_start_event); + board_demo_create_button(page, LV_SYMBOL_STOP " Stop", 456, 68, 100, 40, + board_demo_mqtt_stop_event); + + label = board_demo_create_label(page, "Publish topic", 0, 158, 200); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + mqtt_topic_textarea = lv_textarea_create(page); + lv_obj_set_pos(mqtt_topic_textarea, 0, 184); + lv_obj_set_size(mqtt_topic_textarea, BOARD_DEMO_PAGE_INNER_WIDTH, 44); + lv_textarea_set_one_line(mqtt_topic_textarea, true); + lv_textarea_set_max_length(mqtt_topic_textarea, BOARD_DEMO_MQTT_TOPIC_MAX - 1U); + lv_textarea_set_text(mqtt_topic_textarea, BSP_GD32VW553_MQTT_PUB_TOPIC); + lv_obj_add_event_cb(mqtt_topic_textarea, board_demo_textarea_event, + LV_EVENT_ALL, RT_NULL); + + label = board_demo_create_label(page, "Payload", 0, 244, 200); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + mqtt_payload_textarea = lv_textarea_create(page); + lv_obj_set_pos(mqtt_payload_textarea, 0, 270); + lv_obj_set_size(mqtt_payload_textarea, BOARD_DEMO_PAGE_INNER_WIDTH, 88); + lv_textarea_set_max_length(mqtt_payload_textarea, + BOARD_DEMO_MQTT_PAYLOAD_MAX - 1U); + lv_textarea_set_text(mqtt_payload_textarea, "hello from GD32H77D"); + lv_obj_add_event_cb(mqtt_payload_textarea, board_demo_textarea_event, + LV_EVENT_ALL, RT_NULL); + board_demo_create_button(page, LV_SYMBOL_UPLOAD " Publish", 418, 372, 138, 44, + board_demo_mqtt_publish_event); + + label = board_demo_create_label(page, "Last received topic", 0, 436, 240); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + mqtt_rx_topic_label = board_demo_create_line(page, "-", 0, 464, + BOARD_DEMO_PAGE_INNER_WIDTH); + label = board_demo_create_label(page, "Last received payload", 0, 500, 240); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + rx_panel = lv_obj_create(page); + lv_obj_set_pos(rx_panel, 0, 528); + lv_obj_set_size(rx_panel, BOARD_DEMO_PAGE_INNER_WIDTH, 88); + lv_obj_set_style_radius(rx_panel, 6, 0); + lv_obj_set_style_pad_all(rx_panel, 10, 0); + lv_obj_set_style_border_width(rx_panel, 1, 0); + lv_obj_set_style_border_color(rx_panel, COLOR_BORDER, 0); + lv_obj_set_scroll_dir(rx_panel, LV_DIR_VER); + mqtt_rx_payload_label = board_demo_create_label(rx_panel, "-", 0, 0, + BOARD_DEMO_PAGE_INNER_WIDTH - 24); +} + +static void board_demo_build_pwm_page(void) +{ + lv_obj_t *page; + lv_obj_t *label; + rt_uint8_t brightness = 100U; + char brightness_text[16]; + + page = board_demo_create_page(BOARD_DEMO_PAGE_PWM); + demo_pages[BOARD_DEMO_PAGE_PWM] = page; + + pwm_status_label = board_demo_create_line(page, "PWM idle", 0, 36, + BOARD_DEMO_PAGE_INNER_WIDTH); + lv_obj_set_style_text_color(pwm_status_label, COLOR_MUTED, 0); + + label = board_demo_create_label(page, "Display brightness", 0, 78, 200); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + board_demo_lcd_device = rt_device_find(BOARD_DEMO_LCD_DEVICE_NAME); + if (board_demo_lcd_device != RT_NULL) + { + (void)rt_device_control(board_demo_lcd_device, + RTGRAPHIC_CTRL_GET_BRIGHTNESS, + &brightness); + } + rt_snprintf(brightness_text, sizeof(brightness_text), "%u%%", brightness); + brightness_value_label = board_demo_create_label(page, brightness_text, + 470, 78, 60); + brightness_slider = lv_slider_create(page); + lv_obj_set_pos(brightness_slider, 0, 112); + lv_obj_set_size(brightness_slider, 450, 18); + lv_slider_set_range(brightness_slider, 0, 100); + lv_slider_set_value(brightness_slider, brightness, LV_ANIM_OFF); + lv_obj_add_event_cb(brightness_slider, board_demo_brightness_event, + LV_EVENT_VALUE_CHANGED, RT_NULL); + + label = board_demo_create_label(page, "Output frequency", 0, 154, 150); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + pwm_frequency_dropdown = lv_dropdown_create(page); + lv_obj_set_pos(pwm_frequency_dropdown, 0, 178); + lv_obj_set_size(pwm_frequency_dropdown, 170, 44); + lv_dropdown_set_options(pwm_frequency_dropdown, "1 kHz\n10 kHz\n20 kHz"); + + label = board_demo_create_label(page, "PWM2 channel 2", 0, 250, 180); + pwm2_switch = lv_switch_create(page); + lv_obj_set_pos(pwm2_switch, 200, 244); + lv_obj_set_size(pwm2_switch, 48, 26); + pwm2_slider = lv_slider_create(page); + lv_obj_set_pos(pwm2_slider, 0, 288); + lv_obj_set_size(pwm2_slider, 450, 18); + lv_slider_set_range(pwm2_slider, 0, 100); + lv_slider_set_value(pwm2_slider, 50, LV_ANIM_OFF); + pwm2_value_label = board_demo_create_label(page, "50%", 470, 279, 60); + lv_obj_add_event_cb(pwm2_slider, board_demo_pwm_slider_event, + LV_EVENT_VALUE_CHANGED, pwm2_value_label); + + label = board_demo_create_label(page, "PWM30 channel 3", 0, 346, 180); + pwm30_switch = lv_switch_create(page); + lv_obj_set_pos(pwm30_switch, 200, 340); + lv_obj_set_size(pwm30_switch, 48, 26); + pwm30_slider = lv_slider_create(page); + lv_obj_set_pos(pwm30_slider, 0, 384); + lv_obj_set_size(pwm30_slider, 450, 18); + lv_slider_set_range(pwm30_slider, 0, 100); + lv_slider_set_value(pwm30_slider, 50, LV_ANIM_OFF); + pwm30_value_label = board_demo_create_label(page, "50%", 470, 375, 60); + lv_obj_add_event_cb(pwm30_slider, board_demo_pwm_slider_event, + LV_EVENT_VALUE_CHANGED, pwm30_value_label); + + board_demo_create_button(page, LV_SYMBOL_OK " Apply outputs", 0, 440, 160, 46, + board_demo_pwm_apply_event); +} + +static void board_demo_build_camera_page(void) +{ + lv_obj_t *page; + lv_obj_t *preview_panel; + lv_obj_t *icon; + + page = board_demo_create_page(BOARD_DEMO_PAGE_CAMERA); + demo_pages[BOARD_DEMO_PAGE_CAMERA] = page; + + camera_status_label = board_demo_create_line(page, "Camera idle", 0, 36, + BOARD_DEMO_PAGE_INNER_WIDTH); + lv_obj_set_style_text_color(camera_status_label, COLOR_MUTED, 0); + + preview_panel = lv_obj_create(page); + lv_obj_set_pos(preview_panel, 0, 76); + lv_obj_set_size(preview_panel, BOARD_DEMO_PAGE_INNER_WIDTH, 390); + lv_obj_set_style_bg_color(preview_panel, lv_color_black(), 0); + lv_obj_set_style_border_width(preview_panel, 0, 0); + lv_obj_set_style_radius(preview_panel, 4, 0); + lv_obj_set_scrollable(preview_panel, false); + icon = lv_label_create(preview_panel); + lv_label_set_text(icon, LV_SYMBOL_IMAGE); + lv_obj_set_style_text_color(icon, lv_color_hex(0xAAB6BF), 0); + lv_obj_set_style_text_font(icon, &lv_font_montserrat_16, 0); + lv_obj_center(icon); + + camera_counter_label = board_demo_create_label( + page, "Captured 0 | Shown 0 | Dropped 0 | Errors 0", + 0, 486, BOARD_DEMO_PAGE_INNER_WIDTH); + lv_obj_set_style_text_color(camera_counter_label, COLOR_MUTED, 0); + board_demo_create_button(page, LV_SYMBOL_PLAY " Open preview", 0, 530, 150, 46, + board_demo_camera_open_event); + board_demo_create_button(page, LV_SYMBOL_STOP " Stop", 162, 530, 100, 46, + board_demo_camera_stop_event); +} + +static lv_obj_t *board_demo_create_bus_log(lv_obj_t *page, const char *text, + lv_coord_t y, lv_coord_t height) +{ + lv_obj_t *panel; + + panel = lv_obj_create(page); + lv_obj_set_pos(panel, 0, y); + lv_obj_set_size(panel, BOARD_DEMO_PAGE_INNER_WIDTH, height); + lv_obj_set_style_bg_color(panel, COLOR_SURFACE, 0); + lv_obj_set_style_border_color(panel, COLOR_BORDER, 0); + lv_obj_set_style_radius(panel, 6, 0); + lv_obj_set_style_pad_all(panel, 12, 0); + lv_obj_set_scroll_dir(panel, LV_DIR_VER); + return board_demo_create_label(panel, text, 0, 0, + BOARD_DEMO_PAGE_INNER_WIDTH - 28); +} + +static void board_demo_build_spi_page(void) +{ + lv_obj_t *page; + lv_obj_t *label; + + page = board_demo_create_page(BOARD_DEMO_PAGE_SPI); + demo_pages[BOARD_DEMO_PAGE_SPI] = page; + + spi_status_label = board_demo_create_line(page, "SPI loopback idle", 0, 36, + BOARD_DEMO_PAGE_INNER_WIDTH); + lv_obj_set_style_text_color(spi_status_label, COLOR_MUTED, 0); + + label = board_demo_create_label(page, "Clock", 0, 90, 150); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + spi_frequency_dropdown = lv_dropdown_create(page); + lv_obj_set_pos(spi_frequency_dropdown, 0, 116); + lv_obj_set_size(spi_frequency_dropdown, 170, 44); + lv_dropdown_set_options(spi_frequency_dropdown, "1 MHz\n5 MHz\n10 MHz"); + + label = board_demo_create_label(page, "Transfer length", 200, 90, 150); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + spi_length_dropdown = lv_dropdown_create(page); + lv_obj_set_pos(spi_length_dropdown, 200, 116); + lv_obj_set_size(spi_length_dropdown, 170, 44); + lv_dropdown_set_options(spi_length_dropdown, "32 bytes\n64 bytes\n128 bytes\n256 bytes"); + + spi_run_button = board_demo_create_button(page, LV_SYMBOL_PLAY " Run loopback", + 0, 182, 160, 46, board_demo_spi_run_event); + label = board_demo_create_line(page, "SPI3 | SCK PE12 | MOSI PF1 - MISO PF0", + 0, 244, BOARD_DEMO_PAGE_INNER_WIDTH); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + label = board_demo_create_line(page, "Loopback log", 0, 278, + BOARD_DEMO_PAGE_INNER_WIDTH); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + spi_log_label = board_demo_create_bus_log(page, "No loopback results", 308, 304); +} + +static void board_demo_build_i2c_page(void) +{ + lv_obj_t *page; + lv_obj_t *label; + rt_uint16_t row; + rt_uint16_t column; + + page = board_demo_create_page(BOARD_DEMO_PAGE_I2C); + demo_pages[BOARD_DEMO_PAGE_I2C] = page; + + i2c_status_label = board_demo_create_line(page, "I2C scan idle", 0, 36, + BOARD_DEMO_PAGE_INNER_WIDTH); + lv_obj_set_style_text_color(i2c_status_label, COLOR_MUTED, 0); + + label = board_demo_create_label(page, "External bus", 0, 86, 170); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + board_demo_create_line(page, BOARD_DEMO_I2C_BUS_NAME, 0, 122, 170); + i2c_scan_button = board_demo_create_button(page, LV_SYMBOL_REFRESH " Scan", + 190, 112, 112, 44, board_demo_i2c_scan_event); + + label = board_demo_create_line(page, "Address map", 0, 180, 240); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + i2c_result_table = lv_table_create(page); + lv_obj_set_pos(i2c_result_table, 0, 210); + lv_obj_set_size(i2c_result_table, BOARD_DEMO_PAGE_INNER_WIDTH, 278); + board_demo_style_table(i2c_result_table); + lv_obj_set_style_pad_hor(i2c_result_table, 3, LV_PART_ITEMS); + lv_obj_set_style_pad_ver(i2c_result_table, 4, LV_PART_ITEMS); + lv_obj_set_style_text_align(i2c_result_table, LV_TEXT_ALIGN_CENTER, LV_PART_ITEMS); + lv_table_set_row_count(i2c_result_table, 9U); + lv_table_set_column_count(i2c_result_table, 17U); + lv_table_set_column_width(i2c_result_table, 0U, 42); + lv_table_set_cell_value(i2c_result_table, 0U, 0U, ""); + for (column = 1U; column <= 16U; column++) + { + lv_table_set_column_width(i2c_result_table, column, 32); + lv_table_set_cell_value_fmt(i2c_result_table, 0U, column, "%02X", column - 1U); + } + for (row = 1U; row <= 8U; row++) + { + lv_table_set_cell_value_fmt(i2c_result_table, row, 0U, "%02X:", (row - 1U) * 16U); + for (column = 1U; column <= 16U; column++) + { + lv_table_set_cell_value(i2c_result_table, row, column, ""); + } + } + label = board_demo_create_line(page, "Scan log", 0, 504, + BOARD_DEMO_PAGE_INNER_WIDTH); + lv_obj_set_style_text_color(label, COLOR_MUTED, 0); + i2c_log_label = board_demo_create_bus_log(page, "No scan activity", 534, 78); +} + +static void board_demo_update_storage(void) +{ + rt_uint8_t mounts; + rt_uint8_t changed; + int source; + + if (storage_polled && + (lv_tick_elaps(storage_poll_tick) < BOARD_DEMO_STORAGE_POLL_MS)) + { + return; + } + storage_poll_tick = lv_tick_get(); + mounts = board_demo_storage_mounts(); + changed = storage_polled ? (mounts ^ storage_mounts) : 0U; + storage_mounts = mounts; + storage_polled = RT_TRUE; + for (source = 0; source < BOARD_DEMO_STORAGE_COUNT; source++) + { + board_demo_set_label_text(files_source_status[source], + (mounts & (1U << source)) ? "Mounted" : "Not mounted"); + lv_obj_set_style_text_color(files_source_status[source], + (mounts & (1U << source)) ? COLOR_ACCENT : COLOR_MUTED, 0); + } + + source = board_demo_fs_source(demo_snapshot.fs_path); + if ((source >= 0) && (changed & (1U << source))) + { + /* A replacement drive may not contain the previous subdirectory. */ + board_demo_files_request_path(board_demo_storage_roots[source]); + } +} + +static void board_demo_update_files(void) +{ + char size_text[24]; + rt_uint8_t index; + int source; + + if (files_sequence != demo_snapshot.fs_sequence) + { + files_sequence = demo_snapshot.fs_sequence; + lv_label_set_text(files_path_label, demo_snapshot.fs_path); + lv_label_set_text(files_status_label, demo_snapshot.fs_status); + source = board_demo_fs_source(demo_snapshot.fs_path); + for (index = 0U; index < BOARD_DEMO_STORAGE_COUNT; index++) + { + lv_obj_set_style_bg_color(files_source_buttons[index], + source == index ? COLOR_ACCENT_SOFT : COLOR_SURFACE, 0); + lv_obj_set_style_border_color(files_source_buttons[index], + source == index ? COLOR_ACCENT : COLOR_BORDER, 0); + } + if ((source >= 0) && + (rt_strcmp(demo_snapshot.fs_path, board_demo_storage_roots[source]) != 0)) + { + lv_obj_remove_state(files_up_button, LV_STATE_DISABLED); + } + else + { + lv_obj_add_state(files_up_button, LV_STATE_DISABLED); + } + lv_obj_set_hidden(files_empty_label, demo_snapshot.fs_entry_count != 0U); + lv_label_set_text(files_empty_label, + (source >= 0 && (storage_mounts & (1U << source))) ? + demo_snapshot.fs_status : "Storage unavailable"); + lv_table_set_row_count(files_table, + (uint32_t)demo_snapshot.fs_entry_count + 1U); + for (index = 0U; index < demo_snapshot.fs_entry_count; index++) + { + lv_table_set_cell_value(files_table, index + 1U, 0U, + demo_snapshot.fs_entries[index].is_directory ? + LV_SYMBOL_DIRECTORY : LV_SYMBOL_FILE); + lv_table_set_cell_value(files_table, index + 1U, 1U, + demo_snapshot.fs_entries[index].name); + if (demo_snapshot.fs_entries[index].is_directory) + { + size_text[0] = '\0'; + } + else if (demo_snapshot.fs_entries[index].size >= 1024U * 1024U) + { + rt_snprintf(size_text, sizeof(size_text), "%u MB", + demo_snapshot.fs_entries[index].size / (1024U * 1024U)); + } + else if (demo_snapshot.fs_entries[index].size >= 1024U) + { + rt_snprintf(size_text, sizeof(size_text), "%u KB", + demo_snapshot.fs_entries[index].size / 1024U); + } + else + { + rt_snprintf(size_text, sizeof(size_text), "%u B", + demo_snapshot.fs_entries[index].size); + } + lv_table_set_cell_value(files_table, index + 1U, 2U, size_text); + } + lv_obj_scroll_to_y(files_table, 0, LV_ANIM_OFF); + lv_obj_invalidate(files_table); + LOG_I("storage table updated: %s, %u entries (%s)", + demo_snapshot.fs_path, demo_snapshot.fs_entry_count, + demo_snapshot.fs_status); + } + + if (file_sequence != demo_snapshot.file_sequence) + { + file_sequence = demo_snapshot.file_sequence; + lv_label_set_text(files_preview_title, demo_snapshot.file_name[0] == '\0' ? + "File preview" : demo_snapshot.file_name); + lv_label_set_text(files_preview_label, demo_snapshot.file_preview[0] == '\0' ? + "No file selected" : demo_snapshot.file_preview); + lv_obj_scroll_to_y(files_preview_panel, 0, LV_ANIM_OFF); + } +} + +static void board_demo_update_can(void) +{ + char id_text[16]; + char length_text[8]; + char data_text[32]; + rt_size_t used; + rt_uint8_t frame_index; + rt_uint8_t data_index; + + if (can_sequence == demo_snapshot.can_sequence) + { + return; + } + can_sequence = demo_snapshot.can_sequence; + lv_label_set_text(can_status_label, demo_snapshot.can_status); + lv_table_set_row_count(can_table, (uint32_t)demo_snapshot.can_frame_count + 1U); + + for (frame_index = 0U; frame_index < demo_snapshot.can_frame_count; frame_index++) + { + rt_snprintf(id_text, sizeof(id_text), + demo_snapshot.can_frames[frame_index].extended ? + "%08X" : "%03X", + demo_snapshot.can_frames[frame_index].id); + rt_snprintf(length_text, sizeof(length_text), "%u", + demo_snapshot.can_frames[frame_index].length); + used = 0U; + data_text[0] = '\0'; + for (data_index = 0U; + data_index < demo_snapshot.can_frames[frame_index].length; + data_index++) + { + used += (rt_size_t)rt_snprintf(data_text + used, + sizeof(data_text) - used, + "%02X%s", + demo_snapshot.can_frames[frame_index].data[data_index], + data_index + 1U == + demo_snapshot.can_frames[frame_index].length ? + "" : " "); + if (used >= sizeof(data_text)) + { + break; + } + } + lv_table_set_cell_value(can_table, frame_index + 1U, 0U, + demo_snapshot.can_frames[frame_index].transmitted ? + "TX" : "RX"); + lv_table_set_cell_value(can_table, frame_index + 1U, 1U, id_text); + lv_table_set_cell_value(can_table, frame_index + 1U, 2U, length_text); + lv_table_set_cell_value(can_table, frame_index + 1U, 3U, data_text); + } +} + +static void board_demo_update_bus_log(lv_obj_t *label, const char *text) +{ + lv_obj_t *panel = lv_obj_get_parent(label); + rt_bool_t follow = lv_obj_get_scroll_bottom(panel) <= 0; + + board_demo_set_label_text(label, text); + if (follow) + { + lv_obj_update_layout(panel); + lv_obj_scroll_to_y(panel, lv_obj_get_scroll_y(panel) + + lv_obj_get_scroll_bottom(panel), LV_ANIM_OFF); + } +} + +static void board_demo_update_i2c_table(void) +{ + rt_uint16_t address; + rt_uint16_t row; + rt_uint16_t column; + char text[4]; + const char *value; + + for (address = 0U; address < BOARD_DEMO_I2C_ADDRESS_COUNT; address++) + { + switch (demo_snapshot.i2c_addresses[address]) + { + case BOARD_DEMO_I2C_ACK: + rt_snprintf(text, sizeof(text), "%02X", address); + value = text; + break; + case BOARD_DEMO_I2C_NACK: + value = "--"; + break; + case BOARD_DEMO_I2C_PENDING: + value = ".."; + break; + case BOARD_DEMO_I2C_ERROR: + value = "!!"; + break; + default: + value = ""; + break; + } + row = address / 16U + 1U; + column = address % 16U + 1U; + if (rt_strcmp(lv_table_get_cell_value(i2c_result_table, row, column), value) != 0) + { + lv_table_set_cell_value(i2c_result_table, row, column, value); + } + } +} + +static void board_demo_update_runtime(void) +{ + char text[96]; +#ifdef BSP_USING_OV7670_PREVIEW + struct ov7670_preview_status camera_status; +#endif + + if (mqtt_sequence != demo_snapshot.mqtt_sequence) + { + mqtt_sequence = demo_snapshot.mqtt_sequence; + lv_label_set_text(mqtt_status_label, demo_snapshot.mqtt_status); + lv_label_set_text(mqtt_rx_topic_label, + demo_snapshot.mqtt_rx_topic[0] == '\0' ? + "-" : demo_snapshot.mqtt_rx_topic); + lv_label_set_text(mqtt_rx_payload_label, + demo_snapshot.mqtt_rx_payload[0] == '\0' ? + "-" : demo_snapshot.mqtt_rx_payload); + } + if (pwm_sequence != demo_snapshot.pwm_sequence) + { + pwm_sequence = demo_snapshot.pwm_sequence; + lv_label_set_text(pwm_status_label, demo_snapshot.pwm_status); + } + if (spi_sequence != demo_snapshot.spi_sequence) + { + spi_sequence = demo_snapshot.spi_sequence; + lv_label_set_text(spi_status_label, demo_snapshot.spi_status); + board_demo_update_bus_log(spi_log_label, demo_snapshot.spi_log); + lv_obj_set_style_text_color(spi_status_label, + demo_snapshot.spi_busy ? COLOR_INFO : + (demo_snapshot.spi_passed ? COLOR_ACCENT : COLOR_DANGER), 0); + lv_obj_set_state(spi_run_button, LV_STATE_DISABLED, demo_snapshot.spi_busy); + lv_obj_set_state(spi_frequency_dropdown, LV_STATE_DISABLED, demo_snapshot.spi_busy); + lv_obj_set_state(spi_length_dropdown, LV_STATE_DISABLED, demo_snapshot.spi_busy); + } + if (i2c_sequence != demo_snapshot.i2c_sequence) + { + i2c_sequence = demo_snapshot.i2c_sequence; + lv_label_set_text(i2c_status_label, demo_snapshot.i2c_status); + board_demo_update_i2c_table(); + board_demo_update_bus_log(i2c_log_label, demo_snapshot.i2c_log); + lv_obj_set_style_text_color(i2c_status_label, + demo_snapshot.i2c_busy ? COLOR_INFO : + (demo_snapshot.i2c_result_code != RT_EOK ? COLOR_DANGER : + (demo_snapshot.i2c_device_count ? COLOR_ACCENT : COLOR_WARNING)), 0); + lv_obj_set_state(i2c_scan_button, LV_STATE_DISABLED, demo_snapshot.i2c_busy); + } + +#ifdef BSP_USING_OV7670_PREVIEW + ov7670_preview_get_status(&camera_status); + if (camera_status.active) + { + board_demo_set_label_text(camera_status_label, "Camera preview active"); + } + else if (!camera_status.ready) + { + board_demo_set_label_text(camera_status_label, "Camera preview unavailable"); + } + else if (camera_status.last_result != RT_EOK) + { + rt_snprintf(text, sizeof(text), "Camera idle, last result %d", + camera_status.last_result); + board_demo_set_label_text(camera_status_label, text); + } + else + { + board_demo_set_label_text(camera_status_label, "Camera ready"); + } + rt_snprintf(text, sizeof(text), + "Captured %u | Shown %u\nDropped %u | Errors %u", + camera_status.frame_count, camera_status.display_count, + camera_status.drop_count, camera_status.error_count); + board_demo_set_label_text(camera_counter_label, text); +#else + board_demo_set_label_text(camera_status_label, "Camera disabled"); +#endif +} + +static void board_demo_update_overview(void) +{ + char camera_text[96]; + char storage_text[96]; + char header_text[96]; + char uptime_text[64]; +#ifdef BSP_USING_OV7670_PREVIEW + struct ov7670_preview_status camera_status; + + ov7670_preview_get_status(&camera_status); + rt_snprintf(camera_text, sizeof(camera_text), + "%s | Captured %u | Shown %u | Drop %u | Err %u", + camera_status.active ? "Active" : + (!camera_status.ready ? "Unavailable" : + (camera_status.last_result != RT_EOK ? "Error" : "Ready")), + camera_status.frame_count, camera_status.display_count, + camera_status.drop_count, camera_status.error_count); +#else + rt_snprintf(camera_text, sizeof(camera_text), "Disabled"); +#endif + + rt_snprintf(storage_text, sizeof(storage_text), "Flash %s | SD %s | USB %s", + (storage_mounts & (1U << BOARD_DEMO_STORAGE_FLASH)) ? "ready" : "--", + (storage_mounts & (1U << BOARD_DEMO_STORAGE_SD)) ? "ready" : "--", + (storage_mounts & (1U << BOARD_DEMO_STORAGE_USB)) ? "ready" : "--"); + board_demo_set_label_text(overview_status[BOARD_DEMO_PAGE_FILES], storage_text); + board_demo_set_label_text(overview_status[BOARD_DEMO_PAGE_CAN], demo_snapshot.can_status); + board_demo_set_label_text(overview_status[BOARD_DEMO_PAGE_MQTT], demo_snapshot.mqtt_status); + board_demo_set_label_text(overview_status[BOARD_DEMO_PAGE_PWM], demo_snapshot.pwm_status); + board_demo_set_label_text(overview_status[BOARD_DEMO_PAGE_CAMERA], camera_text); + board_demo_set_label_text(overview_status[BOARD_DEMO_PAGE_SPI], demo_snapshot.spi_status); + board_demo_set_label_text(overview_status[BOARD_DEMO_PAGE_I2C], demo_snapshot.i2c_status); + + rt_snprintf(uptime_text, sizeof(uptime_text), "Uptime %u s | CAN RX %u TX %u", + (rt_uint32_t)(((rt_uint64_t)rt_tick_get()) / RT_TICK_PER_SECOND), + demo_snapshot.can_rx_count, demo_snapshot.can_tx_count); + board_demo_set_label_text(overview_uptime, uptime_text); + + rt_snprintf(header_text, sizeof(header_text), "CAN %s | MQTT %s\nUSB Host %s", + demo_snapshot.can_ready ? "ready" : "unavailable", + demo_snapshot.mqtt_connected ? "online" : "offline", + (storage_mounts & (1U << BOARD_DEMO_STORAGE_USB)) ? "mounted" : "not mounted"); + board_demo_set_label_text(demo_header_status, header_text); +} + +static void board_demo_refresh(lv_timer_t *timer) +{ + RT_UNUSED(timer); + /* Camera preview has its own screen. Refresh the dashboard after returning, + * without reallocating hidden labels or polling storage during each frame. */ + if (lv_screen_active() != demo_screen) + { + return; + } + if (board_demo_backend_get_snapshot(&demo_snapshot) != RT_EOK) + { + return; + } + + board_demo_update_storage(); + board_demo_update_files(); + board_demo_update_can(); + board_demo_update_runtime(); + board_demo_update_overview(); +} + +void board_demo_init(void) +{ + enum board_demo_page_id index; + + if (demo_screen != RT_NULL) + { + return; + } + if (board_demo_backend_init() != RT_EOK) + { + LOG_E("cannot initialize board demo backend"); + return; + } + +#ifdef BSP_USING_OV7670_PREVIEW + ov7670_preview_init(); +#endif + +#if LV_USE_THEME_DEFAULT + lv_display_set_theme(lv_display_get_default(), + lv_theme_default_init(lv_display_get_default(), COLOR_ACCENT, COLOR_INFO, + false, &lv_font_montserrat_16)); +#endif + + demo_screen = lv_obj_create(RT_NULL); + lv_obj_set_style_bg_color(demo_screen, COLOR_PAGE, 0); + lv_obj_set_style_bg_opa(demo_screen, LV_OPA_COVER, 0); + lv_obj_set_scrollable(demo_screen, false); + + board_demo_create_sidebar(); + board_demo_create_header(); + board_demo_build_overview_page(); + board_demo_build_files_page(); + board_demo_build_can_page(); + board_demo_build_mqtt_page(); + board_demo_build_pwm_page(); + board_demo_build_camera_page(); + board_demo_build_spi_page(); + board_demo_build_i2c_page(); + + demo_keyboard = lv_keyboard_create(demo_screen); + lv_obj_set_size(demo_keyboard, BOARD_DEMO_CONTENT_WIDTH, BOARD_DEMO_KEYBOARD_HEIGHT); + lv_obj_align(demo_keyboard, LV_ALIGN_BOTTOM_RIGHT, 0, 0); + lv_obj_set_hidden(demo_keyboard, true); + lv_obj_add_event_cb(demo_keyboard, board_demo_keyboard_event, + LV_EVENT_READY, RT_NULL); + lv_obj_add_event_cb(demo_keyboard, board_demo_keyboard_event, + LV_EVENT_CANCEL, RT_NULL); + + for (index = BOARD_DEMO_PAGE_FILES; + index < BOARD_DEMO_PAGE_COUNT; index++) + { + lv_obj_set_hidden(demo_pages[index], true); + } + board_demo_show_page(BOARD_DEMO_PAGE_OVERVIEW); + lv_screen_load(demo_screen); + + demo_refresh_timer = lv_timer_create(board_demo_refresh, + BOARD_DEMO_REFRESH_MS, RT_NULL); + if (demo_refresh_timer == RT_NULL) + { + LOG_E("cannot create board demo refresh timer"); + } + + board_demo_update_storage(); + board_demo_files_request_path("/flash"); + LOG_I("GD32H77D LVGL board demo started"); +} + +#endif /* PKG_USING_LVGL && BSP_USING_LVGL_BOARD_DEMO */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/applications/board_demo_backend.c b/bsp/gd32/arm/gd32h77d-ginopi/applications/board_demo_backend.c new file mode 100644 index 000000000000..9b596ea2d425 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/applications/board_demo_backend.c @@ -0,0 +1,1687 @@ +/* + * Copyright (c) 2006-2026 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-09-08 CYFS implement board demo services for storage, buses, and MQTT + */ + +#include + +#if defined(PKG_USING_LVGL) && defined(BSP_USING_LVGL_BOARD_DEMO) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board_demo_backend.h" + +#if defined(BSP_USING_GD32VW553_MQTT_DEMO) && defined(PKG_USING_KAWAII_MQTT) +#include "gd32vw553_mqtt_demo.h" +#endif + +#define DBG_TAG "app.board.backend" +#define DBG_LVL DBG_INFO +#include + +#define BOARD_DEMO_WORKER_STACK 8192U +#define BOARD_DEMO_WORKER_PRIORITY 17U +#define BOARD_DEMO_WORKER_TICK 10U +#define BOARD_DEMO_CAN_WORKER_STACK 2048U +#define BOARD_DEMO_CAN_WORKER_PRIORITY 18U +#define BOARD_DEMO_CAN_WORKER_TICK 10U +#define BOARD_DEMO_CAN_POLL_MS 10U +#define BOARD_DEMO_QUEUE_DEPTH 8U +#define BOARD_DEMO_CAN_DEVICE_NAME "can1" +#define BOARD_DEMO_PWM2_DEVICE_NAME "pwm2" +#define BOARD_DEMO_PWM2_CHANNEL 2U +#define BOARD_DEMO_PWM30_DEVICE_NAME "pwm30" +#define BOARD_DEMO_PWM30_CHANNEL 3U +#define BOARD_DEMO_SPI_BUS_NAME "spi3" +#define BOARD_DEMO_SPI_DEVICE_NAME "spi3loop" +#define BOARD_DEMO_SPI_BUFFER_MAX 256U +#define BOARD_DEMO_I2C_PROBE_TIMEOUT_MS 20U +#define BOARD_DEMO_I2C_LOCK_TIMEOUT_MS 100U + +enum board_demo_command_type +{ + BOARD_DEMO_CMD_FS_LIST = 0, + BOARD_DEMO_CMD_FILE_PREVIEW, + BOARD_DEMO_CMD_CAN_BAUD, + BOARD_DEMO_CMD_CAN_SEND, + BOARD_DEMO_CMD_MQTT_START, + BOARD_DEMO_CMD_MQTT_STOP, + BOARD_DEMO_CMD_MQTT_PUBLISH, + BOARD_DEMO_CMD_PWM_APPLY, + BOARD_DEMO_CMD_SPI_LOOPBACK, + BOARD_DEMO_CMD_I2C_SCAN, +}; + +struct board_demo_command +{ + enum board_demo_command_type type; + union + { + struct + { + char path[BOARD_DEMO_FS_PATH_MAX]; + } fs; + struct + { + rt_uint32_t baud_rate; + } can_baud; + struct + { + rt_uint32_t id; + rt_uint8_t data[8]; + rt_uint8_t length; + rt_bool_t extended; + } can_send; + struct + { + char topic[BOARD_DEMO_MQTT_TOPIC_MAX]; + char payload[BOARD_DEMO_MQTT_PAYLOAD_MAX]; + } mqtt; + struct + { + rt_uint32_t frequency; + rt_uint8_t pwm2_duty; + rt_uint8_t pwm30_duty; + rt_bool_t pwm2_enabled; + rt_bool_t pwm30_enabled; + } pwm; + struct + { + rt_uint32_t frequency; + rt_uint16_t length; + } spi; + struct + { + char bus_name[RT_NAME_MAX]; + } i2c; + } data; +}; + +static struct board_demo_snapshot demo_state; +static rt_mutex_t demo_state_lock; +static rt_mq_t demo_command_queue; +static rt_thread_t demo_worker_thread; + +const char *const board_demo_storage_roots[BOARD_DEMO_STORAGE_COUNT] = +{ + "/flash", "/sd", "/udisk", +}; + +#ifdef RT_USING_CAN +static rt_device_t demo_can_device; +static rt_thread_t demo_can_thread; +#endif + +#ifdef RT_USING_PWM +static struct rt_device_pwm *demo_pwm2_device; +static struct rt_device_pwm *demo_pwm30_device; +#endif + +#if defined(RT_USING_SPI) && defined(BSP_USING_SPI3) +static struct rt_spi_device demo_spi_device; +static rt_bool_t demo_spi_attached; +#endif + +static void board_demo_strncpy(char *destination, const char *source, + rt_size_t destination_size) +{ + if (destination_size == 0U) + { + return; + } + + if (source == RT_NULL) + { + destination[0] = '\0'; + return; + } + + rt_strncpy(destination, source, destination_size - 1U); + destination[destination_size - 1U] = '\0'; +} + +static int board_demo_send_command(const struct board_demo_command *command) +{ + if ((demo_command_queue == RT_NULL) || (command == RT_NULL)) + { + return -RT_ERROR; + } + + return rt_mq_send(demo_command_queue, command, sizeof(*command)); +} + +static void board_demo_bus_log(rt_bool_t spi, const char *format, ...) +{ + char line[160]; + char *log; + char *next; + rt_size_t used; + rt_size_t length; + va_list arguments; + + va_start(arguments, format); + rt_vsnprintf(line, sizeof(line), format, arguments); + va_end(arguments); + length = rt_strlen(line); + + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + log = spi ? demo_state.spi_log : demo_state.i2c_log; + used = rt_strlen(log); + /* Keep complete recent lines when repeated progress fills the log. */ + while (used + length + 2U > BOARD_DEMO_BUS_LOG_MAX) + { + next = strchr(log, '\n'); + if (next == RT_NULL) + { + used = 0U; + break; + } + used -= (rt_size_t)(next + 1 - log); + rt_memmove(log, next + 1, used + 1U); + } + rt_memcpy(log + used, line, length); + log[used + length] = '\n'; + log[used + length + 1U] = '\0'; + if (spi) + { + demo_state.spi_sequence++; + } + else + { + demo_state.i2c_sequence++; + } + rt_mutex_release(demo_state_lock); + LOG_I("%s: %s", spi ? "SPI" : "I2C", line); +} + +static int board_demo_queue_bus_command(const struct board_demo_command *command) +{ + rt_bool_t spi = command->type == BOARD_DEMO_CMD_SPI_LOOPBACK; + rt_bool_t *busy; + char *status; + char *log; + rt_err_t result; + + if ((demo_state_lock == RT_NULL) || (demo_command_queue == RT_NULL)) + { + return -RT_ERROR; + } + + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + busy = spi ? &demo_state.spi_busy : &demo_state.i2c_busy; + status = spi ? demo_state.spi_status : demo_state.i2c_status; + log = spi ? demo_state.spi_log : demo_state.i2c_log; + if (*busy) + { + rt_mutex_release(demo_state_lock); + return -RT_EBUSY; + } + + if (spi) + { + demo_state.spi_frequency = command->data.spi.frequency; + demo_state.spi_length = command->data.spi.length; + demo_state.spi_passed = RT_FALSE; + demo_state.spi_mismatch_index = 0xFFFFU; + rt_snprintf(log, BOARD_DEMO_BUS_LOG_MAX, + "QUEUED spi3 | max %u Hz | %u bytes\n", + command->data.spi.frequency, command->data.spi.length); + demo_state.spi_sequence++; + } + else + { + board_demo_strncpy(demo_state.i2c_bus_name, command->data.i2c.bus_name, + sizeof(demo_state.i2c_bus_name)); + demo_state.i2c_device_count = 0U; + demo_state.i2c_result_code = RT_EOK; + rt_memset(demo_state.i2c_addresses, BOARD_DEMO_I2C_SKIPPED, + sizeof(demo_state.i2c_addresses)); + rt_memset(&demo_state.i2c_addresses[0x08], BOARD_DEMO_I2C_PENDING, 112U); + rt_snprintf(log, BOARD_DEMO_BUS_LOG_MAX, "QUEUED %s | 0x08-0x77\n", + command->data.i2c.bus_name); + demo_state.i2c_sequence++; + } + + /* Publish queued state before the worker can publish progress. */ + result = board_demo_send_command(command); + *busy = result == RT_EOK; + if (result == RT_EOK) + { + rt_snprintf(status, BOARD_DEMO_STATUS_MAX, "%s request queued", + spi ? "SPI" : "I2C"); + } + else + { + rt_snprintf(status, BOARD_DEMO_STATUS_MAX, "Request queue failed: %d", result); + rt_snprintf(log, BOARD_DEMO_BUS_LOG_MAX, "FAIL request queue: %d\n", result); + if (!spi) + { + demo_state.i2c_result_code = result; + } + } + rt_mutex_release(demo_state_lock); + return result; +} + +int board_demo_fs_source(const char *path) +{ + const char *component; + const char *cursor; + rt_size_t root_length; + int index; + + if ((path == RT_NULL) || (path[0] != '/') || + (rt_strlen(path) >= BOARD_DEMO_FS_PATH_MAX)) + { + return -1; + } + + /* Reject dot components before DFS normalizes a path outside its volume. */ + for (component = path + 1; *component != '\0'; component = cursor) + { + for (cursor = component; (*cursor != '\0') && (*cursor != '/'); cursor++) + { + } + if (((cursor - component == 1) && (component[0] == '.')) || + ((cursor - component == 2) && (component[0] == '.') && + (component[1] == '.'))) + { + return -1; + } + if (*cursor == '/') + { + cursor++; + } + } + + for (index = 0; index < BOARD_DEMO_STORAGE_COUNT; index++) + { + root_length = rt_strlen(board_demo_storage_roots[index]); + if ((rt_strncmp(path, board_demo_storage_roots[index], root_length) == 0) && + ((path[root_length] == '\0') || (path[root_length] == '/'))) + { + return index; + } + } + + return -1; +} + +static rt_bool_t board_demo_path_is_allowed(const char *path) +{ + return board_demo_fs_source(path) >= 0 ? RT_TRUE : RT_FALSE; +} + +static rt_bool_t board_demo_path_is_mounted(const char *path) +{ + struct dfs_filesystem *filesystem; + rt_bool_t mounted; + int source; + + source = board_demo_fs_source(path); + if (source < 0) + { + return RT_FALSE; + } + /* Keep the mount record valid while USB removal updates the DFS table. */ + dfs_lock(); + filesystem = dfs_filesystem_lookup(path); + mounted = (filesystem != RT_NULL) && (filesystem->path != RT_NULL) && + (rt_strcmp(filesystem->path, board_demo_storage_roots[source]) == 0); + dfs_unlock(); + return mounted; +} + +rt_uint8_t board_demo_storage_mounts(void) +{ + rt_uint8_t mounts = 0U; + int source; + + for (source = 0; source < BOARD_DEMO_STORAGE_COUNT; source++) + { + if (board_demo_path_is_mounted(board_demo_storage_roots[source])) + { + mounts |= (rt_uint8_t)(1U << source); + } + } + return mounts; +} + +static void board_demo_sort_entries(struct board_demo_fs_entry *entries, + rt_uint8_t count) +{ + struct board_demo_fs_entry temporary; + rt_uint8_t current; + rt_uint8_t previous; + rt_bool_t move; + + for (current = 1U; current < count; current++) + { + temporary = entries[current]; + previous = current; + while (previous > 0U) + { + move = RT_FALSE; + if (entries[previous - 1U].is_directory != temporary.is_directory) + { + move = temporary.is_directory; + } + else if (rt_strcmp(entries[previous - 1U].name, temporary.name) > 0) + { + move = RT_TRUE; + } + + if (!move) + { + break; + } + + entries[previous] = entries[previous - 1U]; + previous--; + } + entries[previous] = temporary; + } +} + +static void board_demo_fs_list(const char *path) +{ + struct board_demo_fs_entry entries[BOARD_DEMO_FS_ENTRY_MAX]; + struct dirent *directory_entry; + struct stat file_status; + DIR *directory; + char full_path[BOARD_DEMO_FS_PATH_MAX]; + char status[BOARD_DEMO_STATUS_MAX]; + rt_uint8_t count = 0U; + rt_bool_t limited = RT_FALSE; + int result; + + if (!board_demo_path_is_allowed(path)) + { + rt_snprintf(status, sizeof(status), "Path is outside demo storage roots"); + goto _update_failure; + } + if (!board_demo_path_is_mounted(path)) + { + if (rt_strncmp(path, "/flash", 6U) == 0) + { + rt_snprintf(status, sizeof(status), + "Flash is not mounted or formatted"); + } + else if (board_demo_fs_source(path) == BOARD_DEMO_STORAGE_SD) + { + rt_snprintf(status, sizeof(status), "SD card not mounted"); + } + else + { + rt_snprintf(status, sizeof(status), + "USB Host: no FAT/FAT32 volume mounted"); + } + LOG_W("storage path is not mounted: %s", path); + goto _update_failure; + } + + directory = opendir(path); + if (directory == RT_NULL) + { + rt_snprintf(status, sizeof(status), "Cannot open %s", path); + LOG_E("cannot open storage directory: %s", path); + goto _update_failure; + } + + rt_memset(entries, 0, sizeof(entries)); + while ((directory_entry = readdir(directory)) != RT_NULL) + { + if ((rt_strcmp(directory_entry->d_name, ".") == 0) || + (rt_strcmp(directory_entry->d_name, "..") == 0)) + { + continue; + } + if (count >= BOARD_DEMO_FS_ENTRY_MAX) + { + limited = RT_TRUE; + break; + } + + result = rt_snprintf(full_path, sizeof(full_path), "%s/%s", + path, directory_entry->d_name); + if ((result <= 0) || ((rt_size_t)result >= sizeof(full_path))) + { + limited = RT_TRUE; + continue; + } + + board_demo_strncpy(entries[count].name, directory_entry->d_name, + sizeof(entries[count].name)); + if (directory_entry->d_type == DT_DIR) + { + entries[count].is_directory = RT_TRUE; + entries[count].size = 0U; + } + else + { + entries[count].is_directory = RT_FALSE; + entries[count].size = 0U; + if (stat(full_path, &file_status) == 0) + { + entries[count].is_directory = S_ISDIR(file_status.st_mode) ? + RT_TRUE : RT_FALSE; + entries[count].size = entries[count].is_directory ? 0U : + (rt_uint32_t)file_status.st_size; + } + } + count++; + } + closedir(directory); + + board_demo_sort_entries(entries, count); + if (limited) + { + rt_snprintf(status, sizeof(status), "%u entries | Listing limit reached", count); + } + else if (count == 0U) + { + board_demo_strncpy(status, "Mounted, empty", sizeof(status)); + } + else + { + rt_snprintf(status, sizeof(status), "%u entries", count); + } + LOG_I("listed %u entries from %s", count, path); + + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + board_demo_strncpy(demo_state.fs_path, path, sizeof(demo_state.fs_path)); + board_demo_strncpy(demo_state.fs_status, status, + sizeof(demo_state.fs_status)); + rt_memcpy(demo_state.fs_entries, entries, sizeof(entries)); + demo_state.fs_entry_count = count; + demo_state.fs_sequence++; + demo_state.file_name[0] = '\0'; + demo_state.file_preview[0] = '\0'; + demo_state.file_sequence++; + rt_mutex_release(demo_state_lock); + return; + +_update_failure: + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + board_demo_strncpy(demo_state.fs_path, path, sizeof(demo_state.fs_path)); + board_demo_strncpy(demo_state.fs_status, status, + sizeof(demo_state.fs_status)); + rt_memset(demo_state.fs_entries, 0, sizeof(demo_state.fs_entries)); + demo_state.fs_entry_count = 0U; + demo_state.fs_sequence++; + demo_state.file_name[0] = '\0'; + demo_state.file_preview[0] = '\0'; + demo_state.file_sequence++; + rt_mutex_release(demo_state_lock); +} + +static void board_demo_preview_append(char *buffer, rt_size_t buffer_size, + rt_size_t *used, const char *format, ...) +{ + va_list arguments; + int length; + + if (*used >= buffer_size - 1U) + { + return; + } + + va_start(arguments, format); + length = rt_vsnprintf(buffer + *used, buffer_size - *used, format, arguments); + va_end(arguments); + if (length <= 0) + { + return; + } + + if ((rt_size_t)length >= buffer_size - *used) + { + *used = buffer_size - 1U; + } + else + { + *used += (rt_size_t)length; + } +} + +static void board_demo_file_preview(const char *path) +{ + rt_uint8_t raw_data[384]; + char preview[BOARD_DEMO_FILE_PREVIEW_MAX]; + rt_size_t printable = 0U; + rt_size_t used = 0U; + rt_size_t index; + int descriptor; + int length; + + rt_memset(preview, 0, sizeof(preview)); + if (!board_demo_path_is_allowed(path)) + { + rt_snprintf(preview, sizeof(preview), "Path is outside demo storage roots"); + goto _update; + } + if (!board_demo_path_is_mounted(path)) + { + rt_snprintf(preview, sizeof(preview), "Storage disconnected or not mounted"); + goto _update; + } + + descriptor = open(path, O_RDONLY, 0); + if (descriptor < 0) + { + rt_snprintf(preview, sizeof(preview), "Cannot open %s", path); + goto _update; + } + + length = read(descriptor, raw_data, sizeof(raw_data)); + close(descriptor); + if (length < 0) + { + rt_snprintf(preview, sizeof(preview), "Cannot read %s", path); + goto _update; + } + if (length == 0) + { + rt_snprintf(preview, sizeof(preview), "Empty file"); + goto _update; + } + + for (index = 0U; index < (rt_size_t)length; index++) + { + if (((raw_data[index] >= 0x20U) && (raw_data[index] <= 0x7EU)) || + (raw_data[index] == '\r') || (raw_data[index] == '\n') || + (raw_data[index] == '\t')) + { + printable++; + } + } + + if (printable * 100U >= (rt_size_t)length * 85U) + { + for (index = 0U; + (index < (rt_size_t)length) && (used < sizeof(preview) - 1U); + index++) + { + preview[used++] = (char)raw_data[index]; + } + preview[used] = '\0'; + } + else + { + board_demo_preview_append(preview, sizeof(preview), &used, + "Binary preview (%d bytes shown)\n", length); + for (index = 0U; index < (rt_size_t)length; index++) + { + if ((index % 16U) == 0U) + { + board_demo_preview_append(preview, sizeof(preview), &used, + "%04X: ", (unsigned int)index); + } + board_demo_preview_append(preview, sizeof(preview), &used, + "%02X ", raw_data[index]); + if (((index % 16U) == 15U) || (index + 1U == (rt_size_t)length)) + { + board_demo_preview_append(preview, sizeof(preview), &used, "\n"); + } + } + } + +_update: + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + board_demo_strncpy(demo_state.file_name, path, + sizeof(demo_state.file_name)); + board_demo_strncpy(demo_state.file_preview, preview, + sizeof(demo_state.file_preview)); + demo_state.file_sequence++; + rt_mutex_release(demo_state_lock); +} + +#ifdef RT_USING_CAN +static void board_demo_can_add_frame(const struct rt_can_msg *message, + rt_bool_t transmitted) +{ + struct board_demo_can_frame *frame; + rt_uint8_t length; + + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + if (demo_state.can_frame_count < BOARD_DEMO_CAN_LOG_MAX) + { + demo_state.can_frame_count++; + } + if (demo_state.can_frame_count > 1U) + { + rt_memmove(&demo_state.can_frames[1], &demo_state.can_frames[0], + (demo_state.can_frame_count - 1U) * + sizeof(demo_state.can_frames[0])); + } + + frame = &demo_state.can_frames[0]; + rt_memset(frame, 0, sizeof(*frame)); + frame->id = message->id; + frame->tick = rt_tick_get(); + frame->extended = (message->ide == RT_CAN_EXTID) ? RT_TRUE : RT_FALSE; + frame->transmitted = transmitted; + length = message->len > 8U ? 8U : message->len; + frame->length = length; + rt_memcpy(frame->data, message->data, length); + if (transmitted) + { + demo_state.can_tx_count++; + } + else + { + demo_state.can_rx_count++; + } + demo_state.can_sequence++; + rt_mutex_release(demo_state_lock); +} + +static void board_demo_can_set_status(const char *format, ...) +{ + va_list arguments; + char status[BOARD_DEMO_STATUS_MAX]; + + va_start(arguments, format); + rt_vsnprintf(status, sizeof(status), format, arguments); + va_end(arguments); + + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + board_demo_strncpy(demo_state.can_status, status, + sizeof(demo_state.can_status)); + demo_state.can_sequence++; + rt_mutex_release(demo_state_lock); +} + +static void board_demo_can_open(void) +{ + rt_device_t can_device; + rt_err_t result; + + can_device = rt_device_find(BOARD_DEMO_CAN_DEVICE_NAME); + if (can_device == RT_NULL) + { + board_demo_can_set_status("can1 device not found"); + return; + } + + result = rt_device_open(can_device, + RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX); + if (result != RT_EOK) + { + board_demo_can_set_status("can1 open failed: %d", result); + return; + } + + result = rt_device_control(can_device, RT_CAN_CMD_SET_BAUD, + (void *)(rt_ubase_t)CAN1MBaud); + if (result != RT_EOK) + { + rt_device_close(can_device); + board_demo_can_set_status("can1 baud setup failed: %d", result); + return; + } + + demo_can_device = can_device; + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + demo_state.can_ready = RT_TRUE; + demo_state.can_baud_rate = CAN1MBaud; + board_demo_strncpy(demo_state.can_status, "can1 ready at 1 Mbit/s", + sizeof(demo_state.can_status)); + demo_state.can_sequence++; + rt_mutex_release(demo_state_lock); +} + +static void board_demo_can_set_baud(rt_uint32_t baud_rate) +{ + rt_err_t result; + + if (demo_can_device == RT_NULL) + { + board_demo_can_set_status("can1 is not ready"); + return; + } + + result = rt_device_control(demo_can_device, RT_CAN_CMD_SET_BAUD, + (void *)(rt_ubase_t)baud_rate); + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + if (result == RT_EOK) + { + demo_state.can_baud_rate = baud_rate; + rt_snprintf(demo_state.can_status, sizeof(demo_state.can_status), + "can1 ready at %u kbit/s", baud_rate / 1000U); + } + else + { + demo_state.can_error_count++; + rt_snprintf(demo_state.can_status, sizeof(demo_state.can_status), + "CAN baud setup failed: %d", result); + } + demo_state.can_sequence++; + rt_mutex_release(demo_state_lock); +} + +static void board_demo_can_send(const struct board_demo_command *command) +{ + struct rt_can_msg message; + rt_ssize_t written; + + if (demo_can_device == RT_NULL) + { + board_demo_can_set_status("can1 is not ready"); + return; + } + + rt_memset(&message, 0, sizeof(message)); + message.id = command->data.can_send.id; + message.ide = command->data.can_send.extended ? RT_CAN_EXTID : RT_CAN_STDID; + message.rtr = RT_CAN_DTR; + message.len = command->data.can_send.length; + message.nonblocking = 1U; + rt_memcpy(message.data, command->data.can_send.data, message.len); + + written = rt_device_write(demo_can_device, 0, &message, sizeof(message)); + if (written == sizeof(message)) + { + board_demo_can_add_frame(&message, RT_TRUE); + board_demo_can_set_status("CAN frame sent"); + } + else + { + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + demo_state.can_error_count++; + rt_snprintf(demo_state.can_status, sizeof(demo_state.can_status), + "CAN send failed: %d", (int)written); + demo_state.can_sequence++; + rt_mutex_release(demo_state_lock); + } +} + +static void board_demo_can_poll(void) +{ + struct rt_can_msg message; + rt_ssize_t length; + rt_uint8_t count = 0U; + + if (demo_can_device == RT_NULL) + { + return; + } + + do + { + rt_memset(&message, 0, sizeof(message)); + length = rt_device_read(demo_can_device, 0, &message, sizeof(message)); + if (length == sizeof(message)) + { + board_demo_can_add_frame(&message, RT_FALSE); + count++; + } + } + while ((length == sizeof(message)) && (count < 8U)); +} + +static void board_demo_can_worker_entry(void *parameter) +{ + RT_UNUSED(parameter); + + LOG_I("CAN worker started"); + board_demo_can_open(); + + while (1) + { + board_demo_can_poll(); + rt_thread_mdelay(BOARD_DEMO_CAN_POLL_MS); + } +} +#endif /* RT_USING_CAN */ + +#if defined(BSP_USING_GD32VW553_MQTT_DEMO) && defined(PKG_USING_KAWAII_MQTT) +static void board_demo_mqtt_receive(const char *topic, const void *payload, + rt_size_t payload_length) +{ + rt_size_t copy_length; + + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + board_demo_strncpy(demo_state.mqtt_rx_topic, topic, + sizeof(demo_state.mqtt_rx_topic)); + copy_length = payload_length; + if (copy_length >= sizeof(demo_state.mqtt_rx_payload)) + { + copy_length = sizeof(demo_state.mqtt_rx_payload) - 1U; + } + rt_memcpy(demo_state.mqtt_rx_payload, payload, copy_length); + demo_state.mqtt_rx_payload[copy_length] = '\0'; + board_demo_strncpy(demo_state.mqtt_status, "MQTT message received", + sizeof(demo_state.mqtt_status)); + demo_state.mqtt_sequence++; + rt_mutex_release(demo_state_lock); +} + +static void board_demo_mqtt_status(const char *status, int result) +{ + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + if (result == RT_EOK) + { + board_demo_strncpy(demo_state.mqtt_status, status, + sizeof(demo_state.mqtt_status)); + } + else + { + rt_snprintf(demo_state.mqtt_status, sizeof(demo_state.mqtt_status), + "%s (%d)", status, result); + } + demo_state.mqtt_sequence++; + rt_mutex_release(demo_state_lock); +} + +static void board_demo_mqtt_update_state(void) +{ + rt_bool_t running; + rt_bool_t connected; + + running = gd32vw553_mqtt_demo_is_running(); + connected = gd32vw553_mqtt_demo_is_connected(); + + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + if ((demo_state.mqtt_running != running) || + (demo_state.mqtt_connected != connected)) + { + demo_state.mqtt_running = running; + demo_state.mqtt_connected = connected; + demo_state.mqtt_sequence++; + } + rt_mutex_release(demo_state_lock); +} + +static int board_demo_mqtt_command(const struct board_demo_command *command) +{ + const char *operation; + int result; + + if (command->type == BOARD_DEMO_CMD_MQTT_START) + { + operation = "start"; + result = gd32vw553_mqtt_demo_start(); + } + else if (command->type == BOARD_DEMO_CMD_MQTT_STOP) + { + operation = "stop"; + result = gd32vw553_mqtt_demo_stop(); + } + else + { + operation = "publish"; + result = gd32vw553_mqtt_demo_publish(command->data.mqtt.topic, + command->data.mqtt.payload); + } + LOG_I("MQTT %s command result: %d", operation, result); + + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + if (result == RT_EOK) + { + if (command->type == BOARD_DEMO_CMD_MQTT_PUBLISH) + { + board_demo_strncpy(demo_state.mqtt_tx_topic, + command->data.mqtt.topic, + sizeof(demo_state.mqtt_tx_topic)); + board_demo_strncpy(demo_state.mqtt_tx_payload, + command->data.mqtt.payload, + sizeof(demo_state.mqtt_tx_payload)); + board_demo_strncpy(demo_state.mqtt_status, "MQTT message published", + sizeof(demo_state.mqtt_status)); + } + else if (command->type == BOARD_DEMO_CMD_MQTT_START) + { + board_demo_strncpy(demo_state.mqtt_status, "MQTT worker started", + sizeof(demo_state.mqtt_status)); + } + else + { + board_demo_strncpy(demo_state.mqtt_status, "MQTT stopping", + sizeof(demo_state.mqtt_status)); + } + } + else + { + rt_snprintf(demo_state.mqtt_status, sizeof(demo_state.mqtt_status), + "MQTT operation failed: %d", result); + } + demo_state.mqtt_sequence++; + rt_mutex_release(demo_state_lock); + + board_demo_mqtt_update_state(); + return result; +} +#endif + +#ifdef RT_USING_PWM +static rt_err_t board_demo_pwm_apply_channel(struct rt_device_pwm *device, + rt_uint32_t channel, + rt_uint32_t period, + rt_bool_t enabled, + rt_uint8_t duty) +{ + rt_err_t result; + rt_uint32_t pulse; + + pulse = (rt_uint32_t)(((rt_uint64_t)period * duty) / 100U); + result = rt_pwm_set(device, channel, period, pulse); + if (result != RT_EOK) + { + return result; + } + + return enabled ? rt_pwm_enable(device, channel) : + rt_pwm_disable(device, channel); +} + +static void board_demo_pwm_apply(const struct board_demo_command *command) +{ + rt_uint32_t period; + rt_err_t result; + + if ((demo_pwm2_device == RT_NULL) || (demo_pwm30_device == RT_NULL)) + { + demo_pwm2_device = (struct rt_device_pwm *) + rt_device_find(BOARD_DEMO_PWM2_DEVICE_NAME); + demo_pwm30_device = (struct rt_device_pwm *) + rt_device_find(BOARD_DEMO_PWM30_DEVICE_NAME); + } + if ((demo_pwm2_device == RT_NULL) || (demo_pwm30_device == RT_NULL)) + { + result = -RT_ENOSYS; + goto _update; + } + if (command->data.pwm.frequency == 0U) + { + result = -RT_EINVAL; + goto _update; + } + + period = 1000000000U / command->data.pwm.frequency; + result = board_demo_pwm_apply_channel(demo_pwm2_device, + BOARD_DEMO_PWM2_CHANNEL, + period, + command->data.pwm.pwm2_enabled, + command->data.pwm.pwm2_duty); + if (result == RT_EOK) + { + result = board_demo_pwm_apply_channel(demo_pwm30_device, + BOARD_DEMO_PWM30_CHANNEL, + period, + command->data.pwm.pwm30_enabled, + command->data.pwm.pwm30_duty); + } + +_update: + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + if (result == RT_EOK) + { + demo_state.pwm_frequency = command->data.pwm.frequency; + demo_state.pwm2_enabled = command->data.pwm.pwm2_enabled; + demo_state.pwm2_duty = command->data.pwm.pwm2_duty; + demo_state.pwm30_enabled = command->data.pwm.pwm30_enabled; + demo_state.pwm30_duty = command->data.pwm.pwm30_duty; + rt_snprintf(demo_state.pwm_status, sizeof(demo_state.pwm_status), + "PWM updated at %u Hz", command->data.pwm.frequency); + } + else + { + rt_snprintf(demo_state.pwm_status, sizeof(demo_state.pwm_status), + "PWM update failed: %d", result); + } + demo_state.pwm_sequence++; + rt_mutex_release(demo_state_lock); +} +#endif /* RT_USING_PWM */ + +#if defined(RT_USING_SPI) && defined(BSP_USING_SPI3) +static void board_demo_spi_dump(const char *direction, const rt_uint8_t *data, + rt_uint16_t length) +{ + char line[64]; + rt_size_t used; + rt_uint16_t offset; + rt_uint16_t index; + + for (offset = 0U; offset < length; offset += 8U) + { + used = 0U; + line[0] = '\0'; + board_demo_preview_append(line, sizeof(line), &used, + "%s %03u:", direction, offset); + for (index = offset; (index < length) && (index < offset + 8U); index++) + { + board_demo_preview_append(line, sizeof(line), &used, " %02X", data[index]); + } + board_demo_bus_log(RT_TRUE, "%s", line); + } +} + +static void board_demo_spi_loopback(const struct board_demo_command *command) +{ + struct rt_spi_configuration configuration; + rt_uint8_t transmit_buffer[BOARD_DEMO_SPI_BUFFER_MAX]; + rt_uint8_t receive_buffer[BOARD_DEMO_SPI_BUFFER_MAX]; + rt_uint16_t mismatch = 0xFFFFU; + rt_uint16_t index; + rt_ssize_t transferred; + rt_err_t result; + + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + board_demo_strncpy(demo_state.spi_status, "SPI loopback running...", + sizeof(demo_state.spi_status)); + demo_state.spi_sequence++; + rt_mutex_release(demo_state_lock); + board_demo_bus_log(RT_TRUE, "START spi3 | mode 0 | 8-bit | no CS"); + board_demo_bus_log(RT_TRUE, "SCK PE12 | MOSI PF1 <-> MISO PF0"); + + if (!demo_spi_attached) + { + result = rt_spi_bus_attach_device(&demo_spi_device, + BOARD_DEMO_SPI_DEVICE_NAME, + BOARD_DEMO_SPI_BUS_NAME, + RT_NULL); + if (result != RT_EOK) + { + board_demo_bus_log(RT_TRUE, "FAIL attach %s: %d", + BOARD_DEMO_SPI_BUS_NAME, result); + goto _update; + } + demo_spi_attached = RT_TRUE; + } + + rt_memset(&configuration, 0, sizeof(configuration)); + configuration.data_width = 8U; + configuration.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | + RT_SPI_MSB | RT_SPI_NO_CS; + configuration.max_hz = command->data.spi.frequency; + result = rt_spi_configure(&demo_spi_device, &configuration); + if (result != RT_EOK) + { + board_demo_bus_log(RT_TRUE, "FAIL configure: %d", result); + goto _update; + } + board_demo_bus_log(RT_TRUE, "CONFIG max %u Hz | %u bytes", + command->data.spi.frequency, command->data.spi.length); + + for (index = 0U; index < command->data.spi.length; index++) + { + transmit_buffer[index] = (rt_uint8_t)(0x5AU ^ index ^ (index >> 3U)); + } + rt_memset(receive_buffer, 0, command->data.spi.length); + board_demo_spi_dump("TX", transmit_buffer, command->data.spi.length); + board_demo_bus_log(RT_TRUE, "TRANSFER started"); + transferred = rt_spi_transfer(&demo_spi_device, + transmit_buffer, receive_buffer, + command->data.spi.length); + if (transferred != command->data.spi.length) + { + board_demo_bus_log(RT_TRUE, "FAIL transfer: %d / %u bytes", + (int)transferred, command->data.spi.length); + if ((transferred > 0) && (transferred < command->data.spi.length)) + { + board_demo_spi_dump("RX", receive_buffer, (rt_uint16_t)transferred); + } + result = -RT_EIO; + goto _update; + } + board_demo_spi_dump("RX", receive_buffer, command->data.spi.length); + + result = RT_EOK; + for (index = 0U; index < command->data.spi.length; index++) + { + if (transmit_buffer[index] != receive_buffer[index]) + { + mismatch = index; + result = -RT_ERROR; + board_demo_bus_log(RT_TRUE, "FAIL byte %u: TX %02X != RX %02X", + index, transmit_buffer[index], receive_buffer[index]); + board_demo_bus_log(RT_TRUE, "Check PF1-PF0 loopback connection"); + break; + } + } + if (result == RT_EOK) + { + board_demo_bus_log(RT_TRUE, "PASS: all %u bytes match", command->data.spi.length); + } + +_update: + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + demo_state.spi_frequency = command->data.spi.frequency; + demo_state.spi_length = command->data.spi.length; + demo_state.spi_mismatch_index = mismatch; + demo_state.spi_passed = (result == RT_EOK) ? RT_TRUE : RT_FALSE; + demo_state.spi_busy = RT_FALSE; + if (result == RT_EOK) + { + rt_snprintf(demo_state.spi_status, sizeof(demo_state.spi_status), + "Loopback passed: %u bytes", command->data.spi.length); + } + else if (mismatch != 0xFFFFU) + { + rt_snprintf(demo_state.spi_status, sizeof(demo_state.spi_status), + "Mismatch at byte %u", mismatch); + } + else + { + rt_snprintf(demo_state.spi_status, sizeof(demo_state.spi_status), + "SPI loopback failed: %d", result); + } + demo_state.spi_sequence++; + rt_mutex_release(demo_state_lock); +} +#endif + +#ifdef RT_USING_I2C +static rt_ssize_t board_demo_i2c_probe(struct rt_i2c_bus_device *bus, + rt_uint16_t address) +{ + struct rt_i2c_msg message; + rt_uint32_t saved_timeout; + rt_tick_t probe_timeout; + rt_ssize_t result; + + rt_memset(&message, 0, sizeof(message)); + message.addr = address; + message.flags = RT_I2C_WR; + + result = rt_i2c_bus_lock(bus, + rt_tick_from_millisecond(BOARD_DEMO_I2C_LOCK_TIMEOUT_MS)); + if (result != RT_EOK) + { + return result == -RT_ETIMEOUT ? -RT_EBUSY : result; + } + + /* The recursive bus lock keeps this temporary timeout private to the probe. */ + saved_timeout = bus->timeout; + probe_timeout = rt_tick_from_millisecond(BOARD_DEMO_I2C_PROBE_TIMEOUT_MS); + if ((bus->timeout == 0U) || (bus->timeout > probe_timeout)) + { + bus->timeout = probe_timeout; + } + result = rt_i2c_transfer(bus, &message, 1U); + bus->timeout = saved_timeout; + rt_i2c_bus_unlock(bus); + return result; +} + +static void board_demo_i2c_scan(const char *bus_name) +{ + struct rt_i2c_bus_device *bus; + rt_tick_t start_tick = rt_tick_get(); + rt_uint32_t elapsed_ms; + rt_uint16_t address = 0x08U; + rt_uint16_t probed = 0U; + rt_uint8_t count = 0U; + rt_uint8_t state; + rt_ssize_t transferred; + rt_err_t result = RT_EOK; + + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + board_demo_strncpy(demo_state.i2c_bus_name, bus_name, + sizeof(demo_state.i2c_bus_name)); + rt_snprintf(demo_state.i2c_status, sizeof(demo_state.i2c_status), + "%s scanning...", bus_name); + demo_state.i2c_sequence++; + rt_mutex_release(demo_state_lock); + board_demo_bus_log(RT_FALSE, "START %s | 7-bit addresses 0x08-0x77", bus_name); + board_demo_bus_log(RT_FALSE, "Address-only probe | timeout %u ms", + BOARD_DEMO_I2C_PROBE_TIMEOUT_MS); + + bus = rt_i2c_bus_device_find(bus_name); + if (bus == RT_NULL) + { + board_demo_bus_log(RT_FALSE, "FAIL: bus %s not found", bus_name); + result = -RT_ENOENT; + goto _update; + } + + for (address = 0x08U; address <= 0x77U; address++) + { + transferred = board_demo_i2c_probe(bus, address); + probed++; + if (transferred == 1) + { + state = BOARD_DEMO_I2C_ACK; + count++; + board_demo_bus_log(RT_FALSE, "ACK %s address 0x%02X", bus_name, address); + } + else if ((transferred == 0) || (transferred == -RT_EIO)) + { + /* The GD32 address-only transfer reports an address NACK as EIO. */ + state = BOARD_DEMO_I2C_NACK; + } + else + { + state = BOARD_DEMO_I2C_ERROR; + result = transferred < 0 ? (rt_err_t)transferred : -RT_ERROR; + } + + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + demo_state.i2c_addresses[address] = state; + demo_state.i2c_device_count = count; + rt_snprintf(demo_state.i2c_status, sizeof(demo_state.i2c_status), + "%s | %u/112 | found %u", bus_name, probed, count); + demo_state.i2c_sequence++; + rt_mutex_release(demo_state_lock); + if (result != RT_EOK) + { + board_demo_bus_log(RT_FALSE, "FAIL 0x%02X: %s (%d)", address, + result == -RT_ETIMEOUT ? "bus/probe timeout" : + (result == -RT_EBUSY ? "bus in use" : "transfer error"), + result); + break; + } + } + if ((result == RT_EOK) && (count == 0U)) + { + board_demo_bus_log(RT_FALSE, "No response: check power, SCL/SDA and pull-ups"); + } + +_update: + elapsed_ms = (rt_uint32_t)(((rt_uint64_t)(rt_tick_get() - start_tick) * 1000U) / + RT_TICK_PER_SECOND); + board_demo_bus_log(RT_FALSE, "%s %s | %u/112 | %u devices | %u ms", + result == RT_EOK ? "DONE" : "STOPPED", bus_name, + probed, count, elapsed_ms); + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + board_demo_strncpy(demo_state.i2c_bus_name, bus_name, + sizeof(demo_state.i2c_bus_name)); + demo_state.i2c_device_count = count; + demo_state.i2c_result_code = result; + demo_state.i2c_busy = RT_FALSE; + if (result == RT_EOK) + { + rt_snprintf(demo_state.i2c_status, sizeof(demo_state.i2c_status), + "%s: %u device%s | %u ms", bus_name, count, + count == 1U ? "" : "s", elapsed_ms); + } + else if (bus == RT_NULL) + { + rt_snprintf(demo_state.i2c_status, sizeof(demo_state.i2c_status), + "%s is not available", bus_name); + } + else + { + rt_snprintf(demo_state.i2c_status, sizeof(demo_state.i2c_status), + "%s stopped at 0x%02X: %s (%d)", bus_name, address, + result == -RT_ETIMEOUT ? "timeout" : + (result == -RT_EBUSY ? "bus in use" : "transfer error"), result); + } + demo_state.i2c_sequence++; + rt_mutex_release(demo_state_lock); +} +#endif /* RT_USING_I2C */ + +static void board_demo_handle_command(const struct board_demo_command *command) +{ + switch (command->type) + { + case BOARD_DEMO_CMD_CAN_BAUD: +#ifdef RT_USING_CAN + board_demo_can_set_baud(command->data.can_baud.baud_rate); +#endif + break; + case BOARD_DEMO_CMD_CAN_SEND: +#ifdef RT_USING_CAN + board_demo_can_send(command); +#endif + break; + case BOARD_DEMO_CMD_MQTT_START: + case BOARD_DEMO_CMD_MQTT_STOP: + case BOARD_DEMO_CMD_MQTT_PUBLISH: +#if defined(BSP_USING_GD32VW553_MQTT_DEMO) && defined(PKG_USING_KAWAII_MQTT) + board_demo_mqtt_command(command); +#endif + break; + case BOARD_DEMO_CMD_PWM_APPLY: +#ifdef RT_USING_PWM + board_demo_pwm_apply(command); +#endif + break; + case BOARD_DEMO_CMD_SPI_LOOPBACK: +#if defined(RT_USING_SPI) && defined(BSP_USING_SPI3) + board_demo_spi_loopback(command); +#endif + break; + case BOARD_DEMO_CMD_I2C_SCAN: +#ifdef RT_USING_I2C + board_demo_i2c_scan(command->data.i2c.bus_name); +#endif + break; + default: + break; + } +} + +static void board_demo_worker_entry(void *parameter) +{ + struct board_demo_command command; + rt_ssize_t received; + + RT_UNUSED(parameter); + LOG_I("command worker started"); + + while (1) + { + received = rt_mq_recv(demo_command_queue, &command, sizeof(command), + rt_tick_from_millisecond(20U)); + /* rt_mq_recv returns the byte count on success, not RT_EOK. */ + if (received == (rt_ssize_t)sizeof(command)) + { + LOG_I("processing command %d", command.type); + board_demo_handle_command(&command); + } + else if (received != -RT_ETIMEOUT) + { + LOG_E("command receive returned %d, expected %u bytes", + (int)received, (unsigned int)sizeof(command)); + } + +#if defined(BSP_USING_GD32VW553_MQTT_DEMO) && defined(PKG_USING_KAWAII_MQTT) + board_demo_mqtt_update_state(); +#endif + } +} + +int board_demo_backend_init(void) +{ + rt_err_t result; +#ifdef RT_USING_CAN + rt_err_t can_result; +#endif + + if (demo_worker_thread != RT_NULL) + { + return RT_EOK; + } + + rt_memset(&demo_state, 0, sizeof(demo_state)); + board_demo_strncpy(demo_state.fs_path, "/flash", + sizeof(demo_state.fs_path)); + board_demo_strncpy(demo_state.fs_status, "Storage not scanned", + sizeof(demo_state.fs_status)); + board_demo_strncpy(demo_state.can_status, "CAN initializing", + sizeof(demo_state.can_status)); + board_demo_strncpy(demo_state.mqtt_status, "MQTT stopped", + sizeof(demo_state.mqtt_status)); + board_demo_strncpy(demo_state.pwm_status, "PWM idle", + sizeof(demo_state.pwm_status)); + board_demo_strncpy(demo_state.spi_status, "SPI loopback idle", + sizeof(demo_state.spi_status)); + board_demo_strncpy(demo_state.i2c_status, "I2C scan idle", + sizeof(demo_state.i2c_status)); + demo_state.spi_mismatch_index = 0xFFFFU; + + demo_state_lock = rt_mutex_create("boardui", RT_IPC_FLAG_PRIO); + if (demo_state_lock == RT_NULL) + { + return -RT_ENOMEM; + } + + demo_command_queue = rt_mq_create("boardcmd", sizeof(struct board_demo_command), + BOARD_DEMO_QUEUE_DEPTH, RT_IPC_FLAG_PRIO); + if (demo_command_queue == RT_NULL) + { + rt_mutex_delete(demo_state_lock); + demo_state_lock = RT_NULL; + return -RT_ENOMEM; + } + +#if defined(BSP_USING_GD32VW553_MQTT_DEMO) && defined(PKG_USING_KAWAII_MQTT) + gd32vw553_mqtt_demo_set_receive_callback(board_demo_mqtt_receive); + gd32vw553_mqtt_demo_set_status_callback(board_demo_mqtt_status); +#endif + + demo_worker_thread = rt_thread_create("boarddemo", board_demo_worker_entry, + RT_NULL, BOARD_DEMO_WORKER_STACK, + BOARD_DEMO_WORKER_PRIORITY, + BOARD_DEMO_WORKER_TICK); + if (demo_worker_thread == RT_NULL) + { + rt_mq_delete(demo_command_queue); + rt_mutex_delete(demo_state_lock); + demo_command_queue = RT_NULL; + demo_state_lock = RT_NULL; + return -RT_ENOMEM; + } + + result = rt_thread_startup(demo_worker_thread); + if (result != RT_EOK) + { + rt_thread_delete(demo_worker_thread); + rt_mq_delete(demo_command_queue); + rt_mutex_delete(demo_state_lock); + demo_worker_thread = RT_NULL; + demo_command_queue = RT_NULL; + demo_state_lock = RT_NULL; + return result; + } + +#ifdef RT_USING_CAN + demo_can_thread = rt_thread_create("bdcan", board_demo_can_worker_entry, + RT_NULL, BOARD_DEMO_CAN_WORKER_STACK, + BOARD_DEMO_CAN_WORKER_PRIORITY, + BOARD_DEMO_CAN_WORKER_TICK); + if (demo_can_thread == RT_NULL) + { + board_demo_can_set_status("CAN worker creation failed"); + } + else + { + can_result = rt_thread_startup(demo_can_thread); + if (can_result != RT_EOK) + { + rt_thread_delete(demo_can_thread); + demo_can_thread = RT_NULL; + board_demo_can_set_status("CAN worker startup failed: %d", + can_result); + } + } +#endif + + return RT_EOK; +} + +int board_demo_backend_get_snapshot(struct board_demo_snapshot *snapshot) +{ + if ((snapshot == RT_NULL) || (demo_state_lock == RT_NULL)) + { + return -RT_EINVAL; + } + +#if defined(BSP_USING_GD32VW553_MQTT_DEMO) && defined(PKG_USING_KAWAII_MQTT) + board_demo_mqtt_update_state(); +#endif + + rt_mutex_take(demo_state_lock, RT_WAITING_FOREVER); + rt_memcpy(snapshot, &demo_state, sizeof(*snapshot)); + rt_mutex_release(demo_state_lock); + return RT_EOK; +} + +int board_demo_request_fs_list(const char *path) +{ + if ((path == RT_NULL) || !board_demo_path_is_allowed(path)) + { + return -RT_EINVAL; + } + + board_demo_fs_list(path); + return RT_EOK; +} + +int board_demo_request_file_preview(const char *path) +{ + if ((path == RT_NULL) || !board_demo_path_is_allowed(path)) + { + return -RT_EINVAL; + } + + board_demo_file_preview(path); + return RT_EOK; +} + +int board_demo_request_can_baud(rt_uint32_t baud_rate) +{ + struct board_demo_command command; + + rt_memset(&command, 0, sizeof(command)); + command.type = BOARD_DEMO_CMD_CAN_BAUD; + command.data.can_baud.baud_rate = baud_rate; + return board_demo_send_command(&command); +} + +int board_demo_request_can_send(rt_uint32_t id, rt_bool_t extended, + const rt_uint8_t *data, rt_uint8_t length) +{ + struct board_demo_command command; + + if ((data == RT_NULL) || (length > 8U) || + (!extended && (id > 0x7FFU)) || (extended && (id > 0x1FFFFFFFU))) + { + return -RT_EINVAL; + } + + rt_memset(&command, 0, sizeof(command)); + command.type = BOARD_DEMO_CMD_CAN_SEND; + command.data.can_send.id = id; + command.data.can_send.extended = extended; + command.data.can_send.length = length; + rt_memcpy(command.data.can_send.data, data, length); + return board_demo_send_command(&command); +} + +int board_demo_request_mqtt_start(void) +{ +#if defined(BSP_USING_GD32VW553_MQTT_DEMO) && defined(PKG_USING_KAWAII_MQTT) + struct board_demo_command command; + + rt_memset(&command, 0, sizeof(command)); + command.type = BOARD_DEMO_CMD_MQTT_START; + return board_demo_mqtt_command(&command); +#else + return -RT_ENOSYS; +#endif +} + +int board_demo_request_mqtt_stop(void) +{ +#if defined(BSP_USING_GD32VW553_MQTT_DEMO) && defined(PKG_USING_KAWAII_MQTT) + struct board_demo_command command; + + rt_memset(&command, 0, sizeof(command)); + command.type = BOARD_DEMO_CMD_MQTT_STOP; + return board_demo_mqtt_command(&command); +#else + return -RT_ENOSYS; +#endif +} + +int board_demo_request_mqtt_publish(const char *topic, const char *payload) +{ +#if defined(BSP_USING_GD32VW553_MQTT_DEMO) && defined(PKG_USING_KAWAII_MQTT) + struct board_demo_command command; + + if ((topic == RT_NULL) || (payload == RT_NULL) || + (topic[0] == '\0') || (payload[0] == '\0')) + { + return -RT_EINVAL; + } + + rt_memset(&command, 0, sizeof(command)); + command.type = BOARD_DEMO_CMD_MQTT_PUBLISH; + board_demo_strncpy(command.data.mqtt.topic, topic, + sizeof(command.data.mqtt.topic)); + board_demo_strncpy(command.data.mqtt.payload, payload, + sizeof(command.data.mqtt.payload)); + return board_demo_mqtt_command(&command); +#else + RT_UNUSED(topic); + RT_UNUSED(payload); + return -RT_ENOSYS; +#endif +} + +int board_demo_request_pwm(rt_uint32_t frequency, + rt_bool_t pwm2_enabled, rt_uint8_t pwm2_duty, + rt_bool_t pwm30_enabled, rt_uint8_t pwm30_duty) +{ + struct board_demo_command command; + + if ((frequency == 0U) || (pwm2_duty > 100U) || (pwm30_duty > 100U)) + { + return -RT_EINVAL; + } + + rt_memset(&command, 0, sizeof(command)); + command.type = BOARD_DEMO_CMD_PWM_APPLY; + command.data.pwm.frequency = frequency; + command.data.pwm.pwm2_enabled = pwm2_enabled; + command.data.pwm.pwm2_duty = pwm2_duty; + command.data.pwm.pwm30_enabled = pwm30_enabled; + command.data.pwm.pwm30_duty = pwm30_duty; + return board_demo_send_command(&command); +} + +int board_demo_request_spi_loopback(rt_uint32_t frequency, rt_uint16_t length) +{ +#if defined(RT_USING_SPI) && defined(BSP_USING_SPI3) + struct board_demo_command command; + + if ((frequency == 0U) || (length == 0U) || + (length > BOARD_DEMO_SPI_BUFFER_MAX)) + { + return -RT_EINVAL; + } + + rt_memset(&command, 0, sizeof(command)); + command.type = BOARD_DEMO_CMD_SPI_LOOPBACK; + command.data.spi.frequency = frequency; + command.data.spi.length = length; + return board_demo_queue_bus_command(&command); +#else + RT_UNUSED(frequency); + RT_UNUSED(length); + return -RT_ENOSYS; +#endif +} + +int board_demo_request_i2c_scan(const char *bus_name) +{ +#ifdef RT_USING_I2C + struct board_demo_command command; + + if ((bus_name == RT_NULL) || (rt_strcmp(bus_name, BOARD_DEMO_I2C_BUS_NAME) != 0)) + { + return -RT_EINVAL; + } + + rt_memset(&command, 0, sizeof(command)); + command.type = BOARD_DEMO_CMD_I2C_SCAN; + board_demo_strncpy(command.data.i2c.bus_name, bus_name, + sizeof(command.data.i2c.bus_name)); + return board_demo_queue_bus_command(&command); +#else + RT_UNUSED(bus_name); + return -RT_ENOSYS; +#endif +} + +#endif /* PKG_USING_LVGL && BSP_USING_LVGL_BOARD_DEMO */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/applications/board_demo_backend.h b/bsp/gd32/arm/gd32h77d-ginopi/applications/board_demo_backend.h new file mode 100644 index 000000000000..e7dbef2fba64 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/applications/board_demo_backend.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2006-2026 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef BOARD_DEMO_BACKEND_H +#define BOARD_DEMO_BACKEND_H + +#include + +#define BOARD_DEMO_FS_PATH_MAX 128 +#define BOARD_DEMO_FS_NAME_MAX BOARD_DEMO_FS_PATH_MAX +#define BOARD_DEMO_FS_ENTRY_MAX 32 +#define BOARD_DEMO_FILE_PREVIEW_MAX 1536 +#define BOARD_DEMO_CAN_LOG_MAX 16 +#define BOARD_DEMO_MQTT_TOPIC_MAX 64 +#define BOARD_DEMO_MQTT_PAYLOAD_MAX 256 +#define BOARD_DEMO_STATUS_MAX 80 +#define BOARD_DEMO_I2C_ADDRESS_COUNT 128 +#define BOARD_DEMO_I2C_BUS_NAME "hwi2c1" +#define BOARD_DEMO_BUS_LOG_MAX 3072 + +enum board_demo_i2c_address_state +{ + BOARD_DEMO_I2C_SKIPPED = 0, + BOARD_DEMO_I2C_PENDING, + BOARD_DEMO_I2C_ACK, + BOARD_DEMO_I2C_NACK, + BOARD_DEMO_I2C_ERROR, +}; + +enum board_demo_storage_id +{ + BOARD_DEMO_STORAGE_FLASH = 0, + BOARD_DEMO_STORAGE_SD, + BOARD_DEMO_STORAGE_USB, + BOARD_DEMO_STORAGE_COUNT, +}; + +extern const char *const board_demo_storage_roots[BOARD_DEMO_STORAGE_COUNT]; + +struct board_demo_fs_entry +{ + char name[BOARD_DEMO_FS_NAME_MAX]; + rt_uint32_t size; + rt_bool_t is_directory; +}; + +struct board_demo_can_frame +{ + rt_uint32_t id; + rt_uint32_t tick; + rt_uint8_t data[8]; + rt_uint8_t length; + rt_bool_t extended; + rt_bool_t transmitted; +}; + +struct board_demo_snapshot +{ + rt_uint32_t fs_sequence; + char fs_path[BOARD_DEMO_FS_PATH_MAX]; + char fs_status[BOARD_DEMO_STATUS_MAX]; + struct board_demo_fs_entry fs_entries[BOARD_DEMO_FS_ENTRY_MAX]; + rt_uint8_t fs_entry_count; + + rt_uint32_t file_sequence; + char file_name[BOARD_DEMO_FS_PATH_MAX]; + char file_preview[BOARD_DEMO_FILE_PREVIEW_MAX]; + + rt_uint32_t can_sequence; + char can_status[BOARD_DEMO_STATUS_MAX]; + struct board_demo_can_frame can_frames[BOARD_DEMO_CAN_LOG_MAX]; + rt_uint8_t can_frame_count; + rt_uint32_t can_baud_rate; + rt_uint32_t can_rx_count; + rt_uint32_t can_tx_count; + rt_uint32_t can_error_count; + rt_bool_t can_ready; + + rt_uint32_t mqtt_sequence; + char mqtt_status[BOARD_DEMO_STATUS_MAX]; + char mqtt_rx_topic[BOARD_DEMO_MQTT_TOPIC_MAX]; + char mqtt_rx_payload[BOARD_DEMO_MQTT_PAYLOAD_MAX]; + char mqtt_tx_topic[BOARD_DEMO_MQTT_TOPIC_MAX]; + char mqtt_tx_payload[BOARD_DEMO_MQTT_PAYLOAD_MAX]; + rt_bool_t mqtt_running; + rt_bool_t mqtt_connected; + + rt_uint32_t pwm_sequence; + char pwm_status[BOARD_DEMO_STATUS_MAX]; + rt_uint32_t pwm_frequency; + rt_uint8_t pwm2_duty; + rt_uint8_t pwm30_duty; + rt_bool_t pwm2_enabled; + rt_bool_t pwm30_enabled; + + rt_uint32_t spi_sequence; + char spi_status[BOARD_DEMO_STATUS_MAX]; + char spi_log[BOARD_DEMO_BUS_LOG_MAX]; + rt_uint32_t spi_frequency; + rt_uint16_t spi_length; + rt_uint16_t spi_mismatch_index; + rt_bool_t spi_passed; + rt_bool_t spi_busy; + + rt_uint32_t i2c_sequence; + char i2c_status[BOARD_DEMO_STATUS_MAX]; + char i2c_bus_name[RT_NAME_MAX]; + rt_uint8_t i2c_addresses[BOARD_DEMO_I2C_ADDRESS_COUNT]; + rt_err_t i2c_result_code; + char i2c_log[BOARD_DEMO_BUS_LOG_MAX]; + rt_uint8_t i2c_device_count; + rt_bool_t i2c_busy; +}; + +int board_demo_backend_init(void); +int board_demo_backend_get_snapshot(struct board_demo_snapshot *snapshot); + +int board_demo_fs_source(const char *path); +rt_uint8_t board_demo_storage_mounts(void); +int board_demo_request_fs_list(const char *path); +int board_demo_request_file_preview(const char *path); +int board_demo_request_can_baud(rt_uint32_t baud_rate); +int board_demo_request_can_send(rt_uint32_t id, rt_bool_t extended, + const rt_uint8_t *data, rt_uint8_t length); +int board_demo_request_mqtt_start(void); +int board_demo_request_mqtt_stop(void); +int board_demo_request_mqtt_publish(const char *topic, const char *payload); +int board_demo_request_pwm(rt_uint32_t frequency, + rt_bool_t pwm2_enabled, rt_uint8_t pwm2_duty, + rt_bool_t pwm30_enabled, rt_uint8_t pwm30_duty); +int board_demo_request_spi_loopback(rt_uint32_t frequency, rt_uint16_t length); +int board_demo_request_i2c_scan(const char *bus_name); + +#endif /* BOARD_DEMO_BACKEND_H */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/applications/filesystem_test.c b/bsp/gd32/arm/gd32h77d-ginopi/applications/filesystem_test.c new file mode 100644 index 000000000000..00642b62b04c --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/applications/filesystem_test.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-02-10 Bernard first version + * 2020-04-12 Jianjia Ma add msh cmd + */ +#include +#include +#include +#include +#include +#include +#include + +void writespeed(const char* filename, int total_length, int block_size) +{ + int fd, index, length; + char *buff_ptr; + rt_tick_t tick; + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0); + if (fd < 0) + { + rt_kprintf("open file:%s failed\n", filename); + return; + } + + buff_ptr = rt_malloc(block_size); + if (buff_ptr == RT_NULL) + { + rt_kprintf("no memory\n"); + close(fd); + return; + } + + /* prepare write data */ + for (index = 0; index < block_size; index++) + { + buff_ptr[index] = index; + } + index = 0; + + /* get the beginning tick */ + tick = rt_tick_get(); + while (index < total_length / block_size) + { + length = write(fd, buff_ptr, block_size); + if (length != block_size) + { + rt_kprintf("write failed\n"); + break; + } + + index ++; + } + tick = rt_tick_get() - tick; + + /* close file and release memory */ + close(fd); + rt_free(buff_ptr); + + /* calculate write speed */ + rt_kprintf("File write speed: %d byte/s\n", total_length / tick * RT_TICK_PER_SECOND); +} + +#ifdef RT_USING_FINSH +#include +FINSH_FUNCTION_EXPORT(writespeed, perform file write test); + +static void cmd_writespeed(int argc, char *argv[]) +{ + char* filename; + int length; + int block_size; + + if(argc == 4) + { + filename = argv[1]; + length = atoi(argv[2]); + block_size = atoi(argv[3]); + } + else if(argc == 2) + { + filename = argv[1]; + block_size = 512; + length = 1024*1024; + } + else + { + rt_kprintf("Usage:\nwritespeed [file_path] [length] [block_size]\n"); + rt_kprintf("writespeed [file_path] with default length 1MB and block size 512\n"); + return; + } + writespeed(filename, length, block_size); +} +MSH_CMD_EXPORT_ALIAS(cmd_writespeed, writespeed, test file system write speed); +#endif /* RT_USING_FINSH */ + + +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-02-10 Bernard first version + * 2020-04-12 Jianjia Ma add msh cmd + */ + +#include +#include +#include +#include +#include +#include + +void readspeed(const char* filename, int block_size) +{ + int fd; + char *buff_ptr; + rt_size_t total_length; + rt_tick_t tick; + + fd = open(filename, 0, O_RDONLY); + if (fd < 0) + { + rt_kprintf("open file:%s failed\n", filename); + return; + } + + buff_ptr = rt_malloc(block_size); + if (buff_ptr == RT_NULL) + { + rt_kprintf("no memory\n"); + close(fd); + return; + } + + tick = rt_tick_get(); + total_length = 0; + while (1) + { + int length; + length = read(fd, buff_ptr, block_size); + + if (length <= 0) break; + total_length += length; + } + tick = rt_tick_get() - tick; + + /* close file and release memory */ + close(fd); + rt_free(buff_ptr); + + /* calculate read speed */ + rt_kprintf("File read speed: %d byte/s\n", total_length /tick * RT_TICK_PER_SECOND); +} + +#ifdef RT_USING_FINSH +#include +FINSH_FUNCTION_EXPORT(readspeed, perform file read test); + +static void cmd_readspeed(int argc, char *argv[]) +{ + char* filename; + int block_size; + + if(argc == 3) + { + filename = argv[1]; + block_size = atoi(argv[2]); + } + else if(argc == 2) + { + filename = argv[1]; + block_size = 512; + } + else + { + rt_kprintf("Usage:\nreadspeed [file_path] [block_size]\n"); + rt_kprintf("readspeed [file_path] with default block size 512\n"); + return; + } + readspeed(filename, block_size); +} +MSH_CMD_EXPORT_ALIAS(cmd_readspeed, readspeed, test file system read speed); +#endif /* RT_USING_FINSH */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/applications/gd32vw553_mqtt_demo.c b/bsp/gd32/arm/gd32h77d-ginopi/applications/gd32vw553_mqtt_demo.c new file mode 100644 index 000000000000..052a5c0183f3 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/applications/gd32vw553_mqtt_demo.c @@ -0,0 +1,611 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-09-03 CYFS integrate synchronous MQTT controls over wifi0 with clean shutdown + */ +#include "rtconfig.h" +#if defined(BSP_USING_GD32VW553_MQTT_DEMO) && defined(PKG_USING_KAWAII_MQTT) + +#include +#include +#include +#include + +#include "gd32vw553_mqtt_demo.h" + +#ifndef GD32VW553_SAMPLE_DEVICE_NAME +#define GD32VW553_SAMPLE_DEVICE_NAME "wifi0" +#endif + +#define GD32VW553_MQTT_ARG_LEN 64 +#define GD32VW553_MQTT_TOPIC_LEN 64 +#define GD32VW553_MQTT_MSG_LEN 256 +#define GD32VW553_MQTT_STACK_SIZE 4096 +#define GD32VW553_MQTT_PRIORITY 14 +#define GD32VW553_MQTT_WIFI_WAIT_MS 45000 +#define GD32VW553_MQTT_WIFI_LOG_MS 5000 +#define GD32VW553_MQTT_CMD_TIMEOUT_MS 20000 +#define GD32VW553_MQTT_STOP_TIMEOUT_MS 30000 + +#define GD32VW553_MQTT_STR_HELPER(x) #x +#define GD32VW553_MQTT_STR(x) GD32VW553_MQTT_STR_HELPER(x) + +#ifndef BSP_GD32VW553_MQTT_HOST +#define BSP_GD32VW553_MQTT_HOST "broker.emqx.io" +#endif + +#ifndef BSP_GD32VW553_MQTT_PORT +#define BSP_GD32VW553_MQTT_PORT 1883 +#endif + +#ifndef BSP_GD32VW553_MQTT_SUB_TOPIC +#define BSP_GD32VW553_MQTT_SUB_TOPIC "rtt/gd32vw553/sub" +#endif + +#ifndef BSP_GD32VW553_MQTT_PUB_TOPIC +#define BSP_GD32VW553_MQTT_PUB_TOPIC "rtt/gd32vw553/pub" +#endif + +#ifndef BSP_GD32VW553_MQTT_MESSAGE +#define BSP_GD32VW553_MQTT_MESSAGE "hello from GD32VW553" +#endif + +#ifndef BSP_GD32VW553_MQTT_CLIENT_ID +#define BSP_GD32VW553_MQTT_CLIENT_ID "gd32vw553" +#endif + +#ifndef BSP_GD32VW553_MQTT_USERNAME +#define BSP_GD32VW553_MQTT_USERNAME "" +#endif + +#ifndef BSP_GD32VW553_MQTT_PASSWORD +#define BSP_GD32VW553_MQTT_PASSWORD "" +#endif + +struct gd32vw553_mqtt_arg +{ + char host[GD32VW553_MQTT_ARG_LEN]; + char port[8]; + char sub_topic[GD32VW553_MQTT_TOPIC_LEN]; + char pub_topic[GD32VW553_MQTT_TOPIC_LEN]; + char message[GD32VW553_MQTT_MSG_LEN]; + char client_id[GD32VW553_MQTT_ARG_LEN]; + char username[GD32VW553_MQTT_ARG_LEN]; + char mqtt_password[GD32VW553_MQTT_ARG_LEN]; +}; + +static mqtt_client_t *gd32vw553_mqtt_client = RT_NULL; +static rt_bool_t gd32vw553_mqtt_running = RT_FALSE; +static rt_mutex_t gd32vw553_mqtt_lock = RT_NULL; +static gd32vw553_mqtt_receive_callback_t gd32vw553_mqtt_receive_callback; +static gd32vw553_mqtt_status_callback_t gd32vw553_mqtt_status_callback; + +static void gd32vw553_mqtt_report_status(const char *status, int result) +{ + gd32vw553_mqtt_status_callback_t callback; + + if (result == RT_EOK) + { + rt_kprintf("[mqtt] %s\n", status); + } + else + { + rt_kprintf("[mqtt] %s: %d\n", status, result); + } + + rt_enter_critical(); + callback = gd32vw553_mqtt_status_callback; + rt_exit_critical(); + if (callback != RT_NULL) + { + callback(status, result); + } +} + +static rt_bool_t gd32vw553_mqtt_running_get(void) +{ + rt_bool_t running; + + if (gd32vw553_mqtt_lock == RT_NULL) + { + return RT_FALSE; + } + + rt_mutex_take(gd32vw553_mqtt_lock, RT_WAITING_FOREVER); + running = gd32vw553_mqtt_running; + rt_mutex_release(gd32vw553_mqtt_lock); + + return running; +} + +static void gd32vw553_mqtt_strncpy(char *dst, const char *src, rt_size_t dst_size) +{ + if (dst_size == 0) + { + return; + } + + rt_strncpy(dst, src, dst_size - 1); + dst[dst_size - 1] = '\0'; +} + +static void gd32vw553_mqtt_sub_callback(void *client, message_data_t *msg) +{ + RT_UNUSED(client); + + if (msg == RT_NULL || msg->message == RT_NULL) + { + return; + } + + rt_kprintf("mqtt recv topic: %s\n", msg->topic_name); + rt_kprintf("mqtt recv payload: %.*s\n", + (int)msg->message->payloadlen, (char *)msg->message->payload); + + if (gd32vw553_mqtt_receive_callback != RT_NULL) + { + gd32vw553_mqtt_receive_callback(msg->topic_name, + msg->message->payload, + msg->message->payloadlen); + } +} + +static int gd32vw553_mqtt_publish_text(mqtt_client_t *client, const char *topic, const char *payload) +{ + mqtt_message_t msg; + + rt_memset(&msg, 0, sizeof(msg)); + msg.qos = QOS0; + msg.payload = (void *)payload; + msg.payloadlen = rt_strlen(payload); + + return mqtt_publish(client, topic, &msg); +} + +static rt_bool_t gd32vw553_mqtt_netdev_is_ready(struct at_device *device) +{ + struct netdev *netdev; + + if (device == RT_NULL || device->netdev == RT_NULL) + { + return RT_FALSE; + } + + netdev = device->netdev; + return (netdev_is_up(netdev) && netdev_is_link_up(netdev) && + !ip_addr_isany(&netdev->ip_addr)) ? RT_TRUE : RT_FALSE; +} + +static rt_bool_t gd32vw553_mqtt_wait_netdev_ready(struct at_device *device) +{ + int waited; + + for (waited = 0; waited < GD32VW553_MQTT_WIFI_WAIT_MS; waited += 500) + { + if (gd32vw553_mqtt_netdev_is_ready(device)) + { + return RT_TRUE; + } + + if ((waited % GD32VW553_MQTT_WIFI_LOG_MS) == 0) + { + struct netdev *netdev = device->netdev; + + rt_kprintf("[mqtt] %s state: init=%d up=%d link=%d ip=%s\n", + GD32VW553_SAMPLE_DEVICE_NAME, + device->is_init ? 1 : 0, + (netdev != RT_NULL && netdev_is_up(netdev)) ? 1 : 0, + (netdev != RT_NULL && netdev_is_link_up(netdev)) ? 1 : 0, + (netdev != RT_NULL && !ip_addr_isany(&netdev->ip_addr)) ? + inet_ntoa(netdev->ip_addr) : "0.0.0.0"); + } + + rt_thread_mdelay(500); + } + + return RT_FALSE; +} + +static int gd32vw553_mqtt_prepare_wifi(struct at_device **device_out) +{ + struct at_device *device; + + *device_out = RT_NULL; + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_DEVICE, GD32VW553_SAMPLE_DEVICE_NAME); + if (device == RT_NULL) + { + return -RT_ENOSYS; + } + *device_out = device; + + if (gd32vw553_mqtt_wait_netdev_ready(device) == RT_FALSE) + { + return -RT_ETIMEOUT; + } + + return RT_EOK; +} + +static const char *gd32vw553_mqtt_wifi_error(struct at_device *device) +{ + if (device == RT_NULL || device->netdev == RT_NULL) + { + return "GD32VW553 network interface is unavailable"; + } + if (device->is_init == RT_FALSE) + { + return "GD32VW553 AT initialization failed or is incomplete"; + } + if (!netdev_is_up(device->netdev)) + { + return "GD32VW553 network interface is down"; + } + if (!netdev_is_link_up(device->netdev)) + { + return "GD32VW553 Wi-Fi AP is not connected"; + } + if (ip_addr_isany(&device->netdev->ip_addr)) + { + return "GD32VW553 Wi-Fi has no IPv4 address"; + } + + return "GD32VW553 Wi-Fi is not ready"; +} + +static const char *gd32vw553_mqtt_connect_error(int result) +{ + switch (result) + { + case KAWAII_MQTT_SOCKET_UNKNOWN_HOST_ERROR: + return "MQTT broker DNS lookup failed"; + case KAWAII_MQTT_SOCKET_FAILED_ERROR: + return "MQTT socket creation failed"; + case KAWAII_MQTT_CONNECT_FAILED_ERROR: + return "MQTT broker TCP or protocol connection failed"; + default: + return "MQTT broker connection failed"; + } +} + +static void gd32vw553_mqtt_thread_entry(void *parameter) +{ + struct gd32vw553_mqtt_arg *arg = (struct gd32vw553_mqtt_arg *)parameter; + struct at_device *device = RT_NULL; + struct netdev *saved_default_netdev = RT_NULL; + mqtt_client_t *client; + int result; + rt_bool_t failure_reported = RT_FALSE; + rt_bool_t default_netdev_changed = RT_FALSE; + + gd32vw553_mqtt_report_status("Waiting for GD32VW553 Wi-Fi", RT_EOK); + result = gd32vw553_mqtt_prepare_wifi(&device); + if (result != RT_EOK) + { + gd32vw553_mqtt_report_status( + result == -RT_ENOSYS ? "GD32VW553 AT device is unavailable" : + gd32vw553_mqtt_wifi_error(device), + result); + failure_reported = RT_TRUE; + goto __exit; + } + + saved_default_netdev = netdev_default; + if (device->netdev != netdev_default) + { + netdev_set_default(device->netdev); + default_netdev_changed = RT_TRUE; + } + rt_kprintf("[mqtt] SAL netdev: %s\n", device->netdev->name); + + mqtt_log_init(); + + client = mqtt_lease(); + if (client == RT_NULL) + { + gd32vw553_mqtt_report_status("MQTT client allocation failed", -RT_ENOMEM); + failure_reported = RT_TRUE; + goto __exit; + } + + mqtt_set_host(client, arg->host); + mqtt_set_port(client, arg->port); + mqtt_set_client_id(client, arg->client_id); + mqtt_set_clean_session(client, 1); + mqtt_set_cmd_timeout(client, GD32VW553_MQTT_CMD_TIMEOUT_MS); + + if (arg->username[0] != '\0') + { + mqtt_set_user_name(client, arg->username); + } + if (arg->mqtt_password[0] != '\0') + { + mqtt_set_password(client, arg->mqtt_password); + } + + gd32vw553_mqtt_report_status("Connecting to MQTT broker", RT_EOK); + result = mqtt_connect(client); + if (result != KAWAII_MQTT_SUCCESS_ERROR) + { + gd32vw553_mqtt_report_status(gd32vw553_mqtt_connect_error(result), result); + failure_reported = RT_TRUE; + client->mqtt_client_state = CLIENT_STATE_INVALID; + mqtt_release(client); + goto __exit; + } + + result = mqtt_subscribe(client, arg->sub_topic, QOS0, gd32vw553_mqtt_sub_callback); + if (result != KAWAII_MQTT_SUCCESS_ERROR) + { + gd32vw553_mqtt_report_status("MQTT subscription failed", result); + failure_reported = RT_TRUE; + mqtt_disconnect(client); + mqtt_release(client); + goto __exit; + } + + rt_mutex_take(gd32vw553_mqtt_lock, RT_WAITING_FOREVER); + gd32vw553_mqtt_client = client; + rt_mutex_release(gd32vw553_mqtt_lock); + + rt_kprintf("GD32VW553 MQTT connected to %s:%s\n", arg->host, arg->port); + rt_kprintf("mqtt subscribed: %s\n", arg->sub_topic); + gd32vw553_mqtt_report_status("MQTT connected and subscribed", RT_EOK); + + while (gd32vw553_mqtt_running_get()) + { + rt_thread_mdelay(100); + } + + rt_mutex_take(gd32vw553_mqtt_lock, RT_WAITING_FOREVER); + gd32vw553_mqtt_client = RT_NULL; + rt_mutex_release(gd32vw553_mqtt_lock); + + mqtt_set_cmd_timeout(client, GD32VW553_MQTT_STOP_TIMEOUT_MS); + mqtt_disconnect(client); + mqtt_release(client); + +__exit: + if (default_netdev_changed && saved_default_netdev != RT_NULL && + netdev_default == device->netdev) + { + netdev_set_default(saved_default_netdev); + } + + rt_mutex_take(gd32vw553_mqtt_lock, RT_WAITING_FOREVER); + gd32vw553_mqtt_running = RT_FALSE; + rt_mutex_release(gd32vw553_mqtt_lock); + + rt_free(arg); + if (!failure_reported) + { + gd32vw553_mqtt_report_status("MQTT stopped", RT_EOK); + } +} + +int gd32vw553_mqtt_demo_start(void) +{ + rt_thread_t tid; + struct gd32vw553_mqtt_arg *arg; + const char *host = BSP_GD32VW553_MQTT_HOST; + const char *port_string = GD32VW553_MQTT_STR(BSP_GD32VW553_MQTT_PORT); + + if (host[0] == '\0' || + BSP_GD32VW553_MQTT_SUB_TOPIC[0] == '\0' || + BSP_GD32VW553_MQTT_PUB_TOPIC[0] == '\0' || + BSP_GD32VW553_MQTT_CLIENT_ID[0] == '\0') + { + rt_kprintf("please configure GD32VW553 MQTT demo in menuconfig first\n"); + return -RT_EINVAL; + } + + if (gd32vw553_mqtt_lock == RT_NULL) + { + gd32vw553_mqtt_lock = rt_mutex_create("vw553mq", RT_IPC_FLAG_PRIO); + if (gd32vw553_mqtt_lock == RT_NULL) + { + return -RT_ENOMEM; + } + } + + rt_mutex_take(gd32vw553_mqtt_lock, RT_WAITING_FOREVER); + if (gd32vw553_mqtt_client != RT_NULL || gd32vw553_mqtt_running == RT_TRUE) + { + rt_mutex_release(gd32vw553_mqtt_lock); + rt_kprintf("GD32VW553 MQTT demo is already running\n"); + return -RT_EBUSY; + } + gd32vw553_mqtt_running = RT_TRUE; + rt_mutex_release(gd32vw553_mqtt_lock); + + arg = (struct gd32vw553_mqtt_arg *)rt_calloc(1, sizeof(struct gd32vw553_mqtt_arg)); + if (arg == RT_NULL) + { + rt_mutex_take(gd32vw553_mqtt_lock, RT_WAITING_FOREVER); + gd32vw553_mqtt_running = RT_FALSE; + rt_mutex_release(gd32vw553_mqtt_lock); + return -RT_ENOMEM; + } + + gd32vw553_mqtt_strncpy(arg->host, host, sizeof(arg->host)); + gd32vw553_mqtt_strncpy(arg->port, port_string, sizeof(arg->port)); + gd32vw553_mqtt_strncpy(arg->sub_topic, BSP_GD32VW553_MQTT_SUB_TOPIC, sizeof(arg->sub_topic)); + gd32vw553_mqtt_strncpy(arg->pub_topic, BSP_GD32VW553_MQTT_PUB_TOPIC, sizeof(arg->pub_topic)); + gd32vw553_mqtt_strncpy(arg->message, BSP_GD32VW553_MQTT_MESSAGE, sizeof(arg->message)); + gd32vw553_mqtt_strncpy(arg->client_id, BSP_GD32VW553_MQTT_CLIENT_ID, sizeof(arg->client_id)); + gd32vw553_mqtt_strncpy(arg->username, BSP_GD32VW553_MQTT_USERNAME, sizeof(arg->username)); + gd32vw553_mqtt_strncpy(arg->mqtt_password, BSP_GD32VW553_MQTT_PASSWORD, sizeof(arg->mqtt_password)); + + tid = rt_thread_create("vw553mqtt", gd32vw553_mqtt_thread_entry, arg, + GD32VW553_MQTT_STACK_SIZE, GD32VW553_MQTT_PRIORITY, 10); + if (tid == RT_NULL) + { + rt_mutex_take(gd32vw553_mqtt_lock, RT_WAITING_FOREVER); + gd32vw553_mqtt_running = RT_FALSE; + rt_mutex_release(gd32vw553_mqtt_lock); + rt_free(arg); + return -RT_ERROR; + } + + if (rt_thread_startup(tid) != RT_EOK) + { + rt_thread_delete(tid); + rt_mutex_take(gd32vw553_mqtt_lock, RT_WAITING_FOREVER); + gd32vw553_mqtt_running = RT_FALSE; + rt_mutex_release(gd32vw553_mqtt_lock); + rt_free(arg); + return -RT_ERROR; + } + rt_kprintf("[mqtt] MQTT worker started\n"); + return RT_EOK; +} + +static int gd32vw553_mqtt_start(int argc, char **argv) +{ + RT_UNUSED(argv); + + if (argc != 1) + { + rt_kprintf("usage: gd32vw553_mqtt_start\n"); + return -RT_EINVAL; + } + + return gd32vw553_mqtt_demo_start(); +} +MSH_CMD_EXPORT(gd32vw553_mqtt_start, start GD32VW553 MQTT demo with kawaii-mqtt); + +int gd32vw553_mqtt_demo_publish(const char *topic, const char *payload) +{ + mqtt_client_t *client; + int result; + + if ((topic == RT_NULL) || (payload == RT_NULL) || + (topic[0] == '\0') || (payload[0] == '\0')) + { + return -RT_EINVAL; + } + + if ((rt_strlen(topic) >= GD32VW553_MQTT_TOPIC_LEN) || + (rt_strlen(payload) >= GD32VW553_MQTT_MSG_LEN)) + { + rt_kprintf("MQTT topic or payload is too long\n"); + return -RT_EINVAL; + } + + if (gd32vw553_mqtt_lock == RT_NULL) + { + rt_kprintf("GD32VW553 MQTT demo is not running\n"); + return -RT_ERROR; + } + + rt_mutex_take(gd32vw553_mqtt_lock, RT_WAITING_FOREVER); + if (gd32vw553_mqtt_running == RT_FALSE) + { + rt_mutex_release(gd32vw553_mqtt_lock); + rt_kprintf("GD32VW553 MQTT demo is not running\n"); + return -RT_ERROR; + } + client = gd32vw553_mqtt_client; + if (client == RT_NULL) + { + rt_mutex_release(gd32vw553_mqtt_lock); + rt_kprintf("GD32VW553 MQTT demo is not connected\n"); + return -RT_ERROR; + } + + rt_kprintf("[mqtt] publishing: %s (%u bytes)\n", + topic, (unsigned int)rt_strlen(payload)); + gd32vw553_mqtt_report_status("Sending MQTT message", RT_EOK); + result = gd32vw553_mqtt_publish_text(client, topic, payload); + rt_mutex_release(gd32vw553_mqtt_lock); + + if (result == KAWAII_MQTT_SUCCESS_ERROR) + { + rt_kprintf("mqtt published: %s -> %s\n", topic, payload); + gd32vw553_mqtt_report_status("MQTT message published", RT_EOK); + return RT_EOK; + } + + rt_kprintf("[mqtt] publish failed: topic=%s result=%d\n", topic, result); + gd32vw553_mqtt_report_status("MQTT publish failed", result); + + return result; +} + +static int gd32vw553_mqtt_pub(int argc, char **argv) +{ + if (argc < 3) + { + rt_kprintf("usage: gd32vw553_mqtt_pub \n"); + return -RT_EINVAL; + } + + return gd32vw553_mqtt_demo_publish(argv[1], argv[2]); +} +MSH_CMD_EXPORT(gd32vw553_mqtt_pub, publish through GD32VW553 MQTT demo); + +int gd32vw553_mqtt_demo_stop(void) +{ + if (gd32vw553_mqtt_lock == RT_NULL) + { + rt_kprintf("GD32VW553 MQTT demo is not running\n"); + return -RT_ERROR; + } + + rt_mutex_take(gd32vw553_mqtt_lock, RT_WAITING_FOREVER); + gd32vw553_mqtt_running = RT_FALSE; + rt_mutex_release(gd32vw553_mqtt_lock); + + rt_kprintf("GD32VW553 MQTT demo stopping\n"); + return RT_EOK; +} + +static int gd32vw553_mqtt_stop(int argc, char **argv) +{ + RT_UNUSED(argc); + RT_UNUSED(argv); + + return gd32vw553_mqtt_demo_stop(); +} +MSH_CMD_EXPORT(gd32vw553_mqtt_stop, stop GD32VW553 MQTT demo); + +rt_bool_t gd32vw553_mqtt_demo_is_running(void) +{ + return gd32vw553_mqtt_running_get(); +} + +rt_bool_t gd32vw553_mqtt_demo_is_connected(void) +{ + rt_bool_t connected; + + if (gd32vw553_mqtt_lock == RT_NULL) + { + return RT_FALSE; + } + + rt_mutex_take(gd32vw553_mqtt_lock, RT_WAITING_FOREVER); + connected = (gd32vw553_mqtt_client != RT_NULL) ? RT_TRUE : RT_FALSE; + rt_mutex_release(gd32vw553_mqtt_lock); + return connected; +} + +void gd32vw553_mqtt_demo_set_receive_callback( + gd32vw553_mqtt_receive_callback_t callback) +{ + rt_enter_critical(); + gd32vw553_mqtt_receive_callback = callback; + rt_exit_critical(); +} + +void gd32vw553_mqtt_demo_set_status_callback( + gd32vw553_mqtt_status_callback_t callback) +{ + rt_enter_critical(); + gd32vw553_mqtt_status_callback = callback; + rt_exit_critical(); +} + +#endif /* BSP_USING_GD32VW553_MQTT_DEMO && PKG_USING_KAWAII_MQTT */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/applications/gd32vw553_mqtt_demo.h b/bsp/gd32/arm/gd32h77d-ginopi/applications/gd32vw553_mqtt_demo.h new file mode 100644 index 000000000000..2e9b4fd4b1b2 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/applications/gd32vw553_mqtt_demo.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006-2026 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef GD32VW553_MQTT_DEMO_H +#define GD32VW553_MQTT_DEMO_H + +#include + +typedef void (*gd32vw553_mqtt_receive_callback_t)(const char *topic, + const void *payload, + rt_size_t payload_length); +typedef void (*gd32vw553_mqtt_status_callback_t)(const char *status, + int result); + +int gd32vw553_mqtt_demo_start(void); +int gd32vw553_mqtt_demo_publish(const char *topic, const char *payload); +int gd32vw553_mqtt_demo_stop(void); +rt_bool_t gd32vw553_mqtt_demo_is_running(void); +rt_bool_t gd32vw553_mqtt_demo_is_connected(void); +void gd32vw553_mqtt_demo_set_receive_callback( + gd32vw553_mqtt_receive_callback_t callback); +void gd32vw553_mqtt_demo_set_status_callback( + gd32vw553_mqtt_status_callback_t callback); + +#endif /* GD32VW553_MQTT_DEMO_H */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/applications/main.c b/bsp/gd32/arm/gd32h77d-ginopi/applications/main.c new file mode 100644 index 000000000000..46762f121199 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/applications/main.c @@ -0,0 +1,68 @@ + /* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-08-20 BruceOu first implementation + * 2023-03-05 yuanzihao change the LED pins + */ + +#include +#include +#include + +/* defined the LED1 pin: PC4 */ +#define LED1_PIN GET_PIN(C, 4) +#define VCOM_DEVICE_NAME "vcom" + + +int main(void) +{ +#ifdef BSP_USING_USB_CDC_EXAMPLE + rt_device_t vcom; + rt_uint32_t cdc_count = 0; + char cdc_message[64]; + int cdc_length; +#endif + + /* set LED1 pin mode to output */ + rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT); + +#ifdef BSP_USING_USB_CDC_EXAMPLE + vcom = rt_device_find(VCOM_DEVICE_NAME); + if (vcom == RT_NULL) + { + rt_kprintf("cannot find %s device\n", VCOM_DEVICE_NAME); + } + else if (rt_device_open(vcom, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX) != RT_EOK) + { + rt_kprintf("cannot open %s device\n", VCOM_DEVICE_NAME); + vcom = RT_NULL; + } +#endif + + while (1) + { + rt_pin_write(LED1_PIN, PIN_HIGH); + rt_thread_mdelay(500); + rt_pin_write(LED1_PIN, PIN_LOW); + rt_thread_mdelay(500); + +#ifdef BSP_USING_USB_CDC_EXAMPLE + if (vcom != RT_NULL) + { + cdc_length = rt_snprintf(cdc_message, sizeof(cdc_message), + "GD32H77D USB CDC alive: %u\r\n", + cdc_count++); + if (cdc_length > 0) + { + rt_device_write(vcom, 0, cdc_message, (rt_size_t)cdc_length); + } + } +#endif + } + + return RT_EOK; +} diff --git a/bsp/gd32/arm/gd32h77d-ginopi/applications/ov7670_preview.c b/bsp/gd32/arm/gd32h77d-ginopi/applications/ov7670_preview.c new file mode 100644 index 000000000000..9b3b1f3311e8 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/applications/ov7670_preview.c @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2006-2026 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-08-06 RTT add OV7670 LVGL preview + * 2026-09-08 CYFS implement asynchronous OV7670 preview with internal AXI SRAM buffers + */ + +#include + +#ifdef BSP_USING_OV7670_PREVIEW + +#include +#include +#include +#include + +#include "drv_ov7670.h" +#include "ov7670_preview.h" + +#ifdef RT_USING_FINSH +#include +#endif + +#define DBG_TAG "app.ov7670" +#define DBG_LVL DBG_INFO +#include + +#define OV7670_PREVIEW_PERIOD_MS 10U +#define OV7670_CONTROL_PERIOD_MS 20U +#define OV7670_PREVIEW_BUFFER_COUNT 2U +#define OV7670_PREVIEW_INVALID_BUFFER (-1) +#define OV7670_CAPTURE_THREAD_STACK 4096U + +#ifndef PKG_LVGL_THREAD_PRIO +#define PKG_LVGL_THREAD_PRIO (RT_THREAD_PRIORITY_MAX * 2U / 3U) +#endif + +#define OV7670_CAPTURE_THREAD_PRIORITY (PKG_LVGL_THREAD_PRIO + 1U) + +/* Static storage cannot fall back to the automatically bound SDRAM heap. */ +rt_align(32) static rt_uint8_t ov7670_preview_buffers[OV7670_PREVIEW_BUFFER_COUNT] + [OV7670_FRAME_SIZE]; +#define OV7670_PREVIEW_BUFFER(index) ov7670_preview_buffers[index] + +enum ov7670_preview_request +{ + OV7670_PREVIEW_REQUEST_NONE = 0, + OV7670_PREVIEW_REQUEST_START, + OV7670_PREVIEW_REQUEST_STOP, +}; + +static const lv_image_dsc_t preview_image_descriptors[OV7670_PREVIEW_BUFFER_COUNT] = +{ + { + .header.magic = LV_IMAGE_HEADER_MAGIC, + .header.cf = LV_COLOR_FORMAT_RGB565, + .header.flags = 0, + .header.w = OV7670_WIDTH, + .header.h = OV7670_HEIGHT, + .header.stride = OV7670_WIDTH * 2U, + .data_size = OV7670_FRAME_SIZE, + .data = OV7670_PREVIEW_BUFFER(0U), + }, + { + .header.magic = LV_IMAGE_HEADER_MAGIC, + .header.cf = LV_COLOR_FORMAT_RGB565, + .header.flags = 0, + .header.w = OV7670_WIDTH, + .header.h = OV7670_HEIGHT, + .header.stride = OV7670_WIDTH * 2U, + .data_size = OV7670_FRAME_SIZE, + .data = OV7670_PREVIEW_BUFFER(1U), + }, +}; + +static rt_device_t preview_camera; +static struct rt_thread preview_capture_thread; +rt_align(RT_ALIGN_SIZE) static rt_uint8_t + preview_capture_stack[OV7670_CAPTURE_THREAD_STACK]; +static struct rt_semaphore preview_capture_semaphore; +static lv_obj_t *preview_previous_screen; +static lv_obj_t *preview_screen; +static lv_obj_t *preview_image; +static lv_timer_t *preview_frame_timer; +static lv_timer_t *preview_control_timer; +static volatile enum ov7670_preview_request preview_request; +static volatile rt_bool_t preview_ready; +static volatile rt_bool_t preview_active; +static volatile rt_bool_t preview_capture_enabled; +static volatile rt_bool_t preview_capture_thread_ready; +static volatile rt_int32_t preview_display_buffer; +static volatile rt_int32_t preview_ready_buffer; +static volatile rt_int32_t preview_capture_buffer; +static volatile rt_uint32_t preview_frame_count; +static volatile rt_uint32_t preview_display_count; +static volatile rt_uint32_t preview_drop_count; +static volatile rt_uint32_t preview_error_count; +static volatile rt_err_t preview_last_result; +static volatile rt_tick_t preview_start_tick; + +static rt_int32_t ov7670_preview_acquire_buffer(void) +{ + rt_int32_t index; + + rt_enter_critical(); + if (!preview_capture_enabled) + { + rt_exit_critical(); + return OV7670_PREVIEW_INVALID_BUFFER; + } + + for (index = 0; index < (rt_int32_t)OV7670_PREVIEW_BUFFER_COUNT; index++) + { + if ((index != preview_display_buffer) && + (index != preview_ready_buffer) && + (index != preview_capture_buffer)) + { + preview_capture_buffer = index; + rt_exit_critical(); + return index; + } + } + rt_exit_critical(); + return OV7670_PREVIEW_INVALID_BUFFER; +} + +static void ov7670_preview_capture_entry(void *parameter) +{ + rt_int32_t buffer_index; + rt_ssize_t size; + rt_err_t result; + rt_uint32_t error_count; + + RT_UNUSED(parameter); + while (1) + { + rt_sem_take(&preview_capture_semaphore, RT_WAITING_FOREVER); + while (preview_capture_enabled) + { + buffer_index = ov7670_preview_acquire_buffer(); + if (buffer_index == OV7670_PREVIEW_INVALID_BUFFER) + { + /* With two buffers, a ready frame must be consumed before + * capture resumes. Never overwrite the displayed or ready frame. */ + rt_thread_mdelay(1U); + continue; + } + + size = rt_device_read(preview_camera, 0, + OV7670_PREVIEW_BUFFER(buffer_index), + OV7670_FRAME_SIZE); + result = size == OV7670_FRAME_SIZE ? RT_EOK : + (size < 0 ? (rt_err_t)size : -RT_EIO); + + rt_enter_critical(); + preview_capture_buffer = OV7670_PREVIEW_INVALID_BUFFER; + if (!preview_capture_enabled) + { + rt_exit_critical(); + continue; + } + if (result == RT_EOK) + { + if (preview_ready_buffer != OV7670_PREVIEW_INVALID_BUFFER) + { + preview_drop_count++; + } + preview_ready_buffer = buffer_index; + preview_frame_count++; + preview_last_result = RT_EOK; + rt_exit_critical(); + continue; + } + + preview_error_count++; + error_count = preview_error_count; + preview_last_result = result; + rt_exit_critical(); + if ((error_count <= 3U) || ((error_count % 30U) == 0U)) + { + LOG_E("frame capture failed: %d", result); + } + } + } +} + +static void ov7670_preview_frame(lv_timer_t *timer) +{ + rt_int32_t buffer_index; + + RT_UNUSED(timer); + /* LVGL timer callbacks run after the previous refresh has finished reading + * its image sources; only then can capture reuse the old display buffer. */ + rt_enter_critical(); + buffer_index = preview_ready_buffer; + if (buffer_index != OV7670_PREVIEW_INVALID_BUFFER) + { + preview_ready_buffer = OV7670_PREVIEW_INVALID_BUFFER; + preview_display_buffer = buffer_index; + preview_display_count++; + } + rt_exit_critical(); + + if ((buffer_index == OV7670_PREVIEW_INVALID_BUFFER) || + (preview_image == RT_NULL)) + { + return; + } + + lv_image_set_src(preview_image, &preview_image_descriptors[buffer_index]); + lv_obj_invalidate(preview_image); +} + +static void ov7670_preview_stop(void) +{ + if (!preview_active) + { + return; + } + + rt_enter_critical(); + preview_capture_enabled = RT_FALSE; + preview_ready_buffer = OV7670_PREVIEW_INVALID_BUFFER; + preview_active = RT_FALSE; + rt_exit_critical(); + + if (preview_frame_timer != RT_NULL) + { + lv_timer_del(preview_frame_timer); + preview_frame_timer = RT_NULL; + } + if ((preview_previous_screen != RT_NULL) && + (lv_screen_active() == preview_screen)) + { + lv_screen_load(preview_previous_screen); + } + if (preview_screen != RT_NULL) + { + lv_obj_delete(preview_screen); + } + + preview_previous_screen = RT_NULL; + preview_screen = RT_NULL; + preview_image = RT_NULL; + LOG_I("preview stopped"); +} + +static void ov7670_preview_back_event(lv_event_t *event) +{ + RT_UNUSED(event); + ov7670_preview_request_stop(); +} + +static void ov7670_preview_start(void) +{ + rt_err_t result; + lv_obj_t *back_button; + lv_obj_t *back_label; + + if (preview_active) + { + return; + } + if (!preview_capture_thread_ready) + { + preview_last_result = -RT_ENOSYS; + LOG_E("capture thread is unavailable"); + return; + } + + if (preview_camera == RT_NULL) + { + preview_camera = rt_device_find(OV7670_DEVICE_NAME); + if (preview_camera == RT_NULL) + { + preview_last_result = -RT_ENOSYS; + LOG_E("device %s not found", OV7670_DEVICE_NAME); + return; + } + + result = rt_device_open(preview_camera, RT_DEVICE_OFLAG_RDONLY); + if (result != RT_EOK) + { + preview_camera = RT_NULL; + preview_last_result = result; + LOG_E("camera open failed: %d", result); + return; + } + } + + preview_previous_screen = lv_screen_active(); + preview_screen = lv_obj_create(RT_NULL); + if (preview_screen == RT_NULL) + { + result = -RT_ENOMEM; + goto _failed; + } + lv_obj_set_scrollable(preview_screen, false); + lv_obj_set_style_bg_color(preview_screen, lv_color_black(), 0); + lv_obj_set_style_bg_opa(preview_screen, LV_OPA_COVER, 0); + + preview_image = lv_image_create(preview_screen); + if (preview_image == RT_NULL) + { + result = -RT_ENOMEM; + goto _failed; + } + rt_memset(OV7670_PREVIEW_BUFFER(preview_display_buffer), 0, + OV7670_FRAME_SIZE); + lv_image_set_src(preview_image, + &preview_image_descriptors[preview_display_buffer]); + lv_image_set_scale(preview_image, 512U); + lv_image_set_antialias(preview_image, false); + lv_obj_center(preview_image); + + back_button = lv_button_create(preview_screen); + if (back_button == RT_NULL) + { + result = -RT_ENOMEM; + goto _failed; + } + lv_obj_set_size(back_button, 96, 44); + lv_obj_align(back_button, LV_ALIGN_TOP_LEFT, 16, 16); + lv_obj_add_event_cb(back_button, ov7670_preview_back_event, + LV_EVENT_CLICKED, RT_NULL); + + back_label = lv_label_create(back_button); + if (back_label == RT_NULL) + { + result = -RT_ENOMEM; + goto _failed; + } + lv_label_set_text(back_label, LV_SYMBOL_LEFT " Back"); + lv_obj_center(back_label); + + preview_frame_timer = lv_timer_create(ov7670_preview_frame, + OV7670_PREVIEW_PERIOD_MS, + RT_NULL); + if (preview_frame_timer == RT_NULL) + { + result = -RT_ENOMEM; + goto _failed; + } + + preview_frame_count = 0U; + preview_display_count = 0U; + preview_drop_count = 0U; + preview_error_count = 0U; + preview_last_result = RT_EOK; + preview_start_tick = rt_tick_get(); + rt_enter_critical(); + preview_ready_buffer = OV7670_PREVIEW_INVALID_BUFFER; + preview_capture_enabled = RT_TRUE; + preview_active = RT_TRUE; + rt_exit_critical(); + lv_screen_load(preview_screen); + lv_timer_ready(preview_frame_timer); + rt_sem_release(&preview_capture_semaphore); + LOG_I("preview started: %ux%u RGB565", OV7670_WIDTH, OV7670_HEIGHT); + return; + +_failed: + if (preview_frame_timer != RT_NULL) + { + lv_timer_del(preview_frame_timer); + preview_frame_timer = RT_NULL; + } + if (preview_screen != RT_NULL) + { + lv_obj_delete(preview_screen); + } + preview_previous_screen = RT_NULL; + preview_screen = RT_NULL; + preview_image = RT_NULL; + preview_last_result = result; + LOG_E("preview start failed: %d", result); +} + +static void ov7670_preview_control(lv_timer_t *timer) +{ + enum ov7670_preview_request request; + + RT_UNUSED(timer); + rt_enter_critical(); + request = preview_request; + preview_request = OV7670_PREVIEW_REQUEST_NONE; + rt_exit_critical(); + + if (request == OV7670_PREVIEW_REQUEST_START) + { + ov7670_preview_start(); + } + else if (request == OV7670_PREVIEW_REQUEST_STOP) + { + ov7670_preview_stop(); + } +} + +void ov7670_preview_init(void) +{ + rt_err_t result; + rt_ubase_t buffers_begin = (rt_ubase_t)ov7670_preview_buffers; + + if (preview_control_timer != RT_NULL) + { + return; + } + + if ((buffers_begin < 0x24000000U) || + (buffers_begin + sizeof(ov7670_preview_buffers) > (rt_ubase_t)GD32_SRAM_END)) + { + preview_last_result = -RT_ENOMEM; + LOG_E("preview buffers must be placed in internal AXI SRAM"); + return; + } + + preview_display_buffer = 0; + preview_ready_buffer = OV7670_PREVIEW_INVALID_BUFFER; + preview_capture_buffer = OV7670_PREVIEW_INVALID_BUFFER; + preview_last_result = RT_EOK; + result = rt_sem_init(&preview_capture_semaphore, "ovprev", 0, + RT_IPC_FLAG_FIFO); + if (result != RT_EOK) + { + preview_last_result = result; + LOG_E("cannot initialize capture semaphore: %d", result); + return; + } + + result = rt_thread_init(&preview_capture_thread, "ovcap", + ov7670_preview_capture_entry, RT_NULL, + preview_capture_stack, + sizeof(preview_capture_stack), + OV7670_CAPTURE_THREAD_PRIORITY, 10U); + if (result != RT_EOK) + { + rt_sem_detach(&preview_capture_semaphore); + preview_last_result = result; + LOG_E("cannot initialize capture thread: %d", result); + return; + } + result = rt_thread_startup(&preview_capture_thread); + if (result != RT_EOK) + { + rt_thread_detach(&preview_capture_thread); + rt_sem_detach(&preview_capture_semaphore); + preview_last_result = result; + LOG_E("cannot start capture thread: %d", result); + return; + } + preview_capture_thread_ready = RT_TRUE; + + preview_control_timer = lv_timer_create(ov7670_preview_control, + OV7670_CONTROL_PERIOD_MS, + RT_NULL); + if (preview_control_timer == RT_NULL) + { + preview_last_result = -RT_ENOMEM; + LOG_E("cannot create preview control timer"); + return; + } + preview_ready = RT_TRUE; +} + +rt_err_t ov7670_preview_request_start(void) +{ + if (!preview_ready) + { + return -RT_ENOSYS; + } + + rt_enter_critical(); + preview_request = OV7670_PREVIEW_REQUEST_START; + rt_exit_critical(); + return RT_EOK; +} + +rt_err_t ov7670_preview_request_stop(void) +{ + if (!preview_ready) + { + return -RT_ENOSYS; + } + + rt_enter_critical(); + preview_request = OV7670_PREVIEW_REQUEST_STOP; + rt_exit_critical(); + return RT_EOK; +} + +void ov7670_preview_get_status(struct ov7670_preview_status *status) +{ + if (status == RT_NULL) + { + return; + } + + rt_enter_critical(); + status->ready = preview_ready; + status->active = preview_active; + status->frame_count = preview_frame_count; + status->display_count = preview_display_count; + status->drop_count = preview_drop_count; + status->error_count = preview_error_count; + status->last_result = preview_last_result; + rt_exit_critical(); +} + +#ifdef RT_USING_FINSH +static const char *ov7670_preview_request_name(enum ov7670_preview_request request) +{ + if (request == OV7670_PREVIEW_REQUEST_START) + { + return "start"; + } + if (request == OV7670_PREVIEW_REQUEST_STOP) + { + return "stop"; + } + return "none"; +} + +static void ov7670_preview(int argc, char **argv) +{ + enum ov7670_preview_request request; + rt_bool_t ready; + rt_bool_t active; + rt_uint32_t frames; + rt_uint32_t displayed; + rt_uint32_t dropped; + rt_uint32_t errors; + rt_uint32_t fps_tenths; + rt_tick_t start_tick; + rt_tick_t elapsed_ticks; + rt_err_t last_result; + + if ((argc == 1) || + ((argc == 2) && (rt_strcmp(argv[1], "start") == 0))) + { + ov7670_preview_request_start(); + rt_kprintf("OV7670 preview start requested\n"); + return; + } + if ((argc == 2) && (rt_strcmp(argv[1], "stop") == 0)) + { + ov7670_preview_request_stop(); + rt_kprintf("OV7670 preview stop requested\n"); + return; + } + if ((argc == 2) && (rt_strcmp(argv[1], "status") == 0)) + { + rt_enter_critical(); + ready = preview_ready; + active = preview_active; + request = preview_request; + frames = preview_frame_count; + displayed = preview_display_count; + dropped = preview_drop_count; + errors = preview_error_count; + start_tick = preview_start_tick; + last_result = preview_last_result; + rt_exit_critical(); + elapsed_ticks = rt_tick_get() - start_tick; + fps_tenths = (active && (elapsed_ticks != 0U)) ? + (rt_uint32_t)(((rt_uint64_t)frames * + RT_TICK_PER_SECOND * 10U) / + elapsed_ticks) : 0U; + rt_kprintf("OV7670 preview ready=%d active=%d request=%s " + "captured=%u displayed=%u dropped=%u errors=%u " + "fps=%u.%u last=%d\n", + ready, active, ov7670_preview_request_name(request), + frames, displayed, dropped, errors, + fps_tenths / 10U, fps_tenths % 10U, last_result); + return; + } + + rt_kprintf("usage: ov7670_preview [start|stop|status]\n"); +} +MSH_CMD_EXPORT(ov7670_preview, start or stop the OV7670 LCD preview); +#endif /* RT_USING_FINSH */ + +#endif /* BSP_USING_OV7670_PREVIEW */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/applications/ov7670_preview.h b/bsp/gd32/arm/gd32h77d-ginopi/applications/ov7670_preview.h new file mode 100644 index 000000000000..24d15c51b038 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/applications/ov7670_preview.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006-2026 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OV7670_PREVIEW_H +#define OV7670_PREVIEW_H + +#include + +struct ov7670_preview_status +{ + rt_bool_t ready; + rt_bool_t active; + rt_uint32_t frame_count; + rt_uint32_t display_count; + rt_uint32_t drop_count; + rt_uint32_t error_count; + rt_err_t last_result; +}; + +void ov7670_preview_init(void); +rt_err_t ov7670_preview_request_start(void); +rt_err_t ov7670_preview_request_stop(void); +void ov7670_preview_get_status(struct ov7670_preview_status *status); + +#endif /* OV7670_PREVIEW_H */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/applications/pwm_dual_test.c b/bsp/gd32/arm/gd32h77d-ginopi/applications/pwm_dual_test.c new file mode 100644 index 000000000000..511ab7b5e986 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/applications/pwm_dual_test.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Dual-channel PWM output test for the GD32H77D GinoPi board. + */ + +#include +#include + +#ifdef BSP_USING_PWM_DUAL_TEST + +#define PWM2_DEVICE_NAME "pwm2" +#define PWM2_DEVICE_CHANNEL 2 +#define PWM30_DEVICE_NAME "pwm30" +#define PWM30_DEVICE_CHANNEL 3 +#define PWM_TEST_PERIOD_NS 500000U +#define PWM_TEST_STEP_NS 5000U +#define PWM_TEST_DELAY_MS 50U +#define PWM_TEST_THREAD_STACK 1024U +#define PWM_TEST_THREAD_PRIORITY 20U +#define PWM_TEST_THREAD_TICK 10U + +static struct rt_device_pwm *pwm2_device; +static struct rt_device_pwm *pwm30_device; +static rt_thread_t pwm_test_thread; +static volatile rt_bool_t pwm_test_stop; + +static void pwm_dual_disable(void) +{ + if (pwm2_device != RT_NULL) + { + rt_pwm_disable(pwm2_device, PWM2_DEVICE_CHANNEL); + } + if (pwm30_device != RT_NULL) + { + rt_pwm_disable(pwm30_device, PWM30_DEVICE_CHANNEL); + } +} + +static void pwm_dual_test_entry(void *parameter) +{ + rt_uint32_t pulse = 0U; + rt_bool_t increasing = RT_TRUE; + + (void)parameter; + + while (pwm_test_stop == RT_FALSE) + { + if (rt_pwm_set(pwm2_device, PWM2_DEVICE_CHANNEL, + PWM_TEST_PERIOD_NS, pulse) != RT_EOK || + rt_pwm_set(pwm30_device, PWM30_DEVICE_CHANNEL, + PWM_TEST_PERIOD_NS, pulse) != RT_EOK) + { + rt_kprintf("set dual PWM pulse failed\n"); + break; + } + + rt_thread_mdelay(PWM_TEST_DELAY_MS); + + if (increasing == RT_TRUE) + { + pulse += PWM_TEST_STEP_NS; + if (pulse >= PWM_TEST_PERIOD_NS) + { + pulse = PWM_TEST_PERIOD_NS; + increasing = RT_FALSE; + } + } + else if (pulse > PWM_TEST_STEP_NS) + { + pulse -= PWM_TEST_STEP_NS; + } + else + { + pulse = 0U; + increasing = RT_TRUE; + } + } + + pwm_dual_disable(); + pwm_test_thread = RT_NULL; + rt_kprintf("dual PWM test stopped\n"); +} + +static int pwm_dual_start(void) +{ + rt_err_t result; + + if (pwm_test_thread != RT_NULL) + { + rt_kprintf("dual PWM test is already running\n"); + return -RT_EBUSY; + } + + pwm2_device = (struct rt_device_pwm *)rt_device_find(PWM2_DEVICE_NAME); + pwm30_device = (struct rt_device_pwm *)rt_device_find(PWM30_DEVICE_NAME); + if (pwm2_device == RT_NULL || pwm30_device == RT_NULL) + { + rt_kprintf("cannot find %s or %s device\n", + PWM2_DEVICE_NAME, PWM30_DEVICE_NAME); + return -RT_ERROR; + } + + result = rt_pwm_set(pwm2_device, PWM2_DEVICE_CHANNEL, + PWM_TEST_PERIOD_NS, 0U); + if (result == RT_EOK) + { + result = rt_pwm_set(pwm30_device, PWM30_DEVICE_CHANNEL, + PWM_TEST_PERIOD_NS, 0U); + } + if (result == RT_EOK) + { + result = rt_pwm_enable(pwm2_device, PWM2_DEVICE_CHANNEL); + } + if (result == RT_EOK) + { + result = rt_pwm_enable(pwm30_device, PWM30_DEVICE_CHANNEL); + } + if (result != RT_EOK) + { + pwm_dual_disable(); + rt_kprintf("enable dual PWM failed: %d\n", result); + return result; + } + + pwm_test_stop = RT_FALSE; + pwm_test_thread = rt_thread_create("pwmdual", pwm_dual_test_entry, RT_NULL, + PWM_TEST_THREAD_STACK, + PWM_TEST_THREAD_PRIORITY, + PWM_TEST_THREAD_TICK); + if (pwm_test_thread == RT_NULL) + { + pwm_dual_disable(); + rt_kprintf("create dual PWM test thread failed\n"); + return -RT_ENOMEM; + } + + result = rt_thread_startup(pwm_test_thread); + if (result != RT_EOK) + { + pwm_dual_disable(); + rt_thread_delete(pwm_test_thread); + pwm_test_thread = RT_NULL; + rt_kprintf("start dual PWM test thread failed: %d\n", result); + return result; + } + + rt_kprintf("dual PWM test started: pwm2 ch2 and pwm30 ch3\n"); + return RT_EOK; +} + +static int pwm_dual_test(int argc, char *argv[]) +{ + if (argc == 1 || (argc == 2 && rt_strcmp(argv[1], "start") == 0)) + { + return pwm_dual_start(); + } + + if (argc == 2 && rt_strcmp(argv[1], "stop") == 0) + { + if (pwm_test_thread == RT_NULL) + { + rt_kprintf("dual PWM test is not running\n"); + return RT_EOK; + } + + pwm_test_stop = RT_TRUE; + return RT_EOK; + } + + rt_kprintf("usage: pwm_dual_test [start|stop]\n"); + return -RT_EINVAL; +} + +MSH_CMD_EXPORT(pwm_dual_test, test pwm2 channel 2 and pwm30 channel 3); +#endif /* BSP_USING_PWM_DUAL_TEST */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/applications/rtc_alarm_example.c b/bsp/gd32/arm/gd32h77d-ginopi/applications/rtc_alarm_example.c new file mode 100644 index 000000000000..ca03d2311bbb --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/applications/rtc_alarm_example.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-09-08 CYFS add a one-shot RTC alarm command with ulog output + */ + +#include +#include +#include +#include +#include +#include + +#define DBG_TAG "rtc.alarm" +#define DBG_LVL DBG_INFO +#include + +#ifdef BSP_USING_RTC_ALARM_EXAMPLE +#include + +#define RTC_ALARM_DEFAULT_SECONDS 5UL +#define RTC_ALARM_MAX_SECONDS 86400UL + +static rt_alarm_t demo_alarm; +static time_t demo_deadline; + +static void alarm_log_time(const char *state, time_t timestamp) +{ + struct tm local; + + if (localtime_r(×tamp, &local) == RT_NULL) + { + LOG_E("Cannot convert alarm time"); + return; + } + + LOG_I("%s: %04d-%02d-%02d %02d:%02d:%02d (local time)", state, + local.tm_year + 1900, local.tm_mon + 1, local.tm_mday, + local.tm_hour, local.tm_min, local.tm_sec); +} + +static void alarm_callback(rt_alarm_t alarm, time_t timestamp) +{ + /* The Alarm service owns the list during callbacks; keep the object alive. */ + RT_UNUSED(alarm); + alarm_log_time("Triggered", timestamp); +} + +static rt_err_t alarm_remove(void) +{ + rt_err_t result = RT_EOK; + + if (demo_alarm != RT_NULL) + { + result = rt_alarm_delete(demo_alarm); + demo_alarm = RT_NULL; + } + + return result; +} + +static void alarm_usage(void) +{ + LOG_I("rtc_alarm [seconds] - one-shot alarm, 1..86400 seconds, default 5"); + LOG_I("rtc_alarm status - show the example alarm"); + LOG_I("rtc_alarm stop - cancel and delete the example alarm"); +} + +static int rtc_alarm(int argc, char **argv) +{ + struct rt_alarm_setup setup = {0}; + struct timeval now; + time_t deadline; + rt_int64_t deadline_seconds; + unsigned long seconds = RTC_ALARM_DEFAULT_SECONDS; + char *end; + rt_err_t result; + + if (argc > 2) + { + alarm_usage(); + return -RT_EINVAL; + } + + if (argc == 2) + { + if (rt_strcmp(argv[1], "help") == 0) + { + alarm_usage(); + return RT_EOK; + } + if (rt_strcmp(argv[1], "status") == 0) + { + if (demo_alarm == RT_NULL) + { + LOG_I("No example alarm"); + } + else + { + rt_base_t level = rt_hw_interrupt_disable(); + rt_bool_t armed = (demo_alarm->flag & RT_ALARM_STATE_START) != 0; + rt_hw_interrupt_enable(level); + alarm_log_time(armed ? "Armed" : "Expired", demo_deadline); + } + return RT_EOK; + } + if (rt_strcmp(argv[1], "stop") == 0) + { + result = alarm_remove(); + if (result != RT_EOK) + { + LOG_E("Cannot disable hardware alarm: %d", result); + return result; + } + LOG_I("Example alarm stopped"); + return RT_EOK; + } + + errno = 0; + seconds = strtoul(argv[1], &end, 10); + if ((errno == ERANGE) || (end == argv[1]) || (*end != '\0') || + (argv[1][0] < '0') || (argv[1][0] > '9') || + (seconds == 0UL) || (seconds > RTC_ALARM_MAX_SECONDS)) + { + LOG_E("Invalid delay: use 1..86400 seconds"); + return -RT_EINVAL; + } + } + + /* gettimeofday opens and initializes the RTC before the Alarm API uses it. */ + if (gettimeofday(&now, RT_NULL) != 0) + { + LOG_E("Cannot read RTC; check the drv.rtc log"); + return -RT_ERROR; + } + + deadline_seconds = (rt_int64_t)now.tv_sec + seconds; + deadline = (time_t)deadline_seconds; + if ((rt_int64_t)deadline != deadline_seconds) + { + LOG_E("Alarm deadline exceeds the time_t range"); + return -RT_EINVAL; + } + setup.flag = RT_ALARM_ONESHOT; +#ifdef RT_ALARM_USING_LOCAL_TIME + if (localtime_r(&deadline, &setup.wktime) == RT_NULL) +#else + if (gmtime_r(&deadline, &setup.wktime) == RT_NULL) +#endif + { + LOG_E("Cannot convert alarm deadline"); + return -RT_EINVAL; + } + if ((setup.wktime.tm_year < 100) || (setup.wktime.tm_year > 199)) + { + LOG_E("Alarm date must be between 2000 and 2099"); + return -RT_EINVAL; + } + + result = alarm_remove(); + if (result != RT_EOK) + { + LOG_E("Cannot replace the previous alarm: %d", result); + return result; + } + + demo_alarm = rt_alarm_create(alarm_callback, &setup); + if (demo_alarm == RT_NULL) + { + LOG_E("Cannot allocate alarm"); + return -RT_ENOMEM; + } + + demo_deadline = deadline; + result = rt_alarm_start(demo_alarm); + if (result != RT_EOK) + { + alarm_remove(); + LOG_E("Cannot start alarm: %d", result); + return result; + } + + LOG_I("One-shot alarm in %lu seconds", seconds); + alarm_log_time("Armed", deadline); + return RT_EOK; +} +MSH_CMD_EXPORT(rtc_alarm, set a one-shot RTC alarm or use status/stop/help); +#endif diff --git a/bsp/gd32/arm/gd32h77d-ginopi/applications/sdram_heap_test.c b/bsp/gd32/arm/gd32h77d-ginopi/applications/sdram_heap_test.c new file mode 100644 index 000000000000..274b896b06fe --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/applications/sdram_heap_test.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#ifdef BSP_USING_SDRAM_HEAP_TEST +static int sdram_heap_test(void) +{ + const rt_size_t size = 2U * 1024U * 1024U; + const rt_size_t words = size / sizeof(rt_uint32_t); + volatile rt_uint32_t *buffer; + rt_size_t i; + int result = RT_EOK; + + buffer = (volatile rt_uint32_t *)rt_malloc(size); + if (buffer == RT_NULL) + { + rt_kprintf("allocate failed\n"); + return -RT_ENOMEM; + } + + rt_kprintf("buffer: %p\n", buffer); + if ((rt_ubase_t)buffer < EXT_SDRAM_BEGIN || + (rt_ubase_t)buffer + size > EXT_SDRAM_END) + { + rt_kprintf("not allocated from SDRAM\n"); + rt_free((void *)buffer); + return -RT_ERROR; + } + + for (i = 0; i < words; i++) + buffer[i] = 0x55AA0000U ^ (rt_uint32_t)i; + + for (i = 0; i < words; i++) + { + if (buffer[i] != (0x55AA0000U ^ (rt_uint32_t)i)) + { + rt_kprintf("failed at %p\n", &buffer[i]); + result = -RT_ERROR; + break; + } + } + + rt_free((void *)buffer); + rt_kprintf(result == RT_EOK ? "SDRAM heap test passed\n" : + "SDRAM heap test failed\n"); + return result; +} +MSH_CMD_EXPORT(sdram_heap_test, test SDRAM heap); +#endif diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/Kconfig b/bsp/gd32/arm/gd32h77d-ginopi/board/Kconfig new file mode 100644 index 000000000000..0d6f8b49ee89 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/Kconfig @@ -0,0 +1,787 @@ +menu "Hardware Drivers Config" + +config SOC_SERIES_GD32H77x_H78X + bool + default y + +config SOC_GD32H77DIW + bool + select SOC_SERIES_GD32H77x_H78X + select RT_USING_COMPONENTS_INIT + select RT_USING_USER_MAIN + default y + +menu "Onboard Peripheral Drivers" + + menuconfig BSP_USING_FS + bool "Enable File System" + select RT_USING_DFS + select RT_USING_DFS_ROMFS + default n + + if BSP_USING_FS + config BSP_USING_SDCARD_FATFS + bool "Enable SDCARD (FATFS)" + select BSP_SDIO_USING_SDIO1 + select RT_USING_DFS_ELMFAT + default n + + if BSP_USING_SDCARD_FATFS + config BSP_USING_FS_AUTO_MOUNT + bool "Enable filesystem auto mount" + default y + endif + + config BSP_USING_FLASH_FATFS + bool "Enable FAL filesystem partition based on GD25Q64ESIG" + select BSP_USING_OSPI0 + select RT_USING_FAL + select FAL_PART_HAS_TABLE_CFG + select RT_USING_DFS_ELMFAT + default n + help + Enable the onboard 8 MiB GD25Q64ESIG in Quad SPI mode and + expose its filesystem partition through FAL. FatFs maximum + sector size must be 4096. + + if BSP_USING_FLASH_FATFS + config BSP_OSPI_FLASH_MAX_HZ + int "Maximum QSPI flash clock (Hz)" + range 1000000 100000000 + default 50000000 + + config BSP_USING_FLASH_FS_AUTO_MOUNT + bool "Enable filesystem auto mount" + default y + endif + endif + + menuconfig BSP_USING_ETH + bool "Enable ENET1 (RMII)" + select RT_USING_LWIP + select RT_USING_SAL + default n + help + Enable ENET1 in RMII mode. The PHY must provide the 50 MHz RMII + reference clock on PC12. + + if BSP_USING_ETH + config BSP_ENET_PHY_ADDR + int "Default PHY address" + range 0 31 + default 2 + endif + + config BSP_USING_SDRAM + bool "Enable SDRAM" + select RT_USING_SDRAM + default n + help + Enable the onboard 32 MiB, 16-bit SDRAM connected to EXMC + SDRAM device 0. + + menuconfig BSP_USING_LCD_MIPI + bool "Enable 720x720 MIPI DSI LCD" + select BSP_USING_SDRAM + select BSP_USING_GPIO + select BSP_USING_PWM + select BSP_USING_PWM1 + select RT_USING_LCD + default n + help + Enable the two-lane MIPI DSI LCD using the FL7707N controller. + The RGB565 framebuffer occupies the start of the onboard SDRAM. + Backlight brightness uses TIMER1 channel 0 on PA0. + + config BSP_USING_TOUCH_GT911 + bool "Enable GT911 capacitive touch" + depends on BSP_USING_LCD_MIPI + select BSP_USING_I2C + select BSP_USING_HARD_I2C + select BSP_USING_HARD_I2C3 + select RT_USING_TOUCH + select RT_TOUCH_PIN_IRQ + select PKG_USING_GT911 + default n + help + Enable the GT911 package driver on I2C3. PH3 drives the dedicated + active-low reset input and PH5 selects the default 0x5D address + during reset, then works as the touch interrupt input. + + config BSP_USING_OV7670 + bool "Enable OV7670 camera" + depends on !BSP_USING_PWM0 + depends on !BSP_UART0_TX_USING_DMA && !BSP_UART5_TX_USING_DMA + select BSP_USING_GPIO + select BSP_USING_I2C + select BSP_USING_HARD_I2C + select BSP_USING_HARD_I2C2 + default n + help + Enable the onboard OV7670 on I2C2 and capture QVGA RGB565 frames + through DCI and DMA1 channel 7. Camera XCLK is generated by + TIMER0 channel 0 on PE9. + + menuconfig BSP_USING_USB + bool "Enable USB" + default n + help + Enable the onboard USB Type-C device port and/or USB-A host port. + + if BSP_USING_USB + config BSP_USB_USING_DEVICE + bool "Enable USBHS0 device (Type-C)" + default y + select RT_USING_USB_DEVICE + select RT_USB_DEVICE_COMPOSITE + select RT_USB_DEVICE_CDC + + config BSP_USB_USING_HOST + bool "Enable USBHS1 host (USB-A)" + default n + select RT_USING_USB_HOST + + if BSP_USB_USING_DEVICE + choice + prompt "USBHS0 device speed" + default BSP_USB_DEVICE_USING_HS + + config BSP_USB_DEVICE_USING_HS + bool "High speed" + + config BSP_USB_DEVICE_USING_FS + bool "Full speed" + endchoice + endif + endif + +endmenu + +menu "On-chip Peripheral Drivers" + + config BSP_USING_GPIO + bool "Enable GPIO" + select RT_USING_PIN + default y + + menuconfig BSP_USING_UART + bool "Enable UART" + default y + if BSP_USING_UART + choice BSP_SERIAL_V_SEL + prompt "Select UART framework version" + default BSP_USING_SERIAL_V1 + + config BSP_USING_SERIAL_V1 + bool "Use Serial V1 framework" + select RT_USING_SERIAL + + config BSP_USING_SERIAL_V2 + bool "Use Serial V2 framework" + select RT_USING_SERIAL_V2 + endchoice + + menuconfig BSP_USING_UART0 + bool "Enable UART0" + default n + if BSP_USING_UART0 + config BSP_UART0_TX_PIN + string "UART0 TX name, such as PA8" + default "PF4" + + config BSP_UART0_RX_PIN + string "UART0 RX name, such as PA9" + default "PF5" + + config BSP_UART0_AFIO + string "UART0 alternate function, such as AF7" + default "AF4" + + if BSP_USING_SERIAL_V2 + config BSP_UART0_RX_USING_DMA + bool "Enable UART0 RX DMA" + depends on RT_SERIAL_USING_DMA + default n + + config BSP_UART0_TX_USING_DMA + bool "Enable UART0 TX DMA" + depends on RT_SERIAL_USING_DMA + default n + + config BSP_UART0_DMA_PING_BUFSIZE + int "Set UART0 RX DMA ping-pong buffer size" + range 16 65535 + depends on BSP_UART0_RX_USING_DMA + default 64 + + config BSP_UART0_RX_BUFSIZE + int "Set UART0 RX buffer size" + range 64 65535 + default 128 + + config BSP_UART0_TX_BUFSIZE + int "Set UART0 TX buffer size" + range 0 65535 + default 128 + endif + endif + + menuconfig BSP_USING_UART1 + bool "Enable UART1" + default y + if BSP_USING_UART1 + config BSP_UART1_TX_PIN + string "UART1 TX name, such as PA8" + default "PA2" + + config BSP_UART1_RX_PIN + string "UART1 RX name, such as PA9" + default "PA3" + + config BSP_UART1_AFIO + string "UART1 alternate function, such as AF7" + default "AF7" + + if BSP_USING_SERIAL_V2 + config BSP_UART1_RX_USING_DMA + bool "Enable UART1 RX DMA" + depends on RT_SERIAL_USING_DMA + default n + + config BSP_UART1_TX_USING_DMA + bool "Enable UART1 TX DMA" + depends on RT_SERIAL_USING_DMA + default n + + config BSP_UART1_DMA_PING_BUFSIZE + int "Set UART1 RX DMA ping-pong buffer size" + range 16 65535 + depends on BSP_UART1_RX_USING_DMA + default 64 + + config BSP_UART1_RX_BUFSIZE + int "Set UART1 RX buffer size" + range 64 65535 + default 128 + + config BSP_UART1_TX_BUFSIZE + int "Set UART1 TX buffer size" + range 0 65535 + default 128 + endif + endif + + menuconfig BSP_USING_UART2 + bool "Enable UART2" + default n + if BSP_USING_UART2 + config BSP_UART2_TX_PIN + string "UART2 TX name, such as PA8" + default "PB10" + + config BSP_UART2_RX_PIN + string "UART2 RX name, such as PA9" + default "PB11" + + config BSP_UART2_AFIO + string "UART2 alternate function, such as AF7" + default "AF7" + + if BSP_USING_SERIAL_V2 + config BSP_UART2_RX_USING_DMA + bool "Enable UART2 RX DMA" + depends on RT_SERIAL_USING_DMA + default n + + config BSP_UART2_TX_USING_DMA + bool "Enable UART2 TX DMA" + depends on RT_SERIAL_USING_DMA + default n + + config BSP_UART2_DMA_PING_BUFSIZE + int "Set UART2 RX DMA ping-pong buffer size" + range 16 65535 + depends on BSP_UART2_RX_USING_DMA + default 64 + + config BSP_UART2_RX_BUFSIZE + int "Set UART2 RX buffer size" + range 64 65535 + default 128 + + config BSP_UART2_TX_BUFSIZE + int "Set UART2 TX buffer size" + range 0 65535 + default 128 + endif + endif + + menuconfig BSP_USING_UART3 + bool "Enable UART3" + default n + if BSP_USING_UART3 + config BSP_UART3_TX_PIN + string "UART3 TX name, such as PA8" + default "PC10" + + config BSP_UART3_RX_PIN + string "UART3 RX name, such as PA9" + default "PC11" + + config BSP_UART3_AFIO + string "UART3 alternate function, such as AF7" + default "AF8" + + if BSP_USING_SERIAL_V2 + config BSP_UART3_RX_USING_DMA + bool "Enable UART3 RX DMA" + depends on RT_SERIAL_USING_DMA + default n + + config BSP_UART3_TX_USING_DMA + bool "Enable UART3 TX DMA" + depends on RT_SERIAL_USING_DMA + default n + + config BSP_UART3_DMA_PING_BUFSIZE + int "Set UART3 RX DMA ping-pong buffer size" + range 16 65535 + depends on BSP_UART3_RX_USING_DMA + default 64 + + config BSP_UART3_RX_BUFSIZE + int "Set UART3 RX buffer size" + range 64 65535 + default 128 + + config BSP_UART3_TX_BUFSIZE + int "Set UART3 TX buffer size" + range 0 65535 + default 128 + endif + endif + + menuconfig BSP_USING_UART4 + bool "Enable UART4" + default n + if BSP_USING_UART4 + config BSP_UART4_TX_PIN + string "UART4 TX name, such as PB12" + default "PB12" + + config BSP_UART4_RX_PIN + string "UART4 RX name, such as PB13" + default "PB13" + + config BSP_UART4_AFIO + string "UART4 alternate function, such as AF14" + default "AF14" + + if BSP_USING_SERIAL_V2 + config BSP_UART4_RX_USING_DMA + bool "Enable UART4 RX DMA" + depends on RT_SERIAL_USING_DMA + default n + + config BSP_UART4_TX_USING_DMA + bool "Enable UART4 TX DMA" + depends on RT_SERIAL_USING_DMA + default n + + config BSP_UART4_DMA_PING_BUFSIZE + int "Set UART4 RX DMA ping-pong buffer size" + range 16 65535 + depends on BSP_UART4_RX_USING_DMA + default 64 + + config BSP_UART4_RX_BUFSIZE + int "Set UART4 RX buffer size" + range 64 65535 + default 512 + + config BSP_UART4_TX_BUFSIZE + int "Set UART4 TX buffer size" + range 0 65535 + default 256 + endif + endif + endif + + menuconfig BSP_USING_I2C + bool "Enable I2C" + default n + if BSP_USING_I2C + menuconfig BSP_USING_HARD_I2C + bool "Enable hardware I2C" + select RT_USING_I2C + default n + if BSP_USING_HARD_I2C + menuconfig BSP_USING_HARD_I2C1 + bool "Enable I2C1 hardware bus" + default n + if BSP_USING_HARD_I2C1 + config BSP_I2C1_SCL_PIN + string "I2C1 SCL pin name, such as PH4" + default "PH4" + + config BSP_I2C1_SDA_PIN + string "I2C1 SDA pin name, such as PB11" + default "PB11" + + config BSP_I2C1_AFIO + string "I2C1 alternate function, such as AF4" + default "AF4" + endif + + menuconfig BSP_USING_HARD_I2C2 + bool "Enable I2C2 hardware bus" + default n + if BSP_USING_HARD_I2C2 + config BSP_I2C2_SCL_PIN + string "I2C2 SCL pin name, such as PA8" + default "PA8" + + config BSP_I2C2_SDA_PIN + string "I2C2 SDA pin name, such as PC9" + default "PC9" + + config BSP_I2C2_AFIO + string "I2C2 alternate function, such as AF4" + default "AF4" + endif + + menuconfig BSP_USING_HARD_I2C3 + bool "Enable I2C3 hardware bus" + default n + if BSP_USING_HARD_I2C3 + config BSP_I2C3_SCL_PIN + string "I2C3 SCL pin name, such as PD12" + default "PD12" + + config BSP_I2C3_SDA_PIN + string "I2C3 SDA pin name, such as PD13" + default "PD13" + + config BSP_I2C3_AFIO + string "I2C3 alternate function, such as AF4" + default "AF4" + endif + endif + endif + + menuconfig BSP_USING_SPI + bool "Enable SPI" + select RT_USING_SPI + default n + if BSP_USING_SPI + menuconfig BSP_USING_SPI3 + bool "Enable SPI3 hardware bus" + default n + if BSP_USING_SPI3 + config BSP_SPI3_SCK_PIN + string "SPI3 SCK pin name, such as PE12" + default "PE12" + + config BSP_SPI3_SCK_AFIO + string "SPI3 SCK alternate function, such as AF5" + default "AF5" + + config BSP_SPI3_MISO_PIN + string "SPI3 MISO pin name, such as PF0" + default "PF0" + + config BSP_SPI3_MISO_AFIO + string "SPI3 MISO alternate function, such as AF1" + default "AF1" + + config BSP_SPI3_MOSI_PIN + string "SPI3 MOSI pin name, such as PF1" + default "PF1" + + config BSP_SPI3_MOSI_AFIO + string "SPI3 MOSI alternate function, such as AF1" + default "AF1" + endif + endif + + menuconfig BSP_USING_OSPI0 + bool "Enable OSPI0 (QSPI)" + select BSP_USING_GPIO + select RT_USING_SPI + select RT_USING_QSPI + default n + if BSP_USING_OSPI0 + config BSP_OSPI0_USING_MDMA + bool "Enable OSPI0 MDMA transfer" + default n + + config BSP_OSPI0_MDMA_CHANNEL + int "OSPI0 MDMA channel (0-15)" + range 0 15 + depends on BSP_OSPI0_USING_MDMA + default 0 + + config BSP_OSPI0_CS_PIN + string "OSPI0 CS pin name, such as PC11" + default "PC11" + + config BSP_OSPI0_CS_AFIO + string "OSPI0 CS alternate function, such as AF9" + default "AF9" + + config BSP_OSPI0_CLK_PIN + string "OSPI0 CLK pin name, such as PF10" + default "PF10" + + config BSP_OSPI0_CLK_AFIO + string "OSPI0 CLK alternate function, such as AF9" + default "AF9" + + config BSP_OSPI0_D0_PIN + string "OSPI0/QSPI IO0 pin name, such as PF8" + default "PF8" + + config BSP_OSPI0_D0_AFIO + string "OSPI0/QSPI IO0 alternate function, such as AF10" + default "AF10" + + config BSP_OSPI0_D1_PIN + string "OSPI0/QSPI IO1 pin name, such as PF9" + default "PF9" + + config BSP_OSPI0_D1_AFIO + string "OSPI0/QSPI IO1 alternate function, such as AF10" + default "AF10" + + config BSP_OSPI0_D2_PIN + string "OSPI0/QSPI IO2 pin name, such as PF7" + default "PF7" + + config BSP_OSPI0_D2_AFIO + string "OSPI0/QSPI IO2 alternate function, such as AF10" + default "AF10" + + config BSP_OSPI0_D3_PIN + string "OSPI0/QSPI IO3 pin name, such as PA1" + default "PA1" + + config BSP_OSPI0_D3_AFIO + string "OSPI0/QSPI IO3 alternate function, such as AF9" + default "AF9" + + endif + + menuconfig BSP_SDIO_USING_SDIO1 + bool "Enable SDIO1" + select BSP_USING_GPIO + select RT_USING_SDIO + default n + if BSP_SDIO_USING_SDIO1 + config BSP_SDIO_CLK_PIN + string "SDIO CLK pin name, such as PD6" + default "PD6" + + config BSP_SDIO_CLK_AFIO + string "SDIO CLK alternate function, such as AF11" + default "AF11" + + config BSP_SDIO_CMD_PIN + string "SDIO CMD pin name, such as PD7" + default "PD7" + + config BSP_SDIO_CMD_AFIO + string "SDIO CMD alternate function, such as AF11" + default "AF11" + + config BSP_SDIO_D0_PIN + string "SDIO D0 pin name, such as PF4" + default "PF4" + + config BSP_SDIO_D0_AFIO + string "SDIO D0 alternate function, such as AF10" + default "AF10" + + config BSP_SDIO_D1_PIN + string "SDIO D1 pin name, such as PG10" + default "PG10" + + config BSP_SDIO_D1_AFIO + string "SDIO D1 alternate function, such as AF11" + default "AF11" + + config BSP_SDIO_D2_PIN + string "SDIO D2 pin name, such as PG11" + default "PG11" + + config BSP_SDIO_D2_AFIO + string "SDIO D2 alternate function, such as AF10" + default "AF10" + + config BSP_SDIO_D3_PIN + string "SDIO D3 pin name, such as PG12" + default "PG12" + + config BSP_SDIO_D3_AFIO + string "SDIO D3 alternate function, such as AF10" + default "AF10" + + config BSP_SDIO_USING_CARD_DETECT + bool "Enable SD card detect pin" + default y + + if BSP_SDIO_USING_CARD_DETECT + config BSP_SDIO_CD_PIN + string "SD card detect pin name, such as PE10" + default "PE10" + + config BSP_SDIO_CD_ACTIVE_LOW + bool "SD card detect is active low" + default y + endif + endif + + menuconfig BSP_USING_CAN + bool "Enable CAN" + select BSP_USING_GPIO + select RT_USING_CAN + default n + if BSP_USING_CAN + menuconfig BSP_USING_CAN1 + bool "Enable CAN1" + default n + if BSP_USING_CAN1 + config BSP_CAN1_TX_PIN + string "CAN1 TX pin name, such as PB4" + default "PB4" + + config BSP_CAN1_RX_PIN + string "CAN1 RX pin name, such as PB5" + default "PB5" + + config BSP_CAN1_TX_AFIO + string "CAN1 TX alternate function, such as AF4" + default "AF4" + + config BSP_CAN1_RX_AFIO + string "CAN1 RX alternate function, such as AF9" + default "AF9" + endif + endif + + menuconfig BSP_USING_PWM + bool "Enable PWM" + select BSP_USING_GPIO + select RT_USING_PWM + default n + if BSP_USING_PWM + menuconfig BSP_USING_PWM1 + bool "Enable TIMER1 PWM" + default n + help + TIMER1 CH0 on PA0 is exposed as channel 1 of the pwm1 + device and is used by the LCD backlight. + + menuconfig BSP_USING_PWM2 + bool "Enable TIMER2 PWM" + default n + help + TIMER2 CH1 is exposed as channel 2 of the pwm2 device. + if BSP_USING_PWM2 + config BSP_PWM2_CH1_PIN + string "TIMER2 CH1 pin name, such as PA7" + default "PA7" + + config BSP_PWM2_CH1_AFIO + string "TIMER2 CH1 alternate function, such as AF2" + default "AF2" + endif + + menuconfig BSP_USING_PWM30 + bool "Enable TIMER30 PWM" + default n + help + TIMER30 CH2 is exposed as channel 3 of the pwm30 device. + if BSP_USING_PWM30 + config BSP_PWM30_CH2_PIN + string "TIMER30 CH2 pin name, such as PG7" + default "PG7" + + config BSP_PWM30_CH2_AFIO + string "TIMER30 CH2 alternate function, such as AF4" + default "AF4" + endif + endif + + menuconfig BSP_USING_ONCHIP_RTC + bool "Enable RTC" + select RT_USING_RTC + default n + if BSP_USING_ONCHIP_RTC + config BSP_USING_ALARM + bool "Enable RTC alarm" + select RT_USING_ALARM + default n + if BSP_USING_ALARM + choice BSP_ALARM_SEL + prompt "Select rtc alarm device" + default BSP_USING_ALARM0 + + config BSP_USING_ALARM0 + bool "Enable RTC Alarm0" + + config BSP_USING_ALARM1 + bool "Enable RTC Alarm1" + endchoice + endif + endif + + source "$(BSP_DIR)/../libraries/gd32_drivers/Kconfig" + +endmenu + +menu "Board extended module Drivers" + + config AT_DEVICE_USING_GD32VW553 + bool + default n + + config AT_DEVICE_GD32VW553_SOCKET + bool + select AT_USING_SOCKET + default n + + menuconfig BSP_USING_GD32VW553_AT + bool "Enable GD32VW553 AT Wi-Fi module" + select BSP_USING_GPIO + select BSP_USING_UART + select BSP_USING_UART4 + select PKG_USING_AT_DEVICE + select AT_DEVICE_USING_GD32VW553 + select AT_DEVICE_GD32VW553_SOCKET + default n + help + Enable the onboard GD32VW553 Wi-Fi module through the RT-Thread + at_device and AT socket framework. The default AT UART is UART4 + TX PB12 AF14 and RX PB13 AF14. The reset pin is PC5 and is + active low. + + if BSP_USING_GD32VW553_AT + + config BSP_GD32VW553_AT_BAUDRATE + int "GD32VW553 AT UART baud rate" + range 9600 2000000 + default 115200 + + config BSP_GD32VW553_WIFI_SSID + string "Wi-Fi SSID" + default "rtthread" + + config BSP_GD32VW553_WIFI_PASSWORD + string "Wi-Fi password" + default "12345678" + + endif + +endmenu + +endmenu diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/SConscript b/bsp/gd32/arm/gd32h77d-ginopi/board/SConscript new file mode 100644 index 000000000000..a3b7185ac2f6 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/SConscript @@ -0,0 +1,37 @@ +import os +import rtconfig +from building import * + +cwd = GetCurrentDir() + +# add general drivers +src = Split(''' +board.c +''') + +if GetDepend(['BSP_USING_SDRAM']): + src += ['drv_sdram_port.c'] + +if GetDepend(['BSP_USING_LCD_MIPI']): + src += ['drv_lcd.c'] + +if GetDepend(['BSP_USING_TOUCH_GT911']): + src += ['drv_gt911.c'] + +if GetDepend(['BSP_USING_OV7670']): + src += ['drv_ov7670.c'] + +if GetDepend(['BSP_USING_FLASH_FATFS']): + src += ['drv_qspi_flash.c'] + +if GetDepend(['BSP_USING_FS']): + src += ['drv_filesystem.c'] + +path = [cwd] + +CPPDEFINES = ['GD32H77DXW'] +group = DefineGroup('Drivers', src, depend = [''], CPPPATH = path, CPPDEFINES = CPPDEFINES) + +group = group + SConscript(os.path.join('ports', 'SConscript')) + +Return('group') diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/board.c b/bsp/gd32/arm/gd32h77d-ginopi/board/board.c new file mode 100644 index 000000000000..4f33debbdd6b --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/board.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-09-08 CYFS configure boot vectors and display memory cache policy + */ +#include +#include +#include +#include + +#ifdef BSP_USING_LCD_MIPI +#include "gd32h77x_78x_misc.h" +#endif + +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +extern void Reset_Handler(void); +extern uint32_t __initial_sp; + +__attribute__((section(".ARM.__at_0x08000000"), used)) +const uint32_t boot_fuction_table[] = +{ + (uint32_t)&__initial_sp, + (uint32_t)&Reset_Handler, +}; +#elif defined(__GNUC__) +extern void Reset_Handler(void); +extern uint32_t _sp; + +__attribute__((section(".boot_fuction_table"), used)) +const uint32_t boot_fuction_table[] = +{ + (uint32_t)&_sp, + (uint32_t)&Reset_Handler, +}; +#endif + +#ifdef BSP_USING_LCD_MIPI +#ifndef BSP_USING_SDRAM +#error "MIPI LCD requires SDRAM" +#endif + +#define BOARD_DMA_MPU_SUBREGION_SIZE (512U * 1024U) +#ifdef BSP_USING_OV7670 +#define BOARD_DMA_MPU_SIZE EXT_SDRAM_LCD_SCANOUT_SIZE +#else +#define BOARD_DMA_MPU_SIZE EXT_SDRAM_LCD_RESERVED_SIZE +#endif +#define BOARD_EXT_GUARD_ATTR ARM_MPU_RASR(1U, ARM_MPU_AP_NONE, 0U, \ + 1U, 0U, 0U, 0x87U, \ + ARM_MPU_REGION_SIZE_4GB) + +/* SDRAM board initialization must finish before its windows become cacheable. */ +static void board_mpu_config(void) +{ + mpu_region_init_struct mpu_config; + rt_base_t level; + + RT_ASSERT((SCB->CCR & SCB_CCR_DC_Msk) == 0U); + RT_ASSERT(EXT_SDRAM_LCD_RESERVED_SIZE <= 8U * BOARD_DMA_MPU_SUBREGION_SIZE); + RT_ASSERT((EXT_SDRAM_LCD_RESERVED_SIZE % BOARD_DMA_MPU_SUBREGION_SIZE) == 0U); + RT_ASSERT((BOARD_DMA_MPU_SIZE > 0U) && + (BOARD_DMA_MPU_SIZE <= EXT_SDRAM_LCD_RESERVED_SIZE)); + RT_ASSERT((BOARD_DMA_MPU_SIZE % BOARD_DMA_MPU_SUBREGION_SIZE) == 0U); + + level = rt_hw_interrupt_disable(); + __DSB(); + ARM_MPU_Disable(); + + mpu_region_struct_para_init(&mpu_config); + /* Match the reference project's AXI-SRAM write-through attributes. */ + mpu_config.region_base_address = 0x24000000U; + mpu_config.region_size = MPU_REGION_SIZE_1MB; + mpu_config.access_permission = MPU_AP_FULL_ACCESS; + mpu_config.access_bufferable = MPU_ACCESS_NON_BUFFERABLE; + mpu_config.access_cacheable = MPU_ACCESS_CACHEABLE; + mpu_config.access_shareable = MPU_ACCESS_NON_SHAREABLE; + mpu_config.region_number = MPU_REGION_NUMBER1; + mpu_config.subregion_disable = MPU_SUBREGION_ENABLE; + mpu_config.instruction_exec = MPU_INSTRUCTION_EXEC_PERMIT; + mpu_config.tex_type = MPU_TEX_TYPE0; + mpu_region_config(&mpu_config); + mpu_region_enable(); + + if (rt_hw_sdram_is_ready()) + { + /* CPU-rendered layers can spill out of AXI SRAM into the SDRAM heap. */ + mpu_config.region_base_address = EXT_SDRAM_BEGIN; + mpu_config.region_size = MPU_REGION_SIZE_32MB; + mpu_config.access_permission = MPU_AP_FULL_ACCESS; + mpu_config.access_bufferable = MPU_ACCESS_BUFFERABLE; + mpu_config.access_cacheable = MPU_ACCESS_CACHEABLE; + mpu_config.access_shareable = MPU_ACCESS_NON_SHAREABLE; + mpu_config.region_number = MPU_REGION_NUMBER5; + mpu_config.subregion_disable = MPU_SUBREGION_ENABLE; + mpu_config.instruction_exec = MPU_INSTRUCTION_EXEC_NOT_PERMIT; + mpu_config.tex_type = MPU_TEX_TYPE1; + mpu_region_config(&mpu_config); + mpu_region_enable(); + + /* Keep scanout/IPA buffers write-through. Camera and LVGL draw buffers + * reside in AXI SRAM; the SDRAM heap retains its write-back policy. + * Preserve the existing 3 MiB reservation and heap addresses. */ + mpu_config.region_size = MPU_REGION_SIZE_4MB; + mpu_config.access_bufferable = MPU_ACCESS_NON_BUFFERABLE; + mpu_config.region_number = MPU_REGION_NUMBER6; + mpu_config.subregion_disable = (rt_uint8_t)(0xFFU << + (BOARD_DMA_MPU_SIZE / BOARD_DMA_MPU_SUBREGION_SIZE)); + mpu_config.tex_type = MPU_TEX_TYPE0; + mpu_region_config(&mpu_config); + mpu_region_enable(); + } + +#if !defined(BSP_USING_OSPI1) + /* Block speculative accesses to unused external windows (0x60000000.. + * 0xDFFFFFFF). Regions 5/6 override this guard for valid SDRAM. + * The OSPI0 Flash driver uses indirect transfers, not the mapped window. + * Mapped OSPI needs its own accessible MPU regions before using this guard. */ + MPU->RNR = MPU_REGION_NUMBER0; + if ((MPU->RASR & MPU_RASR_ENABLE_Msk) == 0U) + { + ARM_MPU_SetRegionEx(MPU_REGION_NUMBER0, 0U, BOARD_EXT_GUARD_ATTR); + } +#endif + + ARM_MPU_Enable(MPU_MODE_PRIV_DEFAULT); + __DSB(); + __ISB(); + rt_hw_interrupt_enable(level); +} +#endif /* BSP_USING_LCD_MIPI */ + +/** + * @brief This function is executed in case of error occurrence. + * @param None + * @retval None + */ +void Error_Handler(void) +{ + /* USER CODE BEGIN Error_Handler */ + /* User can add his own implementation to report the HAL error return state */ + while (1) + { + } + /* USER CODE END Error_Handler */ +} + +/** System Clock Configuration +*/ +void SystemClock_Config(void) +{ + SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND); +} + +/** + * This is the timer interrupt service routine. + * + */ +void SysTick_Handler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + rt_tick_increase(); + + /* leave interrupt */ + rt_interrupt_leave(); +} + +void rt_hw_us_delay(rt_uint32_t us) +{ + rt_uint64_t required_ticks; + rt_uint32_t previous; + rt_uint32_t current; + rt_uint32_t elapsed = 0U; + rt_uint32_t reload = SysTick->LOAD; + + required_ticks = (rt_uint64_t)us * + (reload / (1000000U / RT_TICK_PER_SECOND)); + previous = SysTick->VAL; + while (elapsed < required_ticks) + { + current = SysTick->VAL; + if (current != previous) + { + elapsed += (current < previous) ? + (previous - current) : + (reload - current + previous); + previous = current; + } + } +} + +/** + * This function will initial GD32 board. + */ +void rt_hw_board_init() +{ + /* NVIC Configuration */ +#define NVIC_VTOR_MASK 0x3FFFFF80 +#ifdef VECT_TAB_RAM + /* Set the Vector Table base location at 0x10000000 */ + SCB->VTOR = (0x10000000 & NVIC_VTOR_MASK); +#else /* VECT_TAB_FLASH */ + /* Set the Vector Table base location at 0x08010000 */ + SCB->VTOR = (0x08010000 & NVIC_VTOR_MASK); +#endif + + /* Reset entry (or a bootloader handoff) must leave D-Cache disabled. */ + RT_ASSERT((SCB->CCR & SCB_CCR_DC_Msk) == 0U); + + /* Instruction caching does not change the SDRAM data attributes. */ + rt_hw_cpu_icache_enable(); + + SystemClock_Config(); + +#ifdef RT_USING_SERIAL + rt_hw_usart_init(); +#endif + +#ifdef RT_USING_COMPONENTS_INIT + rt_components_board_init(); +#endif + +#ifdef RT_USING_CONSOLE + rt_console_set_device(RT_CONSOLE_DEVICE_NAME); +#endif + +#ifdef BSP_USING_LCD_MIPI + board_mpu_config(); +#endif + /* Board drivers, including SDRAM, ran with uncached data accesses. */ + rt_hw_cpu_dcache_enable(); + +#ifdef RT_USING_HEAP + rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END); +#endif +} + +/*@}*/ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/board.h b/bsp/gd32/arm/gd32h77d-ginopi/board/board.h new file mode 100644 index 000000000000..f2d58361ee55 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/board.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2006-2024 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-08-20 BruceOu first implementation + */ +#ifndef __BOARD_H__ +#define __BOARD_H__ + +#if defined(SOC_GD32H77DIW) && !defined(GD32H77DXW) +#define GD32H77DXW +#endif + +#include "gd32h77x_78x.h" +#include "drv_usart.h" +#include "drv_gpio.h" + +#include "gd32h77x_78x_exti.h" + +#define EXT_SDRAM_BEGIN (0xC0000000U) /* the begining address of external SDRAM */ +#define EXT_SDRAM_END (EXT_SDRAM_BEGIN + (32U * 1024 * 1024)) /* the end address of external SDRAM */ +#define EXT_SDRAM_LCD_RESERVED_SIZE (3U * 1024U * 1024U) +#define EXT_SDRAM_LCD_SCANOUT_SIZE (2U * 1024U * 1024U) + +#ifdef BSP_USING_LCD_MIPI +#define SDRAM_HEAP_BEGIN (EXT_SDRAM_BEGIN + EXT_SDRAM_LCD_RESERVED_SIZE) +#else +#define SDRAM_HEAP_BEGIN EXT_SDRAM_BEGIN +#endif +#define SDRAM_HEAP_SIZE (EXT_SDRAM_END - SDRAM_HEAP_BEGIN) + +#ifdef BSP_USING_SDRAM +rt_bool_t rt_hw_sdram_is_ready(void); +#endif + +#define BSP_USB_HOST_VBUS_PORT_RCC RCU_GPIOF +#define BSP_USB_HOST_VBUS_PORT GPIOF +#define BSP_USB_HOST_VBUS_PIN GPIO_PIN_2 + +/* Internal SRAM memory size[Kbytes] <8-768>*/ +/* Default: 768*/ +#ifdef __ICCARM__ +/* Use *.icf ram symbal, to avoid hardcode.*/ +extern char __ICFEDIT_region_RAM_end__; +#define GD32_SRAM_END &__ICFEDIT_region_RAM_end__ +#else +#define GD32_SRAM_SIZE 768 +#define GD32_SRAM_END (0x24000000 + GD32_SRAM_SIZE * 1024) +#endif + +#ifdef __ARMCC_VERSION +extern int Image$$RW_IRAM1$$ZI$$Limit; +#define HEAP_BEGIN (&Image$$RW_IRAM1$$ZI$$Limit) +#elif __ICCARM__ +#pragma section="HEAP" +#define HEAP_BEGIN (__segment_end("HEAP")) +#else +extern int __bss_end; +#define HEAP_BEGIN (&__bss_end) +#endif + +#define HEAP_END GD32_SRAM_END + +#endif /* __BOARD_H__ */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/drv_filesystem.c b/bsp/gd32/arm/gd32h77d-ginopi/board/drv_filesystem.c new file mode 100644 index 000000000000..f93e9647b57a --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/drv_filesystem.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#ifdef BSP_USING_FLASH_FATFS +#include +#include "fal_cfg.h" +#endif + +#define DBG_TAG "app.filesystem" +#define DBG_LVL DBG_INFO +#include + +#if defined(BSP_USING_SDCARD_FATFS) || defined(BSP_USING_FLASH_FATFS) || defined(RT_USBH_MSTORAGE) +static const struct romfs_dirent _romfs_root[] = +{ +#ifdef BSP_USING_SDCARD_FATFS + {ROMFS_DIRENT_DIR, "sd", RT_NULL, 0}, +#endif +#ifdef BSP_USING_FLASH_FATFS + {ROMFS_DIRENT_DIR, "flash", RT_NULL, 0}, +#endif +#ifdef RT_USBH_MSTORAGE + {ROMFS_DIRENT_DIR, "udisk", RT_NULL, 0}, +#endif +}; +#endif + +const struct romfs_dirent romfs_root = +{ +#if defined(BSP_USING_SDCARD_FATFS) || defined(BSP_USING_FLASH_FATFS) || defined(RT_USBH_MSTORAGE) + ROMFS_DIRENT_DIR, "/", (rt_uint8_t *)_romfs_root, + sizeof(_romfs_root) / sizeof(_romfs_root[0]) +#else + ROMFS_DIRENT_DIR, "/", RT_NULL, 0 +#endif +}; + +#ifdef BSP_USING_FLASH_FATFS +static void qspi_flash_mount(void) +{ + const struct fal_partition *partition; + rt_device_t device; + rt_uint8_t probe_data; + int result; + + if (fal_init() <= 0) + { + LOG_E("FAL initialization failed"); + return; + } + + partition = fal_partition_find(GD25Q64E_FILESYSTEM_PART_NAME); + if (partition == RT_NULL || + fal_partition_read(partition, 0, &probe_data, sizeof(probe_data)) != + (int)sizeof(probe_data)) + { + LOG_E("QSPI flash is not accessible"); + return; + } + + device = rt_device_find(GD25Q64E_FILESYSTEM_PART_NAME); + if (device == RT_NULL) + { + device = fal_blk_device_create(GD25Q64E_FILESYSTEM_PART_NAME); + } + if (device == RT_NULL) + { + LOG_E("create FAL block device failed"); + return; + } + +#ifdef BSP_USING_FLASH_FS_AUTO_MOUNT + result = dfs_mount(GD25Q64E_FILESYSTEM_PART_NAME, + "/flash", "elm", 0, RT_NULL); + if (result == RT_EOK) + { + LOG_I("QSPI flash mounted at /flash"); + } + else + { + LOG_W("QSPI flash mount failed: %d; format with: mkfs -t elm %s", + rt_get_errno(), GD25Q64E_FILESYSTEM_PART_NAME); + LOG_W("then mount with: mount %s /flash elm", + GD25Q64E_FILESYSTEM_PART_NAME); + } +#endif +} +#endif /* BSP_USING_FLASH_FATFS */ + +#ifdef BSP_USING_FS_AUTO_MOUNT +static void sdcard_mount(void) +{ + rt_device_t device = rt_device_find("sd0"); + int result; + + if (device == RT_NULL) + { + LOG_W("SD card device not found"); + return; + } + + if (rt_device_init(device) != RT_EOK) + { + LOG_W("SD card not detected, skip mounting"); + return; + } + + result = dfs_mount("sd0", "/sd", "elm", 0, RT_NULL); + if (result == RT_EOK) + { + LOG_I("SD card mounted at /sd"); + } + else + { + LOG_E("SD card mount failed: %d", rt_get_errno()); + } +} +#endif + +static int filesystem_mount(void) +{ + if (dfs_mount(RT_NULL, "/", "rom", 0, &romfs_root) != RT_EOK) + { + LOG_E("ROM filesystem mount failed"); + return -RT_ERROR; + } + +#ifdef BSP_USING_FLASH_FATFS + qspi_flash_mount(); +#endif + +#ifdef BSP_USING_FS_AUTO_MOUNT + rt_thread_mdelay(500); + sdcard_mount(); +#endif + + return RT_EOK; +} +INIT_ENV_EXPORT(filesystem_mount); diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/drv_gt911.c b/bsp/gd32/arm/gd32h77d-ginopi/board/drv_gt911.c new file mode 100644 index 000000000000..051afa884758 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/drv_gt911.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-09-23 CYFS add GT911 touch support with reset configuration polling + */ + +#include +#include + +#include +#include + +#define DBG_TAG "drv.gt911.port" +#define DBG_LVL DBG_INFO +#include + +#define GT911_DEVICE_NAME "gt911" +#define GT911_I2C_BUS_NAME "hwi2c3" +#define GT911_RESET_PIN GET_PIN(H, 3) +#define GT911_INT_PIN GET_PIN(H, 5) +#define GT911_STARTUP_DELAY_MS 50U +#define GT911_CONFIG_TIMEOUT_MS 500U + +static rt_uint8_t gt911_reset_pin = GT911_RESET_PIN; +static struct rt_touch_config gt911_config; + +int rt_hw_gt911_port_init(void) +{ + struct rt_touch_info info; + rt_device_t touch_device; + char product_id[6] = {0}; + rt_err_t result; + rt_tick_t start; + + if (rt_device_find(GT911_DEVICE_NAME) != RT_NULL) + { + return RT_EOK; + } + + rt_memset(>911_config, 0, sizeof(gt911_config)); + gt911_config.dev_name = GT911_I2C_BUS_NAME; + gt911_config.irq_pin.pin = GT911_INT_PIN; + gt911_config.irq_pin.mode = PIN_MODE_INPUT_PULLUP; + gt911_config.user_data = >911_reset_pin; + + result = rt_hw_gt911_init(GT911_DEVICE_NAME, >911_config); + + /* The package selects address 0x5d with INT low, then must release INT. */ + rt_pin_mode(GT911_INT_PIN, PIN_MODE_INPUT_PULLUP); + + if (result != RT_EOK) + { + LOG_E("GT911 package init failed: %d", result); + return result; + } + + rt_thread_mdelay(GT911_STARTUP_DELAY_MS); + touch_device = rt_device_find(GT911_DEVICE_NAME); + if (touch_device == RT_NULL) + { + LOG_E("touch device %s was not registered", GT911_DEVICE_NAME); + return -RT_ERROR; + } + + rt_memset(&info, 0, sizeof(info)); + start = rt_tick_get(); + do + { + result = rt_device_control(touch_device, RT_TOUCH_CTRL_GET_INFO, &info); + if (result != RT_EOK) + { + LOG_E("get GT911 information failed: %d", result); + return result; + } + if (info.range_x != 0 && info.range_y != 0 && info.point_num != 0) + { + break; + } + /* The controller restores its configuration after the soft reset. */ + rt_thread_mdelay(20); + } + while (rt_tick_get() - start < rt_tick_from_millisecond(GT911_CONFIG_TIMEOUT_MS)); + + if (info.range_x == 0 || info.range_y == 0 || info.point_num == 0) + { + LOG_E("GT911 configuration is not ready after reset"); + return -RT_ETIMEOUT; + } + + if (rt_device_control(touch_device, RT_TOUCH_CTRL_GET_ID, product_id) != RT_EOK) + { + rt_strncpy(product_id, "GT911", sizeof(product_id)); + } + else + { + product_id[4] = '\0'; + } + product_id[sizeof(product_id) - 1] = '\0'; + + LOG_I("%s ready, id=%s, range=%dx%d, points=%d", GT911_DEVICE_NAME, + product_id, info.range_x, info.range_y, info.point_num); + + return RT_EOK; +} +INIT_DEVICE_EXPORT(rt_hw_gt911_port_init); diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/drv_lcd.c b/bsp/gd32/arm/gd32h77d-ginopi/board/drv_lcd.c new file mode 100644 index 000000000000..0e7cd4b9e631 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/drv_lcd.c @@ -0,0 +1,918 @@ +/* + * Copyright (c) 2006-2026 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-08-04 RTT add FL7707N MIPI DSI LCD support + * 2026-09-08 CYFS stabilize FL7707N timing, framebuffer swaps, and backlight control + */ + +#include +#include +#include + +#include "gd32h77x_78x_dsi.h" +#include "gd32h77x_78x_axiim.h" +#include "gd32h77x_78x_tli.h" + +#ifdef BSP_USING_LCD_MIPI + +#define DRV_DEBUG +#define LOG_TAG "drv.lcd" +#include + +#define LCD_DEVICE_NAME "lcd" +#define LCD_WIDTH 720U +#define LCD_HEIGHT 720U +#define LCD_BYTES_PER_PIXEL 2U +#define LCD_FRAMEBUFFER_SIZE (LCD_WIDTH * LCD_HEIGHT * LCD_BYTES_PER_PIXEL) +#define LCD_FRAMEBUFFER_COUNT 2U +#define LCD_FRAMEBUFFER_TOTAL_SIZE (LCD_FRAMEBUFFER_SIZE * LCD_FRAMEBUFFER_COUNT) +#define LCD_FRAMEBUFFER_ADDRESS EXT_SDRAM_BEGIN +#define LCD_FRAMEBUFFER_BACK_ADDRESS (LCD_FRAMEBUFFER_ADDRESS + LCD_FRAMEBUFFER_SIZE) +#define LCD_BACKLIGHT_PIN GET_PIN(A, 0) +#define LCD_BACKLIGHT_PWM_DEVICE_NAME "pwm1" +#define LCD_BACKLIGHT_PWM_CHANNEL 1U +#define LCD_BACKLIGHT_PWM_PERIOD_NS 50000U +#define LCD_BACKLIGHT_DEFAULT_LEVEL 100U +#define LCD_VSYNC_TIMEOUT_MS 100U + +#define LCD_HSYNC 60U +#define LCD_HBP 120U +#define LCD_HFP 106U +#define LCD_VSYNC 4U +#define LCD_VBP 20U +#define LCD_VFP 20U + +#define LCD_PIXEL_CLOCK 46125000U +#define LCD_DSI_LP_CLOCK 12500000U +#define LCD_DSI_HS_RATE 800000000U +#define LCD_DSI_FORMAT DSI_VIDEO_MODE_FMT_RGB565 +#define LCD_DSI_VIDEO_MODE DSI_BURST_MODE +#define LCD_DSI_LANES 2U +#define LCD_DSI_CHANNEL 0U +#define LCD_DSI_TIMEOUT 1000000U +#define LCD_DSI_CMD_MAX_BYTES 64U + +#define MIPI_DSI_DCS_SHORT_WRITE 0x05U +#define MIPI_DSI_DCS_SHORT_WRITE_PARAM 0x15U +#define MIPI_DSI_DCS_LONG_WRITE 0x39U +#define DIV_ROUND_UP(value, divisor) (((value) + (divisor) - 1U) / (divisor)) +#define MAX_VALUE(a, b) ((a) > (b) ? (a) : (b)) + +struct lcd_init_cmd +{ + rt_uint8_t len; + rt_uint16_t delay_ms; + rt_uint8_t data[LCD_DSI_CMD_MAX_BYTES]; +}; + +struct lcd_dphy_timing +{ + rt_uint8_t freq_mode; + rt_uint8_t esc_ck_sel; + rt_uint8_t pll_divider; + rt_uint16_t dlcfg; + rt_uint16_t ck_hs2lp; + rt_uint16_t ck_lp2hs; + rt_uint16_t lp_clkc; + rt_uint8_t dhs_prep; + rt_uint8_t dhs_zero; + rt_uint8_t dhs_trail; + rt_uint8_t dhs_exit; + rt_uint8_t ckhs_prep; + rt_uint8_t ckhs_zero; + rt_uint8_t ckhs_trail; + rt_uint8_t ckhs_exit; +}; + +struct gd32_lcd +{ + struct rt_device parent; + struct rt_device_graphic_info info; + struct rt_device_pwm *backlight_pwm; + rt_base_t backlight_pin; + rt_uint8_t brightness; + rt_bool_t backlight_powered; + rt_bool_t initialized; +}; + +static struct gd32_lcd lcd; +static struct rt_completion lcd_reload_completion; +static struct rt_device_rect_info lcd_dirty_rect; +static rt_bool_t lcd_dirty_valid; +static rt_bool_t lcd_reload_timeout_reported; + +static rt_err_t lcd_backlight_apply(void) +{ + rt_uint32_t pulse; + rt_err_t result; + + if (lcd.backlight_pwm == RT_NULL) + { + lcd.backlight_pwm = (struct rt_device_pwm *) + rt_device_find(LCD_BACKLIGHT_PWM_DEVICE_NAME); + if (lcd.backlight_pwm == RT_NULL) + { + LOG_E("cannot find %s for backlight", + LCD_BACKLIGHT_PWM_DEVICE_NAME); + return -RT_ENOSYS; + } + } + + if (!lcd.backlight_powered || (lcd.brightness == 0U)) + { + result = rt_pwm_disable(lcd.backlight_pwm, + LCD_BACKLIGHT_PWM_CHANNEL); + rt_pin_mode(lcd.backlight_pin, PIN_MODE_OUTPUT); + rt_pin_write(lcd.backlight_pin, PIN_LOW); + return result; + } + + pulse = (rt_uint32_t)(((rt_uint64_t)LCD_BACKLIGHT_PWM_PERIOD_NS * + lcd.brightness) / 100U); + result = rt_pwm_set(lcd.backlight_pwm, LCD_BACKLIGHT_PWM_CHANNEL, + LCD_BACKLIGHT_PWM_PERIOD_NS, pulse); + if (result != RT_EOK) + { + return result; + } + + return rt_pwm_enable(lcd.backlight_pwm, LCD_BACKLIGHT_PWM_CHANNEL); +} + +static rt_err_t lcd_backlight_set_brightness(rt_uint8_t brightness) +{ + if (brightness > 100U) + { + return -RT_EINVAL; + } + + lcd.brightness = brightness; + return lcd_backlight_apply(); +} + +static void lcd_framebuffer_flush(void) +{ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, + (void *)LCD_FRAMEBUFFER_ADDRESS, + LCD_FRAMEBUFFER_TOTAL_SIZE); +} + +static void lcd_framebuffer_fill(rt_uint16_t color) +{ + rt_uint16_t *framebuffer = (rt_uint16_t *)LCD_FRAMEBUFFER_ADDRESS; + rt_size_t index; + + for (index = 0U; + index < LCD_WIDTH * LCD_HEIGHT * LCD_FRAMEBUFFER_COUNT; + index++) + { + framebuffer[index] = color; + } + lcd_framebuffer_flush(); +} + +static const struct lcd_init_cmd panel_init_cmds[] = +{ + { 4, 0, { 0xB9, 0xF1, 0x12, 0x87 } }, + { 4, 0, { 0xB2, 0xB4, 0x03, 0x70 } }, + { 11, 0, { 0xB3, 0x10, 0x10, 0x28, 0x28, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00 } }, + { 2, 0, { 0xB4, 0x80 } }, + { 3, 0, { 0xB5, 0x0A, 0x0A } }, + { 3, 0, { 0xB6, 0x8D, 0x8D } }, + { 5, 0, { 0xB8, 0x26, 0x22, 0xF0, 0x13 } }, + { 28, 0, { 0xBA, 0x31, 0x81, 0x05, 0xF9, 0x0E, 0x0E, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, + 0x25, 0x00, 0x91, 0x0A, 0x00, 0x00, 0x01, 0x4F, + 0x01, 0x00, 0x00, 0x37 } }, + { 2, 0, { 0xBC, 0x47 } }, + { 6, 0, { 0xBF, 0x02, 0x10, 0x00, 0x80, 0x04 } }, + { 10, 0, { 0xC0, 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x12, 0x73, 0x00 } }, + { 18, 0, { 0xC1, 0x36, 0x00, 0x32, 0x32, 0x77, 0xE1, 0x77, 0x77, + 0xCC, 0xCC, 0xFF, 0xFF, 0x11, 0x11, 0x00, 0x00, 0x32 } }, + { 13, 0, { 0xC7, 0x10, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xED, 0xC5, 0x00, 0xA5 } }, + { 5, 0, { 0xC8, 0x10, 0x40, 0x1E, 0x03 } }, + { 2, 0, { 0xCC, 0x0B } }, + { 35, 0, { 0xE0, 0x00, 0x0A, 0x0F, 0x2A, 0x33, 0x3F, 0x44, 0x39, + 0x06, 0x0C, 0x0E, 0x14, 0x15, 0x13, 0x15, 0x10, 0x18, + 0x00, 0x0A, 0x0F, 0x2A, 0x33, 0x3F, 0x44, 0x39, 0x06, + 0x0C, 0x0E, 0x14, 0x15, 0x13, 0x15, 0x10, 0x18 } }, + { 8, 0, { 0xE1, 0x11, 0x11, 0x91, 0x00, 0x00, 0x00, 0x00 } }, + { 15, 0, { 0xE3, 0x07, 0x07, 0x0B, 0x0B, 0x0B, 0x0B, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x04, 0xC0, 0x10 } }, + { 64, 0, { 0xE9, 0xC8, 0x10, 0x0A, 0x00, 0x00, 0x80, 0x81, 0x12, + 0x31, 0x23, 0x4F, 0x86, 0xA0, 0x00, 0x47, 0x08, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x00, 0x98, 0x02, 0x8B, 0xAF, 0x46, 0x02, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x98, 0x13, 0x8B, 0xAF, 0x57, + 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 } }, + { 62, 0, { 0xEA, 0x97, 0x0C, 0x09, 0x09, 0x09, 0x78, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9F, 0x31, 0x8B, 0xA8, 0x31, + 0x75, 0x88, 0x88, 0x88, 0x88, 0x88, 0x9F, 0x20, 0x8B, + 0xA8, 0x20, 0x64, 0x88, 0x88, 0x88, 0x88, 0x88, 0x23, + 0x00, 0x00, 0x02, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x80, 0x81, 0x00, 0x00, 0x00, 0x00 } }, + { 4, 0, { 0xEF, 0xFF, 0xFF, 0x01 } }, + { 2, 0, { 0x3A, 0x50 } }, + { 1, 250, { 0x11 } }, + { 1, 50, { 0x29 } }, +}; + +static rt_err_t dsi_wait_fifo(uint32_t flag, FlagStatus expected) +{ + rt_uint32_t timeout = LCD_DSI_TIMEOUT; + + while ((dsi_fifo_status_get(flag) != expected) && (timeout > 0U)) + { + timeout--; + } + + return timeout > 0U ? RT_EOK : -RT_ETIMEOUT; +} + +static rt_err_t dsi_write_cmd(const rt_uint8_t *data, rt_size_t len) +{ + rt_uint32_t word; + rt_size_t offset; + rt_size_t remaining; + rt_uint8_t type; + + /* Panel commands must not read beyond an initialization entry's payload. */ + if ((data == RT_NULL) || (len == 0U) || (len > LCD_DSI_CMD_MAX_BYTES)) + { + return -RT_EINVAL; + } + + if (dsi_wait_fifo(DSI_FIFOSTAT_LPCTLFIFOF, RESET) != RT_EOK) + { + return -RT_ETIMEOUT; + } + + if (len <= 2U) + { + type = len == 1U ? MIPI_DSI_DCS_SHORT_WRITE : MIPI_DSI_DCS_SHORT_WRITE_PARAM; + word = data[0]; + if (len == 2U) + { + word |= (rt_uint32_t)data[1] << 8; + } + dsi_dcs_word_count_set(0U); + dsi_lp_control_set(type, LCD_DSI_CHANNEL, word); + } + else + { + dsi_dcs_word_count_set((uint32_t)len); + for (offset = 0U; offset < len; offset += sizeof(word)) + { + if (dsi_wait_fifo(DSI_FIFOSTAT_LPDTFIFOF, RESET) != RT_EOK) + { + return -RT_ETIMEOUT; + } + + word = 0U; + remaining = len - offset; + if (remaining > sizeof(word)) + { + remaining = sizeof(word); + } + rt_memcpy(&word, data + offset, remaining); + dsi_lp_data_write(word); + } + dsi_lp_control_set(MIPI_DSI_DCS_LONG_WRITE, LCD_DSI_CHANNEL, (uint32_t)len); + } + + if ((dsi_wait_fifo(DSI_FIFOSTAT_LPCTLFIFOE, SET) != RT_EOK) || + (dsi_wait_fifo(DSI_FIFOSTAT_LPDTFIFOE, SET) != RT_EOK)) + { + return -RT_ETIMEOUT; + } + + return RT_EOK; +} + +static void dsi_command_mode_supported_set(uint32_t supported) +{ + DSI_FUNCPRG = (DSI_FUNCPRG & ~DSI_FUNCPRG_SDTWCM) | + ((supported << 13) & DSI_FUNCPRG_SDTWCM); +} + +static void dsi_wrapper_rgb565_set(void) +{ + DSI_WRGBDF = 1U; +} + +static rt_uint32_t dsi_byte_clock_cycles_get(rt_uint32_t time_ns, + rt_uint32_t ui_count) +{ + rt_uint64_t scaled_time; + + /* Keep nanoseconds and unit intervals exact until the final ceiling. */ + scaled_time = (rt_uint64_t)time_ns * LCD_DSI_HS_RATE + + (rt_uint64_t)ui_count * 1000000000ULL; + return (rt_uint32_t)DIV_ROUND_UP(scaled_time, 8000000000ULL); +} + +static void dsi_timing_calculate(struct lcd_dphy_timing *timing) +{ + timing->pll_divider = LCD_DSI_HS_RATE / 2U / LCD_DSI_LP_CLOCK; + timing->esc_ck_sel = LCD_DSI_LP_CLOCK > 20000000U ? 1U : 0U; + timing->freq_mode = LCD_DSI_HS_RATE > 200000000U ? 1U : 0U; + timing->lp_clkc = dsi_byte_clock_cycles_get(60U, 0U) - 1U; + timing->dhs_prep = dsi_byte_clock_cycles_get(40U, 4U) - 1U; + timing->dhs_zero = dsi_byte_clock_cycles_get(90U, 6U) - 1U; + timing->dhs_trail = MAX_VALUE(1U, dsi_byte_clock_cycles_get(260U, 4U)) - 1U; + timing->dhs_exit = dsi_byte_clock_cycles_get(230U, 0U) - 1U; + timing->ckhs_prep = dsi_byte_clock_cycles_get(50U, 0U) - 1U; + timing->ckhs_zero = dsi_byte_clock_cycles_get(270U, 0U) - 1U; + timing->ckhs_trail = dsi_byte_clock_cycles_get(200U, 0U) - 1U; + timing->ckhs_exit = dsi_byte_clock_cycles_get(230U, 0U) - 1U; + timing->dlcfg = timing->lp_clkc + timing->dhs_prep + timing->dhs_zero + 4U; + timing->ck_hs2lp = timing->ckhs_trail + timing->ckhs_exit + 3U; + timing->ck_lp2hs = timing->lp_clkc + timing->ckhs_prep + timing->ckhs_zero + 6U; +} + +static rt_uint32_t dsi_horizontal_count_get(rt_uint32_t pixels, rt_uint32_t byte_clock) +{ + return (rt_uint32_t)(((rt_uint64_t)pixels * byte_clock) / LCD_PIXEL_CLOCK); +} + +static rt_uint32_t dsi_horizontal_count_round_up_get(rt_uint32_t pixels, + rt_uint32_t byte_clock) +{ + return (rt_uint32_t)((((rt_uint64_t)pixels * byte_clock) + LCD_PIXEL_CLOCK - 1U) / + LCD_PIXEL_CLOCK); +} + +static rt_err_t dsi_host_init(void) +{ + struct lcd_dphy_timing timing; + rt_uint32_t timeout = LCD_DSI_TIMEOUT; + rt_uint32_t byte_clock = LCD_DSI_HS_RATE / 8U; + rt_uint32_t hsa_count; + rt_uint32_t hbp_count; + rt_uint32_t hfp_count; + rt_uint32_t hact_count; + rt_uint32_t htotal_count; + + rt_memset(&timing, 0, sizeof(timing)); + dsi_timing_calculate(&timing); + hsa_count = dsi_horizontal_count_get(LCD_HSYNC, byte_clock); + hbp_count = dsi_horizontal_count_get(LCD_HBP, byte_clock); + hact_count = dsi_horizontal_count_get(LCD_WIDTH, byte_clock); + htotal_count = dsi_horizontal_count_round_up_get(LCD_HSYNC + LCD_HBP + + LCD_WIDTH + LCD_HFP, + byte_clock); + hfp_count = htotal_count - hsa_count - hbp_count - hact_count; + + rcu_periph_clock_enable(RCU_DSI); + rcu_dsi_preceding_stage_clock_config(RCU_DSIPRESEL_HXTAL); + rcu_dsi_clock_div_config(RCU_DSI_DIV2); + + dsi_dphy_dfe_reset_enable(); + dsi_eot_transmission_disable(); + dsi_clock_stopping_enable(); + dsi_data_lanes_number_set(LCD_DSI_LANES); + dsi_command_mode_supported_set(1U); + dsi_wrapper_lp_cmd_data_source_set(DSI_LP_DATASRC_LPDATA); + dsi_wrapper_tli_dma_mode_config(DSI_TLI_DMA_MODE_DISABLE); + dsi_video_mode_virtual_channel_set(LCD_DSI_CHANNEL); + dsi_wrapper_rgb565_set(); + dsi_video_mode_color_format_set(LCD_DSI_FORMAT); + dsi_video_mode_format_set(LCD_DSI_VIDEO_MODE); + dsi_video_bta_disable(); + + dsi_hs_tx_timeout_set(0xFFFFFFU); + dsi_lp_rx_timeout_set(0xFFFFFFU); + dsi_turnaround_timeout_set(0x1FU); + dsi_device_reset_timer_set(0xFFFFU); + dsi_dpi_resolution_set(LCD_WIDTH, LCD_HEIGHT); + dsi_horizontal_sync_active_count_set(hsa_count); + dsi_horizontal_back_porch_count_set(hbp_count); + dsi_horizontal_front_porch_count_set(hfp_count); + dsi_horizontal_active_count_set(hact_count); + dsi_vertical_sync_active_count_set(LCD_VSYNC); + dsi_vertical_back_porch_count_set(LCD_VBP); + dsi_vertical_front_porch_count_set(LCD_VFP); + dsi_init_counter_set(0xD07U); + + dsi_error_auto_recovery_disable(DSI_ERR_AUTO_REC_ECC_MUL); + dsi_error_auto_recovery_disable(DSI_ERR_AUTO_REC_INVLD_DT); + dsi_error_auto_recovery_disable(DSI_ERR_AUTO_REC_HI_CONT); + dsi_error_auto_recovery_disable(DSI_ERR_AUTO_REC_LO_CONT); + dsi_error_auto_recovery_disable(DSI_ERR_AUTO_REC_HS_RX_TO); + dsi_error_auto_recovery_disable(DSI_ERR_AUTO_REC_LP_RX_TO); + + dsi_dphy_hs_prepare_time_set(timing.dhs_prep); + dsi_dphy_hs_zero_time_set(timing.dhs_zero); + dsi_dphy_hs_trail_time_set(timing.dhs_trail); + dsi_dphy_hs_exit_time_set(timing.dhs_exit); + dsi_dphy_clk_prepare_time_set(timing.ckhs_prep); + dsi_dphy_clk_zero_time_set(timing.ckhs_zero); + dsi_dphy_clk_trail_time_set(timing.ckhs_trail); + dsi_dphy_clk_exit_time_set(timing.ckhs_exit); + + DSI_DPHY_TRIMCTL1 = ((rt_uint32_t)timing.pll_divider & 0x7FU) | + (((rt_uint32_t)timing.esc_ck_sel & 0x1U) << 31); + DSI_DPHY_TRIMCTL1 |= DSI_DPHY_TRIMCTL1_CDPD0; + dsi_ddr_freq_mode_set(timing.freq_mode); + dsi_lp_byte_clk_config(timing.lp_clkc); + dsi_data_lane_switch_count_set(timing.dlcfg); + dsi_clk_hs_to_lp_switch_count_set(timing.ck_hs2lp); + dsi_clk_lp_to_hs_switch_count_set(timing.ck_lp2hs); + dsi_device_ready_set(1U); + + while (((DSI_INTSTAT0 & DSI_INTSTAT0_INIT_DONE) == 0U) && (timeout > 0U)) + { + timeout--; + } + + if (timeout == 0U) + { + return -RT_ETIMEOUT; + } + if ((DSI_WINTSTAT & DSI_WINTSTAT_PLLLKS) == 0U) + { + LOG_E("DSI DPHY PLL failed to lock"); + return -RT_ETIMEOUT; + } + + return RT_EOK; +} + +static rt_err_t panel_init(void) +{ + rt_size_t index; + rt_err_t result; + + lcd.backlight_pin = LCD_BACKLIGHT_PIN; + + rt_pin_mode(lcd.backlight_pin, PIN_MODE_OUTPUT); + rt_pin_write(lcd.backlight_pin, PIN_LOW); + + /* LCD reset is tied to MCU_NRST through R13 on GD32H77x_EVB_V0.1. */ + rt_thread_mdelay(120); + + result = dsi_host_init(); + if (result != RT_EOK) + { + LOG_E("DSI host init timed out"); + return result; + } + + for (index = 0U; index < sizeof(panel_init_cmds) / sizeof(panel_init_cmds[0]); index++) + { + result = dsi_write_cmd(panel_init_cmds[index].data, panel_init_cmds[index].len); + if (result != RT_EOK) + { + LOG_E("panel command 0x%02x failed", panel_init_cmds[index].data[0]); + return result; + } + if (panel_init_cmds[index].delay_ms > 0U) + { + rt_thread_mdelay(panel_init_cmds[index].delay_ms); + } + } + + LOG_I("FL7707N initialized with %u commands", (rt_uint32_t)index); + + return RT_EOK; +} + +static rt_err_t mipi_power_init(void) +{ + rt_uint32_t timeout = LCD_DSI_TIMEOUT; + + pmu_mipi_ldo_enable(); + while ((pmu_flag_get(PMU_FLAG_RDYF) == RESET) && (timeout > 0U)) + { + timeout--; + } + if (timeout == 0U) + { + return -RT_ETIMEOUT; + } + + pmu_mipi_power_enable(); + pmu_mipi_output_isolation_enable(); + return RT_EOK; +} + +static rt_err_t tli_clock_init(void) +{ + RCU_PLLALL &= ~(RCU_PLLALL_PLLSEL | RCU_PLLALL_PLL2VCOSEL | RCU_PLLALL_PLL2RNG); + RCU_PLLALL |= RCU_PLLSRC_HXTAL | RCU_PLL2VCO_192M_836M | RCU_PLL2RNG_1M_2M; + + if (rcu_pll2_config(25U, 369U, 1U, 1U, 4U) != SUCCESS) + { + LOG_E("invalid PLL2 configuration"); + return -RT_EINVAL; + } + + rcu_tli_clock_div_config(RCU_PLL2R_DIV2); + rcu_pll_clock_output_enable(RCU_PLL2R); + rcu_osci_on(RCU_PLL2_CK); + if (rcu_osci_stab_wait(RCU_PLL2_CK) != SUCCESS) + { + LOG_E("PLL2 failed to lock"); + return -RT_ETIMEOUT; + } + + LOG_I("TLI pixel clock: %u Hz", rcu_clock_freq_get(CK_PLL2R) / 2U); + rcu_periph_clock_enable(RCU_TLI); + return RT_EOK; +} + +static void lcd_tli_init(void) +{ + tli_parameter_struct tli_config; + tli_layer_parameter_struct layer_config; + + rt_memset(&tli_config, 0, sizeof(tli_config)); + tli_config.signalpolarity_hs = TLI_HSYN_ACTIVE_LOW; + tli_config.signalpolarity_vs = TLI_VSYN_ACTIVE_LOW; + tli_config.signalpolarity_de = TLI_DE_ACTIVE_LOW; + tli_config.signalpolarity_pixelck = TLI_PIXEL_CLOCK_TLI; + tli_config.synpsz_hpsz = LCD_HSYNC - 1U; + tli_config.backpsz_hbpsz = LCD_HSYNC + LCD_HBP - 1U; + tli_config.activesz_hasz = LCD_HSYNC + LCD_HBP + LCD_WIDTH - 1U; + tli_config.totalsz_htsz = LCD_HSYNC + LCD_HBP + LCD_WIDTH + LCD_HFP - 1U; + tli_config.synpsz_vpsz = LCD_VSYNC - 1U; + tli_config.backpsz_vbpsz = LCD_VSYNC + LCD_VBP - 1U; + tli_config.activesz_vasz = LCD_VSYNC + LCD_VBP + LCD_HEIGHT - 1U; + tli_config.totalsz_vtsz = LCD_VSYNC + LCD_VBP + LCD_HEIGHT + LCD_VFP - 1U; + tli_config.backcolor_red = 0U; + tli_config.backcolor_green = 0U; + tli_config.backcolor_blue = 0U; + tli_init(&tli_config); + + rt_memset(&layer_config, 0, sizeof(layer_config)); + layer_config.layer_window_leftpos = LCD_HSYNC + LCD_HBP; + layer_config.layer_window_rightpos = LCD_HSYNC + LCD_HBP + LCD_WIDTH - 1U; + layer_config.layer_window_toppos = LCD_VSYNC + LCD_VBP; + layer_config.layer_window_bottompos = LCD_VSYNC + LCD_VBP + LCD_HEIGHT - 1U; + layer_config.layer_ppf = LAYER_PPF_RGB565; + layer_config.layer_sa = 255U; + layer_config.layer_default_alpha = 0U; + layer_config.layer_acf1 = LAYER_ACF1_PASA; + layer_config.layer_acf2 = LAYER_ACF2_PASA; + layer_config.layer_frame_bufaddr = LCD_FRAMEBUFFER_ADDRESS; + layer_config.layer_frame_line_length = LCD_WIDTH * LCD_BYTES_PER_PIXEL + 7U; + layer_config.layer_frame_buf_stride_offset = LCD_WIDTH * LCD_BYTES_PER_PIXEL; + layer_config.layer_frame_total_line_number = LCD_HEIGHT; + tli_layer_init(LAYER0, &layer_config); + tli_dither_config(TLI_DITHER_DISABLE); + tli_layer_enable(LAYER0); + tli_reload_config(TLI_REQUEST_RELOAD_EN); + AXI_SPX_RDQOS_CTL(SLAVE_PORT5) = + (AXI_SPX_RDQOS_CTL(SLAVE_PORT5) & ~AXI_SPX_RDQOS_CTL_RDQOS) | 0xFU; + + rt_completion_init(&lcd_reload_completion); + tli_interrupt_disable(TLI_INT_LM | TLI_INT_LCR | TLI_INT_FE | TLI_INT_TE); + tli_interrupt_flag_clear(TLI_INT_FLAG_LM | TLI_INT_FLAG_LCR | + TLI_INT_FLAG_FE | TLI_INT_FLAG_TE); + nvic_irq_enable(TLI_IRQn, 5U, 0U); + tli_enable(); +} + +void TLI_IRQHandler(void) +{ + rt_interrupt_enter(); + + if (tli_interrupt_flag_get(TLI_INT_FLAG_LCR) != RESET) + { + tli_interrupt_disable(TLI_INT_LCR); + tli_interrupt_flag_clear(TLI_INT_FLAG_LCR); + rt_completion_done(&lcd_reload_completion); + } + __DSB(); + rt_interrupt_leave(); +} + +static rt_err_t lcd_hardware_init(void) +{ + rt_err_t result; + + if (!rt_hw_sdram_is_ready()) + { + LOG_E("SDRAM is not ready"); + return -RT_EIO; + } + RT_ASSERT(LCD_FRAMEBUFFER_TOTAL_SIZE <= EXT_SDRAM_LCD_RESERVED_SIZE); +#ifdef BSP_USING_OV7670 + RT_ASSERT(LCD_FRAMEBUFFER_TOTAL_SIZE <= EXT_SDRAM_LCD_SCANOUT_SIZE); +#endif + + result = mipi_power_init(); + if (result != RT_EOK) + { + LOG_E("MIPI LDO ready timed out"); + return result; + } + + dsi_deinit(); + + lcd_framebuffer_fill(0x0000U); + + result = panel_init(); + if (result != RT_EOK) + { + return result; + } + + result = tli_clock_init(); + if (result != RT_EOK) + { + LOG_E("TLI clock init failed"); + return result; + } + + lcd_tli_init(); + DSI_INTSTAT0 = DSI_INTSTAT0_DPILTO | DSI_INTSTAT0_DPIPE; + rt_thread_mdelay(40U); + + lcd.backlight_powered = RT_TRUE; + result = lcd_backlight_apply(); + if (result != RT_EOK) + { + LOG_E("backlight PWM initialization failed: %d", result); + return result; + } + LOG_I("backlight %u%% on %s channel %u (PA0 TIMER1_CH0)", + lcd.brightness, LCD_BACKLIGHT_PWM_DEVICE_NAME, + LCD_BACKLIGHT_PWM_CHANNEL); + return RT_EOK; +} + +static rt_err_t lcd_device_init(rt_device_t device) +{ + rt_err_t result; + + if (lcd.initialized) + { + return RT_EOK; + } + + result = lcd_hardware_init(); + if (result == RT_EOK) + { + lcd.initialized = RT_TRUE; + } + return result; +} + +static rt_err_t lcd_dirty_rect_include(const struct rt_device_rect_info *rect) +{ + rt_uint32_t right; + rt_uint32_t bottom; + rt_uint32_t dirty_right; + rt_uint32_t dirty_bottom; + + if (rect == RT_NULL) + { + lcd_dirty_rect.x = 0U; + lcd_dirty_rect.y = 0U; + lcd_dirty_rect.width = LCD_WIDTH; + lcd_dirty_rect.height = LCD_HEIGHT; + lcd_dirty_valid = RT_TRUE; + return RT_EOK; + } + + right = (rt_uint32_t)rect->x + rect->width; + bottom = (rt_uint32_t)rect->y + rect->height; + if ((rect->width == 0U) || (rect->height == 0U) || + (right > LCD_WIDTH) || (bottom > LCD_HEIGHT)) + { + return -RT_EINVAL; + } + + if (!lcd_dirty_valid) + { + lcd_dirty_rect = *rect; + lcd_dirty_valid = RT_TRUE; + return RT_EOK; + } + + dirty_right = (rt_uint32_t)lcd_dirty_rect.x + lcd_dirty_rect.width; + dirty_bottom = (rt_uint32_t)lcd_dirty_rect.y + lcd_dirty_rect.height; + if (rect->x < lcd_dirty_rect.x) + { + lcd_dirty_rect.x = rect->x; + } + if (rect->y < lcd_dirty_rect.y) + { + lcd_dirty_rect.y = rect->y; + } + if (right > dirty_right) + { + dirty_right = right; + } + if (bottom > dirty_bottom) + { + dirty_bottom = bottom; + } + lcd_dirty_rect.width = dirty_right - lcd_dirty_rect.x; + lcd_dirty_rect.height = dirty_bottom - lcd_dirty_rect.y; + + return RT_EOK; +} + +static void lcd_cache_flush(void *framebuffer, + const struct rt_device_rect_info *rect) +{ + rt_uint8_t *base = (rt_uint8_t *)framebuffer; + rt_uint16_t row; + + if (rect == RT_NULL) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, base, LCD_FRAMEBUFFER_SIZE); + return; + } + + if ((rect->x == 0U) && (rect->width == LCD_WIDTH)) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, + base + rect->y * LCD_WIDTH * LCD_BYTES_PER_PIXEL, + rect->height * LCD_WIDTH * LCD_BYTES_PER_PIXEL); + return; + } + + for (row = 0U; row < rect->height; row++) + { + rt_uint8_t *address = base + + ((rect->y + row) * LCD_WIDTH + rect->x) * LCD_BYTES_PER_PIXEL; + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, + address, + rect->width * LCD_BYTES_PER_PIXEL); + } +} + +static rt_err_t lcd_pan_display(void *framebuffer) +{ + rt_ubase_t address = (rt_ubase_t)framebuffer; + + if ((address != LCD_FRAMEBUFFER_ADDRESS) && + (address != LCD_FRAMEBUFFER_BACK_ADDRESS)) + { + return -RT_EINVAL; + } + if ((TLI_RL & (TLI_RL_RQR | TLI_RL_FBR)) != 0U) + { + return -RT_EBUSY; + } + + /* Commit pixels before asking the scanout hardware to use this buffer. */ + if (lcd_dirty_valid) + { + lcd_cache_flush(framebuffer, &lcd_dirty_rect); + } + else + { + lcd_cache_flush(framebuffer, RT_NULL); + } + + tli_interrupt_disable(TLI_INT_LCR); + tli_interrupt_flag_clear(TLI_INT_FLAG_LCR); + (void)rt_completion_wait(&lcd_reload_completion, 0); + TLI_LXFBADDR(LAYER0) = address; + __DSB(); + /* A sampled VDE/line IRQ cannot guarantee the task is still in blanking. + * FBR makes the actual address change atomic with the hardware frame boundary. */ + tli_reload_config(TLI_FRAME_BLANK_RELOAD_EN); + tli_interrupt_enable(TLI_INT_LCR); + __DSB(); + lcd_dirty_valid = RT_FALSE; + + return RT_EOK; +} + +static rt_err_t lcd_wait_vsync(void) +{ + rt_tick_t start = rt_tick_get(); + rt_tick_t timeout = rt_tick_from_millisecond(LCD_VSYNC_TIMEOUT_MS); + + while ((TLI_RL & (TLI_RL_RQR | TLI_RL_FBR)) != 0U) + { + rt_tick_t elapsed = rt_tick_get() - start; + + if (elapsed >= timeout) + { + if ((TLI_RL & (TLI_RL_RQR | TLI_RL_FBR)) == 0U) + { + break; + } + tli_interrupt_disable(TLI_INT_LCR); + if (!lcd_reload_timeout_reported) + { + LOG_E("TLI layer reload timed out"); + lcd_reload_timeout_reported = RT_TRUE; + } + return -RT_ETIMEOUT; + } + (void)rt_completion_wait(&lcd_reload_completion, timeout - elapsed); + } + + /* Hardware completion, not just waking the thread, releases the old buffer. */ + __DSB(); + lcd_reload_timeout_reported = RT_FALSE; + return RT_EOK; +} + +static rt_err_t lcd_device_control(rt_device_t device, int cmd, void *args) +{ + switch (cmd) + { + case RTGRAPHIC_CTRL_RECT_UPDATE: + return lcd_dirty_rect_include((const struct rt_device_rect_info *)args); + + case RTGRAPHIC_CTRL_POWERON: + lcd.backlight_powered = RT_TRUE; + return lcd_backlight_apply(); + + case RTGRAPHIC_CTRL_POWEROFF: + lcd.backlight_powered = RT_FALSE; + return lcd_backlight_apply(); + + case RTGRAPHIC_CTRL_SET_BRIGHTNESS: + if (args == RT_NULL) + { + return -RT_EINVAL; + } + return lcd_backlight_set_brightness(*(rt_uint8_t *)args); + + case RTGRAPHIC_CTRL_GET_BRIGHTNESS: + if (args == RT_NULL) + { + return -RT_EINVAL; + } + *(rt_uint8_t *)args = lcd.brightness; + break; + + case RTGRAPHIC_CTRL_GET_INFO: + if (args == RT_NULL) + { + return -RT_EINVAL; + } + rt_memcpy(args, &lcd.info, sizeof(lcd.info)); + break; + + case RTGRAPHIC_CTRL_PAN_DISPLAY: + if (args == RT_NULL) + { + return -RT_EINVAL; + } + return lcd_pan_display(args); + + case RTGRAPHIC_CTRL_WAIT_VSYNC: + return lcd_wait_vsync(); + + default: + return -RT_EINVAL; + } + + return RT_EOK; +} + +#ifdef RT_USING_DEVICE_OPS +static const struct rt_device_ops lcd_device_ops = +{ + lcd_device_init, + RT_NULL, + RT_NULL, + RT_NULL, + RT_NULL, + lcd_device_control, +}; +#endif + +static int rt_hw_lcd_init(void) +{ + rt_memset(&lcd, 0, sizeof(lcd)); + lcd.brightness = LCD_BACKLIGHT_DEFAULT_LEVEL; + lcd.info.pixel_format = RTGRAPHIC_PIXEL_FORMAT_RGB565; + lcd.info.bits_per_pixel = 16U; + lcd.info.pitch = LCD_WIDTH * LCD_BYTES_PER_PIXEL; + lcd.info.width = LCD_WIDTH; + lcd.info.height = LCD_HEIGHT; + lcd.info.framebuffer = (void *)LCD_FRAMEBUFFER_ADDRESS; + lcd.info.smem_len = LCD_FRAMEBUFFER_TOTAL_SIZE; + lcd.parent.type = RT_Device_Class_Graphic; +#ifdef RT_USING_DEVICE_OPS + lcd.parent.ops = &lcd_device_ops; +#else + lcd.parent.init = lcd_device_init; + lcd.parent.control = lcd_device_control; +#endif + lcd.parent.user_data = &lcd.info; + + return rt_device_register(&lcd.parent, LCD_DEVICE_NAME, RT_DEVICE_FLAG_RDWR); +} +INIT_DEVICE_EXPORT(rt_hw_lcd_init); + +#endif /* BSP_USING_LCD_MIPI */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/drv_ov7670.c b/bsp/gd32/arm/gd32h77d-ginopi/board/drv_ov7670.c new file mode 100644 index 000000000000..759e79d438c0 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/drv_ov7670.c @@ -0,0 +1,888 @@ +/* + * Copyright (c) 2006-2026 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-08-06 RTT add OV7670 camera support + * 2026-09-08 CYFS synchronize frame capture completion and retain error checks + */ + +#include +#include +#include + +#include "drv_ov7670.h" +#include "ports/camera/sensor.h" +#include "gd32h77x_78x_dci.h" +#include "gd32h77x_78x_dma.h" +#include "gd32h77x_78x_i2c.h" +#include "gd32h77x_78x_misc.h" +#include "gd32h77x_78x_rcu.h" +#include "gd32h77x_78x_timer.h" + +#ifdef BSP_USING_OV7670 + +#define DBG_TAG "drv.ov7670" +#define DBG_LVL DBG_INFO +#include + +#define OV7670_I2C_BUS_NAME "hwi2c2" +#define OV7670_SCCB_ADDRESS 0x21U +#define OV7670_SCCB_WIRE_ADDRESS (OV7670_SCCB_ADDRESS << 1U) +#define OV7670_XCLK_TARGET_HZ 24000000U +#define OV7670_CAPTURE_TIMEOUT_MS 500U +#define OV7670_SCCB_PSC 0U +#define OV7670_SCCB_SCL_DELAY 1U +#define OV7670_SCCB_SDA_DELAY 0U +#define OV7670_SCCB_SCL_HIGH 0x98U +#define OV7670_SCCB_SCL_LOW 0xF1U +#define OV7670_SCCB_RETRY_COUNT 3U +#define OV7670_SCCB_RETRY_DELAY_US 1000U +#define OV7670_SCCB_TIMEOUT_MS 20U +#define OV7670_SCCB_RECOVERY_DELAY_US 5U +#define OV7670_SCCB_RECOVERY_PULSES 9U +#define OV7670_SCCB_ERROR_FLAGS (I2C_FLAG_NACK | I2C_FLAG_BERR | \ + I2C_FLAG_LOSTARB | I2C_FLAG_OUERR | \ + I2C_FLAG_TIMEOUT) +#define OV7670_SCCB_CLEAR_FLAGS (OV7670_SCCB_ERROR_FLAGS | \ + I2C_FLAG_STPDET) +#define OV7670_CLOCK_SAMPLE_COUNT 4096U +#define OV7670_CAPTURE_LINE_WORDS 162U +#define OV7670_CAPTURE_LINE_BYTES (OV7670_CAPTURE_LINE_WORDS * \ + sizeof(rt_uint32_t)) +#define OV7670_CAPTURE_SIZE (OV7670_CAPTURE_LINE_BYTES * \ + OV7670_HEIGHT) +#define OV7670_DMA_WORD_COUNT (OV7670_CAPTURE_SIZE / \ + sizeof(rt_uint32_t)) +#define OV7670_LINE_CROP_BYTES ((OV7670_CAPTURE_LINE_BYTES - \ + OV7670_WIDTH * \ + OV7670_BYTES_PER_PIXEL) / 2U) +#define OV7670_DMA_FLAGS (DMA_INT_FLAG_FEE | DMA_INT_FLAG_SDE | \ + DMA_INT_FLAG_TAE | DMA_INT_FLAG_FTF) +#define OV7670_DMA_INTERRUPTS (DMA_INT_FEE | DMA_INT_SDE | \ + DMA_INT_TAE | DMA_INT_FTF) + +struct ov7670_device +{ + struct rt_device parent; + struct rt_i2c_bus_device *bus; + struct rt_mutex lock; + struct ov7670_info info; + rt_bool_t initialized; +}; + +static struct ov7670_device ov7670; +rt_align(32) static rt_uint8_t ov7670_frame_buffer[OV7670_CAPTURE_SIZE]; +static struct rt_completion ov7670_capture_completion; +static volatile rt_bool_t ov7670_capture_active; +static volatile rt_err_t ov7670_capture_result; +static volatile rt_bool_t ov7670_dma_complete; +static volatile rt_bool_t ov7670_frame_complete; +static volatile rt_uint32_t ov7670_capture_flags; + +static rt_uint32_t ov7670_timer_clock_get(void) +{ + rt_uint32_t ahb_clock = rcu_clock_freq_get(CK_AHB); + rt_uint32_t timer_clock = rcu_clock_freq_get(CK_APB2); + rt_uint32_t multiplier; + + multiplier = (RCU_CFG1 & RCU_CFG1_TIMERSEL) ? 4U : 2U; + timer_clock *= multiplier; + if (timer_clock > ahb_clock) + { + timer_clock = ahb_clock; + } + + return timer_clock; +} + +static rt_uint32_t ov7670_xclk_divisor_get(void) +{ + return (ov7670_timer_clock_get() + OV7670_XCLK_TARGET_HZ / 2U) / + OV7670_XCLK_TARGET_HZ; +} + +static void ov7670_xclk_start(void) +{ + timer_parameter_struct timer_config; + timer_oc_parameter_struct output_config; + rt_uint32_t divisor = ov7670_xclk_divisor_get(); + + rcu_periph_clock_enable(RCU_GPIOE); + rcu_periph_clock_enable(RCU_TIMER0); + + gpio_af_set(GPIOE, GPIO_AF_1, GPIO_PIN_9); + gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9); + gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, + GPIO_PIN_9); + + timer_deinit(TIMER0); + timer_struct_para_init(&timer_config); + timer_config.prescaler = 0U; + timer_config.alignedmode = TIMER_COUNTER_EDGE; + timer_config.counterdirection = TIMER_COUNTER_UP; + timer_config.period = divisor - 1U; + timer_config.clockdivision = TIMER_CKDIV_DIV1; + timer_config.repetitioncounter = 0U; + timer_init(TIMER0, &timer_config); + + timer_channel_output_struct_para_init(&output_config); + output_config.outputstate = TIMER_CCX_ENABLE; + output_config.outputnstate = TIMER_CCXN_DISABLE; + output_config.ocpolarity = TIMER_OC_POLARITY_HIGH; + output_config.ocnpolarity = TIMER_OCN_POLARITY_HIGH; + output_config.ocidlestate = TIMER_OC_IDLE_STATE_LOW; + output_config.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW; + timer_channel_output_config(TIMER0, TIMER_CH_0, &output_config); + timer_channel_output_mode_config(TIMER0, TIMER_CH_0, TIMER_OC_MODE_PWM0); + timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, divisor / 2U); + timer_channel_output_shadow_config(TIMER0, TIMER_CH_0, + TIMER_OC_SHADOW_DISABLE); + timer_channel_primary_output_config(TIMER0, TIMER_CH_0, ENABLE); + timer_primary_output_config(TIMER0, ENABLE); + timer_channel_output_state_config(TIMER0, TIMER_CH_0, TIMER_CCX_ENABLE); + timer_enable(TIMER0); +} + +static void ov7670_xclk_stop(void) +{ + timer_channel_output_state_config(TIMER0, TIMER_CH_0, TIMER_CCX_DISABLE); + timer_channel_primary_output_config(TIMER0, TIMER_CH_0, DISABLE); + timer_disable(TIMER0); + gpio_mode_set(GPIOE, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_9); +} + +static void ov7670_sccb_bus_config(void) +{ + /* Use the conservative SCCB timing from the GD32H77DI DCI example. */ + i2c_deinit(I2C2); + i2c_timing_config(I2C2, OV7670_SCCB_PSC, OV7670_SCCB_SCL_DELAY, + OV7670_SCCB_SDA_DELAY); + i2c_master_clock_config(I2C2, OV7670_SCCB_SCL_HIGH, + OV7670_SCCB_SCL_LOW); + i2c_enable(I2C2); +} + +static void ov7670_sccb_bus_recover(void) +{ + rt_uint32_t pulse; + + i2c_disable(I2C2); + rcu_periph_clock_enable(RCU_GPIOA); + rcu_periph_clock_enable(RCU_GPIOC); + + gpio_bit_set(GPIOA, GPIO_PIN_8); + gpio_bit_set(GPIOC, GPIO_PIN_9); + gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_8); + gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_9); + gpio_output_options_set(GPIOA, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, + GPIO_PIN_8); + gpio_output_options_set(GPIOC, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, + GPIO_PIN_9); + rt_hw_us_delay(OV7670_SCCB_RECOVERY_DELAY_US); + + for (pulse = 0U; + (pulse < OV7670_SCCB_RECOVERY_PULSES) && + (gpio_input_bit_get(GPIOC, GPIO_PIN_9) == RESET); + pulse++) + { + gpio_bit_reset(GPIOA, GPIO_PIN_8); + rt_hw_us_delay(OV7670_SCCB_RECOVERY_DELAY_US); + gpio_bit_set(GPIOA, GPIO_PIN_8); + rt_hw_us_delay(OV7670_SCCB_RECOVERY_DELAY_US); + } + + gpio_bit_reset(GPIOC, GPIO_PIN_9); + rt_hw_us_delay(OV7670_SCCB_RECOVERY_DELAY_US); + gpio_bit_set(GPIOA, GPIO_PIN_8); + rt_hw_us_delay(OV7670_SCCB_RECOVERY_DELAY_US); + gpio_bit_set(GPIOC, GPIO_PIN_9); + rt_hw_us_delay(OV7670_SCCB_RECOVERY_DELAY_US); + + gpio_af_set(GPIOA, GPIO_AF_4, GPIO_PIN_8); + gpio_af_set(GPIOC, GPIO_AF_4, GPIO_PIN_9); + gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_8); + gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9); + ov7670_sccb_bus_config(); +} + +static rt_err_t ov7670_sccb_wait_flag(rt_uint32_t flag) +{ + rt_tick_t start_tick = rt_tick_get(); + rt_tick_t timeout = rt_tick_from_millisecond(OV7670_SCCB_TIMEOUT_MS); + + while (i2c_flag_get(I2C2, flag) == RESET) + { + if ((I2C_STAT(I2C2) & OV7670_SCCB_ERROR_FLAGS) != 0U) + { + return -RT_EIO; + } + if ((rt_tick_get() - start_tick) >= timeout) + { + return -RT_ETIMEOUT; + } + } + + return RT_EOK; +} + +static rt_err_t ov7670_sccb_wait_idle(void) +{ + rt_tick_t start_tick = rt_tick_get(); + rt_tick_t timeout = rt_tick_from_millisecond(OV7670_SCCB_TIMEOUT_MS); + + while (i2c_flag_get(I2C2, I2C_FLAG_I2CBSY) != RESET) + { + if ((rt_tick_get() - start_tick) >= timeout) + { + return -RT_ETIMEOUT; + } + } + + return RT_EOK; +} + +static void ov7670_sccb_transfer_finish(void) +{ + i2c_automatic_end_disable(I2C2); + i2c_flag_clear(I2C2, OV7670_SCCB_CLEAR_FLAGS); +} + +static void ov7670_sccb_log_failure(const char *phase, rt_uint8_t reg) +{ + LOG_E("SCCB %s failed at reg 0x%02x (SCL=%u SDA=%u TIMING=%08x STAT=%08x)", + phase, reg, + (unsigned int)gpio_input_bit_get(GPIOA, GPIO_PIN_8), + (unsigned int)gpio_input_bit_get(GPIOC, GPIO_PIN_9), + (unsigned int)I2C_TIMING(I2C2), (unsigned int)I2C_STAT(I2C2)); +} + +static rt_uint32_t ov7670_pin_edge_count(rt_uint32_t gpio_periph, + rt_uint32_t pin) +{ + rt_uint32_t previous = GPIO_ISTAT(gpio_periph) & pin; + rt_uint32_t edges = 0U; + rt_uint32_t index; + + for (index = 0U; index < OV7670_CLOCK_SAMPLE_COUNT; index++) + { + rt_uint32_t current = GPIO_ISTAT(gpio_periph) & pin; + + if (current != previous) + { + edges++; + previous = current; + } + } + return edges; +} + +static void ov7670_clock_log_failure(void) +{ + rt_uint32_t xclk_edges; + rt_uint32_t pclk_edges; + + gpio_af_set(GPIOE, GPIO_AF_13, GPIO_PIN_3); + gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3); + xclk_edges = ov7670_pin_edge_count(GPIOE, GPIO_PIN_9); + pclk_edges = ov7670_pin_edge_count(GPIOE, GPIO_PIN_3); + LOG_E("clock diagnostic: XCLK edges=%u PCLK edges=%u CTL0=%08x CNT=%u CAR=%u CH0CV=%u", + (unsigned int)xclk_edges, (unsigned int)pclk_edges, + (unsigned int)TIMER_CTL0(TIMER0), + (unsigned int)TIMER_CNT(TIMER0), + (unsigned int)TIMER_CAR(TIMER0), + (unsigned int)TIMER_CH0CV(TIMER0)); + LOG_E("clock gates: CHCTL0=%08x CHCTL2=%08x CCHP=%08x CHBRKCTL=%08x", + (unsigned int)TIMER_CHCTL0(TIMER0), + (unsigned int)TIMER_CHCTL2(TIMER0), + (unsigned int)TIMER_CCHP(TIMER0), + (unsigned int)TIMER_CHBRKCTL(TIMER0)); + LOG_E("PE9 mux: CTL=%08x AFSEL1=%08x OMODE=%08x ISTAT=%08x", + (unsigned int)GPIO_CTL(GPIOE), + (unsigned int)GPIO_AFSEL1(GPIOE), + (unsigned int)GPIO_OMODE(GPIOE), + (unsigned int)GPIO_ISTAT(GPIOE)); +} + +static rt_err_t ov7670_write_register(rt_uint8_t reg, rt_uint8_t value) +{ + rt_err_t result; + rt_uint32_t attempt; + + for (attempt = 0U; attempt < OV7670_SCCB_RETRY_COUNT; attempt++) + { + result = ov7670_sccb_wait_idle(); + if (result == RT_EOK) + { + i2c_flag_clear(I2C2, OV7670_SCCB_CLEAR_FLAGS); + i2c_master_addressing(I2C2, OV7670_SCCB_WIRE_ADDRESS, + I2C_MASTER_TRANSMIT); + i2c_transfer_byte_number_config(I2C2, 2U); + i2c_automatic_end_enable(I2C2); + i2c_start_on_bus(I2C2); + + i2c_data_transmit(I2C2, reg); + result = ov7670_sccb_wait_flag(I2C_FLAG_TI); + } + if (result == RT_EOK) + { + i2c_data_transmit(I2C2, value); + result = ov7670_sccb_wait_flag(I2C_FLAG_TI); + } + if (result == RT_EOK) + { + result = ov7670_sccb_wait_flag(I2C_FLAG_STPDET); + } + ov7670_sccb_transfer_finish(); + + if (result == RT_EOK) + { + if (attempt != 0U) + { + LOG_W("SCCB write recovered at reg 0x%02x after %u retries", + reg, (unsigned int)attempt); + } + return RT_EOK; + } + + if ((attempt + 1U) < OV7670_SCCB_RETRY_COUNT) + { + rt_hw_us_delay(OV7670_SCCB_RETRY_DELAY_US); + ov7670_sccb_bus_recover(); + } + } + + ov7670_sccb_log_failure("write", reg); + ov7670_sccb_bus_recover(); + return result; +} + +static rt_err_t ov7670_read_register(rt_uint8_t reg, rt_uint8_t *value) +{ + rt_err_t result; + rt_uint32_t attempt; + + for (attempt = 0U; attempt < OV7670_SCCB_RETRY_COUNT; attempt++) + { + result = ov7670_sccb_wait_idle(); + if (result == RT_EOK) + { + i2c_flag_clear(I2C2, OV7670_SCCB_CLEAR_FLAGS); + i2c_master_addressing(I2C2, OV7670_SCCB_WIRE_ADDRESS, + I2C_MASTER_TRANSMIT); + i2c_transfer_byte_number_config(I2C2, 1U); + i2c_automatic_end_enable(I2C2); + i2c_start_on_bus(I2C2); + + i2c_data_transmit(I2C2, reg); + result = ov7670_sccb_wait_flag(I2C_FLAG_TI); + } + if (result == RT_EOK) + { + result = ov7670_sccb_wait_flag(I2C_FLAG_STPDET); + } + ov7670_sccb_transfer_finish(); + + if (result == RT_EOK) + { + i2c_master_addressing(I2C2, OV7670_SCCB_WIRE_ADDRESS, + I2C_MASTER_RECEIVE); + i2c_transfer_byte_number_config(I2C2, 1U); + i2c_automatic_end_enable(I2C2); + i2c_start_on_bus(I2C2); + result = ov7670_sccb_wait_flag(I2C_FLAG_RBNE); + } + if (result == RT_EOK) + { + *value = i2c_data_receive(I2C2); + result = ov7670_sccb_wait_flag(I2C_FLAG_STPDET); + } + ov7670_sccb_transfer_finish(); + + if (result == RT_EOK) + { + return RT_EOK; + } + + if ((attempt + 1U) < OV7670_SCCB_RETRY_COUNT) + { + rt_hw_us_delay(OV7670_SCCB_RETRY_DELAY_US); + ov7670_sccb_bus_recover(); + } + } + + ov7670_sccb_log_failure("read", reg); + ov7670_sccb_bus_recover(); + return result; +} + +int gd32_camera_sccb_read(rt_uint8_t slv_addr, rt_uint8_t reg, + rt_uint8_t *value) +{ + RT_UNUSED(slv_addr); + return ov7670_read_register(reg, value); +} + +int gd32_camera_sccb_write(rt_uint8_t slv_addr, rt_uint8_t reg, + rt_uint8_t value) +{ + RT_UNUSED(slv_addr); + return ov7670_write_register(reg, value); +} + +static rt_err_t ov7670_sensor_init(void) +{ + rt_err_t result; + int version; + + result = rt_i2c_bus_lock(ov7670.bus, RT_WAITING_FOREVER); + if (result != RT_EOK) + { + return result; + } + + ov7670_xclk_start(); + rt_hw_us_delay(10000U); + ov7670_sccb_bus_recover(); + + result = (sensor_init() == 0) ? RT_EOK : -RT_EIO; + if (result != RT_EOK) + { + ov7670_clock_log_failure(); + } + if (result == RT_EOK) + { + result = sensor_reset(); + } + if (result == RT_EOK) + { + result = sensor_set_pixformat(PIXFORMAT_RGB565); + } + if (result == RT_EOK) + { + result = sensor_set_framesize(FRAMESIZE_QVGA); + } + if (result == RT_EOK) + { + ov7670.info.pid = sensor.chip_id; + version = sensor_read_reg(0x0BU); + result = (version >= 0) ? RT_EOK : -RT_EIO; + if (result == RT_EOK) + { + ov7670.info.ver = (rt_uint8_t)version; + } + } + if (result == RT_EOK) + { + /* Window and scaling registers take effect on a frame boundary. */ + rt_hw_us_delay(100000U); + } + + if (result != RT_EOK) + { + ov7670_xclk_stop(); + } + rt_i2c_bus_unlock(ov7670.bus); + return result; +} + +static void ov7670_gpio_init(void) +{ + rcu_periph_clock_enable(RCU_GPIOA); + rcu_periph_clock_enable(RCU_GPIOB); + rcu_periph_clock_enable(RCU_GPIOC); + rcu_periph_clock_enable(RCU_GPIOD); + rcu_periph_clock_enable(RCU_GPIOE); + rcu_periph_clock_enable(RCU_GPIOG); + rcu_periph_clock_enable(RCU_GPIOH); + + gpio_af_set(GPIOG, GPIO_AF_13, GPIO_PIN_9); + gpio_af_set(GPIOH, GPIO_AF_1, GPIO_PIN_15); + gpio_af_set(GPIOE, GPIO_AF_13, GPIO_PIN_3); + gpio_af_set(GPIOA, GPIO_AF_13, GPIO_PIN_9 | GPIO_PIN_10); + gpio_af_set(GPIOC, GPIO_AF_13, GPIO_PIN_8); + gpio_af_set(GPIOE, GPIO_AF_13, GPIO_PIN_1); + gpio_af_set(GPIOH, GPIO_AF_13, GPIO_PIN_14); + gpio_af_set(GPIOD, GPIO_AF_13, GPIO_PIN_3); + gpio_af_set(GPIOB, GPIO_AF_13, GPIO_PIN_8 | GPIO_PIN_9); + + gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9); + gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, + GPIO_PIN_14 | GPIO_PIN_15); + gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, + GPIO_PIN_1 | GPIO_PIN_3); + gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, + GPIO_PIN_9 | GPIO_PIN_10); + gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8); + gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3); + gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, + GPIO_PIN_8 | GPIO_PIN_9); + + gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, + GPIO_PIN_9); + gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, + GPIO_PIN_14 | GPIO_PIN_15); + gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, + GPIO_PIN_1 | GPIO_PIN_3); + gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, + GPIO_PIN_9 | GPIO_PIN_10); + gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, + GPIO_PIN_8); + gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, + GPIO_PIN_3); + gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, + GPIO_PIN_8 | GPIO_PIN_9); +} + +static void ov7670_dci_configure(void) +{ + dci_parameter_struct dci_config; + + /* Start each snapshot with an empty FIFO, including after an aborted frame. */ + dci_deinit(); + dci_config.capture_mode = DCI_CAPTURE_MODE_SNAPSHOT; + dci_config.clock_polarity = DCI_CK_POLARITY_RISING; + dci_config.hsync_polarity = DCI_HSYNC_POLARITY_LOW; + dci_config.vsync_polarity = DCI_VSYNC_POLARITY_HIGH; + dci_config.frame_rate = DCI_FRAME_RATE_ALL; + dci_config.interface_format = DCI_INTERFACE_FORMAT_8BITS; + dci_init(&dci_config); + dci_interrupt_enable(DCI_INT_OVR | DCI_INT_ESE | DCI_INT_EF); +} + +static void ov7670_dci_init(void) +{ + rcu_periph_clock_enable(RCU_DCI); + rcu_periph_clock_enable(RCU_DMA1); + rcu_periph_clock_enable(RCU_DMAMUX); + ov7670_gpio_init(); + ov7670_dci_configure(); + + nvic_irq_enable(DMA1_Channel7_IRQn, 4U, 0U); + nvic_irq_enable(DCI_IRQn, 4U, 0U); +} + +static void ov7670_dma_prepare(void) +{ + dma_single_data_parameter_struct dma_config; + + dma_channel_deinit(DMA1, DMA_CH7); + dma_single_data_para_struct_init(&dma_config); + dma_config.request = DMA_REQUEST_DCI; + dma_config.periph_addr = (rt_uint32_t)&DCI_DATA; + dma_config.memory0_addr = (rt_uint32_t)ov7670_frame_buffer; + dma_config.direction = DMA_PERIPH_TO_MEMORY; + dma_config.number = OV7670_DMA_WORD_COUNT; + dma_config.periph_inc = DMA_PERIPH_INCREASE_DISABLE; + dma_config.memory_inc = DMA_MEMORY_INCREASE_ENABLE; + dma_config.periph_memory_width = DMA_PERIPH_WIDTH_32BIT; + dma_config.circular_mode = DMA_CIRCULAR_MODE_DISABLE; + dma_config.priority = DMA_PRIORITY_ULTRA_HIGH; + dma_single_data_mode_init(DMA1, DMA_CH7, &dma_config); + dma_interrupt_flag_clear(DMA1, DMA_CH7, OV7670_DMA_FLAGS); + dma_interrupt_enable(DMA1, DMA_CH7, OV7670_DMA_INTERRUPTS); +} + +static void ov7670_capture_finish(rt_err_t result) +{ + if (!ov7670_capture_active) + { + return; + } + + ov7670_capture_flags |= DCI_STAT1; + if ((result == RT_EOK) && + ((ov7670_capture_flags & (DCI_STAT1_OVRF | DCI_STAT1_ESEF)) != 0U)) + { + result = -RT_EIO; + } + dma_channel_disable(DMA1, DMA_CH7); + dma_interrupt_disable(DMA1, DMA_CH7, OV7670_DMA_INTERRUPTS); + dci_capture_disable(); + dci_disable(); + dci_interrupt_disable(DCI_INT_OVR | DCI_INT_ESE | DCI_INT_EF); + ov7670_capture_result = result; + ov7670_capture_active = RT_FALSE; + rt_completion_done(&ov7670_capture_completion); +} + +static void ov7670_capture_update(rt_uint32_t dci_flags, + rt_bool_t dma_error, rt_bool_t dma_finished) +{ + if (!ov7670_capture_active) + { + return; + } + + ov7670_capture_flags |= dci_flags; + if ((dci_flags & DCI_STAT1_EFF) != 0U) + { + ov7670_frame_complete = RT_TRUE; + } + if (dma_finished && !ov7670_dma_complete) + { + ov7670_dma_complete = RT_TRUE; + } + + /* The two IRQs may arrive in either order. An overflow takes precedence + * over completion, even when the DMA IRQ is serviced first. */ + if (dma_error || ((dci_flags & (DCI_STAT1_OVRF | DCI_STAT1_ESEF)) != 0U)) + { + ov7670_capture_finish(-RT_EIO); + } + else if (ov7670_dma_complete && ov7670_frame_complete) + { + rt_bool_t complete = (dma_transfer_number_get(DMA1, DMA_CH7) == 0U) && + ((DCI_STAT0 & DCI_STAT0_FV) == 0U); + + ov7670_capture_finish(complete ? RT_EOK : -RT_EIO); + } +} + +void DMA1_Channel7_IRQHandler(void) +{ + rt_bool_t error; + rt_bool_t finished; + + rt_interrupt_enter(); + error = (dma_interrupt_flag_get(DMA1, DMA_CH7, DMA_INT_FLAG_FEE) != RESET) || + (dma_interrupt_flag_get(DMA1, DMA_CH7, DMA_INT_FLAG_SDE) != RESET) || + (dma_interrupt_flag_get(DMA1, DMA_CH7, DMA_INT_FLAG_TAE) != RESET); + finished = dma_interrupt_flag_get(DMA1, DMA_CH7, DMA_INT_FLAG_FTF) != RESET; + dma_interrupt_flag_clear(DMA1, DMA_CH7, OV7670_DMA_FLAGS); + + ov7670_capture_update(DCI_STAT1, error, finished); + __DSB(); + rt_interrupt_leave(); +} + +void DCI_IRQHandler(void) +{ + rt_uint32_t flags; + + rt_interrupt_enter(); + flags = DCI_STAT1; + dci_interrupt_flag_clear(flags & (DCI_INT_FLAG_EF | DCI_INT_FLAG_OVR | + DCI_INT_FLAG_ESE)); + ov7670_capture_update(flags, RT_FALSE, RT_FALSE); + __DSB(); + rt_interrupt_leave(); +} + +static void ov7670_rgb565_byte_swap(void) +{ + rt_size_t index; + + for (index = 0U; index < OV7670_FRAME_SIZE; index += 2U) + { + rt_uint8_t value = ov7670_frame_buffer[index]; + + ov7670_frame_buffer[index] = ov7670_frame_buffer[index + 1U]; + ov7670_frame_buffer[index + 1U] = value; + } +} + +static void ov7670_compact_frame(void) +{ + rt_size_t row; + + for (row = 0U; row < OV7670_HEIGHT; row++) + { + rt_memmove(ov7670_frame_buffer + + row * OV7670_WIDTH * OV7670_BYTES_PER_PIXEL, + ov7670_frame_buffer + + row * OV7670_CAPTURE_LINE_BYTES + OV7670_LINE_CROP_BYTES, + OV7670_WIDTH * OV7670_BYTES_PER_PIXEL); + } +} + +static rt_err_t ov7670_capture(void) +{ + rt_tick_t timeout; + rt_base_t level; + rt_err_t wait_result; + + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH | RT_HW_CACHE_INVALIDATE, + ov7670_frame_buffer, OV7670_CAPTURE_SIZE); + + level = rt_hw_interrupt_disable(); + ov7670_dci_configure(); + ov7670_dma_prepare(); + ov7670_capture_result = -RT_ETIMEOUT; + ov7670_dma_complete = RT_FALSE; + ov7670_frame_complete = RT_FALSE; + ov7670_capture_flags = 0U; + rt_completion_init(&ov7670_capture_completion); + NVIC_ClearPendingIRQ(DMA1_Channel7_IRQn); + NVIC_ClearPendingIRQ(DCI_IRQn); + ov7670_capture_active = RT_TRUE; + dma_channel_enable(DMA1, DMA_CH7); + dci_enable(); + dci_capture_enable(); + __DSB(); + rt_hw_interrupt_enable(level); + + timeout = rt_tick_from_millisecond(OV7670_CAPTURE_TIMEOUT_MS); + wait_result = rt_completion_wait(&ov7670_capture_completion, timeout); + + level = rt_hw_interrupt_disable(); + if (ov7670_capture_active) + { + ov7670_capture_finish(wait_result == RT_EOK ? -RT_EIO : wait_result); + } + rt_hw_interrupt_enable(level); + + if (ov7670_capture_result != RT_EOK) + { + return ov7670_capture_result; + } + + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, ov7670_frame_buffer, + OV7670_CAPTURE_SIZE); + ov7670_compact_frame(); + if (sensor.hw_flags.rgb_swap) + { + ov7670_rgb565_byte_swap(); + } + return RT_EOK; +} + +static rt_err_t ov7670_device_init(rt_device_t device) +{ + rt_err_t result; + + RT_UNUSED(device); + result = rt_mutex_take(&ov7670.lock, RT_WAITING_FOREVER); + if (result != RT_EOK) + { + return result; + } + if (ov7670.initialized) + { + rt_mutex_release(&ov7670.lock); + return RT_EOK; + } + + ov7670.bus = rt_i2c_bus_device_find(OV7670_I2C_BUS_NAME); + if (ov7670.bus == RT_NULL) + { + LOG_E("I2C bus %s not found", OV7670_I2C_BUS_NAME); + rt_mutex_release(&ov7670.lock); + return -RT_ENOSYS; + } + + result = ov7670_sensor_init(); + if (result == RT_EOK) + { + ov7670_dci_init(); + ov7670.initialized = RT_TRUE; + LOG_I("OV7670 PID=%02x VER=%02x, %ux%u RGB565, XCLK=%u Hz", + ov7670.info.pid, ov7670.info.ver, + ov7670.info.width, ov7670.info.height, ov7670.info.xclk_hz); + } + else + { + LOG_E("sensor initialization failed: %d (PID=%02x VER=%02x)", + result, ov7670.info.pid, ov7670.info.ver); + } + + rt_mutex_release(&ov7670.lock); + return result; +} + +static rt_ssize_t ov7670_device_read(rt_device_t device, rt_off_t position, + void *buffer, rt_size_t size) +{ + rt_err_t result; + + RT_UNUSED(device); + RT_UNUSED(position); + if ((buffer == RT_NULL) || (size < OV7670_FRAME_SIZE)) + { + return -RT_EINVAL; + } + if (!ov7670.initialized) + { + return -RT_EIO; + } + + result = rt_mutex_take(&ov7670.lock, RT_WAITING_FOREVER); + if (result == RT_EOK) + { + result = ov7670_capture(); + if (result == RT_EOK) + { + rt_memcpy(buffer, ov7670_frame_buffer, OV7670_FRAME_SIZE); + } + rt_mutex_release(&ov7670.lock); + } + + return (result == RT_EOK) ? (rt_ssize_t)OV7670_FRAME_SIZE : result; +} + +static rt_err_t ov7670_device_control(rt_device_t device, int command, + void *args) +{ + RT_UNUSED(device); + if (args == RT_NULL) + { + return -RT_EINVAL; + } + + if (command == OV7670_CTRL_GET_INFO) + { + rt_memcpy(args, &ov7670.info, sizeof(ov7670.info)); + return RT_EOK; + } + + return -RT_EINVAL; +} + +#ifdef RT_USING_DEVICE_OPS +static const struct rt_device_ops ov7670_device_ops = +{ + ov7670_device_init, + RT_NULL, + RT_NULL, + ov7670_device_read, + RT_NULL, + ov7670_device_control, +}; +#endif + +static int rt_hw_ov7670_register(void) +{ + rt_uint32_t divisor; + rt_err_t result; + + rt_memset(&ov7670, 0, sizeof(ov7670)); + result = rt_mutex_init(&ov7670.lock, "ov7670", RT_IPC_FLAG_PRIO); + if (result != RT_EOK) + { + return result; + } + + divisor = ov7670_xclk_divisor_get(); + ov7670.info.width = OV7670_WIDTH; + ov7670.info.height = OV7670_HEIGHT; + ov7670.info.frame_size = OV7670_FRAME_SIZE; + ov7670.info.pixel_format = OV7670_PIXEL_FORMAT_RGB565; + ov7670.info.xclk_hz = ov7670_timer_clock_get() / divisor; + ov7670.parent.type = RT_Device_Class_Miscellaneous; +#ifdef RT_USING_DEVICE_OPS + ov7670.parent.ops = &ov7670_device_ops; +#else + ov7670.parent.init = ov7670_device_init; + ov7670.parent.read = ov7670_device_read; + ov7670.parent.control = ov7670_device_control; +#endif + ov7670.parent.user_data = &ov7670.info; + + return rt_device_register(&ov7670.parent, OV7670_DEVICE_NAME, + RT_DEVICE_FLAG_RDONLY); +} +INIT_DEVICE_EXPORT(rt_hw_ov7670_register); + +#endif /* BSP_USING_OV7670 */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/drv_ov7670.h b/bsp/gd32/arm/gd32h77d-ginopi/board/drv_ov7670.h new file mode 100644 index 000000000000..c071401594b8 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/drv_ov7670.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2006-2026 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DRV_OV7670_H__ +#define __DRV_OV7670_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define OV7670_DEVICE_NAME "ov7670" +#define OV7670_WIDTH 320U +#define OV7670_HEIGHT 240U +#define OV7670_BYTES_PER_PIXEL 2U +#define OV7670_FRAME_SIZE (OV7670_WIDTH * OV7670_HEIGHT * \ + OV7670_BYTES_PER_PIXEL) + +#define OV7670_CTRL_GET_INFO (RT_DEVICE_CTRL_BASE(Miscellaneous) + 1) + +enum ov7670_pixel_format +{ + OV7670_PIXEL_FORMAT_RGB565 = 0, +}; + +struct ov7670_info +{ + rt_uint16_t width; + rt_uint16_t height; + rt_uint32_t frame_size; + rt_uint32_t pixel_format; + rt_uint32_t xclk_hz; + rt_uint8_t pid; + rt_uint8_t ver; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __DRV_OV7670_H__ */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/drv_qspi_flash.c b/bsp/gd32/arm/gd32h77d-ginopi/board/drv_qspi_flash.c new file mode 100644 index 000000000000..53321fe1d64c --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/drv_qspi_flash.c @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-07-31 RT-Thread add GD25X512ME OSPI flash support + * 2026-08-31 CYFS adapt GD25Q64E QSPI flash + */ + +#include +#include +#include + +#include "drv_ospi.h" + +#ifdef BSP_USING_FLASH_FATFS + +#define DBG_TAG "drv.qspi_flash" +#define DBG_LVL DBG_INFO +#include + +#define GD25Q64E_DEVICE_NAME "qspi_flash0" +#define GD25Q64E_BUS_NAME "ospi0" +#define GD25Q64E_SECTOR_SIZE (4U * 1024U) +#define GD25Q64E_PAGE_SIZE 256U + +#define GD25Q64E_EXPECTED_ID 0xC84017U + +#define GD25Q64E_CMD_WRITE_ENABLE 0x06U +#define GD25Q64E_CMD_READ_STATUS1 0x05U +#define GD25Q64E_CMD_READ_STATUS2 0x35U +#define GD25Q64E_CMD_WRITE_STATUS2 0x31U +#define GD25Q64E_CMD_READ_ID 0x9FU +#define GD25Q64E_CMD_RESET_ENABLE 0x66U +#define GD25Q64E_CMD_RESET_MEMORY 0x99U +#define GD25Q64E_CMD_QUAD_READ 0x6BU +#define GD25Q64E_CMD_QUAD_PAGE_PROGRAM 0x32U +#define GD25Q64E_CMD_ERASE_4K 0x20U + +#define GD25Q64E_STATUS_WIP 0x01U +#define GD25Q64E_STATUS_WEL 0x02U +#define GD25Q64E_STATUS2_QE 0x02U + +#define GD25Q64E_READ_DUMMY_CYCLES 8U +#define GD25Q64E_PROGRAM_TIMEOUT_MS 1000U +#define GD25Q64E_ERASE_TIMEOUT_MS 2000U + +static struct rt_qspi_device *gd25q64e_device; +static struct rt_mutex gd25q64e_lock; +static rt_bool_t gd25q64e_lock_initialized; +static rt_bool_t gd25q64e_ready; + +static rt_err_t gd25q64e_transfer(rt_uint8_t instruction, + rt_uint8_t instruction_lines, + rt_uint32_t address, + rt_uint8_t address_size, + rt_uint8_t address_lines, + rt_uint32_t dummy_cycles, + const rt_uint8_t *send_buf, + rt_uint8_t *recv_buf, + rt_size_t size, + rt_uint8_t data_lines) +{ + struct rt_qspi_message message = {0}; + rt_ssize_t result; + + message.instruction.content = instruction; + message.instruction.qspi_lines = instruction_lines; + message.address.content = address; + message.address.size = address_size; + message.address.qspi_lines = address_lines; + message.dummy_cycles = dummy_cycles; + message.qspi_data_lines = data_lines; + message.parent.send_buf = send_buf; + message.parent.recv_buf = recv_buf; + message.parent.length = size; + message.parent.cs_take = 1; + message.parent.cs_release = 1; + message.parent.next = RT_NULL; + + result = rt_qspi_transfer_message(gd25q64e_device, &message); + if (size == 0U) + { + return result > 0 ? RT_EOK : -RT_EIO; + } + + return result == (rt_ssize_t)size ? RT_EOK : -RT_EIO; +} + +static rt_err_t gd25q64e_command(rt_uint8_t instruction) +{ + return gd25q64e_transfer(instruction, 1, 0, 0, 0, 0, + RT_NULL, RT_NULL, 0, 0); +} + +static rt_err_t gd25q64e_read_status1(rt_uint8_t *status) +{ + return gd25q64e_transfer(GD25Q64E_CMD_READ_STATUS1, 1, + 0, 0, 0, 0, + RT_NULL, status, 1, 1); +} + +static rt_err_t gd25q64e_read_status2(rt_uint8_t *status) +{ + return gd25q64e_transfer(GD25Q64E_CMD_READ_STATUS2, 1, + 0, 0, 0, 0, + RT_NULL, status, 1, 1); +} + +static rt_err_t gd25q64e_wait_status(rt_uint8_t mask, + rt_uint8_t expected, + rt_uint32_t timeout_ms, + rt_bool_t delay_while_busy) +{ + rt_uint32_t start_ms; + rt_uint8_t status; + rt_err_t result; + + start_ms = (rt_uint32_t)rt_tick_get_millisecond(); + do + { + result = gd25q64e_read_status1(&status); + if (result != RT_EOK) + { + return result; + } + if ((status & mask) == expected) + { + return RT_EOK; + } + + if (delay_while_busy) + { + rt_thread_mdelay(1); + } + } + while ((rt_uint32_t)((rt_uint32_t)rt_tick_get_millisecond() - start_ms) < timeout_ms); + + return -RT_ETIMEOUT; +} + +static rt_err_t gd25q64e_write_enable(void) +{ + rt_err_t result; + + result = gd25q64e_command(GD25Q64E_CMD_WRITE_ENABLE); + if (result != RT_EOK) + { + return result; + } + + return gd25q64e_wait_status(GD25Q64E_STATUS_WEL, + GD25Q64E_STATUS_WEL, + GD25Q64E_PROGRAM_TIMEOUT_MS, + RT_FALSE); +} + +static rt_err_t gd25q64e_set_qe(void) +{ + rt_uint8_t status2 = 0; + rt_err_t result; + + result = gd25q64e_read_status2(&status2); + if (result != RT_EOK || (status2 & GD25Q64E_STATUS2_QE) != 0U) + { + return result; + } + + status2 |= GD25Q64E_STATUS2_QE; + result = gd25q64e_write_enable(); + if (result == RT_EOK) + { + result = gd25q64e_transfer(GD25Q64E_CMD_WRITE_STATUS2, 1, + 0, 0, 0, 0, + &status2, RT_NULL, 1, 1); + } + if (result == RT_EOK) + { + result = gd25q64e_wait_status(GD25Q64E_STATUS_WIP, 0, + GD25Q64E_PROGRAM_TIMEOUT_MS, + RT_TRUE); + } + if (result == RT_EOK) + { + result = gd25q64e_read_status2(&status2); + } + + return result == RT_EOK && (status2 & GD25Q64E_STATUS2_QE) != 0U ? + RT_EOK : -RT_EIO; +} + +static rt_err_t gd25q64e_read_id(rt_uint32_t *flash_id) +{ + rt_uint8_t id[3] = {0}; + rt_err_t result; + + result = gd25q64e_transfer(GD25Q64E_CMD_READ_ID, 1, + 0, 0, 0, 0, + RT_NULL, id, sizeof(id), 1); + if (result == RT_EOK) + { + *flash_id = ((rt_uint32_t)id[0] << 16) | + ((rt_uint32_t)id[1] << 8) | + (rt_uint32_t)id[2]; + } + + return result; +} + +static rt_err_t gd25q64e_reset(void) +{ + rt_err_t result; + + result = gd25q64e_command(GD25Q64E_CMD_RESET_ENABLE); + if (result == RT_EOK) + { + result = gd25q64e_command(GD25Q64E_CMD_RESET_MEMORY); + } + + rt_thread_mdelay(1); + return result; +} + +static int gd25q64e_init(void) +{ + struct rt_qspi_configuration configuration = {0}; + rt_device_t device; + rt_uint32_t flash_id = 0; + rt_err_t result; + + if (gd25q64e_ready) + { + return RT_EOK; + } + + if (!gd25q64e_lock_initialized) + { + result = rt_mutex_init(&gd25q64e_lock, "qspi_fl", RT_IPC_FLAG_PRIO); + if (result != RT_EOK) + { + return result; + } + gd25q64e_lock_initialized = RT_TRUE; + } + + device = rt_device_find(GD25Q64E_DEVICE_NAME); + if (device == RT_NULL) + { + result = rt_hw_ospi_device_attach(GD25Q64E_BUS_NAME, + GD25Q64E_DEVICE_NAME, + 4, RT_NULL, RT_NULL); + if (result != RT_EOK) + { + LOG_E("attach %s to %s failed: %d", + GD25Q64E_DEVICE_NAME, GD25Q64E_BUS_NAME, result); + return result; + } + device = rt_device_find(GD25Q64E_DEVICE_NAME); + } + if (device == RT_NULL) + { + return -RT_ENOSYS; + } + + gd25q64e_device = (struct rt_qspi_device *)device; + configuration.parent.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB; + configuration.parent.data_width = 8; + configuration.parent.max_hz = BSP_OSPI_FLASH_MAX_HZ; + configuration.medium_size = GD25Q64E_FLASH_SIZE; + configuration.ddr_mode = 0; + configuration.qspi_dl_width = 4; + result = rt_qspi_configure(gd25q64e_device, &configuration); + if (result != RT_EOK) + { + LOG_E("configure %s failed: %d", GD25Q64E_DEVICE_NAME, result); + return result; + } + + result = rt_mutex_take(&gd25q64e_lock, RT_WAITING_FOREVER); + if (result != RT_EOK) + { + return result; + } + + result = gd25q64e_reset(); + if (result == RT_EOK) + { + result = gd25q64e_read_id(&flash_id); + } + if (result == RT_EOK && flash_id != GD25Q64E_EXPECTED_ID) + { + LOG_E("unexpected flash ID: 0x%06x", flash_id); + result = -RT_EIO; + } + if (result == RT_EOK) + { + result = gd25q64e_set_qe(); + } + if (result == RT_EOK) + { + gd25q64e_ready = RT_TRUE; + LOG_I("GD25Q64E ready, ID 0x%06x, clock %u Hz", + flash_id, (unsigned int)BSP_OSPI_FLASH_MAX_HZ); + } + + rt_mutex_release(&gd25q64e_lock); + return result; +} +INIT_DEVICE_EXPORT(gd25q64e_init); + +static int gd25q64e_read(long offset, rt_uint8_t *buf, rt_size_t size) +{ + rt_err_t result; + + if (!gd25q64e_ready) + { + return -RT_EIO; + } + if (offset < 0 || buf == RT_NULL || (rt_size_t)offset > GD25Q64E_FLASH_SIZE || + size > GD25Q64E_FLASH_SIZE - (rt_size_t)offset) + { + return -RT_EINVAL; + } + if (size == 0U) + { + return 0; + } + + result = rt_mutex_take(&gd25q64e_lock, RT_WAITING_FOREVER); + if (result == RT_EOK) + { + result = gd25q64e_transfer(GD25Q64E_CMD_QUAD_READ, 1, + (rt_uint32_t)offset, 24, 1, + GD25Q64E_READ_DUMMY_CYCLES, + RT_NULL, buf, size, 4); + rt_mutex_release(&gd25q64e_lock); + } + + return result == RT_EOK ? (int)size : result; +} + +static int gd25q64e_write(long offset, const rt_uint8_t *buf, rt_size_t size) +{ + rt_size_t remaining; + rt_size_t page_remaining; + rt_size_t write_size; + rt_uint32_t address; + rt_err_t result; + + if (!gd25q64e_ready) + { + return -RT_EIO; + } + if (offset < 0 || buf == RT_NULL || (rt_size_t)offset > GD25Q64E_FLASH_SIZE || + size > GD25Q64E_FLASH_SIZE - (rt_size_t)offset) + { + return -RT_EINVAL; + } + if (size == 0U) + { + return 0; + } + + result = rt_mutex_take(&gd25q64e_lock, RT_WAITING_FOREVER); + if (result != RT_EOK) + { + return result; + } + + address = (rt_uint32_t)offset; + remaining = size; + while (remaining > 0U) + { + page_remaining = GD25Q64E_PAGE_SIZE - (address % GD25Q64E_PAGE_SIZE); + write_size = remaining < page_remaining ? remaining : page_remaining; + + result = gd25q64e_write_enable(); + if (result == RT_EOK) + { + result = gd25q64e_transfer(GD25Q64E_CMD_QUAD_PAGE_PROGRAM, 1, + address, 24, 1, 0, + buf, RT_NULL, write_size, 4); + } + if (result == RT_EOK) + { + result = gd25q64e_wait_status(GD25Q64E_STATUS_WIP, 0, + GD25Q64E_PROGRAM_TIMEOUT_MS, + RT_TRUE); + } + if (result != RT_EOK) + { + break; + } + + address += (rt_uint32_t)write_size; + buf += write_size; + remaining -= write_size; + } + + rt_mutex_release(&gd25q64e_lock); + return result == RT_EOK ? (int)size : result; +} + +static int gd25q64e_erase(long offset, rt_size_t size) +{ + rt_size_t remaining; + rt_uint32_t address; + rt_err_t result; + + if (!gd25q64e_ready) + { + return -RT_EIO; + } + if (offset < 0 || ((rt_size_t)offset % GD25Q64E_SECTOR_SIZE) != 0U || + (size % GD25Q64E_SECTOR_SIZE) != 0U || + (rt_size_t)offset > GD25Q64E_FLASH_SIZE || + size > GD25Q64E_FLASH_SIZE - (rt_size_t)offset) + { + return -RT_EINVAL; + } + if (size == 0U) + { + return 0; + } + + result = rt_mutex_take(&gd25q64e_lock, RT_WAITING_FOREVER); + if (result != RT_EOK) + { + return result; + } + + address = (rt_uint32_t)offset; + remaining = size; + while (remaining > 0U) + { + result = gd25q64e_write_enable(); + if (result == RT_EOK) + { + result = gd25q64e_transfer(GD25Q64E_CMD_ERASE_4K, 1, + address, 24, 1, 0, + RT_NULL, RT_NULL, 0, 0); + } + if (result == RT_EOK) + { + result = gd25q64e_wait_status(GD25Q64E_STATUS_WIP, 0, + GD25Q64E_ERASE_TIMEOUT_MS, + RT_TRUE); + } + if (result != RT_EOK) + { + break; + } + + address += GD25Q64E_SECTOR_SIZE; + remaining -= GD25Q64E_SECTOR_SIZE; + } + + rt_mutex_release(&gd25q64e_lock); + return result == RT_EOK ? (int)size : result; +} + +const struct fal_flash_dev gd25q64e_flash = +{ + .name = "gd25q64e", + .addr = 0, + .len = GD25Q64E_FLASH_SIZE, + .blk_size = GD25Q64E_SECTOR_SIZE, + .ops = + { + .init = gd25q64e_init, + .read = gd25q64e_read, + .write = gd25q64e_write, + .erase = gd25q64e_erase, + }, + .write_gran = 1, +}; + +#endif /* BSP_USING_FLASH_FATFS */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/drv_sdram_port.c b/bsp/gd32/arm/gd32h77d-ginopi/board/drv_sdram_port.c new file mode 100644 index 000000000000..c4487b4bce06 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/drv_sdram_port.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-07-27 RTT first version + * 2026-09-04 CYFS clock EXMC from PLL1R for faster SDRAM + */ + +#include + +#ifdef BSP_USING_SDRAM +#include + +static void _sdram_gpio_config(uint32_t gpio_periph, uint32_t gpio_af, uint32_t pins) +{ + gpio_af_set(gpio_periph, gpio_af, pins); + gpio_mode_set(gpio_periph, GPIO_MODE_AF, GPIO_PUPD_PULLUP, pins); + gpio_output_options_set(gpio_periph, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, pins); +} + +void rt_hw_sdram_gpio_init(void) +{ +#if defined(SDRAM_USE_PLL1R_EXMC) && SDRAM_USE_PLL1R_EXMC + if (RESET == rcu_flag_get(RCU_FLAG_PLL1STB)) + { + rcu_pll_input_output_clock_range_config(IDX_PLL1, + RCU_PLL1RNG_4M_8M, + RCU_PLL1VCO_150M_420M); + if (rcu_pll1_config(SDRAM_PLL1_PSC, + SDRAM_PLL1_N, + SDRAM_PLL1_P, + SDRAM_PLL1_Q, + SDRAM_PLL1_R) == SUCCESS) + { + rcu_pll_clock_output_enable(RCU_PLL1R); + rcu_osci_on(RCU_PLL1_CK); + if (rcu_osci_stab_wait(RCU_PLL1_CK) == SUCCESS) + { + rcu_exmc_clock_config(RCU_EXMCSRC_PLL1R); + } + else + { + rcu_exmc_clock_config(RCU_EXMCSRC_AHB); + } + } + else + { + rcu_exmc_clock_config(RCU_EXMCSRC_AHB); + } + } + else + { + rcu_pll_clock_output_enable(RCU_PLL1R); + rcu_exmc_clock_config(RCU_EXMCSRC_PLL1R); + } +#else + rcu_exmc_clock_config(RCU_EXMCSRC_AHB); +#endif + rcu_periph_clock_enable(RCU_EXMC); + rcu_periph_clock_enable(RCU_GPIOA); + rcu_periph_clock_enable(RCU_GPIOB); + rcu_periph_clock_enable(RCU_GPIOC); + rcu_periph_clock_enable(RCU_GPIOD); + rcu_periph_clock_enable(RCU_GPIOE); + rcu_periph_clock_enable(RCU_GPIOF); + rcu_periph_clock_enable(RCU_GPIOG); + rcu_periph_clock_enable(RCU_GPIOH); + + /* D13-D15: PA4-PA6. */ + _sdram_gpio_config(GPIOA, GPIO_AF_4, GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6); + + /* A0-A1, D10-D11, A12, SDNE0 and SDNWE0. */ + _sdram_gpio_config(GPIOB, GPIO_AF_5, GPIO_PIN_0 | GPIO_PIN_1); + _sdram_gpio_config(GPIOB, GPIO_AF_3, GPIO_PIN_2 | GPIO_PIN_6); + _sdram_gpio_config(GPIOB, GPIO_AF_7, GPIO_PIN_3); + _sdram_gpio_config(GPIOB, GPIO_AF_10, GPIO_PIN_7); + _sdram_gpio_config(GPIOB, GPIO_AF_0, GPIO_PIN_10); + + /* D12 and D9. */ + _sdram_gpio_config(GPIOC, GPIO_AF_1, GPIO_PIN_0); + _sdram_gpio_config(GPIOC, GPIO_AF_8, GPIO_PIN_2); + + /* D2-D3 and D0-D1. */ + _sdram_gpio_config(GPIOD, GPIO_AF_12, + GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_14 | GPIO_PIN_15); + + /* NBL0, A2, A4, D4-D5 and D8. */ + _sdram_gpio_config(GPIOE, GPIO_AF_12, + GPIO_PIN_0 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_11); + _sdram_gpio_config(GPIOE, GPIO_AF_4, GPIO_PIN_2); + _sdram_gpio_config(GPIOE, GPIO_AF_11, GPIO_PIN_4); + + /* A3, A5, NRAS and A6-A9. */ + _sdram_gpio_config(GPIOF, GPIO_AF_12, + GPIO_PIN_3 | GPIO_PIN_5 | GPIO_PIN_11 | GPIO_PIN_12 | + GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); + + /* A10-A11, D6-D7, BA0-BA1, CLK and NCAS. */ + _sdram_gpio_config(GPIOG, GPIO_AF_12, + GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5 | + GPIO_PIN_8 | GPIO_PIN_15); + _sdram_gpio_config(GPIOG, GPIO_AF_0, GPIO_PIN_2 | GPIO_PIN_3); + + /* SDCKE0 and NBL1. */ + _sdram_gpio_config(GPIOH, GPIO_AF_12, GPIO_PIN_2 | GPIO_PIN_7); +} + +#endif /* BSP_USING_SDRAM */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/fal_cfg.h b/bsp/gd32/arm/gd32h77d-ginopi/board/fal_cfg.h new file mode 100644 index 000000000000..6db2fc34f7b6 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/fal_cfg.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __FAL_CFG_H__ +#define __FAL_CFG_H__ + +#include + +#define GD25Q64E_FLASH_DEV_NAME "gd25q64e" +#define GD25Q64E_FILESYSTEM_PART_NAME "filesystem" +#define GD25Q64E_FLASH_SIZE (8U * 1024U * 1024U) + +extern const struct fal_flash_dev gd25q64e_flash; + +#define FAL_FLASH_DEV_TABLE \ +{ \ + &gd25q64e_flash, \ +} + +#ifdef FAL_PART_HAS_TABLE_CFG +#define FAL_PART_TABLE \ +{ \ + {FAL_PART_MAGIC_WORD, GD25Q64E_FILESYSTEM_PART_NAME, \ + GD25Q64E_FLASH_DEV_NAME, 0, GD25Q64E_FLASH_SIZE, 0}, \ +} +#endif /* FAL_PART_HAS_TABLE_CFG */ + +#endif /* __FAL_CFG_H__ */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/gd32h77x_78x_libopt.h b/bsp/gd32/arm/gd32h77d-ginopi/board/gd32h77x_78x_libopt.h new file mode 100644 index 000000000000..aa911633975b --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/gd32h77x_78x_libopt.h @@ -0,0 +1,104 @@ +/*! + \file gd32h77x_78x_libopt.h + \brief library optional for gd32h77x_78x + + \version 2026-06-18, V0.4.0, demo for GD32H77x_78x +*/ + +/* + Copyright (c) 2026, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#ifndef GD32H77X_78X_LIBOPT_H +#define GD32H77X_78X_LIBOPT_H + +#include "gd32h77x_78x_adc.h" +#include "gd32h77x_78x_axiim.h" +#include "gd32h77x_78x_can.h" +#include "gd32h77x_78x_cau.h" +#include "gd32h77x_78x_cmp.h" +#include "gd32h77x_78x_cpdm.h" +#include "gd32h77x_78x_crc.h" +#include "gd32h77x_78x_ctc.h" +#include "gd32h77x_78x_dac.h" +#include "gd32h77x_78x_dbg.h" +#include "gd32h77x_78x_dci.h" +#include "gd32h77x_78x_dma.h" +#if (defined(GD32H77X_78X_SUPPORT_DSI)) +#include "gd32h77x_78x_dsi.h" +#endif /* (defined(GD32H77X_78X_SUPPORT_DSI)) */ +#include "gd32h77x_78x_edim.h" +#include "gd32h77x_78x_edim_biss.h" +#include "gd32h77x_78x_edim_endat.h" +#include "gd32h77x_78x_edim_afmt.h" +#include "gd32h77x_78x_edim_tfmt.h" +#include "gd32h77x_78x_edim_hdsl.h" +#include "gd32h77x_78x_edout.h" +#include "gd32h77x_78x_enet.h" +#if (defined(GD32H77X_78X_SUPPORT_ESC)) +#include "gd32h77x_78x_esc.h" +#endif /* (defined(GD32H77X_78X_SUPPORT_ESC)) */ +#include "gd32h77x_78x_exmc.h" +#include "gd32h77x_78x_exti.h" +#include "gd32h77x_78x_fac.h" +#include "gd32h77x_78x_fft.h" +#include "gd32h77x_78x_fwdgt.h" +#include "gd32h77x_78x_gpio.h" +#include "gd32h77x_78x_gpsi.h" +#include "gd32h77x_78x_hau.h" +#include "gd32h77x_78x_hpdf.h" +#include "gd32h77x_78x_hwsem.h" +#include "gd32h77x_78x_i2c.h" +#include "gd32h77x_78x_ipa.h" +#include "gd32h77x_78x_lpdts.h" +#include "gd32h77x_78x_mdio.h" +#include "gd32h77x_78x_mdma.h" +#include "gd32h77x_78x_misc.h" +#include "gd32h77x_78x_nvmc.h" +#include "gd32h77x_78x_ospi.h" +#include "gd32h77x_78x_ospim.h" +#include "gd32h77x_78x_pkcau.h" +#include "gd32h77x_78x_pmu.h" +#include "gd32h77x_78x_rameccmu.h" +#include "gd32h77x_78x_rcu.h" +#include "gd32h77x_78x_rdcm.h" +#include "gd32h77x_78x_rspdif.h" +#include "gd32h77x_78x_rtc.h" +#include "gd32h77x_78x_rtdec.h" +#include "gd32h77x_78x_sai.h" +#include "gd32h77x_78x_sdio.h" +#include "gd32h77x_78x_spi.h" +#include "gd32h77x_78x_syscfg.h" +#include "gd32h77x_78x_timer.h" +#include "gd32h77x_78x_tli.h" +#include "gd32h77x_78x_tmu.h" +#include "gd32h77x_78x_trigsel.h" +#include "gd32h77x_78x_trng.h" +#include "gd32h77x_78x_usart.h" +#include "gd32h77x_78x_vref.h" +#include "gd32h77x_78x_wwdgt.h" + +#endif /* GD32H77X_78X_LIBOPT_H */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/linker_scripts/link.icf b/bsp/gd32/arm/gd32h77d-ginopi/board/linker_scripts/link.icf new file mode 100644 index 000000000000..18482a8d58f1 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/linker_scripts/link.icf @@ -0,0 +1,40 @@ +/*###ICF### Section handled by ICF editor, don't touch! ****/ +/*-Editor annotation file-*/ +/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */ +/*-Specials-*/ +define symbol __ICFEDIT_intvec_start__ = 0x08000000; +/*-Memory Regions-*/ +define symbol __ICFEDIT_region_ROM_start__ = 0x08000000; +define symbol __ICFEDIT_region_ROM_end__ = 0x080FFFFF; +define symbol __ICFEDIT_region_RAM_start__ = 0x20000000; +define symbol __ICFEDIT_region_RAM_end__ = 0x2006FFFF; +/*-Sizes-*/ +define symbol __ICFEDIT_size_cstack__ = 0x2000; +define symbol __ICFEDIT_size_heap__ = 0x2000; +/**** End of ICF editor section. ###ICF###*/ + +export symbol __ICFEDIT_region_RAM_end__; + +define symbol __region_RAM1_start__ = 0x10000000; +define symbol __region_RAM1_end__ = 0x1000FFFF; + +define memory mem with size = 4G; +define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__]; +define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__]; +define region RAM1_region = mem:[from __region_RAM1_start__ to __region_RAM1_end__]; + +define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { }; +define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { }; + +initialize by copy { readwrite }; +do not initialize { section .noinit }; + +keep { section FSymTab }; +keep { section VSymTab }; +keep { section .rti_fn* }; +place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec }; + +place in ROM_region { readonly }; +place in RAM_region { readwrite, + block CSTACK, block HEAP }; +place in RAM1_region { section .sram }; diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/linker_scripts/link.ld b/bsp/gd32/arm/gd32h77d-ginopi/board/linker_scripts/link.ld new file mode 100644 index 000000000000..910347b684b8 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/linker_scripts/link.ld @@ -0,0 +1,242 @@ +/* memory map */ +MEMORY +{ + CNVM_BOOT (rx) : ORIGIN = 0x08000000, LENGTH = 8 + CNVM (rx) : ORIGIN = 0x08010000, LENGTH = 1984K + ECNVM (rx) : ORIGIN = 0x08200000, LENGTH = 7680K + AXISRAM (xrw) : ORIGIN = 0x24000000, LENGTH = 768K + SRAM1 (xrw) : ORIGIN = 0x30004000, LENGTH = 16K + ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 128K + DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 256K +} + +ENTRY(Reset_Handler) +_system_stack_size = 0x1000; + +SECTIONS +{ + .boot_fuction_table ORIGIN(CNVM_BOOT) : + { + . = ALIGN(4); + KEEP(*(.boot_fuction_table)) + . = ALIGN(4); + } > CNVM_BOOT + + ASSERT(SIZEOF(.boot_fuction_table) == 8, "invalid boot function table size") + + /* The application vector table starts at 0x08010000. */ + .vectors ORIGIN(CNVM) : + { + . = ALIGN(4); + KEEP(*(.vectors)) + KEEP(*(.isr_vector)) + . = ALIGN(4); + __Vectors_End = .; + __Vectors_Size = __Vectors_End - __gVectors; + } > CNVM + + __Vectors = __gVectors; + + /* Keep startup code in CNVM so DAP-Link/PyOCD can program the primary flash region. */ + .reset : + { + . = ALIGN(4); + _stext = .; + KEEP(*(.text.Reset_Handler)) + . = ALIGN(4); + } > CNVM = 0 + + .text : + { + . = ALIGN(4); + *(.text) + *(.text.*) + *(.gnu.linkonce.t*) + *(.glue_7) + *(.glue_7t) + *(.eh_frame*) + KEEP(*(.init)) + KEEP(*(.fini)) + + *(.rodata) + *(.rodata*) + *(.gnu.linkonce.r*) + + /* section information for finsh shell */ + . = ALIGN(4); + __fsymtab_start = .; + KEEP(*(FSymTab)) + __fsymtab_end = .; + + . = ALIGN(4); + __vsymtab_start = .; + KEEP(*(VSymTab)) + __vsymtab_end = .; + + /* section information for RT-Thread initialization */ + . = ALIGN(4); + __rt_init_start = .; + KEEP(*(SORT(.rti_fn*))) + __rt_init_end = .; + + /* section information for utest */ + . = ALIGN(4); + __rt_utest_tc_tab_start = .; + KEEP(*(UtestTcTab)) + __rt_utest_tc_tab_end = .; + + . = ALIGN(4); + _etext = .; + } > CNVM = 0 + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > CNVM + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } > CNVM + + .preinit_array (READONLY) : + { + PROVIDE_HIDDEN(__preinit_array_start = .); + KEEP(*(.preinit_array*)) + PROVIDE_HIDDEN(__preinit_array_end = .); + } > CNVM + + .init_array (READONLY) : + { + PROVIDE_HIDDEN(__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array*)) + PROVIDE_HIDDEN(__init_array_end = .); + } > CNVM + + .fini_array (READONLY) : + { + PROVIDE_HIDDEN(__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array*)) + PROVIDE_HIDDEN(__fini_array_end = .); + } > CNVM + + .data : + { + . = ALIGN(4); + _sdata = .; + *(.data) + *(.data.*) + *(.gnu.linkonce.d*) + . = ALIGN(4); + _edata = .; + } > AXISRAM AT> CNVM + _sidata = LOADADDR(.data); + + /* Reserve the MSP before the RT-Thread heap begins at __bss_end. */ + .stack (NOLOAD) : + { + . = ALIGN(8); + _sstack = .; + . = . + _system_stack_size; + . = ALIGN(8); + _estack = .; + PROVIDE(_sp = _estack); + } > AXISRAM + + /* Select the CPU-only pool before the generic .bss.* rule below. + * TLSF initializes this memory itself; it is not a DMA buffer. */ + .lvgl_heap (NOLOAD) : + { + . = ALIGN(8); + KEEP(*(.bss.lvgl_heap)) + . = ALIGN(8); + } > DTCMRAM + + .bss (NOLOAD) : + { + . = ALIGN(4); + _sbss = .; + __bss_start = .; + __bss_start__ = .; + *(.bss) + *(.bss.*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + __bss_end = .; + __bss_end__ = .; + } > AXISRAM + + .enet_dma (NOLOAD) : + { + . = ALIGN(32); + KEEP(*(.enet_dma)) + . = ALIGN(32); + } > SRAM1 + + PROVIDE(end = __bss_end); + PROVIDE(_end = __bss_end); + + .itcmram : + { + . = ALIGN(4); + _sitcmram = .; + *(.itcmram) + *(.itcmram*) + . = ALIGN(4); + _eitcmram = .; + } > ITCMRAM AT> CNVM + _siitcmram = LOADADDR(.itcmram); + + .dtcmram : + { + . = ALIGN(4); + _sdtcmram = .; + *(.dtcmram) + *(.dtcmram*) + . = ALIGN(4); + _edtcmram = .; + } > DTCMRAM AT> CNVM + _sidtcmram = LOADADDR(.dtcmram); + + /* 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) } + + /* DWARF debugging sections. */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +} + +ASSERT(SIZEOF(.vectors) > 0, "missing interrupt vector table") +ASSERT(ADDR(.vectors) == ORIGIN(CNVM), "vector table must start at CNVM base") +ASSERT(SIZEOF(.reset) > 0, "missing Reset_Handler") +ASSERT(SIZEOF(.enet_dma) <= LENGTH(SRAM1), "ENET DMA data exceeds SRAM1") +ASSERT(SIZEOF(.lvgl_heap) + SIZEOF(.dtcmram) <= LENGTH(DTCMRAM), "LVGL pool exceeds DTCM") + +/* input sections */ +GROUP(libgcc.a libc.a libm.a libnosys.a) diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/linker_scripts/link.sct b/bsp/gd32/arm/gd32h77d-ginopi/board/linker_scripts/link.sct new file mode 100644 index 000000000000..dbe02f138f8c --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/linker_scripts/link.sct @@ -0,0 +1,32 @@ +; ************************************************************* +; *** Scatter-Loading Description File generated by uVision *** +; ************************************************************* + +LR_IROM1 0x08010000 0x001F0000 { ; load region size_region + ER_IROM1 0x08010000 0x001F0000 { ; load address = execution address + *.o (RESET, +First) + *(InRoot$$Sections) + ; Prefer CNVM for code and constants, matching the GCC image layout. + .ANY2 (+RO) + .ANY (+XO) + } + RW_ENET 0x30004000 UNINIT 0x00004000 { + *(.enet_dma) + } + ; CPU-only LVGL allocations must not fall back to external SDRAM. + RW_LVGL_DTCM 0x20000000 UNINIT 0x00040000 { + *(.bss.lvgl_heap) + } + RW_IRAM1 0x24000000 0x000C0000 { ; RW data + .ANY (+RW +ZI) + } + RW_IRAM2 0x00000000 0x00020000 { + .ANY (+RW +ZI) + } +} + +LR_IROM2 0x08200000 0x00780000 { + ER_IROM2 0x08200000 0x00780000 { ; load address = execution address + .ANY1 (+RO) + } +} diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/ports/SConscript b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/SConscript new file mode 100644 index 000000000000..20cc30976c95 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/SConscript @@ -0,0 +1,18 @@ +import os +from building import * + +cwd = GetCurrentDir() +src = [] + +if GetDepend('BSP_USING_GD32VW553_AT'): + src += ['gd32vw553_at_port.c'] + +if GetDepend('PKG_USING_LVGL'): + src += ['lv_port.c'] + +group = DefineGroup('BoardPorts', src, depend = [''], CPPPATH = [cwd]) + +if GetDepend('BSP_USING_OV7670'): + group += SConscript(os.path.join('camera', 'SConscript')) + +Return('group') diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/SConscript b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/SConscript new file mode 100644 index 000000000000..b28f03b68250 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/SConscript @@ -0,0 +1,11 @@ +from building import * + +cwd = GetCurrentDir() +src = Split(''' + cambus.c + sensor.c + sensors/ov7670.c +''') + +group = DefineGroup('CameraPort', src, depend = [''], CPPPATH = [cwd, cwd + '/sensors']) +Return('group') diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/cam_boardconfig.h b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/cam_boardconfig.h new file mode 100644 index 000000000000..4236e722dc5f --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/cam_boardconfig.h @@ -0,0 +1,24 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * Board configuration for the GD32H77D OV7670 port. + */ +#ifndef __CAM_BOARDCONFIG_H__ +#define __CAM_BOARDCONFIG_H__ + +#define OMV_XCLK_FREQUENCY (24000000U) +#define OMV_CAM_BUS_NAME "hwi2c2" + +#define OMV_ENABLE_OV7670 (1) +#define OMV_OV7670_VERSION (70) +#define OMV_OV7670_CLKRC (0x00) + +#define ISC_I2C_ID (2U) +#define ISC_I2C_SPEED (0U) + +#endif /* __CAM_BOARDCONFIG_H__ */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/cambus.c b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/cambus.c new file mode 100644 index 000000000000..80ca6f1df898 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/cambus.c @@ -0,0 +1,79 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2023 Ibrahim Abdelkader + * Copyright (c) 2013-2023 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * GD32 SCCB adapter. The low-level transfer is shared with drv_ov7670.c so + * that its timeout and bus-recovery behavior remains in one place. + */ +#include + +#include "cam_boardconfig.h" +#include "omv_i2c.h" + +int omv_i2c_init(omv_i2c_t *bus, rt_uint32_t bus_id, rt_uint32_t speed) +{ + if (bus == RT_NULL) + { + return -1; + } + + bus->id = bus_id; + bus->speed = speed; + bus->inst = rt_i2c_bus_device_find(OMV_CAM_BUS_NAME); + bus->initialized = (bus->inst != RT_NULL); + + return bus->initialized ? 0 : -1; +} + +int omv_i2c_deinit(omv_i2c_t *bus) +{ + if (bus == RT_NULL) + { + return -1; + } + + bus->inst = RT_NULL; + bus->initialized = 0U; + return 0; +} + +int omv_i2c_scan(omv_i2c_t *bus, rt_uint8_t *list, rt_uint8_t size) +{ + RT_UNUSED(bus); + RT_UNUSED(list); + RT_UNUSED(size); + return 0; +} + +int omv_i2c_enable(omv_i2c_t *bus, bool enable) +{ + RT_UNUSED(bus); + RT_UNUSED(enable); + return 0; +} + +int omv_i2c_readb(omv_i2c_t *bus, rt_uint8_t slv_addr, + rt_uint8_t reg_addr, rt_uint8_t *reg_data) +{ + if ((bus == RT_NULL) || !bus->initialized || (reg_data == RT_NULL)) + { + return -1; + } + + return gd32_camera_sccb_read(slv_addr, reg_addr, reg_data); +} + +int omv_i2c_writeb(omv_i2c_t *bus, rt_uint8_t slv_addr, + rt_uint8_t reg_addr, rt_uint8_t reg_data) +{ + if ((bus == RT_NULL) || !bus->initialized) + { + return -1; + } + + return gd32_camera_sccb_write(slv_addr, reg_addr, reg_data); +} diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/omv_i2c.h b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/omv_i2c.h new file mode 100644 index 000000000000..fe5be44f46cb --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/omv_i2c.h @@ -0,0 +1,56 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2023 Ibrahim Abdelkader + * Copyright (c) 2013-2023 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + */ +#ifndef __OMV_I2C_H__ +#define __OMV_I2C_H__ + +#include +#include + +#include "omv_portconfig.h" + +typedef enum +{ + OMV_I2C_SPEED_STANDARD = 0U, + OMV_I2C_SPEED_FULL = 1U, + OMV_I2C_SPEED_FAST = 2U, + OMV_I2C_SPEED_MAX = 3U +} omv_i2c_speed_t; + +typedef enum +{ + OMV_I2C_XFER_NO_FLAGS = 0U, + OMV_I2C_XFER_NO_STOP = 1U << 0, + OMV_I2C_XFER_SUSPEND = 1U << 1 +} omv_i2c_xfer_flags_t; + +typedef struct +{ + rt_uint32_t id; + rt_uint32_t speed; + rt_uint32_t initialized; + omv_gpio_t scl_pin; + omv_gpio_t sda_pin; + omv_i2c_dev_t inst; +} omv_i2c_t; + +int omv_i2c_init(omv_i2c_t *bus, rt_uint32_t bus_id, rt_uint32_t speed); +int omv_i2c_deinit(omv_i2c_t *bus); +int omv_i2c_scan(omv_i2c_t *bus, rt_uint8_t *list, rt_uint8_t size); +int omv_i2c_enable(omv_i2c_t *bus, bool enable); +int omv_i2c_readb(omv_i2c_t *bus, rt_uint8_t slv_addr, + rt_uint8_t reg_addr, rt_uint8_t *reg_data); +int omv_i2c_writeb(omv_i2c_t *bus, rt_uint8_t slv_addr, + rt_uint8_t reg_addr, rt_uint8_t reg_data); + +int gd32_camera_sccb_read(rt_uint8_t slv_addr, rt_uint8_t reg, + rt_uint8_t *value); +int gd32_camera_sccb_write(rt_uint8_t slv_addr, rt_uint8_t reg, + rt_uint8_t value); + +#endif /* __OMV_I2C_H__ */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/omv_portconfig.h b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/omv_portconfig.h new file mode 100644 index 000000000000..f5b939c14bf6 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/omv_portconfig.h @@ -0,0 +1,18 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + */ +#ifndef __OMV_PORTCONFIG_H__ +#define __OMV_PORTCONFIG_H__ + +#include +#include + +typedef rt_uint32_t omv_gpio_t; +typedef struct rt_i2c_bus_device *omv_i2c_dev_t; + +#endif /* __OMV_PORTCONFIG_H__ */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/sensor.c b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/sensor.c new file mode 100644 index 000000000000..3bf72f8337cb --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/sensor.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2006-2026 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Minimal OpenMV-compatible sensor layer for OV7670. + */ +#include + +#include "cam_boardconfig.h" +#include "sensor.h" + +sensor_t sensor; + +int sensor_init(void) +{ + rt_uint8_t pid; + rt_uint8_t ver; + + memset(&sensor, 0, sizeof(sensor)); + sensor.slv_addr = 0x42U; + + if (omv_i2c_init(&sensor.i2c_bus, ISC_I2C_ID, ISC_I2C_SPEED) != 0) + { + return -1; + } + + if (omv_i2c_readb(&sensor.i2c_bus, sensor.slv_addr, 0x0AU, &pid) != 0) + { + return -1; + } + if (omv_i2c_readb(&sensor.i2c_bus, sensor.slv_addr, 0x0BU, &ver) != 0) + { + return -1; + } + if (pid != OV7670_ID) + { + return -1; + } + + sensor.chip_id = pid; + if (ov7670_init(&sensor) != 0) + { + return -1; + } + + sensor.detected = true; + RT_UNUSED(ver); + return 0; +} + +int sensor_reset(void) +{ + return (sensor.reset != RT_NULL) ? sensor.reset(&sensor) : -1; +} + +int sensor_read_reg(uint16_t reg_addr) +{ + return (sensor.read_reg != RT_NULL) ? sensor.read_reg(&sensor, reg_addr) : -1; +} + +int sensor_write_reg(uint16_t reg_addr, uint16_t reg_data) +{ + return (sensor.write_reg != RT_NULL) ? + sensor.write_reg(&sensor, reg_addr, reg_data) : -1; +} + +int sensor_set_pixformat(pixformat_t pixformat) +{ + int ret; + + if (sensor.set_pixformat == RT_NULL) + { + return -1; + } + ret = sensor.set_pixformat(&sensor, pixformat); + if (ret == 0) + { + sensor.pixformat = pixformat; + } + return ret; +} + +int sensor_set_framesize(framesize_t framesize) +{ + int ret; + + if (sensor.set_framesize == RT_NULL) + { + return -1; + } + ret = sensor.set_framesize(&sensor, framesize); + if (ret == 0) + { + sensor.framesize = framesize; + } + return ret; +} + +int sensor_set_hmirror(int enable) +{ + int ret; + + if (sensor.set_hmirror == RT_NULL) + { + return -1; + } + ret = sensor.set_hmirror(&sensor, enable); + if (ret == 0) + { + sensor.hmirror = (enable != 0); + } + return ret; +} + +int sensor_set_vflip(int enable) +{ + int ret; + + if (sensor.set_vflip == RT_NULL) + { + return -1; + } + ret = sensor.set_vflip(&sensor, enable); + if (ret == 0) + { + sensor.vflip = (enable != 0); + } + return ret; +} + +bool sensor_is_detected(void) +{ + return sensor.detected; +} diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/sensor.h b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/sensor.h new file mode 100644 index 000000000000..a6084b6e2cfc --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/sensor.h @@ -0,0 +1,88 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * Minimal sensor abstraction used by the GD32 OV7670 port. + */ +#ifndef __SENSOR_H__ +#define __SENSOR_H__ + +#include +#include +#include + +#include "omv_i2c.h" + +#define OV_CHIP_ID (0x0AU) +#define OV7670_ID (0x76U) +#define SENSOR_HW_FLAGS_YVU422 (1U) + +typedef enum +{ + PIXFORMAT_INVALID = 0, + PIXFORMAT_RGB565, + PIXFORMAT_YUV422, + PIXFORMAT_GRAYSCALE +} pixformat_t; + +typedef enum +{ + FRAMESIZE_INVALID = 0, + FRAMESIZE_QQVGA, + FRAMESIZE_QVGA, + FRAMESIZE_VGA +} framesize_t; + +typedef struct _sensor sensor_t; + +struct _sensor +{ + rt_uint8_t chip_id; + rt_uint8_t slv_addr; + struct + { + rt_uint32_t vsync : 1; + rt_uint32_t hsync : 1; + rt_uint32_t pixck : 1; + rt_uint32_t fsync : 1; + rt_uint32_t jpege : 1; + rt_uint32_t gs_bpp : 2; + rt_uint32_t rgb_swap : 1; + rt_uint32_t yuv_order : 1; + } hw_flags; + bool disable_delays; + bool detected; + pixformat_t pixformat; + framesize_t framesize; + bool hmirror; + bool vflip; + omv_i2c_t i2c_bus; + + int (*reset)(sensor_t *sensor); + int (*sleep)(sensor_t *sensor, int enable); + int (*read_reg)(sensor_t *sensor, uint16_t reg_addr); + int (*write_reg)(sensor_t *sensor, uint16_t reg_addr, uint16_t reg_data); + int (*set_pixformat)(sensor_t *sensor, pixformat_t pixformat); + int (*set_framesize)(sensor_t *sensor, framesize_t framesize); + int (*set_hmirror)(sensor_t *sensor, int enable); + int (*set_vflip)(sensor_t *sensor, int enable); +}; + +extern sensor_t sensor; + +int ov7670_init(sensor_t *sensor); +int sensor_init(void); +int sensor_reset(void); +int sensor_read_reg(uint16_t reg_addr); +int sensor_write_reg(uint16_t reg_addr, uint16_t reg_data); +int sensor_set_pixformat(pixformat_t pixformat); +int sensor_set_framesize(framesize_t framesize); +int sensor_set_hmirror(int enable); +int sensor_set_vflip(int enable); +bool sensor_is_detected(void); + +#endif /* __SENSOR_H__ */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/sensors/ov7670.c b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/sensors/ov7670.c new file mode 100644 index 000000000000..d310b0e5a980 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/sensors/ov7670.c @@ -0,0 +1,462 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * OV7670 driver. + */ +#include "cam_boardconfig.h" +#if (OMV_ENABLE_OV7670 == 1) + +#include +#include +#include + +#include "omv_i2c.h" +#include "sensor.h" +#include "ov7670.h" +#include "ov7670_regs.h" + +static const uint8_t default_regs[][2] = { + // OV7670 reference registers + #if (OMV_OV7670_CLKRC == 0) + { CLKRC, CLKRC_PRESCALER_BYPASS }, + #else + { CLKRC, OMV_OV7670_CLKRC }, + #endif + { TSLB, 0x04 }, + { COM7, 0x00 }, + { HSTART, 0xE7 }, + { HSTOP, 0x01 }, + { HREF, 0xb6 }, + { VSTART, 0x02 }, + { VSTOP, 0x7a }, + { VREF, 0x0a }, + + { COM3, 0x00 }, + { COM14, 0x00 }, + { SCALING_XSC, 0x3a }, + { SCALING_YSC, 0x35 }, + { SCALING_DCWCTR, 0x11 }, + { SCALING_PCLK_DIV, 0xf0 }, + { SCALING_PCLK, 0x02 }, + + /* Gamma curve values */ + { SLOP, 0x20 }, + { GAM1, 0x10 }, + { GAM2, 0x1e }, + { GAM3, 0x35 }, + { GAM4, 0x5a }, + { GAM5, 0x69 }, + { GAM6, 0x76 }, + { GAM7, 0x80 }, + { GAM8, 0x88 }, + { GAM9, 0x8f }, + { GAM10, 0x96 }, + { GAM11, 0xa3 }, + { GAM12, 0xaf }, + { GAM13, 0xc4 }, + { GAM14, 0xd7 }, + { GAM15, 0xe8 }, + + /* AGC and AEC parameters */ + { COM8, COM8_FAST_AEC | COM8_AEC_STEP | COM8_BANDF_EN }, + { GAIN, 0x00 }, + { AECH, 0x00 }, + { COM4, 0x40 }, + { COM9, 0x18 }, + { BD50MAX, 0x05 }, + { BD60MAX, 0x07 }, + { AEW, 0x95 }, + { AEB, 0x33 }, + { VPT, 0xe3 }, + { HAECC1, 0x78 }, + { HAECC2, 0x68 }, + { 0xa1, 0x03 }, + { HAECC3, 0xd8 }, + { HAECC4, 0xd8 }, + { HAECC5, 0xf0 }, + { HAECC6, 0x90 }, + { HAECC7, 0x94 }, + { COM8, COM8_FAST_AEC | COM8_AEC_STEP | COM8_BANDF_EN | COM8_AGC_EN | COM8_AEC_EN }, + + /* Almost all of these are magic "reserved" values. */ + { COM5, 0x61 }, + { COM6, 0x4b }, + { 0x16, 0x02 }, + { 0x21, 0x02 }, + { 0x22, 0x91 }, + { 0x29, 0x07 }, + { 0x33, 0x0b }, + { 0x35, 0x0b }, + { 0x37, 0x1d }, + { 0x38, 0x71 }, + { 0x39, 0x2a }, + { COM12, 0x78 }, + { 0x4d, 0x40 }, + { 0x4e, 0x20 }, + { GFIX, 0x00 }, + { 0x6b, 0x00 }, // PLL Bypass + { 0x74, 0x10 }, + { 0x8d, 0x4f }, + { 0x8e, 0x00 }, + { 0x8f, 0x00 }, + { 0x90, 0x00 }, + { 0x91, 0x00 }, + { 0x96, 0x00 }, + { 0x9a, 0x00 }, + { 0xb0, 0x84 }, + { 0xb1, 0x0c }, + { 0xb2, 0x0e }, + { 0xb3, 0x82 }, + { 0xb8, 0x0a }, + { 0x43, 0x0a }, + { 0x44, 0xf0 }, + { 0x45, 0x34 }, + { 0x46, 0x58 }, + { 0x47, 0x28 }, + { 0x48, 0x3a }, + { 0x59, 0x88 }, + { 0x5a, 0x88 }, + { 0x5b, 0x44 }, + { 0x5c, 0x67 }, + { 0x5d, 0x49 }, + { 0x5e, 0x0e }, + { 0x6c, 0x0a }, + { 0x6d, 0x55 }, + { 0x6e, 0x11 }, + { 0x6f, 0x9e }, + { 0x6a, 0x40 }, + { BLUE, 0x40 }, + { RED, 0x60 }, + { COM8, COM8_FAST_AEC | COM8_AEC_STEP | COM8_BANDF_EN | COM8_AGC_EN | COM8_AEC_EN | COM8_AWB_EN }, + + /* Matrix coefficients */ + { MTX1, 0x80 }, + { MTX2, 0x80 }, + { MTX3, 0x00 }, + { MTX4, 0x22 }, + { MTX5, 0x5e }, + { MTX6, 0x80 }, + { MTXS, 0x9e }, + + { COM16, COM16_AWB_GAIN_EN }, + { EDGE, 0x00 }, + { 0x75, 0x05 }, + { 0x76, 0xe1 }, + { 0x4c, 0x00 }, + { 0x77, 0x01 }, + { COM13, 0xc3 }, + { 0x4b, 0x09 }, + { 0xc9, 0x60 }, + { COM16, 0x38 }, + { 0x56, 0x40 }, + + { 0x34, 0x11 }, + { COM11, COM11_AEC_BANDF | COM11_BANDF_SELECT }, + { 0xa4, 0x88 }, + { 0x96, 0x00 }, + { 0x97, 0x30 }, + { 0x98, 0x20 }, + { 0x99, 0x30 }, + { 0x9a, 0x84 }, + { 0x9b, 0x29 }, + { 0x9c, 0x03 }, + { 0x9d, 0x4c }, + { 0x9e, 0x3f }, + { 0x78, 0x04 }, + { 0x79, 0x01 }, + { 0xc8, 0xf0 }, + { 0x79, 0x0f }, + { 0xc8, 0x00 }, + { 0x79, 0x10 }, + { 0xc8, 0x7e }, + { 0x79, 0x0a }, + { 0xc8, 0x80 }, + { 0x79, 0x0b }, + { 0xc8, 0x01 }, + { 0x79, 0x0c }, + { 0xc8, 0x0f }, + { 0x79, 0x0d }, + { 0xc8, 0x20 }, + { 0x79, 0x09 }, + { 0xc8, 0x80 }, + { 0x79, 0x02 }, + { 0xc8, 0xc0 }, + { 0x79, 0x03 }, + { 0xc8, 0x40 }, + { 0x79, 0x05 }, + { 0xc8, 0x30 }, + { 0x79, 0x26 }, + + { 0xFF, 0xFF }, +}; + +#if (OMV_OV7670_VERSION == 75) +static const uint8_t rgb565_regs[][2] = { + { COM7, COM7_RGB_FMT }, /* Selects RGB mode */ + { RGB444, 0x00 }, /* No RGB444 please */ + { COM1, 0x00 }, /* CCIR601 */ + { COM15, COM15_OUT_00_FF | COM15_FMT_RGB565}, + { COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0x00 }, /* "matrix Coefficient 3" */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { COM13, COM13_GAMMA_EN | COM13_UVSAT_AUTO }, + { 0xFF, 0xFF } +}; +#elif (OMV_OV7670_VERSION == 70) +static const uint8_t rgb565_regs[][2] = { + { COM7, COM7_RGB_FMT }, /* Selects RGB mode */ + { RGB444, 0 }, /* No RGB444 please */ + { COM1, 0x0 }, /* CCIR601 */ + { COM15, COM15_FMT_RGB565 | COM15_OUT_00_FF}, + { COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { MTX1, 0xb3 }, /* "matrix coefficient 1" */ + { MTX2, 0xb3 }, /* "matrix coefficient 2" */ + { MTX3, 0 }, /* vb */ + { MTX4, 0x3d }, /* "matrix coefficient 4" */ + { MTX5, 0xa7 }, /* "matrix coefficient 5" */ + { MTX6, 0xe4 }, /* "matrix coefficient 6" */ + { COM13, COM13_GAMMA_EN | COM13_UVSAT_AUTO}, + { 0xFF, 0xFF } +}; +#else +#error "OV767x sensor is Not defined." +#endif + +// TODO: These registers probably need to be fixed too. +static const uint8_t yuv422_regs[][2] = { + { COM7, 0x00 }, /* Selects YUV mode */ + { RGB444, 0x00 }, /* No RGB444 please */ + { COM1, 0x00 }, /* CCIR601 */ + { COM15, COM15_OUT_00_FF }, + { COM9, 0x48 }, /* 32x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0x80 }, /* "matrix coefficient 1" */ + { 0x50, 0x80 }, /* "matrix coefficient 2" */ + { 0x51, 0x00 }, /* vb */ + { 0x52, 0x22 }, /* "matrix coefficient 4" */ + { 0x53, 0x5e }, /* "matrix coefficient 5" */ + { 0x54, 0x80 }, /* "matrix coefficient 6" */ + { COM13, COM13_GAMMA_EN | COM13_UVSAT_AUTO }, + { 0xFF, 0xFF } +}; + +static const uint8_t vga_regs[][2] = { + { COM3, 0x00 }, + { COM14, 0x00 }, + { 0x72, 0x11 }, // downsample by 4 + { 0x73, 0xf0 }, // divide by 4 + { HSTART, 0x13 }, + { HSTOP, 0x01 }, + { HREF, 0xb6 }, + { VSTART, 0x03 }, + { VSTOP, 0x00 }, + { VREF, 0x0a }, + { 0xFF, 0xFF }, +}; + +#if (OMV_OV7670_VERSION == 75) +static const uint8_t qvga_regs[][2] = { + { COM3, COM3_DCW_EN }, + { COM14, 0x11 }, // Divide by 2 + { 0x72, 0x22 }, // This has no effect on OV7675 + { 0x73, 0xf2 }, // This has no effect on OV7675 + { HSTART, 0x15 }, + { HSTOP, 0x03 }, + { HREF, 0xC0 }, + { VSTART, 0x03 }, + { VSTOP, 0x7B }, + { VREF, 0xF0 }, + { 0xFF, 0xFF }, +}; +#else +static const uint8_t qvga_regs[][2] = { + { COM3, COM3_DCW_EN }, + { COM14, 0x19 }, + { 0x72, 0x11 }, // downsample by 2 + { 0x73, 0xf1 }, // divide by 2 + { HSTART, 0x15 }, + { HSTOP, 0x04 }, + { HREF, 0xa4 }, + { VSTART, 0x02 }, + { VSTOP, 0x7a }, + { VREF, 0x0a }, + { 0XFF, 0XFF }, +}; +#endif + +#if (OMV_OV7670_VERSION == 75) +static const uint8_t qqvga_regs[][2] = { + { COM3, COM3_DCW_EN }, + { COM14, 0x11 }, // Divide by 2 + { 0x72, 0x22 }, // This has no effect on OV7675 + { 0x73, 0xf2 }, // This has no effect on OV7675 + { HSTART, 0x16 }, + { HSTOP, 0x04 }, + { HREF, 0xa4 }, + { VSTART, 0x22 }, + { VSTOP, 0x7a }, + { VREF, 0xfa }, + { 0xFF, 0xFF }, +}; +#else +static const uint8_t qqvga_regs[][2] = { + { COM3, COM3_DCW_EN }, + { COM14, 0x1a}, + { 0x72, 0x22 }, // downsample by 4 + { 0x73, 0xf2 }, // divide by 4 + { HSTART, 0x16 }, + { HSTOP, 0x04 }, + { HREF, 0xa4 }, + { VSTART, 0x02 }, + { VSTOP, 0x7a }, + { VREF, 0x0a }, + { 0XFF, 0XFF }, +}; +#endif + +static int reset(sensor_t *sensor) { + // Reset all registers + int ret = omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, COM7, COM7_RESET); + + // Delay 2 ms + rt_thread_mdelay(2); + + // Write default registers + for (int i = 0; default_regs[i][0] != 0xff; i++) { + ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, default_regs[i][0], default_regs[i][1]); + } + + // Delay 300 ms + if (!sensor->disable_delays) { + rt_thread_mdelay(300); + } + + return ret; +} + +static int sleep(sensor_t *sensor, int enable) { + uint8_t reg; + int ret = omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, COM2, ®); + + if (enable) { + reg |= COM2_SOFT_SLEEP; + } else { + reg &= ~COM2_SOFT_SLEEP; + } + + // Write back register + return omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, COM2, reg) | ret; +} + +static int read_reg(sensor_t *sensor, uint16_t reg_addr) { + uint8_t reg_data; + if (omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, reg_addr, ®_data) != 0) { + return -1; + } + return reg_data; +} + +static int write_reg(sensor_t *sensor, uint16_t reg_addr, uint16_t reg_data) { + return omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, reg_addr, reg_data); +} + +static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) { + int ret = 0; + const uint8_t(*regs)[2]; + + switch (pixformat) { + case PIXFORMAT_RGB565: + regs = rgb565_regs; + break; + case PIXFORMAT_YUV422: + case PIXFORMAT_GRAYSCALE: + regs = yuv422_regs; + break; + default: + return -1; + } + + // Write pixel format registers + for (int i = 0; regs[i][0] != 0xff; i++) { + ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, regs[i][0], regs[i][1]); + } + + return ret; +} + +static int set_framesize(sensor_t *sensor, framesize_t framesize) { + int ret = 0; + const uint8_t(*regs)[2]; + + switch (framesize) { + case FRAMESIZE_VGA: + regs = vga_regs; + break; + case FRAMESIZE_QVGA: + regs = qvga_regs; + break; + case FRAMESIZE_QQVGA: + regs = qqvga_regs; + break; + default: + return -1; + } + + // Write pixel format registers + for (int i = 0; regs[i][0] != 0xFF; i++) { + ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, regs[i][0], regs[i][1]); + } + return ret; +} + +static int set_hmirror(sensor_t *sensor, int enable) { + uint8_t val; + int ret = omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, MVFP, &val); + ret |= + omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, MVFP, + enable ? (val | MVFP_MIRROR) : (val & (~MVFP_MIRROR))); + + return ret; +} + +static int set_vflip(sensor_t *sensor, int enable) { + uint8_t val; + int ret = omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, MVFP, &val); + ret |= + omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, MVFP, + enable ? (val | MVFP_VFLIP) : (val & (~MVFP_VFLIP))); + + return ret; +} +int ov7670_init(sensor_t *sensor) { + // Initialize sensor structure. + sensor->reset = reset; + sensor->sleep = sleep; + sensor->read_reg = read_reg; + sensor->write_reg = write_reg; + sensor->set_pixformat = set_pixformat; + sensor->set_framesize = set_framesize; + sensor->set_hmirror = set_hmirror; + sensor->set_vflip = set_vflip; + + // Set sensor flags + sensor->hw_flags.vsync = 1; + sensor->hw_flags.hsync = 0; + sensor->hw_flags.pixck = 1; + sensor->hw_flags.fsync = 0; + sensor->hw_flags.jpege = 0; + sensor->hw_flags.gs_bpp = 2; + sensor->hw_flags.rgb_swap = 1; + sensor->hw_flags.yuv_order = SENSOR_HW_FLAGS_YVU422; + + return 0; +} +#endif // (OMV_ENABLE_OV7670 == 1) diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/sensors/ov7670.h b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/sensors/ov7670.h new file mode 100644 index 000000000000..5b582ebb7993 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/sensors/ov7670.h @@ -0,0 +1,15 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * OV7670 driver. + */ +#ifndef __OV7670_H__ +#define __OV7670_H__ +#define OV7670_XCLK_FREQ 24000000 +int ov7670_init(sensor_t *sensor); +#endif // __OV7725_H__ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/sensors/ov7670_regs.h b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/sensors/ov7670_regs.h new file mode 100644 index 000000000000..2a24fa2204cf --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/camera/sensors/ov7670_regs.h @@ -0,0 +1,357 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * OV7670 register definitions. + */ +#ifndef __REG_REGS_H__ +#define __REG_REGS_H__ + +#define GAIN 0x00 /* AGC - Gain control gain setting */ +#define BLUE 0x01 /* AWB - Blue channel gain setting */ +#define RED 0x02 /* AWB - Red channel gain setting */ +#define VREF 0x03 /* Vertical Frame Control */ +#define COM1 0x04 /* Common Control 1 */ +#define BAVE 0x05 /* U/B Average Level */ +#define GBAVE 0x06 /* Y/Gb Average Level */ +#define AECHH 0x07 /* Exposure Value - AEC MSB 5 bits */ +#define RAVE 0x08 /* V/R Average Level */ + +#define COM2 0x09 /* Common Control 2 */ +#define COM2_SOFT_SLEEP 0x10 /* Soft sleep mode */ +#define COM2_OUT_DRIVE_1x 0x00 /* Output drive capability 1x */ +#define COM2_OUT_DRIVE_2x 0x01 /* Output drive capability 2x */ +#define COM2_OUT_DRIVE_3x 0x02 /* Output drive capability 3x */ +#define COM2_OUT_DRIVE_4x 0x03 /* Output drive capability 4x */ + +#define PID 0x0A /* Product ID Number MSB */ +#define VER 0x0B /* Product ID Number LSB */ + +#define COM3 0x0C /* Common Control 3 */ +#define COM3_SWAP_MSB 0x40 /* Output data MSB and LSB swap */ +#define COM3_TRI_CLOCK 0x20 /* Tri-state option for output clock at power-down period */ +#define COM3_TRI_DATA 0x10 /* Tri-state option for output data at power-down period */ +#define COM3_SCALE_EN 0x08 /* Scale enable */ +#define COM3_DCW_EN 0x04 /* DCW enable */ + +#define COM4 0x0D /* Common Control 4 */ +#define COM4_AEC_FULL 0x00 /* AEC evaluate full window */ +#define COM4_AEC_1_2 0x10 /* AEC evaluate 1/2 window */ +#define COM4_AEC_1_4 0x20 /* AEC evaluate 1/4 window */ +#define COM4_AEC_2_3 0x30 /* AEC evaluate 2/3 window */ + +#define COM5 0x0E /* Reserved */ + +#define COM6 0x0F /* Common Control 6 */ +#define COM6_HREF_BLACK_EN 0x80 /* Enable HREF at optical black */ +#define COM6_RESET_TIMING 0x02 /* Reset all timing when format changes */ + +#define AECH 0x10 /* AEC[9:2] (see registers AECHH for AEC[15:10] and COM1 for AEC[1:0]) */ + +#define CLKRC 0x11 /* Internal Clock */ +#define CLKRC_PRESCALER_BYPASS 0x40 /* Use external clock directly */ +#define CLKRC_PRESCALER 0x3F /* Internal clock pre-scaler */ + +#define COM7 0x12 /* Common Control 7 */ +#define COM7_RESET 0x80 /* SCCB Register Reset */ +#define COM7_RES_VGA 0x00 /* Resolution VGA */ +#define COM7_RES_CIF 0x20 /* Resolution CIF */ +#define COM7_RES_QVGA 0x10 /* Resolution QVGA */ +#define COM7_RES_QCIF 0x08 /* Resolution QCIF */ +#define COM7_RGB_FMT 0x04 /* Output format RGB */ +#define COM7_CBAR_EN 0x02 /* Color bar selection */ +#define COM7_FMT_RAW 0x01 /* Output format Bayer RAW */ + +#define COM8 0x13 /* Common Control 8 */ +#define COM8_FAST_AEC 0x80 /* Enable fast AGC/AEC algorithm */ +#define COM8_AEC_STEP 0x40 /* AEC - Step size unlimited step size */ +#define COM8_BANDF_EN 0x20 /* Banding filter ON/OFF */ +#define COM8_AGC_EN 0x04 /* AGC Enable */ +#define COM8_AWB_EN 0x02 /* AWB Enable */ +#define COM8_AEC_EN 0x01 /* AEC Enable */ + +#define COM9 0x14 /* Common Control 9 */ +#define COM9_AGC_GAIN_2x 0x00 /* Automatic Gain Ceiling 2x */ +#define COM9_AGC_GAIN_4x 0x10 /* Automatic Gain Ceiling 4x */ +#define COM9_AGC_GAIN_8x 0x20 /* Automatic Gain Ceiling 8x */ +#define COM9_AGC_GAIN_16x 0x30 /* Automatic Gain Ceiling 16x */ +#define COM9_AGC_GAIN_32x 0x40 /* Automatic Gain Ceiling 32x */ +#define COM9_AGC_GAIN_64x 0x50 /* Automatic Gain Ceiling 64x */ +#define COM9_AGC_GAIN_128x 0x60 /* Automatic Gain Ceiling 128x */ +#define COM9_SET_AGC(r, x) ((r & 0x8F) | ((x & 0x07) << 4)) + +#define COM10 0x15 /* Common Control 10 */ +#define COM10_HSYNC_EN 0x40 /* HREF changes to HSYNC */ +#define COM10_PCLK_FREE 0x00 /* PCLK output option: free running PCLK */ +#define COM10_PCLK_MASK 0x20 /* PCLK output option: masked during horizontal blank */ +#define COM10_PCLK_REV 0x10 /* PCLK reverse */ +#define COM10_HREF_REV 0x08 /* HREF reverse */ +#define COM10_VSYNC_FALLING 0x00 /* VSYNC changes on falling edge of PCLK */ +#define COM10_VSYNC_RISING 0x04 /* VSYNC changes on rising edge of PCLK */ +#define COM10_VSYNC_NEG 0x02 /* VSYNC negative */ +#define COM10_HSYNC_NEG 0x01 /* HSYNC negative */ + +#define HSTART 0x17 /* Output Format - Horizontal Frame (HREF column) start high 8-bit */ +#define HSTOP 0x18 /* Output Format - Horizontal Frame (HREF column) end high 8-bit */ +#define VSTART 0x19 /* Output Format - Vertical Frame (row) start high 8-bit */ +#define VSTOP 0x1A /* Output Format - Vertical Frame (row) end high 8-bit */ +#define PSHFT 0x1B /* Data Format - Pixel Delay Select (delays timing of the D[7:0]) */ +#define MIDH 0x1C /* Manufacturer ID Byte - High */ +#define MIDL 0x1D /* Manufacturer ID Byte - Low */ + +#define MVFP 0x1E /* Mirror/VFlip Enable */ +#define MVFP_MIRROR 0x20 /* Mirror image */ +#define MVFP_VFLIP 0x10 /* VFlip image */ +#define MVFP_BLACK_SUN_EN 0x04 /* Black sun enable */ + +#define LAEC 0x1F /* Reserved */ + +#define ADCCTR0 0x20 /* ADC Control */ +#define ADCCTR0_ADC_RANGE_1X 0x00 /* ADC range adjustment 1x range */ +#define ADCCTR0_ADC_RANGE_1_5X 0x08 /* ADC range adjustment 1.5x range */ +#define ADCCTR0_ADC_REF_0_8 0x00 /* ADC reference adjustment 0.8x */ +#define ADCCTR0_ADC_REF_1_0 0x04 /* ADC reference adjustment 1.0x */ +#define ADCCTR0_ADC_REF_1_2 0x07 /* ADC reference adjustment 1.2x */ + +#define ADCCTR1 0x21 /* Reserved */ +#define ADCCTR2 0x22 /* Reserved */ +#define ADCCTR3 0x23 /* Reserved */ + +#define AEW 0x24 /* AGC/AEC - Stable Operating Region (Upper Limit) */ +#define AEB 0x25 /* AGC/AEC - Stable Operating Region (Lower Limit) */ +#define VPT 0x26 /* AGC/AEC Fast Mode Operating Region */ +#define BBIAS 0x27 /* B Channel Signal Output Bias (effective only when COM6[3] = 1) */ +#define GBIAS 0x28 /* Gb Channel Signal Output Bias (effective only when COM6[3] = 1) */ +#define EXHCH 0x2A /* Dummy Pixel Insert MSB */ +#define EXHCL 0x2B /* Dummy Pixel Insert LSB */ +#define RBIAS 0x2C /* R Channel Signal Output Bias (effective only when COM6[3] = 1) */ +#define ADVFL 0x2D /* LSB of insert dummy lines in vertical direction (1 bit equals 1 line) */ +#define ADVFH 0x2E /* MSB of insert dummy lines in vertical direction */ +#define YAVE 0x2F /* Y/G Channel Average Value */ + +#define HSYST 0x30 /* HSYNC Rising Edge Delay (low 8 bits) */ +#define HSYEN 0x31 /* HSYNC Falling Edge Delay (low 8 bits) */ +#define HREF 0x32 /* HREF Control */ +#define CHLF 0x33 /* Array Current Control */ +#define ARBLM 0x34 /* Array Reference Control */ +#define ADC 0x37 /* ADC Control */ +#define ACOM 0x38 /* ADC and Analog Common Mode Control */ +#define OFON 0x39 /* ADC Offset Control */ + +#define TSLB 0x3A /* Line Buffer Test Option */ +#define TSLB_NEGATIVE 0x20 /* Negative image enable */ +#define TSLB_UV_NORMAL 0x00 /* UV output value normal */ +#define TSLB_UV_FIXED 0x10 /* UV output value set in registers MANU and MANV */ +#define TSLB_AUTO_WINDOW 0x01 /* automatically sets output window when resolution changes */ + +#define COM11 0x3B /* Common Control 11 */ +#define COM11_AFR 0x80 /* Auto frame rate control ON/OFF selection (night mode) */ +#define COM11_AFR_0 0x00 /* No reduction of frame rate */ +#define COM11_AFR_1_2 0x20 /* Max reduction to 1/2 frame rate */ +#define COM11_AFR_1_4 0x40 /* Max reduction to 1/4 frame rate */ +#define COM11_AFR_1_8 0x60 /* Max reduction to 1/8 frame rate */ +#define COM11_HZAUTO_EN 0x10 /* Enable 50/60 Hz auto detection */ +#define COM11_BANDF_SELECT 0x08 /* Banding filter value selection */ +#define COM11_AEC_BANDF 0x02 /* Enable AEC below banding value */ + +#define COM12 0x3C /* Common Control 12 */ +#define COM12_HREF_EN 0x80 /* Always has HREF */ + +#define COM13 0x3D /* Common Control 13 */ +#define COM13_GAMMA_EN 0x80 /* Gamma enable */ +#define COM13_UVSAT_AUTO 0x40 /* UV saturation level - UV auto adjustment. */ +#define COM13_UV_SWAP 0x01 /* UV swap */ + +#define COM14 0x3E /* Common Control 14 */ +#define COM14_DCW_EN 0x10 /* DCW and scaling PCLK */ +#define COM14_MANUAL_SCALE 0x08 /* Manual scaling enable for pre-defined resolutions */ +#define COM14_PCLK_DIV_1 0x00 /* PCLK divider. Divided by 1 */ +#define COM14_PCLK_DIV_2 0x01 /* PCLK divider. Divided by 2 */ +#define COM14_PCLK_DIV_4 0x02 /* PCLK divider. Divided by 4 */ +#define COM14_PCLK_DIV_8 0x03 /* PCLK divider. Divided by 8 */ +#define COM14_PCLK_DIV_16 0x04 /* PCLK divider. Divided by 16 */ + +#define EDGE 0x3F /* Edge Enhancement Adjustment */ +#define EDGE_FACTOR_MASK 0x1F /* Edge enhancement factor */ + +#define COM15 0x40 /* Common Control 15 */ +#define COM15_OUT_10_F0 0x00 /* Output data range 10 to F0 */ +#define COM15_OUT_01_FE 0x80 /* Output data range 01 to FE */ +#define COM15_OUT_00_FF 0xC0 /* Output data range 00 to FF */ +#define COM15_FMT_RGB_NORMAL 0x00 /* Normal RGB normal output */ +#define COM15_FMT_RGB565 0x10 /* Normal RGB 565 output */ +#define COM15_FMT_RGB555 0x30 /* Normal RGB 555 output */ + +#define COM16 0x41 /* Common Control 16 */ +#define COM16_EDGE_EN 0x20 /* Enable edge enhancement threshold auto-adjustment */ +#define COM16_DENOISE_EN 0x10 /* De-noise threshold auto-adjustment */ +#define COM16_AWB_GAIN_EN 0x08 /* AWB gain enable */ +#define COM16_COLOR_MTX_EN 0x02 /* Color matrix coefficient double option */ + +#define COM17 0x42 /* Common Control 17 */ +#define COM17_AEC_FULL 0x00 /* AEC evaluate full window */ +#define COM17_AEC_1_2 0x40 /* AEC evaluate 1/2 window */ +#define COM17_AEC_1_4 0x80 /* AEC evaluate 1/4 window */ +#define COM17_AEC_2_3 0xC0 /* AEC evaluate 2/3 window */ +#define COM17_DSP_CBAR_EN 0x08 /* DSP color bar enable */ + +#define AWBC1 0x43 /* Reserved */ +#define AWBC2 0x44 /* Reserved */ +#define AWBC3 0x45 /* Reserved */ +#define AWBC4 0x46 /* Reserved */ +#define AWBC5 0x47 /* Reserved */ +#define AWBC6 0x48 /* Reserved */ + +#define REG4B 0x4B /* REG4B */ +#define REG4B_UV_AVG_EN 0x01 /* UV average enable */ + +#define DNSTH 0x4C /* De-noise Threshold*/ + +#define MTX1 0x4F /* Matrix Coefficient 1 */ +#define MTX2 0x50 /* Matrix Coefficient 2 */ +#define MTX3 0x51 /* Matrix Coefficient 3 */ +#define MTX4 0x52 /* Matrix Coefficient 4 */ +#define MTX5 0x53 /* Matrix Coefficient 5 */ +#define MTX6 0x54 /* Matrix Coefficient 6 */ + +#define BRIGHTNESS 0x55 /* Brightness Control */ +#define CONTRAST 0x56 /* Contrast Control */ +#define CONTRAST_CENTER 0x57 /* Contrast Center */ +#define MTXS 0x58 /* Matrix Coefficient Sign for coefficient 5 to 0*/ +#define LCC1 0x62 /* Lens Correction Option 1 */ +#define LCC2 0x63 /* Lens Correction Option 2 */ +#define LCC3 0x64 /* Lens Correction Option 3 */ +#define LCC4 0x65 /* Lens Correction Option 4 */ +#define LCC5 0x66 /* Lens Correction Control */ +#define LCC5_LC_EN 0x01 /* Lens Correction Enable */ +#define LCC5_LC_CTRL 0x04 /* Lens correction control select */ + +#define MANU 0x67 /* Manual U Value (effective only when register TSLB[4] is high) */ +#define MANV 0x68 /* Manual V Value (effective only when register TSLB[4] is high) */ + +#define GFIX 0x69 /* Fix Gain Control */ +#define GGAIN 0x6A /* G Channel AWB Gain */ + +#define DBLV 0x6B /* PLL control */ +#define DBLV_PLL_BYPASS 0x00 /* Bypass PLL */ +#define DBLV_PLL_4x 0x40 /* Input clock 4x */ +#define DBLV_PLL_6x 0x80 /* Input clock 8x */ +#define DBLV_PLL_8x 0xC0 /* Input clock 16x */ +#define DBLV_REG_BYPASS 0x10 /* Bypass internal regulator */ + +#define AWBCTR3 0x6C /* AWB Control 3 */ +#define AWBCTR2 0x6D /* AWB Control 2 */ +#define AWBCTR1 0x6E /* AWB Control 1 */ +#define AWBCTR0 0x6F /* AWB Control 0 */ + +#define SCALING_XSC 0x70 /* Test Pattern[0] */ +#define SCALING_YSC 0x71 /* Test Pattern[1] */ +#define SCALING_DCWCTR 0x72 /* DCW control parameter */ + +#define SCALING_PCLK_DIV 0x73 /* Clock divider control for DSP.*/ +#define SCALING_PCLK_DIV_BYPASS 0x08 /* Bypass clock divider for DSP scale control */ +#define SCALING_PCLK_DIV_1 0x00 /* Divided by 1 */ +#define SCALING_PCLK_DIV_2 0x01 /* Divided by 2 */ +#define SCALING_PCLK_DIV_4 0x02 /* Divided by 4 */ +#define SCALING_PCLK_DIV_8 0x03 /* Divided by 8 */ +#define SCALING_PCLK_DIV_16 0x04 /* Divided by 16 */ + +#define REG74 0x74 /* REG74 */ +#define REG74_DGAIN_EN 0x10 /* Digital gain control by REG74[1:0] */ +#define REG74_DGAIN_BYPASS 0x00 /* Bypass */ +#define REG74_DGAIN_1X 0x01 /* 1x */ +#define REG74_DGAIN_2X 0x02 /* 2x */ +#define REG74_DGAIN_4X 0x03 /* 4x */ + +#define REG75 0x75 /* REG75 */ +#define REG75_EDGE_LOWER 0x1F /* Edge enhancement lower limit */ + +#define REG76 0x76 /* REG76 */ +#define REG76_WHITE_PIX_CORR 0x80 /* White pixel correction enable */ +#define REG76_BLACK_PIX_CORR 0x40 /* Black pixel correction enable */ +#define REG76_EDGE_HIGHER 0x1F /* Edge enhancement higher limit */ + +#define REG77 0x77 /* Offset, de-noise range control */ + +#define SLOP 0x7A /* Gamma Curve Highest Segment Slope */ +#define GAM1 0x7B /* Gamma Curve 1st Segment Input End Point 0x04 Output Value */ +#define GAM2 0x7C /* Gamma Curve 2nd Segment Input End Point 0x08 Output Value */ +#define GAM3 0x7D /* Gamma Curve 3rd Segment Input End Point 0x10 Output Value */ +#define GAM4 0x7E /* Gamma Curve 4th Segment Input End Point 0x20 Output Value */ +#define GAM5 0x7F /* Gamma Curve 5th Segment Input End Point 0x28 Output Value */ +#define GAM6 0x80 /* Gamma Curve 6th Segment Input End Point 0x30 Output Value */ +#define GAM7 0x81 /* Gamma Curve 7th Segment Input End Point 0x38 Output Value */ +#define GAM8 0x82 /* Gamma Curve 8th Segment Input End Point 0x40 Output Value */ +#define GAM9 0x83 /* Gamma Curve 9th Segment Input Enpd Point 0x48 Output Value */ +#define GAM10 0x84 /* Gamma Curve 10th Segment Input End Point 0x50 Output Value */ +#define GAM11 0x85 /* Gamma Curve 11th Segment Input End Point 0x60 Output Value */ +#define GAM12 0x86 /* Gamma Curve 12th Segment Input End Point 0x70 Output Value */ +#define GAM13 0x87 /* Gamma Curve 13th Segment Input End Point 0x90 Output Value */ +#define GAM14 0x88 /* Gamma Curve 14th Segment Input End Point 0xB0 Output Value */ +#define GAM15 0x89 /* Gamma Curve 15th Segment Input End Point 0xD0 Output Value */ + +#define RGB444 0x8C /* REG444 */ +#define RGB444_RGB444_EN 0x02 /* RGB444 enable, effective only when COM15[4] is high */ +#define RGB444_RGB444_FMT 0x01 /* RGB444 word format. 0 = xR GB 1 = RG Bx */ + +#define DM_LNL 0x92 /* Dummy Line low 8 bits */ +#define DM_LNH 0x93 /* Dummy Line high 8 bits */ +#define LCC6 0x94 /* RW Lens Correction Option 6 (effective only when LCC5[2] is high) */ +#define LCC7 0x95 /* RW Lens Correction Option 7 (effective only when LCC5[2] is high) */ + +#define BD50ST 0x9D /* Hz Banding Filter Value (effective only when COM8[5] is high and COM11[3] is high) */ +#define BD60ST 0x9E /* Hz Banding Filter Value (effective only when COM8[5] is high and COM11[3] is low) */ +#define HAECC1 0x9F /* Histogram-based AEC/AGC Control 1 */ +#define HAECC2 0xA0 /* Histogram-based AEC/AGC Control 2 */ + +#define SCALING_PCLK 0xA2 /* Pixel Clock Delay */ +#define SCALING_PCLK_DELAY 0x7F /* Scaling output delay */ + +#define NT_CTRL 0xA4 /* Auto frame rate adjustment control */ +#define NT_CTRL_AFR_HALF 0x08 /* Reduce frame rate by half */ +#define NT_CTRL_AFR_PT_2X 0x00 /* Insert dummy row at 2x gain */ +#define NT_CTRL_AFR_PT_4X 0x01 /* Insert dummy row at 4x gain */ +#define NT_CTRL_AFR_PT_8X 0x02 /* Insert dummy row at 8x gain */ + +#define BD50MAX 0xA5 /* 50Hz Banding Step Limit */ +#define HAECC3 0xA6 /* Histogram-based AEC/AGC Control 3 */ +#define HAECC4 0xA7 /* Histogram-based AEC/AGC Control 4 */ +#define HAECC5 0xA8 /* Histogram-based AEC/AGC Control 5 */ +#define HAECC6 0xA9 /* Histogram-based AEC/AGC Control 6 */ + +#define HAECC7 0xAA /* AEC algorithm selection */ +#define HAECC7_AEC_AVG 0x00 /* Average-based AEC algorithm */ +#define HAECC7_AEC_HIST 0x80 /* Histogram-based AEC algorithm */ + +#define BD60MAX 0xAB /* 60Hz Banding Step Limit */ + +#define STR_OPT 0xAC /* Strobe Control */ +#define STR_OPT_EN 0x80 /* Strobe Enable */ +#define STR_OPT_CTRL 0x40 /* R/G/B gain controlled by STR_R (0xAD) STR_G (0xAE) STR_B (0xAF) for LED output frame */ +#define STR_OPT_XENON_1ROW 0x00 /* Xenon mode option 1 row */ +#define STR_OPT_XENON_2ROW 0x10 /* Xenon mode option 2 rows */ +#define STR_OPT_XENON_3ROW 0x20 /* Xenon mode option 3 rows */ +#define STR_OPT_XENON_4ROW 0x30 /* Xenon mode option 4 rows */ +#define STR_OPT_MODE_XENON 0x00 /* Mode select */ +#define STR_OPT_MODE_LED1 0x01 /* Mode select */ +#define STR_OPT_MODE_LED2 0x02 /* Mode select */ + +#define STR_R 0xAD /* R Gain for LED Output Frame */ +#define STR_G 0xAE /* G Gain for LED Output Frame */ +#define STR_B 0xAF /* B Gain for LED Output Frame */ + +#define ABLC1 0xB1 /* ABLC Control */ +#define ABLC1_EN 0x04 /* Enable ABLC function */ + +#define THL_ST 0xB3 /* ABLC Target */ + +#define AD_CHB 0xBE /* Blue Channel Black Level Compensation */ +#define AD_CHR 0xBF /* Red Channel Black Level Compensation */ +#define AD_CHGB 0xC0 /* Gb Channel Black Level Compensation */ +#define AD_CHGR 0xC1 /* Gr Channel Black Level Compensation */ +#define SATCTR 0xC9 /* UV Saturation Control */ +#endif //__REG_REGS_H__ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/ports/gd32vw553_at_port.c b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/gd32vw553_at_port.c new file mode 100644 index 000000000000..574f2a1a7489 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/gd32vw553_at_port.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-08-31 CYFS add GD32VW553 board port with hardware reset + */ + +#include +#include +#include + +#ifdef BSP_USING_GD32VW553_AT + +#include + +#define GD32VW553_DEVICE_NAME "wifi0" +#define GD32VW553_CLIENT_NAME "uart4" +#define GD32VW553_RESET_PIN GET_PIN(C, 5) +#define GD32VW553_RECV_LINE_NUM 1024 + +static struct at_device_gd32vw553 gd32vw5530 = +{ + .device_name = GD32VW553_DEVICE_NAME, + .client_name = GD32VW553_CLIENT_NAME, + .wifi_ssid = BSP_GD32VW553_WIFI_SSID, + .wifi_password = BSP_GD32VW553_WIFI_PASSWORD, + .baudrate = BSP_GD32VW553_AT_BAUDRATE, + .recv_line_num = GD32VW553_RECV_LINE_NUM, + .use_reset_pin = RT_TRUE, + .reset_pin = GD32VW553_RESET_PIN, + .reset_active_level = PIN_LOW, +}; + +static int gd32vw553_device_register(void) +{ + return at_device_register(&gd32vw5530.device, + gd32vw5530.device_name, + gd32vw5530.client_name, + AT_DEVICE_CLASS_GD32VW553, + &gd32vw5530); +} +INIT_APP_EXPORT(gd32vw553_device_register); + +#endif /* BSP_USING_GD32VW553_AT */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/ports/lv_conf.h b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/lv_conf.h new file mode 100644 index 000000000000..ea527b706a9c --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/lv_conf.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2006-2026 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-08-04 RTT configure LVGL for the MIPI DSI LCD + */ + +#ifndef LV_CONF_H +#define LV_CONF_H + +#define LV_COLOR_FORMAT_DEFAULT LV_COLOR_FORMAT_RGB565 +#define LV_DISABLE_ASSERT_HANDLER_INCLUDE_WARNING 1 +#define LV_USE_GINO_FACTORY_PROFILE 1 + +#define LV_HOR_RES_MAX 720 +#define LV_VER_RES_MAX 720 + +#if defined(BSP_USING_OV7670) && (defined(__GNUC__) || defined(__ARMCC_VERSION)) +/* The RT-Thread port selects rt_malloc before loading this file. Its automatic + * SDRAM fallback is unsuitable for CPU-rendered image scratch buffers. Only + * CPU-owned LVGL allocations use DTCM; DMA/draw buffers remain in AXI SRAM. */ +#undef LV_USE_STDLIB_MALLOC +#define LV_USE_STDLIB_MALLOC LV_STDLIB_BUILTIN +#define LV_MEM_SIZE (256U * 1024U) +#define LV_MEM_ADR 0 +#define LV_ATTRIBUTE_LARGE_RAM_ARRAY __attribute__((section(".bss.lvgl_heap"), aligned(8))) +#endif + +#define LV_USE_SYSMON 0 +#define LV_USE_PERF_MONITOR 0 +#define LV_USE_MEM_MONITOR 0 + +#define LV_USE_THEME_DEFAULT 1 +#define LV_USE_THEME_SIMPLE 0 +#define LV_USE_THEME_MONO 0 + +#define LV_USE_FLEX 0 +#define LV_USE_GRID 0 + +#define LV_USE_DEMO_BENCHMARK 0 +#define LV_USE_DEMO_SMARTWATCH 0 +#define LV_USE_DEMO_MUSIC 0 + +#define LV_USE_FS_STDIO 0 +#define LV_USE_FS_POSIX 0 +#define LV_USE_FS_WIN32 0 +#define LV_USE_FS_FATFS 0 +#define LV_USE_FS_LITTLEFS 0 +#define LV_USE_FS_ARDUINO_ESP_LITTLEFS 0 +#define LV_USE_FS_ARDUINO_SD 0 +#define LV_USE_FS_UEFI 0 +#define LV_USE_FS_FROGFS 0 +#define LV_USE_FS_MEMFS 0 + +#define LV_USE_BMP 0 +#define LV_USE_LODEPNG 0 +#define LV_USE_LIBPNG 0 +#define LV_USE_TJPGD 0 +#define LV_USE_LIBJPEG_TURBO 0 +#define LV_USE_LIBWEBP 0 +#define LV_USE_SVG 0 +#define LV_USE_RLE 0 +#define LV_USE_LZ4 0 +#define LV_USE_GIF 0 +#define LV_USE_QRCODE 0 +#define LV_USE_BARCODE 0 +#define LV_USE_LOTTIE 0 + +#define LV_USE_ANIMIMG 0 +#define LV_USE_ARC 0 +#define LV_USE_ARCLABEL 0 +#define LV_USE_BAR 1 +#define LV_USE_BUTTON 1 +#define LV_USE_BUTTONMATRIX 1 +#define LV_USE_CALENDAR 0 +#define LV_USE_CANVAS 0 +#define LV_USE_CHART 0 +#define LV_USE_CHECKBOX 0 +#define LV_USE_DROPDOWN 1 +#define LV_USE_IMAGE 1 +#define LV_USE_IMAGEBUTTON 0 +#define LV_USE_KEYBOARD 1 +#define LV_USE_LABEL 1 +#define LV_USE_LED 0 +#define LV_USE_LINE 0 +#define LV_USE_LIST 0 +#define LV_USE_MENU 0 +#define LV_USE_MSGBOX 0 +#define LV_USE_ROLLER 0 +#define LV_USE_SCALE 0 +#define LV_USE_SLIDER 1 +#define LV_USE_SPAN 0 +#define LV_USE_SPINBOX 0 +#define LV_USE_SPINNER 0 +#define LV_USE_SWITCH 1 +#define LV_USE_TABLE 1 +#define LV_USE_TABVIEW 0 +#define LV_USE_TEXTAREA 1 +#define LV_USE_TILEVIEW 0 +#define LV_USE_WIN 0 +#define LV_USE_IME_PINYIN 0 + +#define LV_USE_FILE_EXPLORER 0 +#define LV_USE_FRAGMENT 0 +#define LV_USE_TRANSLATION 0 +#define LV_USE_FREETYPE 0 +#define LV_USE_TINY_TTF 0 +#define LV_USE_FONT_MANAGER 0 +#define LV_USE_IMGFONT 0 + +#define LV_FONT_MONTSERRAT_12 0 +#define LV_FONT_MONTSERRAT_14 0 +#define LV_FONT_MONTSERRAT_16 1 +#define LV_FONT_DEFAULT &lv_font_montserrat_16 + +#endif /* LV_CONF_H */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/board/ports/lv_port.c b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/lv_port.c new file mode 100644 index 000000000000..4302276b2e57 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/board/ports/lv_port.c @@ -0,0 +1,643 @@ +/* + * Copyright (c) 2006-2026 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-08-04 RTT add LVGL port for the MIPI DSI LCD + * 2026-09-08 CYFS port the board demo with internal camera rendering and IPA updates + */ + +#include + +#ifdef PKG_USING_LVGL + +#include +#include +#include + +#include "gd32h77x_78x_ipa.h" + +#define DBG_TAG "lvgl.disp" +#define DBG_LVL DBG_INFO +#include + +#define LCD_DEVICE_NAME "lcd" +#define TOUCH_DEVICE_NAME "gt911" +#define TOUCH_POINT_COUNT 5U + +#define LCD_BYTES_PER_PIXEL 2U + +#if defined(BSP_USING_OV7670) +#define LVGL_DISPLAY_WIDTH LV_HOR_RES_MAX +#define LVGL_DISPLAY_HEIGHT LV_VER_RES_MAX +#define LVGL_USE_DIRECT_FRAMEBUFFER 0 +#define LVGL_USE_IPA_FRAMEBUFFER 1 +#define LCD_DRAW_BUFFER_LINES 32U +#elif LV_USE_DEMO_SMARTWATCH && !defined(BSP_USING_LVGL_BOARD_DEMO) +#define LVGL_DISPLAY_WIDTH 384U +#define LVGL_DISPLAY_HEIGHT 384U +#define LVGL_USE_DIRECT_FRAMEBUFFER 0 +#else +#define LVGL_DISPLAY_WIDTH LV_HOR_RES_MAX +#define LVGL_DISPLAY_HEIGHT LV_VER_RES_MAX +#define LVGL_USE_DIRECT_FRAMEBUFFER 0 +#endif + +#ifndef LVGL_USE_IPA_FRAMEBUFFER +#define LVGL_USE_IPA_FRAMEBUFFER 0 +#define LCD_DRAW_BUFFER_LINES 150U +#endif + +#if !LVGL_USE_DIRECT_FRAMEBUFFER +#define LCD_DRAW_BUFFER_SIZE (LVGL_DISPLAY_WIDTH * LCD_DRAW_BUFFER_LINES * LCD_BYTES_PER_PIXEL) +#endif + +static rt_device_t lcd_device; +static struct rt_device_graphic_info lcd_info; +static lv_display_t *lvgl_display; +#if !LVGL_USE_DIRECT_FRAMEBUFFER +rt_align(32) static uint8_t lcd_draw_buffer_1[LCD_DRAW_BUFFER_SIZE]; +#if !LVGL_USE_IPA_FRAMEBUFFER +rt_align(32) static uint8_t lcd_draw_buffer_2[LCD_DRAW_BUFFER_SIZE]; +#endif +#endif +static rt_uint16_t lcd_viewport_x; +static rt_uint16_t lcd_viewport_y; +#if !LVGL_USE_DIRECT_FRAMEBUFFER && !LVGL_USE_IPA_FRAMEBUFFER +static volatile lv_display_t *g_flushing_display; +#endif +static rt_bool_t display_ready; +#if LVGL_USE_DIRECT_FRAMEBUFFER +static rt_bool_t flush_error_reported; +#endif + +#if LVGL_USE_IPA_FRAMEBUFFER +static rt_uint8_t *lcd_scanout_framebuffer; +static rt_uint8_t *lcd_render_framebuffer; +static lv_area_t lcd_pending_area; +static rt_bool_t lcd_pending_valid; +static rt_bool_t lcd_pipeline_faulted; +static rt_uint32_t lcd_ipa_error_flags; +#endif + +#ifdef BSP_USING_TOUCH_GT911 +int rt_hw_gt911_port_init(void); + +static rt_device_t touch_device; +static lv_coord_t touch_x; +static lv_coord_t touch_y; +static rt_bool_t touch_pressed; +#endif + +#if LVGL_USE_IPA_FRAMEBUFFER +static void lcd_ipa_init(void) +{ + rcu_periph_clock_enable(RCU_IPA); + NVIC_DisableIRQ(IPA_IRQn); + ipa_deinit(); + ipa_interval_clock_num_config(255U); + ipa_inter_timer_config(IPA_INTER_TIMER_ENABLE); +} + +static rt_err_t lcd_ipa_copy(const void *source, void *destination, + rt_uint32_t width, rt_uint32_t height, + rt_uint32_t source_stride, rt_uint32_t destination_stride) +{ + rt_tick_t start; + rt_uint32_t flags; + rt_err_t result = RT_EOK; + + if ((IPA_CTL & IPA_CTL_TEN) != 0U) + { + /* This configuration gives IPA exclusively to the synchronous port. */ + lcd_ipa_error_flags |= IPA_INTF; + ipa_deinit(); + __DSB(); + return -RT_EBUSY; + } + IPA_CTL = IPA_FGTODE; + IPA_INTC = 0x3FU; + IPA_FMADDR = (rt_uint32_t)source; + IPA_DMADDR = (rt_uint32_t)destination; + IPA_FLOFF = source_stride / LCD_BYTES_PER_PIXEL - width; + IPA_DLOFF = destination_stride / LCD_BYTES_PER_PIXEL - width; + IPA_FPCTL = FOREGROUND_PPF_RGB565; + IPA_DPCTL = DESTINATION_PPF_RGB565; + IPA_BSCTL = 0x10001000U; + IPA_DIMS = 0U; + IPA_IMS = (width << 16U) | height; + __DSB(); + start = rt_tick_get(); + IPA_CTL |= IPA_CTL_TEN; + while (1) + { + flags = IPA_INTF; + if ((flags & (IPA_INTF_TAEIF | IPA_INTF_WCFIF | IPA_INTF_LACIF)) != 0U) + { + lcd_ipa_error_flags |= flags; + result = -RT_EIO; + break; + } + if (((flags & IPA_INTF_FTFIF) != 0U) && ((IPA_CTL & IPA_CTL_TEN) == 0U)) + { + break; + } + if ((rt_tick_t)(rt_tick_get() - start) >= rt_tick_from_millisecond(100U)) + { + result = -RT_ETIMEOUT; + break; + } + } + if (result != RT_EOK) + { + /* Stop this port's engine before LVGL can reuse its source buffer. */ + ipa_deinit(); + } + __DSB(); + /* The scanout windows are write-through. Drop stale CPU lines after DMA, + * without writing any CPU cache contents back over the IPA result. */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, destination, + (height - 1U) * destination_stride + width * LCD_BYTES_PER_PIXEL); + return result; +} + +static rt_err_t lcd_ipa_present(void) +{ + rt_err_t result; + rt_uint8_t *old_scanout; + rt_uint32_t row; + rt_uint32_t lines; + rt_uint32_t offset; + rt_uint32_t stride = lcd_info.width * LCD_BYTES_PER_PIXEL; + rt_uint32_t width = lv_area_get_width(&lcd_pending_area); + + result = rt_device_control(lcd_device, RTGRAPHIC_CTRL_PAN_DISPLAY, lcd_render_framebuffer); + if (result != RT_EOK) + { + return result; + } + result = rt_device_control(lcd_device, RTGRAPHIC_CTRL_WAIT_VSYNC, RT_NULL); + if (result != RT_EOK) + { + return result; + } + old_scanout = lcd_scanout_framebuffer; + lcd_scanout_framebuffer = lcd_render_framebuffer; + lcd_render_framebuffer = old_scanout; + + /* Only the hardware reload releases the old front buffer. Synchronize the + * changed rectangle with IPA so the next partial frame retains other pixels. */ + for (row = lcd_pending_area.y1; row <= (rt_uint32_t)lcd_pending_area.y2; row += lines) + { + lines = (rt_uint32_t)lcd_pending_area.y2 - row + 1U; + if (lines > LCD_DRAW_BUFFER_LINES) + { + lines = LCD_DRAW_BUFFER_LINES; + } + offset = row * stride + lcd_pending_area.x1 * LCD_BYTES_PER_PIXEL; + result = lcd_ipa_copy(lcd_scanout_framebuffer + offset, lcd_render_framebuffer + offset, + width, lines, stride, stride); + if (result != RT_EOK) + { + return result; + } + } + lcd_pending_valid = RT_FALSE; + return RT_EOK; +} + +static void lcd_flush(lv_display_t *display, const lv_area_t *area, uint8_t *color_p) +{ + lv_draw_buf_t *draw_buf = lv_display_get_buf_active(display); + rt_uint32_t width = lv_area_get_width(area); + rt_uint32_t height = lv_area_get_height(area); + rt_uint32_t stride; + rt_uint32_t offset; + rt_err_t result = -RT_EINVAL; + + if (lcd_pipeline_faulted) + { + lv_display_flush_ready(display); + return; + } + if ((draw_buf == RT_NULL) || (area->x1 < 0) || (area->y1 < 0) || + (area->x2 >= lcd_info.width) || (area->y2 >= lcd_info.height) || + (area->x2 < area->x1) || (area->y2 < area->y1) || + (color_p != lcd_draw_buffer_1)) + { + goto failed; + } + stride = draw_buf->header.stride; + if ((stride < width * LCD_BYTES_PER_PIXEL) || ((stride & 1U) != 0U) || + (stride > sizeof(lcd_draw_buffer_1) / height)) + { + goto failed; + } + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, color_p, stride * height); + offset = (area->y1 * lcd_info.width + area->x1) * LCD_BYTES_PER_PIXEL; + result = lcd_ipa_copy(color_p, lcd_render_framebuffer + offset, width, height, + stride, lcd_info.width * LCD_BYTES_PER_PIXEL); + if (result != RT_EOK) + { + goto failed; + } + if (!lcd_pending_valid) + { + lcd_pending_area = *area; + lcd_pending_valid = RT_TRUE; + } + else + { + if (area->x1 < lcd_pending_area.x1) lcd_pending_area.x1 = area->x1; + if (area->y1 < lcd_pending_area.y1) lcd_pending_area.y1 = area->y1; + if (area->x2 > lcd_pending_area.x2) lcd_pending_area.x2 = area->x2; + if (area->y2 > lcd_pending_area.y2) lcd_pending_area.y2 = area->y2; + } + if (lv_display_flush_is_last(display)) + { + result = lcd_ipa_present(); + if (result != RT_EOK) + { + goto failed; + } + } + lv_display_flush_ready(display); + return; + +failed: + /* No CPU fallback: hold the last submitted image, never reuse an uncertain + * scanout buffer. The console remains available for diagnostics. */ + lcd_pipeline_faulted = RT_TRUE; + LOG_E("IPA display pipeline stopped: %d flags=%08x; reboot required", result, lcd_ipa_error_flags); + lv_display_flush_ready(display); +} +#elif !LVGL_USE_DIRECT_FRAMEBUFFER +static void lcd_clean_dcache(void *address, rt_size_t size) +{ +#if __CORTEX_M >= 0x07 + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, address, size); +#else + RT_UNUSED(address); + RT_UNUSED(size); +#endif +} + +static void lcd_ipa_init(void) +{ + rcu_periph_clock_enable(RCU_IPA); + ipa_interval_clock_num_config(255U); + ipa_inter_timer_config(IPA_INTER_TIMER_ENABLE); +} + +static void lcd_flush(lv_display_t *display, + const lv_area_t *area, + uint8_t *color_p) +{ + rt_uint32_t width; + rt_uint32_t height; + + width = (rt_uint32_t)lv_area_get_width(area); + height = (rt_uint32_t)lv_area_get_height(area); + + /* LVGL serializes flushes, matching the reference project's strategy. */ + while (g_flushing_display != RT_NULL) + { + } + g_flushing_display = display; + + lcd_clean_dcache(color_p, (rt_size_t)width * height * LCD_BYTES_PER_PIXEL); + + IPA_CTL = IPA_FGTODE | IPA_CTL_FTFIE; + IPA_FMADDR = (rt_uint32_t)color_p; + IPA_DMADDR = (rt_uint32_t)lcd_info.framebuffer + + (rt_uint32_t)2U * + ((rt_uint32_t)lcd_info.width * + ((rt_uint32_t)area->y1 + lcd_viewport_y) + + (rt_uint32_t)area->x1 + lcd_viewport_x); + IPA_FLOFF = 0U; + IPA_DLOFF = (rt_uint32_t)lcd_info.width - width; + IPA_FPCTL = FOREGROUND_PPF_RGB565; + IPA_IMS = (width << 16U) | (height & 0xFFFFU); + IPA_CTL |= IPA_CTL_TEN; +} + +void IPA_IRQHandler(void) +{ + rt_interrupt_enter(); + + if (SET == ipa_interrupt_flag_get(IPA_INT_FLAG_FTF)) + { + ipa_interrupt_flag_clear(IPA_INT_FLAG_FTF); + if (g_flushing_display != RT_NULL) + { + lv_display_flush_ready((lv_display_t *)g_flushing_display); + g_flushing_display = RT_NULL; + } + } + + rt_interrupt_leave(); +} +#else +static void lcd_flush(lv_display_t *display, + const lv_area_t *area, + uint8_t *color_p) +{ + rt_err_t result; + rt_bool_t swap_submitted = RT_FALSE; + + RT_UNUSED(area); + + if (!lv_display_flush_is_last(display)) + { + lv_display_flush_ready(display); + return; + } + + /* Direct mode reuses the old scanout buffer after flush_ready. A failed + * wait must retain ownership, and retrying it must not submit another swap. */ + do + { + if (!swap_submitted) + { + result = rt_device_control(lcd_device, RTGRAPHIC_CTRL_PAN_DISPLAY, + color_p); + swap_submitted = (result == RT_EOK); + } + if (swap_submitted) + { + result = rt_device_control(lcd_device, RTGRAPHIC_CTRL_WAIT_VSYNC, + RT_NULL); + } + if (result != RT_EOK) + { + if (!flush_error_reported) + { + LOG_E("framebuffer swap failed: %d; retaining buffers", result); + flush_error_reported = RT_TRUE; + } + rt_thread_mdelay(1U); + } + } while (result != RT_EOK); + + flush_error_reported = RT_FALSE; + lv_display_flush_ready(display); +} +#endif + +void lv_port_disp_init(void) +{ + rt_err_t result; + rt_uint32_t framebuffer_size; +#if LVGL_USE_DIRECT_FRAMEBUFFER + rt_uint8_t *back_framebuffer; +#endif + + lcd_device = rt_device_find(LCD_DEVICE_NAME); + if (lcd_device == RT_NULL) + { + LOG_E("cannot find %s device", LCD_DEVICE_NAME); + return; + } + + result = rt_device_open(lcd_device, RT_DEVICE_OFLAG_RDWR); + if (result != RT_EOK) + { + LOG_E("cannot open %s device: %d", LCD_DEVICE_NAME, result); + return; + } + + result = rt_device_control(lcd_device, RTGRAPHIC_CTRL_GET_INFO, &lcd_info); + framebuffer_size = (rt_uint32_t)lcd_info.width * lcd_info.height * + LCD_BYTES_PER_PIXEL; + if ((result != RT_EOK) || + (lcd_info.width != LV_HOR_RES_MAX) || + (lcd_info.height != LV_VER_RES_MAX) || + (lcd_info.pixel_format != RTGRAPHIC_PIXEL_FORMAT_RGB565) || + (lcd_info.bits_per_pixel != 16U) || + (lcd_info.framebuffer == RT_NULL) || + (lcd_info.smem_len < framebuffer_size * + ((LVGL_USE_DIRECT_FRAMEBUFFER || LVGL_USE_IPA_FRAMEBUFFER) ? 2U : 1U))) + { + LOG_E("invalid %s framebuffer", LCD_DEVICE_NAME); + rt_device_close(lcd_device); + return; + } + + lcd_viewport_x = (rt_uint16_t)((lcd_info.width - LVGL_DISPLAY_WIDTH) / 2U); + lcd_viewport_y = (rt_uint16_t)((lcd_info.height - LVGL_DISPLAY_HEIGHT) / 2U); + +#if LVGL_USE_IPA_FRAMEBUFFER + if (((rt_ubase_t)lcd_draw_buffer_1 < 0x24000000U) || + ((rt_ubase_t)lcd_draw_buffer_1 + sizeof(lcd_draw_buffer_1) > (rt_ubase_t)GD32_SRAM_END)) + { + LOG_E("LVGL draw buffer must be in AXI SRAM"); + rt_device_close(lcd_device); + return; + } + lcd_scanout_framebuffer = lcd_info.framebuffer; + lcd_render_framebuffer = lcd_scanout_framebuffer + framebuffer_size; +#endif + + lvgl_display = lv_display_create(LVGL_DISPLAY_WIDTH, LVGL_DISPLAY_HEIGHT); + if (lvgl_display == RT_NULL) + { + LOG_E("cannot create LVGL display"); + rt_device_close(lcd_device); + return; + } + + lv_display_set_color_format(lvgl_display, LV_COLOR_FORMAT_RGB565); +#if LVGL_USE_DIRECT_FRAMEBUFFER + back_framebuffer = (rt_uint8_t *)lcd_info.framebuffer + framebuffer_size; + lv_display_set_buffers(lvgl_display, + back_framebuffer, + lcd_info.framebuffer, + framebuffer_size, + LV_DISPLAY_RENDER_MODE_DIRECT); +#elif LVGL_USE_IPA_FRAMEBUFFER + lv_display_set_buffers(lvgl_display, lcd_draw_buffer_1, RT_NULL, + sizeof(lcd_draw_buffer_1), LV_DISPLAY_RENDER_MODE_PARTIAL); +#else + lv_display_set_buffers(lvgl_display, + lcd_draw_buffer_1, + lcd_draw_buffer_2, + sizeof(lcd_draw_buffer_1), + LV_DISPLAY_RENDER_MODE_PARTIAL); +#endif + lv_display_set_flush_cb(lvgl_display, lcd_flush); + lv_display_set_default(lvgl_display); + +#if !LVGL_USE_DIRECT_FRAMEBUFFER + lcd_ipa_init(); +#if !LVGL_USE_IPA_FRAMEBUFFER + nvic_irq_enable(IPA_IRQn, 2U, 0U); +#endif +#endif + display_ready = RT_TRUE; + +#if LVGL_USE_DIRECT_FRAMEBUFFER + LOG_I("%ux%u RGB565 direct framebuffers at %p/%p", + lcd_info.width, lcd_info.height, + lcd_info.framebuffer, back_framebuffer); + LOG_I("vertical blank framebuffer swapping enabled"); +#elif LVGL_USE_IPA_FRAMEBUFFER + LOG_I("AXI SRAM partial rendering: %u lines, %u bytes at %p", + LCD_DRAW_BUFFER_LINES, (rt_uint32_t)sizeof(lcd_draw_buffer_1), lcd_draw_buffer_1); + LOG_I("IPA back-buffer flush and sync, vertical blank swaps at %p/%p", + lcd_scanout_framebuffer, lcd_render_framebuffer); +#else + LOG_I("%ux%u LVGL viewport centered on %ux%u RGB565 panel", + LVGL_DISPLAY_WIDTH, LVGL_DISPLAY_HEIGHT, + lcd_info.width, lcd_info.height); + LOG_I("partial buffers (%u lines) at %p/%p", + LCD_DRAW_BUFFER_LINES, + lcd_draw_buffer_1, lcd_draw_buffer_2); + LOG_I("reference IPA flush strategy enabled"); +#endif +} + +#ifdef BSP_USING_TOUCH_GT911 +static void touch_read(lv_indev_t *indev, lv_indev_data_t *data) +{ + struct rt_touch_data touch_data[TOUCH_POINT_COUNT]; + rt_size_t point_index; + rt_bool_t active_point_found = RT_FALSE; + + RT_UNUSED(indev); + + rt_memset(touch_data, 0, sizeof(touch_data)); + if (rt_device_read(touch_device, 0, touch_data, TOUCH_POINT_COUNT) == TOUCH_POINT_COUNT) + { + for (point_index = 0; point_index < TOUCH_POINT_COUNT; point_index++) + { + if ((touch_data[point_index].event == RT_TOUCH_EVENT_DOWN) || + (touch_data[point_index].event == RT_TOUCH_EVENT_MOVE)) + { + if ((touch_data[point_index].x_coordinate >= lcd_viewport_x) && + (touch_data[point_index].x_coordinate < lcd_viewport_x + LVGL_DISPLAY_WIDTH) && + (touch_data[point_index].y_coordinate >= lcd_viewport_y) && + (touch_data[point_index].y_coordinate < lcd_viewport_y + LVGL_DISPLAY_HEIGHT)) + { + touch_x = (lv_coord_t)(touch_data[point_index].x_coordinate - lcd_viewport_x); + touch_y = (lv_coord_t)(touch_data[point_index].y_coordinate - lcd_viewport_y); + touch_pressed = RT_TRUE; + } + else + { + touch_pressed = RT_FALSE; + } + + active_point_found = RT_TRUE; + break; + } + } + + if (!active_point_found) + { + for (point_index = 0; point_index < TOUCH_POINT_COUNT; point_index++) + { + if (touch_data[point_index].event == RT_TOUCH_EVENT_UP) + { + touch_pressed = RT_FALSE; + break; + } + } + } + } + + data->point.x = touch_x; + data->point.y = touch_y; + data->state = touch_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL; +} +#endif + +void lv_port_indev_init(void) +{ +#ifdef BSP_USING_TOUCH_GT911 + rt_err_t result; + lv_indev_t *input_device; + + touch_device = rt_device_find(TOUCH_DEVICE_NAME); + if (touch_device == RT_NULL) + { + result = rt_hw_gt911_port_init(); + if (result == RT_EOK) + { + touch_device = rt_device_find(TOUCH_DEVICE_NAME); + } + if (touch_device == RT_NULL) + { + LOG_E("cannot initialize %s device: %d", + TOUCH_DEVICE_NAME, result); + return; + } + } + + result = rt_device_open(touch_device, RT_DEVICE_OFLAG_RDONLY); + if (result != RT_EOK) + { + LOG_E("cannot open %s device: %d", TOUCH_DEVICE_NAME, result); + touch_device = RT_NULL; + return; + } + + input_device = lv_indev_create(); + if (input_device == RT_NULL) + { + LOG_E("cannot create LVGL input device"); + rt_device_close(touch_device); + touch_device = RT_NULL; + return; + } + + lv_indev_set_type(input_device, LV_INDEV_TYPE_POINTER); + lv_indev_set_read_cb(input_device, touch_read); + lv_indev_set_display(input_device, lvgl_display); + + LOG_I("%s registered as LVGL pointer", TOUCH_DEVICE_NAME); +#endif +} + +void lv_user_gui_init(void) +{ + if (!display_ready) + { + return; + } + +#ifdef BSP_USING_LVGL_BOARD_DEMO + extern void board_demo_init(void); + + board_demo_init(); + lv_refr_now(NULL); +#elif LV_USE_DEMO_SMARTWATCH + extern void lv_demo_smartwatch(void); + + lv_demo_smartwatch(); + lv_refr_now(NULL); +#elif LV_USE_DEMO_BENCHMARK + extern void lv_demo_benchmark(void); + + lv_demo_benchmark(); + lv_refr_now(NULL); +#elif defined(PKG_USING_LV_MUSIC_DEMO) + extern void lv_demo_music(void); + + lv_demo_music(); + lv_refr_now(NULL); +#else + LOG_W("no LVGL demo is enabled"); + lv_refr_now(NULL); +#endif + +#if defined(BSP_USING_OV7670_PREVIEW) && !defined(BSP_USING_LVGL_BOARD_DEMO) && \ + !LV_USE_DEMO_SMARTWATCH + extern void ov7670_preview_init(void); + + ov7670_preview_init(); +#endif +} + +#endif /* PKG_USING_LVGL */ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/figures/board.png b/bsp/gd32/arm/gd32h77d-ginopi/figures/board.png new file mode 100644 index 000000000000..ffec53224b1a Binary files /dev/null and b/bsp/gd32/arm/gd32h77d-ginopi/figures/board.png differ diff --git a/bsp/gd32/arm/gd32h77d-ginopi/rtconfig.h b/bsp/gd32/arm/gd32h77d-ginopi/rtconfig.h new file mode 100644 index 000000000000..0f11d790728b --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/rtconfig.h @@ -0,0 +1,453 @@ +#ifndef RT_CONFIG_H__ +#define RT_CONFIG_H__ + +/* RT-Thread Kernel */ + +/* klibc options */ + +/* rt_vsnprintf options */ + +/* end of rt_vsnprintf options */ + +/* rt_vsscanf options */ + +/* end of rt_vsscanf options */ + +/* rt_memset options */ + +/* end of rt_memset options */ + +/* rt_memcpy options */ + +/* end of rt_memcpy options */ + +/* rt_memmove options */ + +/* end of rt_memmove options */ + +/* rt_memcmp options */ + +/* end of rt_memcmp options */ + +/* rt_strstr options */ + +/* end of rt_strstr options */ + +/* rt_strcasecmp options */ + +/* end of rt_strcasecmp options */ + +/* rt_strncpy options */ + +/* end of rt_strncpy options */ + +/* rt_strcpy options */ + +/* end of rt_strcpy options */ + +/* rt_strncmp options */ + +/* end of rt_strncmp options */ + +/* rt_strcmp options */ + +/* end of rt_strcmp options */ + +/* rt_strlen options */ + +/* end of rt_strlen options */ + +/* rt_strnlen options */ + +/* end of rt_strnlen options */ +/* end of klibc options */ +#define RT_NAME_MAX 12 +#define RT_CPUS_NR 1 +#define RT_ALIGN_SIZE 8 +#define RT_THREAD_PRIORITY_32 +#define RT_THREAD_PRIORITY_MAX 32 +#define RT_TICK_PER_SECOND 1000 +#define RT_USING_OVERFLOW_CHECK +#define RT_USING_HOOK +#define RT_HOOK_USING_FUNC_PTR +#define RT_USING_IDLE_HOOK +#define RT_IDLE_HOOK_LIST_SIZE 4 +#define IDLE_THREAD_STACK_SIZE 256 +#define RT_USING_TIMER_SOFT +#define RT_TIMER_THREAD_PRIO 4 +#define RT_TIMER_THREAD_STACK_SIZE 512 + +/* kservice options */ + +/* end of kservice options */ +#define RT_USING_DEBUG +#define RT_DEBUGING_ASSERT +#define RT_DEBUGING_COLOR +#define RT_DEBUGING_CONTEXT + +/* Inter-Thread communication */ + +#define RT_USING_SEMAPHORE +#define RT_USING_MUTEX +#define RT_USING_EVENT +#define RT_USING_MAILBOX +#define RT_USING_MESSAGEQUEUE +/* end of Inter-Thread communication */ + +/* Memory Management */ + +#define RT_USING_MEMPOOL +#define RT_USING_SMALL_MEM +#define RT_USING_SMALL_MEM_AS_HEAP +#define RT_USING_HEAP +/* end of Memory Management */ +#define RT_USING_DEVICE +#define RT_USING_CONSOLE +#define RT_CONSOLEBUF_SIZE 128 +#define RT_CONSOLE_DEVICE_NAME "uart1" +#define RT_USING_CONSOLE_OUTPUT_CTL +#define RT_VER_NUM 0x50301 +#define RT_BACKTRACE_LEVEL_MAX_NR 32 +/* end of RT-Thread Kernel */ +#define RT_USING_CACHE +#define RT_USING_HW_ATOMIC +#define ARCH_USING_HW_ATOMIC_8 +#define ARCH_USING_HW_ATOMIC_16 +#define RT_USING_CPU_FFS +#define ARCH_ARM +#define ARCH_ARM_CORTEX_M +#define ARCH_ARM_CORTEX_M7 + +/* RT-Thread Components */ + +#define RT_USING_COMPONENTS_INIT +#define RT_USING_USER_MAIN +#define RT_MAIN_THREAD_STACK_SIZE 2048 +#define RT_MAIN_THREAD_PRIORITY 10 +#define RT_USING_MSH +#define RT_USING_FINSH +#define FINSH_USING_MSH +#define FINSH_THREAD_NAME "tshell" +#define FINSH_THREAD_PRIORITY 20 +#define FINSH_THREAD_STACK_SIZE 4096 +#define FINSH_USING_HISTORY +#define FINSH_HISTORY_LINES 5 +#define FINSH_USING_SYMTAB +#define FINSH_CMD_SIZE 80 +#define MSH_USING_BUILT_IN_COMMANDS +#define FINSH_USING_DESCRIPTION +#define FINSH_ARG_MAX 10 +#define FINSH_USING_OPTION_COMPLETION + +/* DFS: device virtual file system */ + +/* end of DFS: device virtual file system */ + +/* Device Drivers */ + +#define RT_USING_DEVICE_IPC +#define RT_UNAMED_PIPE_NUMBER 64 +#define RT_USING_SERIAL +#define RT_USING_SERIAL_V1 +#define RT_SERIAL_RB_BUFSZ 64 +#define RT_USING_PIN +/* end of Device Drivers */ + +/* C/C++ and POSIX layer */ + +/* ISO-ANSI C layer */ + +/* Timezone and Daylight Saving Time */ + +#define RT_LIBC_USING_LIGHT_TZ_DST +#define RT_LIBC_TZ_DEFAULT_HOUR 8 +#define RT_LIBC_TZ_DEFAULT_MIN 0 +#define RT_LIBC_TZ_DEFAULT_SEC 0 +/* end of Timezone and Daylight Saving Time */ +/* end of ISO-ANSI C layer */ + +/* POSIX (Portable Operating System Interface) layer */ + + +/* Interprocess Communication (IPC) */ + + +/* Socket is in the 'Network' category */ + +/* end of Interprocess Communication (IPC) */ +/* end of POSIX (Portable Operating System Interface) layer */ +/* end of C/C++ and POSIX layer */ + +/* Network */ + +/* end of Network */ + +/* Memory protection */ + +/* end of Memory protection */ + +/* Utilities */ + +/* end of Utilities */ + +/* Using USB legacy version */ + +/* end of Using USB legacy version */ +/* end of RT-Thread Components */ + +/* RT-Thread Utestcases */ + +/* end of RT-Thread Utestcases */ + +/* RT-Thread online packages */ + +/* IoT - internet of things */ + + +/* Wi-Fi */ + +/* Marvell WiFi */ + +/* end of Marvell WiFi */ + +/* Wiced WiFi */ + +/* end of Wiced WiFi */ + +/* CYW43012 WiFi */ + +/* end of CYW43012 WiFi */ + +/* BL808 WiFi */ + +/* end of BL808 WiFi */ + +/* CYW43439 WiFi */ + +/* end of CYW43439 WiFi */ +/* end of Wi-Fi */ + +/* IoT Cloud */ + +/* end of IoT Cloud */ +/* end of IoT - internet of things */ + +/* security packages */ + +/* end of security packages */ + +/* language packages */ + +/* JSON: JavaScript Object Notation, a lightweight data-interchange format */ + +/* end of JSON: JavaScript Object Notation, a lightweight data-interchange format */ + +/* XML: Extensible Markup Language */ + +/* end of XML: Extensible Markup Language */ +/* end of language packages */ + +/* multimedia packages */ + +/* LVGL: powerful and easy-to-use embedded GUI library */ + +/* end of LVGL: powerful and easy-to-use embedded GUI library */ + +/* u8g2: a monochrome graphic library */ + +/* end of u8g2: a monochrome graphic library */ +/* end of multimedia packages */ + +/* tools packages */ + +/* end of tools packages */ + +/* system packages */ + +/* enhanced kernel services */ + +/* end of enhanced kernel services */ + +/* acceleration: Assembly language or algorithmic acceleration packages */ + +/* end of acceleration: Assembly language or algorithmic acceleration packages */ + +/* CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */ + +/* end of CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */ + +/* Micrium: Micrium software products porting for RT-Thread */ + +/* end of Micrium: Micrium software products porting for RT-Thread */ +/* end of system packages */ + +/* peripheral libraries and drivers */ + +/* HAL & SDK Drivers */ + +/* STM32 HAL & SDK Drivers */ + +/* end of STM32 HAL & SDK Drivers */ + +/* Infineon HAL Packages */ + +/* end of Infineon HAL Packages */ + +/* Kendryte SDK */ + +/* end of Kendryte SDK */ + +/* MM32 HAL & SDK Drivers */ + +/* end of MM32 HAL & SDK Drivers */ + +/* WCH HAL & SDK Drivers */ + +/* end of WCH HAL & SDK Drivers */ + +/* AT32 HAL & SDK Drivers */ + +/* end of AT32 HAL & SDK Drivers */ + +/* HC32 DDL Drivers */ + +/* end of HC32 DDL Drivers */ + +/* NXP HAL & SDK Drivers */ + +/* end of NXP HAL & SDK Drivers */ + +/* NUVOTON Drivers */ + +/* end of NUVOTON Drivers */ + +/* GD32 Drivers */ + +#define PKG_USING_GD32_ARM_CMSIS_DRIVER +#define PKG_USING_GD32_ARM_CMSIS_DRIVER_LATEST_VERSION +#define PKG_USING_GD32_ARM_SERIES_DRIVER +#define PKG_USING_GD32_ARM_SERIES_DRIVER_LATEST_VERSION +/* end of GD32 Drivers */ + +/* HPMicro SDK */ + +/* end of HPMicro SDK */ + +/* FT32 HAL & SDK Drivers */ + +/* end of FT32 HAL & SDK Drivers */ + +/* NOVOSNS Drivers */ + +/* end of NOVOSNS Drivers */ +/* end of HAL & SDK Drivers */ + +/* sensors drivers */ + +/* end of sensors drivers */ + +/* touch drivers */ + +/* end of touch drivers */ +/* end of peripheral libraries and drivers */ + +/* AI packages */ + +/* end of AI packages */ + +/* Signal Processing and Control Algorithm Packages */ + +/* end of Signal Processing and Control Algorithm Packages */ + +/* miscellaneous packages */ + +/* project laboratory */ + +/* end of project laboratory */ + +/* samples: kernel and components samples */ + +/* end of samples: kernel and components samples */ + +/* entertainment: terminal games and other interesting software packages */ + +/* end of entertainment: terminal games and other interesting software packages */ +/* end of miscellaneous packages */ + +/* Arduino libraries */ + + +/* Projects and Demos */ + +/* end of Projects and Demos */ + +/* Sensors */ + +/* end of Sensors */ + +/* Display */ + +/* end of Display */ + +/* Timing */ + +/* end of Timing */ + +/* Data Processing */ + +/* end of Data Processing */ + +/* Data Storage */ + +/* Communication */ + +/* end of Communication */ + +/* Device Control */ + +/* end of Device Control */ + +/* Other */ + +/* end of Other */ + +/* Signal IO */ + +/* end of Signal IO */ + +/* Uncategorized */ + +/* end of Arduino libraries */ +/* end of RT-Thread online packages */ +#define SOC_FAMILY_GD32 +#define SOC_SERIES_GD32H77x_H78X + +/* Hardware Drivers Config */ + +#define SOC_GD32H77DIW + +/* Onboard Peripheral Drivers */ + +/* end of Onboard Peripheral Drivers */ + +/* On-chip Peripheral Drivers */ + +#define BSP_USING_GPIO +#define BSP_USING_UART +#define BSP_USING_SERIAL_V1 +#define BSP_USING_UART1 +#define BSP_UART1_TX_PIN "PA2" +#define BSP_UART1_RX_PIN "PA3" +#define BSP_UART1_AFIO "AF7" +/* end of On-chip Peripheral Drivers */ + +/* Board extended module Drivers */ + +/* end of Board extended module Drivers */ +/* end of Hardware Drivers Config */ + +/* Applications */ + +/* end of Applications */ + +#endif diff --git a/bsp/gd32/arm/gd32h77d-ginopi/rtconfig.py b/bsp/gd32/arm/gd32h77d-ginopi/rtconfig.py new file mode 100644 index 000000000000..bfe4cf6dde3b --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/rtconfig.py @@ -0,0 +1,222 @@ +import os + +# toolchains options +ARCH='arm' +CPU='cortex-m7' +CROSS_TOOL='gcc' + +# bsp lib config +BSP_LIBRARY_TYPE = None + +if os.getenv('RTT_CC'): + CROSS_TOOL = os.getenv('RTT_CC') +if os.getenv('RTT_ROOT'): + RTT_ROOT = os.getenv('RTT_ROOT') + +# cross_tool provides the cross compiler +# EXEC_PATH is the compiler execute path, for example, CodeSourcery, Keil MDK, IAR +if CROSS_TOOL == 'gcc': + PLATFORM = 'gcc' + EXEC_PATH = r'/usr/bin' +elif CROSS_TOOL == 'keil': + PLATFORM = 'armclang' + EXEC_PATH = r'C:/Keil_v5' +elif CROSS_TOOL == 'iar': + PLATFORM = 'iccarm' + EXEC_PATH = r'C:/Program Files (x86)/IAR Systems/Embedded Workbench 8.3' +elif CROSS_TOOL == 'llvm-arm': + PLATFORM = 'llvm-arm' + EXEC_PATH = r'D:\Progrem\LLVMEmbeddedToolchainForArm-17.0.1-Windows-x86_64\bin' + +if os.getenv('RTT_EXEC_PATH'): + EXEC_PATH = os.getenv('RTT_EXEC_PATH') + +BUILD = 'release' + +if PLATFORM == 'gcc': + # toolchains + PREFIX = 'arm-none-eabi-' + CC = PREFIX + 'gcc' + AS = PREFIX + 'gcc' + AR = PREFIX + 'ar' + CXX = PREFIX + 'g++' + LINK = PREFIX + 'gcc' + TARGET_EXT = 'elf' + SIZE = PREFIX + 'size' + OBJDUMP = PREFIX + 'objdump' + OBJCPY = PREFIX + 'objcopy' + + DEVICE = ' -mcpu=cortex-m7 -mthumb -mfpu=fpv5-d16 -mfloat-abi=hard -ffunction-sections -fdata-sections' + CFLAGS = DEVICE + ' -Dgcc' + AFLAGS = ' -c' + DEVICE + ' -x assembler-with-cpp -Wa,-mimplicit-it=thumb ' + LFLAGS = DEVICE + ' -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,Reset_Handler -T board/linker_scripts/link.ld' + + CPATH = '' + LPATH = '' + + if BUILD == 'debug': + CFLAGS += ' -O0 -gdwarf-2 -g' + AFLAGS += ' -gdwarf-2' + else: + CFLAGS += ' -O2' + + CXXFLAGS = CFLAGS + CFLAGS += ' -std=c99' + + POST_ACTION = OBJCPY + ' -O binary $TARGET rt-thread.bin\n' + SIZE + ' $TARGET \n' + +elif PLATFORM == 'armcc': + # toolchains + CC = 'armcc' + CXX = 'armcc' + AS = 'armasm' + AR = 'armar' + LINK = 'armlink' + TARGET_EXT = 'axf' + + DEVICE = ' --cpu Cortex-M7.fp.dp ' + CFLAGS = '-c ' + DEVICE + ' --apcs=interwork --c99' + AFLAGS = DEVICE + ' --apcs=interwork ' + LFLAGS = DEVICE + r' --scatter "board\linker_scripts\link.sct" --info sizes --info totals --info unused --info veneers --list rt-thread.map --strict' + CFLAGS += ' -I' + EXEC_PATH + '/ARM/ARMCC/include' + LFLAGS += ' --libpath=' + EXEC_PATH + '/ARM/ARMCC/lib' + + CFLAGS += ' -D__MICROLIB ' + AFLAGS += ' --pd "__MICROLIB SETA 1" ' + LFLAGS += ' --library_type=microlib ' + EXEC_PATH += '/ARM/ARMCC/bin/' + + if BUILD == 'debug': + CFLAGS += ' -g -O0' + AFLAGS += ' -g' + else: + CFLAGS += ' -O2' + + CXXFLAGS = CFLAGS + CFLAGS += ' -std=c99' + + POST_ACTION = 'fromelf --bin $TARGET --output rtthread.bin \nfromelf -z $TARGET' + +elif PLATFORM == 'armclang': + # toolchains + CC = 'armclang' + CXX = 'armclang' + AS = 'armasm' + AR = 'armar' + LINK = 'armlink' + TARGET_EXT = 'axf' + + DEVICE = ' --cpu Cortex-M7.fp.dp ' + CFLAGS = ' --target=arm-arm-none-eabi -mcpu=cortex-m7 ' + CFLAGS += ' -mfpu=fpv5-d16 -mfloat-abi=hard ' + CFLAGS += ' -c -fno-rtti -funsigned-char -fshort-enums -fshort-wchar ' + CFLAGS += ' -gdwarf-3 -ffunction-sections ' + AFLAGS = DEVICE + ' --apcs=interwork ' + LFLAGS = DEVICE + ' --info sizes --info totals --info unused --info veneers ' + LFLAGS += ' --list rt-thread.map ' + LFLAGS += r' --strict --scatter "board\linker_scripts\link.sct" ' + CFLAGS += ' -I' + EXEC_PATH + '/ARM/ARMCLANG/include' + LFLAGS += ' --libpath=' + EXEC_PATH + '/ARM/ARMCLANG/lib' + + EXEC_PATH += '/ARM/ARMCLANG/bin/' + + if BUILD == 'debug': + CFLAGS += ' -g -O1' + AFLAGS += ' -g' + else: + CFLAGS += ' -O2' + + CXXFLAGS = CFLAGS + CFLAGS += ' -std=c99' + + POST_ACTION = 'fromelf --bin $TARGET --output rtthread.bin \nfromelf -z $TARGET' + +elif PLATFORM == 'iccarm': + # toolchains + CC = 'iccarm' + CXX = 'iccarm' + AS = 'iasmarm' + AR = 'iarchive' + LINK = 'ilinkarm' + TARGET_EXT = 'out' + + DEVICE = '-Dewarm' + + CFLAGS = DEVICE + CFLAGS += ' --diag_suppress Pa050' + CFLAGS += ' --no_cse' + CFLAGS += ' --no_unroll' + CFLAGS += ' --no_inline' + CFLAGS += ' --no_code_motion' + CFLAGS += ' --no_tbaa' + CFLAGS += ' --no_clustering' + CFLAGS += ' --no_scheduling' + CFLAGS += ' --endian=little' + CFLAGS += ' --cpu=Cortex-M7' + CFLAGS += ' -e' + CFLAGS += ' --fpu=VFPv5_sp' + CFLAGS += ' --dlib_config "' + EXEC_PATH + '/arm/INC/c/DLib_Config_Normal.h"' + CFLAGS += ' --silent' + + AFLAGS = DEVICE + AFLAGS += ' -s+' + AFLAGS += ' -w+' + AFLAGS += ' -r' + AFLAGS += ' --cpu Cortex-M7' + AFLAGS += ' --fpu VFPv5_sp' + AFLAGS += ' -S' + + if BUILD == 'debug': + CFLAGS += ' --debug' + CFLAGS += ' -On' + else: + CFLAGS += ' -Oh' + + LFLAGS = ' --config "board/linker_scripts/link.icf"' + LFLAGS += ' --entry __iar_program_start' + + CXXFLAGS = CFLAGS + + EXEC_PATH += '/arm/bin/' + POST_ACTION = 'ielftool --bin $TARGET rtthread.bin' + +elif PLATFORM == 'llvm-arm': + # toolchains + PREFIX = 'llvm-' + CC = 'clang' + AS = 'clang' + AR = PREFIX + 'ar' + CXX = 'clang++' + LINK = 'clang' + TARGET_EXT = 'elf' + SIZE = PREFIX + 'size' + OBJDUMP = PREFIX + 'objdump' + OBJCPY = PREFIX + 'objcopy' + + DEVICE = ' --target=arm-none-eabihf -mfloat-abi=hard -march=armv7em -mfpu=fpv5-d16' + DEVICE += ' -ffunction-sections -fdata-sections -fno-exceptions -fno-rtti' + CFLAGS = DEVICE + AFLAGS = ' -c' + DEVICE + ' -Wa,-mimplicit-it=thumb ' + LFLAGS = DEVICE + ' -Wl,--gc-sections,-Map=rt-thread.map,-u,Reset_Handler -lcrt0 -T board/linker_scripts/link.ld' + + CPATH = '' + LPATH = '' + + if BUILD == 'debug': + CFLAGS += ' -O0 -gdwarf-2 -g' + AFLAGS += ' -gdwarf-2' + else: + CFLAGS += ' -O3' + + CXXFLAGS = CFLAGS + + POST_ACTION = OBJCPY + ' -O binary $TARGET rtthread.bin\n' + SIZE + ' $TARGET \n' + + + +def dist_handle(BSP_ROOT, dist_dir): + import sys + cwd_path = os.getcwd() + sys.path.append(os.path.join(os.path.dirname(BSP_ROOT), 'tools')) + from sdk_dist import dist_do_building + dist_do_building(BSP_ROOT, dist_dir) diff --git a/bsp/gd32/arm/gd32h77d-ginopi/template.uvoptx b/bsp/gd32/arm/gd32h77d-ginopi/template.uvoptx new file mode 100644 index 000000000000..da30a04e38d0 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/template.uvoptx @@ -0,0 +1,205 @@ + + + + 1.0 + +
### uVision Project, (C) Keil Software
+ + + *.c + *.s*; *.src; *.a* + *.obj; *.o + *.lib + *.txt; *.h; *.inc; *.md + *.plm + *.cpp; *.cc; *.cxx + 0 + + + + 0 + 0 + + + + GD32H77D_GinoPi + 0x4 + ARM-ADS + + 12000000 + + 1 + 1 + 0 + 1 + 0 + + + 1 + 65535 + 0 + 0 + 0 + + + 79 + 66 + 8 + .\Listings\ + + + 1 + 1 + 1 + 0 + 1 + 1 + 0 + 1 + 0 + 0 + 0 + 0 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + + + 1 + 0 + 1 + + 255 + + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 1 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 0 + 3 + + + + + + + + + + + BIN\CMSIS_AGDI.dll + + + + 0 + CMSIS_AGDI + -X"CMSIS-DAP V2" -UE135C13EE238F49A5DAE3F76 -O206 -S0 -C0 -P00000000 -N00("ARM CoreSight SW-DP") -D00(0BD12477) -L00(0) -TO65554 -TC10000000 -TT10000000 -TP20 -TDS8007 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO15 -FD24000000 -FC4000 -FN2 -FF0GD32H77x_78x_CNVM_2M.FLM -FS08000000 -FL0200000 -FP0($$Device:GD32H77DIW$Flash\GD32H77x_78x_CNVM_2M.FLM) -FF1GD32H77x_78x_ECNVM_7M_512K.FLM -FS18200000 -FL1780000 -FP1($$Device:GD32H77DIW$Flash\GD32H77x_78x_ECNVM_7M_512K.FLM) + + + 0 + UL2CM3 + -U -O206 -S0 -C0 -P00 -TO65554 -TC10000000 -TT10000000 -TP21 -TDS8007 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO7 -FD24000000 -FC4000 -FN2 -FF0GD32H77x_78x_CNVM_2M.FLM -FS08000000 -FL0200000 -FP0($$Device:GD32H77DIW$Flash\GD32H77x_78x_CNVM_2M.FLM) -FF1GD32H77x_78x_ECNVM_7M_512K.FLM -FS18200000 -FL1780000 -FP1($$Device:GD32H77DIW$Flash\GD32H77x_78x_ECNVM_7M_512K.FLM) + + + 0 + ARMRTXEVENTFLAGS + -L70 -Z18 -C0 -M0 -T1 + + + 0 + DLGTARM + (1010=-1,-1,-1,-1,0)(6017=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(6016=-1,-1,-1,-1,0)(1012=-1,-1,-1,-1,0) + + + 0 + ARMDBGFLAGS + + + + 0 + DLGUARM + d + + + + + 0 + + + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + + 0 + 0 + 0 + + + + + + + + + + + + + Application + 1 + 0 + 0 + 0 + + +
diff --git a/bsp/gd32/arm/gd32h77d-ginopi/template.uvprojx b/bsp/gd32/arm/gd32h77d-ginopi/template.uvprojx new file mode 100644 index 000000000000..e49dc5962778 --- /dev/null +++ b/bsp/gd32/arm/gd32h77d-ginopi/template.uvprojx @@ -0,0 +1,406 @@ + + + + 2.1 + +
### uVision Project, (C) Keil Software
+ + + + GD32H77D_GinoPi + 0x4 + ARM-ADS + 6240000::V6.24::ARMCLANG + 1 + + + GD32H77DIW + GigaDevice + GigaDevice.GD32H77x_78x_DFP.0.6.1 + https://gd32mcu.com + IRAM(0x24000000,0x000C0000) IRAM2(0x00000000,0x00020000) IROM(0x08000000,0x0200000) IROM2(0x08200000,0x0780000) XRAM(0x20000000,0x00040000) XRAM2(0x30000000,0x00004000) XRAM3(0x30004000,0x00004000) CPUTYPE("Cortex-M7") FPU3(DFPU) CLOCK(12000000) ELITTLE + + + UL2CM3(-S0 -C0 -P0 -FD24000000 -FC4000 -FN2 -FF0GD32H77x_78x_CNVM_2M -FS08000000 -FL0200000 -FF1GD32H77x_78x_ECNVM_7M_512K -FS18200000 -FL1780000 -FP0($$Device:GD32H77DIW$Flash\GD32H77x_78x_CNVM_2M.FLM) -FP1($$Device:GD32H77DIW$Flash\GD32H77x_78x_ECNVM_7M_512K.FLM)) + 0 + + + + + + + + + + + $$Device:GD32H77DIW$SVD\GD32H77x_78x.svd + 0 + 0 + + + + + + + 0 + 0 + 0 + 0 + 1 + + .\Objects\ + Project + 1 + 0 + 0 + 1 + 1 + .\Listings\ + 1 + 0 + 0 + + 0 + 0 + + + 0 + 0 + 0 + 0 + + + 0 + 0 + + + 0 + 0 + 0 + 0 + + + 0 + 0 + + + 0 + 0 + 0 + 0 + + 0 + + + + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 3 + + + 1 + + + SARMCM3.DLL + -REMAP -MPU + DCM.DLL + -pCM7 + SARMCM3.DLL + -MPU + TCM.DLL + -pCM7 + + + + 1 + 0 + 0 + 0 + 16 + + + + + 1 + 0 + 0 + 1 + 1 + 4096 + + 1 + BIN\UL2CM3.DLL + + + + + + 0 + + + + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + "Cortex-M7" + + 0 + 0 + 0 + 1 + 1 + 1 + 0 + 3 + 0 + 0 + 0 + 1 + 1 + 8 + 1 + 0 + 0 + 0 + 4 + 4 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 1 + 1 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x24000000 + 0xc0000 + + + 1 + 0x8000000 + 0x200000 + + + 1 + 0x20000000 + 0x40000 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x8000000 + 0x200000 + + + 1 + 0x8200000 + 0x780000 + + + 0 + 0x20000000 + 0x40000 + + + 0 + 0x30000000 + 0x4000 + + + 0 + 0x30004000 + 0x4000 + + + 0 + 0x24000000 + 0xc0000 + + + 0 + 0x0 + 0x20000 + + + + + + 1 + 1 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 3 + 0 + 0 + 0 + 0 + 0 + 3 + 3 + 1 + 1 + 0 + 0 + 0 + + + GD32H77DXW + + board;..\libraries\gd32-arm-cmsis-latest\GD32H77x_78x;..\libraries\gd32-arm-cmsis-latest\GD32H77x_78x\GD\GD32H77x_78x\Include;..\libraries\gd32-arm-series-latest\GD32H77x_78x\GD32H77x_78x_standard_peripheral\Include + + + + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 4 + + + + + + + + + 0 + 0 + 0 + 0 + 1 + 0 + 0x08000000 + 0x20000000 + + .\board\linker_scripts\link.sct + + + + + + + + + + + Application + + + + + + + + + + + + + + + template + 1 + + + + +
diff --git a/bsp/gd32/arm/libraries/.gitignore b/bsp/gd32/arm/libraries/.gitignore new file mode 100644 index 000000000000..7eff01c0ce83 --- /dev/null +++ b/bsp/gd32/arm/libraries/.gitignore @@ -0,0 +1,2 @@ +/gd32-arm-cmsis-latest/ +/gd32-arm-series-latest/ diff --git a/bsp/gd32/arm/libraries/Kconfig b/bsp/gd32/arm/libraries/Kconfig index 6cd8bf17f23c..e1b4bd4826a4 100644 --- a/bsp/gd32/arm/libraries/Kconfig +++ b/bsp/gd32/arm/libraries/Kconfig @@ -31,6 +31,11 @@ config SOC_SERIES_GD32H7xx select ARCH_ARM_CORTEX_M7 select SOC_FAMILY_GD32 +config SOC_SERIES_GD32H77x_H78X + bool + select ARCH_ARM_CORTEX_M7 + select SOC_FAMILY_GD32 + config SOC_SERIES_GD32F5xx bool select ARCH_ARM_CORTEX_M33 diff --git a/bsp/gd32/arm/libraries/gd32_drivers/Kconfig b/bsp/gd32/arm/libraries/gd32_drivers/Kconfig index 897c6c81c17d..e8819a9d8d7e 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/Kconfig +++ b/bsp/gd32/arm/libraries/gd32_drivers/Kconfig @@ -39,5 +39,6 @@ endif config BSP_USING_GD_DBG bool + default n if SOC_SERIES_GD32H77x_H78X default y # "Using GD32 debug module" diff --git a/bsp/gd32/arm/libraries/gd32_drivers/SConscript b/bsp/gd32/arm/libraries/gd32_drivers/SConscript index d10212978d2d..475659f14496 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/SConscript +++ b/bsp/gd32/arm/libraries/gd32_drivers/SConscript @@ -3,6 +3,7 @@ Import('rtconfig') from building import * cwd = GetCurrentDir() +CPPDEFINES = [] # add the general drivers. src = Split(""" @@ -28,6 +29,10 @@ if GetDepend(['RT_USING_I2C']): if GetDepend('RT_USING_SPI'): src += ['drv_spi.c'] +# add ospi drivers. +if GetDepend('RT_USING_QSPI') and GetDepend('BSP_USING_OSPI0'): + src += ['drv_ospi.c'] + # add spi flash drivers. if GetDepend('RT_USING_SFUD'): src += ['drv_spi_flash.c', 'drv_spi.c'] @@ -60,13 +65,40 @@ if GetDepend('RT_USING_PWM'): if GetDepend('RT_USING_CAN'): src += ['drv_can.c'] +# add ethernet drivers. +if GetDepend(['SOC_SERIES_GD32H77x_H78X', 'BSP_USING_ETH']): + src += ['drv_enet.c'] + # add sdram drivers. if GetDepend('BSP_USING_SDRAM'): src += ['drv_sdram.c'] +# add USBHS device and/or host drivers. +if GetDepend('BSP_USING_USB'): + src += [ + 'usb/drv_usb_core.c', + 'usb/gd32h7_usb_hw.c', + ] + + if GetDepend('BSP_USB_USING_DEVICE'): + src += [ + 'usb/drv_otghs_dev.c', + 'usb/drv_usb_dev.c', + 'usb/drv_usbd_int.c', + ] + if GetDepend('BSP_USB_USING_HOST'): + src += [ + 'usb/drv_otghs_host.c', + 'usb/drv_usb_host.c', + 'usb/drv_usbh_int.c', + 'usb/drv_usbh_pipe.c', + 'usb/drv_usbh_trans.c', + ] + path = [cwd] path += [cwd + "/config"] +path += [cwd + "/usb"] -group = DefineGroup('Drivers', src, depend = [''], CPPPATH = path) +group = DefineGroup('Drivers', src, depend = [''], CPPPATH = path, CPPDEFINES = CPPDEFINES) Return('group') diff --git a/bsp/gd32/arm/libraries/gd32_drivers/config/can_config.h b/bsp/gd32/arm/libraries/gd32_drivers/config/can_config.h new file mode 100644 index 000000000000..d18bf04eb606 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/config/can_config.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __CAN_CONFIG_H__ +#define __CAN_CONFIG_H__ + +#include + +#ifdef BSP_USING_CAN1 +#ifndef BSP_CAN1_TX_PIN +#define BSP_CAN1_TX_PIN "PB4" +#endif +#ifndef BSP_CAN1_RX_PIN +#define BSP_CAN1_RX_PIN "PB5" +#endif +#ifndef BSP_CAN1_TX_AFIO +#define BSP_CAN1_TX_AFIO "AF4" +#endif +#ifndef BSP_CAN1_RX_AFIO +#define BSP_CAN1_RX_AFIO "AF9" +#endif + +#define CAN1_GPIO_CONFIG \ + { \ + .can_clk = RCU_CAN1, \ + .tx_pin_name = BSP_CAN1_TX_PIN, \ + .rx_pin_name = BSP_CAN1_RX_PIN, \ + .tx_alternate = BSP_CAN1_TX_AFIO, \ + .rx_alternate = BSP_CAN1_RX_AFIO, \ + } +#endif + +#endif /* __CAN_CONFIG_H__ */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/config/i2c_config.h b/bsp/gd32/arm/libraries/gd32_drivers/config/i2c_config.h new file mode 100644 index 000000000000..fb71db6023d7 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/config/i2c_config.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __I2C_CONFIG_H__ +#define __I2C_CONFIG_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef BSP_USING_HARD_I2C0 +#ifndef BSP_I2C0_SCL_PIN +#define BSP_I2C0_SCL_PIN "PB8" +#define BSP_I2C0_SDA_PIN "PB9" +#define BSP_I2C0_AFIO "AF4" +#endif +#ifndef I2C0_CONFIG +#define I2C0_CONFIG \ + { \ + .i2c_periph = I2C0, \ + .per_clk = RCU_I2C0, \ + .scl_pin_name = BSP_I2C0_SCL_PIN, \ + .sda_pin_name = BSP_I2C0_SDA_PIN, \ + .alternate = BSP_I2C0_AFIO, \ + .i2c_bus = &i2c0, \ + .device_name = "hwi2c0", \ + } +#endif +#endif + +#ifdef BSP_USING_HARD_I2C1 +#ifndef BSP_I2C1_SCL_PIN +#define BSP_I2C1_SCL_PIN "PH4" +#define BSP_I2C1_SDA_PIN "PB11" +#define BSP_I2C1_AFIO "AF4" +#endif +#ifndef I2C1_CONFIG +#define I2C1_CONFIG \ + { \ + .i2c_periph = I2C1, \ + .per_clk = RCU_I2C1, \ + .scl_pin_name = BSP_I2C1_SCL_PIN, \ + .sda_pin_name = BSP_I2C1_SDA_PIN, \ + .alternate = BSP_I2C1_AFIO, \ + .i2c_bus = &i2c1, \ + .device_name = "hwi2c1", \ + } +#endif +#endif + +#ifdef BSP_USING_HARD_I2C2 +#ifndef BSP_I2C2_SCL_PIN +#define BSP_I2C2_SCL_PIN "PA8" +#define BSP_I2C2_SDA_PIN "PC9" +#define BSP_I2C2_AFIO "AF4" +#endif +#ifndef I2C2_CONFIG +#define I2C2_CONFIG \ + { \ + .i2c_periph = I2C2, \ + .per_clk = RCU_I2C2, \ + .scl_pin_name = BSP_I2C2_SCL_PIN, \ + .sda_pin_name = BSP_I2C2_SDA_PIN, \ + .alternate = BSP_I2C2_AFIO, \ + .i2c_bus = &i2c2, \ + .device_name = "hwi2c2", \ + } +#endif +#endif + +#ifdef BSP_USING_HARD_I2C3 +#ifndef BSP_I2C3_SCL_PIN +#define BSP_I2C3_SCL_PIN "PF14" +#define BSP_I2C3_SDA_PIN "PF15" +#define BSP_I2C3_AFIO "AF4" +#endif +#ifndef I2C3_CONFIG +#define I2C3_CONFIG \ + { \ + .i2c_periph = I2C3, \ + .per_clk = RCU_I2C3, \ + .scl_pin_name = BSP_I2C3_SCL_PIN, \ + .sda_pin_name = BSP_I2C3_SDA_PIN, \ + .alternate = BSP_I2C3_AFIO, \ + .i2c_bus = &i2c3, \ + .device_name = "hwi2c3", \ + } +#endif +#endif + +#ifdef BSP_USING_HARD_I2C4 +#ifndef BSP_I2C4_SCL_PIN +#define BSP_I2C4_SCL_PIN "PG7" +#define BSP_I2C4_SDA_PIN "PG8" +#define BSP_I2C4_AFIO "AF6" +#endif +#ifndef I2C4_CONFIG +#define I2C4_CONFIG \ + { \ + .i2c_periph = I2C4, \ + .per_clk = RCU_I2C4, \ + .scl_pin_name = BSP_I2C4_SCL_PIN, \ + .sda_pin_name = BSP_I2C4_SDA_PIN, \ + .alternate = BSP_I2C4_AFIO, \ + .i2c_bus = &i2c4, \ + .device_name = "hwi2c4", \ + } +#endif +#endif + +#ifdef BSP_USING_HARD_I2C5 +#ifndef BSP_I2C5_SCL_PIN +#define BSP_I2C5_SCL_PIN "PF11" +#define BSP_I2C5_SDA_PIN "PF12" +#define BSP_I2C5_AFIO "AF4" +#endif +#ifndef I2C5_CONFIG +#define I2C5_CONFIG \ + { \ + .i2c_periph = I2C5, \ + .per_clk = RCU_I2C5, \ + .scl_pin_name = BSP_I2C5_SCL_PIN, \ + .sda_pin_name = BSP_I2C5_SDA_PIN, \ + .alternate = BSP_I2C5_AFIO, \ + .i2c_bus = &i2c5, \ + .device_name = "hwi2c5", \ + } +#endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __I2C_CONFIG_H__ */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/config/sdio_config.h b/bsp/gd32/arm/libraries/gd32_drivers/config/sdio_config.h new file mode 100644 index 000000000000..23782ed03614 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/config/sdio_config.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SDIO_CONFIG_H__ +#define __SDIO_CONFIG_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BSP_SDIO_CLK_PIN +#define BSP_SDIO_CLK_PIN "PD6" +#endif +#ifndef BSP_SDIO_CLK_AFIO +#define BSP_SDIO_CLK_AFIO "AF11" +#endif + +#ifndef BSP_SDIO_CMD_PIN +#define BSP_SDIO_CMD_PIN "PD7" +#endif +#ifndef BSP_SDIO_CMD_AFIO +#define BSP_SDIO_CMD_AFIO "AF11" +#endif + +#ifndef BSP_SDIO_D0_PIN +#define BSP_SDIO_D0_PIN "PF4" +#endif +#ifndef BSP_SDIO_D0_AFIO +#define BSP_SDIO_D0_AFIO "AF10" +#endif + +#ifndef BSP_SDIO_D1_PIN +#define BSP_SDIO_D1_PIN "PG10" +#endif +#ifndef BSP_SDIO_D1_AFIO +#define BSP_SDIO_D1_AFIO "AF11" +#endif + +#ifndef BSP_SDIO_D2_PIN +#define BSP_SDIO_D2_PIN "PG11" +#endif +#ifndef BSP_SDIO_D2_AFIO +#define BSP_SDIO_D2_AFIO "AF10" +#endif + +#ifndef BSP_SDIO_D3_PIN +#define BSP_SDIO_D3_PIN "PG12" +#endif +#ifndef BSP_SDIO_D3_AFIO +#define BSP_SDIO_D3_AFIO "AF10" +#endif + +#ifdef BSP_SDIO_USING_CARD_DETECT +#ifndef BSP_SDIO_CD_PIN +#define BSP_SDIO_CD_PIN "PE10" +#endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __SDIO_CONFIG_H__ */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/config/spi_config.h b/bsp/gd32/arm/libraries/gd32_drivers/config/spi_config.h new file mode 100644 index 000000000000..1f3368f14f89 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/config/spi_config.h @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SPI_CONFIG_H__ +#define __SPI_CONFIG_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef BSP_USING_SPI0 +#ifndef BSP_SPI0_SCK_PIN +#define BSP_SPI0_SCK_PIN "PA5" +#endif +#ifndef BSP_SPI0_MISO_PIN +#define BSP_SPI0_MISO_PIN "PA6" +#endif +#ifndef BSP_SPI0_MOSI_PIN +#define BSP_SPI0_MOSI_PIN "PA7" +#endif +#ifndef BSP_SPI0_SCK_AFIO +#ifdef SOC_SERIES_GD32E23x +#define BSP_SPI0_SCK_AFIO "AF0" +#else +#define BSP_SPI0_SCK_AFIO "AF5" +#endif +#endif +#ifndef BSP_SPI0_MISO_AFIO +#ifdef SOC_SERIES_GD32E23x +#define BSP_SPI0_MISO_AFIO "AF0" +#else +#define BSP_SPI0_MISO_AFIO "AF5" +#endif +#endif +#ifndef BSP_SPI0_MOSI_AFIO +#ifdef SOC_SERIES_GD32E23x +#define BSP_SPI0_MOSI_AFIO "AF0" +#else +#define BSP_SPI0_MOSI_AFIO "AF5" +#endif +#endif +#ifndef SPI0_BUS_CONFIG +#define SPI0_BUS_CONFIG \ + { \ + .spi_periph = SPI0, \ + .bus_name = "spi0", \ + .spi_clk = RCU_SPI0, \ + .spi_bus = &spi_bus0, \ + .sck_pin_name = BSP_SPI0_SCK_PIN, \ + .miso_pin_name = BSP_SPI0_MISO_PIN, \ + .mosi_pin_name = BSP_SPI0_MOSI_PIN, \ + .sck_alternate = BSP_SPI0_SCK_AFIO, \ + .miso_alternate = BSP_SPI0_MISO_AFIO, \ + .mosi_alternate = BSP_SPI0_MOSI_AFIO, \ + } +#endif +#endif + +#ifdef BSP_USING_SPI1 +#ifndef BSP_SPI1_SCK_PIN +#define BSP_SPI1_SCK_PIN "PB13" +#endif +#ifndef BSP_SPI1_MISO_PIN +#define BSP_SPI1_MISO_PIN "PB14" +#endif +#ifndef BSP_SPI1_MOSI_PIN +#define BSP_SPI1_MOSI_PIN "PB15" +#endif +#ifndef BSP_SPI1_SCK_AFIO +#ifdef SOC_SERIES_GD32E23x +#define BSP_SPI1_SCK_AFIO "AF0" +#else +#define BSP_SPI1_SCK_AFIO "AF5" +#endif +#endif +#ifndef BSP_SPI1_MISO_AFIO +#ifdef SOC_SERIES_GD32E23x +#define BSP_SPI1_MISO_AFIO "AF0" +#else +#define BSP_SPI1_MISO_AFIO "AF5" +#endif +#endif +#ifndef BSP_SPI1_MOSI_AFIO +#ifdef SOC_SERIES_GD32E23x +#define BSP_SPI1_MOSI_AFIO "AF0" +#else +#define BSP_SPI1_MOSI_AFIO "AF5" +#endif +#endif +#ifndef SPI1_BUS_CONFIG +#define SPI1_BUS_CONFIG \ + { \ + .spi_periph = SPI1, \ + .bus_name = "spi1", \ + .spi_clk = RCU_SPI1, \ + .spi_bus = &spi_bus1, \ + .sck_pin_name = BSP_SPI1_SCK_PIN, \ + .miso_pin_name = BSP_SPI1_MISO_PIN, \ + .mosi_pin_name = BSP_SPI1_MOSI_PIN, \ + .sck_alternate = BSP_SPI1_SCK_AFIO, \ + .miso_alternate = BSP_SPI1_MISO_AFIO, \ + .mosi_alternate = BSP_SPI1_MOSI_AFIO, \ + } +#endif +#endif + +#ifdef BSP_USING_SPI2 +#ifndef BSP_SPI2_SCK_PIN +#define BSP_SPI2_SCK_PIN "PB3" +#endif +#ifndef BSP_SPI2_MISO_PIN +#define BSP_SPI2_MISO_PIN "PB4" +#endif +#ifndef BSP_SPI2_MOSI_PIN +#define BSP_SPI2_MOSI_PIN "PB5" +#endif +#ifndef BSP_SPI2_SCK_AFIO +#define BSP_SPI2_SCK_AFIO "AF6" +#endif +#ifndef BSP_SPI2_MISO_AFIO +#define BSP_SPI2_MISO_AFIO "AF6" +#endif +#ifndef BSP_SPI2_MOSI_AFIO +#define BSP_SPI2_MOSI_AFIO "AF6" +#endif +#ifndef SPI2_BUS_CONFIG +#define SPI2_BUS_CONFIG \ + { \ + .spi_periph = SPI2, \ + .bus_name = "spi2", \ + .spi_clk = RCU_SPI2, \ + .spi_bus = &spi_bus2, \ + .sck_pin_name = BSP_SPI2_SCK_PIN, \ + .miso_pin_name = BSP_SPI2_MISO_PIN, \ + .mosi_pin_name = BSP_SPI2_MOSI_PIN, \ + .sck_alternate = BSP_SPI2_SCK_AFIO, \ + .miso_alternate = BSP_SPI2_MISO_AFIO, \ + .mosi_alternate = BSP_SPI2_MOSI_AFIO, \ + } +#endif +#endif + +#ifdef BSP_USING_SPI3 +#ifndef BSP_SPI3_SCK_PIN +#define BSP_SPI3_SCK_PIN "PE2" +#endif +#ifndef BSP_SPI3_MISO_PIN +#define BSP_SPI3_MISO_PIN "PE5" +#endif +#ifndef BSP_SPI3_MOSI_PIN +#define BSP_SPI3_MOSI_PIN "PE6" +#endif +#ifndef BSP_SPI3_SCK_AFIO +#define BSP_SPI3_SCK_AFIO "AF5" +#endif +#ifndef BSP_SPI3_MISO_AFIO +#define BSP_SPI3_MISO_AFIO "AF5" +#endif +#ifndef BSP_SPI3_MOSI_AFIO +#define BSP_SPI3_MOSI_AFIO "AF5" +#endif +#ifndef SPI3_BUS_CONFIG +#define SPI3_BUS_CONFIG \ + { \ + .spi_periph = SPI3, \ + .bus_name = "spi3", \ + .spi_clk = RCU_SPI3, \ + .spi_bus = &spi_bus3, \ + .sck_pin_name = BSP_SPI3_SCK_PIN, \ + .miso_pin_name = BSP_SPI3_MISO_PIN, \ + .mosi_pin_name = BSP_SPI3_MOSI_PIN, \ + .sck_alternate = BSP_SPI3_SCK_AFIO, \ + .miso_alternate = BSP_SPI3_MISO_AFIO, \ + .mosi_alternate = BSP_SPI3_MOSI_AFIO, \ + } +#endif +#endif + +#ifdef BSP_USING_SPI4 +#ifndef BSP_SPI4_SCK_PIN +#define BSP_SPI4_SCK_PIN "PF7" +#endif +#ifndef BSP_SPI4_MISO_PIN +#define BSP_SPI4_MISO_PIN "PF8" +#endif +#ifndef BSP_SPI4_MOSI_PIN +#define BSP_SPI4_MOSI_PIN "PF9" +#endif +#ifndef BSP_SPI4_SCK_AFIO +#define BSP_SPI4_SCK_AFIO "AF5" +#endif +#ifndef BSP_SPI4_MISO_AFIO +#define BSP_SPI4_MISO_AFIO "AF5" +#endif +#ifndef BSP_SPI4_MOSI_AFIO +#define BSP_SPI4_MOSI_AFIO "AF5" +#endif +#ifndef SPI4_BUS_CONFIG +#define SPI4_BUS_CONFIG \ + { \ + .spi_periph = SPI4, \ + .bus_name = "spi4", \ + .spi_clk = RCU_SPI4, \ + .spi_bus = &spi_bus4, \ + .sck_pin_name = BSP_SPI4_SCK_PIN, \ + .miso_pin_name = BSP_SPI4_MISO_PIN, \ + .mosi_pin_name = BSP_SPI4_MOSI_PIN, \ + .sck_alternate = BSP_SPI4_SCK_AFIO, \ + .miso_alternate = BSP_SPI4_MISO_AFIO, \ + .mosi_alternate = BSP_SPI4_MOSI_AFIO, \ + } +#endif +#endif + +#ifdef BSP_USING_SPI5 +#ifndef BSP_SPI5_SCK_PIN +#define BSP_SPI5_SCK_PIN "PG13" +#endif +#ifndef BSP_SPI5_MISO_PIN +#define BSP_SPI5_MISO_PIN "PG12" +#endif +#ifndef BSP_SPI5_MOSI_PIN +#define BSP_SPI5_MOSI_PIN "PG14" +#endif +#ifndef BSP_SPI5_SCK_AFIO +#define BSP_SPI5_SCK_AFIO "AF5" +#endif +#ifndef BSP_SPI5_MISO_AFIO +#define BSP_SPI5_MISO_AFIO "AF5" +#endif +#ifndef BSP_SPI5_MOSI_AFIO +#define BSP_SPI5_MOSI_AFIO "AF5" +#endif +#ifndef SPI5_BUS_CONFIG +#define SPI5_BUS_CONFIG \ + { \ + .spi_periph = SPI5, \ + .bus_name = "spi5", \ + .spi_clk = RCU_SPI5, \ + .spi_bus = &spi_bus5, \ + .sck_pin_name = BSP_SPI5_SCK_PIN, \ + .miso_pin_name = BSP_SPI5_MISO_PIN, \ + .mosi_pin_name = BSP_SPI5_MOSI_PIN, \ + .sck_alternate = BSP_SPI5_SCK_AFIO, \ + .miso_alternate = BSP_SPI5_MISO_AFIO, \ + .mosi_alternate = BSP_SPI5_MOSI_AFIO, \ + } +#endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __SPI_CONFIG_H__ */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_can.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_can.c index ac60157a0f86..4b4c4bf2c524 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_can.c +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_can.c @@ -8,18 +8,34 @@ * Change Logs: * Date Author Notes * 2025-18-03 Dmitriy Chernov first implementation for GD32F4xx - * 2025-09-24 CYFS add support for GD32F5xx + * 2026-09-08 CYFS add GD32F5xx and GD32H77D CAN FD support */ #include "drv_can.h" #include "string.h" +#if defined(SOC_SERIES_GD32H77x_H78X) +#include "can_config.h" +#endif #ifdef BSP_USING_CAN #define LOG_TAG "can_drv" #include -#if defined(GD32F405) || defined(GD32F407) /* 42MHz(max) */ +#if defined(SOC_SERIES_GD32H77x_H78X) /* APB2 300MHz(max) */ +static const struct gd32_baudrate_tbl can_baudrate_tbl[] = +{ + {CAN1MBaud, 1, 20, 27, 12, 5}, + {CAN800kBaud, 1, 2, 9, 3, 25}, + {CAN500kBaud, 1, 2, 5, 2, 60}, + {CAN250kBaud, 1, 2, 5, 2, 120}, + {CAN125kBaud, 1, 2, 5, 2, 240}, + {CAN100kBaud, 1, 2, 5, 2, 300}, + {CAN50kBaud, 1, 2, 5, 2, 600}, + {CAN20kBaud, 1, 14, 25, 10, 300}, + {CAN10kBaud, 1, 14, 25, 10, 600}, +}; +#elif defined(GD32F405) || defined(GD32F407) /* 42MHz(max) */ static const struct gd32_baudrate_tbl can_baudrate_tbl[] = { {CAN1MBaud, CAN_BT_SJW_1TQ, CAN_BT_BS1_11TQ, CAN_BT_BS2_2TQ, 3}, @@ -62,7 +78,7 @@ static const struct gd32_baudrate_tbl can_baudrate_tbl[] = #error "CAN driver not implemented for selected device" #endif -#ifdef BSP_USING_CAN0 +#if defined(BSP_USING_CAN0) && !defined(SOC_SERIES_GD32H77x_H78X) static struct gd32_can_device dev_can0 = { .name = "can0", @@ -73,14 +89,14 @@ static struct gd32_can_device dev_can0 = #ifdef BSP_USING_CAN1 static struct gd32_can_device dev_can1 = { - "can1", + .name = "can1", .can_x = CAN1, }; #endif static const struct gd32_can gd32_can_gpio[] = { -#ifdef BSP_USING_CAN0 +#if defined(BSP_USING_CAN0) && !defined(SOC_SERIES_GD32H77x_H78X) { .can_clk = RCU_CAN0, #if defined SOC_SERIES_GD32F4xx || defined SOC_SERIES_GD32F5xx @@ -120,6 +136,9 @@ static const struct gd32_can gd32_can_gpio[] = #endif #ifdef BSP_USING_CAN1 +#if defined(SOC_SERIES_GD32H77x_H78X) + CAN1_GPIO_CONFIG, +#else { .can_clk = RCU_CAN1, #if defined SOC_SERIES_GD32F4xx || defined SOC_SERIES_GD32F5xx @@ -146,12 +165,42 @@ static const struct gd32_can gd32_can_gpio[] = #endif }, #endif +#endif }; -static void gd32_can_gpio_init(void) +static rt_err_t gd32_can_gpio_init(void) { for (rt_uint32_t i = 0; i < sizeof(gd32_can_gpio) / sizeof(gd32_can_gpio[0]); i++) { +#if defined(SOC_SERIES_GD32H77x_H78X) + rt_uint32_t tx_port, rx_port; + rt_uint32_t tx_pin, rx_pin; + rt_uint32_t tx_af, rx_af; + rcu_periph_enum tx_clk, rx_clk; + + if (get_pin_config(gd32_can_gpio[i].tx_pin_name, &tx_port, &tx_pin, &tx_clk) != RT_EOK || + get_pin_config(gd32_can_gpio[i].rx_pin_name, &rx_port, &rx_pin, &rx_clk) != RT_EOK || + pin_alternate_config(gd32_can_gpio[i].tx_alternate, &tx_af) != RT_EOK || + pin_alternate_config(gd32_can_gpio[i].rx_alternate, &rx_af) != RT_EOK) + { + return -RT_EINVAL; + } + +#ifdef BSP_USING_CAN1 + rcu_can_clock_config(IDX_CAN1, RCU_CANSRC_APB2); +#endif + rcu_periph_clock_enable(gd32_can_gpio[i].can_clk); + rcu_periph_clock_enable(tx_clk); + rcu_periph_clock_enable(rx_clk); + + gpio_output_options_set(tx_port, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, tx_pin); + gpio_mode_set(tx_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, tx_pin); + gpio_af_set(tx_port, tx_af, tx_pin); + + gpio_output_options_set(rx_port, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, rx_pin); + gpio_mode_set(rx_port, GPIO_MODE_AF, GPIO_PUPD_NONE, rx_pin); + gpio_af_set(rx_port, rx_af, rx_pin); +#else rcu_periph_clock_enable(gd32_can_gpio[i].can_clk); rcu_periph_clock_enable(gd32_can_gpio[i].tx_clk); rcu_periph_clock_enable(gd32_can_gpio[i].rx_clk); @@ -168,8 +217,11 @@ static void gd32_can_gpio_init(void) #else gpio_init(PIN_GDPORT(gd32_can_gpio[i].tx_pin), GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, PIN_GDPIN(gd32_can_gpio[i].tx_pin)); gpio_init(PIN_GDPORT(gd32_can_gpio[i].rx_pin), GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, PIN_GDPIN(gd32_can_gpio[i].rx_pin)); +#endif #endif } + + return RT_EOK; } static rt_uint32_t get_can_baudrate_index(rt_uint32_t baudrate) @@ -186,17 +238,229 @@ static rt_uint32_t get_can_baudrate_index(rt_uint32_t baudrate) return 0; /* default baudrate is CAN1MBaud */ } +#if defined(SOC_SERIES_GD32H77x_H78X) && defined(RT_CAN_USING_CANFD) +static rt_bool_t _can_timing_segments(rt_uint32_t quanta, rt_bool_t data_phase, + struct gd32_baudrate_tbl *timing) +{ + rt_uint32_t segment_1; + rt_uint32_t min_prop = data_phase ? 0U : 1U; + rt_uint32_t max_prop = data_phase ? 63U : 64U; + + if (quanta < 5U || quanta > 1U + max_prop + 32U + 32U) + { + return RT_FALSE; + } + + timing->tseg2 = (quanta + 2U) / 5U; + if (timing->tseg2 < 2U) + { + timing->tseg2 = 2U; + } + segment_1 = quanta - 1U - timing->tseg2; + timing->prop_seg = segment_1 > 32U ? segment_1 - 32U : min_prop; + timing->tseg1 = segment_1 - timing->prop_seg; + timing->sjw = 1U; + + return timing->prop_seg <= max_prop && timing->tseg1 >= 1U && timing->tseg1 <= 32U; +} + +static rt_err_t _can_fd_timing_get(const struct can_configure *cfg, + struct gd32_baudrate_tbl *nominal, struct gd32_baudrate_tbl *data) +{ + rt_uint32_t clock = rcu_clock_freq_get(CK_APB2); + + if (cfg->baud_rate < CAN10kBaud || cfg->baud_rate > CAN1MBaud || + cfg->baud_rate_fd < cfg->baud_rate || cfg->baud_rate_fd > 8000000U) + { + return -RT_EINVAL; + } + + for (rt_uint32_t prescaler = 1U; prescaler <= 1024U; prescaler++) + { + rt_uint32_t bit_clock = clock / prescaler; + + if (clock % prescaler != 0U || bit_clock % cfg->baud_rate != 0U || + bit_clock % cfg->baud_rate_fd != 0U) + { + continue; + } + if (_can_timing_segments(bit_clock / cfg->baud_rate, RT_FALSE, nominal) && + _can_timing_segments(bit_clock / cfg->baud_rate_fd, RT_TRUE, data)) + { + nominal->prescaler = prescaler; + data->prescaler = prescaler; + return RT_EOK; + } + } + + return -RT_EINVAL; +} +#endif + static rt_err_t _can_config(struct rt_can_device *can, struct can_configure *cfg) { can_parameter_struct can_init_struct; - can_struct_para_init(CAN_INIT_STRUCT, &can_init_struct); + rt_uint32_t baudrate_index; +#if defined(SOC_SERIES_GD32H77x_H78X) + rt_uint32_t interrupt_mask; + rt_bool_t error_interrupt; +#ifdef RT_CAN_USING_CANFD + struct gd32_baudrate_tbl nominal_timing = {0}; + struct gd32_baudrate_tbl data_timing = {0}; +#endif +#endif RT_ASSERT(can); RT_ASSERT(cfg); struct gd32_can_device *can_dev = (struct gd32_can_device *)can->parent.user_data; RT_ASSERT(can_dev != RT_NULL); +#if defined(SOC_SERIES_GD32H77x_H78X) + if (cfg->sndboxnumber == 0U || cfg->sndboxnumber > GD32_CAN_TX_MAILBOX_NUM || + (can->parent.ref_count != 0U && + (cfg->sndboxnumber != can->config.sndboxnumber || cfg->msgboxsz != can->config.msgboxsz))) + { + return -RT_EINVAL; + } +#ifdef RT_CAN_USING_CANFD + if (cfg->sndboxnumber == GD32_CAN_TX_MAILBOX_NUM) + { + return -RT_EINVAL; + } + if (cfg->use_bit_timing) + { + return -RT_ENOSYS; + } + if (cfg->enable_canfd > 1U || + (cfg->enable_canfd && _can_fd_timing_get(cfg, &nominal_timing, &data_timing) != RT_EOK)) + { + return -RT_EINVAL; + } +#endif + if (can->parent.ref_count != 0U) + { + for (rt_uint32_t mailbox = GD32_CAN_TX_MAILBOX_FIRST; mailbox < GD32_CAN_MAILBOX_NUM; mailbox++) + { + rt_uint32_t code = can_mailbox_code_get(can_dev->can_x, mailbox); + + if (code == CAN_MB_TX_STATUS_DATA || code == CAN_MB_TX_STATUS_ABORT) + { + return -RT_EBUSY; + } + } + } + interrupt_mask = CAN_INTEN(can_dev->can_x); + error_interrupt = (CAN_CTL1(can_dev->can_x) & CAN_CTL1_ERRSIE) != 0U; +#endif + can_deinit(can_dev->can_x); + can_struct_para_init(CAN_INIT_STRUCT, &can_init_struct); + + baudrate_index = get_can_baudrate_index(cfg->baud_rate); + +#if defined(SOC_SERIES_GD32H77x_H78X) + can_init_struct.internal_counter_source = CAN_TIMER_SOURCE_BIT_CLOCK; + can_init_struct.self_reception = DISABLE; + can_init_struct.mb_tx_order = CAN_TX_HIGH_PRIORITY_MB_FIRST; + can_init_struct.mb_tx_abort_enable = ENABLE; + can_init_struct.local_priority_enable = DISABLE; + can_init_struct.mb_rx_ide_rtr_type = CAN_IDE_RTR_FILTERED; + can_init_struct.mb_remote_frame = CAN_STORE_REMOTE_REQUEST_FRAME; + can_init_struct.rx_private_filter_queue_enable = ENABLE; + can_init_struct.edge_filter_enable = DISABLE; + can_init_struct.protocol_exception_enable = DISABLE; + can_init_struct.rx_filter_order = CAN_RX_FILTER_ORDER_MAILBOX_FIRST; + can_init_struct.memory_size = CAN_MEMSIZE_32_UNIT; + can_init_struct.mb_public_filter = 0U; + can_init_struct.resync_jump_width = can_baudrate_tbl[baudrate_index].sjw; + can_init_struct.prop_time_segment = can_baudrate_tbl[baudrate_index].prop_seg; + can_init_struct.time_segment_1 = can_baudrate_tbl[baudrate_index].tseg1; + can_init_struct.time_segment_2 = can_baudrate_tbl[baudrate_index].tseg2; + can_init_struct.prescaler = can_baudrate_tbl[baudrate_index].prescaler; + +#ifdef RT_CAN_USING_CANFD + if (cfg->enable_canfd) + { + can_init_struct.resync_jump_width = nominal_timing.sjw; + can_init_struct.prop_time_segment = nominal_timing.prop_seg; + can_init_struct.time_segment_1 = nominal_timing.tseg1; + can_init_struct.time_segment_2 = nominal_timing.tseg2; + can_init_struct.prescaler = nominal_timing.prescaler; + } +#endif + if (can_init(can_dev->can_x, &can_init_struct) != SUCCESS) + { + return -RT_ERROR; + } + can_auto_busoff_recovery_enable(can_dev->can_x); + +#ifdef RT_CAN_USING_CANFD + if (cfg->enable_canfd) + { + can_fd_parameter_struct fd_init; + rt_uint32_t offset = data_timing.prescaler * (1U + data_timing.prop_seg + data_timing.tseg1); + + can_struct_para_init(CAN_FD_INIT_STRUCT, &fd_init); + fd_init.iso_can_fd_enable = ENABLE; + fd_init.bitrate_switch_enable = ENABLE; + fd_init.mailbox_data_size = CAN_MAILBOX_DATA_SIZE_64_BYTES; + fd_init.prescaler = data_timing.prescaler; + fd_init.resync_jump_width = data_timing.sjw; + fd_init.prop_time_segment = data_timing.prop_seg; + fd_init.time_segment_1 = data_timing.tseg1; + fd_init.time_segment_2 = data_timing.tseg2; + fd_init.tdc_enable = cfg->baud_rate_fd > CAN1MBaud ? ENABLE : DISABLE; + fd_init.tdc_offset = offset > 127U ? 127U : offset; + can_fd_config(can_dev->can_x, &fd_init); + } +#endif + + rt_memset(can_dev->rx_message_valid, 0, sizeof(can_dev->rx_message_valid)); + for (rt_uint32_t mailbox = 0U; mailbox < GD32_CAN_RX_MAILBOX_NUM; mailbox++) + { + can_struct_para_init(CAN_MDSC_STRUCT, &can_dev->rx_message[mailbox]); + can_dev->rx_message[mailbox].code = CAN_MB_RX_STATUS_EMPTY; + can_dev->rx_message[mailbox].id = 0x55U; + can_mailbox_config(can_dev->can_x, mailbox, &can_dev->rx_message[mailbox]); + can_private_filter_config(can_dev->can_x, mailbox, 0U); + } + for (rt_uint32_t mailbox = GD32_CAN_TX_MAILBOX_FIRST; mailbox < GD32_CAN_MAILBOX_NUM; mailbox++) + { + can_mailbox_transmit_inactive(can_dev->can_x, mailbox); + } + + can_dev->tx_abort_mask = 0U; + CAN_STAT(can_dev->can_x) = GD32_CAN_TX_INT_MASK; + + switch (cfg->mode) + { + case RT_CAN_MODE_LISTEN: + can_dev->mode = CAN_MONITOR_MODE; + break; + case RT_CAN_MODE_LOOPBACK: + case RT_CAN_MODE_LOOPBACKANLISTEN: + can_dev->mode = CAN_LOOPBACK_SILENT_MODE; + break; + case RT_CAN_MODE_NORMAL: + default: + can_dev->mode = CAN_NORMAL_MODE; + break; + } + + if (can_operation_mode_enter(can_dev->can_x, can_dev->mode) != SUCCESS) + { + return -RT_ERROR; + } + can->config = *cfg; + CAN_INTEN(can_dev->can_x) = interrupt_mask; + if (error_interrupt) + { + can_interrupt_enable(can_dev->can_x, CAN_INT_ERR_SUMMARY); +#ifdef RT_CAN_USING_CANFD + can_interrupt_enable(can_dev->can_x, CAN_INT_ERR_SUMMARY_FD); +#endif + } +#else can_init_struct.time_triggered = DISABLE; can_init_struct.auto_bus_off_recovery = ENABLE; @@ -221,7 +485,6 @@ static rt_err_t _can_config(struct rt_can_device *can, struct can_configure *cfg break; } - rt_uint32_t baudrate_index = get_can_baudrate_index(cfg->baud_rate); can_init_struct.resync_jump_width = can_baudrate_tbl[baudrate_index].sjw; can_init_struct.time_segment_1 = can_baudrate_tbl[baudrate_index].tseg1; can_init_struct.time_segment_2 = can_baudrate_tbl[baudrate_index].tseg2; @@ -232,23 +495,167 @@ static rt_err_t _can_config(struct rt_can_device *can, struct can_configure *cfg } can_filter_init(&can_dev->filter_config); +#endif return RT_EOK; } +#if defined(SOC_SERIES_GD32H77x_H78X) +static void _can_message_irq_config(rt_uint32_t can_x, rt_bool_t enable) +{ +#ifdef CAN0 + if (can_x == CAN0) + { + if (enable) + { + nvic_irq_enable(CAN0_Message_IRQn, 1, 0); + } + else + { + nvic_irq_disable(CAN0_Message_IRQn); + } + } +#endif +#ifdef CAN1 + if (can_x == CAN1) + { + if (enable) + { + nvic_irq_enable(CAN1_Message_IRQn, 1, 0); + } + else + { + nvic_irq_disable(CAN1_Message_IRQn); + } + } +#endif +} + +static void _can_error_irq_config(rt_uint32_t can_x, rt_bool_t enable) +{ +#ifdef CAN0 + if (can_x == CAN0) + { + if (enable) + { + nvic_irq_enable(CAN0_Error_IRQn, 1, 0); + } + else + { + nvic_irq_disable(CAN0_Error_IRQn); + } + } +#endif +#ifdef CAN1 + if (can_x == CAN1) + { + if (enable) + { + nvic_irq_enable(CAN1_Error_IRQn, 1, 0); + } + else + { + nvic_irq_disable(CAN1_Error_IRQn); + } + } +#endif +} + +static rt_uint32_t _can_error_code_get(rt_uint32_t errtype) +{ + rt_uint32_t errcode = 0U; + rt_uint32_t state = GET_ERR1_ERRSI(errtype); + + if ((errtype & (CAN_ERR1_RWERRF | CAN_ERR1_TWERRF)) != 0U) + { + errcode |= BIT(0); + } + if (state == CAN_ERROR_STATE_PASSIVE) + { + errcode |= BIT(1); + } + else if (state >= CAN_ERROR_STATE_BUS_OFF) + { + errcode |= BIT(2); + } + + return errcode; +} + +static rt_uint32_t _can_last_error_get(rt_uint32_t errtype) +{ + if ((errtype & (CAN_ERR1_STFERR | CAN_ERR1_STFFERR)) != 0U) + { + return RT_CAN_BUS_BIT_PAD_ERR; + } + if ((errtype & (CAN_ERR1_FMERR | CAN_ERR1_FMFERR)) != 0U) + { + return RT_CAN_BUS_FORMAT_ERR; + } + if ((errtype & CAN_ERR1_ACKERR) != 0U) + { + return RT_CAN_BUS_ACK_ERR; + } + if ((errtype & (CAN_ERR1_BRERR | CAN_ERR1_BRFERR)) != 0U) + { + return RT_CAN_BUS_IMPLICIT_BIT_ERR; + } + if ((errtype & (CAN_ERR1_BDERR | CAN_ERR1_BDFERR)) != 0U) + { + return RT_CAN_BUS_EXPLICIT_BIT_ERR; + } + if ((errtype & (CAN_ERR1_CRCERR | CAN_ERR1_CRCFERR)) != 0U) + { + return RT_CAN_BUS_CRC_ERR; + } + + return RT_CAN_BUS_NO_ERR; +} +#endif + static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg) { rt_uint32_t argval; +#if defined(SOC_SERIES_GD32H77x_H78X) + struct can_configure config; +#else struct rt_can_filter_config *filter_cfg; +#endif RT_ASSERT(can != RT_NULL); struct gd32_can_device *can_dev = (struct gd32_can_device *)can->parent.user_data; RT_ASSERT(can_dev != RT_NULL); +#if defined(SOC_SERIES_GD32H77x_H78X) + config = can->config; +#endif switch (cmd) { case RT_DEVICE_CTRL_CLR_INT: argval = (rt_uint32_t) arg; +#if defined(SOC_SERIES_GD32H77x_H78X) + if (argval == RT_DEVICE_FLAG_INT_RX) + { + CAN_INTEN(can_dev->can_x) &= ~GD32_CAN_RX_INT_MASK; + } + else if (argval == RT_DEVICE_FLAG_INT_TX) + { + CAN_INTEN(can_dev->can_x) &= ~GD32_CAN_TX_INT_MASK; + } + else if (argval == RT_DEVICE_CAN_INT_ERR) + { + can_interrupt_disable(can_dev->can_x, CAN_INT_ERR_SUMMARY); +#ifdef RT_CAN_USING_CANFD + can_interrupt_disable(can_dev->can_x, CAN_INT_ERR_SUMMARY_FD); +#endif + _can_error_irq_config(can_dev->can_x, RT_FALSE); + } + + if ((CAN_INTEN(can_dev->can_x) & BITS(0, 31)) == 0U) + { + _can_message_irq_config(can_dev->can_x, RT_FALSE); + } +#else if (argval == RT_DEVICE_FLAG_INT_RX) { #ifdef CAN0 @@ -308,9 +715,30 @@ static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg) can_interrupt_disable(can_dev->can_x, CAN_INT_ERRN); can_interrupt_disable(can_dev->can_x, CAN_INT_ERR); } +#endif break; case RT_DEVICE_CTRL_SET_INT: argval = (rt_uint32_t) arg; +#if defined(SOC_SERIES_GD32H77x_H78X) + if (argval == RT_DEVICE_FLAG_INT_RX) + { + CAN_INTEN(can_dev->can_x) |= GD32_CAN_RX_INT_MASK; + _can_message_irq_config(can_dev->can_x, RT_TRUE); + } + else if (argval == RT_DEVICE_FLAG_INT_TX) + { + CAN_INTEN(can_dev->can_x) |= GD32_CAN_TX_INT_MASK; + _can_message_irq_config(can_dev->can_x, RT_TRUE); + } + else if (argval == RT_DEVICE_CAN_INT_ERR) + { + can_interrupt_enable(can_dev->can_x, CAN_INT_ERR_SUMMARY); +#ifdef RT_CAN_USING_CANFD + can_interrupt_enable(can_dev->can_x, CAN_INT_ERR_SUMMARY_FD); +#endif + _can_error_irq_config(can_dev->can_x, RT_TRUE); + } +#else if (argval == RT_DEVICE_FLAG_INT_RX) { can_interrupt_enable(can_dev->can_x, CAN_INT_RFNE0); @@ -370,9 +798,16 @@ static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg) } #endif } +#endif break; case RT_CAN_CMD_SET_FILTER: { +#if defined(SOC_SERIES_GD32H77x_H78X) + if (arg != RT_NULL) + { + return -RT_ENOSYS; + } +#else rt_uint32_t id_h = 0; rt_uint32_t id_l = 0; rt_uint32_t mask_h = 0; @@ -454,6 +889,7 @@ static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg) can_filter_init(&can_dev->filter_config); } } +#endif break; } case RT_CAN_CMD_SET_MODE: @@ -467,8 +903,13 @@ static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg) } if (argval != can_dev->device.config.mode) { +#if defined(SOC_SERIES_GD32H77x_H78X) + config.mode = argval; + return _can_config(can, &config); +#else can_dev->device.config.mode = argval; return _can_config(&can_dev->device, &can_dev->device.config); +#endif } break; case RT_CAN_CMD_SET_BAUD: @@ -487,8 +928,13 @@ static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg) } if (argval != can_dev->device.config.baud_rate) { +#if defined(SOC_SERIES_GD32H77x_H78X) + config.baud_rate = argval; + return _can_config(can, &config); +#else can_dev->device.config.baud_rate = argval; return _can_config(&can_dev->device, &can_dev->device.config); +#endif } break; case RT_CAN_CMD_SET_PRIV: @@ -500,18 +946,72 @@ static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg) } if (argval != can_dev->device.config.privmode) { +#if defined(SOC_SERIES_GD32H77x_H78X) + config.privmode = argval; + return _can_config(can, &config); +#else can_dev->device.config.privmode = argval; return _can_config(&can_dev->device, &can_dev->device.config); +#endif + } + break; + case RT_CAN_CMD_SET_CANFD: +#if defined(SOC_SERIES_GD32H77x_H78X) && defined(RT_CAN_USING_CANFD) + argval = (rt_uint32_t)arg; + if (argval > 1U) + { + return -RT_EINVAL; + } + if (config.enable_canfd != argval) + { + config.enable_canfd = argval; + return _can_config(can, &config); + } + break; +#else + return -RT_ENOSYS; +#endif + case RT_CAN_CMD_SET_BAUD_FD: +#if defined(SOC_SERIES_GD32H77x_H78X) && defined(RT_CAN_USING_CANFD) + { + struct gd32_baudrate_tbl nominal = {0}; + struct gd32_baudrate_tbl data = {0}; + + config.baud_rate_fd = (rt_uint32_t)arg; + if (_can_fd_timing_get(&config, &nominal, &data) != RT_EOK) + { + return -RT_EINVAL; + } + if (config.baud_rate_fd != can->config.baud_rate_fd) + { + return _can_config(can, &config); } break; + } +#else + return -RT_ENOSYS; +#endif + case RT_CAN_CMD_SET_BITTIMING: + return -RT_ENOSYS; case RT_CAN_CMD_GET_STATUS: { +#if defined(SOC_SERIES_GD32H77x_H78X) + can_error_counter_struct error_count; + rt_uint32_t errtype = CAN_ERR1(can_dev->can_x); + + can_error_counter_get(can_dev->can_x, &error_count); + can_dev->device.status.rcverrcnt = error_count.rx_errcnt; + can_dev->device.status.snderrcnt = error_count.tx_errcnt; + can_dev->device.status.lasterrtype = _can_last_error_get(errtype); + can_dev->device.status.errcode = _can_error_code_get(errtype); +#else rt_uint32_t errtype; errtype = CAN_STAT(can_dev->can_x); can_dev->device.status.rcverrcnt = errtype >> 24; can_dev->device.status.snderrcnt = (errtype >> 16 & 0xFF); can_dev->device.status.lasterrtype = errtype & 0x70; can_dev->device.status.errcode = errtype & 0x07; +#endif rt_memcpy(arg, &can_dev->device.status, sizeof(can_dev->device.status)); break; @@ -521,10 +1021,84 @@ static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg) return RT_EOK; } +#if defined(SOC_SERIES_GD32H77x_H78X) +static rt_err_t _can_message_validate(struct gd32_can_device *can_dev, const struct rt_can_msg *pmsg) +{ +#ifdef RT_CAN_USING_CANFD + if (pmsg->fd_frame) + { + if (!can_dev->device.config.enable_canfd || pmsg->rtr || pmsg->len > 64U) + { + return -RT_EINVAL; + } + return RT_EOK; + } + if (pmsg->brs) + { + return -RT_EINVAL; + } +#else + RT_UNUSED(can_dev); +#endif + return pmsg->len <= 8U ? RT_EOK : -RT_EINVAL; +} + +static rt_ssize_t _can_mailbox_send(struct gd32_can_device *can_dev, + const struct rt_can_msg *pmsg, rt_uint32_t box_num) +{ + rt_uint32_t mailbox = box_num + GD32_CAN_TX_MAILBOX_FIRST; + + if (box_num >= GD32_CAN_TX_MAILBOX_NUM || _can_message_validate(can_dev, pmsg) != RT_EOK) + { + return -RT_EINVAL; + } + if (can_mailbox_code_get(can_dev->can_x, mailbox) != CAN_MB_TX_STATUS_INACTIVE) + { + return -RT_EBUSY; + } + + rt_memset(&can_dev->tx_message[box_num], 0, sizeof(can_dev->tx_message[box_num])); + can_dev->tx_message[box_num].rtr = pmsg->rtr; + can_dev->tx_message[box_num].ide = pmsg->ide; + can_dev->tx_message[box_num].code = CAN_MB_TX_STATUS_DATA; + can_dev->tx_message[box_num].id = pmsg->id; + can_dev->tx_message[box_num].prio = pmsg->priv; + can_dev->tx_message[box_num].data_bytes = pmsg->len; +#ifdef RT_CAN_USING_CANFD + can_dev->tx_message[box_num].fdf = pmsg->fd_frame; + can_dev->tx_message[box_num].brs = pmsg->brs; + if (pmsg->fd_frame && pmsg->len > 8U) + { + static const rt_uint8_t lengths[] = {12U, 16U, 20U, 24U, 32U, 48U, 64U}; + + for (rt_uint32_t index = 0U; index < sizeof(lengths); index++) + { + if (pmsg->len <= lengths[index]) + { + can_dev->tx_message[box_num].data_bytes = lengths[index]; + break; + } + } + } +#endif + rt_memcpy(can_dev->tx_message[box_num].data, pmsg->data, pmsg->len); + can_mailbox_config(can_dev->can_x, mailbox, &can_dev->tx_message[box_num]); + + return RT_EOK; +} +#endif + static rt_ssize_t _can_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_t box_num) { RT_ASSERT(can); + RT_ASSERT(buf); +#if defined(SOC_SERIES_GD32H77x_H78X) + struct gd32_can_device *can_dev = (struct gd32_can_device *)can->parent.user_data; + const struct rt_can_msg *pmsg = (const struct rt_can_msg *)buf; + + return _can_mailbox_send(can_dev, pmsg, box_num); +#else can_trasnmit_message_struct transmit_message; can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &transmit_message); rt_uint32_t can_x = ((struct gd32_can_device *)can->parent.user_data)->can_x; @@ -609,12 +1183,44 @@ static rt_ssize_t _can_sendmsg(struct rt_can_device *can, const void *buf, rt_ui CAN_TMI(can_x, box_num) |= CAN_TMI_TEN; return RT_EOK; +#endif } static rt_ssize_t _can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t fifo) { RT_ASSERT(can); + RT_ASSERT(buf); + +#if defined(SOC_SERIES_GD32H77x_H78X) + struct gd32_can_device *can_dev = (struct gd32_can_device *)can->parent.user_data; + struct rt_can_msg *pmsg = (struct rt_can_msg *)buf; + if (fifo >= GD32_CAN_RX_MAILBOX_NUM || can_dev->rx_message_valid[fifo] == 0U) + { + return -RT_EEMPTY; + } + + can_dev->rx_message_valid[fifo] = 0U; + rt_memset(pmsg, 0, sizeof(*pmsg)); + pmsg->id = can_dev->rx_message[fifo].id; + pmsg->ide = can_dev->rx_message[fifo].ide; + pmsg->rtr = can_dev->rx_message[fifo].rtr; + pmsg->len = can_dev->rx_message[fifo].data_bytes; + pmsg->priv = 0U; + pmsg->rxfifo = fifo; + pmsg->hdr_index = fifo; +#ifdef RT_CAN_USING_CANFD + pmsg->fd_frame = can_dev->rx_message[fifo].fdf; + pmsg->brs = can_dev->rx_message[fifo].brs; +#endif + if (pmsg->len > sizeof(pmsg->data)) + { + return -RT_EINVAL; + } + rt_memcpy(pmsg->data, can_dev->rx_message[fifo].data, pmsg->len); + + return RT_EOK; +#else can_receive_message_struct receive_message; can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &receive_message); rt_uint32_t can_x = ((struct gd32_can_device *)can->parent.user_data)->can_x; @@ -660,11 +1266,21 @@ static rt_ssize_t _can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t #endif return RT_EOK; +#endif } rt_ssize_t _can_get_freebox(rt_uint32_t can_x) { rt_uint32_t freebox = 0; +#if defined(SOC_SERIES_GD32H77x_H78X) + for (rt_uint32_t mailbox = GD32_CAN_TX_MAILBOX_FIRST; mailbox < GD32_CAN_MAILBOX_NUM; mailbox++) + { + if (can_mailbox_code_get(can_x, mailbox) == CAN_MB_TX_STATUS_INACTIVE) + { + freebox++; + } + } +#else if ((CAN_STAT(can_x) & CAN_TSTAT_TME0) != 0U) { freebox++; @@ -677,13 +1293,36 @@ rt_ssize_t _can_get_freebox(rt_uint32_t can_x) { freebox++; } +#endif return freebox; } rt_ssize_t _can_sendmsg_nonblocking(struct rt_can_device *can, const void *buf) { RT_ASSERT(can); + RT_ASSERT(buf); +#if defined(SOC_SERIES_GD32H77x_H78X) + struct gd32_can_device *can_dev = (struct gd32_can_device *)can->parent.user_data; + const struct rt_can_msg *pmsg = (const struct rt_can_msg *)buf; + + if (_can_message_validate(can_dev, pmsg) != RT_EOK) + { + return -RT_EINVAL; + } + + for (rt_uint32_t box_num = can->config.sndboxnumber; box_num < GD32_CAN_TX_MAILBOX_NUM; box_num++) + { + rt_uint32_t mailbox = box_num + GD32_CAN_TX_MAILBOX_FIRST; + + if (can_mailbox_code_get(can_dev->can_x, mailbox) == CAN_MB_TX_STATUS_INACTIVE) + { + return _can_mailbox_send(can_dev, pmsg, box_num); + } + } + + return -RT_EBUSY; +#else can_trasnmit_message_struct transmit_message; can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &transmit_message); rt_uint32_t can_x = ((struct gd32_can_device *)can->parent.user_data)->can_x; @@ -721,6 +1360,7 @@ rt_ssize_t _can_sendmsg_nonblocking(struct rt_can_device *can, const void *buf) } return RT_EOK; +#endif } static const struct rt_can_ops _can_ops = @@ -735,8 +1375,27 @@ static const struct rt_can_ops _can_ops = static void _can_rx_isr(struct rt_can_device *can, rt_uint32_t fifo) { RT_ASSERT(can); - rt_uint32_t can_x = ((struct gd32_can_device *)can->parent.user_data)->can_x; + struct gd32_can_device *can_dev = (struct gd32_can_device *)can->parent.user_data; + rt_uint32_t can_x = can_dev->can_x; +#if defined(SOC_SERIES_GD32H77x_H78X) + RT_UNUSED(fifo); + for (rt_uint32_t mailbox = 0U; mailbox < GD32_CAN_RX_MAILBOX_NUM; mailbox++) + { + if ((CAN_STAT(can_x) & CAN_INTEN(can_x) & BIT(mailbox)) != 0U) + { + if (can_mailbox_receive_data_read(can_x, mailbox, &can_dev->rx_message[mailbox]) == SUCCESS) + { + can_dev->rx_message_valid[mailbox] = 1U; + rt_hw_can_isr(can, RT_CAN_EVENT_RX_IND | (mailbox << 8)); + } + else + { + CAN_STAT(can_x) = BIT(mailbox); + } + } + } +#else switch (fifo) { case CAN_RX_FIFO0: @@ -782,12 +1441,67 @@ static void _can_rx_isr(struct rt_can_device *can, rt_uint32_t fifo) } break; } +#endif } static void _can_ewmc_isr(struct rt_can_device *can) { RT_ASSERT(can); - rt_uint32_t can_x = ((struct gd32_can_device *)can->parent.user_data)->can_x; + struct gd32_can_device *can_dev = (struct gd32_can_device *)can->parent.user_data; + rt_uint32_t can_x = can_dev->can_x; +#if defined(SOC_SERIES_GD32H77x_H78X) + rt_uint32_t errtype; + + if (can_interrupt_flag_get(can_x, CAN_INT_FLAG_ERR_SUMMARY) == SET || + can_interrupt_flag_get(can_x, CAN_INT_FLAG_ERR_SUMMARY_FD) == SET) + { + errtype = CAN_ERR1(can_x); + if ((errtype & (CAN_ERR1_STFERR | CAN_ERR1_STFFERR)) != 0U) + { + can->status.bitpaderrcnt++; + } + if ((errtype & (CAN_ERR1_FMERR | CAN_ERR1_FMFERR)) != 0U) + { + can->status.formaterrcnt++; + } + if ((errtype & (CAN_ERR1_CRCERR | CAN_ERR1_CRCFERR)) != 0U) + { + can->status.crcerrcnt++; + } + if ((errtype & CAN_ERR1_ACKERR) != 0U) + { + can->status.ackerrcnt++; + } + if ((errtype & (CAN_ERR1_BDERR | CAN_ERR1_BRERR | CAN_ERR1_BDFERR | CAN_ERR1_BRFERR)) != 0U) + { + can->status.biterrcnt++; + } + can->status.lasterrtype = _can_last_error_get(errtype); + can->status.errcode = _can_error_code_get(errtype); + + if (((errtype & CAN_ERR1_ACKERR) != 0U) || + ((errtype & CAN_ERR1_BOF) != 0U) || + (((errtype & CAN_ERR1_TS) != 0U) && + ((errtype & (CAN_ERR1_STFERR | CAN_ERR1_FMERR | CAN_ERR1_CRCERR | + CAN_ERR1_BDERR | CAN_ERR1_BRERR | CAN_ERR1_STFFERR | + CAN_ERR1_FMFERR | CAN_ERR1_CRCFERR | CAN_ERR1_BDFERR | + CAN_ERR1_BRFERR)) != 0U))) + { + for (rt_uint32_t box_num = 0U; box_num < GD32_CAN_TX_MAILBOX_NUM; box_num++) + { + rt_uint32_t mailbox = box_num + GD32_CAN_TX_MAILBOX_FIRST; + + if (can_mailbox_code_get(can_x, mailbox) == CAN_MB_TX_STATUS_DATA) + { + can_dev->tx_abort_mask |= (rt_uint16_t)BIT(box_num); + can_mailbox_transmit_abort(can_x, mailbox); + } + } + } + can_interrupt_flag_clear(can_x, CAN_INT_FLAG_ERR_SUMMARY); + can_interrupt_flag_clear(can_x, CAN_INT_FLAG_ERR_SUMMARY_FD); + } +#else rt_uint32_t errtype = CAN_ERR(can_x); switch ((errtype & 0x70) >> 4) @@ -854,6 +1568,7 @@ static void _can_ewmc_isr(struct rt_can_device *can) can->status.snderrcnt = (errtype >> 16 & 0xFF); can->status.errcode = errtype & 0x07; CAN_STAT(can_x) |= CAN_STAT_ERRIF; +#endif } static void _can_tx_isr(struct rt_can_device *can) @@ -861,6 +1576,28 @@ static void _can_tx_isr(struct rt_can_device *can) RT_ASSERT(can); rt_uint32_t can_x = ((struct gd32_can_device *)can->parent.user_data)->can_x; +#if defined(SOC_SERIES_GD32H77x_H78X) + struct gd32_can_device *can_dev = (struct gd32_can_device *)can->parent.user_data; + + for (rt_uint32_t mailbox = GD32_CAN_TX_MAILBOX_FIRST; mailbox < GD32_CAN_MAILBOX_NUM; mailbox++) + { + if ((CAN_STAT(can_x) & CAN_INTEN(can_x) & BIT(mailbox)) != 0U) + { + rt_uint32_t box_num = mailbox - GD32_CAN_TX_MAILBOX_FIRST; + rt_uint32_t event = RT_CAN_EVENT_TX_DONE; + + if ((can_dev->tx_abort_mask & BIT(box_num)) != 0U || + can_mailbox_code_get(can_x, mailbox) != CAN_MB_TX_STATUS_INACTIVE) + { + can_dev->tx_abort_mask &= (rt_uint16_t)~BIT(box_num); + can_mailbox_transmit_inactive(can_x, mailbox); + event = RT_CAN_EVENT_TX_FAIL; + } + CAN_STAT(can_x) = BIT(mailbox); + rt_hw_can_isr(can, event | (box_num << 8)); + } + } +#else if (can_interrupt_flag_get(can_x, CAN_INT_FLAG_MTF0)) { if (can_flag_get(can_x, CAN_FLAG_MTFNERR0)) @@ -900,8 +1637,27 @@ static void _can_tx_isr(struct rt_can_device *can) /* Write 0 to Clear transmission status flag RQCPx */ can_interrupt_flag_clear(can_x, CAN_INT_FLAG_MTF2); } +#endif +} + +#if defined(SOC_SERIES_GD32H77x_H78X) +#ifdef BSP_USING_CAN1 +void CAN1_Message_IRQHandler(void) +{ + rt_interrupt_enter(); + _can_rx_isr(&dev_can1.device, 0U); + _can_tx_isr(&dev_can1.device); + rt_interrupt_leave(); } +void CAN1_Error_IRQHandler(void) +{ + rt_interrupt_enter(); + _can_ewmc_isr(&dev_can1.device); + rt_interrupt_leave(); +} +#endif /* BSP_USING_CAN1 */ +#else #ifdef BSP_USING_CAN0 /** * @brief This function handles CAN0 TX interrupts. transmit fifo0/1/2 is empty can trigger this interrupt @@ -985,6 +1741,7 @@ void CAN1_EWMC_IRQHandler(void) rt_interrupt_leave(); } #endif /* BSP_USING_CAN1 */ +#endif /* SOC_SERIES_GD32H77x_H78X */ // void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) // { @@ -1005,19 +1762,33 @@ void CAN1_EWMC_IRQHandler(void) int rt_hw_can_init(void) { struct can_configure config = CANDEFAULTCONFIG; +#if !defined(SOC_SERIES_GD32H77x_H78X) + can_filter_parameter_struct filter_config = {0}; +#endif + config.privmode = RT_CAN_MODE_NOPRIV; config.ticks = 50; +#if defined(SOC_SERIES_GD32H77x_H78X) && defined(RT_CAN_USING_CANFD) + config.baud_rate_fd = 2000000U; +#endif #ifdef RT_CAN_USING_HDR +#if defined(SOC_SERIES_GD32H77x_H78X) + config.maxhdr = GD32_CAN_RX_MAILBOX_NUM; +#else config.maxhdr = 14; #ifdef CAN1 config.maxhdr = 28; #endif +#endif #endif - gd32_can_gpio_init(); + if (gd32_can_gpio_init() != RT_EOK) + { + return -RT_ERROR; + } +#if !defined(SOC_SERIES_GD32H77x_H78X) /* config default filter */ - can_filter_parameter_struct filter_config = {0}; can_struct_para_init(CAN_FILTER_STRUCT, &filter_config); filter_config.filter_list_high = 0x0000; @@ -1029,13 +1800,22 @@ int rt_hw_can_init(void) filter_config.filter_mode = CAN_FILTERMODE_MASK; filter_config.filter_bits = CAN_FILTERBITS_32BIT; filter_config.filter_enable = ENABLE; +#endif +#if defined(SOC_SERIES_GD32H77x_H78X) +#ifdef BSP_USING_CAN1 + dev_can1.device.config = config; + if (rt_hw_can_register(&dev_can1.device, dev_can1.name, &_can_ops, &dev_can1) != RT_EOK) + { + return -RT_ERROR; + } +#endif +#else #ifdef BSP_USING_CAN0 filter_config.filter_number = 0; dev_can0.filter_config = filter_config; dev_can0.device.config = config; - /* register CAN1 device */ rt_hw_can_register(&dev_can0.device, dev_can0.name, &_can_ops, @@ -1047,14 +1827,14 @@ int rt_hw_can_init(void) dev_can1.filter_config = filter_config; dev_can1.device.config = config; - /* register CAN2 device */ rt_hw_can_register(&dev_can1.device, dev_can1.name, &_can_ops, &dev_can1); #endif /* BSP_USING_CAN1 */ +#endif - return 0; + return RT_EOK; } INIT_BOARD_EXPORT(rt_hw_can_init); diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_can.h b/bsp/gd32/arm/libraries/gd32_drivers/drv_can.h index 579e3550491b..528dc631b93c 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_can.h +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_can.h @@ -8,7 +8,6 @@ * Change Logs: * Date Author Notes * 2025-18-03 Dmitriy Chernov first implementation - * 2025-09-24 CYFS add support for GD32F5xx */ #ifndef __DRV_CAN_H__ @@ -30,22 +29,46 @@ extern "C" { #define RRESCLMASK (0x3FF << RRESCLSHIFT ) #define SJWMASK (0x3 << SJWSHIFT ) +#if defined(SOC_SERIES_GD32H77x_H78X) +#ifdef RT_CAN_USING_CANFD +#define GD32_CAN_RX_MAILBOX_NUM 3U +#define GD32_CAN_TX_MAILBOX_NUM 4U +#else +#define GD32_CAN_RX_MAILBOX_NUM 16U +#define GD32_CAN_TX_MAILBOX_NUM 16U +#endif +#define GD32_CAN_TX_MAILBOX_FIRST GD32_CAN_RX_MAILBOX_NUM +#define GD32_CAN_MAILBOX_NUM (GD32_CAN_RX_MAILBOX_NUM + GD32_CAN_TX_MAILBOX_NUM) +#define GD32_CAN_RX_INT_MASK BITS(0, GD32_CAN_RX_MAILBOX_NUM - 1U) +#define GD32_CAN_TX_INT_MASK BITS(GD32_CAN_TX_MAILBOX_FIRST, GD32_CAN_MAILBOX_NUM - 1U) +#endif + struct gd32_can { rcu_periph_enum can_clk; +#if defined(SOC_SERIES_GD32H77x_H78X) + const char *tx_pin_name; + const char *rx_pin_name; + const char *tx_alternate; + const char *rx_alternate; +#else rcu_periph_enum tx_clk; rcu_periph_enum rx_clk; -#if defined SOC_SERIES_GD32F4xx || defined SOC_SERIES_GD32F5xx +#if defined(SOC_SERIES_GD32F4xx) || defined(SOC_SERIES_GD32F5xx) uint32_t alt_func_num; #endif rt_base_t tx_pin; rt_base_t rx_pin; +#endif }; struct gd32_baudrate_tbl { rt_uint32_t baudrate; rt_uint32_t sjw; +#if defined(SOC_SERIES_GD32H77x_H78X) + rt_uint32_t prop_seg; +#endif rt_uint32_t tseg1; rt_uint32_t tseg2; rt_uint32_t prescaler; @@ -56,7 +79,15 @@ struct gd32_can_device { char *name; rt_uint32_t can_x; +#if defined(SOC_SERIES_GD32H77x_H78X) + can_operation_modes_enum mode; + can_mailbox_descriptor_struct tx_message[GD32_CAN_TX_MAILBOX_NUM]; + can_mailbox_descriptor_struct rx_message[GD32_CAN_RX_MAILBOX_NUM]; + rt_uint8_t rx_message_valid[GD32_CAN_RX_MAILBOX_NUM]; + rt_uint16_t tx_abort_mask; +#else can_filter_parameter_struct filter_config; +#endif struct rt_can_device device; /* inherit from can device */ }; diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_enet.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_enet.c new file mode 100644 index 000000000000..9ebc258cc0d6 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_enet.c @@ -0,0 +1,797 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#if defined(SOC_SERIES_GD32H77x_H78X) && defined(BSP_USING_ETH) + +#include +#include +#include "gd32h77x_78x_enet.h" + +#define DBG_TAG "drv.enet" +#define DBG_LVL DBG_INFO +#include + +#define ENET_DEVICE_NAME "e0" +#define ENET_PERIPH ENET1 +#define ENET_PHY_ADDR_INVALID 0xFFU +#define ENET_PHY_ID1_REG 2U +#define ENET_PHY_ID2_REG 3U +#define ENET_PHY_ANAR_REG 4U +#define ENET_PHY_ANLPAR_REG 5U +#define ENET_PHY_EXT_DATA_REG 0x0DU +#define ENET_PHY_EXT_ADDR_REG 0x0EU +#define ENET_PHY_LED_CTRL0_EXT 0x1E33U +#define ENET_PHY_LED_MODE_MASK 0x0070U +#define ENET_PHY_GD30PH201D_ID1 0xADB4U +#define ENET_PHY_GD30PH201D_ID2 0x0400U +#define ENET_PHY_GD30PH201D_ID2_MASK 0xFFF0U +#define ENET_PHY_AN_10_HALF (1U << 5) +#define ENET_PHY_AN_10_FULL (1U << 6) +#define ENET_PHY_AN_100_HALF (1U << 7) +#define ENET_PHY_AN_100_FULL (1U << 8) +#define ENET_PHY_POLL_TICKS RT_TICK_PER_SECOND +#define ENET_TX_WAIT_TICKS RT_TICK_PER_SECOND +#define ENET_DMA_RAM_BASE 0x30004000U +#define ENET_DMA_RAM_SIZE 0x00004000U +#define ENET_DMA_STATUS_W1C 0x0001E7FFU + +#ifdef RT_LWIP_USING_HW_CHECKSUM +#define ENET_RX_CHECKSUM_MODE ENET_AUTOCHECKSUM_DROP_FAILFRAMES +#define ENET_TX_CHECKSUM_MODE ENET_CHECKSUM_TCPUDPICMP_FULL +#else +#define ENET_RX_CHECKSUM_MODE ENET_NO_AUTOCHECKSUM +#define ENET_TX_CHECKSUM_MODE ENET_CHECKSUM_DISABLE +#endif + +struct gd32_enet_dma +{ + volatile enet_descriptors_struct rx_desc[ENET_RXBUF_NUM]; + volatile enet_descriptors_struct tx_desc[ENET_TXBUF_NUM]; + volatile rt_uint8_t rx_buffer[ENET_RXBUF_NUM][ENET_RXBUF_SIZE]; + volatile rt_uint8_t tx_buffer[ENET_TXBUF_NUM][ENET_TXBUF_SIZE]; +}; + +struct gd32_enet +{ + struct eth_device parent; + rt_uint8_t mac[6]; + rt_uint8_t phy_addr; + rt_bool_t link_up; + rt_uint32_t media_mode; + volatile rt_uint32_t dma_error_status; + struct rt_mutex tx_lock; + volatile enet_descriptors_struct *rx_desc; + volatile enet_descriptors_struct *tx_desc; +}; + +static struct gd32_enet gd32_enet_device; +static struct gd32_enet_dma gd32_enet_dma + __attribute__((section(".enet_dma"), aligned(32))); + +typedef char gd32_enet_dma_size_check[ + (sizeof(struct gd32_enet_dma) <= ENET_DMA_RAM_SIZE) ? 1 : -1]; + +static void enet_tx_desc_reset(void) +{ + rt_uint32_t index; + + rt_memset((void *)&gd32_enet_dma.tx_desc[0], 0, sizeof(gd32_enet_dma.tx_desc)); + for (index = 0; index < ENET_TXBUF_NUM; index++) + { + volatile enet_descriptors_struct *desc = &gd32_enet_dma.tx_desc[index]; + + desc->status = ENET_TDES0_TCHM; + desc->buffer1_addr = (rt_uint32_t)&gd32_enet_dma.tx_buffer[index][0]; + desc->buffer2_next_desc_addr = (rt_uint32_t)&gd32_enet_dma.tx_desc[ + (index + 1U) % ENET_TXBUF_NUM]; + } + + gd32_enet_device.tx_desc = &gd32_enet_dma.tx_desc[0]; + __DMB(); + ENET_DMA_TDTADDR(ENET_PERIPH) = (rt_uint32_t)&gd32_enet_dma.tx_desc[0]; + __DSB(); +} + +static rt_err_t enet_tx_stop(void) +{ + rt_tick_t start; + + enet_tx_disable(ENET_PERIPH); + start = rt_tick_get(); + while (GET_DMA_STAT_TP(ENET_DMA_STAT(ENET_PERIPH)) != 0U) + { + if ((rt_tick_get() - start) >= rt_tick_from_millisecond(100)) + { + return -RT_ETIMEOUT; + } + rt_thread_mdelay(1); + } + + return RT_EOK; +} + +static void enet_gpio_config(void) +{ + rcu_periph_clock_enable(RCU_GPIOC); + rcu_periph_clock_enable(RCU_GPIOD); + rcu_periph_clock_enable(RCU_GPIOG); + rcu_periph_clock_enable(RCU_SYSCFG); + + syscfg_enet_phy_interface_config(ENET_PERIPH, SYSCFG_ENET_PHY_RMII); + + /* PC12: REF_CLK, PC6: MDIO, PC7: TX_EN. */ + gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, + GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_12); + gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, + GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_12); + gpio_af_set(GPIOC, GPIO_AF_3, GPIO_PIN_12); + gpio_af_set(GPIOC, GPIO_AF_6, GPIO_PIN_6); + gpio_af_set(GPIOC, GPIO_AF_0, GPIO_PIN_7); + + /* PD2: CRS_DV, PD4: RXD0, PD5: RXD1. */ + gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE, + GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5); + gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, + GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5); + gpio_af_set(GPIOD, GPIO_AF_6, GPIO_PIN_2 | GPIO_PIN_5); + gpio_af_set(GPIOD, GPIO_AF_9, GPIO_PIN_4); + + /* PG6: MDC, PG13: TXD0, PG14: TXD1. */ + gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, + GPIO_PIN_6 | GPIO_PIN_13 | GPIO_PIN_14); + gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, + GPIO_PIN_6 | GPIO_PIN_13 | GPIO_PIN_14); + gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_6 | GPIO_PIN_13 | GPIO_PIN_14); +} + +static void enet_dma_mpu_config(void) +{ + mpu_region_init_struct mpu_config; + + SCB_CleanInvalidateDCache_by_Addr((uint32_t *)ENET_DMA_RAM_BASE, + ENET_DMA_RAM_SIZE); + ARM_MPU_Disable(); + + mpu_region_struct_para_init(&mpu_config); + mpu_config.region_base_address = ENET_DMA_RAM_BASE; + mpu_config.region_size = MPU_REGION_SIZE_16KB; + mpu_config.access_permission = MPU_AP_FULL_ACCESS; + /* Normal non-cacheable memory permits optimized CPU copies and stays DMA coherent. */ + mpu_config.access_bufferable = MPU_ACCESS_NON_BUFFERABLE; + mpu_config.access_cacheable = MPU_ACCESS_NON_CACHEABLE; + mpu_config.access_shareable = MPU_ACCESS_SHAREABLE; + mpu_config.region_number = MPU_REGION_NUMBER7; + mpu_config.subregion_disable = MPU_SUBREGION_ENABLE; + mpu_config.instruction_exec = MPU_INSTRUCTION_EXEC_NOT_PERMIT; + mpu_config.tex_type = MPU_TEX_TYPE1; + mpu_region_config(&mpu_config); + mpu_region_enable(); + + ARM_MPU_Enable(MPU_MODE_PRIV_DEFAULT); +} + +static void enet_dma_desc_init(void) +{ + rt_uint32_t index; + + rt_memset(&gd32_enet_dma, 0, sizeof(gd32_enet_dma)); + + for (index = 0; index < ENET_RXBUF_NUM; index++) + { + volatile enet_descriptors_struct *desc = &gd32_enet_dma.rx_desc[index]; + + desc->status = ENET_RDES0_DAV; + desc->control_buffer_size = ENET_RDES1_RCHM | ENET_RXBUF_SIZE; + desc->buffer1_addr = (rt_uint32_t)&gd32_enet_dma.rx_buffer[index][0]; + desc->buffer2_next_desc_addr = (rt_uint32_t)&gd32_enet_dma.rx_desc[ + (index + 1U) % ENET_RXBUF_NUM]; + } + + gd32_enet_device.rx_desc = &gd32_enet_dma.rx_desc[0]; + __DMB(); + ENET_DMA_RDTADDR(ENET_PERIPH) = (rt_uint32_t)&gd32_enet_dma.rx_desc[0]; + enet_tx_desc_reset(); +} + +static rt_err_t enet_phy_read(rt_uint8_t address, rt_uint8_t reg, rt_uint16_t *value) +{ + return enet_phy_write_read(ENET_PERIPH, ENET_PHY_READ, address, reg, value) == SUCCESS + ? RT_EOK : -RT_ERROR; +} + +static rt_err_t enet_phy_write(rt_uint8_t address, rt_uint8_t reg, rt_uint16_t value) +{ + return enet_phy_write_read(ENET_PERIPH, ENET_PHY_WRITE, address, reg, &value) == SUCCESS + ? RT_EOK : -RT_ERROR; +} + +static rt_err_t enet_phy_ext_read(rt_uint8_t address, rt_uint16_t reg, rt_uint16_t *value) +{ + if (enet_phy_write(address, ENET_PHY_EXT_ADDR_REG, reg) != RT_EOK) + { + return -RT_ERROR; + } + + return enet_phy_read(address, ENET_PHY_EXT_DATA_REG, value); +} + +static rt_err_t enet_phy_ext_write(rt_uint8_t address, rt_uint16_t reg, rt_uint16_t value) +{ + if (enet_phy_write(address, ENET_PHY_EXT_ADDR_REG, reg) != RT_EOK) + { + return -RT_ERROR; + } + + return enet_phy_write(address, ENET_PHY_EXT_DATA_REG, value); +} + +static rt_err_t enet_phy_gd30ph201d_led_mode_init(rt_uint8_t address) +{ + rt_uint16_t id1; + rt_uint16_t id2; + rt_uint16_t value; + + if ((enet_phy_read(address, ENET_PHY_ID1_REG, &id1) != RT_EOK) || + (enet_phy_read(address, ENET_PHY_ID2_REG, &id2) != RT_EOK)) + { + return -RT_ERROR; + } + + if ((id1 != ENET_PHY_GD30PH201D_ID1) || + ((id2 & ENET_PHY_GD30PH201D_ID2_MASK) != ENET_PHY_GD30PH201D_ID2)) + { + return RT_EOK; + } + + if (enet_phy_ext_read(address, ENET_PHY_LED_CTRL0_EXT, &value) != RT_EOK) + { + return -RT_ERROR; + } + value &= (rt_uint16_t)~ENET_PHY_LED_MODE_MASK; + + return enet_phy_ext_write(address, ENET_PHY_LED_CTRL0_EXT, value); +} + +static rt_bool_t enet_phy_address_valid(rt_uint8_t address) +{ + rt_uint16_t id1; + rt_uint16_t id2; + + if ((enet_phy_read(address, ENET_PHY_ID1_REG, &id1) != RT_EOK) || + (enet_phy_read(address, ENET_PHY_ID2_REG, &id2) != RT_EOK)) + { + return RT_FALSE; + } + + return !((id1 == 0U && id2 == 0U) || + (id1 == 0xFFFFU && id2 == 0xFFFFU)); +} + +static rt_uint8_t enet_phy_detect(void) +{ + rt_uint8_t address; + + if (enet_phy_address_valid(BSP_ENET_PHY_ADDR)) + { + return BSP_ENET_PHY_ADDR; + } + + for (address = 0; address < 32U; address++) + { + if ((address != BSP_ENET_PHY_ADDR) && enet_phy_address_valid(address)) + { + return address; + } + } + + return ENET_PHY_ADDR_INVALID; +} + +static rt_err_t enet_phy_restart_autonegotiation(rt_uint8_t address) +{ + rt_tick_t start; + rt_uint16_t value; + + if (enet_phy_write(address, PHY_REG_BCR, PHY_RESET) != RT_EOK) + { + return -RT_ERROR; + } + + start = rt_tick_get(); + do + { + if ((enet_phy_read(address, PHY_REG_BCR, &value) == RT_EOK) && + ((value & PHY_RESET) == 0U)) + { + if (enet_phy_gd30ph201d_led_mode_init(address) != RT_EOK) + { + LOG_W("GD30PH201D LED mode configuration failed"); + } + + return enet_phy_write(address, PHY_REG_BCR, + PHY_AUTONEGOTIATION | PHY_RESTART_AUTONEGOTIATION); + } + rt_thread_mdelay(10); + } while ((rt_tick_get() - start) < (RT_TICK_PER_SECOND * 2U)); + + return -RT_ETIMEOUT; +} + +static rt_err_t enet_phy_media_mode(rt_uint8_t address, rt_uint32_t *mode) +{ + rt_uint16_t advertise; + rt_uint16_t partner; + rt_uint16_t common; + + if ((enet_phy_read(address, ENET_PHY_ANAR_REG, &advertise) != RT_EOK) || + (enet_phy_read(address, ENET_PHY_ANLPAR_REG, &partner) != RT_EOK)) + { + return -RT_ERROR; + } + common = advertise & partner; + + if (common & ENET_PHY_AN_100_FULL) + { + *mode = ENET_100M_FULLDUPLEX; + } + else if (common & ENET_PHY_AN_100_HALF) + { + *mode = ENET_100M_HALFDUPLEX; + } + else if (common & ENET_PHY_AN_10_FULL) + { + *mode = ENET_10M_FULLDUPLEX; + } + else if (common & ENET_PHY_AN_10_HALF) + { + *mode = ENET_10M_HALFDUPLEX; + } + else + { + return -RT_ERROR; + } + + return RT_EOK; +} + +static rt_err_t enet_media_mode_set(rt_uint32_t mode) +{ + if (rt_mutex_take(&gd32_enet_device.tx_lock, ENET_TX_WAIT_TICKS) != RT_EOK) + { + return -RT_ETIMEOUT; + } + + if (enet_tx_stop() != RT_EOK) + { + rt_mutex_release(&gd32_enet_device.tx_lock); + return -RT_ETIMEOUT; + } + enet_rx_disable(ENET_PERIPH); + enet_tx_desc_reset(); + ENET_MAC_CFG(ENET_PERIPH) &= ~(ENET_MAC_CFG_SPD | ENET_MAC_CFG_DPM); + ENET_MAC_CFG(ENET_PERIPH) |= mode; + enet_rx_enable(ENET_PERIPH); + enet_tx_enable(ENET_PERIPH); + gd32_enet_device.media_mode = mode; + rt_mutex_release(&gd32_enet_device.tx_lock); + + return RT_EOK; +} + +static void enet_link_disable(void) +{ + if (rt_mutex_take(&gd32_enet_device.tx_lock, ENET_TX_WAIT_TICKS) != RT_EOK) + { + return; + } + + gd32_enet_device.link_up = RT_FALSE; + if (enet_tx_stop() != RT_EOK) + { + LOG_E("TX DMA stop timed out"); + enet_rx_disable(ENET_PERIPH); + rt_mutex_release(&gd32_enet_device.tx_lock); + return; + } + enet_rx_disable(ENET_PERIPH); + enet_tx_desc_reset(); + rt_mutex_release(&gd32_enet_device.tx_lock); +} + +static void enet_phy_monitor(void *parameter) +{ + rt_uint16_t status = 0; + rt_bool_t link_up; + rt_uint32_t dma_error_status; + + (void)parameter; + + while (gd32_enet_device.phy_addr == ENET_PHY_ADDR_INVALID) + { + gd32_enet_device.phy_addr = enet_phy_detect(); + if (gd32_enet_device.phy_addr == ENET_PHY_ADDR_INVALID) + { + LOG_E("no PHY found, check RMII clock and MDIO/MDC wiring"); + rt_thread_mdelay(2000); + } + } + + LOG_I("PHY found at address %u", gd32_enet_device.phy_addr); + if (enet_phy_restart_autonegotiation(gd32_enet_device.phy_addr) != RT_EOK) + { + LOG_E("PHY reset or auto-negotiation start failed"); + } + + while (1) + { + rt_base_t level; + + level = rt_hw_interrupt_disable(); + dma_error_status = gd32_enet_device.dma_error_status; + gd32_enet_device.dma_error_status = 0U; + rt_hw_interrupt_enable(level); + if (dma_error_status != 0U) + { + LOG_E("DMA fatal bus error, status: 0x%08x", dma_error_status); + } + + /* BSR link status is latch-low, so it must be read twice. */ + if (enet_phy_read(gd32_enet_device.phy_addr, PHY_REG_BSR, &status) != RT_EOK) + { + link_up = RT_FALSE; + } + else + { + link_up = (status & PHY_LINKED_STATUS) ? RT_TRUE : RT_FALSE; + } + + link_up = link_up && (status & PHY_AUTONEGO_COMPLETE); + if (link_up) + { + rt_uint32_t mode; + + if (enet_phy_media_mode(gd32_enet_device.phy_addr, &mode) != RT_EOK) + { + link_up = RT_FALSE; + } + else if ((!gd32_enet_device.link_up) || + (mode != gd32_enet_device.media_mode)) + { + if (enet_media_mode_set(mode) != RT_EOK) + { + link_up = RT_FALSE; + } + } + } + + if (link_up != gd32_enet_device.link_up) + { + if (!link_up) + { + enet_link_disable(); + } + gd32_enet_device.link_up = link_up; + eth_device_linkchange(&gd32_enet_device.parent, link_up); + if (link_up) + { + LOG_I("link up: %sMbps %s-duplex", + (gd32_enet_device.media_mode & ENET_MAC_CFG_SPD) ? "100" : "10", + (gd32_enet_device.media_mode & ENET_MAC_CFG_DPM) ? "full" : "half"); + } + else + { + LOG_I("link down"); + } + } + + rt_thread_delay(ENET_PHY_POLL_TICKS); + } +} + +static rt_err_t gd32_enet_init(rt_device_t dev) +{ + (void)dev; + + RT_ASSERT((rt_ubase_t)&gd32_enet_dma == ENET_DMA_RAM_BASE); + enet_dma_mpu_config(); + enet_gpio_config(); + + rcu_periph_clock_enable(RCU_ENET1); + rcu_periph_clock_enable(RCU_ENET1TX); + rcu_periph_clock_enable(RCU_ENET1RX); + + enet_deinit(ENET_PERIPH); + if (enet_software_reset(ENET_PERIPH) != SUCCESS) + { + LOG_E("MAC software reset timed out"); + return -RT_ETIMEOUT; + } + if (enet_phy_config(ENET_PERIPH) != SUCCESS) + { + LOG_E("MDC clock configuration failed"); + return -RT_ERROR; + } + if (enet_init(ENET_PERIPH, ENET_100M_FULLDUPLEX, + ENET_RX_CHECKSUM_MODE, + ENET_BROADCAST_FRAMES_PASS) != SUCCESS) + { + LOG_E("MAC initialization failed"); + return -RT_ERROR; + } + + enet_mac_address_set(ENET_PERIPH, ENET_MAC_ADDRESS0, gd32_enet_device.mac); + enet_dma_desc_init(); + + ENET_DMA_STAT(ENET_PERIPH) = ENET_DMA_STATUS_W1C; + enet_interrupt_enable(ENET_PERIPH, ENET_DMA_INT_NIE); + enet_interrupt_enable(ENET_PERIPH, ENET_DMA_INT_RIE); + nvic_irq_enable(ENET1_IRQn, 5U, 0U); + + return RT_EOK; +} + +static rt_err_t gd32_enet_open(rt_device_t dev, rt_uint16_t oflag) +{ + (void)dev; + (void)oflag; + return RT_EOK; +} + +static rt_err_t gd32_enet_close(rt_device_t dev) +{ + (void)dev; + return RT_EOK; +} + +static rt_ssize_t gd32_enet_read(rt_device_t dev, rt_off_t pos, + void *buffer, rt_size_t size) +{ + (void)dev; + (void)pos; + (void)buffer; + (void)size; + return 0; +} + +static rt_ssize_t gd32_enet_write(rt_device_t dev, rt_off_t pos, + const void *buffer, rt_size_t size) +{ + (void)dev; + (void)pos; + (void)buffer; + (void)size; + return 0; +} + +static rt_err_t gd32_enet_control(rt_device_t dev, int cmd, void *args) +{ + struct gd32_enet *enet = (struct gd32_enet *)dev; + + if (cmd == NIOCTL_GADDR) + { + if (args == RT_NULL) + { + return -RT_EINVAL; + } + rt_memcpy(args, enet->mac, sizeof(enet->mac)); + return RT_EOK; + } + + return -RT_ENOSYS; +} + +static rt_err_t gd32_enet_tx(rt_device_t dev, struct pbuf *p) +{ + struct gd32_enet *enet = (struct gd32_enet *)dev; + volatile enet_descriptors_struct *desc; + struct pbuf *q; + rt_tick_t start; + rt_uint32_t offset = 0; + + if ((p == RT_NULL) || (p->tot_len > ENET_TXBUF_SIZE)) + { + return -RT_EINVAL; + } + + if (rt_mutex_take(&enet->tx_lock, ENET_TX_WAIT_TICKS) != RT_EOK) + { + return -RT_ETIMEOUT; + } + + if (!enet->link_up) + { + rt_mutex_release(&enet->tx_lock); + return -RT_EBUSY; + } + + desc = enet->tx_desc; + start = rt_tick_get(); + while (desc->status & ENET_TDES0_DAV) + { + if ((rt_tick_get() - start) >= ENET_TX_WAIT_TICKS) + { + rt_mutex_release(&enet->tx_lock); + LOG_E("TX timeout: stat=0x%08x ctl=0x%08x ctd=0x%08x desc=0x%08x", + ENET_DMA_STAT(ENET_PERIPH), ENET_DMA_CTL(ENET_PERIPH), + ENET_DMA_CTDADDR(ENET_PERIPH), (rt_uint32_t)desc); + return -RT_ETIMEOUT; + } + rt_thread_mdelay(1); + } + + for (q = p; q != RT_NULL; q = q->next) + { + rt_memcpy((void *)(desc->buffer1_addr + offset), q->payload, q->len); + offset += q->len; + } + + desc->control_buffer_size = p->tot_len; + __DMB(); + desc->status = ENET_TDES0_TCHM | ENET_TDES0_FSG | ENET_TDES0_LSG | + ENET_TX_CHECKSUM_MODE | ENET_TDES0_DAV; + __DSB(); + + if (ENET_DMA_STAT(ENET_PERIPH) & (ENET_DMA_STAT_TBU | ENET_DMA_STAT_TU)) + { + ENET_DMA_STAT(ENET_PERIPH) = ENET_DMA_STAT_TBU | ENET_DMA_STAT_TU; + } + ENET_DMA_TPEN(ENET_PERIPH) = 0U; + + enet->tx_desc = (enet_descriptors_struct *)desc->buffer2_next_desc_addr; + rt_mutex_release(&enet->tx_lock); + return RT_EOK; +} + +static struct pbuf *gd32_enet_rx(rt_device_t dev) +{ + struct gd32_enet *enet = (struct gd32_enet *)dev; + volatile enet_descriptors_struct *desc = enet->rx_desc; + struct pbuf *p = RT_NULL; + rt_uint32_t length; + + if (desc->status & ENET_RDES0_DAV) + { + return RT_NULL; + } + + if (((desc->status & ENET_RDES0_ERRS) == 0U) && + (desc->status & ENET_RDES0_FDES) && + (desc->status & ENET_RDES0_LDES)) + { + length = GET_RDES0_FRML(desc->status); + if ((length >= 4U) && (length <= ENET_RXBUF_SIZE)) + { + struct pbuf *q; + rt_uint32_t offset = 0; + + length -= 4U; + p = pbuf_alloc(PBUF_RAW, length, PBUF_POOL); + if (p != RT_NULL) + { + for (q = p; q != RT_NULL; q = q->next) + { + rt_memcpy(q->payload, + (const void *)(desc->buffer1_addr + offset), q->len); + offset += q->len; + } + } + } + } + + __DMB(); + desc->status = ENET_RDES0_DAV; + __DSB(); + enet->rx_desc = (enet_descriptors_struct *)desc->buffer2_next_desc_addr; + + if (ENET_DMA_STAT(ENET_PERIPH) & ENET_DMA_STAT_RBU) + { + ENET_DMA_STAT(ENET_PERIPH) = ENET_DMA_STAT_RBU; + ENET_DMA_RPEN(ENET_PERIPH) = 0U; + } + + return p; +} + +void ENET1_IRQHandler(void) +{ + rt_uint32_t status; + rt_uint32_t clear = 0U; + + rt_interrupt_enter(); + status = ENET_DMA_STAT(ENET_PERIPH); + + if (status & ENET_DMA_STAT_RS) + { + clear |= ENET_DMA_STAT_RS | ENET_DMA_STAT_NI; + eth_device_ready(&gd32_enet_device.parent); + } + + if (status & ENET_DMA_STAT_FBE) + { + clear |= ENET_DMA_STAT_FBE | ENET_DMA_STAT_AI; + gd32_enet_device.dma_error_status |= status; + } + ENET_DMA_STAT(ENET_PERIPH) = clear; + rt_interrupt_leave(); +} + +#ifdef RT_USING_DEVICE_OPS +static const struct rt_device_ops gd32_enet_ops = +{ + gd32_enet_init, + gd32_enet_open, + gd32_enet_close, + gd32_enet_read, + gd32_enet_write, + gd32_enet_control, +}; +#endif + +static int rt_hw_gd32_enet_init(void) +{ + rt_thread_t thread; + rt_err_t result; + struct rt_device *device = &gd32_enet_device.parent.parent; + + gd32_enet_device.mac[0] = 0x02; + gd32_enet_device.mac[1] = 0x00; + gd32_enet_device.mac[2] = 0x00; + gd32_enet_device.mac[3] = 0x77; + gd32_enet_device.mac[4] = 0x00; + gd32_enet_device.mac[5] = 0x01; + gd32_enet_device.phy_addr = ENET_PHY_ADDR_INVALID; + gd32_enet_device.link_up = RT_FALSE; + gd32_enet_device.media_mode = ENET_100M_FULLDUPLEX; + gd32_enet_device.dma_error_status = 0U; + +#ifdef RT_USING_DEVICE_OPS + device->ops = &gd32_enet_ops; +#else + device->init = gd32_enet_init; + device->open = gd32_enet_open; + device->close = gd32_enet_close; + device->read = gd32_enet_read; + device->write = gd32_enet_write; + device->control = gd32_enet_control; +#endif + device->user_data = RT_NULL; + gd32_enet_device.parent.eth_rx = gd32_enet_rx; + gd32_enet_device.parent.eth_tx = gd32_enet_tx; + + result = rt_mutex_init(&gd32_enet_device.tx_lock, "enet_tx", RT_IPC_FLAG_PRIO); + if (result != RT_EOK) + { + return result; + } + + result = eth_device_init_with_flag(&gd32_enet_device.parent, ENET_DEVICE_NAME, + NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP); + if (result != RT_EOK) + { + return result; + } + + result = rt_device_init(device); + if (result != RT_EOK) + { + LOG_E("hardware initialization failed: %d", result); + return result; + } + + thread = rt_thread_create("phy", enet_phy_monitor, RT_NULL, 1024, 20, 10); + if (thread == RT_NULL) + { + return -RT_ENOMEM; + } + rt_thread_startup(thread); + + return RT_EOK; +} +INIT_DEVICE_EXPORT(rt_hw_gd32_enet_init); + +#endif /* SOC_SERIES_GD32H77x_H78X && BSP_USING_ETH */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_gpio.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_gpio.c index 01e800cb844d..bc80b5c1109a 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_gpio.c +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_gpio.c @@ -446,7 +446,7 @@ static void gd32_pin_mode(rt_device_t dev, rt_base_t pin, rt_uint8_t mode) const struct pin_index *index = RT_NULL; rt_uint32_t pin_mode = 0; -#if defined SOC_SERIES_GD32F4xx || defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x +#if defined SOC_SERIES_GD32F4xx || (defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H77x_H78X) || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x rt_uint32_t pin_pupd = 0, pin_odpp = 0; #endif @@ -458,7 +458,7 @@ static void gd32_pin_mode(rt_device_t dev, rt_base_t pin, rt_uint8_t mode) /* GPIO Periph clock enable */ rcu_periph_clock_enable(index->clk); -#if defined SOC_SERIES_GD32F4xx || defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x +#if defined SOC_SERIES_GD32F4xx || (defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H77x_H78X) || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x pin_mode = GPIO_MODE_OUTPUT; #else pin_mode = GPIO_MODE_OUT_PP; @@ -468,7 +468,7 @@ static void gd32_pin_mode(rt_device_t dev, rt_base_t pin, rt_uint8_t mode) { case PIN_MODE_OUTPUT: /* output setting */ -#if defined SOC_SERIES_GD32F4xx || defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x +#if defined SOC_SERIES_GD32F4xx || (defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H77x_H78X) || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x pin_mode = GPIO_MODE_OUTPUT; pin_pupd = GPIO_PUPD_NONE; pin_odpp = GPIO_OTYPE_PP; @@ -478,7 +478,7 @@ static void gd32_pin_mode(rt_device_t dev, rt_base_t pin, rt_uint8_t mode) break; case PIN_MODE_OUTPUT_OD: /* output setting: od. */ -#if defined SOC_SERIES_GD32F4xx || defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x +#if defined SOC_SERIES_GD32F4xx || (defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H77x_H78X) || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x pin_mode = GPIO_MODE_OUTPUT; pin_pupd = GPIO_PUPD_NONE; pin_odpp = GPIO_OTYPE_OD; @@ -488,7 +488,7 @@ static void gd32_pin_mode(rt_device_t dev, rt_base_t pin, rt_uint8_t mode) break; case PIN_MODE_INPUT: /* input setting: not pull. */ -#if defined SOC_SERIES_GD32F4xx || defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x +#if defined SOC_SERIES_GD32F4xx || (defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H77x_H78X) || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x pin_mode = GPIO_MODE_INPUT; pin_pupd = GPIO_PUPD_PULLUP | GPIO_PUPD_PULLDOWN; #else @@ -497,7 +497,7 @@ static void gd32_pin_mode(rt_device_t dev, rt_base_t pin, rt_uint8_t mode) break; case PIN_MODE_INPUT_PULLUP: /* input setting: pull up. */ -#if defined SOC_SERIES_GD32F4xx || defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x +#if defined SOC_SERIES_GD32F4xx || (defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H77x_H78X) || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x pin_mode = GPIO_MODE_INPUT; pin_pupd = GPIO_PUPD_PULLUP; #else @@ -506,7 +506,7 @@ static void gd32_pin_mode(rt_device_t dev, rt_base_t pin, rt_uint8_t mode) break; case PIN_MODE_INPUT_PULLDOWN: /* input setting: pull down. */ -#if defined SOC_SERIES_GD32F4xx || defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x +#if defined SOC_SERIES_GD32F4xx || (defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H77x_H78X) || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x pin_mode = GPIO_MODE_INPUT; pin_pupd = GPIO_PUPD_PULLDOWN; #else @@ -523,7 +523,7 @@ static void gd32_pin_mode(rt_device_t dev, rt_base_t pin, rt_uint8_t mode) { gpio_output_options_set(index->gpio_periph, pin_odpp, GPIO_OSPEED_50MHZ, index->pin); } -#elif defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H75E +#elif (defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H77x_H78X) || defined SOC_SERIES_GD32H75E gpio_mode_set(index->gpio_periph, pin_mode, pin_pupd, index->pin); if (pin_mode == GPIO_MODE_OUTPUT) { @@ -743,7 +743,7 @@ static rt_err_t gd32_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_ return -RT_EINVAL; } -#if defined SOC_SERIES_GD32F4xx || defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx +#if defined SOC_SERIES_GD32F4xx || (defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H77x_H78X) || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx rcu_periph_clock_enable(RCU_SYSCFG); #elif defined SOC_SERIES_GD32E23x rcu_periph_clock_enable(RCU_CFGCMP); @@ -758,7 +758,7 @@ static rt_err_t gd32_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_ nvic_irq_enable(irqmap->irqno, 5U, 0U); #endif /* connect EXTI line to GPIO pin */ -#if defined SOC_SERIES_GD32F4xx || defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x +#if defined SOC_SERIES_GD32F4xx || (defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H77x_H78X) || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x syscfg_exti_line_config(index->port_src, index->pin_src); #else gpio_exti_source_select(index->port_src, index->pin_src); @@ -817,7 +817,7 @@ rt_inline void pin_irq_hdr(int irqno) */ void GD32_GPIO_EXTI_IRQHandler(rt_int8_t exti_line) { -#if defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H75E) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined(SOC_SERIES_GD32H75E) exti_line_enum pin_exti_line = exti_line; #else exti_line_enum pin_exti_line = 1 << exti_line; diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_gpio.h b/bsp/gd32/arm/libraries/gd32_drivers/drv_gpio.h index a79efc7d9c67..828dbf8c5a00 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_gpio.h +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_gpio.h @@ -27,7 +27,9 @@ extern "C" { #include "gd32f30x_gpio.h" #elif defined SOC_SERIES_GD32F4xx #include "gd32f4xx_gpio.h" -#elif defined SOC_SERIES_GD32H7xx +#elif defined SOC_SERIES_GD32H77x_H78X +#include "gd32h77x_78x_gpio.h" +#elif (defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H77x_H78X) #include "gd32h7xx_gpio.h" #elif defined SOC_SERIES_GD32H75e #include "gd32h75e_gpio.h" @@ -41,7 +43,7 @@ extern "C" { #define __GD32_PORT(port) GPIO##port -#if defined SOC_SERIES_GD32F4xx || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x || defined SOC_SERIES_GD32H75E || defined SOC_SERIES_GD32H7xx +#if defined SOC_SERIES_GD32F4xx || defined SOC_SERIES_GD32F5xx || defined SOC_SERIES_GD32E23x || defined SOC_SERIES_GD32H75E || (defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H77x_H78X) #define GD32_PIN(index, port, pin) {index, RCU_GPIO##port, \ GPIO##port, GPIO_PIN_##pin, \ EXTI_SOURCE_GPIO##port, \ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_hard_i2c.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_hard_i2c.c index 4c3c668a2655..9cffc5a8093d 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_hard_i2c.c +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_hard_i2c.c @@ -11,6 +11,7 @@ */ #include "drv_hard_i2c.h" +#include "i2c_config.h" #ifdef RT_USING_I2C @@ -60,10 +61,14 @@ struct rt_i2c_bus_device i2c5; #define I2C_MASTER_RECEIVE_GD I2C_ADD_MASTER_RECEIVE #define i2c_transfer_byte_number_config_gd i2c_add_transfer_byte_number_config #define i2c_start_on_bus_gd i2c_add_start_on_bus +#define i2c_automatic_end_enable_gd i2c_add_automatic_end_enable +#define i2c_automatic_end_disable_gd i2c_add_automatic_end_disable #define I2C_MASTER_TRANSMIT_GD I2C_ADD_MASTER_TRANSMIT #define I2C_FLAG_I2CBSY_GD I2C_ADD_FLAG_I2CBSY #define I2C_FLAG_TC_GD I2C_ADD_FLAG_TC #define I2C_FLAG_TI_GD I2C_ADD_FLAG_TI +#define I2C_FLAG_TBE_GD I2C_ADD_FLAG_TBE +#define I2C_FLAG_NACK_GD I2C_ADD_FLAG_NACK #define i2c_stop_on_bus_gd i2c_add_stop_on_bus #define I2C_FLAG_STPDET_GD I2C_ADD_FLAG_STPDET #define i2c_timing_config_gd i2c_add_timing_config @@ -86,10 +91,14 @@ struct rt_i2c_bus_device i2c5; #define I2C_MASTER_RECEIVE_GD I2C_MASTER_RECEIVE #define i2c_transfer_byte_number_config_gd i2c_transfer_byte_number_config #define i2c_start_on_bus_gd i2c_start_on_bus +#define i2c_automatic_end_enable_gd i2c_automatic_end_enable +#define i2c_automatic_end_disable_gd i2c_automatic_end_disable #define I2C_MASTER_TRANSMIT_GD I2C_MASTER_TRANSMIT #define I2C_FLAG_I2CBSY_GD I2C_FLAG_I2CBSY #define I2C_FLAG_TC_GD I2C_FLAG_TC #define I2C_FLAG_TI_GD I2C_FLAG_TI +#define I2C_FLAG_TBE_GD I2C_FLAG_TBE +#define I2C_FLAG_NACK_GD I2C_FLAG_NACK #define i2c_stop_on_bus_gd i2c_stop_on_bus #define I2C_FLAG_STPDET_GD I2C_FLAG_STPDET #define i2c_timing_config_gd i2c_timing_config @@ -103,91 +112,28 @@ struct rt_i2c_bus_device i2c5; #define IS_I2C_LEGACY(periph) ((periph) == I2C0 || (periph) == I2C1 || (periph) == I2C2) #elif defined (SOC_SERIES_GD32F4xx) #define IS_I2C_LEGACY(periph) (1) -#elif defined (SOC_SERIES_GD32H7xx) +#elif (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) #define IS_I2C_LEGACY(periph) (0) #endif static const struct gd32_i2c_bus gd_i2c_config[] = { #ifdef BSP_USING_HARD_I2C0 - { - I2C0, /* uart peripheral index */ - - RCU_I2C0, RCU_GPIOB, RCU_GPIOB, /* periph clock, scl gpio clock, sda gpio clock */ - - GPIOB, GPIO_AF_4, GPIO_PIN_8, /* scl port, scl alternate, scl pin */ - GPIOB, GPIO_AF_4, GPIO_PIN_9, /* sda port, sda alternate, sda pin */ - - &i2c0, - "hwi2c0", - }, + I2C0_CONFIG, #endif - #ifdef BSP_USING_HARD_I2C1 - { - I2C1, /* uart peripheral index */ - - RCU_I2C1, RCU_GPIOH, RCU_GPIOB, /* periph clock, scl gpio clock, sda gpio clock */ - - GPIOH, GPIO_AF_4, GPIO_PIN_4, /* scl port, scl alternate, scl pin */ - GPIOB, GPIO_AF_4, GPIO_PIN_11, /* sda port, sda alternate, sda pin */ - - &i2c1, - "hwi2c1", - }, + I2C1_CONFIG, #endif - #ifdef BSP_USING_HARD_I2C2 - { - I2C2, /* uart peripheral index */ - - RCU_I2C2, RCU_GPIOA, RCU_GPIOC, /* periph clock, scl gpio clock, sda gpio clock */ - - GPIOA, GPIO_AF_4, GPIO_PIN_8, /* scl port, scl alternate, scl pin */ - GPIOC, GPIO_AF_4, GPIO_PIN_9, /* sda port, sda alternate, sda pin */ - - &i2c2, - "hwi2c2", - }, + I2C2_CONFIG, #endif - #ifdef BSP_USING_HARD_I2C3 - { - I2C3, /* uart peripheral index */ - - RCU_I2C3, RCU_GPIOF, RCU_GPIOF, /* periph clock, scl gpio clock, sda gpio clock */ - - GPIOF, GPIO_AF_4, GPIO_PIN_14, /* scl port, scl alternate, scl pin */ - GPIOF, GPIO_AF_4, GPIO_PIN_15, /* sda port, sda alternate, sda pin */ - - &i2c3, - "hwi2c3", - }, + I2C3_CONFIG, #endif #ifdef BSP_USING_HARD_I2C4 - { - I2C4, /* uart peripheral index */ - - RCU_I2C4, RCU_GPIOG, RCU_GPIOG, /* periph clock, scl gpio clock, sda gpio clock */ - - GPIOG, GPIO_AF_6, GPIO_PIN_7, /* scl port, scl alternate, scl pin */ - GPIOG, GPIO_AF_6, GPIO_PIN_8, /* sda port, sda alternate, sda pin */ - - &i2c4, - "hwi2c4", - }, + I2C4_CONFIG, #endif #ifdef BSP_USING_HARD_I2C5 - { - I2C5, /* uart peripheral index */ - - RCU_I2C5, RCU_GPIOF, RCU_GPIOF, /* periph clock, scl gpio clock, sda gpio clock */ - - GPIOF, GPIO_AF_4, GPIO_PIN_11, /* scl port, scl alternate, scl pin */ - GPIOF, GPIO_AF_4, GPIO_PIN_12, /* sda port, sda alternate, sda pin */ - - &i2c5, - "hwi2c5", - } + I2C5_CONFIG, #endif }; @@ -198,29 +144,67 @@ static const struct gd32_i2c_bus gd_i2c_config[] = { */ static void gd32_i2c_gpio_init(const struct gd32_i2c_bus *i2c) { + rt_uint32_t scl_port, sda_port; + rt_uint32_t scl_pin, sda_pin; + rt_uint32_t pin_af; + rcu_periph_enum scl_periph, sda_periph; + + if(get_pin_config(i2c->scl_pin_name, &scl_port, &scl_pin, &scl_periph) != RT_EOK) + { + return; + } + + if(get_pin_config(i2c->sda_pin_name, &sda_port, &sda_pin, &sda_periph) != RT_EOK) + { + return; + } + + pin_alternate_config(i2c->alternate, &pin_af); + /* enable I2C and GPIO clock */ - rcu_periph_clock_enable(i2c->scl_gpio_clk); - rcu_periph_clock_enable(i2c->sda_gpio_clk); + rcu_periph_clock_enable(scl_periph); + rcu_periph_clock_enable(sda_periph); rcu_periph_clock_enable(i2c->per_clk); - /* configure I2C_SCL as alternate function push-pull */ - gpio_af_set(i2c->scl_port, i2c->scl_af, i2c->scl_pin); - gpio_mode_set(i2c->scl_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, i2c->scl_pin); -#if defined (SOC_SERIES_GD32H7xx) - gpio_output_options_set(i2c->scl_port, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, i2c->scl_pin); - /* configure I2C_SDA as alternate function push-pull */ - gpio_af_set(i2c->sda_port, i2c->sda_af, i2c->sda_pin); - gpio_mode_set(i2c->sda_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, i2c->sda_pin); - gpio_output_options_set(i2c->sda_port, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, i2c->sda_pin); + /* configure I2C pins as alternate function open-drain */ + gpio_af_set(scl_port, pin_af, scl_pin); + gpio_mode_set(scl_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, scl_pin); +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) + gpio_output_options_set(scl_port, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, scl_pin); + gpio_af_set(sda_port, pin_af, sda_pin); + gpio_mode_set(sda_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, sda_pin); + gpio_output_options_set(sda_port, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, sda_pin); #else - gpio_output_options_set(i2c->scl_port, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, i2c->scl_pin); - /* configure I2C_SDA as alternate function push-pull */ - gpio_af_set(i2c->sda_port, i2c->sda_af, i2c->sda_pin); - gpio_mode_set(i2c->sda_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, i2c->sda_pin); - gpio_output_options_set(i2c->sda_port, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, i2c->sda_pin); + gpio_output_options_set(scl_port, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, scl_pin); + gpio_af_set(sda_port, pin_af, sda_pin); + gpio_mode_set(sda_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, sda_pin); + gpio_output_options_set(sda_port, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, sda_pin); #endif } +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) +static void gd32_i2c_clock_source_config(rt_uint32_t i2c_periph) +{ + switch(i2c_periph) + { + case I2C0: + rcu_i2c_clock_config(IDX_I2C0, RCU_I2CSRC_IRC64MDIV); + break; + case I2C1: + rcu_i2c_clock_config(IDX_I2C1, RCU_I2CSRC_IRC64MDIV); + break; + case I2C2: + rcu_i2c_clock_config(IDX_I2C2, RCU_I2CSRC_IRC64MDIV); + break; + case I2C3: + rcu_i2c_clock_config(IDX_I2C3, RCU_I2CSRC_IRC64MDIV); + break; + default: + break; + } +} +#endif + /** * @brief read data. * @param i2c_periph @@ -228,11 +212,19 @@ static void gd32_i2c_gpio_init(const struct gd32_i2c_bus *i2c) * @param data_byte * @retval None */ -static uint8_t gd32_i2c_read(rt_uint32_t i2c_periph, rt_uint8_t *p_buffer, rt_uint16_t data_byte) +static uint8_t gd32_i2c_read(rt_uint32_t i2c_periph, rt_uint8_t *p_buffer, + rt_uint16_t data_byte, rt_tick_t timeout) { +#if defined (SOC_SERIES_GD32F5xx) || (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) + rt_tick_t start_tick; +#else + RT_UNUSED(timeout); +#endif + if (data_byte == 0) return 1; -#ifdef BSP_USING_RECEIVING_A +#if defined(BSP_USING_RECEIVING_A) && \ + (defined(SOC_SERIES_GD32F4xx) || defined(SOC_SERIES_GD32F5xx)) /* In single-byte reception, disable ACK because the master needs to send NACK after receiving the first byte,indicating no more data will be @@ -307,8 +299,20 @@ static uint8_t gd32_i2c_read(rt_uint32_t i2c_periph, rt_uint8_t *p_buffer, rt_ui }else #endif { +#if defined (SOC_SERIES_GD32F5xx) || (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) /* wait until the RBNE bit is set */ - while(!i2c_flag_get_gd(i2c_periph, I2C_FLAG_RBNE_GD)); + start_tick = rt_tick_get(); + while(!i2c_flag_get_gd(i2c_periph, I2C_FLAG_RBNE_GD)) + { + if(i2c_flag_get_gd(i2c_periph, I2C_FLAG_NACK_GD)) + { + return 1; + } + if((rt_tick_get() - start_tick) >= timeout) + { + return 1; + } + } /* read a byte */ *p_buffer = i2c_data_receive_gd(i2c_periph); @@ -316,6 +320,7 @@ static uint8_t gd32_i2c_read(rt_uint32_t i2c_periph, rt_uint8_t *p_buffer, rt_ui p_buffer++; /* decrement the read bytes counter */ data_byte--; +#endif } } return 0; @@ -328,8 +333,15 @@ static uint8_t gd32_i2c_read(rt_uint32_t i2c_periph, rt_uint8_t *p_buffer, rt_ui * @param data_byte * @retval None */ -static uint8_t gd32_i2c_write(rt_uint32_t i2c_periph, uint8_t *p_buffer, uint16_t data_byte) +static uint8_t gd32_i2c_write(rt_uint32_t i2c_periph, uint8_t *p_buffer, + uint16_t data_byte, rt_tick_t timeout) { +#if defined (SOC_SERIES_GD32F5xx) || (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) + rt_tick_t start_tick; +#else + RT_UNUSED(timeout); +#endif + if (data_byte == 0) return 1; while(data_byte) @@ -349,17 +361,36 @@ static uint8_t gd32_i2c_write(rt_uint32_t i2c_periph, uint8_t *p_buffer, uint16_ else #endif { -#if defined (SOC_SERIES_GD32F5xx) || defined (SOC_SERIES_GD32H7xx) +#if defined (SOC_SERIES_GD32F5xx) || (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) /* wait until the transmit data buffer is empty */ I2C_STAT_GD(i2c_periph) |= I2C_STAT_TBE_GD; - while(!i2c_flag_get(i2c_periph, I2C_FLAG_TBE)); + start_tick = rt_tick_get(); + while(!i2c_flag_get_gd(i2c_periph, I2C_FLAG_TBE_GD)) + { + if(i2c_flag_get_gd(i2c_periph, I2C_FLAG_NACK_GD) || + ((rt_tick_get() - start_tick) >= timeout)) + { + return 1; + } + } while(data_byte) { /* wait until the TI bit is set */ - while(!i2c_flag_get(i2c_periph, I2C_FLAG_TI_GD)); + start_tick = rt_tick_get(); + while(!i2c_flag_get_gd(i2c_periph, I2C_FLAG_TI_GD)) + { + if(i2c_flag_get_gd(i2c_periph, I2C_FLAG_NACK_GD)) + { + return 1; + } + if((rt_tick_get() - start_tick) >= timeout) + { + return 1; + } + } /* data transmission */ - i2c_data_transmit(i2c_periph, *p_buffer); + i2c_data_transmit_gd(i2c_periph, *p_buffer); /* point to the next byte to be written */ p_buffer++; /* decrement the write bytes counter */ @@ -386,30 +417,195 @@ static uint8_t gd32_i2c_write(rt_uint32_t i2c_periph, uint8_t *p_buffer, uint16_ static rt_ssize_t gd32_i2c_master_xfer(struct rt_i2c_bus_device *bus, struct rt_i2c_msg msgs[], rt_uint32_t num) { - static struct rt_i2c_msg *msg; - - rt_uint32_t i,w_total_byte=0,r_total_byte=0; - rt_err_t ret = RT_ERROR; + struct rt_i2c_msg *msg = RT_NULL; + struct gd32_i2c_bus *gd32_i2c; + rt_uint32_t i; +#if defined (SOC_SERIES_GD32F5xx) || (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) + rt_tick_t start_tick; + rt_bool_t address_probe = RT_FALSE; + rt_bool_t stop_generated = RT_FALSE; + rt_bool_t bus_started = RT_FALSE; + struct rt_i2c_msg *phase_msg; + rt_uint32_t phase_end; + rt_uint32_t phase_total_byte; +#endif + rt_err_t ret = -RT_ERROR; RT_ASSERT(bus != RT_NULL); - struct gd32_i2c_bus *gd32_i2c = (struct gd32_i2c_bus *)bus->priv; - - for(i = 0; i < num; i++) + if(msgs == RT_NULL || num == 0 || (msgs[0].flags & RT_I2C_NO_START)) { - msg = &msgs[i]; + return -RT_EINVAL; + } - if(msg->flags & RT_I2C_RD) + gd32_i2c = (struct gd32_i2c_bus *)bus->priv; + +#if defined (SOC_SERIES_GD32F5xx) || (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) + if(!IS_I2C_LEGACY(gd32_i2c->i2c_periph)) + { + phase_msg = &msgs[0]; + for(i = 1; i < num; i++) { - r_total_byte += msg->len; - }else{ - w_total_byte += msg->len; + if(msgs[i].flags & RT_I2C_NO_START) + { + if((msgs[i].addr != phase_msg->addr) || + ((msgs[i].flags & (RT_I2C_RD | RT_I2C_ADDR_10BIT)) != + (phase_msg->flags & (RT_I2C_RD | RT_I2C_ADDR_10BIT)))) + { + return -RT_EINVAL; + } + }else{ + phase_msg = &msgs[i]; + } } } +#endif - for(i = 0; i < num; i++) + for(i = 0; i < num;) { msg = &msgs[i]; +#if defined (SOC_SERIES_GD32F5xx) || (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) + if(!IS_I2C_LEGACY(gd32_i2c->i2c_periph)) + { + phase_end = i + 1; + phase_total_byte = msg->len; + while((phase_end < num) && (msgs[phase_end].flags & RT_I2C_NO_START)) + { + phase_total_byte += msgs[phase_end].len; + phase_end++; + } + + address_probe = !(msg->flags & (RT_I2C_RD | RT_I2C_NO_START | RT_I2C_NO_STOP)) && + (msg->len == 0) && (phase_end == (i + 1)); + stop_generated = RT_FALSE; + + if(!bus_started) + { + start_tick = rt_tick_get(); + while(i2c_flag_get_gd(gd32_i2c->i2c_periph, I2C_FLAG_I2CBSY_GD)) + { + if((rt_tick_get() - start_tick) >= bus->timeout) + { + LOG_E("i2c bus busy timeout"); + ret = -RT_ETIMEOUT; + goto out; + } + } + } + + { + rt_uint32_t address = msg->addr; + + if(msg->flags & RT_I2C_ADDR_10BIT) + { + /* enable 10-bit addressing mode in master mode */ + i2c_address10_enable_gd(gd32_i2c->i2c_periph); + }else{ + /* disable 10-bit addressing mode in master mode */ + i2c_address10_disable_gd(gd32_i2c->i2c_periph); + address <<= 1; + } + + if(msg->flags & RT_I2C_RD) + { + /* configure slave address */ + i2c_master_addressing_gd(gd32_i2c->i2c_periph, address, I2C_MASTER_RECEIVE_GD); + }else{ + /* configure slave address */ + i2c_master_addressing_gd(gd32_i2c->i2c_periph, address, I2C_MASTER_TRANSMIT_GD); + if(address_probe) + { + i2c_flag_clear_gd(gd32_i2c->i2c_periph, + I2C_FLAG_STPDET_GD | I2C_FLAG_NACK_GD); + i2c_automatic_end_enable_gd(gd32_i2c->i2c_periph); + } + } + + i2c_transfer_byte_number_config_gd(gd32_i2c->i2c_periph, phase_total_byte); + /* send a start condition to I2C bus */ + i2c_start_on_bus_gd(gd32_i2c->i2c_periph); + bus_started = RT_TRUE; + } + + if(address_probe) + { + rt_bool_t address_nack = RT_FALSE; + + start_tick = rt_tick_get(); + while(!i2c_flag_get_gd(gd32_i2c->i2c_periph, I2C_FLAG_STPDET_GD)) + { + if(i2c_flag_get_gd(gd32_i2c->i2c_periph, I2C_FLAG_NACK_GD)) + { + address_nack = RT_TRUE; + } + + if((rt_tick_get() - start_tick) >= bus->timeout) + { + LOG_E("i2c address probe timeout"); + i2c_automatic_end_disable_gd(gd32_i2c->i2c_periph); + ret = -RT_ETIMEOUT; + goto out; + } + } + + if(i2c_flag_get_gd(gd32_i2c->i2c_periph, I2C_FLAG_NACK_GD)) + { + address_nack = RT_TRUE; + } + i2c_automatic_end_disable_gd(gd32_i2c->i2c_periph); + i2c_flag_clear_gd(gd32_i2c->i2c_periph, + I2C_FLAG_STPDET_GD | I2C_FLAG_NACK_GD); + stop_generated = RT_TRUE; + msg = &msgs[phase_end - 1]; + i = phase_end; + if(address_nack) + { + ret = -RT_EIO; + goto out; + } + continue; + } + + while(i < phase_end) + { + msg = &msgs[i]; + if(msg->flags & RT_I2C_RD) + { + if(gd32_i2c_read(gd32_i2c->i2c_periph, msg->buf, msg->len, + bus->timeout) != 0) + { + LOG_E("i2c bus read failed,i2c bus stop!"); + goto out; + } + }else{ + if(gd32_i2c_write(gd32_i2c->i2c_periph, msg->buf, msg->len, + bus->timeout) != 0) + { + LOG_E("i2c bus write failed,i2c bus stop!"); + goto out; + } + } + i++; + } + + start_tick = rt_tick_get(); + while(!i2c_flag_get_gd(gd32_i2c->i2c_periph, I2C_FLAG_TC_GD)) + { + if(i2c_flag_get_gd(gd32_i2c->i2c_periph, I2C_FLAG_NACK_GD)) + { + ret = -RT_EIO; + goto out; + } + if((rt_tick_get() - start_tick) >= bus->timeout) + { + LOG_E("i2c transfer phase timeout"); + ret = -RT_ETIMEOUT; + goto out; + } + } + continue; + } +#endif if (!(msg->flags & RT_I2C_NO_START)) { #if defined (SOC_SERIES_GD32F5xx) || defined (SOC_SERIES_GD32F4xx) @@ -459,7 +655,6 @@ static rt_ssize_t gd32_i2c_master_xfer(struct rt_i2c_bus_device *bus, struct rt_ }else { /* configure slave address */ while(i2c_flag_get(gd32_i2c->i2c_periph, I2C_FLAG_I2CBSY)); - //i2c_transfer_byte_number_config(gd32_i2c->i2c_periph, w_total_byte); /* send a start condition to I2C bus */ i2c_start_on_bus(gd32_i2c->i2c_periph); while(!i2c_flag_get(gd32_i2c->i2c_periph, I2C_FLAG_SBSEND)); @@ -469,64 +664,27 @@ static rt_ssize_t gd32_i2c_master_xfer(struct rt_i2c_bus_device *bus, struct rt_ i2c_flag_clear(gd32_i2c->i2c_periph, I2C_FLAG_ADDSEND); } - }else -#endif - { -#if defined (SOC_SERIES_GD32F5xx) || defined (SOC_SERIES_GD32H7xx) - if(msg->flags & RT_I2C_ADDR_10BIT) - { - /* enable 10-bit addressing mode in master mode */ - i2c_address10_enable_gd(gd32_i2c->i2c_periph); - }else { - /* disable 10-bit addressing mode in master mode */ - i2c_address10_disable_gd(gd32_i2c->i2c_periph); - } - - if(msg->flags & RT_I2C_RD) - { - /* configure slave address */ - i2c_master_addressing_gd(gd32_i2c->i2c_periph, msg->addr, I2C_MASTER_RECEIVE_GD); - - i2c_transfer_byte_number_config_gd(gd32_i2c->i2c_periph, r_total_byte); - /* send a start condition to I2C bus */ - i2c_start_on_bus_gd(gd32_i2c->i2c_periph); - - }else { - /* configure slave address */ - i2c_master_addressing_gd(gd32_i2c->i2c_periph, msg->addr, I2C_MASTER_TRANSMIT_GD); - while(i2c_flag_get_gd(gd32_i2c->i2c_periph, I2C_FLAG_I2CBSY_GD)); - i2c_transfer_byte_number_config_gd(gd32_i2c->i2c_periph, w_total_byte); - /* send a start condition to I2C bus */ - i2c_start_on_bus_gd(gd32_i2c->i2c_periph); - } -#endif } +#endif } if(msg->flags & RT_I2C_RD) { - if(gd32_i2c_read(gd32_i2c->i2c_periph, msg->buf, msg->len) != 0) + if(gd32_i2c_read(gd32_i2c->i2c_periph, msg->buf, msg->len, + bus->timeout) != 0) { LOG_E("i2c bus read failed,i2c bus stop!"); goto out; } }else { - if(gd32_i2c_write(gd32_i2c->i2c_periph, msg->buf, msg->len) != 0) + if(gd32_i2c_write(gd32_i2c->i2c_periph, msg->buf, msg->len, + bus->timeout) != 0) { LOG_E("i2c bus write failed,i2c bus stop!"); goto out; } } -#if defined (SOC_SERIES_GD32F5xx) || defined (SOC_SERIES_GD32H7xx) - if(!IS_I2C_LEGACY(gd32_i2c->i2c_periph)) - { - if(r_total_byte != 0) - { - while(!i2c_flag_get_gd(gd32_i2c->i2c_periph, I2C_FLAG_TC_GD)); - } - } -#endif - + i++; } ret = i; @@ -551,16 +709,44 @@ static rt_ssize_t gd32_i2c_master_xfer(struct rt_i2c_bus_device *bus, struct rt_ }else #endif { -#if defined (SOC_SERIES_GD32F5xx) || defined (SOC_SERIES_GD32H7xx) - if(!(msg->flags & RT_I2C_NO_STOP)) +#if defined (SOC_SERIES_GD32F5xx) || (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) + if(!(msg->flags & RT_I2C_NO_STOP) && !stop_generated) { - while(!i2c_flag_get_gd(gd32_i2c->i2c_periph, I2C_FLAG_TC_GD)); + if((ret >= 0) && !address_probe) + { + start_tick = rt_tick_get(); + while(!i2c_flag_get_gd(gd32_i2c->i2c_periph, I2C_FLAG_TC_GD)) + { + if(i2c_flag_get_gd(gd32_i2c->i2c_periph, I2C_FLAG_NACK_GD)) + { + ret = -RT_EIO; + break; + } + + if((rt_tick_get() - start_tick) >= bus->timeout) + { + LOG_E("i2c transfer complete timeout"); + ret = -RT_ETIMEOUT; + break; + } + } + } /* send a stop condition to I2C bus */ i2c_stop_on_bus_gd(gd32_i2c->i2c_periph); /* wait until stop condition generate */ - while(!i2c_flag_get_gd(gd32_i2c->i2c_periph, I2C_FLAG_STPDET_GD)); + start_tick = rt_tick_get(); + while(!i2c_flag_get_gd(gd32_i2c->i2c_periph, I2C_FLAG_STPDET_GD)) + { + if((rt_tick_get() - start_tick) >= bus->timeout) + { + LOG_E("i2c stop timeout"); + ret = -RT_ETIMEOUT; + break; + } + } /* clear the STPDET bit */ - i2c_flag_clear_gd(gd32_i2c->i2c_periph, I2C_FLAG_STPDET_GD); + i2c_flag_clear_gd(gd32_i2c->i2c_periph, + I2C_FLAG_STPDET_GD | I2C_FLAG_NACK_GD); } #endif } @@ -585,7 +771,9 @@ int rt_hw_i2c_init(void) for(int i = 0; i < obj_num; i++) { - +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) + gd32_i2c_clock_source_config(gd_i2c_config[i].i2c_periph); +#endif gd32_i2c_gpio_init(&gd_i2c_config[i]); /* configure I2C timing. I2C speed clock=400kHz*/ @@ -600,12 +788,17 @@ int rt_hw_i2c_init(void) }else #endif { -#if defined (SOC_SERIES_GD32F5xx) || defined (SOC_SERIES_GD32H7xx) +#if defined (SOC_SERIES_GD32F5xx) i2c_timing_config_gd(gd_i2c_config[i].i2c_periph, 0x1, 0x7, 0); i2c_master_clock_config_gd(gd_i2c_config[i].i2c_periph, 0x2D, 0x87); /* enable I2C1 */ i2c_enable_gd(gd_i2c_config[i].i2c_periph); +#elif (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) + /* GD32H7xx vendor timing for a 64 MHz I2C kernel clock at 400 kHz. */ + i2c_timing_config_gd(gd_i2c_config[i].i2c_periph, 0x0, 0x6, 0); + i2c_master_clock_config_gd(gd_i2c_config[i].i2c_periph, 0x26, 0x73); + i2c_enable_gd(gd_i2c_config[i].i2c_periph); #endif } diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_hard_i2c.h b/bsp/gd32/arm/libraries/gd32_drivers/drv_hard_i2c.h index 1352a768ddcd..007e77605c8e 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_hard_i2c.h +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_hard_i2c.h @@ -23,19 +23,12 @@ extern "C" { /* GD32 i2c driver */ struct gd32_i2c_bus { - uint32_t i2c_periph; /* Todo: 3bits */ + uint32_t i2c_periph; + rcu_periph_enum per_clk; - rcu_periph_enum per_clk; /* Todo: 5bits */ - rcu_periph_enum scl_gpio_clk; /* Todo: 5bits */ - rcu_periph_enum sda_gpio_clk; /* Todo: 5bits */ - - uint32_t scl_port; /* Todo: 4bits */ - uint16_t scl_af; /* Todo: 4bits */ - uint16_t scl_pin; /* Todo: 4bits */ - - uint32_t sda_port; /* Todo: 4bits */ - uint16_t sda_af; /* Todo: 4bits */ - uint16_t sda_pin; /* Todo: 4bits */ + const char *scl_pin_name; + const char *sda_pin_name; + const char *alternate; struct rt_i2c_bus_device *i2c_bus; char *device_name; diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_ospi.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_ospi.c new file mode 100644 index 000000000000..a436b24f85e7 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_ospi.c @@ -0,0 +1,913 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-07-31 RT-Thread first implementation + * 2026-09-23 CYFS adapt OSPI0 QSPI flash with MDMA cache and buffer protection + */ + +#include "board.h" +#include "drv_gpio.h" +#include "drv_ospi.h" + +#ifdef BSP_USING_OSPI0 + +#include "gd32h77x_78x_ospi.h" +#include "gd32h77x_78x_ospim.h" +#ifdef BSP_OSPI0_USING_MDMA +#include "gd32h77x_78x_mdma.h" +#endif + +#define LOG_TAG "drv.ospi" +#include + +#define GD32_OSPI_FIFO_CHUNK_SIZE 16U +#define GD32_OSPI_FIFO_WORD_SIZE 4U + +#ifdef BSP_OSPI0_USING_MDMA +#define GD32_OSPI_MDMA_CHANNEL ((mdma_channel_enum)BSP_OSPI0_MDMA_CHANNEL) +#define GD32_OSPI_MDMA_BUFFER_SIZE 16U +#define GD32_OSPI_MDMA_MIN_SIZE 512U +#define GD32_OSPI_MDMA_MAX_SIZE 0x10000U +#define GD32_OSPI_MDMA_TIMEOUT_MS 1000U +#ifdef RT_USING_CACHE +#define GD32_OSPI_MDMA_ALIGNMENT 32U +#else +#define GD32_OSPI_MDMA_ALIGNMENT GD32_OSPI_MDMA_BUFFER_SIZE +#endif +#define GD32_OSPI_ITCM_START 0x00000000U +#define GD32_OSPI_ITCM_END 0x00020000U +#define GD32_OSPI_DTCM_START 0x20000000U +#define GD32_OSPI_DTCM_END 0x20040000U +#define GD32_OSPI_AHB_SRAM_START 0x30000000U +#define GD32_OSPI_AHB_SRAM_END 0x40000000U +#endif + +struct gd32_ospi_pin +{ + const char *name; + const char *alternate; +}; + +struct gd32_ospi_bus +{ + struct rt_spi_bus parent; + ospi_parameter_struct init; +}; + +static struct gd32_ospi_bus ospi0_bus; + +#ifdef BSP_OSPI0_USING_MDMA +#ifdef RT_USING_CONSOLE +static rt_bool_t ospi0_mdma_reported; +#endif + +static rt_bool_t gd32_ospi_mdma_can_transfer(const void *data, rt_size_t length) +{ + return data != RT_NULL && + ((rt_ubase_t)data & (GD32_OSPI_MDMA_ALIGNMENT - 1U)) == 0U && + length >= GD32_OSPI_MDMA_MIN_SIZE && + length <= GD32_OSPI_MDMA_MAX_SIZE && + (length & (GD32_OSPI_MDMA_ALIGNMENT - 1U)) == 0U; +} + +#ifdef RT_USING_CACHE +static void gd32_ospi_mdma_cache_ops(int ops, void *data, rt_size_t length) +{ + rt_base_t level; + + /* Keep cache maintenance atomic with respect to interrupt stack accesses. + * DMA buffers must own whole cache lines to preserve neighbouring data. */ + level = rt_hw_interrupt_disable(); + rt_hw_cpu_dcache_ops(ops, data, (int)length); + rt_hw_interrupt_enable(level); +} +#endif + +static uint32_t gd32_ospi_mdma_memory_bus(const void *data) +{ + rt_ubase_t address = (rt_ubase_t)data; + + if ((address >= GD32_OSPI_ITCM_START && address < GD32_OSPI_ITCM_END) || + (address >= GD32_OSPI_DTCM_START && address < GD32_OSPI_DTCM_END) || + (address >= GD32_OSPI_AHB_SRAM_START && address < GD32_OSPI_AHB_SRAM_END)) + { + return MDMA_SOURCE_AHB_TCM; + } + + return MDMA_SOURCE_AXI; +} + +static void gd32_ospi_mdma_report(const char *operation, rt_size_t length) +{ +#ifdef RT_USING_CONSOLE + if (!ospi0_mdma_reported && rt_console_get_device() != RT_NULL) + { + ospi0_mdma_reported = RT_TRUE; + LOG_I("ospi0 MDMA channel %d active (%s, %u bytes)", + BSP_OSPI0_MDMA_CHANNEL, operation, (rt_uint32_t)length); + } +#else + RT_UNUSED(operation); + RT_UNUSED(length); +#endif +} + +static void gd32_ospi_mdma_configure(void *data, rt_size_t length, rt_bool_t receive) +{ + mdma_parameter_struct mdma_init_struct; + uint32_t data_bus; + + data_bus = gd32_ospi_mdma_memory_bus(data); + mdma_channel_deinit(GD32_OSPI_MDMA_CHANNEL); + mdma_para_struct_init(&mdma_init_struct); + mdma_init_struct.request = MDMA_REQUEST_OSPI0_FT; + mdma_init_struct.trans_trig_mode = MDMA_BUFFER_TRANSFER; + mdma_init_struct.priority = MDMA_PRIORITY_ULTRA_HIGH; + mdma_init_struct.source_data_size = MDMA_SOURCE_DATASIZE_32BIT; + mdma_init_struct.dest_data_size = MDMA_DESTINATION_DATASIZE_32BIT; + mdma_init_struct.buff_trans_len = GD32_OSPI_MDMA_BUFFER_SIZE - 1U; + mdma_init_struct.tbytes_num_in_block = length; + + if (receive) + { + mdma_init_struct.source_inc = MDMA_SOURCE_INCREASE_DISABLE; + mdma_init_struct.dest_inc = MDMA_DESTINATION_INCREASE_32BIT; + mdma_init_struct.source_burst = MDMA_SOURCE_BURST_SINGLE; + mdma_init_struct.dest_burst = MDMA_DESTINATION_BURST_4BEATS; + mdma_init_struct.source_addr = (uint32_t)&OSPI_DATA(OSPI0); + mdma_init_struct.destination_addr = (uint32_t)data; + mdma_init_struct.source_bus = MDMA_SOURCE_AXI; + mdma_init_struct.destination_bus = data_bus == MDMA_SOURCE_AHB_TCM ? + MDMA_DESTINATION_AHB_TCM : + MDMA_DESTINATION_AXI; + } + else + { + mdma_init_struct.source_inc = MDMA_SOURCE_INCREASE_32BIT; + mdma_init_struct.dest_inc = MDMA_DESTINATION_INCREASE_DISABLE; + mdma_init_struct.source_burst = MDMA_SOURCE_BURST_4BEATS; + mdma_init_struct.dest_burst = MDMA_DESTINATION_BURST_SINGLE; + mdma_init_struct.source_addr = (uint32_t)data; + mdma_init_struct.destination_addr = (uint32_t)&OSPI_DATA(OSPI0); + mdma_init_struct.source_bus = data_bus; + mdma_init_struct.destination_bus = MDMA_DESTINATION_AXI; + } + + mdma_init(GD32_OSPI_MDMA_CHANNEL, &mdma_init_struct); +} + +static rt_bool_t gd32_ospi_mdma_has_error(void) +{ + return SET == mdma_flag_get(GD32_OSPI_MDMA_CHANNEL, MDMA_FLAG_ERR) || + SET == mdma_flag_get(GD32_OSPI_MDMA_CHANNEL, MDMA_FLAG_LDTERR) || + SET == mdma_flag_get(GD32_OSPI_MDMA_CHANNEL, MDMA_FLAG_MDTERR) || + SET == mdma_flag_get(GD32_OSPI_MDMA_CHANNEL, MDMA_FLAG_ASERR) || + SET == mdma_flag_get(GD32_OSPI_MDMA_CHANNEL, MDMA_FLAG_BZERR); +} + +static rt_err_t gd32_ospi_mdma_wait(void) +{ + rt_tick_t start; + rt_tick_t timeout; + rt_err_t result = RT_EOK; + uint32_t ospi_status; + + start = rt_tick_get(); + timeout = rt_tick_from_millisecond(GD32_OSPI_MDMA_TIMEOUT_MS); + + while (RESET == mdma_flag_get(GD32_OSPI_MDMA_CHANNEL, MDMA_FLAG_CHTCF)) + { + if (gd32_ospi_mdma_has_error() || + RESET != (OSPI_STAT(OSPI0) & OSPI_FLAG_TERR)) + { + result = -RT_EIO; + break; + } + if ((rt_tick_get() - start) >= timeout) + { + result = -RT_ETIMEOUT; + break; + } + } + + while (result == RT_EOK && RESET == (OSPI_STAT(OSPI0) & OSPI_FLAG_TC)) + { + if (RESET != (OSPI_STAT(OSPI0) & OSPI_FLAG_TERR)) + { + result = -RT_EIO; + break; + } + if ((rt_tick_get() - start) >= timeout) + { + result = -RT_ETIMEOUT; + break; + } + } + + ospi_status = OSPI_STAT(OSPI0); + ospi_mdma_disable(OSPI0); + mdma_channel_disable(GD32_OSPI_MDMA_CHANNEL); + + if (result != RT_EOK && RESET != (ospi_status & OSPI_FLAG_BUSY)) + { + OSPI_CTL(OSPI0) |= OSPI_CTL_ABORT; + start = rt_tick_get(); + while (RESET != (OSPI_STAT(OSPI0) & OSPI_FLAG_BUSY) && + (rt_tick_get() - start) < timeout) + { + } + } + + OSPI_STATC(OSPI0) = OSPI_STATC_TCC | OSPI_STATC_TERRC; + if (result != RT_EOK) + { + LOG_E("MDMA failed: %d, OSPI: %08x, MDMA: %08x/%08x, addr: %08x", + result, ospi_status, MDMA_CHXSTAT0(GD32_OSPI_MDMA_CHANNEL), + MDMA_CHXSTAT1(GD32_OSPI_MDMA_CHANNEL), + mdma_transfer_error_address_get(GD32_OSPI_MDMA_CHANNEL)); + } + mdma_channel_deinit(GD32_OSPI_MDMA_CHANNEL); + + return result; +} + +static rt_err_t gd32_ospi_mdma_transmit(const rt_uint8_t *data, rt_size_t length) +{ + gd32_ospi_mdma_configure((void *)data, length, RT_FALSE); +#ifdef RT_USING_CACHE + gd32_ospi_mdma_cache_ops(RT_HW_CACHE_FLUSH, (void *)data, length); +#endif + + mdma_channel_enable(GD32_OSPI_MDMA_CHANNEL); + OSPI_CTL(OSPI0) = (OSPI_CTL(OSPI0) & ~OSPI_CTL_FMOD) | OSPI_INDIRECT_WRITE; + ospi_mdma_enable(OSPI0); + gd32_ospi_mdma_report("write", length); + + return gd32_ospi_mdma_wait(); +} + +static rt_err_t gd32_ospi_mdma_receive(const ospi_parameter_struct *configuration, + rt_uint8_t *data, + rt_size_t length) +{ + rt_err_t result; + rt_uint32_t address; + rt_uint32_t instruction; + + address = OSPI_ADDR(OSPI0); + instruction = OSPI_INS(OSPI0); + gd32_ospi_mdma_configure(data, length, RT_TRUE); +#ifdef RT_USING_CACHE + gd32_ospi_mdma_cache_ops(RT_HW_CACHE_FLUSH | RT_HW_CACHE_INVALIDATE, + data, length); +#endif + + mdma_channel_enable(GD32_OSPI_MDMA_CHANNEL); + OSPI_CTL(OSPI0) = (OSPI_CTL(OSPI0) & ~OSPI_CTL_FMOD) | OSPI_INDIRECT_READ; + ospi_mdma_enable(OSPI0); + gd32_ospi_mdma_report("read", length); + + if (configuration->memory_type == OSPI_HYPERBUS_MEMORY_MODE) + { + OSPI_ADDR(OSPI0) = address; + } + else if ((OSPI_TCFG(OSPI0) & OSPI_TCFG_ADDRMOD) != OSPI_ADDRESS_NONE) + { + OSPI_ADDR(OSPI0) = address; + } + else + { + OSPI_INS(OSPI0) = instruction; + } + + result = gd32_ospi_mdma_wait(); +#ifdef RT_USING_CACHE + gd32_ospi_mdma_cache_ops(RT_HW_CACHE_INVALIDATE, data, length); +#endif + + return result; +} +#endif /* BSP_OSPI0_USING_MDMA */ + +static rt_err_t gd32_ospi_transmit(const rt_uint8_t *data, rt_size_t length) +{ + rt_size_t index; + +#ifdef BSP_OSPI0_USING_MDMA + if (gd32_ospi_mdma_can_transfer(data, length)) + { + return gd32_ospi_mdma_transmit(data, length); + } +#endif + + OSPI_CTL(OSPI0) = (OSPI_CTL(OSPI0) & ~OSPI_CTL_FMOD) | OSPI_INDIRECT_WRITE; + + while (length >= GD32_OSPI_FIFO_CHUNK_SIZE) + { + while (RESET == (OSPI_STAT(OSPI0) & OSPI_FLAG_FT)) + { + } + + for (index = 0; index < GD32_OSPI_FIFO_CHUNK_SIZE; index += GD32_OSPI_FIFO_WORD_SIZE) + { + OSPI_DATA(OSPI0) = (rt_uint32_t)data[index] | + ((rt_uint32_t)data[index + 1U] << 8) | + ((rt_uint32_t)data[index + 2U] << 16) | + ((rt_uint32_t)data[index + 3U] << 24); + } + data += GD32_OSPI_FIFO_CHUNK_SIZE; + length -= GD32_OSPI_FIFO_CHUNK_SIZE; + } + + if (length > 0U) + { + while (RESET == (OSPI_STAT(OSPI0) & OSPI_FLAG_FT)) + { + } + + while (length >= GD32_OSPI_FIFO_WORD_SIZE) + { + OSPI_DATA(OSPI0) = (rt_uint32_t)data[0] | + ((rt_uint32_t)data[1] << 8) | + ((rt_uint32_t)data[2] << 16) | + ((rt_uint32_t)data[3] << 24); + data += GD32_OSPI_FIFO_WORD_SIZE; + length -= GD32_OSPI_FIFO_WORD_SIZE; + } + while (length > 0U) + { + *((__IO rt_uint8_t *)&OSPI_DATA(OSPI0)) = *data++; + length--; + } + } + + while (RESET == (OSPI_STAT(OSPI0) & OSPI_FLAG_TC)) + { + } + OSPI_STATC(OSPI0) = OSPI_STATC_TCC; + + return RT_EOK; +} + +static rt_err_t gd32_ospi_receive(const ospi_parameter_struct *configuration, + rt_uint8_t *data, + rt_size_t length) +{ + rt_size_t index; + rt_uint32_t address; + rt_uint32_t instruction; + rt_uint32_t word; + +#ifdef BSP_OSPI0_USING_MDMA + if (gd32_ospi_mdma_can_transfer(data, length)) + { + return gd32_ospi_mdma_receive(configuration, data, length); + } +#endif + + address = OSPI_ADDR(OSPI0); + instruction = OSPI_INS(OSPI0); + OSPI_CTL(OSPI0) = (OSPI_CTL(OSPI0) & ~OSPI_CTL_FMOD) | OSPI_INDIRECT_READ; + + if (configuration->memory_type == OSPI_HYPERBUS_MEMORY_MODE) + { + OSPI_ADDR(OSPI0) = address; + } + else if ((OSPI_TCFG(OSPI0) & OSPI_TCFG_ADDRMOD) != OSPI_ADDRESS_NONE) + { + OSPI_ADDR(OSPI0) = address; + } + else + { + OSPI_INS(OSPI0) = instruction; + } + + while (length >= GD32_OSPI_FIFO_CHUNK_SIZE) + { + while (RESET == (OSPI_STAT(OSPI0) & OSPI_FLAG_FT)) + { + } + + for (index = 0; index < GD32_OSPI_FIFO_CHUNK_SIZE; index += GD32_OSPI_FIFO_WORD_SIZE) + { + word = OSPI_DATA(OSPI0); + data[index] = (rt_uint8_t)word; + data[index + 1U] = (rt_uint8_t)(word >> 8); + data[index + 2U] = (rt_uint8_t)(word >> 16); + data[index + 3U] = (rt_uint8_t)(word >> 24); + } + data += GD32_OSPI_FIFO_CHUNK_SIZE; + length -= GD32_OSPI_FIFO_CHUNK_SIZE; + } + + if (length > 0U) + { + while (RESET == (OSPI_STAT(OSPI0) & OSPI_FLAG_TC)) + { + } + + while (length >= GD32_OSPI_FIFO_WORD_SIZE) + { + word = OSPI_DATA(OSPI0); + data[0] = (rt_uint8_t)word; + data[1] = (rt_uint8_t)(word >> 8); + data[2] = (rt_uint8_t)(word >> 16); + data[3] = (rt_uint8_t)(word >> 24); + data += GD32_OSPI_FIFO_WORD_SIZE; + length -= GD32_OSPI_FIFO_WORD_SIZE; + } + while (length > 0U) + { + *data++ = *((__IO rt_uint8_t *)&OSPI_DATA(OSPI0)); + length--; + } + } + else + { + while (RESET == (OSPI_STAT(OSPI0) & OSPI_FLAG_TC)) + { + } + } + + OSPI_STATC(OSPI0) = OSPI_STATC_TCC; + + return RT_EOK; +} + +static const struct gd32_ospi_pin ospi0_pins[] = +{ + /* OSPI0 is wired to the board's four-line QSPI flash. */ + {BSP_OSPI0_CS_PIN, BSP_OSPI0_CS_AFIO}, + {BSP_OSPI0_CLK_PIN, BSP_OSPI0_CLK_AFIO}, + {BSP_OSPI0_D0_PIN, BSP_OSPI0_D0_AFIO}, + {BSP_OSPI0_D1_PIN, BSP_OSPI0_D1_AFIO}, + {BSP_OSPI0_D2_PIN, BSP_OSPI0_D2_AFIO}, + {BSP_OSPI0_D3_PIN, BSP_OSPI0_D3_AFIO}, +}; + +static rt_err_t gd32_ospi_gpio_init(void) +{ + rt_size_t index; + + for (index = 0; index < sizeof(ospi0_pins) / sizeof(ospi0_pins[0]); index++) + { + uint32_t port; + uint32_t pin; + uint32_t alternate; + rcu_periph_enum clock; + + if (get_pin_config(ospi0_pins[index].name, &port, &pin, &clock) != RT_EOK) + { + LOG_E("invalid OSPI0 pin: %s", ospi0_pins[index].name); + return -RT_EINVAL; + } + + if (pin_alternate_config(ospi0_pins[index].alternate, &alternate) != RT_EOK) + { + LOG_E("invalid OSPI0 alternate function: %s", ospi0_pins[index].alternate); + return -RT_EINVAL; + } + + rcu_periph_clock_enable(clock); + gpio_af_set(port, alternate, pin); + gpio_mode_set(port, GPIO_MODE_AF, GPIO_PUPD_NONE, pin); + gpio_output_options_set(port, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, pin); + } + + return RT_EOK; +} + +static rt_err_t gd32_ospi_hardware_init(void) +{ + rt_err_t result; + + rcu_ospi_clock_config(RCU_OSPI_AHB); + rcu_periph_clock_enable(RCU_OSPIM); + rcu_periph_clock_enable(RCU_OSPI0); +#ifdef BSP_OSPI0_USING_MDMA + rcu_periph_clock_enable(RCU_MDMA); +#endif + + ospim_deinit(); + ospi_deinit(OSPI0); + + result = gd32_ospi_gpio_init(); + if (result != RT_EOK) + { + return result; + } + + ospim_port_sck_config(OSPIM_PORT0, OSPIM_PORT_SCK_ENABLE); + ospim_port_dqs_config(OSPIM_PORT0, OSPIM_PORT_DQS_DISABLE); + ospim_port_csn_config(OSPIM_PORT0, OSPIM_PORT_CSN_ENABLE); + ospim_port_io3_0_config(OSPIM_PORT0, OSPIM_IO_LOW_ENABLE); + ospim_port_io7_4_config(OSPIM_PORT0, OSPIM_IO_HIGH_DISABLE); + + ospim_port_sck_source_select(OSPIM_PORT0, OSPIM_SCK_SOURCE_OSPI0_SCK); + ospim_port_csn_source_select(OSPIM_PORT0, OSPIM_CSN_SOURCE_OSPI0_CSN); + ospim_port_io3_0_source_select(OSPIM_PORT0, OSPIM_SRCPLIO_OSPI0_IO_LOW); + + return RT_EOK; +} + +static rt_bool_t gd32_ospi_width_is_valid(rt_uint8_t width) +{ + return width == 1 || width == 2 || width == 4; +} + +static rt_err_t gd32_ospi_instruction_mode(rt_uint8_t lines, uint32_t *mode) +{ + switch (lines) + { + case 0: + *mode = OSPI_INSTRUCTION_NONE; + break; + case 1: + *mode = OSPI_INSTRUCTION_1_LINE; + break; + case 2: + *mode = OSPI_INSTRUCTION_2_LINES; + break; + case 4: + *mode = OSPI_INSTRUCTION_4_LINES; + break; + case 8: + *mode = OSPI_INSTRUCTION_8_LINES; + break; + default: + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t gd32_ospi_address_mode(rt_uint8_t lines, uint32_t *mode) +{ + switch (lines) + { + case 0: + *mode = OSPI_ADDRESS_NONE; + break; + case 1: + *mode = OSPI_ADDRESS_1_LINE; + break; + case 2: + *mode = OSPI_ADDRESS_2_LINES; + break; + case 4: + *mode = OSPI_ADDRESS_4_LINES; + break; + case 8: + *mode = OSPI_ADDRESS_8_LINES; + break; + default: + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t gd32_ospi_alternate_mode(rt_uint8_t lines, uint32_t *mode) +{ + switch (lines) + { + case 0: + *mode = OSPI_ALTERNATE_BYTES_NONE; + break; + case 1: + *mode = OSPI_ALTERNATE_BYTES_1_LINE; + break; + case 2: + *mode = OSPI_ALTERNATE_BYTES_2_LINES; + break; + case 4: + *mode = OSPI_ALTERNATE_BYTES_4_LINES; + break; + case 8: + *mode = OSPI_ALTERNATE_BYTES_8_LINES; + break; + default: + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t gd32_ospi_data_mode(rt_uint8_t lines, uint32_t *mode) +{ + switch (lines) + { + case 0: + *mode = OSPI_DATA_NONE; + break; + case 1: + *mode = OSPI_DATA_1_LINE; + break; + case 2: + *mode = OSPI_DATA_2_LINES; + break; + case 4: + *mode = OSPI_DATA_4_LINES; + break; + case 8: + *mode = OSPI_DATA_8_LINES; + break; + default: + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t gd32_ospi_address_size(rt_uint8_t size, uint32_t *address_size) +{ + switch (size) + { + case 8: + *address_size = OSPI_ADDRESS_8_BITS; + break; + case 16: + *address_size = OSPI_ADDRESS_16_BITS; + break; + case 24: + *address_size = OSPI_ADDRESS_24_BITS; + break; + case 32: + *address_size = OSPI_ADDRESS_32_BITS; + break; + default: + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t gd32_ospi_alternate_size(rt_uint8_t size, uint32_t *alternate_size) +{ + switch (size) + { + case 8: + *alternate_size = OSPI_ALTERNATE_BYTES_8_BITS; + break; + case 16: + *alternate_size = OSPI_ALTERNATE_BYTES_16_BITS; + break; + case 24: + *alternate_size = OSPI_ALTERNATE_BYTES_24_BITS; + break; + case 32: + *alternate_size = OSPI_ALTERNATE_BYTES_32_BITS; + break; + default: + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t gd32_ospi_configure(struct rt_spi_device *device, + struct rt_spi_configuration *configuration) +{ + struct rt_qspi_device *qspi_device; + struct rt_qspi_configuration *qspi_configuration; + struct rt_spi_configuration *spi_configuration; + struct gd32_ospi_bus *ospi_bus; + uint32_t source_hz; + uint32_t divider; + uint32_t medium_size; + uint32_t address_bits; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(configuration != RT_NULL); + + qspi_device = (struct rt_qspi_device *)device; + qspi_configuration = &qspi_device->config; + spi_configuration = &qspi_configuration->parent; + ospi_bus = (struct gd32_ospi_bus *)device->bus->parent.user_data; + + if (spi_configuration->data_width != 8 || + (spi_configuration->mode & (RT_SPI_CPOL | RT_SPI_CPHA)) != RT_SPI_MODE_0 || + (spi_configuration->mode & RT_SPI_MSB) == 0 || + spi_configuration->max_hz == 0 || + !gd32_ospi_width_is_valid(qspi_configuration->qspi_dl_width)) + { + return -RT_EINVAL; + } + + medium_size = qspi_configuration->medium_size; + if (medium_size < 2 || (medium_size & (medium_size - 1U)) != 0) + { + return -RT_EINVAL; + } + + source_hz = rcu_clock_freq_get(CK_AHB); + divider = source_hz / spi_configuration->max_hz; + if ((source_hz % spi_configuration->max_hz) != 0) + { + divider++; + } + if (divider == 0) + { + divider = 1; + } + if (divider > 256) + { + LOG_E("requested OSPI0 clock is too low: %u Hz", spi_configuration->max_hz); + return -RT_EINVAL; + } + + address_bits = 0; + while (medium_size > 1) + { + address_bits++; + medium_size >>= 1; + } + + ospi_disable(OSPI0); + ospi_struct_init(&ospi_bus->init); + ospi_bus->init.prescaler = divider - 1U; + ospi_bus->init.sample_shift = qspi_configuration->ddr_mode ? + OSPI_SAMPLE_SHIFTING_NONE : + OSPI_SAMPLE_SHIFTING_HALF_CYCLE; + ospi_bus->init.fifo_threshold = OSPI_FIFO_THRESHOLD_16; + ospi_bus->init.device_size = OSPI_MESZ(address_bits - 1U); + ospi_bus->init.cs_hightime = OSPI_CS_HIGH_TIME_3_CYCLE; + ospi_bus->init.memory_type = OSPI_STANDARD_MODE; + ospi_bus->init.delay_hold_cycle = OSPI_DELAY_HOLD_NONE; + ospi_init(OSPI0, &ospi_bus->init); + ospi_enable(OSPI0); + + LOG_D("OSPI0 configured at %u Hz", source_hz / divider); + return RT_EOK; +} + +static rt_ssize_t gd32_ospi_xfer(struct rt_spi_device *device, + struct rt_spi_message *message) +{ + struct rt_qspi_device *qspi_device; + struct rt_qspi_message *qspi_message; + struct gd32_ospi_bus *ospi_bus; + ospi_regular_cmd_struct command = {0}; + rt_uint8_t max_width; + rt_bool_t ddr_mode; + rt_err_t result; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(message != RT_NULL); + + qspi_device = (struct rt_qspi_device *)device; + qspi_message = (struct rt_qspi_message *)message; + ospi_bus = (struct gd32_ospi_bus *)device->bus->parent.user_data; + max_width = qspi_device->config.qspi_dl_width; + ddr_mode = qspi_device->config.ddr_mode != 0; + + if (qspi_message->instruction.qspi_lines > max_width || + qspi_message->address.qspi_lines > max_width || + qspi_message->alternate_bytes.qspi_lines > max_width || + qspi_message->qspi_data_lines > max_width || + qspi_message->dummy_cycles > 31 || + (message->send_buf != RT_NULL && message->recv_buf != RT_NULL)) + { + return -RT_EINVAL; + } + + if ((message->length != 0 && qspi_message->qspi_data_lines == 0) || + (message->length == 0 && qspi_message->qspi_data_lines != 0) || + (message->length != 0 && message->send_buf == RT_NULL && message->recv_buf == RT_NULL)) + { + return -RT_EINVAL; + } + + command.operation_type = OSPI_OPTYPE_COMMON_CFG; + command.device_id = OSPI_DEVICE0_SELECT; + command.instruction = qspi_message->instruction.content; + command.ins_size = OSPI_INSTRUCTION_8_BITS; + command.address = qspi_message->address.content; + command.alter_bytes = qspi_message->alternate_bytes.content; + command.nbdata = message->length; + command.dummy_cycles = qspi_message->dummy_cycles; + command.sioo_mode = OSPI_SIOO_INST_EVERY_CMD; + + if (gd32_ospi_instruction_mode(qspi_message->instruction.qspi_lines, &command.ins_mode) != RT_EOK || + gd32_ospi_address_mode(qspi_message->address.qspi_lines, &command.addr_mode) != RT_EOK || + gd32_ospi_alternate_mode(qspi_message->alternate_bytes.qspi_lines, &command.alter_bytes_mode) != RT_EOK || + gd32_ospi_data_mode(qspi_message->qspi_data_lines, &command.data_mode) != RT_EOK) + { + return -RT_EINVAL; + } + + if (qspi_message->address.qspi_lines != 0 && + gd32_ospi_address_size(qspi_message->address.size, &command.addr_size) != RT_EOK) + { + return -RT_EINVAL; + } + + if (qspi_message->alternate_bytes.qspi_lines != 0 && + gd32_ospi_alternate_size(qspi_message->alternate_bytes.size, &command.alter_bytes_size) != RT_EOK) + { + return -RT_EINVAL; + } + + command.ins_dtr_mode = ddr_mode && command.ins_mode != OSPI_INSTRUCTION_NONE ? + OSPI_INSDTR_MODE_ENABLE : OSPI_INSDTR_MODE_DISABLE; + command.addr_dtr_mode = ddr_mode && command.addr_mode != OSPI_ADDRESS_NONE ? + OSPI_ADDRDTR_MODE_ENABLE : OSPI_ADDRDTR_MODE_DISABLE; + command.alter_bytes_dtr_mode = ddr_mode && command.alter_bytes_mode != OSPI_ALTERNATE_BYTES_NONE ? + OSPI_ABDTR_MODE_ENABLE : OSPI_ABDTR_MODE_DISABLE; + command.data_dtr_mode = ddr_mode && command.data_mode != OSPI_DATA_NONE ? + OSPI_DADTR_MODE_ENABLE : OSPI_DADTR_MODE_DISABLE; + command.dqs_mode = ddr_mode && command.data_mode != OSPI_DATA_NONE ? + OSPI_DQS_ENABLE : OSPI_DQS_DISABLE; + + ospi_command_config(OSPI0, &ospi_bus->init, &command); + + if (message->length == 0) + { + return 1; + } + + if (message->send_buf != RT_NULL) + { + result = gd32_ospi_transmit((const rt_uint8_t *)message->send_buf, + message->length); + return result == RT_EOK ? message->length : result; + } + + if (message->recv_buf != RT_NULL) + { + result = gd32_ospi_receive(&ospi_bus->init, + (rt_uint8_t *)message->recv_buf, + message->length); + return result == RT_EOK ? message->length : result; + } + + return 1; +} + +static const struct rt_spi_ops gd32_ospi_ops = +{ + .configure = gd32_ospi_configure, + .xfer = gd32_ospi_xfer, +}; + +rt_err_t rt_hw_ospi_device_attach(const char *bus_name, + const char *device_name, + rt_uint8_t data_line_width, + void (*enter_ospi_mode)(struct rt_qspi_device *device), + void (*exit_ospi_mode)(struct rt_qspi_device *device)) +{ + struct rt_qspi_device *device; + rt_err_t result; + + if (bus_name == RT_NULL || device_name == RT_NULL || + !gd32_ospi_width_is_valid(data_line_width)) + { + return -RT_EINVAL; + } + + device = (struct rt_qspi_device *)rt_calloc(1, sizeof(struct rt_qspi_device)); + if (device == RT_NULL) + { + return -RT_ENOMEM; + } + + device->config.qspi_dl_width = data_line_width; + device->enter_qspi_mode = enter_ospi_mode; + device->exit_qspi_mode = exit_ospi_mode; + + result = rt_spi_bus_attach_device_cspin(&device->parent, device_name, + bus_name, PIN_NONE, RT_NULL); + if (result != RT_EOK) + { + rt_free(device); + } + + return result; +} + +static int rt_hw_ospi_init(void) +{ + rt_err_t result; + + result = gd32_ospi_hardware_init(); + if (result != RT_EOK) + { + return result; + } + + ospi0_bus.parent.parent.user_data = &ospi0_bus; + result = rt_qspi_bus_register(&ospi0_bus.parent, "ospi0", &gd32_ospi_ops); + if (result == RT_EOK) + { + LOG_D("ospi0 bus registered"); + } + + return result; +} +INIT_BOARD_EXPORT(rt_hw_ospi_init); + +#endif /* BSP_USING_OSPI0 */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_ospi.h b/bsp/gd32/arm/libraries/gd32_drivers/drv_ospi.h new file mode 100644 index 000000000000..c71c257ab43f --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_ospi.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-07-31 RT-Thread first implementation + */ + +#ifndef __DRV_OSPI_H__ +#define __DRV_OSPI_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +rt_err_t rt_hw_ospi_device_attach(const char *bus_name, + const char *device_name, + rt_uint8_t data_line_width, + void (*enter_ospi_mode)(struct rt_qspi_device *device), + void (*exit_ospi_mode)(struct rt_qspi_device *device)); + +#ifdef __cplusplus +} +#endif + +#endif /* __DRV_OSPI_H__ */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_pwm.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_pwm.c index 0b50528c4d04..a4a89980fbba 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_pwm.c +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_pwm.c @@ -34,6 +34,8 @@ typedef struct{ uint32_t gpio_port; uint32_t gpio_af; uint16_t gpio_pin; + const char *pin_name; + const char *alternate; }channel_type; struct gd32_pwm @@ -86,10 +88,20 @@ static struct gd32_pwm gd32_pwm_obj[] = { .timerx = TIMER2, .apb_of = CK_APB1, .channels = { +#if defined(BSP_PWM2_CH1_PIN) && defined(BSP_PWM2_CH1_AFIO) + {0}, + { + .pin_name = BSP_PWM2_CH1_PIN, + .alternate = BSP_PWM2_CH1_AFIO, + }, + {0}, + {0}, +#else {GPIOA, GPIO_AF_1, GPIO_PIN_6}, {GPIOA, GPIO_AF_1, GPIO_PIN_7}, {GPIOB, GPIO_AF_2, GPIO_PIN_0}, {GPIOB, GPIO_AF_2, GPIO_PIN_1}, +#endif }, }, #endif @@ -208,6 +220,22 @@ static struct gd32_pwm gd32_pwm_obj[] = { }, // L2 general-purpose timer is a single-channel timer }, #endif + +#ifdef BSP_USING_PWM30 + { + .name = "pwm30", + .timerx = TIMER30, + .apb_of = CK_APB1, + .channels = { + {0}, + {0}, + { + .pin_name = BSP_PWM30_CH2_PIN, + .alternate = BSP_PWM30_CH2_AFIO, + }, + }, + }, +#endif }; static void gpio_clock_enable(rt_uint32_t Port) @@ -241,6 +269,43 @@ static void gpio_clock_enable(rt_uint32_t Port) } } +static rt_err_t channel_pin_resolve(channel_type *channel) +{ + uint32_t gpio_port; + uint32_t gpio_pin; + uint32_t gpio_af; + rcu_periph_enum gpio_periph; + + if (channel->pin_name == RT_NULL) + { + if (channel->gpio_port != 0U) + { + gpio_clock_enable(channel->gpio_port); + } + + return RT_EOK; + } + + if (get_pin_config(channel->pin_name, &gpio_port, &gpio_pin, &gpio_periph) != RT_EOK) + { + LOG_E("Invalid PWM pin: %s", channel->pin_name); + return -RT_EINVAL; + } + + if (pin_alternate_config(channel->alternate, &gpio_af) != RT_EOK) + { + LOG_E("Invalid PWM alternate function: %s", channel->alternate); + return -RT_EINVAL; + } + + channel->gpio_port = gpio_port; + channel->gpio_pin = (uint16_t)gpio_pin; + channel->gpio_af = gpio_af; + rcu_periph_clock_enable(gpio_periph); + + return RT_EOK; +} + static void timer_clock_enable(uint32_t timer) { switch (timer) @@ -269,69 +334,67 @@ static void timer_clock_enable(uint32_t timer) case TIMER7: rcu_periph_clock_enable(RCU_TIMER7); break; +#ifdef TIMER8 case TIMER8: rcu_periph_clock_enable(RCU_TIMER8); break; +#endif +#ifdef TIMER9 case TIMER9: rcu_periph_clock_enable(RCU_TIMER9); break; +#endif +#ifdef TIMER10 case TIMER10: rcu_periph_clock_enable(RCU_TIMER10); break; +#endif +#ifdef TIMER11 case TIMER11: rcu_periph_clock_enable(RCU_TIMER11); break; +#endif +#ifdef TIMER12 case TIMER12: rcu_periph_clock_enable(RCU_TIMER12); break; +#endif +#ifdef TIMER13 case TIMER13: rcu_periph_clock_enable(RCU_TIMER13); break; +#endif +#ifdef TIMER30 + case TIMER30: + rcu_periph_clock_enable(RCU_TIMER30); + break; +#endif default: LOG_E("Unsport timer periph!\n"); } } -static void rcu_config(void) +static rt_err_t rcu_config(void) { - rt_int16_t i; + rt_size_t i; + rt_size_t j; for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i) { - /* enable GPIO clock */ - switch (gd32_pwm_obj[i].timerx) + for (j = 0; j < sizeof(gd32_pwm_obj[i].channels) / sizeof(gd32_pwm_obj[i].channels[0]); ++j) { - /* Advanced timer */ - case TIMER0: - case TIMER7: - gpio_clock_enable(gd32_pwm_obj[i].nchannels[0].gpio_port); - gpio_clock_enable(gd32_pwm_obj[i].nchannels[1].gpio_port); - gpio_clock_enable(gd32_pwm_obj[i].nchannels[2].gpio_port); - - /* L0 general-purpose timer */ - case TIMER1: - case TIMER2: - case TIMER3: - case TIMER4: - gpio_clock_enable(gd32_pwm_obj[i].channels[2].gpio_port); - gpio_clock_enable(gd32_pwm_obj[i].channels[3].gpio_port); - - /* L1 general-purpose timer */ - case TIMER8: - case TIMER11: - gpio_clock_enable(gd32_pwm_obj[i].channels[1].gpio_port); - - /* L2 general-purpose timer */ - case TIMER9: - case TIMER10: - case TIMER12: - case TIMER13: - gpio_clock_enable(gd32_pwm_obj[i].channels[0].gpio_port); - break; - - default: - LOG_E("Unsport timer periph at rcu_config!\n"); - break; + if (channel_pin_resolve(&gd32_pwm_obj[i].channels[j]) != RT_EOK) + { + return -RT_ERROR; + } + } + + for (j = 0; j < sizeof(gd32_pwm_obj[i].nchannels) / sizeof(gd32_pwm_obj[i].nchannels[0]); ++j) + { + if (channel_pin_resolve(&gd32_pwm_obj[i].nchannels[j]) != RT_EOK) + { + return -RT_ERROR; + } } } @@ -341,79 +404,72 @@ static void rcu_config(void) timer_clock_enable(gd32_pwm_obj[i].timerx); timer_deinit(gd32_pwm_obj[i].timerx); } + + return RT_EOK; } -/** - * @brief Configure PWM output pin to PWM output mode - * @param pwm PWM object - * @param configuration Configuration information passed by PWM driver framework - */ -static void gpio_config_pwmout(const struct gd32_pwm *pwm, - const struct rt_pwm_configuration *configuration) +static const channel_type *pwm_channel_get(const struct gd32_pwm *pwm, + const struct rt_pwm_configuration *configuration) { - channel_type channel; + const channel_type *channel; uint8_t channel_num = configuration->channel; - if(channel_num == 0) + + if (channel_num == 0U) { - LOG_E("PWM channel starts from 1!\n"); - return; + LOG_E("PWM channel starts from 1"); + return RT_NULL; } - if(configuration->complementary) + + if (configuration->complementary) { - if(channel_num > 3) + if (channel_num > sizeof(pwm->nchannels) / sizeof(pwm->nchannels[0])) { - LOG_E("GD32 PWM complementary channel max 3!\n"); - return; - }; - channel = pwm->nchannels[channel_num-1]; + LOG_E("GD32 PWM complementary channel max 3"); + return RT_NULL; + } + channel = &pwm->nchannels[channel_num - 1U]; } else { - if(channel_num > 4) + if (channel_num > sizeof(pwm->channels) / sizeof(pwm->channels[0])) { - LOG_E("GD32 PWM complementary channel max 4!\n"); - return; - }; - channel = pwm->channels[channel_num-1]; + LOG_E("GD32 PWM channel max 4"); + return RT_NULL; + } + channel = &pwm->channels[channel_num - 1U]; + } + + if (channel->gpio_port == 0U || channel->gpio_pin == 0U) + { + LOG_E("%s channel %d is not configured", pwm->name, channel_num); + return RT_NULL; } - gpio_mode_set(channel.gpio_port, GPIO_MODE_AF, GPIO_PUPD_NONE, channel.gpio_pin); - gpio_output_options_set(channel.gpio_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, channel.gpio_pin); - gpio_af_set(channel.gpio_port, channel.gpio_af, channel.gpio_pin); + + return channel; +} + +/** + * @brief Configure PWM output pin to PWM output mode + * @param channel PWM channel + */ +static void gpio_config_pwmout(const channel_type *channel) +{ + gpio_mode_set(channel->gpio_port, GPIO_MODE_AF, GPIO_PUPD_NONE, channel->gpio_pin); +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined(SOC_SERIES_GD32H75E) + gpio_output_options_set(channel->gpio_port, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, channel->gpio_pin); +#else + gpio_output_options_set(channel->gpio_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, channel->gpio_pin); +#endif + gpio_af_set(channel->gpio_port, channel->gpio_af, channel->gpio_pin); } /** * @brief Configure PWM output pin to PWM floating input mode - * @param pwm PWM object - * @param configuration Configuration information passed by PWM driver framework + * @param channel PWM channel */ -static void gpio_config_input(const struct gd32_pwm *pwm, - const struct rt_pwm_configuration *configuration) +static void gpio_config_input(const channel_type *channel) { - channel_type channel = {0}; - uint8_t channel_num = configuration->channel; - if(channel_num == 0) - { - return; - } - if(configuration->complementary) - { - if(channel_num > 3) - { - LOG_E("GD32 PWM complementary channel max 3!\n"); - return; - }; - channel = pwm->nchannels[channel_num-1]; - } - else - { - if(channel_num > 4) - { - LOG_E("GD32 PWM complementary channel max 4!\n"); - return; - }; - channel = pwm->channels[channel_num-1]; - } - gpio_mode_set(channel.gpio_port, GPIO_MODE_INPUT, GPIO_PUPD_NONE, channel.gpio_pin); + gpio_mode_set(channel->gpio_port, GPIO_MODE_INPUT, GPIO_PUPD_NONE, channel->gpio_pin); } static void channel_output_config(rt_uint32_t timer_periph, timer_oc_parameter_struct *ocpara) @@ -430,6 +486,9 @@ static void channel_output_config(rt_uint32_t timer_periph, timer_oc_parameter_s case TIMER2: case TIMER3: case TIMER4: +#ifdef TIMER30 + case TIMER30: +#endif timer_channel_output_config(timer_periph, TIMER_CH_2, ocpara); timer_channel_output_pulse_value_config(timer_periph, TIMER_CH_2, 7999); timer_channel_output_mode_config(timer_periph, TIMER_CH_2, TIMER_OC_MODE_PWM0); @@ -449,8 +508,12 @@ static void channel_output_config(rt_uint32_t timer_periph, timer_oc_parameter_s timer_channel_complementary_output_state_config(timer_periph, TIMER_CH_3, TIMER_CCXN_DISABLE); /* L1 general-purpose timer */ +#ifdef TIMER8 case TIMER8: +#endif +#ifdef TIMER11 case TIMER11: +#endif timer_channel_output_config(timer_periph, TIMER_CH_1, ocpara); timer_channel_output_pulse_value_config(timer_periph, TIMER_CH_1, 7999); timer_channel_output_mode_config(timer_periph, TIMER_CH_1, TIMER_OC_MODE_PWM0); @@ -461,10 +524,18 @@ static void channel_output_config(rt_uint32_t timer_periph, timer_oc_parameter_s timer_channel_complementary_output_state_config(timer_periph, TIMER_CH_1, TIMER_CCXN_DISABLE); /* L2 general-purpose timer */ +#ifdef TIMER9 case TIMER9: +#endif +#ifdef TIMER10 case TIMER10: +#endif +#ifdef TIMER12 case TIMER12: +#endif +#ifdef TIMER13 case TIMER13: +#endif timer_channel_output_config(timer_periph, TIMER_CH_0, ocpara); timer_channel_output_pulse_value_config(timer_periph, TIMER_CH_0, 7999); timer_channel_output_mode_config(timer_periph, TIMER_CH_0, TIMER_OC_MODE_PWM0); @@ -517,32 +588,15 @@ static void timer_config(void) static rt_err_t drv_pwm_enable(struct gd32_pwm *pwm, const struct rt_pwm_configuration *configuration, rt_bool_t enable) { - uint8_t channel_num = configuration->channel; - if(channel_num == 0) + const channel_type *channel = pwm_channel_get(pwm, configuration); + + if (channel == RT_NULL) { - LOG_E("GD32 PWM channel starts from 1!\n"); return -RT_EINVAL; } - if(configuration->complementary) - { - if(channel_num > 3) - { - LOG_E("GD32 PWM complementary channel max 3!\n"); - return -RT_EINVAL; - }; - } - else - { - if(channel_num > 4) - { - LOG_E("GD32 PWM complementary channel max 4!\n"); - return -RT_EINVAL; - }; - } - if (!enable) { - gpio_config_input(pwm, configuration); + gpio_config_input(channel); if (configuration->complementary == RT_TRUE) { timer_channel_complementary_output_state_config(pwm->timerx, configuration->channel-1, @@ -556,7 +610,7 @@ static rt_err_t drv_pwm_enable(struct gd32_pwm *pwm, const struct rt_pwm_configu } else { - gpio_config_pwmout(pwm, configuration); + gpio_config_pwmout(channel); if (configuration->complementary == RT_TRUE) { timer_channel_complementary_output_state_config(pwm->timerx, configuration->channel-1, @@ -578,7 +632,7 @@ static rt_err_t drv_pwm_get(const struct gd32_pwm *pwm, struct rt_pwm_configurat rt_uint16_t psc; rt_uint32_t chxcv; - if(configuration->channel == 0) + if (pwm_channel_get(pwm, configuration) == RT_NULL) { return -RT_EINVAL; } @@ -613,7 +667,7 @@ static rt_err_t drv_pwm_set(struct gd32_pwm *pwm, struct rt_pwm_configuration *c rt_uint8_t coef = (RCU_CFG1&RCU_CFG1_TIMERSEL)?4:2; tim_clock = rcu_clock_freq_get(pwm->apb_of)*coef; - if(configuration->channel == 0) + if (pwm_channel_get(pwm, configuration) == RT_NULL) { return -RT_EINVAL; } @@ -676,7 +730,10 @@ static struct rt_pwm_ops drv_ops = {drv_pwm_control}; static rt_err_t gd32_hw_pwm_init(void) { - rcu_config(); + if (rcu_config() != RT_EOK) + { + return -RT_ERROR; + } timer_config(); /* diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_rtc.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_rtc.c index fee92af39acf..226b06160989 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_rtc.c +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_rtc.c @@ -8,6 +8,7 @@ * 2022-01-25 iysheng first version * 2025-09-25 kurisaw adapt to rt_rtc_ops interface * 2025-09-25 kurisaw add alarm interrupt support + * 2026-09-08 CYFS fix RTC startup, backup state, and alarm synchronization */ #include @@ -29,16 +30,18 @@ #error "Please Enable RTC alarm define (BSP_USING_ALARM0 | BSP_USING_ALARM1)" #elif defined(BSP_USING_ALARM0) #define BSP_ALARM_FLAG RTC_FLAG_ALARM0 + #define BSP_ALARM_WRITE_FLAG RTC_FLAG_ALARM0W #define BSP_RTC_ALARM RTC_ALARM0 #define BSP_RTC_INT_ALARM RTC_INT_ALARM0 #elif defined(BSP_USING_ALARM1) #define BSP_ALARM_FLAG RTC_FLAG_ALARM1 + #define BSP_ALARM_WRITE_FLAG RTC_FLAG_ALARM1W #define BSP_RTC_ALARM RTC_ALARM1 #define BSP_RTC_INT_ALARM RTC_INT_ALARM1 #endif #endif -#if defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32F5xx) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined(SOC_SERIES_GD32F5xx) #define rtc_year year #define rtc_month month @@ -51,9 +54,44 @@ #endif -static time_t get_rtc_timestamp(void); +#define RTC_LXTAL_TIMEOUT_MS 5000U +#define RTC_STATUS_TIMEOUT_MS 1000U +#define PRESCALER_S 0xFFU +#define PRESCALER_A 0x7FU +/* Reserve RTC_BKP0 to retain initialization state even when the year is 2000. */ +#define RTC_INIT_MARKER 0x52544301U + +static rt_err_t get_rtc_timestamp(time_t *time_stamp); static rt_err_t set_rtc_timestamp(time_t time_stamp); +static rt_err_t gd_rtc_wait_flag(uint32_t flag) +{ + rt_uint32_t remaining_ms = RTC_STATUS_TIMEOUT_MS; + + while (rtc_flag_get(flag) == RESET) + { + if (remaining_ms == 0U) + { + return -RT_ETIMEOUT; + } + rt_hw_us_delay(1000); + remaining_ms--; + } + + return RT_EOK; +} + +static rt_err_t gd_rtc_sync_wait(void) +{ + if (rtc_register_sync_wait() == SUCCESS) + { + return RT_EOK; + } + + /* Keep waiting without clearing RSYNF again if the library loop was too short. */ + return gd_rtc_wait_flag(RTC_FLAG_RSYN); +} + /** * @brief Helper function: Convert BCD value to binary. * @param val: BCD value. @@ -120,84 +158,93 @@ void RTC_Alarm_IRQHandler(void) static rt_err_t gd_rtc_init(void) { + rt_uint32_t remaining_ms = RTC_LXTAL_TIMEOUT_MS; + uint32_t clock_source; + rt_err_t result; + /* Enable PMU and backup domain clocks */ rcu_periph_clock_enable(RCU_PMU); pmu_backup_write_enable(); - /* Enable BKP and RTC clocks */ + /* Enable access to backup registers on devices with a separate BKP clock. */ #ifdef SOC_SERIES_GD32F10x rcu_periph_clock_enable(RCU_BKPI); -#else - rcu_periph_clock_enable(RCU_RTC); #endif - /* Check if RTC is already initialized by backup domain reset */ - if (RTC_STAT & RTC_STAT_INITF) - { - LOG_D("RTC already initialized"); - return RT_EOK; - } - - /* Reset backup domain only if RTC is not running */ - if ((RTC_STAT & RTC_STAT_RSYNF) == 0) + /* A backup reset is needed only when changing an already selected source. */ + clock_source = RCU_BDCTL & RCU_BDCTL_RTCSRC; + if ((clock_source != RCU_RTCSRC_NONE) && (clock_source != RCU_RTCSRC_LXTAL)) { + LOG_W("RTC clock source changed; resetting backup domain"); rcu_bkp_reset_enable(); rcu_bkp_reset_disable(); - - /* Re-enable clocks after reset */ #ifdef SOC_SERIES_GD32F10x rcu_periph_clock_enable(RCU_BKPI); -#else - rcu_periph_clock_enable(RCU_RTC); #endif } - /* Use LSE (32.768kHz) as RTC clock source */ -#define PRESCALER_S 0xFF -#define PRESCALER_A 0x7F - + /* Use a timed wait: a fixed CPU loop can expire before the crystal starts. */ rcu_osci_on(RCU_LXTAL); - if (rcu_osci_stab_wait(RCU_LXTAL) != SUCCESS) + while (rcu_flag_get(RCU_FLAG_LXTALSTB) == RESET) { - LOG_E("LSE oscillator failed to stabilize"); - return -RT_ERROR; + if (remaining_ms == 0U) + { + LOG_E("LXTAL failed to stabilize within %u ms, RCU_BDCTL=0x%08x", + RTC_LXTAL_TIMEOUT_MS, (unsigned int)RCU_BDCTL); + return -RT_ETIMEOUT; + } + rt_hw_us_delay(1000); + remaining_ms--; } rcu_rtc_clock_config(RCU_RTCSRC_LXTAL); + rcu_periph_clock_enable(RCU_RTC); LOG_D("RTC clock source: LSE (32.768kHz)"); + /* Recover an interrupted initialization without erasing the calendar. */ + RTC_WPK = RTC_UNLOCK_KEY1; + RTC_WPK = RTC_UNLOCK_KEY2; + rtc_init_mode_exit(); + RTC_WPK = RTC_LOCK_KEY; + /* Wait for RTC registers synchronization */ - if (rtc_register_sync_wait() != SUCCESS) + result = gd_rtc_sync_wait(); + if (result != RT_EOK) { - LOG_E("RTC register synchronization failed"); - return -RT_ERROR; + LOG_E("RTC register synchronization timed out, RTC_STAT=0x%08x", + (unsigned int)RTC_STAT); + return result; } - /* Set default time if RTC is not initialized */ - if ((RTC_DATE == 0) || (RTC_TIME == 0)) + /* INITF means init mode, and 00:00:00 is a valid time, not an empty RTC. */ + if ((RTC_BKP0 != RTC_INIT_MARKER) && (rtc_flag_get(RTC_FLAG_YCM) == RESET)) { time_t default_time = 1704067200; /* 2024-01-01 00:00:00 */ - if (set_rtc_timestamp(default_time) != RT_EOK) + result = set_rtc_timestamp(default_time); + if (result != RT_EOK) { LOG_E("Failed to set default RTC time"); - return -RT_ERROR; + return result; } LOG_D("RTC set to default time: 2024-01-01 00:00:00"); } + RTC_BKP0 = RTC_INIT_MARKER; LOG_D("RTC initialization successful"); return RT_EOK; } -static time_t get_rtc_timestamp(void) +static rt_err_t get_rtc_timestamp(time_t *time_stamp) { struct tm tm_new = {0}; rtc_parameter_struct rtc_current_time; + rt_err_t result; /* Wait for register synchronization before reading */ - if (rtc_register_sync_wait() != SUCCESS) + result = gd_rtc_sync_wait(); + if (result != RT_EOK) { LOG_E("RTC sync failed before reading time"); - return 0; + return result; } rtc_current_time_get(&rtc_current_time); @@ -219,35 +266,40 @@ static time_t get_rtc_timestamp(void) tm_new.tm_isdst = 0; /* No daylight saving */ /* Use timegm instead of mktime to avoid timezone issues */ - return timegm(&tm_new); + *time_stamp = timegm(&tm_new); + return RT_EOK; } static rt_err_t gd_get_secs(time_t *sec) { + rt_err_t result; + if (sec == RT_NULL) { return -RT_EINVAL; } - *sec = get_rtc_timestamp(); - LOG_D("RTC: get timestamp %lu", *sec); + result = get_rtc_timestamp(sec); + if (result == RT_EOK) + { + LOG_D("RTC: get timestamp %lu", *sec); + } - return RT_EOK; + return result; } static rt_err_t set_rtc_timestamp(time_t time_stamp) { struct tm now; rtc_parameter_struct rtc_init_struct; - ErrStatus status; + rt_err_t result; /* Use gmtime_r for thread safety */ - gmtime_r(&time_stamp, &now); - - if (now.tm_year < 100) + if ((gmtime_r(&time_stamp, &now) == RT_NULL) || + (now.tm_year < 100) || (now.tm_year > 199)) { - LOG_E("Year must be >= 2000"); - return -RT_ERROR; + LOG_E("Year must be between 2000 and 2099"); + return -RT_EINVAL; } /* Convert to BCD format */ @@ -268,19 +320,31 @@ static rt_err_t set_rtc_timestamp(time_t time_stamp) rtc_init_struct.factor_syn = PRESCALER_S; rtc_init_struct.am_pm = RTC_AM; - status = rtc_init(&rtc_init_struct); - if (status != SUCCESS) + /* Enter init mode with a timed wait before the library's fixed-loop check. */ + RTC_WPK = RTC_UNLOCK_KEY1; + RTC_WPK = RTC_UNLOCK_KEY2; + RTC_STAT |= RTC_STAT_INITM; + result = gd_rtc_wait_flag(RTC_FLAG_INIT); + if (result != RT_EOK) { - LOG_E("RTC time set failed: %d", status); - return -RT_ERROR; + rtc_init_mode_exit(); + RTC_WPK = RTC_LOCK_KEY; + LOG_E("RTC init mode timed out, RTC_STAT=0x%08x", (unsigned int)RTC_STAT); + return result; } - /* Wait for synchronization after setting time */ - if (rtc_register_sync_wait() != SUCCESS) + /* INITF is already set; rtc_init() can now fail only on its final sync wait. */ + if (rtc_init(&rtc_init_struct) != SUCCESS) { - LOG_E("RTC sync failed after setting time"); - return -RT_ERROR; + result = gd_rtc_wait_flag(RTC_FLAG_RSYN); + if (result != RT_EOK) + { + LOG_E("RTC sync timed out after setting time, RTC_STAT=0x%08x", + (unsigned int)RTC_STAT); + return result; + } } + RTC_BKP0 = RTC_INIT_MARKER; LOG_D("RTC time set successfully: %lu", time_stamp); return RT_EOK; @@ -336,18 +400,43 @@ static rt_err_t gd_get_alarm(struct rt_rtc_wkalarm *alarm) static rt_err_t gd_set_alarm(struct rt_rtc_wkalarm *alarm) { + rtc_alarm_struct rtc_alarm; + rt_err_t result; + if (alarm == RT_NULL) { return -RT_EINVAL; } - rtc_alarm_struct rtc_alarm; + if (alarm->enable && (((rt_uint32_t)alarm->tm_hour > 23U) || + ((rt_uint32_t)alarm->tm_min > 59U) || ((rt_uint32_t)alarm->tm_sec > 59U))) + { + return -RT_EINVAL; + } + + rtc_interrupt_disable(BSP_RTC_INT_ALARM); + if (rtc_alarm_disable(BSP_RTC_ALARM) != SUCCESS) + { + result = gd_rtc_wait_flag(BSP_ALARM_WRITE_FLAG); + if (result != RT_EOK) + { + LOG_E("RTC alarm registers are not writable: %d", result); + return result; + } + } + rtc_flag_clear(BSP_ALARM_FLAG); + exti_flag_clear(EXTI_17); + + if (!alarm->enable) + { + LOG_D("RTC alarm disabled"); + return RT_EOK; + } - rtc_alarm_disable(BSP_RTC_ALARM); /* Initialize alarm structure */ - rtc_alarm.alarm_mask = RTC_ALARM_ALL_MASK; + rtc_alarm.alarm_mask = RTC_ALARM_DATE_MASK; rtc_alarm.weekday_or_date = RTC_ALARM_DATE_SELECTED; - rtc_alarm.alarm_day = 1; /* Not used when mask is set to ALL_MASK */ + rtc_alarm.alarm_day = 1; /* The RT-Thread Alarm service checks the full date. */ rtc_alarm.alarm_hour = bin_to_bcd(alarm->tm_hour); rtc_alarm.alarm_minute = bin_to_bcd(alarm->tm_min); rtc_alarm.alarm_second = bin_to_bcd(alarm->tm_sec); @@ -355,44 +444,13 @@ static rt_err_t gd_set_alarm(struct rt_rtc_wkalarm *alarm) /* Configure alarm */ rtc_alarm_config(BSP_RTC_ALARM, &rtc_alarm); + rtc_alarm_subsecond_config(BSP_RTC_ALARM, RTC_MSKSSC_0_14, 0); - /* Enable or disable alarm */ - if (alarm->enable) - { - /* Clear any pending alarm flag first */ - rtc_flag_clear(BSP_ALARM_FLAG); - - /* Clear EXTI line 17 flag */ - exti_flag_clear(EXTI_17); - - /* Enable RTC alarm interrupt */ - rtc_interrupt_enable(BSP_RTC_INT_ALARM); - - /* Enable alarm */ - rtc_alarm_enable(BSP_RTC_ALARM); - - /* Configure EXTI line 17 for RTC alarm interrupt */ - exti_init(EXTI_17, EXTI_INTERRUPT, EXTI_TRIG_RISING); - - /* Enable RTC Alarm global interrupt in NVIC */ - nvic_irq_enable(RTC_Alarm_IRQn, 0, 0); - - LOG_D("RTC Alarm enabled with interrupt"); - } - else - { - /* Disable alarm interrupt first */ - rtc_interrupt_disable(BSP_RTC_INT_ALARM); - - /* Disable alarm */ - rtc_alarm_disable(BSP_RTC_ALARM); - - /* Clear alarm flag */ - rtc_flag_clear(BSP_ALARM_FLAG); - exti_flag_clear(EXTI_17); - - LOG_D("RTC Alarm disabled"); - } + /* Route the interrupt before enabling the comparator. */ + exti_init(EXTI_17, EXTI_INTERRUPT, EXTI_TRIG_RISING); + nvic_irq_enable(RTC_Alarm_IRQn, 0, 0); + rtc_interrupt_enable(BSP_RTC_INT_ALARM); + rtc_alarm_enable(BSP_RTC_ALARM); LOG_D("RTC: set alarm %02d:%02d:%02d, enable: %d", alarm->tm_hour, alarm->tm_min, alarm->tm_sec, alarm->enable); diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_sdio.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_sdio.c index 813c0b6a4adf..c90f7a217f56 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_sdio.c +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_sdio.c @@ -6,6 +6,7 @@ * Change Logs: * Date Author Notes * 2022-07-20 jiezhi320 the first version + * 2026-09-01 CYFS add card-detect support and fix the GD32H77x SDIO clock */ #include #include @@ -13,6 +14,10 @@ #ifdef RT_USING_SDIO #include "drv_sdio.h" +#ifdef GD32_SDIO_USING_H77X +#include "drv_gpio.h" +#include "sdio_config.h" +#endif //#define DRV_DEBUG @@ -87,10 +92,34 @@ #define SD_MAX_DATA_LENGTH ((uint32_t)0x01FFFFFF) /* the maximum length of data */ #define SD_ALLZERO ((uint32_t)0x00000000) /* all zero */ #define SD_RCA_SHIFT ((uint8_t)0x10) /* RCA shift bits */ +#ifdef GD32_SDIO_USING_H77X +#define SD_CLK_DIV_INIT ((uint16_t)500U) /* 300 MHz / (2 * 500) = 300 kHz */ +#define SD_CLK_DIV_TRANS ((uint16_t)8U) /* 300 MHz / (2 * 8) = 18.75 MHz */ +#define SDIO_POLLING_TIMEOUT ((uint32_t)4000000U) +#define SDIO_MASK_INTC_FLAGS ((uint32_t)0x1FE00FFF) +#define SDIO_MASK_CMD_FLAGS ((uint32_t)0x002000C5) +#define SDIO_MASK_DATA_FLAGS ((uint32_t)0x18000F3A) +#define SDIO_READ_SINGLE_FLAGS (SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_RXORE | \ + SDIO_FLAG_DTBLKEND | SDIO_FLAG_DTEND) +#define SDIO_READ_MULTI_FLAGS SDIO_READ_SINGLE_FLAGS +#define SDIO_WRITE_SINGLE_FLAGS (SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_TXURE | \ + SDIO_FLAG_DTBLKEND | SDIO_FLAG_DTEND) +#define SDIO_WRITE_MULTI_FLAGS SDIO_WRITE_SINGLE_FLAGS +#else #define SD_CLK_DIV_INIT ((uint16_t)0x0076) /* SD clock division in initialization phase */ #define SD_CLK_DIV_TRANS ((uint16_t)0x0002) /* SD clock division in transmission phase */ - #define SDIO_MASK_INTC_FLAGS ((uint32_t)0x00C007FF) /* mask flags of SDIO_INTC */ +#define SDIO_MASK_CMD_FLAGS SDIO_MASK_INTC_FLAGS +#define SDIO_MASK_DATA_FLAGS SDIO_MASK_INTC_FLAGS +#define SDIO_READ_SINGLE_FLAGS (SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_RXORE | \ + SDIO_FLAG_DTBLKEND | SDIO_FLAG_STBITE) +#define SDIO_READ_MULTI_FLAGS (SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_RXORE | \ + SDIO_FLAG_DTEND | SDIO_FLAG_STBITE) +#define SDIO_WRITE_SINGLE_FLAGS (SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_TXURE | \ + SDIO_FLAG_DTBLKEND | SDIO_FLAG_STBITE) +#define SDIO_WRITE_MULTI_FLAGS (SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_TXURE | \ + SDIO_FLAG_DTEND | SDIO_FLAG_STBITE) +#endif typedef struct{ uint32_t sd_scr[2] ; /* content of SCR register */ @@ -121,7 +150,7 @@ static sdcard_opration_t card_opration = { }; /* set sector size to 512 */ -#define SECTOR_SIZE 512 +#define SD_SECTOR_SIZE 512 typedef struct { @@ -145,13 +174,17 @@ static sd_error_enum r1_error_check(uint8_t cmdindex); /* check if error type for R1 response */ static sd_error_enum r1_error_type_check(uint32_t resp); /* check if error occurs for R2 response */ -static sd_error_enum r2_error_check(void); +static sd_error_enum r2_error_check(uint8_t cmdindex); /* check if error occurs for R3 response */ static sd_error_enum r3_error_check(void); /* check if error occurs for R6 response */ static sd_error_enum r6_error_check(uint8_t cmdindex, uint16_t *prca); /* check if error occurs for R7 response */ static sd_error_enum r7_error_check(void); +#ifdef GD32_SDIO_USING_H77X +static void sdio_command_error_log(uint8_t cmdindex, const char *reason, uint32_t status); +static void sdio_data_timeout_log(const char *operation); +#endif /* get the state which the card is in */ static sd_error_enum sd_card_state_get(uint8_t *pcardstate); @@ -163,13 +196,23 @@ static sd_error_enum sd_scr_get(uint16_t rca, uint32_t *pscr); static uint32_t sd_datablocksize_get(uint16_t bytesnumber); /* configure the GPIO of SDIO interface */ -static void gpio_config(void); +static rt_err_t gpio_config(void); +#ifdef GD32_SDIO_USING_H77X +static int sdio_gpio_level_get(const char *pin_name); +static void sdio_gpio_levels_log(void); +#ifdef BSP_SDIO_USING_CARD_DETECT +static rt_err_t sdio_card_detect_config(void); +static rt_bool_t sdio_card_is_present(void); +#endif +#endif /* configure the RCU of SDIO and DMA */ -static void rcu_config(void); +static rt_err_t rcu_config(void); +#ifndef GD32_SDIO_USING_H77X /* configure the DMA for SDIO transfer request */ static void dma_transfer_config(uint32_t *srcbuf, uint32_t bufsize); /* configure the DMA for SDIO reveive request */ static void dma_receive_config(uint32_t *dstbuf, uint32_t bufsize); +#endif static void nvic_config(void); static sd_error_enum sd_config(void); @@ -181,23 +224,23 @@ static void sdio_dma_irq_config(void); /* RT-Thread Device Driver Interface */ static rt_err_t rt_sdcard_init(rt_device_t dev) { - rt_err_t ret = RT_EOK; sd_error_enum sd_error = SD_OK; uint16_t retry = 5; - ret = rt_mutex_init(&sd.sd_lock, "sd_lock", RT_IPC_FLAG_FIFO); - - if (RT_EOK != ret) { - LOG_E("init mutex failed\n"); - return ret; +#ifdef BSP_SDIO_USING_CARD_DETECT + if(RT_EOK != sdio_card_detect_config()){ + return -RT_EINVAL; } - ret = rt_sem_init(&sd.sem, "sd_sem", 0, RT_IPC_FLAG_FIFO); - - if (RT_EOK != ret) { - LOG_E("init semaphore failed\n"); - return ret; + rt_thread_mdelay(5U); + if(!sdio_card_is_present()){ + LOG_W("No SD card detected: CD %s=%d", BSP_SDIO_CD_PIN, + sdio_gpio_level_get(BSP_SDIO_CD_PIN)); + return -RT_EIO; } + LOG_I("SD card detected: CD %s=%d", BSP_SDIO_CD_PIN, + sdio_gpio_level_get(BSP_SDIO_CD_PIN)); +#endif nvic_irq_enable(sd.irqn, 0, 0); @@ -207,17 +250,16 @@ static rt_err_t rt_sdcard_init(rt_device_t dev) } while((SD_OK != sd_error) && (--retry)); if (retry) { - LOG_I("\r\n Card init success!\r\n"); + LOG_I("Card init success"); } else { - LOG_E("\r\n Card init failed!\r\n"); - ret = -RT_EIO; - return ret; + LOG_W("Card init failed: %d", sd_error); + return -RT_EIO; } card_info_get(); - return ret; + return RT_EOK; } @@ -231,7 +273,11 @@ static rt_err_t rt_sdcard_close(rt_device_t dev) return RT_EOK; } -static uint32_t dma_buffer[SECTOR_SIZE/sizeof(uint32_t)]; +static uint32_t dma_buffer[SD_SECTOR_SIZE/sizeof(uint32_t)]; +#ifdef GD32_SDIO_USING_H77X +static rt_bool_t first_block_read = RT_TRUE; +static rt_bool_t first_block_write = RT_TRUE; +#endif static rt_ssize_t rt_sdcard_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size) { RT_ASSERT(dev != RT_NULL); @@ -240,10 +286,16 @@ static rt_ssize_t rt_sdcard_read(rt_device_t dev, rt_off_t pos, void* buffer, rt uint32_t status; gd32_sdio_t *sd = (gd32_sdio_t *)dev->user_data; - if (!buffer) { + if (!buffer || !size) { return 0; } +#ifdef GD32_SDIO_USING_H77X + if(first_block_read){ + LOG_D("first block read: sector=%u count=%u", (uint32_t)pos, (uint32_t)size); + } +#endif + rt_mutex_take(&sd->sd_lock, RT_WAITING_FOREVER); if(((uint32_t)buffer & 0x03) != 0) @@ -251,31 +303,37 @@ static rt_ssize_t rt_sdcard_read(rt_device_t dev, rt_off_t pos, void* buffer, rt /* non-aligned. */ uint32_t i; uint32_t sector_adr; - uint32_t* copy_buffer; + uint8_t *copy_buffer; - sector_adr = pos*SECTOR_SIZE; - copy_buffer = (uint32_t*)buffer; + sector_adr = pos*SD_SECTOR_SIZE; + copy_buffer = (uint8_t *)buffer; for(i=0; isd_lock); if (sd_error == SD_OK){ +#ifdef GD32_SDIO_USING_H77X + if(first_block_read){ + LOG_D("first block read ok"); + first_block_read = RT_FALSE; + } +#endif return size; } else { @@ -294,31 +352,41 @@ static rt_ssize_t rt_sdcard_write (rt_device_t dev, rt_off_t pos, const void* bu uint32_t status; gd32_sdio_t *sd = (gd32_sdio_t *)dev->user_data; + if (!buffer || !size) { + return 0; + } + +#ifdef GD32_SDIO_USING_H77X + if(first_block_write){ + LOG_D("first block write: sector=%u count=%u", (uint32_t)pos, (uint32_t)size); + } +#endif + rt_mutex_take(&sd->sd_lock, RT_WAITING_FOREVER); if (((uint32_t)buffer & 0x03) != 0) { /* non-aligned. */ uint32_t i; rt_size_t sector_adr; - uint32_t* copy_buffer; + const uint8_t *copy_buffer; - sector_adr = pos*SECTOR_SIZE; - copy_buffer = (uint32_t*)buffer; + sector_adr = pos*SD_SECTOR_SIZE; + copy_buffer = (const uint8_t *)buffer; for (i=0; i 0) { - sd_error = sd_block_write((uint32_t*)dma_buffer, sector_adr, SECTOR_SIZE); + sd_error = sd_block_write((uint32_t*)dma_buffer, sector_adr, SD_SECTOR_SIZE); retry--; if (sd_error == SD_OK) { break; } } - sector_adr += SECTOR_SIZE; - copy_buffer += SECTOR_SIZE; + sector_adr += SD_SECTOR_SIZE; + copy_buffer += SD_SECTOR_SIZE; } } else { @@ -326,7 +394,7 @@ static rt_ssize_t rt_sdcard_write (rt_device_t dev, rt_off_t pos, const void* bu if (size == 1) { while (retry > 0) { - sd_error = sd_block_write((uint32_t*)buffer, pos*SECTOR_SIZE, SECTOR_SIZE); + sd_error = sd_block_write((uint32_t*)buffer, pos*SD_SECTOR_SIZE, SD_SECTOR_SIZE); retry--; if (sd_error == SD_OK) { break; @@ -335,7 +403,7 @@ static rt_ssize_t rt_sdcard_write (rt_device_t dev, rt_off_t pos, const void* bu } else { while (retry > 0) { - sd_error = sd_multiblocks_write((uint32_t*)buffer, pos*SECTOR_SIZE, SECTOR_SIZE, size); + sd_error = sd_multiblocks_write((uint32_t*)buffer, pos*SD_SECTOR_SIZE, SD_SECTOR_SIZE, size); retry--; if (sd_error == SD_OK) { break; @@ -350,6 +418,12 @@ static rt_ssize_t rt_sdcard_write (rt_device_t dev, rt_off_t pos, const void* bu LOG_D("sdio e:%d r:%d\n", sd_error, retry); } if (sd_error == SD_OK) { +#ifdef GD32_SDIO_USING_H77X + if(first_block_write){ + LOG_D("first block write ok"); + first_block_write = RT_FALSE; + } +#endif return size; } else { @@ -384,6 +458,8 @@ static rt_err_t rt_sdcard_control(rt_device_t dev, int cmd, void *args) int rt_hw_sdcard_init(void) { + rt_err_t ret; + /* register sdcard device */ sd.sdcard_device.type = RT_Device_Class_Block; sd.sdcard_device.init = rt_sdcard_init; @@ -394,9 +470,32 @@ int rt_hw_sdcard_init(void) sd.sdcard_device.control = rt_sdcard_control; sd.sdcard_device.user_data = &sd; - rt_device_register(&sd.sdcard_device, sd.device_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE | RT_DEVICE_FLAG_STANDALONE); + ret = rt_mutex_init(&sd.sd_lock, "sd_lock", RT_IPC_FLAG_FIFO); + if (ret != RT_EOK) + { + LOG_E("init mutex failed"); + return ret; + } - return RT_EOK; + ret = rt_sem_init(&sd.sem, "sd_sem", 0, RT_IPC_FLAG_FIFO); + if (ret != RT_EOK) + { + LOG_E("init semaphore failed"); + rt_mutex_detach(&sd.sd_lock); + return ret; + } + + ret = rt_device_register(&sd.sdcard_device, sd.device_name, + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE | + RT_DEVICE_FLAG_STANDALONE); + if (ret != RT_EOK) + { + LOG_E("register device failed"); + rt_sem_detach(&sd.sem); + rt_mutex_detach(&sd.sd_lock); + } + + return ret; } INIT_DEVICE_EXPORT(rt_hw_sdcard_init); @@ -415,15 +514,43 @@ sd_error_enum sd_config(void) /* initialize the card */ status = sd_init(); - if(SD_OK == status){ - status = sd_card_information_get(&sd.sd_cardinfo); + if(SD_OK != status){ + LOG_W("SD initialization stage failed: %d", status); + return status; } - if(SD_OK == status){ - status = sd_card_select_deselect(sd.sd_cardinfo.card_rca); +#ifdef GD32_SDIO_USING_H77X + LOG_D("sd_init ok"); +#endif + + status = sd_card_information_get(&sd.sd_cardinfo); + if(SD_OK != status){ + LOG_W("SD card information stage failed: %d", status); + return status; } +#ifdef GD32_SDIO_USING_H77X + LOG_D("sd_card_information_get ok"); +#endif + + status = sd_card_select_deselect(sd.sd_cardinfo.card_rca); + if(SD_OK != status){ + LOG_W("SD card selection stage failed: %d", status); + return status; + } +#ifdef GD32_SDIO_USING_H77X + LOG_D("sd_card_select_deselect ok"); +#endif + status = sd_cardstatus_get(&cardstate); + if(SD_OK != status){ + LOG_W("SD card status stage failed: %d", status); + return status; + } +#ifdef GD32_SDIO_USING_H77X + LOG_D("sd_cardstatus_get ok"); +#endif + if(cardstate & 0x02000000){ - LOG_D("\r\n The card is locked!"); + LOG_E("The card is locked"); #if 0 /* unlock the card if necessary */ status = sd_lock_unlock(SD_UNLOCK); @@ -435,21 +562,29 @@ sd_error_enum sd_config(void) LOG_W("\r\n The card is unlocked! Please reset MCU!"); } #endif - while (1){ - } + return SD_LOCK_UNLOCK_FAILED; } - if ((SD_OK == status) && (!(cardstate & 0x02000000))) - { - /* set bus mode */ - status = sd_bus_mode_config(SDIO_BUSMODE_4BIT); -// status = sd_bus_mode_config( SDIO_BUSMODE_1BIT ); + + /* set bus mode */ + status = sd_bus_mode_config(SDIO_BUSMODE_4BIT); + if(SD_OK != status){ + LOG_W("SD 4-bit bus configuration failed: %d", status); + return status; } - if (SD_OK == status) - { - /* set data transfer mode */ - status = sd_transfer_mode_config(SD_DMA_MODE); - //status = sd_transfer_mode_config(SD_POLLING_MODE); +#ifdef GD32_SDIO_USING_H77X + LOG_D("sd_bus_mode_config ok"); +#endif + + /* set data transfer mode */ +#ifdef GD32_SDIO_USING_H77X + status = sd_transfer_mode_config(SD_POLLING_MODE); +#else + status = sd_transfer_mode_config(SD_DMA_MODE); +#endif + if(SD_OK != status){ + LOG_W("SD transfer mode configuration failed: %d", status); } + return status; } @@ -488,16 +623,16 @@ void card_info_get(void) sd_security = (card_opration.sd_scr[1] & 0x00700000) >> 20; if(2 == sd_security){ - LOG_I("\r\n## SDSC card ##"); + LOG_D("\r\n## SDSC card ##"); }else if(3 == sd_security){ - LOG_I("\r\n## SDHC card ##"); + LOG_D("\r\n## SDHC card ##"); }else if(4 == sd_security){ - LOG_I("\r\n## SDXC card ##"); + LOG_D("\r\n## SDXC card ##"); } block_count = (sd.sd_cardinfo.card_csd.c_size + 1)*1024; block_size = 512; - LOG_I("\r\n## Device size is %dKB ##", sd_card_capacity_get()); + LOG_I("Device size: %d KB", sd_card_capacity_get()); LOG_D("\r\n## Block size is %dB - %dB ##", block_size, sd.sd_cardinfo.card_blocksize); LOG_D("\r\n## Block count is %d ##", block_count); @@ -557,26 +692,44 @@ sd_error_enum sd_init(void) sd_error_enum status = SD_OK; /* configure the RCU and GPIO, deinitialize the SDIO */ - rcu_config(); - gpio_config(); + if(RT_EOK != rcu_config()){ + return SD_OPERATION_IMPROPER; + } + if (RT_EOK != gpio_config()) { + return SD_PARAMETER_INVALID; + } sdio_deinit(); /* configure the clock and work voltage */ status = sd_power_on(); if(SD_OK != status){ + LOG_W("SD power-on sequence failed: %d", status); return status; } +#ifdef GD32_SDIO_USING_H77X + LOG_D("sd_power_on ok"); +#endif /* initialize the card and get CID and CSD of the card */ status = sd_card_init(); if(SD_OK != status){ + LOG_W("SD card identification failed: %d", status); return status; } +#ifdef GD32_SDIO_USING_H77X + LOG_D("sd_card_init ok"); +#endif /* configure the SDIO peripheral */ +#ifdef GD32_SDIO_USING_H77X + sdio_clock_config(SDIO_SDIOCLKEDGE_RISING, SDIO_CLOCKBYPASS_DISABLE, SDIO_CLOCKPWRSAVE_DISABLE, SD_CLK_DIV_INIT); + sdio_bus_mode_set(SDIO_BUSMODE_1BIT); + sdio_hardware_clock_disable(); +#else sdio_clock_config(SDIO_SDIOCLKEDGE_RISING, SDIO_CLOCKBYPASS_DISABLE, SDIO_CLOCKPWRSAVE_DISABLE, SD_CLK_DIV_TRANS); sdio_bus_mode_set(SDIO_BUSMODE_1BIT); sdio_hardware_clock_enable();//sdio_hardware_clock_disable(); +#endif return status; } @@ -604,7 +757,7 @@ sd_error_enum sd_card_init(void) sdio_wait_type_set(SDIO_WAITTYPE_NO); sdio_csm_enable(); /* check if some error occurs */ - status = r2_error_check(); + status = r2_error_check(SD_CMD_ALL_SEND_CID); if(SD_OK != status){ return status; } @@ -639,7 +792,7 @@ sd_error_enum sd_card_init(void) sdio_wait_type_set(SDIO_WAITTYPE_NO); sdio_csm_enable(); /* check if some error occurs */ - status = r2_error_check(); + status = r2_error_check(SD_CMD_SEND_CSD); if(SD_OK != status){ return status; } @@ -668,13 +821,24 @@ sd_error_enum sd_power_on(void) /* configure the SDIO peripheral */ sdio_clock_config(SDIO_SDIOCLKEDGE_RISING, SDIO_CLOCKBYPASS_DISABLE, SDIO_CLOCKPWRSAVE_DISABLE, SD_CLK_DIV_INIT); sdio_bus_mode_set(SDIO_BUSMODE_1BIT); +#ifdef GD32_SDIO_USING_H77X + sdio_hardware_clock_disable(); +#else sdio_hardware_clock_enable();//sdio_hardware_clock_disable(); +#endif sdio_power_state_set(SDIO_POWER_ON); /* enable SDIO_CLK clock output */ sdio_clock_enable(); +#ifdef GD32_SDIO_USING_H77X + /* Provide more than the required 74 initialization clocks at 200 kHz. */ + rt_thread_mdelay(1U); + sdio_gpio_levels_log(); +#endif + /* send CMD0(GO_IDLE_STATE) to reset the card */ sdio_command_response_config(SD_CMD_GO_IDLE_STATE, (uint32_t)0x0, SDIO_RESPONSETYPE_NO); + sdio_flag_clear(SDIO_MASK_CMD_FLAGS); sdio_wait_type_set(SDIO_WAITTYPE_NO); /* enable the CSM */ sdio_csm_enable(); @@ -682,61 +846,60 @@ sd_error_enum sd_power_on(void) /* check if command sent error occurs */ status = cmdsent_error_check(); if(SD_OK != status){ + LOG_W("CMD0 failed: %d", status); return status; } /* send CMD8(SEND_IF_COND) to get SD memory card interface condition */ sdio_command_response_config(SD_CMD_SEND_IF_COND, SD_CHECK_PATTERN, SDIO_RESPONSETYPE_SHORT); + sdio_flag_clear(SDIO_MASK_CMD_FLAGS); sdio_wait_type_set(SDIO_WAITTYPE_NO); sdio_csm_enable(); - if(SD_OK == r7_error_check()){ + status = r7_error_check(); + if(SD_OK == status){ /* SD Card 2.0 */ card_opration.cardtype = SDIO_STD_CAPACITY_SD_CARD_V2_0; sdcardtype = SD_HIGH_CAPACITY; + }else{ + LOG_D("CMD8 failed: %d", status); } - /* send CMD55(APP_CMD) to indicate next command is application specific command */ - sdio_command_response_config(SD_CMD_APP_CMD, (uint32_t)0x0, SDIO_RESPONSETYPE_SHORT); - sdio_wait_type_set(SDIO_WAITTYPE_NO); - sdio_csm_enable(); - - if(SD_OK == r1_error_check(SD_CMD_APP_CMD)){ - /* SD memory card */ - while((!busyflag) && (count < SD_MAX_VOLT_VALIDATION)){ - /* send CMD55(APP_CMD) to indicate next command is application specific command */ - sdio_command_response_config(SD_CMD_APP_CMD, (uint32_t)0x0, SDIO_RESPONSETYPE_SHORT); - sdio_wait_type_set(SDIO_WAITTYPE_NO); - sdio_csm_enable(); - /* check if some error occurs */ - status = r1_error_check(SD_CMD_APP_CMD); - if(SD_OK != status){ - return status; - } - - /* send ACMD41(SD_SEND_OP_COND) to get host capacity support information (HCS) and OCR content */ - sdio_command_response_config(SD_APPCMD_SD_SEND_OP_COND, (SD_VOLTAGE_WINDOW | sdcardtype), SDIO_RESPONSETYPE_SHORT); - sdio_wait_type_set(SDIO_WAITTYPE_NO); - sdio_csm_enable(); - /* check if some error occurs */ - status = r3_error_check(); - if(SD_OK != status){ - return status; - } - /* get the response and check card power up status bit(busy) */ - response = sdio_response_get(SDIO_RESPONSE0); - busyflag = (uint8_t)((response >> 31)&(uint32_t)0x01); - ++count; - } - if(count >= SD_MAX_VOLT_VALIDATION){ - status = SD_VOLTRANGE_INVALID; + /* SD memory card power-up sequence: CMD55 must be followed by ACMD41. */ + while((!busyflag) && (count < SD_MAX_VOLT_VALIDATION)){ + sdio_command_response_config(SD_CMD_APP_CMD, (uint32_t)0x0, SDIO_RESPONSETYPE_SHORT); + sdio_flag_clear(SDIO_MASK_CMD_FLAGS); + sdio_wait_type_set(SDIO_WAITTYPE_NO); + sdio_csm_enable(); + status = r1_error_check(SD_CMD_APP_CMD); + if(SD_OK != status){ + LOG_W("CMD55 failed at attempt %u: %d", count + 1U, status); return status; } - if(response &= SD_HIGH_CAPACITY){ - /* SDHC card */ - card_opration.cardtype = SDIO_HIGH_CAPACITY_SD_CARD; + + sdio_command_response_config(SD_APPCMD_SD_SEND_OP_COND, (SD_VOLTAGE_WINDOW | sdcardtype), SDIO_RESPONSETYPE_SHORT); + sdio_flag_clear(SDIO_MASK_CMD_FLAGS); + sdio_wait_type_set(SDIO_WAITTYPE_NO); + sdio_csm_enable(); + status = r3_error_check(); + if(SD_OK != status){ + LOG_W("ACMD41 failed at attempt %u: %d", count + 1U, status); + return status; } + + response = sdio_response_get(SDIO_RESPONSE0); + busyflag = (uint8_t)((response >> 31)&(uint32_t)0x01); + ++count; + } + if(count >= SD_MAX_VOLT_VALIDATION){ + LOG_D("ACMD41 voltage validation timed out"); + return SD_VOLTRANGE_INVALID; } + if(response & SD_HIGH_CAPACITY){ + /* SDHC card */ + card_opration.cardtype = SDIO_HIGH_CAPACITY_SD_CARD; + } + return status; } @@ -885,7 +1048,10 @@ sd_error_enum sd_block_read(uint32_t *preadbuffer, uint32_t readaddr, uint16_t b /* configure SDIO data transmisson */ sdio_data_config(SD_DATATIMEOUT, card_opration.totalnumber_bytes, datablksize); sdio_data_transfer_config(SDIO_TRANSDIRECTION_TOSDIO, SDIO_TRANSMODE_BLOCK); +#ifndef GD32_SDIO_USING_H77X sdio_dsm_enable(); +#endif + sdio_transfer_start_enable(); /* send CMD17(READ_SINGLE_BLOCK) to read a block */ sdio_command_response_config(SD_CMD_READ_SINGLE_BLOCK, (uint32_t)readaddr, SDIO_RESPONSETYPE_SHORT); @@ -894,12 +1060,16 @@ sd_error_enum sd_block_read(uint32_t *preadbuffer, uint32_t readaddr, uint16_t b /* check if some error occurs */ status = r1_error_check(SD_CMD_READ_SINGLE_BLOCK); if(SD_OK != status){ + sdio_transfer_start_disable(); return status; } if(SD_POLLING_MODE == card_opration.transmode){ /* polling mode */ - while(!sdio_flag_get(SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_RXORE | SDIO_FLAG_DTBLKEND | SDIO_FLAG_STBITE)){ +#ifdef GD32_SDIO_USING_H77X + timeout = SDIO_POLLING_TIMEOUT; +#endif + while(!sdio_flag_get(SDIO_READ_SINGLE_FLAGS)){ if(RESET != sdio_flag_get(SDIO_FLAG_RFH)){ /* at least 8 words can be read in the FIFO */ for(count = 0; count < SD_FIFOHALF_WORDS; count++){ @@ -907,7 +1077,16 @@ sd_error_enum sd_block_read(uint32_t *preadbuffer, uint32_t readaddr, uint16_t b } ptempbuff += SD_FIFOHALF_WORDS; } +#ifdef GD32_SDIO_USING_H77X + if(0U == --timeout){ + sdio_data_timeout_log("block read timeout"); + sdio_transfer_start_disable(); + sdio_flag_clear(SDIO_MASK_DATA_FLAGS); + return SD_DATA_TIMEOUT; + } +#endif } + sdio_transfer_start_disable(); /* whether some error occurs and return it */ if(RESET != sdio_flag_get(SDIO_FLAG_DTCRCERR)){ @@ -927,12 +1106,13 @@ sd_error_enum sd_block_read(uint32_t *preadbuffer, uint32_t readaddr, uint16_t b sdio_flag_clear(SDIO_FLAG_STBITE); return status; } - while(RESET != sdio_flag_get(SDIO_FLAG_RXDTVAL)){ + while(SDIO_FIFO_DATA_AVAILABLE()){ *ptempbuff = sdio_data_read(); ++ptempbuff; } /* clear the SDIO_INTC flags */ - sdio_flag_clear(SDIO_MASK_INTC_FLAGS); + sdio_flag_clear(SDIO_MASK_DATA_FLAGS); +#ifndef GD32_SDIO_USING_H77X }else if(SD_DMA_MODE == card_opration.transmode){ /* DMA mode */ /* enable the SDIO corresponding interrupts and DMA function */ @@ -953,6 +1133,7 @@ sd_error_enum sd_block_read(uint32_t *preadbuffer, uint32_t readaddr, uint16_t b } } #endif +#endif /* GD32_SDIO_USING_H77X */ }else{ status = SD_PARAMETER_INVALID; } @@ -1032,7 +1213,10 @@ sd_error_enum sd_multiblocks_read(uint32_t *preadbuffer, uint32_t readaddr, uint /* configure the SDIO data transmisson */ sdio_data_config(SD_DATATIMEOUT, card_opration.totalnumber_bytes, datablksize); sdio_data_transfer_config(SDIO_TRANSDIRECTION_TOSDIO, SDIO_TRANSMODE_BLOCK); +#ifndef GD32_SDIO_USING_H77X sdio_dsm_enable(); +#endif + sdio_transfer_start_enable(); /* send CMD18(READ_MULTIPLE_BLOCK) to read multiple blocks */ sdio_command_response_config(SD_CMD_READ_MULTIPLE_BLOCK, readaddr, SDIO_RESPONSETYPE_SHORT); @@ -1041,12 +1225,16 @@ sd_error_enum sd_multiblocks_read(uint32_t *preadbuffer, uint32_t readaddr, uint /* check if some error occurs */ status = r1_error_check(SD_CMD_READ_MULTIPLE_BLOCK); if(SD_OK != status){ + sdio_transfer_start_disable(); return status; } if(SD_POLLING_MODE == card_opration.transmode){ /* polling mode */ - while(!sdio_flag_get(SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_RXORE | SDIO_FLAG_DTEND | SDIO_FLAG_STBITE)){ +#ifdef GD32_SDIO_USING_H77X + timeout = SDIO_POLLING_TIMEOUT; +#endif + while(!sdio_flag_get(SDIO_READ_MULTI_FLAGS)){ if(RESET != sdio_flag_get(SDIO_FLAG_RFH)){ /* at least 8 words can be read in the FIFO */ for(count = 0; count < SD_FIFOHALF_WORDS; count++){ @@ -1054,6 +1242,14 @@ sd_error_enum sd_multiblocks_read(uint32_t *preadbuffer, uint32_t readaddr, uint } ptempbuff += SD_FIFOHALF_WORDS; } +#ifdef GD32_SDIO_USING_H77X + if(0U == --timeout){ + sdio_data_timeout_log("multi-block read timeout"); + sdio_transfer_start_disable(); + sdio_flag_clear(SDIO_MASK_DATA_FLAGS); + return SD_DATA_TIMEOUT; + } +#endif } /* whether some error occurs and return it */ @@ -1074,10 +1270,11 @@ sd_error_enum sd_multiblocks_read(uint32_t *preadbuffer, uint32_t readaddr, uint sdio_flag_clear(SDIO_FLAG_STBITE); return status; } - while(RESET != sdio_flag_get(SDIO_FLAG_RXDTVAL)){ + while(SDIO_FIFO_DATA_AVAILABLE()){ *ptempbuff = sdio_data_read(); ++ptempbuff; } + sdio_transfer_start_disable(); if(RESET != sdio_flag_get(SDIO_FLAG_DTEND)){ if((SDIO_STD_CAPACITY_SD_CARD_V1_1 == card_opration.cardtype) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == card_opration.cardtype) || @@ -1093,7 +1290,8 @@ sd_error_enum sd_multiblocks_read(uint32_t *preadbuffer, uint32_t readaddr, uint } } } - sdio_flag_clear(SDIO_MASK_INTC_FLAGS); + sdio_flag_clear(SDIO_MASK_DATA_FLAGS); +#ifndef GD32_SDIO_USING_H77X }else if(SD_DMA_MODE == card_opration.transmode){ /* DMA mode */ /* enable the SDIO corresponding interrupts and DMA function */ @@ -1119,6 +1317,7 @@ sd_error_enum sd_multiblocks_read(uint32_t *preadbuffer, uint32_t readaddr, uint if(SD_OK != card_opration.transerror){ return card_opration.transerror; } +#endif /* GD32_SDIO_USING_H77X */ }else{ status = SD_PARAMETER_INVALID; } @@ -1219,6 +1418,7 @@ sd_error_enum sd_block_write(uint32_t *pwritebuffer, uint32_t writeaddr, uint16_ return SD_ERROR; } +#ifndef GD32_SDIO_USING_H77X /* send CMD24(WRITE_BLOCK) to write a block */ sdio_command_response_config(SD_CMD_WRITE_BLOCK, writeaddr, SDIO_RESPONSETYPE_SHORT); sdio_wait_type_set(SDIO_WAITTYPE_NO); @@ -1228,6 +1428,7 @@ sd_error_enum sd_block_write(uint32_t *pwritebuffer, uint32_t writeaddr, uint16_ if(SD_OK != status){ return status; } +#endif card_opration.stopcondition = 0; card_opration.totalnumber_bytes = blocksize; @@ -1235,11 +1436,29 @@ sd_error_enum sd_block_write(uint32_t *pwritebuffer, uint32_t writeaddr, uint16_ /* configure the SDIO data transmisson */ sdio_data_config(SD_DATATIMEOUT, card_opration.totalnumber_bytes, datablksize); sdio_data_transfer_config(SDIO_TRANSDIRECTION_TOCARD, SDIO_TRANSMODE_BLOCK); +#ifndef GD32_SDIO_USING_H77X sdio_dsm_enable(); +#endif + sdio_transfer_start_enable(); + +#ifdef GD32_SDIO_USING_H77X + /* H77x requires the data path to be ready before issuing a write command. */ + sdio_command_response_config(SD_CMD_WRITE_BLOCK, writeaddr, SDIO_RESPONSETYPE_SHORT); + sdio_wait_type_set(SDIO_WAITTYPE_NO); + sdio_csm_enable(); + status = r1_error_check(SD_CMD_WRITE_BLOCK); + if(SD_OK != status){ + sdio_transfer_start_disable(); + return status; + } +#endif if(SD_POLLING_MODE == card_opration.transmode){ /* polling mode */ - while(!sdio_flag_get(SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_TXURE | SDIO_FLAG_DTBLKEND | SDIO_FLAG_STBITE)){ +#ifdef GD32_SDIO_USING_H77X + timeout = SDIO_POLLING_TIMEOUT; +#endif + while(!sdio_flag_get(SDIO_WRITE_SINGLE_FLAGS)){ if(RESET != sdio_flag_get(SDIO_FLAG_TFH)){ /* at least 8 words can be written into the FIFO */ if((card_opration.totalnumber_bytes - transbytes) < SD_FIFOHALF_BYTES){ @@ -1258,7 +1477,16 @@ sd_error_enum sd_block_write(uint32_t *pwritebuffer, uint32_t writeaddr, uint16_ transbytes += SD_FIFOHALF_BYTES; } } +#ifdef GD32_SDIO_USING_H77X + if(0U == --timeout){ + sdio_data_timeout_log("block write timeout"); + sdio_transfer_start_disable(); + sdio_flag_clear(SDIO_MASK_DATA_FLAGS); + return SD_DATA_TIMEOUT; + } +#endif } + sdio_transfer_start_disable(); /* whether some error occurs and return it */ if(RESET != sdio_flag_get(SDIO_FLAG_DTCRCERR)){ @@ -1278,6 +1506,7 @@ sd_error_enum sd_block_write(uint32_t *pwritebuffer, uint32_t writeaddr, uint16_ sdio_flag_clear(SDIO_FLAG_STBITE); return status; } +#ifndef GD32_SDIO_USING_H77X }else if(SD_DMA_MODE == card_opration.transmode){ /* DMA mode */ /* enable the SDIO corresponding interrupts and DMA */ @@ -1304,13 +1533,14 @@ sd_error_enum sd_block_write(uint32_t *pwritebuffer, uint32_t writeaddr, uint16_ if (SD_OK != card_opration.transerror){ return card_opration.transerror; } +#endif /* GD32_SDIO_USING_H77X */ }else{ status = SD_PARAMETER_INVALID; return status; } /* clear the SDIO_INTC flags */ - sdio_flag_clear(SDIO_MASK_INTC_FLAGS); + sdio_flag_clear(SDIO_MASK_DATA_FLAGS); /* get the card state and wait the card is out of programming and receiving state */ status = sd_card_state_get(&cardstate); while((SD_OK == status) && ((SD_CARDSTATE_PROGRAMMING == cardstate) || (SD_CARDSTATE_RECEIVING == cardstate))){ @@ -1422,6 +1652,7 @@ sd_error_enum sd_multiblocks_write(uint32_t *pwritebuffer, uint32_t writeaddr, u return status; } } +#ifndef GD32_SDIO_USING_H77X /* send CMD25(WRITE_MULTIPLE_BLOCK) to continuously write blocks of data */ sdio_command_response_config(SD_CMD_WRITE_MULTIPLE_BLOCK, writeaddr, SDIO_RESPONSETYPE_SHORT); sdio_wait_type_set(SDIO_WAITTYPE_NO); @@ -1431,6 +1662,7 @@ sd_error_enum sd_multiblocks_write(uint32_t *pwritebuffer, uint32_t writeaddr, u if(SD_OK != status){ return status; } +#endif card_opration.stopcondition = 1; card_opration.totalnumber_bytes = blocksnumber * blocksize; @@ -1438,11 +1670,29 @@ sd_error_enum sd_multiblocks_write(uint32_t *pwritebuffer, uint32_t writeaddr, u /* configure the SDIO data transmisson */ sdio_data_config(SD_DATATIMEOUT, card_opration.totalnumber_bytes, datablksize); sdio_data_transfer_config(SDIO_TRANSDIRECTION_TOCARD, SDIO_TRANSMODE_BLOCK); +#ifndef GD32_SDIO_USING_H77X sdio_dsm_enable(); +#endif + sdio_transfer_start_enable(); + +#ifdef GD32_SDIO_USING_H77X + /* H77x requires the data path to be ready before issuing a write command. */ + sdio_command_response_config(SD_CMD_WRITE_MULTIPLE_BLOCK, writeaddr, SDIO_RESPONSETYPE_SHORT); + sdio_wait_type_set(SDIO_WAITTYPE_NO); + sdio_csm_enable(); + status = r1_error_check(SD_CMD_WRITE_MULTIPLE_BLOCK); + if(SD_OK != status){ + sdio_transfer_start_disable(); + return status; + } +#endif if(SD_POLLING_MODE == card_opration.transmode){ /* polling mode */ - while(!sdio_flag_get(SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_TXURE | SDIO_FLAG_DTEND | SDIO_FLAG_STBITE)){ +#ifdef GD32_SDIO_USING_H77X + timeout = SDIO_POLLING_TIMEOUT; +#endif + while(!sdio_flag_get(SDIO_WRITE_MULTI_FLAGS)){ if(RESET != sdio_flag_get(SDIO_FLAG_TFH)){ /* at least 8 words can be written into the FIFO */ if(!((card_opration.totalnumber_bytes - transbytes) < SD_FIFOHALF_BYTES)){ @@ -1461,7 +1711,16 @@ sd_error_enum sd_multiblocks_write(uint32_t *pwritebuffer, uint32_t writeaddr, u } } } +#ifdef GD32_SDIO_USING_H77X + if(0U == --timeout){ + sdio_data_timeout_log("multi-block write timeout"); + sdio_transfer_start_disable(); + sdio_flag_clear(SDIO_MASK_DATA_FLAGS); + return SD_DATA_TIMEOUT; + } +#endif } + sdio_transfer_start_disable(); /* whether some error occurs and return it */ if(RESET != sdio_flag_get(SDIO_FLAG_DTCRCERR)){ @@ -1496,7 +1755,8 @@ sd_error_enum sd_multiblocks_write(uint32_t *pwritebuffer, uint32_t writeaddr, u } } } - sdio_flag_clear(SDIO_MASK_INTC_FLAGS); + sdio_flag_clear(SDIO_MASK_DATA_FLAGS); +#ifndef GD32_SDIO_USING_H77X }else if(SD_DMA_MODE == card_opration.transmode){ /* DMA mode */ /* enable SDIO corresponding interrupts and DMA */ @@ -1523,6 +1783,7 @@ sd_error_enum sd_multiblocks_write(uint32_t *pwritebuffer, uint32_t writeaddr, u if(SD_OK != card_opration.transerror){ return card_opration.transerror; } +#endif /* GD32_SDIO_USING_H77X */ }else{ status = SD_PARAMETER_INVALID; return status; @@ -1530,7 +1791,7 @@ sd_error_enum sd_multiblocks_write(uint32_t *pwritebuffer, uint32_t writeaddr, u } /* clear the SDIO_INTC flags */ - sdio_flag_clear(SDIO_MASK_INTC_FLAGS); + sdio_flag_clear(SDIO_MASK_DATA_FLAGS); /* get the card state and wait the card is out of programming and receiving state */ status = sd_card_state_get(&cardstate); while((SD_OK == status) && ((SD_CARDSTATE_PROGRAMMING == cardstate) || (SD_CARDSTATE_RECEIVING == cardstate))){ @@ -1565,9 +1826,7 @@ sd_error_enum sd_erase(uint32_t startaddr, uint32_t endaddr) status = SD_FUNCTION_UNSUPPORTED; return status; } - clkdiv = (SDIO_CLKCTL & SDIO_CLKCTL_DIV); - clkdiv += ((SDIO_CLKCTL & SDIO_CLKCTL_DIV8)>>31)*256; - clkdiv += 2; + clkdiv = SDIO_CLOCK_DIVISION_GET(); delay = 168000 / clkdiv; /* check whether the card is locked */ @@ -1802,7 +2061,7 @@ sd_error_enum sd_sdstatus_get(uint32_t *psdstatus) return status; } - while(!sdio_flag_get(SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_RXORE | SDIO_FLAG_DTBLKEND | SDIO_FLAG_STBITE)){ + while(!sdio_flag_get(SDIO_READ_SINGLE_FLAGS)){ if(RESET != sdio_flag_get(SDIO_FLAG_RFH)){ for(count = 0; count < SD_FIFOHALF_WORDS; count++){ *(psdstatus + count) = sdio_data_read(); @@ -1829,13 +2088,13 @@ sd_error_enum sd_sdstatus_get(uint32_t *psdstatus) sdio_flag_clear(SDIO_FLAG_STBITE); return status; } - while(RESET != sdio_flag_get(SDIO_FLAG_RXDTVAL)){ + while(SDIO_FIFO_DATA_AVAILABLE()){ *psdstatus = sdio_data_read(); ++psdstatus; } /* clear the SDIO_INTC flags */ - sdio_flag_clear(SDIO_MASK_INTC_FLAGS); + sdio_flag_clear(SDIO_MASK_DATA_FLAGS); psdstatus -= 16; for(count = 0; count < 16; count++){ psdstatus[count] = ((psdstatus[count] & SD_MASK_0_7BITS) << 24) |((psdstatus[count] & SD_MASK_8_15BITS) << 8) | @@ -1979,7 +2238,7 @@ sd_error_enum sd_lock_unlock(uint8_t lockstate) } /* clear the SDIO_INTC flags */ - sdio_flag_clear(SDIO_MASK_INTC_FLAGS); + sdio_flag_clear(SDIO_MASK_DATA_FLAGS); /* get the card state and wait the card is out of programming and receiving state */ status = sd_card_state_get(&cardstate); while((SD_OK == status) && ((SD_CARDSTATE_PROGRAMMING == cardstate) || (SD_CARDSTATE_RECEIVING == cardstate))){ @@ -2257,6 +2516,30 @@ sd_error_enum sd_card_information_get(sd_card_info_struct *pcardinfo) \param[out] none \retval sd_error_enum */ +#ifdef GD32_SDIO_USING_H77X +static void sdio_command_error_log(uint8_t cmdindex, const char *reason, uint32_t status) +{ + if(SD_CMD_SEND_IF_COND == cmdindex){ + LOG_D("CMD%u %s: STAT=0x%08x CMDCTL=0x%08x ARG=0x%08x RSPIDX=%u RESP0=0x%08x CLKCTL=0x%08x PWRCTL=0x%08x", + cmdindex, reason, status, SDIO_CMDCTL(SDIO_PERIPH), SDIO_CMDAGMT(SDIO_PERIPH), + SDIO_RSPCMDIDX(SDIO_PERIPH), SDIO_RESP0(SDIO_PERIPH), SDIO_CLKCTL(SDIO_PERIPH), + SDIO_PWRCTL(SDIO_PERIPH)); + }else{ + LOG_W("CMD%u %s: STAT=0x%08x CMDCTL=0x%08x ARG=0x%08x RSPIDX=%u RESP0=0x%08x CLKCTL=0x%08x PWRCTL=0x%08x", + cmdindex, reason, status, SDIO_CMDCTL(SDIO_PERIPH), SDIO_CMDAGMT(SDIO_PERIPH), + SDIO_RSPCMDIDX(SDIO_PERIPH), SDIO_RESP0(SDIO_PERIPH), SDIO_CLKCTL(SDIO_PERIPH), + SDIO_PWRCTL(SDIO_PERIPH)); + } +} + +static void sdio_data_timeout_log(const char *operation) +{ + LOG_E("%s: STAT=0x%08x DATACTL=0x%08x DATACNT=%u CMDCTL=0x%08x ARG=0x%08x", + operation, SDIO_STATUS_REG, SDIO_DATACTL(SDIO_PERIPH), + SDIO_DATACNT(SDIO_PERIPH), SDIO_CMDCTL(SDIO_PERIPH), SDIO_CMDAGMT(SDIO_PERIPH)); +} +#endif + static sd_error_enum cmdsent_error_check(void) { sd_error_enum status = SD_OK; @@ -2271,7 +2554,7 @@ static sd_error_enum cmdsent_error_check(void) return status; } /* if the command is sent, clear the SDIO_INTC flags */ - sdio_flag_clear(SDIO_MASK_INTC_FLAGS); + sdio_flag_clear(SDIO_MASK_CMD_FLAGS); return status; } @@ -2335,9 +2618,9 @@ static sd_error_enum r1_error_check(uint8_t cmdindex) uint32_t reg_status = 0, resp_r1 = 0; /* store the content of SDIO_STAT */ - reg_status = SDIO_STAT; + reg_status = SDIO_STATUS_REG; while(!(reg_status & (SDIO_FLAG_CCRCERR | SDIO_FLAG_CMDTMOUT | SDIO_FLAG_CMDRECV))){ - reg_status = SDIO_STAT; + reg_status = SDIO_STATUS_REG; } /* check whether an error or timeout occurs or command response received */ if(reg_status & SDIO_FLAG_CCRCERR){ @@ -2346,6 +2629,9 @@ static sd_error_enum r1_error_check(uint8_t cmdindex) return status; }else if(reg_status & SDIO_FLAG_CMDTMOUT){ status = SD_CMD_RESP_TIMEOUT; +#ifdef GD32_SDIO_USING_H77X + sdio_command_error_log(cmdindex, "response timeout", reg_status); +#endif sdio_flag_clear(SDIO_FLAG_CMDTMOUT); return status; } @@ -2356,7 +2642,7 @@ static sd_error_enum r1_error_check(uint8_t cmdindex) return status; } /* clear all the SDIO_INTC flags */ - sdio_flag_clear(SDIO_MASK_INTC_FLAGS); + sdio_flag_clear(SDIO_MASK_CMD_FLAGS); /* get the SDIO response register 0 for checking */ resp_r1 = sdio_response_get(SDIO_RESPONSE0); if(SD_ALLZERO == (resp_r1 & SD_R1_ERROR_BITS)){ @@ -2372,32 +2658,38 @@ static sd_error_enum r1_error_check(uint8_t cmdindex) /*! \brief check if error occurs for R2 response - \param[in] none + \param[in] cmdindex: the index of command \param[out] none \retval sd_error_enum */ -static sd_error_enum r2_error_check(void) +static sd_error_enum r2_error_check(uint8_t cmdindex) { sd_error_enum status = SD_OK; uint32_t reg_status = 0; /* store the content of SDIO_STAT */ - reg_status = SDIO_STAT; + reg_status = SDIO_STATUS_REG; while(!(reg_status & (SDIO_FLAG_CCRCERR | SDIO_FLAG_CMDTMOUT | SDIO_FLAG_CMDRECV))){ - reg_status = SDIO_STAT; + reg_status = SDIO_STATUS_REG; } /* check whether an error or timeout occurs or command response received */ if(reg_status & SDIO_FLAG_CCRCERR){ status = SD_CMD_CRC_ERROR; +#ifdef GD32_SDIO_USING_H77X + sdio_command_error_log(cmdindex, "response CRC error", reg_status); +#endif sdio_flag_clear(SDIO_FLAG_CCRCERR); return status; }else if(reg_status & SDIO_FLAG_CMDTMOUT){ status = SD_CMD_RESP_TIMEOUT; +#ifdef GD32_SDIO_USING_H77X + sdio_command_error_log(cmdindex, "response timeout", reg_status); +#endif sdio_flag_clear(SDIO_FLAG_CMDTMOUT); return status; } /* clear all the SDIO_INTC flags */ - sdio_flag_clear(SDIO_MASK_INTC_FLAGS); + sdio_flag_clear(SDIO_MASK_CMD_FLAGS); return status; } @@ -2413,17 +2705,20 @@ static sd_error_enum r3_error_check(void) uint32_t reg_status = 0; /* store the content of SDIO_STAT */ - reg_status = SDIO_STAT; + reg_status = SDIO_STATUS_REG; while(!(reg_status & (SDIO_FLAG_CCRCERR | SDIO_FLAG_CMDTMOUT | SDIO_FLAG_CMDRECV))){ - reg_status = SDIO_STAT; + reg_status = SDIO_STATUS_REG; } if(reg_status & SDIO_FLAG_CMDTMOUT){ status = SD_CMD_RESP_TIMEOUT; +#ifdef GD32_SDIO_USING_H77X + sdio_command_error_log(SD_APPCMD_SD_SEND_OP_COND, "response timeout", reg_status); +#endif sdio_flag_clear(SDIO_FLAG_CMDTMOUT); return status; } /* clear all the SDIO_INTC flags */ - sdio_flag_clear(SDIO_MASK_INTC_FLAGS); + sdio_flag_clear(SDIO_MASK_CMD_FLAGS); return status; } @@ -2439,17 +2734,23 @@ static sd_error_enum r6_error_check(uint8_t cmdindex, uint16_t *prca) uint32_t reg_status = 0, response = 0; /* store the content of SDIO_STAT */ - reg_status = SDIO_STAT; + reg_status = SDIO_STATUS_REG; while(!(reg_status & (SDIO_FLAG_CCRCERR | SDIO_FLAG_CMDTMOUT | SDIO_FLAG_CMDRECV))){ - reg_status = SDIO_STAT; + reg_status = SDIO_STATUS_REG; } /* check whether an error or timeout occurs or command response received */ if(reg_status & SDIO_FLAG_CCRCERR){ status = SD_CMD_CRC_ERROR; +#ifdef GD32_SDIO_USING_H77X + sdio_command_error_log(cmdindex, "response CRC error", reg_status); +#endif sdio_flag_clear(SDIO_FLAG_CCRCERR); return status; }else if(reg_status & SDIO_FLAG_CMDTMOUT){ status = SD_CMD_RESP_TIMEOUT; +#ifdef GD32_SDIO_USING_H77X + sdio_command_error_log(cmdindex, "response timeout", reg_status); +#endif sdio_flag_clear(SDIO_FLAG_CMDTMOUT); return status; } @@ -2460,7 +2761,7 @@ static sd_error_enum r6_error_check(uint8_t cmdindex, uint16_t *prca) return status; } /* clear all the SDIO_INTC flags */ - sdio_flag_clear(SDIO_MASK_INTC_FLAGS); + sdio_flag_clear(SDIO_MASK_CMD_FLAGS); /* get the SDIO response register 0 for checking */ response = sdio_response_get(SDIO_RESPONSE0); @@ -2491,21 +2792,34 @@ static sd_error_enum r7_error_check(void) uint32_t reg_status = 0, timeout = 400000; /* store the content of SDIO_STAT */ - reg_status = SDIO_STAT; + reg_status = SDIO_STATUS_REG; while(!(reg_status & (SDIO_FLAG_CCRCERR | SDIO_FLAG_CMDTMOUT | SDIO_FLAG_CMDRECV)) && (timeout > 0)){ - reg_status = SDIO_STAT; + reg_status = SDIO_STATUS_REG; --timeout; } /* check the flags */ if((reg_status & SDIO_FLAG_CMDTMOUT) || (0 == timeout)){ status = SD_CMD_RESP_TIMEOUT; - sdio_flag_clear(SDIO_FLAG_CMDTMOUT); +#ifdef GD32_SDIO_USING_H77X + sdio_command_error_log(SD_CMD_SEND_IF_COND, + (reg_status & SDIO_FLAG_CMDTMOUT) ? "response timeout" : "software wait timeout", + reg_status); +#endif + sdio_flag_clear(SDIO_MASK_CMD_FLAGS); + return status; + } + if(reg_status & SDIO_FLAG_CCRCERR){ + status = SD_CMD_CRC_ERROR; +#ifdef GD32_SDIO_USING_H77X + sdio_command_error_log(SD_CMD_SEND_IF_COND, "response CRC error", reg_status); +#endif + sdio_flag_clear(SDIO_MASK_CMD_FLAGS); return status; } if(reg_status & SDIO_FLAG_CMDRECV){ status = SD_OK; - sdio_flag_clear(SDIO_FLAG_CMDRECV); + sdio_flag_clear(SDIO_MASK_CMD_FLAGS); return status; } return status; @@ -2538,9 +2852,9 @@ static sd_error_enum sd_card_state_get(uint8_t *pcardstate) sdio_csm_enable(); /* store the content of SDIO_STAT */ - reg_status = SDIO_STAT; + reg_status = SDIO_STATUS_REG; while(!(reg_status & (SDIO_FLAG_CCRCERR | SDIO_FLAG_CMDTMOUT | SDIO_FLAG_CMDRECV))){ - reg_status = SDIO_STAT; + reg_status = SDIO_STATUS_REG; } /* check whether an error or timeout occurs or command response received */ if(reg_status & SDIO_FLAG_CCRCERR){ @@ -2560,7 +2874,7 @@ static sd_error_enum sd_card_state_get(uint8_t *pcardstate) return status; } /* clear all the SDIO_INTC flags */ - sdio_flag_clear(SDIO_MASK_INTC_FLAGS); + sdio_flag_clear(SDIO_MASK_CMD_FLAGS); /* get the SDIO response register 0 for checking */ response = sdio_response_get(SDIO_RESPONSE0); *pcardstate = (uint8_t)((response >> 9) & 0x0000000F); @@ -2664,6 +2978,9 @@ static sd_error_enum sd_scr_get(uint16_t rca, uint32_t *pscr) { sd_error_enum status = SD_OK; uint32_t temp_scr[2] = {0, 0}, idx_scr = 0; +#ifdef GD32_SDIO_USING_H77X + LOG_D("sd_scr_get start"); +#endif /* send CMD16(SET_BLOCKLEN) to set block length */ sdio_command_response_config(SD_CMD_SET_BLOCKLEN, (uint32_t)8, SDIO_RESPONSETYPE_SHORT); sdio_wait_type_set(SDIO_WAITTYPE_NO); @@ -2686,7 +3003,7 @@ static sd_error_enum sd_scr_get(uint16_t rca, uint32_t *pscr) /* configure SDIO data */ sdio_data_config(SD_DATATIMEOUT, (uint32_t)8, SDIO_DATABLOCKSIZE_8BYTES); - sdio_data_transfer_config(SDIO_TRANSMODE_BLOCK, SDIO_TRANSDIRECTION_TOSDIO); + sdio_data_transfer_config(SDIO_TRANSDIRECTION_TOSDIO, SDIO_TRANSMODE_BLOCK); sdio_dsm_enable(); /* send ACMD51(SEND_SCR) to read the SD configuration register */ @@ -2700,10 +3017,13 @@ static sd_error_enum sd_scr_get(uint16_t rca, uint32_t *pscr) } /* store the received SCR */ - while(!sdio_flag_get(SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_RXORE | SDIO_FLAG_DTBLKEND | SDIO_FLAG_STBITE)){ - if(RESET != sdio_flag_get(SDIO_FLAG_RXDTVAL)){ - *(temp_scr + idx_scr) = sdio_data_read(); - ++idx_scr; + while(!sdio_flag_get(SDIO_READ_SINGLE_FLAGS)){ + if(SDIO_FIFO_DATA_AVAILABLE()){ + if(idx_scr < 2U){ + temp_scr[idx_scr++] = sdio_data_read(); + }else{ + (void)sdio_data_read(); + } } } @@ -2726,8 +3046,17 @@ static sd_error_enum sd_scr_get(uint16_t rca, uint32_t *pscr) return status; } + if(2U != idx_scr){ +#ifdef GD32_SDIO_USING_H77X + LOG_E("SCR length error: words=%u STAT=0x%08x DATACNT=%u", idx_scr, + SDIO_STATUS_REG, SDIO_DATACNT(SDIO_PERIPH)); +#endif + sdio_flag_clear(SDIO_MASK_DATA_FLAGS); + return SD_ERROR; + } + /* clear all the SDIO_INTC flags */ - sdio_flag_clear(SDIO_MASK_INTC_FLAGS); + sdio_flag_clear(SDIO_MASK_DATA_FLAGS); /* readjust the temp SCR value */ *(pscr) = ((temp_scr[1] & SD_MASK_0_7BITS) << 24) | ((temp_scr[1] & SD_MASK_8_15BITS) << 8) | ((temp_scr[1] & SD_MASK_16_23BITS) >> 8) | ((temp_scr[1] & SD_MASK_24_31BITS) >> 24); @@ -2768,14 +3097,98 @@ static uint32_t sd_datablocksize_get(uint16_t bytesnumber) return DATACTL_BLKSZ(exp_val); } +#ifdef GD32_SDIO_USING_H77X +static int sdio_gpio_level_get(const char *pin_name) +{ + uint32_t port, pin; + rcu_periph_enum gpio_clock; + + if(RT_EOK != get_pin_config(pin_name, &port, &pin, &gpio_clock)){ + return -1; + } + + return (SET == gpio_input_bit_get(port, pin)) ? 1 : 0; +} + +static void sdio_gpio_levels_log(void) +{ + LOG_D("SDIO levels before CMD0: CLK=%d CMD=%d D0=%d D1=%d D2=%d D3=%d", + sdio_gpio_level_get(BSP_SDIO_CLK_PIN), sdio_gpio_level_get(BSP_SDIO_CMD_PIN), + sdio_gpio_level_get(BSP_SDIO_D0_PIN), sdio_gpio_level_get(BSP_SDIO_D1_PIN), + sdio_gpio_level_get(BSP_SDIO_D2_PIN), sdio_gpio_level_get(BSP_SDIO_D3_PIN)); +} + +#ifdef BSP_SDIO_USING_CARD_DETECT +static rt_err_t sdio_card_detect_config(void) +{ + uint32_t port, pin; + rcu_periph_enum gpio_clock; + + if(RT_EOK != get_pin_config(BSP_SDIO_CD_PIN, &port, &pin, &gpio_clock)){ + LOG_E("invalid SD card detect pin: %s", BSP_SDIO_CD_PIN); + return -RT_ERROR; + } + + rcu_periph_clock_enable(gpio_clock); +#ifdef BSP_SDIO_CD_ACTIVE_LOW + gpio_mode_set(port, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, pin); +#else + gpio_mode_set(port, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, pin); +#endif + + return RT_EOK; +} + +static rt_bool_t sdio_card_is_present(void) +{ + int level = sdio_gpio_level_get(BSP_SDIO_CD_PIN); + +#ifdef BSP_SDIO_CD_ACTIVE_LOW + return (0 == level) ? RT_TRUE : RT_FALSE; +#else + return (1 == level) ? RT_TRUE : RT_FALSE; +#endif +} +#endif + +static rt_err_t sdio_gpio_configure(const char *pin_name, const char *af_name, uint32_t pull_up_down) +{ + uint32_t port, pin, alternate; + rcu_periph_enum gpio_clock; + + if ((RT_EOK != get_pin_config(pin_name, &port, &pin, &gpio_clock)) || + (RT_EOK != pin_alternate_config(af_name, &alternate))) { + LOG_E("invalid SDIO pin configuration: %s/%s", pin_name, af_name); + return -RT_ERROR; + } + + rcu_periph_clock_enable(gpio_clock); + gpio_af_set(port, alternate, pin); + gpio_mode_set(port, GPIO_MODE_AF, pull_up_down, pin); + gpio_output_options_set(port, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, pin); + + return RT_EOK; +} +#endif + /*! \brief configure the GPIO of SDIO interface \param[in] none \param[out] none - \retval none + \retval RT_EOK on success, otherwise -RT_ERROR */ -static void gpio_config(void) +static rt_err_t gpio_config(void) { +#ifdef GD32_SDIO_USING_H77X + if ((RT_EOK != sdio_gpio_configure(BSP_SDIO_CLK_PIN, BSP_SDIO_CLK_AFIO, GPIO_PUPD_NONE)) || + (RT_EOK != sdio_gpio_configure(BSP_SDIO_CMD_PIN, BSP_SDIO_CMD_AFIO, GPIO_PUPD_PULLUP)) || + (RT_EOK != sdio_gpio_configure(BSP_SDIO_D0_PIN, BSP_SDIO_D0_AFIO, GPIO_PUPD_PULLUP)) || + (RT_EOK != sdio_gpio_configure(BSP_SDIO_D1_PIN, BSP_SDIO_D1_AFIO, GPIO_PUPD_PULLUP)) || + (RT_EOK != sdio_gpio_configure(BSP_SDIO_D2_PIN, BSP_SDIO_D2_AFIO, GPIO_PUPD_PULLUP)) || + (RT_EOK != sdio_gpio_configure(BSP_SDIO_D3_PIN, BSP_SDIO_D3_AFIO, GPIO_PUPD_PULLUP))) { + return -RT_ERROR; + } +#else /* configure the SDIO_DAT0(PC8), SDIO_DAT1(PC9), SDIO_DAT2(PC10), SDIO_DAT3(PC11), SDIO_CLK(PC12) and SDIO_CMD(PD2) */ gpio_af_set(SDIO_CLK_PORT, GPIO_AF_12, SDIO_CLK_PIN); gpio_af_set(SDIO_CMD_PORT, GPIO_AF_12, SDIO_CMD_PIN); @@ -2801,16 +3214,40 @@ static void gpio_config(void) gpio_mode_set(SDIO_D3_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, SDIO_D3_PIN); gpio_output_options_set(SDIO_D3_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, SDIO_D3_PIN); +#endif + + return RT_EOK; } /*! \brief configure the RCU of SDIO and DMA \param[in] none \param[out] none - \retval none + \retval RT_EOK on success, otherwise error code */ -static void rcu_config(void) +static rt_err_t rcu_config(void) { +#ifdef GD32_SDIO_USING_H77X + static rt_bool_t clock_logged = RT_FALSE; + uint32_t sdio_clock; + + rcu_sdio_clock_config(SDIO_CLOCK_INDEX, SDIO_CLOCK_SOURCE); + rcu_periph_clock_enable(SDIO_PERI_CLOCK); + sdio_clock = rcu_clock_freq_get(SDIO_CLOCK_FREQUENCY); + if(0U == sdio_clock){ + LOG_E("SDIO kernel clock is unavailable"); + return -RT_ERROR; + } + + if(!clock_logged){ +#ifdef BSP_SDIO_USING_SDIO1 + LOG_I("SDIO1 PLL0Q clock: %u Hz", sdio_clock); +#else + LOG_I("SDIO0 PLL0Q clock: %u Hz", sdio_clock); +#endif + clock_logged = RT_TRUE; + } +#else rcu_periph_clock_enable(SDIO_GPIO_CLK); rcu_periph_clock_enable(SDIO_GPIO_CMD); rcu_periph_clock_enable(SDIO_GPIO_D0); @@ -2820,8 +3257,12 @@ static void rcu_config(void) rcu_periph_clock_enable(SDIO_PERI_CLOCK); rcu_periph_clock_enable(SDIO_DMA_CLOCK); +#endif + + return RT_EOK; } +#ifndef GD32_SDIO_USING_H77X /*! \brief configure the DMA1 channel 3 for transferring data \param[in] srcbuf: a pointer point to a buffer which will be transferred @@ -2923,5 +3364,6 @@ void SDIO_DMA_IRQ_HANDLER(void) rt_interrupt_leave(); } #endif /* SDIO_DMA_USE_IPC */ +#endif /* GD32_SDIO_USING_H77X */ #endif /* RT_USING_SDIO */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_sdio.h b/bsp/gd32/arm/libraries/gd32_drivers/drv_sdio.h index 6a54a9c3a625..836c78b33869 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_sdio.h +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_sdio.h @@ -25,9 +25,76 @@ #elif defined SOC_SERIES_GD32F4xx #include "gd32f4xx_sdio.h" #include "gd32f4xx_dma.h" +#elif defined SOC_SERIES_GD32H77x_H78X +#include "gd32h77x_78x_sdio.h" +#define GD32_SDIO_USING_H77X #endif +#ifdef GD32_SDIO_USING_H77X +#ifdef BSP_SDIO_USING_SDIO1 +#define SDIO_PERIPH SDIO1 +#define SDIO_PERI_CLOCK RCU_SDIO1 +#define SDIO_CLOCK_INDEX IDX_SDIO1 +#define SDIO_CLOCK_SOURCE RCU_SDIO1SRC_PLL0Q +#define SDIO_IRQn SDIO1_IRQn +#define SDIO_IRQHandler SDIO1_IRQHandler +#else +#define SDIO_PERIPH SDIO0 +#define SDIO_PERI_CLOCK RCU_SDIO0 +#define SDIO_CLOCK_INDEX IDX_SDIO0 +#define SDIO_CLOCK_SOURCE RCU_SDIO0SRC_PLL0Q +#define SDIO_IRQn SDIO0_IRQn +#define SDIO_IRQHandler SDIO0_IRQHandler +#endif + +#define SDIO_CLOCK_FREQUENCY CK_PLL0Q + +#define SDIO_CLOCKBYPASS_DISABLE ((uint32_t)0U) +#define SDIO_TRANSMODE_BLOCK SDIO_TRANSMODE_BLOCKCOUNT +/* H77x DATSTA is bit 12; the generic H7 header has CMDSTA and DATSTA reversed. */ +#define SDIO_FLAG_RXRUN BIT(12) +#define SDIO_FLAG_TXRUN BIT(12) +#define SDIO_FLAG_STBITE ((uint32_t)0U) +#define SDIO_INT_STBITE ((uint32_t)0U) + +#define SDIO_STATUS_REG SDIO_STAT(SDIO_PERIPH) +#define SDIO_CLOCK_CONTROL_REG SDIO_CLKCTL(SDIO_PERIPH) +#define SDIO_CLOCK_DIVISION_GET() ((SDIO_CLOCK_CONTROL_REG & SDIO_CLKCTL_DIV) * 2U) +#define SDIO_FIFO_DATA_AVAILABLE() ((RESET == sdio_flag_get(SDIO_FLAG_RFE)) && \ + (SET == sdio_flag_get(SDIO_FLAG_DATSTA))) + +#define sdio_deinit() sdio_deinit(SDIO_PERIPH) +#define sdio_clock_config(edge, bypass, powersave, division) sdio_clock_config(SDIO_PERIPH, edge, powersave, division) +#define sdio_hardware_clock_enable() sdio_hardware_clock_enable(SDIO_PERIPH) +#define sdio_hardware_clock_disable() sdio_hardware_clock_disable(SDIO_PERIPH) +#define sdio_bus_mode_set(mode) sdio_bus_mode_set(SDIO_PERIPH, mode) +#define sdio_power_state_set(state) sdio_power_state_set(SDIO_PERIPH, state) +#define sdio_power_state_get() sdio_power_state_get(SDIO_PERIPH) +#define sdio_command_response_config(index, argument, response) sdio_command_response_config(SDIO_PERIPH, index, argument, response) +#define sdio_wait_type_set(type) sdio_wait_type_set(SDIO_PERIPH, type) +#define sdio_csm_enable() sdio_csm_enable(SDIO_PERIPH) +#define sdio_response_get(response) sdio_response_get(SDIO_PERIPH, response) +#define sdio_command_index_get() sdio_command_index_get(SDIO_PERIPH) +#define sdio_data_config(timeout, length, blocksize) sdio_data_config(SDIO_PERIPH, timeout, length, blocksize) +#define sdio_data_transfer_config(direction, mode) sdio_data_transfer_config(SDIO_PERIPH, mode, direction) +#define sdio_dsm_enable() sdio_dsm_enable(SDIO_PERIPH) +#define sdio_dsm_disable() sdio_dsm_disable(SDIO_PERIPH) +#define sdio_transfer_start_enable() sdio_trans_start_enable(SDIO_PERIPH) +#define sdio_transfer_start_disable() sdio_trans_start_disable(SDIO_PERIPH) +#define sdio_data_read() sdio_data_read(SDIO_PERIPH) +#define sdio_data_write(data) sdio_data_write(SDIO_PERIPH, data) +#define sdio_flag_get(flag) sdio_flag_get(SDIO_PERIPH, flag) +#define sdio_flag_clear(flag) sdio_flag_clear(SDIO_PERIPH, flag) +#define sdio_interrupt_enable(flag) sdio_interrupt_enable(SDIO_PERIPH, flag) +#define sdio_interrupt_disable(flag) sdio_interrupt_disable(SDIO_PERIPH, flag) +#define sdio_interrupt_flag_get(flag) sdio_interrupt_flag_get(SDIO_PERIPH, flag) +#define sdio_interrupt_flag_clear(flag) sdio_interrupt_flag_clear(SDIO_PERIPH, flag) +#define sdio_clock_enable() do { } while (0) +#define sdio_dma_enable() do { } while (0) +#define sdio_dma_disable() do { } while (0) +#else #define SDIO_PERI_CLOCK RCU_SDIO + #define SDIO_GPIO_CLK RCU_GPIOC #define SDIO_GPIO_CMD RCU_GPIOD #define SDIO_GPIO_D0 RCU_GPIOC @@ -54,6 +121,15 @@ #define SDIO_DMA_IRQ DMA1_Channel3_IRQn #define SDIO_DMA_IRQ_HANDLER DMA1_Channel3_IRQHandler +#define SDIO_STATUS_REG SDIO_STAT +#define SDIO_CLOCK_CONTROL_REG SDIO_CLKCTL +#define SDIO_CLOCK_DIVISION_GET() ((SDIO_CLOCK_CONTROL_REG & SDIO_CLKCTL_DIV) + \ + (((SDIO_CLOCK_CONTROL_REG & SDIO_CLKCTL_DIV8) >> 31) * 256U) + 2U) +#define SDIO_FIFO_DATA_AVAILABLE() (RESET != sdio_flag_get(SDIO_FLAG_RXDTVAL)) +#define sdio_transfer_start_enable() do { } while (0) +#define sdio_transfer_start_disable() do { } while (0) +#endif + /* SD memory card bus commands index */ #define SD_CMD_GO_IDLE_STATE ((uint8_t)0) /* CMD0, GO_IDLE_STATE */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_sdram.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_sdram.c index 469eb579e683..1954c789bec4 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_sdram.c +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_sdram.c @@ -7,26 +7,40 @@ * Date Author Notes * 2018-12-04 zylx first version * 2023-08-20 yuanzihao adapter gd32f4xx + * 2026-09-08 CYFS initialize SDRAM safely and report readiness and throughput */ #include #ifdef BSP_USING_SDRAM #include +#include #define DRV_DEBUG #define LOG_TAG "drv.sdram" #include +#ifndef SDRAM_HEAP_BEGIN +#define SDRAM_HEAP_BEGIN SDRAM_BANK_ADDR +#endif +#ifndef SDRAM_HEAP_SIZE +#define SDRAM_HEAP_SIZE SDRAM_SIZE +#endif + static exmc_sdram_parameter_struct sdram_init_struct; static exmc_sdram_command_parameter_struct sdram_command_init_struct; static exmc_sdram_timing_parameter_struct sdram_timing_init_struct; +static rt_bool_t sdram_ready; +#if defined(SOC_SERIES_GD32H77x_H78X) +static uint32_t sdram_sdclk_hz; +static uint32_t sdram_refresh_count; +#endif #ifdef RT_USING_MEMHEAP_AS_HEAP static struct rt_memheap system_heap; #endif -static void SDRAM_Initialization_GPIO(void) +rt_weak void rt_hw_sdram_gpio_init(void) { /* enable EXMC clock*/ rcu_periph_clock_enable(RCU_EXMC); @@ -42,14 +56,14 @@ static void SDRAM_Initialization_GPIO(void) /* SDNWE(PC0),SDNE0(PC2),SDCKE0(PC3) pin configuration */ gpio_af_set(GPIOC, GPIO_AF_12, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3); gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3); - gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3); + gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, SDRAM_GPIO_SPEED, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3); /* D2(PD0),D3(PD1),D13(PD8),D14(PD9),D15(PD10),D0(PD14),D1(PD15) pin configuration */ gpio_af_set(GPIOD, GPIO_AF_12, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15); gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15); - gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 | + gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, SDRAM_GPIO_SPEED, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15); /* NBL0(PE0),NBL1(PE1),D4(PE7),D5(PE8),D6(PE9),D7(PE10),D8(PE11),D9(PE12),D10(PE13),D11(PE14),D12(PE15) pin configuration */ @@ -59,7 +73,7 @@ static void SDRAM_Initialization_GPIO(void) gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); - gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_7 | GPIO_PIN_8 | + gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, SDRAM_GPIO_SPEED, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); @@ -70,7 +84,7 @@ static void SDRAM_Initialization_GPIO(void) gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); - gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | + gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, SDRAM_GPIO_SPEED, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); @@ -79,11 +93,61 @@ static void SDRAM_Initialization_GPIO(void) GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15); gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15); - gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | + gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, SDRAM_GPIO_SPEED, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15); } +#if defined(SOC_SERIES_GD32H77x_H78X) +static uint32_t sdram_clock_hz_get(void) +{ + uint32_t clock_hz; + uint32_t divider; + + switch (RCU_CFG4 & RCU_CFG4_EXMCSEL) + { + case RCU_EXMCSRC_AHB: + clock_hz = rcu_clock_freq_get(CK_AHB); + break; + case RCU_EXMCSRC_PLL0Q: + clock_hz = rcu_clock_freq_get(CK_PLL0Q); + break; + case RCU_EXMCSRC_PLL1R: + clock_hz = rcu_clock_freq_get(CK_PLL1R); + break; + case RCU_EXMCSRC_PER: + clock_hz = rcu_clock_freq_get(CK_PER); + break; + default: + return 0U; + } + + switch (EXMC_SDCTL(EXMC_SDRAM_DEVICE0) & + (EXMC_SDCTL_SDCLK | EXMC_SDCTL_SDCLK_2)) + { + case EXMC_SDCLK_PERIODS_2_CK_EXMC: divider = 2U; break; + case EXMC_SDCLK_PERIODS_3_CK_EXMC: divider = 3U; break; + case EXMC_SDCLK_PERIODS_4_CK_EXMC: divider = 4U; break; + case EXMC_SDCLK_PERIODS_5_CK_EXMC: divider = 5U; break; + default: return 0U; + } + return clock_hz / divider; +} + +static uint32_t sdram_refresh_count_get(uint32_t sdclk_hz) +{ + uint32_t cycles = (uint32_t)(((uint64_t)sdclk_hz * SDRAM_REFRESH_PERIOD_MS) / + (1000U * SDRAM_REFRESH_ROWS)); + + /* Round down so refresh never exceeds 64 ms / 8192 rows. */ + if ((cycles <= 20U) || ((cycles - 20U) > (EXMC_SDARI_ARINTV >> 1U))) + { + return 0U; + } + return cycles - 20U; +} +#endif + /** * @brief Perform the SDRAM exernal memory inialization sequence * @param hsdram: SDRAM handle @@ -94,7 +158,7 @@ static rt_err_t SDRAM_Initialization_Sequence(exmc_sdram_parameter_struct *hsdra { __IO uint32_t tmpmrd = 0; uint32_t target_bank = 0; - uint32_t sdram_device = EXMC_SDRAM_DEVICE0; + uint32_t sdram_device = SDRAM_DEVICE; uint32_t command_content = 0; uint32_t timeout = SDRAM_TIMEOUT; @@ -105,7 +169,7 @@ static rt_err_t SDRAM_Initialization_Sequence(exmc_sdram_parameter_struct *hsdra target_bank = EXMC_SDRAM_DEVICE1_SELECT; #endif - SDRAM_Initialization_GPIO(); + rt_hw_sdram_gpio_init(); /* EXMC SDRAM device initialization sequence --------------------------------*/ /* Step 1 : configure SDRAM timing registers --------------------------------*/ /* LMRD: 2 clock cycles */ @@ -132,16 +196,25 @@ static rt_err_t SDRAM_Initialization_Sequence(exmc_sdram_parameter_struct *hsdra sdram_init_struct.cas_latency = SDRAM_CAS_LATENCY; sdram_init_struct.write_protection = DISABLE; sdram_init_struct.sdclock_config = SDCLOCK_PERIOD; - sdram_init_struct.burst_read_switch = ENABLE; + sdram_init_struct.burst_read_switch = SDRAM_BURST_READ; sdram_init_struct.pipeline_read_delay = SDRAM_RPIPE_DELAY; sdram_init_struct.timing = &sdram_timing_init_struct; /* EXMC SDRAM bank initialization */ exmc_sdram_init(&sdram_init_struct); +#if defined(SOC_SERIES_GD32H77x_H78X) + sdram_sdclk_hz = sdram_clock_hz_get(); + sdram_refresh_count = sdram_refresh_count_get(sdram_sdclk_hz); + if (sdram_refresh_count == 0U) + { + LOG_E("invalid SDRAM clock/refresh configuration"); + return -RT_ERROR; + } +#endif /* step 3 : configure CKE high command---------------------------------------*/ sdram_command_init_struct.command = EXMC_SDRAM_CLOCK_ENABLE; sdram_command_init_struct.bank_select = target_bank; - sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_2_SDCLK; + sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK; sdram_command_init_struct.mode_register_content = 0; /* wait until the SDRAM controller is ready */ while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) { @@ -153,13 +226,13 @@ static rt_err_t SDRAM_Initialization_Sequence(exmc_sdram_parameter_struct *hsdra /* send the command */ exmc_sdram_command_config(&sdram_command_init_struct); - /* step 4 : insert 10ms delay----------------------------------------------*/ -// rt_thread_mdelay(10); + /* CKE startup delay must run before PRECHARGE; the scheduler is not running. */ + rt_hw_us_delay(1000U); /* step 5 : configure precharge all command----------------------------------*/ sdram_command_init_struct.command = EXMC_SDRAM_PRECHARGE_ALL; sdram_command_init_struct.bank_select = target_bank; - sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_2_SDCLK; + sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK; sdram_command_init_struct.mode_register_content = 0; /* wait until the SDRAM controller is ready */ timeout = SDRAM_TIMEOUT; @@ -175,7 +248,7 @@ static rt_err_t SDRAM_Initialization_Sequence(exmc_sdram_parameter_struct *hsdra /* step 6 : configure Auto-Refresh command-----------------------------------*/ sdram_command_init_struct.command = EXMC_SDRAM_AUTO_REFRESH; sdram_command_init_struct.bank_select = target_bank; - sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_9_SDCLK; + sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_8_SDCLK; sdram_command_init_struct.mode_register_content = 0; /* wait until the SDRAM controller is ready */ timeout = SDRAM_TIMEOUT; @@ -198,7 +271,7 @@ static rt_err_t SDRAM_Initialization_Sequence(exmc_sdram_parameter_struct *hsdra sdram_command_init_struct.command = EXMC_SDRAM_LOAD_MODE_REGISTER; sdram_command_init_struct.bank_select = target_bank; - sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_2_SDCLK; + sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK; sdram_command_init_struct.mode_register_content = command_content; /* wait until the SDRAM controller is ready */ @@ -214,9 +287,12 @@ static rt_err_t SDRAM_Initialization_Sequence(exmc_sdram_parameter_struct *hsdra /* step 8 : set the auto-refresh rate counter--------------------------------*/ /* 64ms, 8192-cycle refresh, 64ms/8192=7.81us */ - /* SDCLK_Freq = SYS_Freq/2 */ /* (7.81 us * SDCLK_Freq) - 20 */ - exmc_sdram_refresh_count_set(761); +#if defined(SOC_SERIES_GD32H77x_H78X) + exmc_sdram_refresh_count_set(sdram_refresh_count); +#else + exmc_sdram_refresh_count_set(SDRAM_REFRESH_COUNT); +#endif /* wait until the SDRAM controller is ready */ timeout = SDRAM_TIMEOUT; @@ -230,10 +306,37 @@ static rt_err_t SDRAM_Initialization_Sequence(exmc_sdram_parameter_struct *hsdra } +#ifdef RT_USING_MEMHEAP_AS_HEAP +static rt_err_t _sdram_memory_boundary_test(void) +{ + volatile uint32_t *head = (volatile uint32_t *)SDRAM_HEAP_BEGIN; + volatile uint32_t *tail = (volatile uint32_t *)(SDRAM_HEAP_BEGIN + SDRAM_HEAP_SIZE - sizeof(uint32_t)); + + *head = 0x55AA1234U; + *tail = 0xAA55ABCDU; + __DSB(); + + rt_kprintf("sdram boundary test: head=0x%08X, tail=0x%08X\n", *head, *tail); + if ((*head != 0x55AA1234U) || (*tail != 0xAA55ABCDU)) + { + LOG_E("SDRAM boundary test failed!"); + return -RT_ERROR; + } + + return RT_EOK; +} +#endif + +rt_bool_t rt_hw_sdram_is_ready(void) +{ + return sdram_ready; +} + static int SDRAM_Init(void) { int result = RT_EOK; + sdram_ready = RT_FALSE; /* Initialize the SDRAM controller */ if (SDRAM_Initialization_Sequence(&sdram_init_struct, &sdram_command_init_struct) != RT_EOK) { @@ -242,87 +345,164 @@ static int SDRAM_Init(void) } else { - rt_kprintf("sdram init success, mapped at 0x%X, size is %d bytes, data width is %d", SDRAM_BANK_ADDR, SDRAM_SIZE, SDRAM_DATA_WIDTH); + rt_kprintf("sdram controller init success, mapped at 0x%X, size is %d bytes, data width is %d\n", + SDRAM_BANK_ADDR, SDRAM_SIZE, SDRAM_DATA_WIDTH); +#if defined(SOC_SERIES_GD32H77x_H78X) + rt_kprintf("sdram actual sdclk=%u Hz refresh_count=%u\n", + sdram_sdclk_hz, sdram_refresh_count); +#endif #ifdef RT_USING_MEMHEAP_AS_HEAP - /* If RT_USING_MEMHEAP_AS_HEAP is enabled, SDRAM is initialized to the heap */ - rt_memheap_init(&system_heap, "sdram", (void *)SDRAM_BANK_ADDR, SDRAM_SIZE); + if (_sdram_memory_boundary_test() != RT_EOK) + { + result = -RT_ERROR; + } + else if (rt_memheap_init(&system_heap, "sdram", (void *)SDRAM_HEAP_BEGIN, SDRAM_HEAP_SIZE) != RT_EOK) + { + LOG_E("SDRAM memheap init failed!"); + result = -RT_ERROR; + } + else + { + rt_kprintf("sdram memheap init success, mapped at 0x%X, size is %d bytes\n", + SDRAM_HEAP_BEGIN, SDRAM_HEAP_SIZE); + } #endif } + sdram_ready = (result == RT_EOK); return result; } INIT_BOARD_EXPORT(SDRAM_Init); #ifdef DRV_DEBUG #ifdef FINSH_USING_MSH -int sdram_test(void) -{ - int i = 0; - uint32_t start_time = 0, time_cast = 0; +#ifdef RT_USING_MEMHEAP_AS_HEAP +#define SDRAM_TEST_SIZE (1024U * 1024U) +#define SDRAM_TEST_PASSES 8U + #if SDRAM_DATA_WIDTH_IN_NUMBER == 8 - char data_width = 1; - uint8_t data = 0; +typedef uint8_t sdram_test_data_t; #elif SDRAM_DATA_WIDTH_IN_NUMBER == 16 - char data_width = 2; - uint16_t data = 0; +typedef uint16_t sdram_test_data_t; #else - char data_width = 4; - uint32_t data = 0; +typedef uint32_t sdram_test_data_t; #endif - /* write data */ - LOG_D("Writing the %ld bytes data, waiting....", SDRAM_SIZE); - start_time = rt_tick_get(); - for (i = 0; i < SDRAM_SIZE / data_width; i++) +static void sdram_test_print_speed(const char *operation, rt_tick_t ticks) +{ + uint32_t elapsed_ms; + uint32_t speed; + + if (ticks == 0U) { -#if SDRAM_DATA_WIDTH_IN_NUMBER == 8 - *(__IO uint8_t *)(SDRAM_BANK_ADDR + i * data_width) = (uint8_t)0x55; -#elif SDRAM_DATA_WIDTH_IN_NUMBER == 16 - *(__IO uint16_t *)(SDRAM_BANK_ADDR + i * data_width) = (uint16_t)0x5555; -#else - *(__IO uint32_t *)(SDRAM_BANK_ADDR + i * data_width) = (uint32_t)0x55555555; -#endif + rt_kprintf("%-8s %10u <1 tick N/A\n", operation, + SDRAM_TEST_SIZE * SDRAM_TEST_PASSES / 1024U); + return; } - time_cast = rt_tick_get() - start_time; - LOG_D("Write data success, total time: %d.%03dS.", time_cast / RT_TICK_PER_SECOND, - time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000)); - /* read data */ - LOG_D("start Reading and verifying data, waiting...."); - for (i = 0; i < SDRAM_SIZE / data_width; i++) + elapsed_ms = (uint32_t)((uint64_t)ticks * 1000U / RT_TICK_PER_SECOND); + speed = (uint32_t)((uint64_t)SDRAM_TEST_SIZE * SDRAM_TEST_PASSES * + RT_TICK_PER_SECOND * 100U / ((uint64_t)ticks * 1024U * 1024U)); + rt_kprintf("%-8s %10u %10u %8u.%02u\n", operation, + SDRAM_TEST_SIZE * SDRAM_TEST_PASSES / 1024U, + elapsed_ms, speed / 100U, speed % 100U); +} +#endif /* RT_USING_MEMHEAP_AS_HEAP */ + +int sdram_test(void) +{ +#ifdef RT_USING_MEMHEAP_AS_HEAP + void *allocation; + volatile sdram_test_data_t *buffer; + sdram_test_data_t data; + sdram_test_data_t expected; + uint32_t i; + uint32_t pass; + rt_tick_t start_time; + rt_tick_t write_ticks; + rt_tick_t read_ticks; + int result = RT_EOK; + + if (!sdram_ready) { -#if SDRAM_DATA_WIDTH_IN_NUMBER == 8 - data = *(__IO uint8_t *)(SDRAM_BANK_ADDR + i * data_width); - if (data != 0x55) + rt_kprintf("SDRAM test failed: SDRAM is not ready.\n"); + return -RT_ERROR; + } + + /* Allocate from SDRAM explicitly and keep cache operations off heap metadata. */ + allocation = rt_memheap_alloc(&system_heap, SDRAM_TEST_SIZE + RT_CPU_CACHE_LINE_SZ - 1U); + if (allocation == RT_NULL) + { + rt_kprintf("SDRAM test failed: cannot allocate %u KiB from the SDRAM heap.\n", + SDRAM_TEST_SIZE / 1024U); + return -RT_ENOMEM; + } + buffer = (volatile sdram_test_data_t *)RT_ALIGN((rt_uintptr_t)allocation, + RT_CPU_CACHE_LINE_SZ); + + rt_kprintf("SDRAM test: buffer=%p, size=%u KiB, access=%u-bit, passes=%u\n", + (void *)buffer, SDRAM_TEST_SIZE / 1024U, + (unsigned int)(sizeof(*buffer) * 8U), SDRAM_TEST_PASSES); + rt_kprintf("CPU sequential access; timing includes cache maintenance.\n"); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH | RT_HW_CACHE_INVALIDATE, + (void *)buffer, SDRAM_TEST_SIZE); + __DSB(); + + start_time = rt_tick_get(); + for (pass = 0; pass < SDRAM_TEST_PASSES; pass++) + { + for (i = 0; i < SDRAM_TEST_SIZE / sizeof(*buffer); i++) { - LOG_E("SDRAM test failed!"); - break; + buffer[i] = (sdram_test_data_t)(i ^ 0x55555555U); } -#elif SDRAM_DATA_WIDTH_IN_NUMBER == 16 - data = *(__IO uint16_t *)(SDRAM_BANK_ADDR + i * data_width); - if (data != 0x5555) + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, (void *)buffer, SDRAM_TEST_SIZE); + } + __DSB(); + write_ticks = rt_tick_get() - start_time; + + start_time = rt_tick_get(); + for (pass = 0; pass < SDRAM_TEST_PASSES; pass++) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, (void *)buffer, SDRAM_TEST_SIZE); + for (i = 0; i < SDRAM_TEST_SIZE / sizeof(*buffer); i++) { - LOG_E("SDRAM test failed!"); - break; + (void)buffer[i]; } -#else - data = *(__IO uint32_t *)(SDRAM_BANK_ADDR + i * data_width); - if (data != 0x55555555) + } + __DSB(); + read_ticks = rt_tick_get() - start_time; + + rt_kprintf("Operation Total KiB Time ms MiB/s\n"); + sdram_test_print_speed("Write", write_ticks); + sdram_test_print_speed("Read", read_ticks); + + /* Verify separately so comparisons are not counted as read throughput. */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, (void *)buffer, SDRAM_TEST_SIZE); + for (i = 0; i < SDRAM_TEST_SIZE / sizeof(*buffer); i++) + { + expected = (sdram_test_data_t)(i ^ 0x55555555U); + data = buffer[i]; + if (data != expected) { - LOG_E("SDRAM test failed!"); + rt_kprintf("Verify: FAIL at %p, expected=0x%08X, actual=0x%08X\n", + (void *)&buffer[i], (unsigned int)expected, (unsigned int)data); + result = -RT_ERROR; break; } -#endif } - if (i >= SDRAM_SIZE / data_width) + rt_memheap_free(allocation); + if (result == RT_EOK) { - LOG_D("SDRAM test success!"); + rt_kprintf("Verify: PASS\n"); } - - return RT_EOK; + return result; +#else + rt_kprintf("SDRAM test requires RT_USING_MEMHEAP_AS_HEAP.\n"); + return -RT_ENOSYS; +#endif /* RT_USING_MEMHEAP_AS_HEAP */ } -MSH_CMD_EXPORT(sdram_test, sdram test) +MSH_CMD_EXPORT(sdram_test, test SDRAM read/write speed and data integrity); #endif /* FINSH_USING_MSH */ #endif /* DRV_DEBUG */ #endif /* BSP_USING_SDRAM */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_spi.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_spi.c index 6c0a12eb262c..eac84f8b5b3d 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_spi.c +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_spi.c @@ -8,6 +8,7 @@ * 2021-12-20 BruceOu first implementation */ #include "drv_spi.h" +#include "spi_config.h" #ifdef RT_USING_SPI @@ -36,140 +37,23 @@ static struct rt_spi_bus spi_bus5; #endif static const struct gd32_spi spi_bus_obj[] = { - #ifdef BSP_USING_SPI0 - { - SPI0, - "spi0", - RCU_SPI0, - RCU_GPIOA, - RCU_GPIOA, - RCU_GPIOA, - &spi_bus0, - GPIOA, - GPIOA, - GPIOA, -#if defined (SOC_SERIES_GD32F4xx) || defined (SOC_SERIES_GD32H7xx) || (defined SOC_SERIES_GD32F5xx) - GPIO_AF_5, -#endif -#if defined (SOC_SERIES_GD32E23x) - GPIO_AF_0, -#endif - GPIO_PIN_5, - GPIO_PIN_6, - GPIO_PIN_7, - }, + SPI0_BUS_CONFIG, #endif /* BSP_USING_SPI0 */ - #ifdef BSP_USING_SPI1 - { - SPI1, - "spi1", - RCU_SPI1, - RCU_GPIOB, - RCU_GPIOB, - RCU_GPIOB, - &spi_bus1, - GPIOB, - GPIOB, - GPIOB, -#if defined (SOC_SERIES_GD32F4xx) || defined (SOC_SERIES_GD32H7xx) || (defined SOC_SERIES_GD32F5xx) - GPIO_AF_5, -#endif -#if defined SOC_SERIES_GD32E23x - GPIO_AF_0, -#endif - - GPIO_PIN_13, - GPIO_PIN_14, - GPIO_PIN_15, - }, + SPI1_BUS_CONFIG, #endif /* BSP_USING_SPI1 */ - #ifdef BSP_USING_SPI2 - { - SPI2, - "spi2", - RCU_SPI2, - RCU_GPIOB, - RCU_GPIOB, - RCU_GPIOB, - &spi_bus2, - GPIOB, - GPIOB, - GPIOB, -#if defined (SOC_SERIES_GD32F4xx) || defined (SOC_SERIES_GD32H7xx) || (defined SOC_SERIES_GD32F5xx) - GPIO_AF_6, -#endif - GPIO_PIN_3, - GPIO_PIN_4, - GPIO_PIN_5, - }, + SPI2_BUS_CONFIG, #endif /* BSP_USING_SPI2 */ - #ifdef BSP_USING_SPI3 - { - SPI3, - "spi3", - RCU_SPI3, - RCU_GPIOE, - RCU_GPIOE, - RCU_GPIOE, - &spi_bus3, - GPIOE, - GPIOE, - GPIOE, -#if defined (SOC_SERIES_GD32F4xx) || defined (SOC_SERIES_GD32H7xx) || (defined SOC_SERIES_GD32F5xx) - GPIO_AF_5, -#endif - GPIO_PIN_2, - GPIO_PIN_5, - GPIO_PIN_6, - }, + SPI3_BUS_CONFIG, #endif /* BSP_USING_SPI3 */ - #ifdef BSP_USING_SPI4 - { - SPI4, - "spi4", - RCU_SPI4, - RCU_GPIOF, - RCU_GPIOF, - RCU_GPIOF, - &spi_bus4, - GPIOF, - GPIOF, - GPIOF, -#if defined (SOC_SERIES_GD32F4xx) || defined (SOC_SERIES_GD32H7xx) || (defined SOC_SERIES_GD32F5xx) - GPIO_AF_5, -#endif - GPIO_PIN_7, - GPIO_PIN_8, - GPIO_PIN_9, - - }, + SPI4_BUS_CONFIG, #endif /* BSP_USING_SPI4 */ - #ifdef BSP_USING_SPI5 - { - SPI5, - "spi5", - RCU_SPI5, - RCU_GPIOG, - RCU_GPIOG, - - RCU_GPIOG, - &spi_bus5, - GPIOG, - GPIOG, - GPIOG, -#if defined (SOC_SERIES_GD32F4xx) || defined (SOC_SERIES_GD32H7xx) || (defined SOC_SERIES_GD32F5xx) - GPIO_AF_5, -#endif - GPIO_PIN_13, - GPIO_PIN_12, - GPIO_PIN_14, - } + SPI5_BUS_CONFIG, #endif /* BSP_USING_SPI5 */ }; @@ -190,42 +74,68 @@ static struct rt_spi_ops gd32_spi_ops = */ static void gd32_spi_init(struct gd32_spi *gd32_spi) { + rt_uint32_t sck_port, miso_port, mosi_port; + rt_uint32_t sck_pin, miso_pin, mosi_pin; + rcu_periph_enum sck_periph, miso_periph, mosi_periph; +#if defined (SOC_SERIES_GD32F4xx) || (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined (SOC_SERIES_GD32F5xx) || defined (SOC_SERIES_GD32E23x) + rt_uint32_t sck_af, miso_af, mosi_af; +#endif + + if (get_pin_config(gd32_spi->sck_pin_name, &sck_port, &sck_pin, &sck_periph) != RT_EOK) + { + return; + } + if (get_pin_config(gd32_spi->miso_pin_name, &miso_port, &miso_pin, &miso_periph) != RT_EOK) + { + return; + } + if (get_pin_config(gd32_spi->mosi_pin_name, &mosi_port, &mosi_pin, &mosi_periph) != RT_EOK) + { + return; + } + /* enable SPI clock */ rcu_periph_clock_enable(gd32_spi->spi_clk); - rcu_periph_clock_enable(gd32_spi->sck_gpio_clk); - rcu_periph_clock_enable(gd32_spi->miso_gpio_clk); - rcu_periph_clock_enable(gd32_spi->mosi_gpio_clk); - -#if defined (SOC_SERIES_GD32F4xx) || defined (SOC_SERIES_GD32H7xx) || (defined SOC_SERIES_GD32F5xx) || (defined SOC_SERIES_GD32E23x) - /*GPIO pin configuration*/ - gpio_af_set(gd32_spi->sck_spi_port, gd32_spi->alt_func_num, gd32_spi->sck_pin); - gpio_af_set(gd32_spi->miso_spi_port, gd32_spi->alt_func_num, gd32_spi->miso_pin); - gpio_af_set(gd32_spi->mosi_spi_port, gd32_spi->alt_func_num, gd32_spi->mosi_pin); - gpio_mode_set(gd32_spi->sck_spi_port, GPIO_MODE_AF, GPIO_PUPD_NONE, gd32_spi->sck_pin); - gpio_mode_set(gd32_spi->miso_spi_port, GPIO_MODE_AF, GPIO_PUPD_NONE, gd32_spi->miso_pin); - gpio_mode_set(gd32_spi->mosi_spi_port, GPIO_MODE_AF, GPIO_PUPD_NONE, gd32_spi->mosi_pin); - #if defined (SOC_SERIES_GD32H7xx) - gpio_output_options_set(gd32_spi->sck_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, gd32_spi->sck_pin); - gpio_output_options_set(gd32_spi->miso_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, gd32_spi->miso_pin); - gpio_output_options_set(gd32_spi->mosi_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, gd32_spi->mosi_pin); - #elif defined (SOC_SERIES_GD32E23x) - gpio_output_options_set(gd32_spi->sck_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, gd32_spi->sck_pin); - gpio_output_options_set(gd32_spi->miso_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, gd32_spi->miso_pin); - gpio_output_options_set(gd32_spi->mosi_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, gd32_spi->mosi_pin); - #else - gpio_output_options_set(gd32_spi->sck_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, gd32_spi->sck_pin); - gpio_output_options_set(gd32_spi->miso_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, gd32_spi->miso_pin); - gpio_output_options_set(gd32_spi->mosi_spi_port, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, gd32_spi->mosi_pin); - #endif + rcu_periph_clock_enable(sck_periph); + rcu_periph_clock_enable(miso_periph); + rcu_periph_clock_enable(mosi_periph); + +#if defined (SOC_SERIES_GD32F4xx) || (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined (SOC_SERIES_GD32F5xx) || defined (SOC_SERIES_GD32E23x) + if (pin_alternate_config(gd32_spi->sck_alternate, &sck_af) != RT_EOK || + pin_alternate_config(gd32_spi->miso_alternate, &miso_af) != RT_EOK || + pin_alternate_config(gd32_spi->mosi_alternate, &mosi_af) != RT_EOK) + { + return; + } + + /* GPIO pin configuration */ + gpio_af_set(sck_port, sck_af, sck_pin); + gpio_af_set(miso_port, miso_af, miso_pin); + gpio_af_set(mosi_port, mosi_af, mosi_pin); + gpio_mode_set(sck_port, GPIO_MODE_AF, GPIO_PUPD_NONE, sck_pin); + gpio_mode_set(miso_port, GPIO_MODE_AF, GPIO_PUPD_NONE, miso_pin); + gpio_mode_set(mosi_port, GPIO_MODE_AF, GPIO_PUPD_NONE, mosi_pin); +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) + gpio_output_options_set(sck_port, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, sck_pin); + gpio_output_options_set(miso_port, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, miso_pin); + gpio_output_options_set(mosi_port, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, mosi_pin); +#elif defined (SOC_SERIES_GD32E23x) + gpio_output_options_set(sck_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, sck_pin); + gpio_output_options_set(miso_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, miso_pin); + gpio_output_options_set(mosi_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, mosi_pin); +#else + gpio_output_options_set(sck_port, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, sck_pin); + gpio_output_options_set(miso_port, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, miso_pin); + gpio_output_options_set(mosi_port, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, mosi_pin); +#endif #else /* Init SPI SCK MOSI */ - gpio_init(gd32_spi->sck_spi_port, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, gd32_spi->sck_pin); - gpio_init(gd32_spi->mosi_spi_port, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, gd32_spi->mosi_pin); + gpio_init(sck_port, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, sck_pin); + gpio_init(mosi_port, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, mosi_pin); /* Init SPI MISO */ - gpio_init(gd32_spi->miso_spi_port, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, gd32_spi->miso_pin); + gpio_init(miso_port, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, miso_pin); #endif - } static rt_err_t spi_configure(struct rt_spi_device* device, @@ -242,7 +152,7 @@ static rt_err_t spi_configure(struct rt_spi_device* device, /* Init SPI */ gd32_spi_init(spi_device); -#if defined SOC_SERIES_GD32H7xx +#if (defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H77x_H78X) /* data_width */ if(configuration->data_width >=4 && configuration->data_width <= 32) { @@ -360,6 +270,11 @@ static rt_err_t spi_configure(struct rt_spi_device* device, spi_init_struct.nss = SPI_NSS_SOFT; spi_crc_off(spi_periph); +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) + /* enable byte access and NSS output in master mode */ + spi_byte_access_enable(spi_periph); + spi_nss_output_enable(spi_periph); +#endif /* init SPI */ spi_init(spi_periph, &spi_init_struct); @@ -418,20 +333,21 @@ static rt_ssize_t spixfer(struct rt_spi_device* device, struct rt_spi_message* m /* Todo: replace register read/write by gd32f4 lib */ /* Wait until the transmit buffer is empty */ - #if defined (SOC_SERIES_GD32H7xx) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) + spi_master_transfer_start(spi_periph, SPI_TRANS_START); while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TP)); - #else +#else while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TBE)); - #endif +#endif /* Send the byte */ spi_i2s_data_transmit(spi_periph, data); /* Wait until a data is received */ - #if defined (SOC_SERIES_GD32H7xx) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RP)); - #else +#else while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RBNE)); - #endif +#endif /* Get the received data */ data = spi_i2s_data_receive(spi_periph); @@ -458,20 +374,21 @@ static rt_ssize_t spixfer(struct rt_spi_device* device, struct rt_spi_message* m } /* Wait until the transmit buffer is empty */ - #if defined (SOC_SERIES_GD32H7xx) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) + spi_master_transfer_start(spi_periph, SPI_TRANS_START); while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TP)); - #else +#else while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TBE)); - #endif +#endif /* Send the byte */ spi_i2s_data_transmit(spi_periph, data); /* Wait until a data is received */ - #if defined (SOC_SERIES_GD32H7xx) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RP)); - #else +#else while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RBNE)); - #endif +#endif /* Get the received data */ data = spi_i2s_data_receive(spi_periph); @@ -481,13 +398,14 @@ static rt_ssize_t spixfer(struct rt_spi_device* device, struct rt_spi_message* m } } } - #if defined SOC_SERIES_GD32H7xx +#if (defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32H77x_H78X) else if(config->data_width <= 32) { const rt_uint32_t * send_ptr = message->send_buf; rt_uint32_t * recv_ptr = message->recv_buf; rt_uint32_t size = message->length; + spi_master_transfer_start(spi_periph, SPI_TRANS_START); while(size--) { rt_uint32_t data = 0xFF; @@ -513,7 +431,7 @@ static rt_ssize_t spixfer(struct rt_spi_device* device, struct rt_spi_message* m } } } - #endif +#endif } /* release CS */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_spi.h b/bsp/gd32/arm/libraries/gd32_drivers/drv_spi.h index 6cca0755c70b..56bb16f82c8e 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_spi.h +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_spi.h @@ -32,19 +32,13 @@ struct gd32_spi uint32_t spi_periph; char *bus_name; rcu_periph_enum spi_clk; - rcu_periph_enum sck_gpio_clk; - rcu_periph_enum miso_gpio_clk; - rcu_periph_enum mosi_gpio_clk; struct rt_spi_bus *spi_bus; - uint32_t sck_spi_port; - uint32_t miso_spi_port; - uint32_t mosi_spi_port; -#if defined SOC_SERIES_GD32F4xx || defined SOC_SERIES_GD32E23x || defined SOC_SERIES_GD32H7xx || defined SOC_SERIES_GD32F5xx - uint32_t alt_func_num; -#endif - uint16_t sck_pin; - uint16_t miso_pin; - uint16_t mosi_pin; + const char *sck_pin_name; + const char *miso_pin_name; + const char *mosi_pin_name; + const char *sck_alternate; + const char *miso_alternate; + const char *mosi_alternate; }; rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, rt_base_t cs_pin); diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_usart.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_usart.c index 5cb9d6b9dd64..834cc0b8e4b8 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_usart.c +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_usart.c @@ -25,7 +25,7 @@ #if defined(SOC_SERIES_GD32E50x) || defined(SOC_SERIES_GD32F10x) || defined(SOC_SERIES_GD32F20x) || defined(SOC_SERIES_GD32F30x) #define GPIO_OSPEED GPIO_OSPEED_50MHZ -#elif defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H75E) +#elif (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined(SOC_SERIES_GD32H75E) #define GPIO_OTYPE GPIO_OTYPE_PP #define GPIO_OSPEED GPIO_OSPEED_60MHZ #elif defined(SOC_SERIES_GD32F4xx) || defined(SOC_SERIES_GD32F5xx) || defined(SOC_SERIES_GD32E23x) @@ -52,7 +52,7 @@ struct rt_serial_device serial0; gd32_uart_dma uart0_rxdma = { DMA0, DMA_CH0, -#if defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H75E) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined(SOC_SERIES_GD32H75E) DMA_REQUEST_USART0_RX, #endif DMA_INTF_FTFIF, @@ -62,7 +62,7 @@ gd32_uart_dma uart0_rxdma = { gd32_uart_dma uart0_txdma = { DMA1, DMA_CH0, -#if defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H75E) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined(SOC_SERIES_GD32H75E) DMA_REQUEST_USART0_TX, #endif DMA_INTF_FTFIF, @@ -116,7 +116,7 @@ struct rt_serial_device serial1; gd32_uart_dma uart1_rxdma = { DMA0, DMA_CH1, -#if defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H75E) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined(SOC_SERIES_GD32H75E) DMA_REQUEST_USART1_RX, #endif DMA_INTF_FTFIF, @@ -156,7 +156,7 @@ struct rt_serial_device serial2; gd32_uart_dma uart2_rxdma = { DMA0, DMA_CH2, -#if defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H75E) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined(SOC_SERIES_GD32H75E) DMA_REQUEST_USART2_RX, #endif DMA_INTF_FTFIF, @@ -166,7 +166,7 @@ gd32_uart_dma uart2_rxdma = { gd32_uart_dma uart2_txdma = { DMA1, DMA_CH2, -#if defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H75E) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined(SOC_SERIES_GD32H75E) DMA_REQUEST_USART2_TX, #endif DMA_INTF_FTFIF, @@ -220,7 +220,7 @@ struct rt_serial_device serial3; gd32_uart_dma uart3_rxdma = { DMA0, DMA_CH3, -#if defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H75E) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined(SOC_SERIES_GD32H75E) DMA_REQUEST_UART3_RX, #endif DMA_INTF_FTFIF, @@ -260,7 +260,7 @@ struct rt_serial_device serial4; gd32_uart_dma uart4_rxdma = { DMA0, DMA_CH4, -#if defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H75E) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined(SOC_SERIES_GD32H75E) DMA_REQUEST_UART4_RX, #endif DMA_INTF_FTFIF, @@ -299,7 +299,7 @@ struct rt_serial_device serial5; gd32_uart_dma uart5_rxdma = { DMA0, DMA_CH5, -#if defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H75E) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined(SOC_SERIES_GD32H75E) DMA_REQUEST_USART5_RX, #endif DMA_INTF_FTFIF, @@ -339,7 +339,7 @@ struct rt_serial_device serial6; gd32_uart_dma uart6_rxdma = { DMA0, DMA_CH6, -#if defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H75E) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined(SOC_SERIES_GD32H75E) DMA_REQUEST_UART6_RX, #endif DMA_INTF_FTFIF, @@ -379,7 +379,7 @@ struct rt_serial_device serial7; gd32_uart_dma uart7_rxdma = { DMA0, DMA_CH7, -#if defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H75E) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined(SOC_SERIES_GD32H75E) DMA_REQUEST_UART7_RX, #endif DMA_INTF_FTFIF, diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_usart.h b/bsp/gd32/arm/libraries/gd32_drivers/drv_usart.h index 8257a879dd37..95fe7b34bc0f 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_usart.h +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_usart.h @@ -35,7 +35,7 @@ typedef struct uint32_t dma_periph; /* dma channel */ dma_channel_enum dma_ch; -#if defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H75E) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) || defined(SOC_SERIES_GD32H75E) /* rx dma request */ uint32_t dma_mux_req_rx; #endif diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_wdt.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_wdt.c index 6f218e2b16be..6211cf88b1b7 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_wdt.c +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_wdt.c @@ -47,7 +47,7 @@ static gd32_wdt_device_t g_wdt_dev; static rt_err_t gd32_wdt_init(rt_watchdog_t *wdt) { -#if defined(SOC_SERIES_GD32H7xx) +#if (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) /* Enable and wait for oscillator stabilization */ rcu_osci_on(WDT_OSCI_TYPE); if (ERROR == rcu_osci_stab_wait(WDT_OSCI_TYPE)) diff --git a/bsp/gd32/arm/libraries/gd32_drivers/sdram_port.h b/bsp/gd32/arm/libraries/gd32_drivers/sdram_port.h index d762b0dd9a7e..d65882765975 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/sdram_port.h +++ b/bsp/gd32/arm/libraries/gd32_drivers/sdram_port.h @@ -27,24 +27,56 @@ #define SDRAM_ROW_BITS EXMC_SDRAM_ROW_ADDRESS_13 /* cas latency clock number: 1, 2, 3 */ #define SDRAM_CAS_LATENCY EXMC_CAS_LATENCY_3_SDCLK + +#if defined(SOC_SERIES_GD32H77x_H78X) && defined(BSP_USING_OV7670) +/* Keep the camera configuration validated with internal rendering and IPA. */ +#define SDRAM_BURST_READ DISABLE +#else +#define SDRAM_BURST_READ ENABLE +#endif + /* read pipe delay: 0, 1, 2 */ +#if defined(SOC_SERIES_GD32H77x_H78X) +/* The Gino board port selects CK_AHB (300 MHz). + * Refresh is derived from the actual clock, not a nominal PLL target. */ +#define SDRAM_USE_PLL1R_EXMC 0 +#define SDRAM_REFRESH_PERIOD_MS 64U +#define SDRAM_REFRESH_ROWS 8192U +#if defined(BSP_USING_OV7670) +#define SDRAM_RPIPE_DELAY EXMC_PIPELINE_DELAY_2_CK_EXMC +#else +#define SDRAM_RPIPE_DELAY EXMC_PIPELINE_DELAY_1_CK_EXMC +#endif +#if defined(BSP_USING_OV7670) +/* CK_AHB / 3 = 100 MHz; non-camera projects retain their 150 MHz clock. */ +#define SDCLOCK_PERIOD EXMC_SDCLK_PERIODS_3_CK_EXMC +#else +#define SDCLOCK_PERIOD EXMC_SDCLK_PERIODS_2_CK_EXMC +#endif +#define SDRAM_GPIO_SPEED GPIO_OSPEED_85MHZ + +/* Keep the 150 MHz timing-cycle counts; 100 MHz increases their duration. */ +#define LOADTOACTIVEDELAY 2 +#define EXITSELFREFRESHDELAY 15 +#define SELFREFRESHTIME 14 +#define ROWCYCLEDELAY 9 +#define WRITERECOVERYTIME 3 +#define RPDELAY 4 +#define RCDDELAY 4 +#else #define SDRAM_RPIPE_DELAY EXMC_PIPELINE_DELAY_2_HCLK -/* clock divid: 2, 3 */ #define SDCLOCK_PERIOD EXMC_SDCLK_PERIODS_3_HCLK -/* refresh rate counter */ +#define SDRAM_GPIO_SPEED GPIO_OSPEED_50MHZ #define SDRAM_REFRESH_COUNT ((uint32_t)0x02A5) -#define SDRAM_SIZE ((uint32_t)0x2000000) -#define SDRAM_TIMEOUT ((uint32_t)0x0000FFFF) /* Timing configuration for W9825G6KH-6 */ -/* 100 MHz of HCKL3 clock frequency (200MHz/2) */ /* TMRD: 2 Clock cycles */ #define LOADTOACTIVEDELAY 2 /* TXSR: 8x10ns */ #define EXITSELFREFRESHDELAY 8 -/* TRAS: 5x10ns */ +/* TRC: 7x10ns */ #define SELFREFRESHTIME 7 -/* TRC: 7x10ns */ +/* TRAS: 5x10ns */ #define ROWCYCLEDELAY 5 /* TWR: 2 Clock cycles */ #define WRITERECOVERYTIME 2 @@ -52,6 +84,10 @@ #define RPDELAY 3 /* TRCD: 2x10ns */ #define RCDDELAY 3 +#endif + +#define SDRAM_SIZE ((uint32_t)0x2000000) +#define SDRAM_TIMEOUT ((uint32_t)0x0000FFFF) /* memory mode register */ #define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000) @@ -66,4 +102,6 @@ #define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000) #define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200) +void rt_hw_sdram_gpio_init(void); + #endif diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_otghs_dev.c b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_otghs_dev.c new file mode 100644 index 000000000000..cbe501fa8a41 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_otghs_dev.c @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#include "drv_otghs_dev.h" +#include +#include +#include "board.h" +#include "drv_usb_hw.h" + +#ifdef RT_USING_FINSH +#include +#endif + +#if defined(BSP_USING_USB) && defined(RT_USING_USB_DEVICE) + +usb_core_driver gd_usb_device_core; +#define gd_usb_core gd_usb_device_core + +static struct udcd _gd_udc; + +volatile rt_uint32_t gd_usb_irq_count; +volatile rt_uint32_t gd_usb_reset_count; +volatile rt_uint32_t gd_usb_enum_count; +volatile rt_uint32_t gd_usb_setup_count; +volatile rt_uint32_t gd_usb_ep0_in_count; +volatile rt_uint32_t gd_usb_ep0_out_count; +volatile rt_uint32_t gd_usb_rxfifo_count; +volatile rt_uint32_t gd_usb_setup_rx_count; +volatile rt_uint32_t gd_usb_last_rx_status; + +#if defined(BSP_USB_DEVICE_USING_FS) +static struct ep_id _ep_pool[] = { + { 0x0, USB_EP_ATTR_CONTROL, USB_DIR_INOUT, 64, ID_ASSIGNED }, + { 0x1, USB_EP_ATTR_BULK, USB_DIR_IN, 64, ID_UNASSIGNED }, + { 0x1, USB_EP_ATTR_BULK, USB_DIR_OUT, 64, ID_UNASSIGNED }, + { 0x2, USB_EP_ATTR_INT, USB_DIR_IN, 64, ID_UNASSIGNED }, + { 0x2, USB_EP_ATTR_INT, USB_DIR_OUT, 64, ID_UNASSIGNED }, + { 0x3, USB_EP_ATTR_BULK, USB_DIR_IN, 64, ID_UNASSIGNED }, + { 0x3, USB_EP_ATTR_BULK, USB_DIR_OUT, 64, ID_UNASSIGNED }, + { 0xFF, USB_EP_ATTR_TYPE_MASK, USB_DIR_MASK, 0, ID_ASSIGNED }, +}; +#elif defined(BSP_USB_DEVICE_USING_HS) +static struct ep_id _ep_pool[] = { + { 0x0, USB_EP_ATTR_CONTROL, USB_DIR_INOUT, 64, ID_ASSIGNED }, + { 0x1, USB_EP_ATTR_BULK, USB_DIR_IN, 64, ID_UNASSIGNED }, + { 0x1, USB_EP_ATTR_BULK, USB_DIR_OUT, 512, ID_UNASSIGNED }, + { 0x2, USB_EP_ATTR_INT, USB_DIR_IN, 512, ID_UNASSIGNED }, + { 0x2, USB_EP_ATTR_INT, USB_DIR_OUT, 512, ID_UNASSIGNED }, + { 0x3, USB_EP_ATTR_BULK, USB_DIR_IN, 512, ID_UNASSIGNED }, + { 0x3, USB_EP_ATTR_BULK, USB_DIR_OUT, 512, ID_UNASSIGNED }, + { 0xFF, USB_EP_ATTR_TYPE_MASK, USB_DIR_MASK, 0, ID_ASSIGNED }, +}; +#endif + +void usbd_reset_callback(void) +{ + gd_usb_reset_count++; + rt_usbd_reset_handler(&_gd_udc); +} + +void usbd_setup_stage_callback(struct urequest *setup_packet) +{ + gd_usb_setup_count++; + rt_usbd_ep0_setup_handler(&_gd_udc, setup_packet); +} + +void usbd_data_in_stage_callback(uint8_t epnum, uint16_t data_count) +{ + if (epnum == 0) + { + gd_usb_ep0_in_count++; + rt_usbd_ep0_in_handler(&_gd_udc); + } + else + { + rt_usbd_ep_in_handler(&_gd_udc, 0x80 | epnum, data_count); + } +} + +void usbd_connect_callback(void) +{ + rt_usbd_connect_handler(&_gd_udc); +} + +void usbd_sof_callback(void) +{ + rt_usbd_sof_handler(&_gd_udc); +} + +void usbd_disconnect_callback(void) +{ + rt_usbd_disconnect_handler(&_gd_udc); +} + +void usbd_data_out_stage_callback(uint8_t epnum, uint16_t data_count) +{ + if (epnum == 0) + { + gd_usb_ep0_out_count++; + rt_usbd_ep0_out_handler(&_gd_udc, data_count); + } + else + { + rt_usbd_ep_out_handler(&_gd_udc, epnum, data_count); + } +} + +void usbd_set_connection_state(uint8_t state) +{ + if (state == 1) + { + usb_dev_connect(&gd_usb_core); + + usb_mdelay(3); + } + else + { + usb_dev_disconnect(&gd_usb_core); + + usb_mdelay(3); + } +} + +static rt_err_t _ep_set_stall(rt_uint8_t address) +{ + usb_ep_stall(&gd_usb_core, address); + + return RT_EOK; +} + +static rt_err_t _ep_clear_stall(rt_uint8_t address) +{ + usb_ep_clrstall(&gd_usb_core, address); + + return RT_EOK; +} + +static rt_err_t _set_address(rt_uint8_t address) +{ + usb_devaddr_set(&gd_usb_core, address); + + return RT_EOK; +} + +static rt_err_t _set_config(rt_uint8_t address) +{ + return RT_EOK; +} + +static rt_err_t _ep_enable(uep_t ep) +{ + RT_ASSERT(ep != RT_NULL); + RT_ASSERT(ep->ep_desc != RT_NULL); + + usb_ep_active(&gd_usb_core, + ep->ep_desc->bEndpointAddress, + ep->ep_desc->wMaxPacketSize, + ep->ep_desc->bmAttributes); + + return RT_EOK; +} + +static rt_err_t _ep_disable(uep_t ep) +{ + RT_ASSERT(ep != RT_NULL); + RT_ASSERT(ep->ep_desc != RT_NULL); + + usb_ep_deactivate(&gd_usb_core, ep->ep_desc->bEndpointAddress); + + return RT_EOK; +} + +static rt_ssize_t _ep_read(rt_uint8_t address, void *buffer) +{ + rt_size_t size = 0; + RT_ASSERT(buffer != RT_NULL); + + return size; +} + +static rt_ssize_t _ep_read_prepare(rt_uint8_t address, void *buffer, rt_size_t size) +{ + usb_ep_outxfer(&gd_usb_core, address, buffer, size); + + return size; +} + +static rt_ssize_t _ep_write(rt_uint8_t address, void *buffer, rt_size_t size) +{ + usb_ep_inxfer(&gd_usb_core, address, buffer, size); + + return size; +} + +static rt_err_t _ep0_send_status(void) +{ + usb_ep_inxfer(&gd_usb_core, 0, NULL, 0); + + usb_ctlep_startout(&gd_usb_core); + + return RT_EOK; +} + +static rt_err_t _suspend(void) +{ + return RT_EOK; +} + +static rt_err_t _wakeup(void) +{ + return RT_EOK; +} + +static rt_err_t _init(rt_device_t device) +{ + usb_rcu_config(USBHS0); + +#if defined(BSP_USB_DEVICE_USING_HS) + usb_para_init(&gd_usb_core, USBHS0, USB_SPEED_HIGH, DEVICE_MODE); +#else + usb_para_init(&gd_usb_core, USBHS0, USB_SPEED_FULL, DEVICE_MODE); +#endif + + /* configure USB capabilites */ + usb_basic_init(&gd_usb_core.bp, &gd_usb_core.regs); + + usb_globalint_disable(&gd_usb_core.regs); + + /* initailizes the USB core*/ + usb_core_init(gd_usb_core.bp, &gd_usb_core.regs); + + /* set device disconnect */ + usbd_set_connection_state(0); + + usb_curmode_set(&gd_usb_core.regs, DEVICE_MODE); + + /* initailizes device mode */ + usb_devcore_init(&gd_usb_core); + +#if defined(BSP_USB_DEVICE_USING_HS) + pllusb_rcu_config(USBHS0); +#endif + + usb_intr_config(USBHS0); + + usb_globalint_enable(&gd_usb_core.regs); + + /* connect only after the PHY clock and interrupt are ready */ + usbd_set_connection_state(1); + + return RT_EOK; +} + +const static struct udcd_ops _udc_ops = { + _set_address, + _set_config, + _ep_set_stall, + _ep_clear_stall, + _ep_enable, + _ep_disable, + _ep_read_prepare, + _ep_read, + _ep_write, + _ep0_send_status, + _suspend, + _wakeup, +}; + +int gd_usbd_register(void) +{ + rt_memset((void *)&_gd_udc, 0, sizeof(struct udcd)); + _gd_udc.parent.type = RT_Device_Class_USBDevice; + _gd_udc.parent.init = _init; + _gd_udc.parent.user_data = NULL; + _gd_udc.ops = &_udc_ops; + /* Register endpoint infomation */ + _gd_udc.ep_pool = _ep_pool; + _gd_udc.ep0.id = &_ep_pool[0]; + + rt_device_register((rt_device_t)&_gd_udc, "usbd", 0); + rt_usb_device_init(); + return RT_EOK; +} +INIT_DEVICE_EXPORT(gd_usbd_register); + +#ifdef RT_USING_FINSH +static void usbstat(void) +{ + rt_kprintf("usb irq=%u reset=%u enum=%u setup=%u ep0in=%u ep0out=%u\n", + gd_usb_irq_count, gd_usb_reset_count, gd_usb_enum_count, + gd_usb_setup_count, gd_usb_ep0_in_count, gd_usb_ep0_out_count); + rt_kprintf("rx fifo=%u setup=%u last=%08x\n", + gd_usb_rxfifo_count, gd_usb_setup_rx_count, gd_usb_last_rx_status); + rt_kprintf("core GAHBCS=%08x GUSBCS=%08x GINTF=%08x GINTEN=%08x\n", + gd_usb_core.regs.gr->GAHBCS, gd_usb_core.regs.gr->GUSBCS, + gd_usb_core.regs.gr->GINTF, gd_usb_core.regs.gr->GINTEN); + rt_kprintf("fifo GRSTCTL=%08x GRFLEN=%08x TX0=%08x GCCFG=%08x GOTGCS=%08x\n", + gd_usb_core.regs.gr->GRSTCTL, gd_usb_core.regs.gr->GRFLEN, + gd_usb_core.regs.gr->DIEP0TFLEN_HNPTFLEN, + gd_usb_core.regs.gr->GCCFG, gd_usb_core.regs.gr->GOTGCS); + rt_kprintf("dev DCFG=%08x DCTL=%08x DSTAT=%08x DAEPINT=%08x DAEPINTEN=%08x\n", + gd_usb_core.regs.dr->DCFG, gd_usb_core.regs.dr->DCTL, + gd_usb_core.regs.dr->DSTAT, gd_usb_core.regs.dr->DAEPINT, + gd_usb_core.regs.dr->DAEPINTEN); + rt_kprintf("ep0o CTL=%08x LEN=%08x INTF=%08x\n", + gd_usb_core.regs.er_out[0]->DOEPCTL, + gd_usb_core.regs.er_out[0]->DOEPLEN, + gd_usb_core.regs.er_out[0]->DOEPINTF); + rt_kprintf("ep0i CTL=%08x LEN=%08x INTF=%08x\n", + gd_usb_core.regs.er_in[0]->DIEPCTL, + gd_usb_core.regs.er_in[0]->DIEPLEN, + gd_usb_core.regs.er_in[0]->DIEPINTF); + rt_kprintf("nvic enable=%u pending=%u active=%u pll=%08x\n", + NVIC_GetEnableIRQ(USBHS0_IRQn), NVIC_GetPendingIRQ(USBHS0_IRQn), + NVIC_GetActive(USBHS0_IRQn), RCU_ADDCTL1); +} +MSH_CMD_EXPORT(usbstat, show USB device enumeration status); +#endif + +#ifdef RT_USB_DEVICE_HID + +void usbd_hid_send(uint8_t *buffer, uint16_t size) +{ + struct hid_s + { + struct rt_device parent; + struct ufunction *func; + uep_t ep_in; + uep_t ep_out; + int status; + rt_uint16_t protocol; + rt_uint8_t report_buf[64]; + struct rt_messagequeue hid_mq; + }; + + udevice_t device = RT_NULL; + uintf_t intf; + ufunction_t func; + + struct hid_s *hiddev; + + device = rt_usbd_find_device(&_gd_udc); + + intf = rt_usbd_find_interface(device, 0, &func); + + if (RT_NULL != intf) + { + hiddev = (struct hid_s *)func->user_data; + + hiddev->parent.write(&hiddev->parent, 0, buffer, size); + } +} + +#endif + +#endif /* BSP_USING_USB && RT_USING_USB_DEVICE */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_otghs_dev.h b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_otghs_dev.h new file mode 100644 index 000000000000..f5f8e8d8f5a6 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_otghs_dev.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#ifndef __DRV_OTGFS_DEV_H__ +#define __DRV_OTGFS_DEV_H__ + +#include +#include "drv_usb_dev.h" + +extern usb_core_driver gd_usb_device_core; + +extern volatile rt_uint32_t gd_usb_irq_count; +extern volatile rt_uint32_t gd_usb_reset_count; +extern volatile rt_uint32_t gd_usb_enum_count; +extern volatile rt_uint32_t gd_usb_setup_count; +extern volatile rt_uint32_t gd_usb_ep0_in_count; +extern volatile rt_uint32_t gd_usb_ep0_out_count; +extern volatile rt_uint32_t gd_usb_rxfifo_count; +extern volatile rt_uint32_t gd_usb_setup_rx_count; +extern volatile rt_uint32_t gd_usb_last_rx_status; + +void usbd_reset_callback(void); + +void usbd_setup_stage_callback(struct urequest *setup_packet); + +void usbd_data_in_stage_callback(uint8_t epnum, uint16_t data_count); + +void usbd_data_out_stage_callback(uint8_t epnum, uint16_t data_count); + +void usbd_set_connection_state(uint8_t state); + +int gd_usbd_register(void); + +#endif diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_otghs_host.c b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_otghs_host.c new file mode 100644 index 000000000000..ed30a86cb3cb --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_otghs_host.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + * 2026-09-01 CYFS start host after filesystem initialization + */ + +#include "drv_otghs_host.h" +#include +#include +#include "board.h" + +#include "drv_usb_hw.h" + +#ifdef RT_USING_FINSH +#include +#endif + +#if defined(BSP_USING_USB) && defined(RT_USING_USB_HOST) + +#define OTG_FS_PORT 1 + +usb_core_driver gd_usb_host_core; +#define gd_usb_core gd_usb_host_core + +static struct rt_completion urb_completion; +static volatile rt_bool_t connect_status = RT_FALSE; +volatile rt_uint32_t gd_usbh_irq_count; + +void usbh_connect_callback(usb_core_driver *hhcd) +{ + uhcd_t hcd = (uhcd_t)hhcd->host.data; + if (!connect_status) + { + connect_status = RT_TRUE; + rt_usbh_root_hub_connect_handler(hcd, OTG_FS_PORT, RT_FALSE); + } +} + +void usbh_disconnect_callback(usb_core_driver *hhcd) +{ + uhcd_t hcd = (uhcd_t)hhcd->host.data; + if (connect_status) + { + connect_status = RT_FALSE; + rt_usbh_root_hub_disconnect_handler(hcd, OTG_FS_PORT); + } +} + +void usbh_hc_notifyurbchange_callback(usb_core_driver *hhcd, uint8_t chnum, usb_urb_state urb_state) +{ + rt_completion_done(&urb_completion); +} + +static rt_err_t drv_reset_port(rt_uint8_t port) +{ + usb_port_reset(&gd_usb_core); + + return RT_EOK; +} + +static int drv_pipe_xfer(upipe_t pipe, rt_uint8_t token, void *buffer, int nbytes, int timeout) +{ + while (1) + { + uint8_t dir = (pipe->ep.bEndpointAddress & 0x80) >> 7; + + if (!connect_status) + { + return -1; + } + + rt_completion_init(&urb_completion); + + if (dir == 0) + { + if (token == 0) + { + usbh_ctlsetup_send(&gd_usb_core, buffer, pipe->pipe_index); + } + else + { + usbh_data_send(&gd_usb_core, buffer, pipe->pipe_index, nbytes); + } + } + else + { + usbh_data_recev(&gd_usb_core, buffer, pipe->pipe_index, nbytes); + } + + + rt_completion_wait(&urb_completion, timeout); + + rt_thread_mdelay(1); + + if (usbh_urbstate_get(&gd_usb_core, pipe->pipe_index) == URB_NOTREADY) + { + if (pipe->ep.bmAttributes == USB_EP_ATTR_INT) + { + rt_thread_delay((pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) > 0 ? (pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) : 1); + } + + usb_pipe_halt(&gd_usb_core, pipe->pipe_index); + + usbh_pipe_create(&gd_usb_core, + pipe->inst->address, + (uint8_t)PORT_SPEED_FULL, + pipe->pipe_index, + pipe->ep.bEndpointAddress, + pipe->ep.bmAttributes, + pipe->ep.wMaxPacketSize); + continue; + } + else if (usbh_urbstate_get(&gd_usb_core, pipe->pipe_index) == URB_STALL) + { + pipe->status = UPIPE_STATUS_STALL; + if (pipe->callback != RT_NULL) + { + pipe->callback(pipe); + } + return -1; + } + else if (usbh_urbstate_get(&gd_usb_core, pipe->pipe_index) == URB_ERROR) + { + pipe->status = UPIPE_STATUS_ERROR; + if (pipe->callback != RT_NULL) + { + pipe->callback(pipe); + } + return -1; + } + else if (usbh_urbstate_get(&gd_usb_core, pipe->pipe_index) == URB_DONE) + { + pipe->status = UPIPE_STATUS_OK; + if (pipe->callback != RT_NULL) + { + pipe->callback(pipe); + } + if (pipe->ep.bEndpointAddress & 0x80) + { + return usbh_xfercount_get(&gd_usb_core, pipe->pipe_index); + } + return nbytes; + } + return -1; + } +} + +static rt_uint16_t pipe_index = 0; +static rt_uint8_t drv_get_free_pipe_index() +{ + rt_uint8_t idx; + for (idx = 0; idx < USBHS_MAX_CHANNEL_COUNT; idx++) + { + if (!(pipe_index & (0x01 << idx))) + { + pipe_index |= (0x01 << idx); + return idx; + } + } + return 0xff; +} + +static void drv_free_pipe_index(rt_uint8_t index) +{ + if (index < USBHS_MAX_CHANNEL_COUNT) + { + pipe_index &= ~(0x01 << index); + } +} + +static rt_err_t drv_open_pipe(upipe_t pipe) +{ + pipe->pipe_index = drv_get_free_pipe_index(); + if (pipe->pipe_index == 0xff) + { + return -RT_ERROR; + } + + usbh_pipe_create(&gd_usb_core, + pipe->inst->address, + (uint8_t)PORT_SPEED_FULL, + pipe->pipe_index, + pipe->ep.bEndpointAddress, + pipe->ep.bmAttributes, + pipe->ep.wMaxPacketSize); + + /* Set DATA0 PID token*/ + if (gd_usb_core.host.pipe[pipe->pipe_index].ep.dir) + { + gd_usb_core.host.pipe[pipe->pipe_index].data_toggle_in = 0; + } + else + { + gd_usb_core.host.pipe[pipe->pipe_index].data_toggle_out = 0; + } + + return RT_EOK; +} + +static rt_err_t drv_close_pipe(upipe_t pipe) +{ + usb_pipe_halt(&gd_usb_core, pipe->pipe_index); + + drv_free_pipe_index(pipe->pipe_index); + + return RT_EOK; +} + +struct uhcd_ops _uhcd_ops = { + drv_reset_port, + drv_pipe_xfer, + drv_open_pipe, + drv_close_pipe, +}; + +static rt_err_t gd32_hcd_init(rt_device_t device) +{ + usb_rcu_config(USBHS1); + + /* configure GPIO pin used for switching VBUS power and charge pump I/O */ + usb_vbus_config(); + + uint8_t i = 0U; + pipe_index = 0U; + + usb_para_init(&gd_usb_core, USBHS1, USB_SPEED_FULL, HOST_MODE); + + gd_usb_core.host.connect_status = 0U; + + for (i = 0U; i < USBHS_MAX_TX_FIFOS; i++) + { + gd_usb_core.host.pipe[i].err_count = 0U; + gd_usb_core.host.pipe[i].pp_status = PIPE_IDLE; + gd_usb_core.host.backup_xfercount[i] = 0U; + } + + gd_usb_core.host.pipe[0].ep.mps = 8U; + + usb_basic_init(&gd_usb_core.bp, &gd_usb_core.regs); + + usb_globalint_disable(&gd_usb_core.regs); + + usb_core_init(gd_usb_core.bp, &gd_usb_core.regs); + + usb_curmode_set(&gd_usb_core.regs, HOST_MODE); + + usb_host_init(&gd_usb_core); + + usb_globalint_enable(&gd_usb_core.regs); + + /* enable interrupts */ + usb_intr_config(USBHS1); + + return RT_EOK; +} + +int gd_usbh_register(void) +{ + rt_err_t res = -RT_ERROR; + + uhcd_t uhcd = (uhcd_t)rt_malloc(sizeof(struct uhcd)); + if (uhcd == RT_NULL) + { + rt_kprintf("uhcd malloc failed\r\n"); + return -RT_ERROR; + } + + rt_memset((void *)uhcd, 0, sizeof(struct uhcd)); + uhcd->parent.type = RT_Device_Class_USBHost; + uhcd->parent.init = gd32_hcd_init; + uhcd->parent.user_data = &gd_usb_core; + + uhcd->ops = &_uhcd_ops; + uhcd->num_ports = OTG_FS_PORT; + gd_usb_core.host.data = uhcd; + + res = rt_device_register(&uhcd->parent, "usbh", RT_DEVICE_FLAG_DEACTIVATE); + if (res != RT_EOK) + { + rt_kprintf("register usb host failed res = %d\r\n", res); + return -RT_ERROR; + } + + return RT_EOK; +} +INIT_DEVICE_EXPORT(gd_usbh_register); + +static int gd_usbh_start(void) +{ + return rt_usb_host_init("usbh"); +} +INIT_APP_EXPORT(gd_usbh_start); + +#ifdef RT_USING_FINSH +static void usbhstat(void) +{ + rt_kprintf("usbh irq=%u connected=%u core_connected=%u\n", + gd_usbh_irq_count, connect_status, + gd_usb_core.host.connect_status); + rt_kprintf("core GAHBCS=%08x GUSBCS=%08x GINTF=%08x GINTEN=%08x\n", + gd_usb_core.regs.gr->GAHBCS, gd_usb_core.regs.gr->GUSBCS, + gd_usb_core.regs.gr->GINTF, gd_usb_core.regs.gr->GINTEN); + rt_kprintf("host HCTL=%08x HFINFR=%08x HACHINT=%08x HPCS=%08x\n", + gd_usb_core.regs.hr->HCTL, gd_usb_core.regs.hr->HFINFR, + gd_usb_core.regs.hr->HACHINT, *gd_usb_core.regs.HPCS); + rt_kprintf("nvic enable=%u pending=%u active=%u\n", + NVIC_GetEnableIRQ(USBHS1_IRQn), NVIC_GetPendingIRQ(USBHS1_IRQn), + NVIC_GetActive(USBHS1_IRQn)); +} +MSH_CMD_EXPORT(usbhstat, show USB host status); +#endif + +#endif /* BSP_USING_USB && RT_USING_USB_HOST */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_otghs_host.h b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_otghs_host.h new file mode 100644 index 000000000000..a91d87661268 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_otghs_host.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#ifndef __DRV_OTGFS_HOST_H__ +#define __DRV_OTGFS_HOST_H__ + + +#include + +#include "drv_usb_host.h" +#include "drv_usbh_transc.h" +#include "drv_usbh_pipe.h" + +#define OTG_FS_PORT 1 + +#if defined(BSP_USING_USB) && defined(RT_USING_USB_HOST) + +extern usb_core_driver gd_usb_host_core; +extern volatile rt_uint32_t gd_usbh_irq_count; + +void usbh_connect_callback(usb_core_driver *hhcd); + +void usbh_disconnect_callback(usb_core_driver *hhcd); + +void usbh_hc_notifyurbchange_callback(usb_core_driver *hhcd, uint8_t chnum, usb_urb_state urb_state); + +int gd_usbh_register(void); + +#endif /* BSP_USING_USB && RT_USING_USB_HOST */ +#endif diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_core.c b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_core.c new file mode 100644 index 000000000000..58dedc1058d6 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_core.c @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#include "drv_usb_hw.h" +#include "drv_usb_core.h" + +#if defined(BSP_USING_USB) + +/* local function prototypes ('static') */ +static void usb_core_reset(usb_core_regs *usb_regs); + +/*! + \brief configures USB parameters + \param[in] udev: pointer to USB core instance + \param[in] usb_periph: USBHSx(x=0,1) + \param[in] usb_speed: USB speed type (full-speed or high-speed) + \param[in] usb_mode: USB host or device mode + \param[out] none + \retval none +*/ +void usb_para_init(usb_core_driver *udev, uint32_t usb_periph, uint32_t usb_speed, uint32_t usb_mode) +{ + usb_core_basic *usb_basic = &udev->bp; + + /* configure USB default transfer mode as FIFO mode */ + usb_basic->transfer_mode = (uint8_t)USB_USE_FIFO; + + usb_basic->core_speed = (uint8_t)usb_speed; + usb_basic->core_enum = (uint8_t)usb_mode; + + /* USB basic register address setting */ + switch (usb_periph) + { + case USBHS0: + usb_basic->base_reg = (uint32_t)USBHS0_REG_BASE; + break; + case USBHS1: + usb_basic->base_reg = (uint32_t)USBHS1_REG_BASE; + break; + default: + break; + } + + /* set the host channel numbers */ + usb_basic->num_pipe = USBHS_MAX_CHANNEL_COUNT; + + /* set the device endpoint numbers */ + usb_basic->num_ep = USBHS_MAX_EP_COUNT; + + if (USB_SPEED_HIGH == usb_speed) + { +#ifdef USB_EXTERNAL_ULPI_PHY_ENABLED + usb_basic->phy_itf = USB_ULPI_PHY_EXTERNAL; +#else + usb_basic->phy_itf = USB_EMBEDDED_PHY_HS; +#endif + } + else if (USB_SPEED_FULL == usb_speed) + { + usb_basic->phy_itf = USB_EMBEDDED_PHY_FS; + } + else + { + /* no operation */ + } + +#ifdef USB_INTERNAL_DMA_ENABLED + usb_basic->transfer_mode = USB_USE_DMA; +#endif /* USB_INTERNAL_DMA_ENABLED */ + + usb_basic->sof_enable = (usb_basic->core_enum == HOST_MODE) ? 1U : 0U; + usb_basic->low_power = (uint8_t)USB_LOW_POWER; + +#if (1U == LPM_ENABLED) + usb_basic->lpm_enable = 1U; +#endif /* LPM_ENABLED */ +} +/*! + \brief configure USB core basic + \param[in] usb_basic: pointer to USB capabilities + \param[in] usb_regs: pointer to USB core registers + \param[out] none + \retval operation status +*/ +usb_status usb_basic_init(usb_core_basic *usb_basic, usb_core_regs *usb_regs) +{ + /* assign main registers address */ + *usb_regs = (usb_core_regs){ + .gr = (usb_gr *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_CORE), + .hr = (usb_hr *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_HOST), + .dr = (usb_dr *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_DEV), + + .HPCS = (uint32_t *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_PORT), + .PWRCLKCTL = (uint32_t *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_PWRCLKCTL) + }; + + /* assign device endpoint registers address */ + for (uint8_t i = 0U; i < usb_basic->num_ep; i++) + { + usb_regs->er_in[i] = (usb_erin *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_EP_IN + (i * (uint32_t)USB_REG_OFFSET_EP)); + + usb_regs->er_out[i] = (usb_erout *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_EP_OUT + (i * (uint32_t)USB_REG_OFFSET_EP)); + } + + /* assign host pipe registers address */ + for (uint8_t i = 0U; i < usb_basic->num_pipe; i++) + { + usb_regs->pr[i] = (usb_pr *)(usb_basic->base_reg + (uint32_t)USB_REG_OFFSET_CH_INOUT + (i * (uint32_t)USB_REG_OFFSET_CH)); + + usb_regs->DFIFO[i] = (uint32_t *)(usb_basic->base_reg + (uint32_t)USB_DATA_FIFO_OFFSET + (i * (uint32_t)USB_DATA_FIFO_SIZE)); + } + + return USB_OK; +} + +/*! + \brief initializes the USB controller registers and + prepares the core device mode or host mode operation + \param[in] usb_basic: pointer to USB capabilities + \param[in] usb_regs: pointer to USB core registers + \param[out] none + \retval operation status +*/ +usb_status usb_core_init(usb_core_basic usb_basic, usb_core_regs *usb_regs) +{ + if (USB_ULPI_PHY_EXTERNAL == usb_basic.phy_itf) + { + usb_regs->gr->GCCFG &= ~GCCFG_PWRON; + + usb_regs->gr->GUSBCS &= ~(GUSBCS_EMBPHY_HS | GUSBCS_EMBPHY_FS); + + if (usb_basic.sof_enable) + { + usb_regs->gr->GCCFG |= GCCFG_SOFOEN; + } + + /* initialize the ULPI interface */ + usb_regs->gr->GUSBCS &= ~GUSBCS_ULPIEOI; + +#ifdef USBHS_EXTERNAL_VBUS_ENABLED + /* use external VBUS driver */ + usb_regs->gr->GUSBCS |= GUSBCS_ULPIEVD; +#else + /* use internal VBUS driver */ + usb_regs->gr->GUSBCS &= ~GUSBCS_ULPIEVD; +#endif /* USBHS_EXTERNAL_VBUS_ENABLED */ + + /* soft reset the core */ + usb_core_reset(usb_regs); + } + else if (USB_EMBEDDED_PHY_HS == usb_basic.phy_itf) + { + usb_regs->gr->GUSBCS |= GUSBCS_EMBPHY_HS; + + /* soft reset the core */ + usb_core_reset(usb_regs); + + usb_regs->gr->GCCFG = 0U; + +#ifdef VBUS_SENSING_ENABLED + /* active the transceiver and enable VBUS sensing */ + usb_regs->gr->GCCFG |= GCCFG_VDEN | GCCFG_PWRON; +#else + if (usb_basic.core_enum == HOST_MODE) + { + usb_regs->gr->GOTGCS |= GOTGCS_AVOV | GOTGCS_AVOE; + } + else + { + usb_regs->gr->GOTGCS |= GOTGCS_BVOV | GOTGCS_BVOE; + } + + /* active the transceiver */ + usb_regs->gr->GCCFG |= GCCFG_PWRON; +#endif /* VBUS_SENSING_ENABLED */ + + if (usb_basic.sof_enable) + { + usb_regs->gr->GCCFG |= GCCFG_SOFOEN; + } + + usb_mdelay(20U); + } + else if (USB_EMBEDDED_PHY_FS == usb_basic.phy_itf) + { + usb_regs->gr->GUSBCS |= GUSBCS_EMBPHY_FS; + + /* soft reset the core */ + usb_core_reset(usb_regs); + +#ifdef VBUS_SENSING_ENABLED + /* active the transceiver and enable VBUS sensing */ + usb_regs->gr->GCCFG |= GCCFG_VDEN | GCCFG_PWRON; +#else + if (usb_basic.core_enum == HOST_MODE) + { + usb_regs->gr->GOTGCS |= GOTGCS_AVOV | GOTGCS_AVOE; + } + else + { + usb_regs->gr->GOTGCS |= GOTGCS_BVOV | GOTGCS_BVOE; + } + + /* active the transceiver */ + usb_regs->gr->GCCFG |= GCCFG_PWRON; +#endif /* VBUS_SENSING_ENABLED */ + + /* enable SOF output */ + if (usb_basic.sof_enable) + { + usb_regs->gr->GCCFG |= GCCFG_SOFOEN; + } + + usb_mdelay(20U); + } + else + { + /* no operation */ + } + + if ((uint8_t)USB_USE_DMA == usb_basic.transfer_mode) + { + usb_regs->gr->GAHBCS &= ~GAHBCS_BURST; + usb_regs->gr->GAHBCS |= DMA_INCR8 | GAHBCS_DMAEN; + } + +#ifdef USE_OTG_MODE + + /* enable USB OTG features */ + usb_regs->gr->GUSBCS |= GUSBCS_HNPCEN | GUSBCS_SRPCEN; + + /* enable the USB wakeup and suspend interrupts */ + usb_regs->gr->GINTF = 0xBFFFFFFFU; + + usb_regs->gr->GINTEN = GINTEN_WKUPIE | GINTEN_SPIE | + GINTEN_OTGIE | GINTEN_SESIE | GINTEN_IDPSCIE; + +#endif /* USE_OTG_MODE */ + + return USB_OK; +} + +/*! + \brief write a packet into the TX FIFO associated with the endpoint + \param[in] usb_regs: pointer to USB core registers + \param[in] src_buf: pointer to source buffer + \param[in] fifo_num: FIFO number which is in (0..5) + \param[in] byte_count: packet byte count + \param[out] none + \retval operation status +*/ +usb_status usb_txfifo_write(usb_core_regs *usb_regs, + uint8_t *src_buf, + uint8_t fifo_num, + uint16_t byte_count) +{ + uint32_t word_count = (byte_count + 3U) / 4U; + + __IO uint32_t *fifo = usb_regs->DFIFO[fifo_num]; + + while (word_count-- > 0U) + { + *fifo = *((__IO uint32_t *)src_buf); + + src_buf += 4U; + } + + return USB_OK; +} + +/*! + \brief read a packet from the Rx FIFO associated with the endpoint + \param[in] usb_regs: pointer to USB core registers + \param[in] dest_buf: pointer to destination buffer + \param[in] byte_count: packet byte count + \param[out] none + \retval void type pointer +*/ +void *usb_rxfifo_read(usb_core_regs *usb_regs, uint8_t *dest_buf, uint16_t byte_count) +{ + __IO uint32_t word_count = (byte_count + 3U) / 4U; + __IO uint32_t *fifo = usb_regs->DFIFO[0]; + + while (word_count-- > 0U) + { + *(__IO uint32_t *)dest_buf = *fifo; + + dest_buf += 4U; + } + + return ((void *)dest_buf); +} + +/*! + \brief flush a TX FIFO or all TX FIFOs + \param[in] usb_regs: pointer to USB core registers + \param[in] fifo_num: FIFO number which is in (0..5) + \param[out] none + \retval operation status +*/ +usb_status usb_txfifo_flush(usb_core_regs *usb_regs, uint8_t fifo_num) +{ + usb_regs->gr->GRSTCTL = ((uint32_t)fifo_num << 6) | GRSTCTL_TXFF; + + /* wait for TX FIFO flush bit is set */ + while (usb_regs->gr->GRSTCTL & GRSTCTL_TXFF) + { + /* no operation */ + } + + /* wait for 3 PHY clocks*/ + usb_udelay(3U); + + return USB_OK; +} + +/*! + \brief flush the entire RX FIFO + \param[in] usb_regs: pointer to USB core registers + \param[out] none + \retval operation status +*/ +usb_status usb_rxfifo_flush(usb_core_regs *usb_regs) +{ + usb_regs->gr->GRSTCTL = GRSTCTL_RXFF; + + /* wait for RX FIFO flush bit is set */ + while (usb_regs->gr->GRSTCTL & GRSTCTL_RXFF) + { + /* no operation */ + } + + /* wait for 3 PHY clocks */ + usb_udelay(3U); + + return USB_OK; +} + +/*! + \brief set endpoint or channel TX FIFO size + \param[in] usb_regs: pointer to USB core registers + \param[in] fifo: TX FIFO number + \param[in] size: assigned TX FIFO size + \param[out] none + \retval none +*/ +void usb_set_txfifo(usb_core_regs *usb_regs, uint8_t fifo, uint16_t size) +{ + uint32_t tx_offset = usb_regs->gr->GRFLEN; + + if (0U == fifo) + { + usb_regs->gr->DIEP0TFLEN_HNPTFLEN = ((uint32_t)size << 16) | tx_offset; + } + else + { + tx_offset += (usb_regs->gr->DIEP0TFLEN_HNPTFLEN) >> 16; + + for (uint8_t i = 0U; i < (fifo - 1U); i++) + { + tx_offset += (usb_regs->gr->DIEPTFLEN[i] >> 16); + } + + usb_regs->gr->DIEPTFLEN[fifo - 1U] = ((uint32_t)size << 16) | tx_offset; + } +} + +/*! + \brief set USB current mode + \param[in] usb_regs: pointer to USB core registers + \param[in] mode: USB current mode + \param[out] none + \retval none +*/ +void usb_curmode_set(usb_core_regs *usb_regs, uint8_t mode) +{ + usb_regs->gr->GUSBCS &= ~(GUSBCS_FDM | GUSBCS_FHM); + + if (DEVICE_MODE == mode) + { + usb_regs->gr->GUSBCS |= GUSBCS_FDM; + } + else if (HOST_MODE == mode) + { + usb_regs->gr->GUSBCS |= GUSBCS_FHM; + } + else + { + /* OTG mode and other mode can not be here! */ + } +} + +/*! + \brief configure USB core to soft reset + \param[in] usb_regs: pointer to USB core registers + \param[out] none + \retval none +*/ +static void usb_core_reset(usb_core_regs *usb_regs) +{ + /* enable core soft reset */ + usb_regs->gr->GRSTCTL |= GRSTCTL_CSRST; + + /* wait for the core to be soft reset */ + while (usb_regs->gr->GRSTCTL & GRSTCTL_CSRST) + { + /* no operation */ + } + + /* wait for additional 3 PHY clocks */ + usb_udelay(3U); +} + +/*! + \brief active USB core clock + \param[in] udev: pointer to USB device + \param[out] none + \retval none +*/ +void usb_clock_active(usb_core_driver *udev) +{ + if (udev->bp.low_power) + { + if (udev->regs.dr->DSTAT & DSTAT_SPST) + { + /* un-gate USB Core clock */ + *udev->regs.PWRCLKCTL &= ~(PWRCLKCTL_SHCLK | PWRCLKCTL_SUCLK); + } + } +} + +#endif /* BSP_USING_USB */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_core.h b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_core.h new file mode 100644 index 000000000000..178ad3649d58 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_core.h @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#ifndef DRV_USB_CORE_H +#define DRV_USB_CORE_H + +#include "drv_usb_regs.h" + +#define USB_FS_EP0_MAX_LEN 64U /*!< maximum packet size of endpoint 0 */ +#define HC_MAX_PACKET_COUNT 140U /*!< maximum packet count */ + +#define EP_ID(x) ((uint8_t)((x) & 0x7FU)) /*!< endpoint number */ +#define EP_DIR(x) ((uint8_t)((x) >> 7)) /*!< endpoint direction */ + +typedef struct urequest usb_req; + +enum _usb_eptype +{ + USB_EPTYPE_CTRL = 0U, /*!< control endpoint type */ + USB_EPTYPE_ISOC = 1U, /*!< isochronous endpoint type */ + USB_EPTYPE_BULK = 2U, /*!< bulk endpoint type */ + USB_EPTYPE_INTR = 3U, /*!< interrupt endpoint type */ + USB_EPTYPE_MASK = 3U /*!< endpoint type mask */ +}; + +typedef enum +{ + USB_OTG_OK = 0U, /*!< USB OTG status OK */ + USB_OTG_FAIL /*!< USB OTG status fail */ +} usb_otg_status; + +typedef enum +{ + USB_OK = 0U, /*!< USB status OK */ + USB_FAIL /*!< USB status fail */ +} usb_status; + +typedef enum +{ + USB_USE_FIFO, /*!< USB use FIFO transfer mode */ + USB_USE_DMA /*!< USB use DMA transfer mode */ +} usb_transfer_mode; + +typedef enum +{ + LPM_L0 = 0U, /*!< on */ + LPM_L1, /*!< LPM L1 sleep */ + LPM_L2, /*!< suspend */ + LPM_L3, /*!< off */ +} usb_lpm_state; + +enum usb_ctl_status +{ + USB_CTL_IDLE = 0U, /*!< USB control transfer idle state */ + USB_CTL_DATA_IN, /*!< USB control transfer data IN state */ + USB_CTL_LAST_DATA_IN, /*!< USB control transfer last data IN state */ + USB_CTL_DATA_OUT, /*!< USB control transfer data OUT state */ + USB_CTL_LAST_DATA_OUT, /*!< USB control transfer last data OUT state */ + USB_CTL_STATUS_IN, /*!< USB control transfer status IN state*/ + USB_CTL_STATUS_OUT /*!< USB control transfer status OUT state */ +}; + +typedef enum +{ + LPM_L0_ACTIVE = 0U, /*!< on */ + LPM_L1_ACTIVE, /*!< LPM L1 sleep */ +} usb_lpm_type; + +typedef struct +{ + uint8_t core_enum; /*!< USB core type */ + uint8_t core_speed; /*!< USB core speed */ + uint8_t num_pipe; /*!< USB host channel numbers */ + uint8_t num_ep; /*!< USB device endpoint numbers */ + uint8_t transfer_mode; /*!< USB transfer mode */ + uint8_t phy_itf; /*!< USB core PHY interface */ + uint8_t sof_enable; /*!< USB SOF output */ + uint8_t low_power; /*!< USB low power */ + uint8_t lpm_enable; /*!< USB link power mode(LPM) */ + uint8_t vbus_sensing_enable; /*!< USB VBUS sensing feature */ + uint8_t use_dedicated_ep1; /*!< USB dedicated endpoint1 interrupt */ + uint8_t use_external_vbus; /*!< enable or disable the use of the external VBUS */ + uint32_t base_reg; /*!< base register address */ +} usb_core_basic; + +#ifdef USE_DEVICE_MODE + +/* USB descriptor */ +typedef struct _usb_desc +{ + uint8_t *dev_desc; /*!< device descriptor */ + uint8_t *config_desc; /*!< configuration descriptor */ + uint8_t *bos_desc; /*!< BOS descriptor */ + +#ifdef BSP_USB_DEVICE_USING_HS + uint8_t *other_speed_config_desc; /*!< other speed configuration descriptor */ + uint8_t *qualifier_desc; /*!< qualifier descriptor */ +#endif + + void * const *strings; /*!< string descriptor */ +} usb_desc; + +/* USB power management */ +typedef struct _usb_pm +{ + uint8_t power_mode; /*!< power mode */ + uint8_t power_low; /*!< power low */ + uint8_t dev_remote_wakeup; /*!< remote wakeup */ + uint8_t remote_wakeup_on; /*!< remote wakeup on */ + usb_lpm_state lpm_state; /*!< LPM state */ + uint32_t BESL; /*!< BESL value */ +} usb_pm; + +/* USB control information */ +typedef struct _usb_control +{ + usb_req req; /*!< USB standard device request */ + + uint8_t ctl_state; /*!< USB control transfer state */ + uint8_t ctl_zlp; /*!< zero length package */ +} usb_control; + +typedef struct +{ + struct + { + uint8_t num : 4U; /*!< the endpoint number.it can be from 0 to 6 */ + uint8_t pad : 3U; /*!< padding between number and direction */ + uint8_t dir : 1U; /*!< the endpoint direction */ + } ep_addr; + + uint8_t ep_type; /*!< USB endpoint type */ + uint8_t ep_stall; /*!< USB endpoint STALL status */ + + uint8_t frame_num; /*!< number of frame */ + uint16_t max_len; /*!< maximum packet length */ + + /* transaction level variables */ + uint8_t *xfer_buf; /*!< transmit buffer */ + uint32_t xfer_len; /*!< transmit buffer length */ + uint32_t xfer_count; /*!< transmit buffer count */ + + uint32_t remain_len; /*!< remain packet length */ + + uint32_t dma_addr; /*!< DMA address */ +} usb_transc; + +typedef struct _usb_core_driver usb_dev; + +typedef struct _usb_class_core +{ + uint8_t alter_set; /*!< alternative set */ + uint8_t command; /*!< device class request command */ + + uint8_t (*init)(usb_dev *udev, uint8_t config_index); /*!< initialize handler */ + uint8_t (*deinit)(usb_dev *udev, uint8_t config_index); /*!< de-initialize handler */ + uint8_t (*req_proc)(usb_dev *udev, usb_req *req); /*!< device request handler */ + uint8_t (*set_intf)(usb_dev *udev, usb_req *req); /*!< device set interface callback */ + uint8_t (*ctlx_in)(usb_dev *udev); /*!< device control IN callback */ + uint8_t (*ctlx_out)(usb_dev *udev); /*!< device control OUT callback */ + uint8_t (*data_in)(usb_dev *udev, uint8_t ep_num); /*!< device data IN handler */ + uint8_t (*data_out)(usb_dev *udev, uint8_t ep_num); /*!< device data OUT handler */ + uint8_t (*SOF)(usb_dev *udev); /*!< start of frame handler */ + uint8_t (*incomplete_isoc_in)(usb_dev *udev); /*!< incomplete synchronization IN transfer handler */ + uint8_t (*incomplete_isoc_out)(usb_dev *udev); /*!< incomplete synchronization OUT transfer handler */ +} usb_class_core; + +typedef struct _usb_perp_dev +{ + uint8_t config; /*!< configuration */ + uint8_t dev_addr; /*!< device address */ + + __IO uint8_t cur_status; /*!< current status */ + __IO uint8_t backup_status; /*!< backup status */ + + usb_transc transc_in[USBHS_MAX_TX_FIFOS]; /*!< endpoint IN transaction */ + usb_transc transc_out[USBHS_MAX_TX_FIFOS]; /*!< endpoint OUT transaction */ + + usb_pm pm; /*!< power management */ + usb_control control; /*!< USB control information */ + usb_desc *desc; /*!< USB descriptors pointer */ + usb_class_core *class_core; /*!< class driver */ + void *class_data[4]; /*!< class data pointer */ + void *user_data; /*!< user data pointer */ + void *pdata; /*!< reserved data pointer */ +} usb_perp_dev; + +#endif /* USE_DEVICE_MODE */ + +#ifdef USE_HOST_MODE + +typedef enum _usb_pipe_status +{ + PIPE_IDLE = 0U, /*!< host pipe IDLE status */ + PIPE_XF, /*!< host pipe transfer completed status */ + PIPE_HALTED, /*!< host pipe halted status */ + PIPE_NAK, /*!< host pipe NAK status */ + PIPE_NYET, /*!< host pipe NYET status */ + PIPE_STALL, /*!< host pipe STALL status */ + PIPE_TRACERR, /*!< host pipe transaction error status */ + PIPE_BBERR, /*!< host pipe babble error status */ + PIPE_REQOVR, /*!< host pipe frame overrun status */ + PIPE_DTGERR, /*!< host pipe data toggle error status */ +} usb_pipe_status; + +typedef enum _usb_pipe_mode +{ + PIPE_PERIOD = 0U, /*!< USB host pipe PERIOD mode */ + PIPE_NON_PERIOD = 1U /*!< USB host pipe NOT PERIOD mode */ +} usb_pipe_mode; + +typedef enum _usb_urb_state +{ + URB_IDLE = 0U, /*!< USB URB IDLE state */ + URB_DONE, /*!< USB URB DONE state */ + URB_NOTREADY, /*!< USB URB NOT READY state */ + URB_ERROR, /*!< USB URB ERROR state */ + URB_STALL, /*!< USB URB STALL state */ + URB_PING /*!< USB URB PING state */ +} usb_urb_state; + +typedef struct _usb_pipe +{ + uint8_t in_used; /*!< pipe used */ + uint8_t dev_addr; /*!< USB device address */ + uint32_t dev_speed; /*!< USB device speed */ + + struct + { + uint8_t num; /*!< endpoint numbers */ + uint8_t dir; /*!< endpoint direction */ + uint8_t type; /*!< endpoint transfer type */ + uint16_t mps; /*!< endpoint max packet size */ + } ep; + + __IO uint8_t supp_ping; /*!< host pipe support PING */ + __IO uint8_t do_ping; /*!< host pipe do PING */ + __IO uint32_t DPID; /*!< data PID */ + + uint8_t *xfer_buf; /*!< USB transfer buffer */ + uint32_t xfer_len; /*!< USB transfer length */ + uint32_t xfer_count; /*!< USB transfer count */ + + uint8_t data_toggle_in; /*!< toggle DATA IN */ + uint8_t data_toggle_out; /*!< toggle DATA OUT */ + + __IO uint32_t err_count; /*!< error count */ + __IO usb_pipe_status pp_status; /*!< USB pipe status */ + __IO usb_urb_state urb_state; /*!< USB URB state */ +} usb_pipe; + +typedef struct _usb_host_drv +{ + __IO uint32_t connect_status; /*!< connect status */ + __IO uint32_t port_enabled; /*!< USB port enable */ + __IO uint32_t backup_xfercount[USBHS_MAX_TX_FIFOS]; /*!< USB backup transfer data count */ + + usb_pipe pipe[USBHS_MAX_TX_FIFOS]; /*!< USB host pipe handles */ + void *data; /*!< user data pointer */ +} usb_host_drv; + +#endif /* USE_HOST_MODE */ + +typedef struct _usb_core_driver +{ + usb_core_basic bp; /*!< USB basic parameters */ + usb_core_regs regs; /*!< USB registers */ + +#ifdef USE_DEVICE_MODE + usb_perp_dev dev; /*!< USB peripheral device */ +#endif /* USE_DEVICE_MODE */ + +#ifdef USE_HOST_MODE + usb_host_drv host; /*!< USB peripheral host */ +#endif /* USE_HOST_MODE */ +} usb_core_driver; + +typedef enum +{ + BCD_NONE = 0, + BCD_SDP, + BCD_DCP, + BCD_CDP, + BCD_PS2, + BCD_ERROR +} bcd_type; + +/* static inline function definitions */ + +/*! + \brief get the global interrupts + \param[in] usb_regs: pointer to USB core registers + \param[out] none + \retval interrupt status +*/ +__STATIC_INLINE uint32_t usb_coreintr_get(usb_core_regs *usb_regs) +{ + uint32_t reg_data = usb_regs->gr->GINTEN; + + reg_data &= usb_regs->gr->GINTF; + + return reg_data; +} + +/*! + \brief set USB RX FIFO size + \param[in] usb_regs: pointer to USB core registers + \param[in] size: assigned FIFO size + \param[out] none + \retval none +*/ +__STATIC_INLINE void usb_set_rxfifo(usb_core_regs *usb_regs, uint16_t size) +{ + usb_regs->gr->GRFLEN = size; +} + +/*! + \brief enable the global interrupts + \param[in] usb_regs: pointer to USB core registers + \param[out] none + \retval none +*/ +__STATIC_INLINE void usb_globalint_enable(usb_core_regs *usb_regs) +{ + /* enable USB global interrupt */ + usb_regs->gr->GAHBCS |= GAHBCS_GINTEN; +} + +/*! + \brief disable the global interrupts + \param[in] usb_regs: pointer to USB core registers + \param[out] none + \retval none +*/ +__STATIC_INLINE void usb_globalint_disable(usb_core_regs *usb_regs) +{ + /* disable USB global interrupt */ + usb_regs->gr->GAHBCS &= ~GAHBCS_GINTEN; +} + +/* function declarations */ +/* configures USB parameters */ +void usb_para_init(usb_core_driver *core, uint32_t usb_periph, uint32_t usb_speed, uint32_t usb_mode); +/* configure core capabilities */ +usb_status usb_basic_init(usb_core_basic *usb_basic, usb_core_regs *usb_regs); +/*initializes the USB controller registers and prepares the core device mode or host mode operation*/ +usb_status usb_core_init(usb_core_basic usb_basic, usb_core_regs *usb_regs); +/* write a packet into the TX FIFO associated with the endpoint */ +usb_status usb_txfifo_write(usb_core_regs *usb_regs, uint8_t *src_buf, uint8_t fifo_num, uint16_t byte_count); +/* read a packet from the RX FIFO associated with the endpoint */ +void *usb_rxfifo_read(usb_core_regs *usb_regs, uint8_t *dest_buf, uint16_t byte_count); +/* flush a TX FIFO or all TX FIFOs */ +usb_status usb_txfifo_flush(usb_core_regs *usb_regs, uint8_t fifo_num); +/* flush the entire RX FIFO */ +usb_status usb_rxfifo_flush(usb_core_regs *usb_regs); +/* set endpoint or channel TX FIFO size */ +void usb_set_txfifo(usb_core_regs *usb_regs, uint8_t fifo, uint16_t size); +/* set USB current mode */ +void usb_curmode_set(usb_core_regs *usb_regs, uint8_t mode); +void usb_clock_active(usb_core_driver *udev); + +#endif /* DRV_USB_CORE_H */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_dev.c b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_dev.c new file mode 100644 index 000000000000..fcef58f25464 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_dev.c @@ -0,0 +1,756 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#include "drv_usb_hw.h" +#include "drv_usb_dev.h" + +#include + +#if defined(BSP_USING_USB) && defined(RT_USING_USB_DEVICE) + +/* endpoint 0 max packet length */ +static const uint8_t EP0_MAXLEN[4] = { + [DSTAT_EM_HS_PHY_30MHZ_60MHZ] = EP0MPL_64, + [DSTAT_EM_FS_PHY_30MHZ_60MHZ] = EP0MPL_64, + [DSTAT_EM_FS_PHY_48MHZ] = EP0MPL_64, + [DSTAT_EM_LS_PHY_6MHZ] = EP0MPL_8 +}; + +/* USB endpoint TX FIFO size */ +static uint16_t USBHS_TX_FIFO_SIZE[USBHS_MAX_EP_COUNT] = { + (uint16_t)USB_DEV_TX0_FIFO_SIZE, + (uint16_t)USB_DEV_TX1_FIFO_SIZE, + (uint16_t)USB_DEV_TX2_FIFO_SIZE, + (uint16_t)USB_DEV_TX3_FIFO_SIZE, + (uint16_t)USB_DEV_TX4_FIFO_SIZE, + (uint16_t)USB_DEV_TX5_FIFO_SIZE, + (uint16_t)USB_DEV_TX6_FIFO_SIZE, + (uint16_t)USB_DEV_TX7_FIFO_SIZE, +}; + +/*! + \brief initialize USB core registers for device mode + \param[in] udev: pointer to USB device + \param[out] none + \retval operation status +*/ +usb_status usb_devcore_init(usb_core_driver *udev) +{ + uint8_t i = 0U; + + /* restart the PHY clock (maybe don't need to...) */ + *udev->regs.PWRCLKCTL = 0U; + + /* configure periodic frame interval to default value */ + udev->regs.dr->DCFG &= ~DCFG_EOPFT; + udev->regs.dr->DCFG |= FRAME_INTERVAL_80; + + udev->regs.dr->DCFG &= ~DCFG_DS; + + if (USB_EMBEDDED_PHY_HS == udev->bp.phy_itf) + { + udev->regs.dr->DCFG |= USB_SPEED_INP_HIGH; + } + else if (USB_EMBEDDED_PHY_FS == udev->bp.phy_itf) + { + udev->regs.dr->DCFG |= USB_SPEED_INP_FULL; + } + else + { + /* no operation */ + } + + /* set RX FIFO size */ + usb_set_rxfifo(&udev->regs, (uint16_t)USB_DEV_RX_FIFO_SIZE); + + /* set endpoint 0 to 3's TX FIFO length and RAM address */ + for (i = 0U; i < USBHS_MAX_EP_COUNT; i++) + { + usb_set_txfifo(&udev->regs, i, USBHS_TX_FIFO_SIZE[i]); + } + + /* make sure all FIFOs are flushed */ + + /* flush all TX FIFOs */ + (void)usb_txfifo_flush(&udev->regs, 0x10U); + + /* flush entire RX FIFO */ + (void)usb_rxfifo_flush(&udev->regs); + + /* clear all pending device interrupts */ + udev->regs.dr->DIEPINTEN = 0U; + udev->regs.dr->DOEPINTEN = 0U; + udev->regs.dr->DAEPINT = 0xFFFFFFFFU; + udev->regs.dr->DAEPINTEN = 0U; + + /* configure all IN/OUT endpoints */ + for (i = 0U; i < udev->bp.num_ep; i++) + { + if (udev->regs.er_in[i]->DIEPCTL & DEPCTL_EPEN) + { + udev->regs.er_in[i]->DIEPCTL |= DEPCTL_EPD | DEPCTL_SNAK; + } + else + { + udev->regs.er_in[i]->DIEPCTL = 0U; + } + + /* set IN endpoint transfer length to 0 */ + udev->regs.er_in[i]->DIEPLEN = 0U; + + /* clear all pending IN endpoint interrupts */ + udev->regs.er_in[i]->DIEPINTF = 0xFFU; + + if (udev->regs.er_out[i]->DOEPCTL & DEPCTL_EPEN) + { + udev->regs.er_out[i]->DOEPCTL |= DEPCTL_EPD | DEPCTL_SNAK; + } + else + { + udev->regs.er_out[i]->DOEPCTL = 0U; + } + + /* set OUT endpoint transfer length to 0 */ + udev->regs.er_out[i]->DOEPLEN = 0U; + + /* clear all pending OUT endpoint interrupts */ + udev->regs.er_out[i]->DOEPINTF = 0xFFU; + } + + udev->regs.dr->DIEPINTEN |= DIEPINTEN_EPTXFUDEN; + + (void)usb_devint_enable(udev); + + /* configure LPM function */ + if (1U == udev->bp.lpm_enable) + { + udev->dev.pm.lpm_state = LPM_L0; + udev->regs.gr->GLPMCFG = GLPMCFG_LPMEN | GLPMCFG_ACKLPM | GLPMCFG_BESLEN; + } + + return USB_OK; +} + +/*! + \brief enable the USB device mode interrupts + \param[in] udev: pointer to USB device + \param[out] none + \retval operation status +*/ +usb_status usb_devint_enable(usb_core_driver *udev) +{ + /* clear any pending USB OTG interrupts */ + udev->regs.gr->GOTGINTF = 0xFFFFFFFFU; + + /* clear any pending interrupts */ + udev->regs.gr->GINTF = 0xBFFFFFFFU; + + /* enable the USB wakeup and suspend interrupts */ + udev->regs.gr->GINTEN = GINTEN_WKUPIE | GINTEN_SPIE; + + /* enable device_mode-related interrupts */ + if ((uint8_t)USB_USE_FIFO == udev->bp.transfer_mode) + { + udev->regs.gr->GINTEN |= GINTEN_RXFNEIE; + } + + udev->regs.gr->GINTEN |= GINTEN_RSTIE | GINTEN_ENUMFIE | GINTEN_IEPIE | + GINTEN_OEPIE | GINTEN_SOFIE | GINTEN_ISOONCIE | GINTEN_ISOINCIE; + +#ifdef VBUS_SENSING_ENABLED + udev->regs.gr->GINTEN |= GINTEN_SESIE | GINTEN_OTGIE; +#endif /* VBUS_SENSING_ENABLED */ + + if (1U == udev->bp.lpm_enable) + { + udev->regs.gr->GINTEN |= GINTEN_LPMIE; + } + + return USB_OK; +} + +/*! + \brief active the USB endpoint0 transaction + \param[in] udev: pointer to USB device + \param[in] transc: the USB endpoint0 transaction + \param[out] none + \retval operation status +*/ +usb_status usb_transc0_active(usb_core_driver *udev, usb_transc *transc) +{ + __IO uint32_t *reg_addr = NULL; + + uint32_t enum_speed = udev->regs.dr->DSTAT & DSTAT_ES; + + /* get the endpoint number */ + uint8_t ep_num = transc->ep_addr.num; + + if (ep_num) + { + /* not endpoint 0 */ + return USB_FAIL; + } + + if (transc->ep_addr.dir) + { + reg_addr = &udev->regs.er_in[0]->DIEPCTL; + } + else + { + reg_addr = &udev->regs.er_out[0]->DOEPCTL; + } + + /* endpoint 0 is activated after USB clock is enabled */ + *reg_addr &= ~(DEPCTL_MPL | DEPCTL_EPTYPE | DIEPCTL_TXFNUM); + + /* set endpoint 0 maximum packet length */ + *reg_addr |= EP0_MAXLEN[enum_speed]; + + /* activate endpoint */ + *reg_addr |= ((uint32_t)transc->ep_type << 18) | ((uint32_t)ep_num << 22) | DEPCTL_SD0PID | DEPCTL_EPACT; + + return USB_OK; +} + +/*! + \brief active the USB transaction + \param[in] udev: pointer to USB device + \param[in] transc: the USB transaction + \param[out] none + \retval status +*/ +usb_status usb_ep_active(usb_core_driver *udev, uint8_t ep_addr, uint16_t ep_mps, uint8_t ep_attr) +{ + __IO uint32_t *reg_addr = NULL; + uint32_t epinten = 0U; + uint32_t enum_speed = udev->regs.dr->DSTAT & DSTAT_ES; + + usb_transc *transc = NULL; + + /* get the endpoint number */ + uint8_t ep_num = EP_ID(ep_addr); + + /* enable endpoint interrupt number */ + if (EP_DIR(ep_addr)) + { + reg_addr = &udev->regs.er_in[ep_num]->DIEPCTL; + epinten = 1U << ep_num; + transc = &udev->dev.transc_in[ep_num]; + } + else + { + reg_addr = &udev->regs.er_out[ep_num]->DOEPCTL; + epinten = 1U << (16U + ep_num); + transc = &udev->dev.transc_out[ep_num]; + } + + *transc = (usb_transc){ 0 }; + transc->ep_addr.num = ep_num; + transc->ep_addr.dir = EP_DIR(ep_addr) ? 1U : 0U; + transc->max_len = ep_mps; + transc->ep_type = ep_attr & USB_EPTYPE_MASK; + + /* if the endpoint is not active, need change the endpoint control register */ + if (!(*reg_addr & DEPCTL_EPACT)) + { + *reg_addr &= ~(DEPCTL_MPL | DEPCTL_EPTYPE | DIEPCTL_TXFNUM); + + /* set endpoint maximum packet length */ + if (0U == ep_num) + { + *reg_addr |= EP0_MAXLEN[enum_speed]; + } + else + { + *reg_addr |= transc->max_len; + } + + /* activate endpoint */ + *reg_addr |= ((uint32_t)transc->ep_type << 18) | ((uint32_t)ep_num << 22) | DEPCTL_SD0PID | DEPCTL_EPACT; + } + +#ifdef USB_DEDICATED_EP1_ENABLED + if (1U == ep_num) + { + udev->regs.dr->DEP1INTEN |= epinten; + } + else +#endif + { + /* enable the interrupts for this endpoint */ + udev->regs.dr->DAEPINTEN |= epinten; + } + + return USB_OK; +} + +/*! + \brief deactivate the USB transaction + \param[in] udev: pointer to USB device + \param[in] transc: the USB transaction + \param[out] none + \retval status +*/ +usb_status usb_ep_deactivate(usb_core_driver *udev, uint8_t ep_addr) +{ + uint32_t epinten = 0U; + + uint8_t ep_num = EP_ID(ep_addr); + + /* disable endpoint interrupt number */ + if (EP_DIR(ep_addr)) + { + epinten = 1U << ep_num; + + udev->regs.er_in[ep_num]->DIEPCTL &= ~DEPCTL_EPACT; + } + else + { + epinten = 1U << (ep_num + 16U); + + udev->regs.er_out[ep_num]->DOEPCTL &= ~DEPCTL_EPACT; + } + +#ifdef USB_DEDICATED_EP1_ENABLED + if (1U == ep_num) + { + udev->regs.dr->DEP1INTEN &= ~epinten; + } + else +#endif + { + /* disable the interrupts for this endpoint */ + udev->regs.dr->DAEPINTEN &= ~epinten; + } + + return USB_OK; +} + +/*! + \brief configure USB transaction to start IN transfer + \param[in] udev: pointer to USB device + \param[in] transc: the USB IN transaction + \param[out] none + \retval operation status +*/ +usb_status usb_ep_inxfer(usb_core_driver *udev, uint8_t ep_addr, uint8_t *buf, uint16_t count) +{ + usb_status status = USB_OK; + uint8_t ep_num = EP_ID(ep_addr); + + __IO uint32_t epctl = udev->regs.er_in[ep_num]->DIEPCTL; + __IO uint32_t eplen = udev->regs.er_in[ep_num]->DIEPLEN; + + usb_transc *transc = &udev->dev.transc_in[ep_num]; + + eplen &= ~(DEPLEN_TLEN | DEPLEN_PCNT); + + transc->xfer_len = count; + transc->xfer_buf = buf; + transc->xfer_count = 0U; + + /* zero length packet or endpoint 0 */ + if (0U == count) + { + /* set transfer packet count to 1 */ + eplen |= 1U << 19; + } + else + { + /* set transfer packet count */ + if (0U == ep_num) + { + transc->xfer_len = USB_MIN(count, transc->max_len); + + eplen |= 1U << 19; + } + else + { + eplen |= ((count - 1 + transc->max_len) / transc->max_len) << 19; + } + + /* set endpoint transfer length */ + eplen |= count; + + if ((uint8_t)USB_EPTYPE_ISOC == transc->ep_type) + { + eplen |= DIEPLEN_MCNT & (1U << 29); + } + } + + udev->regs.er_in[ep_num]->DIEPLEN = eplen; + + if ((uint8_t)USB_EPTYPE_ISOC == transc->ep_type) + { + if (((udev->regs.dr->DSTAT & DSTAT_FNRSOF) >> 8) & 0x01U) + { + epctl |= DEPCTL_SEVNFRM; + } + else + { + epctl |= DEPCTL_SODDFRM; + } + } + + if ((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) + { + udev->regs.er_in[ep_num]->DIEPDMAADDR = transc->dma_addr; + } + + /* enable the endpoint and clear the NAK */ + epctl |= DEPCTL_CNAK | DEPCTL_EPEN; + + udev->regs.er_in[ep_num]->DIEPCTL = epctl; + + if ((uint8_t)USB_USE_FIFO == udev->bp.transfer_mode) + { + if ((uint8_t)USB_EPTYPE_ISOC != transc->ep_type) + { + /* enable the TX FIFO empty interrupt for this endpoint */ + if (count > 0U) + { + udev->regs.dr->DIEPFEINTEN |= 1U << ep_num; + } + } + else + { + (void)usb_txfifo_write(&udev->regs, transc->xfer_buf, ep_num, (uint16_t)transc->xfer_len); + } + } + + return status; +} + +/*! + \brief configure USB transaction to start OUT transfer + \param[in] udev: pointer to USB device + \param[in] transc: the USB OUT transaction + \param[out] none + \retval status +*/ +usb_status usb_ep_outxfer(usb_core_driver *udev, uint8_t ep_addr, uint8_t *buf, uint16_t count) +{ + usb_status status = USB_OK; + + uint8_t ep_num = EP_ID(ep_addr); + + uint32_t epctl = udev->regs.er_out[ep_num]->DOEPCTL; + uint32_t eplen = udev->regs.er_out[ep_num]->DOEPLEN; + + usb_transc *transc = &udev->dev.transc_out[ep_num]; + + transc->xfer_buf = buf; + transc->xfer_len = count; + + eplen &= ~(DEPLEN_TLEN | DEPLEN_PCNT); + + /* zero length packet or endpoint 0 */ + if ((0U == count) || (0U == ep_num)) + { + /* set the transfer length to max packet size */ + eplen |= transc->max_len; + + /* set the transfer packet count to 1 */ + eplen |= 1U << 19; + } + else + { + /* configure the transfer size and packet count as follows: + * pktcnt = N + * xfersize = N * maxpacket + */ + uint32_t packet_count = (count + transc->max_len - 1) / transc->max_len; + + eplen |= packet_count << 19; + eplen |= packet_count * transc->max_len; + } + + udev->regs.er_out[ep_num]->DOEPLEN = eplen; + + if ((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) + { + udev->regs.er_out[ep_num]->DOEPDMAADDR = transc->dma_addr; + } + + if ((uint8_t)USB_EPTYPE_ISOC == transc->ep_type) + { + if (transc->frame_num) + { + epctl |= DEPCTL_SD1PID; + } + else + { + epctl |= DEPCTL_SD0PID; + } + } + + /* enable the endpoint and clear the NAK */ + epctl |= DEPCTL_EPEN | DEPCTL_CNAK; + + udev->regs.er_out[ep_num]->DOEPCTL = epctl; + + return status; +} + +/*! + \brief set the USB transaction STALL status + \param[in] udev: pointer to USB device + \param[in] transc: the USB transaction + \param[out] none + \retval status +*/ +usb_status usb_ep_stall(usb_core_driver *udev, uint8_t ep_addr) +{ + __IO uint32_t *reg_addr = NULL; + + uint8_t ep_num = EP_ID(ep_addr); + + if (EP_DIR(ep_addr)) + { + reg_addr = &(udev->regs.er_in[ep_num]->DIEPCTL); + + /* set the endpoint disable bit */ + if (*reg_addr & DEPCTL_EPEN) + { + *reg_addr |= DEPCTL_EPD; + } + } + else + { + /* set the endpoint STALL bit */ + reg_addr = &(udev->regs.er_out[ep_num]->DOEPCTL); + } + + /* set the endpoint STALL bit */ + *reg_addr |= DEPCTL_STALL; + + return USB_OK; +} + +/*! + \brief clear the USB transaction STALL status + \param[in] udev: pointer to USB device + \param[in] transc: the USB transaction + \param[out] none + \retval operation status +*/ +usb_status usb_ep_clrstall(usb_core_driver *udev, uint8_t ep_addr) +{ + __IO uint32_t *reg_addr = NULL; + uint32_t ep_type = 0U; + + uint8_t ep_num = EP_ID(ep_addr); + + if (EP_DIR(ep_addr)) + { + reg_addr = &(udev->regs.er_in[ep_num]->DIEPCTL); + } + else + { + reg_addr = &(udev->regs.er_out[ep_num]->DOEPCTL); + } + + /* clear the endpoint STALL bit */ + *reg_addr &= ~DEPCTL_STALL; + + ep_type = (DEPCTL_EPTYPE & *reg_addr) >> 18; + + /* reset data PID of the periodic endpoints */ + if (((uint8_t)USB_EPTYPE_INTR == ep_type) || ((uint8_t)USB_EPTYPE_BULK == ep_type)) + { + *reg_addr |= DEPCTL_SD0PID; + } + + return USB_OK; +} + +/*! + \brief read device IN endpoint interrupt flag register + \param[in] udev: pointer to USB device + \param[in] ep_num: endpoint number + \param[out] none + \retval interrupt value +*/ +uint32_t usb_iepintr_read(usb_core_driver *udev, uint8_t ep_num) +{ + __IO uint32_t value = 0U; + uint32_t fifoemptymask, commonintmask; + + commonintmask = udev->regs.dr->DIEPINTEN; + fifoemptymask = udev->regs.dr->DIEPFEINTEN; + + /* check FIFO empty interrupt enable bit */ + commonintmask |= ((fifoemptymask >> ep_num) & 0x1U) << 7; + + value = udev->regs.er_in[ep_num]->DIEPINTF & commonintmask; + + return value; +} + +/*! + \brief configures OUT endpoint 0 to receive SETUP packets + \param[in] udev: pointer to USB device + \param[out] none + \retval none +*/ +void usb_ctlep_startout(usb_core_driver *udev) +{ + /* set OUT endpoint 0 receive length to 24 bytes, 1 packet and 3 SETUP packets */ + udev->regs.er_out[0]->DOEPLEN = DOEP0_TLEN(8U * 3U) | DOEP0_PCNT(1U) | DOEP0_STPCNT(3U); + + if ((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) + { + udev->regs.er_out[0]->DOEPDMAADDR = (uint32_t)&udev->dev.control.req; + + /* endpoint enable */ + udev->regs.er_out[0]->DOEPCTL |= DEPCTL_EPACT | DEPCTL_EPEN; + } +} + +/*! + \brief active remote wakeup signaling + \param[in] udev: pointer to USB device + \param[out] none + \retval none +*/ +void usb_rwkup_active(usb_core_driver *udev) +{ + if (udev->dev.pm.dev_remote_wakeup) + { + if (udev->regs.dr->DSTAT & DSTAT_SPST) + { + if (udev->bp.low_power) + { + /* ungate USB core clock */ + *udev->regs.PWRCLKCTL &= ~(PWRCLKCTL_SHCLK | PWRCLKCTL_SUCLK); + } + + /* active remote wakeup signaling */ + udev->regs.dr->DCTL |= DCTL_RWKUP; + + usb_mdelay(5U); + + udev->regs.dr->DCTL &= ~DCTL_RWKUP; + } + } +} + +/*! + \brief USB device suspend + \param[in] udev: pointer to USB device + \param[out] none + \retval none +*/ +void usb_dev_suspend(usb_core_driver *udev) +{ + __IO uint32_t devstat = udev->regs.dr->DSTAT; + + if ((devstat & DSTAT_SPST) && (udev->bp.low_power)) + { + /* switch-off the USB clocks */ + *udev->regs.PWRCLKCTL |= PWRCLKCTL_SHCLK; + + /* enter DEEP_SLEEP mode with LDO in low power mode */ + pmu_to_deepsleepmode(WFI_CMD); + } +} + +/*! + \brief stop the device and clean up FIFOs + \param[in] udev: pointer to USB device + \param[out] none + \retval none +*/ +void usb_dev_stop(usb_core_driver *udev) +{ + uint32_t i; + + udev->dev.cur_status = 1U; + + /* clear all interrupt flag and enable bits */ + for (i = 0U; i < udev->bp.num_ep; i++) + { + udev->regs.er_in[i]->DIEPINTF = 0xFFU; + udev->regs.er_out[i]->DOEPINTF = 0xFFU; + } + + udev->regs.dr->DIEPINTEN = 0U; + udev->regs.dr->DOEPINTEN = 0U; + udev->regs.dr->DAEPINTEN = 0U; + udev->regs.dr->DAEPINT = 0xFFFFFFFFU; + + /* flush the FIFO */ + (void)usb_rxfifo_flush(&udev->regs); + (void)usb_txfifo_flush(&udev->regs, 0x10U); +} + +/*! + \brief usb battery charging detect operation + \param[in] udev: pointer to USB device instance + \param[out] none + \retval bcd type +*/ +bcd_type usbd_bcd_detect(usb_core_driver *udev) +{ + udev->regs.gr->GCCFG |= GCCFG_DCDEN; + + usb_mdelay(1000U); + + if (0U == (udev->regs.gr->GCCFG & GCCFG_DCDF)) + { + udev->regs.gr->GCCFG &= ~GCCFG_DCDEN; + + return BCD_ERROR; + } + + udev->regs.gr->GCCFG &= ~GCCFG_DCDEN; + usb_mdelay(20U); + udev->regs.gr->GCCFG |= GCCFG_PEMEN; + usb_mdelay(50U); + + if (udev->regs.gr->GCCFG & GCCFG_PDF) + { + udev->regs.gr->GCCFG &= ~GCCFG_PEMEN; + usb_mdelay(20U); + udev->regs.gr->GCCFG |= GCCFG_SDMEN; + usb_mdelay(50U); + + if (udev->regs.gr->GCCFG & GCCFG_SDF) + { + udev->regs.gr->GCCFG &= ~GCCFG_SDMEN; + usb_mdelay(20U); + + return BCD_DCP; + } + else + { + udev->regs.gr->GCCFG &= ~GCCFG_SDMEN; + usb_mdelay(20U); + + return BCD_CDP; + } + } + else if (udev->regs.gr->GCCFG & GCCFG_PS2F) + { + udev->regs.gr->GCCFG &= ~GCCFG_PEMEN; + usb_mdelay(20U); + + return BCD_PS2; + } + else + { + udev->regs.gr->GCCFG &= ~GCCFG_PEMEN; + usb_mdelay(20U); + + return BCD_SDP; + } +} + +#endif /* BSP_USING_USB && RT_USING_USB_DEVICE */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_dev.h b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_dev.h new file mode 100644 index 000000000000..811aa3c321ee --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_dev.h @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#ifndef DRV_USB_DEV_H +#define DRV_USB_DEV_H + +#include "drv_usb_core.h" + +#if defined(BSP_USING_USB) && defined(RT_USING_USB_DEVICE) + +#define EP_IN(x) ((uint8_t)(0x80U | (x))) /*!< device IN endpoint */ +#define EP_OUT(x) ((uint8_t)(x)) /*!< device OUT endpoint */ +#define EP_MAX_PACKET_SIZE_MASK 0x07FFU /*!< endpoint maximum packet size mask */ + +#define USB_MIN(a, b) (((a) < (b)) ? (a) : (b)) + +/* static inline function definitions */ + +/*! + \brief configure the USB device to be disconnected + \param[in] udev: pointer to USB device + \param[out] none + \retval none +*/ +__STATIC_INLINE void usb_dev_disconnect(usb_core_driver *udev) +{ + udev->regs.dr->DCTL |= DCTL_SD; +} + +/*! + \brief configure the USB device to be connected + \param[in] udev: pointer to USB device + \param[out] none + \retval none +*/ +__STATIC_INLINE void usb_dev_connect(usb_core_driver *udev) +{ + udev->regs.dr->DCTL &= ~DCTL_SD; +} + +/*! + \brief set the USB device address + \param[in] udev: pointer to USB device + \param[in] dev_addr: device address for setting + \param[out] none + \retval none +*/ +__STATIC_INLINE void usb_devaddr_set(usb_core_driver *udev, uint8_t dev_addr) +{ + udev->regs.dr->DCFG &= ~DCFG_DAR; + udev->regs.dr->DCFG |= (uint32_t)dev_addr << 4; +} + +/*! + \brief read device all OUT endpoint interrupt register + \param[in] udev: pointer to USB device + \param[out] none + \retval interrupt status +*/ +__STATIC_INLINE uint32_t usb_oepintnum_read(usb_core_driver *udev) +{ + uint32_t value = udev->regs.dr->DAEPINT; + + value &= udev->regs.dr->DAEPINTEN; + + return (value & DAEPINT_OEPITB) >> 16; +} + +/*! + \brief read device OUT endpoint interrupt flag register + \param[in] udev: pointer to USB device + \param[in] ep_num: endpoint number + \param[out] none + \retval interrupt status +*/ +__STATIC_INLINE uint32_t usb_oepintr_read(usb_core_driver *udev, uint8_t ep_num) +{ + uint32_t value = udev->regs.er_out[ep_num]->DOEPINTF; + + value &= udev->regs.dr->DOEPINTEN; + + return value; +} + +/*! + \brief read device all IN endpoint interrupt register + \param[in] udev: pointer to USB device + \param[out] none + \retval interrupt status +*/ +__STATIC_INLINE uint32_t usb_iepintnum_read(usb_core_driver *udev) +{ + uint32_t value = udev->regs.dr->DAEPINT; + + value &= udev->regs.dr->DAEPINTEN; + + return value & DAEPINT_IEPITB; +} + +/*! + \brief set remote wakeup signaling + \param[in] udev: pointer to USB device + \param[out] none + \retval none +*/ +__STATIC_INLINE void usb_rwkup_set(usb_core_driver *udev) +{ + if (udev->dev.pm.dev_remote_wakeup) + { + /* enable remote wakeup signaling */ + udev->regs.dr->DCTL |= DCTL_RWKUP; + } +} + +/*! + \brief reset remote wakeup signaling + \param[in] udev: pointer to USB device + \param[out] none + \retval none +*/ +__STATIC_INLINE void usb_rwkup_reset(usb_core_driver *udev) +{ + if (udev->dev.pm.dev_remote_wakeup) + { + /* disable remote wakeup signaling */ + udev->regs.dr->DCTL &= ~DCTL_RWKUP; + } +} + +/*! + \brief enable USB BCD function + \param[in] udev: pointer to USB device + \param[out] none + \retval none +*/ +__STATIC_INLINE void usb_bcd_enable(usb_core_driver *udev) +{ + udev->regs.gr->GCCFG |= GCCFG_BCDEN; +} + +/*! + \brief disable USB BCD function + \param[in] udev: pointer to USB device + \param[out] none + \retval none +*/ +__STATIC_INLINE void usb_bcd_disable(usb_core_driver *udev) +{ + udev->regs.gr->GCCFG &= ~GCCFG_BCDEN; +} + +/* function declarations */ +/* initialize USB core registers for device mode */ +usb_status usb_devcore_init(usb_core_driver *udev); +/* enable the USB device mode interrupts */ +usb_status usb_devint_enable(usb_core_driver *udev); +/* active the USB endpoint 0 transaction */ +usb_status usb_transc0_active(usb_core_driver *udev, usb_transc *transc); +/* active the USB transaction */ +usb_status usb_ep_active(usb_core_driver *udev, uint8_t ep_addr, uint16_t ep_mps, uint8_t ep_attr); +/* deactivate the USB transaction */ +usb_status usb_ep_deactivate(usb_core_driver *udev, uint8_t ep_addr); +/* configure USB transaction to start IN transfer */ +usb_status usb_ep_inxfer(usb_core_driver *udev, uint8_t ep_addr, uint8_t *buf, uint16_t count); +/* configure USB transaction to start OUT transfer */ +usb_status usb_ep_outxfer(usb_core_driver *udev, uint8_t ep_addr, uint8_t *buf, uint16_t count); +/* set the USB transaction STALL status */ +usb_status usb_ep_stall(usb_core_driver *udev, uint8_t ep_addr); +/* clear the USB transaction STALL status */ +usb_status usb_ep_clrstall(usb_core_driver *udev, uint8_t ep_addr); +/* read device IN endpoint interrupt flag register */ +uint32_t usb_iepintr_read(usb_core_driver *udev, uint8_t ep_num); +/* configures OUT endpoint 0 to receive SETUP packets */ +void usb_ctlep_startout(usb_core_driver *udev); +/* active remote wakeup signaling */ +void usb_rwkup_active(usb_core_driver *udev); +/* active USB core clock */ +void usb_clock_active(usb_core_driver *udev); +/* USB device suspend */ +void usb_dev_suspend(usb_core_driver *udev); +/* stop the device and clean up FIFOs */ +void usb_dev_stop(usb_core_driver *udev); +/* USB battery charging detect operation */ +bcd_type usbd_bcd_detect(usb_core_driver *udev); + +#endif /* BSP_USING_USB && RT_USING_USB_DEVICE */ + +#endif /* DRV_USB_DEV_H */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_host.c b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_host.c new file mode 100644 index 000000000000..527b184e00d8 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_host.c @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#include "drv_usb_hw.h" +#include "drv_usb_host.h" + +#if defined(BSP_USING_USB) && defined(RT_USING_USB_HOST) + +const uint32_t PIPE_DPID[2] = { + PIPE_DPID_DATA0, + PIPE_DPID_DATA1 +}; + +/*! + \brief initializes USB core for host mode + \param[in] udev: pointer to selected USB host + \param[out] none + \retval operation status +*/ +usb_status usb_host_init(usb_core_driver *udev) +{ + uint32_t i = 0U, inten = 0U; + + uint32_t nptxfifolen = 0U; + uint32_t ptxfifolen = 0U; + + /* restart the PHY Clock */ + *udev->regs.PWRCLKCTL = 0U; + + /* support HS, FS and LS */ + udev->regs.hr->HCTL &= ~HCTL_SPDFSLS; + + /* set RX FIFO size */ + udev->regs.gr->GRFLEN = USB_HOST_RX_FIFO_SIZE; + + /* set non-periodic TX FIFO size and address */ + nptxfifolen |= USB_HOST_RX_FIFO_SIZE; + nptxfifolen |= USB_HOST_TX_NPFIFO_SIZE << 16; + udev->regs.gr->DIEP0TFLEN_HNPTFLEN = nptxfifolen; + + /* set periodic TX FIFO size and address */ + ptxfifolen |= USB_HOST_RX_FIFO_SIZE + USB_HOST_TX_PFIFO_SIZE; + ptxfifolen |= USB_HOST_TX_PFIFO_SIZE << 16; + udev->regs.gr->HPTFLEN = ptxfifolen; + +#ifdef USE_OTG_MODE + /* clear host set HNP enable in the usb_otg control register */ + udev->regs.gr->GOTGCS &= ~GOTGCS_HHNPEN; +#endif /* USE_OTG_MODE */ + + /* make sure the FIFOs are flushed */ + + /* flush all TX FIFOs in device or host mode */ + usb_txfifo_flush(&udev->regs, 0x10U); + + /* flush the entire RX FIFO */ + usb_rxfifo_flush(&udev->regs); + + /* disable all interrupts */ + udev->regs.gr->GINTEN = 0U; + + /* clear any pending USB OTG interrupts */ + udev->regs.gr->GOTGINTF = 0xFFFFFFFFU; + + /* enable the USB wakeup and suspend interrupts */ + udev->regs.gr->GINTF = 0xBFFFFFFFU; + + /* clear all pending host channel interrupts */ + for (i = 0U; i < udev->bp.num_pipe; i++) + { + udev->regs.pr[i]->HCHINTF = 0xFFFFFFFFU; + udev->regs.pr[i]->HCHINTEN = 0U; + } + +#ifndef USE_OTG_MODE + usb_portvbus_switch(udev, 1U); +#endif /* USE_OTG_MODE */ + + udev->regs.gr->GINTEN = GINTEN_WKUPIE | GINTEN_SPIE; + + /* enable host_mode-related interrupts */ + if (USB_USE_FIFO == udev->bp.transfer_mode) + { + inten = GINTEN_RXFNEIE; + } + + /* configure USBHS interrupts */ + inten |= GINTEN_HPIE | GINTEN_HCIE | GINTEN_ISOINCIE; + udev->regs.gr->GINTEN |= inten; + + inten = GINTEN_DISCIE | GINTEN_SOFIE; + udev->regs.gr->GINTEN &= ~inten; + + return USB_OK; +} + +/*! + \brief control the VBUS to power + \param[in] udev: pointer to selected USB host + \param[in] state: VBUS state + \param[out] none + \retval none +*/ +void usb_portvbus_switch(usb_core_driver *udev, uint8_t state) +{ + uint32_t port = 0U; + + /* enable or disable the external charge pump */ + usb_vbus_drive(state); + + /* turn on the host port power. */ + port = usb_port_read(udev); + + if ((!(port & HPCS_PP)) && (1U == state)) + { + port |= HPCS_PP; + } + + if ((port & HPCS_PP) && (0U == state)) + { + port &= ~HPCS_PP; + } + + *udev->regs.HPCS = port; + + usb_mdelay(200U); +} + +/*! + \brief reset host port + \param[in] udev: pointer to USB device + \param[out] none + \retval operation status +*/ +uint32_t usb_port_reset(usb_core_driver *udev) +{ + __IO uint32_t port = usb_port_read(udev); + + *udev->regs.HPCS = port | HPCS_PRST; + + usb_mdelay(20U); /* see note */ + + *udev->regs.HPCS = port & ~HPCS_PRST; + + usb_mdelay(20U); + + return 1U; +} + +/*! + \brief initialize host pipe + \param[in] udev: pointer to USB device + \param[in] pipe_num: host pipe number which is in (0..7) + \param[out] none + \retval operation status +*/ +usb_status usb_pipe_init(usb_core_driver *udev, uint8_t pipe_num) +{ + usb_status status = USB_OK; + + __IO uint32_t pp_ctl = 0U; + __IO uint32_t pp_inten = HCHINTEN_TFIE; + + usb_pipe *pp = &udev->host.pipe[pipe_num]; + + /* clear old interrupt conditions for this host channel */ + udev->regs.pr[pipe_num]->HCHINTF = 0xFFFFFFFFU; + + if (USB_USE_DMA == udev->bp.transfer_mode) + { + pp_inten |= HCHINTEN_DMAERIE; + } + + if (pp->ep.dir) + { + pp_inten |= HCHINTEN_BBERIE; + } + + /* enable channel interrupts required for this transfer */ + switch (pp->ep.type) + { + case USB_EPTYPE_CTRL: + case USB_EPTYPE_BULK: + pp_inten |= HCHINTEN_STALLIE | HCHINTEN_USBERIE | HCHINTEN_DTERIE | HCHINTEN_NAKIE; + + if (!pp->ep.dir) + { + if (PORT_SPEED_HIGH == pp->dev_speed) + { + pp_inten |= HCHINTEN_NYETIE; + pp_inten |= HCHINTEN_ACKIE; + } + } + break; + + case USB_EPTYPE_INTR: + pp_inten |= HCHINTEN_STALLIE | HCHINTEN_USBERIE | HCHINTEN_DTERIE | HCHINTEN_NAKIE | HCHINTEN_REQOVRIE; + break; + + case USB_EPTYPE_ISOC: + pp_inten |= HCHINTEN_REQOVRIE | HCHINTEN_ACKIE; + + if (pp->ep.dir) + { + pp_inten |= HCHINTEN_USBERIE; + } + break; + + default: + break; + } + + udev->regs.pr[pipe_num]->HCHINTEN = pp_inten; + + /* enable the top level host channel interrupt */ + udev->regs.hr->HACHINTEN |= 1U << pipe_num; + + /* make sure host channel interrupts are enabled */ + udev->regs.gr->GINTEN |= GINTEN_HCIE; + + /* program the host channel control register */ + pp_ctl |= PIPE_CTL_DAR(pp->dev_addr); + pp_ctl |= PIPE_CTL_EPNUM(pp->ep.num); + pp_ctl |= PIPE_CTL_EPDIR(pp->ep.dir); + pp_ctl |= PIPE_CTL_EPTYPE(pp->ep.type); + pp_ctl |= PIPE_CTL_LSD(PORT_SPEED_LOW == pp->dev_speed); + + pp_ctl |= pp->ep.mps; + pp_ctl |= ((uint32_t)(USB_EPTYPE_INTR == pp->ep.type) << 29) & HCHCTL_ODDFRM; + + udev->regs.pr[pipe_num]->HCHCTL = pp_ctl; + + return status; +} + +/*! + \brief prepare host channel for transferring packets + \param[in] udev: pointer to USB device + \param[in] pipe_num: host pipe number which is in (0..7) + \param[out] none + \retval operation status +*/ +usb_status usb_pipe_xfer(usb_core_driver *udev, uint8_t pipe_num) +{ + usb_status status = USB_OK; + + uint16_t dword_len = 0U; + uint16_t packet_count = 0U; + + __IO uint32_t pp_ctl = 0U; + + usb_pipe *pp = &udev->host.pipe[pipe_num]; + + uint16_t max_packet_len = pp->ep.mps; + + /* compute the expected number of packets associated to the transfer */ + if (pp->xfer_len > 0U) + { + packet_count = (uint16_t)((pp->xfer_len + max_packet_len - 1U) / max_packet_len); + + if (packet_count > HC_MAX_PACKET_COUNT) + { + packet_count = HC_MAX_PACKET_COUNT; + pp->xfer_len = (uint16_t)(packet_count * max_packet_len); + } + } + else + { + packet_count = 1U; + } + + if (pp->ep.dir) + { + pp->xfer_len = (uint16_t)(packet_count * max_packet_len); + } + + /* initialize the host channel transfer information */ + udev->regs.pr[pipe_num]->HCHLEN = pp->xfer_len | pp->DPID | PIPE_XFER_PCNT(packet_count); + + if (USB_USE_DMA == udev->bp.transfer_mode) + { + udev->regs.pr[pipe_num]->HCHDMAADDR = (unsigned int)pp->xfer_buf; + } + + pp_ctl = udev->regs.pr[pipe_num]->HCHCTL; + + if (usb_frame_even(udev)) + { + pp_ctl |= HCHCTL_ODDFRM; + } + else + { + pp_ctl &= ~HCHCTL_ODDFRM; + } + + /* set host channel enabled */ + pp_ctl |= HCHCTL_CEN; + pp_ctl &= ~HCHCTL_CDIS; + + udev->regs.pr[pipe_num]->HCHCTL = pp_ctl; + + if (USB_USE_FIFO == udev->bp.transfer_mode) + { + if ((0U == pp->ep.dir) && (pp->xfer_len > 0U)) + { + switch (pp->ep.type) + { + /* non-periodic transfer */ + case USB_EPTYPE_CTRL: + case USB_EPTYPE_BULK: + dword_len = (uint16_t)((pp->xfer_len + 3U) / 4U); + + /* check if there is enough space in FIFO space */ + if (dword_len > (udev->regs.gr->HNPTFQSTAT & HNPTFQSTAT_NPTXFS)) + { + /* need to process data in nptxfempty interrupt */ + udev->regs.gr->GINTEN |= GINTEN_NPTXFEIE; + } + break; + + /* periodic transfer */ + case USB_EPTYPE_INTR: + case USB_EPTYPE_ISOC: + dword_len = (uint16_t)((pp->xfer_len + 3U) / 4U); + + /* check if there is enough space in FIFO space */ + if (dword_len > (udev->regs.hr->HPTFQSTAT & HPTFQSTAT_PTXFS)) + { + /* need to process data in ptxfempty interrupt */ + udev->regs.gr->GINTEN |= GINTEN_PTXFEIE; + } + break; + + default: + break; + } + + /* write packet into the TX FIFO */ + usb_txfifo_write(&udev->regs, pp->xfer_buf, pipe_num, (uint16_t)pp->xfer_len); + } + } + + return status; +} + +/*! + \brief halt pipe + \param[in] udev: pointer to USB device + \param[in] pipe_num: host pipe number which is in (0..7) + \param[out] none + \retval operation status +*/ +usb_status usb_pipe_halt(usb_core_driver *udev, uint8_t pipe_num) +{ + __IO uint32_t pp_ctl = udev->regs.pr[pipe_num]->HCHCTL; + + uint8_t ep_type = (uint8_t)((pp_ctl & HCHCTL_EPTYPE) >> 18U); + + pp_ctl |= HCHCTL_CEN | HCHCTL_CDIS; + + switch (ep_type) + { + case USB_EPTYPE_CTRL: + case USB_EPTYPE_BULK: + if (0U == (udev->regs.gr->HNPTFQSTAT & HNPTFQSTAT_NPTXFS)) + { + pp_ctl &= ~HCHCTL_CEN; + } + break; + + case USB_EPTYPE_INTR: + case USB_EPTYPE_ISOC: + if (0U == (udev->regs.hr->HPTFQSTAT & HPTFQSTAT_PTXFS)) + { + pp_ctl &= ~HCHCTL_CEN; + } + break; + + default: + break; + } + + udev->regs.pr[pipe_num]->HCHCTL = pp_ctl; + + return USB_OK; +} + +/*! + \brief configure host pipe to do ping operation + \param[in] udev: pointer to USB device + \param[in] pipe_num: host pipe number which is in (0..7) + \param[out] none + \retval operation status +*/ +usb_status usb_pipe_ping(usb_core_driver *udev, uint8_t pipe_num) +{ + uint32_t pp_ctl = 0U; + + udev->regs.pr[pipe_num]->HCHLEN = HCHLEN_PING; + + pp_ctl = udev->regs.pr[pipe_num]->HCHCTL; + + pp_ctl |= HCHCTL_CEN; + pp_ctl &= ~HCHCTL_CDIS; + + udev->regs.pr[pipe_num]->HCHCTL = pp_ctl; + + return USB_OK; +} + +/*! + \brief stop the USB host and clean up FIFO + \param[in] udev: pointer to USB device + \param[out] none + \retval none +*/ +void usb_host_stop(usb_core_driver *udev) +{ + uint32_t i; + __IO uint32_t pp_ctl = 0U; + + udev->regs.hr->HACHINTEN = 0x0U; + udev->regs.hr->HACHINT = 0xFFFFFFFFU; + + /* flush out any leftover queued requests. */ + for (i = 0U; i < udev->bp.num_pipe; i++) + { + pp_ctl = udev->regs.pr[i]->HCHCTL; + + pp_ctl &= ~(HCHCTL_CEN | HCHCTL_EPDIR); + pp_ctl |= HCHCTL_CDIS; + + udev->regs.pr[i]->HCHCTL = pp_ctl; + } + + /* flush the FIFO */ + usb_rxfifo_flush(&udev->regs); + usb_txfifo_flush(&udev->regs, 0x10U); +} + +#endif /* BSP_USING_USB && RT_USING_USB_HOST */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_host.h b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_host.h new file mode 100644 index 000000000000..eec31f6ae5aa --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_host.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#ifndef DRV_USB_HOST_H +#define DRV_USB_HOST_H + +#include "drv_usb_core.h" + +#if defined(BSP_USING_USB) && defined(RT_USING_USB_HOST) + +typedef enum +{ + USBH_OK = 0U, + USBH_BUSY, + USBH_FAIL, + USBH_NOT_SUPPORTED, + USBH_UNRECOVERED_ERROR, + USBH_SPEED_UNKNOWN_ERROR, + USBH_APPLY_DEINIT +} usbh_status; + +/*! + \brief get USB even frame + \param[in] udev: pointer to USB device + \param[out] none + \retval USB even frame state +*/ +__STATIC_INLINE uint8_t usb_frame_even(usb_core_driver *udev) +{ + return (uint8_t) !(udev->regs.hr->HFINFR & 0x01U); +} + +/*! + \brief read USB port + \param[in] udev: pointer to USB device + \param[out] none + \retval port status +*/ +__STATIC_INLINE uint32_t usb_port_read(usb_core_driver *udev) +{ + return *udev->regs.HPCS & ~(HPCS_PE | HPCS_PCD | HPCS_PEDC); +} + +/*! + \brief get USB current speed + \param[in] udev: pointer to USB device + \param[out] none + \retval USB current speed +*/ +__STATIC_INLINE uint32_t usb_curspeed_get(usb_core_driver *udev) +{ + return *udev->regs.HPCS & HPCS_PS; +} + +/*! + \brief get USB current frame + \param[in] udev: pointer to USB device + \param[out] none + \retval USB current frame +*/ +__STATIC_INLINE uint32_t usb_curframe_get(usb_core_driver *udev) +{ + return (udev->regs.hr->HFINFR & 0xFFFFU); +} + +/* function declarations */ +/* initializes USB core for host mode */ +usb_status usb_host_init(usb_core_driver *udev); +/* control the VBUS to power */ +void usb_portvbus_switch(usb_core_driver *udev, uint8_t state); +/* reset host port */ +uint32_t usb_port_reset(usb_core_driver *udev); +/* initialize host pipe */ +usb_status usb_pipe_init(usb_core_driver *udev, uint8_t pipe_num); +/* prepare host pipe for transferring packets */ +usb_status usb_pipe_xfer(usb_core_driver *udev, uint8_t pipe_num); +/* halt host pipe */ +usb_status usb_pipe_halt(usb_core_driver *udev, uint8_t pipe_num); +/* configure host pipe to do ping operation */ +usb_status usb_pipe_ping(usb_core_driver *udev, uint8_t pipe_num); +/* stop the USB host and clean up FIFO */ +void usb_host_stop(usb_core_driver *udev); + +#endif /* BSP_USING_USB && RT_USING_USB_HOST */ + +#endif /* DRV_USB_HOST_H */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_hw.h b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_hw.h new file mode 100644 index 000000000000..413252160f54 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_hw.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#ifndef DRV_USB_HW_H +#define DRV_USB_HW_H + +#include "drv_usb_core.h" + +/* function declarations */ +/* configure USB clock */ +void usb_rcu_config(uint32_t usb_periph); +/* configure USB data line GPIO */ +void usb_gpio_config(void); +/* configure USB interrupt */ +void usb_intr_config(uint32_t usb_periph); +/* delay in microseconds */ +void usb_udelay(const uint32_t usec); +/* delay in milliseconds */ +void usb_mdelay(const uint32_t msec); + +/* configure USB clock */ +void pllusb_rcu_config(uint32_t usb_periph); + +#if defined(RT_USING_USB_HOST) +/* configure systick */ +void systick_config(void); +/* configure USB VBus */ +void usb_vbus_config(void); +/* drive USB VBus */ +void usb_vbus_drive(uint8_t State); +#endif /* RT_USING_USB_HOST */ + +#endif /* DRV_USB_HW_H */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_regs.h b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_regs.h new file mode 100644 index 000000000000..9bfb372bf375 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usb_regs.h @@ -0,0 +1,728 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#ifndef DRV_USB_REGS_H +#define DRV_USB_REGS_H + +#include "usb_conf.h" + +#define USBHS0 USBHS_BASE +#define USBHS1 (USBHS_BASE + 0x00040000U) + +#define USBHS0_REG_BASE USBHS0 /*!< base address of USBHS0 registers */ +#define USBHS1_REG_BASE USBHS1 /*!< base address of USBHS1 registers */ + +/* register definition */ + +#define USBHS_MAX_TX_FIFOS 16U /*!< FIFO number */ + +typedef struct +{ + __IO uint32_t GOTGCS; /*!< USB global OTG control and status register 000h */ + __IO uint32_t GOTGINTF; /*!< USB global OTG interrupt flag register 004h */ + __IO uint32_t GAHBCS; /*!< USB global AHB control and status register 008h */ + __IO uint32_t GUSBCS; /*!< USB global USB control and status register 00Ch */ + __IO uint32_t GRSTCTL; /*!< USB global reset control register 010h */ + __IO uint32_t GINTF; /*!< USB global interrupt flag register 014h */ + __IO uint32_t GINTEN; /*!< USB global interrupt enable register 018h */ + __IO uint32_t GRSTATR; /*!< USB receive status debug read register 01Ch */ + __IO uint32_t GRSTATP; /*!< USB receive status and pop register 020h */ + __IO uint32_t GRFLEN; /*!< USB global receive FIFO length register 024h */ + __IO uint32_t DIEP0TFLEN_HNPTFLEN; /*!< USB device IN endpoint 0/host non-periodic transmit FIFO length register 028h */ + __IO uint32_t HNPTFQSTAT; /*!< USB host non-periodic FIFO/queue status register 02Ch */ + uint32_t Reserved30[2]; /*!< Reserved 030h */ + __IO uint32_t GCCFG; /*!< USB global core configuration register 038h */ + __IO uint32_t CID; /*!< USB core ID register 03Ch */ + uint32_t Reserved40[5]; /*!< Reserved 040h-053h */ + __IO uint32_t GLPMCFG; /*!< LPM configuration register 54h */ + __IO uint32_t PWRD; /*!< power down register 58h */ + uint32_t Reserved5C; /*!< Reserved 5Ch */ + __IO uint32_t ADPCTL; /*!< ADP control and status register 60h */ + uint32_t Reserved64[3]; /*!< Reserved 64h-6Fh */ + __IO uint32_t PHYCTL; /*!< USBHS PHY control register 70h */ + uint32_t Reserved74[35]; /*!< Reserved 74h-FFh */ + __IO uint32_t HPTFLEN; /*!< USB host periodic transmit FIFO length register 100h */ + __IO uint32_t DIEPTFLEN[15]; /*!< USB device IN endpoint transmit FIFO length register 104h */ +} usb_gr; + +typedef struct +{ + __IO uint32_t HCTL; /*!< USB host control register 400h */ + __IO uint32_t HFT; /*!< USB host frame interval register 404h */ + __IO uint32_t HFINFR; /*!< USB host frame information remaining register 408h */ + uint32_t Reserved40C; /*!< Reserved 40Ch */ + __IO uint32_t HPTFQSTAT; /*!< USB host periodic transmit FIFO/queue status register 410h */ + __IO uint32_t HACHINT; /*!< USB host all channels interrupt register 414h */ + __IO uint32_t HACHINTEN; /*!< USB host all channels interrupt enable register 418h */ +} usb_hr; + +typedef struct +{ + __IO uint32_t HCHCTL; /*!< USB host channel control register 500h */ + __IO uint32_t HCHSTCTL; /*!< Reserved 504h */ + __IO uint32_t HCHINTF; /*!< USB host channel interrupt flag register 508h */ + __IO uint32_t HCHINTEN; /*!< USB host channel interrupt enable register 50Ch */ + __IO uint32_t HCHLEN; /*!< USB host channel transfer length register 510h */ + __IO uint32_t HCHDMAADDR; /*!< USB host channel-x DMA address register 514h*/ + uint32_t Reserved[2]; +} usb_pr; + +typedef struct +{ + __IO uint32_t DCFG; /*!< USB device configuration register 800h */ + __IO uint32_t DCTL; /*!< USB device control register 804h */ + __IO uint32_t DSTAT; /*!< USB device status register 808h */ + uint32_t Reserved0C; /*!< Reserved 80Ch */ + __IO uint32_t DIEPINTEN; /*!< USB device IN endpoint common interrupt enable register 810h */ + __IO uint32_t DOEPINTEN; /*!< USB device OUT endpoint common interrupt enable register 814h */ + __IO uint32_t DAEPINT; /*!< USB device all endpoints interrupt register 818h */ + __IO uint32_t DAEPINTEN; /*!< USB device all endpoints interrupt enable register 81Ch */ + uint32_t Reserved20; /*!< Reserved 820h */ + uint32_t Reserved24; /*!< Reserved 824h */ + __IO uint32_t DVBUSDT; /*!< USB device VBUS discharge time register 828h */ + __IO uint32_t DVBUSPT; /*!< USB device VBUS pulsing time register 82Ch */ + uint32_t Reserved30; /*!< Reserved 830h */ + __IO uint32_t DIEPFEINTEN; /*!< USB Device IN endpoint FIFO empty interrupt enable register 834h */ + __IO uint32_t DEP1INT; /*!< USB device endpoint 1 interrupt register 838h */ + __IO uint32_t DEP1INTEN; /*!< USB device endpoint 1 interrupt enable register 83Ch */ + uint32_t Reserved40; /*!< Reserved 840h */ + __IO uint32_t DIEP1INTEN; /*!< USB device IN endpoint-1 interrupt enable register 844h */ + uint32_t Reserved48[15]; /*!< Reserved 848-880h */ + __IO uint32_t DOEP1INTEN; /*!< USB device OUT endpoint-1 interrupt enable register 884h */ +} usb_dr; + +typedef struct +{ + __IO uint32_t DIEPCTL; /*!< USB device IN endpoint control register 900h + (EpNum * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved 900h + (EpNum * 20h) + 04h */ + __IO uint32_t DIEPINTF; /*!< USB device IN endpoint interrupt flag register 900h + (EpNum * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved 900h + (EpNum * 20h) + 0Ch */ + __IO uint32_t DIEPLEN; /*!< USB device IN endpoint transfer length register 900h + (EpNum * 20h) + 10h */ + __IO uint32_t DIEPDMAADDR; /*!< Device IN endpoint-x DMA address register 900h + (EpNum * 20h) + 14h */ + __IO uint32_t DIEPTFSTAT; /*!< USB device IN endpoint transmit FIFO status register 900h + (EpNum * 20h) + 18h */ +} usb_erin; + +typedef struct +{ + __IO uint32_t DOEPCTL; /*!< USB device IN endpoint control register B00h + (EpNum * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved B00h + (EpNum * 20h) + 04h */ + __IO uint32_t DOEPINTF; /*!< USB device IN endpoint interrupt flag register B00h + (EpNum * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved B00h + (EpNum * 20h) + 0Ch */ + __IO uint32_t DOEPLEN; /*!< USB device IN endpoint transfer length register B00h + (EpNum * 20h) + 10h */ + __IO uint32_t DOEPDMAADDR; /*!< Device OUT endpoint-x DMA address register B00h + (EpNum * 20h) + 0Ch */ +} usb_erout; + +typedef struct _usb_regs +{ + usb_gr *gr; /*!< USBHS global registers */ + usb_dr *dr; /*!< device control and status registers */ + usb_hr *hr; /*!< host control and status registers */ + usb_erin *er_in[8]; /*!< USB device IN endpoint register */ + usb_erout *er_out[8]; /*!< USB device OUT endpoint register */ + usb_pr *pr[16]; /*!< USB Host channel-x control register */ + + __IO uint32_t *HPCS; /*!< USB host port control and status register */ + __IO uint32_t *DFIFO[USBHS_MAX_TX_FIFOS]; + __IO uint32_t *PWRCLKCTL; /*!< USB power and clock control register */ +} usb_core_regs; + +/* global OTG control and status register bits definitions */ +#define GOTGCS_OV BIT(20) /*!< select OTG version */ +#define GOTGCS_BSV BIT(19) /*!< B-Session Valid */ +#define GOTGCS_ASV BIT(18) /*!< A-session valid */ +#define GOTGCS_DI BIT(17) /*!< debounce interval */ +#define GOTGCS_IDPS BIT(16) /*!< id pin status */ +#define GOTGCS_EHE BIT(12) /*!< embedded host enable */ +#define GOTGCS_DHNPEN BIT(11) /*!< device HNP enable */ +#define GOTGCS_HHNPEN BIT(10) /*!< host HNP enable */ +#define GOTGCS_HNPREQ BIT(9) /*!< HNP request */ +#define GOTGCS_HNPS BIT(8) /*!< HNP successes */ +#define GOTGCS_BVOV BIT(7) /*!< override value of B-peripheral session valid */ +#define GOTGCS_BVOE BIT(6) /*!< override enable of B-peripheral session valid */ +#define GOTGCS_AVOV BIT(5) /*!< override value of A-peripheral session valid */ +#define GOTGCS_AVOE BIT(4) /*!< override enable of A-peripheral session valid */ +#define GOTGCS_VOV BIT(3) /*!< override value of VBUS valid */ +#define GOTGCS_VOE BIT(2) /*!< override enable of VBUS valid */ +#define GOTGCS_SRPREQ BIT(1) /*!< SRP request */ +#define GOTGCS_SRPS BIT(0) /*!< SRP successes */ + +/* global OTG interrupt flag register bits definitions */ +#define GOTGINTF_IDCHG BIT(20) /*!< ID input change */ +#define GOTGINTF_DF BIT(19) /*!< debounce finish */ +#define GOTGINTF_ADTO BIT(18) /*!< A-device timeout */ +#define GOTGINTF_HNPDET BIT(17) /*!< host negotiation request detected */ +#define GOTGINTF_HNPEND BIT(9) /*!< HNP end */ +#define GOTGINTF_SRPEND BIT(8) /*!< SRP end */ +#define GOTGINTF_SESEND BIT(2) /*!< session end */ + +/* global AHB control and status register bits definitions */ +#define GAHBCS_PTXFTH BIT(8) /*!< periodic Tx FIFO threshold */ +#define GAHBCS_TXFTH BIT(7) /*!< tx FIFO threshold */ +#define GAHBCS_DMAEN BIT(5) /*!< DMA function Enable */ +#define GAHBCS_BURST BITS(1, 4) /*!< the AHB burst type used by DMA */ +#define GAHBCS_GINTEN BIT(0) /*!< global interrupt enable */ + +/* global USB control and status register bits definitions */ +#define GUSBCS_FDM BIT(30) /*!< force device mode */ +#define GUSBCS_FHM BIT(29) /*!< force host mode */ +#define GUSBCS_ULPIEOI BIT(21) /*!< ULPI external over-current indicator */ +#define GUSBCS_ULPIEVD BIT(20) /*!< ULPI external VBUS driver */ +#define GUSBCS_UTT BITS(10, 13) /*!< USB turnaround time */ +#define GUSBCS_HNPCEN BIT(9) /*!< HNP capability enable */ +#define GUSBCS_SRPCEN BIT(8) /*!< SRP capability enable */ +#define GUSBCS_EMBPHY_FS BIT(6) /*!< embedded FS PHY selected */ +#define GUSBCS_EMBPHY_HS BIT(5) /*!< embedded HS PHY selected */ +#define GUSBCS_HS_CUR_FE BIT(4) /*!< HS current software enable */ +#define GUSBCS_TOC BITS(0, 2) /*!< timeout calibration */ + +/* global reset control register bits definitions */ +#define GRSTCTL_DMAIDL BIT(31) /*!< DMA idle state */ +#define GRSTCTL_DMABSY BIT(30) /*!< DMA busy */ +#define GRSTCTL_TXFNUM BITS(6, 10) /*!< tx FIFO number */ +#define GRSTCTL_TXFF BIT(5) /*!< tx FIFO flush */ +#define GRSTCTL_RXFF BIT(4) /*!< rx FIFO flush */ +#define GRSTCTL_HFCRST BIT(2) /*!< host frame counter reset */ +#define GRSTCTL_HCSRST BIT(1) /*!< HCLK soft reset */ +#define GRSTCTL_CSRST BIT(0) /*!< core soft reset */ + +/* global interrupt flag register bits definitions */ +#define GINTF_WKUPIF BIT(31) /*!< wakeup interrupt flag */ +#define GINTF_SESIF BIT(30) /*!< session interrupt flag */ +#define GINTF_DISCIF BIT(29) /*!< disconnect interrupt flag */ +#define GINTF_IDPSC BIT(28) /*!< id pin status change */ +#define GINTF_LPMIF BIT(27) /*!< LPM interrupt flag */ +#define GINTF_PTXFEIF BIT(26) /*!< periodic tx FIFO empty interrupt flag */ +#define GINTF_HCIF BIT(25) /*!< host channels interrupt flag */ +#define GINTF_HPIF BIT(24) /*!< host port interrupt flag */ +#define GINTF_PXNCIF BIT(21) /*!< periodic transfer not complete interrupt flag */ +#define GINTF_ISOONCIF BIT(21) /*!< isochronous OUT transfer not complete interrupt flag */ +#define GINTF_ISOINCIF BIT(20) /*!< isochronous IN transfer not complete interrupt flag */ +#define GINTF_OEPIF BIT(19) /*!< OUT endpoint interrupt flag */ +#define GINTF_IEPIF BIT(18) /*!< IN endpoint interrupt flag */ +#define GINTF_EOPFIF BIT(15) /*!< end of periodic frame interrupt flag */ +#define GINTF_ISOOPDIF BIT(14) /*!< isochronous OUT packet dropped interrupt flag */ +#define GINTF_ENUMFIF BIT(13) /*!< enumeration finished */ +#define GINTF_RST BIT(12) /*!< USB reset */ +#define GINTF_SP BIT(11) /*!< USB suspend */ +#define GINTF_ESP BIT(10) /*!< early suspend */ +#define GINTF_GONAK BIT(7) /*!< global OUT NAK effective */ +#define GINTF_GNPINAK BIT(6) /*!< global IN non-periodic NAK effective */ +#define GINTF_NPTXFEIF BIT(5) /*!< non-periodic tx FIFO empty interrupt flag */ +#define GINTF_RXFNEIF BIT(4) /*!< rx FIFO non-empty interrupt flag */ +#define GINTF_SOF BIT(3) /*!< start of frame */ +#define GINTF_OTGIF BIT(2) /*!< OTG interrupt flag */ +#define GINTF_MFIF BIT(1) /*!< mode fault interrupt flag */ +#define GINTF_COPM BIT(0) /*!< current operation mode */ + +/* global interrupt enable register bits definitions */ +#define GINTEN_WKUPIE BIT(31) /*!< wakeup interrupt enable */ +#define GINTEN_SESIE BIT(30) /*!< session interrupt enable */ +#define GINTEN_DISCIE BIT(29) /*!< disconnect interrupt enable */ +#define GINTEN_IDPSCIE BIT(28) /*!< id pin status change interrupt enable */ +#define GINTEN_LPMIE BIT(27) /*!< LPM interrupt enable */ +#define GINTEN_PTXFEIE BIT(26) /*!< periodic tx FIFO empty interrupt enable */ +#define GINTEN_HCIE BIT(25) /*!< host channels interrupt enable */ +#define GINTEN_HPIE BIT(24) /*!< host port interrupt enable */ +#define GINTEN_IPXIE BIT(21) /*!< periodic transfer not complete interrupt enable */ +#define GINTEN_ISOONCIE BIT(21) /*!< isochronous OUT transfer not complete interrupt enable */ +#define GINTEN_ISOINCIE BIT(20) /*!< isochronous IN transfer not complete interrupt enable */ +#define GINTEN_OEPIE BIT(19) /*!< OUT endpoints interrupt enable */ +#define GINTEN_IEPIE BIT(18) /*!< IN endpoints interrupt enable */ +#define GINTEN_EOPFIE BIT(15) /*!< end of periodic frame interrupt enable */ +#define GINTEN_ISOOPDIE BIT(14) /*!< isochronous OUT packet dropped interrupt enable */ +#define GINTEN_ENUMFIE BIT(13) /*!< enumeration finish enable */ +#define GINTEN_RSTIE BIT(12) /*!< USB reset interrupt enable */ +#define GINTEN_SPIE BIT(11) /*!< USB suspend interrupt enable */ +#define GINTEN_ESPIE BIT(10) /*!< early suspend interrupt enable */ +#define GINTEN_GONAKIE BIT(7) /*!< global OUT NAK effective interrupt enable */ +#define GINTEN_GNPINAKIE BIT(6) /*!< global non-periodic IN NAK effective interrupt enable */ +#define GINTEN_NPTXFEIE BIT(5) /*!< non-periodic Tx FIFO empty interrupt enable */ +#define GINTEN_RXFNEIE BIT(4) /*!< receive FIFO non-empty interrupt enable */ +#define GINTEN_SOFIE BIT(3) /*!< start of frame interrupt enable */ +#define GINTEN_OTGIE BIT(2) /*!< OTG interrupt enable */ +#define GINTEN_MFIE BIT(1) /*!< mode fault interrupt enable */ + +/* global receive status read and pop register bits definitions */ +#define GRSTATRP_RPCKST BITS(17, 20) /*!< received packet status */ +#define GRSTATRP_DPID BITS(15, 16) /*!< data PID */ +#define GRSTATRP_BCOUNT BITS(4, 14) /*!< byte count */ +#define GRSTATRP_CNUM BITS(0, 3) /*!< channel number */ +#define GRSTATRP_EPNUM BITS(0, 3) /*!< endpoint number */ + +/* global receive FIFO length register bits definitions */ +#define GRFLEN_RXFD BITS(0, 15) /*!< rx FIFO depth */ + +/* host non-periodic transmit FIFO length register bits definitions */ +#define HNPTFLEN_HNPTXFD BITS(16, 31) /*!< non-periodic Tx FIFO depth */ +#define HNPTFLEN_HNPTXRSAR BITS(0, 15) /*!< non-periodic Tx RAM start address */ + +/* USB IN endpoint 0 transmit FIFO length register bits definitions */ +#define DIEP0TFLEN_IEP0TXFD BITS(16, 31) /*!< IN Endpoint 0 Tx FIFO depth */ +#define DIEP0TFLEN_IEP0TXRSAR BITS(0, 15) /*!< IN Endpoint 0 TX RAM start address */ + +/* host non-periodic transmit FIFO/queue status register bits definitions */ +#define HNPTFQSTAT_NPTXRQTOP BITS(24, 30) /*!< top entry of the non-periodic Tx request queue */ +#define HNPTFQSTAT_NPTXRQS BITS(16, 23) /*!< non-periodic Tx request queue space */ +#define HNPTFQSTAT_NPTXFS BITS(0, 15) /*!< non-periodic Tx FIFO space */ +#define HNPTFQSTAT_CNUM BITS(27, 30) /*!< channel number*/ +#define HNPTFQSTAT_EPNUM BITS(27, 30) /*!< endpoint number */ +#define HNPTFQSTAT_TYPE BITS(25, 26) /*!< token type */ +#define HNPTFQSTAT_TMF BIT(24) /*!< terminate flag */ + +/* global core configuration register bits definitions */ +#define GCCFG_VDEN BIT(21) /*!< enable of VBUS sensing comparator to detect VBUS valid */ +#define GCCFG_SOFOEN BIT(20) /*!< SOF output enable */ +#define GCCFG_PWRON BIT(16) /*!< power on */ +#define GCCFG_SDMEN BIT(15) /*!< secondary detection mode enable */ +#define GCCFG_PEMEN BIT(14) /*!< primary detection mode enable */ +#define GCCFG_DCDEN BIT(13) /*!< data connect detection mode enable */ +#define GCCFG_BCDEN BIT(12) /*!< battery charging detection enable */ +#define GCCFG_PS2F BIT(3) /*!< PS2 detection status */ +#define GCCFG_SDF BIT(2) /*!< secondary detection status */ +#define GCCFG_PDF BIT(1) /*!< primary detection status */ +#define GCCFG_DCDF BIT(0) /*!< battery connect detection status */ + +/* core ID register bits definitions */ +#define CID_CID BITS(0, 31) /*!< core ID */ + +/* global core LPM configuration register bits definitions */ +#define GLPMCFG_BESLEN BIT(28) /*!< LPM Errata selection enable */ +#define GLPMCFG_LPMRCS BITS(25, 27) /*!< LPM retry count status */ +#define GLPMCFG_LPMSND BIT(24) /*!< send LPM transaction */ +#define GLPMCFG_LPMRC BITS(21, 23) /*!< LPM retry count */ +#define GLPMCFG_LPMCHI BITS(17, 20) /*!< channel number index */ +#define GLPMCFG_RSOK BIT(16) /*!< Resume can be sent after sleep state */ +#define GLPMCFG_LPMSLPS BIT(15) /*!< sleep status */ +#define GLPMCFG_LPMRSP BITS(13, 14) /*!< response of LPM */ +#define GLPMCFG_DSEN BIT(12) /*!< deep sleep enable */ +#define GLPMCFG_BESLTH BITS(8, 11) /*!< BESL threshold */ +#define GLPMCFG_SSEN BIT(7) /*!< shallow sleep enable */ +#define GLPMCFG_REW BIT(6) /*!< bRemoteWake value */ +#define GLPMCFG_BESL BITS(2, 5) /*!< best effort service latency */ +#define GLPMCFG_ACKLPM BIT(1) /*!< ACK in LPM transaction enable */ +#define GLPMCFG_LPMEN BIT(0) /*!< LPM enable */ + +/* power down register bits definition */ +#define PWRD_ADPF BIT(23) /*!< ADP event interrupt flag */ +#define PWRD_ADPMEN BIT(0) /*!< ADP module enable */ + +/* ADP control and status register bits definition */ +#define ADPCTL_RWR BITS(27, 28) /*!< read and write request */ +#define ADPCTL_ADPTFM BIT(26) /*!< the mask of ADP timeout interrupt flag */ +#define ADPCTL_ADPSNFM BIT(25) /*!< the mask of ADP sense interrupt flag */ +#define ADPCTL_ADPPRFM BIT(24) /*!< the mask of ADP probe interrupt flag */ +#define ADPCTL_ADPTF BIT(23) /*!< ADP timeout interrupt flag */ +#define ADPCTL_ADPSNF BIT(22) /*!< ADP sense interrupt flag */ +#define ADPCTL_ADPPRF BIT(21) /*!< ADP probe interrupt flag */ +#define ADPCTL_ADPEN BIT(20) /*!< ADP enable */ +#define ADPCTL_ADPRST BIT(19) /*!< ADP reset */ +#define ADPCTL_SNEN BIT(18) /*!< ADP sense enable */ +#define ADPCTL_PREN BIT(17) /*!< ADP probe enable */ +#define ADPCTL_CHGT BITS(6, 16) /*!< the latest time that VBUS ramps from VADPSINK to VADPPRB */ +#define ADPCTL_PERPR BITS(4, 5) /*!< period of probe */ +#define ADPCTL_RESOPR BITS(2, 3) /*!< the resolution of CHGT value */ +#define ADPCTL_DSCHGPR BITS(0, 1) /*!< Time of probe discharge */ + +/* USBHS PHY control register bits definitions */ +#define PHYCTL_HS_CALEN BIT(31) /*!< HS PHY calibration enable */ +#define PHYCTL_HS_CALR BITS(25, 30) /*!< HS PHY calibration value */ +#define PHYCTL_HS_RCALR BITS(21, 24) /*!< HS PHY R calibration value */ +#define PHYCTL_SQUECH BIT(16) /*!< HS PHY squech flag */ +#define PHYCTL_HS_BIST_DONE BIT(15) /*!< HS PHY bist finished flag */ +#define PHYCTL_HS_BIST_SUCCESS BIT(14) /*!< HS PHY bist success flag */ +#define PHYCTL_HS_BIST_FAIL BIT(13) /*!< HS PHY bist fail flag */ +#define PHYCTL_HS_BIST_PKT_SEL BITS(11, 12) /*!< HS PHY bist packet select */ +#define PHYCTL_HS_BIST_SEED BITS(3, 10) /*!< HS PHY bist seed */ +#define PHYCTL_HS_BIST_EN BIT(2) /*!< HS PHY bist enable */ +#define PHYCTL_HS_BIST_RESET BIT(1) /*!< HS PHY bist logic reset */ +#define PHYCTL_HS_BIST_START BIT(0) /*!< HS PHY bist start */ + +/* host periodic transmit FIFO length register bits definitions */ +#define HPTFLEN_HPTXFD BITS(16, 31) /*!< host periodic Tx FIFO depth */ +#define HPTFLEN_HPTXFSAR BITS(0, 15) /*!< host periodic Tx RAM start address */ + +/* device IN endpoint transmit FIFO length register bits definitions */ +#define DIEPTFLEN_IEPTXFD BITS(16, 31) /*!< IN endpoint Tx FIFO x depth */ +#define DIEPTFLEN_IEPTXRSAR BITS(0, 15) /*!< IN endpoint FIFOx Tx x RAM start address */ + +/* host control register bits definitions */ +#define HCTL_SPDFSLS BIT(2) /*!< speed limited to FS and LS */ + +/* host frame interval register bits definitions */ +#define HFT_FRI BITS(0, 15) /*!< frame interval */ + +/* host frame information remaining register bits definitions */ +#define HFINFR_FRT BITS(16, 31) /*!< frame remaining time */ +#define HFINFR_FRNUM BITS(0, 15) /*!< frame number */ + +/* host periodic transmit FIFO/queue status register bits definitions */ +#define HPTFQSTAT_PTXREQT BITS(24, 31) /*!< top entry of the periodic Tx request queue */ +#define HPTFQSTAT_PTXREQS BITS(16, 23) /*!< periodic Tx request queue space */ +#define HPTFQSTAT_PTXFS BITS(0, 15) /*!< periodic Tx FIFO space */ +#define HPTFQSTAT_OEFRM BIT(31) /*!< odd/eveb frame */ +#define HPTFQSTAT_CNUM BITS(27, 30) /*!< channel number */ +#define HPTFQSTAT_EPNUM BITS(27, 30) /*!< endpoint number */ +#define HPTFQSTAT_TYPE BITS(25, 26) /*!< token type */ +#define HPTFQSTAT_TMF BIT(24) /*!< terminate flag */ + +#define TFQSTAT_TXFS BITS(0, 15) +#define TFQSTAT_CNUM BITS(27, 30) + +/* host all channels interrupt register bits definitions */ +#define HACHINT_HACHINT BITS(0, 15) /*!< host all channel interrupts */ + +/* host all channels interrupt enable register bits definitions */ +#define HACHINTEN_CINTEN BITS(0, 15) /*!< channel interrupt enable */ + +/* host port control and status register bits definitions */ +#define HPCS_PS BITS(17, 18) /*!< port speed */ +#define HPCS_PTEST BITS(13, 16) /*!< port test control */ +#define HPCS_PP BIT(12) /*!< port power */ +#define HPCS_PLST BITS(10, 11) /*!< port line status */ +#define HPCS_PRST BIT(8) /*!< port reset */ +#define HPCS_PSP BIT(7) /*!< port suspend */ +#define HPCS_PREM BIT(6) /*!< port resume */ +#define HPCS_PEDC BIT(3) /*!< port enable/disable change */ +#define HPCS_PE BIT(2) /*!< port enable */ +#define HPCS_PCD BIT(1) /*!< port connect detected */ +#define HPCS_PCST BIT(0) /*!< port connect status */ + +/* host channel-x control register bits definitions */ +#define HCHCTL_CEN BIT(31) /*!< channel enable */ +#define HCHCTL_CDIS BIT(30) /*!< channel disable */ +#define HCHCTL_ODDFRM BIT(29) /*!< odd frame */ +#define HCHCTL_DAR BITS(22, 28) /*!< device address */ +#define HCHCTL_MPC BITS(20, 21) /*!< multiple packet count */ +#define HCHCTL_EPTYPE BITS(18, 19) /*!< endpoint type */ +#define HCHCTL_LSD BIT(17) /*!< low-speed device */ +#define HCHCTL_EPDIR BIT(15) /*!< endpoint direction */ +#define HCHCTL_EPNUM BITS(11, 14) /*!< endpoint number */ +#define HCHCTL_MPL BITS(0, 10) /*!< maximum packet length */ + +/* host channel-x split transaction register bits definitions */ +#define HCHSTCTL_SPLEN BIT(31) /*!< enable high-speed split transaction */ +#define HCHSTCTL_CSPLT BIT(16) /*!< complete-split enable */ +#define HCHSTCTL_ISOPCE BITS(14, 15) /*!< isochronous OUT payload continuation encoding */ +#define HCHSTCTL_HADDR BITS(7, 13) /*!< HUB address */ +#define HCHSTCTL_PADDR BITS(0, 6) /*!< port address */ + +/* host channel-x interrupt flag register bits definitions */ +#define HCHINTF_DTER BIT(10) /*!< data toggle error */ +#define HCHINTF_REQOVR BIT(9) /*!< request queue overrun */ +#define HCHINTF_BBER BIT(8) /*!< babble error */ +#define HCHINTF_USBER BIT(7) /*!< USB bus Error */ +#define HCHINTF_NYET BIT(6) /*!< NYET */ +#define HCHINTF_ACK BIT(5) /*!< ACK */ +#define HCHINTF_NAK BIT(4) /*!< NAK */ +#define HCHINTF_STALL BIT(3) /*!< STALL */ +#define HCHINTF_DMAER BIT(2) /*!< DMA error */ +#define HCHINTF_CH BIT(1) /*!< channel halted */ +#define HCHINTF_TF BIT(0) /*!< transfer finished */ + +/* host channel-x interrupt enable register bits definitions */ +#define HCHINTEN_DTERIE BIT(10) /*!< data toggle error interrupt enable */ +#define HCHINTEN_REQOVRIE BIT(9) /*!< request queue overrun interrupt enable */ +#define HCHINTEN_BBERIE BIT(8) /*!< babble error interrupt enable */ +#define HCHINTEN_USBERIE BIT(7) /*!< USB bus error interrupt enable */ +#define HCHINTEN_NYETIE BIT(6) /*!< NYET interrupt enable */ +#define HCHINTEN_ACKIE BIT(5) /*!< ACK interrupt enable */ +#define HCHINTEN_NAKIE BIT(4) /*!< NAK interrupt enable */ +#define HCHINTEN_STALLIE BIT(3) /*!< STALL interrupt enable */ +#define HCHINTEN_DMAERIE BIT(2) /*!< DMA error interrupt enable */ +#define HCHINTEN_CHIE BIT(1) /*!< channel halted interrupt enable */ +#define HCHINTEN_TFIE BIT(0) /*!< transfer finished interrupt enable */ + +/* host channel-x transfer length register bits definitions */ +#define HCHLEN_PING BIT(31) /*!< PING token request */ +#define HCHLEN_DPID BITS(29, 30) /*!< data PID */ +#define HCHLEN_PCNT BITS(19, 28) /*!< packet count */ +#define HCHLEN_TLEN BITS(0, 18) /*!< transfer length */ + +/* host channel-x DMA address register bits definitions */ +#define HCHDMAADDR_DMAADDR BITS(0, 31) /*!< DMA address */ + +#define PORT_SPEED(x) (((uint32_t)(x) << 17U) & HPCS_PS) /*!< Port speed */ + +#define PORT_SPEED_HIGH PORT_SPEED(0U) /*!< high speed */ +#define PORT_SPEED_FULL PORT_SPEED(1U) /*!< full speed */ +#define PORT_SPEED_LOW PORT_SPEED(2U) /*!< low speed */ + +#define PIPE_CTL_DAR(x) (((uint32_t)(x) << 22U) & HCHCTL_DAR) /*!< device address */ +#define PIPE_CTL_EPTYPE(x) (((uint32_t)(x) << 18U) & HCHCTL_EPTYPE) /*!< endpoint type */ +#define PIPE_CTL_EPNUM(x) (((uint32_t)(x) << 11U) & HCHCTL_EPNUM) /*!< endpoint number */ +#define PIPE_CTL_EPDIR(x) (((uint32_t)(x) << 15U) & HCHCTL_EPDIR) /*!< endpoint direction */ +#define PIPE_CTL_EPMPL(x) (((uint32_t)(x) << 0U) & HCHCTL_MPL) /*!< maximum packet length */ +#define PIPE_CTL_LSD(x) (((uint32_t)(x) << 17U) & HCHCTL_LSD) /*!< low-Speed device */ + +#define PIPE_XFER_PCNT(x) (((uint32_t)(x) << 19U) & HCHLEN_PCNT) /*!< packet count */ +#define PIPE_XFER_DPID(x) (((uint32_t)(x) << 29U) & HCHLEN_DPID) /*!< data PID */ + +#define PIPE_DPID_DATA0 PIPE_XFER_DPID(0) /*!< DATA0 */ +#define PIPE_DPID_DATA1 PIPE_XFER_DPID(2) /*!< DATA1 */ +#define PIPE_DPID_DATA2 PIPE_XFER_DPID(1) /*!< DATA2 */ +#define PIPE_DPID_SETUP PIPE_XFER_DPID(3) /*!< MDATA (non-control)/SETUP (control) */ + +extern const uint32_t PIPE_DPID[2]; + +/* device configuration registers bits definitions */ +#define DCFG_EOPFT BITS(11, 12) /*!< end of periodic frame time */ +#define DCFG_DAR BITS(4, 10) /*!< device address */ +#define DCFG_NZLSOH BIT(2) /*!< non-zero-length status OUT handshake */ +#define DCFG_DS BITS(0, 1) /*!< device speed */ + +/* device control registers bits definitions */ +#define DCTL_L1RJCT BIT(18) /*!< deep sleep reject */ +#define DCTL_POIF BIT(11) /*!< power-on initialization finished */ +#define DCTL_CGONAK BIT(10) /*!< clear global OUT NAK */ +#define DCTL_SGONAK BIT(9) /*!< set global OUT NAK */ +#define DCTL_CGINAK BIT(8) /*!< clear global IN NAK */ +#define DCTL_SGINAK BIT(7) /*!< set global IN NAK */ +#define DCTL_DTEST BITS(4, 6) /*!< device test control */ +#define DCTL_GONS BIT(3) /*!< global OUT NAK status */ +#define DCTL_GINS BIT(2) /*!< global IN NAK status */ +#define DCTL_SD BIT(1) /*!< soft disconnect */ +#define DCTL_RWKUP BIT(0) /*!< remote wakeup */ + +/* device status registers bits definitions */ +#define DSTAT_FNRSOF BITS(8, 21) /*!< the frame number of the received SOF. */ +#define DSTAT_ES BITS(1, 2) /*!< enumerated speed */ +#define DSTAT_SPST BIT(0) /*!< suspend status */ + +/* device IN endpoint common interrupt enable registers bits definitions */ +#define DIEPINTEN_NAKEN BIT(13) /*!< NAK handshake sent by USBHS interrupt enable bit */ +#define DIEPINTEN_TXFEEN BIT(7) /*!< transmit FIFO empty interrupt enable bit */ +#define DIEPINTEN_IEPNEEN BIT(6) /*!< IN endpoint NAK effective interrupt enable bit */ +#define DIEPINTEN_EPTXFUDEN BIT(4) /*!< endpoint Tx FIFO underrun interrupt enable bit */ +#define DIEPINTEN_CITOEN BIT(3) /*!< control In Timeout interrupt enable bit */ +#define DIEPINTEN_EPDISEN BIT(1) /*!< endpoint disabled interrupt enable bit */ +#define DIEPINTEN_TFEN BIT(0) /*!< transfer finished interrupt enable bit */ + +/* device OUT endpoint common interrupt enable registers bits definitions */ +#define DOEPINTEN_NYETEN BIT(14) /*!< NYET handshake is sent interrupt enable bit */ +#define DOEPINTEN_BTBSTPEN BIT(6) /*!< back-to-back SETUP packets interrupt enable bit */ +#define DOEPINTEN_EPRXFOVREN BIT(4) /*!< endpoint Rx FIFO overrun interrupt enable bit */ +#define DOEPINTEN_STPFEN BIT(3) /*!< SETUP phase finished interrupt enable bit */ +#define DOEPINTEN_EPDISEN BIT(1) /*!< endpoint disabled interrupt enable bit */ +#define DOEPINTEN_TFEN BIT(0) /*!< transfer finished interrupt enable bit */ + +/* device all endpoints interrupt registers bits definitions */ +#define DAEPINT_OEPITB BITS(16, 23) /*!< device all OUT endpoint interrupt bits */ +#define DAEPINT_IEPITB BITS(0, 7) /*!< device all IN endpoint interrupt bits */ + +/* device all endpoints interrupt enable registers bits definitions */ +#define DAEPINTEN_OEPIE BITS(16, 23) /*!< OUT endpoint interrupt enable */ +#define DAEPINTEN_IEPIE BITS(0, 7) /*!< IN endpoint interrupt enable */ + +/* device VBUS discharge time registers bits definitions */ +#define DVBUSDT_DVBUSDT BITS(0, 15) /*!< device VBUS discharge time */ + +/* device VBUS pulsing time registers bits definitions */ +#define DVBUSPT_DVBUSPT BITS(0, 11) /*!< device VBUS pulsing time */ + +/* device IN endpoint FIFO empty interrupt enable register bits definitions */ +#define DIEPFEINTEN_IEPTXFEIE BITS(0, 5) /*!< IN endpoint Tx FIFO empty interrupt enable bits */ + +/* device endpoint 0 control register bits definitions */ +#define DEP0CTL_EPEN BIT(31) /*!< endpoint enable */ +#define DEP0CTL_EPD BIT(30) /*!< endpoint disable */ +#define DEP0CTL_SNAK BIT(27) /*!< set NAK */ +#define DEP0CTL_CNAK BIT(26) /*!< clear NAK */ +#define DIEP0CTL_TXFNUM BITS(22, 25) /*!< tx FIFO number */ +#define DEP0CTL_STALL BIT(21) /*!< STALL handshake */ +#define DOEP0CTL_SNOOP BIT(20) /*!< snoop mode */ +#define DEP0CTL_EPTYPE BITS(18, 19) /*!< endpoint type */ +#define DEP0CTL_NAKS BIT(17) /*!< NAK status */ +#define DEP0CTL_EPACT BIT(15) /*!< endpoint active */ +#define DEP0CTL_MPL BITS(0, 1) /*!< maximum packet length */ + +/* device endpoint x control register bits definitions */ +#define DEPCTL_EPEN BIT(31) /*!< endpoint enable */ +#define DEPCTL_EPD BIT(30) /*!< endpoint disable */ +#define DEPCTL_SODDFRM BIT(29) /*!< set odd frame */ +#define DEPCTL_SD1PID BIT(29) /*!< set DATA1 PID */ +#define DEPCTL_SEVNFRM BIT(28) /*!< set even frame */ +#define DEPCTL_SD0PID BIT(28) /*!< set DATA0 PID */ +#define DEPCTL_SNAK BIT(27) /*!< set NAK */ +#define DEPCTL_CNAK BIT(26) /*!< clear NAK */ +#define DIEPCTL_TXFNUM BITS(22, 25) /*!< tx FIFO number */ +#define DEPCTL_STALL BIT(21) /*!< STALL handshake */ +#define DOEPCTL_SNOOP BIT(20) /*!< snoop mode */ +#define DEPCTL_EPTYPE BITS(18, 19) /*!< endpoint type */ +#define DEPCTL_NAKS BIT(17) /*!< NAK status */ +#define DEPCTL_EOFRM BIT(16) /*!< even/odd frame */ +#define DEPCTL_DPID BIT(16) /*!< endpoint data PID */ +#define DEPCTL_EPACT BIT(15) /*!< endpoint active */ +#define DEPCTL_MPL BITS(0, 10) /*!< maximum packet length */ + +/* device IN endpoint-x interrupt flag register bits definitions */ +#define DIEPINTF_NAK BIT(13) /*!< NAK handshake sent by USBHS */ +#define DIEPINTF_TXFE BIT(7) /*!< transmit FIFO empty */ +#define DIEPINTF_IEPNE BIT(6) /*!< IN endpoint NAK effective */ +#define DIEPINTF_EPTXFUD BIT(4) /*!< endpoint Tx FIFO underrun */ +#define DIEPINTF_CITO BIT(3) /*!< control In Timeout interrupt */ +#define DIEPINTF_EPDIS BIT(1) /*!< endpoint disabled */ +#define DIEPINTF_TF BIT(0) /*!< transfer finished */ + +/* device OUT endpoint-x interrupt flag register bits definitions */ +#define DOEPINTF_NYET BIT(14) /*!< NYET handshake is sent */ +#define DOEPINTF_BTBSTP BIT(6) /*!< back-to-back SETUP packets */ +#define DOEPINTF_EPRXFOVR BIT(4) /*!< endpoint Rx FIFO overrun */ +#define DOEPINTF_STPF BIT(3) /*!< SETUP phase finished */ +#define DOEPINTF_EPDIS BIT(1) /*!< endpoint disabled */ +#define DOEPINTF_TF BIT(0) /*!< transfer finished */ + +/* device IN endpoint 0 transfer length register bits definitions */ +#define DIEP0LEN_PCNT BITS(19, 20) /*!< packet count */ +#define DIEP0LEN_TLEN BITS(0, 6) /*!< transfer length */ + +/* device OUT endpoint 0 transfer length register bits definitions */ +#define DOEP0LEN_STPCNT BITS(29, 30) /*!< SETUP packet count */ +#define DOEP0LEN_PCNT BIT(19) /*!< packet count */ +#define DOEP0LEN_TLEN BITS(0, 6) /*!< transfer length */ + +/* device OUT endpoint-x transfer length register bits definitions */ +#define DOEPLEN_RXDPID BITS(29, 30) /*!< received data PID */ +#define DOEPLEN_STPCNT BITS(29, 30) /*!< SETUP packet count */ +#define DIEPLEN_MCNT BITS(29, 30) /*!< multi count */ +#define DEPLEN_PCNT BITS(19, 28) /*!< packet count */ +#define DEPLEN_TLEN BITS(0, 18) /*!< transfer length */ + +/* device IN endpoint-x DMA address register bits definitions */ +#define DIEPDMAADDR_DMAADDR BITS(0, 31) /*!< DMA address */ + +/* device OUT endpoint-x DMA address register bits definitions */ +#define DOEPDMAADDR_DMAADDR BITS(0, 31) /*!< DMA address */ + +/* device IN endpoint-x transmit FIFO status register bits definitions */ +#define DIEPTFSTAT_IEPTFS BITS(0, 15) /*!< IN endpoint Tx FIFO space remaining */ + +/* USB power and clock registers bits definition */ +#define PWRCLKCTL_DSLEEP BIT(7) /*!< PHY is in deep sleep status */ +#define PWRCLKCTL_SSLEEP BIT(6) /*!< PHY is in shallow sleep status */ +#define PWRCLKCTL_SCGEN BIT(5) /*!< When this bit is set, the internal clock gating is enabled */ +#define PWRCLKCTL_SUSP BIT(4) /*!< PHY is in suspend status */ +#define PWRCLKCTL_SHCLK BIT(1) /*!< stop HCLK */ +#define PWRCLKCTL_SUCLK BIT(0) /*!< stop the USB clock */ + +#define RSTAT_GOUT_NAK 1U /*!< global OUT NAK (triggers an interrupt) */ +#define RSTAT_DATA_UPDT 2U /*!< OUT data packet received */ +#define RSTAT_XFER_COMP 3U /*!< OUT transfer completed (triggers an interrupt) */ +#define RSTAT_SETUP_COMP 4U /*!< SETUP transaction completed (triggers an interrupt) */ +#define RSTAT_SETUP_UPDT 6U /*!< SETUP data packet received */ + +#define DSTAT_EM_HS_PHY_30MHZ_60MHZ 0U /*!< USB enumerate speed use high-speed PHY clock in 30MHz or 60MHz */ +#define DSTAT_EM_FS_PHY_30MHZ_60MHZ 1U /*!< USB enumerate speed use full-speed PHY clock in 30MHz or 60MHz */ +#define DSTAT_EM_LS_PHY_6MHZ 2U /*!< USB enumerate speed use low-speed PHY clock in 6MHz */ +#define DSTAT_EM_FS_PHY_48MHZ 3U /*!< USB enumerate speed use full-speed PHY clock in 48MHz */ + +#define DPID_DATA0 0U /*!< device endpoint data PID is DATA0 */ +#define DPID_DATA1 2U /*!< device endpoint data PID is DATA1 */ +#define DPID_DATA2 1U /*!< device endpoint data PID is DATA2 */ +#define DPID_MDATA 3U /*!< device endpoint data PID is MDATA */ + +#define GAHBCS_DMAINCR(regval) (GAHBCS_BURST & ((regval) << 1U)) /*!< AHB burst type used by DMA */ + +#define DMA_INCR0 GAHBCS_DMAINCR(0U) /*!< single burst type used by DMA*/ +#define DMA_INCR1 GAHBCS_DMAINCR(1U) /*!< 4-beat incrementing burst type used by DMA */ +#define DMA_INCR4 GAHBCS_DMAINCR(3U) /*!< 8-beat incrementing burst type used by DMA */ +#define DMA_INCR8 GAHBCS_DMAINCR(5U) /*!< 16-beat incrementing burst type used by DMA */ +#define DMA_INCR16 GAHBCS_DMAINCR(7U) /*!< 32-beat incrementing burst type used by DMA */ + +#define DCFG_PFRI(regval) (DCFG_EOPFT & ((regval) << 11U)) /*!< end of periodic frame time configuration */ + +#define FRAME_INTERVAL_80 DCFG_PFRI(0U) /*!< 80% of the frame time */ +#define FRAME_INTERVAL_85 DCFG_PFRI(1U) /*!< 85% of the frame time */ +#define FRAME_INTERVAL_90 DCFG_PFRI(2U) /*!< 90% of the frame time */ +#define FRAME_INTERVAL_95 DCFG_PFRI(3U) /*!< 95% of the frame time */ + +#define DCFG_DEVSPEED(regval) (DCFG_DS & ((regval) << 0U)) /*!< device speed configuration */ + +#define USB_SPEED_INP_HIGH DCFG_DEVSPEED(0U) /*!< device internal PHY high speed */ +#define USB_SPEED_INP_FULL DCFG_DEVSPEED(1U) /*!< device internal PHY full speed */ + +#define DEP0_MPL(regval) (DEP0CTL_MPL & ((regval) << 0U)) /*!< maximum packet length configuration */ + +#define EP0MPL_64 DEP0_MPL(0U) /*!< maximum packet length 64 bytes */ +#define EP0MPL_32 DEP0_MPL(1U) /*!< maximum packet length 32 bytes */ +#define EP0MPL_16 DEP0_MPL(2U) /*!< maximum packet length 16 bytes */ +#define EP0MPL_8 DEP0_MPL(3U) /*!< maximum packet length 8 bytes */ + +#define DOEP0_TLEN(regval) (DOEP0LEN_TLEN & ((regval) << 0U)) /*!< Transfer length */ +#define DOEP0_PCNT(regval) (DOEP0LEN_PCNT & ((regval) << 19U)) /*!< Packet count */ +#define DOEP0_STPCNT(regval) (DOEP0LEN_STPCNT & ((regval) << 29U)) /*!< SETUP packet count */ + +#define GRXSTS_PKTSTS_IN 2U +#define GRXSTS_PKTSTS_IN_XFER_COMP 3U +#define GRXSTS_PKTSTS_DATA_TOGGLE_ERR 5U +#define GRXSTS_PKTSTS_CH_HALTED 7U + +#define DEVICE_MODE 0U /*!< device mode */ +#define HOST_MODE 1U /*!< host mode */ +#define OTG_MODE 2U /*!< OTG mode */ + +#define HCTL_30_60MHZ 0U /*!< USB clock 30-60MHZ */ +#define HCTL_48MHZ 1U /*!< USB clock 48MHZ */ +#define HCTL_6MHZ 2U /*!< USB clock 6MHZ */ + +#define USBHS_MAX_PACKET_SIZE 512U /*!< USBHS max packet size */ +#define USBHS_MAX_CHANNEL_COUNT 16U /*!< USBHS host channel count */ +#define USBHS_MAX_EP_COUNT 8U /*!< USBHS device endpoint count */ +#define USBHS_MAX_FIFO_WORDLEN 1280U /*!< USBHS max fifo size in words */ + +#define USB_DATA_FIFO_OFFSET 0x1000U /*!< USB data fifo offset */ +#define USB_DATA_FIFO_SIZE 0x1000U /*!< USB data fifo size */ + +typedef enum +{ + USB_EMBEDDED_PHY_FS = 0U, /*!< USB embedded FS PHY */ + USB_EMBEDDED_PHY_HS, /*!< USB embedded HS PHY */ + USB_ULPI_PHY_EXTERNAL /*!< USB external ULPI PHY */ +} usb_phy_enum; + +enum usb_reg_offset +{ + USB_REG_OFFSET_CORE = 0x0000U, /*!< global OTG control and status register */ + USB_REG_OFFSET_DEV = 0x0800U, /*!< device mode control and status registers */ + USB_REG_OFFSET_EP = 0x0020U, /*!< offset of each endpoint */ + USB_REG_OFFSET_EP_IN = 0x0900U, /*!< device IN endpoint 0 control register */ + USB_REG_OFFSET_EP_OUT = 0x0B00U, /*!< device OUT endpoint 0 control register */ + USB_REG_OFFSET_HOST = 0x0400U, /*!< host control register */ + USB_REG_OFFSET_CH = 0x0020U, /*!< offset of each channel */ + USB_REG_OFFSET_PORT = 0x0440U, /*!< host port control and status register */ + USB_REG_OFFSET_CH_INOUT = 0x0500U, /*!< host channel-x control registers */ + USB_REG_OFFSET_PWRCLKCTL = 0x0E00U /*!< power and clock register */ +}; + +enum USB_SPEED +{ + USB_SPEED_UNKNOWN = 0, /*!< USB speed unknown */ + USB_SPEED_LOW, /*!< USB speed low */ + USB_SPEED_FULL, /*!< USB speed full */ + USB_SPEED_HIGH /*!< USB speed high */ +}; + +#define EP0_OUT ((uint8_t)0x00U) /*!< endpoint out 0 */ +#define EP0_IN ((uint8_t)0x80U) /*!< endpoint in 0 */ +#define EP1_OUT ((uint8_t)0x01U) /*!< endpoint out 1 */ +#define EP1_IN ((uint8_t)0x81U) /*!< endpoint in 1 */ +#define EP2_OUT ((uint8_t)0x02U) /*!< endpoint out 2 */ +#define EP2_IN ((uint8_t)0x82U) /*!< endpoint in 2 */ +#define EP3_OUT ((uint8_t)0x03U) /*!< endpoint out 3 */ +#define EP3_IN ((uint8_t)0x83U) /*!< endpoint in 3 */ +#define EP4_OUT ((uint8_t)0x04U) /*!< endpoint out 4 */ +#define EP4_IN ((uint8_t)0x84U) /*!< endpoint in 4 */ +#define EP5_OUT ((uint8_t)0x05U) /*!< endpoint out 5 */ +#define EP5_IN ((uint8_t)0x85U) /*!< endpoint in 5 */ +#define EP6_OUT ((uint8_t)0x06U) /*!< endpoint out 6 */ +#define EP6_IN ((uint8_t)0x86U) /*!< endpoint in 6 */ +#define EP7_OUT ((uint8_t)0x06U) /*!< endpoint out 7 */ +#define EP7_IN ((uint8_t)0x86U) /*!< endpoint in 7 */ + +#endif /* DRV_USB_REGS_H */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbd_int.c b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbd_int.c new file mode 100644 index 000000000000..91472885a372 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbd_int.c @@ -0,0 +1,708 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#include "drv_usbd_int.h" +#include "drv_otghs_dev.h" + +#include + +#if defined(BSP_USING_USB) && defined(RT_USING_USB_DEVICE) + +static const uint8_t USB_SPEED[4] = { + [DSTAT_EM_HS_PHY_30MHZ_60MHZ] = (uint8_t)USB_SPEED_HIGH, + [DSTAT_EM_FS_PHY_30MHZ_60MHZ] = (uint8_t)USB_SPEED_FULL, + [DSTAT_EM_FS_PHY_48MHZ] = (uint8_t)USB_SPEED_FULL, + [DSTAT_EM_LS_PHY_6MHZ] = (uint8_t)USB_SPEED_LOW +}; + +/* local function prototypes ('static') */ +static uint32_t usbd_int_epout(usb_core_driver *udev); +static uint32_t usbd_int_epin(usb_core_driver *udev); +static uint32_t usbd_int_rxfifo(usb_core_driver *udev); +static uint32_t usbd_int_reset(usb_core_driver *udev); +static uint32_t usbd_int_enumfinish(usb_core_driver *udev); +static uint32_t usbd_int_suspend(usb_core_driver *udev); +static uint32_t usbd_int_wakeup(usb_core_driver *udev); + +#if (1U == LPM_ENABLED) +static uint32_t usbd_int_lpm(usb_core_driver *udev, usb_lpm_type active_type); +#endif /* LPM_ENABLED */ + +static uint32_t usbd_emptytxfifo_write(usb_core_driver *udev, uint32_t ep_num); + +/*! + \brief USB device-mode interrupts global service routine handler + \param[in] udev: pointer to USB device instance + \param[out] none + \retval none +*/ +void usbd_isr(usb_core_driver *udev) +{ + if (HOST_MODE != (udev->regs.gr->GINTF & GINTF_COPM)) + { + uint32_t intr = udev->regs.gr->GINTF; + intr &= udev->regs.gr->GINTEN; + + /* there are no interrupts, avoid spurious interrupt */ + if (!intr) + { + return; + } + + /* OUT endpoints interrupts */ + if (intr & GINTF_OEPIF) + { + (void)usbd_int_epout(udev); + } + + /* IN endpoints interrupts */ + if (intr & GINTF_IEPIF) + { + (void)usbd_int_epin(udev); + } + + /* suspend interrupt */ + if (intr & GINTF_SP) + { + (void)usbd_int_suspend(udev); + } + + /* wakeup interrupt */ + if (intr & GINTF_WKUPIF) + { + (void)usbd_int_wakeup(udev); + } + + /* start of frame interrupt */ + if (intr & GINTF_SOF) + { + /* clear interrupt */ + udev->regs.gr->GINTF = GINTF_SOF; + } + + /* receive FIFO not empty interrupt */ + if (intr & GINTF_RXFNEIF) + { + (void)usbd_int_rxfifo(udev); + } + + /* USB reset interrupt */ + if (intr & GINTF_RST) + { + (void)usbd_int_reset(udev); + } + + /* enumeration has been done interrupt */ + if (intr & GINTF_ENUMFIF) + { + (void)usbd_int_enumfinish(udev); + } + + /* incomplete synchronization IN transfer interrupt*/ + if (intr & GINTF_ISOINCIF) + { + if (NULL != udev->dev.class_core->incomplete_isoc_in) + { + (void)udev->dev.class_core->incomplete_isoc_in(udev); + } + + /* clear interrupt */ + udev->regs.gr->GINTF = GINTF_ISOINCIF; + } + + /* incomplete synchronization OUT transfer interrupt*/ + if (intr & GINTF_ISOONCIF) + { + if (NULL != udev->dev.class_core->incomplete_isoc_out) + { + (void)udev->dev.class_core->incomplete_isoc_out(udev); + } + + /* clear interrupt */ + udev->regs.gr->GINTF = GINTF_ISOONCIF; + } + +#if (1U == LPM_ENABLED) + if (intr & GINTF_LPMIF) + { + /* clear interrupt */ + udev->regs.gr->GINTF = GINTF_LPMIF; + + udev->dev.pm.BESL = (udev->regs.gr->GLPMCFG & GLPMCFG_BESL) >> 2U; + udev->dev.pm.dev_remote_wakeup = (udev->regs.gr->GLPMCFG & GLPMCFG_REW) >> 6U; + + if (LPM_L0 == udev->dev.pm.lpm_state) + { + udev->dev.pm.lpm_state = LPM_L1; + usbd_int_lpm(udev, LPM_L1_ACTIVE); + } + else + { + udev->dev.pm.lpm_state = LPM_L0; + usbd_int_lpm(udev, LPM_L0_ACTIVE); + } + } +#endif /* LPM_ENABLED */ + +#ifdef VBUS_SENSING_ENABLED + + /* session request interrupt */ + if (intr & GINTF_SESIF) + { + udev->regs.gr->GINTF = GINTF_SESIF; + } + + /* OTG mode interrupt */ + if (intr & GINTF_OTGIF) + { + if (udev->regs.gr->GOTGINTF & GOTGINTF_SESEND) + { + } + + /* clear OTG interrupt */ + udev->regs.gr->GINTF = GINTF_OTGIF; + } +#endif /* VBUS_SENSING_ENABLED */ + } +} + +/*! + \brief indicates that an OUT endpoint has a pending interrupt + \param[in] udev: pointer to USB device instance + \param[out] none + \retval operation status +*/ +static uint32_t usbd_int_epout(usb_core_driver *udev) +{ + uint32_t epintnum = 0U; + uint8_t ep_num = 0U; + + for (epintnum = usb_oepintnum_read(udev); epintnum; epintnum >>= 1) + { + if (epintnum & 0x01U) + { + __IO uint32_t oepintr = usb_oepintr_read(udev, ep_num); + + /* transfer complete interrupt */ + if (oepintr & DOEPINTF_TF) + { + /* clear the bit in DOEPINTF for this interrupt */ + udev->regs.er_out[ep_num]->DOEPINTF = DOEPINTF_TF; + + if ((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) + { + __IO uint32_t eplen = udev->regs.er_out[ep_num]->DOEPLEN; + + udev->dev.transc_out[ep_num].xfer_count = udev->dev.transc_out[ep_num].max_len - + (eplen & DEPLEN_TLEN); + } + + /* inform upper layer: data ready */ + usbd_data_out_stage_callback(ep_num, udev->dev.transc_out[ep_num].xfer_len); + + if ((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) + { + if ((0U == ep_num) && ((uint8_t)USB_CTL_STATUS_OUT == udev->dev.control.ctl_state)) + { + usb_ctlep_startout(udev); + } + } + } + + /* SETUP phase finished interrupt (control endpoints) */ + if (oepintr & DOEPINTF_STPF) + { + /* inform the upper layer that a SETUP packet is available */ + usbd_setup_stage_callback((struct urequest *)&udev->dev.control.req); + + udev->regs.er_out[ep_num]->DOEPINTF = DOEPINTF_STPF; + } + } + + ep_num++; + } + + return 1U; +} + +/*! + \brief indicates that an IN endpoint has a pending interrupt + \param[in] udev: pointer to USB device instance + \param[out] none + \retval operation status +*/ +static uint32_t usbd_int_epin(usb_core_driver *udev) +{ + uint32_t epintnum = 0U; + uint8_t ep_num = 0U; + + for (epintnum = usb_iepintnum_read(udev); epintnum; epintnum >>= 1) + { + if (epintnum & 0x1U) + { + __IO uint32_t iepintr = usb_iepintr_read(udev, ep_num); + + if (iepintr & DIEPINTF_TF) + { + udev->regs.er_in[ep_num]->DIEPINTF = DIEPINTF_TF; + + /* data transmission is completed */ + usbd_data_in_stage_callback(ep_num, udev->dev.transc_in[ep_num].xfer_len); + + if ((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) + { + if ((0U == ep_num) && ((uint8_t)USB_CTL_STATUS_IN == udev->dev.control.ctl_state)) + { + usb_ctlep_startout(udev); + } + } + } + + if (iepintr & DIEPINTF_TXFE) + { + usbd_emptytxfifo_write(udev, (uint32_t)ep_num); + + udev->regs.er_in[ep_num]->DIEPINTF = DIEPINTF_TXFE; + } + } + + ep_num++; + } + + return 1U; +} + +/*! + \brief handle the RX status queue level interrupt + \param[in] udev: pointer to USB device instance + \param[out] none + \retval operation status +*/ +static uint32_t usbd_int_rxfifo(usb_core_driver *udev) +{ + usb_transc *transc = NULL; + + uint8_t data_PID = 0U; + uint32_t bcount = 0U; + + __IO uint32_t devrxstat = 0U; + + /* disable the RX status queue non-empty interrupt */ + udev->regs.gr->GINTEN &= ~GINTEN_RXFNEIE; + + /* get the status from the top of the FIFO */ + devrxstat = udev->regs.gr->GRSTATP; + gd_usb_rxfifo_count++; + gd_usb_last_rx_status = devrxstat; + + uint8_t ep_num = (uint8_t)(devrxstat & GRSTATRP_EPNUM); + + transc = &udev->dev.transc_out[ep_num]; + + bcount = (devrxstat & GRSTATRP_BCOUNT) >> 4; + data_PID = (uint8_t)((devrxstat & GRSTATRP_DPID) >> 15); + + switch ((devrxstat & GRSTATRP_RPCKST) >> 17) + { + case RSTAT_GOUT_NAK: + break; + + case RSTAT_DATA_UPDT: + if (bcount > 0U) + { + (void)usb_rxfifo_read(&udev->regs, transc->xfer_buf, (uint16_t)bcount); + + transc->xfer_buf += bcount; + transc->xfer_count += bcount; + } + break; + + case RSTAT_XFER_COMP: + /* trigger the OUT endpoint interrupt */ + break; + + case RSTAT_SETUP_COMP: + /* trigger the OUT endpoint interrupt */ + break; + + case RSTAT_SETUP_UPDT: + gd_usb_setup_rx_count++; + if ((0U == transc->ep_addr.num) && (8U == bcount) && (DPID_DATA0 == data_PID)) + { + /* copy the SETUP packet received in FIFO into the setup buffer in RAM */ + (void)usb_rxfifo_read(&udev->regs, (uint8_t *)&udev->dev.control.req, (uint16_t)bcount); + + transc->xfer_count += bcount; + } + break; + + default: + break; + } + + /* enable the RX status queue level interrupt */ + udev->regs.gr->GINTEN |= GINTEN_RXFNEIE; + + return 1U; +} + +/*! + \brief handle USB reset interrupt + \param[in] udev: pointer to USB device instance + \param[out] none + \retval status +*/ +static uint32_t usbd_int_reset(usb_core_driver *udev) +{ + uint32_t i; + + /* clear the remote wakeup signaling */ + udev->regs.dr->DCTL &= ~DCTL_RWKUP; + + /* flush the TX FIFO */ + (void)usb_txfifo_flush(&udev->regs, 0U); + + for (i = 0U; i < udev->bp.num_ep; i++) + { + udev->regs.er_in[i]->DIEPINTF = 0xFFU; + udev->regs.er_out[i]->DOEPINTF = 0xFFU; + } + + /* clear all pending device endpoint interrupts */ + udev->regs.dr->DAEPINT = 0xFFFFFFFFU; + + /* enable endpoint 0 interrupts */ + udev->regs.dr->DAEPINTEN = 1U | (1U << 16U); + + /* enable OUT endpoint interrupts */ + udev->regs.dr->DOEPINTEN = DOEPINTEN_STPFEN | DOEPINTEN_TFEN; + +#ifdef USB_DEDICATED_EP1_ENABLED + udev->regs.dr->DOEP1INTEN = DOEPINTEN_STPFEN | DOEPINTEN_TFEN; +#endif + + /* enable IN endpoint interrupts */ + udev->regs.dr->DIEPINTEN = DIEPINTEN_TFEN; + +#ifdef USB_DEDICATED_EP1_ENABLED + udev->regs.dr->DIEP1INTEN = DIEPINTEN_TFEN; +#endif + + /* reset device address */ + udev->regs.dr->DCFG &= ~DCFG_DAR; + + /* configure endpoint 0 to receive SETUP packets */ + usb_ctlep_startout(udev); + + /* clear USB reset interrupt */ + udev->regs.gr->GINTF = GINTF_RST; + + usb_ep_active(udev, 0x00, USB_FS_EP0_MAX_LEN, USB_EPTYPE_CTRL); + usb_ep_active(udev, 0x80, USB_FS_EP0_MAX_LEN, USB_EPTYPE_CTRL); + + usbd_reset_callback(); + + return 1U; +} + +/*! + \brief handle USB speed enumeration finish interrupt + \param[in] udev: pointer to USB device instance + \param[out] none + \retval status +*/ +static uint32_t usbd_int_enumfinish(usb_core_driver *udev) +{ + uint8_t enum_speed = (uint8_t)((udev->regs.dr->DSTAT & DSTAT_ES) >> 1); + + udev->regs.dr->DCTL &= ~DCTL_CGINAK; + udev->regs.dr->DCTL |= DCTL_CGINAK; + + udev->regs.gr->GUSBCS &= ~GUSBCS_UTT; + + /* set USB turn-around time based on device speed and PHY interface */ + if ((uint8_t)USB_SPEED_HIGH == USB_SPEED[enum_speed]) + { + udev->bp.core_speed = (uint8_t)USB_SPEED_HIGH; + + udev->regs.gr->GUSBCS |= 0x09U << 10; + } + else + { + udev->bp.core_speed = (uint8_t)USB_SPEED_FULL; + + udev->regs.gr->GUSBCS |= 0x05U << 10; + } + + /* clear interrupt */ + udev->regs.gr->GINTF = GINTF_ENUMFIF; + + gd_usb_enum_count++; + + return 1U; +} + +/*! + \brief USB suspend interrupt handler + \param[in] udev: pointer to USB device instance + \param[out] none + \retval operation status +*/ +static uint32_t usbd_int_suspend(usb_core_driver *udev) +{ + __IO uint8_t low_power = udev->bp.low_power; + __IO uint8_t suspend = (uint8_t)(udev->regs.dr->DSTAT & DSTAT_SPST); + + udev->dev.backup_status = udev->dev.cur_status; + + if (low_power && suspend) + { + /* switch-off the USB clocks */ + *udev->regs.PWRCLKCTL |= PWRCLKCTL_SUCLK | PWRCLKCTL_SHCLK; + + /* enter DEEP_SLEEP mode with LDO in low power mode */ + pmu_to_deepsleepmode(WFI_CMD); + } + + /* clear interrupt */ + udev->regs.gr->GINTF = GINTF_SP; + + return 1U; +} + +/*! + \brief USB wakeup interrupt handler + \param[in] udev: pointer to USB device instance + \param[out] none + \retval operation status +*/ +static uint32_t usbd_int_wakeup(usb_core_driver *udev) +{ + __IO uint8_t low_power = udev->bp.low_power; + __IO uint8_t remote_wakeup = udev->dev.pm.dev_remote_wakeup; + +#if (1U == LPM_ENABLED) + if (LPM_L1 == udev->dev.pm.lpm_state) + { + udev->dev.pm.lpm_state = LPM_L0; + usbd_int_lpm(udev, LPM_L0_ACTIVE); + } + else +#endif /* LPM_ENABLED */ + { + if (remote_wakeup && low_power) + { + /* resume MCU CLK */ + + /* reset SLEEPDEEP bit of Cortex-M33 system control register */ + SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk); + } + + usb_clock_active(udev); + + /* inform upper layer by the resume event */ + udev->dev.cur_status = udev->dev.backup_status; + } + + /* clear interrupt */ + udev->regs.gr->GINTF = GINTF_WKUPIF; + + return 1U; +} + +#ifdef USB_DEDICATED_EP1_ENABLED + +/*! + \brief USB dedicated OUT endpoint 1 interrupt service routine handler + \param[in] udev: pointer to USB device instance + \param[out] none + \retval operation status +*/ +uint32_t usbd_int_dedicated_ep1out(usb_core_driver *udev) +{ + uint32_t oepintr = 0U; + uint32_t oeplen = 0U; + + oepintr = udev->regs.er_out[1]->DOEPINTF; + oepintr &= udev->regs.dr->DOEP1INTEN; + + /* transfer complete */ + if (oepintr & DOEPINTF_TF) + { + /* clear the bit in DOEPINTn for this interrupt */ + udev->regs.er_out[1]->DOEPINTF = DOEPINTF_TF; + + if (USB_USE_DMA == udev->bp.transfer_mode) + { + oeplen = udev->regs.er_out[1]->DOEPLEN; + + /* ToDo : handle more than one single MPS size packet */ + udev->dev.transc_out[1].xfer_count = udev->dev.transc_out[1].max_len - + (oeplen & DEPLEN_TLEN); + } + + /* RX COMPLETE */ + usbd_out_transc(udev, 1U); + } + + return 1U; +} + +/*! + \brief USB dedicated IN endpoint 1 interrupt service routine handler + \param[in] udev: pointer to USB device instance + \param[out] none + \retval operation status +*/ +uint32_t usbd_int_dedicated_ep1in(usb_core_driver *udev) +{ + uint32_t inten, intr, emptyen; + + inten = udev->regs.dr->DIEP1INTEN; + emptyen = udev->regs.dr->DIEPFEINTEN; + + inten |= ((emptyen >> 1U) & 0x1U) << 7U; + + intr = udev->regs.er_in[1]->DIEPINTF & inten; + + if (intr & DIEPINTF_TF) + { + udev->regs.dr->DIEPFEINTEN &= ~(0x1U << 1U); + + udev->regs.er_in[1]->DIEPINTF = DIEPINTF_TF; + + /* TX COMPLETE */ + usbd_in_transc(udev, 1U); + } + + if (intr & DIEPINTF_TXFE) + { + usbd_emptytxfifo_write(udev, 1U); + + udev->regs.er_in[1]->DIEPINTF = DIEPINTF_TXFE; + } + + return 1U; +} + +#endif + +#if (1U == LPM_ENABLED) + +/*! + \brief USB LPM interrupt handler + \param[in] udev: pointer to USB device instance + \param[in] active_type: active type + only one parameter can be selected which is shown as below: + \arg LPM_L0_ACTIVE: select L0 to active + \arg LPM_L1_ACTIVE: select L1 to active + \param[out] none + \retval operation status +*/ +static uint32_t usbd_int_lpm(usb_core_driver *udev, usb_lpm_type active_type) +{ + __IO uint8_t low_power = udev->bp.low_power; + __IO uint8_t suspend = (uint8_t)(udev->regs.dr->DSTAT & DSTAT_SPST); + __IO uint8_t is_configured = (udev->dev.cur_status == (uint8_t)USBD_CONFIGURED) ? 1U : 0U; + + switch (active_type) + { + case LPM_L0_ACTIVE: + udev->dev.cur_status = udev->dev.backup_status; + + /* switch-on the OTG clocks */ + usb_clock_active(udev); + + if (low_power) + { + /* resume MCU CLK */ + + /* reset SLEEPDEEP bit of Cortex-M7 system control register */ + SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk); + } + break; + + case LPM_L1_ACTIVE: + udev->dev.backup_status = udev->dev.cur_status; + udev->dev.cur_status = (uint8_t)USBD_SUSPENDED; + + /* add delay */ + usb_mdelay(300U); + + /* switch-off the OTG clocks */ + *udev->regs.PWRCLKCTL |= PWRCLKCTL_SUCLK | PWRCLKCTL_SHCLK; + + if (low_power) + { + /* enter DEEP_SLEEP mode with LDO in low power mode */ + pmu_to_deepsleepmode(WFI_CMD); + } + break; + } + + return 1U; +} + +#endif /* LPM_ENABLED */ + +/*! + \brief check FIFO for the next packet to be loaded + \param[in] udev: pointer to USB device instance + \param[in] ep_num: endpoint identifier which is in (0..5) + \param[out] none + \retval status +*/ +static uint32_t usbd_emptytxfifo_write(usb_core_driver *udev, uint32_t ep_num) +{ + uint32_t len; + uint32_t word_count; + + usb_transc *transc = &udev->dev.transc_in[ep_num]; + + len = transc->xfer_len - transc->xfer_count; + + /* get the data length to write */ + if (len > transc->max_len) + { + len = transc->max_len; + } + + word_count = (len + 3U) / 4U; + + while (((udev->regs.er_in[ep_num]->DIEPTFSTAT & DIEPTFSTAT_IEPTFS) >= word_count) && + (transc->xfer_count < transc->xfer_len)) + { + len = transc->xfer_len - transc->xfer_count; + + if (len > transc->max_len) + { + len = transc->max_len; + } + + /* write FIFO in word(4bytes) */ + word_count = (len + 3U) / 4U; + + /* write the FIFO */ + (void)usb_txfifo_write(&udev->regs, transc->xfer_buf, (uint8_t)ep_num, (uint16_t)len); + + transc->xfer_buf += len; + transc->xfer_count += len; + + if (transc->xfer_count == transc->xfer_len) + { + /* disable the device endpoint FIFO empty interrupt */ + udev->regs.dr->DIEPFEINTEN &= ~(0x01U << ep_num); + } + } + + return 1U; +} + +#endif /* BSP_USING_USB && RT_USING_USB_DEVICE */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbd_int.h b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbd_int.h new file mode 100644 index 000000000000..0dfc08431bf4 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbd_int.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#ifndef DRV_USBD_INT_H +#define DRV_USBD_INT_H + +#include "drv_usb_core.h" +#include "drv_usb_dev.h" + +/* function declarations */ +#ifdef USB_DEDICATED_EP1_ENABLED +/* USB dedicated OUT endpoint 1 interrupt service routine handler */ +uint32_t usbd_int_dedicated_ep1out(usb_core_driver *udev); +/* USB dedicated IN endpoint 1 interrupt service routine handler */ +uint32_t usbd_int_dedicated_ep1in(usb_core_driver *udev); +#endif /* USB_DEDICATED_EP1_ENABLED */ + +/* USB device-mode interrupts global service routine handler */ +void usbd_isr(usb_core_driver *udev); + +#endif /* DRV_USBD_INT_H */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_int.c b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_int.c new file mode 100644 index 000000000000..1d71da91ea66 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_int.c @@ -0,0 +1,693 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#include "drv_usbh_int.h" +#include "drv_otghs_host.h" + +#if defined(__CC_ARM) /*!< ARM compiler */ +#pragma O0 +#elif defined(__GNUC__) /*!< GNU compiler */ +#pragma GCC optimize("O0") +#elif defined(__TASKING__) /*!< TASKING compiler */ +#pragma optimize = 0 +#endif /* __CC_ARM */ + +#if defined(BSP_USING_USB) && defined(RT_USING_USB_HOST) + +/* local function prototypes ('static') */ +static uint32_t usbh_int_port(usb_core_driver *udev); +static uint32_t usbh_int_pipe(usb_core_driver *udev); +static uint32_t usbh_int_pipe_in(usb_core_driver *udev, uint32_t pp_num); +static uint32_t usbh_int_pipe_out(usb_core_driver *udev, uint32_t pp_num); +static uint32_t usbh_int_rxfifonoempty(usb_core_driver *udev); +static uint32_t usbh_int_txfifoempty(usb_core_driver *udev, usb_pipe_mode pp_mode); + +/*! + \brief handle global host interrupt + \param[in] udev: pointer to USB core instance + \param[out] none + \retval operation status +*/ +uint32_t usbh_isr(usb_core_driver *udev) +{ + uint32_t retval = 0U; + + __IO uint32_t intr = 0U; + + /* check if host mode */ + if (HOST_MODE == (udev->regs.gr->GINTF & GINTF_COPM)) + { + intr = usb_coreintr_get(&udev->regs); + + if (!intr) + { + return 0U; + } + + if (intr & GINTF_SOF) + { + /* clear interrupt */ + udev->regs.gr->GINTF = GINTF_SOF; + } + + if (intr & GINTF_RXFNEIF) + { + retval |= usbh_int_rxfifonoempty(udev); + } + + if (intr & GINTF_NPTXFEIF) + { + retval |= usbh_int_txfifoempty(udev, PIPE_NON_PERIOD); + } + + if (intr & GINTF_PTXFEIF) + { + retval |= usbh_int_txfifoempty(udev, PIPE_PERIOD); + } + + if (intr & GINTF_HCIF) + { + retval |= usbh_int_pipe(udev); + } + + if (intr & GINTF_HPIF) + { + retval |= usbh_int_port(udev); + } + + if (intr & GINTF_WKUPIF) + { + /* clear interrupt */ + udev->regs.gr->GINTF = GINTF_WKUPIF; + } + + if (intr & GINTF_DISCIF) + { + if ((*udev->regs.HPCS & HPCS_PCST) == 0) + { + usbh_disconnect_callback(udev); + } + + /* clear interrupt */ + udev->regs.gr->GINTF = GINTF_DISCIF; + } + + if (intr & GINTF_ISOONCIF) + { + udev->regs.pr[0]->HCHCTL |= HCHCTL_CEN | HCHCTL_CDIS; + + /* clear interrupt */ + udev->regs.gr->GINTF = GINTF_ISOONCIF; + } + } + + return retval; +} + +/*! + \brief handle USB pipe halt + \param[in] udev: pointer to USB core instance + \param[in] pp_num: pp_num: host channel number which is in (0..7) + \param[in] pp_int: pipe interrupt + \param[in] pp_status: pipe status + \param[out] none + \retval none +*/ +static inline void usb_pp_halt(usb_core_driver *udev, + uint8_t pp_num, + uint32_t pp_int, + usb_pipe_status pp_status) +{ + udev->regs.pr[pp_num]->HCHINTEN |= HCHINTEN_CHIE; + + usb_pipe_halt(udev, pp_num); + + udev->regs.pr[pp_num]->HCHINTF = pp_int; + + udev->host.pipe[pp_num].pp_status = pp_status; +} + +/*! + \brief handle the host port interrupt + \param[in] udev: pointer to USB device instance + \param[out] none + \retval operation status +*/ +#if defined(__ICCARM__) /*!< IAR compiler */ +#pragma optimize = none +#endif /* __ICCARM */ +static uint32_t usbh_int_port(usb_core_driver *udev) +{ + uint32_t retval = 0U; + + __IO uint32_t port_state = *udev->regs.HPCS; + + __IO uint32_t port_reset = 0U; + + /* clear the interrupt bit in GINTF */ + port_state &= ~(HPCS_PE | HPCS_PCD | HPCS_PEDC); + + /* port connect detected */ + if (*udev->regs.HPCS & HPCS_PCD) + { + port_state |= HPCS_PCD; + + if ((*udev->regs.HPCS & HPCS_PCST) == HPCS_PCST) + { + usbh_connect_callback(udev); + } + + retval |= 1U; + } + + /* port enable changed */ + if (*udev->regs.HPCS & HPCS_PEDC) + { + port_state |= HPCS_PEDC; + + if (*udev->regs.HPCS & HPCS_PE) + { + uint32_t port_speed = usb_curspeed_get(udev); + + udev->host.connect_status = 1U; + + if (PORT_SPEED_LOW == port_speed) + { + udev->regs.hr->HFT = 6000U; + } + else if (PORT_SPEED_FULL == port_speed) + { + udev->regs.hr->HFT = 48000U; + } + else + { + /* no operation */ + } + + port_reset = 1U; + + udev->regs.gr->GINTEN |= GINTEN_DISCIE | GINTEN_SOFIE; + } + else + { + /* no operation */ + } + } + + if (1U == port_reset) + { + usb_port_reset(udev); + } + + /* clear port interrupts */ + *udev->regs.HPCS = port_state; + + return retval; +} + +/*! + \brief handle all host channels interrupt + \param[in] udev: pointer to USB device instance + \param[out] none + \retval operation status +*/ +static uint32_t usbh_int_pipe(usb_core_driver *udev) +{ + uint32_t pp_num = 0U; + uint32_t retval = 0U; + + for (pp_num = 0U; pp_num < udev->bp.num_pipe; pp_num++) + { + if ((udev->regs.hr->HACHINT & HACHINT_HACHINT) & (1UL << pp_num)) + { + if (udev->regs.pr[pp_num]->HCHCTL & HCHCTL_EPDIR) + { + retval |= usbh_int_pipe_in(udev, pp_num); + } + else + { + retval |= usbh_int_pipe_out(udev, pp_num); + } + } + } + + return retval; +} + +/*! + \brief handle the IN channel interrupt + \param[in] udev: pointer to USB device instance + \param[in] pp_num: host channel number which is in (0..7) + \param[out] none + \retval operation status +*/ +#if defined(__ICCARM__) /*!< IAR compiler */ +#pragma optimize = none +#endif /* __ICCARM */ +static uint32_t usbh_int_pipe_in(usb_core_driver *udev, uint32_t pp_num) +{ + usb_pr *pp_reg = udev->regs.pr[pp_num]; + usb_pipe *pp = &udev->host.pipe[pp_num]; + + uint32_t intr_pp = pp_reg->HCHINTF; + intr_pp &= pp_reg->HCHINTEN; + + uint8_t ep_type = (uint8_t)((pp_reg->HCHCTL & HCHCTL_EPTYPE) >> 18); + + if (intr_pp & HCHINTF_ACK) + { + pp_reg->HCHINTF = HCHINTF_ACK; + } + else if (intr_pp & HCHINTF_STALL) + { + usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_STALL, PIPE_STALL); + pp_reg->HCHINTF = HCHINTF_NAK; + + /* note: When there is a 'STALL', reset also NAK, + else, the udev->host.pp_status = HC_STALL + will be overwritten by 'NAK' in code below */ + intr_pp &= ~HCHINTF_NAK; + } + else if (intr_pp & HCHINTF_DTER) + { + usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_DTER, PIPE_DTGERR); + pp_reg->HCHINTF = HCHINTF_NAK; + } + else + { + /* no operation */ + } + + if (intr_pp & HCHINTF_REQOVR) + { + usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_REQOVR, PIPE_REQOVR); + } + else if (intr_pp & HCHINTF_TF) + { + if ((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) + { + udev->host.backup_xfercount[pp_num] = pp->xfer_len - (pp_reg->HCHLEN & HCHLEN_TLEN); + } + + pp->pp_status = PIPE_XF; + pp->err_count = 0U; + + pp_reg->HCHINTF = HCHINTF_TF; + + switch (ep_type) + { + case USB_EPTYPE_CTRL: + case USB_EPTYPE_BULK: + usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_NAK, PIPE_XF); + + pp->data_toggle_in ^= 1U; + break; + + case USB_EPTYPE_INTR: + case USB_EPTYPE_ISOC: + pp_reg->HCHCTL |= HCHCTL_ODDFRM; + pp->urb_state = URB_DONE; + +#ifdef USBH_USE_RTOS + usbh_hc_notifyurbchange_callback(udev, pp_num, pp->urb_state); +#endif /* USBH_USE_RTOS */ + break; + + default: + break; + } + } + else if (intr_pp & HCHINTF_CH) + { + pp_reg->HCHINTEN &= ~HCHINTEN_CHIE; + + switch (pp->pp_status) + { + case PIPE_XF: + pp->urb_state = URB_DONE; + break; + + case PIPE_STALL: + pp->urb_state = URB_STALL; + break; + + case PIPE_TRACERR: + case PIPE_DTGERR: + pp->err_count = 0U; + pp->urb_state = URB_ERROR; + + pp->data_toggle_in ^= 1U; + break; + + case PIPE_IDLE: + case PIPE_HALTED: + case PIPE_NAK: + case PIPE_NYET: + case PIPE_BBERR: + case PIPE_REQOVR: + default: + if ((uint8_t)USB_EPTYPE_INTR == ep_type) + { + pp->data_toggle_in ^= 1U; + } + break; + } + +#ifdef USBH_USE_RTOS + usbh_hc_notifyurbchange_callback(udev, pp_num, pp->urb_state); +#endif /* USBH_USE_RTOS */ + + pp_reg->HCHINTF = HCHINTF_CH; + } + else if (intr_pp & HCHINTF_USBER) + { + pp->err_count++; + usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_USBER, PIPE_TRACERR); + } + else if (intr_pp & HCHINTF_NAK) + { + pp_reg->HCHINTF = HCHINTF_NAK; + switch (ep_type) + { + case USB_EPTYPE_CTRL: + case USB_EPTYPE_BULK: + /* re-activate the channel */ + pp_reg->HCHCTL = (pp_reg->HCHCTL | HCHCTL_CEN) & ~HCHCTL_CDIS; + break; + + case USB_EPTYPE_INTR: + pp_reg->HCHINTEN |= HCHINTEN_CHIE; + + (void)usb_pipe_halt(udev, (uint8_t)pp_num); + break; + + default: + break; + } + + pp->pp_status = PIPE_NAK; + } + else + { + /* no operation */ + } + + return 1U; +} + +/*! + \brief handle the OUT channel interrupt + \param[in] udev: pointer to USB device instance + \param[in] pp_num: host channel number which is in (0..7) + \param[out] none + \retval operation status +*/ +#if defined(__ICCARM__) /*!< IAR compiler */ +#pragma optimize = none +#endif /* __ICCARM */ +static uint32_t usbh_int_pipe_out(usb_core_driver *udev, uint32_t pp_num) +{ + usb_pr *pp_reg = udev->regs.pr[pp_num]; + usb_pipe *pp = &udev->host.pipe[pp_num]; + + uint32_t intr_pp = pp_reg->HCHINTF; + intr_pp &= pp_reg->HCHINTEN; + + if (intr_pp & HCHINTF_ACK) + { + if (1U == udev->host.pipe[pp_num].do_ping) + { + udev->host.pipe[pp_num].do_ping = 0U; + pp->err_count = 0U; + usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_ACK, PIPE_NAK); + } + + pp_reg->HCHINTF = HCHINTF_ACK; + } + else if (intr_pp & HCHINTF_STALL) + { + usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_STALL, PIPE_STALL); + } + else if (intr_pp & HCHINTF_DTER) + { + usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_DTER, PIPE_DTGERR); + pp_reg->HCHINTF = HCHINTF_NAK; + } + else if (intr_pp & HCHINTF_REQOVR) + { + usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_REQOVR, PIPE_REQOVR); + } + else if (intr_pp & HCHINTF_TF) + { + pp->err_count = 0U; + usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_TF, PIPE_XF); + } + else if (intr_pp & HCHINTF_NAK) + { + if (0U == udev->host.pipe[pp_num].do_ping) + { + if (1U == udev->host.pipe[pp_num].supp_ping) + { + udev->host.pipe[pp_num].do_ping = 1U; + } + } + + pp->err_count = 0U; + if (USB_USE_FIFO == udev->bp.transfer_mode) + { + usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_NAK, PIPE_NAK); + } + else + { + pp_reg->HCHINTF = HCHINTF_NAK; + } + usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_NAK, PIPE_NAK); + } + else if (intr_pp & HCHINTF_USBER) + { + pp->err_count++; + usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_USBER, PIPE_TRACERR); + } + else if (intr_pp & HCHINTF_NYET) + { + usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_NYET, PIPE_NYET); + + pp->err_count = 0U; + } + else if (intr_pp & HCHINTF_CH) + { + udev->regs.pr[pp_num]->HCHINTEN &= ~HCHINTEN_CHIE; + + switch (pp->pp_status) + { + case PIPE_XF: + pp->urb_state = URB_DONE; + + if ((uint8_t)USB_EPTYPE_BULK == ((pp_reg->HCHCTL & HCHCTL_EPTYPE) >> 18U)) + { + pp->data_toggle_out ^= 1U; + } + break; + + case PIPE_NAK: + pp->urb_state = URB_NOTREADY; + break; + + case PIPE_NYET: + pp->urb_state = URB_DONE; + + if ((uint8_t)USB_EPTYPE_BULK == ((pp_reg->HCHCTL & HCHCTL_EPTYPE) >> 18U)) + { + pp->data_toggle_out ^= 1U; + } + break; + + case PIPE_STALL: + pp->urb_state = URB_STALL; + break; + + case PIPE_TRACERR: + if (3U == pp->err_count) + { + pp->urb_state = URB_ERROR; + pp->err_count = 0U; + } + break; + + case PIPE_IDLE: + case PIPE_HALTED: + case PIPE_BBERR: + case PIPE_REQOVR: + case PIPE_DTGERR: + default: + break; + } + +#ifdef USBH_USE_RTOS + usbh_hc_notifyurbchange_callback(udev, pp_num, pp->urb_state); +#endif /* USBH_USE_RTOS */ + + pp_reg->HCHINTF = HCHINTF_CH; + } + else + { + /* no operation */ + } + + return 1U; +} + +/*! + \brief handle the RX FIFO non-empty interrupt + \param[in] udev: pointer to USB device instance + \param[out] none + \retval operation status +*/ +#if defined(__ICCARM__) /*!< IAR compiler */ +#pragma optimize = none +#endif /* __ICCARM */ +static uint32_t usbh_int_rxfifonoempty(usb_core_driver *udev) +{ + uint32_t count = 0U, xfer_count = 0U; + + __IO uint8_t pp_num = 0U; + __IO uint32_t rx_stat = 0U; + + /* disable the RX status queue level interrupt */ + udev->regs.gr->GINTEN &= ~GINTEN_RXFNEIE; + + rx_stat = udev->regs.gr->GRSTATP; + pp_num = (uint8_t)(rx_stat & GRSTATRP_CNUM); + + switch ((rx_stat & GRSTATRP_RPCKST) >> 17) + { + case GRXSTS_PKTSTS_IN: + count = (rx_stat & GRSTATRP_BCOUNT) >> 4; + + /* read the data into the host buffer. */ + if ((NULL != udev->host.pipe[pp_num].xfer_buf) && (count > 0U)) + { + (void)usb_rxfifo_read(&udev->regs, udev->host.pipe[pp_num].xfer_buf, (uint16_t)count); + + /* manage multiple transfer packet */ + udev->host.pipe[pp_num].xfer_buf += count; + udev->host.pipe[pp_num].xfer_count += count; + + xfer_count = udev->host.pipe[pp_num].xfer_count; + + udev->host.backup_xfercount[pp_num] = xfer_count; + + if (udev->regs.pr[pp_num]->HCHLEN & HCHLEN_PCNT) + { + /* re-activate the channel when more packets are expected */ + uint32_t pp_ctl = udev->regs.pr[pp_num]->HCHCTL; + + pp_ctl |= HCHCTL_CEN; + pp_ctl &= ~HCHCTL_CDIS; + + udev->regs.pr[pp_num]->HCHCTL = pp_ctl; + } + } + break; + + case GRXSTS_PKTSTS_IN_XFER_COMP: + break; + + case GRXSTS_PKTSTS_DATA_TOGGLE_ERR: + count = (rx_stat & GRSTATRP_BCOUNT) >> 4; + + while (count > 0U) + { + rx_stat = udev->regs.gr->GRSTATP; + count--; + } + break; + + case GRXSTS_PKTSTS_CH_HALTED: + break; + + default: + break; + } + + /* enable the RX status queue level interrupt */ + udev->regs.gr->GINTEN |= GINTEN_RXFNEIE; + + return 1U; +} + +/*! + \brief handle the TX FIFO empty interrupt + \param[in] udev: pointer to USB device instance + \param[in] pp_mode: pipe mode + \param[out] none + \retval operation status +*/ +#if defined(__ICCARM__) /*!< IAR compiler */ +#pragma optimize = none +#endif /* __ICCARM */ +static uint32_t usbh_int_txfifoempty(usb_core_driver *udev, usb_pipe_mode pp_mode) +{ + uint8_t pp_num = 0U; + uint16_t word_count = 0U, len = 0U; + __IO uint32_t *txfiforeg = 0U, txfifostate = 0U; + + if (PIPE_NON_PERIOD == pp_mode) + { + txfiforeg = &udev->regs.gr->HNPTFQSTAT; + } + else if (PIPE_PERIOD == pp_mode) + { + txfiforeg = &udev->regs.hr->HPTFQSTAT; + } + else + { + return 0U; + } + + txfifostate = *txfiforeg; + + pp_num = (uint8_t)((txfifostate & TFQSTAT_CNUM) >> 27); + + word_count = (uint16_t)(udev->host.pipe[pp_num].xfer_len + 3U) / 4U; + + while (((txfifostate & TFQSTAT_TXFS) >= word_count) && (0U != udev->host.pipe[pp_num].xfer_len)) + { + len = (uint16_t)(txfifostate & TFQSTAT_TXFS) * 4U; + + if (len > udev->host.pipe[pp_num].xfer_len) + { + /* last packet */ + len = (uint16_t)udev->host.pipe[pp_num].xfer_len; + + if (PIPE_NON_PERIOD == pp_mode) + { + udev->regs.gr->GINTEN &= ~GINTEN_NPTXFEIE; + } + else + { + udev->regs.gr->GINTEN &= ~GINTEN_PTXFEIE; + } + } + + word_count = (uint16_t)((udev->host.pipe[pp_num].xfer_len + 3U) / 4U); + usb_txfifo_write(&udev->regs, udev->host.pipe[pp_num].xfer_buf, pp_num, len); + + udev->host.pipe[pp_num].xfer_buf += len; + udev->host.pipe[pp_num].xfer_len -= len; + udev->host.pipe[pp_num].xfer_count += len; + + txfifostate = *txfiforeg; + } + + return 1U; +} + +#endif /* BSP_USING_USB && RT_USING_USB_HOST */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_int.h b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_int.h new file mode 100644 index 000000000000..fe1afdc820f0 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_int.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#ifndef DRV_USBH_INT_H +#define DRV_USBH_INT_H + +#include "drv_usb_host.h" + +/* handle global host interrupt */ +uint32_t usbh_isr(usb_core_driver *udev); + +#endif /* DRV_USBH_INT_H */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_pipe.c b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_pipe.c new file mode 100644 index 000000000000..542ebbe69162 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_pipe.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#include "drv_usbh_pipe.h" + +#if defined(BSP_USING_USB) && defined(RT_USING_USB_HOST) + +static uint16_t usbh_freepipe_get(usb_core_driver *pudev); + +/*! + \brief create a pipe + \param[in] pudev: pointer to usb core instance + \param[in] pp_num: pipe number + \param[in] udev: USB device + \param[in] ep_type: endpoint type + \param[in] ep_mpl: endpoint max packet length + \param[out] none + \retval operation status +*/ +uint8_t usbh_pipe_create(usb_core_driver *pudev, + uint8_t dev_addr, + uint8_t dev_speed, + uint8_t pp_num, + uint8_t ep_num, + uint8_t ep_type, + uint16_t ep_mpl) +{ + usb_pipe *pp = &pudev->host.pipe[pp_num]; + + pp->dev_addr = dev_addr; + pp->dev_speed = dev_speed; + pp->ep.num = ep_num & 0x7FU; + pp->ep.type = ep_type; + pp->ep.mps = ep_mpl; + + if (((USB_EPTYPE_BULK == pp->ep.type) || (USB_EPTYPE_CTRL == pp->ep.type))) + { + pp->supp_ping = (uint8_t)(pp->dev_speed == PORT_SPEED_HIGH); + } + + if (0x80U == (ep_num & 0x80U)) + { + pp->ep.dir = 1; + } + else + { + pp->ep.dir = 0; + } + + usb_pipe_init(pudev, pp_num); + + return HC_OK; +} + +/*! + \brief modify a pipe + \param[in] pudev: pointer to usb core instance + \param[in] pp_num: pipe number + \param[in] dev_addr: device address + \param[in] dev_speed: device speed + \param[in] ep_type: endpoint type + \param[in] ep_mpl: endpoint max packet length + \param[out] none + \retval operation status +*/ +uint8_t usbh_pipe_update(usb_core_driver *pudev, + uint8_t pp_num, + uint8_t dev_addr, + uint32_t dev_speed, + uint16_t ep_mpl) +{ + usb_pipe *pp = &pudev->host.pipe[pp_num]; + + if ((pp->dev_addr != dev_addr) && (dev_addr)) + { + pp->dev_addr = dev_addr; + } + + if ((pp->dev_speed != dev_speed) && (dev_speed)) + { + pp->dev_speed = dev_speed; + } + + if ((pp->ep.mps != ep_mpl) && (ep_mpl)) + { + pp->ep.mps = ep_mpl; + } + + usb_pipe_init(pudev, pp_num); + + return HC_OK; +} + +/*! + \brief allocate a new pipe + \param[in] pudev: pointer to usb core instance + \param[in] ep_addr: endpoint address + \param[out] none + \retval operation status +*/ +uint8_t usbh_pipe_allocate(usb_core_driver *pudev, uint8_t ep_addr) +{ + uint16_t pp_num = usbh_freepipe_get(pudev); + + if (HC_ERROR != pp_num) + { + pudev->host.pipe[pp_num].in_used = 1U; + pudev->host.pipe[pp_num].ep.dir = EP_DIR(ep_addr); + pudev->host.pipe[pp_num].ep.num = EP_ID(ep_addr); + } + + return pp_num; +} + +/*! + \brief free a pipe + \param[in] pudev: pointer to usb core instance + \param[in] pp_num: pipe number + \param[out] none + \retval operation status +*/ +uint8_t usbh_pipe_free(usb_core_driver *pudev, uint8_t pp_num) +{ + if (pp_num < HC_MAX) + { + pudev->host.pipe[pp_num].in_used = 0U; + } + + return USBH_OK; +} + +/*! + \brief delete all USB host pipe + \param[in] pudev: pointer to usb core instance + \param[out] none + \retval operation status +*/ +uint8_t usbh_pipe_delete(usb_core_driver *pudev) +{ + uint8_t pp_num = 0U; + + for (pp_num = 2U; pp_num < HC_MAX; pp_num++) + { + pudev->host.pipe[pp_num] = (usb_pipe){ 0 }; + } + + return USBH_OK; +} + +/*! + \brief get a free pipe number for allocation + \param[in] pudev: pointer to usb core instance + \param[out] none + \retval operation status +*/ +static uint16_t usbh_freepipe_get(usb_core_driver *pudev) +{ + uint8_t pp_num = 0U; + + for (pp_num = 0U; pp_num < HC_MAX; pp_num++) + { + if (pudev->host.pipe[pp_num].in_used == 0U) + { + return pp_num; + } + } + + return HC_ERROR; +} + +#endif /* BSP_USING_USB && RT_USING_USB_HOST */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_pipe.h b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_pipe.h new file mode 100644 index 000000000000..03852f134200 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_pipe.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#ifndef __DRV_USBH_PIPE_H +#define __DRV_USBH_PIPE_H + +#include "drv_usb_host.h" + +#define HC_MAX 8U + +#define HC_OK 0x0000U +#define HC_USED 0x8000U +#define HC_ERROR 0xFFFFU +#define HC_USED_MASK 0x7FFFU + +/* allocate a new pipe */ +uint8_t usbh_pipe_allocate(usb_core_driver *pudev, uint8_t ep_addr); + +/* delete all USB host pipe */ +uint8_t usbh_pipe_delete(usb_core_driver *pudev); + +/* free a pipe */ +uint8_t usbh_pipe_free(usb_core_driver *pudev, uint8_t pp_num); + +/* create a pipe */ +uint8_t usbh_pipe_create(usb_core_driver *pudev, + uint8_t dev_addr, + uint8_t dev_speed, + uint8_t pp_num, + uint8_t ep_num, + uint8_t ep_type, + uint16_t ep_mpl); + +/* modify a pipe */ +uint8_t usbh_pipe_update(usb_core_driver *pudev, + uint8_t pp_num, + uint8_t dev_addr, + uint32_t dev_speed, + uint16_t ep_mpl); + +#endif /* __DRV_USBH_PIPE_H */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_trans.c b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_trans.c new file mode 100644 index 000000000000..3af506c95da2 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_trans.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#include "drv_usbh_transc.h" + +#if defined(BSP_USING_USB) && defined(RT_USING_USB_HOST) + +#define USB_SETUP_PACKET_LEN 8U + +/*! + \brief prepare a pipe and start a transfer + \param[in] pudev: pointer to usb core instance + \param[in] pp_num: pipe number + \param[out] none + \retval operation status +*/ +static uint32_t usbh_request_submit(usb_core_driver *pudev, uint8_t pp_num) +{ + pudev->host.pipe[pp_num].urb_state = URB_IDLE; + pudev->host.pipe[pp_num].xfer_count = 0U; + + return usb_pipe_xfer(pudev, pp_num); +} + +/*! + \brief send the setup packet to the USB device + \param[in] pudev: pointer to usb core instance + \param[in] buf: data buffer which will be sent to USB device + \param[in] pp_num: pipe number + \param[out] none + \retval operation status +*/ +usbh_status usbh_ctlsetup_send(usb_core_driver *pudev, uint8_t *buf, uint8_t pp_num) +{ + usb_pipe *pp = &pudev->host.pipe[pp_num]; + + pp->DPID = PIPE_DPID_SETUP; + pp->xfer_buf = buf; + pp->xfer_len = USB_SETUP_PACKET_LEN; + + return (usbh_status)usbh_request_submit(pudev, pp_num); +} + +/*! + \brief send a data packet to the USB device + \param[in] pudev: pointer to usb core instance + \param[in] buf: data buffer which will be sent to USB device + \param[in] pp_num: pipe number + \param[in] len: length of the data to be sent + \param[out] none + \retval operation status +*/ +usbh_status usbh_data_send(usb_core_driver *pudev, uint8_t *buf, uint8_t pp_num, uint16_t len) +{ + usb_pipe *pp = &pudev->host.pipe[pp_num]; + + pp->xfer_buf = buf; + pp->xfer_len = len; + + switch (pp->ep.type) + { + case USB_EPTYPE_CTRL: + if (0U == len) + { + pp->data_toggle_out = 1U; + } + + pp->DPID = PIPE_DPID[pp->data_toggle_out]; + break; + + case USB_EPTYPE_INTR: + pp->DPID = PIPE_DPID[pp->data_toggle_out]; + + pp->data_toggle_out ^= 1U; + break; + + case USB_EPTYPE_BULK: + pp->DPID = PIPE_DPID[pp->data_toggle_out]; + break; + + case USB_EPTYPE_ISOC: + pp->DPID = PIPE_DPID[0]; + break; + + default: + break; + } + + usbh_request_submit(pudev, pp_num); + + return USBH_OK; +} + +/*! + \brief receive a data packet from the USB device + \param[in] pudev: pointer to usb core instance + \param[in] buf: data buffer which will be received from USB device + \param[in] pp_num: pipe number + \param[in] len: length of the data to be received + \param[out] none + \retval operation status +*/ +usbh_status usbh_data_recev(usb_core_driver *pudev, uint8_t *buf, uint8_t pp_num, uint16_t len) +{ + usb_pipe *pp = &pudev->host.pipe[pp_num]; + + pp->xfer_buf = buf; + pp->xfer_len = len; + + switch (pp->ep.type) + { + case USB_EPTYPE_CTRL: + pp->DPID = PIPE_DPID[1]; + break; + + case USB_EPTYPE_INTR: + pp->DPID = PIPE_DPID[pp->data_toggle_in]; + + /* Toggle DATA PID */ + pp->data_toggle_in ^= 1U; + break; + + case USB_EPTYPE_BULK: + pp->DPID = PIPE_DPID[pp->data_toggle_in]; + break; + + case USB_EPTYPE_ISOC: + pp->DPID = PIPE_DPID[0]; + break; + + default: + break; + } + + usbh_request_submit(pudev, pp_num); + + return USBH_OK; +} + +#endif /* BSP_USING_USB && RT_USING_USB_HOST */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_transc.h b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_transc.h new file mode 100644 index 000000000000..e931daee028d --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/drv_usbh_transc.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#ifndef __DRV_USBH_TRANSC_H +#define __DRV_USBH_TRANSC_H + +#include "drv_usb_host.h" + +#if defined(BSP_USING_USB) && defined(RT_USING_USB_HOST) + +/* send the setup packet to the USB device */ +usbh_status usbh_ctlsetup_send(usb_core_driver *pudev, uint8_t *buf, uint8_t pp_num); + +/* send a data packet to the USB device */ +usbh_status usbh_data_send(usb_core_driver *pudev, uint8_t *buf, uint8_t pp_num, uint16_t len); + +/* receive a data packet from the USB device */ +usbh_status usbh_data_recev(usb_core_driver *pudev, uint8_t *buf, uint8_t pp_num, uint16_t len); + +/* get USB URB state */ +static inline usb_urb_state usbh_urbstate_get(usb_core_driver *pudev, uint8_t pp_num) +{ + return pudev->host.pipe[pp_num].urb_state; +} + +/* get USB transfer data count */ +static inline uint32_t usbh_xfercount_get(usb_core_driver *pudev, uint8_t pp_num) +{ + return pudev->host.backup_xfercount[pp_num]; +} + +#endif /* BSP_USING_USB && RT_USING_USB_HOST */ +#endif /* __DRV_USBH_TRANSC_H */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/gd32h7_usb_hw.c b/bsp/gd32/arm/libraries/gd32_drivers/usb/gd32h7_usb_hw.c new file mode 100644 index 000000000000..f045e2f39bd0 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/gd32h7_usb_hw.c @@ -0,0 +1,592 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#include "drv_usb_hw.h" +#include +#include +#include + +#if defined(RT_USING_USB_DEVICE) +#include "drv_usbd_int.h" +#include "drv_otghs_dev.h" +#endif + +#if defined(RT_USING_USB_HOST) +#include "drv_usbh_int.h" +#include "drv_otghs_host.h" +#endif + +#if defined(BSP_USING_USB) + +#if defined(RT_USING_USB_HOST) +#ifndef USE_ULPI_PHY + +#if !defined(BSP_USB_HOST_VBUS_PORT_RCC) || \ + !defined(BSP_USB_HOST_VBUS_PORT) || \ + !defined(BSP_USB_HOST_VBUS_PIN) +#error "Please define the USB host VBUS control pin in board.h" +#endif + +#define HOST_POWERSW_PORT_RCC BSP_USB_HOST_VBUS_PORT_RCC +#define HOST_POWERSW_PORT BSP_USB_HOST_VBUS_PORT +#define HOST_POWERSW_VBUS BSP_USB_HOST_VBUS_PIN + +#else + +#ifdef USE_USBHS0 +#define HOST_POWERSW_PORT_RCC RCU_GPIOC +#define HOST_POWERSW_PORT GPIOC +#define HOST_POWERSW_VBUS GPIO_PIN_7 +#endif + +#ifdef USE_USBHS1 +#define HOST_POWERSW_PORT_RCC RCU_GPIOC +#define HOST_POWERSW_PORT GPIOC +#define HOST_POWERSW_VBUS GPIO_PIN_9 +#endif + +#endif + +#endif + +/*! + \brief configure USB GPIO + \param[in] usb_periph: USBHS0 or USBHS1 + \param[out] none + \retval none +*/ +void usb_gpio_config(void) +{ +#ifdef USE_USBHS0 + rcu_periph_clock_enable(RCU_GPIOA); + rcu_periph_clock_enable(RCU_GPIOB); + rcu_periph_clock_enable(RCU_GPIOC); + + /* ULPI_STP(PC0) GPIO pin configuration */ + gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0); + gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_0); + + /* ULPI_CK(PA5) GPIO pin configuration */ + gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5); + gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_5); + + /* ULPI_NXT(PC3) GPIO pin configuration */ + gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3); + gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_3); + + /* ULPI_DIR(PC2) GPIO pin configuration */ + gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2); + gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_2); + + /* ULPI_D1(PB0), ULPI_D2(PB1), ULPI_D3(PB10), ULPI_D4(PB11) \ + ULPI_D5(PB12), ULPI_D6(PB13) and ULPI_D7(PB5) GPIO pin configuration */ + gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, + GPIO_PIN_0 | + GPIO_PIN_1 | GPIO_PIN_10 | GPIO_PIN_11); + gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, + GPIO_PIN_12 | + GPIO_PIN_13 | GPIO_PIN_5); + + /* ULPI_D0(PA3) GPIO pin configuration */ + gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3); + gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_3); + + gpio_af_set(GPIOA, GPIO_AF_10, GPIO_PIN_5); + gpio_af_set(GPIOB, GPIO_AF_10, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_5 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13); + gpio_af_set(GPIOC, GPIO_AF_10, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3); + +#endif /* USE_USBHS0 */ + +#ifdef USE_USBHS1 + rcu_periph_clock_enable(RCU_GPIOH); + rcu_periph_clock_enable(RCU_GPIOG); + + /* ULPI_STP(PH2) GPIO pin configuration */ + gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2); + gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_2); + + + /* ULPI_CK(PH5) GPIO pin configuration */ + gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5); + gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_5); + + /* ULPI_NXT(PH4) GPIO pin configuration */ + gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4); + gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_4); + + /* ULPI_DIR(PH3) GPIO pin configuration */ + gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3); + gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_3); + + /* ULPI_D0(PH6) GPIO pin configuration */ + /* ULPI_D1(PH7), ULPI_D2(PH8), ULPI_D3(PH9), ULPI_D4(PH10) \ + ULPI_D5(PH11), ULPI_D6(PH12) and ULPI_D7(PG5) GPIO pin configuration */ + gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, + GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | + GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12); + gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, + GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | + GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12); + + gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5); + gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, GPIO_PIN_5); + + gpio_af_set(GPIOG, GPIO_AF_8, GPIO_PIN_5); + gpio_af_set(GPIOH, GPIO_AF_8, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12); +#endif /* USE_USBHS1 */ +} + +#ifdef USE_USBHS0 +/*! + \brief this function handles USBHS0 interrupt + \param[in] usb_periph: USBHS0 or USBHS1 + \param[out] none + \retval none +*/ +void USBHS0_IRQHandler(void) +{ + rt_interrupt_enter(); + +#if defined(BSP_USB_USING_DEVICE) + gd_usb_irq_count++; + usbd_isr(&gd_usb_device_core); +#endif + + rt_interrupt_leave(); +} + +#endif /* USE_USBHS0 */ + +#ifdef USE_USBHS1 +/*! + \brief this function handles USBHS1 interrupt + \param[in] usb_periph: USBHS0 or USBHS1 + \param[out] none + \retval none +*/ +void USBHS1_IRQHandler(void) +{ + rt_interrupt_enter(); + +#if defined(BSP_USB_USING_HOST) + gd_usbh_irq_count++; + usbh_isr(&gd_usb_host_core); +#endif + + rt_interrupt_leave(); +} + +#endif /* USE_USBHS1 */ + +/*! + \brief resume MCU clock + \param[in] usb_periph: USBHS0 or USBHS1 + \param[out] none + \retval none +*/ +static void resume_mcu_clk(void) +{ + /* enable HXTAL */ + rcu_osci_on(RCU_HXTAL); + + /* wait till HXTAL is ready */ + while (RESET == rcu_flag_get(RCU_FLAG_HXTALSTB)) + { + } + + /* enable PLL */ + rcu_osci_on(RCU_PLL0_CK); + + /* wait till PLL is ready */ + while (RESET == rcu_flag_get(RCU_FLAG_PLL0STB)) + { + } + + /* select PLL as system clock source */ + rcu_system_clock_source_config(RCU_CKSYSSRC_PLL0P); + + /* wait till PLL is used as system clock source */ + while (RCU_SCSS_PLL0P != rcu_system_clock_source_get()) + { + } +} + +#ifdef USE_USBHS0 +/*! + \brief this function handles USBHS0 wakeup interrupt request + \param[in] none + \param[out] none + \retval none +*/ +void USBHS0_WKUP_IRQHandler(void) +{ + rt_interrupt_enter(); + + if (gd_usb_device_core.bp.low_power) + { + resume_mcu_clk(); + +#ifndef USE_IRC48M + rcu_usb48m_clock_config(IDX_USBHS0, RCU_USB48MSRC_PLL0R); +#else + /* enable IRC48M clock */ + rcu_osci_on(RCU_IRC48M); + + /* wait till IRC48M is ready */ + while (SUCCESS != rcu_osci_stab_wait(RCU_IRC48M)) + { + } + + rcu_ck48m_clock_config(RCU_CK48MSRC_IRC48M); +#endif /* USE_IRC48M */ + + rcu_periph_clock_enable(RCU_USBHS0); + + usb_clock_active(&gd_usb_device_core); + } + + exti_interrupt_flag_clear(EXTI_31); + + rt_interrupt_leave(); +} + +#endif /* USE_USBHS0 */ + +#ifdef USE_USBHS1 +/*! + \brief this function handles USBHS1 wakeup interrupt request + \param[in] none + \param[out] none + \retval none +*/ +void USBHS1_WKUP_IRQHandler(void) +{ + rt_interrupt_enter(); + + if (gd_usb_host_core.bp.low_power) + { + resume_mcu_clk(); + +#ifndef USE_IRC48M + rcu_usb48m_clock_config(IDX_USBHS1, RCU_USB48MSRC_PLL1Q); +#else + /* enable IRC48M clock */ + rcu_osci_on(RCU_IRC48M); + + /* wait till IRC48M is ready */ + while (SUCCESS != rcu_osci_stab_wait(RCU_IRC48M)) + { + } + + rcu_ck48m_clock_config(RCU_CK48MSRC_IRC48M); +#endif /* USE_IRC48M */ + + rcu_periph_clock_enable(RCU_USBHS1); + + usb_clock_active(&gd_usb_host_core); + } + + exti_interrupt_flag_clear(EXTI_32); + + rt_interrupt_leave(); +} + +#endif /* USE_USBHS1 */ + +#ifdef USB_DEDICATED_EP1_ENABLED + +#ifdef USE_USBHS0 +/*! + \brief this function handles USBHS0 dedicated endpoint 1 OUT interrupt request. + \param[in] none + \param[out] none + \retval none +*/ +void USBHS0_EP1_OUT_IRQHandler(void) +{ + usbd_int_dedicated_ep1out(&cdc_acm); +} + +#endif /* USE_USBHS0 */ + +#ifdef USE_USBHS1 +/*! + \brief this function handles USBHS1 dedicated endpoint 1 OUT interrupt request. + \param[in] none + \param[out] none + \retval none +*/ +void USBHS1_EP1_OUT_IRQHandler(void) +{ + usbd_int_dedicated_ep1out(&cdc_acm); +} + +#endif /* USE_USBHS1 */ + +#ifdef USE_USBHS0 +/*! + \brief this function handles USBHS0 dedicated endpoint 1 IN interrupt request + \param[in] none + \param[out] none + \retval none +*/ +void USBHS0_EP1_IN_IRQHandler(void) +{ + usbd_int_dedicated_ep1in(&cdc_acm); +} + +#endif /* USE_USBHS0 */ + +#ifdef USE_USBHS1 +/*! + \brief this function handles USBHS1 dedicated endpoint 1 IN interrupt request + \param[in] none + \param[out] none + \retval none +*/ +void USBHS1_EP1_IN_IRQHandler(void) +{ + usbd_int_dedicated_ep1in(&cdc_acm); +} + +#endif /* USE_USBHS1 */ + +#endif /* USB_DEDICATED_EP1_ENABLED */ + +/*! + \brief configure USB clock + \param[in] usb_periph: USBHS0 or USBHS1 + \param[out] none + \retval none +*/ +void usb_rcu_config(uint32_t usb_periph) +{ + pmu_usb_regulator_enable(); + pmu_usb_voltage_detector_enable(); + while (SET != pmu_flag_get(PMU_FLAG_USB33RF)) + { + } + +#ifndef USE_IRC48M + if (USBHS0 == usb_periph) + { + rcu_periph_clock_enable(RCU_USBHS0); + + /* Required by the GD32H77x USBHS0 embedded-PHY startup sequence. */ + ((usb_gr *)USBHS0_REG_BASE)->GUSBCS |= GUSBCS_EMBPHY_FS; + ((usb_gr *)USBHS0_REG_BASE)->GCCFG |= GCCFG_PWRON; + + rcu_pllusb0_config(RCU_PLLUSBHSPRE_HXTAL, RCU_PLLUSBHSPRE_DIV5, + RCU_PLLUSBHS_MUL96, RCU_USBHS_DIV10); + RCU_ADDCTL1 |= RCU_ADDCTL1_PLLUSBHS0EN; + while (0U == (RCU_ADDCTL1 & RCU_ADDCTL1_PLLUSBHS0STB)) + { + } + + rcu_usb48m_clock_config(IDX_USBHS0, RCU_USB48MSRC_PLLUSBHS); + } + else if (USBHS1 == usb_periph) + { + rcu_periph_clock_enable(RCU_USBHS1); + + /* configure the pll1 input and output clock range */ + rcu_pll_input_output_clock_range_config(IDX_PLL1, RCU_PLL1RNG_4M_8M, RCU_PLL1VCO_192M_836M); + + rcu_pll1_config(5, 96, 2, 10, 2); + /* enable PLL1Q clock output */ + rcu_pll_clock_output_enable(RCU_PLL1Q); + rcu_osci_on(RCU_PLL1_CK); + + rcu_usbhs_pll1qpsc_config(IDX_USBHS1, RCU_USBHSPSC_DIV1); + + rcu_usb48m_clock_config(IDX_USBHS1, RCU_USB48MSRC_PLL1Q); + } + +#else + /* enable IRC48M clock */ + rcu_osci_on(RCU_IRC48M); + + /* wait till IRC48M is ready */ + while (SUCCESS != rcu_osci_stab_wait(RCU_IRC48M)) + { + } + + rcu_ck48m_clock_config(RCU_CK48MSRC_IRC48M); + + if (USBHS0 == usb_periph) + { + rcu_periph_clock_enable(RCU_USBHS0); + rcu_usb48m_clock_config(IDX_USBHS0, RCU_USB48MSRC_IRC48M); + } + else if (USBHS1 == usb_periph) + { + rcu_periph_clock_enable(RCU_USBHS1); + rcu_usb48m_clock_config(IDX_USBHS1, RCU_USB48MSRC_IRC48M); + } + +#endif /* USE_IRC48M */ + +#ifdef USE_ULPI_PHY +#ifdef USE_USBHS0 + rcu_periph_clock_enable(RCU_USBHS0ULPI); +#endif + +#ifdef USE_USBHS1 + rcu_periph_clock_enable(RCU_USBHS1ULPI); +#endif +#endif /* USE_ULPI_PHY */ +} + +/*! + \brief configure USB interrupt + \param[in] usb_periph: USBHS0 or USBHS1 + \param[out] none + \retval none +*/ +void usb_intr_config(uint32_t usb_periph) +{ + nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); + + if (USBHS0 == usb_periph) + { + nvic_irq_enable((uint8_t)USBHS0_IRQn, 3U, 0U); + } + else if (USBHS1 == usb_periph) + { + nvic_irq_enable((uint8_t)USBHS1_IRQn, 3U, 0U); + } + + /* enable the power module clock */ + rcu_periph_clock_enable(RCU_PMU); + + if (USBHS0 == usb_periph) + { + /* USB wakeup EXTI line configuration */ + exti_interrupt_flag_clear(EXTI_31); + exti_init(EXTI_31, EXTI_INTERRUPT, EXTI_TRIG_RISING); + exti_interrupt_enable(EXTI_31); + + nvic_irq_enable((uint8_t)USBHS0_WKUP_IRQn, 1U, 0U); + } + else if (USBHS1 == usb_periph) + { + /* USB wakeup EXTI line configuration */ + exti_interrupt_flag_clear(EXTI_32); + exti_init(EXTI_32, EXTI_INTERRUPT, EXTI_TRIG_RISING); + exti_interrupt_enable(EXTI_32); + + nvic_irq_enable((uint8_t)USBHS1_WKUP_IRQn, 1U, 0U); + } + +#ifdef USB_DEDICATED_EP1_ENABLED + +#ifdef USE_USBHS0 + nvic_irq_enable((uint8_t)USBHS0_EP1_OUT_IRQn, 1U, 0U); + nvic_irq_enable((uint8_t)USBHS0_EP1_IN_IRQn, 1U, 0U); +#endif /* USE_USBHS0 */ + +#ifdef USE_USBHS1 + nvic_irq_enable((uint8_t)USBHS1_EP1_OUT_IRQn, 1U, 0U); + nvic_irq_enable((uint8_t)USBHS1_EP1_IN_IRQn, 1U, 0U); +#endif /* USE_USBHS1 */ + +#endif /* USB_DEDICATED_EP1_ENABLED */ +} + + +#if defined(RT_USING_USB_HOST) +/*! + \brief drives the VBUS signal through GPIO + \param[in] state: VBUS states + \param[out] none + \retval none +*/ +void usb_vbus_drive(uint8_t state) +{ + if (0U == state) + { + /* disable is needed on output of the power switch */ + gpio_bit_reset(HOST_POWERSW_PORT, HOST_POWERSW_VBUS); + } + else + { + /* enable the power switch by driving the enable high */ + gpio_bit_set(HOST_POWERSW_PORT, HOST_POWERSW_VBUS); + } +} + +/*! + \brief configures the GPIO for the VBUS + \param[in] none + \param[out] none + \retval none +*/ +void usb_vbus_config(void) +{ + rcu_periph_clock_enable(HOST_POWERSW_PORT_RCC); + + /* USB host VBUS control pin configuration */ + gpio_mode_set(HOST_POWERSW_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, HOST_POWERSW_VBUS); + gpio_output_options_set(HOST_POWERSW_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, HOST_POWERSW_VBUS); + + /* by default, disable is needed on output of the power switch */ + usb_vbus_drive(0U); + + /* delay is need for stabilizing the VBUS low in reset condition, + * when VBUS = 1 and reset-button is pressed by user + */ + usb_mdelay(200); +} + +#endif + +/*! + \brief delay in microseconds + \param[in] usec: value of delay required in microseconds + \param[out] none + \retval none +*/ +void usb_udelay(const uint32_t usec) +{ + rt_hw_us_delay(usec); +} + +/*! + \brief delay in milliseconds + \param[in] msec: value of delay required in milliseconds + \param[out] none + \retval none +*/ +void usb_mdelay(const uint32_t msec) +{ + rt_hw_us_delay(msec * 1000U); +} + +/*! + \brief configure the PLL of USB + \param[in] usb_periph: USBHS0 + \param[out] none + \retval none +*/ +void pllusb_rcu_config(uint32_t usb_periph) +{ + if (USBHS0 == usb_periph) + { + rcu_pllusb0_config(RCU_PLLUSBHSPRE_HXTAL, RCU_PLLUSBHSPRE_DIV5, RCU_PLLUSBHS_MUL96, RCU_USBHS_DIV10); + RCU_ADDCTL1 |= RCU_ADDCTL1_PLLUSBHS0EN; + while (0U == (RCU_ADDCTL1 & RCU_ADDCTL1_PLLUSBHS0STB)) + { + } + + rcu_usb48m_clock_config(IDX_USBHS0, RCU_USB48MSRC_PLLUSBHS); + } +} + +#endif /* BSP_USING_USB */ diff --git a/bsp/gd32/arm/libraries/gd32_drivers/usb/usb_conf.h b/bsp/gd32/arm/libraries/gd32_drivers/usb/usb_conf.h new file mode 100644 index 000000000000..20cf563a6158 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/usb/usb_conf.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-11-07 RealThread the first version + */ + +#ifndef USB_CONF_H +#define USB_CONF_H + +#include +#include + +#if defined(BSP_USING_USB) + +#if defined(SOC_SERIES_GD32H77x_H78X) +#include "gd32h77x_78x.h" +#elif defined(SOC_SERIES_GD32H75E) +#include "gd32h75e.h" +#elif (defined(SOC_SERIES_GD32H7xx) || defined(SOC_SERIES_GD32H77x_H78X)) +#include "gd32h7xx.h" +#endif + +#if defined(BSP_USB_USING_DEVICE) +#define USE_USBHS0 +#endif + +#if defined(BSP_USB_USING_HOST) +#define USE_USBHS1 +#endif + +/* USBHS0 and USBHS1 may run different roles at the same time. Keep both + * role-specific data models enabled when both RT-Thread options are set. */ +#if defined(RT_USING_USB_DEVICE) +#define USE_DEVICE_MODE +#endif + +#if defined(RT_USING_USB_HOST) +#define USE_HOST_MODE +#define USBH_USE_RTOS +#endif + +/* USB FIFO size config */ +#define USB_DEV_RX_FIFO_SIZE 512U +#define USB_DEV_TX0_FIFO_SIZE 128U +#define USB_DEV_TX1_FIFO_SIZE 384U +#define USB_DEV_TX2_FIFO_SIZE 0U +#define USB_DEV_TX3_FIFO_SIZE 0U +#define USB_DEV_TX4_FIFO_SIZE 0U +#define USB_DEV_TX5_FIFO_SIZE 0U +#define USB_DEV_TX6_FIFO_SIZE 0U +#define USB_DEV_TX7_FIFO_SIZE 0U + +#define USB_HOST_RX_FIFO_SIZE 512U +#define USB_HOST_TX_NPFIFO_SIZE 256U +#define USB_HOST_TX_PFIFO_SIZE 256U + +/* General USB Configuration */ +#define USB_LOW_POWER 0U + +/* if uncomment it, need jump to USB JP */ +//#define VBUS_SENSING_ENABLED + +//#define USB_INTERNAL_DMA_ENABLED +//#define USB_DEDICATED_EP1_ENABLED +/* End General USB Configuration */ + +#ifdef USE_ULPI_PHY +#define USB_EXTERNAL_ULPI_PHY_ENABLED +#endif /* USE_ULPI_PHY */ + +/* all variables and data structures during the transaction process should be 4-bytes aligned */ +#if defined(__GNUC__) /* GNU Compiler */ +#define __ALIGN_END __attribute__((aligned(4))) +#define __ALIGN_BEGIN +#else +#define __ALIGN_END + +#if defined(__CC_ARM) /* ARM Compiler */ +#define __ALIGN_BEGIN __align(4) +#elif defined(__ICCARM__) /* IAR Compiler */ +#define __ALIGN_BEGIN +#elif defined(__TASKING__)/* TASKING Compiler */ +#define __ALIGN_BEGIN __align(4) +#endif /* __CC_ARM */ +#endif /* __GNUC__ */ + +/* __packed keyword used to decrease the data type alignment to 1-byte */ +#if defined(__GNUC__) /* GNU Compiler */ +#ifndef __packed +#define __packed __unaligned +#endif +#elif defined(__TASKING__) /* TASKING Compiler */ +#define __packed __unaligned +#endif /* __GNUC__ */ + +#endif /* BSP_USING_USB */ + +#endif /* USB_CONF_H */ diff --git a/components/net/netdev/src/netdev.c b/components/net/netdev/src/netdev.c index 5a4056b97a7b..1366946963ed 100644 --- a/components/net/netdev/src/netdev.c +++ b/components/net/netdev/src/netdev.c @@ -1390,15 +1390,18 @@ int netdev_cmd_ping(char* target_name, char *netdev_name, rt_uint32_t times, rt_ } else { + const char *resp_addr = ip_addr_isany(&(ping_resp.ip_addr)) ? + target_name : inet_ntoa(ping_resp.ip_addr); + if (ping_resp.ttl == 0) { rt_kprintf("%d bytes from %s icmp_seq=%d time=%d ms\n", - ping_resp.data_len, inet_ntoa(ping_resp.ip_addr), index + 1, ping_resp.ticks); + ping_resp.data_len, resp_addr, index + 1, ping_resp.ticks); } else { rt_kprintf("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n", - ping_resp.data_len, inet_ntoa(ping_resp.ip_addr), index + 1, ping_resp.ttl, ping_resp.ticks); + ping_resp.data_len, resp_addr, index + 1, ping_resp.ttl, ping_resp.ticks); } received += 1; if (ping_resp.ticks > max_time) @@ -1427,7 +1430,8 @@ int netdev_cmd_ping(char* target_name, char *netdev_name, rt_uint32_t times, rt_ loss = (uint32_t)((1 - ((float)received) / index) * 100); avg_time = (uint32_t)(avg_time / received); - rt_kprintf("\n--- %s ping statistics ---\n", inet_ntoa(ping_resp.ip_addr)); + rt_kprintf("\n--- %s ping statistics ---\n", + ip_addr_isany(&(ping_resp.ip_addr)) ? target_name : inet_ntoa(ping_resp.ip_addr)); rt_kprintf("%d packets transmitted, %d received, %d%% packet loss\n", index, received, loss); if (received > 0) {