Heim Der Blog Blog Details

Wie erreicht man eine Ultraschalldistanzmessung mit 51 Mikrocontrollern?

October 13 2025
Ampheo

Anfrage

Globaler Lieferant elektronischer Komponenten AMPHEO PTY LTD: Umfangreiches Inventar für One-Stop-Shopping. Einfache Anfragen, schnelle, individuelle Lösungen und Angebote.

SCHNELLE ANFRAGE
ZUR RFQ-LISTE HINZUFÜGEN
Hier ist eine praxisnahe Anleitung, wie du mit einem klassischen 8051 (z. B. AT89C51) die Ultraschall-Entfernungsmessung mit einem gängigen 5-V-Modul wie HC-SR04 (oder US-015) umsetzt.

Hier ist eine praxisnahe Anleitung, wie du mit einem klassischen 8051 (z. B. AT89C51) die Ultraschall-Entfernungsmessung mit einem gängigen 5-V-Modul wie HC-SR04 (oder US-015) umsetzt.

Wie erreicht man eine Ultraschalldistanzmessung mit 51 Mikrocontrollern?

1) Was du benötigst

  • MCU: Beliebiger 8051 (AT89C51/52 oder kompatibel) @ 11,0592 MHz (die Beispiele unten gehen von diesem Takt aus).

  • Sensor: HC-SR04 (5-V-Logik, ein TRIG-Eingang, ein ECHO-Ausgang).

  • Versorgung: 5 V (MCU und Sensor müssen gemeinsame Masse haben).

  • Verdrahtung:

    • TRIG → beliebiger 8051-GPIO-Ausgang (Beispiel: P2.0)

    • ECHO → beliebiger 8051-GPIO-Eingang (Beispiel: P2.1 für einfaches Polling, oder P3.2/INT0 wenn du per Interrupt stoppen willst)

    • VCC → 5 V

    • GND → GND

  • (Optional): UART oder LCD zur Anzeige der gemessenen Distanz.

2) So funktioniert die Messung (Physik + Timing)

  • Du sendest einen ≥ 10 µs langen HIGH-Impuls auf TRIG.

  • Der Sensor sendet Ultraschall aus und setzt ECHO auf HIGH. Die HIGH-Zeit von ECHO entspricht der Laufzeit des Schalls für Hin- und Rückweg.

  • Umrechnung Pulsbreite → Distanz:

    • Schallgeschwindigkeit bei ~20 °C: ≈ 343 m/s = 0,0343 cm/µs

    • Entfernung (cm) ≈ (Puls_µs × 0,0343) / 2 ≈ Puls_µs / 58

  • Bei 11,0592 MHz 8051 gilt im Timer-Mode 1 (16-Bit):
    Timer-Tick = 12 / Fosc ≈ 1,085 µs.
    Entfernung (cm) ≈ (Timercounts × 1,085) / 58 ≈ Timercounts × 0,01871

3) Strategie auf dem 8051

Zwei einfache Vorgehensweisen:

A. Polling (am simpelsten, ausreichend genau)

  • Auf HIGH von ECHO warten → Timer0 starten.

  • Auf LOW von ECHO warten → Timer0 stoppen.

  • Zähler auslesen → in cm umrechnen.

  • Timeout vorsehen, damit du nicht ewig blockierst, wenn kein Echo kommt.

B. Hybrid (präziseres Ende-Timing)

  • Rising Edge per Polling erkennen, Timer0 starten.

  • ECHO an INT0 (P3.2) legen, auf fallende Flanke triggern; im ISR den Timer stoppen.

  • Zähler im Main auswerten.

Unten steht Option A (Polling) der Klarheit halber.

4) Minimale Schaltungstipps

  • Leitungen zum Sensor kurz halten; keine „fliegenden“ langen Kabel.

  • 0,1 µF Stützkondensator nah am Sensor-VCC.

  • Zwischen Messungen ≥ 60 ms pausieren (Klingeln/Mehrfach-Echos vermeiden).

  • Bei wasserdichten Modulen (z. B. JSN-SR04T) auf etwas längere Mindestdistanz und leicht andere Timing-Charakteristik achten.

