From 52d7f9be76535c94c1ca5edfc66bd613269b3ef6 Mon Sep 17 00:00:00 2001 From: cbb <2625274797@qq.com> Date: Thu, 16 Jul 2026 23:00:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(device=5Fexmc):=20=E6=B7=BB=E5=8A=A0GD32E5?= =?UTF-8?q?08=20EXMC=E5=A4=96=E8=AE=BE=E6=94=AF=E6=8C=81=E7=94=A8=E4=BA=8E?= =?UTF-8?q?LAN9252=E9=80=9A=E4=BF=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加EXMC 16-bit并行总线驱动实现,用于LAN9252 ESC通信。 在cproject.yml中添加device_exmc组和相关源文件,包括 gd32e50x_exmc.c/h文件,并更新头文件搜索路径。 由于GigaDevice DFP未将EXMC注册为CMSIS组件,需要本地编入 RTE/Device目录下的EXMC源文件以支持LAN9252并行总线功能。 fix(canfd): 优化CANFD驱动中的条件编译和DLC处理逻辑 将CANFD相关的配置项从硬编码改为使用sys/config.h中的配置。 添加对DLC=0情况的支持(表示无数据负载的远程帧), 修正了数据长度检查逻辑,确保DLC为0时不需要提供数据指针。 同时重构了调试代码的条件编译,使用CONFIG_CAN_STARTUP_DIAG_VERBOSE 等配置开关控制调试输出函数的编译。 refactor(ethercat): 统一配置项管理,移除硬编码常量 将EtherCAT相关配置项从硬编码改为统一的sys/config.h配置, 提升代码可维护性。 docs(spi): 更新SPI命令响应缓冲区大小说明 更新CAN桥接和ID快照命令的响应payload大小计算说明, 统一使用AppSpiCommand::kTxBufSize - V2_OVERHEAD作为上限。 --- .../GD32E508_DCU_APP.cproject.yml | 10 +- .../RTE/Device/GD32E508VE/gd32e50x_exmc.c | 708 +++++++ .../GD32E508VE/gd32e50x_exmc.c.base@1.7.0 | 708 +++++++ .../RTE/Device/GD32E508VE/gd32e50x_exmc.h | 440 ++++ GD32E508_DCU_APP/comm/esc_exmc_bus.cpp | 71 +- GD32E508_DCU_APP/comm/ethercat_slave.h | 4 +- GD32E508_DCU_APP/device/CANFD.cpp | 73 +- GD32E508_DCU_APP/device/canfd.h | 20 +- .../device/spi/app_spi_cmd_can_bridge.cpp | 5 +- .../device/spi/app_spi_cmd_can_feedback.cpp | 7 +- .../device/spi/app_spi_cmd_id_snapshot.cpp | 8 +- .../device/spi/app_spi_command.cpp | 1772 ++++++++--------- GD32E508_DCU_APP/device/spi/app_spi_command.h | 20 +- GD32E508_DCU_APP/device/temp_sensor.cpp | 19 - GD32E508_DCU_APP/device/temp_sensor.h | 22 - GD32E508_DCU_APP/main.h | 4 - GD32E508_DCU_APP/sys/config.h | 46 +- GD32E508_DCU_APP/sys/initial.cpp | 5 +- GD32E508_DCU_APP/sys/task_manager.cpp | 7 - GD32E508_DCU_APP/sys/task_manager.h | 2 +- GD32E508_DCU_APP/sys/task_runner.cpp | 18 +- GD32E508_DCU_APP/sys/task_runner.h | 4 +- communication_ids.html | 338 ---- 23 files changed, 2853 insertions(+), 1458 deletions(-) create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exmc.c create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exmc.c.base@1.7.0 create mode 100644 GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exmc.h delete mode 100644 GD32E508_DCU_APP/device/temp_sensor.cpp delete mode 100644 GD32E508_DCU_APP/device/temp_sensor.h delete mode 100644 communication_ids.html diff --git a/GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml b/GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml index 3c83793..9b97076 100644 --- a/GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml +++ b/GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml @@ -30,10 +30,14 @@ project: - file: ./device/timer.cpp - file: ./device/usart0.h - file: ./device/usart0.cpp - - file: ./device/temp_sensor.h - - file: ./device/temp_sensor.cpp - file: ./device/watchdog.h - file: ./device/watchdog.cpp + # GigaDevice DFP 未把 EXMC 注册为 CMSIS 组件(仅有 FMC=片内 Flash), + # 但外设源文件存在于 Pack;LAN9252 并行总线需要 EXMC,故本地编入。 + - group: device_exmc + files: + - file: ./RTE/Device/GD32E508VE/gd32e50x_exmc.c + - file: ./RTE/Device/GD32E508VE/gd32e50x_exmc.h - group: device/spi files: - file: ./device/spi/app_spi_command.h @@ -99,6 +103,8 @@ project: - -D__MICROLIB - -fshort-enums - -fshort-wchar + # EXMC 未作为 CMSIS 组件分发;头文件在 RTE 本地目录 + - -IRTE/Device/GD32E508VE - -Wno-register - -IG:/Robot_v2/DCU/spi_link C: diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exmc.c b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exmc.c new file mode 100644 index 0000000..43a3158 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exmc.c @@ -0,0 +1,708 @@ +/*! + \file gd32e50x_exmc.c + \brief EXMC driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_exmc.h" + +/* EXMC bank0 register reset value */ +#define BANK0_SNCTL_REGION0_RESET ((uint32_t)0x000030DBU) +#define BANK0_SNCTL_REGION1_2_3_RESET ((uint32_t)0x000030D2U) +#define BANK0_SNTCFG_RESET ((uint32_t)0x0FFFFFFFU) +#define BANK0_SNWTCFG_RESET ((uint32_t)0x0FFFFFFFU) + +/* EXMC bank1/2 register reset mask */ +#define BANK1_2_NPCTL_RESET ((uint32_t)0x00000018U) +#define BANK1_2_NPINTEN_RESET ((uint32_t)0x00000042U) +#define BANK1_2_NPCTCFG_RESET ((uint32_t)0xFCFCFCFCU) +#define BANK1_2_NPATCFG_RESET ((uint32_t)0xFCFCFCFCU) + +/* EXMC bank3 register reset mask */ +#define BANK3_NPCTL_RESET ((uint32_t)0x00000018U) +#define BANK3_NPINTEN_RESET ((uint32_t)0x00000043U) +#define BANK3_NPCTCFG_RESET ((uint32_t)0xFCFCFCFCU) +#define BANK3_NPATCFG_RESET ((uint32_t)0xFCFCFCFCU) +#define BANK3_PIOTCFG3_RESET ((uint32_t)0xFCFCFCFCU) + +/* EXMC register bit offset */ +#define SNCTL_NRMUX_OFFSET ((uint32_t)1U) +#define SNCTL_SBRSTEN_OFFSET ((uint32_t)8U) +#define SNCTL_WRAPEN_OFFSET ((uint32_t)10U) +#define SNCTL_WREN_OFFSET ((uint32_t)12U) +#define SNCTL_NRWTEN_OFFSET ((uint32_t)13U) +#define SNCTL_EXMODEN_OFFSET ((uint32_t)14U) +#define SNCTL_ASYNCWAIT_OFFSET ((uint32_t)15U) + +#define SNTCFG_AHLD_OFFSET ((uint32_t)4U) +#define SNTCFG_DSET_OFFSET ((uint32_t)8U) +#define SNTCFG_BUSLAT_OFFSET ((uint32_t)16U) + +#define SNWTCFG_WAHLD_OFFSET ((uint32_t)4U) +#define SNWTCFG_WDSET_OFFSET ((uint32_t)8U) +#define SNWTCFG_WBUSLAT_OFFSET ((uint32_t)16U) + +#define NPCTL_NDWTEN_OFFSET ((uint32_t)1U) +#define NPCTL_ECCEN_OFFSET ((uint32_t)6U) + +#define NPCTCFG_COMWAIT_OFFSET ((uint32_t)8U) +#define NPCTCFG_COMHLD_OFFSET ((uint32_t)16U) +#define NPCTCFG_COMHIZ_OFFSET ((uint32_t)24U) + +#define NPATCFG_ATTWAIT_OFFSET ((uint32_t)8U) +#define NPATCFG_ATTHLD_OFFSET ((uint32_t)16U) +#define NPATCFG_ATTHIZ_OFFSET ((uint32_t)24U) + +#define PIOTCFG_IOWAIT_OFFSET ((uint32_t)8U) +#define PIOTCFG_IOHLD_OFFSET ((uint32_t)16U) +#define PIOTCFG_IOHIZ_OFFSET ((uint32_t)24U) + +#define INTEN_INTS_OFFSET ((uint32_t)3U) + +/*! + \brief deinitialize EXMC NOR/SRAM region + \param[in] exmc_norsram_region: select the region of bank0 + only one parameter can be selected which is shown as below: + \arg EXMC_BANK0_NORSRAM_REGIONx(x=0..3) + \param[out] none + \retval none +*/ +void exmc_norsram_deinit(uint32_t exmc_norsram_region) +{ + /* reset the registers */ + if(EXMC_BANK0_NORSRAM_REGION0 == exmc_norsram_region){ + EXMC_SNCTL(exmc_norsram_region) = BANK0_SNCTL_REGION0_RESET; + }else{ + EXMC_SNCTL(exmc_norsram_region) = BANK0_SNCTL_REGION1_2_3_RESET; + } + EXMC_SNTCFG(exmc_norsram_region) = BANK0_SNTCFG_RESET; + EXMC_SNWTCFG(exmc_norsram_region) = BANK0_SNWTCFG_RESET; +} + +/*! + \brief initialize exmc_norsram_parameter_struct with the default values + \param[in] none + \param[out] exmc_norsram_init_struct: the initialized struct exmc_norsram_parameter_struct pointer + \retval none +*/ +void exmc_norsram_struct_para_init(exmc_norsram_parameter_struct* exmc_norsram_init_struct) +{ + /* configure the structure with default values */ + exmc_norsram_init_struct->norsram_region = EXMC_BANK0_NORSRAM_REGION0; + exmc_norsram_init_struct->address_data_mux = ENABLE; + exmc_norsram_init_struct->memory_type = EXMC_MEMORY_TYPE_SRAM; + exmc_norsram_init_struct->databus_width = EXMC_NOR_DATABUS_WIDTH_8B; + exmc_norsram_init_struct->burst_mode = DISABLE; + exmc_norsram_init_struct->nwait_polarity = EXMC_NWAIT_POLARITY_LOW; + exmc_norsram_init_struct->wrap_burst_mode = DISABLE; + exmc_norsram_init_struct->nwait_config = EXMC_NWAIT_CONFIG_BEFORE; + exmc_norsram_init_struct->memory_write = ENABLE; + exmc_norsram_init_struct->nwait_signal = ENABLE; + exmc_norsram_init_struct->extended_mode = DISABLE; + exmc_norsram_init_struct->asyn_wait = DISABLE; + exmc_norsram_init_struct->write_mode = EXMC_ASYN_WRITE; + + /* read/write timing configure */ + exmc_norsram_init_struct->read_write_timing->asyn_address_setuptime = 0xFU; + exmc_norsram_init_struct->read_write_timing->asyn_address_holdtime = 0xFU; + exmc_norsram_init_struct->read_write_timing->asyn_data_setuptime = 0xFFU; + exmc_norsram_init_struct->read_write_timing->bus_latency = 0xFU; + exmc_norsram_init_struct->read_write_timing->syn_clk_division = EXMC_SYN_CLOCK_RATIO_16_CLK; + exmc_norsram_init_struct->read_write_timing->syn_data_latency = EXMC_DATALAT_17_CLK; + exmc_norsram_init_struct->read_write_timing->asyn_access_mode = EXMC_ACCESS_MODE_A; + + /* write timing configure, when extended mode is used */ + exmc_norsram_init_struct->write_timing->asyn_address_setuptime = 0xFU; + exmc_norsram_init_struct->write_timing->asyn_address_holdtime = 0xFU; + exmc_norsram_init_struct->write_timing->asyn_data_setuptime = 0xFFU; + exmc_norsram_init_struct->write_timing->bus_latency = 0xFU; + exmc_norsram_init_struct->write_timing->asyn_access_mode = EXMC_ACCESS_MODE_A; +} + +/*! + \brief initialize EXMC NOR/SRAM region + \param[in] exmc_norsram_parameter_struct: configure the EXMC NOR/SRAM parameter + norsram_region: EXMC_BANK0_NORSRAM_REGIONx,x=0..3 + write_mode: EXMC_ASYN_WRITE,EXMC_SYN_WRITE + extended_mode: ENABLE or DISABLE + asyn_wait: ENABLE or DISABLE + nwait_signal: ENABLE or DISABLE + memory_write: ENABLE or DISABLE + nwait_config: EXMC_NWAIT_CONFIG_BEFORE,EXMC_NWAIT_CONFIG_DURING + wrap_burst_mode: ENABLE or DISABLE + nwait_polarity: EXMC_NWAIT_POLARITY_LOW,EXMC_NWAIT_POLARITY_HIGH + burst_mode: ENABLE or DISABLE + databus_width: EXMC_NOR_DATABUS_WIDTH_8B,EXMC_NOR_DATABUS_WIDTH_16B + memory_type: EXMC_MEMORY_TYPE_SRAM,EXMC_MEMORY_TYPE_PSRAM,EXMC_MEMORY_TYPE_NOR + address_data_mux: ENABLE or DISABLE + read_write_timing: struct exmc_norsram_timing_parameter_struct set the time + asyn_access_mode: EXMC_ACCESS_MODE_A,EXMC_ACCESS_MODE_B,EXMC_ACCESS_MODE_C,EXMC_ACCESS_MODE_D + syn_data_latency: EXMC_DATALAT_n_CLK,(n=2,..,17) + syn_clk_division: EXMC_SYN_CLOCK_RATIO_DISABLE, EXMC_SYN_CLOCK_RATIO_n_CLK,(n=2,..,16) + bus_latency: 1,..,16 + asyn_data_setuptime: 2,..,256 + asyn_address_holdtime: 2,..,16 + asyn_address_setuptime: 1,..,16 + write_timing: struct exmc_norsram_timing_parameter_struct set the time + asyn_access_mode: EXMC_ACCESS_MODE_A,EXMC_ACCESS_MODE_B,EXMC_ACCESS_MODE_C,EXMC_ACCESS_MODE_D + syn_data_latency: EXMC_DATALAT_n_CLK,(n=2,..,17) + syn_clk_division: EXMC_SYN_CLOCK_RATIO_DISABLE, EXMC_SYN_CLOCK_RATIO_n_CLK,(n=2,..,16) + bus_latency: 1,..,16 + asyn_data_setuptime: 2,..,256 + asyn_address_holdtime: 2,..,16 + asyn_address_setuptime: 1,..,16 + \param[out] none + \retval none +*/ +void exmc_norsram_init(exmc_norsram_parameter_struct* exmc_norsram_init_struct) +{ + uint32_t snctl = 0x00000000U,sntcfg = 0x00000000U,snwtcfg = 0x00000000U; + + /* get the register value */ + snctl = EXMC_SNCTL(exmc_norsram_init_struct->norsram_region); + + /* clear relative bits */ + snctl &= ((uint32_t)~(EXMC_SNCTL_NRMUX | EXMC_SNCTL_NRTP | EXMC_SNCTL_NRW | EXMC_SNCTL_SBRSTEN | + EXMC_SNCTL_NREN | EXMC_SNCTL_NRWTPOL | EXMC_SNCTL_WRAPEN | EXMC_SNCTL_NRWTCFG | + EXMC_SNCTL_WREN | EXMC_SNCTL_NRWTEN | EXMC_SNCTL_EXMODEN | EXMC_SNCTL_ASYNCWAIT | + EXMC_SNCTL_SYNCWR )); + + snctl |= (uint32_t)(exmc_norsram_init_struct->address_data_mux << SNCTL_NRMUX_OFFSET) | + exmc_norsram_init_struct->memory_type | + exmc_norsram_init_struct->databus_width | + (exmc_norsram_init_struct->burst_mode << SNCTL_SBRSTEN_OFFSET) | + exmc_norsram_init_struct->nwait_polarity | + (exmc_norsram_init_struct->wrap_burst_mode << SNCTL_WRAPEN_OFFSET) | + exmc_norsram_init_struct->nwait_config | + (exmc_norsram_init_struct->memory_write << SNCTL_WREN_OFFSET) | + (exmc_norsram_init_struct->nwait_signal << SNCTL_NRWTEN_OFFSET) | + (exmc_norsram_init_struct->extended_mode << SNCTL_EXMODEN_OFFSET) | + (exmc_norsram_init_struct->asyn_wait << SNCTL_ASYNCWAIT_OFFSET) | + exmc_norsram_init_struct->write_mode; + + sntcfg = (uint32_t)((exmc_norsram_init_struct->read_write_timing->asyn_address_setuptime - 1U ) & EXMC_SNTCFG_ASET )| + (((exmc_norsram_init_struct->read_write_timing->asyn_address_holdtime - 1U ) << SNTCFG_AHLD_OFFSET ) & EXMC_SNTCFG_AHLD ) | + (((exmc_norsram_init_struct->read_write_timing->asyn_data_setuptime - 1U ) << SNTCFG_DSET_OFFSET ) & EXMC_SNTCFG_DSET ) | + (((exmc_norsram_init_struct->read_write_timing->bus_latency - 1U ) << SNTCFG_BUSLAT_OFFSET ) & EXMC_SNTCFG_BUSLAT )| + exmc_norsram_init_struct->read_write_timing->syn_clk_division | + exmc_norsram_init_struct->read_write_timing->syn_data_latency | + exmc_norsram_init_struct->read_write_timing->asyn_access_mode; + + /* nor flash access enable */ + if(EXMC_MEMORY_TYPE_NOR == exmc_norsram_init_struct->memory_type){ + snctl |= (uint32_t)EXMC_SNCTL_NREN; + } + + /* extended mode configure */ + if(ENABLE == exmc_norsram_init_struct->extended_mode){ + snwtcfg = (uint32_t)((exmc_norsram_init_struct->write_timing->asyn_address_setuptime - 1U) & EXMC_SNWTCFG_WASET ) | + (((exmc_norsram_init_struct->write_timing->asyn_address_holdtime -1U ) << SNWTCFG_WAHLD_OFFSET ) & EXMC_SNWTCFG_WAHLD )| + (((exmc_norsram_init_struct->write_timing->asyn_data_setuptime -1U ) << SNWTCFG_WDSET_OFFSET ) & EXMC_SNWTCFG_WDSET )| + (((exmc_norsram_init_struct->write_timing->bus_latency - 1U ) << SNWTCFG_WBUSLAT_OFFSET ) & EXMC_SNWTCFG_WBUSLAT ) | + exmc_norsram_init_struct->write_timing->asyn_access_mode; + }else{ + snwtcfg = BANK0_SNWTCFG_RESET; + } + + /* configure the registers */ + EXMC_SNCTL(exmc_norsram_init_struct->norsram_region) = snctl; + EXMC_SNTCFG(exmc_norsram_init_struct->norsram_region) = sntcfg; + EXMC_SNWTCFG(exmc_norsram_init_struct->norsram_region) = snwtcfg; +} + +/*! + \brief enable EXMC NOR/PSRAM bank region + \param[in] exmc_norsram_region: specifie the region of NOR/PSRAM bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK0_NORSRAM_REGIONx(x=0..3) + \param[out] none + \retval none +*/ +void exmc_norsram_enable(uint32_t exmc_norsram_region) +{ + EXMC_SNCTL(exmc_norsram_region) |= (uint32_t)EXMC_SNCTL_NRBKEN; +} + +/*! + \brief disable EXMC NOR/PSRAM bank region + \param[in] exmc_norsram_region: specifie the region of NOR/PSRAM Bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK0_NORSRAM_REGIONx(x=0..3) + \param[out] none + \retval none +*/ +void exmc_norsram_disable(uint32_t exmc_norsram_region) +{ + EXMC_SNCTL(exmc_norsram_region) &= ~(uint32_t)EXMC_SNCTL_NRBKEN; +} + +/*! + \brief configure CRAM page size + \param[in] exmc_norsram_region: specifie the region of NOR/PSRAM bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK0_NORSRAM_REGIONx(x=0..3) + \param[in] page_size: CRAM page size + only one parameter can be selected which is shown as below: + \arg EXMC_CRAM_AUTO_SPLIT: the clock is generated only during synchronous access + \arg EXMC_CRAM_PAGE_SIZE_128_BYTES: page size is 128 bytes + \arg EXMC_CRAM_PAGE_SIZE_256_BYTES: page size is 256 bytes + \arg EXMC_CRAM_PAGE_SIZE_512_BYTES: page size is 512 bytes + \arg EXMC_CRAM_PAGE_SIZE_1024_BYTES: page size is 1024 bytes + \param[out] none + \retval none +*/ +void exmc_norsram_page_size_config(uint32_t exmc_norsram_region, uint32_t page_size) +{ + /* reset the bits */ + EXMC_SNCTL(exmc_norsram_region) &= ~EXMC_SNCTL_CPS; + + /* set the CPS bits */ + EXMC_SNCTL(exmc_norsram_region) |= page_size; +} + +/*! + \brief deinitialize EXMC NAND bank + \param[in] exmc_nand_bank: select the bank of NAND + only one parameter can be selected which is shown as below: + \arg EXMC_BANKx_NAND(x=1..2) + \param[out] none + \retval none +*/ +void exmc_nand_deinit(uint32_t exmc_nand_bank) +{ + /* EXMC_BANK1_NAND or EXMC_BANK2_NAND */ + EXMC_NPCTL(exmc_nand_bank) = BANK1_2_NPCTL_RESET; + EXMC_NPINTEN(exmc_nand_bank) = BANK1_2_NPINTEN_RESET; + EXMC_NPCTCFG(exmc_nand_bank) = BANK1_2_NPCTCFG_RESET; + EXMC_NPATCFG(exmc_nand_bank) = BANK1_2_NPATCFG_RESET; +} + +/*! + \brief initialize exmc_nand_parameter_struct with the default values + \param[in] none + \param[out] the initialized struct exmc_nand_parameter_struct pointer + \retval none +*/ +void exmc_nand_struct_para_init(exmc_nand_parameter_struct* exmc_nand_init_struct) +{ + /* configure the structure with default values */ + exmc_nand_init_struct->nand_bank = EXMC_BANK1_NAND; + exmc_nand_init_struct->wait_feature = DISABLE; + exmc_nand_init_struct->databus_width = EXMC_NAND_DATABUS_WIDTH_8B; + exmc_nand_init_struct->ecc_logic = DISABLE; + exmc_nand_init_struct->ecc_size = EXMC_ECC_SIZE_256BYTES; + exmc_nand_init_struct->ctr_latency = 0x0U; + exmc_nand_init_struct->atr_latency = 0x0U; + exmc_nand_init_struct->common_space_timing->setuptime = 0xFCU; + exmc_nand_init_struct->common_space_timing->waittime = 0xFCU; + exmc_nand_init_struct->common_space_timing->holdtime = 0xFCU; + exmc_nand_init_struct->common_space_timing->databus_hiztime = 0xFCU; + exmc_nand_init_struct->attribute_space_timing->setuptime = 0xFCU; + exmc_nand_init_struct->attribute_space_timing->waittime = 0xFCU; + exmc_nand_init_struct->attribute_space_timing->holdtime = 0xFCU; + exmc_nand_init_struct->attribute_space_timing->databus_hiztime = 0xFCU; +} + +/*! + \brief initialize EXMC NAND bank + \param[in] exmc_nand_parameter_struct: configure the EXMC NAND parameter + nand_bank: EXMC_BANK1_NAND,EXMC_BANK2_NAND + ecc_size: EXMC_ECC_SIZE_xBYTES,x=256,512,1024,2048,4096 + atr_latency: EXMC_ALE_RE_DELAY_x_HCLK,x=1..16 + ctr_latency: EXMC_CLE_RE_DELAY_x_HCLK,x=1..16 + ecc_logic: ENABLE or DISABLE + databus_width: EXMC_NAND_DATABUS_WIDTH_8B,EXMC_NAND_DATABUS_WIDTH_16B + wait_feature: ENABLE or DISABLE + common_space_timing: struct exmc_nand_pccard_timing_parameter_struct set the time + databus_hiztime: 1,..,255 + holdtime: 1,..,254 + waittime: 2,..,255 + setuptime: 1,..,255 + attribute_space_timing: struct exmc_nand_pccard_timing_parameter_struct set the time + databus_hiztime: 1,..,255 + holdtime: 1,..,254 + waittime: 2,..,255 + setuptime: 1,..,255 + \param[out] none + \retval none +*/ +void exmc_nand_init(exmc_nand_parameter_struct* exmc_nand_init_struct) +{ + uint32_t npctl = 0x00000000U, npctcfg = 0x00000000U, npatcfg = 0x00000000U; + + npctl = (uint32_t)(exmc_nand_init_struct->wait_feature << NPCTL_NDWTEN_OFFSET)| + EXMC_NPCTL_NDTP | + exmc_nand_init_struct->databus_width | + (exmc_nand_init_struct->ecc_logic << NPCTL_ECCEN_OFFSET)| + exmc_nand_init_struct->ecc_size | + exmc_nand_init_struct->ctr_latency | + exmc_nand_init_struct->atr_latency; + + npctcfg = (uint32_t)((exmc_nand_init_struct->common_space_timing->setuptime - 1U) & EXMC_NPCTCFG_COMSET ) | + (((exmc_nand_init_struct->common_space_timing->waittime - 1U) << NPCTCFG_COMWAIT_OFFSET) & EXMC_NPCTCFG_COMWAIT ) | + ((exmc_nand_init_struct->common_space_timing->holdtime << NPCTCFG_COMHLD_OFFSET) & EXMC_NPCTCFG_COMHLD ) | + (((exmc_nand_init_struct->common_space_timing->databus_hiztime - 1U) << NPCTCFG_COMHIZ_OFFSET) & EXMC_NPCTCFG_COMHIZ ); + + npatcfg = (uint32_t)((exmc_nand_init_struct->attribute_space_timing->setuptime - 1U) & EXMC_NPATCFG_ATTSET ) | + (((exmc_nand_init_struct->attribute_space_timing->waittime - 1U) << NPATCFG_ATTWAIT_OFFSET) & EXMC_NPATCFG_ATTWAIT ) | + ((exmc_nand_init_struct->attribute_space_timing->holdtime << NPATCFG_ATTHLD_OFFSET) & EXMC_NPATCFG_ATTHLD ) | + (((exmc_nand_init_struct->attribute_space_timing->databus_hiztime -1U) << NPATCFG_ATTHIZ_OFFSET) & EXMC_NPATCFG_ATTHIZ ); + + /* EXMC_BANK1_NAND or EXMC_BANK2_NAND initialize */ + EXMC_NPCTL(exmc_nand_init_struct->nand_bank) = npctl; + EXMC_NPCTCFG(exmc_nand_init_struct->nand_bank) = npctcfg; + EXMC_NPATCFG(exmc_nand_init_struct->nand_bank) = npatcfg; +} + +/*! + \brief enable NAND bank + \param[in] exmc_nand_bank: specifie the NAND bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANKx_NAND(x=1,2) + \param[out] none + \retval none +*/ +void exmc_nand_enable(uint32_t exmc_nand_bank) +{ + EXMC_NPCTL(exmc_nand_bank) |= EXMC_NPCTL_NDBKEN; +} + +/*! + \brief disable NAND bank + \param[in] exmc_nand_bank: specifie the NAND bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANKx_NAND(x=1,2) + \param[out] none + \retval none +*/ +void exmc_nand_disable(uint32_t exmc_nand_bank) +{ + EXMC_NPCTL(exmc_nand_bank) &= (~EXMC_NPCTL_NDBKEN); +} + +/*! + \brief enable or disable the EXMC NAND ECC function + \param[in] exmc_nand_bank: specifie the NAND bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANKx_NAND(x=1,2) + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void exmc_nand_ecc_config(uint32_t exmc_nand_bank, ControlStatus newvalue) +{ + if (ENABLE == newvalue){ + /* enable the selected NAND bank ECC function */ + EXMC_NPCTL(exmc_nand_bank) |= EXMC_NPCTL_ECCEN; + }else{ + /* disable the selected NAND bank ECC function */ + EXMC_NPCTL(exmc_nand_bank) &= (~EXMC_NPCTL_ECCEN); + } +} + +/*! + \brief get the EXMC ECC value + \param[in] exmc_nand_bank: specifie the NAND bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANKx_NAND(x=1,2) + \param[out] none + \retval the error correction code(ECC) value +*/ +uint32_t exmc_ecc_get(uint32_t exmc_nand_bank) +{ + return (EXMC_NECC(exmc_nand_bank)); +} + +/*! + \brief deinitialize EXMC PC card bank + \param[in] none + \param[out] none + \retval none +*/ +void exmc_pccard_deinit(void) +{ + /* EXMC_BANK3_PCCARD */ + EXMC_NPCTL3 = BANK3_NPCTL_RESET; + EXMC_NPINTEN3 = BANK3_NPINTEN_RESET; + EXMC_NPCTCFG3 = BANK3_NPCTCFG_RESET; + EXMC_NPATCFG3 = BANK3_NPATCFG_RESET; + EXMC_PIOTCFG3 = BANK3_PIOTCFG3_RESET; +} + +/*! + \brief initialize exmc_pccard_parameter_struct parameter with the default values + \param[in] none + \param[out] the initialized struct exmc_pccard_parameter_struct pointer + \retval none +*/ +void exmc_pccard_struct_para_init(exmc_pccard_parameter_struct* exmc_pccard_init_struct) +{ + /* configure the structure with default values */ + exmc_pccard_init_struct->wait_feature = DISABLE; + exmc_pccard_init_struct->ctr_latency = 0x0U; + exmc_pccard_init_struct->atr_latency = 0x0U; + exmc_pccard_init_struct->common_space_timing->setuptime = 0xFCU; + exmc_pccard_init_struct->common_space_timing->waittime = 0xFCU; + exmc_pccard_init_struct->common_space_timing->holdtime = 0xFCU; + exmc_pccard_init_struct->common_space_timing->databus_hiztime = 0xFCU; + exmc_pccard_init_struct->attribute_space_timing->setuptime = 0xFCU; + exmc_pccard_init_struct->attribute_space_timing->waittime = 0xFCU; + exmc_pccard_init_struct->attribute_space_timing->holdtime = 0xFCU; + exmc_pccard_init_struct->attribute_space_timing->databus_hiztime = 0xFCU; + exmc_pccard_init_struct->io_space_timing->setuptime = 0xFCU; + exmc_pccard_init_struct->io_space_timing->waittime = 0xFCU; + exmc_pccard_init_struct->io_space_timing->holdtime = 0xFCU; + exmc_pccard_init_struct->io_space_timing->databus_hiztime = 0xFCU; +} + +/*! + \brief initialize EXMC PC card bank + \param[in] exmc_pccard_parameter_struct: configure the EXMC NAND parameter + atr_latency: EXMC_ALE_RE_DELAY_x_HCLK,x=1..16 + ctr_latency: EXMC_CLE_RE_DELAY_x_HCLK,x=1..16 + wait_feature: ENABLE or DISABLE + common_space_timing: struct exmc_nand_pccard_timing_parameter_struct set the time + databus_hiztime: 1,..,255 + holdtime: 1,..,254 + waittime: 2,..,255 + setuptime: 1,..,255 + attribute_space_timing: struct exmc_nand_pccard_timing_parameter_struct set the time + databus_hiztime: 1,..,255 + holdtime: 1,..,254 + waittime: 2,..,255 + setuptime: 1,..,255 + io_space_timing: exmc_nand_pccard_timing_parameter_struct set the time + databus_hiztime: 0,..,255 + holdtime: 1,..,255 + waittime: 2,..,256 + setuptime: 1,..,256 + \param[out] none + \retval none +*/ +void exmc_pccard_init(exmc_pccard_parameter_struct* exmc_pccard_init_struct) +{ + /* configure the EXMC bank3 PC card control register */ + EXMC_NPCTL3 = (uint32_t)(exmc_pccard_init_struct->wait_feature << NPCTL_NDWTEN_OFFSET) | + EXMC_NAND_DATABUS_WIDTH_16B | + exmc_pccard_init_struct->ctr_latency | + exmc_pccard_init_struct->atr_latency ; + + /* configure the EXMC bank3 PC card common space timing configuration register */ + EXMC_NPCTCFG3 = (uint32_t)((exmc_pccard_init_struct->common_space_timing->setuptime - 1U)& EXMC_NPCTCFG_COMSET ) | + (((exmc_pccard_init_struct->common_space_timing->waittime - 1U) << NPCTCFG_COMWAIT_OFFSET) & EXMC_NPCTCFG_COMWAIT ) | + ((exmc_pccard_init_struct->common_space_timing->holdtime << NPCTCFG_COMHLD_OFFSET) & EXMC_NPCTCFG_COMHLD ) | + (((exmc_pccard_init_struct->common_space_timing->databus_hiztime - 1U) << NPCTCFG_COMHIZ_OFFSET) & EXMC_NPCTCFG_COMHIZ ); + + /* configure the EXMC bank3 PC card attribute space timing configuration register */ + EXMC_NPATCFG3 = (uint32_t)((exmc_pccard_init_struct->attribute_space_timing->setuptime - 1U) & EXMC_NPATCFG_ATTSET ) | + (((exmc_pccard_init_struct->attribute_space_timing->waittime - 1U) << NPATCFG_ATTWAIT_OFFSET) & EXMC_NPATCFG_ATTWAIT ) | + ((exmc_pccard_init_struct->attribute_space_timing->holdtime << NPATCFG_ATTHLD_OFFSET) & EXMC_NPATCFG_ATTHLD )| + (((exmc_pccard_init_struct->attribute_space_timing->databus_hiztime -1U) << NPATCFG_ATTHIZ_OFFSET) & EXMC_NPATCFG_ATTHIZ ); + + /* configure the EXMC bank3 PC card io space timing configuration register */ + EXMC_PIOTCFG3 = (uint32_t)((exmc_pccard_init_struct->io_space_timing->setuptime - 1U) & EXMC_PIOTCFG3_IOSET ) | + (((exmc_pccard_init_struct->io_space_timing->waittime - 1U) << PIOTCFG_IOWAIT_OFFSET) & EXMC_PIOTCFG3_IOWAIT ) | + ((exmc_pccard_init_struct->io_space_timing->holdtime << PIOTCFG_IOHLD_OFFSET) & EXMC_PIOTCFG3_IOHLD )| + ((exmc_pccard_init_struct->io_space_timing->databus_hiztime << PIOTCFG_IOHIZ_OFFSET) & EXMC_PIOTCFG3_IOHIZ ); +} + +/*! + \brief enable PC Card Bank + \param[in] none + \param[out] none + \retval none +*/ +void exmc_pccard_enable(void) +{ + EXMC_NPCTL3 |= EXMC_NPCTL_NDBKEN; +} + +/*! + \brief disable PC Card Bank + \param[in] none + \param[out] none + \retval none +*/ +void exmc_pccard_disable(void) +{ + EXMC_NPCTL3 &= (~EXMC_NPCTL_NDBKEN); +} + +/*! + \brief enable EXMC interrupt + \param[in] exmc_bank: specifies the NAND bank,PC card bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK1_NAND: the NAND bank1 + \arg EXMC_BANK2_NAND: the NAND bank2 + \arg EXMC_BANK3_PCCARD: the PC card bank + \param[in] interrupt: EXMC interrupt flag + only one parameter can be selected which are shown as below: + \arg EXMC_NAND_PCCARD_INT_FLAG_RISE: rising edge interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_LEVEL: high-level interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_FALL: falling edge interrupt and corresponding flag + \param[out] none + \retval none +*/ +void exmc_interrupt_enable(uint32_t exmc_bank,uint32_t interrupt) +{ + /* NAND bank1,bank2 or PC card bank3 */ + EXMC_NPINTEN(exmc_bank) |= interrupt; +} + +/*! + \brief disable EXMC interrupt + \param[in] exmc_bank: specifies the NAND bank , PC card bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK1_NAND: the NAND bank1 + \arg EXMC_BANK2_NAND: the NAND bank2 + \arg EXMC_BANK3_PCCARD: the PC card bank + \param[in] interrupt: EXMC interrupt flag + only one parameter can be selected which are shown as below: + \arg EXMC_NAND_PCCARD_INT_FLAG_RISE: rising edge interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_LEVEL: high-level interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_FALL: falling edge interrupt and corresponding flag + \param[out] none + \retval none +*/ +void exmc_interrupt_disable(uint32_t exmc_bank,uint32_t interrupt) +{ + /* NAND bank1,bank2 or PC card bank3 */ + EXMC_NPINTEN(exmc_bank) &= (~interrupt); +} + +/*! + \brief get EXMC flag status + \param[in] exmc_bank: specifies the NAND bank , PC card bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK1_NAND: the NAND bank1 + \arg EXMC_BANK2_NAND: the NAND bank2 + \arg EXMC_BANK3_PCCARD: the PC Card bank + \param[in] flag: EXMC status and flag + only one parameter can be selected which are shown as below: + \arg EXMC_NAND_PCCARD_FLAG_RISE: interrupt rising edge status + \arg EXMC_NAND_PCCARD_FLAG_LEVEL: interrupt high-level status + \arg EXMC_NAND_PCCARD_FLAG_FALL: interrupt falling edge status + \arg EXMC_NAND_PCCARD_FLAG_FIFOE: FIFO empty flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus exmc_flag_get(uint32_t exmc_bank,uint32_t flag) +{ + uint32_t status = 0x00000000U; + + /* NAND bank1,bank2 or PC card bank3 */ + status = EXMC_NPINTEN(exmc_bank); + + if ((status & flag) != (uint32_t)flag ){ + /* flag is reset */ + return RESET; + }else{ + /* flag is set */ + return SET; + } +} + +/*! + \brief clear EXMC flag status + \param[in] exmc_bank: specifie the NAND bank , PCCARD bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK1_NAND: the NAND bank1 + \arg EXMC_BANK2_NAND: the NAND bank2 + \arg EXMC_BANK3_PCCARD: the PC card bank + \param[in] flag: EXMC status and flag + only one parameter can be selected which are shown as below: + \arg EXMC_NAND_PCCARD_FLAG_RISE: interrupt rising edge status + \arg EXMC_NAND_PCCARD_FLAG_LEVEL: interrupt high-level status + \arg EXMC_NAND_PCCARD_FLAG_FALL: interrupt falling edge status + \arg EXMC_NAND_PCCARD_FLAG_FIFOE: FIFO empty flag + \param[out] none + \retval none +*/ +void exmc_flag_clear(uint32_t exmc_bank,uint32_t flag) +{ + /* NAND bank1,bank2 or PC card bank3 */ + EXMC_NPINTEN(exmc_bank) &= (~flag); +} + +/*! + \brief get EXMC interrupt flag + \param[in] exmc_bank: specifies the NAND bank , PC card bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK1_NAND: the NAND bank1 + \arg EXMC_BANK2_NAND: the NAND bank2 + \arg EXMC_BANK3_PCCARD: the PC card bank + \param[in] interrupt: EXMC interrupt flag + only one parameter can be selected which are shown as below: + \arg EXMC_NAND_PCCARD_INT_FLAG_RISE: rising edge interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_LEVEL: high-level interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_FALL: falling edge interrupt and corresponding flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus exmc_interrupt_flag_get(uint32_t exmc_bank,uint32_t interrupt) +{ + uint32_t status = 0x00000000U,interrupt_enable = 0x00000000U,interrupt_state = 0x00000000U; + + /* NAND bank1,bank2 or PC card bank3 */ + status = EXMC_NPINTEN(exmc_bank); + interrupt_state = (status & (interrupt >> INTEN_INTS_OFFSET)); + + interrupt_enable = (status & interrupt); + + if ((interrupt_enable) && (interrupt_state)){ + /* interrupt flag is set */ + return SET; + }else{ + /* interrupt flag is reset */ + return RESET; + } +} + +/*! + \brief clear EXMC interrupt flag + \param[in] exmc_bank: specifies the NAND bank , PC card bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK1_NAND: the NAND bank1 + \arg EXMC_BANK2_NAND: the NAND bank2 + \arg EXMC_BANK3_PCCARD: the PC card bank + \param[in] interrupt: EXMC interrupt flag + only one parameter can be selected which are shown as below: + \arg EXMC_NAND_PCCARD_INT_FLAG_RISE: rising edge interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_LEVEL: high-level interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_FALL: falling edge interrupt and corresponding flag + \param[out] none + \retval none +*/ +void exmc_interrupt_flag_clear(uint32_t exmc_bank,uint32_t interrupt) +{ + /* NAND bank1,bank2 or PC card bank3 */ + EXMC_NPINTEN(exmc_bank) &= ~(interrupt >> INTEN_INTS_OFFSET); +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exmc.c.base@1.7.0 b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exmc.c.base@1.7.0 new file mode 100644 index 0000000..43a3158 --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exmc.c.base@1.7.0 @@ -0,0 +1,708 @@ +/*! + \file gd32e50x_exmc.c + \brief EXMC driver + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#include "gd32e50x_exmc.h" + +/* EXMC bank0 register reset value */ +#define BANK0_SNCTL_REGION0_RESET ((uint32_t)0x000030DBU) +#define BANK0_SNCTL_REGION1_2_3_RESET ((uint32_t)0x000030D2U) +#define BANK0_SNTCFG_RESET ((uint32_t)0x0FFFFFFFU) +#define BANK0_SNWTCFG_RESET ((uint32_t)0x0FFFFFFFU) + +/* EXMC bank1/2 register reset mask */ +#define BANK1_2_NPCTL_RESET ((uint32_t)0x00000018U) +#define BANK1_2_NPINTEN_RESET ((uint32_t)0x00000042U) +#define BANK1_2_NPCTCFG_RESET ((uint32_t)0xFCFCFCFCU) +#define BANK1_2_NPATCFG_RESET ((uint32_t)0xFCFCFCFCU) + +/* EXMC bank3 register reset mask */ +#define BANK3_NPCTL_RESET ((uint32_t)0x00000018U) +#define BANK3_NPINTEN_RESET ((uint32_t)0x00000043U) +#define BANK3_NPCTCFG_RESET ((uint32_t)0xFCFCFCFCU) +#define BANK3_NPATCFG_RESET ((uint32_t)0xFCFCFCFCU) +#define BANK3_PIOTCFG3_RESET ((uint32_t)0xFCFCFCFCU) + +/* EXMC register bit offset */ +#define SNCTL_NRMUX_OFFSET ((uint32_t)1U) +#define SNCTL_SBRSTEN_OFFSET ((uint32_t)8U) +#define SNCTL_WRAPEN_OFFSET ((uint32_t)10U) +#define SNCTL_WREN_OFFSET ((uint32_t)12U) +#define SNCTL_NRWTEN_OFFSET ((uint32_t)13U) +#define SNCTL_EXMODEN_OFFSET ((uint32_t)14U) +#define SNCTL_ASYNCWAIT_OFFSET ((uint32_t)15U) + +#define SNTCFG_AHLD_OFFSET ((uint32_t)4U) +#define SNTCFG_DSET_OFFSET ((uint32_t)8U) +#define SNTCFG_BUSLAT_OFFSET ((uint32_t)16U) + +#define SNWTCFG_WAHLD_OFFSET ((uint32_t)4U) +#define SNWTCFG_WDSET_OFFSET ((uint32_t)8U) +#define SNWTCFG_WBUSLAT_OFFSET ((uint32_t)16U) + +#define NPCTL_NDWTEN_OFFSET ((uint32_t)1U) +#define NPCTL_ECCEN_OFFSET ((uint32_t)6U) + +#define NPCTCFG_COMWAIT_OFFSET ((uint32_t)8U) +#define NPCTCFG_COMHLD_OFFSET ((uint32_t)16U) +#define NPCTCFG_COMHIZ_OFFSET ((uint32_t)24U) + +#define NPATCFG_ATTWAIT_OFFSET ((uint32_t)8U) +#define NPATCFG_ATTHLD_OFFSET ((uint32_t)16U) +#define NPATCFG_ATTHIZ_OFFSET ((uint32_t)24U) + +#define PIOTCFG_IOWAIT_OFFSET ((uint32_t)8U) +#define PIOTCFG_IOHLD_OFFSET ((uint32_t)16U) +#define PIOTCFG_IOHIZ_OFFSET ((uint32_t)24U) + +#define INTEN_INTS_OFFSET ((uint32_t)3U) + +/*! + \brief deinitialize EXMC NOR/SRAM region + \param[in] exmc_norsram_region: select the region of bank0 + only one parameter can be selected which is shown as below: + \arg EXMC_BANK0_NORSRAM_REGIONx(x=0..3) + \param[out] none + \retval none +*/ +void exmc_norsram_deinit(uint32_t exmc_norsram_region) +{ + /* reset the registers */ + if(EXMC_BANK0_NORSRAM_REGION0 == exmc_norsram_region){ + EXMC_SNCTL(exmc_norsram_region) = BANK0_SNCTL_REGION0_RESET; + }else{ + EXMC_SNCTL(exmc_norsram_region) = BANK0_SNCTL_REGION1_2_3_RESET; + } + EXMC_SNTCFG(exmc_norsram_region) = BANK0_SNTCFG_RESET; + EXMC_SNWTCFG(exmc_norsram_region) = BANK0_SNWTCFG_RESET; +} + +/*! + \brief initialize exmc_norsram_parameter_struct with the default values + \param[in] none + \param[out] exmc_norsram_init_struct: the initialized struct exmc_norsram_parameter_struct pointer + \retval none +*/ +void exmc_norsram_struct_para_init(exmc_norsram_parameter_struct* exmc_norsram_init_struct) +{ + /* configure the structure with default values */ + exmc_norsram_init_struct->norsram_region = EXMC_BANK0_NORSRAM_REGION0; + exmc_norsram_init_struct->address_data_mux = ENABLE; + exmc_norsram_init_struct->memory_type = EXMC_MEMORY_TYPE_SRAM; + exmc_norsram_init_struct->databus_width = EXMC_NOR_DATABUS_WIDTH_8B; + exmc_norsram_init_struct->burst_mode = DISABLE; + exmc_norsram_init_struct->nwait_polarity = EXMC_NWAIT_POLARITY_LOW; + exmc_norsram_init_struct->wrap_burst_mode = DISABLE; + exmc_norsram_init_struct->nwait_config = EXMC_NWAIT_CONFIG_BEFORE; + exmc_norsram_init_struct->memory_write = ENABLE; + exmc_norsram_init_struct->nwait_signal = ENABLE; + exmc_norsram_init_struct->extended_mode = DISABLE; + exmc_norsram_init_struct->asyn_wait = DISABLE; + exmc_norsram_init_struct->write_mode = EXMC_ASYN_WRITE; + + /* read/write timing configure */ + exmc_norsram_init_struct->read_write_timing->asyn_address_setuptime = 0xFU; + exmc_norsram_init_struct->read_write_timing->asyn_address_holdtime = 0xFU; + exmc_norsram_init_struct->read_write_timing->asyn_data_setuptime = 0xFFU; + exmc_norsram_init_struct->read_write_timing->bus_latency = 0xFU; + exmc_norsram_init_struct->read_write_timing->syn_clk_division = EXMC_SYN_CLOCK_RATIO_16_CLK; + exmc_norsram_init_struct->read_write_timing->syn_data_latency = EXMC_DATALAT_17_CLK; + exmc_norsram_init_struct->read_write_timing->asyn_access_mode = EXMC_ACCESS_MODE_A; + + /* write timing configure, when extended mode is used */ + exmc_norsram_init_struct->write_timing->asyn_address_setuptime = 0xFU; + exmc_norsram_init_struct->write_timing->asyn_address_holdtime = 0xFU; + exmc_norsram_init_struct->write_timing->asyn_data_setuptime = 0xFFU; + exmc_norsram_init_struct->write_timing->bus_latency = 0xFU; + exmc_norsram_init_struct->write_timing->asyn_access_mode = EXMC_ACCESS_MODE_A; +} + +/*! + \brief initialize EXMC NOR/SRAM region + \param[in] exmc_norsram_parameter_struct: configure the EXMC NOR/SRAM parameter + norsram_region: EXMC_BANK0_NORSRAM_REGIONx,x=0..3 + write_mode: EXMC_ASYN_WRITE,EXMC_SYN_WRITE + extended_mode: ENABLE or DISABLE + asyn_wait: ENABLE or DISABLE + nwait_signal: ENABLE or DISABLE + memory_write: ENABLE or DISABLE + nwait_config: EXMC_NWAIT_CONFIG_BEFORE,EXMC_NWAIT_CONFIG_DURING + wrap_burst_mode: ENABLE or DISABLE + nwait_polarity: EXMC_NWAIT_POLARITY_LOW,EXMC_NWAIT_POLARITY_HIGH + burst_mode: ENABLE or DISABLE + databus_width: EXMC_NOR_DATABUS_WIDTH_8B,EXMC_NOR_DATABUS_WIDTH_16B + memory_type: EXMC_MEMORY_TYPE_SRAM,EXMC_MEMORY_TYPE_PSRAM,EXMC_MEMORY_TYPE_NOR + address_data_mux: ENABLE or DISABLE + read_write_timing: struct exmc_norsram_timing_parameter_struct set the time + asyn_access_mode: EXMC_ACCESS_MODE_A,EXMC_ACCESS_MODE_B,EXMC_ACCESS_MODE_C,EXMC_ACCESS_MODE_D + syn_data_latency: EXMC_DATALAT_n_CLK,(n=2,..,17) + syn_clk_division: EXMC_SYN_CLOCK_RATIO_DISABLE, EXMC_SYN_CLOCK_RATIO_n_CLK,(n=2,..,16) + bus_latency: 1,..,16 + asyn_data_setuptime: 2,..,256 + asyn_address_holdtime: 2,..,16 + asyn_address_setuptime: 1,..,16 + write_timing: struct exmc_norsram_timing_parameter_struct set the time + asyn_access_mode: EXMC_ACCESS_MODE_A,EXMC_ACCESS_MODE_B,EXMC_ACCESS_MODE_C,EXMC_ACCESS_MODE_D + syn_data_latency: EXMC_DATALAT_n_CLK,(n=2,..,17) + syn_clk_division: EXMC_SYN_CLOCK_RATIO_DISABLE, EXMC_SYN_CLOCK_RATIO_n_CLK,(n=2,..,16) + bus_latency: 1,..,16 + asyn_data_setuptime: 2,..,256 + asyn_address_holdtime: 2,..,16 + asyn_address_setuptime: 1,..,16 + \param[out] none + \retval none +*/ +void exmc_norsram_init(exmc_norsram_parameter_struct* exmc_norsram_init_struct) +{ + uint32_t snctl = 0x00000000U,sntcfg = 0x00000000U,snwtcfg = 0x00000000U; + + /* get the register value */ + snctl = EXMC_SNCTL(exmc_norsram_init_struct->norsram_region); + + /* clear relative bits */ + snctl &= ((uint32_t)~(EXMC_SNCTL_NRMUX | EXMC_SNCTL_NRTP | EXMC_SNCTL_NRW | EXMC_SNCTL_SBRSTEN | + EXMC_SNCTL_NREN | EXMC_SNCTL_NRWTPOL | EXMC_SNCTL_WRAPEN | EXMC_SNCTL_NRWTCFG | + EXMC_SNCTL_WREN | EXMC_SNCTL_NRWTEN | EXMC_SNCTL_EXMODEN | EXMC_SNCTL_ASYNCWAIT | + EXMC_SNCTL_SYNCWR )); + + snctl |= (uint32_t)(exmc_norsram_init_struct->address_data_mux << SNCTL_NRMUX_OFFSET) | + exmc_norsram_init_struct->memory_type | + exmc_norsram_init_struct->databus_width | + (exmc_norsram_init_struct->burst_mode << SNCTL_SBRSTEN_OFFSET) | + exmc_norsram_init_struct->nwait_polarity | + (exmc_norsram_init_struct->wrap_burst_mode << SNCTL_WRAPEN_OFFSET) | + exmc_norsram_init_struct->nwait_config | + (exmc_norsram_init_struct->memory_write << SNCTL_WREN_OFFSET) | + (exmc_norsram_init_struct->nwait_signal << SNCTL_NRWTEN_OFFSET) | + (exmc_norsram_init_struct->extended_mode << SNCTL_EXMODEN_OFFSET) | + (exmc_norsram_init_struct->asyn_wait << SNCTL_ASYNCWAIT_OFFSET) | + exmc_norsram_init_struct->write_mode; + + sntcfg = (uint32_t)((exmc_norsram_init_struct->read_write_timing->asyn_address_setuptime - 1U ) & EXMC_SNTCFG_ASET )| + (((exmc_norsram_init_struct->read_write_timing->asyn_address_holdtime - 1U ) << SNTCFG_AHLD_OFFSET ) & EXMC_SNTCFG_AHLD ) | + (((exmc_norsram_init_struct->read_write_timing->asyn_data_setuptime - 1U ) << SNTCFG_DSET_OFFSET ) & EXMC_SNTCFG_DSET ) | + (((exmc_norsram_init_struct->read_write_timing->bus_latency - 1U ) << SNTCFG_BUSLAT_OFFSET ) & EXMC_SNTCFG_BUSLAT )| + exmc_norsram_init_struct->read_write_timing->syn_clk_division | + exmc_norsram_init_struct->read_write_timing->syn_data_latency | + exmc_norsram_init_struct->read_write_timing->asyn_access_mode; + + /* nor flash access enable */ + if(EXMC_MEMORY_TYPE_NOR == exmc_norsram_init_struct->memory_type){ + snctl |= (uint32_t)EXMC_SNCTL_NREN; + } + + /* extended mode configure */ + if(ENABLE == exmc_norsram_init_struct->extended_mode){ + snwtcfg = (uint32_t)((exmc_norsram_init_struct->write_timing->asyn_address_setuptime - 1U) & EXMC_SNWTCFG_WASET ) | + (((exmc_norsram_init_struct->write_timing->asyn_address_holdtime -1U ) << SNWTCFG_WAHLD_OFFSET ) & EXMC_SNWTCFG_WAHLD )| + (((exmc_norsram_init_struct->write_timing->asyn_data_setuptime -1U ) << SNWTCFG_WDSET_OFFSET ) & EXMC_SNWTCFG_WDSET )| + (((exmc_norsram_init_struct->write_timing->bus_latency - 1U ) << SNWTCFG_WBUSLAT_OFFSET ) & EXMC_SNWTCFG_WBUSLAT ) | + exmc_norsram_init_struct->write_timing->asyn_access_mode; + }else{ + snwtcfg = BANK0_SNWTCFG_RESET; + } + + /* configure the registers */ + EXMC_SNCTL(exmc_norsram_init_struct->norsram_region) = snctl; + EXMC_SNTCFG(exmc_norsram_init_struct->norsram_region) = sntcfg; + EXMC_SNWTCFG(exmc_norsram_init_struct->norsram_region) = snwtcfg; +} + +/*! + \brief enable EXMC NOR/PSRAM bank region + \param[in] exmc_norsram_region: specifie the region of NOR/PSRAM bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK0_NORSRAM_REGIONx(x=0..3) + \param[out] none + \retval none +*/ +void exmc_norsram_enable(uint32_t exmc_norsram_region) +{ + EXMC_SNCTL(exmc_norsram_region) |= (uint32_t)EXMC_SNCTL_NRBKEN; +} + +/*! + \brief disable EXMC NOR/PSRAM bank region + \param[in] exmc_norsram_region: specifie the region of NOR/PSRAM Bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK0_NORSRAM_REGIONx(x=0..3) + \param[out] none + \retval none +*/ +void exmc_norsram_disable(uint32_t exmc_norsram_region) +{ + EXMC_SNCTL(exmc_norsram_region) &= ~(uint32_t)EXMC_SNCTL_NRBKEN; +} + +/*! + \brief configure CRAM page size + \param[in] exmc_norsram_region: specifie the region of NOR/PSRAM bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK0_NORSRAM_REGIONx(x=0..3) + \param[in] page_size: CRAM page size + only one parameter can be selected which is shown as below: + \arg EXMC_CRAM_AUTO_SPLIT: the clock is generated only during synchronous access + \arg EXMC_CRAM_PAGE_SIZE_128_BYTES: page size is 128 bytes + \arg EXMC_CRAM_PAGE_SIZE_256_BYTES: page size is 256 bytes + \arg EXMC_CRAM_PAGE_SIZE_512_BYTES: page size is 512 bytes + \arg EXMC_CRAM_PAGE_SIZE_1024_BYTES: page size is 1024 bytes + \param[out] none + \retval none +*/ +void exmc_norsram_page_size_config(uint32_t exmc_norsram_region, uint32_t page_size) +{ + /* reset the bits */ + EXMC_SNCTL(exmc_norsram_region) &= ~EXMC_SNCTL_CPS; + + /* set the CPS bits */ + EXMC_SNCTL(exmc_norsram_region) |= page_size; +} + +/*! + \brief deinitialize EXMC NAND bank + \param[in] exmc_nand_bank: select the bank of NAND + only one parameter can be selected which is shown as below: + \arg EXMC_BANKx_NAND(x=1..2) + \param[out] none + \retval none +*/ +void exmc_nand_deinit(uint32_t exmc_nand_bank) +{ + /* EXMC_BANK1_NAND or EXMC_BANK2_NAND */ + EXMC_NPCTL(exmc_nand_bank) = BANK1_2_NPCTL_RESET; + EXMC_NPINTEN(exmc_nand_bank) = BANK1_2_NPINTEN_RESET; + EXMC_NPCTCFG(exmc_nand_bank) = BANK1_2_NPCTCFG_RESET; + EXMC_NPATCFG(exmc_nand_bank) = BANK1_2_NPATCFG_RESET; +} + +/*! + \brief initialize exmc_nand_parameter_struct with the default values + \param[in] none + \param[out] the initialized struct exmc_nand_parameter_struct pointer + \retval none +*/ +void exmc_nand_struct_para_init(exmc_nand_parameter_struct* exmc_nand_init_struct) +{ + /* configure the structure with default values */ + exmc_nand_init_struct->nand_bank = EXMC_BANK1_NAND; + exmc_nand_init_struct->wait_feature = DISABLE; + exmc_nand_init_struct->databus_width = EXMC_NAND_DATABUS_WIDTH_8B; + exmc_nand_init_struct->ecc_logic = DISABLE; + exmc_nand_init_struct->ecc_size = EXMC_ECC_SIZE_256BYTES; + exmc_nand_init_struct->ctr_latency = 0x0U; + exmc_nand_init_struct->atr_latency = 0x0U; + exmc_nand_init_struct->common_space_timing->setuptime = 0xFCU; + exmc_nand_init_struct->common_space_timing->waittime = 0xFCU; + exmc_nand_init_struct->common_space_timing->holdtime = 0xFCU; + exmc_nand_init_struct->common_space_timing->databus_hiztime = 0xFCU; + exmc_nand_init_struct->attribute_space_timing->setuptime = 0xFCU; + exmc_nand_init_struct->attribute_space_timing->waittime = 0xFCU; + exmc_nand_init_struct->attribute_space_timing->holdtime = 0xFCU; + exmc_nand_init_struct->attribute_space_timing->databus_hiztime = 0xFCU; +} + +/*! + \brief initialize EXMC NAND bank + \param[in] exmc_nand_parameter_struct: configure the EXMC NAND parameter + nand_bank: EXMC_BANK1_NAND,EXMC_BANK2_NAND + ecc_size: EXMC_ECC_SIZE_xBYTES,x=256,512,1024,2048,4096 + atr_latency: EXMC_ALE_RE_DELAY_x_HCLK,x=1..16 + ctr_latency: EXMC_CLE_RE_DELAY_x_HCLK,x=1..16 + ecc_logic: ENABLE or DISABLE + databus_width: EXMC_NAND_DATABUS_WIDTH_8B,EXMC_NAND_DATABUS_WIDTH_16B + wait_feature: ENABLE or DISABLE + common_space_timing: struct exmc_nand_pccard_timing_parameter_struct set the time + databus_hiztime: 1,..,255 + holdtime: 1,..,254 + waittime: 2,..,255 + setuptime: 1,..,255 + attribute_space_timing: struct exmc_nand_pccard_timing_parameter_struct set the time + databus_hiztime: 1,..,255 + holdtime: 1,..,254 + waittime: 2,..,255 + setuptime: 1,..,255 + \param[out] none + \retval none +*/ +void exmc_nand_init(exmc_nand_parameter_struct* exmc_nand_init_struct) +{ + uint32_t npctl = 0x00000000U, npctcfg = 0x00000000U, npatcfg = 0x00000000U; + + npctl = (uint32_t)(exmc_nand_init_struct->wait_feature << NPCTL_NDWTEN_OFFSET)| + EXMC_NPCTL_NDTP | + exmc_nand_init_struct->databus_width | + (exmc_nand_init_struct->ecc_logic << NPCTL_ECCEN_OFFSET)| + exmc_nand_init_struct->ecc_size | + exmc_nand_init_struct->ctr_latency | + exmc_nand_init_struct->atr_latency; + + npctcfg = (uint32_t)((exmc_nand_init_struct->common_space_timing->setuptime - 1U) & EXMC_NPCTCFG_COMSET ) | + (((exmc_nand_init_struct->common_space_timing->waittime - 1U) << NPCTCFG_COMWAIT_OFFSET) & EXMC_NPCTCFG_COMWAIT ) | + ((exmc_nand_init_struct->common_space_timing->holdtime << NPCTCFG_COMHLD_OFFSET) & EXMC_NPCTCFG_COMHLD ) | + (((exmc_nand_init_struct->common_space_timing->databus_hiztime - 1U) << NPCTCFG_COMHIZ_OFFSET) & EXMC_NPCTCFG_COMHIZ ); + + npatcfg = (uint32_t)((exmc_nand_init_struct->attribute_space_timing->setuptime - 1U) & EXMC_NPATCFG_ATTSET ) | + (((exmc_nand_init_struct->attribute_space_timing->waittime - 1U) << NPATCFG_ATTWAIT_OFFSET) & EXMC_NPATCFG_ATTWAIT ) | + ((exmc_nand_init_struct->attribute_space_timing->holdtime << NPATCFG_ATTHLD_OFFSET) & EXMC_NPATCFG_ATTHLD ) | + (((exmc_nand_init_struct->attribute_space_timing->databus_hiztime -1U) << NPATCFG_ATTHIZ_OFFSET) & EXMC_NPATCFG_ATTHIZ ); + + /* EXMC_BANK1_NAND or EXMC_BANK2_NAND initialize */ + EXMC_NPCTL(exmc_nand_init_struct->nand_bank) = npctl; + EXMC_NPCTCFG(exmc_nand_init_struct->nand_bank) = npctcfg; + EXMC_NPATCFG(exmc_nand_init_struct->nand_bank) = npatcfg; +} + +/*! + \brief enable NAND bank + \param[in] exmc_nand_bank: specifie the NAND bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANKx_NAND(x=1,2) + \param[out] none + \retval none +*/ +void exmc_nand_enable(uint32_t exmc_nand_bank) +{ + EXMC_NPCTL(exmc_nand_bank) |= EXMC_NPCTL_NDBKEN; +} + +/*! + \brief disable NAND bank + \param[in] exmc_nand_bank: specifie the NAND bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANKx_NAND(x=1,2) + \param[out] none + \retval none +*/ +void exmc_nand_disable(uint32_t exmc_nand_bank) +{ + EXMC_NPCTL(exmc_nand_bank) &= (~EXMC_NPCTL_NDBKEN); +} + +/*! + \brief enable or disable the EXMC NAND ECC function + \param[in] exmc_nand_bank: specifie the NAND bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANKx_NAND(x=1,2) + \param[in] newvalue: ENABLE or DISABLE + \param[out] none + \retval none +*/ +void exmc_nand_ecc_config(uint32_t exmc_nand_bank, ControlStatus newvalue) +{ + if (ENABLE == newvalue){ + /* enable the selected NAND bank ECC function */ + EXMC_NPCTL(exmc_nand_bank) |= EXMC_NPCTL_ECCEN; + }else{ + /* disable the selected NAND bank ECC function */ + EXMC_NPCTL(exmc_nand_bank) &= (~EXMC_NPCTL_ECCEN); + } +} + +/*! + \brief get the EXMC ECC value + \param[in] exmc_nand_bank: specifie the NAND bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANKx_NAND(x=1,2) + \param[out] none + \retval the error correction code(ECC) value +*/ +uint32_t exmc_ecc_get(uint32_t exmc_nand_bank) +{ + return (EXMC_NECC(exmc_nand_bank)); +} + +/*! + \brief deinitialize EXMC PC card bank + \param[in] none + \param[out] none + \retval none +*/ +void exmc_pccard_deinit(void) +{ + /* EXMC_BANK3_PCCARD */ + EXMC_NPCTL3 = BANK3_NPCTL_RESET; + EXMC_NPINTEN3 = BANK3_NPINTEN_RESET; + EXMC_NPCTCFG3 = BANK3_NPCTCFG_RESET; + EXMC_NPATCFG3 = BANK3_NPATCFG_RESET; + EXMC_PIOTCFG3 = BANK3_PIOTCFG3_RESET; +} + +/*! + \brief initialize exmc_pccard_parameter_struct parameter with the default values + \param[in] none + \param[out] the initialized struct exmc_pccard_parameter_struct pointer + \retval none +*/ +void exmc_pccard_struct_para_init(exmc_pccard_parameter_struct* exmc_pccard_init_struct) +{ + /* configure the structure with default values */ + exmc_pccard_init_struct->wait_feature = DISABLE; + exmc_pccard_init_struct->ctr_latency = 0x0U; + exmc_pccard_init_struct->atr_latency = 0x0U; + exmc_pccard_init_struct->common_space_timing->setuptime = 0xFCU; + exmc_pccard_init_struct->common_space_timing->waittime = 0xFCU; + exmc_pccard_init_struct->common_space_timing->holdtime = 0xFCU; + exmc_pccard_init_struct->common_space_timing->databus_hiztime = 0xFCU; + exmc_pccard_init_struct->attribute_space_timing->setuptime = 0xFCU; + exmc_pccard_init_struct->attribute_space_timing->waittime = 0xFCU; + exmc_pccard_init_struct->attribute_space_timing->holdtime = 0xFCU; + exmc_pccard_init_struct->attribute_space_timing->databus_hiztime = 0xFCU; + exmc_pccard_init_struct->io_space_timing->setuptime = 0xFCU; + exmc_pccard_init_struct->io_space_timing->waittime = 0xFCU; + exmc_pccard_init_struct->io_space_timing->holdtime = 0xFCU; + exmc_pccard_init_struct->io_space_timing->databus_hiztime = 0xFCU; +} + +/*! + \brief initialize EXMC PC card bank + \param[in] exmc_pccard_parameter_struct: configure the EXMC NAND parameter + atr_latency: EXMC_ALE_RE_DELAY_x_HCLK,x=1..16 + ctr_latency: EXMC_CLE_RE_DELAY_x_HCLK,x=1..16 + wait_feature: ENABLE or DISABLE + common_space_timing: struct exmc_nand_pccard_timing_parameter_struct set the time + databus_hiztime: 1,..,255 + holdtime: 1,..,254 + waittime: 2,..,255 + setuptime: 1,..,255 + attribute_space_timing: struct exmc_nand_pccard_timing_parameter_struct set the time + databus_hiztime: 1,..,255 + holdtime: 1,..,254 + waittime: 2,..,255 + setuptime: 1,..,255 + io_space_timing: exmc_nand_pccard_timing_parameter_struct set the time + databus_hiztime: 0,..,255 + holdtime: 1,..,255 + waittime: 2,..,256 + setuptime: 1,..,256 + \param[out] none + \retval none +*/ +void exmc_pccard_init(exmc_pccard_parameter_struct* exmc_pccard_init_struct) +{ + /* configure the EXMC bank3 PC card control register */ + EXMC_NPCTL3 = (uint32_t)(exmc_pccard_init_struct->wait_feature << NPCTL_NDWTEN_OFFSET) | + EXMC_NAND_DATABUS_WIDTH_16B | + exmc_pccard_init_struct->ctr_latency | + exmc_pccard_init_struct->atr_latency ; + + /* configure the EXMC bank3 PC card common space timing configuration register */ + EXMC_NPCTCFG3 = (uint32_t)((exmc_pccard_init_struct->common_space_timing->setuptime - 1U)& EXMC_NPCTCFG_COMSET ) | + (((exmc_pccard_init_struct->common_space_timing->waittime - 1U) << NPCTCFG_COMWAIT_OFFSET) & EXMC_NPCTCFG_COMWAIT ) | + ((exmc_pccard_init_struct->common_space_timing->holdtime << NPCTCFG_COMHLD_OFFSET) & EXMC_NPCTCFG_COMHLD ) | + (((exmc_pccard_init_struct->common_space_timing->databus_hiztime - 1U) << NPCTCFG_COMHIZ_OFFSET) & EXMC_NPCTCFG_COMHIZ ); + + /* configure the EXMC bank3 PC card attribute space timing configuration register */ + EXMC_NPATCFG3 = (uint32_t)((exmc_pccard_init_struct->attribute_space_timing->setuptime - 1U) & EXMC_NPATCFG_ATTSET ) | + (((exmc_pccard_init_struct->attribute_space_timing->waittime - 1U) << NPATCFG_ATTWAIT_OFFSET) & EXMC_NPATCFG_ATTWAIT ) | + ((exmc_pccard_init_struct->attribute_space_timing->holdtime << NPATCFG_ATTHLD_OFFSET) & EXMC_NPATCFG_ATTHLD )| + (((exmc_pccard_init_struct->attribute_space_timing->databus_hiztime -1U) << NPATCFG_ATTHIZ_OFFSET) & EXMC_NPATCFG_ATTHIZ ); + + /* configure the EXMC bank3 PC card io space timing configuration register */ + EXMC_PIOTCFG3 = (uint32_t)((exmc_pccard_init_struct->io_space_timing->setuptime - 1U) & EXMC_PIOTCFG3_IOSET ) | + (((exmc_pccard_init_struct->io_space_timing->waittime - 1U) << PIOTCFG_IOWAIT_OFFSET) & EXMC_PIOTCFG3_IOWAIT ) | + ((exmc_pccard_init_struct->io_space_timing->holdtime << PIOTCFG_IOHLD_OFFSET) & EXMC_PIOTCFG3_IOHLD )| + ((exmc_pccard_init_struct->io_space_timing->databus_hiztime << PIOTCFG_IOHIZ_OFFSET) & EXMC_PIOTCFG3_IOHIZ ); +} + +/*! + \brief enable PC Card Bank + \param[in] none + \param[out] none + \retval none +*/ +void exmc_pccard_enable(void) +{ + EXMC_NPCTL3 |= EXMC_NPCTL_NDBKEN; +} + +/*! + \brief disable PC Card Bank + \param[in] none + \param[out] none + \retval none +*/ +void exmc_pccard_disable(void) +{ + EXMC_NPCTL3 &= (~EXMC_NPCTL_NDBKEN); +} + +/*! + \brief enable EXMC interrupt + \param[in] exmc_bank: specifies the NAND bank,PC card bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK1_NAND: the NAND bank1 + \arg EXMC_BANK2_NAND: the NAND bank2 + \arg EXMC_BANK3_PCCARD: the PC card bank + \param[in] interrupt: EXMC interrupt flag + only one parameter can be selected which are shown as below: + \arg EXMC_NAND_PCCARD_INT_FLAG_RISE: rising edge interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_LEVEL: high-level interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_FALL: falling edge interrupt and corresponding flag + \param[out] none + \retval none +*/ +void exmc_interrupt_enable(uint32_t exmc_bank,uint32_t interrupt) +{ + /* NAND bank1,bank2 or PC card bank3 */ + EXMC_NPINTEN(exmc_bank) |= interrupt; +} + +/*! + \brief disable EXMC interrupt + \param[in] exmc_bank: specifies the NAND bank , PC card bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK1_NAND: the NAND bank1 + \arg EXMC_BANK2_NAND: the NAND bank2 + \arg EXMC_BANK3_PCCARD: the PC card bank + \param[in] interrupt: EXMC interrupt flag + only one parameter can be selected which are shown as below: + \arg EXMC_NAND_PCCARD_INT_FLAG_RISE: rising edge interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_LEVEL: high-level interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_FALL: falling edge interrupt and corresponding flag + \param[out] none + \retval none +*/ +void exmc_interrupt_disable(uint32_t exmc_bank,uint32_t interrupt) +{ + /* NAND bank1,bank2 or PC card bank3 */ + EXMC_NPINTEN(exmc_bank) &= (~interrupt); +} + +/*! + \brief get EXMC flag status + \param[in] exmc_bank: specifies the NAND bank , PC card bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK1_NAND: the NAND bank1 + \arg EXMC_BANK2_NAND: the NAND bank2 + \arg EXMC_BANK3_PCCARD: the PC Card bank + \param[in] flag: EXMC status and flag + only one parameter can be selected which are shown as below: + \arg EXMC_NAND_PCCARD_FLAG_RISE: interrupt rising edge status + \arg EXMC_NAND_PCCARD_FLAG_LEVEL: interrupt high-level status + \arg EXMC_NAND_PCCARD_FLAG_FALL: interrupt falling edge status + \arg EXMC_NAND_PCCARD_FLAG_FIFOE: FIFO empty flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus exmc_flag_get(uint32_t exmc_bank,uint32_t flag) +{ + uint32_t status = 0x00000000U; + + /* NAND bank1,bank2 or PC card bank3 */ + status = EXMC_NPINTEN(exmc_bank); + + if ((status & flag) != (uint32_t)flag ){ + /* flag is reset */ + return RESET; + }else{ + /* flag is set */ + return SET; + } +} + +/*! + \brief clear EXMC flag status + \param[in] exmc_bank: specifie the NAND bank , PCCARD bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK1_NAND: the NAND bank1 + \arg EXMC_BANK2_NAND: the NAND bank2 + \arg EXMC_BANK3_PCCARD: the PC card bank + \param[in] flag: EXMC status and flag + only one parameter can be selected which are shown as below: + \arg EXMC_NAND_PCCARD_FLAG_RISE: interrupt rising edge status + \arg EXMC_NAND_PCCARD_FLAG_LEVEL: interrupt high-level status + \arg EXMC_NAND_PCCARD_FLAG_FALL: interrupt falling edge status + \arg EXMC_NAND_PCCARD_FLAG_FIFOE: FIFO empty flag + \param[out] none + \retval none +*/ +void exmc_flag_clear(uint32_t exmc_bank,uint32_t flag) +{ + /* NAND bank1,bank2 or PC card bank3 */ + EXMC_NPINTEN(exmc_bank) &= (~flag); +} + +/*! + \brief get EXMC interrupt flag + \param[in] exmc_bank: specifies the NAND bank , PC card bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK1_NAND: the NAND bank1 + \arg EXMC_BANK2_NAND: the NAND bank2 + \arg EXMC_BANK3_PCCARD: the PC card bank + \param[in] interrupt: EXMC interrupt flag + only one parameter can be selected which are shown as below: + \arg EXMC_NAND_PCCARD_INT_FLAG_RISE: rising edge interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_LEVEL: high-level interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_FALL: falling edge interrupt and corresponding flag + \param[out] none + \retval FlagStatus: SET or RESET +*/ +FlagStatus exmc_interrupt_flag_get(uint32_t exmc_bank,uint32_t interrupt) +{ + uint32_t status = 0x00000000U,interrupt_enable = 0x00000000U,interrupt_state = 0x00000000U; + + /* NAND bank1,bank2 or PC card bank3 */ + status = EXMC_NPINTEN(exmc_bank); + interrupt_state = (status & (interrupt >> INTEN_INTS_OFFSET)); + + interrupt_enable = (status & interrupt); + + if ((interrupt_enable) && (interrupt_state)){ + /* interrupt flag is set */ + return SET; + }else{ + /* interrupt flag is reset */ + return RESET; + } +} + +/*! + \brief clear EXMC interrupt flag + \param[in] exmc_bank: specifies the NAND bank , PC card bank + only one parameter can be selected which is shown as below: + \arg EXMC_BANK1_NAND: the NAND bank1 + \arg EXMC_BANK2_NAND: the NAND bank2 + \arg EXMC_BANK3_PCCARD: the PC card bank + \param[in] interrupt: EXMC interrupt flag + only one parameter can be selected which are shown as below: + \arg EXMC_NAND_PCCARD_INT_FLAG_RISE: rising edge interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_LEVEL: high-level interrupt and corresponding flag + \arg EXMC_NAND_PCCARD_INT_FLAG_FALL: falling edge interrupt and corresponding flag + \param[out] none + \retval none +*/ +void exmc_interrupt_flag_clear(uint32_t exmc_bank,uint32_t interrupt) +{ + /* NAND bank1,bank2 or PC card bank3 */ + EXMC_NPINTEN(exmc_bank) &= ~(interrupt >> INTEN_INTS_OFFSET); +} diff --git a/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exmc.h b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exmc.h new file mode 100644 index 0000000..6eb110f --- /dev/null +++ b/GD32E508_DCU_APP/RTE/Device/GD32E508VE/gd32e50x_exmc.h @@ -0,0 +1,440 @@ +/*! + \file gd32e50x_exmc.h + \brief definitions for the EXMC + + \version 2026-02-09, V1.7.0, firmware for GD32E50x +*/ + +/* + Copyright (c) 2025, GigaDevice Semiconductor Inc. + + Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#ifndef GD32E50X_EXMC_H +#define GD32E50X_EXMC_H + +#include "gd32e50x.h" + +/* EXMC definitions */ +#define EXMC (EXMC_BASE) /*!< EXMC register base address */ + +/* registers definitions */ +/* NOR/PSRAM */ +#define EXMC_SNCTL0 REG32(EXMC + 0x00U) /*!< EXMC SRAM/NOR flash control register0 */ +#define EXMC_SNTCFG0 REG32(EXMC + 0x04U) /*!< EXMC SRAM/NOR flash timing configuration register0 */ +#define EXMC_SNWTCFG0 REG32(EXMC + 0x104U) /*!< EXMC SRAM/NOR flash write timing configuration register0 */ + +#define EXMC_SNCTL1 REG32(EXMC + 0x08U) /*!< EXMC SRAM/NOR flash control register1 */ +#define EXMC_SNTCFG1 REG32(EXMC + 0x0CU) /*!< EXMC SRAM/NOR flash timing configuration register1 */ +#define EXMC_SNWTCFG1 REG32(EXMC + 0x10CU) /*!< EXMC SRAM/NOR flash write timing configuration register1 */ + +#define EXMC_SNCTL2 REG32(EXMC + 0x10U) /*!< EXMC SRAM/NOR flash control register2 */ +#define EXMC_SNTCFG2 REG32(EXMC + 0x14U) /*!< EXMC SRAM/NOR flash timing configuration register2 */ +#define EXMC_SNWTCFG2 REG32(EXMC + 0x114U) /*!< EXMC SRAM/NOR flash write timing configuration register2 */ + +#define EXMC_SNCTL3 REG32(EXMC + 0x18U) /*!< EXMC SRAM/NOR flash control register3 */ +#define EXMC_SNTCFG3 REG32(EXMC + 0x1CU) /*!< EXMC SRAM/NOR flash timing configuration register3 */ +#define EXMC_SNWTCFG3 REG32(EXMC + 0x11CU) /*!< EXMC SRAM/NOR flash write timing configuration register3 */ + +/* NAND/PC card */ +#define EXMC_NPCTL1 REG32(EXMC + 0x60U) /*!< EXMC NAND/PC card control register1 */ +#define EXMC_NPINTEN1 REG32(EXMC + 0x64U) /*!< EXMC NAND/PC card interrupt enable register1 */ +#define EXMC_NPCTCFG1 REG32(EXMC + 0x68U) /*!< EXMC NAND/PC card common space timing configuration register1 */ +#define EXMC_NPATCFG1 REG32(EXMC + 0x6CU) /*!< EXMC NAND/PC card attribute space timing configuration register1 */ +#define EXMC_NECC1 REG32(EXMC + 0x74U) /*!< EXMC NAND ECC register1 */ + +#define EXMC_NPCTL2 REG32(EXMC + 0x80U) /*!< EXMC NAND/PC card control register2 */ +#define EXMC_NPINTEN2 REG32(EXMC + 0x84U) /*!< EXMC NAND/PC card interrupt enable register2 */ +#define EXMC_NPCTCFG2 REG32(EXMC + 0x88U) /*!< EXMC NAND/PC card common space timing configuration register2 */ +#define EXMC_NPATCFG2 REG32(EXMC + 0x8CU) /*!< EXMC NAND/PC card attribute space timing configuration register2 */ +#define EXMC_NECC2 REG32(EXMC + 0x94U) /*!< EXMC NAND ECC register2 */ + +#define EXMC_NPCTL3 REG32(EXMC + 0xA0U) /*!< EXMC NAND/PC card control register3 */ +#define EXMC_NPINTEN3 REG32(EXMC + 0xA4U) /*!< EXMC NAND/PC card interrupt enable register3 */ +#define EXMC_NPCTCFG3 REG32(EXMC + 0xA8U) /*!< EXMC NAND/PC card common space timing configuration register3 */ +#define EXMC_NPATCFG3 REG32(EXMC + 0xACU) /*!< EXMC NAND/PC card attribute space timing configuration register3 */ +#define EXMC_PIOTCFG3 REG32(EXMC + 0xB0U) /*!< EXMC PC card I/O space timing configuration register */ + +/* bits definitions */ +/* EXMC_SNCTLx,x=0..3 */ +#define EXMC_SNCTL_NRBKEN BIT(0) /*!< NOR bank enable */ +#define EXMC_SNCTL_NRMUX BIT(1) /*!< NOR bank memory address/data multiplexing enable */ +#define EXMC_SNCTL_NRTP BITS(2,3) /*!< NOR bank memory type */ +#define EXMC_SNCTL_NRW BITS(4,5) /*!< NOR bank memory data bus width */ +#define EXMC_SNCTL_NREN BIT(6) /*!< NOR flash access enable */ +#define EXMC_SNCTL_SBRSTEN BIT(8) /*!< synchronous burst enable */ +#define EXMC_SNCTL_NRWTPOL BIT(9) /*!< NWAIT signal polarity */ +#define EXMC_SNCTL_WRAPEN BIT(10) /*!< wrapped burst mode enable */ +#define EXMC_SNCTL_NRWTCFG BIT(11) /*!< NWAIT signal configuration, only work in synchronous mode */ +#define EXMC_SNCTL_WREN BIT(12) /*!< write enable */ +#define EXMC_SNCTL_NRWTEN BIT(13) /*!< NWAIT signal enable */ +#define EXMC_SNCTL_EXMODEN BIT(14) /*!< extended mode enable */ +#define EXMC_SNCTL_ASYNCWAIT BIT(15) /*!< asynchronous wait enable */ +#define EXMC_SNCTL_CPS BITS(16,18) /*!< CRAM page size */ +#define EXMC_SNCTL_SYNCWR BIT(19) /*!< synchronous write config */ + +/* EXMC_SNTCFGx,x=0..3 */ +#define EXMC_SNTCFG_ASET BITS(0,3) /*!< asynchronous address setup time */ +#define EXMC_SNTCFG_AHLD BITS(4,7) /*!< asynchronous address hold time */ +#define EXMC_SNTCFG_DSET BITS(8,15) /*!< asynchronous data setup time */ +#define EXMC_SNTCFG_BUSLAT BITS(16,19) /*!< bus latency */ +#define EXMC_SNTCFG_CKDIV BITS(20,23) /*!< synchronous clock divide ratio */ +#define EXMC_SNTCFG_DLAT BITS(24,27) /*!< synchronous data latency for NOR flash */ +#define EXMC_SNTCFG_ASYNCMOD BITS(28,29) /*!< asynchronous access mode */ + +/* EXMC_SNWTCFGx,x=0..3 */ +#define EXMC_SNWTCFG_WASET BITS(0,3) /*!< asynchronous address setup time */ +#define EXMC_SNWTCFG_WAHLD BITS(4,7) /*!< asynchronous address hold time */ +#define EXMC_SNWTCFG_WDSET BITS(8,15) /*!< asynchronous data setup time */ +#define EXMC_SNWTCFG_WBUSLAT BITS(16,19) /*!< bus latency */ +#define EXMC_SNWTCFG_WASYNCMOD BITS(28,29) /*!< asynchronous access mode */ + +/* EXMC_NPCTLx,x=1..3 */ +#define EXMC_NPCTL_NDWTEN BIT(1) /*!< wait feature enable */ +#define EXMC_NPCTL_NDBKEN BIT(2) /*!< NAND bank enable */ +#define EXMC_NPCTL_NDTP BIT(3) /*!< NAND bank memory type */ +#define EXMC_NPCTL_NDW BITS(4,5) /*!< NAND bank memory data bus width */ +#define EXMC_NPCTL_ECCEN BIT(6) /*!< ECC enable */ +#define EXMC_NPCTL_CTR BITS(9,12) /*!< CLE to RE delay */ +#define EXMC_NPCTL_ATR BITS(13,16) /*!< ALE to RE delay */ +#define EXMC_NPCTL_ECCSZ BITS(17,19) /*!< ECC size */ + +/* EXMC_NPINTENx,x=1..3 */ +#define EXMC_NPINTEN_INTRS BIT(0) /*!< interrupt rising edge status */ +#define EXMC_NPINTEN_INTHS BIT(1) /*!< interrupt high-level status */ +#define EXMC_NPINTEN_INTFS BIT(2) /*!< interrupt falling edge status */ +#define EXMC_NPINTEN_INTREN BIT(3) /*!< interrupt rising edge detection enable */ +#define EXMC_NPINTEN_INTHEN BIT(4) /*!< interrupt high-level detection enable */ +#define EXMC_NPINTEN_INTFEN BIT(5) /*!< interrupt falling edge detection enable */ +#define EXMC_NPINTEN_FFEPT BIT(6) /*!< FIFO empty flag */ + +/* EXMC_NPCTCFGx,x=1..3 */ +#define EXMC_NPCTCFG_COMSET BITS(0,7) /*!< common memory setup time */ +#define EXMC_NPCTCFG_COMWAIT BITS(8,15) /*!< common memory wait time */ +#define EXMC_NPCTCFG_COMHLD BITS(16,23) /*!< common memory hold time */ +#define EXMC_NPCTCFG_COMHIZ BITS(24,31) /*!< common memory data bus HiZ time */ + +/* EXMC_NPATCFGx,x=1..3 */ +#define EXMC_NPATCFG_ATTSET BITS(0,7) /*!< attribute memory setup time */ +#define EXMC_NPATCFG_ATTWAIT BITS(8,15) /*!< attribute memory wait time */ +#define EXMC_NPATCFG_ATTHLD BITS(16,23) /*!< attribute memory hold time */ +#define EXMC_NPATCFG_ATTHIZ BITS(24,31) /*!< attribute memory data bus HiZ time */ + +/* EXMC_PIOTCFG3 */ +#define EXMC_PIOTCFG3_IOSET BITS(0,7) /*!< IO space setup time */ +#define EXMC_PIOTCFG3_IOWAIT BITS(8,15) /*!< IO space wait time */ +#define EXMC_PIOTCFG3_IOHLD BITS(16,23) /*!< IO space hold time */ +#define EXMC_PIOTCFG3_IOHIZ BITS(24,31) /*!< IO space data bus HiZ time */ + +/* EXMC_NECCx,x=1,2 */ +#define EXMC_NECC_ECC BITS(0,31) /*!< ECC result */ + +/* constants definitions */ +/* EXMC NOR/SRAM timing initialize struct */ +typedef struct +{ + uint32_t asyn_access_mode; /*!< asynchronous access mode */ + uint32_t syn_data_latency; /*!< configure the data latency, synchronous access mode valid */ + uint32_t syn_clk_division; /*!< configure the clock divide ratio, synchronous access mode valid */ + uint32_t bus_latency; /*!< configure the bus latency */ + uint32_t asyn_data_setuptime; /*!< configure the data setup time, asynchronous access mode valid */ + uint32_t asyn_address_holdtime; /*!< configure the address hold time, asynchronous access mode valid */ + uint32_t asyn_address_setuptime; /*!< configure the data setup time, asynchronous access mode valid */ +}exmc_norsram_timing_parameter_struct; + +/* EXMC NOR/SRAM initialize struct */ +typedef struct +{ + uint32_t norsram_region; /*!< select the region of EXMC NOR/SRAM bank */ + uint32_t write_mode; /*!< the write mode, synchronous mode or asynchronous mode */ + uint32_t extended_mode; /*!< enable or disable the extended mode */ + uint32_t asyn_wait; /*!< enable or disable the asynchronous wait function */ + uint32_t nwait_signal; /*!< enable or disable the NWAIT signal while in synchronous bust mode */ + uint32_t memory_write; /*!< enable or disable the write operation */ + uint32_t nwait_config; /*!< NWAIT signal configuration, only work in synchronous mode */ + uint32_t wrap_burst_mode; /*!< enable or disable the wrap burst mode */ + uint32_t nwait_polarity; /*!< specifies the polarity of NWAIT signal from memory */ + uint32_t burst_mode; /*!< enable or disable the burst mode */ + uint32_t databus_width; /*!< specifies the databus width of external memory */ + uint32_t memory_type; /*!< specifies the type of external memory */ + uint32_t address_data_mux; /*!< specifies whether the data bus and address bus are multiplexed */ + exmc_norsram_timing_parameter_struct* read_write_timing; /*!< timing parameters for read and write if the extended mode is not used or the timing + parameters for read if the extended mode is used */ + exmc_norsram_timing_parameter_struct* write_timing; /*!< timing parameters for write when the extended mode is used */ +}exmc_norsram_parameter_struct; + +/* EXMC NAND/PC card timing initialize struct */ +typedef struct +{ + uint32_t databus_hiztime; /*!< configure the dadtabus HiZ time for write operation */ + uint32_t holdtime; /*!< configure the address hold time(or the data hold time for write operation) */ + uint32_t waittime; /*!< configure the minimum wait time */ + uint32_t setuptime; /*!< configure the address setup time */ +}exmc_nand_pccard_timing_parameter_struct; + +/* EXMC NAND initialize struct */ +typedef struct +{ + uint32_t nand_bank; /*!< select the bank of NAND */ + uint32_t ecc_size; /*!< the page size for the ECC calculation */ + uint32_t atr_latency; /*!< configure the latency of ALE low to RB low */ + uint32_t ctr_latency; /*!< configure the latency of CLE low to RB low */ + uint32_t ecc_logic; /*!< enable or disable the ECC calculation logic */ + uint32_t databus_width; /*!< the NAND flash databus width */ + uint32_t wait_feature; /*!< enable or disable the wait feature */ + exmc_nand_pccard_timing_parameter_struct* common_space_timing; /*!< the timing parameters for NAND flash common space */ + exmc_nand_pccard_timing_parameter_struct* attribute_space_timing; /*!< the timing parameters for NAND flash attribute space */ +}exmc_nand_parameter_struct; + +/* EXMC PC card initialize struct */ +typedef struct +{ + uint32_t atr_latency; /*!< configure the latency of ALE low to RB low */ + uint32_t ctr_latency; /*!< configure the latency of CLE low to RB low */ + uint32_t wait_feature; /*!< enable or disable the wait feature */ + exmc_nand_pccard_timing_parameter_struct* common_space_timing; /*!< the timing parameters for PC card common space */ + exmc_nand_pccard_timing_parameter_struct* attribute_space_timing; /*!< the timing parameters for PC card attribute space */ + exmc_nand_pccard_timing_parameter_struct* io_space_timing; /*!< the timing parameters for PC card IO space */ +}exmc_pccard_parameter_struct; + +/* EXMC_register address */ +#define EXMC_SNCTL(region) REG32(EXMC + 0x08U * (region)) /*!< EXMC SRAM/NOR flash control registers, region = 0,1,2,3 */ +#define EXMC_SNTCFG(region) REG32(EXMC + 0x04U + 0x08U * (region)) /*!< EXMC SRAM/NOR flash timing configuration registers, region = 0,1,2,3 */ +#define EXMC_SNWTCFG(region) REG32(EXMC + 0x104U + 0x08U * (region)) /*!< EXMC SRAM/NOR flash write timing configuration registers, region = 0,1,2,3 */ + +#define EXMC_NPCTL(bank) REG32(EXMC + 0x40U + 0x20U * (bank)) /*!< EXMC NAND/PC card control registers, bank = 1,2,3 */ +#define EXMC_NPINTEN(bank) REG32(EXMC + 0x44U + 0x20U * (bank)) /*!< EXMC NAND/PC card interrupt enable registers, bank = 1,2,3 */ +#define EXMC_NPCTCFG(bank) REG32(EXMC + 0x48U + 0x20U * (bank)) /*!< EXMC NAND/PC card common space timing configuration registers, bank = 1,2,3 */ +#define EXMC_NPATCFG(bank) REG32(EXMC + 0x4CU + 0x20U * (bank)) /*!< EXMC NAND/PC card attribute space timing configuration registers, bank = 1,2,3 */ +#define EXMC_NECC(bank) REG32(EXMC + 0x54U + 0x20U * (bank)) /*!< EXMC NAND ECC registers, bank = 1,2 */ + +/* CRAM page size */ +#define SNCTL_CPS(regval) (BITS(16,18) & ((uint32_t)(regval) << 16)) +#define EXMC_CRAM_AUTO_SPLIT SNCTL_CPS(0) /*!< automatic burst split on page boundary crossing */ +#define EXMC_CRAM_PAGE_SIZE_128_BYTES SNCTL_CPS(1) /*!< page size is 128 bytes */ +#define EXMC_CRAM_PAGE_SIZE_256_BYTES SNCTL_CPS(2) /*!< page size is 256 bytes */ +#define EXMC_CRAM_PAGE_SIZE_512_BYTES SNCTL_CPS(3) /*!< page size is 512 bytes */ +#define EXMC_CRAM_PAGE_SIZE_1024_BYTES SNCTL_CPS(4) /*!< page size is 1024 bytes */ + +/* NOR bank memory data bus width */ +#define SNCTL_NRW(regval) (BITS(4,5) & ((uint32_t)(regval) << 4)) +#define EXMC_NOR_DATABUS_WIDTH_8B SNCTL_NRW(0) /*!< NOR data width is 8 bits */ +#define EXMC_NOR_DATABUS_WIDTH_16B SNCTL_NRW(1) /*!< NOR data width is 16 bits */ + +/* NOR bank memory type */ +#define SNCTL_NRTP(regval) (BITS(2,3) & ((uint32_t)(regval) << 2)) +#define EXMC_MEMORY_TYPE_SRAM SNCTL_NRTP(0) /*!< SRAM,ROM */ +#define EXMC_MEMORY_TYPE_PSRAM SNCTL_NRTP(1) /*!< PSRAM,CRAM */ +#define EXMC_MEMORY_TYPE_NOR SNCTL_NRTP(2) /*!< NOR flash */ + +/* asynchronous access mode */ +#define SNTCFG_ASYNCMOD(regval) (BITS(28,29) & ((uint32_t)(regval) << 28)) +#define EXMC_ACCESS_MODE_A SNTCFG_ASYNCMOD(0) /*!< mode A access */ +#define EXMC_ACCESS_MODE_B SNTCFG_ASYNCMOD(1) /*!< mode B access */ +#define EXMC_ACCESS_MODE_C SNTCFG_ASYNCMOD(2) /*!< mode C access */ +#define EXMC_ACCESS_MODE_D SNTCFG_ASYNCMOD(3) /*!< mode D access */ + +/* data latency for NOR flash */ +#define SNTCFG_DLAT(regval) (BITS(24,27) & ((uint32_t)(regval) << 24)) +#define EXMC_DATALAT_2_CLK SNTCFG_DLAT(0) /*!< data latency of first burst access is 2 EXMC_CLK */ +#define EXMC_DATALAT_3_CLK SNTCFG_DLAT(1) /*!< data latency of first burst access is 3 EXMC_CLK */ +#define EXMC_DATALAT_4_CLK SNTCFG_DLAT(2) /*!< data latency of first burst access is 4 EXMC_CLK */ +#define EXMC_DATALAT_5_CLK SNTCFG_DLAT(3) /*!< data latency of first burst access is 5 EXMC_CLK */ +#define EXMC_DATALAT_6_CLK SNTCFG_DLAT(4) /*!< data latency of first burst access is 6 EXMC_CLK */ +#define EXMC_DATALAT_7_CLK SNTCFG_DLAT(5) /*!< data latency of first burst access is 7 EXMC_CLK */ +#define EXMC_DATALAT_8_CLK SNTCFG_DLAT(6) /*!< data latency of first burst access is 8 EXMC_CLK */ +#define EXMC_DATALAT_9_CLK SNTCFG_DLAT(7) /*!< data latency of first burst access is 9 EXMC_CLK */ +#define EXMC_DATALAT_10_CLK SNTCFG_DLAT(8) /*!< data latency of first burst access is 10 EXMC_CLK */ +#define EXMC_DATALAT_11_CLK SNTCFG_DLAT(9) /*!< data latency of first burst access is 11 EXMC_CLK */ +#define EXMC_DATALAT_12_CLK SNTCFG_DLAT(10) /*!< data latency of first burst access is 12 EXMC_CLK */ +#define EXMC_DATALAT_13_CLK SNTCFG_DLAT(11) /*!< data latency of first burst access is 13 EXMC_CLK */ +#define EXMC_DATALAT_14_CLK SNTCFG_DLAT(12) /*!< data latency of first burst access is 14 EXMC_CLK */ +#define EXMC_DATALAT_15_CLK SNTCFG_DLAT(13) /*!< data latency of first burst access is 15 EXMC_CLK */ +#define EXMC_DATALAT_16_CLK SNTCFG_DLAT(14) /*!< data latency of first burst access is 16 EXMC_CLK */ +#define EXMC_DATALAT_17_CLK SNTCFG_DLAT(15) /*!< data latency of first burst access is 17 EXMC_CLK */ + +/* synchronous clock divide ratio */ +#define SNTCFG_CKDIV(regval) (BITS(20,23) & ((uint32_t)(regval) << 20)) +#define EXMC_SYN_CLOCK_RATIO_DISABLE SNTCFG_CKDIV(0) /*!< EXMC_CLK disable */ +#define EXMC_SYN_CLOCK_RATIO_2_CLK SNTCFG_CKDIV(1) /*!< frequency EXMC_CLK = HCLK/2 */ +#define EXMC_SYN_CLOCK_RATIO_3_CLK SNTCFG_CKDIV(2) /*!< frequency EXMC_CLK = HCLK/3 */ +#define EXMC_SYN_CLOCK_RATIO_4_CLK SNTCFG_CKDIV(3) /*!< frequency EXMC_CLK = HCLK/4 */ +#define EXMC_SYN_CLOCK_RATIO_5_CLK SNTCFG_CKDIV(4) /*!< frequency EXMC_CLK = HCLK/5 */ +#define EXMC_SYN_CLOCK_RATIO_6_CLK SNTCFG_CKDIV(5) /*!< frequency EXMC_CLK = HCLK/6 */ +#define EXMC_SYN_CLOCK_RATIO_7_CLK SNTCFG_CKDIV(6) /*!< frequency EXMC_CLK = HCLK/7 */ +#define EXMC_SYN_CLOCK_RATIO_8_CLK SNTCFG_CKDIV(7) /*!< frequency EXMC_CLK = HCLK/8 */ +#define EXMC_SYN_CLOCK_RATIO_9_CLK SNTCFG_CKDIV(8) /*!< frequency EXMC_CLK = HCLK/9 */ +#define EXMC_SYN_CLOCK_RATIO_10_CLK SNTCFG_CKDIV(9) /*!< frequency EXMC_CLK = HCLK/10 */ +#define EXMC_SYN_CLOCK_RATIO_11_CLK SNTCFG_CKDIV(10) /*!< frequency EXMC_CLK = HCLK/11 */ +#define EXMC_SYN_CLOCK_RATIO_12_CLK SNTCFG_CKDIV(11) /*!< frequency EXMC_CLK = HCLK/12 */ +#define EXMC_SYN_CLOCK_RATIO_13_CLK SNTCFG_CKDIV(12) /*!< frequency EXMC_CLK = HCLK/13 */ +#define EXMC_SYN_CLOCK_RATIO_14_CLK SNTCFG_CKDIV(13) /*!< frequency EXMC_CLK = HCLK/14 */ +#define EXMC_SYN_CLOCK_RATIO_15_CLK SNTCFG_CKDIV(14) /*!< frequency EXMC_CLK = HCLK/15 */ +#define EXMC_SYN_CLOCK_RATIO_16_CLK SNTCFG_CKDIV(15) /*!< frequency EXMC_CLK = HCLK/16 */ + +/* ECC size */ +#define NPCTL_ECCSZ(regval) (BITS(17,19) & ((uint32_t)(regval) << 17)) +#define EXMC_ECC_SIZE_256BYTES NPCTL_ECCSZ(0) /* ECC size is 256 bytes */ +#define EXMC_ECC_SIZE_512BYTES NPCTL_ECCSZ(1) /* ECC size is 512 bytes */ +#define EXMC_ECC_SIZE_1024BYTES NPCTL_ECCSZ(2) /* ECC size is 1024 bytes */ +#define EXMC_ECC_SIZE_2048BYTES NPCTL_ECCSZ(3) /* ECC size is 2048 bytes */ +#define EXMC_ECC_SIZE_4096BYTES NPCTL_ECCSZ(4) /* ECC size is 4096 bytes */ +#define EXMC_ECC_SIZE_8192BYTES NPCTL_ECCSZ(5) /* ECC size is 8192 bytes */ + +/* ALE to RE delay */ +#define NPCTL_ATR(regval) (BITS(13,16) & ((uint32_t)(regval) << 13)) +#define EXMC_ALE_RE_DELAY_1_HCLK NPCTL_ATR(0) /* ALE to RE delay = 1*HCLK */ +#define EXMC_ALE_RE_DELAY_2_HCLK NPCTL_ATR(1) /* ALE to RE delay = 2*HCLK */ +#define EXMC_ALE_RE_DELAY_3_HCLK NPCTL_ATR(2) /* ALE to RE delay = 3*HCLK */ +#define EXMC_ALE_RE_DELAY_4_HCLK NPCTL_ATR(3) /* ALE to RE delay = 4*HCLK */ +#define EXMC_ALE_RE_DELAY_5_HCLK NPCTL_ATR(4) /* ALE to RE delay = 5*HCLK */ +#define EXMC_ALE_RE_DELAY_6_HCLK NPCTL_ATR(5) /* ALE to RE delay = 6*HCLK */ +#define EXMC_ALE_RE_DELAY_7_HCLK NPCTL_ATR(6) /* ALE to RE delay = 7*HCLK */ +#define EXMC_ALE_RE_DELAY_8_HCLK NPCTL_ATR(7) /* ALE to RE delay = 8*HCLK */ +#define EXMC_ALE_RE_DELAY_9_HCLK NPCTL_ATR(8) /* ALE to RE delay = 9*HCLK */ +#define EXMC_ALE_RE_DELAY_10_HCLK NPCTL_ATR(9) /* ALE to RE delay = 10*HCLK */ +#define EXMC_ALE_RE_DELAY_11_HCLK NPCTL_ATR(10) /* ALE to RE delay = 11*HCLK */ +#define EXMC_ALE_RE_DELAY_12_HCLK NPCTL_ATR(11) /* ALE to RE delay = 12*HCLK */ +#define EXMC_ALE_RE_DELAY_13_HCLK NPCTL_ATR(12) /* ALE to RE delay = 13*HCLK */ +#define EXMC_ALE_RE_DELAY_14_HCLK NPCTL_ATR(13) /* ALE to RE delay = 14*HCLK */ +#define EXMC_ALE_RE_DELAY_15_HCLK NPCTL_ATR(14) /* ALE to RE delay = 15*HCLK */ +#define EXMC_ALE_RE_DELAY_16_HCLK NPCTL_ATR(15) /* ALE to RE delay = 16*HCLK */ + +/* CLE to RE delay */ +#define NPCTL_CTR(regval) (BITS(9,12) & ((uint32_t)(regval) << 9)) +#define EXMC_CLE_RE_DELAY_1_HCLK NPCTL_CTR(0) /* CLE to RE delay = 1*HCLK */ +#define EXMC_CLE_RE_DELAY_2_HCLK NPCTL_CTR(1) /* CLE to RE delay = 2*HCLK */ +#define EXMC_CLE_RE_DELAY_3_HCLK NPCTL_CTR(2) /* CLE to RE delay = 3*HCLK */ +#define EXMC_CLE_RE_DELAY_4_HCLK NPCTL_CTR(3) /* CLE to RE delay = 4*HCLK */ +#define EXMC_CLE_RE_DELAY_5_HCLK NPCTL_CTR(4) /* CLE to RE delay = 5*HCLK */ +#define EXMC_CLE_RE_DELAY_6_HCLK NPCTL_CTR(5) /* CLE to RE delay = 6*HCLK */ +#define EXMC_CLE_RE_DELAY_7_HCLK NPCTL_CTR(6) /* CLE to RE delay = 7*HCLK */ +#define EXMC_CLE_RE_DELAY_8_HCLK NPCTL_CTR(7) /* CLE to RE delay = 8*HCLK */ +#define EXMC_CLE_RE_DELAY_9_HCLK NPCTL_CTR(8) /* CLE to RE delay = 9*HCLK */ +#define EXMC_CLE_RE_DELAY_10_HCLK NPCTL_CTR(9) /* CLE to RE delay = 10*HCLK */ +#define EXMC_CLE_RE_DELAY_11_HCLK NPCTL_CTR(10) /* CLE to RE delay = 11*HCLK */ +#define EXMC_CLE_RE_DELAY_12_HCLK NPCTL_CTR(11) /* CLE to RE delay = 12*HCLK */ +#define EXMC_CLE_RE_DELAY_13_HCLK NPCTL_CTR(12) /* CLE to RE delay = 13*HCLK */ +#define EXMC_CLE_RE_DELAY_14_HCLK NPCTL_CTR(13) /* CLE to RE delay = 14*HCLK */ +#define EXMC_CLE_RE_DELAY_15_HCLK NPCTL_CTR(14) /* CLE to RE delay = 15*HCLK */ +#define EXMC_CLE_RE_DELAY_16_HCLK NPCTL_CTR(15) /* CLE to RE delay = 16*HCLK */ + +/* NAND bank memory data bus width */ +#define NPCTL_NDW(regval) (BITS(4,5) & ((uint32_t)(regval) << 4)) +#define EXMC_NAND_DATABUS_WIDTH_8B NPCTL_NDW(0) /*!< NAND data width is 8 bits */ +#define EXMC_NAND_DATABUS_WIDTH_16B NPCTL_NDW(1) /*!< NAND data width is 16 bits */ + +/* EXMC NOR/SRAM bank region definition */ +#define EXMC_BANK0_NORSRAM_REGION0 ((uint32_t)0x00000000U) /*!< bank0 NOR/SRAM region0 */ +#define EXMC_BANK0_NORSRAM_REGION1 ((uint32_t)0x00000001U) /*!< bank0 NOR/SRAM region1 */ +#define EXMC_BANK0_NORSRAM_REGION2 ((uint32_t)0x00000002U) /*!< bank0 NOR/SRAM region2 */ +#define EXMC_BANK0_NORSRAM_REGION3 ((uint32_t)0x00000003U) /*!< bank0 NOR/SRAM region3 */ + +/* EXMC NOR/SRAM write mode */ +#define EXMC_ASYN_WRITE ((uint32_t)0x00000000U) /*!< asynchronous write mode */ +#define EXMC_SYN_WRITE EXMC_SNCTL_SYNCWR /*!< synchronous write mode */ + +/* EXMC NWAIT signal configuration */ +#define EXMC_NWAIT_CONFIG_BEFORE ((uint32_t)0x00000000U) /*!< NWAIT signal is active one data cycle before wait state */ +#define EXMC_NWAIT_CONFIG_DURING EXMC_SNCTL_NRWTCFG /*!< NWAIT signal is active during wait state */ + +/* EXMC NWAIT signal polarity configuration */ +#define EXMC_NWAIT_POLARITY_LOW ((uint32_t)0x00000000U) /*!< low level is active of NWAIT */ +#define EXMC_NWAIT_POLARITY_HIGH EXMC_SNCTL_NRWTPOL /*!< high level is active of NWAIT */ + +/* EXMC NAND/PC card bank definition */ +#define EXMC_BANK1_NAND ((uint32_t)0x00000001U) /*!< bank1 NAND flash */ +#define EXMC_BANK2_NAND ((uint32_t)0x00000002U) /*!< bank2 NAND flash */ +#define EXMC_BANK3_PCCARD ((uint32_t)0x00000003U) /*!< bank3 PC card */ + +/* EXMC flag bits */ +#define EXMC_NAND_PCCARD_FLAG_RISE EXMC_NPINTEN_INTRS /*!< interrupt rising edge status */ +#define EXMC_NAND_PCCARD_FLAG_LEVEL EXMC_NPINTEN_INTHS /*!< interrupt high-level status */ +#define EXMC_NAND_PCCARD_FLAG_FALL EXMC_NPINTEN_INTFS /*!< interrupt falling edge status */ +#define EXMC_NAND_PCCARD_FLAG_FIFOE EXMC_NPINTEN_FFEPT /*!< FIFO empty flag */ + +/* EXMC interrupt flag bits */ +#define EXMC_NAND_PCCARD_INT_FLAG_RISE EXMC_NPINTEN_INTREN /*!< rising edge interrupt and corresponding flag */ +#define EXMC_NAND_PCCARD_INT_FLAG_LEVEL EXMC_NPINTEN_INTHEN /*!< high-level interrupt and corresponding flag */ +#define EXMC_NAND_PCCARD_INT_FLAG_FALL EXMC_NPINTEN_INTFEN /*!< falling edge interrupt and corresponding flag */ + +/* function declarations */ +/* NOR/SRAM */ +/* deinitialize EXMC NOR/SRAM region */ +void exmc_norsram_deinit(uint32_t exmc_norsram_region); +/* initialize exmc_norsram_parameter_struct with the default values */ +void exmc_norsram_struct_para_init(exmc_norsram_parameter_struct* exmc_norsram_init_struct); +/* initialize EXMC NOR/SRAM region */ +void exmc_norsram_init(exmc_norsram_parameter_struct* exmc_norsram_init_struct); +/* enable EXMC NOR/SRAM region */ +void exmc_norsram_enable(uint32_t exmc_norsram_region); +/* disable EXMC NOR/SRAM region */ +void exmc_norsram_disable(uint32_t exmc_norsram_region); +/* configure CRAM page size */ +void exmc_norsram_page_size_config(uint32_t exmc_norsram_region, uint32_t page_size); + +/* NAND */ +/* deinitialize EXMC NAND bank */ +void exmc_nand_deinit(uint32_t exmc_nand_bank); +/* initialize exmc_nand_parameter_struct with the default values */ +void exmc_nand_struct_para_init(exmc_nand_parameter_struct* exmc_nand_init_struct); +/* initialize EXMC NAND bank */ +void exmc_nand_init(exmc_nand_parameter_struct* exmc_nand_init_struct); +/* enable EXMC NAND bank */ +void exmc_nand_enable(uint32_t exmc_nand_bank); +/* disable EXMC NAND bank */ +void exmc_nand_disable(uint32_t exmc_nand_bank); +/* enable or disable the EXMC NAND ECC function */ +void exmc_nand_ecc_config(uint32_t exmc_nand_bank, ControlStatus newvalue); +/* get the EXMC ECC value */ +uint32_t exmc_ecc_get(uint32_t exmc_nand_bank); + +/* PC card */ +/* deinitialize EXMC PC card bank */ +void exmc_pccard_deinit(void); +/* initialize exmc_pccard_parameter_struct parameter with the default values */ +void exmc_pccard_struct_para_init(exmc_pccard_parameter_struct* exmc_pccard_init_struct); +/* initialize EXMC PC card bank */ +void exmc_pccard_init(exmc_pccard_parameter_struct* exmc_pccard_init_struct); +/* enable EXMC PC card bank */ +void exmc_pccard_enable(void); +/* disable EXMC PC card bank */ +void exmc_pccard_disable(void); + +/* interrupt & flag functions */ +/* enable EXMC interrupt */ +void exmc_interrupt_enable(uint32_t exmc_bank,uint32_t interrupt); +/* disable EXMC interrupt */ +void exmc_interrupt_disable(uint32_t exmc_bank,uint32_t interrupt); +/* get EXMC flag status */ +FlagStatus exmc_flag_get(uint32_t exmc_bank,uint32_t flag); +/* clear EXMC flag status */ +void exmc_flag_clear(uint32_t exmc_bank,uint32_t flag); +/* get EXMC interrupt flag */ +FlagStatus exmc_interrupt_flag_get(uint32_t exmc_bank,uint32_t interrupt); +/* clear EXMC interrupt flag */ +void exmc_interrupt_flag_clear(uint32_t exmc_bank,uint32_t interrupt); + +#endif /* GD32E50X_EXMC_H */ diff --git a/GD32E508_DCU_APP/comm/esc_exmc_bus.cpp b/GD32E508_DCU_APP/comm/esc_exmc_bus.cpp index d3c0b94..1370280 100644 --- a/GD32E508_DCU_APP/comm/esc_exmc_bus.cpp +++ b/GD32E508_DCU_APP/comm/esc_exmc_bus.cpp @@ -1,6 +1,10 @@ /** * @file esc_exmc_bus.cpp * @brief LAN9252 ESC 的 EXMC 16-bit 并行总线驱动实现。 + * + * 使用 GigaDevice gd32e50x_exmc 标准库。 + * 注意:DFP 只把 FMC 注册为 CMSIS 组件,EXMC 未进组件列表; + * 源文件放在 RTE/Device/... 并由 cproject group device_exmc 本地编译。 */ #include "comm/esc_exmc_bus.h" #include "sys/config.h" @@ -8,22 +12,17 @@ extern "C" { #include "gd32e50x_rcu.h" #include "gd32e50x_gpio.h" -#ifdef EXMC_NORSRAM_REGION1 -#include "gd32e50x_exmc.h" -#endif +#include "gd32e50x_exmc.h" /* RTE/Device/GD32E508VE 或 -I 同目录 */ } -/* 内存映射访问宏:基址 + 偏移(按 16-bit 半字对齐,LAN9252 HBI 地址左移 1 位)。 - * 使用 volatile uint16_t* 转换避免依赖 CMSIS 的 __IO 宏。 */ +/* 内存映射访问宏:基址 + 偏移(按 16-bit 半字对齐,LAN9252 HBI 地址左移 1 位)。 */ #define ESC_EXMC_REG16(offset) (*(volatile uint16_t*)(CONFIG_ESC_EXMC_BASE_ADDR + (offset))) void EscExmcBus::init() { - /* 使能 GPIO 与 EXMC 时钟 */ rcu_periph_clock_enable(RCU_GPIOD); rcu_periph_clock_enable(RCU_GPIOE); rcu_periph_clock_enable(RCU_AF); -#ifdef EXMC_NORSRAM_REGION1 rcu_periph_clock_enable(RCU_EXMC); /* 配置 16-bit 数据线 D0-D15 为 AF 推挽 */ @@ -50,31 +49,47 @@ void EscExmcBus::init() gpio_init(CONFIG_EXMC_NWE_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_NWE_PIN); gpio_init(CONFIG_EXMC_NE1_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_NE1_PIN); - /* EXMC NOR/SRAM Region1 时序配置(16-bit,非复用,异步) */ + /* + * EXMC NOR/SRAM Region1:16-bit、非复用、异步写。 + * 时序(AHB=180MHz,1 HCLK ≈ 5.56ns): + * LAN9252 HBI 最小读/写周期约 100ns → data_setup ≥ 18 HCLK。 + * asyn_* 字段单位为 HCLK 计数(库文档范围 address_setup 1..16,data_setup 2..256)。 + */ exmc_norsram_parameter_struct norsram_init; + exmc_norsram_timing_parameter_struct rw_timing; + exmc_norsram_timing_parameter_struct wr_timing; + + norsram_init.read_write_timing = &rw_timing; + norsram_init.write_timing = &wr_timing; exmc_norsram_struct_para_init(&norsram_init); - norsram_init.norsram_region = CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1; - norsram_init.write_mode = EXMC_ASYN_WRITE; - norsram_init.read_mode = EXMC_ASYN_READ; - norsram_init.nwait_mode = EXMC_NWAIT_DISABLE; - norsram_init.asyn_wait = DISABLE; - norsram_init.nwait_polarity = EXMC_NWAIT_POLARITY_LOW; - norsram_init.databus_width = EXMC_NOR_DATABUS_WIDTH_16B; - /* TODO(spec): 时序:按 LAN9252 HBI 要求配置 read/write wait state - * LAN9252 HBI 最小 read/write cycle = 100ns。 - * AHB=180MHz(RCU_MODIFY_UP 强制 /1),1 HCLK = 5.6ns。 - * 6 HCLK = 33ns → 不满足,需 ≥ 18 HCLK 才能达到 100ns。 - * 当前 ESC 未启用(selfTest FAIL),EXMC 库未纳入编译,此参数仅占位。 - * 启用 ESC 时需安装 EXMC 库并实测调整 HCLK 数。 */ - norsram_init.read_latency = EXMC_DEN_ALE_TR_6_HCLK; // 占位,启用 ESC 时需调整为 ≥18 HCLK - norsram_init.write_latency = EXMC_DEN_ALE_TR_6_HCLK; // 占位,启用 ESC 时需调整为 ≥18 HCLK + + norsram_init.norsram_region = CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1; + norsram_init.write_mode = EXMC_ASYN_WRITE; + norsram_init.extended_mode = DISABLE; + norsram_init.asyn_wait = DISABLE; + norsram_init.nwait_signal = DISABLE; + norsram_init.memory_write = ENABLE; + norsram_init.nwait_config = EXMC_NWAIT_CONFIG_BEFORE; + norsram_init.wrap_burst_mode = DISABLE; + norsram_init.nwait_polarity = EXMC_NWAIT_POLARITY_LOW; + norsram_init.burst_mode = DISABLE; + norsram_init.databus_width = EXMC_NOR_DATABUS_WIDTH_16B; + norsram_init.memory_type = EXMC_MEMORY_TYPE_SRAM; + norsram_init.address_data_mux = DISABLE; + + /* 读/写共用时序(非 extended mode) */ + rw_timing.asyn_access_mode = EXMC_ACCESS_MODE_A; + rw_timing.syn_data_latency = EXMC_DATALAT_2_CLK; + rw_timing.syn_clk_division = EXMC_SYN_CLOCK_RATIO_DISABLE; + rw_timing.bus_latency = 1U; + rw_timing.asyn_address_setuptime = 2U; /* ~11ns */ + rw_timing.asyn_address_holdtime = 2U; /* min 2 */ + rw_timing.asyn_data_setuptime = 20U; /* ~111ns ≥ 100ns HBI */ + + wr_timing = rw_timing; + exmc_norsram_init(&norsram_init); exmc_norsram_enable(CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1); -#else - /* EXMC 外设库未安装(gd32e50x_exmc.h 缺失):EtherCAT ESC EXMC 总线初始化为空, - * 运行期 selfTest 必失败,EtherCAT 自动禁用。启用 ESC 需先安装 EXMC 组件。 */ -#warning "EXMC peripheral library not included; EtherCAT ESC EXMC bus init is a no-op" -#endif } __attribute__((section(".ramfunc"), noinline)) diff --git a/GD32E508_DCU_APP/comm/ethercat_slave.h b/GD32E508_DCU_APP/comm/ethercat_slave.h index 0113faf..aab9aa0 100644 --- a/GD32E508_DCU_APP/comm/ethercat_slave.h +++ b/GD32E508_DCU_APP/comm/ethercat_slave.h @@ -14,9 +14,7 @@ #include #include - -/** @brief 默认 PDO 大小(可按需调整)。 */ -constexpr size_t CONFIG_ETHERCAT_PDO_SIZE = 32U; +#include "sys/config.h" /* CONFIG_ETHERCAT_PDO_SIZE */ /** @brief EtherCAT AL 状态码(EtherCAT 协议定义,ESC AL Status 寄存器低 4 位)。 */ constexpr uint8_t AL_STATE_SAFEOP = 0x04U; /**< SAFEOP 状态(SYNC0 可触发下限)。 */ diff --git a/GD32E508_DCU_APP/device/CANFD.cpp b/GD32E508_DCU_APP/device/CANFD.cpp index 9ccec9a..1c08c5c 100644 --- a/GD32E508_DCU_APP/device/CANFD.cpp +++ b/GD32E508_DCU_APP/device/CANFD.cpp @@ -57,6 +57,7 @@ static const char *canLastErrorName(uint32_t errReg) return names[(errReg >> 4U) & 0x07U]; } +#if (CONFIG_CAN_STARTUP_BUS_TEST_ENABLE != 0) struct CanSelfTestResult { bool txReq; uint8_t mailbox; @@ -113,6 +114,7 @@ static CanSelfTestResult runSelfTestFrame(uint8_t channelIndex, uint32_t extId, result.busOff = (result.err & CAN_ERR_BOERR) != 0U; return result; } +#endif /* CONFIG_CAN_STARTUP_BUS_TEST_ENABLE */ /** * @brief 对 CAN 外设执行完整硬件级复位(SWRST + RCU 电源周期)。 @@ -309,6 +311,7 @@ bool CanFd::initFdMode(uint32_t canPeriph) #endif } +#if (CONFIG_CAN_STARTUP_DIAG_VERBOSE != 0) /** @brief 打印 SYS/APB1 时钟与目标波特率。 */ void CanFd::printClockInfo() { @@ -316,6 +319,7 @@ void CanFd::printClockInfo() static_cast(SystemCoreClock), static_cast(rcu_clock_freq_get(CK_APB1))); } +#endif /** @brief 单通道硬件初始化(hardResetCan+重试+退出init mode+中断使能+RX队列清零)。 */ void CanFd::initChannelHardware(uint8_t i) @@ -428,6 +432,7 @@ void CanFd::configureAllFilters() static_cast(CAN_FW(CAN0))); } +#if (CONFIG_CAN_STARTUP_DIAG_VERBOSE != 0) /** @brief 打印初始化结果汇总(BT/DBT/滤波器状态)。 */ void CanFd::printInitResult() { @@ -481,12 +486,13 @@ void CanFd::printErrorState() (err & CAN_ERR_WERR) ? 1U : 0U); /* EWARN bit0 */ } } +#endif /* CONFIG_CAN_STARTUP_DIAG_VERBOSE */ -/** @brief 物理总线自测(受 CONFIG_CAN_STARTUP_BUS_TEST_ENABLE 控制)。 */ +#if (CONFIG_CAN_STARTUP_BUS_TEST_ENABLE != 0) +/** @brief 物理总线自测。 */ void CanFd::runSelfTest() { /* ===== Self-test (NORMAL mode, 发到物理总线测 ACK) ===== */ -#if (CONFIG_CAN_STARTUP_BUS_TEST_ENABLE != 0) printf("=== CAN SELF-TEST (normal mode) ===\r\n"); for (size_t i = 0; i < kChannelCount; ++i) { if (!kChannelEnabled[i]) { @@ -516,16 +522,15 @@ void CanFd::runSelfTest() static_cast(fd.tec)); #endif } -#else - printf("=== CAN SELF-TEST SKIPPED (CONFIG_CAN_STARTUP_BUS_TEST_ENABLE=0) ===\r\n"); -#endif } -/** @brief 内部回环测试(受 CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE 控制)。 */ +#endif /* CONFIG_CAN_STARTUP_BUS_TEST_ENABLE */ + +#if (CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE != 0) +/** @brief 内部回环测试。 */ void CanFd::runLoopbackTest() { /* ===== Loopback 回环测试 (内部 TX→RX,不走物理总线) ===== */ -#if (CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE != 0) printf("=== CAN LOOPBACK (internal) ===\r\n"); for (size_t i = 0; i < kChannelCount; ++i) { const auto &ch = kChannels[i]; @@ -663,11 +668,10 @@ void CanFd::runLoopbackTest() static_cast(CAN_CTL(ch.canPeriph))); } printf("========================\r\n"); -#else - printf("=== CAN LOOPBACK SKIPPED (CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE=0) ===\r\n"); -#endif } +#endif /* CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE */ +#if (CONFIG_CAN_STARTUP_DIAG_VERBOSE != 0) /** @brief 全寄存器快照转储(CTL/STAT/BT/DBT/ERR/INTEN/FCTL/FW/RFL0)。 */ void CanFd::dumpRegisters() { @@ -713,6 +717,7 @@ void CanFd::dumpRegisters() } printf("===============================\r\n"); } +#endif /* CONFIG_CAN_STARTUP_DIAG_VERBOSE */ /** * @brief 初始化三路 CANFD 控制器、过滤器、中断与软件队列。 @@ -733,7 +738,9 @@ void CanFd::init() s_feedbackSeq = 0U; initGpio(); +#if (CONFIG_CAN_STARTUP_DIAG_VERBOSE != 0) printClockInfo(); +#endif for (uint8_t i = 0; i < kChannelCount; ++i) { initChannelHardware(i); @@ -741,11 +748,17 @@ void CanFd::init() s_nextConsumeChannel = 0U; configureAllFilters(); +#if (CONFIG_CAN_STARTUP_DIAG_VERBOSE != 0) printInitResult(); printErrorState(); - runSelfTest(); - runLoopbackTest(); dumpRegisters(); +#endif +#if (CONFIG_CAN_STARTUP_BUS_TEST_ENABLE != 0) + runSelfTest(); +#endif +#if (CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE != 0) + runLoopbackTest(); +#endif } /** @@ -843,7 +856,7 @@ bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length, uint8_t * @param[in] channelIndex 目标 CAN 通道号(0/1/2)。 * @param[in] ext_id 扩展帧 ID(29 位)。 * @param[in] data 数据负载指针。 - * @param[in] dlc 数据长度(1~64)。 + * @param[in] dlc 数据长度(0~64;0 表示无数据负载/远程类空帧)。 * @param[in] periodic 是否为周期性发送;true 时仅允许一个邮箱保持自动重发, * 不主动 abort 旧帧,且跳过 TEC 保护以匹配诊断行为。 * @retval CanTxResult kOk 表示发送请求已进入硬件邮箱;其它值表示具体失败原因。 @@ -853,8 +866,11 @@ bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length, uint8_t CanFd::CanTxResult CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id, const uint8_t *data, uint8_t dlc, bool periodic) { - if (channelIndex >= kChannelCount || !kChannelEnabled[channelIndex] - || data == nullptr || dlc == 0U || dlc > 64U) { + /* DLC=0 合法(仅 ID);DLC>0 时 data 必填 */ + if (channelIndex >= kChannelCount || !kChannelEnabled[channelIndex] || dlc > 64U) { + return CanTxResult::kErrInvalid; + } + if (dlc > 0U && data == nullptr) { return CanTxResult::kErrInvalid; } @@ -919,8 +935,10 @@ CanFd::CanTxResult CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id, #endif tx_msg.fd_esi = CAN_ESI_DOMINANT; #endif - for (uint8_t i = 0U; i < dlc; ++i) { - tx_msg.tx_data[i] = data[i]; + if (data != nullptr && dlc > 0U) { + for (uint8_t i = 0U; i < dlc; ++i) { + tx_msg.tx_data[i] = data[i]; + } } uint8_t mailbox = can_message_transmit(canPeriph, &tx_msg); @@ -965,27 +983,6 @@ CanFd::CanTxResult CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id, return CanTxResult::kErrMailbox; } -/** - * @brief 检查指定 CAN 通道当前是否处于健康可发送状态。 - * @param[in] channelIndex 通道索引 0/1/2。 - * @retval bool true=健康(未 BUS_OFF 且 TEC<=96),false=应避免发送。 - * @note 主要用于 taskCanTxSend 等周期性测试帧发送前判断,防止病态通道被 - * 持续重发的测试帧反复推回 BUS_OFF,干扰硬件 auto_bus_off_recovery。 - */ -bool CanFd::isChannelHealthy(uint8_t channelIndex) -{ - if (channelIndex >= kChannelCount || !kChannelEnabled[channelIndex]) { - return false; - } - const uint32_t canPeriph = kChannels[channelIndex].canPeriph; - const uint32_t errReg = CAN_ERR(canPeriph); - if (errReg & CAN_ERR_BOERR) { - return false; - } - const uint32_t tec = (errReg >> 16) & 0xFFU; - return tec <= 96U; -} - /** * @brief 获取 CANFD 诊断统计信息只读指针。 * @param[in] 无。 diff --git a/GD32E508_DCU_APP/device/canfd.h b/GD32E508_DCU_APP/device/canfd.h index 4835801..58776a3 100644 --- a/GD32E508_DCU_APP/device/canfd.h +++ b/GD32E508_DCU_APP/device/canfd.h @@ -3,14 +3,16 @@ #include -/** @brief 每通道反馈快照中保存的最近一帧最大数据长度,受 SPI 应答帧总长度约束。 */ +#include "sys/config.h" + +/** @brief 每通道反馈快照中保存的最近一帧最大数据长度(本地诊断用)。 */ #define CANFD_FEEDBACK_LAST_DATA_MAX 32U -/** @brief 每通道不重复 ID 快照表最大条目数。 */ -#define CANFD_ID_SNAPSHOT_MAX 16U +/** @brief 每通道不重复 ID 快照表最大条目数(CONFIG_CANFD_ID_SNAPSHOT_MAX)。 */ +#define CANFD_ID_SNAPSHOT_MAX CONFIG_CANFD_ID_SNAPSHOT_MAX -/** @brief 快照条目中最大数据字节数(等于 CANFD DLC 上限)。 */ -#define CANFD_ID_SNAPSHOT_DATA_MAX 64U +/** @brief 快照条目中最大数据字节数(CONFIG_CANFD_ID_SNAPSHOT_DATA_MAX)。 */ +#define CANFD_ID_SNAPSHOT_DATA_MAX CONFIG_CANFD_ID_SNAPSHOT_DATA_MAX /** * @brief CANFD 通信模块。 @@ -51,7 +53,6 @@ public: uint32_t rx_drop_count; uint32_t rx_fifo_full_count; uint32_t rx_fifo_overfull_count; - uint32_t tx_drop_count; uint32_t tx_abort_count; uint32_t last_rx_id; uint32_t last_tx_id; @@ -138,13 +139,6 @@ public: const uint8_t *data, uint8_t dlc, bool periodic = false); static const Diag *diag(); - /** - * @brief 检查指定 CAN 通道当前是否处于健康可发送状态。 - * @retval true 未处于 BUS_OFF 且 TEC 未超过预警阈值。 - * @retval false 通道处于 BUS_OFF 或 TEC 过高,应避免再向其发送测试帧。 - */ - static bool isChannelHealthy(uint8_t channelIndex); - /** * @brief 刷新三路 1Hz 反馈快照。 * @note 通常由任务调度器在 1 秒节拍调用,将各通道实时计数与最近一帧信息抓拍到 s_feedback。 diff --git a/GD32E508_DCU_APP/device/spi/app_spi_cmd_can_bridge.cpp b/GD32E508_DCU_APP/device/spi/app_spi_cmd_can_bridge.cpp index eeeaef6..31aa1c0 100644 --- a/GD32E508_DCU_APP/device/spi/app_spi_cmd_can_bridge.cpp +++ b/GD32E508_DCU_APP/device/spi/app_spi_cmd_can_bridge.cpp @@ -45,7 +45,8 @@ public: void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) override { - if (ctx.payload_len < 6U) { + /* 最短 5 字节:ch(1)+id(4);DLC=0(如 CANFD_BOOT)合法,勿再要求 ≥6 */ + if (ctx.payload_len < 5U) { #if (CONFIG_SPI_APP_LOG_ENABLE != 0) char log_msg[AppSpiCommand::kLogBufSize] = {}; (void)snprintf(log_msg, sizeof(log_msg), @@ -64,8 +65,8 @@ public: | (static_cast(ctx.payload[2]) << 16) | (static_cast(ctx.payload[3]) << 8) | static_cast(ctx.payload[4]); - const uint8_t *can_data = &ctx.payload[5]; const uint8_t can_dlc = static_cast(ctx.payload_len - 5U); + const uint8_t *can_data = (can_dlc > 0U) ? &ctx.payload[5] : nullptr; if (can_ch >= 3U) { postCanSendLog(bus, "ERROR(channel)", can_ch, can_id, can_data, can_dlc); diff --git a/GD32E508_DCU_APP/device/spi/app_spi_cmd_can_feedback.cpp b/GD32E508_DCU_APP/device/spi/app_spi_cmd_can_feedback.cpp index 9998db8..21eb967 100644 --- a/GD32E508_DCU_APP/device/spi/app_spi_cmd_can_feedback.cpp +++ b/GD32E508_DCU_APP/device/spi/app_spi_cmd_can_feedback.cpp @@ -8,7 +8,7 @@ * 请求 payload: * [0] channel 0/1/2 * [1] page 页号(0 = 条目 0~7,1 = 条目 8~15) - * 应答 payload(变长,最大 kTxBufSize - overhead = 123 字节): + * 应答 payload(变长;上限 = AppSpiCommand::kTxBufSize - V2_OVERHEAD = 256-20 = 236): * [0] channel * [1] page * [2] total_count 该通道当前条目总数 @@ -39,6 +39,7 @@ #include "device/canfd.h" #include "sys/config.h" #include "sys/ota_boot_request.h" +#include "spi_link_v2.h" #include #include @@ -46,7 +47,9 @@ * 全局作用域的 canFeedbackPushStep() 共同复用) ---- */ static constexpr uint8_t kFeedbackChannelCount = 3U; -static constexpr uint16_t kFeedbackPayloadLen = 123U; /* kTxBufSize=128 - overhead=5 */ +/* 业务 payload 上限:APP TX 缓冲 256 - v2 帧开销 20 = 236(与 ESP32 kFeedbackPayloadLen 一致) */ +static constexpr uint16_t kFeedbackPayloadLen = + static_cast(AppSpiCommand::kTxBufSize - V2_OVERHEAD); static constexpr uint8_t kPageEntries = 8U; static constexpr uint16_t kFixedHeaderLen = 28U; /* ch(1)+page(1)+total(1)+cnt(1)+mode(1)+seq(4)+rx(4)+tx(4)+drop(4)+err(4)+tec(1)+rec(1)+bus_state(1) */ diff --git a/GD32E508_DCU_APP/device/spi/app_spi_cmd_id_snapshot.cpp b/GD32E508_DCU_APP/device/spi/app_spi_cmd_id_snapshot.cpp index 0fd59f7..8f023d0 100644 --- a/GD32E508_DCU_APP/device/spi/app_spi_cmd_id_snapshot.cpp +++ b/GD32E508_DCU_APP/device/spi/app_spi_cmd_id_snapshot.cpp @@ -8,13 +8,13 @@ * [0] channel 0/1/2 * [1] page 页号(0 = 条目 0~7,1 = 条目 8~15) * - * 应答 payload(变长,最大 kTxBufSize - 5 = 123 字节): + * 应答 payload(变长;上限 = AppSpiCommand::kTxBufSize - V2_OVERHEAD = 256-20 = 236): * [0] channel * [1] page * [2] total_count 该通道当前条目总数 * [3] entry_count 本页实际条目数 * [4..] 条目逐条拼接: - * id(4LE) + seq(4LE) + rx_count(4LE) + dlc(1) + flags(1) + data(dlc) + * id(4LE) + seq(4LE) + rx_count(4LE) + dlc(1) + flags(1) + rsv(2) + data(dlc) * 每条 = 16 + dlc 字节 */ @@ -22,13 +22,15 @@ #include "device/spi/app_spi_command.h" #include "device/spi/app_spi_command_defs.h" #include "device/canfd.h" +#include "spi_link_v2.h" #include #include namespace { constexpr uint8_t kPageEntries = 8U; -constexpr uint16_t kMaxPayloadLen = 123U; /* kTxBufSize=128 - overhead=5 */ +constexpr uint16_t kMaxPayloadLen = + static_cast(AppSpiCommand::kTxBufSize - V2_OVERHEAD); class SpiIdSnapshotHandler final : public ISpiCommandHandler { public: diff --git a/GD32E508_DCU_APP/device/spi/app_spi_command.cpp b/GD32E508_DCU_APP/device/spi/app_spi_command.cpp index 17050e5..e70b8bc 100644 --- a/GD32E508_DCU_APP/device/spi/app_spi_command.cpp +++ b/GD32E508_DCU_APP/device/spi/app_spi_command.cpp @@ -1,921 +1,851 @@ -/** - * @file app_spi_command.cpp - * @brief APP 运行期 SPI 链路与命令路由实现。 - * - * 本模块现在只负责: - * - SPI0 从机链路初始化(GPIO/SPI/DMA/EXTI) - * - CS、DMA、中断驱动的帧接收与发送 - * - 公共帧格式解析与应答构建 - * - 业务命令的注册与分发 - * - * 具体命令处理逻辑已经拆到独立的 app_spi_cmd_*.cpp, - * 通过 registerHandler() 注册进来,后续新增 SPI 业务时 - * 只需要新增命令处理器,不再继续膨胀本文件。 - * - * 握手架构(v2 DATA_READY,PB6<->ESP32 GPIO10 开漏单线): - * - PB6 低 = 有待发数据(m_txLen>0 或 m_ringPending),高 = 队列空。 - * - 命令处理在 onCsRelease() EXTI ISR 内同步完成(对齐 BOOT 的 isrProcessFrame), - * 完成后由 updateDataReady() 依据待发数据驱动 PB6,不再延迟拉高。 - * - GD32 可通过 requestEsp32Read() 设 m_ringPending 并由 updateDataReady() - * 拉低 PB6 主动按铃,请求 ESP32 来读。 - */ - -#include "device/spi/app_spi_command.h" -#include "device/spi/spi_command_handler.h" -#include "device/spi/app_spi_command_defs.h" -#include "spi_link_v2.h" -#include "sys/ota_boot_request.h" - -extern "C" { -#include "gd32e50x_exti.h" -} -#include "gd32e50x_gpio.h" -#include "gd32e50x_misc.h" -#include "gd32e50x_rcu.h" -#include "gd32e50x_dma.h" -#include "gd32e50x_spi.h" -#include "sys/config.h" -#include "sys/critical.h" -#include "sys/task_manager.h" -#include "sys/systick.h" -#include "main.h" -#include -#include - -/* ============================================================ - * 本地别名:保持当前实现文件内部风格不变 - * ============================================================ */ -static constexpr uint8_t kTypeBootReq = SpiDefs::kTypeBootReq; -static constexpr uint8_t kTypeOtaReq = SpiDefs::kTypeOtaReq; -static constexpr uint8_t kTypeOtaData = SpiDefs::kTypeOtaData; -static constexpr uint8_t kTypeOtaCancel = SpiDefs::kTypeOtaCancel; -static constexpr uint8_t kRspError = SpiDefs::kRspError; - -/* DMA 全双工配置(与 BOOT SpiOta 一致): - * DMA0_CH1: SPI0_RX DMA0_CH2: SPI0_TX - * RX 走 DMA 后禁用 RBNE 逐字节中断,避免 256B 事务 RX-OVR/SOF 丢失。 */ -#define SPI_TX_DMA_PERIPH DMA0 -#define SPI_TX_DMA_CH DMA_CH2 -#define SPI_RX_DMA_PERIPH DMA0 -#define SPI_RX_DMA_CH DMA_CH1 - -static void startSpiTxDma(const uint8_t *buf, uint16_t len) -{ - DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) &= ~DMA_CHXCTL_CHEN; - DMA_INTC(SPI_TX_DMA_PERIPH) |= DMA_FLAG_ADD(DMA_CHINTF_RESET_VALUE, SPI_TX_DMA_CH); - DMA_CHMADDR(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) = (uint32_t)buf; - DMA_CHCNT(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) = len; - DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) |= DMA_CHXCTL_CHEN; -} - -/** @brief 启动 DMA 接收 SPI0_DATA → m_rxBuf(容量 kRxBufSize)。 - * 每次 CS 空闲期预装,下一笔 CS 上升沿时由 stopSpiDmas() 读 CHCNT 反算实际长度。 */ -static void startSpiRxDma(uint8_t *rx_buf, uint16_t rx_cap) -{ - DMA_CHCTL(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) &= ~DMA_CHXCTL_CHEN; - DMA_INTC(SPI_RX_DMA_PERIPH) |= DMA_FLAG_ADD(DMA_CHINTF_RESET_VALUE, SPI_RX_DMA_CH); - DMA_CHMADDR(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) = (uint32_t)rx_buf; - DMA_CHCNT(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) = rx_cap; - DMA_CHCTL(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) |= DMA_CHXCTL_CHEN; -} - -/** @brief 清空 SPI 接收通路残留(含 OVR),对齐 BOOT drainSpiRxHw。 */ -static void drainSpiRxHw(void) -{ - while (spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE) != RESET) { - (void)spi_i2s_data_receive(SPI0); - } - if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RXORERR) != RESET) { - (void)spi_i2s_data_receive(SPI0); - (void)SPI_STAT(SPI0); - } -} - -/** @brief 停止双 DMA;返回本 CS 实际 RX 字节数(必须在 disable 前读 CHCNT)。 - * 对齐 BOOT stopSpiDmas:先读 remaining 再 disable,否则计数器被清。 */ -static uint16_t stopSpiDmas(uint16_t rx_cap) -{ - const uint16_t remaining = (uint16_t)DMA_CHCNT(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH); - DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) &= ~DMA_CHXCTL_CHEN; - DMA_CHCTL(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) &= ~DMA_CHXCTL_CHEN; - spi_dma_disable(SPI0, SPI_DMA_TRANSMIT); - spi_dma_disable(SPI0, SPI_DMA_RECEIVE); - uint16_t got = 0U; - if (remaining <= rx_cap) { - got = (uint16_t)(rx_cap - remaining); - } - return got; -} - -static bool isAllBytesValue(const uint8_t *data, uint16_t len, uint8_t value) -{ - if (!data || len == 0U) { - return false; - } - for (uint16_t i = 0U; i < len; ++i) { - if (data[i] != value) { - return false; - } - } - return true; -} - -static bool shouldIgnoreTransaction(const uint8_t *data, uint16_t len) -{ - if (!data || len == 0U) { - return true; - } - - if (isAllBytesValue(data, len, 0xFFU) || isAllBytesValue(data, len, 0x00U)) { - return true; - } - - /* v2 协议帧最小长度为 V2_OVERHEAD(20B)。 - * 短于该长度的事务只能是启动切换期的残包,直接忽略。 - * (0xB0 进 BOOT 现已走标准 v2 帧 handler,不再有裸字节通道。) */ - return (len < V2_OVERHEAD); -} - -static bool isFlashRelatedFrame(uint8_t frame_type) -{ - switch (frame_type) { - case kTypeOtaReq: - case kTypeOtaData: - case kTypeOtaCancel: - case kTypeBootReq: - return true; - default: - return false; - } -} - -/* GD32 APP 侧同步状态机(ISR 与主循环只读/原子写,避免临界区开销)。 */ -static volatile uint8_t g_localCredit = 8U; -static volatile uint8_t g_remoteCredit = 8U; - -/* SPI Link v2 会话/序号状态机(ISR 写、主循环读写,均原子,无需临界区)。 */ -static volatile uint32_t g_sessionId = 0; /* v2 会话 ID(0=未建立) */ -static volatile uint8_t g_txSeq = 0; /* GD32 发送序号(事件/响应帧) */ -static volatile uint8_t g_expectedRxSeq = 0; /* 期望收到的 ESP32 请求序号 */ -static volatile bool g_v2Active = false; /* v2 session 是否激活 */ -/* 当前请求的 TX_SEQ,写入响应 ACK_SEQ,供 ESP32 做响应归属匹配(延迟一拍)。 */ -static volatile uint8_t g_pendingAckSeq = 0; - -/* 响应缓存:用于重复请求重发同一响应(避免业务 handler 重复执行)。 */ -static uint8_t s_lastReqSeq = 0xFF; -static uint8_t s_lastRespBuf[256]; -static uint16_t s_lastRespLen = 0; -static bool s_hasCachedResp = false; - -/* ============================================================ - * v2 帧构建 / 帧解析(委托 spi_link_v2.h,三工程共用) - * ============================================================ */ - -/* 构建响应帧;上层接口(type/payload)保持不变,handler 无需改动。 - * 内部调用 v2_build_frame,FLAGS 默认 V2_FLAG_RESPONSE,session/seq/credit - * 从 v2 状态全局变量取。session 未建立时 session_id 填 0。 - * ACK_SEQ = g_pendingAckSeq(对端请求的 TX_SEQ),不能写死 0,否则 ESP32 - * otaFindFrame 在 m_v2Active 时会把合法延迟响应当成 stale 丢弃。 */ -static bool spi_build_frame(uint8_t type, const uint8_t *payload, uint16_t payload_len, - uint8_t *out, uint16_t *out_len) -{ - if (!out || !out_len) { return false; } - if ((uint32_t)V2_OVERHEAD + payload_len > AppSpiCommand::kTxBufSize) { return false; } - - const uint32_t sess = g_v2Active ? g_sessionId : 0U; - size_t built = 0U; - const bool ok = v2_build_frame(type, V2_FLAG_RESPONSE, sess, - g_txSeq, g_pendingAckSeq, (uint8_t)g_localCredit, - payload, payload_len, out, &built); - if (!ok) { return false; } - *out_len = (uint16_t)built; - return true; -} - -/* 解析 v2 帧;委托 v2_parse_frame,扫描 SOF 并校验 VERSION/CRC32。 - * 旧的 0xA5 CRC8 帧兼容路径已移除(v2 不再向后兼容 v1)。 */ -static bool spi_try_parse_frame(const uint8_t *frame, uint16_t frame_len, - uint8_t *type, const uint8_t **payload, uint16_t *payload_len, - v2_header_t *out_hdr = nullptr) -{ - if (!frame || !type || !payload || !payload_len) { return false; } - - v2_header_t hdr; - const uint8_t *pload = nullptr; - const bool ok = v2_parse_frame(frame, frame_len, &hdr, &pload); - if (!ok) { return false; } - - g_remoteCredit = hdr.credit; - *type = hdr.type; - *payload = pload; - *payload_len = hdr.payload_len; - if (out_hdr) { *out_hdr = hdr; } - return true; -} - -/* ============================================================ - * 调试日志辅助 - * ============================================================ */ - -void AppSpiCommand::postLog(const char *msg) -{ -#if (CONFIG_SPI_APP_LOG_ENABLE != 0) - if (!msg) return; - snprintf(m_logBuf, kLogBufSize, "[SPI-APP] %s", msg); - m_logPending = true; -#else - (void)msg; -#endif -} - -void AppSpiCommand::postLogFmt(const char *fmt, uint8_t val) -{ -#if (CONFIG_SPI_APP_LOG_ENABLE != 0) - if (!fmt) return; - snprintf(m_logBuf, kLogBufSize, fmt, val); - m_logPending = true; -#else - (void)fmt; - (void)val; -#endif -} - -void AppSpiCommand::buildResponse(uint8_t *tx_buf, uint16_t tx_buf_size, - uint16_t *tx_len, uint8_t rsp_type, - const uint8_t *payload, uint16_t payload_len) -{ - if (!tx_buf || !tx_len || tx_buf_size == 0U) { - return; - } - - uint16_t built_len = 0U; - const bool ok = spi_build_frame(rsp_type, payload, payload_len, tx_buf, &built_len); - if (ok) { - *tx_len = built_len; - } -} - -/* ============================================================ - * 硬件配置 - * ============================================================ */ - -void AppSpiCommand::configureGpio() -{ - rcu_periph_clock_enable(RCU_GPIOA); - rcu_periph_clock_enable(RCU_GPIOB); - rcu_periph_clock_enable(RCU_AF); - - gpio_pin_remap_config(GPIO_SPI0_REMAP, ENABLE); - gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP, ENABLE); - - gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3 | GPIO_PIN_5); - gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4); - gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_15); - - /* PB6 复用为 DATA_READY 握手线(开漏输出,线与)。 - * 开漏模式:写 BRR = 下拉驱动低;写 BSRR = 高阻释放,靠外部/对端上拉拉高。 - * 语义:低 = 有待发数据,高 = 队列空;默认释放(HIGH)。 */ - gpio_init(CONFIG_SPI_READY_PORT, GPIO_MODE_OUT_OD, GPIO_OSPEED_50MHZ, CONFIG_SPI_READY_PIN); - gpio_bit_set(CONFIG_SPI_READY_PORT, CONFIG_SPI_READY_PIN); -} - -void AppSpiCommand::setSpiReady(bool ready) -{ - /* 开漏线与:ready=true 释放(BSRR,高阻,靠上拉拉高); - * ready=false 下拉(BRR,驱动低)。 */ - if (ready) { - gpio_bit_set(CONFIG_SPI_READY_PORT, CONFIG_SPI_READY_PIN); - } else { - gpio_bit_reset(CONFIG_SPI_READY_PORT, CONFIG_SPI_READY_PIN); - } -} - -/* v2 DATA_READY: PB6 低 = 有待发数据,高 = 队列空。 - * L5:同步 g_localCredit(单缓冲 0/1)。 */ -void AppSpiCommand::updateDataReady() -{ - const bool has_data = (m_txLen > 0U) || m_ringPending; - setSpiReady(!has_data); - g_localCredit = has_data ? 0U : 1U; -} - -void AppSpiCommand::configureSpi0() -{ - rcu_periph_clock_enable(RCU_SPI0); - spi_i2s_deinit(SPI0); - (void)spi_i2s_data_receive(SPI0); /* 清空 SPI_DATA 残留值 */ - - spi_parameter_struct spi_init_struct; - spi_struct_para_init(&spi_init_struct); - spi_init_struct.device_mode = SPI_SLAVE; - spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; - spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; - spi_init_struct.nss = SPI_NSS_SOFT; - spi_init_struct.endian = SPI_ENDIAN_MSB; - spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; - spi_init_struct.prescale = SPI_PSC_8; - spi_init(SPI0, &spi_init_struct); - - /* 内部 NSS 常低:SPI 从机始终就绪,不依赖 EXTI ISR 切换 internal NSS。 - * PA15 仅作 EXTI 事务边界信号,用于 onCsAssert/onCsRelease 控制 DMA 装载。 */ - spi_nss_internal_low(SPI0); - (void)spi_i2s_data_receive(SPI0); - spi_i2s_data_transmit(SPI0, 0xFFU); - /* RX 改走 DMA0_CH1:禁用 RBNE 逐字节中断,避免 256B 事务 RX-OVR/SOF 丢失。 */ - spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_RBNE); - spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_ERR); - spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_TBE); - nvic_irq_enable(SPI0_IRQn, 2U, 0U); - spi_enable(SPI0); -} - -void AppSpiCommand::configureDma() -{ - rcu_periph_clock_enable(RCU_DMA0); - - /* TX: memory → SPI0_DATA */ - dma_deinit(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH); - dma_parameter_struct dma_cfg; - dma_struct_para_init(&dma_cfg); - dma_cfg.periph_addr = (uint32_t)(SPI0 + 0x0CU); /* SPI0_DATA 寄存器 */ - dma_cfg.periph_width = DMA_PERIPHERAL_WIDTH_8BIT; - dma_cfg.memory_addr = (uint32_t)m_txBuf; - dma_cfg.memory_width = DMA_MEMORY_WIDTH_8BIT; - dma_cfg.number = 0U; - dma_cfg.priority = DMA_PRIORITY_HIGH; - dma_cfg.periph_inc = DMA_PERIPH_INCREASE_DISABLE; - dma_cfg.memory_inc = DMA_MEMORY_INCREASE_ENABLE; - dma_cfg.direction = DMA_MEMORY_TO_PERIPHERAL; - dma_init(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH, &dma_cfg); - dma_circulation_disable(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH); - dma_channel_disable(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH); - - /* RX: SPI0_DATA → m_rxBuf(替代旧 RBNE 逐字节累积)。 - * 与 BOOT SpiOta 对齐:DMA0_CH1 固定映射 SPI0_RX。 */ - dma_deinit(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH); - dma_struct_para_init(&dma_cfg); - dma_cfg.periph_addr = (uint32_t)(SPI0 + 0x0CU); - dma_cfg.periph_width = DMA_PERIPHERAL_WIDTH_8BIT; - dma_cfg.memory_addr = (uint32_t)m_rxBuf; - dma_cfg.memory_width = DMA_MEMORY_WIDTH_8BIT; - dma_cfg.number = kRxBufSize; - dma_cfg.priority = DMA_PRIORITY_HIGH; - dma_cfg.periph_inc = DMA_PERIPH_INCREASE_DISABLE; - dma_cfg.memory_inc = DMA_MEMORY_INCREASE_ENABLE; - dma_cfg.direction = DMA_PERIPHERAL_TO_MEMORY; - dma_init(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH, &dma_cfg); - dma_circulation_disable(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH); - dma_channel_disable(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH); -} - -void AppSpiCommand::configureExti() -{ - gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_15); - exti_init(EXTI_15, EXTI_INTERRUPT, EXTI_TRIG_BOTH); - exti_interrupt_enable(EXTI_15); - exti_interrupt_flag_clear(EXTI_15); - nvic_irq_enable(EXTI10_15_IRQn, 1U, 1U); -} - - - -/* ============================================================ - * 默认命令注册入口(由各 app_spi_cmd_*.cpp 提供) - * ============================================================ */ -extern void registerSpiBootRequestCommand(AppSpiCommand &bus); -extern void registerSpiOtaWaitCommand(AppSpiCommand &bus); -extern void registerSpiAppPingCommand(AppSpiCommand &bus); -extern void registerSpiCanBridgeCommand(AppSpiCommand &bus); -extern void registerSpiCanFeedbackCommand(AppSpiCommand &bus); -extern void registerSpiIdSnapshotCommand(AppSpiCommand &bus); -AppSpiCommand AppSpiCommandBus; -void AppSpiCommand::init() -{ - m_csActive = false; - m_rxLen = 0U; - m_txLen = 0U; - m_logPending = false; - m_txActiveThisCs = false; - m_commandCount = 0U; - m_ringPending = false; - m_ringDeadline = 0U; - memset(m_txBuf, 0xFF, sizeof(m_txBuf)); - memset(m_logBuf, 0, sizeof(m_logBuf)); - memset(m_commands, 0, sizeof(m_commands)); - - registerSpiBootRequestCommand(AppSpiCommandBus); - registerSpiOtaWaitCommand(AppSpiCommandBus); - registerSpiAppPingCommand(AppSpiCommandBus); - registerSpiCanBridgeCommand(AppSpiCommandBus); - registerSpiCanFeedbackCommand(AppSpiCommandBus); - registerSpiIdSnapshotCommand(AppSpiCommandBus); - - configureGpio(); - configureDma(); - configureSpi0(); - configureExti(); - /* 首笔 CS 到来前预装 DMA,避免 EXTI 晚于首 SCK。 - * 必须在 configureSpi0/configureDma 之后调用。 */ - armSpiDmasForNextCs(); -} - -/** @brief 在 CS 空闲期预装下一笔全双工 DMA。 - * - * 必须在首个 SCK 之前完成:EXTI CS 下降相对 ESP32 时钟经常偏晚, - * 若在 onCsAssert 里才 start DMA,整笔事务会收 0 字节 → 永不 HELLO_ACK → MISO 全 0xFF。 - * - * TX 始终按 kTxBufSize 装载:响应帧在头部,其余 0xFF。 - * 若只 DMA 实际帧长(~26B),主机打 96 时钟时 TX underrun,后续 MISO 不定。 - * - * 对齐 BOOT SpiOta::armSpiDmasForNextCs。 - */ -void AppSpiCommand::armSpiDmasForNextCs() -{ - DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) &= ~DMA_CHXCTL_CHEN; - DMA_CHCTL(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) &= ~DMA_CHXCTL_CHEN; - spi_dma_disable(SPI0, SPI_DMA_TRANSMIT); - spi_dma_disable(SPI0, SPI_DMA_RECEIVE); - drainSpiRxHw(); - - /* 预填 TX:有挂起响应用响应帧(头部)+ 0xFF 填充到满缓冲 */ - if (m_txLen > 0U && m_txLen <= kTxBufSize) { - if (m_txLen < kTxBufSize) { - memset(&m_txBuf[m_txLen], 0xFF, (size_t)(kTxBufSize - m_txLen)); - } - } else { - m_txLen = 0U; - memset(m_txBuf, 0xFF, kTxBufSize); - } - - /* RX 缓冲清零(便于诊断时区分有效字节与残留) */ - memset(m_rxBuf, 0x00, kRxBufSize); - m_rxLen = 0U; - - /* 预装 SPI_DATA,避免 DMA 启动前首拍 TBE 吐旧值 */ - spi_i2s_data_transmit(SPI0, m_txBuf[0]); - - /* 先使能 SPI DMA 请求,再开 DMA 通道,保证首字节即可搬运 */ - spi_dma_enable(SPI0, SPI_DMA_RECEIVE); - spi_dma_enable(SPI0, SPI_DMA_TRANSMIT); - startSpiRxDma(m_rxBuf, kRxBufSize); - /* TX 永远满缓冲长度,与主机 96~256B 事务匹配,避免 underrun */ - startSpiTxDma(m_txBuf, kTxBufSize); -} - -/* ============================================================ - * 命令路由 - * ============================================================ */ - -void AppSpiCommand::registerHandler(ISpiCommandHandler *handler) -{ - if (!handler || m_commandCount >= kMaxCommands) { - return; - } - m_commands[m_commandCount++] = handler; -} - -void AppSpiCommand::processCommand() -{ - if (m_rxLen == 0U) { - return; - } - - /* 0xB0 已受理、正在写 OTA 配置页 / 即将复位:不再处理业务帧,避免打断 Flash。 */ - if (OtaBootRequest::isSpiFrozenForBoot() || OtaBootRequest::isRebootPending()) { - m_txLen = 0U; - updateDataReady(); - return; - } - - /* 尝试按 v2 协议帧解析;同时取回完整帧头用于 session/seq 校验 */ - uint8_t frame_type = 0U; - const uint8_t *payload = nullptr; - uint16_t payload_len = 0U; - v2_header_t hdr; - - if (!spi_try_parse_frame(m_rxBuf, m_rxLen, &frame_type, &payload, &payload_len, &hdr)) { - /* 无法解析为合法 v2 帧:构建 ERROR 响应 */ - postLogFmt("[SPI-APP] 非v2帧 0x%02X -> ERROR", m_rxBuf[0]); - uint16_t tx_len = 0U; - uint8_t err = 0xEEU; - spi_build_frame(kRspError, &err, 1U, m_txBuf, &tx_len); - m_txLen = tx_len; - - if (m_logPending) { - TaskManager::set(1UL << TASK_SPI_LOG); - } - if (m_txLen > 0U) { - armRingPending(); - } - updateDataReady(); - return; - } - - if (CONFIG_SPI_APP_LOG_ENABLE != 0 && !isFlashRelatedFrame(frame_type)) { - postLogFmt("[SPI-APP] 收到非刷写 SPI type=0x%02X", frame_type); - } - - /* 响应归属:ACK_SEQ 必须回显请求 TX_SEQ(延迟一拍 + ESP32 otaFindFrame 匹配)。 */ - g_pendingAckSeq = hdr.tx_seq; - - /* ---------- HELLO 建链路处理(在 session 校验之前) ---------- */ - if (frame_type == V2_TYPE_LINK_HELLO) { - /* 接受 ESP32 提供的 session_id,重置本端序号状态机 */ - g_sessionId = hdr.session_id; - g_txSeq = 0; - g_expectedRxSeq = 0; - g_v2Active = true; - s_hasCachedResp = false; - - /* 构建 HELLO_ACK 响应帧(回显 session_id + 角色 + credit_max) */ - size_t built_len = 0U; - v2_build_hello_ack_frame(g_sessionId, V2_ROLE_APP, (uint8_t)g_localCredit, - 0, hdr.tx_seq, m_txBuf, &built_len); - m_txLen = (uint16_t)built_len; - - /* 缓存 HELLO_ACK 便于重复 HELLO 重发 */ - s_lastReqSeq = hdr.tx_seq; - memcpy(s_lastRespBuf, m_txBuf, m_txLen); - s_lastRespLen = m_txLen; - s_hasCachedResp = true; - - if (m_logPending) { - TaskManager::set(1UL << TASK_SPI_LOG); - } - if (m_txLen > 0U) { - armRingPending(); - } - updateDataReady(); - return; - } - - /* ---------- SESSION / SEQ 校验(仅在 v2 session 激活后生效) ---------- */ - if (g_v2Active) { - /* session_id 不匹配:构建 LINK_ERROR(V2_ERR_SESSION),跳过 handler */ - if (hdr.session_id != g_sessionId) { - postLogFmt("[SPI-APP] session 不匹配 0x%02X -> LINK_ERROR", - (uint8_t)(hdr.session_id & 0xFFU)); - size_t built_len = 0U; - v2_build_error_frame(g_sessionId, g_txSeq, hdr.tx_seq, - (uint8_t)g_localCredit, V2_ERR_SESSION, - (uint8_t)g_expectedRxSeq, m_txBuf, &built_len); - m_txLen = (uint16_t)built_len; - - if (m_logPending) { - TaskManager::set(1UL << TASK_SPI_LOG); - } - if (m_txLen > 0U) { - armRingPending(); - } - updateDataReady(); - return; - } - - /* 序号回绕距离判定 */ - const uint8_t dist = v2_seq_distance(hdr.tx_seq, g_expectedRxSeq); - if (dist == 0U) { - /* 正常:序号匹配,推进期望序号 */ - g_expectedRxSeq = (uint8_t)(g_expectedRxSeq + 1U); - } else if (dist >= 128U) { - /* 重复请求:重发缓存的响应帧,跳过 handler */ - if (s_hasCachedResp && hdr.tx_seq == s_lastReqSeq) { - postLogFmt("[SPI-APP] 重复请求 seq=0x%02X -> 重发缓存", hdr.tx_seq); - memcpy(m_txBuf, s_lastRespBuf, s_lastRespLen); - m_txLen = s_lastRespLen; - - if (m_logPending) { - TaskManager::set(1UL << TASK_SPI_LOG); - } - if (m_txLen > 0U) { - armRingPending(); - } - updateDataReady(); - return; - } - /* 无缓存可用:按正常请求处理 */ - } else { - /* dist 1-127:序号跳变。对齐期望序号后: - * - 普通业务:回 LINK_ERROR 并丢弃(避免副作用) - * - 0xB0 ENTER_BOOT:控制面命令,仍必须执行,否则永远进不了 BOOT */ - postLogFmt("[SPI-APP] SEQ jump rx=0x%02X", hdr.tx_seq); - g_expectedRxSeq = (uint8_t)(hdr.tx_seq + 1U); - - if (frame_type != kTypeBootReq) { - size_t built_len = 0U; - v2_build_error_frame(g_sessionId, g_txSeq, hdr.tx_seq, - (uint8_t)g_localCredit, V2_ERR_SEQ_JUMP, - (uint8_t)g_expectedRxSeq, m_txBuf, &built_len); - m_txLen = (uint16_t)built_len; - - if (m_logPending) { - TaskManager::set(1UL << TASK_SPI_LOG); - } - if (m_txLen > 0U) { - armRingPending(); - } - updateDataReady(); - return; - } - postLog("SEQ jump but BOOT_REQ still handled"); - /* fall through → 业务分发 */ - } - } - - /* ---------- 业务命令分发 ---------- */ - uint16_t tx_len = 0U; - bool handled = false; - - SpiCommandContext ctx{}; - ctx.frame_type = frame_type; - ctx.payload = payload; - ctx.payload_len = payload_len; - ctx.tx_buf = m_txBuf; - ctx.tx_buf_size = kTxBufSize; - ctx.tx_len = &tx_len; - - for (uint8_t i = 0U; i < m_commandCount; ++i) { - ISpiCommandHandler *handler = m_commands[i]; - if (handler && handler->canHandle(frame_type)) { - handler->handle(AppSpiCommandBus, ctx); - handled = true; - break; - } - } - - if (!handled) { - postLogFmt("[SPI-APP] 未知 type=0x%02X -> ERROR", frame_type); - uint8_t err = 0xEEU; - spi_build_frame(kRspError, &err, 1U, m_txBuf, &tx_len); - } - - if (tx_len == 0U) { - /* 兜底:避免残留上一次应答帧 */ - uint8_t err = 0xEEU; - spi_build_frame(kRspError, &err, 1U, m_txBuf, &tx_len); - } - - m_txLen = tx_len; - - /* 缓存本次响应,便于重复请求时重发(避免 handler 重复执行副作用) */ - if (g_v2Active) { - s_lastReqSeq = hdr.tx_seq; - memcpy(s_lastRespBuf, m_txBuf, m_txLen); - s_lastRespLen = m_txLen; - s_hasCachedResp = true; - /* 推进本端发送序号(响应帧占用一个 tx_seq 槽位) */ - g_txSeq = (uint8_t)(g_txSeq + 1U); - } - - if (m_logPending) { - /* 置位 TASK_SPI_LOG,唤醒主循环立刻打印日志 */ - TaskManager::set(1UL << TASK_SPI_LOG); - } - - /* 应答帧已就绪:由 updateDataReady() 依据 m_txLen 驱动 PB6(有待发=低)。 - * 命令处理已在 ISR 内同步完成,无需 m_cmdPending 推迟机制。 */ - - /* 有响应待发时,设置按铃标志阻止主循环 canFeedbackPushStep() 覆盖 m_txBuf。 - * ESP32 下次 CS assert 时 DMA 输出响应,onCsRelease 清除 m_ringPending。 - * 50ms 超时后 cancelRing() 清除,恢复 CAN 反馈推送。 */ - if (m_txLen > 0U) { - armRingPending(); - } - updateDataReady(); -} - -/* ============================================================ - * GD32 主动按铃:设 m_ringPending + updateDataReady() 拉低 PB6 请求 ESP32 来读 - * ============================================================ */ - -void AppSpiCommand::armRingPending() -{ - m_ringPending = true; - m_ringDeadline = SysTickTimer::getMs() + kRingTimeoutMs; -} - -void AppSpiCommand::cancelRing() -{ - m_ringPending = false; - /* 清除待发响应,防止超时后残留帧在下一次 CS assert 时被错误输出 */ - m_txLen = 0U; - /* 释放 SPIready(高阻),避免永久占用线。 - * cancelRing 已清 m_txLen 和 m_ringPending,updateDataReady() 会释放为高。 */ - updateDataReady(); -} - -bool AppSpiCommand::requestEsp32Read(uint8_t frame_type, const uint8_t *payload, uint16_t payload_len) -{ - /* 若上一次按铃尚未被 ESP32 读走,拒绝重复按铃 */ - if (m_ringPending) { - return false; - } - - /* 构建 v2 EVENT 帧到 m_txBuf(ESP32 下次读事务时通过 DMA 输出到 MISO)。 - * 主动按铃帧使用 V2_FLAG_EVENT,session/seq 从 v2 状态全局变量取; - * session 未建立时 session_id 填 0。 */ - const uint32_t sess = g_v2Active ? g_sessionId : 0U; - /* L5:单缓冲 — 装载前 credit 报 1,装载后 updateDataReady 置 0 */ - g_localCredit = 1U; - size_t built_len = 0U; - if (!v2_build_frame(frame_type, V2_FLAG_EVENT, sess, - g_txSeq, 0U, (uint8_t)g_localCredit, - payload, payload_len, m_txBuf, &built_len)) { - return false; - } - /* EVENT 帧占用一个发送序号槽位 */ - if (g_v2Active) { - g_txSeq = (uint8_t)(g_txSeq + 1U); - } - - m_txLen = (uint16_t)built_len; - armRingPending(); - - /* v2 DATA_READY: 已设 m_ringPending=true 且 m_txLen>0, - * updateDataReady() 据此拉低 PB6,并置 g_localCredit=0。 */ - updateDataReady(); - return true; -} - -/* ============================================================ - * 主循环轮询:打印延迟日志 + 按铃超时看护 - * ============================================================ */ - -void AppSpiCommand::poll() -{ - /* 打印 ISR 推迟的日志 */ - char log_copy[kLogBufSize] = {}; - bool has_log = false; - - uint32_t save = RT_CRITICAL_SAVE(); - if (m_logPending) { - memcpy(log_copy, m_logBuf, sizeof(log_copy)); - m_logPending = false; - has_log = true; - } - RT_CRITICAL_RESTORE(save); - - if (has_log) { - printf("%s\r\n", log_copy); - } - - /* 按铃超时看护:ESP32 未在 kRingTimeoutMs 内来读则放弃本次按铃。 - * 防止 ESP32 不响应时永久占用 SPIready 线导致双方死锁。 */ - if (m_ringPending) { - uint32_t save2 = RT_CRITICAL_SAVE(); - if (m_ringPending) { - if ((int32_t)(SysTickTimer::getMs() - m_ringDeadline) >= 0) { - printf("[SPI-APP] 按铃超时取消 (ESP32 未响应, 释放 SPIready 重试)\r\n"); - cancelRing(); - } - } - RT_CRITICAL_RESTORE(save2); - } -} - -/* ============================================================ - * CS 边沿处理 - * ============================================================ */ - -void AppSpiCommand::onCsAssert() -{ - if (m_csActive) { - return; - } - m_csActive = true; - /* CS 下降:DMA 已在空闲期由 armSpiDmasForNextCs() 预装,此处只标记忙碌。 - * v2 DATA_READY: CS 本身即互斥,PB6 不再表示「忙碌」,不在 CS 下降沿拉低。 - * 切勿在这里 stop/restart DMA,否则赶不上 ESP32 已发出的 SCK(对齐 BOOT)。 */ - m_txActiveThisCs = false; -} - -void AppSpiCommand::onCsRelease() -{ - if (!m_csActive) { - /* EXTI 丢失下降沿时:仍尝试收尾并 re-arm,避免永久失步(对齐 BOOT) */ - m_rxLen = stopSpiDmas(kRxBufSize); - drainSpiRxHw(); - if (m_txLen > 0U) { - m_txLen = 0U; - memset(m_txBuf, 0xFF, sizeof(m_txBuf)); - } - if (m_rxLen >= V2_OVERHEAD) { - processCommand(); - } - armSpiDmasForNextCs(); - updateDataReady(); - return; - } - m_csActive = false; - - /* 停 DMA 并取本笔 RX 长度(先读 CHCNT 再 disable,否则计数器被清) */ - m_rxLen = stopSpiDmas(kRxBufSize); - drainSpiRxHw(); - - /* 若本次 CS 事务是 ESP32 响应 GD32 按铃来读请求帧, - * 则 GD32 的请求帧已通过 DMA 输出到 MISO,取消按铃状态。 - * - * 但需区分:ESP32 发的是 dummy 读(按铃响应)还是 v2 协议帧(如 PING/OTA/0xB0)。 - * 若 GD32 正在按铃时 ESP32 发协议帧,会因 m_ringPending=true 被误吞, - * 导致命令无法处理。检测到 v2 SOF 时取消按铃并走正常命令处理。 */ - if (m_ringPending) { - /* 判断本次收到的数据是否为 v2 协议帧(扫描 SOF0 SOF1)。 - * 取代旧 containsSyncSof,直接用 V2_SOF0/V2_SOF1。 - * 0xB0 进 BOOT 现已走标准 v2 帧 handler,不再单独检测裸字节。 */ - bool is_v2_frame = false; - if (m_rxLen >= 2U) { - const uint16_t last = (m_rxLen < 16U) ? (uint16_t)(m_rxLen - 1U) : 16U; - for (uint16_t i = 0U; i < last; ++i) { - if (m_rxBuf[i] == V2_SOF0 && m_rxBuf[i + 1U] == V2_SOF1) { - is_v2_frame = true; - break; - } - } - } - if (is_v2_frame) { - /* ESP32 发的是 v2 协议帧(如 PING/OTA/0xB0 等),不是按铃响应。 - * 取消按铃,清空 TX 让 processCommand() 重新装载对应应答。 - * processCommand() 末尾由 updateDataReady() 依据新 m_txLen 驱动 PB6。 */ - m_ringPending = false; - m_txLen = 0U; - memset(m_txBuf, 0xFF, sizeof(m_txBuf)); - /* 继续走 shouldIgnoreTransaction + processCommand(),不 return */ - } else { - /* 正常按铃响应:请求帧已被读走,取消按铃状态 */ - m_ringPending = false; - /* 请求帧已被读走,清空 TX 防止下次事务重复输出 */ - m_txLen = 0U; - memset(m_txBuf, 0xFF, sizeof(m_txBuf)); - /* 重新预装下一笔 DMA,保证下一 CS 首字节即可收发 */ - armSpiDmasForNextCs(); - updateDataReady(); - return; - } - } - - if (shouldIgnoreTransaction(m_rxBuf, m_rxLen)) { - m_rxLen = 0U; - if (m_txActiveThisCs) { - m_txLen = 0U; - memset(m_txBuf, 0xFF, sizeof(m_txBuf)); - } - m_txActiveThisCs = false; - /* v2 DATA_READY: shouldIgnoreTransaction 路径已清 m_txLen=0, - * updateDataReady() 释放为高,避免 PB6 卡 LOW。 */ - armSpiDmasForNextCs(); - updateDataReady(); - return; - } - - m_txLen = 0U; - m_txActiveThisCs = false; - memset(m_txBuf, 0xFF, sizeof(m_txBuf)); - - /* 命令处理在 EXTI ISR 内同步完成(对齐 BOOT 的 isrProcessFrame 模式), - * 消除主循环延迟。处理完成后 processCommand() 末尾再 re-arm DMA + 释放 SPIready。 */ - processCommand(); - - /* 处理完立即预装下一笔,保证下一 CS 首字节即可收发 */ - armSpiDmasForNextCs(); - updateDataReady(); -} - -/* ============================================================ - * SPI0 / EXTI 中断处理 - * ============================================================ */ - -void AppSpiCommand::onInterrupt() -{ - /* RX 已改走 DMA0_CH1:RBNE 逐字节中断已禁用,本 ISR 只保留错误标志清理。 - * CS 边沿处理全部在 onCsExtiInterrupt 的 EXTI15 ISR 里完成。 */ - if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RXORERR) != RESET) { - /* RXORERR 理论上不应再出现(RX 走 DMA),但兜底清理避免标志卡死。 - * 读 SPI_DATA + SPI_STAT 清除 OVR 标志。 */ - (void)spi_i2s_data_receive(SPI0); - (void)SPI_STAT(SPI0); - } - if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_FERR) != RESET) { - spi_i2s_format_error_clear(SPI0, SPI_FLAG_FERR); - } -} - -void AppSpiCommand::onCsExtiInterrupt() -{ - if (exti_interrupt_flag_get(EXTI_15) != RESET) { - exti_interrupt_flag_clear(EXTI_15); - if (gpio_input_bit_get(GPIOA, GPIO_PIN_15) == RESET) { - onCsAssert(); - } else { - onCsRelease(); - } - } -} - -extern "C" void SPI0_IRQHandler(void) -{ - AppSpiCommandBus.onInterrupt(); -} - -extern "C" void EXTI10_15_IRQHandler(void) -{ - AppSpiCommandBus.onCsExtiInterrupt(); -} +/** + * @file app_spi_command.cpp + * @brief APP 运行期 SPI 链路与命令路由实现。 + * + * 本模块现在只负责: + * - SPI0 从机链路初始化(GPIO/SPI/DMA/EXTI) + * - CS、DMA、中断驱动的帧接收与发送 + * - 公共帧格式解析与应答构建 + * - 业务命令的注册与分发 + * + * 具体命令处理逻辑已经拆到独立的 app_spi_cmd_*.cpp, + * 通过 registerHandler() 注册进来,后续新增 SPI 业务时 + * 只需要新增命令处理器,不再继续膨胀本文件。 + * + * 握手架构(v2 DATA_READY,PB6<->ESP32 GPIO10 开漏单线): + * - PB6 低 = 有待发数据(m_txLen>0 或 m_ringPending),高 = 队列空。 + * - 命令处理在 onCsRelease() EXTI ISR 内同步完成(对齐 BOOT 的 isrProcessFrame), + * 完成后由 updateDataReady() 依据待发数据驱动 PB6,不再延迟拉高。 + * - GD32 可通过 requestEsp32Read() 设 m_ringPending 并由 updateDataReady() + * 拉低 PB6 主动按铃,请求 ESP32 来读。 + */ + +#include "device/spi/app_spi_command.h" +#include "device/spi/spi_command_handler.h" +#include "device/spi/app_spi_command_defs.h" +#include "spi_link_v2.h" +#include "sys/ota_boot_request.h" + +extern "C" { +#include "gd32e50x_exti.h" +} +#include "gd32e50x_gpio.h" +#include "gd32e50x_misc.h" +#include "gd32e50x_rcu.h" +#include "gd32e50x_dma.h" +#include "gd32e50x_spi.h" +#include "sys/config.h" +#include "sys/critical.h" +#include "sys/systick.h" +#include +#include + +/* ============================================================ + * 本地别名:保持当前实现文件内部风格不变 + * ============================================================ */ +static constexpr uint8_t kTypeBootReq = SpiDefs::kTypeBootReq; +static constexpr uint8_t kTypeOtaReq = SpiDefs::kTypeOtaReq; +static constexpr uint8_t kTypeOtaData = SpiDefs::kTypeOtaData; +static constexpr uint8_t kTypeOtaCancel = SpiDefs::kTypeOtaCancel; +static constexpr uint8_t kRspError = SpiDefs::kRspError; + +/* DMA 全双工配置(与 BOOT SpiOta 一致): + * DMA0_CH1: SPI0_RX DMA0_CH2: SPI0_TX + * RX 走 DMA 后禁用 RBNE 逐字节中断,避免 256B 事务 RX-OVR/SOF 丢失。 */ +#define SPI_TX_DMA_PERIPH DMA0 +#define SPI_TX_DMA_CH DMA_CH2 +#define SPI_RX_DMA_PERIPH DMA0 +#define SPI_RX_DMA_CH DMA_CH1 + +static void startSpiTxDma(const uint8_t *buf, uint16_t len) +{ + DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) &= ~DMA_CHXCTL_CHEN; + DMA_INTC(SPI_TX_DMA_PERIPH) |= DMA_FLAG_ADD(DMA_CHINTF_RESET_VALUE, SPI_TX_DMA_CH); + DMA_CHMADDR(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) = (uint32_t)buf; + DMA_CHCNT(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) = len; + DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) |= DMA_CHXCTL_CHEN; +} + +/** @brief 启动 DMA 接收 SPI0_DATA → m_rxBuf(容量 kRxBufSize)。 + * 每次 CS 空闲期预装,下一笔 CS 上升沿时由 stopSpiDmas() 读 CHCNT 反算实际长度。 */ +static void startSpiRxDma(uint8_t *rx_buf, uint16_t rx_cap) +{ + DMA_CHCTL(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) &= ~DMA_CHXCTL_CHEN; + DMA_INTC(SPI_RX_DMA_PERIPH) |= DMA_FLAG_ADD(DMA_CHINTF_RESET_VALUE, SPI_RX_DMA_CH); + DMA_CHMADDR(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) = (uint32_t)rx_buf; + DMA_CHCNT(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) = rx_cap; + DMA_CHCTL(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) |= DMA_CHXCTL_CHEN; +} + +/** @brief 清空 SPI 接收通路残留(含 OVR),对齐 BOOT drainSpiRxHw。 */ +static void drainSpiRxHw(void) +{ + while (spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE) != RESET) { + (void)spi_i2s_data_receive(SPI0); + } + if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RXORERR) != RESET) { + (void)spi_i2s_data_receive(SPI0); + (void)SPI_STAT(SPI0); + } +} + +/** @brief 停止双 DMA;返回本 CS 实际 RX 字节数(必须在 disable 前读 CHCNT)。 + * 对齐 BOOT stopSpiDmas:先读 remaining 再 disable,否则计数器被清。 */ +static uint16_t stopSpiDmas(uint16_t rx_cap) +{ + const uint16_t remaining = (uint16_t)DMA_CHCNT(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH); + DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) &= ~DMA_CHXCTL_CHEN; + DMA_CHCTL(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) &= ~DMA_CHXCTL_CHEN; + spi_dma_disable(SPI0, SPI_DMA_TRANSMIT); + spi_dma_disable(SPI0, SPI_DMA_RECEIVE); + uint16_t got = 0U; + if (remaining <= rx_cap) { + got = (uint16_t)(rx_cap - remaining); + } + return got; +} + +static bool isAllBytesValue(const uint8_t *data, uint16_t len, uint8_t value) +{ + if (!data || len == 0U) { + return false; + } + for (uint16_t i = 0U; i < len; ++i) { + if (data[i] != value) { + return false; + } + } + return true; +} + +static bool shouldIgnoreTransaction(const uint8_t *data, uint16_t len) +{ + if (!data || len == 0U) { + return true; + } + + if (isAllBytesValue(data, len, 0xFFU) || isAllBytesValue(data, len, 0x00U)) { + return true; + } + + /* v2 协议帧最小长度为 V2_OVERHEAD(20B)。 + * 短于该长度的事务只能是启动切换期的残包,直接忽略。 + * (0xB0 进 BOOT 现已走标准 v2 帧 handler,不再有裸字节通道。) */ + return (len < V2_OVERHEAD); +} + +static bool isFlashRelatedFrame(uint8_t frame_type) +{ + switch (frame_type) { + case kTypeOtaReq: + case kTypeOtaData: + case kTypeOtaCancel: + case kTypeBootReq: + return true; + default: + return false; + } +} + +/* GD32 APP 侧同步状态机(ISR 与主循环只读/原子写,避免临界区开销)。 */ +static volatile uint8_t g_localCredit = 8U; + +/* SPI Link v2 会话/序号状态机(ISR 写、主循环读写,均原子,无需临界区)。 */ +static volatile uint32_t g_sessionId = 0; /* v2 会话 ID(0=未建立) */ +static volatile uint8_t g_txSeq = 0; /* GD32 发送序号(事件/响应帧) */ +static volatile uint8_t g_expectedRxSeq = 0; /* 期望收到的 ESP32 请求序号 */ +static volatile bool g_v2Active = false; /* v2 session 是否激活 */ +/* 当前请求的 TX_SEQ,写入响应 ACK_SEQ,供 ESP32 做响应归属匹配(延迟一拍)。 */ +static volatile uint8_t g_pendingAckSeq = 0; + +/* 响应缓存:用于重复请求重发同一响应(避免业务 handler 重复执行)。 */ +static uint8_t s_lastReqSeq = 0xFF; +static uint8_t s_lastRespBuf[256]; +static uint16_t s_lastRespLen = 0; +static bool s_hasCachedResp = false; + +/* ============================================================ + * v2 帧构建 / 帧解析(委托 spi_link_v2.h,三工程共用) + * ============================================================ */ + +/* 构建响应帧;上层接口(type/payload)保持不变,handler 无需改动。 + * 内部调用 v2_build_frame,FLAGS 默认 V2_FLAG_RESPONSE,session/seq/credit + * 从 v2 状态全局变量取。session 未建立时 session_id 填 0。 + * ACK_SEQ = g_pendingAckSeq(对端请求的 TX_SEQ),不能写死 0,否则 ESP32 + * otaFindFrame 在 m_v2Active 时会把合法延迟响应当成 stale 丢弃。 */ +static bool spi_build_frame(uint8_t type, const uint8_t *payload, uint16_t payload_len, + uint8_t *out, uint16_t *out_len) +{ + if (!out || !out_len) { return false; } + if ((uint32_t)V2_OVERHEAD + payload_len > AppSpiCommand::kTxBufSize) { return false; } + + const uint32_t sess = g_v2Active ? g_sessionId : 0U; + size_t built = 0U; + const bool ok = v2_build_frame(type, V2_FLAG_RESPONSE, sess, + g_txSeq, g_pendingAckSeq, (uint8_t)g_localCredit, + payload, payload_len, out, &built); + if (!ok) { return false; } + *out_len = (uint16_t)built; + return true; +} + +/* 解析 v2 帧;委托 v2_parse_frame,扫描 SOF 并校验 VERSION/CRC32。 + * 仅 v2:v2_parse_frame 扫 SOF0/SOF1 + CRC32;禁止再加 v1 CRC8 分支。 */ +static bool spi_try_parse_frame(const uint8_t *frame, uint16_t frame_len, + uint8_t *type, const uint8_t **payload, uint16_t *payload_len, + v2_header_t *out_hdr = nullptr) +{ + if (!frame || !type || !payload || !payload_len) { return false; } + + v2_header_t hdr; + const uint8_t *pload = nullptr; + const bool ok = v2_parse_frame(frame, frame_len, &hdr, &pload); + if (!ok) { return false; } + + *type = hdr.type; + *payload = pload; + *payload_len = hdr.payload_len; + if (out_hdr) { *out_hdr = hdr; } + return true; +} + +/* ============================================================ + * 调试日志辅助 + * ============================================================ */ + +void AppSpiCommand::postLog(const char *msg) +{ +#if (CONFIG_SPI_APP_LOG_ENABLE != 0) + if (!msg) return; + snprintf(m_logBuf, kLogBufSize, "[SPI-APP] %s", msg); + m_logPending = true; +#else + (void)msg; +#endif +} + +void AppSpiCommand::postLogFmt(const char *fmt, uint8_t val) +{ +#if (CONFIG_SPI_APP_LOG_ENABLE != 0) + if (!fmt) return; + snprintf(m_logBuf, kLogBufSize, fmt, val); + m_logPending = true; +#else + (void)fmt; + (void)val; +#endif +} + +void AppSpiCommand::buildResponse(uint8_t *tx_buf, uint16_t tx_buf_size, + uint16_t *tx_len, uint8_t rsp_type, + const uint8_t *payload, uint16_t payload_len) +{ + if (!tx_buf || !tx_len || tx_buf_size == 0U) { + return; + } + + uint16_t built_len = 0U; + const bool ok = spi_build_frame(rsp_type, payload, payload_len, tx_buf, &built_len); + if (ok) { + *tx_len = built_len; + } +} + +/* ============================================================ + * 硬件配置 + * ============================================================ */ + +void AppSpiCommand::configureGpio() +{ + rcu_periph_clock_enable(RCU_GPIOA); + rcu_periph_clock_enable(RCU_GPIOB); + rcu_periph_clock_enable(RCU_AF); + + gpio_pin_remap_config(GPIO_SPI0_REMAP, ENABLE); + gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP, ENABLE); + + gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3 | GPIO_PIN_5); + gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4); + gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_15); + + /* PB6 复用为 DATA_READY 握手线(开漏输出,线与)。 + * 开漏模式:写 BRR = 下拉驱动低;写 BSRR = 高阻释放,靠外部/对端上拉拉高。 + * 语义:低 = 有待发数据,高 = 队列空;默认释放(HIGH)。 */ + gpio_init(CONFIG_SPI_READY_PORT, GPIO_MODE_OUT_OD, GPIO_OSPEED_50MHZ, CONFIG_SPI_READY_PIN); + gpio_bit_set(CONFIG_SPI_READY_PORT, CONFIG_SPI_READY_PIN); +} + +void AppSpiCommand::setSpiReady(bool ready) +{ + /* 开漏线与:ready=true 释放(BSRR,高阻,靠上拉拉高); + * ready=false 下拉(BRR,驱动低)。 */ + if (ready) { + gpio_bit_set(CONFIG_SPI_READY_PORT, CONFIG_SPI_READY_PIN); + } else { + gpio_bit_reset(CONFIG_SPI_READY_PORT, CONFIG_SPI_READY_PIN); + } +} + +/* v2 DATA_READY: PB6 低 = 有待发数据,高 = 队列空。 + * L5:同步 g_localCredit(单缓冲 0/1)。 */ +void AppSpiCommand::updateDataReady() +{ + const bool has_data = (m_txLen > 0U) || m_ringPending; + setSpiReady(!has_data); + g_localCredit = has_data ? 0U : 1U; +} + +void AppSpiCommand::configureSpi0() +{ + rcu_periph_clock_enable(RCU_SPI0); + spi_i2s_deinit(SPI0); + (void)spi_i2s_data_receive(SPI0); /* 清空 SPI_DATA 残留值 */ + + spi_parameter_struct spi_init_struct; + spi_struct_para_init(&spi_init_struct); + spi_init_struct.device_mode = SPI_SLAVE; + spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; + spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; + spi_init_struct.nss = SPI_NSS_SOFT; + spi_init_struct.endian = SPI_ENDIAN_MSB; + spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; + spi_init_struct.prescale = SPI_PSC_8; + spi_init(SPI0, &spi_init_struct); + + /* 内部 NSS 常低:SPI 从机始终就绪,不依赖 EXTI ISR 切换 internal NSS。 + * PA15 仅作 EXTI 事务边界信号,用于 onCsAssert/onCsRelease 控制 DMA 装载。 */ + spi_nss_internal_low(SPI0); + (void)spi_i2s_data_receive(SPI0); + spi_i2s_data_transmit(SPI0, 0xFFU); + /* RX 改走 DMA0_CH1:禁用 RBNE 逐字节中断,避免 256B 事务 RX-OVR/SOF 丢失。 */ + spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_RBNE); + spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_ERR); + spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_TBE); + nvic_irq_enable(SPI0_IRQn, 2U, 0U); + spi_enable(SPI0); +} + +void AppSpiCommand::configureDma() +{ + rcu_periph_clock_enable(RCU_DMA0); + + /* TX: memory → SPI0_DATA */ + dma_deinit(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH); + dma_parameter_struct dma_cfg; + dma_struct_para_init(&dma_cfg); + dma_cfg.periph_addr = (uint32_t)(SPI0 + 0x0CU); /* SPI0_DATA 寄存器 */ + dma_cfg.periph_width = DMA_PERIPHERAL_WIDTH_8BIT; + dma_cfg.memory_addr = (uint32_t)m_txBuf; + dma_cfg.memory_width = DMA_MEMORY_WIDTH_8BIT; + dma_cfg.number = 0U; + dma_cfg.priority = DMA_PRIORITY_HIGH; + dma_cfg.periph_inc = DMA_PERIPH_INCREASE_DISABLE; + dma_cfg.memory_inc = DMA_MEMORY_INCREASE_ENABLE; + dma_cfg.direction = DMA_MEMORY_TO_PERIPHERAL; + dma_init(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH, &dma_cfg); + dma_circulation_disable(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH); + dma_channel_disable(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH); + + /* RX: SPI0_DATA → m_rxBuf(替代旧 RBNE 逐字节累积)。 + * 与 BOOT SpiOta 对齐:DMA0_CH1 固定映射 SPI0_RX。 */ + dma_deinit(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH); + dma_struct_para_init(&dma_cfg); + dma_cfg.periph_addr = (uint32_t)(SPI0 + 0x0CU); + dma_cfg.periph_width = DMA_PERIPHERAL_WIDTH_8BIT; + dma_cfg.memory_addr = (uint32_t)m_rxBuf; + dma_cfg.memory_width = DMA_MEMORY_WIDTH_8BIT; + dma_cfg.number = kRxBufSize; + dma_cfg.priority = DMA_PRIORITY_HIGH; + dma_cfg.periph_inc = DMA_PERIPH_INCREASE_DISABLE; + dma_cfg.memory_inc = DMA_MEMORY_INCREASE_ENABLE; + dma_cfg.direction = DMA_PERIPHERAL_TO_MEMORY; + dma_init(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH, &dma_cfg); + dma_circulation_disable(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH); + dma_channel_disable(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH); +} + +void AppSpiCommand::configureExti() +{ + gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_15); + exti_init(EXTI_15, EXTI_INTERRUPT, EXTI_TRIG_BOTH); + exti_interrupt_enable(EXTI_15); + exti_interrupt_flag_clear(EXTI_15); + nvic_irq_enable(EXTI10_15_IRQn, 1U, 1U); +} + + + +/* ============================================================ + * 默认命令注册入口(由各 app_spi_cmd_*.cpp 提供) + * ============================================================ */ +extern void registerSpiBootRequestCommand(AppSpiCommand &bus); +extern void registerSpiOtaWaitCommand(AppSpiCommand &bus); +extern void registerSpiAppPingCommand(AppSpiCommand &bus); +extern void registerSpiCanBridgeCommand(AppSpiCommand &bus); +extern void registerSpiCanFeedbackCommand(AppSpiCommand &bus); +extern void registerSpiIdSnapshotCommand(AppSpiCommand &bus); +AppSpiCommand AppSpiCommandBus; +void AppSpiCommand::init() +{ + m_csActive = false; + m_rxLen = 0U; + m_txLen = 0U; + m_logPending = false; + m_commandCount = 0U; + m_ringPending = false; + m_ringDeadline = 0U; + memset(m_txBuf, 0xFF, sizeof(m_txBuf)); + memset(m_logBuf, 0, sizeof(m_logBuf)); + memset(m_commands, 0, sizeof(m_commands)); + + registerSpiBootRequestCommand(AppSpiCommandBus); + registerSpiOtaWaitCommand(AppSpiCommandBus); + registerSpiAppPingCommand(AppSpiCommandBus); + registerSpiCanBridgeCommand(AppSpiCommandBus); + registerSpiCanFeedbackCommand(AppSpiCommandBus); + registerSpiIdSnapshotCommand(AppSpiCommandBus); + + configureGpio(); + configureDma(); + configureSpi0(); + configureExti(); + /* 首笔 CS 到来前预装 DMA,避免 EXTI 晚于首 SCK。 + * 必须在 configureSpi0/configureDma 之后调用。 */ + armSpiDmasForNextCs(); +} + +/** @brief 在 CS 空闲期预装下一笔全双工 DMA。 + * + * 必须在首个 SCK 之前完成:EXTI CS 下降相对 ESP32 时钟经常偏晚, + * 若在 onCsAssert 里才 start DMA,整笔事务会收 0 字节 → 永不 HELLO_ACK → MISO 全 0xFF。 + * + * TX 始终按 kTxBufSize 装载:响应帧在头部,其余 0xFF。 + * 若只 DMA 实际帧长(~26B),主机打 96 时钟时 TX underrun,后续 MISO 不定。 + * + * 对齐 BOOT SpiOta::armSpiDmasForNextCs。 + */ +void AppSpiCommand::armSpiDmasForNextCs() +{ + DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) &= ~DMA_CHXCTL_CHEN; + DMA_CHCTL(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) &= ~DMA_CHXCTL_CHEN; + spi_dma_disable(SPI0, SPI_DMA_TRANSMIT); + spi_dma_disable(SPI0, SPI_DMA_RECEIVE); + drainSpiRxHw(); + + /* 预填 TX:有挂起响应用响应帧(头部)+ 0xFF 填充到满缓冲 */ + if (m_txLen > 0U && m_txLen <= kTxBufSize) { + if (m_txLen < kTxBufSize) { + memset(&m_txBuf[m_txLen], 0xFF, (size_t)(kTxBufSize - m_txLen)); + } + } else { + m_txLen = 0U; + memset(m_txBuf, 0xFF, kTxBufSize); + } + + /* RX 缓冲清零(便于诊断时区分有效字节与残留) */ + memset(m_rxBuf, 0x00, kRxBufSize); + m_rxLen = 0U; + + /* 预装 SPI_DATA,避免 DMA 启动前首拍 TBE 吐旧值 */ + spi_i2s_data_transmit(SPI0, m_txBuf[0]); + + /* 先使能 SPI DMA 请求,再开 DMA 通道,保证首字节即可搬运 */ + spi_dma_enable(SPI0, SPI_DMA_RECEIVE); + spi_dma_enable(SPI0, SPI_DMA_TRANSMIT); + startSpiRxDma(m_rxBuf, kRxBufSize); + /* TX 永远满缓冲长度,与主机 96~256B 事务匹配,避免 underrun */ + startSpiTxDma(m_txBuf, kTxBufSize); +} + +/* ============================================================ + * 命令路由 + * ============================================================ */ + +void AppSpiCommand::registerHandler(ISpiCommandHandler *handler) +{ + if (!handler || m_commandCount >= kMaxCommands) { + return; + } + m_commands[m_commandCount++] = handler; +} + +void AppSpiCommand::finishResponseAndReady() +{ + /* 有响应待发时 arm 按铃,阻止 canFeedbackPushStep 覆盖 m_txBuf; + * updateDataReady 按 m_txLen/m_ringPending 驱动 PB6。 + * 延迟日志由 poll() 无条件消费 m_logPending,无需再置任务位。 */ + if (m_txLen > 0U) { + armRingPending(); + } + updateDataReady(); +} + +void AppSpiCommand::processCommand() +{ + if (m_rxLen == 0U) { + return; + } + + /* 0xB0 已受理、正在写 OTA 配置页 / 即将复位:不再处理业务帧,避免打断 Flash。 */ + if (OtaBootRequest::isSpiFrozenForBoot() || OtaBootRequest::isRebootPending()) { + m_txLen = 0U; + updateDataReady(); + return; + } + + uint8_t frame_type = 0U; + const uint8_t *payload = nullptr; + uint16_t payload_len = 0U; + v2_header_t hdr; + + if (!spi_try_parse_frame(m_rxBuf, m_rxLen, &frame_type, &payload, &payload_len, &hdr)) { + postLogFmt("[SPI-APP] 非v2帧 0x%02X -> ERROR", m_rxBuf[0]); + uint16_t tx_len = 0U; + uint8_t err = 0xEEU; + spi_build_frame(kRspError, &err, 1U, m_txBuf, &tx_len); + m_txLen = tx_len; + finishResponseAndReady(); + return; + } + + if (CONFIG_SPI_APP_LOG_ENABLE != 0 && !isFlashRelatedFrame(frame_type)) { + postLogFmt("[SPI-APP] 收到非刷写 SPI type=0x%02X", frame_type); + } + + g_pendingAckSeq = hdr.tx_seq; + + if (frame_type == V2_TYPE_LINK_HELLO) { + g_sessionId = hdr.session_id; + g_txSeq = 0; + g_expectedRxSeq = 0; + g_v2Active = true; + s_hasCachedResp = false; + + size_t built_len = 0U; + v2_build_hello_ack_frame(g_sessionId, V2_ROLE_APP, (uint8_t)g_localCredit, + 0, hdr.tx_seq, m_txBuf, &built_len); + m_txLen = (uint16_t)built_len; + + s_lastReqSeq = hdr.tx_seq; + memcpy(s_lastRespBuf, m_txBuf, m_txLen); + s_lastRespLen = m_txLen; + s_hasCachedResp = true; + + finishResponseAndReady(); + return; + } + + if (g_v2Active) { + if (hdr.session_id != g_sessionId) { + postLogFmt("[SPI-APP] session 不匹配 0x%02X -> LINK_ERROR", + (uint8_t)(hdr.session_id & 0xFFU)); + size_t built_len = 0U; + v2_build_error_frame(g_sessionId, g_txSeq, hdr.tx_seq, + (uint8_t)g_localCredit, V2_ERR_SESSION, + (uint8_t)g_expectedRxSeq, m_txBuf, &built_len); + m_txLen = (uint16_t)built_len; + finishResponseAndReady(); + return; + } + + const uint8_t dist = v2_seq_distance(hdr.tx_seq, g_expectedRxSeq); + if (dist == 0U) { + g_expectedRxSeq = (uint8_t)(g_expectedRxSeq + 1U); + } else if (dist >= 128U) { + if (s_hasCachedResp && hdr.tx_seq == s_lastReqSeq) { + postLogFmt("[SPI-APP] 重复请求 seq=0x%02X -> 重发缓存", hdr.tx_seq); + memcpy(m_txBuf, s_lastRespBuf, s_lastRespLen); + m_txLen = s_lastRespLen; + finishResponseAndReady(); + return; + } + } else { + postLogFmt("[SPI-APP] SEQ jump rx=0x%02X", hdr.tx_seq); + g_expectedRxSeq = (uint8_t)(hdr.tx_seq + 1U); + + if (frame_type != kTypeBootReq) { + size_t built_len = 0U; + v2_build_error_frame(g_sessionId, g_txSeq, hdr.tx_seq, + (uint8_t)g_localCredit, V2_ERR_SEQ_JUMP, + (uint8_t)g_expectedRxSeq, m_txBuf, &built_len); + m_txLen = (uint16_t)built_len; + finishResponseAndReady(); + return; + } + postLog("SEQ jump but BOOT_REQ still handled"); + } + } + + uint16_t tx_len = 0U; + bool handled = false; + + SpiCommandContext ctx{}; + ctx.frame_type = frame_type; + ctx.payload = payload; + ctx.payload_len = payload_len; + ctx.tx_buf = m_txBuf; + ctx.tx_buf_size = kTxBufSize; + ctx.tx_len = &tx_len; + + for (uint8_t i = 0U; i < m_commandCount; ++i) { + ISpiCommandHandler *handler = m_commands[i]; + if (handler && handler->canHandle(frame_type)) { + handler->handle(AppSpiCommandBus, ctx); + handled = true; + break; + } + } + + if (!handled) { + postLogFmt("[SPI-APP] 未知 type=0x%02X -> ERROR", frame_type); + uint8_t err = 0xEEU; + spi_build_frame(kRspError, &err, 1U, m_txBuf, &tx_len); + } + + if (tx_len == 0U) { + uint8_t err = 0xEEU; + spi_build_frame(kRspError, &err, 1U, m_txBuf, &tx_len); + } + + m_txLen = tx_len; + + if (g_v2Active) { + s_lastReqSeq = hdr.tx_seq; + memcpy(s_lastRespBuf, m_txBuf, m_txLen); + s_lastRespLen = m_txLen; + s_hasCachedResp = true; + g_txSeq = (uint8_t)(g_txSeq + 1U); + } + + finishResponseAndReady(); +} + + +/* ============================================================ + * GD32 主动按铃:设 m_ringPending + updateDataReady() 拉低 PB6 请求 ESP32 来读 + * ============================================================ */ + +void AppSpiCommand::armRingPending() +{ + m_ringPending = true; + m_ringDeadline = SysTickTimer::getMs() + kRingTimeoutMs; +} + +void AppSpiCommand::cancelRing() +{ + m_ringPending = false; + /* 清除待发响应,防止超时后残留帧在下一次 CS assert 时被错误输出 */ + m_txLen = 0U; + /* 释放 SPIready(高阻),避免永久占用线。 + * cancelRing 已清 m_txLen 和 m_ringPending,updateDataReady() 会释放为高。 */ + updateDataReady(); +} + +bool AppSpiCommand::requestEsp32Read(uint8_t frame_type, const uint8_t *payload, uint16_t payload_len) +{ + /* 若上一次按铃尚未被 ESP32 读走,拒绝重复按铃 */ + if (m_ringPending) { + return false; + } + + /* 构建 v2 EVENT 帧到 m_txBuf(ESP32 下次读事务时通过 DMA 输出到 MISO)。 + * 主动按铃帧使用 V2_FLAG_EVENT,session/seq 从 v2 状态全局变量取; + * session 未建立时 session_id 填 0。 */ + const uint32_t sess = g_v2Active ? g_sessionId : 0U; + /* L5:单缓冲 — 装载前 credit 报 1,装载后 updateDataReady 置 0 */ + g_localCredit = 1U; + size_t built_len = 0U; + if (!v2_build_frame(frame_type, V2_FLAG_EVENT, sess, + g_txSeq, 0U, (uint8_t)g_localCredit, + payload, payload_len, m_txBuf, &built_len)) { + return false; + } + /* EVENT 帧占用一个发送序号槽位 */ + if (g_v2Active) { + g_txSeq = (uint8_t)(g_txSeq + 1U); + } + + m_txLen = (uint16_t)built_len; + armRingPending(); + + /* v2 DATA_READY: 已设 m_ringPending=true 且 m_txLen>0, + * updateDataReady() 据此拉低 PB6,并置 g_localCredit=0。 */ + updateDataReady(); + return true; +} + +/* ============================================================ + * 主循环轮询:打印延迟日志 + 按铃超时看护 + * ============================================================ */ + +void AppSpiCommand::poll() +{ + /* 打印 ISR 推迟的日志 */ + char log_copy[kLogBufSize] = {}; + bool has_log = false; + + uint32_t save = RT_CRITICAL_SAVE(); + if (m_logPending) { + memcpy(log_copy, m_logBuf, sizeof(log_copy)); + m_logPending = false; + has_log = true; + } + RT_CRITICAL_RESTORE(save); + + if (has_log) { + printf("%s\r\n", log_copy); + } + + /* 按铃超时看护:ESP32 未在 kRingTimeoutMs 内来读则放弃本次按铃。 + * 防止 ESP32 不响应时永久占用 SPIready 线导致双方死锁。 */ + if (m_ringPending) { + uint32_t save2 = RT_CRITICAL_SAVE(); + if (m_ringPending) { + if ((int32_t)(SysTickTimer::getMs() - m_ringDeadline) >= 0) { + printf("[SPI-APP] 按铃超时取消 (ESP32 未响应, 释放 SPIready 重试)\r\n"); + cancelRing(); + } + } + RT_CRITICAL_RESTORE(save2); + } +} + +/* ============================================================ + * CS 边沿处理 + * ============================================================ */ + +void AppSpiCommand::onCsAssert() +{ + if (m_csActive) { + return; + } + m_csActive = true; + /* CS 下降:DMA 已在空闲期由 armSpiDmasForNextCs() 预装,此处只标记忙碌。 + * v2 DATA_READY: CS 本身即互斥,PB6 不再表示「忙碌」,不在 CS 下降沿拉低。 + * 切勿在这里 stop/restart DMA,否则赶不上 ESP32 已发出的 SCK(对齐 BOOT)。 */ +} + +void AppSpiCommand::onCsRelease() +{ + if (!m_csActive) { + /* EXTI 丢失下降沿时:仍尝试收尾并 re-arm,避免永久失步(对齐 BOOT) */ + m_rxLen = stopSpiDmas(kRxBufSize); + drainSpiRxHw(); + if (m_txLen > 0U) { + m_txLen = 0U; + memset(m_txBuf, 0xFF, sizeof(m_txBuf)); + } + if (m_rxLen >= V2_OVERHEAD) { + processCommand(); + } + armSpiDmasForNextCs(); + updateDataReady(); + return; + } + m_csActive = false; + + /* 停 DMA 并取本笔 RX 长度(先读 CHCNT 再 disable,否则计数器被清) */ + m_rxLen = stopSpiDmas(kRxBufSize); + drainSpiRxHw(); + + /* 若本次 CS 事务是 ESP32 响应 GD32 按铃来读请求帧, + * 则 GD32 的请求帧已通过 DMA 输出到 MISO,取消按铃状态。 + * + * 但需区分:ESP32 发的是 dummy 读(按铃响应)还是 v2 协议帧(如 PING/OTA/0xB0)。 + * 若 GD32 正在按铃时 ESP32 发协议帧,会因 m_ringPending=true 被误吞, + * 导致命令无法处理。检测到 v2 SOF 时取消按铃并走正常命令处理。 */ + if (m_ringPending) { + /* 判断本次收到的数据是否为 v2 协议帧(扫描 SOF0 SOF1)。 + * 取代旧 containsSyncSof,直接用 V2_SOF0/V2_SOF1。 + * 0xB0 进 BOOT 现已走标准 v2 帧 handler,不再单独检测裸字节。 */ + bool is_v2_frame = false; + if (m_rxLen >= 2U) { + const uint16_t last = (m_rxLen < 16U) ? (uint16_t)(m_rxLen - 1U) : 16U; + for (uint16_t i = 0U; i < last; ++i) { + if (m_rxBuf[i] == V2_SOF0 && m_rxBuf[i + 1U] == V2_SOF1) { + is_v2_frame = true; + break; + } + } + } + if (is_v2_frame) { + /* ESP32 发的是 v2 协议帧(如 PING/OTA/0xB0 等),不是按铃响应。 + * 取消按铃,清空 TX 让 processCommand() 重新装载对应应答。 + * processCommand() 末尾由 updateDataReady() 依据新 m_txLen 驱动 PB6。 */ + m_ringPending = false; + m_txLen = 0U; + memset(m_txBuf, 0xFF, sizeof(m_txBuf)); + /* 继续走 shouldIgnoreTransaction + processCommand(),不 return */ + } else { + /* 正常按铃响应:请求帧已被读走,取消按铃状态 */ + m_ringPending = false; + /* 请求帧已被读走,清空 TX 防止下次事务重复输出 */ + m_txLen = 0U; + memset(m_txBuf, 0xFF, sizeof(m_txBuf)); + /* 重新预装下一笔 DMA,保证下一 CS 首字节即可收发 */ + armSpiDmasForNextCs(); + updateDataReady(); + return; + } + } + + if (shouldIgnoreTransaction(m_rxBuf, m_rxLen)) { + m_rxLen = 0U; + /* v2 DATA_READY: 无待处理帧时 updateDataReady() 释放 PB6。 */ + armSpiDmasForNextCs(); + updateDataReady(); + return; + } + + m_txLen = 0U; + memset(m_txBuf, 0xFF, sizeof(m_txBuf)); + + /* 命令处理在 EXTI ISR 内同步完成(对齐 BOOT 的 isrProcessFrame 模式), + * 消除主循环延迟。处理完成后 processCommand() 末尾再 re-arm DMA + 释放 SPIready。 */ + processCommand(); + + /* 处理完立即预装下一笔,保证下一 CS 首字节即可收发 */ + armSpiDmasForNextCs(); + updateDataReady(); +} + +/* ============================================================ + * SPI0 / EXTI 中断处理 + * ============================================================ */ + +void AppSpiCommand::onInterrupt() +{ + /* RX 已改走 DMA0_CH1:RBNE 逐字节中断已禁用,本 ISR 只保留错误标志清理。 + * CS 边沿处理全部在 onCsExtiInterrupt 的 EXTI15 ISR 里完成。 */ + if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RXORERR) != RESET) { + /* RXORERR 理论上不应再出现(RX 走 DMA),但兜底清理避免标志卡死。 + * 读 SPI_DATA + SPI_STAT 清除 OVR 标志。 */ + (void)spi_i2s_data_receive(SPI0); + (void)SPI_STAT(SPI0); + } + if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_FERR) != RESET) { + spi_i2s_format_error_clear(SPI0, SPI_FLAG_FERR); + } +} + +void AppSpiCommand::onCsExtiInterrupt() +{ + if (exti_interrupt_flag_get(EXTI_15) != RESET) { + exti_interrupt_flag_clear(EXTI_15); + if (gpio_input_bit_get(GPIOA, GPIO_PIN_15) == RESET) { + onCsAssert(); + } else { + onCsRelease(); + } + } +} + +extern "C" void SPI0_IRQHandler(void) +{ + AppSpiCommandBus.onInterrupt(); +} + +extern "C" void EXTI10_15_IRQHandler(void) +{ + AppSpiCommandBus.onCsExtiInterrupt(); +} diff --git a/GD32E508_DCU_APP/device/spi/app_spi_command.h b/GD32E508_DCU_APP/device/spi/app_spi_command.h index 306eb84..3930b8b 100644 --- a/GD32E508_DCU_APP/device/spi/app_spi_command.h +++ b/GD32E508_DCU_APP/device/spi/app_spi_command.h @@ -2,6 +2,7 @@ #define DEVICE_APP_SPI_COMMAND_H #include +#include "sys/config.h" class ISpiCommandHandler; @@ -88,21 +89,20 @@ public: * 0xB0 进 BOOT 等路径需公开调用,避免 0xC1 残留盖住 ACK。 */ void cancelRing(); - static constexpr uint16_t kRxBufSize = 256U; - static constexpr uint16_t kTxBufSize = 256U; - static constexpr uint16_t kLogBufSize = 256U; + static constexpr uint16_t kRxBufSize = CONFIG_SPI_RX_BUF_SIZE; + static constexpr uint16_t kTxBufSize = CONFIG_SPI_TX_BUF_SIZE; + static constexpr uint16_t kLogBufSize = CONFIG_SPI_LOG_BUF_SIZE; private: - static constexpr uint8_t kMaxCommands = 8U; - /** GD32 主动按铃超时(ms):ESP32 未在该时间内来读则放弃。 */ - static constexpr uint32_t kRingTimeoutMs = 50U; + static constexpr uint8_t kMaxCommands = CONFIG_SPI_MAX_COMMANDS; + /** GD32 主动按铃超时(ms):见 CONFIG_SPI_RING_TIMEOUT_MS。 */ + static constexpr uint32_t kRingTimeoutMs = CONFIG_SPI_RING_TIMEOUT_MS; volatile bool m_csActive = false; /** 本笔 CS 实际收到的字节数(CS 上升沿时由 RX DMA CHCNT 反算得到)。 */ volatile uint16_t m_rxLen = 0U; volatile uint16_t m_txLen = 0U; volatile bool m_logPending = false; - bool m_txActiveThisCs = false; /** RX DMA 接收缓冲(替代旧 RBNE 逐字节累积的 m_rxBuf)。 * CS 上升沿时 m_rxLen 为本笔实际收到的字节数,本缓冲前 m_rxLen 字节有效。 */ uint8_t m_rxBuf[kRxBufSize] = {}; @@ -124,8 +124,10 @@ private: void onCsRelease(); void processCommand(); - /** @brief 设置 m_ringPending=true 并启动超时看护截止时刻。 - * 统一 7 处「m_ringPending=true; m_ringDeadline=now+timeout;」赋值模式。 */ + /** @brief 响应装载完成后的统一收尾:按铃 + updateDataReady。 */ + void finishResponseAndReady(); + + /** @brief 设置 m_ringPending=true 并启动超时看护截止时刻。 */ void armRingPending(); /** @brief 在 CS 空闲期预装下一笔全双工 DMA(TX + RX)。 diff --git a/GD32E508_DCU_APP/device/temp_sensor.cpp b/GD32E508_DCU_APP/device/temp_sensor.cpp deleted file mode 100644 index d653427..0000000 --- a/GD32E508_DCU_APP/device/temp_sensor.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @file temp_sensor.cpp - * @brief 温度传感模块实现。 - */ - -#include "device/adc.h" -#include "device/temp_sensor.h" - -/** - * @brief 初始化温度传感采样依赖模块。 - * @param[in] 无。 - * @param[out] 无。 - * @retval 无返回值。 - * @note 当前内部直接复用 `Adc::init()` 完成温度与参考电压采样链路初始化。 - */ -void TempSensor::init() -{ - Adc::init(); -} \ No newline at end of file diff --git a/GD32E508_DCU_APP/device/temp_sensor.h b/GD32E508_DCU_APP/device/temp_sensor.h deleted file mode 100644 index 1e82ee0..0000000 --- a/GD32E508_DCU_APP/device/temp_sensor.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef DEVICE_TEMP_SENSOR_H -#define DEVICE_TEMP_SENSOR_H - -#include "device/adc.h" - -/** - * @brief 温度采样模块。 - * @note 当前作为 ADC 模块的轻量门面,复用 Adc::Sample 作为结果类型。 - */ -class TempSensor { -public: - /** - * @brief 初始化温度采样依赖资源。 - * @param[in] 无。 - * @param[out] 无。 - * @retval 无返回值。 - * @note 当前通过调用 Adc::init() 完成内部通道准备。 - */ - static void init(); -}; - -#endif /* DEVICE_TEMP_SENSOR_H */ \ No newline at end of file diff --git a/GD32E508_DCU_APP/main.h b/GD32E508_DCU_APP/main.h index ef2c0b5..8d4f8c7 100644 --- a/GD32E508_DCU_APP/main.h +++ b/GD32E508_DCU_APP/main.h @@ -17,10 +17,8 @@ enum system_task_t { TASK_ADC_PROCESS, /**< 对 ADC 原始值进行电压与温度计算任务。 */ TASK_CAN_RX_PARSE, /**< 解析并消费已接收 CANFD 帧任务。 */ TASK_CAN_TX_SEND, /**< CANFD 周期发送任务,每 1ms 每通道发送一帧。 */ - TASK_STATUS_LED, /**< 状态灯维护任务。 */ TASK_HEARTBEAT, /**< 系统心跳维护任务。 */ TASK_DIAGNOSTIC_PRINT, /**< 诊断信息打印任务。 */ - TASK_SPI_LOG, /**< SPI 延迟日志打印任务。 */ TASK_ETHERCAT_PROCESS, /**< EtherCAT 周期/事件处理任务(非实时应用层 PDO 映射)。 */ TASK_COUNT /**< 任务总数,用于边界或统计场景。 */ }; @@ -45,8 +43,6 @@ struct task_scheduler_t { volatile uint16_t div_can_rx; /** @brief 调度分频计数:CAN 周期发送(1ms)。 */ volatile uint16_t div_can_tx; - /** @brief 调度分频计数:状态灯(100ms)。 */ - volatile uint16_t div_status_led; /** @brief 调度分频计数:心跳(1s)。 */ volatile uint16_t div_heartbeat; /** @brief 调度分频计数:APP 状态/诊断打印(5s)。 */ diff --git a/GD32E508_DCU_APP/sys/config.h b/GD32E508_DCU_APP/sys/config.h index 4732b91..dc6483a 100644 --- a/GD32E508_DCU_APP/sys/config.h +++ b/GD32E508_DCU_APP/sys/config.h @@ -7,11 +7,9 @@ #define SYS_CONFIG_H #include +#include #include "gd32e50x.h" -/** @brief 应用启动横幅字符串。 */ -constexpr const char *APP_BANNER_TEXT = "GD32E508 DCU task scheduler start"; - /* ============================================================ * APP 固件版本号 * ============================================================ */ @@ -42,18 +40,9 @@ constexpr float CONFIG_ADC_TEMP_V25 = 1.43f; constexpr float CONFIG_ADC_TEMP_AVG_SLOPE = 0.0043f; /** @brief ADC 上电后稳定等待时间,单位 ms。 */ constexpr uint32_t CONFIG_ADC_STABILIZE_DELAY_MS = 2U; -/** @brief ADC 规则通道采样时间配置。 */ -constexpr uint32_t CONFIG_ADC_SAMPLE_TIME = ADC_SAMPLETIME_239POINT5; -/** @brief ADC 空输入保护时默认 VDDA,单位 V。 */ -constexpr float CONFIG_ADC_DEFAULT_VDDA = 3.3f; - /** @brief ADC 中值滤波窗口长度(最终样本数为该值 + 1)。 */ constexpr uint32_t CONFIG_ADC_FILTER_SIZE = 6U; -/** @brief CAN 外设实例。 */ -constexpr uint32_t CONFIG_CAN_INSTANCE = CAN0; -/** @brief CAN 外设时钟使能枚举。 */ -constexpr rcu_periph_enum CONFIG_CAN_RCU = RCU_CAN0; /** @brief CAN 状态报文标准帧 ID。 */ constexpr uint32_t CONFIG_CAN_STATUS_ID = 0x321U; /** @brief ESP32 请求 DCU_APP 进入 Bootloader OTA 窗口的扩展帧 ID(备用 CAN 入口)。 */ @@ -67,12 +56,22 @@ constexpr uint8_t CONFIG_CAN_OTA_BOOT_MAGIC3 = 0x21U; constexpr uint8_t CONFIG_SPI_APP_OTA_BOOT_CMD = 0xB0U; /** @brief SPI 进入 Bootloader OTA 命令确认状态:成功。 */ constexpr uint8_t CONFIG_SPI_APP_OTA_BOOT_ACK_OK = 0xB5U; -/** @brief SPI 进入 Bootloader OTA 命令确认状态:失败。 */ -constexpr uint8_t CONFIG_SPI_APP_OTA_BOOT_ACK_ERROR = 0xE2U; -/** @brief SPI Ready 握手输出端口(PB6 复用为 SPI Ready)。 */ +/** @brief SPI DATA_READY 输出端口(PB6 → ESP32;v2:低=有待读,高=空闲)。 */ constexpr uint32_t CONFIG_SPI_READY_PORT = GPIOB; -/** @brief SPI Ready 握手输出引脚。 */ +/** @brief SPI DATA_READY 输出引脚。 */ constexpr uint32_t CONFIG_SPI_READY_PIN = GPIO_PIN_6; +/** @brief APP SPI DMA/软件缓冲(字节)。业务 payload 上限 = 本值 − V2_OVERHEAD(20)。 */ +constexpr uint16_t CONFIG_SPI_TX_BUF_SIZE = 256U; +constexpr uint16_t CONFIG_SPI_RX_BUF_SIZE = 256U; +constexpr uint16_t CONFIG_SPI_LOG_BUF_SIZE = 256U; +/** @brief 主动按铃超时:ESP32 未在该时间内来读则 cancelRing(ms)。 */ +constexpr uint32_t CONFIG_SPI_RING_TIMEOUT_MS = 50U; +/** @brief SPI 命令 handler 注册表容量。 */ +constexpr uint8_t CONFIG_SPI_MAX_COMMANDS = 8U; +/** @brief 每通道不重复 ID 快照表最大条目数(与 ESP32 CANFD_ID_SNAPSHOT_MAX 对齐)。 */ +constexpr uint8_t CONFIG_CANFD_ID_SNAPSHOT_MAX = 16U; +/** @brief 快照条目最大数据字节数(CAN FD DLC 上限)。 */ +constexpr uint8_t CONFIG_CANFD_ID_SNAPSHOT_DATA_MAX = 64U; /** @brief ESP32 硬件复位控制端口(PC9 接 ESP32 EN/RESET)。 */ constexpr uint32_t CONFIG_ESP32_RESET_PORT = GPIOC; @@ -118,6 +117,8 @@ constexpr uint32_t CONFIG_CAN_DATA_BS2 = CAN_BT_BS2_1TQ; #define CONFIG_CAN_STARTUP_BUS_TEST_ENABLE 0 /** @brief 启动时切换控制器到内部回环执行自检;正常固件默认关闭。 */ #define CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE 0 +/** @brief CAN 启动详细诊断(时钟/init 汇总/ERR 快照/寄存器 dump);默认关闭。 */ +#define CONFIG_CAN_STARTUP_DIAG_VERBOSE 0 /** @brief CAN0 通道使能开关:true=启用初始化/收发/自测,false=完全禁用。 */ constexpr bool CONFIG_CAN_CH0_ENABLE = true; @@ -159,8 +160,6 @@ constexpr uint32_t CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS = 10U; constexpr uint32_t CONFIG_TASK_PERIOD_CAN_RX_TICKS = 100U; /** @brief 调度基准节拍:CAN 周期发送任务周期,10 tick = 1ms。 */ constexpr uint32_t CONFIG_TASK_PERIOD_CAN_TX_TICKS = 10U; -/** @brief 调度基准节拍:状态灯任务周期,1000 tick = 100ms。 */ -constexpr uint32_t CONFIG_TASK_PERIOD_STATUS_LED_TICKS = 1000U; /** @brief 调度基准节拍:心跳周期,单位 100us tick。 */ constexpr uint32_t CONFIG_TASK_PERIOD_HEARTBEAT_TICKS = 10000U; /** @brief 调度基准节拍:APP 状态/诊断打印周期,50000 tick = 5s。 */ @@ -221,15 +220,16 @@ constexpr uint32_t CONFIG_IRQ_PREEMPT_SPI_USART = 3U; constexpr uint32_t CONFIG_ETHERCAT_ISR_MAX_US_THRESHOLD = 5U; constexpr uint32_t CONFIG_ESC_PDI_INPUT_OFFSET = 0x1000U; constexpr uint32_t CONFIG_ESC_PDI_OUTPUT_OFFSET = 0x1200U; +/** @brief 默认 PDO 缓冲大小(字节),应用/ISR 三缓冲共用。 */ +constexpr size_t CONFIG_ETHERCAT_PDO_SIZE = 32U; /* ---------- EXMC 引脚配置(GD32E508 EXMC 标准映射) ---------- */ constexpr uint32_t CONFIG_ESC_EXMC_BASE_ADDR = 0x60000000U; -/** @note EXMC_BANK0_NORSRAM_REGION1 条件编译,保留 #define。 */ -#ifdef EXMC_BANK0_NORSRAM_REGION1 -#define CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1 EXMC_BANK0_NORSRAM_REGION1 -#else -#define CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1 0U +/* Region1 = NE1 @ 0x60000000;数值与 gd32e50x_exmc.h 中 EXMC_BANK0_NORSRAM_REGION1 一致 */ +#ifndef EXMC_BANK0_NORSRAM_REGION1 +#define EXMC_BANK0_NORSRAM_REGION1 ((uint32_t)0x00000001U) #endif +#define CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1 EXMC_BANK0_NORSRAM_REGION1 /* ---------- ESC 中断引脚(PB0/PB1/PB2,三个独立 EXTI 向量) ---------- */ /** @note 保留 PA0 给未来 STANDBY 模式唤醒(WKUP_PIN0)。 */ diff --git a/GD32E508_DCU_APP/sys/initial.cpp b/GD32E508_DCU_APP/sys/initial.cpp index 6f5462d..1488b5a 100644 --- a/GD32E508_DCU_APP/sys/initial.cpp +++ b/GD32E508_DCU_APP/sys/initial.cpp @@ -6,7 +6,6 @@ #include "sys/initial.h" #include "device/canfd.h" -#include "device/temp_sensor.h" #include "device/usart0.h" #include "device/timer.h" #include "device/adc.h" @@ -41,8 +40,8 @@ void AppInitializer::init() printf("[APP] init: USART0...\r\n"); Usart0::configurePins(); Usart0::init(); - printf("[APP] init: TempSensor...\r\n"); - TempSensor::init(); + printf("[APP] init: ADC...\r\n"); + Adc::init(); printf("[APP] init: CANFD...\r\n"); CanFd::init(); printf("[APP] init: OtaBootRequest...\r\n"); diff --git a/GD32E508_DCU_APP/sys/task_manager.cpp b/GD32E508_DCU_APP/sys/task_manager.cpp index 2daf93c..5b406b5 100644 --- a/GD32E508_DCU_APP/sys/task_manager.cpp +++ b/GD32E508_DCU_APP/sys/task_manager.cpp @@ -31,7 +31,6 @@ void TaskManager::init() g_scheduler.div_temp_sample = 0U; g_scheduler.div_can_rx = 0U; g_scheduler.div_can_tx = 0U; - g_scheduler.div_status_led = 0U; g_scheduler.div_heartbeat = 0U; g_scheduler.div_diag_print = 0U; } @@ -100,12 +99,6 @@ void TaskManager::tick100us() set(1UL << TASK_CAN_TX_SEND); } - if(++g_scheduler.div_status_led >= CONFIG_TASK_PERIOD_STATUS_LED_TICKS) { - g_scheduler.div_status_led = 0U; - g_scheduler.tick_100ms++; - set(1UL << TASK_STATUS_LED); - } - if(++g_scheduler.div_heartbeat >= CONFIG_TASK_PERIOD_HEARTBEAT_TICKS) { g_scheduler.div_heartbeat = 0U; g_scheduler.tick_1s++; diff --git a/GD32E508_DCU_APP/sys/task_manager.h b/GD32E508_DCU_APP/sys/task_manager.h index b91c553..46478a1 100644 --- a/GD32E508_DCU_APP/sys/task_manager.h +++ b/GD32E508_DCU_APP/sys/task_manager.h @@ -24,7 +24,7 @@ public: * @param[in] 无。 * @param[out] 无。 * @retval 无返回值。 - * @note 由 TIMER2 ISR 调用;按周期分档置位 ADC/CAN/LED/心跳/诊断等任务标志。 + * @note 由 TIMER2 ISR 调用;按周期分档置位 ADC/CAN/心跳/诊断等任务标志。 */ static void tick100us(); diff --git a/GD32E508_DCU_APP/sys/task_runner.cpp b/GD32E508_DCU_APP/sys/task_runner.cpp index bbc6993..21cd240 100644 --- a/GD32E508_DCU_APP/sys/task_runner.cpp +++ b/GD32E508_DCU_APP/sys/task_runner.cpp @@ -8,7 +8,6 @@ #include "device/watchdog.h" #include "device/spi/app_spi_command.h" #include "device/canfd.h" -#include "device/temp_sensor.h" #include "sys/config.h" #include "sys/ota_boot_request.h" #include "sys/task_manager.h" @@ -208,15 +207,6 @@ void TaskRunner::taskCanRxParse() } } -/** - * @brief 执行状态灯维护任务。 - * @param[in] 无。 - * @param[out] 无。 - * @retval 无返回值。 - * @note 当前仅保留任务入口。 - */ -void TaskRunner::taskStatusLed() { /* 预留 */ } - /** * @brief 执行系统心跳维护任务。 * @param[in] 无。 @@ -254,7 +244,7 @@ void TaskRunner::taskDiagnosticPrint() static_cast(vin_adc_mV / 1000U), static_cast(vin_adc_mV % 1000U)); #else - printf("[APP] uptime=%lus valid=%u, temp=%ld.%02ld C, vdda=%lu.%03lu V, vin_adc=%lu.%03lu V, rx_ch=%u, rx_id=0x%08lX, rx_len=%u, tx_ch=%u, can_tx=%lu, can_rx=%lu, sw_drop=%lu, hw_full=%lu, hw_over=%lu, tx_drop=%lu, tx_abort=%lu, can_err=%lu\r\n", + printf("[APP] uptime=%lus valid=%u, temp=%ld.%02ld C, vdda=%lu.%03lu V, vin_adc=%lu.%03lu V, rx_ch=%u, rx_id=0x%08lX, rx_len=%u, tx_ch=%u, can_tx=%lu, can_rx=%lu, sw_drop=%lu, hw_full=%lu, hw_over=%lu, tx_abort=%lu, can_err=%lu\r\n", (unsigned long)g_app_uptime_sec, static_cast(s.valid), static_cast(temp_x100 / 100), @@ -272,7 +262,6 @@ void TaskRunner::taskDiagnosticPrint() static_cast(diag->rx_drop_count), static_cast(diag->rx_fifo_full_count), static_cast(diag->rx_fifo_overfull_count), - static_cast(diag->tx_drop_count), static_cast(diag->tx_abort_count), static_cast(diag->error_count)); #endif @@ -340,9 +329,6 @@ void TaskRunner::runPending() Watchdog::feed(); /* poll() 含按铃超时看门狗(cancelRing),必须每轮无条件执行。 - * 否则日志关闭时(CONFIG_SPI_APP_LOG_ENABLE=0) m_logPending 永远为 false, - * TASK_SPI_LOG 永不置位,poll() 永不执行,m_ringPending 卡死导致 - * canFeedbackPushStep() 永久 return -> SPI 反馈链路死锁。 * 放在 canFeedbackPushStep() 前确保卡死的按铃先被清掉再推送下一帧。 */ AppSpiCommandBus.poll(); @@ -375,9 +361,7 @@ void TaskRunner::runPending() if(pending & (1UL << TASK_ADC_PROCESS)) taskAdcProcess(); if(pending & (1UL << TASK_CAN_RX_PARSE)) taskCanRxParse(); if(pending & (1UL << TASK_CAN_TX_SEND)) taskCanTxSend(); - if(pending & (1UL << TASK_STATUS_LED)) taskStatusLed(); if(pending & (1UL << TASK_HEARTBEAT)) taskHeartbeat(); if(pending & (1UL << TASK_DIAGNOSTIC_PRINT)) taskDiagnosticPrint(); - /* TASK_SPI_LOG 的 poll() 已移至 runPending() 开头无条件调用(含按铃看门狗) */ if(pending & (1UL << TASK_ETHERCAT_PROCESS)) taskEthercatProcess(); } diff --git a/GD32E508_DCU_APP/sys/task_runner.h b/GD32E508_DCU_APP/sys/task_runner.h index 4169dbe..f787dc1 100644 --- a/GD32E508_DCU_APP/sys/task_runner.h +++ b/GD32E508_DCU_APP/sys/task_runner.h @@ -39,9 +39,7 @@ private: static void taskCanRxParse(); /** @brief CAN 周期发送任务;每 1ms 每通道发送一帧测试报文。 */ static void taskCanTxSend(); - /** @brief 状态灯维护任务(当前预留)。 */ - static void taskStatusLed(); - /** @brief 心跳维护任务(当前预留)。 */ + /** @brief 心跳维护任务。 */ static void taskHeartbeat(); /** @brief 诊断打印任务。 */ static void taskDiagnosticPrint(); diff --git a/communication_ids.html b/communication_ids.html deleted file mode 100644 index 79a6adc..0000000 --- a/communication_ids.html +++ /dev/null @@ -1,338 +0,0 @@ - - - - - DCU_APP SPI 通信 ID 参考 - - - - -
-

