Initial commit: CCU621_M firmware project with BLE debug link support.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-08 17:28:36 +08:00
commit 9ceb218f80
1597 changed files with 724159 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
# Keil MDK build outputs
Project/Objects/
Project/Listings/
Project/*.uvguix.*
Project/*.uvguix
*.dep
*.crf
*.o
*.d
*.axf
*.lnp
*.htm
*.build_log.htm
# IDE / OS
.vscode/
.idea/
*.swp
*~
Thumbs.db
Desktop.ini
+252
View File
@@ -0,0 +1,252 @@
/*!
\file gpio.h
\brief the header file of systick
\version 2026-01-01, for JC
*/
/*
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 "gd32h7xx.h"
#include "gd32h7xx_gpio.h"
#include "gpio.h"
/*!
\brief Configure GPIO OUT pins
\param[in] none
\param[out] none
\retval none
*/
void v_outpin_config(void)
{
/* 开启GPIO - outpin 外设时钟 经测试一句使能所有时钟不起作用 */
//rcu_periph_clock_enable(RCU_GPIOA | RCU_GPIOB | RCU_GPIOC | RCU_GPIOD | RCU_GPIOE | RCU_GPIOF | RCU_GPIOG | RCU_GPIOJ | RCU_GPIOK);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_GPIOC);
rcu_periph_clock_enable(RCU_GPIOD);
rcu_periph_clock_enable(RCU_GPIOE);
rcu_periph_clock_enable(RCU_GPIOF);
rcu_periph_clock_enable(RCU_GPIOG);
rcu_periph_clock_enable(RCU_GPIOJ);
rcu_periph_clock_enable(RCU_GPIOK);
/* 单阶段初始化:先预置 OCTL 为高电平(断开态),再切换到推挽输出。 */
gpio_bit_set(GPIOG,
GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 |
GPIO_PIN_11);
gpio_bit_set(GPIOC, GPIO_PIN_8 | GPIO_PIN_9);
//gpio_bit_set(GPIOK, GPIO_PIN_1 | GPIO_PIN_2);
/* 主控板开关量输出控制(DO)
PG4 114 DO OUT1|板载继电器K1控制
PG5 115 DO OUT2|板载继电器K2控制
PG6 116 DO OUT3|板载继电器K3控制
PG7 117 DO OUT4|板载继电器K4控制
PG8 118 DO OUT5|板载继电器K5控制
PC8 124 DO OUT6|板载继电器K6控制
PC9 125 DO OUT7|板载继电器K7控制
PG9 153 DO OUT8|板载继电器K8控制
PG10 154 DO OUT9|板载继电器K9控制
PG11 155 DO OUT10|板载继电器K10控制
PK1 108 D0 A_gun_elelock_ ctr|A枪电子锁控制
PK2 109 DO B_gun_elelock_ ctr|B枪电子锁控制
*/
/* Configure GPIO pins as output mode (no pull-up/pull-down) */
gpio_mode_set(GPIOG, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_4 | GPIO_PIN_5 |GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 );
/* Set output type to push-pull and speed to 60MHz */
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_4 | GPIO_PIN_5 |GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11);
/* Configure GPIO pins as output mode (no pull-up/pull-down) */
gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_8 | GPIO_PIN_9);
/* Set output type to push-pull and speed to 60MHz */
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_8 | GPIO_PIN_9);
/* Configure GPIO pins as output mode (no pull-up/pull-down) */
gpio_mode_set(GPIOK, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_1 | GPIO_PIN_2 );
/* Set output type to push-pull and speed to 60MHz */
gpio_output_options_set(GPIOK, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_1 | GPIO_PIN_2 );
/* DO/电子锁电平已在切换为输出模式前预置为高电平(断开态),避免继电器抖动。 */
/* 主控板内部开关量输出控制(DO)
PE4 3 DO INSA_CTL|A枪绝缘监测接地开关
PE3 2 DO UNBALA_CTL|A枪绝缘监测正极回路开关
PA5 50 DO INSB_CTL|B枪绝缘监测接地开关
PE5 4 DO UNBALB_CTL|B枪绝缘监测正极回路开关
*/
/* Configure GPIO pins as output mode (no pull-up/pull-down) */
gpio_mode_set(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE,GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5);
/* Set output type to push-pull and speed to 60MHz */
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ,GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5);
/* Configure GPIO pins as output mode (no pull-up/pull-down) */
gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_5 );
/* Set output type to push-pull and speed to 60MHz */
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_5 );
/* 板上LED指示灯
PG15 DO H7_RUN
*/
/* Configure GPIO pins as output mode (no pull-up/pull-down) */
gpio_mode_set(GPIOG, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE,GPIO_PIN_15);
/* Set output type to push-pull and speed to 60MHz */
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ,GPIO_PIN_15);
/* 主控板内部连接确认模式选择控制
PF8 28 DO CHK_CSA_CTL
PF2 20 DO CHK_CSB_CTL
*/
/* Configure GPIO pins as output mode (no pull-up/pull-down) */
gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE,GPIO_PIN_2 | GPIO_PIN_8);
/* Set output type to push-pull and speed to 60MHz */
gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ,GPIO_PIN_2 | GPIO_PIN_8);
/* 主控板内部控制输出
PE2 1 DO WDO
PB7 166 DO ESP8685_EN|ESP8685蓝牙WIFI模块使能控制
PE6 5 DO FC41D_RST|FC41D蓝牙WIFI模块复位(高有效,常态低)
PF3 21 DO 4G_RST|WH606 4G模块复位控制
PC13 9 DO 4G_POWER|WH606 4G模块电源控制
PA10 129 DO CIU_RST|CIU98_A加密芯片复位控制
*/
gpio_bit_reset(FC41D_RST_PORT, FC41D_RST_PIN); /* FC41D 常态低(高电平复位) */
/* Configure GPIO pins as output mode (no pull-up/pull-down) */
gpio_mode_set(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE,GPIO_PIN_2 | GPIO_PIN_6);
/* Set output type to push-pull and speed to 60MHz */
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ,GPIO_PIN_2 | GPIO_PIN_6);
gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_7 );
/* Set output type to push-pull and speed to 60MHz */
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_7 );
gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_3 );
/* Set output type to push-pull and speed to 60MHz */
gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_3 );
gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_13 );
/* Set output type to push-pull and speed to 60MHz */
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_13 );
gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_10 );
/* Set output type to push-pull and speed to 60MHz */
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_10 );
/* 温度采集通道选择控制
PD10 数字地址“A”输出控制
PD13 数字地址“B”输出控制
PJ8 数字地址“C”输出控制
*/
/* Configure GPIO pins as output mode (no pull-up/pull-down) */
gpio_mode_set(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE,GPIO_PIN_10 | GPIO_PIN_13);
/* Set output type to push-pull and speed to 60MHz */
gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ,GPIO_PIN_10 | GPIO_PIN_13);
gpio_mode_set(GPIOJ, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_8 );
/* Set output type to push-pull and speed to 60MHz */
gpio_output_options_set(GPIOJ, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_8);
}
void v_inpin_config(void)
{
/* 开启GPIO - inpin 外设时钟 */
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_GPIOD);
rcu_periph_clock_enable(RCU_GPIOE);
rcu_periph_clock_enable(RCU_GPIOF);
rcu_periph_clock_enable(RCU_GPIOG);
/* 开关量输入检测
PC0 DI YX1|系统开关量输入检测
PB0 55 DI YX2|系统开关量输入检测
PF14 61 DI YX3|系统开关量输入检测
PB1 56 DI YX4|系统开关量输入检测
PF15 62 DI YX5|系统开关量输入检测
PB2 57 DI YX6|系统开关量输入检测
PG0 63 DI YX7|系统开关量输入检测
PF11 58 DI YX8|系统开关量输入检测
PE11 73 DI YX9|系统开关量输入检测
PF12 59 DI YX10|系统开关量输入检测
PE15 77 DI YX11|系统开关量输入检测
PF13 60 DI YX12|系统开关量输入检测
PG1 66 DI Aux_Padet|A枪辅助电源检测
PB10 78 DI Aux_Pbdet|B枪辅助电源检测
*/
/* configure pin as input */
gpio_mode_set(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_0);
gpio_mode_set(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_0);
gpio_mode_set(GPIOF, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_mode_set(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_mode_set(GPIOF, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_15);
gpio_mode_set(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_mode_set(GPIOG, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_0);
gpio_mode_set(GPIOF, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_mode_set(GPIOE, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_mode_set(GPIOF, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_mode_set(GPIOE, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_15);
gpio_mode_set(GPIOF, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_mode_set(GPIOG, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_mode_set(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_10);
/* 连接确认开关量输入检测
PF10 30 DI CHECKOUTA1|A枪链接确认开关量输入检测
PF9 29 DI CHECKOUTA2|A枪链接确认开关量输入检测
PD3 146 DI CHECKOUTB1|B枪链接确认开关量输入检测
PA15 139 DI CHECKOUTB2|B枪链接确认开关量输入检测
*/
/* configure pin as input */
gpio_mode_set(GPIOF, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_9 | GPIO_PIN_10);
gpio_mode_set(GPIOD, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_3);
gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_15);
}
//温度采集通道选择控制
void v_temp_config(void)
{
//开启GPIO时钟
rcu_periph_clock_enable(RCU_GPIOD);
rcu_periph_clock_enable(RCU_GPIOJ);
//数字地址“A”配置
gpio_mode_set(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_10);
//数字地址“B”配置
gpio_mode_set(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_13);
//数字地址“C”配置
gpio_mode_set(GPIOJ, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_8);
}
void v_4g_config()
{
//开启GPIO时钟
rcu_periph_clock_enable(RCU_GPIOF);
rcu_periph_clock_enable(RCU_GPIOC);
//4G模块RTS配置
gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_3);
//4G模块电源控制
gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_13);
}
+148
View File
@@ -0,0 +1,148 @@
#ifndef GPIO_H
#define GPIO_H
#include <stdint.h>
#ifndef __PIN_DEF_H
#define __PIN_DEF_H
#include "gd32h7xx.h" // 根据实际芯片替换
/* ==================== 板载继电器控制 (K1K10) ==================== */
#define RELAY_K1_PORT GPIOG
#define RELAY_K1_PIN GPIO_PIN_4
#define RELAY_K2_PORT GPIOG
#define RELAY_K2_PIN GPIO_PIN_5
#define RELAY_K3_PORT GPIOG
#define RELAY_K3_PIN GPIO_PIN_6
#define RELAY_K4_PORT GPIOG
#define RELAY_K4_PIN GPIO_PIN_7
#define RELAY_K5_PORT GPIOG
#define RELAY_K5_PIN GPIO_PIN_8
#define RELAY_K6_PORT GPIOC
#define RELAY_K6_PIN GPIO_PIN_8
#define RELAY_K7_PORT GPIOC
#define RELAY_K7_PIN GPIO_PIN_9
#define RELAY_K8_PORT GPIOG
#define RELAY_K8_PIN GPIO_PIN_9
#define RELAY_K9_PORT GPIOG
#define RELAY_K9_PIN GPIO_PIN_10
#define RELAY_K10_PORT GPIOG
#define RELAY_K10_PIN GPIO_PIN_11
/* 快速操作宏(可选) */
#define RELAY_ON(port, pin) gpio_bit_set(port, pin)
#define RELAY_OFF(port, pin) gpio_bit_reset(port, pin)
/* ==================== 电子锁控制 ==================== */
#define ELOCK_A_PORT GPIOK
#define ELOCK_A_PIN GPIO_PIN_1
#define ELOCK_B_PORT GPIOK
#define ELOCK_B_PIN GPIO_PIN_2
/* ==================== 绝缘监测开关控制 ==================== */
#define INSA_GND_PORT GPIOE
#define INSA_GND_PIN GPIO_PIN_4 // INSA_CTL
#define INSA_POS_PORT GPIOE
#define INSA_POS_PIN GPIO_PIN_3 // UNBALA_CTL
#define INSB_GND_PORT GPIOA
#define INSB_GND_PIN GPIO_PIN_5 // INSB_CTL
#define INSB_POS_PORT GPIOE
#define INSB_POS_PIN GPIO_PIN_5 // UNBALB_CTL
/* ==================== LED 指示灯 ==================== */
#define LED_RUN_PORT GPIOG
#define LED_RUN_PIN GPIO_PIN_15
/* ==================== 连接确认模式选择 ==================== */
#define CHK_CSA_PORT GPIOF
#define CHK_CSA_PIN GPIO_PIN_8
#define CHK_CSB_PORT GPIOF
#define CHK_CSB_PIN GPIO_PIN_2
/* ==================== 内部控制输出 ==================== */
#define WDO_PORT GPIOE
#define WDO_PIN GPIO_PIN_2
#define ESP8685_EN_PORT GPIOB
#define ESP8685_EN_PIN GPIO_PIN_7
/* FC41D 蓝牙/WiFi 模组复位(PE6,高有效,常态低) */
#define FC41D_RST_PORT GPIOE
#define FC41D_RST_PIN GPIO_PIN_6
#define BLUE_EN_PORT FC41D_RST_PORT /* 兼容旧名 */
#define BLUE_EN_PIN FC41D_RST_PIN
#define CIU_RST_PORT GPIOA
#define CIU_RST_PIN GPIO_PIN_10
/* ==================== 温度通道地址选择 ==================== */
#define TEMP_ADDR_A_PORT GPIOD
#define TEMP_ADDR_A_PIN GPIO_PIN_10
#define TEMP_ADDR_B_PORT GPIOD
#define TEMP_ADDR_B_PIN GPIO_PIN_13
#define TEMP_ADDR_C_PORT GPIOJ
#define TEMP_ADDR_C_PIN GPIO_PIN_8
/* ==================== 开关量输入 (DI) ==================== */
// YX 系列
#define YX1_PORT GPIOC
#define YX1_PIN GPIO_PIN_0
#define YX2_PORT GPIOB
#define YX2_PIN GPIO_PIN_0
#define YX3_PORT GPIOF
#define YX3_PIN GPIO_PIN_14
#define YX4_PORT GPIOB
#define YX4_PIN GPIO_PIN_1
#define YX5_PORT GPIOF
#define YX5_PIN GPIO_PIN_15
#define YX6_PORT GPIOB
#define YX6_PIN GPIO_PIN_2
#define YX7_PORT GPIOG
#define YX7_PIN GPIO_PIN_0
#define YX8_PORT GPIOF
#define YX8_PIN GPIO_PIN_11
#define YX9_PORT GPIOE
#define YX9_PIN GPIO_PIN_11
#define YX10_PORT GPIOF
#define YX10_PIN GPIO_PIN_12
#define YX11_PORT GPIOE
#define YX11_PIN GPIO_PIN_15
#define YX12_PORT GPIOF
#define YX12_PIN GPIO_PIN_13
// 辅助电源检测
#define AUX_PADET_PORT GPIOG
#define AUX_PADET_PIN GPIO_PIN_1 // A枪
#define AUX_PBDT_PORT GPIOB
#define AUX_PBDT_PIN GPIO_PIN_10 // B枪
// 连接确认输入
#define CHECKOUTA1_PORT GPIOF
#define CHECKOUTA1_PIN GPIO_PIN_10
#define CHECKOUTA2_PORT GPIOF
#define CHECKOUTA2_PIN GPIO_PIN_9
#define CHECKOUTB1_PORT GPIOD
#define CHECKOUTB1_PIN GPIO_PIN_3
#define CHECKOUTB2_PORT GPIOA
#define CHECKOUTB2_PIN GPIO_PIN_15
//4G模块
#define PWR_4G_PORT GPIOF
#define PWR_4G_PIN GPIO_PIN_3
#define RST_4G_PORT GPIOC
#define RST_4G_PIN GPIO_PIN_13
/* ==================== 输入读取宏 ==================== */
#define DI_READ(port, pin) (gpio_input_bit_get(port, pin))
#endif /* __PIN_DEF_H */
void v_outpin_config(void);
void v_inpin_config(void);
void v_4g_config(void);
void v_temp_config(void);
#endif
+484
View File
@@ -0,0 +1,484 @@
/*!
\file spi3_charger.c
\brief SPI3 for A&B gun charger voltage acquisition and insulation detection
\version 2026-03-09, V1.4.0, firmware for GD32H7xx
*/
/*
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 "adc_spi3_insuVolt.h"
#include <stdio.h>
#include "gd32h7xx.h"
#include "FreeRTOS.h"
#include "task.h"
#include "spi_if.h"
#include "sgm51652hx.h"
#include <string.h>
#include "sys_drv_init.h"
// ======== 配置 =========
#define NUM_CHS 4 //通道数:芯片1CH0、CH1+芯片2CH0、CH1
#define NUM_COLL 5 //每个通道采集次数
#define V_REF 4.096f //芯片基准电压
int16_t adc3_final[NUM_CHS][NUM_COLL];
//uint8_t dma_buf[ADC_CHANNELS][BYTES_PER_CH]; //临时接收缓存
/* 私有变量 */
static uint8_t spi3_initialized = 0;
static spi_dev_t spi3_dev1; // SPI3设备(A枪)
static spi_dev_t spi3_dev2; // SPI3设备(B枪)
static sgm51652hx_dev_t sgm51652hx_chip1; // 芯片1A枪)
static sgm51652hx_dev_t sgm51652hx_chip2; // 芯片2B枪)
/* 私有函数声明 */
static void spi3_gpio_config(void);
static void spi3_config(void);
/*!
\brief configure SPI3 GPIO peripheral for charger
\param[in] none
\param[out] none
\retval none
\note SPI3引脚配置 for SGM51652H4XTS38G/T:
- SPI3_CSA -> PA4 (A枪片选)
- SPI3_CSB -> PA3 (B枪片选)
- SPI3_SCK -> PE12 (时钟)
- SPI3_MISO -> PE13 (数据输入)
- SPI3_MOSI -> PE14 (数据输出)
*/
void spi3_gpio_config(void)
{
/* 使能GPIO时钟 */
rcu_periph_clock_enable(SPI3_RCU_CS);
rcu_periph_clock_enable(SPI3_RCU_SCK_MSIO);
/* 使能SPI3时钟 */
rcu_periph_clock_enable(RCU_SPI3);
/* 使能DMA时钟 */
// rcu_periph_clock_enable(RCU_DMA0);
// rcu_periph_clock_enable(RCU_DMAMUX);
/* 配置SPI3时钟源 */
rcu_spi_clock_config(IDX_SPI3, RCU_SPISRC_APB2); //RCU_SPISRC_PLL0Q
/* 配置A枪片选引脚 (PA4) - 软件控制 */
gpio_mode_set(SPI3_CS1_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SPI3_CS1_PIN);
gpio_output_options_set(SPI3_CS1_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, SPI3_CS1_PIN);
/* 默认拉高,不选中 */
SPI3_CS1_HIGH(); //gpio_bit_set(GPIOA, GPIO_PIN_4);
/* 配置B枪片选引脚 (PA3) - 软件控制 */
gpio_mode_set(SPI3_CS2_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SPI3_CS2_PIN);
gpio_output_options_set(SPI3_CS2_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, SPI3_CS2_PIN);
/* 默认拉高,不选中B枪 */
SPI3_CS2_HIGH(); //gpio_bit_set(GPIOA, GPIO_PIN_3);
/* 配置SPI3引脚复用功能 */
//spi_related_ios_af_en(SPI3); //SPI标准库函数,一键配置SPIx所有引脚复用(SCK/MOSI/MISO/NSSlyn260401
gpio_af_set(GPIOE, GPIO_AF_5, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14);
/* 配置SCK引脚 (PE12) - 复用功能输出 */
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_12);
/* 配置MISO引脚 (PE13) - 复用功能输入 */
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_13);
/* 配置MOSI引脚 (PE14) - 复用功能输出 */
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_14);
}
/*!
\brief configure SPI3 peripheral for charger
\param[in] none
\param[out] none
\retval none
\note SPI3基本配置,针对SGM51652H4XTS38G/T优化
*/
void spi3_config(void)
{
spi_parameter_struct spi_init_struct;
/* 去初始化SPI3 */
spi_i2s_deinit(SPI3);
/* 初始化SPI参数结构体 */
spi_struct_para_init(&spi_init_struct);
/* SPI3参数配置 for SGM51652H4XTS38G/T */
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; // 全双工模式
spi_init_struct.device_mode = SPI_MASTER; // 主模式
spi_init_struct.data_size = SPI_DATASIZE_8BIT; // 8位数据帧
spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_1EDGE; // 模式3sgm51652只能工作在Model
spi_init_struct.nss = SPI_NSS_SOFT; // 软件NSS控制:soft-自己用GPIO控制CS1/CS2
spi_init_struct.prescale = SPI_PSC_32; // 分频系数,sgm51652最高时钟10Mhz~10Mhz,SPI3在APB2300Mhz
spi_init_struct.endian = SPI_ENDIAN_MSB; // MSB先行 sgm51652规定必须高位先传
/* 初始化SPI3 */
spi_init(SPI3, &spi_init_struct);
/* 使能字节访问 */
spi_byte_access_enable(SPI3);
/* 使能NSS输出 (兼容性) */
spi_nss_output_enable(SPI3);
}
#if 0 //DMA相关配置,未调通 lyn20260410
/*!
\brief configure the DMA peripheral
\param[in] none
\param[out] none
\retval none
*/
void adc_read_single(uint8_t chip, uint8_t cmd, uint8_t index)
{
dma_single_data_parameter_struct dma_init_struct;
uint8_t buf[2];
//选中芯片
if(chip == CHIP_1)
{
SPI3_CS1_LOW();
SPI3_CS2_HIGH();
}
else
{
SPI3_CS2_LOW();
SPI3_CS1_HIGH();
}
//DMA配置
/* deinitialize DMA registers of a channel */
dma_deinit(DMA0, DMA_CH0);
dma_single_data_para_struct_init(&dma_init_struct);
/* SPI0 receive DMA config: DMA_CH0 */
dma_init_struct.request = DMA_REQUEST_SPI3_RX; //选哪个外设:SPI3-RX
dma_init_struct.periph_addr = (uint32_t)&SPI_RDATA(SPI3); //外设地址:SPI3
dma_init_struct.periph_inc = DMA_MEMORY_INCREASE_DISABLE; //禁止外设自增
dma_init_struct.memory0_addr = (uint32_t)buf; //内存地址:定义的数组
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; //内存地址自增
dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; //数据位宽:8/16/32位
dma_init_struct.circular_mode = DMA_CIRCULAR_MODE_ENABLE; //是否循环模式:是
dma_init_struct.direction = DMA_PERIPH_TO_MEMORY; //方向:外设->内存
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; //优先级
dma_init_struct.number = 2; //SPI一次DMA收多少字节,与数据位宽对应 1路ADC=2字节
dma_single_data_mode_init(DMA0, DMA_CH0, &dma_init_struct); //初始化
/* enable DMA channel */
dma_channel_enable(DMA0, DMA_CH0);
//开启SPI3 DMA接收
spi_dma_enable(SPI3, SPI_DMA_RECEIVE);
#if 0
//发通道命令 = 软件启动转换
if(spi3_charger_read_data(CHARGER_GUN_A, cmd, data, 2) != 0) {
return -1.0f;
}
//等待DMA完成
// 等待 DMA 完成
while(!dma_flag_get(DMA0, DMA_CH0, DMA_INTF_FTFIF));
dma_flag_clear(DMA0, DMA_CH0, DMA_INTF_FTFIF);
// 关闭
spi_dma_disable(SPI3, SPI_DMA_RECEIVE);
dma_channel_disable(DMA0, DMA_CH0);
SPI3_CS1_HIGH();
SPI3_CS2_HIGH();
// 合并 16 位 ADC 数据
adc_result[index] = (int16_t)(buf[0] << 8 | buf[1]);
#endif
}
#endif
/*!
\brief SPI3完整初始化 for charger
\param[in] none
\param[out] none
\retval 0: 成功, -1: 失败
\note 专门为A&B枪充电电压采集和绝缘检测优化
*/
int adc_spi3_insuVolt_init(void)
{
if(spi3_initialized) {
return 0; // 已初始化
}
/* 配置GPIO */
spi3_gpio_config();
/* 配置SPI外设 */
spi3_config();
/* 使能SPI3 */
spi_enable(SPI3);
/* 初始化SPI设备。注意:芯片1/2使用不同的片选引脚,需要单独处理*/
/* 初始化芯片1A枪) */
spi_init_dev(&spi3_dev1, SPI3, GPIOA, GPIO_PIN_4, 0, 0);
sgm51652hx_init(&sgm51652hx_chip1, &spi3_dev1);
/* 初始化芯片2B枪) */
spi_init_dev(&spi3_dev2, SPI3, GPIOA, GPIO_PIN_3, 0, 0);
sgm51652hx_init(&sgm51652hx_chip2, &spi3_dev2);
/* 设置通道量程 */
for (uint8_t i = 0; i < 2; i++) {
sgm51652hx_set_range(&sgm51652hx_chip1, i, RGVL_BP_256); // ±2.56V
sgm51652hx_set_range(&sgm51652hx_chip2, i, RGVL_BP_256); // ±2.56V
}
spi3_initialized = 1;
return 0;
}
/*!
\brief SPI3去初始化
\param[in] none
\param[out] none
\retval none
*/
void adc_spi3_batVoltCurr_deinit(void)
{
/* 去初始化芯片 */
sgm51652hx_deinit(&sgm51652hx_chip1);
sgm51652hx_deinit(&sgm51652hx_chip2);
/* 去初始化SPI设备 */
spi_deinit_dev(&spi3_dev1);
spi_deinit_dev(&spi3_dev2);
/* 禁用SPI3 */
spi_disable(SPI3);
/* 去初始化SPI3 */
spi_i2s_deinit(SPI3);
/* 所有片选拉高 */
gpio_bit_set(GPIOA, GPIO_PIN_3);
gpio_bit_set(GPIOA, GPIO_PIN_4);
spi3_initialized = 0;
}
/*
函数功能:将MCU通过SPI接收到的数据转换为控制器端口采到的电压
输入: uint16_t read_data MCU通过SPI接收到的数据
uint8_t adc_zf 正负极区分,0正极,1负级
uint8_t flag 绝缘检测光电继电器闭合标志位,0:未闭合,1:已经闭合
float data 转换后的电压
备注:V_REF:芯片基准电压
芯片采集到的电压计算方式:
每个LSB代表的电压值 = 芯片输入范围(±0.625,即0.625 * 2 * 基准电压(V_REF / 2^16;
ADC读到的电压值 = 每个LSB代表的电压值 * read_data
0V对应点ADC读到电压值为0.625 * V_REFADC读到的电压值减去 0.625 * V_REF即为当前芯片采集电压值。
芯片采集到电压到控制器端口采集电压转换关系如下:
绝缘检测光电继电器闭合前:控制器端口电压 = 芯片采集电压值 * 1376.89/(0.625 *V_REF)
绝缘检测光电继电器闭合后:控制器端口电压:
正极电压 = 芯片采集电压值 * 689 /(0.625 *V_REF)
负极电压 = 芯片采集电压值 * 1380 /(0.625 *V_REF)
*/
float f_read_to_Vol(float read_data, uint8_t adc_zf, uint8_t flag)
{
float data = 0.0f;
//data = chip_volt; //等同于gm51652hx_calc_voltage(raw_value, RGVL_BP_256)
data = (0.625f * 2.0f * V_REF * read_data / 65535.0f) - (0.625f * V_REF);
if(flag == 0)
{
data = data * 1376.89f / (0.625f * V_REF);
}
else
{
if(adc_zf == 0) //正负极计算方式不同,分开计算
{
data = data * 689.0f / (0.625f * V_REF);
}
else
{
data = data * 1380.0f / (0.625f * V_REF);
}
}
return data;
}
//==================== 单通道读取 (核心函数) ====================
uint16_t sgm51652hx_spiReadBytes(uint8_t chip, uint8_t channel) //读取芯片chip通道channel的返回原始ADC值
{
uint16_t raw_value = 0;
sgm51652hx_dev_t *dev = NULL;
if(chip == CHIP_1)
dev = &sgm51652hx_chip1; //芯片1-A枪绝缘电压检测
else
dev = &sgm51652hx_chip2; //芯片2-B枪绝缘电压检测
/* 设置通道量程(TBD:上电初始化设置一次就行,还是需要每次发送都要设置?) */
//sgm51652hx_set_range(dev, channel, RGVL_BP_256);
/* 读取原始值 */
if(sgm51652hx_read_channel(dev, channel, &raw_value) != ERR_OK_SGM)
return 0;
//printf("spi3 chip=%d,channel=%d: %d\n", chip, channel, raw_value);
return raw_value;
}
/*
函数功能:获取指定控制器端口采集电压
输入:uint8_t chip 芯片号,等同于枪号,1 对应A枪,2 对应B枪
uint8_t channel 通道号,等同于正负级,channel_0对应正极,channel_1对应负级
uint8_t relayOnOff 绝缘检测光电继电器闭合标志位,0:未闭合,1:已经闭合
*/
float spi3_read_analog_voltage(uint8_t chip, uint8_t channel)
{
uint16_t raw_value[NUM_COLL] = {0};
uint16_t i = 0, j = 0;
uint32_t raw_sum_avg = 0;
uint8_t relayOnOff;
float real_voltage = 0;
if(chip == 1)
relayOnOff = GUN1_INS_KM_READ;
else
relayOnOff = GUN2_INS_KM_READ;
// printf("chip = %d,relayOnOff = %d\n",chip,relayOnOff);
//读取指定端口与通道数据NUM_COLL次
for(i=0; i<NUM_COLL; i++)
raw_value[i] = sgm51652hx_spiReadBytes(chip, channel);
for(j=0; j<NUM_COLL; j++)
raw_sum_avg += raw_value[j];
raw_sum_avg /= NUM_COLL;
/* 转换为芯片引脚电压值 --> 融合进f_read_to_vol() */
//chip_voltage = sgm51652hx_calc_voltage(raw_sum_avg, RGVL_BP_256);
#if 1
/* 根据绝缘回路,将芯片引脚电压转换为控制器端口电压数据 */
real_voltage = f_read_to_Vol(raw_sum_avg, channel, relayOnOff); //TODO: 参考DCU606
if(channel == 1) //通道1-负级
real_voltage = -real_voltage;
if(relayOnOff == 0) //光电继电器闭合标志,闭合前后,数据分开校准
{
if(channel == 0) //校准系数
{
real_voltage = real_voltage / 1000 * 993; //997(100欧/V);300V-990 ,400V-997 ,500V-997 ,600V-
//997(500欧/V);400V-997
}
else if(channel == 1)
{
real_voltage = real_voltage / 1000 * 997; //1004(100欧/V);300V-1007 ,500V-
//1004(500欧/V);400V-1007 ,
}
}
else
{
if(channel == 0) //校准系数
{
real_voltage = real_voltage / 1000 * 998; //997(100欧/V);300V-990 ,400V-997 ,500V-997 ,600V
//997(500欧/V);400V-997
}
else if(channel == 1)
{
real_voltage = real_voltage / 1000 * 995; //1004(100欧/V);300V-1007 ,500V-
//1004(500欧/V);400V-1007 ,
}
}
#endif
// printf("SPI3 chip(%d)-channel(%d): raw=%d, realU=%.2f \n", chip, channel, raw_sum_avg, real_voltage);
return real_voltage;
}
//==================== 读取全部4通道 - TODO:改为对应用层接口函数 ====================
void sgm51652_read_all_channels(void)
{
float adc_result[4] = {0};
// 芯片1 CH0
adc_result[0] = spi3_read_analog_voltage(CHIP_1, CHNL_0);
// 芯片1 CH1
adc_result[1] = spi3_read_analog_voltage(CHIP_1, CHNL_1);
// 芯片2 CH0
adc_result[2] = spi3_read_analog_voltage(CHIP_2, CHNL_0);
// 芯片2 CH1
adc_result[3] = spi3_read_analog_voltage(CHIP_2, CHNL_1);
(void)adc_result;
}
/*!
\brief 充电器自检
\param[in] none
\param[out] none
\retval 0: 正常, -1: 故障
*/
void spi3_charger_self_test(void)
{
printf("spi3 adc-insuvolt test started...\n");
/* 检查SPI3初始化状态 */
if(!spi3_initialized) {
printf("SPI3 not initialized\n");
return;
}
// spi3_charger_write_command(1, 0, 0, 0);
//
// vTaskDelay(50);
// spi3_charger_read_command(1, 0, 0, 0);
while(1)
{
//sgm51652hx_spiReadBytes(1, 0);
//spi3_read_analog_voltage(1, 0, 5);
// spi3_charger_read_data_A_0(); //
sgm51652_read_all_channels();
vTaskDelay(250);
}
}
+83
View File
@@ -0,0 +1,83 @@
/*!
\file spi3_charger.h
\brief header file of SPI3 for A&B gun charger voltage acquisition and insulation detection
\version 2026-03-09, V1.4.0, firmware for GD32H7xx
*/
/*
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 SPI3_CHARGER_H
#define SPI3_CHARGER_H
#include <stdint.h>
/* SPI3引脚定义 for SGM51652H4XTS38G/T */
#define SPI3_SCK_PORT GPIOE // PE12 - 时钟
#define SPI3_SCK_PIN GPIO_PIN_12
#define SPI3_MISO_PORT GPIOE // PE13 - 数据输入
#define SPI3_MISO_PIN GPIO_PIN_13
#define SPI3_MOSI_PORT GPIOE // PE14 - 数据输出
#define SPI3_MOSI_PIN GPIO_PIN_14
/* SPI3复用功能编号 */
#define SPI3_AF GPIO_AF_5
/* 双芯片片选 */
#define SPI3_CS1_PORT GPIOA // PA4 - 芯片1片选
#define SPI3_CS1_PIN GPIO_PIN_4
#define SPI3_CS2_PORT GPIOA // PA3 - 芯片2片选
#define SPI3_CS2_PIN GPIO_PIN_3
/* SPIC3 时钟 */
#define SPI3_RCU_CS RCU_GPIOA
#define SPI3_RCU_SCK_MSIO RCU_GPIOE
/* 芯片/通道选择 */
#define CHIP_1 1 //芯片1-CSA
#define CHIP_2 2 //芯片2-CSB
#define CHNL_0 0 //通道0
#define CHNL_1 1 //通道1
/* 引脚宏操作 */
#define SPI3_CS1_LOW() gpio_bit_reset(SPI3_CS1_PORT, SPI3_CS1_PIN)
#define SPI3_CS1_HIGH() gpio_bit_set(SPI3_CS1_PORT, SPI3_CS1_PIN)
#define SPI3_CS2_LOW() gpio_bit_reset(SPI3_CS2_PORT, SPI3_CS2_PIN)
#define SPI3_CS2_HIGH() gpio_bit_set(SPI3_CS2_PORT, SPI3_CS2_PIN)
/* 完整初始化和去初始化 */
int adc_spi3_insuVolt_init(void);
void adc_spi3_insuVolt_deinit(void);
void spi3_charger_self_test(void);
float spi3_read_analog_voltage(uint8_t chip, uint8_t channel);
#endif /* SPI3_CHARGER_H */
+474
View File
@@ -0,0 +1,474 @@
/*!
\file spi5_analog.c
\brief SPI5 for analog data acquisition
\version 2026-03-09, V1.4.0, firmware for GD32H7xx
*/
/*
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 "adc_spi5_batVoltCurr.h"
#include <stdio.h>
#include "gd32h7xx.h"
#include "sgm51652hx.h"
#include "spi_if.h"
//#include "task.h"
#define V_REF 4.096 //芯片基准电压
#define N 50 //ADC采样次数
float adc_ref = 0.0; //基准电压值
/* 私有变量 */
static uint8_t spi5_initialized = 0;
static spi_dev_t spi5_dev; // SPI5设备
static sgm51652hx_dev_t sgm51652hx_chip; // SGM51652HX芯片
/* 私有函数声明 */
static void spi5_gpio_config(void);
static void spi5_config(void);
//static float spi5_convert_adc_value(uint16_t raw_value, uint8_t channel);
static uint8_t spi5_validate_channel(uint8_t channel);
/*!
\brief configure SPI5 GPIO peripheral for analog acquisition
\param[in] none
\param[out] none
\retval none
\note SPI5引脚配置 for analog acquisition:
- SPI5_CS -> PG12 (片选)
- SPI5_SCK -> PG13 (时钟)
- SPI5_MISO -> PG14 (数据输入)
- SPI5_MOSI -> PA6 (数据输出)
*/
void spi5_gpio_config(void)
{
/* 使能GPIO时钟 */
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOG);
/* 使能SPI5时钟 */
rcu_periph_clock_enable(RCU_SPI5);
/* 配置SPI5时钟源 */
rcu_spi_clock_config(IDX_SPI5, RCU_SPISRC_APB2);
/* 配置CS引脚 (PG12) - 软件控制 */
gpio_mode_set(GPIOG, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_12);
/* 默认拉高,不选中模拟芯片 */
gpio_bit_set(GPIOG, GPIO_PIN_12);
/* 配置SPI5引脚复用功能 */
gpio_af_set(GPIOG, GPIO_AF_5, GPIO_PIN_13 | GPIO_PIN_14); // SCK, MOSI
gpio_af_set(GPIOA, GPIO_AF_8, GPIO_PIN_6); // MISO,复用AF8
/* 配置SCK引脚 (PG13) - 复用功能输出 */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_13);
/* 配置MOSI引脚 (PG14) - 复用功能输出 */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_14);
/* 配置MISO引脚 (PA6) - 复用功能输入 */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_6);
}
/*!
\brief configure SPI5 peripheral for analog acquisition
\param[in] none
\param[out] none
\retval none
\note SPI5基本配置,针对模拟量采集优化
*/
void spi5_config(void)
{
spi_parameter_struct spi_init_struct;
/* 去初始化SPI5 */
spi_i2s_deinit(SPI5);
/* 初始化SPI参数结构体 */
spi_struct_para_init(&spi_init_struct);
/* SPI5参数配置 for analog acquisition */
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; // 全双工模式
spi_init_struct.device_mode = SPI_MASTER; // 主模式
spi_init_struct.data_size = SPI_DATASIZE_8BIT; // 8位数据帧
spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_1EDGE; // 时钟极性低,第一边沿采样 (Mode 0)
spi_init_struct.nss = SPI_NSS_SOFT; // 软件NSS控制
spi_init_struct.prescale = SPI_PSC_32; // 分频系数
spi_init_struct.endian = SPI_ENDIAN_MSB; // MSB先行
/* 初始化SPI5 */
spi_init(SPI5, &spi_init_struct);
/* 使能字节访问 */
spi_byte_access_enable(SPI5);
/* 使能NSS输出 (兼容性) */
spi_nss_output_enable(SPI5);
}
#if 0
static uint8_t spi5_transmit_receive(uint8_t data)
{
/* 等待发送缓冲区为空 */
while(RESET == spi_i2s_flag_get(SPI5, SPI_FLAG_TP));
/* 发送数据 */
spi_i2s_data_transmit(SPI5, data);
/* 等待接收缓冲区非空 */
while(RESET == spi_i2s_flag_get(SPI5, SPI_FLAG_RP));
/* 接收数据 */
return (uint8_t)spi_i2s_data_receive(SPI5);
}
static void spi5_send_data(uint8_t data)
{
/* 等待发送缓冲区为空 */
while(RESET == spi_i2s_flag_get(SPI5, SPI_FLAG_TP));
/* 发送数据 */
spi_i2s_data_transmit(SPI5, data);
}
int spi5_write_range(sgm51652hxReg_e reg, sgm51652hxRngVlu_e rangVlu) //设置指定通道量程
{
//uint8_t txbuf[3] = {0x01, };
uint8_t cmd = 0;
uint8_t rxbuf[4] = {0};
if(!spi5_initialized) {
return -1;
}
spi5_cs_control(1); //选中
cmd = (((uint8_t)reg)<<1) | 1; //寄存器地址+写指令1
//rxbuf[0] = spi5_transmit_receive(0x01); //写命令
//rxbuf[1] = spi5_transmit_receive((uint8_t)reg); //关键程序寄存器地址
rxbuf[0] = spi5_transmit_receive(cmd);
rxbuf[1] = spi5_transmit_receive((uint8_t)rangVlu); //写量程,返回的值应该是写入的值
rxbuf[2] = spi5_transmit_receive(0x00);
rxbuf[3] = spi5_transmit_receive(0x00);
spi5_cs_control(0); //取消选中
printf("spi5 regAddr[%d]: set value = %x -- %x %x %x %x \n", reg, rangVlu, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]);
return 0;
}
int spi5_read_range(sgm51652hxReg_e reg) //设置指定通道量程
{
//uint8_t txbuf[3] = {0x01, };
uint8_t cmd = 0;
uint8_t rxbuf[3] = {0};
if(!spi5_initialized) {
return -1;
}
spi5_cs_control(1); //选中
cmd = (((uint8_t)reg)<<1); //寄存器地址+读指令0
rxbuf[0] = spi5_transmit_receive(cmd); //
rxbuf[1] = spi5_transmit_receive(0x00); //返回值
rxbuf[2] = spi5_transmit_receive(0x00);
spi5_cs_control(0); //取消选中
printf("spi5 regAddr[%d]: read value -- %x %x %x\n", reg, rxbuf[0], rxbuf[1], rxbuf[2]);
return 0;
}
#endif
#if 0 //调试
int sgm51652hx_write_reg_test1(sgm51652hx_dev_t *dev, uint8_t reg, uint8_t value)
{
uint8_t rxbuf[4] = {0};
if (!dev || !dev->initialized) {
return ERR_INVALIDPARAMETER;
}
/* 选中芯片 */
spi_cs_control(&dev->spi_dev, 1);
//spi5_cs_control(1); //选中
/* 开启发送 */
spi_master_transfer_start(dev->spi_dev.spi, SPI_TRANS_START);
/* 发送写命令:寄存器地址 << 1 | 1 */
uint8_t cmd = (reg << 1) | 1;
#if 0
spi5_send_data(cmd);
/* 发送数据 */
spi5_send_data(value);
/* 发送两个空字节 */
spi5_send_data(0x00);
spi5_send_data(0x00);
#endif
#if 0
spi_transmit_byte(dev->spi_dev.spi, cmd);
/* 发送数据 */
spi_transmit_byte(dev->spi_dev.spi, value);
/* 发送两个空字节 */
spi_transmit_byte(dev->spi_dev.spi, 0x00);
spi_transmit_byte(dev->spi_dev.spi, 0x00);
#endif
#if 1
rxbuf[0] = spi_transmit_receive_byte(dev->spi_dev.spi, cmd);/* 发送数据 */
rxbuf[1] = spi_transmit_receive_byte(dev->spi_dev.spi, value);/* 发送两个空字节 */
rxbuf[2] = spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
rxbuf[3] = spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
#endif
#if 0
rxbuf[0] = spi5_transmit_receive(cmd);/* 发送数据 */
rxbuf[1] = spi5_transmit_receive(value);/* 发送两个空字节 */
rxbuf[2] = spi5_transmit_receive(0x00);
rxbuf[3] = spi5_transmit_receive(0x00);
#endif
/* 取消片选 */
spi_cs_control(&dev->spi_dev, 0);
//spi5_cs_control(0); //选中
printf("spi5 regAddr[%d]: set value = %x %x %x %x \n", reg, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]);
return ERR_OK_SGM;
}
int sgm51652hx_set_range_test1(sgm51652hx_dev_t *dev, uint8_t channel, sgm51652hxRngVlu_e range)
{
if (!dev || !dev->initialized || channel > 7) {
return ERR_INVALIDPARAMETER;
}
/* 计算量程寄存器地址 */
uint8_t reg = 0x05 + channel; /* RANGE_CH0 = 0x05 */
/* 写入量程配置 */
return sgm51652hx_write_reg_test1(dev, reg, (uint8_t)range);
}
#endif
/*!
\brief SPI5完整初始化 for analog acquisition
\param[in] none
\param[out] none
\retval 0: 成功, -1: 失败
\note 专门为模拟量采集优化
*/
int adc_spi5_batVoltCurr_init(void)
{
int i = 0;
if(spi5_initialized) {
return 0; // 已初始化
}
/* 配置GPIO */
spi5_gpio_config();
/* 配置SPI外设 */
spi5_config();
/* 使能SPI5 */
spi_enable(SPI5);
/* 初始化SPI设备 */
spi_init_dev(&spi5_dev, SPI5, GPIOG, GPIO_PIN_12, 0, 0);
/* 初始化SGM51652HX芯片 */
sgm51652hx_init(&sgm51652hx_chip, &spi5_dev);
spi5_initialized = 1;
// for(i=0; i<ANALOG_MAX_CHANNELS; i++)
// spi5_write_range(RANGE_CH0+i, RGVL_UP_512); //8个通道都设置为单极性,0~5.26V量程
/* 设置通道量程 */
for(i=0; i<ANALOG_MAX_CHANNELS; i++)
sgm51652hx_set_range(&sgm51652hx_chip, i, RGVL_UP_512); //所有通道都设置为单极性,0~5.26V量程
// printf("SPI5 analog acquisition initialized successfully, supporting %d channels with %d-bit resolution\n",
// ANALOG_MAX_CHANNELS, analog_state.resolution); //SPI5模拟采集初始化成功,支持%d通道,分辨率%d位
return 0;
}
/*!
\brief SPI5去初始化
\param[in] none
\param[out] none
\retval none
*/
void spi5_analog_deinit(void)
{
/* 去初始化芯片 */
sgm51652hx_deinit(&sgm51652hx_chip);
/* 去初始化SPI设备 */
spi_deinit_dev(&spi5_dev);
/* 禁用SPI5 */
spi_disable(SPI5);
/* 去初始化SPI5 */
spi_i2s_deinit(SPI5);
/* CS引脚拉高 */
gpio_bit_set(GPIOG, GPIO_PIN_12);
spi5_initialized = 0;
}
static uint8_t spi5_validate_channel(uint8_t channel)
{
return (channel < ANALOG_MAX_CHANNELS) ? 1 : 0;
}
/*!
\brief 读取模拟量通道原始值
\param[in] channel: 通道号 (0-7)
\param[out] none
\retval 16位原始ADC值
*/
uint16_t spi5_read_analog_raw(uint8_t channel)
{
uint16_t raw_value = 0;
if(!spi5_initialized || !spi5_validate_channel(channel)) {
return 0;
}
/* 检查通道是否启用 */
if(0 == spi5_validate_channel(channel)) {
return 0;
}
/* 读取通道数据 */
if(sgm51652hx_read_channel(&sgm51652hx_chip, channel, &raw_value) != ERR_OK_SGM) {
return 0;
}
return raw_value;
}
float f_read_spi5_to_Vol(float read_data)
{
float data = 0.0f;
data = (1.25 * V_REF * read_data / 65535) ;
return data;
}
/**************************************************************
* 函数名称: 计算采集电流
* 参 数: float vol 芯片采集电压值
* 计算公式:U=1000*UADC/16; U为分流器两端电压,单位mV;
* I=1000*UADC/16R I为分流器两端电流,R为分流器两端电阻;
* 返 回 值: 计算出的采集电流
* 描 述:
***************************************************************/
float f_cal_curr(float vol)
{
float ret = 0.0,data = 0.0;
data = f_read_spi5_to_Vol(vol);
ret = (1000 * data)/16; //分流器两端电压,单位mV
ret = ret * 300 /75; //计算电流,分流器规格,300A 75mV
return ret;
}
//获取实际电压值(将这个值取平均值 50 次,调整系数1013)
float f_actual_voltage_data(float read_data )
{
float data = 0.0,actVol = 0.0;
data = f_read_spi5_to_Vol(read_data);
actVol = (data - 0.686f) / 8.2f / 270.0f * 2000270.0f;
//1013为系数,可自行调整以趋近实际值
return actVol * 1013 /1000;
}
//读取基准电压
float f_read_reference_voltage(float read_data)
{
float data = 0.0;
data = f_read_spi5_to_Vol(read_data);
return data ;
}
/*!
\brief 读取模拟量通道电压值
\param[in] channel: 通道号 (0-7)
channel 0 A枪CC1
channel 1 B枪CC1
channel 2 B枪外侧电压
channel 3 B枪外侧基准电压
channel 4 A枪外侧电压
channel 5 A枪外侧基准电压
channel 6 B枪电流
channel 7 A枪电流
\param[out] none
\retval 电压值 (V)
*/
float spi5_read_analog_voltage(uint8_t channel)
{
float f_actua_data; //实际数据值
if(!spi5_initialized || !spi5_validate_channel(channel))
{
return 0.0f;
}
/* 读取原始值 */
uint16_t raw_value = spi5_read_analog_raw(channel);
if(channel == 0) /* A枪CC1电压 */
f_actua_data = f_read_spi5_to_Vol(raw_value)*4;
else if(channel == 1) /* B枪CC1电压 */
f_actua_data = f_read_spi5_to_Vol(raw_value)*4;
else if(channel == 2) /* B枪外侧电压值*/
f_actua_data = f_actual_voltage_data(raw_value);
else if(channel == 3) /* B枪基准电压 */
{
f_actua_data = f_read_reference_voltage(raw_value);
}
else if(channel == 4) /* A枪外侧电压值 */
{
f_actua_data = f_actual_voltage_data(raw_value);
}
else if(channel == 5) /* A枪基准电压值 */
{
f_actua_data = f_read_reference_voltage(raw_value);
}
else if(channel == 6) /*B枪电流值*/
f_actua_data = f_cal_curr(raw_value);
else if(channel == 7) /*A枪电流值*/
f_actua_data = f_cal_curr(raw_value);
//printf("SPI5 channel(%d): raw=%d, realU=%.2f \n", channel, raw_value, f_actua_data);
return f_actua_data;
}
+76
View File
@@ -0,0 +1,76 @@
/*!
\file spi5_analog.h
\brief header file of SPI5 for analog data acquisition
\version 2026-03-09, V1.4.0, firmware for GD32H7xx
*/
/*
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 SPI5_ANALOG_H
#define SPI5_ANALOG_H
#include <stdint.h>
/* SPI5引脚定义 for analog acquisition */
#define SPI5_CS_PIN GPIO_PIN_12 // PG12 - 片选
#define SPI5_SCK_PIN GPIO_PIN_13 // PG13 - 时钟
#define SPI5_MISO_PIN GPIO_PIN_14 // PG14 - 数据输入
#define SPI5_MOSI_PIN GPIO_PIN_6 // PA6 - 数据输出
#define SPI5_CS_PORT GPIOG
#define SPI5_SCK_PORT GPIOG
#define SPI5_MISO_PORT GPIOG
#define SPI5_MOSI_PORT GPIOA
/* SPI5复用功能编号 */
#define SPI5_AF GPIO_AF_5
/* 本项目启用通道数 */
#define ANALOG_MAX_CHANNELS 8
/* 函数声明 */
/* GPIO配置 */
int adc_spi5_batVoltCurr_init(void);
/* 完整初始化和去初始化 */
int spi5_analog_init(void);
void spi5_analog_deinit(void);
/* 状态检查 */
uint8_t spi5_analog_is_initialized(void);
/* 基本读取操作 */
uint16_t spi5_read_analog_raw(uint8_t channel);
float spi5_read_analog_voltage(uint8_t channel);
uint8_t spi5_read_all_channels(float *voltages, uint8_t max_channels);
void spi5_analog_self_test(void);
#endif /* SPI5_ANALOG_H */
+303
View File
@@ -0,0 +1,303 @@
#include "gd32h7xx.h"
#include "adc_temp.h"
#include <string.h>
#include <math.h>
#include "usart.h"
#include "sys_drv_init.h"
#include "gd32h7xx_dma.h"
/* 忙等待延时函数 */
static void delay_ms(uint32_t ms)
{
/* 使用固定的时钟频率 600MHz 来计算延时,避免 SystemCoreClock 未初始化的问题 */
for(volatile uint32_t i = 0; i < (600000000 / 4000) * ms; i++)
{
__NOP();
}
}
/* ADC2 DMA连续采样缓冲:DMA中断只负责“完成标记”,数据处理放在 get_adc_data()。 */
#define ADC_TEMP_DMA_PERIPH DMA1
#define ADC_TEMP_DMA_CH DMA_CH0
#define ADC_TEMP_DMA_IRQN DMA1_Channel0_IRQn
#define ADC_TEMP_DMA_SAMPLE_CNT (16U)
static __IO uint32_t g_adc_dma_buf[ADC_TEMP_DMA_SAMPLE_CNT];
static volatile uint32_t g_adc_dma_frame_cnt;
__IO uint32_t adc_value[4];
void init_adc(void)
{
dma_single_data_parameter_struct dma_init_struct;
/* GPIO时钟使能 */
rcu_periph_clock_enable(RCU_GPIOC);
/* ADC时钟使能 */
rcu_periph_clock_enable(RCU_ADC2);
/* DMA时钟使能 */
rcu_periph_clock_enable(RCU_DMA1);
/* GPIO参数配置*/
gpio_mode_set(GPIOC, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_2);
/* ADC初始化 -- 根据数据手册,ADC2_IN0--PC2端子仅能使用ADC2第0个通道 */
adc_deinit(ADC2);
/* ADC时钟配置 */
adc_clock_config(ADC2, ADC_CLK_SYNC_HCLK_DIV6);
/* 单通道连续转换 + DMA */
adc_special_function_config(ADC2, ADC_SCAN_MODE, DISABLE);
adc_special_function_config(ADC2, ADC_CONTINUOUS_MODE, ENABLE);
/* 转换结果(12/14位)存放在 32 位寄存器的低 16 位中 */
adc_data_alignment_config(ADC2, ADC_DATAALIGN_RIGHT);
/* ADC 通道配置(规则通道组,单通道) */
adc_channel_length_config(ADC2, ADC_REGULAR_CHANNEL, 1);
/* 配置规则通道序列,决定 ADC 在转换时,对 PC2 引脚电压进行采样的时间长度*/
adc_regular_channel_config(ADC2, 0, ADC_CHANNEL_0, 240);
/* 禁用外部触发*/
adc_external_trigger_config(ADC2, ADC_REGULAR_CHANNEL, EXTERNAL_TRIGGER_DISABLE);
/* DMA配置:ADC2 regular data -> g_adc_dma_buf[] (循环) */
dma_deinit(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH);
dma_single_data_para_struct_init(&dma_init_struct);
dma_init_struct.request = DMA_REQUEST_ADC2;
dma_init_struct.periph_addr = (uint32_t)&ADC_RDATA(ADC2);
dma_init_struct.periph_inc = DMA_MEMORY_INCREASE_DISABLE;
dma_init_struct.memory0_addr = (uint32_t)g_adc_dma_buf;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_32BIT;
dma_init_struct.circular_mode = DMA_CIRCULAR_MODE_ENABLE;
dma_init_struct.direction = DMA_PERIPH_TO_MEMORY;
dma_init_struct.priority = DMA_PRIORITY_HIGH;
dma_init_struct.number = ADC_TEMP_DMA_SAMPLE_CNT;
dma_single_data_mode_init(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH, &dma_init_struct);
dma_interrupt_enable(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH, DMA_INT_HTF);
dma_interrupt_enable(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH, DMA_INT_FTF);
nvic_irq_enable(ADC_TEMP_DMA_IRQN, 6U, 0U);
/* 使能 ADC2 模块 */
adc_enable(ADC2);
/* wait for ADC stability */
delay_ms(1);
/* 配置校准模式,偏移量校准,用于纠正 ADC 自身的零点误差 */
adc_calibration_mode_config(ADC2, ADC_CALIBRATION_OFFSET);
/* 配置校准次数 */
adc_calibration_number(ADC2, ADC_CALIBRATION_NUM1);
/*执行校准 */
adc_calibration_enable(ADC2);
/* 启动DMA与ADC连续转换 */
memset((void *)g_adc_dma_buf, 0, sizeof(g_adc_dma_buf));
g_adc_dma_frame_cnt = 0U;
dma_channel_enable(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH);
adc_dma_mode_enable(ADC2);
adc_dma_request_after_last_enable(ADC2);
adc_software_trigger_enable(ADC2, ADC_REGULAR_CHANNEL);
}
uint32_t adc_channel_sample(uint8_t channel)
{
uint32_t timeout = 1000000U;
/* 重新配置规则通道 */
adc_regular_channel_config(ADC2, 0U, channel, 240);
/* 软件触发转换 */
adc_software_trigger_enable(ADC2, ADC_REGULAR_CHANNEL);
/* 等待转换结束 */
while(!adc_flag_get(ADC2, ADC_FLAG_EOC)){
if (timeout-- == 0U) {
return 0U;
}
}
/* 清除标志 */
adc_flag_clear(ADC2, ADC_FLAG_EOC);
/* 读取结果并返回 */
return (adc_regular_data_read(ADC2));
}
void DMA1_Channel0_IRQHandler(void)
{
if (SET == dma_interrupt_flag_get(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH, DMA_INT_FLAG_HTF)) {
dma_interrupt_flag_clear(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH, DMA_INT_FLAG_HTF);
g_adc_dma_frame_cnt++;
}
if (SET == dma_interrupt_flag_get(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH, DMA_INT_FLAG_FTF)) {
dma_interrupt_flag_clear(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH, DMA_INT_FLAG_FTF);
g_adc_dma_frame_cnt++;
}
}
void select_temp_channel(uint8_t flag_set)
{
//温度采集选通,
/*
OUT8、OUT7设置为高电平,A枪正温度
OUT8设置为高电平,OUT7设置为低电平,A枪负温度
OUT8设置为低电平,OUT7设置为高电平,B枪正温度
OUT8、OUT7设置为低电平,B枪负温度
*/
if(flag_set == 0)
{
TEMP_MULT_CTRA(1);
TEMP_MULT_CTRB(1);
TEMP_MULT_CTRC(1);
}
else if(flag_set == 1)
{
TEMP_MULT_CTRA(0);
TEMP_MULT_CTRB(1);
TEMP_MULT_CTRC(1);
}
else if(flag_set == 2)
{
TEMP_MULT_CTRA(1);
TEMP_MULT_CTRB(0);
TEMP_MULT_CTRC(1);
}
else if(flag_set == 3)
{
TEMP_MULT_CTRA(0);
TEMP_MULT_CTRB(0);
TEMP_MULT_CTRC(1);
}
else if(flag_set == 4)
{
TEMP_MULT_CTRA(1);
TEMP_MULT_CTRB(1);
TEMP_MULT_CTRC(0);
}
else if(flag_set == 5)
{
TEMP_MULT_CTRA(0);
TEMP_MULT_CTRB(1);
TEMP_MULT_CTRC(0);
}
else if(flag_set == 6)
{
TEMP_MULT_CTRA(1);
TEMP_MULT_CTRB(0);
TEMP_MULT_CTRC(0);
}
else if(flag_set == 7)
{
TEMP_MULT_CTRA(0);
TEMP_MULT_CTRB(0);
TEMP_MULT_CTRC(0);
}
}
double double_Vol_to_resistance(double vol)
{
double ret = 0;
//BL1117-50CX输出+5V_Tem,可通过测量VOL_Vin2校准+5V_Tem,Rt=3kΩ*Vo/+5V_Tem-Vo
ret = (3000 * vol) / (5.04 - vol);
return ret / 1000.0;
}
double PT1000_Vol_To_Temperature(double vol)
{
// 定义PT1000常数
double R;
const double R0 = 1000.0; // 0°C时的电阻值
const double A = 3.9083e-3; // IEC751系数
const double B = -5.775e-7;
const double C = -4.183e-12; // 负温度区系数
// 计算电阻值(kΩ单位)
R = double_Vol_to_resistance(vol);
/* 校准系数(保持kΩ单位) */
if (R > 1.219)
{
R = R * 1.01778; // 高温区补偿
}
else if (R > 1)
{
R = R * 1.00395; // 常温区补偿
}
else
{
R = R * 0.98884; // 低温区补偿
}
// 转换为Ω单位
double R_ohm = R * 1000.0;
// 计算温度
if (R_ohm >= R0)
{
// 正温度区使用二次方程
double discriminant = A * A - 4 * B * (1 - R_ohm / R0);
return (-A + sqrt(discriminant)) / (2 * B);
}
else
{
// 负温度区使用牛顿迭代法
const int max_iter = 100; // 最大迭代次数
const double tol = 1e-6; // 收敛误差
double T = -50.0; // 初始温度估计值
for (int i = 0; i < max_iter; i++)
{
double T2 = T * T;
double T3 = T2 * T;
// Callendar-Van Dusen方程
double f = R0 * (1 + A * T + B * T2 + C * (T - 100) * T3) - R_ohm;
// 检查是否收敛
if (fabs(f) < tol)
{
return T;
}
// 导数计算
double df = R0 * (A + 2 * B * T + C * (4 * T3 - 300 * T2));
// 避免除零错误
if (fabs(df) < 1e-10)
{
break;
}
// 牛顿迭代法更新
T -= f / df;
}
// 返回收敛值
return T;
}
}
uint32_t get_adc_data(void)
{
uint64_t sum = 0U;
uint32_t i;
/* 仅处理DMA中断已写入的采样数据,不触发硬件采样。 */
for (i = 0U; i < ADC_TEMP_DMA_SAMPLE_CNT; i++)
{
sum += g_adc_dma_buf[i];
// printf("data = %d\n", g_adc_dma_buf[i]);
}
return (uint32_t)(sum / ADC_TEMP_DMA_SAMPLE_CNT);
}
uint32_t adc_readval(void)
{
float ret, temp_data = 0.0f;
ret = (get_adc_data() * 3.0f) / 4095.0f;
temp_data = PT1000_Vol_To_Temperature(ret);
return temp_data;
}
void adc_test(uint8_t i)
{
float val_value;
(void)i;
delay_ms(1000);
adc_value[0] = adc_channel_sample(ADC_CHANNEL_0);
val_value = (adc_value[0] * 3.0f) / 4095.0f;
(void)val_value;
}
+25
View File
@@ -0,0 +1,25 @@
/*!
\file adc_temp.h
\brief ADC温度采集头文件
\version 2026-03-09, V1.4.0, firmware for GD32H7xx
*/
#ifndef __ADC_TEMP_H
#define __ADC_TEMP_H
#include <stdint.h>
//#include <math.h>
#ifdef __cplusplus
extern "C" {
#endif
void select_temp_channel(uint8_t flag_set);
void init_adc(void);
uint32_t get_adc_data(void);
uint32_t adc_readval(void);
#endif /* __ADC_TEMP_H */
+230
View File
@@ -0,0 +1,230 @@
#include "sgm51652hx.h"
#include "gd32h7xx_gpio.h"
#include "FreeRTOS.h"
#include "task.h"
/*!
\brief 初始化SGM51652HX芯片
\param[in] dev: 芯片设备结构体指针
\param[in] spi_dev: SPI设备结构体指针
\param[out] none
\retval 0: 成功, -1: 失败
*/
int sgm51652hx_init(sgm51652hx_dev_t *dev, spi_dev_t *spi_dev)
{
if (!dev || !spi_dev || !spi_dev->initialized) {
return ERR_INVALIDPARAMETER;
}
/* 初始化设备结构体 */
dev->spi_dev = *spi_dev;
dev->initialized = 1;
return ERR_OK_SGM;
}
/*!
\brief 去初始化SGM51652HX芯片
\param[in] dev: 芯片设备结构体指针
\param[out] none
\retval 0: 成功, -1: 失败
*/
int sgm51652hx_deinit(sgm51652hx_dev_t *dev)
{
if (!dev || !dev->initialized) {
return ERR_INVALIDPARAMETER;
}
/* 复位芯片 */
spi_rst_control(&dev->spi_dev, 1);
dev->initialized = 0;
return ERR_OK_SGM;
}
/*!
\brief 向SGM51652HX芯片写入寄存器
\param[in] dev: 芯片设备结构体指针
\param[in] reg: 寄存器地址
\param[in] value: 要写入的值
\param[out] none
\retval 0: 成功, -1: 失败
*/
int sgm51652hx_write_reg(sgm51652hx_dev_t *dev, uint8_t reg, uint8_t value)
{
if (!dev || !dev->initialized) {
return ERR_INVALIDPARAMETER;
}
/* 选中芯片 */
spi_cs_control(&dev->spi_dev, 1);
/* 开启发送 */
spi_master_transfer_start(dev->spi_dev.spi, SPI_TRANS_START);
/* 发送写命令:寄存器地址 << 1 | 1 */
uint8_t cmd = (reg << 1) | 1;
spi_transmit_receive_byte(dev->spi_dev.spi, cmd); //TBD:经测试,调用发送接收函数,后续采样正常,如果只调用发送函数,后续采样不正常;但另一版程序没有此问题,原因待定 lyn260411
/* 发送数据 */
spi_transmit_receive_byte(dev->spi_dev.spi, value);
/* 发送两个空字节 */
spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
/* 取消片选 */
spi_cs_control(&dev->spi_dev, 0);
return ERR_OK_SGM;
}
/*!
\brief 从SGM51652HX芯片读取寄存器
\param[in] dev: 芯片设备结构体指针
\param[in] reg: 寄存器地址
\param[out] value: 读取的值
\retval 0: 成功, -1: 失败
*/
int sgm51652hx_read_reg(sgm51652hx_dev_t *dev, uint8_t reg, uint8_t *value)
{
if (!dev || !dev->initialized || !value) {
return ERR_INVALIDPARAMETER;
}
/* 选中芯片 */
spi_cs_control(&dev->spi_dev, 1);
/* 开启发送 */
spi_master_transfer_start(dev->spi_dev.spi, SPI_TRANS_START);
/* 发送读命令:寄存器地址 << 1 | 0 */
uint8_t cmd = (reg << 1) | 0;
spi_transmit_byte(dev->spi_dev.spi, cmd);
/* 发送空字节 */
spi_transmit_byte(dev->spi_dev.spi, 0x00);
/* 读取数据 */
*value = spi_receive_byte(dev->spi_dev.spi);
/* 读取最后一个空字节 */
spi_receive_byte(dev->spi_dev.spi);
/* 取消片选 */
spi_cs_control(&dev->spi_dev, 0);
return ERR_OK_SGM;
}
/*!
\brief 设置SGM51652HX芯片通道量程
\param[in] dev: 芯片设备结构体指针
\param[in] channel: 通道号 (0-7)
\param[in] range: 量程配置值
\param[out] none
\retval 0: 成功, -1: 失败
*/
int sgm51652hx_set_range(sgm51652hx_dev_t *dev, uint8_t channel, sgm51652hxRngVlu_e range)
{
if (!dev || !dev->initialized || channel > 7) {
return ERR_INVALIDPARAMETER;
}
/* 计算量程寄存器地址 */
uint8_t reg = 0x05 + channel; /* RANGE_CH0 = 0x05 */
/* 写入量程配置 */
return sgm51652hx_write_reg(dev, reg, (uint8_t)range);
}
/*!
\brief 从SGM51652HX芯片读取通道数据
\param[in] dev: 芯片设备结构体指针
\param[in] channel: 通道号 (0-7)
\param[out] value: 读取的16位ADC值
\retval 0: 成功, -1: 失败
*/
int sgm51652hx_read_channel(sgm51652hx_dev_t *dev, uint8_t channel, uint16_t *value)
{
if (!dev || !dev->initialized || !value || channel > 7) {
return ERR_INVALIDPARAMETER;
}
/* 计算通道命令 */
uint16_t cmd = 0xC000 + (channel * 0x400); /* MAN_CH_0 = 0xC000 */
/* 选中芯片 */
spi_cs_control(&dev->spi_dev, 1);
/* 开启发送 */
spi_master_transfer_start(dev->spi_dev.spi, SPI_TRANS_START);
/* 发送命令 */
spi_transmit_receive_byte(dev->spi_dev.spi, (cmd >> 8) & 0xFF); //cmd高
spi_transmit_receive_byte(dev->spi_dev.spi, cmd & 0xFF); //
spi_transmit_receive_byte(dev->spi_dev.spi, 0x00); //MSB格式,接收高8位
spi_transmit_receive_byte(dev->spi_dev.spi, 0x00); //接收低8位
/* 取消片选 */
spi_cs_control(&dev->spi_dev, 0); //
/* ADC芯片需要转换时间,添加延时确保数据就绪 */
vTaskDelay(pdMS_TO_TICKS(1)); // 延时1毫秒
/* 再次选中芯片,读取数据 */
spi_cs_control(&dev->spi_dev, 1);
/* 发送四个空字节,同时读取数据 */
uint8_t rxbuf[4] = {0};
rxbuf[0] = spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
rxbuf[1] = spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
rxbuf[2] = spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
rxbuf[3] = spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
/* 取消片选 */
spi_cs_control(&dev->spi_dev, 0);
/* 合并16位数据 */
*value = (rxbuf[2] << 8) | rxbuf[3];
//printf("spix recv:%d %d %d %d\n", rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]);
return ERR_OK_SGM;
}
/*!
\brief 根据原始ADC值和量程配置计算电压 芯片引脚真实电压
\param[in] adc_val: 原始ADC值
\param[in] range: 量程配置值
\param[out] none
\retval 计算后的电压值 (V)
*/
float sgm51652hx_calc_voltage(uint16_t adc_val, sgm51652hxRngVlu_e range)
{
float voltage = 0.0f;
switch(range)
{
// ==================== 双极性 Bipolar ====================
case RGVL_BP_1024: // ±10.24V
voltage = (int16_t)adc_val * 10.24f / 32768.0f;
break;
case RGVL_BP_512: // ±5.12V
voltage = (int16_t)adc_val * 5.12f / 32768.0f;
break;
case RGVL_BP_256: // ±2.56V
voltage = (int16_t)adc_val * 2.56f / 32768.0f;
break;
// ==================== 单极性 Unipolar ====================
case RGVL_UP_1024: // 0~10.24V
voltage = (float)adc_val * 10.24f / 65535.0f;
break;
case RGVL_UP_512: // 0~5.12V
voltage = (float)adc_val * 5.12f / 65535.0f;
break;
default: // 错误配置
voltage = 0.0f;
break;
}
return voltage;
}
+129
View File
@@ -0,0 +1,129 @@
/*!
\file sgm51652hx
\brief
1. 芯片基本信息:
1)型号:sgm51652H4(4通道)、sgm51652H8(8通道)
2)类型:16位500kSPS单电源 SAR ADC
3)基准:片内4.096V高精度基准(温漂9ppm/℃)
4)供电:AVDD:4.75~5.25(典型5V),DVDD1.65~AVDD(典型3.3V)
2. 模拟输入特性
1)支持输入类型:双极性单端、单极性单端、双极性差分
2)量程配置(软件独立设置)
量程配置值 -- 输入范围 -- 对应VREF倍数
0000 ±10.24V ±2.5 * VREF
0001 ±5.12V ±1.25 * VREF
0010 ±2.56V ±0.625 * VREF
0101 0~10.24V 0~2.5 * VREF
0110 0~5.12V 0~1.25 * VREF
3. 数字接口与时序
1)通信接口:SPI,模式Mode3(CPOL=1 时钟空闲时为高电平、CPHA=1 数据在时钟下降沿采样)
2SCLK最高频率:17Mhz。(GD32H759芯片SPI3/SPI5挂在APB2总线,当前APB2时钟300Mhz,按32预分频设置)
3)转换时间1μs,采集时间1μs
4)帧结构:寄存器读写24个SCLK,数据读取32个SCLK
***每个通道读数,是CS拉低后【此句很重要,查看芯片数据手册波形图】,后面16个时钟读出的数据才是。虽然这个图是auto的,但跟手动是一样的。***
5)支持拓扑:菊花链,最多挂4片
4. 寄存器与命令系统
1)命令寄存器(16位,只写),详见 sgm51652hxCmd_e
2)关键程序寄存器,详见 sgm51652hxReg_e
5. 工作模式
1)自动扫描模式(AUTO_SCAN):按使能通道升序循环采集
2)手动通道模式(MAN_CH_n):固定采集指定单通道
3)待机模式(STDBY):低功耗,唤醒约20μs,保留配置
4)掉电模式(PWR_DOWN):极低功耗,唤醒需15ms
5)复位模式(RST):寄存器恢复默认值
*/
#ifndef _SGM51652HX_H_
#define _SGM51652HX_H_
#include <stdint.h>
#include "spi_if.h"
/******* Macro Definitions *******************/
#define __SGM51652H8__ 1 //SPI3-H4、SPI5-H8
#define ERR_OK_SGM 0
#define ERR_INVALIDPARAMETER -1
#define ERR_TIMEOUT_SGM -2
typedef struct {
spi_dev_t spi_dev; // SPI设备
uint8_t initialized; // 初始化状态
} sgm51652hx_dev_t;
// 兼容旧结构体名称
typedef sgm51652hx_dev_t sgm51652hxDevice_t;
//命令寄存器映射(命令寄存器是1个 16 位的只写寄存器,用于设置SGM51652H4/SGM51652H8 的工作模式。)
typedef enum{
NO_OP = 0x0000, //保持当前模式
STDBY = 0x8200, //进入待机模式
PWR_DN = 0x8300, //进入断电模式
RST = 0x8500, //重置程序寄存器
AUTO_RST = 0xA000, //启用通道自动扫描模式
MAN_CH_0 = 0xC000, //选择通道0手动读取模式
MAN_CH_1 = 0xC400, //选择通道1手动读取模式
MAN_CH_2 = 0xC800, //选择通道2手动读取模式
MAN_CH_3 = 0xCC00, //选择通道3手动读取模式
#if __SGM51652H8__ == 1
MAN_CH_4 = 0xD000,
MAN_CH_5 = 0xD400,
MAN_CH_6 = 0xD800,
MAN_CH_7 = 0xDC00,
#endif
MAN_AUX = 0xE000, //特殊功能,手动采辅助通道Aux,纯差分输入,无PGA/滤波,范围-VREF~+VREF
MAX_CMD_NUM
}sgm51652hxCmd_e;
//程序寄存器映射(程序寄存器是 16 位的可读写寄存器,用于设置SGM51652H4/SGM51652H8 的工作状态。)
/*
数据位 DB[15:9]是寄存器地址。数据位 DB[8]是写或读指令位。
在写操作周期中,SDI 引脚上的 DIN[7:0]是写入目标寄存器的数据,SDO 引脚上的 DIN[7:0]是从目标寄存器读回的数据。读回数据可用于验证写入是否成功。
在1个读周期中,SDI 数据位 DB[8]是读指令位。SDO DOUT[7:0]上的数据是来自目标地址程序寄存器的读回数据。
读回数据是MSB(高位优先)模式
*/
typedef enum{
AUTO_SEQ_EN = 0x01, // 自动扫描通道使能。通道自扫描模式下,扫描通道的选定,数据bit7-bit0代表通道7-通道9,0:不选定该通道,1:选定该通道
CHANNEL_PD = 0x02, // 通道独立断电。通道自扫描模式下,通道电源控制,数据bit7-bit0代表通道7-通道9,0:通道开启电源,1:通道电源关闭
FEATURE_SEL, // 菊花链/输出格式
RANGE_CH0 = 0x05, // 0x05~0x0C:CH0-CH7量程配置。 RANGE_CHn 通道 n 选择输入范围位,数据bit7-bit4补0,数据bit3-bit0定义如下
RANGE_CH1, // 0000 = 输入范围设置为±2.5 × V(默认)
RANGE_CH2, // 0001 = 输入范围设置为±1.25 × V
RANGE_CH3, // 0010 = 输入范围设置为±0.625 × V
#if __SGM51652H4__ == 0 // 0101 = 输入范围设置为 0 到 2.5 × V
RANGE_CH4, // 0110 = 输入范围设置为 0 到 1.25 × V
RANGE_CH5,
RANGE_CH6,
RANGE_CH7,
#endif
FLOATING_DETECTION_EN = 0x0D, //悬空检测使能。输入浮动检测启用控制,数据bit7:0 = 禁用,1 = 启用,bit6-bit0 保留,补0
FLOATING_DETECTION_STATUS, //悬空检测状态。查询输入浮动状态,数据bit7-bit0代表通道7-1,0=通道未处于浮动状态(默认)1=通道处于浮动状态
CMD_READ_BK = 0x3F, //命令回读(只读),查询前1个数据帧中执行的命令
}sgm51652hxReg_e;
/* 量程配置值(写进CHx量程寄存器的配置值)
* 详见本文件顶部2.2。 双极性bipolar,单极性unipolar
*/
typedef enum{
RGVL_BP_1024 = 0x00, //类型:双极性,输入范围:±10.24V
RGVL_BP_512,
RGVL_BP_256,
RGVL_UP_1024 = 0x05, //类型:单极性,输入范围:0~10.24V
RGVL_UP_512,
}sgm51652hxRngVlu_e;
// 芯片操作函数
int sgm51652hx_init(sgm51652hx_dev_t *dev, spi_dev_t *spi_dev);
int sgm51652hx_deinit(sgm51652hx_dev_t *dev);
int sgm51652hx_write_reg(sgm51652hx_dev_t *dev, uint8_t reg, uint8_t value);
int sgm51652hx_read_reg(sgm51652hx_dev_t *dev, uint8_t reg, uint8_t *value);
int sgm51652hx_set_range(sgm51652hx_dev_t *dev, uint8_t channel, sgm51652hxRngVlu_e range);
int sgm51652hx_read_channel(sgm51652hx_dev_t *dev, uint8_t channel, uint16_t *value);
float sgm51652hx_calc_voltage(uint16_t adc_val, sgm51652hxRngVlu_e range);
#endif
+145
View File
@@ -0,0 +1,145 @@
#include "spi_if.h"
#include <gd32h7xx_spi.h>
#include <gd32h7xx_gpio.h>
/*!
\brief SPIx发送数据 (私有函数)
\param[in] spi_periph: SPIx(x=0,1,2,3,4,5)
\param[in] data: 要发送的数据(uint8_t,一次发送1个字节!!)
\param[out] none
\retval none
*/
void spi_transmit_byte(uint32_t spi_periph, uint8_t data)
{
/* 等待发送缓冲区有空间 */
while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TP));
/* 发送数据 */
spi_i2s_data_transmit(spi_periph, data);
}
/*!
\brief SPIx接收数据 (私有函数)
\param[in] spi_periph: SPIx(x=0,1,2,3,4,5)
\param[in] none
\param[out] none
\retval 接收到的数据(uint8_t 一次接收1个字节)
*/
uint8_t spi_receive_byte(uint32_t spi_periph)
{
/* 等待接收缓冲区非空 */
while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RP));
/* 接收数据 */
return (uint8_t)spi_i2s_data_receive(spi_periph);
}
/*!
\brief SPIx发送接收数据 (私有函数)
\param[in] spi_periph: SPIx(x=0,1,2,3,4,5)
\param[in] tx_data: 要发送的数据
\param[out] none
\retval 接收到的数据
*/
uint8_t spi_transmit_receive_byte(uint32_t spi_periph, uint8_t tx_data)
{
/* 等待发送缓冲区有空间 */
while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TP));
/* 发送数据 */
spi_i2s_data_transmit(spi_periph, tx_data);
/* 等待接收缓冲区非空 */
while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RP));
/* 接收数据 */
return (uint8_t)spi_i2s_data_receive(spi_periph);
}
/*!
\brief 控制SPI设备的片选引脚
\param[in] dev: SPI设备结构体指针
\param[in] enable: 1-选中设备, 0-取消选中
\param[out] none
\retval none
*/
void spi_cs_control(spi_dev_t *dev, uint8_t enable)
{
if (dev && dev->csPort) {
if (enable) {
/* 拉低片选引脚,选中设备 */
gpio_bit_reset((uint32_t)dev->csPort, dev->csPin);
} else {
/* 拉高片选引脚,取消选中设备 */
gpio_bit_set((uint32_t)dev->csPort, dev->csPin);
}
}
}
/*!
\brief 控制SPI设备的复位引脚
\param[in] dev: SPI设备结构体指针
\param[in] enable: 1-复位, 0-正常
\param[out] none
\retval none
*/
void spi_rst_control(spi_dev_t *dev, uint8_t enable)
{
if (dev && dev->rstPort) {
if (enable) {
/* 拉低复位引脚,复位设备 */
gpio_bit_reset((uint32_t)dev->rstPort, dev->rstPin);
} else {
/* 拉高复位引脚,设备正常工作 */
gpio_bit_set((uint32_t)dev->rstPort, dev->rstPin);
}
}
}
/*!
\brief 初始化SPI设备
\param[in] dev: SPI设备结构体指针
\param[in] spi_periph: SPI外设
\param[in] csPort: 片选GPIO端口
\param[in] csPin: 片选GPIO引脚
\param[in] rstPort: 复位GPIO端口
\param[in] rstPin: 复位GPIO引脚
\param[out] none
\retval 0: 成功, -1: 失败
*/
int spi_init_dev(spi_dev_t *dev, uint32_t spi_periph, uint32_t csPort, uint16_t csPin, uint32_t rstPort, uint16_t rstPin)
{
if (!dev) {
return -1;
}
/* 初始化设备结构体 */
dev->spi = spi_periph;
dev->csPort = csPort;
dev->csPin = csPin;
dev->rstPort = rstPort;
dev->rstPin = rstPin;
dev->initialized = 1;
return 0;
}
/*!
\brief 去初始化SPI设备
\param[in] dev: SPI设备结构体指针
\param[out] none
\retval 0: 成功, -1: 失败
*/
int spi_deinit_dev(spi_dev_t *dev)
{
if (!dev || !dev->initialized) {
return -1;
}
dev->initialized = 0;
return 0;
}
+41
View File
@@ -0,0 +1,41 @@
/*!
\file spi_if.h
\brief spi接收发送统一接口函数
\version firmware for GD32H7xx
*/
#ifndef _SPI_IF_H_
#define _SPI_IF_H_
#include <stdint.h>
typedef struct {
uint32_t spi; // MCU对应的SPI口
uint32_t rstPort; // 复位GPIO引脚
uint16_t rstPin; // 复位pin引脚
uint32_t csPort; // 片选GPIO引脚
uint16_t csPin; // 片选pin引脚
uint8_t initialized; // 初始化状态
} spi_dev_t;
// 兼容旧结构体名称
typedef spi_dev_t spi_t;
/* 对外接口函数 */
void spi_transmit_byte(uint32_t spi_periph, uint8_t data);
uint8_t spi_receive_byte(uint32_t spi_periph);
uint8_t spi_transmit_receive_byte(uint32_t spi_periph, uint8_t tx_data);
// GPIO控制函数
void spi_cs_control(spi_dev_t *dev, uint8_t enable);
void spi_rst_control(spi_dev_t *dev, uint8_t enable);
// SPI初始化函数
int spi_init_dev(spi_dev_t *dev, uint32_t spi_periph, uint32_t csPort, uint16_t csPin, uint32_t rstPort, uint16_t rstPin);
int spi_deinit_dev(spi_dev_t *dev);
#endif
+333
View File
@@ -0,0 +1,333 @@
/**
* @file app_fatfs.c
* @brief FATFS 文件系统 API 桩实现(功能待实现)
*
* 本文件为 app_fatfs.h 中声明的所有函数提供桩实现。
* 所有函数返回"未实现"错误状态,待 FATFS 功能完整移植后再替换为真实实现。
*/
#include "app_fatfs.h"
#include <string.h>
/* 文件系统基础操作 */
FS_Status fs_init(void)
{
return FS_STATUS_INIT_FAILED;
}
FS_Status fs_deinit(void)
{
return FS_STATUS_OK;
}
FS_Status fs_mount(const char *path)
{
(void)path;
return FS_STATUS_FS_ERROR;
}
FS_Status fs_unmount(const char *path)
{
(void)path;
return FS_STATUS_OK;
}
FS_Status fs_check_space(void)
{
return FS_STATUS_NO_SPACE;
}
bool fs_get_is_mounted(void)
{
return false;
}
/* 文件系统格式化 */
FS_Status fs_format_drive(const char *drive_path)
{
(void)drive_path;
return FS_STATUS_FS_ERROR;
}
FS_Status fs_format_drive_fat16(const char *drive_path)
{
(void)drive_path;
return FS_STATUS_FS_ERROR;
}
/* 目录操作 */
FS_Status fs_mkdir(const char *path)
{
(void)path;
return FS_STATUS_FS_ERROR;
}
FS_Status fs_dir_exists(const char *path, bool *exists)
{
(void)path;
if (exists != NULL) {
*exists = false;
}
return FS_STATUS_OK;
}
FS_Status fs_list_dir(const char *path, FS_FileInfo *file_list, uint32_t *count, uint32_t max_count)
{
(void)path;
(void)file_list;
(void)max_count;
if (count != NULL) {
*count = 0;
}
return FS_STATUS_OK;
}
/* 文件操作 */
FS_Status fs_create_file(const char *path)
{
(void)path;
return FS_STATUS_FS_ERROR;
}
FS_Status fs_delete_file(const char *path)
{
(void)path;
return FS_STATUS_FS_ERROR;
}
FS_Status fs_file_exists(const char *path, bool *exists)
{
(void)path;
if (exists != NULL) {
*exists = false;
}
return FS_STATUS_OK;
}
FS_Status fs_get_file_size(const char *path, uint32_t *size)
{
(void)path;
if (size != NULL) {
*size = 0;
}
return FS_STATUS_FILE_NOT_FOUND;
}
FS_Status fs_get_file_info(const char *path, FS_FileInfo *info)
{
(void)path;
if (info != NULL) {
memset(info, 0, sizeof(*info));
}
return FS_STATUS_FILE_NOT_FOUND;
}
FS_Status fs_rename_file(const char *old_path, const char *new_path)
{
(void)old_path;
(void)new_path;
return FS_STATUS_FS_ERROR;
}
FS_Status fs_copy_file(const char *src_path, const char *dst_path)
{
(void)src_path;
(void)dst_path;
return FS_STATUS_FS_ERROR;
}
/* 文件读写 */
FS_Status fs_open(void *file, const char *path, uint8_t mode)
{
(void)file;
(void)path;
(void)mode;
return FS_STATUS_FS_ERROR;
}
FS_Status fs_close(void *file)
{
(void)file;
return FS_STATUS_OK;
}
FS_Status fs_read(void *file, void *buffer, uint32_t size, uint32_t *bytes_read)
{
(void)file;
(void)buffer;
(void)size;
if (bytes_read != NULL) {
*bytes_read = 0;
}
return FS_STATUS_READ_FAILED;
}
FS_Status fs_write(void *file, const void *buffer, uint32_t size, uint32_t *bytes_written)
{
(void)file;
(void)buffer;
(void)size;
if (bytes_written != NULL) {
*bytes_written = 0;
}
return FS_STATUS_WRITE_FAILED;
}
FS_Status fs_seek(void *file, uint32_t offset)
{
(void)file;
(void)offset;
return FS_STATUS_FS_ERROR;
}
FS_Status fs_truncate(void *file)
{
(void)file;
return FS_STATUS_FS_ERROR;
}
FS_Status fs_sync(void *file)
{
(void)file;
return FS_STATUS_OK;
}
/* 文件内容操作 */
FS_Status fs_read_file_content(const char *path, char *buffer, uint32_t buffer_size, FS_ReadRange *range)
{
(void)path;
(void)buffer;
(void)buffer_size;
if (range != NULL) {
range->actual_read = 0;
}
return FS_STATUS_READ_FAILED;
}
FS_Status fs_write_file_content(const char *path, const void *data, uint32_t size, bool append)
{
(void)path;
(void)data;
(void)size;
(void)append;
return FS_STATUS_WRITE_FAILED;
}
FS_Status fs_write_string(const char *path, const char *str, bool append)
{
(void)path;
(void)str;
(void)append;
return FS_STATUS_WRITE_FAILED;
}
/* 文件系统信息 */
FS_Status fs_get_volume_info(const char *path, uint32_t *total_kb, uint32_t *free_kb, float *usage_percent)
{
(void)path;
if (total_kb != NULL) *total_kb = 0;
if (free_kb != NULL) *free_kb = 0;
if (usage_percent != NULL) *usage_percent = 0.0f;
return FS_STATUS_FS_ERROR;
}
/* 通用文件管理 */
FS_Status fs_file_manager_check_and_roll_ex(const FileManagerConfig *config, const char *current_file,
uint32_t pending_write_size, char *new_filename, uint32_t new_filename_len)
{
(void)config;
(void)current_file;
(void)pending_write_size;
(void)new_filename;
(void)new_filename_len;
return FS_STATUS_FS_ERROR;
}
FS_Status fs_file_manager_create_new(const FileManagerConfig *config, char *new_filename, uint32_t max_len)
{
(void)config;
if (new_filename != NULL && max_len > 0) new_filename[0] = '\0';
return FS_STATUS_FS_ERROR;
}
FS_Status fs_file_manager_ensure_dir_exists(const FileManagerConfig *config)
{
(void)config;
return FS_STATUS_FS_ERROR;
}
FS_Status fs_file_manager_get_count(const FileManagerConfig *config, uint32_t *count)
{
(void)config;
if (count != NULL) *count = 0;
return FS_STATUS_OK;
}
FS_Status fs_file_manager_delete_oldest(const FileManagerConfig *config)
{
(void)config;
return FS_STATUS_FS_ERROR;
}
FS_Status fs_file_manager_find_latest(const FileManagerConfig *config, char *filename, uint32_t max_len)
{
(void)config;
if (filename != NULL && max_len > 0) filename[0] = '\0';
return FS_STATUS_FILE_NOT_FOUND;
}
FS_Status fs_file_manager_find_oldest(const FileManagerConfig *config, char *filename, uint32_t max_len)
{
(void)config;
if (filename != NULL && max_len > 0) filename[0] = '\0';
return FS_STATUS_FILE_NOT_FOUND;
}
FS_Status fs_file_manager_resolve_log_append_path(const FileManagerConfig *config, char *filename, uint32_t max_len)
{
(void)config;
if (filename != NULL && max_len > 0) filename[0] = '\0';
return FS_STATUS_FS_ERROR;
}
void fs_log_set_filename_start_seq(uint8_t start_seq)
{
(void)start_seq;
}
FS_Status fs_generate_timestamp_filename(const FileManagerConfig *config, char *filename, uint32_t max_len)
{
(void)config;
if (filename != NULL && max_len > 0) filename[0] = '\0';
return FS_STATUS_FS_ERROR;
}
bool fs_is_filename_valid(const char *filename, const FileManagerConfig *config)
{
(void)filename;
(void)config;
return false;
}
FS_Status fs_cleanup_invalid_files(const FileManagerConfig *config)
{
(void)config;
return FS_STATUS_OK;
}
uint32_t fs_get_current_fat_time(void)
{
return 0;
}
FS_Status fs_get_current_time_string(char *buffer, uint32_t size)
{
if (buffer != NULL && size > 0) buffer[0] = '\0';
return FS_STATUS_FS_ERROR;
}
FS_Status fs_get_formatted_time_string(char *buffer, uint32_t size)
{
if (buffer != NULL && size > 0) buffer[0] = '\0';
return FS_STATUS_FS_ERROR;
}
+156
View File
@@ -0,0 +1,156 @@
/**
* @file app_fatfs.h
* @brief FATFS 文件系统兼容头文件(整合自 CCU601E_D)
*
* CCU621_M 平台:FATFS 功能由 flash_file_mgr + externalflash + sdmmc 共同实现。
* 本头文件为从 CCU601E_D 移植的应用层代码提供兼容的类型和 API 声明。
*/
#ifndef APP_FATFS_H
#define APP_FATFS_H
#include <stdint.h>
#include <stdbool.h>
#include "publicdata/type.h"
#include "publicdata/public_define.h"
#define FS_PATH_LEN 64
#define FS_FILE_NAME_LEN 32
#define FS_READ_BUF_SIZE 512
#define LOG_DIR_PATH "0:/log"
#define LOG_FILE_MAX_SIZE (2*1024*1024)
#define LOG_FILE_MAX_COUNT 3
#define LOG_MAX_FILES 20
#if FATFS_ENABLE_FAULT
#define FAULT_DIR_PATH "0:/fault"
#define FAULT_FILE_MAX_SIZE (1024*500)
#define FAULT_FILE_MAX_COUNT 2
#define FAULT_MAX_FILES 20
#endif
#if FATFS_ENABLE_ORDER
#define ORDER_DIR_PATH "0:/order"
#define ORDER_FILE_MAX_SIZE (1024*500)
#define ORDER_FILE_MAX_COUNT 2
#define ORDER_MAX_FILES 20
#endif
#define LOG_SYNC_INTERVAL 10
typedef enum {
FS_STATUS_OK = 0,
FS_STATUS_INIT_FAILED,
FS_STATUS_FS_ERROR,
FS_STATUS_FILE_FULL,
FS_STATUS_WRITE_FAILED,
FS_STATUS_READ_FAILED,
FS_STATUS_FILE_NOT_FOUND,
FS_STATUS_PARAM_ERROR,
FS_STATUS_NO_SPACE
} FS_Status;
typedef struct {
char name[FS_FILE_NAME_LEN];
uint32_t size;
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
} FS_FileInfo;
typedef struct {
uint32_t start_pos;
uint32_t end_pos;
uint32_t actual_read;
} FS_ReadRange;
typedef enum {
FILE_TYPE_LOG = 0,
FILE_TYPE_FAULT,
FILE_TYPE_ORDER,
FILE_TYPE_MAX
} FileType;
typedef struct {
FileType type;
const char *dir_path;
const char *file_prefix;
const char *file_extension;
uint32_t max_file_size;
uint32_t max_file_count;
} FileManagerConfig;
/* 文件系统基础操作 */
FS_Status fs_init(void);
FS_Status fs_deinit(void);
FS_Status fs_mount(const char *path);
FS_Status fs_unmount(const char *path);
FS_Status fs_check_space(void);
bool fs_get_is_mounted(void);
/* 文件系统格式化 */
FS_Status fs_format_drive(const char *drive_path);
FS_Status fs_format_drive_fat16(const char *drive_path);
/* 目录操作 */
FS_Status fs_mkdir(const char *path);
FS_Status fs_dir_exists(const char *path, bool *exists);
FS_Status fs_list_dir(const char *path, FS_FileInfo *file_list, uint32_t *count, uint32_t max_count);
/* 文件操作 */
FS_Status fs_create_file(const char *path);
FS_Status fs_delete_file(const char *path);
FS_Status fs_file_exists(const char *path, bool *exists);
FS_Status fs_get_file_size(const char *path, uint32_t *size);
FS_Status fs_get_file_info(const char *path, FS_FileInfo *info);
FS_Status fs_rename_file(const char *old_path, const char *new_path);
FS_Status fs_copy_file(const char *src_path, const char *dst_path);
/* 文件读写(FatFs FIL 类型用 void* 代理,避免引入 ff.h */
FS_Status fs_open(void *file, const char *path, uint8_t mode);
FS_Status fs_close(void *file);
FS_Status fs_read(void *file, void *buffer, uint32_t size, uint32_t *bytes_read);
FS_Status fs_write(void *file, const void *buffer, uint32_t size, uint32_t *bytes_written);
FS_Status fs_seek(void *file, uint32_t offset);
FS_Status fs_truncate(void *file);
FS_Status fs_sync(void *file);
/* 文件内容操作 */
FS_Status fs_read_file_content(const char *path, char *buffer, uint32_t buffer_size, FS_ReadRange *range);
FS_Status fs_write_file_content(const char *path, const void *data, uint32_t size, bool append);
FS_Status fs_write_string(const char *path, const char *str, bool append);
/* 文件系统信息 */
FS_Status fs_get_volume_info(const char *path, uint32_t *total_kb, uint32_t *free_kb, float *usage_percent);
/* 通用文件管理 */
FS_Status fs_file_manager_check_and_roll_ex(const FileManagerConfig *config, const char *current_file,
uint32_t pending_write_size, char *new_filename, uint32_t new_filename_len);
FS_Status fs_file_manager_create_new(const FileManagerConfig *config, char *new_filename, uint32_t max_len);
FS_Status fs_file_manager_ensure_dir_exists(const FileManagerConfig *config);
FS_Status fs_file_manager_get_count(const FileManagerConfig *config, uint32_t *count);
FS_Status fs_file_manager_delete_oldest(const FileManagerConfig *config);
FS_Status fs_file_manager_find_latest(const FileManagerConfig *config, char *filename, uint32_t max_len);
FS_Status fs_file_manager_find_oldest(const FileManagerConfig *config, char *filename, uint32_t max_len);
FS_Status fs_file_manager_resolve_log_append_path(const FileManagerConfig *config, char *filename, uint32_t max_len);
void fs_log_set_filename_start_seq(uint8_t start_seq);
FS_Status fs_generate_timestamp_filename(const FileManagerConfig *config, char *filename, uint32_t max_len);
bool fs_is_filename_valid(const char *filename, const FileManagerConfig *config);
FS_Status fs_cleanup_invalid_files(const FileManagerConfig *config);
uint32_t fs_get_current_fat_time(void);
FS_Status fs_get_current_time_string(char *buffer, uint32_t size);
FS_Status fs_get_formatted_time_string(char *buffer, uint32_t size);
extern const FileManagerConfig g_log_file_config;
#if FATFS_ENABLE_FAULT
extern const FileManagerConfig g_fault_file_config;
#endif
#if FATFS_ENABLE_ORDER
extern const FileManagerConfig g_order_file_config;
#endif
#endif /* APP_FATFS_H */
+57
View File
@@ -0,0 +1,57 @@
/**
* @file fatfs_card.h
* @brief 卡号白名单兼容头文件(整合自 CCU601E_D)
*
* 实际存储由 flash_file_mgr/card_flash_impl 通过 EEPROM + SPI Flash 实现。
*/
#ifndef FATFS_CARD_H
#define FATFS_CARD_H
#include <stdint.h>
#include <stdbool.h>
#define CARD_DIR_PATH "0:/card"
#define CARD_FILE_NAME "card_data.csv"
#define CARD_NUM_MAX_LEN 32
#define CARD_MAX_COUNT 595
typedef struct {
char card_num[CARD_NUM_MAX_LEN];
uint32_t balance;
uint32_t create_time;
uint32_t last_use_time;
uint16_t status;
uint8_t reserved[8];
} CardInfo;
typedef enum {
CARD_STATUS_OK = 0,
CARD_STATUS_INIT_FAILED,
CARD_STATUS_FS_ERROR,
CARD_STATUS_FILE_FULL,
CARD_STATUS_WRITE_FAILED,
CARD_STATUS_READ_FAILED,
CARD_STATUS_FILE_NOT_FOUND,
CARD_STATUS_PARAM_ERROR,
CARD_STATUS_NO_SPACE,
CARD_STATUS_CARD_EXIST,
CARD_STATUS_CARD_NOT_FOUND,
CARD_STATUS_ALREADY_INIT
} CardStatus;
CardStatus card_init(void);
void card_deinit(void);
CardStatus card_add(const CardInfo *card_info);
CardStatus card_update(const CardInfo *card_info);
CardStatus card_delete(const char *card_num);
CardStatus card_find(const char *card_num, CardInfo *card_info);
CardStatus card_get_count(uint32_t *count);
CardStatus card_get_all(CardInfo *card_list, uint32_t *count, uint32_t max_count);
CardStatus card_check_exist(const char *card_num, bool *exist);
CardStatus card_backup(void);
CardStatus card_restore(void);
CardStatus card_clear_all(void);
#endif /* FATFS_CARD_H */
+12
View File
@@ -0,0 +1,12 @@
/**
* @file fatfs_fault.h
* @brief FATFS 故障记录模块兼容头文件
*/
#ifndef FATFS_FAULT_H
#define FATFS_FAULT_H
#include <stdint.h>
/* Fault module placeholder — fault storage is handled by flash_file_mgr/fault_flash_impl */
#endif /* FATFS_FAULT_H */
+100
View File
@@ -0,0 +1,100 @@
/**
* @file fatfs_init.c
* @brief FATFS 初始化模块桩实现(功能待实现)
*
* 本文件为 fatfs_init.h 中声明的函数提供桩实现。
* 所有函数提供安全默认行为,待 FATFS 功能完整移植后再替换为真实实现。
*/
#include "fatfs_init.h"
#include <stddef.h>
void fatfs_init(void)
{
/* FATFS 尚未初始化 */
}
void fatfs_action(void)
{
/* 无操作 */
}
#if FATFS_SHELL_RM_CAT_EN
U8_T u8_file_clear_action(U8_T u8_type, U8_T u8_action, U8_T u8_flag)
{
(void)u8_type;
(void)u8_action;
(void)u8_flag;
return 0;
}
U8_T u8_file_set_cat_cmd(char *path)
{
(void)path;
return 0;
}
U8_T u8_file_card_action(U8_T action, const char *card_num, U32_T balance)
{
(void)action;
(void)card_num;
(void)balance;
return 0;
}
#endif
U8_T cache_add_log(const char *format, ...)
{
(void)format;
return 0;
}
U8_T cache_add_card(const char *card_num, U32_T balance)
{
(void)card_num;
(void)balance;
return 0;
}
U8_T cache_add_fault(const void *fault_data)
{
(void)fault_data;
return 0;
}
U8_T cache_add_order(const void *order_data)
{
(void)order_data;
return 0;
}
void cache_process_all(void)
{
/* 无缓存数据待处理 */
}
void cache_clear_all(void)
{
/* 无缓存数据待清除 */
}
U8_T fatfs_set_write_disable_flag(U8_T flag_mask, U8_T enable)
{
(void)flag_mask;
(void)enable;
return 0;
}
U8_T fatfs_get_write_disable_flag(U8_T flag_mask)
{
(void)flag_mask;
return 0;
}
void v_app_fatfs_log_real_file_name(char *fileName, int len, int pathEn)
{
if (fileName != NULL && len > 0) {
fileName[0] = '\0';
}
(void)pathEn;
}
+99
View File
@@ -0,0 +1,99 @@
/**
* @file fatfs_init.h
* @brief FATFS 模块初始化兼容头文件(整合自 CCU601E_D)
*
* CCU621_M 的 BSP 没有独立的 app_fatfs 模块,FATFS 功能通过
* flash_file_mgr + externalflash + sdmmc 实现。
* 本头文件为从 CCU601E_D 移植的应用层代码提供兼容的 API 声明。
*/
#ifndef FATFS_INIT_H
#define FATFS_INIT_H
#ifdef __cplusplus
extern "C" {
#endif
#include "publicdata/type.h"
#include "publicdata/public_define.h"
#define USE_FREERTOS_HEAP
#define USE_CACHE_MUTEX
#ifdef USE_FREERTOS_HEAP
#include "FreeRTOS.h"
#include "task.h"
#define CACHE_MALLOC(size) pvPortMalloc(size)
#define CACHE_FREE(ptr) vPortFree(ptr)
#else
#define CACHE_MALLOC(size) malloc(size)
#define CACHE_FREE(ptr) free(ptr)
#endif
#ifdef USE_CACHE_MUTEX
#include "semphr.h"
#define CACHE_MUTEX_CREATE() xSemaphoreCreateMutex()
#define CACHE_MUTEX_TAKE(mutex) xSemaphoreTake(mutex, portMAX_DELAY)
#define CACHE_MUTEX_GIVE(mutex) xSemaphoreGive(mutex)
#define CACHE_MUTEX_DELETE(mutex) vSemaphoreDelete(mutex)
#else
#define CACHE_MUTEX_CREATE() NULL
#define CACHE_MUTEX_TAKE(mutex) (void)0
#define CACHE_MUTEX_GIVE(mutex) (void)0
#define CACHE_MUTEX_DELETE(mutex) (void)0
#endif
#define FATFS_SHELL_RM_CAT_EN (0)
#define FATFS_FILE_ACTION_FLAG (0xFF)
#define FATFS_WRITE_DISABLE_TCP_DEBUG 0x10
#define FATFS_PRINT_EN (0)
#if FATFS_PRINT_EN
#define FATFS_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define FATFS_PRINT(fmt, ...) do {} while(0)
#endif
typedef enum {
CACHE_TYPE_NONE = 0,
#if FATFS_ENABLE_LOG
CACHE_TYPE_LOG,
#endif
#if FATFS_ENABLE_CARD
CACHE_TYPE_CARD,
#endif
#if FATFS_ENABLE_FAULT
CACHE_TYPE_FAULT,
#endif
#if FATFS_ENABLE_ORDER
CACHE_TYPE_ORDER,
#endif
} CacheType;
void fatfs_init(void);
void fatfs_action(void);
#if FATFS_SHELL_RM_CAT_EN
U8_T u8_file_clear_action(U8_T u8_type, U8_T u8_action, U8_T u8_flag);
U8_T u8_file_set_cat_cmd(char *path);
U8_T u8_file_card_action(U8_T action, const char *card_num, U32_T balance);
#endif
U8_T cache_add_log(const char *format, ...);
U8_T cache_add_card(const char *card_num, U32_T balance);
U8_T cache_add_fault(const void *fault_data);
U8_T cache_add_order(const void *order_data);
void cache_process_all(void);
void cache_clear_all(void);
U8_T fatfs_set_write_disable_flag(U8_T flag_mask, U8_T enable);
U8_T fatfs_get_write_disable_flag(U8_T flag_mask);
void v_app_fatfs_log_real_file_name(char *fileName, int len, int pathEn);
#ifdef __cplusplus
}
#endif
#endif /* FATFS_INIT_H */
+14
View File
@@ -0,0 +1,14 @@
/**
* @file fatfs_log.h
* @brief FATFS 日志模块兼容头文件
*/
#ifndef FATFS_LOG_H
#define FATFS_LOG_H
#include <stdint.h>
/* Log module placeholder — log storage is handled by flash_file_mgr */
void fatfs_log_init(void);
void fatfs_log_write(const char *format, ...);
#endif /* FATFS_LOG_H */
+12
View File
@@ -0,0 +1,12 @@
/**
* @file fatfs_order.h
* @brief FATFS 充电订单模块兼容头文件
*/
#ifndef FATFS_ORDER_H
#define FATFS_ORDER_H
#include <stdint.h>
/* Order module placeholder — order storage is handled by flash_file_mgr/unsettled_order_mng */
#endif /* FATFS_ORDER_H */
+485
View File
@@ -0,0 +1,485 @@
#include "gd32h7xx.h"
#include "app_rtc/app_rtc.h"
#include "publicdata/publicdata.h"
#include <stdio.h>
#include <string.h>
/* 秒换算常量(用于时间戳转换函数)。 */
#define xMINUTE (60U)
#define xHOUR (60U * xMINUTE)
#define xDAY (24U * xHOUR)
#define xYEAR (365U * xDAY)
/* 备份寄存器标记值:用于判断 RTC 是否已经做过首次配置。 */
#define BKP_VALUE 0x32F0U
/* RTC 首次默认时间(未配置/备份域失效时使用,BCD 格式)。 */
#define RTC_DEFAULT_YEAR_BCD 0x26U
#define RTC_DEFAULT_MONTH_BCD 0x01U
#define RTC_DEFAULT_DAY_BCD 0x01U
#define RTC_DEFAULT_HOUR_BCD 0x12U
#define RTC_DEFAULT_MINUTE_BCD 0x00U
#define RTC_DEFAULT_SECOND_BCD 0x00U
#define RTC_DEFAULT_WEEKDAY 0x04U /* 1=周一 ... 7=周日;2026-01-01 为周四 */
/* RTC 预分频配置(LXTAL=32.768kHz 的标准配置)。 */
static __IO uint32_t s_prescaler_a = 0U;
static __IO uint32_t s_prescaler_s = 0U;
/* RTC 时钟源选择标志(来自 RCU_BDCTL[9:8])。 */
static uint32_t s_rtcsrc_flag = 0U;
/* 前置声明:RTC 初始化与校验辅助函数。 */
static void rtc_pre_config(void);
static void rtc_setup_default_if_needed(void);
static uint8_t rtc_is_leap_year_u16(uint16_t year);
static uint8_t rtc_days_in_month_u16(uint16_t year, uint8_t month);
static uint8_t rtc_calc_weekday(uint16_t year, uint8_t month, uint8_t day);
static uint8_t rtc_validate_comm_time(const Comm_Time *time);
/**
* @brief RTC 模块初始化入口。
* @details
* - 打开 PMU 与备份域写保护;
* - 选择并配置 RTC 时钟源(当前默认 LXTAL);
* - 首次上电或备份标记缺失时写入默认时间。
*/
void v_rtc_init(void)
{
rcu_periph_clock_enable(RCU_PMU);
pmu_backup_write_enable();
rtc_tamper_disable(RTC_TAMPER0);
s_rtcsrc_flag = GET_BITS(RCU_BDCTL, 8, 9);
rtc_pre_config();
rtc_setup_default_if_needed();
}
/**
* @brief 配置 RTC 时钟源与预分频。
* @details 默认使用 LXTAL32.768kHz),对应 A=127/S=255。
*/
static void rtc_pre_config(void)
{
rcu_osci_on(RCU_LXTAL);
(void)rcu_osci_stab_wait(RCU_LXTAL);
rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
s_prescaler_s = 0xFFU;
s_prescaler_a = 0x7FU;
rcu_periph_clock_enable(RCU_RTC);
(void)rtc_register_sync_wait();
}
/**
* @brief 首次配置 RTC 默认时间并写入备份标记。
*/
static void rtc_setup_default_if_needed(void)
{
rtc_parameter_struct rtc_initpara;
if ((RTC_BKP0 == BKP_VALUE) && (s_rtcsrc_flag != 0x00U)) {
return;
}
memset(&rtc_initpara, 0, sizeof(rtc_parameter_struct));
rtc_initpara.year = RTC_DEFAULT_YEAR_BCD;
rtc_initpara.day_of_week = RTC_DEFAULT_WEEKDAY;
rtc_initpara.month = RTC_DEFAULT_MONTH_BCD;
rtc_initpara.date = RTC_DEFAULT_DAY_BCD;
rtc_initpara.factor_asyn = s_prescaler_a;
rtc_initpara.factor_syn = s_prescaler_s;
rtc_initpara.display_format = RTC_24HOUR;
rtc_initpara.am_pm = RTC_AM;
rtc_initpara.hour = RTC_DEFAULT_HOUR_BCD;
rtc_initpara.minute = RTC_DEFAULT_MINUTE_BCD;
rtc_initpara.second = RTC_DEFAULT_SECOND_BCD;
if (rtc_init(&rtc_initpara) == SUCCESS) {
RTC_BKP0 = BKP_VALUE;
}
rcu_all_reset_flag_clear();
}
/**
* @brief 判断闰年(完整年份,如 2026)。
*/
static uint8_t rtc_is_leap_year_u16(uint16_t year)
{
return (uint8_t)(((year % 4U == 0U) && (year % 100U != 0U)) || (year % 400U == 0U));
}
/**
* @brief 获取指定年月的天数。
*/
static uint8_t rtc_days_in_month_u16(uint16_t year, uint8_t month)
{
static const uint8_t days_in_month[] = {31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U};
if ((month < 1U) || (month > 12U)) {
return 0U;
}
if ((month == 2U) && (rtc_is_leap_year_u16(year) != 0U)) {
return 29U;
}
return days_in_month[month - 1U];
}
/**
* @brief 计算星期(返回 1~7,周一~周日)。
*/
static uint8_t rtc_calc_weekday(uint16_t year, uint8_t month, uint8_t day)
{
static const uint8_t t[] = {0U, 3U, 2U, 5U, 0U, 3U, 5U, 1U, 4U, 6U, 2U, 4U};
uint16_t y = year;
uint8_t w;
if (month < 3U) {
y--;
}
w = (uint8_t)((y + y / 4U - y / 100U + y / 400U + t[month - 1U] + day) % 7U);
return (w == 0U) ? 7U : w;
}
/**
* @brief 校验 Comm_Time 时间合法性。
*/
static uint8_t rtc_validate_comm_time(const Comm_Time *time)
{
uint8_t max_day;
if (time == NULL) {
return 0U;
}
if ((time->iYear < 2000U) || (time->iYear > 2099U)) {
return 0U;
}
if ((time->ucMonth < 1U) || (time->ucMonth > 12U)) {
return 0U;
}
max_day = rtc_days_in_month_u16(time->iYear, time->ucMonth);
if ((time->ucDay < 1U) || (time->ucDay > max_day)) {
return 0U;
}
if ((time->ucHour > 23U) || (time->ucMin > 59U) || (time->ucSec > 59U)) {
return 0U;
}
return 1U;
}
/**
* @brief 读取 RTC 当前时间(转换到 Comm_Time)。
*/
void GetCurrentTime(Comm_Time *curTime)
{
rtc_parameter_struct rtc_initpara;
if (curTime == NULL) {
return;
}
memset(&rtc_initpara, 0, sizeof(rtc_parameter_struct));
rtc_current_time_get(&rtc_initpara);
curTime->iYear = (U16_T)(2000U + bcd_to_dec(rtc_initpara.year));
curTime->ucMonth = bcd_to_dec(rtc_initpara.month);
curTime->ucDay = bcd_to_dec(rtc_initpara.date);
curTime->ucHour = bcd_to_dec(rtc_initpara.hour);
curTime->ucMin = bcd_to_dec(rtc_initpara.minute);
curTime->ucSec = bcd_to_dec(rtc_initpara.second);
}
/**
* @brief 设置 RTC 时间(输入为 Comm_Time)。
*/
void v_rtc_set_time(Comm_Time *curTime)
{
uint8_t weekday;
rtc_parameter_struct rtc_initpara;
if (rtc_validate_comm_time(curTime) == 0U) {
return;
}
/* Ensure backup domain write access is enabled before touching RTC registers. */
rcu_periph_clock_enable(RCU_PMU);
pmu_backup_write_enable();
weekday = rtc_calc_weekday(curTime->iYear, curTime->ucMonth, curTime->ucDay);
memset(&rtc_initpara, 0, sizeof(rtc_initpara));
rtc_initpara.year = dec_to_bcd((uint8_t)(curTime->iYear - 2000U));
rtc_initpara.month = dec_to_bcd(curTime->ucMonth);
rtc_initpara.date = dec_to_bcd(curTime->ucDay);
rtc_initpara.day_of_week = weekday;
rtc_initpara.hour = dec_to_bcd(curTime->ucHour);
rtc_initpara.minute = dec_to_bcd(curTime->ucMin);
rtc_initpara.second = dec_to_bcd(curTime->ucSec);
rtc_initpara.display_format = RTC_24HOUR;
rtc_initpara.am_pm = RTC_AM;
rtc_initpara.factor_asyn = s_prescaler_a;
rtc_initpara.factor_syn = s_prescaler_s;
if (rtc_init(&rtc_initpara) != SUCCESS) {
return;
}
(void)rtc_register_sync_wait();
}
/**
* @brief 本地时间(UTC+8)转 Unix 秒(UTC)。
*/
unsigned int xDate2Seconds(Comm_Time *time)
{
static const unsigned int month_sec[12] = {
0U * xDAY, 31U * xDAY, 59U * xDAY, 90U * xDAY, 120U * xDAY, 151U * xDAY,
181U * xDAY, 212U * xDAY, 243U * xDAY, 273U * xDAY, 304U * xDAY, 334U * xDAY
};
unsigned int year;
unsigned int seconds;
if ((time == NULL) || (rtc_validate_comm_time(time) == 0U) || (time->iYear < 1970U)) {
return 0U;
}
year = (unsigned int)time->iYear - 1970U;
seconds = xYEAR * year + xDAY * ((year + 1U) / 4U);
seconds += month_sec[time->ucMonth - 1U];
if ((time->ucMonth > 2U) && (((unsigned int)time->iYear % 4U) == 0U)) {
seconds += xDAY;
}
seconds += xDAY * ((unsigned int)time->ucDay - 1U);
seconds += xHOUR * (unsigned int)time->ucHour;
seconds += xMINUTE * (unsigned int)time->ucMin;
seconds += (unsigned int)time->ucSec;
if (seconds >= (8U * xHOUR)) {
seconds -= (8U * xHOUR);
}
return seconds;
}
/**
* @brief Unix 秒(UTC)转本地时间(UTC+8)。
*/
void xSeconds2Date(unsigned long seconds, Comm_Time *time)
{
static const unsigned int month_day[12] = {31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U};
unsigned int days;
unsigned short leap_y_count;
if (time == NULL) {
return;
}
time->ucSec = (U8_T)(seconds % 60UL);
seconds /= 60UL;
time->ucMin = (U8_T)(seconds % 60UL);
seconds += 8UL * 60UL;
seconds /= 60UL;
time->ucHour = (U8_T)(seconds % 24UL);
days = (unsigned int)(seconds / 24UL);
leap_y_count = (unsigned short)((days + 365U) / 1461U);
if (((days + 366U) % 1461U) == 0U) {
time->iYear = (U16_T)(1970U + (days / 366U));
time->ucMonth = 12U;
time->ucDay = 31U;
return;
}
days -= leap_y_count;
time->iYear = (U16_T)(1970U + (days / 365U));
days %= 365U;
days += 1U;
if ((time->iYear % 4U) == 0U) {
if (days > 60U) {
--days;
} else if (days == 60U) {
time->ucMonth = 2U;
time->ucDay = 29U;
return;
}
}
for (time->ucMonth = 0U; month_day[time->ucMonth] < days; time->ucMonth++) {
days -= month_day[time->ucMonth];
}
time->ucMonth += 1U;
time->ucDay = (U8_T)days;
}
/**
* @brief 根据时区偏移调整时间(支持跨天/月/年)。
*/
void v_adjust_time_with_timezone(Comm_Time *ctTime, int hour_offset, int min_offset)
{
const int minutes_per_day = 24 * 60;
int total_minutes;
int days_adjust;
int year;
U8_T month;
int day;
U8_T max_days;
if (ctTime == NULL) {
return;
}
total_minutes = (int)ctTime->ucHour * 60 + (int)ctTime->ucMin;
total_minutes += hour_offset * 60 + min_offset;
days_adjust = total_minutes / minutes_per_day;
total_minutes %= minutes_per_day;
if (total_minutes < 0) {
total_minutes += minutes_per_day;
days_adjust--;
}
ctTime->ucHour = (U8_T)(total_minutes / 60);
ctTime->ucMin = (U8_T)(total_minutes % 60);
if (days_adjust == 0) {
return;
}
year = (int)ctTime->iYear;
month = ctTime->ucMonth;
day = (int)ctTime->ucDay + days_adjust;
while (day > (max_days = rtc_days_in_month_u16((uint16_t)year, month))) {
day -= max_days;
month++;
if (month > 12U) {
month = 1U;
year++;
}
}
while (day < 1) {
month--;
if (month < 1U) {
month = 12U;
year--;
}
day += rtc_days_in_month_u16((uint16_t)year, month);
}
ctTime->ucDay = (U8_T)day;
ctTime->ucMonth = month;
ctTime->iYear = (U16_T)year;
}
/**
* @brief 解析 OCPP 时间戳字符串到 Comm_Time。
* @param ctTime 解析结果输出。
* @param time_str 输入字符串,支持 `Z` 与 `+/-HH:MM`。
* @param time_type 0=输出 UTC1=输出北京时间(UTC+8)。
* @return 0 成功,1 失败。
*/
U8_T u8_ocpp_timestamp_to_tm(Comm_Time *ctTime, const char *time_str, U8_T time_type)
{
char time_copy[64];
char *time_part;
int hour;
int min;
int sec;
int millisec = 0;
char tz_sign = 0;
int tz_hour = 0;
int tz_min = 0;
int parsed;
if ((ctTime == NULL) || (time_str == NULL) || (strlen(time_str) >= sizeof(time_copy))) {
return 1U;
}
strcpy(time_copy, time_str);
time_part = strchr(time_copy, 'T');
if (time_part == NULL) {
return 1U;
}
*time_part = '\0';
time_part++;
if (sscanf(time_copy, "%hu-%hhu-%hhu", &ctTime->iYear, &ctTime->ucMonth, &ctTime->ucDay) != 3) {
return 1U;
}
parsed = sscanf(time_part, "%d:%d:%d.%d%c%d:%d", &hour, &min, &sec, &millisec, &tz_sign, &tz_hour, &tz_min);
if (parsed != 7) {
parsed = sscanf(time_part, "%d:%d:%d.%dZ", &hour, &min, &sec, &millisec);
if (parsed == 4) {
tz_sign = 'Z';
} else {
parsed = sscanf(time_part, "%d:%d:%d%c%d:%d", &hour, &min, &sec, &tz_sign, &tz_hour, &tz_min);
if (parsed != 6) {
parsed = sscanf(time_part, "%d:%d:%dZ", &hour, &min, &sec);
if (parsed == 3) {
tz_sign = 'Z';
} else {
return 1U;
}
}
}
}
ctTime->ucHour = (U8_T)hour;
ctTime->ucMin = (U8_T)min;
ctTime->ucSec = (U8_T)sec;
if (time_type == 1U) {
if ((tz_sign == 'Z') || (tz_sign == 0)) {
v_adjust_time_with_timezone(ctTime, 8, 0);
} else {
int off_h = (tz_sign == '+') ? tz_hour : -tz_hour;
int off_m = (tz_sign == '+') ? tz_min : -tz_min;
v_adjust_time_with_timezone(ctTime, -off_h, -off_m);
v_adjust_time_with_timezone(ctTime, 8, 0);
}
} else if ((tz_sign != 'Z') && (tz_sign != 0)) {
int off_h = (tz_sign == '+') ? tz_hour : -tz_hour;
int off_m = (tz_sign == '+') ? tz_min : -tz_min;
v_adjust_time_with_timezone(ctTime, -off_h, -off_m);
}
return (rtc_validate_comm_time(ctTime) != 0U) ? 0U : 1U;
}
/**
* @brief RTC 自测:读取当前时间并打印。
*/
void test_rtc(void)
{
Comm_Time now_time;
GetCurrentTime(&now_time);
printf("[RTC] now: %04u-%02u-%02u %02u:%02u:%02u\r\n",
now_time.iYear,
now_time.ucMonth,
now_time.ucDay,
now_time.ucHour,
now_time.ucMin,
now_time.ucSec);
}
//获取两个时间相差多少分钟,忽略年月日
int CalculateTimeDiffInMinutes(const Comm_Time time1, const Comm_Time time2) {
// 步骤1: 将时间转换为分钟数 (忽略秒)
int minutes1 = time1.ucHour * 60 + time1.ucMin;
int minutes2 = time2.ucHour * 60 + time2.ucMin;
// 步骤2: 计算绝对差值
int diff = abs(minutes1 - minutes2);
// 步骤3: 处理跨天情况 (一天共1440分钟)
if (diff > 720) { // 超过720分钟则跨天更短
diff = 1440 - diff;
}
return diff;
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef APP_RTC_H
#define APP_RTC_H
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
extern "C" {
#endif
#include "main.h"
#include "publicdata/type.h"
/* 对外接口函数声明 */
void v_rtc_init(void);
/* app_rtc 兼容接口(对业务层使用 Comm_Time) */
void GetCurrentTime(Comm_Time *curTime);
void v_rtc_set_time(Comm_Time *curTime);
unsigned int xDate2Seconds(Comm_Time *time);
void xSeconds2Date(unsigned long seconds, Comm_Time *time);
U8_T u8_ocpp_timestamp_to_tm(Comm_Time *ctTime, const char *time_str, U8_T time_type);
void v_adjust_time_with_timezone(Comm_Time *ctTime, int hour_offset, int min_offset);
int CalculateTimeDiffInMinutes(const Comm_Time time1, const Comm_Time time2);
void test_rtc(void);
#ifdef __cplusplus
}
#endif
#endif /* APP_RTC_H */
+247
View File
@@ -0,0 +1,247 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "publicdata/type.h"
typedef unsigned char U8_T;
typedef unsigned short U16_T;
// 闰年检查
static U8_T u8_is_leap_year(U16_T year)
{
return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));
}
// 获取月份天数
static U8_T u8_get_days_in_month(U16_T year, U8_T month)
{
static const U8_T days_in_month[] = {31,28,31,30,31,30,31,31,30,31,30,31};
if (month < 1 || month > 12) return 0;
U8_T days = days_in_month[month-1];
// 闰年二月特殊处理
if (month == 2 && u8_is_leap_year(year)) {
days = 29;
}
return days;
}
// 日期时间验证
static U8_T u8_validate_datetime(const Comm_Time *ctTime)
{
// 年份范围检查
if (ctTime->iYear < 2020 || ctTime->iYear > 2100) {
return 0;
}
// 月份检查
if (ctTime->ucMonth < 1 || ctTime->ucMonth > 12) {
return 0;
}
// 日期检查
U8_T max_days = u8_get_days_in_month(ctTime->iYear, ctTime->ucMonth);
if (ctTime->ucDay < 1 || ctTime->ucDay > max_days) {
return 0;
}
// 时间检查
if (ctTime->ucHour > 23 || ctTime->ucMin > 59 || ctTime->ucSec > 59) {
return 0;
}
return 1;
}
#if 0 /* CCU621_M: duplicate of app_rtc.c — disabled to avoid L6200E */
/*******************************************************************************
* 名称: v_adjust_time_with_timezone
* 功能: 按给定时区偏移量调整 Comm_Time 中的时刻与日期,用于 OCPP 时间戳与本地/UTC 转换。
* 先将“时+分”与偏移量合并为总分钟数,归一化到 [0, 24*60) 并得到日进退数,
* 再按日进退数正确跨月、跨年调整日期(含闰年与各月天数)。
*
* 参数:
* ctTime - 输入输出,待调整的日期时间结构(会直接修改 ucHour/ucMin/ucDay/ucMonth/iYear
* hour_offset - 时区偏移小时数,可为负(如 UTC+8 传入 8,UTC-5 传入 -5
* min_offset - 时区偏移分钟数,可为负(如 +08:30 则 hour_offset=8, min_offset=30
*
* 说明:
* - 仅修改时、分、日、月、年;秒(ucSec)不参与计算也不改动。
* - 日期跨月、跨年时按公历规则处理(含闰年二月 28/29 天)。
* - 若调整后年份超出合理范围,由调用方 u8_validate_datetime() 等做校验或裁剪。
******************************************************************************/
void v_adjust_time_with_timezone(Comm_Time *ctTime, int hour_offset, int min_offset)
{
const int minutes_per_day = 24 * 60;
int total_minutes;
int days_adjust;
int year;
U8_T month;
int day;
U8_T max_days;
if (ctTime == NULL) {
return;
}
/* 1) 将“当前时+分”与偏移量合并为总分钟数 */
total_minutes = ctTime->ucHour * 60 + ctTime->ucMin;
total_minutes += hour_offset * 60 + min_offset;
/* 2) 归一化到 [0, minutes_per_day),并得到需要进退的天数 */
days_adjust = total_minutes / minutes_per_day;
total_minutes = total_minutes % minutes_per_day;
if (total_minutes < 0) {
total_minutes += minutes_per_day;
days_adjust--;
}
/* 3) 写回时、分 */
ctTime->ucHour = (U8_T)(total_minutes / 60);
ctTime->ucMin = (U8_T)(total_minutes % 60);
/* 4) 按日进退数调整日期,并正确处理跨月、跨年 */
if (days_adjust == 0) {
return;
}
year = (int)ctTime->iYear;
month = ctTime->ucMonth;
day = (int)ctTime->ucDay + days_adjust;
/* 向后推日期:day 超出当月则进入下月/下年 */
while (day > (max_days = u8_get_days_in_month((U16_T)year, month))) {
day -= max_days;
month++;
if (month > 12) {
month = 1;
year++;
}
}
/* 向前推日期:day 小于 1 则进入上月/上年 */
while (day < 1) {
month--;
if (month < 1) {
month = 12;
year--;
}
day += u8_get_days_in_month((U16_T)year, month);
}
ctTime->ucDay = (U8_T)day;
ctTime->ucMonth = month;
ctTime->iYear = (U16_T)year;
}
// 主函数:OCPP时间戳解析
// time_type: 0-存入国际标准时间(UTC), 1-存入北京时间(UTC+8)
// 返回:0-成功,非0-失败
U8_T u8_ocpp_timestamp_to_tm(Comm_Time *ctTime, const char *time_str, U8_T time_type)
{
if (ctTime == NULL || time_str == NULL) {
return 1; // 参数错误
}
// 复制输入字符串,避免修改原始数据
char time_copy[64];
if (strlen(time_str) >= sizeof(time_copy)) {
return 1; // 输入过长
}
strcpy(time_copy, time_str);
// 分割日期时间部分和时区部分
char *time_part = strchr(time_copy, 'T');
if (time_part == NULL) {
return 1; // 格式错误,缺少'T'
}
*time_part = '\0';
time_part++;
// 解析日期部分 (YYYY-MM-DD)
if (sscanf(time_copy, "%hu-%hhu-%hhu",
&ctTime->iYear, &ctTime->ucMonth, &ctTime->ucDay) != 3) {
return 1; // 日期解析失败
}
int hour, min, sec, millisec = 0;
char tz_sign = 0;
int tz_hour = 0, tz_min = 0;
// 尝试解析带毫秒的格式
int parsed = sscanf(time_part, "%d:%d:%d.%d%c%d:%d",
&hour, &min, &sec, &millisec, &tz_sign, &tz_hour, &tz_min);
if (parsed == 7) {
// Case 1: 带毫秒和时区偏移(如 "10:00:28.990+08:00"
} else {
// 尝试解析带毫秒的UTC格式(Z结尾)
parsed = sscanf(time_part, "%d:%d:%d.%dZ", &hour, &min, &sec, &millisec);
if (parsed == 4) {
// Case 2: 带毫秒的UTC时间(如 "10:00:28.990Z"
tz_sign = 'Z';
} else {
// 尝试无毫秒的格式
parsed = sscanf(time_part, "%d:%d:%d%c%d:%d",
&hour, &min, &sec, &tz_sign, &tz_hour, &tz_min);
if (parsed == 6) {
// Case 3: 无毫秒带时区偏移(如 "10:14:14+08:00"
} else {
parsed = sscanf(time_part, "%d:%d:%dZ", &hour, &min, &sec);
if (parsed == 3) {
// Case 4: 无毫秒UTC时间(如 "15:09:18Z"
tz_sign = 'Z';
} else {
return 1; // 时间格式解析失败
}
}
}
}
// 填充时间部分
ctTime->ucHour = hour;
ctTime->ucMin = min;
ctTime->ucSec = sec;
// 时区处理逻辑
if (time_type == 1) {
// 需要转换为北京时间 (UTC+8)
if (tz_sign == 'Z' || tz_sign == 0) {
// UTC时间直接加8小时
v_adjust_time_with_timezone(ctTime, 8, 0);
} else {
// 处理带时区偏移的情况
// 先将输入时间转换为UTC,再转换为北京时间
int original_offset_hour = (tz_sign == '+') ? tz_hour : -tz_hour;
int original_offset_min = (tz_sign == '+') ? tz_min : -tz_min;
// 转换为UTC时间(减去原始偏移)
v_adjust_time_with_timezone(ctTime, -original_offset_hour, -original_offset_min);
// 再转换为北京时间(加上8小时)
v_adjust_time_with_timezone(ctTime, 8, 0);
}
} else {
// time_type == 0,存入国际标准时间(UTC)
if (tz_sign != 'Z' && tz_sign != 0) {
// 非UTC时间需要转换为UTC
int original_offset_hour = (tz_sign == '+') ? tz_hour : -tz_hour;
int original_offset_min = (tz_sign == '+') ? tz_min : -tz_min;
// 转换为UTC时间(减去原始偏移)
v_adjust_time_with_timezone(ctTime, -original_offset_hour, -original_offset_min);
}
// 如果已经是UTC时间(Z结尾),则不需要转换
}
// 最终数据验证
if (!u8_validate_datetime(ctTime)) {
return 1; // 数据验证失败
}
return 0; // 成功
}
#endif /* CCU621_M: duplicate disabled */
+93
View File
@@ -0,0 +1,93 @@
# RTC 模块说明
## 1. 模块位置
- `BSP/rtc/rtc.c`
- `BSP/rtc/rtc.h`
## 2. 模块职责
- 完成 MCU 内部 RTC 外设初始化(默认 `LXTAL 32.768kHz`
- 提供统一时间读写接口(业务层使用 `Comm_Time`
- 提供时间算法工具:
- 本地时间(UTC+8)与 Unix 时间戳互转
- OCPP 时间戳解析
- 按时区偏移调整时间(支持跨天/月/年)
## 3. 硬件与掉电行为说明
### 3.1 `RTC_BKP0` 是什么
- `RTC_BKP0` 是 RTC 备份寄存器中的一个槽位(软件可读写)
- 本项目中它作为“RTC 是否已初始化”的标记位使用
- 典型逻辑:
- `RTC_BKP0 == BKP_VALUE`:认为已配置过,不覆盖当前时间
- `RTC_BKP0 != BKP_VALUE`:按默认时间初始化并写入标记
### 3.2 掉电后时间会不会继续走
- `RTC_BKP0` 只是存储标记,不会自动计时
- 时间能否在掉电期间继续增加,取决于 RTC 域是否持续供电(VBAT)以及 RTC 时钟是否持续工作
- 若掉电导致备份域失电,RTC 计时会停止;下次上电会按“首次初始化”逻辑处理
## 4. 默认时间策略
- 当前默认时间由 `rtc.c` 宏定义控制:
- `RTC_DEFAULT_YEAR_BCD`
- `RTC_DEFAULT_MONTH_BCD`
- `RTC_DEFAULT_DAY_BCD`
- `RTC_DEFAULT_HOUR_BCD`
- `RTC_DEFAULT_MINUTE_BCD`
- `RTC_DEFAULT_SECOND_BCD`
- `RTC_DEFAULT_WEEKDAY`
- 默认值仅在“未初始化/备份标记无效”时生效,不会每次上电都覆盖已有时间
## 5. 对外接口
- `void v_rtc_init(void);`
- RTC 初始化入口(由 `BSP/sys_drv_init.c` 调用)
- `void GetCurrentTime(Comm_Time *curTime);`
- 读取当前时间到 `Comm_Time`
- `void v_rtc_set_time(Comm_Time *curTime);`
- 写 RTC 时间(内含时间合法性检查)
- `unsigned int xDate2Seconds(Comm_Time *time);`
- 本地时间(UTC+8)转 Unix 秒(UTC
- `void xSeconds2Date(unsigned long seconds, Comm_Time *time);`
- Unix 秒(UTC)转本地时间(UTC+8
- `U8_T u8_ocpp_timestamp_to_tm(Comm_Time *ctTime, const char *time_str, U8_T time_type);`
- 解析 OCPP 时间戳(支持 `Z``+08:00``-05:30` 等)
- `time_type=0`:输出 UTC
- `time_type=1`:输出北京时间(UTC+8
- `void v_adjust_time_with_timezone(Comm_Time *ctTime, int hour_offset, int min_offset);`
-`Comm_Time` 按时区偏移修正
- `void test_rtc(void);`
- RTC 自测接口(打印当前时间)
## 6. 启动与调用链
1. `main.c` -> `v_sys_hardware_init()`
2. `BSP/sys_drv_init.c` -> `v_rtc_init()`
3. 业务层通过 `GetCurrentTime/v_rtc_set_time` 访问时间
4. `app/app_init/app_test.c` 在周期测试中可触发 RTC 读写测试
## 7. 常见问题
- **Q: 修改默认时间后,为什么上电看到的不是默认值?**
A: 因为备份标记仍有效,模块判断为“已初始化”,不会覆盖当前时间。
- **Q: 想强制重新走默认时间怎么办?**
A: 清空备份标记(例如清 `RTC_BKP0`)或清备份域后重启。
## 8. 相关文档
- [返回主说明 README](../../README.md)
- [externalflash 模块说明](../externalflash/externalflash模块说明.md)
+29
View File
@@ -0,0 +1,29 @@
#ifndef BSP_INCLUDE_H
#define BSP_INCLUDE_H
#if SYSTEM_TYPE == SYSTEM_TYPE_CCU601E_D
#include "iic_eeprom/fm24cl16b.h" /* [CCU601E_D] */
#include "spi_Flash/flash_external_data.h"
#include "spi_Flash/flash_upgrade.h"
#include "net_lwip/ethernetif.h" /* is_eth_link_up */
#include "fdcan/app_fdcan.h"
#include "can_spi/CANSPI.h"
#define BMS_CAN_ID_1 CAN_1
#define BMS_CAN_ID_2 CAN_2
#define MDU_CAN_ID CAN_3
#define CAN_RECV_BUF_SIZE CAN_BUFFER_CNT
#elif SYSTEM_TYPE == SYSTEM_TYPE_CCU621_M
#include "eeprom/fm24cl16.h" /* [CCU621_M] */
#include "externalflash/flash_external_data.h"
#include "externalflash/flash_upgrade.h"
#include "net_lwip/eth_link.h" /* netif_is_link_up */
#include "can.h"
#define BMS_CAN_ID_1 E_CAN_1
#define BMS_CAN_ID_2 E_CAN_0
#define MDU_CAN_ID E_CAN_2
#define CAN_RECV_BUF_SIZE CAN_RX_BUFFER_SIZE
#endif
#endif
+521
View File
@@ -0,0 +1,521 @@
/*!
\file can.c
\brief CAN统一接口实现(整合 CAN0/CAN1/CAN2
*/
#include "gd32h7xx.h"
#include "can.h"
#include <string.h>
/* RX strategy: TX on MB0, RX on MB1 (validated on all 3 CAN ports). */
/* 支持的 CAN 端口数量(CAN0/CAN1/CAN2 */
#define CAN_PORT_COUNT 3U
/* 每个端口的软件接收环形缓存深度(单位:帧) */
/* 单帧最大数据长度(Classic CAN,8 字节) */
#define CAN_DATA_MAX_LEN 8U
/**
* @brief 单帧接收缓存条目
*/
typedef struct {
uint32_t id; /* 帧 ID(标准/扩展均可) */
uint8_t data[CAN_DATA_MAX_LEN]; /* 帧数据 */
uint8_t dlc; /* 数据长度 */
uint8_t ide; /* 帧类型:0 标准帧,1 扩展帧 */
uint8_t rtr; /* 帧类型:0 数据帧,1 远程帧 */
uint8_t used; /* 条目是否有效:0 无效,1 有效 */
} can_rx_item_t;
/**
* @brief 单端口运行态(软件接收环形缓存)
*/
typedef struct {
can_rx_item_t rx_buffer[CAN_RX_BUFFER_SIZE]; /* 缓存数组 */
volatile uint8_t head; /* 写指针(中断侧推进) */
volatile uint8_t tail; /* 读指针(任务侧推进) */
} can_port_state_t;
/**
* @brief 端口硬件静态配置表项
*/
typedef struct {
uint32_t canx; /* CAN 外设基址(CAN0/CAN1/CAN2 */
can_idx_enum idx; /* 时钟源索引(IDX_CANx */
rcu_periph_enum can_clock; /* CAN 外设时钟 */
rcu_periph_enum gpio_clock; /* GPIO 端口时钟 */
uint32_t gpio_port; /* GPIO 端口 */
uint32_t gpio_af; /* 复用功能编号 */
uint32_t tx_pin; /* TX 引脚 */
uint32_t rx_pin; /* RX 引脚 */
IRQn_Type irqn; /* 对应中断号 */
} can_port_cfg_t;
/* 各端口运行态(接收缓存和读写索引) */
static can_port_state_t g_can_state[CAN_PORT_COUNT];
static uint8_t g_can_initialized = 0U;
/* 各端口硬件配置映射表 */
static const can_port_cfg_t g_can_cfg[CAN_PORT_COUNT] = {
{ CAN0, IDX_CAN0, RCU_CAN0, RCU_GPIOB, GPIOB, GPIO_AF_9, GPIO_PIN_9, GPIO_PIN_8, CAN0_Message_IRQn },
{ CAN1, IDX_CAN1, RCU_CAN1, RCU_GPIOB, GPIOB, GPIO_AF_9, GPIO_PIN_6, GPIO_PIN_5, CAN1_Message_IRQn },
{ CAN2, IDX_CAN2, RCU_CAN2, RCU_GPIOF, GPIOF, GPIO_AF_2, GPIO_PIN_7, GPIO_PIN_6, CAN2_Message_IRQn }
};
/**
* @brief 获取指定端口的配置项
* @param port 端口号(0/1/2
* @return 配置项指针,非法端口返回 NULL
*/
static const can_port_cfg_t *can_get_cfg(uint8_t port)
{
if (port >= CAN_PORT_COUNT) {
return NULL;
}
return &g_can_cfg[port];
}
/**
* @brief 初始化指定端口的软件接收缓存
* @param port 端口号(0/1/2
*/
static void can_buffer_init(uint8_t port)
{
memset(&g_can_state[port], 0, sizeof(g_can_state[port]));
}
/**
* @brief 配置指定端口 CAN GPIO 和时钟
* @param port 端口号(0/1/2
*/
static void can_gpio_config(uint8_t port)
{
const can_port_cfg_t *cfg = can_get_cfg(port);
if (cfg == NULL) {
return;
}
rcu_can_clock_config(cfg->idx, RCU_CANSRC_APB2);
rcu_periph_clock_enable(cfg->can_clock);
rcu_periph_clock_enable(cfg->gpio_clock);
gpio_af_set(cfg->gpio_port, cfg->gpio_af, cfg->rx_pin);
gpio_mode_set(cfg->gpio_port, GPIO_MODE_AF, GPIO_PUPD_NONE, cfg->rx_pin);
gpio_output_options_set(cfg->gpio_port, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, cfg->rx_pin);
gpio_af_set(cfg->gpio_port, cfg->gpio_af, cfg->tx_pin);
gpio_mode_set(cfg->gpio_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, cfg->tx_pin);
gpio_output_options_set(cfg->gpio_port, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, cfg->tx_pin);
}
/**
* @brief 配置指定端口 CAN 协议参数和中断
* @param port 端口号(0/1/2
* @note CAN0/CAN1: 250kbps, CAN2: 125kbps,采样点约 87.5%
*/
static void can_parameter_config(uint8_t port)
{
const can_port_cfg_t *cfg = can_get_cfg(port);
can_parameter_struct can_parameter;
uint32_t prescaler;
if (cfg == NULL) {
return;
}
/* 根据端口选择预分频器:CAN2使用125kbps,其他使用250kbps */
if (port == 2) { /* CAN2 */
prescaler = 150U; /* 125 kbps */
} else {
prescaler = 75U; /* 250 kbps */
}
can_deinit(cfg->canx);
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.internal_counter_source = CAN_TIMER_SOURCE_BIT_CLOCK;
can_parameter.self_reception = DISABLE;
can_parameter.mb_tx_order = CAN_TX_HIGH_PRIORITY_MB_FIRST;
can_parameter.mb_tx_abort_enable = ENABLE;
can_parameter.local_priority_enable = DISABLE;
can_parameter.mb_rx_ide_rtr_type = CAN_IDE_RTR_FILTERED;
can_parameter.mb_remote_frame = CAN_STORE_REMOTE_REQUEST_FRAME;
can_parameter.rx_private_filter_queue_enable = DISABLE;
can_parameter.edge_filter_enable = DISABLE;
can_parameter.protocol_exception_enable = DISABLE;
can_parameter.rx_filter_order = CAN_RX_FILTER_ORDER_MAILBOX_FIRST;
can_parameter.memory_size = CAN_MEMSIZE_32_UNIT;
/* Follow official examples mailbox filter setting. */
can_parameter.mb_public_filter = 0U;
can_parameter.resync_jump_width = 1U;
can_parameter.prop_time_segment = 2U;
can_parameter.time_segment_1 = 11U;
can_parameter.time_segment_2 = 2U;
can_parameter.prescaler = prescaler;
can_init(cfg->canx, &can_parameter);
// nvic_irq_enable(cfg->irqn, 0U, 0U); /* 已迁移到 sys_drv_init.c:nvic_configuration() */
/* Mailbox path: TX on MB0, RX on MB1. */
can_interrupt_enable(cfg->canx, CAN_INT_MB0); /* TX complete/abort events */
can_interrupt_enable(cfg->canx, CAN_INT_MB1);
/* Enable error-related interrupts to avoid "silent dead" and help recovery. */
can_interrupt_enable(cfg->canx, CAN_INT_ERR_SUMMARY);
can_interrupt_enable(cfg->canx, CAN_INT_BUSOFF);
can_interrupt_enable(cfg->canx, CAN_INT_BUSOFF_RECOVERY);
can_interrupt_enable(cfg->canx, CAN_INT_RX_WARNING);
can_interrupt_enable(cfg->canx, CAN_INT_TX_WARNING);
/* Real bus communication mode. */
can_operation_mode_enter(cfg->canx, CAN_NORMAL_MODE);
}
/**
* @brief 配置指定端口接收过滤器(当前放行标准帧和扩展帧)
* @param port 端口号(0/1/2
*/
static void can_filter_config(uint8_t port)
{
if (can_get_cfg(port) == NULL) {
return;
}
/* Mailbox RX uses mb_public_filter configured in can_parameter_config(). */
}
/**
* @brief 指定端口发送一帧 CAN 数据
* @param port 端口号(0/1/2
* @param id 帧 ID
* @param data 数据指针
* @param dlc 数据长度(1~8
* @param ide 帧类型:0 标准帧,1 扩展帧
* @param rtr 帧类型:0 数据帧,1 远程帧
* @return 0 成功,1 参数错误
*/
static uint8_t can_port_send_data(uint8_t port, uint32_t id, const uint8_t *data, uint8_t dlc, uint8_t ide, uint8_t rtr)
{
can_mailbox_descriptor_struct tx_mailbox; /* 发送邮箱描述符 */
uint32_t tx_buffer[CAN_DATA_MAX_LEN] = {0}; /* 局部发送缓冲(驱动要求 32bit 对齐) */
const can_port_cfg_t *cfg = can_get_cfg(port); /* 端口配置 */
if ((cfg == NULL) || (data == NULL) || (dlc == 0U) || (dlc > CAN_DATA_MAX_LEN)) {
return 1U;
}
can_struct_para_init(CAN_MDSC_STRUCT, &tx_mailbox);
tx_mailbox.id = id;
tx_mailbox.dlc = dlc;
tx_mailbox.ide = ide;
tx_mailbox.rtr = rtr;
tx_mailbox.srr = (ide == 1U) ? 1U : 0U;
tx_mailbox.code = CAN_MB_TX_STATUS_DATA;
tx_mailbox.data_bytes = dlc;
memcpy(tx_buffer, data, dlc);
tx_mailbox.data = tx_buffer;
can_mailbox_config(cfg->canx, 0U, &tx_mailbox);
return 0U;
}
/**
* @brief 从指定端口的软件接收缓存读取一帧数据
* @param port 端口号(0/1/2
* @param id 输出:帧 ID
* @param data 输出:帧数据
* @param dlc 输出:数据长度
* @param ide 输出:帧类型(标准/扩展)
* @param rtr 输出:帧类型(数据/远程)
* @return 0 成功,1 无数据,2 参数错误
*/
static uint8_t can_port_receive_data(uint8_t port, uint32_t *id, uint8_t *data, uint8_t *dlc, uint8_t *ide, uint8_t *rtr)
{
can_port_state_t *state; /* 指向端口运行态 */
if ((port >= CAN_PORT_COUNT) || (id == NULL) || (data == NULL) || (dlc == NULL) || (ide == NULL) || (rtr == NULL)) {
return 2U;
}
state = &g_can_state[port];
if (state->rx_buffer[state->tail].used == 0U) {
return 1U;
}
*id = state->rx_buffer[state->tail].id;
*dlc = state->rx_buffer[state->tail].dlc;
*ide = state->rx_buffer[state->tail].ide;
*rtr = state->rx_buffer[state->tail].rtr;
memcpy(data, state->rx_buffer[state->tail].data, *dlc);
state->rx_buffer[state->tail].used = 0U;
state->tail = (uint8_t)((state->tail + 1U) % CAN_RX_BUFFER_SIZE);
return 0U;
}
/**
* @brief 统一中断处理逻辑(按端口复用)
* @param port 端口号(0/1/2
* @note 在中断上下文执行:从硬件邮箱读帧并入软件环形缓存
*/
static void can_irq_handler_common(uint8_t port)
{
can_port_state_t *state; /* 端口运行态 */
const can_port_cfg_t *cfg = can_get_cfg(port); /* 端口配置 */
uint8_t next_head; /* 计算后的下一写指针 */
if (cfg == NULL) {
return;
}
{
can_mailbox_descriptor_struct rx_mb;
uint32_t rx_data_u32[2] = {0U, 0U}; /* 8 bytes aligned buffer */
if (RESET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_MB1)) {
return;
}
can_struct_para_init(CAN_MDSC_STRUCT, &rx_mb);
rx_mb.data = rx_data_u32;
rx_mb.data_bytes = 8U;
if (SUCCESS != can_mailbox_receive_data_read(cfg->canx, 1U, &rx_mb)) {
can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_MB1);
return;
}
/* Only accept: extended + data frame */
if ((rx_mb.ide == 0U) || (rx_mb.rtr != 0U)) {
can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_MB1);
return;
}
state = &g_can_state[port];
next_head = (uint8_t)((state->head + 1U) % CAN_RX_BUFFER_SIZE);
if ((next_head != state->tail) && (state->rx_buffer[state->head].used == 0U)) {
uint8_t dlc = (uint8_t)rx_mb.dlc;
if (dlc > CAN_DATA_MAX_LEN) {
dlc = CAN_DATA_MAX_LEN;
}
state->rx_buffer[state->head].id = rx_mb.id;
state->rx_buffer[state->head].dlc = dlc;
state->rx_buffer[state->head].ide = (uint8_t)rx_mb.ide;
state->rx_buffer[state->head].rtr = (uint8_t)rx_mb.rtr;
memcpy(state->rx_buffer[state->head].data, (uint8_t *)rx_data_u32, dlc);
state->rx_buffer[state->head].used = 1U;
state->head = next_head;
}
can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_MB1);
}
}
/**
* @brief CAN 中断统一入口(消息/错误/BusOff/告警等均可调用)
* @param can_port 端口号(0/1/2
*
* 说明:
* - GD32H7 的 CAN 中断线有 Message/Busoff/Error/TEC/REC 多条,
* 不同中断源触发哪一条与库/配置有关。这里统一收敛到一个入口,保证不中断“漏接”。
*/
void v_can_irq_handler(uint8_t can_port)
{
const can_port_cfg_t *cfg = can_get_cfg(can_port);
if (cfg == NULL) {
return;
}
/* Message path dispatch (MB1 RX mailbox). */
if (SET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_MB1)) {
can_irq_handler_common(can_port);
}
/* MB0 indicates TX mailbox event, clear it to avoid stale status. */
if (SET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_MB0)) {
can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_MB0);
}
/* 清理可能导致反复进入的错误/BusOff 标志(不依赖业务侧处理) */
if (SET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_BUSOFF)) {
can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_BUSOFF);
}
if (SET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_BUSOFF_RECOVERY)) {
can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_BUSOFF_RECOVERY);
}
if (SET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_ERR_SUMMARY)) {
can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_ERR_SUMMARY);
}
if (SET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_RX_WARNING)) {
can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_RX_WARNING);
}
if (SET == can_interrupt_flag_get(cfg->canx, CAN_INT_FLAG_TX_WARNING)) {
can_interrupt_flag_clear(cfg->canx, CAN_INT_FLAG_TX_WARNING);
}
}
/**
* @brief 初始化全部 CAN 端口(GPIO/参数/过滤器/缓存)
*/
void v_can_interface_init(void)
{
uint8_t port; /* 端口遍历索引 */
for (port = 0U; port < CAN_PORT_COUNT; port++) {
can_mailbox_descriptor_struct rx_mb;
uint32_t dummy_data[2] = {0U, 0U};
can_gpio_config(port);
can_parameter_config(port);
can_filter_config(port);
can_buffer_init(port);
/* Configure MB1 as RX mailbox. */
can_struct_para_init(CAN_MDSC_STRUCT, &rx_mb);
rx_mb.rtr = 0U;
rx_mb.ide = 1U; /* expect extended */
rx_mb.code = CAN_MB_RX_STATUS_EMPTY;
/* No ID filtering in mailbox setup; extension/data filtering is done in software path. */
rx_mb.id = 0U;
rx_mb.data = dummy_data;
rx_mb.data_bytes = 8U;
can_mailbox_config(g_can_cfg[port].canx, 1U, &rx_mb);
}
g_can_initialized = 1U;
}
uint8_t u8_can_is_initialized(void)
{
return g_can_initialized;
}
/**
* @brief 统一 CAN 发送接口(兼容旧接口)
* @param can_port 端口号(0/1/2
* @param id 帧 ID
* @param data 数据指针
* @param dlc 数据长度(1~8
* @param ide 0 标准帧,1 扩展帧
* @param rtr 0 数据帧,1 远程帧
* @return 0 成功,1 参数错误
*/
uint8_t can_send_data(uint8_t can_port, uint32_t id, uint8_t *data, uint8_t dlc, uint8_t ide, uint8_t rtr)
{
return can_port_send_data(can_port, id, data, dlc, ide, rtr);
}
/**
* @brief 统一 CAN 接收接口(兼容旧接口)
* @param can_port 端口号(0/1/2
* @param id 输出:帧 ID
* @param data 输出:帧数据
* @param dlc 输出:数据长度
* @param ide 输出:帧类型(标准/扩展)
* @param rtr 输出:帧类型(数据/远程)
* @return 0 成功,1 无数据,2 参数错误
*/
uint8_t can_receive_data(uint8_t can_port, uint32_t *id, uint8_t *data, uint8_t *dlc, uint8_t *ide, uint8_t *rtr)
{
return can_port_receive_data(can_port, id, data, dlc, ide, rtr);
}
/**
* @brief 对外统一发送接口(与参考工程接口风格一致)
* @param can_id 端口枚举
* @param can_data 发送数据结构
*/
void v_can_tx(E_CAN_ID can_id, const CAN_DATA *can_data)
{
if ((can_data == NULL) || (can_data->Len == 0U) || (can_data->Len > CAN_DATA_MAX_LEN)) {
return;
}
(void)can_send_data((uint8_t)can_id,
can_data->ID,
(uint8_t *)can_data->Data,
can_data->Len,
1U,
0U);
}
/**
* @brief 对外统一接收接口(仿照参考工程:一次取出当前软件缓存内全部CAN报文)
* @param can_id 端口枚举
* @param can_data 输出:接收数据结构体数组
* @return 返回本次拷出的报文数量;0 表示无数据或参数错误
*/
uint8_t u8_can_rx(E_CAN_ID can_id, CAN_DATA *can_data)
{
can_port_state_t *state;
const can_port_cfg_t *cfg;
uint8_t count = 0U;
if ((can_id >= E_CAN_0 + CAN_PORT_COUNT) || (can_data == NULL)) {
return 0U;
}
cfg = can_get_cfg((uint8_t)can_id);
if (cfg == NULL) {
return 0U;
}
state = &g_can_state[(uint8_t)can_id];
NVIC_DisableIRQ(cfg->irqn);
while ((count < CAN_RX_BUFFER_SIZE) && (state->rx_buffer[state->tail].used != 0U)) {
can_data[count].ID = state->rx_buffer[state->tail].id;
can_data[count].Len = state->rx_buffer[state->tail].dlc;
memcpy(can_data[count].Data, state->rx_buffer[state->tail].data, can_data[count].Len);
state->rx_buffer[state->tail].used = 0U;
state->tail = (uint8_t)((state->tail + 1U) % CAN_RX_BUFFER_SIZE);
count++;
}
if (state->head == state->tail) {
state->head = 0U;
state->tail = 0U;
}
NVIC_EnableIRQ(cfg->irqn);
return count;
}
/**
* @brief 打包扩展帧 ID(协议字段组装)
* @param prior 优先级字段
* @param cmd 命令字段
* @param desAddr 目的地址字段
* @param oriAddr 源地址字段
* @return 29 位扩展帧 ID
*/
uint32_t u32_get_can_ID(uint8_t prior, uint8_t cmd, uint8_t desAddr, uint8_t oriAddr)
{
return ((uint32_t)prior << 26)
| ((uint32_t)cmd << 16)
| ((uint32_t)desAddr << 8)
| (uint32_t)oriAddr;
}
/**
* @brief 解析扩展帧 ID(协议字段拆解)
* @param u32_id 待解析 ID
* @param prior 输出:优先级(可为 NULL)
* @param cmd 输出:命令(可为 NULL
* @param desAddr 输出:目的地址(可为 NULL)
* @param oriAddr 输出:源地址(可为 NULL)
* @return 1 成功,0 参数 ID 非法
*/
uint8_t u8_can_ID_unpack(uint32_t u32_id, uint8_t *prior, uint8_t *cmd, uint8_t *desAddr, uint8_t *oriAddr)
{
if ((u32_id == 0U) || (u32_id > 0x1FFFFFFFU)) {
return 0U;
}
if (prior != NULL) {
*prior = (uint8_t)((u32_id >> 26) & 0xFFU);
}
if (cmd != NULL) {
*cmd = (uint8_t)((u32_id >> 16) & 0xFFU);
}
if (desAddr != NULL) {
*desAddr = (uint8_t)((u32_id >> 8) & 0xFFU);
}
if (oriAddr != NULL) {
*oriAddr = (uint8_t)(u32_id & 0xFFU);
}
return 1U;
}
+55
View File
@@ -0,0 +1,55 @@
/*!
\file can.h
\brief CAN配置头文件
\version 2026-03-09, V1.4.0, firmware for GD32H7xx
*/
#ifndef CAN_H
#define CAN_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define CAN_RX_BUFFER_SIZE 32U
typedef enum {
E_CAN_0 = 0,
E_CAN_1 = 1,
E_CAN_2 = 2
} E_CAN_ID;
typedef struct {
uint32_t ID;
uint8_t Len;
uint8_t Data[8U];
} CAN_DATA;
/* CAN统一初始化接口 */
void v_can_interface_init(void);
uint8_t u8_can_is_initialized(void);
/* CAN 中断分发(供 gd32h7xx_it.c 复用) */
void v_can_irq_handler(uint8_t can_port);
/* 对外统一发送/接收接口(扩展帧、数据帧) */
void v_can_tx(E_CAN_ID can_id, const CAN_DATA *can_data);
uint8_t u8_can_rx(E_CAN_ID can_id, CAN_DATA *can_data);
/* 兼容当前工程的原统一接口 */
uint8_t can_send_data(uint8_t can_port, uint32_t id, uint8_t *data, uint8_t dlc, uint8_t ide, uint8_t rtr);
uint8_t can_receive_data(uint8_t can_port, uint32_t *id, uint8_t *data, uint8_t *dlc, uint8_t *ide, uint8_t *rtr);
/* 扩展帧ID打包/解析工具 */
uint32_t u32_get_can_ID(uint8_t prior, uint8_t cmd, uint8_t desAddr, uint8_t oriAddr);
uint8_t u8_can_ID_unpack(uint32_t u32_id, uint8_t *prior, uint8_t *cmd, uint8_t *desAddr, uint8_t *oriAddr);
#ifdef __cplusplus
}
#endif
#endif /* CAN_H */
+715
View File
@@ -0,0 +1,715 @@
/*!
\file usart.c
\brief 串口(USART/UART)统一驱动入口(集中式)
目标:把 `BSP\com\` 下分散的串口实现整理为“单入口/统一API”,形态上尽量贴近
`CCU601E_RUN\BSP\ST_HAL\usart.c`(集中管理各串口初始化、发送、接收缓存、中断)。
*/
#include "usart.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "gd32h7xx.h"
#include "publicdata/public_define.h"
#include "ringfifo/ringfifo.h"
#ifndef DEBUG_SHELL_USART_NULL
#define DEBUG_SHELL_USART_NULL (0xFFU)
#endif
#ifndef DEBUG_SHELL_USART_ID
#define DEBUG_SHELL_USART_ID DEBUG_SHELL_USART_NULL
#endif
/* ===== 通用:ringfifo 接收缓存(每路一套)===== */
#define UART0_RX_BUFFER_SIZE 256U
#define UART2_RX_BUFFER_SIZE 256U
#define UART3_RX_BUFFER_SIZE 256U
#define UART4_RX_BUFFER_SIZE 256U
#define UART5_RX_BUFFER_SIZE 256U
#define UART6_RX_BUFFER_SIZE 4096U
#define UART7_RX_BUFFER_SIZE 256U
#define UART0_BAUDRATE 115200U
#define UART2_BAUDRATE 115200U
#define UART3_BAUDRATE 2400U
#define UART4_BAUDRATE 2400U
#define UART5_BAUDRATE 115200U
#define UART6_BAUDRATE 115200U
#define UART7_BAUDRATE 115200U
static _fifo_t s_uart0_rx;
static _fifo_t s_uart2_rx;
static _fifo_t s_uart3_rx;
static _fifo_t s_uart4_rx;
static _fifo_t s_uart5_rx;
static _fifo_t s_uart6_rx;
static _fifo_t s_uart7_rx;
static uint8_t s_uart0_rx_mem[UART0_RX_BUFFER_SIZE];
static uint8_t s_uart2_rx_mem[UART2_RX_BUFFER_SIZE];
static uint8_t s_uart3_rx_mem[UART3_RX_BUFFER_SIZE];
static uint8_t s_uart4_rx_mem[UART4_RX_BUFFER_SIZE];
static uint8_t s_uart5_rx_mem[UART5_RX_BUFFER_SIZE];
static uint8_t s_uart6_rx_mem[UART6_RX_BUFFER_SIZE];
static uint8_t s_uart7_rx_mem[UART7_RX_BUFFER_SIZE];
/* 前置声明:避免 usart6_init 中调用时出现隐式声明 */
void uart6_init(void);
/* 与 CCU601E_D 一致:按 slot 0~6 对应 7 路物理串口(实际编号 0/2/3/4/5/6/7),Shell 用 usart_printf 配置。 */
U8_T u8_uasrt_print[USART_NUM_PRINT_CNT] = {0};
static uint8_t usart_hw_num_to_slot(uint8_t usart_num)
{
switch (usart_num) {
case 0: return 0U;
case 2: return 1U;
case 3: return 2U;
case 4: return 3U;
case 5: return 4U;
case 6: return 5U;
case 7: return 6U;
default: return 0xFFU;
}
}
static void usart_dump_line(const char *tag, uint8_t usart_num, const uint8_t *data, uint16_t len)
{
uint16_t i;
if (data == NULL || len == 0U) {
return;
}
(void)printf("\r\n--------------usart(%u) %s :\r\n", (unsigned)usart_num, tag);
for (i = 0U; i < len; i++) {
(void)printf("%02X ", (unsigned int)data[i]);
}
(void)printf("\r\n---------------%s end\r\n", tag);
}
/**
* @brief 根据串口编号返回对应 ringfifo 句柄
* @param usart_num 串口编号
* @retval 对应的 _fifo_t 指针;不支持时返回 NULL
*/
static _fifo_t *get_fifo_by_id(uint8_t usart_num)
{
switch (usart_num) {
case 0: return &s_uart0_rx;
case 2: return &s_uart2_rx;
case 3: return &s_uart3_rx;
case 4: return &s_uart4_rx;
case 5: return &s_uart5_rx;
case 6: return &s_uart6_rx;
case 7: return &s_uart7_rx;
default: return NULL;
}
}
/**
* @brief 注册全部串口接收 FIFO(仅执行一次)
* @retval none
*/
static void usart_fifo_register_all(void)
{
static uint8_t registered = 0U;
if (registered) {
return;
}
fifo_register(&s_uart0_rx, s_uart0_rx_mem, UART0_RX_BUFFER_SIZE, NULL, NULL);
fifo_register(&s_uart2_rx, s_uart2_rx_mem, UART2_RX_BUFFER_SIZE, NULL, NULL);
fifo_register(&s_uart3_rx, s_uart3_rx_mem, UART3_RX_BUFFER_SIZE, NULL, NULL);
fifo_register(&s_uart4_rx, s_uart4_rx_mem, UART4_RX_BUFFER_SIZE, NULL, NULL);
fifo_register(&s_uart5_rx, s_uart5_rx_mem, UART5_RX_BUFFER_SIZE, NULL, NULL);
fifo_register(&s_uart6_rx, s_uart6_rx_mem, UART6_RX_BUFFER_SIZE, NULL, NULL);
fifo_register(&s_uart7_rx, s_uart7_rx_mem, UART7_RX_BUFFER_SIZE, NULL, NULL);
registered = 1U;
}
/* ===== 通用:阻塞式发送 ===== */
/**
* @brief 阻塞发送指定长度数据
* @param periph 串口外设基地址
* @param data 待发送数据指针
* @param len 待发送长度
* @retval none
*/
static void usart_send_blocking(uint32_t periph, const uint8_t *data, uint16_t len)
{
for (uint16_t i = 0; i < len; i++) {
while (RESET == usart_flag_get(periph, USART_FLAG_TBE)) {}
usart_data_transmit(periph, data[i]);
}
while (RESET == usart_flag_get(periph, USART_FLAG_TC)) {}
}
static uint32_t usart_periph_from_id(uint8_t usart_id)
{
switch (usart_id) {
case E_USART_0: return USART0;
case E_USART_2: return USART2;
case E_USART_3: return UART3;
case E_USART_4: return UART4;
case E_USART_5: return USART5;
case E_USART_6: return UART6;
case E_USART_7: return UART7;
default: return 0U;
}
}
/* ===== 各串口硬件初始化(集中在同一个文件里)===== */
/**
* @brief USART0 初始化:PF4/PF51152008N1
* @retval none
*/
void usart0_init(void)
{
rcu_periph_clock_enable(RCU_GPIOF);
rcu_periph_clock_enable(RCU_USART0);
gpio_af_set(GPIOF, GPIO_AF_4, GPIO_PIN_4);
gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_4);
gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_4);
gpio_af_set(GPIOF, GPIO_AF_4, GPIO_PIN_5);
gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
usart_deinit(USART0);
usart_word_length_set(USART0, USART_WL_8BIT);
usart_stop_bit_set(USART0, USART_STB_1BIT);
usart_parity_config(USART0, USART_PM_NONE);
usart_baudrate_set(USART0, UART0_BAUDRATE);
usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE);
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
// nvic_irq_enable(USART0_IRQn, 3U, 1U); /* 已迁移到 sys_drv_init.c:nvic_configuration() */
usart_interrupt_enable(USART0, USART_INT_RBNE);
usart_enable(USART0);
}
/**
* @brief USART2 初始化:PD8/PD91152008N1
* @retval none
*/
void usart2_init(void)
{
rcu_periph_clock_enable(RCU_GPIOD);
rcu_periph_clock_enable(RCU_USART2);
gpio_af_set(GPIOD, GPIO_AF_7, GPIO_PIN_8);
gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_8);
gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_8);
gpio_af_set(GPIOD, GPIO_AF_7, GPIO_PIN_9);
gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_9);
usart_deinit(USART2);
usart_word_length_set(USART2, USART_WL_8BIT);
usart_stop_bit_set(USART2, USART_STB_1BIT);
usart_parity_config(USART2, USART_PM_NONE);
usart_baudrate_set(USART2, UART2_BAUDRATE);
usart_receive_config(USART2, USART_RECEIVE_ENABLE);
usart_transmit_config(USART2, USART_TRANSMIT_ENABLE);
// nvic_irq_enable(USART2_IRQn, 3U, 2U); /* 已迁移到 sys_drv_init.c:nvic_configuration() */
usart_interrupt_enable(USART2, USART_INT_RBNE);
usart_enable(USART2);
}
/**
* @brief UART3 初始化:PC10/PC1124008E1
* @retval none
*/
void uart3_init(void)
{
rcu_periph_clock_enable(RCU_GPIOC);
rcu_periph_clock_enable(RCU_UART3);
gpio_af_set(GPIOC, GPIO_AF_8, GPIO_PIN_10);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_10);
gpio_af_set(GPIOC, GPIO_AF_8, GPIO_PIN_11);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
usart_deinit(UART3);
/* 8E1: data(8) + parity(1) -> word length must be 9-bit on this peripheral */
usart_word_length_set(UART3, USART_WL_9BIT);
usart_stop_bit_set(UART3, USART_STB_1BIT);
usart_parity_config(UART3, USART_PM_EVEN);
usart_baudrate_set(UART3, UART3_BAUDRATE);
usart_hardware_flow_rts_config(UART3, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(UART3, USART_CTS_DISABLE);
usart_receive_config(UART3, USART_RECEIVE_ENABLE);
usart_transmit_config(UART3, USART_TRANSMIT_ENABLE);
// nvic_irq_enable(UART3_IRQn, 3U, 0U); /* 已迁移到 sys_drv_init.c:nvic_configuration() */
usart_interrupt_enable(UART3, USART_INT_RBNE);
usart_enable(UART3);
}
/**
* @brief UART4 初始化:PC12/PD224008E1
* @retval none
*/
void uart4_init(void)
{
rcu_periph_clock_enable(RCU_GPIOC);
rcu_periph_clock_enable(RCU_GPIOD);
rcu_periph_clock_enable(RCU_UART4);
gpio_af_set(GPIOC, GPIO_AF_8, GPIO_PIN_12);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_12);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_12);
gpio_af_set(GPIOD, GPIO_AF_8, GPIO_PIN_2);
gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_2);
usart_deinit(UART4);
/* 8E1: data(8) + parity(1) -> word length must be 9-bit on this peripheral */
usart_word_length_set(UART4, USART_WL_9BIT);
usart_stop_bit_set(UART4, USART_STB_1BIT);
usart_parity_config(UART4, USART_PM_EVEN);
usart_baudrate_set(UART4, UART4_BAUDRATE);
usart_hardware_flow_rts_config(UART4, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(UART4, USART_CTS_DISABLE);
usart_receive_config(UART4, USART_RECEIVE_ENABLE);
usart_transmit_config(UART4, USART_TRANSMIT_ENABLE);
// nvic_irq_enable(UART4_IRQn, 2U, 0U); /* 已迁移到 sys_drv_init.c:nvic_configuration() */
usart_interrupt_enable(UART4, USART_INT_RBNE);
usart_enable(UART4);
}
/**
* @brief USART5 初始化:PC6/PC71152008N1
* @retval none
*/
void usart5_init(void)
{
rcu_periph_clock_enable(RCU_GPIOC);
rcu_periph_clock_enable(RCU_USART5);
gpio_af_set(GPIOC, GPIO_AF_7, GPIO_PIN_6);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_6);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_6);
gpio_af_set(GPIOC, GPIO_AF_7, GPIO_PIN_7);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
usart_deinit(USART5);
usart_oversample_config(USART5, USART_OVSMOD_16);
usart_word_length_set(USART5, USART_WL_8BIT);
usart_stop_bit_set(USART5, USART_STB_1BIT);
usart_parity_config(USART5, USART_PM_NONE);
usart_baudrate_set(USART5, UART5_BAUDRATE);
usart_hardware_flow_rts_config(USART5, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(USART5, USART_CTS_DISABLE);
usart_receive_config(USART5, USART_RECEIVE_ENABLE);
usart_transmit_config(USART5, USART_TRANSMIT_ENABLE);
// nvic_irq_enable(USART5_IRQn, 1U, 0U); /* 已迁移到 sys_drv_init.c:nvic_configuration() */
usart_interrupt_enable(USART5, USART_INT_RBNE);
usart_enable(USART5);
}
/**
* @brief 兼容接口:旧代码调用 usart6_init,内部转到 uart6_init
* @retval none
*/
void usart6_init(void)
{
/* 兼容旧头文件声明:usart6_init -> uart6_init */
uart6_init();
}
/**
* @brief UART6 初始化:PB4/PB31152008N1
* @retval none
*/
void uart6_init(void)
{
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_UART6);
gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_4);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_4);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_4);
gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_3);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
usart_deinit(UART6);
usart_word_length_set(UART6, USART_WL_8BIT);
usart_stop_bit_set(UART6, USART_STB_1BIT);
usart_parity_config(UART6, USART_PM_NONE);
usart_baudrate_set(UART6, UART6_BAUDRATE);
usart_hardware_flow_rts_config(UART6, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(UART6, USART_CTS_DISABLE);
usart_receive_config(UART6, USART_RECEIVE_ENABLE);
usart_transmit_config(UART6, USART_TRANSMIT_ENABLE);
// nvic_irq_enable(UART6_IRQn, 0U, 0U); /* 已迁移到 sys_drv_init.c:nvic_configuration() */
usart_interrupt_enable(UART6, USART_INT_RBNE);
usart_enable(UART6);
}
/**
* @brief UART7 初始化:PE1/PE01152008N1
* @retval none
*/
void uart7_init(void)
{
rcu_periph_clock_enable(RCU_GPIOE);
rcu_periph_clock_enable(RCU_UART7);
gpio_af_set(GPIOE, GPIO_AF_8, GPIO_PIN_1);
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_1);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_1);
gpio_af_set(GPIOE, GPIO_AF_8, GPIO_PIN_0);
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0);
usart_deinit(UART7);
usart_word_length_set(UART7, USART_WL_8BIT);
usart_stop_bit_set(UART7, USART_STB_1BIT);
usart_parity_config(UART7, USART_PM_NONE);
usart_baudrate_set(UART7, UART7_BAUDRATE);
usart_hardware_flow_rts_config(UART7, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(UART7, USART_CTS_DISABLE);
usart_receive_config(UART7, USART_RECEIVE_ENABLE);
usart_transmit_config(UART7, USART_TRANSMIT_ENABLE);
// nvic_irq_enable(UART7_IRQn, 3U, 7U); /* 已迁移到 sys_drv_init.c:nvic_configuration() */
usart_interrupt_enable(UART7, USART_INT_RBNE);
usart_enable(UART7);
}
/* ===== 中断服务程序(集中管理)===== */
/**
* @brief USART0 中断处理函数,接收数据压入 FIFO
* @retval none
*/
void USART0_IRQHandler(void)
{
if (RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) {
uint8_t b = usart_data_receive(USART0);
fifo_write(&s_uart0_rx, &b, 1U);
usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);
}
}
/**
* @brief USART2 中断处理函数,接收数据压入 FIFO
* @retval none
*/
void USART2_IRQHandler(void)
{
if (RESET != usart_interrupt_flag_get(USART2, USART_INT_FLAG_RBNE)) {
uint8_t b = usart_data_receive(USART2);
fifo_write(&s_uart2_rx, &b, 1U);
usart_interrupt_flag_clear(USART2, USART_INT_FLAG_RBNE);
}
}
/**
* @brief UART3 中断处理函数,接收数据压入 FIFO
* @retval none
*/
void UART3_IRQHandler(void)
{
if (RESET != usart_flag_get(UART3, USART_FLAG_RBNE)) {
uint8_t b = usart_data_receive(UART3);
fifo_write(&s_uart3_rx, &b, 1U);
}
}
/**
* @brief UART4 中断处理函数,接收数据压入 FIFO
* @retval none
*/
void UART4_IRQHandler(void)
{
if (RESET != usart_flag_get(UART4, USART_FLAG_RBNE)) {
uint8_t b = usart_data_receive(UART4);
fifo_write(&s_uart4_rx, &b, 1U);
}
}
/**
* @brief USART5 中断处理函数,接收数据压入 FIFO
* @retval none
*/
void USART5_IRQHandler(void)
{
if (RESET != usart_interrupt_flag_get(USART5, USART_INT_FLAG_RBNE)) {
uint8_t b = usart_data_receive(USART5);
fifo_write(&s_uart5_rx, &b, 1U);
}
}
/**
* @brief UART6 中断处理函数,接收数据压入 FIFO
* @retval none
*/
void UART6_IRQHandler(void)
{
if (RESET != usart_interrupt_flag_get(UART6, USART_INT_FLAG_RBNE)) {
uint8_t b = usart_data_receive(UART6);
fifo_write(&s_uart6_rx, &b, 1U);
}
}
/**
* @brief UART7 中断处理函数,接收数据压入 FIFO
* @retval none
*/
void UART7_IRQHandler(void)
{
if (RESET != usart_interrupt_flag_get(UART7, USART_INT_FLAG_RBNE)) {
uint8_t b = usart_data_receive(UART7);
fifo_write(&s_uart7_rx, &b, 1U);
}
}
/* ===== 对外:兼容现有统一API ===== */
/**
* @brief 初始化指定串口
* @param usart_num 串口编号(0/2/3/4/5/6/7
* @retval 0 初始化成功;-1 参数不支持
*/
int v_usart_init(uint8_t usart_num)
{
usart_fifo_register_all();
switch (usart_num) {
case 0: usart0_init(); return 0;
case 2: usart2_init(); return 0;
case 3: uart3_init(); return 0;
case 4: uart4_init(); return 0;
case 5: usart5_init(); return 0;
case 6: uart6_init(); return 0;
case 7: uart7_init(); return 0;
default: return -1;
}
}
/**
* @brief 批量初始化全部串口
* @retval 0
*/
int v_usart_init_all(void)
{
usart_fifo_register_all();
usart0_init();
usart2_init();
uart3_init();
uart4_init();
usart5_init();
uart6_init();
uart7_init();
return 0;
}
/**
* @brief 统一发送二进制数据接口
* @param usart_num 串口编号
* @param data 发送缓冲区
* @param len 发送长度
* @retval 0 发送成功;-1 参数或编号不支持
*/
int v_usart_send_data(uint8_t usart_num, uint8_t *data, uint16_t len)
{
if (data == NULL || len == 0U) return -1;
switch (usart_num) {
case 0: usart_send_blocking(USART0, data, len); return 0;
case 2: usart_send_blocking(USART2, data, len); return 0;
case 3: usart_send_blocking(UART3, data, len); return 0;
case 4: usart_send_blocking(UART4, data, len); return 0;
case 5: usart_send_blocking(USART5, data, len); return 0;
case 6: usart_send_blocking(UART6, data, len); return 0;
case 7: usart_send_blocking(UART7, data, len); return 0;
default: return -1;
}
}
/**
* @brief 查询指定串口接收 FIFO 的有效数据长度
* @param usart_num 串口编号
* @retval >=0 有效数据长度;-1 编号不支持
*/
int v_usart_get_rx_data_count(uint8_t usart_num)
{
_fifo_t *f = get_fifo_by_id(usart_num);
if (f == NULL) return -1;
return (int)fifo_get_occupy_size(f);
}
/**
* @brief 从指定串口 FIFO 读取数据
* @param usart_num 串口编号
* @param data 读取目标缓冲区
* @param len 期望读取长度
* @retval >=0 实际读取长度;-1 参数或编号不支持
*/
int v_usart_read_rx_buffer(uint8_t usart_num, uint8_t *data, uint16_t len)
{
if (data == NULL || len == 0U) return -1;
_fifo_t *f = get_fifo_by_id(usart_num);
if (f == NULL) return -1;
return (int)fifo_read(f, data, len);
}
/**
* @brief 快速读取接口(当前复用普通读取实现)
* @param usart_num 串口编号
* @param data 读取目标缓冲区
* @param len 期望读取长度
* @retval >=0 实际读取长度;-1 参数或编号不支持
*/
int v_usart_read_rx_buffer_fast(uint8_t usart_num, uint8_t *data, uint16_t len)
{
return v_usart_read_rx_buffer(usart_num, data, len);
}
/**
* @brief 清空指定串口接收 FIFO
* @param usart_num 串口编号
* @retval 0 清空成功;-1 编号不支持
*/
int v_usart_clear_rx_buffer(uint8_t usart_num)
{
_fifo_t *f = get_fifo_by_id(usart_num);
if (f == NULL) return -1;
fifo_release(f);
switch (usart_num) {
case 0: fifo_register(f, s_uart0_rx_mem, UART0_RX_BUFFER_SIZE, NULL, NULL); break;
case 2: fifo_register(f, s_uart2_rx_mem, UART2_RX_BUFFER_SIZE, NULL, NULL); break;
case 3: fifo_register(f, s_uart3_rx_mem, UART3_RX_BUFFER_SIZE, NULL, NULL); break;
case 4: fifo_register(f, s_uart4_rx_mem, UART4_RX_BUFFER_SIZE, NULL, NULL); break;
case 5: fifo_register(f, s_uart5_rx_mem, UART5_RX_BUFFER_SIZE, NULL, NULL); break;
case 6: fifo_register(f, s_uart6_rx_mem, UART6_RX_BUFFER_SIZE, NULL, NULL); break;
case 7: fifo_register(f, s_uart7_rx_mem, UART7_RX_BUFFER_SIZE, NULL, NULL); break;
default: return -1;
}
return 0;
}
/**
* @brief 对外统一二进制接收接口
* @param usart_id 串口ID
* @param u8_recvBuf 接收缓存
* @param u16_len 期望读取长度
* @retval 实际读取长度,失败返回0
*/
uint16_t u16_usart_recv(E_USART_ID usart_id ,uint8_t* u8_recvBuf ,uint16_t u16_len)
{
if(usart_id == E_USART_3)
{
//printf("\r\n");
}
int ret = v_usart_read_rx_buffer((uint8_t)usart_id, u8_recvBuf, u16_len);
if (ret <= 0) {
return 0U;
}
#if 1
{
uint8_t slot = usart_hw_num_to_slot((uint8_t)usart_id);
if ((ret > 0) && (slot < USART_NUM_PRINT_CNT) && (u8_uasrt_print[slot] != 0U)) {
usart_dump_line("recv", (uint8_t)usart_id, u8_recvBuf, (uint16_t)ret);
}
}
#endif
return (uint16_t)ret;
}
/**
* @brief 对外统一二进制发送接口
* @param usart_id 串口ID
* @param u8_sendBuf 发送缓存
* @param u16_len 待发送长度
* @retval 实际发送长度,失败返回0
*/
uint16_t u16_usart_send(E_USART_ID usart_id ,uint8_t* u8_sendBuf ,uint16_t u16_len)
{
int ret;
#if 1
{
uint8_t slot = usart_hw_num_to_slot((uint8_t)usart_id);
if ((u16_len > 0U) && (slot < USART_NUM_PRINT_CNT) && (u8_uasrt_print[slot] != 0U)) {
usart_dump_line("send", (uint8_t)usart_id, u8_sendBuf, u16_len);
}
}
#endif
ret = v_usart_send_data((uint8_t)usart_id, u8_sendBuf, u16_len);
if (ret < 0) {
return 0U;
}
return u16_len;
}
/**
* @brief 按串口编号打印格式化字符串
* @param usart_num 串口编号
* @param format 格式化字符串
* @param ... 可变参数
* @retval >0 输出长度;<=0 表示失败或无输出
*/
int v_usart_printf(uint8_t usart_num, const char *format, ...)
{
char buf[256];
va_list args;
va_start(args, format);
int n = vsnprintf(buf, sizeof(buf), format, args);
va_end(args);
if (n <= 0) return n;
if ((size_t)n >= sizeof(buf)) n = (int)(sizeof(buf) - 1U);
v_usart_send_data(usart_num, (uint8_t *)buf, (uint16_t)n);
return n;
}
void printf_hex(const char *mag , U8_T *data , U16_T len)
{
U16_T i;
const char *tag = (mag != NULL) ? mag : "";
if (data == NULL) {
printf("%s (00): <null>\r\n", tag);
return;
}
printf("%s (%02u): ", tag, (unsigned int)len);
for (i = 0U; i < len; i++) {
printf("%02X ", (unsigned int)data[i]);
}
printf("\r\n");
}
/**
* @brief printf 重定向接口,默认输出到 USART5
* @param ch 字符
* @param f 文件指针(未使用)
* @retval 原字符 ch
*/
int fputc(int ch, FILE *f)
{
(void)f;
uint8_t b = (uint8_t)ch;
uint32_t debug_periph;
if ((uint8_t)DEBUG_SHELL_USART_ID == (uint8_t)DEBUG_SHELL_USART_NULL) {
return ch;
}
debug_periph = usart_periph_from_id((uint8_t)DEBUG_SHELL_USART_ID);
if (debug_periph != 0U) {
usart_send_blocking(debug_periph, &b, 1U);
}
return ch;
}
+68
View File
@@ -0,0 +1,68 @@
/*!
\file usart.h
\brief the header file of USART
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
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 USART_H
#define USART_H
#include <stdint.h>
#include "main.h"
#include "publicdata/public_define.h"
typedef enum{
E_USART_0 = 0,
E_USART_2 = 2,
E_USART_3 = 3,
E_USART_4 = 4,
E_USART_5 = 5,
E_USART_6 = 6,
E_USART_7 = 7
}E_USART_ID;
/* 批量初始化所有串口 */
int v_usart_init_all(void);
/* 兼容层原子接口(meter_coll_task 等模块直接调用) */
int v_usart_send_data(uint8_t usart_num, uint8_t *data, uint16_t len);
int v_usart_read_rx_buffer(uint8_t usart_num, uint8_t *data, uint16_t len);
/* 对外二进制收发统一接口 */
uint16_t u16_usart_recv(E_USART_ID usart_id ,uint8_t* u8_recvBuf ,uint16_t u16_len);
uint16_t u16_usart_send(E_USART_ID usart_id ,uint8_t* u8_sendBuf ,uint16_t u16_len);
void printf_hex(const char *mag , U8_T *data , U16_T len);
/* Shell: usart_printf <slot 0~6> <0|1> 对应物理口 USART0,USART2,3,4,5,6,7;与 CCU601E_D 行为一致,按 slot 下标。 */
#define USART_NUM_PRINT_CNT (7U)
extern U8_T u8_uasrt_print[USART_NUM_PRINT_CNT];
#endif /* USART_H */
+13
View File
@@ -0,0 +1,13 @@
/**
* @file eeprom_map.h
* @brief Compatibility shim for legacy includes.
*
* EEPROM/FRAM capacity and logical partition macros are now defined in
* `fm24cl16.h`. Keep this header to avoid breaking old include paths.
*/
#ifndef EEPROM_MAP_H
#define EEPROM_MAP_H
#include "fm24cl16.h"
#endif /* EEPROM_MAP_H */
+102
View File
@@ -0,0 +1,102 @@
# eeprom 模块说明(i2c1 / fm24cl16
> 本文档描述 `CCU621_M` 工程中 `BSP/eeprom` 的职责、接口、初始化方式与测试方法。
[返回主说明](./../../README.md)
## 快速跳转
- [1. 模块职责](#1-模块职责)
- [2. 涉及文件](#2-涉及文件)
- [3. 初始化流程](#3-初始化流程)
- [4. 对外接口](#4-对外接口)
- [5. 已接入测试](#5-已接入测试)
- [6. 维护注意事项](#6-维护注意事项)
- [返回主说明 README](./../../README.md)
---
## 1. 模块职责
`BSP/eeprom` 负责 FM24CL16I2C FRAM)基础驱动能力,当前职责为:
- I2C1 引脚与时序配置;
- FRAM 地址初始化;
- FRAM 指定地址写入与读出;
- 提供上层测试调用接口(由 `app_test` 验证读写一致性)。
---
## 2. 涉及文件
| 文件 | 说明 |
|---|---|
| `BSP/eeprom/i2c1.h` | I2C1 宏配置与函数声明 |
| `BSP/eeprom/i2c1.c` | I2C1 GPIO/I2C 初始化与总线复位 |
| `BSP/eeprom/fm24cl16.h` | FM24CL16 对外接口声明 + 容量/地址分区宏 |
| `BSP/eeprom/fm24cl16.c` | FM24CL16 读写实现(`eeprom_buffer_write/read` 等) |
| `BSP/eeprom/eeprom_map.h` | 兼容头(转发到 `fm24cl16.h` |
> 说明:历史冗余文件 `eeprom_I2C_fm24cl16.c/.h` 已移除,避免双实现并存导致维护混乱。
---
## 3. 初始化流程
当前已在 `BSP/sys_drv_init.c``v_sys_hardware_init()` 中接入:
1. `gpio_config()`:配置 I2C1 对应 GPIO
2. `i2c_config()`:配置 I2C1 时序并使能;
3. `i2c_eeprom_init()`:初始化 FM24CL16 设备地址等参数。
调用位置:系统硬件初始化阶段(通信外设初始化流程中)。
---
## 4. 对外接口
### 4.1 I2C 基础接口(`i2c1.c`
- `void gpio_config(void);`
- `void i2c_config(void);`
- `void i2c_bus_reset(void);`
### 4.2 EEPROM 接口与地址分区(`fm24cl16.h` + `fm24cl16.c`
- `void i2c_eeprom_init(void);`
- `void eeprom_buffer_write(uint8_t *p_buffer, uint16_t write_address, uint16_t number_of_byte);`
- `void eeprom_buffer_read(uint8_t *p_buffer, uint16_t read_address, uint16_t number_of_byte);`
- `void eeprom_driver_init(void);`
- 容量/范围宏:`FRAM_SIZE_BYTES``FRAM_START_ADDR``FRAM_END_ADDR` 等;
- 分区宏:`EEPROM_ADDR_CHGRCD_MNG``EEPROM_ADDR_HISALARM_MNG``EEPROM_ADDR_UNSETTLED_MNG``EEPROM_ADDR_CARD_MNG``EEPROM_ADDR_A_LOG_DATA``EEPROM_ADDR_B_LOG_DATA`
> 说明:`eeprom_page_write()` 为驱动内部函数,已收敛为 `fm24cl16.c` 内部 `static`,不再对外暴露。
---
## 5. 已接入测试
`app/app_init/app_test.c` 中,测试流程已统一由 `v_app_test_periodic()` 调度,并受总开关控制:
- `APP_TEST_ENABLE`:0 时不编译测试实现,仅保留空函数桩;
- EEPROM 基础读写:固定地址 32B;
- EEPROM 多地址测试:覆盖分区起点与测试窗口地址,执行备份-写读校验-恢复;
- EEPROM 速度测试:在测试窗口执行循环读写并统计吞吐;
- Flash 测试:固定地址测速 + 1MB 步长 32MB 全空间校验。
---
## 6. 维护注意事项
1. 保持 EEPROM 仅一套实现,避免同名接口重复定义;
2. 修改 I2C 引脚/时序时,同步更新 `i2c1.h` 与硬件原理图;
3. 增加上层功能前,先通过 `u8_app_test_eeprom_rw_once()` 回归;
4. EEPROM 驱动已引入互斥访问,新增调用方需避免在中断上下文直接调用阻塞式接口;
5. 建议业务数据区与测试区分离(例如测试使用 `0x0600` 起始窗口),避免互相覆盖。
---
## 关联文档
- 主索引:[`CCU621_M/README.md`](./../../README.md)
- 本文位置:`BSP/eeprom/eeprom模块说明.md`
+631
View File
@@ -0,0 +1,631 @@
/*!
\file fm24cl16.c
\brief the read and write function file
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
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 "fm24cl16.h"
#include "i2c1.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#define EEPROM_BLOCK0_ADDRESS 0xA0
#define MAX_RELOAD_SIZE 255
#define EEPROM_WRITE_DELAY_MS 5U
#define EEPROM_STATE_LOOP_MAX 64U
/* Stable mode on this board: fixed slave address + 16-bit internal address */
#define EEPROM_MEM_ADDR_16BIT 1U
#define EEPROM_DEVADDR_FIXED 1U
static uint8_t eeprom_address;
static volatile uint32_t g_eeprom_last_error = 0U;
static SemaphoreHandle_t g_eeprom_mutex = NULL;
/* forward declarations for internal helpers */
static void eeprom_i2c_clear_error_flags(void);
uint8_t eeprom_probe(uint16_t mem_address);
static void eeprom_lock(void)
{
if (g_eeprom_mutex != NULL) {
(void)xSemaphoreTake(g_eeprom_mutex, portMAX_DELAY);
}
}
static void eeprom_unlock(void)
{
if (g_eeprom_mutex != NULL) {
(void)xSemaphoreGive(g_eeprom_mutex);
}
}
enum {
EEPROM_ERR_NONE = 0U,
EEPROM_ERR_WR_BUSY_TIMEOUT = 1U,
EEPROM_ERR_WR_ADDR_TBE_TIMEOUT = 2U,
EEPROM_ERR_WR_DATA_TBE_TIMEOUT = 3U,
EEPROM_ERR_WR_STOP_TIMEOUT = 4U,
EEPROM_ERR_RD_BUSY_TIMEOUT = 5U,
EEPROM_ERR_RD_ADDR_TBE_TIMEOUT = 6U,
EEPROM_ERR_RD_RESTART_TC_TIMEOUT = 7U,
EEPROM_ERR_RD_RELOAD_TCR_TIMEOUT = 8U,
EEPROM_ERR_RD_DATA_RBNE_TIMEOUT = 9U,
EEPROM_ERR_RD_STOP_TIMEOUT = 10U,
EEPROM_ERR_LOOP_GUARD = 11U,
EEPROM_ERR_I2C_NACK = 12U,
EEPROM_ERR_I2C_BUS = 13U
};
static uint8_t eeprom_i2c_error_pending(void)
{
if(i2c_flag_get(I2CX, I2C_FLAG_NACK)) {
g_eeprom_last_error = EEPROM_ERR_I2C_NACK;
i2c_flag_clear(I2CX, I2C_FLAG_NACK);
return 1U;
}
if(i2c_flag_get(I2CX, I2C_FLAG_BERR) || i2c_flag_get(I2CX, I2C_FLAG_LOSTARB) ||
i2c_flag_get(I2CX, I2C_FLAG_OUERR)) {
g_eeprom_last_error = EEPROM_ERR_I2C_BUS;
i2c_flag_clear(I2CX, I2C_FLAG_BERR);
i2c_flag_clear(I2CX, I2C_FLAG_LOSTARB);
i2c_flag_clear(I2CX, I2C_FLAG_OUERR);
return 1U;
}
return 0U;
}
static void eeprom_i2c_clear_error_flags(void)
{
i2c_flag_clear(I2CX, I2C_FLAG_NACK);
i2c_flag_clear(I2CX, I2C_FLAG_BERR);
i2c_flag_clear(I2CX, I2C_FLAG_LOSTARB);
i2c_flag_clear(I2CX, I2C_FLAG_OUERR);
i2c_flag_clear(I2CX, I2C_FLAG_TIMEOUT);
}
static uint8_t eeprom_make_dev_addr(uint16_t mem_addr)
{
#if (EEPROM_DEVADDR_FIXED == 1U)
(void)mem_addr;
return (uint8_t)EEPROM_BLOCK0_ADDRESS;
#else
return (uint8_t)(EEPROM_BLOCK0_ADDRESS | ((mem_addr >> 7U) & 0x0EU));
#endif
}
static void eeprom_page_write(uint8_t *p_buffer, uint16_t write_address, uint8_t number_of_byte);
static uint8_t eeprom_read_chunk(uint8_t *p_buffer, uint16_t read_address, uint8_t number_of_byte);
uint32_t eeprom_get_last_error(void)
{
return g_eeprom_last_error;
}
uint8_t eeprom_probe(uint16_t mem_address)
{
uint32_t timeout = 0U;
uint8_t dev_addr = eeprom_make_dev_addr(mem_address);
eeprom_lock();
eeprom_i2c_clear_error_flags();
if(eeprom_i2c_error_pending()) {
i2c_bus_reset();
eeprom_unlock();
return I2C_FAIL;
}
i2c_master_addressing(I2CX, dev_addr, I2C_MASTER_TRANSMIT);
i2c_transfer_byte_number_config(I2CX,
#if (EEPROM_MEM_ADDR_16BIT == 1U)
2U
#else
1U
#endif
);
i2c_automatic_end_disable(I2CX);
while(i2c_flag_get(I2CX, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_WR_BUSY_TIMEOUT;
i2c_bus_reset();
eeprom_unlock();
return I2C_FAIL;
}
i2c_start_on_bus(I2CX);
timeout = 0U;
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
if(eeprom_i2c_error_pending()) {
i2c_stop_on_bus(I2CX);
i2c_bus_reset();
eeprom_unlock();
return I2C_FAIL;
}
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_WR_ADDR_TBE_TIMEOUT;
i2c_bus_reset();
eeprom_unlock();
return I2C_FAIL;
}
/* send memory address bytes to validate addressing scheme */
#if (EEPROM_MEM_ADDR_16BIT == 1U)
i2c_data_transmit(I2CX, (uint8_t)((mem_address >> 8U) & 0xFFU));
timeout = 0U;
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
if(eeprom_i2c_error_pending()) {
i2c_stop_on_bus(I2CX);
i2c_bus_reset();
eeprom_unlock();
return I2C_FAIL;
}
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_WR_ADDR_TBE_TIMEOUT;
i2c_bus_reset();
eeprom_unlock();
return I2C_FAIL;
}
#endif
i2c_data_transmit(I2CX, (uint8_t)(mem_address & 0xFFU));
timeout = 0U;
while((!i2c_flag_get(I2CX, I2C_FLAG_TC)) && (timeout < I2C_TIME_OUT)) {
if(eeprom_i2c_error_pending()) {
i2c_stop_on_bus(I2CX);
i2c_bus_reset();
eeprom_unlock();
return I2C_FAIL;
}
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_WR_DATA_TBE_TIMEOUT;
i2c_bus_reset();
eeprom_unlock();
return I2C_FAIL;
}
i2c_stop_on_bus(I2CX);
timeout = 0U;
while((!i2c_flag_get(I2CX, I2C_FLAG_STPDET)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_WR_STOP_TIMEOUT;
i2c_bus_reset();
eeprom_unlock();
return I2C_FAIL;
}
i2c_flag_clear(I2CX, I2C_FLAG_STPDET);
g_eeprom_last_error = EEPROM_ERR_NONE;
eeprom_unlock();
return I2C_OK;
}
/*!
\brief unified init entry for EEPROM driver
\param[in] none
\param[out] none
\retval none
*/
void eeprom_driver_init(void)
{
gpio_config();
i2c_config();
i2c_eeprom_init();
if (g_eeprom_mutex == NULL) {
g_eeprom_mutex = xSemaphoreCreateMutex();
}
}
/*!
\brief initialize peripherals used by the I2C EEPROM driver
\param[in] none
\param[out] none
\retval none
*/
void i2c_eeprom_init(void)
{
eeprom_address = EEPROM_BLOCK0_ADDRESS;
}
/*!
\brief write buffer of data to the I2C EEPROM
\param[in] p_buffer: pointer to the buffer containing the data to be written to the EEPROM
\param[in] write_address: EEPROM's internal address to write to
\param[in] number_of_byte: number of bytes to write to the EEPROM
\param[out] none
\retval none
*/
void eeprom_buffer_write(uint8_t *p_buffer, uint16_t write_address, uint16_t number_of_byte)
{
uint16_t remain;
uint16_t off;
uint8_t chunk;
uint8_t page_left;
uint8_t retry;
if ((p_buffer == NULL) || (number_of_byte == 0U)) {
g_eeprom_last_error = EEPROM_ERR_NONE;
return;
}
eeprom_lock();
g_eeprom_last_error = EEPROM_ERR_NONE;
remain = number_of_byte;
off = 0U;
while (remain > 0U) {
/* Do not cross page boundary (AT24/FRAM compatible safe strategy). */
page_left = (uint8_t)(I2C_PAGE_SIZE - (((uint16_t)(write_address + off)) % I2C_PAGE_SIZE));
if (page_left == 0U) {
page_left = I2C_PAGE_SIZE;
}
chunk = (remain > (uint16_t)page_left) ? page_left : (uint8_t)remain;
if (chunk > I2C_PAGE_SIZE) {
chunk = I2C_PAGE_SIZE;
}
retry = 0U;
while (retry < 3U) {
eeprom_page_write(&p_buffer[off], (uint16_t)(write_address + off), chunk);
if (g_eeprom_last_error == EEPROM_ERR_NONE) {
break;
}
/* retry after simple bus recovery */
i2c_bus_reset();
vTaskDelay(pdMS_TO_TICKS(1U));
retry++;
}
if (g_eeprom_last_error != EEPROM_ERR_NONE) {
eeprom_unlock();
return;
}
off = (uint16_t)(off + (uint16_t)chunk);
remain = (uint16_t)(remain - (uint16_t)chunk);
vTaskDelay(pdMS_TO_TICKS(EEPROM_WRITE_DELAY_MS));
}
eeprom_unlock();
}
/*!
\brief write more than one byte to the EEPROM with a single write cycle
\param[in] p_buffer: pointer to the buffer containing the data to be written to the EEPROM
\param[in] write_address: EEPROM's internal address to write to
\param[in] number_of_byte: number of bytes to write to the EEPROM
\param[out] none
\retval none
*/
static void eeprom_page_write(uint8_t *p_buffer, uint16_t write_address, uint8_t number_of_byte)
{
uint32_t timeout = 0U;
uint8_t bytes_sent = 0;
uint8_t mem_high_addr = (uint8_t)((write_address >> 8U) & 0xFFU);
uint8_t mem_low_addr = (uint8_t)(write_address & 0xFFU);
if ((p_buffer == NULL) || (number_of_byte == 0U)) {
return;
}
eeprom_address = eeprom_make_dev_addr(write_address);
eeprom_i2c_clear_error_flags();
i2c_flag_clear(I2CX, I2C_FLAG_STPDET);
while(i2c_flag_get(I2CX, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_WR_BUSY_TIMEOUT;
i2c_bus_reset();
return;
}
i2c_master_addressing(I2CX, eeprom_address, I2C_MASTER_TRANSMIT);
i2c_transfer_byte_number_config(I2CX,
#if (EEPROM_MEM_ADDR_16BIT == 1U)
(uint32_t)number_of_byte + 2U
#else
(uint32_t)number_of_byte + 1U
#endif
);
/* Use explicit TC->STOP sequence to avoid partial writes */
i2c_automatic_end_disable(I2CX);
i2c_reload_disable(I2CX);
i2c_start_on_bus(I2CX);
timeout = 0U;
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
if(eeprom_i2c_error_pending()) {
i2c_bus_reset();
return;
}
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_WR_ADDR_TBE_TIMEOUT;
i2c_bus_reset();
return;
}
#if (EEPROM_MEM_ADDR_16BIT == 1U)
i2c_data_transmit(I2CX, mem_high_addr);
timeout = 0U;
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
if(eeprom_i2c_error_pending()) {
i2c_bus_reset();
return;
}
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_WR_ADDR_TBE_TIMEOUT;
i2c_bus_reset();
return;
}
#endif
i2c_data_transmit(I2CX, mem_low_addr);
/* wait address byte(s) accepted before data */
timeout = 0U;
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
if(eeprom_i2c_error_pending()) {
i2c_bus_reset();
return;
}
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_WR_ADDR_TBE_TIMEOUT;
i2c_bus_reset();
return;
}
while(bytes_sent < number_of_byte) {
timeout = 0U;
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
if(eeprom_i2c_error_pending()) {
i2c_bus_reset();
return;
}
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_WR_DATA_TBE_TIMEOUT;
i2c_bus_reset();
return;
}
i2c_data_transmit(I2CX, *p_buffer);
p_buffer++;
bytes_sent++;
}
/* wait transfer complete then send stop */
timeout = 0U;
while((!i2c_flag_get(I2CX, I2C_FLAG_TC)) && (timeout < I2C_TIME_OUT)) {
if(eeprom_i2c_error_pending()) {
i2c_bus_reset();
return;
}
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_WR_DATA_TBE_TIMEOUT;
i2c_bus_reset();
return;
}
i2c_stop_on_bus(I2CX);
timeout = 0U;
while((!i2c_flag_get(I2CX, I2C_FLAG_STPDET)) && (timeout < I2C_TIME_OUT)) {
if(eeprom_i2c_error_pending()) {
i2c_bus_reset();
return;
}
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_WR_STOP_TIMEOUT;
i2c_bus_reset();
return;
}
i2c_flag_clear(I2CX, I2C_FLAG_STPDET);
}
static uint8_t eeprom_read_chunk(uint8_t *p_buffer, uint16_t read_address, uint8_t number_of_byte)
{
uint32_t timeout = 0U;
uint8_t mem_high_addr = (uint8_t)((read_address >> 8U) & 0xFFU);
uint8_t mem_low_addr = (uint8_t)(read_address & 0xFFU);
uint8_t dev_addr = eeprom_make_dev_addr(read_address);
uint8_t i = 0U;
if ((p_buffer == NULL) || (number_of_byte == 0U)) {
return I2C_FAIL;
}
eeprom_i2c_clear_error_flags();
while(i2c_flag_get(I2CX, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_RD_BUSY_TIMEOUT;
i2c_bus_reset();
return I2C_FAIL;
}
/* phase 1: set memory low address */
i2c_master_addressing(I2CX, dev_addr, I2C_MASTER_TRANSMIT);
i2c_transfer_byte_number_config(I2CX,
#if (EEPROM_MEM_ADDR_16BIT == 1U)
2U
#else
1U
#endif
);
i2c_automatic_end_disable(I2CX);
i2c_start_on_bus(I2CX);
timeout = 0U;
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
if(eeprom_i2c_error_pending()) {
i2c_bus_reset();
return I2C_FAIL;
}
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_RD_ADDR_TBE_TIMEOUT;
i2c_bus_reset();
return I2C_FAIL;
}
#if (EEPROM_MEM_ADDR_16BIT == 1U)
i2c_data_transmit(I2CX, mem_high_addr);
timeout = 0U;
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
if(eeprom_i2c_error_pending()) {
i2c_bus_reset();
return I2C_FAIL;
}
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_RD_ADDR_TBE_TIMEOUT;
i2c_bus_reset();
return I2C_FAIL;
}
#endif
i2c_data_transmit(I2CX, mem_low_addr);
timeout = 0U;
while((!i2c_flag_get(I2CX, I2C_FLAG_TC)) && (timeout < I2C_TIME_OUT)) {
if(eeprom_i2c_error_pending()) {
i2c_bus_reset();
return I2C_FAIL;
}
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_RD_RESTART_TC_TIMEOUT;
i2c_bus_reset();
return I2C_FAIL;
}
/* phase 2: repeated start + read bytes */
i2c_master_addressing(I2CX, dev_addr, I2C_MASTER_RECEIVE);
i2c_transfer_byte_number_config(I2CX, number_of_byte);
i2c_automatic_end_enable(I2CX);
i2c_start_on_bus(I2CX);
for(i = 0U; i < number_of_byte; i++) {
timeout = 0U;
while((!i2c_flag_get(I2CX, I2C_FLAG_RBNE)) && (timeout < I2C_TIME_OUT)) {
if(eeprom_i2c_error_pending()) {
i2c_bus_reset();
return I2C_FAIL;
}
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_RD_DATA_RBNE_TIMEOUT;
i2c_bus_reset();
return I2C_FAIL;
}
p_buffer[i] = (uint8_t)i2c_data_receive(I2CX);
}
timeout = 0U;
while((!i2c_flag_get(I2CX, I2C_FLAG_STPDET)) && (timeout < I2C_TIME_OUT)) {
if(eeprom_i2c_error_pending()) {
i2c_bus_reset();
return I2C_FAIL;
}
timeout++;
}
if(timeout >= I2C_TIME_OUT) {
g_eeprom_last_error = EEPROM_ERR_RD_STOP_TIMEOUT;
i2c_bus_reset();
return I2C_FAIL;
}
i2c_flag_clear(I2CX, I2C_FLAG_STPDET);
return I2C_OK;
}
/*!
\brief read data from the EEPROM
\param[in] p_buffer: pointer to the buffer that receives the data read from the EEPROM
\param[in] read_address: EEPROM's internal address to start reading from
\param[in] number_of_byte: number of bytes to reads from the EEPROM
\param[out] none
\retval none
*/
void eeprom_buffer_read(uint8_t *p_buffer, uint16_t read_address, uint16_t number_of_byte)
{
uint16_t remain;
uint16_t off;
uint8_t chunk;
uint8_t retry;
if ((p_buffer == NULL) || (number_of_byte == 0U)) {
g_eeprom_last_error = EEPROM_ERR_NONE;
return;
}
eeprom_lock();
g_eeprom_last_error = EEPROM_ERR_NONE;
remain = number_of_byte;
off = 0U;
while (remain > 0U) {
/* For read, allow larger chunk but keep it reasonable */
chunk = (remain > 128U) ? 128U : (uint8_t)remain;
retry = 0U;
while (retry < 3U) {
if (eeprom_read_chunk(&p_buffer[off], (uint16_t)(read_address + off), chunk) == I2C_OK) {
break;
}
i2c_bus_reset();
vTaskDelay(pdMS_TO_TICKS(1U));
retry++;
}
if (g_eeprom_last_error != EEPROM_ERR_NONE) {
eeprom_unlock();
return;
}
off = (uint16_t)(off + (uint16_t)chunk);
remain = (uint16_t)(remain - (uint16_t)chunk);
vTaskDelay(pdMS_TO_TICKS(1U));
}
eeprom_unlock();
}
+120
View File
@@ -0,0 +1,120 @@
/*!
\file fm24cl16.h
\brief the header file of AT24Cxx
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
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 FM24CL16_H
#define FM24CL16_H
#include "gd32h7xx_it.h"
typedef enum {
I2C_START = 0,
I2C_SEND_ADDRESS,
I2C_RESTART,
I2C_TRANSMIT_DATA,
I2C_RELOAD,
I2C_STOP,
I2C_END
} i2c_process_enum;
#define I2C_TIME_OUT (uint32_t)(20000)
#define EEP_FIRST_PAGE 0x00
#define I2C_OK 0
#define I2C_FAIL 1
/* FM24xx logical capacity/address map */
#define FRAM_SIZE_KBIT (16U)
#define FRAM_SIZE_BYTES (2048U)
#define FRAM_ADDR_BITS (11U)
#define FRAM_START_ADDR (0x0000U)
#define FRAM_END_ADDR (0x07FFU)
#define FRAM_TOTAL_ADDR (0x0800U)
#define FRAM_PAGE_SIZE (256U)
#define FRAM_PAGES_COUNT (8U)
/* Logical partitions (ported from CCU601E_D) */
#define EEPROM_ADDR_CHGRCD_MNG (0x0000U) /* charge record manager */
#define EEPROM_ADDR_HISALARM_MNG (0x0020U) /* history alarm manager */
#define EEPROM_ADDR_UNSETTLED_MNG (0x0040U) /* unsettled order manager */
#define EEPROM_ADDR_CARD_MNG (0x00A0U) /* card whitelist manager */
#define EEPROM_ADDR_A_LOG_DATA (0x0100U) /* gun A temporary order data */
#define EEPROM_ADDR_B_LOG_DATA (0x0400U) /* gun B temporary order data */
#define EEPROM_ADDR_OCPP_MV_OFFLINE_CTRL 0x0700U
#define EEPROM_OCPP_MV_OFFLINE_CTRL_SIZE 128u
#define EEPROM_ADDR_OCPP_FW_INSTALLED_PENDING 0x0780U
#define EEPROM_OCPP_FW_INSTALLED_PENDING_VAL 0x55U
#define EEPROM_OCPP_FW_INSTALLED_PENDING_CLEAR 0x00U
#define EEPROM_ADDR_IN_RANGE(addr) ((uint16_t)(addr) <= (uint16_t)FRAM_END_ADDR)
/* function declarations */
/* unified EEPROM init: GPIO + I2C + device address */
void eeprom_driver_init(void);
/* initialize peripherals used by the I2C EEPROM driver */
void i2c_eeprom_init(void);
/* write buffer of data to the I2C EEPROM */
void eeprom_buffer_write(uint8_t *p_buffer, uint16_t write_address, uint16_t number_of_byte);
/* read data from the EEPROM */
void eeprom_buffer_read(uint8_t *p_buffer, uint16_t read_address, uint16_t number_of_byte);
/* read latest driver error code (0 means no error) */
uint32_t eeprom_get_last_error(void);
/* probe one EEPROM block address and return I2C_OK/I2C_FAIL */
uint8_t eeprom_probe(uint16_t mem_address);
/* --------------------------------------------------------------------------
* Compatibility layer for migrated modules from CCU601E_D
* - old API: eeprom_write/eeprom_read(...) returns HAL_OK on success
* - local API: eeprom_buffer_write/eeprom_buffer_read(...) with error queried
* -------------------------------------------------------------------------- */
#ifndef HAL_OK
#define HAL_OK (0)
#endif
#ifndef HAL_ERROR
#define HAL_ERROR (1)
#endif
static inline int eeprom_write(uint16_t addr, uint8_t *buf, uint16_t len)
{
eeprom_buffer_write(buf, addr, len);
return (eeprom_get_last_error() == 0U) ? HAL_OK : HAL_ERROR;
}
static inline int eeprom_read(uint16_t addr, uint8_t *buf, uint16_t len)
{
eeprom_buffer_read(buf, addr, len);
return (eeprom_get_last_error() == 0U) ? HAL_OK : HAL_ERROR;
}
#endif /* AT24CXX_H */
+128
View File
@@ -0,0 +1,128 @@
/*!
\file i2c.c
\brief I2C configuration file
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
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 "i2c1.h"
#include <stdio.h>
#include "gd32h7xx.h"
/*!
\brief configure the GPIO ports
\param[in] none
\param[out] none
\retval none
*/
void gpio_config(void)
{
/* enable GPIO clock */
rcu_periph_clock_enable(RCU_GPIO_I2C);
/* enable I2C clock */
rcu_periph_clock_enable(RCU_I2C);
/* connect I2C_SCL_PIN to I2C_SCL */
gpio_af_set(I2C_SCL_PORT, I2C_GPIO_AF, I2C_SCL_PIN);
/* connect I2C_SDA_PIN to I2C_SDA */
gpio_af_set(I2C_SDA_PORT, I2C_GPIO_AF, I2C_SDA_PIN);
/* configure GPIO pins of I2C */
gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SCL_PIN);
gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, I2C_SCL_PIN);
gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SDA_PIN);
gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, I2C_SDA_PIN);
}
/*!
\brief configure the I2C interface
\param[in] none
\param[out] none
\retval none
*/
void i2c_config(void)
{
/* configure I2C timing */
i2c_timing_config(I2CX, 0x3, 0x7, 0);
i2c_master_clock_config(I2CX, 0x32, 0x32);
/* enable analog filter and disable digital filter for bus robustness */
i2c_analog_noise_filter_enable(I2CX);
i2c_digital_noise_filter_config(I2CX, FILTER_DISABLE);
/* enable I2C */
i2c_enable(I2CX);
}
/*!
\brief reset I2C gpio configure
\param[in] none
\param[out] none
\retval none
*/
void i2c_gpio_reset(void)
{
/* reset I2C_SCL_PIN and I2C_SDA_PIN */
gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, I2C_SCL_PIN);
gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, I2C_SCL_PIN);
gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, I2C_SDA_PIN);
gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, I2C_SDA_PIN);
}
/*!
\brief reset i2c bus
\param[in] none
\param[out] none
\retval none
*/
void i2c_bus_reset()
{
/* configure SDA/SCL for GPIO */
GPIO_BC(I2C_SCL_PORT) |= I2C_SCL_PIN;
GPIO_BC(I2C_SDA_PORT) |= I2C_SDA_PIN;
/* reset I2C_SCL_PIN and I2C_SDA_PIN */
i2c_gpio_reset();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
/* stop signal */
GPIO_BOP(I2C_SCL_PORT) |= I2C_SCL_PIN;
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
GPIO_BOP(I2C_SDA_PORT) |= I2C_SDA_PIN;
/* connect I2C_SCL_PIN to I2C_SCL */
/* connect I2C_SDA_PIN to I2C_SDA */
gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SCL_PIN);
gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, I2C_SCL_PIN);
gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SDA_PIN);
gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, I2C_SDA_PIN);
}
+60
View File
@@ -0,0 +1,60 @@
/*!
\file i2c.h
\brief the header file of I2C
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
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 I2C_H
#define I2C_H
#define I2C_SPEED 400000
#define I2C_PAGE_SIZE 8
#define I2CX I2C1
#define RCU_GPIO_I2C RCU_GPIOF
#define RCU_I2C RCU_I2C1
#define I2C_SCL_PORT GPIOF
#define I2C_SDA_PORT GPIOF
#define I2C_SCL_PIN GPIO_PIN_1
#define I2C_SDA_PIN GPIO_PIN_0
#define I2C_GPIO_AF GPIO_AF_4
/* function declarations */
/* configure the GPIO ports */
void gpio_config(void);
/* configure the I2C interface */
void i2c_config(void);
/* reset I2C gpio configure */
void i2c_gpio_reset(void);
/* reset i2c bus */
void i2c_bus_reset(void);
#endif /* I2C_H */
+550
View File
@@ -0,0 +1,550 @@
#include "exflash_spi4_gd25qxx.h"
#include "gd32h7xx_dma.h"
#include <stddef.h>
/* NOR Flash 标准指令 */
#define EXFLASH_CMD_WRITE (0x02u) /* Page Program */
#define EXFLASH_CMD_WREN (0x06u) /* Write Enable */
#define EXFLASH_CMD_READ (0x03u) /* Read Data */
#define EXFLASH_CMD_RDSR (0x05u) /* Read Status Register */
#define EXFLASH_CMD_RDID (0x9Fu) /* Read JEDEC ID */
#define EXFLASH_CMD_SE (0x20u) /* 4KB Sector Erase */
#define EXFLASH_CMD_BE (0xC7u) /* Bulk Erase */
#define EXFLASH_CMD_EN4B (0xB7u) /* Enter 4-byte address mode */
/* 状态寄存器位定义 */
#define EXFLASH_SR_WIP_MASK (0x01u) /* WIP=1 表示写/擦进行中 */
/* 读操作发送的占位字节 */
#define EXFLASH_DUMMY_BYTE (0xA5u)
#define EXFLASH_DMA_PERIPH (DMA0)
#define EXFLASH_DMA_RX_CH (DMA_CH1)
#define EXFLASH_DMA_TX_CH (DMA_CH2)
#define EXFLASH_DMA_WAIT_MAX_LOOP (0x00FFFFFFu)
/* Cortex-M7 D-Cache line size = 32 bytes */
#define EXFLASH_DCACHE_LINE_SIZE (32u)
/* DMA 中断置位标志:1=该通道传输完成 */
static volatile uint8_t s_spi4_dma_rx_done = 0u;
static volatile uint8_t s_spi4_dma_tx_done = 0u;
/* 读阶段 TX 使用的固定哑字节;DMA 配置为 memory_inc disable */
static uint8_t s_spi4_dma_dummy_tx = EXFLASH_DUMMY_BYTE;
/* 写阶段 RX 丢弃字节;DMA 配置为 memory_inc disable */
static uint8_t s_spi4_dma_dummy_rx = 0u;
static uint32_t cache_addr_down_align(uint32_t addr, uint32_t align)
{
return addr & ~(align - 1u);
}
static uint32_t cache_len_up_align(uint32_t addr, uint32_t len, uint32_t align)
{
uint32_t end = addr + len;
uint32_t end_aligned = (end + (align - 1u)) & ~(align - 1u);
return end_aligned - cache_addr_down_align(addr, align);
}
static void dcache_clean_by_addr(uint32_t addr, uint32_t len)
{
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
uint32_t a = cache_addr_down_align(addr, EXFLASH_DCACHE_LINE_SIZE);
uint32_t l = cache_len_up_align(addr, len, EXFLASH_DCACHE_LINE_SIZE);
SCB_CleanDCache_by_Addr((uint32_t *)a, (int32_t)l);
#else
(void)addr;
(void)len;
#endif
}
static void dcache_invalidate_by_addr(uint32_t addr, uint32_t len)
{
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
uint32_t a = cache_addr_down_align(addr, EXFLASH_DCACHE_LINE_SIZE);
uint32_t l = cache_len_up_align(addr, len, EXFLASH_DCACHE_LINE_SIZE);
SCB_InvalidateDCache_by_Addr((uint32_t *)a, (int32_t)l);
#else
(void)addr;
(void)len;
#endif
}
/**
* @brief 发送 32bit 地址(A31~A0)。
* @param addr 线性地址。
*/
static void spi_flash_send_addr(uint32_t addr)
{
spi_flash_send_byte((uint8_t)((addr >> 24) & 0xFFu));
spi_flash_send_byte((uint8_t)((addr >> 16) & 0xFFu));
spi_flash_send_byte((uint8_t)((addr >> 8) & 0xFFu));
spi_flash_send_byte((uint8_t)(addr & 0xFFu));
}
/**
* @brief 进入 4-byte 地址模式(支持 32MB 地址空间访问)。
*/
static void spi_flash_enter_4byte_addr_mode(void)
{
SPI_FLASH_CS_LOW();
spi_flash_send_byte(EXFLASH_CMD_EN4B);
SPI_FLASH_CS_HIGH();
}
/**
* @brief 等待 DMA 收发双通道完成。
* @return 1 成功,0 超时。
*/
static uint8_t spi4_dma_wait_done(void)
{
uint32_t timeout = EXFLASH_DMA_WAIT_MAX_LOOP;
while (timeout-- > 0u) {
if ((s_spi4_dma_rx_done == 1u) && (s_spi4_dma_tx_done == 1u)) {
return 1u;
}
}
return 0u;
}
/**
* @brief 启动 SPI4 DMA 全双工传输(同时配置 RX/TX)。
* @param tx_buf TX 源地址。
* @param tx_inc TX 地址是否自增。
* @param rx_buf RX 目标地址。
* @param rx_inc RX 地址是否自增。
* @param length 传输字节数。
* @return 1 启动并完成成功,0 超时/失败。
*/
static uint8_t spi4_dma_transfer(uint8_t *tx_buf,
uint32_t tx_inc,
uint8_t *rx_buf,
uint32_t rx_inc,
uint16_t length)
{
dma_single_data_parameter_struct dma_init_struct;
if ((tx_buf == NULL) || (rx_buf == NULL) || (length == 0u)) {
return 0u;
}
s_spi4_dma_rx_done = 0u;
s_spi4_dma_tx_done = 0u;
dma_deinit(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH);
dma_deinit(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH);
dma_single_data_para_struct_init(&dma_init_struct);
/* DMA 读 TX 前清 cacheDMA 写 RX 后需要 invalidate cache */
if (tx_inc == DMA_MEMORY_INCREASE_ENABLE) {
dcache_clean_by_addr((uint32_t)tx_buf, (uint32_t)length);
}
if (rx_inc == DMA_MEMORY_INCREASE_ENABLE) {
dcache_invalidate_by_addr((uint32_t)rx_buf, (uint32_t)length);
}
/* RX: SPI4_RDATA -> memory */
dma_init_struct.request = DMA_REQUEST_SPI4_RX;
dma_init_struct.periph_addr = (uint32_t)&SPI_RDATA(SPI4);
dma_init_struct.periph_inc = DMA_MEMORY_INCREASE_DISABLE;
dma_init_struct.memory0_addr = (uint32_t)rx_buf;
dma_init_struct.memory_inc = rx_inc;
dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
dma_init_struct.circular_mode = DMA_CIRCULAR_MODE_DISABLE;
dma_init_struct.direction = DMA_PERIPH_TO_MEMORY;
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_init_struct.number = length;
dma_single_data_mode_init(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH, &dma_init_struct);
dma_interrupt_enable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH, DMA_INT_FTF);
/* TX: memory -> SPI4_TDATA */
dma_init_struct.request = DMA_REQUEST_SPI4_TX;
dma_init_struct.periph_addr = (uint32_t)&SPI_TDATA(SPI4);
dma_init_struct.periph_inc = DMA_MEMORY_INCREASE_DISABLE;
dma_init_struct.memory0_addr = (uint32_t)tx_buf;
dma_init_struct.memory_inc = tx_inc;
dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
dma_init_struct.circular_mode = DMA_CIRCULAR_MODE_DISABLE;
dma_init_struct.direction = DMA_MEMORY_TO_PERIPH;
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_init_struct.number = length;
dma_single_data_mode_init(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH, &dma_init_struct);
dma_interrupt_enable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH, DMA_INT_FTF);
spi_dma_enable(SPI4, SPI_DMA_RECEIVE);
spi_dma_enable(SPI4, SPI_DMA_TRANSMIT);
/* 显式启动一次主机传输,保证时钟输出 */
spi_master_transfer_start(SPI4, SPI_TRANS_START);
dma_channel_enable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH);
dma_channel_enable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH);
if (spi4_dma_wait_done() == 0u) {
spi_dma_disable(SPI4, SPI_DMA_RECEIVE);
spi_dma_disable(SPI4, SPI_DMA_TRANSMIT);
dma_channel_disable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH);
dma_channel_disable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH);
return 0u;
}
spi_dma_disable(SPI4, SPI_DMA_RECEIVE);
spi_dma_disable(SPI4, SPI_DMA_TRANSMIT);
dma_channel_disable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH);
dma_channel_disable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH);
/* DMA 写完 RX 后,CPU 读取前再次 invalidate,避免 cache 仍是旧数据 */
if (rx_inc == DMA_MEMORY_INCREASE_ENABLE) {
dcache_invalidate_by_addr((uint32_t)rx_buf, (uint32_t)length);
}
return 1u;
}
/**
* @brief 初始化 SPI4 与外部 Flash 片选 GPIO。
* @details
* - CS: PJ9(软件控制)
* - SCK: PK0MOSI: PJ10MISO: PJ11
* - SPI Mode08bitMSB first
*/
void spi_flash_init(void)
{
spi_parameter_struct spi_init_struct;
/* 1) 时钟使能 */
rcu_periph_clock_enable(RCU_GPIOJ);
rcu_periph_clock_enable(RCU_GPIOK);
rcu_periph_clock_enable(RCU_SPI4);
rcu_periph_clock_enable(RCU_DMA0);
rcu_periph_clock_enable(RCU_DMAMUX);
rcu_spi_clock_config(IDX_SPI4, RCU_SPISRC_APB2);
/* DMA 中断配置放在 DMA 时钟就绪后,避免早期访问 DMA 寄存器引发异常 */
nvic_irq_enable(DMA0_Channel1_IRQn, 4U, 0U);
nvic_irq_enable(DMA0_Channel2_IRQn, 4U, 0U);
/* 2) 片选脚初始化:默认拉高(未选中) */
gpio_mode_set(GPIOJ, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_9);
gpio_output_options_set(GPIOJ, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_9);
SPI_FLASH_CS_HIGH();
/* 3) SPI4 复用脚初始化 */
gpio_af_set(GPIOK, GPIO_AF_5, GPIO_PIN_0); /* SCK */
gpio_af_set(GPIOJ, GPIO_AF_5, GPIO_PIN_10 | GPIO_PIN_11); /* MOSI/MISO */
gpio_mode_set(GPIOK, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0);
gpio_output_options_set(GPIOK, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_0);
gpio_mode_set(GPIOJ, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOJ, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_11);
gpio_mode_set(GPIOJ, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
gpio_output_options_set(GPIOJ, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_10);
/* 4) SPI4 参数初始化 */
spi_i2s_deinit(SPI4);
spi_struct_para_init(&spi_init_struct);
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; /* 全双工 */
spi_init_struct.device_mode = SPI_MASTER; /* 主机 */
spi_init_struct.data_size = SPI_DATASIZE_8BIT; /* 8bit 帧 */
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; /* Mode 0 */
spi_init_struct.nss = SPI_NSS_SOFT; /* 软件 NSS */
spi_init_struct.prescale = SPI_PSC_32; /* 分频,保证稳定 */
spi_init_struct.endian = SPI_ENDIAN_MSB; /* 高位先发 */
spi_init(SPI4, &spi_init_struct);
/* 5) 使能 SPI 外设 */
spi_byte_access_enable(SPI4);
spi_nss_output_enable(SPI4);
spi_enable(SPI4);
/* 清理可能残留的 DMA 中断完成标志,防止首次传输误触发 */
dma_interrupt_flag_clear(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH, DMA_INT_FLAG_FTF);
dma_interrupt_flag_clear(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH, DMA_INT_FLAG_FTF);
/* 启用 4-byte 地址模式,支持访问 0x00000000~0x01FFFFFF32MB */
spi_flash_enter_4byte_addr_mode();
}
/**
* @brief 擦除指定地址所在扇区(4KB)。
* @param sector_addr 32bit 扇区地址(建议 4KB 对齐)。
*/
void spi_flash_sector_erase(uint32_t sector_addr)
{
spi_flash_write_enable();
SPI_FLASH_CS_LOW();
spi_flash_send_byte(EXFLASH_CMD_SE);
spi_flash_send_addr(sector_addr);
SPI_FLASH_CS_HIGH();
spi_flash_wait_for_write_end();
}
/**
* @brief 全片擦除。
* @warning 全片擦除耗时很长,仅在必要时调用。
*/
void spi_flash_bulk_erase(void)
{
spi_flash_write_enable();
SPI_FLASH_CS_LOW();
spi_flash_send_byte(EXFLASH_CMD_BE);
SPI_FLASH_CS_HIGH();
spi_flash_wait_for_write_end();
}
/**
* @brief 页内编程(不跨页)。
* @param pbuffer 写入源缓冲区。
* @param write_addr 写起始地址。
* @param num_byte_to_write 页内写入字节数(建议 <=256 且不跨页)。
*/
void spi_flash_page_write(uint8_t *pbuffer, uint32_t write_addr, uint16_t num_byte_to_write)
{
if ((pbuffer == NULL) || (num_byte_to_write == 0u)) {
return;
}
spi_flash_write_enable();
SPI_FLASH_CS_LOW();
spi_flash_send_byte(EXFLASH_CMD_WRITE);
spi_flash_send_addr(write_addr);
(void)spi4_dma_transfer(pbuffer,
DMA_MEMORY_INCREASE_ENABLE,
&s_spi4_dma_dummy_rx,
DMA_MEMORY_INCREASE_DISABLE,
num_byte_to_write);
SPI_FLASH_CS_HIGH();
spi_flash_wait_for_write_end();
}
/**
* @brief 连续写(自动按页拆分)。
* @param pbuffer 写入源缓冲区。
* @param write_addr 写起始地址。
* @param num_byte_to_write 写入总字节数。
* @details
* 原始实现将分页计数变量定义为 uint8_t,在数据量 >255 页时会溢出;
* 此处统一使用 uint32_t,避免分页计算错误。
*/
void spi_flash_buffer_write(uint8_t *pbuffer, uint32_t write_addr, uint16_t num_byte_to_write)
{
uint32_t addr_offset;
uint32_t first_page_space;
uint32_t remain;
uint32_t page_bytes;
if ((pbuffer == NULL) || (num_byte_to_write == 0u)) {
return;
}
addr_offset = write_addr % SPI_FLASH_PAGE_SIZE;
first_page_space = SPI_FLASH_PAGE_SIZE - addr_offset;
remain = num_byte_to_write;
/* 若首地址非页对齐,先补齐当前页 */
if ((addr_offset != 0u) && (remain != 0u)) {
page_bytes = (remain < first_page_space) ? remain : first_page_space;
spi_flash_page_write(pbuffer, write_addr, (uint16_t)page_bytes);
write_addr += page_bytes;
pbuffer += page_bytes;
remain -= page_bytes;
}
/* 整页写 */
while (remain >= SPI_FLASH_PAGE_SIZE) {
spi_flash_page_write(pbuffer, write_addr, SPI_FLASH_PAGE_SIZE);
write_addr += SPI_FLASH_PAGE_SIZE;
pbuffer += SPI_FLASH_PAGE_SIZE;
remain -= SPI_FLASH_PAGE_SIZE;
}
/* 尾包写 */
if (remain > 0u) {
spi_flash_page_write(pbuffer, write_addr, (uint16_t)remain);
}
}
/**
* @brief 连续读取数据。
* @param pbuffer 读出目标缓冲区。
* @param read_addr 读取起始地址。
* @param num_byte_to_read 读取字节数。
*/
void spi_flash_buffer_read(uint8_t *pbuffer, uint32_t read_addr, uint16_t num_byte_to_read)
{
if ((pbuffer == NULL) || (num_byte_to_read == 0u)) {
return;
}
SPI_FLASH_CS_LOW();
spi_flash_send_byte(EXFLASH_CMD_READ);
spi_flash_send_addr(read_addr);
(void)spi4_dma_transfer(&s_spi4_dma_dummy_tx,
DMA_MEMORY_INCREASE_DISABLE,
pbuffer,
DMA_MEMORY_INCREASE_ENABLE,
num_byte_to_read);
SPI_FLASH_CS_HIGH();
}
/**
* @brief 读取 JEDEC ID(三字节)。
* @return 24bit IDManufacturer[23:16] | MemoryType[15:8] | Capacity[7:0]。
*/
uint32_t spi_flash_read_id(void)
{
uint32_t id0;
uint32_t id1;
uint32_t id2;
SPI_FLASH_CS_LOW();
spi_flash_send_byte(EXFLASH_CMD_RDID);
id0 = spi_flash_send_byte(EXFLASH_DUMMY_BYTE);
id1 = spi_flash_send_byte(EXFLASH_DUMMY_BYTE);
id2 = spi_flash_send_byte(EXFLASH_DUMMY_BYTE);
SPI_FLASH_CS_HIGH();
return (id0 << 16) | (id1 << 8) | id2;
}
/**
* @brief 发起连续读序列(保持片选有效)。
* @param read_addr 读取起始地址。
*/
void spi_flash_start_read_sequence(uint32_t read_addr)
{
SPI_FLASH_CS_LOW();
spi_flash_send_byte(EXFLASH_CMD_READ);
spi_flash_send_addr(read_addr);
}
/**
* @brief 在连续读序列中读取 1 字节。
* @return 读到的数据字节。
*/
uint8_t spi_flash_read_byte(void)
{
return spi_flash_send_byte(EXFLASH_DUMMY_BYTE);
}
/**
* @brief SPI4 全双工收发一个字节。
* @param byte 发送字节。
* @return 同步接收字节。
*/
uint8_t spi_flash_send_byte(uint8_t byte)
{
uint32_t timeout = 0x00FFFFFFu;
/* GD32H7 SPI 主机在部分场景需要显式启动传输,否则时钟不输出,RP 永远不置位 */
spi_master_transfer_start(SPI4, SPI_TRANS_START);
while ((RESET == spi_i2s_flag_get(SPI4, SPI_FLAG_TP)) && (timeout-- > 0u)) {
/* 等待发送缓冲区可写 */
}
if (timeout == 0u) {
return 0xFFu;
}
spi_i2s_data_transmit(SPI4, byte);
timeout = 0x00FFFFFFu;
while ((RESET == spi_i2s_flag_get(SPI4, SPI_FLAG_RP)) && (timeout-- > 0u)) {
/* 等待接收缓冲区有数据 */
}
if (timeout == 0u) {
return 0xFFu;
}
return (uint8_t)spi_i2s_data_receive(SPI4);
}
/**
* @brief SPI4 全双工收发半字。
* @param half_word 发送半字。
* @return 同步接收半字。
*/
uint16_t spi_flash_send_halfword(uint16_t half_word)
{
uint32_t timeout = 0x00FFFFFFu;
spi_master_transfer_start(SPI4, SPI_TRANS_START);
while ((RESET == spi_i2s_flag_get(SPI4, SPI_FLAG_TP)) && (timeout-- > 0u)) {
/* 等待发送缓冲区可写 */
}
if (timeout == 0u) {
return 0xFFFFu;
}
spi_i2s_data_transmit(SPI4, half_word);
timeout = 0x00FFFFFFu;
while ((RESET == spi_i2s_flag_get(SPI4, SPI_FLAG_RP)) && (timeout-- > 0u)) {
/* 等待接收缓冲区有数据 */
}
if (timeout == 0u) {
return 0xFFFFu;
}
return (uint16_t)spi_i2s_data_receive(SPI4);
}
/**
* @brief 发送 Write Enable 指令(WREN)。
*/
void spi_flash_write_enable(void)
{
SPI_FLASH_CS_LOW();
spi_flash_send_byte(EXFLASH_CMD_WREN);
SPI_FLASH_CS_HIGH();
}
/**
* @brief 等待写/擦操作结束(轮询 SR.WIP)。
*/
void spi_flash_wait_for_write_end(void)
{
uint8_t flash_status;
SPI_FLASH_CS_LOW();
spi_flash_send_byte(EXFLASH_CMD_RDSR);
do {
flash_status = spi_flash_send_byte(EXFLASH_DUMMY_BYTE);
} while ((flash_status & EXFLASH_SR_WIP_MASK) != 0u);
SPI_FLASH_CS_HIGH();
}
/**
* @brief DMA0 Channel1 IRQSPI4 RX 完成中断。
*/
void DMA0_Channel1_IRQHandler(void)
{
if (SET == dma_interrupt_flag_get(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH, DMA_INT_FLAG_FTF)) {
dma_interrupt_flag_clear(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH, DMA_INT_FLAG_FTF);
s_spi4_dma_rx_done = 1u;
}
}
/**
* @brief DMA0 Channel2 IRQSPI4 TX 完成中断。
*/
void DMA0_Channel2_IRQHandler(void)
{
if (SET == dma_interrupt_flag_get(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH, DMA_INT_FLAG_FTF)) {
dma_interrupt_flag_clear(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH, DMA_INT_FLAG_FTF);
s_spi4_dma_tx_done = 1u;
}
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef EXFLASH_SPI4_GD25QXX_H
#define EXFLASH_SPI4_GD25QXX_H
#include "gd32h7xx.h"
/* GD25QXX 页大小:每页 256 字节 */
#define SPI_FLASH_PAGE_SIZE (0x100u)
/* 片选脚由 PJ9 软件控制 */
#define SPI_FLASH_CS_LOW() gpio_bit_reset(GPIOJ, GPIO_PIN_9)
#define SPI_FLASH_CS_HIGH() gpio_bit_set(GPIOJ, GPIO_PIN_9)
/* 初始化 SPI4 与片选 GPIO */
void spi_flash_init(void);
/* 擦除 4KB 扇区 */
void spi_flash_sector_erase(uint32_t sector_addr);
/* 全片擦除(耗时较长) */
void spi_flash_bulk_erase(void);
/* 页内写(不处理跨页) */
void spi_flash_page_write(uint8_t *pbuffer, uint32_t write_addr, uint16_t num_byte_to_write);
/* 连续写(自动处理跨页) */
void spi_flash_buffer_write(uint8_t *pbuffer, uint32_t write_addr, uint16_t num_byte_to_write);
/* 连续读 */
void spi_flash_buffer_read(uint8_t *pbuffer, uint32_t read_addr, uint16_t num_byte_to_read);
/* 读取 JEDEC ID */
uint32_t spi_flash_read_id(void);
/* 发起连续读命令序列(随后可重复 spi_flash_read_byte */
void spi_flash_start_read_sequence(uint32_t read_addr);
/* 连续读阶段读取 1 字节 */
uint8_t spi_flash_read_byte(void);
/* SPI 收发 1 字节 */
uint8_t spi_flash_send_byte(uint8_t byte);
/* SPI 收发 1 半字 */
uint16_t spi_flash_send_halfword(uint16_t half_word);
/* 置写使能(WREN */
void spi_flash_write_enable(void);
/* 轮询 WIP 位,等待写/擦完成 */
void spi_flash_wait_for_write_end(void);
#endif
@@ -0,0 +1,75 @@
# externalflash 模块说明(SPI4 外部 Flash
> 本文档描述 `CCU621_M/BSP/externalflash` 模块的职责、接口、初始化位置以及 DMA + D-Cache 场景下的使用注意事项。
[返回主说明](./../../README.md)
## 1. 模块组成
- **底层驱动(SPI4 + GD25QXX**
- `exflash_spi4_gd25qxx.c`
- `exflash_spi4_gd25qxx.h`
- **统一对外接口(业务层推荐使用)**
- `flash_external_data.c`
- `flash_external_data.h`
## 2. 初始化入口
- 系统硬件初始化入口:`BSP/sys_drv_init.c`
- `v_sys_hardware_init()` 中调用 `v_flash_dataflash_init()`,完成 SPI4/GPIO/DMA 初始化。
> 约束:业务层/测试层不要重复 init,避免初始化时序与资源重复配置。
## 3. 推荐对外接口(统一接口)
业务层请优先使用以下接口(位于 `flash_external_data.c/.h`):
- `U8_T v_flash_dataflash_init(void)`
- `U8_T s32_flash_dataflash_write(U32_T addr, U8_T *buf, U32_T len)`
- `U8_T s32_flash_dataflash_read(U32_T addr, U8_T *buf, U32_T len)`
- `U8_T s32_flash_dataflash_erase_sector(U32_T addr)`
- 兼容旧命名:`unsigned int SPI_flsh_ReadID(void)`
### 3.1 地址规划(摘自 `flash_external_data.h`
- 全空间(4-byte 地址模式):`0x00000000 ~ 0x01FFFFFF`32MB
- OTA 区:`0x00000000 ~ 0x001FFFFF`
- 参数区:`0x210000 ~ ...`
- 业务记录区:`0x220000 ~ ...`
- FatFs 映射区:`0xA00000 ~ 0x01FFFFFF`
> 说明:以上为“线性地址”规划,调用统一接口时直接传该地址即可。
## 4. DMA + D-Cache 使用注意事项(非常重要)
当前 SPI4 外部 Flash 读写采用 **DMA** 方式提升性能;在 Cortex-M7 **开启 D-Cache** 的情况下,DMA 与 CPU 会出现数据一致性问题:
- DMA 写入内存后,CPU 可能仍读取到旧 cache 数据;
- DMA 从内存读取 TX 数据时,可能读不到 CPU 刚写入但未 clean 的 cache 内容。
### 4.1 当前工程的处理策略
- 底层 SPI4 DMA 传输在驱动内部做了 D-Cache clean/invalidate(按 32B cache line 对齐)。
- 统一接口 `s32_flash_dataflash_read/write` 额外做了一层保护:
- 如果业务传入的 `buf` **不是 32B 对齐**,内部会自动使用 **对齐 bounce buffer** 完成 DMA 传输,再 `memcpy` 到业务缓冲。
### 4.2 业务层建议
- 业务层可直接使用 `s32_flash_dataflash_read/write`,无需自行对齐或自行做 cache 维护。
- 若业务存在高频读写,可考虑在模块内复用一块“32B 对齐的工作缓冲”,减少 malloc/free 次数(后续可优化)。
## 5. 测试入口
- `app/app_init/app_test.c`
- 周期执行 flash 擦写读回校验
- 输出擦除/读写耗时与速度统计
- 新增 `u8_app_test_flash_32m_stride_rw()`:以 1MB 步长扫描 32MB 地址空间做擦写读回校验
## 6. 常见问题排查
- **读回校验失败**
- 首先确认是否走统一接口(避免绕过 bounce buffer
- 其次检查 D-Cache 是否开启、MPU 区域属性是否覆盖了 DMA buffer
- **卡死在 SPI_FLAG_RP 等待**
- 通常表示 SPI 主机传输未启动或时钟未输出,需要检查 `spi_master_transfer_start` 是否调用、片选是否正确、外设时钟是否使能
+149
View File
@@ -0,0 +1,149 @@
#include "flash_external_data.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "app_init/app_init.h"
#include "exflash_spi4_gd25qxx.h"
#include "FreeRTOS.h"
#include "portable.h"
#define FLASH_IO_ALIGN (32u)
#define FLASH_IO_GUARD_SIZE (32u)
static uint8_t *align_up_u8(uint8_t *p, uint32_t align)
{
uintptr_t v = (uintptr_t)p;
v = (v + (align - 1U)) & ~(uintptr_t)(align - 1U);
return (uint8_t *)v;
}
static uint8_t need_bounce_buf(const void *p)
{
return (((uintptr_t)p) & (FLASH_IO_ALIGN - 1U)) ? 1U : 0U;
}
/**
* @brief 外部 Flash 初始化(统一接口)。
* @return 0 成功。
*/
U8_T v_flash_dataflash_init(void)
{
spi_flash_init();
printf("[EXFLASH] JEDEC ID: 0x%06X\r\n", (unsigned int)spi_flash_read_id());
return 0U;
}
/**
* @brief 外部 Flash 写接口(统一接口)。
* @param addr 24bit 线性地址。
* @param buf 写入数据缓冲区。
* @param len 写入长度(字节)。
* @return 0 成功,1 参数错误,2 长度超驱动接口上限。
*/
U8_T s32_flash_dataflash_write(U32_T addr, U8_T *buf, U32_T len)
{
uint8_t *raw;
uint8_t *aligned;
uint32_t alloc_size;
if ((buf == NULL) || (len == 0U)) {
return 1U;
}
/* 底层驱动 write 接口长度参数为 uint16_t。 */
if (len > 0xFFFFu) {
return 2U;
}
/* 若用户缓冲未按 32B 对齐,使用 bounce buffer,避免 DMA/DCache 维护影响堆头或触发一致性问题 */
if (need_bounce_buf(buf) == 0U) {
spi_flash_buffer_write(buf, addr, (U16_T)len);
return 0U;
}
alloc_size = len + FLASH_IO_ALIGN + FLASH_IO_GUARD_SIZE;
raw = (uint8_t *)pvPortMalloc(alloc_size);
if (raw == NULL) {
return 3U;
}
aligned = align_up_u8(raw, FLASH_IO_ALIGN);
memcpy(aligned, buf, len);
spi_flash_buffer_write(aligned, addr, (U16_T)len);
vPortFree(raw);
return 0U;
}
/**
* @brief 外部 Flash 读接口(统一接口)。
* @param addr 24bit 线性地址。
* @param buf 读出数据缓冲区。
* @param len 读取长度(字节)。
* @return 0 成功,1 参数错误,2 长度超驱动接口上限。
*/
U8_T s32_flash_dataflash_read(U32_T addr, U8_T *buf, U32_T len)
{
uint8_t *raw;
uint8_t *aligned;
uint32_t alloc_size;
if ((buf == NULL) || (len == 0U)) {
return 1U;
}
if (len > 0xFFFFu) {
return 2U;
}
if (need_bounce_buf(buf) == 0U) {
spi_flash_buffer_read(buf, addr, (U16_T)len);
return 0U;
}
alloc_size = len + FLASH_IO_ALIGN + FLASH_IO_GUARD_SIZE;
raw = (uint8_t *)pvPortMalloc(alloc_size);
if (raw == NULL) {
return 3U;
}
aligned = align_up_u8(raw, FLASH_IO_ALIGN);
spi_flash_buffer_read(aligned, addr, (U16_T)len);
memcpy(buf, aligned, len);
vPortFree(raw);
return 0U;
}
/**
* @brief 擦除指定地址所在扇区。
* @param addr 扇区起始地址(建议 4KB 对齐)。
* @return 0 成功。
*/
U8_T s32_flash_dataflash_erase_sector(U32_T addr)
{
spi_flash_sector_erase(addr);
return 0U;
}
/**
* @brief 读取外部 Flash JEDEC ID(兼容旧命名)。
*/
unsigned int SPI_flsh_ReadID(void)
{
return (unsigned int)spi_flash_read_id();
}
/**
* @brief 擦除 FatFs 映射区全部扇区。
*/
void v_flash_dataflash_fatfs_esaes_sectot(void)
{
U32_T i;
U32_T sector_addr;
for (i = 0U; i < FATFS_TOTAL_SECTORS; i++) {
sector_addr = FATFS_PHYSICAL_START_ADDR + (i * SPI_SECTOR_SIZE);
printf("SPI flash erase sector: 0x%08X\r\n", (unsigned int)sector_addr);
spi_flash_sector_erase(sector_addr);
mSleep(1U);
}
}
+100
View File
@@ -0,0 +1,100 @@
#ifndef FLASH_EXTERNAL_DATA_H
#define FLASH_EXTERNAL_DATA_H
#include "publicdata/type.h"
#include "meter_calculate/meter_calculate_impl.h"
/* ============================================================================
* 外部 Flash 容量与地址说明
*
* 当前上电识别 JEDEC ID: 0xC84019
* - Manufacturer: 0xC8GigaDevice
* - Capacity code: 0x19(典型为 256Mbit = 32MB
*
* 当前底层驱动已启用 4-byte 地址模式(4 字节地址):
* - 可直接访问线性地址范围:0x00000000 ~ 0x01FFFFFF32MB
* - 总容量:0x02000000 bytes
* ============================================================================ */
#define EXFLASH_ADDR_MIN (0x00000000u)
#define EXFLASH_ADDR_MAX (0x01FFFFFFu)
#define EXFLASH_TOTAL_SIZE (0x02000000u)
/* SPI NOR Flash 物理扇区大小(字节) */
#define SPI_SECTOR_SIZE (4096u)
#ifndef RECORD_ZERO_SAVE
#define RECORD_ZERO_SAVE (0x7FFFu)
#endif
#ifndef RECORD_HEADER
#define RECORD_HEADER (0x55u)
#endif
/* OTA 区(升级保留区) */
#define UPGRADE_START_ADDR (0x00000000u)
#define UPGRADE_END_ADDR (0x001FFFFFu)
#define UPGRADE_TOTAL_SIZE (UPGRADE_END_ADDR - UPGRADE_START_ADDR + 1u)
/* 配置区 */
#define DATAFLASH_VAR_CFG_DATA1_ADDR (0x210000u)
#define DATAFLASH_VAR_CFG_DATA2_ADDR (0x211000u)
#define DATAFLASH_VAR_CFG_DATA3_ADDR (0x212000u)
#define DATAFLASH_FIX_CFG_DATA1_ADDR (0x213000u)
#define DATAFLASH_FIX_CFG_DATA2_ADDR (0x214000u)
#define DATAFLASH_FIX_CFG_DATA3_ADDR (0x215000u)
#define DATAFLASH_OCPP_CFG_DATA_ADDR (0x216000u)
/* 故障、卡号、历史记录区 */
#define DATAFLASH_FAULT_ADDR (0x220000u)
#define DATAFLASH_FAULT_SECTOR_CNT (2u)
#define DATAFLASH_FAULT_END_ADDR (DATAFLASH_FAULT_ADDR + (DATAFLASH_FAULT_SECTOR_CNT * SPI_SECTOR_SIZE) - 1u)
#define DATAFLASH_OCPP_MV_OFFLINE_ADDR (0x2C0000u)
#define DATAFLASH_OCPP_MV_OFFLINE_HALF_SIZE (0x8000u)
#define DATAFLASH_OCPP_MV_OFFLINE_SECTOR_CNT (16u)
#define DATAFLASH_OCPP_MV_OFFLINE_TOTAL_SIZE (DATAFLASH_OCPP_MV_OFFLINE_HALF_SIZE * 2u)
#define DATAFLASH_CARD_ADDR (0x222000u)
#define DATAFLASH_CARD_SECTOR_CNT (7u)
#define DATAFLASH_CARD_END_ADDR (DATAFLASH_CARD_ADDR + (DATAFLASH_CARD_SECTOR_CNT * SPI_SECTOR_SIZE) - 1u)
#define DATAFLASH_HIS_RECORD_ADDR (0x230000u)
#define DATAFLASH_HIS_RECORD_END_ADDR (0x2BFFFFu)
#define DATAFLASH_HIS_RECORD_SIZE (DATAFLASH_HIS_RECORD_END_ADDR - DATAFLASH_HIS_RECORD_ADDR + 1u)
#define DATAFLASH_HIS_RECORD_SECTOR_CNT (DATAFLASH_HIS_RECORD_SIZE / SPI_SECTOR_SIZE)
/* Charge-order storage layout (compatible with migrated flash_file_mgr module) */
#ifndef CHG_ORDER_RECORD_SIZE
#define CHG_ORDER_RECORD_SIZE (sizeof(S_LOG_DATA))
#endif
#ifndef CHG_ORDER_FULL_RECORD_SIZE
#define CHG_ORDER_FULL_RECORD_SIZE (1u + CHG_ORDER_RECORD_SIZE + 1u)
#endif
#ifndef CHG_ORDER_PER_SECTOR_CNT
#define CHG_ORDER_PER_SECTOR_CNT (SPI_SECTOR_SIZE / CHG_ORDER_FULL_RECORD_SIZE)
#endif
#ifndef CHG_ORDER_MAX_CNT
#define CHG_ORDER_MAX_CNT (200u)
#endif
#ifndef TEMP_CHG_RECORD_SIZE
#define TEMP_CHG_RECORD_SIZE (sizeof(S_LOG_DATA))
#endif
/* FatFs 映射区 */
#define FATFS_PHYSICAL_START_ADDR (0xA00000u)
#define FATFS_PHYSICAL_STOP_ADDR (EXFLASH_ADDR_MAX)
#define FATFS_PARTITION_SIZE (FATFS_PHYSICAL_STOP_ADDR - FATFS_PHYSICAL_START_ADDR + 1u)
#define FATFS_TOTAL_SECTORS (FATFS_PARTITION_SIZE / SPI_SECTOR_SIZE)
/* 统一对外接口:0=成功,非0=失败 */
U8_T v_flash_dataflash_init(void);
U8_T s32_flash_dataflash_write(U32_T addr, U8_T *buf, U32_T len);
U8_T s32_flash_dataflash_read(U32_T addr, U8_T *buf, U32_T len);
U8_T s32_flash_dataflash_erase_sector(U32_T addr);
/* 兼容旧接口命名 */
unsigned int SPI_flsh_ReadID(void);
void v_flash_dataflash_fatfs_esaes_sectot(void);
#endif
+189
View File
@@ -0,0 +1,189 @@
/**
* @file flash_upgrade.c
* @brief 外置 Flash 固件升级区读写(与 CCU601E_D / CCU621_IAP 布局一致)
*/
#include "flash_upgrade.h"
#include "app_init/app_init.h"
#include "wdt_task/wdt_task.h"
#include "exflash_spi4_gd25qxx.h"
#include <stdio.h>
#include <string.h>
extern U8_T s32_flash_dataflash_write(U32_T addr, U8_T *buf, U32_T len);
extern U8_T s32_flash_dataflash_read(U32_T addr, U8_T *buf, U32_T len);
extern U8_T s32_flash_dataflash_erase_sector(U32_T addr);
/* 0: 按包擦扇区;1: upgrade_erase_program_area 已整体擦除 */
static int g_upgrade_program_pre_erased = 0;
static uint8_t s_upgrade_header_buf[sizeof(upgrade_header_t)]
__attribute__((aligned(32)));
__attribute__((weak)) void upgrade_erase_yield(void)
{
}
int upgrade_init(void)
{
upgrade_header_t header;
if (upgrade_read_header(&header) == 0) {
if (header.upgrade_flag == UPGRADE_FLAG_NEED_UP) {
return 1;
}
if (header.upgrade_flag == UPGRADE_FLAG_DONE) {
return 2;
}
}
g_upgrade_program_pre_erased = 0;
return 0;
}
int upgrade_write_header(const upgrade_header_t *header)
{
if (header == NULL) {
return -1;
}
spi_flash_wait_for_write_end();
v_wdt_dog_toggle();
s32_flash_dataflash_erase_sector(HEADER_SECTOR_ADDR);
v_wdt_dog_toggle();
memcpy(s_upgrade_header_buf, header, sizeof(upgrade_header_t));
if (s32_flash_dataflash_write(HEADER_SECTOR_ADDR, s_upgrade_header_buf,
sizeof(upgrade_header_t)) != 0U) {
printf("[FW_UPGRADE] header write failed\r\n");
return -1;
}
spi_flash_wait_for_write_end();
v_wdt_dog_toggle();
return 0;
}
int upgrade_read_header(upgrade_header_t *header)
{
if (header == NULL) {
return -1;
}
if (s32_flash_dataflash_read(HEADER_SECTOR_ADDR, (U8_T *)header, sizeof(upgrade_header_t)) != 0U) {
return -1;
}
return 0;
}
int upgrade_success_update_header(upgrade_header_t *header)
{
return upgrade_write_header(header);
}
int upgrade_write_packet(uint32_t packet_index, const uint8_t *data, uint32_t data_len)
{
uint32_t packet_addr;
uint32_t sector_start;
if ((data == NULL) || (data_len == 0U) || (data_len > PACKET_SIZE)) {
return -1;
}
packet_addr = PROGRAM_START_ADDR + (packet_index * PACKET_SIZE);
if (packet_addr + data_len > UPGRADE_END_ADDR) {
return -1;
}
if (!g_upgrade_program_pre_erased) {
sector_start = (packet_addr / SECTOR_SIZE) * SECTOR_SIZE;
if ((packet_index * PACKET_SIZE) % SECTOR_SIZE == 0U) {
s32_flash_dataflash_erase_sector(sector_start);
v_wdt_dog_toggle();
mSleep(1);
}
}
if (s32_flash_dataflash_write(packet_addr, (U8_T *)data, data_len) != 0U) {
return -1;
}
return 0;
}
int upgrade_read_packet(uint32_t packet_index, uint8_t *data, uint32_t *data_len)
{
uint32_t packet_addr;
uint32_t max_read_size;
if ((data == NULL) || (data_len == NULL)) {
return -1;
}
packet_addr = PROGRAM_START_ADDR + (packet_index * PACKET_SIZE);
if (packet_addr > UPGRADE_END_ADDR) {
return -1;
}
if (s32_flash_dataflash_read(packet_addr, data, PACKET_SIZE) != 0U) {
return -1;
}
max_read_size = UPGRADE_END_ADDR - packet_addr + 1U;
if (max_read_size < PACKET_SIZE) {
*data_len = max_read_size;
} else {
*data_len = PACKET_SIZE;
}
return 0;
}
int upgrade_erase_program_area(void)
{
return upgrade_erase_program_area_for_size(PROGRAM_MAX_SIZE);
}
int upgrade_erase_program_area_for_size(uint32_t firmware_bytes)
{
uint32_t sector_addr;
uint32_t last_byte;
uint32_t last_sector;
if (firmware_bytes == 0U) {
return -1;
}
if (firmware_bytes > PROGRAM_MAX_SIZE) {
return -1;
}
last_byte = PROGRAM_START_ADDR + firmware_bytes - 1U;
if (last_byte > UPGRADE_END_ADDR) {
return -1;
}
last_sector = (last_byte / SECTOR_SIZE) * SECTOR_SIZE;
printf("[FW_UPGRADE] erase start size=%lu sectors=0x%08lX..0x%08lX\r\n",
(unsigned long)firmware_bytes,
(unsigned long)PROGRAM_START_ADDR,
(unsigned long)last_sector);
for (sector_addr = PROGRAM_START_ADDR; sector_addr <= last_sector; sector_addr += SECTOR_SIZE) {
s32_flash_dataflash_erase_sector(sector_addr);
v_wdt_dog_toggle();
mSleep(1);
upgrade_erase_yield();
if (((sector_addr - PROGRAM_START_ADDR) / SECTOR_SIZE) % 10U == 9U) {
printf("[FW_UPGRADE] erase progress at 0x%08lX\r\n",
(unsigned long)sector_addr);
}
}
printf("[FW_UPGRADE] erase done\r\n");
g_upgrade_program_pre_erased = 1;
return 0;
}
+59
View File
@@ -0,0 +1,59 @@
/**
* @file flash_upgrade.h
* @brief 固件升级兼容头文件(整合自 CCU601E_D BSP/spi_Flash/flash_upgrade.h
*
* CCU621_M 平台:升级相关常量、类型与接口与 CCU601E_D 保持一致。
*/
#ifndef __FLASH_UPGRADE_H__
#define __FLASH_UPGRADE_H__
#include <stdint.h>
#include "main.h"
#include "externalflash/flash_external_data.h"
/* 扇区大小 (4KB) */
#define SECTOR_SIZE 4096
/* 数据包大小 */
#define PACKET_SIZE (1024)
/* 头部信息扇区 (第一个扇区) */
#define HEADER_SECTOR_ADDR UPGRADE_START_ADDR
#define HEADER_SECTOR_SIZE SECTOR_SIZE
/* 程序数据起始地址 (第二个扇区开始) */
#define PROGRAM_START_ADDR (UPGRADE_START_ADDR + HEADER_SECTOR_SIZE)
#define PROGRAM_MAX_SIZE (UPGRADE_TOTAL_SIZE - HEADER_SECTOR_SIZE)
/* 升级标志定义 */
#define UPGRADE_FLAG_IDLE 0x00
#define UPGRADE_FLAG_NEED_UP 0x55AA
#define UPGRADE_FLAG_DONE 0xAA55
#define UPGRADE_FLAG_ERROR 0xEEFF
/* 升级头部信息结构 */
#pragma pack(push, 1)
typedef struct {
uint16_t upgrade_flag;
uint32_t file_size;
uint32_t packet_count;
uint32_t crc32;
uint32_t timestamp;
uint8_t version[16];
uint8_t reserved[32];
} upgrade_header_t;
#pragma pack(pop)
/* 函数声明 */
int upgrade_init(void);
int upgrade_read_header(upgrade_header_t *header);
int upgrade_write_packet(uint32_t packet_index, const uint8_t *data, uint32_t data_len);
int upgrade_read_packet(uint32_t packet_index, uint8_t *data, uint32_t *data_len);
int upgrade_success_update_header(upgrade_header_t *header);
int upgrade_erase_program_area(void);
int upgrade_erase_program_area_for_size(uint32_t firmware_bytes);
/** 擦除循环中让出 CPU,读 BLE/TCP 入站(默认 weak 空实现,tcp_server 覆盖) */
void upgrade_erase_yield(void);
#endif /* __FLASH_UPGRADE_H__ */
+732
View File
@@ -0,0 +1,732 @@
/**
* @file card_flash_impl.c
* @brief 卡号白名单:EEPROM `S_CARD_MNG` + SPI Flash 定长槽(48B/槽,与 fault 相同的帧头+CRC 低字节)
*
* 单槽布局:RECORD_HEADER + CARD_FLASH_BODY_T(46) + crc8
* 写任意槽需对该槽所在扇区做读-合并-擦除-整扇区写回(NOR Flash 不可原地改已写位)。
*/
#include "publicdata/public_define.h"
#include "publicdata/publicdata.h"
#include "app_fatfs/fatfs_card.h"
#include "app_fatfs/fatfs_init.h"
#include "card_flash_impl.h"
#include "eeprom/fm24cl16.h"
#include "externalflash/flash_external_data.h"
#include <string.h>
/* 动态内存抽象:默认使用 FreeRTOS 堆接口,可按需覆盖 */
#ifndef FLASH_MGR_MALLOC
#define FLASH_MGR_MALLOC(sz) pvPortMalloc((sz))
#endif
#ifndef FLASH_MGR_FREE
#define FLASH_MGR_FREE(ptr) vPortFree((ptr))
#endif
#if FATFS_EN
#if 1
#define CARD_FLASH_MAX_CNT CARD_MAX_COUNT
#define CARD_BODY_SIZE 46u
#define CARD_SLOT_SIZE 48u
#define CARD_SLOTS_PER_SECTOR ((U32_T)SPI_SECTOR_SIZE / (U32_T)CARD_SLOT_SIZE)
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
_Static_assert(sizeof(CARD_FLASH_BODY_T) == 46u, "CARD_FLASH_BODY_T must be 46 bytes");
#endif
static bool g_card_init;
static S_CARD_MNG s_cardMng;
/**
* @brief z_strnlen。
*/
static size_t z_strnlen(const char *s, size_t max)
{
size_t i;
for (i = 0; i < max && s[i] != '\0'; i++) {
}
return i;
}
/**
* @brief v_pack_card_num。
*/
static void v_pack_card_num(const char *src, U8_T dst[32])
{
memset(dst, 0, 32u);
if (src == NULL) {
return;
}
size_t n = z_strnlen(src, (size_t)CARD_NUM_MAX_LEN);
memcpy(dst, src, n);
}
/**
* @brief b_card_num_eq。
*/
static bool b_card_num_eq(const char *src, const U8_T dst[32])
{
U8_T a[32];
v_pack_card_num(src, a);
return memcmp(a, dst, 32u) == 0;
}
/**
* @brief v_read_eeprom_card_mng。
*/
static void v_read_eeprom_card_mng(S_CARD_MNG *m)
{
int i;
S_CARD_MNG tmp;
for (i = 0; i < 3; i++) {
if (eeprom_read(EEPROM_ADDR_CARD_MNG, (U8_T *)&tmp, (S32_T)sizeof(S_CARD_MNG)) == HAL_OK) {
m->u16_head = tmp.u16_head;
m->u16_cardCnt = tmp.u16_cardCnt;
m->u16_reserved = tmp.u16_reserved;
return;
}
}
m->u16_head = 0;
m->u16_cardCnt = 0;
m->u16_reserved = 0;
}
/**
* @brief v_write_eeprom_card_mng。
*/
static void v_write_eeprom_card_mng(const S_CARD_MNG *m)
{
S_CARD_MNG out;
out.u16_head = CARD_MNG_HEAD;
out.u16_cardCnt = m->u16_cardCnt;
out.u16_reserved = m->u16_reserved;
eeprom_write(EEPROM_ADDR_CARD_MNG, (U8_T *)&out, (S32_T)sizeof(S_CARD_MNG));
}
/**
* @brief u32_slot_addr。
*/
static U32_T u32_slot_addr(U16_T slot)
{
U32_T sec = (U32_T)slot / CARD_SLOTS_PER_SECTOR;
U32_T j = (U32_T)slot % CARD_SLOTS_PER_SECTOR;
return (U32_T)DATAFLASH_CARD_ADDR + sec * (U32_T)SPI_SECTOR_SIZE + j * (U32_T)CARD_SLOT_SIZE;
}
/**
* @brief v_read_slot_raw。
*/
static void v_read_slot_raw(U16_T slot, U8_T *out)
{
(void)s32_flash_dataflash_read(u32_slot_addr(slot), out, CARD_SLOT_SIZE);
}
/**
* @brief b_slot_buf_valid。
*/
static bool b_slot_buf_valid(const U8_T *buf)
{
U8_T crc;
if (buf[0] != RECORD_HEADER) {
return false;
}
crc = (U8_T)u16_crc_checksum((U8_T *)(buf + 1), (U16_T)CARD_BODY_SIZE);
return buf[CARD_SLOT_SIZE - 1u] == crc;
}
/**
* @brief v_body_to_card_info。
*/
static void v_body_to_card_info(const CARD_FLASH_BODY_T *b, CardInfo *ci)
{
uint32_t ct;
uint32_t lt;
memset(ci, 0, sizeof(*ci));
memcpy(ci->card_num, b->card_num, sizeof(b->card_num));
ci->card_num[CARD_NUM_MAX_LEN - 1u] = '\0';
ci->balance = b->amount_milli / 10u;
memcpy(&ct, &b->rsv[0], 4u);
memcpy(&lt, &b->rsv[4], 4u);
ci->create_time = ct;
ci->last_use_time = lt;
ci->status = b->status;
memcpy(ci->reserved, b->rsv, sizeof(b->rsv));
}
/**
* @brief v_card_info_to_body。
*/
static void v_card_info_to_body(const CardInfo *ci, CARD_FLASH_BODY_T *b)
{
memset(b, 0, sizeof(*b));
v_pack_card_num(ci->card_num, b->card_num);
b->amount_milli = ci->balance * 10u;
b->status = ci->status;
memcpy(&b->rsv[0], &ci->create_time, 4u);
memcpy(&b->rsv[4], &ci->last_use_time, 4u);
}
/**
* @brief u16_count_valid_slots。
*/
static U16_T u16_count_valid_slots(void)
{
U16_T n = 0;
U16_T i;
U8_T buf[CARD_SLOT_SIZE];
for (i = 0; i < CARD_FLASH_MAX_CNT; i++) {
v_read_slot_raw(i, buf);
if (b_slot_buf_valid(buf)) {
n++;
}
}
return n;
}
/**
* @brief s_find_slot_by_num。
*/
static S16_T s_find_slot_by_num(const char *num)
{
U16_T i;
U8_T buf[CARD_SLOT_SIZE];
if (num == NULL) {
return -1;
}
for (i = 0; i < CARD_FLASH_MAX_CNT; i++) {
v_read_slot_raw(i, buf);
if (!b_slot_buf_valid(buf)) {
continue;
}
if (b_card_num_eq(num, buf + 1)) {
return (S16_T)i;
}
}
return -1;
}
/**
* @brief s_find_free_slot。
*/
static S16_T s_find_free_slot(void)
{
U16_T i;
U8_T buf[CARD_SLOT_SIZE];
for (i = 0; i < CARD_FLASH_MAX_CNT; i++) {
v_read_slot_raw(i, buf);
if (!b_slot_buf_valid(buf)) {
return (S16_T)i;
}
}
return -1;
}
/**
* @brief v_erase_all_card_sectors。
*/
static void v_erase_all_card_sectors(void)
{
U8_T s;
for (s = 0; s < (U8_T)DATAFLASH_CARD_SECTOR_CNT; s++) {
(void)s32_flash_dataflash_erase_sector((U32_T)DATAFLASH_CARD_ADDR + (U32_T)s * (U32_T)SPI_SECTOR_SIZE);
}
}
/**
* @brief 写单槽:读扇区内其它有效槽,合并后擦除扇区再写回整扇区
* @param body_or_null 非 NULL 写入/更新;NULL 表示删除该槽(置为擦除态)
*/
static CardStatus e_flush_slot(U16_T slot, const CARD_FLASH_BODY_T *body_or_null)
{
U32_T sec = (U32_T)slot / CARD_SLOTS_PER_SECTOR;
U32_T j = (U32_T)slot % CARD_SLOTS_PER_SECTOR;
U32_T sector_base = (U32_T)DATAFLASH_CARD_ADDR + sec * (U32_T)SPI_SECTOR_SIZE;
U32_T k;
U8_T tmp[CARD_SLOT_SIZE];
U8_T *sector_rw = (U8_T *)FLASH_MGR_MALLOC((size_t)SPI_SECTOR_SIZE);
if (sector_rw == NULL) {
return CARD_STATUS_WRITE_FAILED;
}
memset(sector_rw, 0xFF, SPI_SECTOR_SIZE);
for (k = 0; k < CARD_SLOTS_PER_SECTOR; k++) {
U16_T g = (U16_T)(sec * CARD_SLOTS_PER_SECTOR + k);
if (g >= CARD_FLASH_MAX_CNT) {
break;
}
if (g == slot) {
continue;
}
v_read_slot_raw(g, tmp);
if (b_slot_buf_valid(tmp)) {
memcpy(&sector_rw[k * CARD_SLOT_SIZE], tmp, CARD_SLOT_SIZE);
}
}
if (body_or_null != NULL) {
U8_T *dst = &sector_rw[j * CARD_SLOT_SIZE];
dst[0] = RECORD_HEADER;
memcpy(dst + 1, body_or_null, sizeof(CARD_FLASH_BODY_T));
dst[CARD_SLOT_SIZE - 1u] =
(U8_T)u16_crc_checksum(dst + 1, (U16_T)CARD_BODY_SIZE);
}
(void)s32_flash_dataflash_erase_sector(sector_base);
if (s32_flash_dataflash_write(sector_base, sector_rw, (U32_T)SPI_SECTOR_SIZE) != 0u) {
FLASH_MGR_FREE(sector_rw);
return CARD_STATUS_WRITE_FAILED;
}
FLASH_MGR_FREE(sector_rw);
return CARD_STATUS_OK;
}
/**
* @brief card_init。
*/
CardStatus card_init(void)
{
U16_T actual;
if (g_card_init) {
return CARD_STATUS_ALREADY_INIT;
}
v_read_eeprom_card_mng(&s_cardMng);
actual = u16_count_valid_slots();
if (s_cardMng.u16_head != CARD_MNG_HEAD) {
s_cardMng.u16_head = CARD_MNG_HEAD;
s_cardMng.u16_cardCnt = actual;
s_cardMng.u16_reserved = 0;
v_write_eeprom_card_mng(&s_cardMng);
} else if (s_cardMng.u16_cardCnt != actual) {
s_cardMng.u16_cardCnt = actual;
v_write_eeprom_card_mng(&s_cardMng);
}
g_card_init = true;
FATFS_PRINT("card flash: init ok, cnt=%u\r\n", (unsigned)s_cardMng.u16_cardCnt);
return CARD_STATUS_OK;
}
/**
* @brief card_deinit。
*/
void card_deinit(void)
{
g_card_init = false;
}
/**
* @brief card_add。
*/
CardStatus card_add(const CardInfo *card_info)
{
CARD_FLASH_BODY_T body;
S16_T free_slot;
if (!g_card_init) {
return CARD_STATUS_INIT_FAILED;
}
if (card_info == NULL) {
return CARD_STATUS_PARAM_ERROR;
}
if (s_find_slot_by_num(card_info->card_num) >= 0) {
return CARD_STATUS_CARD_EXIST;
}
free_slot = s_find_free_slot();
if (free_slot < 0) {
return CARD_STATUS_NO_SPACE;
}
v_card_info_to_body(card_info, &body);
if (e_flush_slot((U16_T)free_slot, &body) != CARD_STATUS_OK) {
return CARD_STATUS_WRITE_FAILED;
}
s_cardMng.u16_cardCnt++;
v_write_eeprom_card_mng(&s_cardMng);
return CARD_STATUS_OK;
}
/**
* @brief card_update。
*/
CardStatus card_update(const CardInfo *card_info)
{
CARD_FLASH_BODY_T body;
S16_T idx;
if (!g_card_init) {
return CARD_STATUS_INIT_FAILED;
}
if (card_info == NULL) {
return CARD_STATUS_PARAM_ERROR;
}
idx = s_find_slot_by_num(card_info->card_num);
if (idx < 0) {
return CARD_STATUS_CARD_NOT_FOUND;
}
v_card_info_to_body(card_info, &body);
if (e_flush_slot((U16_T)idx, &body) != CARD_STATUS_OK) {
return CARD_STATUS_WRITE_FAILED;
}
return CARD_STATUS_OK;
}
/**
* @brief card_delete。
*/
CardStatus card_delete(const char *card_num)
{
S16_T idx;
if (!g_card_init) {
return CARD_STATUS_INIT_FAILED;
}
if (card_num == NULL) {
return CARD_STATUS_PARAM_ERROR;
}
idx = s_find_slot_by_num(card_num);
if (idx < 0) {
return CARD_STATUS_CARD_NOT_FOUND;
}
if (e_flush_slot((U16_T)idx, NULL) != CARD_STATUS_OK) {
return CARD_STATUS_WRITE_FAILED;
}
if (s_cardMng.u16_cardCnt > 0) {
s_cardMng.u16_cardCnt--;
}
v_write_eeprom_card_mng(&s_cardMng);
return CARD_STATUS_OK;
}
/**
* @brief card_find。
*/
CardStatus card_find(const char *card_num, CardInfo *card_info)
{
S16_T idx;
U8_T buf[CARD_SLOT_SIZE];
CARD_FLASH_BODY_T body;
if (!g_card_init) {
return CARD_STATUS_INIT_FAILED;
}
if (card_num == NULL || card_info == NULL) {
return CARD_STATUS_PARAM_ERROR;
}
idx = s_find_slot_by_num(card_num);
if (idx < 0) {
return CARD_STATUS_CARD_NOT_FOUND;
}
v_read_slot_raw((U16_T)idx, buf);
if (!b_slot_buf_valid(buf)) {
return CARD_STATUS_READ_FAILED;
}
memcpy(&body, buf + 1, sizeof(CARD_FLASH_BODY_T));
v_body_to_card_info(&body, card_info);
return CARD_STATUS_OK;
}
/**
* @brief card_get_count。
*/
CardStatus card_get_count(uint32_t *count)
{
if (!g_card_init) {
return CARD_STATUS_INIT_FAILED;
}
if (count == NULL) {
return CARD_STATUS_PARAM_ERROR;
}
*count = (uint32_t)s_cardMng.u16_cardCnt;
return CARD_STATUS_OK;
}
/**
* @brief card_get_all。
*/
CardStatus card_get_all(CardInfo *card_list, uint32_t *count, uint32_t max_count)
{
U16_T i;
U32_T n;
U8_T buf[CARD_SLOT_SIZE];
CARD_FLASH_BODY_T body;
if (!g_card_init) {
return CARD_STATUS_INIT_FAILED;
}
if (count == NULL) {
return CARD_STATUS_PARAM_ERROR;
}
n = 0;
for (i = 0; i < CARD_FLASH_MAX_CNT && n < max_count; i++) {
v_read_slot_raw(i, buf);
if (!b_slot_buf_valid(buf)) {
continue;
}
if (card_list != NULL) {
memcpy(&body, buf + 1, sizeof(CARD_FLASH_BODY_T));
v_body_to_card_info(&body, &card_list[n]);
}
n++;
}
*count = n;
return CARD_STATUS_OK;
}
/**
* @brief card_check_exist。
*/
CardStatus card_check_exist(const char *card_num, bool *exist)
{
if (card_num == NULL || exist == NULL) {
return CARD_STATUS_PARAM_ERROR;
}
if (!g_card_init) {
return CARD_STATUS_INIT_FAILED;
}
*exist = (s_find_slot_by_num(card_num) >= 0);
return CARD_STATUS_OK;
}
/**
* @brief card_backup。
*/
CardStatus card_backup(void)
{
/* 无独立备份区;保留接口兼容旧 CSV 备份语义 */
return CARD_STATUS_OK;
}
/**
* @brief card_restore。
*/
CardStatus card_restore(void)
{
return CARD_STATUS_OK;
}
/**
* @brief card_clear_all。
*/
CardStatus card_clear_all(void)
{
if (!g_card_init) {
return CARD_STATUS_INIT_FAILED;
}
v_erase_all_card_sectors();
s_cardMng.u16_head = CARD_MNG_HEAD;
s_cardMng.u16_cardCnt = 0;
s_cardMng.u16_reserved = 0;
v_write_eeprom_card_mng(&s_cardMng);
FATFS_PRINT("card flash: cleared\r\n");
return CARD_STATUS_OK;
}
#else /* !FATFS_ENABLE_CARD */
CardStatus card_init(void)
{
return CARD_STATUS_INIT_FAILED;
}
void card_deinit(void) {}
/**
* @brief card_add。
*/
CardStatus card_add(const CardInfo *card_info)
{
(void)card_info;
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_update。
*/
CardStatus card_update(const CardInfo *card_info)
{
(void)card_info;
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_delete。
*/
CardStatus card_delete(const char *card_num)
{
(void)card_num;
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_find。
*/
CardStatus card_find(const char *card_num, CardInfo *card_info)
{
(void)card_num;
(void)card_info;
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_get_count。
*/
CardStatus card_get_count(uint32_t *count)
{
if (count) {
*count = 0;
}
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_get_all。
*/
CardStatus card_get_all(CardInfo *card_list, uint32_t *count, uint32_t max_count)
{
(void)card_list;
(void)max_count;
if (count) {
*count = 0;
}
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_check_exist。
*/
CardStatus card_check_exist(const char *card_num, bool *exist)
{
(void)card_num;
if (exist) {
*exist = false;
}
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_backup。
*/
CardStatus card_backup(void)
{
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_restore。
*/
CardStatus card_restore(void)
{
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_clear_all。
*/
CardStatus card_clear_all(void)
{
return CARD_STATUS_INIT_FAILED;
}
#endif /* FATFS_ENABLE_CARD */
#else /* !FATFS_EN */
CardStatus card_init(void)
{
return CARD_STATUS_INIT_FAILED;
}
void card_deinit(void) {}
/**
* @brief card_add。
*/
CardStatus card_add(const CardInfo *card_info)
{
(void)card_info;
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_update。
*/
CardStatus card_update(const CardInfo *card_info)
{
(void)card_info;
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_delete。
*/
CardStatus card_delete(const char *card_num)
{
(void)card_num;
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_find。
*/
CardStatus card_find(const char *card_num, CardInfo *card_info)
{
(void)card_num;
(void)card_info;
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_get_count。
*/
CardStatus card_get_count(uint32_t *count)
{
if (count) {
*count = 0;
}
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_get_all。
*/
CardStatus card_get_all(CardInfo *card_list, uint32_t *count, uint32_t max_count)
{
(void)card_list;
(void)max_count;
if (count) {
*count = 0;
}
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_check_exist。
*/
CardStatus card_check_exist(const char *card_num, bool *exist)
{
(void)card_num;
if (exist) {
*exist = false;
}
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_backup。
*/
CardStatus card_backup(void)
{
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_restore。
*/
CardStatus card_restore(void)
{
return CARD_STATUS_INIT_FAILED;
}
/**
* @brief card_clear_all。
*/
CardStatus card_clear_all(void)
{
return CARD_STATUS_INIT_FAILED;
}
#endif /* FATFS_EN */
+46
View File
@@ -0,0 +1,46 @@
/**
* @file card_flash_impl.h
* @brief 卡号白名单:EEPROM 管理信息 + 外部 SPI Flash 槽位存储(与 fault_flash_impl 同类策略)
*
* 对外业务接口见 `app_fatfs/fatfs_card.h`card_init / card_add / card_find 等)。
* 本头文件提供介质层结构说明,便于调试或扩展。
*/
#ifndef CARD_FLASH_IMPL_H
#define CARD_FLASH_IMPL_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
#include "publicdata/type.h"
#define CARD_MNG_HEAD 0x5AA8u
/** EEPROM 中的卡管理块(地址 EEPROM_ADDR_CARD_MNG */
typedef struct
{
U16_T u16_head;
U16_T u16_cardCnt;
U16_T u16_reserved;
} S_CARD_MNG;
/**
* Flash 单槽有效载荷 46 字节(槽位总占用 481 字节 RECORD_HEADER + 46 + 1 字节 CRC 低 8 位)
* - amount_milli:金额,单位 0.001 元(与 API 中 balance「分」换算:毫元 = 分 * 10)
* - rsv[8]:前 4 字节 create_time,后 4 字节 last_use_timeUnix 秒,小端),与 CardInfo 时间字段对应
*/
typedef struct __attribute__((packed))
{
U8_T card_num[32];
U32_T amount_milli;
U16_T status;
U8_T rsv[8];
} CARD_FLASH_BODY_T;
#ifdef __cplusplus
}
#endif
#endif /* CARD_FLASH_IMPL_H */
+241
View File
@@ -0,0 +1,241 @@
/**
* @file fault_flash_impl.c
* @brief 历史故障记录:EEPROM 索引 + 外部 Flash 数据区实现。
*
* 迁移说明(参考 CCU601E_D):
* - EEPROMfm24cl16)仅存储 S_HIS_FAULT_MNG 索引信息,减少擦写与上电恢复成本。
* - 外部 Flashexternalflash)按扇区保存 HIS_FAULT_DATA_T 记录内容。
* - 每条记录格式:1字节 RECORD_HEADER + 数据 + 1字节CRC(低8位)。
*/
#include "flash_file_mgr/fault_flash_impl.h"
#include <string.h>
#include "publicdata/publicdata.h"
#include "publicdata/public_define.h"
#include "eeprom/fm24cl16.h"
#include "externalflash/flash_external_data.h"
/* 兼容抽象:默认使用 FreeRTOS 堆 */
#ifndef FLASH_MGR_MALLOC
#define FLASH_MGR_MALLOC(sz) pvPortMalloc((sz))
#endif
#ifndef FLASH_MGR_FREE
#define FLASH_MGR_FREE(ptr) vPortFree((ptr))
#endif
#define HIS_FAULT_MNG_HEADER (0x5AA5u)
#define FAULT_MNG_EEPROM_ADDR ((U16_T)EEPROM_ADDR_HISALARM_MNG)
#define FAULT_RECORD_RETRY (3u)
/* 来自故障任务:全局管理索引 */
extern S_HIS_FAULT_MNG s_hisFaultMng;
void v_fault_flash_init(void)
{
/* 上电恢复 EEPROM 中的历史故障管理索引。 */
v_read_eeprom_hisFaultMng(&s_hisFaultMng);
/* 管理信息异常时回退到安全默认值。 */
if ((s_hisFaultMng.u16_head != HIS_FAULT_MNG_HEADER) ||
(s_hisFaultMng.u16_hisFaultCnt > HIS_FAULT_MAX_CNT) ||
(s_hisFaultMng.u16_currIndex > HIS_FAULT_MAX_CNT)) {
v_fault_clear_his_fault();
}
}
static U16_T fault_sector_record_count(void)
{
return (U16_T)(SPI_SECTOR_SIZE / (sizeof(HIS_FAULT_DATA_T) + 2u));
}
void v_write_eeprom_hisFaultMng(S_HIS_FAULT_MNG *hisFaultMng)
{
S_HIS_FAULT_MNG tmp;
if (hisFaultMng == NULL) {
return;
}
tmp.u16_head = HIS_FAULT_MNG_HEADER;
tmp.u16_hisFaultCnt = hisFaultMng->u16_hisFaultCnt;
tmp.u16_currIndex = hisFaultMng->u16_currIndex;
eeprom_buffer_write((U8_T *)&tmp, FAULT_MNG_EEPROM_ADDR, (U16_T)sizeof(S_HIS_FAULT_MNG));
}
void v_read_eeprom_hisFaultMng(S_HIS_FAULT_MNG *hisFaultMng)
{
S_HIS_FAULT_MNG tmp;
U8_T i;
if (hisFaultMng == NULL) {
return;
}
memset(&tmp, 0, sizeof(tmp));
for (i = 0u; i < FAULT_RECORD_RETRY; i++) {
eeprom_buffer_read((U8_T *)&tmp, FAULT_MNG_EEPROM_ADDR, (U16_T)sizeof(S_HIS_FAULT_MNG));
if ((eeprom_get_last_error() == 0u) && (tmp.u16_head == HIS_FAULT_MNG_HEADER)) {
hisFaultMng->u16_head = HIS_FAULT_MNG_HEADER;
hisFaultMng->u16_hisFaultCnt = tmp.u16_hisFaultCnt;
hisFaultMng->u16_currIndex = tmp.u16_currIndex;
return;
}
}
/* 失败时给出安全默认值,避免野数据参与索引计算。 */
hisFaultMng->u16_head = 0u;
hisFaultMng->u16_hisFaultCnt = 0u;
hisFaultMng->u16_currIndex = RECORD_ZERO_SAVE;
}
void v_fault_clear_his_fault(void)
{
s_hisFaultMng.u16_head = HIS_FAULT_MNG_HEADER;
s_hisFaultMng.u16_hisFaultCnt = 0u;
s_hisFaultMng.u16_currIndex = RECORD_ZERO_SAVE;
v_write_eeprom_hisFaultMng(&s_hisFaultMng);
}
U16_T u16_fault_get_his_count(void)
{
S_HIS_FAULT_MNG mng;
memset(&mng, 0, sizeof(mng));
v_read_eeprom_hisFaultMng(&mng);
if (mng.u16_head != HIS_FAULT_MNG_HEADER) {
return 0u;
}
if (mng.u16_hisFaultCnt > HIS_FAULT_MAX_CNT) {
return HIS_FAULT_MAX_CNT;
}
return mng.u16_hisFaultCnt;
}
U8_T u8_fault_read_his_by_newest(U16_T newest_pos, HIS_FAULT_DATA_T *pt_hisFault)
{
S_HIS_FAULT_MNG mng;
U16_T total;
U16_T slot;
if (pt_hisFault == NULL) {
return 0u;
}
memset(pt_hisFault, 0, sizeof(*pt_hisFault));
total = u16_fault_get_his_count();
if ((total == 0u) || (newest_pos >= total)) {
return 0u;
}
memset(&mng, 0, sizeof(mng));
v_read_eeprom_hisFaultMng(&mng);
if ((mng.u16_head != HIS_FAULT_MNG_HEADER) || (mng.u16_currIndex == RECORD_ZERO_SAVE)) {
return 0u;
}
slot = (U16_T)((mng.u16_currIndex + HIS_FAULT_MAX_CNT - newest_pos) % HIS_FAULT_MAX_CNT);
v_read_hisFault_record(pt_hisFault, slot);
return (pt_hisFault->e_fault_info == E_FAULT_NULL) ? 0u : 1u;
}
void v_read_hisFault_record(HIS_FAULT_DATA_T *pt_hisFault, U16_T u16_index)
{
U16_T rec_size = (U16_T)sizeof(HIS_FAULT_DATA_T);
U16_T one_sector_cnt = fault_sector_record_count();
U32_T addr;
U16_T sector_idx;
U16_T sector_rec_idx;
U8_T retry;
U8_T *buf;
if ((pt_hisFault == NULL) || (one_sector_cnt == 0u)) {
return;
}
buf = (U8_T *)FLASH_MGR_MALLOC((size_t)rec_size + 2u);
if (buf == NULL) {
return;
}
memset(pt_hisFault, 0, sizeof(*pt_hisFault));
for (retry = 0u; retry < FAULT_RECORD_RETRY; retry++) {
sector_idx = (U16_T)(u16_index / one_sector_cnt);
sector_rec_idx = (U16_T)(u16_index % one_sector_cnt);
addr = (U32_T)(DATAFLASH_FAULT_ADDR +
(U32_T)sector_idx * SPI_SECTOR_SIZE +
(U32_T)sector_rec_idx * (rec_size + 2u));
if (s32_flash_dataflash_read(addr, buf, (U32_T)(rec_size + 2u)) != 0u) {
continue;
}
if ((buf[0] == RECORD_HEADER) &&
(buf[rec_size + 1u] == (U8_T)u16_crc_checksum(buf + 1u, rec_size))) {
memcpy(pt_hisFault, buf + 1u, rec_size);
break;
}
}
FLASH_MGR_FREE(buf);
}
static void v_write_hisFault_record(HIS_FAULT_DATA_T *pt_hisFault, U16_T u16_index)
{
U16_T rec_size = (U16_T)sizeof(HIS_FAULT_DATA_T);
U16_T one_sector_cnt = fault_sector_record_count();
U16_T sector_idx;
U16_T sector_rec_idx;
U32_T sector_addr;
U32_T addr;
U8_T *buf;
if ((pt_hisFault == NULL) || (one_sector_cnt == 0u)) {
return;
}
buf = (U8_T *)FLASH_MGR_MALLOC((size_t)rec_size + 2u);
if (buf == NULL) {
return;
}
buf[0] = RECORD_HEADER;
memcpy(buf + 1u, pt_hisFault, rec_size);
buf[rec_size + 1u] = (U8_T)u16_crc_checksum((U8_T *)pt_hisFault, rec_size);
sector_idx = (U16_T)(u16_index / one_sector_cnt);
sector_rec_idx = (U16_T)(u16_index % one_sector_cnt);
sector_addr = (U32_T)(DATAFLASH_FAULT_ADDR + (U32_T)sector_idx * SPI_SECTOR_SIZE);
addr = (U32_T)(sector_addr + (U32_T)sector_rec_idx * (rec_size + 2u));
/* 扇区首条记录写入前先擦除,避免旧数据残留影响 CRC。 */
if (sector_rec_idx == 0u) {
(void)s32_flash_dataflash_erase_sector(sector_addr);
}
(void)s32_flash_dataflash_write(addr, buf, (U32_T)(rec_size + 2u));
FLASH_MGR_FREE(buf);
}
void v_hisFault_save_record(HIS_FAULT_DATA_T *pt_hisFault_data)
{
if (pt_hisFault_data == NULL) {
return;
}
if (s_hisFaultMng.u16_currIndex == RECORD_ZERO_SAVE) {
s_hisFaultMng.u16_currIndex = 0u;
} else {
s_hisFaultMng.u16_currIndex =
(U16_T)((s_hisFaultMng.u16_currIndex + 1u) % HIS_FAULT_MAX_CNT);
}
if (s_hisFaultMng.u16_hisFaultCnt < HIS_FAULT_MAX_CNT) {
s_hisFaultMng.u16_hisFaultCnt++;
}
v_write_hisFault_record(pt_hisFault_data, s_hisFaultMng.u16_currIndex);
v_write_eeprom_hisFaultMng(&s_hisFaultMng);
}
+58
View File
@@ -0,0 +1,58 @@
/**
* @file fault_flash_impl.h
* @brief 历史故障记录持久化接口(EEPROM索引 + 外部Flash数据)。
*/
#ifndef FAULT_FLASH_IMPL_H
#define FAULT_FLASH_IMPL_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
#include "fault_cheak/faultcheck_task.h"
#include "fault_cheak/fault_interface.h"
#define FAULT_INFO_TEXT_MAX_LEN (64u)
/* 故障历史存储功能开关:
* - 0:先屏蔽(避免影响 EEPROM 可靠性排查)
* - 1:使能(恢复 EEPROM 索引 + Flash 记录读写)
*/
#ifndef FAULT_FLASH_MGR_ENABLE
#define FAULT_FLASH_MGR_ENABLE (1u)
#endif
/**
* @brief 历史故障管理信息(存放在 EEPROM)。
*/
typedef struct
{
U16_T u16_head; /* 固定头 0x5AA5 */
U16_T u16_hisFaultCnt; /* 历史记录总数(上限 HIS_FAULT_MAX_CNT */
U16_T u16_currIndex; /* 当前最新记录逻辑索引,初值 RECORD_ZERO_SAVE */
} S_HIS_FAULT_MNG;
typedef struct
{
E_FAULT_INFO e_mag;
char info[FAULT_INFO_TEXT_MAX_LEN];
} S_FAULT_INFO;
void v_fault_flash_init(void);
void v_write_eeprom_hisFaultMng(S_HIS_FAULT_MNG *hisFaultMng);
void v_read_eeprom_hisFaultMng(S_HIS_FAULT_MNG *hisFaultMng);
void v_fault_clear_his_fault(void);
U16_T u16_fault_get_his_count(void);
U8_T u8_fault_read_his_by_newest(U16_T newest_pos, HIS_FAULT_DATA_T *pt_hisFault);
void v_hisFault_save_record(HIS_FAULT_DATA_T *pt_hisFault_data);
void v_read_hisFault_record(HIS_FAULT_DATA_T *pt_hisFault, U16_T u16_index);
#ifdef __cplusplus
}
#endif
#endif /* FAULT_FLASH_IMPL_H */
@@ -0,0 +1,72 @@
# flash_file_mgr 模块说明
本文档用于说明 `BSP/flash_file_mgr` 目录下各文件职责、数据落盘位置与调用关系,便于定位问题和后续扩展。
## 目录内文件与功能
### 1) `fault_flash_impl.h` / `fault_flash_impl.c`
- **功能**:历史故障记录持久化(EEPROM 管理信息 + SPI Flash 记录体)。
- **管理信息**`S_HIS_FAULT_MNG`,存放在 EEPROM `EEPROM_ADDR_HISALARM_MNG`
- **记录数据**:从 `DATAFLASH_FAULT_ADDR` 开始,单条记录格式:
- `RECORD_HEADER(1B)` + `HIS_FAULT_DATA_T` + `CRC低8位(1B)`
- **核心接口**
- `v_hisFault_save_record()`:保存一条故障记录。
- `v_read_hisFault_record()`:按逻辑索引读取记录。
- `v_fault_clear_his_fault()`:清空故障管理信息。
### 2) `card_flash_impl.h` / `card_flash_impl.c`
- **功能**:卡号白名单持久化(替代 CSV/FAT 文件实现),由 `fatfs_card.h` 对外暴露业务接口。
- **管理信息**`S_CARD_MNG`,存放在 EEPROM `EEPROM_ADDR_CARD_MNG`
- **记录数据**:从 `DATAFLASH_CARD_ADDR` 开始,单槽格式:
- `RECORD_HEADER(1B)` + `CARD_FLASH_BODY_T` + `CRC低8位(1B)`
- **关键特性**
- 按卡号查重/查找。
- 写入采用“读扇区 -> 合并 -> 擦除 -> 整扇区回写”策略,避免 NOR Flash 原地改写问题。
- **对外接口(由 `fatfs_card.h` 声明)**
- `card_init/card_add/card_update/card_delete/card_find/...`
### 3) `meter_calculate_flash_impl.h` / `meter_calculate_flash_impl.c`
- **功能**:充电订单历史记录 + 临时订单(断电续传)持久化。
- **管理信息**`S_CHG_ORDER_MNG`,存放在 EEPROM `EEPROM_ADDR_CHGRCD_MNG`
- **历史订单数据**:从 `DATAFLASH_HIS_RECORD_ADDR` 开始,记录体为 `S_LOG_DATA`,单条格式:
- `RECORD_HEADER(1B)` + `S_LOG_DATA` + `CRC低8位(1B)`
- **临时订单数据**:A/B 枪分别存放在 EEPROM `EEPROM_ADDR_A_LOG_DATA` / `EEPROM_ADDR_B_LOG_DATA`
- **核心接口**
- `u32_chg_order_save_record()`:保存并分配唯一索引。
- `v_read_chg_order_record()`:按索引读取。
- `v_temp_chg_data_save/v_temp_chg_data_read/...`:临时订单保存/恢复。
### 4) `unsettled_order_mng.h` / `unsettled_order_mng.c`
- **功能**:未结算订单索引列表管理(仅存索引,不存订单正文)。
- **存储位置**EEPROM `EEPROM_ADDR_UNSETTLED_MNG`
- **数据结构**`S_UNSETTLED_ORDER_MNG`,包含头标志、数量、CRC 和 3 字节压缩索引数组。
- **核心接口**
- `u8_add_unsettled_order()`:新增未结订单索引。
- `u8_remove_unsettled_order()`:删除已结索引。
- `u8_check_order_settled()`:检查是否仍未结。
### 5) `ocpp_mv_offline_flash_impl.h` / `ocpp_mv_offline_flash_impl.c`
- **功能**:平台离线时周期缓存 MeterValuesFlash 为 **整池定长帧数组**(仿 `fault_flash_impl`:帧头+32B 负载+CRC),**全局序号**与物理槽 `seq % POOL_MAX` 一一对应;FRAM 存 `u32_next_seq`**流水号→(seq_start,count)** 映射表(最多 6 笔并发),上送时按序号直接读 Flash。
- **容量**`OCPP_MV_OFFLINE_POOL_MAX` = 每扇区 `floor(4096/帧长)` × `DATAFLASH_OCPP_MV_OFFLINE_SECTOR_CNT`(扇区尾截断不用)。
- **滚动**`u32_next_seq` 递增写满池后自然覆盖最旧物理槽;映射行上送后应 `u8_ocpp_mv_offline_map_remove`
- **核心接口**`v_ocpp_mv_offline_init``u32_ocpp_mv_offline_pool_max``u8_ocpp_mv_offline_mv_save``u8_ocpp_mv_offline_mv_read_by_seq``u8_ocpp_mv_offline_map_get``u8_ocpp_mv_offline_map_remove``v_ocpp_mv_offline_flash_erase_pool``u8_ocpp_mv_offline_fram_read` / `u8_ocpp_mv_offline_fram_write`
- **RAM 镜像**`s_ocppMvOfflineFramMng`;上电 `v_ocpp_mv_offline_init()`(兼容宏 `v_ocpp_mv_offline_flash_init()`)。
## 地址与容量宏来源
`flash_file_mgr` 模块统一依赖 `BSP/spi_Flash/flash_external_data.h` 中定义的地址与容量宏(如 `DATAFLASH_FAULT_ADDR``DATAFLASH_CARD_ADDR``DATAFLASH_HIS_RECORD_ADDR``DATAFLASH_OCPP_MV_OFFLINE_ADDR``CHG_ORDER_*` 等)。
建议后续若调整分区地址,仅修改 `flash_external_data.h`,并回归验证:
- 故障记录读写;
- 卡号增删查;
- 订单保存与按索引回读;
- 未结算订单索引增删。
## 模块关系(简版)
- `faultcheck_task` -> `fault_flash_impl`:故障历史。
- `fatfs_init / bs_public_impl` -> `fatfs_card.h` -> `card_flash_impl`:本地卡鉴权与白名单。
- `meter_calculate_impl` -> `meter_calculate_flash_impl`:订单持久化。
- `meter_calculate_flash_impl` + `bs_public_impl` -> `unsettled_order_mng`:未结算订单索引维护。
- (规划)`BS_ocpp_ctrl` / OCPP 离线路径 -> `ocpp_mv_offline_flash_impl`:离线 MeterValues 采样落盘与联网后补发。
@@ -0,0 +1,108 @@
# flash_file_mgr 模块说明
> 路径:`BSP/flash_file_mgr/`
> 作用:提供历史故障记录的掉电持久化能力,采用“EEPROM 管理索引 + 外部 Flash 存储内容”的双存储架构。
## 1. 模块文件
- `fault_flash_impl.h`
- 历史故障持久化对外接口声明
- `S_HIS_FAULT_MNG` 管理结构定义
- `fault_flash_impl.c`
- EEPROM 索引读写实现
- 外部 Flash 记录读写实现
- 历史记录计数、按“最新N条”读取实现
- `meter_calculate_flash_impl.c`
- 充电订单记录持久化实现(EEPROM 管理块 + 外部 Flash 订单正文)
- 提供订单保存/按索引读取/按位置读取、临时订单 EEPROM 缓存接口
- 支持双枪临时订单标志管理(用于掉电恢复)
- `unsettled_order_mng.c`
- 未结算订单列表管理(EEPROM 持久化)
- 支持未结订单添加、移除、查询、清空与 CRC 校验
## 2. 设计目标
- 将高频变化的小数据(计数、索引)放 EEPROM,降低 Flash 管理复杂度;
- 将体积较大的历史故障记录放 externalflash,便于顺序存储与扩展;
- 提供统一接口给 `fault_cheak``meter_calculate`,业务层只关心“保存/读取故障或订单记录”。
## 3. 存储布局
### 3.1 EEPROM(索引区)
- 地址:`EEPROM_ADDR_HISALARM_MNG`
- 数据:`S_HIS_FAULT_MNG`
- `u16_head`:固定头 `0x5AA5`,用于有效性校验
- `u16_hisFaultCnt`:历史记录数
- `u16_currIndex`:当前最新逻辑索引(环形)
### 3.2 externalflash(数据区)
- 基址:`DATAFLASH_FAULT_ADDR`
- 扇区大小:`SPI_SECTOR_SIZE`
- 单条记录格式:
- `1 byte RECORD_HEADER`
- `sizeof(HIS_FAULT_DATA_T) bytes 数据体`
- `1 byte CRC``u16_crc_checksum` 低 8 位)
## 4. 核心接口说明
- `v_hisFault_save_record(HIS_FAULT_DATA_T *pt_hisFault_data)`
- 保存一条历史故障
- 更新环形索引与计数
- 写 externalflash 记录后更新 EEPROM 管理信息
- `u8_fault_read_his_by_newest(U16_T newest_pos, HIS_FAULT_DATA_T *pt_hisFault)`
- 按“最新优先”读取历史记录
- `newest_pos=0` 表示最新一条
- `u16_fault_get_his_count(void)`
- 返回历史记录数量(带上限保护)
- `v_fault_clear_his_fault(void)`
- 清空管理信息(不逐条清除 Flash 区内容)
### 4.1 计量订单相关接口(meter_calculate
- `v_chg_order_flash_init(void)`
- 初始化充电订单管理信息(读取 EEPROM 管理块,必要时恢复默认)
- `u32_chg_order_save_record(S_LOG_DATA *order_data)`
- 保存订单到外部 Flash,并返回单调递增订单索引
- `v_read_chg_order_record(S_LOG_DATA *order_data, U32_T index)`
- 按订单索引读取订单记录
- `v_save_temp_chg_record_to_eeprom(...)` / `v_read_temp_chg_record_from_eeprom(...)`
- 充电过程临时订单缓存/恢复(EEPROM)
- `u8_add_unsettled_order(U32_T orderIndex)` / `u8_remove_unsettled_order(U32_T orderIndex)`
- 未结算订单添加/移除
- `u8_check_order_settled(U32_T orderIndex)`
- 查询订单是否仍处于未结算列表
## 5. 写入策略
- 采用环形索引写入,达到上限后覆盖最老记录;
- 每个扇区的第 1 条记录写入前先擦除该扇区,避免旧数据干扰;
- 记录读取时校验:
- 头字节必须是 `RECORD_HEADER`
- CRC 必须匹配
- 否则判定为无效记录。
## 6. 依赖关系
- EEPROM 驱动:`BSP/eeprom/fm24cl16.c`
- 外部 Flash 驱动:`BSP/externalflash/flash_external_data.c`
- CRC 工具:`app/publicdata/public_func.c``u16_crc_checksum`
- 故障数据结构:`app/fault_cheak/faultcheck_task.h`
- 计量订单结构:`app/meter_calculate/meter_calculate_impl.h``S_LOG_DATA`
## 7. 注意事项
- 请保证 `DATAFLASH_FAULT_ADDR` 区域不与其他业务区重叠;
- `HIS_FAULT_MAX_CNT` 与扇区分配需协同评估,避免有效容量不足;
- 计量订单区(`DATAFLASH_HIS_RECORD_ADDR`)与故障区地址需避免重叠;
- 未结算订单列表与订单管理块均依赖 EEPROM,建议控制写频率并保留 CRC 校验;
- 该模块默认在任务上下文调用,若多任务并发写入建议增加互斥保护。
## 8. 相关文档
- [主说明 README](../../README.md)
- [fault_cheak 模块说明](../../app/fault_cheak/fault_cheak模块说明.md)
- [eeprom 模块说明](../eeprom/eeprom模块说明.md)
- [externalflash 模块说明](../externalflash/externalflash模块说明.md)
@@ -0,0 +1,526 @@
/**
* @file meter_calculate_flash_impl.c
* @brief 充电订单与临时订单的 EEPROM + 外部 SPI Flash 持久化实现
*
* 功能说明:
* - 订单管理块 `S_CHG_ORDER_MNG` 存于 EEPROM`EEPROM_ADDR_CHGRCD_MNG`):头标志、已存条数、
* 环形槽当前位置、单调递增的 `u32_lastIndex`(对外唯一订单号)。
* - 订单正文 `S_LOG_DATA` 存于 Flash 区 `DATAFLASH_HIS_RECORD_ADDR` 起;单条布局为
* 1 字节 `RECORD_HEADER` + 数据 + 1 字节 CRC`u16_crc_checksum` 低 8 位),按扇区擦除后顺序写入。
* - `u32_chg_order_save_record`:分配新 `u32_lastIndex`,按 `(lastIndex-1)%CHG_ORDER_MAX_CNT` 映射槽位并写 Flash。
* - `v_read_chg_order_record(index)`:用 `(index-1)%CHG_ORDER_MAX_CNT` 定位槽,并校验记录内 `u32_index` 与请求一致,防止覆盖残留。
* - `v_read_chg_order_by_position`:在未满/已满环形缓冲下,按“第几条历史”语义换算槽位。
* - 双枪临时订单:标志字节 + `S_LOG_DATA` 存于 EEPROM`EEPROM_ADDR_A_LOG_DATA` / `B`),用于断电续传;清除时仅写无效标志以省擦写。
*
* 注意:`v_chg_order_clear_all` 仅复位管理元数据,不整片擦除 Flash(由后续写入覆盖)。
*/
#include "meter_calculate_flash_impl.h"
#include <string.h>
#include <stdlib.h>
#include "mylog/mylog.h"
/* 动态内存抽象:默认使用 FreeRTOS 堆接口,可按需覆盖 */
#ifndef FLASH_MGR_MALLOC
#define FLASH_MGR_MALLOC(sz) pvPortMalloc((sz))
#endif
#ifndef FLASH_MGR_FREE
#define FLASH_MGR_FREE(ptr) vPortFree((ptr))
#endif
/* 模块内部全局变量 */
S_CHG_ORDER_MNG s_chgOrderMng;
/* 内部函数声明 */
static void v_write_chg_order_record(S_LOG_DATA *order_data, U16_T position);
static U8_T v_read_chg_order_record_internal(S_LOG_DATA *order_data, U16_T position);
/***************************************************************
* EEPROM管理函数
***************************************************************/
/**
* @brief 写入充电订单管理信息到EEPROM
*/
void v_write_eeprom_chgOrderMng(S_CHG_ORDER_MNG *chgOrderMng)
{
S_CHG_ORDER_MNG tempMng;
// 设置头标志
tempMng.u16_head = 0x5AA5;
tempMng.u16_chgOrderCnt = chgOrderMng->u16_chgOrderCnt;
tempMng.u16_currIndex = chgOrderMng->u16_currIndex;
tempMng.u32_lastIndex = chgOrderMng->u32_lastIndex;
// 写入EEPROM
eeprom_write(EEPROM_ADDR_CHGRCD_MNG, (U8_T *)&tempMng, sizeof(S_CHG_ORDER_MNG));
}
/**
* @brief 从EEPROM读取充电订单管理信息
*/
void v_read_eeprom_chgOrderMng(S_CHG_ORDER_MNG *chgOrderMng)
{
S_CHG_ORDER_MNG tempMng;
int i;
for (i = 0; i < 3; i++) {
// eeprom_read返回HAL_OK(0)表示成功,非0表示失败
if (eeprom_read(EEPROM_ADDR_CHGRCD_MNG, (U8_T *)&tempMng, sizeof(S_CHG_ORDER_MNG)) == HAL_OK) {
// 验证头标志
if (tempMng.u16_head == 0x5AA5) {
chgOrderMng->u16_head = 0x5AA5;
chgOrderMng->u16_chgOrderCnt = tempMng.u16_chgOrderCnt;
chgOrderMng->u16_currIndex = tempMng.u16_currIndex;
chgOrderMng->u32_lastIndex = tempMng.u32_lastIndex;
return;
}
}
}
// 读取失败或数据无效,初始化默认值
chgOrderMng->u16_head = 0x5AA5;
chgOrderMng->u16_chgOrderCnt = 0;
chgOrderMng->u16_currIndex = RECORD_ZERO_SAVE;
chgOrderMng->u32_lastIndex = 0;
}
/***************************************************************
* Flash记录读写函数(内部)
***************************************************************/
/**
* @brief 向Flash写充电订单记录(内部函数)
*
* @param order_data 充电订单数据指针
* @param position 循环缓冲区中的位置(1-N范围)
*/
static void v_write_chg_order_record(S_LOG_DATA *order_data, U16_T position)
{
U16_T u16_oneRcdSize = CHG_ORDER_RECORD_SIZE; // 一条记录大小(可能大于255
U32_T u32_addr; // 存储地址
U16_T SectorIndex = 0; // 扇区偏移索引
U16_T SectorRcdIndex = 0; // 扇区内记录索引号
U16_T OneSectorRcdCnt = CHG_ORDER_PER_SECTOR_CNT; // 一个扇区内最多存储记录数
U32_T SectorAddr = 0; // 扇区地址
U16_T CurSectorRcdCnt = 0; // 当前扇区内应存放最大数目
// position参数范围是1-N,转换为0-N范围用于内部计算
U16_T internal_position = position - 1;
U8_T *tmpbuf = (U8_T *)FLASH_MGR_MALLOC(CHG_ORDER_FULL_RECORD_SIZE);
if (tmpbuf == NULL) {
return;
}
// 填充待存储数据缓存
tmpbuf[0] = RECORD_HEADER;
memcpy(tmpbuf + 1, order_data, u16_oneRcdSize); // 充电订单记录
// 计算并存储CRC
U16_T calculated_crc = u16_crc_checksum((U8_T *)order_data, u16_oneRcdSize);
tmpbuf[u16_oneRcdSize + 1] = (U8_T)calculated_crc; // 存储CRC低字节
#ifdef DEBUG_FLASH_WRITE
MYLOG_MSG(TASK_ID_Meterfee, "[DEBUG] Write to addr 0x%08X (position %u, internal %u):", u32_addr, position, internal_position);
MYLOG_MSG(TASK_ID_Meterfee, " Header: 0x%02X", tmpbuf[0]);
MYLOG_MSG(TASK_ID_Meterfee, " Calculated CRC: 0x%04X -> stored low byte: 0x%02X", calculated_crc, tmpbuf[u16_oneRcdSize + 1]);
#endif
// 确定存储位置(使用internal_position,范围0-N
SectorIndex = internal_position / OneSectorRcdCnt;
SectorRcdIndex = internal_position % OneSectorRcdCnt;
SectorAddr = DATAFLASH_HIS_RECORD_ADDR + SectorIndex * SPI_SECTOR_SIZE;
// 如果是扇区内第一条记录,需要擦除整个扇区
if (SectorRcdIndex == 0) {
s32_flash_dataflash_erase_sector(SectorAddr);
// 如果记录数超过最大限制,需要调整计数
if (s_chgOrderMng.u16_chgOrderCnt > CHG_ORDER_MAX_CNT) {
// 如果待存储扇区为最后一个扇区
U16_T totalSectors = (CHG_ORDER_MAX_CNT + OneSectorRcdCnt - 1) / OneSectorRcdCnt;
if ((SectorIndex + 1) == totalSectors) {
CurSectorRcdCnt = CHG_ORDER_MAX_CNT % OneSectorRcdCnt;
if (CurSectorRcdCnt == 0) {
CurSectorRcdCnt = OneSectorRcdCnt;
}
} else {
CurSectorRcdCnt = OneSectorRcdCnt;
}
s_chgOrderMng.u16_chgOrderCnt = CHG_ORDER_MAX_CNT - CurSectorRcdCnt + 1;
}
}
// 计算具体地址并写入
u32_addr = SectorAddr + SectorRcdIndex * CHG_ORDER_FULL_RECORD_SIZE;
s32_flash_dataflash_write(u32_addr, tmpbuf, CHG_ORDER_FULL_RECORD_SIZE);
FLASH_MGR_FREE(tmpbuf);
}
/**
* @brief 从Flash读充电订单记录(内部函数)
*
* @param order_data 充电订单数据指针(用于存储读取到的数据)
* @param position 循环缓冲区中的位置(1-N范围)
* @return U8_T 读取结果:0-成功,1-失败
*/
static U8_T v_read_chg_order_record_internal(S_LOG_DATA *order_data, U16_T position)
{
U16_T u16_oneRcdSize = CHG_ORDER_RECORD_SIZE; // 一条记录大小(可能大于255
U32_T u32_addr; // 读取地址
U16_T SectorIndex = 0; // 扇区偏移索引
U16_T SectorRcdIndex = 0; // 扇区内记录索引号
U16_T OneSectorRcdCnt = CHG_ORDER_PER_SECTOR_CNT; // 一个扇区内最多存储记录数
// position参数范围是1-N,转换为0-N范围用于内部计算
U16_T internal_position = position - 1;
U8_T *tmpbuf = (U8_T *)FLASH_MGR_MALLOC(CHG_ORDER_FULL_RECORD_SIZE);
if (tmpbuf == NULL) {
return 1;
}
// 确定读取位置(使用internal_position,范围0-N
SectorIndex = internal_position / OneSectorRcdCnt;
SectorRcdIndex = internal_position % OneSectorRcdCnt;
u32_addr = DATAFLASH_HIS_RECORD_ADDR + SectorIndex * SPI_SECTOR_SIZE +
SectorRcdIndex * CHG_ORDER_FULL_RECORD_SIZE;
// 读取数据
s32_flash_dataflash_read(u32_addr, tmpbuf, CHG_ORDER_FULL_RECORD_SIZE);
// 调试信息:打印读取到的数据
#ifdef DEBUG_FLASH_READ
MYLOG_MSG(TASK_ID_Meterfee, "[DEBUG] Read from addr 0x%08X (position %u, internal %u):", u32_addr, position, internal_position);
MYLOG_MSG(TASK_ID_Meterfee, " Header: 0x%02X (expected: 0x%02X)", tmpbuf[0], RECORD_HEADER);
// 计算CRC
U16_T calculated_crc = u16_crc_checksum(tmpbuf + 1, u16_oneRcdSize);
U8_T stored_crc = tmpbuf[u16_oneRcdSize + 1];
MYLOG_MSG(TASK_ID_Meterfee, " Stored CRC: 0x%02X", stored_crc);
MYLOG_MSG(TASK_ID_Meterfee, " Calculated CRC: 0x%04X -> low byte: 0x%02X", calculated_crc, (U8_T)calculated_crc);
#endif
// 验证数据完整性
if ((tmpbuf[0] == RECORD_HEADER) &&
(tmpbuf[u16_oneRcdSize + 1] == (U8_T)u16_crc_checksum(tmpbuf + 1, u16_oneRcdSize))) {
#ifdef DEBUG_FLASH_READ
MYLOG_MSG(TASK_ID_Meterfee, "[DEBUG] v_read_chg_order_record_internal: data ok, memcpy dst=0x%08X, size=%u",
(U32_T)order_data, u16_oneRcdSize);
#endif
memcpy(order_data, tmpbuf + 1, u16_oneRcdSize);
FLASH_MGR_FREE(tmpbuf);
return 0;
}
#ifdef DEBUG_FLASH_READ
MYLOG_MSG(TASK_ID_Meterfee, "[DEBUG] CRC check failed!");
MYLOG_MSG(TASK_ID_Meterfee, " Header match: %s", (tmpbuf[0] == RECORD_HEADER) ? "YES" : "NO");
MYLOG_MSG(TASK_ID_Meterfee, " CRC match: %s", (tmpbuf[u16_oneRcdSize + 1] == (U8_T)u16_crc_checksum(tmpbuf + 1, u16_oneRcdSize)) ? "YES" : "NO");
#endif
FLASH_MGR_FREE(tmpbuf);
return 1;
}
/***************************************************************
* 公共接口函数实现
***************************************************************/
/**
* @brief 初始化充电订单Flash存储模块
*/
void v_chg_order_flash_init(void)
{
// 从EEPROM读取管理信息
v_read_eeprom_chgOrderMng(&s_chgOrderMng);
// 如果头标志无效,初始化默认值
if (s_chgOrderMng.u16_head != 0x5AA5)
{
s_chgOrderMng.u16_head = 0x5AA5;
s_chgOrderMng.u16_chgOrderCnt = 0;
s_chgOrderMng.u16_currIndex = RECORD_ZERO_SAVE;
s_chgOrderMng.u32_lastIndex = 0;
// 写入EEPROM
v_write_eeprom_chgOrderMng(&s_chgOrderMng);
}
// MYLOG_MSG(TASK_ID_Meterfee, "OrderMng: init ok, cnt=%u",
// s_chgOrderMng.u16_chgOrderCnt);
}
/**
* @brief 清除所有充电订单记录
*/
void v_chg_order_clear_all(void)
{
// 重置管理信息
s_chgOrderMng.u16_head = 0x5AA5;
s_chgOrderMng.u16_chgOrderCnt = 0;
s_chgOrderMng.u16_currIndex = RECORD_ZERO_SAVE;
s_chgOrderMng.u32_lastIndex = 0;
// 写入EEPROM
v_write_eeprom_chgOrderMng(&s_chgOrderMng);
// 注意:这里不清除Flash中的实际数据,因为Flash擦除成本高
// 实际数据会在后续写入时被覆盖
}
/**
* @brief 保存充电订单记录到Flash
*
* 新设计:索引号直接对应Flash物理位置
* 索引号 = u32_lastIndex + 1
* 存储位置 = (u32_lastIndex % CHG_ORDER_MAX_CNT)
* 注意:此函数会修改order_data中的u32_index字段
*/
U32_T u32_chg_order_save_record(S_LOG_DATA *order_data)
{
U32_T assignedIndex;
// 分配唯一递增索引
s_chgOrderMng.u32_lastIndex++;
assignedIndex = s_chgOrderMng.u32_lastIndex;
// 计算存储位置(基于索引号的循环缓冲区)
U16_T storagePosition = (s_chgOrderMng.u32_lastIndex - 1) % CHG_ORDER_MAX_CNT;
// 更新当前索引位置(存储0-N范围)
s_chgOrderMng.u16_currIndex = storagePosition;
// 记录条数递增(不超过最大限制)
if (s_chgOrderMng.u16_chgOrderCnt < CHG_ORDER_MAX_CNT) {
s_chgOrderMng.u16_chgOrderCnt++;
}
// 直接修改传入的订单数据,设置索引
order_data->u32_index = assignedIndex;
// 写入Flash记录,传递1-N范围的position
v_write_chg_order_record(order_data, storagePosition + 1);
// 更新管理信息到EEPROM
v_write_eeprom_chgOrderMng(&s_chgOrderMng);
return assignedIndex;
}
/**
* @brief 从Flash读取指定索引的充电订单记录
*
* 新设计:根据索引号直接计算存储位置,无需遍历查找
* 存储位置 = (index - 1) % CHG_ORDER_MAX_CNT
*/
U8_T v_read_chg_order_record(S_LOG_DATA *order_data, U32_T index)
{
// 检查索引号有效性
if (index == 0 || index > s_chgOrderMng.u32_lastIndex) {
return 1; // 索引号无效
}
// 如果没有记录,直接返回失败
if (s_chgOrderMng.u16_chgOrderCnt == 0) {
return 1;
}
// 计算存储位置(基于索引号的循环缓冲区)
U16_T storagePosition = (index - 1) % CHG_ORDER_MAX_CNT;
// 直接读取到传入的缓冲区,传递1-N范围的position
if (v_read_chg_order_record_internal(order_data, storagePosition + 1) == 0) {
// 检查索引是否匹配(防止数据被覆盖)
if (order_data->u32_index == index) {
return 0; // 成功
}
}
return 1; // 读取失败或索引不匹配
}
/**
* @brief 读取指定位置的充电订单记录(基于循环缓冲区位置)
*/
U8_T v_read_chg_order_by_position(S_LOG_DATA *order_data, U16_T position)
{
// 检查位置是否有效
if (position >= s_chgOrderMng.u16_chgOrderCnt) {
return 1;
}
// 计算实际Flash位置(考虑循环缓冲区)
U16_T actual_position;
if (s_chgOrderMng.u16_chgOrderCnt < CHG_ORDER_MAX_CNT) {
// 缓冲区未满,直接按顺序读取
actual_position = position;
} else {
// 缓冲区已满,需要计算循环位置
actual_position = (s_chgOrderMng.u16_currIndex + 1 + position) % CHG_ORDER_MAX_CNT;
}
// 传递1-N范围的position给内部函数
return v_read_chg_order_record_internal(order_data, actual_position + 1);
}
/**
* @brief 获取充电订单记录总数
*/
U16_T v_get_chg_order_count(void)
{
return s_chgOrderMng.u16_chgOrderCnt;
}
/**
* @brief 获取当前索引位置
*/
U16_T v_get_chg_order_curr_index(void)
{
return s_chgOrderMng.u16_currIndex;
}
/**
* @brief 获取最后分配的索引值
*/
U32_T v_get_chg_order_last_index(void)
{
return s_chgOrderMng.u32_lastIndex;
}
/***************************************************************
* EEPROM临时充电记录存储函数
***************************************************************/
/**
* @brief 获取指定枪号的EEPROM地址
*/
static U32_T v_get_eeprom_addr_for_gun(U8_T gunNo)
{
if (gunNo == 0) {
return EEPROM_ADDR_A_LOG_DATA;
} else {
return EEPROM_ADDR_B_LOG_DATA;
}
}
/**
* @brief 保存临时充电记录到EEPROM(用于断电续传)
*/
U8_T v_save_temp_chg_record_to_eeprom(U8_T gunNo, const S_LOG_DATA *order_data, E_TEMP_CHG_FLAG flag)
{
U32_T eeprom_addr = v_get_eeprom_addr_for_gun(gunNo);
U8_T *buffer = NULL;
U8_T ret = 1;
// 检查参数有效性
if (order_data == NULL || flag >= TEMP_CHG_FLAG_INVALID) {
return 1;
}
// 动态分配内存
buffer = (U8_T *)FLASH_MGR_MALLOC(TEMP_CHG_RECORD_SIZE + 1);
if (buffer == NULL) {
return 1; // 内存分配失败
}
// 构建缓冲区:标志位 + 订单数据
buffer[0] = (U8_T)flag;
memcpy(buffer + 1, order_data, TEMP_CHG_RECORD_SIZE);
// 写入EEPROMeeprom_write返回HAL_OK(0)表示成功
if (eeprom_write(eeprom_addr, buffer, TEMP_CHG_RECORD_SIZE + 1) == HAL_OK) {
ret = 0; // 成功
}
// 释放内存
FLASH_MGR_FREE(buffer);
return ret;
}
/**
* @brief 从EEPROM读取临时充电记录(用于断电续传恢复)
*/
U8_T v_read_temp_chg_record_from_eeprom(U8_T gunNo, S_LOG_DATA *order_data, E_TEMP_CHG_FLAG *flag)
{
U32_T eeprom_addr = v_get_eeprom_addr_for_gun(gunNo);
U8_T *buffer = NULL;
// 检查参数有效性
if (order_data == NULL) {
return 1;
}
// 动态分配内存
buffer = (U8_T *)FLASH_MGR_MALLOC(TEMP_CHG_RECORD_SIZE + 1);
if (buffer == NULL) {
return 1; // 内存分配失败
}
// 从EEPROM读取,eeprom_read返回HAL_OK(0)表示成功
if (eeprom_read(eeprom_addr, buffer, TEMP_CHG_RECORD_SIZE + 1) != HAL_OK) {
FLASH_MGR_FREE(buffer);
return 1; // 读取失败
}
// 检查标志位有效性
U8_T read_flag = buffer[0];
if (read_flag >= TEMP_CHG_FLAG_INVALID) {
FLASH_MGR_FREE(buffer);
return 1; // 无效标志位
}
// 返回标志位(如果提供了指针)
if (flag != NULL) {
*flag = (E_TEMP_CHG_FLAG)read_flag;
}
// 复制订单数据
memcpy(order_data, buffer + 1, TEMP_CHG_RECORD_SIZE);
// 释放内存
FLASH_MGR_FREE(buffer);
return 0; // 成功
}
/**
* @brief 清除EEPROM中的临时充电记录
*/
U8_T v_clear_temp_chg_record_in_eeprom(U8_T gunNo)
{
U32_T eeprom_addr = v_get_eeprom_addr_for_gun(gunNo);
U8_T invalid_flag = TEMP_CHG_FLAG_INVALID;
// 只写入无效标志位,不清除整个区域(节省EEPROM写寿命)
// eeprom_write返回HAL_OK(0)表示成功
if (eeprom_write(eeprom_addr, &invalid_flag, 1) == HAL_OK) {
return 0; // 成功
}
return 1; // 失败
}
/**
* @brief 检查EEPROM中是否有有效的临时充电记录
*/
U8_T v_check_temp_chg_record_valid(U8_T gunNo)
{
U32_T eeprom_addr = v_get_eeprom_addr_for_gun(gunNo);
U8_T flag_byte;
// 读取标志位,eeprom_read返回HAL_OK(0)表示成功
if (eeprom_read(eeprom_addr, &flag_byte, 1) != HAL_OK) {
return 0; // 读取失败,视为无效
}
// 检查标志位是否有效
if (flag_byte == TEMP_CHG_FLAG_UNSETTLED || flag_byte == TEMP_CHG_FLAG_SETTLED) {
return 1; // 有有效记录
}
return 0; // 无效记录
}
@@ -0,0 +1,185 @@
/**
* @file meter_calculate_flash_impl.h
* @brief 充电订单 / 临时订单 EEPROM+SPI Flash 存储接口声明
*
* 与 `meter_calculate_flash_impl.c` 配套:管理元数据在 EEPROM,订单记录在 `DATAFLASH_HIS_RECORD_ADDR`
* 区域;并提供双枪临时订单 EEPROM 备份与恢复接口。具体地址与记录格式见 `public_define.h`、`flash_external_data.h`。
*/
#ifndef METER_CALCULATE_FLASH_IMPL_H
#define METER_CALCULATE_FLASH_IMPL_H
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
extern "C" {
#endif
/*------ Exported includes(头文件) ----------------------*/
#include "main.h"
#include "meter_calculate/meter_calculate_impl.h" /* S_LOG_DATA 等 */
#include "eeprom/fm24cl16.h"
#include "externalflash/flash_external_data.h"
/*------ Exported macro(宏定义) -------------------------*/
// 调试宏定义
// #define DEBUG_FLASH_READ 0 // 启用Flash读取调试
// #define DEBUG_FLASH_WRITE 0 // 启用Flash写入调试
/* 充电订单记录容量相关宏(CHG_ORDER_* / TEMP_CHG_RECORD_SIZE
* 统一由 `BSP/spi_Flash/flash_external_data.h` 提供,避免多处重复定义。
*/
// 断电续传标志位定义
typedef enum
{
TEMP_CHG_FLAG_UNSETTLED = 0x00, // 未结算状态(充电中)
TEMP_CHG_FLAG_SETTLED = 0x01, // 已结算状态(充电完成)
TEMP_CHG_FLAG_INVALID = 0xFF // 无效状态
} E_TEMP_CHG_FLAG;
/*------ Exported struct(结构体定义) --------------------*/
/**
* @brief 充电订单管理信息结构体
*
* 存储在EEPROM中,地址:EEPROM_ADDR_CHGRCD_MNG (0x0000)
* 用于管理充电订单的索引和计数信息
*/
typedef struct
{
U16_T u16_head; // 头标志 (0x5AA5)
U16_T u16_chgOrderCnt; // 充电订单总条数
U16_T u16_currIndex; // 当前索引(循环缓冲区中的位置)
U32_T u32_lastIndex; // 最后分配的索引值(保证索引唯一性递增)
} S_CHG_ORDER_MNG;
/*------ Exported variables(变量定义) -------------------*/
// 充电订单管理信息全局变量
extern S_CHG_ORDER_MNG s_chgOrderMng;
/*------ Exported function prototypes (函数声明) -------*/
/**
* @brief 初始化充电订单Flash存储模块
*
* 从EEPROM读取管理信息,如果不存在则初始化默认值
*/
void v_chg_order_flash_init(void);
/**
* @brief 写入充电订单管理信息到EEPROM
*
* @param chgOrderMng 充电订单管理信息指针
*/
void v_write_eeprom_chgOrderMng(S_CHG_ORDER_MNG *chgOrderMng);
/**
* @brief 从EEPROM读取充电订单管理信息
*
* @param chgOrderMng 充电订单管理信息指针(用于存储读取到的数据)
*/
void v_read_eeprom_chgOrderMng(S_CHG_ORDER_MNG *chgOrderMng);
/**
* @brief 清除所有充电订单记录
*
* 重置管理信息并清除Flash中的记录
*/
void v_chg_order_clear_all(void);
/**
* @brief 保存充电订单记录到Flash
*
* 自动分配索引并保存记录,更新管理信息
* 注意:此函数会修改order_data中的u32_index字段
*
* @param order_data 充电订单数据指针(会被修改)
* @return U32_T 分配的索引值(唯一标识)
*/
U32_T u32_chg_order_save_record(S_LOG_DATA *order_data);
/**
* @brief 从Flash读取指定索引的充电订单记录
*
* @param order_data 充电订单数据指针(用于存储读取到的数据)
* @param index 要读取的记录索引
* @return U8_T 读取结果:0-成功,1-失败
*/
U8_T v_read_chg_order_record(S_LOG_DATA *order_data, U32_T index);
/**
* @brief 获取充电订单记录总数
*
* @return U16_T 充电订单记录总数
*/
U16_T v_get_chg_order_count(void);
/**
* @brief 获取当前索引位置
*
* @return U16_T 当前索引位置
*/
U16_T v_get_chg_order_curr_index(void);
/**
* @brief 获取最后分配的索引值
*
* @return U32_T 最后分配的索引值(唯一递增)
*/
U32_T v_get_chg_order_last_index(void);
/**
* @brief 读取指定位置的充电订单记录(基于循环缓冲区位置)
*
* @param order_data 充电订单数据指针(用于存储读取到的数据)
* @param position 循环缓冲区中的位置(0-based)
* @return U8_T 读取结果:0-成功,1-失败
*/
U8_T v_read_chg_order_by_position(S_LOG_DATA *order_data, U16_T position);
/**
* @brief 保存临时充电记录到EEPROM(用于断电续传)
*
* 在充电过程中周期更新实时充电信息,第一个字节为断电续传标志位
*
* @param gunNo 枪号(0-A枪,1-B枪)
* @param order_data 充电订单数据指针
* @param flag 断电续传标志位(0-未结算,1-已结算)
* @return U8_T 保存结果:0-成功,1-失败
*/
U8_T v_save_temp_chg_record_to_eeprom(U8_T gunNo, const S_LOG_DATA *order_data, E_TEMP_CHG_FLAG flag);
/**
* @brief 从EEPROM读取临时充电记录(用于断电续传恢复)
*
* @param gunNo 枪号(0-A枪,1-B枪)
* @param order_data 充电订单数据指针(用于存储读取到的数据)
* @param flag 读取到的断电续传标志位指针(可选)
* @return U8_T 读取结果:0-成功,1-失败
*/
U8_T v_read_temp_chg_record_from_eeprom(U8_T gunNo, S_LOG_DATA *order_data, E_TEMP_CHG_FLAG *flag);
/**
* @brief 清除EEPROM中的临时充电记录
*
* 将断电续传标志位设置为无效状态
*
* @param gunNo 枪号(0-A枪,1-B枪)
* @return U8_T 清除结果:0-成功,1-失败
*/
U8_T v_clear_temp_chg_record_in_eeprom(U8_T gunNo);
/**
* @brief 检查EEPROM中是否有有效的临时充电记录
*
* @param gunNo 枪号(0-A枪,1-B枪)
* @return U8_T 检查结果:0-无有效记录,1-有有效记录
*/
U8_T v_check_temp_chg_record_valid(U8_T gunNo);
#ifdef __cplusplus
}
#endif
#endif /* METER_CALCULATE_FLASH_IMPL_H */
@@ -0,0 +1,570 @@
/**
* @file ocpp_mv_offline_flash_impl.c
* @brief OCPP 离线 MeterValuesSPI Flash 线性池 + FRAM 流水号映射(实现仿 fault_flash_impl
*
* =============================================================================
* 本文件按功能划分为以下模块(自上而下阅读):
* -----------------------------------------------------------------------------
* [1] 编译期检查、全局变量与文件内静态资源
* [2] FRAM 管理块:CRC 计算、默认值、读/写 EEPROM、上电初始化
* [3] Flash 池寻址:枪半区基址、该枪序号 ↔ 槽、槽 ↔ 线性字节地址
* [4] Flash 单槽访问:组帧/校验、按物理槽读一条、按物理槽写一条(含扇区擦除策略)
* [5] FRAM 映射表:按流水号查找行、查找空行
* [6] 对外接口:池容量、负载合法性、保存/按序号读取 MV
* [7] 对外:映射表查询/删除;整池擦除仅本文件 static
* =============================================================================
*/
#include "ocpp_mv_offline_flash_impl.h"
#include "unsettled_order_mng.h"
#include "publicdata/publicdata.h"
#include <string.h>
/* -------------------------------------------------------------------------- */
/* [1] 编译期检查、全局变量与文件内静态资源 */
/* -------------------------------------------------------------------------- */
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
_Static_assert(OCPP_MV_OFFLINE_RECORDS_PER_SECTOR > 0u, "records per sector");
_Static_assert(OCPP_MV_OFFLINE_POOL_MAX_PER_GUN > 0u, "pool max per gun");
#else
typedef int __ocpp_mv_check_c1[(OCPP_MV_OFFLINE_RECORDS_PER_SECTOR > 0u) ? 1 : -1];
typedef int __ocpp_mv_check_c2[(OCPP_MV_OFFLINE_POOL_MAX_PER_GUN > 0u) ? 1 : -1];
#endif
/**
* @var s_ocppMvOfflineFramMng
* @brief OCPP 离线 MV 的 FRAM 管理块在 RAM 中的镜像
* @details
* - 与 FM24 地址 `EEPROM_ADDR_OCPP_MV_OFFLINE_CTRL`0x0700)对应,长度为 128 字节。
* - 含 `u32_next_seq[2]`(每枪下一条单调序号)及最多 `OCPP_MV_OFFLINE_MAP_ROWS` 行流水号+枪映射。
* - 上电由 `v_ocpp_mv_offline_init` 通过 `u8_ocpp_mv_offline_fram_read` 装载;业务写入 MV 后
* 由 `u8_ocpp_mv_offline_fram_write` 回写 FRAM,掉电保持序号与映射连续性。
*/
static S_OCPP_MV_OFFLINE_FRAM_MNG s_ocppMvOfflineFramMng;
/**
* @var s_ocpp_mv_frame_buf
* @brief 单条 Flash 记录在 RAM 中的组帧缓冲(静态、本文件内可见)
* @details 布局与 `fault_flash_impl` 一致:`[0]` 帧头、`[1..64]` 负载、`[65]` CRC 低 8 位,
* 总长 `OCPP_MV_OFFLINE_FRAME_BYTES`34 = 1+32+1)。供底层按物理槽读/写时复用,非重入。
*/
static U8_T s_ocpp_mv_frame_buf[OCPP_MV_OFFLINE_FRAME_BYTES];
/* -------------------------------------------------------------------------- */
/* [2] FRAM 管理块:CRC、默认值、读/写 EEPROM、上电初始化 */
/* -------------------------------------------------------------------------- */
/**
* @fn u16_ocpp_mv_fram_crc
* @brief 计算 FRAM 管理块整结构的 CRC-16(算法与未结算模块 `u16_calculate_crc` 一致)
* @param[in] p 待计算的管理块指针(只读)
* @return 16 位 CRC 值
* @note 计算前将副本中 `u16_crc` 置 0,再对整块字节流做 CRC,与写入 EEPROM 时的规则一致。
*/
static U16_T u16_ocpp_mv_fram_crc(const S_OCPP_MV_OFFLINE_FRAM_MNG *p)
{
S_OCPP_MV_OFFLINE_FRAM_MNG z;
(void)memcpy(&z, p, sizeof(z));
z.u16_crc = 0;
return u16_calculate_crc((const U8_T *)&z, (U16_T)sizeof(z));
}
/**
* @fn v_ocpp_mv_offline_fram_default
* @brief 将 FRAM 管理块置为出厂/安全默认并填入合法 CRC(不写 EEPROM)
* @param[in,out] mng 目标结构体指针;为 NULL 时直接返回
* @details 清零后设置 `u16_head`、`u16_version``u32_next_seq[]` 与映射表全 0。
*/
static void v_ocpp_mv_offline_fram_default(S_OCPP_MV_OFFLINE_FRAM_MNG *mng)
{
if (mng == NULL) {
return;
}
(void)memset(mng, 0, sizeof(*mng));
mng->u16_head = OCPP_MV_OFFLINE_FRAM_HEAD;
mng->u16_version = (U16_T)OCPP_MV_OFFLINE_FRAM_MNG_VER;
mng->u16_crc = u16_ocpp_mv_fram_crc(mng);
}
/**
* @fn u8_ocpp_mv_offline_fram_read
* @brief 从 FRAM(FM24)读取管理块到调用方缓冲区,并校验头与 CRC
* @param[out] out 输出缓冲区,不可为 NULL
* @return 0 读取且校验成功;1 空指针;2 I2C 读失败(已写入默认块到 out);3 自愈写 EEPROM 失败
* @details
* - 若头标记、版本号或 CRC 不匹配,则调用 `v_ocpp_mv_offline_fram_default` 填充 out
* 并尝试写回 EEPROM 以自愈坏块。
*/
static U8_T u8_ocpp_mv_offline_fram_read(S_OCPP_MV_OFFLINE_FRAM_MNG *out)
{
S_OCPP_MV_OFFLINE_FRAM_MNG tmp;
U16_T crc_expect;
if (out == NULL) {
return 1u;
}
if (eeprom_read((S32_T)EEPROM_ADDR_OCPP_MV_OFFLINE_CTRL, (U8_T *)&tmp, (S32_T)sizeof(tmp)) != HAL_OK) {
v_ocpp_mv_offline_fram_default(out);
return 2u;
}
crc_expect = u16_ocpp_mv_fram_crc(&tmp);
if (tmp.u16_head != OCPP_MV_OFFLINE_FRAM_HEAD || tmp.u16_version != (U16_T)OCPP_MV_OFFLINE_FRAM_MNG_VER
|| tmp.u16_crc != crc_expect) {
v_ocpp_mv_offline_fram_default(out);
if (eeprom_write((S32_T)EEPROM_ADDR_OCPP_MV_OFFLINE_CTRL, (U8_T *)out, (S32_T)sizeof(*out)) != HAL_OK) {
return 3u;
}
return 0u;
}
(void)memcpy(out, &tmp, sizeof(tmp));
return 0u;
}
/**
* @fn u8_ocpp_mv_offline_fram_write
* @brief 将管理块写入 FRAM,并自动重算 `u16_crc` 后落盘
* @param[in] mng 待写入的管理块指针;为 NULL 时返回错误
* @return 0 成功;1 空指针;2 EEPROM 写失败
*/
static U8_T u8_ocpp_mv_offline_fram_write(const S_OCPP_MV_OFFLINE_FRAM_MNG *mng)
{
S_OCPP_MV_OFFLINE_FRAM_MNG tmp;
if (mng == NULL) {
return 1u;
}
(void)memcpy(&tmp, mng, sizeof(tmp));
tmp.u16_crc = u16_ocpp_mv_fram_crc(&tmp);
if (eeprom_write((S32_T)EEPROM_ADDR_OCPP_MV_OFFLINE_CTRL, (U8_T *)&tmp, (S32_T)sizeof(tmp)) != HAL_OK) {
return 2u;
}
return 0u;
}
/**
* @fn v_ocpp_mv_offline_init
* @brief 模块上电初始化:从 FRAM 恢复 `s_ocppMvOfflineFramMng`
* @details 若读失败或数据非法,则写入默认管理块,保证后续 `mv_save` 有合法序号与空映射表。
*/
void v_ocpp_mv_offline_init(void)
{
if (u8_ocpp_mv_offline_fram_read(&s_ocppMvOfflineFramMng) != 0u) {
v_ocpp_mv_offline_fram_default(&s_ocppMvOfflineFramMng);
(void)u8_ocpp_mv_offline_fram_write(&s_ocppMvOfflineFramMng);
}
}
/* -------------------------------------------------------------------------- */
/* [3] Flash 池寻址:枪半区基址、该枪单调序号 ↔ 槽、槽 ↔ 线性地址 */
/* -------------------------------------------------------------------------- */
/**
* @fn u32_ocpp_mv_gun_half_base
* @brief 指定枪在 SPI Flash 上的半区首字节地址(枪0=前半 32KB,枪1=后半 32KB
*/
static U32_T u32_ocpp_mv_gun_half_base(U8_T gun)
{
return DATAFLASH_OCPP_MV_OFFLINE_ADDR + (U32_T)gun * DATAFLASH_OCPP_MV_OFFLINE_HALF_SIZE;
}
/**
* @fn u32_gun_seq_to_slot
* @brief 该枪单调序号映射为该枪半区内物理槽(环形覆盖)
*/
static U32_T u32_gun_seq_to_slot(U32_T seq)
{
return (U32_T)(seq % (U32_T)OCPP_MV_OFFLINE_POOL_MAX_PER_GUN);
}
/**
* @fn u32_gun_slot_to_addr
* @brief 枪号 + 该枪半区内槽索引 → 线性字节地址
*/
static U32_T u32_gun_slot_to_addr(U8_T gun, U32_T slot_in_gun)
{
U32_T rps = (U32_T)OCPP_MV_OFFLINE_RECORDS_PER_SECTOR;
U32_T sec_in_gun = slot_in_gun / rps;
U32_T pos = slot_in_gun % rps;
U32_T sec_base = u32_ocpp_mv_gun_half_base(gun) + sec_in_gun * SPI_SECTOR_SIZE;
return sec_base + pos * (U32_T)OCPP_MV_OFFLINE_FRAME_BYTES;
}
/* -------------------------------------------------------------------------- */
/* [4] Flash 单槽访问:读一条、写一条(扇区擦除策略同 fault) */
/* -------------------------------------------------------------------------- */
/**
* @fn u8_ocpp_mv_read_at_gun_slot
* @brief 从指定枪半区内槽读取一条 MV 负载(校验帧头与 CRC)
*/
static U8_T u8_ocpp_mv_read_at_gun_slot(U8_T gun, U32_T slot_in_gun, S_OCPP_MV_OFFLINE_MV *mv)
{
U32_T addr = u32_gun_slot_to_addr(gun, slot_in_gun);
if (mv == NULL) {
return 1u;
}
if (s32_flash_dataflash_read(addr, s_ocpp_mv_frame_buf, (U32_T)OCPP_MV_OFFLINE_FRAME_BYTES) != 0u) {
return 2u;
}
if (s_ocpp_mv_frame_buf[0] != RECORD_HEADER) {
return 3u;
}
if (s_ocpp_mv_frame_buf[1u + sizeof(S_OCPP_MV_OFFLINE_MV)] !=
(U8_T)u16_crc_checksum(s_ocpp_mv_frame_buf + 1u, (U16_T)sizeof(S_OCPP_MV_OFFLINE_MV))) {
return 3u;
}
(void)memcpy(mv, s_ocpp_mv_frame_buf + 1u, sizeof(S_OCPP_MV_OFFLINE_MV));
return 0u;
}
/**
* @fn u8_ocpp_mv_write_at_gun_slot
* @brief 向指定枪半区内槽写入一条 MV(组帧、按扇区擦除后写入)
* @details 扇区首槽擦扇区;非首槽若本扇区 0 槽无效则先擦整扇区(同 fault 策略)。
*/
static U8_T u8_ocpp_mv_write_at_gun_slot(U8_T gun, U32_T slot_in_gun, const S_OCPP_MV_OFFLINE_MV *mv)
{
U32_T rps = (U32_T)OCPP_MV_OFFLINE_RECORDS_PER_SECTOR;
U32_T sec_in_gun = slot_in_gun / rps;
U32_T pos = slot_in_gun % rps;
U32_T sec_base = u32_ocpp_mv_gun_half_base(gun) + sec_in_gun * SPI_SECTOR_SIZE;
U32_T addr = sec_base + pos * (U32_T)OCPP_MV_OFFLINE_FRAME_BYTES;
U32_T slot0_in_sector = sec_in_gun * rps;
S_OCPP_MV_OFFLINE_MV dummy;
if (mv == NULL) {
return 1u;
}
/* 非扇区首槽:仅在本扇区尚无其它有效记录时擦除(避免 seq 20 写入后 seq 21 因 slot0 空再次擦扇区抹掉 20) */
if (pos != 0u) {
if (u8_ocpp_mv_read_at_gun_slot(gun, slot0_in_sector, &dummy) != 0u) {
U32_T s;
U8_T sector_has_data = 0u;
for (s = slot0_in_sector + 1u; s < slot_in_gun; s++) {
if (u8_ocpp_mv_read_at_gun_slot(gun, s, &dummy) == 0u) {
sector_has_data = 1u;
break;
}
}
if (sector_has_data == 0u) {
(void)s32_flash_dataflash_erase_sector(sec_base);
}
}
}
s_ocpp_mv_frame_buf[0] = RECORD_HEADER;
(void)memcpy(s_ocpp_mv_frame_buf + 1u, mv, sizeof(S_OCPP_MV_OFFLINE_MV));
s_ocpp_mv_frame_buf[1u + sizeof(S_OCPP_MV_OFFLINE_MV)] =
(U8_T)u16_crc_checksum((U8_T *)mv, (U16_T)sizeof(S_OCPP_MV_OFFLINE_MV));
if (pos == 0u) {
(void)s32_flash_dataflash_erase_sector(sec_base);
}
return s32_flash_dataflash_write(addr, s_ocpp_mv_frame_buf, (U32_T)OCPP_MV_OFFLINE_FRAME_BYTES);
}
/* -------------------------------------------------------------------------- */
/* [5] FRAM 映射表:按会话键(当前为开始充电秒 + hi=0)查找、查找空行 */
/* -------------------------------------------------------------------------- */
/**
* @fn p_ocpp_mv_map_find_row
* @brief 在映射表中查找与给定会话键(`u32_tx_sn_lo` / `u32_tx_sn_hi`)及枪号完全匹配的一行
*/
static S_OCPP_MV_OFFLINE_MAP_ROW *p_ocpp_mv_map_find_row(U32_T tx_sn_lo, U32_T tx_sn_hi, U8_T gun)
{
U32_T i;
for (i = 0u; i < (U32_T)OCPP_MV_OFFLINE_MAP_ROWS; i++) {
S_OCPP_MV_OFFLINE_MAP_ROW *r = &s_ocppMvOfflineFramMng.aMap[i];
if (r->u32_tx_sn_lo == tx_sn_lo && r->u32_tx_sn_hi == tx_sn_hi && r->u8_gun_no == gun) {
return r;
}
}
return NULL;
}
/**
* @fn p_ocpp_mv_map_find_empty
* @brief 在映射表中查找第一个空闲行(约定:`u32_tx_sn_lo` 与 `u32_tx_sn_hi` 均为 0 表示未使用)
* @return 空闲行指针;若无空行返回 NULL(表示已达映射行上限)
*/
static S_OCPP_MV_OFFLINE_MAP_ROW *p_ocpp_mv_map_find_empty(void)
{
U32_T i;
for (i = 0u; i < (U32_T)OCPP_MV_OFFLINE_MAP_ROWS; i++) {
S_OCPP_MV_OFFLINE_MAP_ROW *r = &s_ocppMvOfflineFramMng.aMap[i];
if (r->u32_tx_sn_lo == 0u && r->u32_tx_sn_hi == 0u) {
return r;
}
}
return NULL;
}
/**
* @brief 映射表满时,复用指定枪最旧一行(seq_start 最小)
* @details 起充不再批量 clear_gun;满表时仅在新会话首条 MV 前淘汰一行,避免 “map full” 丢采样
*/
static S_OCPP_MV_OFFLINE_MAP_ROW *p_ocpp_mv_map_evict_oldest_gun(U8_T gun)
{
U32_T i;
S_OCPP_MV_OFFLINE_MAP_ROW *best = NULL;
for (i = 0u; i < (U32_T)OCPP_MV_OFFLINE_MAP_ROWS; i++) {
S_OCPP_MV_OFFLINE_MAP_ROW *r = &s_ocppMvOfflineFramMng.aMap[i];
if (r->u8_gun_no != gun || (r->u32_tx_sn_lo == 0u && r->u32_tx_sn_hi == 0u)) {
continue;
}
if (best == NULL || r->u32_seq_start < best->u32_seq_start) {
best = r;
}
}
if (best != NULL) {
(void)memset(best, 0, sizeof(*best));
}
return best;
}
/* -------------------------------------------------------------------------- */
/* [6] 对外:负载校验、保存 MV、按枪序号读取 MV */
/* -------------------------------------------------------------------------- */
/**
* @fn u8_ocpp_mv_offline_mv_is_valid
* @brief 判断内存中的一条 MV 负载是否带有合法魔数与版本号(不读 Flash)
* @param[in] mv 负载指针;NULL 视为无效
* @return 1 有效;0 无效
*/
U8_T u8_ocpp_mv_offline_mv_is_valid(const S_OCPP_MV_OFFLINE_MV *mv)
{
if (mv == NULL) {
return 0u;
}
if (mv->u32_magic != OCPP_MV_OFFLINE_FLASH_MAGIC) {
return 0u;
}
if (mv->u16_version != (U16_T)OCPP_MV_OFFLINE_FLASH_VER) {
return 0u;
}
return 1u;
}
/**
* @brief 为指定会话键在该枪半区追加保存一条离线 MV:分配序号、写 Flash、更新 FRAM
* @param[in] tx_sn_lo 会话键:当前为 `xDate2Seconds(StartChargeTime)`(单路 U32
* @param[in] tx_sn_hi 当前恒 0(与 FRAM 行 `u32_tx_sn_hi` 一致)
* @param[in] u8_gun_no 枪号 0 或 1
* @param[out] out_seq 本次在该枪下的单调序号(与物理槽 `seq % POOL_MAX_PER_GUN` 对应)
* @details
* - `seq` 取当前 `u32_next_seq[u8_gun_no]`;已存在映射行时要求 `seq == seq_start + count`。
* - Flash 写失败时回滚 RAM 中对映射行的修改,不推进该枪的 `u32_next_seq`。
*/
U8_T u8_ocpp_mv_offline_mv_save(U8_T u8_gun_no, U32_T tx_sn_lo, U32_T tx_sn_hi, const S_OCPP_MV_OFFLINE_MV *mv,
U32_T *out_seq)
{
S_OCPP_MV_OFFLINE_MV wr;
S_OCPP_MV_OFFLINE_MAP_ROW *row;
S_OCPP_MV_OFFLINE_MAP_ROW *empty;
U32_T seq;
U32_T slot;
U8_T ret;
if (mv == NULL || out_seq == NULL) {
return 1u;
}
if (u8_gun_no >= OCPP_MV_OFFLINE_GUN_CNT) {
return 1u;
}
(void)memcpy(&wr, mv, sizeof(wr));
if (wr.u32_magic != OCPP_MV_OFFLINE_FLASH_MAGIC) {
wr.u32_magic = OCPP_MV_OFFLINE_FLASH_MAGIC;
}
if (wr.u16_version != (U16_T)OCPP_MV_OFFLINE_FLASH_VER) {
wr.u16_version = (U16_T)OCPP_MV_OFFLINE_FLASH_VER;
}
seq = s_ocppMvOfflineFramMng.u32_next_seq[u8_gun_no];
row = p_ocpp_mv_map_find_row(tx_sn_lo, tx_sn_hi, u8_gun_no);
if (row != NULL) {
if (seq != row->u32_seq_start + (U32_T)row->u16_count) {
return 5u;
}
row->u16_count++;
} else {
empty = p_ocpp_mv_map_find_empty();
if (empty == NULL) {
empty = p_ocpp_mv_map_evict_oldest_gun(u8_gun_no);
if (empty == NULL) {
return 2u;
}
}
empty->u32_tx_sn_lo = tx_sn_lo;
empty->u32_tx_sn_hi = tx_sn_hi;
empty->u32_seq_start = seq;
empty->u16_count = 1u;
empty->u8_gun_no = u8_gun_no;
empty->u8_reserved = 0u;
}
slot = u32_gun_seq_to_slot(seq);
ret = u8_ocpp_mv_write_at_gun_slot(u8_gun_no, slot, &wr);
if (ret != 0u) {
if (row != NULL) {
row->u16_count--;
} else {
empty = p_ocpp_mv_map_find_row(tx_sn_lo, tx_sn_hi, u8_gun_no);
if (empty != NULL) {
(void)memset(empty, 0, sizeof(*empty));
}
}
return 3u;
}
s_ocppMvOfflineFramMng.u32_next_seq[u8_gun_no] = seq + 1u;
*out_seq = seq;
ret = u8_ocpp_mv_offline_fram_write(&s_ocppMvOfflineFramMng);
if (ret != 0u) {
return 4u;
}
return 0u;
}
/**
* @brief 按该枪单调序号读取一条 MV`seq % POOL_MAX_PER_GUN`
*/
U8_T u8_ocpp_mv_offline_mv_read_by_seq(U8_T u8_gun_no, U32_T seq, S_OCPP_MV_OFFLINE_MV *out)
{
U32_T slot;
if (out == NULL) {
return 1u;
}
if (u8_gun_no >= OCPP_MV_OFFLINE_GUN_CNT) {
return 1u;
}
slot = u32_gun_seq_to_slot(seq);
return u8_ocpp_mv_read_at_gun_slot(u8_gun_no, slot, out);
}
/* -------------------------------------------------------------------------- */
/* [7] 对外:映射表查询/删除、整池擦除 */
/* -------------------------------------------------------------------------- */
/**
* @fn u8_ocpp_mv_offline_map_get
* @brief 根据会话键查询其在池中的连续序号区间(供上送循环 `seq_start .. seq_start+count-1`
* @param[in] tx_sn_lo 会话键(当前为开始充电秒)
* @param[in] tx_sn_hi 当前恒 0
* @param[out] seq_start 输出该区间的首条全局序号
* @param[out] count 输出连续条数
* @return 0 找到;1 空指针;2 未找到对应键
*/
U8_T u8_ocpp_mv_offline_map_get(U8_T u8_gun_no, U32_T tx_sn_lo, U32_T tx_sn_hi, U32_T *seq_start, U16_T *count)
{
S_OCPP_MV_OFFLINE_MAP_ROW *row;
if (seq_start == NULL || count == NULL) {
return 1u;
}
if (u8_gun_no >= OCPP_MV_OFFLINE_GUN_CNT) {
return 1u;
}
row = p_ocpp_mv_map_find_row(tx_sn_lo, tx_sn_hi, u8_gun_no);
if (row == NULL) {
return 2u;
}
*seq_start = row->u32_seq_start;
*count = row->u16_count;
return 0u;
}
/**
* @fn u8_ocpp_mv_offline_map_remove
* @brief 上送完成后删除指定会话键的映射行(不自动擦除 Flash 池中对应物理数据)
* @param[in] tx_sn_lo 会话键(当前为开始充电秒)
* @param[in] tx_sn_hi 当前恒 0
* @return 0 成功删除并已写回 FRAM;1 未找到该键;2 FRAM 写失败
* @note 若需同时清空整池 Flash,可调用本文件内静态函数 `v_ocpp_mv_offline_flash_erase_pool`(维护/调试用)。
*/
U8_T u8_ocpp_mv_offline_map_remove(U8_T u8_gun_no, U32_T tx_sn_lo, U32_T tx_sn_hi)
{
S_OCPP_MV_OFFLINE_MAP_ROW *row;
if (u8_gun_no >= OCPP_MV_OFFLINE_GUN_CNT) {
return 1u;
}
row = p_ocpp_mv_map_find_row(tx_sn_lo, tx_sn_hi, u8_gun_no);
if (row == NULL) {
return 1u;
}
(void)memset(row, 0, sizeof(*row));
return u8_ocpp_mv_offline_fram_write(&s_ocppMvOfflineFramMng);
}
/**
* @fn u8_ocpp_mv_offline_map_clear_gun
* @brief 新一笔充电开始前,清除指定枪在 FRAM 中的全部离线 MV 映射行
*
* @details
* - **作用对象**:仅 `s_ocppMvOfflineFramMng.aMap[]`(会话键 → Flash 全局序号区间),**不擦除** SPI Flash 池内已写入的 MV 物理记录;池区仍按环形序号覆盖写入。
* - **调用时机**:维护/调试;正常起充不再批量调用(同枪多笔离线充须保留各行映射)。映射满时由 `mv_save` 按枪淘汰最旧一行。
* - **与 `u8_ocpp_mv_offline_map_remove` 区别**`map_remove` 在**单笔**离线 MV 上送完成后按会话键删一行;本函数在**起充边沿**按枪批量清空,不区分 `tx_sn_lo`/`tx_sn_hi`。
* - **上送侧**:清空后本枪需重新落盘才会建立新映射;进行中的 `session` 补发状态机在桥接层会随起充一并 `memset` 复位。
*
* @param[in] u8_gun_no 枪索引 `0 .. OCPP_MV_OFFLINE_GUN_CNT-1`
* @return 0 成功(含本枪本无映射行、无需写 FRAM);1 枪号非法;2 有行被清但 FRAM 写回失败
*/
U8_T u8_ocpp_mv_offline_map_clear_gun(U8_T u8_gun_no)
{
U32_T i;
U8_T changed = 0u;
if (u8_gun_no >= OCPP_MV_OFFLINE_GUN_CNT) {
return 1u;
}
for (i = 0u; i < (U32_T)OCPP_MV_OFFLINE_MAP_ROWS; i++) {
S_OCPP_MV_OFFLINE_MAP_ROW *r = &s_ocppMvOfflineFramMng.aMap[i];
if (r->u8_gun_no == u8_gun_no && (r->u32_tx_sn_lo != 0u || r->u32_tx_sn_hi != 0u)) {
(void)memset(r, 0, sizeof(*r));
changed = 1u;
}
}
if (changed == 0u) {
return 0u;
}
if (u8_ocpp_mv_offline_fram_write(&s_ocppMvOfflineFramMng) != 0u) {
return 2u;
}
return 0u;
}
/**
* @fn v_ocpp_mv_offline_flash_erase_pool
* @brief 擦除离线 MV 专用分区内的全部物理扇区(整池 Flash 清空)
* @param[in] also_reset_fram 非 0 时同时将 `s_ocppMvOfflineFramMng` 置默认并写回 FRAM(序号与映射清零)
* @details 遍历 `DATAFLASH_OCPP_MV_OFFLINE_SECTOR_CNT`,对每扇区基址调用 `s32_flash_dataflash_erase_sector`。
*/
static void v_ocpp_mv_offline_flash_erase_pool(U8_T also_reset_fram)
{
U32_T i;
for (i = 0u; i < DATAFLASH_OCPP_MV_OFFLINE_SECTOR_CNT; i++) {
(void)s32_flash_dataflash_erase_sector(DATAFLASH_OCPP_MV_OFFLINE_ADDR + i * SPI_SECTOR_SIZE);
}
if (also_reset_fram != 0u) {
v_ocpp_mv_offline_fram_default(&s_ocppMvOfflineFramMng);
(void)u8_ocpp_mv_offline_fram_write(&s_ocppMvOfflineFramMng);
}
}
@@ -0,0 +1,195 @@
/**
* @file ocpp_mv_offline_flash_impl.h
* @brief OCPP 离线 MeterValues:仿 `fault_flash_impl` — SPI Flash 线性池 + FRAM 流水号→序号映射
*
* **为何更简单**
* - Flash 侧为「定长记录数组」:每条在 **该枪半区** 内逻辑序号与物理槽一一对应(`seq` → 地址由宏公式算出)。
* - FRAM 侧存:**每枪下一单调序号** `u32_next_seq[2]`、以及若干行「流水号(tx)+枪号 ↔ 起始序号 + 条数」;
* 上送时 `u8_ocpp_mv_offline_map_get(gun, tx...)` 得 `seq_start,count`,再
* `for (k=0;k<count;k++) u8_ocpp_mv_offline_mv_read_by_seq(gun, seq_start+k, &buf)`。
*
* **Flash 分区(双枪互不穿插)**
* - 区间 `0x2C0000`~`0x2CFFFF`64KB)见 `flash_external_data.h`**前半 32KB 仅枪0、后半 32KB 仅枪1**
* 保证连续 Flash 地址均为同一把枪的环形池;双枪同时充电时不会在物理上交错两条订单的 MV。
*
* **Flash 格式(与故障记录一致思路)**
* - 每条:`RECORD_HEADER`(1B) + `S_OCPP_MV_OFFLINE_MV`(32B) + CRC8低字节(1B),总长 `OCPP_MV_OFFLINE_FRAME_BYTES`。
* - 扇区内可容纳条数 = `SPI_SECTOR_SIZE / OCPP_MV_OFFLINE_FRAME_BYTES`(向下取整,尾部截断不用)。
* - **每枪**最大条数 = `OCPP_MV_OFFLINE_POOL_MAX_PER_GUN`(每枪 8 扇区 × 每扇区条数)。
*
* **环形滚动(按枪)**
* - 每枪单调序号 `u32_next_seq[gun]`(存 FRAM):`seq = next_seq[gun]++`;物理槽 `idx = seq % POOL_MAX_PER_GUN`(仅在该枪半区内取模)。
* - 池满后自然覆盖该枪最旧物理槽;上送完成后应 `u8_ocpp_mv_offline_map_remove(gun, tx...)` 删除对应行。
*/
#ifndef OCPP_MV_OFFLINE_FLASH_IMPL_H
#define OCPP_MV_OFFLINE_FLASH_IMPL_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
#include "externalflash/flash_external_data.h"
#include "eeprom/fm24cl16.h"
/** @brief 单包 MeterValues 负载长度(字节),与结构体 `S_OCPP_MV_OFFLINE_MV` 一致(与 BS_ocpp_ctrl 已实现 measurand 对齐) */
#define OCPP_MV_OFFLINE_PAYLOAD_BYTES 32u
/** @brief 单条落盘帧长:帧头 + 负载 + CRC1(与 `fault_flash_impl` 相同思路) */
#define OCPP_MV_OFFLINE_FRAME_BYTES (1u + OCPP_MV_OFFLINE_PAYLOAD_BYTES + 1u)
/** @brief 每个物理扇区内可容纳的 MV 条数(4096 / FRAME,向下取整,尾部截断) */
#define OCPP_MV_OFFLINE_RECORDS_PER_SECTOR (SPI_SECTOR_SIZE / OCPP_MV_OFFLINE_FRAME_BYTES)
/** @brief 枪号:0 = 前半区(枪1),1 = 后半区(枪2) */
#define OCPP_MV_OFFLINE_GUN_CNT 2u
/** @brief 每个枪在 Flash 内占用的连续扇区数(半区 32KB / 4096 */
#define OCPP_MV_OFFLINE_SECTORS_PER_GUN (DATAFLASH_OCPP_MV_OFFLINE_HALF_SIZE / SPI_SECTOR_SIZE)
/** @brief 单枪环形池最大条数(每枪 8 扇区 × 每扇区条数) */
#define OCPP_MV_OFFLINE_POOL_MAX_PER_GUN (OCPP_MV_OFFLINE_RECORDS_PER_SECTOR * OCPP_MV_OFFLINE_SECTORS_PER_GUN)
/** @brief FRAM 中可同时保存的「流水号→序号区间」映射行数(受 128B 控制块限制) */
#define OCPP_MV_OFFLINE_MAP_ROWS 6u
/** @brief 负载内魔数(小端可视 OCMV) */
#define OCPP_MV_OFFLINE_FLASH_MAGIC 0x564D434Fu
/** @brief 负载格式版本(32B 布局为 4;与旧 64B 不兼容,升级后建议整池擦除) */
#define OCPP_MV_OFFLINE_FLASH_VER 4u
/** @brief FRAM 管理块头 */
#define OCPP_MV_OFFLINE_FRAM_HEAD 0x5AA9u
/** @brief FRAM 管理块布局版本(变更字段时递增;5:每枪 next_seq + 映射行含枪号) */
#define OCPP_MV_OFFLINE_FRAM_MNG_VER 5u
/**
* @brief 一条待补发的 MeterValues「测量快照」负载(固定 32B)
*
* 字段与 `BS_ocpp_ctrl.c` 中 `u8_get_ocpp_measurand_data` **已实现** 的 measurand 对齐,便于离线存盘与联网后按同口径组 SampledValue
* - `ENUM_EnergyActiveImportRegister`:累计 Wh(源码为 u64/10,此处存 u32,超高 Wh 截断)
* - `ENUM_EnergyActiveImportInterval`:事务段增量 Wh → `u32_energy_interval_wh`
* - `ENUM_Voltage` / `ENUM_CurrentImport`:电表电压 0.1V、电流 0.01A(原 0.1A×10
* - `ENUM_PowerActiveImport`:有功功率 W(饱和 65535,极大桩可再约定用比例)
* - `ENUM_PowerOffered`:额定功率,单位 **0.1 kW**(与 `E_BS_GET_SYS_DATA_RATED_POWER` 一致)
* - `ENUM_CurrentOffered`:最大电流 0.01A
* - `ENUM_SoC` + `u8_soc_data_type`SOC% 与 0当前/1起始/2结束(与函数入参 `u8_data_type` 一致)
*
* 流水号 **tx** 不在本结构重复保存,以 FRAM 映射 `aMap[]` 为准。
*/
typedef struct
{
U32_T u32_magic; /**< 须为 OCPP_MV_OFFLINE_FLASH_MAGIC */
U32_T u32_timestamp_unix; /**< 取样时刻日历秒:`xDate2Seconds(GetCurrentTime)`,与补发 JSON 中 timestamp 一致(勿存上电单调秒) */
U32_T u32_meter_import_wh; /**< Energy.Active.Import.Register:累计 Whu64/10 截断 u32 */
U32_T u32_energy_interval_wh; /**< Energy.Active.Import.Interval:事务段增量 Wh */
U16_T u16_voltage_v_x10; /**< Voltage:输出电压 0.1V */
U16_T u16_current_import_a_x100; /**< Current.Import:电流 0.01A */
U16_T u16_power_active_w; /**< Power.Active.Import:功率 W(饱和) */
U16_T u16_power_offered_01kw; /**< Power.Offered:额定功率 0.1kW 单位 */
U16_T u16_current_offered_a_x100; /**< Current.Offered:最大电流 0.01A */
U8_T u8_soc; /**< SoC0~100 */
U8_T u8_soc_data_type; /**< SoC 上下文:0 当前 / 1 Transaction.Begin / 2 Transaction.End */
U8_T u8_connector_id; /**< 连接器号 */
U8_T u8_evse_id; /**< EVSE/枪逻辑号 */
U16_T u16_version; /**< 须为 OCPP_MV_OFFLINE_FLASH_VER */
} S_OCPP_MV_OFFLINE_MV;
/**
* @brief FRAM 中一行:会话键(当前实现为开始充电秒)↔ 在 Flash 池中的 **连续序号区间**
*
* 第 k 条(0<=k<count)在该枪下的单调序号为 `u32_seq_start + k`,读 Flash 时用
* `idx = (u32_seq_start + k) % OCPP_MV_OFFLINE_POOL_MAX_PER_GUN`(与本模块 `mv_read_by_seq` 一致)。
*/
typedef struct
{
U32_T u32_tx_sn_lo; /**< 当前:`xDate2Seconds(StartChargeTime)`;字段名历史兼容 */
U32_T u32_tx_sn_hi; /**< 当前约定恒 0(保留);非 64 位键的高半字 */
U32_T u32_seq_start; /**< 该流水号在该枪下第一条 MV 的单调序号 */
U16_T u16_count; /**< 连续条数 */
U8_T u8_gun_no; /**< 0=前半区枪11=后半区枪2(与 `u8_ocpp_mv_offline_mv_save` 入参一致) */
U8_T u8_reserved; /**< 预留 */
} S_OCPP_MV_OFFLINE_MAP_ROW;
/**
* @brief FRAM 管理块(128B`EEPROM_ADDR_OCPP_MV_OFFLINE_CTRL`
*
* CRC:整结构 `u16_crc` 置 0 后按 `u16_calculate_crc` 计算(与未结算模块相同)。
*/
typedef struct
{
U16_T u16_head; /**< OCPP_MV_OFFLINE_FRAM_HEAD */
U16_T u16_version; /**< OCPP_MV_OFFLINE_FRAM_MNG_VER */
U16_T u16_crc; /**< CRC-16 */
U8_T u8_flags; /**< 预留标志位 */
U8_T u8_reserved1; /**< 预留 */
U32_T u32_next_seq[OCPP_MV_OFFLINE_GUN_CNT]; /**< 每枪下一条将分配的单调序号(掉电连续) */
S_OCPP_MV_OFFLINE_MAP_ROW aMap[OCPP_MV_OFFLINE_MAP_ROWS]; /**< 会话键+枪→序号区间 */
U8_T u8_reserved2[16]; /**< 补齐至 128 字节 */
} S_OCPP_MV_OFFLINE_FRAM_MNG;
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
_Static_assert(sizeof(S_OCPP_MV_OFFLINE_MV) == OCPP_MV_OFFLINE_PAYLOAD_BYTES, "MV payload 32B");
_Static_assert(sizeof(S_OCPP_MV_OFFLINE_FRAM_MNG) == 128u, "FRAM mng 128B");
_Static_assert(sizeof(S_OCPP_MV_OFFLINE_FRAM_MNG) <= EEPROM_OCPP_MV_OFFLINE_CTRL_SIZE, "FRAM fit");
#else
/* ARMCC v5 不支持 _Static_assert,使用编译期断言替代 */
typedef int __ocpp_mv_size_check_1[(sizeof(S_OCPP_MV_OFFLINE_MV) == OCPP_MV_OFFLINE_PAYLOAD_BYTES) ? 1 : -1];
typedef int __ocpp_mv_size_check_2[(sizeof(S_OCPP_MV_OFFLINE_FRAM_MNG) == 128u) ? 1 : -1];
typedef int __ocpp_mv_size_check_3[(sizeof(S_OCPP_MV_OFFLINE_FRAM_MNG) <= EEPROM_OCPP_MV_OFFLINE_CTRL_SIZE) ? 1 : -1];
#endif
/** @brief 上电:从 FRAM 恢复管理块 RAM 镜像,非法则默认并写回 */
void v_ocpp_mv_offline_init(void);
/**
* @brief 保存一条离线 MV:分配该枪单调序号、写 Flash、更新/追加映射行
* @param u8_gun_no 枪号:0=前半区(枪1),1=后半区(枪2)
* @param tx_sn_lo 会话键:当前为 `xDate2Seconds(StartChargeTime)`(单路 U32
* @param tx_sn_hi 当前恒 0(与 `u32_tx_sn_hi` 一致;接口双参仅为与 FRAM 行字段对齐)
* @param out_seq 输出本次在该枪下的单调序号
* @return 0 成功;1 参数非法(含枪号越界);2 映射表满;3 Flash 失败;4 FRAM 失败;
* 5 流水号已存在但本次序号不连续(调用顺序错误)
*/
U8_T u8_ocpp_mv_offline_mv_save(U8_T u8_gun_no, U32_T tx_sn_lo, U32_T tx_sn_hi, const S_OCPP_MV_OFFLINE_MV *mv,
U32_T *out_seq);
/**
* @brief 按该枪单调序号读一条 MV(物理槽:`seq % POOL_MAX_PER_GUN`,落在该枪半区)
* @return 0 成功;1 参数非法;2 校验/帧头失败
*/
U8_T u8_ocpp_mv_offline_mv_read_by_seq(U8_T u8_gun_no, U32_T seq, S_OCPP_MV_OFFLINE_MV *out);
/** @brief 判断负载是否为当前版本有效数据 */
U8_T u8_ocpp_mv_offline_mv_is_valid(const S_OCPP_MV_OFFLINE_MV *mv);
/**
* @brief 查询指定枪上会话键(`tx_sn_lo` + `tx_sn_hi`,当前 hi 恒 0)对应的序号区间(用于上送循环)
* @return 0 找到;1 空指针;2 未找到
*/
U8_T u8_ocpp_mv_offline_map_get(U8_T u8_gun_no, U32_T tx_sn_lo, U32_T tx_sn_hi, U32_T *seq_start, U16_T *count);
/**
* @brief 上送完成后删除该枪上映射行(不擦整池 Flash;若需整池清空在 `ocpp_mv_offline_flash_impl.c` 内维护调用)
* @return 0 成功;1 未找到;2 FRAM 写失败
*/
U8_T u8_ocpp_mv_offline_map_remove(U8_T u8_gun_no, U32_T tx_sn_lo, U32_T tx_sn_hi);
/**
* @brief 新一笔充电开始前:清除该枪在 FRAM 中的全部 MV 映射行(释放 map 槽位;不擦 SPI Flash 池)
* @param[in] u8_gun_no 枪索引
* @return 0 成功;1 枪号非法;2 FRAM 写失败
* @see u8_ocpp_mv_offline_mv_save(映射满时按枪淘汰最旧一行)
*/
U8_T u8_ocpp_mv_offline_map_clear_gun(U8_T u8_gun_no);
/* 整池擦除、FRAM 读写默认块、池容量查询等为模块内部实现,无对外声明。 */
#ifdef __cplusplus
}
#endif
#endif /* OCPP_MV_OFFLINE_FLASH_IMPL_H */
+441
View File
@@ -0,0 +1,441 @@
/**
* @file unsettled_order_mng.c
* @brief 未结算订单列表:EEPROM 持久化与 CRC 校验
*
* 功能说明:
* - 在 EEPROM `EEPROM_ADDR_UNSETTLED_MNG` 保存 `S_UNSETTLED_ORDER_MNG`:头标志 `UNSETTLED_HEADER`、
* 未结条数、以及除 CRC 字段外的 CRC-16 校验(与 `u16_calculate_crc` 一致)。
* - 每条未结订单仅保存其 Flash 订单号(`u32_chg_order_save_record` 返回的 `u32_index`)的 24 位压缩形式,
* 最多 `UNSETTLED_MAX_CNT` 条;列表按顺序追加,删除时前移尾部元素。
* - `u8_check_order_settled`:返回 1 表示该索引仍在未结列表中(未结算),0 表示不在列表中(视为已结或从未登记)。
* 命名与注释以头文件为准。
*/
#include "unsettled_order_mng.h"
#include "mylog/mylog.h"
/* 本文件打印使能:0=不编译打印,1=编译打印 */
#ifndef UNSETTLED_MNG_LOG_EN
#define UNSETTLED_MNG_LOG_EN (1)
#endif
#if UNSETTLED_MNG_LOG_EN
#define UNSETTLED_MNG_LOG(fmt, ...) MYLOG_MSG(TASK_ID_Meterfee, fmt, ##__VA_ARGS__)
#else
#define UNSETTLED_MNG_LOG(fmt, ...) ((void)0)
#endif
/*------ Local variables(局部变量) ----------------------*/
// 未结算订单管理信息全局变量
S_UNSETTLED_ORDER_MNG s_unsettledOrderMng;
/*------ Local function prototypes(局部函数声明) ---------*/
static void v_write_eeprom_unsettledMng(S_UNSETTLED_ORDER_MNG *unsettledMng);
static void v_read_eeprom_unsettledMng(S_UNSETTLED_ORDER_MNG *unsettledMng);
static U16_T u16_calculate_unsettled_crc(S_UNSETTLED_ORDER_MNG *unsettledMng);
static U8_T u8_find_order_index(U32_T orderIndex, U16_T *position);
static void v_uint32_to_3bytes(U32_T value, U8_T *bytes);
static U32_T u32_3bytes_to_uint32(const U8_T *bytes);
/*------ Exported functions(导出函数实现) ----------------*/
/**
* @brief 初始化未结算订单管理模块
*/
void v_unsettled_order_init(void)
{
// 读取EEPROM中的管理信息
v_read_eeprom_unsettledMng(&s_unsettledOrderMng);
// 检查数据完整性
if (s_unsettledOrderMng.u16_head != UNSETTLED_HEADER)
{
// 头标志错误,初始化默认值
s_unsettledOrderMng.u16_head = UNSETTLED_HEADER;
s_unsettledOrderMng.u16_unsettledCnt = 0;
// 清空索引数组(3字节数组)
for (U16_T i = 0; i < UNSETTLED_MAX_CNT; i++)
{
s_unsettledOrderMng.u8_unsettledIndexes[i][0] = 0;
s_unsettledOrderMng.u8_unsettledIndexes[i][1] = 0;
s_unsettledOrderMng.u8_unsettledIndexes[i][2] = 0;
}
// 计算并保存CRC
s_unsettledOrderMng.u16_crc = u16_calculate_unsettled_crc(&s_unsettledOrderMng);
v_write_eeprom_unsettledMng(&s_unsettledOrderMng);
UNSETTLED_MNG_LOG("UnsettledMng: init default");
}
else
{
// 验证CRC
U16_T calculatedCrc = u16_calculate_unsettled_crc(&s_unsettledOrderMng);
if (calculatedCrc != s_unsettledOrderMng.u16_crc)
{
// CRC校验失败,恢复默认值
UNSETTLED_MNG_LOG("UnsettledMng: CRC fail, reset default");
s_unsettledOrderMng.u16_unsettledCnt = 0;
// 清空索引数组(3字节数组)
for (U16_T i = 0; i < UNSETTLED_MAX_CNT; i++)
{
s_unsettledOrderMng.u8_unsettledIndexes[i][0] = 0;
s_unsettledOrderMng.u8_unsettledIndexes[i][1] = 0;
s_unsettledOrderMng.u8_unsettledIndexes[i][2] = 0;
}
// 重新计算CRC并保存
s_unsettledOrderMng.u16_crc = u16_calculate_unsettled_crc(&s_unsettledOrderMng);
v_write_eeprom_unsettledMng(&s_unsettledOrderMng);
}
else
{
#if 0 // 测试读取
s_unsettledOrderMng.u16_unsettledCnt = 1;
s_unsettledOrderMng.u8_unsettledIndexes[0][0] = 0;
s_unsettledOrderMng.u8_unsettledIndexes[0][1] = 0;
s_unsettledOrderMng.u8_unsettledIndexes[0][2] = 1;
s_unsettledOrderMng.u8_unsettledIndexes[1][0] = 0;
s_unsettledOrderMng.u8_unsettledIndexes[1][1] = 0;
s_unsettledOrderMng.u8_unsettledIndexes[1][2] = 0;
#endif
UNSETTLED_MNG_LOG("UnsettledMng: init ok, cnt=%u",
s_unsettledOrderMng.u16_unsettledCnt);
}
}
}
/**
* @brief 添加未结算订单索引
*/
U8_T u8_add_unsettled_order(U32_T orderIndex)
{
// 检查订单索引是否有效
if (orderIndex == 0)
{
UNSETTLED_MNG_LOG("UnsettledMng: invalid index");
return 1;
}
// 检查索引是否超过3字节范围(最大16,777,215
if (orderIndex > 0xFFFFFF)
{
UNSETTLED_MNG_LOG("UnsettledMng: index >24b, idx=%u", orderIndex);
return 1;
}
// 检查是否已存在
U16_T position;
if (u8_find_order_index(orderIndex, &position) == 0)
{
UNSETTLED_MNG_LOG("UnsettledMng: index exists, idx=%u", orderIndex);
return 0; // 已存在,不算失败
}
// 检查列表是否已满
if (s_unsettledOrderMng.u16_unsettledCnt >= UNSETTLED_MAX_CNT)
{
UNSETTLED_MNG_LOG("UnsettledMng: list full, add reject");
return 1;
}
// 添加到列表末尾(使用3字节存储)
v_uint32_to_3bytes(orderIndex, s_unsettledOrderMng.u8_unsettledIndexes[s_unsettledOrderMng.u16_unsettledCnt]);
s_unsettledOrderMng.u16_unsettledCnt++;
// 更新CRC并保存到EEPROM
s_unsettledOrderMng.u16_crc = u16_calculate_unsettled_crc(&s_unsettledOrderMng);
v_write_eeprom_unsettledMng(&s_unsettledOrderMng);
UNSETTLED_MNG_LOG("UnsettledMng: add ok, idx=%u, cnt=%u",
orderIndex, s_unsettledOrderMng.u16_unsettledCnt);
return 0;
}
/**
* @brief 移除已结算订单索引
*/
U8_T u8_remove_unsettled_order(U32_T orderIndex)
{
// 查找订单索引位置
U16_T position;
if (u8_find_order_index(orderIndex, &position) != 0)
{
UNSETTLED_MNG_LOG("UnsettledMng: idx not found, idx=%u", orderIndex);
return 1;
}
// 从列表中移除(将后面的元素前移)
for (U16_T i = position; i < s_unsettledOrderMng.u16_unsettledCnt - 1; i++)
{
// 复制3字节索引
s_unsettledOrderMng.u8_unsettledIndexes[i][0] = s_unsettledOrderMng.u8_unsettledIndexes[i + 1][0];
s_unsettledOrderMng.u8_unsettledIndexes[i][1] = s_unsettledOrderMng.u8_unsettledIndexes[i + 1][1];
s_unsettledOrderMng.u8_unsettledIndexes[i][2] = s_unsettledOrderMng.u8_unsettledIndexes[i + 1][2];
}
// 清空最后一个元素
s_unsettledOrderMng.u8_unsettledIndexes[s_unsettledOrderMng.u16_unsettledCnt - 1][0] = 0;
s_unsettledOrderMng.u8_unsettledIndexes[s_unsettledOrderMng.u16_unsettledCnt - 1][1] = 0;
s_unsettledOrderMng.u8_unsettledIndexes[s_unsettledOrderMng.u16_unsettledCnt - 1][2] = 0;
s_unsettledOrderMng.u16_unsettledCnt--;
// 更新CRC并保存到EEPROM
s_unsettledOrderMng.u16_crc = u16_calculate_unsettled_crc(&s_unsettledOrderMng);
v_write_eeprom_unsettledMng(&s_unsettledOrderMng);
UNSETTLED_MNG_LOG("UnsettledMng: rm ok, idx=%u, cnt=%u",
orderIndex, s_unsettledOrderMng.u16_unsettledCnt);
return 0;
}
/**
* @brief 检查订单是否已结算
*/
U8_T u8_check_order_settled(U32_T orderIndex)
{
U16_T position;
// 在未结算列表中查找
if (u8_find_order_index(orderIndex, &position) == 0)
{
return 1; // 找到,说明未结算
}
return 0; // 未找到,说明已结算
}
/**
* @brief 获取未结算订单数量
*/
U16_T u16_get_unsettled_order_count(void)
{
return s_unsettledOrderMng.u16_unsettledCnt;
}
/**
* @brief 获取所有未结算订单索引
*/
void v_get_unsettled_order_indexes(U32_T *indexArray, U16_T *count)
{
if (indexArray == NULL || count == NULL)
{
return;
}
*count = s_unsettledOrderMng.u16_unsettledCnt;
for (U16_T i = 0; i < s_unsettledOrderMng.u16_unsettledCnt; i++)
{
// 将3字节索引转换为32位整数
indexArray[i] = u32_3bytes_to_uint32(s_unsettledOrderMng.u8_unsettledIndexes[i]);
}
}
/**
* @brief 清除所有未结算订单记录
*/
void v_clear_all_unsettled_orders(void)
{
s_unsettledOrderMng.u16_unsettledCnt = 0;
// 清空索引数组(3字节数组)
for (U16_T i = 0; i < UNSETTLED_MAX_CNT; i++)
{
s_unsettledOrderMng.u8_unsettledIndexes[i][0] = 0;
s_unsettledOrderMng.u8_unsettledIndexes[i][1] = 0;
s_unsettledOrderMng.u8_unsettledIndexes[i][2] = 0;
}
// 更新CRC并保存到EEPROM
s_unsettledOrderMng.u16_crc = u16_calculate_unsettled_crc(&s_unsettledOrderMng);
v_write_eeprom_unsettledMng(&s_unsettledOrderMng);
UNSETTLED_MNG_LOG("UnsettledMng: clear all");
}
/**
* @brief 打印未结算订单状态(调试用)
*/
void v_print_unsettled_order_status(void)
{
UNSETTLED_MNG_LOG("=== Unsettled status ===");
UNSETTLED_MNG_LOG("Unsettled cnt: %u", s_unsettledOrderMng.u16_unsettledCnt);
if (s_unsettledOrderMng.u16_unsettledCnt > 0)
{
UNSETTLED_MNG_LOG("Unsettled idx list:");
for (U16_T i = 0; i < s_unsettledOrderMng.u16_unsettledCnt; i++)
{
U32_T orderIndex = u32_3bytes_to_uint32(s_unsettledOrderMng.u8_unsettledIndexes[i]);
UNSETTLED_MNG_LOG(" [%u] index=%u", i, orderIndex);
}
}
else
{
UNSETTLED_MNG_LOG("No unsettled order");
}
UNSETTLED_MNG_LOG("=====================");
}
/**
* @brief 计算CRC校验值
*/
U16_T u16_calculate_crc(const U8_T *data, U16_T length)
{
U16_T crc = 0xFFFF;
if (data == NULL || length == 0)
{
return crc;
}
for (U16_T i = 0; i < length; i++)
{
crc ^= (U16_T)data[i] << 8;
for (U8_T j = 0; j < 8; j++)
{
if (crc & 0x8000)
{
crc = (crc << 1) ^ UNSETTLED_CRC_POLY;
}
else
{
crc <<= 1;
}
}
}
return crc;
}
/*------ Local functions(局部函数实现) -------------------*/
/**
* @brief 写入未结算订单管理信息到EEPROM
*/
static void v_write_eeprom_unsettledMng(S_UNSETTLED_ORDER_MNG *unsettledMng)
{
if (unsettledMng == NULL)
{
return;
}
// 先计算CRC
unsettledMng->u16_crc = u16_calculate_unsettled_crc(unsettledMng);
// 写入到EEPROM
eeprom_write(EEPROM_ADDR_UNSETTLED_MNG, (U8_T *)unsettledMng, sizeof(S_UNSETTLED_ORDER_MNG));
}
/**
* @brief 从EEPROM读取未结算订单管理信息
*/
static void v_read_eeprom_unsettledMng(S_UNSETTLED_ORDER_MNG *unsettledMng)
{
if (unsettledMng == NULL)
{
return;
}
// 从EEPROM读取,eeprom_read返回HAL_OK(0)表示成功
if (eeprom_read(EEPROM_ADDR_UNSETTLED_MNG, (U8_T *)unsettledMng, sizeof(S_UNSETTLED_ORDER_MNG)) != HAL_OK)
{
// 读取失败,初始化默认值
unsettledMng->u16_head = 0;
unsettledMng->u16_unsettledCnt = 0;
unsettledMng->u16_crc = 0;
for (U16_T i = 0; i < UNSETTLED_MAX_CNT; i++)
{
unsettledMng->u8_unsettledIndexes[i][0] = 0;
unsettledMng->u8_unsettledIndexes[i][1] = 0;
unsettledMng->u8_unsettledIndexes[i][2] = 0;
}
}
}
/**
* @brief 计算未结算订单管理信息的CRC
*/
static U16_T u16_calculate_unsettled_crc(S_UNSETTLED_ORDER_MNG *unsettledMng)
{
if (unsettledMng == NULL)
{
return 0;
}
// 手动计算结构体大小,避免结构体对齐问题
// 结构体布局:u16_head(2) + u16_unsettledCnt(2) + u16_crc(2) + u8_unsettledIndexes[30][3](90)
// 总大小:2 + 2 + 2 + 90 = 96字节
// 计算除CRC字段外的数据:u16_head + u16_unsettledCnt + u8_unsettledIndexes
U8_T tempBuffer[94]; // 2 + 2 + 90 = 94字节
U16_T offset = 0;
// 复制头标志
tempBuffer[offset++] = (unsettledMng->u16_head >> 8) & 0xFF;
tempBuffer[offset++] = unsettledMng->u16_head & 0xFF;
// 复制未结算订单数量
tempBuffer[offset++] = (unsettledMng->u16_unsettledCnt >> 8) & 0xFF;
tempBuffer[offset++] = unsettledMng->u16_unsettledCnt & 0xFF;
// 复制索引数组(3字节数组)
for (U16_T i = 0; i < UNSETTLED_MAX_CNT; i++)
{
for (U8_T j = 0; j < 3; j++)
{
tempBuffer[offset++] = unsettledMng->u8_unsettledIndexes[i][j];
}
}
// 计算CRC
return u16_calculate_crc(tempBuffer, offset);
}
/**
* @brief 在未结算订单列表中查找订单索引
*/
static U8_T u8_find_order_index(U32_T orderIndex, U16_T *position)
{
for (U16_T i = 0; i < s_unsettledOrderMng.u16_unsettledCnt; i++)
{
U32_T storedIndex = u32_3bytes_to_uint32(s_unsettledOrderMng.u8_unsettledIndexes[i]);
if (storedIndex == orderIndex)
{
if (position != NULL)
{
*position = i;
}
return 0; // 找到
}
}
return 1; // 未找到
}
/**
* @brief 将32位整数转换为3字节数组
*/
static void v_uint32_to_3bytes(U32_T value, U8_T *bytes)
{
bytes[0] = (value >> 16) & 0xFF; // 最高字节
bytes[1] = (value >> 8) & 0xFF; // 中间字节
bytes[2] = value & 0xFF; // 最低字节
}
/**
* @brief 将3字节数组转换为32位整数
*/
static U32_T u32_3bytes_to_uint32(const U8_T *bytes)
{
return ((U32_T)bytes[0] << 16) | ((U32_T)bytes[1] << 8) | (U32_T)bytes[2];
}
+138
View File
@@ -0,0 +1,138 @@
/**
* @file unsettled_order_mng.h
* @brief 未结算订单索引列表(EEPROM 持久化)
*
* 与充电订单 Flash 模块配合:订单保存成功后可将 `u32_index` 加入本列表;平台确认结算后移除。
* 数据区独立 EEPROM 段,结构含头、计数、CRC 与 3 字节压缩索引表,详见 `S_UNSETTLED_ORDER_MNG`。
*/
#ifndef UNSETTLED_ORDER_MNG_H
#define UNSETTLED_ORDER_MNG_H
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
extern "C" {
#endif
/*------ Exported includes(头文件) ----------------------*/
#include "main.h"
#include "eeprom/fm24cl16.h"
/*------ Exported macro(宏定义) -------------------------*/
// 最大未结算订单数(基于可用空间计算)
// 地址范围:0x0040 - 0x00FF = 192字节
// 结构体大小:头标志(2) + 计数(2) + CRC(2) = 6字节
// 剩余空间:192 - 6 = 186字节
// 每个索引使用3字节压缩存储(支持最大索引值16,777,215)
// 最大存储数量:186 / 3 = 62个(向下取整)
// 用户要求最大数量为60,满足要求
#define UNSETTLED_MAX_CNT 30 // 最大未结算订单数
// 头标志定义
#define UNSETTLED_HEADER 0x5AA6 // 未结算订单管理信息头标志
// CRC多项式(简单校验)
#define UNSETTLED_CRC_POLY 0x1021 // CRC-16-CCITT多项式
/*------ Exported struct(结构体定义) --------------------*/
/**
* @brief 未结算订单管理信息结构体
*
* 存储在EEPROM地址:EEPROM_ADDR_UNSETTLED_MNG (0x0040)
* 用于管理未结算订单的索引列表
* 使用3字节压缩存储索引,支持最大60个未结算订单
*/
typedef struct
{
U16_T u16_head; // 头标志 (0x5AA6)
U16_T u16_unsettledCnt; // 未结算订单数量
U16_T u16_crc; // CRC校验值
U8_T u8_unsettledIndexes[UNSETTLED_MAX_CNT][3]; // 未结算订单索引数组(每个索引3字节)
} S_UNSETTLED_ORDER_MNG;
/*------ Exported variables(变量定义) -------------------*/
// 未结算订单管理信息全局变量
extern S_UNSETTLED_ORDER_MNG s_unsettledOrderMng;
/*------ Exported function prototypes (函数声明) -------*/
/**
* @brief 初始化未结算订单管理模块
*
* 从EEPROM读取管理信息,如果不存在则初始化默认值
* 验证数据完整性(CRC校验)
*/
void v_unsettled_order_init(void);
/**
* @brief 添加未结算订单索引
*
* 当新的充电订单保存到Flash时调用此函数
*
* @param orderIndex 订单索引(由u32_chg_order_save_record返回)
* @return U8_T 添加结果:0-成功,1-失败(列表已满)
*/
U8_T u8_add_unsettled_order(U32_T orderIndex);
/**
* @brief 移除已结算订单索引
*
* 当平台确认结算后调用此函数
*
* @param orderIndex 要移除的订单索引
* @return U8_T 移除结果:0-成功,1-失败(未找到该索引)
*/
U8_T u8_remove_unsettled_order(U32_T orderIndex);
/**
* @brief 检查订单是否已结算
*
* @param orderIndex 要检查的订单索引
* @return U8_T 检查结果:0-已结算,1-未结算
*/
U8_T u8_check_order_settled(U32_T orderIndex);
/**
* @brief 获取未结算订单数量
*
* @return U16_T 未结算订单数量
*/
U16_T u16_get_unsettled_order_count(void);
/**
* @brief 获取所有未结算订单索引
*
* @param indexArray 索引数组指针(用于存储读取到的数据)
* @param count 实际读取到的索引数量指针
*/
void v_get_unsettled_order_indexes(U32_T *indexArray, U16_T *count);
/**
* @brief 清除所有未结算订单记录
*
* 用于系统重置或测试
*/
void v_clear_all_unsettled_orders(void);
/**
* @brief 打印未结算订单状态(调试用)
*/
void v_print_unsettled_order_status(void);
/**
* @brief 计算CRC校验值
*
* @param data 数据指针
* @param length 数据长度(字节)
* @return U16_T CRC校验值
*/
U16_T u16_calculate_crc(const U8_T *data, U16_T length);
#ifdef __cplusplus
}
#endif
#endif /* UNSETTLED_ORDER_MNG_H */
+126
View File
@@ -0,0 +1,126 @@
/*
* FreeRTOS Kernel V10.3.1
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/*-----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
*
* See http://www.freertos.org/a00110.html
*----------------------------------------------------------*/
/* Ensure stdint is only used by the compiler, and not the assembler. */
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__TASKING__) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
#define configUSE_PREEMPTION 1
#define configUSE_IDLE_HOOK 1
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ ( SystemCoreClock )
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES ( 32 )
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 130 )
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 150 * 1024 ) )
#define configMAX_TASK_NAME_LEN ( 16 )
#define configUSE_TRACE_FACILITY 1
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1
#define configUSE_MUTEXES 1
#define configQUEUE_REGISTRY_SIZE 8
#define configCHECK_FOR_STACK_OVERFLOW 2
#define configUSE_RECURSIVE_MUTEXES 1
#define configUSE_MALLOC_FAILED_HOOK 1
#define configUSE_APPLICATION_TASK_TAG 0
#define configUSE_COUNTING_SEMAPHORES 1
#define configGENERATE_RUN_TIME_STATS 0
/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
/* Software timer definitions. */
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY ( 2 )
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 )
/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 0
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4 /* 15 priority levels */
#endif
/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf
/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
/* Interrupt priorities used by the kernel port layer itself. These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
#endif /* FREERTOS_CONFIG_H */
+282
View File
@@ -0,0 +1,282 @@
/**
* @file app_freertos.c
* @brief FreeRTOS 运行信息查看(任务/堆/CPU占用)
*
* 参考实现:`CCU601E_RUN/BSP/freertos/app_freertos.c`
*
* 本工程适配说明:
* - 任务“栈大小/周期”从 `app_init/app_init.c` 的任务表 `my_task_data[]` 获取
* - 看门狗计数 `wdtCount` 通过 `u16_get_wdt_cnt()` 获取实时值
* - 通过 letter_shell 导出 `ps` 命令打印任务信息
*/
#include "freertos/app_freertos.h"
#include <string.h>
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include "app_init/app_init.h"
#include "wdt_task/wdt_task.h"
#include "publicdata/public_define.h"
#if (MY_SHELL_EN)
#include "letter_shell/shell_port.h"
#define APP_RTOS_OUTPUT(str_) shell_send_String((str_))
#else
static void app_rtos_output(const char *str)
{
(void)str;
}
#define APP_RTOS_OUTPUT(str_) app_rtos_output((str_))
#endif
/* 按任务名查表:type=0 栈大小(字节)type=1 周期(ms) */
static uint32_t get_task_cfg_by_name(const char *name, uint8_t type)
{
if (name == NULL) {
return 0U;
}
for (uint32_t i = 0U; i < (uint32_t)MY_TASK_NUM; i++) {
if (strcmp(name, my_task_data[i].task_name) == 0) {
return (type == 0U) ? (uint32_t)my_task_data[i].task_size
: (uint32_t)my_task_data[i].task_timer;
}
}
return 0U;
}
/* 按任务名查任务ID:成功返回1,并通过 task_id 输出;失败返回0 */
static uint8_t get_task_id_by_name(const char *name, TASK_ID *task_id)
{
if ((name == NULL) || (task_id == NULL)) {
return 0U;
}
for (uint32_t i = 0U; i < (uint32_t)MY_TASK_NUM; i++) {
if (strcmp(name, my_task_data[i].task_name) == 0) {
*task_id = (TASK_ID)i;
return 1U;
}
}
return 0U;
}
/**
* @brief 按优先级对任务状态数组进行排序,返回排序后的原始数组下标
* @param pxTaskStatusArray 任务状态数组指针
* @param uxArraySize 数组长度
* @param rtos_pri_index 输出缓冲区(最大30个元素),存储排序后的原始数组下标
* @return 实际存储数量(≤uxArraySize
*/
static UBaseType_t SortTasksByPriorityIndex(const TaskStatus_t *pxTaskStatusArray,
UBaseType_t uxArraySize,
UBaseType_t rtos_pri_index[30])
{
if (pxTaskStatusArray == NULL || rtos_pri_index == NULL) {
return 0U;
}
UBaseType_t valid_size = (uxArraySize > 30U) ? 30U : uxArraySize;
UBaseType_t indexes[30];
for (UBaseType_t i = 0U; i < valid_size; i++) {
indexes[i] = i;
}
for (UBaseType_t i = 0U; i + 1U < valid_size; i++) {
for (UBaseType_t j = 0U; j + 1U < (valid_size - i); j++) {
if (pxTaskStatusArray[indexes[j]].uxCurrentPriority <=
pxTaskStatusArray[indexes[j + 1U]].uxCurrentPriority) {
UBaseType_t tmp = indexes[j];
indexes[j] = indexes[j + 1U];
indexes[j + 1U] = tmp;
}
}
}
for (UBaseType_t i = 0U; i < valid_size; i++) {
rtos_pri_index[i] = indexes[i];
}
return valid_size;
}
SystemInfo_t *getSystemInfo(void)
{
TaskStatus_t *pxTaskStatusArray = NULL;
UBaseType_t uxArraySize;
UBaseType_t rtos_pri[30] = {0};
const char states[] = {'X', 'R', 'B', 'S', 'D'};
uint32_t ulTotalTime = 0U;
uxArraySize = uxTaskGetNumberOfTasks();
pxTaskStatusArray = (TaskStatus_t *)pvPortMalloc(uxArraySize * sizeof(TaskStatus_t));
if (pxTaskStatusArray == NULL) {
return NULL;
}
uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, &ulTotalTime);
UBaseType_t sorted_count = SortTasksByPriorityIndex(pxTaskStatusArray, uxArraySize, rtos_pri);
SystemInfo_t *sysInfo = (SystemInfo_t *)pvPortMalloc(sizeof(SystemInfo_t));
if (sysInfo == NULL) {
vPortFree(pxTaskStatusArray);
return NULL;
}
memset(sysInfo, 0, sizeof(SystemInfo_t));
sysInfo->tasks = (TaskInfo_t *)pvPortMalloc(sorted_count * sizeof(TaskInfo_t));
if (sysInfo->tasks == NULL) {
vPortFree(pxTaskStatusArray);
vPortFree(sysInfo);
return NULL;
}
memset(sysInfo->tasks, 0, sorted_count * sizeof(TaskInfo_t));
sysInfo->taskCount = (unsigned int)sorted_count;
for (UBaseType_t i = 0U; i < sorted_count; i++) {
UBaseType_t x = rtos_pri[i];
if (x >= uxArraySize) {
break;
}
TaskInfo_t *task = &sysInfo->tasks[i];
strncpy(task->taskName, pxTaskStatusArray[x].pcTaskName, sizeof(task->taskName) - 1U);
task->taskName[sizeof(task->taskName) - 1U] = '\0';
if (pxTaskStatusArray[x].eCurrentState <= eDeleted) {
task->state = states[pxTaskStatusArray[x].eCurrentState];
} else {
task->state = '?';
}
task->id = (unsigned int)i;
task->priority = (unsigned int)pxTaskStatusArray[x].uxCurrentPriority;
task->allocatedStack = (unsigned int)get_task_cfg_by_name(pxTaskStatusArray[x].pcTaskName, 0U);
task->cycle = (unsigned int)get_task_cfg_by_name(pxTaskStatusArray[x].pcTaskName, 1U);
{
TASK_ID task_id;
task->wdtCount = (get_task_id_by_name(pxTaskStatusArray[x].pcTaskName, &task_id) == 1U)
? (unsigned int)u16_get_wdt_cnt((uint8_t)task_id)
: 0U;
}
task->minFreeStack = (unsigned int)pxTaskStatusArray[x].usStackHighWaterMark * 4U;
sysInfo->totalAllocatedStack += task->allocatedStack;
if (task->allocatedStack > 0U) {
unsigned int freePct = (task->minFreeStack * 100U) / task->allocatedStack;
task->stackUsagePercentage = (freePct == 0U) ? 0U : (100U - freePct);
} else {
task->stackUsagePercentage = 0U;
}
task->cpuUsagePercentage = (ulTotalTime > 0U)
? (unsigned int)((pxTaskStatusArray[x].ulRunTimeCounter * 100UL) / ulTotalTime)
: 0U;
}
sysInfo->freeHeapSize = (unsigned int)xPortGetFreeHeapSize();
sysInfo->minEverFreeHeapSize = (unsigned int)xPortGetMinimumEverFreeHeapSize();
sysInfo->totalHeapSize = (unsigned int)configTOTAL_HEAP_SIZE;
vPortFree(pxTaskStatusArray);
return sysInfo;
}
void freeSystemInfo(SystemInfo_t *sysInfo)
{
if (sysInfo != NULL) {
if (sysInfo->tasks != NULL) {
vPortFree(sysInfo->tasks);
}
vPortFree(sysInfo);
}
}
void printSystemInfo(void)
{
SystemInfo_t *sysInfo = getSystemInfo();
if (sysInfo == NULL) {
APP_RTOS_OUTPUT("Error: Failed to get system information!\r\n");
return;
}
char buffer[256];
(void)snprintf(buffer, sizeof(buffer),
"%-3s %-12s %-2s %-4s %-6s %-6s %-6s %-6s %-5s %-5s\r\n",
"ID", "TaskName", "St", "Pri", "Cycle", "Stack", "Wdt", "Free", "Use%", "CPU%");
APP_RTOS_OUTPUT(buffer);
for (unsigned int i = 0U; i < sysInfo->taskCount; i++) {
TaskInfo_t *task = &sysInfo->tasks[i];
const char *cpuStr = (task->cpuUsagePercentage > 0U) ? "" : "<1";
if (task->cpuUsagePercentage > 0U) {
(void)snprintf(buffer, sizeof(buffer),
"%-3u %-12.12s %-2c %-4u %-6u %-6u %-6u %-6u %-5u %-5u%%\r\n",
task->id,
task->taskName,
task->state,
task->priority,
task->cycle,
task->allocatedStack,
task->wdtCount,
task->minFreeStack,
task->stackUsagePercentage,
task->cpuUsagePercentage);
} else {
(void)snprintf(buffer, sizeof(buffer),
"%-3u %-12.12s %-2c %-4u %-6u %-6u %-6u %-6u %-5u %-5s%%\r\n",
task->id,
task->taskName,
task->state,
task->priority,
task->cycle,
task->allocatedStack,
task->wdtCount,
task->minFreeStack,
task->stackUsagePercentage,
cpuStr);
}
APP_RTOS_OUTPUT(buffer);
}
float free_percent = (sysInfo->totalHeapSize > 0U) ? ((float)sysInfo->freeHeapSize * 100.0f) / (float)sysInfo->totalHeapSize : 0.0f;
float min_percent = (sysInfo->totalHeapSize > 0U) ? ((float)sysInfo->minEverFreeHeapSize * 100.0f) / (float)sysInfo->totalHeapSize : 0.0f;
(void)snprintf(buffer, sizeof(buffer),
"\r\nHeap:\r\n"
"Free: %u bytes (%.1f%%)\r\n"
"Min: %u bytes (%.1f%%)\r\n"
"Stacks: %u bytes\r\n"
"Total: %u bytes\r\n"
"----------------------------------------\r\n",
sysInfo->freeHeapSize, free_percent,
sysInfo->minEverFreeHeapSize, min_percent,
sysInfo->totalAllocatedStack,
sysInfo->totalHeapSize);
APP_RTOS_OUTPUT(buffer);
freeSystemInfo(sysInfo);
}
#if (MY_SHELL_EN)
/* 兼容入口:对齐参考工程函数名 */
static void printTaskStackUsage(void)
{
printSystemInfo();
}
/* Shell 命令:ps */
static void v_task_show(void)
{
printTaskStackUsage();
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
ps, v_task_show, myshlle-- task runing state show);
#endif
+44
View File
@@ -0,0 +1,44 @@
/**
* @file app_freertos.h
* @brief FreeRTOS 运行信息查看(任务/堆/CPU占用)
*
* 说明:
* - 参考 `CCU601E_RUN/BSP/freertos/app_freertos.c` 的用途,在 Shell 中提供 `ps` 命令
* - 本工程接入任务看门狗统计,`wdtCount` 为各任务实时看门狗计数值
*/
#ifndef APP_FREERTOS_H
#define APP_FREERTOS_H
#include "FreeRTOS.h"
/* 单任务信息快照 */
typedef struct {
char taskName[configMAX_TASK_NAME_LEN];
char state;
unsigned int id;
unsigned int priority;
unsigned int cycle; /* 任务周期(ms) - 来自任务表 */
unsigned int allocatedStack; /* 分配堆栈大小(字节) - 来自任务表 */
unsigned int wdtCount; /* 看门狗计数(秒级累加,实时读取) */
unsigned int minFreeStack; /* 最小剩余堆栈(字节) */
unsigned int stackUsagePercentage; /* 堆栈使用百分比 */
unsigned int cpuUsagePercentage; /* CPU使用百分比 */
} TaskInfo_t;
/* 系统信息快照 */
typedef struct {
TaskInfo_t *tasks; /* 任务数组 */
unsigned int taskCount; /* 任务数量 */
unsigned int freeHeapSize; /* 当前剩余堆空间 */
unsigned int minEverFreeHeapSize; /* 历史最小剩余堆 */
unsigned int totalAllocatedStack; /* 已知分配任务空间(字节) */
unsigned int totalHeapSize; /* 总堆空间大小(字节) */
} SystemInfo_t;
SystemInfo_t *getSystemInfo(void);
void freeSystemInfo(SystemInfo_t *sysInfo);
void printSystemInfo(void);
#endif /* APP_FREERTOS_H */
+48
View File
@@ -0,0 +1,48 @@
/**
* @file app_myshell.c
* @brief 扩展 Shell 命令(与 CCU601E_D\BSP\letter_shell\app_myshell.c 对齐的串口打印开关等)
*/
#include "publicdata/public_define.h"
#if (MY_SHELL_EN)
#include "letter_shell/shell_port.h"
#include "usart.h"
#include "shell.h"
#include "publicdata/publicdata.h"
/* slot 0~6 对应:USART0, USART2, UART3, UART4, USART5, UART6, UART7(与 u16_usart_* 的 E_USART_ID 一致) */
static const uint8_t s_usart_slot_hw_num[USART_NUM_PRINT_CNT] = { 0U, 2U, 3U, 4U, 5U, 6U, 7U };
void usart_printf_Func(int id, int on)
{
if ((id < 0) || (id >= (int)USART_NUM_PRINT_CNT)) {
v_shell_print("usart_printf: id must be 0~6 (0=USART0,1=USART2,2=UART3,...)\r\n");
return;
}
if ((on < 0) || (on > 1)) {
v_shell_print("usart_printf: on must be 0 or 1\r\n");
return;
}
u8_uasrt_print[(uint8_t)id] = (U8_T)on;
v_shell_print("usart slot[%d] hw=USART%u print=%d\r\n", id, (unsigned int)s_usart_slot_hw_num[(uint8_t)id], on);
v_shell_print("flags [%d][%d][%d][%d] [%d][%d][%d]\r\n",
(int)u8_uasrt_print[0], (int)u8_uasrt_print[1], (int)u8_uasrt_print[2], (int)u8_uasrt_print[3],
(int)u8_uasrt_print[4], (int)u8_uasrt_print[5], (int)u8_uasrt_print[6]);
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
usart_printf,
usart_printf_Func,
shell-- usart_printf slot(0-6) on(0/1) dump u16 send/recv);
// 命令
static void v_reboot_func(void)
{
v_shell_print("Execute system reboot command\r\n");
v_sys_reboot();
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), reboot, v_reboot_func, myshlle-- reboot sys);
#endif /* MY_SHELL_EN */
+179
View File
@@ -0,0 +1,179 @@
/**
* @file shell_cfg_user.h
* @author Letter (nevermindzzt@gmail.com)
* @brief shell config
* @version 3.0.0
* @date 2019-12-31
*
* @copyright (c) 2019 Letter
*
*/
#ifndef __SHELL_CFG_USER_H__
#define __SHELL_CFG_USER_H__
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
/**
* @brief 是否使用默认shell任务while循环,使能宏`SHELL_USING_TASK`后此宏有意义
* 使能此宏,则`shellTask()`函数会一直循环读取输入,一般使用操作系统建立shell
* 任务时使能此宏,关闭此宏的情况下,一般适用于无操作系统,在主循环中调用`shellTask()`
*/
#define SHELL_TASK_WHILE 1
/**
* @brief 是否使用命令导出方式
* 使能此宏后,可以使用`SHELL_EXPORT_CMD()`等导出命令
* 定义shell命令,关闭此宏的情况下,需要使用命令表的方式
*/
#define SHELL_USING_CMD_EXPORT 1
/**
* @brief 是否使用shell伴生对象
* 一些扩展的组件(文件系统支持,日志工具等)需要使用伴生对象
*/
#define SHELL_USING_COMPANION 0
/**
* @brief 支持shell尾行模式
*/
#define SHELL_SUPPORT_END_LINE 1
/**
* @brief 是否在输出命令列表中列出用户
*/
#define SHELL_HELP_LIST_USER 0
/**
* @brief 是否在输出命令列表中列出变量
*/
#define SHELL_HELP_LIST_VAR 0
/**
* @brief 是否在输出命令列表中列出按键
*/
#define SHELL_HELP_LIST_KEY 0
/**
* @brief 是否在输出命令列表中展示命令权限
*/
#define SHELL_HELP_SHOW_PERMISSION 1
/**
* @brief 使用LF作为命令行回车触发
* 可以和SHELL_ENTER_CR同时开启
*/
#define SHELL_ENTER_LF 0
/**
* @brief 使用CR作为命令行回车触发
* 可以和SHELL_ENTER_LF同时开启
*/
#define SHELL_ENTER_CR 1
/**
* @brief 使用CRLF作为命令行回车触发
* 不可以和SHELL_ENTER_LF或SHELL_ENTER_CR同时开启
*/
#define SHELL_ENTER_CRLF 0
/**
* @brief 使用执行未导出函数的功能
* 启用后,可以通过`exec [addr] [args]`直接执行对应地址的函数
* @attention 如果地址错误,可能会直接引起程序崩溃
*/
#define SHELL_EXEC_UNDEF_FUNC 0
/**
* @brief shell命令参数最大数量
* 包含命令名在内,超过16个参数并且使用了参数自动转换的情况下,需要修改源码
*/
#define SHELL_PARAMETER_MAX_NUMBER 4
/**
* @brief 历史命令记录数量
*/
#define SHELL_HISTORY_MAX_NUMBER 5
/**
* @brief 双击间隔(ms)
* 使能宏`SHELL_LONG_HELP`后此宏生效,定义双击tab补全help的时间间隔
*/
#define SHELL_DOUBLE_CLICK_TIME 200
/**
* @brief 管理的最大shell数量 --允许最多 2 个 Shell 实例
*/
#define SHELL_MAX_NUMBER 1
/**
* @brief shell格式化输出的缓冲大小
* 为0时不使用shell格式化输出
*/
#define SHELL_PRINT_BUFFER 128
/**
* @brief shell格式化输入的缓冲大小
* 为0时不使用shell格式化输入
* @note shell格式化输入会阻塞shellTask, 仅适用于在有操作系统的情况下使用
*/
#define SHELL_SCAN_BUFFER 0
/**
* @brief 获取系统时间(ms)
* 定义此宏为获取系统Tick,如`HAL_GetTick()`
* @note 此宏不定义时无法使用双击tab补全命令help,无法使用shell超时锁定
*/
#define SHELL_GET_TICK() ((uint32_t)(xTaskGetTickCount() * portTICK_PERIOD_MS))
/**
* @brief 使用锁
* @note 使用shell锁时,需要对加锁和解锁进行实现
*/
#define SHELL_USING_LOCK 1
/**
* @brief shell内存分配
* shell本身不需要此接口,若使用shell伴生对象,需要进行定义
*/
#define SHELL_MALLOC(size) 0
/**
* @brief shell内存释放
* shell本身不需要此接口,若使用shell伴生对象,需要进行定义
*/
#define SHELL_FREE(obj) 0
/**
* @brief 是否显示shell信息
*/
#define SHELL_SHOW_INFO 1
/**
* @brief 是否在登录后清除命令行
*/
#define SHELL_CLS_WHEN_LOGIN 1
/**
* @brief shell默认用户
*/
#define SHELL_DEFAULT_USER "jiankalka"
/**
* @brief shell默认用户密码
* 若默认用户不需要密码,设为""
*/
#define SHELL_DEFAULT_USER_PASSWORD ""//"ieszt1234"
/**
* @brief shell自动锁定超时
* shell当前用户密码有效的时候生效,超时后会自动重新锁定shell
* 设置为0时关闭自动锁定功能,时间单位为`SHELL_GET_TICK()`单位
* @note 使用超时锁定必须保证`SHELL_GET_TICK()`有效
*/
#define SHELL_LOCK_TIMEOUT 0 * 60 * 1000
#endif
+184
View File
@@ -0,0 +1,184 @@
/**
* @file shell_port.c
* @author Letter (NevermindZZT@gmail.com)
* @brief
* @version 0.1
* @date 2019-02-22
*
* @copyright (c) 2019 Letter
*
*/
#include "publicdata/public_define.h"
#if (MY_SHELL_EN)
#include "shell.h"
#include <stdarg.h>
#include <stdio.h>
#include "FreeRTOS.h"
#include "semphr.h"
#include "task.h"
#include "usart.h"
#include "wdt_task/wdt_task.h"
#include "app_init/app_init.h"
Shell shell;
char shellBuffer[512];
#define SHELL_USART_ID ((E_USART_ID)DEBUG_SHELL_USART_ID)
static SemaphoreHandle_t shellMutex;
/**
* @brief 用户shell写
*
* @param data 数据
* @param len 数据长度
*
* @return short 实际写入的数据长度
*/
short userShellWrite(char *data, unsigned short len)
{
if ((uint8_t)DEBUG_SHELL_USART_ID == (uint8_t)DEBUG_SHELL_USART_NULL) {
return 0;
}
u16_usart_send(SHELL_USART_ID,(uint8_t *)data,len);
return len;
}
/**
* @brief 用户shell读
*
* @param data 数据
* @param len 数据长度
*
* @return short 实际读取到
*/
short userShellRead(char *data, unsigned short len)
{
short recv_len;
if ((uint8_t)DEBUG_SHELL_USART_ID == (uint8_t)DEBUG_SHELL_USART_NULL) {
return 0;
}
recv_len = u16_usart_recv(SHELL_USART_ID,(uint8_t *)data,len);
return recv_len;
}
/**
* @brief 用户shell上锁
*
* @param shell shell
*
* @return int 0
*/
int userShellLock(Shell *shell)
{
(void)shell;
if (shellMutex != NULL) {
(void)xSemaphoreTakeRecursive(shellMutex, portMAX_DELAY);
}
return 0;
}
/**
* @brief 用户shell解锁
*
* @param shell shell
*
* @return int 0
*/
int userShellUnlock(Shell *shell)
{
(void)shell;
if (shellMutex != NULL) {
(void)xSemaphoreGiveRecursive(shellMutex);
}
return 0;
}
/**
* @brief 用户shell初始化
*
*/
void userShellInit(void)
{
if ((uint8_t)DEBUG_SHELL_USART_ID == (uint8_t)DEBUG_SHELL_USART_NULL) {
return;
}
shellMutex = xSemaphoreCreateRecursiveMutex();
shell.write = userShellWrite;
shell.read = userShellRead;
shell.lock = userShellLock;
shell.unlock = userShellUnlock;
shellInit(&shell, shellBuffer, 512);
}
//CEVENT_EXPORT(EVENT_INIT_STAGE2, userShellInit);
void v_myshell_task(void *argument)
{
for (;;) {
v_wdt_setFlag(TASK_ID_MyShell);
shellTask(argument);
/* shellTask 内部有 while(1) + vTaskDelay(10ms)
* 正常情况下不会执行到这里;若返回则重新进入 */
mSleep(10U);
}
}
#else /* MY_SHELL_EN */
/* 关闭 Shell 时,该文件不产生任何可执行代码 */
#endif /* MY_SHELL_EN */
/************************************************************************************* */
#if (MY_SHELL_EN)
unsigned short shell_send_String(const char *string)
{
unsigned short count = 0;
const char *p = string;
SHELL_ASSERT(shell.write, return 0);
while(*p++)
{
count ++;
}
return shell.write((char *)string, count);
}
#define SHELL_PRINT_BUFFER_SIZE 256 // 可调整缓冲区大小
// 类似printf的格式化输出函数
void v_shell_print(const char *format, ...)
{
va_list args;
char buffer[SHELL_PRINT_BUFFER_SIZE];
int len;
// 1. 处理可变参数
va_start(args, format);
// 2. 安全格式化字符串
len = vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
// 3. 校验长度有效性
if(len <= 0) return; // 格式化失败
if(len >= (int)sizeof(buffer)) {
// 截断到最大长度
buffer[sizeof(buffer) - 1] = '\0';
len = sizeof(buffer) - 1;
}
// 4. 发送数据到串口
if(len > 0) {
if ((uint8_t)DEBUG_SHELL_USART_ID == (uint8_t)DEBUG_SHELL_USART_NULL) {
return;
}
u16_usart_send(SHELL_USART_ID, (uint8_t *)buffer, (uint16_t)len);
}
}
#endif /* MY_SHELL_EN */
+33
View File
@@ -0,0 +1,33 @@
/**
* @file shell_port.h
* @author Letter (NevermindZZT@gmail.com)
* @brief
* @version 0.1
* @date 2019-02-22
*
* @copyright (c) 2019 Letter
*
*/
#ifndef __SHELL_PORT_H__
#define __SHELL_PORT_H__
#include "publicdata/public_define.h"
#if (MY_SHELL_EN)
#include "shell.h"
#endif
/* MY_SHELL_EN=0 时提供空桩,避免链接进 shell 代码 */
#if (MY_SHELL_EN)
extern Shell shell;
void userShellInit(void);
void v_myshell_task(void *argument);
#else
static inline void userShellInit(void) {}
static inline void v_myshell_task(void *argument) { (void)argument; }
#endif
unsigned short shell_send_String(const char *string);
void v_shell_print(const char *format, ...);
#endif
+940
View File
@@ -0,0 +1,940 @@
/*!
\file exmc_nandflash_fixed.c
\brief improved NAND Flash driver with complete GPIO configuration
\version 2026-03-09, V1.4.0, firmware for GD32H7xx
*/
/*
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.
*/
#if 0
#include "gd32h7xx.h"
#include "exmc_nandflash_fixed.h"
#include <stdio.h>
/* NAND Flash操作区域定义 */
#define NAND_CMD_AREA *(__IO uint8_t *)(BANK_NAND_ADDR | EXMC_CMD_AREA)
#define NAND_ADDR_AREA *(__IO uint8_t *)(BANK_NAND_ADDR | EXMC_ADDR_AREA)
#define NAND_DATA_AREA *(__IO uint8_t *)(BANK_NAND_ADDR | EXMC_DATA_AREA)
/* NAND Flash地址计算宏 */
#define ROW_ADDRESS (address.page + (address.block + (address.zone * NAND_ZONE_SIZE)) * NAND_BLOCK_SIZE)
/* 页位计数 */
#define PAGE_BIT 6
/* 私有变量 */
static uint8_t nand_initialized = 0;
/* 私有函数声明 */
static uint8_t exmc_nand_getstatus(void);
static uint8_t exmc_nand_writedata(uint8_t *pbuffer, nand_address_struct physicaladdress, uint16_t bytecount);
static uint8_t exmc_nand_readdata(uint8_t *pbuffer, nand_address_struct phyaddress, uint16_t bytecount);
static uint8_t exmc_nand_writepage(uint8_t *pbuffer, nand_address_struct address, uint16_t bytecount);
static uint8_t exmc_nand_readpage(uint8_t *pbuffer, nand_address_struct address, uint16_t bytecount);
static uint8_t exmc_nand_eraseblock(uint32_t blocknum);
static uint8_t exmc_nand_readstatus(void);
static void exmc_nand_gpio_config(void);
static void exmc_nand_timing_config(void);
static void exmc_nand_delay_ms(uint32_t ms);
/*!
\brief 配置NAND Flash的GPIO引脚
\param[in] none
\param[out] none
\retval none
\note 硬件配置:EXMC_D0-PD14, EXMC_D1-PD15, EXMC_D2-PD0, EXMC_D3-PD1,
EXMC_D4-PE7, EXMC_D5-PE8, EXMC_D6-PE9, EXMC_D7-PE10,
EXMC_NCE-PD7, EXMC_CLE-PD11, EXMC_ALE-PD12,
EXMC_NWE-PD5, EXMC_NOE-PD4, EXMC_NWAIT-PD6
*/
static void exmc_nand_gpio_config(void)
{
printf("配置NAND Flash GPIO引脚...\n");
/* 数据总线配置 */
printf(" 配置数据总线...\n");
/* D2(PD0), D3(PD1) - 数据线 */
gpio_af_set(GPIOD, NAND_EXMC_AF, NAND_EXMC_D2_PIN | NAND_EXMC_D3_PIN);
gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, NAND_EXMC_D2_PIN | NAND_EXMC_D3_PIN);
gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, NAND_EXMC_D2_PIN | NAND_EXMC_D3_PIN);
/* D0(PD14), D1(PD15) - 数据线 */
gpio_af_set(GPIOD, NAND_EXMC_AF, NAND_EXMC_D0_PIN | NAND_EXMC_D1_PIN);
gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, NAND_EXMC_D0_PIN | NAND_EXMC_D1_PIN);
gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, NAND_EXMC_D0_PIN | NAND_EXMC_D1_PIN);
/* D4(PE7), D5(PE8), D6(PE9), D7(PE10) - 数据线 */
gpio_af_set(GPIOE, NAND_EXMC_AF, NAND_EXMC_D4_PIN | NAND_EXMC_D5_PIN | NAND_EXMC_D6_PIN | NAND_EXMC_D7_PIN);
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_PULLUP, NAND_EXMC_D4_PIN | NAND_EXMC_D5_PIN | NAND_EXMC_D6_PIN | NAND_EXMC_D7_PIN);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, NAND_EXMC_D4_PIN | NAND_EXMC_D5_PIN | NAND_EXMC_D6_PIN | NAND_EXMC_D7_PIN);
/* 控制信号配置 */
printf(" 配置控制信号...\n");
/* CLE(PD11), ALE(PD12) - 命令/地址锁存使能 */
gpio_af_set(GPIOD, NAND_EXMC_AF, NAND_EXMC_CLE_PIN | NAND_EXMC_ALE_PIN);
gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, NAND_EXMC_CLE_PIN | NAND_EXMC_ALE_PIN);
gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, NAND_EXMC_CLE_PIN | NAND_EXMC_ALE_PIN);
/* NOE(PD4), NWE(PD5), NWAIT(PD6) - 读/写/等待信号 */
gpio_af_set(GPIOD, NAND_EXMC_AF, NAND_EXMC_NOE_PIN | NAND_EXMC_NWE_PIN | NAND_EXMC_NWAIT_PIN);
gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, NAND_EXMC_NOE_PIN | NAND_EXMC_NWE_PIN | NAND_EXMC_NWAIT_PIN);
gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, NAND_EXMC_NOE_PIN | NAND_EXMC_NWE_PIN | NAND_EXMC_NWAIT_PIN);
/* NCE(PD7) - 芯片使能信号 */
gpio_af_set(GPIOD, NAND_EXMC_AF, NAND_EXMC_NCE_PIN);
gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, NAND_EXMC_NCE_PIN);
gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_85MHZ, NAND_EXMC_NCE_PIN);
printf("NAND Flash GPIO配置完成\n");
}
/*!
\brief 配置NAND Flash时序参数
\param[in] none
\param[out] none
\retval none
*/
static void exmc_nand_timing_config(void)
{
printf("配置NAND Flash时序参数...\n");
/* 时序参数配置
SET: (tCS - tWP) = 15 - 12 = 3ns
HLD: tCH = 5ns
HIZ: tWH = 10ns
WAIT: tWP = 12ns
CTR: CLE to RE delay: 10ns
ATR: ALE to RE delay: 10ns
*/
printf(" 设置时序: SET=2, WAIT=12, HLD=5, HIZ=5\n");
}
/*!
\brief 毫秒级延时
\param[in] ms: 延时毫秒数
\param[out] none
\retval none
*/
static void exmc_nand_delay_ms(uint32_t ms)
{
for(volatile uint32_t i = 0; i < (SystemCoreClock / 4000) * ms; i++) {
__NOP();
}
}
/*!
\brief 初始化NAND Flash
\param[in] none
\param[out] none
\retval 0: 成功, -1: 失败
*/
int exmc_nandflash_init(void)
{
exmc_nand_parameter_struct nand_init_struct;
exmc_nand_timing_parameter_struct nand_timing_init_struct;
if(nand_initialized) {
return 0; /* 已初始化 */
}
printf("开始初始化NAND Flash...\n");
/* 使能时钟 */
printf(" 使能时钟...\n");
rcu_periph_clock_enable(RCU_EXMC);
rcu_periph_clock_enable(RCU_GPIOD);
rcu_periph_clock_enable(RCU_GPIOE);
rcu_periph_clock_enable(RCU_GPIOF);
rcu_periph_clock_enable(RCU_GPIOG);
/* 配置GPIO */
exmc_nand_gpio_config();
/* 配置时序 */
exmc_nand_timing_config();
/* EXMC NAND参数初始化 */
exmc_nand_struct_para_init(&nand_init_struct);
/* 时序参数配置 */
nand_timing_init_struct.setuptime = 2;
nand_timing_init_struct.waittime = 12; /* GD Flash时序 */
nand_timing_init_struct.holdtime = 5;
nand_timing_init_struct.databus_hiztime = 5;
/* NAND Flash参数配置 */
nand_init_struct.ecc_size = EXMC_ECC_SIZE_2048BYTES;
nand_init_struct.atr_latency = EXMC_ALE_RE_DELAY_5_CK_EXMC;
nand_init_struct.ctr_latency = EXMC_CLE_RE_DELAY_5_CK_EXMC;
nand_init_struct.ecc_logic = ENABLE;
nand_init_struct.databus_width = EXMC_NAND_DATABUS_WIDTH_8B;
nand_init_struct.wait_feature = ENABLE;
nand_init_struct.common_space_timing = &nand_timing_init_struct;
nand_init_struct.attribute_space_timing = &nand_timing_init_struct;
/* 初始化EXMC NAND */
exmc_nand_init(&nand_init_struct);
/* 使能EXMC NAND */
exmc_nand_enable();
nand_initialized = 1;
printf("NAND Flash初始化成功\n");
return 0;
}
/*!
\brief 去初始化NAND Flash
\param[in] none
\param[out] none
\retval none
*/
void exmc_nandflash_deinit(void)
{
/* 禁用EXMC NAND */
exmc_nand_disable();
/* 去初始化EXMC NAND */
exmc_nand_deinit();
/* 重置GPIO为输入模式 */
gpio_mode_set(GPIOD, GPIO_MODE_INPUT, GPIO_PUPD_NONE,
NAND_EXMC_D2_PIN | NAND_EXMC_D3_PIN | NAND_EXMC_NOE_PIN |
NAND_EXMC_NWE_PIN | NAND_EXMC_NWAIT_PIN | NAND_EXMC_NCE_PIN |
NAND_EXMC_CLE_PIN | NAND_EXMC_ALE_PIN | NAND_EXMC_D0_PIN | NAND_EXMC_D1_PIN);
gpio_mode_set(GPIOE, GPIO_MODE_INPUT, GPIO_PUPD_NONE,
NAND_EXMC_D4_PIN | NAND_EXMC_D5_PIN | NAND_EXMC_D6_PIN | NAND_EXMC_D7_PIN);
nand_initialized = 0;
printf("NAND Flash去初始化完成\n");
}
/*!
\brief 检查是否已初始化
\param[in] none
\param[out] none
\retval 1: 已初始化, 0: 未初始化
*/
uint8_t exmc_nandflash_is_initialized(void)
{
return nand_initialized;
}
/*!
\brief 读取NAND Flash ID
\param[in] nand_id: NAND Flash ID结构体指针
\param[out] none
\retval 0: 成功, -1: 失败
*/
int nand_read_id(nand_id_struct *nand_id)
{
uint32_t data;
if(!nand_initialized || !nand_id) {
return -1;
}
printf("读取NAND Flash ID...\n");
/* 发送读ID命令 */
NAND_CMD_AREA = NAND_CMD_READID;
__DSB();
/* 发送地址 */
NAND_ADDR_AREA = 0x00;
__DSB();
/* 读取ID */
data = *(__IO uint32_t *)(BANK_NAND_ADDR | EXMC_DATA_AREA);
nand_id->maker_id = ADDR_1ST_CYCLE(data);
nand_id->device_id = ADDR_2ND_CYCLE(data);
nand_id->third_id = ADDR_3RD_CYCLE(data);
nand_id->fourth_id = ADDR_4TH_CYCLE(data);
printf("NAND Flash ID: 制造商=0x%02X, 设备=0x%02X, 第三=0x%02X, 第四=0x%02X\n",
nand_id->maker_id, nand_id->device_id, nand_id->third_id, nand_id->fourth_id);
return 0;
}
/*!
\brief 写入NAND Flash页
\param[in] pbuffer: 数据缓冲区指针
\param[in] address: 写入地址
\param[in] bytecount: 写入字节数
\param[out] none
\retval 0: 成功, -1: 失败
*/
static uint8_t exmc_nand_writepage(uint8_t *pbuffer, nand_address_struct address, uint16_t bytecount)
{
uint16_t i;
uint32_t ecc_value;
uint8_t ecc_status = 0U;
if(!nand_initialized || !pbuffer) {
return -1;
}
/* ECC配置检查 */
if(RESET != (EXMC_NCTL & EXMC_NCTL_ECCEN)) {
exmc_nand_ecc_config(DISABLE);
exmc_nand_ecc_config(ENABLE);
ecc_status = 1U;
}
/* 发送页编程命令第一周期 */
NAND_CMD_AREA = NAND_CMD_WRITE_1ST;
__DSB();
/* 发送地址 */
NAND_ADDR_AREA = address.page_in_offset;
__DSB();
NAND_ADDR_AREA = address.page_in_offset >> 8;
__DSB();
NAND_ADDR_AREA = ADDR_1ST_CYCLE(ROW_ADDRESS);
__DSB();
NAND_ADDR_AREA = ADDR_2ND_CYCLE(ROW_ADDRESS);
__DSB();
/* 写入数据 */
for(i = 0; i < bytecount; i++) {
NAND_DATA_AREA = pbuffer[i];
__DSB();
}
/* 发送页编程命令第二周期 */
NAND_CMD_AREA = NAND_CMD_WRITE_2ND;
__DSB();
if(1U == ecc_status) {
while(RESET == (EXMC_NINTEN & EXMC_NINTEN_FFEPT));
ecc_value = exmc_ecc_get();
/* 避免编译警告 */
ecc_value = ecc_value;
}
/* 检查操作状态 */
if(NAND_READY == exmc_nand_getstatus()) {
return 0;
}
return -1;
}
/*!
\brief 读取NAND Flash页
\param[in] pbuffer: 数据缓冲区指针
\param[in] address: 读取地址
\param[in] bytecount: 读取字节数
\param[out] none
\retval 0: 成功, -1: 失败
*/
static uint8_t exmc_nand_readpage(uint8_t *pbuffer, nand_address_struct address, uint16_t bytecount)
{
uint16_t i;
uint32_t ecc_value;
uint8_t ecc_status = 0U;
if(!nand_initialized || !pbuffer) {
return -1;
}
/* ECC配置检查 */
if(RESET != (EXMC_NCTL & EXMC_NCTL_ECCEN)) {
exmc_nand_ecc_config(DISABLE);
exmc_nand_ecc_config(ENABLE);
ecc_status = 1U;
}
/* 发送读命令第一周期 */
NAND_CMD_AREA = NAND_CMD_READ1_1ST;
__DSB();
/* 发送地址 */
NAND_ADDR_AREA = address.page_in_offset;
__DSB();
NAND_ADDR_AREA = address.page_in_offset >> 8;
__DSB();
NAND_ADDR_AREA = ADDR_1ST_CYCLE(ROW_ADDRESS);
__DSB();
NAND_ADDR_AREA = ADDR_2ND_CYCLE(ROW_ADDRESS);
__DSB();
/* 发送读命令第二周期 */
NAND_CMD_AREA = NAND_CMD_READ1_2ND;
__DSB();
/* 读取数据 */
for(i = 0; i < bytecount; i++) {
pbuffer[i] = NAND_DATA_AREA;
}
if(1U == ecc_status) {
while(RESET == (EXMC_NINTEN & EXMC_NINTEN_FFEPT));
ecc_value = exmc_ecc_get();
/* 避免编译警告 */
ecc_value = ecc_value;
}
/* 检查操作状态 */
if(NAND_READY == exmc_nand_getstatus()) {
return 0;
}
return -1;
}
/*!
\brief 擦除NAND Flash块
\param[in] blocknum: 块号
\param[out] none
\retval 0: 成功, -1: 失败
*/
static uint8_t exmc_nand_eraseblock(uint32_t blocknum)
{
if(!nand_initialized) {
return -1;
}
printf("擦除块%d...\n", blocknum);
/* 发送擦除命令第一周期 */
NAND_CMD_AREA = NAND_CMD_ERASE_1ST;
__DSB();
/* 发送块地址 */
blocknum <<= PAGE_BIT;
NAND_ADDR_AREA = ADDR_1ST_CYCLE(blocknum);
__DSB();
NAND_ADDR_AREA = ADDR_2ND_CYCLE(blocknum);
__DSB();
/* 发送擦除命令第二周期 */
NAND_CMD_AREA = NAND_CMD_ERASE_2ND;
__DSB();
return (exmc_nand_getstatus());
}
/*!
\brief 写入数据到NAND Flash
\param[in] pbuffer: 数据缓冲区指针
\param[in] address: 写入地址
\param[in] bytecount: 写入字节数
\param[out] none
\retval 0: 成功, -1: 失败
*/
static uint8_t exmc_nand_writedata(uint8_t *pbuffer, nand_address_struct physicaladdress, uint16_t bytecount)
{
uint8_t *temp_pbuffer = pbuffer;
if(!nand_initialized || !pbuffer) {
return -1;
}
/* 写入前先擦除块 */
if(exmc_nand_eraseblock(physicaladdress.block) != NAND_READY) {
printf("擦除块%d失败\n", physicaladdress.block);
return -1;
}
/* 跨页写入处理 */
while(bytecount + physicaladdress.page_in_offset > NAND_PAGE_SIZE) {
if(exmc_nand_writepage(temp_pbuffer, physicaladdress, NAND_PAGE_SIZE - physicaladdress.page_in_offset) != 0) {
return -1;
}
bytecount -= NAND_PAGE_SIZE - physicaladdress.page_in_offset;
temp_pbuffer += NAND_PAGE_SIZE - physicaladdress.page_in_offset;
physicaladdress.page++;
physicaladdress.page_in_offset = 0;
}
/* 写入剩余数据 */
if(bytecount > 0) {
if(exmc_nand_writepage(temp_pbuffer, physicaladdress, bytecount) != 0) {
return -1;
}
}
return 0;
}
/*!
\brief 从NAND Flash读取数据
\param[in] pbuffer: 数据缓冲区指针
\param[in] phyaddress: 读取地址
\param[in] bytecount: 读取字节数
\param[out] none
\retval 0: 成功, -1: 失败
*/
static uint8_t exmc_nand_readdata(uint8_t *pbuffer, nand_address_struct phyaddress, uint16_t bytecount)
{
uint8_t *temp_pbuffer = pbuffer;
if(!nand_initialized || !pbuffer) {
return -1;
}
/* 跨页读取处理 */
while(bytecount + phyaddress.page_in_offset > NAND_PAGE_SIZE) {
if(exmc_nand_readpage(temp_pbuffer, phyaddress, NAND_PAGE_SIZE - phyaddress.page_in_offset) != 0) {
return -1;
}
phyaddress.page++;
temp_pbuffer += NAND_PAGE_SIZE - phyaddress.page_in_offset;
bytecount -= NAND_PAGE_SIZE - phyaddress.page_in_offset;
phyaddress.page_in_offset = 0;
}
/* 读取剩余数据 */
if(bytecount > 0) {
if(exmc_nand_readpage(temp_pbuffer, phyaddress, bytecount) != 0) {
return -1;
}
}
return 0;
}
/*!
\brief 读取NAND Flash状态
\param[in] none
\param[out] none
\retval 状态值
*/
static uint8_t exmc_nand_readstatus(void)
{
uint8_t data;
uint8_t status = NAND_BUSY;
/* 发送读状态命令 */
NAND_CMD_AREA = NAND_CMD_STATUS;
__DSB();
data = NAND_DATA_AREA;
if((data & NAND_ERROR) == NAND_ERROR) {
status = NAND_ERROR;
} else if((data & NAND_READY) == NAND_READY) {
status = NAND_READY;
} else {
status = NAND_BUSY;
}
return status;
}
/*!
\brief 获取NAND操作状态
\param[in] none
\param[out] none
\retval 操作状态
*/
static uint8_t exmc_nand_getstatus(void)
{
uint32_t timeout = 0x10000;
uint8_t status = NAND_READY;
status = exmc_nand_readstatus();
/* 等待操作完成或超时 */
while((status != NAND_READY) && (timeout != 0x00)) {
status = exmc_nand_readstatus();
timeout--;
}
if(timeout == 0x00) {
status = NAND_TIMEOUT_ERROR;
}
return status;
}
/*!
\brief 写入指定逻辑地址的数据
\param[in] memaddr: 逻辑地址
\param[in] pwritebuf: 写入数据缓冲区
\param[in] bytecount: 写入字节数
\param[out] none
\retval 0: 成功, -1: 失败
*/
int nand_write(uint32_t memaddr, uint8_t *pwritebuf, uint16_t bytecount)
{
uint32_t temp_blockremainsize;
nand_address_struct physicaladdress;
uint32_t temp;
if(!nand_initialized || !pwritebuf) {
return -1;
}
printf("写入数据到地址%d, 长度%d字节\n", memaddr, bytecount);
temp = memaddr % (NAND_BLOCK_SIZE * NAND_PAGE_SIZE);
/* 计算物理地址 */
physicaladdress.zone = memaddr / (NAND_BLOCK_SIZE * NAND_PAGE_SIZE * NAND_ZONE_SIZE);
physicaladdress.block = memaddr / (NAND_BLOCK_SIZE * NAND_PAGE_SIZE);
physicaladdress.page = temp / NAND_PAGE_SIZE;
physicaladdress.page_in_offset = temp % NAND_PAGE_SIZE;
temp_blockremainsize = (NAND_BLOCK_SIZE * NAND_PAGE_SIZE) - (NAND_PAGE_SIZE * physicaladdress.page +
physicaladdress.page_in_offset);
/* 跨块写入处理 */
while(bytecount > temp_blockremainsize) {
if(exmc_nand_writedata(pwritebuf, physicaladdress, temp_blockremainsize) != 0) {
printf("写入块%d失败\n", physicaladdress.block);
return -1;
}
physicaladdress.block++;
pwritebuf += temp_blockremainsize;
bytecount -= temp_blockremainsize;
physicaladdress.page = 0;
physicaladdress.page_in_offset = 0;
temp_blockremainsize = (NAND_BLOCK_SIZE * NAND_PAGE_SIZE);
}
/* 写入剩余数据 */
if(bytecount > 0) {
if(exmc_nand_writedata(pwritebuf, physicaladdress, bytecount) != 0) {
printf("写入剩余数据失败\n");
return -1;
}
}
printf("数据写入完成\n");
return 0;
}
/*!
\brief 从指定逻辑地址读取数据
\param[in] memaddr: 逻辑地址
\param[in] preadbuf: 读取数据缓冲区
\param[in] bytecount: 读取字节数
\param[out] none
\retval 0: 成功, -1: 失败
*/
int nand_read(uint32_t memaddr, uint8_t *preadbuf, uint16_t bytecount)
{
uint32_t temp_blockremainsize;
nand_address_struct physicaladdress;
uint32_t temp;
if(!nand_initialized || !preadbuf) {
return -1;
}
printf("从地址%d读取数据, 长度%d字节\n", memaddr, bytecount);
temp = memaddr % (NAND_BLOCK_SIZE * NAND_PAGE_SIZE);
/* 计算物理地址 */
physicaladdress.zone = memaddr / (NAND_BLOCK_SIZE * NAND_PAGE_SIZE * NAND_ZONE_SIZE);
physicaladdress.block = memaddr / (NAND_BLOCK_SIZE * NAND_PAGE_SIZE);
physicaladdress.page = temp / NAND_PAGE_SIZE;
physicaladdress.page_in_offset = temp % NAND_PAGE_SIZE;
temp_blockremainsize = (NAND_BLOCK_SIZE * NAND_PAGE_SIZE) - (NAND_PAGE_SIZE * physicaladdress.page + physicaladdress.page_in_offset);
/* 跨块读取处理 */
while(bytecount > temp_blockremainsize) {
if(exmc_nand_readdata(preadbuf, physicaladdress, temp_blockremainsize) != 0) {
printf("读取块%d失败\n", physicaladdress.block);
return -1;
}
physicaladdress.block++;
preadbuf += temp_blockremainsize;
bytecount -= temp_blockremainsize;
physicaladdress.page = 0;
physicaladdress.page_in_offset = 0;
temp_blockremainsize = (NAND_BLOCK_SIZE * NAND_PAGE_SIZE);
}
/* 读取剩余数据 */
if(bytecount > 0) {
if(exmc_nand_readdata(preadbuf, physicaladdress, bytecount) != 0) {
printf("读取剩余数据失败\n");
return -1;
}
}
printf("数据读取完成\n");
return 0;
}
/*!
\brief 格式化NAND Flash
\param[in] none
\param[out] none
\retval 0: 成功, -1: 失败
*/
int nand_format(void)
{
uint16_t i;
if(!nand_initialized) {
return -1;
}
printf("开始格式化NAND Flash (%d个块)...\n", NAND_BLOCK_COUNT);
for(i = 0; i < NAND_BLOCK_COUNT; i++) {
if(NAND_READY != exmc_nand_eraseblock(i)) {
printf("擦除块%d失败\n", i);
return -1;
}
/* 进度显示 */
if(i % 100 == 0) {
printf("格式化进度: %d/%d (%.1f%%)\n",
i + 1, NAND_BLOCK_COUNT,
(float)(i + 1) * 100.0f / NAND_BLOCK_COUNT);
}
}
printf("NAND Flash格式化完成\n");
return 0;
}
/*!
\brief 重置NAND Flash
\param[in] none
\param[out] none
\retval 0: 成功, -1: 失败
*/
int nand_reset(void)
{
if(!nand_initialized) {
return -1;
}
printf("重置NAND Flash...\n");
NAND_CMD_AREA = NAND_CMD_RESET;
__DSB();
/* 检查操作状态 */
if(NAND_READY == exmc_nand_getstatus()) {
printf("NAND Flash重置成功\n");
return 0;
}
printf("NAND Flash重置失败\n");
return -1;
}
/*!
\brief 写入备用区数据
\param[in] pbuffer: 数据缓冲区指针
\param[in] address: 写入地址
\param[in] bytecount: 写入字节数
\param[out] none
\retval 0: 成功, -1: 失败
*/
int exmc_nand_writespare(uint8_t *pbuffer, nand_address_struct address, uint16_t bytecount)
{
if(!nand_initialized || !pbuffer) {
return -1;
}
/* 检查备用区地址范围 */
if(address.page_in_offset <= NAND_PAGE_SIZE) {
printf("地址不在备用区范围\n");
return -1;
}
if(bytecount + address.page_in_offset >= NAND_PAGE_TOTAL_SIZE) {
printf("写入数据超出备用区范围\n");
return -1;
}
return exmc_nand_writepage(pbuffer, address, bytecount);
}
/*!
\brief 读取备用区数据
\param[in] pbuffer: 数据缓冲区指针
\param[in] address: 读取地址
\param[in] bytecount: 读取字节数
\param[out] none
\retval 0: 成功, -1: 失败
*/
int exmc_nand_readspare(uint8_t *pbuffer, nand_address_struct address, uint16_t bytecount)
{
if(!nand_initialized || !pbuffer) {
return -1;
}
/* 检查备用区地址范围 */
if(address.page_in_offset <= NAND_PAGE_SIZE) {
printf("地址不在备用区范围\n");
return -1;
}
if(bytecount + address.page_in_offset >= NAND_PAGE_TOTAL_SIZE) {
printf("读取数据超出备用区范围\n");
return -1;
}
return exmc_nand_readpage(pbuffer, address, bytecount);
}
/*!
\brief 填充缓冲区
\param[in] pbuffer: 数据缓冲区指针
\param[in] buffer_length: 缓冲区长度
\param[in] value: 填充值
\param[out] none
\retval none
*/
void fill_buffer_nand(uint8_t *pbuffer, uint16_t buffer_length, uint32_t value)
{
uint16_t index;
if(!pbuffer) {
return;
}
for(index = 0; index < buffer_length; index++) {
pbuffer[index] = value + index;
}
}
/*!
\brief NAND Flash自检
\param[in] none
\param[out] none
\retval 0: 成功, -1: 失败
*/
int nand_self_test(void)
{
printf("开始NAND Flash自检...\n");
/* 检查初始化状态 */
if(!nand_initialized) {
printf("NAND Flash未初始化\n");
return -1;
}
/* 读取ID测试 */
nand_id_struct nand_id;
if(nand_read_id(&nand_id) != 0) {
printf("❌ 读取NAND Flash ID失败\n");
return -1;
}
printf("✅ NAND Flash ID读取成功\n");
/* 重置测试 */
if(nand_reset() != 0) {
printf("❌ NAND Flash重置失败\n");
return -1;
}
printf("✅ NAND Flash重置成功\n");
/* 基本读写测试 */
printf("测试基本读写功能...\n");
/* 准备测试数据 */
uint8_t write_buffer[256];
uint8_t read_buffer[256];
for(int i = 0; i < 256; i++) {
write_buffer[i] = (uint8_t)(i & 0xFF);
}
/* 写入测试 */
if(nand_write(0, write_buffer, 256) != 0) {
printf("❌ 写入测试失败\n");
return -1;
}
/* 读取测试 */
if(nand_read(0, read_buffer, 256) != 0) {
printf("❌ 读取测试失败\n");
return -1;
}
/* 数据验证 */
int error_count = 0;
for(int i = 0; i < 256; i++) {
if(write_buffer[i] != read_buffer[i]) {
error_count++;
if(error_count <= 5) {
printf("数据错误: 索引%d, 写入0x%02X, 读取0x%02X\n",
i, write_buffer[i], read_buffer[i]);
}
}
}
if(error_count == 0) {
printf("✅ 基本读写测试通过\n");
} else {
printf("❌ 基本读写测试失败,错误数: %d\n", error_count);
return -1;
}
printf("NAND Flash自检完成\n");
return 0;
}
/*!
\brief NAND Flash信息显示
\param[in] none
\param[out] none
\retval none
*/
void nand_print_info(void)
{
printf("\n=== NAND Flash信息 ===\n");
printf("容量: %d MB (%d KB)\n",
(NAND_BLOCK_COUNT * NAND_BLOCK_SIZE * NAND_PAGE_SIZE) / (1024 * 1024),
(NAND_BLOCK_COUNT * NAND_BLOCK_SIZE * NAND_PAGE_SIZE) / 1024);
printf("块数量: %d\n", NAND_BLOCK_COUNT);
printf("每块页数: %d\n", NAND_BLOCK_SIZE);
printf("页大小: %d bytes\n", NAND_PAGE_SIZE);
printf("备用区大小: %d bytes\n", NAND_SPARE_AREA_SIZE);
printf("总页大小: %d bytes\n", NAND_PAGE_TOTAL_SIZE);
printf("数据总线宽度: 8 bits\n");
printf("ECC支持: 2048 bytes\n");
printf("硬件配置:\n");
printf(" 数据总线: D0-PD14, D1-PD15, D2-PD0, D3-PD1\n");
printf(" D4-PE7, D5-PE8, D6-PE9, D7-PE10\n");
printf(" 控制信号: NCE-PD7, CLE-PD11, ALE-PD12\n");
printf(" NWE-PD5, NOE-PD4, NWAIT-PD6\n");
printf("=========================\n\n");
}
#endif
+164
View File
@@ -0,0 +1,164 @@
/*!
\file exmc_nandflash_fixed.h
\brief header file of improved NAND Flash driver
\version 2026-03-09, V1.4.0, firmware for GD32H7xx
*/
/*
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 EXMC_NANDFLASH_FIXED_H
#define EXMC_NANDFLASH_FIXED_H
#include "gd32h7xx.h"
/* EXMC NAND Flash区域定义 */
#define EXMC_CMD_AREA (uint32_t)(1<<16) /* A16 = CLE high command area */
#define EXMC_ADDR_AREA (uint32_t)(1<<17) /* A17 = ALE high address area */
#define EXMC_DATA_AREA ((uint32_t)0x00000000) /* data area */
/* NAND Flash命令定义 */
#define NAND_CMD_READ1_1ST ((uint8_t)0x00)
#define NAND_CMD_READ1_2ND ((uint8_t)0x30)
#define NAND_CMD_WRITE_1ST ((uint8_t)0x80)
#define NAND_CMD_WRITE_2ND ((uint8_t)0x10)
#define NAND_CMD_ERASE_1ST ((uint8_t)0x60)
#define NAND_CMD_ERASE_2ND ((uint8_t)0xD0)
#define NAND_CMD_READID ((uint8_t)0x90)
#define NAND_CMD_STATUS ((uint8_t)0x70)
#define NAND_CMD_LOCK_STATUS ((uint8_t)0x7A)
#define NAND_CMD_RESET ((uint8_t)0xFF)
/* NAND Flash状态定义 */
#define NAND_BUSY ((uint8_t)0x00)
#define NAND_ERROR ((uint8_t)0x01)
#define NAND_READY ((uint8_t)0x40)
#define NAND_TIMEOUT_ERROR ((uint8_t)0x80)
/* NAND Flash参数定义 */
#define NAND_ZONE_COUNT ((uint16_t)0x0001) /* 区域数量 */
#define NAND_ZONE_SIZE ((uint16_t)0x0400) /* 每区块数 (1024) */
#define NAND_BLOCK_SIZE ((uint16_t)0x0040) /* 每块页数 (64) */
#define NAND_PAGE_SIZE ((uint16_t)0x0800) /* 页大小 (2048字节) */
#define NAND_SPARE_AREA_SIZE ((uint16_t)0x0080) /* 备用区大小 (128字节) */
#define NAND_PAGE_TOTAL_SIZE (NAND_PAGE_SIZE + NAND_SPARE_AREA_SIZE) /* 总页大小 */
#define NAND_MAX_ADDRESS (((NAND_ZONE_COUNT*NAND_ZONE_SIZE)*NAND_BLOCK_SIZE)*NAND_PAGE_SIZE) /* 最大地址 */
#define NAND_BLOCK_COUNT 1024 /* 块总数 */
/* NAND Flash地址计算宏 */
#define ADDR_1ST_CYCLE(ADDR) (uint8_t)((ADDR)& 0xFF)
#define ADDR_2ND_CYCLE(ADDR) (uint8_t)(((ADDR)& 0xFF00) >> 8)
#define ADDR_3RD_CYCLE(ADDR) (uint8_t)(((ADDR)& 0xFF0000) >> 16)
#define ADDR_4TH_CYCLE(ADDR) (uint8_t)(((ADDR)& 0xFF000000) >> 24)
/* 返回值定义 */
#define NAND_OK 0
#define NAND_FAIL 1
/* EXMC NAND Flash基地址 */
#define BANK_NAND_ADDR ((uint32_t)0x80000000)
/* 硬件引脚定义 */
#define NAND_EXMC_D0_PIN GPIO_PIN_14 /* PD14 */
#define NAND_EXMC_D1_PIN GPIO_PIN_15 /* PD15 */
#define NAND_EXMC_D2_PIN GPIO_PIN_0 /* PD0 */
#define NAND_EXMC_D3_PIN GPIO_PIN_1 /* PD1 */
#define NAND_EXMC_D4_PIN GPIO_PIN_7 /* PE7 */
#define NAND_EXMC_D5_PIN GPIO_PIN_8 /* PE8 */
#define NAND_EXMC_D6_PIN GPIO_PIN_9 /* PE9 */
#define NAND_EXMC_D7_PIN GPIO_PIN_10 /* PE10 */
#define NAND_EXMC_NCE_PIN GPIO_PIN_7 /* PD7 */
#define NAND_EXMC_CLE_PIN GPIO_PIN_11 /* PD11 */
#define NAND_EXMC_ALE_PIN GPIO_PIN_12 /* PD12 */
#define NAND_EXMC_NWE_PIN GPIO_PIN_5 /* PD5 */
#define NAND_EXMC_NOE_PIN GPIO_PIN_4 /* PD4 */
#define NAND_EXMC_NWAIT_PIN GPIO_PIN_6 /* PD6 */
/* 硬件端口定义 */
#define NAND_EXMC_D0_PORT GPIOD
#define NAND_EXMC_D1_PORT GPIOD
#define NAND_EXMC_D2_PORT GPIOD
#define NAND_EXMC_D3_PORT GPIOD
#define NAND_EXMC_D4_PORT GPIOE
#define NAND_EXMC_D5_PORT GPIOE
#define NAND_EXMC_D6_PORT GPIOE
#define NAND_EXMC_D7_PORT GPIOE
#define NAND_EXMC_NCE_PORT GPIOD
#define NAND_EXMC_CLE_PORT GPIOD
#define NAND_EXMC_ALE_PORT GPIOD
#define NAND_EXMC_NWE_PORT GPIOD
#define NAND_EXMC_NOE_PORT GPIOD
#define NAND_EXMC_NWAIT_PORT GPIOD
/* 复用功能定义 */
#define NAND_EXMC_AF GPIO_AF_12
/* NAND Flash ID结构体 */
typedef struct {
uint8_t maker_id; /* 制造商ID */
uint8_t device_id; /* 设备ID */
uint8_t third_id; /* 第三字节ID */
uint8_t fourth_id; /* 第四字节ID */
} nand_id_struct;
/* NAND Flash地址结构体 */
typedef struct {
uint16_t zone; /* 区域号 */
uint16_t block; /* 块号 */
uint16_t page; /* 页号 */
uint16_t page_in_offset; /* 页内偏移 */
} nand_address_struct;
/* 新的API函数声明 */
/* 初始化和配置 */
int exmc_nandflash_init(void); /* 完整初始化 */
void exmc_nandflash_deinit(void); /* 完整去初始化 */
uint8_t exmc_nandflash_is_initialized(void); /* 检查初始化状态 */
/* 基本操作 */
int nand_read_id(nand_id_struct *nand_id); /* 读取NAND Flash ID */
int nand_reset(void); /* 重置NAND Flash */
int nand_format(void); /* 格式化NAND Flash */
/* 数据读写 */
int nand_write(uint32_t memaddr, uint8_t *pwritebuf, uint16_t bytecount); /* 写入数据 */
int nand_read(uint32_t memaddr, uint8_t *preadbuf, uint16_t bytecount); /* 读取数据 */
/* 备用区操作 */
int exmc_nand_writespare(uint8_t *pbuffer, nand_address_struct address, uint16_t bytecount); /* 写入备用区 */
int exmc_nand_readspare(uint8_t *pbuffer, nand_address_struct address, uint16_t bytecount); /* 读取备用区 */
/* 工具函数 */
void fill_buffer_nand(uint8_t *pbuffer, uint16_t buffer_length, uint32_t value); /* 填充缓冲区 */
int nand_self_test(void); /* 自检测试 */
void nand_print_info(void); /* 显示NAND Flash信息 */
/* 兼容原有接口已统一到新API中,无需重复声明 */
#endif /* EXMC_NANDFLASH_FIXED_H */
+526
View File
@@ -0,0 +1,526 @@
/*!
\file gd32h7xx_enet_eval.c
\brief ethernet hardware configuration
\version 2025-02-19, V2.1.0, demo for GD32H7xx
*/
/*
Copyright (c) 2024, 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 "gd32h7xx_enet.h"
#include "main.h"
/**
* PHY 复位 GPIO 阶段在调度器启动之前执行,禁止使用 vTaskDelay。
* 使用 DWT CYCCNT 忙等毫秒级延时(与 app/main.c 中 delay_ms 思路一致)。
*/
static void enet_phy_gpio_reset_delay_ms(uint32_t ms)
{
uint32_t start;
uint32_t ticks;
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
start = DWT->CYCCNT;
ticks = (SystemCoreClock / 1000U) * ms;
while ((DWT->CYCCNT - start) < ticks) {
__NOP();
}
}
static __IO uint32_t enet_init_status = 0U;
uint8_t enet_hw_is_ready(void)
{
return (enet_init_status != 0U) ? 1U : 0U;
}
static void enet_gpio_config(void);
static void enet_mac_dma_config(void);
static void enet_gpio_resetConfig(void);
/*!
\brief setup ethernet system(GPIOs, clocks, MAC, DMA, systick)
\param[in] none
\param[out] none
\retval none
*/
void enet_system_setup(void)
{
enet_gpio_resetConfig(); //PHY复位
/* configure the GPIO ports for ethernet pins */
enet_gpio_config();
/* configure the ethernet MAC/DMA */
enet_mac_dma_config();
if (0U == enet_init_status) {
/* 不自旋死锁:无网线/PHY 异常时仍应能进 FreeRTOS,运行灯可闪烁 */
return;
}
#ifdef USE_ENET0
enet_interrupt_enable(ENET0, ENET_DMA_INT_NIE);
enet_interrupt_enable(ENET0, ENET_DMA_INT_RIE);
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
enet_desc_select_enhanced_mode(ENET0);
#endif /* SELECT_DESCRIPTORS_ENHANCED_MODE */
#endif /* USE_ENET0 */
#ifdef USE_ENET1
enet_interrupt_enable(ENET1, ENET_DMA_INT_NIE);
enet_interrupt_enable(ENET1, ENET_DMA_INT_RIE);
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
enet_desc_select_enhanced_mode(ENET1);
#endif /* SELECT_DESCRIPTORS_ENHANCED_MODE */
#endif /* USE_ENET1 */
}
/*!
\brief configures the ethernet interface
\param[in] none
\param[out] none
\retval none
*/
static void enet_mac_dma_config(void)
{
ErrStatus reval_state = ERROR;
#ifdef USE_ENET0
/* enable ethernet clock */
rcu_periph_clock_enable(RCU_ENET0);
rcu_periph_clock_enable(RCU_ENET0TX);
rcu_periph_clock_enable(RCU_ENET0RX);
/* reset ethernet on AHB bus */
enet_deinit(ENET0);
reval_state = enet_software_reset(ENET0);
if (ERROR == reval_state) {
enet_init_status = 0U;
return;
}
#ifdef CHECKSUM_BY_HARDWARE
enet_init_status = enet_init(ENET0, ENET_AUTO_NEGOTIATION, ENET_AUTOCHECKSUM_DROP_FAILFRAMES, ENET_BROADCAST_FRAMES_PASS);
#else
enet_init_status = enet_init(ENET0, ENET_AUTO_NEGOTIATION, ENET_NO_AUTOCHECKSUM, ENET_BROADCAST_FRAMES_PASS);
#endif /* CHECKSUM_BY_HARDWARE */
#endif /* USE_ENET0 */
#ifdef USE_ENET1
/* enable ethernet clock */
rcu_periph_clock_enable(RCU_ENET1);
rcu_periph_clock_enable(RCU_ENET1TX);
rcu_periph_clock_enable(RCU_ENET1RX);
/* reset ethernet on AHB bus */
enet_deinit(ENET1);
reval_state = enet_software_reset(ENET1);
if (ERROR == reval_state) {
enet_init_status = 0U;
return;
}
#ifdef CHECKSUM_BY_HARDWARE
enet_init_status = enet_init(ENET1, ENET_AUTO_NEGOTIATION, ENET_AUTOCHECKSUM_DROP_FAILFRAMES, ENET_BROADCAST_FRAMES_PASS);
#else
enet_init_status = enet_init(ENET1, ENET_AUTO_NEGOTIATION, ENET_NO_AUTOCHECKSUM, ENET_BROADCAST_FRAMES_PASS);
#endif /* CHECKSUM_BY_HARDWARE */
#endif /* USE_ENET1 */
}
/*!
\brief configures the different GPIO ports
\param[in] none
\param[out] none
\retval none
*/
static void enet_gpio_config(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_GPIOC);
// rcu_periph_clock_enable(RCU_GPIOD);
// rcu_periph_clock_enable(RCU_GPIOE);
// rcu_periph_clock_enable(RCU_GPIOG);
// rcu_periph_clock_enable(RCU_GPIOH);
// gpio_af_set(GPIOA, GPIO_AF_0, GPIO_PIN_8);
// gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
// gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8);
/* enable SYSCFG clock */
rcu_periph_clock_enable(RCU_SYSCFG);
#ifdef MII_MODE
#ifdef PHY_CLOCK_MCO
/* output HXTAL clock (25MHz) on CKOUT0 pin(PA8) to clock the PHY */
rcu_ckout0_config(RCU_CKOUT0SRC_HXTAL, RCU_CKOUT0_DIV1);
#endif /* PHY_CLOCK_MCO */
#ifdef USE_ENET0
syscfg_enet_phy_interface_config(ENET0, SYSCFG_ENET_PHY_MII);
#endif /* USE_ENET0 */
#ifdef USE_ENET1
syscfg_enet_phy_interface_config(ENET1, SYSCFG_ENET_PHY_MII);
#endif /* USE_ENET1 */
#elif defined RMII_MODE
/* choose DIV12 to get 50MHz from 600MHz on CKOUT0 pin (PA8) to clock the PHY */
// rcu_ckout0_config(RCU_CKOUT0SRC_PLL0P, RCU_CKOUT0_DIV12);
#ifdef USE_ENET0
syscfg_enet_phy_interface_config(ENET0, SYSCFG_ENET_PHY_RMII);
#endif /* USE_ENET0 */
#ifdef USE_ENET1
syscfg_enet_phy_interface_config(ENET1, SYSCFG_ENET_PHY_RMII);
#endif /* USE_ENET1 */
#endif /* MII_MODE */
#ifdef USE_ENET0
#ifdef MII_MODE
/* PA1: ETH0_MII_RX_CLK */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_1);
/* PA2: ETH0_MDIO */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_2);
/* PA7: ETH0_MII_RX_DV */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7);
gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_1);
gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_2);
gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_7);
/* PB8: ETH0_MII_TXD3 */
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8);
/* PB10: ETH0_MII_RX_ER */
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_10);
gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_8);
gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_10);
/* PC1: ETH0_MDC */
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_1);
/* PC2: ETH0_MII_TXD2 */
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_2);
/* PC3: ETH0_MII_TX_CLK */
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_3);
/* PC4: ETH0_MII_RXD0 */
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_4);
/* PC5: ETH0_MII_RXD1 */
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_5);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_1);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_2);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_3);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_4);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_5);
/* PH2: ETH0_MII_CRS */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_2);
/* PH3: ETH0_MII_COL */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_3);
/* PH6: ETH0_MII_RXD2 */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6);
/* PH7: ETH0_MII_RXD3 */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7);
gpio_af_set(GPIOH, GPIO_AF_11, GPIO_PIN_2);
gpio_af_set(GPIOH, GPIO_AF_11, GPIO_PIN_3);
gpio_af_set(GPIOH, GPIO_AF_11, GPIO_PIN_6);
gpio_af_set(GPIOH, GPIO_AF_11, GPIO_PIN_7);
/* PG11: ETH0_MII_TX_EN */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11);
/* PG13: ETH0_MII_TXD0 */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13);
/* PG14: ETH0_MII_TXD1 */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14);
gpio_af_set(GPIOG, GPIO_AF_11, GPIO_PIN_11);
gpio_af_set(GPIOG, GPIO_AF_11, GPIO_PIN_13);
gpio_af_set(GPIOG, GPIO_AF_11, GPIO_PIN_14);
/* PD8: ETH0_INT */
gpio_mode_set(GPIOD, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_8);
#elif defined RMII_MODE
/* PC1: ETH0_MDC */
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_1);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_1);
/* PA2: ETH0_MDIO */
gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_2);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_2);
/* PB12: ETH0_RMII_TXD0 */
gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_12);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12);
/* PB13: ETH0_RMII_TXD1 */
gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_13);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13);
/* PB11: ETH0_RMII_TX_EN */
gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_11);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11);
/* PC4: ETH0_RMII_RXD0 */
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_4);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_4);
/* PC5: ETH0_RMII_RXD1 */
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_5);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_5);
/* PA7: ETH0_RMII_CRS_DV */
gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_7);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7);
/* PA1: ETH0_RMII_REF_CLK */
gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_1);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_1);
#endif /* MII_MODE */
#endif /* USE_ENET0 */
#ifdef USE_ENET1
#ifdef MII_MODE
/* PH6: ETH1_MII_RXD2 */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6);
/* PH7: ETH1_MII_RXD3 */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7);
/* PH8: ETH1_MII_RXD0 */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8);
/* PH9: ETH1_MII_RXD1 */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9);
/* PH10: ETH1_MII_RX_ER */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_10);
/* PH11: ETH1_MII_RX_DV */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11);
/* PH12: ETH1_MII_RX_CLK */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12);
/* PH13: ETH1_MII_COL */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13);
/* PH14: ETH1_MDIO */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14);
/* PH15: ETH1_MII_CRS */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_15);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_15);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_6);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_7);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_8);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_9);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_10);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_11);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_12);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_13);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_14);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_15);
/* PG6: ETH1_MDC */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6);
/* PG9: ETH1_MII_TX_CLK */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9);
/* PG11: ETH1_MII_TX_EN */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11);
/* PG12: ETH1_MII_TXD2 */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12);
/* PG13: ETH1_MII_TXD0 */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13);
/* PG14: ETH1_MII_TXD1 */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14);
/* PG15: ETH1_MII_TXD3 */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_15);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_15);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_6);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_9);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_11);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_12);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_13);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_14);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_15);
/* PE1: ETH1_INT */
gpio_mode_set(GPIOE, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_1);
#elif defined RMII_MODE
/* PH8: ETH1_RMII_RXD0 */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8);
/* PH9: ETH1_RMII_RXD1 */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9);
/* PH11: ETH1_RMII_CRS_DV */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11);
/* PH12: ETH1_RMII_REF_CLK */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12);
/* PH14: ETH1_MDIO */
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_8);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_9);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_11);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_12);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_14);
/* PG6: ETH1_MDC */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6);
/* PG11: ETH1_RMII_TX_EN */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11);
/* PG13: ETH1_RMII_TXD0 */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13);
/* PG14: ETH1_RMII_TXD1 */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_6);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_11);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_13);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_14);
#endif /* MII_MODE */
#endif /* USE_ENET1 */
}
static void enet_gpio_resetConfig(void) //PHY复位引脚:PA0
{
rcu_periph_clock_enable(RCU_GPIOA);
gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_0);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_0);
gpio_bit_set(GPIOA, GPIO_PIN_0);
enet_phy_gpio_reset_delay_ms(10U);
gpio_bit_reset(GPIOA, GPIO_PIN_0); /* 复位 PHY */
enet_phy_gpio_reset_delay_ms(30U);
gpio_bit_set(GPIOA, GPIO_PIN_0);
/* 复位后勿立刻配置 MAC,至少 250ms(调度器未起,不可用 vTaskDelay */
enet_phy_gpio_reset_delay_ms(250U);
}
+47
View File
@@ -0,0 +1,47 @@
/*!
\file gd32h7xx_enet_eval.h
\brief the header file of gd32h7xx_enet_eval
\version 2025-02-19, V2.1.0, demo for GD32H7xx
*/
/*
Copyright (c) 2024, 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 GD32H7xx_ENET_EVAL_H
#define GD32H7xx_ENET_EVAL_H
#include <stdint.h>
#include "netif.h"
/* function declarations */
/* setup ethernet system(GPIOs, clocks, MAC, DMA, systick) */
void enet_system_setup(void);
/* 非 0 表示 MAC/PHY 初始化成功,可安全启用 lwIP 与 ENET 中断 */
uint8_t enet_hw_is_ready(void);
#endif /* GD32H7xx_ENET_EVAL_H */
+39
View File
@@ -0,0 +1,39 @@
#include "eth_link.h"
#include "lwip/netif.h"
#include "app_init/app_init.h"
#include "wdt_task/wdt_task.h"
#include "net_lwip/netconf.h"
/**
* @brief Ethernet link state monitor thread.
*
* @details
* - Aligns task architecture with reference project's ethernet_link_thread.
* - Polls link state of the given netif.
* - Feeds watchdog flag for EthLink task.
*
* @param argument Must be a pointer to struct netif.
*/
void ethernet_link_thread(void *argument)
{
struct netif *netif = (struct netif *)argument;
if (netif == NULL) {
for (;;) {
v_wdt_setFlag(TASK_ID_EthLink);
mSleep(MY_GET_SLEEP_TIME(TASK_ID_EthLink));
}
}
for (;;) {
(void)netif_is_link_up(netif);
v_wdt_setFlag(TASK_ID_EthLink);
mSleep(MY_GET_SLEEP_TIME(TASK_ID_EthLink));
}
}
int is_eth_link_up(void)
{
return (int)netif_is_link_up(NETCONF_ETH_NETIF);
}
+7
View File
@@ -0,0 +1,7 @@
#ifndef ETH_LINK_H
#define ETH_LINK_H
void ethernet_link_thread(void *argument);
int is_eth_link_up(void);
#endif
+220
View File
@@ -0,0 +1,220 @@
/*!
\file lwipopts.h
\brief LwIP options configuration
\version 2025-02-19, V2.1.0, demo for GD32H7xx
*/
/*
Copyright (c) 2024, 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 LWIPOPTS_H
#define LWIPOPTS_H
#define ETHARP_TRUST_IP_MAC 0
#define ARP_QUEUEING 0
#define SYS_LIGHTWEIGHT_PROT 1 /* SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection
for certain critical regions during buffer allocation,
deallocation and memory allocation and deallocation */
#define NO_SYS 0 /* NO_SYS==1: provides VERY minimal functionality.
Otherwise, use lwIP facilities */
/* memory options */
#define MEM_ALIGNMENT 4 /* should be set to the alignment of the CPU for which lwIP
is compiled. 4 byte alignment -> define MEM_ALIGNMENT
to 4, 2 byte alignment -> define MEM_ALIGNMENT to 2 */
#define MEM_SIZE (14*1024) /* the size of the heap memory, if the application will
send a lot of data that needs to be copied, this should
be set high */
/* Relocate the LwIP RAM heap pointer */
#define LWIP_RAM_HEAP_POINTER (0x30004000)
#define MEMP_NUM_PBUF 100 /* the number of memp struct pbufs. If the application
sends a lot of data out of ROM (or other static memory),
this should be set high */
#define MEMP_NUM_UDP_PCB 6 /* the number of UDP protocol control blocks, one
per active UDP "connection" */
#define MEMP_NUM_TCP_PCB 10 /* the number of simulatenously active TCP connections */
#define MEMP_NUM_TCP_PCB_LISTEN 5 /* the number of listening TCP connections */
#define MEMP_NUM_TCP_SEG 20 /* the number of simultaneously queued TCP segments */
#define MEMP_NUM_SYS_TIMEOUT 10 /* the number of simulateously active timeouts */
#define MEMP_NUM_NETBUF 8 /* the number of struct netbufs */
/* Pbuf options */
#define PBUF_POOL_SIZE 40 /* the number of buffers in the pbuf pool */
#define PBUF_POOL_BUFSIZE 1514 /* the size of each pbuf in the pbuf pool */
#define IP_REASS_MAX_PBUFS 20 /* total maximum amount of pbufs waiting to be reassembled */
/* TCP options */
#define LWIP_TCP 1
#define TCP_TTL 255
#define TCP_QUEUE_OOSEQ 0 /* controls if TCP should queue segments that arrive out of
order, Define to 0 if your device is low on memory. */
#define TCP_MSS (1500 - 40) /* TCP Maximum segment size,
TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */
#define TCP_SND_BUF (4*TCP_MSS) /* TCP sender buffer space (bytes) */
#define TCP_SND_QUEUELEN ((4* TCP_SND_BUF)/TCP_MSS) /* TCP sender buffer space (pbufs), this must be at least
as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work */
#define TCP_WND (4*TCP_MSS) /* TCP receive window */
/* ICMP options */
#define LWIP_ICMP 1
/* DHCP options */
#define LWIP_DHCP 0 /* define to 1 if you want DHCP configuration of interfaces,
DHCP is not implemented in lwIP 0.5.1, however, so
turning this on does currently not work. */
/* UDP options */
#define LWIP_UDP 1
#define UDP_TTL 255
/* DNS options */
#define LWIP_DNS 1
#define DNS_TABLE_SIZE 2
#define DNS_MAX_NAME_LENGTH 64
#define DNS_MAX_SERVERS 1
#define DNS_MAX_RETRIES 3
#define MEMP_NUM_DNS_API_MSG 2
#define MEMP_NUM_NETDB 1
#define LWIP_DNS_SECURE LWIP_DNS_SECURE_RAND_XID
/* statistics options */
#define LWIP_STATS 0
#define LWIP_PROVIDE_ERRNO 1
/* checksum options */
#define CHECKSUM_BY_HARDWARE /* computing and verifying the IP, UDP, TCP and ICMP
checksums by hardware */
/* sequential layer options */
#define LWIP_NETCONN 1 /* set to 1 to enable netconn API (require to use api_lib.c) */
#define MEMP_NUM_NETCONN 10 /* netconn pool, raise to avoid ENOBUFS during socket create */
/* socket options */
#define LWIP_SOCKET 1 /* set to 1 to enable socket API (require to use sockets.c) */
#define LWIP_SO_RCVTIMEO 1 /* set to 1 to enable receive timeout for sockets/netconns and
SO_RCVTIMEO processing */
/* Lwip debug options */
#define LWIP_DEBUG 0
#ifdef CHECKSUM_BY_HARDWARE
/* CHECKSUM_GEN_IP==0: generate checksums by hardware for outgoing IP packets.*/
#define CHECKSUM_GEN_IP 0
/* CHECKSUM_GEN_UDP==0: generate checksums by hardware for outgoing UDP packets.*/
#define CHECKSUM_GEN_UDP 0
/* CHECKSUM_GEN_TCP==0: generate checksums by hardware for outgoing TCP packets.*/
#define CHECKSUM_GEN_TCP 0
/* CHECKSUM_CHECK_IP==0: check checksums by hardware for incoming IP packets.*/
#define CHECKSUM_CHECK_IP 0
/* CHECKSUM_CHECK_UDP==0: check checksums by hardware for incoming UDP packets.*/
#define CHECKSUM_CHECK_UDP 0
/* CHECKSUM_CHECK_TCP==0: check checksums by hardware for incoming TCP packets.*/
#define CHECKSUM_CHECK_TCP 0
#define CHECKSUM_GEN_ICMP 0
#else
/* CHECKSUM_GEN_IP==1: generate checksums in software for outgoing IP packets.*/
#define CHECKSUM_GEN_IP 1
/* CHECKSUM_GEN_UDP==1: generate checksums in software for outgoing UDP packets.*/
#define CHECKSUM_GEN_UDP 1
/* CHECKSUM_GEN_TCP==1: generate checksums in software for outgoing TCP packets.*/
#define CHECKSUM_GEN_TCP 1
/* CHECKSUM_CHECK_IP==1: check checksums in software for incoming IP packets.*/
#define CHECKSUM_CHECK_IP 1
/* CHECKSUM_CHECK_UDP==1: check checksums in software for incoming UDP packets.*/
#define CHECKSUM_CHECK_UDP 1
/* CHECKSUM_CHECK_TCP==1: check checksums in software for incoming TCP packets.*/
#define CHECKSUM_CHECK_TCP 1
#define CHECKSUM_GEN_ICMP 1
#endif
/*
---------------------------------
---------- OS options ----------
---------------------------------
*/
#include "FreeRTOSConfig.h"
#include "FreeRTOS.h"
#include "task.h"
#include "app_init/app_init.h"
#include <stdint.h>
/* 与 CCU601E_D 一致:关闭 lwIP FreeRTOS 端“多线程严格检查”,避免与第三方驱动并发风格冲突 */
#ifndef LWIP_FREERTOS_CHECK_CORE_LOCKING
#define LWIP_FREERTOS_CHECK_CORE_LOCKING 0
#endif
#define MEM_LIBC_MALLOC 1
#define mem_clib_free vPortFree
#define mem_clib_malloc pvPortMalloc
uint32_t get_current_seconds(void);
#define LWIP_RAND() ((u32_t)get_current_seconds())
/* TCPIP 线程参数直接引用 app_init 任务表,保证单一配置源。 */
#define TCPIP_THREAD_NAME MY_TCPIP_NAME
#define TCPIP_THREAD_STACKSIZE MY_TCPIP_SIZE
#define TCPIP_THREAD_PRIO MY_TCPIP_PRIO
#define TCPIP_MBOX_SIZE 8
#define DEFAULT_THREAD_STACKSIZE 1024
#define LWIP_COMPAT_MUTEX 1
#define DEFAULT_TCP_RECVMBOX_SIZE 6
#define DEFAULT_UDP_RECVMBOX_SIZE 6
#define DEFAULT_ACCEPTMBOX_SIZE 6
/* ethernetif.c 收包模式:0=仅中断唤醒(默认),1=中断+超时兜底轮询 */
#ifndef ENET_RX_FALLBACK_POLLING
#define ENET_RX_FALLBACK_POLLING 0
#endif
#endif /* LWIPOPTS_H */
+39
View File
@@ -0,0 +1,39 @@
/*!
\file net_init.c
\brief deferred Ethernet + lwIP bring-up (FreeRTOS task)
*/
#include "net_init.h"
#include "enet_PHY.h"
#include "netconf.h"
/* Match app_init.h: TASK_SIZE_SIZE_2048 == configMINIMAL_STACK_SIZE * 16 */
#ifndef NET_INIT_TASK_STACK_WORDS
#define NET_INIT_TASK_STACK_WORDS ((uint16_t)((configMINIMAL_STACK_SIZE * 16U) / sizeof(StackType_t)))
#endif
static void v_net_init_task(void *arg)
{
(void)arg;
/* 等待系统其余任务先进入稳定运行态,再启动网络 */
vTaskDelay(pdMS_TO_TICKS(NET_INIT_DELAY_MS));
enet_system_setup();
if (enet_hw_is_ready() != 0U) {
lwip_stack_init();
}
vTaskDelete(NULL);
}
BaseType_t net_init_task_create(void)
{
return xTaskCreate(v_net_init_task,
"NetInit",
NET_INIT_TASK_STACK_WORDS,
NULL,
(UBaseType_t)(tskIDLE_PRIORITY + 1U),
NULL);
}
+18
View File
@@ -0,0 +1,18 @@
/*!
\file net_init.h
\brief deferred Ethernet + lwIP bring-up (FreeRTOS task)
*/
#ifndef NET_INIT_H
#define NET_INIT_H
#include "FreeRTOS.h"
#include "task.h"
#ifndef NET_INIT_DELAY_MS
#define NET_INIT_DELAY_MS 1000U
#endif
BaseType_t net_init_task_create(void);
#endif /* NET_INIT_H */
+346
View File
@@ -0,0 +1,346 @@
/*!
\file netconf.c
\brief network connection configuration
\version 2025-02-19, V2.1.0, demo for GD32H7xx
*/
/*
Copyright (c) 2024, 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 "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/dhcp.h"
#include "lwip/dns.h"
#include "ethernetif.h"
#include "main.h"
#include "netconf.h"
#include "publicdata/publicdata.h"
#include "lwip/tcpip.h"
#include <stdio.h>
#include "lwip/errno.h"
#include "queue.h"
#define DHCP_TRIES_MAX_TIMES 3
int errno;
typedef enum
{
DHCP_ADDR_NONE = 0,
DHCP_ADDR_BEGIN,
DHCP_ADDR_GOT,
DHCP_ADDR_FAIL
}dhcp_addr_status_enum;
#ifdef USE_DHCP
dhcp_addr_status_enum dhcp_state = DHCP_ADDR_NONE;
#endif /* USE_DHCP */
#ifdef USE_ENET0
struct netif g_mynetif0;
#endif /* USE_ENET0 */
#ifdef USE_ENET1
struct netif g_mynetif1;
#endif /* USE_ENET1 */
ip_addr_t ip_address = {0};
static uint8_t netconf_is_all_value(const U8_T *buf, U8_T value)
{
return (uint8_t)((buf[0] == value) && (buf[1] == value) && (buf[2] == value) && (buf[3] == value));
}
static uint8_t netconf_is_valid_host_ip(const U8_T *ip)
{
if (netconf_is_all_value(ip, 0x00U) || netconf_is_all_value(ip, 0xFFU)) {
return 0U;
}
if ((ip[0] == 0U) || (ip[0] == 127U) || (ip[0] >= 224U)) {
return 0U;
}
return 1U;
}
static uint8_t netconf_is_valid_netmask(const U8_T *mask)
{
uint32_t m;
uint8_t seen_zero = 0U;
if (netconf_is_all_value(mask, 0x00U) || netconf_is_all_value(mask, 0xFFU)) {
return 0U;
}
m = ((uint32_t)mask[0] << 24) | ((uint32_t)mask[1] << 16) | ((uint32_t)mask[2] << 8) | (uint32_t)mask[3];
for (uint32_t bit = 0U; bit < 32U; bit++) {
uint8_t cur = (uint8_t)((m >> (31U - bit)) & 0x1U);
if (cur == 0U) {
seen_zero = 1U;
} else if (seen_zero != 0U) {
return 0U;
}
}
return 1U;
}
static uint8_t netconf_is_same_subnet(const U8_T *ip, const U8_T *gw, const U8_T *mask)
{
uint32_t ip32 = ((uint32_t)ip[0] << 24) | ((uint32_t)ip[1] << 16) | ((uint32_t)ip[2] << 8) | (uint32_t)ip[3];
uint32_t gw32 = ((uint32_t)gw[0] << 24) | ((uint32_t)gw[1] << 16) | ((uint32_t)gw[2] << 8) | (uint32_t)gw[3];
uint32_t m32 = ((uint32_t)mask[0] << 24) | ((uint32_t)mask[1] << 16) | ((uint32_t)mask[2] << 8) | (uint32_t)mask[3];
return (uint8_t)(((ip32 & m32) == (gw32 & m32)) ? 1U : 0U);
}
static void netconf_get_static_ip_from_cfg(ip_addr_t *ip, ip_addr_t *mask, ip_addr_t *gw)
{
const U8_T *cfg_ip = g_t_share_data.t_sys_fix_cfg.s_net_cfg.u8_eth_ip;
const U8_T *cfg_mask = g_t_share_data.t_sys_fix_cfg.s_net_cfg.u8_eth_mask;
const U8_T *cfg_gw = g_t_share_data.t_sys_fix_cfg.s_net_cfg.u8_eth_gate;
uint8_t use_cfg = 1U;
if (netconf_is_valid_host_ip(cfg_ip) == 0U) {
use_cfg = 0U;
}
if (netconf_is_valid_host_ip(cfg_gw) == 0U) {
use_cfg = 0U;
}
if (netconf_is_valid_netmask(cfg_mask) == 0U) {
use_cfg = 0U;
}
if ((use_cfg != 0U) && (netconf_is_same_subnet(cfg_ip, cfg_gw, cfg_mask) == 0U)) {
use_cfg = 0U;
}
if (use_cfg != 0U) {
IP4_ADDR(ip, cfg_ip[0], cfg_ip[1], cfg_ip[2], cfg_ip[3]);
IP4_ADDR(mask, cfg_mask[0], cfg_mask[1], cfg_mask[2], cfg_mask[3]);
IP4_ADDR(gw, cfg_gw[0], cfg_gw[1], cfg_gw[2], cfg_gw[3]);
printf("[NETCONF] static ip from cfg: %u.%u.%u.%u, mask=%u.%u.%u.%u, gw=%u.%u.%u.%u\r\n",
cfg_ip[0], cfg_ip[1], cfg_ip[2], cfg_ip[3],
cfg_mask[0], cfg_mask[1], cfg_mask[2], cfg_mask[3],
cfg_gw[0], cfg_gw[1], cfg_gw[2], cfg_gw[3]);
} else {
IP4_ADDR(ip, BOARD_IP_ADDR0, BOARD_IP_ADDR1, BOARD_IP_ADDR2, BOARD_IP_ADDR3);
IP4_ADDR(mask, BOARD_NETMASK_ADDR0, BOARD_NETMASK_ADDR1, BOARD_NETMASK_ADDR2, BOARD_NETMASK_ADDR3);
IP4_ADDR(gw, BOARD_GW_ADDR0, BOARD_GW_ADDR1, BOARD_GW_ADDR2, BOARD_GW_ADDR3);
printf("[NETCONF] cfg ip invalid, fallback board ip: %u.%u.%u.%u\r\n",
BOARD_IP_ADDR0, BOARD_IP_ADDR1, BOARD_IP_ADDR2, BOARD_IP_ADDR3);
}
}
/*!
\brief after the netif is fully configured, it will be called to initialize the function of telnet, client and udp
\param[in] netif: the struct used for lwIP network interface
\param[out] none
\retval none
*/
void lwip_netif_status_callback(struct netif *netif)
{
if(((netif->flags & NETIF_FLAG_UP) != 0) && (0 != netif->ip_addr.addr)) {
}
}
/*!
\brief initializes the LwIP stack
\param[in] none
\param[out] none
\retval none
*/
void lwip_stack_init(void)
{
ip_addr_t gd_ipaddr;
ip_addr_t gd_netmask;
ip_addr_t gd_gw;
/* create tcp_ip stack thread */
tcpip_init(NULL, NULL);
/* IP address setting */
#ifdef USE_DHCP
gd_ipaddr.addr = 0;
gd_netmask.addr = 0;
gd_gw.addr = 0;
#else
netconf_get_static_ip_from_cfg(&gd_ipaddr, &gd_netmask, &gd_gw);
#endif /* USE_DHCP */
#ifdef USE_ENET0
/* add a new network interface */
netif_add(&g_mynetif0, &gd_ipaddr, &gd_netmask, &gd_gw, NULL, &ethernetif_init, &tcpip_input);
/* set a default network interface */
netif_set_default(&g_mynetif0);
/* set a callback when interface is up/down */
netif_set_status_callback(&g_mynetif0, lwip_netif_status_callback);
/* set the flag of netif as NETIF_FLAG_LINK_UP */
netif_set_link_up(&g_mynetif0);
/* bring an interface up and set the flag of netif as NETIF_FLAG_UP */
netif_set_up(&g_mynetif0);
#if LWIP_DNS
/* 静态 IP 时无 DHCP 下发 DNS,需手动设置 */
{
ip_addr_t dns_server;
IP_ADDR4(&dns_server, 114, 114, 114, 114);
dns_setserver(0, &dns_server);
}
#endif /* LWIP_DNS */
#endif /* USE_ENET0 */
#ifdef USE_ENET1
/* add a new network interface */
netif_add(&g_mynetif1, &gd_ipaddr, &gd_netmask, &gd_gw, NULL, &ethernetif_init, &tcpip_input);
/* set a default network interface */
netif_set_default(&g_mynetif1);
/* set a callback when interface is up/down */
netif_set_status_callback(&g_mynetif1, lwip_netif_status_callback);
/* set the flag of netif as NETIF_FLAG_LINK_UP */
netif_set_link_up(&g_mynetif1);
/* bring an interface up and set the flag of netif as NETIF_FLAG_UP */
netif_set_up(&g_mynetif1);
#if LWIP_DNS
{
ip_addr_t dns_server;
IP_ADDR4(&dns_server, 114, 114, 114, 114);
dns_setserver(0, &dns_server);
}
#endif /* LWIP_DNS */
#endif /* USE_ENET1 */
}
#ifdef USE_DHCP
/*!
\brief dhcp_task
\param[in] none
\param[out] none
\retval none
*/
void dhcp_task(void * pvParameters)
{
ip_addr_t gd_ipaddr;
ip_addr_t gd_netmask;
ip_addr_t gd_gw;
#ifdef USE_ENET0
struct dhcp *dhcp_client0;
#endif /* USE_ENET0 */
#ifdef USE_ENET1
struct dhcp *dhcp_client1;
#endif /* USE_ENET1 */
for(;;){
#ifdef USE_ENET0
switch(dhcp_state){
case DHCP_ADDR_NONE:
dhcp_start(&g_mynetif0);
/* IP address should be set to 0 every time we want to assign a new DHCP address*/
ip_address.addr = 0;
dhcp_state = DHCP_ADDR_BEGIN;
break;
case DHCP_ADDR_BEGIN:
/* got the IP address */
ip_address.addr = g_mynetif0.ip_addr.addr;
if(0 != ip_address.addr){
dhcp_state = DHCP_ADDR_GOT;
printf("\r\nDHCP -- eval board ip address: %d.%d.%d.%d \r\n", ip4_addr1_16(&ip_address), \
ip4_addr2_16(&ip_address), ip4_addr3_16(&ip_address), ip4_addr4_16(&ip_address));
}else{
/* DHCP timeout */
dhcp_client0 = netif_dhcp_data(&g_mynetif0);
if(dhcp_client0->tries > DHCP_TRIES_MAX_TIMES){
dhcp_state = DHCP_ADDR_FAIL;
/* stop DHCP */
dhcp_stop(&g_mynetif0);
/* use static address as IP address */
IP4_ADDR(&gd_ipaddr, BOARD_IP_ADDR0, BOARD_IP_ADDR1, BOARD_IP_ADDR2, BOARD_IP_ADDR3);
IP4_ADDR(&gd_netmask, BOARD_NETMASK_ADDR0, BOARD_NETMASK_ADDR1, BOARD_NETMASK_ADDR2, BOARD_NETMASK_ADDR3);
IP4_ADDR(&gd_gw, BOARD_GW_ADDR0, BOARD_GW_ADDR1, BOARD_GW_ADDR2, BOARD_GW_ADDR3);
netif_set_addr(&g_mynetif0, &gd_ipaddr, &gd_netmask, &gd_gw);
}
}
break;
default:
break;
}
#endif /* USE_ENET0 */
#ifdef USE_ENET1
switch(dhcp_state){
case DHCP_ADDR_NONE:
dhcp_start(&g_mynetif1);
/* IP address should be set to 0 every time we want to assign a new DHCP address*/
ip_address.addr = 0;
dhcp_state = DHCP_ADDR_BEGIN;
break;
case DHCP_ADDR_BEGIN:
/* got the IP address */
ip_address.addr = g_mynetif1.ip_addr.addr;
if(0 != ip_address.addr){
dhcp_state = DHCP_ADDR_GOT;
printf("\r\nDHCP -- eval board ip address: %d.%d.%d.%d \r\n", ip4_addr1_16(&ip_address), \
ip4_addr2_16(&ip_address), ip4_addr3_16(&ip_address), ip4_addr4_16(&ip_address));
}else{
/* DHCP timeout */
dhcp_client1 = netif_dhcp_data(&g_mynetif1);
if(dhcp_client1->tries > DHCP_TRIES_MAX_TIMES){
dhcp_state = DHCP_ADDR_FAIL;
/* stop DHCP */
dhcp_stop(&g_mynetif1);
/* use static address as IP address */
IP4_ADDR(&gd_ipaddr, BOARD_IP_ADDR0, BOARD_IP_ADDR1, BOARD_IP_ADDR2, BOARD_IP_ADDR3);
IP4_ADDR(&gd_netmask, BOARD_NETMASK_ADDR0, BOARD_NETMASK_ADDR1, BOARD_NETMASK_ADDR2, BOARD_NETMASK_ADDR3);
IP4_ADDR(&gd_gw, BOARD_GW_ADDR0, BOARD_GW_ADDR1, BOARD_GW_ADDR2, BOARD_GW_ADDR3);
netif_set_addr(&g_mynetif1, &gd_ipaddr, &gd_netmask, &gd_gw);
}
}
break;
default:
break;
}
#endif /* USE_ENET1 */
/* wait 250 ms */
vTaskDelay(250);
}
}
#endif /* USE_DHCP */
+68
View File
@@ -0,0 +1,68 @@
/*!
\file netconf.h
\brief the header file of netconf
\version 2025-02-19, V2.1.0, demo for GD32H7xx
*/
/*
Copyright (c) 2024, 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 NETCONF_H
#define NETCONF_H
#include "main.h"
struct netif;
#if defined(USE_ENET0) && defined(USE_ENET1)
#error "USE_ENET0 and USE_ENET1 cannot both be defined (see main.h)"
#endif
#if !defined(USE_ENET0) && !defined(USE_ENET1)
#error "Define either USE_ENET0 or USE_ENET1 in main.h for Ethernet netif"
#endif
#ifdef USE_ENET0
extern struct netif g_mynetif0;
#define NETCONF_ETH_NETIF (&g_mynetif0)
#endif
#ifdef USE_ENET1
extern struct netif g_mynetif1;
#define NETCONF_ETH_NETIF (&g_mynetif1)
#endif
/* function declarations */
/* initializes the LwIP stack */
void lwip_stack_init(void);
/* dhcp_task */
void dhcp_task(void * pvParameters);
/* netif status callback function */
//void lwip_netif_status_callback(struct netif *netif);
#endif /* NETCONF_H */
+194
View File
@@ -0,0 +1,194 @@
# CCU621_M Pin Config Table
> 路径:`CCU621_M/BSP/pin_config_table.md`
> 说明:根据当前工程实际代码(`BSP/GPIO/gpio.h`、`BSP/sys_drv_init.h`)整理。
> 主索引:[README](../README.md)
> 若与硬件原理图/BOM有差异,以硬件文档为准。
## 1. DO输出(继电器/电子锁)
| 逻辑名 | 端口引脚 | 宏定义 | 用途 |
| --- | --- | --- | --- |
| DO1 | PG4 | `RELAY_K1_PORT/PIN` | 板载继电器 K1 |
| DO2 | PG5 | `RELAY_K2_PORT/PIN` | 板载继电器 K2 |
| DO3 | PG6 | `RELAY_K3_PORT/PIN` | 板载继电器 K3 |
| DO4 | PG7 | `RELAY_K4_PORT/PIN` | 板载继电器 K4 |
| DO5 | PG8 | `RELAY_K5_PORT/PIN` | 板载继电器 K5 |
| DO6 | PC8 | `RELAY_K6_PORT/PIN` | 板载继电器 K6 |
| DO7 | PC9 | `RELAY_K7_PORT/PIN` | 板载继电器 K7 |
| DO8 | PG9 | `RELAY_K8_PORT/PIN` | 板载继电器 K8 |
| DO9 | PG10 | `RELAY_K9_PORT/PIN` | 板载继电器 K9 |
| DO10 | PG11 | `RELAY_K10_PORT/PIN` | 板载继电器 K10 |
| ELOCK_A | PK1 | `ELOCK_A_PORT/PIN` | A枪电子锁控制 |
| ELOCK_B | PK2 | `ELOCK_B_PORT/PIN` | B枪电子锁控制 |
## 2. 绝缘检测相关DO
| 逻辑名 | 端口引脚 | 宏定义 | 用途 |
| --- | --- | --- | --- |
| INSA_CTL | PE4 | `INSA_GND_PORT/PIN` | A枪绝缘监测接地开关 |
| UNBALA_CTL | PE3 | `INSA_POS_PORT/PIN` | A枪绝缘监测正极回路开关 |
| INSB_CTL | PA5 | `INSB_GND_PORT/PIN` | B枪绝缘监测接地开关 |
| UNBALB_CTL | PE5 | `INSB_POS_PORT/PIN` | B枪绝缘监测正极回路开关 |
## 3. 其它控制DO
| 逻辑名 | 端口引脚 | 宏定义 | 用途 |
| --- | --- | --- | --- |
| H7_RUN | PG15 | `LED_RUN_PORT/PIN` | 运行指示灯 |
| CHK_CSA_CTL | PF8 | `CHK_CSA_PORT/PIN` | 连接确认模式选择 A |
| CHK_CSB_CTL | PF2 | `CHK_CSB_PORT/PIN` | 连接确认模式选择 B |
| WDO | PE2 | `WDO_PORT/PIN` | 看门狗翻转输出 |
| ESP8685_EN | PB7 | `ESP8685_EN_PORT/PIN` | ESP8685使能 |
| BLUE_EN | PE6 | `BLUE_EN_PORT/PIN` | 蓝牙/WiFi模块使能 |
| CIU_RST | PA10 | `CIU_RST_PORT/PIN` | 加密芯片复位 |
| 4G_POWER | PF3 | `PWR_4G_PORT/PIN` | 4G模块电源控制 |
| 4G_RST | PC13 | `RST_4G_PORT/PIN` | 4G模块复位控制 |
| TEMP_ADDR_A | PD10 | `TEMP_ADDR_A_PORT/PIN` | 温度采集地址A |
| TEMP_ADDR_B | PD13 | `TEMP_ADDR_B_PORT/PIN` | 温度采集地址B |
| TEMP_ADDR_C | PJ8 | `TEMP_ADDR_C_PORT/PIN` | 温度采集地址C |
## 4. DI输入(YX
| 逻辑名 | 端口引脚 | 宏定义 | 备注 |
| --- | --- | --- | --- |
| YX1 | PC0 | `YX1_PORT/PIN` | 系统DI |
| YX2 | PB0 | `YX2_PORT/PIN` | 系统DI |
| YX3 | PF14 | `YX3_PORT/PIN` | 系统DI |
| YX4 | PB1 | `YX4_PORT/PIN` | 系统DI |
| YX5 | PF15 | `YX5_PORT/PIN` | 系统DI |
| YX6 | PB2 | `YX6_PORT/PIN` | 系统DI |
| YX7 | PG0 | `YX7_PORT/PIN` | 系统DI |
| YX8 | PF11 | `YX8_PORT/PIN` | 系统DI |
| YX9 | PE11 | `YX9_PORT/PIN` | 系统DI |
| YX10 | PF12 | `YX10_PORT/PIN` | 系统DI |
| YX11 | PE15 | `YX11_PORT/PIN` | 系统DI |
| YX12 | PF13 | `YX12_PORT/PIN` | 系统DI |
## 5. 其它输入
| 逻辑名 | 端口引脚 | 宏定义 | 备注 |
| --- | --- | --- | --- |
| AUX_PADET | PG1 | `AUX_PADET_PORT/PIN` | A枪辅助电源检测 |
| AUX_PBDET | PB10 | `AUX_PBDT_PORT/PIN` | B枪辅助电源检测 |
| CHECKOUTA1 | PF10 | `CHECKOUTA1_PORT/PIN` | A枪连接确认输入 |
| CHECKOUTA2 | PF9 | `CHECKOUTA2_PORT/PIN` | A枪连接确认输入 |
| CHECKOUTB1 | PD3 | `CHECKOUTB1_PORT/PIN` | B枪连接确认输入 |
| CHECKOUTB2 | PA15 | `CHECKOUTB2_PORT/PIN` | B枪连接确认输入 |
## 6. 串口(USART/UART
| 外设 | TX | RX | 波特率 | 备注 |
| --- | --- | --- | --- | --- |
| USART0 | PF4 | PF5 | 115200 | 副板通信 `SubComm` |
| USART2 | PD8 | PD9 | 115200 | 通用串口 |
| UART3 | PC10 | PC11 | 2400 | 8E1 |
| UART4 | PC12 | PD2 | 2400 | 8E1 |
| USART5 | PC6 | PC7 | 115200 | 默认 `printf` 输出口 |
| UART6 | PB4 | PB3 | 115200 | 通用串口 |
| UART7 | PE1 | PE0 | 115200 | 通用串口 |
## 7. CAN
| 外设 | TX | RX | AF | 备注 |
| --- | --- | --- | --- | --- |
| CAN0 | PB9 | PB8 | AF9 | 250 kbps |
| CAN1 | PB6 | PB5 | AF9 | 250 kbps |
| CAN2 | PF7 | PF6 | AF2 | 125 kbps |
## 8. I2C / EEPROM
| 外设 | SCL | SDA | AF | 备注 |
| --- | --- | --- | --- | --- |
| I2C1 | PF1 | PF0 | AF4 | EEPROM / FM24CL16 |
## 9. SPI与模拟采集/存储
### 9.1 SPI1(预留安全芯片)
| 信号 | 引脚 | AF | 备注 |
| --- | --- | --- | --- |
| SPI1_NSS | PA8 | GPIO输出 | 软件片选 |
| SPI1_SCK | PA9 | AF5 | 时钟 |
| SPI1_MISO | PG2 | AF5 | 输入 |
| SPI1_MOSI | PG3 | AF5 | 输出 |
### 9.2 SPI3(绝缘/高压采样)
| 信号 | 引脚 | AF | 备注 |
| --- | --- | --- | --- |
| SPI3_CSA | PA4 | GPIO输出 | A枪片选 |
| SPI3_CSB | PA3 | GPIO输出 | B枪片选 |
| SPI3_SCK | PE12 | AF5 | 时钟 |
| SPI3_MISO | PE13 | AF5 | 输入 |
| SPI3_MOSI | PE14 | AF5 | 输出 |
### 9.3 SPI4(外部Flash
| 信号 | 引脚 | AF | 备注 |
| --- | --- | --- | --- |
| SPI4_CS | PJ9 | GPIO输出 | 外部Flash片选 |
| SPI4_SCK | PK0 | AF5 | 时钟 |
| SPI4_MOSI | PJ11 | AF5 | 输出 |
| SPI4_MISO | PJ10 | AF5 | 输入 |
### 9.4 SPI5(模拟量采集)
| 信号 | 引脚 | AF | 备注 |
| --- | --- | --- | --- |
| SPI5_CS | PG12 | GPIO输出 | 模拟采集芯片片选 |
| SPI5_SCK | PG13 | AF5 | 时钟 |
| SPI5_MISO | PA6 | AF8 | 输入 |
| SPI5_MOSI | PG14 | AF5 | 输出 |
## 10. NAND Flash / EXMC
| 信号 | 引脚 | AF | 备注 |
| --- | --- | --- | --- |
| EXMC_D0 | PD14 | AF12 | NAND数据线 |
| EXMC_D1 | PD15 | AF12 | NAND数据线 |
| EXMC_D2 | PD0 | AF12 | NAND数据线 |
| EXMC_D3 | PD1 | AF12 | NAND数据线 |
| EXMC_D4 | PE7 | AF12 | NAND数据线 |
| EXMC_D5 | PE8 | AF12 | NAND数据线 |
| EXMC_D6 | PE9 | AF12 | NAND数据线 |
| EXMC_D7 | PE10 | AF12 | NAND数据线 |
| EXMC_NCE | PD7 | AF12 | 片选 |
| EXMC_CLE | PD11 | AF12 | 命令锁存 |
| EXMC_ALE | PD12 | AF12 | 地址锁存 |
| EXMC_NWE | PD5 | AF12 | 写使能 |
| EXMC_NOE | PD4 | AF12 | 读使能 |
| EXMC_NWAIT | PD6 | AF12 | 等待输入 |
## 11. 以太网(ENET
> 当前工程代码中已配置 ENET 相关 GPIO;以下为代码里明确可见的 RMII/MII 相关引脚,实际使用模式以 `BSP/net_lwip/enet_PHY.c` 为准。
| 信号 | 引脚 | AF | 备注 |
| --- | --- | --- | --- |
| ENET_REF_CLK / RX_CLK | PA1 | AF11 | 以太网时钟相关 |
| ENET_MDIO | PA2 | AF11 | PHY管理 |
| ENET_CRS_DV / CRS | PA7 | AF11 | RMII/MII相关 |
| ENET_TXD3 / TX_EN相关 | PB8 | AF11 | 以太网数据 |
| ENET_RXD0 / RX_ER相关 | PB10 | AF11 | 以太网数据 |
| ENET_MDC / TXD0 / TXD1 / RXD0 / RXD1 | PC1 / PC2 / PC3 / PC4 / PC5 | AF11 | 以太网数据/管理 |
| ENET_TXD0/TXD1/TXD2/TXD3/COL/CRS/RXD2/RXD3/RX_ER/RX_DV | PH2 / PH3 / PH6 / PH7 / PH8 / PH9 / PH10 / PH11 / PH12 / PH13 / PH14 / PH15 | AF11/AF6 | 具体模式见源码 |
| ENET_TX_EN / RX_DV / RXD2 / RXD3 | PG11 / PG13 / PG14 / PG6 / PG9 / PG12 | AF11/AF6 | 具体模式见源码 |
## 12. IO电平约定(软件侧)
- `sys_drv_init.h` 中 DI 逻辑定义为:低电平导通、高电平断开(`DIx = DI_READ ? 0 : 1`)。
- `DOx` 宏当前约定:
- `DOx(0)`:断开
- `DOx(1)`:闭合
- `DO*_READ()` / `ELOCK_*_READ()` 返回GPIO引脚电平:`1=高电平``0=低电平`
## 13. 维护建议
- 变更任一引脚时,同步更新:
- `BSP/GPIO/gpio.h`
- `BSP/GPIO/gpio.c``v_outpin_config` / `v_inpin_config`
- `BSP/sys_drv_init.h`(宏映射)
- 外设驱动文件(如 `BSP/com/usart.c``BSP/can/can.c``BSP/eeprom/i2c1.c``BSP/adc/*.c``BSP/externalflash/*.c`
- 本文档
- 新增点位时建议增加“信号名/端口引脚/用途”三元信息,避免后续只看宏名难以定位硬件。
+147
View File
@@ -0,0 +1,147 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "ringfifo.h"
void fifo_register(_fifo_t *pfifo, uint8_t *pfifo_buf, uint32_t size,
lock_fun lock, lock_fun unlock)
{
pfifo->buf_size = size;
pfifo->buf = pfifo_buf;
pfifo->pwrite = pfifo->buf;
pfifo->pread = pfifo->buf;
pfifo->occupy_size = 0;
pfifo->lock = lock;
pfifo->unlock = unlock;
}
void fifo_release(_fifo_t *pfifo)
{
pfifo->buf_size = 0;
pfifo->occupy_size = 0;
pfifo->buf = NULL;
pfifo->pwrite = 0;
pfifo->pread = 0;
pfifo->lock = NULL;
pfifo->unlock = NULL;
}
uint32_t fifo_write(_fifo_t *pfifo, const uint8_t *pbuf, uint32_t size)
{
uint32_t w_size = 0;
uint32_t free_size = 0;
if ((size == 0U) || (pfifo == NULL) || (pbuf == NULL))
{
return 0;
}
free_size = fifo_get_free_size(pfifo);
if (free_size == 0U)
{
return 0;
}
if (free_size < size)
{
size = free_size;
}
w_size = size;
if (pfifo->lock != NULL)
{
pfifo->lock();
}
while (w_size-- > 0U)
{
*pfifo->pwrite++ = *pbuf++;
if (pfifo->pwrite >= (pfifo->buf + pfifo->buf_size))
{
pfifo->pwrite = pfifo->buf;
}
pfifo->occupy_size++;
}
if (pfifo->unlock != NULL)
{
pfifo->unlock();
}
return size;
}
uint32_t fifo_read(_fifo_t *pfifo, uint8_t *pbuf, uint32_t size)
{
uint32_t r_size = 0;
uint32_t occupy_size = 0;
if ((size == 0U) || (pfifo == NULL) || (pbuf == NULL))
{
return 0;
}
occupy_size = fifo_get_occupy_size(pfifo);
if (occupy_size == 0U)
{
return 0;
}
if (occupy_size < size)
{
size = occupy_size;
}
if (pfifo->lock != NULL)
{
pfifo->lock();
}
r_size = size;
while (r_size-- > 0U)
{
*pbuf++ = *pfifo->pread++;
if (pfifo->pread >= (pfifo->buf + pfifo->buf_size))
{
pfifo->pread = pfifo->buf;
}
pfifo->occupy_size--;
}
if (pfifo->unlock != NULL)
{
pfifo->unlock();
}
return size;
}
uint32_t fifo_get_total_size(_fifo_t *pfifo)
{
if (pfifo == NULL)
{
return 0;
}
return pfifo->buf_size;
}
uint32_t fifo_get_free_size(_fifo_t *pfifo)
{
uint32_t size;
if (pfifo == NULL)
{
return 0;
}
size = pfifo->buf_size - fifo_get_occupy_size(pfifo);
return size;
}
uint32_t fifo_get_occupy_size(_fifo_t *pfifo)
{
if (pfifo == NULL)
{
return 0;
}
return pfifo->occupy_size;
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef RING_FIFO_H
#define RING_FIFO_H
#include <stdbool.h>
#include <stdint.h>
typedef void (*lock_fun)(void);
typedef struct
{
uint8_t *buf;
uint32_t buf_size;
uint32_t occupy_size;
uint8_t *pwrite;
uint8_t *pread;
void (*lock)(void);
void (*unlock)(void);
}_fifo_t;
void fifo_register(_fifo_t *pfifo, uint8_t *pfifo_buf, uint32_t size,
lock_fun lock, lock_fun unlock);
void fifo_release(_fifo_t *pfifo);
uint32_t fifo_write(_fifo_t *pfifo, const uint8_t *pbuf, uint32_t size);
uint32_t fifo_read(_fifo_t *pfifo, uint8_t *pbuf, uint32_t size);
uint32_t fifo_get_total_size(_fifo_t *pfifo);
uint32_t fifo_get_free_size(_fifo_t *pfifo);
uint32_t fifo_get_occupy_size(_fifo_t *pfifo);
#endif
+22
View File
@@ -0,0 +1,22 @@
#ifndef _RTC_H_
#define _RTC_H_
#include "gd32h7xx.h"
#include "publicdata/type.h"
/* 对外接口函数声明 */
void v_rtc_init(void);
/* app_rtc 兼容接口(对业务层使用 Comm_Time) */
void GetCurrentTime(Comm_Time *curTime);
void v_rtc_set_time(Comm_Time *curTime);
unsigned int xDate2Seconds(Comm_Time *time);
void xSeconds2Date(unsigned long seconds, Comm_Time *time);
U8_T u8_ocpp_timestamp_to_tm(Comm_Time *ctTime, const char *time_str, U8_T time_type);
void v_adjust_time_with_timezone(Comm_Time *ctTime, int hour_offset, int min_offset);
void test_rtc(void);
#endif
+93
View File
@@ -0,0 +1,93 @@
# RTC 模块说明
## 1. 模块位置
- `BSP/rtc/rtc.c`
- `BSP/rtc/rtc.h`
## 2. 模块职责
- 完成 MCU 内部 RTC 外设初始化(默认 `LXTAL 32.768kHz`
- 提供统一时间读写接口(业务层使用 `Comm_Time`
- 提供时间算法工具:
- 本地时间(UTC+8)与 Unix 时间戳互转
- OCPP 时间戳解析
- 按时区偏移调整时间(支持跨天/月/年)
## 3. 硬件与掉电行为说明
### 3.1 `RTC_BKP0` 是什么
- `RTC_BKP0` 是 RTC 备份寄存器中的一个槽位(软件可读写)
- 本项目中它作为“RTC 是否已初始化”的标记位使用
- 典型逻辑:
- `RTC_BKP0 == BKP_VALUE`:认为已配置过,不覆盖当前时间
- `RTC_BKP0 != BKP_VALUE`:按默认时间初始化并写入标记
### 3.2 掉电后时间会不会继续走
- `RTC_BKP0` 只是存储标记,不会自动计时
- 时间能否在掉电期间继续增加,取决于 RTC 域是否持续供电(VBAT)以及 RTC 时钟是否持续工作
- 若掉电导致备份域失电,RTC 计时会停止;下次上电会按“首次初始化”逻辑处理
## 4. 默认时间策略
- 当前默认时间由 `rtc.c` 宏定义控制:
- `RTC_DEFAULT_YEAR_BCD`
- `RTC_DEFAULT_MONTH_BCD`
- `RTC_DEFAULT_DAY_BCD`
- `RTC_DEFAULT_HOUR_BCD`
- `RTC_DEFAULT_MINUTE_BCD`
- `RTC_DEFAULT_SECOND_BCD`
- `RTC_DEFAULT_WEEKDAY`
- 默认值仅在“未初始化/备份标记无效”时生效,不会每次上电都覆盖已有时间
## 5. 对外接口
- `void v_rtc_init(void);`
- RTC 初始化入口(由 `BSP/sys_drv_init.c` 调用)
- `void GetCurrentTime(Comm_Time *curTime);`
- 读取当前时间到 `Comm_Time`
- `void v_rtc_set_time(Comm_Time *curTime);`
- 写 RTC 时间(内含时间合法性检查)
- `unsigned int xDate2Seconds(Comm_Time *time);`
- 本地时间(UTC+8)转 Unix 秒(UTC
- `void xSeconds2Date(unsigned long seconds, Comm_Time *time);`
- Unix 秒(UTC)转本地时间(UTC+8
- `U8_T u8_ocpp_timestamp_to_tm(Comm_Time *ctTime, const char *time_str, U8_T time_type);`
- 解析 OCPP 时间戳(支持 `Z``+08:00``-05:30` 等)
- `time_type=0`:输出 UTC
- `time_type=1`:输出北京时间(UTC+8
- `void v_adjust_time_with_timezone(Comm_Time *ctTime, int hour_offset, int min_offset);`
-`Comm_Time` 按时区偏移修正
- `void test_rtc(void);`
- RTC 自测接口(打印当前时间)
## 6. 启动与调用链
1. `main.c` -> `v_sys_hardware_init()`
2. `BSP/sys_drv_init.c` -> `v_rtc_init()`
3. 业务层通过 `GetCurrentTime/v_rtc_set_time` 访问时间
4. `app/app_init/app_test.c` 在周期测试中可触发 RTC 读写测试
## 7. 常见问题
- **Q: 修改默认时间后,为什么上电看到的不是默认值?**
A: 因为备份标记仍有效,模块判断为“已初始化”,不会覆盖当前时间。
- **Q: 想强制重新走默认时间怎么办?**
A: 清空备份标记(例如清 `RTC_BKP0`)或清备份域后重启。
## 8. 相关文档
- [返回主说明 README](../../README.md)
- [externalflash 模块说明](../externalflash/externalflash模块说明.md)
+330
View File
@@ -0,0 +1,330 @@
/*!
\file spi1_fixed.c
\brief SPI1 configuration file (Corrected Version)
\version 2026-03-09, V1.4.0, firmware for GD32H7xx
*/
/*
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 "spi1_fixed.h"
#include <stdio.h>
#include "gd32h7xx.h"
/* 私有变量 */
static uint8_t spi1_initialized = 0;
/*!
\brief configure SPI1 GPIO peripheral
\param[in] none
\param[out] none
\retval none
\note SPI1引脚配置:
- SPI1_NSS -> PA8 (软件控制)
- SPI1_SCK -> PA9 (AF5)
- SPI1_MISO -> PG2 (AF5)
- SPI1_MOSI -> PG3 (AF5)
*/
void spi1_gpio_config(void)
{
/* 使能GPIO时钟 */
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOG);
/* 使能SPI1时钟 */
rcu_periph_clock_enable(RCU_SPI1);
/* 配置SPI1时钟源 */
rcu_spi_clock_config(IDX_SPI1, RCU_SPISRC_PLL0Q);
/* 配置NSS引脚 (PA8) - 软件控制,GPIO输出模式 */
gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_8);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_8);
/* 默认拉高,不选中从设备 */
gpio_bit_set(GPIOA, GPIO_PIN_8);
/* 配置SPI1引脚复用功能 */
gpio_af_set(GPIOA, GPIO_AF_5, GPIO_PIN_9); // SCK
gpio_af_set(GPIOG, GPIO_AF_5, GPIO_PIN_2 | GPIO_PIN_3); // MISO, MOSI
/* 配置SCK引脚 (PA9) - 复用功能输出 */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_9);
/* 配置MISO引脚 (PG2) - 复用功能输入 */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
/* 配置MOSI引脚 (PG3) - 复用功能输出 */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_3);
}
/*!
\brief configure SPI1 peripheral
\param[in] none
\param[out] none
\retval none
\note SPI1基本配置,使用8位数据,MSB先行,主模式
*/
void spi1_config(void)
{
spi_parameter_struct spi_init_struct;
/* 去初始化SPI1 */
spi_i2s_deinit(SPI1);
/* 初始化SPI参数结构体 */
spi_struct_para_init(&spi_init_struct);
/* SPI1参数配置 */
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; // 全双工模式
spi_init_struct.device_mode = SPI_MASTER; // 主模式
spi_init_struct.data_size = SPI_DATASIZE_8BIT; // 8位数据帧 (修正: frame_size -> data_size)
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; // 时钟极性低,第一边沿采样
spi_init_struct.nss = SPI_NSS_SOFT; // 软件NSS控制
spi_init_struct.prescale = SPI_PSC_16; // 16分频
spi_init_struct.endian = SPI_ENDIAN_MSB; // MSB先行
/* 初始化SPI1 */
spi_init(SPI1, &spi_init_struct);
/* 使能字节访问 */
spi_byte_access_enable(SPI1);
/* 使能NSS输出 (虽然使用软件控制,但保持兼容性) */
spi_nss_output_enable(SPI1);
}
/*!
\brief SPI1完整初始化
\param[in] none
\param[out] none
\retval 0: 成功, -1: 失败
\note 完整的SPI1初始化流程
*/
int spi1_init(void)
{
if(spi1_initialized) {
return 0; // 已初始化
}
/* 配置GPIO */
spi1_gpio_config();
/* 配置SPI外设 */
spi1_config();
/* 使能SPI1 */
spi_enable(SPI1);
spi1_initialized = 1;
return 0;
}
/*!
\brief SPI1去初始化
\param[in] none
\param[out] none
\retval none
\note 禁用SPI1并复位相关配置
*/
void spi1_deinit(void)
{
/* 禁用SPI1 */
spi_disable(SPI1);
/* 去初始化SPI1 */
spi_i2s_deinit(SPI1);
/* NSS引脚拉高 */
gpio_bit_set(GPIOA, GPIO_PIN_8);
spi1_initialized = 0;
}
/*!
\brief SPI1发送数据
\param[in] data: 要发送的数据
\param[out] none
\retval none
\note 阻塞式发送
*/
void spi1_send_data(uint8_t data)
{
/* 等待发送缓冲区为空 */
while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_TP));
/* 发送数据 (修正API名称) */
spi_i2s_data_transmit(SPI1, data);
}
/*!
\brief SPI1接收数据
\param[in] none
\param[out] none
\retval 接收到的数据
\note 阻塞式接收
*/
uint8_t spi1_receive_data(void)
{
/* 等待接收缓冲区非空 */
while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_RP));
/* 接收数据 (修正API名称) */
return (uint8_t)spi_i2s_data_receive(SPI1);
}
/*!
\brief SPI1发送接收数据
\param[in] tx_data: 要发送的数据
\param[out] none
\retval 接收到的数据
\note 全双工通信,同时发送和接收
*/
uint8_t spi1_transmit_receive(uint8_t tx_data)
{
/* 等待发送缓冲区为空 */
while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_TP));
/* 发送数据 (修正API名称) */
spi_i2s_data_transmit(SPI1, tx_data);
/* 等待接收缓冲区非空 */
while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_RP));
/* 接收数据 (修正API名称) */
return (uint8_t)spi_i2s_data_receive(SPI1);
}
/*!
\brief SPI1发送数据块
\param[in] data: 数据缓冲区指针
\param[in] size: 数据大小
\param[out] none
\retval 0: 成功, -1: 参数错误
*/
int spi1_send_buffer(uint8_t *data, uint16_t size)
{
if(!data || size == 0) {
return -1;
}
for(uint16_t i = 0; i < size; i++) {
spi1_send_data(data[i]);
}
return 0;
}
/*!
\brief SPI1接收数据块
\param[in] data: 数据缓冲区指针
\param[in] size: 数据大小
\param[out] none
\retval 0: 成功, -1: 参数错误
*/
int spi1_receive_buffer(uint8_t *data, uint16_t size)
{
if(!data || size == 0) {
return -1;
}
for(uint16_t i = 0; i < size; i++) {
data[i] = spi1_receive_data();
}
return 0;
}
/*!
\brief SPI1片选控制
\param[in] enable: 1-选中从设备, 0-取消选中
\param[out] none
\retval none
\note 软件控制NSS信号
*/
void spi1_nss_control(uint8_t enable)
{
if(enable) {
/* 拉低NSS,选中从设备 */
gpio_bit_reset(GPIOA, GPIO_PIN_8);
} else {
/* 拉高NSS,取消选中从设备 */
gpio_bit_set(GPIOA, GPIO_PIN_8);
}
}
/*!
\brief 检查SPI1是否已初始化
\param[in] none
\param[out] none
\retval 1: 已初始化, 0: 未初始化
*/
uint8_t spi1_is_initialized(void)
{
return spi1_initialized;
}
/*!
\brief SPI1中断处理函数
\param[in] none
\param[out] none
\retval none
\note 需要在SPI1_IRQHandler中调用此函数
*/
void spi1_irq_handler(void)
{
/* 检查接收缓冲区非空中断标志 */
if(spi_i2s_interrupt_flag_get(SPI1, SPI_I2S_INT_FLAG_RP)) {
/* 读取接收数据,清除中断标志 */
uint8_t data = (uint8_t)spi_i2s_data_receive(SPI1);
/* 这里可以添加数据处理逻辑,例如存储到接收缓冲区 */
(void)data; /* 避免未使用变量警告 */
}
/* 检查发送缓冲区空中断标志 */
if(spi_i2s_interrupt_flag_get(SPI1, SPI_I2S_INT_FLAG_TP)) {
/* 这里可以添加发送数据处理逻辑 */
}
/* 检查错误中断标志 */
if(spi_i2s_interrupt_flag_get(SPI1, SPI_I2S_INT_FLAG_CRCERR)) {
/* CRC错误处理 */
spi_i2s_flag_clear(SPI1, SPI_FLAG_CRCERR);
}
if(spi_i2s_interrupt_flag_get(SPI1, SPI_I2S_INT_FLAG_RXORERR)) {
/* 溢出错误处理 */
spi_i2s_flag_clear(SPI1, SPI_FLAG_RXORERR);
}
if(spi_i2s_interrupt_flag_get(SPI1, SPI_I2S_INT_FLAG_TXURERR)) {
/* 欠载错误处理 */
spi_i2s_flag_clear(SPI1, SPI_FLAG_TXURERR);
}
}
+83
View File
@@ -0,0 +1,83 @@
/*!
\file spi1_fixed.h
\brief header file of SPI1 (Corrected Version)
\version 2026-03-09, V1.4.0, firmware for GD32H7xx
*/
/*
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 SPI1_FIXED_H
#define SPI1_FIXED_H
#include <stdint.h>
/* SPI1引脚定义 */
#define SPI1_NSS_PIN GPIO_PIN_8 // PA8
#define SPI1_SCK_PIN GPIO_PIN_9 // PA9
#define SPI1_MISO_PIN GPIO_PIN_2 // PG2
#define SPI1_MOSI_PIN GPIO_PIN_3 // PG3
#define SPI1_NSS_PORT GPIOA
#define SPI1_SCK_PORT GPIOA
#define SPI1_MISO_PORT GPIOG
#define SPI1_MOSI_PORT GPIOG
/* SPI1复用功能编号 */
#define SPI1_AF GPIO_AF_5
/* 函数声明 */
/* GPIO配置 */
void spi1_gpio_config(void);
/* SPI外设配置 */
void spi1_config(void);
/* 完整初始化和去初始化 */
int spi1_init(void);
void spi1_deinit(void);
/* 状态检查 */
uint8_t spi1_is_initialized(void);
/* 数据传输函数 */
void spi1_send_data(uint8_t data);
uint8_t spi1_receive_data(void);
uint8_t spi1_transmit_receive(uint8_t tx_data);
/* 缓冲区操作 */
int spi1_send_buffer(uint8_t *data, uint16_t size);
int spi1_receive_buffer(uint8_t *data, uint16_t size);
/* 片选控制 */
void spi1_nss_control(uint8_t enable);
/* 中断处理 */
void spi1_irq_handler(void);
#endif /* SPI1_FIXED_H */
+185
View File
@@ -0,0 +1,185 @@
#include "sys_drv_init.h"
#include "gpio.h"
#include "usart.h"
#include "app_rtc/app_rtc.h"
#include "gd32h7xx.h"
#include "can.h"
#include "adc_temp.h"
#include "nandflash_exmc_MT29F4G08.h"
#include "exflash_spi4_gd25qxx.h"
#include "flash_external_data.h"
#include "adc_spi3_insuVolt.h"
#include "adc_spi5_batVoltCurr.h"
#include "fm24cl16.h"
#include "tim/app_tim.h"
#include "main.h"
//#include <cachel1_armv7.h>
/**
* @brief 使能 Cortex-M7 指令/数据缓存
* @note 先失效 D-Cache,再开启 D-Cache,避免历史脏数据影响
*/
static void cache_enable(void)
{
SCB_EnableICache();
SCB_DisableDCache();
SCB_InvalidateDCache();
SCB_EnableDCache();
}
/**
* @brief 配置 NVIC 优先级分组
* @note FreeRTOS 建议使用 PRE4_SUB0(4 位抢占优先级 + 0 位子优先级)
*/
static void nvic_configuration(void)
{
/* 统一中断优先级配置入口(参考 CCU601E_D 的 NVIC_Priority_Config 思路) */
nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
/* ENET IRQ 内会调用 xSemaphoreGiveFromISR,优先级必须 >= configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY(5) */
#ifdef USE_ENET0
nvic_irq_enable(ENET0_IRQn, 6U, 0U);
#endif
#ifdef USE_ENET1
nvic_irq_enable(ENET1_IRQn, 6U, 0U);
#endif
/* USART/UART 中断优先级统一配置 */
nvic_irq_enable(USART0_IRQn, 3U, 1U);
nvic_irq_enable(USART2_IRQn, 3U, 2U);
nvic_irq_enable(UART3_IRQn, 3U, 0U);
nvic_irq_enable(UART4_IRQn, 2U, 0U);
nvic_irq_enable(USART5_IRQn, 1U, 0U);
nvic_irq_enable(UART6_IRQn, 0U, 0U);
nvic_irq_enable(UART7_IRQn, 3U, 7U);
/* ADC DMA 中断优先级统一配置 */
nvic_irq_enable(DMA1_Channel0_IRQn, 6U, 0U);
/* CAN 中断优先级统一配置 */
nvic_irq_enable(CAN0_Message_IRQn, 0U, 0U);
nvic_irq_enable(CAN1_Message_IRQn, 0U, 0U);
nvic_irq_enable(CAN2_Message_IRQn, 0U, 0U);
/* CAN error/busoff/warning IRQs (some CAN configs may route events here) */
nvic_irq_enable(CAN0_Busoff_IRQn, 0U, 0U);
nvic_irq_enable(CAN0_Error_IRQn, 0U, 0U);
nvic_irq_enable(CAN0_TEC_IRQn, 0U, 0U);
nvic_irq_enable(CAN0_REC_IRQn, 0U, 0U);
nvic_irq_enable(CAN1_Busoff_IRQn, 0U, 0U);
nvic_irq_enable(CAN1_Error_IRQn, 0U, 0U);
nvic_irq_enable(CAN1_TEC_IRQn, 0U, 0U);
nvic_irq_enable(CAN1_REC_IRQn, 0U, 0U);
nvic_irq_enable(CAN2_Busoff_IRQn, 0U, 0U);
nvic_irq_enable(CAN2_Error_IRQn, 0U, 0U);
nvic_irq_enable(CAN2_TEC_IRQn, 0U, 0U);
nvic_irq_enable(CAN2_REC_IRQn, 0U, 0U);
nvic_irq_enable(TIMER7_UP_IRQn, 5U, 0U);
}
/**
* @brief 配置 MPUMemory Protection Unit)区域属性
*
* 详细说明:
* 1) 该函数主要用于给“被 DMA 与 CPU 同时访问”的内存区域设置合适的属性,
* 避免 D-Cache 引入的数据一致性问题(CPU 看到旧数据、DMA 写入不可见等)。
*
* 2) 当前配置了两块 16KB 区域(均位于 SRAM3):
* - 区域00x30000000 ~ 0x30003FFF
* 典型用于 DMA 描述符、ETH Rx/Tx 缓冲等需要强一致性的内存。
* - 区域10x30004000 ~ 0x30007FFF
* 典型用于 lwIP heappbuf/memp 等运行期内存)。
*
* 3) 两个区域都设置为 Non-Cacheable(不可缓存):
* - 目的:让 CPU 每次都从实际内存读取,保证与 DMA 读写一致。
* - 代价:这部分区域访问速度会比 Cacheable 区域慢,但稳定性更高。
*
* 4) 该函数应在外设初始化前执行(当前在 v_sys_hardware_init() 早期调用),
* 这样后续网络/DMA模块在运行时就使用了正确的内存属性。
*
* 5) 结论:此函数是“网络与 DMA 稳定运行”的基础配置,尤其在 Cortex-M7
* 启用 D-Cache 场景下必不可少。
*/
void mpu_config(void)
{
mpu_region_init_struct mpu_init_struct;
mpu_region_struct_para_init(&mpu_init_struct);
/* disable MPU */
ARM_MPU_SetRegion(0U, 0U);
/* 区域0:DMA描述符/收发缓冲区(非缓存,避免 DMA 与 Cache 不一致) */
mpu_init_struct.region_base_address = 0x30000000;
mpu_init_struct.region_size = MPU_REGION_SIZE_16KB;
mpu_init_struct.access_permission = MPU_AP_FULL_ACCESS;
mpu_init_struct.access_bufferable = MPU_ACCESS_BUFFERABLE;
mpu_init_struct.access_cacheable = MPU_ACCESS_NON_CACHEABLE;
mpu_init_struct.access_shareable = MPU_ACCESS_NON_SHAREABLE;
mpu_init_struct.region_number = MPU_REGION_NUMBER0;
mpu_init_struct.subregion_disable = MPU_SUBREGION_ENABLE;
mpu_init_struct.instruction_exec = MPU_INSTRUCTION_EXEC_PERMIT;
mpu_init_struct.tex_type = MPU_TEX_TYPE0;
mpu_region_config(&mpu_init_struct);
mpu_region_enable();
/* 区域1lwIP heap(非缓存 + shareable,便于总线主设备协同访问) */
mpu_init_struct.region_base_address = 0x30004000;
mpu_init_struct.region_size = MPU_REGION_SIZE_16KB;
mpu_init_struct.access_permission = MPU_AP_FULL_ACCESS;
mpu_init_struct.access_bufferable = MPU_ACCESS_NON_BUFFERABLE;
mpu_init_struct.access_cacheable = MPU_ACCESS_NON_CACHEABLE;
mpu_init_struct.access_shareable = MPU_ACCESS_SHAREABLE;
mpu_init_struct.region_number = MPU_REGION_NUMBER1;
mpu_init_struct.subregion_disable = MPU_SUBREGION_ENABLE;
mpu_init_struct.instruction_exec = MPU_INSTRUCTION_EXEC_PERMIT;
mpu_init_struct.tex_type = MPU_TEX_TYPE1;
mpu_region_config(&mpu_init_struct);
mpu_region_enable();
/* enable MPU */
ARM_MPU_Enable(MPU_MODE_PRIV_DEFAULT);
}
/**
* @brief 系统硬件初始化总入口
* @note 按“基础系统 -> 通信/IO -> 业务外设”顺序初始化
*/
void v_sys_hardware_init(void)
{
/* 基础系统配置 */
nvic_configuration();
mpu_config();
cache_enable();
/* 基础外设 */
/* 先初始化关键DO输出,避免上电过程继电器抖动/误动作 */
v_outpin_config();
v_rtc_init();
v_inpin_config();
/* 通信外设 */
v_usart_init_all();
/* EEPROM(FM24CL16) I2C1 初始化 */
eeprom_driver_init();
/* SPI Flash 驱动初始化(统一封装接口) */
(void)v_flash_dataflash_init();
/* CAN init moved into hardware stage to avoid first-frame loss after power-on. */
v_can_interface_init();
/* 1ms hardware timer interrupt for periodic software counters. */
v_app_tim_init();
/* 以太网与 lwIP 须在 FreeRTOS 调度器启动后初始化(见 app_os_init),
* 否则 enet_system_setup() 失败会死在 while(1),运行灯也不会亮。 */
/* 采样与电源相关外设 */
adc_spi3_insuVolt_init();
adc_spi5_batVoltCurr_init();
v_temp_config();
// v_4g_config();
/* 可选外设(按需启用)
exmc_nandflash_init();
lwip_system_init();
*/
}
+88
View File
@@ -0,0 +1,88 @@
#ifndef _SYS_DRV_INIT_H_
#define _SYS_DRV_INIT_H_
#include "gd32h7xx.h"
#include "gpio.h"
#include "gd32h7xx_gpio.h"
//函数执行 调用时需要加;
#define RUN_LED_TOGGLE() gpio_bit_toggle(LED_RUN_PORT, LED_RUN_PIN) //运行灯翻转
#define RUN_LED_ON() RELAY_OFF(LED_RUN_PORT, LED_RUN_PIN) //运行灯亮
#define RUN_LED_OFF() RELAY_ON(LED_RUN_PORT, LED_RUN_PIN) //运行灯灭
//DI口 低电平是导通,高电平断开
#define DI1 (DI_READ(YX1_PORT,YX1_PIN)?0:1)
#define DI2 (DI_READ(YX2_PORT,YX2_PIN)?0:1)
#define DI3 (DI_READ(YX3_PORT,YX3_PIN)?0:1)
#define DI4 (DI_READ(YX4_PORT,YX4_PIN)?0:1)
#define DI5 (DI_READ(YX5_PORT,YX5_PIN)?0:1)
#define DI6 (DI_READ(YX6_PORT,YX6_PIN)?0:1)
#define DI7 (DI_READ(YX7_PORT,YX7_PIN)?0:1)
#define DI8 (DI_READ(YX8_PORT,YX8_PIN)?0:1)
#define DI9 (DI_READ(YX9_PORT,YX9_PIN)?0:1)
#define DI10 (DI_READ(YX10_PORT,YX10_PIN)?0:1)
#define DI11 (DI_READ(YX11_PORT,YX11_PIN)?0:1)
#define DI12 (DI_READ(YX12_PORT,YX12_PIN)?0:1)
#define AUX_A (DI_READ(AUX_PADET_PORT,AUX_PADET_PIN)?0:1)
#define AUX_B (DI_READ(AUX_PBDT_PORT,AUX_PBDT_PIN)?0:1)
//DO口 低电平是导通,高电平时断开-- 输出口控制,0断开,1闭合
/* K1K10 与 PK1/PK2 引脚 mode 及上电后相对复位默认的输出取反:BSP/GPIO/gpio.c -> v_outpin_config() */
#define DO1(x) (x!=0 ? RELAY_OFF(RELAY_K1_PORT, RELAY_K1_PIN) : RELAY_ON(RELAY_K1_PORT, RELAY_K1_PIN))
#define DO2(x) (x!=0 ? RELAY_OFF(RELAY_K2_PORT, RELAY_K2_PIN) : RELAY_ON(RELAY_K2_PORT, RELAY_K2_PIN))
#define DO3(x) (x!=0 ? RELAY_OFF(RELAY_K3_PORT, RELAY_K3_PIN) : RELAY_ON(RELAY_K3_PORT, RELAY_K3_PIN))
#define DO4(x) (x!=0 ? RELAY_OFF(RELAY_K4_PORT, RELAY_K4_PIN) : RELAY_ON(RELAY_K4_PORT, RELAY_K4_PIN))
#define DO5(x) (x!=0 ? RELAY_OFF(RELAY_K5_PORT, RELAY_K5_PIN) : RELAY_ON(RELAY_K5_PORT, RELAY_K5_PIN))
#define DO6(x) (x!=0 ? RELAY_OFF(RELAY_K6_PORT, RELAY_K6_PIN) : RELAY_ON(RELAY_K6_PORT, RELAY_K6_PIN))
#define DO7(x) (x!=0 ? RELAY_OFF(RELAY_K7_PORT, RELAY_K7_PIN) : RELAY_ON(RELAY_K7_PORT, RELAY_K7_PIN))
#define DO8(x) (x!=0 ? RELAY_OFF(RELAY_K8_PORT, RELAY_K8_PIN) : RELAY_ON(RELAY_K8_PORT, RELAY_K8_PIN))
#define DO9(x) (x!=0 ? RELAY_OFF(RELAY_K9_PORT, RELAY_K9_PIN) : RELAY_ON(RELAY_K9_PORT, RELAY_K9_PIN))
#define DO10(x) (x!=0 ? RELAY_OFF(RELAY_K10_PORT,RELAY_K10_PIN): RELAY_ON(RELAY_K10_PORT, RELAY_K10_PIN))
//电子锁控制(PK1/PK2
#define ELOCK_A(x) (x==0 ? RELAY_OFF(ELOCK_A_PORT, ELOCK_A_PIN) : RELAY_ON(ELOCK_A_PORT, ELOCK_A_PIN))
#define ELOCK_B(x) (x==0 ? RELAY_OFF(ELOCK_B_PORT, ELOCK_B_PIN) : RELAY_ON(ELOCK_B_PORT, ELOCK_B_PIN))
/* DO/电子锁 输出引脚电平回读:1=高电平,0=低电平(与 gpio_output_bit_get 一致) */
#define DO_PIN_LEVEL_READ(port, pin) ((gpio_output_bit_get((port), (pin)) == SET) ? 1U : 0U)
#define DO1_READ() DO_PIN_LEVEL_READ(RELAY_K1_PORT, RELAY_K1_PIN)
#define DO2_READ() DO_PIN_LEVEL_READ(RELAY_K2_PORT, RELAY_K2_PIN)
#define DO3_READ() DO_PIN_LEVEL_READ(RELAY_K3_PORT, RELAY_K3_PIN)
#define DO4_READ() DO_PIN_LEVEL_READ(RELAY_K4_PORT, RELAY_K4_PIN)
#define DO5_READ() DO_PIN_LEVEL_READ(RELAY_K5_PORT, RELAY_K5_PIN)
#define DO6_READ() DO_PIN_LEVEL_READ(RELAY_K6_PORT, RELAY_K6_PIN)
#define DO7_READ() DO_PIN_LEVEL_READ(RELAY_K7_PORT, RELAY_K7_PIN)
#define DO8_READ() DO_PIN_LEVEL_READ(RELAY_K8_PORT, RELAY_K8_PIN)
#define DO9_READ() DO_PIN_LEVEL_READ(RELAY_K9_PORT, RELAY_K9_PIN)
#define DO10_READ() DO_PIN_LEVEL_READ(RELAY_K10_PORT, RELAY_K10_PIN)
#define ELOCK_A_READ() DO_PIN_LEVEL_READ(ELOCK_A_PORT, ELOCK_A_PIN)
#define ELOCK_B_READ() DO_PIN_LEVEL_READ(ELOCK_B_PORT, ELOCK_B_PIN)
//绝缘检测继电器控制
#define GUN1_INS_CTL(x) (x==0 ? RELAY_OFF(INSA_GND_PORT, INSA_GND_PIN) : RELAY_ON(INSA_GND_PORT, INSA_GND_PIN))
#define GUN1_UBAL_CTL(x) (x==0 ? RELAY_OFF(INSA_POS_PORT, INSA_POS_PIN) : RELAY_ON(INSA_POS_PORT, INSA_POS_PIN))
#define GUN2_INS_CTL(x) (x==0 ? RELAY_OFF(INSB_GND_PORT, INSB_GND_PIN) : RELAY_ON(INSB_GND_PORT, INSB_GND_PIN))
#define GUN2_UBAL_CTL(x) (x==0 ? RELAY_OFF(INSB_POS_PORT, INSB_POS_PIN) : RELAY_ON(INSB_POS_PORT, INSB_POS_PIN))
#define GUN1_INS_KM_READ DO_PIN_LEVEL_READ(INSA_POS_PORT, INSA_POS_PIN)
#define GUN2_INS_KM_READ DO_PIN_LEVEL_READ(INSB_POS_PORT, INSB_POS_PIN)
//连接确认控制
#define GUN1_UBAL_READ_H (DI_READ(CHECKOUTA1_PORT, CHECKOUTA1_PIN))
#define GUN1_UBAL_READ_L (DI_READ(CHECKOUTA2_PORT, CHECKOUTA2_PIN))
#define GUN2_UBAL_READ_H (DI_READ(CHECKOUTB1_PORT, CHECKOUTB1_PIN))
#define GUN2_UBAL_READ_L (DI_READ(CHECKOUTB2_PORT, CHECKOUTB2_PIN))
// 硬件看门狗喂狗
#define WATCKDOG_TOGGLE() gpio_bit_toggle(WDO_PORT, WDO_PIN)
//4G模块控制
#define PWR_4G(x) (((x)==0) ? RELAY_OFF(PWR_4G_PORT, PWR_4G_PIN) : RELAY_ON(PWR_4G_PORT, PWR_4G_PIN))
#define RST_4G(x) (((x)==0) ? RELAY_OFF(RST_4G_PORT, RST_4G_PIN) : RELAY_ON(RST_4G_PORT, RST_4G_PIN))
/* FC41D 复位 PE6x=0 正常运行(低)x=1 拉复位(高) */
#define FC41D_RST(x) (((x)==0) ? RELAY_OFF(FC41D_RST_PORT, FC41D_RST_PIN) : RELAY_ON(FC41D_RST_PORT, FC41D_RST_PIN))
//温度采集及板卡供电检测
#define TEMP_MULT_CTRA(x) (x==0 ? RELAY_OFF(TEMP_ADDR_A_PORT,TEMP_ADDR_A_PIN) : RELAY_ON(TEMP_ADDR_A_PORT, TEMP_ADDR_A_PIN))
#define TEMP_MULT_CTRB(x) (x==0 ? RELAY_OFF(TEMP_ADDR_B_PORT,TEMP_ADDR_B_PIN) : RELAY_ON(TEMP_ADDR_B_PORT, TEMP_ADDR_B_PIN))
#define TEMP_MULT_CTRC(x) (x==0 ? RELAY_OFF(TEMP_ADDR_C_PORT,TEMP_ADDR_C_PIN) : RELAY_ON(TEMP_ADDR_C_PORT, TEMP_ADDR_C_PIN))
//===============================================
void v_sys_hardware_init(void);
#endif
+57
View File
@@ -0,0 +1,57 @@
#include "tim/app_tim.h"
#include "bms/bms_interface.h"
#include "gd32h7xx.h"
#include "sys_drv_init.h"
void v_app_tim_init(void)
{
timer_parameter_struct timer_initpara;
/* TIMER7: generate update interrupt periodically. */
rcu_periph_clock_enable(RCU_TIMER7);
timer_deinit(TIMER7);
timer_struct_para_init(&timer_initpara);
timer_initpara.prescaler = 23999U;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = 9U;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.repetitioncounter = 0U;
timer_init(TIMER7, &timer_initpara);
timer_interrupt_flag_clear(TIMER7, TIMER_INT_FLAG_UP);
timer_interrupt_enable(TIMER7, TIMER_INT_UP);
timer_enable(TIMER7);
}
void v_app_tim_irq_handler(void)
{
if (RESET != timer_interrupt_flag_get(TIMER7, TIMER_INT_FLAG_UP))
{
timer_interrupt_flag_clear(TIMER7, TIMER_INT_FLAG_UP);
v_app_tim_tick_1ms();
}
}
void v_app_tim_tick_1ms(void)
{
static U16_T cnt = 0U;
cnt++;
/* Run LED heartbeat from timer context (independent from IdleHook). */
if ((cnt % 500U) == 0U)
{
RUN_LED_TOGGLE();
WATCKDOG_TOGGLE();
}
if ((cnt % 10U) == 0U)
{
v_bms_add_timer(); /* 10ms */
}
v_delay_set_tymer_val(); /* bms timeout counter */
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef APP_TIM_H
#define APP_TIM_H
#ifdef __cplusplus
extern "C" {
#endif
void v_app_tim_init(void);
void v_app_tim_irq_handler(void);
void v_app_tim_tick_1ms(void);
#ifdef __cplusplus
}
#endif
#endif /* APP_TIM_H */
+111
View File
@@ -0,0 +1,111 @@
#include "tls/bs_tls_mbed.h"
#if (BS_TLS_MBEDTLS_EN)
/*
* 简单的 mbedtls TLS client 封装,供 plat_comm 使用。
* 证书校验策略、CA 证书加载等可根据项目需要在此扩展。
*/
/* 可根据项目实际把根证书放到只读 Flash,这里先留空,由用户自行填充 */
static const char *s_default_root_ca_pem = NULL;
int bs_tls_client_connect(BS_TLS_CTX *ctx, int fd, const char *host)
{
int ret;
const char *pers = "ccu-bs-tls";
if (ctx == NULL || host == NULL) {
return -1;
}
memset(ctx, 0, sizeof(BS_TLS_CTX));
mbedtls_net_init(&ctx->net_ctx);
mbedtls_ssl_init(&ctx->ssl);
mbedtls_ssl_config_init(&ctx->conf);
mbedtls_ctr_drbg_init(&ctx->ctr_drbg);
mbedtls_entropy_init(&ctx->entropy);
if ((ret = mbedtls_ctr_drbg_seed(&ctx->ctr_drbg,
mbedtls_entropy_func,
&ctx->entropy,
(const unsigned char *)pers,
strlen(pers))) != 0) {
return ret;
}
if ((ret = mbedtls_ssl_config_defaults(&ctx->conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
return ret;
}
/* 证书校验策略:先使用 VERIFY_OPTIONAL,后续可根据需要收紧为 VERIFY_REQUIRED */
mbedtls_ssl_conf_authmode(&ctx->conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
mbedtls_ssl_conf_rng(&ctx->conf, mbedtls_ctr_drbg_random, &ctx->ctr_drbg);
/* TODO: 如需严格校验证书,可在此加载根证书:
* mbedtls_x509_crt_init(...);
* mbedtls_x509_crt_parse(..., s_default_root_ca_pem, ...);
* mbedtls_ssl_conf_ca_chain(&ctx->conf, &cacert, NULL);
*/
if ((ret = mbedtls_ssl_setup(&ctx->ssl, &ctx->conf)) != 0) {
return ret;
}
if ((ret = mbedtls_ssl_set_hostname(&ctx->ssl, host)) != 0) {
return ret;
}
/* 将已有的 TCP socket fd 绑定到 mbedtls 的 net_sockets 封装上 */
ctx->net_ctx.fd = fd;
mbedtls_ssl_set_bio(&ctx->ssl, &ctx->net_ctx, mbedtls_net_send, mbedtls_net_recv, NULL);
while ((ret = mbedtls_ssl_handshake(&ctx->ssl)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
return ret;
}
}
ctx->inited = 1U;
return 0;
}
int bs_tls_client_send(BS_TLS_CTX *ctx, const unsigned char *buf, size_t len)
{
if (ctx == NULL || ctx->inited == 0U) {
return -1;
}
return mbedtls_ssl_write(&ctx->ssl, buf, len);
}
int bs_tls_client_recv(BS_TLS_CTX *ctx, unsigned char *buf, size_t len)
{
if (ctx == NULL || ctx->inited == 0U) {
return -1;
}
return mbedtls_ssl_read(&ctx->ssl, buf, len);
}
void bs_tls_client_close(BS_TLS_CTX *ctx)
{
if (ctx == NULL || ctx->inited == 0U) {
return;
}
(void)mbedtls_ssl_close_notify(&ctx->ssl);
mbedtls_ssl_free(&ctx->ssl);
mbedtls_ssl_config_free(&ctx->conf);
mbedtls_ctr_drbg_free(&ctx->ctr_drbg);
mbedtls_entropy_free(&ctx->entropy);
mbedtls_net_free(&ctx->net_ctx);
ctx->inited = 0U;
}
#endif /* BS_TLS_MBEDTLS_EN */
+43
View File
@@ -0,0 +1,43 @@
#ifndef BS_TLS_MBED_H
#define BS_TLS_MBED_H
#include "publicdata/public_define.h"
#if (BS_TLS_MBEDTLS_EN)
/* 仅在启用 TLS 时才编译 mbedtls 相关代码,避免未下载库时报错 */
#include "mbedtls/platform.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/ssl.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/entropy.h"
#include "mbedtls/error.h"
typedef struct
{
mbedtls_net_context net_ctx; /* 绑定的底层 TCP socket fd */
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_entropy_context entropy;
U8_T inited; /* 0=未初始化 1=已初始化 */
} BS_TLS_CTX;
/*
* 初始化 TLS 上下文(不包含握手),fd 由上层创建 TCP 后传入
* host 用于 SNI/证书校验
*/
int bs_tls_client_connect(BS_TLS_CTX *ctx, int fd, const char *host);
/* TLS 封装的发送/接收接口,返回值语义与 mbedtls_ssl_write/read 一致 */
int bs_tls_client_send(BS_TLS_CTX *ctx, const unsigned char *buf, size_t len);
int bs_tls_client_recv(BS_TLS_CTX *ctx, unsigned char *buf, size_t len);
/* 关闭 TLS 会话并释放资源,但不主动关闭底层 fd(交由上层统一处理) */
void bs_tls_client_close(BS_TLS_CTX *ctx);
#endif /* BS_TLS_MBEDTLS_EN */
#endif /* BS_TLS_MBED_H */
@@ -0,0 +1,406 @@
/*!
\file gd32h7xx.h
\brief general definitions for GD32H7xx
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
* Copyright (c) 2009-2021 Arm Limited. All rights reserved.
* Copyright (c) 2025, GigaDevice Semiconductor Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* This file refers the CMSIS standard, some adjustments are made according to GigaDevice chips */
#ifndef GD32H7XX_H
#define GD32H7XX_H
#ifdef __cplusplus
extern "C" {
#endif
#if !defined (GD32H7XX)
#error "Please select the target GD32H7XX device used in your application (in gd32h7xx.h file)"
#endif /* undefine GD32H7XX tip */
/* define value of high speed crystal oscillator (HXTAL) in Hz */
#if !defined (HXTAL_VALUE)
#define HXTAL_VALUE ((uint32_t)25000000)
#endif /* high speed crystal oscillator value */
/* define startup timeout value of high speed crystal oscillator (HXTAL) */
#if !defined (HXTAL_STARTUP_TIMEOUT)
#define HXTAL_STARTUP_TIMEOUT ((uint16_t)0x0FFFF)
#endif /* high speed crystal oscillator startup timeout */
/* define value of internal 64MHz RC oscillator (IRC64M) in Hz */
#if !defined (IRC64M_VALUE)
#define IRC64M_VALUE ((uint32_t)64000000)
#endif /* internal 64MHz RC oscillator value */
/* define startup timeout value of internal 64MHz RC oscillator (IRC64M) */
#if !defined (IRC64M_STARTUP_TIMEOUT)
#define IRC64M_STARTUP_TIMEOUT ((uint16_t)0x0500)
#endif /* internal 64MHz RC oscillator startup timeout */
/* define value of Low Power Internal 4Mhz RC oscillator (LPIRC4M) in Hz */
#if !defined (LPIRC4M_VALUE)
#define LPIRC4M_VALUE ((uint32_t)4000000)
#endif /* Low Power Internal 4Mhz RC oscillator value */
/* define startup timeout value of internal Low Power Internal 4Mhz RC oscillator (LPIRC4M) */
#if !defined (LPIRC4M_STARTUP_TIMEOUT)
#define LPIRC4M_STARTUP_TIMEOUT ((uint16_t)0x0500)
#endif /* Low Power Internal 4Mhz RC oscillator startup timeout */
/* define value of internal 48MHz RC oscillator (IRC48M) in Hz */
#if !defined (IRC48M_VALUE)
#define IRC48M_VALUE ((uint32_t)48000000)
#endif /* internal 48MHz RC oscillator value */
/* define startup timeout value of internal 48MHz RC oscillator (IRC48M) */
#if !defined (IRC48M_STARTUP_TIMEOUT)
#define IRC48M_STARTUP_TIMEOUT ((uint16_t)0x0500)
#endif /* internal 48MHz RC oscillator startup timeout */
/* define value of internal 32KHz RC oscillator(IRC32K) in Hz */
#if !defined (IRC32K_VALUE)
#define IRC32K_VALUE ((uint32_t)32000)
#endif /* internal 32KHz RC oscillator value */
/* define startup timeout value of internal 32KHz RC oscillator (IRC32K) */
#if !defined (IRC32K_STARTUP_TIMEOUT)
#define IRC32K_STARTUP_TIMEOUT ((uint16_t)0x0500)
#endif /* internal 32KHz RC oscillator startup timeout */
/* define value of low speed crystal oscillator (LXTAL)in Hz */
#if !defined (LXTAL_VALUE)
#define LXTAL_VALUE ((uint32_t)32768)
#endif /* low speed crystal oscillator value */
/* define startup timeout value of low speed crystal oscillator (LXTAL) */
#if !defined (LXTAL_STARTUP_TIMEOUT)
#define LXTAL_STARTUP_TIMEOUT ((uint32_t)0x0FFFFFFF)
#endif /* low speed crystal oscillator startup timeout */
/* GD32H7xx firmware library version number V1.4.0 */
#define __GD32H7XX_STDPERIPH_VERSION_MAIN (0x01) /*!< [31:24] main version */
#define __GD32H7XX_STDPERIPH_VERSION_SUB1 (0x04) /*!< [23:16] sub1 version */
#define __GD32H7XX_STDPERIPH_VERSION_SUB2 (0x00) /*!< [15:8] sub2 version */
#define __GD32H7XX_STDPERIPH_VERSION_RC (0x00) /*!< [7:0] release candidate */
#define __GD32H7XX_STDPERIPH_VERSION ((__GD32H7XX_STDPERIPH_VERSION_MAIN << 24)\
|(__GD32H7XX_STDPERIPH_VERSION_SUB1 << 16)\
|(__GD32H7XX_STDPERIPH_VERSION_SUB2 << 8)\
|(__GD32H7XX_STDPERIPH_VERSION_RC))
/* configuration of the Cortex-M7 processor and core peripherals */
#define __CM7_REV 0x0102U /*!< Cortex-M7 revision r1p2 */
#define __MPU_PRESENT 1 /*!< CM7 provides an MPU */
#define __NVIC_PRIO_BITS 4 /*!< CM7 uses 4 Bits for the Priority Levels */
#define __Vendor_SysTickConfig 0 /*!< Set to 1 if different SysTick Config is used */
#define __FPU_PRESENT 1 /*!< FPU present */
#define __ICACHE_PRESENT 1 /*!< CM7 instruction cache present */
#define __DCACHE_PRESENT 1 /*!< CM7 data cache present */
/* define interrupt number */
typedef enum IRQn {
/* Cortex-M7 processor exceptions numbers */
NonMaskableInt_IRQn = -14, /*!< non mask-able interrupt */
HardFault_IRQn = -13, /*!< hard-fault interrupt */
MemoryManagement_IRQn = -12, /*!< 4 Cortex-M7 memory management interrupt */
BusFault_IRQn = -11, /*!< 5 Cortex-M7 bus fault interrupt */
UsageFault_IRQn = -10, /*!< 6 Cortex-M7 usage fault interrupt */
SVCall_IRQn = -5, /*!< 11 Cortex-M7 sv call interrupt */
DebugMonitor_IRQn = -4, /*!< 12 Cortex-M7 debug monitor interrupt */
PendSV_IRQn = -2, /*!< 14 Cortex-M7 pend sv interrupt */
SysTick_IRQn = -1, /*!< 15 Cortex-M7 system tick interrupt */
/* interruput numbers */
WWDGT_IRQn = 0, /*!< window watchdog timer interrupt */
VAVD_LVD_VOVD_IRQn = 1, /*!< AVD_LVD_OVD_IRQn through EXTI line detect interrupt */
TAMPER_STAMP_LXTAL_IRQn = 2, /*!< RTC tamper and timestamp interrupt/LXTAL clock stuck interrupt */
RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt */
FMC_IRQn = 4, /*!< FMC interrupt */
RCU_IRQn = 5, /*!< RCU interrupt */
EXTI0_IRQn = 6, /*!< EXTI line 0 */
EXTI1_IRQn = 7, /*!< EXTI line 1 */
EXTI2_IRQn = 8, /*!< EXTI line 2 */
EXTI3_IRQn = 9, /*!< EXTI line 3 */
EXTI4_IRQn = 10, /*!< EXTI line 4 */
DMA0_Channel0_IRQn = 11, /*!< DMA0 channel 0 interrupt */
DMA0_Channel1_IRQn = 12, /*!< DMA0 channel 1 interrupt */
DMA0_Channel2_IRQn = 13, /*!< DMA0 channel 2 interrupt */
DMA0_Channel3_IRQn = 14, /*!< DMA0 channel 3 interrupt */
DMA0_Channel4_IRQn = 15, /*!< DMA0 channel 4 interrupt */
DMA0_Channel5_IRQn = 16, /*!< DMA0 channel 5 interrupt */
DMA0_Channel6_IRQn = 17, /*!< DMA0 channel 6 interrupt */
ADC0_1_IRQn = 18, /*!< ADC0 and ADC1 */
EXTI5_9_IRQn = 23, /*!< EXTI line 5 to 9 */
TIMER0_BRK_IRQn = 24, /*!< TIMER0 break interrupt */
TIMER0_UP_IRQn = 25, /*!< TIMER0 update interrupt */
TIMER0_TRG_CMT_IRQn = 26, /*!< TIMER0 trigger/commutation interrupt */
TIMER0_Channel_IRQn = 27, /*!< TIMER0 capture/compare interrupt */
TIMER1_IRQn = 28, /*!< TIMER1 interrupt */
TIMER2_IRQn = 29, /*!< TIMER2 interrupt */
TIMER3_IRQn = 30, /*!< TIMER3 interrupt */
I2C0_EV_IRQn = 31, /*!< I2C0 event interrupt */
I2C0_ER_IRQn = 32, /*!< I2C0 error interrupt */
I2C1_EV_IRQn = 33, /*!< I2C1 event interrupt */
I2C1_ER_IRQn = 34, /*!< I2C1 error interrupt */
SPI0_IRQn = 35, /*!< SPI0 interrupt */
SPI1_IRQn = 36, /*!< SPI1 interrupt */
USART0_IRQn = 37, /*!< USART0 global and wakeup interrupt */
USART1_IRQn = 38, /*!< USART1 global and wakeup interrupt */
USART2_IRQn = 39, /*!< USART2 global and wakeup interrupt */
EXTI10_15_IRQn = 40, /*!< EXTI line 10 to 15 */
RTC_Alarm_IRQn = 41, /*!< RTC Alarm interrupt */
TIMER7_BRK_IRQn = 43, /*!< TIMER7 Break global interrupt */
TIMER7_UP_IRQn = 44, /*!< TIMER7 Update global interrupt */
TIMER7_TRG_CMT_IRQn = 45, /*!< TIMER7 Trigger and Commutation global interrupt */
TIMER7_Channel_IRQn = 46, /*!< TIMER7 Capture Compare interrupt */
DMA0_Channel7_IRQn = 47, /*!< DMA0 channel 7 interrupt */
EXMC_IRQn = 48, /*!< EXMC interrupt */
SDIO0_IRQn = 49, /*!< SDMMC0 interrupt */
TIMER4_IRQn = 50, /*!< TIMER4 interrupt */
SPI2_IRQn = 51, /*!< SPI2 interrupt */
UART3_IRQn = 52, /*!< UART3 interrupt */
UART4_IRQn = 53, /*!< UART4 interrupt */
TIMER5_DAC_UDR_IRQn = 54, /*!< TIMER5 global interrupt and DAC1/DAC0 underrun */
TIMER6_IRQn = 55, /*!< TIMER6 interrupt */
DMA1_Channel0_IRQn = 56, /*!< DMA1 channel0 interrupt */
DMA1_Channel1_IRQn = 57, /*!< DMA1 channel1 interrupt */
DMA1_Channel2_IRQn = 58, /*!< DMA1 channel2 interrupt */
DMA1_Channel3_IRQn = 59, /*!< DMA1 channel3 interrupt */
DMA1_Channel4_IRQn = 60, /*!< DMA1 channel4 interrupt */
ENET0_IRQn = 61, /*!< ENET 0 interrupt */
ENET0_WKUP_IRQn = 62, /*!< ENET 0 wakeup through EXTI line interrupt */
DMA1_Channel5_IRQn = 68, /*!< DMA1 channel 5 interrupt */
DMA1_Channel6_IRQn = 69, /*!< DMA1 channel 6 interrupt */
DMA1_Channel7_IRQn = 70, /*!< DMA1 channel 7 interrupt */
USART5_IRQn = 71, /*!< UART5 global and wakeup interrupt */
I2C2_EV_IRQn = 72, /*!< I2C2 event interrupt */
I2C2_ER_IRQn = 73, /*!< I2C2 error interrupt */
USBHS0_EP1_OUT_IRQn = 74, /*!< USBHS0 endpoint 1 out interrupt */
USBHS0_EP1_IN_IRQn = 75, /*!< USBHS0 endpoint 1 in interrupt */
USBHS0_WKUP_IRQn = 76, /*!< USBHS0 wakeup through EXTI line interrupt */
USBHS0_IRQn = 77, /*!< USBHS0 interrupt */
DCI_IRQn = 78, /*!< DCI interrupt */
CAU_IRQn = 79, /*!< CAU interrupt */
HAU_TRNG_IRQn = 80, /*!< HAU and TRNG interrupt */
FPU_IRQn = 81, /*!< FPU interrupt */
UART6_IRQn = 82, /*!< UART6 interrupt */
UART7_IRQn = 83, /*!< UART7 interrupt */
SPI3_IRQn = 84, /*!< SPI3 interrupt */
SPI4_IRQn = 85, /*!< SPI4 interrupt */
SPI5_IRQn = 86, /*!< SPI5 interrupt */
SAI0_IRQn = 87, /*!< SAI0 interrupt */
TLI_IRQn = 88, /*!< TLI interrupt */
TLI_ER_IRQn = 89, /*!< TLI error interrupt */
IPA_IRQn = 90, /*!< IPA interrupt */
SAI1_IRQn = 91, /*!< SAI1 interrupt */
OSPI0_IRQn = 92, /*!< OSPI0 global interrupt */
I2C3_EV_IRQn = 95, /*!< I2C3 event interrupt */
I2C3_ER_IRQn = 96, /*!< I2C3 error interrupt */
RSPDIF_IRQn = 97, /*!< RSPDIF global interrupt */
DMAMUX_OVR_IRQn = 102, /*!< DMAMUX overrun interrupt */
HPDF_INT0_IRQn = 110, /*!< HPDF filiter 0 interrupt */
HPDF_INT1_IRQn = 111, /*!< HPDF filiter 1 interrupt */
HPDF_INT2_IRQn = 112, /*!< HPDF filiter 2 interrupt */
HPDF_INT3_IRQn = 113, /*!< HPDF filiter 3 interrupt */
SAI2_IRQn = 114, /*!< SAI2 interrupt */
TIMER14_IRQn = 116, /*!< TIMER14 interrupt */
TIMER15_IRQn = 117, /*!< TIMER15 interrupt */
TIMER16_IRQn = 118, /*!< TIMER16 interrupt */
MDIO_IRQn = 120, /*!< MDIO interrupt */
MDMA_IRQn = 122, /*!< MDMA interrupt */
SDIO1_IRQn = 124, /*!< SDIO1 interrupt */
HWSEM_IRQn = 125, /*!< HWSEM interrupt */
ADC2_IRQn = 127, /*!< ADC2 interrupt */
CMP0_1_IRQn = 137, /*!< CMP0 and CMP1 interrupt */
CTC_IRQn = 144, /*!< CTC interrupt */
RAMECCMU_IRQn = 145, /*!< RAMECCMU interrupt */
OSPI1_IRQn = 150, /*!< OSPI1 interrupt */
RTDEC0_IRQn = 151, /*!< RTDEC0 interrupt */
RTDEC1_IRQn = 152, /*!< RTDEC1 interrupt */
FAC_IRQn = 153, /*!< FAC interrupt */
TMU_IRQn = 154, /*!< TMU interrupt */
TIMER22_IRQn = 161, /*!< TIMER22 interrupt */
TIMER23_IRQn = 162, /*!< TIMER23 interrupt */
TIMER30_IRQn = 163, /*!< TIMER30 interrupt */
TIMER31_IRQn = 164, /*!< TIMER31 interrupt */
TIMER40_IRQn = 165, /*!< TIMER40 interrupt */
TIMER41_IRQn = 166, /*!< TIMER41 interrupt */
TIMER42_IRQn = 167, /*!< TIMER42 interrupt */
TIMER43_IRQn = 168, /*!< TIMER43 interrupt */
TIMER44_IRQn = 169, /*!< TIMER44 interrupt */
TIMER50_IRQn = 170, /*!< TIMER50 interrupt */
TIMER51_IRQn = 171, /*!< TIMER51 interrupt */
USBHS1_EP1_OUT_IRQn = 172, /*!< USBHS1 endpoint 1 out interrupt */
USBHS1_EP1_IN_IRQn = 173, /*!< USBHS1 endpoint 1 in interrupt */
USBHS1_WKUP_IRQn = 174, /*!< USBHS1 wakeup through EXTI line interrupt */
USBHS1_IRQn = 175, /*!< USBHS1 interrupt */
ENET1_IRQn = 176, /*!< ENET1 interrupt */
ENET1_WKUP_IRQn = 177, /*!< ENET1 wakeup through EXTI line interrupt */
CAN0_WKUP_IRQn = 179, /*!< CAN 0 wakeup through EXTI line interrupt */
CAN0_Message_IRQn = 180, /*!< CAN 0 message buffer interrupt */
CAN0_Busoff_IRQn = 181, /*!< CAN 0 bus off / bus off done interrupt */
CAN0_Error_IRQn = 182, /*!< CAN 0 error interrupt */
CAN0_FastError_IRQn = 183, /*!< CAN 0 error in fast transmission interrupt */
CAN0_TEC_IRQn = 184, /*!< CAN 0 transmit warning interrupt */
CAN0_REC_IRQn = 185, /*!< CAN 0 receive warning interrupt */
CAN1_WKUP_IRQn = 186, /*!< CAN 1 wakeup through EXTI line interrupt */
CAN1_Message_IRQn = 187, /*!< CAN 1 message buffer interrupt */
CAN1_Busoff_IRQn = 188, /*!< CAN 1 bus off / bus off done interrupt */
CAN1_Error_IRQn = 189, /*!< CAN 1 error interrupt */
CAN1_FastError_IRQn = 190, /*!< CAN 1 error in fast transmission interrupt */
CAN1_TEC_IRQn = 191, /*!< CAN 1 transmit warning interrupt */
CAN1_REC_IRQn = 192, /*!< CAN 1 receive warning interrupt */
CAN2_WKUP_IRQn = 193, /*!< CAN 2 wakeup through EXTI line interrupt */
CAN2_Message_IRQn = 194, /*!< CAN 2 message buffer interrupt */
CAN2_Busoff_IRQn = 195, /*!< CAN 2 bus off / bus off done interrupt */
CAN2_Error_IRQn = 196, /*!< CAN 2 error interrupt */
CAN2_FastError_IRQn = 197, /*!< CAN 2 error in fast transmission interrupt */
CAN2_TEC_IRQn = 198, /*!< CAN 2 transmit warning interrupt */
CAN2_REC_IRQn = 199, /*!< CAN 2 receive warning interrupt */
EFUSE_IRQn = 200, /*!< EFUSE interrupt */
I2C0_WKUP_IRQn = 201, /*!< I2C 0 wakeup through EXTI line interrupt */
I2C1_WKUP_IRQn = 202, /*!< I2C 1 wakeup through EXTI line interrupt */
I2C2_WKUP_IRQn = 203, /*!< I2C 2 wakeup through EXTI line interrupt */
I2C3_WKUP_IRQn = 204, /*!< I2C 3 wakeup through EXTI line interrupt */
LPDTS_IRQn = 205, /*!< LPDTS interrupt */
LPDTS_WKUP_IRQn = 206, /*!< LPDTS wakeup through EXTI line interrupt */
TIMER0_DEC_IRQn = 207, /*!< TIMER0 DEC interrupt */
TIMER7_DEC_IRQn = 208, /*!< TIMER7 DEC interrupt */
TIMER1_DEC_IRQn = 209, /*!< TIMER1 DEC interrupt */
TIMER2_DEC_IRQn = 210, /*!< TIMER2 DEC interrupt */
TIMER3_DEC_IRQn = 211, /*!< TIMER3 DEC interrupt */
TIMER4_DEC_IRQn = 212, /*!< TIMER4 DEC interrupt */
TIMER22_DEC_IRQn = 213, /*!< TIMER22 DEC interrupt */
TIMER23_DEC_IRQn = 214, /*!< TIMER23 DEC interrupt */
TIMER30_DEC_IRQn = 215, /*!< TIMER30 DEC interrupt */
TIMER31_DEC_IRQn = 216, /*!< TIMER31 DEC interrupt */
} IRQn_Type;
/* includes */
#include <stdint.h>
#include "system_gd32h7xx.h"
#include "core_cm7.h"
/* enum definitions */
typedef enum {DISABLE = 0, ENABLE = !DISABLE} EventStatus, ControlStatus;
typedef enum {RESET = 0, SET = !RESET} FlagStatus;
typedef enum {ERROR = 0, SUCCESS = !ERROR} ErrStatus;
/* bit operations */
#define REG64(addr) (*(volatile uint64_t *)(uint32_t)(addr))
#define REG32(addr) (*(volatile uint32_t *)(uint32_t)(addr))
#define REG16(addr) (*(volatile uint16_t *)(uint32_t)(addr))
#define REG8(addr) (*(volatile uint8_t *)(uint32_t)(addr))
#define BIT(x) ((uint32_t)((uint32_t)0x01U << (x)))
#define BITS(start, end) ((0xFFFFFFFFUL << (start)) & (0xFFFFFFFFUL >> (31U - (uint32_t)(end))))
#define GET_BITS(regval, start, end) (((regval) & BITS((start),(end))) >> (start))
/* main flash and SRAM memory map */
#define FLASH_BASE ((uint32_t)0x08000000U) /*!< main FLASH base address */
#define SRAM_BASE ((uint32_t)0x24000000U) /*!< SRAM base address */
/* SRAM and peripheral base bit-band region */
#define SRAM_BB_BASE ((uint32_t)0x22000000U) /*!< SRAM bit-band base address */
#define PERIPH_BB_BASE ((uint32_t)0x42000000U) /*!< peripheral bit-band base address */
/* peripheral memory map */
#define APB1_BUS_BASE ((uint32_t)0x40000000U) /*!< apb1 base address */
#define APB2_BUS_BASE ((uint32_t)0x40010000U) /*!< apb2 base address */
#define APB3_BUS_BASE ((uint32_t)0x50000000U) /*!< apb3 base address */
#define APB4_BUS_BASE ((uint32_t)0x58000000U) /*!< apb4 base address */
#define AHB1_BUS_BASE ((uint32_t)0x40020000U) /*!< ahb1 base address */
#define AHB2_BUS_BASE ((uint32_t)0x48000000U) /*!< ahb2 base address */
#define AHB3_BUS_BASE ((uint32_t)0x51000000U) /*!< ahb3 base address */
#define AHB4_BUS_BASE ((uint32_t)0x58020000U) /*!< ahb4 base address */
/* advanced peripheral bus 1 memory map */
#define TIMER_BASE (APB1_BUS_BASE + 0x00000000U) /*!< TIMER base address */
#define SPI_BASE (APB1_BUS_BASE + 0x00003800U) /*!< SPI base address */
#define RSPDIF_BASE (APB1_BUS_BASE + 0x00004000U) /*!< RSPDIF base address */
#define USART_BASE (APB1_BUS_BASE + 0x00004400U) /*!< USART base address */
#define I2C_BASE (APB1_BUS_BASE + 0x00005400U) /*!< I2C base address */
#define DAC_BASE (APB1_BUS_BASE + 0x00007400U) /*!< DAC base address */
#define CTC_BASE (APB1_BUS_BASE + 0x00008400U) /*!< CTC base address */
#define MDIO_BASE (APB1_BUS_BASE + 0x00009400U) /*!< MDIO base address */
/* advanced peripheral bus 2 memory map */
#define ADC_BASE (APB2_BUS_BASE + 0x00002400U) /*!< ADC base address */
#define SAI_BASE (APB2_BUS_BASE + 0x00005800U) /*!< SAI base address */
#define HPDF_BASE (APB2_BUS_BASE + 0x00007000U) /*!< HPDF base address */
#define TRIGSEL_BASE (APB2_BUS_BASE + 0x00008400U) /*!< TRIGSEL base address */
#define EDOUT_BASE (APB2_BUS_BASE + 0x00008800U) /*!< EDOUT base address */
#define CAN_BASE (APB2_BUS_BASE + 0x0000A000U) /*!< CAN base address */
/* advanced peripheral bus 3 memory map */
#define TLI_BASE (APB3_BUS_BASE + 0x00001000U) /*!< TLI base address */
#define WWDGT_BASE (APB3_BUS_BASE + 0x00003000U) /*!< WWDGT base address */
/* advanced peripheral bus 4 memory map */
#define EXTI_BASE (APB4_BUS_BASE + 0x00000000U) /*!< EXTI base address */
#define SYSCFG_BASE (APB4_BUS_BASE + 0x00000400U) /*!< SYSCFG base address */
#define CMP_BASE (APB4_BUS_BASE + 0x00003800U) /*!< CMP base address */
#define VREF_BASE (APB4_BUS_BASE + 0x00003C00U) /*!< VREF base address */
#define RTC_BASE (APB4_BUS_BASE + 0x00004000U) /*!< CMP base address */
#define FWDGT_BASE (APB4_BUS_BASE + 0x00004800U) /*!< FWDGT base address */
#define PMU_BASE (APB4_BUS_BASE + 0x00005800U) /*!< PMU base address */
#define LPDTS_BASE (APB4_BUS_BASE + 0x00006800U) /*!< LPDTS base address */
/* advanced high performance bus 1 memory map */
#define DMA_BASE (AHB1_BUS_BASE + 0x00000000U) /*!< DMA base address */
#define DMAMUX_BASE (AHB1_BUS_BASE + 0x00000800U) /*!< DMAMUX base address */
#define EFUSE_BASE (AHB1_BUS_BASE + 0x00002800U) /*!< EFUSE base address */
#define ENET_BASE (AHB1_BUS_BASE + 0x00008000U) /*!< ENET base address */
#define USBHS_BASE (AHB1_BUS_BASE + 0x00020000U) /*!< USBHS base address */
/* advanced high performance bus 2 memory map */
#define DCI_BASE (AHB2_BUS_BASE + 0x00020000U) /*!< DCI base address */
#define CAU_BASE (AHB2_BUS_BASE + 0x00021000U) /*!< CAU base address */
#define HAU_BASE (AHB2_BUS_BASE + 0x00021400U) /*!< HAU base address */
#define TRNG_BASE (AHB2_BUS_BASE + 0x00021800U) /*!< TRNG base address */
#define SDIO_BASE (AHB2_BUS_BASE + 0x00022400U) /*!< SDIO base address */
#define CPDM_BASE (AHB2_BUS_BASE + 0x00022800U) /*!< CPDM base address */
#define RAMECCMU_BASE (AHB2_BUS_BASE + 0x00023000U) /*!< RAMECCMU base address */
#define TMU_BASE (AHB2_BUS_BASE + 0x00024400U) /*!< TMU base address */
#define FAC_BASE (AHB2_BUS_BASE + 0x00024800U) /*!< FAC base address */
/* advanced high performance bus 3 memory map */
#define AXIM_BASE (AHB3_BUS_BASE + 0x00000000U) /*!< AXIM base address */
#define MDMA_BASE (AHB3_BUS_BASE + 0x01000000U) /*!< MDMA base address */
#define IPA_BASE (AHB3_BUS_BASE + 0x01001000U) /*!< IPA base address */
#define FMC_BASE (AHB3_BUS_BASE + 0x01002000U) /*!< FMC base address */
#define FLEXRAMC_BASE (AHB3_BUS_BASE + 0x01003000U) /*!< FLEXRAMC base address */
#define EXMC_BASE (AHB3_BUS_BASE + 0x01004000U) /*!< EXMC base address */
#define OSPI_BASE (AHB3_BUS_BASE + 0x01005000U) /*!< OSPI base address */
#define OSPM_BASE (AHB3_BUS_BASE + 0x0100B400U) /*!< OSPM base address */
#define RTDEC_BASE (AHB3_BUS_BASE + 0x0100B800U) /*!< RTDEC base address */
/* advanced high performance bus 4 memory map */
#define GPIO_BASE (AHB4_BUS_BASE + 0x00000000U) /*!< GPIO base address */
#define RCU_BASE (AHB4_BUS_BASE + 0x00004400U) /*!< RCU base address */
#define CRC_BASE (AHB4_BUS_BASE + 0x00004C00U) /*!< CRC base address */
#define HWSEM_BASE (AHB4_BUS_BASE + 0x00006400U) /*!< HWSEM base address */
/* option byte and debug memory map */
#define OB_BASE ((uint32_t)0x1FFFF800U) /*!< OB base address */
#define DBG_BASE ((uint32_t)0xE00E1000U) /*!< DBG base address */
/* define marco USE_STDPERIPH_DRIVER */
#if !defined USE_STDPERIPH_DRIVER
#define USE_STDPERIPH_DRIVER
#endif
#ifdef USE_STDPERIPH_DRIVER
#include "gd32h7xx_libopt.h"
#endif /* USE_STDPERIPH_DRIVER */
#ifdef __cplusplus
}
#endif
#endif /* GD32H7XX_H */
@@ -0,0 +1,56 @@
/*!
\file system_gd32h7xx.h
\brief CMSIS Cortex-M7 Device Peripheral Access Layer Header File for
gd32h7xx Device Series
*/
/*
* Copyright (c) 2009-2021 Arm Limited. All rights reserved.
* Copyright (c) 2025, GigaDevice Semiconductor Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* This file refers the CMSIS standard, some adjustments are made according to GigaDevice chips */
#ifndef SYSTEM_GD32H7XX_H
#define SYSTEM_GD32H7XX_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/* firmware version can be aquired by uncommenting the macro */
#define __FIRMWARE_VERSION_DEFINE
/* system clock frequency (core clock) */
extern uint32_t SystemCoreClock;
/* function declarations */
/* initialize the system and update the SystemCoreClock variable */
extern void SystemInit (void);
/* update the SystemCoreClock with current core clock retrieved from cpu registers */
extern void SystemCoreClockUpdate (void);
#ifdef __FIRMWARE_VERSION_DEFINE
/* get firmware version */
extern uint32_t gd32h7xx_firmware_version_get(void);
#endif /* __FIRMWARE_VERSION_DEFINE */
#ifdef __cplusplus
}
#endif
#endif /* SYSTEM_GD32H7XX_H */
@@ -0,0 +1,729 @@
;/*!
; \file startup_gd32h7xx.s
; \brief start up file
; \version 2025-01-24, V1.4.0, firmware for GD32H7xx
;*/
;/*
; * Copyright (c) 2009-2018 Arm Limited. All rights reserved.
; * Copyright (c) 2025, GigaDevice Semiconductor Inc.
; *
; * SPDX-License-Identifier: Apache-2.0
; *
; * Licensed under the Apache License, Version 2.0 (the License); you may
; * not use this file except in compliance with the License.
; * You may obtain a copy of the License at
; *
; * www.apache.org/licenses/LICENSE-2.0
; *
; * Unless required by applicable law or agreed to in writing, software
; * distributed under the License is distributed on an AS IS BASIS, WITHOUT
; * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; * See the License for the specific language governing permissions and
; * limitations under the License.
; */
;/* This file refers the CMSIS standard, some adjustments are made according to GigaDevice chips */
; <h> Stack Configuration
; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Stack_Size EQU 0x00001000
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size EQU 0x000000800
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
PRESERVE8
THUMB
; /* reset Vector Mapped to at Address 0 */
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; /* external interrupts handler */
DCD WWDGT_IRQHandler ; 16:Window Watchdog Timer
DCD VAVD_LVD_VOVD_IRQHandler ; 17:VAVD/LVD/VOVD through EXTI Line detect
DCD TAMPER_STAMP_LXTAL_IRQHandler ; 18:RTC Tamper and TimeStamp through EXTI Line detect, LXTAL clock security system interrupt
DCD RTC_WKUP_IRQHandler ; 19:RTC Wakeup from EXTI interrupt
DCD FMC_IRQHandler ; 20:FMC global interrupt
DCD RCU_IRQHandler ; 21:RCU global interrupt
DCD EXTI0_IRQHandler ; 22:EXTI Line 0
DCD EXTI1_IRQHandler ; 23:EXTI Line 1
DCD EXTI2_IRQHandler ; 24:EXTI Line 2
DCD EXTI3_IRQHandler ; 25:EXTI Line 3
DCD EXTI4_IRQHandler ; 26:EXTI Line 4
DCD DMA0_Channel0_IRQHandler ; 27:DMA0 Channel 0
DCD DMA0_Channel1_IRQHandler ; 28:DMA0 Channel 1
DCD DMA0_Channel2_IRQHandler ; 29:DMA0 Channel 2
DCD DMA0_Channel3_IRQHandler ; 30:DMA0 Channel 3
DCD DMA0_Channel4_IRQHandler ; 31:DMA0 Channel 4
DCD DMA0_Channel5_IRQHandler ; 32:DMA0 Channel 5
DCD DMA0_Channel6_IRQHandler ; 33:DMA0 Channel 6
DCD ADC0_1_IRQHandler ; 34:ADC0 and ADC1 interrupt
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD EXTI5_9_IRQHandler ; 39:EXTI5 to EXTI9
DCD TIMER0_BRK_IRQHandler ; 40:TIMER0 Break
DCD TIMER0_UP_IRQHandler ; 41:TIMER0 Update
DCD TIMER0_TRG_CMT_IRQHandler ; 42:TIMER0 Trigger and Commutation
DCD TIMER0_Channel_IRQHandler ; 43:TIMER0 Capture Compare
DCD TIMER1_IRQHandler ; 44:TIMER1
DCD TIMER2_IRQHandler ; 45:TIMER2
DCD TIMER3_IRQHandler ; 46:TIMER3
DCD I2C0_EV_IRQHandler ; 47:I2C0 Event
DCD I2C0_ER_IRQHandler ; 48:I2C0 Error
DCD I2C1_EV_IRQHandler ; 49:I2C1 Event
DCD I2C1_ER_IRQHandler ; 50:I2C1 Error
DCD SPI0_IRQHandler ; 51:SPI0
DCD SPI1_IRQHandler ; 52:SPI1
DCD USART0_IRQHandler ; 53:USART0 global and wakeup
DCD USART1_IRQHandler ; 54:USART1 global and wakeup
DCD USART2_IRQHandler ; 55:USART2 global and wakeup
DCD EXTI10_15_IRQHandler ; 56:EXTI10 to EXTI15
DCD RTC_Alarm_IRQHandler ; 57:RTC Alarm
DCD 0 ; Reserved
DCD TIMER7_BRK_IRQHandler ; 59:TIMER7 Break
DCD TIMER7_UP_IRQHandler ; 60:TIMER7 Update
DCD TIMER7_TRG_CMT_IRQHandler ; 61:TIMER7 Trigger and Commutation
DCD TIMER7_Channel_IRQHandler ; 62:TIMER7 Channel Capture Compare
DCD DMA0_Channel7_IRQHandler ; 63:DMA0 Channel 7
DCD EXMC_IRQHandler ; 64:EXMC
DCD SDIO0_IRQHandler ; 65:SDIO0
DCD TIMER4_IRQHandler ; 66:TIMER4
DCD SPI2_IRQHandler ; 67:SPI2
DCD UART3_IRQHandler ; 68:UART3
DCD UART4_IRQHandler ; 69:UART4
DCD TIMER5_DAC_UDR_IRQHandler ; 70:TIMER5 global interrupt and DAC1/DAC0 underrun error
DCD TIMER6_IRQHandler ; 71:TIMER6
DCD DMA1_Channel0_IRQHandler ; 72:DMA1 Channel0
DCD DMA1_Channel1_IRQHandler ; 73:DMA1 Channel1
DCD DMA1_Channel2_IRQHandler ; 74:DMA1 Channel2
DCD DMA1_Channel3_IRQHandler ; 75:DMA1 Channel3
DCD DMA1_Channel4_IRQHandler ; 76:DMA1 Channel4
DCD ENET0_IRQHandler ; 77:ENET0
DCD ENET0_WKUP_IRQHandler ; 78:ENET0 Wakeup through EXTI Line
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD DMA1_Channel5_IRQHandler ; 84:DMA1 Channel5
DCD DMA1_Channel6_IRQHandler ; 85:DMA1 Channel6
DCD DMA1_Channel7_IRQHandler ; 86:DMA1 Channel7
DCD USART5_IRQHandler ; 87:USART5 global and wakeup
DCD I2C2_EV_IRQHandler ; 88:I2C2 Event
DCD I2C2_ER_IRQHandler ; 89:I2C2 Error
DCD USBHS0_EP1_OUT_IRQHandler ; 90:USBHS0 Endpoint 1 Out
DCD USBHS0_EP1_IN_IRQHandler ; 91:USBHS0 Endpoint 1 in
DCD USBHS0_WKUP_IRQHandler ; 92:USBHS0 Wakeup through EXTI Line
DCD USBHS0_IRQHandler ; 93:USBHS0
DCD DCI_IRQHandler ; 94:DCI
DCD CAU_IRQHandler ; 95:CAU
DCD HAU_TRNG_IRQHandler ; 96:HAU and TRNG
DCD FPU_IRQHandler ; 97:FPU
DCD UART6_IRQHandler ; 98:UART6
DCD UART7_IRQHandler ; 99:UART7
DCD SPI3_IRQHandler ; 100:SPI3
DCD SPI4_IRQHandler ; 101:SPI4
DCD SPI5_IRQHandler ; 102:SPI5
DCD SAI0_IRQHandler ; 103:SAI0
DCD TLI_IRQHandler ; 104:TLI
DCD TLI_ER_IRQHandler ; 105:TLI Error
DCD IPA_IRQHandler ; 106:IPA
DCD SAI1_IRQHandler ; 107:SAI1
DCD OSPI0_IRQHandler ; 108:OSPI0
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD I2C3_EV_IRQHandler ; 111:I2C3 Event
DCD I2C3_ER_IRQHandler ; 112:I2C3 Error
DCD RSPDIF_IRQHandler ; 113:RSPDIF
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD DMAMUX_OVR_IRQHandler ; 118:DMAMUX Overrun interrupt
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD HPDF_INT0_IRQHandler ; 126:HPDF global interrupt 0
DCD HPDF_INT1_IRQHandler ; 127:HPDF global interrupt 1
DCD HPDF_INT2_IRQHandler ; 128:HPDF global interrupt 2
DCD HPDF_INT3_IRQHandler ; 129:HPDF global interrupt 3
DCD SAI2_IRQHandler ; 130:SAI2 global interrupt
DCD 0 ; Reserved
DCD TIMER14_IRQHandler ; 132:TIMER14
DCD TIMER15_IRQHandler ; 133:TIMER15
DCD TIMER16_IRQHandler ; 134:TIMER16
DCD 0 ; Reserved
DCD MDIO_IRQHandler ; 136:MDIO
DCD 0 ; Reserved
DCD MDMA_IRQHandler ; 138:MDMA
DCD 0 ; Reserved
DCD SDIO1_IRQHandler ; 140:SDIO1
DCD HWSEM_IRQHandler ; 141:HWSEM
DCD 0 ; Reserved
DCD ADC2_IRQHandler ; 143:ADC2
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD CMP0_1_IRQHandler ; 153:CMP0 and CMP1
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD CTC_IRQHandler ; 160:Clock Recovery System
DCD RAMECCMU_IRQHandler ; 161:RAMECCMU
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD OSPI1_IRQHandler ; 166:OSPI1
DCD RTDEC0_IRQHandler ; 167:RTDEC0
DCD RTDEC1_IRQHandler ; 168:RTDEC1
DCD FAC_IRQHandler ; 169:FAC
DCD TMU_IRQHandler ; 170:TMU
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD TIMER22_IRQHandler ; 177:TIMER22
DCD TIMER23_IRQHandler ; 178:TIMER23
DCD TIMER30_IRQHandler ; 179:TIMER30
DCD TIMER31_IRQHandler ; 180:TIMER31
DCD TIMER40_IRQHandler ; 181:TIMER40
DCD TIMER41_IRQHandler ; 182:TIMER41
DCD TIMER42_IRQHandler ; 183:TIMER42
DCD TIMER43_IRQHandler ; 184:TIMER43
DCD TIMER44_IRQHandler ; 185:TIMER44
DCD TIMER50_IRQHandler ; 186:TIMER50
DCD TIMER51_IRQHandler ; 187:TIMER51
DCD USBHS1_EP1_OUT_IRQHandler ; 188:USBHS1 endpoint 1 out
DCD USBHS1_EP1_IN_IRQHandler ; 189:USBHS1 endpoint 1 in
DCD USBHS1_WKUP_IRQHandler ; 190:USBHS1 wakeup
DCD USBHS1_IRQHandler ; 191:USBHS1
DCD ENET1_IRQHandler ; 192:ENET1
DCD ENET1_WKUP_IRQHandler ; 193:ENET1 wakeup
DCD 0 ; Reserved
DCD CAN0_WKUP_IRQHandler ; 195:CAN0 wakeup
DCD CAN0_Message_IRQHandler ; 196:CAN0 interrupt for message buffer
DCD CAN0_Busoff_IRQHandler ; 197:CAN0 interrupt for Bus off / Bus off done
DCD CAN0_Error_IRQHandler ; 198:CAN0 interrupt for Error
DCD CAN0_FastError_IRQHandler ; 199:CAN0 interrupt for Error in fast transmission
DCD CAN0_TEC_IRQHandler ; 200:CAN0 interrupt for Transmit warning
DCD CAN0_REC_IRQHandler ; 201:CAN0 interrupt for Receive warning
DCD CAN1_WKUP_IRQHandler ; 202:CAN1 wakeup
DCD CAN1_Message_IRQHandler ; 203:CAN1 interrupt for message buffer
DCD CAN1_Busoff_IRQHandler ; 204:CAN1 interrupt for Bus off / Bus off done
DCD CAN1_Error_IRQHandler ; 205:CAN1 interrupt for Error
DCD CAN1_FastError_IRQHandler ; 206:CAN1 interrupt for Error in fast transmission
DCD CAN1_TEC_IRQHandler ; 207:CAN1 interrupt for Transmit warning
DCD CAN1_REC_IRQHandler ; 208:CAN1 interrupt for Receive warning
DCD CAN2_WKUP_IRQHandler ; 209:CAN2 wakeup
DCD CAN2_Message_IRQHandler ; 210:CAN2 interrupt for message buffer
DCD CAN2_Busoff_IRQHandler ; 211:CAN2 interrupt for Bus off / Bus off done
DCD CAN2_Error_IRQHandler ; 212:CAN2 interrupt for Error
DCD CAN2_FastError_IRQHandler ; 213:CAN2 interrupt for Error in fast transmission
DCD CAN2_TEC_IRQHandler ; 214:CAN2 interrupt for Transmit warning
DCD CAN2_REC_IRQHandler ; 215:CAN2 interrupt for Receive warning
DCD EFUSE_IRQHandler ; 216:EFUSE
DCD I2C0_WKUP_IRQHandler ; 217:I2C0 wakeup
DCD I2C1_WKUP_IRQHandler ; 218:I2C1 wakeup
DCD I2C2_WKUP_IRQHandler ; 219:I2C2 wakeup
DCD I2C3_WKUP_IRQHandler ; 220:I2C3 wakeup
DCD LPDTS_IRQHandler ; 221:LPDTS
DCD LPDTS_WKUP_IRQHandler ; 222:LPDTS wakeup
DCD TIMER0_DEC_IRQHandler ; 223:TIMER0 DEC
DCD TIMER7_DEC_IRQHandler ; 224:TIMER7 DEC
DCD TIMER1_DEC_IRQHandler ; 225:TIMER1 DEC
DCD TIMER2_DEC_IRQHandler ; 226:TIMER2 DEC
DCD TIMER3_DEC_IRQHandler ; 227:TIMER3 DEC
DCD TIMER4_DEC_IRQHandler ; 228:TIMER4 DEC
DCD TIMER22_DEC_IRQHandler ; 229:TIMER22 DEC
DCD TIMER23_DEC_IRQHandler ; 230:TIMER23 DEC
DCD TIMER30_DEC_IRQHandler ; 231:TIMER30 DEC
DCD TIMER31_DEC_IRQHandler ; 232:TIMER31 DEC
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
AREA |.text|, CODE, READONLY
;/* reset Handler */
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
IMPORT |Image$$RW_IRAM1$$RW$$Base|
LDR R0, =|Image$$RW_IRAM1$$RW$$Base|
ADD R1, R0, #0x8000
LDR R2, =0x0
MEM_INIT STRD R2, R2, [ R0 ] , #8
CMP R0, R1
BNE MEM_INIT
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
;/* dummy Exception Handlers */
NMI_Handler\
PROC
EXPORT NMI_Handler [WEAK]
B .
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK]
B .
ENDP
BusFault_Handler\
PROC
EXPORT BusFault_Handler [WEAK]
B .
ENDP
UsageFault_Handler\
PROC
EXPORT UsageFault_Handler [WEAK]
B .
ENDP
SVC_Handler PROC
EXPORT SVC_Handler [WEAK]
B .
ENDP
DebugMon_Handler\
PROC
EXPORT DebugMon_Handler [WEAK]
B .
ENDP
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
Default_Handler PROC
; /* external interrupts handler */
EXPORT WWDGT_IRQHandler [WEAK]
EXPORT VAVD_LVD_VOVD_IRQHandler [WEAK]
EXPORT TAMPER_STAMP_LXTAL_IRQHandler [WEAK]
EXPORT RTC_WKUP_IRQHandler [WEAK]
EXPORT FMC_IRQHandler [WEAK]
EXPORT RCU_IRQHandler [WEAK]
EXPORT EXTI0_IRQHandler [WEAK]
EXPORT EXTI1_IRQHandler [WEAK]
EXPORT EXTI2_IRQHandler [WEAK]
EXPORT EXTI3_IRQHandler [WEAK]
EXPORT EXTI4_IRQHandler [WEAK]
EXPORT DMA0_Channel0_IRQHandler [WEAK]
EXPORT DMA0_Channel1_IRQHandler [WEAK]
EXPORT DMA0_Channel2_IRQHandler [WEAK]
EXPORT DMA0_Channel3_IRQHandler [WEAK]
EXPORT DMA0_Channel4_IRQHandler [WEAK]
EXPORT DMA0_Channel5_IRQHandler [WEAK]
EXPORT DMA0_Channel6_IRQHandler [WEAK]
EXPORT ADC0_1_IRQHandler [WEAK]
EXPORT EXTI5_9_IRQHandler [WEAK]
EXPORT TIMER0_BRK_IRQHandler [WEAK]
EXPORT TIMER0_UP_IRQHandler [WEAK]
EXPORT TIMER0_TRG_CMT_IRQHandler [WEAK]
EXPORT TIMER0_Channel_IRQHandler [WEAK]
EXPORT TIMER1_IRQHandler [WEAK]
EXPORT TIMER2_IRQHandler [WEAK]
EXPORT TIMER3_IRQHandler [WEAK]
EXPORT I2C0_EV_IRQHandler [WEAK]
EXPORT I2C0_ER_IRQHandler [WEAK]
EXPORT I2C1_EV_IRQHandler [WEAK]
EXPORT I2C1_ER_IRQHandler [WEAK]
EXPORT SPI0_IRQHandler [WEAK]
EXPORT SPI1_IRQHandler [WEAK]
EXPORT USART0_IRQHandler [WEAK]
EXPORT USART1_IRQHandler [WEAK]
EXPORT USART2_IRQHandler [WEAK]
EXPORT EXTI10_15_IRQHandler [WEAK]
EXPORT RTC_Alarm_IRQHandler [WEAK]
EXPORT TIMER7_BRK_IRQHandler [WEAK]
EXPORT TIMER7_UP_IRQHandler [WEAK]
EXPORT TIMER7_TRG_CMT_IRQHandler [WEAK]
EXPORT TIMER7_Channel_IRQHandler [WEAK]
EXPORT DMA0_Channel7_IRQHandler [WEAK]
EXPORT EXMC_IRQHandler [WEAK]
EXPORT SDIO0_IRQHandler [WEAK]
EXPORT TIMER4_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT UART3_IRQHandler [WEAK]
EXPORT UART4_IRQHandler [WEAK]
EXPORT TIMER5_DAC_UDR_IRQHandler [WEAK]
EXPORT TIMER6_IRQHandler [WEAK]
EXPORT DMA1_Channel0_IRQHandler [WEAK]
EXPORT DMA1_Channel1_IRQHandler [WEAK]
EXPORT DMA1_Channel2_IRQHandler [WEAK]
EXPORT DMA1_Channel3_IRQHandler [WEAK]
EXPORT DMA1_Channel4_IRQHandler [WEAK]
EXPORT ENET0_IRQHandler [WEAK]
EXPORT ENET0_WKUP_IRQHandler [WEAK]
EXPORT DMA1_Channel5_IRQHandler [WEAK]
EXPORT DMA1_Channel6_IRQHandler [WEAK]
EXPORT DMA1_Channel7_IRQHandler [WEAK]
EXPORT USART5_IRQHandler [WEAK]
EXPORT I2C2_EV_IRQHandler [WEAK]
EXPORT I2C2_ER_IRQHandler [WEAK]
EXPORT USBHS0_EP1_OUT_IRQHandler [WEAK]
EXPORT USBHS0_EP1_IN_IRQHandler [WEAK]
EXPORT USBHS0_WKUP_IRQHandler [WEAK]
EXPORT USBHS0_IRQHandler [WEAK]
EXPORT DCI_IRQHandler [WEAK]
EXPORT CAU_IRQHandler [WEAK]
EXPORT HAU_TRNG_IRQHandler [WEAK]
EXPORT FPU_IRQHandler [WEAK]
EXPORT UART6_IRQHandler [WEAK]
EXPORT UART7_IRQHandler [WEAK]
EXPORT SPI3_IRQHandler [WEAK]
EXPORT SPI4_IRQHandler [WEAK]
EXPORT SPI5_IRQHandler [WEAK]
EXPORT SAI0_IRQHandler [WEAK]
EXPORT TLI_IRQHandler [WEAK]
EXPORT TLI_ER_IRQHandler [WEAK]
EXPORT IPA_IRQHandler [WEAK]
EXPORT SAI1_IRQHandler [WEAK]
EXPORT OSPI0_IRQHandler [WEAK]
EXPORT I2C3_EV_IRQHandler [WEAK]
EXPORT I2C3_ER_IRQHandler [WEAK]
EXPORT RSPDIF_IRQHandler [WEAK]
EXPORT DMAMUX_OVR_IRQHandler [WEAK]
EXPORT HPDF_INT0_IRQHandler [WEAK]
EXPORT HPDF_INT1_IRQHandler [WEAK]
EXPORT HPDF_INT2_IRQHandler [WEAK]
EXPORT HPDF_INT3_IRQHandler [WEAK]
EXPORT SAI2_IRQHandler [WEAK]
EXPORT TIMER14_IRQHandler [WEAK]
EXPORT TIMER15_IRQHandler [WEAK]
EXPORT TIMER16_IRQHandler [WEAK]
EXPORT MDIO_IRQHandler [WEAK]
EXPORT MDMA_IRQHandler [WEAK]
EXPORT SDIO1_IRQHandler [WEAK]
EXPORT HWSEM_IRQHandler [WEAK]
EXPORT ADC2_IRQHandler [WEAK]
EXPORT CMP0_1_IRQHandler [WEAK]
EXPORT CTC_IRQHandler [WEAK]
EXPORT RAMECCMU_IRQHandler [WEAK]
EXPORT OSPI1_IRQHandler [WEAK]
EXPORT RTDEC0_IRQHandler [WEAK]
EXPORT RTDEC1_IRQHandler [WEAK]
EXPORT FAC_IRQHandler [WEAK]
EXPORT TMU_IRQHandler [WEAK]
EXPORT TIMER22_IRQHandler [WEAK]
EXPORT TIMER23_IRQHandler [WEAK]
EXPORT TIMER30_IRQHandler [WEAK]
EXPORT TIMER31_IRQHandler [WEAK]
EXPORT TIMER40_IRQHandler [WEAK]
EXPORT TIMER41_IRQHandler [WEAK]
EXPORT TIMER42_IRQHandler [WEAK]
EXPORT TIMER43_IRQHandler [WEAK]
EXPORT TIMER44_IRQHandler [WEAK]
EXPORT TIMER50_IRQHandler [WEAK]
EXPORT TIMER51_IRQHandler [WEAK]
EXPORT USBHS1_EP1_OUT_IRQHandler [WEAK]
EXPORT USBHS1_EP1_IN_IRQHandler [WEAK]
EXPORT USBHS1_WKUP_IRQHandler [WEAK]
EXPORT USBHS1_IRQHandler [WEAK]
EXPORT ENET1_IRQHandler [WEAK]
EXPORT ENET1_WKUP_IRQHandler [WEAK]
EXPORT CAN0_WKUP_IRQHandler [WEAK]
EXPORT CAN0_Message_IRQHandler [WEAK]
EXPORT CAN0_Busoff_IRQHandler [WEAK]
EXPORT CAN0_Error_IRQHandler [WEAK]
EXPORT CAN0_FastError_IRQHandler [WEAK]
EXPORT CAN0_TEC_IRQHandler [WEAK]
EXPORT CAN0_REC_IRQHandler [WEAK]
EXPORT CAN1_WKUP_IRQHandler [WEAK]
EXPORT CAN1_Message_IRQHandler [WEAK]
EXPORT CAN1_Busoff_IRQHandler [WEAK]
EXPORT CAN1_Error_IRQHandler [WEAK]
EXPORT CAN1_FastError_IRQHandler [WEAK]
EXPORT CAN1_TEC_IRQHandler [WEAK]
EXPORT CAN1_REC_IRQHandler [WEAK]
EXPORT CAN2_WKUP_IRQHandler [WEAK]
EXPORT CAN2_Message_IRQHandler [WEAK]
EXPORT CAN2_Busoff_IRQHandler [WEAK]
EXPORT CAN2_Error_IRQHandler [WEAK]
EXPORT CAN2_FastError_IRQHandler [WEAK]
EXPORT CAN2_TEC_IRQHandler [WEAK]
EXPORT CAN2_REC_IRQHandler [WEAK]
EXPORT EFUSE_IRQHandler [WEAK]
EXPORT I2C0_WKUP_IRQHandler [WEAK]
EXPORT I2C1_WKUP_IRQHandler [WEAK]
EXPORT I2C2_WKUP_IRQHandler [WEAK]
EXPORT I2C3_WKUP_IRQHandler [WEAK]
EXPORT LPDTS_IRQHandler [WEAK]
EXPORT LPDTS_WKUP_IRQHandler [WEAK]
EXPORT TIMER0_DEC_IRQHandler [WEAK]
EXPORT TIMER7_DEC_IRQHandler [WEAK]
EXPORT TIMER1_DEC_IRQHandler [WEAK]
EXPORT TIMER2_DEC_IRQHandler [WEAK]
EXPORT TIMER3_DEC_IRQHandler [WEAK]
EXPORT TIMER4_DEC_IRQHandler [WEAK]
EXPORT TIMER22_DEC_IRQHandler [WEAK]
EXPORT TIMER23_DEC_IRQHandler [WEAK]
EXPORT TIMER30_DEC_IRQHandler [WEAK]
EXPORT TIMER31_DEC_IRQHandler [WEAK]
;/* external interrupts handler */
WWDGT_IRQHandler
VAVD_LVD_VOVD_IRQHandler
TAMPER_STAMP_LXTAL_IRQHandler
RTC_WKUP_IRQHandler
FMC_IRQHandler
RCU_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA0_Channel0_IRQHandler
DMA0_Channel1_IRQHandler
DMA0_Channel2_IRQHandler
DMA0_Channel3_IRQHandler
DMA0_Channel4_IRQHandler
DMA0_Channel5_IRQHandler
DMA0_Channel6_IRQHandler
ADC0_1_IRQHandler
EXTI5_9_IRQHandler
TIMER0_BRK_IRQHandler
TIMER0_UP_IRQHandler
TIMER0_TRG_CMT_IRQHandler
TIMER0_Channel_IRQHandler
TIMER1_IRQHandler
TIMER2_IRQHandler
TIMER3_IRQHandler
I2C0_EV_IRQHandler
I2C0_ER_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
SPI0_IRQHandler
SPI1_IRQHandler
USART0_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
EXTI10_15_IRQHandler
RTC_Alarm_IRQHandler
TIMER7_BRK_IRQHandler
TIMER7_UP_IRQHandler
TIMER7_TRG_CMT_IRQHandler
TIMER7_Channel_IRQHandler
DMA0_Channel7_IRQHandler
EXMC_IRQHandler
SDIO0_IRQHandler
TIMER4_IRQHandler
SPI2_IRQHandler
UART3_IRQHandler
UART4_IRQHandler
TIMER5_DAC_UDR_IRQHandler
TIMER6_IRQHandler
DMA1_Channel0_IRQHandler
DMA1_Channel1_IRQHandler
DMA1_Channel2_IRQHandler
DMA1_Channel3_IRQHandler
DMA1_Channel4_IRQHandler
ENET0_IRQHandler
ENET0_WKUP_IRQHandler
DMA1_Channel5_IRQHandler
DMA1_Channel6_IRQHandler
DMA1_Channel7_IRQHandler
USART5_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
USBHS0_EP1_OUT_IRQHandler
USBHS0_EP1_IN_IRQHandler
USBHS0_WKUP_IRQHandler
USBHS0_IRQHandler
DCI_IRQHandler
CAU_IRQHandler
HAU_TRNG_IRQHandler
FPU_IRQHandler
UART6_IRQHandler
UART7_IRQHandler
SPI3_IRQHandler
SPI4_IRQHandler
SPI5_IRQHandler
SAI0_IRQHandler
TLI_IRQHandler
TLI_ER_IRQHandler
IPA_IRQHandler
SAI1_IRQHandler
OSPI0_IRQHandler
I2C3_EV_IRQHandler
I2C3_ER_IRQHandler
RSPDIF_IRQHandler
DMAMUX_OVR_IRQHandler
HPDF_INT0_IRQHandler
HPDF_INT1_IRQHandler
HPDF_INT2_IRQHandler
HPDF_INT3_IRQHandler
SAI2_IRQHandler
TIMER14_IRQHandler
TIMER15_IRQHandler
TIMER16_IRQHandler
MDIO_IRQHandler
MDMA_IRQHandler
SDIO1_IRQHandler
HWSEM_IRQHandler
ADC2_IRQHandler
CMP0_1_IRQHandler
CTC_IRQHandler
RAMECCMU_IRQHandler
OSPI1_IRQHandler
RTDEC0_IRQHandler
RTDEC1_IRQHandler
FAC_IRQHandler
TMU_IRQHandler
TIMER22_IRQHandler
TIMER23_IRQHandler
TIMER30_IRQHandler
TIMER31_IRQHandler
TIMER40_IRQHandler
TIMER41_IRQHandler
TIMER42_IRQHandler
TIMER43_IRQHandler
TIMER44_IRQHandler
TIMER50_IRQHandler
TIMER51_IRQHandler
USBHS1_EP1_OUT_IRQHandler
USBHS1_EP1_IN_IRQHandler
USBHS1_WKUP_IRQHandler
USBHS1_IRQHandler
ENET1_IRQHandler
ENET1_WKUP_IRQHandler
CAN0_WKUP_IRQHandler
CAN0_Message_IRQHandler
CAN0_Busoff_IRQHandler
CAN0_Error_IRQHandler
CAN0_FastError_IRQHandler
CAN0_TEC_IRQHandler
CAN0_REC_IRQHandler
CAN1_WKUP_IRQHandler
CAN1_Message_IRQHandler
CAN1_Busoff_IRQHandler
CAN1_Error_IRQHandler
CAN1_FastError_IRQHandler
CAN1_TEC_IRQHandler
CAN1_REC_IRQHandler
CAN2_WKUP_IRQHandler
CAN2_Message_IRQHandler
CAN2_Busoff_IRQHandler
CAN2_Error_IRQHandler
CAN2_FastError_IRQHandler
CAN2_TEC_IRQHandler
CAN2_REC_IRQHandler
EFUSE_IRQHandler
I2C0_WKUP_IRQHandler
I2C1_WKUP_IRQHandler
I2C2_WKUP_IRQHandler
I2C3_WKUP_IRQHandler
LPDTS_IRQHandler
LPDTS_WKUP_IRQHandler
TIMER0_DEC_IRQHandler
TIMER7_DEC_IRQHandler
TIMER1_DEC_IRQHandler
TIMER2_DEC_IRQHandler
TIMER3_DEC_IRQHandler
TIMER4_DEC_IRQHandler
TIMER22_DEC_IRQHandler
TIMER23_DEC_IRQHandler
TIMER30_DEC_IRQHandler
TIMER31_DEC_IRQHandler
B .
ENDP
ALIGN
; user Initial Stack & Heap
IF :DEF:__MICROLIB
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
IMPORT __use_two_region_memory
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR R0, = Heap_Mem
LDR R1, =(Stack_Mem + Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
ALIGN
ENDIF
END
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,891 @@
/*!
\file system_gd32h7xx.c
\brief CMSIS Cortex-M7 Device Peripheral Access Layer Source File for
gd32h7xx Device Series
*/
/*
* Copyright (c) 2009-2021 Arm Limited. All rights reserved.
* Copyright (c) 2025, GigaDevice Semiconductor Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* This file refers the CMSIS standard, some adjustments are made according to GigaDevice chips */
#include "gd32h7xx.h"
/* system frequency define */
#define __IRC64M (IRC64M_VALUE) /* internal 64 MHz RC oscillator frequency */
#define __HXTAL (HXTAL_VALUE) /* high speed crystal oscillator frequency */
#define __LPIRC4M (LPIRC4M_VALUE) /* low power internal 4 MHz RC oscillator frequency */
#define __SYS_OSC_CLK (__IRC64M) /* main oscillator frequency */
#define VECT_TAB_OFFSET (uint32_t)0x00 /* vector table base offset */
#define RCU_APB4EN_SYSCFG (uint32_t)0x01 /* enable SYSCFG clk */
/* select a system clock by uncommenting the following line */
/* use IRC64M */
//#define __SYSTEM_CLOCK_IRC64M (__IRC64M)
//#define __SYSTEM_CLOCK_480M_PLL0_IRC64M (uint32_t)(480000000)
//#define __SYSTEM_CLOCK_600M_PLL0_IRC64M (uint32_t)(600000000)
/* use LPIRC4M */
//#define __SYSTEM_CLOCK_LPIRC4M (__LPIRC4M)
/* use HXTAL(CK_HXTAL = 25M) */
//#define __SYSTEM_CLOCK_HXTAL (__HXTAL)
//#define __SYSTEM_CLOCK_200M_PLL0_HXTAL (uint32_t)(200000000)
//#define __SYSTEM_CLOCK_400M_PLL0_HXTAL (uint32_t)(400000000)
//#define __SYSTEM_CLOCK_480M_PLL0_HXTAL (uint32_t)(480000000)
#define __SYSTEM_CLOCK_600M_PLL0_HXTAL (uint32_t)(600000000)
/*
Note: the power mode need to match the mcu selection and external power supply circuit.
for iar project:
for 100-pin mcu, need to define macro GD32H7XXV.
for 144-pin mcu, need to define macro GD32H7XXZ.
for 176-pin mcu, need to define macro GD32H7XXI.
for keil project:
do not need to define these macros extra.
according to the selected mcu and external power supply circuit to uncomment
the following macro SEL_PMU_SMPS_MODE.
*/
#if defined(GD32H7XXI)
//#define SEL_PMU_SMPS_MODE PMU_LDO_SUPPLY
//#define SEL_PMU_SMPS_MODE PMU_DIRECT_SMPS_SUPPLY
#define SEL_PMU_SMPS_MODE PMU_BYPASS //电源模式应选为旁路模式,直接使用外部电源供电 lyn
#elif defined(GD32H7XXZ) | defined(GD32H7XXV)
//#define SEL_PMU_SMPS_MODE PMU_LDO_SUPPLY
//#define SEL_PMU_SMPS_MODE PMU_BYPASS
#endif
#define SEL_IRC64MDIV 0x00U
#define SEL_HXTAL 0x01U
#define SEL_LPIRC4M 0x02U
#define SEL_PLL0P 0x03U
#define PLL0PSC_REG_OFFSET 0U
#define PLL0N_REG_OFFSET 6U
#define PLL0P_REG_OFFSET 16U
#define PLL0Q_REG_OFFSET 0U
#define PLL0R_REG_OFFSET 24U
/* set the system clock frequency and declare the system clock configuration function */
#ifdef __SYSTEM_CLOCK_IRC64M
uint32_t SystemCoreClock = __SYSTEM_CLOCK_IRC64M;
static void system_clock_64m_irc64m(void);
#elif defined (__SYSTEM_CLOCK_480M_PLL0_IRC64M)
#define PLL0PSC 16U
#define PLL0N (120U - 1U)
#define PLL0P (1U - 1U)
#define PLL0Q (2U - 1U)
#define PLL0R (2U - 1U)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_480M_PLL0_IRC64M;
static void system_clock_480m_irc64m(void);
#elif defined (__SYSTEM_CLOCK_600M_PLL0_IRC64M)
#define PLL0PSC 16U
#define PLL0N (150U - 1U)
#define PLL0P (1U - 1U)
#define PLL0Q (2U - 1U)
#define PLL0R (2U - 1U)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_600M_PLL0_IRC64M;
static void system_clock_600m_irc64m(void);
#elif defined (__SYSTEM_CLOCK_LPIRC4M)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_LPIRC4M;
static void system_clock_4m_lpirc4m(void);
#elif defined (__SYSTEM_CLOCK_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_HXTAL;
static void system_clock_hxtal(void);
#elif defined (__SYSTEM_CLOCK_200M_PLL0_HXTAL)
#define PLL0PSC 5U
#define PLL0N (40U - 1U)
#define PLL0P (1U - 1U)
#define PLL0Q (2U - 1U)
#define PLL0R (2U - 1U)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_200M_PLL0_HXTAL;
static void system_clock_200m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_400M_PLL0_HXTAL)
#define PLL0PSC 5U
#define PLL0N (80U - 1U)
#define PLL0P (1U - 1U)
#define PLL0Q (2U - 1U)
#define PLL0R (2U - 1U)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_400M_PLL0_HXTAL;
static void system_clock_400m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_480M_PLL0_HXTAL)
#define PLL0PSC 5U
#define PLL0N (96U - 1U)
#define PLL0P (1U - 1U)
#define PLL0Q (2U - 1U)
#define PLL0R (2U - 1U)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_480M_PLL0_HXTAL;
static void system_clock_480m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_600M_PLL0_HXTAL)
#define PLL0PSC 5U
#define PLL0N (120U - 1U)
#define PLL0P (1U - 1U)
#define PLL0Q (2U - 1U)
#define PLL0R (2U - 1U)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_600M_PLL0_HXTAL;
static void system_clock_600m_hxtal(void);
#endif /* __SYSTEM_CLOCK_IRC64M */
/* configure the system clock */
static void system_clock_config(void);
/*!
\brief setup the microcontroller system, initialize the system
\param[in] none
\param[out] none
\retval none
*/
void SystemInit(void)
{
/* FPU settings */
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1U)
/* set CP10 and CP11 Full Access */
SCB->CPACR |= (uint32_t)((0x03U << 10U * 2U) | (0x03U << 11U * 2U));
#endif
SCB_EnableDCache();
SCB_DisableDCache();
/* enable IRC64M */
RCU_CTL |= RCU_CTL_IRC64MEN;
while(0U == (RCU_CTL & RCU_CTL_IRC64MSTB)) {
}
/* no TCM wait state */
RCU_APB4EN |= RCU_APB4EN_SYSCFG;
SYSCFG_SRAMCFG1 &= ~SYSCFG_SRAMCFG1_TCM_WAITSTATE;
RCU_CFG0 &= ~RCU_CFG0_SCS;
/* reset RCU */
/* reset HXTALEN, CKMEN, PLL0EN, PLL1EN, PLL2EN, PLLUSB0 and PLLUSB1 bits */
RCU_CTL &= ~(RCU_CTL_HXTALEN | RCU_CTL_CKMEN | RCU_CTL_PLL0EN | RCU_CTL_PLL1EN | RCU_CTL_PLL2EN | RCU_CTL_HXTALBPS);
RCU_ADDCTL1 &= ~(RCU_ADDCTL1_PLLUSBHS0EN | RCU_ADDCTL1_PLLUSBHS1EN | RCU_ADDCTL1_LPIRC4MEN);
/* reset CFG0, CFG1, CFG2, CFG3 registers */
RCU_CFG0 &= ~(RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | RCU_CFG0_APB3PSC | RCU_CFG0_APB4PSC | RCU_CFG0_AHBPSC |
RCU_CFG0_I2C0SEL | RCU_CFG0_SCS | RCU_CFG0_RTCDIV);
RCU_CFG1 &= ~(RCU_CFG1_HPDFSEL | RCU_CFG1_TIMERSEL | RCU_CFG1_PERSEL |
RCU_CFG1_RSPDIFSEL | RCU_CFG1_USART0SEL | RCU_CFG1_USART1SEL | RCU_CFG1_USART2SEL | RCU_CFG1_USART5SEL | RCU_CFG1_PLL2RDIV);
RCU_CFG2 &= ~(RCU_CFG2_SAI2B1SEL | RCU_CFG2_SAI2B0SEL | RCU_CFG2_SAI1SEL | RCU_CFG2_SAI0SEL |
RCU_CFG2_CKOUT0SEL | RCU_CFG2_CKOUT1SEL | RCU_CFG2_CKOUT0DIV | RCU_CFG2_CKOUT1DIV);
RCU_CFG3 &= ~(RCU_CFG3_ADC01SEL | RCU_CFG3_ADC2SEL | RCU_CFG3_SDIO1SEL
| RCU_CFG3_I2C3SEL | RCU_CFG3_I2C2SEL | RCU_CFG3_I2C1SEL);
RCU_CFG4 &= ~(RCU_CFG4_EXMCSEL | RCU_CFG4_SDIO0SEL);
RCU_CFG5 &= ~(RCU_CFG5_SPI0SEL | RCU_CFG5_SPI1SEL | RCU_CFG5_SPI2SEL |
RCU_CFG5_SPI3SEL | RCU_CFG5_SPI4SEL | RCU_CFG5_SPI5SEL);
/* disable all interrupts */
RCU_INT = 0x14FF0000U;
RCU_ADDINT = 0x00700000U;
/* reset all PLL0 parameter */
RCU_PLL0 = 0x01002020U;
RCU_PLL1 = 0x01012020U;
RCU_PLL2 = 0x01012020U;
RCU_PLLALL = 0x00000000U;
RCU_PLLADDCTL = 0x00010101U;
RCU_PLLUSBCFG = 0x00000000U;
RCU_PLL0FRA = 0x00000000U;
RCU_PLL1FRA = 0x00000000U;
RCU_PLL2FRA = 0x00000000U;
#if defined (SEL_PMU_SMPS_MODE)
/* power supply config */
pmu_smps_ldo_supply_config(SEL_PMU_SMPS_MODE);
#endif
/* configure system clock */
system_clock_config();
#ifdef VECT_TAB_SRAM
nvic_vector_table_set(NVIC_VECTTAB_RAM, VECT_TAB_OFFSET);
#else
nvic_vector_table_set(NVIC_VECTTAB_FLASH, VECT_TAB_OFFSET);
#endif
}
/*!
\brief configure the system clock
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_config(void)
{
#ifdef __SYSTEM_CLOCK_IRC64M
system_clock_64m_irc64m();
#elif defined (__SYSTEM_CLOCK_480M_PLL0_IRC64M)
system_clock_480m_irc64m();
#elif defined (__SYSTEM_CLOCK_600M_PLL0_IRC64M)
system_clock_600m_irc64m();
#elif defined (__SYSTEM_CLOCK_LPIRC4M)
system_clock_4m_lpirc4m();
#elif defined (__SYSTEM_CLOCK_HXTAL)
system_clock_hxtal();
#elif defined (__SYSTEM_CLOCK_200M_PLL0_HXTAL)
system_clock_200m_hxtal();
#elif defined (__SYSTEM_CLOCK_400M_PLL0_HXTAL)
system_clock_400m_hxtal();
#elif defined (__SYSTEM_CLOCK_480M_PLL0_HXTAL)
system_clock_480m_hxtal();
#elif defined (__SYSTEM_CLOCK_600M_PLL0_HXTAL)
system_clock_600m_hxtal();
#endif /* __SYSTEM_CLOCK_IRC64M */
}
#ifdef __SYSTEM_CLOCK_IRC64M
/*!
\brief configure the system clock to 64M by IRC64M
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_64m_irc64m(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
/* enable IRC64M */
RCU_CTL |= RCU_CTL_IRC64MEN;
/* wait until IRC64M is stable or the startup time is longer than IRC64M_STARTUP_TIMEOUT */
do {
timeout++;
stab_flag = (RCU_CTL & RCU_CTL_IRC64MSTB);
} while((0U == stab_flag) && (IRC64M_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL & RCU_CTL_IRC64MSTB)) {
while(1) {
}
}
/* AHB = SYSCLK / 1 */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
/* APB4 = AHB / 1 */
RCU_CFG0 |= RCU_APB4_CKAHB_DIV1;
/* APB3 = AHB / 1 */
RCU_CFG0 |= RCU_APB3_CKAHB_DIV1;
/* APB2 = AHB / 1 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB / 1 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV1;
/* configure IRC64M div */
RCU_ADDCTL1 &= ~(RCU_ADDCTL1_IRC64MDIV);
RCU_ADDCTL1 |= RCU_IRC64M_DIV1;
/* select IRC64M as system clock */
RCU_CFG0 &= ~RCU_CFG0_SCS;
RCU_CFG0 |= RCU_CKSYSSRC_IRC64MDIV;
/* wait until IRC64M is selected as system clock */
while(RCU_SCSS_IRC64MDIV != (RCU_CFG0 & RCU_CFG0_SCSS)) {
}
}
#elif defined (__SYSTEM_CLOCK_480M_PLL0_IRC64M)
/*!
\brief configure the system clock to 480M by PLL0 which selects IRC64M as its clock source
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_480m_irc64m(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
/* enable IRC64M */
RCU_CTL |= RCU_CTL_IRC64MEN;
/* wait until IRC64M is stable or the startup time is longer than IRC64M_STARTUP_TIMEOUT */
do {
timeout++;
stab_flag = (RCU_CTL & RCU_CTL_IRC64MSTB);
} while((0U == stab_flag) && (IRC64M_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL & RCU_CTL_IRC64MSTB)) {
while(1) {
}
}
/* insert TCM wait state at 480MHz */
RCU_APB4EN |= RCU_APB4EN_SYSCFG;
SYSCFG_SRAMCFG1 |= SYSCFG_SRAMCFG1_TCM_WAITSTATE;
/* IRC64M is already stable */
/* AHB = SYSCLK / 2 */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV2;
/* APB4 = AHB / 2 */
RCU_CFG0 |= RCU_APB4_CKAHB_DIV2;
/* APB3 = AHB / 2 */
RCU_CFG0 |= RCU_APB3_CKAHB_DIV2;
/* APB2 = AHB / 1 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB / 2 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
/* PLL0 select IRC64MDIV, config IRC64MDIV as IRC64M, PLL0 input and output range */
RCU_ADDCTL1 &= ~(RCU_ADDCTL1_IRC64MDIV);
RCU_ADDCTL1 |= RCU_IRC64M_DIV1;
RCU_PLLALL &= ~(RCU_PLLALL_PLLSEL | RCU_PLLALL_PLL0VCOSEL | RCU_PLLALL_PLL0RNG);
RCU_PLLALL |= (RCU_PLLSRC_IRC64MDIV | RCU_PLL0RNG_4M_8M);
/* PLL0P = IRC64MDIV / 16 * 120 / 1 = 480 MHz */
RCU_PLL0 &= ~(RCU_PLL0_PLL0N | RCU_PLL0_PLL0PSC | RCU_PLL0_PLL0P | RCU_PLL0_PLL0R | RCU_PLL0_PLLSTBSRC);
RCU_PLL0 |= ((PLL0N << PLL0N_REG_OFFSET) | (PLL0PSC << PLL0PSC_REG_OFFSET) | (PLL0P << PLL0P_REG_OFFSET) | (PLL0R << PLL0R_REG_OFFSET));
RCU_PLLADDCTL &= ~(RCU_PLLADDCTL_PLL0Q);
RCU_PLLADDCTL |= (PLL0Q << PLL0Q_REG_OFFSET);
/* enable PLL0P, PLL0Q, PLL0R */
RCU_PLLADDCTL |= RCU_PLLADDCTL_PLL0PEN | RCU_PLLADDCTL_PLL0QEN | RCU_PLLADDCTL_PLL0REN;
/* enable PLL0 */
RCU_CTL |= RCU_CTL_PLL0EN;
/* wait until PLL0 is stable */
while(0U == (RCU_CTL & RCU_CTL_PLL0STB)) {
}
/* select PLL0 as system clock */
RCU_CFG0 &= ~RCU_CFG0_SCS;
RCU_CFG0 |= RCU_CKSYSSRC_PLL0P;
/* wait until PLL0 is selected as system clock */
while(RCU_SCSS_PLL0P != (RCU_CFG0 & RCU_CFG0_SCSS)) {
}
}
#elif defined (__SYSTEM_CLOCK_600M_PLL0_IRC64M)
/*!
\brief configure the system clock to 600M by PLL0 which selects IRC64M as its clock source
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_600m_irc64m(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
/* enable IRC64M */
RCU_CTL |= RCU_CTL_IRC64MEN;
/* wait until IRC64M is stable or the startup time is longer than IRC64M_STARTUP_TIMEOUT */
do {
timeout++;
stab_flag = (RCU_CTL & RCU_CTL_IRC64MSTB);
} while((0U == stab_flag) && (IRC64M_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL & RCU_CTL_IRC64MSTB)) {
while(1) {
}
}
/* insert TCM wait state at 600MHz */
RCU_APB4EN |= RCU_APB4EN_SYSCFG;
SYSCFG_SRAMCFG1 |= SYSCFG_SRAMCFG1_TCM_WAITSTATE;
/* IRC64M is already stable */
/* AHB = SYSCLK / 2 */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV2;
/* APB4 = AHB / 2 */
RCU_CFG0 |= RCU_APB4_CKAHB_DIV2;
/* APB3 = AHB / 2 */
RCU_CFG0 |= RCU_APB3_CKAHB_DIV2;
/* APB2 = AHB / 1 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB / 2 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
/* PLL0 select IRC64MDIV, config IRC64MDIV as IRC64M, PLL0 input and output range */
RCU_ADDCTL1 &= ~(RCU_ADDCTL1_IRC64MDIV);
RCU_ADDCTL1 |= RCU_IRC64M_DIV1;
RCU_PLLALL &= ~(RCU_PLLALL_PLLSEL | RCU_PLLALL_PLL0VCOSEL | RCU_PLLALL_PLL0RNG);
RCU_PLLALL |= (RCU_PLLSRC_IRC64MDIV | RCU_PLL0RNG_4M_8M);
/* PLL0P = IRC64MDIV / 16 * 150 / 1 = 600 MHz */
RCU_PLL0 &= ~(RCU_PLL0_PLL0N | RCU_PLL0_PLL0PSC | RCU_PLL0_PLL0P | RCU_PLL0_PLL0R | RCU_PLL0_PLLSTBSRC);
RCU_PLL0 |= ((PLL0N << PLL0N_REG_OFFSET) | (PLL0PSC << PLL0PSC_REG_OFFSET) | (PLL0P << PLL0P_REG_OFFSET) | (PLL0R << PLL0R_REG_OFFSET));
RCU_PLLADDCTL &= ~(RCU_PLLADDCTL_PLL0Q);
RCU_PLLADDCTL |= (PLL0Q << PLL0Q_REG_OFFSET);
/* enable PLL0P, PLL0Q, PLL0R */
RCU_PLLADDCTL |= RCU_PLLADDCTL_PLL0PEN | RCU_PLLADDCTL_PLL0QEN | RCU_PLLADDCTL_PLL0REN;
/* enable PLL0 */
RCU_CTL |= RCU_CTL_PLL0EN;
/* wait until PLL0 is stable */
while(0U == (RCU_CTL & RCU_CTL_PLL0STB)) {
}
/* select PLL0 as system clock */
RCU_CFG0 &= ~RCU_CFG0_SCS;
RCU_CFG0 |= RCU_CKSYSSRC_PLL0P;
/* wait until PLL0 is selected as system clock */
while(RCU_SCSS_PLL0P != (RCU_CFG0 & RCU_CFG0_SCSS)) {
}
}
#elif defined (__SYSTEM_CLOCK_LPIRC4M)
/*!
\brief configure the system clock to LPIRC4M
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_4m_lpirc4m(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
/* enable LPIRC4M */
RCU_ADDCTL1 |= RCU_ADDCTL1_LPIRC4MEN;
/* wait until LPIRC4M is stable or the startup time is longer than LPIRC4M_STARTUP_TIMEOUT */
do {
timeout++;
stab_flag = (RCU_ADDCTL1 & RCU_ADDCTL1_LPIRC4MSTB);
} while((0U == stab_flag) && (LPIRC4M_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_ADDCTL1 & RCU_ADDCTL1_LPIRC4MSTB)) {
while(1) {
}
}
/* LPIRC4M is stable */
/* AHB = SYSCLK / 1*/
RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
/* APB4 = AHB / 1 */
RCU_CFG0 |= RCU_APB4_CKAHB_DIV1;
/* APB3 = AHB / 1 */
RCU_CFG0 |= RCU_APB3_CKAHB_DIV1;
/* APB2 = AHB / 1 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB / 1 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV1;
/* select LPIRC4M as system clock */
RCU_CFG0 &= ~RCU_CFG0_SCS;
RCU_CFG0 |= RCU_CKSYSSRC_LPIRC4M;
/* wait until LPIRC4M is selected as system clock */
while(RCU_SCSS_LPIRC4M != (RCU_CFG0 & RCU_CFG0_SCSS)) {
}
}
#elif defined (__SYSTEM_CLOCK_HXTAL)
/*!
\brief configure the system clock to HXTAL
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_hxtal(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
/* enable HXTAL */
RCU_CTL |= RCU_CTL_HXTALEN;
/* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
do {
timeout++;
stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
} while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)) {
while(1) {
}
}
/* HXTAL is stable */
/* AHB = SYSCLK / 1*/
RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
/* APB4 = AHB / 1 */
RCU_CFG0 |= RCU_APB4_CKAHB_DIV1;
/* APB3 = AHB / 1 */
RCU_CFG0 |= RCU_APB3_CKAHB_DIV1;
/* APB2 = AHB / 1 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB / 1 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV1;
/* select HXTAL as system clock */
RCU_CFG0 &= ~RCU_CFG0_SCS;
RCU_CFG0 |= RCU_CKSYSSRC_HXTAL;
/* wait until HXTAL is selected as system clock */
while(RCU_SCSS_HXTAL != (RCU_CFG0 & RCU_CFG0_SCSS)) {
}
}
#elif defined (__SYSTEM_CLOCK_200M_PLL0_HXTAL)
/*!
\brief configure the system clock to 200M by PLL0 which selects HXTAL as its clock source
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_200m_hxtal(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
/* enable HXTAL */
RCU_CTL |= RCU_CTL_HXTALEN;
/* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
do {
timeout++;
stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
} while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)) {
while(1) {
}
}
/* HXTAL is stable */
/* AHB = SYSCLK / 1 */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
/* APB4 = AHB / 2 */
RCU_CFG0 |= RCU_APB4_CKAHB_DIV2;
/* APB3 = AHB / 2 */
RCU_CFG0 |= RCU_APB3_CKAHB_DIV2;
/* APB2 = AHB / 1 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB / 2 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
/* PLL0 select HXTAL, configure PLL0 input and output range */
RCU_PLLALL &= ~(RCU_PLLALL_PLLSEL | RCU_PLLALL_PLL0VCOSEL | RCU_PLLALL_PLL0RNG);
RCU_PLLALL |= (RCU_PLLSRC_HXTAL | RCU_PLLALL_PLL0VCOSEL | RCU_PLL0RNG_4M_8M);
/* PLL0P = HXTAL / 5 * 40 = 200 MHz */
RCU_PLL0 &= ~(RCU_PLL0_PLL0N | RCU_PLL0_PLL0PSC | RCU_PLL0_PLL0P | RCU_PLL0_PLL0R | RCU_PLL0_PLLSTBSRC);
RCU_PLL0 |= ((PLL0N << PLL0N_REG_OFFSET) | (PLL0PSC << PLL0PSC_REG_OFFSET) | (PLL0P << PLL0P_REG_OFFSET) | (PLL0R << PLL0R_REG_OFFSET));
RCU_PLLADDCTL &= ~(RCU_PLLADDCTL_PLL0Q);
RCU_PLLADDCTL |= (PLL0Q << PLL0Q_REG_OFFSET);
/* enable PLL0P, PLL0Q, PLL0R */
RCU_PLLADDCTL |= RCU_PLLADDCTL_PLL0PEN | RCU_PLLADDCTL_PLL0QEN | RCU_PLLADDCTL_PLL0REN;
/* enable PLL0 */
RCU_CTL |= RCU_CTL_PLL0EN;
/* wait until PLL0 is stable */
while(0U == (RCU_CTL & RCU_CTL_PLL0STB)) {
}
/* select PLL0 as system clock */
RCU_CFG0 &= ~RCU_CFG0_SCS;
RCU_CFG0 |= RCU_CKSYSSRC_PLL0P;
/* wait until PLL0 is selected as system clock */
while(RCU_SCSS_PLL0P != (RCU_CFG0 & RCU_CFG0_SCSS)) {
}
}
#elif defined (__SYSTEM_CLOCK_400M_PLL0_HXTAL)
/*!
\brief configure the system clock to 400M by PLL0 which selects HXTAL as its clock source
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_400m_hxtal(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
/* enable HXTAL */
RCU_CTL |= RCU_CTL_HXTALEN;
/* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
do {
timeout++;
stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
} while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)) {
while(1) {
}
}
/* insert TCM wait state at 400MHz */
RCU_APB4EN |= RCU_APB4EN_SYSCFG;
SYSCFG_SRAMCFG1 |= SYSCFG_SRAMCFG1_TCM_WAITSTATE;
/* HXTAL is stable */
/* AHB = SYSCLK / 1 */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV2;
/* APB4 = AHB / 2 */
RCU_CFG0 |= RCU_APB4_CKAHB_DIV2;
/* APB3 = AHB / 2 */
RCU_CFG0 |= RCU_APB3_CKAHB_DIV2;
/* APB2 = AHB / 1 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB / 2 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
/* PLL0 select HXTAL, configure PLL0 input and output range */
RCU_PLLALL &= ~(RCU_PLLALL_PLLSEL | RCU_PLLALL_PLL0VCOSEL | RCU_PLLALL_PLL0RNG);
RCU_PLLALL |= (RCU_PLLSRC_HXTAL | RCU_PLLALL_PLL0VCOSEL | RCU_PLL0RNG_4M_8M);
/* PLL0P = HXTAL / 5 * 80 = 400 MHz */
RCU_PLL0 &= ~(RCU_PLL0_PLL0N | RCU_PLL0_PLL0PSC | RCU_PLL0_PLL0P | RCU_PLL0_PLL0R | RCU_PLL0_PLLSTBSRC);
RCU_PLL0 |= ((PLL0N << PLL0N_REG_OFFSET) | (PLL0PSC << PLL0PSC_REG_OFFSET) | (PLL0P << PLL0P_REG_OFFSET) | (PLL0R << PLL0R_REG_OFFSET));
RCU_PLLADDCTL &= ~(RCU_PLLADDCTL_PLL0Q);
RCU_PLLADDCTL |= (PLL0Q << PLL0Q_REG_OFFSET);
/* enable PLL0P, PLL0Q, PLL0R */
RCU_PLLADDCTL |= RCU_PLLADDCTL_PLL0PEN | RCU_PLLADDCTL_PLL0QEN | RCU_PLLADDCTL_PLL0REN;
/* enable PLL */
RCU_CTL |= RCU_CTL_PLL0EN;
/* wait until PLL0 is stable */
while(0U == (RCU_CTL & RCU_CTL_PLL0STB)) {
}
/* select PLL0 as system clock */
RCU_CFG0 &= ~RCU_CFG0_SCS;
RCU_CFG0 |= RCU_CKSYSSRC_PLL0P;
/* wait until PLL0 is selected as system clock */
while(RCU_SCSS_PLL0P != (RCU_CFG0 & RCU_CFG0_SCSS)) {
}
}
#elif defined (__SYSTEM_CLOCK_480M_PLL0_HXTAL)
/*!
\brief configure the system clock to 480M by PLL0 which selects HXTAL as its clock source
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_480m_hxtal(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
/* enable HXTAL */
RCU_CTL |= RCU_CTL_HXTALEN;
/* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
do {
timeout++;
stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
} while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)) {
while(1) {
}
}
/* insert TCM wait state at 480MHz */
RCU_APB4EN |= RCU_APB4EN_SYSCFG;
SYSCFG_SRAMCFG1 |= SYSCFG_SRAMCFG1_TCM_WAITSTATE;
/* HXTAL is stable */
/* AHB = SYSCLK / 2 */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV2;
/* APB4 = AHB / 2 */
RCU_CFG0 |= RCU_APB4_CKAHB_DIV2;
/* APB3 = AHB / 2 */
RCU_CFG0 |= RCU_APB3_CKAHB_DIV2;
/* APB2 = AHB / 1 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB / 2 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
/* PLL select HXTAL, configure PLL input and output range */
RCU_PLLALL &= ~(RCU_PLLALL_PLLSEL | RCU_PLLALL_PLL0VCOSEL | RCU_PLLALL_PLL0RNG);
RCU_PLLALL |= (RCU_PLLSRC_HXTAL | RCU_PLL0RNG_4M_8M);
/* PLL0P = HXTAL / 5 * 96 = 480 MHz */
RCU_PLL0 &= ~(RCU_PLL0_PLL0N | RCU_PLL0_PLL0PSC | RCU_PLL0_PLL0P | RCU_PLL0_PLL0R | RCU_PLL0_PLLSTBSRC);
RCU_PLL0 |= ((PLL0N << PLL0N_REG_OFFSET) | (PLL0PSC << PLL0PSC_REG_OFFSET) | (PLL0P << PLL0P_REG_OFFSET) | (PLL0R << PLL0R_REG_OFFSET));
RCU_PLLADDCTL &= ~(RCU_PLLADDCTL_PLL0Q);
RCU_PLLADDCTL |= (PLL0Q << PLL0Q_REG_OFFSET);
/* enable PLL0P, PLL0Q, PLL0R */
RCU_PLLADDCTL |= RCU_PLLADDCTL_PLL0PEN | RCU_PLLADDCTL_PLL0QEN | RCU_PLLADDCTL_PLL0REN;
/* enable PLL0 */
RCU_CTL |= RCU_CTL_PLL0EN;
/* wait until PLL0 is stable */
while(0U == (RCU_CTL & RCU_CTL_PLL0STB)) {
}
/* select PLL0 as system clock */
RCU_CFG0 &= ~RCU_CFG0_SCS;
RCU_CFG0 |= RCU_CKSYSSRC_PLL0P;
/* wait until PLL0 is selected as system clock */
while(RCU_SCSS_PLL0P != (RCU_CFG0 & RCU_CFG0_SCSS)) {
}
}
#elif defined (__SYSTEM_CLOCK_600M_PLL0_HXTAL)
/*!
\brief configure the system clock to 600M by PLL0 which selects HXTAL as its clock source
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_600m_hxtal(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
/* enable HXTAL */
RCU_CTL |= RCU_CTL_HXTALEN;
/* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
do {
timeout++;
stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
} while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)) {
while(1) {
}
}
/* insert TCM wait state at 600MHz */
RCU_APB4EN |= RCU_APB4EN_SYSCFG;
SYSCFG_SRAMCFG1 |= SYSCFG_SRAMCFG1_TCM_WAITSTATE;
/* HXTAL is stable */
/* AHB = SYSCLK / 2 */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV2;
/* APB4 = AHB / 2 */
RCU_CFG0 |= RCU_APB4_CKAHB_DIV2;
/* APB3 = AHB / 2 */
RCU_CFG0 |= RCU_APB3_CKAHB_DIV2;
/* APB2 = AHB / 1 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB / 2 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
/* PLL select HXTAL, configure PLL input and output range */
RCU_PLLALL &= ~(RCU_PLLALL_PLLSEL | RCU_PLLALL_PLL0VCOSEL | RCU_PLLALL_PLL0RNG);
RCU_PLLALL |= (RCU_PLLSRC_HXTAL | RCU_PLL0RNG_4M_8M);
/* PLL0P = HXTAL / 5 * 120 = 600 MHz */
RCU_PLL0 &= ~(RCU_PLL0_PLL0N | RCU_PLL0_PLL0PSC | RCU_PLL0_PLL0P | RCU_PLL0_PLL0R | RCU_PLL0_PLLSTBSRC);
RCU_PLL0 |= ((PLL0N << PLL0N_REG_OFFSET) | (PLL0PSC << PLL0PSC_REG_OFFSET) | (PLL0P << PLL0P_REG_OFFSET) | (PLL0R << PLL0R_REG_OFFSET));
RCU_PLLADDCTL &= ~(RCU_PLLADDCTL_PLL0Q);
RCU_PLLADDCTL |= (PLL0Q << PLL0Q_REG_OFFSET);
/* enable PLL0P, PLL0Q, PLL0R */
RCU_PLLADDCTL |= RCU_PLLADDCTL_PLL0PEN | RCU_PLLADDCTL_PLL0QEN | RCU_PLLADDCTL_PLL0REN;
/* enable PLL0 */
RCU_CTL |= RCU_CTL_PLL0EN;
/* wait until PLL0 is stable */
while(0U == (RCU_CTL & RCU_CTL_PLL0STB)) {
}
/* select PLL0 as system clock */
RCU_CFG0 &= ~RCU_CFG0_SCS;
RCU_CFG0 |= RCU_CKSYSSRC_PLL0P;
/* wait until PLL0 is selected as system clock */
while(RCU_SCSS_PLL0P != (RCU_CFG0 & RCU_CFG0_SCSS)) {
}
}
#endif /* __SYSTEM_CLOCK_IRC64M */
/*!
\brief update the SystemCoreClock with current core clock retrieved from cpu registers
\param[in] none
\param[out] none
\retval none
*/
void SystemCoreClockUpdate(void)
{
uint32_t sws = 0U;
uint32_t irc64div = 0U;
uint32_t pllpsc = 0U, plln = 0U, pllp = 0U, pllsel = 0U;
sws = GET_BITS(RCU_CFG0, 2, 3);
switch(sws) {
/* IRC64M is selected as CK_SYS */
case SEL_IRC64MDIV:
irc64div = (1U << GET_BITS(RCU_ADDCTL1, 16, 17));
SystemCoreClock = IRC64M_VALUE / irc64div;
break;
/* HXTAL is selected as CK_SYS */
case SEL_LPIRC4M:
SystemCoreClock = LPIRC4M_VALUE;
break;
/* HXTAL is selected as CK_SYS */
case SEL_HXTAL:
SystemCoreClock = HXTAL_VALUE;
break;
/* PLL0P is selected as CK_SYS */
case SEL_PLL0P:
/* get the value of PLL0PSC[0,5], PLL0N[6,14], PLL0P[16,22] */
pllpsc = GET_BITS(RCU_PLL0, 0, 5);
plln = GET_BITS(RCU_PLL0, 6, 14) + 1U;
pllp = GET_BITS(RCU_PLL0, 16, 22) + 1U;
/* PLL clock source selection, HXTAL or IRC64M_VALUE or LPIRC4M_VALUE */
pllsel = GET_BITS(RCU_PLLALL, 16, 17);
if(0U == pllsel) {
irc64div = (1U << GET_BITS(RCU_ADDCTL1, 16, 17));
SystemCoreClock = (IRC64M_VALUE / irc64div / pllpsc) * plln / pllp;
} else if(1U == pllsel) {
SystemCoreClock = (LPIRC4M_VALUE / pllpsc) * plln / pllp;
} else {
SystemCoreClock = (HXTAL_VALUE / pllpsc) * plln / pllp;
}
break;
default:
/* should not be here */
break;
}
}
#ifdef __FIRMWARE_VERSION_DEFINE
/*!
\brief get firmware version
\param[in] none
\param[out] none
\retval firmware version
*/
uint32_t gd32h7xx_firmware_version_get(void)
{
return __GD32H7XX_STDPERIPH_VERSION;
}
#endif /* __FIRMWARE_VERSION_DEFINE */
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -0,0 +1,623 @@
/*!
\file gd32h7xx_adc.h
\brief definitions for the ADC
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
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 GD32H7XX_ADC_H
#define GD32H7XX_ADC_H
#include "gd32h7xx.h"
/* ADC definitions */
#define ADC0 ADC_BASE /*!< ADC0 base address */
#define ADC1 (ADC_BASE + 0x00000400U) /*!< ADC1 base address */
#define ADC2 (ADC_BASE + 0x00000800U) /*!< ADC2 base address */
/* registers definitions */
#define ADC_STAT(adcx) REG32((adcx) + 0x00000000U) /*!< ADC status register */
#define ADC_CTL0(adcx) REG32((adcx) + 0x00000004U) /*!< ADC control register 0 */
#define ADC_CTL1(adcx) REG32((adcx) + 0x00000008U) /*!< ADC control register 1 */
#define ADC_IOFF0(adcx) REG32((adcx) + 0x0000000CU) /*!< ADC inserted channel data offset register 0 */
#define ADC_IOFF1(adcx) REG32((adcx) + 0x00000010U) /*!< ADC inserted channel data offset register 1 */
#define ADC_IOFF2(adcx) REG32((adcx) + 0x00000014U) /*!< ADC inserted channel data offset register 2 */
#define ADC_IOFF3(adcx) REG32((adcx) + 0x00000018U) /*!< ADC inserted channel data offset register 3 */
#define ADC_WDHT0(adcx) REG32((adcx) + 0x0000001CU) /*!< ADC watchdog high threshold register 0 */
#define ADC_WDLT0(adcx) REG32((adcx) + 0x00000020U) /*!< ADC watchdog low threshold register 0 */
#define ADC_RSQ0(adcx) REG32((adcx) + 0x00000024U) /*!< ADC regular sequence register 0 */
#define ADC_RSQ1(adcx) REG32((adcx) + 0x00000028U) /*!< ADC regular sequence register 1 */
#define ADC_RSQ2(adcx) REG32((adcx) + 0x0000002CU) /*!< ADC regular sequence register 2 */
#define ADC_RSQ3(adcx) REG32((adcx) + 0x00000030U) /*!< ADC regular sequence register 3 */
#define ADC_RSQ4(adcx) REG32((adcx) + 0x00000034U) /*!< ADC regular sequence register 4 */
#define ADC_RSQ5(adcx) REG32((adcx) + 0x00000038U) /*!< ADC regular sequence register 5 */
#define ADC_RSQ6(adcx) REG32((adcx) + 0x0000003CU) /*!< ADC regular sequence register 6 */
#define ADC_RSQ7(adcx) REG32((adcx) + 0x00000040U) /*!< ADC regular sequence register 7 */
#define ADC_RSQ8(adcx) REG32((adcx) + 0x00000044U) /*!< ADC regular sequence register 8 */
#define ADC_ISQ0(adcx) REG32((adcx) + 0x00000048U) /*!< ADC inserted sequence register 0 */
#define ADC_ISQ1(adcx) REG32((adcx) + 0x0000004CU) /*!< ADC inserted sequence register 1 */
#define ADC_ISQ2(adcx) REG32((adcx) + 0x00000050U) /*!< ADC inserted sequence register 2 */
#define ADC_IDATA0(adcx) REG32((adcx) + 0x00000054U) /*!< ADC inserted data register 0 */
#define ADC_IDATA1(adcx) REG32((adcx) + 0x00000058U) /*!< ADC inserted data register 1 */
#define ADC_IDATA2(adcx) REG32((adcx) + 0x0000005CU) /*!< ADC inserted data register 2 */
#define ADC_IDATA3(adcx) REG32((adcx) + 0x00000060U) /*!< ADC inserted data register 3 */
#define ADC_RDATA(adcx) REG32((adcx) + 0x00000064U) /*!< ADC regular data register */
#define ADC_OVSAMPCTL(adcx) REG32((adcx) + 0x00000080U) /*!< ADC oversampling control register */
#define ADC_WD1SR(adcx) REG32((adcx) + 0x000000A0U) /*!< ADC watchdog 1 channel selection register */
#define ADC_WD2SR(adcx) REG32((adcx) + 0x000000A4U) /*!< ADC watchdog 2 channel selection register */
#define ADC_WDHT1(adcx) REG32((adcx) + 0x000000A8U) /*!< ADC watchdog high threshold register 1 */
#define ADC_WDLT1(adcx) REG32((adcx) + 0x000000ACU) /*!< ADC watchdog low threshold register 1 */
#define ADC_WDHT2(adcx) REG32((adcx) + 0x000000B0U) /*!< ADC watchdog high threshold register 2 */
#define ADC_WDLT2(adcx) REG32((adcx) + 0x000000B4U) /*!< ADC watchdog low threshold register 2 */
#define ADC_DIFCTL(adcx) REG32((adcx) + 0x000000B8U) /*!< ADC differential mode control register */
#define ADC_SSTAT REG32((ADC0) + 0x00000300U) /*!< ADC summary status register */
#define ADC_SYNCCTL(adcx) REG32((adcx) + 0x00000304U) /*!< ADC sync control register */
#define ADC_SYNCDATA0 REG32((ADC0) + 0x00000308U) /*!< ADC sync regular data register 0 */
#define ADC_SYNCDATA1 REG32((ADC0) + 0x0000030CU) /*!< ADC sync regular data register 1 */
/* bits definitions */
/* ADC_STAT */
#define ADC_STAT_WDE0 BIT(0) /*!< analog watchdog 0 event flag */
#define ADC_STAT_EOC BIT(1) /*!< end of group conversion flag */
#define ADC_STAT_EOIC BIT(2) /*!< end of inserted group conversion flag */
#define ADC_STAT_STIC BIT(3) /*!< start flag of inserted channel group */
#define ADC_STAT_STRC BIT(4) /*!< start flag of regular channel group */
#define ADC_STAT_ROVF BIT(5) /*!< regular data register overflow */
#define ADC_STAT_WDE1 BIT(30) /*!< analog watchdog 1 event flag */
#define ADC_STAT_WDE2 BIT(31) /*!< analog watchdog 2 event flag */
/* ADC_CTL0 */
#define ADC_CTL0_WD0CHSEL BITS(0,4) /*!< analog watchdog channel select */
#define ADC_CTL0_EOCIE BIT(5) /*!< interrupt enable for EOC */
#define ADC_CTL0_WDE0IE BIT(6) /*!< interrupt enable for WDE0 */
#define ADC_CTL0_EOICIE BIT(7) /*!< interrupt enable for EOIC */
#define ADC_CTL0_SM BIT(8) /*!< scan mode */
#define ADC_CTL0_WD0SC BIT(9) /*!< when in scan mode, analog watchdog 0 is effective on a single channel */
#define ADC_CTL0_ICA BIT(10) /*!< inserted channel group convert automatically */
#define ADC_CTL0_DISRC BIT(11) /*!< discontinuous mode on regular channels */
#define ADC_CTL0_DISIC BIT(12) /*!< discontinuous mode on inserted channels */
#define ADC_CTL0_DISNUM BITS(13,15) /*!< number of conversions in discontinuous mode */
#define ADC_CTL0_IWD0EN BIT(22) /*!< inserted channel analog watchdog 0 enable */
#define ADC_CTL0_RWD0EN BIT(23) /*!< regular channel analog watchdog 0 enable */
#define ADC_CTL0_DRES BITS(24,25) /*!< ADC data resolution */
#define ADC_CTL0_ROVFIE BIT(26) /*!< interrupt enable for ROVF */
#define ADC_CTL0_WDE1IE BIT(30) /*!< interrupt enable for WDE1 */
#define ADC_CTL0_WDE2IE BIT(31) /*!< interrupt enable for WDE2 */
/* ADC_CTL1 */
#define ADC_CTL1_ADCON BIT(0) /*!< ADC on */
#define ADC_CTL1_CTN BIT(1) /*!< continuous mode */
#define ADC_CTL1_CLB BIT(2) /*!< ADC calibration */
#define ADC_CTL1_RSTCLB BIT(3) /*!< reset calibration */
#define ADC_CTL1_CALNUM BITS(4,6) /*!< ADC calibration times */
#define ADC_CTL1_DMA BIT(8) /*!< DMA request enable */
#define ADC_CTL1_DDM BIT(9) /*!< DMA disable mode */
#define ADC_CTL1_EOCM BIT(10) /*!< end of conversion mode */
#define ADC_CTL1_DAL BIT(11) /*!< data alignment */
#define ADC_CTL1_HPDFCFG BIT(12) /*!< HPDF mode configuration */
#define ADC_CTL1_ETMIC BITS(20,21) /*!< external trigger mode for inserted channels */
#define ADC_CTL1_SWICST BIT(22) /*!< software start on inserted channel */
#define ADC_CTL1_TSVEN1 BIT(23) /*!< temperature sensor channel enable */
#define ADC_CTL1_INREFEN BIT(24) /*!< vrefint channel enable */
#define ADC_CTL1_VBATEN BIT(25) /*!< vbat channel enable */
#define ADC_CTL1_CALMOD BIT(27) /*!< ADC calibration mode */
#define ADC_CTL1_ETMRC BITS(28,29) /*!< external trigger mode for regular channels */
#define ADC_CTL1_SWRCST BIT(30) /*!< software start on regular channel. */
#define ADC_CTL1_TSVEN2 BIT(31) /*!< high-precision temperature sensor channel enable */
/* ADC_IOFFx x=0..3 */
#define ADC_IOFFX_IOFF BITS(0,23) /*!< data offset for inserted channel x */
/* ADC_WDHT0 */
#define ADC_WDHT0_WDHT0 BITS(0,23) /*!< high threshold for analog watchdog 0 */
/* ADC_WDLT0 */
#define ADC_WDLT0_WDLT0 BITS(0,23) /*!< low threshold for analog watchdog 0 */
/* ADC_RSQx x=0..8 */
#define ADC_RSQX_RSQN BITS(0,4) /*!< nth conversion channel number in the regular channel group */
#define ADC_RSQX_RSMPN BITS(5,14) /*!< nth conversion sample time in the regular channel group */
#define ADC_RSQ0_RL BITS(20,23) /*!< regular channel group length */
/* ADC_ISQx x=0..2 */
#define ADC_ISQX_ISQN BITS(0,4) /*!< nth conversion channel number in the inserted channel group */
#define ADC_ISQX_ISMPN BITS(5,14) /*!< nth conversion sample time in the inserted channel group */
#define ADC_ISQ0_IL BITS(20,21) /*!< inserted channel group length */
/* ADC_IDATAx x=0..3 */
#define ADC_IDATAX_IDATAN BITS(0,31) /*!< Inserted number n conversion data */
/* ADC_RDATA */
#define ADC_RDATA_RDATA BITS(0,31) /*!< regular channel data */
/* ADC_OVSAMPCTL */
#define ADC_OVSAMPCTL_OVSEN BIT(0) /*!< oversampling enable */
#define ADC_OVSAMPCTL_OVSS BITS(5,8) /*!< oversampling shift */
#define ADC_OVSAMPCTL_TOVS BIT(9) /*!< triggered oversampling */
#define ADC_OVSAMPCTL_OVSR BITS(16,25) /*!< oversampling ratio */
/* ADC_WD1SR */
#define ADC_WD1SR_AWD1CS BITS(0,21) /*!< analog watchdog 1 channel selection */
/* ADC_WD2SR */
#define ADC_WD2SR_AWD2CS BITS(0,21) /*!< analog watchdog 2 channel selection */
/* ADC_WDHT1 */
#define ADC_WDHT1_WDHT1 BITS(0,23) /*!< high threshold for analog watchdog 1 */
/* ADC_WDHT1 */
#define ADC_WDLT1_WDLT1 BITS(0,23) /*!< low threshold for analog watchdog 1 */
/* ADC_WDHT2 */
#define ADC_WDHT2_WDHT2 BITS(0,23) /*!< high threshold for analog watchdog 2 */
/* ADC_WDHT2 */
#define ADC_WDLT2_WDLT2 BITS(0,23) /*!< low threshold for analog watchdog 2 */
/* ADC_DIFCTL */
#define ADC_DIFCTL_DIFCTL BITS(0,21) /*!< Differential mode for channel 0..21 */
/* ADC_SSTAT */
#define ADC_SSTAT_ADC0_WDE0 BIT(0) /*!< the bit is mirror image of the WDE0 bit of ADC0 */
#define ADC_SSTAT_ADC0_WDE1 BIT(1) /*!< the bit is mirror image of the WDE1 bit of ADC0 */
#define ADC_SSTAT_ADC0_WDE2 BIT(2) /*!< the bit is mirror image of the WDE2 bit of ADC0 */
#define ADC_SSTAT_ADC0_EOC BIT(3) /*!< the bit is mirror image of the EOC bit of ADC0 */
#define ADC_SSTAT_ADC0_EOIC BIT(4) /*!< the bit is mirror image of the EOIC bit of ADC0 */
#define ADC_SSTAT_ADC0_STIC BIT(5) /*!< the bit is mirror image of the STIC bit of ADC0 */
#define ADC_SSTAT_ADC0_STRC BIT(6) /*!< the bit is mirror image of the STRC bit of ADC0 */
#define ADC_SSTAT_ADC0_ROVF BIT(7) /*!< the bit is mirror image of the ROVF bit of ADC0 */
#define ADC_SSTAT_ADC1_WDE0 BIT(8) /*!< the bit is mirror image of the WDE0 bit of ADC1 */
#define ADC_SSTAT_ADC1_WDE1 BIT(9) /*!< the bit is mirror image of the WDE1 bit of ADC1 */
#define ADC_SSTAT_ADC1_WDE2 BIT(10) /*!< the bit is mirror image of the WDE2 bit of ADC1 */
#define ADC_SSTAT_ADC1_EOC BIT(11) /*!< the bit is mirror image of the EOC bit of ADC1 */
#define ADC_SSTAT_ADC1_EOIC BIT(12) /*!< the bit is mirror image of the EOIC bit of ADC1 */
#define ADC_SSTAT_ADC1_STIC BIT(13) /*!< the bit is mirror image of the STIC bit of ADC1 */
#define ADC_SSTAT_ADC1_STRC BIT(14) /*!< the bit is mirror image of the STRC bit of ADC1 */
#define ADC_SSTAT_ADC1_ROVF BIT(15) /*!< the bit is mirror image of the ROVF bit of ADC1 */
#define ADC_SSTAT_ADC2_WDE0 BIT(16) /*!< the bit is mirror image of the WDE0 bit of ADC2 */
#define ADC_SSTAT_ADC2_WDE1 BIT(17) /*!< the bit is mirror image of the WDE1 bit of ADC2 */
#define ADC_SSTAT_ADC2_WDE2 BIT(18) /*!< the bit is mirror image of the WDE2 bit of ADC2 */
#define ADC_SSTAT_ADC2_EOC BIT(19) /*!< the bit is mirror image of the EOC bit of ADC2 */
#define ADC_SSTAT_ADC2_EOIC BIT(20) /*!< the bit is mirror image of the EOIC bit of ADC2 */
#define ADC_SSTAT_ADC2_STIC BIT(21) /*!< the bit is mirror image of the STIC bit of ADC2 */
#define ADC_SSTAT_ADC2_STRC BIT(22) /*!< the bit is mirror image of the STRC bit of ADC2 */
#define ADC_SSTAT_ADC2_ROVF BIT(23) /*!< the bit is mirror image of the ROVF bit of ADC2 */
/* ADC_SYNCCTL */
#define ADC_SYNCCTL_SYNCM BITS(0,3) /*!< ADC sync mode */
#define ADC_SYNCCTL_SYNCDLY BITS(8,11) /*!< ADC sync delay */
#define ADC_SYNCCTL_SYNCDDM BIT(13) /*!< ADC sync DMA disable mode */
#define ADC_SYNCCTL_SYNCDMA BITS(14,15) /*!< ADC sync DMA mode selection */
#define ADC_SYNCCTL_ADCSCK BITS(16,19) /*!< ADC sync clock mode */
#define ADC_SYNCCTL_ADCCK BITS(20,23) /*!< ADC clock prescaler */
/* ADC_SYNCDATA0 */
#define ADC_SYNCDATA0_SYNCDATA0 BITS(0,15) /*!< ADC0 regular data in ADC synchronization mode */
#define ADC_SYNCDATA0_SYNCDATA1 BITS(16,31) /*!< ADC1 regular data in ADC synchronization mode */
/* constants definitions */
/* ADC status flag */
#define ADC_FLAG_WDE0 ADC_STAT_WDE0 /*!< analog watchdog 0 event flag */
#define ADC_FLAG_EOC ADC_STAT_EOC /*!< end of group conversion flag */
#define ADC_FLAG_EOIC ADC_STAT_EOIC /*!< end of inserted group conversion flag */
#define ADC_FLAG_STIC ADC_STAT_STIC /*!< start flag of inserted channel group */
#define ADC_FLAG_STRC ADC_STAT_STRC /*!< start flag of regular channel group */
#define ADC_FLAG_ROVF ADC_STAT_ROVF /*!< regular data register overflow */
#define ADC_FLAG_WDE1 ADC_STAT_WDE1 /*!< analog watchdog 1 event flag */
#define ADC_FLAG_WDE2 ADC_STAT_WDE2 /*!< analog watchdog 2 event flag */
/* ADC interrupt */
#define ADC_INT_WDE0 ADC_CTL0_WDE0IE /*!< interrupt enable for WDE0 */
#define ADC_INT_EOC ADC_CTL0_EOCIE /*!< interrupt enable for EOC */
#define ADC_INT_EOIC ADC_CTL0_EOICIE /*!< interrupt enable for EOIC */
#define ADC_INT_ROVF ADC_CTL0_ROVFIE /*!< interrupt enable for ROVF */
#define ADC_INT_WDE1 ADC_CTL0_WDE1IE /*!< interrupt enable for WDE1 */
#define ADC_INT_WDE2 ADC_CTL0_WDE2IE /*!< interrupt enable for WDE2 */
/* ADC interrupt flag */
#define ADC_INT_FLAG_WDE0 ADC_STAT_WDE0 /*!< analog watchdog 0 event interrupt flag */
#define ADC_INT_FLAG_EOC ADC_STAT_EOC /*!< end of group conversion interrupt flag */
#define ADC_INT_FLAG_EOIC ADC_STAT_EOIC /*!< end of inserted group conversion interrupt flag */
#define ADC_INT_FLAG_ROVF ADC_STAT_ROVF /*!< regular data register overflow interrupt flag */
#define ADC_INT_FLAG_WDE1 ADC_STAT_WDE1 /*!< analog watchdog 1 event interrupt flag */
#define ADC_INT_FLAG_WDE2 ADC_STAT_WDE2 /*!< analog watchdog 2 event interrupt flag */
/* number of conversions in discontinuous mode */
#define CTL0_DISNUM(regval) (BITS(13,15) & ((uint32_t)(regval) << 13U)) /*!< write value to ADC_CTL0_DISNUM bit field */
/* ADC special function definitions */
#define ADC_SCAN_MODE ADC_CTL0_SM /*!< scan mode */
#define ADC_INSERTED_CHANNEL_AUTO ADC_CTL0_ICA /*!< inserted channel group convert automatically */
#define ADC_CONTINUOUS_MODE ADC_CTL1_CTN /*!< continuous mode */
/* ADC calibration mode */
#define ADC_CALIBRATION_OFFSET_MISMATCH ((uint32_t)0x00000000U) /*!< ADC calibration offset and mismatch mode */
#define ADC_CALIBRATION_OFFSET ADC_CTL1_CALMOD /*!< ADC calibration mode */
/* ADC calibration times */
#define CTL1_CALNUM(regval) (BITS(4,6) & ((uint32_t)(regval) << 4U)) /*!< write value to ADC_CTL1_CLBNUM bit field */
#define ADC_CALIBRATION_NUM1 CTL1_CALNUM(0) /*!< ADC calibration 1 time */
#define ADC_CALIBRATION_NUM2 CTL1_CALNUM(1) /*!< ADC calibration 2 times */
#define ADC_CALIBRATION_NUM4 CTL1_CALNUM(2) /*!< ADC calibration 4 times */
#define ADC_CALIBRATION_NUM8 CTL1_CALNUM(3) /*!< ADC calibration 8 times */
#define ADC_CALIBRATION_NUM16 CTL1_CALNUM(4) /*!< ADC calibration 16 times */
#define ADC_CALIBRATION_NUM32 CTL1_CALNUM(5) /*!< ADC calibration 32 times */
/* ADC data alignment */
#define ADC_DATAALIGN_RIGHT ((uint32_t)0x00000000U) /*!< LSB alignment */
#define ADC_DATAALIGN_LEFT ADC_CTL1_DAL /*!< MSB alignment */
/* end of conversion mode */
#define ADC_EOC_SET_SEQUENCE ((uint32_t)0x00000000U) /*!< only at the end of a sequence of regular conversions, the EOC bit is set */
#define ADC_EOC_SET_CONVERSION ADC_CTL1_EOCM /*!< at the end of each regular conversion, the EOC bit is set */
/* ADC internal channel definitions */
#define ADC_CHANNEL_INTERNAL_TEMPSENSOR ADC_CTL1_TSVEN1 /*!< temperature sensor channel */
#define ADC_CHANNEL_INTERNAL_VREFINT ADC_CTL1_INREFEN /*!< vrefint channel */
#define ADC_CHANNEL_INTERNAL_VBAT ADC_CTL1_VBATEN /*!< vbat channel */
#define ADC_CHANNEL_INTERNAL_HP_TEMPSENSOR ADC_CTL1_TSVEN2 /*!< high-precision temperature sensor channel */
/* ADC data offset for inserted channel x */
#define IOFFX_IOFF(regval) (BITS(0,23) & ((uint32_t)(regval) << 0U)) /*!< write value to ADC_IOFFX_IOFF bit field */
/* ADC high threshold for analog watchdog 0 */
#define WDHT0_WDHT0(regval) (BITS(0,23) & ((uint32_t)(regval) << 0U)) /*!< write value to ADC_WDHT0_WDHT0 bit field */
/* ADC low threshold for analog watchdog 0 */
#define WDLT0_WDLT0(regval) (BITS(0,23) & ((uint32_t)(regval) << 0U)) /*!< write value to ADC_WDLT0_WDLT0 bit field */
/* ADC high threshold for analog watchdog 1 */
#define WDHT1_WDHT1(regval) (BITS(0,23) & ((uint32_t)(regval) << 0U)) /*!< write value to ADC_WDHT1_WDHT1 bit field */
/* ADC low threshold for analog watchdog 1 */
#define WDLT1_WDLT1(regval) (BITS(0,23) & ((uint32_t)(regval) << 0U)) /*!< write value to ADC_WDLT1_WDLT1 bit field */
/* ADC high threshold for analog watchdog 2 */
#define WDHT2_WDHT2(regval) (BITS(0,23) & ((uint32_t)(regval) << 0U)) /*!< write value to ADC_WDHT2_WDHT2 bit field */
/* ADC low threshold for analog watchdog 2 */
#define WDLT2_WDLT2(regval) (BITS(0,23) & ((uint32_t)(regval) << 0U)) /*!< write value to ADC_WDLT2_WDLT2 bit field */
/* ADC sequence sample time */
#define SQX_SMP(regval) (BITS(5,14) & ((uint32_t)(regval) << 5U)) /*!< write value to RSQX_SMPn or ISQX_SMPn bit field */
/* ADC regular channel group length */
#define RSQ0_RL(regval) (BITS(20,23) & ((uint32_t)(regval) << 20U)) /*!< write value to ADC_RSQ0_RL bit field */
/* ADC inserted channel group length */
#define ISQ0_IL(regval) (BITS(20,21) & ((uint32_t)(regval) << 20U)) /*!< write value to ADC_ISQ0_IL bit field */
/* ADC resolution */
#define CTL0_DRES(regval) (BITS(24,25) & ((uint32_t)(regval) << 24U)) /*!< write value to ADC_CTL0_DRES bit field */
#define ADC_RESOLUTION_14B ((uint8_t)0x00U) /*!< 14-bit ADC resolution */
#define ADC_RESOLUTION_12B ((uint8_t)0x01U) /*!< 12-bit ADC resolution */
#define ADC_RESOLUTION_10B ((uint8_t)0x02U) /*!< 10-bit ADC resolution */
#define ADC_RESOLUTION_8B ((uint8_t)0x03U) /*!< 8-bit ADC resolution */
#define ADC_RESOLUTION_6B ((uint8_t)0x04U) /*!< 6-bit ADC resolution */
/* oversampling shift */
#define OVSCR_OVSS(regval) (BITS(5,8) & ((uint32_t)(regval) << 5U)) /*!< write value to ADC_OVSAMPCTL_OVSS bit field */
#define ADC_OVERSAMPLING_SHIFT_NONE OVSCR_OVSS(0) /*!< no oversampling shift */
#define ADC_OVERSAMPLING_SHIFT_1B OVSCR_OVSS(1) /*!< 1-bit oversampling shift */
#define ADC_OVERSAMPLING_SHIFT_2B OVSCR_OVSS(2) /*!< 2-bit oversampling shift */
#define ADC_OVERSAMPLING_SHIFT_3B OVSCR_OVSS(3) /*!< 3-bit oversampling shift */
#define ADC_OVERSAMPLING_SHIFT_4B OVSCR_OVSS(4) /*!< 4-bit oversampling shift */
#define ADC_OVERSAMPLING_SHIFT_5B OVSCR_OVSS(5) /*!< 5-bit oversampling shift */
#define ADC_OVERSAMPLING_SHIFT_6B OVSCR_OVSS(6) /*!< 6-bit oversampling shift */
#define ADC_OVERSAMPLING_SHIFT_7B OVSCR_OVSS(7) /*!< 7-bit oversampling shift */
#define ADC_OVERSAMPLING_SHIFT_8B OVSCR_OVSS(8) /*!< 8-bit oversampling shift */
#define ADC_OVERSAMPLING_SHIFT_9B OVSCR_OVSS(9) /*!< 9-bit oversampling shift */
#define ADC_OVERSAMPLING_SHIFT_10B OVSCR_OVSS(10) /*!< 10-bit oversampling shift */
#define ADC_OVERSAMPLING_SHIFT_11B OVSCR_OVSS(11) /*!< 11-bit oversampling shift */
/* oversampling ratio */
#define OVSCR_OVSR(regval) (BITS(16,25) & ((uint32_t)(regval) << 16U)) /*!< write value to ADC_OVSAMPCTL_OVSR bit field */
/* triggered oversampling */
#define ADC_OVERSAMPLING_ALL_CONVERT ((uint32_t)0x00000000U) /*!< all oversampled conversions for a channel are done consecutively after a trigger */
#define ADC_OVERSAMPLING_ONE_CONVERT ADC_OVSAMPCTL_TOVS /*!< each oversampled conversion for a channel needs a trigger */
/* configure the ADC clock */
#define SYNCCTL_ADCSCK(regval) (BITS(16,19) & ((uint32_t)(regval) << 16U)) /*!< write value to ADC_SYNCCTL_ADCSCK bit field */
#define SYNCCTL_ADCCK(regval) (BITS(20,23) & ((uint32_t)(regval) << 20U)) /*!< write value to ADC_SYNCCTL_ADCCK bit field */
#define ADC_CLK_SYNC_HCLK_DIV2 (SYNCCTL_ADCCK(0) | SYNCCTL_ADCSCK(8)) /*!< ADC sync clock mode HCLK div2 */
#define ADC_CLK_SYNC_HCLK_DIV4 (SYNCCTL_ADCCK(0) | SYNCCTL_ADCSCK(9)) /*!< ADC sync clock mode HCLK div4 */
#define ADC_CLK_SYNC_HCLK_DIV6 (SYNCCTL_ADCCK(0) | SYNCCTL_ADCSCK(10)) /*!< ADC sync clock mode HCLK div6 */
#define ADC_CLK_SYNC_HCLK_DIV8 (SYNCCTL_ADCCK(0) | SYNCCTL_ADCSCK(11)) /*!< ADC sync clock mode HCLK div8 */
#define ADC_CLK_SYNC_HCLK_DIV10 (SYNCCTL_ADCCK(0) | SYNCCTL_ADCSCK(12)) /*!< ADC sync clock mode HCLK div10 */
#define ADC_CLK_SYNC_HCLK_DIV12 (SYNCCTL_ADCCK(0) | SYNCCTL_ADCSCK(13)) /*!< ADC sync clock mode HCLK div12 */
#define ADC_CLK_SYNC_HCLK_DIV14 (SYNCCTL_ADCCK(0) | SYNCCTL_ADCSCK(14)) /*!< ADC sync clock mode HCLK div14 */
#define ADC_CLK_SYNC_HCLK_DIV16 (SYNCCTL_ADCCK(0) | SYNCCTL_ADCSCK(15)) /*!< ADC sync clock mode HCLK div16 */
#define ADC_CLK_ASYNC_DIV1 (SYNCCTL_ADCCK(0) | SYNCCTL_ADCSCK(0)) /*!< ADC async clock mode div1 */
#define ADC_CLK_ASYNC_DIV2 (SYNCCTL_ADCCK(1) | SYNCCTL_ADCSCK(0)) /*!< ADC async clock mode div2 */
#define ADC_CLK_ASYNC_DIV4 (SYNCCTL_ADCCK(2) | SYNCCTL_ADCSCK(0)) /*!< ADC async clock mode div4 */
#define ADC_CLK_ASYNC_DIV6 (SYNCCTL_ADCCK(3) | SYNCCTL_ADCSCK(0)) /*!< ADC async clock mode div6 */
#define ADC_CLK_ASYNC_DIV8 (SYNCCTL_ADCCK(4) | SYNCCTL_ADCSCK(0)) /*!< ADC async clock mode div8 */
#define ADC_CLK_ASYNC_DIV10 (SYNCCTL_ADCCK(5) | SYNCCTL_ADCSCK(0)) /*!< ADC async clock mode div10 */
#define ADC_CLK_ASYNC_DIV12 (SYNCCTL_ADCCK(6) | SYNCCTL_ADCSCK(0)) /*!< ADC async clock mode div12 */
#define ADC_CLK_ASYNC_DIV16 (SYNCCTL_ADCCK(7) | SYNCCTL_ADCSCK(0)) /*!< ADC async clock mode div16 */
#define ADC_CLK_ASYNC_DIV32 (SYNCCTL_ADCCK(8) | SYNCCTL_ADCSCK(0)) /*!< ADC async clock mode div32 */
#define ADC_CLK_ASYNC_DIV64 (SYNCCTL_ADCCK(9) | SYNCCTL_ADCSCK(0)) /*!< ADC async clock mode div64 */
#define ADC_CLK_ASYNC_DIV128 (SYNCCTL_ADCCK(10) | SYNCCTL_ADCSCK(0)) /*!< ADC async clock mode div128 */
#define ADC_CLK_ASYNC_DIV256 (SYNCCTL_ADCCK(11) | SYNCCTL_ADCSCK(0)) /*!< ADC async clock mode div256 */
/* ADC sync DMA mode selection */
#define SYNCCTL_SYNCDMA(regval) (BITS(14,15) & ((uint32_t)(regval) << 14U)) /*!< write value to ADC_SYNCCTL_SYNCDMA bit field */
#define ADC_SYNC_DMA_DISABLE SYNCCTL_SYNCDMA(0) /*!< ADC sync DMA disabled */
#define ADC_SYNC_DMA_MODE0 SYNCCTL_SYNCDMA(1) /*!< ADC sync DMA mode 0 */
#define ADC_SYNC_DMA_MODE1 SYNCCTL_SYNCDMA(2) /*!< ADC sync DMA mode 1 */
/* ADC sync delay */
#define SYNCCTL_SYNCDLY(regval) (BITS(8,11) & ((uint32_t)(regval) << 8U)) /*!< write value to ADC_SYNCCTL_SYNCDLY bit field */
#define ADC_SYNC_DELAY_5CYCLE SYNCCTL_SYNCDLY(0) /*!< the delay between 2 sampling phases in ADC synchronization modes to 5 ADC clock cycles. */
#define ADC_SYNC_DELAY_6CYCLE SYNCCTL_SYNCDLY(1) /*!< the delay between 2 sampling phases in ADC synchronization modes to 6 ADC clock cycles. */
#define ADC_SYNC_DELAY_7CYCLE SYNCCTL_SYNCDLY(2) /*!< the delay between 2 sampling phases in ADC synchronization modes to 7 ADC clock cycles. */
#define ADC_SYNC_DELAY_8CYCLE SYNCCTL_SYNCDLY(3) /*!< the delay between 2 sampling phases in ADC synchronization modes to 8 ADC clock cycles. */
#define ADC_SYNC_DELAY_9CYCLE SYNCCTL_SYNCDLY(4) /*!< the delay between 2 sampling phases in ADC synchronization modes to 9 ADC clock cycles. */
#define ADC_SYNC_DELAY_10CYCLE SYNCCTL_SYNCDLY(5) /*!< the delay between 2 sampling phases in ADC synchronization modes to 10 ADC clock cycles. */
#define ADC_SYNC_DELAY_11CYCLE SYNCCTL_SYNCDLY(6) /*!< the delay between 2 sampling phases in ADC synchronization modes to 11 ADC clock cycles. */
#define ADC_SYNC_DELAY_12CYCLE SYNCCTL_SYNCDLY(7) /*!< the delay between 2 sampling phases in ADC synchronization modes to 12 ADC clock cycles. */
#define ADC_SYNC_DELAY_13CYCLE SYNCCTL_SYNCDLY(8) /*!< the delay between 2 sampling phases in ADC synchronization modes to 13 ADC clock cycles. */
#define ADC_SYNC_DELAY_14CYCLE SYNCCTL_SYNCDLY(9) /*!< the delay between 2 sampling phases in ADC synchronization modes to 14 ADC clock cycles. */
#define ADC_SYNC_DELAY_15CYCLE SYNCCTL_SYNCDLY(10) /*!< the delay between 2 sampling phases in ADC synchronization modes to 15 ADC clock cycles. */
#define ADC_SYNC_DELAY_16CYCLE SYNCCTL_SYNCDLY(11) /*!< the delay between 2 sampling phases in ADC synchronization modes to 16 ADC clock cycles. */
#define ADC_SYNC_DELAY_17CYCLE SYNCCTL_SYNCDLY(12) /*!< the delay between 2 sampling phases in ADC synchronization modes to 17 ADC clock cycles. */
#define ADC_SYNC_DELAY_18CYCLE SYNCCTL_SYNCDLY(13) /*!< the delay between 2 sampling phases in ADC synchronization modes to 18 ADC clock cycles. */
#define ADC_SYNC_DELAY_19CYCLE SYNCCTL_SYNCDLY(14) /*!< the delay between 2 sampling phases in ADC synchronization modes to 19 ADC clock cycles. */
#define ADC_SYNC_DELAY_20CYCLE SYNCCTL_SYNCDLY(15) /*!< the delay between 2 sampling phases in ADC synchronization modes to 20 ADC clock cycles. */
/* ADC sync mode */
#define SYNCCTL_SYNCM(regval) (BITS(0,3) & ((uint32_t)(regval) << 0U)) /*!< write value to ADC_SYNCCTL_SYNCM bit field */
#define ADC_SYNC_MODE_INDEPENDENT SYNCCTL_SYNCM(0) /*!< ADC synchronization mode disabled.All the ADCs work independently */
#define ADC_DAUL_REGULAL_PARALLEL_INSERTED_PARALLEL SYNCCTL_SYNCM(1) /*!< combined regular parallel & inserted parallel mode */
#define ADC_DAUL_REGULAL_PARALLEL_INSERTED_ROTATION SYNCCTL_SYNCM(2) /*!< combined regular parallel & trigger rotation mode */
#define ADC_DAUL_INSERTED_PARALLEL SYNCCTL_SYNCM(5) /*!< inserted parallel mode */
#define ADC_DAUL_REGULAL_PARALLEL SYNCCTL_SYNCM(6) /*!< regular parallel mode */
#define ADC_DAUL_REGULAL_FOLLOW_UP SYNCCTL_SYNCM(7) /*!< follow-up mode */
#define ADC_DAUL_INSERTED_TRIGGER_ROTATION SYNCCTL_SYNCM(9) /*!< trigger rotation mode */
/* external trigger mode for regular and inserted channel */
#define EXTERNAL_TRIGGER_DISABLE ((uint32_t)0x00000000U) /*!< external trigger disable */
#define EXTERNAL_TRIGGER_RISING ((uint32_t)0x00000001U) /*!< rising edge of external trigger */
#define EXTERNAL_TRIGGER_FALLING ((uint32_t)0x00000002U) /*!< falling edge of external trigger */
#define EXTERNAL_TRIGGER_RISING_FALLING ((uint32_t)0x00000003U) /*!< rising and falling edge of external trigger */
/* ADC channel group definitions */
#define ADC_REGULAR_CHANNEL ((uint8_t)0x01U) /*!< ADC regular channel group */
#define ADC_INSERTED_CHANNEL ((uint8_t)0x02U) /*!< ADC inserted channel group */
#define ADC_REGULAR_INSERTED_CHANNEL ((uint8_t)0x03U) /*!< both regular and inserted channel group */
#define ADC_CHANNEL_DISCON_DISABLE ((uint8_t)0x04U) /*!< disable discontinuous mode of regular & inserted channel */
/* ADC inserted channel definitions */
#define ADC_INSERTED_CHANNEL_0 ((uint8_t)0x00U) /*!< ADC inserted channel 0 */
#define ADC_INSERTED_CHANNEL_1 ((uint8_t)0x01U) /*!< ADC inserted channel 1 */
#define ADC_INSERTED_CHANNEL_2 ((uint8_t)0x02U) /*!< ADC inserted channel 2 */
#define ADC_INSERTED_CHANNEL_3 ((uint8_t)0x03U) /*!< ADC inserted channel 3 */
/* ADC channel definitions */
#define ADC_CHANNEL_0 ((uint8_t)0x00U) /*!< ADC channel 0 */
#define ADC_CHANNEL_1 ((uint8_t)0x01U) /*!< ADC channel 1 */
#define ADC_CHANNEL_2 ((uint8_t)0x02U) /*!< ADC channel 2 */
#define ADC_CHANNEL_3 ((uint8_t)0x03U) /*!< ADC channel 3 */
#define ADC_CHANNEL_4 ((uint8_t)0x04U) /*!< ADC channel 4 */
#define ADC_CHANNEL_5 ((uint8_t)0x05U) /*!< ADC channel 5 */
#define ADC_CHANNEL_6 ((uint8_t)0x06U) /*!< ADC channel 6 */
#define ADC_CHANNEL_7 ((uint8_t)0x07U) /*!< ADC channel 7 */
#define ADC_CHANNEL_8 ((uint8_t)0x08U) /*!< ADC channel 8 */
#define ADC_CHANNEL_9 ((uint8_t)0x09U) /*!< ADC channel 9 */
#define ADC_CHANNEL_10 ((uint8_t)0x0AU) /*!< ADC channel 10 */
#define ADC_CHANNEL_11 ((uint8_t)0x0BU) /*!< ADC channel 11 */
#define ADC_CHANNEL_12 ((uint8_t)0x0CU) /*!< ADC channel 12 */
#define ADC_CHANNEL_13 ((uint8_t)0x0DU) /*!< ADC channel 13 */
#define ADC_CHANNEL_14 ((uint8_t)0x0EU) /*!< ADC channel 14 */
#define ADC_CHANNEL_15 ((uint8_t)0x0FU) /*!< ADC channel 15 */
#define ADC_CHANNEL_16 ((uint8_t)0x10U) /*!< ADC channel 16 */
#define ADC_CHANNEL_17 ((uint8_t)0x11U) /*!< ADC channel 17 */
#define ADC_CHANNEL_18 ((uint8_t)0x12U) /*!< ADC channel 18 */
#define ADC_CHANNEL_19 ((uint8_t)0x13U) /*!< ADC channel 19 */
#define ADC_CHANNEL_20 ((uint8_t)0x14U) /*!< ADC channel 20 */
/* analog watchdog 1/2 channel selection for channel n(n=0..20) */
#define ADC_AWD1_2_SELECTION_CHANNEL_0 ((uint32_t)0x00000001U) /*!< ADC channel 0 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_1 ((uint32_t)0x00000002U) /*!< ADC channel 1 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_2 ((uint32_t)0x00000004U) /*!< ADC channel 2 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_3 ((uint32_t)0x00000008U) /*!< ADC channel 3 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_4 ((uint32_t)0x00000010U) /*!< ADC channel 4 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_5 ((uint32_t)0x00000020U) /*!< ADC channel 5 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_6 ((uint32_t)0x00000040U) /*!< ADC channel 6 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_7 ((uint32_t)0x00000080U) /*!< ADC channel 7 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_8 ((uint32_t)0x00000100U) /*!< ADC channel 8 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_9 ((uint32_t)0x00000200U) /*!< ADC channel 9 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_10 ((uint32_t)0x00000400U) /*!< ADC channel 10 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_11 ((uint32_t)0x00000800U) /*!< ADC channel 11 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_12 ((uint32_t)0x00001000U) /*!< ADC channel 12 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_13 ((uint32_t)0x00002000U) /*!< ADC channel 13 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_14 ((uint32_t)0x00004000U) /*!< ADC channel 14 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_15 ((uint32_t)0x00008000U) /*!< ADC channel 15 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_16 ((uint32_t)0x00010000U) /*!< ADC channel 16 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_17 ((uint32_t)0x00020000U) /*!< ADC channel 17 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_18 ((uint32_t)0x00040000U) /*!< ADC channel 18 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_19 ((uint32_t)0x00080000U) /*!< ADC channel 19 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_20 ((uint32_t)0x00100000U) /*!< ADC channel 20 analog watchdog 1/2 selection */
#define ADC_AWD1_2_SELECTION_CHANNEL_ALL ((uint32_t)0x003FFFFFU) /*!< all ADC channels analog watchdog 1/2 selection */
/* Differential mode for channel n(n=0..21) */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_0 ((uint32_t)0x00000001U) /*!< ADC channel 0 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_1 ((uint32_t)0x00000002U) /*!< ADC channel 1 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_2 ((uint32_t)0x00000004U) /*!< ADC channel 2 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_3 ((uint32_t)0x00000008U) /*!< ADC channel 3 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_4 ((uint32_t)0x00000010U) /*!< ADC channel 4 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_5 ((uint32_t)0x00000020U) /*!< ADC channel 5 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_6 ((uint32_t)0x00000040U) /*!< ADC channel 6 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_7 ((uint32_t)0x00000080U) /*!< ADC channel 7 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_8 ((uint32_t)0x00000100U) /*!< ADC channel 8 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_9 ((uint32_t)0x00000200U) /*!< ADC channel 9 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_10 ((uint32_t)0x00000400U) /*!< ADC channel 10 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_11 ((uint32_t)0x00000800U) /*!< ADC channel 11 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_12 ((uint32_t)0x00001000U) /*!< ADC channel 12 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_13 ((uint32_t)0x00002000U) /*!< ADC channel 13 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_14 ((uint32_t)0x00004000U) /*!< ADC channel 14 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_15 ((uint32_t)0x00008000U) /*!< ADC channel 15 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_16 ((uint32_t)0x00010000U) /*!< ADC channel 16 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_17 ((uint32_t)0x00020000U) /*!< ADC channel 17 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_18 ((uint32_t)0x00040000U) /*!< ADC channel 18 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_19 ((uint32_t)0x00080000U) /*!< ADC channel 19 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_20 ((uint32_t)0x00100000U) /*!< ADC channel 20 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_21 ((uint32_t)0x00200000U) /*!< ADC channel 21 differential mode */
#define ADC_DIFFERENTIAL_MODE_CHANNEL_ALL ((uint32_t)0x003FFFFFU) /*!< all ADC channels differential mode */
/* function declarations */
/* initialization config */
/* reset ADC */
void adc_deinit(uint32_t adc_periph);
/* configure the ADC clock */
void adc_clock_config(uint32_t adc_periph, uint32_t prescaler);
/* enable or disable ADC special function */
void adc_special_function_config(uint32_t adc_periph, uint32_t function, ControlStatus newvalue);
/* configure ADC data alignment */
void adc_data_alignment_config(uint32_t adc_periph, uint32_t data_alignment);
/* enable ADC interface */
void adc_enable(uint32_t adc_periph);
/* disable ADC interface */
void adc_disable(uint32_t adc_periph);
/* configure ADC calibration mode */
void adc_calibration_mode_config(uint32_t adc_periph, uint32_t clb_mode);
/* configure ADC calibration number */
void adc_calibration_number(uint32_t adc_periph, uint32_t clb_num);
/* ADC calibration and reset calibration */
void adc_calibration_enable(uint32_t adc_periph);
/* configure ADC resolution */
void adc_resolution_config(uint32_t adc_periph, uint32_t resolution);
/* enable or disable ADC internal channels */
void adc_internal_channel_config(uint32_t internal_channel, ControlStatus newvalue);
/* DMA config */
/* enable DMA request */
void adc_dma_mode_enable(uint32_t adc_periph);
/* disable DMA request */
void adc_dma_mode_disable(uint32_t adc_periph);
/* when DMA=1, the DMA engine issues a request at end of each regular conversion */
void adc_dma_request_after_last_enable(uint32_t adc_periph);
/* the DMA engine is disabled after the end of transfer signal from DMA controller is detected */
void adc_dma_request_after_last_disable(uint32_t adc_periph);
/* enable hpdf mode */
void adc_hpdf_mode_enable(uint32_t adc_periph);
/* disable hpdf mode */
void adc_hpdf_mode_disable(uint32_t adc_periph);
/* regular group and inserted group config */
/* configure ADC discontinuous mode */
void adc_discontinuous_mode_config(uint32_t adc_periph, uint8_t adc_channel_group, uint32_t length);
/* configure the length of regular channel group or inserted channel group */
void adc_channel_length_config(uint32_t adc_periph, uint8_t adc_channel_group, uint32_t length);
/* configure ADC regular channel */
void adc_regular_channel_config(uint32_t adc_periph, uint8_t rank, uint8_t adc_channel, uint32_t sample_time);
/* configure ADC inserted channel */
void adc_inserted_channel_config(uint32_t adc_periph, uint8_t rank, uint8_t adc_channel, uint32_t sample_time);
/* configure ADC inserted channel offset */
void adc_inserted_channel_offset_config(uint32_t adc_periph, uint8_t inserted_channel, uint32_t offset);
/* configure differential mode for channel */
void adc_channel_differential_mode_config(uint32_t adc_periph, uint32_t adc_channel, ControlStatus newvalue);
/* enable ADC external trigger */
void adc_external_trigger_config(uint32_t adc_periph, uint8_t adc_channel_group, uint32_t trigger_mode);
/* enable ADC software trigger */
void adc_software_trigger_enable(uint32_t adc_periph, uint8_t adc_channel_group);
/* configure end of conversion mode */
void adc_end_of_conversion_config(uint32_t adc_periph, uint32_t end_selection);
/* get channel data */
/* read ADC regular group data register */
uint32_t adc_regular_data_read(uint32_t adc_periph);
/* read ADC inserted group data register */
uint32_t adc_inserted_data_read(uint32_t adc_periph, uint8_t inserted_channel);
/* ADC analog watchdog functions */
/* configure ADC analog watchdog 0 single channel */
void adc_watchdog0_single_channel_enable(uint32_t adc_periph, uint8_t adc_channel);
/* configure ADC analog watchdog 0 group channel */
void adc_watchdog0_group_channel_enable(uint32_t adc_periph, uint8_t adc_channel_group);
/* disable ADC analog watchdog 0 */
void adc_watchdog0_disable(uint32_t adc_periph);
/* configure ADC analog watchdog 1 channel */
void adc_watchdog1_channel_config(uint32_t adc_periph, uint32_t selection_channel, ControlStatus newvalue);
/* configure ADC analog watchdog 2 channel */
void adc_watchdog2_channel_config(uint32_t adc_periph, uint32_t selection_channel, ControlStatus newvalue);
/* disable ADC analog watchdog 1 */
void adc_watchdog1_disable(uint32_t adc_periph);
/* disable ADC analog watchdog 2 */
void adc_watchdog2_disable(uint32_t adc_periph);
/* configure ADC analog watchdog 0 threshold */
void adc_watchdog0_threshold_config(uint32_t adc_periph, uint32_t low_threshold, uint32_t high_threshold);
/* configure ADC analog watchdog 1 threshold */
void adc_watchdog1_threshold_config(uint32_t adc_periph, uint32_t low_threshold, uint32_t high_threshold);
/* configure ADC analog watchdog 2 threshold */
void adc_watchdog2_threshold_config(uint32_t adc_periph, uint32_t low_threshold, uint32_t high_threshold);
/* ADC oversample functions */
/* configure ADC oversample mode */
void adc_oversample_mode_config(uint32_t adc_periph, uint32_t mode, uint16_t shift, uint16_t ratio);
/* enable ADC oversample mode */
void adc_oversample_mode_enable(uint32_t adc_periph);
/* disable ADC oversample mode */
void adc_oversample_mode_disable(uint32_t adc_periph);
/* flag and interrupt functions */
/* get the ADC flag bits */
FlagStatus adc_flag_get(uint32_t adc_periph, uint32_t flag);
/* clear the ADC flag bits */
void adc_flag_clear(uint32_t adc_periph, uint32_t flag);
/* enable ADC interrupt */
void adc_interrupt_enable(uint32_t adc_periph, uint32_t interrupt);
/* disable ADC interrupt */
void adc_interrupt_disable(uint32_t adc_periph, uint32_t interrupt);
/* get the ADC interrupt bits */
FlagStatus adc_interrupt_flag_get(uint32_t adc_periph, uint32_t int_flag);
/* clear the ADC flag */
void adc_interrupt_flag_clear(uint32_t adc_periph, uint32_t int_flag);
/* ADC synchronization */
/* configure the ADC sync mode */
void adc_sync_mode_config(uint32_t sync_mode);
/* configure the delay between 2 sampling phases in ADC sync modes */
void adc_sync_delay_config(uint32_t sample_delay);
/* configure ADC sync DMA mode selection */
void adc_sync_dma_config(uint32_t dma_mode);
/* configure ADC sync DMA engine issues requests according to the SYNCDMA bits */
void adc_sync_dma_request_after_last_enable(void);
/* configure ADC sync DMA engine is disabled after the end of transfer signal from DMA controller is detected */
void adc_sync_dma_request_after_last_disable(void);
/* read ADC sync master adc regular data register 0 */
uint32_t adc_sync_master_adc_regular_data0_read(void);
/* read ADC sync slave adc regular data register 0 */
uint32_t adc_sync_slave_adc_regular_data0_read(void);
/* read ADC sync regular data register 1 */
uint32_t adc_sync_regular_data1_read(void);
#endif /* GD32H7XX_ADC_H */
@@ -0,0 +1,226 @@
/*!
\file gd32h7xx_axiim.h
\brief definitions for AXIIM(AXI interconnect matrix)
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
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 GD32H7XX_AXIIM_H
#define GD32H7XX_AXIIM_H
#include "gd32h7xx.h"
/* AXI interconnect matrix definitions */
#define AXIIM_BASE ((uint32_t)0x51000000U) /*!< AXI interconnect matrix base address */
#define AXIIM AXIIM_BASE /*!< AXI interconnect matrix definition */
/* registers definitions */
#define AXI_PERIPH_ID4 REG32(AXIIM + 0x00001FD0U) /*!< AXI peripheral ID4 register */
#define AXI_PERIPH_ID0 REG32(AXIIM + 0x00001FE0U) /*!< AXI peripheral ID0 register */
#define AXI_PERIPH_ID1 REG32(AXIIM + 0x00001FE4U) /*!< AXI peripheral ID1 register */
#define AXI_PERIPH_ID2 REG32(AXIIM + 0x00001FE8U) /*!< AXI peripheral ID2 register */
#define AXI_PERIPH_ID3 REG32(AXIIM + 0x00001FECU) /*!< AXI peripheral ID3 register */
#define AXI_COMP_ID0 REG32(AXIIM + 0x00001FF0U) /*!< AXI componet ID0 register */
#define AXI_COMP_ID1 REG32(AXIIM + 0x00001FF4U) /*!< AXI componet ID1 register */
#define AXI_COMP_ID2 REG32(AXIIM + 0x00001FF8U) /*!< AXI componet ID2 register */
#define AXI_COMP_ID3 REG32(AXIIM + 0x00001FFCU) /*!< AXI componet ID3 register */
#define AXI_MPXBM_ISS_CTL(mportx) REG32(AXIIM + 0x00002008U + 0x00001000U * (mportx)) /*!< AXI master port x bus matrix issuing functionality control register */
#define AXI_MP0BM_ISS_CTL REG32(AXIIM + 0x00002008U) /*!< AXI master port 0 bus matrix issuing functionality control register */
#define AXI_MP1BM_ISS_CTL REG32(AXIIM + 0x00003008U) /*!< AXI master port 1 bus matrix issuing functionality control register */
#define AXI_MP2BM_ISS_CTL REG32(AXIIM + 0x00004008U) /*!< AXI master port 2 bus matrix issuing functionality control register */
#define AXI_MP3BM_ISS_CTL REG32(AXIIM + 0x00005008U) /*!< AXI master port 3 bus matrix issuing functionality control register */
#define AXI_MP4BM_ISS_CTL REG32(AXIIM + 0x00006008U) /*!< AXI master port 4 bus matrix issuing functionality control register */
#define AXI_MP5BM_ISS_CTL REG32(AXIIM + 0x00007008U) /*!< AXI master port 5 bus matrix issuing functionality control register */
#define AXI_MP6BM_ISS_CTL REG32(AXIIM + 0x00008008U) /*!< AXI master port 6 bus matrix issuing functionality control register */
#define AXI_MP7BM_ISS_CTL REG32(AXIIM + 0x00009008U) /*!< AXI master port 7 bus matrix issuing functionality control register */
#define AXI_MPXBM_CTL(mportx) REG32(AXIIM + 0x00002024U + 0x00001000U * (mportx)) /*!< AXI master port x bus matrix functionality control register */
#define AXI_MP0BM_CTL REG32(AXIIM + 0x00002024U) /*!< AXI master port 0 bus matrix functionality control register */
#define AXI_MP1BM_CTL REG32(AXIIM + 0x00003024U) /*!< AXI master port 1 bus matrix functionality control register */
#define AXI_MP6BM_CTL REG32(AXIIM + 0x00008024U) /*!< AXI master port 6 bus matrix functionality control register */
#define AXI_MP7BM_CTL REG32(AXIIM + 0x00009024U) /*!< AXI master port 7 bus matrix functionality control register */
#define AXI_MPX_LB_CTL(mportx) REG32(AXIIM + 0x0000202CU + 0x00001000U * (mportx)) /*!< AXI master port x long burst functionality control register */
#define AXI_MP0_LB_CTL REG32(AXIIM + 0x0000202CU) /*!< AXI master port 0 long burst functionality control register */
#define AXI_MP1_LB_CTL REG32(AXIIM + 0x0000302CU) /*!< AXI master port 1 long burst functionality control register */
#define AXI_MPX_ISS_CTL(mportx) REG32(AXIIM + 0x00002108U + 0x00001000U * (mportx)) /*!< AXI master port x issuing functionality control register */
#define AXI_MP0_ISS_CTL REG32(AXIIM + 0x00002108U) /*!< AXI master port 0 issuing functionality control register */
#define AXI_MP1_ISS_CTL REG32(AXIIM + 0x00003108U) /*!< AXI master port 1 issuing functionality control register */
#define AXI_MP6_ISS_CTL REG32(AXIIM + 0x00008108U) /*!< AXI master port 6 issuing functionality control register */
#define AXI_MP7_ISS_CTL REG32(AXIIM + 0x00009108U) /*!< AXI master port 7 issuing functionality control register */
#define AXI_SPX_CTL(sportx) REG32(AXIIM + 0x00042024U + 0x00001000U * (sportx)) /*!< AXI slave port x functionality control register */
#define AXI_SP0_CTL REG32(AXIIM + 0x00042024U) /*!< AXI slave port 0 functionality control register */
#define AXI_SP2_CTL REG32(AXIIM + 0x00044024U) /*!< AXI slave port 2 functionality control register */
#define AXI_SPX_AHBISS_CTL(sportx) REG32(AXIIM + 0x00042028U + 0x00001000U * (sportx)) /*!< AXI slave port x AHB issuing functionality control register */
#define AXI_SP0_AHBISS_CTL REG32(AXIIM + 0x00042028U) /*!< AXI slave port 0 AHB issuing functionality control register */
#define AXI_SP2_AHBISS_CTL REG32(AXIIM + 0x00044028U) /*!< AXI slave port 2 AHB issuing functionality control register */
/* AXI slave port x = 0 to 5 */
#define AXI_SPX_RDQOS_CTL(sportx) REG32(AXIIM + 0x00042100U + 0x00001000U * (sportx)) /*!< AXI slave port x read QOS control register */
#define AXI_SPX_WRQOS_CTL(sportx) REG32(AXIIM + 0x00042104U + 0x00001000U * (sportx)) /*!< AXI slave port x write QOS control register */
#define AXI_SPX_ISS_CTL(sportx) REG32(AXIIM + 0x00042108U + 0x00001000U * (sportx)) /*!< AXI slave port x issuing functionality control register */
#define AXI_PERIPH_ID4_JEP106CCODE BITS(0,3) /*!< JEP106 continuation code */
#define AXI_PERIPH_ID4_4KBCNT BITS(4,7) /*!< register file size */
#define AXI_PERIPH_ID0_PARTNUM BITS(0,7) /*!< part number bit[7:0] */
#define AXI_PERIPH_ID1_PARTNUM BITS(0,3) /*!< part number bit[11:8] */
#define AXI_PERIPH_ID1_JEP106ID BITS(4,7) /*!< JEP106 identity[3:0] */
#define AXI_PERIPH_ID2_JEP106ID BITS(0,2) /*!< JEP106 identity[6:4] */
#define AXI_PERIPH_ID2_JEP106CF BIT(3) /*!< JEP106 code flag */
#define AXI_PERIPH_ID2_PARTREV BITS(4,7) /*!< part revision */
#define AXI_PERIPH_ID3_CUSTMOD BITS(0,3) /*!< customer modification[3:0] */
#define AXI_PERIPH_ID3_CUSTREV BITS(4,7) /*!< customer version */
#define AXI_COMP_ID0_PREAMB BITS(0,7) /*!< preamble bits[7:0] */
#define AXI_COMP_ID1_PREAMB BITS(0,3) /*!< preamble bits[11:8] */
#define AXI_COMP_ID1_CLASS BITS(4,7) /*!< component class */
#define AXI_COMP_ID2_PREAMB BITS(0,7) /*!< preamble bits[19:12] */
#define AXI_COMP_ID3_PREAMB BITS(0,7) /*!< preamble bits[27:20] */
#define AXI_MPXBM_ISS_CTL_RD_ISSOV BIT(0) /*!< override target read issuing function */
#define AXI_MPXBM_ISS_CTL_WR_ISSOV BIT(1) /*!< override target write issuing function */
#define AXI_MPXBM_CTL_BPDIS BIT(0) /*!< beats packing function disable configure */
#define AXI_MPX_LB_CTL_LBEN BIT(0) /*!< control long burst function */
#define AXI_MPX_ISS_CTL_RD_ISSOV BIT(0) /*!< override AMIB read issuing function */
#define AXI_MPX_ISS_CTL_WR_ISSOV BIT(1) /*!< override AMIB write issuing function */
#define AXI_SPX_CTL_TRANSALT BIT(0) /*!< slave port transaction alteration configure bit */
#define AXI_SPX_AHBISS_CTL_WR_AHB_ISSOV BIT(0) /*!< converts AHB-Lite write transaction to single beat AXI transaction */
#define AXI_SPX_AHBISS_CTL_RD_AHB_ISSOV BIT(1) /*!< converts AHB-Lite read transaction to single beat AXI transaction */
#define AXI_SPX_RDQOS_CTL_RDQOS BITS(0,3) /*!< slave port read channel QoS configure bits */
#define AXI_SPX_WRQOS_CTL_WRQOS BITS(0,3) /*!< slave port write channel QoS configure bits */
#define AXI_SPX_ISS_CTL_RD_ISSOV BIT(0) /*!< slave port override ASIB read issuing control bit */
#define AXI_SPX_ISS_CTL_WR_ISSOV BIT(1) /*!< slave port override ASIB write issuing control bit */
/* AXI master port select */
typedef enum
{
MASTER_PORT0 = 0U, /*!< AXI master port 0 */
MASTER_PORT1, /*!< AXI master port 1 */
MASTER_PORT2, /*!< AXI master port 2 */
MASTER_PORT3, /*!< AXI master port 3 */
MASTER_PORT4, /*!< AXI master port 4 */
MASTER_PORT5, /*!< AXI master port 5 */
MASTER_PORT6, /*!< AXI master port 6 */
MASTER_PORT7 /*!< AXI master port 7 */
} master_port_enum;
/* AXI slave port select */
typedef enum
{
SLAVE_PORT0 = 0U, /*!< AXI slave port 0 */
SLAVE_PORT1, /*!< AXI slave port 1 */
SLAVE_PORT2, /*!< AXI slave port 2 */
SLAVE_PORT3, /*!< AXI slave port 3 */
SLAVE_PORT4, /*!< AXI slave port 4 */
SLAVE_PORT5 /*!< AXI slave port 5 */
} slave_port_enum;
/* AXI master port x bus mutrix read issuing capability is set to 1, mportx: MASTER_PORTx (x=0..7) */
#define __AXI_MPXBM_READ_ISSOV_ENABLE(mportx) {AXI_MPXBM_ISS_CTL(mportx) |= AXI_MPXBM_ISS_CTL_RD_ISSOV}
/* AXI master port x bus mutrix read issuing capability is normal, mportx: MASTER_PORTx (x=0..7) */
#define __AXI_MPXBM_READ_ISSOV_DISABLE(mportx) {AXI_MPXBM_ISS_CTL(mportx) &= ~AXI_MPXBM_ISS_CTL_RD_ISSOV}
/* AXI master port x bus mutrix write issuing capability is set to 1, mportx: MASTER_PORTx (x=0..7) */
#define __AXI_MPXBM_WRITE_ISSOV_ENABLE(mportx) {AXI_MPXBM_ISS_CTL(mportx) |= AXI_MPXBM_ISS_CTL_WR_ISSOV}
/* AXI master port x bus mutrix write issuing capability is normal, mportx: MASTER_PORTx (x=0..7) */
#define __AXI_MPXBM_WRITE_ISSOV_DISABLE(mportx) {AXI_MPXBM_ISS_CTL(mportx) &= ~AXI_MPXBM_ISS_CTL_WR_ISSOV}
/* enable AXI master port x beats packing function, mportx: MASTER_PORTx (x=0,1,6,7) */
#define __AXI_MPXBM_BEATS_PACKING_ENABLE(mportx) {AXI_MPXBM_CTL(mportx) &= ~AXI_MPXBM_CTL_BPDIS}
/* disable AXI master port x beats packing function, mportx: MASTER_PORTx (x=0,1,6,7) */
#define __AXI_MPXBM_BEATS_PACKING_DISABLE(mportx) {AXI_MPXBM_CTL(mportx) |= AXI_MPXBM_CTL_BPDIS}
/* enable AXI master port long burst function, mportx: MASTER_PORTx (x=0,1) */
#define __AXI_MPX_LONG_BURST_ENABLE(mportx) {AXI_MPX_LB_CTL(mportx) |= AXI_MPX_LB_CTL_LBEN}
/* disable AXI master port x long burst function, mportx: MASTER_PORTx (x=0,1) */
#define __AXI_MPX_LONG_BURST_DISABLE(mportx) {AXI_MPX_LB_CTL(mportx) &= ~AXI_MPX_LB_CTL_LBEN}
/* force AMIB read issuing capability to 1, mportx: MASTER_PORTx (x=0,1,2,7) */
#define __AXI_MPX_READ_ISSOV_ENABLE(mportx) {AXI_MPX_ISS_CTL(mportx) |= AXI_MPX_ISS_CTL_RD_ISSOV}
/* AMIB read issuing capability is normal, mportx: MASTER_PORTx (x=0,1,2,7) */
#define __AXI_MPX_READ_ISSOV_DISABLE(mportx) {AXI_MPX_ISS_CTL(mportx) &= ~AXI_MPX_ISS_CTL_RD_ISSOV}
/* force AMIB write issuing capability to 1, mportx: MASTER_PORTx (x=0,1,2,7) */
#define __AXI_MPX_WRITE_ISSOV_ENABLE(mportx) {AXI_MPX_ISS_CTL(mportx) |= AXI_MPX_ISS_CTL_WR_ISSOV}
/* AMIB write issuing capability is normal, mportx: MASTER_PORTx (x=0,1,2,7) */
#define __AXI_MPX_WRITE_ISSOV_DISABLE(mportx) {AXI_MPX_ISS_CTL(mportx) &= ~AXI_MPX_ISS_CTL_WR_ISSOV}
/* enable AXI slave port transaction alteration function, mportx: SLAVE_PORTx (x=0,2) */
#define __AXI_SPX_TRANSACTION_ALTER_ENABLE(sportx) {AXI_SPX_CTL(sportx) |= AXI_SPX_CTL_TRANSALT}
/* AXI slave port transaction alteration function in normal operation, mportx: SLAVE_PORTx (x=0,2) */
#define __AXI_SPX_TRANSACTION_ALTER_DISABLE(sportx) {AXI_SPX_CTL(sportx) &= ~AXI_SPX_CTL_TRANSALT}
/* enable convert AHB-Lite write transaction to single beat AXI transaction, mportx: SLAVE_PORTx (x=0,2) */
#define __AXI_SPX_CONVERT_AHB_WR_TO_SINGLE_BEAT_TRANS_ENABLE(sportx) {AXI_SPX_AHBISS_CTL(sportx) |= AXI_SPX_AHBISS_CTL_WR_AHB_ISSOV}
/* disable convert AHB-Lite write transaction to single beat AXI transaction, mportx: SLAVE_PORTx (x=0,2) */
#define __AXI_SPX_CONVERT_AHB_WR_TO_SINGLE_BEAT_TRANS_DISABLE(sportx) {AXI_SPX_AHBISS_CTL(sportx) &= ~AXI_SPX_AHBISS_CTL_WR_AHB_ISSOV}
/* enable convert AHB-Lite read transaction to single beat AXI transaction, mportx: SLAVE_PORTx (x=0,2) */
#define __AXI_SPX_CONVERT_AHB_RD_TO_SINGLE_BEAT_TRANS_ENABLE(sportx) {AXI_SPX_AHBISS_CTL(sportx) |= AXI_SPX_AHBISS_CTL_RD_AHB_ISSOV}
/* disable convert AHB-Lite read transaction to single beat AXI transaction, mportx: SLAVE_PORTx (x=0,2) */
#define __AXI_SPX_CONVERT_AHB_RD_TO_SINGLE_BEAT_TRANS_DISABLE(sportx) {AXI_SPX_AHBISS_CTL(sportx) &= ~AXI_SPX_AHBISS_CTL_RD_AHB_ISSOV}
/* configure slave port read channel QoS, sportx: SLAVE_PORTx (x=0..5), priority: 0x0~0xF */
#define __AXI_SPX_READ_QOS_SET(sportx, priority) {AXI_SPX_RDQOS_CTL(sportx) |= ( (priority) & AXI_SPX_RDQOS_CTL_RDQOS)}
/* configure slave port write channel QoS, sportx: SLAVE_PORTx (x=0..5), priority: 0x0~0xF */
#define __AXI_SPX_WRITE_QOS_SET(sportx, priority) {AXI_SPX_WRQOS_CTL(sportx) |= ( (priority) & AXI_SPX_WRQOS_CTL_WRQOS)}
/* force ASIB read issuing capability to 1, sportx: SLAVE_PORTx (x=0..5) */
#define __AXI_SPX_READ_ISSOV_ENABLE(sportx) {AXI_SPX_ISS_CTL(sportx) |= AXI_SPX_ISS_CTL_RD_ISSOV}
/* ASIB read issuing capability in normal operation, mportx: SLAVE_PORTx (x=0..5) */
#define __AXI_SPX_READ_ISSOV_DISABLE(sportx) {AXI_SPX_ISS_CTL(sportx) &= ~AXI_SPX_ISS_CTL_RD_ISSOV}
/* force ASIB write issuing capability to 1, sportx: SLAVE_PORTx (x=0..5) */
#define __AXI_SPX_WRITE_ISSOV_ENABLE(sportx) {AXI_SPX_ISS_CTL(sportx) |= AXI_SPX_ISS_CTL_WD_ISSOV}
/* ASIB write issuing capability in normal operation, mportx: SLAVE_PORTx (x=0..5) */
#define __AXI_SPX_WRITE_ISSOV_DISABLE(sportx) {AXI_SPX_ISS_CTL(sportx) &= ~AXI_SPX_ISS_CTL_WD_ISSOV}
#endif /* GD32H7XX_AXIIM_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,322 @@
/*!
\file gd32h7xx_cau.h
\brief definitions for the CAU
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
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 GD32H7XX_CAU_H
#define GD32H7XX_CAU_H
#include "gd32h7xx.h"
/* CAU definitions */
#define CAU CAU_BASE /*!< CAU base address */
/* registers definitions */
#define CAU_CTL REG32(CAU + 0x00000000U) /*!< control register */
#define CAU_STAT0 REG32(CAU + 0x00000004U) /*!< status register 0 */
#define CAU_DI REG32(CAU + 0x00000008U) /*!< data input register */
#define CAU_DO REG32(CAU + 0x0000000CU) /*!< data output register */
#define CAU_DMAEN REG32(CAU + 0x00000010U) /*!< DMA enable register */
#define CAU_INTEN REG32(CAU + 0x00000014U) /*!< interrupt enable register */
#define CAU_STAT1 REG32(CAU + 0x00000018U) /*!< status register 1 */
#define CAU_INTF REG32(CAU + 0x0000001CU) /*!< interrupt flag register */
#define CAU_KEY0H REG32(CAU + 0x00000020U) /*!< key 0 high register */
#define CAU_KEY0L REG32(CAU + 0x00000024U) /*!< key 0 low register */
#define CAU_KEY1H REG32(CAU + 0x00000028U) /*!< key 1 high register */
#define CAU_KEY1L REG32(CAU + 0x0000002CU) /*!< key 1 low register */
#define CAU_KEY2H REG32(CAU + 0x00000030U) /*!< key 2 high register */
#define CAU_KEY2L REG32(CAU + 0x00000034U) /*!< key 2 low register */
#define CAU_KEY3H REG32(CAU + 0x00000038U) /*!< key 3 high register */
#define CAU_KEY3L REG32(CAU + 0x0000003CU) /*!< key 3 low register */
#define CAU_IV0H REG32(CAU + 0x00000040U) /*!< initial vector 0 high register */
#define CAU_IV0L REG32(CAU + 0x00000044U) /*!< initial vector 0 low register */
#define CAU_IV1H REG32(CAU + 0x00000048U) /*!< initial vector 1 high register */
#define CAU_IV1L REG32(CAU + 0x0000004CU) /*!< initial vector 1 low register */
#define CAU_GCMCCMCTXSx(x) REG32(CAU + 0x00000050U + (uint32_t)(4U * (x))) /*!< GCM or CCM mode context switch register, x = 0...7 */
#define CAU_GCMCTXSx(x) REG32(CAU + 0x00000070U + (uint32_t)(4U * (x))) /*!< GCM mode context switch register, x = 0...7 */
/* bits definitions */
/* CAU_CTL */
#define CAU_CTL_KEY_SEL BIT(0) /*!< key select */
#define CAU_CTL_CAUDIR BIT(2) /*!< algorithm direction */
#define CAU_CTL_ALGM (BITS(3,5) | BIT(19)) /*!< cryptographic algorithm mode */
#define CAU_CTL_DATAM BITS(6,7) /*!< data swapping selection */
#define CAU_CTL_KEYM BITS(8,9) /*!< key length selection when aes mode */
#define CAU_CTL_FFLUSH BIT(14) /*!< FIFO flush */
#define CAU_CTL_CAUEN BIT(15) /*!< cryptographic module enable */
#define CAU_CTL_GCM_CCMPH BITS(16,17) /*!< GCM CCM phase */
#define CAU_CTL_NBPILB BITS(20,23) /*!< number of bytes padding in last block */
/* CAU_STAT0 */
#define CAU_STAT0_IEM BIT(0) /*!< IN FIFO empty flag */
#define CAU_STAT0_INF BIT(1) /*!< IN FIFO not full flag */
#define CAU_STAT0_ONE BIT(2) /*!< OUT FIFO not empty flag */
#define CAU_STAT0_OFU BIT(3) /*!< OUT FIFO full flag */
#define CAU_STAT0_BUSY BIT(4) /*!< busy flag */
/* CAU_DI */
#define CAU_DI_DI BITS(0,31) /*!< data input */
/* CAU_DO */
#define CAU_DO_DO BITS(0,31) /*!< data output */
/* CAU_DMAEN */
#define CAU_DMAEN_DMAIEN BIT(0) /*!< IN FIFO DMA enable */
#define CAU_DMAEN_DMAOEN BIT(1) /*!< OUT FIFO DMA enable */
/* CAU_INTEN */
#define CAU_INTEN_IINTEN BIT(0) /*!< IN FIFO interrupt enable */
#define CAU_INTEN_OINTEN BIT(1) /*!< OUT FIFO interrupt enable */
/* CAU_STAT1 */
#define CAU_STAT1_ISTA BIT(0) /*!< flag set when there is less than 4 words in IN FIFO */
#define CAU_STAT1_OSTA BIT(1) /*!< flag set when there is one or more word in OUT FIFO */
/* CAU_INTF */
#define CAU_INTF_IINTF BIT(0) /*!< IN FIFO interrupt flag */
#define CAU_INTF_OINTF BIT(1) /*!< OUT FIFO interrupt flag */
/* CAU_KEYxH x=0..3 */
#define CAU_KEYXH_KEYXH BITS(0,31) /*!< the key for des, tdes, aes */
/* CAU_KEYxL x=0..3 */
#define CAU_KEYXL_KEYXL BITS(0,31) /*!< the key for des, tdes, aes */
/* CAU_IVxH x=0..1 */
#define CAU_IVXH_IVXH BITS(0,31) /*!< the initialization vector for des, tdes, aes */
/* CAU_IVxL x=0..1 */
#define CAU_IVXL_IVXL BITS(0,31) /*!< the initialization vector for des, tdes, aes */
/* constants definitions */
/* structure for keys initialization of the cau */
typedef struct {
uint32_t key_0_high; /*!< key 0 high */
uint32_t key_0_low; /*!< key 0 low */
uint32_t key_1_high; /*!< key 1 high */
uint32_t key_1_low; /*!< key 1 low */
uint32_t key_2_high; /*!< key 2 high */
uint32_t key_2_low; /*!< key 2 low */
uint32_t key_3_high; /*!< key 3 high */
uint32_t key_3_low; /*!< key 3 low */
} cau_key_parameter_struct;
/* structure for vectors initialization of the cau */
typedef struct {
uint32_t iv_0_high; /*!< init vector 0 high */
uint32_t iv_0_low; /*!< init vector 0 low */
uint32_t iv_1_high; /*!< init vector 1 high */
uint32_t iv_1_low; /*!< init vector 1 low */
} cau_iv_parameter_struct;
/* structure for cau context swapping */
typedef struct {
uint32_t ctl_config; /*!< current configuration */
uint32_t iv_0_high; /*!< init vector 0 high */
uint32_t iv_0_low; /*!< init vector 0 low */
uint32_t iv_1_high; /*!< init vector 1 high */
uint32_t iv_1_low; /*!< init vector 1 low */
uint32_t key_0_high; /*!< key 0 high */
uint32_t key_0_low; /*!< key 0 low */
uint32_t key_1_high; /*!< key 1 high */
uint32_t key_1_low; /*!< key 1 low */
uint32_t key_2_high; /*!< key 2 high */
uint32_t key_2_low; /*!< key 2 low */
uint32_t key_3_high; /*!< key 3 high */
uint32_t key_3_low; /*!< key 3 low */
uint32_t gcmccmctxs[8]; /*!< GCM or CCM mode context switch */
uint32_t gcmctxs[8]; /*!< GCM mode context switch */
} cau_context_parameter_struct;
/* structure for encrypt and decrypt parameters */
typedef struct {
uint32_t alg_dir; /*!< algorithm directory */
uint8_t *key; /*!< key */
uint32_t key_size; /*!< key size in bytes */
uint8_t *iv; /*!< initialization vector */
uint32_t iv_size; /*!< iv size in bytes */
uint8_t *input; /*!< input data */
uint32_t in_length; /*!< input data length in bytes */
uint8_t *aad; /*!< additional authentication data */
uint32_t aad_size; /*!< aad size */
} cau_parameter_struct;
/* cau_ctl register value */
#define CAU_ENCRYPT ((uint32_t)0x00000000U) /*!< encrypt */
#define CAU_DECRYPT CAU_CTL_CAUDIR /*!< decrypt */
#define CTL_ALGM(regval) ((BITS(3,5) & ((uint32_t)(regval) << 3U)) | \
(BIT(19) & ((uint32_t)(regval) << 16U))) /*!< write value to CAU_CTL_ALGM bit field */
#define CAU_MODE_TDES_ECB CTL_ALGM(0) /*!< TDES-ECB (3DES Electronic codebook) */
#define CAU_MODE_TDES_CBC CTL_ALGM(1) /*!< TDES-CBC (3DES Cipher block chaining) */
#define CAU_MODE_DES_ECB CTL_ALGM(2) /*!< DES-ECB (simple DES Electronic codebook) */
#define CAU_MODE_DES_CBC CTL_ALGM(3) /*!< DES-CBC (simple DES Cipher block chaining) */
#define CAU_MODE_AES_ECB CTL_ALGM(4) /*!< AES-ECB (AES Electronic codebook) */
#define CAU_MODE_AES_CBC CTL_ALGM(5) /*!< AES-CBC (AES Cipher block chaining) */
#define CAU_MODE_AES_CTR CTL_ALGM(6) /*!< AES-CTR (AES counter mode) */
#define CAU_MODE_AES_KEY CTL_ALGM(7) /*!< AES decryption key preparation mode */
#define CAU_MODE_AES_GCM CTL_ALGM(8) /*!< AES-GCM (AES Galois/counter mode) */
#define CAU_MODE_AES_CCM CTL_ALGM(9) /*!< AES-CCM (AES combined cipher machine mode) */
#define CAU_MODE_AES_CFB CTL_ALGM(10) /*!< AES-CFB (cipher feedback mode) */
#define CAU_MODE_AES_OFB CTL_ALGM(11) /*!< AES-OFB (output feedback mode) */
#define CTL_DATAM(regval) (BITS(6,7) & ((uint32_t)(regval) << 6U)) /*!< write value to CAU_CTL_DATAM bit field */
#define CAU_SWAPPING_32BIT CTL_DATAM(0) /*!< no swapping */
#define CAU_SWAPPING_16BIT CTL_DATAM(1) /*!< half-word swapping */
#define CAU_SWAPPING_8BIT CTL_DATAM(2) /*!< bytes swapping */
#define CAU_SWAPPING_1BIT CTL_DATAM(3) /*!< bit swapping */
#define CAU_KEY 0x00000000U /*!< use the key from CAU register */
#define CAU_EFUSE_KEY 0x00000001U /*!< use the key from EFUSE */
#define CTL_KEYM(regval) (BITS(8,9) & ((uint32_t)(regval) << 8U)) /*!< write value to CAU_CTL_KEYM bit field */
#define CAU_KEYSIZE_128BIT CTL_KEYM(0) /*!< 128 bit key length */
#define CAU_KEYSIZE_192BIT CTL_KEYM(1) /*!< 192 bit key length */
#define CAU_KEYSIZE_256BIT CTL_KEYM(2) /*!< 256 bit key length */
#define CTL_GCM_CCMPH(regval) (BITS(16,17) & ((uint32_t)(regval) << 16U)) /*!< write value to CAU_CTL_GCM_CCMPH bit field */
#define CAU_PREPARE_PHASE CTL_GCM_CCMPH(0) /*!< prepare phase */
#define CAU_AAD_PHASE CTL_GCM_CCMPH(1) /*!< AAD phase */
#define CAU_ENCRYPT_DECRYPT_PHASE CTL_GCM_CCMPH(2) /*!< encryption/decryption phase */
#define CAU_TAG_PHASE CTL_GCM_CCMPH(3) /*!< tag phase */
#define CAU_PADDING_BYTES(regval) (BITS(20, 23) & ((uint32_t)(regval) << 20U))
/* cau_stat0 register value */
#define CAU_FLAG_INFIFO_EMPTY CAU_STAT0_IEM /*!< IN FIFO empty */
#define CAU_FLAG_INFIFO_NO_FULL CAU_STAT0_INF /*!< IN FIFO is not full */
#define CAU_FLAG_OUTFIFO_NO_EMPTY CAU_STAT0_ONE /*!< OUT FIFO not empty */
#define CAU_FLAG_OUTFIFO_FULL CAU_STAT0_OFU /*!< OUT FIFO is full */
#define CAU_FLAG_BUSY CAU_STAT0_BUSY /*!< the CAU core is busy */
/* cau_dmaen register value */
#define CAU_DMA_INFIFO CAU_DMAEN_DMAIEN /*!< DMA input enable */
#define CAU_DMA_OUTFIFO CAU_DMAEN_DMAOEN /*!< DMA output enable */
/* cau_inten register value */
#define CAU_INT_INFIFO CAU_INTEN_IINTEN /*!< IN FIFO Interrupt */
#define CAU_INT_OUTFIFO CAU_INTEN_OINTEN /*!< OUT FIFO Interrupt */
/* cau_stat1 register value */
#define CAU_FLAG_INFIFO (CAU_STAT1_ISTA | BIT(31)) /*!< IN FIFO flag status */
#define CAU_FLAG_OUTFIFO (CAU_STAT1_OSTA | BIT(31)) /*!< OUT FIFO flag status */
/* cau_intf register value */
#define CAU_INT_FLAG_INFIFO CAU_INTF_IINTF /*!< IN FIFO interrupt status */
#define CAU_INT_FLAG_OUTFIFO CAU_INTF_OINTF /*!< OUT FIFO interrupt status */
/* function declarations */
/* initialization functions */
/* reset the CAU peripheral */
void cau_deinit(void);
/* initialize the CAU encrypt and decrypt parameter struct with the default values */
void cau_struct_para_init(cau_parameter_struct *cau_parameter);
/* initialize the key parameter struct with the default values */
void cau_key_struct_para_init(cau_key_parameter_struct *key_initpara);
/* initialize the vectors parameter struct with the default values */
void cau_iv_struct_para_init(cau_iv_parameter_struct *iv_initpara);
/* initialize the context parameter struct with the default values */
void cau_context_struct_para_init(cau_context_parameter_struct *cau_context);
/* configuration functions */
/* enable the CAU peripheral */
void cau_enable(void);
/* disable the CAU peripheral */
void cau_disable(void);
/* enable the CAU DMA interface */
void cau_dma_enable(uint32_t dma_req);
/* disable the CAU DMA interface */
void cau_dma_disable(uint32_t dma_req);
/* initialize the CAU peripheral */
void cau_init(uint32_t alg_dir, uint32_t algo_mode, uint32_t swapping);
/* configure key selection */
void cau_aes_key_select(uint32_t key_selection);
/* configure key size if use AES algorithm */
void cau_aes_keysize_config(uint32_t key_size);
/* initialize the key parameters */
void cau_key_init(cau_key_parameter_struct *key_initpara);
/* initialize the vectors parameters */
void cau_iv_init(cau_iv_parameter_struct *iv_initpara);
/* configure phase */
void cau_phase_config(uint32_t phase);
/* flush the IN and OUT FIFOs */
void cau_fifo_flush(void);
/* return whether CAU peripheral is enabled or disabled */
ControlStatus cau_enable_state_get(void);
/* read and write functions */
/* write data to the IN FIFO */
void cau_data_write(uint32_t data);
/* return the last data entered into the output FIFO */
uint32_t cau_data_read(void);
/* context switch functions */
/* save context before context switching */
void cau_context_save(cau_context_parameter_struct *cau_context, cau_key_parameter_struct *key_initpara);
/* restore context after context switching */
void cau_context_restore(cau_context_parameter_struct *cau_context);
/* encrypt and decrypt functions */
/* encrypt and decrypt using AES in ECB mode */
ErrStatus cau_aes_ecb(cau_parameter_struct *cau_parameter, uint8_t *output);
/* encrypt and decrypt using AES in CBC mode */
ErrStatus cau_aes_cbc(cau_parameter_struct *cau_parameter, uint8_t *output);
/* encrypt and decrypt using AES in CTR mode */
ErrStatus cau_aes_ctr(cau_parameter_struct *cau_parameter, uint8_t *output);
/* encrypt and decrypt using AES in CFB mode */
ErrStatus cau_aes_cfb(cau_parameter_struct *cau_parameter, uint8_t *output);
/* encrypt and decrypt using AES in OFB mode */
ErrStatus cau_aes_ofb(cau_parameter_struct *cau_parameter, uint8_t *output);
/* encrypt and decrypt using AES in GCM mode */
ErrStatus cau_aes_gcm(cau_parameter_struct *cau_parameter, uint8_t *output, uint8_t *tag);
/* encrypt and decrypt using AES in CCM mode */
ErrStatus cau_aes_ccm(cau_parameter_struct *cau_parameter, uint8_t *output, uint8_t tag[], uint32_t tag_size, uint8_t aad_buf[]);
/* encrypt and decrypt using TDES in ECB mode */
ErrStatus cau_tdes_ecb(cau_parameter_struct *cau_parameter, uint8_t *output);
/* encrypt and decrypt using TDES in CBC mode */
ErrStatus cau_tdes_cbc(cau_parameter_struct *cau_parameter, uint8_t *output);
/* encrypt and decrypt using DES in ECB mode */
ErrStatus cau_des_ecb(cau_parameter_struct *cau_parameter, uint8_t *output);
/* encrypt and decrypt using DES in CBC mode */
ErrStatus cau_des_cbc(cau_parameter_struct *cau_parameter, uint8_t *output);
/* interrupt & flag functions */
/* get the CAU flag status */
FlagStatus cau_flag_get(uint32_t flag);
/* enable the CAU interrupts */
void cau_interrupt_enable(uint32_t interrupt);
/* disable the CAU interrupts */
void cau_interrupt_disable(uint32_t interrupt);
/* get the interrupt flag */
FlagStatus cau_interrupt_flag_get(uint32_t int_flag);
#endif /* GD32H7XX_CAU_H */
@@ -0,0 +1,219 @@
/*!
\file gd32h7xx_cmp.h
\brief definitions for the CMP
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
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 GD32H7XX_CMP_H
#define GD32H7XX_CMP_H
#include "gd32h7xx.h"
/* CMP definitions */
#define CMP CMP_BASE /*!< CMP base address */
/* registers definitions */
#define CMP_STAT REG32((CMP) + 0x00000000U) /*!< CMP status register */
#define CMP_IFC REG32((CMP) + 0x00000004U) /*!< CMP interrupt flag clear register */
#define CMP_SR REG32((CMP) + 0x00000008U) /*!< CMP alternate select register */
#define CMP0_CS REG32((CMP) + 0x0000000CU) /*!< CMP0 control and status register */
#define CMP1_CS REG32((CMP) + 0x00000010U) /*!< CMP1 control and status register */
/* bits definitions */
/* CMP_STAT */
#define CMP_STAT_CMP0O BIT(0) /*!< CMP0 output */
#define CMP_STAT_CMP1O BIT(1) /*!< CMP1 output */
#define CMP_STAT_CMP0IF BIT(16) /*!< CMP0 interrupt flag */
#define CMP_STAT_CMP1IF BIT(17) /*!< CMP1 interrupt flag */
/* CMP_IFC */
#define CMP_IFC_CMP0IC BIT(16) /*!< CMP0 interrupt flag clear */
#define CMP_IFC_CMP1IC BIT(17) /*!< CMP1 interrupt flag clear */
/* CMP_SR */
#define CMP_SR_AFSE_PA6 BIT(0) /*!< PA6 alternate function select for CMPx_OUT */
#define CMP_SR_AFSE_PA8 BIT(1) /*!< PA8 alternate function select for CMPx_OUT */
#define CMP_SR_AFSE_PB12 BIT(2) /*!< PB12 alternate function select for CMPx_OUT */
#define CMP_SR_AFSE_PE6 BIT(3) /*!< PE6 alternate function select for CMPx_OUT */
#define CMP_SR_AFSE_PE15 BIT(4) /*!< PE15 alternate function select for CMPx_OUT */
#define CMP_SR_AFSE_PG2 BIT(5) /*!< PG2 alternate function select for CMPx_OUT */
#define CMP_SR_AFSE_PG3 BIT(6) /*!< PG3 alternate function select for CMPx_OUT */
#define CMP_SR_AFSE_PG4 BIT(7) /*!< PG4 alternate function select for CMPx_OUT */
#define CMP_SR_AFSE_PK0 BIT(8) /*!< PK0 alternate function select for CMPx_OUT */
#define CMP_SR_AFSE_PK1 BIT(9) /*!< PK1 alternate function select for CMPx_OUT */
#define CMP_SR_AFSE_PK2 BIT(10) /*!< PK2 alternate function select for CMPx_OUT */
/* CMPx_CS */
#define CMP_CS_CMPXEN BIT(0) /*!< CMPx enable */
#define CMP_CS_CMPXBEN BIT(1) /*!< CMPx scaler bridge enable bit */
#define CMP_CS_CMPXSEN BIT(2) /*!< CMPx voltage scaler enable bit */
#define CMP_CS_CMPXPL BIT(3) /*!< CMPx output polarity */
#define CMP_CS_WNDEN BIT(4) /*!< CMP Window mode enable */
#define CMP_CS_CMPXINTEN BIT(6) /*!< CMPx interrupt enable */
#define CMP_CS_CMPXHST BITS(8,9) /*!< CMPx hysteresis */
#define CMP_CS_CMPXM BITS(12,13) /*!< CMPx mode */
#define CMP_CS_CMPXMISEL BITS(16,18) /*!< CMP_IM input selection */
#define CMP_CS_CMPXPSEL BIT(20) /*!< CMP_IP input selection */
#define CMP_CS_CMPXBLK BITS(24,27) /*!< CMPx output blanking source */
#define CMP_CS_CMPXLK BIT(31) /*!< CMPx lock */
/* constants definitions */
/* CMP units */
typedef enum{
CMP0, /*!< comparator 0 */
CMP1 /*!< comparator 1 */
}cmp_enum;
/* CMP operating mode */
#define CS_CMPXM(regval) (BITS(12,13) & ((uint32_t)(regval) << 12U))
#define CMP_MODE_HIGHSPEED CS_CMPXM(0) /*!< CMP mode high speed */
#define CMP_MODE_MIDDLESPEED CS_CMPXM(1) /*!< CMP mode middle speed */
#define CMP_MODE_VERYLOWSPEED CS_CMPXM(3) /*!< CMP mode very low speed */
/* CMP hysteresis */
#define CS_CMPXHST(regval) (BITS(8,9) & ((uint32_t)(regval) << 8U))
#define CMP_HYSTERESIS_NO CS_CMPXHST(0) /*!< CMP output no hysteresis */
#define CMP_HYSTERESIS_LOW CS_CMPXHST(1) /*!< CMP output low hysteresis */
#define CMP_HYSTERESIS_MIDDLE CS_CMPXHST(2) /*!< CMP output middle hysteresis */
#define CMP_HYSTERESIS_HIGH CS_CMPXHST(3) /*!< CMP output high hysteresis */
/* CMP inverting input */
#define CS_CMPXMSEL(regval) (BITS(16,18) & ((uint32_t)(regval) << 16U))
#define CMP_INVERTING_INPUT_1_4VREFINT CS_CMPXMSEL(0) /*!< CMP inverting input 1/4 Vrefint */
#define CMP_INVERTING_INPUT_1_2VREFINT CS_CMPXMSEL(1) /*!< CMP inverting input 1/2 Vrefint */
#define CMP_INVERTING_INPUT_3_4VREFINT CS_CMPXMSEL(2) /*!< CMP inverting input 3/4 Vrefint */
#define CMP_INVERTING_INPUT_VREFINT CS_CMPXMSEL(3) /*!< CMP inverting input Vrefint */
#define CMP_INVERTING_INPUT_DAC0_OUT0 CS_CMPXMSEL(4) /*!< CMP inverting input DAC0_OUT0 */
#define CMP_INVERTING_INPUT_DAC0_OUT1 CS_CMPXMSEL(5) /*!< CMP inverting input DAC0_OUT1 */
#define CMP_INVERTING_INPUT_PB1_PE10 CS_CMPXMSEL(6) /*!< CMP inverting input PB1 for CMP0 or PE10 for CMP1 */
#define CMP_INVERTING_INPUT_PC4_PE7 CS_CMPXMSEL(7) /*!< CMP inverting input PC4 for CMP0 or PE7 for CMP1 */
/* CMP noninverting input*/
#define CS_CMPXPSEL(regval) (BIT(20) & ((uint32_t)(regval) << 20U))
#define CMP_NONINVERTING_INPUT_PB0_PE9 CS_CMPXPSEL(0) /*!< CMP noninverting input PB0 for CMP0 or PE9 for CMP1 */
#define CMP_NONINVERTING_INPUT_PB2_PE11 CS_CMPXPSEL(1) /*!< CMP noninverting input PB2 for CMP0 or PE11 for CMP1 */
/* CMP output polarity*/
#define CS_CMPXPL(regval) (BIT(3) & ((uint32_t)(regval) << 3U))
#define CMP_OUTPUT_POLARITY_NONINVERTED CS_CMPXPL(0) /*!< CMP output not inverted */
#define CMP_OUTPUT_POLARITY_INVERTED CS_CMPXPL(1) /*!< CMP output inverted */
/* CMP blanking suorce */
#define CS_CMPXBLK(regval) (BITS(24,27) & ((uint32_t)(regval) << 24U))
#define CMP_BLANKING_NONE CS_CMPXBLK(0) /*!< CMP no blanking source */
#define CMP_BLANKING_TIMER0_OC0 CS_CMPXBLK(1) /*!< CMP TIMER0_CH0 output compare signal selected as blanking source */
#define CMP_BLANKING_TIMER1_OC2 CS_CMPXBLK(2) /*!< CMP TIMER1_CH2 output compare signal selected as blanking source */
#define CMP_BLANKING_TIMER2_OC2 CS_CMPXBLK(3) /*!< CMP TIMER2_CH2 output compare signal selected as blanking source */
#define CMP_BLANKING_TIMER2_OC3 CS_CMPXBLK(4) /*!< CMP TIMER2_CH3 output compare signal selected as blanking source */
#define CMP_BLANKING_TIMER7_OC0 CS_CMPXBLK(5) /*!< CMP TIMER7_CH0 output compare signal selected as blanking source */
#define CMP_BLANKING_TIMER14_OC0 CS_CMPXBLK(6) /*!< CMP TIMER14_CH0 output compare signal selected as blanking source */
/* comparator output level */
#define CMP_OUTPUTLEVEL_HIGH ((uint32_t)0x00000001U) /*!< comparator output high */
#define CMP_OUTPUTLEVEL_LOW ((uint32_t)0x00000000U) /*!< comparator output low */
/* CMP alternate output ports */
#define CMP_AFSE_GPIO_PA6 CMP_SR_AFSE_PA6 /*!< CMP alternate GPIO PA6 */
#define CMP_AFSE_GPIO_PA8 CMP_SR_AFSE_PA8 /*!< CMP alternate GPIO PA8 */
#define CMP_AFSE_GPIO_PB12 CMP_SR_AFSE_PB12 /*!< CMP alternate GPIO PB12 */
#define CMP_AFSE_GPIO_PE6 CMP_SR_AFSE_PE6 /*!< CMP alternate GPIO PE6 */
#define CMP_AFSE_GPIO_PE15 CMP_SR_AFSE_PE15 /*!< CMP alternate GPIO PE15 */
#define CMP_AFSE_GPIO_PG2 CMP_SR_AFSE_PG2 /*!< CMP alternate GPIO PG2 */
#define CMP_AFSE_GPIO_PG3 CMP_SR_AFSE_PG3 /*!< CMP alternate GPIO PG3 */
#define CMP_AFSE_GPIO_PG4 CMP_SR_AFSE_PG4 /*!< CMP alternate GPIO PG4 */
#define CMP_AFSE_GPIO_PK0 CMP_SR_AFSE_PK0 /*!< CMP alternate GPIO PK0 */
#define CMP_AFSE_GPIO_PK1 CMP_SR_AFSE_PK1 /*!< CMP alternate GPIO PK1*/
#define CMP_AFSE_GPIO_PK2 CMP_SR_AFSE_PK2 /*!< CMP alternate GPIO PK2 */
/* CMP flag definitions */
#define CMP_FLAG_COMPARE CMP_STAT_CMP0IF /*!< CMP compare flag */
/* CMP interrupt definitions */
#define CMP_INT_COMPARE CMP_CS_CMPXINTEN /*!< CMP compare interrupt */
/* CMP interrupt flag */
#define CMP_INT_FLAG_COMPARE CMP_STAT_CMP0IF /*!< CMP interrupt flag */
/* function declarations */
/* initialization functions */
/* CMP deinit */
void cmp_deinit(cmp_enum cmp_periph);
/* CMP mode init */
void cmp_mode_init(cmp_enum cmp_periph, uint32_t operating_mode, uint32_t inverting_input, uint32_t output_hysteresis);
/* CMP noninverting input select */
void cmp_noninverting_input_select(cmp_enum cmp_periph, uint32_t noninverting_input);
/* CMP output init */
void cmp_output_init(cmp_enum cmp_periph, uint32_t output_polarity);
/* config comparator output port */
void cmp_output_mux_config(cmp_enum cmp_periph, uint32_t cmp_output_sel);
/* CMP output blanking function init */
void cmp_blanking_init(cmp_enum cmp_periph,uint32_t blanking_source_selection);
/* enable functions */
/* enable CMP */
void cmp_enable(cmp_enum cmp_periph);
/* disable CMP */
void cmp_disable(cmp_enum cmp_periph);
/* enable the window mode */
void cmp_window_enable(void);
/* disable the window mode */
void cmp_window_disable(void);
/* lock the CMP */
void cmp_lock_enable(cmp_enum cmp_periph);
/* enable the voltage scaler */
void cmp_voltage_scaler_enable(cmp_enum cmp_periph);
/* disable the voltage scaler */
void cmp_voltage_scaler_disable(cmp_enum cmp_periph);
/* enable the scaler bridge */
void cmp_scaler_bridge_enable(cmp_enum cmp_periph);
/* disable the scaler bridge */
void cmp_scaler_bridge_disable(cmp_enum cmp_periph);
/* get state related functions */
/* get output level */
uint32_t cmp_output_level_get(cmp_enum cmp_periph);
/* flag and interrupt functions */
/* get CMP flag */
FlagStatus cmp_flag_get(cmp_enum cmp_periph, uint32_t flag);
/* clear CMP flag */
void cmp_flag_clear(cmp_enum cmp_periph, uint32_t flag);
/* enable CMP interrupt */
void cmp_interrupt_enable(cmp_enum cmp_periph, uint32_t interrupt);
/* disable CMP interrupt */
void cmp_interrupt_disable(cmp_enum cmp_periph, uint32_t interrupt);
/* get CMP interrupt flag */
FlagStatus cmp_interrupt_flag_get(cmp_enum cmp_periph, uint32_t flag);
/* clear CMP interrupt flag */
void cmp_interrupt_flag_clear(cmp_enum cmp_periph, uint32_t flag);
#endif /* GD32H7XX_CMP_H */
@@ -0,0 +1,106 @@
/*!
\file gd32h7xx_cpdm.h
\brief definitions for the CPDM
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
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 GD32H7XX_CPDM_H
#define GD32H7XX_CPDM_H
#include "gd32h7xx.h"
/* CPDM definitions */
#define CPDM_SDIO0 (SDIO0 + 0x00001000U) /*!< SDIO0 CPDM base address */
#define CPDM_SDIO1 (SDIO1 + 0x00000400U) /*!< SDIO1 CPDM base address */
/* registers definitions */
#define CPDM_CTL(cpdmx) REG32((cpdmx) + 0x00000000U) /*!< CPDM control register */
#define CPDM_CFG(cpdmx) REG32((cpdmx) + 0x00000004U) /*!< CPDM configuration register */
/* bits definitions */
/* CPDM_CTL */
#define CPDM_CTL_CPDMEN BIT(0) /*!< CPDM enable */
#define CPDM_CTL_DLSEN BIT(1) /*!< CPDM delay line sample module enable */
/* CPDM_CFG */
#define CPDM_CFG_CPSEL BITS(0,3) /*!< select the phase of the output clock */
#define CPDM_CFG_DLSTCNT BITS(8,14) /*!< define a delay step count for a unit delay UNIT */
#define CPDM_CFG_DLLEN BITS(16,27) /*!< delay line length */
#define CPDM_CFG_DLLENF BIT(31) /*!< valid mark of delay line length */
/* constants definitions */
/* output clock phase selection enum definition */
typedef enum
{
CPDM_OUTPUT_PHASE_SELECTION_0 = 0, /*!< output clock phase = input clock */
CPDM_OUTPUT_PHASE_SELECTION_1, /*!< output clock phase = input clock + 1 * UNIT delay */
CPDM_OUTPUT_PHASE_SELECTION_2, /*!< output clock phase = input clock + 2 * UNIT delay */
CPDM_OUTPUT_PHASE_SELECTION_3, /*!< output clock phase = input clock + 3 * UNIT delay */
CPDM_OUTPUT_PHASE_SELECTION_4, /*!< output clock phase = input clock + 4 * UNIT delay */
CPDM_OUTPUT_PHASE_SELECTION_5, /*!< output clock phase = input clock + 5 * UNIT delay */
CPDM_OUTPUT_PHASE_SELECTION_6, /*!< output clock phase = input clock + 6 * UNIT delay */
CPDM_OUTPUT_PHASE_SELECTION_7, /*!< output clock phase = input clock + 7 * UNIT delay */
CPDM_OUTPUT_PHASE_SELECTION_8, /*!< output clock phase = input clock + 8 * UNIT delay */
CPDM_OUTPUT_PHASE_SELECTION_9, /*!< output clock phase = input clock + 9 * UNIT delay */
CPDM_OUTPUT_PHASE_SELECTION_10, /*!< output clock phase = input clock + 10 * UNIT delay */
CPDM_OUTPUT_PHASE_SELECTION_11, /*!< output clock phase = input clock + 11 * UNIT delay */
CPDM_OUTPUT_PHASE_SELECTION_12, /*!< output clock phase = input clock + 12 * UNIT delay */
}cpdm_output_phase_enum;
#define CPDM_MAX_DELAY_STEP_COUNT ((uint32_t)0x0000007FU) /*!< max UNIT value */
#define CPDM_MAX_PHASE ((uint32_t)0x0000000CU) /*!< max select value of the phase */
/* function declarations */
/* deinitialization and initialization functions */
/* enable CPDM */
void cpdm_enable(uint32_t cpdm_periph);
/* disable CPDM */
void cpdm_disable(uint32_t cpdm_periph);
/* enable CPDM delay line sample module */
void cpdm_delayline_sample_enable(uint32_t cpdm_periph);
/* disable CPDM delay line sample module */
void cpdm_delayline_sample_disable(uint32_t cpdm_periph);
/* output clock configuration functions */
/* select CPDM output clock phase */
void cpdm_output_clock_phase_select(uint32_t cpdm_periph, cpdm_output_phase_enum output_clock_phase);
/* configure CPDM delay step */
void cpdm_delay_step_config(uint32_t cpdm_periph, uint8_t delay_step);
/* get delay line length valid flag */
FlagStatus cpdm_delayline_length_valid_flag_get(uint32_t cpdm_periph);
/* get delay line length */
uint16_t cpdm_delayline_length_get(uint32_t cpdm_periph);
/* clock output function */
/* configure CPDM clock output */
ErrStatus cpdm_clock_output(uint32_t cpdm_periph, cpdm_output_phase_enum output_clock_phase);
#endif /* GD32H7XX_CPDM_H */
@@ -0,0 +1,122 @@
/*!
\file gd32h7xx_crc.h
\brief definitions for the CRC
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
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 GD32H7XX_CRC_H
#define GD32H7XX_CRC_H
#include "gd32h7xx.h"
/* CRC definitions */
#define CRC CRC_BASE /*!< CRC bsae address */
/* registers definitions */
#define CRC_DATA REG32((CRC) + 0x00000000U) /*!< CRC data register */
#define CRC_FDATA REG32((CRC) + 0x00000004U) /*!< CRC free data register */
#define CRC_CTL REG32((CRC) + 0x00000008U) /*!< CRC control register */
#define CRC_IDATA REG32((CRC) + 0x00000010U) /*!< CRC initialization data register */
#define CRC_POLY REG32((CRC) + 0x00000014U) /*!< CRC polynomial register */
/* bits definitions */
/* CRC_DATA */
#define CRC_DATA_DATA BITS(0,31) /*!< CRC data bits */
/* CRC_FDATA */
#define CRC_FDATA_FDATA BITS(0,7) /*!< CRC free data bits */
/* CRC_CTL */
#define CRC_CTL_RST BIT(0) /*!< CRC reset bit */
#define CRC_CTL_PS BITS(3,4) /*!< size of polynomial function bits */
#define CRC_CTL_REV_I BITS(5,6) /*!< input data reverse function bits */
#define CRC_CTL_REV_O BIT(7) /*!< output data reverse function bit */
/* CRC_INIT */
#define CRC_IDATA_IDATA BITS(0,31) /*!< CRC initialization data bits */
/* CRC_POLY */
#define CRC_POLY_POLY BITS(0,31) /*!< CRC polynomial value bits */
/* constants definitions */
/* size of polynomial function */
#define CTL_PS(regval) (BITS(3,4) & ((regval) << 3U))
#define CRC_CTL_PS_32 CTL_PS(0) /*!< 32-bit polynomial for CRC calculation */
#define CRC_CTL_PS_16 CTL_PS(1) /*!< 16-bit polynomial for CRC calculation */
#define CRC_CTL_PS_8 CTL_PS(2) /*!< 8-bit polynomial for CRC calculation */
#define CRC_CTL_PS_7 CTL_PS(3) /*!< 7-bit polynomial for CRC calculation */
/* input data reverse function */
#define CTL_REV_I(regval) (BITS(5,6) & ((regval) << 5U))
#define CRC_INPUT_DATA_NOT CTL_REV_I(0) /*!< input data not reverse */
#define CRC_INPUT_DATA_BYTE CTL_REV_I(1) /*!< input data reversed by byte type */
#define CRC_INPUT_DATA_HALFWORD CTL_REV_I(2) /*!< input data reversed by half-word type */
#define CRC_INPUT_DATA_WORD CTL_REV_I(3) /*!< input data reversed by word type */
/* input data format */
#define INPUT_FORMAT_WORD 0U /*!< input data in word format */
#define INPUT_FORMAT_HALFWORD 1U /*!< input data in half-word format */
#define INPUT_FORMAT_BYTE 2U /*!< input data in byte format */
/* function declarations */
/* deinit CRC calculation unit */
void crc_deinit(void);
/* enable the reverse operation of output data */
void crc_reverse_output_data_enable(void);
/* disable the reverse operation of output data */
void crc_reverse_output_data_disable(void);
/* reset data register to the value of initialization data register */
void crc_data_register_reset(void);
/* read the data register */
uint32_t crc_data_register_read(void);
/* read the free data register */
uint8_t crc_free_data_register_read(void);
/* write the free data register */
void crc_free_data_register_write(uint8_t free_data);
/* write the initial value register */
void crc_init_data_register_write(uint32_t init_data);
/* configure the CRC input data function */
void crc_input_data_reverse_config(uint32_t data_reverse);
/* configure the CRC size of polynomial function */
void crc_polynomial_size_set(uint32_t poly_size);
/* configure the CRC polynomial value function */
void crc_polynomial_set(uint32_t poly);
/* CRC calculate single data */
uint32_t crc_single_data_calculate(uint32_t sdata, uint8_t data_format);
/* CRC calculate a data array */
uint32_t crc_block_data_calculate(void *array, uint32_t size, uint8_t data_format);
#endif /* GD32H7XX_CRC_H */
@@ -0,0 +1,184 @@
/*!
\file gd32h7xx_ctc.h
\brief definitions for the CTC
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
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 GD32H7XX_CTC_H
#define GD32H7XX_CTC_H
#include "gd32h7xx.h"
/* CTC definitions */
#define CTC CTC_BASE
/* registers definitions */
#define CTC_CTL0 REG32((CTC) + 0x00U) /*!< CTC control register 0 */
#define CTC_CTL1 REG32((CTC) + 0x04U) /*!< CTC control register 1 */
#define CTC_STAT REG32((CTC) + 0x08U) /*!< CTC status register */
#define CTC_INTC REG32((CTC) + 0x0CU) /*!< CTC interrupt clear register */
/* bits definitions */
/* CTC_CTL0 */
#define CTC_CTL0_CKOKIE BIT(0) /*!< clock trim OK(CKOKIF) interrupt enable */
#define CTC_CTL0_CKWARNIE BIT(1) /*!< clock trim warning(CKWARNIF) interrupt enable */
#define CTC_CTL0_ERRIE BIT(2) /*!< error(ERRIF) interrupt enable */
#define CTC_CTL0_EREFIE BIT(3) /*!< EREFIF interrupt enable */
#define CTC_CTL0_CNTEN BIT(5) /*!< CTC counter enable */
#define CTC_CTL0_AUTOTRIM BIT(6) /*!< hardware automatically trim mode */
#define CTC_CTL0_SWREFPUL BIT(7) /*!< software reference source sync pulse */
#define CTC_CTL0_TRIMVALUE BITS(8,13) /*!< IRC48M trim value */
/* CTC_CTL1 */
#define CTC_CTL1_RLVALUE BITS(0,15) /*!< CTC counter reload value */
#define CTC_CTL1_CKLIM BITS(16,23) /*!< clock trim base limit value */
#define CTC_CTL1_REFPSC BITS(24,26) /*!< reference signal source prescaler */
#define CTC_CTL1_REFSEL BITS(28,29) /*!< reference signal source selection */
#define CTC_CTL1_REFPOL BIT(31) /*!< reference signal source polarity */
/* CTC_STAT */
#define CTC_STAT_CKOKIF BIT(0) /*!< clock trim OK interrupt flag */
#define CTC_STAT_CKWARNIF BIT(1) /*!< clock trim warning interrupt flag */
#define CTC_STAT_ERRIF BIT(2) /*!< error interrupt flag */
#define CTC_STAT_EREFIF BIT(3) /*!< expect reference interrupt flag */
#define CTC_STAT_CKERR BIT(8) /*!< clock trim error bit */
#define CTC_STAT_REFMISS BIT(9) /*!< reference sync pulse miss */
#define CTC_STAT_TRIMERR BIT(10) /*!< trim value error bit */
#define CTC_STAT_REFDIR BIT(15) /*!< CTC trim counter direction when reference sync pulse occurred */
#define CTC_STAT_REFCAP BITS(16,31) /*!< CTC counter capture when reference sync pulse occurred */
/* CTC_INTC */
#define CTC_INTC_CKOKIC BIT(0) /*!< CKOKIF interrupt clear bit */
#define CTC_INTC_CKWARNIC BIT(1) /*!< CKWARNIF interrupt clear bit */
#define CTC_INTC_ERRIC BIT(2) /*!< ERRIF interrupt clear bit */
#define CTC_INTC_EREFIC BIT(3) /*!< EREFIF interrupt clear bit */
/* constants definitions */
/* hardware automatically trim mode definitions */
#define CTC_HARDWARE_TRIM_MODE_ENABLE CTC_CTL0_AUTOTRIM /*!< hardware automatically trim mode enable*/
#define CTC_HARDWARE_TRIM_MODE_DISABLE ((uint32_t)0x00000000U) /*!< hardware automatically trim mode disable*/
/* reference signal source polarity definitions */
#define CTC_REFSOURCE_POLARITY_FALLING CTC_CTL1_REFPOL /*!< reference signal source polarity is falling edge*/
#define CTC_REFSOURCE_POLARITY_RISING ((uint32_t)0x00000000U) /*!< reference signal source polarity is rising edge*/
/* reference signal source selection definitions */
#define CTL1_REFSEL(regval) (BITS(28,29) & ((uint32_t)(regval) << 28))
#define CTC_REFSOURCE_GPIO CTL1_REFSEL(0) /*!< GPIO is selected */
#define CTC_REFSOURCE_LXTAL CTL1_REFSEL(1) /*!< LXTAL is selected */
/* reference signal source prescaler definitions */
#define CTL1_REFPSC(regval) (BITS(24,26) & ((uint32_t)(regval) << 24))
#define CTC_REFSOURCE_PSC_OFF CTL1_REFPSC(0) /*!< reference signal not divided */
#define CTC_REFSOURCE_PSC_DIV2 CTL1_REFPSC(1) /*!< reference signal divided by 2 */
#define CTC_REFSOURCE_PSC_DIV4 CTL1_REFPSC(2) /*!< reference signal divided by 4 */
#define CTC_REFSOURCE_PSC_DIV8 CTL1_REFPSC(3) /*!< reference signal divided by 8 */
#define CTC_REFSOURCE_PSC_DIV16 CTL1_REFPSC(4) /*!< reference signal divided by 16 */
#define CTC_REFSOURCE_PSC_DIV32 CTL1_REFPSC(5) /*!< reference signal divided by 32 */
#define CTC_REFSOURCE_PSC_DIV64 CTL1_REFPSC(6) /*!< reference signal divided by 64 */
#define CTC_REFSOURCE_PSC_DIV128 CTL1_REFPSC(7) /*!< reference signal divided by 128 */
/* CTC interrupt enable definitions */
#define CTC_INT_CKOK CTC_CTL0_CKOKIE /*!< clock trim OK interrupt enable */
#define CTC_INT_CKWARN CTC_CTL0_CKWARNIE /*!< clock trim warning interrupt enable */
#define CTC_INT_ERR CTC_CTL0_ERRIE /*!< error interrupt enable */
#define CTC_INT_EREF CTC_CTL0_EREFIE /*!< expect reference interrupt enable */
/* CTC interrupt source definitions */
#define CTC_INT_FLAG_CKOK CTC_STAT_CKOKIF /*!< clock trim OK interrupt flag */
#define CTC_INT_FLAG_CKWARN CTC_STAT_CKWARNIF /*!< clock trim warning interrupt flag */
#define CTC_INT_FLAG_ERR CTC_STAT_ERRIF /*!< error interrupt flag */
#define CTC_INT_FLAG_EREF CTC_STAT_EREFIF /*!< expect reference interrupt flag */
#define CTC_INT_FLAG_CKERR CTC_STAT_CKERR /*!< clock trim error bit */
#define CTC_INT_FLAG_REFMISS CTC_STAT_REFMISS /*!< reference sync pulse miss */
#define CTC_INT_FLAG_TRIMERR CTC_STAT_TRIMERR /*!< trim value error */
/* CTC flag definitions */
#define CTC_FLAG_CKOK CTC_STAT_CKOKIF /*!< clock trim OK flag */
#define CTC_FLAG_CKWARN CTC_STAT_CKWARNIF /*!< clock trim warning flag */
#define CTC_FLAG_ERR CTC_STAT_ERRIF /*!< error flag */
#define CTC_FLAG_EREF CTC_STAT_EREFIF /*!< expect reference flag */
#define CTC_FLAG_CKERR CTC_STAT_CKERR /*!< clock trim error bit */
#define CTC_FLAG_REFMISS CTC_STAT_REFMISS /*!< reference sync pulse miss */
#define CTC_FLAG_TRIMERR CTC_STAT_TRIMERR /*!< trim value error bit */
/* function declarations */
/* reset ctc clock trim controller */
void ctc_deinit(void);
/* enable CTC trim counter */
void ctc_counter_enable(void);
/* disable CTC trim counter */
void ctc_counter_disable(void);
/* configure the IRC48M trim value */
void ctc_irc48m_trim_value_config(uint8_t trim_value);
/* generate software reference source sync pulse */
void ctc_software_refsource_pulse_generate(void);
/* configure hardware automatically trim mode */
void ctc_hardware_trim_mode_config(uint32_t hardmode);
/* configure reference signal source polarity */
void ctc_refsource_polarity_config(uint32_t polarity);
/* select reference signal source */
void ctc_refsource_signal_select(uint32_t refs);
/* configure reference signal source prescaler */
void ctc_refsource_prescaler_config(uint32_t prescaler);
/* configure clock trim base limit value */
void ctc_clock_limit_value_config(uint8_t limit_value);
/* configure CTC counter reload value */
void ctc_counter_reload_value_config(uint16_t reload_value);
/* read CTC counter capture value when reference sync pulse occurred */
uint16_t ctc_counter_capture_value_read(void);
/* read CTC trim counter direction when reference sync pulse occurred */
FlagStatus ctc_counter_direction_read(void);
/* read CTC counter reload value */
uint16_t ctc_counter_reload_value_read(void);
/* read the IRC48M trim value */
uint8_t ctc_irc48m_trim_value_read(void);
/* interrupt & flag functions */
/* get CTC flag */
FlagStatus ctc_flag_get(uint32_t flag);
/* clear CTC flag */
void ctc_flag_clear(uint32_t flag);
/* enable the CTC interrupt */
void ctc_interrupt_enable(uint32_t interrupt);
/* disable the CTC interrupt */
void ctc_interrupt_disable(uint32_t interrupt);
/* get CTC interrupt flag */
FlagStatus ctc_interrupt_flag_get(uint32_t int_flag);
/* clear CTC interrupt flag */
void ctc_interrupt_flag_clear(uint32_t int_flag);
#endif /* GD32H7XX_CTC_H */

Some files were not shown because too many files have changed in this diff Show More