5) „Getestet-Style“ 8051-Beispiel (Keil C) — Polling-Version

Annahme: AT89C51 @ 11,0592 MHz, TRIG an P2.0, ECHO an P2.1.
Gibt die Distanz per UART 9600 bps aus (optional — UART-Teil kann entfernt werden).

 

#include <reg52.h>

// -------- Pins --------
sbit TRIG = P2^0;      // Trigger-Ausgang zum HC-SR04
sbit ECHO = P2^1;      // Echo-Eingang vom HC-SR04

// -------- UART (optional, 9600 bps @ 11.0592 MHz) --------
void UART_Init(void) {
    SCON = 0x50;  // 8-Bit UART, REN=1
    TMOD |= 0x20; // Timer1 Mode2 (Auto-Reload) für Baudrate
    TH1  = 0xFD;  // 9600 Baud
    TL1  = 0xFD;
    TR1  = 1;     // Timer1 starten
}

void UART_PutChar(char c) {
    SBUF = c;
    while(TI == 0);
    TI = 0;
}

void UART_Puts(char *s) {
    while(*s) UART_PutChar(*s++);
}

// -------- Kleine Delays (grob) --------
// Ausreichend für TRIG-Puls und Messintervall.
void delay_us_approx(unsigned int us) {
    // ~1 us pro Schleifenrumpf bei 11.0592 MHz (ungefähr).
    // Für >=10us TRIG reicht die Genauigkeit locker.
    while(us--) { _nop_(); }
}

void delay_ms(unsigned int ms) {
    unsigned int i;
    while(ms--) {
        for(i=0; i<123; i++) { _nop_(); } // ~1ms bei 11.0592MHz (grob)
    }
}

// -------- Timer0 Setup (Mode1, 16-Bit) --------
void Timer0_Init(void) {
    TMOD &= 0xF0;   // T0-Bits löschen
    TMOD |= 0x01;   // Timer0 Mode1 (16-Bit)
    TR0 = 0;        // stoppen
    TF0 = 0;        // Overflow löschen
}

// 10–15us TRIG-Impuls erzeugen
void HC_SR04_Trigger(void) {
    TRIG = 0;
    delay_us_approx(2);
    TRIG = 1;
    delay_us_approx(15);  // >=10us
    TRIG = 0;
}

// ECHO-Pulsbreite in Timer0-Zählern messen (1 Count ≈ 1,085us @ 11.0592MHz)
unsigned int MeasureEchoCounts(void) {
    unsigned int counts;
    unsigned int timeout;

    // 1) Auf HIGH (Rising Edge) von ECHO warten, mit Timeout
    timeout = 60000;                 // grober Timeout-Schutz
    while(!ECHO && --timeout);
    if(timeout == 0) return 0xFFFF;  // kein Echo (Timeout)

    // 2) Timer am Rising Edge starten
    TR0 = 0; TF0 = 0;
    TH0 = 0; TL0 = 0;
    TR0 = 1;

    // 3) Auf Falling Edge warten oder Overflow prüfen
    while(ECHO && !TF0);

    TR0 = 0;                         // Timer stoppen

    if(TF0) {                        // Overflow: Echo zu lang (zu weit)
        TF0 = 0;
        return 0xFFFE;
    }

    counts = ((unsigned int)TH0 << 8) | TL0;
    return counts;
}

// Umrechnung Zählerstände → Distanz (cm)
// tick_us ≈ 12 / Fosc_MHz = 1,085us (bei 11.0592MHz)
// dist_cm ≈ (counts * 1,085) / 58  ≈ counts * 0,01871
float CountsToCm(unsigned int counts) {
    return (float)counts * 0.01871f;
}

