/*********************************************************************************************************************
 * 文件名称：AD_DEAL.c
 * 芯片平台：STC8A8K64D4
 * 功能概述：ADC采集、滑动均值滤波、NTC热敏电阻温度换算、电流/水位模拟量换算
 * 采集方式：DMA多通道连续搬运ADC原始数据至DmaAdBuffer缓冲区
 * 调度时机：main.c 分时调度，每2ms执行一次AD_Deal()
 *
 * 重要说明：
 * 1、usSRegInBuf[] 为Modbus从机共享输入寄存器缓冲区，16位变量；
 *    51内核无法原子完成2字节读写，主循环写入 + Modbus中断读取会产生【数据撕裂】，
 *    现象：运行随机出现超大数值、负数。解决方案：所有赋值语句前后关闭/开启总中断EA，保证原子写入。
 * 2、NTC参数：B值配套阻值51K上拉电阻，测温量程表格 -10℃ ~ 100℃
 *    最终输出统一限位 0 ~ 11000（对应上位机：0=-10℃，11000=110℃）
 * 3、新增 adc_sample_cnt 采样计数器，配合main主循环实现DMA/ADC卡死自动重启检测
 * 4、ADC增加突变限幅滤波，抑制线路干扰造成数值跳变
 *
 * 修改记录：
 * [1] DmaAdBuffer、AD_Store 添加volatile，防止编译器优化DMA访问内存
 * [2] 增加adc_sample_cnt 用于ADC卡死看门狗检测
 * [3] 所有对usSRegInBuf[]赋值增加EA开关，实现16bit原子写入，解决随机异常数值
 * [4] v2优化：移除循环内频繁开关EA；先运算至局部缓存，末尾整块拷贝，防止长期运行死机、缓解从机串口阻塞
 * [5] 温度换算修复：移除超表格上限直接跳变11000逻辑；计算完成后统一钳位0~11000，消除调节断层跳变
 ********************************************************************************************************************/
#include "AD_DEAL.h"

// ---------------- NTC温度转换查表常量 (-10℃~100℃, 共111个点) ----------------
const uint32 Find_Const[111] = {
    568000, 536000, 506000, 478000, 451000, 426000, 403000, 381000, 360000, 341000,
    323000, 306000, 290000, 275000, 261000, 248000, 235000, 224000, 213000, 203000,

    193000, 184000, 176000, 168000, 160000, 153000, 146000, 140000, 134000, 128000,
    123000, 118000, 113000, 108000, 104000, 100000, 96100, 92400, 88900, 85600,

    82400, 79400, 76500, 73700, 71100, 68500, 66100, 63800, 61600, 59500,
    57500, 55500, 53700, 51900, 50200, 48600, 47000, 45500, 44100, 42700,

    41400, 40100, 38900, 37700, 36500, 35400, 34300, 33300, 32300, 31400,
    30500, 29600, 28700, 27900, 27100, 26300, 25600, 24900, 24200, 23500,

    22900, 22300, 21700, 21100, 20600, 20100, 19600, 19100, 18600, 18100,
    17700, 17200, 16800, 16400, 16000, 15600, 15200, 14800, 14500, 14100,

    13800, 13400, 13100, 12800, 12500, 12200, 11900, 11600, 11300, 11000,
    10800
};

/*********************************************************************************************************************
 * 本地常量
 ********************************************************************************************************************/
#define	ADC_CH		        13
#define	ADC_DATA	        36

// 滤波深度：4点滑动平均
#define FILT_DEPTH          4

// ADC原始值最大突变限制（0~4095）
#define ADC_MAX_STEP        22   // 根据噪声大小调整，越大响应越快，越小越稳

/*********************************************************************************************************************
 * 变量
 ********************************************************************************************************************/
u8 chn = 0;
volatile u8 xdata DmaAdBuffer[ADC_CH][ADC_DATA];   //【修改】添加volatile，DMA后台改写内存，禁止编译器优化
volatile uint16 AD_Store[13] = {0};                //【修改】添加volatile，存放解析完成单通道ADC原始值

uint32 adc_sample_cnt = 0;  //【新增】ADC采样帧计数，main循环用于DMA卡死巡检恢复

// 所有13路通道统一滤波缓存（只滤波原始ADC）
uint16 filt_buf[13][FILT_DEPTH] = {0};
uint16 filt_last[13] = {0};
uint8  filt_idx[13]  = {0};

/*********************************************************************************************************************
 * ADC 底层配置 【完全恢复你原版，宏名称不修改】
 ********************************************************************************************************************/
