Akıllı lojik prob

Bu projeyi, klasik lojik probların sade işlevini mikrodenetleyici gücüyle birleştirerek, kompakt ve akıllı bir test probu oluşturmak amacı ile hazırladım. ATtiny1606 mikroişlemci, SSD1306 (128×32) oled ekran ve birkaç direnç ve mosfetten oluşan devre 3.7V li-po pil ile besleniyor.

110 kΩ / 10 kΩ gerilim bölücü dirençler ile ölçüm aralığı; 0-30V’tur. Dahili 2.5V referans kullanıldığından ölçüm sonuçları besleme voltajına bağlı değişiklik göstermez. Ölçüm hassasiyeti 1mV’tur.

Akıllı prob özellikleri

  • Desteklenen lojik seviyeler (modlar):
  • TTL (5 V)
  • CMOS 1.8 V
  • CMOS 2.5 V
  • CMOS 3.3 V
  • CMOS 5.0 V

Her mod, kendi eşik değerlerine göre LOW / HIGH / UNDEF olarak sınıflandırılır.
“UNDEF” bölgesi kararsız girişleri tespit etmeye yardımcı olur.

  • Giriş 6 V üzerinde olduğunda ekranda “HV” (high voltage) uyarısı çıkar.
  • Okumayı kolaylaştırmak için ölçülen voltaj 0.1 V hassasiyetle ekranda gösterilir.
  • HIGH veya HV algılandığında ekranda kilit simgesi belirir. SIL düğmesi ile silene kadar ekranda sabit kalır. Kısa süreli palsleri yakalamanıza yardımcı olur.
  • MOD: TTL/CMOS modları arasında geçiş yapmanızı sağlar. Uzun basılırsa kalibrasyon menüsü açılır.
  • SIL: Kilit simgesini sıfırlama ve kalibrasyon onayı için kullanılır.
  • ON: probu çalıştırır. 2 dakika kullanılmadığında prob batarya voltajını keserek kendini kapatır.
  • MOD butonuna uzun basıldığında kalibrasyon modu devreye girer; prob uçlarına bağlayacağınız 5V referansa göre kendini kalibre eder ve EEPROM’a kaydeder. (Kalibrasyon için, kalibrasyon modunda; prob uçlarına 5V standart (USB gibi) güç kaynağı bağlayın ve SIL tuşuna basın.)

Program kodu

Arduino “megaTinyCore” kütüphanesi kullanılarak oluşturuldu. UPDI ile programlamak için aşağıdaki yazımıza gözatabilirsiniz.

Akıllı lojik prob program kodu;

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// ---------- Güç Yönetimi ----------
#define POWER_HOLD_PIN PIN_PA5
const unsigned long AUTO_OFF_MS = 120000UL;
unsigned long lastActivity = 0;

// ---------- ADC Referans ----------
// DİKKAT: 2.5V referans kullanıldığı için pil voltajı (VCC) 2.7V altına düşmemelidir.
#define ADC_REF_MV 2500.0f  // Dahili 2.5V referans

// ---------- Giriş bölücü ----------
// 10k toprak hattında, 110k prob ucunda (Toplam 120k)
#define K_DIV_PROBE (10000.0f / (110000.0f + 10000.0f))  // 10k / 120k ≈ 0.0833

#define PROBE_ADC_PIN PIN_PA7
#define BTN_MOD PIN_PA0
#define BTN_SIL PIN_PA1
#define CAL_TARGET_MV 5000.0f

// ---------- Lojik Modları ----------
enum LogicMode { TTL_MODE, CMOS18_MODE, CMOS25_MODE, CMOS33_MODE, CMOS50_MODE };
enum Level { LV_LOW, LV_UNDEF, LV_HIGH };

#define CMOS_LOW_FRAC   0.30f
#define CMOS_HIGH_FRAC  0.60f

// ---------- Buton yapısı ----------
struct Btn {
    uint8_t pin;
    bool lastStable = HIGH;
    bool lastRead = HIGH;
    unsigned long lastChange = 0;
    bool pressed = false;
    unsigned long pressTime = 0;
};
Btn btnMod{BTN_MOD}, btnSil{BTN_SIL};
const unsigned long DEBOUNCE_MS = 30;
const unsigned long LONGPRESS_MS = 800;

// ---------- Kalibrasyon ----------
struct CalData {
    float scale_k;
    float offset_mv;
    uint8_t crc;
};
CalData cal = {1.0f, 0.0f, 0};

uint8_t crc8(const CalData& c) {
    const uint8_t* p = (const uint8_t*)&c;
    uint8_t s = 0;
    for (size_t i = 0; i < sizeof(CalData) - 1; i++) s += p[i];
    return s;
}
void calLoad() {
    EEPROM.get(0, cal);
    if (isnan(cal.scale_k) || fabs(cal.scale_k) > 10.0f || cal.crc != crc8(cal)) {
        cal = {1.0f, 0.0f, 0};
        cal.crc = crc8(cal);
        EEPROM.put(0, cal);
    }
}
void calSave() { cal.crc = crc8(cal); EEPROM.put(0, cal); }