void PrintFloat1(float x) {
    // Minimalformat: xxx.x cm
    unsigned int i = (unsigned int)(x);
    unsigned int d = (unsigned int)((x - i) * 10.0f);
    char buf[8];
    buf[0] = (i/100)%10 + '0';
    buf[1] = (i/10)%10  + '0';
    buf[2] = (i%10)     + '0';
    buf[3] = '.';
    buf[4] = d + '0';
    buf[5] = '\0';

    // Führende Nullen etwas hübschen (optional)
    if(buf[0]=='0') { buf[0]=buf[1]; buf[1]=buf[2]; buf[2]=buf[3]; buf[3]=buf[4]; buf[4]='\0'; }
    if(buf[0]=='0') { buf[0]=buf[1]; buf[1]=buf[2]; buf[2]='\0'; }

    UART_Puts(buf);
}

void main(void) {
    unsigned int c;
    float dist;

    UART_Init();
    Timer0_Init();

    UART_Puts("\r\nUltraschall-Distanz (cm):\r\n");

    while(1) {
        HC_SR04_Trigger();
        c = MeasureEchoCounts();

        if(c == 0xFFFF) {
            UART_Puts("Kein Echo (Timeout)\r\n");
        } else if(c == 0xFFFE) {
            UART_Puts("Außer Reichweite (Overflow)\r\n");
        } else {
            dist = CountsToCm(c);
            UART_Puts("D = ");
            PrintFloat1(dist);
            UART_Puts(" cm\r\n");
        }

        delay_ms(70); // >=60ms zwischen Messungen
    }
}

Hinweise

  • Wenn dein Quarz nicht 11,0592 MHz hat, passe die Umrechnung an:
    tick_us = 12 / Fosc_MHzdist_cm = (counts * tick_us) / 58.

  • Du kannst float vermeiden, z. B. rein integerbasiert:
    distance_mm ≈ counts * 187 / 10 (entspricht 0,0187 cm → 0,187 mm pro Count).

6) Genauigkeit & Robustheit verbessern

  • Temperaturkompensation (optional):
    Schallgeschwindigkeit c m/s (T in °C).
    Dann Distanz (cm)=(Pulsμs×(c×0,1))/2
    Implementiere das mit einem Temperatursensor oder einer festen Korrektur, wenn die Umgebung stabil ist.

  • Median-/Mittelwert-Filterung: 3–5 Messungen, Ausreißer verwerfen, Rest mitteln.

  • Mindestdistanz & Öffnungswinkel: HC-SR04 min. ~2 cm, FOV ~15°. Schlecht reflektierende, weiche Oberflächen vermeiden; auf Achse messen.

  • Mechanik: Gegen Vibration entkoppeln; nicht knapp an Kanten messen (Mehrwege-Echos).

  • Versorgung: Saubere Masseführung; 0,1 µF + 10 µF nahe am Sensor-VCC; während der Messung starke Schaltspitzen vermeiden.

7) Option B: Timer mit INT0 stoppen (präzisere fallende Flanke)

Wenn du das Ende genauer erfassen willst, ECHO → P3.2 (INT0):

  • Rising Edge per Polling erkennen → Timer0 starten.

  • IT0=1 (Flankenmodus) setzen, EX0=1 aktivieren.

  • Bei fallender Flanke stoppt das INT0-ISR den Timer und setzt ein „captured“-Flag.

  • Umrechnung wie oben.

(Hinweis: Beide Flanken rein per Interrupt am selben Pin sauber zu trennen ist beim klassischen 8051 nicht vorgesehen; meist nimmt man eine Flanke per INT0 und die andere per Polling oder mit externer Logik.)

8) Häufige Stolperfallen

  • < 60 ms zwischen Trigger-Pulsen (führt zu Geistermessungen).

  • Lange Kabel / fehlende Stützkondensatoren → jitterige Werte.

  • Falsche Umrechnung, wenn Fosc ≠ 11,0592 MHz.

  • Enge Gehäuse → Mehrfachreflexionen; Dämpfung/Orientierung anpassen.


Fertig! Verdrahten, flashen, seriellen Monitor auf 9600 bps öffnen — dann solltest du stabile cm-Werte sehen.

Ampheo