الرئيسية قاعدة المعرفة الكهرباء والإلكترون ADC وقراءة المستشعرات التناظرية: تحويل العالم الحقيقي لأرقام
الكهرباء والإلكترون

ADC وقراءة المستشعرات التناظرية: تحويل العالم الحقيقي لأرقام

كيف يعمل محوّل التناظري إلى الرقمي ADC

في العالم الحقيقي، الإشارات تناظرية: درجة الحرارة تتغير بشكل مستمر، وكذلك التيار والضغط. محوّل ADC (Analog-to-Digital Converter) يحوّل هذه الإشارات المستمرة إلى أرقام يفهمها المتحكم.

العملية بسيطة: يقارن ADC الجهد على الطرف بجهد مرجعي (عادة 3.3V)، ويُخرج رقماً يتناسب مع النسبة بينهما.

المعادلة الأساسية

القيمة الرقمية = (جهد الدخل / جهد المرجع) × (2^الدقة - 1)

مثلاً: جهد 1.65V مع مرجع 3.3V ودقة 12-bit:

القيمة = (1.65 / 3.3) × 4095 = 2047

الدقة والعينات: 10-bit مقابل 12-bit

دقة ADC تحدد عدد المستويات التي يمكنه تمييزها:

الدقة عدد المستويات أصغر تغيير (3.3V مرجع)
10-bit 1024 3.22 mV
12-bit 4096 0.81 mV
16-bit 65536 0.05 mV

في التطبيقات الصناعية، 12-bit كافية لمعظم قراءات المستشعرات. الدقة الأعلى تحتاجها فقط في القياسات الدقيقة جداً.

معدل العينات (Sample Rate)

كم مرة يقرأ ADC القيمة في الثانية. لقراءة حرارة محرك تتغير ببطء، 10 عينات/ثانية كافية. لقياس اهتزاز، قد تحتاج 10,000 عينة/ثانية أو أكثر.

قراءة مستشعر حرارة NTC Thermistor

مقاومة NTC Thermistor تتغير عكسياً مع الحرارة. نربطها في دائرة مقسم جهد لتحويل التغير في المقاومة إلى تغير في الجهد يقرأه ADC.

#include "stm32f4xx_hal.h"
#include <math.h>

#define ADC_RESOLUTION   4095.0f
#define V_REF            3.3f
#define R_FIXED          10000.0f  // مقاومة ثابتة 10KΩ
#define B_COEFFICIENT    3950.0f   // معامل B للـ NTC
#define R_NOMINAL        10000.0f  // مقاومة NTC عند 25°C
#define T_NOMINAL        298.15f   // 25°C بالكلفن

ADC_HandleTypeDef hadc1;

void adc_init(void) {
    __HAL_RCC_ADC1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitTypeDef gpio = {0};
    gpio.Pin = GPIO_PIN_0;
    gpio.Mode = GPIO_MODE_ANALOG;
    HAL_GPIO_Init(GPIOA, &gpio);

    hadc1.Instance = ADC1;
    hadc1.Init.Resolution = ADC_RESOLUTION_12B;
    hadc1.Init.ScanConvMode = DISABLE;
    hadc1.Init.ContinuousConvMode = DISABLE;
    HAL_ADC_Init(&hadc1);

    ADC_ChannelConfTypeDef config = {0};
    config.Channel = ADC_CHANNEL_0;
    config.Rank = 1;
    config.SamplingTime = ADC_SAMPLETIME_84CYCLES;
    HAL_ADC_ConfigChannel(&hadc1, &config);
}

uint16_t adc_read(void) {
    HAL_ADC_Start(&hadc1);
    HAL_ADC_PollForConversion(&hadc1, 10);
    return HAL_ADC_GetValue(&hadc1);
}

float ntc_to_celsius(uint16_t adc_value) {
    float voltage = (adc_value / ADC_RESOLUTION) * V_REF;
    float resistance = R_FIXED * voltage / (V_REF - voltage);
    float steinhart = log(resistance / R_NOMINAL) / B_COEFFICIENT;
    steinhart += 1.0f / T_NOMINAL;
    return (1.0f / steinhart) - 273.15f;
}

قراءة مستشعر تيار ACS712

مستشعر ACS712 يقيس التيار المتردد أو المستمر بدون تلامس، ويُخرج جهداً يتناسب مع التيار. عند تيار صفر، يُخرج نصف جهد التغذية (2.5V). النسخة 20A تعطي 100mV لكل أمبير.

#define ACS712_ZERO_CURRENT_V   2.5f
#define ACS712_SENSITIVITY      0.100f  // 100mV/A للنسخة 20A

