Compare commits

...
3 Commits
Author SHA1 Message Date
cbb 934836618a feat(spi): 添加ADC反馈命令处理
新增SPI命令类型0xC4用于ADC调试反馈,实现在GD32上采集
温度/VDDA/VIN等模拟量数据的反馈功能,并支持主动推送和被动查询模式。

feat(spi): 扩展PING命令响应格式

修改PING命令响应格式,增加APP身份信息16字节载荷,包含版本号、
启动时间、功能开关位图等信息,用于主机获取从设备详细状态。

refactor(can_feedback): 优化CAN反馈推送逻辑

改进CAN反馈推送机制,跳过编译期关闭的通道,只遍历启用的通道,
确保推送效率并避免无效操作。

feat(comm): 添加EXMC总线初始化和诊断功能

添加了EXMC总线的初始化功能,包括GPIO配置、NOR/SRAM区域参数设置,
以及总线探活机制。实现了总线可见性检测,当检测到全0或全1的挂死状态时
会返回false并输出错误信息。同时添加了printf日志输出用于调试。

refactor(esc_lan9252): 重构ESC初始化返回值

将EscLan9252::init()方法的返回类型从void改为bool,
使其能够返回EXMC总线准备状态,便于上层进行状态检查。

docs(app_spi_cmd_ping): 添加代码注释文档

为SPI命令处理相关类和方法添加详细的Doxygen风格注释文档,
提高代码可读性和维护性。
2026-07-20 02:36:56 +08:00
cbb fedd39b902 feat(canfd): 添加CANFD硬件自检功能并优化初始化时序
- 新增s_channelHwOk数组跟踪各通道硬件初始化状态
- 修改CANFD初始化流程,在GPIO上电后延时200ms再进入Normal模式
- 实现selfTestL0()轻量自检函数,检查硬件初始化、退出init模式、收发器状态等
- 更新CANFD初始化逻辑,只有自检通过才标记为成功

refactor(spi): 调整SPI DMA和EXTI配置顺序避免通信失步

- 将armSpiDmasForNextCs()调用移至configureExti()之前
- 添加注释说明DMA预装和EXTI开启的时序要求
- 避免OTA跳转后ESP32立即发送HELLO导致的SPI从机失步问题

fix(app_spi): 修复HELLO_ACK响应中credit初始化问题

- 在HELLO_ACK构建前将g_localCredit设置为1
- 与requestEsp32Read()保持一致避免TX占用时credit=0的问题
- 防止主机因remoteCredit=0而拒绝发送PING帧

refactor(initial): 重构应用初始化流程添加模块状态跟踪

- 引入ModuleBit枚举和掩码机制跟踪各模块初始化状态
- 实现mark()和report()方法统一管理模块启动结果
- 按NVIC→SysTick→ESP32_EN→USART0→AppSpi→ADC→CANFD→OTA→TaskManager→Scheduler→EtherCAT→Watchdog顺序启动
- 将CANFD模块成功条件改为selfTestL0()通过才标记成功
2026-07-17 02:03:00 +08:00
cbb 52d7f9be76 feat(device_exmc): 添加GD32E508 EXMC外设支持用于LAN9252通信
添加EXMC 16-bit并行总线驱动实现,用于LAN9252 ESC通信。
在cproject.yml中添加device_exmc组和相关源文件,包括
gd32e50x_exmc.c/h文件,并更新头文件搜索路径。

由于GigaDevice DFP未将EXMC注册为CMSIS组件,需要本地编入
RTE/Device目录下的EXMC源文件以支持LAN9252并行总线功能。

fix(canfd): 优化CANFD驱动中的条件编译和DLC处理逻辑

将CANFD相关的配置项从硬编码改为使用sys/config.h中的配置。
添加对DLC=0情况的支持(表示无数据负载的远程帧),
修正了数据长度检查逻辑,确保DLC为0时不需要提供数据指针。

同时重构了调试代码的条件编译,使用CONFIG_CAN_STARTUP_DIAG_VERBOSE
等配置开关控制调试输出函数的编译。

refactor(ethercat): 统一配置项管理,移除硬编码常量

将EtherCAT相关配置项从硬编码改为统一的sys/config.h配置,
提升代码可维护性。

docs(spi): 更新SPI命令响应缓冲区大小说明

更新CAN桥接和ID快照命令的响应payload大小计算说明,
统一使用AppSpiCommand::kTxBufSize - V2_OVERHEAD作为上限。
2026-07-16 23:00:52 +08:00
34 changed files with 4013 additions and 1582 deletions
@@ -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 */
+330
View File
@@ -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);
}
+74
View File
@@ -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 */
+80 -30
View File
@@ -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))
+9 -2
View File
@@ -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 */
+11 -2
View File
@@ -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()
+10 -3
View File
@@ -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 */
+45 -9
View File
@@ -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))
+1 -3
View File
@@ -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 可触发下限)。 */
+23
View File
@@ -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))
+99 -40
View File
@@ -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] 无。
+16 -13
View File
@@ -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
+26 -9
View File
@@ -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);
-19
View File
@@ -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();
}
-22
View File
@@ -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 */
-4
View File
@@ -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)。 */
+62 -57
View File
@@ -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;
+114 -30
View File
@@ -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();
}
+29 -8
View File
@@ -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 */
-7
View File
@@ -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++;
+1 -1
View File
@@ -24,7 +24,7 @@ public:
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 由 TIMER2 ISR 调用;按周期分档置位 ADC/CAN/LED/心跳/诊断等任务标志。
* @note 由 TIMER2 ISR 调用;按周期分档置位 ADC/CAN/心跳/诊断等任务标志。
*/
static void tick100us();
+26 -19
View File
@@ -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();
}
+10 -3
View File
@@ -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();
-338
View File
@@ -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 &le; 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 &le; 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>