void	ADC_config(void)
{
	ADC_InitTypeDef		ADC_InitStructure;
	ADC_InitStructure.ADC_SMPduty   = 31;
	ADC_InitStructure.ADC_CsSetup   = 0;
	ADC_InitStructure.ADC_CsHold    = 1;
	ADC_InitStructure.ADC_Speed     = ADC_SPEED_2X16T;
	ADC_InitStructure.ADC_AdjResult = ADC_RIGHT_JUSTIFIED;
	ADC_Inilize(&ADC_InitStructure);
	ADC_PowerControl(ENABLE);
	NVIC_ADC_Init(DISABLE,Priority_0);
}

void	DMA_config(void)
{
	DMA_ADC_InitTypeDef		DMA_ADC_InitStructure;
	DMA_ADC_InitStructure.DMA_Enable = ENABLE;
	DMA_ADC_InitStructure.DMA_Channel = 0x7f3f;
	DMA_ADC_InitStructure.DMA_Buffer = DmaAdBuffer;
	DMA_ADC_InitStructure.DMA_Times = ADC_16_Times;
	DMA_ADC_Inilize(&DMA_ADC_InitStructure);
	NVIC_DMA_ADC_Init(ENABLE,Priority_0,Priority_0);
	DMA_ADC_TRIG();
}

/*********************************************************************************************************************
 * 函数：adc_read_ch
 * 功能：从DMA大块缓冲区提取13路ADC数据，拼装成16bit存入AD_Store[]
 * !!!【原样恢复你原版内容，删除内部DmaADCFlag赋值，杜绝 not an lvalue报错】
 ********************************************************************************************************************/
void adc_read_ch(void)
{
	AD_Store[0]=((u16)DmaAdBuffer[4][10] << 8) | DmaAdBuffer[4][11];   // TH01
	AD_Store[1]=((u16)DmaAdBuffer[2][10] << 8) | DmaAdBuffer[2][11];   // TH02
	AD_Store[2]=((u16)DmaAdBuffer[0][10] << 8) | DmaAdBuffer[0][11];   // TH03
	AD_Store[3]=((u16)DmaAdBuffer[10][10] << 8) | DmaAdBuffer[10][11]; // TH04
	AD_Store[4]=((u16)DmaAdBuffer[9][10] << 8) | DmaAdBuffer[9][11];   // TH05
	AD_Store[5]=((u16)DmaAdBuffer[8][10] << 8) | DmaAdBuffer[8][11];
	AD_Store[6]=((u16)DmaAdBuffer[7][10] << 8) | DmaAdBuffer[7][11];
	AD_Store[7]=((u16)DmaAdBuffer[6][10] << 8) | DmaAdBuffer[6][11];
	AD_Store[8]=((u16)DmaAdBuffer[3][10] << 8) | DmaAdBuffer[3][11];
	AD_Store[9]=((u16)DmaAdBuffer[1][10] << 8) | DmaAdBuffer[1][11];
	AD_Store[10]=((u16)DmaAdBuffer[12][10] << 8)| DmaAdBuffer[12][11];
	AD_Store[11]=((u16)DmaAdBuffer[11][10] << 8)| DmaAdBuffer[11][11];
	AD_Store[12]=((u16)DmaAdBuffer[5][10] << 8) | DmaAdBuffer[5][11];
}

/*********************************************************************************************************************
 * 函数：ADC_Filter
 * 功能：单通道ADC滑动窗口均值滤波 + 数值突变限幅，抑制尖峰干扰
 * 输入：ch通道号，val原始ADC值
 * 返回：平滑后ADC数值
 ********************************************************************************************************************/
uint16 ADC_Filter(uint8 ch, uint16 val)
{
    uint32 sum = 0;
    uint8 i;
    int32 diff;
    uint16 avg;
    uint16 safe_val;

    if(ch >= 13)
        return val;

    if(val > 4095) val = 4095;
    if(val < 0)    val = 0;

    if(filt_last[ch] == 0)
        filt_last[ch] = val;

    if(filt_idx[ch] >= FILT_DEPTH)
        filt_idx[ch] = 0;

    diff = (int32)val - filt_last[ch];
    safe_val = val;
    if(diff >  ADC_MAX_STEP) safe_val = filt_last[ch] + ADC_MAX_STEP;
    if(diff < -ADC_MAX_STEP) safe_val = filt_last[ch] - ADC_MAX_STEP;

    filt_buf[ch][filt_idx[ch]] = safe_val;
    filt_idx[ch]++;

    for(i = 0; i < FILT_DEPTH; i++)
        sum += filt_buf[ch][i];
    avg = sum / FILT_DEPTH;

    if(avg > 4095) avg = 4095;
    if(avg < 0)    avg = 0;

    filt_last[ch] = avg;
    return avg;
}
/*********************************************************************************************************************
 * 函数：AD_Deal
 * 调度周期：2ms执行一次（main分时调度）
 * 算法说明：NTC热敏电阻查表插值法(线性遍历查表，非二分查找)，通过电阻匹配温度表，相邻点线性插值得到温度
 * v2优化：
 * 1、全部运算先存入局部临时数组tmp_reg，不直接操作共享usSRegInBuf
 * 2、移除循环内部频繁EA开关，规避长期运行中断拥堵死机
 * 3、运算完成后整块原子拷贝
 ********************************************************************************************************************/