float read_current_amps(void) {
    uint16_t raw = adc_read();
    float voltage = (raw / ADC_RESOLUTION) * V_REF;
    float current = (voltage - ACS712_ZERO_CURRENT_V) / ACS712_SENSITIVITY;
    return current;
}

ملاحظة مهمة للتيار المتردد

عند قياس AC، تحتاج أخذ عدة عينات خلال دورة كاملة (20ms لتردد 50Hz) وحساب القيمة الفعالة (RMS):

float read_current_rms(uint16_t samples_per_cycle) {
    float sum_sq = 0.0f;
    for (uint16_t i = 0; i < samples_per_cycle; i++) {
        float current = read_current_amps();
        sum_sq += current * current;
        HAL_Delay(20 / samples_per_cycle);  // توزيع العينات على دورة
    }
    return sqrtf(sum_sq / samples_per_cycle);
}

التشويش والتصفية: المتوسط المتحرك

في البيئة الصناعية، التشويش الكهربائي من المحركات والمحولات يؤثر على قراءات ADC. أبسط طريقة للتصفية هي المتوسط المتحرك (Moving Average).

#define FILTER_SIZE 16

typedef struct {
    uint16_t buffer[FILTER_SIZE];
    uint8_t  index;
    uint32_t sum;
    uint8_t  filled;
} MovingAverage;

void filter_init(MovingAverage *f) {
    f->index = 0;
    f->sum = 0;
    f->filled = 0;
}

uint16_t filter_update(MovingAverage *f, uint16_t new_value) {
    if (f->filled) {
        f->sum -= f->buffer[f->index];
    }
    f->buffer[f->index] = new_value;
    f->sum += new_value;
    f->index = (f->index + 1) % FILTER_SIZE;
    if (f->index == 0) f->filled = 1;

    uint8_t count = f->filled ? FILTER_SIZE : f->index;
    return (uint16_t)(f->sum / count);
}

مرشح EMA (أبسط وأقل ذاكرة)

المتوسط الأُسي المتحرك (Exponential Moving Average) يحتاج متغيراً واحداً فقط:

float ema_update(float prev, float new_val, float alpha) {
    return alpha * new_val + (1.0f - alpha) * prev;
}
// alpha = 0.1 تصفية قوية، 0.5 تصفية معتدلة

مثال عملي: قراءة 4 مستشعرات تناظرية وعرضها

نظام يقرأ حرارة محرك، تيار محرك، ضغط هيدروليكي، ومستوى خزان، ويرسل القراءات عبر UART.

typedef struct {
    float motor_temp_c;
    float motor_current_a;
    float pressure_bar;
    float tank_level_pct;
} SensorReadings;

MovingAverage filters[4];

float adc_to_pressure(uint16_t raw) {
    // مستشعر 4-20mA محوّل لـ 1-5V، مدى 0-10 بار
    float v = (raw / ADC_RESOLUTION) * V_REF;
    return (v - 1.0f) * 10.0f / 4.0f;
}

float adc_to_level(uint16_t raw) {
    float v = (raw / ADC_RESOLUTION) * V_REF;
    return (v / V_REF) * 100.0f;
}

SensorReadings read_all_sensors(void) {
    SensorReadings r;
    uint16_t raw;

    raw = filter_update(&filters[0], read_adc_channel(0));
    r.motor_temp_c = ntc_to_celsius(raw);

    raw = filter_update(&filters[1], read_adc_channel(1));
    float v = (raw / ADC_RESOLUTION) * V_REF;
    r.motor_current_a = (v - 2.5f) / 0.1f;

    raw = filter_update(&filters[2], read_adc_channel(2));
    r.pressure_bar = adc_to_pressure(raw);

    raw = filter_update(&filters[3], read_adc_channel(3));
    r.tank_level_pct = adc_to_level(raw);

    return r;
}

int main(void) {
    HAL_Init();
    adc_init();
    uart_init(115200);
    for (int i = 0; i < 4; i++) filter_init(&filters[i]);

    while (1) {
        SensorReadings s = read_all_sensors();
        printf("T=%.1fC I=%.2fA P=%.1fbar L=%.0f%%\r\n",
               s.motor_temp_c, s.motor_current_a,
               s.pressure_bar, s.tank_level_pct);
        HAL_Delay(100);
    }
}

الخلاصة

محوّل ADC هو جسر المتحكم نحو العالم التناظري. فهم دقته وحدوده، واستخدام تقنيات التصفية المناسبة، ضروري لبناء أنظمة قياس صناعية موثوقة. في الدرس القادم سنتعلم بروتوكولات الاتصال UART و SPI و I2C للتواصل مع المستشعرات الرقمية والأجهزة الأخرى.

ADC analog sensor-reading resolution calibration noise المحوّل التناظري قراءة المستشعرات الدقة المعايرة التشويش العينات