Compare commits
3
Commits
76da7a3dd6
...
934836618a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
934836618a | ||
|
|
fedd39b902 | ||
|
|
52d7f9be76 |
@@ -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
|
||||
@@ -46,6 +50,7 @@ project:
|
||||
- file: ./device/spi/app_spi_cmd_can_bridge.cpp
|
||||
- file: ./device/spi/app_spi_cmd_can_feedback.cpp
|
||||
- file: ./device/spi/app_spi_cmd_id_snapshot.cpp
|
||||
- file: ./device/spi/app_spi_cmd_adc_feedback.cpp
|
||||
- group: spi_link
|
||||
files:
|
||||
- file: ../../spi_link/spi_link_v2.cpp
|
||||
@@ -99,6 +104,8 @@ project:
|
||||
- -D__MICROLIB
|
||||
- -fshort-enums
|
||||
- -fshort-wchar
|
||||
# EXMC 未作为 CMSIS 组件分发;头文件在 RTE 本地目录
|
||||
- -IRTE/Device/GD32E508VE
|
||||
- -Wno-register
|
||||
- -IG:/Robot_v2/DCU/spi_link
|
||||
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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,330 @@
|
||||
/**
|
||||
* @file esc_eeprom.cpp
|
||||
* @brief 通过 LAN9252 HBI Indexed 间接访问烧录外部 AT24C32 EEPROM (ESI) 实现。
|
||||
*
|
||||
* HBI Indexed 间接访问协议:
|
||||
* 1. 写 HBI_IDX_0 = 目标 ESC 寄存器字节地址
|
||||
* 2. 读/写 HBI_DATA_0 = 数据(低 word + 高 word)
|
||||
*
|
||||
* HBI 寄存器 EXMC 偏移(A1=EXMC_A16=PD11, A2=EXMC_A17=PD12):
|
||||
* HBI_IDX_0 低 word: A2=0, A1=0 → EXMC 偏移 0x00000
|
||||
* HBI_DATA_0 低 word: A2=1, A1=0 → EXMC 偏移 0x40000
|
||||
* HBI_DATA_0 高 word: A2=1, A1=1 → EXMC 偏移 0x60000
|
||||
*
|
||||
* 注意:32-bit 寄存器访问时,写一次 IDX 后连续写/读两次 DATA_0(低 word 在前),
|
||||
* HBI 内部 BYTE/WORD 计数器会自动组装 DWORD,无需改 IDX 地址。
|
||||
*/
|
||||
#include "comm/esc_eeprom.h"
|
||||
#include "comm/esc_exmc_bus.h"
|
||||
#include "sys/config.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
/* HBI Indexed Mode 寄存器 EXMC 偏移
|
||||
* 地址解码(A2 是 IDX/DATA 选择位,必须接到 EXMC_A17=PD12):
|
||||
* A2=0, A1=0 → word 0 → 偏移 0x00000 → HBI_IDX_0 低 word
|
||||
* A2=0, A1=1 → word 1 → 偏移 0x20000 → HBI_IDX_0 高 word(A1=EXMC_A16=PD11)
|
||||
* A2=1, A1=0 → word 2 → 偏移 0x40000 → HBI_DATA_0 低 word(A2=EXMC_A17=PD12)
|
||||
* A2=1, A1=1 → word 3 → 偏移 0x60000 → HBI_DATA_0 高 word
|
||||
* 注:EXMC A16 对应 byte 偏移 0x20000,A17 对应 0x40000。 */
|
||||
constexpr uint32_t HBI_IDX0_OFFSET = 0x00000U; /* A2=0, A1=0 */
|
||||
constexpr uint32_t HBI_DATA0_LOW_OFF = 0x40000U; /* A2=1, A1=0 */
|
||||
constexpr uint32_t HBI_DATA0_HIGH_OFF = 0x60000U; /* A2=1, A1=1 */
|
||||
|
||||
/* ESC EEPROM 寄存器字节地址 */
|
||||
constexpr uint16_t ESC_EEP_CFG = 0x0500U; /* 8-bit */
|
||||
constexpr uint16_t ESC_EEP_PDI_ACC = 0x0501U; /* 8-bit */
|
||||
constexpr uint16_t ESC_EEP_CTRL = 0x0502U; /* 16-bit */
|
||||
constexpr uint16_t ESC_EEP_ADDR = 0x0504U; /* 32-bit */
|
||||
constexpr uint16_t ESC_EEP_DATA = 0x0508U; /* 32-bit (低 16 位为读写数据) */
|
||||
|
||||
/* EEPROM Control/Status bit 定义 */
|
||||
constexpr uint16_t EEP_CTRL_BUSY = 0x8000U;
|
||||
constexpr uint16_t EEP_CTRL_ERR_CSUM = 0x1000U;
|
||||
constexpr uint16_t EEP_CTRL_CMD_MASK = 0x0700U;
|
||||
constexpr uint16_t EEP_CTRL_CMD_READ = 0x0100U;
|
||||
constexpr uint16_t EEP_CTRL_CMD_WRITE = 0x0200U;
|
||||
constexpr uint16_t EEP_CTRL_CMD_RELOAD = 0x0400U;
|
||||
constexpr uint16_t EEP_CTRL_LOADING_STATUS = 0x0800U; /* 1=未加载/失败 */
|
||||
constexpr uint16_t EEP_CTRL_2ADDR = 0x0080U; /* 1=2 地址字节(32Kbit~4Mbit) */
|
||||
constexpr uint16_t EEP_CTRL_ECAT_WE = 0x0001U;
|
||||
|
||||
/* EEPROM Configuration bit */
|
||||
constexpr uint8_t EEP_CFG_PDI_CTRL = 0x01U;
|
||||
constexpr uint8_t EEP_CFG_FORCE_ECAT = 0x02U;
|
||||
|
||||
/* EEPROM PDI Access State bit */
|
||||
constexpr uint8_t EEP_PDI_ACC_ACCESS = 0x01U;
|
||||
|
||||
/* 等待 EEPROM 接口空闲(Busy=0)。
|
||||
* 写操作后 AT24C32 内部需要 ~5ms 页写时间,控制器会保持 Busy。
|
||||
* 超时设为 20ms(200000 次空循环约 20ms @180MHz)。 */
|
||||
bool waitIdle(uint32_t timeout_loops = 500000U)
|
||||
{
|
||||
while (timeout_loops-- > 0U) {
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, ESC_EEP_CTRL);
|
||||
uint16_t ctrl = EscExmcBus::read16(HBI_DATA0_LOW_OFF);
|
||||
if ((ctrl & EEP_CTRL_BUSY) == 0U) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* === HBI Indexed 间接访问 ESC 寄存器 === */
|
||||
|
||||
uint8_t readEsc8(uint16_t esc_addr)
|
||||
{
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, esc_addr);
|
||||
return (uint8_t)(EscExmcBus::read16(HBI_DATA0_LOW_OFF) & 0x00FFU);
|
||||
}
|
||||
|
||||
void writeEsc8(uint16_t esc_addr, uint8_t val)
|
||||
{
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, esc_addr);
|
||||
EscExmcBus::write16(HBI_DATA0_LOW_OFF, (uint16_t)val);
|
||||
}
|
||||
|
||||
uint16_t readEsc16(uint16_t esc_addr)
|
||||
{
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, esc_addr);
|
||||
return EscExmcBus::read16(HBI_DATA0_LOW_OFF);
|
||||
}
|
||||
|
||||
void writeEsc16(uint16_t esc_addr, uint16_t val)
|
||||
{
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, esc_addr);
|
||||
EscExmcBus::write16(HBI_DATA0_LOW_OFF, val);
|
||||
}
|
||||
|
||||
uint32_t readEsc32(uint16_t esc_addr)
|
||||
{
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, esc_addr);
|
||||
uint16_t lo = EscExmcBus::read16(HBI_DATA0_LOW_OFF);
|
||||
uint16_t hi = EscExmcBus::read16(HBI_DATA0_LOW_OFF); /* 计数器自增,读高 word */
|
||||
return ((uint32_t)hi << 16) | lo;
|
||||
}
|
||||
|
||||
void writeEsc32(uint16_t esc_addr, uint32_t val)
|
||||
{
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, esc_addr);
|
||||
EscExmcBus::write16(HBI_DATA0_LOW_OFF, (uint16_t)(val & 0xFFFFU)); /* 低 word */
|
||||
EscExmcBus::write16(HBI_DATA0_LOW_OFF, (uint16_t)((val >> 16) & 0xFFFFU));/* 高 word */
|
||||
}
|
||||
|
||||
} /* anonymous namespace */
|
||||
|
||||
bool EscEeprom::s_inited = false;
|
||||
|
||||
bool EscEeprom::init()
|
||||
{
|
||||
s_inited = false;
|
||||
|
||||
if (!EscExmcBus::isOk()) {
|
||||
printf("[EEPROM] init: FAIL, EXMC bus not ready\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 步骤 1:置 PDI EEPROM Control = 1,把 EEPROM 接口交给 PDI(GD32) */
|
||||
writeEsc8(ESC_EEP_CFG, EEP_CFG_PDI_CTRL);
|
||||
|
||||
/* 步骤 2:置 Access to EEPROM = 1,获取访问权 */
|
||||
writeEsc8(ESC_EEP_PDI_ACC, EEP_PDI_ACC_ACCESS);
|
||||
|
||||
/* 步骤 3:等待接口空闲,读回状态确认 */
|
||||
if (!waitIdle()) {
|
||||
printf("[EEPROM] init: FAIL, EEPROM interface busy timeout\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 读回 Configuration 寄存器确认 PDI 控制已生效 */
|
||||
uint8_t cfg = readEsc8(ESC_EEP_CFG);
|
||||
if ((cfg & EEP_CFG_PDI_CTRL) == 0U) {
|
||||
printf("[EEPROM] init: FAIL, PDI EEPROM Control not set (cfg=0x%02X)\r\n", (unsigned)cfg);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 确认 EEPROM 算法:AT24C32 = 32Kbit,需要 2 地址字节模式 */
|
||||
uint16_t ctrl = readEsc16(ESC_EEP_CTRL);
|
||||
if ((ctrl & EEP_CTRL_2ADDR) == 0U) {
|
||||
printf("[EEPROM] init: WARN, 2-address-byte mode not set (ctrl=0x%04X), AT24C32 requires it\r\n",
|
||||
(unsigned)ctrl);
|
||||
/* 不直接 fail,允许继续尝试;可能由 strap 自动配置 */
|
||||
}
|
||||
|
||||
s_inited = true;
|
||||
printf("[EEPROM] init: OK, PDI has EEPROM control (cfg=0x%02X, ctrl=0x%04X)\r\n",
|
||||
(unsigned)cfg, (unsigned)ctrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EscEeprom::readWord(uint16_t word_addr, uint16_t &out)
|
||||
{
|
||||
if (!s_inited) {
|
||||
return false;
|
||||
}
|
||||
if (word_addr >= EEPROM_WORD_COUNT) {
|
||||
printf("[EEPROM] readWord: addr %u out of range\r\n", (unsigned)word_addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!waitIdle()) {
|
||||
printf("[EEPROM] readWord: busy timeout before read (addr=%u)\r\n", (unsigned)word_addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 写 EEPROM 地址寄存器(word 地址,bit0=first word) */
|
||||
writeEsc32(ESC_EEP_ADDR, (uint32_t)word_addr);
|
||||
|
||||
/* 发 Read 命令 */
|
||||
writeEsc16(ESC_EEP_CTRL, EEP_CTRL_CMD_READ);
|
||||
|
||||
if (!waitIdle()) {
|
||||
printf("[EEPROM] readWord: busy timeout after read (addr=%u)\r\n", (unsigned)word_addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 读 EEPROM Data 寄存器低 16 位 */
|
||||
uint32_t data_reg = readEsc32(ESC_EEP_DATA);
|
||||
out = (uint16_t)(data_reg & 0xFFFFU);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EscEeprom::writeWord(uint16_t word_addr, uint16_t data)
|
||||
{
|
||||
if (!s_inited) {
|
||||
return false;
|
||||
}
|
||||
if (word_addr >= EEPROM_WORD_COUNT) {
|
||||
printf("[EEPROM] writeWord: addr %u out of range\r\n", (unsigned)word_addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!waitIdle()) {
|
||||
printf("[EEPROM] writeWord: busy timeout before write (addr=%u)\r\n", (unsigned)word_addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 写 EEPROM 地址寄存器 */
|
||||
writeEsc32(ESC_EEP_ADDR, (uint32_t)word_addr);
|
||||
|
||||
/* 写 EEPROM Data 寄存器低 16 位(高 16 位无关) */
|
||||
writeEsc32(ESC_EEP_DATA, (uint32_t)data);
|
||||
|
||||
/* 发 Write 命令 */
|
||||
writeEsc16(ESC_EEP_CTRL, EEP_CTRL_CMD_WRITE);
|
||||
|
||||
/* 等待写完成(AT24C32 页写 ~5ms,控制器会保持 Busy) */
|
||||
if (!waitIdle()) {
|
||||
printf("[EEPROM] writeWord: busy timeout after write (addr=%u)\r\n", (unsigned)word_addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EscEeprom::writeEsi(const uint8_t *data, size_t len)
|
||||
{
|
||||
if (!s_inited) {
|
||||
printf("[EEPROM] writeEsi: not initialized\r\n");
|
||||
return false;
|
||||
}
|
||||
if (data == nullptr || len == 0U) {
|
||||
return false;
|
||||
}
|
||||
if (len > EEPROM_SIZE_BYTES) {
|
||||
printf("[EEPROM] writeEsi: len %u exceeds EEPROM size %u\r\n",
|
||||
(unsigned)len, (unsigned)EEPROM_SIZE_BYTES);
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("[EEPROM] writeEsi: writing %u bytes (%u words)...\r\n",
|
||||
(unsigned)len, (unsigned)((len + 1U) / 2U));
|
||||
|
||||
size_t word_count = (len + 1U) / 2U;
|
||||
for (size_t i = 0U; i < word_count; ++i) {
|
||||
uint16_t word = 0xFFFFU; /* 不足 2 字节时高位补 0xFF */
|
||||
word = (uint16_t)data[i * 2U];
|
||||
if ((i * 2U + 1U) < len) {
|
||||
word |= ((uint16_t)data[i * 2U + 1U] << 8);
|
||||
}
|
||||
|
||||
if (!writeWord((uint16_t)i, word)) {
|
||||
printf("[EEPROM] writeEsi: FAIL at word %u\r\n", (unsigned)i);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 每 256 word(512 字节)打印一次进度 */
|
||||
if ((i % 256U) == 0U && i > 0U) {
|
||||
printf("[EEPROM] writeEsi: %u/%u words\r\n", (unsigned)i, (unsigned)word_count);
|
||||
}
|
||||
}
|
||||
|
||||
printf("[EEPROM] writeEsi: done (%u words written)\r\n", (unsigned)word_count);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EscEeprom::verifyEsi(const uint8_t *data, size_t len)
|
||||
{
|
||||
if (!s_inited) {
|
||||
return false;
|
||||
}
|
||||
if (data == nullptr || len == 0U) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t word_count = (len + 1U) / 2U;
|
||||
for (size_t i = 0U; i < word_count; ++i) {
|
||||
uint16_t expected = 0xFFFFU;
|
||||
expected = (uint16_t)data[i * 2U];
|
||||
if ((i * 2U + 1U) < len) {
|
||||
expected |= ((uint16_t)data[i * 2U + 1U] << 8);
|
||||
}
|
||||
|
||||
uint16_t actual = 0U;
|
||||
if (!readWord((uint16_t)i, actual)) {
|
||||
printf("[EEPROM] verifyEsi: read FAIL at word %u\r\n", (unsigned)i);
|
||||
return false;
|
||||
}
|
||||
if (actual != expected) {
|
||||
printf("[EEPROM] verifyEsi: MISMATCH at word %u: expected=0x%04X actual=0x%04X\r\n",
|
||||
(unsigned)i, (unsigned)expected, (unsigned)actual);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
printf("[EEPROM] verifyEsi: OK (%u words verified)\r\n", (unsigned)word_count);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EscEeprom::reload()
|
||||
{
|
||||
if (!s_inited) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!waitIdle()) {
|
||||
printf("[EEPROM] reload: busy timeout\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 发 Reload 命令(100b) */
|
||||
writeEsc16(ESC_EEP_CTRL, EEP_CTRL_CMD_RELOAD);
|
||||
printf("[EEPROM] reload: command sent\r\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EscEeprom::isLoaded()
|
||||
{
|
||||
uint16_t ctrl = readEsc16(ESC_EEP_CTRL);
|
||||
/* Loading Status bit=0 表示已加载且设备信息正常 */
|
||||
bool loaded = ((ctrl & EEP_CTRL_LOADING_STATUS) == 0U);
|
||||
bool csum_ok = ((ctrl & EEP_CTRL_ERR_CSUM) == 0U);
|
||||
if (!loaded) {
|
||||
printf("[EEPROM] isLoaded: not loaded (ctrl=0x%04X)\r\n", (unsigned)ctrl);
|
||||
} else if (!csum_ok) {
|
||||
printf("[EEPROM] isLoaded: loaded but checksum error (ctrl=0x%04X)\r\n", (unsigned)ctrl);
|
||||
}
|
||||
return (loaded && csum_ok);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* @file esc_eeprom.h
|
||||
* @brief 通过 LAN9252 HBI Indexed 间接访问烧录外部 AT24C32 EEPROM (ESI)。
|
||||
*
|
||||
* 硬件前提(必须满足,否则模块不可用):
|
||||
* - LAN9252 A2 (Pin 29) 必须接到 GD32 EXMC A17 (PD12),以访问 HBI_DATA_0 寄存器。
|
||||
* 注意:LAN9252 引脚顺序为 Pin25=A1, Pin26=A3, Pin27=A4, Pin28=CS, Pin29=A2。
|
||||
* A2 是区分 IDX/DATA 寄存器的关键位(A2=0→IDX, A2=1→DATA),不接 A2 无法访问 DATA 寄存器。
|
||||
* - A3/A4 可不接(仅访问 HBI_IDX_0/DATA_0 时不需要)。
|
||||
* - LAN9252 处于 16-bit HBI Indexed Mode(CHIP_MODE 配置)。
|
||||
*
|
||||
* ESC EEPROM 寄存器映射(参见 LAN9252 DS00001909A 12.14.47~12.14.51):
|
||||
* 0x0500 EEPROM Configuration (8-bit)
|
||||
* 0x0501 EEPROM PDI Access State (8-bit)
|
||||
* 0x0502 EEPROM Control/Status (16-bit)
|
||||
* 0x0504 EEPROM Address (32-bit)
|
||||
* 0x0508 EEPROM Data (32-bit, 低 16 位为读写数据)
|
||||
*
|
||||
* 调用约束:INIT-ONLY,仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用。
|
||||
*/
|
||||
#ifndef COMM_ESC_EEPROM_H
|
||||
#define COMM_ESC_EEPROM_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
class EscEeprom {
|
||||
public:
|
||||
/** @brief 初始化:获取 PDI 对 EEPROM 接口的控制权并验证总线。
|
||||
* @return true 表示成功获取 EEPROM 控制权。 */
|
||||
static bool init();
|
||||
|
||||
/** @brief 读取 EEPROM 一个 16-bit word。
|
||||
* @param word_addr word 地址(0 ~ 2047,对应 AT24C32 的 4KB 空间)。
|
||||
* @param out 输出读取到的 16-bit 数据。
|
||||
* @return true 表示读取成功。 */
|
||||
static bool readWord(uint16_t word_addr, uint16_t &out);
|
||||
|
||||
/** @brief 写入 EEPROM 一个 16-bit word。
|
||||
* @param word_addr word 地址。
|
||||
* @param data 要写入的 16-bit 数据。
|
||||
* @return true 表示写入成功。 */
|
||||
static bool writeWord(uint16_t word_addr, uint16_t data);
|
||||
|
||||
/** @brief 批量烧录 ESI 数据。
|
||||
* @param data 数据缓冲区(按 byte 流给出,内部按 word 对齐处理)。
|
||||
* @param len 数据长度(字节,应为 2 的倍数;奇数时末尾补 0xFF)。
|
||||
* @return true 表示全部写入成功。 */
|
||||
static bool writeEsi(const uint8_t *data, size_t len);
|
||||
|
||||
/** @brief 回读校验 ESI 数据。
|
||||
* @param data 期望数据缓冲区。
|
||||
* @param len 校验长度(字节)。
|
||||
* @return true 表示回读数据与期望一致。 */
|
||||
static bool verifyEsi(const uint8_t *data, size_t len);
|
||||
|
||||
/** @brief 触发 EEPROM 重新加载(写完 ESI 后调用,使 LAN9252 重新读取配置)。
|
||||
* @return true 表示 Reload 命令已发出。 */
|
||||
static bool reload();
|
||||
|
||||
/** @brief 查询 EEPROM 加载状态。
|
||||
* @return true 表示 EEPROM 已加载且设备信息正常。 */
|
||||
static bool isLoaded();
|
||||
|
||||
/** @brief AT24C32 容量(字节)。 */
|
||||
static constexpr size_t EEPROM_SIZE_BYTES = 4096U;
|
||||
/** @brief AT24C32 word 数。 */
|
||||
static constexpr uint16_t EEPROM_WORD_COUNT = 2048U;
|
||||
|
||||
private:
|
||||
static bool s_inited;
|
||||
};
|
||||
|
||||
#endif /* COMM_ESC_EEPROM_H */
|
||||
@@ -1,29 +1,34 @@
|
||||
/**
|
||||
* @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"
|
||||
#include <stdio.h>
|
||||
|
||||
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()
|
||||
bool EscExmcBus::s_ok = false;
|
||||
|
||||
bool EscExmcBus::init()
|
||||
{
|
||||
/* 使能 GPIO 与 EXMC 时钟 */
|
||||
s_ok = false;
|
||||
|
||||
printf("[ECAT][EXMC] init: enable RCU clocks\r\n");
|
||||
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 推挽 */
|
||||
@@ -44,37 +49,82 @@ void EscExmcBus::init()
|
||||
gpio_init(CONFIG_EXMC_D14_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D14_PIN);
|
||||
gpio_init(CONFIG_EXMC_D15_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D15_PIN);
|
||||
|
||||
/* 控制线:A16 / NOE / NWE / NE1 */
|
||||
/* 控制线:A16 / A17 / NOE / NWE / NE1
|
||||
* A17(PD12) 用于 HBI Indexed Mode 访问 HBI_DATA_0,已将 LAN9252 A2 (Pin 29) 接到 PD12。 */
|
||||
gpio_init(CONFIG_EXMC_A16_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_A16_PIN);
|
||||
gpio_init(CONFIG_EXMC_A17_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_A17_PIN);
|
||||
gpio_init(CONFIG_EXMC_NOE_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_NOE_PIN);
|
||||
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);
|
||||
printf("[ECAT][EXMC] init: GPIO configured (D0-D15/A16/A17/NOE/NWE/NE1)\r\n");
|
||||
|
||||
/* 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;
|
||||
|
||||
printf("[ECAT][EXMC] init: applying NOR/SRAM Region1 params (16b, async, t_data=%u HCLK)\r\n",
|
||||
(unsigned)rw_timing.asyn_data_setuptime);
|
||||
|
||||
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
|
||||
|
||||
/* 总线探活:Region1 使能后立即读取 LAN9252 芯片 ID 低半字(期望 0x4321)。
|
||||
* 0x0000/0xFFFF 通常意味着 EXMC 时序不满足、地址线接错或 ESC 未上电;
|
||||
* 此处仅做总线可见性探活,芯片 ID 完整性校验留给 EscLan9252::init()。 */
|
||||
const uint16_t probe_low = read16((uint32_t)0x0000U << 1U);
|
||||
const uint16_t probe_high = read16((uint32_t)0x0002U << 1U);
|
||||
const uint32_t probe_id = ((uint32_t)probe_high << 16) | probe_low;
|
||||
printf("[ECAT][EXMC] init: Region1 enabled, probe id_lo=0x%04X id_hi=0x%04X (id=0x%08lX)\r\n",
|
||||
(unsigned)probe_low, (unsigned)probe_high, (unsigned long)probe_id);
|
||||
|
||||
/* 总线挂死(全 0/全 1)视为 EXMC 未就绪;chip ID 完整校验在 EscLan9252::init()。 */
|
||||
if (probe_low == 0x0000U && probe_high == 0x0000U) {
|
||||
printf("[ECAT][EXMC] init: FAIL, bus reads all-zero (check wiring/NE1/A16/timing)\r\n");
|
||||
return false;
|
||||
}
|
||||
if (probe_low == 0xFFFFU && probe_high == 0xFFFFU) {
|
||||
printf("[ECAT][EXMC] init: FAIL, bus reads all-ones (check ESC power/NOE/NWE)\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
s_ok = true;
|
||||
printf("[ECAT][EXMC] init: OK, bus visible\r\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
__attribute__((section(".ramfunc"), noinline))
|
||||
|
||||
@@ -16,14 +16,21 @@
|
||||
*/
|
||||
class EscExmcBus {
|
||||
public:
|
||||
/** @brief 初始化 EXMC NOR/SRAM Region1 与全部 GPIO(AF 复用)。 */
|
||||
static void init();
|
||||
/** @brief 初始化 EXMC NOR/SRAM Region1 与全部 GPIO(AF 复用)。
|
||||
* @return bool true 表示 EXMC Region1 已成功使能且首次回读探活通过。 */
|
||||
static bool init();
|
||||
|
||||
/** @brief ISR-only:仅供 preempt=0 调用,无锁。 */
|
||||
static uint16_t read16(uint32_t offset);
|
||||
|
||||
/** @brief ISR-only:仅供 preempt=0 调用,无锁。 */
|
||||
static void write16(uint32_t offset, uint16_t data);
|
||||
|
||||
/** @brief init 后查询:EXMC Region1 是否使能且探活通过(供上层诊断汇总)。 */
|
||||
static bool isOk() { return s_ok; }
|
||||
|
||||
private:
|
||||
static bool s_ok; /**< EXMC Region1 使能 + 探活结果(init 后置位)。 */
|
||||
};
|
||||
|
||||
#endif /* COMM_ESC_EXMC_BUS_H */
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "comm/esc_lan9252.h"
|
||||
#include "comm/esc_exmc_bus.h"
|
||||
#include "sys/config.h"
|
||||
#include <stdio.h>
|
||||
#include "RTE_Components.h"
|
||||
#include CMSIS_device_header
|
||||
|
||||
@@ -18,14 +19,21 @@ constexpr uint32_t LAN9252_CHIP_ID_VALUE = 0x87654321U; /**< 期望的 32-bit
|
||||
constexpr uint16_t LAN9252_PDI_BASE_OFFSET = 0x1000U;
|
||||
}
|
||||
|
||||
void EscLan9252::init()
|
||||
bool EscLan9252::s_busReady = false;
|
||||
|
||||
bool EscLan9252::init()
|
||||
{
|
||||
/* 使能 DWT 周期计数器,供 periodicIsr 执行时间测量 */
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
DWT->CYCCNT = 0U;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
|
||||
EscExmcBus::init();
|
||||
s_busReady = EscExmcBus::init();
|
||||
if (!s_busReady) {
|
||||
printf("[ECAT][ESC] init: FAIL, EXMC bus not ready (see [ECAT][EXMC] log)\r\n");
|
||||
return false;
|
||||
}
|
||||
printf("[ECAT][ESC] init: EXMC bus ready\r\n");
|
||||
|
||||
/* ============================================================
|
||||
* TODO(spec): 完整 ESC 寄存器配置(需 SSC 或等价协议栈代码,本 spec 不实现)
|
||||
@@ -56,6 +64,7 @@ void EscLan9252::init()
|
||||
* 本 spec 仅通过 EthercatSlave::isEscReady() 的 AL 状态守卫使
|
||||
* "SYNC0 不会触发"在运行期可观测,完整配置留待 SSC 集成 spec。
|
||||
* ============================================================ */
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t EscLan9252::readChipId()
|
||||
|
||||
@@ -21,8 +21,9 @@
|
||||
*/
|
||||
class EscLan9252 {
|
||||
public:
|
||||
/** @brief 初始化 EXMC 总线与 ESC 基本寄存器。 */
|
||||
static void init();
|
||||
/** @brief 初始化 EXMC 总线与 ESC 基本寄存器。
|
||||
* @return bool true 表示 EXMC 总线探活通过(可继续读取芯片 ID)。 */
|
||||
static bool init();
|
||||
|
||||
/**
|
||||
* @brief 读取 ESC 芯片 ID(INIT-ONLY,供 init 打印诊断用)。
|
||||
@@ -41,6 +42,12 @@ public:
|
||||
|
||||
/** @brief INIT-ONLY:仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用,需 ESC 寄存器访问须通过 SYNC0 ISR 代访队列(本 spec 不涵盖)。 */
|
||||
static uint16_t initOnlyReadReg(uint16_t addr);
|
||||
|
||||
/** @brief EXMC 总线是否就绪(init 后可查询)。 */
|
||||
static bool isBusReady() { return s_busReady; }
|
||||
|
||||
private:
|
||||
static bool s_busReady; /**< EXMC 探活结果(init 后置位,供上层诊断汇总)。 */
|
||||
};
|
||||
|
||||
#endif /* COMM_ESC_LAN9252_H */
|
||||
#endif /* COMM_ESC_LAN9252_H */
|
||||
@@ -32,36 +32,72 @@ uint8_t EthercatSlave::s_alState = 0U;
|
||||
|
||||
void EthercatSlave::init()
|
||||
{
|
||||
EscLan9252::init();
|
||||
printf("[ECAT][SLAVE] init: begin\r\n");
|
||||
|
||||
if (!EscLan9252::init()) {
|
||||
/* EXMC 总线未就绪:无法继续读芯片 ID 与 AL 状态,统一上报 FAIL。 */
|
||||
s_chipId = 0U;
|
||||
s_alState = 0U;
|
||||
s_escReady = false;
|
||||
printf("[ECAT][SLAVE] init: FAIL, ESC bus not ready\r\n");
|
||||
s_inputWriteIdx = 0U;
|
||||
s_outputReadIdx = 0U;
|
||||
s_periodicSeq = 0U;
|
||||
s_processedSeq = 0U;
|
||||
return;
|
||||
}
|
||||
|
||||
/* 第 1 层调试可观测性:读取芯片 ID 并打印 selfTest 结果 */
|
||||
s_chipId = EscLan9252::readChipId();
|
||||
const bool chipIdOk = (s_chipId == 0x87654321U);
|
||||
printf("[ECAT][SLAVE] chip id=0x%08lX expected=0x%08lX selfTest=%s\r\n",
|
||||
(unsigned long)s_chipId, (unsigned long)0x87654321UL,
|
||||
chipIdOk ? "PASS" : "FAIL");
|
||||
|
||||
/* AL 状态守卫:仅在芯片 ID 匹配时读取 AL 状态(ID 不匹配时 EXMC 可能读回 0/0xFFFF,无意义)。
|
||||
* AL ≥ SAFEOP(0x04) 才认为 SYNC0 可触发;完整 ESC 配置(SM/FMMU/AL Control/SYNC0)需 SSC 集成。 */
|
||||
s_alState = 0U;
|
||||
uint16_t alStatusReg = 0U; /* 0x0130:AL Status(bit0-3=state,bit8-15=AL Status Code) */
|
||||
if (chipIdOk) {
|
||||
s_alState = (uint8_t)(EscLan9252::initOnlyReadReg(0x0130U) & 0x000FU);
|
||||
alStatusReg = EscLan9252::initOnlyReadReg(0x0130U);
|
||||
s_alState = (uint8_t)(alStatusReg & 0x000FU);
|
||||
}
|
||||
const uint8_t alCode = (uint8_t)((alStatusReg >> 8U) & 0xFFU);
|
||||
s_escReady = chipIdOk && (s_alState >= AL_STATE_SAFEOP);
|
||||
|
||||
/* AL 状态码 → 可读字符串(EtherCAT 状态机定义,见 ETG.1000.6)。
|
||||
* 0x01=INIT, 0x02=PREOP, 0x03=BOOT, 0x04=SAFEOP, 0x08=OP。
|
||||
* 本工程 AL_STATE_SAFEOP=0x04 与官方定义一致;守卫 s_alState>=0x04,
|
||||
* 因此 OP(0x08) 也会通过,SAFEOP(0x04) 刚好通过,BOOT/PREOP/INIT 不通过。 */
|
||||
const char *alStateName = "UNKNOWN";
|
||||
switch (s_alState) {
|
||||
case 0x00U: alStateName = "NONE"; break;
|
||||
case 0x01U: alStateName = "INIT"; break;
|
||||
case 0x02U: alStateName = "PREOP"; break;
|
||||
case 0x03U: alStateName = "BOOT"; break;
|
||||
case 0x04U: alStateName = "SAFEOP"; break;
|
||||
case 0x08U: alStateName = "OP"; break;
|
||||
default: break;
|
||||
}
|
||||
/* AL Status Code(寄存器 0x0130 高 8 位):非 0 表示从站进入错误状态
|
||||
* (如 SM Watchdog=0x1B、FMMU 配置错、邮箱协议错),需主站介入。 */
|
||||
|
||||
if (!chipIdOk) {
|
||||
printf("[ECAT] ESC chip id=0x%08lX selfTest=FAIL\r\n", (unsigned long)s_chipId);
|
||||
printf("[ECAT] ESC NOT ready, sync ISR disabled (check EXMC wiring/timing)\r\n");
|
||||
printf("[ECAT][SLAVE] ESC NOT ready, sync ISR disabled (check EXMC wiring/timing)\r\n");
|
||||
} else if (s_alState < AL_STATE_SAFEOP) {
|
||||
printf("[ECAT] ESC chip id=0x%08lX selfTest=PASS, but AL state=0x%02X < SAFEOP(0x%02X)\r\n",
|
||||
(unsigned long)s_chipId, (unsigned)s_alState, (unsigned)AL_STATE_SAFEOP);
|
||||
printf("[ECAT] SYNC0 will not fire, full ESC config requires SSC integration\r\n");
|
||||
printf("[ECAT][SLAVE] chip id PASS, but AL state=0x%02X(%s) < SAFEOP(0x%02X), code=0x%02X\r\n",
|
||||
(unsigned)s_alState, alStateName, (unsigned)AL_STATE_SAFEOP, (unsigned)alCode);
|
||||
printf("[ECAT][SLAVE] SYNC0 will not fire, full ESC config requires SSC integration\r\n");
|
||||
} else {
|
||||
printf("[ECAT] ESC ready, chip id=0x%08lX, AL state=0x%02X\r\n",
|
||||
(unsigned long)s_chipId, (unsigned)s_alState);
|
||||
printf("[ECAT][SLAVE] ESC ready, chip id=0x%08lX, AL state=0x%02X(%s), code=0x%02X\r\n",
|
||||
(unsigned long)s_chipId, (unsigned)s_alState, alStateName, (unsigned)alCode);
|
||||
}
|
||||
|
||||
s_inputWriteIdx = 0U;
|
||||
s_outputReadIdx = 0U;
|
||||
s_periodicSeq = 0U;
|
||||
s_processedSeq = 0U;
|
||||
printf("[ECAT][SLAVE] init: %s\r\n", s_escReady ? "OK" : "FAIL(partial)");
|
||||
}
|
||||
|
||||
__attribute__((section(".ramfunc"), noinline))
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
/** @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 可触发下限)。 */
|
||||
|
||||
@@ -20,6 +20,12 @@ extern "C" {
|
||||
|
||||
void EthercatSync::init()
|
||||
{
|
||||
#if (CONFIG_ETHERCAT_SYNC_MODE == 0)
|
||||
printf("[ECAT][SYNC] init: mode=EXTI (hardware SYNC0, CONFIG_ETHERCAT_SYNC_MODE=0)\r\n");
|
||||
#else
|
||||
printf("[ECAT][SYNC] init: mode=TIMER0 (degraded, CONFIG_ETHERCAT_SYNC_MODE=1)\r\n");
|
||||
#endif
|
||||
|
||||
/* 使能 GPIOB 时钟(SYNC0=PB0 / IRQ=PB1 / SYNC1=PB2) */
|
||||
rcu_periph_clock_enable(RCU_GPIOB);
|
||||
rcu_periph_clock_enable(RCU_AF);
|
||||
@@ -27,6 +33,10 @@ void EthercatSync::init()
|
||||
/* 配置三个引脚为上拉输入(EXTI 下降沿触发:LAN9252 输出低有效脉冲) */
|
||||
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ,
|
||||
CONFIG_ESC_SYNC0_PIN | CONFIG_ESC_IRQ_PIN | CONFIG_ESC_SYNC1_PIN);
|
||||
printf("[ECAT][SYNC] init: GPIOB pins configured (SYNC0=PB%u IRQ=PB%u SYNC1=PB%u)\r\n",
|
||||
(unsigned)CONFIG_ESC_SYNC0_EXTI_SOURCE_PIN,
|
||||
(unsigned)CONFIG_ESC_IRQ_EXTI_SOURCE_PIN,
|
||||
(unsigned)CONFIG_ESC_SYNC1_EXTI_SOURCE_PIN);
|
||||
|
||||
#if (CONFIG_ETHERCAT_SYNC_MODE == 0)
|
||||
/* ---- 默认:EXTI 外部触发模式 ---- */
|
||||
@@ -36,6 +46,9 @@ void EthercatSync::init()
|
||||
exti_interrupt_enable(CONFIG_ESC_SYNC0_EXTI);
|
||||
exti_interrupt_flag_clear(CONFIG_ESC_SYNC0_EXTI);
|
||||
nvic_irq_enable(CONFIG_ESC_SYNC0_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC, 0U);
|
||||
printf("[ECAT][SYNC] init: SYNC0 EXTI%u enabled (falling, preempt=%u)\r\n",
|
||||
(unsigned)CONFIG_ESC_SYNC0_EXTI_SOURCE_PIN,
|
||||
(unsigned)CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC);
|
||||
#else
|
||||
/* ---- 降级模式:TIMER0 内部定时 ---- */
|
||||
rcu_periph_clock_enable(RCU_TIMER0);
|
||||
@@ -52,6 +65,8 @@ void EthercatSync::init()
|
||||
timer_interrupt_enable(TIMER0, TIMER_INT_UP);
|
||||
nvic_irq_enable(TIMER0_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC, 0U);
|
||||
timer_enable(TIMER0);
|
||||
printf("[ECAT][SYNC] init: TIMER0 enabled (100us, preempt=%u)\r\n",
|
||||
(unsigned)CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC);
|
||||
#endif
|
||||
|
||||
/* 公共:ESC IRQ (PB1->EXTI1) + SYNC1 (PB2->EXTI2) */
|
||||
@@ -60,12 +75,20 @@ void EthercatSync::init()
|
||||
exti_interrupt_enable(CONFIG_ESC_IRQ_EXTI);
|
||||
exti_interrupt_flag_clear(CONFIG_ESC_IRQ_EXTI);
|
||||
nvic_irq_enable(CONFIG_ESC_IRQ_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_ESC, 0U);
|
||||
printf("[ECAT][SYNC] init: ESC IRQ EXTI%u enabled (falling, preempt=%u)\r\n",
|
||||
(unsigned)CONFIG_ESC_IRQ_EXTI_SOURCE_PIN,
|
||||
(unsigned)CONFIG_IRQ_PREEMPT_ETHERCAT_ESC);
|
||||
|
||||
gpio_exti_source_select(CONFIG_ESC_SYNC1_EXTI_SOURCE_PORT, CONFIG_ESC_SYNC1_EXTI_SOURCE_PIN);
|
||||
exti_init(CONFIG_ESC_SYNC1_EXTI, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
|
||||
exti_interrupt_enable(CONFIG_ESC_SYNC1_EXTI);
|
||||
exti_interrupt_flag_clear(CONFIG_ESC_SYNC1_EXTI);
|
||||
nvic_irq_enable(CONFIG_ESC_SYNC1_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC1, 0U);
|
||||
printf("[ECAT][SYNC] init: SYNC1 EXTI%u enabled (falling, preempt=%u)\r\n",
|
||||
(unsigned)CONFIG_ESC_SYNC1_EXTI_SOURCE_PIN,
|
||||
(unsigned)CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC1);
|
||||
|
||||
printf("[ECAT][SYNC] init: OK\r\n");
|
||||
}
|
||||
|
||||
__attribute__((section(".ramfunc"), noinline))
|
||||
|
||||
@@ -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 电源周期)。
|
||||
@@ -161,6 +163,7 @@ CanFd::ChannelStats CanFd::s_channelStats[3]{};
|
||||
CanFd::ChannelFeedback CanFd::s_feedback[3]{};
|
||||
uint32_t CanFd::s_feedbackSeq = 0U;
|
||||
CanFd::IdSnapshotTable CanFd::s_idSnapshot[3]{};
|
||||
bool CanFd::s_channelHwOk[3]{};
|
||||
|
||||
/**
|
||||
* @brief 查询指定通道是否已使能。
|
||||
@@ -210,6 +213,8 @@ void CanFd::initGpio()
|
||||
gpio_init(ch.rxPort, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, ch.rxPin);
|
||||
gpio_init(ch.txPort, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, ch.txPin);
|
||||
gpio_init(ch.slpPort, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, ch.slpPin);
|
||||
/* 上电先 STB=1(待机),延时后再进入 Normal */
|
||||
gpio_bit_set(ch.slpPort, ch.slpPin);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,6 +314,7 @@ bool CanFd::initFdMode(uint32_t canPeriph)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if (CONFIG_CAN_STARTUP_DIAG_VERBOSE != 0)
|
||||
/** @brief 打印 SYS/APB1 时钟与目标波特率。 */
|
||||
void CanFd::printClockInfo()
|
||||
{
|
||||
@@ -316,15 +322,17 @@ void CanFd::printClockInfo()
|
||||
static_cast<unsigned long>(SystemCoreClock),
|
||||
static_cast<unsigned long>(rcu_clock_freq_get(CK_APB1)));
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @brief 单通道硬件初始化(hardResetCan+重试+退出init mode+中断使能+RX队列清零)。 */
|
||||
void CanFd::initChannelHardware(uint8_t i)
|
||||
{
|
||||
const auto &ch = kChannels[i];
|
||||
if (!kChannelEnabled[i]) {
|
||||
printf("[CAN] CH%u DISABLED, skip init\r\n", static_cast<unsigned>(i));
|
||||
s_channelHwOk[i] = false;
|
||||
return;
|
||||
}
|
||||
s_channelHwOk[i] = false;
|
||||
rcu_periph_clock_enable(ch.canRcu);
|
||||
gpio_bit_reset(ch.slpPort, ch.slpPin); /* STB=0 -> Normal */
|
||||
const uint8_t rxLevelBefore = (gpio_input_bit_get(ch.rxPort, ch.rxPin) != RESET) ? 1U : 0U;
|
||||
@@ -408,6 +416,9 @@ void CanFd::initChannelHardware(uint8_t i)
|
||||
s_rxQueues[i].head = 0U;
|
||||
s_rxQueues[i].tail = 0U;
|
||||
s_rxQueues[i].count = 0U;
|
||||
|
||||
const bool exitOk = ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) == 0U);
|
||||
s_channelHwOk[i] = (nominalOk && fdOk && exitOk);
|
||||
}
|
||||
|
||||
/** @brief 统一配置三路滤波器(HBC1F + per-channel filter)。 */
|
||||
@@ -428,6 +439,7 @@ void CanFd::configureAllFilters()
|
||||
static_cast<unsigned long>(CAN_FW(CAN0)));
|
||||
}
|
||||
|
||||
#if (CONFIG_CAN_STARTUP_DIAG_VERBOSE != 0)
|
||||
/** @brief 打印初始化结果汇总(BT/DBT/滤波器状态)。 */
|
||||
void CanFd::printInitResult()
|
||||
{
|
||||
@@ -481,12 +493,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 +529,15 @@ void CanFd::runSelfTest()
|
||||
static_cast<unsigned long>(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 +675,10 @@ void CanFd::runLoopbackTest()
|
||||
static_cast<unsigned long>(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,13 +724,14 @@ void CanFd::dumpRegisters()
|
||||
}
|
||||
printf("===============================\r\n");
|
||||
}
|
||||
#endif /* CONFIG_CAN_STARTUP_DIAG_VERBOSE */
|
||||
|
||||
/**
|
||||
* @brief 初始化三路 CANFD 控制器、过滤器、中断与软件队列。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 初始化后默认将三路 TCAN1057A 收发器置为 Normal,并使能 FIFO0 的非空/满/overfull 中断统计。
|
||||
* @note 初始化收发器 GPIO 后延时进入 Normal,并启用 FIFO0 中断统计。
|
||||
*/
|
||||
void CanFd::init()
|
||||
{
|
||||
@@ -729,11 +741,18 @@ void CanFd::init()
|
||||
for (size_t i = 0; i < kChannelCount; ++i) {
|
||||
s_channelStats[i] = {};
|
||||
s_feedback[i] = {};
|
||||
s_channelHwOk[i] = false;
|
||||
}
|
||||
s_feedbackSeq = 0U;
|
||||
|
||||
initGpio();
|
||||
#if (CONFIG_CAN_STARTUP_DIAG_VERBOSE != 0)
|
||||
printClockInfo();
|
||||
#endif
|
||||
|
||||
/* 收发器待机保持后,延时再进入 Normal */
|
||||
constexpr uint32_t kCanPowerOnDeferMs = 200U;
|
||||
SysTickTimer::delayMs(kCanPowerOnDeferMs);
|
||||
|
||||
for (uint8_t i = 0; i < kChannelCount; ++i) {
|
||||
initChannelHardware(i);
|
||||
@@ -741,11 +760,67 @@ 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
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief L0 轻量自检:无总线发帧,仅校验使能通道硬件与收发器状态。
|
||||
*/
|
||||
bool CanFd::selfTestL0()
|
||||
{
|
||||
constexpr uint32_t kTecRecLimit = 16U;
|
||||
bool anyEnabled = false;
|
||||
bool allOk = true;
|
||||
|
||||
for (uint8_t i = 0U; i < static_cast<uint8_t>(kChannelCount); ++i) {
|
||||
if (!kChannelEnabled[i]) {
|
||||
continue;
|
||||
}
|
||||
anyEnabled = true;
|
||||
const auto &ch = kChannels[i];
|
||||
|
||||
if (!s_channelHwOk[i]) {
|
||||
allOk = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 仍停在初始化模式 */
|
||||
if ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) != 0U) {
|
||||
allOk = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* STB=0 为 Normal;高电平视为仍待机 */
|
||||
if (gpio_output_bit_get(ch.slpPort, ch.slpPin) != RESET) {
|
||||
allOk = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t errReg = CAN_ERR(ch.canPeriph);
|
||||
if ((errReg & CAN_ERR_BOERR) != 0U) {
|
||||
allOk = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t tec = (errReg >> 16) & 0xFFU;
|
||||
const uint32_t rec = (errReg >> 24) & 0xFFU;
|
||||
if (tec > kTecRecLimit || rec > kTecRecLimit) {
|
||||
allOk = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return anyEnabled && allOk;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -843,7 +918,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 +928,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 +997,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 +1045,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] 无。
|
||||
|
||||
@@ -3,14 +3,16 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** @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;
|
||||
@@ -117,6 +118,13 @@ public:
|
||||
|
||||
static void init();
|
||||
|
||||
/**
|
||||
* @brief L0 轻量自检(不向物理总线发帧)。
|
||||
* @retval true 所有已使能通道通过;false 无使能通道或任一路失败。
|
||||
* @note 检查:硬件 init 结果、退出 init mode、收发器 Normal、无 BUS_OFF、TEC/REC 阈值。
|
||||
*/
|
||||
static bool selfTestL0();
|
||||
|
||||
/**
|
||||
* @brief 查询指定通道是否已使能。
|
||||
* @param[in] channelIndex 通道索引 0/1/2。
|
||||
@@ -138,13 +146,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。
|
||||
@@ -213,6 +214,8 @@ private:
|
||||
static volatile Mode s_mode;
|
||||
static ChannelStats s_channelStats[3];
|
||||
static ChannelFeedback s_feedback[3];
|
||||
/** 各通道硬件 init(时序+退出 init mode)是否成功 */
|
||||
static bool s_channelHwOk[3];
|
||||
static uint32_t s_feedbackSeq;
|
||||
static IdSnapshotTable s_idSnapshot[3];
|
||||
};
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* @file app_spi_cmd_adc_feedback.cpp
|
||||
* @brief GD32 ADC 调试反馈(温度 / VDDA / VIN)SPI 命令与 1Hz 主动推送。
|
||||
*
|
||||
* TYPE = SpiDefs::kTypeAdcFeedback (0xC4)
|
||||
*
|
||||
* 总开关:sys/config.h 中 CONFIG_SPI_ADC_FEEDBACK_ENABLE
|
||||
* - 0:不注册 handler、不推送(本文件仍参与链接,函数空实现)
|
||||
* - 1:1Hz 主动 requestEsp32Read(0xC4) + 支持 ESP32 被动查询
|
||||
*
|
||||
* 与 0xC1 CAN 反馈同款模型:主循环 pushStep 非阻塞;ring 未读走则跳过。
|
||||
* 优先级低于 CAN 反馈(runPending 里先 canFeedbackPushStep 再本函数)。
|
||||
*
|
||||
* payload v1 固定 20 字节(小端):
|
||||
* [0] ver=1
|
||||
* [1] flags bit0=sample_valid
|
||||
* [2..3] temp_x100 int16 百分之一摄氏度
|
||||
* [4..5] vdda_mV uint16
|
||||
* [6..7] vin_adc_mV uint16 引脚分压后电压 mV
|
||||
* [8..9] temp_raw uint16
|
||||
* [10..11] vref_raw uint16
|
||||
* [12..15] seq uint32 推送序号
|
||||
* [16..19] uptime_sec uint32
|
||||
*/
|
||||
|
||||
#include "device/spi/spi_command_handler.h"
|
||||
#include "device/spi/app_spi_command.h"
|
||||
#include "device/spi/app_spi_command_defs.h"
|
||||
#include "device/adc.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/ota_boot_request.h"
|
||||
#include "sys/task_runner.h"
|
||||
#include "main.h"
|
||||
#include <cstring>
|
||||
|
||||
#if (CONFIG_SPI_ADC_FEEDBACK_ENABLE != 0)
|
||||
|
||||
namespace {
|
||||
|
||||
static constexpr uint8_t kPayloadVer = 1U;
|
||||
static constexpr uint16_t kPayloadLen = 20U;
|
||||
|
||||
static uint32_t s_pushSeq = 0U;
|
||||
static volatile bool s_needPush = false;
|
||||
|
||||
/**
|
||||
* @brief 构建 0xC4 ADC 反馈 payload(20B)。
|
||||
* @param[out] out 输出缓冲
|
||||
* @param max_len 缓冲容量
|
||||
* @return 写入长度;失败 0
|
||||
*/
|
||||
static uint16_t buildAdcFeedbackPayload(uint8_t *out, uint16_t max_len)
|
||||
{
|
||||
if (out == nullptr || max_len < kPayloadLen) {
|
||||
return 0U;
|
||||
}
|
||||
|
||||
Adc::Sample s{};
|
||||
const bool have = TaskRunner::copyAdcSample(&s);
|
||||
|
||||
const int32_t temp_x100 = have
|
||||
? static_cast<int32_t>(s.temperature * 100.0f)
|
||||
: 0;
|
||||
const uint16_t vdda_mV = have
|
||||
? static_cast<uint16_t>(s.vdda * 1000.0f)
|
||||
: 0U;
|
||||
const uint16_t vin_mV = have
|
||||
? static_cast<uint16_t>(s.vin_adc_v * 1000.0f)
|
||||
: 0U;
|
||||
|
||||
std::memset(out, 0, kPayloadLen);
|
||||
out[0] = kPayloadVer;
|
||||
out[1] = (have && s.valid) ? 0x01U : 0x00U;
|
||||
/* int16 百分之一℃,按两字节小端原样写入 */
|
||||
writeU16Le(&out[2], static_cast<uint16_t>(static_cast<int16_t>(temp_x100)));
|
||||
writeU16Le(&out[4], vdda_mV);
|
||||
writeU16Le(&out[6], vin_mV);
|
||||
writeU16Le(&out[8], have ? s.temp_raw : 0U);
|
||||
writeU16Le(&out[10], have ? s.vref_raw : 0U);
|
||||
writeU32Le(&out[12], s_pushSeq);
|
||||
writeU32Le(&out[16], g_app_uptime_sec);
|
||||
return kPayloadLen;
|
||||
}
|
||||
|
||||
/** @brief SPI 0xC4 ADC 反馈:被动查询应答。 */
|
||||
class SpiAdcFeedbackHandler final : public ISpiCommandHandler {
|
||||
public:
|
||||
/** @brief 是否处理 0xC4。 */
|
||||
bool canHandle(uint8_t frame_type) const override
|
||||
{
|
||||
return frame_type == SpiDefs::kTypeAdcFeedback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 被动查询:回当前 ADC 快照。
|
||||
* @param bus 命令总线
|
||||
* @param ctx 请求上下文
|
||||
*/
|
||||
void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) override
|
||||
{
|
||||
uint8_t payload[kPayloadLen] = {};
|
||||
const uint16_t n = buildAdcFeedbackPayload(payload, sizeof(payload));
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kTypeAdcFeedback, payload, n);
|
||||
}
|
||||
};
|
||||
|
||||
static SpiAdcFeedbackHandler s_handler;
|
||||
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
* @brief 标记需要推送一帧 0xC4(由 1Hz taskHeartbeat 调用)。
|
||||
*/
|
||||
void adcFeedbackStartRound()
|
||||
{
|
||||
s_needPush = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 非阻塞推送一步:ring 空闲则 requestEsp32Read(0xC4)。
|
||||
* @note 主循环调用;与 CAN 反馈共用 ring,忙则跳过。
|
||||
*/
|
||||
void adcFeedbackPushStep()
|
||||
{
|
||||
if (!s_needPush) {
|
||||
return;
|
||||
}
|
||||
if (OtaBootRequest::isRebootPending()) {
|
||||
s_needPush = false;
|
||||
return;
|
||||
}
|
||||
/* 与 CAN 反馈共用 ring:未读走则等下一主循环,不抢占 */
|
||||
if (AppSpiCommandBus.isRingPending()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t payload[kPayloadLen] = {};
|
||||
const uint16_t n = buildAdcFeedbackPayload(payload, sizeof(payload));
|
||||
if (n == 0U) {
|
||||
s_needPush = false;
|
||||
return;
|
||||
}
|
||||
|
||||
++s_pushSeq;
|
||||
writeU32Le(&payload[12], s_pushSeq);
|
||||
|
||||
if (AppSpiCommandBus.requestEsp32Read(SpiDefs::kTypeAdcFeedback, payload, n)) {
|
||||
s_needPush = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief 注册 0xC4 处理器。 */
|
||||
void registerSpiAdcFeedbackCommand(AppSpiCommand &bus)
|
||||
{
|
||||
bus.registerHandler(&s_handler);
|
||||
}
|
||||
|
||||
#else /* CONFIG_SPI_ADC_FEEDBACK_ENABLE == 0 */
|
||||
|
||||
/** @brief 开关关闭时的空实现。 */
|
||||
void adcFeedbackStartRound() {}
|
||||
/** @brief 开关关闭时的空实现。 */
|
||||
void adcFeedbackPushStep() {}
|
||||
/** @brief 开关关闭时不注册 handler。 */
|
||||
void registerSpiAdcFeedbackCommand(AppSpiCommand & /*bus*/) {}
|
||||
|
||||
#endif
|
||||
@@ -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<uint32_t>(ctx.payload[2]) << 16)
|
||||
| (static_cast<uint32_t>(ctx.payload[3]) << 8)
|
||||
| static_cast<uint32_t>(ctx.payload[4]);
|
||||
const uint8_t *can_data = &ctx.payload[5];
|
||||
const uint8_t can_dlc = static_cast<uint8_t>(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);
|
||||
|
||||
@@ -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 <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
@@ -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<uint16_t>(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) */
|
||||
|
||||
@@ -134,14 +137,21 @@ static uint16_t buildFeedbackPayload(uint8_t channel, uint8_t page,
|
||||
|
||||
namespace {
|
||||
|
||||
/** @brief SPI 0xC1 反馈查询 + 0xC2 模式切换处理器。 */
|
||||
class SpiCanFeedbackHandler final : public ISpiCommandHandler {
|
||||
public:
|
||||
/** @brief 是否处理 0xC1 / 0xC2。 */
|
||||
bool canHandle(uint8_t frame_type) const override
|
||||
{
|
||||
return (frame_type == SpiDefs::kTypeCanFeedback)
|
||||
|| (frame_type == SpiDefs::kTypeCanSetMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 分发到 handleFeedback / handleSetMode。
|
||||
* @param bus 命令总线
|
||||
* @param ctx 请求上下文
|
||||
*/
|
||||
void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) override
|
||||
{
|
||||
if (ctx.frame_type == SpiDefs::kTypeCanFeedback) {
|
||||
@@ -152,6 +162,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 被动 0xC1:按 channel/page 回一页反馈。
|
||||
* @param bus 命令总线
|
||||
* @param ctx 请求上下文(payload[0]=ch, [1]=page)
|
||||
*/
|
||||
void handleFeedback(AppSpiCommand &bus, const SpiCommandContext &ctx)
|
||||
{
|
||||
if (ctx.payload_len < 2U) {
|
||||
@@ -177,6 +192,11 @@ private:
|
||||
SpiDefs::kRspAck, payload, payload_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 0xC2:切换三路 CAN 接收过滤模式。
|
||||
* @param bus 命令总线
|
||||
* @param ctx 请求上下文(payload[0]=mode)
|
||||
*/
|
||||
void handleSetMode(AppSpiCommand &bus, const SpiCommandContext &ctx)
|
||||
{
|
||||
if (ctx.payload_len < 1U) {
|
||||
@@ -237,11 +257,18 @@ static SpiCanFeedbackHandler s_handler;
|
||||
*/
|
||||
static volatile bool s_roundActive = true; /* 首次允许启动,无需等 1s */
|
||||
|
||||
/**
|
||||
* @brief 激活新一轮 1Hz CAN 反馈推送(taskHeartbeat 调用)。
|
||||
*/
|
||||
void canFeedbackStartRound()
|
||||
{
|
||||
s_roundActive = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 非阻塞推送一步 0xC1(单页);跳过未使能通道。
|
||||
* @note 主循环调用;ring 忙或本轮未激活则立即返回。
|
||||
*/
|
||||
void canFeedbackPushStep()
|
||||
{
|
||||
static uint8_t s_channel = 0U;
|
||||
@@ -264,6 +291,22 @@ void canFeedbackPushStep()
|
||||
return;
|
||||
}
|
||||
|
||||
/* 跳过编译期关闭的通道(与 APP PING V2_FEAT_CAN_CHx / CONFIG_CAN_CHx_ENABLE 一致) */
|
||||
if (!CanFd::isChannelEnabled(s_channel)) {
|
||||
s_page = 0U;
|
||||
for (;;) {
|
||||
++s_channel;
|
||||
if (s_channel >= kFeedbackChannelCount) {
|
||||
s_channel = 0U;
|
||||
s_roundActive = false;
|
||||
return;
|
||||
}
|
||||
if (CanFd::isChannelEnabled(s_channel)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t payload[kFeedbackPayloadLen] = {};
|
||||
uint16_t payload_len = buildFeedbackPayload(s_channel, s_page,
|
||||
payload, sizeof(payload));
|
||||
@@ -272,17 +315,27 @@ void canFeedbackPushStep()
|
||||
payload, payload_len);
|
||||
}
|
||||
|
||||
/* 推进到下一页/下一通道,循环遍历 3 通道 × 2 页 */
|
||||
/* 推进:每通道 2 页,仅遍历使能通道;到末通道后结束本秒轮次 */
|
||||
if (++s_page >= 2U) {
|
||||
s_page = 0U;
|
||||
if (++s_channel >= kFeedbackChannelCount) {
|
||||
s_channel = 0U;
|
||||
/* 回到 ch=0,page=0:一轮推完,等待下一秒 taskHeartbeat 激活 */
|
||||
s_roundActive = false;
|
||||
for (;;) {
|
||||
++s_channel;
|
||||
if (s_channel >= kFeedbackChannelCount) {
|
||||
s_channel = 0U;
|
||||
s_roundActive = false;
|
||||
break;
|
||||
}
|
||||
if (CanFd::isChannelEnabled(s_channel)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册 0xC1/0xC2 CAN 反馈与模式切换处理器。
|
||||
* @param bus 命令总线
|
||||
*/
|
||||
void registerSpiCanFeedbackCommand(AppSpiCommand &bus)
|
||||
{
|
||||
bus.registerHandler(&s_handler);
|
||||
|
||||
@@ -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 <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint8_t kPageEntries = 8U;
|
||||
constexpr uint16_t kMaxPayloadLen = 123U; /* kTxBufSize=128 - overhead=5 */
|
||||
constexpr uint16_t kMaxPayloadLen =
|
||||
static_cast<uint16_t>(AppSpiCommand::kTxBufSize - V2_OVERHEAD);
|
||||
|
||||
class SpiIdSnapshotHandler final : public ISpiCommandHandler {
|
||||
public:
|
||||
|
||||
@@ -5,47 +5,93 @@
|
||||
#include "sys/ota_boot_request.h"
|
||||
#include "sys/systick.h"
|
||||
#include "main.h"
|
||||
#include "spi_link_v2.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* @brief 汇总 GD32 APP 编译期功能开关 → V2_FEAT_* 位图。
|
||||
*/
|
||||
static uint16_t buildAppFeatureFlags()
|
||||
{
|
||||
uint16_t feat = 0U;
|
||||
#if (CONFIG_SPI_ADC_FEEDBACK_ENABLE != 0)
|
||||
feat = static_cast<uint16_t>(feat | V2_FEAT_ADC_FEEDBACK);
|
||||
#endif
|
||||
#if (CONFIG_UART_TEMP_PERIODIC_PRINT != 0)
|
||||
feat = static_cast<uint16_t>(feat | V2_FEAT_UART_TEMP_PRINT);
|
||||
#endif
|
||||
#if (CONFIG_SPI_APP_LOG_ENABLE != 0)
|
||||
feat = static_cast<uint16_t>(feat | V2_FEAT_SPI_APP_LOG);
|
||||
#endif
|
||||
#if (CONFIG_CAN_FD_BRS_ENABLE != 0)
|
||||
feat = static_cast<uint16_t>(feat | V2_FEAT_CAN_BRS);
|
||||
#endif
|
||||
if (CONFIG_CAN_CH0_ENABLE) {
|
||||
feat = static_cast<uint16_t>(feat | V2_FEAT_CAN_CH0);
|
||||
}
|
||||
if (CONFIG_CAN_CH1_ENABLE) {
|
||||
feat = static_cast<uint16_t>(feat | V2_FEAT_CAN_CH1);
|
||||
}
|
||||
if (CONFIG_CAN_CH2_ENABLE) {
|
||||
feat = static_cast<uint16_t>(feat | V2_FEAT_CAN_CH2);
|
||||
}
|
||||
return feat;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
* @brief 填充 APP 身份 16B(版本 + feat),供 HELLO_ACK 附加块与 PING 共用。
|
||||
* @param[out] out 至少 V2_APP_IDENTITY_LEN 字节
|
||||
*/
|
||||
void fillGd32AppIdentityPayload(uint8_t *out)
|
||||
{
|
||||
if (out == nullptr) {
|
||||
return;
|
||||
}
|
||||
std::memset(out, 0, V2_APP_IDENTITY_LEN);
|
||||
|
||||
out[0] = (uint8_t)APP_FW_VERSION_MAJOR;
|
||||
out[1] = (uint8_t)APP_FW_VERSION_MINOR;
|
||||
out[2] = (uint8_t)APP_FW_VERSION_PATCH;
|
||||
out[3] = (uint8_t)APP_FW_VERSION_BUILD;
|
||||
|
||||
const BootVersion boot_ver = OtaBootRequest::readBootVersion();
|
||||
out[4] = boot_ver.major;
|
||||
out[5] = boot_ver.minor;
|
||||
out[6] = boot_ver.patch;
|
||||
out[7] = boot_ver.build;
|
||||
|
||||
writeU32Le(&out[8], SysTickTimer::getMs());
|
||||
|
||||
out[12] = V2_FEAT_MAP_VER;
|
||||
writeU16Le(&out[13], buildAppFeatureFlags());
|
||||
/* [15] reserved = 0 */
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/** @brief SPI 0xA1 APP PING 处理器:应答身份 16B。 */
|
||||
class SpiAppPingHandler final : public ISpiCommandHandler {
|
||||
public:
|
||||
/** @brief 是否处理该帧类型。 */
|
||||
bool canHandle(uint8_t frame_type) const override
|
||||
{
|
||||
return frame_type == SpiDefs::kTypeAppPing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理 PING:填充与 HELLO 相同的身份块并回 0x80。
|
||||
* @param bus 命令总线
|
||||
* @param ctx 请求上下文
|
||||
*/
|
||||
void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) override
|
||||
{
|
||||
/* v2 PING 响应 payload(16B):
|
||||
* [0..3] APP version (major/minor/patch/build)
|
||||
* [4..7] BOOT version (major/minor/patch/build)
|
||||
* [8..11] uptime_ms (4B LE)
|
||||
* [12..15] build_hash (4B, 暂用 0 占位) */
|
||||
uint8_t ping_payload[16];
|
||||
std::memset(ping_payload, 0, sizeof(ping_payload));
|
||||
|
||||
ping_payload[0] = (uint8_t)APP_FW_VERSION_MAJOR;
|
||||
ping_payload[1] = (uint8_t)APP_FW_VERSION_MINOR;
|
||||
ping_payload[2] = (uint8_t)APP_FW_VERSION_PATCH;
|
||||
ping_payload[3] = (uint8_t)APP_FW_VERSION_BUILD;
|
||||
|
||||
/* BOOT 版本号从 OTA 配置页 Flash 读取(封装函数,避免裸指针散落) */
|
||||
const BootVersion boot_ver = OtaBootRequest::readBootVersion();
|
||||
ping_payload[4] = boot_ver.major;
|
||||
ping_payload[5] = boot_ver.minor;
|
||||
ping_payload[6] = boot_ver.patch;
|
||||
ping_payload[7] = boot_ver.build;
|
||||
|
||||
const uint32_t uptime_ms = SysTickTimer::getMs();
|
||||
ping_payload[8] = (uint8_t)(uptime_ms & 0xFFU);
|
||||
ping_payload[9] = (uint8_t)((uptime_ms >> 8) & 0xFFU);
|
||||
ping_payload[10] = (uint8_t)((uptime_ms >> 16) & 0xFFU);
|
||||
ping_payload[11] = (uint8_t)((uptime_ms >> 24) & 0xFFU);
|
||||
|
||||
/* build_hash 占位 0(ping_payload[12..15] 已由 memset 清零) */
|
||||
|
||||
/* 与 HELLO_ACK 附加块同一布局,用于存活/uptime 刷新 */
|
||||
uint8_t ping_payload[V2_APP_PING_LEN];
|
||||
fillGd32AppIdentityPayload(ping_payload);
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspAck, ping_payload, sizeof(ping_payload));
|
||||
}
|
||||
@@ -55,6 +101,7 @@ static SpiAppPingHandler s_handler;
|
||||
|
||||
} // namespace
|
||||
|
||||
/** @brief 注册 0xA1 PING 命令处理器。 */
|
||||
void registerSpiAppPingCommand(AppSpiCommand &bus)
|
||||
{
|
||||
bus.registerHandler(&s_handler);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@
|
||||
#define DEVICE_APP_SPI_COMMAND_H
|
||||
|
||||
#include <cstdint>
|
||||
#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)。
|
||||
@@ -162,7 +164,22 @@ void canFeedbackPushStep();
|
||||
/** @brief 激活新一轮 CAN 反馈推送(由 1Hz taskHeartbeat 调用)。 */
|
||||
void canFeedbackStartRound();
|
||||
|
||||
/**
|
||||
* @brief ADC 调试反馈(0xC4)主动推送一步。
|
||||
* @note CONFIG_SPI_ADC_FEEDBACK_ENABLE=0 时为空操作。
|
||||
*/
|
||||
void adcFeedbackPushStep();
|
||||
|
||||
/** @brief 激活一次 ADC 调试反馈推送(1Hz taskHeartbeat)。 */
|
||||
void adcFeedbackStartRound();
|
||||
|
||||
/** @brief 全局 SPI 命令总线实例。 */
|
||||
extern AppSpiCommand AppSpiCommandBus;
|
||||
|
||||
/**
|
||||
* @brief 填充 APP 身份 16B(版本 + 功能开关),供 HELLO_ACK 与 PING 共用。
|
||||
* @param out 至少 V2_APP_IDENTITY_LEN 字节。
|
||||
*/
|
||||
void fillGd32AppIdentityPayload(uint8_t *out);
|
||||
|
||||
#endif /* DEVICE_APP_SPI_COMMAND_H */
|
||||
|
||||
@@ -24,6 +24,9 @@ static constexpr uint8_t kTypeCanSend = 0xC0U;
|
||||
static constexpr uint8_t kTypeCanFeedback = 0xC1U; /**< ESP32 -> GD32 轮询 CAN 反馈快照 */
|
||||
static constexpr uint8_t kTypeCanSetMode = 0xC2U; /**< ESP32 -> GD32 切换 CAN 接收过滤模式 */
|
||||
static constexpr uint8_t kTypeIdSnapshot = 0xC3U; /**< ESP32 -> GD32 拉取不重复 ID 快照表 */
|
||||
/** @brief GD32 ADC 调试反馈(温度/VDDA/VIN);1Hz 主动 EVENT 或被动查询。
|
||||
* 由 CONFIG_SPI_ADC_FEEDBACK_ENABLE 门控,调试用可随时关。 */
|
||||
static constexpr uint8_t kTypeAdcFeedback = 0xC4U;
|
||||
|
||||
/* 响应类型 */
|
||||
static constexpr uint8_t kRspAck = 0x80U;
|
||||
@@ -32,6 +35,12 @@ static constexpr uint8_t kRspError = 0x82U;
|
||||
|
||||
} // namespace SpiDefs
|
||||
|
||||
/** @brief 将 16 位值按小端写入 2 字节缓冲。 */
|
||||
inline void writeU16Le(uint8_t *dst, uint16_t val) {
|
||||
dst[0] = static_cast<uint8_t>(val & 0xFFU);
|
||||
dst[1] = static_cast<uint8_t>((val >> 8) & 0xFFU);
|
||||
}
|
||||
|
||||
/** @brief 将 32 位值按小端写入 4 字节缓冲(供 CAN 反馈/ID 快照等 payload 复用)。 */
|
||||
inline void writeU32Le(uint8_t *dst, uint32_t val) {
|
||||
dst[0] = static_cast<uint8_t>(val & 0xFFU);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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)。 */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* @file config.h
|
||||
* @brief 应用级集中配置项定义。
|
||||
* @note 用于统一管理当前项目中可调的外设、时序、通信与算法常量。
|
||||
@@ -7,10 +7,42 @@
|
||||
#define SYS_CONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "gd32e50x.h"
|
||||
|
||||
/** @brief 应用启动横幅字符串。 */
|
||||
constexpr const char *APP_BANNER_TEXT = "GD32E508 DCU task scheduler start";
|
||||
/* ============================================================
|
||||
* 功能开关(调试/特性总闸 — 改这里即可开关,改完重编)
|
||||
* 0/false=关,1/true=开。需要 #if 的保留 #define。
|
||||
* ============================================================ */
|
||||
/** @brief 周期温度/电压 UART 打印(5s,task_manager #if)。 */
|
||||
#define CONFIG_UART_TEMP_PERIODIC_PRINT 1
|
||||
/** @brief UART 诊断详细级别(task_runner #if;0=精简 1=含 CAN 统计)。 */
|
||||
#define CONFIG_UART_DIAG_LEVEL 1
|
||||
/**
|
||||
* @brief SPI 调试反馈 0xC4:GD32 ADC 温度/VDDA/VIN 1Hz 主动推送。
|
||||
* @note 调试用;0=不注册 handler、不推送。与 ESP32 GD32_ADC_FEEDBACK_ENABLE 配对。
|
||||
*/
|
||||
#define CONFIG_SPI_ADC_FEEDBACK_ENABLE 1
|
||||
/** @brief SPI 命令逐帧日志(#if)。 */
|
||||
#define CONFIG_SPI_APP_LOG_ENABLE 0
|
||||
/** @brief CAN_SEND 成功帧详细日志(#if)。 */
|
||||
#define CONFIG_SPI_CAN_SEND_VERBOSE_LOG 0
|
||||
/** @brief CAN-FD BRS 使能(canfd.cpp #if)。 */
|
||||
#define CONFIG_CAN_FD_BRS_ENABLE 1U
|
||||
/** @brief 启动时总线探测帧;正常固件关。 */
|
||||
#define CONFIG_CAN_STARTUP_BUS_TEST_ENABLE 0
|
||||
/** @brief 启动时内部回环自检;正常固件关。 */
|
||||
#define CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE 0
|
||||
/** @brief CAN 启动详细诊断 dump;默认关。 */
|
||||
#define CONFIG_CAN_STARTUP_DIAG_VERBOSE 0
|
||||
/** @brief CAN0/1/2 通道使能。 */
|
||||
constexpr bool CONFIG_CAN_CH0_ENABLE = true;
|
||||
constexpr bool CONFIG_CAN_CH1_ENABLE = true;
|
||||
constexpr bool CONFIG_CAN_CH2_ENABLE = true;
|
||||
/** @brief 主循环 WFI(main.cpp #if)。 */
|
||||
#define CONFIG_MAIN_LOOP_WFI_ENABLE 0
|
||||
/** @brief EtherCAT SYNC0 同步模式(#if)。 */
|
||||
#define CONFIG_ETHERCAT_SYNC_MODE 0
|
||||
|
||||
/* ============================================================
|
||||
* APP 固件版本号
|
||||
@@ -42,18 +74,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 +90,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;
|
||||
@@ -110,21 +143,7 @@ constexpr uint32_t CONFIG_CAN_DATA_BS1 = CAN_BT_BS1_3TQ;
|
||||
/** @brief 数据段时间段 2。 */
|
||||
constexpr uint32_t CONFIG_CAN_DATA_BS2 = CAN_BT_BS2_1TQ;
|
||||
|
||||
/** @brief BRS(比特率切换)使能开关。
|
||||
* @note 保留 #define:canfd.cpp 中用 #if 做编译期分支。 */
|
||||
#define CONFIG_CAN_FD_BRS_ENABLE 1U
|
||||
|
||||
/** @brief 启动时向物理总线发送 classic/FD/BRS 探测帧;正常固件默认关闭。 */
|
||||
#define CONFIG_CAN_STARTUP_BUS_TEST_ENABLE 0
|
||||
/** @brief 启动时切换控制器到内部回环执行自检;正常固件默认关闭。 */
|
||||
#define CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE 0
|
||||
|
||||
/** @brief CAN0 通道使能开关:true=启用初始化/收发/自测,false=完全禁用。 */
|
||||
constexpr bool CONFIG_CAN_CH0_ENABLE = true;
|
||||
/** @brief CAN1 通道使能开关。 */
|
||||
constexpr bool CONFIG_CAN_CH1_ENABLE = true;
|
||||
/** @brief CAN2 通道使能开关。 */
|
||||
constexpr bool CONFIG_CAN_CH2_ENABLE = true;
|
||||
/* BRS / 启动探测 / 通道使能:见文件顶部「功能开关」 */
|
||||
|
||||
/** @brief CAN 接收过滤器目标标准帧 ID。 */
|
||||
constexpr uint32_t CONFIG_CAN_RX_FILTER_ID = CONFIG_CAN_STATUS_ID;
|
||||
@@ -159,22 +178,12 @@ 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。 */
|
||||
constexpr uint32_t CONFIG_TASK_PERIOD_DIAG_PRINT_TICKS = 50000U;
|
||||
|
||||
/** @brief 周期温度/电压 UART 打印开关。@note 保留 #define:task_manager.cpp #if 使用。 */
|
||||
#define CONFIG_UART_TEMP_PERIODIC_PRINT 1
|
||||
/** @brief UART 诊断打印级别。@note 保留 #define:task_runner.cpp #if 使用。 */
|
||||
#define CONFIG_UART_DIAG_LEVEL 1
|
||||
/** @brief SPI 命令逐帧日志开关。@note 保留 #define:#if 使用。 */
|
||||
#define CONFIG_SPI_APP_LOG_ENABLE 0
|
||||
/** @brief CAN_SEND 成功帧详细日志开关。@note 保留 #define:#if 使用。 */
|
||||
#define CONFIG_SPI_CAN_SEND_VERBOSE_LOG 0
|
||||
|
||||
/* UART/SPI 调试开关:见文件顶部「功能开关」 */
|
||||
|
||||
/** @brief FWDGT 独立看门狗预分频值。 */
|
||||
constexpr uint32_t CONFIG_FWDGT_PSC = FWDGT_PSC_DIV64;
|
||||
@@ -207,29 +216,21 @@ constexpr uint32_t CONFIG_IRQ_PREEMPT_ETHERCAT_ESC = 1U;
|
||||
constexpr uint32_t CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC1 = 1U;
|
||||
constexpr uint32_t CONFIG_IRQ_PREEMPT_SPI_USART = 3U;
|
||||
/* ============================================================
|
||||
* 主循环与实时策略
|
||||
* EtherCAT LAN9252 配置(开关见顶部 CONFIG_MAIN_LOOP_WFI / ETHERCAT_SYNC_MODE)
|
||||
* ============================================================ */
|
||||
/** @brief 主循环 WFI 开关。@note 保留 #define:main.cpp #if 使用。 */
|
||||
#define CONFIG_MAIN_LOOP_WFI_ENABLE 0
|
||||
|
||||
/* ============================================================
|
||||
* EtherCAT LAN9252 使能与配置
|
||||
* ============================================================ */
|
||||
/** @brief SYNC0 同步模式。@note 保留 #define:#if 使用。 */
|
||||
#define CONFIG_ETHERCAT_SYNC_MODE 0
|
||||
|
||||
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)。 */
|
||||
@@ -272,6 +273,10 @@ constexpr uint32_t CONFIG_EXMC_D13_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC
|
||||
constexpr uint32_t CONFIG_EXMC_D14_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_D14_PIN = GPIO_PIN_9;
|
||||
constexpr uint32_t CONFIG_EXMC_D15_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_D15_PIN = GPIO_PIN_10;
|
||||
constexpr uint32_t CONFIG_EXMC_A16_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_A16_PIN = GPIO_PIN_11;
|
||||
/* A17 (PD12) 用于 HBI Indexed Mode 访问 HBI_DATA_0 寄存器,已将 LAN9252 A2 (Pin 29) 接到 PD12。
|
||||
* 注意 LAN9252 引脚顺序:Pin25=A1, Pin26=A3, Pin27=A4, Pin28=CS, Pin29=A2。
|
||||
* A2 是 IDX/DATA 选择位,必须接到 PD12 才能访问 DATA 寄存器,EEPROM 烧录功能可用。 */
|
||||
constexpr uint32_t CONFIG_EXMC_A17_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_A17_PIN = GPIO_PIN_12;
|
||||
constexpr uint32_t CONFIG_EXMC_NOE_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_NOE_PIN = GPIO_PIN_4;
|
||||
constexpr uint32_t CONFIG_EXMC_NWE_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_NWE_PIN = GPIO_PIN_5;
|
||||
constexpr uint32_t CONFIG_EXMC_NE1_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_NE1_PIN = GPIO_PIN_7;
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
/**
|
||||
* @file initial.cpp
|
||||
* @brief 应用启动初始化实现。
|
||||
* @brief 应用启动初始化:模块成功置位,结束统一报告结果。
|
||||
*
|
||||
* 顺序:
|
||||
* NVIC → SysTick → ESP32_EN 保持 → USART0 → AppSpi(尽早 HELLO 探活)→
|
||||
* ADC → CANFD → OtaBootRequest → TaskManager → Scheduler →
|
||||
* EtherCAT(+Sync 可选) → Watchdog → report
|
||||
*/
|
||||
|
||||
#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"
|
||||
@@ -19,49 +23,129 @@
|
||||
#include "comm/ethercat_slave.h"
|
||||
#include "comm/ethercat_sync.h"
|
||||
|
||||
/**
|
||||
* @brief 执行应用级外设与调度初始化。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 依次完成 SysTick、USART0、ADC/温度采样、CANFD、任务管理器与调度定时器的启动。
|
||||
*/
|
||||
uint32_t AppInitializer::s_mask = 0U;
|
||||
|
||||
namespace {
|
||||
|
||||
struct InitEntry {
|
||||
uint32_t bit;
|
||||
const char *name;
|
||||
bool critical;
|
||||
};
|
||||
|
||||
static const InitEntry kEntries[] = {
|
||||
{ AppInitializer::kNvic, "nvic", true },
|
||||
{ AppInitializer::kSysTick, "systick", true },
|
||||
{ AppInitializer::kEsp32Hold, "esp32_hold", true },
|
||||
{ AppInitializer::kUsart0, "usart0", true },
|
||||
{ AppInitializer::kAppSpi, "app_spi", true },
|
||||
{ AppInitializer::kAdc, "adc", true },
|
||||
{ AppInitializer::kCanFd, "canfd", true },
|
||||
{ AppInitializer::kOtaBootReq, "ota_boot_req", true },
|
||||
{ AppInitializer::kTaskManager, "task_manager", true },
|
||||
{ AppInitializer::kScheduler, "scheduler", true },
|
||||
{ AppInitializer::kEthercat, "ethercat", false },
|
||||
{ AppInitializer::kEthercatSync, "ethercat_sync", false },
|
||||
{ AppInitializer::kWatchdog, "watchdog", true },
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void AppInitializer::mark(ModuleBit b)
|
||||
{
|
||||
s_mask |= static_cast<uint32_t>(b);
|
||||
}
|
||||
|
||||
void AppInitializer::report()
|
||||
{
|
||||
bool critical_ok = true;
|
||||
bool all_ok = true;
|
||||
char fail_list[160] = {0};
|
||||
size_t fail_len = 0U;
|
||||
|
||||
for (size_t i = 0; i < sizeof(kEntries) / sizeof(kEntries[0]); ++i) {
|
||||
const InitEntry &e = kEntries[i];
|
||||
const bool ok = (s_mask & e.bit) != 0U;
|
||||
if (ok) {
|
||||
continue;
|
||||
}
|
||||
all_ok = false;
|
||||
if (e.critical) {
|
||||
critical_ok = false;
|
||||
}
|
||||
if (fail_len + 2U < sizeof(fail_list)) {
|
||||
if (fail_len > 0U) {
|
||||
fail_list[fail_len++] = ',';
|
||||
}
|
||||
const char *p = e.name;
|
||||
while (*p != '\0' && fail_len + 1U < sizeof(fail_list)) {
|
||||
fail_list[fail_len++] = *p++;
|
||||
}
|
||||
fail_list[fail_len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (critical_ok && all_ok) {
|
||||
printf("[APP] init: OK (all modules)\r\n");
|
||||
} else if (critical_ok) {
|
||||
printf("[APP] init: OK (critical), skipped/failed: %s\r\n",
|
||||
fail_list[0] != '\0' ? fail_list : "?");
|
||||
} else {
|
||||
printf("[APP] init: FAIL, modules: %s\r\n",
|
||||
fail_list[0] != '\0' ? fail_list : "?");
|
||||
}
|
||||
}
|
||||
|
||||
void AppInitializer::init()
|
||||
{
|
||||
/* 必须最先设置 NVIC 优先级分组:确保后续 CanFd/SPI0 等外设 init 中
|
||||
* 编码抢占/子优先级字节时使用正确的分组(PRE4_SUB0),避免在错误分组
|
||||
* 下编码后被重新解释。EtherCAT 实时架构整改 Task3。 */
|
||||
s_mask = 0U;
|
||||
|
||||
nvic_priority_group_set(CONFIG_NVIC_PRIORITY_GROUP);
|
||||
printf("[APP] init: SysTick...\r\n");
|
||||
mark(kNvic);
|
||||
|
||||
SysTickTimer::initMs();
|
||||
/* ESP32 EN/RESET (PC9) 直接配置为推挽输出高电平,保持 ESP32 正常运行 */
|
||||
mark(kSysTick);
|
||||
|
||||
rcu_periph_clock_enable(RCU_GPIOC);
|
||||
gpio_bit_set(CONFIG_ESP32_RESET_PORT, CONFIG_ESP32_RESET_PIN);
|
||||
gpio_init(CONFIG_ESP32_RESET_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, CONFIG_ESP32_RESET_PIN);
|
||||
printf("[APP] init: USART0...\r\n");
|
||||
mark(kEsp32Hold);
|
||||
|
||||
Usart0::configurePins();
|
||||
Usart0::init();
|
||||
printf("[APP] init: TempSensor...\r\n");
|
||||
TempSensor::init();
|
||||
printf("[APP] init: CANFD...\r\n");
|
||||
CanFd::init();
|
||||
printf("[APP] init: OtaBootRequest...\r\n");
|
||||
OtaBootRequest::init();
|
||||
printf("[APP] init: AppSpiCommand...\r\n");
|
||||
mark(kUsart0);
|
||||
|
||||
/* SPI 尽早就绪:OTA FINISH 跳转后 ESP32 用 HELLO 探活,不能等 CAN 上电延时 */
|
||||
AppSpiCommandBus.init();
|
||||
printf("[APP] init: TaskManager...\r\n");
|
||||
mark(kAppSpi);
|
||||
|
||||
Adc::init();
|
||||
mark(kAdc);
|
||||
|
||||
CanFd::init();
|
||||
/* L0 通过才置 canfd 位 */
|
||||
if (CanFd::selfTestL0()) {
|
||||
mark(kCanFd);
|
||||
}
|
||||
|
||||
OtaBootRequest::init();
|
||||
mark(kOtaBootReq);
|
||||
|
||||
TaskManager::init();
|
||||
printf("[APP] init: SchedulerTimer...\r\n");
|
||||
mark(kTaskManager);
|
||||
|
||||
SchedulerTimer::init();
|
||||
printf("[APP] init: EthercatSlave...\r\n");
|
||||
mark(kScheduler);
|
||||
|
||||
EthercatSlave::init();
|
||||
if (EthercatSlave::isEscReady()) {
|
||||
printf("[APP] init: EthercatSync...\r\n");
|
||||
mark(kEthercat);
|
||||
EthercatSync::init();
|
||||
} else {
|
||||
printf("[APP] init: EthercatSync SKIPPED (ESC not ready)\r\n");
|
||||
mark(kEthercatSync);
|
||||
}
|
||||
printf("[APP] init: Watchdog...\r\n");
|
||||
|
||||
Watchdog::init();
|
||||
printf("[APP] init: all done\r\n");
|
||||
mark(kWatchdog);
|
||||
|
||||
report();
|
||||
}
|
||||
|
||||
@@ -1,20 +1,41 @@
|
||||
#ifndef SYS_INITIAL_H
|
||||
#define SYS_INITIAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @brief 应用初始化器。
|
||||
* @note 负责按固定顺序组织系统时基、串口、采样、通信、调度器等底层模块启动。
|
||||
* @brief 应用初始化器:按固定顺序启动模块,成功位置 1,末尾汇总。
|
||||
*/
|
||||
class AppInitializer {
|
||||
public:
|
||||
/** 启动模块位(成功置 1) */
|
||||
enum ModuleBit : uint32_t {
|
||||
kNvic = 1u << 0,
|
||||
kSysTick = 1u << 1,
|
||||
kEsp32Hold = 1u << 2,
|
||||
kUsart0 = 1u << 3,
|
||||
kAdc = 1u << 4,
|
||||
kCanFd = 1u << 5,
|
||||
kOtaBootReq = 1u << 6,
|
||||
kAppSpi = 1u << 7,
|
||||
kTaskManager = 1u << 8,
|
||||
kScheduler = 1u << 9,
|
||||
kEthercat = 1u << 10,
|
||||
kEthercatSync = 1u << 11,
|
||||
kWatchdog = 1u << 12,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 按固定顺序完成应用所有底层模块初始化。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 初始化顺序:SysTick → USART0 GPIO/外设 → 温度采样 → 等待 ADC DMA 就绪 → CANFD → 任务调度 → 调度定时器。
|
||||
* @brief 按固定顺序完成模块初始化(过程只置位,结束统一报告成败/失败模块)。
|
||||
*/
|
||||
static void init();
|
||||
|
||||
private:
|
||||
static uint32_t s_mask;
|
||||
|
||||
static void mark(ModuleBit b);
|
||||
static void report();
|
||||
};
|
||||
|
||||
#endif /* SYS_INITIAL_H */
|
||||
#endif /* SYS_INITIAL_H */
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 由 TIMER2 ISR 调用;按周期分档置位 ADC/CAN/LED/心跳/诊断等任务标志。
|
||||
* @note 由 TIMER2 ISR 调用;按周期分档置位 ADC/CAN/心跳/诊断等任务标志。
|
||||
*/
|
||||
static void tick100us();
|
||||
|
||||
|
||||
@@ -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,23 +207,35 @@ void TaskRunner::taskCanRxParse()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 执行状态灯维护任务。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 当前仅保留任务入口。
|
||||
*/
|
||||
void TaskRunner::taskStatusLed() { /* 预留 */ }
|
||||
|
||||
/**
|
||||
* @brief 执行系统心跳维护任务。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 当前任务:推进 OTA Boot 请求、刷新 CAN 反馈快照、启动反馈推送轮次。
|
||||
* @note 推进 OTA Boot 请求、刷新 CAN 反馈快照、启动 CAN/ADC 1Hz 推送轮次。
|
||||
*/
|
||||
void TaskRunner::taskHeartbeat() { OtaBootRequest::process(); CanFd::refreshFeedbackSnapshot(); canFeedbackStartRound(); }
|
||||
void TaskRunner::taskHeartbeat()
|
||||
{
|
||||
OtaBootRequest::process();
|
||||
CanFd::refreshFeedbackSnapshot();
|
||||
canFeedbackStartRound();
|
||||
/* ADC 调试反馈 1Hz:CONFIG_SPI_ADC_FEEDBACK_ENABLE=0 时为空操作 */
|
||||
adcFeedbackStartRound();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 拷贝当前 ADC 工程量快照(供 SPI 0xC4 等只读路径)。
|
||||
* @param[out] out 非空时写入最新 Sample
|
||||
* @return true 已写入(不保证 sample.valid==1)
|
||||
*/
|
||||
bool TaskRunner::copyAdcSample(Adc::Sample *out)
|
||||
{
|
||||
if (out == nullptr) {
|
||||
return false;
|
||||
}
|
||||
*out = s_runtime.sample;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 输出当前温度、电压与 CAN 诊断信息。
|
||||
@@ -254,7 +265,7 @@ void TaskRunner::taskDiagnosticPrint()
|
||||
static_cast<unsigned long>(vin_adc_mV / 1000U),
|
||||
static_cast<unsigned long>(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<unsigned int>(s.valid),
|
||||
static_cast<long>(temp_x100 / 100),
|
||||
@@ -272,7 +283,6 @@ void TaskRunner::taskDiagnosticPrint()
|
||||
static_cast<unsigned long>(diag->rx_drop_count),
|
||||
static_cast<unsigned long>(diag->rx_fifo_full_count),
|
||||
static_cast<unsigned long>(diag->rx_fifo_overfull_count),
|
||||
static_cast<unsigned long>(diag->tx_drop_count),
|
||||
static_cast<unsigned long>(diag->tx_abort_count),
|
||||
static_cast<unsigned long>(diag->error_count));
|
||||
#endif
|
||||
@@ -340,9 +350,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();
|
||||
|
||||
@@ -356,6 +363,8 @@ void TaskRunner::runPending()
|
||||
* 替代 ESP32 主动轮询。非阻塞:上一帧未读走时立即返回。
|
||||
* 每次主循环迭代调用一次,ESP32 读走一帧后 CS 释放中断唤醒主循环继续推送。 */
|
||||
canFeedbackPushStep();
|
||||
/* ADC 调试反馈(0xC4):优先级低于 CAN;ring 忙则跳过。可 config 关闭。 */
|
||||
adcFeedbackPushStep();
|
||||
|
||||
/* 周期路径( SYNC0 ISR preempt=0 )用 s_periodicSeq 序列号通知主循环,
|
||||
* 不再走 task_flags,避免 BASEPRI 临界区与 preempt=0 的 RMW 竞态。
|
||||
@@ -375,9 +384,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();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef SYS_TASK_RUNNER_H
|
||||
#define SYS_TASK_RUNNER_H
|
||||
|
||||
#include "device/adc.h"
|
||||
|
||||
/**
|
||||
* @brief 任务执行器。
|
||||
* @note 负责消费 TaskManager 置位的任务,并驱动各模块完成实际工作。
|
||||
@@ -25,6 +27,13 @@ public:
|
||||
*/
|
||||
static void runPending();
|
||||
|
||||
/**
|
||||
* @brief 拷贝当前 ADC 工程量快照(供 SPI 0xC4 调试反馈等只读路径)。
|
||||
* @param[out] out 非空时写入最新 Sample。
|
||||
* @return true 表示 out 已写入(不保证 valid==1,调用方看 sample.valid)。
|
||||
*/
|
||||
static bool copyAdcSample(Adc::Sample *out);
|
||||
|
||||
private:
|
||||
struct Runtime;
|
||||
static Runtime s_runtime;
|
||||
@@ -39,9 +48,7 @@ private:
|
||||
static void taskCanRxParse();
|
||||
/** @brief CAN 周期发送任务;每 1ms 每通道发送一帧测试报文。 */
|
||||
static void taskCanTxSend();
|
||||
/** @brief 状态灯维护任务(当前预留)。 */
|
||||
static void taskStatusLed();
|
||||
/** @brief 心跳维护任务(当前预留)。 */
|
||||
/** @brief 心跳维护任务。 */
|
||||
static void taskHeartbeat();
|
||||
/** @brief 诊断打印任务。 */
|
||||
static void taskDiagnosticPrint();
|
||||
|
||||
@@ -1,338 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>DCU_APP SPI 通信 ID 参考</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style>
|
||||
:root{--bg:#f6f8fb;--card:#fff;--line:#dfe3ea;--muted:#6b7280;--text:#111827}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;padding:28px 20px 48px;background:var(--bg);color:var(--text);font-family:"Segoe UI","Microsoft YaHei","PingFang SC",Arial,sans-serif;line-height:1.7}
|
||||
.page{max-width:1080px;margin:0 auto}
|
||||
h1{margin:0 0 8px;font-size:22px}
|
||||
.muted{margin:0 0 22px;color:var(--muted);font-size:13px}
|
||||
.stack{display:grid;grid-template-columns:1fr;gap:22px}
|
||||
.card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:18px 20px;box-shadow:0 1px 2px rgba(16,24,40,.04)}
|
||||
h2{margin:0 0 8px;font-size:18px}
|
||||
.sub{margin:0 0 14px;color:var(--muted);font-size:12px}
|
||||
details{border:1px solid var(--line);border-radius:10px;background:#fff;margin-top:12px}
|
||||
summary{cursor:pointer;padding:14px 16px;font-weight:600}
|
||||
.inside{padding:0 16px 16px}
|
||||
table{width:100%;border-collapse:collapse;margin:10px 0 16px;font-size:14px}
|
||||
th,td{border:1px solid var(--line);padding:10px 12px;vertical-align:top}
|
||||
th{background:#f1f4f8;text-align:left;width:180px}
|
||||
.note{background:#f8fafc;border:1px solid var(--line);border-radius:10px;padding:12px 14px;color:#374151;font-size:13px}
|
||||
code{background:#f8fafc;padding:1px 4px;border-radius:4px}
|
||||
.diagram{display:grid;grid-template-columns:1fr;gap:14px;margin:12px 0 16px}
|
||||
.cell{flex:0 1 150px;min-width:120px;border:1px solid var(--line);border-radius:10px;background:#fff;overflow:hidden;page-break-inside:avoid;break-inside:avoid}
|
||||
.row{display:flex;flex-wrap:wrap;gap:12px;margin:10px 0 14px;align-items:stretch}
|
||||
.cell{border:1px solid var(--line);border-radius:10px;background:#fff;overflow:hidden}
|
||||
.cell .top{padding:8px 10px;font-weight:700;background:#eef2f8;text-align:center;font-size:14px}
|
||||
.cell .mid{padding:8px 10px;text-align:center;font-size:13px;color:#111}
|
||||
.cell .bot{padding:8px 10px;border-top:1px solid var(--line);color:#374151;font-size:12px;text-align:center}
|
||||
.cell.error .top{background:#fee2e2}
|
||||
.cell.ok .top{background:#dcfce7}
|
||||
.cell.info .top{background:#dbeafe}
|
||||
.foot{margin-top:22px;color:var(--muted);font-size:12px}
|
||||
.section-title{margin:18px 0 8px;font-size:14px;font-weight:600}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<h1>DCU_APP SPI 通信 ID(按注册命令展开,三行直观图)</h1>
|
||||
<p class="muted">按 <code>device/spi/app_spi_command.cpp</code> 注册顺序组织;每个命令均可展开查看 payload,并用“序号 / 组合 / 说明”三行显示。</p>
|
||||
<div class="note" style="margin-bottom:18px">⚠ 单帧 payload 最大 <code>123</code> 字节(<code>kTxBufSize=128</code> - 协议开销 5 字节),超过将被 <code>AppSpiCommand::processCommand()</code> 拒收。</div>
|
||||
|
||||
<div class="stack">
|
||||
|
||||
<section class="card">
|
||||
<h2>Boot Request</h2>
|
||||
<div class="sub">注册函数:<code>registerSpiBootRequestCommand</code></div>
|
||||
<details>
|
||||
<summary>展开查看 ID / payload / 功能</summary>
|
||||
<div class="inside">
|
||||
<table>
|
||||
<tr><th>命令/响应</th><th>符号</th><th>值</th></tr>
|
||||
<tr><td>命令帧类型</td><td><code>kTypeBootReq</code></td><td><code>0xB0</code></td></tr>
|
||||
<tr><td>成功响应</td><td><code>kRspAck</code></td><td><code>0x80</code></td></tr>
|
||||
</table>
|
||||
<div class="section-title">payload 直观图</div>
|
||||
<div class="diagram">
|
||||
<div class="cell info">
|
||||
<div class="top">req</div>
|
||||
<div class="mid">—</div>
|
||||
<div class="bot">无可扩展 payload 字段</div>
|
||||
</div>
|
||||
<div class="cell ok">
|
||||
<div class="top">ack</div>
|
||||
<div class="mid">0x80</div>
|
||||
<div class="bot">成功应答</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note">功能:请求进入 Boot 状态,回 ACK。</div>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>OTA Wait</h2>
|
||||
<div class="sub">注册函数:<code>registerSpiOtaWaitCommand</code></div>
|
||||
<details>
|
||||
<summary>展开查看 ID / payload / 功能</summary>
|
||||
<div class="inside">
|
||||
<table>
|
||||
<tr><th>命令</th><th>符号</th><th>值</th></tr>
|
||||
<tr><td>OTA 请求</td><td><code>kTypeOtaReq</code></td><td><code>0x01</code></td></tr>
|
||||
<tr><td>OTA 数据</td><td><code>kTypeOtaData</code></td><td><code>0x02</code></td></tr>
|
||||
<tr><td>OTA 取消</td><td><code>kTypeOtaCancel</code></td><td><code>0x03</code></td></tr>
|
||||
<tr><td>响应</td><td><code>kRspWait</code></td><td><code>0x81</code></td></tr>
|
||||
</table>
|
||||
|
||||
<div class="section-title">response payload 直观图</div>
|
||||
<div class="diagram">
|
||||
<div class="cell info">
|
||||
<div class="top">[0]</div>
|
||||
<div class="mid">0x01</div>
|
||||
<div class="bot">hint 固定值</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="note">功能:OTA 占位/流控,占位/流控。</div>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>App Ping</h2>
|
||||
<div class="sub">注册函数:<code>registerSpiAppPingCommand</code></div>
|
||||
<details>
|
||||
<summary>展开查看 ID / payload / 功能</summary>
|
||||
<div class="inside">
|
||||
<table>
|
||||
<tr><th>命令/响应</th><th>符号</th><th>值</th></tr>
|
||||
<tr><td>命令帧类型</td><td><code>kTypeAppPing</code></td><td><code>0xA1</code></td></tr>
|
||||
<tr><td>响应</td><td><code>kRspAck</code></td><td><code>0x80</code></td></tr>
|
||||
</table>
|
||||
|
||||
<div class="section-title">response payload 直观图</div>
|
||||
<div class="diagram">
|
||||
<div class="row">
|
||||
<div class="cell info"><div class="top">[0]</div><div class="mid">0xA1</div><div class="bot">magic</div></div>
|
||||
<div class="cell info"><div class="top">[1]</div><div class="mid">0x55</div><div class="bot">APP version tag</div></div>
|
||||
<div class="cell ok"><div class="top">[2..5]</div><div class="mid">APP_VER</div><div class="bot">MAJOR/MINOR/PATCH/BUILD</div></div>
|
||||
<div class="cell info"><div class="top">[6]</div><div class="mid">0xAA</div><div class="bot">BOOT version tag</div></div>
|
||||
<div class="cell ok"><div class="top">[7..10]</div><div class="mid">BOOT_VER</div><div class="bot">Boot版本</div></div>
|
||||
<div class="cell ok"><div class="top">[11..14]</div><div class="mid">uptime</div><div class="bot">运行秒数</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="note">功能:读取版本与 uptime。</div>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>CAN Bridge</h2>
|
||||
<div class="sub">注册函数:<code>registerSpiCanBridgeCommand</code></div>
|
||||
<details>
|
||||
<summary>展开查看 ID / payload / 功能</summary>
|
||||
<div class="inside">
|
||||
<table>
|
||||
<tr><th>命令/响应</th><th>符号</th><th>值</th></tr>
|
||||
<tr><td>命令帧类型</td><td><code>kTypeCanSend</code></td><td><code>0xC0</code></td></tr>
|
||||
<tr><td>成功响应</td><td><code>kRspAck</code></td><td><code>0x80</code></td></tr>
|
||||
<tr><td>错误响应</td><td><code>kRspError</code></td><td><code>0x82</code></td></tr>
|
||||
</table>
|
||||
|
||||
<div class="section-title">request payload 直观图</div>
|
||||
<div class="diagram">
|
||||
<div class="row">
|
||||
<div class="cell ok"><div class="top">[0]</div><div class="mid">can_ch</div><div class="bot">通道 0/1/2</div></div>
|
||||
<div class="cell info"><div class="top">[1..4]</div><div class="mid">can_id</div><div class="bot">CAN ID(32bit)</div></div>
|
||||
<div class="cell info"><div class="top">[5..N]</div><div class="mid">can_data</div><div class="bot">数据区</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-title">response payload 直观图</div>
|
||||
<div class="diagram">
|
||||
<div class="row">
|
||||
<div class="cell ok"><div class="top">ack</div><div class="mid">0x00</div><div class="bot">OK</div></div>
|
||||
<div class="cell error"><div class="top">err</div><div class="mid">E0/E1/E2</div><div class="bot">短帧/通道/忙</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="note">错误码:<code>E0</code>=payload 不足,<code>E1</code>=通道非法,<code>E2</code>=发送忙/失败。</div>
|
||||
<div class="note">功能:ESP32 透传 CANFD 发送帧,校验后发送。</div>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>CAN Feedback (ID Snapshot) / Set Mode</h2>
|
||||
<div class="sub">注册函数:<code>registerSpiCanFeedbackCommand</code></div>
|
||||
<details>
|
||||
<summary>展开查看 ID / payload / 功能</summary>
|
||||
<div class="inside">
|
||||
<table>
|
||||
<tr><th>命令</th><th>符号</th><th>值</th></tr>
|
||||
<tr><td>反馈查询(分页)</td><td><code>kTypeCanFeedback</code></td><td><code>0xC1</code></td></tr>
|
||||
<tr><td>模式切换</td><td><code>kTypeCanSetMode</code></td><td><code>0xC2</code></td></tr>
|
||||
<tr><td>成功响应</td><td><code>kRspAck</code></td><td><code>0x80</code></td></tr>
|
||||
<tr><td>错误响应</td><td><code>kRspError</code></td><td><code>0x82</code></td></tr>
|
||||
</table>
|
||||
|
||||
<div class="section-title">0xC1 request payload 直观图(分页请求)</div>
|
||||
<div class="diagram">
|
||||
<div class="row">
|
||||
<div class="cell ok"><div class="top">[0]</div><div class="mid">channel</div><div class="bot">通道 0/1/2</div></div>
|
||||
<div class="cell ok"><div class="top">[1]</div><div class="mid">page</div><div class="bot">页号 0 或 1</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-title">0xC1 response payload 直观图(统计头 + ID 快照条目)</div>
|
||||
<div class="sub">应答变长,最大 123 字节。固定头 25B + 条目逐条拼接。</div>
|
||||
<div class="diagram">
|
||||
<div class="sub" style="font-weight:600;">固定头(25 字节)</div>
|
||||
<div class="row">
|
||||
<div class="cell ok"><div class="top">[0]</div><div class="mid">channel</div><div class="bot">回显通道 0/1/2</div></div>
|
||||
<div class="cell ok"><div class="top">[1]</div><div class="mid">page</div><div class="bot">回显页号</div></div>
|
||||
<div class="cell info"><div class="top">[2]</div><div class="mid">total_count</div><div class="bot">ID 条目总数(0~16)</div></div>
|
||||
<div class="cell info"><div class="top">[3]</div><div class="mid">entry_count</div><div class="bot">本页条目数</div></div>
|
||||
<div class="cell info"><div class="top">[4]</div><div class="mid">mode</div><div class="bot">当前接收模式</div></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="cell info"><div class="top">[5..8]</div><div class="mid">snapshot_seq</div><div class="bot">快照序号(LE)</div></div>
|
||||
<div class="cell info"><div class="top">[9..12]</div><div class="mid">rx_count</div><div class="bot">通道累计接收(LE)</div></div>
|
||||
<div class="cell info"><div class="top">[13..16]</div><div class="mid">tx_count</div><div class="bot">通道累计发送(LE)</div></div>
|
||||
<div class="cell info"><div class="top">[17..20]</div><div class="mid">rx_drop_count</div><div class="bot">通道丢帧(LE)</div></div>
|
||||
<div class="cell info"><div class="top">[21..24]</div><div class="mid">error_count</div><div class="bot">通道错误(LE)</div></div>
|
||||
</div>
|
||||
<div class="sub" style="font-weight:600;margin-top:14px;">条目区(从 [25] 开始,逐条拼接,变长)</div>
|
||||
<div class="row">
|
||||
<div class="cell ok"><div class="top">[+0..+3]</div><div class="mid">id</div><div class="bot">CAN 报文 ID(LE)</div></div>
|
||||
<div class="cell info"><div class="top">[+4]</div><div class="mid">dlc</div><div class="bot">数据长度 1~64</div></div>
|
||||
<div class="cell info"><div class="top">[+5]</div><div class="mid">flags</div><div class="bot">bit0=FD bit1=BRS bit2=ESI</div></div>
|
||||
<div class="cell info"><div class="top">[+6..+7]</div><div class="mid">reserved</div><div class="bot">0x00</div></div>
|
||||
<div class="cell ok"><div class="top">[+8..+N]</div><div class="mid">data</div><div class="bot">报文数据(dlc 字节)</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="note">
|
||||
<b>条目大小</b> = 8 + dlc 字节。标准 CAN (dlc=8):14B/条,每页最多 8 条。CANFD (dlc=64):72B/条,每页最多 1 条。<br/>
|
||||
<b>total_count</b> = 实际不重复 ID 数(0~16,不满 16 就是实际值)。total_count ≤ 8 时 page=1 的 entry_count=0。<br/>
|
||||
<b>ESP32 每轮</b>:每通道发 page=0 和 page=1 两次请求(共 6 次 SPI/s),拉完整张表。
|
||||
</div>
|
||||
|
||||
<div class="section-title">0xC2 request payload 直观图</div>
|
||||
<div class="diagram">
|
||||
<div class="row">
|
||||
<div class="cell ok"><div class="top">[0]</div><div class="mid">mode</div><div class="bot">0=全收 1=过滤</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-title">0xC2 response payload 直观图</div>
|
||||
<div class="diagram">
|
||||
<div class="row">
|
||||
<div class="cell ok"><div class="top">[0]</div><div class="mid">actual</div><div class="bot">生效模式</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note">错误码:<code>E0</code>=payload 不足,<code>E1</code>=参数非法(含非法通道),<code>E3</code>=页号非法。</div>
|
||||
<div class="note">注:<code>E2</code>=快照不可用 仅用于 0xC3,不在 0xC1/0xC2 中返回。</div>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>ID Snapshot(仅 ID 快照表)</h2>
|
||||
<div class="sub">注册函数:<code>registerSpiIdSnapshotCommand</code></div>
|
||||
<details>
|
||||
<summary>展开查看 ID / payload / 功能</summary>
|
||||
<div class="inside">
|
||||
<table>
|
||||
<tr><th>命令/响应</th><th>符号</th><th>值</th></tr>
|
||||
<tr><td>命令帧类型</td><td><code>kTypeIdSnapshot</code></td><td><code>0xC3</code></td></tr>
|
||||
<tr><td>成功响应</td><td><code>kRspAck</code></td><td><code>0x80</code></td></tr>
|
||||
<tr><td>错误响应</td><td><code>kRspError</code></td><td><code>0x82</code></td></tr>
|
||||
</table>
|
||||
|
||||
<div class="section-title">0xC3 request payload 直观图(分页请求)</div>
|
||||
<div class="diagram">
|
||||
<div class="row">
|
||||
<div class="cell ok"><div class="top">[0]</div><div class="mid">channel</div><div class="bot">通道 0/1/2</div></div>
|
||||
<div class="cell ok"><div class="top">[1]</div><div class="mid">page</div><div class="bot">页号 0 或 1</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-title">0xC3 response payload 直观图(轻量快照头 + ID 条目)</div>
|
||||
<div class="sub">应答变长,最大 123 字节。固定头 4B + 条目逐条拼接。</div>
|
||||
<div class="diagram">
|
||||
<div class="sub" style="font-weight:600;">固定头(4 字节)</div>
|
||||
<div class="row">
|
||||
<div class="cell ok"><div class="top">[0]</div><div class="mid">channel</div><div class="bot">回显通道 0/1/2</div></div>
|
||||
<div class="cell ok"><div class="top">[1]</div><div class="mid">page</div><div class="bot">回显页号</div></div>
|
||||
<div class="cell info"><div class="top">[2]</div><div class="mid">total_count</div><div class="bot">ID 条目总数(0~16)</div></div>
|
||||
<div class="cell info"><div class="top">[3]</div><div class="mid">entry_count</div><div class="bot">本页条目数</div></div>
|
||||
</div>
|
||||
<div class="sub" style="font-weight:600;margin-top:14px;">条目区(从 [4] 开始,逐条拼接,变长)</div>
|
||||
<div class="row">
|
||||
<div class="cell ok"><div class="top">[+0..+3]</div><div class="mid">id</div><div class="bot">CAN 报文 ID(LE)</div></div>
|
||||
<div class="cell info"><div class="top">[+4..+7]</div><div class="mid">seq</div><div class="bot">该 ID 更新序号(LE)</div></div>
|
||||
<div class="cell info"><div class="top">[+8..+11]</div><div class="mid">rx_count</div><div class="bot">该 ID 累计接收(LE)</div></div>
|
||||
<div class="cell info"><div class="top">[+12]</div><div class="mid">dlc</div><div class="bot">数据长度 1~64</div></div>
|
||||
<div class="cell info"><div class="top">[+13]</div><div class="mid">flags</div><div class="bot">bit0=FD bit1=BRS bit2=ESI</div></div>
|
||||
<div class="cell info"><div class="top">[+14..+15]</div><div class="mid">reserved</div><div class="bot">0x00</div></div>
|
||||
<div class="cell ok"><div class="top">[+16..+N]</div><div class="mid">data</div><div class="bot">报文数据(dlc 字节)</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="note">
|
||||
<b>条目大小</b> = 16 + dlc 字节。标准 CAN (dlc=8):24B/条,每页最多 5 条。CANFD (dlc=64):80B/条,每页最多 1 条。<br/>
|
||||
<b>total_count</b> = 实际不重复 ID 数(0~16,不满 16 就是实际值)。total_count ≤ 8 时 page=1 的 entry_count=0。<br/>
|
||||
<b>ESP32 行为</b>:当前代码实现了 0xC3,但主链路主要通过 0xC1 拉取“统计头 + ID 快照表”。
|
||||
</div>
|
||||
|
||||
<div class="note">错误码:<code>E0</code>=payload 不足,<code>E1</code>=通道非法,<code>E2</code>=快照不可用,<code>E3</code>=页号非法。</div>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>公共响应 / 协议常量</h2>
|
||||
<div class="sub">来源:<code>app_spi_command_defs.h</code>、<code>app_spi_command.cpp</code></div>
|
||||
<details>
|
||||
<summary>展开查看公共 ID / payload</summary>
|
||||
<div class="inside">
|
||||
<div class="section-title">公共帧头与响应</div>
|
||||
<div class="diagram">
|
||||
<div class="cell info"><div class="top">SOF</div><div class="mid">0xA5</div><div class="bot">起始帧头</div></div>
|
||||
<div class="cell ok"><div class="top">ACK</div><div class="mid">0x80</div><div class="bot">成功</div></div>
|
||||
<div class="cell info"><div class="top">WAIT</div><div class="mid">0x81</div><div class="bot">等待中</div></div>
|
||||
<div class="cell error"><div class="top">ERR</div><div class="mid">0x82</div><div class="bot">错误</div></div>
|
||||
</div>
|
||||
<div class="note">公共响应 payload 长度与含义由各命令自行定义。</div>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<p class="foot">说明:CAN ID 在 CAN Bridge/Feedback 场景下为运行时值,不在文档中固化为常量。</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user