void AD_Deal(void)
{
    uint8 i, const_i;
    uint32 resistance;
    uint16 raw_adc;
    uint32 adc_val, denominator;
    static uint16 tmp_reg[13];  // static移出栈空间
    int32 num, den;
    int32 temp_out;
	  int32 total_val;

    if(DmaADCFlag)
    {
        adc_sample_cnt++;
        adc_read_ch();

        // ===================== 0~9 温度通道 完全沿用你原始查表逻辑 =====================
        for (i = 0; i < 10; i++)
				{
					//	raw_adc = ADC_Filter(i, AD_Store[i]);
					// raw_adc = ADC_Filter(i, AD_Store[i]);
            raw_adc = AD_Store[i];  
						temp_out = 0; // 关键：每一路计算前清零，消除上一轮累加残留

						adc_val = raw_adc;
						denominator = 4096UL - adc_val;
						if(denominator == 0)
								denominator = 1;

						resistance = (adc_val * 51000UL) / denominator;

						const_i = 0;
						while (const_i < 110 && resistance < Find_Const[const_i])
						{
								const_i++;
								//====新增：查表循环内喂狗，防止长循环卡死看门狗====
								if((const_i % 30) == 0)
								{
										WDT_Feed();
								}
						}

						if (const_i != 0)
						{
								temp_out = (const_i - 1) * 100;
								num = (int32)Find_Const[const_i-1] - (int32)resistance;
								den = (int32)Find_Const[const_i-1] - (int32)Find_Const[const_i];
								if(den == 0) den = 1; // 防止除0
								if(num > 0 && den > 0)
								{
										temp_out += (int16)(num * 100UL / den);
								}
						}

						// 单次计算值限位
						if(temp_out > 11000) temp_out = 11000;
						if(temp_out < 0)     temp_out = 0;

						// 类型安全相加偏移
						total_val = (int32)temp_out + (int16)usSRegHoldBuf[0xC0 + i];

						// 相加后二次限位
						if(total_val > 11000) total_val = 11000;
						if(total_val < 0)     total_val = 0;

						tmp_reg[i] = (uint16)total_val;

						WDT_Feed();
				}

        // ===================== 10~11 电流通道 =====================
        for (i = 10; i < 12; i++)
        {
            raw_adc = ADC_Filter(i, AD_Store[i]);
            tmp_reg[i] = (uint16)(((raw_adc * 10000UL / 4096UL) * 10UL / 100UL)+usSRegHoldBuf[0xC0+i]);
            WDT_Feed();
        }

        // ===================== 12 水位通道 =====================
        raw_adc = ADC_Filter(12, AD_Store[12]);
        tmp_reg[12] = (uint16)(((raw_adc * 9UL / 2UL / 4096UL) * 100UL)+usSRegHoldBuf[0xCC]);
        WDT_Feed();

        //==================== 整块关闭中断原子拷贝 ====================
        WDT_Feed();
        EA = 0;
        usSRegInBuf[0]  = tmp_reg[0];
        usSRegInBuf[1]  = tmp_reg[1];
        usSRegInBuf[2]  = tmp_reg[2];
        usSRegInBuf[3]  = tmp_reg[3];
        usSRegInBuf[4]  = tmp_reg[4];
        usSRegInBuf[5]  = tmp_reg[5];
        usSRegInBuf[6]  = tmp_reg[6];
        usSRegInBuf[7]  = tmp_reg[7];
        usSRegInBuf[8]  = tmp_reg[8];
        usSRegInBuf[9]  = tmp_reg[9];
        usSRegInBuf[10] = tmp_reg[10];
        usSRegInBuf[11] = tmp_reg[11];
        usSRegInBuf[12] = tmp_reg[12];
        EA = 1;

        DmaADCFlag = 0;
        DMA_ADC_TRIG();
        WDT_Feed();
    }
}