Embedded Systems

Xử lý tín hiệu ADC

Phân tích kỹ thuật luồng xử lý chuyển đổi ADC sang Nhiệt độ với độ chính xác cao.

Mô phỏng thực tế

Thông số cảm biến

MFP_7_103F3950AF Thermistor NTC 10k, B = 3950

Đây là cảm biến nhiệt analog.

System Logs Live Status: OK
// Hệ thống sẵn sàng...

Luồng Xử Lý Chi Tiết

1

Kiểm tra giới hạn cực đại/cực tiểu (Clamp)

Sử dụng tham số margin để bảo vệ đầu vào. Nếu ADC nằm quá xa dải dữ liệu thực tế (thí dụ ADC=0 hoặc ADC=5000), thuật toán trả về ngay giá trị nhiệt độ thấp nhất/cao nhất để tránh lỗi tính toán hoặc treo hệ thống.

2

Xử lý ngoại suy (Extrapolation)

Trong thực tế, linh kiện có thể bị sai số nhỏ (ví dụ ADC thấp hơn 55 một chút). Thuật toán không cắt bỏ mà dùng 2 điểm đầu (hoặc 2 điểm cuối) để tính một đường thẳng kéo dài, giúp giá trị nhiệt độ mượt mà hơn ở các vùng cận biên.

3

Tìm kiếm điểm kẹp (Binary Search)

Thay vì so sánh từng phần tử, thuật toán nhảy vào giữa mảng và thu hẹp dần vùng chứa ADC. Điều này cực kỳ quan trọng đối với vi điều khiển (như ESP32) để giảm thời gian chiếm dụng CPU. Kết quả trả về là 2 chỉ số i1i2 bao vây giá trị ADC.

4

Nội suy tuyến tính (Linear Interpolation)

Bảng dữ liệu chỉ có các điểm nguyên (°C). Bước cuối cùng tính toán tỷ lệ chính xác của ADC giữa 2 điểm kẹp để tìm ra giá trị nhiệt độ lẻ (ví dụ 25.43°C), tăng độ chính xác lên hàng trăm lần so với việc tra bảng đơn thuần.

Mã nguồn Python

# ===== ADC TABLE (Mỗi loại cảm biến khác nhau sẽ có bảng dữ liệu riêng) =====
ADC_DATA = {
    "MFP_7_103F3950AF": {
        "data": [
            55, 59, 64, 70, 76, 82, 88, 94, 106, 118, 130, 142, 154, 166, 180,
            195.6, 211.1, 226.6, 242.2, 257.8, 273.3, 290.9, 309.4, 330, 350.29,
            370.79, 395, 418, 441, 472, 506, 533, 557, 580, 606.54, 637, 682.15,
            758, 813.61, 846.18, 870, 907.02, 931, 968.01, 997, 1010, 1044, 1071,
            1100, 1135, 1165, 1195, 1235, 1275, 1313, 1365.11, 1417.72, 1462,
            1518.76, 1565.51, 1623.51, 1670.57, 1721.02, 1776, 1830, 1885, 1938.42,
            1981, 2028, 2060, 2104, 2140, 2183, 2229, 2260, 2290, 2320.93, 2350.92,
            2381, 2410, 2447.08, 2482.23, 2524.07, 2561.47, 2602.57, 2642.47, 2683.56,
            2725.95, 2766.1, 2803.85, 2844.91, 2878.75, 2916.59, 2947.67, 2967.67,
            3006.96, 3027.71, 3059.71, 3094.1, 3133.15, 3156.8, 3190.08, 3212.21,
            3235.4, 3262.43, 3288.91, 3312.9, 3341.81, 3363.02, 3387.59, 3411, 3435,
            3459.63, 3483.8, 3504.27, 3524.87, 3544.34, 3564.87, 3584.97, 3608.87,
            3633.77, 3653.45, 3675.84, 3698.62, 3720.27, 3740.61, 3761, 3781.43,
            3802, 3822.87, 3844, 3865, 3877.39, 3890, 3902.99, 3915.59, 3928.83,
            3941.27, 3953.23, 3966, 3978.87, 3991, 4003, 4015, 4027, 4040,
        ],
        "min": -40,
        "max": 105,
    }
}

def linear(x, x1, y1, x2, y2):
    if x2 == x1: return y1
    return y1 + (x - x1) * (y2 - y1) / (x2 - x1)

def find_bracket(x_data, target):
    left, right = 0, len(x_data) - 1
    if target == x_data[left]: return left, left
    if target == x_data[right]: return right, right
    while left <= right:
        mid = (left + right) // 2
        if x_data[mid] == target: return mid, mid
        elif x_data[mid] < target: left = mid + 1
        else: right = mid - 1
    return right, left