DCU_APP SPI 通信 ID(按注册命令展开,三行直观图)

-

按 device/spi/app_spi_command.cpp 注册顺序组织;每个命令均可展开查看 payload,并用“序号 / 组合 / 说明”三行显示。

-
⚠ 单帧 payload 最大 123 字节(kTxBufSize=128 - 协议开销 5 字节),超过将被 AppSpiCommand::processCommand() 拒收。
- -
- -
-

Boot Request

-
注册函数:registerSpiBootRequestCommand
-
- 展开查看 ID / payload / 功能 -
- - - - -
命令/响应符号值
命令帧类型kTypeBootReq0xB0
成功响应kRspAck0x80
-
payload 直观图
-
-
-
req
-
—
-
无可扩展 payload 字段
-
-
-
ack
-
0x80
-
成功应答
-
-
-
功能:请求进入 Boot 状态,回 ACK。
-
-
-
- -
-

OTA Wait

-
注册函数:registerSpiOtaWaitCommand
-
- 展开查看 ID / payload / 功能 -
- - - - - - -
命令符号值
OTA 请求kTypeOtaReq0x01
OTA 数据kTypeOtaData0x02
OTA 取消kTypeOtaCancel0x03
响应kRspWait0x81
- -
response payload 直观图
-
-
-
[0]
-
0x01
-
hint 固定值
-
-
- -
功能:OTA 占位/流控,占位/流控。
-
-
-
- -
-

