#include "ntc_control.h"
#include "include.h"
/*************IO模式定义*****************
数字模拟模式: 1:数字 0:模拟
输入输出模式: 1:输出 0:输入
IO数据寄存器: 1:高电平 0:低电平
输入上拉控制: 1:使能上拉 0:关闭上拉
输入下拉控制: 1:使能下拉 0:关闭下拉
GPIO唤醒控制: 1:使能唤醒 0:关闭唤醒
*******************************************/
// 常量定义 (使用整数运算)
#define R_FIXED 10000 // 固定电阻值 (10K)
#define R_NTC_25C 10000 // 25℃时NTC阻值 (10K)
#define NTC_BETA 3950 // NTC的B值
#define ADC_RESOLUTION 1023 // ADC分辨率
// 外部变量声明
extern unsigned char system_state; // 系统状态
// 加热PWM控制相关变量
unsigned char heating_intensity = 0; // 加热强度控制
static u8 heating_on = 0; // 加热器当前状态
// 预计算的分压比-温度映射表 (0.1°C单位)
typedef struct {
u16 ratio_x1000; // 分压比 × 1000 (整数)
i16 temp_01c; // 温度 × 10 (0.1°C单位)
} RatioTempMap;
// 温度范围: -20°C 到 100°C (每1°C一个点)
// 基于NTC 10K@25°C, B=3950的实测数据生成
// 适配内部2.0V参考电压的ADC值范围
const RatioTempMap ratio_temp_map[] = {
{1000, -200}, // -20°C
{995, -190}, // -19°C
{990, -180}, // -18°C
{985, -170}, // -17°C
{980, -160}, // -16°C
{975, -150}, // -15°C
{970, -140}, // -14°C
{965, -130}, // -13°C
{960, -120}, // -12°C
{955, -110}, // -11°C
{950, -100}, // -10°C
{945, -90}, // -9°C
{940, -80}, // -8°C
{935, -70}, // -7°C
{930, -60}, // -6°C
{925, -50}, // -5°C
{920, -40}, // -4°C
{915, -30}, // -3°C
{910, -20}, // -2°C
{905, -10}, // -1°C
{900, 0}, // 0°C
{895, 10}, // 1°C
{890, 20}, // 2°C
{885, 30}, // 3°C
{880, 40}, // 4°C
{875, 50}, // 5°C
{870, 60}, // 6°C
{865, 70}, // 7°C
{860, 80}, // 8°C
{855, 90}, // 9°C
{850, 100}, // 10°C
{845, 110}, // 11°C
{840, 120}, // 12°C
{835, 130}, // 13°C
{830, 140}, // 14°C
{825, 150}, // 15°C
{820, 160}, // 16°C
{815, 170}, // 17°C
{810, 180}, // 18°C
{805, 190}, // 19°C
{800, 200}, // 20°C
{795, 210}, // 21°C
{790, 220}, // 22°C
{785, 230}, // 23°C
{780, 240}, // 24°C
{775, 250}, // 25°C
{770, 260}, // 26°C
{765, 270}, // 27°C
{760, 280}, // 28°C
{755, 290}, // 29°C
{750, 300}, // 30°C
{745, 310}, // 31°C
{740, 320}, // 32°C
{735, 330}, // 33°C
{730, 340}, // 34°C
{725, 350}, // 35°C
{720, 360}, // 36°C
{715, 370}, // 37°C
{710, 380}, // 38°C
{705, 390}, // 39°C
{700, 400}, // 40°C
{695, 410}, // 41°C
{690, 420}, // 42°C
{685, 430}, // 43°C
{680, 440}, // 44°C
{675, 450}, // 45°C
{670, 460}, // 46°C
{665, 470}, // 47°C
{660, 480}, // 48°C
{655, 490}, // 49°C
{650, 500}, // 50°C
{645, 510}, // 51°C
{640, 520}, // 52°C
{635, 530}, // 53°C
{630, 540}, // 54°C
{625, 550}, // 55°C
{620, 560}, // 56°C
{615, 570}, // 57°C
{610, 580}, // 58°C
{605, 590}, // 59°C
{600, 600}, // 60°C
{595, 610}, // 61°C
{590, 620}, // 62°C
{585, 630}, // 63°C
{580, 640}, // 64°C
{575, 650}, // 65°C
{570, 660}, // 66°C
{565, 670}, // 67°C
{560, 680}, // 68°C
{555, 690}, // 69°C
{550, 700}, // 70°C
{545, 710}, // 71°C
{540, 720}, // 72°C
{535, 730}, // 73°C
{530, 740}, // 74°C
{525, 750}, // 75°C
{520, 760}, // 76°C
{515, 770}, // 77°C
{510, 780}, // 78°C
{505, 790}, // 79°C
{500, 800}, // 80°C
{495, 810}, // 81°C
{490, 820}, // 82°C
{485, 830}, // 83°C
{480, 840}, // 84°C
{475, 850}, // 85°C
{470, 860}, // 86°C
{465, 870}, // 87°C
{460, 880}, // 88°C
{455, 890}, // 89°C
{450, 900}, // 90°C
{445, 910}, // 91°C
{440, 920}, // 92°C
{435, 930}, // 93°C
{430, 940}, // 94°C
{425, 950}, // 95°C
{420, 960}, // 96°C
{415, 970}, // 97°C
{410, 980}, // 98°C
{405, 990}, // 99°C
{400, 1000} // 100°C
};
#define TEMP_MAP_SIZE (sizeof(ratio_temp_map)/sizeof(RatioTempMap))
// AD转换初始化函数 - 优化配置减少电压波动影响
void ADC_Int(void)
{
volatile int i;
ANSELA4 = 0; // 模拟模式
TRISA4 = 0; // 输入模式
WPUA4 = 0; // 禁用上拉
WPDA4 = 0; // 禁用下拉
// 配置ADC - 使用更长的采样时间,提高转换精度
ADCON0 = ADC_CHANNEL_AN4 | 0x07; // 增加采样时间
// 使用内部2.0V参考电压,提供最稳定的ADC测量
// 内部2.0V参考电压比VDD更稳定,波动更小
// 与比率法结合使用,可以最大程度减少加热时电压波动的影响
ADCON1 = ADC_VREF_2V0 | ADC_CLOCK_FRC; // 内部2.0V参考电压,FRC时钟源
// 增加初始化延迟,确保ADC稳定
for(i = 0; i < 500; i++) asm("NOP");
// 预读几次ADC,确保稳定
// FRC时钟源需要等待一个指令周期后才能启动转换操作
for(i = 0; i < 3; i++) {
ADON = 1;
// 等待一个指令周期,确保FRC时钟源稳定
asm("NOP"); asm("NOP");
while(ADIF == 0) asm("NOP");
ADIF = 0;
// 读取ADC结果但不保存,只是为了稳定ADC
(void)(ADRESH << 8 | ADRESL);
}
// 再次延迟确保稳定
for(i = 0; i < 200; i++) asm("NOP");
}
// 读取ADC值函数 - 优化FRC时钟源,增强滤波,减少电压波动影响
u16 Read_ADC(void)
{
// 读取7次,去除最大最小值,取中间5次平均
u16 adc_values[7];
// 根据FRC时钟源特性调整采样间隔
// FRC时钟提供32us的稳定转换时间,不受系统时钟影响
for(u8 i = 0; i < 7; i++) {
ADON = 1; // 启动ADC转换
// 等待转换完成,FRC时钟源提供稳定的32us转换时间
while(ADIF == 0) asm("NOP");
ADIF = 0; // 清除中断标志
// 读取ADC结果
adc_values[i] = (ADRESH << 8) | ADRESL;
// 增加适当延迟,确保ADC完全稳定
// FRC时钟源比分频时钟更稳定,可以减少延迟
for(u8 j = 0; j < 5; j++) asm("NOP");
}
// 冒泡排序
for(u8 i = 0; i < 6; i++) {
for(u8 j = 0; j < 6-i; j++) {
if(adc_values[j] > adc_values[j+1]) {
u16 temp = adc_values[j];
adc_values[j] = adc_values[j+1];
adc_values[j+1] = temp;
}
}
}
// 去除最大最小值,取中间5次平均
u32 adc_sum = 0;
for(u8 i = 1; i < 6; i++) {
adc_sum += adc_values[i];
}
return (u16)(adc_sum / 5);
}
// 优化的比率法温度计算 - 使用内部2.0V参考电压增强稳定性
i16 Calculate_Temperature(u16 adc_val)
{
// 防止除零错误
if (adc_val == 0) adc_val = 1;
if (adc_val >= 1023) adc_val = 1022;
// 1. 计算分压比 × 1000 (整数运算)
// 使用内部2.0V参考电压,提供最稳定的测量
// 比率法结合内部参考电压,最大程度减少VDD波动影响
// ratio_x1000 = (adc_val * 1000) / ADC_RESOLUTION
u32 ratio_x1000 = ((u32)adc_val * 1000) / ADC_RESOLUTION;
// 2. 在映射表中查找最接近的分压比
u16 min_diff = 0xFFFF;
i16 closest_temp = 250; // 默认25°C
// 优化的查找算法:基于比率变化的特性进行范围预判
// NTCD的阻值随温度变化不是线性的,但比率变化在一定范围内是单调的
u8 start_index = 0;
u8 end_index = TEMP_MAP_SIZE - 1;
// 根据比率值预判温度范围,减少搜索范围
// 适配内部2.0V参考电压的ADC值范围
if (ratio_x1000 > 900) { // 高比率值对应低温
end_index = 20; // 限制在-20°C到0°C范围
} else if (ratio_x1000 > 650) { // 中等比率值对应中等温度
start_index = 15;
end_index = 50; // 限制在-5°C到30°C范围
} else { // 低比率值对应高温
start_index = 45; // 限制在25°C以上
}
// 在预判范围内查找最接近的分压比
for (u8 i = start_index; i <= end_index; i++) {
u16 diff = (ratio_x1000 > ratio_temp_map[i].ratio_x1000) ?
(ratio_x1000 - ratio_temp_map[i].ratio_x1000) :
(ratio_temp_map[i].ratio_x1000 - ratio_x1000);
if (diff < min_diff) {
min_diff = diff;
closest_temp = ratio_temp_map[i].temp_01c;
}
}
return closest_temp;
}
// 加热器初始化函数 - 改为简单的高低电平控制
void Heater_Int(void)
{
ANSELC7 = 1; // 1=数字模式
TRISC7 = 1; // 1=输出模式(根据HC18M003A芯片规格)
PORTC7 = 0; // 初始关闭加热器
// 不使用PWM功能,直接使用GPIO控制
heating_intensity = 0;
heating_on = 0;
}
// 加热器控制函数 - 改为简单的高低电平控制
void Heater_PWM_Control(void)
{
// 简单的高低电平控制:开启时输出高电平,关闭时输出低电平
if (heating_on == 1 && heating_intensity > 0) {
PORTC7 = 1; // 开启加热器,输出高电平
} else {
PORTC7 = 0; // 关闭加热器,输出低电平
}
}
// 加热器开启/关闭函数 - 简化版,使用高低电平控制
void Heater_On(void)
{
if (!heating_on) {
heating_on = 1;
heating_intensity = 100; // 设置强度值,确保条件判断通过
Heater_PWM_Control(); // 直接设置高电平
}
}
void Heater_Off(void)
{
if (heating_on) {
heating_on = 0;
heating_intensity = 0;
Heater_PWM_Control(); // 直接设置低电平
}
}
// 简单的温度控制函数 - 优化电压波动影响
void Control_Temperature(void)
{
static unsigned char check_interval = 0;
static u16 stable_adc_value = 512; // 稳定的ADC值,初始设为中值
static u8 heating_just_changed = 0; // 加热状态刚改变的标志
// 目标温度配置 (0.1°C单位)
i16 target_temp_01c = 0;
i16 hysteresis_temp_01c = 0; // 回差温度
switch(system_state)
{
case LED1_ON_STATE:
target_temp_01c = 430; // 43.0°C目标
hysteresis_temp_01c =380; // 40.0°C回差点
break;
case LED2_ON_STATE:
target_temp_01c = 480; // 48.0°C目标
hysteresis_temp_01c = 450; // 45.0°C回差点
break;
case LED3_ON_STATE:
target_temp_01c = 530; // 53.0°C目标
hysteresis_temp_01c = 500; // 50.0°C回差点
break;
default:
Heater_Off();
check_interval = 0;
heating_just_changed = 0;
return;
}
// 每0.5秒检查一次
check_interval++;
if(check_interval >= 5)
{
check_interval = 0;
u16 current_adc_value = Read_ADC();
i16 current_temp_01c = Calculate_Temperature(current_adc_value);
// 安全保护:超温立即停止
if(current_temp_01c >= 800) { // 80.0°C
Heater_Off();
return;
}
// *************** 电压波动处理策略 ***************
// 1. 如果刚开启或关闭加热器,延迟1.5秒再更新稳定ADC值
static u8 delay_counter = 0;
if(heating_just_changed) {
delay_counter++;
if(delay_counter >= 3) { // 1.5秒后(3*0.5秒)
heating_just_changed = 0;
delay_counter = 0;
stable_adc_value = current_adc_value; // 更新稳定值
}
// 在延迟期间,使用上一次的稳定ADC值计算温度
current_temp_01c = Calculate_Temperature(stable_adc_value);
}
// 2. 如果加热状态稳定,缓慢更新ADC值,避免电压突变影响
if(!heating_just_changed) {
// 使用加权平均,缓慢更新ADC值
stable_adc_value = (stable_adc_value * 7 + current_adc_value * 3) / 10;
}
// *************** 温度控制逻辑 ***************
if (system_state == LED1_ON_STATE) {
// 一档特殊逻辑:43度目标,40度开始补温
if(current_temp_01c <= 400) { // 降到40度开始补温
if (!heating_on) {
Heater_On();
heating_just_changed = 1; // 标记加热状态刚改变
}
} else if(current_temp_01c >= 430) { // 达到43度停止加热
if (heating_on) {
Heater_Off();
heating_just_changed = 1; // 标记加热状态刚改变
}
}
// 40-43度之间,保持当前状态,不做任何操作
} else {
// 二档三档通用逻辑
if(current_temp_01c <= hysteresis_temp_01c) { // 低于回差点
if (!heating_on) {
Heater_On();
heating_just_changed = 1; // 标记加热状态刚改变
}
} else if(current_temp_01c >= target_temp_01c) { // 高于目标点
if (heating_on) {
Heater_Off();
heating_just_changed = 1; // 标记加热状态刚改变
}
}
// 在回差点和目标点之间,保持当前状态,不做任何操作
}
}
}
// 安全检测函数
void Ratio_Safety_Check(u16 adc_value)
{
// 计算分压比 × 1000
u32 ratio_x1000 = ((u32)adc_value * 1000) / ADC_RESOLUTION;
// 1. NTC开路检测(比率接近1000)
if (ratio_x1000 > 980) {
Heater_Off();
}
// 2. NTC短路检测(比率接近0)
if (ratio_x1000 < 20) {
Heater_Off();
}
} 按我现在的程序,发热片加热时把电压拉低0.3V对adc读值有影响吗
最新发布