Napadl mne jeden smysluplný projekt pro borce Arduinisty. Je to projekt, kde je málo vedlejšího hardware a hlavně to stojí a padá se software. Možno použít Arduino. Kromě Arduina potřebujete ještě:
3 kusy odporů
3 kusy kondenzátorů
3 kusy signálových diod
1 kus displej, doporučuju barevný grafický
1 kus kovové krabičky
2 kusy konektorů pro připojení napájení a pro připojení testované součástky.
To je všechno. Když vhodně a šikovně zapojíte ty tři odpory a tři kondenzátory a tři diody k Božskému Arduinu a sestavíte software pro Božské Arduino, můžete měřit impedanci obvodů a součástek a zobrazovat závislost impedance do grafů.
Kterýpak borec Arduinista toto dokáže vyřešit?
Arduino jako měřič impedance
Arduino jako měřič impedance
S časem se vše zhoršuje (zákon prof. Parkinsona)

Re: Arduino jako měřič impedance
Schéma zapojení:

Arduino program:

Arduino program:
Kód: Vybrat vše
/*
Arduino Impedancni Analyzator v1.0.0
Deska: Arduino UNO R4 Minima / UNO R4 WiFi (nutny skutecny DAC na A0)
Displej: ILI9341 320x240 SPI, knihovny Adafruit_GFX + Adafruit_ILI9341
Zapojeni, ktere tento program ocekava:
A0/DAC OUT ---- VIN ---- RREF 1k ---- VOUT ---- Zx ---- GND
| |
A1 A2
TFT ILI9341:
CS -> D10
DC -> D9
RST -> D8
MOSI/SCK -> hardwarove SPI piny desky
DULEZITE:
- Vstupy A1 a A2 meri uzly VIN a VOUT. V merici ceste nesmi byt RC filtry,
ktere by menily amplitudu nebo fazi signalu.
- DAC vytvari kladne posunuty sinusovy signal; neni potreba seriovy vazebni
kondenzator C1. Pokud ho pouzijete, je nutne doplnit spravne DC predpeti.
- S jedinym referencnim odporem 1 kOhm je rozumny prakticky rozsah priblizne
100 Ohm az 10 kOhm. Pro Ohmy az MOhmy je nutne prepinani RREF.
Princip:
I = (VIN - VOUT) / RREF
Zx = VOUT / I = RREF * VOUT / (VIN - VOUT)
Program snima komplexni zakladni harmonickou pomoci synchronni demodulace,
takze zobrazuje modul |Z| i fazi impedance.
*/
#include <Arduino.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <math.h>
#define PROGRAM_VERSION "v1.0.0"
// ------------------------------ Piny ---------------------------------------
static constexpr uint8_t PIN_DAC = A0;
static constexpr uint8_t PIN_VIN = A1;
static constexpr uint8_t PIN_VOUT = A2;
static constexpr uint8_t TFT_CS = 10;
static constexpr uint8_t TFT_DC = 9;
static constexpr uint8_t TFT_RST = 8;
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
// --------------------------- Merici parametry ------------------------------
static constexpr float RREF_OHM = 1000.0f;
static constexpr uint16_t DAC_MID = 2048; // 12bit DAC; stred signalu
static constexpr uint16_t DAC_AMPLITUDE = 1000; // bezpecne pod maximem 4095
static constexpr uint8_t SETTLE_CYCLES = 3;
static constexpr uint8_t MEASURE_CYCLES = 6;
static constexpr uint8_t MAX_POINTS_PER_CYCLE = 48;
static const float TEST_FREQUENCIES_HZ[] = {
100.0f, 160.0f, 250.0f, 400.0f, 630.0f,
1000.0f, 1600.0f, 2500.0f, 4000.0f
};
static constexpr uint8_t FREQ_COUNT = sizeof(TEST_FREQUENCIES_HZ) / sizeof(TEST_FREQUENCIES_HZ[0]);
// ------------------------------ Graf ---------------------------------------
static constexpr int16_t GRAPH_X = 42;
static constexpr int16_t GRAPH_Y = 49;
static constexpr int16_t GRAPH_W = 267;
static constexpr int16_t GRAPH_H = 130;
static constexpr float GRAPH_Z_MIN = 10.0f;
static constexpr float GRAPH_Z_MAX = 100000.0f;
struct ComplexValue {
float re;
float im;
};
struct Measurement {
float frequencyHz;
float magnitudeOhm;
float phaseDeg;
float vinMagnitude;
float voutMagnitude;
bool valid;
};
Measurement measurements[FREQ_COUNT];
bool continuousMode = true;
uint32_t lastSweepMs = 0;
// ------------------------- Komplexni matematika ----------------------------
ComplexValue complexSubtract(const ComplexValue &a, const ComplexValue &b) {
return {a.re - b.re, a.im - b.im};
}
float complexMagnitude(const ComplexValue &a) {
return sqrtf(a.re * a.re + a.im * a.im);
}
ComplexValue complexDivide(const ComplexValue &a, const ComplexValue &b) {
const float d = b.re * b.re + b.im * b.im;
if (d < 1.0e-12f) {
return {NAN, NAN};
}
return {(a.re * b.re + a.im * b.im) / d,
(a.im * b.re - a.re * b.im) / d};
}
// ----------------------------- Pomocne funkce ------------------------------
uint8_t pointsPerCycleForFrequency(float frequencyHz) {
if (frequencyHz <= 1000.0f) return 48;
if (frequencyHz <= 2500.0f) return 32;
return 20;
}
uint16_t dacCodeForPhase(float phaseRad) {
const float value = static_cast<float>(DAC_MID) + static_cast<float>(DAC_AMPLITUDE) * sinf(phaseRad);
if (value < 0.0f) return 0;
if (value > 4095.0f) return 4095;
return static_cast<uint16_t>(value + 0.5f);
}
void waitUntilMicros(uint32_t targetUs) {
while (static_cast<int32_t>(micros() - targetUs) < 0) {
// Aktivni cekani zamerne: udrzuje pravidelnost buzeni a vzorkovani.
}
}
void setDacMidpoint() {
analogWrite(PIN_DAC, DAC_MID);
}
// ------------------------------- Mereni ------------------------------------
Measurement measureAtFrequency(float frequencyHz) {
Measurement result{};
result.frequencyHz = frequencyHz;
result.valid = false;
const uint8_t points = pointsPerCycleForFrequency(frequencyHz);
const uint16_t samples = static_cast<uint16_t>(points) * MEASURE_CYCLES;
const float stepPeriodUsF = 1000000.0f / (frequencyHz * static_cast<float>(points));
const uint32_t stepPeriodUs = static_cast<uint32_t>(stepPeriodUsF + 0.5f);
// Frekvence, kterou ve skutecnosti nastavime celočíselnym casovanim.
const float actualFrequencyHz = 1000000.0f / (static_cast<float>(stepPeriodUs) * static_cast<float>(points));
result.frequencyHz = actualFrequencyHz;
uint32_t nextUs = micros() + 200;
// Ustaleni obvodu pred vlastnim snimanim.
for (uint16_t n = 0; n < static_cast<uint16_t>(points) * SETTLE_CYCLES; ++n) {
const uint8_t k = n % points;
const float phase = TWO_PI * static_cast<float>(k) / static_cast<float>(points);
analogWrite(PIN_DAC, dacCodeForPhase(phase));
nextUs += stepPeriodUs;
waitUntilMicros(nextUs);
}
ComplexValue vin{0.0f, 0.0f};
ComplexValue vout{0.0f, 0.0f};
for (uint16_t n = 0; n < samples; ++n) {
const uint8_t k = n % points;
const float phase = TWO_PI * static_cast<float>(k) / static_cast<float>(points);
const float c = cosf(phase);
const float s = sinf(phase);
analogWrite(PIN_DAC, dacCodeForPhase(phase));
// Vzorek se odebere priblizne uprostred nastaveneho DAC kroku.
waitUntilMicros(nextUs + stepPeriodUs / 2U);
const float rawVin = static_cast<float>(analogRead(PIN_VIN));
const float rawVout = static_cast<float>(analogRead(PIN_VOUT));
vin.re += rawVin * c;
vin.im -= rawVin * s;
vout.re += rawVout * c;
vout.im -= rawVout * s;
nextUs += stepPeriodUs;
waitUntilMicros(nextUs);
}
setDacMidpoint();
const float scale = 2.0f / static_cast<float>(samples);
vin.re *= scale;
vin.im *= scale;
vout.re *= scale;
vout.im *= scale;
const ComplexValue delta = complexSubtract(vin, vout);
const float deltaMagnitude = complexMagnitude(delta);
result.vinMagnitude = complexMagnitude(vin);
result.voutMagnitude = complexMagnitude(vout);
// Pri velmi malem napeti na RREF nelze spolehlive urcit proud.
if (result.vinMagnitude < 2.0f || deltaMagnitude < 1.5f) {
result.magnitudeOhm = NAN;
result.phaseDeg = NAN;
return result;
}
ComplexValue z = complexDivide(vout, delta);
z.re *= RREF_OHM;
z.im *= RREF_OHM;
result.magnitudeOhm = complexMagnitude(z);
result.phaseDeg = atan2f(z.im, z.re) * 180.0f / PI;
result.valid = isfinite(result.magnitudeOhm) && isfinite(result.phaseDeg) && result.magnitudeOhm > 0.0f;
return result;
}
// ------------------------------- Displej -----------------------------------
void drawHeader() {
tft.fillScreen(ILI9341_BLACK);
tft.setTextColor(ILI9341_CYAN);
tft.setTextSize(2);
tft.setCursor(6, 6);
tft.print(F("IMPEDANCE |Z|"));
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(1);
tft.setCursor(226, 10);
tft.print(PROGRAM_VERSION);
tft.setCursor(6, 28);
tft.print(F("UNO R4 DAC A0 | VIN A1 | VOUT A2 | Rref=1k"));
}
int16_t graphYForMagnitude(float magnitudeOhm) {
if (!isfinite(magnitudeOhm) || magnitudeOhm <= 0.0f) return GRAPH_Y + GRAPH_H;
float z = magnitudeOhm;
if (z < GRAPH_Z_MIN) z = GRAPH_Z_MIN;
if (z > GRAPH_Z_MAX) z = GRAPH_Z_MAX;
const float fraction = (log10f(z) - log10f(GRAPH_Z_MIN)) /
(log10f(GRAPH_Z_MAX) - log10f(GRAPH_Z_MIN));
return GRAPH_Y + GRAPH_H - static_cast<int16_t>(fraction * GRAPH_H + 0.5f);
}
int16_t graphXForIndex(uint8_t index) {
if (FREQ_COUNT <= 1) return GRAPH_X;
return GRAPH_X + static_cast<int16_t>((static_cast<long>(index) * GRAPH_W) / (FREQ_COUNT - 1));
}
void drawGraphBackground() {
tft.fillRect(0, 42, 320, 145, ILI9341_BLACK);
tft.drawRect(GRAPH_X, GRAPH_Y, GRAPH_W, GRAPH_H, ILI9341_DARKGREY);
const float labels[] = {10.0f, 100.0f, 1000.0f, 10000.0f, 100000.0f};
const char *labelText[] = {"10", "100", "1k", "10k", "100k"};
tft.setTextSize(1);
for (uint8_t i = 0; i < 5; ++i) {
const int16_t y = graphYForMagnitude(labels[i]);
tft.drawFastHLine(GRAPH_X, y, GRAPH_W, ILI9341_DARKGREY);
tft.setTextColor(ILI9341_WHITE);
tft.setCursor(4, y - 3);
tft.print(labelText[i]);
}
for (uint8_t i = 0; i < FREQ_COUNT; ++i) {
const int16_t x = graphXForIndex(i);
tft.drawFastVLine(x, GRAPH_Y, GRAPH_H, ILI9341_DARKGREY);
}
tft.setTextColor(ILI9341_WHITE);
tft.setCursor(GRAPH_X, GRAPH_Y + GRAPH_H + 4);
tft.print(F("100Hz"));
tft.setCursor(GRAPH_X + 102, GRAPH_Y + GRAPH_H + 4);
tft.print(F("1k"));
tft.setCursor(GRAPH_X + GRAPH_W - 30, GRAPH_Y + GRAPH_H + 4);
tft.print(F("4k"));
}
void displayLatestValues(const Measurement &m) {
tft.fillRect(0, 195, 320, 45, ILI9341_BLACK);
tft.setTextSize(1);
tft.setTextColor(ILI9341_YELLOW);
tft.setCursor(6, 198);
tft.print(F("Posledni: "));
tft.print(m.frequencyHz, 0);
tft.print(F(" Hz"));
tft.setCursor(6, 212);
if (m.valid) {
tft.setTextColor(ILI9341_GREEN);
tft.print(F("|Z|="));
if (m.magnitudeOhm >= 1000.0f) {
tft.print(m.magnitudeOhm / 1000.0f, 2);
tft.print(F(" kOhm"));
} else {
tft.print(m.magnitudeOhm, 1);
tft.print(F(" Ohm"));
}
tft.setTextColor(ILI9341_MAGENTA);
tft.setCursor(172, 212);
tft.print(F("faze="));
tft.print(m.phaseDeg, 1);
tft.print(F(" deg"));
} else {
tft.setTextColor(ILI9341_RED);
tft.print(F("MIMO ROZSAH / bez proudu"));
}
tft.setTextColor(ILI9341_WHITE);
tft.setCursor(6, 227);
tft.print(F("Serial: s=scan, c=continuous ON/OFF"));
}
void plotMeasurements() {
drawGraphBackground();
bool havePrevious = false;
int16_t previousX = 0;
int16_t previousY = 0;
for (uint8_t i = 0; i < FREQ_COUNT; ++i) {
if (!measurements[i].valid) {
havePrevious = false;
continue;
}
const int16_t x = graphXForIndex(i);
const int16_t y = graphYForMagnitude(measurements[i].magnitudeOhm);
if (havePrevious) {
tft.drawLine(previousX, previousY, x, y, ILI9341_GREEN);
}
tft.fillCircle(x, y, 2, ILI9341_YELLOW);
previousX = x;
previousY = y;
havePrevious = true;
}
}
// ------------------------------- Sweep -------------------------------------
void runSweep() {
tft.fillRect(0, 186, 320, 9, ILI9341_BLACK);
tft.setTextSize(1);
tft.setTextColor(ILI9341_CYAN);
tft.setCursor(6, 187);
tft.print(F("Merim..."));
Serial.println();
Serial.println(F("frequency_Hz;abs_Z_Ohm;phase_deg;Vin_DFT;Vout_DFT;status"));
for (uint8_t i = 0; i < FREQ_COUNT; ++i) {
measurements[i] = measureAtFrequency(TEST_FREQUENCIES_HZ[i]);
const Measurement &m = measurements[i];
Serial.print(m.frequencyHz, 2);
Serial.print(';');
if (m.valid) {
Serial.print(m.magnitudeOhm, 3);
Serial.print(';');
Serial.print(m.phaseDeg, 3);
Serial.print(';');
Serial.print(m.vinMagnitude, 3);
Serial.print(';');
Serial.print(m.voutMagnitude, 3);
Serial.println(F(";OK"));
} else {
Serial.print(F("NaN;NaN;"));
Serial.print(m.vinMagnitude, 3);
Serial.print(';');
Serial.print(m.voutMagnitude, 3);
Serial.println(F(";OUT_OF_RANGE"));
}
}
plotMeasurements();
displayLatestValues(measurements[FREQ_COUNT - 1]);
tft.fillRect(0, 186, 320, 9, ILI9341_BLACK);
tft.setTextColor(ILI9341_CYAN);
tft.setCursor(6, 187);
tft.print(continuousMode ? F("AUTO SWEEP") : F("MANUAL MODE"));
lastSweepMs = millis();
}
// ------------------------------ Arduino API --------------------------------
void setup() {
Serial.begin(115200);
delay(300);
analogWriteResolution(12);
analogReadResolution(12);
setDacMidpoint();
tft.begin();
tft.setRotation(1);
drawHeader();
drawGraphBackground();
tft.setTextSize(1);
tft.setTextColor(ILI9341_WHITE);
tft.setCursor(6, 198);
tft.print(F("Start mereni..."));
Serial.println(F("Arduino Impedancni Analyzator " PROGRAM_VERSION));
Serial.println(F("UNO R4: DAC=A0, VIN=A1, VOUT=A2, RREF=1000 Ohm"));
Serial.println(F("Poznamka: s jednim RREF=1k je prakticky rozsah zhruba 100 Ohm az 10 kOhm."));
Serial.println(F("Prikazy: s = jednorazovy sweep, c = prepnout automaticke mereni."));
runSweep();
}
void loop() {
if (Serial.available() > 0) {
const char command = static_cast<char>(Serial.read());
if (command == 's' || command == 'S') {
runSweep();
} else if (command == 'c' || command == 'C') {
continuousMode = !continuousMode;
Serial.print(F("Continuous mode: "));
Serial.println(continuousMode ? F("ON") : F("OFF"));
displayLatestValues(measurements[FREQ_COUNT - 1]);
}
}
if (continuousMode && millis() - lastSweepMs >= 1500UL) {
runSweep();
}
}
Kdo je online
Uživatelé prohlížející si toto fórum: Žádní registrovaní uživatelé a 2 hosti