App Ping

-
注册函数:registerSpiAppPingCommand
-
- 展开查看 ID / payload / 功能 -
- - - - -
命令/响应符号值
命令帧类型kTypeAppPing0xA1
响应kRspAck0x80
- -
response payload 直观图
-
-
-
[0]
0xA1
magic
-
[1]
0x55
APP version tag
-
[2..5]
APP_VER
MAJOR/MINOR/PATCH/BUILD
-
[6]
0xAA
BOOT version tag
-
[7..10]
BOOT_VER
Boot版本
-
[11..14]
uptime
运行秒数
-
-
- -
功能:读取版本与 uptime。
-
-
-
- -
-

CAN Bridge

-
注册函数:registerSpiCanBridgeCommand
-
- 展开查看 ID / payload / 功能 -
- - - - - -
命令/响应符号值
命令帧类型kTypeCanSend0xC0
成功响应kRspAck0x80
错误响应kRspError0x82
- -
request payload 直观图
-
-
-
[0]
can_ch
通道 0/1/2
-
[1..4]
can_id
CAN ID(32bit)
-
[5..N]
can_data
数据区
-
-
- -
response payload 直观图
-
-
-
ack
0x00
OK
-
err
E0/E1/E2
短帧/通道/忙
-
-
- -
错误码:E0=payload 不足,E1=通道非法,E2=发送忙/失败。
-
功能:ESP32 透传 CANFD 发送帧,校验后发送。
-
-
-
- -
-