def cal_temperature(adc_value, sensor_type, margin=100):
    sensor = ADC_DATA[sensor_type]
    x_data, t_min, t_max = sensor["data"], sensor["min"], sensor["max"]
    
    # 1. Clamp
    if adc_value < x_data[0] - margin: return float(t_min)
    if adc_value > x_data[-1] + margin: return float(t_max)
    
    # 2. Ngoại suy biên
    if adc_value < x_data[0]:
        return round(linear(adc_value, x_data[0], t_min, x_data[1], t_min + 1), 2)
    if adc_value > x_data[-1]:
        return round(linear(adc_value, x_data[-2], t_max - 1, x_data[-1], t_max), 2)

    # 3. Tra bảng & Nội suy
    i1, i2 = find_bracket(x_data, adc_value)
    if i1 == i2: return float(t_min + i1)
    return round(linear(adc_value, x_data[i1], t_min + i1, x_data[i2], t_min + i2), 2)

# ===== TEST =====
if __name__ == "__main__":
    tests = [30, 55, 57, 1000, 4040, 4100, 5000]

    for v in tests:
        t = cal_temperature(v, "MFP_7_103F3950AF")
        print(f"ADC={v} => T={t}°C")

Mã nguồn C++ (ESP32 - Full Implementation)

#include <Arduino.h>

// Bảng dữ liệu ADC của cảm biến MFP_7_103F3950AF
const float ADC_TABLE[] = {
    55, 59, 64, 70, 76, 82, 88, 94, 106, 118, 130, 142, 154, 166, 180,
    195.6, 211.1, 226.6, 242.2, 257.8, 273.3, 290.9, 309.4, 330, 350.29,
    370.79, 395, 418, 441, 472, 506, 533, 557, 580, 606.54, 637, 682.15,
    758, 813.61, 846.18, 870, 907.02, 931, 968.01, 997, 1010, 1044, 1071,
    1100, 1135, 1165, 1195, 1235, 1275, 1313, 1365.11, 1417.72, 1462,
    1518.76, 1565.51, 1623.51, 1670.57, 1721.02, 1776, 1830, 1885, 1938.42,
    1981, 2028, 2060, 2104, 2140, 2183, 2229, 2260, 2290, 2320.93, 2350.92,
    2381, 2410, 2447.08, 2482.23, 2524.07, 2561.47, 2602.57, 2642.47, 2683.56,
    2725.95, 2766.1, 2803.85, 2844.91, 2878.75, 2916.59, 2947.67, 2967.67,
    3006.96, 3027.71, 3059.71, 3094.1, 3133.15, 3156.8, 3190.08, 3212.21,
    3235.4, 3262.43, 3288.91, 3312.9, 3341.81, 3363.02, 3387.59, 3411, 3435,
    3459.63, 3483.8, 3504.27, 3524.87, 3544.34, 3564.87, 3584.97, 3608.87,
    3633.77, 3653.45, 3675.84, 3698.62, 3720.27, 3740.61, 3761, 3781.43,
    3802, 3822.87, 3844, 3865, 3877.39, 3890, 3902.99, 3915.59, 3928.83,
    3941.27, 3953.23, 3966, 3978.87, 3991, 4003, 4015, 4027, 4040
};

const int TABLE_SIZE = sizeof(ADC_TABLE) / sizeof(ADC_TABLE[0]);
const int T_MIN = -40;

float linear_interp(float x, float x1, float y1, float x2, float y2) {
    if (abs(x2 - x1) < 0.001) return y1;
    return y1 + (x - x1) * (y2 - y1) / (x2 - x1);
}

float calculate_temperature(float adc_val, int margin = 100) {
    // 1. Clamp
    if (adc_val < ADC_TABLE[0] - margin) return (float)T_MIN;
    if (adc_val > ADC_TABLE[TABLE_SIZE - 1] + margin) return (float)(T_MIN + TABLE_SIZE - 1);

    // 2. Ngoại suy biên
    if (adc_val < ADC_TABLE[0])
        return linear_interp(adc_val, ADC_TABLE[0], T_MIN, ADC_TABLE[1], T_MIN + 1);
    if (adc_val > ADC_TABLE[TABLE_SIZE - 1])
        return linear_interp(adc_val, ADC_TABLE[TABLE_SIZE-2], T_MIN + TABLE_SIZE - 2, ADC_TABLE[TABLE_SIZE-1], T_MIN + TABLE_SIZE - 1);

    // 3. Binary Search
    int low = 0, high = TABLE_SIZE - 1;
    while (low <= high) {
        int mid = low + (high - low) / 2;
        if (abs(ADC_TABLE[mid] - adc_val) < 0.001) return (float)(T_MIN + mid);
        if (ADC_TABLE[mid] < adc_val) low = mid + 1;
        else high = mid - 1;
    }

    // 4. Nội suy (high và low là 2 điểm kẹp)
    return linear_interp(adc_val, ADC_TABLE[high], T_MIN + high, ADC_TABLE[low], T_MIN + low);
}

void setup() {
    Serial.begin(115200);
    float test_adc = 1000.0;
    Serial.print("ADC: "); Serial.print(test_adc);
    Serial.print(" => Temp: "); Serial.println(calculate_temperature(test_adc));
}

void loop() {}