Initial commit: CCU621_M firmware project with BLE debug link support.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+21
@@ -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
@@ -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
@@ -0,0 +1,148 @@
|
||||
|
||||
|
||||
#ifndef GPIO_H
|
||||
#define GPIO_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef __PIN_DEF_H
|
||||
#define __PIN_DEF_H
|
||||
|
||||
#include "gd32h7xx.h" // 根据实际芯片替换
|
||||
|
||||
/* ==================== 板载继电器控制 (K1~K10) ==================== */
|
||||
#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
|
||||
|
||||
@@ -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 //通道数:芯片1(CH0、CH1)+芯片2(CH0、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; // 芯片1(A枪)
|
||||
static sgm51652hx_dev_t sgm51652hx_chip2; // 芯片2(B枪)
|
||||
|
||||
/* 私有函数声明 */
|
||||
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/NSS)lyn260401
|
||||
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; // 模式3,sgm51652只能工作在Model
|
||||
spi_init_struct.nss = SPI_NSS_SOFT; // 软件NSS控制:soft-自己用GPIO控制CS1/CS2
|
||||
spi_init_struct.prescale = SPI_PSC_32; // 分频系数,sgm51652最高时钟10Mhz~10Mhz,SPI3在APB2(300Mhz)
|
||||
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使用不同的片选引脚,需要单独处理*/
|
||||
/* 初始化芯片1(A枪) */
|
||||
spi_init_dev(&spi3_dev1, SPI3, GPIOA, GPIO_PIN_4, 0, 0);
|
||||
sgm51652hx_init(&sgm51652hx_chip1, &spi3_dev1);
|
||||
|
||||
/* 初始化芯片2(B枪) */
|
||||
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_REF,ADC读到的电压值减去 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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),DVDD:1.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 数据在时钟下降沿采样)
|
||||
2)SCLK最高频率: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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 默认使用 LXTAL(32.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=输出 UTC,1=输出北京时间(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;
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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/PF5,115200,8N1
|
||||
* @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/PD9,115200,8N1
|
||||
* @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/PC11,2400,8E1
|
||||
* @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/PD2,2400,8E1
|
||||
* @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/PC7,115200,8N1
|
||||
* @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/PB3,115200,8N1
|
||||
* @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/PE0,115200,8N1
|
||||
* @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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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` 负责 FM24CL16(I2C 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`
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 前清 cache;DMA 写 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: PK0,MOSI: PJ10,MISO: PJ11
|
||||
* - SPI Mode0,8bit,MSB 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~0x01FFFFFF(32MB) */
|
||||
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 ID:Manufacturer[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 IRQ:SPI4 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 IRQ:SPI4 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;
|
||||
}
|
||||
}
|
||||
@@ -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` 是否调用、片选是否正确、外设时钟是否使能
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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: 0xC8(GigaDevice)
|
||||
* - Capacity code: 0x19(典型为 256Mbit = 32MB)
|
||||
*
|
||||
* 当前底层驱动已启用 4-byte 地址模式(4 字节地址):
|
||||
* - 可直接访问线性地址范围:0x00000000 ~ 0x01FFFFFF(32MB)
|
||||
* - 总容量: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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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__ */
|
||||
@@ -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(<, &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(§or_rw[k * CARD_SLOT_SIZE], tmp, CARD_SLOT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
if (body_or_null != NULL) {
|
||||
U8_T *dst = §or_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 */
|
||||
@@ -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 字节(槽位总占用 48:1 字节 RECORD_HEADER + 46 + 1 字节 CRC 低 8 位)
|
||||
* - amount_milli:金额,单位 0.001 元(与 API 中 balance「分」换算:毫元 = 分 * 10)
|
||||
* - rsv[8]:前 4 字节 create_time,后 4 字节 last_use_time(Unix 秒,小端),与 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 */
|
||||
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* @file fault_flash_impl.c
|
||||
* @brief 历史故障记录:EEPROM 索引 + 外部 Flash 数据区实现。
|
||||
*
|
||||
* 迁移说明(参考 CCU601E_D):
|
||||
* - EEPROM(fm24cl16)仅存储 S_HIS_FAULT_MNG 索引信息,减少擦写与上电恢复成本。
|
||||
* - 外部 Flash(externalflash)按扇区保存 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);
|
||||
}
|
||||
|
||||
@@ -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`
|
||||
- **功能**:平台离线时周期缓存 MeterValues;Flash 为 **整池定长帧数组**(仿 `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);
|
||||
|
||||
// 写入EEPROM,eeprom_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 离线 MeterValues:SPI 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:累计 Wh(u64/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; /**< SoC:0~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=前半区枪1,1=后半区枪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 */
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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, ðernetif_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, ðernetif_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 */
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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`)
|
||||
- 本文档
|
||||
- 新增点位时建议增加“信号名/端口引脚/用途”三元信息,避免后续只看宏名难以定位硬件。
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 配置 MPU(Memory Protection Unit)区域属性
|
||||
*
|
||||
* 详细说明:
|
||||
* 1) 该函数主要用于给“被 DMA 与 CPU 同时访问”的内存区域设置合适的属性,
|
||||
* 避免 D-Cache 引入的数据一致性问题(CPU 看到旧数据、DMA 写入不可见等)。
|
||||
*
|
||||
* 2) 当前配置了两块 16KB 区域(均位于 SRAM3):
|
||||
* - 区域0:0x30000000 ~ 0x30003FFF
|
||||
* 典型用于 DMA 描述符、ETH Rx/Tx 缓冲等需要强一致性的内存。
|
||||
* - 区域1:0x30004000 ~ 0x30007FFF
|
||||
* 典型用于 lwIP heap(pbuf/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();
|
||||
|
||||
/* 区域1:lwIP 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();
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -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闭合
|
||||
/* K1~K10 与 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 复位 PE6:x=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
|
||||
@@ -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 */
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
@@ -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
Reference in New Issue
Block a user