CAN Feedback (ID Snapshot) / Set Mode

-
注册函数:registerSpiCanFeedbackCommand
-
- 展开查看 ID / payload / 功能 -
- - - - - - -
命令符号值
反馈查询(分页)kTypeCanFeedback0xC1
模式切换kTypeCanSetMode0xC2
成功响应kRspAck0x80
错误响应kRspError0x82
- -
0xC1 request payload 直观图(分页请求)
-
-
-
[0]
channel
通道 0/1/2
-
[1]
page
页号 0 或 1
-
-
- -
0xC1 response payload 直观图(统计头 + ID 快照条目)
-
应答变长,最大 123 字节。固定头 25B + 条目逐条拼接。
-
-
固定头(25 字节)
-
-
[0]
channel
回显通道 0/1/2
-
[1]
page
回显页号
-
[2]
total_count
ID 条目总数(0~16)
-
[3]
entry_count
本页条目数
-
[4]
mode
当前接收模式
-
-
-
[5..8]
snapshot_seq
快照序号(LE)
-
[9..12]
rx_count
通道累计接收(LE)
-
[13..16]
tx_count
通道累计发送(LE)
-
[17..20]
rx_drop_count
通道丢帧(LE)
-
[21..24]
error_count
通道错误(LE)
-
-
条目区(从 [25] 开始,逐条拼接,变长)
-
-
[+0..+3]
id
CAN 报文 ID(LE)
-
[+4]
dlc
数据长度 1~64
-
[+5]
flags
bit0=FD bit1=BRS bit2=ESI
-
[+6..+7]
reserved
0x00
-
[+8..+N]
data
报文数据(dlc 字节)
-
-
- -
- 条目大小 = 8 + dlc 字节。标准 CAN (dlc=8):14B/条,每页最多 8 条。CANFD (dlc=64):72B/条,每页最多 1 条。
- total_count = 实际不重复 ID 数(0~16,不满 16 就是实际值)。total_count ≤ 8 时 page=1 的 entry_count=0。
- ESP32 每轮:每通道发 page=0 和 page=1 两次请求(共 6 次 SPI/s),拉完整张表。 -
- -
0xC2 request payload 直观图
-
-
-
[0]
mode
0=全收 1=过滤
-
-
- -
0xC2 response payload 直观图
-
-
-
[0]
actual
生效模式
-
-
-
错误码:E0=payload 不足,E1=参数非法(含非法通道),E3=页号非法。
-
注:E2=快照不可用 仅用于 0xC3,不在 0xC1/0xC2 中返回。
-
-
-
- -
-

