9ceb218f80
Co-authored-by: Cursor <cursoragent@cursor.com>
304 lines
8.8 KiB
C
304 lines
8.8 KiB
C
#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;
|
||
}
|
||
|