From 31189c2939a33f60b890d9f16234bfaf1d1c4878 Mon Sep 17 00:00:00 2001 From: wgzAIIT <820906721@qq.com> Date: Wed, 23 Nov 2022 18:15:53 +0800 Subject: [PATCH] add edu-riscv64 board on nuttx --- .../aiit_board/edu-riscv64/Kconfig | 166 +++ .../aiit_board/edu-riscv64/README.txt | 42 + .../edu-riscv64/configs/nsh/defconfig | 60 ++ .../aiit_board/edu-riscv64/img/K-Flash.jpg | Bin 0 -> 26457 bytes .../aiit_board/edu-riscv64/img/gccdir.jpg | Bin 0 -> 23952 bytes .../aiit_board/edu-riscv64/img/k210-shell.jpg | Bin 0 -> 29734 bytes .../aiit_board/edu-riscv64/img/menuconfig.png | Bin 0 -> 31565 bytes .../edu-riscv64/img/menuconfig1.png | Bin 0 -> 18959 bytes .../edu-riscv64/img/menuconfigexit.png | Bin 0 -> 11206 bytes .../aiit_board/edu-riscv64/include/board.h | 205 ++++ .../aiit_board/edu-riscv64/kernel/Makefile | 92 ++ .../edu-riscv64/kernel/k210_userspace.c | 114 +++ .../aiit_board/edu-riscv64/readme.md | 174 ++++ .../aiit_board/edu-riscv64/scripts/Make.defs | 70 ++ .../aiit_board/edu-riscv64/scripts/gnu-elf.ld | 115 +++ .../aiit_board/edu-riscv64/scripts/ld.script | 100 ++ .../aiit_board/edu-riscv64/scripts/memory.ld | 37 + .../edu-riscv64/scripts/user-space.ld | 94 ++ .../aiit_board/edu-riscv64/src/Makefile | 65 ++ .../aiit_board/edu-riscv64/src/can_demo.c | 147 +++ .../aiit_board/edu-riscv64/src/ch376_demo.c | 70 ++ .../aiit_board/edu-riscv64/src/ch376inc.h | 584 +++++++++++ .../aiit_board/edu-riscv64/src/ch438_demo.c | 84 ++ .../aiit_board/edu-riscv64/src/edu-riscv64.h | 36 + .../aiit_board/edu-riscv64/src/k210_appinit.c | 76 ++ .../aiit_board/edu-riscv64/src/k210_boot.c | 59 ++ .../aiit_board/edu-riscv64/src/k210_bringup.c | 141 +++ .../aiit_board/edu-riscv64/src/k210_ch376.c | 960 ++++++++++++++++++ .../aiit_board/edu-riscv64/src/k210_ch376.h | 124 +++ .../aiit_board/edu-riscv64/src/k210_ch438.c | 922 +++++++++++++++++ .../aiit_board/edu-riscv64/src/k210_ch438.h | 326 ++++++ .../aiit_board/edu-riscv64/src/k210_gpio.c | 180 ++++ .../aiit_board/edu-riscv64/src/k210_lcd.c | 353 +++++++ .../aiit_board/edu-riscv64/src/k210_leds.c | 47 + .../aiit_board/edu-riscv64/src/k210_reset.c | 60 ++ .../aiit_board/edu-riscv64/src/k210_touch.c | 475 +++++++++ .../aiit_board/edu-riscv64/src/k210_touch.h | 75 ++ .../aiit_board/edu-riscv64/src/k210_w5500.c | 840 +++++++++++++++ .../aiit_board/edu-riscv64/src/k210_w5500.h | 319 ++++++ .../app_match_nuttx/build.sh | 1 + .../app_match_nuttx/nuttx/boards/Kconfig | 12 + 41 files changed, 7225 insertions(+) create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/Kconfig create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/README.txt create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/configs/nsh/defconfig create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/img/K-Flash.jpg create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/img/gccdir.jpg create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/img/k210-shell.jpg create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/img/menuconfig.png create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/img/menuconfig1.png create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/img/menuconfigexit.png create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/include/board.h create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/kernel/Makefile create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/kernel/k210_userspace.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/readme.md create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/Make.defs create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/gnu-elf.ld create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/ld.script create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/memory.ld create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/user-space.ld create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/Makefile create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/can_demo.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/ch376_demo.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/ch376inc.h create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/ch438_demo.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/edu-riscv64.h create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_appinit.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_boot.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_bringup.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch376.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch376.h create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch438.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch438.h create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_gpio.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_lcd.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_leds.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_reset.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_touch.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_touch.h create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_w5500.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_w5500.h diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/Kconfig b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/Kconfig new file mode 100644 index 000000000..5c2b01d41 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/Kconfig @@ -0,0 +1,166 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +if ARCH_BOARD_EDU_RISCV64 + +menuconfig BSP_USING_CH376 + bool "Using CH376 device" + default n + select K210_16550_UART + select K210_16550_UART3 + +if BSP_USING_CH376 + +choice + prompt "select ch376 function." + default CH376_USB_FUNCTION + +config CH376_USB_FUNCTION + bool "select ch376 usb function" + +config CH376_SD_FUNCTION + bool "select ch376 sd function" +endchoice + +config CH376_WORK_MODE + hex "ch376 work mode set:0x03 sd,0x06 u-disk" + default 0x03 if CH376_SD_FUNCTION + default 0x06 if CH376_USB_FUNCTION + +endif # BSP_USING_CH376 + +menuconfig BSP_USING_ENET + bool "Using ENET device" + default n + +menuconfig BSP_USING_TOUCH + bool "Using touch device" + default n + +menuconfig BSP_USING_CAN + select K210_16550_UART + select K210_16550_UART1 + bool "Using CAN device" + default n + +menuconfig BSP_USING_CH438 + bool "Using CH438 device" + default n + +if BSP_USING_CH438 +config CH438_EXTUART0 + bool "Using Ch438 Port 0" + default n + +menu "Ch438 Port 0 Configuration" + depends on CH438_EXTUART0 + + config CH438_EXTUART0_BAUD + int "Ch438 Port 0 Baud Rate." + default 115200 + ---help--- + The configured BAUD of the CH438 EXTUART0. +endmenu + +config CH438_EXTUART1 + bool "Using Ch438 Port 1" + default n + +menu "Ch438 Port 1 Configuration" + depends on CH438_EXTUART1 + + config CH438_EXTUART1_BAUD + int "Ch438 Port 1 Baud Rate." + default 115200 + ---help--- + The configured BAUD of the CH438 EXTUART1. +endmenu + +config CH438_EXTUART2 + bool "Using Ch438 Port 2" + default n + +menu "Ch438 Port 2 Configuration" + depends on CH438_EXTUART2 + + config CH438_EXTUART2_BAUD + int "Ch438 Port 2 Baud Rate." + default 115200 + ---help--- + The configured BAUD of the CH438 EXTUART2. +endmenu + +config CH438_EXTUART3 + bool "Using Ch438 Port 3" + default n + +menu "Ch438 Port 3 Configuration" + depends on CH438_EXTUART3 + + config CH438_EXTUART3_BAUD + int "Ch438 Port 3 Baud Rate." + default 115200 + ---help--- + The configured BAUD of the CH438 EXTUART3. +endmenu + +config CH438_EXTUART4 + bool "Using Ch438 Port 4" + default n + +menu "Ch438 Port 4 Configuration" + depends on CH438_EXTUART4 + + config CH438_EXTUART4_BAUD + int "Ch438 Port 4 Baud Rate." + default 115200 + ---help--- + The configured BAUD of the CH438 EXTUART4. +endmenu + +config CH438_EXTUART5 + bool "Using Ch438 Port 5" + default n + +menu "Ch438 Port 5 Configuration" + depends on CH438_EXTUART5 + + config CH438_EXTUART5_BAUD + int "Ch438 Port 5 Baud Rate." + default 115200 + ---help--- + The configured BAUD of the CH438 EXTUART5. +endmenu + +config CH438_EXTUART6 + bool "Using Ch438 Port 6" + default n + +menu "Ch438 Port 6 Configuration" + depends on CH438_EXTUART6 + + config CH438_EXTUART6_BAUD + int "Ch438 Port 6 Baud Rate." + default 115200 + ---help--- + The configured BAUD of the CH438 EXTUART6. +endmenu + +config CH438_EXTUART7 + bool "Using Ch438 Port 7" + default n + +menu "Ch438 Port 7 Configuration" + depends on CH438_EXTUART7 + + config CH438_EXTUART7_BAUD + int "Ch438 Port 7 Baud Rate." + default 115200 + ---help--- + The configured BAUD of the CH438 EXTUART7. +endmenu + +endif # BSP_USING_CH438 +endif # ARCH_BOARD_EDU_RISCV64 diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/README.txt b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/README.txt new file mode 100644 index 000000000..2520cc71b --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/README.txt @@ -0,0 +1,42 @@ +1. Download and install toolchain and openocd-k210 + + $ curl https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14.tar.gz + $ export PATH=$PATH:/$TOOL_CHAIN_PATH/bin + +2. Build openocd-k210 + + $ git clone https://github.com/kendryte/openocd-kendryte + $ cd openocd-kendryte + $ ./bootstrap & ./configure & make + +3. Configure and build NuttX + + $ mkdir ./nuttx; cd ./nuttx + $ git clone https://github.com/apache/incubator-nuttx.git nuttx + $ git clone https://github.com/apache/incubator-nuttx-apps.git apps + $ cd nuttx + $ make distclean + $ ./tools/configure.sh edu-riscv64:nsh + $ make V=1 + +4. Download and run the nuttx from SRAM (not SPI-Flash) + + $ picocom -b 115200 /dev/ttyUSB0 + $ sudo ./src/openocd -s ./tcl -f ./tcl/kendryte.cfg -m 0 + $ riscv64-unknown-elf-gdb ./nuttx + (gdb) target extended-remote :3333 + (gdb) load nuttx + (gdb) c + +5. Write nuttx.bin to SPI-Flash + + $ pip3 install kflash + $ kflash -p /dev/ttyUSB0 -b 1500000 ./nuttx/nuttx.bin + + NOTE: The kflash_gui is not recommended because it's unstable + +6. TODO + + Support peripherals such as GPIO/SPI/I2C/... + Support FPU + Support RISC-V U-mode including memory protection diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/configs/nsh/defconfig b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/configs/nsh/defconfig new file mode 100644 index 000000000..c93933657 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/configs/nsh/defconfig @@ -0,0 +1,60 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_NSH_DISABLE_LOSMART is not set +# CONFIG_STANDARD_SERIAL is not set +CONFIG_ADD_NUTTX_FETURES=y +CONFIG_ARCH="risc-v" +CONFIG_ARCH_BOARD="edu-riscv64" +CONFIG_ARCH_BOARD_EDU_RISCV64=y +CONFIG_ARCH_CHIP="k210" +CONFIG_ARCH_CHIP_K210=y +CONFIG_ARCH_INTERRUPTSTACK=2048 +CONFIG_ARCH_RISCV=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_BINFMT_DISABLE=y +CONFIG_BOARD_LOOPSPERMSEC=46000 +CONFIG_BUILTIN=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_IDLETHREAD_STACKSIZE=2048 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INIT_STACKSIZE=3072 +CONFIG_INTELHEX_BINARY=y +CONFIG_LIBC_PERROR_STDOUT=y +CONFIG_LIBC_STRERROR=y +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_DISABLE_IFUPDOWN=y +CONFIG_NSH_DISABLE_MKDIR=y +CONFIG_NSH_DISABLE_RM=y +CONFIG_NSH_DISABLE_RMDIR=y +CONFIG_NSH_DISABLE_UMOUNT=y +CONFIG_NSH_READLINE=y +CONFIG_NSH_STRERROR=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_RAM_SIZE=2097152 +CONFIG_RAM_START=0x80400000 +CONFIG_RAW_BINARY=y +CONFIG_READLINE_CMD_HISTORY=y +CONFIG_READLINE_CMD_HISTORY_LEN=100 +CONFIG_READLINE_CMD_HISTORY_LINELEN=120 +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_WAITPID=y +CONFIG_STACK_COLORATION=y +CONFIG_START_DAY=28 +CONFIG_START_MONTH=12 +CONFIG_START_YEAR=2019 +CONFIG_SYSTEM_NSH=y +CONFIG_TASK_NAME_SIZE=20 +CONFIG_TESTING_GETPRIME=y +CONFIG_UART0_SERIAL_CONSOLE=y +CONFIG_READLINE_TABCOMPLETION=y +CONFIG_SCHED_HPWORK=y +CONFIG_DEV_GPIO=y +CONFIG_BOARDCTL_RESET=y diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/img/K-Flash.jpg b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/img/K-Flash.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aa5ebc0f5ede9bf8d96e10d5c1ba2a440d0f47fc GIT binary patch literal 26457 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 O6G8v~I%oN3^1lHeQg2HD literal 0 HcmV?d00001 diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/img/menuconfig.png b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/img/menuconfig.png new file mode 100644 index 0000000000000000000000000000000000000000..ea275e1ffa5d3f865710b38bc7077b5839ae638f GIT binary patch literal 31565 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 h#W&EOhY4FqNa-j43tqhLxI=cu9#A5p1ec)8r+` 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 + +#ifndef __ASSEMBLY__ +# include +#endif + +#include "k210.h" + +#include "k210_fpioa.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + + + +/* Map pad 14 to gpiohs io 0 */ +#define BOARD_LED_IO_FUNC K210_IO_FUNC_GPIOHS0 + +#define LED_STARTED 0 /* N/C */ +#define LED_HEAPALLOCATE 1 /* N/C */ +#define LED_IRQSENABLED 2 /* N/C */ +#define LED_STACKCREATED 3 /* N/C */ +#define LED_INIRQ 4 /* N/C */ +#define LED_SIGNAL 5 /* N/C */ +#define LED_ASSERTION 6 /* N/C */ +#define LED_PANIC 7 /* blink */ + +/* GPIO pins used by the GPIO Subsystem */ + +#define BOARD_NGPIOOUT 3 /* Amount of register GPIO Output pins */ +#define BOARD_NGPIOINT 0 /* Amount of GPIO Input */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/*************************** GPIO define ***************************/ + +/* UART IO */ +#define GPIO_WIFI_RXD 7 +#define GPIO_WIFI_TXD 6 +#define GPIO_EC200T_RXD 21 +#define GPIO_EC200T_TXD 20 +#define GPIO_CH376T_RXD 22 +#define GPIO_CH376T_TXD 23 +#define GPIO_CAN_RXD 18 +#define GPIO_CAN_TXD 19 + +/* ch438 IO */ +#define CH438_ALE_PIN 24 +#define CH438_NWR_PIN 25 +#define CH438_NRD_PIN 26 +#define CH438_D0_PIN 27 +#define CH438_D1_PIN 28 +#define CH438_D2_PIN 29 +#define CH438_D3_PIN 30 +#define CH438_D4_PIN 31 +#define CH438_D5_PIN 32 +#define CH438_D6_PIN 33 +#define CH438_D7_PIN 34 +#define CH438_INT_PIN 35 + +/* w5500 IO */ +#define BSP_ENET_SCLK 9 +#define BSP_ENET_MISO 10 +#define BSP_ENET_MOSI 11 +#define BSP_ENET_NCS 12 +#define BSP_ENET_NRST 13 +#define BSP_ENET_NINT 14 + +/* LCD IO */ +#define BSP_LCD_NRST 37 +#define BSP_LCD_SCLK 38 +#define BSP_LCD_MOSI 39 +#define BSP_LCD_MISO 40 +#define BSP_LCD_NCS 41 +#define BSP_LCD_BL_PIN 47 + +/* I2C */ +#define BSP_IIC_SDA 15 +#define BSP_IIC_SCL 17 + +/* other mode io */ +#define GPIO_E220_M0 44 +#define GPIO_E220_M1 45 +#define GPIO_E18_MODE 46 +#define GPIO_WIFI_EN 8 +#define GPIO_CAN_CFG 43 + +/************************** end GPIO define **************************/ + + +/*************************** FPIOA define ***************************/ + +/* UART FPOA */ +#define FPOA_USART1_RX K210_IO_FUNC_UART1_RX +#define FPOA_USART1_TX K210_IO_FUNC_UART1_TX +#define FPOA_USART2_RX K210_IO_FUNC_UART2_RX +#define FPOA_USART2_TX K210_IO_FUNC_UART2_TX +#define FPOA_USART3_RX K210_IO_FUNC_UART3_RX +#define FPOA_USART3_TX K210_IO_FUNC_UART3_TX + +/* ch438 FPIOA */ +#define FPIOA_CH438_ALE 11 +#define FPIOA_CH438_NWR 12 +#define FPIOA_CH438_NRD 13 +#define FPIOA_CH438_D0 14 +#define FPIOA_CH438_D1 15 +#define FPIOA_CH438_D2 16 +#define FPIOA_CH438_D3 17 +#define FPIOA_CH438_D4 18 +#define FPIOA_CH438_D5 29 +#define FPIOA_CH438_D6 20 +#define FPIOA_CH438_D7 31 +#define FPIOA_CH438_INT 22 + +/* w5500 FPIOA */ +#define FPIOA_ENET_NRST 0 +#define FPIOA_ENET_NINT 9 +#define FPIOA_ENET_SCLK 28 +#define FPIOA_ENET_MISO 29 +#define FPIOA_ENET_MOSI 23 +#define FPIOA_ENET_NCS 31 + +/* LCD FPIOA */ +#define FPIOA_LCD_NRST 0 +#define FPIOA_LCD_BL 9 +#define FPIOA_LCD_SCLK 28 +#define FPIOA_LCD_MOSI 29 +#define FPIOA_LCD_MISO 23 +#define FPIOA_LCD_NCS 31 + +/* I2C */ +#define FPIOA_IIC_SDA 7 +#define FPIOA_IIC_SCL 8 + +/* other mode FPIOA */ +#define FPIOA_E220_M0 1 +#define FPIOA_E220_M1 2 +#define FPIOA_E18_MODE 3 +#define FPIOA_WIFI_EN 4 +#define FPIOA_CAN_NCFG 5 + +/************************** end FPIOA define **************************/ + + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: k210_boardinitialize + ****************************************************************************/ + +void k210_boardinitialize(void); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif +#endif /* __ASSEMBLY__ */ +#endif /* __BOARDS_RISC-V_K210_EDU_RISCV64_INCLUDE_BOARD_H */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/kernel/Makefile b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/kernel/Makefile new file mode 100644 index 000000000..43d582f0b --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/kernel/Makefile @@ -0,0 +1,92 @@ +############################################################################ +# boards/risc-v/k210/edu-riscv64/kernel/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(TOPDIR)/Make.defs + +# The entry point name (if none is provided in the .config file) + +CONFIG_INIT_ENTRYPOINT ?= user_start +ENTRYPT = $(patsubst "%",%,$(CONFIG_INIT_ENTRYPOINT)) + +# Get the paths to the libraries and the links script path in format that +# is appropriate for the host OS + +USER_LIBPATHS = $(addprefix -L,$(call CONVERT_PATH,$(addprefix $(TOPDIR)$(DELIM),$(dir $(USERLIBS))))) +USER_LDSCRIPT = -T $(call CONVERT_PATH,$(BOARD_DIR)$(DELIM)scripts$(DELIM)memory.ld) +USER_LDSCRIPT += -T $(call CONVERT_PATH,$(BOARD_DIR)$(DELIM)scripts$(DELIM)user-space.ld) +USER_HEXFILE += $(call CONVERT_PATH,$(TOPDIR)$(DELIM)nuttx_user.hex) +USER_SRECFILE += $(call CONVERT_PATH,$(TOPDIR)$(DELIM)nuttx_user.srec) +USER_BINFILE += $(call CONVERT_PATH,$(TOPDIR)$(DELIM)nuttx_user.bin) + +USER_LDFLAGS = --undefined=$(ENTRYPT) --entry=$(ENTRYPT) $(USER_LDSCRIPT) +USER_LDLIBS = $(patsubst lib%,-l%,$(basename $(notdir $(USERLIBS)))) +USER_LIBGCC = "${shell "$(CC)" $(ARCHCPUFLAGS) -print-libgcc-file-name}" + +# Source files + +CSRCS = k210_userspace.c +COBJS = $(CSRCS:.c=$(OBJEXT)) +OBJS = $(COBJS) + +# Targets: + +all: $(TOPDIR)$(DELIM)nuttx_user.elf $(TOPDIR)$(DELIM)User.map +.PHONY: nuttx_user.elf depend clean distclean + +$(COBJS): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +# Create the nuttx_user.elf file containing all of the user-mode code + +nuttx_user.elf: $(OBJS) + $(Q) $(LD) -o $@ $(USER_LDFLAGS) $(USER_LIBPATHS) $(OBJS) --start-group $(USER_LDLIBS) --end-group $(USER_LIBGCC) + +$(TOPDIR)$(DELIM)nuttx_user.elf: nuttx_user.elf + @echo "LD: nuttx_user.elf" + $(Q) cp -a nuttx_user.elf $(TOPDIR)$(DELIM)nuttx_user.elf +ifeq ($(CONFIG_INTELHEX_BINARY),y) + @echo "CP: nuttx_user.hex" + $(Q) $(OBJCOPY) $(OBJCOPYARGS) -O ihex nuttx_user.elf $(USER_HEXFILE) +endif +ifeq ($(CONFIG_MOTOROLA_SREC),y) + @echo "CP: nuttx_user.srec" + $(Q) $(OBJCOPY) $(OBJCOPYARGS) -O srec nuttx_user.elf $(USER_SRECFILE) +endif +ifeq ($(CONFIG_RAW_BINARY),y) + @echo "CP: nuttx_user.bin" + $(Q) $(OBJCOPY) $(OBJCOPYARGS) -O binary nuttx_user.elf $(USER_BINFILE) +endif + +$(TOPDIR)$(DELIM)User.map: nuttx_user.elf + @echo "MK: User.map" + $(Q) $(NM) -n nuttx_user.elf >$(TOPDIR)$(DELIM)User.map + $(Q) $(CROSSDEV)size nuttx_user.elf + +.depend: + +depend: .depend + +clean: + $(call DELFILE, nuttx_user.elf) + $(call DELFILE, "$(TOPDIR)$(DELIM)nuttx_user.*") + $(call DELFILE, "$(TOPDIR)$(DELIM)User.map") + $(call CLEAN) + +distclean: clean diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/kernel/k210_userspace.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/kernel/k210_userspace.c new file mode 100644 index 000000000..9fd37af39 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/kernel/k210_userspace.c @@ -0,0 +1,114 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file k210_userspace.c + * @brief edu-riscv64 k210_userspace.c + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.03.17 + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include +#include + +#if defined(CONFIG_BUILD_PROTECTED) && !defined(__KERNEL__) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifndef CONFIG_NUTTX_USERSPACE +# error "CONFIG_NUTTX_USERSPACE not defined" +#endif + +#if CONFIG_NUTTX_USERSPACE != 0x80100000 +# error "CONFIG_NUTTX_USERSPACE must match the value in memory.ld" +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* These 'addresses' of these values are setup by the linker script. + * They are not actual uint32_t storage locations! + * They are only used meaningfully in the following way: + * + * - The linker script defines, for example, the symbol_sdata. + * - The declaration extern uint32_t _sdata; makes C happy. C will believe + * that the value _sdata is the address of a uint32_t variable _data + * (it is not!). + * - We can recover the linker value then by simply taking the address of + * of _data. like: uint32_t *pdata = &_sdata; + */ + +extern uint32_t _stext; /* Start of .text */ +extern uint32_t _etext; /* End_1 of .text + .rodata */ +extern const uint32_t _eronly; /* End+1 of read only section */ +extern uint32_t _sdata; /* Start of .data */ +extern uint32_t _edata; /* End+1 of .data */ +extern uint32_t _sbss; /* Start of .bss */ +extern uint32_t _ebss; /* End+1 of .bss */ + +/* This is the user space entry point */ + +int CONFIG_INIT_ENTRYPOINT(int argc, char *argv[]); + +const struct userspace_s userspace locate_data(".userspace") = +{ + /* General memory map */ + + .us_entrypoint = (main_t)CONFIG_INIT_ENTRYPOINT, + .us_textstart = (uintptr_t)&_stext, + .us_textend = (uintptr_t)&_etext, + .us_datasource = (uintptr_t)&_eronly, + .us_datastart = (uintptr_t)&_sdata, + .us_dataend = (uintptr_t)&_edata, + .us_bssstart = (uintptr_t)&_sbss, + .us_bssend = (uintptr_t)&_ebss, + + /* Memory manager heap structure */ + + .us_heap = &g_mmheap, + + /* Task/thread startup routines */ + + .task_startup = nxtask_startup, + + /* Signal handler trampoline */ + + .signal_handler = up_signal_handler, + + /* User-space work queue support (declared in include/nuttx/wqueue.h) */ + +#ifdef CONFIG_LIBC_USRWORK + .work_usrstart = work_usrstart, +#endif +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#endif /* CONFIG_BUILD_PROTECTED && !__KERNEL__ */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/readme.md b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/readme.md new file mode 100644 index 000000000..269c0187f --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/readme.md @@ -0,0 +1,174 @@ +# 从零开始构建矽璓工业物联操作系统:使用ARM架构的矽达通 + +# edu-riscv64 + +[XiUOS](http://xuos.io/) (X Industrial Ubiquitous Operating System) 矽璓XiUOS是一款面向智慧车间的工业物联网操作系统,主要由一个极简的微型实时操作系统内核和其上的工业物联框架构成,通过高效管理工业物联网设备、支撑工业物联应用,在生产车间内实现智能化的“感知环境、联网传输、知悉识别、控制调整”,促进以工业设备和工业控制系统为核心的人、机、物深度互联,帮助提升生产线的数字化和智能化水平。 + +## 1. 简介 + +| 硬件 | 描述 | +| -- | -- | +|芯片型号| 勘智K210 | +|架构| 双核riscv64 | +|主频| 400MHz | +|片内SRAM| 8M | +|外设支持| 内嵌AES与SHA256算法加速器 | +|| DVP、JTAG、OTP、FPIOA、GPIO、UART、SPI、RTC、I²S、I²C、WDT、Timer与PWM | + +XiUOS板级当前支持使用CH438、GPIO、UART等。 + +## 2. 开发环境搭建 + +### 推荐使用: + +**操作系统:** ubuntu18.04 [https://ubuntu.com/download/desktop](https://ubuntu.com/download/desktop) + +更新`ubuntu 18.04`源的方法:(根据自身情况而定,可以不更改) + +第一步:打开sources.list文件 + +```c +sudo vim /etc/apt/sources.list +``` + +第二步:将以下内容复制到sources.list文件 + +```c +deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse +deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse +deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse +deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse +deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse +deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse +deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse +deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse +deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse +deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse +``` + +第三步:更新源和系统软件 + +```c +sudo apt-get update +sudo apt-get upgrade +``` + +**开发工具推荐使用 VSCode ,VScode下载地址为:** VSCode [https://code.visualstudio.com/](https://code.visualstudio.com/),推荐下载地址为 [http://vscode.cdn.azure.cn/stable/3c4e3df9e89829dce27b7b5c24508306b151f30d/code_1.55.2-1618307277_amd64.deb](http://vscode.cdn.azure.cn/stable/3c4e3df9e89829dce27b7b5c24508306b151f30d/code_1.55.2-1618307277_amd64.deb) + +### 依赖包安装: + +``` +$ sudo apt install build-essential pkg-config git +$ sudo apt install gcc make libncurses5-dev openssl libssl-dev bison flex libelf-dev autoconf libtool gperf libc6-dev +``` + +**XiUOS操作系统源码下载,这个仓里的代码可能不是最新的,最好git clone自己仓里的代码:** XiUOS [https://www.gitlink.org.cn/xuos/xiuos](https://www.gitlink.org.cn/xuos/xiuos) + +新建一个空文件夹并进入文件夹中,并下载源码,具体命令如下: + +```c +mkdir test && cd test +git clone https://gitlink.org.cn/xuos/xiuos.git +git checkout origin/prepare_for_master (以实际分支为准) +``` + +打开XiUOS源码文件包可以看到以下目录: +| 名称 | 说明 | +| -- | -- | +| APP_Framework | 应用代码 | +| Ubiquitous | 板级支持包,支持NuttX、RT-Thread和XiZi内核 | + + +### 裁减配置工具的下载 + +裁减配置工具: + +**工具地址:** kconfig-frontends [https://www.gitlink.org.cn/xuos/kconfig-frontends](https://www.gitlink.org.cn/xuos/kconfig-frontends),下载与安装的具体命令如下: + +```c +mkdir kfrontends && cd kfrontends +git clone https://gitlink.org.cn/xuos/kconfig-frontends.git +``` + +下载源码后按以下步骤执行软件安装: + +```c +cd kconfig-frontends +./xs_build.sh +``` + +### 编译工具链: + +Riscv64: riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar,在我的个人仓保存有一份: + +https://gitlink.org.cn/wgzAIIT/build_tools.git + +下载后解压到Ubantu环境的/opt目录下。 + +再将/opt/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin目录下的全部二进制文件软连接到/usr/bin,例如 + +![gccdir](img/gccdir.jpg) + + + +## 3.编译bin包 + +1.XiUOS操作系统源码下载: [https://www.gitlink.org.cn/xuos/xiuos](https://www.gitlink.org.cn/xuos/xiuos) + + 目前大家都使用的个人仓,请注意此处的路径及分支,如果需要须进行分支切换 + +2.在Ubuntu18.04环境中的代码路径,执行以下命令,生成配置文件 + +```shell +cd ./Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx +source build.sh + +执行完毕会自动进入./Ubiquitous/Nuttx_Fusion_XiUOS/nuttx下,继续执行 + +./tools/configure.sh edu-riscv64:nsh +make menuconfig +视情况而定,如果需要前面加sudo +``` + +3..在menuconfig界面配置需要关闭和开启的功能,按回车键进入下级菜单,按Y键选中需要开启的功能,按N键选中需要关闭的功能,配置结束后保存并退出(本例旨在演示简单的输出例程,所以没有需要配置的选项,双击快捷键ESC退出配置) + +![menuconfig](./img/menuconfig.png) + +退出时选择`yes`保存上面所配置的内容,如下图所示: + +![menuconfig1](./img/menuconfigexit.png) + +4.继续执行以下命令,进行编译 + +```shell +make +或 +make -j8 +``` + +make时加上V=1参数可以看到较为详细的编译信息,但是编译过程会比较慢。 + +5.如果编译正确无误,会在当前目录下产生nuttx.bin、nuttx、nuttx.hex等文件。 + +## 4. 烧写及运行 + +### 4.1 烧写 +1、烧写工具:K-Flash.exe,可https://gitlink.org.cn/wgzAIIT/build_tools.git下载 + +![K-Flash](img/K-Flash.jpg) + +在①选择串口 com 号 + +在②处选择波特率,选择 115200 + +在③处选择编译出的 nuttx.bin 文件 + +设备在上电时确保 Boot 和 GND 短接,这是升级模式。 + +点击④处 Flash 开始烧录,显示烧录完成即可,中间有报错的话,重新 Flash。 + +### 4.2 运行结果 + +烧写完毕重新上电,进入 shell。 + +![k210-shell](img/k210-shell.jpg) diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/Make.defs b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/Make.defs new file mode 100644 index 000000000..c153000db --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/Make.defs @@ -0,0 +1,70 @@ +############################################################################ +# boards/risc-v/k210/edu-riscv64/scripts/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(TOPDIR)/.config +include $(TOPDIR)/tools/Config.mk +include $(TOPDIR)/arch/risc-v/src/common/Toolchain.defs + +LDSCRIPT = ld.script + +ARCHSCRIPT += $(BOARD_DIR)$(DELIM)scripts$(DELIM)$(LDSCRIPT) + +ifeq ($(CONFIG_DEBUG_SYMBOLS),y) + ARCHOPTIMIZATION = -g + ASARCHCPUFLAGS += -Wa,-g +endif + +MAXOPTIMIZATION = -Os + +ifneq ($(CONFIG_DEBUG_NOOPT),y) + ARCHOPTIMIZATION += $(MAXOPTIMIZATION) -fno-strict-aliasing +endif + +ARCHCPUFLAGS += -mcmodel=medany -mstrict-align +ARCHCFLAGS = -fno-common -ffunction-sections -fdata-sections +ARCHCXXFLAGS = -fno-common -fno-exceptions -fcheck-new -fno-rtti +ARCHWARNINGS = -Wall -Wstrict-prototypes -Wshadow -Wundef +ARCHWARNINGSXX = -Wall -Wshadow -Wundef + +CFLAGS := $(APPPATHS) $(ARCHCFLAGS) $(ARCHWARNINGS) $(ARCHOPTIMIZATION) $(ARCHCPUFLAGS) $(ARCHINCLUDES) $(ARCHDEFINES) $(EXTRAFLAGS) -pipe +CPICFLAGS = $(ARCHPICFLAGS) $(CFLAGS) +CXXFLAGS := $(ARCHCXXFLAGS) $(ARCHWARNINGSXX) $(ARCHOPTIMIZATION) $(ARCHCPUFLAGS) $(ARCHXXINCLUDES) $(ARCHDEFINES) $(EXTRAFLAGS) -pipe +CXXPICFLAGS = $(ARCHPICFLAGS) $(CXXFLAGS) +CPPFLAGS := $(ARCHINCLUDES) $(ARCHDEFINES) $(EXTRAFLAGS) +AFLAGS += $(CFLAGS) -D__ASSEMBLY__ $(ASARCHCPUFLAGS) + +# Loadable module definitions + +CMODULEFLAGS = $(CFLAGS) + +LDMODULEFLAGS = -r -e module_initialize +LDMODULEFLAGS += -T $(call CONVERT_PATH,$(TOPDIR)/libs/libc/modlib/gnu-elf.ld) + +# ELF module definitions + +CELFFLAGS = $(CFLAGS) +CXXELFFLAGS = $(CXXFLAGS) + +LDELFFLAGS = -r -e main +LDELFFLAGS += -T $(call CONVERT_PATH,$(BOARD_DIR)$(DELIM)scripts$(DELIM)gnu-elf.ld) + +# File extensions + +LDFLAGS += --gc-sections -melf64lriscv diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/gnu-elf.ld b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/gnu-elf.ld new file mode 100644 index 000000000..4cc79b314 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/gnu-elf.ld @@ -0,0 +1,115 @@ +/**************************************************************************** + * boards/risc-v/k210/edu-riscv64/scripts/gnu-elf.ld + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +SECTIONS +{ + .text 0x00000000 : + { + _stext = . ; + *(.text) + *(.text.*) + *(.gnu.warning) + *(.stub) + *(.glue_7) + *(.glue_7t) + *(.jcr) + + /* C++ support: The .init and .fini sections contain specific logic + * to manage static constructors and destructors. + */ + + *(.gnu.linkonce.t.*) + *(.init) /* Old ABI */ + *(.fini) /* Old ABI */ + _etext = . ; + } + + .rodata : + { + _srodata = . ; + *(.rodata) + *(.rodata1) + *(.rodata.*) + *(.gnu.linkonce.r*) + _erodata = . ; + } + + .data : + { + _sdata = . ; + *(.data) + *(.data1) + *(.data.*) + *(.gnu.linkonce.d*) + . = ALIGN(4); + _edata = . ; + } + + /* C++ support. For each global and static local C++ object, + * GCC creates a small subroutine to construct the object. Pointers + * to these routines (not the routines themselves) are stored as + * simple, linear arrays in the .ctors section of the object file. + * Similarly, pointers to global/static destructor routines are + * stored in .dtors. + */ + + .ctors : + { + _sctors = . ; + *(.ctors) /* Old ABI: Unallocated */ + *(.init_array) /* New ABI: Allocated */ + _edtors = . ; + } + + .dtors : + { + _sdtors = . ; + *(.dtors) /* Old ABI: Unallocated */ + *(.fini_array) /* New ABI: Allocated */ + _edtors = . ; + } + + .bss : + { + _sbss = . ; + *(.bss) + *(.bss.*) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.b*) + *(COMMON) + _ebss = . ; + } + + /* Stabs debugging sections. */ + + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_info 0 : { *(.debug_info) } + .debug_line 0 : { *(.debug_line) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_aranges 0 : { *(.debug_aranges) } +} diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/ld.script b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/ld.script new file mode 100644 index 000000000..efd5c05ad --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/ld.script @@ -0,0 +1,100 @@ +/**************************************************************************** + * boards/risc-v/k210/edu-riscv64/scripts/ld.script + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Reg Access Start addr End addr Size + * MEM0 CPU w/ cache 0x80000000 - 0x803fffff : 4MB + * MEM1 CPU w/ cache 0x80400000 - 0x805fffff : 2MB + * MEM0 CPU w/o cache 0x40000000 - 0x403fffff : 4MB + * MEM1 CPU w/o cache 0x40400000 - 0x405fffff : 4MB + */ + +MEMORY +{ + progmem (rx) : ORIGIN = 0x80000000, LENGTH = 4096K /* w/ cache */ + sram (rwx) : ORIGIN = 0x80400000, LENGTH = 2048K /* w/ cache */ +} + +OUTPUT_ARCH("riscv") + +ENTRY(_stext) +EXTERN(_vectors) +SECTIONS +{ + .text : { + _stext = ABSOLUTE(.); + *(.vectors) + *(.text .text.*) + *(.fixup) + *(.gnu.warning) + *(.rodata .rodata.* .srodata .srodata.*) + *(.gnu.linkonce.t.*) + *(.glue_7) + *(.glue_7t) + *(.got) + *(.gcc_except_table) + *(.gnu.linkonce.r.*) + _etext = ABSOLUTE(.); + } > progmem + + .init_section : ALIGN(4) { + _sinit = ABSOLUTE(.); + KEEP(*(.init_array .init_array.*)) + _einit = ABSOLUTE(.); + } > progmem + + _eronly = ABSOLUTE(.); + + .data : ALIGN(4) { + _sdata = ABSOLUTE(.); + *(.data .data.*) + *(.sdata .sdata.* .sdata2.*) + *(.gnu.linkonce.d.*) + *(.gnu.linkonce.s.*) + CONSTRUCTORS + . = ALIGN(4); + _edata = ABSOLUTE(.); + } > sram AT > progmem + + .bss : ALIGN(4) { + _sbss = ABSOLUTE(.); + *(.bss .bss.*) + *(.sbss .sbss.*) + *(.gnu.linkonce.b.*) + *(.gnu.linkonce.sb.*) + *(COMMON) + . = ALIGN(32); + _ebss = ABSOLUTE(.); + } > sram + + /* Stabs debugging sections. */ + + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_info 0 : { *(.debug_info) } + .debug_line 0 : { *(.debug_line) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_aranges 0 : { *(.debug_aranges) } +} diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/memory.ld b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/memory.ld new file mode 100644 index 000000000..c6d9b605c --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/memory.ld @@ -0,0 +1,37 @@ +/**************************************************************************** + * boards/risc-v/k210/edu-riscv64/scripts/memory.ld + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Reg Access Start addr End addr Size + * MEM0 CPU w/ cache 0x80000000 - 0x803fffff : 4MB + * MEM1 CPU w/ cache 0x80400000 - 0x805fffff : 2MB + * MEM0 CPU w/o cache 0x40000000 - 0x403fffff : 4MB + * MEM1 CPU w/o cache 0x40400000 - 0x405fffff : 4MB + */ + +MEMORY +{ + kflash (rx) : ORIGIN = 0x80000000, LENGTH = 1024K /* w/ cache */ + uflash (rx) : ORIGIN = 0x80100000, LENGTH = 1024K /* w/ cache */ + xflash (rx) : ORIGIN = 0x80200000, LENGTH = 2048K /* w/ cache */ + + ksram (rwx) : ORIGIN = 0x80400000, LENGTH = 512K /* w/ cache */ + usram (rwx) : ORIGIN = 0x80480000, LENGTH = 512K /* w/ cache */ + xsram (rwx) : ORIGIN = 0x80500000, LENGTH = 1024K /* w/ cache */ +} diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/user-space.ld b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/user-space.ld new file mode 100644 index 000000000..0a899c79b --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/scripts/user-space.ld @@ -0,0 +1,94 @@ +/**************************************************************************** + * boards/risc-v/k210/edu-riscv64/scripts/user-space.ld + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* NOTE: This depends on the memory.ld script having been included prior to + * this script. + */ + +OUTPUT_ARCH("riscv") + +SECTIONS +{ + .userspace : { + *(.userspace) + } > uflash + + .text : { + _stext = ABSOLUTE(.); + *(.text .text.*) + *(.fixup) + *(.gnu.warning) + *(.rodata .rodata.*) + *(.gnu.linkonce.t.*) + *(.glue_7) + *(.glue_7t) + *(.got) + *(.gcc_except_table) + *(.gnu.linkonce.r.*) + _etext = ABSOLUTE(.); + } > uflash + + .init_section : { + _sinit = ABSOLUTE(.); + KEEP(*(.init_array .init_array.*)) + _einit = ABSOLUTE(.); + } > uflash + + __exidx_start = ABSOLUTE(.); + + __exidx_end = ABSOLUTE(.); + + _eronly = ABSOLUTE(.); + + .data : { + _sdata = ABSOLUTE(.); + *(.data .data.*) + *(.sdata .sdata.* .sdata2.*) + *(.gnu.linkonce.d.*) + CONSTRUCTORS + . = ALIGN(4); + _edata = ABSOLUTE(.); + } > usram AT > uflash + + .bss : { + _sbss = ABSOLUTE(.); + *(.bss .bss.*) + *(.sbss .sbss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _ebss = ABSOLUTE(.); + } > usram + + /* Stabs debugging sections */ + + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_info 0 : { *(.debug_info) } + .debug_line 0 : { *(.debug_line) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_aranges 0 : { *(.debug_aranges) } +} diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/Makefile b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/Makefile new file mode 100644 index 000000000..be1e64aa8 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/Makefile @@ -0,0 +1,65 @@ +############################################################################ +# boards/risc-v/k210/edu-riscv64/src/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(TOPDIR)/Make.defs + +CSRCS = k210_bringup.c k210_boot.c + +ifeq ($(CONFIG_BOARDCTL_RESET),y) +CSRCS += k210_reset.c +endif + +ifeq ($(CONFIG_BOARDCTL),y) +CSRCS += k210_appinit.c +endif + +ifeq ($(CONFIG_ARCH_LEDS),y) +CSRCS += k210_leds.c +endif + +ifeq ($(CONFIG_K210_LCD),y) +CSRCS += k210_lcd.c +endif + +ifeq ($(CONFIG_DEV_GPIO),y) +CSRCS += k210_gpio.c +endif + +ifeq ($(CONFIG_BSP_USING_CH438),y) +CSRCS += k210_ch438.c ch438_demo.c +endif + +ifeq ($(CONFIG_BSP_USING_CH376),y) +CSRCS += k210_ch376.c ch376_demo.c +endif + +ifeq ($(CONFIG_BSP_USING_ENET),y) +CSRCS += k210_w5500.c +endif + +ifeq ($(CONFIG_BSP_USING_TOUCH),y) +CSRCS += k210_touch.c +endif + +ifeq ($(CONFIG_BSP_USING_CAN),y) +CSRCS += can_demo.c +endif + +include $(TOPDIR)/boards/Board.mk diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/can_demo.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/can_demo.c new file mode 100644 index 000000000..390f92732 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/can_demo.c @@ -0,0 +1,147 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file can_demo.c + * @brief edu-riscv64 can_demo.c + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.11.10 + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "time.h" +#include +#include +#include +#include +#include +#include "k210_uart_16550.h" +#include "k210_fpioa.h" +#include "k210_gpiohs.h" +#include "k210_gpio_common.h" + +static int fd, flag=0; + +static void serial_thread_entry(void) +{ + uint8_t ch; + while(read(fd, &ch, 1) == 1) + { + printf("%02x ",ch); + } +} + +static void start_thread(void) +{ + int ret; + pthread_t thread; + pthread_attr_t attr = PTHREAD_ATTR_INITIALIZER; + attr.priority = 20; + attr.stacksize = 2048; + + ret = pthread_create(&thread, &attr, (void*)serial_thread_entry, NULL); + if (ret != 0) + { + printf("task create failed, status=%d\n", ret); + } + + flag = 1; +} + +static void set_baud(unsigned long speed) +{ + struct termios cfg; + tcgetattr(fd, &cfg); + cfsetspeed(&cfg, speed); + tcsetattr(fd, TCSANOW, &cfg); +} + +static void can_cfg_start(void) +{ + uint8_t cmd[8]; + set_baud(9600); + up_mdelay(1000); + + k210_gpiohs_set_direction(FPIOA_CAN_NCFG, GPIO_DM_OUTPUT); + k210_gpiohs_set_value(FPIOA_CAN_NCFG, GPIO_PV_LOW); + up_mdelay(200); + + cmd[0] = 0xAA; + cmd[1] = 0x55; + cmd[2] = 0xFD; + cmd[3] = 0x32; + cmd[4] = 0x01; + cmd[5] = 0x0B; + cmd[6] = 0xc4; + cmd[7] = 0x29; + write(fd, cmd, 8); +} + +static void can_cfg_end(void) +{ + k210_gpiohs_set_direction(FPIOA_CAN_NCFG, GPIO_DM_OUTPUT); + k210_gpiohs_set_value(FPIOA_CAN_NCFG, GPIO_PV_HIGH); + set_baud(115200); +} + +void can_test(void) +{ + uint8_t msg[8]; + uint8_t i; + + fd = open("/dev/ttyS1", O_RDWR); + if (flag == 0) + { + /* 1、start thread */ + start_thread(); + up_mdelay(20); + + /* 2、config can prama */ + can_cfg_start(); + up_mdelay(20); + + /* 3、exit config */ + can_cfg_end(); + up_mdelay(20);; + } + + /* 4、send data */ + for(i=0;i<10;i++) + { + msg[0] = 0x11; + msg[1] = 0x22; + msg[2] = 0x33; + msg[3] = 0x44; + msg[4] = 0x55; + msg[5] = 0x66; + msg[6] = 0x77; + msg[7] = 0x99; + write(fd, msg, 8); + up_mdelay(20); + } +} \ No newline at end of file diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/ch376_demo.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/ch376_demo.c new file mode 100644 index 000000000..ceb18d53f --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/ch376_demo.c @@ -0,0 +1,70 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file ch376_demo.c + * @brief edu-riscv64 ch376_demo.c + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.10.11 + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ +#include "k210_ch376.h" + +uint8_t buf[64]; + +void CH376Demo(void) +{ + uint8_t s; + s = mInitCH376Host(); + printf ("ch376 init stat=0x%02x\n",(uint16_t)s); + +#ifdef CONFIG_CH376_USB_FUNCTION + printf( "Wait Udisk/SD\n" ); + while ( CH376DiskConnect( ) != USB_INT_SUCCESS ) + { + up_mdelay( 100 ); + } +#endif + + for ( s = 0; s < 10; s ++ ) + { + up_mdelay( 50 ); + printf( "Ready ?\n" ); + if ( CH376DiskMount( ) == USB_INT_SUCCESS ) break; + } + s = CH376ReadBlock( buf ); + if ( s == sizeof( INQUIRY_DATA ) ) + { + buf[ s ] = 0; + printf( "UdiskInfo: %s\n", ((P_INQUIRY_DATA)buf) -> VendorIdStr ); + } + + printf( "Create /YEAR2022/DEMO2022.TXT \n" ); + s = CH376DirCreate((PUINT8)"/YEAR2022" ); + printf("CH376DirCreate:0x%02x\n",(uint16_t)s ); + + s = CH376FileCreatePath((PUINT8)"/YEAR2022/DEMO2022.TXT" ); + printf( "CH376FileCreatePath:0x%02x\n",(uint16_t)s ); + + printf( "Write some data to file\n" ); + strcpy( (char *)buf, "This is test case!\xd\xa" ); + s = CH376ByteWrite(buf, strlen((char *)buf), NULL ); + printf( "CH376ByteWrite:0x%02x\n",(uint16_t)s ); + + printf( "Close\n" ); + s = CH376FileClose( TRUE ); + printf( "CH376FileClose:0x%02x\n",(uint16_t)s ); +} diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/ch376inc.h b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/ch376inc.h new file mode 100644 index 000000000..53c277f99 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/ch376inc.h @@ -0,0 +1,584 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file ch376inc.h + * @brief edu-riscv64 ch376inc.h + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.10.10 + */ + +#ifndef __CH376INC_H__ +#define __CH376INC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Common types and constant definitions */ + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif +#ifndef NULL +#define NULL 0 +#endif + +#ifndef UINT8 +typedef unsigned char UINT8; +#endif +#ifndef UINT16 +typedef unsigned short UINT16; +#endif +#ifndef UINT32 +typedef unsigned long UINT32; +#endif +#ifndef PUINT8 +typedef unsigned char *PUINT8; +#endif +#ifndef PUINT16 +typedef unsigned short *PUINT16; +#endif +#ifndef PUINT32 +typedef unsigned long *PUINT32; +#endif +#ifndef UINT8V +typedef unsigned char volatile UINT8V; +#endif +#ifndef PUINT8V +typedef unsigned char volatile *PUINT8V; +#endif + +#define CH376_DAT_BLOCK_LEN 0x40 +#define CMD01_GET_IC_VER 0x01 +#define CMD21_SET_BAUDRATE 0x02 +#define CMD00_ENTER_SLEEP 0x03 +#define CMD00_RESET_ALL 0x05 +#define CMD11_CHECK_EXIST 0x06 +#define CMD20_CHK_SUSPEND 0x0B +#define CMD20_SET_SDO_INT 0x0B +#define CMD14_GET_FILE_SIZE 0x0C +#define CMD50_SET_FILE_SIZE 0x0D +#define CMD11_SET_USB_MODE 0x15 +#define CMD01_GET_STATUS 0x22 +#define CMD00_UNLOCK_USB 0x23 +#define CMD01_RD_USB_DATA0 0x27 +#define CMD01_RD_USB_DATA 0x28 +#define CMD10_WR_USB_DATA7 0x2B +#define CMD10_WR_HOST_DATA 0x2C +#define CMD01_WR_REQ_DATA 0x2D +#define CMD20_WR_OFS_DATA 0x2E +#define CMD10_SET_FILE_NAME 0x2F +#define CMD0H_DISK_CONNECT 0x30 +#define CMD0H_DISK_MOUNT 0x31 +#define CMD0H_FILE_OPEN 0x32 +#define CMD0H_FILE_ENUM_GO 0x33 +#define CMD0H_FILE_CREATE 0x34 +#define CMD0H_FILE_ERASE 0x35 +#define CMD1H_FILE_CLOSE 0x36 +#define CMD1H_DIR_INFO_READ 0x37 +#define CMD0H_DIR_INFO_SAVE 0x38 +#define CMD4H_BYTE_LOCATE 0x39 +#define CMD2H_BYTE_READ 0x3A +#define CMD0H_BYTE_RD_GO 0x3B +#define CMD2H_BYTE_WRITE 0x3C +#define CMD0H_BYTE_WR_GO 0x3D +#define CMD0H_DISK_CAPACITY 0x3E +#define CMD0H_DISK_QUERY 0x3F +#define CMD0H_DIR_CREATE 0x40 +#define CMD4H_SEC_LOCATE 0x4A +#define CMD1H_SEC_READ 0x4B +#define CMD1H_SEC_WRITE 0x4C +#define CMD0H_DISK_BOC_CMD 0x50 +#define CMD5H_DISK_READ 0x54 +#define CMD0H_DISK_RD_GO 0x55 +#define CMD5H_DISK_WRITE 0x56 +#define CMD0H_DISK_WR_GO 0x57 +#define CMD10_SET_USB_SPEED 0x04 +#define CMD11_GET_DEV_RATE 0x0A +#define CMD11_GET_TOGGLE 0x0A +#define CMD11_READ_VAR8 0x0A +#define CMD20_SET_RETRY 0x0B +#define CMD20_WRITE_VAR8 0x0B +#define CMD14_READ_VAR32 0x0C +#define CMD50_WRITE_VAR32 0x0D +#define CMD01_DELAY_100US 0x0F +#define CMD40_SET_USB_ID 0x12 +#define CMD10_SET_USB_ADDR 0x13 +#define CMD01_TEST_CONNECT 0x16 +#define CMD00_ABORT_NAK 0x17 +#define CMD10_SET_ENDP2 0x18 +#define CMD10_SET_ENDP3 0x19 +#define CMD10_SET_ENDP4 0x1A +#define CMD10_SET_ENDP5 0x1B +#define CMD10_SET_ENDP6 0x1C +#define CMD10_SET_ENDP7 0x1D +#define CMD00_DIRTY_BUFFER 0x25 +#define CMD10_WR_USB_DATA3 0x29 +#define CMD10_WR_USB_DATA5 0x2A +#define CMD1H_CLR_STALL 0x41 +#define CMD1H_SET_ADDRESS 0x45 +#define CMD1H_GET_DESCR 0x46 +#define CMD1H_SET_CONFIG 0x49 +#define CMD0H_AUTO_SETUP 0x4D +#define CMD2H_ISSUE_TKN_X 0x4E +#define CMD1H_ISSUE_TOKEN 0x4F +#define CMD0H_DISK_INIT 0x51 +#define CMD0H_DISK_RESET 0x52 +#define CMD0H_DISK_SIZE 0x53 +#define CMD0H_DISK_INQUIRY 0x58 +#define CMD0H_DISK_READY 0x59 +#define CMD0H_DISK_R_SENSE 0x5A +#define CMD0H_RD_DISK_SEC 0x5B +#define CMD0H_WR_DISK_SEC 0x5C +#define CMD0H_DISK_MAX_LUN 0x5D + +/* The following definitions are only for compatibility with the command name format in the INCLUDE file of CH375 */ + +#ifndef _NO_CH375_COMPATIBLE_ +#define CMD_GET_IC_VER CMD01_GET_IC_VER +#define CMD_SET_BAUDRATE CMD21_SET_BAUDRATE +#define CMD_ENTER_SLEEP CMD00_ENTER_SLEEP +#define CMD_RESET_ALL CMD00_RESET_ALL +#define CMD_CHECK_EXIST CMD11_CHECK_EXIST +#define CMD_CHK_SUSPEND CMD20_CHK_SUSPEND +#define CMD_SET_SDO_INT CMD20_SET_SDO_INT +#define CMD_GET_FILE_SIZE CMD14_GET_FILE_SIZE +#define CMD_SET_FILE_SIZE CMD50_SET_FILE_SIZE +#define CMD_SET_USB_MODE CMD11_SET_USB_MODE +#define CMD_GET_STATUS CMD01_GET_STATUS +#define CMD_UNLOCK_USB CMD00_UNLOCK_USB +#define CMD_RD_USB_DATA0 CMD01_RD_USB_DATA0 +#define CMD_RD_USB_DATA CMD01_RD_USB_DATA +#define CMD_WR_USB_DATA7 CMD10_WR_USB_DATA7 +#define CMD_WR_HOST_DATA CMD10_WR_HOST_DATA +#define CMD_WR_REQ_DATA CMD01_WR_REQ_DATA +#define CMD_WR_OFS_DATA CMD20_WR_OFS_DATA +#define CMD_SET_FILE_NAME CMD10_SET_FILE_NAME +#define CMD_DISK_CONNECT CMD0H_DISK_CONNECT +#define CMD_DISK_MOUNT CMD0H_DISK_MOUNT +#define CMD_FILE_OPEN CMD0H_FILE_OPEN +#define CMD_FILE_ENUM_GO CMD0H_FILE_ENUM_GO +#define CMD_FILE_CREATE CMD0H_FILE_CREATE +#define CMD_FILE_ERASE CMD0H_FILE_ERASE +#define CMD_FILE_CLOSE CMD1H_FILE_CLOSE +#define CMD_DIR_INFO_READ CMD1H_DIR_INFO_READ +#define CMD_DIR_INFO_SAVE CMD0H_DIR_INFO_SAVE +#define CMD_BYTE_LOCATE CMD4H_BYTE_LOCATE +#define CMD_BYTE_READ CMD2H_BYTE_READ +#define CMD_BYTE_RD_GO CMD0H_BYTE_RD_GO +#define CMD_BYTE_WRITE CMD2H_BYTE_WRITE +#define CMD_BYTE_WR_GO CMD0H_BYTE_WR_GO +#define CMD_DISK_CAPACITY CMD0H_DISK_CAPACITY +#define CMD_DISK_QUERY CMD0H_DISK_QUERY +#define CMD_DIR_CREATE CMD0H_DIR_CREATE +#define CMD_SEC_LOCATE CMD4H_SEC_LOCATE +#define CMD_SEC_READ CMD1H_SEC_READ +#define CMD_SEC_WRITE CMD1H_SEC_WRITE +#define CMD_DISK_BOC_CMD CMD0H_DISK_BOC_CMD +#define CMD_DISK_READ CMD5H_DISK_READ +#define CMD_DISK_RD_GO CMD0H_DISK_RD_GO +#define CMD_DISK_WRITE CMD5H_DISK_WRITE +#define CMD_DISK_WR_GO CMD0H_DISK_WR_GO +#define CMD_SET_USB_SPEED CMD10_SET_USB_SPEED +#define CMD_GET_DEV_RATE CMD11_GET_DEV_RATE +#define CMD_GET_TOGGLE CMD11_GET_TOGGLE +#define CMD_READ_VAR8 CMD11_READ_VAR8 +#define CMD_SET_RETRY CMD20_SET_RETRY +#define CMD_WRITE_VAR8 CMD20_WRITE_VAR8 +#define CMD_READ_VAR32 CMD14_READ_VAR32 +#define CMD_WRITE_VAR32 CMD50_WRITE_VAR32 +#define CMD_DELAY_100US CMD01_DELAY_100US +#define CMD_SET_USB_ID CMD40_SET_USB_ID +#define CMD_SET_USB_ADDR CMD10_SET_USB_ADDR +#define CMD_TEST_CONNECT CMD01_TEST_CONNECT +#define CMD_ABORT_NAK CMD00_ABORT_NAK +#define CMD_SET_ENDP2 CMD10_SET_ENDP2 +#define CMD_SET_ENDP3 CMD10_SET_ENDP3 +#define CMD_SET_ENDP4 CMD10_SET_ENDP4 +#define CMD_SET_ENDP5 CMD10_SET_ENDP5 +#define CMD_SET_ENDP6 CMD10_SET_ENDP6 +#define CMD_SET_ENDP7 CMD10_SET_ENDP7 +#define CMD_DIRTY_BUFFER CMD00_DIRTY_BUFFER +#define CMD_WR_USB_DATA3 CMD10_WR_USB_DATA3 +#define CMD_WR_USB_DATA5 CMD10_WR_USB_DATA5 +#define CMD_CLR_STALL CMD1H_CLR_STALL +#define CMD_SET_ADDRESS CMD1H_SET_ADDRESS +#define CMD_GET_DESCR CMD1H_GET_DESCR +#define CMD_SET_CONFIG CMD1H_SET_CONFIG +#define CMD_AUTO_SETUP CMD0H_AUTO_SETUP +#define CMD_ISSUE_TKN_X CMD2H_ISSUE_TKN_X +#define CMD_ISSUE_TOKEN CMD1H_ISSUE_TOKEN +#define CMD_DISK_INIT CMD0H_DISK_INIT +#define CMD_DISK_RESET CMD0H_DISK_RESET +#define CMD_DISK_SIZE CMD0H_DISK_SIZE +#define CMD_DISK_INQUIRY CMD0H_DISK_INQUIRY +#define CMD_DISK_READY CMD0H_DISK_READY +#define CMD_DISK_R_SENSE CMD0H_DISK_R_SENSE +#define CMD_RD_DISK_SEC CMD0H_RD_DISK_SEC +#define CMD_WR_DISK_SEC CMD0H_WR_DISK_SEC +#define CMD_DISK_MAX_LUN CMD0H_DISK_MAX_LUN +#endif + +/* ********************************************************************************************************************* */ +#ifndef PARA_STATE_INTB +#define PARA_STATE_INTB 0x80 +#define PARA_STATE_BUSY 0x10 +#endif + +#ifndef SER_CMD_TIMEOUT +#define SER_CMD_TIMEOUT 32 +#define SER_SYNC_CODE1 0x57 +#define SER_SYNC_CODE2 0xAB +#endif + +#ifndef CMD_RET_SUCCESS +#define CMD_RET_SUCCESS 0x51 +#define CMD_RET_ABORT 0x5F +#endif + +/***********************************************************************************************************************/ +#ifndef USB_INT_EP0_SETUP + +#define USB_INT_USB_SUSPEND 0x05 +#define USB_INT_WAKE_UP 0x06 +#define USB_INT_EP0_SETUP 0x0C +#define USB_INT_EP0_OUT 0x00 +#define USB_INT_EP0_IN 0x08 +#define USB_INT_EP1_OUT 0x01 +#define USB_INT_EP1_IN 0x09 +#define USB_INT_EP2_OUT 0x02 +#define USB_INT_EP2_IN 0x0A +#define USB_INT_BUS_RESET1 0x03 +#define USB_INT_BUS_RESET2 0x07 +#define USB_INT_BUS_RESET3 0x0B +#define USB_INT_BUS_RESET4 0x0F + +#endif + + +#ifndef USB_INT_SUCCESS +#define USB_INT_SUCCESS 0x14 +#define USB_INT_CONNECT 0x15 +#define USB_INT_DISCONNECT 0x16 +#define USB_INT_BUF_OVER 0x17 +#define USB_INT_USB_READY 0x18 +#define USB_INT_DISK_READ 0x1D +#define USB_INT_DISK_WRITE 0x1E +#define USB_INT_DISK_ERR 0x1F +#endif + +#ifndef ERR_DISK_DISCON +#define ERR_DISK_DISCON 0x82 +#define ERR_LARGE_SECTOR 0x84 +#define ERR_TYPE_ERROR 0x92 +#define ERR_BPB_ERROR 0xA1 +#define ERR_DISK_FULL 0xB1 +#define ERR_FDT_OVER 0xB2 +#define ERR_FILE_CLOSE 0xB4 +#define ERR_OPEN_DIR 0x41 +#define ERR_MISS_FILE 0x42 +#define ERR_FOUND_NAME 0x43 + + +#define ERR_MISS_DIR 0xB3 +#define ERR_LONG_BUF_OVER 0x48 +#define ERR_LONG_NAME_ERR 0x49 +#define ERR_NAME_EXIST 0x4A +#endif + +/* ******************************************************************************************************************** */ +#ifndef DEF_DISK_UNKNOWN +#define DEF_DISK_UNKNOWN 0x00 +#define DEF_DISK_DISCONN 0x01 +#define DEF_DISK_CONNECT 0x02 +#define DEF_DISK_MOUNTED 0x03 +#define DEF_DISK_READY 0x10 +#define DEF_DISK_OPEN_ROOT 0x12 +#define DEF_DISK_OPEN_DIR 0x13 +#define DEF_DISK_OPEN_FILE 0x14 +#endif + +/* **********************************************************************************************************************/ +#ifndef DEF_SECTOR_SIZE +#define DEF_SECTOR_SIZE 512 +#endif + +#ifndef DEF_WILDCARD_CHAR +#define DEF_WILDCARD_CHAR 0x2A +#define DEF_SEPAR_CHAR1 0x5C +#define DEF_SEPAR_CHAR2 0x2F +#define DEF_FILE_YEAR 2004 +#define DEF_FILE_MONTH 1 +#define DEF_FILE_DATE 1 +#endif + +#ifndef ATTR_DIRECTORY + +typedef struct _FAT_DIR_INFO { + UINT8 DIR_Name[11]; + UINT8 DIR_Attr; + UINT8 DIR_NTRes; + UINT8 DIR_CrtTimeTenth; + UINT16 DIR_CrtTime; + UINT16 DIR_CrtDate; + UINT16 DIR_LstAccDate; + UINT16 DIR_FstClusHI; + UINT16 DIR_WrtTime; + UINT16 DIR_WrtDate; + UINT16 DIR_FstClusLO; + UINT32 DIR_FileSize; +} FAT_DIR_INFO, *P_FAT_DIR_INFO; + +#define ATTR_READ_ONLY 0x01 +#define ATTR_HIDDEN 0x02 +#define ATTR_SYSTEM 0x04 +#define ATTR_VOLUME_ID 0x08 +#define ATTR_DIRECTORY 0x10 +#define ATTR_ARCHIVE 0x20 +#define ATTR_LONG_NAME (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID) +#define ATTR_LONG_NAME_MASK (ATTR_LONG_NAME | ATTR_DIRECTORY | ATTR_ARCHIVE) + +#define MAKE_FILE_TIME( h, m, s ) ( (h<<11) + (m<<5) + (s>>1) ) +#define MAKE_FILE_DATE( y, m, d ) ( ((y-1980)<<9) + (m<<5) + d ) + +#define LONE_NAME_MAX_CHAR (255*2) +#define LONG_NAME_PER_DIR (13*2) + +#endif + +/* ********************************************************************************************************************* */ +#ifndef SPC_CMD_INQUIRY + +#define SPC_CMD_INQUIRY 0x12 +#define SPC_CMD_READ_CAPACITY 0x25 +#define SPC_CMD_READ10 0x28 +#define SPC_CMD_WRITE10 0x2A +#define SPC_CMD_TEST_READY 0x00 +#define SPC_CMD_REQUEST_SENSE 0x03 +#define SPC_CMD_MODESENSE6 0x1A +#define SPC_CMD_MODESENSE10 0x5A +#define SPC_CMD_START_STOP 0x1B + +typedef struct _BULK_ONLY_CBW { + UINT32 CBW_Sig; + UINT32 CBW_Tag; + UINT8 CBW_DataLen0; + UINT8 CBW_DataLen1; + UINT16 CBW_DataLen2; + UINT8 CBW_Flag; + UINT8 CBW_LUN; + UINT8 CBW_CB_Len; + UINT8 CBW_CB_Buf[16]; +} BULK_ONLY_CBW, *P_BULK_ONLY_CBW; + +typedef struct _INQUIRY_DATA { + UINT8 DeviceType; + UINT8 RemovableMedia; + UINT8 Versions; + UINT8 DataFormatAndEtc; + UINT8 AdditionalLength; + UINT8 Reserved1; + UINT8 Reserved2; + UINT8 MiscFlag; + UINT8 VendorIdStr[8]; + UINT8 ProductIdStr[16]; + UINT8 ProductRevStr[4]; +} INQUIRY_DATA, *P_INQUIRY_DATA; + + +typedef struct _SENSE_DATA { + UINT8 ErrorCode; + UINT8 SegmentNumber; + UINT8 SenseKeyAndEtc; + UINT8 Information0; + UINT8 Information1; + UINT8 Information2; + UINT8 Information3; + UINT8 AdditSenseLen; + UINT8 CmdSpecInfo[4]; + UINT8 AdditSenseCode; + UINT8 AddSenCodeQual; + UINT8 FieldReplaUnit; + UINT8 SenseKeySpec[3]; +} SENSE_DATA, *P_SENSE_DATA; + +#endif + +/* ********************************************************************************************************************* */ +#ifndef MAX_FILE_NAME_LEN + +#define MAX_FILE_NAME_LEN (13+1) + +typedef union _CH376_CMD_DATA { + struct { + UINT8 mBuffer[ MAX_FILE_NAME_LEN ]; + } Default; + + INQUIRY_DATA DiskMountInq; + FAT_DIR_INFO OpenDirInfo; + FAT_DIR_INFO EnumDirInfo; + struct { + UINT8 mUpdateFileSz; + } FileCLose; + + struct { + UINT8 mDirInfoIndex; + } DirInfoRead; + + union { + UINT32 mByteOffset; + UINT32 mSectorLba; + } ByteLocate; + + struct { + UINT16 mByteCount; + } ByteRead; + + struct { + UINT16 mByteCount; + } ByteWrite; + + union { + UINT32 mSectorOffset; + UINT32 mSectorLba; + } SectorLocate; + + struct { + UINT8 mSectorCount; + UINT8 mReserved1; + UINT8 mReserved2; + UINT8 mReserved3; + UINT32 mStartSector; + } SectorRead; + + struct { + UINT8 mSectorCount; + UINT8 mReserved1; + UINT8 mReserved2; + UINT8 mReserved3; + UINT32 mStartSector; +} SectorWrite; + + struct { + UINT32 mDiskSizeSec; + } DiskCapacity; + + struct { + UINT32 mTotalSector; + UINT32 mFreeSector; + UINT8 mDiskFat; + } DiskQuery; + + BULK_ONLY_CBW DiskBocCbw; + + struct { + UINT8 mMaxLogicUnit; + } DiskMaxLun; + + INQUIRY_DATA DiskInitInq; + INQUIRY_DATA DiskInqData; + SENSE_DATA ReqSenseData; + struct { + UINT32 mDiskSizeSec; + } DiskSize; + + struct { + UINT32 mStartSector; + UINT8 mSectorCount; + } DiskRead; + + struct { + UINT32 mStartSector; + UINT8 mSectorCount; + } DiskWrite; +} CH376_CMD_DATA, *P_CH376_CMD_DATA; + +#endif + +/* ********************************************************************************************************************* */ + +#ifndef VAR_FILE_SIZE + +#define VAR_SYS_BASE_INFO 0x20 +#define VAR_RETRY_TIMES 0x25 +#define VAR_FILE_BIT_FLAG 0x26 +#define VAR_DISK_STATUS 0x2B +#define VAR_SD_BIT_FLAG 0x30 +#define VAR_UDISK_TOGGLE 0x31 +#define VAR_UDISK_LUN 0x34 +#define VAR_SEC_PER_CLUS 0x38 +#define VAR_FILE_DIR_INDEX 0x3B +#define VAR_CLUS_SEC_OFS 0x3C + +#define VAR_DISK_ROOT 0x44 +#define VAR_DSK_TOTAL_CLUS 0x48 +#define VAR_DSK_START_LBA 0x4C +#define VAR_DSK_DAT_START 0x50 +#define VAR_LBA_BUFFER 0x54 +#define VAR_LBA_CURRENT 0x58 +#define VAR_FAT_DIR_LBA 0x5C +#define VAR_START_CLUSTER 0x60 +#define VAR_CURRENT_CLUST 0x64 +#define VAR_FILE_SIZE 0x68 +#define VAR_CURRENT_OFFSET 0x6C + +#endif + +/* ********************************************************************************************************************* */ +#ifndef DEF_USB_PID_SETUP +#define DEF_USB_PID_NULL 0x00 +#define DEF_USB_PID_SOF 0x05 +#define DEF_USB_PID_SETUP 0x0D +#define DEF_USB_PID_IN 0x09 +#define DEF_USB_PID_OUT 0x01 +#define DEF_USB_PID_ACK 0x02 +#define DEF_USB_PID_NAK 0x0A +#define DEF_USB_PID_STALL 0x0E +#define DEF_USB_PID_DATA0 0x03 +#define DEF_USB_PID_DATA1 0x0B +#define DEF_USB_PID_PRE 0x0C +#endif + +#ifndef DEF_USB_REQ_TYPE +#define DEF_USB_REQ_READ 0x80 +#define DEF_USB_REQ_WRITE 0x00 +#define DEF_USB_REQ_TYPE 0x60 +#define DEF_USB_REQ_STAND 0x00 +#define DEF_USB_REQ_CLASS 0x20 +#define DEF_USB_REQ_VENDOR 0x40 +#define DEF_USB_REQ_RESERVE 0x60 +#endif + +#ifndef DEF_USB_GET_DESCR +#define DEF_USB_CLR_FEATURE 0x01 +#define DEF_USB_SET_FEATURE 0x03 +#define DEF_USB_GET_STATUS 0x00 +#define DEF_USB_SET_ADDRESS 0x05 +#define DEF_USB_GET_DESCR 0x06 +#define DEF_USB_SET_DESCR 0x07 +#define DEF_USB_GET_CONFIG 0x08 +#define DEF_USB_SET_CONFIG 0x09 +#define DEF_USB_GET_INTERF 0x0A +#define DEF_USB_SET_INTERF 0x0B +#define DEF_USB_SYNC_FRAME 0x0C +#endif + +/* ********************************************************************************************************************* */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/ch438_demo.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/ch438_demo.c new file mode 100644 index 000000000..285a2d009 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/ch438_demo.c @@ -0,0 +1,84 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file ch438_demo.c + * @brief edu-riscv64 ch438_demo.c + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.03.17 + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ +#include "k210_ch438.h" +#include +#include + +void CH438Demo(void) +{ + int fd, m0fd, m1fd; + int i; + char sendbuffer1[4] = {0xC0,0x04,0x01,0x09}; + char sendbuffer2[6] = {0xC0,0x00,0x03,0x12,0x34,0x61}; + char sendbuffer3[3] = {0xC1,0x04,0x01}; + char sendbuffer4[3] = {0xC1,0x00,0x03}; + char buffer[256]; + int readlen; + + fd = open("/dev/extuart_dev3", O_RDWR); + ioctl(fd, OPE_INT, (unsigned long)9600); + m0fd = open("/dev/gpio0", O_RDWR); + m1fd = open("/dev/gpio1", O_RDWR); + ioctl(m0fd, GPIOC_WRITE, (unsigned long)1); + ioctl(m1fd, GPIOC_WRITE, (unsigned long)1); + sleep(1); + + write(fd, sendbuffer1,4); + sleep(1); + readlen = read(fd, buffer, 256); + printf("readlen1 = %d\n", readlen); + for(i = 0;i< readlen; ++i) + { + printf("0x%x\n", buffer[i]); + } + + write(fd, sendbuffer2,6); + sleep(1); + readlen = read(fd, buffer, 256); + printf("readlen1 = %d\n", readlen); + for(i = 0;i< readlen; ++i) + { + printf("0x%x\n", buffer[i]); + } + + write(fd, sendbuffer3,3); + sleep(1); + readlen = read(fd, buffer, 256); + printf("readlen1 = %d\n", readlen); + for(i = 0;i< readlen; ++i) + { + printf("0x%x\n", buffer[i]); + } + + write(fd, sendbuffer4,3); + sleep(1); + readlen = read(fd, buffer, 256); + printf("readlen1 = %d\n", readlen); + for(i = 0;i< readlen; ++i) + { + printf("0x%x\n", buffer[i]); + } + + close(fd); +} diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/edu-riscv64.h b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/edu-riscv64.h new file mode 100644 index 000000000..3403bc946 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/edu-riscv64.h @@ -0,0 +1,36 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file edu-riscv64.h + * @brief edu-riscv64 edu-riscv64.h + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.03.17 + */ + +#ifndef __BOARDS_RISCV_K210_EDU_RISCV64_SRC_EDU_RISCV64_H +#define __BOARDS_RISCV_K210_EDU_RISCV64_SRC_EDU_RISCV64_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +int k210_bringup(void); + +#ifdef CONFIG_DEV_GPIO +int k210_gpio_init(void); +#endif + +#endif /* __BOARDS_RISCV_K210_EDU_RISCV64_SRC_EDU_RISCV64_H */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_appinit.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_appinit.c new file mode 100644 index 000000000..ce8f9c6f3 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_appinit.c @@ -0,0 +1,76 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file k210_appinit.c + * @brief edu-riscv64 k210_appinit.c + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.03.17 + */ + + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include + +#include "k210.h" +#include "edu-riscv64.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_app_initialize + * + * Description: + * Perform architecture specific initialization + * + * Input Parameters: + * arg - The boardctl() argument is passed to the board_app_initialize() + * implementation without modification. The argument has no + * meaning to NuttX; the meaning of the argument is a contract + * between the board-specific initialization logic and the + * matching application logic. The value could be such things as a + * mode enumeration value, a set of DIP switch switch settings, a + * pointer to configuration data read from a file or serial FLASH, + * or whatever you would like to do with it. Every implementation + * should accept zero/NULL as a default configuration. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure to indicate the nature of the failure. + * + ****************************************************************************/ + +int board_app_initialize(uintptr_t arg) +{ +#ifdef CONFIG_BOARD_LATE_INITIALIZE + /* Board initialization already performed by board_late_initialize() */ + + return OK; +#else + /* Perform board-specific initialization */ + + return k210_bringup(); +#endif +} diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_boot.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_boot.c new file mode 100644 index 000000000..5d89d5e26 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_boot.c @@ -0,0 +1,59 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file k210_boot.c + * @brief edu-riscv64 k210_boot.c + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.03.17 + */ + + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: k210_boardinitialize + * + * Description: + * All K210 architectures must provide the following entry point. + * This entry point is called early in the initialization -- after all + * memory has been configured and mapped but before any devices have been + * initialized. + * + ****************************************************************************/ + +void k210_boardinitialize(void) +{ + board_autoled_initialize(); +} diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_bringup.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_bringup.c new file mode 100644 index 000000000..1bfee36c4 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_bringup.c @@ -0,0 +1,141 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file k210_bringup.c + * @brief edu-riscv64 k210_bringup.c + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.03.17 + */ + + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "k210.h" +#include "k210_clockconfig.h" +#include "edu-riscv64.h" +#include +#include "k210_sysctl.h" +#include "k210_fpioa.h" +#include "k210_gpiohs.h" +#include "k210_gpio_common.h" + +#ifdef CONFIG_BSP_USING_CH438 +# include "k210_ch438.h" +#endif + +#ifdef CONFIG_BSP_USING_TOUCH +# include "k210_touch.h" +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: k210_bringup + ****************************************************************************/ + +int k210_bringup(void) +{ + int ret = OK; + +#ifdef CONFIG_FS_PROCFS + /* Mount the procfs file system */ + + ret = nx_mount(NULL, "/proc", "procfs", 0, NULL); + if (ret < 0) + { + serr("ERROR: Failed to mount procfs at %s: %d\n", "/proc", ret); + } +#endif + +#ifdef CONFIG_DEV_GPIO + ret = k210_gpio_init(); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize GPIO Driver: %d\n", ret); + return ret; + } +#endif + +#ifdef CONFIG_BSP_USING_CH438 + board_ch438_initialize(); +#endif + +#ifdef CONFIG_K210_LCD + k210_sysctl_init(); + board_lcd_initialize(); +#endif + +#ifdef CONFIG_BSP_USING_TOUCH + board_touch_initialize(); +#endif + +#ifdef CONFIG_K210_16550_UART1 +#ifdef CONFIG_ADAPTER_ESP8285_WIFI + sysctl_clock_enable(SYSCTL_CLOCK_UART1); + sysctl_reset(SYSCTL_RESET_UART1); + + fpioa_set_function(GPIO_WIFI_TXD, FPOA_USART1_RX); + fpioa_set_function(GPIO_WIFI_RXD, FPOA_USART1_TX); + + fpioa_set_function(GPIO_WIFI_EN, K210_IO_FUNC_GPIOHS0 + FPIOA_WIFI_EN); + k210_gpiohs_set_direction(FPIOA_WIFI_EN, GPIO_DM_OUTPUT); + k210_gpiohs_set_value(FPIOA_WIFI_EN, GPIO_PV_LOW); + up_mdelay(50); + k210_gpiohs_set_value(FPIOA_WIFI_EN, GPIO_PV_HIGH); +#endif + +#ifdef CONFIG_BSP_USING_CAN + sysctl_clock_enable(SYSCTL_CLOCK_UART1); + sysctl_reset(SYSCTL_RESET_UART1); + + fpioa_set_function(GPIO_CAN_TXD, FPOA_USART1_TX); + fpioa_set_function(GPIO_CAN_RXD, FPOA_USART1_RX); + + k210_fpioa_config(GPIO_CAN_CFG, HS_GPIO(FPIOA_CAN_NCFG) | K210_IOFLAG_GPIOHS); +#endif +#endif + +#ifdef CONFIG_K210_16550_UART2 + sysctl_clock_enable(SYSCTL_CLOCK_UART2); + sysctl_reset(SYSCTL_RESET_UART2); + + fpioa_set_function(GPIO_EC200T_RXD, FPOA_USART2_RX); + fpioa_set_function(GPIO_EC200T_TXD, FPOA_USART2_TX); +#endif + +#ifdef CONFIG_K210_16550_UART3 + sysctl_clock_enable(SYSCTL_CLOCK_UART3); + sysctl_reset(SYSCTL_RESET_UART3); + + fpioa_set_function(GPIO_CH376T_RXD, FPOA_USART3_RX); + fpioa_set_function(GPIO_CH376T_TXD, FPOA_USART3_TX); +#endif + + return ret; +} diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch376.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch376.c new file mode 100644 index 000000000..fe4e04276 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch376.c @@ -0,0 +1,960 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file k210_ch376.c + * @brief edu-riscv64 k210_ch376.c + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.10.10 + */ + + +#if 0 +#define DEF_IC_V43_U 1 +#endif + +#include "k210_ch376.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ +static int fd; + +/* Serial port mode is not used */ +void xEndCH376Cmd(void) +{ +} + +void xWriteCH376Cmd(UINT8 cmd) +{ + UINT8 temp[3]; + temp[0] = 0x57; + temp[1] = 0xab; + temp[2] = cmd; + up_udelay(5); + write(fd, temp, 3); +} + +void xWriteCH376Data(UINT8 dat) +{ + UINT8 tmp = dat; + write(fd, &tmp, 1); + up_udelay(2); +} + +UINT8 xReadCH376Data(void) +{ + UINT32 i; + UINT8 data; + int res; + for(i=0;i<500000;i++) + { + res = read(fd, &data, 1); + if(res == 1) + { + return ((UINT8)data); + } + up_udelay(1); + } + return ERR_USB_UNKNOWN; +} + +UINT8 CH376ReadBlock(PUINT8 buf) +{ + UINT8 s, l; + xWriteCH376Cmd( CMD01_RD_USB_DATA0 ); + s = l = xReadCH376Data( ); + if ( l ) { + do { + *buf = xReadCH376Data( ); + buf ++; + } while ( -- l ); + } + xEndCH376Cmd( ); + return( s ); +} + +UINT8 Query376Interrupt(void) +{ + //When an interrupt occurs, the serial port will receive a data, read it directly, and discard it + if(xReadCH376Data() == ERR_USB_UNKNOWN) return FALSE ; + else return TRUE ; +} + +/* CH376 INIT */ +UINT8 mInitCH376Host(void) +{ + UINT8 res; + /* After power on, delay operation for at least 50ms */ + up_mdelay(50); + fd = open("/dev/ttyS3", O_RDWR); + up_mdelay(600); + /* Test the communication interface between SCM and CH376 */ + xWriteCH376Cmd(CMD11_CHECK_EXIST); + xWriteCH376Data(0x65); + res = xReadCH376Data(); + xEndCH376Cmd(); + if ( res != 0x9A ) return( ERR_USB_UNKNOWN ); + + xWriteCH376Cmd(CMD11_SET_USB_MODE); /* SET USB MODE */ + xWriteCH376Data(CONFIG_CH376_WORK_MODE); + up_udelay(20); + res = xReadCH376Data(); + xEndCH376Cmd(); + if (res == CMD_RET_SUCCESS) return(USB_INT_SUCCESS); + else return(ERR_USB_UNKNOWN); /* SET MODE ERROR */ + +} + +/* Write the requested data block to the internally specified buffer, and return the length */ +UINT8 CH376WriteReqBlock(PUINT8 buf) +{ + UINT8 s, l; + xWriteCH376Cmd( CMD01_WR_REQ_DATA ); + s = l = xReadCH376Data(); + if ( l ) { + do { + xWriteCH376Data( *buf ); + buf ++; + } while ( -- l ); + } + xEndCH376Cmd( ); + return( s ); +} + +/* Write data block to the send buffer of USB host endpoint */ +void CH376WriteHostBlock(PUINT8 buf, UINT8 len) +{ + xWriteCH376Cmd( CMD10_WR_HOST_DATA ); + xWriteCH376Data( len ); + if ( len ) { + do { + xWriteCH376Data( *buf ); + buf ++; + } while ( -- len ); + } + xEndCH376Cmd( ); +} + +/* Specify offset address to write data block to internal buffer */ +void CH376WriteOfsBlock( PUINT8 buf, UINT8 ofs, UINT8 len ) +{ + xWriteCH376Cmd( CMD20_WR_OFS_DATA ); + xWriteCH376Data( ofs ); /* Offset address */ + xWriteCH376Data( len ); /* length */ + if ( len ) { + do { + xWriteCH376Data( *buf ); + buf ++; + } while ( -- len ); + } + xEndCH376Cmd( ); +} + +/* Set the file name of the file to be operated on */ +void CH376SetFileName( PUINT8 name ) +{ + UINT8 c; +#ifndef DEF_IC_V43_U + UINT8 s; + xWriteCH376Cmd( CMD01_GET_IC_VER ); + if ( xReadCH376Data( ) < 0x43 ) { + if ( CH376ReadVar8( VAR_DISK_STATUS ) < DEF_DISK_READY ) { + xWriteCH376Cmd( CMD10_SET_FILE_NAME ); + xWriteCH376Data( 0 ); + s = CH376SendCmdWaitInt( CMD0H_FILE_OPEN ); + if ( s == USB_INT_SUCCESS ) { + s = CH376ReadVar8( 0xCF ); + if ( s ) { + CH376WriteVar32( 0x4C, CH376ReadVar32( 0x4C ) + ( (UINT16)s << 8 ) ); + CH376WriteVar32( 0x50, CH376ReadVar32( 0x50 ) + ( (UINT16)s << 8 ) ); + CH376WriteVar32( 0x70, 0 ); + } + } + } + } +#endif + xWriteCH376Cmd( CMD10_SET_FILE_NAME ); + c = *name; + xWriteCH376Data( c ); + while ( c ) { + name ++; + c = *name; + /* Force the file name to expire */ + if ( c == DEF_SEPAR_CHAR1 || c == DEF_SEPAR_CHAR2 ) c = 0; + xWriteCH376Data( c ); + } + xEndCH376Cmd( ); +} + +/* Read 32-bit data from CH376 chip and end the command */ +UINT32 CH376Read32bitDat( void ) +{ + UINT8 c0, c1, c2, c3; + c0 = xReadCH376Data( ); + c1 = xReadCH376Data( ); + c2 = xReadCH376Data( ); + c3 = xReadCH376Data( ); + xEndCH376Cmd( ); + return( c0 | (UINT16)c1 << 8 | (UINT32)c2 << 16 | (UINT32)c3 << 24 ); +} + + +UINT8 CH376ReadVar8( UINT8 var ) +{ + UINT8 c0; + xWriteCH376Cmd( CMD11_READ_VAR8 ); + xWriteCH376Data( var ); + c0 = xReadCH376Data( ); + xEndCH376Cmd( ); + return( c0 ); +} + +void CH376WriteVar8( UINT8 var, UINT8 dat ) +{ + xWriteCH376Cmd( CMD20_WRITE_VAR8 ); + xWriteCH376Data( var ); + xWriteCH376Data( dat ); + xEndCH376Cmd( ); +} + +UINT32 CH376ReadVar32( UINT8 var ) +{ + xWriteCH376Cmd( CMD14_READ_VAR32 ); + xWriteCH376Data( var ); + return( CH376Read32bitDat( ) ); +} + +void CH376WriteVar32( UINT8 var, UINT32 dat ) +{ + xWriteCH376Cmd( CMD50_WRITE_VAR32 ); + xWriteCH376Data( var ); + xWriteCH376Data( (UINT8)dat ); + xWriteCH376Data( (UINT8)( (UINT16)dat >> 8 ) ); + xWriteCH376Data( (UINT8)( dat >> 16 ) ); + xWriteCH376Data( (UINT8)( dat >> 24 ) ); + xEndCH376Cmd( ); +} + +void CH376EndDirInfo( void ) +{ + CH376WriteVar8( 0x0D, 0x00 ); +} + +UINT32 CH376GetFileSize( void ) +{ + return( CH376ReadVar32( VAR_FILE_SIZE ) ); +} + +UINT8 CH376GetDiskStatus( void ) +{ + return( CH376ReadVar8( VAR_DISK_STATUS ) ); +} + +UINT8 CH376GetIntStatus( void ) +{ + UINT8 s; + xWriteCH376Cmd( CMD01_GET_STATUS ); + s = xReadCH376Data( ); + xEndCH376Cmd( ); + return( s ); +} + +#ifndef NO_DEFAULT_CH376_INT +UINT8 Wait376Interrupt( void ) +{ +#ifdef DEF_INT_TIMEOUT +#if DEF_INT_TIMEOUT < 1 + while ( Query376Interrupt( ) == FALSE ); + return( CH376GetIntStatus( ) ); +#else + UINT32 i; + for ( i = 0; i < DEF_INT_TIMEOUT; i ++ ) { + if ( Query376Interrupt( ) ) return( CH376GetIntStatus( ) ); + } + return( ERR_USB_UNKNOWN ); +#endif +#else + UINT32 i; + for ( i = 0; i < 5000000; i ++ ) { + if ( Query376Interrupt( ) ) return( CH376GetIntStatus( ) ); + } + return( ERR_USB_UNKNOWN ); +#endif +} +#endif + +UINT8 CH376SendCmdWaitInt( UINT8 mCmd ) +{ + xWriteCH376Cmd( mCmd ); + xEndCH376Cmd( ); + return( Wait376Interrupt( ) ); +} + +UINT8 CH376SendCmdDatWaitInt( UINT8 mCmd, UINT8 mDat ) +{ + xWriteCH376Cmd( mCmd ); + xWriteCH376Data( mDat ); + xEndCH376Cmd( ); + return(Wait376Interrupt()); +} + +UINT8 CH376DiskReqSense( void ) +{ + UINT8 s; + up_mdelay( 5 ); + s = CH376SendCmdWaitInt( CMD0H_DISK_R_SENSE ); + up_mdelay( 5 ); + return( s ); +} + +UINT8 CH376DiskConnect( void ) +{ + if ( Query376Interrupt( ) ) CH376GetIntStatus( ); + return( CH376SendCmdWaitInt( CMD0H_DISK_CONNECT ) ); +} + +UINT8 CH376DiskMount( void ) +{ + return( CH376SendCmdWaitInt( CMD0H_DISK_MOUNT ) ); +} + +UINT8 CH376FileOpen( PUINT8 name ) +{ + CH376SetFileName( name ); +#ifndef DEF_IC_V43_U + if ( name[0] == DEF_SEPAR_CHAR1 || name[0] == DEF_SEPAR_CHAR2 ) CH376WriteVar32( VAR_CURRENT_CLUST, 0 ); +#endif + return( CH376SendCmdWaitInt( CMD0H_FILE_OPEN ) ); +} + +UINT8 CH376FileCreate( PUINT8 name ) +{ + if ( name ) CH376SetFileName( name ); + return( CH376SendCmdWaitInt( CMD0H_FILE_CREATE ) ); +} + +UINT8 CH376DirCreate( PUINT8 name ) +{ + CH376SetFileName( name ); +#ifndef DEF_IC_V43_U + if ( name[0] == DEF_SEPAR_CHAR1 || name[0] == DEF_SEPAR_CHAR2 ) CH376WriteVar32( VAR_CURRENT_CLUST, 0 ); +#endif + return( CH376SendCmdWaitInt( CMD0H_DIR_CREATE ) ); +} + +UINT8 CH376SeparatePath( PUINT8 path ) +{ + PUINT8 pName; + for ( pName = path; *pName != 0; ++ pName ); + while ( *pName != DEF_SEPAR_CHAR1 && *pName != DEF_SEPAR_CHAR2 && pName != path ) pName --; + if ( pName != path ) pName ++; + return( pName - path ); +} + +UINT8 CH376FileOpenDir( PUINT8 PathName, UINT8 StopName ) +{ + UINT8 i, s; + s = 0; + i = 1; + while ( 1 ) { + while ( PathName[i] != DEF_SEPAR_CHAR1 && PathName[i] != DEF_SEPAR_CHAR2 && PathName[i] != 0 ) ++ i; + if ( PathName[i] ) i ++; + else i = 0; + s = CH376FileOpen( &PathName[s] ); + if ( i && i != StopName ) { + if ( s != ERR_OPEN_DIR ) { + if ( s == USB_INT_SUCCESS ) return( ERR_FOUND_NAME ); + else if ( s == ERR_MISS_FILE ) return( ERR_MISS_DIR ); + else return( s ); + } + s = i; + } + else return( s ); + } +} + +UINT8 CH376FileOpenPath( PUINT8 PathName ) +{ + return( CH376FileOpenDir( PathName, 0xFF ) ); +} + +UINT8 CH376FileCreatePath( PUINT8 PathName ) +{ + UINT8 s; + UINT8 Name; + Name = CH376SeparatePath( PathName ); + if ( Name ) { + s = CH376FileOpenDir( PathName, Name ); + if ( s != ERR_OPEN_DIR ) { + if ( s == USB_INT_SUCCESS ) return( ERR_FOUND_NAME ); + else if ( s == ERR_MISS_FILE ) return( ERR_MISS_DIR ); + else return( s ); + } + } + return( CH376FileCreate( &PathName[Name] ) ); +} + +#ifdef EN_DIR_CREATE +UINT8 CH376DirCreatePath( PUINT8 PathName ) +{ + UINT8 s; + UINT8 Name; + UINT8 ClustBuf[4]; + Name = CH376SeparatePath( PathName ); + if ( Name ) { + s = CH376FileOpenDir( PathName, Name ); + if ( s != ERR_OPEN_DIR ) { + if ( s == USB_INT_SUCCESS ) return( ERR_FOUND_NAME ); + else if ( s == ERR_MISS_FILE ) return( ERR_MISS_DIR ); + else return( s ); + } + xWriteCH376Cmd( CMD14_READ_VAR32 ); + xWriteCH376Data( VAR_START_CLUSTER ); + for ( s = 0; s != 4; s ++ ) ClustBuf[ s ] = xReadCH376Data( ); + xEndCH376Cmd( ); + s = CH376DirCreate( &PathName[Name] ); + if ( s != USB_INT_SUCCESS ) return( s ); + CH376WriteVar32( VAR_FILE_SIZE, sizeof(FAT_DIR_INFO) * 2 ); + s = CH376ByteLocate( sizeof(FAT_DIR_INFO) + STRUCT_OFFSET( FAT_DIR_INFO, DIR_FstClusHI ) ); + if ( s != USB_INT_SUCCESS ) return( s ); + s = CH376ByteWrite( &ClustBuf[2], 2, NULL ); + if ( s != USB_INT_SUCCESS ) return( s ); + s = CH376ByteLocate( sizeof(FAT_DIR_INFO) + STRUCT_OFFSET( FAT_DIR_INFO, DIR_FstClusLO ) ); + if ( s != USB_INT_SUCCESS ) return( s ); + s = CH376ByteWrite( ClustBuf, 2, NULL ); + if ( s != USB_INT_SUCCESS ) return( s ); + s = CH376ByteLocate( 0 ); + if ( s != USB_INT_SUCCESS ) return( s ); + CH376WriteVar32( VAR_FILE_SIZE, 0 ); + return( s ); + } + else { + if ( PathName[0] == DEF_SEPAR_CHAR1 || PathName[0] == DEF_SEPAR_CHAR2 ) return( CH376DirCreate( PathName ) ); + else return( ERR_MISS_DIR ); + } +} +#endif + +UINT8 CH376FileErase( PUINT8 PathName ) +{ + UINT8 s; + if ( PathName ) { + for ( s = 1; PathName[s] != DEF_SEPAR_CHAR1 && PathName[s] != DEF_SEPAR_CHAR2 && PathName[s] != 0; ++ s ); + if ( PathName[s] ) { + s = CH376FileOpenPath( PathName ); + if ( s != USB_INT_SUCCESS && s != ERR_OPEN_DIR ) return( s ); + } + else CH376SetFileName( PathName ); + } + return( CH376SendCmdWaitInt( CMD0H_FILE_ERASE ) ); +} + +UINT8 CH376FileClose( UINT8 UpdateSz ) +{ + return( CH376SendCmdDatWaitInt( CMD1H_FILE_CLOSE, UpdateSz ) ); +} + +UINT8 CH376DirInfoRead( void ) +{ + return( CH376SendCmdDatWaitInt( CMD1H_DIR_INFO_READ, 0xFF ) ); +} + +UINT8 CH376DirInfoSave( void ) +{ + return( CH376SendCmdWaitInt( CMD0H_DIR_INFO_SAVE ) ); +} + +UINT8 CH376ByteLocate( UINT32 offset ) +{ + xWriteCH376Cmd( CMD4H_BYTE_LOCATE ); + xWriteCH376Data( (UINT8)offset ); + xWriteCH376Data( (UINT8)((UINT16)offset>>8) ); + xWriteCH376Data( (UINT8)(offset>>16) ); + xWriteCH376Data( (UINT8)(offset>>24) ); + xEndCH376Cmd( ); + return( Wait376Interrupt( ) ); +} + +UINT8 CH376ByteRead( PUINT8 buf, UINT16 ReqCount, PUINT16 RealCount ) +{ + UINT8 s; + xWriteCH376Cmd( CMD2H_BYTE_READ ); + xWriteCH376Data( (UINT8)ReqCount ); + xWriteCH376Data( (UINT8)(ReqCount>>8) ); + xEndCH376Cmd( ); + if ( RealCount ) *RealCount = 0; + while ( 1 ) { + s = Wait376Interrupt( ); + if ( s == USB_INT_DISK_READ ) { + s = CH376ReadBlock( buf ); + xWriteCH376Cmd( CMD0H_BYTE_RD_GO ); + xEndCH376Cmd( ); + buf += s; + if ( RealCount ) *RealCount += s; + } + else return( s ); + } +} + +UINT8 CH376ByteWrite( PUINT8 buf, UINT16 ReqCount, PUINT16 RealCount ) +{ + UINT8 s; + xWriteCH376Cmd( CMD2H_BYTE_WRITE ); + xWriteCH376Data( (UINT8)ReqCount ); + xWriteCH376Data( (UINT8)(ReqCount>>8) ); + xEndCH376Cmd( ); + if ( RealCount ) *RealCount = 0; + while ( 1 ) { + s = Wait376Interrupt( ); + if ( s == USB_INT_DISK_WRITE ) { + s = CH376WriteReqBlock( buf ); + xWriteCH376Cmd( CMD0H_BYTE_WR_GO ); + xEndCH376Cmd( ); + buf += s; + if ( RealCount ) *RealCount += s; + } + else return( s ); + } +} + +#ifdef EN_DISK_QUERY + +UINT8 CH376DiskCapacity( PUINT32 DiskCap ) +{ + UINT8 s; + s = CH376SendCmdWaitInt( CMD0H_DISK_CAPACITY ); + if ( s == USB_INT_SUCCESS ) { + xWriteCH376Cmd( CMD01_RD_USB_DATA0 ); + xReadCH376Data( ); + *DiskCap = CH376Read32bitDat( ); + } + else *DiskCap = 0; + return( s ); +} + +UINT8 CH376DiskQuery( PUINT32 DiskFre ) +{ + UINT8 s; + UINT8 c0, c1, c2, c3; +#ifndef DEF_IC_V43_U + xWriteCH376Cmd( CMD01_GET_IC_VER ); + if ( xReadCH376Data( ) < 0x43 ) { + if ( CH376ReadVar8( VAR_DISK_STATUS ) >= DEF_DISK_READY ) CH376WriteVar8( VAR_DISK_STATUS, DEF_DISK_MOUNTED ); + } +#endif + s = CH376SendCmdWaitInt( CMD0H_DISK_QUERY ); + if ( s == USB_INT_SUCCESS ) { + xWriteCH376Cmd( CMD01_RD_USB_DATA0 ); + xReadCH376Data( ); + xReadCH376Data( ); + xReadCH376Data( ); + xReadCH376Data( ); + xReadCH376Data( ); + c0 = xReadCH376Data( ); + c1 = xReadCH376Data( ); + c2 = xReadCH376Data( ); + c3 = xReadCH376Data( ); + *DiskFre = c0 | (UINT16)c1 << 8 | (UINT32)c2 << 16 | (UINT32)c3 << 24; + xReadCH376Data( ); + xEndCH376Cmd( ); + } + else *DiskFre = 0; + return( s ); +} + +#endif + +UINT8 CH376SecLocate( UINT32 offset ) +{ + xWriteCH376Cmd( CMD4H_SEC_LOCATE ); + xWriteCH376Data( (UINT8)offset ); + xWriteCH376Data( (UINT8)((UINT16)offset>>8) ); + xWriteCH376Data( (UINT8)(offset>>16) ); + xWriteCH376Data( 0 ); + xEndCH376Cmd( ); + return( Wait376Interrupt( ) ); +} + +#ifdef EN_SECTOR_ACCESS + +UINT8 CH376DiskReadSec( PUINT8 buf, UINT32 iLbaStart, UINT8 iSectorCount ) + +{ + UINT8 s, err; + UINT16 mBlockCount; + for ( err = 0; err != 3; ++ err ) { + xWriteCH376Cmd( CMD5H_DISK_READ ); + xWriteCH376Data( (UINT8)iLbaStart ); + xWriteCH376Data( (UINT8)( (UINT16)iLbaStart >> 8 ) ); + xWriteCH376Data( (UINT8)( iLbaStart >> 16 ) ); + xWriteCH376Data( (UINT8)( iLbaStart >> 24 ) ); + xWriteCH376Data( iSectorCount ); + xEndCH376Cmd( ); + for ( mBlockCount = iSectorCount * DEF_SECTOR_SIZE / CH376_DAT_BLOCK_LEN; mBlockCount != 0; -- mBlockCount ) { + s = Wait376Interrupt( ); + if ( s == USB_INT_DISK_READ ) { + s = CH376ReadBlock( buf ); + xWriteCH376Cmd( CMD0H_DISK_RD_GO ); + xEndCH376Cmd( ); + buf += s; + } + else break; + } + if ( mBlockCount == 0 ) { + s = Wait376Interrupt( ); + if ( s == USB_INT_SUCCESS ) return( USB_INT_SUCCESS ); + } + if ( s == USB_INT_DISCONNECT ) return( s ); + CH376DiskReqSense( ); + } + return( s ); +} + +UINT8 CH376DiskWriteSec( PUINT8 buf, UINT32 iLbaStart, UINT8 iSectorCount ) +{ + UINT8 s, err; + UINT16 mBlockCount; + for ( err = 0; err != 3; ++ err ) { + xWriteCH376Cmd( CMD5H_DISK_WRITE ); + xWriteCH376Data( (UINT8)iLbaStart ); + xWriteCH376Data( (UINT8)( (UINT16)iLbaStart >> 8 ) ); + xWriteCH376Data( (UINT8)( iLbaStart >> 16 ) ); + xWriteCH376Data( (UINT8)( iLbaStart >> 24 ) ); + xWriteCH376Data( iSectorCount ); + xEndCH376Cmd( ); + for ( mBlockCount = iSectorCount * DEF_SECTOR_SIZE / CH376_DAT_BLOCK_LEN; mBlockCount != 0; -- mBlockCount ) { + s = Wait376Interrupt( ); + if ( s == USB_INT_DISK_WRITE ) { + CH376WriteHostBlock( buf, CH376_DAT_BLOCK_LEN ); + xWriteCH376Cmd( CMD0H_DISK_WR_GO ); + xEndCH376Cmd( ); + buf += CH376_DAT_BLOCK_LEN; + } + else break; + } + if ( mBlockCount == 0 ) { + s = Wait376Interrupt( ); + if ( s == USB_INT_SUCCESS ) return( USB_INT_SUCCESS ); + } + if ( s == USB_INT_DISCONNECT ) return( s ); + CH376DiskReqSense( ); + } + return( s ); +} + +UINT8 CH376SecRead( PUINT8 buf, UINT8 ReqCount, PUINT8 RealCount ) +{ + UINT8 s; + UINT8 cnt; + UINT32 StaSec; +#ifndef DEF_IC_V43_U + UINT32 fsz, fofs; +#endif + if ( RealCount ) *RealCount = 0; + do { +#ifndef DEF_IC_V43_U + xWriteCH376Cmd( CMD01_GET_IC_VER ); + cnt = xReadCH376Data( ); + if ( cnt == 0x41 ) { + xWriteCH376Cmd( CMD14_READ_VAR32 ); + xWriteCH376Data( VAR_FILE_SIZE ); + xReadCH376Data( ); + fsz = xReadCH376Data( ); + fsz |= (UINT16)(xReadCH376Data( )) << 8; + cnt = xReadCH376Data( ); + fsz |= (UINT32)cnt << 16; + xWriteCH376Cmd( CMD14_READ_VAR32 ); + xWriteCH376Data( VAR_CURRENT_OFFSET ); + xReadCH376Data( ); + fofs = xReadCH376Data( ); + fofs |= (UINT16)(xReadCH376Data( )) << 8; + fofs |= (UINT32)(xReadCH376Data( )) << 16; + if ( fsz >= fofs + 510 ) CH376WriteVar8( VAR_FILE_SIZE + 3, 0xFF ); + else cnt = 0xFF; + } + else cnt = 0xFF; +#endif + xWriteCH376Cmd( CMD1H_SEC_READ ); + xWriteCH376Data( ReqCount ); + xEndCH376Cmd( ); + s = Wait376Interrupt( ); +#ifndef DEF_IC_V43_U + if ( cnt != 0xFF ) CH376WriteVar8( VAR_FILE_SIZE + 3, cnt ); +#endif + if ( s != USB_INT_SUCCESS ) return( s ); + xWriteCH376Cmd( CMD01_RD_USB_DATA0 ); + xReadCH376Data( ); + cnt = xReadCH376Data( ); + xReadCH376Data( ); + xReadCH376Data( ); + xReadCH376Data( ); + StaSec = CH376Read32bitDat( ); + if ( cnt == 0 ) break; + s = CH376DiskReadSec( buf, StaSec, cnt ); + if ( s != USB_INT_SUCCESS ) return( s ); + buf += cnt * DEF_SECTOR_SIZE; + if ( RealCount ) *RealCount += cnt; + ReqCount -= cnt; + } while ( ReqCount ); + return( s ); +} + +UINT8 CH376SecWrite( PUINT8 buf, UINT8 ReqCount, PUINT8 RealCount ) +{ + UINT8 s; + UINT8 cnt; + UINT32 StaSec; + if ( RealCount ) *RealCount = 0; + do { + xWriteCH376Cmd( CMD1H_SEC_WRITE ); + xWriteCH376Data( ReqCount ); + xEndCH376Cmd( ); + s = Wait376Interrupt( ); + if ( s != USB_INT_SUCCESS ) return( s ); + xWriteCH376Cmd( CMD01_RD_USB_DATA0 ); + xReadCH376Data( ); + cnt = xReadCH376Data( ); + xReadCH376Data( ); + xReadCH376Data( ); + xReadCH376Data( ); + StaSec = CH376Read32bitDat( ); + if ( cnt == 0 ) break; + s = CH376DiskWriteSec( buf, StaSec, cnt ); + if ( s != USB_INT_SUCCESS ) return( s ); + buf += cnt * DEF_SECTOR_SIZE; + if ( RealCount ) *RealCount += cnt; + ReqCount -= cnt; + } while ( ReqCount ); + return( s ); +} + +#endif + +#ifdef EN_LONG_NAME + +UINT8 CH376LongNameWrite( PUINT8 buf, UINT16 ReqCount ) +{ + UINT8 s; +#ifndef DEF_IC_V43_U + UINT8 c; + c = CH376ReadVar8( VAR_DISK_STATUS ); + if ( c == DEF_DISK_OPEN_ROOT ) CH376WriteVar8( VAR_DISK_STATUS, DEF_DISK_OPEN_DIR ); +#endif + xWriteCH376Cmd( CMD2H_BYTE_WRITE ); + xWriteCH376Data( (UINT8)ReqCount ); + xWriteCH376Data( (UINT8)(ReqCount>>8) ); + xEndCH376Cmd( ); + while ( 1 ) { + s = Wait376Interrupt( ); + if ( s == USB_INT_DISK_WRITE ) { + if ( buf ) buf += CH376WriteReqBlock( buf ); + else { + xWriteCH376Cmd( CMD01_WR_REQ_DATA ); + s = xReadCH376Data( ); + while ( s -- ) xWriteCH376Data( 0 ); + } + xWriteCH376Cmd( CMD0H_BYTE_WR_GO ); + xEndCH376Cmd( ); + } + else { +#ifndef DEF_IC_V43_U + if ( c == DEF_DISK_OPEN_ROOT ) CH376WriteVar8( VAR_DISK_STATUS, c ); +#endif + return( s ); + } + } +} + +UINT8 CH376CheckNameSum( PUINT8 DirName ) +{ + UINT8 NameLen; + UINT8 CheckSum; + CheckSum = 0; + for ( NameLen = 0; NameLen != 11; NameLen ++ ) CheckSum = ( CheckSum & 1 ? 0x80 : 0x00 ) + ( CheckSum >> 1 ) + *DirName++; + return( CheckSum ); +} + +UINT8 CH376LocateInUpDir( PUINT8 PathName ) +{ + UINT8 s; + xWriteCH376Cmd( CMD14_READ_VAR32 ); + xWriteCH376Data( VAR_FAT_DIR_LBA ); + for ( s = 4; s != 8; s ++ ) GlobalBuf[ s ] = xReadCH376Data( ); + xEndCH376Cmd( ); + s = CH376SeparatePath( PathName ); + if ( s ) s = CH376FileOpenDir( PathName, s ); + else s = CH376FileOpen( "/" ); + if ( s != ERR_OPEN_DIR ) return( s ); + *(PUINT32)(&GlobalBuf[0]) = 0; + while ( 1 ) { + s = CH376SecLocate( *(PUINT32)(&GlobalBuf[0]) ); + if ( s != USB_INT_SUCCESS ) return( s ); + CH376ReadBlock( &GlobalBuf[8] ); + if ( *(PUINT32)(&GlobalBuf[8]) == *(PUINT32)(&GlobalBuf[4]) ) return( USB_INT_SUCCESS ); + xWriteCH376Cmd( CMD50_WRITE_VAR32 ); + xWriteCH376Data( VAR_FAT_DIR_LBA ); + for ( s = 8; s != 12; s ++ ) xWriteCH376Data( GlobalBuf[ s ] ); + xEndCH376Cmd( ); + ++ *(PUINT32)(&GlobalBuf[0]); + } +} + +UINT8 CH376GetLongName( PUINT8 PathName, PUINT8 LongName ) +{ + UINT8 s; + UINT16 NameCount; + s = CH376FileOpenPath( PathName ); + if ( s != USB_INT_SUCCESS && s != ERR_OPEN_DIR ) return( s ); + s = CH376DirInfoRead( ); + if ( s != USB_INT_SUCCESS ) return( s ); + CH376ReadBlock( GlobalBuf ); + CH376EndDirInfo( ); + GlobalBuf[32] = CH376CheckNameSum( GlobalBuf ); + GlobalBuf[33] = CH376ReadVar8( VAR_FILE_DIR_INDEX ); + NameCount = 0; + while ( 1 ) { + if ( GlobalBuf[33] == 0 ) { + s = CH376LocateInUpDir( PathName ); + if ( s != USB_INT_SUCCESS ) break; + if ( CH376ReadVar32( VAR_CURRENT_OFFSET ) == 0 ) { + s = ERR_LONG_NAME_ERR; + break; + } + GlobalBuf[33] = DEF_SECTOR_SIZE / sizeof( FAT_DIR_INFO ); + } + GlobalBuf[33] --; + s = CH376SendCmdDatWaitInt( CMD1H_DIR_INFO_READ, GlobalBuf[33] ); + if ( s != USB_INT_SUCCESS ) break; + CH376ReadBlock( GlobalBuf ); + CH376EndDirInfo( ); + if ( ( GlobalBuf[11] & ATTR_LONG_NAME_MASK ) != ATTR_LONG_NAME || GlobalBuf[13] != GlobalBuf[32] ) { + s = ERR_LONG_NAME_ERR; + break; + } + for ( s = 1; s < sizeof( FAT_DIR_INFO ); s += 2 ) { + if ( s == 1 + 5 * 2 ) s = 14; + else if ( s == 14 + 6 * 2 ) s = 28; + LongName[ NameCount++ ] = GlobalBuf[ s ]; + LongName[ NameCount++ ] = GlobalBuf[ s + 1 ]; + if ( GlobalBuf[ s ] == 0 && GlobalBuf[ s + 1 ] == 0 ) break; + if ( NameCount >= LONG_NAME_BUF_LEN ) { + s = ERR_LONG_BUF_OVER; + goto CH376GetLongNameE; + } + } + if ( GlobalBuf[0] & 0x40 ) { + if ( s >= sizeof( FAT_DIR_INFO ) ) *(PUINT16)( &LongName[ NameCount ] ) = 0x0000; + s = USB_INT_SUCCESS; + break; + } + } +CH376GetLongNameE: + CH376FileClose( FALSE ); + return( s ); +} + +UINT8 CH376CreateLongName( PUINT8 PathName, PUINT8 LongName ) +{ + UINT8 s, i; + UINT8 DirBlockCnt; + UINT16 count; + UINT16 NameCount; + UINT32 NewFileLoc; + for ( count = 0; count < LONG_NAME_BUF_LEN; count += 2 ) if ( *(PUINT16)(&LongName[count]) == 0 ) break; + if ( count == 0 || count >= LONG_NAME_BUF_LEN || count > LONE_NAME_MAX_CHAR ) return( ERR_LONG_NAME_ERR ); + DirBlockCnt = count / LONG_NAME_PER_DIR; + i = count - DirBlockCnt * LONG_NAME_PER_DIR; + if ( i ) { + if ( ++ DirBlockCnt * LONG_NAME_PER_DIR > LONG_NAME_BUF_LEN ) return( ERR_LONG_BUF_OVER ); + count += 2; + i += 2; + if ( i < LONG_NAME_PER_DIR ) { + while ( i++ < LONG_NAME_PER_DIR ) LongName[count++] = 0xFF; + } + } + s = CH376FileOpenPath( PathName ); + if ( s == USB_INT_SUCCESS ) { + s = ERR_NAME_EXIST; + goto CH376CreateLongNameE; + } + if ( s != ERR_MISS_FILE ) return( s ); + s = CH376FileCreatePath( PathName ); + if ( s != USB_INT_SUCCESS ) return( s ); + i = CH376ReadVar8( VAR_FILE_DIR_INDEX ); + s = CH376LocateInUpDir( PathName ); + if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; + NewFileLoc = CH376ReadVar32( VAR_CURRENT_OFFSET ) + i * sizeof(FAT_DIR_INFO); + s = CH376ByteLocate( NewFileLoc ); + if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; + s = CH376ByteRead( &GlobalBuf[ sizeof(FAT_DIR_INFO) ], sizeof(FAT_DIR_INFO), NULL ); + if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; + for ( i = DirBlockCnt; i != 0; -- i ) { + s = CH376ByteRead( GlobalBuf, sizeof(FAT_DIR_INFO), &count ); + if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; + if ( count == 0 ) break; + if ( GlobalBuf[0] && GlobalBuf[0] != 0xE5 ) { + s = CH376ByteLocate( NewFileLoc ); + if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; + GlobalBuf[ 0 ] = 0xE5; + for ( s = 1; s != sizeof(FAT_DIR_INFO); s ++ ) GlobalBuf[ s ] = GlobalBuf[ sizeof(FAT_DIR_INFO) + s ]; + s = CH376LongNameWrite( GlobalBuf, sizeof(FAT_DIR_INFO) ); + if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; + do { + s = CH376ByteRead( GlobalBuf, sizeof(FAT_DIR_INFO), &count ); + if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; + } while ( count && GlobalBuf[0] ); + NewFileLoc = CH376ReadVar32( VAR_CURRENT_OFFSET ); + i = DirBlockCnt + 1; + if ( count == 0 ) break; + NewFileLoc -= sizeof(FAT_DIR_INFO); + } + } + if ( i ) { + s = CH376ReadVar8( VAR_SEC_PER_CLUS ); + if ( s == 128 ) { + s = ERR_FDT_OVER; + goto CH376CreateLongNameE; + } + count = s * DEF_SECTOR_SIZE; + if ( count < i * sizeof(FAT_DIR_INFO) ) count <<= 1; + s = CH376LongNameWrite( NULL, count ); + if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; + } + s = CH376ByteLocate( NewFileLoc ); + if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; + GlobalBuf[11] = ATTR_LONG_NAME; + GlobalBuf[12] = 0x00; + GlobalBuf[13] = CH376CheckNameSum( &GlobalBuf[ sizeof(FAT_DIR_INFO) ] ); + GlobalBuf[26] = 0x00; + GlobalBuf[27] = 0x00; + for ( s = 0; DirBlockCnt != 0; ) { + GlobalBuf[0] = s ? DirBlockCnt : DirBlockCnt | 0x40; + DirBlockCnt --; + NameCount = DirBlockCnt * LONG_NAME_PER_DIR; + for ( s = 1; s < sizeof( FAT_DIR_INFO ); s += 2 ) { + if ( s == 1 + 5 * 2 ) s = 14; + else if ( s == 14 + 6 * 2 ) s = 28; + GlobalBuf[ s ] = LongName[ NameCount++ ]; + GlobalBuf[ s + 1 ] = LongName[ NameCount++ ]; + } + s = CH376LongNameWrite( GlobalBuf, sizeof(FAT_DIR_INFO) ); + if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; + } + s = CH376LongNameWrite( &GlobalBuf[ sizeof(FAT_DIR_INFO) ], sizeof(FAT_DIR_INFO) ); +CH376CreateLongNameE: + CH376FileClose( FALSE ); + return( s ); +} + +#endif diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch376.h b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch376.h new file mode 100644 index 000000000..87086eafd --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch376.h @@ -0,0 +1,124 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file k210_ch376.h + * @brief edu-riscv64 k210_ch376.h + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.10.10 + * */ + +#ifndef __CH376_FS_H__ +#define __CH376_FS_H__ + +#include "ch376inc.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "k210_uart_16550.h" + +#define ERR_USB_UNKNOWN 0xFA + +void xEndCH376Cmd( void ); +void xWriteCH376Cmd( UINT8 mCmd ); +void xWriteCH376Data( UINT8 mData ); +UINT8 xReadCH376Data( void ); +UINT8 Query376Interrupt( void ); +UINT8 mInitCH376Host( void ); + +#define STRUCT_OFFSET( s, m ) ( (UINT8)( & ((s *)0) -> m ) ) + +#ifdef EN_LONG_NAME +#ifndef LONG_NAME_BUF_LEN +#define LONG_NAME_BUF_LEN ( LONG_NAME_PER_DIR * 20 ) +#endif +#endif + +UINT8 CH376ReadBlock( PUINT8 buf ); +UINT8 CH376WriteReqBlock( PUINT8 buf ); +void CH376WriteHostBlock( PUINT8 buf, UINT8 len ); +void CH376WriteOfsBlock( PUINT8 buf, UINT8 ofs, UINT8 len ); +void CH376SetFileName( PUINT8 name ); +UINT32 CH376Read32bitDat( void ); +UINT8 CH376ReadVar8( UINT8 var ); +void CH376WriteVar8( UINT8 var, UINT8 dat ); +UINT32 CH376ReadVar32( UINT8 var ); +void CH376WriteVar32( UINT8 var, UINT32 dat ); +void CH376EndDirInfo( void ); +UINT32 CH376GetFileSize( void ); +UINT8 CH376GetDiskStatus( void ); +UINT8 CH376GetIntStatus( void ); + +#ifndef NO_DEFAULT_CH376_INT +UINT8 Wait376Interrupt( void ); +#endif + +UINT8 CH376SendCmdWaitInt( UINT8 mCmd ); +UINT8 CH376SendCmdDatWaitInt( UINT8 mCmd, UINT8 mDat ); +UINT8 CH376DiskReqSense( void ); +UINT8 CH376DiskConnect( void ); +UINT8 CH376DiskMount( void ); +UINT8 CH376FileOpen( PUINT8 name ); +UINT8 CH376FileCreate( PUINT8 name ); +UINT8 CH376DirCreate( PUINT8 name ); +UINT8 CH376SeparatePath( PUINT8 path ); +UINT8 CH376FileOpenDir( PUINT8 PathName, UINT8 StopName ); +UINT8 CH376FileOpenPath( PUINT8 PathName ); +UINT8 CH376FileCreatePath( PUINT8 PathName ); + +#ifdef EN_DIR_CREATE +UINT8 CH376DirCreatePath( PUINT8 PathName ); +#endif + +UINT8 CH376FileErase( PUINT8 PathName ); +UINT8 CH376FileClose( UINT8 UpdateSz ); +UINT8 CH376DirInfoRead( void ); +UINT8 CH376DirInfoSave( void ); +UINT8 CH376ByteLocate( UINT32 offset ); +UINT8 CH376ByteRead( PUINT8 buf, UINT16 ReqCount, PUINT16 RealCount ); +UINT8 CH376ByteWrite( PUINT8 buf, UINT16 ReqCount, PUINT16 RealCount ); + +#ifdef EN_DISK_QUERY +UINT8 CH376DiskCapacity( PUINT32 DiskCap ); +UINT8 CH376DiskQuery( PUINT32 DiskFre ); +#endif + +UINT8 CH376SecLocate( UINT32 offset ); + +#ifdef EN_SECTOR_ACCESS +UINT8 CH376DiskReadSec( PUINT8 buf, UINT32 iLbaStart, UINT8 iSectorCount ); +UINT8 CH376DiskWriteSec( PUINT8 buf, UINT32 iLbaStart, UINT8 iSectorCount ); +UINT8 CH376SecRead( PUINT8 buf, UINT8 ReqCount, PUINT8 RealCount ); +UINT8 CH376SecWrite( PUINT8 buf, UINT8 ReqCount, PUINT8 RealCount ); +#endif + +#ifdef EN_LONG_NAME +UINT8 CH376LongNameWrite( PUINT8 buf, UINT16 ReqCount ); +UINT8 CH376CheckNameSum( PUINT8 DirName ); +UINT8 CH376LocateInUpDir( PUINT8 PathName ); +UINT8 CH376GetLongName( PUINT8 PathName, PUINT8 LongName ); +UINT8 CH376CreateLongName( PUINT8 PathName, PUINT8 LongName ); +#endif + +#endif diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch438.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch438.c new file mode 100644 index 000000000..8996182ea --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch438.c @@ -0,0 +1,922 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file k210_ch438.c + * @brief edu-riscv64 k210_ch438.c + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.06.08 + */ + +#include "k210_ch438.h" + +#define CH438PORTNUM 8 +#define CH438_BUFFSIZE 256 +#define CH438_INCREMENT MSEC2TICK(33) + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +static FAR void getInterruptStatus(FAR void *arg); +static void ch438_io_config(void); +static void CH438SetOutput(void); +static void CH438SetInput(void); +static uint8_t ReadCH438Data(uint8_t addr); +static void WriteCH438Data(uint8_t addr, const uint8_t dat); +static void WriteCH438Block(uint8_t mAddr, uint8_t mLen, const uint8_t *mBuf); +static void Ch438UartSend(uint8_t ext_uart_no, const uint8_t *Data, uint16_t Num); +uint8_t CH438UARTRcv(uint8_t ext_uart_no, uint8_t *buf, size_t size); +static void K210CH438Init(void); +static void CH438PortInit(uint8_t ext_uart_no, uint32_t baud_rate); +static int K210Ch438WriteData(uint8_t ext_uart_no, const uint8_t *write_buffer, size_t size); +static size_t K210Ch438ReadData(uint8_t ext_uart_no, size_t size); +static void Ch438InitDefault(void); + +static int ch438_open(FAR struct file *filep); +static int ch438_close(FAR struct file *filep); +static ssize_t ch438_read(FAR struct file *filep, FAR char *buffer, size_t buflen); +static ssize_t ch438_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); +static int ch438_ioctl(FAR struct file *filep, int cmd, unsigned long arg); +static int ch438_register(FAR const char *devpath, uint8_t ext_uart_no); + +/**************************************************************************** + * Private type + ****************************************************************************/ +struct ch438_dev_s +{ + sem_t devsem; /* ch438 port devsem */ + uint8_t port; /* ch438 port number*/ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/*mutex of corresponding port*/ +static pthread_mutex_t mutex[CH438PORTNUM] = +{ + PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER +}; + +/* Condition variable of corresponding port */ +static pthread_cond_t cond[CH438PORTNUM] = +{ + PTHREAD_COND_INITIALIZER, + PTHREAD_COND_INITIALIZER, + PTHREAD_COND_INITIALIZER, + PTHREAD_COND_INITIALIZER, + PTHREAD_COND_INITIALIZER, + PTHREAD_COND_INITIALIZER, + PTHREAD_COND_INITIALIZER, + PTHREAD_COND_INITIALIZER +}; + +/* This array shows whether the current serial port is selected */ +static bool const g_uart_selected[CH438PORTNUM] = +{ +#ifdef CONFIG_CH438_EXTUART0 + [0] = true, +#endif +#ifdef CONFIG_CH438_EXTUART1 + [1] = true, +#endif +#ifdef CONFIG_CH438_EXTUART2 + [2] = true, +#endif +#ifdef CONFIG_CH438_EXTUART3 + [3] = true, +#endif +#ifdef CONFIG_CH438_EXTUART4 + [4] = true, +#endif +#ifdef CONFIG_CH438_EXTUART5 + [5] = true, +#endif +#ifdef CONFIG_CH438_EXTUART6 + [6] = true, +#endif +#ifdef CONFIG_CH438_EXTUART7 + [7] = true, +#endif +}; + +/* ch438 Callback work queue structure */ +static struct work_s g_ch438irqwork; + +/* there is data available on the corresponding port */ +static volatile bool done[CH438PORTNUM] = {false,false,false,false,false,false,false,false}; + +/* Eight port data buffer */ +static uint8_t buff[CH438PORTNUM][CH438_BUFFSIZE]; + +/* the value of interrupt number of SSR register */ +static uint8_t Interruptnum[CH438PORTNUM] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,}; + +/* Offset address of serial port number */ +static uint8_t offsetadd[CH438PORTNUM] = {0x00,0x10,0x20,0x30,0x08,0x18,0x28,0x38,}; + +/* port open status global variable */ +static volatile bool g_ch438open[CH438PORTNUM] = {false,false,false,false,false,false,false,false}; + +/* Ch438 POSIX interface */ +static const struct file_operations g_ch438fops = +{ + ch438_open, + ch438_close, + ch438_read, + ch438_write, + NULL, + ch438_ioctl, + NULL +}; + +/**************************************************************************** + * Name: getInterruptStatus + * + * Description: + * thread task getInterruptStatus + * + ****************************************************************************/ +static FAR void getInterruptStatus(FAR void *arg) +{ + uint8_t i; + uint8_t gInterruptStatus; /* Interrupt register status */ + + gInterruptStatus = ReadCH438Data(REG_SSR_ADDR); + + if(gInterruptStatus) + { + for(i = 0; i < CH438PORTNUM; i++) + { + if(g_uart_selected[i] && (gInterruptStatus & Interruptnum[i])) + { + pthread_mutex_lock(&mutex[i]); + done[i] = true; + pthread_cond_signal(&cond[i]); + pthread_mutex_unlock(&mutex[i]); + } + } + } + + work_queue(HPWORK, &g_ch438irqwork, getInterruptStatus, NULL, CH438_INCREMENT); +} + +/**************************************************************************** + * Name: Ch438SetPinMode + * + * Description: + * Configure pin mode + * + * Input Parameters: + * hard_io - Hardware IO + * soft_io - Software IO mapped to + * dir - true for output, false for input + * + ****************************************************************************/ +static void ch438_io_config(void) +{ + k210_fpioa_config(CH438_NWR_PIN, CH438_FUNC_GPIO(FPIOA_CH438_NWR)); + k210_fpioa_config(CH438_NRD_PIN, CH438_FUNC_GPIO(FPIOA_CH438_NRD)); + k210_fpioa_config(CH438_ALE_PIN, CH438_FUNC_GPIO(FPIOA_CH438_ALE)); + + k210_fpioa_config(CH438_D0_PIN, CH438_FUNC_GPIO(FPIOA_CH438_D0)); + k210_fpioa_config(CH438_D1_PIN, CH438_FUNC_GPIO(FPIOA_CH438_D1)); + k210_fpioa_config(CH438_D2_PIN, CH438_FUNC_GPIO(FPIOA_CH438_D2)); + k210_fpioa_config(CH438_D3_PIN, CH438_FUNC_GPIO(FPIOA_CH438_D3)); + k210_fpioa_config(CH438_D4_PIN, CH438_FUNC_GPIO(FPIOA_CH438_D4)); + k210_fpioa_config(CH438_D5_PIN, CH438_FUNC_GPIO(FPIOA_CH438_D5)); + k210_fpioa_config(CH438_D6_PIN, CH438_FUNC_GPIO(FPIOA_CH438_D6)); + k210_fpioa_config(CH438_D7_PIN, CH438_FUNC_GPIO(FPIOA_CH438_D7)); +} + +/**************************************************************************** + * Name: CH438SetOutput + * + * Description: + * Configure pin mode to output + * + ****************************************************************************/ + +static void CH438SetOutput(void) +{ + k210_gpiohs_set_direction(FPIOA_CH438_D0, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_CH438_D1, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_CH438_D2, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_CH438_D3, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_CH438_D4, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_CH438_D5, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_CH438_D6, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_CH438_D7, GPIO_DM_OUTPUT); +} + +/**************************************************************************** + * Name: CH438SetInput + * + * Description: + * Configure pin mode to input + * + ****************************************************************************/ +static void CH438SetInput(void) +{ + k210_gpiohs_set_direction(FPIOA_CH438_D0, GPIO_DM_INPUT_PULL_UP); + k210_gpiohs_set_direction(FPIOA_CH438_D1, GPIO_DM_INPUT_PULL_UP); + k210_gpiohs_set_direction(FPIOA_CH438_D2, GPIO_DM_INPUT_PULL_UP); + k210_gpiohs_set_direction(FPIOA_CH438_D3, GPIO_DM_INPUT_PULL_UP); + k210_gpiohs_set_direction(FPIOA_CH438_D4, GPIO_DM_INPUT_PULL_UP); + k210_gpiohs_set_direction(FPIOA_CH438_D5, GPIO_DM_INPUT_PULL_UP); + k210_gpiohs_set_direction(FPIOA_CH438_D6, GPIO_DM_INPUT_PULL_UP); + k210_gpiohs_set_direction(FPIOA_CH438_D7, GPIO_DM_INPUT_PULL_UP); +} + +/**************************************************************************** + * Name: ReadCH438Data + * + * Description: + * Read data from ch438 address + * + ****************************************************************************/ +static uint8_t ReadCH438Data(uint8_t addr) +{ + uint8_t dat = 0; + k210_gpiohs_set_value(FPIOA_CH438_NWR, true); + k210_gpiohs_set_value(FPIOA_CH438_NRD, true); + k210_gpiohs_set_value(FPIOA_CH438_ALE, true); + + CH438SetOutput(); + up_udelay(1); + + if(addr &0x80) k210_gpiohs_set_value(FPIOA_CH438_D7, true); else k210_gpiohs_set_value(FPIOA_CH438_D7, false); + if(addr &0x40) k210_gpiohs_set_value(FPIOA_CH438_D6, true); else k210_gpiohs_set_value(FPIOA_CH438_D6, false); + if(addr &0x20) k210_gpiohs_set_value(FPIOA_CH438_D5, true); else k210_gpiohs_set_value(FPIOA_CH438_D5, false); + if(addr &0x10) k210_gpiohs_set_value(FPIOA_CH438_D4, true); else k210_gpiohs_set_value(FPIOA_CH438_D4, false); + if(addr &0x08) k210_gpiohs_set_value(FPIOA_CH438_D3, true); else k210_gpiohs_set_value(FPIOA_CH438_D3, false); + if(addr &0x04) k210_gpiohs_set_value(FPIOA_CH438_D2, true); else k210_gpiohs_set_value(FPIOA_CH438_D2, false); + if(addr &0x02) k210_gpiohs_set_value(FPIOA_CH438_D1, true); else k210_gpiohs_set_value(FPIOA_CH438_D1, false); + if(addr &0x01) k210_gpiohs_set_value(FPIOA_CH438_D0, true); else k210_gpiohs_set_value(FPIOA_CH438_D0, false); + + up_udelay(1); + + k210_gpiohs_set_value(FPIOA_CH438_ALE, false); + up_udelay(1); + + CH438SetInput(); + up_udelay(1); + + k210_gpiohs_set_value(FPIOA_CH438_NRD, false); + up_udelay(1); + + if (k210_gpiohs_get_value(FPIOA_CH438_D7)) dat |= 0x80; + if (k210_gpiohs_get_value(FPIOA_CH438_D6)) dat |= 0x40; + if (k210_gpiohs_get_value(FPIOA_CH438_D5)) dat |= 0x20; + if (k210_gpiohs_get_value(FPIOA_CH438_D4)) dat |= 0x10; + if (k210_gpiohs_get_value(FPIOA_CH438_D3)) dat |= 0x08; + if (k210_gpiohs_get_value(FPIOA_CH438_D2)) dat |= 0x04; + if (k210_gpiohs_get_value(FPIOA_CH438_D1)) dat |= 0x02; + if (k210_gpiohs_get_value(FPIOA_CH438_D0)) dat |= 0x01; + + k210_gpiohs_set_value(FPIOA_CH438_NRD, true); + k210_gpiohs_set_value(FPIOA_CH438_ALE, true); + up_udelay(1); + + return dat; +} + +/**************************************************************************** + * Name: WriteCH438Data + * + * Description: + * write data to ch438 address + * + ****************************************************************************/ +static void WriteCH438Data(uint8_t addr, const uint8_t dat) +{ + k210_gpiohs_set_value(FPIOA_CH438_ALE, true); + k210_gpiohs_set_value(FPIOA_CH438_NRD, true); + k210_gpiohs_set_value(FPIOA_CH438_NWR, true); + + CH438SetOutput(); + up_udelay(1); + + if(addr &0x80) k210_gpiohs_set_value(FPIOA_CH438_D7, true); else k210_gpiohs_set_value(FPIOA_CH438_D7, false); + if(addr &0x40) k210_gpiohs_set_value(FPIOA_CH438_D6, true); else k210_gpiohs_set_value(FPIOA_CH438_D6, false); + if(addr &0x20) k210_gpiohs_set_value(FPIOA_CH438_D5, true); else k210_gpiohs_set_value(FPIOA_CH438_D5, false); + if(addr &0x10) k210_gpiohs_set_value(FPIOA_CH438_D4, true); else k210_gpiohs_set_value(FPIOA_CH438_D4, false); + if(addr &0x08) k210_gpiohs_set_value(FPIOA_CH438_D3, true); else k210_gpiohs_set_value(FPIOA_CH438_D3, false); + if(addr &0x04) k210_gpiohs_set_value(FPIOA_CH438_D2, true); else k210_gpiohs_set_value(FPIOA_CH438_D2, false); + if(addr &0x02) k210_gpiohs_set_value(FPIOA_CH438_D1, true); else k210_gpiohs_set_value(FPIOA_CH438_D1, false); + if(addr &0x01) k210_gpiohs_set_value(FPIOA_CH438_D0, true); else k210_gpiohs_set_value(FPIOA_CH438_D0, false); + + up_udelay(1); + + k210_gpiohs_set_value(FPIOA_CH438_ALE, false); + up_udelay(1); + + if(dat &0x80) k210_gpiohs_set_value(FPIOA_CH438_D7, true); else k210_gpiohs_set_value(FPIOA_CH438_D7, false); + if(dat &0x40) k210_gpiohs_set_value(FPIOA_CH438_D6, true); else k210_gpiohs_set_value(FPIOA_CH438_D6, false); + if(dat &0x20) k210_gpiohs_set_value(FPIOA_CH438_D5, true); else k210_gpiohs_set_value(FPIOA_CH438_D5, false); + if(dat &0x10) k210_gpiohs_set_value(FPIOA_CH438_D4, true); else k210_gpiohs_set_value(FPIOA_CH438_D4, false); + if(dat &0x08) k210_gpiohs_set_value(FPIOA_CH438_D3, true); else k210_gpiohs_set_value(FPIOA_CH438_D3, false); + if(dat &0x04) k210_gpiohs_set_value(FPIOA_CH438_D2, true); else k210_gpiohs_set_value(FPIOA_CH438_D2, false); + if(dat &0x02) k210_gpiohs_set_value(FPIOA_CH438_D1, true); else k210_gpiohs_set_value(FPIOA_CH438_D1, false); + if(dat &0x01) k210_gpiohs_set_value(FPIOA_CH438_D0, true); else k210_gpiohs_set_value(FPIOA_CH438_D0, false); + + up_udelay(1); + + k210_gpiohs_set_value(FPIOA_CH438_NWR, false); + up_udelay(1); + + k210_gpiohs_set_value(FPIOA_CH438_NWR, true); + k210_gpiohs_set_value(FPIOA_CH438_ALE, true); + up_udelay(1); + + CH438SetInput(); + + return; +} + +/**************************************************************************** + * Name: WriteCH438Block + * + * Description: + * Write data block from ch438 address + * + ****************************************************************************/ +static void WriteCH438Block(uint8_t mAddr, uint8_t mLen, const uint8_t *mBuf) +{ + while(mLen--) + WriteCH438Data(mAddr, *mBuf++); +} + +/**************************************************************************** + * Name: CH438UARTSend + * + * Description: + * Enable FIFO mode, which is used for ch438 serial port to send multi byte data, + * with a maximum of 128 bytes of data sent at a time + * + ****************************************************************************/ +static void Ch438UartSend(uint8_t ext_uart_no, const uint8_t *Data, uint16_t Num) +{ + uint8_t REG_LSR_ADDR,REG_THR_ADDR; + REG_LSR_ADDR = offsetadd[ext_uart_no] | REG_LSR0_ADDR; + REG_THR_ADDR = offsetadd[ext_uart_no] | REG_THR0_ADDR; + + while(1) + { + while((ReadCH438Data(REG_LSR_ADDR) & BIT_LSR_TEMT) == 0); /* wait for sending data done, THR and TSR is NULL */ + if(Num <= 128) + { + WriteCH438Block(REG_THR_ADDR, Num, Data); + break; + } + else + { + WriteCH438Block(REG_THR_ADDR, 128, Data); + Num -= 128; + Data += 128; + } + } +} + +/**************************************************************************** + * Name: CH438UARTRcv + * + * Description: + * Disable FIFO mode for ch438 serial port to receive multi byte data + * + ****************************************************************************/ +uint8_t CH438UARTRcv(uint8_t ext_uart_no, uint8_t *buf, size_t size) +{ + uint8_t rcv_num = 0; + uint8_t dat = 0; + uint8_t REG_LSR_ADDR,REG_RBR_ADDR; + uint8_t *read_buffer; + size_t buffer_index = 0; + + read_buffer = buf; + + REG_LSR_ADDR = offsetadd[ext_uart_no] | REG_LSR0_ADDR; + REG_RBR_ADDR = offsetadd[ext_uart_no] | REG_RBR0_ADDR; + + /* Wait for the data to be ready */ + while ((ReadCH438Data(REG_LSR_ADDR) & BIT_LSR_DATARDY) == 0); + + while (((ReadCH438Data(REG_LSR_ADDR) & BIT_LSR_DATARDY) == 0x01) && (size != 0)) + { + dat = ReadCH438Data(REG_RBR_ADDR); + *read_buffer = dat; + read_buffer++; + buffer_index++; + if (255 == buffer_index) { + buffer_index = 0; + read_buffer = buf; + } + + ++rcv_num; + --size; + } + + return rcv_num; +} + +/**************************************************************************** + * Name: K210CH438Init + * + * Description: + * ch438 initialization + * + ****************************************************************************/ +static void K210CH438Init(void) +{ + CH438SetOutput(); + k210_gpiohs_set_direction(FPIOA_CH438_NWR, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_CH438_NRD, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_CH438_ALE, GPIO_DM_OUTPUT); + + k210_gpiohs_set_value(FPIOA_CH438_NWR, true); + k210_gpiohs_set_value(FPIOA_CH438_NRD, true); + k210_gpiohs_set_value(FPIOA_CH438_ALE, true); +} + +/**************************************************************************** + * Name: CH438PortInit + * + * Description: + * ch438 port initialization + * + ****************************************************************************/ +static void CH438PortInit(uint8_t ext_uart_no, uint32_t baud_rate) +{ + uint32_t div; + uint8_t DLL,DLM,dlab; + uint8_t REG_LCR_ADDR; + uint8_t REG_DLL_ADDR; + uint8_t REG_DLM_ADDR; + uint8_t REG_IER_ADDR; + uint8_t REG_MCR_ADDR; + uint8_t REG_FCR_ADDR; + + REG_LCR_ADDR = offsetadd[ext_uart_no] | REG_LCR0_ADDR; + REG_DLL_ADDR = offsetadd[ext_uart_no] | REG_DLL0_ADDR; + REG_DLM_ADDR = offsetadd[ext_uart_no] | REG_DLM0_ADDR; + REG_IER_ADDR = offsetadd[ext_uart_no] | REG_IER0_ADDR; + REG_MCR_ADDR = offsetadd[ext_uart_no] | REG_MCR0_ADDR; + REG_FCR_ADDR = offsetadd[ext_uart_no] | REG_FCR0_ADDR; + + /* reset the uart */ + WriteCH438Data(REG_IER_ADDR, BIT_IER_RESET); + up_mdelay(50); + + dlab = ReadCH438Data(REG_IER_ADDR); + dlab &= 0xDF; + WriteCH438Data(REG_IER_ADDR, dlab); + + /* set LCR register DLAB bit 1 */ + dlab = ReadCH438Data(REG_LCR_ADDR); + dlab |= 0x80; + WriteCH438Data(REG_LCR_ADDR, dlab); + + div = (Fpclk >> 4) / baud_rate; + DLM = div >> 8; + DLL = div & 0xff; + + /* set bps */ + WriteCH438Data(REG_DLL_ADDR, DLL); + WriteCH438Data(REG_DLM_ADDR, DLM); + + /* set FIFO mode, 112 bytes */ + WriteCH438Data(REG_FCR_ADDR, BIT_FCR_RECVTG1 | BIT_FCR_RECVTG0 | BIT_FCR_FIFOEN); + + /* 8 bit word size, 1 bit stop bit, no crc */ + WriteCH438Data(REG_LCR_ADDR, BIT_LCR_WORDSZ1 | BIT_LCR_WORDSZ0); + + /* enable interrupt */ + WriteCH438Data(REG_IER_ADDR, BIT_IER_IERECV); + + /* allow interrupt output, DTR and RTS is 1 */ + WriteCH438Data(REG_MCR_ADDR, BIT_MCR_OUT2); + + /* release the data in FIFO */ + WriteCH438Data(REG_FCR_ADDR, ReadCH438Data(REG_FCR_ADDR)| BIT_FCR_TFIFORST); +} + +/**************************************************************************** + * Name: K210Ch438ReadData + * + * Description: + * Read data from ch438 port + * + ****************************************************************************/ +static int K210Ch438WriteData(uint8_t ext_uart_no, const uint8_t *write_buffer, size_t size) +{ + int write_len, write_len_continue; + int i, write_index; + DEBUGASSERT(write_buffer != NULL); + + write_len = size; + write_len_continue = size; + + if(write_len > 256) + { + if(0 == write_len % 256) + { + write_index = write_len / 256; + for(i = 0; i < write_index; i ++) + { + Ch438UartSend(ext_uart_no, write_buffer + i * 256, 256); + } + } + else + { + write_index = 0; + while(write_len_continue > 256) + { + Ch438UartSend(ext_uart_no, write_buffer + write_index * 256, 256); + write_index++; + write_len_continue = write_len - write_index * 256; + } + Ch438UartSend(ext_uart_no, write_buffer + write_index * 256, write_len_continue); + } + } + else + { + Ch438UartSend(ext_uart_no, write_buffer, write_len); + } + + return OK; +} + +/**************************************************************************** + * Name: K210Ch438ReadData + * + * Description: + * Read data from ch438 port + * + ****************************************************************************/ +static size_t K210Ch438ReadData(uint8_t ext_uart_no, size_t size) +{ + size_t RevLen = 0; + uint8_t InterruptStatus; + uint8_t REG_IIR_ADDR; + uint8_t REG_LSR_ADDR; + uint8_t REG_MSR_ADDR; + + pthread_mutex_lock(&mutex[ext_uart_no]); + while(done[ext_uart_no] == false) + pthread_cond_wait(&cond[ext_uart_no], &mutex[ext_uart_no]); + if(done[ext_uart_no] == true) + { + REG_IIR_ADDR = offsetadd[ext_uart_no] | REG_IIR0_ADDR; + REG_LSR_ADDR = offsetadd[ext_uart_no] | REG_LSR0_ADDR; + REG_MSR_ADDR = offsetadd[ext_uart_no] | REG_MSR0_ADDR; + /* Read the interrupt status of the serial port */ + InterruptStatus = ReadCH438Data(REG_IIR_ADDR) & 0x0f; + ch438info("InterruptStatus is %d\n", InterruptStatus); + + switch(InterruptStatus) + { + case INT_NOINT: /* no interrupt */ + break; + case INT_THR_EMPTY: /* the transmit hold register is not interrupted */ + break; + case INT_RCV_OVERTIME: /* receive data timeout interrupt */ + case INT_RCV_SUCCESS: /* receive data available interrupt */ + RevLen = CH438UARTRcv(ext_uart_no, buff[ext_uart_no], size); + break; + case INT_RCV_LINES: /* receive line status interrupt */ + ReadCH438Data(REG_LSR_ADDR); + break; + case INT_MODEM_CHANGE: /* modem input change interrupt */ + ReadCH438Data(REG_MSR_ADDR); + break; + default: + break; + } + done[ext_uart_no] = false; + } + pthread_mutex_unlock(&mutex[ext_uart_no]); + + return RevLen; +} + +/**************************************************************************** + * Name: Ch438InitDefault + * + * Description: + * Ch438 default initialization function + * + ****************************************************************************/ +static void Ch438InitDefault(void) +{ + int ret = 0; + int i; + + /* Initialize the mutex */ + for(i = 0; i < CH438PORTNUM; i++) + { + if(!g_uart_selected[i]) + { + continue; + } + + ret = pthread_mutex_init(&mutex[i], NULL); + if(ret != 0) + { + ch438err("pthread_mutex_init failed, status=%d\n", ret); + } + } + + /* Initialize the condition variable */ + for(i = 0; i < CH438PORTNUM; i++) + { + if(!g_uart_selected[i]) + { + continue; + } + + ret = pthread_cond_init(&cond[i], NULL); + if(ret != 0) + { + ch438err("pthread_cond_init failed, status=%d\n", ret); + } + } + + ch438_io_config(); + K210CH438Init(); + +/* If a port is checked, the port will be initialized. Otherwise, the interrupt of the port will be disabled. */ + +#ifdef CONFIG_CH438_EXTUART0 + CH438PortInit(0, CONFIG_CH438_EXTUART0_BAUD); +#else + WriteCH438Data(REG_IER0_ADDR, 0x00); +#endif + +#ifdef CONFIG_CH438_EXTUART1 + CH438PortInit(1, CONFIG_CH438_EXTUART1_BAUD); +#else + WriteCH438Data(REG_IER1_ADDR, 0x00); +#endif + +#ifdef CONFIG_CH438_EXTUART2 + CH438PortInit(2, CONFIG_CH438_EXTUART2_BAUD); +#else + WriteCH438Data(REG_IER2_ADDR, 0x00); +#endif + +#ifdef CONFIG_CH438_EXTUART3 + CH438PortInit(3, CONFIG_CH438_EXTUART3_BAUD); +#else + WriteCH438Data(REG_IER3_ADDR, 0x00); +#endif + +#ifdef CONFIG_CH438_EXTUART4 + CH438PortInit(4, CONFIG_CH438_EXTUART4_BAUD); +#else + WriteCH438Data(REG_IER4_ADDR, 0x00); +#endif + +#ifdef CONFIG_CH438_EXTUART5 + CH438PortInit(5, CONFIG_CH438_EXTUART5_BAUD); +#else + WriteCH438Data(REG_IER5_ADDR, 0x00); +#endif + +#ifdef CONFIG_CH438_EXTUART6 + CH438PortInit(6, CONFIG_CH438_EXTUART6_BAUD); +#else + WriteCH438Data(REG_IER6_ADDR, 0x00); +#endif + +#ifdef CONFIG_CH438_EXTUART7 + CH438PortInit(7, CONFIG_CH438_EXTUART7_BAUD); +#else + WriteCH438Data(REG_IER7_ADDR, 0x00); +#endif + + up_mdelay(10); + + work_queue(HPWORK, &g_ch438irqwork, getInterruptStatus, NULL, CH438_INCREMENT); +} + +/**************************************************************************** + * Name: ch438_open + ****************************************************************************/ +static int ch438_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct ch438_dev_s *priv = inode->i_private; + uint8_t port = priv->port; + int ret = OK; + + DEBUGASSERT(port >= 0 && port < CH438PORTNUM); + + ret = nxsem_wait_uninterruptible(&priv->devsem); + if (ret < 0) + { + return ret; + } + + if(g_ch438open[port]) + { + ch438err("ERROR: ch438 port %d is opened!\n",port); + return -EBUSY; + } + g_ch438open[port] = true; + + nxsem_post(&priv->devsem); + + return ret; +} + +/**************************************************************************** + * Name: ch438_close + ****************************************************************************/ +static int ch438_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct ch438_dev_s *priv = inode->i_private; + uint8_t port = priv->port; + int ret = OK; + + DEBUGASSERT(port >= 0 && port < CH438PORTNUM); + + ret = nxsem_wait_uninterruptible(&priv->devsem); + if (ret < 0) + { + return ret; + } + + if(!g_ch438open[port]) + { + ch438err("ERROR: ch438 port %d is closed!\n",port); + return -EBUSY; + } + g_ch438open[port] = false; + + nxsem_post(&priv->devsem); + return ret; +} + +/**************************************************************************** + * Name: ch438_read + ****************************************************************************/ +static ssize_t ch438_read(FAR struct file *filep, FAR char *buffer, size_t buflen) +{ + size_t length = 0; + FAR struct inode *inode = filep->f_inode; + FAR struct ch438_dev_s *priv = inode->i_private; + uint8_t port = priv->port; + + DEBUGASSERT(port >= 0 && port < CH438PORTNUM); + + length = K210Ch438ReadData(port, buflen); + memcpy(buffer, buff[port], length); + + if(length > buflen) + { + length = buflen; + } + + return length; +} + +/**************************************************************************** + * Name: ch438_write + ****************************************************************************/ +static ssize_t ch438_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct ch438_dev_s *priv = inode->i_private; + uint8_t port = priv->port; + + DEBUGASSERT(port >= 0 && port < CH438PORTNUM); + + K210Ch438WriteData(port, (const uint8_t *)buffer, buflen); + + return buflen; +} + +/**************************************************************************** + * Name: ch438_ioctl + ****************************************************************************/ +static int ch438_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct ch438_dev_s *priv = inode->i_private; + uint8_t port = priv->port; + int ret = OK; + + DEBUGASSERT(port >= 0 && port < CH438PORTNUM); + + switch(cmd) + { + case OPE_INT: + case OPE_CFG: + CH438PortInit(port, (uint32_t)arg); + break; + + default: + ch438info("Unrecognized cmd: %d\n", cmd); + ret = -ENOTTY; + break; + } + return ret; +} + + +/**************************************************************************** + * Name: ch438_register + * + * Description: + * Register /dev/ext_uartN + * + ****************************************************************************/ +static int ch438_register(FAR const char *devpath, uint8_t port) +{ + FAR struct ch438_dev_s *priv; + int ret = 0; + + /* port number check */ + DEBUGASSERT(port >= 0 && port < CH438PORTNUM); + + priv = (FAR struct ch438_dev_s *)kmm_malloc(sizeof(struct ch438_dev_s)); + if(priv == NULL) + { + ch438err("ERROR: Failed to allocate instance\n"); + return -ENOMEM; + } + + priv->port = port; + nxsem_init(&priv->devsem, 0, 1); + + /* Register the character driver */ + ret = register_driver(devpath, &g_ch438fops, 0666, priv); + if(ret < 0) + { + kmm_free(priv); + } + + return ret; +} + +/**************************************************************************** + * Name: board_ch438_initialize + * + * Description: + * ch438 initialize + * + ****************************************************************************/ +void board_ch438_initialize(void) +{ + Ch438InitDefault(); + +#ifdef CONFIG_CH438_EXTUART0 + ch438_register("/dev/extuart_dev0", 0); +#endif + +#ifdef CONFIG_CH438_EXTUART1 + ch438_register("/dev/extuart_dev1", 1); +#endif + +#ifdef CONFIG_CH438_EXTUART2 + ch438_register("/dev/extuart_dev2", 2); +#endif + +#ifdef CONFIG_CH438_EXTUART3 + ch438_register("/dev/extuart_dev3", 3); +#endif + +#ifdef CONFIG_CH438_EXTUART4 + ch438_register("/dev/extuart_dev4", 4); +#endif + +#ifdef CONFIG_CH438_EXTUART5 + ch438_register("/dev/extuart_dev5", 5); +#endif + +#ifdef CONFIG_CH438_EXTUART6 + ch438_register("/dev/extuart_dev6", 6); +#endif + +#ifdef CONFIG_CH438_EXTUART7 + ch438_register("/dev/extuart_dev7", 7); +#endif +} \ No newline at end of file diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch438.h b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch438.h new file mode 100644 index 000000000..ead371b6f --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_ch438.h @@ -0,0 +1,326 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file k210_ch438.h + * @brief edu-riscv64 k210_ch438.h + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.04.26 + */ + +#ifndef __BOARDS_XIDATONG_SRC_RISCV64_CH438_H +#define __BOARDS_XIDATONG_SRC_RISCV64_CH438_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "riscv_internal.h" + +#include "k210_config.h" +#include "k210_fpioa.h" +#include "k210_gpiohs.h" +#include "edu-riscv64.h" + +/******************************************************************************************/ + +/* chip definition */ +/* CH438serial port0 register address */ + +#define REG_RBR0_ADDR 0x00 /* serial port0receive buffer register address */ +#define REG_THR0_ADDR 0x00 /* serial port0send hold register address */ +#define REG_IER0_ADDR 0x01 /* serial port0interrupt enable register address */ +#define REG_IIR0_ADDR 0x02 /* serial port0interrupt identifies register address */ +#define REG_FCR0_ADDR 0x02 /* serial port0FIFO controls register address */ +#define REG_LCR0_ADDR 0x03 /* serial port0circuit control register address */ +#define REG_MCR0_ADDR 0x04 /* serial port0MODEM controls register address */ +#define REG_LSR0_ADDR 0x05 /* serial port0line status register address */ +#define REG_MSR0_ADDR 0x06 /* serial port0address of MODEM status register */ +#define REG_SCR0_ADDR 0x07 /* serial port0the user can define the register address */ +#define REG_DLL0_ADDR 0x00 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM0_ADDR 0x01 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port1 register address */ + +#define REG_RBR1_ADDR 0x10 /* serial port1receive buffer register address */ +#define REG_THR1_ADDR 0x10 /* serial port1send hold register address */ +#define REG_IER1_ADDR 0x11 /* serial port1interrupt enable register address */ +#define REG_IIR1_ADDR 0x12 /* serial port1interrupt identifies register address */ +#define REG_FCR1_ADDR 0x12 /* serial port1FIFO controls register address */ +#define REG_LCR1_ADDR 0x13 /* serial port1circuit control register address */ +#define REG_MCR1_ADDR 0x14 /* serial port1MODEM controls register address */ +#define REG_LSR1_ADDR 0x15 /* serial port1line status register address */ +#define REG_MSR1_ADDR 0x16 /* serial port1address of MODEM status register */ +#define REG_SCR1_ADDR 0x17 /* serial port1the user can define the register address */ +#define REG_DLL1_ADDR 0x10 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM1_ADDR 0x11 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port2 register address */ + +#define REG_RBR2_ADDR 0x20 /* serial port2receive buffer register address */ +#define REG_THR2_ADDR 0x20 /* serial port2send hold register address */ +#define REG_IER2_ADDR 0x21 /* serial port2interrupt enable register address */ +#define REG_IIR2_ADDR 0x22 /* serial port2interrupt identifies register address */ +#define REG_FCR2_ADDR 0x22 /* serial port2FIFO controls register address */ +#define REG_LCR2_ADDR 0x23 /* serial port2circuit control register address */ +#define REG_MCR2_ADDR 0x24 /* serial port2MODEM controls register address */ +#define REG_LSR2_ADDR 0x25 /* serial port2line status register address */ +#define REG_MSR2_ADDR 0x26 /* serial port2address of MODEM status register */ +#define REG_SCR2_ADDR 0x27 /* serial port2the user can define the register address */ +#define REG_DLL2_ADDR 0x20 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM2_ADDR 0x21 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port3 register address */ + +#define REG_RBR3_ADDR 0x30 /* serial port3receive buffer register address */ +#define REG_THR3_ADDR 0x30 /* serial port3send hold register address */ +#define REG_IER3_ADDR 0x31 /* serial port3interrupt enable register address */ +#define REG_IIR3_ADDR 0x32 /* serial port3interrupt identifies register address */ +#define REG_FCR3_ADDR 0x32 /* serial port3FIFO controls register address */ +#define REG_LCR3_ADDR 0x33 /* serial port3circuit control register address */ +#define REG_MCR3_ADDR 0x34 /* serial port3MODEM controls register address */ +#define REG_LSR3_ADDR 0x35 /* serial port3line status register address */ +#define REG_MSR3_ADDR 0x36 /* serial port3address of MODEM status register */ +#define REG_SCR3_ADDR 0x37 /* serial port3the user can define the register address */ +#define REG_DLL3_ADDR 0x30 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM3_ADDR 0x31 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port4 register address */ + +#define REG_RBR4_ADDR 0x08 /* serial port4receive buffer register address */ +#define REG_THR4_ADDR 0x08 /* serial port4send hold register address */ +#define REG_IER4_ADDR 0x09 /* serial port4interrupt enable register address */ +#define REG_IIR4_ADDR 0x0A /* serial port4interrupt identifies register address */ +#define REG_FCR4_ADDR 0x0A /* serial port4FIFO controls register address */ +#define REG_LCR4_ADDR 0x0B /* serial port4circuit control register address */ +#define REG_MCR4_ADDR 0x0C /* serial port4MODEM controls register address */ +#define REG_LSR4_ADDR 0x0D /* serial port4line status register address */ +#define REG_MSR4_ADDR 0x0E /* serial port4address of MODEM status register */ +#define REG_SCR4_ADDR 0x0F /* serial port4the user can define the register address */ +#define REG_DLL4_ADDR 0x08 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM4_ADDR 0x09 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port5 register address */ + +#define REG_RBR5_ADDR 0x18 /* serial port5receive buffer register address */ +#define REG_THR5_ADDR 0x18 /* serial port5send hold register address */ +#define REG_IER5_ADDR 0x19 /* serial port5interrupt enable register address */ +#define REG_IIR5_ADDR 0x1A /* serial port5interrupt identifies register address */ +#define REG_FCR5_ADDR 0x1A /* serial port5FIFO controls register address */ +#define REG_LCR5_ADDR 0x1B /* serial port5circuit control register address */ +#define REG_MCR5_ADDR 0x1C /* serial port5MODEM controls register address */ +#define REG_LSR5_ADDR 0x1D /* serial port5line status register address */ +#define REG_MSR5_ADDR 0x1E /* serial port5address of MODEM status register */ +#define REG_SCR5_ADDR 0x1F /* serial port5the user can define the register address */ +#define REG_DLL5_ADDR 0x18 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM5_ADDR 0x19 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port6 register address */ + +#define REG_RBR6_ADDR 0x28 /* serial port6receive buffer register address */ +#define REG_THR6_ADDR 0x28 /* serial port6send hold register address */ +#define REG_IER6_ADDR 0x29 /* serial port6interrupt enable register address */ +#define REG_IIR6_ADDR 0x2A /* serial port6interrupt identifies register address */ +#define REG_FCR6_ADDR 0x2A /* serial port6FIFO controls register address */ +#define REG_LCR6_ADDR 0x2B /* serial port6circuit control register address */ +#define REG_MCR6_ADDR 0x2C /* serial port6MODEM controls register address */ +#define REG_LSR6_ADDR 0x2D /* serial port6line status register address */ +#define REG_MSR6_ADDR 0x2E /* serial port6address of MODEM status register */ +#define REG_SCR6_ADDR 0x2F /* serial port6the user can define the register address */ +#define REG_DLL6_ADDR 0x28 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM6_ADDR 0x29 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port7 register address */ + +#define REG_RBR7_ADDR 0x38 /* serial port7receive buffer register address */ +#define REG_THR7_ADDR 0x38 /* serial port7send hold register address */ +#define REG_IER7_ADDR 0x39 /* serial port7interrupt enable register address */ +#define REG_IIR7_ADDR 0x3A /* serial port7interrupt identifies register address */ +#define REG_FCR7_ADDR 0x3A /* serial port7FIFO controls register address */ +#define REG_LCR7_ADDR 0x3B /* serial port7circuit control register address */ +#define REG_MCR7_ADDR 0x3C /* serial port7MODEM controls register address */ +#define REG_LSR7_ADDR 0x3D /* serial port7line status register address */ +#define REG_MSR7_ADDR 0x3E /* serial port7address of MODEM status register */ +#define REG_SCR7_ADDR 0x3F /* serial port7the user can define the register address */ +#define REG_DLL7_ADDR 0x38 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM7_ADDR 0x39 /* Baud rate divisor latch high 8-bit byte address */ + + +#define REG_SSR_ADDR 0x4F /* pecial status register address */ + + +/* IER register bit */ + +#define BIT_IER_RESET 0x80 /* The bit is 1 soft reset serial port */ +#define BIT_IER_LOWPOWER 0x40 /* The bit is 1 close serial port internal reference clock */ +#define BIT_IER_SLP 0x20 /* serial port0 is SLP, 1 close clock vibrator */ +#define BIT_IER1_CK2X 0x20 /* serial port1 is CK2X, 1 force the external clock signal after 2 times as internal */ +#define BIT_IER_IEMODEM 0x08 /* The bit is 1 allows MODEM input status to interrupt */ +#define BIT_IER_IELINES 0x04 /* The bit is 1 allow receiving line status to be interrupted */ +#define BIT_IER_IETHRE 0x02 /* The bit is 1 allows the send hold register to break in mid-air */ +#define BIT_IER_IERECV 0x01 /* The bit is 1 allows receiving data interrupts */ + +/* IIR register bit */ + +#define BIT_IIR_FIFOENS1 0x80 +#define BIT_IIR_FIFOENS0 0x40 /* The two is 1 said use FIFO */ + +/* Interrupt type: 0001 has no interrupt, 0110 receiving line status is interrupted, 0100 receiving data can be interrupted, +1100 received data timeout interrupt, 0010THR register air interrupt, 0000MODEM input change interrupt */ +#define BIT_IIR_IID3 0x08 +#define BIT_IIR_IID2 0x04 +#define BIT_IIR_IID1 0x02 +#define BIT_IIR_NOINT 0x01 + +/* FCR register bit */ + +/* Trigger point: 00 corresponds to 1 byte, 01 corresponds to 16 bytes, 10 corresponds to 64 bytes, 11 corresponds to 112 bytes */ +#define BIT_FCR_RECVTG1 0x80 /* Set the trigger point for FIFO interruption and automatic hardware flow control */ +#define BIT_FCR_RECVTG0 0x40 /* Set the trigger point for FIFO interruption and automatic hardware flow control */ + +#define BIT_FCR_TFIFORST 0x04 /* The bit is 1 empty the data sent in FIFO */ +#define BIT_FCR_RFIFORST 0x02 /* The bit is 1 empty the data sent in FIFO */ +#define BIT_FCR_FIFOEN 0x01 /* The bit is 1 use FIFO, 0 disable FIFO */ + +/* LCR register bit */ + +#define BIT_LCR_DLAB 0x80 /* To access DLL, DLM, 0 to access RBR/THR/IER */ +#define BIT_LCR_BREAKEN 0x40 /* 1 forces a BREAK line interval*/ + +/* Set the check format: when PAREN is 1, 00 odd check, 01 even check, 10 MARK (set 1), 11 blank (SPACE, clear 0) */ +#define BIT_LCR_PARMODE1 0x20 /* Sets the parity bit format */ +#define BIT_LCR_PARMODE0 0x10 /* Sets the parity bit format */ + +#define BIT_LCR_PAREN 0x08 /* A value of 1 allows you to generate and receive parity bits when sending */ +#define BIT_LCR_STOPBIT 0x04 /* If is 1, then two stop bits, is 0, a stop bit */ + +/* Set word length: 00 for 5 data bits, 01 for 6 data bits, 10 for 7 data bits and 11 for 8 data bits */ +#define BIT_LCR_WORDSZ1 0x02 /* Set the word length length */ +#define BIT_LCR_WORDSZ0 0x01 + +/* MCR register bit */ + +#define BIT_MCR_AFE 0x20 /* For 1 allows automatic flow control of CTS and RTS hardware */ +#define BIT_MCR_LOOP 0x10 /* Is the test mode of 1 enabling internal loop */ +#define BIT_MCR_OUT2 0x08 /* 1 Allows an interrupt request for the serial port output */ +#define BIT_MCR_OUT1 0x04 /* The MODEM control bit defined for the user */ +#define BIT_MCR_RTS 0x02 /* The bit is 1 RTS pin output effective */ +#define BIT_MCR_DTR 0x01 /* The bit is 1 DTR pin output effective */ + +/* LSR register bit */ + +#define BIT_LSR_RFIFOERR 0x80 /* 1 said There is at least one error in receiving FIFO */ +#define BIT_LSR_TEMT 0x40 /* 1 said THR and TSR are empty */ +#define BIT_LSR_THRE 0x20 /* 1 said THR is empty*/ +#define BIT_LSR_BREAKINT 0x10 /* The bit is 1 said the BREAK line interval was detected*/ +#define BIT_LSR_FRAMEERR 0x08 /* The bit is 1 said error reading data frame */ +#define BIT_LSR_PARERR 0x04 /* The bit is 1 said parity error */ +#define BIT_LSR_OVERR 0x02 /* 1 said receive FIFO buffer overflow */ +#define BIT_LSR_DATARDY 0x01 /* The bit is 1 said receive data received in FIFO */ + +/* MSR register bit */ + +#define BIT_MSR_DCD 0x80 /* The bit is 1 said DCD pin effective */ +#define BIT_MSR_RI 0x40 /* The bit is 1 said RI pin effective */ +#define BIT_MSR_DSR 0x20 /* The bit is 1 said DSR pin effective */ +#define BIT_MSR_CTS 0x10 /* The bit is 1 said CTS pin effective */ +#define BIT_MSR_DDCD 0x08 /* The bit is 1 said DCD pin The input state has changed */ +#define BIT_MSR_TERI 0x04 /* The bit is 1 said RI pin The input state has changed */ +#define BIT_MSR_DDSR 0x02 /* The bit is 1 said DSR pin The input state has changed */ +#define BIT_MSR_DCTS 0x01 /* The bit is 1 said CTS pin The input state has changed */ + +/* Interrupt status code */ + +#define INT_NOINT 0x01 /* There is no interruption */ +#define INT_THR_EMPTY 0x02 /* THR empty interruption */ +#define INT_RCV_OVERTIME 0x0C /* Receive timeout interrupt */ +#define INT_RCV_SUCCESS 0x04 /* Interrupts are available to receive data */ +#define INT_RCV_LINES 0x06 /* Receiving line status interrupted */ +#define INT_MODEM_CHANGE 0x00 /* MODEM input changes interrupt */ + +#define CH438_IIR_FIFOS_ENABLED 0xC0 /* use FIFO */ + + +#define Fpclk 1843200 /* Define the internal clock frequency*/ + +/* ch438 debug */ +#ifdef CONFIG_DEBUG_CH438_ERROR +# define ch438err _err +#else +# define ch438err _none +#endif + +#ifdef CONFIG_DEBUG_CH438_WARN +# define ch438warn _warn +#else +# define ch438warn _none +#endif + +#ifdef CONFIG_DEBUG_CH438_INFO +# define ch438info _info +#else +# define ch438info _none +#endif + +#define CH438_FUNC_GPIO(n) ((K210_IO_FUNC_GPIOHS0 + n) | K210_IOFLAG_GPIOHS) + +/* ioctl cmd */ +#define OPE_INT 0x0000 +#define OPE_CFG 0x0001 + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ +#ifdef CONFIG_BSP_USING_CH438 +void board_ch438_initialize(void); +#endif + +#endif /* __BOARDS_XIDATONG_SRC_RISCV64_CH438_H */ + diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_gpio.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_gpio.c new file mode 100644 index 000000000..622815449 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_gpio.c @@ -0,0 +1,180 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file k210_gpio.c + * @brief edu-riscv64 k210_gpio.c + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.06.08 + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "k210_fpioa.h" +#include "k210_gpiohs.h" + +#if defined(CONFIG_DEV_GPIO) && !defined(CONFIG_GPIO_LOWER_HALF) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Pin 1 and 2 are used for this example as GPIO outputs. */ + + + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct k210gpio_dev_s +{ + struct gpio_dev_s gpio; + uint8_t id; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#if BOARD_NGPIOOUT > 0 +static int gpout_read(FAR struct gpio_dev_s *dev, FAR bool *value); +static int gpout_write(FAR struct gpio_dev_s *dev, bool value); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#if BOARD_NGPIOOUT > 0 +static const struct gpio_operations_s gpout_ops = +{ + .go_read = gpout_read, + .go_write = gpout_write, + .go_attach = NULL, + .go_enable = NULL, +}; + +/* This array maps the GPIO pins used as OUTPUT */ + +static const uint32_t g_gpiooutputs[BOARD_NGPIOOUT] = +{ + GPIO_E220_M0, + GPIO_E220_M1, + GPIO_E18_MODE +}; + +static const uint32_t g_fpioa[BOARD_NGPIOOUT] = +{ + FPIOA_E220_M0, + FPIOA_E220_M1, + FPIOA_E18_MODE +}; + +static struct k210gpio_dev_s g_gpout[BOARD_NGPIOOUT]; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: gpout_read + ****************************************************************************/ + +#if BOARD_NGPIOOUT > 0 +static int gpout_read(FAR struct gpio_dev_s *dev, FAR bool *value) +{ + FAR struct k210gpio_dev_s *k210gpio = + (FAR struct k210gpio_dev_s *)dev; + + DEBUGASSERT(k210gpio != NULL && value != NULL); + DEBUGASSERT(k210gpio->id < BOARD_NGPIOOUT); + gpioinfo("Reading...\n"); + + *value = (int) k210_gpiohs_get_value(g_fpioa[k210gpio->id]); + return OK; +} + +/**************************************************************************** + * Name: gpout_write + ****************************************************************************/ + +static int gpout_write(FAR struct gpio_dev_s *dev, bool value) +{ + FAR struct k210gpio_dev_s *k210gpio = + (FAR struct k210gpio_dev_s *)dev; + + DEBUGASSERT(k210gpio != NULL); + DEBUGASSERT(k210gpio->id < BOARD_NGPIOOUT); + gpioinfo("Writing %d\n", (int)value); + + k210_gpiohs_set_value(g_fpioa[k210gpio->id], value); + + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: k210_gpio_init + ****************************************************************************/ + +int k210_gpio_init(void) +{ + int i; + int pincount = 0; + +#if BOARD_NGPIOOUT > 0 + for (i = 0; i < BOARD_NGPIOOUT; i++) + { + /* Setup and register the GPIO pin */ + + g_gpout[i].gpio.gp_pintype = GPIO_OUTPUT_PIN; + g_gpout[i].gpio.gp_ops = &gpout_ops; + g_gpout[i].id = i; + gpio_pin_register(&g_gpout[i].gpio, pincount); + + /* Configure the pins that will be used as output */ + + k210_fpioa_config(g_gpiooutputs[i], + (K210_IO_FUNC_GPIOHS0 + g_fpioa[i]) | K210_IOFLAG_GPIOHS); + k210_gpiohs_set_direction(g_fpioa[i], GPIO_DM_OUTPUT); + k210_gpiohs_set_value(g_fpioa[i], false); + pincount++; + } +#endif + + return OK; +} +#endif /* CONFIG_DEV_GPIO && !CONFIG_GPIO_LOWER_HALF */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_lcd.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_lcd.c new file mode 100644 index 000000000..e5a54f668 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_lcd.c @@ -0,0 +1,353 @@ +/* +* Copyright (c) 2022 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file k210_lcd.c + * @brief LCD relative driver + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.7.21 + */ + + +/**************************************************************************** + * Included Files + ****************************************************************************/ +#include +#include "k210_fpioa.h" +#include "k210_gpiohs.h" +#include "nuttx/arch.h" +#include "nuttx/lcd/lt768.h" +#include "nuttx/lcd/lt768_lib.h" +#include "nuttx/lcd/if_port.h" +#include "nuttx/lcd/lt768_learn.h" +#include +#include +#include +#include +#ifdef CONFIG_LCD_LCDDRV_SPIIF +#include "nuttx/lcd/lcddrv_spiif.h" +#endif + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +#define NCS_H() k210_gpiohs_set_value(FPIOA_LCD_NCS, GPIO_PV_HIGH); up_udelay(20) +#define NCS_L() k210_gpiohs_set_value(FPIOA_LCD_NCS, GPIO_PV_LOW); up_udelay(20) +#define CLK_H() k210_gpiohs_set_value(FPIOA_LCD_SCLK, GPIO_PV_HIGH); up_udelay(20) +#define CLK_L() k210_gpiohs_set_value(FPIOA_LCD_SCLK, GPIO_PV_LOW); up_udelay(20) +#define MOSI_H() k210_gpiohs_set_value(FPIOA_LCD_MOSI, GPIO_PV_HIGH) +#define MOSI_L() k210_gpiohs_set_value(FPIOA_LCD_MOSI, GPIO_PV_LOW) + +static int lcd_open(FAR struct file *filep); +static int lcd_close(FAR struct file *filep); +static ssize_t lcd_read(FAR struct file *filep, FAR char *buffer, size_t buflen); +static ssize_t lcd_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); + +/**************************************************************************** + * Private Data + ****************************************************************************/ +/* LCD POSIX interface */ +static const struct file_operations g_lcdfops = +{ + lcd_open, + lcd_close, + lcd_read, + lcd_write, + NULL, + NULL, + NULL +}; +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void lcd_pin_init(void) +{ + k210_fpioa_config(BSP_LCD_NRST, HS_GPIO(FPIOA_LCD_NRST) | K210_IOFLAG_GPIOHS); + k210_fpioa_config(BSP_LCD_SCLK, HS_GPIO(FPIOA_LCD_SCLK) | K210_IOFLAG_GPIOHS); + k210_fpioa_config(BSP_LCD_MOSI, HS_GPIO(FPIOA_LCD_MOSI) | K210_IOFLAG_GPIOHS); + k210_fpioa_config(BSP_LCD_MISO, HS_GPIO(FPIOA_LCD_MISO) | K210_IOFLAG_GPIOHS); + k210_fpioa_config(BSP_LCD_NCS, HS_GPIO(FPIOA_LCD_NCS) | K210_IOFLAG_GPIOHS); + + k210_gpiohs_set_direction(FPIOA_LCD_MISO, GPIO_DM_INPUT); + k210_gpiohs_set_direction(FPIOA_LCD_NRST, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_LCD_SCLK, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_LCD_MOSI, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_LCD_NCS, GPIO_DM_OUTPUT); + + k210_gpiohs_set_value(FPIOA_LCD_SCLK, GPIO_PV_HIGH); + k210_gpiohs_set_value(FPIOA_LCD_NCS, GPIO_PV_HIGH); + k210_gpiohs_set_value(FPIOA_LCD_NRST, GPIO_PV_HIGH); +} + +void lcd_backlight_init(bool enable) +{ + k210_fpioa_config(BSP_LCD_BL_PIN, HS_GPIO(FPIOA_LCD_BL) | K210_IOFLAG_GPIOHS); + k210_gpiohs_set_direction(FPIOA_LCD_BL, GPIO_DM_OUTPUT); + k210_gpiohs_set_value(FPIOA_LCD_BL, enable); +} + +#ifdef CONFIG_LCD_LCDDRV_SPIIF +int spiif_backlight(FAR struct lcddrv_lcd_s *lcd, int level) +{ + lcd_backlight_init(true); + return 1; +} +#endif + +uint8_t lcd_transfer_byte(uint8_t dat) +{ + uint8_t i, rx_data = 0; + + for(i = 0; i < 8; i++) + { + CLK_H(); + + // MOSI during falling edge + if((dat << i) & 0x80) + { + MOSI_H(); + } + else + { + MOSI_L(); + } + + CLK_L(); + + // MISO during rising edge + rx_data <<= 1; + if(k210_gpiohs_get_value(FPIOA_LCD_MISO)) + rx_data ++; + } + CLK_H(); + return rx_data; +} + +void LCD_CmdWrite(uint8_t cmd) +{ + NCS_L(); + lcd_transfer_byte(0x00); + lcd_transfer_byte(cmd); + NCS_H(); +} + +void LCD_DataWrite(uint8_t data) +{ + NCS_L(); + lcd_transfer_byte(0x80); + lcd_transfer_byte(data); + NCS_H(); +} + +void LCD_DataWrite_Pixel(uint8_t data) +{ + NCS_L(); + lcd_transfer_byte(0x80); + lcd_transfer_byte(data); + NCS_H(); + NCS_L(); + lcd_transfer_byte(0x80); + lcd_transfer_byte(data >> 8); + NCS_H(); +} + +uint8_t LCD_StatusRead(void) +{ + uint8_t temp = 0; + NCS_L(); + lcd_transfer_byte(0x40); + temp = lcd_transfer_byte(0xff); + NCS_H(); + return temp; +} + +uint8_t LCD_DataRead(void) +{ + uint8_t temp = 0; + NCS_L(); + lcd_transfer_byte(0xc0); + temp = lcd_transfer_byte(0xff); + NCS_H(); + return temp; +} + +/*****************************************************************************/ + +void lcd_drv_init(void) +{ + uint8_t PwmControl = 100; + + lcd_pin_init(); + lt768_init(); + Select_SFI_Dual_Mode0(); + + // PWM1 enable backlight + LT768_PWM1_Init(1, 0, 200, 100, PwmControl); + + // enable RGB output + Display_ON(); + + Main_Image_Start_Address(LCD_START_ADDR); + Main_Image_Width(LCD_XSIZE_TFT); + Main_Window_Start_XY(0, 0); + Canvas_Image_Start_address(LCD_START_ADDR); + Canvas_image_width(LCD_XSIZE_TFT); + Active_Window_XY(0, 0); + Active_Window_WH(LCD_XSIZE_TFT, LCD_YSIZE_TFT); + up_mdelay(10); + Canvas_Image_Start_address(LCD_START_ADDR); + + //fill blue background + LT768_DrawSquare_Fill(0, 0, LCD_XSIZE_TFT, LCD_YSIZE_TFT, WHITE); +} + +/**************************************************************************** + * Name: k210_backlight + * + * Description: + * If CONFIG_K210_LCD_BACKLIGHT is defined, then the board-specific + * logic must provide this interface to turn the backlight on and off. + * + ****************************************************************************/ + +#ifdef CONFIG_K210_LCD_BACKLIGHT +void k210_backlight(bool blon) +{ + lcd_backlight_init(blon); +} +#endif + + +/**************************************************************************** + * Name: lcd_open + ****************************************************************************/ +static int lcd_open(FAR struct file *filep) +{ + return OK; +} + +/**************************************************************************** + * Name: lcd_close + ****************************************************************************/ +static int lcd_close(FAR struct file *filep) +{ + return OK; +} + +/**************************************************************************** + * Name: lcd_read + ****************************************************************************/ +static ssize_t lcd_read(FAR struct file *filep, FAR char *buffer, size_t buflen) +{ + return OK; +} + +/**************************************************************************** + * Name: ch438_write + ****************************************************************************/ + static ssize_t lcd_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) +{ + ssize_t ret = buflen; + if (buffer == NULL) + { + return -ERROR; + } + LcdWriteParam * show = (LcdWriteParam *)buffer; + + /* output string */ + switch (show->type) + { + + /* output string */ + case SHOW_STRING: + LT768_DrawSquare_Fill(0, 0, LCD_XSIZE_TFT, LCD_YSIZE_TFT, WHITE); + LT768_Select_Internal_Font_Init(show->string_info.height, 1, 1, 1, 1); + LT768_Print_Internal_Font_String(show->string_info.x_pos, show->string_info.y_pos, show->string_info.font_color,show->string_info.back_color,show->string_info.addr); + break; + + /* output dot */ + case SHOW_WDOT: + LT768_DrawSquare_Fill(0, 0, LCD_XSIZE_TFT, LCD_YSIZE_TFT, WHITE); + LT768_DrawSquare_Fill(show->pixel_info.x_startpos,show->pixel_info.y_startpos, show->pixel_info.x_endpos, show->pixel_info.y_endpos, *(uint32_t *)(show->pixel_info.pixel_color)); + break; + + /* output rgb */ + case SHOW_RGB: + LT768_DrawSquare_Fill(0, 0, LCD_XSIZE_TFT, LCD_YSIZE_TFT, WHITE); + Display_RGB(); + break; + + /* output pip */ + case SHOW_PIP: + LT768_DrawSquare_Fill(0, 0, LCD_XSIZE_TFT, LCD_YSIZE_TFT, WHITE); + Display_PIP(); + break; + + /* output Internal Font */ + case SHOW_INTERNAL_FONT: + LT768_DrawSquare_Fill(0, 0, LCD_XSIZE_TFT, LCD_YSIZE_TFT, WHITE); + Display_Internal_Font(); + break; + + /* output Outside Font */ + case SHOW_OUTSIDE_FONT: + LT768_DrawSquare_Fill(0, 0, LCD_XSIZE_TFT, LCD_YSIZE_TFT, WHITE); + Display_Outside_Font(); + break; + + /* output Triangle */ + case SHOW_TRIANGLE: + LT768_DrawSquare_Fill(0, 0, LCD_XSIZE_TFT, LCD_YSIZE_TFT, WHITE); + Display_Triangle(); + break; + + /* output picture */ + case SHOW_PICTURE: + LT768_DrawSquare_Fill(0, 0, LCD_XSIZE_TFT, LCD_YSIZE_TFT, WHITE); + Display_Picture(); + break; + + default: + ret = -ERROR; + break; + } + + return ret; +} +/**************************************************************************** + * Name: k210_lcd_initialize + * + * Description: + * Initialize the LCD. Setup backlight (initially off) + * + ****************************************************************************/ +int board_lcd_initialize(void) +{ + /* Configure the LCD backlight (and turn the backlight off) */ + lcd_backlight_init(true); + lcd_drv_init(); + up_mdelay(10); + Main_Image_Start_Address(LCD_START_ADDR); + Main_Image_Width(LCD_XSIZE_TFT); + Main_Window_Start_XY(0, 0); + Canvas_Image_Start_address(LCD_START_ADDR); + Canvas_image_width(LCD_XSIZE_TFT); + Active_Window_XY(0, 0); + Active_Window_WH(LCD_XSIZE_TFT, LCD_YSIZE_TFT); + up_mdelay(10); + Canvas_Image_Start_address(LCD_START_ADDR); + /* register device */ + register_driver("/dev/lcd_dev", &g_lcdfops, 0666, NULL); + return OK; +} diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_leds.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_leds.c new file mode 100644 index 000000000..0967b6421 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_leds.c @@ -0,0 +1,47 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file k210_leds.c + * @brief edu-riscv64 k210_leds.c + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.06.08 + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "k210_fpioa.h" +#include "k210_gpiohs.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void board_autoled_initialize(void) +{ +} + +void board_autoled_on(int led) +{ +} + +void board_autoled_off(int led) +{ +} diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_reset.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_reset.c new file mode 100644 index 000000000..bb96e664c --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_reset.c @@ -0,0 +1,60 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file k210_reset.c + * @brief k210_reset.c support reboot + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.06.27 + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#ifdef CONFIG_BOARDCTL_RESET + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_reset + * + * Description: + * Reset board. Support for this function is required by board-level + * logic if CONFIG_BOARDCTL_RESET is selected. + * + * Input Parameters: + * status - Status information provided with the reset event. This + * meaning of this status information is board-specific. If not + * used by a board, the value zero may be provided in calls to + * board_reset(). + * + * Returned Value: + * If this function returns, then it was not possible to power-off the + * board due to some constraints. The return value int this case is a + * board-specific reason for the failure to shutdown. + * + ****************************************************************************/ +int board_reset(int status) +{ + up_systemreset(); + return 0; +} + +#endif /* CONFIG_BOARDCTL_RESET */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_touch.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_touch.c new file mode 100644 index 000000000..288de316c --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_touch.c @@ -0,0 +1,475 @@ +/* +* Copyright (c) 2022 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file k210_touch.c + * @brief gt911 touch driver + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.10.25 + */ + + +/**************************************************************************** + * Included Files + ****************************************************************************/ +#include "k210_touch.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +static void IIC_Init(void); +static void SDA_IN(void); +static void SDA_OUT(void); +static uint8_t READ_SDA(void); +static void IIC_SCL(uint8_t val); +static void IIC_SDA(uint8_t val); +static void IIC_Start(void); +static void IIC_Stop(void); +static uint8_t IIC_Wait_Ack(void); +static void IIC_Ack(void); +static void IIC_NAck(void); +static void IIC_Send_Byte(uint8_t txd); +static uint8_t IIC_Read_Byte(uint8_t ack); +static bool GT911_Scan(POINT* point); + +static int touch_open(FAR struct file *filep); +static int touch_close(FAR struct file *filep); +static ssize_t touch_read(FAR struct file *filep, FAR char *buffer, size_t buflen); +static ssize_t touch_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); + +/**************************************************************************** + * Private Data + ****************************************************************************/ +/* touch POSIX interface */ +static const struct file_operations g_touchfops = +{ + touch_open, + touch_close, + touch_read, + touch_write, + NULL, + NULL, + NULL +}; + +/**************************************************************************** + * Name: IIC_Init + * Description: i2c pin mode configure + * input: None + * output: None + * return:none + ****************************************************************************/ +static void IIC_Init(void) +{ + /* config simluate IIC bus */ + k210_fpioa_config(BSP_IIC_SDA, GT911_FUNC_GPIO(FPIOA_IIC_SDA)); + k210_fpioa_config(BSP_IIC_SCL, GT911_FUNC_GPIO(FPIOA_IIC_SCL)); + + k210_gpiohs_set_direction(FPIOA_IIC_SDA, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_IIC_SCL, GPIO_DM_OUTPUT); +} + +/**************************************************************************** + * Name: SDA_IN + * Description: set sda input mode + * input: None + * output: None + * return:none + ****************************************************************************/ +static void SDA_IN(void) +{ + k210_gpiohs_set_direction(FPIOA_IIC_SDA, GPIO_DM_INPUT_PULL_UP); +} + +/**************************************************************************** + * Name: SDA_OUT + * Description: set sda output mode + * input: None + * output: None + * return:none + ****************************************************************************/ +static void SDA_OUT(void) +{ + k210_gpiohs_set_direction(FPIOA_IIC_SDA, GPIO_DM_OUTPUT); +} + +/**************************************************************************** + * Name: READ_SDA + * Description: read sda value + * input: None + * output: None + * return: sda pin value + ****************************************************************************/ +static uint8_t READ_SDA(void) +{ + return k210_gpiohs_get_value(FPIOA_IIC_SDA); +} + +/**************************************************************************** + * Name: IIC_SCL + * Description: set the value of scl + * input: val:the value to be set + * output: None + * return: None + ****************************************************************************/ +static void IIC_SCL(uint8_t val) +{ + if (val) + k210_gpiohs_set_value(FPIOA_IIC_SCL,GPIO_PV_HIGH); + else + { + k210_gpiohs_set_value(FPIOA_IIC_SCL,GPIO_PV_LOW); + } +} + +/**************************************************************************** + * Name: IIC_SDA + * Description: set the value of sda + * input: val:the value to be set + * output: None + * return: None + ****************************************************************************/ +static void IIC_SDA(uint8_t val) +{ + if (val) + k210_gpiohs_set_value(FPIOA_IIC_SDA,GPIO_PV_HIGH); + else + { + k210_gpiohs_set_value(FPIOA_IIC_SDA,GPIO_PV_LOW); + } +} + +/**************************************************************************** + * Name: IIC_Start + * Description: Generate i2c start signal + * input: None + * output: None + * return: None + ****************************************************************************/ +static void IIC_Start(void) +{ + SDA_OUT(); + IIC_SDA(1); + IIC_SCL(1); + up_mdelay(30); + IIC_SDA(0); + up_mdelay(2); + IIC_SCL(0); +} + +/**************************************************************************** + * Name: IIC_Start + * Description: Generate i2c stop signal + * input: None + * output: None + * return: None + ****************************************************************************/ +static void IIC_Stop(void) +{ + SDA_OUT(); + IIC_SCL(1); + up_mdelay(30); + IIC_SDA(0); + up_mdelay(2); + IIC_SDA(1); +} + +/******************************************************************************************* + * Name: IIC_Wait_Ack + * Description: Wait for the reply signal to arrive + * input: None + * output: None + * return: Return value: 1:failed to receive response,0:the received response is successful. +********************************************************************************************/ +static uint8_t IIC_Wait_Ack(void) +{ + uint16_t ucErrTime=0; + SDA_IN(); + IIC_SDA(1); + IIC_SCL(1); + up_mdelay(2); + while(READ_SDA()) + { + ucErrTime++; + if(ucErrTime>2500) + { + IIC_Stop(); + return 1; + } + up_mdelay(2); + } + IIC_SCL(0); + return 0; +} + +/**************************************************************************** + * Name: IIC_Ack + * Description: generate ack response + * input: None + * output: None + * return: None + ****************************************************************************/ +static void IIC_Ack(void) +{ + IIC_SCL(0); + SDA_OUT(); + up_mdelay(2); + IIC_SDA(0); + up_mdelay(2); + IIC_SCL(1); + up_mdelay(2); + IIC_SCL(0); +} + +/**************************************************************************** + * Name: IIC_NAck + * Description: No ACK response is generated + * input: None + * output: None + * return: None + ****************************************************************************/ +static void IIC_NAck(void) +{ + IIC_SCL(0); + SDA_OUT(); + up_mdelay(2); + IIC_SDA(1); + up_mdelay(2); + IIC_SCL(1); + up_mdelay(2); + IIC_SCL(0); +} + +/**************************************************************************** + * Name: IIC_Send_Byte + * Description: IIC sends a byte,Return whether the slave has a response + * input: None + * output: None + * return: 1:there is a response,0:no response + ****************************************************************************/ +static void IIC_Send_Byte(uint8_t txd) +{ + uint8_t t; + SDA_OUT(); + IIC_SCL(0); + up_mdelay(2); + for(t=0;t<8;t++) + { + IIC_SDA((txd&0x80)>>7); + txd<<=1; + IIC_SCL(1); + up_mdelay(2); + IIC_SCL(0); + up_mdelay(2); + } +} + +/**************************************************************************** + * Name: IIC_Read_Byte + * Description: Read 1 byte, when ack=1, send ACK, when ack=0, send nACK + * input: None + * output: None + * return: Returns one byte of data read + ****************************************************************************/ +static uint8_t IIC_Read_Byte(uint8_t ack) +{ + uint8_t i,receive=0; + SDA_IN(); + up_mdelay(30); + for(i=0;i<8;i++ ) + { + IIC_SCL(0); + up_mdelay(2); + IIC_SCL(1); + up_udelay(1); + receive<<=1; + if(READ_SDA())receive++; + up_udelay(1); + } + if (!ack) + IIC_NAck(); + else + IIC_Ack(); + return receive; +} + +/*********************************************************************************** + * Name: GT911_WR_Reg + * Description: Write data to GT911 once + * input: reg: start register address,buf: data cache area,len: write data length + * output: None + * return: Return value: 0, success; 1, failure. + ***********************************************************************************/ +static uint8_t GT911_WR_Reg(uint16_t reg,uint8_t *buf,uint8_t len) +{ + uint8_t i; + uint8_t ret=0; + IIC_Start(); + IIC_Send_Byte(CT_CMD_WR); + IIC_Wait_Ack(); + IIC_Send_Byte(reg>>8); + IIC_Wait_Ack(); + IIC_Send_Byte(reg&0XFF); + IIC_Wait_Ack(); + for(i=0;i>8); + IIC_Wait_Ack(); + IIC_Send_Byte(reg&0XFF); + IIC_Wait_Ack(); + IIC_Stop(); + + IIC_Start(); + IIC_Send_Byte(CT_CMD_RD); + IIC_Wait_Ack(); + for(i=0;i 5) || (Dev_Now.TouchCount == 0) ) + { + GT911_WR_Reg(GT911_READ_XY_REG, (uint8_t *)&Clearbuf, 1); + return false; + } + GT911_RD_Reg(GT911_READ_XY_REG + 1, &buf[1], Dev_Now.TouchCount*8); + GT911_WR_Reg(GT911_READ_XY_REG, (uint8_t *)&Clearbuf, 1); + + for (uint8_t i = 0;i < Dev_Now.TouchCount; i++) + { + Dev_Now.Touchkeytrackid[i] = buf[1+(8*i)]; + Dev_Now.X[i] = ((uint16_t)buf[3+(8*i)] << 8) + buf[2+(8*i)]; + Dev_Now.Y[i] = ((uint16_t)buf[5+(8*i)] << 8) + buf[4+(8*i)]; + Dev_Now.S[i] = ((uint16_t)buf[7+(8*i)] << 8) + buf[6+(8*i)]; + + + if(Dev_Now.Y[i] < 20) Dev_Now.Y[i] = 20; + if(Dev_Now.Y[i] > GT911_MAX_HEIGHT -20) Dev_Now.Y[i]=GT911_MAX_HEIGHT - 20; + if(Dev_Now.X[i] < 20) Dev_Now.X[i] = 20; + if(Dev_Now.X[i] > GT911_MAX_WIDTH-20) Dev_Now.X[i] = GT911_MAX_WIDTH - 20; + point->x = Dev_Now.X[i]; + point->y = Dev_Now.Y[i]; + } + } + return true; +} + +/**************************************************************************** + * Name: touch_open + ****************************************************************************/ +static int touch_open(FAR struct file *filep) +{ + return OK; +} + +/**************************************************************************** + * Name: touch_close + ****************************************************************************/ +static int touch_close(FAR struct file *filep) +{ + return OK; +} + +/**************************************************************************** + * Name: lcd_read + ****************************************************************************/ +static ssize_t touch_read(FAR struct file *filep, FAR char *buffer, size_t buflen) +{ + int ret = -ERROR; + POINT touch_point = {0, 0, 0}; + + if (buffer == NULL) + { + return -ERROR; + } + + POINT* data = (POINT*)buffer; + while(1) + { + if(GT911_Scan(&touch_point)) + { + data->x = touch_point.x; + data->y = touch_point.y; + ret = buflen; + break; + } + } + return ret; +} + +/**************************************************************************** + * Name: lcd_read + ****************************************************************************/ +static ssize_t touch_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) +{ + return OK; +} + +/*********************************************************************************** + * Name: board_touch_initialize + * Description: touch initialize + * input: None + * output: None + * return: None + ***********************************************************************************/ +void board_touch_initialize(void) +{ + IIC_Init(); + /* register device */ + register_driver("/dev/touch_dev", &g_touchfops, 0666, NULL); +} \ No newline at end of file diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_touch.h b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_touch.h new file mode 100644 index 000000000..0956f2fce --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_touch.h @@ -0,0 +1,75 @@ +/* +* Copyright (c) 2022 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file k210_touch.h + * @brief gt911 touch driver + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.10.25 + */ + +#ifndef _K210_TOUCH_H_ +#define _K210_TOUCH_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "k210_config.h" +#include "k210_fpioa.h" +#include "k210_gpiohs.h" +#include "nuttx/arch.h" +#include "k210_gpio_common.h" + +#define GT911_FUNC_GPIO(n) ((K210_IO_FUNC_GPIOHS0 + n) | K210_IOFLAG_GPIOHS) + +#define GT911_MAX_WIDTH (uint16_t)800 +#define GT911_MAX_HEIGHT (uint16_t)480 +#define CT_CMD_WR (uint8_t)0XBA +#define CT_CMD_RD (uint8_t)0XBB +#define CT_MAX_TOUCH (uint8_t)5 +#define GT911_COMMAND_REG (uint16_t)0x8040 +#define GT911_CONFIG_REG (uint16_t)0x8047 +#define GT911_PRODUCT_ID_REG (uint16_t)0x8140 +#define GT911_FIRMWARE_VERSION_REG (uint16_t)0x8144 +#define GT911_READ_XY_REG (uint16_t)0x814E + +typedef struct +{ + uint8_t TouchCount; + uint8_t Touchkeytrackid[CT_MAX_TOUCH]; + uint16_t X[CT_MAX_TOUCH]; + uint16_t Y[CT_MAX_TOUCH]; + uint16_t S[CT_MAX_TOUCH]; +}GT911_Dev; + +typedef struct +{ + uint16_t x; + uint16_t y; + uint16_t press; +}POINT; + +void board_touch_initialize(void); + +#endif diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_w5500.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_w5500.c new file mode 100644 index 000000000..755404f91 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_w5500.c @@ -0,0 +1,840 @@ +/* +* Copyright (c) 2022 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file k210_w5500.c +* @brief w5500 driver based on simulated SPI +* @version 1.0 +* @author AIIT XUOS Lab +* @date 2022-9-14 +*/ + +#include "nuttx/arch.h" +#include "k210_w5500.h" +#include "k210_gpio_common.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ +w5500_param_t w5500_param; + +static uint8_t rx_buf[256]; +static uint8_t tx_buf[256]; + +static uint8_t config_ip_addr[] = {10, 0, 30, 50}; +static uint8_t config_ip_mask[] = {255, 255, 255, 0}; +static uint8_t config_gw_addr[] = {10, 0, 30, 1}; +static uint8_t config_mac_addr[] = {0x0C, 0x29, 0xAB, 0x7C, 0x00, 0x01}; +static uint8_t config_dst_ip[] = {10, 0, 30, 57}; +static uint16_t config_local_port = 5000; +static uint16_t config_dst_port = 6000; +static uint8_t config_mode = SOCK_TCP_CLI; + +/**************************************************************************** + * Name: spi_read_byte + * Description: Read one byte spi data returned + * input: None + * output: None + * return:Read register data + ****************************************************************************/ +static uint8_t spi_read_byte(void) +{ + uint8_t i, dat = 0; + SCLK_L(); + + for(i = 0; i < 8; i++) + { + SCLK_H(); + dat <<= 1; + dat |= k210_gpiohs_get_value(FPIOA_ENET_MISO); + up_udelay(1); + SCLK_L(); + } + + return dat; +} + +/**************************************************************************** + * Name: spi_write_byte + * Description: send 1 byte to spi + * input: data + * output: None + * return: None + ****************************************************************************/ +static void spi_write_byte(uint8_t dat) +{ + uint8_t i; + + for(i = 0; i < 8; i++) + { + SCLK_L(); + + if((dat << i) & 0x80) + { + MOSI_H(); + } + else + { + MOSI_L(); + } + + SCLK_H(); + } + + SCLK_L(); +} + +/**************************************************************************** + * Name: spi_write_short + * Description: send 2 bytes to spi + * input: data + * output: None + * return: None + ****************************************************************************/ +static void spi_write_short(uint16_t dat) +{ + spi_write_byte((uint8_t)(dat / 256)); + spi_write_byte(dat); +} + +/**************************************************************************** + * Name: w5500_write_byte + *Description: Write 1 byte data to the specified address register through SPI + *Input: reg: 16 bit register address, dat: data to be written + * output: None + * return: None + ****************************************************************************/ +static void w5500_write_byte(uint16_t reg, uint8_t dat) +{ + NCS_L(); + spi_write_short(reg); + spi_write_byte(FDM1 | RWB_WRITE | COMMON_R); + spi_write_byte(dat); + NCS_H(); +} + +/**************************************************************************** + * Name: w5500_write_short + * Description: Write 2 bytes data to the specified address register through SPI + * Input: reg: 16 bit register address, dat: data to be written + * output: None + * return: None + ****************************************************************************/ +static void w5500_write_short(uint16_t reg, uint16_t dat) +{ + NCS_L(); + spi_write_short(reg); + spi_write_byte(FDM2 | RWB_WRITE | COMMON_R); + spi_write_short(dat); + NCS_H(); +} + +/**************************************************************************** + * Name: w5500_write_bytes + * Description: Write n bytes data to the specified address register through SPI + * Input: reg: 16 bit register address, dat: data to be written,size:Length of data to be written + * output: None + * return: None + ****************************************************************************/ +static void w5500_write_bytes(uint16_t reg, uint8_t *dat, uint16_t size) +{ + uint16_t i; + NCS_L(); + spi_write_short(reg); + spi_write_byte(VDM | RWB_WRITE | COMMON_R); + + for(i = 0; i < size; i++) + { + spi_write_byte(*dat++); + } + + NCS_H(); +} + +/**************************************************************************** + * Name: w5500_write_sock_byte + * Description: Write 1 byte data to the specified port register through SPI + * Input: sock: port number, reg: 16 bit register address, dat: data to be written + * Output: None + * return: None + ****************************************************************************/ +void w5500_write_sock_byte(socket_t sock, uint16_t reg, uint8_t dat) +{ + NCS_L(); + spi_write_short(reg); + spi_write_byte(FDM1 | RWB_WRITE | (sock * 0x20 + 0x08)); + spi_write_byte(dat); + NCS_H(); +} + +/**************************************************************************** + * Name: w5500_write_sock_short + * Description: Write 2 bytes data to the specified port register through SPI + * Input: sock: port number, reg: 16 bit register address, dat: data to be written + * Output: None + * return: None + ****************************************************************************/ +void w5500_write_sock_short(socket_t sock, uint16_t reg, uint16_t dat) +{ + NCS_L(); + spi_write_short(reg); + spi_write_byte(FDM2 | RWB_WRITE | (sock * 0x20 + 0x08)); + spi_write_short(dat); + NCS_H(); +} + +/**************************************************************************** + * Name: w5500_write_sock_long + * Description: Write 4 bytes data to the specified port register through SPI + * Input: sock: port number, reg: 16 bit register address, dat: 4 byte buffer pointers to be written + * Output: None + * return: None + ****************************************************************************/ +void w5500_write_sock_long(socket_t sock, uint16_t reg, uint8_t *dat) +{ + NCS_L(); + spi_write_short(reg); + spi_write_byte(FDM4 | RWB_WRITE | (sock * 0x20 + 0x08)); + spi_write_byte(*dat++); + spi_write_byte(*dat++); + spi_write_byte(*dat++); + spi_write_byte(*dat++); + NCS_H(); +} + +/******************************************************************************* +*Function name: w5500_read_byte +*Description: Read 1 byte data of W5500 specified address register +*Input: reg: 16 bit register address +*Output: None +*Return : 1 byte data read from the register +*******************************************************************************/ +uint8_t w5500_read_byte(uint16_t reg) +{ + uint8_t val; + NCS_L(); + spi_write_short(reg); + spi_write_byte(FDM1 | RWB_READ | COMMON_R); + val = spi_read_byte(); + NCS_H(); + return val; +} + +/******************************************************************************* +*Function name: w5500_read_sock_byte +*Description: Read 1 byte data of W5500 specified port register +*Input: sock: port number, reg: 16 bit register address +*Output: None +*Return: 1 byte data read from the register +*Description: None +*******************************************************************************/ +uint8_t w5500_read_sock_byte(socket_t sock, uint16_t reg) +{ + uint8_t val; + NCS_L(); + spi_write_short(reg); + spi_write_byte(FDM1 | RWB_READ | (sock * 0x20 + 0x08)); + val = spi_read_byte(); + NCS_H(); + return val; +} + +/******************************************************************************* +*Function name: w5500_read_sock_short +*Description: Read 2 bytes of W5500 specified port register +*Input: sock: port number, reg: 16 bit register address +*Output: None +*Return: read 2 bytes of data from the register (16 bits) +*******************************************************************************/ +uint16_t w5500_read_sock_short(socket_t sock, uint16_t reg) +{ + uint16_t val; + NCS_L(); + spi_write_short(reg); + spi_write_byte(FDM2 | RWB_READ |(sock * 0x20 + 0x08)); + val = spi_read_byte(); + val *= 256; + val |= spi_read_byte(); + NCS_H(); + return val; +} + +/******************************************************************************* +*Function name: w5500_read_sock_bytes +*Description: Read data from W5500 receive data buffer +*Input: sock: port number, * dat: data saving buffer pointer +*Output: None +*Return: read data length +*******************************************************************************/ +uint16_t w5500_read_sock_bytes(socket_t sock, uint8_t *dat) +{ + uint16_t recv_size, write_addr; + uint16_t recv_addr; + uint16_t i; + uint8_t val; + recv_size = w5500_read_sock_short(sock, W5500_SN_RX_RSR_REG); + + /* no receive data */ + if(recv_size == 0) + { + return 0; + } + + if(recv_size > W5500_MAX_PACK_SIZE) + { + recv_size = W5500_MAX_PACK_SIZE; + } + + recv_addr = w5500_read_sock_short(sock, W5500_SN_RX_RD_REG); + write_addr = recv_addr; + + /* calculate physical address */ + recv_addr &= (SOCK_RECV_SIZE - 1); + NCS_L(); + spi_write_short(recv_addr); + spi_write_byte(VDM | RWB_READ | (sock * 0x20 + 0x18)); + + if((recv_addr + recv_size) < SOCK_RECV_SIZE) + { + for(i = 0; i < recv_size; i++) + { + val = spi_read_byte(); + *dat = val; + dat++; + } + } + else + { + recv_addr = SOCK_RECV_SIZE - recv_addr; + + for(i = 0; i < recv_addr; i++) + { + val = spi_read_byte(); + *dat = val; + dat++; + } + + NCS_H(); + NCS_L(); + spi_write_short(0x00); + spi_write_byte(VDM | RWB_READ | (sock * 0x20 + 0x18)); + + for(; i < recv_size; i++) + { + val= spi_read_byte(); + *dat = val; + dat++; + } + } + + NCS_H(); + write_addr += recv_size; + + w5500_write_sock_short(sock, W5500_SN_RX_RD_REG, write_addr); + /* start receive */ + w5500_write_sock_byte(sock, W5500_SN_CR_REG, SN_CR_RECV); + return recv_size; +} + +/******************************************************************************* +*Function name: w5500_write_sock_bytes +*Description: Write data to the data sending buffer of W5500 +*Input: sock: port number, dat: data storage buffer pointer, size: length of data to be written +*Output: None +*Return: None +*******************************************************************************/ +void w5500_write_sock_bytes(socket_t sock, uint8_t *dat, uint16_t size) +{ + uint16_t recv_addr, write_addr; + uint16_t i; + + /* if udp mode, set ip and port */ + if(w5500_read_sock_byte(sock, W5500_SN_MR_REG) != SOCK_UDP) + { + w5500_write_sock_long(sock, W5500_SN_DIPR_REG, w5500_param.udp_ip); + w5500_write_sock_short(sock, W5500_SN_DPORTR_REG, w5500_param.udp_port); + } + + recv_addr = w5500_read_sock_short(sock, W5500_SN_TX_WR_REG); + write_addr = recv_addr; + recv_addr &= (SOCK_SEND_SIZE - 1); + + NCS_L(); + spi_write_short(recv_addr); + spi_write_byte(VDM | RWB_WRITE | (sock * 0x20 + 0x10)); + + if((recv_addr + size) < SOCK_SEND_SIZE) + { + for(i = 0; i < size; i++) + { + spi_write_byte(*dat++); + } + } + else + { + recv_addr = SOCK_SEND_SIZE - recv_addr; + + for(i = 0; i < recv_addr; i++) + { + spi_write_byte(*dat++); + } + + NCS_H(); + NCS_L(); + + spi_write_short(0x00); + spi_write_byte(VDM | RWB_WRITE | (sock * 0x20 + 0x10)); + + for(; i < size; i++) + { + spi_write_byte(*dat++); + } + } + + NCS_H(); + write_addr += size; + + w5500_write_sock_short(sock, W5500_SN_TX_WR_REG, write_addr); + w5500_write_sock_byte(sock, W5500_SN_CR_REG, SN_CR_SEND); +} + +/******************************************************************************* +*Function name: w5500_reset +*Description: Hardware reset W5500 +*Input: None +*Output: None +*Return value: None +*Note: The reset pin of the W5500 can be encircled only when the low level is at least 500us +*******************************************************************************/ +void w5500_reset(void) +{ + uint8_t dat = 0; + + RST_L(); + RST_H(); + + /* wait connect ok */ + while((dat & LINK) == 0) + { + up_mdelay(500); + dat = w5500_read_byte(W5500_PHYCFGR_REG); + } +} + +/******************************************************************************* +*Function name: w5500_config_init +*Description: Initialize W5500 register functions +*Input: None +*Output: None +*Return value: None +*Note: Before using W5500, initialize W5500 +*******************************************************************************/ +void w5500_config_init(void) +{ + uint8_t i = 0; + + /* software reset, set 1 and auto clear 0 */ + w5500_write_byte(W5500_MR_REG, MR_RST); + up_mdelay(100); + + w5500_write_bytes(W5500_GAR_REG, w5500_param.gw_addr, 4); + w5500_write_bytes(W5500_SUBR_REG, w5500_param.ip_mask, 4); + w5500_write_bytes(W5500_SHAR_REG, w5500_param.mac_addr, 6); + w5500_write_bytes(W5500_SIPR_REG, w5500_param.ip_addr, 4); + + /* set socket rx and tx memory size 2k */ + for(i = 0; i < 8; i++) + { + w5500_write_sock_byte(i, W5500_SN_RXBUF_SIZE_REG, 0x02); + w5500_write_sock_byte(i, W5500_SN_TXBUF_SIZE_REG, 0x02); + } + + /* set retry time 200ms (0x07d0 = 2000) */ + w5500_write_short(W5500_RTR_REG, 0x07d0); + + /* retry time with 8, when exceed it, produce overtime interrupt, set W5500_SN_IR_REG(TIMEOUT) */ + w5500_write_byte(W5500_RCR_REG, 8); +} + +/******************************************************************************* +*Function name: Detect_Gateway +*Description: Check the gateway server +*Input: None +*Output: None +*Return value: TRUE (0xFF) for success, FALSE (0x00) for failure +*******************************************************************************/ +uint8_t w5500_detect_gateway(void) +{ + uint8_t ip_addr[4] = {w5500_param.ip_addr[0] + 1, w5500_param.ip_addr[1] + 1, w5500_param.ip_addr[2] + 1, w5500_param.ip_addr[3] + 1}; + + /* check gateway and get gateway phyiscal address */ + w5500_write_sock_long(0, W5500_SN_DIPR_REG, ip_addr); + w5500_write_sock_byte(0, W5500_SN_MR_REG, SN_MR_TCP); + w5500_write_sock_byte(0, W5500_SN_CR_REG, SN_CR_OPEN); + up_mdelay(5); + + if(w5500_read_sock_byte(0, W5500_SN_SR_REG) != SOCK_INIT) + { + w5500_write_sock_byte(0, W5500_SN_CR_REG, SN_CR_CLOSE); + return FALSE; + } + + /* set socket connection mode */ + w5500_write_sock_byte(0, W5500_SN_CR_REG, SN_CR_CONNECT); + + do + { + uint8_t val = 0; + /* read socket0 interrupt register */ + val = w5500_read_sock_byte(0, W5500_SN_IR_REG); + + if(val != 0) + { + w5500_write_sock_byte(0, W5500_SN_IR_REG, val); + } + + up_mdelay(5); + + if((val & IR_TIMEOUT) == IR_TIMEOUT) + { + return FALSE; + } + else if(w5500_read_sock_byte(0, W5500_SN_DHAR_REG) != 0xff) + { + /* close socket */ + w5500_write_sock_byte(0, W5500_SN_CR_REG, SN_CR_CLOSE); + return TRUE; + } + } while(1); + return TRUE; +} + +/******************************************************************************* +*Function name: w5500_socket_init +*Description: Specify Socket (0~7) initialization +*Input: sock: port to be initialized +*Output: None +*Return value: None +*******************************************************************************/ +void w5500_socket_init(socket_t sock) +{ + /* max partition bytes = 30 */ + w5500_write_sock_short(0, W5500_SN_MSSR_REG, 30); + + switch(sock) + { + case 0: + w5500_write_sock_short(0, W5500_SN_PORT_REG, w5500_param.sock.local_port); + w5500_write_sock_short(0, W5500_SN_DPORTR_REG, w5500_param.sock.dst_port); + w5500_write_sock_long(0, W5500_SN_DIPR_REG, w5500_param.sock.dst_ip); + break; + + default: + break; + } +} + +/******************************************************************************* +*Function name: w5500_socket_connect +*Description: Set the specified Socket (0~7) as the client to connect with the remote server +*Input: sock: port to be set +*Output: None +*Return value: TRUE (0xFF) for success, FALSE (0x00) for failure +*******************************************************************************/ +uint8_t w5500_socket_connect(socket_t sock) +{ + w5500_write_sock_byte(sock, W5500_SN_MR_REG, SN_MR_TCP); + w5500_write_sock_byte(sock, W5500_SN_CR_REG, SN_CR_OPEN); + up_mdelay(5); + + if(w5500_read_sock_byte(sock, W5500_SN_SR_REG) != SOCK_INIT) + { + w5500_write_sock_byte(sock, W5500_SN_CR_REG, SN_CR_CLOSE); + return FALSE; + } + + w5500_write_sock_byte(sock, W5500_SN_CR_REG, SN_CR_CONNECT); + return TRUE; +} + +/******************************************************************************* +*Function name: w5500_socket_listen +*Description: Set the specified Socket (0~7) as the server to wait for the connection of the remote host +*Input: sock: port to be set +*Output: None +*Return value: TRUE (0xFF) for success, FALSE (0x00) for failure +*******************************************************************************/ +uint8_t w5500_socket_listen(socket_t sock) +{ + w5500_write_sock_byte(sock, W5500_SN_MR_REG, SN_MR_TCP); + w5500_write_sock_byte(sock, W5500_SN_CR_REG, SN_CR_OPEN); + up_mdelay(5); + + if(w5500_read_sock_byte(sock, W5500_SN_SR_REG) != SOCK_INIT) + { + w5500_write_sock_byte(sock, W5500_SN_CR_REG, SN_CR_CLOSE); + return FALSE; + } + + w5500_write_sock_byte(sock, W5500_SN_CR_REG, SN_CR_LISTEN); + up_mdelay(5); + + if(w5500_read_sock_byte(sock, W5500_SN_SR_REG) != SOCK_LISTEN) + { + w5500_write_sock_byte(sock, W5500_SN_CR_REG, SN_CR_CLOSE); + return FALSE; + } + + return TRUE; +} + +/******************************************************************************* +*Function name: w5500_socket_set_udp +*Description: Set the specified Socket (0~7) to UDP mode +*Input: sock: port to be set +*Output: None +*Return value: TRUE (0xFF) for success, FALSE (0x00) for failure +*******************************************************************************/ +uint8_t w5500_socket_set_udp(socket_t sock) +{ + w5500_write_sock_byte(sock, W5500_SN_MR_REG, SN_MR_UDP); + w5500_write_sock_byte(sock, W5500_SN_CR_REG, SN_CR_OPEN); + up_mdelay(5); + + if(w5500_read_sock_byte(sock, W5500_SN_SR_REG) != SOCK_UDP) + { + w5500_write_sock_byte(sock, W5500_SN_CR_REG, SN_CR_CLOSE); + return FALSE; + } + return TRUE; +} + +/******************************************************************************* +*Function name: w5500_irq_process +*Description: W5500 interrupt handler framework +*Input: None +*Output: None +*Return value: None +*Description: None +*******************************************************************************/ +void w5500_irq_process(void) +{ + uint8_t ir_flag, sn_flag; + ir_flag = w5500_read_byte(W5500_SIR_REG); + do + { + /* handle socket0 event */ + if((ir_flag & S0_INT) == S0_INT) + { + sn_flag = w5500_read_sock_byte(0, W5500_SN_IR_REG); + w5500_write_sock_byte(0, W5500_SN_IR_REG, sn_flag); + + if(sn_flag & IR_CON) + { + /* socket connection finished */ + w5500_param.sock.flag |= SOCK_FLAG_CONN; + } + + if(sn_flag & IR_DISCON) + { + /* disconnect state */ + w5500_write_sock_byte(0, W5500_SN_CR_REG, SN_CR_CLOSE); + w5500_socket_init(0); + w5500_param.sock.flag = 0; + } + + if(sn_flag & IR_SEND_OK) + { + /* send one package ok */ + w5500_param.sock.state |= SOCK_STAT_SEND; + } + + if(sn_flag & IR_RECV) + { + w5500_param.sock.state |= SOCK_STAT_RECV; + } + + if(sn_flag & IR_TIMEOUT) + { + /* close socket, connection failed */ + w5500_write_sock_byte(0, W5500_SN_CR_REG, SN_CR_CLOSE); + w5500_param.sock.flag = 0; + } + } + ir_flag = w5500_read_byte(W5500_SIR_REG); + }while(ir_flag); +} + +/******************************************************************************* +*Function name: w5500_intialization +*Description: W5500 initial configuration +*Input: None +*Output: None +*Return value: None +*******************************************************************************/ +void w5500_intialization(void) +{ + w5500_config_init(); + w5500_detect_gateway(); + w5500_socket_init(0); +} + +/******************************************************************************* +*Function name: w5500_load_param +*Description: load param to w5500_param +*Input: None +*Output: None +*Return value: None +*******************************************************************************/ +void w5500_load_param(void) +{ + w5500_param_t *param = &w5500_param; + memcpy(param->ip_addr, config_ip_addr, sizeof(config_ip_addr)); + memcpy(param->ip_mask, config_ip_mask, sizeof(config_ip_mask)); + memcpy(param->gw_addr, config_gw_addr, sizeof(config_gw_addr)); + memcpy(param->mac_addr, config_mac_addr, sizeof(config_mac_addr)); + memcpy(param->sock.dst_ip, config_dst_ip, sizeof(config_dst_ip)); + param->sock.local_port = config_local_port; + param->sock.dst_port = config_dst_port; + param->sock.mode = config_mode; +} + +/******************************************************************************* +*Function name: w5500_socket_config +*Description: W5500 port initialization configuration +*Input: None +*Output: None +*Return value: None +*******************************************************************************/ +void w5500_socket_config(void) +{ + if(w5500_param.sock.flag == 0) + { + /* TCP Sever */ + if(w5500_param.sock.mode == SOCK_TCP_SVR) + { + if(w5500_socket_listen(0) == TRUE) + w5500_param.sock.flag = SOCK_FLAG_INIT; + else + w5500_param.sock.flag = 0; + } + + /* TCP Client */ + else if(w5500_param.sock.mode == SOCK_TCP_CLI) + { + if(w5500_socket_connect(0) == TRUE) + w5500_param.sock.flag = SOCK_FLAG_INIT; + else + w5500_param.sock.flag = 0; + } + + /* UDP */ + else + { + if(w5500_socket_set_udp(0) == TRUE) + w5500_param.sock.flag = SOCK_FLAG_INIT|SOCK_FLAG_CONN; + else + w5500_param.sock.flag = 0; + } + } +} + +/******************************************************************************* +*Function name: Process_Socket_Data +*Description: W5500 receives and sends the received data +*Input: sock: port number +*Output: None +*Return value: receive data length +*******************************************************************************/ +uint16_t Process_Socket_Data(socket_t sock) +{ + uint16_t size; + size = w5500_read_sock_bytes(sock, rx_buf); + memcpy(tx_buf, rx_buf, size); + w5500_write_sock_bytes(sock, tx_buf, size); + + return size; + +} + +/**************************************************************************** + * Name: SPI_Configuration + * Description: spi pin mode configure + * input: None + * output: None + * return:none + ****************************************************************************/ +void SPI_Configuration(void) +{ + /* config simluate SPI bus */ + k210_fpioa_config(BSP_ENET_SCLK, HS_GPIO(FPIOA_ENET_SCLK) | K210_IOFLAG_GPIOHS); + k210_fpioa_config(BSP_ENET_NRST, HS_GPIO(FPIOA_ENET_NRST) | K210_IOFLAG_GPIOHS); + k210_fpioa_config(BSP_ENET_MOSI, HS_GPIO(FPIOA_ENET_MOSI) | K210_IOFLAG_GPIOHS); + k210_fpioa_config(BSP_ENET_MISO, HS_GPIO(FPIOA_ENET_MISO) | K210_IOFLAG_GPIOHS); + k210_fpioa_config(BSP_ENET_NCS, HS_GPIO(FPIOA_ENET_NCS) | K210_IOFLAG_GPIOHS); + k210_fpioa_config(BSP_ENET_NINT, HS_GPIO(FPIOA_ENET_NINT) | K210_IOFLAG_GPIOHS); + + k210_gpiohs_set_direction(FPIOA_ENET_MISO, GPIO_DM_INPUT); + k210_gpiohs_set_direction(FPIOA_ENET_NRST, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_ENET_SCLK, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_ENET_MOSI, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_ENET_NCS, GPIO_DM_OUTPUT); + k210_gpiohs_set_direction(FPIOA_ENET_NINT, GPIO_DM_INPUT); + + k210_gpiohs_set_value(FPIOA_ENET_SCLK, GPIO_PV_HIGH); + k210_gpiohs_set_value(FPIOA_ENET_MOSI, GPIO_PV_HIGH); + k210_gpiohs_set_value(FPIOA_ENET_NCS, GPIO_PV_LOW); + k210_gpiohs_set_value(FPIOA_ENET_NRST, GPIO_PV_HIGH); +} + +void w5500_test(void) +{ + uint8_t cnt = 0; + uint8_t length = 0; + SPI_Configuration(); + w5500_load_param(); + w5500_reset(); + w5500_intialization(); + while(1) + { + w5500_socket_config(); + w5500_irq_process(); + + /* If Socket0 receives data */ + if((w5500_param.sock.state & SOCK_STAT_RECV) == SOCK_STAT_RECV) + { + w5500_param.sock.state &= ~SOCK_STAT_RECV; + length = Process_Socket_Data(0); + printf("w5500 receive: "); + for(int i = 0; i < length; i++) + { + printf("%x ", rx_buf[i]); + } + printf("\n"); + } + + /* Otherwise, send data regularly */ + else if(cnt >= 5) + { + if(w5500_param.sock.flag == (SOCK_FLAG_INIT|SOCK_FLAG_CONN)) + { + w5500_param.sock.state &= ~SOCK_STAT_SEND; + memcpy(tx_buf, "\r\nWelcome To internet!\r\n", 21); + w5500_write_sock_bytes(0, tx_buf, 21); + } + cnt = 0; + } + up_mdelay(100); + cnt++; + } + +} diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_w5500.h b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_w5500.h new file mode 100644 index 000000000..86e238b03 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/edu-riscv64/src/k210_w5500.h @@ -0,0 +1,319 @@ +/* +* Copyright (c) 2022 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file k210_w5500.h +* @brief w5500 driver +* @version 1.0 +* @author AIIT XUOS Lab +* @date 2022-9-15 +*/ + +#ifndef _K210_W5500_H_ +#define _K210_W5500_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "k210_config.h" +#include "k210_fpioa.h" +#include "k210_gpiohs.h" +#include "nuttx/arch.h" +#include "k210_gpio_common.h" + +/***************** Common Register *****************/ +#define W5500_MR_REG 0x0000 +#define MR_RST 0x80 +#define MR_WOL 0x20 +#define MR_PB 0x10 +#define MR_PPP 0x08 +#define MR_FARP 0x02 + +#define W5500_GAR_REG 0x0001 +#define W5500_SUBR_REG 0x0005 +#define W5500_SHAR_REG 0x0009 +#define W5500_SIPR_REG 0x000f + +#define W5500_INT_REG 0x0013 + +#define W5500_IR_REG 0x0015 +#define IR_CONFLICT 0x80 +#define IR_UNREACH 0x40 +#define IR_PPPOE 0x20 +#define IR_MP 0x10 + +#define W5500_IMR_REG 0x0016 +#define IMR_IR7 0x80 +#define IMR_IR6 0x40 +#define IMR_IR5 0x20 +#define IMR_IR4 0x10 + +#define W5500_SIR_REG 0x0017 +#define S7_INT 0x80 +#define S6_INT 0x40 +#define S5_INT 0x20 +#define S4_INT 0x10 +#define S3_INT 0x08 +#define S2_INT 0x04 +#define S1_INT 0x02 +#define S0_INT 0x01 + +#define W5500_SIMR_REG 0x0018 +#define S7_IMR 0x80 +#define S6_IMR 0x40 +#define S5_IMR 0x20 +#define S4_IMR 0x10 +#define S3_IMR 0x08 +#define S2_IMR 0x04 +#define S1_IMR 0x02 +#define S0_IMR 0x01 + +#define W5500_RTR_REG 0x0019 +#define W5500_RCR_REG 0x001B + +#define W5500_PTIMER_REG 0x001C +#define W5500_PMAGIC_REG 0x001D +#define W5500_PHA_REG 0x001E +#define W5500_PSID_REG 0x0024 +#define W5500_PMRU_REG 0x0026 + +#define W5500_UIPR_REG 0x0028 +#define W5500_UPORT_REG 0x002C + +#define W5500_PHYCFGR_REG 0x002E +#define RST_PHY 0x80 +#define OPMODE 0x40 +#define DPX 0x04 +#define SPD 0x02 +#define LINK 0x01 + +#define W5500_VER_REG 0x0039 + +/********************* Socket Register *******************/ +#define W5500_SN_MR_REG 0x0000 +#define SN_MR_MULTI_MFEN 0x80 +#define SN_MR_BCASTB 0x40 +#define SN_MR_ND_MC_MMB 0x20 +#define SN_MR_UCASTB_MIP6B 0x10 +#define SN_MR_CLOSE 0x00 +#define SN_MR_TCP 0x01 +#define SN_MR_UDP 0x02 +#define SN_MR_MACRAW 0x04 + +#define W5500_SN_CR_REG 0x0001 +#define SN_CR_OPEN 0x01 +#define SN_CR_LISTEN 0x02 +#define SN_CR_CONNECT 0x04 +#define SN_CR_DISCON 0x08 +#define SN_CR_CLOSE 0x10 +#define SN_CR_SEND 0x20 +#define SN_CR_SEND_MAC 0x21 +#define SN_CR_SEND_KEEP 0x22 +#define SN_CR_RECV 0x40 + +#define W5500_SN_IR_REG 0x0002 +#define IR_SEND_OK 0x10 +#define IR_TIMEOUT 0x08 +#define IR_RECV 0x04 +#define IR_DISCON 0x02 +#define IR_CON 0x01 + +#define W5500_SN_SR_REG 0x0003 +#define SOCK_CLOSED 0x00 +#define SOCK_INIT 0x13 +#define SOCK_LISTEN 0x14 +#define SOCK_ESTABLISHED 0x17 +#define SOCK_CLOSE_WAIT 0x1C +#define SOCK_UDP 0x22 +#define SOCK_MACRAW 0x02 + +#define SOCK_SYNSEND 0x15 +#define SOCK_SYNRECV 0x16 +#define SOCK_FIN_WAI 0x18 +#define SOCK_CLOSING 0x1A +#define SOCK_TIME_WAIT 0x1B +#define SOCK_LAST_ACK 0x1D + +#define W5500_SN_PORT_REG 0x0004 +#define W5500_SN_DHAR_REG 0x0006 +#define W5500_SN_DIPR_REG 0x000C +#define W5500_SN_DPORTR_REG 0x0010 + +#define W5500_SN_MSSR_REG 0x0012 +#define W5500_SN_TOS_REG 0x0015 +#define W5500_SN_TTL_REG 0x0016 + +#define W5500_SN_RXBUF_SIZE_REG 0x001E +#define W5500_SN_TXBUF_SIZE_REG 0x001F +#define W5500_SN_TX_FSR_REG 0x0020 +#define W5500_SN_TX_RD_REG 0x0022 +#define W5500_SN_TX_WR_REG 0x0024 +#define W5500_SN_RX_RSR_REG 0x0026 +#define W5500_SN_RX_RD_REG 0x0028 +#define W5500_SN_RX_WR_REG 0x002A + +#define W5500_SN_IMR_REG 0x002C +#define IMR_SENDOK 0x10 +#define IMR_TIMEOUT 0x08 +#define IMR_RECV 0x04 +#define IMR_DISCON 0x02 +#define IMR_CON 0x01 + +#define W5500_SN_FRAG_REG 0x002D +#define W5500_SN_KPALVTR_REG 0x002F + +/************************ SPI Control Data *************************/ + +/* Operation mode bits */ + +#define VDM 0x00 +#define FDM1 0x01 +#define FDM2 0x02 +#define FDM4 0x03 + +/* Read_Write control bit */ +#define RWB_READ 0x00 +#define RWB_WRITE 0x04 + +/* Block select bits */ +#define COMMON_R 0x00 + +/* Socket 0 */ +#define S0_REG 0x08 +#define S0_TX_BUF 0x10 +#define S0_RX_BUF 0x18 + +/* Socket 1 */ +#define S1_REG 0x28 +#define S1_TX_BUF 0x30 +#define S1_RX_BUF 0x38 + +/* Socket 2 */ +#define S2_REG 0x48 +#define S2_TX_BUF 0x50 +#define S2_RX_BUF 0x58 + +/* Socket 3 */ +#define S3_REG 0x68 +#define S3_TX_BUF 0x70 +#define S3_RX_BUF 0x78 + +/* Socket 4 */ +#define S4_REG 0x88 +#define S4_TX_BUF 0x90 +#define S4_RX_BUF 0x98 + +/* Socket 5 */ +#define S5_REG 0xa8 +#define S5_TX_BUF 0xb0 +#define S5_RX_BUF 0xb8 + +/* Socket 6 */ +#define S6_REG 0xc8 +#define S6_TX_BUF 0xd0 +#define S6_RX_BUF 0xd8 + +/* Socket 7 */ +#define S7_REG 0xe8 +#define S7_TX_BUF 0xf0 +#define S7_RX_BUF 0xf8 + +// socket receive buffer size based on RMSR +#define SOCK_RECV_SIZE 2048 +// socket send buffer size based on RMSR +#define SOCK_SEND_SIZE 2048 + +#define W5500_IP_ADDR_LEN 4 +#define W5500_IP_MASK_LEN 4 +#define W5500_GW_ADDR_LEN 4 +#define W5500_MAC_ADDR_LEN 6 + +//for every socket + +// socket mode +#define SOCK_TCP_SVR 0 //server mode +#define SOCK_TCP_CLI 1 //client mode +#define SOCK_UDP_MOD 2 //udp mode + +// socket flag +#define SOCK_FLAG_INIT 1 +#define SOCK_FLAG_CONN 2 + +// socket data state +#define SOCK_STAT_RECV 1 +#define SOCK_STAT_SEND 2 + +typedef struct w5500_socket_s +{ + uint16_t local_port; + uint8_t dst_ip[W5500_IP_ADDR_LEN]; + uint16_t dst_port; + uint8_t mode; // 0: TCP Server; 1: TCP client; 2: UDP + uint8_t flag; // 1: init ok; 2: connected + uint8_t state; // 1: receive one; 2: send ok +}w5500_socket_t; + +typedef struct +{ + uint8_t ip_addr[W5500_IP_ADDR_LEN]; + uint8_t ip_mask[W5500_IP_MASK_LEN]; + uint8_t gw_addr[W5500_GW_ADDR_LEN]; + uint8_t mac_addr[W5500_MAC_ADDR_LEN]; + uint8_t udp_ip[4]; + uint16_t udp_port; + w5500_socket_t sock; +}w5500_param_t; + + +#define W5500_MAX_PACK_SIZE 1460 +typedef unsigned char socket_t; + +#define NCS_L() k210_gpiohs_set_value(FPIOA_ENET_NCS, GPIO_PV_LOW); up_udelay(1); +#define NCS_H() k210_gpiohs_set_value(FPIOA_ENET_NCS, GPIO_PV_HIGH); up_udelay(1); +#define SCLK_L() k210_gpiohs_set_value(FPIOA_ENET_SCLK, GPIO_PV_LOW); up_udelay(1); +#define SCLK_H() k210_gpiohs_set_value(FPIOA_ENET_SCLK, GPIO_PV_HIGH); up_udelay(1); +#define MOSI_L() k210_gpiohs_set_value(FPIOA_ENET_MOSI, GPIO_PV_LOW); up_udelay(1); +#define MOSI_H() k210_gpiohs_set_value(FPIOA_ENET_MOSI, GPIO_PV_HIGH); up_udelay(1); +#define RST_L() k210_gpiohs_set_value(FPIOA_ENET_NRST, GPIO_PV_LOW); up_mdelay(200); +#define RST_H() k210_gpiohs_set_value(FPIOA_ENET_NRST, GPIO_PV_HIGH); up_mdelay(200); + +void w5500_write_sock_byte(socket_t sock, uint16_t reg, uint8_t dat); +void w5500_write_sock_short(socket_t sock, uint16_t reg, uint16_t dat); +void w5500_write_sock_long(socket_t sock, uint16_t reg, uint8_t *dat); +uint8_t w5500_read_byte(uint16_t reg); +uint8_t w5500_read_sock_byte(socket_t sock, uint16_t reg); +uint16_t w5500_read_sock_short(socket_t sock, uint16_t reg); +void w5500_write_sock_bytes(socket_t sock, uint8_t *dat, uint16_t size); +void w5500_reset(void); +void w5500_config_init(void); +uint8_t w5500_detect_gateway(void); +void w5500_socket_init(socket_t sock); +uint8_t w5500_socket_connect(socket_t sock); +uint8_t w5500_socket_listen(socket_t sock); +uint8_t w5500_socket_set_udp(socket_t sock); +void w5500_irq_process(void); +void w5500_intialization(void); +void w5500_load_param(void); +void w5500_socket_config(void); +uint16_t Process_Socket_Data(socket_t sock); +void SPI_Configuration(void); +void w5500_test(void); + +#endif diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/build.sh b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/build.sh index ab7a4426c..7ab8bbe9c 100755 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/build.sh +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/build.sh @@ -18,6 +18,7 @@ cp -rf $current/apps $nuttx cp -rf $nuttx/aiit_board/aiit-arm32-board $nuttx/nuttx/boards/arm/stm32 cp -rf $nuttx/aiit_board/aiit-riscv64-board $nuttx/nuttx/boards/risc-v/k210 cp -rf $nuttx/aiit_board/xidatong-riscv64 $nuttx/nuttx/boards/risc-v/k210 +cp -rf $nuttx/aiit_board/edu-riscv64 $nuttx/nuttx/boards/risc-v/k210 cp -rf $nuttx/aiit_board/xidatong-arm32 $nuttx/nuttx/boards/arm/imxrt cp -rf $nuttx/aiit_board/hc32f4a0 $nuttx/nuttx/boards/arm/hc32 diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/boards/Kconfig b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/boards/Kconfig index 0dd66009b..41c7b0ef4 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/boards/Kconfig +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/boards/Kconfig @@ -702,6 +702,14 @@ config ARCH_BOARD_XIDATONG_RISCV64 This is the board configuration for the port of NuttX to the xidatong-riscv64 board. This board features the RISC-V K210 +config ARCH_BOARD_EDU_RISCV64 + bool "edu riscv64 board" + depends on ARCH_CHIP_K210 + select ARCH_HAVE_LEDS if !K210_WITH_QEMU + ---help--- + This is the board configuration for the port of NuttX to the + edu-riscv64 board. This board features the RISC-V K210 + config ARCH_BOARD_SMARTL_C906 bool "smartl evaluation board for C906" depends on ARCH_CHIP_C906 @@ -2502,6 +2510,7 @@ config ARCH_BOARD default "maix-bit" if ARCH_BOARD_MAIX_BIT default "aiit-riscv64-board" if ARCH_BOARD_AIIT_RISCV64 default "xidatong-riscv64" if ARCH_BOARD_XIDATONG_RISCV64 + default "edu-riscv64" if ARCH_BOARD_EDU_RISCV64 default "smartl-c906" if ARCH_BOARD_SMARTL_C906 default "icicle" if ARCH_BOARD_ICICLE_MPFS default "m100pfsevp" if ARCH_BOARD_M100PFSEVP_MPFS @@ -3338,6 +3347,9 @@ endif if ARCH_BOARD_XIDATONG_RISCV64 source "boards/risc-v/k210/xidatong-riscv64/Kconfig" endif +if ARCH_BOARD_EDU_RISCV64 +source "boards/risc-v/k210/edu-riscv64/Kconfig" +endif if ARCH_BOARD_SMARTL_C906 source "boards/risc-v/c906/smartl-c906/Kconfig" endif