ID Snapshot(仅 ID 快照表)

-
注册函数:registerSpiIdSnapshotCommand
-
- 展开查看 ID / payload / 功能 -
- - - - - -
命令/响应符号值
命令帧类型kTypeIdSnapshot0xC3
成功响应kRspAck0x80
错误响应kRspError0x82
- -
0xC3 request payload 直观图(分页请求)
-
-
-
[0]
channel
通道 0/1/2
-
[1]
page
页号 0 或 1
-
-
- -
0xC3 response payload 直观图(轻量快照头 + ID 条目)
-
应答变长,最大 123 字节。固定头 4B + 条目逐条拼接。
-
-
固定头(4 字节)
-
-
[0]
channel
回显通道 0/1/2
-
[1]
page
回显页号
-
[2]
total_count
ID 条目总数(0~16)
-
[3]
entry_count
本页条目数
-
-
条目区(从 [4] 开始,逐条拼接,变长)
-
-
[+0..+3]
id
CAN 报文 ID(LE)
-
[+4..+7]
seq
该 ID 更新序号(LE)
-
[+8..+11]
rx_count
该 ID 累计接收(LE)
-
[+12]
dlc
数据长度 1~64
-
[+13]
flags
bit0=FD bit1=BRS bit2=ESI
-
[+14..+15]
reserved
0x00
-
[+16..+N]
data
报文数据(dlc 字节)
-
-
- -
- 条目大小 = 16 + dlc 字节。标准 CAN (dlc=8):24B/条,每页最多 5 条。CANFD (dlc=64):80B/条,每页最多 1 条。
- total_count = 实际不重复 ID 数(0~16,不满 16 就是实际值)。total_count ≤ 8 时 page=1 的 entry_count=0。
- ESP32 行为:当前代码实现了 0xC3,但主链路主要通过 0xC1 拉取“统计头 + ID 快照表”。 -
- -
错误码:E0=payload 不足,E1=通道非法,E2=快照不可用,E3=页号非法。
-
-
-
- -
-

公共响应 / 协议常量

-
来源:app_spi_command_defs.h、app_spi_command.cpp
-
- 展开查看公共 ID / payload -
-
公共帧头与响应
-
-
SOF
0xA5
起始帧头
-
ACK
0x80
成功
-
WAIT
0x81
等待中
-
ERR
0x82
错误
-
-
公共响应 payload 长度与含义由各命令自行定义。
-
-
-
- -
- -

说明:CAN ID 在 CAN Bridge/Feedback 场景下为运行时值,不在文档中固化为常量。

-
- - - - - - - - - - - - - - - - - -