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

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-08 17:28:36 +08:00
commit 9ceb218f80
1597 changed files with 724159 additions and 0 deletions
+484
View File
@@ -0,0 +1,484 @@
/*!
\file spi3_charger.c
\brief SPI3 for A&B gun charger voltage acquisition and insulation detection
\version 2026-03-09, V1.4.0, firmware for GD32H7xx
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#include "adc_spi3_insuVolt.h"
#include <stdio.h>
#include "gd32h7xx.h"
#include "FreeRTOS.h"
#include "task.h"
#include "spi_if.h"
#include "sgm51652hx.h"
#include <string.h>
#include "sys_drv_init.h"
// ======== 配置 =========
#define NUM_CHS 4 //通道数:芯片1CH0、CH1+芯片2CH0、CH1
#define NUM_COLL 5 //每个通道采集次数
#define V_REF 4.096f //芯片基准电压
int16_t adc3_final[NUM_CHS][NUM_COLL];
//uint8_t dma_buf[ADC_CHANNELS][BYTES_PER_CH]; //临时接收缓存
/* 私有变量 */
static uint8_t spi3_initialized = 0;
static spi_dev_t spi3_dev1; // SPI3设备(A枪)
static spi_dev_t spi3_dev2; // SPI3设备(B枪)
static sgm51652hx_dev_t sgm51652hx_chip1; // 芯片1A枪)
static sgm51652hx_dev_t sgm51652hx_chip2; // 芯片2B枪)
/* 私有函数声明 */
static void spi3_gpio_config(void);
static void spi3_config(void);
/*!
\brief configure SPI3 GPIO peripheral for charger
\param[in] none
\param[out] none
\retval none
\note SPI3引脚配置 for SGM51652H4XTS38G/T:
- SPI3_CSA -> PA4 (A枪片选)
- SPI3_CSB -> PA3 (B枪片选)
- SPI3_SCK -> PE12 (时钟)
- SPI3_MISO -> PE13 (数据输入)
- SPI3_MOSI -> PE14 (数据输出)
*/
void spi3_gpio_config(void)
{
/* 使能GPIO时钟 */
rcu_periph_clock_enable(SPI3_RCU_CS);
rcu_periph_clock_enable(SPI3_RCU_SCK_MSIO);
/* 使能SPI3时钟 */
rcu_periph_clock_enable(RCU_SPI3);
/* 使能DMA时钟 */
// rcu_periph_clock_enable(RCU_DMA0);
// rcu_periph_clock_enable(RCU_DMAMUX);
/* 配置SPI3时钟源 */
rcu_spi_clock_config(IDX_SPI3, RCU_SPISRC_APB2); //RCU_SPISRC_PLL0Q
/* 配置A枪片选引脚 (PA4) - 软件控制 */
gpio_mode_set(SPI3_CS1_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SPI3_CS1_PIN);
gpio_output_options_set(SPI3_CS1_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, SPI3_CS1_PIN);
/* 默认拉高,不选中 */
SPI3_CS1_HIGH(); //gpio_bit_set(GPIOA, GPIO_PIN_4);
/* 配置B枪片选引脚 (PA3) - 软件控制 */
gpio_mode_set(SPI3_CS2_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SPI3_CS2_PIN);
gpio_output_options_set(SPI3_CS2_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, SPI3_CS2_PIN);
/* 默认拉高,不选中B枪 */
SPI3_CS2_HIGH(); //gpio_bit_set(GPIOA, GPIO_PIN_3);
/* 配置SPI3引脚复用功能 */
//spi_related_ios_af_en(SPI3); //SPI标准库函数,一键配置SPIx所有引脚复用(SCK/MOSI/MISO/NSSlyn260401
gpio_af_set(GPIOE, GPIO_AF_5, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14);
/* 配置SCK引脚 (PE12) - 复用功能输出 */
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_12);
/* 配置MISO引脚 (PE13) - 复用功能输入 */
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_13);
/* 配置MOSI引脚 (PE14) - 复用功能输出 */
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_14);
}
/*!
\brief configure SPI3 peripheral for charger
\param[in] none
\param[out] none
\retval none
\note SPI3基本配置,针对SGM51652H4XTS38G/T优化
*/
void spi3_config(void)
{
spi_parameter_struct spi_init_struct;
/* 去初始化SPI3 */
spi_i2s_deinit(SPI3);
/* 初始化SPI参数结构体 */
spi_struct_para_init(&spi_init_struct);
/* SPI3参数配置 for SGM51652H4XTS38G/T */
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; // 全双工模式
spi_init_struct.device_mode = SPI_MASTER; // 主模式
spi_init_struct.data_size = SPI_DATASIZE_8BIT; // 8位数据帧
spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_1EDGE; // 模式3sgm51652只能工作在Model
spi_init_struct.nss = SPI_NSS_SOFT; // 软件NSS控制:soft-自己用GPIO控制CS1/CS2
spi_init_struct.prescale = SPI_PSC_32; // 分频系数,sgm51652最高时钟10Mhz~10Mhz,SPI3在APB2300Mhz
spi_init_struct.endian = SPI_ENDIAN_MSB; // MSB先行 sgm51652规定必须高位先传
/* 初始化SPI3 */
spi_init(SPI3, &spi_init_struct);
/* 使能字节访问 */
spi_byte_access_enable(SPI3);
/* 使能NSS输出 (兼容性) */
spi_nss_output_enable(SPI3);
}
#if 0 //DMA相关配置,未调通 lyn20260410
/*!
\brief configure the DMA peripheral
\param[in] none
\param[out] none
\retval none
*/
void adc_read_single(uint8_t chip, uint8_t cmd, uint8_t index)
{
dma_single_data_parameter_struct dma_init_struct;
uint8_t buf[2];
//选中芯片
if(chip == CHIP_1)
{
SPI3_CS1_LOW();
SPI3_CS2_HIGH();
}
else
{
SPI3_CS2_LOW();
SPI3_CS1_HIGH();
}
//DMA配置
/* deinitialize DMA registers of a channel */
dma_deinit(DMA0, DMA_CH0);
dma_single_data_para_struct_init(&dma_init_struct);
/* SPI0 receive DMA config: DMA_CH0 */
dma_init_struct.request = DMA_REQUEST_SPI3_RX; //选哪个外设:SPI3-RX
dma_init_struct.periph_addr = (uint32_t)&SPI_RDATA(SPI3); //外设地址:SPI3
dma_init_struct.periph_inc = DMA_MEMORY_INCREASE_DISABLE; //禁止外设自增
dma_init_struct.memory0_addr = (uint32_t)buf; //内存地址:定义的数组
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; //内存地址自增
dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; //数据位宽:8/16/32位
dma_init_struct.circular_mode = DMA_CIRCULAR_MODE_ENABLE; //是否循环模式:是
dma_init_struct.direction = DMA_PERIPH_TO_MEMORY; //方向:外设->内存
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; //优先级
dma_init_struct.number = 2; //SPI一次DMA收多少字节,与数据位宽对应 1路ADC=2字节
dma_single_data_mode_init(DMA0, DMA_CH0, &dma_init_struct); //初始化
/* enable DMA channel */
dma_channel_enable(DMA0, DMA_CH0);
//开启SPI3 DMA接收
spi_dma_enable(SPI3, SPI_DMA_RECEIVE);
#if 0
//发通道命令 = 软件启动转换
if(spi3_charger_read_data(CHARGER_GUN_A, cmd, data, 2) != 0) {
return -1.0f;
}
//等待DMA完成
// 等待 DMA 完成
while(!dma_flag_get(DMA0, DMA_CH0, DMA_INTF_FTFIF));
dma_flag_clear(DMA0, DMA_CH0, DMA_INTF_FTFIF);
// 关闭
spi_dma_disable(SPI3, SPI_DMA_RECEIVE);
dma_channel_disable(DMA0, DMA_CH0);
SPI3_CS1_HIGH();
SPI3_CS2_HIGH();
// 合并 16 位 ADC 数据
adc_result[index] = (int16_t)(buf[0] << 8 | buf[1]);
#endif
}
#endif
/*!
\brief SPI3完整初始化 for charger
\param[in] none
\param[out] none
\retval 0: 成功, -1: 失败
\note 专门为A&B枪充电电压采集和绝缘检测优化
*/
int adc_spi3_insuVolt_init(void)
{
if(spi3_initialized) {
return 0; // 已初始化
}
/* 配置GPIO */
spi3_gpio_config();
/* 配置SPI外设 */
spi3_config();
/* 使能SPI3 */
spi_enable(SPI3);
/* 初始化SPI设备。注意:芯片1/2使用不同的片选引脚,需要单独处理*/
/* 初始化芯片1A枪) */
spi_init_dev(&spi3_dev1, SPI3, GPIOA, GPIO_PIN_4, 0, 0);
sgm51652hx_init(&sgm51652hx_chip1, &spi3_dev1);
/* 初始化芯片2B枪) */
spi_init_dev(&spi3_dev2, SPI3, GPIOA, GPIO_PIN_3, 0, 0);
sgm51652hx_init(&sgm51652hx_chip2, &spi3_dev2);
/* 设置通道量程 */
for (uint8_t i = 0; i < 2; i++) {
sgm51652hx_set_range(&sgm51652hx_chip1, i, RGVL_BP_256); // ±2.56V
sgm51652hx_set_range(&sgm51652hx_chip2, i, RGVL_BP_256); // ±2.56V
}
spi3_initialized = 1;
return 0;
}
/*!
\brief SPI3去初始化
\param[in] none
\param[out] none
\retval none
*/
void adc_spi3_batVoltCurr_deinit(void)
{
/* 去初始化芯片 */
sgm51652hx_deinit(&sgm51652hx_chip1);
sgm51652hx_deinit(&sgm51652hx_chip2);
/* 去初始化SPI设备 */
spi_deinit_dev(&spi3_dev1);
spi_deinit_dev(&spi3_dev2);
/* 禁用SPI3 */
spi_disable(SPI3);
/* 去初始化SPI3 */
spi_i2s_deinit(SPI3);
/* 所有片选拉高 */
gpio_bit_set(GPIOA, GPIO_PIN_3);
gpio_bit_set(GPIOA, GPIO_PIN_4);
spi3_initialized = 0;
}
/*
函数功能:将MCU通过SPI接收到的数据转换为控制器端口采到的电压
输入: uint16_t read_data MCU通过SPI接收到的数据
uint8_t adc_zf 正负极区分,0正极,1负级
uint8_t flag 绝缘检测光电继电器闭合标志位,0:未闭合,1:已经闭合
float data 转换后的电压
备注:V_REF:芯片基准电压
芯片采集到的电压计算方式:
每个LSB代表的电压值 = 芯片输入范围(±0.625,即0.625 * 2 * 基准电压(V_REF / 2^16;
ADC读到的电压值 = 每个LSB代表的电压值 * read_data
0V对应点ADC读到电压值为0.625 * V_REFADC读到的电压值减去 0.625 * V_REF即为当前芯片采集电压值。
芯片采集到电压到控制器端口采集电压转换关系如下:
绝缘检测光电继电器闭合前:控制器端口电压 = 芯片采集电压值 * 1376.89/(0.625 *V_REF)
绝缘检测光电继电器闭合后:控制器端口电压:
正极电压 = 芯片采集电压值 * 689 /(0.625 *V_REF)
负极电压 = 芯片采集电压值 * 1380 /(0.625 *V_REF)
*/
float f_read_to_Vol(float read_data, uint8_t adc_zf, uint8_t flag)
{
float data = 0.0f;
//data = chip_volt; //等同于gm51652hx_calc_voltage(raw_value, RGVL_BP_256)
data = (0.625f * 2.0f * V_REF * read_data / 65535.0f) - (0.625f * V_REF);
if(flag == 0)
{
data = data * 1376.89f / (0.625f * V_REF);
}
else
{
if(adc_zf == 0) //正负极计算方式不同,分开计算
{
data = data * 689.0f / (0.625f * V_REF);
}
else
{
data = data * 1380.0f / (0.625f * V_REF);
}
}
return data;
}
//==================== 单通道读取 (核心函数) ====================
uint16_t sgm51652hx_spiReadBytes(uint8_t chip, uint8_t channel) //读取芯片chip通道channel的返回原始ADC值
{
uint16_t raw_value = 0;
sgm51652hx_dev_t *dev = NULL;
if(chip == CHIP_1)
dev = &sgm51652hx_chip1; //芯片1-A枪绝缘电压检测
else
dev = &sgm51652hx_chip2; //芯片2-B枪绝缘电压检测
/* 设置通道量程(TBD:上电初始化设置一次就行,还是需要每次发送都要设置?) */
//sgm51652hx_set_range(dev, channel, RGVL_BP_256);
/* 读取原始值 */
if(sgm51652hx_read_channel(dev, channel, &raw_value) != ERR_OK_SGM)
return 0;
//printf("spi3 chip=%d,channel=%d: %d\n", chip, channel, raw_value);
return raw_value;
}
/*
函数功能:获取指定控制器端口采集电压
输入:uint8_t chip 芯片号,等同于枪号,1 对应A枪,2 对应B枪
uint8_t channel 通道号,等同于正负级,channel_0对应正极,channel_1对应负级
uint8_t relayOnOff 绝缘检测光电继电器闭合标志位,0:未闭合,1:已经闭合
*/
float spi3_read_analog_voltage(uint8_t chip, uint8_t channel)
{
uint16_t raw_value[NUM_COLL] = {0};
uint16_t i = 0, j = 0;
uint32_t raw_sum_avg = 0;
uint8_t relayOnOff;
float real_voltage = 0;
if(chip == 1)
relayOnOff = GUN1_INS_KM_READ;
else
relayOnOff = GUN2_INS_KM_READ;
// printf("chip = %d,relayOnOff = %d\n",chip,relayOnOff);
//读取指定端口与通道数据NUM_COLL次
for(i=0; i<NUM_COLL; i++)
raw_value[i] = sgm51652hx_spiReadBytes(chip, channel);
for(j=0; j<NUM_COLL; j++)
raw_sum_avg += raw_value[j];
raw_sum_avg /= NUM_COLL;
/* 转换为芯片引脚电压值 --> 融合进f_read_to_vol() */
//chip_voltage = sgm51652hx_calc_voltage(raw_sum_avg, RGVL_BP_256);
#if 1
/* 根据绝缘回路,将芯片引脚电压转换为控制器端口电压数据 */
real_voltage = f_read_to_Vol(raw_sum_avg, channel, relayOnOff); //TODO: 参考DCU606
if(channel == 1) //通道1-负级
real_voltage = -real_voltage;
if(relayOnOff == 0) //光电继电器闭合标志,闭合前后,数据分开校准
{
if(channel == 0) //校准系数
{
real_voltage = real_voltage / 1000 * 993; //997(100欧/V);300V-990 ,400V-997 ,500V-997 ,600V-
//997(500欧/V);400V-997
}
else if(channel == 1)
{
real_voltage = real_voltage / 1000 * 997; //1004(100欧/V);300V-1007 ,500V-
//1004(500欧/V);400V-1007 ,
}
}
else
{
if(channel == 0) //校准系数
{
real_voltage = real_voltage / 1000 * 998; //997(100欧/V);300V-990 ,400V-997 ,500V-997 ,600V
//997(500欧/V);400V-997
}
else if(channel == 1)
{
real_voltage = real_voltage / 1000 * 995; //1004(100欧/V);300V-1007 ,500V-
//1004(500欧/V);400V-1007 ,
}
}
#endif
// printf("SPI3 chip(%d)-channel(%d): raw=%d, realU=%.2f \n", chip, channel, raw_sum_avg, real_voltage);
return real_voltage;
}
//==================== 读取全部4通道 - TODO:改为对应用层接口函数 ====================
void sgm51652_read_all_channels(void)
{
float adc_result[4] = {0};
// 芯片1 CH0
adc_result[0] = spi3_read_analog_voltage(CHIP_1, CHNL_0);
// 芯片1 CH1
adc_result[1] = spi3_read_analog_voltage(CHIP_1, CHNL_1);
// 芯片2 CH0
adc_result[2] = spi3_read_analog_voltage(CHIP_2, CHNL_0);
// 芯片2 CH1
adc_result[3] = spi3_read_analog_voltage(CHIP_2, CHNL_1);
(void)adc_result;
}
/*!
\brief 充电器自检
\param[in] none
\param[out] none
\retval 0: 正常, -1: 故障
*/
void spi3_charger_self_test(void)
{
printf("spi3 adc-insuvolt test started...\n");
/* 检查SPI3初始化状态 */
if(!spi3_initialized) {
printf("SPI3 not initialized\n");
return;
}
// spi3_charger_write_command(1, 0, 0, 0);
//
// vTaskDelay(50);
// spi3_charger_read_command(1, 0, 0, 0);
while(1)
{
//sgm51652hx_spiReadBytes(1, 0);
//spi3_read_analog_voltage(1, 0, 5);
// spi3_charger_read_data_A_0(); //
sgm51652_read_all_channels();
vTaskDelay(250);
}
}
+83
View File
@@ -0,0 +1,83 @@
/*!
\file spi3_charger.h
\brief header file of SPI3 for A&B gun charger voltage acquisition and insulation detection
\version 2026-03-09, V1.4.0, firmware for GD32H7xx
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#ifndef SPI3_CHARGER_H
#define SPI3_CHARGER_H
#include <stdint.h>
/* SPI3引脚定义 for SGM51652H4XTS38G/T */
#define SPI3_SCK_PORT GPIOE // PE12 - 时钟
#define SPI3_SCK_PIN GPIO_PIN_12
#define SPI3_MISO_PORT GPIOE // PE13 - 数据输入
#define SPI3_MISO_PIN GPIO_PIN_13
#define SPI3_MOSI_PORT GPIOE // PE14 - 数据输出
#define SPI3_MOSI_PIN GPIO_PIN_14
/* SPI3复用功能编号 */
#define SPI3_AF GPIO_AF_5
/* 双芯片片选 */
#define SPI3_CS1_PORT GPIOA // PA4 - 芯片1片选
#define SPI3_CS1_PIN GPIO_PIN_4
#define SPI3_CS2_PORT GPIOA // PA3 - 芯片2片选
#define SPI3_CS2_PIN GPIO_PIN_3
/* SPIC3 时钟 */
#define SPI3_RCU_CS RCU_GPIOA
#define SPI3_RCU_SCK_MSIO RCU_GPIOE
/* 芯片/通道选择 */
#define CHIP_1 1 //芯片1-CSA
#define CHIP_2 2 //芯片2-CSB
#define CHNL_0 0 //通道0
#define CHNL_1 1 //通道1
/* 引脚宏操作 */
#define SPI3_CS1_LOW() gpio_bit_reset(SPI3_CS1_PORT, SPI3_CS1_PIN)
#define SPI3_CS1_HIGH() gpio_bit_set(SPI3_CS1_PORT, SPI3_CS1_PIN)
#define SPI3_CS2_LOW() gpio_bit_reset(SPI3_CS2_PORT, SPI3_CS2_PIN)
#define SPI3_CS2_HIGH() gpio_bit_set(SPI3_CS2_PORT, SPI3_CS2_PIN)
/* 完整初始化和去初始化 */
int adc_spi3_insuVolt_init(void);
void adc_spi3_insuVolt_deinit(void);
void spi3_charger_self_test(void);
float spi3_read_analog_voltage(uint8_t chip, uint8_t channel);
#endif /* SPI3_CHARGER_H */
+474
View File
@@ -0,0 +1,474 @@
/*!
\file spi5_analog.c
\brief SPI5 for analog data acquisition
\version 2026-03-09, V1.4.0, firmware for GD32H7xx
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#include "adc_spi5_batVoltCurr.h"
#include <stdio.h>
#include "gd32h7xx.h"
#include "sgm51652hx.h"
#include "spi_if.h"
//#include "task.h"
#define V_REF 4.096 //芯片基准电压
#define N 50 //ADC采样次数
float adc_ref = 0.0; //基准电压值
/* 私有变量 */
static uint8_t spi5_initialized = 0;
static spi_dev_t spi5_dev; // SPI5设备
static sgm51652hx_dev_t sgm51652hx_chip; // SGM51652HX芯片
/* 私有函数声明 */
static void spi5_gpio_config(void);
static void spi5_config(void);
//static float spi5_convert_adc_value(uint16_t raw_value, uint8_t channel);
static uint8_t spi5_validate_channel(uint8_t channel);
/*!
\brief configure SPI5 GPIO peripheral for analog acquisition
\param[in] none
\param[out] none
\retval none
\note SPI5引脚配置 for analog acquisition:
- SPI5_CS -> PG12 (片选)
- SPI5_SCK -> PG13 (时钟)
- SPI5_MISO -> PG14 (数据输入)
- SPI5_MOSI -> PA6 (数据输出)
*/
void spi5_gpio_config(void)
{
/* 使能GPIO时钟 */
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOG);
/* 使能SPI5时钟 */
rcu_periph_clock_enable(RCU_SPI5);
/* 配置SPI5时钟源 */
rcu_spi_clock_config(IDX_SPI5, RCU_SPISRC_APB2);
/* 配置CS引脚 (PG12) - 软件控制 */
gpio_mode_set(GPIOG, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_12);
/* 默认拉高,不选中模拟芯片 */
gpio_bit_set(GPIOG, GPIO_PIN_12);
/* 配置SPI5引脚复用功能 */
gpio_af_set(GPIOG, GPIO_AF_5, GPIO_PIN_13 | GPIO_PIN_14); // SCK, MOSI
gpio_af_set(GPIOA, GPIO_AF_8, GPIO_PIN_6); // MISO,复用AF8
/* 配置SCK引脚 (PG13) - 复用功能输出 */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_13);
/* 配置MOSI引脚 (PG14) - 复用功能输出 */
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_14);
/* 配置MISO引脚 (PA6) - 复用功能输入 */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_6);
}
/*!
\brief configure SPI5 peripheral for analog acquisition
\param[in] none
\param[out] none
\retval none
\note SPI5基本配置,针对模拟量采集优化
*/
void spi5_config(void)
{
spi_parameter_struct spi_init_struct;
/* 去初始化SPI5 */
spi_i2s_deinit(SPI5);
/* 初始化SPI参数结构体 */
spi_struct_para_init(&spi_init_struct);
/* SPI5参数配置 for analog acquisition */
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; // 全双工模式
spi_init_struct.device_mode = SPI_MASTER; // 主模式
spi_init_struct.data_size = SPI_DATASIZE_8BIT; // 8位数据帧
spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_1EDGE; // 时钟极性低,第一边沿采样 (Mode 0)
spi_init_struct.nss = SPI_NSS_SOFT; // 软件NSS控制
spi_init_struct.prescale = SPI_PSC_32; // 分频系数
spi_init_struct.endian = SPI_ENDIAN_MSB; // MSB先行
/* 初始化SPI5 */
spi_init(SPI5, &spi_init_struct);
/* 使能字节访问 */
spi_byte_access_enable(SPI5);
/* 使能NSS输出 (兼容性) */
spi_nss_output_enable(SPI5);
}
#if 0
static uint8_t spi5_transmit_receive(uint8_t data)
{
/* 等待发送缓冲区为空 */
while(RESET == spi_i2s_flag_get(SPI5, SPI_FLAG_TP));
/* 发送数据 */
spi_i2s_data_transmit(SPI5, data);
/* 等待接收缓冲区非空 */
while(RESET == spi_i2s_flag_get(SPI5, SPI_FLAG_RP));
/* 接收数据 */
return (uint8_t)spi_i2s_data_receive(SPI5);
}
static void spi5_send_data(uint8_t data)
{
/* 等待发送缓冲区为空 */
while(RESET == spi_i2s_flag_get(SPI5, SPI_FLAG_TP));
/* 发送数据 */
spi_i2s_data_transmit(SPI5, data);
}
int spi5_write_range(sgm51652hxReg_e reg, sgm51652hxRngVlu_e rangVlu) //设置指定通道量程
{
//uint8_t txbuf[3] = {0x01, };
uint8_t cmd = 0;
uint8_t rxbuf[4] = {0};
if(!spi5_initialized) {
return -1;
}
spi5_cs_control(1); //选中
cmd = (((uint8_t)reg)<<1) | 1; //寄存器地址+写指令1
//rxbuf[0] = spi5_transmit_receive(0x01); //写命令
//rxbuf[1] = spi5_transmit_receive((uint8_t)reg); //关键程序寄存器地址
rxbuf[0] = spi5_transmit_receive(cmd);
rxbuf[1] = spi5_transmit_receive((uint8_t)rangVlu); //写量程,返回的值应该是写入的值
rxbuf[2] = spi5_transmit_receive(0x00);
rxbuf[3] = spi5_transmit_receive(0x00);
spi5_cs_control(0); //取消选中
printf("spi5 regAddr[%d]: set value = %x -- %x %x %x %x \n", reg, rangVlu, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]);
return 0;
}
int spi5_read_range(sgm51652hxReg_e reg) //设置指定通道量程
{
//uint8_t txbuf[3] = {0x01, };
uint8_t cmd = 0;
uint8_t rxbuf[3] = {0};
if(!spi5_initialized) {
return -1;
}
spi5_cs_control(1); //选中
cmd = (((uint8_t)reg)<<1); //寄存器地址+读指令0
rxbuf[0] = spi5_transmit_receive(cmd); //
rxbuf[1] = spi5_transmit_receive(0x00); //返回值
rxbuf[2] = spi5_transmit_receive(0x00);
spi5_cs_control(0); //取消选中
printf("spi5 regAddr[%d]: read value -- %x %x %x\n", reg, rxbuf[0], rxbuf[1], rxbuf[2]);
return 0;
}
#endif
#if 0 //调试
int sgm51652hx_write_reg_test1(sgm51652hx_dev_t *dev, uint8_t reg, uint8_t value)
{
uint8_t rxbuf[4] = {0};
if (!dev || !dev->initialized) {
return ERR_INVALIDPARAMETER;
}
/* 选中芯片 */
spi_cs_control(&dev->spi_dev, 1);
//spi5_cs_control(1); //选中
/* 开启发送 */
spi_master_transfer_start(dev->spi_dev.spi, SPI_TRANS_START);
/* 发送写命令:寄存器地址 << 1 | 1 */
uint8_t cmd = (reg << 1) | 1;
#if 0
spi5_send_data(cmd);
/* 发送数据 */
spi5_send_data(value);
/* 发送两个空字节 */
spi5_send_data(0x00);
spi5_send_data(0x00);
#endif
#if 0
spi_transmit_byte(dev->spi_dev.spi, cmd);
/* 发送数据 */
spi_transmit_byte(dev->spi_dev.spi, value);
/* 发送两个空字节 */
spi_transmit_byte(dev->spi_dev.spi, 0x00);
spi_transmit_byte(dev->spi_dev.spi, 0x00);
#endif
#if 1
rxbuf[0] = spi_transmit_receive_byte(dev->spi_dev.spi, cmd);/* 发送数据 */
rxbuf[1] = spi_transmit_receive_byte(dev->spi_dev.spi, value);/* 发送两个空字节 */
rxbuf[2] = spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
rxbuf[3] = spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
#endif
#if 0
rxbuf[0] = spi5_transmit_receive(cmd);/* 发送数据 */
rxbuf[1] = spi5_transmit_receive(value);/* 发送两个空字节 */
rxbuf[2] = spi5_transmit_receive(0x00);
rxbuf[3] = spi5_transmit_receive(0x00);
#endif
/* 取消片选 */
spi_cs_control(&dev->spi_dev, 0);
//spi5_cs_control(0); //选中
printf("spi5 regAddr[%d]: set value = %x %x %x %x \n", reg, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]);
return ERR_OK_SGM;
}
int sgm51652hx_set_range_test1(sgm51652hx_dev_t *dev, uint8_t channel, sgm51652hxRngVlu_e range)
{
if (!dev || !dev->initialized || channel > 7) {
return ERR_INVALIDPARAMETER;
}
/* 计算量程寄存器地址 */
uint8_t reg = 0x05 + channel; /* RANGE_CH0 = 0x05 */
/* 写入量程配置 */
return sgm51652hx_write_reg_test1(dev, reg, (uint8_t)range);
}
#endif
/*!
\brief SPI5完整初始化 for analog acquisition
\param[in] none
\param[out] none
\retval 0: 成功, -1: 失败
\note 专门为模拟量采集优化
*/
int adc_spi5_batVoltCurr_init(void)
{
int i = 0;
if(spi5_initialized) {
return 0; // 已初始化
}
/* 配置GPIO */
spi5_gpio_config();
/* 配置SPI外设 */
spi5_config();
/* 使能SPI5 */
spi_enable(SPI5);
/* 初始化SPI设备 */
spi_init_dev(&spi5_dev, SPI5, GPIOG, GPIO_PIN_12, 0, 0);
/* 初始化SGM51652HX芯片 */
sgm51652hx_init(&sgm51652hx_chip, &spi5_dev);
spi5_initialized = 1;
// for(i=0; i<ANALOG_MAX_CHANNELS; i++)
// spi5_write_range(RANGE_CH0+i, RGVL_UP_512); //8个通道都设置为单极性,0~5.26V量程
/* 设置通道量程 */
for(i=0; i<ANALOG_MAX_CHANNELS; i++)
sgm51652hx_set_range(&sgm51652hx_chip, i, RGVL_UP_512); //所有通道都设置为单极性,0~5.26V量程
// printf("SPI5 analog acquisition initialized successfully, supporting %d channels with %d-bit resolution\n",
// ANALOG_MAX_CHANNELS, analog_state.resolution); //SPI5模拟采集初始化成功,支持%d通道,分辨率%d位
return 0;
}
/*!
\brief SPI5去初始化
\param[in] none
\param[out] none
\retval none
*/
void spi5_analog_deinit(void)
{
/* 去初始化芯片 */
sgm51652hx_deinit(&sgm51652hx_chip);
/* 去初始化SPI设备 */
spi_deinit_dev(&spi5_dev);
/* 禁用SPI5 */
spi_disable(SPI5);
/* 去初始化SPI5 */
spi_i2s_deinit(SPI5);
/* CS引脚拉高 */
gpio_bit_set(GPIOG, GPIO_PIN_12);
spi5_initialized = 0;
}
static uint8_t spi5_validate_channel(uint8_t channel)
{
return (channel < ANALOG_MAX_CHANNELS) ? 1 : 0;
}
/*!
\brief 读取模拟量通道原始值
\param[in] channel: 通道号 (0-7)
\param[out] none
\retval 16位原始ADC值
*/
uint16_t spi5_read_analog_raw(uint8_t channel)
{
uint16_t raw_value = 0;
if(!spi5_initialized || !spi5_validate_channel(channel)) {
return 0;
}
/* 检查通道是否启用 */
if(0 == spi5_validate_channel(channel)) {
return 0;
}
/* 读取通道数据 */
if(sgm51652hx_read_channel(&sgm51652hx_chip, channel, &raw_value) != ERR_OK_SGM) {
return 0;
}
return raw_value;
}
float f_read_spi5_to_Vol(float read_data)
{
float data = 0.0f;
data = (1.25 * V_REF * read_data / 65535) ;
return data;
}
/**************************************************************
* 函数名称: 计算采集电流
* 参 数: float vol 芯片采集电压值
* 计算公式:U=1000*UADC/16; U为分流器两端电压,单位mV;
* I=1000*UADC/16R I为分流器两端电流,R为分流器两端电阻;
* 返 回 值: 计算出的采集电流
* 描 述:
***************************************************************/
float f_cal_curr(float vol)
{
float ret = 0.0,data = 0.0;
data = f_read_spi5_to_Vol(vol);
ret = (1000 * data)/16; //分流器两端电压,单位mV
ret = ret * 300 /75; //计算电流,分流器规格,300A 75mV
return ret;
}
//获取实际电压值(将这个值取平均值 50 次,调整系数1013)
float f_actual_voltage_data(float read_data )
{
float data = 0.0,actVol = 0.0;
data = f_read_spi5_to_Vol(read_data);
actVol = (data - 0.686f) / 8.2f / 270.0f * 2000270.0f;
//1013为系数,可自行调整以趋近实际值
return actVol * 1013 /1000;
}
//读取基准电压
float f_read_reference_voltage(float read_data)
{
float data = 0.0;
data = f_read_spi5_to_Vol(read_data);
return data ;
}
/*!
\brief 读取模拟量通道电压值
\param[in] channel: 通道号 (0-7)
channel 0 A枪CC1
channel 1 B枪CC1
channel 2 B枪外侧电压
channel 3 B枪外侧基准电压
channel 4 A枪外侧电压
channel 5 A枪外侧基准电压
channel 6 B枪电流
channel 7 A枪电流
\param[out] none
\retval 电压值 (V)
*/
float spi5_read_analog_voltage(uint8_t channel)
{
float f_actua_data; //实际数据值
if(!spi5_initialized || !spi5_validate_channel(channel))
{
return 0.0f;
}
/* 读取原始值 */
uint16_t raw_value = spi5_read_analog_raw(channel);
if(channel == 0) /* A枪CC1电压 */
f_actua_data = f_read_spi5_to_Vol(raw_value)*4;
else if(channel == 1) /* B枪CC1电压 */
f_actua_data = f_read_spi5_to_Vol(raw_value)*4;
else if(channel == 2) /* B枪外侧电压值*/
f_actua_data = f_actual_voltage_data(raw_value);
else if(channel == 3) /* B枪基准电压 */
{
f_actua_data = f_read_reference_voltage(raw_value);
}
else if(channel == 4) /* A枪外侧电压值 */
{
f_actua_data = f_actual_voltage_data(raw_value);
}
else if(channel == 5) /* A枪基准电压值 */
{
f_actua_data = f_read_reference_voltage(raw_value);
}
else if(channel == 6) /*B枪电流值*/
f_actua_data = f_cal_curr(raw_value);
else if(channel == 7) /*A枪电流值*/
f_actua_data = f_cal_curr(raw_value);
//printf("SPI5 channel(%d): raw=%d, realU=%.2f \n", channel, raw_value, f_actua_data);
return f_actua_data;
}
+76
View File
@@ -0,0 +1,76 @@
/*!
\file spi5_analog.h
\brief header file of SPI5 for analog data acquisition
\version 2026-03-09, V1.4.0, firmware for GD32H7xx
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#ifndef SPI5_ANALOG_H
#define SPI5_ANALOG_H
#include <stdint.h>
/* SPI5引脚定义 for analog acquisition */
#define SPI5_CS_PIN GPIO_PIN_12 // PG12 - 片选
#define SPI5_SCK_PIN GPIO_PIN_13 // PG13 - 时钟
#define SPI5_MISO_PIN GPIO_PIN_14 // PG14 - 数据输入
#define SPI5_MOSI_PIN GPIO_PIN_6 // PA6 - 数据输出
#define SPI5_CS_PORT GPIOG
#define SPI5_SCK_PORT GPIOG
#define SPI5_MISO_PORT GPIOG
#define SPI5_MOSI_PORT GPIOA
/* SPI5复用功能编号 */
#define SPI5_AF GPIO_AF_5
/* 本项目启用通道数 */
#define ANALOG_MAX_CHANNELS 8
/* 函数声明 */
/* GPIO配置 */
int adc_spi5_batVoltCurr_init(void);
/* 完整初始化和去初始化 */
int spi5_analog_init(void);
void spi5_analog_deinit(void);
/* 状态检查 */
uint8_t spi5_analog_is_initialized(void);
/* 基本读取操作 */
uint16_t spi5_read_analog_raw(uint8_t channel);
float spi5_read_analog_voltage(uint8_t channel);
uint8_t spi5_read_all_channels(float *voltages, uint8_t max_channels);
void spi5_analog_self_test(void);
#endif /* SPI5_ANALOG_H */
+303
View File
@@ -0,0 +1,303 @@
#include "gd32h7xx.h"
#include "adc_temp.h"
#include <string.h>
#include <math.h>
#include "usart.h"
#include "sys_drv_init.h"
#include "gd32h7xx_dma.h"
/* 忙等待延时函数 */
static void delay_ms(uint32_t ms)
{
/* 使用固定的时钟频率 600MHz 来计算延时,避免 SystemCoreClock 未初始化的问题 */
for(volatile uint32_t i = 0; i < (600000000 / 4000) * ms; i++)
{
__NOP();
}
}
/* ADC2 DMA连续采样缓冲:DMA中断只负责“完成标记”,数据处理放在 get_adc_data()。 */
#define ADC_TEMP_DMA_PERIPH DMA1
#define ADC_TEMP_DMA_CH DMA_CH0
#define ADC_TEMP_DMA_IRQN DMA1_Channel0_IRQn
#define ADC_TEMP_DMA_SAMPLE_CNT (16U)
static __IO uint32_t g_adc_dma_buf[ADC_TEMP_DMA_SAMPLE_CNT];
static volatile uint32_t g_adc_dma_frame_cnt;
__IO uint32_t adc_value[4];
void init_adc(void)
{
dma_single_data_parameter_struct dma_init_struct;
/* GPIO时钟使能 */
rcu_periph_clock_enable(RCU_GPIOC);
/* ADC时钟使能 */
rcu_periph_clock_enable(RCU_ADC2);
/* DMA时钟使能 */
rcu_periph_clock_enable(RCU_DMA1);
/* GPIO参数配置*/
gpio_mode_set(GPIOC, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_2);
/* ADC初始化 -- 根据数据手册,ADC2_IN0--PC2端子仅能使用ADC2第0个通道 */
adc_deinit(ADC2);
/* ADC时钟配置 */
adc_clock_config(ADC2, ADC_CLK_SYNC_HCLK_DIV6);
/* 单通道连续转换 + DMA */
adc_special_function_config(ADC2, ADC_SCAN_MODE, DISABLE);
adc_special_function_config(ADC2, ADC_CONTINUOUS_MODE, ENABLE);
/* 转换结果(12/14位)存放在 32 位寄存器的低 16 位中 */
adc_data_alignment_config(ADC2, ADC_DATAALIGN_RIGHT);
/* ADC 通道配置(规则通道组,单通道) */
adc_channel_length_config(ADC2, ADC_REGULAR_CHANNEL, 1);
/* 配置规则通道序列,决定 ADC 在转换时,对 PC2 引脚电压进行采样的时间长度*/
adc_regular_channel_config(ADC2, 0, ADC_CHANNEL_0, 240);
/* 禁用外部触发*/
adc_external_trigger_config(ADC2, ADC_REGULAR_CHANNEL, EXTERNAL_TRIGGER_DISABLE);
/* DMA配置:ADC2 regular data -> g_adc_dma_buf[] (循环) */
dma_deinit(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH);
dma_single_data_para_struct_init(&dma_init_struct);
dma_init_struct.request = DMA_REQUEST_ADC2;
dma_init_struct.periph_addr = (uint32_t)&ADC_RDATA(ADC2);
dma_init_struct.periph_inc = DMA_MEMORY_INCREASE_DISABLE;
dma_init_struct.memory0_addr = (uint32_t)g_adc_dma_buf;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_32BIT;
dma_init_struct.circular_mode = DMA_CIRCULAR_MODE_ENABLE;
dma_init_struct.direction = DMA_PERIPH_TO_MEMORY;
dma_init_struct.priority = DMA_PRIORITY_HIGH;
dma_init_struct.number = ADC_TEMP_DMA_SAMPLE_CNT;
dma_single_data_mode_init(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH, &dma_init_struct);
dma_interrupt_enable(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH, DMA_INT_HTF);
dma_interrupt_enable(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH, DMA_INT_FTF);
nvic_irq_enable(ADC_TEMP_DMA_IRQN, 6U, 0U);
/* 使能 ADC2 模块 */
adc_enable(ADC2);
/* wait for ADC stability */
delay_ms(1);
/* 配置校准模式,偏移量校准,用于纠正 ADC 自身的零点误差 */
adc_calibration_mode_config(ADC2, ADC_CALIBRATION_OFFSET);
/* 配置校准次数 */
adc_calibration_number(ADC2, ADC_CALIBRATION_NUM1);
/*执行校准 */
adc_calibration_enable(ADC2);
/* 启动DMA与ADC连续转换 */
memset((void *)g_adc_dma_buf, 0, sizeof(g_adc_dma_buf));
g_adc_dma_frame_cnt = 0U;
dma_channel_enable(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH);
adc_dma_mode_enable(ADC2);
adc_dma_request_after_last_enable(ADC2);
adc_software_trigger_enable(ADC2, ADC_REGULAR_CHANNEL);
}
uint32_t adc_channel_sample(uint8_t channel)
{
uint32_t timeout = 1000000U;
/* 重新配置规则通道 */
adc_regular_channel_config(ADC2, 0U, channel, 240);
/* 软件触发转换 */
adc_software_trigger_enable(ADC2, ADC_REGULAR_CHANNEL);
/* 等待转换结束 */
while(!adc_flag_get(ADC2, ADC_FLAG_EOC)){
if (timeout-- == 0U) {
return 0U;
}
}
/* 清除标志 */
adc_flag_clear(ADC2, ADC_FLAG_EOC);
/* 读取结果并返回 */
return (adc_regular_data_read(ADC2));
}
void DMA1_Channel0_IRQHandler(void)
{
if (SET == dma_interrupt_flag_get(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH, DMA_INT_FLAG_HTF)) {
dma_interrupt_flag_clear(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH, DMA_INT_FLAG_HTF);
g_adc_dma_frame_cnt++;
}
if (SET == dma_interrupt_flag_get(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH, DMA_INT_FLAG_FTF)) {
dma_interrupt_flag_clear(ADC_TEMP_DMA_PERIPH, ADC_TEMP_DMA_CH, DMA_INT_FLAG_FTF);
g_adc_dma_frame_cnt++;
}
}
void select_temp_channel(uint8_t flag_set)
{
//温度采集选通,
/*
OUT8、OUT7设置为高电平,A枪正温度
OUT8设置为高电平,OUT7设置为低电平,A枪负温度
OUT8设置为低电平,OUT7设置为高电平,B枪正温度
OUT8、OUT7设置为低电平,B枪负温度
*/
if(flag_set == 0)
{
TEMP_MULT_CTRA(1);
TEMP_MULT_CTRB(1);
TEMP_MULT_CTRC(1);
}
else if(flag_set == 1)
{
TEMP_MULT_CTRA(0);
TEMP_MULT_CTRB(1);
TEMP_MULT_CTRC(1);
}
else if(flag_set == 2)
{
TEMP_MULT_CTRA(1);
TEMP_MULT_CTRB(0);
TEMP_MULT_CTRC(1);
}
else if(flag_set == 3)
{
TEMP_MULT_CTRA(0);
TEMP_MULT_CTRB(0);
TEMP_MULT_CTRC(1);
}
else if(flag_set == 4)
{
TEMP_MULT_CTRA(1);
TEMP_MULT_CTRB(1);
TEMP_MULT_CTRC(0);
}
else if(flag_set == 5)
{
TEMP_MULT_CTRA(0);
TEMP_MULT_CTRB(1);
TEMP_MULT_CTRC(0);
}
else if(flag_set == 6)
{
TEMP_MULT_CTRA(1);
TEMP_MULT_CTRB(0);
TEMP_MULT_CTRC(0);
}
else if(flag_set == 7)
{
TEMP_MULT_CTRA(0);
TEMP_MULT_CTRB(0);
TEMP_MULT_CTRC(0);
}
}
double double_Vol_to_resistance(double vol)
{
double ret = 0;
//BL1117-50CX输出+5V_Tem,可通过测量VOL_Vin2校准+5V_Tem,Rt=3kΩ*Vo/+5V_Tem-Vo
ret = (3000 * vol) / (5.04 - vol);
return ret / 1000.0;
}
double PT1000_Vol_To_Temperature(double vol)
{
// 定义PT1000常数
double R;
const double R0 = 1000.0; // 0°C时的电阻值
const double A = 3.9083e-3; // IEC751系数
const double B = -5.775e-7;
const double C = -4.183e-12; // 负温度区系数
// 计算电阻值(kΩ单位)
R = double_Vol_to_resistance(vol);
/* 校准系数(保持kΩ单位) */
if (R > 1.219)
{
R = R * 1.01778; // 高温区补偿
}
else if (R > 1)
{
R = R * 1.00395; // 常温区补偿
}
else
{
R = R * 0.98884; // 低温区补偿
}
// 转换为Ω单位
double R_ohm = R * 1000.0;
// 计算温度
if (R_ohm >= R0)
{
// 正温度区使用二次方程
double discriminant = A * A - 4 * B * (1 - R_ohm / R0);
return (-A + sqrt(discriminant)) / (2 * B);
}
else
{
// 负温度区使用牛顿迭代法
const int max_iter = 100; // 最大迭代次数
const double tol = 1e-6; // 收敛误差
double T = -50.0; // 初始温度估计值
for (int i = 0; i < max_iter; i++)
{
double T2 = T * T;
double T3 = T2 * T;
// Callendar-Van Dusen方程
double f = R0 * (1 + A * T + B * T2 + C * (T - 100) * T3) - R_ohm;
// 检查是否收敛
if (fabs(f) < tol)
{
return T;
}
// 导数计算
double df = R0 * (A + 2 * B * T + C * (4 * T3 - 300 * T2));
// 避免除零错误
if (fabs(df) < 1e-10)
{
break;
}
// 牛顿迭代法更新
T -= f / df;
}
// 返回收敛值
return T;
}
}
uint32_t get_adc_data(void)
{
uint64_t sum = 0U;
uint32_t i;
/* 仅处理DMA中断已写入的采样数据,不触发硬件采样。 */
for (i = 0U; i < ADC_TEMP_DMA_SAMPLE_CNT; i++)
{
sum += g_adc_dma_buf[i];
// printf("data = %d\n", g_adc_dma_buf[i]);
}
return (uint32_t)(sum / ADC_TEMP_DMA_SAMPLE_CNT);
}
uint32_t adc_readval(void)
{
float ret, temp_data = 0.0f;
ret = (get_adc_data() * 3.0f) / 4095.0f;
temp_data = PT1000_Vol_To_Temperature(ret);
return temp_data;
}
void adc_test(uint8_t i)
{
float val_value;
(void)i;
delay_ms(1000);
adc_value[0] = adc_channel_sample(ADC_CHANNEL_0);
val_value = (adc_value[0] * 3.0f) / 4095.0f;
(void)val_value;
}
+25
View File
@@ -0,0 +1,25 @@
/*!
\file adc_temp.h
\brief ADC温度采集头文件
\version 2026-03-09, V1.4.0, firmware for GD32H7xx
*/
#ifndef __ADC_TEMP_H
#define __ADC_TEMP_H
#include <stdint.h>
//#include <math.h>
#ifdef __cplusplus
extern "C" {
#endif
void select_temp_channel(uint8_t flag_set);
void init_adc(void);
uint32_t get_adc_data(void);
uint32_t adc_readval(void);
#endif /* __ADC_TEMP_H */
+230
View File
@@ -0,0 +1,230 @@
#include "sgm51652hx.h"
#include "gd32h7xx_gpio.h"
#include "FreeRTOS.h"
#include "task.h"
/*!
\brief 初始化SGM51652HX芯片
\param[in] dev: 芯片设备结构体指针
\param[in] spi_dev: SPI设备结构体指针
\param[out] none
\retval 0: 成功, -1: 失败
*/
int sgm51652hx_init(sgm51652hx_dev_t *dev, spi_dev_t *spi_dev)
{
if (!dev || !spi_dev || !spi_dev->initialized) {
return ERR_INVALIDPARAMETER;
}
/* 初始化设备结构体 */
dev->spi_dev = *spi_dev;
dev->initialized = 1;
return ERR_OK_SGM;
}
/*!
\brief 去初始化SGM51652HX芯片
\param[in] dev: 芯片设备结构体指针
\param[out] none
\retval 0: 成功, -1: 失败
*/
int sgm51652hx_deinit(sgm51652hx_dev_t *dev)
{
if (!dev || !dev->initialized) {
return ERR_INVALIDPARAMETER;
}
/* 复位芯片 */
spi_rst_control(&dev->spi_dev, 1);
dev->initialized = 0;
return ERR_OK_SGM;
}
/*!
\brief 向SGM51652HX芯片写入寄存器
\param[in] dev: 芯片设备结构体指针
\param[in] reg: 寄存器地址
\param[in] value: 要写入的值
\param[out] none
\retval 0: 成功, -1: 失败
*/
int sgm51652hx_write_reg(sgm51652hx_dev_t *dev, uint8_t reg, uint8_t value)
{
if (!dev || !dev->initialized) {
return ERR_INVALIDPARAMETER;
}
/* 选中芯片 */
spi_cs_control(&dev->spi_dev, 1);
/* 开启发送 */
spi_master_transfer_start(dev->spi_dev.spi, SPI_TRANS_START);
/* 发送写命令:寄存器地址 << 1 | 1 */
uint8_t cmd = (reg << 1) | 1;
spi_transmit_receive_byte(dev->spi_dev.spi, cmd); //TBD:经测试,调用发送接收函数,后续采样正常,如果只调用发送函数,后续采样不正常;但另一版程序没有此问题,原因待定 lyn260411
/* 发送数据 */
spi_transmit_receive_byte(dev->spi_dev.spi, value);
/* 发送两个空字节 */
spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
/* 取消片选 */
spi_cs_control(&dev->spi_dev, 0);
return ERR_OK_SGM;
}
/*!
\brief 从SGM51652HX芯片读取寄存器
\param[in] dev: 芯片设备结构体指针
\param[in] reg: 寄存器地址
\param[out] value: 读取的值
\retval 0: 成功, -1: 失败
*/
int sgm51652hx_read_reg(sgm51652hx_dev_t *dev, uint8_t reg, uint8_t *value)
{
if (!dev || !dev->initialized || !value) {
return ERR_INVALIDPARAMETER;
}
/* 选中芯片 */
spi_cs_control(&dev->spi_dev, 1);
/* 开启发送 */
spi_master_transfer_start(dev->spi_dev.spi, SPI_TRANS_START);
/* 发送读命令:寄存器地址 << 1 | 0 */
uint8_t cmd = (reg << 1) | 0;
spi_transmit_byte(dev->spi_dev.spi, cmd);
/* 发送空字节 */
spi_transmit_byte(dev->spi_dev.spi, 0x00);
/* 读取数据 */
*value = spi_receive_byte(dev->spi_dev.spi);
/* 读取最后一个空字节 */
spi_receive_byte(dev->spi_dev.spi);
/* 取消片选 */
spi_cs_control(&dev->spi_dev, 0);
return ERR_OK_SGM;
}
/*!
\brief 设置SGM51652HX芯片通道量程
\param[in] dev: 芯片设备结构体指针
\param[in] channel: 通道号 (0-7)
\param[in] range: 量程配置值
\param[out] none
\retval 0: 成功, -1: 失败
*/
int sgm51652hx_set_range(sgm51652hx_dev_t *dev, uint8_t channel, sgm51652hxRngVlu_e range)
{
if (!dev || !dev->initialized || channel > 7) {
return ERR_INVALIDPARAMETER;
}
/* 计算量程寄存器地址 */
uint8_t reg = 0x05 + channel; /* RANGE_CH0 = 0x05 */
/* 写入量程配置 */
return sgm51652hx_write_reg(dev, reg, (uint8_t)range);
}
/*!
\brief 从SGM51652HX芯片读取通道数据
\param[in] dev: 芯片设备结构体指针
\param[in] channel: 通道号 (0-7)
\param[out] value: 读取的16位ADC值
\retval 0: 成功, -1: 失败
*/
int sgm51652hx_read_channel(sgm51652hx_dev_t *dev, uint8_t channel, uint16_t *value)
{
if (!dev || !dev->initialized || !value || channel > 7) {
return ERR_INVALIDPARAMETER;
}
/* 计算通道命令 */
uint16_t cmd = 0xC000 + (channel * 0x400); /* MAN_CH_0 = 0xC000 */
/* 选中芯片 */
spi_cs_control(&dev->spi_dev, 1);
/* 开启发送 */
spi_master_transfer_start(dev->spi_dev.spi, SPI_TRANS_START);
/* 发送命令 */
spi_transmit_receive_byte(dev->spi_dev.spi, (cmd >> 8) & 0xFF); //cmd高
spi_transmit_receive_byte(dev->spi_dev.spi, cmd & 0xFF); //
spi_transmit_receive_byte(dev->spi_dev.spi, 0x00); //MSB格式,接收高8位
spi_transmit_receive_byte(dev->spi_dev.spi, 0x00); //接收低8位
/* 取消片选 */
spi_cs_control(&dev->spi_dev, 0); //
/* ADC芯片需要转换时间,添加延时确保数据就绪 */
vTaskDelay(pdMS_TO_TICKS(1)); // 延时1毫秒
/* 再次选中芯片,读取数据 */
spi_cs_control(&dev->spi_dev, 1);
/* 发送四个空字节,同时读取数据 */
uint8_t rxbuf[4] = {0};
rxbuf[0] = spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
rxbuf[1] = spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
rxbuf[2] = spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
rxbuf[3] = spi_transmit_receive_byte(dev->spi_dev.spi, 0x00);
/* 取消片选 */
spi_cs_control(&dev->spi_dev, 0);
/* 合并16位数据 */
*value = (rxbuf[2] << 8) | rxbuf[3];
//printf("spix recv:%d %d %d %d\n", rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]);
return ERR_OK_SGM;
}
/*!
\brief 根据原始ADC值和量程配置计算电压 芯片引脚真实电压
\param[in] adc_val: 原始ADC值
\param[in] range: 量程配置值
\param[out] none
\retval 计算后的电压值 (V)
*/
float sgm51652hx_calc_voltage(uint16_t adc_val, sgm51652hxRngVlu_e range)
{
float voltage = 0.0f;
switch(range)
{
// ==================== 双极性 Bipolar ====================
case RGVL_BP_1024: // ±10.24V
voltage = (int16_t)adc_val * 10.24f / 32768.0f;
break;
case RGVL_BP_512: // ±5.12V
voltage = (int16_t)adc_val * 5.12f / 32768.0f;
break;
case RGVL_BP_256: // ±2.56V
voltage = (int16_t)adc_val * 2.56f / 32768.0f;
break;
// ==================== 单极性 Unipolar ====================
case RGVL_UP_1024: // 0~10.24V
voltage = (float)adc_val * 10.24f / 65535.0f;
break;
case RGVL_UP_512: // 0~5.12V
voltage = (float)adc_val * 5.12f / 65535.0f;
break;
default: // 错误配置
voltage = 0.0f;
break;
}
return voltage;
}
+129
View File
@@ -0,0 +1,129 @@
/*!
\file sgm51652hx
\brief
1. 芯片基本信息:
1)型号:sgm51652H4(4通道)、sgm51652H8(8通道)
2)类型:16位500kSPS单电源 SAR ADC
3)基准:片内4.096V高精度基准(温漂9ppm/℃)
4)供电:AVDD:4.75~5.25(典型5V),DVDD1.65~AVDD(典型3.3V)
2. 模拟输入特性
1)支持输入类型:双极性单端、单极性单端、双极性差分
2)量程配置(软件独立设置)
量程配置值 -- 输入范围 -- 对应VREF倍数
0000 ±10.24V ±2.5 * VREF
0001 ±5.12V ±1.25 * VREF
0010 ±2.56V ±0.625 * VREF
0101 0~10.24V 0~2.5 * VREF
0110 0~5.12V 0~1.25 * VREF
3. 数字接口与时序
1)通信接口:SPI,模式Mode3(CPOL=1 时钟空闲时为高电平、CPHA=1 数据在时钟下降沿采样)
2SCLK最高频率:17Mhz。(GD32H759芯片SPI3/SPI5挂在APB2总线,当前APB2时钟300Mhz,按32预分频设置)
3)转换时间1μs,采集时间1μs
4)帧结构:寄存器读写24个SCLK,数据读取32个SCLK
***每个通道读数,是CS拉低后【此句很重要,查看芯片数据手册波形图】,后面16个时钟读出的数据才是。虽然这个图是auto的,但跟手动是一样的。***
5)支持拓扑:菊花链,最多挂4片
4. 寄存器与命令系统
1)命令寄存器(16位,只写),详见 sgm51652hxCmd_e
2)关键程序寄存器,详见 sgm51652hxReg_e
5. 工作模式
1)自动扫描模式(AUTO_SCAN):按使能通道升序循环采集
2)手动通道模式(MAN_CH_n):固定采集指定单通道
3)待机模式(STDBY):低功耗,唤醒约20μs,保留配置
4)掉电模式(PWR_DOWN):极低功耗,唤醒需15ms
5)复位模式(RST):寄存器恢复默认值
*/
#ifndef _SGM51652HX_H_
#define _SGM51652HX_H_
#include <stdint.h>
#include "spi_if.h"
/******* Macro Definitions *******************/
#define __SGM51652H8__ 1 //SPI3-H4、SPI5-H8
#define ERR_OK_SGM 0
#define ERR_INVALIDPARAMETER -1
#define ERR_TIMEOUT_SGM -2
typedef struct {
spi_dev_t spi_dev; // SPI设备
uint8_t initialized; // 初始化状态
} sgm51652hx_dev_t;
// 兼容旧结构体名称
typedef sgm51652hx_dev_t sgm51652hxDevice_t;
//命令寄存器映射(命令寄存器是1个 16 位的只写寄存器,用于设置SGM51652H4/SGM51652H8 的工作模式。)
typedef enum{
NO_OP = 0x0000, //保持当前模式
STDBY = 0x8200, //进入待机模式
PWR_DN = 0x8300, //进入断电模式
RST = 0x8500, //重置程序寄存器
AUTO_RST = 0xA000, //启用通道自动扫描模式
MAN_CH_0 = 0xC000, //选择通道0手动读取模式
MAN_CH_1 = 0xC400, //选择通道1手动读取模式
MAN_CH_2 = 0xC800, //选择通道2手动读取模式
MAN_CH_3 = 0xCC00, //选择通道3手动读取模式
#if __SGM51652H8__ == 1
MAN_CH_4 = 0xD000,
MAN_CH_5 = 0xD400,
MAN_CH_6 = 0xD800,
MAN_CH_7 = 0xDC00,
#endif
MAN_AUX = 0xE000, //特殊功能,手动采辅助通道Aux,纯差分输入,无PGA/滤波,范围-VREF~+VREF
MAX_CMD_NUM
}sgm51652hxCmd_e;
//程序寄存器映射(程序寄存器是 16 位的可读写寄存器,用于设置SGM51652H4/SGM51652H8 的工作状态。)
/*
数据位 DB[15:9]是寄存器地址。数据位 DB[8]是写或读指令位。
在写操作周期中,SDI 引脚上的 DIN[7:0]是写入目标寄存器的数据,SDO 引脚上的 DIN[7:0]是从目标寄存器读回的数据。读回数据可用于验证写入是否成功。
在1个读周期中,SDI 数据位 DB[8]是读指令位。SDO DOUT[7:0]上的数据是来自目标地址程序寄存器的读回数据。
读回数据是MSB(高位优先)模式
*/
typedef enum{
AUTO_SEQ_EN = 0x01, // 自动扫描通道使能。通道自扫描模式下,扫描通道的选定,数据bit7-bit0代表通道7-通道9,0:不选定该通道,1:选定该通道
CHANNEL_PD = 0x02, // 通道独立断电。通道自扫描模式下,通道电源控制,数据bit7-bit0代表通道7-通道9,0:通道开启电源,1:通道电源关闭
FEATURE_SEL, // 菊花链/输出格式
RANGE_CH0 = 0x05, // 0x05~0x0C:CH0-CH7量程配置。 RANGE_CHn 通道 n 选择输入范围位,数据bit7-bit4补0,数据bit3-bit0定义如下
RANGE_CH1, // 0000 = 输入范围设置为±2.5 × V(默认)
RANGE_CH2, // 0001 = 输入范围设置为±1.25 × V
RANGE_CH3, // 0010 = 输入范围设置为±0.625 × V
#if __SGM51652H4__ == 0 // 0101 = 输入范围设置为 0 到 2.5 × V
RANGE_CH4, // 0110 = 输入范围设置为 0 到 1.25 × V
RANGE_CH5,
RANGE_CH6,
RANGE_CH7,
#endif
FLOATING_DETECTION_EN = 0x0D, //悬空检测使能。输入浮动检测启用控制,数据bit7:0 = 禁用,1 = 启用,bit6-bit0 保留,补0
FLOATING_DETECTION_STATUS, //悬空检测状态。查询输入浮动状态,数据bit7-bit0代表通道7-1,0=通道未处于浮动状态(默认)1=通道处于浮动状态
CMD_READ_BK = 0x3F, //命令回读(只读),查询前1个数据帧中执行的命令
}sgm51652hxReg_e;
/* 量程配置值(写进CHx量程寄存器的配置值)
* 详见本文件顶部2.2。 双极性bipolar,单极性unipolar
*/
typedef enum{
RGVL_BP_1024 = 0x00, //类型:双极性,输入范围:±10.24V
RGVL_BP_512,
RGVL_BP_256,
RGVL_UP_1024 = 0x05, //类型:单极性,输入范围:0~10.24V
RGVL_UP_512,
}sgm51652hxRngVlu_e;
// 芯片操作函数
int sgm51652hx_init(sgm51652hx_dev_t *dev, spi_dev_t *spi_dev);
int sgm51652hx_deinit(sgm51652hx_dev_t *dev);
int sgm51652hx_write_reg(sgm51652hx_dev_t *dev, uint8_t reg, uint8_t value);
int sgm51652hx_read_reg(sgm51652hx_dev_t *dev, uint8_t reg, uint8_t *value);
int sgm51652hx_set_range(sgm51652hx_dev_t *dev, uint8_t channel, sgm51652hxRngVlu_e range);
int sgm51652hx_read_channel(sgm51652hx_dev_t *dev, uint8_t channel, uint16_t *value);
float sgm51652hx_calc_voltage(uint16_t adc_val, sgm51652hxRngVlu_e range);
#endif
+145
View File
@@ -0,0 +1,145 @@
#include "spi_if.h"
#include <gd32h7xx_spi.h>
#include <gd32h7xx_gpio.h>
/*!
\brief SPIx发送数据 (私有函数)
\param[in] spi_periph: SPIx(x=0,1,2,3,4,5)
\param[in] data: 要发送的数据(uint8_t,一次发送1个字节!!)
\param[out] none
\retval none
*/
void spi_transmit_byte(uint32_t spi_periph, uint8_t data)
{
/* 等待发送缓冲区有空间 */
while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TP));
/* 发送数据 */
spi_i2s_data_transmit(spi_periph, data);
}
/*!
\brief SPIx接收数据 (私有函数)
\param[in] spi_periph: SPIx(x=0,1,2,3,4,5)
\param[in] none
\param[out] none
\retval 接收到的数据(uint8_t 一次接收1个字节)
*/
uint8_t spi_receive_byte(uint32_t spi_periph)
{
/* 等待接收缓冲区非空 */
while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RP));
/* 接收数据 */
return (uint8_t)spi_i2s_data_receive(spi_periph);
}
/*!
\brief SPIx发送接收数据 (私有函数)
\param[in] spi_periph: SPIx(x=0,1,2,3,4,5)
\param[in] tx_data: 要发送的数据
\param[out] none
\retval 接收到的数据
*/
uint8_t spi_transmit_receive_byte(uint32_t spi_periph, uint8_t tx_data)
{
/* 等待发送缓冲区有空间 */
while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TP));
/* 发送数据 */
spi_i2s_data_transmit(spi_periph, tx_data);
/* 等待接收缓冲区非空 */
while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RP));
/* 接收数据 */
return (uint8_t)spi_i2s_data_receive(spi_periph);
}
/*!
\brief 控制SPI设备的片选引脚
\param[in] dev: SPI设备结构体指针
\param[in] enable: 1-选中设备, 0-取消选中
\param[out] none
\retval none
*/
void spi_cs_control(spi_dev_t *dev, uint8_t enable)
{
if (dev && dev->csPort) {
if (enable) {
/* 拉低片选引脚,选中设备 */
gpio_bit_reset((uint32_t)dev->csPort, dev->csPin);
} else {
/* 拉高片选引脚,取消选中设备 */
gpio_bit_set((uint32_t)dev->csPort, dev->csPin);
}
}
}
/*!
\brief 控制SPI设备的复位引脚
\param[in] dev: SPI设备结构体指针
\param[in] enable: 1-复位, 0-正常
\param[out] none
\retval none
*/
void spi_rst_control(spi_dev_t *dev, uint8_t enable)
{
if (dev && dev->rstPort) {
if (enable) {
/* 拉低复位引脚,复位设备 */
gpio_bit_reset((uint32_t)dev->rstPort, dev->rstPin);
} else {
/* 拉高复位引脚,设备正常工作 */
gpio_bit_set((uint32_t)dev->rstPort, dev->rstPin);
}
}
}
/*!
\brief 初始化SPI设备
\param[in] dev: SPI设备结构体指针
\param[in] spi_periph: SPI外设
\param[in] csPort: 片选GPIO端口
\param[in] csPin: 片选GPIO引脚
\param[in] rstPort: 复位GPIO端口
\param[in] rstPin: 复位GPIO引脚
\param[out] none
\retval 0: 成功, -1: 失败
*/
int spi_init_dev(spi_dev_t *dev, uint32_t spi_periph, uint32_t csPort, uint16_t csPin, uint32_t rstPort, uint16_t rstPin)
{
if (!dev) {
return -1;
}
/* 初始化设备结构体 */
dev->spi = spi_periph;
dev->csPort = csPort;
dev->csPin = csPin;
dev->rstPort = rstPort;
dev->rstPin = rstPin;
dev->initialized = 1;
return 0;
}
/*!
\brief 去初始化SPI设备
\param[in] dev: SPI设备结构体指针
\param[out] none
\retval 0: 成功, -1: 失败
*/
int spi_deinit_dev(spi_dev_t *dev)
{
if (!dev || !dev->initialized) {
return -1;
}
dev->initialized = 0;
return 0;
}
+41
View File
@@ -0,0 +1,41 @@
/*!
\file spi_if.h
\brief spi接收发送统一接口函数
\version firmware for GD32H7xx
*/
#ifndef _SPI_IF_H_
#define _SPI_IF_H_
#include <stdint.h>
typedef struct {
uint32_t spi; // MCU对应的SPI口
uint32_t rstPort; // 复位GPIO引脚
uint16_t rstPin; // 复位pin引脚
uint32_t csPort; // 片选GPIO引脚
uint16_t csPin; // 片选pin引脚
uint8_t initialized; // 初始化状态
} spi_dev_t;
// 兼容旧结构体名称
typedef spi_dev_t spi_t;
/* 对外接口函数 */
void spi_transmit_byte(uint32_t spi_periph, uint8_t data);
uint8_t spi_receive_byte(uint32_t spi_periph);
uint8_t spi_transmit_receive_byte(uint32_t spi_periph, uint8_t tx_data);
// GPIO控制函数
void spi_cs_control(spi_dev_t *dev, uint8_t enable);
void spi_rst_control(spi_dev_t *dev, uint8_t enable);
// SPI初始化函数
int spi_init_dev(spi_dev_t *dev, uint32_t spi_periph, uint32_t csPort, uint16_t csPin, uint32_t rstPort, uint16_t rstPin);
int spi_deinit_dev(spi_dev_t *dev);
#endif