// ---------- Buton İşlevleri ----------
void btnInit(Btn& b) {
    pinMode(b.pin, INPUT_PULLUP);
    b.lastStable = b.lastRead = digitalRead(b.pin);
    b.lastChange = millis();
}
bool btnFallingEdge(Btn& b) {
    bool now = digitalRead(b.pin);
    if (now != b.lastRead) { b.lastRead = now; b.lastChange = millis(); }
    if ((millis() - b.lastChange) > DEBOUNCE_MS && b.lastStable != b.lastRead) {
        bool prev = b.lastStable;
        b.lastStable = b.lastRead;
        if (prev == HIGH && b.lastStable == LOW) { b.pressed = true; b.pressTime = millis(); return true; }
        if (prev == LOW && b.lastStable == HIGH) b.pressed = false;
    }
    return false;
}
bool btnLongPress(Btn& b) {
    if (b.pressed && (millis() - b.pressTime) >= LONGPRESS_MS) { b.pressed = false; return true; }
    return false;
}

// ---------- ADC ----------
uint16_t adcReadAveraged(uint8_t pin, uint8_t n = 8) {
    uint32_t acc = 0;
    // İlk okumayı atla (ADC kanal değişimi stabilizasyonu için)
    analogRead(pin); 
    for (uint8_t i = 0; i < n; i++) { 
        acc += analogRead(pin); 
        delayMicroseconds(50); 
    }
    return acc / n;
}
float adcToMv(uint16_t raw) { return (raw * ADC_REF_MV) / 1023.0f; }

float probeVin_mv() {
    uint16_t raw = adcReadAveraged(PROBE_ADC_PIN, 8);
    float vout = adcToMv(raw);
    return (vout / K_DIV_PROBE) * cal.scale_k + cal.offset_mv;
}

// ---------- Seviye Sınıflandırma ----------
Level classifyTTL(float mv) {
    if (mv < 800.0f) return LV_LOW;
    if (mv > 2000.0f) return LV_HIGH;
    return LV_UNDEF;
}

Level classifyCMOS(float mv, float vdd_mv) {
    if (mv < CMOS_LOW_FRAC * vdd_mv) return LV_LOW;
    if (mv > CMOS_HIGH_FRAC * vdd_mv) return LV_HIGH;
    return LV_UNDEF;
}

// ---------- Yardımcı Fonksiyonlar ----------
LogicMode nextMode(LogicMode m) {
    switch (m) {
        case TTL_MODE:    return CMOS18_MODE;
        case CMOS18_MODE: return CMOS25_MODE;
        case CMOS25_MODE: return CMOS33_MODE;
        case CMOS33_MODE: return CMOS50_MODE;
        case CMOS50_MODE: return TTL_MODE;
    }
    return TTL_MODE;
}

const __FlashStringHelper* modeLabel(LogicMode m) {
    switch (m) {
        case TTL_MODE:    return F("TTL");
        case CMOS18_MODE: return F("CM1.8");
        case CMOS25_MODE: return F("CM2.5");
        case CMOS33_MODE: return F("CM3.3");
        case CMOS50_MODE: return F("CM5.0");
    }
    return F("?");
}

// ---------- Ekran Yardımcıları ----------
void drawLock(bool show) {
    if (show) {
        display.fillRect(110, 0, 12, 12, SSD1306_WHITE);
        display.fillRect(113, 4, 6, 6, SSD1306_BLACK);
    }
}
void printVoltage(float mv) {
    uint16_t tenths = (uint16_t)((mv + 50) / 100); // 0.1V çözünürlük
    uint8_t whole = tenths / 10;
    uint8_t frac = tenths % 10;
    display.print(whole);
    display.print('.');
    display.print(frac);
    display.print(F("V"));
}
void drawUI(Level lv, float vin_mv, bool showLock, LogicMode mode) {
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);
    display.setTextSize(2);
    display.setCursor(0, 0);
    bool hv = (vin_mv > 6000.0f);
    if (hv) display.print(F("HV "));
    else if (lv == LV_LOW) display.print(F("LOW "));
    else if (lv == LV_HIGH) display.print(F("HIGH"));
    else display.print(F("UNDEF"));
    drawLock(showLock);

    display.setTextSize(2);
    display.setCursor(0, 16);
    printVoltage(vin_mv);
    display.print(F(" "));

    display.setTextSize(1);
    display.setCursor(96, 24);
    display.print(modeLabel(mode));
    display.display();
}
void drawCalibUI(float vin_mv) {
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);
    display.setTextSize(1);
    display.setCursor(0, 0);
    display.print(F("CAL "));
    printVoltage(vin_mv);

    display.setCursor(0, 12);
    display.print(F("Bagla: 5.00V"));

    display.setCursor(0, 22);
    display.print(F("SIL=Kaydet MOD=Iptal"));
    display.display();
}

