Files
FilmForge/Src/ntc.c
T
hulk 8900240c65 Heater: PID v0.1.14 - banded controller + sensor glitch guards
Rebuild PID as three-zone banded controller: far field P/D full power,
2026-10-15 20:55:02 +08:00

143 lines
5.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "ntc.h"
#include "gd32f4xx_adc.h"
#include "gd32f4xx_rcu.h"
#include "gd32f4xx_gpio.h"
#include "board_config.h"
#include <math.h>
/**
* @brief 配置 NTC 引脚(PC5 模拟输入)与 ADC0 常规通道。
*/
static void ntc_gpio_adc_config(void) {
/* 使能 GPIOC 与 ADC0 时钟 */
rcu_periph_clock_enable(NTC_GPIO_RCU);
rcu_periph_clock_enable(NTC_ADC_RCU);
/* PC5 模拟输入模式 */
gpio_mode_set(NTC_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, NTC_GPIO_PIN);
/* ADC0 独立模式,常规通道软件触发、单次转换、右对齐 */
adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);
adc_special_function_config(NTC_ADC, ADC_CONTINUOUS_MODE, DISABLE);
adc_special_function_config(NTC_ADC, ADC_INSERTED_CHANNEL, DISABLE);
adc_external_trigger_config(NTC_ADC, ADC_ROUTINE_CHANNEL, EXTERNAL_TRIGGER_DISABLE);
adc_data_alignment_config(NTC_ADC, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(NTC_ADC, ADC_ROUTINE_CHANNEL, 1U);
adc_routine_channel_config(NTC_ADC, 0U, NTC_ADC_CHANNEL, NTC_SAMPLETIME);
adc_enable(NTC_ADC);
adc_calibration_enable(NTC_ADC);
}
void ntc_init(void) {
ntc_gpio_adc_config();
}
/**
* @brief 触发一次转换并读取 12bit 原始值。
*/
static uint16_t ntc_read_raw_once(void) {
adc_flag_clear(NTC_ADC, ADC_FLAG_EOC);
adc_software_trigger_enable(NTC_ADC, ADC_ROUTINE_CHANNEL);
/* 等待转换完成(EOC) */
while (SET != adc_flag_get(NTC_ADC, ADC_FLAG_EOC)) {}
return (uint16_t)adc_routine_data_read(NTC_ADC);
}
float ntc_read_temperature(void) {
uint16_t samples[NTC_SAMPLE_COUNT];
uint32_t sum = 0U;
/* 采集 NTC_SAMPLE_COUNT 个样本 */
for (uint8_t i = 0; i < NTC_SAMPLE_COUNT; i++) {
samples[i] = ntc_read_raw_once();
}
/* 插入排序(16 元素,开销可忽略),为修剪均值做准备 */
for (uint8_t i = 1; i < NTC_SAMPLE_COUNT; i++) {
uint16_t key = samples[i];
int8_t j = (int8_t)(i - 1);
while (j >= 0 && samples[j] > key) {
samples[j + 1] = samples[j];
j--;
}
samples[j + 1] = key;
}
/* 修剪均值:去掉每端 NTC_TRIM_COUNT 个极值(野值/毛刺)后取平均 */
for (uint8_t i = NTC_TRIM_COUNT; i < (NTC_SAMPLE_COUNT - NTC_TRIM_COUNT); i++) {
sum += samples[i];
}
float adc_val = (float)sum / (float)(NTC_SAMPLE_COUNT - 2U * NTC_TRIM_COUNT);
/* 分压点电压:上拉 R_PULL 在 VREF 侧,NTC 接 GND 侧 */
float voltage = adc_val * NTC_VREF / NTC_ADC_MAX;
/* 开路/短路判定:电压贴近电源轨说明 NTC 支路没有形成分压通路 */
if (voltage >= 0.97f * NTC_VREF) {
return -273.15f; /* 开路(PC5 被上拉拉满,NTC 未连通) */
}
if (voltage <= 0.03f * NTC_VREF) {
return -274.15f; /* 短路(PC5 被拉到 GND) */
}
/* 反解 NTC 阻值:V = VREF * R / (R_PULL + R) => R = R_PULL * V / (VREF - V) */
float r_ntc = NTC_R_PULL * voltage / (NTC_VREF - voltage);
/* B 值公式:1/T = 1/T25 + ln(R/R25)/B */
float t_k = 1.0f / (1.0f / NTC_T25 + logf(r_ntc / NTC_R25) / (float)NTC_B_VALUE);
return t_k - 273.15f;
}
/******************************************************************************/
/* 后台采样 + 全局最新值:各消费者(debug 打印 / M1 / M2 上报)共享一次采样 */
/******************************************************************************/
static float s_ntc_last_temp = -273.15f; /* 最新温度(初值 = 开路标记) */
static uint16_t s_ntc_sample_divider = 0; /* 主循环 10ms 圈分频计数 */
/* 滑动中值滤波(V0.1.11):真机读数毛刺可持续 1.4s+(曾抓到 14 帧连续
OPEN),会穿透控制环的斜率限幅并触发猛拉。最近 NTC_MEDIAN_N 个样本取
中值——单点/短串毛刺被完全剔除,滞后仅 ~0.2s(远小于低通滤波)。
故障标记值(-273/-274)参与排序:偶发 OPEN 被中值滤掉,连续 >=3 帧才
成为中值(等效又一层去抖),故障判定语义不变。 */
#define NTC_MEDIAN_N 5U
static float s_ntc_win[NTC_MEDIAN_N];
static uint8_t s_ntc_win_idx = 0;
static uint8_t s_ntc_win_fill = 0;
void ntc_sample_task(void) {
/* 主循环周期 10ms:按 NTC_SAMPLE_PERIOD_MS 分频触发一次采样 */
if (++s_ntc_sample_divider >= (NTC_SAMPLE_PERIOD_MS / 10U)) {
s_ntc_sample_divider = 0;
float v = ntc_read_temperature();
s_ntc_win[s_ntc_win_idx] = v;
s_ntc_win_idx = (uint8_t)((s_ntc_win_idx + 1U) % NTC_MEDIAN_N);
if (s_ntc_win_fill < NTC_MEDIAN_N) {
s_ntc_win_fill++;
}
/* 插入排序后取中值(5 点,开销可忽略) */
float tmp[NTC_MEDIAN_N];
for (uint8_t i = 0; i < s_ntc_win_fill; i++) {
tmp[i] = s_ntc_win[i];
}
for (uint8_t i = 1; i < s_ntc_win_fill; i++) {
float key = tmp[i];
int8_t j = (int8_t)(i - 1);
while (j >= 0 && tmp[j] > key) {
tmp[j + 1] = tmp[j];
j--;
}
tmp[j + 1] = key;
}
s_ntc_last_temp = tmp[s_ntc_win_fill / 2U];
}
}
float ntc_get_temperature(void) {
return s_ntc_last_temp;
}