// ---------- Değişkenler ----------
LogicMode logicMode = TTL_MODE;
bool lockIconVisible = false;
bool calibMode = false;
unsigned long calibMsgTick = 0;
const unsigned long CALIB_MSG_MS = 1500;
Level prevLv = LV_UNDEF;
bool prevHv = false;

// ---------- Kurulum ----------
void setup() {
    Wire.begin();
    display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
    display.clearDisplay(); display.display();

    pinMode(POWER_HOLD_PIN, OUTPUT);
    digitalWrite(POWER_HOLD_PIN, HIGH);

    // --- DÜZELTİLEN KISIM BAŞLANGICI ---
    // MegaTinyCore için ADC referansını 2.5V olarak ayarla.
    // Bu ayar sayesinde pil voltajı 3.3V'dan 2.8V'a düşse bile 
    // ADC referansı 2.5V'da sabit kalır ve ölçüm bozulmaz.
    analogReference(INTERNAL2V5);
    // --- DÜZELTİLEN KISIM SONU ---

    calLoad();
    btnInit(btnMod);
    btnInit(btnSil);

    display.setTextSize(2);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0, 0);
    display.print(F("Logic Prob"));
    display.setTextSize(1);
    display.setCursor(40, 24);
    display.print(F("v1.20")); 
    display.display();
    delay(2000);

    lastActivity = millis();
    calibMsgTick = 0;
}

// ---------- Ana Döngü ----------
void loop() {
    bool modEdge = btnFallingEdge(btnMod);
    bool silEdge = btnFallingEdge(btnSil);

    if (modEdge && !calibMode) {
        logicMode = nextMode(logicMode);
        lastActivity = millis();
    }

    if (btnLongPress(btnMod)) {
        calibMode = true;
        lastActivity = millis();
    }

    float vin_mv = probeVin_mv();

    Level lv;
    switch (logicMode) {
        case TTL_MODE:    lv = classifyTTL(vin_mv); break;
        case CMOS18_MODE: lv = classifyCMOS(vin_mv, 1800.0f); break;
        case CMOS25_MODE: lv = classifyCMOS(vin_mv, 2500.0f); break;
        case CMOS33_MODE: lv = classifyCMOS(vin_mv, 3300.0f); break;
        case CMOS50_MODE: lv = classifyCMOS(vin_mv, 5000.0f); break;
    }

    bool hv = (vin_mv > 6000.0f);

    if (!calibMode) {
        if ((lv == LV_HIGH || hv) && !lockIconVisible) {
            lockIconVisible = true;
            lastActivity = millis();
        }
        if (lv != prevLv || hv != prevHv) {
            lastActivity = millis();
            prevLv = lv;
            prevHv = hv;
        }
    }

    if (silEdge) {
        if (calibMode) {
            uint16_t raw = adcReadAveraged(PROBE_ADC_PIN, 16);
            float v_meas = (adcToMv(raw) / K_DIV_PROBE);
            if (v_meas > 1.0f) {
                cal.scale_k = CAL_TARGET_MV / v_meas;
                calSave();
            }
            calibMode = false;
            calibMsgTick = millis();
        } else {
            lockIconVisible = false;
        }
        lastActivity = millis();
    }

    if (calibMode) {
        drawCalibUI(vin_mv);
    } else {
        drawUI(lv, vin_mv, lockIconVisible, logicMode);
        if (calibMsgTick > 0 && (millis() - calibMsgTick) < CALIB_MSG_MS) {
            display.setTextSize(1);
            display.setCursor(0, 24);
            display.print(F("Kalib. kaydedildi"));
            display.display();
        }
    }

    if ((millis() - lastActivity) > AUTO_OFF_MS) {
        display.ssd1306_command(SSD1306_DISPLAYOFF);
        delay(10);
        pinMode(POWER_HOLD_PIN, INPUT);
        while (1) { }
    }

    delay(60);
}
Erdoğan Demirtaş
Erdoğan Demirtaş
Cumhuriyet Üniversitesi, Sivas Meslek Yüksekokulu, Endüstriyel Elektronik bölümü mezunuyum. Endüstriyel alanda kullanılan (medikal, telekomünikasyon, savunma sanayi, tekstil, cnc, rafineri, matbaa vb.) elektronik kartların/cihazların bakım ve onarımını yapmaktayım.
ilgili yazılar

Yorum Bırak

Lütfen yorumunuzu giriniz!
Lütfen isminizi buraya giriniz

son eklenenler